@vellumai/assistant 0.7.1 → 0.7.2
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/ARCHITECTURE.md +32 -49
- package/Dockerfile +1 -0
- package/README.md +1 -2
- package/__tests__/permissions/gateway-threshold-reader.test.ts +9 -3
- package/bun.lock +26 -26
- package/docs/architecture/security.md +20 -0
- package/docs/plugins.md +7 -9
- package/knip.json +1 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +39 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +11 -0
- package/node_modules/@vellumai/service-contracts/package.json +2 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +4 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +107 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +5 -1
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +24 -0
- package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +84 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +9 -0
- package/node_modules/@vellumai/twilio-client/bun.lock +24 -0
- package/node_modules/@vellumai/twilio-client/package.json +18 -0
- package/node_modules/@vellumai/twilio-client/src/__tests__/twilio-client.test.ts +128 -0
- package/node_modules/@vellumai/twilio-client/src/index.ts +179 -0
- package/node_modules/@vellumai/twilio-client/tsconfig.json +20 -0
- package/openapi.yaml +565 -12
- package/package.json +6 -3
- package/src/__tests__/app-builder-tool-scripts.test.ts +3 -3
- package/src/__tests__/app-bundler.test.ts +170 -1
- package/src/__tests__/app-control-flow.test.ts +374 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +98 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +621 -0
- package/src/__tests__/app-executors.test.ts +30 -43
- package/src/__tests__/approval-routes-http.test.ts +23 -6
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +146 -0
- package/src/__tests__/assistant-event-hub-targeted.test.ts +257 -0
- package/src/__tests__/assistant-event-hub.test.ts +109 -2
- package/src/__tests__/assistant-event.test.ts +10 -0
- package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -2
- package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -7
- package/src/__tests__/background-shell-host-bash.test.ts +14 -15
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +44 -0
- package/src/__tests__/btw-routes.test.ts +13 -4
- package/src/__tests__/call-controller.test.ts +49 -1
- package/src/__tests__/call-domain.test.ts +0 -2
- package/src/__tests__/call-routes-http.test.ts +0 -2
- package/src/__tests__/channel-readiness-service.test.ts +59 -1
- package/src/__tests__/checker.test.ts +3 -4
- package/src/__tests__/config-loader-backfill.test.ts +90 -155
- package/src/__tests__/config-loader-platform-defaults.test.ts +196 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -1
- package/src/__tests__/config-set-platform-guard.test.ts +48 -4
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +2 -2
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/conversation-app-control-instantiation.test.ts +392 -0
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +237 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-lifecycle.test.ts +36 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +283 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +6 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +120 -72
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/conversation-slash-commands.test.ts +0 -4
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +317 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +5 -12
- package/src/__tests__/credential-execution-managed-contract.test.ts +3 -131
- package/src/__tests__/credentials-cli.test.ts +5 -12
- package/src/__tests__/cu-unified-flow.test.ts +185 -23
- package/src/__tests__/daemon-credential-client.test.ts +101 -19
- package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -2
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -2
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -1
- package/src/__tests__/heartbeat-service.test.ts +718 -1
- package/src/__tests__/helpers/call-route-handler.ts +7 -1
- package/src/__tests__/host-app-control-proxy.test.ts +602 -0
- package/src/__tests__/host-app-control-routes.test.ts +263 -0
- package/src/__tests__/host-bash-proxy.test.ts +246 -47
- package/src/__tests__/host-bash-routes.test.ts +294 -0
- package/src/__tests__/host-browser-proxy.test.ts +24 -22
- package/src/__tests__/host-browser-routes.test.ts +39 -13
- package/src/__tests__/host-cu-proxy.test.ts +41 -52
- package/src/__tests__/host-cu-routes-targeted.test.ts +300 -0
- package/src/__tests__/host-file-edit-tool.test.ts +47 -1
- package/src/__tests__/host-file-proxy-targeted.test.ts +339 -0
- package/src/__tests__/host-file-proxy.test.ts +37 -43
- package/src/__tests__/host-file-read-tool.test.ts +17 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +262 -0
- package/src/__tests__/host-file-write-tool.test.ts +42 -1
- package/src/__tests__/host-proxy-base.test.ts +312 -0
- package/src/__tests__/host-shell-tool.test.ts +22 -4
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +583 -0
- package/src/__tests__/host-transfer-proxy.test.ts +121 -22
- package/src/__tests__/host-transfer-routes-targeted.test.ts +447 -0
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/identity-intro-cache.test.ts +29 -0
- package/src/__tests__/identity-routes.test.ts +103 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +26 -3
- package/src/__tests__/inline-command-runner.test.ts +0 -1
- package/src/__tests__/inline-skill-load-permissions.test.ts +5 -11
- package/src/__tests__/integration-status.test.ts +85 -5
- package/src/__tests__/intent-routing.test.ts +0 -1
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +95 -5
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +17 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
- package/src/__tests__/mcp-auth-routes.test.ts +197 -0
- package/src/__tests__/mcp-cli.test.ts +338 -2
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
- package/src/__tests__/migration-import-commit-http.test.ts +108 -2
- package/src/__tests__/mock-gateway-ipc.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +0 -2
- package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
- package/src/__tests__/prechat-onboarding-contract.test.ts +3 -1
- package/src/__tests__/process-message-background-slack.test.ts +2 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
- package/src/__tests__/public-ingress-urls.test.ts +97 -0
- package/src/__tests__/require-fresh-approval.test.ts +0 -1
- package/src/__tests__/retry-backoff.test.ts +87 -0
- package/src/__tests__/runtime-events-sse.test.ts +10 -6
- package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
- package/src/__tests__/schedule-retry.test.ts +715 -0
- package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
- package/src/__tests__/skill-feature-flags.test.ts +43 -41
- package/src/__tests__/skill-load-feature-flag.test.ts +13 -14
- package/src/__tests__/skill-load-inline-command.test.ts +0 -51
- package/src/__tests__/skill-load-inline-includes.test.ts +0 -43
- package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
- package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
- package/src/__tests__/slack-channel-config.test.ts +9 -14
- package/src/__tests__/system-prompt-ask-mode.test.ts +0 -1
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/telegram-config.test.ts +0 -1
- package/src/__tests__/test-preload.ts +8 -0
- package/src/__tests__/tool-approval-handler.test.ts +3 -4
- package/src/__tests__/tool-audit-listener.test.ts +48 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -1
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/twilio-config.test.ts +3 -16
- package/src/__tests__/twilio-routes.test.ts +3 -5
- package/src/__tests__/twilio-validation.test.ts +93 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -4
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -4
- package/src/__tests__/voice-ingress-preflight.test.ts +19 -0
- package/src/__tests__/workspace-migration-006-services-config.test.ts +3 -2
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +1 -5
- package/src/__tests__/workspace-migration-down-functions.test.ts +8 -8
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
- package/src/backup/__tests__/paths.test.ts +0 -22
- package/src/backup/__tests__/restore.test.ts +51 -151
- package/src/backup/paths.ts +2 -18
- package/src/backup/restore.ts +107 -231
- package/src/bundler/app-bundler.ts +51 -3
- package/src/calls/relay-server.ts +4 -44
- package/src/calls/twilio-config.ts +2 -17
- package/src/calls/twilio-rest.ts +33 -105
- package/src/calls/twilio-routes.ts +11 -12
- package/src/channels/types.ts +8 -7
- package/src/cli/commands/__tests__/backup.test.ts +6 -277
- package/src/cli/commands/__tests__/gateway.test.ts +288 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +4 -0
- package/src/cli/commands/__tests__/webhooks.test.ts +0 -1
- package/src/cli/commands/backup.ts +6 -331
- package/src/cli/commands/clients.ts +36 -37
- package/src/cli/commands/contacts.ts +73 -0
- package/src/cli/commands/conversations.ts +2 -5
- package/src/cli/commands/credentials.ts +15 -7
- package/src/cli/commands/domain.ts +66 -15
- package/src/cli/commands/gateway.ts +183 -0
- package/src/cli/commands/keys.ts +9 -6
- package/src/cli/commands/mcp.ts +116 -156
- package/src/cli/commands/memory-v2.ts +296 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +0 -2
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -2
- package/src/cli/commands/platform/__tests__/status.test.ts +13 -15
- package/src/cli/commands/platform/disconnect.ts +5 -4
- package/src/cli/commands/platform/index.ts +0 -18
- package/src/cli/lib/daemon-credential-client.ts +110 -28
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +67 -10
- package/src/config/bundled-skills/acp/SKILL.md +6 -0
- package/src/config/bundled-skills/acp/TOOLS.json +1 -22
- package/src/config/bundled-skills/app-builder/SKILL.md +14 -109
- package/src/config/bundled-skills/app-builder/TOOLS.json +1 -28
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -10
- package/src/config/bundled-skills/app-control/SKILL.md +75 -0
- package/src/config/bundled-skills/app-control/TOOLS.json +299 -0
- package/src/config/bundled-skills/app-control/tools/app-control-click.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-combo.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-drag.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-observe.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-press.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-sequence.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-start.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-stop.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-type.ts +12 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +6 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +67 -43
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -16
- package/src/config/bundled-skills/document/TOOLS.json +0 -8
- package/src/config/bundled-skills/followups/TOOLS.json +0 -12
- package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
- package/src/config/bundled-skills/image-studio/TOOLS.json +0 -4
- package/src/config/bundled-skills/media-processing/TOOLS.json +0 -24
- package/src/config/bundled-skills/messaging/TOOLS.json +0 -40
- package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +19 -4
- package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
- package/src/config/bundled-skills/schedule/TOOLS.json +14 -14
- package/src/config/bundled-skills/sequences/TOOLS.json +0 -36
- package/src/config/bundled-skills/settings/SKILL.md +4 -0
- package/src/config/bundled-skills/settings/TOOLS.json +0 -12
- package/src/config/bundled-skills/skill-management/SKILL.md +6 -0
- package/src/config/bundled-skills/skill-management/TOOLS.json +0 -8
- package/src/config/bundled-skills/subagent/SKILL.md +6 -2
- package/src/config/bundled-skills/subagent/TOOLS.json +0 -20
- package/src/config/bundled-skills/transcribe/SKILL.md +4 -0
- package/src/config/bundled-skills/transcribe/TOOLS.json +0 -4
- package/src/config/bundled-tool-registry.ts +21 -0
- package/src/config/env-registry.ts +0 -2
- package/src/config/env.ts +19 -12
- package/src/config/feature-flag-registry.json +21 -133
- package/src/config/loader.ts +73 -99
- package/src/config/sanitize-for-transfer.ts +2 -0
- package/src/config/schemas/__tests__/memory-lifecycle.test.ts +80 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -4
- package/src/config/schemas/calls.ts +0 -9
- package/src/config/schemas/heartbeat.ts +63 -0
- package/src/config/schemas/ingress.ts +10 -6
- package/src/config/schemas/llm.ts +5 -10
- package/src/config/schemas/memory-lifecycle.ts +77 -24
- package/src/config/schemas/memory-v2.ts +48 -4
- package/src/config/schemas/platform.ts +6 -0
- package/src/config/schemas/services.ts +1 -15
- package/src/config/schemas/skills.ts +0 -6
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/contacts/contact-store.ts +0 -30
- package/src/contacts/contacts-write.ts +0 -27
- package/src/context/window-manager.ts +1 -2
- package/src/credential-execution/feature-gates.ts +10 -10
- package/src/credential-execution/process-manager.ts +12 -41
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +126 -5
- package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
- package/src/daemon/config-watcher.ts +4 -3
- package/src/daemon/conversation-agent-loop-handlers.ts +21 -3
- package/src/daemon/conversation-agent-loop.ts +32 -28
- package/src/daemon/conversation-lifecycle.ts +8 -1
- package/src/daemon/conversation-process.ts +16 -11
- package/src/daemon/conversation-runtime-assembly.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +125 -4
- package/src/daemon/conversation-tool-setup.ts +16 -55
- package/src/daemon/conversation.ts +21 -2
- package/src/daemon/doordash-steps.ts +1 -1
- package/src/daemon/handlers/shared.ts +4 -1
- package/src/daemon/host-app-control-proxy.ts +293 -0
- package/src/daemon/host-bash-proxy.ts +84 -74
- package/src/daemon/host-browser-proxy.ts +67 -82
- package/src/daemon/host-cu-proxy.ts +81 -86
- package/src/daemon/host-file-proxy.ts +93 -69
- package/src/daemon/host-proxy-base.ts +294 -0
- package/src/daemon/host-proxy-preactivation.ts +82 -0
- package/src/daemon/host-transfer-proxy.ts +247 -129
- package/src/daemon/lifecycle.ts +115 -117
- package/src/daemon/message-protocol.ts +3 -8
- package/src/daemon/message-types/contacts.ts +23 -1
- package/src/daemon/message-types/conversations.ts +11 -8
- package/src/daemon/message-types/host-app-control.ts +150 -0
- package/src/daemon/message-types/host-bash.ts +4 -0
- package/src/daemon/message-types/host-cu.ts +2 -0
- package/src/daemon/message-types/host-file.ts +4 -0
- package/src/daemon/message-types/host-transfer.ts +3 -0
- package/src/daemon/message-types/schedules.ts +8 -3
- package/src/daemon/message-types/skills.ts +2 -2
- package/src/daemon/process-message.ts +18 -1
- package/src/daemon/shutdown-handlers.ts +0 -3
- package/src/daemon/tool-setup-types.ts +51 -0
- package/src/daemon/tool-side-effects.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -1
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +15 -7
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +216 -0
- package/src/heartbeat/heartbeat-run-store.ts +236 -0
- package/src/heartbeat/heartbeat-service.ts +280 -49
- package/src/home/__tests__/post-connect-feed.test.ts +99 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +11 -9
- package/src/home/__tests__/suggested-prompts.test.ts +89 -0
- package/src/home/post-connect-feed.ts +68 -0
- package/src/home/relationship-state-writer.ts +17 -92
- package/src/home/suggested-prompts.ts +46 -10
- package/src/inbound/public-ingress-urls.ts +32 -34
- package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
- package/src/ipc/assistant-server.ts +14 -1
- package/src/ipc/cli-client.ts +32 -1
- package/src/live-voice/live-voice-metrics.ts +10 -10
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +304 -0
- package/src/mcp/mcp-auth-orchestrator.ts +213 -0
- package/src/mcp/mcp-auth-state.ts +133 -0
- package/src/mcp/mcp-oauth-provider.ts +19 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
- package/src/memory/__tests__/qdrant-client-sentinel.test.ts +49 -0
- package/src/memory/__tests__/sparse-tokenize.test.ts +66 -0
- package/src/memory/anisotropy.test.ts +247 -0
- package/src/memory/anisotropy.ts +443 -0
- package/src/memory/auto-analysis-constants.ts +17 -0
- package/src/memory/auto-analysis-guard.ts +5 -15
- package/src/memory/canonical-guardian-store.ts +7 -7
- package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +122 -0
- package/src/memory/context-search/agent-protocol.ts +6 -6
- package/src/memory/context-search/agent-runner.ts +32 -7
- package/src/memory/context-search/sources/memory-v2.ts +17 -5
- package/src/memory/conversation-crud.ts +1 -1
- package/src/memory/conversation-key-store.ts +2 -15
- package/src/memory/db-init.ts +4 -0
- package/src/memory/embedding-backend.ts +9 -21
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +49 -4
- package/src/memory/graph/conversation-graph-memory.ts +1 -24
- package/src/memory/graph/graph-search.ts +8 -0
- package/src/memory/graph/retriever.ts +28 -0
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +8 -2
- package/src/memory/jobs/embed-concept-page.ts +28 -2
- package/src/memory/jobs/embed-pkb-file.test.ts +2 -2
- package/src/memory/jobs-store.ts +66 -22
- package/src/memory/jobs-worker.ts +112 -63
- package/src/memory/memory-v2-activation-log-store.ts +1 -1
- package/src/memory/migrations/237-heartbeat-runs.ts +45 -0
- package/src/memory/migrations/238-schedule-retry-policy.ts +20 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/pkb/pkb-search.ts +7 -0
- package/src/memory/qdrant-client.ts +50 -20
- package/src/memory/schema/infrastructure.ts +15 -0
- package/src/memory/search/semantic.ts +7 -0
- package/src/memory/sparse-tokenize.ts +49 -0
- package/src/memory/v2/__tests__/activation.test.ts +77 -95
- package/src/memory/v2/__tests__/injection.test.ts +43 -21
- package/src/memory/v2/__tests__/sim.test.ts +166 -6
- package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
- package/src/memory/v2/__tests__/static-context.test.ts +0 -1
- package/src/memory/v2/activation.ts +69 -88
- package/src/memory/v2/consolidation-job.ts +3 -5
- package/src/memory/v2/constants.ts +7 -0
- package/src/memory/v2/injection.ts +86 -53
- package/src/memory/v2/prompts/consolidation.ts +312 -91
- package/src/memory/v2/qdrant.ts +99 -1
- package/src/memory/v2/sim.ts +126 -16
- package/src/memory/v2/skill-qdrant.ts +12 -3
- package/src/memory/v2/skill-store.ts +16 -1
- package/src/memory/v2/sparse-bm25.ts +245 -0
- package/src/memory/v2/static-context.ts +6 -5
- package/src/messaging/providers/gmail/types.ts +0 -49
- package/src/messaging/providers/slack/adapter.ts +1 -31
- package/src/messaging/providers/slack/types.ts +0 -32
- package/src/notifications/README.md +10 -10
- package/src/notifications/broadcaster.ts +1 -1
- package/src/notifications/guardian-question-mode.ts +5 -5
- package/src/oauth/connect-orchestrator.ts +4 -0
- package/src/oauth/credential-token-resolver.ts +1 -3
- package/src/oauth/manual-token-connection.ts +0 -4
- package/src/outbound-proxy/index.ts +1 -37
- package/src/outbound-proxy/logging.ts +1 -1
- package/src/outbound-proxy/policy.ts +6 -5
- package/src/outbound-proxy/router.ts +2 -1
- package/src/permissions/approval-policy.test.ts +6 -275
- package/src/permissions/approval-policy.ts +0 -51
- package/src/permissions/checker.test.ts +0 -1
- package/src/permissions/checker.ts +3 -17
- package/src/permissions/gateway-threshold-reader.ts +2 -0
- package/src/permissions/prompter.ts +34 -1
- package/src/permissions/secret-prompter.ts +6 -2
- package/src/prompts/bootstrap-cleanup.ts +27 -0
- package/src/prompts/system-prompt.ts +3 -18
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/providers/speech-to-text/provider-catalog.ts +7 -8
- package/src/runtime/assistant-event-hub.ts +118 -96
- package/src/runtime/assistant-event.ts +1 -0
- package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
- package/src/runtime/auth/middleware.ts +0 -96
- package/src/runtime/auth/route-policy.ts +19 -0
- package/src/runtime/btw-sidechain.ts +2 -3
- package/src/runtime/channel-invite-transport.ts +2 -48
- package/src/runtime/channel-invite-transports/email.ts +1 -1
- package/src/runtime/channel-invite-transports/slack.ts +1 -1
- package/src/runtime/channel-invite-transports/telegram.ts +1 -1
- package/src/runtime/channel-invite-transports/voice.ts +1 -1
- package/src/runtime/channel-invite-transports/whatsapp.ts +1 -1
- package/src/runtime/channel-invite-types.ts +54 -0
- package/src/runtime/channel-readiness-service.ts +32 -13
- package/src/runtime/http-server.ts +3 -329
- package/src/runtime/http-types.ts +0 -5
- package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +413 -0
- package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +260 -0
- package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +189 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +153 -1
- package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +451 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +515 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +437 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +319 -0
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +51 -1
- package/src/runtime/migrations/migration-transport.ts +7 -7
- package/src/runtime/migrations/vbundle-builder.ts +327 -60
- package/src/runtime/migrations/vbundle-import-analyzer.ts +4 -4
- package/src/runtime/migrations/vbundle-import-policy.ts +172 -0
- package/src/runtime/migrations/vbundle-importer.ts +245 -68
- package/src/runtime/migrations/vbundle-streaming-importer.ts +326 -35
- package/src/runtime/migrations/vbundle-streaming-validator.ts +157 -4
- package/src/runtime/migrations/vbundle-tar-stream.ts +15 -6
- package/src/runtime/migrations/vbundle-validator.ts +114 -0
- package/src/runtime/pending-interactions.ts +35 -9
- package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
- package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +242 -0
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +112 -0
- package/src/runtime/routes/approval-interception-types.ts +13 -0
- package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +1 -1
- package/src/runtime/routes/backup-routes.ts +15 -38
- package/src/runtime/routes/btw-routes.ts +14 -37
- package/src/runtime/routes/client-routes.ts +1 -0
- package/src/runtime/routes/contact-prompt-routes.ts +183 -0
- package/src/runtime/routes/conversation-query-routes.ts +36 -1
- package/src/runtime/routes/conversation-routes.ts +30 -13
- package/src/runtime/routes/document-pdf-renderer.ts +165 -0
- package/src/runtime/routes/documents-routes.ts +30 -0
- package/src/runtime/routes/errors.ts +19 -4
- package/src/runtime/routes/events-routes.ts +12 -6
- package/src/runtime/routes/gateway-log-routes.ts +79 -0
- package/src/runtime/routes/guardian-approval-interception.ts +2 -8
- package/src/runtime/routes/heartbeat-routes.ts +103 -38
- package/src/runtime/routes/host-app-control-routes.ts +134 -0
- package/src/runtime/routes/host-bash-routes.ts +36 -6
- package/src/runtime/routes/host-browser-routes.ts +108 -13
- package/src/runtime/routes/host-cu-routes.ts +44 -14
- package/src/runtime/routes/host-file-routes.ts +33 -10
- package/src/runtime/routes/host-transfer-routes.ts +64 -24
- package/src/runtime/routes/http-adapter.ts +1 -0
- package/src/runtime/routes/identity-intro-cache.ts +30 -0
- package/src/runtime/routes/identity-routes.ts +15 -43
- package/src/runtime/routes/inbound-message-handler.ts +1 -9
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -7
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +0 -8
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -20
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +5 -13
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/mcp-auth-routes.ts +132 -0
- package/src/runtime/routes/memory-item-routes.ts +10 -12
- package/src/runtime/routes/memory-v2-routes.ts +441 -1
- package/src/runtime/routes/migration-routes.ts +96 -0
- package/src/runtime/routes/schedule-routes.ts +7 -0
- package/src/runtime/verification-templates.ts +4 -7
- package/src/schedule/integration-status.ts +66 -2
- package/src/schedule/recurrence-engine.ts +4 -1
- package/src/schedule/retry-backoff.ts +18 -0
- package/src/schedule/retry-policy.ts +82 -0
- package/src/schedule/schedule-recovery.ts +64 -0
- package/src/schedule/schedule-store.ts +106 -2
- package/src/schedule/scheduler-types.ts +25 -0
- package/src/schedule/scheduler.ts +63 -38
- package/src/security/oauth-callback-registry.ts +8 -0
- package/src/sequence/analytics.ts +5 -5
- package/src/sequence/engine.ts +1 -1
- package/src/skills/catalog-files.ts +2 -8
- package/src/skills/include-graph.ts +5 -5
- package/src/skills/remote-skill-policy.ts +5 -5
- package/src/skills/skill-file-provider.ts +1 -1
- package/src/skills/skill-file-types.ts +13 -0
- package/src/skills/skillssh-audit-types.ts +28 -0
- package/src/skills/skillssh-registry.ts +8 -21
- package/src/telemetry/types.ts +2 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +21 -0
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/app-control/skill-proxy-bridge.ts +28 -0
- package/src/tools/apps/executors.ts +56 -69
- package/src/tools/browser/__tests__/browser-status.test.ts +21 -18
- package/src/tools/browser/browser-execution.ts +2 -2
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +55 -4
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +12 -6
- package/src/tools/browser/cdp-client/factory.ts +23 -24
- package/src/tools/browser/cdp-client/index.ts +1 -14
- package/src/tools/computer-use/definitions.ts +42 -20
- package/src/tools/executor.ts +2 -0
- package/src/tools/host-filesystem/edit.ts +26 -0
- package/src/tools/host-filesystem/read.ts +26 -0
- package/src/tools/host-filesystem/transfer.ts +31 -1
- package/src/tools/host-filesystem/write.ts +26 -0
- package/src/tools/host-terminal/host-shell.ts +58 -0
- package/src/tools/schedule/create.ts +6 -0
- package/src/tools/schedule/list.ts +2 -0
- package/src/tools/schedule/update.ts +10 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +2 -0
- package/src/tools/shared/filesystem/path-policy.ts +25 -1
- package/src/tools/skills/load.ts +0 -32
- package/src/tools/tool-approval-handler.ts +1 -5
- package/src/tools/types.ts +4 -0
- package/src/usage/pricing.ts +1 -1
- package/src/workspace/hatched-date.ts +86 -0
- package/src/workspace/migrations/003-seed-device-id.ts +1 -1
- package/src/workspace/migrations/006-services-config.ts +8 -5
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +3 -9
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +4 -10
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +4 -10
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +4 -11
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +3 -10
- package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +3 -2
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +2 -1
- package/src/workspace/migrations/059-move-pid-to-workspace.ts +3 -8
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +3 -8
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
- package/src/workspace/migrations/utils.ts +21 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +0 -443
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +0 -226
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +0 -427
- package/src/__tests__/twilio-rest.test.ts +0 -34
- package/src/backup/__tests__/backup-key.test.ts +0 -152
- package/src/backup/__tests__/backup-worker.test.ts +0 -782
- package/src/backup/__tests__/offsite-writer.test.ts +0 -641
- package/src/backup/__tests__/stream-crypt.test.ts +0 -228
- package/src/backup/backup-key.ts +0 -137
- package/src/backup/backup-worker.ts +0 -472
- package/src/backup/offsite-writer.ts +0 -222
- package/src/backup/stream-crypt.ts +0 -263
- package/src/daemon/message-types/pairing.ts +0 -58
- package/src/outbound-proxy/config.ts +0 -20
- package/src/outbound-proxy/health.ts +0 -18
- package/src/outbound-proxy/types.ts +0 -150
- package/src/runtime/capability-tokens.ts +0 -190
- package/src/signals/mcp-reload.ts +0 -18
|
@@ -25,7 +25,9 @@ import { extract as tarExtract } from "tar-stream";
|
|
|
25
25
|
export interface StreamedTarHeader {
|
|
26
26
|
name: string;
|
|
27
27
|
size: number;
|
|
28
|
-
type: "file" | "directory" | "pax-header" | "other";
|
|
28
|
+
type: "file" | "directory" | "pax-header" | "symlink" | "other";
|
|
29
|
+
/** Populated only when `type === "symlink"`; the symlink's target string from the tar header. */
|
|
30
|
+
linkname?: string;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
export interface StreamedTarEntry {
|
|
@@ -57,6 +59,8 @@ function normalizeHeaderType(
|
|
|
57
59
|
return "directory";
|
|
58
60
|
case "pax-header":
|
|
59
61
|
return "pax-header";
|
|
62
|
+
case "symlink":
|
|
63
|
+
return "symlink";
|
|
60
64
|
default:
|
|
61
65
|
return "other";
|
|
62
66
|
}
|
|
@@ -125,12 +129,17 @@ export async function* parseVBundleStream(
|
|
|
125
129
|
// Avoid unhandled "error" on body streams destroyed mid-flight; the
|
|
126
130
|
// extractor itself propagates the real error to its "error" listener.
|
|
127
131
|
body.on("error", () => {});
|
|
132
|
+
const normalizedType = normalizeHeaderType(header.type);
|
|
133
|
+
const surfaced: StreamedTarHeader = {
|
|
134
|
+
name: header.name,
|
|
135
|
+
size: header.size,
|
|
136
|
+
type: normalizedType,
|
|
137
|
+
};
|
|
138
|
+
if (normalizedType === "symlink" && header.linkname) {
|
|
139
|
+
surfaced.linkname = header.linkname;
|
|
140
|
+
}
|
|
128
141
|
pushEntry({
|
|
129
|
-
header:
|
|
130
|
-
name: header.name,
|
|
131
|
-
size: header.size,
|
|
132
|
-
type: normalizeHeaderType(header.type),
|
|
133
|
-
},
|
|
142
|
+
header: surfaced,
|
|
134
143
|
body,
|
|
135
144
|
next,
|
|
136
145
|
});
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { createHash, randomUUID } from "node:crypto";
|
|
17
|
+
import { posix } from "node:path";
|
|
17
18
|
import { gunzipSync } from "node:zlib";
|
|
18
19
|
|
|
19
20
|
import { z } from "zod";
|
|
@@ -26,6 +27,7 @@ const ManifestFileEntry = z.object({
|
|
|
26
27
|
path: z.string().min(1),
|
|
27
28
|
sha256: z.string().regex(/^[0-9a-f]{64}$/),
|
|
28
29
|
size_bytes: z.number().int().nonnegative(),
|
|
30
|
+
link_target: z.string().min(1).optional(),
|
|
29
31
|
});
|
|
30
32
|
|
|
31
33
|
const AssistantInfo = z.object({
|
|
@@ -201,6 +203,9 @@ export interface VBundleTarEntry {
|
|
|
201
203
|
name: string;
|
|
202
204
|
data: Uint8Array;
|
|
203
205
|
size: number;
|
|
206
|
+
/** Set when the tar entry is typeflag-2 (symlink); carries the link target
|
|
207
|
+
* decoded from the ustar linkname field. */
|
|
208
|
+
linkname?: string;
|
|
204
209
|
}
|
|
205
210
|
|
|
206
211
|
export interface VBundleValidationResult {
|
|
@@ -220,6 +225,9 @@ interface TarEntry {
|
|
|
220
225
|
name: string;
|
|
221
226
|
data: Uint8Array;
|
|
222
227
|
size: number;
|
|
228
|
+
/** Set when the tar entry is typeflag-2 (symlink); carries the link target
|
|
229
|
+
* decoded from the ustar linkname field. */
|
|
230
|
+
linkname?: string;
|
|
223
231
|
}
|
|
224
232
|
|
|
225
233
|
/**
|
|
@@ -282,6 +290,25 @@ function parseTar(buffer: Uint8Array): TarEntry[] {
|
|
|
282
290
|
continue;
|
|
283
291
|
}
|
|
284
292
|
|
|
293
|
+
// Symlink (type '2') — empty body regardless of declared size; the link
|
|
294
|
+
// target lives in the ustar linkname field (157..256). We preserve the
|
|
295
|
+
// tar-declared `size` here (rather than forcing it to 0) so the
|
|
296
|
+
// downstream `archiveEntry.size !== 0` check can surface
|
|
297
|
+
// `FILE_SIZE_MISMATCH` on malformed symlink headers. The body itself is
|
|
298
|
+
// always an empty buffer — symlinks have no data body even if the
|
|
299
|
+
// header lies about it.
|
|
300
|
+
if (typeFlag === "2") {
|
|
301
|
+
const linkname = decodeNullTerminated(header, 157, 100);
|
|
302
|
+
entries.push({
|
|
303
|
+
name: normalizePath(name),
|
|
304
|
+
data: new Uint8Array(0),
|
|
305
|
+
size,
|
|
306
|
+
linkname,
|
|
307
|
+
});
|
|
308
|
+
offset = dataStart + dataBlocks * BLOCK_SIZE;
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
|
|
285
312
|
// Regular file or hard link
|
|
286
313
|
if (typeFlag === "0" || typeFlag === "\0" || typeFlag === "") {
|
|
287
314
|
entries.push({ name: normalizePath(name), data, size });
|
|
@@ -509,6 +536,93 @@ export function validateVBundle(data: Uint8Array): VBundleValidationResult {
|
|
|
509
536
|
continue;
|
|
510
537
|
}
|
|
511
538
|
|
|
539
|
+
if (fileEntry.link_target !== undefined) {
|
|
540
|
+
// Symlink branch: typeflag agreement, linkname agreement, sha over the
|
|
541
|
+
// link target string, size==0 on both sides, and path-traversal rejection.
|
|
542
|
+
if (archiveEntry.linkname === undefined) {
|
|
543
|
+
errors.push({
|
|
544
|
+
code: "SYMLINK_TYPEFLAG_MISMATCH",
|
|
545
|
+
message: `Manifest declares symlink for ${fileEntry.path} but tar entry is not typeflag-2`,
|
|
546
|
+
path: fileEntry.path,
|
|
547
|
+
});
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (archiveEntry.linkname !== fileEntry.link_target) {
|
|
552
|
+
errors.push({
|
|
553
|
+
code: "LINK_TARGET_MISMATCH",
|
|
554
|
+
message: `Symlink linkname mismatch for ${fileEntry.path}: manifest declares "${fileEntry.link_target}", tar carries "${archiveEntry.linkname}"`,
|
|
555
|
+
path: fileEntry.path,
|
|
556
|
+
});
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (archiveEntry.size !== 0) {
|
|
561
|
+
errors.push({
|
|
562
|
+
code: "FILE_SIZE_MISMATCH",
|
|
563
|
+
message: `Size mismatch for ${fileEntry.path}: manifest declares ${fileEntry.size_bytes} bytes, archive has ${archiveEntry.size} bytes`,
|
|
564
|
+
path: fileEntry.path,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (fileEntry.size_bytes !== 0) {
|
|
569
|
+
errors.push({
|
|
570
|
+
code: "FILE_SIZE_MISMATCH",
|
|
571
|
+
message: `Size mismatch for ${fileEntry.path}: manifest declares ${fileEntry.size_bytes} bytes, archive has ${archiveEntry.size} bytes`,
|
|
572
|
+
path: fileEntry.path,
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const expected = sha256Hex(fileEntry.link_target);
|
|
577
|
+
if (expected !== fileEntry.sha256) {
|
|
578
|
+
errors.push({
|
|
579
|
+
code: "FILE_CHECKSUM_MISMATCH",
|
|
580
|
+
message: `Checksum mismatch for ${fileEntry.path}: expected ${fileEntry.sha256}, computed ${expected}`,
|
|
581
|
+
path: fileEntry.path,
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Absolute POSIX targets are unconstrained by the bundle root — reject
|
|
586
|
+
// them up front. The `posix.normalize` guard below only catches
|
|
587
|
+
// `..`-based escapes; an absolute path like `/etc/passwd` survives
|
|
588
|
+
// normalization unchanged and would otherwise pass.
|
|
589
|
+
if (fileEntry.link_target.startsWith("/")) {
|
|
590
|
+
errors.push({
|
|
591
|
+
code: "SYMLINK_TARGET_ESCAPES_ARCHIVE",
|
|
592
|
+
message: `Symlink target is absolute, which escapes the archive root for ${fileEntry.path}: target=${fileEntry.link_target}`,
|
|
593
|
+
path: fileEntry.path,
|
|
594
|
+
});
|
|
595
|
+
} else {
|
|
596
|
+
const normalized = posix.normalize(
|
|
597
|
+
posix.join(posix.dirname(fileEntry.path), fileEntry.link_target),
|
|
598
|
+
);
|
|
599
|
+
// Defense-in-depth: also reject if the joined+normalized path is
|
|
600
|
+
// absolute, in case `dirname` ever resolves to an absolute root.
|
|
601
|
+
if (
|
|
602
|
+
normalized.startsWith("../") ||
|
|
603
|
+
normalized === ".." ||
|
|
604
|
+
normalized.startsWith("/")
|
|
605
|
+
) {
|
|
606
|
+
errors.push({
|
|
607
|
+
code: "SYMLINK_TARGET_ESCAPES_ARCHIVE",
|
|
608
|
+
message: `Symlink target escapes archive root for ${fileEntry.path}: target=${fileEntry.link_target}, normalized=${normalized}`,
|
|
609
|
+
path: fileEntry.path,
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (archiveEntry.linkname !== undefined) {
|
|
617
|
+
// Tar carries a typeflag-2 entry but manifest declares a regular file.
|
|
618
|
+
errors.push({
|
|
619
|
+
code: "SYMLINK_NOT_DECLARED",
|
|
620
|
+
message: `Tar entry ${fileEntry.path} is typeflag-2 but manifest does not declare link_target`,
|
|
621
|
+
path: fileEntry.path,
|
|
622
|
+
});
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
|
|
512
626
|
// Verify size
|
|
513
627
|
if (archiveEntry.size !== fileEntry.size_bytes) {
|
|
514
628
|
errors.push({
|
|
@@ -3,10 +3,14 @@
|
|
|
3
3
|
* confirmation, secret, host_bash, host_file, host_cu, host_browser, and
|
|
4
4
|
* host_transfer interactions.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* For confirmation_request and secret_request, the onEvent callback in
|
|
7
|
+
* assistant-event-hub registers the interaction here.
|
|
8
|
+
*
|
|
9
|
+
* For host proxy interactions (host_bash, host_file, host_cu, host_browser,
|
|
10
|
+
* host_transfer), the proxy itself registers with full RPC lifecycle state
|
|
11
|
+
* (resolve/reject callbacks, timer, abort detach). This eliminates the
|
|
12
|
+
* per-proxy `private pending` maps — all pending state lives here.
|
|
13
|
+
*
|
|
10
14
|
* Standalone HTTP endpoints (/v1/confirm, /v1/secret, /v1/trust-rules,
|
|
11
15
|
* /v1/host-bash-result, /v1/host-file-result, /v1/host-cu-result,
|
|
12
16
|
* /v1/host-browser-result) look up the conversation from this tracker to
|
|
@@ -14,6 +18,7 @@
|
|
|
14
18
|
*/
|
|
15
19
|
|
|
16
20
|
import type { UserDecision } from "../permissions/types.js";
|
|
21
|
+
import type { ToolExecutionResult } from "../tools/types.js";
|
|
17
22
|
|
|
18
23
|
export interface ConfirmationDetails {
|
|
19
24
|
toolName: string;
|
|
@@ -46,11 +51,27 @@ export interface PendingInteraction {
|
|
|
46
51
|
| "host_file"
|
|
47
52
|
| "host_cu"
|
|
48
53
|
| "host_browser"
|
|
54
|
+
| "host_app_control"
|
|
49
55
|
| "host_transfer"
|
|
50
56
|
| "acp_confirmation";
|
|
51
57
|
confirmationDetails?: ConfirmationDetails;
|
|
52
58
|
/** For ACP permissions: resolves directly without a Conversation object. */
|
|
53
59
|
directResolve?: (decision: UserDecision) => void;
|
|
60
|
+
/** When set, the host_bash request should be routed to this specific client. */
|
|
61
|
+
targetClientId?: string;
|
|
62
|
+
|
|
63
|
+
// -- RPC lifecycle (populated by host proxies) --
|
|
64
|
+
|
|
65
|
+
/** Resolve the caller's Promise with a tool execution result. */
|
|
66
|
+
rpcResolve?: (result: ToolExecutionResult) => void;
|
|
67
|
+
/** Reject the caller's Promise with an error. */
|
|
68
|
+
rpcReject?: (err: Error) => void;
|
|
69
|
+
/** Proxy-side timeout timer. Cleared on resolve/abort/dispose. */
|
|
70
|
+
timer?: ReturnType<typeof setTimeout>;
|
|
71
|
+
/** Detach the abort listener from the caller's signal. No-op when no signal was passed. */
|
|
72
|
+
detachAbort?: () => void;
|
|
73
|
+
/** Proxy-specific metadata (e.g. timeoutSec for bash, operation/path for file). */
|
|
74
|
+
metadata?: Record<string, unknown>;
|
|
54
75
|
}
|
|
55
76
|
|
|
56
77
|
const pending = new Map<string, PendingInteraction>();
|
|
@@ -64,12 +85,15 @@ export function register(
|
|
|
64
85
|
|
|
65
86
|
/**
|
|
66
87
|
* Remove and return the pending interaction for the given requestId.
|
|
88
|
+
* Auto-clears the proxy timer and detaches the abort listener if present.
|
|
67
89
|
* Returns undefined if no interaction is registered.
|
|
68
90
|
*/
|
|
69
91
|
export function resolve(requestId: string): PendingInteraction | undefined {
|
|
70
92
|
const interaction = pending.get(requestId);
|
|
71
93
|
if (interaction) {
|
|
72
94
|
pending.delete(requestId);
|
|
95
|
+
if (interaction.timer != null) clearTimeout(interaction.timer);
|
|
96
|
+
interaction.detachAbort?.();
|
|
73
97
|
}
|
|
74
98
|
return interaction;
|
|
75
99
|
}
|
|
@@ -102,11 +126,12 @@ export function getByConversation(
|
|
|
102
126
|
* Remove pending confirmation and secret interactions for a given conversation.
|
|
103
127
|
* Used when auto-denying all pending interactions (e.g. new user message).
|
|
104
128
|
*
|
|
105
|
-
* host_bash, host_file, host_cu, host_browser, and
|
|
106
|
-
* are intentionally skipped — they represent
|
|
107
|
-
* to the client, not confirmations to
|
|
108
|
-
* the request: the client would POST to
|
|
109
|
-
* /v1/host-
|
|
129
|
+
* host_bash, host_file, host_cu, host_browser, host_app_control, and
|
|
130
|
+
* host_transfer interactions are intentionally skipped — they represent
|
|
131
|
+
* in-flight tool executions proxied to the client, not confirmations to
|
|
132
|
+
* auto-deny. Removing them would orphan the request: the client would POST to
|
|
133
|
+
* /v1/host-bash-result, /v1/host-file-result, /v1/host-cu-result,
|
|
134
|
+
* /v1/host-browser-result, /v1/host-app-control-result, or
|
|
110
135
|
* /v1/host-transfer-result after completing the operation, get a 404, and the
|
|
111
136
|
* proxy timer would fire with a spurious timeout error.
|
|
112
137
|
*/
|
|
@@ -118,6 +143,7 @@ export function removeByConversation(conversationId: string): void {
|
|
|
118
143
|
interaction.kind !== "host_file" &&
|
|
119
144
|
interaction.kind !== "host_cu" &&
|
|
120
145
|
interaction.kind !== "host_browser" &&
|
|
146
|
+
interaction.kind !== "host_app_control" &&
|
|
121
147
|
interaction.kind !== "host_transfer" &&
|
|
122
148
|
interaction.kind !== "acp_confirmation"
|
|
123
149
|
) {
|
|
@@ -22,7 +22,6 @@ import { tmpdir } from "node:os";
|
|
|
22
22
|
import { join } from "node:path";
|
|
23
23
|
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
24
24
|
|
|
25
|
-
import type { BackupRunResult } from "../../../backup/backup-worker.js";
|
|
26
25
|
import type { RestoreResult, VerifyResult } from "../../../backup/restore.js";
|
|
27
26
|
import type { BackupConfig } from "../../../config/schema.js";
|
|
28
27
|
import { BackupConfigSchema } from "../../../config/schema.js";
|
|
@@ -114,23 +113,6 @@ mock.module("../../../backup/backup-key.js", () => ({
|
|
|
114
113
|
ensureBackupKey: async (_path: string) => mockBackupKey ?? Buffer.alloc(32),
|
|
115
114
|
}));
|
|
116
115
|
|
|
117
|
-
// -- Backup worker mock ----------------------------------------------------
|
|
118
|
-
|
|
119
|
-
let mockCreateSnapshotResult: BackupRunResult | null = null;
|
|
120
|
-
let mockCreateSnapshotError: Error | null = null;
|
|
121
|
-
let mockCreateSnapshotCalls = 0;
|
|
122
|
-
|
|
123
|
-
mock.module("../../../backup/backup-worker.js", () => ({
|
|
124
|
-
createSnapshotNow: async (_config: BackupConfig, _now: Date) => {
|
|
125
|
-
mockCreateSnapshotCalls += 1;
|
|
126
|
-
if (mockCreateSnapshotError) throw mockCreateSnapshotError;
|
|
127
|
-
if (mockCreateSnapshotResult == null) {
|
|
128
|
-
throw new Error("Test forgot to set mockCreateSnapshotResult");
|
|
129
|
-
}
|
|
130
|
-
return mockCreateSnapshotResult;
|
|
131
|
-
},
|
|
132
|
-
}));
|
|
133
|
-
|
|
134
116
|
// -- Restore module mock ---------------------------------------------------
|
|
135
117
|
|
|
136
118
|
interface RestoreCall {
|
|
@@ -179,21 +161,20 @@ mock.module("../../../backup/restore.js", () => ({
|
|
|
179
161
|
restoreFromSnapshot: async (
|
|
180
162
|
path: string,
|
|
181
163
|
opts: {
|
|
182
|
-
key?: Buffer;
|
|
183
164
|
workspaceDir?: string;
|
|
184
165
|
},
|
|
185
166
|
) => {
|
|
186
167
|
recoveryCallOrder.push("restoreFromSnapshot");
|
|
187
168
|
lastRestoreArgs = {
|
|
188
169
|
path,
|
|
189
|
-
hasKey:
|
|
170
|
+
hasKey: false,
|
|
190
171
|
workspaceDir: opts.workspaceDir,
|
|
191
172
|
};
|
|
192
173
|
if (mockRestoreError) throw mockRestoreError;
|
|
193
174
|
return mockRestoreResult;
|
|
194
175
|
},
|
|
195
|
-
verifySnapshot: async (path: string
|
|
196
|
-
lastVerifyArgs = { path, hasKey:
|
|
176
|
+
verifySnapshot: async (path: string) => {
|
|
177
|
+
lastVerifyArgs = { path, hasKey: false };
|
|
197
178
|
return mockVerifyResult;
|
|
198
179
|
},
|
|
199
180
|
}));
|
|
@@ -248,9 +229,6 @@ beforeEach(() => {
|
|
|
248
229
|
}
|
|
249
230
|
mockBackupKey = Buffer.alloc(32, 0xaa);
|
|
250
231
|
mockReadBackupKeyCalls = 0;
|
|
251
|
-
mockCreateSnapshotResult = null;
|
|
252
|
-
mockCreateSnapshotError = null;
|
|
253
|
-
mockCreateSnapshotCalls = 0;
|
|
254
232
|
lastRestoreArgs = null;
|
|
255
233
|
lastVerifyArgs = null;
|
|
256
234
|
mockRestoreError = null;
|
|
@@ -431,105 +409,19 @@ describe("handleBackupList", () => {
|
|
|
431
409
|
});
|
|
432
410
|
});
|
|
433
411
|
|
|
412
|
+
// ---------------------------------------------------------------------------
|
|
434
413
|
// ---------------------------------------------------------------------------
|
|
435
414
|
// handleBackupCreate
|
|
436
415
|
// ---------------------------------------------------------------------------
|
|
437
416
|
|
|
438
417
|
describe("handleBackupCreate", () => {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
filename: "backup-20260411-100000.vbundle",
|
|
443
|
-
createdAt: new Date("2026-04-11T10:00:00Z"),
|
|
444
|
-
sizeBytes: 100,
|
|
445
|
-
encrypted: false,
|
|
446
|
-
},
|
|
447
|
-
offsite: [],
|
|
448
|
-
durationMs: 42,
|
|
449
|
-
};
|
|
450
|
-
|
|
451
|
-
test("manual create bypasses enabled flag and succeeds with disabled config", async () => {
|
|
452
|
-
mockBackupConfig = makeConfig({
|
|
453
|
-
enabled: false,
|
|
454
|
-
localDirectory: LOCAL_DIR,
|
|
455
|
-
offsite: { enabled: false, destinations: null },
|
|
456
|
-
});
|
|
457
|
-
mockCreateSnapshotResult = fakeRunResult;
|
|
458
|
-
|
|
459
|
-
const result = await handleBackupCreate();
|
|
460
|
-
expect(result.durationMs).toBe(42);
|
|
461
|
-
expect(result.offsite).toEqual([]);
|
|
462
|
-
expect(mockCreateSnapshotCalls).toBe(1);
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
test("plaintext-only destinations do not create backup.key file", async () => {
|
|
466
|
-
const plaintextDir = join(ROOT, "offsite-plain");
|
|
467
|
-
mkdirSync(plaintextDir, { recursive: true });
|
|
468
|
-
mockBackupConfig = makeConfig({
|
|
469
|
-
enabled: true,
|
|
470
|
-
localDirectory: LOCAL_DIR,
|
|
471
|
-
offsite: {
|
|
472
|
-
enabled: true,
|
|
473
|
-
destinations: [{ path: plaintextDir, encrypt: false }],
|
|
474
|
-
},
|
|
475
|
-
});
|
|
476
|
-
mockCreateSnapshotResult = fakeRunResult;
|
|
477
|
-
mockReadBackupKeyCalls = 0;
|
|
478
|
-
|
|
479
|
-
await handleBackupCreate();
|
|
480
|
-
expect(mockReadBackupKeyCalls).toBe(0);
|
|
481
|
-
const keyFileExists = await import("node:fs").then((m) =>
|
|
482
|
-
m.existsSync(join(ROOT, "workspace", ".backup.key")),
|
|
483
|
-
);
|
|
484
|
-
expect(keyFileExists).toBe(false);
|
|
485
|
-
});
|
|
486
|
-
|
|
487
|
-
test("concurrent call throws ConflictError when mock raises 'snapshot in progress'", async () => {
|
|
488
|
-
mockBackupConfig = makeConfig({ localDirectory: LOCAL_DIR });
|
|
489
|
-
mockCreateSnapshotError = new Error("snapshot in progress");
|
|
490
|
-
|
|
491
|
-
try {
|
|
492
|
-
await handleBackupCreate();
|
|
493
|
-
expect.unreachable("should have thrown");
|
|
494
|
-
} catch (err) {
|
|
495
|
-
expect(err).toBeInstanceOf(RouteError);
|
|
496
|
-
expect((err as RouteError).statusCode).toBe(409);
|
|
497
|
-
expect((err as RouteError).code).toBe("CONFLICT");
|
|
498
|
-
}
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
test("cross-process conflict ('locked by pid N') is still mapped to 409", async () => {
|
|
502
|
-
mockBackupConfig = makeConfig({ localDirectory: LOCAL_DIR });
|
|
503
|
-
mockCreateSnapshotError = new Error(
|
|
504
|
-
"snapshot in progress (locked by pid 12345)",
|
|
418
|
+
test("always throws BadRequestError redirecting to gateway", async () => {
|
|
419
|
+
await expect(handleBackupCreate()).rejects.toThrow(
|
|
420
|
+
"Backup snapshot creation has moved to the gateway",
|
|
505
421
|
);
|
|
506
|
-
|
|
507
|
-
try {
|
|
508
|
-
await handleBackupCreate();
|
|
509
|
-
expect.unreachable("should have thrown");
|
|
510
|
-
} catch (err) {
|
|
511
|
-
expect(err).toBeInstanceOf(RouteError);
|
|
512
|
-
expect((err as RouteError).statusCode).toBe(409);
|
|
513
|
-
expect((err as RouteError).code).toBe("CONFLICT");
|
|
514
|
-
}
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
test("other errors are surfaced as 500", async () => {
|
|
518
|
-
mockCreateSnapshotError = new Error("disk full");
|
|
519
|
-
|
|
520
|
-
try {
|
|
521
|
-
await handleBackupCreate();
|
|
522
|
-
expect.unreachable("should have thrown");
|
|
523
|
-
} catch (err) {
|
|
524
|
-
expect(err).toBeInstanceOf(RouteError);
|
|
525
|
-
expect((err as RouteError).statusCode).toBe(500);
|
|
526
|
-
expect((err as RouteError).code).toBe("INTERNAL_ERROR");
|
|
527
|
-
expect((err as RouteError).message).toBe("disk full");
|
|
528
|
-
}
|
|
529
422
|
});
|
|
530
423
|
});
|
|
531
424
|
|
|
532
|
-
// ---------------------------------------------------------------------------
|
|
533
425
|
// handleBackupRestore
|
|
534
426
|
// ---------------------------------------------------------------------------
|
|
535
427
|
|
|
@@ -613,29 +505,7 @@ describe("handleBackupRestore", () => {
|
|
|
613
505
|
expect(lastRestoreArgs!.path).toBe(expectedRealpath);
|
|
614
506
|
});
|
|
615
507
|
|
|
616
|
-
test("encrypted .vbundle.enc
|
|
617
|
-
const snapshotPath = writeBackupFile(
|
|
618
|
-
LOCAL_DIR,
|
|
619
|
-
"backup-20260411-100000.vbundle.enc",
|
|
620
|
-
);
|
|
621
|
-
mockBackupConfig = makeConfig({
|
|
622
|
-
localDirectory: LOCAL_DIR,
|
|
623
|
-
offsite: { enabled: true, destinations: [] },
|
|
624
|
-
});
|
|
625
|
-
mockBackupKey = Buffer.alloc(32, 0xbb);
|
|
626
|
-
mockReadBackupKeyCalls = 0;
|
|
627
|
-
|
|
628
|
-
await handleBackupRestore({
|
|
629
|
-
body: { path: snapshotPath },
|
|
630
|
-
pathParams: {},
|
|
631
|
-
queryParams: {},
|
|
632
|
-
});
|
|
633
|
-
expect(mockReadBackupKeyCalls).toBe(1);
|
|
634
|
-
expect(lastRestoreArgs).not.toBeNull();
|
|
635
|
-
expect(lastRestoreArgs!.hasKey).toBe(true);
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
test("encrypted bundle with missing backup.key throws BadRequestError", async () => {
|
|
508
|
+
test("encrypted .vbundle.enc is rejected with gateway redirect error", async () => {
|
|
639
509
|
const snapshotPath = writeBackupFile(
|
|
640
510
|
LOCAL_DIR,
|
|
641
511
|
"backup-20260411-100000.vbundle.enc",
|
|
@@ -644,7 +514,6 @@ describe("handleBackupRestore", () => {
|
|
|
644
514
|
localDirectory: LOCAL_DIR,
|
|
645
515
|
offsite: { enabled: true, destinations: [] },
|
|
646
516
|
});
|
|
647
|
-
mockBackupKey = null;
|
|
648
517
|
|
|
649
518
|
try {
|
|
650
519
|
await handleBackupRestore({
|
|
@@ -656,7 +525,7 @@ describe("handleBackupRestore", () => {
|
|
|
656
525
|
} catch (err) {
|
|
657
526
|
expect(err).toBeInstanceOf(RouteError);
|
|
658
527
|
expect((err as RouteError).statusCode).toBe(400);
|
|
659
|
-
expect((err as RouteError).message).toMatch(/
|
|
528
|
+
expect((err as RouteError).message).toMatch(/gateway/i);
|
|
660
529
|
}
|
|
661
530
|
expect(lastRestoreArgs).toBeNull();
|
|
662
531
|
});
|
|
@@ -794,7 +663,7 @@ describe("handleBackupVerify", () => {
|
|
|
794
663
|
expect(result.valid).toBe(true);
|
|
795
664
|
});
|
|
796
665
|
|
|
797
|
-
test("encrypted bundle
|
|
666
|
+
test("encrypted bundle is rejected with gateway redirect error", async () => {
|
|
798
667
|
const snapshotPath = writeBackupFile(
|
|
799
668
|
LOCAL_DIR,
|
|
800
669
|
"backup-20260411-100000.vbundle.enc",
|
|
@@ -803,16 +672,19 @@ describe("handleBackupVerify", () => {
|
|
|
803
672
|
localDirectory: LOCAL_DIR,
|
|
804
673
|
offsite: { enabled: true, destinations: [] },
|
|
805
674
|
});
|
|
806
|
-
mockBackupKey = Buffer.alloc(32, 0xcc);
|
|
807
|
-
mockReadBackupKeyCalls = 0;
|
|
808
675
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
676
|
+
try {
|
|
677
|
+
await handleBackupVerify({
|
|
678
|
+
body: { path: snapshotPath },
|
|
679
|
+
pathParams: {},
|
|
680
|
+
queryParams: {},
|
|
681
|
+
});
|
|
682
|
+
expect.unreachable("should have thrown");
|
|
683
|
+
} catch (err) {
|
|
684
|
+
expect(err).toBeInstanceOf(RouteError);
|
|
685
|
+
expect((err as RouteError).statusCode).toBe(400);
|
|
686
|
+
expect((err as RouteError).message).toMatch(/gateway/i);
|
|
687
|
+
}
|
|
816
688
|
});
|
|
817
689
|
|
|
818
690
|
test("path outside allowed directories throws BadRequestError", async () => {
|
|
@@ -29,6 +29,7 @@ mock.module("../../../config/loader.js", () => ({
|
|
|
29
29
|
},
|
|
30
30
|
}));
|
|
31
31
|
|
|
32
|
+
import type { ConversationCreateType } from "../../../memory/conversation-crud.js";
|
|
32
33
|
import { getDb } from "../../../memory/db-connection.js";
|
|
33
34
|
import { initializeDb } from "../../../memory/db-init.js";
|
|
34
35
|
import {
|
|
@@ -39,8 +40,10 @@ import {
|
|
|
39
40
|
recordMemoryV2ActivationLog,
|
|
40
41
|
} from "../../../memory/memory-v2-activation-log-store.js";
|
|
41
42
|
import {
|
|
43
|
+
conversations,
|
|
42
44
|
llmRequestLogs,
|
|
43
45
|
memoryV2ActivationLogs,
|
|
46
|
+
messages,
|
|
44
47
|
} from "../../../memory/schema.js";
|
|
45
48
|
import { ROUTES } from "../conversation-query-routes.js";
|
|
46
49
|
|
|
@@ -68,6 +71,40 @@ function clearTables(): void {
|
|
|
68
71
|
const db = getDb();
|
|
69
72
|
db.delete(llmRequestLogs).run();
|
|
70
73
|
db.delete(memoryV2ActivationLogs).run();
|
|
74
|
+
db.delete(messages).run();
|
|
75
|
+
db.delete(conversations).run();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function seedConversationAndMessage(args: {
|
|
79
|
+
conversationId: string;
|
|
80
|
+
messageId: string;
|
|
81
|
+
source: string;
|
|
82
|
+
conversationType: ConversationCreateType;
|
|
83
|
+
}): void {
|
|
84
|
+
const now = Date.now();
|
|
85
|
+
getDb()
|
|
86
|
+
.insert(conversations)
|
|
87
|
+
.values({
|
|
88
|
+
id: args.conversationId,
|
|
89
|
+
title: null,
|
|
90
|
+
createdAt: now,
|
|
91
|
+
updatedAt: now,
|
|
92
|
+
source: args.source,
|
|
93
|
+
conversationType: args.conversationType,
|
|
94
|
+
memoryScopeId: "default",
|
|
95
|
+
})
|
|
96
|
+
.run();
|
|
97
|
+
getDb()
|
|
98
|
+
.insert(messages)
|
|
99
|
+
.values({
|
|
100
|
+
id: args.messageId,
|
|
101
|
+
conversationId: args.conversationId,
|
|
102
|
+
role: "assistant",
|
|
103
|
+
content: "",
|
|
104
|
+
createdAt: now,
|
|
105
|
+
metadata: null,
|
|
106
|
+
})
|
|
107
|
+
.run();
|
|
71
108
|
}
|
|
72
109
|
|
|
73
110
|
function seedRequestLog(messageId: string, id: string): void {
|
|
@@ -143,6 +180,67 @@ describe("GET /v1/messages/:id/llm-context — memoryV2Activation", () => {
|
|
|
143
180
|
});
|
|
144
181
|
});
|
|
145
182
|
|
|
183
|
+
describe("GET /v1/messages/:id/llm-context — conversationKind", () => {
|
|
184
|
+
beforeEach(() => {
|
|
185
|
+
clearTables();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("returns 'background_memory_consolidation' for memory_v2_consolidation source", async () => {
|
|
189
|
+
seedConversationAndMessage({
|
|
190
|
+
conversationId: "conv-mem-consol",
|
|
191
|
+
messageId: "msg-mem-consol",
|
|
192
|
+
source: "memory_v2_consolidation",
|
|
193
|
+
conversationType: "background",
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const body = (await dispatchLlmContext("msg-mem-consol")) as {
|
|
197
|
+
conversationKind: string;
|
|
198
|
+
logs: unknown[];
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
expect(body.conversationKind).toBe("background_memory_consolidation");
|
|
202
|
+
expect(body.logs).toEqual([]);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("returns 'background' for non-consolidation background conversations", async () => {
|
|
206
|
+
seedConversationAndMessage({
|
|
207
|
+
conversationId: "conv-bg",
|
|
208
|
+
messageId: "msg-bg",
|
|
209
|
+
source: "memory_consolidation",
|
|
210
|
+
conversationType: "background",
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const body = (await dispatchLlmContext("msg-bg")) as {
|
|
214
|
+
conversationKind: string;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
expect(body.conversationKind).toBe("background");
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("returns 'user' for standard conversations", async () => {
|
|
221
|
+
seedConversationAndMessage({
|
|
222
|
+
conversationId: "conv-user",
|
|
223
|
+
messageId: "msg-user",
|
|
224
|
+
source: "user",
|
|
225
|
+
conversationType: "standard",
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const body = (await dispatchLlmContext("msg-user")) as {
|
|
229
|
+
conversationKind: string;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
expect(body.conversationKind).toBe("user");
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("falls back to 'user' when the message can't be resolved", async () => {
|
|
236
|
+
const body = (await dispatchLlmContext("msg-missing")) as {
|
|
237
|
+
conversationKind: string;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
expect(body.conversationKind).toBe("user");
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
146
244
|
describe("PUT /v1/config/llm/profiles/:name", () => {
|
|
147
245
|
beforeEach(() => {
|
|
148
246
|
savedRawConfig = null;
|