@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
package/server/server.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { isExternallySupervised, isRelayRuntimeInvocation, runDirectRelaySupervisor } from './relay-self-restart.mjs';
|
|
5
|
+
|
|
6
|
+
const scriptPath = fileURLToPath(import.meta.url);
|
|
7
|
+
const runtimeArgs = process.argv.slice(2);
|
|
8
|
+
const interactiveStdio = process.stdin.isTTY && process.stdout.isTTY && process.stderr.isTTY;
|
|
9
|
+
|
|
10
|
+
// `--relay-runtime` marks the child this script spawned. `--supervised` means an
|
|
11
|
+
// outer supervisor (the CLI extension) owns restarts and needs to observe exit
|
|
12
|
+
// code 75 itself, so the runtime runs in this process instead of a grandchild.
|
|
13
|
+
if (isRelayRuntimeInvocation(runtimeArgs) || isExternallySupervised(runtimeArgs)) {
|
|
14
|
+
await import('./server-runtime.mjs');
|
|
15
|
+
} else {
|
|
16
|
+
await runDirectRelaySupervisor({
|
|
17
|
+
scriptPath,
|
|
18
|
+
args: runtimeArgs,
|
|
19
|
+
cwd: process.cwd(),
|
|
20
|
+
env: process.env,
|
|
21
|
+
stdio: interactiveStdio ? 'inherit' : ['ignore', 'pipe', 'pipe'],
|
|
22
|
+
logger: console,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function normalizeId(value) {
|
|
4
|
+
const text = String(value || '').trim();
|
|
5
|
+
return text || null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function loadRelayQuestionColumns(db) {
|
|
9
|
+
try {
|
|
10
|
+
const rows = db.prepare(`PRAGMA table_info(relay_questions)`).all();
|
|
11
|
+
return new Set(rows.map((row) => String(row?.name || '').trim()).filter(Boolean));
|
|
12
|
+
} catch {
|
|
13
|
+
return new Set();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createAskUserRoutingService(db, options = {}) {
|
|
18
|
+
const continuationRoutingEnabled = options?.continuationRoutingEnabled === true;
|
|
19
|
+
const sessionWorkerRegistry = options?.sessionWorkerRegistry || null;
|
|
20
|
+
const relayQuestionColumns = loadRelayQuestionColumns(db);
|
|
21
|
+
const hasContinuationColumns =
|
|
22
|
+
relayQuestionColumns.has('continuation_id')
|
|
23
|
+
&& relayQuestionColumns.has('continuation_question_id');
|
|
24
|
+
const hasStructuredAnswerColumn = relayQuestionColumns.has('structured_answer');
|
|
25
|
+
|
|
26
|
+
const findQuestion = db.prepare(`SELECT * FROM relay_questions WHERE id = ?`);
|
|
27
|
+
const updateAnswered = db.prepare(`
|
|
28
|
+
UPDATE relay_questions
|
|
29
|
+
SET status = 'answered', answer = ?, answered_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
30
|
+
WHERE id = ?
|
|
31
|
+
`);
|
|
32
|
+
const updateAnsweredStructured = hasStructuredAnswerColumn
|
|
33
|
+
? db.prepare(`
|
|
34
|
+
UPDATE relay_questions
|
|
35
|
+
SET status = 'answered', answer = ?, structured_answer = ?, answered_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
|
36
|
+
WHERE id = ?
|
|
37
|
+
`)
|
|
38
|
+
: null;
|
|
39
|
+
const findBySdkSessionAndMessage = db.prepare(`
|
|
40
|
+
SELECT * FROM relay_questions
|
|
41
|
+
WHERE sdk_session_id = ? AND message_id = ?
|
|
42
|
+
LIMIT 1
|
|
43
|
+
`);
|
|
44
|
+
const findByContinuation = hasContinuationColumns
|
|
45
|
+
? db.prepare(`
|
|
46
|
+
SELECT *
|
|
47
|
+
FROM relay_questions
|
|
48
|
+
WHERE continuation_id = ?
|
|
49
|
+
AND continuation_question_id = ?
|
|
50
|
+
ORDER BY created_at DESC
|
|
51
|
+
LIMIT 1
|
|
52
|
+
`)
|
|
53
|
+
: null;
|
|
54
|
+
|
|
55
|
+
function routeAnswer({ question_id, sdk_session_id, answer, structured_answer, conversation_id, continuation_id, continuation_question_id }) {
|
|
56
|
+
const row = findQuestion.get(question_id);
|
|
57
|
+
if (!row) return { ok: false, error: 'Question not found' };
|
|
58
|
+
|
|
59
|
+
const rowSession = normalizeId(row.sdk_session_id);
|
|
60
|
+
const incomingSession = normalizeId(sdk_session_id);
|
|
61
|
+
const rowConversation = normalizeId(row.conversation_id);
|
|
62
|
+
const incomingConversation = normalizeId(conversation_id);
|
|
63
|
+
const rowContinuationId = normalizeId(row.continuation_id);
|
|
64
|
+
const incomingContinuationId = normalizeId(continuation_id);
|
|
65
|
+
const rowContinuationQuestionId = normalizeId(row.continuation_question_id);
|
|
66
|
+
const incomingContinuationQuestionId = normalizeId(continuation_question_id);
|
|
67
|
+
const ownerWorkerId = normalizeId(row.owner_worker_id);
|
|
68
|
+
const workerOwnerSessionId = ownerWorkerId
|
|
69
|
+
? normalizeId(sessionWorkerRegistry?.getWorkerByWorkerId?.(ownerWorkerId)?.sdkSessionId)
|
|
70
|
+
: null;
|
|
71
|
+
|
|
72
|
+
if (continuationRoutingEnabled) {
|
|
73
|
+
if (rowContinuationId && incomingContinuationId && rowContinuationId !== incomingContinuationId) {
|
|
74
|
+
return { ok: false, error: 'continuation mismatch' };
|
|
75
|
+
}
|
|
76
|
+
if (rowContinuationQuestionId && incomingContinuationQuestionId && rowContinuationQuestionId !== incomingContinuationQuestionId) {
|
|
77
|
+
return { ok: false, error: 'continuation question mismatch' };
|
|
78
|
+
}
|
|
79
|
+
if (rowConversation && incomingConversation && rowConversation !== incomingConversation) {
|
|
80
|
+
return { ok: false, error: 'conversation mismatch' };
|
|
81
|
+
}
|
|
82
|
+
if (rowSession && workerOwnerSessionId && rowSession !== workerOwnerSessionId) {
|
|
83
|
+
return { ok: false, error: 'owner mismatch' };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const expectedSession = rowSession || workerOwnerSessionId;
|
|
88
|
+
if (expectedSession || incomingSession) {
|
|
89
|
+
if (!expectedSession || !incomingSession || expectedSession !== incomingSession) {
|
|
90
|
+
return { ok: false, error: 'session mismatch' };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (structured_answer && updateAnsweredStructured) {
|
|
95
|
+
updateAnsweredStructured.run(answer, structured_answer, question_id);
|
|
96
|
+
} else {
|
|
97
|
+
updateAnswered.run(answer, question_id);
|
|
98
|
+
}
|
|
99
|
+
return { ok: true };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function lookupQuestion({ sdk_session_id, message_id, continuation_id, continuation_question_id }) {
|
|
103
|
+
const continuationId = normalizeId(continuation_id);
|
|
104
|
+
const continuationQuestionId = normalizeId(continuation_question_id);
|
|
105
|
+
if (hasContinuationColumns && continuationId && continuationQuestionId) {
|
|
106
|
+
const continuationMatch = findByContinuation?.get(continuationId, continuationQuestionId) || null;
|
|
107
|
+
if (continuationMatch) return continuationMatch;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const sessionId = normalizeId(sdk_session_id);
|
|
111
|
+
const messageId = normalizeId(message_id);
|
|
112
|
+
if (!sessionId || !messageId) return null;
|
|
113
|
+
return findBySdkSessionAndMessage.get(sessionId, messageId) || null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return { routeAnswer, lookupQuestion };
|
|
117
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function normalizeId(value) {
|
|
4
|
+
return String(value || '').trim();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function buildLegacyConversationList(db) {
|
|
8
|
+
return db.prepare(`
|
|
9
|
+
SELECT id, sdk_session_id, archived, status, updated_at
|
|
10
|
+
FROM conversations
|
|
11
|
+
WHERE status != 'deleted'
|
|
12
|
+
AND (sdk_session_id IS NULL OR sdk_session_id = '')
|
|
13
|
+
ORDER BY updated_at DESC, id ASC
|
|
14
|
+
`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function buildBoundConversationList(db) {
|
|
18
|
+
return db.prepare(`
|
|
19
|
+
SELECT id, sdk_session_id
|
|
20
|
+
FROM conversations
|
|
21
|
+
WHERE status != 'deleted'
|
|
22
|
+
AND sdk_session_id IS NOT NULL
|
|
23
|
+
AND sdk_session_id != ''
|
|
24
|
+
ORDER BY updated_at DESC, id ASC
|
|
25
|
+
`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function buildConversationActivityStatements(db) {
|
|
29
|
+
return {
|
|
30
|
+
queueCount: db.prepare(`
|
|
31
|
+
SELECT COUNT(*) AS cnt
|
|
32
|
+
FROM queue
|
|
33
|
+
WHERE conversation_id = ?
|
|
34
|
+
AND status IN ('pending', 'processing', 'parked')
|
|
35
|
+
`),
|
|
36
|
+
runtimeSessionCount: db.prepare(`
|
|
37
|
+
SELECT COUNT(*) AS cnt
|
|
38
|
+
FROM runtime_sessions
|
|
39
|
+
WHERE conversation_id = ?
|
|
40
|
+
`),
|
|
41
|
+
pendingQuestionCount: db.prepare(`
|
|
42
|
+
SELECT COUNT(*) AS cnt
|
|
43
|
+
FROM relay_questions
|
|
44
|
+
WHERE conversation_id = ?
|
|
45
|
+
AND status = 'pending'
|
|
46
|
+
`),
|
|
47
|
+
conversationRows: db.prepare(`
|
|
48
|
+
SELECT id
|
|
49
|
+
FROM conversations
|
|
50
|
+
WHERE status != 'deleted'
|
|
51
|
+
`),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createCacheRebuildService({
|
|
56
|
+
db,
|
|
57
|
+
stmts,
|
|
58
|
+
io,
|
|
59
|
+
fs,
|
|
60
|
+
path,
|
|
61
|
+
uploadsDir,
|
|
62
|
+
bootstrapRuntimeSessionBindings,
|
|
63
|
+
collectOrphanedUploadsFromConversation,
|
|
64
|
+
deleteOrphanedUploads,
|
|
65
|
+
}) {
|
|
66
|
+
const legacyConversationList = buildLegacyConversationList(db);
|
|
67
|
+
const boundConversationList = buildBoundConversationList(db);
|
|
68
|
+
const activity = buildConversationActivityStatements(db);
|
|
69
|
+
|
|
70
|
+
function isLegacyConversationActive(conversationId) {
|
|
71
|
+
const sid = normalizeId(conversationId);
|
|
72
|
+
if (!sid) return false;
|
|
73
|
+
const queueCount = Number(activity.queueCount.get(sid)?.cnt || 0);
|
|
74
|
+
const runtimeSessionCount = Number(activity.runtimeSessionCount.get(sid)?.cnt || 0);
|
|
75
|
+
const pendingQuestionCount = Number(activity.pendingQuestionCount.get(sid)?.cnt || 0);
|
|
76
|
+
return queueCount > 0 || runtimeSessionCount > 0 || pendingQuestionCount > 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function purgeConversation(conversationId) {
|
|
80
|
+
const sid = normalizeId(conversationId);
|
|
81
|
+
if (!sid) return [];
|
|
82
|
+
const orphanedUploads = collectOrphanedUploadsFromConversation(sid);
|
|
83
|
+
db.transaction(() => {
|
|
84
|
+
db.prepare(`DELETE FROM relay_questions WHERE conversation_id = ?`).run(sid);
|
|
85
|
+
db.prepare(`DELETE FROM relay_boards WHERE conversation_id = ?`).run(sid);
|
|
86
|
+
db.prepare(`DELETE FROM relay_activity WHERE conversation_id = ?`).run(sid);
|
|
87
|
+
db.prepare(`DELETE FROM relay_stream_events WHERE conversation_id = ?`).run(sid);
|
|
88
|
+
db.prepare(`DELETE FROM relay_thought WHERE conversation_id = ?`).run(sid);
|
|
89
|
+
db.prepare(`DELETE FROM queue WHERE conversation_id = ?`).run(sid);
|
|
90
|
+
db.prepare(`DELETE FROM messages WHERE conversation_id = ?`).run(sid);
|
|
91
|
+
db.prepare(`DELETE FROM runtime_sessions WHERE conversation_id = ?`).run(sid);
|
|
92
|
+
db.prepare(`DELETE FROM conversations WHERE id = ?`).run(sid);
|
|
93
|
+
})();
|
|
94
|
+
deleteOrphanedUploads(orphanedUploads);
|
|
95
|
+
return orphanedUploads;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function archiveConversation(conversationId) {
|
|
99
|
+
const sid = normalizeId(conversationId);
|
|
100
|
+
if (!sid) return false;
|
|
101
|
+
const result = db.prepare(`UPDATE conversations SET archived = 1, updated_at = ? WHERE id = ? AND status != 'deleted'`).run(new Date().toISOString(), sid);
|
|
102
|
+
return Number(result?.changes || 0) > 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function fullWipeCache() {
|
|
106
|
+
const now = new Date().toISOString();
|
|
107
|
+
const conversationIds = activity.conversationRows.all().map((row) => normalizeId(row?.id)).filter(Boolean);
|
|
108
|
+
const manualTitleConversations = db.prepare(`
|
|
109
|
+
SELECT id
|
|
110
|
+
FROM conversations
|
|
111
|
+
WHERE status != 'deleted'
|
|
112
|
+
AND title_source = 'manual'
|
|
113
|
+
`).all().map((row) => normalizeId(row?.id)).filter(Boolean);
|
|
114
|
+
const manualTitleConversationSet = new Set(manualTitleConversations);
|
|
115
|
+
const uploadHashes = [];
|
|
116
|
+
for (const conversationId of conversationIds) {
|
|
117
|
+
for (const hash of collectOrphanedUploadsFromConversation(conversationId)) {
|
|
118
|
+
if (!uploadHashes.includes(hash)) uploadHashes.push(hash);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
db.transaction(() => {
|
|
123
|
+
db.prepare(`DELETE FROM relay_questions`).run();
|
|
124
|
+
db.prepare(`DELETE FROM relay_boards`).run();
|
|
125
|
+
db.prepare(`DELETE FROM relay_activity`).run();
|
|
126
|
+
db.prepare(`DELETE FROM relay_stream_events`).run();
|
|
127
|
+
db.prepare(`DELETE FROM relay_thought`).run();
|
|
128
|
+
db.prepare(`DELETE FROM queue`).run();
|
|
129
|
+
db.prepare(`DELETE FROM messages`).run();
|
|
130
|
+
db.prepare(`DELETE FROM runtime_sessions`).run();
|
|
131
|
+
db.prepare(`DELETE FROM conversations WHERE status = 'deleted' OR title_source != 'manual'`).run();
|
|
132
|
+
for (const conversationId of manualTitleConversations) {
|
|
133
|
+
db.prepare(`
|
|
134
|
+
UPDATE conversations
|
|
135
|
+
SET archived = 0,
|
|
136
|
+
status = 'active',
|
|
137
|
+
compacted_into = NULL,
|
|
138
|
+
compacted_from = NULL,
|
|
139
|
+
summary_seed = NULL,
|
|
140
|
+
seed_pending = 0,
|
|
141
|
+
updated_at = ?
|
|
142
|
+
WHERE id = ?
|
|
143
|
+
`).run(now, conversationId);
|
|
144
|
+
}
|
|
145
|
+
db.prepare(`DELETE FROM deleted_sdk_sessions`).run();
|
|
146
|
+
db.prepare(`DELETE FROM sdk_delete_requests`).run();
|
|
147
|
+
db.prepare(`DELETE FROM sdk_session_imports`).run();
|
|
148
|
+
db.prepare(`DELETE FROM upload_refs`).run();
|
|
149
|
+
db.prepare(`DELETE FROM uploaded_files`).run();
|
|
150
|
+
})();
|
|
151
|
+
|
|
152
|
+
deleteOrphanedUploads(uploadHashes);
|
|
153
|
+
|
|
154
|
+
if (uploadsDir && fs?.existsSync?.(uploadsDir)) {
|
|
155
|
+
try {
|
|
156
|
+
for (const entry of fs.readdirSync(uploadsDir, { withFileTypes: true })) {
|
|
157
|
+
const entryPath = path.join(uploadsDir, entry.name);
|
|
158
|
+
fs.rmSync(entryPath, { recursive: true, force: true });
|
|
159
|
+
}
|
|
160
|
+
} catch {}
|
|
161
|
+
try {
|
|
162
|
+
fs.mkdirSync(uploadsDir, { recursive: true });
|
|
163
|
+
} catch {}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
wipedAt: now,
|
|
168
|
+
conversationsRemoved: conversationIds.length - manualTitleConversations.length,
|
|
169
|
+
manualTitleConversationsRetained: manualTitleConversationSet.size,
|
|
170
|
+
uploadsRemoved: uploadHashes.length,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function reconcileCache() {
|
|
175
|
+
const legacyRows = legacyConversationList.all();
|
|
176
|
+
const summary = {
|
|
177
|
+
purgedConversationIds: [],
|
|
178
|
+
archivedConversationIds: [],
|
|
179
|
+
retainedConversationIds: [],
|
|
180
|
+
runtimeSessionsBootstrapped: 0,
|
|
181
|
+
uploadsRemoved: 0,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
for (const row of legacyRows) {
|
|
185
|
+
const conversationId = normalizeId(row?.id);
|
|
186
|
+
if (!conversationId) continue;
|
|
187
|
+
|
|
188
|
+
if (isLegacyConversationActive(conversationId)) {
|
|
189
|
+
if (archiveConversation(conversationId)) {
|
|
190
|
+
summary.archivedConversationIds.push(conversationId);
|
|
191
|
+
} else {
|
|
192
|
+
summary.retainedConversationIds.push(conversationId);
|
|
193
|
+
}
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
purgeConversation(conversationId);
|
|
198
|
+
summary.purgedConversationIds.push(conversationId);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
summary.runtimeSessionsBootstrapped = bootstrapRuntimeSessionBindings();
|
|
202
|
+
return summary;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function rebuildCache({ mode = 'reconcile' } = {}) {
|
|
206
|
+
const normalizedMode = String(mode || 'reconcile').trim().toLowerCase();
|
|
207
|
+
if (normalizedMode === 'full' || normalizedMode === 'wipe' || normalizedMode === 'rebuild') {
|
|
208
|
+
return {
|
|
209
|
+
mode: 'full',
|
|
210
|
+
summary: fullWipeCache(),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (normalizedMode !== 'reconcile') {
|
|
215
|
+
const error = new Error(`Unsupported cache rebuild mode: ${normalizedMode || 'empty'}`);
|
|
216
|
+
error.statusCode = 400;
|
|
217
|
+
throw error;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
mode: 'reconcile',
|
|
222
|
+
summary: reconcileCache(),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
rebuildCache,
|
|
228
|
+
reconcileCache,
|
|
229
|
+
fullWipeCache,
|
|
230
|
+
purgeConversation,
|
|
231
|
+
archiveConversation,
|
|
232
|
+
isLegacyConversationActive,
|
|
233
|
+
listLegacyConversations: () => legacyConversationList.all(),
|
|
234
|
+
listBoundConversations: () => boundConversationList.all(),
|
|
235
|
+
};
|
|
236
|
+
}
|