@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,317 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const IMAGE_SCHEMA_VERSION = 1;
|
|
4
|
+
|
|
5
|
+
export function migrateImageConversationSchema(db) {
|
|
6
|
+
db.exec(`
|
|
7
|
+
CREATE TABLE IF NOT EXISTS image_sessions (
|
|
8
|
+
id TEXT PRIMARY KEY,
|
|
9
|
+
conversation_id TEXT NOT NULL,
|
|
10
|
+
origin TEXT NOT NULL CHECK (origin IN ('native', 'legacy_verified', 'legacy_reconstructed')),
|
|
11
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status = 'active'),
|
|
12
|
+
schema_version INTEGER NOT NULL DEFAULT ${IMAGE_SCHEMA_VERSION},
|
|
13
|
+
created_at TEXT NOT NULL,
|
|
14
|
+
updated_at TEXT NOT NULL,
|
|
15
|
+
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_image_sessions_conversation_updated
|
|
19
|
+
ON image_sessions(conversation_id, updated_at DESC);
|
|
20
|
+
|
|
21
|
+
CREATE TABLE IF NOT EXISTS image_operations (
|
|
22
|
+
id TEXT PRIMARY KEY,
|
|
23
|
+
image_session_id TEXT NOT NULL,
|
|
24
|
+
source_message_id TEXT NOT NULL UNIQUE,
|
|
25
|
+
queue_message_id TEXT NOT NULL UNIQUE,
|
|
26
|
+
kind TEXT NOT NULL CHECK (kind IN ('generate', 'edit', 'legacy_edit')),
|
|
27
|
+
parent_node_id TEXT,
|
|
28
|
+
prompt TEXT NOT NULL,
|
|
29
|
+
selected_image_model TEXT NOT NULL,
|
|
30
|
+
orchestration_model TEXT,
|
|
31
|
+
provider TEXT NOT NULL,
|
|
32
|
+
execution_mode TEXT NOT NULL CHECK (execution_mode IN ('native_responses', 'direct_images')),
|
|
33
|
+
parameters_json TEXT NOT NULL,
|
|
34
|
+
request_fingerprint TEXT NOT NULL,
|
|
35
|
+
idempotency_key TEXT NOT NULL,
|
|
36
|
+
replaces_operation_id TEXT,
|
|
37
|
+
created_at TEXT NOT NULL,
|
|
38
|
+
FOREIGN KEY (image_session_id) REFERENCES image_sessions(id) ON DELETE CASCADE,
|
|
39
|
+
FOREIGN KEY (source_message_id) REFERENCES messages(id) ON DELETE CASCADE,
|
|
40
|
+
FOREIGN KEY (replaces_operation_id) REFERENCES image_operations(id) ON DELETE SET NULL
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_image_operations_session_created
|
|
44
|
+
ON image_operations(image_session_id, created_at);
|
|
45
|
+
|
|
46
|
+
CREATE TABLE IF NOT EXISTS image_nodes (
|
|
47
|
+
id TEXT PRIMARY KEY,
|
|
48
|
+
image_session_id TEXT NOT NULL,
|
|
49
|
+
operation_id TEXT NOT NULL,
|
|
50
|
+
assistant_message_id TEXT NOT NULL,
|
|
51
|
+
attachment_image_id TEXT NOT NULL,
|
|
52
|
+
output_index INTEGER NOT NULL CHECK (output_index >= 0),
|
|
53
|
+
created_at TEXT NOT NULL,
|
|
54
|
+
UNIQUE (operation_id, output_index),
|
|
55
|
+
UNIQUE (assistant_message_id, attachment_image_id),
|
|
56
|
+
FOREIGN KEY (image_session_id) REFERENCES image_sessions(id) ON DELETE CASCADE,
|
|
57
|
+
FOREIGN KEY (operation_id) REFERENCES image_operations(id) ON DELETE CASCADE,
|
|
58
|
+
FOREIGN KEY (assistant_message_id) REFERENCES messages(id) ON DELETE CASCADE
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
CREATE INDEX IF NOT EXISTS idx_image_nodes_session_created
|
|
62
|
+
ON image_nodes(image_session_id, created_at);
|
|
63
|
+
|
|
64
|
+
CREATE TABLE IF NOT EXISTS image_edges (
|
|
65
|
+
image_session_id TEXT NOT NULL,
|
|
66
|
+
parent_node_id TEXT NOT NULL,
|
|
67
|
+
child_node_id TEXT NOT NULL UNIQUE,
|
|
68
|
+
operation_id TEXT NOT NULL,
|
|
69
|
+
relation TEXT NOT NULL CHECK (relation = 'edit'),
|
|
70
|
+
created_at TEXT NOT NULL,
|
|
71
|
+
PRIMARY KEY (parent_node_id, child_node_id),
|
|
72
|
+
CHECK (parent_node_id != child_node_id),
|
|
73
|
+
FOREIGN KEY (image_session_id) REFERENCES image_sessions(id) ON DELETE CASCADE,
|
|
74
|
+
FOREIGN KEY (parent_node_id) REFERENCES image_nodes(id) ON DELETE CASCADE,
|
|
75
|
+
FOREIGN KEY (child_node_id) REFERENCES image_nodes(id) ON DELETE CASCADE,
|
|
76
|
+
FOREIGN KEY (operation_id) REFERENCES image_operations(id) ON DELETE CASCADE
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
CREATE TABLE IF NOT EXISTS image_operation_assets (
|
|
80
|
+
operation_id TEXT NOT NULL,
|
|
81
|
+
ordinal INTEGER NOT NULL CHECK (ordinal >= 0),
|
|
82
|
+
role TEXT NOT NULL CHECK (role IN ('reference', 'mask')),
|
|
83
|
+
upload_id TEXT NOT NULL,
|
|
84
|
+
content_sha256 TEXT NOT NULL,
|
|
85
|
+
media_type TEXT NOT NULL,
|
|
86
|
+
created_at TEXT NOT NULL,
|
|
87
|
+
PRIMARY KEY (operation_id, ordinal),
|
|
88
|
+
FOREIGN KEY (operation_id) REFERENCES image_operations(id) ON DELETE CASCADE,
|
|
89
|
+
FOREIGN KEY (content_sha256) REFERENCES uploaded_files(sha256) ON DELETE RESTRICT
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
CREATE TABLE IF NOT EXISTS image_operation_attempts (
|
|
93
|
+
id TEXT PRIMARY KEY,
|
|
94
|
+
operation_id TEXT NOT NULL,
|
|
95
|
+
attempt_number INTEGER NOT NULL CHECK (attempt_number > 0),
|
|
96
|
+
status TEXT NOT NULL CHECK (status IN ('started', 'succeeded', 'failed_terminal', 'failed_retryable', 'uncertain')),
|
|
97
|
+
provider TEXT NOT NULL,
|
|
98
|
+
capability_snapshot_json TEXT NOT NULL,
|
|
99
|
+
provider_request_id TEXT,
|
|
100
|
+
provider_conversation_id TEXT,
|
|
101
|
+
provider_response_id TEXT,
|
|
102
|
+
http_status INTEGER,
|
|
103
|
+
error_code TEXT,
|
|
104
|
+
error_message TEXT,
|
|
105
|
+
started_at TEXT NOT NULL,
|
|
106
|
+
completed_at TEXT,
|
|
107
|
+
UNIQUE (operation_id, attempt_number),
|
|
108
|
+
FOREIGN KEY (operation_id) REFERENCES image_operations(id) ON DELETE CASCADE
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
CREATE INDEX IF NOT EXISTS idx_image_attempts_operation
|
|
112
|
+
ON image_operation_attempts(operation_id, attempt_number DESC);
|
|
113
|
+
|
|
114
|
+
CREATE TABLE IF NOT EXISTS image_provider_state (
|
|
115
|
+
id TEXT PRIMARY KEY,
|
|
116
|
+
image_session_id TEXT NOT NULL,
|
|
117
|
+
head_node_id TEXT NOT NULL,
|
|
118
|
+
provider TEXT NOT NULL,
|
|
119
|
+
endpoint_fingerprint TEXT NOT NULL,
|
|
120
|
+
provider_conversation_id TEXT,
|
|
121
|
+
provider_response_id TEXT,
|
|
122
|
+
context_version INTEGER NOT NULL CHECK (context_version > 0),
|
|
123
|
+
reconstruction_mode TEXT NOT NULL CHECK (reconstruction_mode IN ('native', 'verified_replay', 'image_only')),
|
|
124
|
+
safe_state_json TEXT NOT NULL,
|
|
125
|
+
created_at TEXT NOT NULL,
|
|
126
|
+
updated_at TEXT NOT NULL,
|
|
127
|
+
UNIQUE (head_node_id, provider, endpoint_fingerprint),
|
|
128
|
+
FOREIGN KEY (image_session_id) REFERENCES image_sessions(id) ON DELETE CASCADE,
|
|
129
|
+
FOREIGN KEY (head_node_id) REFERENCES image_nodes(id) ON DELETE CASCADE
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
CREATE TABLE IF NOT EXISTS image_provider_capabilities (
|
|
133
|
+
endpoint_fingerprint TEXT PRIMARY KEY,
|
|
134
|
+
provider TEXT NOT NULL,
|
|
135
|
+
supports_responses INTEGER,
|
|
136
|
+
supports_conversations INTEGER,
|
|
137
|
+
supports_conversation_delete INTEGER,
|
|
138
|
+
supports_guaranteed_idempotency INTEGER NOT NULL DEFAULT 0,
|
|
139
|
+
probe_status TEXT NOT NULL CHECK (probe_status IN ('supported', 'partial', 'unsupported', 'error')),
|
|
140
|
+
safe_reason_code TEXT,
|
|
141
|
+
checked_at TEXT NOT NULL,
|
|
142
|
+
expires_at TEXT NOT NULL
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
CREATE TABLE IF NOT EXISTS image_provider_deletion_tombstones (
|
|
146
|
+
id TEXT PRIMARY KEY,
|
|
147
|
+
provider TEXT NOT NULL,
|
|
148
|
+
endpoint_fingerprint TEXT NOT NULL,
|
|
149
|
+
provider_conversation_id TEXT NOT NULL,
|
|
150
|
+
status TEXT NOT NULL CHECK (status IN ('pending', 'retrying', 'deleted', 'unsupported', 'abandoned')),
|
|
151
|
+
attempt_count INTEGER NOT NULL DEFAULT 0,
|
|
152
|
+
last_error_code TEXT,
|
|
153
|
+
next_attempt_at TEXT NOT NULL,
|
|
154
|
+
created_at TEXT NOT NULL,
|
|
155
|
+
updated_at TEXT NOT NULL,
|
|
156
|
+
UNIQUE (provider, endpoint_fingerprint, provider_conversation_id)
|
|
157
|
+
);
|
|
158
|
+
`);
|
|
159
|
+
|
|
160
|
+
const queueColumns = new Set(db.prepare(`PRAGMA table_info(queue)`).all().map((column) => column.name));
|
|
161
|
+
if (!queueColumns.has('image_operation_id')) {
|
|
162
|
+
db.exec(`ALTER TABLE queue ADD COLUMN image_operation_id TEXT`);
|
|
163
|
+
}
|
|
164
|
+
db.exec(`
|
|
165
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_queue_image_operation
|
|
166
|
+
ON queue(image_operation_id)
|
|
167
|
+
WHERE image_operation_id IS NOT NULL;
|
|
168
|
+
`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function createImageConversationRepository(db) {
|
|
172
|
+
const statements = {
|
|
173
|
+
insertSession: db.prepare(`
|
|
174
|
+
INSERT INTO image_sessions (id, conversation_id, origin, status, schema_version, created_at, updated_at)
|
|
175
|
+
VALUES (@id, @conversationId, @origin, 'active', @schemaVersion, @createdAt, @updatedAt)
|
|
176
|
+
`),
|
|
177
|
+
touchSession: db.prepare(`UPDATE image_sessions SET updated_at = ? WHERE id = ?`),
|
|
178
|
+
getSession: db.prepare(`SELECT * FROM image_sessions WHERE id = ?`),
|
|
179
|
+
insertOperation: db.prepare(`
|
|
180
|
+
INSERT INTO image_operations (
|
|
181
|
+
id, image_session_id, source_message_id, queue_message_id, kind, parent_node_id,
|
|
182
|
+
prompt, selected_image_model, orchestration_model, provider, execution_mode,
|
|
183
|
+
parameters_json, request_fingerprint, idempotency_key, replaces_operation_id, created_at
|
|
184
|
+
) VALUES (
|
|
185
|
+
@id, @imageSessionId, @sourceMessageId, @queueMessageId, @kind, @parentNodeId,
|
|
186
|
+
@prompt, @selectedImageModel, @orchestrationModel, @provider, @executionMode,
|
|
187
|
+
@parametersJson, @requestFingerprint, @idempotencyKey, @replacesOperationId, @createdAt
|
|
188
|
+
)
|
|
189
|
+
`),
|
|
190
|
+
getOperation: db.prepare(`
|
|
191
|
+
SELECT operation.*, session.conversation_id
|
|
192
|
+
FROM image_operations operation
|
|
193
|
+
JOIN image_sessions session ON session.id = operation.image_session_id
|
|
194
|
+
WHERE operation.id = ?
|
|
195
|
+
`),
|
|
196
|
+
getOperationByQueueMessageId: db.prepare(`
|
|
197
|
+
SELECT operation.*, session.conversation_id
|
|
198
|
+
FROM image_operations operation
|
|
199
|
+
JOIN image_sessions session ON session.id = operation.image_session_id
|
|
200
|
+
WHERE operation.queue_message_id = ?
|
|
201
|
+
`),
|
|
202
|
+
linkQueueOperation: db.prepare(`UPDATE queue SET image_operation_id = ? WHERE id = ? AND image_operation_id IS NULL`),
|
|
203
|
+
insertAsset: db.prepare(`
|
|
204
|
+
INSERT INTO image_operation_assets (
|
|
205
|
+
operation_id, ordinal, role, upload_id, content_sha256, media_type, created_at
|
|
206
|
+
) VALUES (@operationId, @ordinal, @role, @uploadId, @contentSha256, @mediaType, @createdAt)
|
|
207
|
+
`),
|
|
208
|
+
listAssets: db.prepare(`
|
|
209
|
+
SELECT asset.*, file.original_name, file.size_bytes
|
|
210
|
+
FROM image_operation_assets asset
|
|
211
|
+
JOIN uploaded_files file ON file.sha256 = asset.content_sha256
|
|
212
|
+
WHERE asset.operation_id = ?
|
|
213
|
+
ORDER BY asset.ordinal ASC
|
|
214
|
+
`),
|
|
215
|
+
getNode: db.prepare(`
|
|
216
|
+
SELECT node.*, session.conversation_id
|
|
217
|
+
FROM image_nodes node
|
|
218
|
+
JOIN image_sessions session ON session.id = node.image_session_id
|
|
219
|
+
WHERE node.id = ?
|
|
220
|
+
`),
|
|
221
|
+
getNodeByAttachment: db.prepare(`
|
|
222
|
+
SELECT node.*, session.conversation_id
|
|
223
|
+
FROM image_nodes node
|
|
224
|
+
JOIN image_sessions session ON session.id = node.image_session_id
|
|
225
|
+
WHERE node.assistant_message_id = ? AND node.attachment_image_id = ?
|
|
226
|
+
`),
|
|
227
|
+
insertNode: db.prepare(`
|
|
228
|
+
INSERT INTO image_nodes (
|
|
229
|
+
id, image_session_id, operation_id, assistant_message_id, attachment_image_id, output_index, created_at
|
|
230
|
+
) VALUES (@id, @imageSessionId, @operationId, @assistantMessageId, @attachmentImageId, @outputIndex, @createdAt)
|
|
231
|
+
`),
|
|
232
|
+
insertEdge: db.prepare(`
|
|
233
|
+
INSERT INTO image_edges (
|
|
234
|
+
image_session_id, parent_node_id, child_node_id, operation_id, relation, created_at
|
|
235
|
+
) VALUES (@imageSessionId, @parentNodeId, @childNodeId, @operationId, 'edit', @createdAt)
|
|
236
|
+
`),
|
|
237
|
+
findPathToNode: db.prepare(`
|
|
238
|
+
WITH RECURSIVE descendants(node_id) AS (
|
|
239
|
+
SELECT child_node_id FROM image_edges WHERE parent_node_id = ?
|
|
240
|
+
UNION
|
|
241
|
+
SELECT edge.child_node_id
|
|
242
|
+
FROM image_edges edge
|
|
243
|
+
JOIN descendants ON edge.parent_node_id = descendants.node_id
|
|
244
|
+
)
|
|
245
|
+
SELECT 1 AS found FROM descendants WHERE node_id = ? LIMIT 1
|
|
246
|
+
`),
|
|
247
|
+
getLatestAttempt: db.prepare(`
|
|
248
|
+
SELECT * FROM image_operation_attempts
|
|
249
|
+
WHERE operation_id = ?
|
|
250
|
+
ORDER BY attempt_number DESC
|
|
251
|
+
LIMIT 1
|
|
252
|
+
`),
|
|
253
|
+
insertAttempt: db.prepare(`
|
|
254
|
+
INSERT INTO image_operation_attempts (
|
|
255
|
+
id, operation_id, attempt_number, status, provider, capability_snapshot_json, started_at
|
|
256
|
+
) VALUES (@id, @operationId, @attemptNumber, 'started', @provider, @capabilitySnapshotJson, @startedAt)
|
|
257
|
+
`),
|
|
258
|
+
finishAttempt: db.prepare(`
|
|
259
|
+
UPDATE image_operation_attempts
|
|
260
|
+
SET status = @status,
|
|
261
|
+
provider_request_id = @providerRequestId,
|
|
262
|
+
provider_conversation_id = @providerConversationId,
|
|
263
|
+
provider_response_id = @providerResponseId,
|
|
264
|
+
http_status = @httpStatus,
|
|
265
|
+
error_code = @errorCode,
|
|
266
|
+
error_message = @errorMessage,
|
|
267
|
+
completed_at = @completedAt
|
|
268
|
+
WHERE id = @id AND status = 'started'
|
|
269
|
+
`),
|
|
270
|
+
listNodesForOperation: db.prepare(`
|
|
271
|
+
SELECT node.*, message.attachments, message.text, message.model, message.mode, message.timestamp
|
|
272
|
+
FROM image_nodes node
|
|
273
|
+
JOIN messages message ON message.id = node.assistant_message_id
|
|
274
|
+
WHERE node.operation_id = ?
|
|
275
|
+
ORDER BY node.output_index ASC
|
|
276
|
+
`),
|
|
277
|
+
getParentEdge: db.prepare(`SELECT * FROM image_edges WHERE child_node_id = ?`),
|
|
278
|
+
listProviderStatesForConversation: db.prepare(`
|
|
279
|
+
SELECT DISTINCT state.provider, state.endpoint_fingerprint, state.provider_conversation_id
|
|
280
|
+
FROM image_provider_state state
|
|
281
|
+
JOIN image_sessions session ON session.id = state.image_session_id
|
|
282
|
+
WHERE session.conversation_id = ? AND state.provider_conversation_id IS NOT NULL
|
|
283
|
+
`),
|
|
284
|
+
insertDeletionTombstone: db.prepare(`
|
|
285
|
+
INSERT INTO image_provider_deletion_tombstones (
|
|
286
|
+
id, provider, endpoint_fingerprint, provider_conversation_id, status,
|
|
287
|
+
attempt_count, next_attempt_at, created_at, updated_at
|
|
288
|
+
) VALUES (@id, @provider, @endpointFingerprint, @providerConversationId, 'pending', 0, @nextAttemptAt, @createdAt, @updatedAt)
|
|
289
|
+
ON CONFLICT(provider, endpoint_fingerprint, provider_conversation_id) DO NOTHING
|
|
290
|
+
`),
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
function insertEdgeChecked(edge) {
|
|
294
|
+
const parent = statements.getNode.get(edge.parentNodeId);
|
|
295
|
+
const child = statements.getNode.get(edge.childNodeId);
|
|
296
|
+
if (!parent || !child) throw new Error('Image lineage node not found');
|
|
297
|
+
if (parent.image_session_id !== child.image_session_id || parent.image_session_id !== edge.imageSessionId) {
|
|
298
|
+
throw new Error('Image lineage nodes must belong to the same image session');
|
|
299
|
+
}
|
|
300
|
+
if (parent.conversation_id !== child.conversation_id) {
|
|
301
|
+
throw new Error('Image lineage nodes must belong to the same conversation');
|
|
302
|
+
}
|
|
303
|
+
if (Date.parse(parent.created_at) > Date.parse(child.created_at)) {
|
|
304
|
+
throw new Error('Image lineage parent cannot postdate its child');
|
|
305
|
+
}
|
|
306
|
+
if (statements.findPathToNode.get(edge.childNodeId, edge.parentNodeId)) {
|
|
307
|
+
throw new Error('Image lineage cycle detected');
|
|
308
|
+
}
|
|
309
|
+
return statements.insertEdge.run(edge);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
...statements,
|
|
314
|
+
imageSchemaVersion: IMAGE_SCHEMA_VERSION,
|
|
315
|
+
insertEdgeChecked,
|
|
316
|
+
};
|
|
317
|
+
}
|