Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions packages/opencode/src/altimate/telemetry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,10 @@ export namespace Telemetry {
let flushTimer: ReturnType<typeof setInterval> | undefined
let userEmail = ""
let machineId = ""
// Where this serve process was launched from ("ide" when spawned by an
// editor extension, "" for CLI/TUI). Stamped onto every event so analytics
// can attribute sessions to IDE vs terminal usage.
let launchSurface = ""
let sessionId = ""
let projectId = ""
let appInsights: AppInsightsConfig | undefined
Expand Down Expand Up @@ -1228,6 +1232,7 @@ export namespace Telemetry {
cli_version: Installation.VERSION,
project_id: fields.project_id ?? projectId,
...(machineId && { machine_id: machineId }),
...(launchSurface && { launch_surface: launchSurface }),
}
const measurements: Record<string, number> = {}

Expand Down Expand Up @@ -1319,6 +1324,13 @@ export namespace Telemetry {
} catch {
// Account unavailable — proceed without user ID
}
// Set by the IDE extension when it spawns `altimate serve`; absent for
// direct CLI/TUI invocations. Normalize casing/whitespace and restrict to
// a known set so a stray env value can't inflate the launch_surface
// dimension's cardinality. New surfaces must be added to KNOWN_LAUNCH_SURFACES.
const KNOWN_LAUNCH_SURFACES = new Set(["ide", "cli", "tui"])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: KNOWN_LAUNCH_SURFACES includes "cli" and "tui" as accepted values, but no code path ever sets them — the IDE extension only ever sets "ide" and direct CLI/TUI leaves the env var unset. These are effectively dead allowlist entries that could inflate perceived surface support. Either remove them or add a comment reserving them for future use. If they remain, consider that setting ALTIMATE_LAUNCH_SURFACE=cli manually would pass through, so there is no harm — but dead entries risk misleading future maintainers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/altimate/telemetry/index.ts, line 1331:

<comment>`KNOWN_LAUNCH_SURFACES` includes `"cli"` and `"tui"` as accepted values, but no code path ever sets them — the IDE extension only ever sets `"ide"` and direct CLI/TUI leaves the env var unset. These are effectively dead allowlist entries that could inflate perceived surface support. Either remove them or add a comment reserving them for future use. If they remain, consider that setting `ALTIMATE_LAUNCH_SURFACE=cli` manually would pass through, so there is no harm — but dead entries risk misleading future maintainers.</comment>

<file context>
@@ -1325,8 +1325,12 @@ export namespace Telemetry {
+      // direct CLI/TUI invocations. Normalize casing/whitespace and restrict to
+      // a known set so a stray env value can't inflate the launch_surface
+      // dimension's cardinality. New surfaces must be added to KNOWN_LAUNCH_SURFACES.
+      const KNOWN_LAUNCH_SURFACES = new Set(["ide", "cli", "tui"])
+      const rawLaunchSurface = (process.env.ALTIMATE_LAUNCH_SURFACE ?? "").trim().toLowerCase()
+      launchSurface = KNOWN_LAUNCH_SURFACES.has(rawLaunchSurface) ? rawLaunchSurface : ""
</file context>

const rawLaunchSurface = (process.env.ALTIMATE_LAUNCH_SURFACE ?? "").trim().toLowerCase()
launchSurface = KNOWN_LAUNCH_SURFACES.has(rawLaunchSurface) ? rawLaunchSurface : ""
try {
const machineIdPath = path.join(os.homedir(), ".altimate", "machine-id")
try {
Expand Down Expand Up @@ -1449,6 +1461,7 @@ export namespace Telemetry {
sessionId = ""
projectId = ""
machineId = ""
launchSurface = ""
initPromise = undefined
initDone = false
}
Expand Down
76 changes: 76 additions & 0 deletions packages/opencode/test/telemetry/telemetry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,82 @@ describe("telemetry.toAppInsightsEnvelopes (indirect)", () => {
}
})

test("launch_surface is stamped on every event when ALTIMATE_LAUNCH_SURFACE is set", async () => {
const origSurface = process.env.ALTIMATE_LAUNCH_SURFACE
process.env.ALTIMATE_LAUNCH_SURFACE = "ide"
const { fetchCalls, cleanup } = await initWithMockedFetch()
try {
Telemetry.track({ type: "session_start", timestamp: 1700000000000, session_id: "sess-ide" })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== telemetry test context =="
sed -n '520,650p' packages/opencode/test/telemetry/telemetry.test.ts | cat -n

echo
echo "== Event type / Telemetry.track declaration search =="
rg -n "type Event|interface Event|Telemetry\.track|track\(" packages/opencode -g '!**/dist/**' -g '!**/build/**'

echo
echo "== likely telemetry source files =="
fd -a "telemetry" packages/opencode/src packages/opencode/test packages/opencode -t f

Repository: AltimateAI/altimate-code

Length of output: 29640


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== altimate telemetry type definition =="
sed -n '1,220p' packages/opencode/src/altimate/telemetry/index.ts | cat -n

echo
echo "== track implementation area =="
sed -n '1360,1405p' packages/opencode/src/altimate/telemetry/index.ts | cat -n

echo
echo "== any event construction helpers around session_start =="
rg -n '"session_start"|session_start' packages/opencode/src/altimate/telemetry/index.ts packages/opencode/test/telemetry/telemetry.test.ts packages/opencode/test/altimate/telemetry-signals.test.ts

Repository: AltimateAI/altimate-code

Length of output: 14026


Add the required session_start fields to both new telemetry calls
Telemetry.Event["session_start"] requires model_id, provider_id, agent, project_id, os, arch, and node_version; the calls at packages/opencode/test/telemetry/telemetry.test.ts:604 and :621 only pass type, timestamp, and session_id.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/test/telemetry/telemetry.test.ts` at line 604, The two
Telemetry.track calls for session_start in telemetry.test.ts are missing
required fields for the session_start event. Update both calls at the relevant
test cases to include model_id, provider_id, agent, project_id, os, arch, and
node_version alongside type, timestamp, and session_id, using the existing test
fixtures or mock values already used for Telemetry.Event["session_start"].

await Telemetry.flush()

expect(fetchCalls.length).toBeGreaterThan(0)
const envelopes = JSON.parse(fetchCalls[0].body)
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
expect(envelopes.length).toBeGreaterThan(0)
expect(envelopes[0].data.baseData.properties.launch_surface).toBe("ide")
} finally {
cleanup()
if (origSurface !== undefined) process.env.ALTIMATE_LAUNCH_SURFACE = origSurface
else delete process.env.ALTIMATE_LAUNCH_SURFACE
}
})

test("launch_surface is omitted when ALTIMATE_LAUNCH_SURFACE is unset (CLI/TUI)", async () => {
const origSurface = process.env.ALTIMATE_LAUNCH_SURFACE
delete process.env.ALTIMATE_LAUNCH_SURFACE
const { fetchCalls, cleanup } = await initWithMockedFetch()
try {
Telemetry.track({ type: "session_start", timestamp: 1700000000000, session_id: "sess-cli" })
await Telemetry.flush()

expect(fetchCalls.length).toBeGreaterThan(0)
const envelopes = JSON.parse(fetchCalls[0].body)
expect(envelopes.length).toBeGreaterThan(0)
expect(envelopes[0].data.baseData.properties.launch_surface).toBeUndefined()
} finally {
cleanup()
if (origSurface !== undefined) process.env.ALTIMATE_LAUNCH_SURFACE = origSurface
else delete process.env.ALTIMATE_LAUNCH_SURFACE
}
})

test("launch_surface normalizes casing and surrounding whitespace", async () => {
const origSurface = process.env.ALTIMATE_LAUNCH_SURFACE
process.env.ALTIMATE_LAUNCH_SURFACE = " IDE "
const { fetchCalls, cleanup } = await initWithMockedFetch()
try {
Telemetry.track({ type: "session_start", timestamp: 1700000000000, session_id: "sess-norm" })
await Telemetry.flush()

expect(fetchCalls.length).toBeGreaterThan(0)
const envelopes = JSON.parse(fetchCalls[0].body)
expect(envelopes.length).toBeGreaterThan(0)
expect(envelopes[0].data.baseData.properties.launch_surface).toBe("ide")
} finally {
cleanup()
if (origSurface !== undefined) process.env.ALTIMATE_LAUNCH_SURFACE = origSurface
else delete process.env.ALTIMATE_LAUNCH_SURFACE
}
})

test("launch_surface drops unrecognized values to keep the dimension low-cardinality", async () => {
const origSurface = process.env.ALTIMATE_LAUNCH_SURFACE
process.env.ALTIMATE_LAUNCH_SURFACE = "totally-bogus"
const { fetchCalls, cleanup } = await initWithMockedFetch()
try {
Telemetry.track({ type: "session_start", timestamp: 1700000000000, session_id: "sess-bogus" })
await Telemetry.flush()

expect(fetchCalls.length).toBeGreaterThan(0)
const envelopes = JSON.parse(fetchCalls[0].body)
expect(envelopes.length).toBeGreaterThan(0)
expect(envelopes[0].data.baseData.properties.launch_surface).toBeUndefined()
} finally {
cleanup()
if (origSurface !== undefined) process.env.ALTIMATE_LAUNCH_SURFACE = origSurface
else delete process.env.ALTIMATE_LAUNCH_SURFACE
}
})

test("numeric fields go to measurements, string fields go to properties", async () => {
const { fetchCalls, cleanup } = await initWithMockedFetch()
try {
Expand Down
Loading