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 diff --git a/packages/opencode/src/altimate/telemetry/index.ts b/packages/opencode/src/altimate/telemetry/index.ts index b2b0e7103..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" @@ -62,6 +63,7 @@ export namespace Telemetry { os: string arch: string node_version: string + source?: string } // altimate_change end | { @@ -1220,12 +1222,21 @@ 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 const properties: Record = { cli_version: Installation.VERSION, + source: clientSource, project_id: fields.project_id ?? projectId, ...(machineId && { machine_id: machineId }), } diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index f2be3e10a..41f8b439c 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,10 @@ 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({ messageID: MessageID.zod, @@ -223,6 +229,7 @@ export namespace Session { title: z.string().optional(), permission: Info.shape.permission, workspaceID: WorkspaceID.zod.optional(), + metadata: Info.shape.metadata, }) .optional(), async (input) => { @@ -232,6 +239,7 @@ export namespace Session { title: input?.title, permission: input?.permission, workspaceID: input?.workspaceID, + metadata: input?.metadata, }) }, ) @@ -249,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() @@ -301,6 +313,7 @@ export namespace Session { workspaceID?: WorkspaceID directory: string permission?: PermissionNext.Ruleset + metadata?: Record }) { const result: Info = { id: SessionID.descending(input.id), @@ -312,6 +325,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 72027eedf..60c530d81 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -922,6 +922,19 @@ 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. 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) 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(), diff --git a/packages/opencode/test/session/session.test.ts b/packages/opencode/test/session/session.test.ts index 0c18f92ba..8c8761d81 100644 --- a/packages/opencode/test/session/session.test.ts +++ b/packages/opencode/test/session/session.test.ts @@ -140,3 +140,49 @@ 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) + }, + }) + }) + + 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/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() + } + }) + }) }) // --------------------------------------------------------------------------- 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": {