@vellumai/assistant 0.10.4 → 0.10.5-dev.202607022329.d89bf2f
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 +94 -91
- package/Dockerfile +5 -16
- package/README.md +7 -10
- package/bun.lock +0 -4
- package/docs/architecture/security.md +20 -20
- package/docs/stt-provider-onboarding.md +4 -5
- package/docs/trusted-contact-access.md +7 -7
- package/eslint-rules/cli-no-daemon-internals.js +4 -0
- package/eslint.config.mjs +7 -1
- package/knip.json +0 -1
- package/node_modules/@vellumai/gateway-client/src/__tests__/contact-read-contracts.test.ts +36 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/invite-contract.test.ts +233 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +40 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +20 -8
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +33 -0
- package/node_modules/@vellumai/gateway-client/src/invite-contract.ts +216 -0
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +91 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +39 -3
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +0 -8
- package/node_modules/@vellumai/service-contracts/src/__tests__/package-boundary.test.ts +1 -5
- package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +3 -14
- package/openapi.yaml +1073 -225
- package/package.json +1 -4
- package/scripts/test.sh +35 -13
- package/src/__tests__/access-request-seed-content-blocks.test.ts +60 -4
- package/src/__tests__/acp-session.test.ts +4 -1
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +0 -6
- package/src/__tests__/add-message-preassigned-id.test.ts +56 -0
- package/src/__tests__/agent-loop-set-system-prompt.test.ts +139 -0
- package/src/__tests__/app-conversation-ids-backfill.test.ts +3 -0
- package/src/__tests__/app-source-watcher.test.ts +11 -0
- package/src/__tests__/approval-interception-trust-gates.test.ts +148 -3
- package/src/__tests__/assistant-attachments.test.ts +62 -4
- package/src/__tests__/assistant-id-boundary-guard.test.ts +0 -1
- package/src/__tests__/attachments-store-heic-normalize.test.ts +230 -0
- package/src/__tests__/attachments-store.test.ts +3 -0
- package/src/__tests__/background-shell-bash.test.ts +34 -0
- package/src/__tests__/background-shell-host-bash.test.ts +12 -0
- package/src/__tests__/background-tool-registry.test.ts +97 -0
- package/src/__tests__/background-tool-routes.test.ts +72 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/btw-routes.test.ts +57 -3
- package/src/__tests__/call-controller.test.ts +474 -84
- package/src/__tests__/call-domain.test.ts +48 -1
- package/src/__tests__/call-pointer-messages.test.ts +57 -19
- package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +2 -6
- package/src/__tests__/call-routes-http.test.ts +15 -14
- package/src/__tests__/call-setup-flow-invite.test.ts +568 -0
- package/src/__tests__/call-setup-flow-midcall-trust.test.ts +231 -0
- package/src/__tests__/call-setup-flow-name-capture.test.ts +754 -0
- package/src/__tests__/call-setup-flow-verification.test.ts +925 -0
- package/src/__tests__/call-setup-flow.test.ts +311 -0
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -1
- package/src/__tests__/channel-guardian.test.ts +4 -7
- package/src/__tests__/channel-readiness-service.test.ts +0 -6
- package/src/__tests__/channel-readiness-slack-remote.test.ts +0 -4
- package/src/__tests__/channel-retry-sweep.test.ts +76 -64
- package/src/__tests__/channel-setup-panel-ack.test.ts +184 -0
- package/src/__tests__/clear-all-lexical.test.ts +90 -0
- package/src/__tests__/compactor-media-strip.test.ts +177 -0
- package/src/__tests__/config-loader-backfill.test.ts +5 -5
- package/src/__tests__/config-schema.test.ts +17 -0
- package/src/__tests__/contact-store-interaction-info.test.ts +27 -70
- package/src/__tests__/contacts-relay-reads.test.ts +188 -23
- package/src/__tests__/contacts-tools.test.ts +42 -1
- package/src/__tests__/contacts-write.test.ts +181 -0
- package/src/__tests__/context-search-conversations-source.test.ts +220 -81
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +39 -0
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -1
- package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -0
- package/src/__tests__/conversation-clear-safety.test.ts +0 -1
- package/src/__tests__/conversation-crud-enabled-plugins.test.ts +60 -0
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -1
- package/src/__tests__/conversation-disk-view.test.ts +1 -0
- package/src/__tests__/conversation-enabledplugins-route.test.ts +184 -0
- package/src/__tests__/conversation-error.test.ts +0 -18
- package/src/__tests__/conversation-fork-crud.test.ts +67 -0
- package/src/__tests__/conversation-history-web-search.test.ts +7 -137
- package/src/__tests__/conversation-initial-prompt.test.ts +110 -0
- package/src/__tests__/conversation-loop-db-lock-resilience.test.ts +191 -0
- package/src/__tests__/conversation-process-callsite.test.ts +37 -0
- package/src/__tests__/conversation-queue.test.ts +149 -0
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +392 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +84 -0
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +407 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +36 -16
- package/src/__tests__/conversation-skill-tools.test.ts +73 -0
- package/src/__tests__/conversation-wipe.test.ts +20 -5
- package/src/__tests__/credential-prompt-route.test.ts +69 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -3
- package/src/__tests__/db-drop-messages-fts.test.ts +115 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -62
- package/src/__tests__/edit-propagation.test.ts +56 -2
- package/src/__tests__/empty-state-greeting-cache.test.ts +9 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +15 -110
- package/src/__tests__/guardian-expiry-notifier.test.ts +1 -0
- package/src/__tests__/guardian-question-mode.test.ts +57 -0
- package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -1
- package/src/__tests__/guardian-wait-controller.test.ts +619 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +33 -2
- package/src/__tests__/heic-fixture.ts +55 -0
- package/src/__tests__/helpers/seed-contact-channel.ts +0 -2
- package/src/__tests__/http-conversation-lineage.test.ts +6 -1
- package/src/__tests__/image-conversion.test.ts +170 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +72 -341
- package/src/__tests__/introduction-card-resolver.test.ts +354 -0
- package/src/__tests__/invite-redeemed-ipc.test.ts +97 -0
- package/src/__tests__/invite-routes-http.test.ts +246 -420
- package/src/__tests__/job-handler-registration-guard.test.ts +104 -0
- package/src/__tests__/lexical-index-dual-write.test.ts +426 -0
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +2 -2
- package/src/__tests__/list-messages-attachments.test.ts +159 -0
- package/src/__tests__/list-messages-background-tool-completion.test.ts +141 -0
- package/src/__tests__/list-messages-queued.test.ts +25 -0
- package/src/__tests__/managed-store.test.ts +129 -1
- package/src/__tests__/media-stream-audio-transcode.test.ts +113 -0
- package/src/__tests__/media-stream-output.test.ts +350 -19
- package/src/__tests__/media-stream-server-integration.test.ts +943 -169
- package/src/__tests__/media-stream-stt-session.test.ts +498 -1
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +2 -2
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +19 -0
- package/src/__tests__/non-member-access-request.test.ts +349 -3
- package/src/__tests__/notification-decision-fallback.test.ts +104 -8
- package/src/__tests__/notification-decision-strategy.test.ts +57 -8
- package/src/__tests__/onboarding-template-contract.test.ts +27 -43
- package/src/__tests__/openai-provider.test.ts +33 -0
- package/src/__tests__/openai-responses-provider.test.ts +40 -0
- package/src/__tests__/persistence-layering-guard.test.ts +29 -20
- package/src/__tests__/persona-resolver.test.ts +56 -2
- package/src/__tests__/personal-memory-auth-bypass.test.ts +62 -0
- package/src/__tests__/phone.test.ts +1 -15
- package/src/__tests__/plugin-bootstrap.test.ts +25 -196
- package/src/__tests__/plugin-disabled-state.test.ts +33 -0
- package/src/__tests__/plugin-effective-enabled-set.test.ts +227 -0
- package/src/__tests__/plugin-event-hub-facade.test.ts +323 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +156 -42
- package/src/__tests__/plugin-injector-contribution.test.ts +4 -20
- package/src/__tests__/plugin-pipeline.test.ts +63 -1
- package/src/__tests__/plugin-route-contribution.test.ts +62 -115
- package/src/__tests__/plugin-tool-contribution.test.ts +0 -22
- package/src/__tests__/plugin-types.test.ts +0 -1
- package/src/__tests__/pointer-conversation-trust.test.ts +38 -6
- package/src/__tests__/pre-model-call-sanitize.test.ts +1 -1
- package/src/__tests__/prune-old-tool-invocations-job.test.ts +97 -0
- package/src/__tests__/public-ingress-urls.test.ts +0 -48
- package/src/__tests__/qdrant-collection-migration.test.ts +145 -2
- package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +0 -1
- package/src/__tests__/resolve-trust-class.test.ts +28 -10
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +137 -0
- package/src/__tests__/schedule-retry.test.ts +38 -14
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +3 -3
- package/src/__tests__/schedule-routes.test.ts +14 -6
- package/src/__tests__/schedule-store.test.ts +3 -1
- package/src/__tests__/scheduler-disk-pressure.test.ts +17 -6
- package/src/__tests__/scheduler-recurrence.test.ts +57 -107
- package/src/__tests__/scheduler-reuse-conversation.test.ts +51 -46
- package/src/__tests__/scheduler-wake.test.ts +13 -23
- package/src/__tests__/scheduler-worker-standdown.test.ts +220 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/shell-observability.test.ts +1 -0
- package/src/__tests__/skill-boundary-guard.test.ts +3 -4
- package/src/__tests__/skill-load-plugin-scope.test.ts +238 -0
- package/src/__tests__/skill-projection-feature-flag.test.ts +2 -0
- package/src/__tests__/skill-script-runner-host.test.ts +53 -0
- package/src/__tests__/skill-script-runner-sandbox.test.ts +5 -2
- package/src/__tests__/skill-tool-factory.test.ts +73 -1
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -14
- package/src/__tests__/slack-inbound-verification.test.ts +36 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +89 -2
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +51 -1
- package/src/__tests__/stt-hints.test.ts +2 -256
- package/src/__tests__/subagent-allowlist-validation.test.ts +47 -0
- package/src/__tests__/subagent-call-site-routing.test.ts +72 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +14 -2
- package/src/__tests__/subagent-persistence.test.ts +122 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
- package/src/__tests__/subagent-terminal-message.test.ts +99 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -1
- package/src/__tests__/system-prompt.test.ts +17 -118
- package/src/__tests__/task-scheduler.test.ts +68 -89
- package/src/__tests__/telephony-credential-preflight.test.ts +312 -0
- package/src/__tests__/telephony-tts-guard.test.ts +538 -0
- package/src/__tests__/title-generate-hook.test.ts +16 -0
- package/src/__tests__/tool-approval-handler.test.ts +87 -1
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +45 -20
- package/src/__tests__/tool-grant-request-escalation.test.ts +39 -0
- package/src/__tests__/trust-context-guards.test.ts +21 -10
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -0
- package/src/__tests__/twilio-provider.test.ts +17 -17
- package/src/__tests__/twilio-routes-twiml.test.ts +6 -543
- package/src/__tests__/twilio-routes.test.ts +257 -387
- package/src/__tests__/twilio-validation.test.ts +1 -1
- package/src/__tests__/unified-turn-context-client-os.test.ts +45 -0
- package/src/__tests__/usage-cache-backfill-migration.test.ts +5 -0
- package/src/__tests__/voice-approval-resolver-relay.test.ts +150 -0
- package/src/__tests__/voice-ingress-preflight.test.ts +61 -0
- package/src/__tests__/voice-quality.test.ts +2 -2
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -7
- package/src/__tests__/voice-session-bridge-processing-wait.test.ts +27 -0
- package/src/__tests__/voice-session-bridge.test.ts +1 -10
- package/src/__tests__/workspace-git-service.test.ts +1 -1
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -0
- package/src/__tests__/workspace-migration-118-seed-now-md.test.ts +120 -0
- package/src/__tests__/workspace-migration-120-revise-onboarding-threads.test.ts +175 -0
- package/src/__tests__/workspace-migration-121-seed-default-user-guardrails.test.ts +118 -0
- package/src/__tests__/workspace-migration-122-relocate-default-user-boundary.test.ts +119 -0
- package/src/__tests__/workspace-migration-123-swap-quality-profile-to-fable.test.ts +191 -0
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +235 -241
- package/src/acp/__tests__/prepare-agent-env.test.ts +0 -97
- package/src/acp/__tests__/session-manager-persistence.test.ts +2 -0
- package/src/acp/__tests__/session-manager-resume.test.ts +8 -0
- package/src/acp/__tests__/session-manager-usage.test.ts +6 -0
- package/src/acp/__tests__/session-manager.test.ts +6 -0
- package/src/acp/agent-process.test.ts +116 -0
- package/src/acp/agent-process.ts +71 -0
- package/src/acp/auto-install.ts +12 -8
- package/src/acp/failure-error.test.ts +109 -0
- package/src/acp/failure-error.ts +111 -0
- package/src/acp/prepare-agent-env.ts +0 -12
- package/src/acp/resolve-agent.test.ts +6 -45
- package/src/acp/resolve-agent.ts +2 -4
- package/src/acp/session-manager.test.ts +312 -0
- package/src/acp/session-manager.ts +102 -46
- package/src/agent/ax-tree-compaction.test.ts +4 -1
- package/src/agent/image-optimize.ts +17 -127
- package/src/agent/loop.ts +45 -192
- package/src/api/events/background-tool-completed.test.ts +51 -0
- package/src/api/events/background-tool-completed.ts +29 -0
- package/src/api/events/background-tool-started.test.ts +36 -0
- package/src/api/events/background-tool-started.ts +28 -0
- package/src/api/events/conversation-error.ts +0 -1
- package/src/api/events/open-panel.ts +33 -0
- package/src/api/events/subagent-status-changed.ts +4 -1
- package/src/api/index.ts +19 -0
- package/src/api/responses/conversation-message.ts +34 -0
- package/src/approvals/guardian-decision-primitive.ts +74 -24
- package/src/approvals/guardian-request-resolvers.ts +450 -122
- package/src/apps/app-store.ts +1 -0
- package/src/apps/shared-app-links-store.ts +1 -0
- package/src/avatar/identity-avatar.ts +3 -7
- package/src/background-wake/wake-intent-hooks.test.ts +1 -1
- package/src/calls/__tests__/fish-audio-client.test.ts +73 -0
- package/src/calls/__tests__/gateway-invite-reader.test.ts +170 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +126 -102
- package/src/calls/call-controller.ts +134 -48
- package/src/calls/call-domain.ts +18 -42
- package/src/calls/call-pointer-messages.ts +51 -67
- package/src/calls/call-setup-flow-types.ts +76 -0
- package/src/calls/call-setup-flow.ts +1743 -0
- package/src/calls/call-speech-output.ts +87 -23
- package/src/calls/call-store.ts +4 -0
- package/src/calls/call-transport.ts +22 -36
- package/src/calls/fish-audio-client.ts +3 -0
- package/src/calls/gateway-invite-reader.ts +98 -0
- package/src/calls/guardian-wait-controller.ts +451 -0
- package/src/calls/media-stream-audio-transcode.ts +82 -2
- package/src/calls/media-stream-output.ts +142 -36
- package/src/calls/media-stream-server.ts +297 -178
- package/src/calls/media-stream-stt-session.ts +393 -50
- package/src/calls/media-turn-detector.ts +4 -6
- package/src/calls/relay-access-wait.ts +3 -4
- package/src/calls/relay-setup-router.ts +22 -43
- package/src/calls/relay-verification.ts +17 -24
- package/src/calls/resolve-call-tts-provider.ts +96 -29
- package/src/calls/stt-hints.ts +24 -87
- package/src/calls/telephony-credential-preflight.ts +159 -0
- package/src/calls/telephony-tts-capability.ts +183 -0
- package/src/calls/tts-call-strategy.ts +18 -20
- package/src/calls/twilio-provider.ts +3 -2
- package/src/calls/twilio-routes.ts +47 -351
- package/src/calls/types.ts +5 -3
- package/src/calls/voice-ingress-preflight.ts +9 -0
- package/src/calls/voice-quality.ts +13 -18
- package/src/calls/voice-session-bridge.ts +25 -35
- package/src/channels/__tests__/types.test.ts +28 -0
- package/src/channels/channel-verification-sessions.ts +7 -2
- package/src/channels/config.ts +0 -42
- package/src/channels/slack-thread-store.ts +12 -0
- package/src/channels/types.ts +23 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -1
- package/src/cli/commands/__tests__/schedules.test.ts +83 -34
- package/src/cli/commands/__tests__/status.test.ts +42 -2
- package/src/cli/commands/contacts.ts +2 -2
- package/src/cli/commands/credentials.ts +17 -0
- package/src/cli/commands/monitoring.ts +191 -0
- package/src/cli/commands/schedules-worker.ts +175 -0
- package/src/cli/commands/schedules.ts +169 -43
- package/src/cli/commands/status.ts +12 -1
- package/src/cli/commands/tools.ts +5 -1
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +1 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +29 -1
- package/src/cli/lib/__tests__/toggle-plugin.test.ts +7 -0
- package/src/cli/lib/search-plugins.ts +6 -0
- package/src/cli/lib/toggle-plugin.ts +21 -2
- package/src/cli/program.ts +3 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +23 -0
- package/src/config/acp-defaults.test.ts +0 -10
- package/src/config/acp-defaults.ts +0 -6
- package/src/config/bundled-skills/acp/SKILL.md +13 -45
- package/src/config/bundled-skills/acp/TOOLS.json +3 -3
- package/src/config/bundled-skills/app-builder/SKILL.md +3 -3
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +9 -9
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +1 -1
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -9
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +10 -7
- package/src/config/bundled-skills/skill-management/TOOLS.json +10 -0
- package/src/config/call-site-defaults.ts +0 -2
- package/src/config/feature-flag-registry.json +16 -8
- package/src/config/loader.ts +11 -0
- package/src/config/schema.ts +9 -1
- package/src/config/schemas/__tests__/memory-v2.test.ts +2 -2
- package/src/config/schemas/__tests__/memory-v3.test.ts +64 -0
- package/src/config/schemas/call-site-catalog.ts +0 -12
- package/src/config/schemas/calls.ts +8 -0
- package/src/config/schemas/elevenlabs.ts +1 -1
- package/src/config/schemas/llm.ts +0 -2
- package/src/config/schemas/memory-v2.ts +1 -1
- package/src/config/schemas/memory-v3.ts +82 -0
- package/src/config/schemas/monitoring.ts +61 -0
- package/src/config/schemas/schedules.ts +35 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills-plugin-filter.test.ts +96 -0
- package/src/config/skills.ts +26 -0
- package/src/contacts/__tests__/member-write-relay.test.ts +156 -4
- package/src/contacts/canonical-guardian-store.ts +5 -0
- package/src/contacts/contact-store.ts +81 -35
- package/src/contacts/contacts-write.ts +50 -25
- package/src/contacts/member-write-relay.ts +115 -31
- package/src/contacts/types.ts +0 -8
- package/src/context/compactor.ts +29 -15
- package/src/context/outbound-sanitize.ts +202 -0
- package/src/daemon/__tests__/abort-null-controller.test.ts +167 -0
- package/src/daemon/__tests__/conversation-tool-setup-plugin-scope.test.ts +122 -0
- package/src/daemon/__tests__/embedding-reconcile.test.ts +99 -16
- package/src/daemon/__tests__/wake-conversation-ops-completion.test.ts +107 -0
- package/src/daemon/abort-watchdog.ts +7 -0
- package/src/daemon/app-source-watcher.ts +9 -0
- package/src/daemon/assistant-attachments.ts +42 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +256 -147
- package/src/daemon/conversation-agent-loop.ts +61 -68
- package/src/daemon/conversation-error.ts +1 -15
- package/src/daemon/conversation-history.ts +32 -236
- package/src/daemon/conversation-initial-prompt.ts +77 -0
- package/src/daemon/conversation-lifecycle.ts +25 -1
- package/src/daemon/conversation-messaging.ts +2 -2
- package/src/daemon/conversation-plugin-scope.ts +38 -0
- package/src/daemon/conversation-process.ts +50 -14
- package/src/daemon/conversation-runtime-assembly.ts +36 -6
- package/src/daemon/conversation-skill-tools.ts +19 -3
- package/src/daemon/conversation-store.ts +3 -3
- package/src/daemon/conversation-surfaces.ts +171 -0
- package/src/daemon/conversation-tool-setup.test.ts +95 -0
- package/src/daemon/conversation-tool-setup.ts +116 -1
- package/src/daemon/conversation-turn-finalize.ts +211 -0
- package/src/daemon/conversation.ts +92 -1
- package/src/daemon/disk-pressure-policy.ts +2 -1
- package/src/daemon/event-loop-watchdog.ts +22 -6
- package/src/daemon/external-plugins-bootstrap.ts +47 -134
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +1 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +26 -14
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +27 -14
- package/src/daemon/handlers/__tests__/config-channels.test.ts +0 -5
- package/src/daemon/handlers/config-a2a.ts +7 -14
- package/src/daemon/handlers/config-channels.ts +2 -10
- package/src/daemon/handlers/conversation-history.ts +3 -1
- package/src/daemon/handlers/conversations.ts +0 -52
- package/src/daemon/handlers/shared.ts +4 -24
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +59 -472
- package/src/daemon/message-protocol.ts +3 -3
- package/src/daemon/message-provenance.ts +12 -14
- package/src/daemon/message-types/background-tools.ts +28 -0
- package/src/daemon/message-types/contacts.ts +2 -10
- package/src/daemon/message-types/conversations.ts +10 -0
- package/src/daemon/message-types/integrations.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pointer-conversation-trust.ts +23 -9
- package/src/daemon/pointer-turn-runner.ts +174 -0
- package/src/daemon/providers-setup.ts +16 -0
- package/src/daemon/shutdown-handlers.ts +139 -47
- package/src/daemon/skill-memory-refresh.ts +2 -2
- package/src/daemon/sqlite-corruption-watchdog.test.ts +217 -0
- package/src/daemon/sqlite-corruption-watchdog.ts +155 -0
- package/src/daemon/tool-setup-types.ts +8 -0
- package/src/daemon/trust-context.ts +21 -6
- package/src/daemon/turn-latency-tracker.test.ts +93 -0
- package/src/daemon/turn-latency-tracker.ts +38 -0
- package/src/daemon/wake-conversation-ops.ts +10 -8
- package/src/documents/document-comments-store.test.ts +7 -1
- package/src/documents/document-comments-store.ts +8 -0
- package/src/documents/document-store.ts +15 -0
- package/src/export/__tests__/transcript-formatter.test.ts +11 -0
- package/src/export/transcript-formatter.ts +49 -67
- package/src/home/__tests__/feed-writer.test.ts +247 -1
- package/src/home/conversation-starter-checkpoints.ts +1 -0
- package/src/home/feed-writer.ts +61 -0
- package/src/home/job-handlers/conversation-starters.ts +1 -0
- package/src/hooks/hook-loader.ts +12 -0
- package/src/hooks/registry.ts +32 -4
- package/src/inbound/public-ingress-urls.ts +0 -17
- package/src/ipc/__tests__/contacts-info-ipc.test.ts +285 -0
- package/src/ipc/assistant-server.ts +28 -7
- package/src/ipc/routes/__tests__/contacts-mirror-apply-atomicity.test.ts +62 -0
- package/src/ipc/routes/__tests__/contacts-mirror-apply.test.ts +272 -0
- package/src/ipc/routes/__tests__/contacts-mirror-ipc-routes.test.ts +322 -0
- package/src/ipc/routes/__tests__/invite-ipc-routes.test.ts +60 -4
- package/src/ipc/routes/contacts-info-ipc-routes.ts +239 -0
- package/src/ipc/routes/contacts-mirror-ipc-routes.ts +202 -0
- package/src/ipc/routes/db-proxy.ts +5 -2
- package/src/ipc/routes/invite-ipc-routes.ts +53 -39
- package/src/jobs/worker.ts +9 -2
- package/src/live-voice/__tests__/live-voice-archive.test.ts +10 -1
- package/src/live-voice/live-voice-archive.ts +5 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +22 -6
- package/src/messaging/providers/channel-transport.ts +6 -0
- package/src/messaging/providers/index.ts +3 -0
- package/src/messaging/providers/slack/api.test.ts +49 -1
- package/src/messaging/providers/slack/api.ts +110 -0
- package/src/messaging/providers/slack/send.test.ts +85 -0
- package/src/messaging/providers/slack/send.ts +100 -28
- package/src/messaging/providers/slack/transport.ts +7 -13
- package/src/messaging/providers/whatsapp/send.ts +29 -9
- package/src/monitoring/__tests__/control.test.ts +235 -0
- package/src/monitoring/__tests__/sample-ring-buffer.test.ts +96 -0
- package/src/monitoring/control.ts +105 -0
- package/src/monitoring/resource-sampler.ts +258 -0
- package/src/monitoring/sample-ring-buffer.ts +108 -0
- package/src/monitoring/worker.ts +94 -0
- package/src/notifications/README.md +1 -1
- package/src/notifications/access-request-copy.ts +94 -23
- package/src/notifications/adapters/slack.ts +89 -21
- package/src/notifications/approval-card-builder.ts +33 -18
- package/src/notifications/approval-card-data.ts +48 -15
- package/src/notifications/broadcaster.ts +25 -16
- package/src/notifications/decision-engine.ts +131 -32
- package/src/notifications/guardian-question-mode.ts +58 -10
- package/src/permissions/approval-policy.ts +8 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/gateway-threshold-reader.ts +7 -6
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/secret-prompter.ts +11 -3
- package/src/permissions/types.ts +2 -2
- package/src/persistence/__tests__/conversation-queries-search.test.ts +397 -0
- package/src/persistence/__tests__/conversation-search-lexical.test.ts +103 -0
- package/src/persistence/__tests__/jobs-worker-memory-disabled.test.ts +95 -0
- package/src/persistence/__tests__/slow-query-log.test.ts +266 -0
- package/src/persistence/__tests__/slow-sync-log.test.ts +155 -0
- package/src/persistence/a2a-invite-store.ts +205 -0
- package/src/persistence/attachments-store.ts +94 -10
- package/src/persistence/checkpoints.ts +45 -0
- package/src/persistence/conversation-crud.ts +453 -260
- package/src/persistence/conversation-display-order-migration.ts +8 -2
- package/src/persistence/conversation-group-migration.test.ts +8 -0
- package/src/persistence/conversation-group-migration.ts +9 -0
- package/src/persistence/conversation-queries.ts +244 -125
- package/src/persistence/conversation-row-batch-delete.ts +9 -5
- package/src/persistence/conversation-search-lexical.ts +49 -0
- package/src/persistence/db-async-query.ts +42 -0
- package/src/persistence/db-connection.ts +13 -0
- package/src/persistence/delivery-channels.ts +0 -82
- package/src/persistence/delivery-crud.ts +27 -6
- package/src/persistence/embeddings/__tests__/embedding-identity.test.ts +18 -5
- package/src/persistence/embeddings/__tests__/messages-lexical-index.test.ts +334 -0
- package/src/persistence/embeddings/embedding-identity.ts +17 -5
- package/src/persistence/embeddings/embedding-runtime-manager.ts +31 -0
- package/src/persistence/embeddings/messages-lexical-index.ts +396 -0
- package/src/persistence/embeddings/qdrant-client.ts +81 -5
- package/src/persistence/group-crud.ts +13 -1
- package/src/persistence/job-handlers/__tests__/message-lexical-backfill.test.ts +506 -0
- package/src/persistence/job-handlers/__tests__/message-lexical-enqueue.test.ts +164 -0
- package/src/persistence/job-handlers/__tests__/message-lexical.test.ts +259 -0
- package/src/persistence/job-handlers/cleanup.ts +57 -9
- package/src/persistence/job-handlers/message-lexical-backfill.ts +187 -0
- package/src/persistence/job-handlers/message-lexical.ts +251 -0
- package/src/persistence/jobs-store.ts +102 -8
- package/src/persistence/jobs-worker.ts +68 -28
- package/src/persistence/llm-request-log-store.ts +35 -19
- package/src/persistence/llm-usage-store.ts +11 -0
- package/src/persistence/memory-lifecycle-hooks.test.ts +65 -0
- package/src/persistence/memory-lifecycle-hooks.ts +55 -1
- package/src/persistence/migrations/028-call-session-mode.ts +1 -1
- package/src/persistence/migrations/116-messages-fts.ts +8 -1
- package/src/persistence/migrations/311-create-subagents-table.ts +50 -0
- package/src/persistence/migrations/312-drop-inbox-conversation-state-table.ts +15 -0
- package/src/persistence/migrations/313-drop-messages-fts.ts +17 -0
- package/src/persistence/migrations/314-add-conversation-enabled-plugins.ts +25 -0
- package/src/persistence/migrations/315-create-a2a-invites.ts +58 -0
- package/src/persistence/migrations/316-drop-contact-channels-invite-id.ts +28 -0
- package/src/persistence/migrations/317-canonical-guardian-requester-signals.ts +24 -0
- package/src/persistence/migrations/318-drop-contact-channel-telemetry.ts +32 -0
- package/src/persistence/migrations/__tests__/315-create-a2a-invites.test.ts +161 -0
- package/src/persistence/migrations/__tests__/316-drop-contact-channels-invite-id.test.ts +156 -0
- package/src/persistence/migrations/__tests__/318-drop-contact-channel-telemetry.test.ts +134 -0
- package/src/persistence/migrations/__tests__/migration-prefix-guard.test.ts +99 -0
- package/src/persistence/raw-query.ts +50 -11
- package/src/persistence/schema/a2a.ts +14 -0
- package/src/persistence/schema/contacts.ts +0 -62
- package/src/persistence/schema/conversation-groups.ts +12 -0
- package/src/persistence/schema/conversations.ts +2 -0
- package/src/persistence/schema/documents.ts +65 -0
- package/src/persistence/schema/guardian.ts +4 -0
- package/src/persistence/schema/index.ts +4 -0
- package/src/persistence/schema/memory-injection.ts +62 -0
- package/src/persistence/schema/workflows.ts +55 -0
- package/src/persistence/slow-query-log.ts +437 -0
- package/src/persistence/slow-sync-log.ts +204 -0
- package/src/persistence/steps.ts +16 -0
- package/src/persistence/subagent-store.ts +125 -0
- package/src/persistence/worker-control.ts +45 -238
- package/src/plugin-api/event-hub-facade.ts +172 -0
- package/src/plugin-api/index.ts +9 -1
- package/src/plugin-api/types.ts +40 -9
- package/src/plugins/defaults/default-plugin-names.ts +60 -0
- package/src/plugins/defaults/index.ts +19 -29
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +46 -67
- package/src/plugins/defaults/memory/__tests__/db-async-query.test.ts +36 -1
- package/src/plugins/defaults/memory/__tests__/find-most-recent-retrospective-for.test.ts +2 -4
- package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +2 -2
- package/src/plugins/defaults/memory/__tests__/jobs-worker-v2-schedule.test.ts +9 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +7 -4
- package/src/plugins/defaults/memory/config.ts +11 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +1 -1
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +124 -113
- package/src/plugins/defaults/memory/embeddings.ts +77 -0
- package/src/plugins/defaults/memory/find-most-recent-retrospective-for.ts +66 -0
- package/src/plugins/defaults/memory/frontmatter.ts +50 -0
- package/src/plugins/defaults/memory/graph/bootstrap.test.ts +36 -10
- package/src/plugins/defaults/memory/graph/bootstrap.ts +6 -5
- package/src/plugins/defaults/memory/graph/consolidation.ts +3 -5
- package/src/plugins/defaults/memory/graph/extraction.ts +3 -6
- package/src/plugins/defaults/memory/graph/graph-search.ts +12 -23
- package/src/plugins/defaults/memory/graph/narrative.ts +3 -5
- package/src/plugins/defaults/memory/graph/pattern-scan.ts +3 -5
- package/src/plugins/defaults/memory/graph/retriever.test.ts +1 -6
- package/src/plugins/defaults/memory/graph/retriever.ts +5 -10
- package/src/plugins/defaults/memory/graph/tools.ts +1 -1
- package/src/plugins/defaults/memory/hooks/init.ts +31 -5
- package/src/plugins/defaults/memory/indexer.ts +2 -3
- package/src/plugins/defaults/memory/injectors.ts +2 -2
- package/src/plugins/defaults/memory/job-handler-registration.ts +96 -0
- package/src/plugins/defaults/memory/job-handlers/backfill.ts +4 -26
- package/src/plugins/defaults/memory/job-handlers/embedding.test.ts +6 -19
- package/src/plugins/defaults/memory/job-handlers/embedding.ts +9 -22
- package/src/plugins/defaults/memory/job-handlers/index-maintenance.ts +2 -3
- package/src/plugins/defaults/memory/job-handlers.ts +13 -12
- package/src/plugins/defaults/memory/jobs/embed-concept-page.ts +1 -1
- package/src/plugins/defaults/memory/llm-helpers.ts +76 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +8 -5
- package/src/plugins/defaults/memory/memory-retrospective-startup-cleanup.ts +2 -2
- package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +2 -0
- package/src/plugins/defaults/memory/persistence-hooks.ts +46 -1
- package/src/plugins/defaults/memory/pkb/pkb-index.ts +1 -5
- package/src/plugins/defaults/memory/pkb/pkb-search.ts +2 -2
- package/src/plugins/defaults/memory/routes/__tests__/memory-v2-simulate-route.test.ts +4 -6
- package/src/plugins/defaults/memory/routes/memory-item-routes.ts +1 -1
- package/src/plugins/defaults/memory/search/semantic.ts +2 -2
- package/src/plugins/defaults/memory/startup.ts +244 -0
- package/src/plugins/defaults/memory/task-memory-cleanup.ts +8 -0
- package/src/{daemon → plugins/defaults/memory/v2}/__tests__/memory-v2-startup.test.ts +6 -6
- package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +2 -13
- package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +4 -5
- package/src/plugins/defaults/memory/v2/__tests__/sweep-job.test.ts +1 -8
- package/src/plugins/defaults/memory/v2/activation.ts +2 -4
- package/src/plugins/defaults/memory/v2/cli-command-store.ts +2 -4
- package/src/plugins/defaults/memory/v2/frontmatter-sweep.ts +1 -1
- package/src/{daemon → plugins/defaults/memory/v2}/memory-v2-startup.test.ts +8 -8
- package/src/{daemon → plugins/defaults/memory/v2}/memory-v2-startup.ts +20 -22
- package/src/plugins/defaults/memory/v2/migration.ts +2 -5
- package/src/plugins/defaults/memory/v2/page-store.ts +1 -1
- package/src/plugins/defaults/memory/v2/qdrant.ts +2 -3
- package/src/plugins/defaults/memory/v2/router.ts +5 -5
- package/src/plugins/defaults/memory/v2/sim.ts +2 -4
- package/src/plugins/defaults/memory/v2/skill-store.ts +2 -4
- package/src/plugins/defaults/memory/v2/sweep-job.ts +6 -6
- package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +11 -9
- package/src/plugins/defaults/memory/v3/__tests__/dense.test.ts +107 -2
- package/src/plugins/defaults/memory/v3/__tests__/gate-flag.test.ts +69 -0
- package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +181 -0
- package/src/plugins/defaults/memory/v3/__tests__/live-integration.test.ts +7 -3
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +345 -8
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +1 -4
- package/src/plugins/defaults/memory/v3/__tests__/section-needle.test.ts +64 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +18 -13
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +62 -1
- package/src/plugins/defaults/memory/v3/card.ts +1 -4
- package/src/plugins/defaults/memory/v3/dense.ts +60 -25
- package/src/plugins/defaults/memory/v3/edge.ts +1 -1
- package/src/plugins/defaults/memory/v3/gate-flag.ts +20 -0
- package/src/plugins/defaults/memory/v3/gate.ts +158 -0
- package/src/plugins/defaults/memory/v3/maintain-job.ts +2 -4
- package/src/plugins/defaults/memory/v3/orchestrate.ts +186 -25
- package/src/plugins/defaults/memory/v3/pool-select.test.ts +7 -9
- package/src/plugins/defaults/memory/v3/pool-select.ts +6 -6
- package/src/plugins/defaults/memory/v3/prune.ts +2 -2
- package/src/plugins/defaults/memory/v3/section-dense-store.ts +8 -11
- package/src/plugins/defaults/memory/v3/section-needle.ts +27 -7
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +10 -0
- package/src/plugins/defaults/memory/v3-eval/eval-packets.ts +1 -4
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +5 -0
- package/src/plugins/defaults/turn-context/injectors.ts +1 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +12 -0
- package/src/plugins/injector-registry.ts +13 -2
- package/src/plugins/mtime-cache.ts +36 -47
- package/src/plugins/pipeline.ts +15 -1
- package/src/plugins/types.ts +14 -23
- package/src/prompts/__tests__/system-prompt.test.ts +170 -32
- package/src/prompts/persona-resolver.ts +13 -9
- package/src/prompts/system-prompt.ts +30 -9
- package/src/prompts/templates/BOOTSTRAP.md +16 -119
- package/src/prompts/templates/SOUL.md +43 -13
- package/src/prompts/templates/system-sections.ts +32 -27
- package/src/prompts/templates/users/default.md +19 -0
- package/src/providers/__tests__/placeholder-sentinels.test.ts +56 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +96 -1
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +14 -1
- package/src/providers/openai/responses-provider.ts +18 -0
- package/src/providers/placeholder-sentinels.ts +18 -8
- package/src/providers/retry.ts +1 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +0 -64
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +92 -79
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +98 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +62 -3
- package/src/providers/speech-to-text/provider-catalog.ts +0 -89
- package/src/providers/speech-to-text/resolve.ts +55 -0
- package/src/providers/tool-progress-events.test.ts +50 -0
- package/src/providers/tool-progress-events.ts +99 -0
- package/src/providers/types.ts +1 -1
- package/src/providers/vellum-model-routing.test.ts +51 -0
- package/src/providers/vellum-model-routing.ts +84 -0
- package/src/runtime/AGENTS.md +5 -4
- package/src/runtime/__tests__/agent-wake.test.ts +158 -1
- package/src/runtime/__tests__/channel-verification-service.test.ts +2 -2
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +37 -8
- package/src/runtime/access-request-helper.ts +154 -3
- package/src/runtime/actor-trust-resolver.ts +7 -21
- package/src/runtime/agent-wake.ts +41 -1
- package/src/runtime/assistant-event.ts +86 -12
- package/src/runtime/auth/__tests__/route-policy.test.ts +31 -0
- package/src/runtime/channel-approval-types.ts +52 -2
- package/src/runtime/channel-approvals.ts +23 -8
- package/src/runtime/channel-invite-transport.ts +2 -2
- package/src/runtime/channel-invite-transports/voice.ts +1 -1
- package/src/runtime/channel-readiness-service.ts +3 -5
- package/src/runtime/channel-retry-sweep.ts +18 -24
- package/src/runtime/channel-verification-service.ts +9 -11
- package/src/runtime/finalize-event-delivery.ts +30 -16
- package/src/runtime/guardian-action-service.ts +10 -11
- package/src/runtime/guardian-reply-router.ts +122 -21
- package/src/runtime/http-server.ts +44 -101
- package/src/runtime/introduction-policy.test.ts +214 -0
- package/src/runtime/introduction-policy.ts +284 -0
- package/src/runtime/invite-service.ts +104 -361
- package/src/runtime/local-actor-identity.test.ts +38 -0
- package/src/runtime/local-actor-identity.ts +14 -0
- package/src/runtime/middleware/__tests__/rate-limiter.test.ts +22 -0
- package/src/runtime/middleware/rate-limiter.ts +30 -0
- package/src/runtime/middleware/twilio-validation.ts +5 -7
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +28 -0
- package/src/runtime/migrations/origin-mode.ts +6 -6
- package/src/runtime/pre-first-message-gate.ts +1 -0
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +6 -35
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +24 -3
- package/src/runtime/routes/__tests__/contact-routes.test.ts +80 -15
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +7 -1
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -3
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +142 -1
- package/src/runtime/routes/__tests__/invite-relay-routes.test.ts +119 -30
- package/src/runtime/routes/__tests__/migration-import-allowed-hosts-env.test.ts +70 -0
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +194 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +755 -29
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +4 -0
- package/src/runtime/routes/__tests__/schedule-routes-create.test.ts +70 -0
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +161 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +1 -1
- package/src/runtime/routes/acp-routes.ts +15 -3
- package/src/runtime/routes/app-management-routes.ts +4 -7
- package/src/runtime/routes/approval-routes.ts +4 -0
- package/src/runtime/routes/attachment-routes.ts +54 -3
- package/src/runtime/routes/avatar-routes.ts +0 -4
- package/src/runtime/routes/background-tool-routes.ts +34 -5
- package/src/runtime/routes/btw-routes.ts +32 -4
- package/src/runtime/routes/channel-route-shared.ts +18 -14
- package/src/runtime/routes/channel-verification-routes.ts +0 -3
- package/src/runtime/routes/contact-routes.ts +298 -117
- package/src/runtime/routes/conversation-list-routes.ts +6 -0
- package/src/runtime/routes/conversation-management-routes.ts +74 -37
- package/src/runtime/routes/conversation-query-routes.ts +5 -2
- package/src/runtime/routes/conversation-routes.ts +222 -82
- package/src/runtime/routes/conversations-import-routes.ts +8 -1
- package/src/runtime/routes/credential-prompt-routes.ts +20 -5
- package/src/runtime/routes/debug-routes.ts +1 -0
- package/src/runtime/routes/documents-routes.ts +5 -2
- package/src/runtime/routes/empty-state-greeting-cache.ts +37 -10
- package/src/runtime/routes/global-search-routes.ts +2 -1
- package/src/runtime/routes/guardian-approval-interception.ts +51 -0
- package/src/runtime/routes/home-feed-routes.ts +60 -1
- package/src/runtime/routes/identity-routes.ts +4 -87
- package/src/runtime/routes/inbound-message-handler.ts +82 -41
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +430 -57
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +302 -572
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +76 -270
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +21 -116
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +9 -1
- package/src/runtime/routes/inbound-stages/escalation-intercept.test.ts +158 -0
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +22 -1
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +22 -16
- package/src/runtime/routes/internal-twilio-routes.ts +0 -15
- package/src/runtime/routes/messages-lexical-routes.ts +75 -0
- package/src/runtime/routes/migration-routes.ts +69 -6
- package/src/runtime/routes/monitoring-routes.ts +237 -0
- package/src/runtime/routes/plugins-routes.ts +329 -8
- package/src/runtime/routes/schedule-routes.ts +49 -8
- package/src/runtime/routes/schedule-worker-routes.ts +210 -0
- package/src/runtime/routes/secret-routes.ts +1 -1
- package/src/runtime/routes/surface-conversation-resolver.ts +1 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +54 -9
- package/src/runtime/services/__tests__/auto-analysis-enqueue.test.ts +30 -6
- package/src/runtime/services/__tests__/conversation-analyze-job.test.ts +43 -8
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +20 -0
- package/src/runtime/services/analyze-conversation.ts +14 -4
- package/src/runtime/services/conversation-serializer.ts +5 -0
- package/src/runtime/slack-no-response.ts +47 -0
- package/src/runtime/slack-reply-session.test.ts +633 -0
- package/src/runtime/slack-reply-session.ts +382 -0
- package/src/runtime/slack-task-progress.ts +95 -0
- package/src/runtime/sync/resource-sync-events.ts +25 -0
- package/src/runtime/tool-grant-request-helper.ts +17 -1
- package/src/runtime/trust-class.test.ts +141 -0
- package/src/runtime/trust-class.ts +121 -0
- package/src/runtime/trust-verdict-consumer.ts +7 -6
- package/src/schedule/__tests__/run-script.test.ts +21 -0
- package/src/schedule/__tests__/worker-control.test.ts +241 -0
- package/src/schedule/run-script.ts +13 -6
- package/src/schedule/schedule-usage-store.ts +1 -0
- package/src/schedule/scheduler.ts +178 -127
- package/src/schedule/worker-control.ts +118 -0
- package/src/schedule/worker.ts +131 -0
- package/src/skills/__tests__/categories-cache.test.ts +38 -0
- package/src/skills/categories-cache.ts +40 -5
- package/src/skills/managed-store.ts +41 -6
- package/src/skills/validate-input.ts +47 -0
- package/src/stt/types.ts +5 -9
- package/src/subagent/manager.ts +267 -39
- package/src/subagent/types.ts +6 -2
- package/src/subagent/validate-allowlists.ts +65 -0
- package/src/telemetry/watchdog-direct-emit.test.ts +109 -0
- package/src/telemetry/watchdog-direct-emit.ts +89 -0
- package/src/tools/acp/list-agents.test.ts +0 -6
- package/src/tools/background-tool-registry.ts +61 -0
- package/src/tools/document/document-comment-tool.test.ts +4 -0
- package/src/tools/host-terminal/host-shell.test.ts +397 -0
- package/src/tools/host-terminal/host-shell.ts +203 -14
- package/src/tools/skills/find-similar-skills.test.ts +152 -1
- package/src/tools/skills/find-similar-skills.ts +30 -4
- package/src/tools/skills/load.ts +36 -0
- package/src/tools/skills/scaffold-managed.ts +66 -34
- package/src/tools/skills/skill-script-runner.ts +17 -0
- package/src/tools/skills/skill-tool-factory.ts +5 -1
- package/src/tools/terminal/safe-env.ts +3 -0
- package/src/tools/terminal/shell.test.ts +177 -0
- package/src/tools/terminal/shell.ts +98 -4
- package/src/tools/tool-approval-handler.ts +106 -29
- package/{node_modules/@vellumai/skill-host-contracts/src → src/tools}/tool-types.ts +16 -17
- package/src/tools/types.ts +29 -17
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/tts/__tests__/provider-adapters.test.ts +37 -0
- package/src/tts/__tests__/provider-catalog.test.ts +21 -0
- package/src/tts/provider-catalog.ts +40 -9
- package/src/tts/providers/fish-audio-provider.ts +19 -10
- package/src/tts/types.ts +3 -4
- package/src/util/__tests__/process-tree.test.ts +10 -2
- package/src/util/__tests__/text-spacing.test.ts +53 -0
- package/src/util/cgroup-memory.ts +142 -0
- package/src/util/image-conversion.ts +281 -0
- package/src/util/phone.ts +1 -8
- package/src/util/platform.ts +33 -0
- package/src/util/process-tree.ts +10 -2
- package/src/util/text-spacing.ts +53 -0
- package/src/util/worker-process.ts +271 -0
- package/src/workflows/journal-store.ts +16 -1
- package/src/workflows/library.test.ts +35 -20
- package/src/workflows/library.ts +105 -66
- package/src/workspace/git-service.ts +1 -0
- package/src/workspace/migrations/011-backfill-installation-id.ts +83 -31
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +41 -22
- package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +1 -1
- package/src/workspace/migrations/118-seed-now-md.ts +64 -0
- package/src/workspace/migrations/119-strip-persisted-memory-v3-tuning-defaults.ts +169 -0
- package/src/workspace/migrations/120-revise-onboarding-threads.ts +47 -0
- package/src/workspace/migrations/121-seed-default-user-guardrails.ts +82 -0
- package/src/workspace/migrations/122-relocate-default-user-boundary.ts +111 -0
- package/src/workspace/migrations/123-swap-quality-profile-to-fable.ts +85 -0
- package/src/workspace/migrations/__tests__/119-strip-persisted-memory-v3-tuning-defaults.test.ts +181 -0
- package/src/workspace/migrations/registry.ts +12 -0
- package/drizzle/0000_dizzy_maggott.sql +0 -301
- package/drizzle/meta/0000_snapshot.json +0 -1933
- package/drizzle/meta/_journal.json +0 -13
- package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +0 -887
- package/node_modules/@vellumai/skill-host-contracts/bun.lock +0 -24
- package/node_modules/@vellumai/skill-host-contracts/package.json +0 -18
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -97
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +0 -1349
- package/node_modules/@vellumai/skill-host-contracts/src/index.ts +0 -6
- package/node_modules/@vellumai/skill-host-contracts/src/runtime-mode.ts +0 -11
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +0 -32
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +0 -333
- package/node_modules/@vellumai/skill-host-contracts/tsconfig.json +0 -20
- package/node_modules/@vellumai/skill-host-contracts/tsconfig.test.json +0 -12
- package/src/__tests__/invite-redemption-service.test.ts +0 -1180
- package/src/__tests__/invite-service-ipc.test.ts +0 -350
- package/src/__tests__/job-handler-registry-guard.test.ts +0 -125
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +0 -203
- package/src/__tests__/relay-server.test.ts +0 -6153
- package/src/__tests__/telephony-stt-routing.test.ts +0 -329
- package/src/__tests__/voice-invite-redemption.test.ts +0 -702
- package/src/calls/relay-server.ts +0 -2463
- package/src/calls/telephony-stt-routing.ts +0 -145
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +0 -128
- package/src/daemon/__tests__/daemon-skill-host.test.ts +0 -273
- package/src/daemon/daemon-skill-host.ts +0 -262
- package/src/daemon/message-types/meet.ts +0 -143
- package/src/daemon/shutdown-registry.ts +0 -40
- package/src/ipc/__tests__/skill-server-bidirectional.test.ts +0 -253
- package/src/ipc/__tests__/skill-server.test.ts +0 -182
- package/src/ipc/skill-ipc-types.ts +0 -54
- package/src/ipc/skill-routes/__tests__/config.test.ts +0 -146
- package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +0 -448
- package/src/ipc/skill-routes/__tests__/identity.test.ts +0 -62
- package/src/ipc/skill-routes/__tests__/log.test.ts +0 -133
- package/src/ipc/skill-routes/__tests__/memory.test.ts +0 -203
- package/src/ipc/skill-routes/__tests__/platform.test.ts +0 -111
- package/src/ipc/skill-routes/__tests__/providers.test.ts +0 -265
- package/src/ipc/skill-routes/__tests__/registries.test.ts +0 -400
- package/src/ipc/skill-routes/config.ts +0 -47
- package/src/ipc/skill-routes/events.ts +0 -147
- package/src/ipc/skill-routes/identity.ts +0 -21
- package/src/ipc/skill-routes/index.ts +0 -37
- package/src/ipc/skill-routes/log.ts +0 -40
- package/src/ipc/skill-routes/memory.ts +0 -92
- package/src/ipc/skill-routes/platform.ts +0 -39
- package/src/ipc/skill-routes/providers.ts +0 -162
- package/src/ipc/skill-routes/registries.ts +0 -338
- package/src/ipc/skill-server.ts +0 -831
- package/src/ipc/skill-socket-path.ts +0 -20
- package/src/jobs/register-job-handlers.ts +0 -74
- package/src/persistence/invite-store.ts +0 -481
- package/src/plugins/job-handler-registry.ts +0 -94
- package/src/providers/cache-control.ts +0 -26
- package/src/runtime/__tests__/runtime-mode.test.ts +0 -62
- package/src/runtime/invite-redemption-service.ts +0 -828
- package/src/runtime/invite-redemption-templates.ts +0 -46
- package/src/runtime/runtime-mode.ts +0 -40
- package/src/runtime/slack-dm-text-delivery.ts +0 -177
- package/src/util/voice-code.ts +0 -31
|
@@ -37,6 +37,12 @@ mock.module("../agent-process.js", () => ({
|
|
|
37
37
|
async cancel(sessionId: string): Promise<void> {
|
|
38
38
|
cancelCalls.push(sessionId);
|
|
39
39
|
}
|
|
40
|
+
markStderr(): number {
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
stderrSince(): string {
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
40
46
|
kill(): void {}
|
|
41
47
|
},
|
|
42
48
|
}));
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { AcpAgentProcess } from "./agent-process.js";
|
|
4
|
+
import type { AcpAgentConfig } from "./types.js";
|
|
5
|
+
|
|
6
|
+
const config: AcpAgentConfig = { command: "noop", args: [] };
|
|
7
|
+
|
|
8
|
+
// The client factory is never invoked in these tests (no spawn), so a stub is
|
|
9
|
+
// sufficient.
|
|
10
|
+
const clientFactory = (() => ({})) as never;
|
|
11
|
+
|
|
12
|
+
function newProcess(): AcpAgentProcess {
|
|
13
|
+
return new AcpAgentProcess("test-agent", config, clientFactory);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// retainStderr is the private handler path the stderr "data" listener calls
|
|
17
|
+
// after logging; feed it directly to keep the test deterministic (no real
|
|
18
|
+
// child process or stream timing).
|
|
19
|
+
function feed(proc: AcpAgentProcess, line: string): void {
|
|
20
|
+
(proc as unknown as { retainStderr(text: string): void }).retainStderr(line);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("AcpAgentProcess.stderrSince(0)", () => {
|
|
24
|
+
test("returns empty string before any stderr is captured", () => {
|
|
25
|
+
expect(newProcess().stderrSince(0)).toBe("");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("retains captured lines joined by newlines, in order", () => {
|
|
29
|
+
const proc = newProcess();
|
|
30
|
+
feed(proc, "line 1");
|
|
31
|
+
feed(proc, "line 2");
|
|
32
|
+
feed(proc, "line 3");
|
|
33
|
+
|
|
34
|
+
expect(proc.stderrSince(0)).toBe("line 1\nline 2\nline 3");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("evicts oldest lines once the byte cap is exceeded", () => {
|
|
38
|
+
const proc = newProcess();
|
|
39
|
+
// Each line is ~1 KB; a handful exceeds the ~4 KB cap and forces eviction
|
|
40
|
+
// of the earliest lines while preserving newest-line ordering.
|
|
41
|
+
const kb = "x".repeat(1024);
|
|
42
|
+
for (let i = 0; i < 8; i++) {
|
|
43
|
+
feed(proc, `${i}:${kb}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const retained = proc.stderrSince(0).split("\n");
|
|
47
|
+
// Oldest ("0:") evicted, newest ("7:") retained, still in insertion order.
|
|
48
|
+
expect(retained.at(0)).not.toContain("0:");
|
|
49
|
+
expect(retained.at(-1)).toContain("7:");
|
|
50
|
+
expect(Buffer.byteLength(proc.stderrSince(0))).toBeLessThanOrEqual(
|
|
51
|
+
4096 + kb.length,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const indices = retained.map((l) => Number(l.split(":")[0]));
|
|
55
|
+
const sorted = [...indices].sort((a, b) => a - b);
|
|
56
|
+
expect(indices).toEqual(sorted);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("truncates an oversized line to the cap, keeping its tail (the diagnostic)", () => {
|
|
60
|
+
const proc = newProcess();
|
|
61
|
+
// The real adapter error sits at the END of the chunk; deriveFailureError
|
|
62
|
+
// reads from the tail, so truncation must drop the head, not the tail.
|
|
63
|
+
const huge = "H".repeat(6000) + "TAIL_DIAGNOSTIC";
|
|
64
|
+
feed(proc, huge);
|
|
65
|
+
|
|
66
|
+
const retained = proc.stderrSince(0);
|
|
67
|
+
expect(retained.length).toBe(4096);
|
|
68
|
+
expect(retained.endsWith("TAIL_DIAGNOSTIC")).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("stderrSince(0) is pure: repeated reads do not clear the buffer", () => {
|
|
72
|
+
const proc = newProcess();
|
|
73
|
+
feed(proc, "only line");
|
|
74
|
+
|
|
75
|
+
expect(proc.stderrSince(0)).toBe("only line");
|
|
76
|
+
expect(proc.stderrSince(0)).toBe("only line");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe("AcpAgentProcess.markStderr / stderrSince", () => {
|
|
81
|
+
test("stderrSince returns only lines produced after the mark", () => {
|
|
82
|
+
const proc = newProcess();
|
|
83
|
+
feed(proc, "before 1");
|
|
84
|
+
feed(proc, "before 2");
|
|
85
|
+
|
|
86
|
+
const mark = proc.markStderr();
|
|
87
|
+
feed(proc, "after 1");
|
|
88
|
+
feed(proc, "after 2");
|
|
89
|
+
|
|
90
|
+
expect(proc.stderrSince(mark)).toBe("after 1\nafter 2");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("a mark taken after all lines excludes everything retained so far", () => {
|
|
94
|
+
const proc = newProcess();
|
|
95
|
+
feed(proc, "line 1");
|
|
96
|
+
feed(proc, "line 2");
|
|
97
|
+
|
|
98
|
+
const mark = proc.markStderr();
|
|
99
|
+
expect(proc.stderrSince(mark)).toBe("");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("mark is monotonic across eviction: seq keeps counting evicted lines", () => {
|
|
103
|
+
const proc = newProcess();
|
|
104
|
+
// Overflow the byte cap so early lines are evicted, then mark and add one
|
|
105
|
+
// fresh line: only the post-mark line comes back, unaffected by eviction.
|
|
106
|
+
const kb = "z".repeat(1024);
|
|
107
|
+
for (let i = 0; i < 8; i++) {
|
|
108
|
+
feed(proc, `${i}:${kb}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const mark = proc.markStderr();
|
|
112
|
+
feed(proc, "fresh failure");
|
|
113
|
+
|
|
114
|
+
expect(proc.stderrSince(mark)).toBe("fresh failure");
|
|
115
|
+
});
|
|
116
|
+
});
|
package/src/acp/agent-process.ts
CHANGED
|
@@ -30,6 +30,13 @@ const log = getLogger("acp");
|
|
|
30
30
|
*/
|
|
31
31
|
const AUTH_REQUIRED_CODE = -32000;
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Rough byte cap for retained stderr. Oldest lines are evicted once the sum of
|
|
35
|
+
* retained line lengths exceeds this, so the stderr ring (read via stderrSince)
|
|
36
|
+
* stays bounded.
|
|
37
|
+
*/
|
|
38
|
+
const STDERR_RETENTION_BYTES = 4096;
|
|
39
|
+
|
|
33
40
|
/**
|
|
34
41
|
* Detects the ACP auth-required error. Checks the `code` property rather than
|
|
35
42
|
* `instanceof acp.RequestError` so plain JSON-RPC error objects are also
|
|
@@ -69,6 +76,20 @@ export class AcpAgentProcess {
|
|
|
69
76
|
*/
|
|
70
77
|
private spawnedEnv: NodeJS.ProcessEnv | null = null;
|
|
71
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Ring of the most recent stderr lines, bounded to ~STDERR_RETENTION_BYTES.
|
|
81
|
+
* Read once on the failure path to surface the real adapter error. Each
|
|
82
|
+
* entry carries a monotonic `seq` so a caller can scope reads to lines
|
|
83
|
+
* produced after a checkpoint (see markStderr/stderrSince).
|
|
84
|
+
*/
|
|
85
|
+
private stderrRing: { seq: number; text: string }[] = [];
|
|
86
|
+
private stderrRingBytes = 0;
|
|
87
|
+
/**
|
|
88
|
+
* Cumulative count of stderr lines ever retained, including ones later
|
|
89
|
+
* evicted. Assigned as each line's `seq`; markStderr() snapshots it.
|
|
90
|
+
*/
|
|
91
|
+
private stderrSeq = 0;
|
|
92
|
+
|
|
72
93
|
constructor(
|
|
73
94
|
public readonly agentId: string,
|
|
74
95
|
private readonly config: AcpAgentConfig,
|
|
@@ -108,6 +129,7 @@ export class AcpAgentProcess {
|
|
|
108
129
|
const text = chunk.toString().trim();
|
|
109
130
|
if (text) {
|
|
110
131
|
log.error({ agentId: this.agentId, stderr: text }, "ACP agent stderr");
|
|
132
|
+
this.retainStderr(text);
|
|
111
133
|
}
|
|
112
134
|
});
|
|
113
135
|
|
|
@@ -124,6 +146,55 @@ export class AcpAgentProcess {
|
|
|
124
146
|
});
|
|
125
147
|
}
|
|
126
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Appends a stderr line to the ring, evicting oldest lines once the retained
|
|
151
|
+
* total exceeds STDERR_RETENTION_BYTES. Always keeps at least the newest line
|
|
152
|
+
* so a single oversized line is not fully dropped.
|
|
153
|
+
*/
|
|
154
|
+
private retainStderr(text: string): void {
|
|
155
|
+
// Cap a single oversized line: the "keep newest" rule below never evicts it,
|
|
156
|
+
// so an untruncated multi-MB chunk would blow the ring budget and make the
|
|
157
|
+
// failure-path stderr scan (deriveFailureError) super-linear on huge input.
|
|
158
|
+
// Keep the TAIL: the adapter's error JSON / last line sits at the end, and
|
|
159
|
+
// deriveFailureError reads from the end, so dropping the head is lossless.
|
|
160
|
+
const line =
|
|
161
|
+
text.length > STDERR_RETENTION_BYTES
|
|
162
|
+
? text.slice(-STDERR_RETENTION_BYTES)
|
|
163
|
+
: text;
|
|
164
|
+
this.stderrSeq += 1;
|
|
165
|
+
this.stderrRing.push({ seq: this.stderrSeq, text: line });
|
|
166
|
+
this.stderrRingBytes += Buffer.byteLength(line);
|
|
167
|
+
while (
|
|
168
|
+
this.stderrRingBytes > STDERR_RETENTION_BYTES &&
|
|
169
|
+
this.stderrRing.length > 1
|
|
170
|
+
) {
|
|
171
|
+
const evicted = this.stderrRing.shift()!;
|
|
172
|
+
this.stderrRingBytes -= Buffer.byteLength(evicted.text);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Snapshots the current cumulative stderr line count. Pair with
|
|
178
|
+
* stderrSince() to read only stderr produced after this checkpoint, so a
|
|
179
|
+
* prompt's failure derives from its own stderr rather than lines retained
|
|
180
|
+
* from startup, resume, or an earlier (possibly cancelled) prompt.
|
|
181
|
+
*/
|
|
182
|
+
markStderr(): number {
|
|
183
|
+
return this.stderrSeq;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Returns retained stderr lines produced after `mark` (a markStderr()
|
|
188
|
+
* checkpoint), joined by newlines. Best-effort: lines pushed after the mark
|
|
189
|
+
* but since evicted are simply absent; returns "" when nothing newer remains.
|
|
190
|
+
*/
|
|
191
|
+
stderrSince(mark: number): string {
|
|
192
|
+
return this.stderrRing
|
|
193
|
+
.filter((e) => e.seq > mark)
|
|
194
|
+
.map((e) => e.text)
|
|
195
|
+
.join("\n");
|
|
196
|
+
}
|
|
197
|
+
|
|
127
198
|
/**
|
|
128
199
|
* Initializes the ACP connection by negotiating protocol version and capabilities.
|
|
129
200
|
*/
|
package/src/acp/auto-install.ts
CHANGED
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
* never the untrusted task project dir. A clean cwd has no project-local
|
|
17
17
|
* `node_modules/.bin`, `bunfig.toml`, or `.npmrc`, so none of the
|
|
18
18
|
* cwd-based package-resolution hijacks apply.
|
|
19
|
-
* - The installer env is a SANITIZED copy of `process.env` with
|
|
20
|
-
* `CLAUDE_CODE_OAUTH_TOKEN
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* a malicious
|
|
19
|
+
* - The installer env is a SANITIZED copy of `process.env` with known
|
|
20
|
+
* ambient secrets (`CLAUDE_CODE_OAUTH_TOKEN`, plus the `GEMINI_API_KEY`
|
|
21
|
+
* used by the Gemini LLM provider) stripped, so no secret is ever in scope
|
|
22
|
+
* during package resolution, and `BUN_CONFIG_REGISTRY` forced to the
|
|
23
|
+
* public npm registry so a redirected registry cannot serve a malicious
|
|
24
|
+
* package.
|
|
24
25
|
* - The token is injected ONLY later, at spawn time, on the real installed
|
|
25
26
|
* binary (see `prepare-agent-env.ts`). `prepareAgentEnv` is never called
|
|
26
27
|
* here.
|
|
@@ -99,9 +100,12 @@ function execFileWithTimeout(
|
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
/**
|
|
102
|
-
* A copy of `process.env` safe to hand to the package installer:
|
|
103
|
-
* secrets are stripped so they can never leak into a resolved
|
|
104
|
-
* lifecycle/runtime, and the registry is pinned to the trusted
|
|
103
|
+
* A copy of `process.env` safe to hand to the package installer: known
|
|
104
|
+
* ambient secrets are stripped so they can never leak into a resolved
|
|
105
|
+
* package's lifecycle/runtime, and the registry is pinned to the trusted
|
|
106
|
+
* public one. `CLAUDE_CODE_OAUTH_TOKEN` is the ACP adapter token;
|
|
107
|
+
* `GEMINI_API_KEY` belongs to the Gemini LLM provider but may be present in
|
|
108
|
+
* the daemon env, so it is stripped here as defense-in-depth.
|
|
105
109
|
*/
|
|
106
110
|
function sanitizedInstallEnv(): NodeJS.ProcessEnv {
|
|
107
111
|
const env: NodeJS.ProcessEnv = { ...process.env };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { deriveFailureError } from "./failure-error.js";
|
|
4
|
+
|
|
5
|
+
const CODEX_MESSAGE =
|
|
6
|
+
"The 'gpt-5.3-codex' model is not supported when using Codex with a ChatGPT account.";
|
|
7
|
+
|
|
8
|
+
const ESC = String.fromCharCode(27); // ANSI escape (0x1B)
|
|
9
|
+
|
|
10
|
+
// A realistic codex-acp stderr line: a JSON error blob wrapped in a coloured
|
|
11
|
+
// log prefix and a trailing reset code.
|
|
12
|
+
const ansiCodexStderr =
|
|
13
|
+
`${ESC}[2m2026-06-30T12:00:00Z${ESC}[0m ${ESC}[31mERROR${ESC}[0m codex_core: ` +
|
|
14
|
+
`request failed {"type":"error","status":400,"error":` +
|
|
15
|
+
`{"type":"invalid_request_error","message":"${CODEX_MESSAGE}"}}${ESC}[0m`;
|
|
16
|
+
|
|
17
|
+
describe("deriveFailureError", () => {
|
|
18
|
+
test("extracts the inner error.message from codex-acp JSON stderr", () => {
|
|
19
|
+
const stderr =
|
|
20
|
+
`{"type":"error","status":400,"error":` +
|
|
21
|
+
`{"type":"invalid_request_error","message":"${CODEX_MESSAGE}"}}`;
|
|
22
|
+
expect(deriveFailureError("Internal error", stderr)).toBe(CODEX_MESSAGE);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("strips ANSI codes and extracts error.message from a real log line", () => {
|
|
26
|
+
const result = deriveFailureError("Internal error", ansiCodexStderr);
|
|
27
|
+
expect(result).toBe(CODEX_MESSAGE);
|
|
28
|
+
expect(result).not.toContain(ESC);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("parses JSON embedded in surrounding log text", () => {
|
|
32
|
+
const stderr = `2026-06-30 fatal: the adapter said {"error":{"message":"boom"}} and gave up`;
|
|
33
|
+
expect(deriveFailureError("Internal error", stderr)).toBe("boom");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("returns the LAST JSON error.message when several are present", () => {
|
|
37
|
+
const stderr =
|
|
38
|
+
`{"error":{"message":"first failure"}}\n` +
|
|
39
|
+
`{"error":{"message":"final failure"}}`;
|
|
40
|
+
expect(deriveFailureError("Internal error", stderr)).toBe("final failure");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("empty stderr yields the ack message", () => {
|
|
44
|
+
expect(deriveFailureError("Internal error", "")).toBe("Internal error");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("generic ack with informative non-JSON stderr yields the last stderr line, ANSI-stripped", () => {
|
|
48
|
+
const stderr = `starting up...\n${ESC}[31mfatal: could not connect to backend${ESC}[0m`;
|
|
49
|
+
expect(deriveFailureError("Internal error", stderr)).toBe(
|
|
50
|
+
"fatal: could not connect to backend",
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("already-specific ack is preserved when stderr is empty", () => {
|
|
55
|
+
expect(deriveFailureError(CODEX_MESSAGE, "")).toBe(CODEX_MESSAGE);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("already-specific ack is preserved (not double-appended) when stderr duplicates it", () => {
|
|
59
|
+
// codex often echoes the same message to stderr; we must not repeat it.
|
|
60
|
+
expect(deriveFailureError(CODEX_MESSAGE, CODEX_MESSAGE)).toBe(
|
|
61
|
+
CODEX_MESSAGE,
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("structured error duplicating the generic ack yields the clean ack, not the raw JSON", () => {
|
|
66
|
+
// The adapter acked "Internal error" and also emitted it as a JSON blob;
|
|
67
|
+
// return the clean ack rather than echoing the raw JSON line.
|
|
68
|
+
const stderr = `{"error":{"message":"Internal error"}}`;
|
|
69
|
+
expect(deriveFailureError("Internal error", stderr)).toBe("Internal error");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("falls back to the ack message when stderr has no JSON and no lines", () => {
|
|
73
|
+
expect(deriveFailureError("Internal error", " \n \n")).toBe(
|
|
74
|
+
"Internal error",
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("ignores malformed JSON braces and uses the last stderr line", () => {
|
|
79
|
+
const stderr = "noise {not: valid json} more noise\nreal failure detail";
|
|
80
|
+
expect(deriveFailureError("Internal error", stderr)).toBe(
|
|
81
|
+
"real failure detail",
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("a stray unmatched brace before the JSON does not block extraction", () => {
|
|
86
|
+
const stderr =
|
|
87
|
+
`some log { unmatched brace here\n` +
|
|
88
|
+
`... {"type":"error","error":{"message":"The real error"}} ...`;
|
|
89
|
+
expect(deriveFailureError("Internal error", stderr)).toBe("The real error");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("braces inside a JSON string literal don't throw off the scan", () => {
|
|
93
|
+
const stderr = `{"error":{"message":"unexpected } brace {in} the text"}}`;
|
|
94
|
+
expect(deriveFailureError("Internal error", stderr)).toBe(
|
|
95
|
+
"unexpected } brace {in} the text",
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("prefers the outer adapter error over a nested error.message", () => {
|
|
100
|
+
// JSON-RPC style: the real failure is the top-level error.message; a nested
|
|
101
|
+
// error.data.error.message must not shadow it.
|
|
102
|
+
const stderr =
|
|
103
|
+
`{"error":{"message":"outer adapter failure",` +
|
|
104
|
+
`"data":{"error":{"message":"inner debug detail"}}}}`;
|
|
105
|
+
expect(deriveFailureError("Internal error", stderr)).toBe(
|
|
106
|
+
"outer adapter failure",
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derives a human-meaningful failure message for a failed ACP prompt.
|
|
3
|
+
*
|
|
4
|
+
* ACP adapters (e.g. codex-acp) frequently ack a failure with a generic
|
|
5
|
+
* "Internal error" while printing the real cause to stderr as a JSON blob like
|
|
6
|
+
* `{"error":{"message":"..."}}`. This picks the most specific signal available:
|
|
7
|
+
* a structured stderr error, else the last stderr line, else the ack message.
|
|
8
|
+
*
|
|
9
|
+
* Pure and dependency-free.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// CSI escape sequences (colour codes, cursor moves) that adapters interleave
|
|
13
|
+
// with their log lines: ESC `[`, parameter/intermediate bytes, then final byte.
|
|
14
|
+
const ANSI_ESCAPE = /\u001B\[[0-?]*[ -/]*[@-~]/g;
|
|
15
|
+
|
|
16
|
+
export function deriveFailureError(ackMessage: string, stderr: string): string {
|
|
17
|
+
const clean = stderr.replace(ANSI_ESCAPE, "").trim();
|
|
18
|
+
|
|
19
|
+
// Most precise: a structured adapter error — prefer it over the ack.
|
|
20
|
+
const jsonMessage = lastJsonErrorMessage(clean);
|
|
21
|
+
if (jsonMessage) return jsonMessage;
|
|
22
|
+
|
|
23
|
+
// An already-specific ack is preserved when stderr has no better detail.
|
|
24
|
+
if (ackMessage.length > 0 && ackMessage !== "Internal error")
|
|
25
|
+
return ackMessage;
|
|
26
|
+
|
|
27
|
+
// Generic/empty ack: fall back to the last meaningful stderr line.
|
|
28
|
+
return lastNonEmptyLine(clean) ?? ackMessage;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Returns the `error.message` of the LAST JSON object embedded in `text` whose
|
|
33
|
+
* shape is `{"error":{"message":"..."}}`, or null. Objects may be surrounded by
|
|
34
|
+
* arbitrary log text.
|
|
35
|
+
*
|
|
36
|
+
* A candidate object is attempted at EACH `{`, so a stray unmatched `{` in the
|
|
37
|
+
* surrounding log text never balances and is skipped rather than swallowing a
|
|
38
|
+
* later valid object.
|
|
39
|
+
*/
|
|
40
|
+
function lastJsonErrorMessage(text: string): string | null {
|
|
41
|
+
let found: string | null = null;
|
|
42
|
+
for (let i = 0; i < text.length; i++) {
|
|
43
|
+
if (text[i] !== "{") {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const candidate = balancedObjectAt(text, i);
|
|
47
|
+
if (candidate === null) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const parsed = JSON.parse(candidate) as { error?: { message?: unknown } };
|
|
52
|
+
// A valid top-level object: skip past its end so we don't restart
|
|
53
|
+
// candidates at its nested braces. Otherwise a nested error.message
|
|
54
|
+
// (e.g. a JSON-RPC error.data payload) would shadow the outer adapter
|
|
55
|
+
// error. Only reached on a successful parse, so a stray unmatched brace
|
|
56
|
+
// that balances-but-fails-to-parse still lets inner objects be scanned.
|
|
57
|
+
i += candidate.length - 1;
|
|
58
|
+
const message = parsed.error?.message;
|
|
59
|
+
if (typeof message === "string" && message.length > 0) {
|
|
60
|
+
found = message;
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
// Not valid JSON at this position; keep scanning later braces.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return found;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Returns the balanced `{...}` substring starting at `start`, tracking string
|
|
71
|
+
* literals so braces inside JSON strings don't throw off the depth count, or
|
|
72
|
+
* null if the braces never balance before the text ends.
|
|
73
|
+
*/
|
|
74
|
+
function balancedObjectAt(text: string, start: number): string | null {
|
|
75
|
+
let depth = 0;
|
|
76
|
+
let inString = false;
|
|
77
|
+
let escaped = false;
|
|
78
|
+
for (let i = start; i < text.length; i++) {
|
|
79
|
+
const ch = text[i];
|
|
80
|
+
if (inString) {
|
|
81
|
+
if (escaped) {
|
|
82
|
+
escaped = false;
|
|
83
|
+
} else if (ch === "\\") {
|
|
84
|
+
escaped = true;
|
|
85
|
+
} else if (ch === '"') {
|
|
86
|
+
inString = false;
|
|
87
|
+
}
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (ch === '"') {
|
|
91
|
+
inString = true;
|
|
92
|
+
} else if (ch === "{") {
|
|
93
|
+
depth++;
|
|
94
|
+
} else if (ch === "}") {
|
|
95
|
+
depth--;
|
|
96
|
+
if (depth === 0) {
|
|
97
|
+
return text.slice(start, i + 1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function lastNonEmptyLine(text: string): string | null {
|
|
105
|
+
const lines = text.split("\n");
|
|
106
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
107
|
+
const trimmed = lines[i].trim();
|
|
108
|
+
if (trimmed.length > 0) return trimmed;
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
@@ -145,11 +145,6 @@ async function injectOptionalCredential(
|
|
|
145
145
|
* `binary_not_found` preflight in `resolveAcpAgent` and strictly better
|
|
146
146
|
* than a `warn` + zombie subprocess 10 seconds later.
|
|
147
147
|
*
|
|
148
|
-
* For `gemini` the env var is `GEMINI_API_KEY`, provisioned the same two
|
|
149
|
-
* ways (config.json override wins, vault field `gemini_api_key` second),
|
|
150
|
-
* but it is OPTIONAL: the Gemini CLI supports its own OAuth login, so a
|
|
151
|
-
* vault miss proceeds without the key instead of failing the spawn.
|
|
152
|
-
*
|
|
153
148
|
* For `codex-acp` the env vars are `OPENAI_API_KEY` (vault field
|
|
154
149
|
* `acp/openai_api_key`) and `CODEX_API_KEY` (vault field
|
|
155
150
|
* `acp/codex_api_key`), provisioned the same two ways (config.json
|
|
@@ -182,13 +177,6 @@ export async function prepareAgentEnv(
|
|
|
182
177
|
"(or set it under acp.agents.<id>.env in config.json).",
|
|
183
178
|
);
|
|
184
179
|
}
|
|
185
|
-
} else if (adapterCommand === "gemini") {
|
|
186
|
-
await injectOptionalCredential(
|
|
187
|
-
env,
|
|
188
|
-
"gemini_api_key",
|
|
189
|
-
"GEMINI_API_KEY",
|
|
190
|
-
"Gemini API key for ACP agent authentication",
|
|
191
|
-
);
|
|
192
180
|
} else if (adapterCommand === "codex-acp") {
|
|
193
181
|
// The two reads target independent vault fields and write disjoint env
|
|
194
182
|
// keys, so running them concurrently is safe.
|
|
@@ -65,17 +65,6 @@ describe("resolveAcpAgent", () => {
|
|
|
65
65
|
expect(result.agent.command).toBe("claude-agent-acp");
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
test("falls back to the gemini default profile when no user entry", () => {
|
|
69
|
-
config.setConfig({ agents: {} });
|
|
70
|
-
|
|
71
|
-
const result = resolveAcpAgent("gemini");
|
|
72
|
-
|
|
73
|
-
expect(result.ok).toBe(true);
|
|
74
|
-
if (!result.ok) return;
|
|
75
|
-
expect(result.agent.command).toBe("gemini");
|
|
76
|
-
expect(result.agent.args).toEqual(["--acp"]);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
68
|
test.each([
|
|
80
69
|
["claude code", "claude-agent-acp"],
|
|
81
70
|
["Claude Code", "claude-agent-acp"],
|
|
@@ -83,9 +72,6 @@ describe("resolveAcpAgent", () => {
|
|
|
83
72
|
["claude_code", "claude-agent-acp"],
|
|
84
73
|
["codex cli", "codex-acp"],
|
|
85
74
|
["OpenAI Codex", "codex-acp"],
|
|
86
|
-
["gemini cli", "gemini"],
|
|
87
|
-
["Gemini CLI", "gemini"],
|
|
88
|
-
["google gemini", "gemini"],
|
|
89
75
|
])("alias %p resolves to the %p profile", (alias, command) => {
|
|
90
76
|
config.setConfig({ agents: {} });
|
|
91
77
|
|
|
@@ -152,12 +138,7 @@ describe("resolveAcpAgent", () => {
|
|
|
152
138
|
expect(result.reason).toBe("unknown_agent");
|
|
153
139
|
if (result.reason !== "unknown_agent") return;
|
|
154
140
|
// Defaults plus user-only ids, deduped, in stable order (defaults first).
|
|
155
|
-
expect(result.available).toEqual([
|
|
156
|
-
"claude",
|
|
157
|
-
"codex",
|
|
158
|
-
"gemini",
|
|
159
|
-
"user-only",
|
|
160
|
-
]);
|
|
141
|
+
expect(result.available).toEqual(["claude", "codex", "user-only"]);
|
|
161
142
|
});
|
|
162
143
|
|
|
163
144
|
test("unknown_agent available list contains both defaults when user config is empty", () => {
|
|
@@ -171,7 +152,6 @@ describe("resolveAcpAgent", () => {
|
|
|
171
152
|
if (result.reason !== "unknown_agent") return;
|
|
172
153
|
expect(result.available).toContain("claude");
|
|
173
154
|
expect(result.available).toContain("codex");
|
|
174
|
-
expect(result.available).toContain("gemini");
|
|
175
155
|
});
|
|
176
156
|
|
|
177
157
|
test("returns binary_not_found with the registered install hint", () => {
|
|
@@ -333,7 +313,7 @@ describe("listAcpAgents", () => {
|
|
|
333
313
|
const result = listAcpAgents();
|
|
334
314
|
|
|
335
315
|
const ids = result.agents.map((a) => a.id);
|
|
336
|
-
expect(ids).toEqual(["claude", "codex"
|
|
316
|
+
expect(ids).toEqual(["claude", "codex"]);
|
|
337
317
|
for (const entry of result.agents) {
|
|
338
318
|
expect(entry.source).toBe("default");
|
|
339
319
|
expect(entry.available).toBe(true);
|
|
@@ -355,7 +335,6 @@ describe("listAcpAgents", () => {
|
|
|
355
335
|
which.setWhich({
|
|
356
336
|
"my-claude": "/usr/bin/my-claude",
|
|
357
337
|
"codex-acp": "/usr/bin/codex-acp",
|
|
358
|
-
gemini: "/usr/bin/gemini",
|
|
359
338
|
});
|
|
360
339
|
|
|
361
340
|
const result = listAcpAgents();
|
|
@@ -385,7 +364,6 @@ describe("listAcpAgents", () => {
|
|
|
385
364
|
expect(result.agents.map((a) => a.command)).toEqual([
|
|
386
365
|
"claude-agent-acp",
|
|
387
366
|
"codex-acp",
|
|
388
|
-
"gemini",
|
|
389
367
|
]);
|
|
390
368
|
});
|
|
391
369
|
|
|
@@ -401,30 +379,15 @@ describe("listAcpAgents", () => {
|
|
|
401
379
|
expect(codex?.setupHint).toBe("bun add -g @zed-industries/codex-acp");
|
|
402
380
|
});
|
|
403
381
|
|
|
404
|
-
test("unavailable gemini surfaces the @google/gemini-cli install hint", () => {
|
|
405
|
-
config.setConfig({ agents: {} });
|
|
406
|
-
which.setWhich({
|
|
407
|
-
"claude-agent-acp": "/usr/bin/claude-agent-acp",
|
|
408
|
-
"codex-acp": "/usr/bin/codex-acp",
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
const result = listAcpAgents();
|
|
412
|
-
|
|
413
|
-
const gemini = result.agents.find((a) => a.id === "gemini");
|
|
414
|
-
expect(gemini?.available).toBe(false);
|
|
415
|
-
expect(gemini?.unavailableReason).toBe("'gemini' is not on PATH");
|
|
416
|
-
expect(gemini?.setupHint).toBe("bun add -g @google/gemini-cli");
|
|
417
|
-
});
|
|
418
|
-
|
|
419
382
|
test("aliases are resolution sugar, not catalog entries", () => {
|
|
420
383
|
config.setConfig({ agents: {} });
|
|
421
384
|
|
|
422
|
-
// "
|
|
423
|
-
expect(resolveAcpAgent("
|
|
385
|
+
// "codex cli" resolves via the alias...
|
|
386
|
+
expect(resolveAcpAgent("codex cli").ok).toBe(true);
|
|
424
387
|
|
|
425
388
|
// ...but the catalog lists only canonical ids.
|
|
426
389
|
const ids = listAcpAgents().agents.map((a) => a.id);
|
|
427
|
-
expect(ids).toEqual(["claude", "codex"
|
|
390
|
+
expect(ids).toEqual(["claude", "codex"]);
|
|
428
391
|
});
|
|
429
392
|
|
|
430
393
|
test("user-only agent appended after defaults in stable order", () => {
|
|
@@ -440,7 +403,6 @@ describe("listAcpAgents", () => {
|
|
|
440
403
|
which.setWhich({
|
|
441
404
|
"claude-agent-acp": "/x",
|
|
442
405
|
"codex-acp": "/x",
|
|
443
|
-
gemini: "/x",
|
|
444
406
|
"my-binary": "/x",
|
|
445
407
|
});
|
|
446
408
|
|
|
@@ -449,10 +411,9 @@ describe("listAcpAgents", () => {
|
|
|
449
411
|
expect(result.agents.map((a) => a.id)).toEqual([
|
|
450
412
|
"claude",
|
|
451
413
|
"codex",
|
|
452
|
-
"gemini",
|
|
453
414
|
"my-agent",
|
|
454
415
|
]);
|
|
455
|
-
const userOnly = result.agents[
|
|
416
|
+
const userOnly = result.agents[2];
|
|
456
417
|
expect(userOnly.source).toBe("config");
|
|
457
418
|
expect(userOnly.description).toBe("user-only");
|
|
458
419
|
});
|