@vellumai/assistant 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +45 -29
- package/Dockerfile +1 -0
- package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
- package/bun.lock +3 -0
- package/docs/architecture/memory.md +5 -2
- package/knip.json +1 -0
- package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
- package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
- package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
- package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
- package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
- package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
- package/openapi.yaml +470 -25
- package/package.json +3 -1
- package/src/__tests__/annotate-risk-options.test.ts +291 -0
- package/src/__tests__/app-control-flow.test.ts +21 -11
- package/src/__tests__/approval-cascade.test.ts +8 -16
- package/src/__tests__/approval-routes-http.test.ts +6 -0
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/assistant-event.test.ts +0 -10
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
- package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
- package/src/__tests__/auto-analysis-end-to-end.test.ts +48 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
- package/src/__tests__/call-constants.test.ts +10 -1
- package/src/__tests__/call-controller.test.ts +127 -0
- package/src/__tests__/call-conversation-messages.test.ts +8 -2
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
- package/src/__tests__/channel-readiness-service.test.ts +4 -2
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +58 -28
- package/src/__tests__/config-loader-backfill.test.ts +379 -0
- package/src/__tests__/config-loader-platform-defaults.test.ts +284 -1
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
- package/src/__tests__/config-watcher.test.ts +140 -69
- package/src/__tests__/context-search-agent-runner.test.ts +61 -3
- package/src/__tests__/context-search-conversations-source.test.ts +0 -24
- package/src/__tests__/context-search-fanout.test.ts +0 -1
- package/src/__tests__/context-search-memory-source.test.ts +6 -33
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
- package/src/__tests__/context-search-pkb-source.test.ts +12 -7
- package/src/__tests__/context-search-workspace-source.test.ts +0 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop.test.ts +457 -8
- package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
- package/src/__tests__/conversation-error.test.ts +150 -3
- package/src/__tests__/conversation-init.benchmark.test.ts +1 -1
- package/src/__tests__/conversation-process-callsite.test.ts +38 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +74 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
- package/src/__tests__/conversation-speed-override.test.ts +0 -3
- package/src/__tests__/conversation-store.test.ts +0 -18
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
- package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +476 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +61 -5
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/credentials-cli.test.ts +7 -0
- package/src/__tests__/cu-unified-flow.test.ts +176 -10
- package/src/__tests__/date-context.test.ts +164 -2
- package/src/__tests__/disk-pressure-guard.test.ts +262 -0
- package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
- package/src/__tests__/disk-pressure-policy.test.ts +241 -0
- package/src/__tests__/disk-pressure-routes.test.ts +379 -0
- package/src/__tests__/disk-pressure-tools.test.ts +277 -0
- package/src/__tests__/disk-usage.test.ts +150 -0
- package/src/__tests__/events-client-registration.test.ts +52 -0
- package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
- package/src/__tests__/file-write-tool.test.ts +4 -10
- package/src/__tests__/filing-service.test.ts +2 -20
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -26
- package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
- package/src/__tests__/heartbeat-service.test.ts +260 -11
- package/src/__tests__/host-app-control-proxy.test.ts +195 -25
- package/src/__tests__/host-bash-proxy.test.ts +227 -34
- package/src/__tests__/host-bash-routes.test.ts +178 -13
- package/src/__tests__/host-cu-proxy.test.ts +210 -3
- package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
- package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
- package/src/__tests__/host-file-proxy.test.ts +268 -6
- package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
- package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
- package/src/__tests__/http-user-message-parity.test.ts +107 -1
- package/src/__tests__/injector-chain.test.ts +36 -16
- package/src/__tests__/injector-disk-pressure.test.ts +224 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +154 -67
- package/src/__tests__/managed-profile-guard.test.ts +18 -0
- package/src/__tests__/mcp-abort-signal.test.ts +130 -0
- package/src/__tests__/memory-admin-recall.test.ts +3 -11
- package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
- package/src/__tests__/normalize-onboarding.test.ts +180 -0
- package/src/__tests__/notification-decision-fallback.test.ts +91 -0
- package/src/__tests__/notification-decision-strategy.test.ts +22 -0
- package/src/__tests__/oauth-cli.test.ts +121 -0
- package/src/__tests__/oauth-connect-routes.test.ts +316 -0
- package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
- package/src/__tests__/onboarding-persona-write.test.ts +308 -0
- package/src/__tests__/openai-provider.test.ts +45 -8
- package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
- package/src/__tests__/platform-callback-registration.test.ts +21 -4
- package/src/__tests__/platform.test.ts +2 -1
- package/src/__tests__/playbook-execution.test.ts +0 -43
- package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +214 -27
- package/src/__tests__/provider-tool-name.test.ts +23 -0
- package/src/__tests__/relay-server.test.ts +60 -5
- package/src/__tests__/runtime-events-sse.test.ts +4 -8
- package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
- package/src/__tests__/secret-ingress-http.test.ts +0 -1
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
- package/src/__tests__/secret-response-routing.test.ts +7 -5
- package/src/__tests__/server-history-render.test.ts +82 -0
- package/src/__tests__/skill-include-graph.test.ts +31 -0
- package/src/__tests__/skill-load-tool.test.ts +44 -16
- package/src/__tests__/skills.test.ts +39 -0
- package/src/__tests__/suggestion-routes.test.ts +46 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
- package/src/__tests__/tool-executor.test.ts +155 -0
- package/src/__tests__/twilio-validation.test.ts +2 -2
- package/src/__tests__/voice-session-bridge.test.ts +3 -0
- package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
- package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
- package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
- package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +120 -0
- package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
- package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +78 -0
- package/src/agent/loop.ts +11 -0
- package/src/approvals/guardian-request-resolvers.ts +3 -32
- package/src/backup/snapshot-lock.ts +2 -27
- package/src/bundler/compiler-tools.ts +3 -2
- package/src/calls/call-constants.ts +5 -8
- package/src/calls/call-controller.ts +130 -67
- package/src/calls/call-conversation-messages.ts +46 -10
- package/src/calls/relay-server.ts +7 -1
- package/src/calls/voice-session-bridge.ts +1 -1
- package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
- package/src/cli/commands/bash.ts +35 -108
- package/src/cli/commands/contacts.ts +64 -25
- package/src/cli/commands/credentials.ts +56 -0
- package/src/cli/commands/memory-v2.ts +11 -10
- package/src/cli/commands/oauth/__tests__/connect.test.ts +401 -219
- package/src/cli/commands/oauth/connect.ts +124 -40
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
- package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
- package/src/cli/commands/platform/index.ts +16 -7
- package/src/cli/commands/status.ts +57 -0
- package/src/cli/program.ts +4 -2
- package/src/config/assistant-feature-flags.ts +13 -3
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
- package/src/config/env.ts +0 -8
- package/src/config/feature-flag-registry.json +13 -5
- package/src/config/loader.ts +199 -27
- package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
- package/src/config/schemas/call-site-catalog.ts +14 -0
- package/src/config/schemas/channels.ts +0 -5
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +2 -0
- package/src/config/schemas/memory-lifecycle.ts +13 -0
- package/src/config/schemas/memory-v2.ts +76 -12
- package/src/config/schemas/platform.ts +43 -3
- package/src/config/schemas/services.ts +28 -0
- package/src/config/seed-inference-profiles.ts +230 -33
- package/src/contacts/contact-store.ts +0 -25
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +32 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
- package/src/daemon/assistant-attachments.ts +4 -4
- package/src/daemon/config-watcher.ts +85 -57
- package/src/daemon/conversation-agent-loop-handlers.ts +38 -0
- package/src/daemon/conversation-agent-loop.ts +183 -43
- package/src/daemon/conversation-error.ts +87 -15
- package/src/daemon/conversation-lifecycle.ts +22 -10
- package/src/daemon/conversation-process.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +26 -0
- package/src/daemon/conversation-store.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +211 -29
- package/src/daemon/conversation-tool-setup.ts +66 -19
- package/src/daemon/conversation.ts +18 -23
- package/src/daemon/date-context.ts +71 -22
- package/src/daemon/disk-pressure-background-gate.ts +73 -0
- package/src/daemon/disk-pressure-guard.ts +343 -0
- package/src/daemon/disk-pressure-policy.ts +163 -0
- package/src/daemon/handlers/shared.ts +26 -1
- package/src/daemon/handlers/skills.ts +3 -4
- package/src/daemon/host-app-control-proxy.ts +137 -41
- package/src/daemon/host-bash-proxy.ts +47 -22
- package/src/daemon/host-browser-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +50 -4
- package/src/daemon/host-file-proxy.ts +44 -8
- package/src/daemon/host-transfer-proxy.ts +97 -6
- package/src/daemon/lifecycle.ts +167 -101
- package/src/daemon/meet-host-supervisor.ts +4 -4
- package/src/daemon/meet-manifest-loader.ts +0 -1
- package/src/daemon/memory-v2-startup.ts +66 -15
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +4 -0
- package/src/daemon/message-types/disk-pressure.ts +9 -0
- package/src/daemon/message-types/messages.ts +22 -1
- package/src/daemon/profiler-run-store.ts +5 -5
- package/src/daemon/tool-setup-types.ts +2 -2
- package/src/documents/document-store.ts +119 -0
- package/src/filing/filing-service.ts +29 -5
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
- package/src/heartbeat/heartbeat-run-store.ts +13 -0
- package/src/heartbeat/heartbeat-service.ts +205 -31
- package/src/home/feed-scheduler.ts +18 -0
- package/src/inbound/platform-callback-registration.ts +8 -15
- package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
- package/src/ipc/assistant-server.ts +149 -38
- package/src/ipc/gateway-client.ts +37 -3
- package/src/ipc/skill-server.ts +99 -42
- package/src/live-voice/live-voice-archive.ts +4 -4
- package/src/live-voice/protocol.ts +5 -7
- package/src/media/image-service.ts +1 -7
- package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +34 -51
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
- package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
- package/src/memory/admin.ts +5 -9
- package/src/memory/context-search/agent-runner.ts +19 -2
- package/src/memory/context-search/sources/conversations.ts +2 -11
- package/src/memory/context-search/sources/memory-v2.ts +1 -16
- package/src/memory/context-search/sources/memory.ts +2 -3
- package/src/memory/context-search/sources/pkb.ts +2 -3
- package/src/memory/context-search/types.ts +0 -1
- package/src/memory/conversation-crud.ts +4 -12
- package/src/memory/db-init.ts +2 -0
- package/src/memory/embedding-runtime-manager.ts +119 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +136 -82
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
- package/src/memory/graph/conversation-graph-memory.ts +72 -61
- package/src/memory/graph/extraction.ts +1 -3
- package/src/memory/graph/graph-search.test.ts +11 -67
- package/src/memory/graph/graph-search.ts +4 -24
- package/src/memory/graph/retriever.test.ts +12 -1
- package/src/memory/graph/retriever.ts +10 -15
- package/src/memory/graph/tool-handlers.ts +3 -4
- package/src/memory/graph/tools.ts +4 -4
- package/src/memory/indexer.ts +53 -45
- package/src/memory/job-handlers/backfill.ts +2 -11
- package/src/memory/job-handlers/cleanup.ts +43 -0
- package/src/memory/job-handlers/embedding.ts +6 -8
- package/src/memory/job-handlers/summarization.ts +2 -7
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +116 -0
- package/src/memory/jobs/embed-concept-page.ts +223 -87
- package/src/memory/jobs-store.ts +48 -0
- package/src/memory/jobs-worker.ts +85 -43
- package/src/memory/memory-v2-activation-log-store.ts +32 -14
- package/src/memory/memory-v2-concept-frequency.ts +169 -0
- package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +7 -0
- package/src/memory/pkb/pkb-search.ts +4 -5
- package/src/memory/qdrant-client.ts +3 -13
- package/src/memory/rerank-local.ts +374 -0
- package/src/memory/search/semantic.ts +10 -72
- package/src/memory/trace-event-store.ts +1 -17
- package/src/memory/v2/__tests__/activation.test.ts +346 -255
- package/src/memory/v2/__tests__/consolidation-job.test.ts +61 -40
- package/src/memory/v2/__tests__/injection.test.ts +297 -190
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +326 -9
- package/src/memory/v2/__tests__/reranker.test.ts +338 -0
- package/src/memory/v2/__tests__/sim.test.ts +113 -196
- package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
- package/src/memory/v2/__tests__/static-context.test.ts +77 -14
- package/src/memory/v2/__tests__/sweep-job.test.ts +19 -33
- package/src/memory/v2/activation.ts +149 -156
- package/src/memory/v2/consolidation-job.ts +69 -20
- package/src/memory/v2/injection.ts +75 -68
- package/src/memory/v2/page-store.ts +39 -0
- package/src/memory/v2/prompts/consolidation.ts +41 -1
- package/src/memory/v2/qdrant.ts +306 -46
- package/src/memory/v2/reranker.ts +177 -0
- package/src/memory/v2/sim.ts +77 -110
- package/src/memory/v2/skill-content.ts +4 -3
- package/src/memory/v2/skill-store.ts +82 -59
- package/src/memory/v2/static-context.ts +26 -8
- package/src/memory/v2/sweep-job.ts +5 -6
- package/src/memory/v2/types.ts +17 -10
- package/src/notifications/copy-composer.ts +47 -0
- package/src/notifications/decision-engine.ts +46 -0
- package/src/notifications/signal.ts +4 -0
- package/src/oauth/AGENTS.md +3 -1
- package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
- package/src/oauth/connect-orchestrator.ts +2 -0
- package/src/oauth/connection-resolver.test.ts +66 -1
- package/src/oauth/connection-resolver.ts +55 -1
- package/src/oauth/oauth-connect-state.ts +77 -0
- package/src/oauth/seed-providers.ts +58 -1
- package/src/permissions/gateway-threshold-reader.ts +116 -8
- package/src/permissions/prompter.ts +86 -96
- package/src/permissions/secret-prompter.ts +31 -31
- package/src/plugins/defaults/injectors.ts +36 -4
- package/src/plugins/defaults/memory-retrieval.ts +5 -6
- package/src/plugins/types.ts +7 -0
- package/src/proactive-artifact/aux-message-injector.ts +74 -0
- package/src/proactive-artifact/decision.test.ts +226 -0
- package/src/proactive-artifact/decision.ts +165 -0
- package/src/proactive-artifact/index.ts +7 -0
- package/src/proactive-artifact/job.test.ts +914 -0
- package/src/proactive-artifact/job.ts +366 -0
- package/src/proactive-artifact/message-copy.ts +58 -0
- package/src/proactive-artifact/trigger-state.test.ts +277 -0
- package/src/proactive-artifact/trigger-state.ts +119 -0
- package/src/prompts/normalize-onboarding.ts +80 -0
- package/src/prompts/persona-resolver.ts +101 -9
- package/src/prompts/system-prompt.ts +21 -7
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/SOUL.md +13 -28
- package/src/providers/__tests__/retry-callsite.test.ts +222 -1
- package/src/providers/model-intents.ts +7 -0
- package/src/providers/openrouter/client.ts +8 -0
- package/src/providers/retry.ts +50 -0
- package/src/providers/types.ts +1 -0
- package/src/runtime/__tests__/agent-wake.test.ts +456 -3
- package/src/runtime/agent-wake.ts +238 -100
- package/src/runtime/assistant-event-hub.ts +36 -6
- package/src/runtime/assistant-event.ts +0 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
- package/src/runtime/auth/route-policy.ts +15 -1
- package/src/runtime/auth/same-actor.ts +216 -0
- package/src/runtime/channel-approvals.ts +3 -2
- package/src/runtime/channel-retry-sweep.ts +65 -1
- package/src/runtime/local-actor-identity.ts +52 -11
- package/src/runtime/pending-interactions.ts +27 -15
- package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
- package/src/runtime/routes/approval-routes.ts +7 -3
- package/src/runtime/routes/client-routes.ts +20 -2
- package/src/runtime/routes/consolidation-routes.ts +8 -9
- package/src/runtime/routes/contact-routes.ts +0 -25
- package/src/runtime/routes/conversation-query-routes.ts +44 -1
- package/src/runtime/routes/conversation-routes.ts +35 -26
- package/src/runtime/routes/debug-bash-routes.ts +165 -0
- package/src/runtime/routes/disk-pressure-routes.ts +121 -0
- package/src/runtime/routes/document-pdf-renderer.ts +6 -2
- package/src/runtime/routes/documents-routes.ts +2 -75
- package/src/runtime/routes/events-routes.ts +41 -9
- package/src/runtime/routes/filing-routes.ts +2 -3
- package/src/runtime/routes/host-bash-routes.ts +23 -3
- package/src/runtime/routes/host-cu-routes.ts +33 -6
- package/src/runtime/routes/host-file-routes.ts +32 -6
- package/src/runtime/routes/host-transfer-routes.ts +79 -16
- package/src/runtime/routes/identity-routes.ts +7 -138
- package/src/runtime/routes/inbound-message-handler.ts +77 -12
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/memory-item-routes.test.ts +37 -17
- package/src/runtime/routes/memory-item-routes.ts +5 -6
- package/src/runtime/routes/memory-v2-routes.ts +136 -17
- package/src/runtime/routes/oauth-connect-routes.ts +153 -0
- package/src/runtime/verification-outbound-actions.ts +4 -4
- package/src/schedule/run-script.ts +37 -5
- package/src/schedule/scheduler.ts +20 -1
- package/src/security/encrypted-store.ts +2 -0
- package/src/security/secure-keys.ts +55 -0
- package/src/skills/include-graph.ts +35 -13
- package/src/skills/remote-skill-policy.ts +4 -10
- package/src/subagent/index.ts +1 -7
- package/src/subagent/manager.ts +1 -15
- package/src/tasks/task-runner.ts +0 -1
- package/src/tasks/task-store.ts +0 -3
- package/src/tools/background-tool-registry.ts +17 -3
- package/src/tools/document/document-tool.ts +20 -0
- package/src/tools/executor.ts +18 -2
- package/src/tools/host-filesystem/edit.test.ts +151 -0
- package/src/tools/host-filesystem/edit.ts +43 -1
- package/src/tools/host-filesystem/read.test.ts +129 -0
- package/src/tools/host-filesystem/read.ts +43 -1
- package/src/tools/host-filesystem/transfer.test.ts +127 -2
- package/src/tools/host-filesystem/transfer.ts +56 -11
- package/src/tools/host-filesystem/write.test.ts +134 -0
- package/src/tools/host-filesystem/write.ts +43 -1
- package/src/tools/host-terminal/host-shell.ts +13 -6
- package/src/tools/mcp/mcp-tool-factory.ts +2 -1
- package/src/tools/memory/register.test.ts +14 -9
- package/src/tools/memory/register.ts +1 -2
- package/src/tools/permission-checker.ts +15 -0
- package/src/tools/provider-tool-name.ts +28 -0
- package/src/tools/registry.ts +30 -9
- package/src/tools/skills/load.ts +24 -20
- package/src/tools/terminal/shell.ts +9 -1
- package/src/tools/tool-approval-handler.ts +31 -6
- package/src/tools/tool-name-aliases.ts +19 -0
- package/src/tools/types.ts +43 -3
- package/src/tts/provider-catalog.ts +3 -5
- package/src/util/disk-usage.ts +138 -0
- package/src/util/platform.ts +21 -11
- package/src/util/process-liveness.ts +26 -0
- package/src/workspace/heartbeat-service.ts +19 -0
- package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
- package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
- package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +14 -0
- package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
- package/src/workspace/migrations/069-seed-onboarding-threads.ts +28 -0
- package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
- package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
- package/src/workspace/migrations/registry.ts +14 -0
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
- package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
- package/src/memory/v2/skill-qdrant.ts +0 -404
- package/src/signals/bash.ts +0 -198
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
} from "../messaging/providers/slack/render-transcript.js";
|
|
34
34
|
import { getInjectors } from "../plugins/registry.js";
|
|
35
35
|
import type {
|
|
36
|
+
DiskPressureInjectionContext,
|
|
36
37
|
InjectionBlock,
|
|
37
38
|
InjectionPlacement,
|
|
38
39
|
TurnContext,
|
|
@@ -788,6 +789,9 @@ export interface UnifiedTurnContextOptions {
|
|
|
788
789
|
interfaceName?: string;
|
|
789
790
|
channelName?: string;
|
|
790
791
|
actorContext?: InboundActorContext | null;
|
|
792
|
+
configuredUserTimezone?: string | null;
|
|
793
|
+
clientTimezone?: string | null;
|
|
794
|
+
detectedTimezone?: string | null;
|
|
791
795
|
/**
|
|
792
796
|
* Human-readable duration since the previous user message (e.g. "14h ago",
|
|
793
797
|
* "yesterday", "3d ago"). Only populated when the gap exceeds 12 hours so
|
|
@@ -830,6 +834,25 @@ export function buildUnifiedTurnContextBlock(
|
|
|
830
834
|
|
|
831
835
|
const lines: string[] = ["<turn_context>"];
|
|
832
836
|
lines.push(`current_time: ${options.timestamp}`);
|
|
837
|
+
const configuredUserTimezone = options.configuredUserTimezone ?? null;
|
|
838
|
+
const clientDeviceTimezone =
|
|
839
|
+
options.clientTimezone ?? options.detectedTimezone ?? null;
|
|
840
|
+
const hasTimezoneMismatch =
|
|
841
|
+
configuredUserTimezone !== null &&
|
|
842
|
+
clientDeviceTimezone !== null &&
|
|
843
|
+
configuredUserTimezone !== clientDeviceTimezone;
|
|
844
|
+
if (hasTimezoneMismatch) {
|
|
845
|
+
const sanitizedConfiguredTimezone = sanitizeInlineContextValue(
|
|
846
|
+
configuredUserTimezone,
|
|
847
|
+
);
|
|
848
|
+
const sanitizedClientDeviceTimezone =
|
|
849
|
+
sanitizeInlineContextValue(clientDeviceTimezone);
|
|
850
|
+
lines.push(`configured_user_timezone: ${sanitizedConfiguredTimezone}`);
|
|
851
|
+
lines.push(`client_device_timezone: ${sanitizedClientDeviceTimezone}`);
|
|
852
|
+
lines.push(
|
|
853
|
+
`timezone_update_available: after explicit user confirmation, persist client_device_timezone with \`assistant config set ui.userTimezone "${sanitizedClientDeviceTimezone}"\``,
|
|
854
|
+
);
|
|
855
|
+
}
|
|
833
856
|
if (options.timeSinceLastMessage) {
|
|
834
857
|
lines.push(`time_since_last_message: ${options.timeSinceLastMessage}`);
|
|
835
858
|
}
|
|
@@ -1609,6 +1632,7 @@ export function loadSlackActiveThreadFocusBlock(
|
|
|
1609
1632
|
const RUNTIME_INJECTION_PREFIXES = [
|
|
1610
1633
|
"<channel_capabilities>",
|
|
1611
1634
|
"<channel_command_context>",
|
|
1635
|
+
"<disk_pressure_warning>",
|
|
1612
1636
|
"<channel_turn_context>", // backward-compat: strip legacy separate channel blocks
|
|
1613
1637
|
"<guardian_context>",
|
|
1614
1638
|
"<inbound_actor_context>", // backward-compat: strip legacy separate actor blocks
|
|
@@ -1872,6 +1896,7 @@ function applyInjectionBlock(
|
|
|
1872
1896
|
* plugin-overridable default injectors.
|
|
1873
1897
|
*/
|
|
1874
1898
|
export interface RuntimeInjectionOptions {
|
|
1899
|
+
diskPressureContext?: DiskPressureInjectionContext | null;
|
|
1875
1900
|
/**
|
|
1876
1901
|
* Active dashboard-surface context (read from `<active_workspace>`). Kept
|
|
1877
1902
|
* on the options bag rather than an injector because it is a
|
|
@@ -1990,6 +2015,7 @@ function buildTurnInjectionInputs(
|
|
|
1990
2015
|
): TurnInjectionInputs {
|
|
1991
2016
|
return {
|
|
1992
2017
|
mode: options.mode,
|
|
2018
|
+
diskPressureContext: options.diskPressureContext,
|
|
1993
2019
|
workspaceTopLevelContext: options.workspaceTopLevelContext,
|
|
1994
2020
|
unifiedTurnContext: options.unifiedTurnContext,
|
|
1995
2021
|
pkbContext: options.pkbContext,
|
|
@@ -22,7 +22,7 @@ import { RateLimitProvider } from "../providers/ratelimit.js";
|
|
|
22
22
|
import { getProvider } from "../providers/registry.js";
|
|
23
23
|
import { getSubagentManager } from "../subagent/index.js";
|
|
24
24
|
import { getSandboxWorkingDir } from "../util/platform.js";
|
|
25
|
-
import { Conversation
|
|
25
|
+
import { Conversation } from "./conversation.js";
|
|
26
26
|
import type { ConversationEvictor } from "./conversation-evictor.js";
|
|
27
27
|
import type { ConversationCreateOptions } from "./handlers/shared.js";
|
|
28
28
|
import { buildTransportHints } from "./transport-hints.js";
|
|
@@ -180,6 +180,7 @@ function applyTransportMetadata(
|
|
|
180
180
|
if (!transport) return;
|
|
181
181
|
conversation.setTransportHints(buildTransportHints(transport));
|
|
182
182
|
conversation.applyHostEnvFromTransport(transport);
|
|
183
|
+
conversation.applyClientTimezoneFromTransport(transport);
|
|
183
184
|
}
|
|
184
185
|
|
|
185
186
|
/**
|
|
@@ -253,7 +254,6 @@ export async function getOrCreateConversation(
|
|
|
253
254
|
maxTokens,
|
|
254
255
|
sendToClient,
|
|
255
256
|
workingDir,
|
|
256
|
-
DEFAULT_MEMORY_POLICY,
|
|
257
257
|
sharedCesClient,
|
|
258
258
|
storedOptions?.speed,
|
|
259
259
|
undefined,
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
assistantEventHub,
|
|
19
19
|
broadcastMessage,
|
|
20
20
|
} from "../runtime/assistant-event-hub.js";
|
|
21
|
+
import { enforceSameActorOrErrorResult } from "../runtime/auth/same-actor.js";
|
|
21
22
|
import type {
|
|
22
23
|
InteractiveUiRequest,
|
|
23
24
|
InteractiveUiResult,
|
|
@@ -53,6 +54,148 @@ const log = getLogger("conversation-surfaces");
|
|
|
53
54
|
|
|
54
55
|
const MAX_UNDO_DEPTH = 10;
|
|
55
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Debounce window for persisting `ui_surface_update` data back to the
|
|
59
|
+
* message row. Surfaces typically receive bursts of updates (e.g. a
|
|
60
|
+
* Workspace Health Check ticking off items rapidly) — collapsing them
|
|
61
|
+
* to a single DB write avoids hammering SQLite while still bounding the
|
|
62
|
+
* "lost work on crash" window to ~half a second.
|
|
63
|
+
*/
|
|
64
|
+
const SURFACE_PERSIST_DEBOUNCE_MS = 500;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* In-flight debounced persist timers keyed by `surfaceId`. Surface IDs
|
|
68
|
+
* are UUIDs and globally unique, so a module-level map is safe across
|
|
69
|
+
* conversations. Each entry holds the latest data snapshot — newer
|
|
70
|
+
* updates clobber older ones since the persisted row carries the full
|
|
71
|
+
* merged state, not a delta.
|
|
72
|
+
*/
|
|
73
|
+
const pendingSurfacePersists = new Map<
|
|
74
|
+
string,
|
|
75
|
+
{
|
|
76
|
+
timer: ReturnType<typeof setTimeout>;
|
|
77
|
+
conversationId: string;
|
|
78
|
+
data: SurfaceData;
|
|
79
|
+
}
|
|
80
|
+
>();
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Persist the latest `data` for a `ui_surface` content block by
|
|
84
|
+
* scanning the conversation's messages for one containing the given
|
|
85
|
+
* `surfaceId` and patching its `data` field. Mirrors the scan-and-patch
|
|
86
|
+
* pattern in `markSurfaceCompleted`.
|
|
87
|
+
*
|
|
88
|
+
* Safe to call before the assistant message has been persisted (mid-stream):
|
|
89
|
+
* the scan simply finds nothing and bails. The next update after
|
|
90
|
+
* `handleMessageComplete` runs will pick up the now-persisted row.
|
|
91
|
+
*/
|
|
92
|
+
function persistSurfaceData(
|
|
93
|
+
conversationId: string,
|
|
94
|
+
surfaceId: string,
|
|
95
|
+
data: SurfaceData,
|
|
96
|
+
): void {
|
|
97
|
+
try {
|
|
98
|
+
const rows = getMessages(conversationId);
|
|
99
|
+
for (let r = rows.length - 1; r >= 0; r--) {
|
|
100
|
+
let parsed: unknown[];
|
|
101
|
+
try {
|
|
102
|
+
const result = JSON.parse(rows[r].content);
|
|
103
|
+
if (!Array.isArray(result)) continue;
|
|
104
|
+
parsed = result;
|
|
105
|
+
} catch {
|
|
106
|
+
// Plain-text content rows — skip and keep scanning.
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
let found = false;
|
|
110
|
+
for (const pb of parsed) {
|
|
111
|
+
const rb = pb as Record<string, unknown>;
|
|
112
|
+
if (rb.type === "ui_surface" && rb.surfaceId === surfaceId) {
|
|
113
|
+
rb.data = data;
|
|
114
|
+
found = true;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (found) {
|
|
119
|
+
updateMessageContent(rows[r].id, JSON.stringify(parsed));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
log.debug(
|
|
125
|
+
{ err, surfaceId, conversationId },
|
|
126
|
+
"Failed to persist surface data update",
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Schedule a debounced write of the merged surface data back to the
|
|
133
|
+
* persisted message row. Repeated calls within the debounce window
|
|
134
|
+
* collapse to a single write carrying the latest data.
|
|
135
|
+
*/
|
|
136
|
+
export function scheduleSurfaceDataPersist(
|
|
137
|
+
conversationId: string,
|
|
138
|
+
surfaceId: string,
|
|
139
|
+
data: SurfaceData,
|
|
140
|
+
): void {
|
|
141
|
+
const existing = pendingSurfacePersists.get(surfaceId);
|
|
142
|
+
if (existing) {
|
|
143
|
+
clearTimeout(existing.timer);
|
|
144
|
+
}
|
|
145
|
+
const timer = setTimeout(() => {
|
|
146
|
+
pendingSurfacePersists.delete(surfaceId);
|
|
147
|
+
persistSurfaceData(conversationId, surfaceId, data);
|
|
148
|
+
}, SURFACE_PERSIST_DEBOUNCE_MS);
|
|
149
|
+
pendingSurfacePersists.set(surfaceId, { timer, conversationId, data });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Force-flush any pending debounced persist for `surfaceId`. Called on
|
|
154
|
+
* surface completion so the final state is durable before the surface
|
|
155
|
+
* record transitions to `completed`.
|
|
156
|
+
*/
|
|
157
|
+
export function flushSurfaceDataPersist(surfaceId: string): void {
|
|
158
|
+
const pending = pendingSurfacePersists.get(surfaceId);
|
|
159
|
+
if (!pending) return;
|
|
160
|
+
clearTimeout(pending.timer);
|
|
161
|
+
pendingSurfacePersists.delete(surfaceId);
|
|
162
|
+
persistSurfaceData(pending.conversationId, surfaceId, pending.data);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Cancel all pending debounced persists. Called on conversation
|
|
167
|
+
* teardown to avoid timers firing against torn-down state.
|
|
168
|
+
*
|
|
169
|
+
* Use `flushPendingSurfaceDataPersists` instead on a clean shutdown
|
|
170
|
+
* path where the latest in-flight surface state should still be
|
|
171
|
+
* written before teardown.
|
|
172
|
+
*/
|
|
173
|
+
export function cancelPendingSurfaceDataPersists(
|
|
174
|
+
conversationId?: string,
|
|
175
|
+
): void {
|
|
176
|
+
for (const [surfaceId, pending] of pendingSurfacePersists) {
|
|
177
|
+
if (conversationId && pending.conversationId !== conversationId) continue;
|
|
178
|
+
clearTimeout(pending.timer);
|
|
179
|
+
pendingSurfacePersists.delete(surfaceId);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Synchronously flush all pending debounced persists, optionally scoped
|
|
185
|
+
* to a single conversation. Called on clean conversation teardown so an
|
|
186
|
+
* update that arrived inside the 500ms debounce window still lands in
|
|
187
|
+
* the DB before the conversation goes away. Each entry is removed from
|
|
188
|
+
* the pending map after its write fires.
|
|
189
|
+
*/
|
|
190
|
+
export function flushPendingSurfaceDataPersists(conversationId?: string): void {
|
|
191
|
+
for (const [surfaceId, pending] of pendingSurfacePersists) {
|
|
192
|
+
if (conversationId && pending.conversationId !== conversationId) continue;
|
|
193
|
+
clearTimeout(pending.timer);
|
|
194
|
+
pendingSurfacePersists.delete(surfaceId);
|
|
195
|
+
persistSurfaceData(pending.conversationId, surfaceId, pending.data);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
56
199
|
/**
|
|
57
200
|
* Mark a `ui_surface` content block as completed in the database so that
|
|
58
201
|
* history reconstruction preserves the completion state. Also updates
|
|
@@ -63,6 +206,10 @@ export function markSurfaceCompleted(
|
|
|
63
206
|
surfaceId: string,
|
|
64
207
|
summary: string,
|
|
65
208
|
): void {
|
|
209
|
+
// Force-flush any pending debounced data persist so the completion
|
|
210
|
+
// patch lands on top of the latest data instead of racing with it.
|
|
211
|
+
flushSurfaceDataPersist(surfaceId);
|
|
212
|
+
|
|
66
213
|
// Update in-memory messages when available so subsequent reads within
|
|
67
214
|
// this session see the change without waiting for DB.
|
|
68
215
|
if (ctx.messages) {
|
|
@@ -1012,6 +1159,7 @@ export async function handleSurfaceAction(
|
|
|
1012
1159
|
summary,
|
|
1013
1160
|
submittedData: data,
|
|
1014
1161
|
});
|
|
1162
|
+
markSurfaceCompleted(ctx, surfaceId, summary);
|
|
1015
1163
|
|
|
1016
1164
|
// Cleanup and resolve — order matters: cleanup clears the timer
|
|
1017
1165
|
// before resolve() unblocks the caller.
|
|
@@ -1358,20 +1506,6 @@ export async function handleSurfaceAction(
|
|
|
1358
1506
|
surfaceData,
|
|
1359
1507
|
);
|
|
1360
1508
|
|
|
1361
|
-
// Forms are one-shot surfaces — auto-complete immediately so the client
|
|
1362
|
-
// transitions from the "Submitting…" spinner to a completion chip without
|
|
1363
|
-
// requiring the LLM to call ui_dismiss.
|
|
1364
|
-
if (pending.surfaceType === "form") {
|
|
1365
|
-
broadcastMessage({
|
|
1366
|
-
type: "ui_surface_complete",
|
|
1367
|
-
conversationId: ctx.conversationId,
|
|
1368
|
-
surfaceId,
|
|
1369
|
-
summary,
|
|
1370
|
-
submittedData: mergedData,
|
|
1371
|
-
});
|
|
1372
|
-
markSurfaceCompleted(ctx, surfaceId, summary);
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
1509
|
// Extract file attachments from action data so they are sent as proper
|
|
1376
1510
|
// image/file content blocks instead of dumping base64 into the text.
|
|
1377
1511
|
let pendingAttachments: UserMessageAttachment[] = [];
|
|
@@ -1501,6 +1635,21 @@ export async function handleSurfaceAction(
|
|
|
1501
1635
|
return;
|
|
1502
1636
|
}
|
|
1503
1637
|
|
|
1638
|
+
// One-shot interactive surfaces — auto-complete now that the message has
|
|
1639
|
+
// been accepted. Deferred until after rejection check so the surface stays
|
|
1640
|
+
// active and retryable if the queue was full.
|
|
1641
|
+
const ONE_SHOT_SURFACE_TYPES = ["form", "confirmation", "file_upload"];
|
|
1642
|
+
if (ONE_SHOT_SURFACE_TYPES.includes(pending.surfaceType)) {
|
|
1643
|
+
broadcastMessage({
|
|
1644
|
+
type: "ui_surface_complete",
|
|
1645
|
+
conversationId: ctx.conversationId,
|
|
1646
|
+
surfaceId,
|
|
1647
|
+
summary,
|
|
1648
|
+
submittedData: mergedDataForText,
|
|
1649
|
+
});
|
|
1650
|
+
markSurfaceCompleted(ctx, surfaceId, summary);
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1504
1653
|
// One-shot: clear accumulated state now that the message has been accepted.
|
|
1505
1654
|
// Deferred until after rejection check so state is preserved for retry on rejection.
|
|
1506
1655
|
if (accumulatedState && Object.keys(accumulatedState).length > 0) {
|
|
@@ -1783,15 +1932,19 @@ export async function surfaceProxyResolver(
|
|
|
1783
1932
|
// Record the action and proxy to the connected desktop client
|
|
1784
1933
|
const reasoning =
|
|
1785
1934
|
typeof input.reasoning === "string" ? input.reasoning : undefined;
|
|
1786
|
-
|
|
1787
|
-
typeof input.target_client_id === "string" &&
|
|
1935
|
+
let targetClientId: string | undefined =
|
|
1936
|
+
typeof input.target_client_id === "string" &&
|
|
1937
|
+
input.target_client_id !== ""
|
|
1788
1938
|
? input.target_client_id
|
|
1789
1939
|
: undefined;
|
|
1790
1940
|
|
|
1791
|
-
// Validate targetClientId
|
|
1792
|
-
//
|
|
1793
|
-
//
|
|
1794
|
-
//
|
|
1941
|
+
// Validate targetClientId existence, capability, and same-user binding
|
|
1942
|
+
// before recordAction so an invalid or cross-user ID does not burn a
|
|
1943
|
+
// step or pollute action history. HostBashProxy / HostFileProxy
|
|
1944
|
+
// validate at the tool-resolution layer for the same reason. The proxy
|
|
1945
|
+
// re-checks same-user (single authoritative gate); using the shared
|
|
1946
|
+
// helper keeps log payload and error wording identical at both layers.
|
|
1947
|
+
const sourceActorPrincipalId = ctx.trustContext?.guardianPrincipalId;
|
|
1795
1948
|
if (targetClientId != null) {
|
|
1796
1949
|
const client = assistantEventHub.getClientById(targetClientId);
|
|
1797
1950
|
if (!client) {
|
|
@@ -1806,14 +1959,24 @@ export async function surfaceProxyResolver(
|
|
|
1806
1959
|
isError: true,
|
|
1807
1960
|
};
|
|
1808
1961
|
}
|
|
1962
|
+
const rejection = enforceSameActorOrErrorResult({
|
|
1963
|
+
hub: assistantEventHub,
|
|
1964
|
+
sourceActorPrincipalId,
|
|
1965
|
+
targetClientId,
|
|
1966
|
+
op: "host_cu",
|
|
1967
|
+
});
|
|
1968
|
+
if (rejection) return rejection;
|
|
1809
1969
|
}
|
|
1810
1970
|
|
|
1811
|
-
// Guard: require explicit targeting when multiple CU-capable
|
|
1812
|
-
// connected. The tool schemas document target_client_id as
|
|
1813
|
-
// multiple clients support host_cu" but nothing enforced
|
|
1814
|
-
// until now. Without this guard, the request would
|
|
1815
|
-
// capable clients simultaneously, causing the same CU
|
|
1816
|
-
// on multiple machines.
|
|
1971
|
+
// Guard: require explicit targeting when multiple same-user CU-capable
|
|
1972
|
+
// clients are connected. The tool schemas document target_client_id as
|
|
1973
|
+
// "required when multiple clients support host_cu" but nothing enforced
|
|
1974
|
+
// it at runtime until now. Without this guard, the request would
|
|
1975
|
+
// broadcast to all capable clients simultaneously, causing the same CU
|
|
1976
|
+
// action to execute on multiple machines. The filter mirrors
|
|
1977
|
+
// HostFileProxy's auto-resolve: only same-user clients participate, so
|
|
1978
|
+
// a cross-user client connected to the same daemon does not falsely
|
|
1979
|
+
// trigger this ambiguity error.
|
|
1817
1980
|
//
|
|
1818
1981
|
// Asymmetry with host_bash / host_file (host-shell.ts): the bash/file
|
|
1819
1982
|
// guard additionally checks `transportInterface != null &&
|
|
@@ -1824,13 +1987,25 @@ export async function surfaceProxyResolver(
|
|
|
1824
1987
|
// host_cu-capable transport for which auto-routing-to-self would be
|
|
1825
1988
|
// appropriate. We therefore fire whenever there is genuine ambiguity.
|
|
1826
1989
|
if (targetClientId == null) {
|
|
1827
|
-
const
|
|
1828
|
-
|
|
1990
|
+
const allCuClients = assistantEventHub.listClientsByCapability("host_cu");
|
|
1991
|
+
const sameUserCuClients = allCuClients.filter(
|
|
1992
|
+
(c) => c.actorPrincipalId === sourceActorPrincipalId,
|
|
1993
|
+
);
|
|
1994
|
+
if (sameUserCuClients.length > 1) {
|
|
1829
1995
|
return {
|
|
1830
1996
|
content: `Error: multiple clients support host_cu. Specify which client to target with \`target_client_id\`. Run \`assistant clients list --capability host_cu\` to see client IDs and labels.`,
|
|
1831
1997
|
isError: true,
|
|
1832
1998
|
};
|
|
1833
1999
|
}
|
|
2000
|
+
// When cross-user host_cu clients are connected, we MUST auto-resolve
|
|
2001
|
+
// to the unique same-user client (or fail explicitly) — otherwise the
|
|
2002
|
+
// proxy would broadcast untargeted and the CU action would reach the
|
|
2003
|
+
// cross-user client too. Setting targetClientId here forces the proxy
|
|
2004
|
+
// to deliver only to that client, with the same-user check below as
|
|
2005
|
+
// belt-and-suspenders.
|
|
2006
|
+
if (sameUserCuClients.length === 1 && allCuClients.length > 1) {
|
|
2007
|
+
targetClientId = sameUserCuClients[0].clientId;
|
|
2008
|
+
}
|
|
1834
2009
|
}
|
|
1835
2010
|
|
|
1836
2011
|
ctx.hostCuProxy.recordAction(toolName, input, reasoning);
|
|
@@ -1842,6 +2017,7 @@ export async function surfaceProxyResolver(
|
|
|
1842
2017
|
reasoning,
|
|
1843
2018
|
signal,
|
|
1844
2019
|
targetClientId,
|
|
2020
|
+
sourceActorPrincipalId,
|
|
1845
2021
|
);
|
|
1846
2022
|
}
|
|
1847
2023
|
|
|
@@ -1876,7 +2052,7 @@ export async function surfaceProxyResolver(
|
|
|
1876
2052
|
// union on `tool` ("start" | "observe" | "press" | …). The agent's raw
|
|
1877
2053
|
// tool input only carries the action-specific payload (app, x/y, text,
|
|
1878
2054
|
// …) — the discriminator is implied by `toolName` (`app_control_<tool>`).
|
|
1879
|
-
// Inject it here so the proxy's
|
|
2055
|
+
// Inject it here so the proxy's session-lock guard (`input.tool ===
|
|
1880
2056
|
// "start"`) and the Swift client's discriminated-union decoder both see
|
|
1881
2057
|
// the field they require.
|
|
1882
2058
|
const tool = toolName.slice("app_control_".length);
|
|
@@ -2077,6 +2253,12 @@ export async function surfaceProxyResolver(
|
|
|
2077
2253
|
ctx.currentTurnSurfaces[idx].data = mergedData;
|
|
2078
2254
|
}
|
|
2079
2255
|
|
|
2256
|
+
// Persist the merged data back to the assistant message's
|
|
2257
|
+
// `ui_surface` content block so a refresh / restart shows the
|
|
2258
|
+
// current state instead of the original creation-time snapshot.
|
|
2259
|
+
// Debounced to coalesce bursts of rapid updates.
|
|
2260
|
+
scheduleSurfaceDataPersist(ctx.conversationId, surfaceId, mergedData);
|
|
2261
|
+
|
|
2080
2262
|
return { content: "Surface updated", isError: false };
|
|
2081
2263
|
}
|
|
2082
2264
|
|
|
@@ -30,12 +30,14 @@ import {
|
|
|
30
30
|
ACTIVITY_SKIP_SET,
|
|
31
31
|
injectActivityField,
|
|
32
32
|
} from "../tools/schema-transforms.js";
|
|
33
|
-
import
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
import { resolveToolNameAlias } from "../tools/tool-name-aliases.js";
|
|
34
|
+
import {
|
|
35
|
+
isDiskPressureCleanupToolName,
|
|
36
|
+
type ProxyApprovalCallback,
|
|
37
|
+
type ProxyApprovalRequest,
|
|
38
|
+
type ToolContext,
|
|
39
|
+
type ToolExecutionResult,
|
|
40
|
+
type ToolLifecycleEventHandler,
|
|
39
41
|
} from "../tools/types.js";
|
|
40
42
|
import { allUiSurfaceTools } from "../tools/ui-surface/definitions.js";
|
|
41
43
|
import { getLogger } from "../util/logger.js";
|
|
@@ -121,7 +123,9 @@ export function createToolExecutor(
|
|
|
121
123
|
toolUseId?: string,
|
|
122
124
|
turnContext?: import("../plugins/types.js").TurnContext,
|
|
123
125
|
) => {
|
|
124
|
-
|
|
126
|
+
const executionName = resolveToolNameAlias(name, ctx.allowedToolNames);
|
|
127
|
+
|
|
128
|
+
if (isDoordashCommand(executionName, input)) {
|
|
125
129
|
markDoordashStepInProgress(ctx, input);
|
|
126
130
|
}
|
|
127
131
|
|
|
@@ -135,6 +139,7 @@ export function createToolExecutor(
|
|
|
135
139
|
taskRunId: ctx.taskRunId,
|
|
136
140
|
trustClass: resolveTrustClass(ctx.trustContext),
|
|
137
141
|
executionChannel: ctx.trustContext?.sourceChannel,
|
|
142
|
+
sourceActorPrincipalId: ctx.trustContext?.guardianPrincipalId,
|
|
138
143
|
callSessionId: ctx.callSessionId,
|
|
139
144
|
triggeredBySurfaceAction:
|
|
140
145
|
ctx.surfaceActionRequestIds?.has(ctx.currentRequestId ?? "") ?? false,
|
|
@@ -151,7 +156,7 @@ export function createToolExecutor(
|
|
|
151
156
|
onOutput,
|
|
152
157
|
signal: ctx.abortController?.signal,
|
|
153
158
|
allowedToolNames: ctx.allowedToolNames,
|
|
154
|
-
|
|
159
|
+
diskPressureCleanupModeActive: ctx.diskPressureCleanupModeActive,
|
|
155
160
|
toolUseId,
|
|
156
161
|
isPlatformHosted: getIsPlatform(),
|
|
157
162
|
cesClient: ctx.cesClient,
|
|
@@ -206,8 +211,9 @@ export function createToolExecutor(
|
|
|
206
211
|
// route through the full executor pipeline so the underlying tool's
|
|
207
212
|
// risk level, permission checks, hooks, and lifecycle events all fire
|
|
208
213
|
// with the real tool name.
|
|
209
|
-
if (
|
|
210
|
-
const
|
|
214
|
+
if (executionName === "skill_execute") {
|
|
215
|
+
const rawToolName = typeof input.tool === "string" ? input.tool : "";
|
|
216
|
+
const toolName = resolveToolNameAlias(rawToolName, ctx.allowedToolNames);
|
|
211
217
|
const rawToolInput =
|
|
212
218
|
input.input != null && typeof input.input === "object"
|
|
213
219
|
? (input.input as Record<string, unknown>)
|
|
@@ -240,7 +246,7 @@ export function createToolExecutor(
|
|
|
240
246
|
}
|
|
241
247
|
|
|
242
248
|
const result = await executor.execute(
|
|
243
|
-
|
|
249
|
+
executionName,
|
|
244
250
|
input,
|
|
245
251
|
toolContext,
|
|
246
252
|
turnContext,
|
|
@@ -249,7 +255,7 @@ export function createToolExecutor(
|
|
|
249
255
|
ctx.approvedViaPromptThisTurn = true;
|
|
250
256
|
}
|
|
251
257
|
|
|
252
|
-
runPostExecutionSideEffects(
|
|
258
|
+
runPostExecutionSideEffects(executionName, input, result, { ctx });
|
|
253
259
|
|
|
254
260
|
return result;
|
|
255
261
|
};
|
|
@@ -305,6 +311,8 @@ export interface SkillProjectionContext {
|
|
|
305
311
|
readonly hasNoClient?: boolean;
|
|
306
312
|
/** When set, only tools in this set are included in the resolved tool list (subagent delegation). */
|
|
307
313
|
subagentAllowedTools?: Set<string>;
|
|
314
|
+
/** True when the current turn is restricted to disk-pressure cleanup-safe tools. */
|
|
315
|
+
diskPressureCleanupModeActive?: boolean;
|
|
308
316
|
/** True when this conversation belongs to a subagent spawned by SubagentManager. */
|
|
309
317
|
readonly isSubagent?: boolean;
|
|
310
318
|
/**
|
|
@@ -351,6 +359,29 @@ export const HOST_TOOL_TO_CAPABILITY = new Map<string, HostProxyCapability>([
|
|
|
351
359
|
// Derived from HOST_TOOL_TO_CAPABILITY so the invariant "every host tool has
|
|
352
360
|
// a capability mapping" is a structural fact — no runtime assertion needed.
|
|
353
361
|
export const HOST_TOOL_NAMES = new Set(HOST_TOOL_TO_CAPABILITY.keys());
|
|
362
|
+
/**
|
|
363
|
+
* Capabilities eligible for cross-client exposure on non-host-proxy
|
|
364
|
+
* transports (e.g. web, ios routing to a connected macOS client).
|
|
365
|
+
* Adding a capability here exposes ALL tools that map to it (per
|
|
366
|
+
* HOST_TOOL_TO_CAPABILITY) on non-host-proxy transports — the daemon then
|
|
367
|
+
* routes the actual invocation to the connected capable client via the
|
|
368
|
+
* proxy's targetClientId path.
|
|
369
|
+
*
|
|
370
|
+
* Inclusions:
|
|
371
|
+
* - host_bash (Phase 1, PR #29322)
|
|
372
|
+
* - host_file (Phases 2 & 3, PRs #29398 + #29440)
|
|
373
|
+
*
|
|
374
|
+
* Exclusions:
|
|
375
|
+
* - host_browser: chrome-extension is its own executor; web turns don't
|
|
376
|
+
* have a CDP target model. Re-evaluate when host browser via macOS
|
|
377
|
+
* host proxy ships (PR #27489).
|
|
378
|
+
* - host_app_control, host_cu: not in HOST_TOOL_TO_CAPABILITY
|
|
379
|
+
* (skill-routed).
|
|
380
|
+
*/
|
|
381
|
+
const CROSS_CLIENT_EXPOSED_CAPABILITIES = new Set<HostProxyCapability>([
|
|
382
|
+
"host_bash",
|
|
383
|
+
"host_file",
|
|
384
|
+
]);
|
|
354
385
|
const CLIENT_CAPABILITY_TOOL_NAMES = new Set(["app_open"]);
|
|
355
386
|
const PLATFORM_TOOL_NAMES = new Set(["request_system_permission"]);
|
|
356
387
|
|
|
@@ -385,16 +416,22 @@ export function isToolActiveForContext(
|
|
|
385
416
|
// Per-capability check is authoritative for structural support: if the
|
|
386
417
|
// transport cannot service this capability, the tool is filtered out.
|
|
387
418
|
if (transport && capability && !supportsHostProxy(transport, capability)) {
|
|
388
|
-
// Cross-client exception: allow
|
|
389
|
-
//
|
|
390
|
-
//
|
|
391
|
-
//
|
|
392
|
-
//
|
|
419
|
+
// Cross-client exception: allow host tools whose capabilities have
|
|
420
|
+
// cross-client routing infrastructure (Phases 1–3) to be exposed for
|
|
421
|
+
// non-host-proxy transports (e.g. "web", "ios") when at least one
|
|
422
|
+
// capable client is connected via the event hub. Members of
|
|
423
|
+
// CROSS_CLIENT_EXPOSED_CAPABILITIES (host_bash, host_file) qualify;
|
|
424
|
+
// host_browser is intentionally excluded (chrome-extension is its
|
|
425
|
+
// own executor and web turns don't have a CDP target model).
|
|
426
|
+
// chrome-extension transport is excluded as a security boundary
|
|
427
|
+
// (extension only gets host_browser); hasNoClient turns are excluded
|
|
428
|
+
// (no interactive approval UI available).
|
|
393
429
|
if (
|
|
394
|
-
capability
|
|
430
|
+
capability &&
|
|
431
|
+
CROSS_CLIENT_EXPOSED_CAPABILITIES.has(capability) &&
|
|
395
432
|
transport !== "chrome-extension" &&
|
|
396
433
|
!ctx.hasNoClient &&
|
|
397
|
-
assistantEventHub.listClientsByCapability(
|
|
434
|
+
assistantEventHub.listClientsByCapability(capability).length > 0
|
|
398
435
|
) {
|
|
399
436
|
return true;
|
|
400
437
|
}
|
|
@@ -516,6 +553,16 @@ export function createResolveToolsCallback(
|
|
|
516
553
|
}
|
|
517
554
|
turnAllowed.add(name);
|
|
518
555
|
}
|
|
556
|
+
if (ctx.diskPressureCleanupModeActive === true) {
|
|
557
|
+
const cleanupDefs = allBaseDefs.filter((d) =>
|
|
558
|
+
isDiskPressureCleanupToolName(d.name),
|
|
559
|
+
);
|
|
560
|
+
ctx.allowedToolNames = new Set(
|
|
561
|
+
Array.from(turnAllowed).filter(isDiskPressureCleanupToolName),
|
|
562
|
+
);
|
|
563
|
+
return injectActivityField(cleanupDefs, ACTIVITY_SKIP_SET);
|
|
564
|
+
}
|
|
565
|
+
|
|
519
566
|
ctx.allowedToolNames = turnAllowed;
|
|
520
567
|
return injectActivityField(allBaseDefs, ACTIVITY_SKIP_SET);
|
|
521
568
|
};
|