@oar-sh/oar 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/extensions/web-relay/README.md +190 -0
- package/.github/extensions/web-relay/extension.mjs +972 -0
- package/.github/extensions/web-relay/model-api/model-switching.mjs +338 -0
- package/.github/extensions/web-relay/polling/heartbeat.mjs +55 -0
- package/.github/extensions/web-relay/polling/polling-loop.mjs +1105 -0
- package/.github/extensions/web-relay/runtime/api-client.mjs +34 -0
- package/.github/extensions/web-relay/runtime/banner-state.mjs +126 -0
- package/.github/extensions/web-relay/runtime/config-loader.mjs +104 -0
- package/.github/extensions/web-relay/runtime/debug-log.mjs +27 -0
- package/.github/extensions/web-relay/runtime/send-and-wait-errors.mjs +170 -0
- package/.github/extensions/web-relay/runtime/session-binding.mjs +78 -0
- package/.github/extensions/web-relay/runtime/session-io.mjs +349 -0
- package/.github/extensions/web-relay/runtime/session-join-retry.mjs +44 -0
- package/.github/extensions/web-relay/runtime/session-registry.mjs +29 -0
- package/.github/extensions/web-relay/runtime/session-runtime-manager.mjs +156 -0
- package/.github/extensions/web-relay/runtime/session-sync-bridge.mjs +74 -0
- package/.github/extensions/web-relay/runtime/worker-websocket-link.mjs +325 -0
- package/.github/extensions/web-relay/runtime/workspace-root.mjs +55 -0
- package/.github/extensions/web-relay/server-lifecycle/managed-server.mjs +386 -0
- package/.github/extensions/web-relay/skills/prompt-context.mjs +160 -0
- package/.github/extensions/web-relay/skills/question-bridge.mjs +141 -0
- package/.github/extensions/web-relay/skills/question-routing-hooks.mjs +463 -0
- package/.github/extensions/web-relay/skills/reasoning-stream.mjs +285 -0
- package/.github/extensions/web-relay/skills/subagent-lifecycle.mjs +345 -0
- package/.github/extensions/web-relay/skills/tool-activity.mjs +243 -0
- package/.github/extensions/web-relay/utils/process-utils.mjs +25 -0
- package/.github/extensions/web-relay/utils/token-generation.mjs +58 -0
- package/CHANGELOG.md +25 -0
- package/LICENSE +21 -0
- package/README.md +717 -0
- package/bin/bat/oar.bat +46 -0
- package/bin/oar.js +665 -0
- package/package.json +68 -0
- package/server/README.md +1379 -0
- package/server/claude-worker/claude-attachments.mjs +134 -0
- package/server/claude-worker/claude-preview-tool.mjs +57 -0
- package/server/claude-worker/claude-sdk-adapter.mjs +420 -0
- package/server/claude-worker/claude-session-process.mjs +2391 -0
- package/server/claude-worker/claude-session-test-harness.mjs +236 -0
- package/server/claude-worker/claude-session-worker.mjs +186 -0
- package/server/claude-worker/claude-transcript-relocator.mjs +189 -0
- package/server/claude-worker/claude-turn-publisher.mjs +291 -0
- package/server/claude-worker/fixtures/workflow-run-record.json +1 -0
- package/server/claude-worker/sdk-message-normalizer.mjs +629 -0
- package/server/claude-worker/workflow-progress-digest.mjs +269 -0
- package/server/copilot-sdk-runtime.mjs +126 -0
- package/server/copilot-worker/copilot-attachments.mjs +97 -0
- package/server/copilot-worker/copilot-byok-provider.mjs +121 -0
- package/server/copilot-worker/copilot-continuation-signals.mjs +349 -0
- package/server/copilot-worker/copilot-plan-board.mjs +110 -0
- package/server/copilot-worker/copilot-prompt-context.mjs +86 -0
- package/server/copilot-worker/copilot-question-bridge.mjs +216 -0
- package/server/copilot-worker/copilot-sdk-adapter.mjs +494 -0
- package/server/copilot-worker/copilot-sdk-event-normalizer.mjs +800 -0
- package/server/copilot-worker/copilot-sdk-session-process.mjs +1729 -0
- package/server/copilot-worker/copilot-sdk-session-worker.mjs +167 -0
- package/server/copilot-worker/copilot-sdk-test-harness.mjs +329 -0
- package/server/copilot-worker/fixtures/abort-turn.json +588 -0
- package/server/copilot-worker/fixtures/ask-user-turn.json +1154 -0
- package/server/copilot-worker/fixtures/background-timer-continuation.json +445 -0
- package/server/copilot-worker/fixtures/background-timer-turn.json +497 -0
- package/server/copilot-worker/fixtures/happy-turn.json +566 -0
- package/server/copilot-worker/fixtures/quota-turn.json +647 -0
- package/server/copilot-worker/fixtures/reasoning-turn.json +625 -0
- package/server/copilot-worker/fixtures/resume-turn.json +700 -0
- package/server/copilot-worker/fixtures/subagent-turn.json +1378 -0
- package/server/copilot-worker/fixtures/tool-permission-turn.json +1293 -0
- package/server/cursor-worker/cursor-ask-user-tool.mjs +83 -0
- package/server/cursor-worker/cursor-attachments.mjs +88 -0
- package/server/cursor-worker/cursor-context-usage.mjs +85 -0
- package/server/cursor-worker/cursor-preview-tool.mjs +44 -0
- package/server/cursor-worker/cursor-sdk-adapter.mjs +554 -0
- package/server/cursor-worker/cursor-session-worker.mjs +145 -0
- package/server/cursor-worker/cursor-subagent-roster.mjs +81 -0
- package/server/cursor-worker/cursor-turn-runner.mjs +717 -0
- package/server/cursor-worker/sdk-message-normalizer.mjs +485 -0
- package/server/db-schema.mjs +932 -0
- package/server/features.mjs +106 -0
- package/server/grok-worker/acp-client.mjs +434 -0
- package/server/grok-worker/acp-host-services.mjs +277 -0
- package/server/grok-worker/grok-context-usage.mjs +77 -0
- package/server/grok-worker/grok-sdk-adapter.mjs +334 -0
- package/server/grok-worker/grok-session-worker.mjs +145 -0
- package/server/grok-worker/grok-turn-runner.mjs +523 -0
- package/server/grok-worker/sdk-message-normalizer.mjs +311 -0
- package/server/migrations/0001-add-structured-answer.mjs +80 -0
- package/server/migrations/0002-recent-workspace-roots-path-key.mjs +121 -0
- package/server/migrations/0003-push-subscriptions.mjs +87 -0
- package/server/public/app/action-confirmations.js +267 -0
- package/server/public/app/activity-replay-state.mjs +149 -0
- package/server/public/app/api-client.js +1082 -0
- package/server/public/app/ask-user-view.js +545 -0
- package/server/public/app/attachments-view.js +2112 -0
- package/server/public/app/auto-compact-window-options.mjs +59 -0
- package/server/public/app/background-tasks-view.mjs +494 -0
- package/server/public/app/bootstrap.js +4347 -0
- package/server/public/app/claude-auth-ui.js +394 -0
- package/server/public/app/claude-thinking-options.mjs +38 -0
- package/server/public/app/cli-install-ui.js +455 -0
- package/server/public/app/code-copy.mjs +85 -0
- package/server/public/app/composer-attachment-cache.mjs +151 -0
- package/server/public/app/composer-control-state.mjs +107 -0
- package/server/public/app/composer-paste.mjs +132 -0
- package/server/public/app/context-tier-options.mjs +68 -0
- package/server/public/app/context-usage-view.mjs +338 -0
- package/server/public/app/conversation-draft-timestamp-utils.mjs +11 -0
- package/server/public/app/conversation-preferences.mjs +91 -0
- package/server/public/app/conversation-provider-indicator.mjs +88 -0
- package/server/public/app/conversation-view.js +2898 -0
- package/server/public/app/cwd-menu-placement.mjs +85 -0
- package/server/public/app/cwd-picker.js +605 -0
- package/server/public/app/emoji-view.js +101 -0
- package/server/public/app/external-link-policy.mjs +80 -0
- package/server/public/app/file-preview-navigation.mjs +51 -0
- package/server/public/app/font-scaling.js +262 -0
- package/server/public/app/git-changes-view.js +339 -0
- package/server/public/app/git-diff-model.mjs +109 -0
- package/server/public/app/grok-auth-ui.js +387 -0
- package/server/public/app/image-downscale.mjs +174 -0
- package/server/public/app/in-flight-snapshot.mjs +40 -0
- package/server/public/app/infinite-loader.js +164 -0
- package/server/public/app/journal-view.js +1141 -0
- package/server/public/app/known-cwd-options.mjs +41 -0
- package/server/public/app/launch-cwd-path.mjs +23 -0
- package/server/public/app/live-message-dedupe.mjs +57 -0
- package/server/public/app/message-search-view.js +242 -0
- package/server/public/app/model-selector-options.mjs +31 -0
- package/server/public/app/model-vision-support.mjs +56 -0
- package/server/public/app/new-conversation-model-choice.mjs +67 -0
- package/server/public/app/plan-usage-view.mjs +307 -0
- package/server/public/app/preview-cards.mjs +273 -0
- package/server/public/app/preview-command.mjs +103 -0
- package/server/public/app/push-settings.js +370 -0
- package/server/public/app/pwa-install.js +456 -0
- package/server/public/app/question-schema-view.mjs +71 -0
- package/server/public/app/reasoning-effort-labels.mjs +42 -0
- package/server/public/app/relay-board-view.js +117 -0
- package/server/public/app/relay-dot-state.mjs +69 -0
- package/server/public/app/relay-error-ctas.mjs +52 -0
- package/server/public/app/relay-prompt-sanitizer.mjs +114 -0
- package/server/public/app/relay-thoughts.mjs +98 -0
- package/server/public/app/relay-watchdog-policy.mjs +29 -0
- package/server/public/app/repo-browser-preferences.mjs +79 -0
- package/server/public/app/repo-browser-tree-state.mjs +97 -0
- package/server/public/app/router.js +657 -0
- package/server/public/app/selection-guard.mjs +93 -0
- package/server/public/app/settings-modal.js +1484 -0
- package/server/public/app/settings-tabs.js +129 -0
- package/server/public/app/slash-autocomplete.mjs +188 -0
- package/server/public/app/slash-commands.mjs +183 -0
- package/server/public/app/socket-handlers.js +703 -0
- package/server/public/app/status-store.mjs +242 -0
- package/server/public/app/status-view.mjs +315 -0
- package/server/public/app/store.js +1454 -0
- package/server/public/app/stream-state.mjs +73 -0
- package/server/public/app/streaming-dom-patch.mjs +23 -0
- package/server/public/app/sync-outbox.mjs +208 -0
- package/server/public/app/tap-actions.js +62 -0
- package/server/public/app/thread-order.mjs +54 -0
- package/server/public/app/tmux-inspector-view.js +422 -0
- package/server/public/app/transcript-separators.mjs +282 -0
- package/server/public/app-icon-192.png +0 -0
- package/server/public/app-icon-512.png +0 -0
- package/server/public/app-icon.svg +4 -0
- package/server/public/favicon.ico +0 -0
- package/server/public/index.html +5743 -0
- package/server/public/manifest.webmanifest +32 -0
- package/server/public/sw.js +307 -0
- package/server/relay-exit-codes.mjs +4 -0
- package/server/relay-self-restart.mjs +170 -0
- package/server/relay-tools.md +27 -0
- package/server/relay.mjs +1026 -0
- package/server/repositories/image-conversation-repository.mjs +317 -0
- package/server/repositories/message-repository.mjs +434 -0
- package/server/repositories/question-repository.mjs +161 -0
- package/server/repositories/session-repository.mjs +320 -0
- package/server/repositories/share-visibility-statements.mjs +24 -0
- package/server/routes/ask-user-routes.mjs +292 -0
- package/server/routes/cache-routes.mjs +44 -0
- package/server/routes/git-routes.mjs +92 -0
- package/server/routes/messages-routes-test-harness.mjs +111 -0
- package/server/routes/messages-routes.mjs +7107 -0
- package/server/routes/preview-routes.mjs +108 -0
- package/server/routes/push-routes.mjs +59 -0
- package/server/routes/relay-board-routes.mjs +268 -0
- package/server/routes/sessions-routes.mjs +6150 -0
- package/server/server-runtime.mjs +7463 -0
- package/server/server.js +24 -0
- package/server/services/ask-user-routing-service.mjs +117 -0
- package/server/services/cache-rebuild-service.mjs +236 -0
- package/server/services/claude-auth-service.mjs +578 -0
- package/server/services/claude-context-usage.mjs +240 -0
- package/server/services/claude-session-root-service.mjs +228 -0
- package/server/services/cli-install-service.mjs +1152 -0
- package/server/services/cli-process-runner.mjs +181 -0
- package/server/services/cloudflared-tunnel-service.mjs +358 -0
- package/server/services/context-snapshot-service.mjs +499 -0
- package/server/services/context-usage-view.mjs +160 -0
- package/server/services/conversation-preferences-service.mjs +208 -0
- package/server/services/cursor-dashboard-usage.mjs +217 -0
- package/server/services/cursor-session-root-service.mjs +62 -0
- package/server/services/delete-archive-service.mjs +154 -0
- package/server/services/drives-path-helpers.mjs +66 -0
- package/server/services/fixtures/claude-auth-stub.sh +62 -0
- package/server/services/fixtures/cli-install-stub.sh +75 -0
- package/server/services/fixtures/grok-stub.sh +86 -0
- package/server/services/generated-image-cleanup-service.mjs +114 -0
- package/server/services/git-changes-service.mjs +195 -0
- package/server/services/github-billing-usage.mjs +133 -0
- package/server/services/grok-auth-service.mjs +593 -0
- package/server/services/grok-billing-usage.mjs +120 -0
- package/server/services/image-context-service.mjs +106 -0
- package/server/services/image-operation-service.mjs +135 -0
- package/server/services/keyed-mutex-service.mjs +82 -0
- package/server/services/mime-sniffer.mjs +118 -0
- package/server/services/oar-cli-helpers.mjs +109 -0
- package/server/services/oar-state-migration-service.mjs +243 -0
- package/server/services/plan-usage-claude.mjs +387 -0
- package/server/services/plan-usage-contract.mjs +271 -0
- package/server/services/plan-usage-copilot.mjs +421 -0
- package/server/services/plan-usage-cursor.mjs +566 -0
- package/server/services/plan-usage-grok.mjs +436 -0
- package/server/services/plan-usage-service.mjs +487 -0
- package/server/services/preview-health-probe.mjs +82 -0
- package/server/services/preview-proxy-server.mjs +498 -0
- package/server/services/preview-registry-service.mjs +457 -0
- package/server/services/preview-static-handler.mjs +237 -0
- package/server/services/provider-model-selection.mjs +91 -0
- package/server/services/provider-reasoning-effort.mjs +94 -0
- package/server/services/push-active-device-service.mjs +57 -0
- package/server/services/push-dispatch-service.mjs +459 -0
- package/server/services/rate-limit-service.mjs +63 -0
- package/server/services/relay-bridge-owner-service.mjs +84 -0
- package/server/services/relay-cli-launcher-service.mjs +209 -0
- package/server/services/relay-prompt-sanitizer.mjs +137 -0
- package/server/services/relay-queue-gate-service.mjs +38 -0
- package/server/services/relay-restart-orchestrator-service.mjs +935 -0
- package/server/services/relay-singleton-guard.mjs +124 -0
- package/server/services/safe-served-content.mjs +57 -0
- package/server/services/sdk-session-import-service.mjs +271 -0
- package/server/services/sdk-session-sync-service.mjs +207 -0
- package/server/services/session-history-refresh-service.mjs +497 -0
- package/server/services/session-transcript-service.mjs +419 -0
- package/server/services/session-worker-launch-service.mjs +786 -0
- package/server/services/session-worker-process-service.mjs +319 -0
- package/server/services/session-worker-registry-service.mjs +194 -0
- package/server/services/session-worker-stop-service.mjs +141 -0
- package/server/services/session-worker-supervisor-service.mjs +898 -0
- package/server/services/session-worker-websocket-service.mjs +483 -0
- package/server/services/ssh-tunnel-manager-service.mjs +423 -0
- package/server/services/status-event-service.mjs +158 -0
- package/server/services/tmux-inspector-access-policy.mjs +77 -0
- package/server/services/tmux-inspector-socket-service.mjs +191 -0
- package/server/services/tmux-inspector-stream-service.mjs +552 -0
- package/server/services/tunnel-worker-path-guard.mjs +107 -0
- package/server/services/upload-sweep.mjs +60 -0
- package/server/services/usage-snapshot-helpers.mjs +209 -0
- package/server/services/windows-autostart-service.mjs +115 -0
- package/server/services/workspace-root-defaults-service.mjs +34 -0
- package/server/services/workspace-root-path-policy.mjs +196 -0
- package/server/services/workspace-root-relaunch-service.mjs +132 -0
- package/server/services/workspace-symlink-guard.mjs +54 -0
- package/server/tools/capture-proxy.mjs +173 -0
- package/server/tty-console-bootstrap.mjs +75 -0
- package/server/uploads/README.md +36 -0
- package/server/uploads/image_input.md +540 -0
- package/server/windows-terminal-launcher.mjs +29 -0
- package/server/workspace-root.mjs +87 -0
- package/shared/ask-user-bridge.mjs +131 -0
- package/shared/auto-compact-window.mjs +116 -0
- package/shared/background-task-timeout.mjs +52 -0
- package/shared/claude-thinking.mjs +101 -0
- package/shared/context-window-fallbacks.mjs +148 -0
- package/shared/control-poller.mjs +115 -0
- package/shared/empty-turn-completion.mjs +20 -0
- package/shared/model-catalog-freshness.mjs +11 -0
- package/shared/model-descriptors.mjs +118 -0
- package/shared/model-id.mjs +147 -0
- package/shared/model-refresh.mjs +24 -0
- package/shared/openai-reasoning.mjs +13 -0
- package/shared/plan-lines.mjs +13 -0
- package/shared/preview-instructions.mjs +77 -0
- package/shared/preview-tool-core.mjs +191 -0
- package/shared/provider-routing.mjs +29 -0
- package/shared/question-schema.mjs +315 -0
- package/shared/question-timeout.mjs +18 -0
- package/shared/stream-emit-gating.mjs +28 -0
- package/shared/subagent-run-id.mjs +17 -0
- package/shared/thought-cap.mjs +13 -0
- package/shared/tool-activity.mjs +98 -0
- package/shared/turn-ceiling.mjs +53 -0
- package/shared/worker-bootstrap.mjs +44 -0
- package/shared/worker-crash-guard.mjs +60 -0
|
@@ -0,0 +1,932 @@
|
|
|
1
|
+
// SQLite schema DDL and upgrade migrations for the copilot-remote database.
|
|
2
|
+
//
|
|
3
|
+
// Extracted verbatim from server-runtime.mjs so tests can build a real replica
|
|
4
|
+
// of the production schema instead of hand-copying CREATE TABLE statements.
|
|
5
|
+
// applySchema(db) runs the exact same code path production boot uses: the
|
|
6
|
+
// CREATE TABLE IF NOT EXISTS block for fresh databases, then the ALTER TABLE
|
|
7
|
+
// upgrade sequence for existing ones. On a fresh :memory: database the ALTERs
|
|
8
|
+
// are all no-ops (every column already exists), so the end state is the true
|
|
9
|
+
// final schema either way.
|
|
10
|
+
import { rebuildRecentWorkspaceRootsTable } from './migrations/0002-recent-workspace-roots-path-key.mjs';
|
|
11
|
+
import { ensurePushSubscriptionsTable } from './migrations/0003-push-subscriptions.mjs';
|
|
12
|
+
import { migrateImageConversationSchema } from './repositories/image-conversation-repository.mjs';
|
|
13
|
+
|
|
14
|
+
// Mirrors DEFAULT_RELAY_MODE in server-runtime.mjs; used only by the one-time
|
|
15
|
+
// legacy per-mode preference carry-over below.
|
|
16
|
+
const DEFAULT_RELAY_MODE = 'agent';
|
|
17
|
+
|
|
18
|
+
// Base DDL for a fresh database. Existing databases reach the same shape via
|
|
19
|
+
// the upgrade sequence in applySchema below.
|
|
20
|
+
export const SCHEMA_SQL = `
|
|
21
|
+
CREATE TABLE IF NOT EXISTS conversations (
|
|
22
|
+
id TEXT PRIMARY KEY,
|
|
23
|
+
title TEXT NOT NULL,
|
|
24
|
+
title_source TEXT NOT NULL DEFAULT 'auto',
|
|
25
|
+
sdk_session_id TEXT,
|
|
26
|
+
preferred_relay_mode TEXT,
|
|
27
|
+
preferred_model TEXT,
|
|
28
|
+
preferred_reasoning_effort TEXT,
|
|
29
|
+
auto_compact_window INTEGER,
|
|
30
|
+
thinking_enabled INTEGER,
|
|
31
|
+
thinking_display TEXT,
|
|
32
|
+
configured_workspace_root_path TEXT,
|
|
33
|
+
runtime_workspace_root_path TEXT,
|
|
34
|
+
archived INTEGER NOT NULL DEFAULT 0,
|
|
35
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
36
|
+
compacted_into TEXT,
|
|
37
|
+
compacted_from TEXT,
|
|
38
|
+
summary_seed TEXT,
|
|
39
|
+
seed_pending INTEGER NOT NULL DEFAULT 0,
|
|
40
|
+
draft_text TEXT,
|
|
41
|
+
draft_updated_at TEXT,
|
|
42
|
+
draft_updated_by_client_id TEXT,
|
|
43
|
+
draft_attachments TEXT,
|
|
44
|
+
created_at TEXT NOT NULL,
|
|
45
|
+
updated_at TEXT NOT NULL
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
49
|
+
id TEXT PRIMARY KEY,
|
|
50
|
+
conversation_id TEXT NOT NULL,
|
|
51
|
+
role TEXT NOT NULL,
|
|
52
|
+
text TEXT NOT NULL,
|
|
53
|
+
model TEXT,
|
|
54
|
+
mode TEXT,
|
|
55
|
+
attachments TEXT,
|
|
56
|
+
model_requested TEXT,
|
|
57
|
+
model_actual TEXT,
|
|
58
|
+
model_origin TEXT,
|
|
59
|
+
hidden_from_shares INTEGER NOT NULL DEFAULT 0,
|
|
60
|
+
share_hidden_at TEXT,
|
|
61
|
+
timestamp TEXT NOT NULL,
|
|
62
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
CREATE INDEX IF NOT EXISTS idx_messages_conv ON messages(conversation_id, timestamp);
|
|
66
|
+
|
|
67
|
+
CREATE TABLE IF NOT EXISTS queue (
|
|
68
|
+
id TEXT PRIMARY KEY,
|
|
69
|
+
conversation_id TEXT NOT NULL,
|
|
70
|
+
runtime_session_id TEXT,
|
|
71
|
+
is_new_conversation INTEGER NOT NULL DEFAULT 0,
|
|
72
|
+
model TEXT,
|
|
73
|
+
model_variant_id TEXT,
|
|
74
|
+
reasoning_effort TEXT,
|
|
75
|
+
context_tier TEXT,
|
|
76
|
+
relay_mode TEXT NOT NULL DEFAULT 'agent',
|
|
77
|
+
text TEXT NOT NULL,
|
|
78
|
+
attachments TEXT,
|
|
79
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
80
|
+
timestamp TEXT NOT NULL,
|
|
81
|
+
processing_at TEXT,
|
|
82
|
+
response_message_id TEXT,
|
|
83
|
+
response TEXT,
|
|
84
|
+
retry_count INTEGER NOT NULL DEFAULT 0,
|
|
85
|
+
next_attempt_at TEXT,
|
|
86
|
+
owner_sdk_session_id TEXT,
|
|
87
|
+
owner_assigned_at TEXT,
|
|
88
|
+
owner_lease_expires_at TEXT,
|
|
89
|
+
owner_last_claimed_at TEXT,
|
|
90
|
+
parked_at TEXT,
|
|
91
|
+
parked_target_session_id TEXT,
|
|
92
|
+
parked_transaction_id TEXT,
|
|
93
|
+
parked_reason TEXT
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
CREATE INDEX IF NOT EXISTS idx_queue_status ON queue(status, timestamp);
|
|
97
|
+
|
|
98
|
+
CREATE TABLE IF NOT EXISTS message_usage_snapshots (
|
|
99
|
+
response_message_id TEXT PRIMARY KEY,
|
|
100
|
+
queue_message_id TEXT,
|
|
101
|
+
conversation_id TEXT NOT NULL,
|
|
102
|
+
source TEXT NOT NULL DEFAULT 'live',
|
|
103
|
+
stale INTEGER NOT NULL DEFAULT 0,
|
|
104
|
+
premium_remaining REAL,
|
|
105
|
+
premium_entitlement REAL,
|
|
106
|
+
premium_used_percent REAL,
|
|
107
|
+
premium_delta_used REAL,
|
|
108
|
+
chat_remaining REAL,
|
|
109
|
+
chat_entitlement REAL,
|
|
110
|
+
chat_used_percent REAL,
|
|
111
|
+
chat_delta_used REAL,
|
|
112
|
+
plan_remaining REAL,
|
|
113
|
+
plan_entitlement REAL,
|
|
114
|
+
plan_used_percent REAL,
|
|
115
|
+
plan_delta_used REAL,
|
|
116
|
+
captured_at TEXT NOT NULL,
|
|
117
|
+
FOREIGN KEY (response_message_id) REFERENCES messages(id) ON DELETE CASCADE,
|
|
118
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
CREATE INDEX IF NOT EXISTS idx_message_usage_conv_time ON message_usage_snapshots(conversation_id, captured_at DESC);
|
|
122
|
+
CREATE INDEX IF NOT EXISTS idx_message_usage_queue_id ON message_usage_snapshots(queue_message_id);
|
|
123
|
+
|
|
124
|
+
CREATE TABLE IF NOT EXISTS runtime_sessions (
|
|
125
|
+
id TEXT PRIMARY KEY,
|
|
126
|
+
conversation_id TEXT NOT NULL UNIQUE,
|
|
127
|
+
sdk_session_id TEXT,
|
|
128
|
+
strategy TEXT NOT NULL DEFAULT 'isolated',
|
|
129
|
+
runtime_key TEXT NOT NULL,
|
|
130
|
+
model TEXT,
|
|
131
|
+
provider_type TEXT NOT NULL DEFAULT 'github',
|
|
132
|
+
provider_model TEXT,
|
|
133
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
134
|
+
created_at TEXT NOT NULL,
|
|
135
|
+
last_used_at TEXT NOT NULL,
|
|
136
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
CREATE INDEX IF NOT EXISTS idx_runtime_sessions_last_used ON runtime_sessions(last_used_at DESC);
|
|
140
|
+
|
|
141
|
+
CREATE TABLE IF NOT EXISTS deleted_sdk_sessions (
|
|
142
|
+
sdk_session_id TEXT PRIMARY KEY,
|
|
143
|
+
deleted_at TEXT NOT NULL
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
CREATE TABLE IF NOT EXISTS sdk_delete_requests (
|
|
147
|
+
sdk_session_id TEXT PRIMARY KEY,
|
|
148
|
+
conversation_id TEXT,
|
|
149
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
150
|
+
requested_at TEXT NOT NULL,
|
|
151
|
+
updated_at TEXT NOT NULL,
|
|
152
|
+
processing_at TEXT,
|
|
153
|
+
retry_count INTEGER NOT NULL DEFAULT 0,
|
|
154
|
+
next_attempt_at TEXT,
|
|
155
|
+
last_error TEXT
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
CREATE INDEX IF NOT EXISTS idx_sdk_delete_requests_status
|
|
159
|
+
ON sdk_delete_requests(status, requested_at, next_attempt_at);
|
|
160
|
+
|
|
161
|
+
CREATE TABLE IF NOT EXISTS sdk_session_imports (
|
|
162
|
+
sdk_session_id TEXT PRIMARY KEY,
|
|
163
|
+
conversation_id TEXT,
|
|
164
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
165
|
+
attempt_count INTEGER NOT NULL DEFAULT 0,
|
|
166
|
+
started_at TEXT,
|
|
167
|
+
completed_at TEXT,
|
|
168
|
+
source_started_at TEXT,
|
|
169
|
+
source_modified_at TEXT,
|
|
170
|
+
updated_at TEXT NOT NULL,
|
|
171
|
+
last_error TEXT
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
CREATE INDEX IF NOT EXISTS idx_sdk_session_imports_status
|
|
175
|
+
ON sdk_session_imports(status, updated_at);
|
|
176
|
+
|
|
177
|
+
-- Copilot SDK sessions the relay created as execution vehicles for existing
|
|
178
|
+
-- conversations. The startup import sweep must never surface these as new
|
|
179
|
+
-- conversations: their history already lives in the conversation they ran
|
|
180
|
+
-- for (that duplication is how "shadow" conversations like a stray
|
|
181
|
+
-- "procees" appeared in the conversation list).
|
|
182
|
+
CREATE TABLE IF NOT EXISTS relay_session_links (
|
|
183
|
+
sdk_session_id TEXT PRIMARY KEY,
|
|
184
|
+
conversation_id TEXT NOT NULL,
|
|
185
|
+
created_at TEXT NOT NULL
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
-- path_key is the dedupe key: on Windows it is the whole path lower-cased, so
|
|
189
|
+
-- "C:\Git\Repo" and "c:\git\repo" occupy one row instead of two. See
|
|
190
|
+
-- migrations/0002-recent-workspace-roots-path-key.mjs for existing databases.
|
|
191
|
+
CREATE TABLE IF NOT EXISTS recent_workspace_roots (
|
|
192
|
+
path_key TEXT PRIMARY KEY,
|
|
193
|
+
path TEXT NOT NULL,
|
|
194
|
+
last_seen_at TEXT NOT NULL
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
CREATE INDEX IF NOT EXISTS idx_recent_workspace_roots_last_seen
|
|
198
|
+
ON recent_workspace_roots(last_seen_at DESC);
|
|
199
|
+
|
|
200
|
+
CREATE TABLE IF NOT EXISTS app_settings (
|
|
201
|
+
key TEXT PRIMARY KEY,
|
|
202
|
+
value TEXT,
|
|
203
|
+
updated_at TEXT NOT NULL
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
-- Web Push subscriptions, one row per subscribed browser installation. See
|
|
207
|
+
-- migrations/0003-push-subscriptions.mjs for existing databases.
|
|
208
|
+
CREATE TABLE IF NOT EXISTS push_subscriptions (
|
|
209
|
+
id TEXT PRIMARY KEY,
|
|
210
|
+
device_id TEXT NOT NULL,
|
|
211
|
+
device_label TEXT,
|
|
212
|
+
endpoint TEXT NOT NULL UNIQUE,
|
|
213
|
+
keys_json TEXT NOT NULL,
|
|
214
|
+
preferences_json TEXT NOT NULL,
|
|
215
|
+
user_agent TEXT,
|
|
216
|
+
created_at TEXT NOT NULL,
|
|
217
|
+
updated_at TEXT NOT NULL,
|
|
218
|
+
last_success_at TEXT,
|
|
219
|
+
last_error TEXT,
|
|
220
|
+
failure_count INTEGER NOT NULL DEFAULT 0
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
CREATE INDEX IF NOT EXISTS idx_push_subscriptions_device
|
|
224
|
+
ON push_subscriptions(device_id);
|
|
225
|
+
|
|
226
|
+
CREATE TABLE IF NOT EXISTS model_selector_state (
|
|
227
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
228
|
+
source TEXT,
|
|
229
|
+
refreshed_at TEXT,
|
|
230
|
+
error TEXT,
|
|
231
|
+
updated_at TEXT NOT NULL
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
CREATE TABLE IF NOT EXISTS model_variants (
|
|
235
|
+
variant_id TEXT PRIMARY KEY,
|
|
236
|
+
base_model_id TEXT NOT NULL,
|
|
237
|
+
provider TEXT NOT NULL,
|
|
238
|
+
label TEXT NOT NULL,
|
|
239
|
+
release_status TEXT,
|
|
240
|
+
reasoning_effort TEXT,
|
|
241
|
+
context_limit_tokens INTEGER,
|
|
242
|
+
long_context_limit_tokens INTEGER,
|
|
243
|
+
pricing_json TEXT,
|
|
244
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
245
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
246
|
+
updated_at TEXT NOT NULL
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
CREATE INDEX IF NOT EXISTS idx_model_variants_enabled
|
|
250
|
+
ON model_variants(enabled, provider, sort_order, variant_id);
|
|
251
|
+
|
|
252
|
+
CREATE TABLE IF NOT EXISTS relay_control_requests (
|
|
253
|
+
id TEXT PRIMARY KEY,
|
|
254
|
+
type TEXT NOT NULL,
|
|
255
|
+
conversation_id TEXT,
|
|
256
|
+
queue_message_id TEXT,
|
|
257
|
+
sdk_session_id TEXT,
|
|
258
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
259
|
+
request TEXT,
|
|
260
|
+
result TEXT,
|
|
261
|
+
error TEXT,
|
|
262
|
+
created_at TEXT NOT NULL,
|
|
263
|
+
updated_at TEXT NOT NULL,
|
|
264
|
+
completed_at TEXT
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
CREATE INDEX IF NOT EXISTS idx_relay_control_requests_status
|
|
268
|
+
ON relay_control_requests(status, sdk_session_id, created_at);
|
|
269
|
+
CREATE INDEX IF NOT EXISTS idx_relay_control_requests_queue
|
|
270
|
+
ON relay_control_requests(queue_message_id, type, status, created_at);
|
|
271
|
+
|
|
272
|
+
CREATE TABLE IF NOT EXISTS relay_questions (
|
|
273
|
+
id TEXT PRIMARY KEY,
|
|
274
|
+
queue_id TEXT NOT NULL,
|
|
275
|
+
conversation_id TEXT NOT NULL,
|
|
276
|
+
message_id TEXT NOT NULL,
|
|
277
|
+
relay_mode TEXT NOT NULL DEFAULT 'agent',
|
|
278
|
+
prompt TEXT NOT NULL,
|
|
279
|
+
choices TEXT,
|
|
280
|
+
request TEXT,
|
|
281
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
282
|
+
answer TEXT,
|
|
283
|
+
structured_answer TEXT,
|
|
284
|
+
request_schema TEXT,
|
|
285
|
+
sdk_session_id TEXT,
|
|
286
|
+
owner_worker_id TEXT,
|
|
287
|
+
continuation_id TEXT,
|
|
288
|
+
continuation_question_id TEXT,
|
|
289
|
+
created_at TEXT NOT NULL,
|
|
290
|
+
answered_at TEXT,
|
|
291
|
+
expires_at TEXT NOT NULL,
|
|
292
|
+
FOREIGN KEY (queue_id) REFERENCES queue(id) ON DELETE CASCADE
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
CREATE INDEX IF NOT EXISTS idx_relay_questions_status ON relay_questions(status, expires_at, created_at);
|
|
296
|
+
CREATE INDEX IF NOT EXISTS idx_relay_questions_conversation ON relay_questions(conversation_id, status, created_at);
|
|
297
|
+
|
|
298
|
+
CREATE TABLE IF NOT EXISTS relay_boards (
|
|
299
|
+
id TEXT PRIMARY KEY,
|
|
300
|
+
queue_id TEXT NOT NULL,
|
|
301
|
+
conversation_id TEXT NOT NULL,
|
|
302
|
+
message_id TEXT NOT NULL,
|
|
303
|
+
board_type TEXT NOT NULL,
|
|
304
|
+
relay_mode TEXT NOT NULL DEFAULT 'agent',
|
|
305
|
+
title TEXT NOT NULL,
|
|
306
|
+
body TEXT NOT NULL,
|
|
307
|
+
actions_json TEXT,
|
|
308
|
+
recommended_action TEXT,
|
|
309
|
+
context_json TEXT,
|
|
310
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
311
|
+
selected_action TEXT,
|
|
312
|
+
acted_at TEXT,
|
|
313
|
+
created_at TEXT NOT NULL,
|
|
314
|
+
updated_at TEXT NOT NULL,
|
|
315
|
+
FOREIGN KEY (queue_id) REFERENCES queue(id) ON DELETE CASCADE,
|
|
316
|
+
UNIQUE(message_id, board_type)
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
CREATE INDEX IF NOT EXISTS idx_relay_boards_status ON relay_boards(status, created_at);
|
|
320
|
+
CREATE INDEX IF NOT EXISTS idx_relay_boards_conversation ON relay_boards(conversation_id, status, created_at);
|
|
321
|
+
|
|
322
|
+
CREATE TABLE IF NOT EXISTS relay_activity (
|
|
323
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
324
|
+
queue_message_id TEXT NOT NULL,
|
|
325
|
+
response_message_id TEXT,
|
|
326
|
+
conversation_id TEXT NOT NULL,
|
|
327
|
+
relay_mode TEXT NOT NULL DEFAULT 'agent',
|
|
328
|
+
text TEXT NOT NULL,
|
|
329
|
+
created_at TEXT NOT NULL,
|
|
330
|
+
metadata_json TEXT,
|
|
331
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
CREATE INDEX IF NOT EXISTS idx_relay_activity_queue ON relay_activity(queue_message_id, id);
|
|
335
|
+
CREATE INDEX IF NOT EXISTS idx_relay_activity_response ON relay_activity(response_message_id, id);
|
|
336
|
+
|
|
337
|
+
CREATE TABLE IF NOT EXISTS relay_stream_events (
|
|
338
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
339
|
+
queue_message_id TEXT NOT NULL,
|
|
340
|
+
response_message_id TEXT,
|
|
341
|
+
conversation_id TEXT NOT NULL,
|
|
342
|
+
relay_mode TEXT NOT NULL DEFAULT 'agent',
|
|
343
|
+
seq INTEGER NOT NULL,
|
|
344
|
+
text TEXT NOT NULL DEFAULT '',
|
|
345
|
+
done INTEGER NOT NULL DEFAULT 0,
|
|
346
|
+
created_at TEXT NOT NULL,
|
|
347
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE,
|
|
348
|
+
UNIQUE(queue_message_id, seq)
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
CREATE INDEX IF NOT EXISTS idx_relay_stream_events_queue
|
|
352
|
+
ON relay_stream_events(queue_message_id, seq);
|
|
353
|
+
CREATE INDEX IF NOT EXISTS idx_relay_stream_events_response
|
|
354
|
+
ON relay_stream_events(response_message_id, seq);
|
|
355
|
+
CREATE INDEX IF NOT EXISTS idx_relay_stream_events_conversation
|
|
356
|
+
ON relay_stream_events(conversation_id, queue_message_id, seq);
|
|
357
|
+
|
|
358
|
+
CREATE TABLE IF NOT EXISTS relay_thought (
|
|
359
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
360
|
+
queue_message_id TEXT NOT NULL,
|
|
361
|
+
response_message_id TEXT,
|
|
362
|
+
conversation_id TEXT NOT NULL,
|
|
363
|
+
relay_mode TEXT NOT NULL DEFAULT 'agent',
|
|
364
|
+
reasoning_id TEXT,
|
|
365
|
+
seq INTEGER NOT NULL,
|
|
366
|
+
text TEXT NOT NULL,
|
|
367
|
+
done INTEGER NOT NULL DEFAULT 0,
|
|
368
|
+
created_at TEXT NOT NULL,
|
|
369
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE,
|
|
370
|
+
UNIQUE(queue_message_id, seq)
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
CREATE INDEX IF NOT EXISTS idx_relay_thought_queue
|
|
374
|
+
ON relay_thought(queue_message_id, seq);
|
|
375
|
+
CREATE INDEX IF NOT EXISTS idx_relay_thought_response
|
|
376
|
+
ON relay_thought(response_message_id, seq);
|
|
377
|
+
|
|
378
|
+
CREATE TABLE IF NOT EXISTS subagent_runs (
|
|
379
|
+
id TEXT PRIMARY KEY,
|
|
380
|
+
queue_message_id TEXT NOT NULL,
|
|
381
|
+
conversation_id TEXT NOT NULL,
|
|
382
|
+
parent_subagent_id TEXT,
|
|
383
|
+
display_name TEXT,
|
|
384
|
+
status TEXT NOT NULL DEFAULT 'running',
|
|
385
|
+
started_at TEXT NOT NULL,
|
|
386
|
+
updated_at TEXT NOT NULL,
|
|
387
|
+
completed_at TEXT,
|
|
388
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
CREATE INDEX IF NOT EXISTS idx_subagent_runs_queue
|
|
392
|
+
ON subagent_runs(queue_message_id, started_at);
|
|
393
|
+
CREATE INDEX IF NOT EXISTS idx_subagent_runs_conversation
|
|
394
|
+
ON subagent_runs(conversation_id, status);
|
|
395
|
+
|
|
396
|
+
-- Final workflow digests attached to the assistant message that reports a
|
|
397
|
+
-- background workflow's completion (the transcript's "Finished background
|
|
398
|
+
-- task" card). Written inside the /api/response finalize transaction, so the
|
|
399
|
+
-- rows key directly on the response message id — no queue-id resolution
|
|
400
|
+
-- needed. digest_json is the sanitized workflowProgress contract, ≤ a few KB.
|
|
401
|
+
-- Also declared in the upgrade db.exec below for existing databases.
|
|
402
|
+
CREATE TABLE IF NOT EXISTS workflow_runs (
|
|
403
|
+
id TEXT PRIMARY KEY,
|
|
404
|
+
response_message_id TEXT NOT NULL,
|
|
405
|
+
conversation_id TEXT NOT NULL,
|
|
406
|
+
run_index INTEGER NOT NULL DEFAULT 0,
|
|
407
|
+
digest_json TEXT NOT NULL,
|
|
408
|
+
created_at TEXT NOT NULL,
|
|
409
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
CREATE INDEX IF NOT EXISTS idx_workflow_runs_response
|
|
413
|
+
ON workflow_runs(response_message_id, run_index);
|
|
414
|
+
CREATE INDEX IF NOT EXISTS idx_workflow_runs_conversation
|
|
415
|
+
ON workflow_runs(conversation_id);
|
|
416
|
+
|
|
417
|
+
-- Previews published mid-turn, pinned to the transcript. Snapshot JSON, not a
|
|
418
|
+
-- registry reference: the registry is in-memory and the card must still
|
|
419
|
+
-- render after the preview closes or the relay restarts. Written with the
|
|
420
|
+
-- queue id (the response id does not exist yet mid-turn) and linked to the
|
|
421
|
+
-- response at finalize, exactly like relay_activity.
|
|
422
|
+
CREATE TABLE IF NOT EXISTS preview_cards (
|
|
423
|
+
id TEXT PRIMARY KEY,
|
|
424
|
+
queue_message_id TEXT NOT NULL,
|
|
425
|
+
response_message_id TEXT,
|
|
426
|
+
conversation_id TEXT NOT NULL,
|
|
427
|
+
preview_json TEXT NOT NULL,
|
|
428
|
+
created_at TEXT NOT NULL,
|
|
429
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
CREATE INDEX IF NOT EXISTS idx_preview_cards_response
|
|
433
|
+
ON preview_cards(response_message_id, created_at);
|
|
434
|
+
CREATE INDEX IF NOT EXISTS idx_preview_cards_queue
|
|
435
|
+
ON preview_cards(queue_message_id);
|
|
436
|
+
|
|
437
|
+
CREATE TABLE IF NOT EXISTS uploaded_files (
|
|
438
|
+
sha256 TEXT PRIMARY KEY,
|
|
439
|
+
original_name TEXT,
|
|
440
|
+
mime_type TEXT,
|
|
441
|
+
size_bytes INTEGER NOT NULL,
|
|
442
|
+
created_at TEXT NOT NULL
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
CREATE TABLE IF NOT EXISTS upload_refs (
|
|
446
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
447
|
+
file_sha256 TEXT NOT NULL,
|
|
448
|
+
conversation_id TEXT NOT NULL,
|
|
449
|
+
message_id TEXT NOT NULL,
|
|
450
|
+
created_at TEXT NOT NULL,
|
|
451
|
+
UNIQUE(file_sha256, message_id),
|
|
452
|
+
FOREIGN KEY (file_sha256) REFERENCES uploaded_files(sha256) ON DELETE CASCADE,
|
|
453
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
CREATE INDEX IF NOT EXISTS idx_upload_refs_conv ON upload_refs(conversation_id, file_sha256);
|
|
457
|
+
CREATE INDEX IF NOT EXISTS idx_upload_refs_sha ON upload_refs(file_sha256);
|
|
458
|
+
|
|
459
|
+
CREATE TABLE IF NOT EXISTS conversation_shares (
|
|
460
|
+
token TEXT PRIMARY KEY,
|
|
461
|
+
conversation_id TEXT NOT NULL,
|
|
462
|
+
created_at TEXT NOT NULL,
|
|
463
|
+
last_accessed_at TEXT,
|
|
464
|
+
revoked_at TEXT,
|
|
465
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
CREATE INDEX IF NOT EXISTS idx_conversation_shares_conversation
|
|
469
|
+
ON conversation_shares(conversation_id, revoked_at, created_at DESC);
|
|
470
|
+
|
|
471
|
+
CREATE TABLE IF NOT EXISTS status_events (
|
|
472
|
+
id TEXT PRIMARY KEY,
|
|
473
|
+
timestamp INTEGER NOT NULL,
|
|
474
|
+
type TEXT NOT NULL,
|
|
475
|
+
source TEXT NOT NULL,
|
|
476
|
+
payload_json TEXT NOT NULL
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
CREATE INDEX IF NOT EXISTS idx_status_events_timeline
|
|
480
|
+
ON status_events(timestamp DESC, id DESC);
|
|
481
|
+
`;
|
|
482
|
+
|
|
483
|
+
// Creates all tables/indexes/triggers and runs the column-backfill migrations.
|
|
484
|
+
// Byte-for-byte the schema/migration section previously inlined in
|
|
485
|
+
// server-runtime.mjs — same statements, same order, same tolerance for
|
|
486
|
+
// already-migrated databases.
|
|
487
|
+
export function applySchema(db) {
|
|
488
|
+
db.exec(SCHEMA_SQL);
|
|
489
|
+
db.exec(`
|
|
490
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts
|
|
491
|
+
USING fts5(text, content='messages', content_rowid='rowid');
|
|
492
|
+
|
|
493
|
+
CREATE TRIGGER IF NOT EXISTS messages_fts_after_insert
|
|
494
|
+
AFTER INSERT ON messages
|
|
495
|
+
BEGIN
|
|
496
|
+
INSERT INTO messages_fts(rowid, text)
|
|
497
|
+
VALUES (new.rowid, new.text);
|
|
498
|
+
END;
|
|
499
|
+
|
|
500
|
+
CREATE TRIGGER IF NOT EXISTS messages_fts_after_delete
|
|
501
|
+
AFTER DELETE ON messages
|
|
502
|
+
BEGIN
|
|
503
|
+
INSERT INTO messages_fts(messages_fts, rowid, text)
|
|
504
|
+
VALUES ('delete', old.rowid, old.text);
|
|
505
|
+
END;
|
|
506
|
+
|
|
507
|
+
CREATE TRIGGER IF NOT EXISTS messages_fts_after_update
|
|
508
|
+
AFTER UPDATE OF text ON messages
|
|
509
|
+
BEGIN
|
|
510
|
+
INSERT INTO messages_fts(messages_fts, rowid, text)
|
|
511
|
+
VALUES ('delete', old.rowid, old.text);
|
|
512
|
+
INSERT INTO messages_fts(rowid, text)
|
|
513
|
+
VALUES (new.rowid, new.text);
|
|
514
|
+
END;
|
|
515
|
+
`);
|
|
516
|
+
// The extension-backed history queue is superseded by the local SDK importer.
|
|
517
|
+
db.exec(`DROP TABLE IF EXISTS sdk_history_fetch_requests`);
|
|
518
|
+
// Only rebuild the FTS index if the virtual table is empty (first migration or new DB).
|
|
519
|
+
// A full rebuild is O(N) in message count and blocks the sync event loop, so we skip it
|
|
520
|
+
// when existing trigger-maintained rows are already present.
|
|
521
|
+
{
|
|
522
|
+
const ftsRowCount = db.prepare(`SELECT COUNT(*) AS cnt FROM messages_fts`).get()?.cnt || 0;
|
|
523
|
+
if (ftsRowCount === 0) {
|
|
524
|
+
db.exec(`INSERT INTO messages_fts(messages_fts) VALUES ('rebuild')`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Backfill schema for pre-model databases.
|
|
529
|
+
const messageColumns = db.prepare(`PRAGMA table_info(messages)`).all().map((c) => c.name);
|
|
530
|
+
if (!messageColumns.includes('attachments')) {
|
|
531
|
+
db.exec(`ALTER TABLE messages ADD COLUMN attachments TEXT`);
|
|
532
|
+
}
|
|
533
|
+
if (!messageColumns.includes('mode')) {
|
|
534
|
+
db.exec(`ALTER TABLE messages ADD COLUMN mode TEXT`);
|
|
535
|
+
}
|
|
536
|
+
if (!messageColumns.includes('model_requested')) {
|
|
537
|
+
db.exec(`ALTER TABLE messages ADD COLUMN model_requested TEXT`);
|
|
538
|
+
}
|
|
539
|
+
if (!messageColumns.includes('model_actual')) {
|
|
540
|
+
db.exec(`ALTER TABLE messages ADD COLUMN model_actual TEXT`);
|
|
541
|
+
}
|
|
542
|
+
if (!messageColumns.includes('model_origin')) {
|
|
543
|
+
db.exec(`ALTER TABLE messages ADD COLUMN model_origin TEXT`);
|
|
544
|
+
}
|
|
545
|
+
if (!messageColumns.includes('hidden_from_shares')) {
|
|
546
|
+
db.exec(`ALTER TABLE messages ADD COLUMN hidden_from_shares INTEGER NOT NULL DEFAULT 0`);
|
|
547
|
+
}
|
|
548
|
+
if (!messageColumns.includes('share_hidden_at')) {
|
|
549
|
+
db.exec(`ALTER TABLE messages ADD COLUMN share_hidden_at TEXT`);
|
|
550
|
+
}
|
|
551
|
+
if (!messageColumns.includes('executed_provider')) {
|
|
552
|
+
// Which provider actually ran the turn, derived from the authenticated
|
|
553
|
+
// responder identity — never from the response payload. A value that
|
|
554
|
+
// differs from the conversation's provider marks a cross-provider
|
|
555
|
+
// execution (e.g. the Copilot relay answering a Cursor conversation).
|
|
556
|
+
db.exec(`ALTER TABLE messages ADD COLUMN executed_provider TEXT`);
|
|
557
|
+
}
|
|
558
|
+
if (!messageColumns.includes('kind')) {
|
|
559
|
+
// 'continuation' marks an assistant message the CLI produced on its own
|
|
560
|
+
// after a background task settled (no user prompt); the client badges it.
|
|
561
|
+
db.exec(`ALTER TABLE messages ADD COLUMN kind TEXT`);
|
|
562
|
+
}
|
|
563
|
+
db.exec(`
|
|
564
|
+
CREATE INDEX IF NOT EXISTS idx_messages_share_visibility
|
|
565
|
+
ON messages(conversation_id, hidden_from_shares, timestamp)
|
|
566
|
+
`);
|
|
567
|
+
|
|
568
|
+
const sdkSessionImportColumns = db.prepare(`PRAGMA table_info(sdk_session_imports)`).all().map((c) => c.name);
|
|
569
|
+
if (!sdkSessionImportColumns.includes('source_started_at')) {
|
|
570
|
+
db.exec(`ALTER TABLE sdk_session_imports ADD COLUMN source_started_at TEXT`);
|
|
571
|
+
}
|
|
572
|
+
if (!sdkSessionImportColumns.includes('source_modified_at')) {
|
|
573
|
+
db.exec(`ALTER TABLE sdk_session_imports ADD COLUMN source_modified_at TEXT`);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const queueColumns = db.prepare(`PRAGMA table_info(queue)`).all().map((c) => c.name);
|
|
577
|
+
if (!queueColumns.includes('model')) {
|
|
578
|
+
db.exec(`ALTER TABLE queue ADD COLUMN model TEXT`);
|
|
579
|
+
}
|
|
580
|
+
if (!queueColumns.includes('model_variant_id')) {
|
|
581
|
+
db.exec(`ALTER TABLE queue ADD COLUMN model_variant_id TEXT`);
|
|
582
|
+
}
|
|
583
|
+
if (!queueColumns.includes('reasoning_effort')) {
|
|
584
|
+
db.exec(`ALTER TABLE queue ADD COLUMN reasoning_effort TEXT`);
|
|
585
|
+
}
|
|
586
|
+
if (!queueColumns.includes('context_tier')) {
|
|
587
|
+
db.exec(`ALTER TABLE queue ADD COLUMN context_tier TEXT`);
|
|
588
|
+
}
|
|
589
|
+
if (!queueColumns.includes('runtime_session_id')) {
|
|
590
|
+
db.exec(`ALTER TABLE queue ADD COLUMN runtime_session_id TEXT`);
|
|
591
|
+
}
|
|
592
|
+
if (!queueColumns.includes('relay_mode')) {
|
|
593
|
+
db.exec(`ALTER TABLE queue ADD COLUMN relay_mode TEXT NOT NULL DEFAULT 'agent'`);
|
|
594
|
+
}
|
|
595
|
+
if (!queueColumns.includes('attachments')) {
|
|
596
|
+
db.exec(`ALTER TABLE queue ADD COLUMN attachments TEXT`);
|
|
597
|
+
}
|
|
598
|
+
if (!queueColumns.includes('retry_count')) {
|
|
599
|
+
db.exec(`ALTER TABLE queue ADD COLUMN retry_count INTEGER NOT NULL DEFAULT 0`);
|
|
600
|
+
}
|
|
601
|
+
if (!queueColumns.includes('next_attempt_at')) {
|
|
602
|
+
db.exec(`ALTER TABLE queue ADD COLUMN next_attempt_at TEXT`);
|
|
603
|
+
}
|
|
604
|
+
if (!queueColumns.includes('owner_sdk_session_id')) {
|
|
605
|
+
db.exec(`ALTER TABLE queue ADD COLUMN owner_sdk_session_id TEXT`);
|
|
606
|
+
}
|
|
607
|
+
if (!queueColumns.includes('owner_assigned_at')) {
|
|
608
|
+
db.exec(`ALTER TABLE queue ADD COLUMN owner_assigned_at TEXT`);
|
|
609
|
+
}
|
|
610
|
+
if (!queueColumns.includes('owner_lease_expires_at')) {
|
|
611
|
+
db.exec(`ALTER TABLE queue ADD COLUMN owner_lease_expires_at TEXT`);
|
|
612
|
+
}
|
|
613
|
+
if (!queueColumns.includes('owner_last_claimed_at')) {
|
|
614
|
+
db.exec(`ALTER TABLE queue ADD COLUMN owner_last_claimed_at TEXT`);
|
|
615
|
+
}
|
|
616
|
+
if (!queueColumns.includes('response_message_id')) {
|
|
617
|
+
db.exec(`ALTER TABLE queue ADD COLUMN response_message_id TEXT`);
|
|
618
|
+
}
|
|
619
|
+
if (!queueColumns.includes('parked_at')) {
|
|
620
|
+
db.exec(`ALTER TABLE queue ADD COLUMN parked_at TEXT`);
|
|
621
|
+
}
|
|
622
|
+
if (!queueColumns.includes('parked_target_session_id')) {
|
|
623
|
+
db.exec(`ALTER TABLE queue ADD COLUMN parked_target_session_id TEXT`);
|
|
624
|
+
}
|
|
625
|
+
if (!queueColumns.includes('kind')) {
|
|
626
|
+
// 'continuation' rows are synthetic turns for the Claude worker's
|
|
627
|
+
// background-task continuations: born 'processing', never deliverable, and
|
|
628
|
+
// torn down (not requeued) when their worker disappears.
|
|
629
|
+
db.exec(`ALTER TABLE queue ADD COLUMN kind TEXT`);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// recent_workspace_roots gained a case-normalized primary key (path_key). The
|
|
633
|
+
// CREATE TABLE IF NOT EXISTS above only covers fresh databases, so upgrade an
|
|
634
|
+
// existing one in place before any statement referencing path_key is prepared.
|
|
635
|
+
const recentWorkspaceRootsRebuild = rebuildRecentWorkspaceRootsTable(db);
|
|
636
|
+
if (recentWorkspaceRootsRebuild.applied) {
|
|
637
|
+
console.log(`[workspace-root] rebuilt recent CWD history: ${recentWorkspaceRootsRebuild.rowsBefore} row(s) -> ${recentWorkspaceRootsRebuild.rowsAfter} distinct directory/ies`);
|
|
638
|
+
}
|
|
639
|
+
// The CREATE TABLE IF NOT EXISTS above covers fresh databases; this upgrades
|
|
640
|
+
// an existing one before the push dispatch service prepares its statements.
|
|
641
|
+
const pushSubscriptionsMigration = ensurePushSubscriptionsTable(db);
|
|
642
|
+
if (pushSubscriptionsMigration.applied) {
|
|
643
|
+
console.log(`[push] created push_subscriptions table`);
|
|
644
|
+
}
|
|
645
|
+
if (!queueColumns.includes('parked_transaction_id')) {
|
|
646
|
+
db.exec(`ALTER TABLE queue ADD COLUMN parked_transaction_id TEXT`);
|
|
647
|
+
}
|
|
648
|
+
if (!queueColumns.includes('parked_reason')) {
|
|
649
|
+
db.exec(`ALTER TABLE queue ADD COLUMN parked_reason TEXT`);
|
|
650
|
+
}
|
|
651
|
+
db.exec(`UPDATE queue SET relay_mode = 'agent' WHERE relay_mode IS NULL OR relay_mode = ''`);
|
|
652
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_queue_next_attempt ON queue(status, next_attempt_at, timestamp)`);
|
|
653
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_queue_owner_pending ON queue(status, owner_sdk_session_id, next_attempt_at, timestamp)`);
|
|
654
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_queue_parked_release ON queue(status, parked_transaction_id, parked_target_session_id, parked_at, timestamp)`);
|
|
655
|
+
migrateImageConversationSchema(db);
|
|
656
|
+
|
|
657
|
+
const runtimeSessionColumns = db.prepare(`PRAGMA table_info(runtime_sessions)`).all().map((c) => c.name);
|
|
658
|
+
if (runtimeSessionColumns.length) {
|
|
659
|
+
if (!runtimeSessionColumns.includes('strategy')) {
|
|
660
|
+
db.exec(`ALTER TABLE runtime_sessions ADD COLUMN strategy TEXT NOT NULL DEFAULT 'isolated'`);
|
|
661
|
+
}
|
|
662
|
+
if (!runtimeSessionColumns.includes('sdk_session_id')) {
|
|
663
|
+
db.exec(`ALTER TABLE runtime_sessions ADD COLUMN sdk_session_id TEXT`);
|
|
664
|
+
}
|
|
665
|
+
if (!runtimeSessionColumns.includes('runtime_key')) {
|
|
666
|
+
db.exec(`ALTER TABLE runtime_sessions ADD COLUMN runtime_key TEXT NOT NULL DEFAULT ''`);
|
|
667
|
+
db.exec(`UPDATE runtime_sessions SET runtime_key = id WHERE runtime_key IS NULL OR runtime_key = ''`);
|
|
668
|
+
}
|
|
669
|
+
if (!runtimeSessionColumns.includes('model')) {
|
|
670
|
+
db.exec(`ALTER TABLE runtime_sessions ADD COLUMN model TEXT`);
|
|
671
|
+
}
|
|
672
|
+
if (!runtimeSessionColumns.includes('provider_type')) {
|
|
673
|
+
db.exec(`ALTER TABLE runtime_sessions ADD COLUMN provider_type TEXT NOT NULL DEFAULT 'github'`);
|
|
674
|
+
}
|
|
675
|
+
if (!runtimeSessionColumns.includes('provider_model')) {
|
|
676
|
+
db.exec(`ALTER TABLE runtime_sessions ADD COLUMN provider_model TEXT`);
|
|
677
|
+
}
|
|
678
|
+
if (!runtimeSessionColumns.includes('claude_native_session_id')) {
|
|
679
|
+
// Native Claude Agent SDK session id captured from the worker's first
|
|
680
|
+
// turn; passed back as `resume` so Claude conversations survive worker
|
|
681
|
+
// restarts.
|
|
682
|
+
db.exec(`ALTER TABLE runtime_sessions ADD COLUMN claude_native_session_id TEXT`);
|
|
683
|
+
}
|
|
684
|
+
if (!runtimeSessionColumns.includes('cursor_agent_id')) {
|
|
685
|
+
// Cursor Agent SDK durable agent id; replayed on queue messages so Cursor
|
|
686
|
+
// conversations survive worker restarts.
|
|
687
|
+
db.exec(`ALTER TABLE runtime_sessions ADD COLUMN cursor_agent_id TEXT`);
|
|
688
|
+
}
|
|
689
|
+
if (!runtimeSessionColumns.includes('grok_native_session_id')) {
|
|
690
|
+
// ACP session id from Grok agent session/new; replayed via session/load so
|
|
691
|
+
// Grok conversations survive worker restarts.
|
|
692
|
+
db.exec(`ALTER TABLE runtime_sessions ADD COLUMN grok_native_session_id TEXT`);
|
|
693
|
+
}
|
|
694
|
+
if (!runtimeSessionColumns.includes('context_usage_json')) {
|
|
695
|
+
// Latest context-window breakdown reported by the Claude Agent SDK. Claude
|
|
696
|
+
// sessions have no Copilot events.jsonl to tail, so this is the only source
|
|
697
|
+
// of context data for them.
|
|
698
|
+
db.exec(`ALTER TABLE runtime_sessions ADD COLUMN context_usage_json TEXT`);
|
|
699
|
+
}
|
|
700
|
+
if (!runtimeSessionColumns.includes('context_usage_captured_at')) {
|
|
701
|
+
db.exec(`ALTER TABLE runtime_sessions ADD COLUMN context_usage_captured_at TEXT`);
|
|
702
|
+
}
|
|
703
|
+
if (!runtimeSessionColumns.includes('status')) {
|
|
704
|
+
db.exec(`ALTER TABLE runtime_sessions ADD COLUMN status TEXT NOT NULL DEFAULT 'active'`);
|
|
705
|
+
}
|
|
706
|
+
if (!runtimeSessionColumns.includes('created_at')) {
|
|
707
|
+
db.exec(`ALTER TABLE runtime_sessions ADD COLUMN created_at TEXT NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'now'))`);
|
|
708
|
+
}
|
|
709
|
+
if (!runtimeSessionColumns.includes('last_used_at')) {
|
|
710
|
+
db.exec(`ALTER TABLE runtime_sessions ADD COLUMN last_used_at TEXT NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'now'))`);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
const conversationColumns = db.prepare(`PRAGMA table_info(conversations)`).all().map((c) => c.name);
|
|
715
|
+
if (!conversationColumns.includes('archived')) {
|
|
716
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN archived INTEGER NOT NULL DEFAULT 0`);
|
|
717
|
+
}
|
|
718
|
+
if (!conversationColumns.includes('sdk_session_id')) {
|
|
719
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN sdk_session_id TEXT`);
|
|
720
|
+
}
|
|
721
|
+
if (!conversationColumns.includes('preferred_relay_mode')) {
|
|
722
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN preferred_relay_mode TEXT`);
|
|
723
|
+
}
|
|
724
|
+
if (!conversationColumns.includes('preferred_model')) {
|
|
725
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN preferred_model TEXT`);
|
|
726
|
+
}
|
|
727
|
+
if (!conversationColumns.includes('preferred_reasoning_effort')) {
|
|
728
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN preferred_reasoning_effort TEXT`);
|
|
729
|
+
// One-time carry-over from the retired per-mode preference maps: seed the flat
|
|
730
|
+
// preference from the entry stored for the conversation's preferred mode.
|
|
731
|
+
if (conversationColumns.includes('preferred_models_by_mode')) {
|
|
732
|
+
const legacyRows = db.prepare(`
|
|
733
|
+
SELECT id, preferred_relay_mode, preferred_models_by_mode, preferred_reasoning_by_mode
|
|
734
|
+
FROM conversations
|
|
735
|
+
WHERE preferred_models_by_mode IS NOT NULL OR preferred_reasoning_by_mode IS NOT NULL
|
|
736
|
+
`).all();
|
|
737
|
+
const seedFlatPreference = db.prepare(`UPDATE conversations SET preferred_model = ?, preferred_reasoning_effort = ? WHERE id = ?`);
|
|
738
|
+
for (const legacyRow of legacyRows) {
|
|
739
|
+
let modelsByMode = {};
|
|
740
|
+
let reasoningByMode = {};
|
|
741
|
+
try { modelsByMode = JSON.parse(legacyRow.preferred_models_by_mode || '{}') || {}; } catch { modelsByMode = {}; }
|
|
742
|
+
try { reasoningByMode = JSON.parse(legacyRow.preferred_reasoning_by_mode || '{}') || {}; } catch { reasoningByMode = {}; }
|
|
743
|
+
const mode = String(legacyRow.preferred_relay_mode || '').trim() || DEFAULT_RELAY_MODE;
|
|
744
|
+
const model = String(modelsByMode?.[mode] || '').trim() || null;
|
|
745
|
+
const effort = String(reasoningByMode?.[mode] || '').trim().toLowerCase() || null;
|
|
746
|
+
if (model || effort) seedFlatPreference.run(model, effort, legacyRow.id);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
if (!conversationColumns.includes('auto_compact_window')) {
|
|
751
|
+
// Token count (not a percent); NULL = "Auto", i.e. the model-tuned default
|
|
752
|
+
// the Claude CLI picks on its own. See shared/auto-compact-window.mjs.
|
|
753
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN auto_compact_window INTEGER`);
|
|
754
|
+
}
|
|
755
|
+
if (!conversationColumns.includes('thinking_enabled')) {
|
|
756
|
+
// NULL = never set (read back as the relay default, which is ON),
|
|
757
|
+
// 1 = on, 0 = off. See shared/claude-thinking.mjs.
|
|
758
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN thinking_enabled INTEGER`);
|
|
759
|
+
}
|
|
760
|
+
if (!conversationColumns.includes('thinking_display')) {
|
|
761
|
+
// NULL = never set (read back as the relay default, 'summarized' —
|
|
762
|
+
// visible thought bubbles), or 'summarized' / 'omitted'.
|
|
763
|
+
// See shared/claude-thinking.mjs.
|
|
764
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN thinking_display TEXT`);
|
|
765
|
+
}
|
|
766
|
+
if (!conversationColumns.includes('configured_workspace_root_path')) {
|
|
767
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN configured_workspace_root_path TEXT`);
|
|
768
|
+
}
|
|
769
|
+
if (!conversationColumns.includes('runtime_workspace_root_path')) {
|
|
770
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN runtime_workspace_root_path TEXT`);
|
|
771
|
+
}
|
|
772
|
+
if (!conversationColumns.includes('compacted_into')) {
|
|
773
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN compacted_into TEXT`);
|
|
774
|
+
}
|
|
775
|
+
if (!conversationColumns.includes('compacted_from')) {
|
|
776
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN compacted_from TEXT`);
|
|
777
|
+
}
|
|
778
|
+
if (!conversationColumns.includes('summary_seed')) {
|
|
779
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN summary_seed TEXT`);
|
|
780
|
+
}
|
|
781
|
+
if (!conversationColumns.includes('seed_pending')) {
|
|
782
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN seed_pending INTEGER NOT NULL DEFAULT 0`);
|
|
783
|
+
}
|
|
784
|
+
if (!conversationColumns.includes('status')) {
|
|
785
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN status TEXT NOT NULL DEFAULT 'active'`);
|
|
786
|
+
}
|
|
787
|
+
if (!conversationColumns.includes('title_source')) {
|
|
788
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN title_source TEXT NOT NULL DEFAULT 'auto'`);
|
|
789
|
+
}
|
|
790
|
+
if (!conversationColumns.includes('draft_text')) {
|
|
791
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN draft_text TEXT`);
|
|
792
|
+
}
|
|
793
|
+
if (!conversationColumns.includes('draft_updated_at')) {
|
|
794
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN draft_updated_at TEXT`);
|
|
795
|
+
}
|
|
796
|
+
if (!conversationColumns.includes('draft_updated_by_client_id')) {
|
|
797
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN draft_updated_by_client_id TEXT`);
|
|
798
|
+
}
|
|
799
|
+
if (!conversationColumns.includes('draft_attachments')) {
|
|
800
|
+
db.exec(`ALTER TABLE conversations ADD COLUMN draft_attachments TEXT`);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_runtime_sessions_sdk_session_id ON runtime_sessions(sdk_session_id) WHERE sdk_session_id IS NOT NULL AND sdk_session_id != ''`);
|
|
804
|
+
|
|
805
|
+
const relayQuestionColumns = db.prepare(`PRAGMA table_info(relay_questions)`).all().map((c) => c.name);
|
|
806
|
+
if (relayQuestionColumns.length && !relayQuestionColumns.includes('sdk_session_id')) {
|
|
807
|
+
db.exec(`ALTER TABLE relay_questions ADD COLUMN sdk_session_id TEXT`);
|
|
808
|
+
}
|
|
809
|
+
if (relayQuestionColumns.length && !relayQuestionColumns.includes('owner_worker_id')) {
|
|
810
|
+
db.exec(`ALTER TABLE relay_questions ADD COLUMN owner_worker_id TEXT`);
|
|
811
|
+
}
|
|
812
|
+
if (relayQuestionColumns.length && !relayQuestionColumns.includes('continuation_id')) {
|
|
813
|
+
db.exec(`ALTER TABLE relay_questions ADD COLUMN continuation_id TEXT`);
|
|
814
|
+
}
|
|
815
|
+
if (relayQuestionColumns.length && !relayQuestionColumns.includes('continuation_question_id')) {
|
|
816
|
+
db.exec(`ALTER TABLE relay_questions ADD COLUMN continuation_question_id TEXT`);
|
|
817
|
+
}
|
|
818
|
+
if (relayQuestionColumns.length && !relayQuestionColumns.includes('structured_answer')) {
|
|
819
|
+
db.exec(`ALTER TABLE relay_questions ADD COLUMN structured_answer TEXT`);
|
|
820
|
+
}
|
|
821
|
+
if (relayQuestionColumns.length && !relayQuestionColumns.includes('request_schema')) {
|
|
822
|
+
db.exec(`ALTER TABLE relay_questions ADD COLUMN request_schema TEXT`);
|
|
823
|
+
}
|
|
824
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_relay_questions_continuation ON relay_questions(continuation_id, continuation_question_id, status, created_at)`);
|
|
825
|
+
|
|
826
|
+
db.exec(`
|
|
827
|
+
CREATE TABLE IF NOT EXISTS subagent_runs (
|
|
828
|
+
id TEXT PRIMARY KEY,
|
|
829
|
+
queue_message_id TEXT NOT NULL,
|
|
830
|
+
conversation_id TEXT NOT NULL,
|
|
831
|
+
parent_subagent_id TEXT,
|
|
832
|
+
display_name TEXT,
|
|
833
|
+
status TEXT NOT NULL DEFAULT 'running',
|
|
834
|
+
started_at TEXT NOT NULL,
|
|
835
|
+
updated_at TEXT NOT NULL,
|
|
836
|
+
completed_at TEXT,
|
|
837
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
|
|
838
|
+
);
|
|
839
|
+
CREATE INDEX IF NOT EXISTS idx_subagent_runs_queue
|
|
840
|
+
ON subagent_runs(queue_message_id, started_at);
|
|
841
|
+
CREATE INDEX IF NOT EXISTS idx_subagent_runs_conversation
|
|
842
|
+
ON subagent_runs(conversation_id, status);
|
|
843
|
+
`);
|
|
844
|
+
|
|
845
|
+
// Existing databases pick up the workflow_runs side table here; fresh ones get
|
|
846
|
+
// it from the main schema block above (same dual-declaration pattern as
|
|
847
|
+
// subagent_runs).
|
|
848
|
+
db.exec(`
|
|
849
|
+
CREATE TABLE IF NOT EXISTS workflow_runs (
|
|
850
|
+
id TEXT PRIMARY KEY,
|
|
851
|
+
response_message_id TEXT NOT NULL,
|
|
852
|
+
conversation_id TEXT NOT NULL,
|
|
853
|
+
run_index INTEGER NOT NULL DEFAULT 0,
|
|
854
|
+
digest_json TEXT NOT NULL,
|
|
855
|
+
created_at TEXT NOT NULL,
|
|
856
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
|
|
857
|
+
);
|
|
858
|
+
CREATE INDEX IF NOT EXISTS idx_workflow_runs_response
|
|
859
|
+
ON workflow_runs(response_message_id, run_index);
|
|
860
|
+
CREATE INDEX IF NOT EXISTS idx_workflow_runs_conversation
|
|
861
|
+
ON workflow_runs(conversation_id);
|
|
862
|
+
`);
|
|
863
|
+
|
|
864
|
+
// Existing databases pick up preview_cards here; fresh ones get it from the
|
|
865
|
+
// main schema block above (same dual-declaration pattern as workflow_runs).
|
|
866
|
+
db.exec(`
|
|
867
|
+
CREATE TABLE IF NOT EXISTS preview_cards (
|
|
868
|
+
id TEXT PRIMARY KEY,
|
|
869
|
+
queue_message_id TEXT NOT NULL,
|
|
870
|
+
response_message_id TEXT,
|
|
871
|
+
conversation_id TEXT NOT NULL,
|
|
872
|
+
preview_json TEXT NOT NULL,
|
|
873
|
+
created_at TEXT NOT NULL,
|
|
874
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
|
|
875
|
+
);
|
|
876
|
+
CREATE INDEX IF NOT EXISTS idx_preview_cards_response
|
|
877
|
+
ON preview_cards(response_message_id, created_at);
|
|
878
|
+
CREATE INDEX IF NOT EXISTS idx_preview_cards_queue
|
|
879
|
+
ON preview_cards(queue_message_id);
|
|
880
|
+
`);
|
|
881
|
+
|
|
882
|
+
const relayActivityColumns = db.prepare(`PRAGMA table_info(relay_activity)`).all().map((c) => c.name);
|
|
883
|
+
if (relayActivityColumns.length && !relayActivityColumns.includes('subagent_run_id')) {
|
|
884
|
+
db.exec(`ALTER TABLE relay_activity ADD COLUMN subagent_run_id TEXT`);
|
|
885
|
+
}
|
|
886
|
+
// Structured payload for activity rows that the transcript renders as more
|
|
887
|
+
// than prose — today only the Claude compaction boundary
|
|
888
|
+
// ({kind:'compact_boundary', preTokens, postTokens}), which becomes a
|
|
889
|
+
// full-width break row instead of a line inside the tool-activity details.
|
|
890
|
+
if (relayActivityColumns.length && !relayActivityColumns.includes('metadata_json')) {
|
|
891
|
+
db.exec(`ALTER TABLE relay_activity ADD COLUMN metadata_json TEXT`);
|
|
892
|
+
}
|
|
893
|
+
const relayThoughtColumns = db.prepare(`PRAGMA table_info(relay_thought)`).all().map((c) => c.name);
|
|
894
|
+
if (relayThoughtColumns.length && !relayThoughtColumns.includes('subagent_run_id')) {
|
|
895
|
+
db.exec(`ALTER TABLE relay_thought ADD COLUMN subagent_run_id TEXT`);
|
|
896
|
+
}
|
|
897
|
+
db.exec(`
|
|
898
|
+
DELETE FROM relay_thought
|
|
899
|
+
WHERE id IN (
|
|
900
|
+
SELECT older.id
|
|
901
|
+
FROM relay_thought AS older
|
|
902
|
+
JOIN relay_thought AS newer
|
|
903
|
+
ON older.queue_message_id = newer.queue_message_id
|
|
904
|
+
AND older.reasoning_id = newer.reasoning_id
|
|
905
|
+
AND older.reasoning_id IS NOT NULL
|
|
906
|
+
AND older.reasoning_id != ''
|
|
907
|
+
AND (
|
|
908
|
+
older.seq < newer.seq
|
|
909
|
+
OR (older.seq = newer.seq AND older.id < newer.id)
|
|
910
|
+
)
|
|
911
|
+
)
|
|
912
|
+
`);
|
|
913
|
+
db.exec(`
|
|
914
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_relay_thought_queue_reasoning
|
|
915
|
+
ON relay_thought(queue_message_id, reasoning_id)
|
|
916
|
+
WHERE reasoning_id IS NOT NULL AND reasoning_id != ''
|
|
917
|
+
`);
|
|
918
|
+
const relayStreamEventColumns = db.prepare(`PRAGMA table_info(relay_stream_events)`).all().map((c) => c.name);
|
|
919
|
+
if (relayStreamEventColumns.length && !relayStreamEventColumns.includes('subagent_run_id')) {
|
|
920
|
+
db.exec(`ALTER TABLE relay_stream_events ADD COLUMN subagent_run_id TEXT`);
|
|
921
|
+
}
|
|
922
|
+
const modelVariantColumns = db.prepare(`PRAGMA table_info(model_variants)`).all().map((c) => c.name);
|
|
923
|
+
if (modelVariantColumns.length && !modelVariantColumns.includes('context_limit_tokens')) {
|
|
924
|
+
db.exec(`ALTER TABLE model_variants ADD COLUMN context_limit_tokens INTEGER`);
|
|
925
|
+
}
|
|
926
|
+
if (modelVariantColumns.length && !modelVariantColumns.includes('long_context_limit_tokens')) {
|
|
927
|
+
db.exec(`ALTER TABLE model_variants ADD COLUMN long_context_limit_tokens INTEGER`);
|
|
928
|
+
}
|
|
929
|
+
if (modelVariantColumns.length && !modelVariantColumns.includes('pricing_json')) {
|
|
930
|
+
db.exec(`ALTER TABLE model_variants ADD COLUMN pricing_json TEXT`);
|
|
931
|
+
}
|
|
932
|
+
}
|