From ae65906e121bb4adb5db47aeaf17f4ff5705fafc Mon Sep 17 00:00:00 2001 From: saravmajestic Date: Fri, 26 Jun 2026 03:00:59 +0000 Subject: [PATCH 1/6] feat: add source field to session_start telemetry event Adds source: string to the session_start event type definition and populates it with Flag.ALTIMATE_CLI_CLIENT at the call site. Defaults to "cli" for terminal invocations; overridden to "vscode" when the VS Code extension spawns the server via ALTIMATE_CLI_CLIENT env var. --- packages/opencode/src/altimate/telemetry/index.ts | 1 + packages/opencode/src/session/prompt.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/opencode/src/altimate/telemetry/index.ts b/packages/opencode/src/altimate/telemetry/index.ts index b2b0e7103..20d54ab05 100644 --- a/packages/opencode/src/altimate/telemetry/index.ts +++ b/packages/opencode/src/altimate/telemetry/index.ts @@ -62,6 +62,7 @@ export namespace Telemetry { os: string arch: string node_version: string + source: string } // altimate_change end | { diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 72027eedf..cf13fc56e 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -922,6 +922,7 @@ export namespace SessionPrompt { os: process.platform, arch: process.arch, node_version: process.version, + source: Flag.ALTIMATE_CLI_CLIENT, }) // altimate_change start — task intent classification (keyword/regex, zero LLM cost) const userMsg = msgs.find((m) => m.info.id === lastUser!.id) From 01189468320df41e866b0c9ebba96777da2f895d Mon Sep 17 00:00:00 2001 From: saravmajestic Date: Fri, 26 Jun 2026 03:09:42 +0000 Subject: [PATCH 2/6] refactor: inject source globally in telemetry (all events, not just session_start) --- packages/opencode/src/altimate/telemetry/index.ts | 7 ++++++- packages/opencode/src/session/prompt.ts | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/altimate/telemetry/index.ts b/packages/opencode/src/altimate/telemetry/index.ts index 20d54ab05..b528ae969 100644 --- a/packages/opencode/src/altimate/telemetry/index.ts +++ b/packages/opencode/src/altimate/telemetry/index.ts @@ -62,7 +62,6 @@ export namespace Telemetry { os: string arch: string node_version: string - source: string } // altimate_change end | { @@ -1201,6 +1200,7 @@ export namespace Telemetry { let machineId = "" let sessionId = "" let projectId = "" + let clientSource = "cli" let appInsights: AppInsightsConfig | undefined let droppedEvents = 0 let initPromise: Promise | undefined @@ -1227,6 +1227,7 @@ export namespace Telemetry { const properties: Record = { cli_version: Installation.VERSION, + source: clientSource, project_id: fields.project_id ?? projectId, ...(machineId && { machine_id: machineId }), } @@ -1349,6 +1350,10 @@ export namespace Telemetry { projectId = opts.projectId } + export function setSource(s: string): void { + clientSource = s + } + export function getContext() { return { sessionId, projectId } } diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index cf13fc56e..324523023 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -362,6 +362,7 @@ export namespace SessionPrompt { // altimate_change end // altimate_change start — session telemetry tracking await Telemetry.init() + Telemetry.setSource(Flag.ALTIMATE_CLI_CLIENT) Telemetry.setContext({ sessionId: sessionID, projectId: Instance.project?.id ?? "" }) const sessionStartTime = Date.now() let sessionTotalCost = 0 @@ -922,7 +923,6 @@ export namespace SessionPrompt { os: process.platform, arch: process.arch, node_version: process.version, - source: Flag.ALTIMATE_CLI_CLIENT, }) // altimate_change start — task intent classification (keyword/regex, zero LLM cost) const userMsg = msgs.find((m) => m.info.id === lastUser!.id) From a205cc958434c73ef8e4ae784502f92fefb17387 Mon Sep 17 00:00:00 2001 From: saravmajestic Date: Fri, 26 Jun 2026 03:28:04 +0000 Subject: [PATCH 3/6] feat: session-level source in session_start telemetry (reads from session metadata) - Add optional metadata column to SessionTable (JSON blob, backward-compatible) - Add metadata field to Session.Info schema, fromRow, toRow, create, and createNext - Add source?: string to session_start event type in telemetry/index.ts - In prompt.ts session_start emit, override source with session.metadata?.source when present, falling back to Flag.ALTIMATE_CLI_CLIENT for sessions without it VS Code extensions (datamates, poweruser) can now pass { metadata: { source: "datamates" } } in POST /session to produce distinguishable session_start telemetry even though both extensions share the same altimate serve process. The global source injection in toAppInsightsEnvelopes() (clientSource) is unchanged and still covers all other events. --- packages/opencode/src/altimate/telemetry/index.ts | 1 + packages/opencode/src/session/index.ts | 7 +++++++ packages/opencode/src/session/prompt.ts | 8 ++++++++ packages/opencode/src/session/session.sql.ts | 1 + 4 files changed, 17 insertions(+) diff --git a/packages/opencode/src/altimate/telemetry/index.ts b/packages/opencode/src/altimate/telemetry/index.ts index b528ae969..82a2beb8e 100644 --- a/packages/opencode/src/altimate/telemetry/index.ts +++ b/packages/opencode/src/altimate/telemetry/index.ts @@ -62,6 +62,7 @@ export namespace Telemetry { os: string arch: string node_version: string + source?: string } // altimate_change end | { diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index f2be3e10a..0ae3e86fd 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -76,6 +76,7 @@ export namespace Session { share, revert, permission: row.permission ?? undefined, + metadata: row.metadata ?? undefined, time: { created: row.time_created, updated: row.time_updated, @@ -102,6 +103,7 @@ export namespace Session { summary_diffs: info.summary?.diffs, revert: info.revert ?? null, permission: info.permission, + metadata: info.metadata, time_created: info.time.created, time_updated: info.time.updated, time_compacting: info.time.compacting, @@ -149,6 +151,7 @@ export namespace Session { archived: z.number().optional(), }), permission: PermissionNext.Ruleset.optional(), + metadata: z.record(z.string(), z.unknown()).optional(), revert: z .object({ messageID: MessageID.zod, @@ -223,6 +226,7 @@ export namespace Session { title: z.string().optional(), permission: Info.shape.permission, workspaceID: WorkspaceID.zod.optional(), + metadata: z.record(z.string(), z.unknown()).optional(), }) .optional(), async (input) => { @@ -232,6 +236,7 @@ export namespace Session { title: input?.title, permission: input?.permission, workspaceID: input?.workspaceID, + metadata: input?.metadata, }) }, ) @@ -301,6 +306,7 @@ export namespace Session { workspaceID?: WorkspaceID directory: string permission?: PermissionNext.Ruleset + metadata?: Record }) { const result: Info = { id: SessionID.descending(input.id), @@ -312,6 +318,7 @@ export namespace Session { parentID: input.parentID, title: input.title ?? createDefaultTitle(!!input.parentID), permission: input.permission, + metadata: input.metadata, time: { created: Date.now(), updated: Date.now(), diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 324523023..b50a7a78d 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -923,6 +923,14 @@ export namespace SessionPrompt { os: process.platform, arch: process.arch, node_version: process.version, + // altimate_change start — per-session source override: VS Code extensions set + // metadata.source (e.g. "datamates", "poweruser") when creating the session via POST /session. + // This lets both extensions share the same altimate serve process while producing + // distinguishable session_start telemetry. Falls back to the process-level + // Flag.ALTIMATE_CLI_CLIENT (set by ALTIMATE_CLI_CLIENT env var, e.g. "vscode") if + // the session carries no metadata source. + source: (session.metadata?.source as string | undefined) ?? Flag.ALTIMATE_CLI_CLIENT, + // altimate_change end }) // altimate_change start — task intent classification (keyword/regex, zero LLM cost) const userMsg = msgs.find((m) => m.info.id === lastUser!.id) diff --git a/packages/opencode/src/session/session.sql.ts b/packages/opencode/src/session/session.sql.ts index 189a59687..112825f3b 100644 --- a/packages/opencode/src/session/session.sql.ts +++ b/packages/opencode/src/session/session.sql.ts @@ -32,6 +32,7 @@ export const SessionTable = sqliteTable( summary_diffs: text({ mode: "json" }).$type(), revert: text({ mode: "json" }).$type<{ messageID: MessageID; partID?: PartID; snapshot?: string; diff?: string }>(), permission: text({ mode: "json" }).$type(), + metadata: text({ mode: "json" }).$type>(), ...Timestamps, time_compacting: integer(), time_archived: integer(), From d8089826b1216d2a564c04a3fd95c21211bd4d74 Mon Sep 17 00:00:00 2001 From: saravmajestic Date: Fri, 26 Jun 2026 04:18:00 +0000 Subject: [PATCH 4/6] fix: add SQLite migration for session.metadata column --- .../migration.sql | 1 + .../snapshot.json | 1347 +++++++++++++++++ 2 files changed, 1348 insertions(+) create mode 100644 packages/opencode/migration/20260626041744_elite_malcolm_colcord/migration.sql create mode 100644 packages/opencode/migration/20260626041744_elite_malcolm_colcord/snapshot.json diff --git a/packages/opencode/migration/20260626041744_elite_malcolm_colcord/migration.sql b/packages/opencode/migration/20260626041744_elite_malcolm_colcord/migration.sql new file mode 100644 index 000000000..0ce73631f --- /dev/null +++ b/packages/opencode/migration/20260626041744_elite_malcolm_colcord/migration.sql @@ -0,0 +1 @@ +ALTER TABLE `session` ADD `metadata` text; \ No newline at end of file diff --git a/packages/opencode/migration/20260626041744_elite_malcolm_colcord/snapshot.json b/packages/opencode/migration/20260626041744_elite_malcolm_colcord/snapshot.json new file mode 100644 index 000000000..5b55dbe0c --- /dev/null +++ b/packages/opencode/migration/20260626041744_elite_malcolm_colcord/snapshot.json @@ -0,0 +1,1347 @@ +{ + "version": "7", + "dialect": "sqlite", + "id": "192974be-33e8-4678-8e59-a964f4a0e71a", + "prevIds": [ + "f13dfa58-7fb4-47a2-8f6b-dc70258e14ed" + ], + "ddl": [ + { + "name": "account_state", + "entityType": "tables" + }, + { + "name": "account", + "entityType": "tables" + }, + { + "name": "control_account", + "entityType": "tables" + }, + { + "name": "workspace", + "entityType": "tables" + }, + { + "name": "project", + "entityType": "tables" + }, + { + "name": "message", + "entityType": "tables" + }, + { + "name": "part", + "entityType": "tables" + }, + { + "name": "permission", + "entityType": "tables" + }, + { + "name": "session", + "entityType": "tables" + }, + { + "name": "todo", + "entityType": "tables" + }, + { + "name": "session_share", + "entityType": "tables" + }, + { + "name": "event_sequence", + "entityType": "tables" + }, + { + "name": "event", + "entityType": "tables" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "account_state" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "active_account_id", + "entityType": "columns", + "table": "account_state" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "active_org_id", + "entityType": "columns", + "table": "account_state" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "email", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "url", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "access_token", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "refresh_token", + "entityType": "columns", + "table": "account" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "token_expiry", + "entityType": "columns", + "table": "account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "email", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "url", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "access_token", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "refresh_token", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "token_expiry", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "active", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "control_account" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "type", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "branch", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "name", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "directory", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "extra", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "project_id", + "entityType": "columns", + "table": "workspace" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "worktree", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "vcs", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "name", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "icon_url", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "icon_color", + "entityType": "columns", + "table": "project" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "project" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "project" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_initialized", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "sandboxes", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "commands", + "entityType": "columns", + "table": "project" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "message" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "message" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "message" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "message" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "data", + "entityType": "columns", + "table": "message" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "part" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "message_id", + "entityType": "columns", + "table": "part" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "part" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "part" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "part" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "data", + "entityType": "columns", + "table": "part" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "project_id", + "entityType": "columns", + "table": "permission" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "permission" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "permission" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "data", + "entityType": "columns", + "table": "permission" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "project_id", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "workspace_id", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "parent_id", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "slug", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "directory", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "title", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "version", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "share_url", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "summary_additions", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "summary_deletions", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "summary_files", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "summary_diffs", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "revert", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "permission", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "metadata", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_compacting", + "entityType": "columns", + "table": "session" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_archived", + "entityType": "columns", + "table": "session" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "todo" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "content", + "entityType": "columns", + "table": "todo" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "status", + "entityType": "columns", + "table": "todo" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "priority", + "entityType": "columns", + "table": "todo" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "position", + "entityType": "columns", + "table": "todo" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "todo" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "todo" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "session_id", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "secret", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "url", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_created", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "time_updated", + "entityType": "columns", + "table": "session_share" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "aggregate_id", + "entityType": "columns", + "table": "event_sequence" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "seq", + "entityType": "columns", + "table": "event_sequence" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "event" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "aggregate_id", + "entityType": "columns", + "table": "event" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "seq", + "entityType": "columns", + "table": "event" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "type", + "entityType": "columns", + "table": "event" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "data", + "entityType": "columns", + "table": "event" + }, + { + "columns": [ + "active_account_id" + ], + "tableTo": "account", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "SET NULL", + "nameExplicit": false, + "name": "fk_account_state_active_account_id_account_id_fk", + "entityType": "fks", + "table": "account_state" + }, + { + "columns": [ + "project_id" + ], + "tableTo": "project", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_workspace_project_id_project_id_fk", + "entityType": "fks", + "table": "workspace" + }, + { + "columns": [ + "session_id" + ], + "tableTo": "session", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_message_session_id_session_id_fk", + "entityType": "fks", + "table": "message" + }, + { + "columns": [ + "message_id" + ], + "tableTo": "message", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_part_message_id_message_id_fk", + "entityType": "fks", + "table": "part" + }, + { + "columns": [ + "project_id" + ], + "tableTo": "project", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_permission_project_id_project_id_fk", + "entityType": "fks", + "table": "permission" + }, + { + "columns": [ + "project_id" + ], + "tableTo": "project", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_session_project_id_project_id_fk", + "entityType": "fks", + "table": "session" + }, + { + "columns": [ + "session_id" + ], + "tableTo": "session", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_todo_session_id_session_id_fk", + "entityType": "fks", + "table": "todo" + }, + { + "columns": [ + "session_id" + ], + "tableTo": "session", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_session_share_session_id_session_id_fk", + "entityType": "fks", + "table": "session_share" + }, + { + "columns": [ + "aggregate_id" + ], + "tableTo": "event_sequence", + "columnsTo": [ + "aggregate_id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_event_aggregate_id_event_sequence_aggregate_id_fk", + "entityType": "fks", + "table": "event" + }, + { + "columns": [ + "email", + "url" + ], + "nameExplicit": false, + "name": "control_account_pk", + "entityType": "pks", + "table": "control_account" + }, + { + "columns": [ + "session_id", + "position" + ], + "nameExplicit": false, + "name": "todo_pk", + "entityType": "pks", + "table": "todo" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "account_state_pk", + "table": "account_state", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "account_pk", + "table": "account", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "workspace_pk", + "table": "workspace", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "project_pk", + "table": "project", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "message_pk", + "table": "message", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "part_pk", + "table": "part", + "entityType": "pks" + }, + { + "columns": [ + "project_id" + ], + "nameExplicit": false, + "name": "permission_pk", + "table": "permission", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "session_pk", + "table": "session", + "entityType": "pks" + }, + { + "columns": [ + "session_id" + ], + "nameExplicit": false, + "name": "session_share_pk", + "table": "session_share", + "entityType": "pks" + }, + { + "columns": [ + "aggregate_id" + ], + "nameExplicit": false, + "name": "event_sequence_pk", + "table": "event_sequence", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "event_pk", + "table": "event", + "entityType": "pks" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + }, + { + "value": "time_created", + "isExpression": false + }, + { + "value": "id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "message_session_time_created_id_idx", + "entityType": "indexes", + "table": "message" + }, + { + "columns": [ + { + "value": "message_id", + "isExpression": false + }, + { + "value": "id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "part_message_id_id_idx", + "entityType": "indexes", + "table": "part" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "part_session_idx", + "entityType": "indexes", + "table": "part" + }, + { + "columns": [ + { + "value": "project_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_project_idx", + "entityType": "indexes", + "table": "session" + }, + { + "columns": [ + { + "value": "workspace_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_workspace_idx", + "entityType": "indexes", + "table": "session" + }, + { + "columns": [ + { + "value": "parent_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "session_parent_idx", + "entityType": "indexes", + "table": "session" + }, + { + "columns": [ + { + "value": "session_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "todo_session_idx", + "entityType": "indexes", + "table": "todo" + } + ], + "renames": [] +} \ No newline at end of file From 1c1c100776c01c5befc80e170962ccefb606cadb Mon Sep 17 00:00:00 2001 From: anandgupta42 Date: Mon, 6 Jul 2026 21:52:09 -0700 Subject: [PATCH 5/6] fix: [PR-968] address telemetry source-field review feedback Resolves review findings on the telemetry `source` field: - Read `Flag.ALTIMATE_CLI_CLIENT` live at envelope-build time instead of a hardcoded `clientSource = "cli"` set only inside the prompt flow. Events emitted before the first prompt (startup, connection setup, standalone CLI subcommands) are now labelled with the real client instead of defaulting to `"cli"`. Removes the now-redundant `clientSource` variable, `setSource()`, and its call in `session/prompt.ts`. - Type-guard `session.metadata?.source` in the `session_start` emit (was an unchecked `as string`) so malformed client JSON falls back to the flag instead of being routed to `measurements` downstream. - Document `Session.Info.metadata` as an intentional open extension point. - Add tests: telemetry envelope `source` behaviour (env-flag default, `"cli"` fallback, per-event override, non-string routing contract) and session `metadata` round-trip through `create` -> `get`. Comments clarified per Codex review: events that carry their own `source` field (session_start, and pre-existing `skill_*`/connections events) override the injected process-level value. Co-Authored-By: Claude Opus 4.8 --- .../opencode/src/altimate/telemetry/index.ts | 14 +- packages/opencode/src/session/index.ts | 3 + packages/opencode/src/session/prompt.ts | 14 +- .../opencode/test/session/session.test.ts | 32 ++++ .../opencode/test/telemetry/telemetry.test.ts | 139 ++++++++++++++++++ 5 files changed, 192 insertions(+), 10 deletions(-) diff --git a/packages/opencode/src/altimate/telemetry/index.ts b/packages/opencode/src/altimate/telemetry/index.ts index 82a2beb8e..d5018e455 100644 --- a/packages/opencode/src/altimate/telemetry/index.ts +++ b/packages/opencode/src/altimate/telemetry/index.ts @@ -1,5 +1,6 @@ import { Account } from "@/account" import { Config } from "@/config/config" +import { Flag } from "@/flag/flag" import { Installation } from "@/installation" import { Log } from "@/util/log" import { createHash, randomUUID } from "crypto" @@ -1201,7 +1202,6 @@ export namespace Telemetry { let machineId = "" let sessionId = "" let projectId = "" - let clientSource = "cli" let appInsights: AppInsightsConfig | undefined let droppedEvents = 0 let initPromise: Promise | undefined @@ -1222,6 +1222,14 @@ export namespace Telemetry { } function toAppInsightsEnvelopes(events: Event[], cfg: AppInsightsConfig): object[] { + // Process-level client, read live from the env flag (defaults to "cli") so events + // emitted before any prompt runs — startup, connection setup, standalone CLI + // subcommands — are labelled correctly instead of stuck at a hardcoded default. + // Any event that already carries its own `source` field overrides this via the field + // loop below — session_start (the per-session client, from session.metadata.source) and + // a few pre-existing events (skill_*, connections) whose `source` means something else + // (cli/tui, connection origin). Events without their own `source` report this value. + const clientSource = Flag.ALTIMATE_CLI_CLIENT return events.map((event) => { const { type, timestamp, ...fields } = event as any const sid: string = fields.session_id ?? sessionId @@ -1351,10 +1359,6 @@ export namespace Telemetry { projectId = opts.projectId } - export function setSource(s: string): void { - clientSource = s - } - export function getContext() { return { sessionId, projectId } } diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 0ae3e86fd..84370311f 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -151,6 +151,9 @@ export namespace Session { archived: z.number().optional(), }), permission: PermissionNext.Ruleset.optional(), + // Open extension point for clients (e.g. VS Code extensions) to tag a session via + // POST /session. Only `source` is consumed today (session_start telemetry); kept as an + // open record so new keys need no schema change. metadata: z.record(z.string(), z.unknown()).optional(), revert: z .object({ diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index b50a7a78d..60c530d81 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -362,7 +362,6 @@ export namespace SessionPrompt { // altimate_change end // altimate_change start — session telemetry tracking await Telemetry.init() - Telemetry.setSource(Flag.ALTIMATE_CLI_CLIENT) Telemetry.setContext({ sessionId: sessionID, projectId: Instance.project?.id ?? "" }) const sessionStartTime = Date.now() let sessionTotalCost = 0 @@ -926,10 +925,15 @@ export namespace SessionPrompt { // altimate_change start — per-session source override: VS Code extensions set // metadata.source (e.g. "datamates", "poweruser") when creating the session via POST /session. // This lets both extensions share the same altimate serve process while producing - // distinguishable session_start telemetry. Falls back to the process-level - // Flag.ALTIMATE_CLI_CLIENT (set by ALTIMATE_CLI_CLIENT env var, e.g. "vscode") if - // the session carries no metadata source. - source: (session.metadata?.source as string | undefined) ?? Flag.ALTIMATE_CLI_CLIENT, + // distinguishable session_start telemetry. session_start carries this per-session value; + // events without their own `source` fall back to the process-level Flag.ALTIMATE_CLI_CLIENT + // injected in telemetry, so per-extension attribution of those events requires a join on + // session_id. metadata is arbitrary client JSON, so type-guard source and fall back to the + // flag if it is absent or not a string (a non-string would otherwise be routed to + // measurements downstream instead of appearing as the source property). + source: + (typeof session.metadata?.source === "string" ? session.metadata.source : undefined) ?? + Flag.ALTIMATE_CLI_CLIENT, // altimate_change end }) // altimate_change start — task intent classification (keyword/regex, zero LLM cost) diff --git a/packages/opencode/test/session/session.test.ts b/packages/opencode/test/session/session.test.ts index 0c18f92ba..9ba46829d 100644 --- a/packages/opencode/test/session/session.test.ts +++ b/packages/opencode/test/session/session.test.ts @@ -140,3 +140,35 @@ describe("step-finish token propagation via Bus event", () => { { timeout: 30000 }, ) }) + +describe("session metadata", () => { + test("persists and reads back metadata.source through create -> get", async () => { + await Instance.provide({ + directory: projectRoot, + fn: async () => { + const session = await Session.create({ metadata: { source: "datamates" } }) + expect(session.metadata).toEqual({ source: "datamates" }) + + const loaded = await Session.get(session.id) + expect(loaded.metadata).toEqual({ source: "datamates" }) + + await Session.remove(session.id) + }, + }) + }) + + test("metadata is undefined when not provided", async () => { + await Instance.provide({ + directory: projectRoot, + fn: async () => { + const session = await Session.create({}) + expect(session.metadata).toBeUndefined() + + const loaded = await Session.get(session.id) + expect(loaded.metadata).toBeUndefined() + + await Session.remove(session.id) + }, + }) + }) +}) diff --git a/packages/opencode/test/telemetry/telemetry.test.ts b/packages/opencode/test/telemetry/telemetry.test.ts index 2f0d2dce8..a9490888e 100644 --- a/packages/opencode/test/telemetry/telemetry.test.ts +++ b/packages/opencode/test/telemetry/telemetry.test.ts @@ -715,6 +715,145 @@ describe("telemetry.toAppInsightsEnvelopes (indirect)", () => { cleanup() } }) + + // --- source field (ALTIMATE_CLI_CLIENT injection + per-event override) --- + + function withClientEnv(value: string | undefined, fn: () => Promise): Promise { + const origAcc = process.env.ALTIMATE_CLI_CLIENT + const origOc = process.env.OPENCODE_CLIENT + delete process.env.OPENCODE_CLIENT + if (value === undefined) delete process.env.ALTIMATE_CLI_CLIENT + else process.env.ALTIMATE_CLI_CLIENT = value + return fn().finally(() => { + if (origAcc !== undefined) process.env.ALTIMATE_CLI_CLIENT = origAcc + else delete process.env.ALTIMATE_CLI_CLIENT + if (origOc !== undefined) process.env.OPENCODE_CLIENT = origOc + else delete process.env.OPENCODE_CLIENT + }) + } + + test("source on a generic event reflects the ALTIMATE_CLI_CLIENT env flag", async () => { + await withClientEnv("vscode", async () => { + const { fetchCalls, cleanup } = await initWithMockedFetch() + try { + Telemetry.track({ + type: "session_end", + timestamp: 1700000000000, + session_id: "sess-1", + total_cost: 0.05, + total_tokens: 1500, + tool_call_count: 10, + duration_ms: 30000, + }) + await Telemetry.flush() + expect(JSON.parse(fetchCalls[0].body)[0].data.baseData.properties.source).toBe("vscode") + } finally { + cleanup() + } + }) + }) + + test('source defaults to "cli" when no client env is set', async () => { + await withClientEnv(undefined, async () => { + const { fetchCalls, cleanup } = await initWithMockedFetch() + try { + Telemetry.track({ + type: "session_end", + timestamp: 1700000000000, + session_id: "sess-1", + total_cost: 0.01, + total_tokens: 10, + tool_call_count: 1, + duration_ms: 100, + }) + await Telemetry.flush() + expect(JSON.parse(fetchCalls[0].body)[0].data.baseData.properties.source).toBe("cli") + } finally { + cleanup() + } + }) + }) + + test("per-event source (session_start) overrides the process-level flag", async () => { + await withClientEnv("vscode", async () => { + const { fetchCalls, cleanup } = await initWithMockedFetch() + try { + Telemetry.track({ + type: "session_start", + timestamp: 1700000000000, + session_id: "sess-1", + model_id: "m", + provider_id: "p", + agent: "a", + project_id: "proj", + os: "linux", + arch: "x64", + node_version: "v22.0.0", + source: "datamates", + }) + await Telemetry.flush() + // Event-level source wins over the global "vscode" flag. + expect(JSON.parse(fetchCalls[0].body)[0].data.baseData.properties.source).toBe("datamates") + } finally { + cleanup() + } + }) + }) + + test("session_start without a source falls back to the process-level flag", async () => { + await withClientEnv("vscode", async () => { + const { fetchCalls, cleanup } = await initWithMockedFetch() + try { + Telemetry.track({ + type: "session_start", + timestamp: 1700000000000, + session_id: "sess-1", + model_id: "m", + provider_id: "p", + agent: "a", + project_id: "proj", + os: "linux", + arch: "x64", + node_version: "v22.0.0", + }) + await Telemetry.flush() + expect(JSON.parse(fetchCalls[0].body)[0].data.baseData.properties.source).toBe("vscode") + } finally { + cleanup() + } + }) + }) + + test("a non-string source is routed to measurements, not properties (why session_start type-guards it)", async () => { + await withClientEnv("vscode", async () => { + const { fetchCalls, cleanup } = await initWithMockedFetch() + try { + // Simulates a malformed per-event source reaching the envelope (e.g. if the prompt + // guard were removed and a client passed metadata.source as a number). The field loop + // routes numbers to measurements, so `properties.source` would silently retain the + // injected process-level flag rather than the intended value — hence the guard. + Telemetry.track({ + type: "session_start", + timestamp: 1700000000000, + session_id: "sess-1", + model_id: "m", + provider_id: "p", + agent: "a", + project_id: "proj", + os: "linux", + arch: "x64", + node_version: "v22.0.0", + source: 42 as unknown as string, + }) + await Telemetry.flush() + const baseData = JSON.parse(fetchCalls[0].body)[0].data.baseData + expect(baseData.measurements.source).toBe(42) + expect(baseData.properties.source).toBe("vscode") + } finally { + cleanup() + } + }) + }) }) // --------------------------------------------------------------------------- From 6c60c31ea6969087c0a43cb444c48ffe8cd9bb1c Mon Sep 17 00:00:00 2001 From: anandgupta42 Date: Tue, 7 Jul 2026 07:46:34 -0700 Subject: [PATCH 6/6] =?UTF-8?q?fix:=20[PR-968]=20address=20code=20review?= =?UTF-8?q?=20=E2=80=94=20fork=20metadata,=20SDK=20artifacts,=20schema=20r?= =?UTF-8?q?euse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `Session.fork` now inherits `original.metadata`, so forked sessions keep the origin (`source`) of the session they were forked from instead of falling back to the process-level `Flag.ALTIMATE_CLI_CLIENT` in their `session_start` telemetry. Adds a fork-inherits-metadata test. - Regenerate the committed SDK/OpenAPI artifacts for the new `metadata` field: `packages/sdk/openapi.json` (`Session`, `GlobalSession`, and the `/session` POST body) and `packages/sdk/js/src/v2/gen/types.gen.ts` (`Session`, `GlobalSession`, `SessionCreateData.body`). Applied only the `metadata` delta — verified byte-for-byte against the real `@hey-api/openapi-ts` output — to avoid importing unrelated pre-existing spec drift. - Reuse `Info.shape.metadata` in the `Session.create` request schema instead of re-declaring the Zod record, matching the `permission` pattern. Co-Authored-By: Claude Opus 4.8 --- packages/opencode/src/session/index.ts | 6 +++++- .../opencode/test/session/session.test.ts | 14 +++++++++++++ packages/sdk/js/src/v2/gen/types.gen.ts | 9 ++++++++ packages/sdk/openapi.json | 21 +++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index 84370311f..41f8b439c 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -229,7 +229,7 @@ export namespace Session { title: z.string().optional(), permission: Info.shape.permission, workspaceID: WorkspaceID.zod.optional(), - metadata: z.record(z.string(), z.unknown()).optional(), + metadata: Info.shape.metadata, }) .optional(), async (input) => { @@ -257,6 +257,10 @@ export namespace Session { directory: Instance.directory, workspaceID: original.workspaceID, title, + // Inherit the source label so a forked session keeps the origin of the session + // it was forked from (e.g. "datamates"/"poweruser") instead of falling back to + // the process-level Flag.ALTIMATE_CLI_CLIENT in its session_start telemetry. + metadata: original.metadata, }) const msgs = await messages({ sessionID: input.sessionID }) const idMap = new Map() diff --git a/packages/opencode/test/session/session.test.ts b/packages/opencode/test/session/session.test.ts index 9ba46829d..8c8761d81 100644 --- a/packages/opencode/test/session/session.test.ts +++ b/packages/opencode/test/session/session.test.ts @@ -171,4 +171,18 @@ describe("session metadata", () => { }, }) }) + + test("fork inherits metadata (source) from the original session", async () => { + await Instance.provide({ + directory: projectRoot, + fn: async () => { + const original = await Session.create({ metadata: { source: "datamates" } }) + const forked = await Session.fork({ sessionID: original.id }) + expect(forked.metadata).toEqual({ source: "datamates" }) + + await Session.remove(forked.id) + await Session.remove(original.id) + }, + }) + }) }) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 40a2d6988..6d530a1df 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -834,6 +834,9 @@ export type Session = { archived?: number } permission?: PermissionRuleset + metadata?: { + [key: string]: unknown + } revert?: { messageID: string partID?: string @@ -1792,6 +1795,9 @@ export type GlobalSession = { archived?: number } permission?: PermissionRuleset + metadata?: { + [key: string]: unknown + } revert?: { messageID: string partID?: string @@ -2916,6 +2922,9 @@ export type SessionCreateData = { title?: string permission?: PermissionRuleset workspaceID?: string + metadata?: { + [key: string]: unknown + } } path?: never query?: { diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index cfb4affc7..e37cc53b4 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -1941,6 +1941,13 @@ "workspaceID": { "type": "string", "pattern": "^wrk.*" + }, + "metadata": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} } } } @@ -9352,6 +9359,13 @@ "permission": { "$ref": "#/components/schemas/PermissionRuleset" }, + "metadata": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, "revert": { "type": "object", "properties": { @@ -11638,6 +11652,13 @@ "permission": { "$ref": "#/components/schemas/PermissionRuleset" }, + "metadata": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": {} + }, "revert": { "type": "object", "properties": {