@vellumai/assistant 0.8.0 → 0.8.1
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/AGENTS.md +11 -0
- package/Dockerfile +5 -4
- package/README.md +2 -2
- package/docker-entrypoint.sh +16 -0
- package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +420 -0
- package/eslint-rules/cli-no-daemon-internals.js +283 -0
- package/eslint.config.mjs +12 -0
- package/knip.json +2 -1
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -1
- package/openapi.yaml +4847 -1698
- package/package.json +3 -1
- package/scripts/generate-openapi.ts +52 -4
- package/scripts/sync-llm-catalog.ts +165 -0
- package/scripts/sync-web-search-catalog.ts +107 -0
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +169 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +26 -1
- package/src/__tests__/anthropic-provider.test.ts +92 -2
- package/src/__tests__/app-control-flow.test.ts +7 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +232 -0
- package/src/__tests__/avatar-identity-sync.test.ts +87 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +11 -22
- package/src/__tests__/btw-routes.test.ts +1 -0
- package/src/__tests__/call-site-routing-provider.test.ts +172 -45
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +44 -3
- package/src/__tests__/channel-policy.test.ts +12 -0
- package/src/__tests__/checker.test.ts +89 -0
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +35 -7
- package/src/__tests__/compact-event-conversation-id-guard.test.ts +33 -5
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +26 -1
- package/src/__tests__/config-loader-backfill.test.ts +526 -102
- package/src/__tests__/config-loader-corrupt.test.ts +68 -0
- package/src/__tests__/config-loader-platform-defaults.test.ts +77 -23
- package/src/__tests__/config-schema-cmd.test.ts +63 -29
- package/src/__tests__/config-schema.test.ts +14 -3
- package/src/__tests__/config-set-platform-guard.test.ts +75 -152
- package/src/__tests__/config-set-route.test.ts +198 -0
- package/src/__tests__/config-watcher.test.ts +6 -0
- package/src/__tests__/contacts-tools.test.ts +51 -199
- package/src/__tests__/context-search-agent-protocol.test.ts +21 -2
- package/src/__tests__/context-search-agent-runner.test.ts +22 -138
- package/src/__tests__/context-search-conversations-source.test.ts +42 -16
- package/src/__tests__/context-search-fanout.test.ts +20 -157
- package/src/__tests__/context-search-memory-v2-source.test.ts +3 -3
- package/src/__tests__/context-search-types.test.ts +7 -2
- package/src/__tests__/context-window-manager.test.ts +389 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-crud-inference-profile.test.ts +100 -0
- package/src/__tests__/conversation-error.test.ts +38 -0
- package/src/__tests__/conversation-fork-crud.test.ts +241 -1
- package/src/__tests__/conversation-inference-profile-route.test.ts +14 -14
- package/src/__tests__/conversation-init.benchmark.test.ts +1 -0
- package/src/__tests__/conversation-lifecycle.test.ts +124 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +100 -1
- package/src/__tests__/conversation-process-callsite.test.ts +21 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +4 -4
- package/src/__tests__/conversation-slash-commands.test.ts +194 -2
- package/src/__tests__/conversation-surfaces-app-control.test.ts +323 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -6
- package/src/__tests__/daemon-credential-client.test.ts +56 -1
- package/src/__tests__/db-activation-state-fk-cascade.test.ts +132 -0
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +37 -0
- package/src/__tests__/db-memory-graph-event-date-repair.test.ts +43 -20
- package/src/__tests__/db-proxy-transaction.test.ts +206 -0
- package/src/__tests__/external-plugin-loader.test.ts +458 -0
- package/src/__tests__/filing-service.test.ts +23 -3
- package/src/__tests__/fixtures/mock-chrome-extension.ts +5 -0
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/graph-extraction-event-date.test.ts +34 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -8
- package/src/__tests__/heartbeat-disk-pressure.test.ts +21 -8
- package/src/__tests__/heartbeat-service.test.ts +50 -233
- package/src/__tests__/history-repair.test.ts +89 -0
- package/src/__tests__/host-app-control-proxy.test.ts +109 -1
- package/src/__tests__/host-app-control-routes.test.ts +247 -1
- package/src/__tests__/host-browser-proxy.test.ts +416 -20
- package/src/__tests__/host-browser-routes.test.ts +325 -33
- package/src/__tests__/host-proxy-preactivation.test.ts +211 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +246 -0
- package/src/__tests__/inference-profile-reaper.test.ts +154 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +398 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +236 -0
- package/src/__tests__/inline-skill-load-permissions.test.ts +6 -1
- package/src/__tests__/install-skill-routing.test.ts +2 -2
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +15 -0
- package/src/__tests__/llm-callsite-catalog.test.ts +20 -1
- package/src/__tests__/llm-catalog-parity.test.ts +146 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +188 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +124 -0
- package/src/__tests__/llm-resolver.test.ts +46 -0
- package/src/__tests__/managed-profile-guard.test.ts +131 -2
- package/src/__tests__/mcp-auth-routes.test.ts +1 -0
- package/src/__tests__/mcp-cli.test.ts +182 -220
- package/src/__tests__/mcp-health-check.test.ts +56 -27
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +18 -11
- package/src/__tests__/message-complete-display-id.test.ts +175 -0
- package/src/__tests__/notification-platform-adapter.test.ts +229 -0
- package/src/__tests__/oauth-cli.test.ts +38 -2009
- package/src/__tests__/oauth-commands-routes.test.ts +711 -0
- package/src/__tests__/oauth-connect-routes.test.ts +174 -11
- package/src/__tests__/oauth-providers-routes.test.ts +14 -10
- package/src/__tests__/openai-responses-cutover-guard.test.ts +33 -12
- package/src/__tests__/openai-responses-provider.test.ts +17 -0
- package/src/__tests__/plugin-bootstrap.test.ts +31 -2
- package/src/__tests__/plugin-route-contribution.test.ts +31 -3
- package/src/__tests__/plugin-tool-contribution.test.ts +31 -3
- package/src/__tests__/plugin-types.test.ts +13 -11
- package/src/__tests__/process-message-background-slack.test.ts +46 -0
- package/src/__tests__/profile-entry-status.test.ts +43 -0
- package/src/__tests__/provider-managed-proxy-integration.test.ts +12 -4
- package/src/__tests__/provider-registry-ollama.test.ts +12 -4
- package/src/__tests__/provider-send-message-override-profile.test.ts +10 -4
- package/src/__tests__/relay-server.test.ts +118 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +15 -0
- package/src/__tests__/schedule-retry.test.ts +56 -4
- package/src/__tests__/schedule-routes.test.ts +104 -0
- package/src/__tests__/scheduler-disk-pressure.test.ts +0 -4
- package/src/__tests__/scheduler-recurrence.test.ts +87 -34
- package/src/__tests__/scheduler-reuse-conversation.test.ts +161 -5
- package/src/__tests__/scheduler-wake.test.ts +0 -63
- package/src/__tests__/secret-allowlist.test.ts +1 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +12 -4
- package/src/__tests__/shell-credential-ref.test.ts +95 -3
- package/src/__tests__/shell-tool-proxy-mode.test.ts +14 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
- package/src/__tests__/skill-load-tool.test.ts +2 -4
- package/src/__tests__/subagent-call-site-routing.test.ts +78 -16
- package/src/__tests__/suggestion-routes.test.ts +3 -3
- package/src/__tests__/sync-message-contract.test.ts +63 -0
- package/src/__tests__/task-scheduler.test.ts +88 -23
- package/src/__tests__/update-bulletin-job.test.ts +96 -193
- package/src/__tests__/usage-cli.test.ts +11 -73
- package/src/__tests__/user-plugin-loader.test.ts +145 -0
- package/src/__tests__/vercel-config.test.ts +168 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +86 -0
- package/src/__tests__/web-search.test.ts +303 -2
- package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +1 -21
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +58 -0
- package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +53 -20
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +191 -0
- package/src/__tests__/workspace-migration-076-drop-services-inference-mode.test.ts +211 -0
- package/src/__tests__/workspace-migration-077-seed-memory-router-callsite.test.ts +174 -0
- package/src/__tests__/workspace-migration-079-home-feed-notification-only.test.ts +323 -0
- package/src/__tests__/workspace-migration-080-restrict-vercel-api-token-metadata.test.ts +299 -0
- package/src/__tests__/workspace-migration-081-backfill-bash-allowed-tools.test.ts +410 -0
- package/src/__tests__/workspace-migration-082-backfill-managed-profile-labels.test.ts +268 -0
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +3 -3
- package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +115 -0
- package/src/acp/__tests__/helpers/which-stub.ts +4 -2
- package/src/acp/resolve-agent.test.ts +25 -0
- package/src/acp/resolve-agent.ts +13 -2
- package/src/acp/session-manager.ts +14 -0
- package/src/approvals/guardian-request-resolvers.ts +32 -87
- package/src/calls/relay-server.ts +35 -0
- package/src/calls/relay-setup-router.ts +36 -0
- package/src/calls/types.ts +1 -0
- package/src/calls/voice-session-bridge.ts +23 -4
- package/src/channels/config.ts +14 -1
- package/src/channels/types.ts +1 -0
- package/src/cli/AGENTS.md +164 -4
- package/src/cli/__tests__/notifications.test.ts +54 -0
- package/src/cli/commands/__tests__/avatar.test.ts +540 -0
- package/src/cli/commands/__tests__/backup.test.ts +236 -776
- package/src/cli/commands/__tests__/cache.test.ts +1 -1
- package/src/cli/commands/__tests__/changelog.test.ts +593 -0
- package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +503 -0
- package/src/cli/commands/__tests__/conversations-import.test.ts +515 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +140 -167
- package/src/cli/commands/__tests__/domain-status.test.ts +137 -76
- package/src/cli/commands/__tests__/email-attachment.test.ts +314 -337
- package/src/cli/commands/__tests__/email-core.test.ts +579 -0
- package/src/cli/commands/__tests__/image-generation.test.ts +87 -824
- package/src/cli/commands/__tests__/inference-send.test.ts +30 -266
- package/src/cli/commands/__tests__/inference-session.test.ts +423 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +81 -110
- package/src/cli/commands/__tests__/skills.test.ts +563 -0
- package/src/cli/commands/__tests__/status.test.ts +249 -0
- package/src/cli/commands/__tests__/stt.test.ts +320 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +4 -603
- package/src/cli/commands/__tests__/tts.test.ts +321 -0
- package/src/cli/commands/__tests__/webhooks.test.ts +86 -511
- package/src/cli/commands/attachment.ts +8 -3
- package/src/cli/commands/audit.ts +95 -64
- package/src/cli/commands/auth.ts +61 -58
- package/src/cli/commands/avatar.ts +276 -390
- package/src/cli/commands/backup.ts +409 -505
- package/src/cli/commands/bash.ts +9 -5
- package/src/cli/commands/browser.ts +28 -9
- package/src/cli/commands/cache.ts +9 -4
- package/src/cli/commands/changelog.ts +414 -0
- package/src/cli/commands/channel-verification-sessions.ts +238 -317
- package/src/cli/commands/clients.ts +8 -3
- package/src/cli/commands/completions.ts +9 -9
- package/src/cli/commands/config.ts +102 -72
- package/src/cli/commands/contacts.ts +575 -696
- package/src/cli/commands/conversations-defer.ts +17 -69
- package/src/cli/commands/conversations-import.ts +90 -253
- package/src/cli/commands/conversations.ts +346 -436
- package/src/cli/commands/credential-execution.ts +9 -6
- package/src/cli/commands/credentials.ts +456 -736
- package/src/cli/commands/domain.ts +128 -206
- package/src/cli/commands/email.ts +606 -794
- package/src/cli/commands/gateway.ts +8 -1
- package/src/cli/commands/image-generation.ts +157 -205
- package/src/cli/commands/inference-providers.ts +352 -0
- package/src/cli/commands/inference-session.ts +415 -0
- package/src/cli/commands/inference.ts +87 -65
- package/src/cli/commands/keys.ts +8 -3
- package/src/cli/commands/mcp.ts +103 -287
- package/src/cli/commands/memory-v2.ts +162 -516
- package/src/cli/commands/notifications.ts +33 -7
- package/src/cli/commands/oauth/apps.ts +292 -261
- package/src/cli/commands/oauth/connect.ts +176 -297
- package/src/cli/commands/oauth/disconnect.ts +16 -215
- package/src/cli/commands/oauth/index.ts +49 -45
- package/src/cli/commands/oauth/mode.ts +43 -199
- package/src/cli/commands/oauth/ping.ts +17 -125
- package/src/cli/commands/oauth/providers.ts +732 -921
- package/src/cli/commands/oauth/request.ts +60 -350
- package/src/cli/commands/oauth/shared.ts +11 -121
- package/src/cli/commands/oauth/status.ts +31 -121
- package/src/cli/commands/oauth/token.ts +13 -55
- package/src/cli/commands/pending.ts +19 -10
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +133 -183
- package/src/cli/commands/platform/__tests__/connect.test.ts +66 -181
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +71 -227
- package/src/cli/commands/platform/__tests__/status.test.ts +169 -287
- package/src/cli/commands/platform/connect.ts +16 -80
- package/src/cli/commands/platform/disconnect.ts +14 -112
- package/src/cli/commands/platform/index.ts +177 -246
- package/src/cli/commands/routes.ts +153 -336
- package/src/cli/commands/sequence.ts +316 -360
- package/src/cli/commands/skills.ts +449 -671
- package/src/cli/commands/status.ts +58 -37
- package/src/cli/commands/stt.ts +94 -262
- package/src/cli/commands/task.ts +14 -40
- package/src/cli/commands/trust.ts +8 -3
- package/src/cli/commands/tts.ts +162 -167
- package/src/cli/commands/ui.ts +35 -42
- package/src/cli/commands/usage.ts +188 -126
- package/src/cli/commands/watchers.ts +8 -3
- package/src/cli/commands/webhooks.ts +99 -193
- package/src/cli/lib/__tests__/register-command.test.ts +85 -0
- package/src/cli/lib/daemon-credential-client.ts +4 -5
- package/src/cli/lib/nested-value.ts +44 -0
- package/src/cli/lib/open-browser.ts +36 -0
- package/src/cli/lib/register-command.ts +19 -0
- package/src/cli/lib/time-ago.ts +34 -0
- package/src/cli/program.ts +2 -4
- package/src/cli/utils/__tests__/conversation-id.test.ts +66 -0
- package/src/cli/utils/__tests__/parse-duration.test.ts +49 -0
- package/src/cli/utils/conversation-id.ts +30 -0
- package/src/cli/utils/parse-duration.ts +41 -0
- package/src/config/acp-defaults.test.ts +5 -1
- package/src/config/acp-defaults.ts +11 -4
- package/src/config/bundled-skills/acp/TOOLS.json +2 -2
- package/src/config/bundled-skills/app-control/TOOLS.json +32 -0
- package/src/config/bundled-skills/contacts/SKILL.md +12 -45
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -57
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +0 -12
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +0 -58
- package/src/config/bundled-tool-registry.ts +0 -2
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/llm-resolver.ts +16 -1
- package/src/config/loader.ts +76 -14
- package/src/config/raw-config-utils.ts +2 -30
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +49 -0
- package/src/config/schemas/call-site-catalog.ts +29 -7
- package/src/config/schemas/llm-request-logs.ts +57 -0
- package/src/config/schemas/llm.ts +52 -2
- package/src/config/schemas/memory-retrospective.ts +48 -0
- package/src/config/schemas/memory-v2.ts +32 -1
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/services.ts +15 -12
- package/src/config/seed-inference-profiles.ts +195 -134
- package/src/contacts/contact-store.ts +0 -61
- package/src/context/window-manager.ts +191 -5
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +79 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +109 -4
- package/src/daemon/__tests__/daemon-skill-host.test.ts +10 -4
- package/src/daemon/approval-generators.ts +23 -29
- package/src/daemon/config-watcher.ts +2 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +24 -0
- package/src/daemon/conversation-agent-loop.ts +127 -97
- package/src/daemon/conversation-error.ts +21 -0
- package/src/daemon/conversation-lifecycle.ts +46 -5
- package/src/daemon/conversation-process.ts +36 -19
- package/src/daemon/conversation-runtime-assembly.ts +14 -5
- package/src/daemon/conversation-slash.ts +175 -23
- package/src/daemon/conversation-store.ts +17 -10
- package/src/daemon/conversation-surfaces.ts +76 -12
- package/src/daemon/conversation-tool-setup.ts +24 -14
- package/src/daemon/conversation.ts +48 -9
- package/src/daemon/external-plugins-bootstrap.ts +18 -8
- package/src/daemon/guardian-action-generators.ts +7 -22
- package/src/daemon/handlers/config-model.ts +8 -126
- package/src/daemon/handlers/config-slack-channel.ts +10 -7
- package/src/daemon/handlers/config-vercel.ts +3 -1
- package/src/daemon/handlers/skills.ts +84 -5
- package/src/daemon/history-repair.ts +33 -6
- package/src/daemon/host-app-control-proxy.ts +44 -19
- package/src/daemon/host-bash-proxy.ts +85 -158
- package/src/daemon/host-browser-proxy.ts +96 -35
- package/src/daemon/host-proxy-base.ts +13 -1
- package/src/daemon/host-proxy-preactivation.ts +25 -1
- package/src/daemon/identity-helpers.ts +19 -0
- package/src/daemon/lifecycle.ts +42 -43
- package/src/daemon/meet-host-supervisor.ts +15 -15
- package/src/daemon/memory-v2-startup.ts +9 -2
- package/src/daemon/message-protocol.ts +6 -0
- package/src/daemon/message-types/bookmarks.ts +18 -0
- package/src/daemon/message-types/conversations.ts +12 -9
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/sync.ts +60 -0
- package/src/daemon/pkb-reminder-builder.test.ts +54 -13
- package/src/daemon/pkb-reminder-builder.ts +21 -7
- package/src/daemon/process-message.ts +56 -23
- package/src/daemon/server.ts +23 -18
- package/src/daemon/shutdown-handlers.ts +0 -2
- package/src/daemon/tool-setup-types.ts +9 -0
- package/src/daemon/tool-side-effects.ts +6 -4
- package/src/daemon/wake-target-adapter.ts +11 -0
- package/src/export/transcript-formatter.ts +61 -2
- package/src/filing/filing-service.ts +40 -53
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +359 -0
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +148 -127
- package/src/home/__tests__/feed-types.test.ts +63 -131
- package/src/home/__tests__/feed-writer.test.ts +77 -278
- package/src/home/__tests__/post-connect-feed.test.ts +9 -12
- package/src/home/feed-types.ts +19 -73
- package/src/home/feed-writer.ts +25 -156
- package/src/home/post-connect-feed.ts +1 -3
- package/src/ipc/__tests__/cli-ipc.test.ts +2 -0
- package/src/ipc/__tests__/email-ipc.test.ts +506 -0
- package/src/ipc/__tests__/exit-helper.test.ts +104 -0
- package/src/ipc/__tests__/streaming-client.test.ts +237 -0
- package/src/ipc/__tests__/streaming-framing.test.ts +142 -0
- package/src/ipc/assistant-server.ts +55 -6
- package/src/ipc/cli-client.ts +370 -50
- package/src/ipc/routes/db-proxy-transaction.ts +151 -0
- package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +60 -0
- package/src/ipc/skill-routes/events.ts +30 -3
- package/src/live-voice/__tests__/live-voice-session-manager.test.ts +46 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +1 -0
- package/src/live-voice/live-voice-session-manager.ts +11 -4
- package/src/live-voice/live-voice-session.ts +14 -6
- package/src/memory/__tests__/bookmark-crud.test.ts +258 -0
- package/src/memory/__tests__/bookmark-schema.test.ts +181 -0
- package/src/memory/__tests__/conversation-types.test.ts +36 -0
- package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +130 -0
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +177 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +328 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +213 -0
- package/src/memory/__tests__/memory-retrospective-trigger-check.test.ts +90 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +69 -0
- package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +3 -0
- package/src/memory/bookmark-crud.ts +179 -0
- package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +31 -9
- package/src/memory/context-search/agent-protocol.ts +5 -1
- package/src/memory/context-search/agent-runner.ts +60 -85
- package/src/memory/context-search/limits.ts +1 -4
- package/src/memory/context-search/search.ts +23 -113
- package/src/memory/context-search/sources/conversations.ts +18 -6
- package/src/memory/context-search/sources/memory-v2.ts +39 -14
- package/src/memory/context-search/sources/memory.ts +7 -0
- package/src/memory/context-search/sources/workspace.ts +13 -10
- package/src/memory/context-search/types.ts +1 -1
- package/src/memory/conversation-bootstrap.ts +11 -0
- package/src/memory/conversation-crud.ts +312 -10
- package/src/memory/conversation-queries.ts +9 -5
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/conversation-types.ts +16 -0
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.ts +2 -1
- package/src/memory/embedding-runtime-manager.ts +1 -2
- package/src/memory/graph/__tests__/remember-description.test.ts +55 -0
- package/src/memory/graph/conversation-graph-memory.ts +76 -5
- package/src/memory/graph/extraction.ts +4 -0
- package/src/memory/graph/graph-memory-state-store.ts +16 -3
- package/src/memory/graph/tool-handlers.ts +17 -7
- package/src/memory/graph/tools.ts +44 -5
- package/src/memory/indexer.ts +17 -0
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +13 -15
- package/src/memory/jobs/embed-concept-page.ts +45 -9
- package/src/memory/jobs-store.ts +51 -1
- package/src/memory/jobs-worker.ts +52 -3
- package/src/memory/llm-request-log-source-clickhouse.ts +317 -0
- package/src/memory/llm-request-log-source-local.ts +26 -0
- package/src/memory/llm-request-log-source.ts +97 -0
- package/src/memory/llm-request-log-store.ts +1 -1
- package/src/memory/memory-retrospective-constants.ts +13 -0
- package/src/memory/memory-retrospective-enqueue.ts +114 -0
- package/src/memory/memory-retrospective-job.ts +351 -0
- package/src/memory/memory-retrospective-startup-cleanup.ts +108 -0
- package/src/memory/memory-retrospective-state.ts +162 -0
- package/src/memory/memory-retrospective-trigger-check.ts +91 -0
- package/src/memory/memory-v2-activation-log-store.ts +49 -5
- package/src/memory/memory-v2-concept-frequency.ts +4 -0
- package/src/memory/message-content.ts +38 -1
- package/src/memory/migrations/227-add-conversation-inference-profile.ts +6 -1
- package/src/memory/migrations/228-rename-inference-profile-snake-case.ts +20 -7
- package/src/memory/migrations/229-delete-private-conversations.test.ts +70 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +12 -0
- package/src/memory/migrations/231-repair-memory-graph-event-dates.ts +16 -2
- package/src/memory/migrations/240-conversation-inference-profile-session.ts +25 -0
- package/src/memory/migrations/241-activation-state-fk-cascade.ts +50 -0
- package/src/memory/migrations/242-message-bookmarks.ts +38 -0
- package/src/memory/migrations/243-provider-connections.ts +68 -0
- package/src/memory/migrations/244-provider-connection-status-label.ts +23 -0
- package/src/memory/migrations/245-memory-retrospective-state.ts +36 -0
- package/src/memory/migrations/246-backfill-provider-connection-label.ts +81 -0
- package/src/memory/migrations/__tests__/244-provider-connection-status-label.test.ts +84 -0
- package/src/memory/migrations/__tests__/245-memory-retrospective-state.test.ts +125 -0
- package/src/memory/migrations/__tests__/246-backfill-provider-connection-label.test.ts +192 -0
- package/src/memory/migrations/index.ts +7 -0
- package/src/memory/published-pages-store.ts +16 -0
- package/src/memory/schema/bookmarks.ts +38 -0
- package/src/memory/schema/conversations.ts +2 -0
- package/src/memory/schema/index.ts +2 -0
- package/src/memory/schema/inference.ts +29 -0
- package/src/memory/schema/memory-core.ts +9 -0
- package/src/memory/search/semantic.ts +1 -4
- package/src/memory/v2/__tests__/__snapshots__/prompts-router.test.ts.snap +27 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +5 -5
- package/src/memory/v2/__tests__/activation.test.ts +11 -4
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +38 -21
- package/src/memory/v2/__tests__/consolidation-job.test.ts +123 -135
- package/src/memory/v2/__tests__/edge-index.test.ts +1 -1
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +111 -0
- package/src/memory/v2/__tests__/injection.test.ts +628 -10
- package/src/memory/v2/__tests__/migration.test.ts +7 -3
- package/src/memory/v2/__tests__/page-index.test.ts +277 -0
- package/src/memory/v2/__tests__/page-store.test.ts +14 -1
- package/src/memory/v2/__tests__/prompts-router.test.ts +257 -0
- package/src/memory/v2/__tests__/qdrant.test.ts +72 -0
- package/src/memory/v2/__tests__/reranker.test.ts +4 -4
- package/src/memory/v2/__tests__/router.test.ts +516 -0
- package/src/memory/v2/__tests__/sim.test.ts +45 -1
- package/src/memory/v2/__tests__/skill-store.test.ts +58 -3
- package/src/memory/v2/__tests__/static-context.test.ts +7 -22
- package/src/memory/v2/__tests__/sweep-job.test.ts +95 -0
- package/src/memory/v2/activation-store.ts +34 -5
- package/src/memory/v2/activation.ts +40 -27
- package/src/memory/v2/backfill-jobs.ts +17 -84
- package/src/memory/v2/consolidation-job.ts +85 -78
- package/src/memory/v2/frontmatter-sweep.ts +91 -0
- package/src/memory/v2/injection.ts +440 -109
- package/src/memory/v2/migration.ts +117 -20
- package/src/memory/v2/page-index.ts +191 -0
- package/src/memory/v2/page-store.ts +3 -0
- package/src/memory/v2/prompts/consolidation.ts +9 -7
- package/src/memory/v2/prompts/router.ts +192 -0
- package/src/memory/v2/qdrant.ts +100 -87
- package/src/memory/v2/reranker.ts +14 -7
- package/src/memory/v2/router.ts +322 -0
- package/src/memory/v2/sim.ts +25 -12
- package/src/memory/v2/skill-store.ts +118 -29
- package/src/memory/v2/static-context.ts +16 -9
- package/src/memory/v2/sweep-job.ts +122 -96
- package/src/memory/v2/types.ts +10 -6
- package/src/memory/validation.ts +13 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +182 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +199 -0
- package/src/notifications/__tests__/signal-registry.test.ts +17 -0
- package/src/notifications/adapters/platform.ts +171 -0
- package/src/notifications/conversation-pairing.ts +2 -2
- package/src/notifications/copy-composer.ts +15 -0
- package/src/notifications/destination-resolver.ts +21 -0
- package/src/notifications/emit-signal.ts +28 -1
- package/src/notifications/home-feed-side-effect.ts +111 -0
- package/src/notifications/signal.ts +5 -0
- package/src/permissions/checker.ts +12 -0
- package/src/permissions/ipc-risk-types.ts +2 -0
- package/src/plugin-api/index.ts +13 -0
- package/src/plugin-api/package.json +12 -0
- package/src/plugin-api/types.ts +62 -0
- package/src/plugins/defaults/injectors.ts +19 -3
- package/src/plugins/external-plugin-loader.ts +294 -0
- package/src/plugins/types.ts +46 -30
- package/src/plugins/user-loader.ts +64 -41
- package/src/proactive-artifact/job.test.ts +12 -4
- package/src/proactive-artifact/job.ts +4 -0
- package/src/proactive-artifact/trigger-state.test.ts +9 -0
- package/src/proactive-artifact/trigger-state.ts +4 -0
- package/src/prompts/__tests__/system-prompt.test.ts +105 -0
- package/src/prompts/system-prompt.ts +22 -1
- package/src/prompts/update-bulletin-job.ts +61 -73
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +279 -0
- package/src/providers/__tests__/inference.test.ts +288 -0
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +6 -0
- package/src/providers/__tests__/retry-callsite.test.ts +14 -32
- package/src/providers/__tests__/satellite-connection-routing.test.ts +510 -0
- package/src/providers/__tests__/search-provider-catalog.test.ts +80 -0
- package/src/providers/anthropic/client.ts +95 -26
- package/src/providers/call-site-routing.ts +94 -16
- package/src/providers/connection-resolution.ts +163 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +250 -0
- package/src/providers/inference/adapter-factory.ts +173 -0
- package/src/providers/inference/auth.ts +112 -0
- package/src/providers/inference/backfill.ts +196 -0
- package/src/providers/inference/connections.ts +356 -0
- package/src/providers/inference/resolve-auth.ts +65 -0
- package/src/providers/model-catalog.ts +104 -6
- package/src/providers/openai/responses-provider.ts +4 -2
- package/src/providers/provider-env-vars.ts +17 -7
- package/src/providers/provider-secret-catalog.ts +49 -30
- package/src/providers/provider-send-message.ts +41 -20
- package/src/providers/registry.ts +143 -159
- package/src/providers/retry.ts +18 -10
- package/src/providers/search-provider-catalog.ts +121 -0
- package/src/runtime/AGENTS.md +18 -5
- package/src/runtime/__tests__/background-job-runner.test.ts +357 -0
- package/src/runtime/__tests__/pre-first-message-gate.test.ts +82 -0
- package/src/runtime/actor-trust-resolver.ts +32 -10
- package/src/runtime/agent-wake.ts +35 -6
- package/src/runtime/assistant-event-hub.ts +3 -85
- package/src/runtime/auth/route-policy.ts +303 -8
- package/src/runtime/auth/same-actor.ts +2 -0
- package/src/runtime/background-job-runner.ts +339 -0
- package/src/runtime/btw-sidechain.ts +1 -0
- package/src/runtime/http-router.ts +36 -1
- package/src/runtime/http-server.ts +31 -5
- package/src/runtime/http-types.ts +2 -0
- package/src/runtime/middleware/__tests__/request-logger.test.ts +162 -0
- package/src/runtime/middleware/request-logger.ts +62 -1
- package/src/runtime/pre-first-message-gate.ts +83 -0
- package/src/runtime/routes/__tests__/backup-routes.test.ts +8 -1
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +251 -0
- package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +142 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +315 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +189 -0
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +15 -136
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +736 -0
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +4 -4
- package/src/runtime/routes/__tests__/stt-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +384 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/acp-routes.ts +10 -8
- package/src/runtime/routes/app-management-routes.ts +228 -3
- package/src/runtime/routes/approval-routes.ts +0 -18
- package/src/runtime/routes/audit-routes.ts +43 -0
- package/src/runtime/routes/auth-routes.ts +72 -0
- package/src/runtime/routes/avatar-routes.ts +273 -20
- package/src/runtime/routes/backup-routes.ts +406 -2
- package/src/runtime/routes/bookmark-routes.ts +154 -0
- package/src/runtime/routes/channel-verification-routes.ts +2 -1
- package/src/runtime/routes/contact-routes.ts +0 -160
- package/src/runtime/routes/conversation-cli-routes.ts +192 -0
- package/src/runtime/routes/conversation-management-routes.ts +30 -43
- package/src/runtime/routes/conversation-query-routes.ts +334 -86
- package/src/runtime/routes/conversation-routes.ts +31 -10
- package/src/runtime/routes/conversations-import-routes.ts +229 -0
- package/src/runtime/routes/credential-routes.ts +540 -0
- package/src/runtime/routes/debug-routes.ts +2 -2
- package/src/runtime/routes/document-pdf-renderer.ts +5 -1
- package/src/runtime/routes/domain-routes.ts +167 -0
- package/src/runtime/routes/email-routes.ts +603 -0
- package/src/runtime/routes/errors.ts +2 -2
- package/src/runtime/routes/events-routes.ts +192 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -78
- package/src/runtime/routes/host-app-control-routes.ts +44 -2
- package/src/runtime/routes/host-browser-routes.ts +103 -22
- package/src/runtime/routes/http-adapter.ts +2 -0
- package/src/runtime/routes/identity-routes.ts +5 -0
- package/src/runtime/routes/image-generation-routes.ts +99 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +137 -1
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +87 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +156 -0
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +22 -4
- package/src/runtime/routes/index.ts +36 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +312 -0
- package/src/runtime/routes/inference-profile-session-reaper.ts +98 -0
- package/src/runtime/routes/inference-profile-session-routes.ts +146 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +317 -0
- package/src/runtime/routes/inference-send-routes.ts +115 -0
- package/src/runtime/routes/integrations/twilio.ts +1 -0
- package/src/runtime/routes/mcp-auth-routes.ts +283 -9
- package/src/runtime/routes/memory-v2-routes.ts +13 -398
- package/src/runtime/routes/notification-routes.ts +2 -0
- package/src/runtime/routes/oauth-apps.ts +112 -7
- package/src/runtime/routes/oauth-commands-routes.ts +1007 -0
- package/src/runtime/routes/oauth-connect-routes.ts +67 -5
- package/src/runtime/routes/oauth-providers.ts +298 -8
- package/src/runtime/routes/platform-routes.ts +336 -0
- package/src/runtime/routes/playground/inject-failures.ts +2 -1
- package/src/runtime/routes/playground/reset-circuit.ts +2 -1
- package/src/runtime/routes/playground/state.ts +2 -1
- package/src/runtime/routes/publish-routes.ts +221 -0
- package/src/runtime/routes/schedule-routes.ts +82 -0
- package/src/runtime/routes/sequence-routes.ts +291 -0
- package/src/runtime/routes/settings-routes.ts +2 -10
- package/src/runtime/routes/skills-routes.ts +31 -1
- package/src/runtime/routes/stt-routes.ts +240 -3
- package/src/runtime/routes/surface-action-routes.ts +43 -7
- package/src/runtime/routes/tts-routes.ts +67 -0
- package/src/runtime/routes/types.ts +32 -0
- package/src/runtime/routes/user-routes-cli.ts +243 -0
- package/src/runtime/routes/webhook-routes.ts +165 -0
- package/src/runtime/sync/resource-sync-events.ts +25 -0
- package/src/runtime/sync/sync-publisher.test.ts +105 -0
- package/src/runtime/sync/sync-publisher.ts +21 -0
- package/src/schedule/scheduler.ts +200 -123
- package/src/security/__tests__/provider-key-env-fallback.test.ts +12 -6
- package/src/security/secret-patterns.ts +3 -0
- package/src/sequence/engine.ts +38 -40
- package/src/subagent/manager.ts +20 -15
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +206 -0
- package/src/tools/browser/browser-execution.ts +15 -4
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +174 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +16 -13
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +24 -1
- package/src/tools/browser/cdp-client/factory.ts +66 -5
- package/src/tools/browser/runtime-check.ts +77 -0
- package/src/tools/memory/register.test.ts +3 -3
- package/src/tools/memory/register.ts +9 -1
- package/src/tools/network/__tests__/web-search.test.ts +156 -0
- package/src/tools/network/web-search.ts +280 -37
- package/src/tools/permission-checker.ts +13 -5
- package/src/tools/subagent/spawn.ts +3 -3
- package/src/tools/terminal/shell.ts +44 -0
- package/src/usage/attribution.ts +3 -2
- package/src/util/pricing.ts +86 -160
- package/src/watcher/__tests__/engine.test.ts +301 -0
- package/src/watcher/constants.ts +7 -0
- package/src/watcher/engine.ts +90 -90
- package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +6 -9
- package/src/workspace/migrations/054-seed-recall-callsite.ts +10 -1
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +28 -4
- package/src/workspace/migrations/069-seed-onboarding-threads.ts +8 -2
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +104 -0
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +93 -0
- package/src/workspace/migrations/074-drop-deprecated-secret-detection-keys.ts +117 -0
- package/src/workspace/migrations/075-memory-v2-bm25-b-default-reembed.ts +61 -0
- package/src/workspace/migrations/076-drop-services-inference-mode.ts +62 -0
- package/src/workspace/migrations/077-seed-memory-router-callsite.ts +89 -0
- package/src/workspace/migrations/078-release-notes-tavily-web-search.ts +66 -0
- package/src/workspace/migrations/079-home-feed-notification-only.ts +197 -0
- package/src/workspace/migrations/080-restrict-vercel-api-token-metadata.ts +182 -0
- package/src/workspace/migrations/081-backfill-bash-allowed-tools-for-injection-credentials.ts +160 -0
- package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +154 -0
- package/src/workspace/migrations/registry.ts +22 -0
- package/src/workspace/migrations/runner.ts +13 -2
- package/src/workspace/migrations/types.ts +13 -3
- package/src/workspace/provider-commit-message-generator.ts +3 -2
- package/src/__tests__/context-search-pkb-source.test.ts +0 -498
- package/src/__tests__/credentials-cli.test.ts +0 -1225
- package/src/__tests__/memory-admin-recall.test.ts +0 -213
- package/src/approvals/__tests__/guardian-feed-event.test.ts +0 -303
- package/src/cli/commands/__tests__/email-download.test.ts +0 -260
- package/src/cli/commands/__tests__/email-list.test.ts +0 -216
- package/src/cli/commands/__tests__/email-register.test.ts +0 -186
- package/src/cli/commands/__tests__/email-send.test.ts +0 -416
- package/src/cli/commands/__tests__/email-status.test.ts +0 -185
- package/src/cli/commands/__tests__/email-unregister.test.ts +0 -168
- package/src/cli/commands/__tests__/routes.test.ts +0 -562
- package/src/cli/commands/__tests__/stt-transcribe.test.ts +0 -454
- package/src/cli/commands/autonomy.ts +0 -365
- package/src/cli/commands/memory.ts +0 -424
- package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -947
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +0 -686
- package/src/cli/commands/oauth/__tests__/mode.test.ts +0 -632
- package/src/cli/commands/oauth/__tests__/ping.test.ts +0 -631
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +0 -573
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +0 -330
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +0 -521
- package/src/cli/commands/oauth/__tests__/status.test.ts +0 -551
- package/src/cli/commands/oauth/__tests__/token.test.ts +0 -420
- package/src/cli/lib/daemon-avatar-client.ts +0 -37
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -87
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +0 -207
- package/src/daemon/__tests__/conversation-feed-event.test.ts +0 -304
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +0 -233
- package/src/home/__tests__/assistant-feed-authoring.test.ts +0 -156
- package/src/home/__tests__/emit-feed-event.test.ts +0 -169
- package/src/home/__tests__/feed-population-integration.test.ts +0 -312
- package/src/home/__tests__/feed-scheduler.test.ts +0 -222
- package/src/home/__tests__/phase5-exit-criteria.test.ts +0 -229
- package/src/home/__tests__/platform-gmail-digest.test.ts +0 -222
- package/src/home/__tests__/rollup-producer.test.ts +0 -507
- package/src/home/assistant-feed-authoring.ts +0 -135
- package/src/home/emit-feed-event.ts +0 -169
- package/src/home/feed-scheduler.ts +0 -281
- package/src/home/platform-gmail-digest.ts +0 -163
- package/src/home/rewrite-command-preview.ts +0 -66
- package/src/home/rewrite-feed-title.ts +0 -58
- package/src/home/rollup-producer.ts +0 -426
- package/src/memory/admin.ts +0 -326
- package/src/memory/context-search/sources/pkb.ts +0 -476
- package/src/memory/graph/compaction.ts +0 -299
- /package/src/cli/{commands → lib}/cache-fs.ts +0 -0
package/src/ipc/cli-client.ts
CHANGED
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
* deterministic fallback for long AF_UNIX paths.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { Socket } from "node:net";
|
|
13
13
|
|
|
14
14
|
import { getLogger } from "../util/logger.js";
|
|
15
|
+
import { IpcFrameReader, writeMessage } from "./ipc-framing.js";
|
|
15
16
|
import { getAssistantSocketPath } from "./socket-path.js";
|
|
16
17
|
|
|
17
18
|
const log = getLogger("cli-ipc-client");
|
|
@@ -20,12 +21,6 @@ const log = getLogger("cli-ipc-client");
|
|
|
20
21
|
// Types (mirror cli-server.ts protocol)
|
|
21
22
|
// ---------------------------------------------------------------------------
|
|
22
23
|
|
|
23
|
-
type IpcRequest = {
|
|
24
|
-
id: string;
|
|
25
|
-
method: string;
|
|
26
|
-
params?: Record<string, unknown>;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
24
|
type IpcResponse = {
|
|
30
25
|
id: string;
|
|
31
26
|
result?: unknown;
|
|
@@ -74,10 +69,15 @@ export interface CliIpcCallResult<T = unknown> {
|
|
|
74
69
|
export async function cliIpcCall<T = unknown>(
|
|
75
70
|
method: string,
|
|
76
71
|
params?: Record<string, unknown>,
|
|
77
|
-
options?: { timeoutMs?: number },
|
|
72
|
+
options?: { timeoutMs?: number; signal?: AbortSignal },
|
|
78
73
|
): Promise<CliIpcCallResult<T>> {
|
|
74
|
+
if (options?.signal?.aborted) {
|
|
75
|
+
throw options.signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
76
|
+
}
|
|
77
|
+
|
|
79
78
|
const socketPath = getAssistantSocketPath();
|
|
80
79
|
const callTimeoutMs = options?.timeoutMs ?? DEFAULT_CALL_TIMEOUT_MS;
|
|
80
|
+
const opts = options; // alias used in the Promise callback below
|
|
81
81
|
|
|
82
82
|
return new Promise<CliIpcCallResult<T>>((resolve) => {
|
|
83
83
|
let settled = false;
|
|
@@ -99,20 +99,65 @@ export async function cliIpcCall<T = unknown>(
|
|
|
99
99
|
);
|
|
100
100
|
finish({
|
|
101
101
|
ok: false,
|
|
102
|
-
error:
|
|
102
|
+
error: `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.`,
|
|
103
103
|
});
|
|
104
104
|
}, CONNECT_TIMEOUT_MS);
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
// Create the socket without connecting first so error/close handlers are
|
|
107
|
+
// registered before initiating the connection. In Bun, socket errors can
|
|
108
|
+
// fire synchronously during connect(), before listeners added afterward.
|
|
109
|
+
const socket = new Socket();
|
|
107
110
|
socket.unref();
|
|
108
111
|
|
|
109
|
-
|
|
112
|
+
socket.on("error", (err) => {
|
|
113
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
114
|
+
log.debug({ err, code, method, socketPath }, "CLI IPC socket error");
|
|
115
|
+
finish({
|
|
116
|
+
ok: false,
|
|
117
|
+
error:
|
|
118
|
+
code === "ENOENT" || code === "ECONNREFUSED"
|
|
119
|
+
? `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.`
|
|
120
|
+
: `Connection error: ${code ?? err.message}`,
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
socket.on("close", (hadError) => {
|
|
125
|
+
if (!settled) {
|
|
126
|
+
finish({
|
|
127
|
+
ok: false,
|
|
128
|
+
// hadError is true when close follows a socket error (e.g. ENOENT).
|
|
129
|
+
error: hadError
|
|
130
|
+
? `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.`
|
|
131
|
+
: "Connection closed before response",
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
110
136
|
const reqId = crypto.randomUUID();
|
|
111
137
|
|
|
138
|
+
opts?.signal?.addEventListener("abort", () => {
|
|
139
|
+
finish({ ok: false, error: "Request aborted" });
|
|
140
|
+
}, { once: true });
|
|
141
|
+
|
|
142
|
+
const reader = new IpcFrameReader(
|
|
143
|
+
(envelope) => {
|
|
144
|
+
if (envelope.id !== reqId) return;
|
|
145
|
+
const msg = envelope as IpcResponse;
|
|
146
|
+
if (msg.error) {
|
|
147
|
+
finish({ ok: false, error: msg.error,
|
|
148
|
+
...(msg.statusCode != null && { statusCode: msg.statusCode }),
|
|
149
|
+
...(msg.errorCode != null && { errorCode: msg.errorCode }),
|
|
150
|
+
...(msg.errorDetails != null && { errorDetails: msg.errorDetails }) });
|
|
151
|
+
} else {
|
|
152
|
+
finish({ ok: true, result: msg.result as T });
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
(err) => finish({ ok: false, error: err.message }),
|
|
156
|
+
);
|
|
157
|
+
|
|
112
158
|
socket.on("connect", () => {
|
|
113
159
|
clearTimeout(connectTimer);
|
|
114
|
-
|
|
115
|
-
socket.write(JSON.stringify(req) + "\n");
|
|
160
|
+
writeMessage(socket, { id: reqId, method, params });
|
|
116
161
|
|
|
117
162
|
callTimer = setTimeout(() => {
|
|
118
163
|
log.debug(
|
|
@@ -123,61 +168,336 @@ export async function cliIpcCall<T = unknown>(
|
|
|
123
168
|
}, callTimeoutMs);
|
|
124
169
|
|
|
125
170
|
socket.on("data", (chunk) => {
|
|
126
|
-
|
|
127
|
-
let newlineIdx: number;
|
|
128
|
-
while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
|
|
129
|
-
const line = buffer.slice(0, newlineIdx).trim();
|
|
130
|
-
buffer = buffer.slice(newlineIdx + 1);
|
|
131
|
-
if (!line) continue;
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
const msg = JSON.parse(line) as IpcResponse;
|
|
135
|
-
if (msg.id === reqId) {
|
|
136
|
-
if (msg.error) {
|
|
137
|
-
finish({
|
|
138
|
-
ok: false,
|
|
139
|
-
error: msg.error,
|
|
140
|
-
...(msg.statusCode !== undefined && {
|
|
141
|
-
statusCode: msg.statusCode,
|
|
142
|
-
}),
|
|
143
|
-
...(msg.errorCode !== undefined && {
|
|
144
|
-
errorCode: msg.errorCode,
|
|
145
|
-
}),
|
|
146
|
-
...(msg.errorDetails !== undefined && {
|
|
147
|
-
errorDetails: msg.errorDetails,
|
|
148
|
-
}),
|
|
149
|
-
});
|
|
150
|
-
} else {
|
|
151
|
-
finish({ ok: true, result: msg.result as T });
|
|
152
|
-
}
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
} catch {
|
|
156
|
-
// Ignore malformed lines
|
|
157
|
-
}
|
|
158
|
-
}
|
|
171
|
+
reader.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
159
172
|
});
|
|
160
173
|
});
|
|
161
174
|
|
|
175
|
+
socket.connect(socketPath);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
// Binary one-shot client
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
|
|
183
|
+
const DEFAULT_BINARY_TIMEOUT_MS = 60_000;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* One-shot IPC call that expects a single binary frame response.
|
|
187
|
+
*
|
|
188
|
+
* Use when the route returns an IpcBinaryResponse (content-length header
|
|
189
|
+
* + one binary data frame). Returns the headers and raw bytes.
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* const r = await cliIpcCallBinary("export_file", { id: "abc" });
|
|
193
|
+
* if (!r.ok) return exitFromIpcResult(r, cmd);
|
|
194
|
+
* fs.writeFileSync("out.bin", Buffer.from(r.bytes));
|
|
195
|
+
*/
|
|
196
|
+
export async function cliIpcCallBinary(
|
|
197
|
+
method: string,
|
|
198
|
+
params?: Record<string, unknown>,
|
|
199
|
+
opts?: { timeoutMs?: number; signal?: AbortSignal },
|
|
200
|
+
): Promise<
|
|
201
|
+
| { ok: true; headers: Record<string, string>; bytes: Uint8Array }
|
|
202
|
+
| { ok: false; error: string; statusCode?: number; errorCode?: string; errorDetails?: unknown }
|
|
203
|
+
> {
|
|
204
|
+
if (opts?.signal?.aborted) {
|
|
205
|
+
throw opts.signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const socketPath = getAssistantSocketPath();
|
|
209
|
+
const callTimeoutMs = opts?.timeoutMs ?? DEFAULT_BINARY_TIMEOUT_MS;
|
|
210
|
+
|
|
211
|
+
return new Promise((resolve) => {
|
|
212
|
+
let settled = false;
|
|
213
|
+
let callTimer: ReturnType<typeof setTimeout> | undefined;
|
|
214
|
+
|
|
215
|
+
const finish = (
|
|
216
|
+
result:
|
|
217
|
+
| { ok: true; headers: Record<string, string>; bytes: Uint8Array }
|
|
218
|
+
| { ok: false; error: string; statusCode?: number; errorCode?: string; errorDetails?: unknown },
|
|
219
|
+
) => {
|
|
220
|
+
if (settled) return;
|
|
221
|
+
settled = true;
|
|
222
|
+
clearTimeout(connectTimer);
|
|
223
|
+
if (callTimer) clearTimeout(callTimer);
|
|
224
|
+
socket.destroy();
|
|
225
|
+
resolve(result);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const connectTimer = setTimeout(() => {
|
|
229
|
+
log.debug({ method, socketPath, timeoutMs: CONNECT_TIMEOUT_MS }, "CLI IPC binary connect timed out");
|
|
230
|
+
finish({ ok: false, error: `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.` });
|
|
231
|
+
}, CONNECT_TIMEOUT_MS);
|
|
232
|
+
|
|
233
|
+
const socket = new Socket();
|
|
234
|
+
socket.unref();
|
|
235
|
+
|
|
162
236
|
socket.on("error", (err) => {
|
|
163
237
|
const code = (err as NodeJS.ErrnoException).code;
|
|
164
|
-
log.debug({ err, code, method, socketPath }, "CLI IPC socket error");
|
|
238
|
+
log.debug({ err, code, method, socketPath }, "CLI IPC binary socket error");
|
|
165
239
|
finish({
|
|
166
240
|
ok: false,
|
|
167
241
|
error:
|
|
168
242
|
code === "ENOENT" || code === "ECONNREFUSED"
|
|
169
|
-
?
|
|
243
|
+
? `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.`
|
|
170
244
|
: `Connection error: ${code ?? err.message}`,
|
|
171
245
|
});
|
|
172
246
|
});
|
|
173
247
|
|
|
174
|
-
socket.on("close", () => {
|
|
248
|
+
socket.on("close", (hadError) => {
|
|
175
249
|
if (!settled) {
|
|
176
250
|
finish({
|
|
177
251
|
ok: false,
|
|
178
|
-
error:
|
|
252
|
+
error: hadError
|
|
253
|
+
? `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.`
|
|
254
|
+
: "Connection closed before response",
|
|
179
255
|
});
|
|
180
256
|
}
|
|
181
257
|
});
|
|
258
|
+
|
|
259
|
+
const reqId = crypto.randomUUID();
|
|
260
|
+
|
|
261
|
+
opts?.signal?.addEventListener("abort", () => {
|
|
262
|
+
finish({ ok: false, error: "Request aborted" });
|
|
263
|
+
}, { once: true });
|
|
264
|
+
|
|
265
|
+
const reader = new IpcFrameReader(
|
|
266
|
+
(envelope, binary) => {
|
|
267
|
+
if (envelope.id !== reqId) return;
|
|
268
|
+
const msg = envelope as IpcResponse;
|
|
269
|
+
if (msg.error) {
|
|
270
|
+
finish({ ok: false, error: msg.error,
|
|
271
|
+
...(msg.statusCode != null && { statusCode: msg.statusCode }),
|
|
272
|
+
...(msg.errorCode != null && { errorCode: msg.errorCode }),
|
|
273
|
+
...(msg.errorDetails != null && { errorDetails: msg.errorDetails }) });
|
|
274
|
+
} else if (binary === undefined) {
|
|
275
|
+
finish({ ok: false, error: "Expected binary frame but received JSON-only response" });
|
|
276
|
+
} else {
|
|
277
|
+
finish({ ok: true, headers: (envelope.headers ?? {}) as Record<string, string>, bytes: binary });
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
(err) => finish({ ok: false, error: err.message }),
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
socket.on("connect", () => {
|
|
284
|
+
clearTimeout(connectTimer);
|
|
285
|
+
writeMessage(socket, { id: reqId, method, params });
|
|
286
|
+
|
|
287
|
+
callTimer = setTimeout(() => {
|
|
288
|
+
log.debug({ method, socketPath, timeoutMs: callTimeoutMs }, "CLI IPC binary call timed out");
|
|
289
|
+
finish({ ok: false, error: "Request timed out" });
|
|
290
|
+
}, callTimeoutMs);
|
|
291
|
+
|
|
292
|
+
socket.on("data", (chunk) => {
|
|
293
|
+
reader.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
socket.connect(socketPath);
|
|
182
298
|
});
|
|
183
299
|
}
|
|
300
|
+
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
// Streaming client
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
|
|
305
|
+
const DEFAULT_FIRST_BYTE_TIMEOUT_MS = 30_000;
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Streaming IPC call. Returns a ReadableStream of binary chunks.
|
|
309
|
+
*
|
|
310
|
+
* The promise resolves once the opening envelope arrives (or fails). The
|
|
311
|
+
* stream body is delivered asynchronously afterward. Call abort() to
|
|
312
|
+
* cancel mid-stream — this sends a $cancel envelope to the server.
|
|
313
|
+
*
|
|
314
|
+
* No total timeout. Only a first-byte timeout (default 30s).
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* const r = await cliIpcCallStream("export_stream", { id: "abc" });
|
|
318
|
+
* if (!r.ok) return exitFromIpcResult(r, cmd);
|
|
319
|
+
* for await (const chunk of r.body) process.stdout.write(chunk);
|
|
320
|
+
*/
|
|
321
|
+
export async function cliIpcCallStream(
|
|
322
|
+
method: string,
|
|
323
|
+
params?: Record<string, unknown>,
|
|
324
|
+
opts?: { firstByteTimeoutMs?: number; signal?: AbortSignal },
|
|
325
|
+
): Promise<
|
|
326
|
+
| { ok: true; headers: Record<string, string>; body: ReadableStream<Uint8Array>; abort: () => void }
|
|
327
|
+
| { ok: false; error: string; statusCode?: number; errorCode?: string; errorDetails?: unknown }
|
|
328
|
+
> {
|
|
329
|
+
if (opts?.signal?.aborted) {
|
|
330
|
+
throw opts.signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const socketPath = getAssistantSocketPath();
|
|
334
|
+
const firstByteTimeoutMs = opts?.firstByteTimeoutMs ?? DEFAULT_FIRST_BYTE_TIMEOUT_MS;
|
|
335
|
+
|
|
336
|
+
return new Promise((resolve) => {
|
|
337
|
+
let settled = false;
|
|
338
|
+
let firstByteTimer: ReturnType<typeof setTimeout> | undefined;
|
|
339
|
+
let streamController: ReadableStreamDefaultController<Uint8Array> | undefined;
|
|
340
|
+
|
|
341
|
+
const finishError = (
|
|
342
|
+
result: { ok: false; error: string; statusCode?: number; errorCode?: string; errorDetails?: unknown },
|
|
343
|
+
) => {
|
|
344
|
+
if (settled) return;
|
|
345
|
+
settled = true;
|
|
346
|
+
clearTimeout(connectTimer);
|
|
347
|
+
clearTimeout(firstByteTimer);
|
|
348
|
+
socket.destroy();
|
|
349
|
+
resolve(result);
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
const abort = () => {
|
|
353
|
+
if (settled && streamController) {
|
|
354
|
+
streamController.error(new DOMException("Aborted", "AbortError"));
|
|
355
|
+
streamController = undefined;
|
|
356
|
+
}
|
|
357
|
+
if (!socket.destroyed) {
|
|
358
|
+
writeMessage(socket, {
|
|
359
|
+
id: crypto.randomUUID(),
|
|
360
|
+
method: "$cancel",
|
|
361
|
+
params: { targetId: reqId },
|
|
362
|
+
});
|
|
363
|
+
// Use end() not destroy() so the $cancel frame flushes before the FIN.
|
|
364
|
+
socket.end();
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
const connectTimer = setTimeout(() => {
|
|
369
|
+
log.debug({ method, socketPath, timeoutMs: CONNECT_TIMEOUT_MS }, "CLI IPC stream connect timed out");
|
|
370
|
+
finishError({ ok: false, error: `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.` });
|
|
371
|
+
}, CONNECT_TIMEOUT_MS);
|
|
372
|
+
|
|
373
|
+
const socket = new Socket();
|
|
374
|
+
socket.unref();
|
|
375
|
+
|
|
376
|
+
socket.on("error", (err) => {
|
|
377
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
378
|
+
log.debug({ err, code, method, socketPath }, "CLI IPC stream socket error");
|
|
379
|
+
if (!settled) {
|
|
380
|
+
finishError({
|
|
381
|
+
ok: false,
|
|
382
|
+
error:
|
|
383
|
+
code === "ENOENT" || code === "ECONNREFUSED"
|
|
384
|
+
? `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.`
|
|
385
|
+
: `Connection error: ${code ?? err.message}`,
|
|
386
|
+
});
|
|
387
|
+
} else {
|
|
388
|
+
streamController?.error(err);
|
|
389
|
+
streamController = undefined;
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
socket.on("close", (hadError) => {
|
|
394
|
+
if (!settled) {
|
|
395
|
+
finishError({
|
|
396
|
+
ok: false,
|
|
397
|
+
error: hadError
|
|
398
|
+
? `Could not connect to the assistant at ${socketPath}.\nRun \`assistant status\` to check, or \`assistant gateway start\` to start it.`
|
|
399
|
+
: "Connection closed before response",
|
|
400
|
+
});
|
|
401
|
+
} else if (streamController) {
|
|
402
|
+
streamController.error(new Error("Connection closed before stream ended"));
|
|
403
|
+
streamController = undefined;
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const reqId = crypto.randomUUID();
|
|
408
|
+
|
|
409
|
+
opts?.signal?.addEventListener("abort", () => { abort(); }, { once: true });
|
|
410
|
+
|
|
411
|
+
const reader = new IpcFrameReader(
|
|
412
|
+
(envelope) => {
|
|
413
|
+
// Non-streaming envelope with error (e.g. method not found, auth failure)
|
|
414
|
+
if (envelope.id !== reqId) return;
|
|
415
|
+
const msg = envelope as IpcResponse;
|
|
416
|
+
finishError({ ok: false, error: msg.error ?? "Unexpected non-streaming response",
|
|
417
|
+
...(msg.statusCode != null && { statusCode: msg.statusCode }),
|
|
418
|
+
...(msg.errorCode != null && { errorCode: msg.errorCode }),
|
|
419
|
+
...(msg.errorDetails != null && { errorDetails: msg.errorDetails }) });
|
|
420
|
+
},
|
|
421
|
+
(err) => finishError({ ok: false, error: err.message }),
|
|
422
|
+
{
|
|
423
|
+
onStreamStart: (envelope) => {
|
|
424
|
+
if (envelope.id !== reqId) return;
|
|
425
|
+
clearTimeout(firstByteTimer);
|
|
426
|
+
const body = new ReadableStream<Uint8Array>({
|
|
427
|
+
start(ctrl) {
|
|
428
|
+
streamController = ctrl;
|
|
429
|
+
},
|
|
430
|
+
cancel() {
|
|
431
|
+
// Consumer cancelled (reader.cancel(), for-await break, pipe abort).
|
|
432
|
+
// Clear the controller reference first so abort() skips the
|
|
433
|
+
// already-closing stream's error() call, then send $cancel.
|
|
434
|
+
streamController = undefined;
|
|
435
|
+
abort();
|
|
436
|
+
},
|
|
437
|
+
});
|
|
438
|
+
settled = true;
|
|
439
|
+
clearTimeout(connectTimer);
|
|
440
|
+
resolve({ ok: true, headers: (envelope.headers ?? {}) as Record<string, string>, body, abort });
|
|
441
|
+
},
|
|
442
|
+
onStreamChunk: (chunk) => {
|
|
443
|
+
streamController?.enqueue(chunk);
|
|
444
|
+
},
|
|
445
|
+
onStreamEnd: () => {
|
|
446
|
+
streamController?.close();
|
|
447
|
+
streamController = undefined;
|
|
448
|
+
socket.destroy();
|
|
449
|
+
},
|
|
450
|
+
},
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
socket.on("connect", () => {
|
|
454
|
+
clearTimeout(connectTimer);
|
|
455
|
+
writeMessage(socket, { id: reqId, method, params });
|
|
456
|
+
|
|
457
|
+
firstByteTimer = setTimeout(() => {
|
|
458
|
+
log.debug({ method, socketPath, timeoutMs: firstByteTimeoutMs }, "CLI IPC stream first-byte timeout");
|
|
459
|
+
finishError({ ok: false, error: "Stream timed out waiting for first byte" });
|
|
460
|
+
}, firstByteTimeoutMs);
|
|
461
|
+
|
|
462
|
+
socket.on("data", (chunk) => {
|
|
463
|
+
reader.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
socket.connect(socketPath);
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// ---------------------------------------------------------------------------
|
|
472
|
+
// Exit helper
|
|
473
|
+
// ---------------------------------------------------------------------------
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Map an IPC error result to a process exit code and terminate.
|
|
477
|
+
*
|
|
478
|
+
* Exit code matrix (DESIGN.md §3.6):
|
|
479
|
+
* 0 — success (not reached via this helper; call process.exit(0) directly)
|
|
480
|
+
* 1 — generic CLI error (fallback for unexpected status codes)
|
|
481
|
+
* 2 — daemon returned 4xx (bad params, not found, unauthorized)
|
|
482
|
+
* 3 — daemon returned 5xx (server-side error)
|
|
483
|
+
* 10 — IPC transport error (can't connect, timeout, closed before response)
|
|
484
|
+
*
|
|
485
|
+
* @example
|
|
486
|
+
* const r = await cliIpcCall<FooResponse>("foo", params);
|
|
487
|
+
* if (!r.ok) return exitFromIpcResult(r, cmd);
|
|
488
|
+
*/
|
|
489
|
+
export function exitFromIpcResult(
|
|
490
|
+
r: { ok: boolean; error?: string; statusCode?: number },
|
|
491
|
+
_cmd?: unknown,
|
|
492
|
+
): never {
|
|
493
|
+
process.stderr.write((r.error ?? "Unknown error") + "\n");
|
|
494
|
+
if (r.statusCode === undefined) {
|
|
495
|
+
process.exit(10);
|
|
496
|
+
} else if (r.statusCode >= 500) {
|
|
497
|
+
process.exit(3);
|
|
498
|
+
} else if (r.statusCode >= 400) {
|
|
499
|
+
process.exit(2);
|
|
500
|
+
} else {
|
|
501
|
+
process.exit(1);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ⚠️ TEMPORARY HACK — DO NOT EXTEND ⚠️
|
|
3
|
+
*
|
|
4
|
+
* IPC route that lets the gateway execute multiple write statements against
|
|
5
|
+
* the assistant's SQLite database inside a single atomic transaction.
|
|
6
|
+
*
|
|
7
|
+
* Companion to db-proxy.ts. Exists because some gateway-orchestrated writes
|
|
8
|
+
* (e.g. invite redemption: upsert contact channel + record invite use) must
|
|
9
|
+
* be all-or-nothing. With the contacts/guardian/invite tables still living
|
|
10
|
+
* in the assistant DB, the gateway needs a way to commit several writes
|
|
11
|
+
* atomically there.
|
|
12
|
+
*
|
|
13
|
+
* Each step is a write (INSERT/UPDATE/DELETE). All steps run inside a
|
|
14
|
+
* BEGIN IMMEDIATE transaction. If any step throws — including a step whose
|
|
15
|
+
* `requireChanges` constraint is unmet — the entire transaction rolls back.
|
|
16
|
+
*
|
|
17
|
+
* Read-modify-write across steps is not supported (the IPC is one-shot;
|
|
18
|
+
* later steps cannot react to earlier step results except via SQL conditions
|
|
19
|
+
* embedded in the WHERE clause and the optional `requireChanges` guard).
|
|
20
|
+
*
|
|
21
|
+
* This route is intentionally NOT in the shared ROUTES array — it is a
|
|
22
|
+
* private implementation detail between the gateway and assistant IPC
|
|
23
|
+
* servers and must not be discoverable by clients or the OpenAPI spec.
|
|
24
|
+
*
|
|
25
|
+
* Remove once contacts/guardian/invite logic is fully migrated to the
|
|
26
|
+
* gateway's own database.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { getSqlite } from "../../memory/db-connection.js";
|
|
30
|
+
import { RouteError } from "../../runtime/routes/errors.js";
|
|
31
|
+
import { getLogger } from "../../util/logger.js";
|
|
32
|
+
|
|
33
|
+
const log = getLogger("db-proxy-transaction");
|
|
34
|
+
|
|
35
|
+
/** Column value types that SQLite can return. */
|
|
36
|
+
type SqliteValue = string | number | null | Uint8Array;
|
|
37
|
+
|
|
38
|
+
export interface DbProxyTransactionStep {
|
|
39
|
+
/** The SQL write statement to execute. */
|
|
40
|
+
sql: string;
|
|
41
|
+
/** Positional bind parameters. */
|
|
42
|
+
bind?: SqliteValue[];
|
|
43
|
+
/**
|
|
44
|
+
* If set, abort the transaction (rollback) when this step's row-change
|
|
45
|
+
* count is below this threshold. Used for stale-write detection — e.g.
|
|
46
|
+
* "increment use_count only if status = 'active' AND use_count < max_uses",
|
|
47
|
+
* with `requireChanges: 1` to abort when no rows match.
|
|
48
|
+
*/
|
|
49
|
+
requireChanges?: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface DbProxyTransactionParams {
|
|
53
|
+
steps: DbProxyTransactionStep[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type DbProxyTransactionResult =
|
|
57
|
+
| {
|
|
58
|
+
ok: true;
|
|
59
|
+
results: Array<{ changes: number; lastInsertRowid: number }>;
|
|
60
|
+
}
|
|
61
|
+
| {
|
|
62
|
+
ok: false;
|
|
63
|
+
reason: "require_changes_failed";
|
|
64
|
+
failedStep: number;
|
|
65
|
+
actualChanges: number;
|
|
66
|
+
requiredChanges: number;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export function handleDbProxyTransaction(
|
|
70
|
+
params: DbProxyTransactionParams,
|
|
71
|
+
): DbProxyTransactionResult {
|
|
72
|
+
const db = getSqlite();
|
|
73
|
+
|
|
74
|
+
if (!Array.isArray(params.steps) || params.steps.length === 0) {
|
|
75
|
+
throw new RouteError(
|
|
76
|
+
"db_proxy_transaction requires at least one step",
|
|
77
|
+
"INVALID_PARAMS",
|
|
78
|
+
400,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Sentinel used to abort the transaction without leaking through as a generic
|
|
83
|
+
// SQL error. Better-sqlite3 rolls back when the inner function throws.
|
|
84
|
+
class RequireChangesFailure extends Error {
|
|
85
|
+
constructor(
|
|
86
|
+
public failedStep: number,
|
|
87
|
+
public actualChanges: number,
|
|
88
|
+
public requiredChanges: number,
|
|
89
|
+
) {
|
|
90
|
+
super(
|
|
91
|
+
`Step ${failedStep} affected ${actualChanges} rows, requires ${requiredChanges}`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const results: Array<{ changes: number; lastInsertRowid: number }> = [];
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
db.transaction(() => {
|
|
100
|
+
for (let i = 0; i < params.steps.length; i++) {
|
|
101
|
+
const step = params.steps[i];
|
|
102
|
+
const stmt = db.prepare(step.sql);
|
|
103
|
+
const result = step.bind ? stmt.run(...step.bind) : stmt.run();
|
|
104
|
+
const changes = result.changes;
|
|
105
|
+
results.push({
|
|
106
|
+
changes,
|
|
107
|
+
lastInsertRowid: Number(result.lastInsertRowid),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
step.requireChanges !== undefined &&
|
|
112
|
+
changes < step.requireChanges
|
|
113
|
+
) {
|
|
114
|
+
throw new RequireChangesFailure(i, changes, step.requireChanges);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}).immediate();
|
|
118
|
+
} catch (err) {
|
|
119
|
+
if (err instanceof RequireChangesFailure) {
|
|
120
|
+
log.debug(
|
|
121
|
+
{
|
|
122
|
+
failedStep: err.failedStep,
|
|
123
|
+
actualChanges: err.actualChanges,
|
|
124
|
+
requiredChanges: err.requiredChanges,
|
|
125
|
+
},
|
|
126
|
+
"db-proxy-transaction aborted by requireChanges guard",
|
|
127
|
+
);
|
|
128
|
+
return {
|
|
129
|
+
ok: false,
|
|
130
|
+
reason: "require_changes_failed",
|
|
131
|
+
failedStep: err.failedStep,
|
|
132
|
+
actualChanges: err.actualChanges,
|
|
133
|
+
requiredChanges: err.requiredChanges,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
// Wrap raw SQL/runtime errors in RouteError so the IPC envelope carries
|
|
137
|
+
// a statusCode + errorCode. Without this, the gateway-side strict caller
|
|
138
|
+
// sees a structureless `msg.error` and misclassifies it as a transport
|
|
139
|
+
// failure ("assistant may not be ready"), masking the real SQL error
|
|
140
|
+
// and breaking debuggability + retry decisions.
|
|
141
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
142
|
+
log.warn({ err }, "db-proxy-transaction execution failed");
|
|
143
|
+
throw new RouteError(message, "DB_PROXY_TRANSACTION_FAILED", 500);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
log.debug(
|
|
147
|
+
{ stepCount: params.steps.length },
|
|
148
|
+
"db-proxy-transaction committed",
|
|
149
|
+
);
|
|
150
|
+
return { ok: true, results };
|
|
151
|
+
}
|
|
@@ -192,6 +192,66 @@ describe("host.events.publish", () => {
|
|
|
192
192
|
|
|
193
193
|
client.close();
|
|
194
194
|
});
|
|
195
|
+
|
|
196
|
+
test.each([
|
|
197
|
+
"host_bash_request",
|
|
198
|
+
"host_bash_cancel",
|
|
199
|
+
"host_file_request",
|
|
200
|
+
"host_file_cancel",
|
|
201
|
+
"host_browser_request",
|
|
202
|
+
"host_browser_cancel",
|
|
203
|
+
"host_cu_request",
|
|
204
|
+
"host_transfer_request",
|
|
205
|
+
"confirmation_request",
|
|
206
|
+
"secret_request",
|
|
207
|
+
])("rejects blocked event type: %s", async (blockedType) => {
|
|
208
|
+
const client = await openClient();
|
|
209
|
+
const event = {
|
|
210
|
+
id: "evt-blocked",
|
|
211
|
+
emittedAt: new Date().toISOString(),
|
|
212
|
+
message: { type: blockedType },
|
|
213
|
+
};
|
|
214
|
+
client.send({
|
|
215
|
+
id: "req-blocked",
|
|
216
|
+
method: "host.events.publish",
|
|
217
|
+
params: { event },
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const frame = await client.nextFrame();
|
|
221
|
+
expect("error" in frame && frame.error).toContain("cannot publish");
|
|
222
|
+
|
|
223
|
+
client.close();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("allows non-blocked event types through", async () => {
|
|
227
|
+
const received: AssistantEvent[] = [];
|
|
228
|
+
const subscription = assistantEventHub.subscribe({
|
|
229
|
+
type: "process",
|
|
230
|
+
callback: (evt) => { received.push(evt); },
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
const client = await openClient();
|
|
235
|
+
const event = {
|
|
236
|
+
id: "evt-ok",
|
|
237
|
+
emittedAt: new Date().toISOString(),
|
|
238
|
+
message: { type: "skill_custom_event", data: "hello" },
|
|
239
|
+
};
|
|
240
|
+
client.send({
|
|
241
|
+
id: "req-ok",
|
|
242
|
+
method: "host.events.publish",
|
|
243
|
+
params: { event },
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const frame = await client.nextFrame();
|
|
247
|
+
expect("result" in frame && frame.result).toEqual({ published: true });
|
|
248
|
+
expect(received).toHaveLength(1);
|
|
249
|
+
|
|
250
|
+
client.close();
|
|
251
|
+
} finally {
|
|
252
|
+
subscription.dispose();
|
|
253
|
+
}
|
|
254
|
+
});
|
|
195
255
|
});
|
|
196
256
|
|
|
197
257
|
describe("host.events.buildEvent", () => {
|