@vellumai/assistant 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +32 -49
- package/Dockerfile +1 -0
- package/README.md +1 -2
- package/__tests__/permissions/gateway-threshold-reader.test.ts +9 -3
- package/bun.lock +26 -26
- package/docs/architecture/security.md +20 -0
- package/docs/plugins.md +7 -9
- package/knip.json +1 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +39 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +11 -0
- package/node_modules/@vellumai/service-contracts/package.json +2 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +4 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +107 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +5 -1
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +24 -0
- package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +84 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +9 -0
- package/node_modules/@vellumai/twilio-client/bun.lock +24 -0
- package/node_modules/@vellumai/twilio-client/package.json +18 -0
- package/node_modules/@vellumai/twilio-client/src/__tests__/twilio-client.test.ts +128 -0
- package/node_modules/@vellumai/twilio-client/src/index.ts +179 -0
- package/node_modules/@vellumai/twilio-client/tsconfig.json +20 -0
- package/openapi.yaml +565 -12
- package/package.json +6 -3
- package/src/__tests__/app-builder-tool-scripts.test.ts +3 -3
- package/src/__tests__/app-bundler.test.ts +170 -1
- package/src/__tests__/app-control-flow.test.ts +374 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +98 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +621 -0
- package/src/__tests__/app-executors.test.ts +30 -43
- package/src/__tests__/approval-routes-http.test.ts +23 -6
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +146 -0
- package/src/__tests__/assistant-event-hub-targeted.test.ts +257 -0
- package/src/__tests__/assistant-event-hub.test.ts +109 -2
- package/src/__tests__/assistant-event.test.ts +10 -0
- package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -2
- package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -7
- package/src/__tests__/background-shell-host-bash.test.ts +14 -15
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +44 -0
- package/src/__tests__/btw-routes.test.ts +13 -4
- package/src/__tests__/call-controller.test.ts +49 -1
- package/src/__tests__/call-domain.test.ts +0 -2
- package/src/__tests__/call-routes-http.test.ts +0 -2
- package/src/__tests__/channel-readiness-service.test.ts +59 -1
- package/src/__tests__/checker.test.ts +3 -4
- package/src/__tests__/config-loader-backfill.test.ts +90 -155
- package/src/__tests__/config-loader-platform-defaults.test.ts +196 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -1
- package/src/__tests__/config-set-platform-guard.test.ts +48 -4
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +2 -2
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/conversation-app-control-instantiation.test.ts +392 -0
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +237 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-lifecycle.test.ts +36 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +283 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +6 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +120 -72
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/conversation-slash-commands.test.ts +0 -4
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +317 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +5 -12
- package/src/__tests__/credential-execution-managed-contract.test.ts +3 -131
- package/src/__tests__/credentials-cli.test.ts +5 -12
- package/src/__tests__/cu-unified-flow.test.ts +185 -23
- package/src/__tests__/daemon-credential-client.test.ts +101 -19
- package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -2
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -2
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -1
- package/src/__tests__/heartbeat-service.test.ts +718 -1
- package/src/__tests__/helpers/call-route-handler.ts +7 -1
- package/src/__tests__/host-app-control-proxy.test.ts +602 -0
- package/src/__tests__/host-app-control-routes.test.ts +263 -0
- package/src/__tests__/host-bash-proxy.test.ts +246 -47
- package/src/__tests__/host-bash-routes.test.ts +294 -0
- package/src/__tests__/host-browser-proxy.test.ts +24 -22
- package/src/__tests__/host-browser-routes.test.ts +39 -13
- package/src/__tests__/host-cu-proxy.test.ts +41 -52
- package/src/__tests__/host-cu-routes-targeted.test.ts +300 -0
- package/src/__tests__/host-file-edit-tool.test.ts +47 -1
- package/src/__tests__/host-file-proxy-targeted.test.ts +339 -0
- package/src/__tests__/host-file-proxy.test.ts +37 -43
- package/src/__tests__/host-file-read-tool.test.ts +17 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +262 -0
- package/src/__tests__/host-file-write-tool.test.ts +42 -1
- package/src/__tests__/host-proxy-base.test.ts +312 -0
- package/src/__tests__/host-shell-tool.test.ts +22 -4
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +583 -0
- package/src/__tests__/host-transfer-proxy.test.ts +121 -22
- package/src/__tests__/host-transfer-routes-targeted.test.ts +447 -0
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/identity-intro-cache.test.ts +29 -0
- package/src/__tests__/identity-routes.test.ts +103 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +26 -3
- package/src/__tests__/inline-command-runner.test.ts +0 -1
- package/src/__tests__/inline-skill-load-permissions.test.ts +5 -11
- package/src/__tests__/integration-status.test.ts +85 -5
- package/src/__tests__/intent-routing.test.ts +0 -1
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +95 -5
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +17 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
- package/src/__tests__/mcp-auth-routes.test.ts +197 -0
- package/src/__tests__/mcp-cli.test.ts +338 -2
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
- package/src/__tests__/migration-import-commit-http.test.ts +108 -2
- package/src/__tests__/mock-gateway-ipc.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +0 -2
- package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
- package/src/__tests__/prechat-onboarding-contract.test.ts +3 -1
- package/src/__tests__/process-message-background-slack.test.ts +2 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
- package/src/__tests__/public-ingress-urls.test.ts +97 -0
- package/src/__tests__/require-fresh-approval.test.ts +0 -1
- package/src/__tests__/retry-backoff.test.ts +87 -0
- package/src/__tests__/runtime-events-sse.test.ts +10 -6
- package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
- package/src/__tests__/schedule-retry.test.ts +715 -0
- package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
- package/src/__tests__/skill-feature-flags.test.ts +43 -41
- package/src/__tests__/skill-load-feature-flag.test.ts +13 -14
- package/src/__tests__/skill-load-inline-command.test.ts +0 -51
- package/src/__tests__/skill-load-inline-includes.test.ts +0 -43
- package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
- package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
- package/src/__tests__/slack-channel-config.test.ts +9 -14
- package/src/__tests__/system-prompt-ask-mode.test.ts +0 -1
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/telegram-config.test.ts +0 -1
- package/src/__tests__/test-preload.ts +8 -0
- package/src/__tests__/tool-approval-handler.test.ts +3 -4
- package/src/__tests__/tool-audit-listener.test.ts +48 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -1
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/twilio-config.test.ts +3 -16
- package/src/__tests__/twilio-routes.test.ts +3 -5
- package/src/__tests__/twilio-validation.test.ts +93 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -4
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -4
- package/src/__tests__/voice-ingress-preflight.test.ts +19 -0
- package/src/__tests__/workspace-migration-006-services-config.test.ts +3 -2
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +1 -5
- package/src/__tests__/workspace-migration-down-functions.test.ts +8 -8
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
- package/src/backup/__tests__/paths.test.ts +0 -22
- package/src/backup/__tests__/restore.test.ts +51 -151
- package/src/backup/paths.ts +2 -18
- package/src/backup/restore.ts +107 -231
- package/src/bundler/app-bundler.ts +51 -3
- package/src/calls/relay-server.ts +4 -44
- package/src/calls/twilio-config.ts +2 -17
- package/src/calls/twilio-rest.ts +33 -105
- package/src/calls/twilio-routes.ts +11 -12
- package/src/channels/types.ts +8 -7
- package/src/cli/commands/__tests__/backup.test.ts +6 -277
- package/src/cli/commands/__tests__/gateway.test.ts +288 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +4 -0
- package/src/cli/commands/__tests__/webhooks.test.ts +0 -1
- package/src/cli/commands/backup.ts +6 -331
- package/src/cli/commands/clients.ts +36 -37
- package/src/cli/commands/contacts.ts +73 -0
- package/src/cli/commands/conversations.ts +2 -5
- package/src/cli/commands/credentials.ts +15 -7
- package/src/cli/commands/domain.ts +66 -15
- package/src/cli/commands/gateway.ts +183 -0
- package/src/cli/commands/keys.ts +9 -6
- package/src/cli/commands/mcp.ts +116 -156
- package/src/cli/commands/memory-v2.ts +296 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +0 -2
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -2
- package/src/cli/commands/platform/__tests__/status.test.ts +13 -15
- package/src/cli/commands/platform/disconnect.ts +5 -4
- package/src/cli/commands/platform/index.ts +0 -18
- package/src/cli/lib/daemon-credential-client.ts +110 -28
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +67 -10
- package/src/config/bundled-skills/acp/SKILL.md +6 -0
- package/src/config/bundled-skills/acp/TOOLS.json +1 -22
- package/src/config/bundled-skills/app-builder/SKILL.md +14 -109
- package/src/config/bundled-skills/app-builder/TOOLS.json +1 -28
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -10
- package/src/config/bundled-skills/app-control/SKILL.md +75 -0
- package/src/config/bundled-skills/app-control/TOOLS.json +299 -0
- package/src/config/bundled-skills/app-control/tools/app-control-click.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-combo.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-drag.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-observe.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-press.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-sequence.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-start.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-stop.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-type.ts +12 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +6 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +67 -43
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -16
- package/src/config/bundled-skills/document/TOOLS.json +0 -8
- package/src/config/bundled-skills/followups/TOOLS.json +0 -12
- package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
- package/src/config/bundled-skills/image-studio/TOOLS.json +0 -4
- package/src/config/bundled-skills/media-processing/TOOLS.json +0 -24
- package/src/config/bundled-skills/messaging/TOOLS.json +0 -40
- package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +19 -4
- package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
- package/src/config/bundled-skills/schedule/TOOLS.json +14 -14
- package/src/config/bundled-skills/sequences/TOOLS.json +0 -36
- package/src/config/bundled-skills/settings/SKILL.md +4 -0
- package/src/config/bundled-skills/settings/TOOLS.json +0 -12
- package/src/config/bundled-skills/skill-management/SKILL.md +6 -0
- package/src/config/bundled-skills/skill-management/TOOLS.json +0 -8
- package/src/config/bundled-skills/subagent/SKILL.md +6 -2
- package/src/config/bundled-skills/subagent/TOOLS.json +0 -20
- package/src/config/bundled-skills/transcribe/SKILL.md +4 -0
- package/src/config/bundled-skills/transcribe/TOOLS.json +0 -4
- package/src/config/bundled-tool-registry.ts +21 -0
- package/src/config/env-registry.ts +0 -2
- package/src/config/env.ts +19 -12
- package/src/config/feature-flag-registry.json +21 -133
- package/src/config/loader.ts +73 -99
- package/src/config/sanitize-for-transfer.ts +2 -0
- package/src/config/schemas/__tests__/memory-lifecycle.test.ts +80 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -4
- package/src/config/schemas/calls.ts +0 -9
- package/src/config/schemas/heartbeat.ts +63 -0
- package/src/config/schemas/ingress.ts +10 -6
- package/src/config/schemas/llm.ts +5 -10
- package/src/config/schemas/memory-lifecycle.ts +77 -24
- package/src/config/schemas/memory-v2.ts +48 -4
- package/src/config/schemas/platform.ts +6 -0
- package/src/config/schemas/services.ts +1 -15
- package/src/config/schemas/skills.ts +0 -6
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/contacts/contact-store.ts +0 -30
- package/src/contacts/contacts-write.ts +0 -27
- package/src/context/window-manager.ts +1 -2
- package/src/credential-execution/feature-gates.ts +10 -10
- package/src/credential-execution/process-manager.ts +12 -41
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +126 -5
- package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
- package/src/daemon/config-watcher.ts +4 -3
- package/src/daemon/conversation-agent-loop-handlers.ts +21 -3
- package/src/daemon/conversation-agent-loop.ts +32 -28
- package/src/daemon/conversation-lifecycle.ts +8 -1
- package/src/daemon/conversation-process.ts +16 -11
- package/src/daemon/conversation-runtime-assembly.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +125 -4
- package/src/daemon/conversation-tool-setup.ts +16 -55
- package/src/daemon/conversation.ts +21 -2
- package/src/daemon/doordash-steps.ts +1 -1
- package/src/daemon/handlers/shared.ts +4 -1
- package/src/daemon/host-app-control-proxy.ts +293 -0
- package/src/daemon/host-bash-proxy.ts +84 -74
- package/src/daemon/host-browser-proxy.ts +67 -82
- package/src/daemon/host-cu-proxy.ts +81 -86
- package/src/daemon/host-file-proxy.ts +93 -69
- package/src/daemon/host-proxy-base.ts +294 -0
- package/src/daemon/host-proxy-preactivation.ts +82 -0
- package/src/daemon/host-transfer-proxy.ts +247 -129
- package/src/daemon/lifecycle.ts +115 -117
- package/src/daemon/message-protocol.ts +3 -8
- package/src/daemon/message-types/contacts.ts +23 -1
- package/src/daemon/message-types/conversations.ts +11 -8
- package/src/daemon/message-types/host-app-control.ts +150 -0
- package/src/daemon/message-types/host-bash.ts +4 -0
- package/src/daemon/message-types/host-cu.ts +2 -0
- package/src/daemon/message-types/host-file.ts +4 -0
- package/src/daemon/message-types/host-transfer.ts +3 -0
- package/src/daemon/message-types/schedules.ts +8 -3
- package/src/daemon/message-types/skills.ts +2 -2
- package/src/daemon/process-message.ts +18 -1
- package/src/daemon/shutdown-handlers.ts +0 -3
- package/src/daemon/tool-setup-types.ts +51 -0
- package/src/daemon/tool-side-effects.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -1
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +15 -7
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +216 -0
- package/src/heartbeat/heartbeat-run-store.ts +236 -0
- package/src/heartbeat/heartbeat-service.ts +280 -49
- package/src/home/__tests__/post-connect-feed.test.ts +99 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +11 -9
- package/src/home/__tests__/suggested-prompts.test.ts +89 -0
- package/src/home/post-connect-feed.ts +68 -0
- package/src/home/relationship-state-writer.ts +17 -92
- package/src/home/suggested-prompts.ts +46 -10
- package/src/inbound/public-ingress-urls.ts +32 -34
- package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
- package/src/ipc/assistant-server.ts +14 -1
- package/src/ipc/cli-client.ts +32 -1
- package/src/live-voice/live-voice-metrics.ts +10 -10
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +304 -0
- package/src/mcp/mcp-auth-orchestrator.ts +213 -0
- package/src/mcp/mcp-auth-state.ts +133 -0
- package/src/mcp/mcp-oauth-provider.ts +19 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
- package/src/memory/__tests__/qdrant-client-sentinel.test.ts +49 -0
- package/src/memory/__tests__/sparse-tokenize.test.ts +66 -0
- package/src/memory/anisotropy.test.ts +247 -0
- package/src/memory/anisotropy.ts +443 -0
- package/src/memory/auto-analysis-constants.ts +17 -0
- package/src/memory/auto-analysis-guard.ts +5 -15
- package/src/memory/canonical-guardian-store.ts +7 -7
- package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +122 -0
- package/src/memory/context-search/agent-protocol.ts +6 -6
- package/src/memory/context-search/agent-runner.ts +32 -7
- package/src/memory/context-search/sources/memory-v2.ts +17 -5
- package/src/memory/conversation-crud.ts +1 -1
- package/src/memory/conversation-key-store.ts +2 -15
- package/src/memory/db-init.ts +4 -0
- package/src/memory/embedding-backend.ts +9 -21
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +49 -4
- package/src/memory/graph/conversation-graph-memory.ts +1 -24
- package/src/memory/graph/graph-search.ts +8 -0
- package/src/memory/graph/retriever.ts +28 -0
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +8 -2
- package/src/memory/jobs/embed-concept-page.ts +28 -2
- package/src/memory/jobs/embed-pkb-file.test.ts +2 -2
- package/src/memory/jobs-store.ts +66 -22
- package/src/memory/jobs-worker.ts +112 -63
- package/src/memory/memory-v2-activation-log-store.ts +1 -1
- package/src/memory/migrations/237-heartbeat-runs.ts +45 -0
- package/src/memory/migrations/238-schedule-retry-policy.ts +20 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/pkb/pkb-search.ts +7 -0
- package/src/memory/qdrant-client.ts +50 -20
- package/src/memory/schema/infrastructure.ts +15 -0
- package/src/memory/search/semantic.ts +7 -0
- package/src/memory/sparse-tokenize.ts +49 -0
- package/src/memory/v2/__tests__/activation.test.ts +77 -95
- package/src/memory/v2/__tests__/injection.test.ts +43 -21
- package/src/memory/v2/__tests__/sim.test.ts +166 -6
- package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
- package/src/memory/v2/__tests__/static-context.test.ts +0 -1
- package/src/memory/v2/activation.ts +69 -88
- package/src/memory/v2/consolidation-job.ts +3 -5
- package/src/memory/v2/constants.ts +7 -0
- package/src/memory/v2/injection.ts +86 -53
- package/src/memory/v2/prompts/consolidation.ts +312 -91
- package/src/memory/v2/qdrant.ts +99 -1
- package/src/memory/v2/sim.ts +126 -16
- package/src/memory/v2/skill-qdrant.ts +12 -3
- package/src/memory/v2/skill-store.ts +16 -1
- package/src/memory/v2/sparse-bm25.ts +245 -0
- package/src/memory/v2/static-context.ts +6 -5
- package/src/messaging/providers/gmail/types.ts +0 -49
- package/src/messaging/providers/slack/adapter.ts +1 -31
- package/src/messaging/providers/slack/types.ts +0 -32
- package/src/notifications/README.md +10 -10
- package/src/notifications/broadcaster.ts +1 -1
- package/src/notifications/guardian-question-mode.ts +5 -5
- package/src/oauth/connect-orchestrator.ts +4 -0
- package/src/oauth/credential-token-resolver.ts +1 -3
- package/src/oauth/manual-token-connection.ts +0 -4
- package/src/outbound-proxy/index.ts +1 -37
- package/src/outbound-proxy/logging.ts +1 -1
- package/src/outbound-proxy/policy.ts +6 -5
- package/src/outbound-proxy/router.ts +2 -1
- package/src/permissions/approval-policy.test.ts +6 -275
- package/src/permissions/approval-policy.ts +0 -51
- package/src/permissions/checker.test.ts +0 -1
- package/src/permissions/checker.ts +3 -17
- package/src/permissions/gateway-threshold-reader.ts +2 -0
- package/src/permissions/prompter.ts +34 -1
- package/src/permissions/secret-prompter.ts +6 -2
- package/src/prompts/bootstrap-cleanup.ts +27 -0
- package/src/prompts/system-prompt.ts +3 -18
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/providers/speech-to-text/provider-catalog.ts +7 -8
- package/src/runtime/assistant-event-hub.ts +118 -96
- package/src/runtime/assistant-event.ts +1 -0
- package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
- package/src/runtime/auth/middleware.ts +0 -96
- package/src/runtime/auth/route-policy.ts +19 -0
- package/src/runtime/btw-sidechain.ts +2 -3
- package/src/runtime/channel-invite-transport.ts +2 -48
- package/src/runtime/channel-invite-transports/email.ts +1 -1
- package/src/runtime/channel-invite-transports/slack.ts +1 -1
- package/src/runtime/channel-invite-transports/telegram.ts +1 -1
- package/src/runtime/channel-invite-transports/voice.ts +1 -1
- package/src/runtime/channel-invite-transports/whatsapp.ts +1 -1
- package/src/runtime/channel-invite-types.ts +54 -0
- package/src/runtime/channel-readiness-service.ts +32 -13
- package/src/runtime/http-server.ts +3 -329
- package/src/runtime/http-types.ts +0 -5
- package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +413 -0
- package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +260 -0
- package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +189 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +153 -1
- package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +451 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +515 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +437 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +319 -0
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +51 -1
- package/src/runtime/migrations/migration-transport.ts +7 -7
- package/src/runtime/migrations/vbundle-builder.ts +327 -60
- package/src/runtime/migrations/vbundle-import-analyzer.ts +4 -4
- package/src/runtime/migrations/vbundle-import-policy.ts +172 -0
- package/src/runtime/migrations/vbundle-importer.ts +245 -68
- package/src/runtime/migrations/vbundle-streaming-importer.ts +326 -35
- package/src/runtime/migrations/vbundle-streaming-validator.ts +157 -4
- package/src/runtime/migrations/vbundle-tar-stream.ts +15 -6
- package/src/runtime/migrations/vbundle-validator.ts +114 -0
- package/src/runtime/pending-interactions.ts +35 -9
- package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
- package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +242 -0
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +112 -0
- package/src/runtime/routes/approval-interception-types.ts +13 -0
- package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +1 -1
- package/src/runtime/routes/backup-routes.ts +15 -38
- package/src/runtime/routes/btw-routes.ts +14 -37
- package/src/runtime/routes/client-routes.ts +1 -0
- package/src/runtime/routes/contact-prompt-routes.ts +183 -0
- package/src/runtime/routes/conversation-query-routes.ts +36 -1
- package/src/runtime/routes/conversation-routes.ts +30 -13
- package/src/runtime/routes/document-pdf-renderer.ts +165 -0
- package/src/runtime/routes/documents-routes.ts +30 -0
- package/src/runtime/routes/errors.ts +19 -4
- package/src/runtime/routes/events-routes.ts +12 -6
- package/src/runtime/routes/gateway-log-routes.ts +79 -0
- package/src/runtime/routes/guardian-approval-interception.ts +2 -8
- package/src/runtime/routes/heartbeat-routes.ts +103 -38
- package/src/runtime/routes/host-app-control-routes.ts +134 -0
- package/src/runtime/routes/host-bash-routes.ts +36 -6
- package/src/runtime/routes/host-browser-routes.ts +108 -13
- package/src/runtime/routes/host-cu-routes.ts +44 -14
- package/src/runtime/routes/host-file-routes.ts +33 -10
- package/src/runtime/routes/host-transfer-routes.ts +64 -24
- package/src/runtime/routes/http-adapter.ts +1 -0
- package/src/runtime/routes/identity-intro-cache.ts +30 -0
- package/src/runtime/routes/identity-routes.ts +15 -43
- package/src/runtime/routes/inbound-message-handler.ts +1 -9
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -7
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +0 -8
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -20
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +5 -13
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/mcp-auth-routes.ts +132 -0
- package/src/runtime/routes/memory-item-routes.ts +10 -12
- package/src/runtime/routes/memory-v2-routes.ts +441 -1
- package/src/runtime/routes/migration-routes.ts +96 -0
- package/src/runtime/routes/schedule-routes.ts +7 -0
- package/src/runtime/verification-templates.ts +4 -7
- package/src/schedule/integration-status.ts +66 -2
- package/src/schedule/recurrence-engine.ts +4 -1
- package/src/schedule/retry-backoff.ts +18 -0
- package/src/schedule/retry-policy.ts +82 -0
- package/src/schedule/schedule-recovery.ts +64 -0
- package/src/schedule/schedule-store.ts +106 -2
- package/src/schedule/scheduler-types.ts +25 -0
- package/src/schedule/scheduler.ts +63 -38
- package/src/security/oauth-callback-registry.ts +8 -0
- package/src/sequence/analytics.ts +5 -5
- package/src/sequence/engine.ts +1 -1
- package/src/skills/catalog-files.ts +2 -8
- package/src/skills/include-graph.ts +5 -5
- package/src/skills/remote-skill-policy.ts +5 -5
- package/src/skills/skill-file-provider.ts +1 -1
- package/src/skills/skill-file-types.ts +13 -0
- package/src/skills/skillssh-audit-types.ts +28 -0
- package/src/skills/skillssh-registry.ts +8 -21
- package/src/telemetry/types.ts +2 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +21 -0
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/app-control/skill-proxy-bridge.ts +28 -0
- package/src/tools/apps/executors.ts +56 -69
- package/src/tools/browser/__tests__/browser-status.test.ts +21 -18
- package/src/tools/browser/browser-execution.ts +2 -2
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +55 -4
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +12 -6
- package/src/tools/browser/cdp-client/factory.ts +23 -24
- package/src/tools/browser/cdp-client/index.ts +1 -14
- package/src/tools/computer-use/definitions.ts +42 -20
- package/src/tools/executor.ts +2 -0
- package/src/tools/host-filesystem/edit.ts +26 -0
- package/src/tools/host-filesystem/read.ts +26 -0
- package/src/tools/host-filesystem/transfer.ts +31 -1
- package/src/tools/host-filesystem/write.ts +26 -0
- package/src/tools/host-terminal/host-shell.ts +58 -0
- package/src/tools/schedule/create.ts +6 -0
- package/src/tools/schedule/list.ts +2 -0
- package/src/tools/schedule/update.ts +10 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +2 -0
- package/src/tools/shared/filesystem/path-policy.ts +25 -1
- package/src/tools/skills/load.ts +0 -32
- package/src/tools/tool-approval-handler.ts +1 -5
- package/src/tools/types.ts +4 -0
- package/src/usage/pricing.ts +1 -1
- package/src/workspace/hatched-date.ts +86 -0
- package/src/workspace/migrations/003-seed-device-id.ts +1 -1
- package/src/workspace/migrations/006-services-config.ts +8 -5
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +3 -9
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +4 -10
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +4 -10
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +4 -11
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +3 -10
- package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +3 -2
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +2 -1
- package/src/workspace/migrations/059-move-pid-to-workspace.ts +3 -8
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +3 -8
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
- package/src/workspace/migrations/utils.ts +21 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +0 -443
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +0 -226
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +0 -427
- package/src/__tests__/twilio-rest.test.ts +0 -34
- package/src/backup/__tests__/backup-key.test.ts +0 -152
- package/src/backup/__tests__/backup-worker.test.ts +0 -782
- package/src/backup/__tests__/offsite-writer.test.ts +0 -641
- package/src/backup/__tests__/stream-crypt.test.ts +0 -228
- package/src/backup/backup-key.ts +0 -137
- package/src/backup/backup-worker.ts +0 -472
- package/src/backup/offsite-writer.ts +0 -222
- package/src/backup/stream-crypt.ts +0 -263
- package/src/daemon/message-types/pairing.ts +0 -58
- package/src/outbound-proxy/config.ts +0 -20
- package/src/outbound-proxy/health.ts +0 -18
- package/src/outbound-proxy/types.ts +0 -150
- package/src/runtime/capability-tokens.ts +0 -190
- package/src/signals/mcp-reload.ts +0 -18
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
// ─── Mocks ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
let mockConnectedProviders = new Set<string>();
|
|
6
|
+
|
|
7
|
+
mock.module("../../oauth/oauth-store.js", () => ({
|
|
8
|
+
isProviderConnected: async (provider: string) =>
|
|
9
|
+
mockConnectedProviders.has(provider),
|
|
10
|
+
listProviders: () => [
|
|
11
|
+
{ provider: "google" },
|
|
12
|
+
{ provider: "slack" },
|
|
13
|
+
{ provider: "notion" },
|
|
14
|
+
{ provider: "linear" },
|
|
15
|
+
{ provider: "github" },
|
|
16
|
+
],
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
mock.module("../../util/logger.js", () => ({
|
|
20
|
+
getLogger: () =>
|
|
21
|
+
new Proxy({} as Record<string, unknown>, {
|
|
22
|
+
get: () => () => {},
|
|
23
|
+
}),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
const { getSuggestedPrompts } = await import("../suggested-prompts.js");
|
|
27
|
+
|
|
28
|
+
// ─── Tests ─────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
describe("getSuggestedPrompts", () => {
|
|
31
|
+
test("shows 'Connect X' prompts when providers are disconnected", async () => {
|
|
32
|
+
mockConnectedProviders = new Set();
|
|
33
|
+
|
|
34
|
+
const prompts = await getSuggestedPrompts();
|
|
35
|
+
const ids = prompts.map((p) => p.id);
|
|
36
|
+
|
|
37
|
+
expect(ids).toContain("connect-google");
|
|
38
|
+
expect(ids).toContain("connect-slack");
|
|
39
|
+
expect(prompts.find((p) => p.id === "connect-google")!.label).toBe(
|
|
40
|
+
"Connect Gmail",
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("shows email management prompts when Google is connected", async () => {
|
|
45
|
+
mockConnectedProviders = new Set(["google"]);
|
|
46
|
+
|
|
47
|
+
const prompts = await getSuggestedPrompts();
|
|
48
|
+
const ids = prompts.map((p) => p.id);
|
|
49
|
+
|
|
50
|
+
// Should NOT show "Connect Gmail"
|
|
51
|
+
expect(ids).not.toContain("connect-google");
|
|
52
|
+
|
|
53
|
+
// Should show management prompts
|
|
54
|
+
expect(ids).toContain("manage-google-triage-my-inbox");
|
|
55
|
+
expect(ids).toContain("manage-google-summarize-today's-emails");
|
|
56
|
+
|
|
57
|
+
const triage = prompts.find(
|
|
58
|
+
(p) => p.id === "manage-google-triage-my-inbox",
|
|
59
|
+
);
|
|
60
|
+
expect(triage).toBeDefined();
|
|
61
|
+
expect(triage!.label).toBe("Triage my inbox");
|
|
62
|
+
expect(triage!.icon).toBe("mail");
|
|
63
|
+
expect(triage!.source).toBe("deterministic");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("still shows Connect prompts for disconnected providers alongside management prompts", async () => {
|
|
67
|
+
mockConnectedProviders = new Set(["google"]);
|
|
68
|
+
|
|
69
|
+
const prompts = await getSuggestedPrompts();
|
|
70
|
+
const ids = prompts.map((p) => p.id);
|
|
71
|
+
|
|
72
|
+
// Gmail management prompts
|
|
73
|
+
expect(ids).toContain("manage-google-triage-my-inbox");
|
|
74
|
+
// Slack still disconnected
|
|
75
|
+
expect(ids).toContain("connect-slack");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("providers without connectedPrompts show nothing when connected", async () => {
|
|
79
|
+
mockConnectedProviders = new Set(["slack"]);
|
|
80
|
+
|
|
81
|
+
const prompts = await getSuggestedPrompts();
|
|
82
|
+
const ids = prompts.map((p) => p.id);
|
|
83
|
+
|
|
84
|
+
// No connect prompt since connected
|
|
85
|
+
expect(ids).not.toContain("connect-slack");
|
|
86
|
+
// No management prompts since Slack doesn't define any
|
|
87
|
+
expect(ids.filter((id) => id.startsWith("manage-slack"))).toHaveLength(0);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-connection feed nudge.
|
|
3
|
+
*
|
|
4
|
+
* Emits a one-time nudge feed item when the user successfully connects
|
|
5
|
+
* an email-capable OAuth provider. The nudge highlights ongoing email
|
|
6
|
+
* management capabilities (inbox triage, daily digests) so the user
|
|
7
|
+
* discovers what they can do beyond the initial setup.
|
|
8
|
+
*
|
|
9
|
+
* Uses a deterministic id (`connect-nudge:<service>`) so reconnecting
|
|
10
|
+
* the same provider replaces the existing nudge in place rather than
|
|
11
|
+
* appending a duplicate.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { FeedItem } from "./feed-types.js";
|
|
15
|
+
import { appendFeedItem } from "./feed-writer.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Services that should trigger an email management nudge on connection.
|
|
19
|
+
* Only providers with real email integration are listed — see
|
|
20
|
+
* `relationship-state-writer.ts` for the same "only Gmail is real" note.
|
|
21
|
+
*/
|
|
22
|
+
const EMAIL_SERVICES = new Set(["google"]);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Emit a feed nudge for a newly connected email provider.
|
|
26
|
+
*
|
|
27
|
+
* No-ops silently when the service is not email-capable. Never throws —
|
|
28
|
+
* the feed writer's warn-log contract absorbs persistence failures.
|
|
29
|
+
*/
|
|
30
|
+
export async function emitPostConnectNudge(service: string): Promise<void> {
|
|
31
|
+
if (!EMAIL_SERVICES.has(service)) return;
|
|
32
|
+
|
|
33
|
+
const now = new Date();
|
|
34
|
+
const expiresAt = new Date(
|
|
35
|
+
now.getTime() + 7 * 24 * 60 * 60 * 1000,
|
|
36
|
+
).toISOString();
|
|
37
|
+
|
|
38
|
+
const item: FeedItem = {
|
|
39
|
+
id: `connect-nudge:${service}`,
|
|
40
|
+
type: "nudge",
|
|
41
|
+
priority: 70,
|
|
42
|
+
title: "Gmail connected — want ongoing help?",
|
|
43
|
+
summary:
|
|
44
|
+
"I can triage your inbox, summarize new emails, or draft replies to important threads.",
|
|
45
|
+
source: "gmail",
|
|
46
|
+
timestamp: now.toISOString(),
|
|
47
|
+
status: "new",
|
|
48
|
+
expiresAt,
|
|
49
|
+
author: "platform",
|
|
50
|
+
createdAt: now.toISOString(),
|
|
51
|
+
actions: [
|
|
52
|
+
{
|
|
53
|
+
id: "inbox-triage",
|
|
54
|
+
label: "Triage my inbox",
|
|
55
|
+
prompt:
|
|
56
|
+
"Help me triage my inbox — summarize what's unread and flag anything that needs a reply",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "daily-digest",
|
|
60
|
+
label: "Set up daily digest",
|
|
61
|
+
prompt:
|
|
62
|
+
"Set up a daily email digest that summarizes my unread messages each morning",
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
await appendFeedItem(item);
|
|
68
|
+
}
|
|
@@ -18,13 +18,7 @@
|
|
|
18
18
|
* a missing or unreadable file degrades gracefully to an empty string.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import {
|
|
22
|
-
existsSync,
|
|
23
|
-
mkdirSync,
|
|
24
|
-
readFileSync,
|
|
25
|
-
statSync,
|
|
26
|
-
writeFileSync,
|
|
27
|
-
} from "node:fs";
|
|
21
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
28
22
|
import { join } from "node:path";
|
|
29
23
|
|
|
30
24
|
import { countConversations as countConversationsDb } from "../memory/conversation-queries.js";
|
|
@@ -39,6 +33,7 @@ import {
|
|
|
39
33
|
getWorkspaceDir,
|
|
40
34
|
getWorkspacePromptPath,
|
|
41
35
|
} from "../util/platform.js";
|
|
36
|
+
import { resolveAndPersistHatchedAt } from "../workspace/hatched-date.js";
|
|
42
37
|
import { computeProgressPercent, computeTier } from "./progress-formula.js";
|
|
43
38
|
import {
|
|
44
39
|
type Capability,
|
|
@@ -149,11 +144,12 @@ function readOnboardingSidecar(): OnboardingContext | null {
|
|
|
149
144
|
* Reads USER.md / SOUL.md / IDENTITY.md, queries the oauth connection
|
|
150
145
|
* store, and counts conversations via the DB-authoritative helper.
|
|
151
146
|
*
|
|
152
|
-
* Side effect: on the very first call
|
|
153
|
-
*
|
|
154
|
-
* `data/hatched.json`
|
|
155
|
-
* timestamp. All other paths are read-only. Callers
|
|
156
|
-
* persist the full snapshot should use
|
|
147
|
+
* Side effect: on the very first call without an explicit Hatched
|
|
148
|
+
* bullet or existing hatched sidecar, the shared resolver persists a
|
|
149
|
+
* one-time `data/hatched.json` value seeded from IDENTITY.md metadata
|
|
150
|
+
* or a real current timestamp. All other paths are read-only. Callers
|
|
151
|
+
* that want to persist the full snapshot should use
|
|
152
|
+
* `writeRelationshipState()`.
|
|
157
153
|
*/
|
|
158
154
|
export async function computeRelationshipState(): Promise<RelationshipState> {
|
|
159
155
|
// Persona source-of-truth:
|
|
@@ -682,74 +678,17 @@ function countConversations(): number {
|
|
|
682
678
|
}
|
|
683
679
|
}
|
|
684
680
|
|
|
685
|
-
/**
|
|
686
|
-
* Filename for the hatched-date sidecar, used as a stable fallback
|
|
687
|
-
* when IDENTITY.md is missing / unreadable / has no explicit hatched
|
|
688
|
-
* bullet and file stat is unavailable. Lives under the workspace
|
|
689
|
-
* data dir alongside `relationship-state.json`.
|
|
690
|
-
*/
|
|
691
|
-
const HATCHED_SIDECAR_FILENAME = "hatched.json";
|
|
692
|
-
|
|
693
|
-
function getHatchedSidecarPath(): string {
|
|
694
|
-
return join(getDataDir(), HATCHED_SIDECAR_FILENAME);
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
/**
|
|
698
|
-
* Resolve a stable hatched-date fallback timestamp.
|
|
699
|
-
*
|
|
700
|
-
* The Swift client, OpenAPI schema, and UI have no special handling
|
|
701
|
-
* for a Unix-epoch sentinel — they'll render "1/1/1970" to the user.
|
|
702
|
-
* Instead, we use `new Date().toISOString()` the first time a
|
|
703
|
-
* fallback is needed and persist it to a small sidecar file
|
|
704
|
-
* (`data/hatched.json`). Subsequent calls read the sidecar first, so
|
|
705
|
-
* the returned timestamp is monotonic across writes and the
|
|
706
|
-
* `hatchedDate` field never drifts once initialized.
|
|
707
|
-
*
|
|
708
|
-
* Never throws — a sidecar read/write failure still yields a valid
|
|
709
|
-
* (though non-stable) `now` timestamp, which is still far better than
|
|
710
|
-
* the epoch sentinel.
|
|
711
|
-
*/
|
|
712
|
-
function resolveFallbackHatchedDate(): string {
|
|
713
|
-
const path = getHatchedSidecarPath();
|
|
714
|
-
try {
|
|
715
|
-
if (existsSync(path)) {
|
|
716
|
-
const parsed = JSON.parse(readFileSync(path, "utf-8")) as {
|
|
717
|
-
hatchedAt?: string;
|
|
718
|
-
};
|
|
719
|
-
if (parsed.hatchedAt && !isNaN(Date.parse(parsed.hatchedAt))) {
|
|
720
|
-
return parsed.hatchedAt;
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
} catch {
|
|
724
|
-
// Fall through to write a fresh sidecar.
|
|
725
|
-
}
|
|
726
|
-
const now = new Date().toISOString();
|
|
727
|
-
try {
|
|
728
|
-
mkdirSync(getDataDir(), { recursive: true });
|
|
729
|
-
writeFileSync(path, JSON.stringify({ hatchedAt: now }, null, 2), "utf-8");
|
|
730
|
-
} catch {
|
|
731
|
-
// If even the sidecar write fails, return `now` anyway — the
|
|
732
|
-
// caller will just get a fresh-looking date on every call. Not
|
|
733
|
-
// ideal, but far better than the epoch sentinel.
|
|
734
|
-
}
|
|
735
|
-
return now;
|
|
736
|
-
}
|
|
737
|
-
|
|
738
681
|
/**
|
|
739
682
|
* Pull `assistantName` and `hatchedDate` from IDENTITY.md.
|
|
740
683
|
*
|
|
741
684
|
* IDENTITY.md is a freeform markdown file, so for the name we scan
|
|
742
685
|
* bullet lines for any recognizable `name` label (`Name`,
|
|
743
686
|
* `Assistant Name`, `Preferred Name`, etc.). For the hatched date we
|
|
744
|
-
* prefer any explicit `hatched:` / `birth:` bullet, then
|
|
745
|
-
*
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
*
|
|
749
|
-
* boundary, so a raw `Date.now()` fallback would cause `hatchedDate`
|
|
750
|
-
* to drift forward on every write. Instead, when nothing else is
|
|
751
|
-
* readable we resolve via `resolveFallbackHatchedDate()` which
|
|
752
|
-
* persists a stable timestamp to a sidecar file on first use.
|
|
687
|
+
* prefer any explicit `hatched:` / `birth:` bullet, then use the
|
|
688
|
+
* shared hatched-date resolver. That resolver reads an existing
|
|
689
|
+
* `data/hatched.json` sidecar first, otherwise seeds it from valid
|
|
690
|
+
* IDENTITY.md birthtime/mtime or a real current timestamp. This keeps
|
|
691
|
+
* `hatchedDate` stable without writing from read-only HTTP handlers.
|
|
753
692
|
*/
|
|
754
693
|
function parseIdentity(identityPath: string): {
|
|
755
694
|
assistantName: string;
|
|
@@ -792,24 +731,10 @@ function parseIdentity(identityPath: string): {
|
|
|
792
731
|
return { assistantName, hatchedDate: explicitHatched };
|
|
793
732
|
}
|
|
794
733
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
const stats = statSync(identityPath);
|
|
800
|
-
const candidate =
|
|
801
|
-
stats.birthtime.getTime() > 0 ? stats.birthtime : stats.mtime;
|
|
802
|
-
if (candidate.getTime() > 0) {
|
|
803
|
-
return { assistantName, hatchedDate: candidate.toISOString() };
|
|
804
|
-
}
|
|
805
|
-
} catch {
|
|
806
|
-
// File missing or unreadable — fall through to the sidecar.
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
// Last-ditch fallback: `resolveFallbackHatchedDate` returns a stable,
|
|
810
|
-
// real timestamp (persisted to `data/hatched.json` on first call) so
|
|
811
|
-
// the wire contract always carries a valid ISO date.
|
|
812
|
-
return { assistantName, hatchedDate: resolveFallbackHatchedDate() };
|
|
734
|
+
return {
|
|
735
|
+
assistantName,
|
|
736
|
+
hatchedDate: resolveAndPersistHatchedAt(identityPath),
|
|
737
|
+
};
|
|
813
738
|
}
|
|
814
739
|
|
|
815
740
|
/**
|
|
@@ -21,14 +21,34 @@ const log = getLogger("suggested-prompts");
|
|
|
21
21
|
* listed here produce deterministic "Connect X" prompts when disconnected.
|
|
22
22
|
* The icon values are VIcon case names rendered by the macOS client.
|
|
23
23
|
*/
|
|
24
|
+
interface PromptEntry {
|
|
25
|
+
label: string;
|
|
26
|
+
prompt: string;
|
|
27
|
+
icon: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
24
30
|
const CONNECT_PROMPT_META: Record<
|
|
25
31
|
string,
|
|
26
|
-
|
|
32
|
+
PromptEntry & { connectedPrompts?: PromptEntry[] }
|
|
27
33
|
> = {
|
|
28
34
|
google: {
|
|
29
35
|
label: "Connect Gmail",
|
|
30
36
|
prompt: "Help me connect my Gmail account",
|
|
31
37
|
icon: "mail",
|
|
38
|
+
connectedPrompts: [
|
|
39
|
+
{
|
|
40
|
+
label: "Triage my inbox",
|
|
41
|
+
prompt:
|
|
42
|
+
"Help me triage my inbox — summarize what's unread and flag anything that needs a reply",
|
|
43
|
+
icon: "mail",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
label: "Summarize today's emails",
|
|
47
|
+
prompt:
|
|
48
|
+
"Summarize the emails I received today and highlight anything important",
|
|
49
|
+
icon: "mail",
|
|
50
|
+
},
|
|
51
|
+
],
|
|
32
52
|
},
|
|
33
53
|
slack: {
|
|
34
54
|
label: "Connect Slack",
|
|
@@ -75,7 +95,9 @@ export async function getSuggestedPrompts(): Promise<SuggestedPrompt[]> {
|
|
|
75
95
|
|
|
76
96
|
/**
|
|
77
97
|
* Check which well-known OAuth providers are not connected and return
|
|
78
|
-
* a "Connect X" prompt for each.
|
|
98
|
+
* a "Connect X" prompt for each. For connected providers that have
|
|
99
|
+
* `connectedPrompts`, return those instead so users discover ongoing
|
|
100
|
+
* management capabilities.
|
|
79
101
|
*/
|
|
80
102
|
async function getDeterministicPrompts(): Promise<SuggestedPrompt[]> {
|
|
81
103
|
const providers = listProviders();
|
|
@@ -86,15 +108,29 @@ async function getDeterministicPrompts(): Promise<SuggestedPrompt[]> {
|
|
|
86
108
|
if (!meta) continue;
|
|
87
109
|
|
|
88
110
|
const connected = await isProviderConnected(provider.provider);
|
|
89
|
-
if (connected) continue;
|
|
90
111
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
112
|
+
if (!connected) {
|
|
113
|
+
prompts.push({
|
|
114
|
+
id: `connect-${provider.provider}`,
|
|
115
|
+
label: meta.label,
|
|
116
|
+
icon: meta.icon,
|
|
117
|
+
prompt: meta.prompt,
|
|
118
|
+
source: "deterministic",
|
|
119
|
+
});
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (meta.connectedPrompts) {
|
|
124
|
+
for (const cp of meta.connectedPrompts) {
|
|
125
|
+
prompts.push({
|
|
126
|
+
id: `manage-${provider.provider}-${cp.label.toLowerCase().replace(/\s+/g, "-")}`,
|
|
127
|
+
label: cp.label,
|
|
128
|
+
icon: cp.icon,
|
|
129
|
+
prompt: cp.prompt,
|
|
130
|
+
source: "deterministic",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
98
134
|
}
|
|
99
135
|
|
|
100
136
|
return prompts;
|
|
@@ -27,17 +27,30 @@
|
|
|
27
27
|
* All public-facing ingress URL construction is centralized here.
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
+
import {
|
|
31
|
+
buildTwilioConnectActionUrl,
|
|
32
|
+
buildTwilioMediaStreamUrl,
|
|
33
|
+
buildTwilioRelayUrl,
|
|
34
|
+
buildTwilioStatusWebhookUrl,
|
|
35
|
+
buildTwilioVoiceWebhookUrl,
|
|
36
|
+
normalizePublicBaseUrl,
|
|
37
|
+
} from "@vellumai/service-contracts/twilio-ingress";
|
|
38
|
+
|
|
30
39
|
import { getIngressPublicBaseUrl } from "../config/env.js";
|
|
31
40
|
|
|
32
41
|
export interface IngressConfig {
|
|
33
|
-
ingress?: {
|
|
42
|
+
ingress?: {
|
|
43
|
+
enabled?: boolean;
|
|
44
|
+
publicBaseUrl?: string;
|
|
45
|
+
};
|
|
34
46
|
}
|
|
35
47
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
function assertPublicIngressEnabled(config: IngressConfig): void {
|
|
49
|
+
if (config.ingress?.enabled === false) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
"Public ingress is disabled. Ask the assistant to enable it, or update it from the Settings page.",
|
|
52
|
+
);
|
|
53
|
+
}
|
|
41
54
|
}
|
|
42
55
|
|
|
43
56
|
/**
|
|
@@ -51,23 +64,15 @@ function normalizeUrl(url: string): string {
|
|
|
51
64
|
* Throws if no source provides a non-empty value or if ingress is disabled.
|
|
52
65
|
*/
|
|
53
66
|
export function getPublicBaseUrl(config: IngressConfig): string {
|
|
54
|
-
|
|
55
|
-
throw new Error(
|
|
56
|
-
"Public ingress is disabled. Ask the assistant to enable it, or update it from the Settings page.",
|
|
57
|
-
);
|
|
58
|
-
}
|
|
67
|
+
assertPublicIngressEnabled(config);
|
|
59
68
|
|
|
60
69
|
const ingressValue = config.ingress?.publicBaseUrl;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (normalized) return normalized;
|
|
64
|
-
}
|
|
70
|
+
const normalizedIngressValue = normalizePublicBaseUrl(ingressValue);
|
|
71
|
+
if (normalizedIngressValue) return normalizedIngressValue;
|
|
65
72
|
|
|
66
73
|
const ingressEnvValue = getIngressPublicBaseUrl();
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (normalized) return normalized;
|
|
70
|
-
}
|
|
74
|
+
const normalizedIngressEnvValue = normalizePublicBaseUrl(ingressEnvValue);
|
|
75
|
+
if (normalizedIngressEnvValue) return normalizedIngressEnvValue;
|
|
71
76
|
|
|
72
77
|
throw new Error(
|
|
73
78
|
"No public base URL configured. Set ingress.publicBaseUrl in config.",
|
|
@@ -87,27 +92,24 @@ export function getTwilioVoiceWebhookUrl(
|
|
|
87
92
|
config: IngressConfig,
|
|
88
93
|
callSessionId?: string,
|
|
89
94
|
): string {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return `${base}/webhooks/twilio/voice`;
|
|
95
|
+
return buildTwilioVoiceWebhookUrl(
|
|
96
|
+
getPublicBaseUrl(config),
|
|
97
|
+
callSessionId,
|
|
98
|
+
);
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
/**
|
|
98
102
|
* Build the Twilio status callback URL.
|
|
99
103
|
*/
|
|
100
104
|
export function getTwilioStatusCallbackUrl(config: IngressConfig): string {
|
|
101
|
-
|
|
102
|
-
return `${base}/webhooks/twilio/status`;
|
|
105
|
+
return buildTwilioStatusWebhookUrl(getPublicBaseUrl(config));
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
/**
|
|
106
109
|
* Build the Twilio connect-action callback URL.
|
|
107
110
|
*/
|
|
108
111
|
export function getTwilioConnectActionUrl(config: IngressConfig): string {
|
|
109
|
-
|
|
110
|
-
return `${base}/webhooks/twilio/connect-action`;
|
|
112
|
+
return buildTwilioConnectActionUrl(getPublicBaseUrl(config));
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
/**
|
|
@@ -115,9 +117,7 @@ export function getTwilioConnectActionUrl(config: IngressConfig): string {
|
|
|
115
117
|
* Converts http:// → ws:// and https:// → wss://.
|
|
116
118
|
*/
|
|
117
119
|
export function getTwilioRelayUrl(config: IngressConfig): string {
|
|
118
|
-
|
|
119
|
-
const wsBase = base.replace(/^http(s?)/, "ws$1");
|
|
120
|
-
return `${wsBase}/webhooks/twilio/relay`;
|
|
120
|
+
return buildTwilioRelayUrl(getPublicBaseUrl(config));
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
/**
|
|
@@ -127,9 +127,7 @@ export function getTwilioRelayUrl(config: IngressConfig): string {
|
|
|
127
127
|
* Converts http:// → ws:// and https:// → wss://.
|
|
128
128
|
*/
|
|
129
129
|
export function getTwilioMediaStreamUrl(config: IngressConfig): string {
|
|
130
|
-
|
|
131
|
-
const wsBase = base.replace(/^http(s?)/, "ws$1");
|
|
132
|
-
return `${wsBase}/webhooks/twilio/media-stream`;
|
|
130
|
+
return buildTwilioMediaStreamUrl(getPublicBaseUrl(config));
|
|
133
131
|
}
|
|
134
132
|
|
|
135
133
|
/**
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the IPC error envelope built from `RouteError` instances.
|
|
3
|
+
*
|
|
4
|
+
* Asserts that `AssistantIpcServer.buildErrorResponse` forwards the full
|
|
5
|
+
* `RouteError` shape — including the `details` field — into the IPC response
|
|
6
|
+
* envelope so IPC clients (e.g. gateway→daemon) receive the same structured
|
|
7
|
+
* payload as HTTP clients (e.g. `version_incompatible` migration imports).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, expect, test } from "bun:test";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
RouteError,
|
|
14
|
+
UnprocessableEntityError,
|
|
15
|
+
} from "../../runtime/routes/errors.js";
|
|
16
|
+
import { AssistantIpcServer, type IpcResponse } from "../assistant-server.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* `buildErrorResponse` is private; access it through an interface cast so the
|
|
20
|
+
* test exercises the actual production code path without exporting a
|
|
21
|
+
* test-only API on the server class.
|
|
22
|
+
*/
|
|
23
|
+
type PrivateApi = {
|
|
24
|
+
buildErrorResponse(id: string, err: unknown): IpcResponse;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function buildErrorResponse(err: unknown): IpcResponse {
|
|
28
|
+
const server = new AssistantIpcServer() as unknown as PrivateApi;
|
|
29
|
+
return server.buildErrorResponse("req-1", err);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("AssistantIpcServer error envelope", () => {
|
|
33
|
+
test("forwards RouteError message, statusCode, and code", () => {
|
|
34
|
+
const err = new RouteError("boom", "BOOM", 418);
|
|
35
|
+
const response = buildErrorResponse(err);
|
|
36
|
+
|
|
37
|
+
expect(response.id).toBe("req-1");
|
|
38
|
+
expect(response.error).toBe("boom");
|
|
39
|
+
expect(response.statusCode).toBe(418);
|
|
40
|
+
expect(response.errorCode).toBe("BOOM");
|
|
41
|
+
expect(response.errorDetails).toBeUndefined();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("forwards RouteError.details into errorDetails when present", () => {
|
|
45
|
+
const details = {
|
|
46
|
+
reason: "version_incompatible" as const,
|
|
47
|
+
bundle_compat: { engineMin: "0.7.0" },
|
|
48
|
+
runtime_version: "0.6.0",
|
|
49
|
+
};
|
|
50
|
+
const err = new UnprocessableEntityError(
|
|
51
|
+
"incompatible bundle version",
|
|
52
|
+
details,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const response = buildErrorResponse(err);
|
|
56
|
+
|
|
57
|
+
expect(response.errorCode).toBe("UNPROCESSABLE_ENTITY");
|
|
58
|
+
expect(response.statusCode).toBe(422);
|
|
59
|
+
expect(response.errorDetails).toEqual(details);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("omits errorDetails when RouteError has no details", () => {
|
|
63
|
+
const err = new UnprocessableEntityError("plain validation failure");
|
|
64
|
+
|
|
65
|
+
const response = buildErrorResponse(err);
|
|
66
|
+
|
|
67
|
+
expect(response.errorCode).toBe("UNPROCESSABLE_ENTITY");
|
|
68
|
+
// `errorDetails` must be omitted entirely (not `undefined` value) so the
|
|
69
|
+
// serialized JSON envelope stays minimal.
|
|
70
|
+
expect("errorDetails" in response).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("non-RouteError errors are stringified into `error`", () => {
|
|
74
|
+
const response = buildErrorResponse(new Error("raw"));
|
|
75
|
+
|
|
76
|
+
expect(response.error).toBe("Error: raw");
|
|
77
|
+
expect(response.errorCode).toBeUndefined();
|
|
78
|
+
expect(response.errorDetails).toBeUndefined();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -70,6 +70,14 @@ export type IpcResponse = {
|
|
|
70
70
|
statusCode?: number;
|
|
71
71
|
/** Machine-readable error code (e.g. "NOT_FOUND") for RouteError instances. */
|
|
72
72
|
errorCode?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Structured error payload mirroring `RouteError.details` — present only
|
|
75
|
+
* when the originating `RouteError` carries a `details` field (e.g.
|
|
76
|
+
* `version_incompatible` migration imports). Mirrors the HTTP adapter's
|
|
77
|
+
* `error.details` envelope so IPC clients can recover the same
|
|
78
|
+
* machine-readable context as HTTP clients.
|
|
79
|
+
*/
|
|
80
|
+
errorDetails?: unknown;
|
|
73
81
|
headers?: Record<string, string>;
|
|
74
82
|
};
|
|
75
83
|
|
|
@@ -136,6 +144,7 @@ export class AssistantIpcServer {
|
|
|
136
144
|
}
|
|
137
145
|
|
|
138
146
|
// ⚠️ TEMPORARY — gateway→assistant DB proxy (see ipc/routes/db-proxy.ts).
|
|
147
|
+
// This is the ONLY route defined directly here; all other routes go in ROUTES.
|
|
139
148
|
// Remove once contacts/guardian-binding logic is fully migrated to the
|
|
140
149
|
// gateway's own database.
|
|
141
150
|
this.methods.set("db_proxy", (params) =>
|
|
@@ -273,12 +282,16 @@ export class AssistantIpcServer {
|
|
|
273
282
|
|
|
274
283
|
private buildErrorResponse(id: string, err: unknown): IpcResponse {
|
|
275
284
|
if (err instanceof RouteError) {
|
|
276
|
-
|
|
285
|
+
const response: IpcResponse = {
|
|
277
286
|
id,
|
|
278
287
|
error: err.message,
|
|
279
288
|
statusCode: err.statusCode,
|
|
280
289
|
errorCode: err.code,
|
|
281
290
|
};
|
|
291
|
+
if (err.details !== undefined) {
|
|
292
|
+
response.errorDetails = err.details;
|
|
293
|
+
}
|
|
294
|
+
return response;
|
|
282
295
|
}
|
|
283
296
|
return { id, error: String(err) };
|
|
284
297
|
}
|