@vellumai/assistant 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +45 -29
- package/Dockerfile +1 -0
- package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
- package/bun.lock +3 -0
- package/docs/architecture/memory.md +5 -2
- package/knip.json +1 -0
- package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +13 -4
- package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
- package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
- package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -9
- package/node_modules/@vellumai/slack-text/src/index.test.ts +18 -35
- package/node_modules/@vellumai/slack-text/src/index.ts +2 -48
- package/openapi.yaml +470 -25
- package/package.json +3 -1
- package/src/__tests__/annotate-risk-options.test.ts +291 -0
- package/src/__tests__/app-control-flow.test.ts +21 -11
- package/src/__tests__/approval-cascade.test.ts +8 -16
- package/src/__tests__/approval-routes-http.test.ts +6 -0
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/assistant-event.test.ts +0 -10
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -7
- package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -0
- package/src/__tests__/auto-analysis-end-to-end.test.ts +48 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +268 -0
- package/src/__tests__/call-constants.test.ts +10 -1
- package/src/__tests__/call-controller.test.ts +127 -0
- package/src/__tests__/call-conversation-messages.test.ts +8 -2
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +537 -0
- package/src/__tests__/channel-readiness-service.test.ts +4 -2
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +58 -28
- package/src/__tests__/config-loader-backfill.test.ts +379 -0
- package/src/__tests__/config-loader-platform-defaults.test.ts +284 -1
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +18 -9
- package/src/__tests__/config-watcher.test.ts +140 -69
- package/src/__tests__/context-search-agent-runner.test.ts +61 -3
- package/src/__tests__/context-search-conversations-source.test.ts +0 -24
- package/src/__tests__/context-search-fanout.test.ts +0 -1
- package/src/__tests__/context-search-memory-source.test.ts +6 -33
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -2
- package/src/__tests__/context-search-pkb-source.test.ts +12 -7
- package/src/__tests__/context-search-workspace-source.test.ts +0 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +223 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop.test.ts +457 -8
- package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
- package/src/__tests__/conversation-error.test.ts +150 -3
- package/src/__tests__/conversation-init.benchmark.test.ts +1 -1
- package/src/__tests__/conversation-process-callsite.test.ts +38 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +74 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
- package/src/__tests__/conversation-speed-override.test.ts +0 -3
- package/src/__tests__/conversation-store.test.ts +0 -18
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
- package/src/__tests__/conversation-surfaces-app-control.test.ts +15 -4
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +476 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +61 -5
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/credentials-cli.test.ts +7 -0
- package/src/__tests__/cu-unified-flow.test.ts +176 -10
- package/src/__tests__/date-context.test.ts +164 -2
- package/src/__tests__/disk-pressure-guard.test.ts +262 -0
- package/src/__tests__/disk-pressure-lifecycle.test.ts +168 -0
- package/src/__tests__/disk-pressure-policy.test.ts +241 -0
- package/src/__tests__/disk-pressure-routes.test.ts +379 -0
- package/src/__tests__/disk-pressure-tools.test.ts +277 -0
- package/src/__tests__/disk-usage.test.ts +150 -0
- package/src/__tests__/events-client-registration.test.ts +52 -0
- package/src/__tests__/events-dev-bypass-actor.test.ts +162 -0
- package/src/__tests__/file-write-tool.test.ts +4 -10
- package/src/__tests__/filing-service.test.ts +2 -20
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -26
- package/src/__tests__/heartbeat-disk-pressure.test.ts +183 -0
- package/src/__tests__/heartbeat-service.test.ts +260 -11
- package/src/__tests__/host-app-control-proxy.test.ts +195 -25
- package/src/__tests__/host-bash-proxy.test.ts +227 -34
- package/src/__tests__/host-bash-routes.test.ts +178 -13
- package/src/__tests__/host-cu-proxy.test.ts +210 -3
- package/src/__tests__/host-cu-routes-targeted.test.ts +141 -12
- package/src/__tests__/host-file-proxy-targeted.test.ts +48 -9
- package/src/__tests__/host-file-proxy.test.ts +268 -6
- package/src/__tests__/host-file-routes-targeted.test.ts +175 -17
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +408 -59
- package/src/__tests__/host-transfer-routes-targeted.test.ts +232 -17
- package/src/__tests__/http-user-message-parity.test.ts +107 -1
- package/src/__tests__/injector-chain.test.ts +36 -16
- package/src/__tests__/injector-disk-pressure.test.ts +224 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +154 -67
- package/src/__tests__/managed-profile-guard.test.ts +18 -0
- package/src/__tests__/mcp-abort-signal.test.ts +130 -0
- package/src/__tests__/memory-admin-recall.test.ts +3 -11
- package/src/__tests__/memory-retrieval-pipeline.test.ts +22 -1
- package/src/__tests__/normalize-onboarding.test.ts +180 -0
- package/src/__tests__/notification-decision-fallback.test.ts +91 -0
- package/src/__tests__/notification-decision-strategy.test.ts +22 -0
- package/src/__tests__/oauth-cli.test.ts +121 -0
- package/src/__tests__/oauth-connect-routes.test.ts +316 -0
- package/src/__tests__/oauth-provider-seed-logos.test.ts +24 -2
- package/src/__tests__/onboarding-persona-write.test.ts +308 -0
- package/src/__tests__/openai-provider.test.ts +45 -8
- package/src/__tests__/persist-onboarding-artifacts.test.ts +44 -64
- package/src/__tests__/platform-callback-registration.test.ts +21 -4
- package/src/__tests__/platform.test.ts +2 -1
- package/src/__tests__/playbook-execution.test.ts +0 -43
- package/src/__tests__/plugin-tool-contribution.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +214 -27
- package/src/__tests__/provider-tool-name.test.ts +23 -0
- package/src/__tests__/relay-server.test.ts +60 -5
- package/src/__tests__/runtime-events-sse.test.ts +4 -8
- package/src/__tests__/scheduler-disk-pressure.test.ts +148 -0
- package/src/__tests__/secret-ingress-http.test.ts +0 -1
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
- package/src/__tests__/secret-response-routing.test.ts +7 -5
- package/src/__tests__/server-history-render.test.ts +82 -0
- package/src/__tests__/skill-include-graph.test.ts +31 -0
- package/src/__tests__/skill-load-tool.test.ts +44 -16
- package/src/__tests__/skills.test.ts +39 -0
- package/src/__tests__/suggestion-routes.test.ts +46 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
- package/src/__tests__/tool-executor.test.ts +155 -0
- package/src/__tests__/twilio-validation.test.ts +2 -2
- package/src/__tests__/voice-session-bridge.test.ts +3 -0
- package/src/__tests__/workspace-migration-065-bump-stale-heartbeat-interval.test.ts +122 -0
- package/src/__tests__/workspace-migration-066-seed-heartbeat-callsite-cost-default.test.ts +285 -0
- package/src/__tests__/workspace-migration-068-release-notes-local-timezone.test.ts +90 -0
- package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +120 -0
- package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
- package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +78 -0
- package/src/agent/loop.ts +11 -0
- package/src/approvals/guardian-request-resolvers.ts +3 -32
- package/src/backup/snapshot-lock.ts +2 -27
- package/src/bundler/compiler-tools.ts +3 -2
- package/src/calls/call-constants.ts +5 -8
- package/src/calls/call-controller.ts +130 -67
- package/src/calls/call-conversation-messages.ts +46 -10
- package/src/calls/relay-server.ts +7 -1
- package/src/calls/voice-session-bridge.ts +1 -1
- package/src/cli/commands/__tests__/webhooks.test.ts +0 -4
- package/src/cli/commands/bash.ts +35 -108
- package/src/cli/commands/contacts.ts +64 -25
- package/src/cli/commands/credentials.ts +56 -0
- package/src/cli/commands/memory-v2.ts +11 -10
- package/src/cli/commands/oauth/__tests__/connect.test.ts +401 -219
- package/src/cli/commands/oauth/connect.ts +124 -40
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -3
- package/src/cli/commands/platform/__tests__/connect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +103 -6
- package/src/cli/commands/platform/index.ts +16 -7
- package/src/cli/commands/status.ts +57 -0
- package/src/cli/program.ts +4 -2
- package/src/config/assistant-feature-flags.ts +13 -3
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -3
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +13 -7
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +2 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -2
- package/src/config/env.ts +0 -8
- package/src/config/feature-flag-registry.json +13 -5
- package/src/config/loader.ts +199 -27
- package/src/config/schemas/__tests__/memory-v2.test.ts +10 -5
- package/src/config/schemas/call-site-catalog.ts +14 -0
- package/src/config/schemas/channels.ts +0 -5
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +2 -0
- package/src/config/schemas/memory-lifecycle.ts +13 -0
- package/src/config/schemas/memory-v2.ts +76 -12
- package/src/config/schemas/platform.ts +43 -3
- package/src/config/schemas/services.ts +28 -0
- package/src/config/seed-inference-profiles.ts +230 -33
- package/src/contacts/contact-store.ts +0 -25
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +32 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +86 -25
- package/src/daemon/assistant-attachments.ts +4 -4
- package/src/daemon/config-watcher.ts +85 -57
- package/src/daemon/conversation-agent-loop-handlers.ts +38 -0
- package/src/daemon/conversation-agent-loop.ts +183 -43
- package/src/daemon/conversation-error.ts +87 -15
- package/src/daemon/conversation-lifecycle.ts +22 -10
- package/src/daemon/conversation-process.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +26 -0
- package/src/daemon/conversation-store.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +211 -29
- package/src/daemon/conversation-tool-setup.ts +66 -19
- package/src/daemon/conversation.ts +18 -23
- package/src/daemon/date-context.ts +71 -22
- package/src/daemon/disk-pressure-background-gate.ts +73 -0
- package/src/daemon/disk-pressure-guard.ts +343 -0
- package/src/daemon/disk-pressure-policy.ts +163 -0
- package/src/daemon/handlers/shared.ts +26 -1
- package/src/daemon/handlers/skills.ts +3 -4
- package/src/daemon/host-app-control-proxy.ts +137 -41
- package/src/daemon/host-bash-proxy.ts +47 -22
- package/src/daemon/host-browser-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +50 -4
- package/src/daemon/host-file-proxy.ts +44 -8
- package/src/daemon/host-transfer-proxy.ts +97 -6
- package/src/daemon/lifecycle.ts +167 -101
- package/src/daemon/meet-host-supervisor.ts +4 -4
- package/src/daemon/meet-manifest-loader.ts +0 -1
- package/src/daemon/memory-v2-startup.ts +66 -15
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +4 -0
- package/src/daemon/message-types/disk-pressure.ts +9 -0
- package/src/daemon/message-types/messages.ts +22 -1
- package/src/daemon/profiler-run-store.ts +5 -5
- package/src/daemon/tool-setup-types.ts +2 -2
- package/src/documents/document-store.ts +119 -0
- package/src/filing/filing-service.ts +29 -5
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +9 -16
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +36 -0
- package/src/heartbeat/heartbeat-run-store.ts +13 -0
- package/src/heartbeat/heartbeat-service.ts +205 -31
- package/src/home/feed-scheduler.ts +18 -0
- package/src/inbound/platform-callback-registration.ts +8 -15
- package/src/ipc/__tests__/clients-list-ipc.test.ts +169 -0
- package/src/ipc/assistant-server.ts +149 -38
- package/src/ipc/gateway-client.ts +37 -3
- package/src/ipc/skill-server.ts +99 -42
- package/src/live-voice/live-voice-archive.ts +4 -4
- package/src/live-voice/protocol.ts +5 -7
- package/src/media/image-service.ts +1 -7
- package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +21 -13
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +34 -51
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +0 -6
- package/src/memory/__tests__/memory-v2-concept-frequency.test.ts +272 -0
- package/src/memory/admin.ts +5 -9
- package/src/memory/context-search/agent-runner.ts +19 -2
- package/src/memory/context-search/sources/conversations.ts +2 -11
- package/src/memory/context-search/sources/memory-v2.ts +1 -16
- package/src/memory/context-search/sources/memory.ts +2 -3
- package/src/memory/context-search/sources/pkb.ts +2 -3
- package/src/memory/context-search/types.ts +0 -1
- package/src/memory/conversation-crud.ts +4 -12
- package/src/memory/db-init.ts +2 -0
- package/src/memory/embedding-runtime-manager.ts +119 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +136 -82
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
- package/src/memory/graph/conversation-graph-memory.ts +72 -61
- package/src/memory/graph/extraction.ts +1 -3
- package/src/memory/graph/graph-search.test.ts +11 -67
- package/src/memory/graph/graph-search.ts +4 -24
- package/src/memory/graph/retriever.test.ts +12 -1
- package/src/memory/graph/retriever.ts +10 -15
- package/src/memory/graph/tool-handlers.ts +3 -4
- package/src/memory/graph/tools.ts +4 -4
- package/src/memory/indexer.ts +53 -45
- package/src/memory/job-handlers/backfill.ts +2 -11
- package/src/memory/job-handlers/cleanup.ts +43 -0
- package/src/memory/job-handlers/embedding.ts +6 -8
- package/src/memory/job-handlers/summarization.ts +2 -7
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +116 -0
- package/src/memory/jobs/embed-concept-page.ts +223 -87
- package/src/memory/jobs-store.ts +48 -0
- package/src/memory/jobs-worker.ts +85 -43
- package/src/memory/memory-v2-activation-log-store.ts +32 -14
- package/src/memory/memory-v2-concept-frequency.ts +169 -0
- package/src/memory/migrations/239-trace-events-created-at-index.ts +18 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/pkb/pkb-search.test.ts +7 -0
- package/src/memory/pkb/pkb-search.ts +4 -5
- package/src/memory/qdrant-client.ts +3 -13
- package/src/memory/rerank-local.ts +374 -0
- package/src/memory/search/semantic.ts +10 -72
- package/src/memory/trace-event-store.ts +1 -17
- package/src/memory/v2/__tests__/activation.test.ts +346 -255
- package/src/memory/v2/__tests__/consolidation-job.test.ts +61 -40
- package/src/memory/v2/__tests__/injection.test.ts +297 -190
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +61 -2
- package/src/memory/v2/__tests__/qdrant.test.ts +326 -9
- package/src/memory/v2/__tests__/reranker.test.ts +338 -0
- package/src/memory/v2/__tests__/sim.test.ts +113 -196
- package/src/memory/v2/__tests__/skill-store.test.ts +71 -65
- package/src/memory/v2/__tests__/static-context.test.ts +77 -14
- package/src/memory/v2/__tests__/sweep-job.test.ts +19 -33
- package/src/memory/v2/activation.ts +149 -156
- package/src/memory/v2/consolidation-job.ts +69 -20
- package/src/memory/v2/injection.ts +75 -68
- package/src/memory/v2/page-store.ts +39 -0
- package/src/memory/v2/prompts/consolidation.ts +41 -1
- package/src/memory/v2/qdrant.ts +306 -46
- package/src/memory/v2/reranker.ts +177 -0
- package/src/memory/v2/sim.ts +77 -110
- package/src/memory/v2/skill-content.ts +4 -3
- package/src/memory/v2/skill-store.ts +82 -59
- package/src/memory/v2/static-context.ts +26 -8
- package/src/memory/v2/sweep-job.ts +5 -6
- package/src/memory/v2/types.ts +17 -10
- package/src/notifications/copy-composer.ts +47 -0
- package/src/notifications/decision-engine.ts +46 -0
- package/src/notifications/signal.ts +4 -0
- package/src/oauth/AGENTS.md +3 -1
- package/src/oauth/__tests__/oauth-connect-state.test.ts +137 -0
- package/src/oauth/connect-orchestrator.ts +2 -0
- package/src/oauth/connection-resolver.test.ts +66 -1
- package/src/oauth/connection-resolver.ts +55 -1
- package/src/oauth/oauth-connect-state.ts +77 -0
- package/src/oauth/seed-providers.ts +58 -1
- package/src/permissions/gateway-threshold-reader.ts +116 -8
- package/src/permissions/prompter.ts +86 -96
- package/src/permissions/secret-prompter.ts +31 -31
- package/src/plugins/defaults/injectors.ts +36 -4
- package/src/plugins/defaults/memory-retrieval.ts +5 -6
- package/src/plugins/types.ts +7 -0
- package/src/proactive-artifact/aux-message-injector.ts +74 -0
- package/src/proactive-artifact/decision.test.ts +226 -0
- package/src/proactive-artifact/decision.ts +165 -0
- package/src/proactive-artifact/index.ts +7 -0
- package/src/proactive-artifact/job.test.ts +914 -0
- package/src/proactive-artifact/job.ts +366 -0
- package/src/proactive-artifact/message-copy.ts +58 -0
- package/src/proactive-artifact/trigger-state.test.ts +277 -0
- package/src/proactive-artifact/trigger-state.ts +119 -0
- package/src/prompts/normalize-onboarding.ts +80 -0
- package/src/prompts/persona-resolver.ts +101 -9
- package/src/prompts/system-prompt.ts +21 -7
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/SOUL.md +13 -28
- package/src/providers/__tests__/retry-callsite.test.ts +222 -1
- package/src/providers/model-intents.ts +7 -0
- package/src/providers/openrouter/client.ts +8 -0
- package/src/providers/retry.ts +50 -0
- package/src/providers/types.ts +1 -0
- package/src/runtime/__tests__/agent-wake.test.ts +456 -3
- package/src/runtime/agent-wake.ts +238 -100
- package/src/runtime/assistant-event-hub.ts +36 -6
- package/src/runtime/assistant-event.ts +0 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +64 -0
- package/src/runtime/auth/route-policy.ts +15 -1
- package/src/runtime/auth/same-actor.ts +216 -0
- package/src/runtime/channel-approvals.ts +3 -2
- package/src/runtime/channel-retry-sweep.ts +65 -1
- package/src/runtime/local-actor-identity.ts +52 -11
- package/src/runtime/pending-interactions.ts +27 -15
- package/src/runtime/routes/__tests__/client-routes.test.ts +155 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +0 -5
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
- package/src/runtime/routes/approval-routes.ts +7 -3
- package/src/runtime/routes/client-routes.ts +20 -2
- package/src/runtime/routes/consolidation-routes.ts +8 -9
- package/src/runtime/routes/contact-routes.ts +0 -25
- package/src/runtime/routes/conversation-query-routes.ts +44 -1
- package/src/runtime/routes/conversation-routes.ts +35 -26
- package/src/runtime/routes/debug-bash-routes.ts +165 -0
- package/src/runtime/routes/disk-pressure-routes.ts +121 -0
- package/src/runtime/routes/document-pdf-renderer.ts +6 -2
- package/src/runtime/routes/documents-routes.ts +2 -75
- package/src/runtime/routes/events-routes.ts +41 -9
- package/src/runtime/routes/filing-routes.ts +2 -3
- package/src/runtime/routes/host-bash-routes.ts +23 -3
- package/src/runtime/routes/host-cu-routes.ts +33 -6
- package/src/runtime/routes/host-file-routes.ts +32 -6
- package/src/runtime/routes/host-transfer-routes.ts +79 -16
- package/src/runtime/routes/identity-routes.ts +7 -138
- package/src/runtime/routes/inbound-message-handler.ts +77 -12
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/memory-item-routes.test.ts +37 -17
- package/src/runtime/routes/memory-item-routes.ts +5 -6
- package/src/runtime/routes/memory-v2-routes.ts +136 -17
- package/src/runtime/routes/oauth-connect-routes.ts +153 -0
- package/src/runtime/verification-outbound-actions.ts +4 -4
- package/src/schedule/run-script.ts +37 -5
- package/src/schedule/scheduler.ts +20 -1
- package/src/security/encrypted-store.ts +2 -0
- package/src/security/secure-keys.ts +55 -0
- package/src/skills/include-graph.ts +35 -13
- package/src/skills/remote-skill-policy.ts +4 -10
- package/src/subagent/index.ts +1 -7
- package/src/subagent/manager.ts +1 -15
- package/src/tasks/task-runner.ts +0 -1
- package/src/tasks/task-store.ts +0 -3
- package/src/tools/background-tool-registry.ts +17 -3
- package/src/tools/document/document-tool.ts +20 -0
- package/src/tools/executor.ts +18 -2
- package/src/tools/host-filesystem/edit.test.ts +151 -0
- package/src/tools/host-filesystem/edit.ts +43 -1
- package/src/tools/host-filesystem/read.test.ts +129 -0
- package/src/tools/host-filesystem/read.ts +43 -1
- package/src/tools/host-filesystem/transfer.test.ts +127 -2
- package/src/tools/host-filesystem/transfer.ts +56 -11
- package/src/tools/host-filesystem/write.test.ts +134 -0
- package/src/tools/host-filesystem/write.ts +43 -1
- package/src/tools/host-terminal/host-shell.ts +13 -6
- package/src/tools/mcp/mcp-tool-factory.ts +2 -1
- package/src/tools/memory/register.test.ts +14 -9
- package/src/tools/memory/register.ts +1 -2
- package/src/tools/permission-checker.ts +15 -0
- package/src/tools/provider-tool-name.ts +28 -0
- package/src/tools/registry.ts +30 -9
- package/src/tools/skills/load.ts +24 -20
- package/src/tools/terminal/shell.ts +9 -1
- package/src/tools/tool-approval-handler.ts +31 -6
- package/src/tools/tool-name-aliases.ts +19 -0
- package/src/tools/types.ts +43 -3
- package/src/tts/provider-catalog.ts +3 -5
- package/src/util/disk-usage.ts +138 -0
- package/src/util/platform.ts +21 -11
- package/src/util/process-liveness.ts +26 -0
- package/src/workspace/heartbeat-service.ts +19 -0
- package/src/workspace/migrations/065-bump-stale-heartbeat-interval.ts +60 -0
- package/src/workspace/migrations/066-seed-heartbeat-callsite-cost-default.ts +146 -0
- package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +14 -0
- package/src/workspace/migrations/068-release-notes-local-timezone.ts +65 -0
- package/src/workspace/migrations/069-seed-onboarding-threads.ts +28 -0
- package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
- package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
- package/src/workspace/migrations/registry.ts +14 -0
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +0 -167
- package/src/memory/v2/__tests__/skill-qdrant.test.ts +0 -657
- package/src/memory/v2/skill-qdrant.ts +0 -404
- package/src/signals/bash.ts +0 -198
|
@@ -3,7 +3,7 @@ import { createServer, type Server } from "node:http";
|
|
|
3
3
|
import type { Command } from "commander";
|
|
4
4
|
|
|
5
5
|
import { getIsContainerized } from "../../../config/env-registry.js";
|
|
6
|
-
import {
|
|
6
|
+
import { cliIpcCall } from "../../../ipc/cli-client.js";
|
|
7
7
|
import {
|
|
8
8
|
getAppByProviderAndClientId,
|
|
9
9
|
getMostRecentAppByProvider,
|
|
@@ -69,6 +69,39 @@ function startManagedRedirectServer(provider: string): Promise<{
|
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// IPC polling helpers
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
type OAuthConnectStatusResponse =
|
|
77
|
+
| { status: "pending"; service: string }
|
|
78
|
+
| { status: "complete"; service: string; account_info?: string; granted_scopes?: string[] }
|
|
79
|
+
| { status: "error"; service: string; error?: string };
|
|
80
|
+
|
|
81
|
+
async function pollOAuthConnectStatus(
|
|
82
|
+
state: string,
|
|
83
|
+
opts: { intervalMs: number; timeoutMs: number },
|
|
84
|
+
): Promise<OAuthConnectStatusResponse> {
|
|
85
|
+
const deadline = Date.now() + opts.timeoutMs;
|
|
86
|
+
while (Date.now() < deadline) {
|
|
87
|
+
const r = await cliIpcCall<OAuthConnectStatusResponse>(
|
|
88
|
+
"internal_oauth_connect_status",
|
|
89
|
+
{ pathParams: { state } },
|
|
90
|
+
);
|
|
91
|
+
if (r.ok && r.result) {
|
|
92
|
+
const { status } = r.result;
|
|
93
|
+
if (status === "complete" || status === "error") {
|
|
94
|
+
return r.result;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (!r.ok && r.statusCode !== undefined) {
|
|
98
|
+
return { status: "error", service: "?", error: r.error ?? "assistant error during OAuth status poll" };
|
|
99
|
+
}
|
|
100
|
+
await new Promise<void>((res) => setTimeout(res, opts.intervalMs));
|
|
101
|
+
}
|
|
102
|
+
return { status: "error", service: "?", error: "Timed out waiting for OAuth callback" };
|
|
103
|
+
}
|
|
104
|
+
|
|
72
105
|
// ---------------------------------------------------------------------------
|
|
73
106
|
// Command registration
|
|
74
107
|
// ---------------------------------------------------------------------------
|
|
@@ -392,53 +425,104 @@ Examples:
|
|
|
392
425
|
}
|
|
393
426
|
}
|
|
394
427
|
|
|
395
|
-
// e.
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
428
|
+
// e. Try daemon-orchestrated path first (fixes heap-split for gateway transport).
|
|
429
|
+
const startResult = await cliIpcCall<{ auth_url: string; state: string }>(
|
|
430
|
+
"internal_oauth_connect_start",
|
|
431
|
+
{
|
|
432
|
+
body: {
|
|
433
|
+
service: provider,
|
|
434
|
+
clientId,
|
|
435
|
+
...(clientSecret !== undefined ? { clientSecret } : {}),
|
|
436
|
+
callbackTransport: opts.callbackTransport,
|
|
437
|
+
...(opts.scopes ? { requestedScopes: opts.scopes } : {}),
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
if (startResult.ok && startResult.result?.auth_url) {
|
|
443
|
+
const { auth_url, state } = startResult.result;
|
|
444
|
+
|
|
445
|
+
if (opts.browser !== false) {
|
|
446
|
+
await openInHostBrowser(auth_url);
|
|
411
447
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
// existing CLI script consumers; the internal field on
|
|
419
|
-
// `result` is `authorizeUrl`.
|
|
420
|
-
authUrl: result.authorizeUrl,
|
|
421
|
-
service: result.service,
|
|
448
|
+
if (!jsonMode) {
|
|
449
|
+
log.info("Waiting for authorization in browser... (press Ctrl+C to cancel)");
|
|
450
|
+
}
|
|
451
|
+
const final = await pollOAuthConnectStatus(state, {
|
|
452
|
+
intervalMs: 2000,
|
|
453
|
+
timeoutMs: 5 * 60 * 1000, // match LOOPBACK_TIMEOUT_MS in oauth2.ts (5 min)
|
|
422
454
|
});
|
|
455
|
+
|
|
456
|
+
if (final.status === "complete") {
|
|
457
|
+
if (jsonMode) {
|
|
458
|
+
writeOutput(cmd, {
|
|
459
|
+
ok: true,
|
|
460
|
+
grantedScopes: final.granted_scopes ?? [],
|
|
461
|
+
accountInfo: final.account_info,
|
|
462
|
+
});
|
|
463
|
+
} else {
|
|
464
|
+
process.stdout.write(
|
|
465
|
+
`Connected to ${provider}${final.account_info ? ` as ${final.account_info}` : ""}\n`,
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (final.status === "error") {
|
|
472
|
+
// Includes the timeout sentinel emitted by pollOAuthConnectStatus.
|
|
473
|
+
writeError(final.error ?? "OAuth connect failed");
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Defensive: pollOAuthConnectStatus should never return pending,
|
|
478
|
+
// but TS narrowing requires us to handle it.
|
|
479
|
+
writeError("OAuth connect ended in an unexpected pending state");
|
|
480
|
+
return;
|
|
423
481
|
} else {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
482
|
+
// --no-browser: return the URL immediately, matching existing deferred behavior.
|
|
483
|
+
if (jsonMode) {
|
|
484
|
+
writeOutput(cmd, {
|
|
485
|
+
ok: true,
|
|
486
|
+
deferred: true,
|
|
487
|
+
authUrl: auth_url,
|
|
488
|
+
state,
|
|
489
|
+
service: provider,
|
|
490
|
+
});
|
|
491
|
+
} else {
|
|
492
|
+
process.stdout.write(
|
|
493
|
+
`\nAuthorize with ${provider}:\n\n${auth_url}\n\nThe connection will complete automatically once you authorize.\n`,
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
return;
|
|
427
497
|
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// ok:true but no auth_url means a malformed daemon response — surface an error rather
|
|
501
|
+
// than falling back to in-process (which would re-introduce the heap-split bug for
|
|
502
|
+
// gateway transport).
|
|
503
|
+
if (startResult.ok && !startResult.result?.auth_url) {
|
|
504
|
+
writeError("assistant returned unexpected response for OAuth connect start");
|
|
428
505
|
return;
|
|
429
506
|
}
|
|
430
507
|
|
|
431
|
-
//
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
});
|
|
438
|
-
} else {
|
|
439
|
-
const msg = `Connected to ${provider}${result.accountInfo ? ` as ${result.accountInfo}` : ""}`;
|
|
440
|
-
process.stdout.write(msg + "\n");
|
|
508
|
+
// If the daemon was reachable but returned an error, surface it rather than
|
|
509
|
+
// falling back to in-process (which would re-introduce the heap-split bug for
|
|
510
|
+
// gateway transport).
|
|
511
|
+
if (!startResult.ok && startResult.statusCode !== undefined) {
|
|
512
|
+
writeError(startResult.error ?? "OAuth connect failed (assistant error)");
|
|
513
|
+
return;
|
|
441
514
|
}
|
|
515
|
+
|
|
516
|
+
// IPC unavailable: the assistant must be running for OAuth connect. The
|
|
517
|
+
// gateway-routed callback lands in the assistant's process, and any tokens
|
|
518
|
+
// acquired need the assistant to store and use them — so an unreachable
|
|
519
|
+
// assistant is a fatal precondition. Surface a clear error and exit 1.
|
|
520
|
+
writeError(
|
|
521
|
+
startResult.error
|
|
522
|
+
? `Could not reach the assistant: ${startResult.error}. Is the assistant running?`
|
|
523
|
+
: "Could not reach the assistant. Is the assistant running?",
|
|
524
|
+
);
|
|
525
|
+
return;
|
|
442
526
|
}
|
|
443
527
|
} catch (err) {
|
|
444
528
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -10,7 +10,6 @@ let mockResolvePlatformCallbackRegistrationContext: () => Promise<
|
|
|
10
10
|
isPlatform: false,
|
|
11
11
|
platformBaseUrl: "",
|
|
12
12
|
assistantId: "",
|
|
13
|
-
hasInternalApiKey: false,
|
|
14
13
|
hasAssistantApiKey: false,
|
|
15
14
|
authHeader: null,
|
|
16
15
|
enabled: false,
|
|
@@ -93,7 +92,6 @@ function connectedContext(
|
|
|
93
92
|
isPlatform: false,
|
|
94
93
|
platformBaseUrl: "https://dev-platform.vellum.ai",
|
|
95
94
|
assistantId: "019d6d4f-6dbd-779f-91d3-cb273b9429a5",
|
|
96
|
-
hasInternalApiKey: false,
|
|
97
95
|
hasAssistantApiKey: true,
|
|
98
96
|
authHeader: "Api-Key vak_test123",
|
|
99
97
|
enabled: false,
|
|
@@ -181,7 +179,6 @@ describe("assistant platform callback-routes list", () => {
|
|
|
181
179
|
isPlatform: false,
|
|
182
180
|
platformBaseUrl: "",
|
|
183
181
|
assistantId: "",
|
|
184
|
-
hasInternalApiKey: false,
|
|
185
182
|
hasAssistantApiKey: false,
|
|
186
183
|
authHeader: null,
|
|
187
184
|
enabled: false,
|
|
@@ -32,6 +32,13 @@ mock.module("../../../../security/secure-keys.js", () => ({
|
|
|
32
32
|
onCesClientChanged: () => ({ unsubscribe: () => {} }),
|
|
33
33
|
setCesReconnect: () => {},
|
|
34
34
|
getActiveBackendName: () => "file",
|
|
35
|
+
getActiveBackendInfoAsync: async () => ({
|
|
36
|
+
backend: "encrypted-store",
|
|
37
|
+
storePath: "/tmp/keys.enc",
|
|
38
|
+
storeKeyPath: "/tmp/store.key",
|
|
39
|
+
storeExists: false,
|
|
40
|
+
storeKeyExists: false,
|
|
41
|
+
}),
|
|
35
42
|
_resetBackend: () => {},
|
|
36
43
|
}));
|
|
37
44
|
|
|
@@ -45,7 +52,6 @@ mock.module("../../../../inbound/platform-callback-registration.js", () => ({
|
|
|
45
52
|
isPlatform: false,
|
|
46
53
|
platformBaseUrl: "",
|
|
47
54
|
assistantId: "",
|
|
48
|
-
hasInternalApiKey: false,
|
|
49
55
|
hasAssistantApiKey: false,
|
|
50
56
|
authHeader: null,
|
|
51
57
|
enabled: false,
|
|
@@ -39,7 +39,6 @@ mock.module("../../../../inbound/platform-callback-registration.js", () => ({
|
|
|
39
39
|
isPlatform: false,
|
|
40
40
|
platformBaseUrl: "",
|
|
41
41
|
assistantId: "",
|
|
42
|
-
hasInternalApiKey: false,
|
|
43
42
|
hasAssistantApiKey: false,
|
|
44
43
|
authHeader: null,
|
|
45
44
|
enabled: false,
|
|
@@ -64,6 +63,13 @@ mock.module("../../../../security/secure-keys.js", () => ({
|
|
|
64
63
|
onCesClientChanged: () => ({ unsubscribe: () => {} }),
|
|
65
64
|
setCesReconnect: () => {},
|
|
66
65
|
getActiveBackendName: () => "file",
|
|
66
|
+
getActiveBackendInfoAsync: async () => ({
|
|
67
|
+
backend: "encrypted-store",
|
|
68
|
+
storePath: "/tmp/keys.enc",
|
|
69
|
+
storeKeyPath: "/tmp/store.key",
|
|
70
|
+
storeExists: false,
|
|
71
|
+
storeKeyExists: false,
|
|
72
|
+
}),
|
|
67
73
|
_resetBackend: () => {},
|
|
68
74
|
}));
|
|
69
75
|
|
|
@@ -14,12 +14,16 @@ let mockResolvePlatformCallbackRegistrationContext: () => Promise<
|
|
|
14
14
|
isPlatform: false,
|
|
15
15
|
platformBaseUrl: "",
|
|
16
16
|
assistantId: "",
|
|
17
|
-
hasInternalApiKey: false,
|
|
18
17
|
hasAssistantApiKey: false,
|
|
19
18
|
authHeader: null,
|
|
20
19
|
enabled: false,
|
|
21
20
|
});
|
|
22
21
|
|
|
22
|
+
let mockIpcGetVelayStatus: () => Promise<{
|
|
23
|
+
connected: boolean;
|
|
24
|
+
publicUrl: string | null;
|
|
25
|
+
} | null> = async () => null;
|
|
26
|
+
|
|
23
27
|
// ---------------------------------------------------------------------------
|
|
24
28
|
// Mocks
|
|
25
29
|
// ---------------------------------------------------------------------------
|
|
@@ -47,6 +51,13 @@ mock.module("../../../../security/secure-keys.js", () => ({
|
|
|
47
51
|
onCesClientChanged: () => ({ unsubscribe: () => {} }),
|
|
48
52
|
setCesReconnect: () => {},
|
|
49
53
|
getActiveBackendName: () => "file",
|
|
54
|
+
getActiveBackendInfoAsync: async () => ({
|
|
55
|
+
backend: "encrypted-store",
|
|
56
|
+
storePath: "/tmp/keys.enc",
|
|
57
|
+
storeKeyPath: "/tmp/store.key",
|
|
58
|
+
storeExists: false,
|
|
59
|
+
storeKeyExists: false,
|
|
60
|
+
}),
|
|
50
61
|
_resetBackend: () => {},
|
|
51
62
|
}));
|
|
52
63
|
|
|
@@ -100,6 +111,15 @@ mock.module("../../../../config/loader.js", () => ({
|
|
|
100
111
|
mergeDefaultWorkspaceConfig: () => {},
|
|
101
112
|
}));
|
|
102
113
|
|
|
114
|
+
mock.module("../../../../ipc/gateway-client.js", () => ({
|
|
115
|
+
ipcGetVelayStatus: () => mockIpcGetVelayStatus(),
|
|
116
|
+
ipcCall: async () => undefined,
|
|
117
|
+
ipcCallPersistent: async () => undefined,
|
|
118
|
+
resetPersistentClient: () => {},
|
|
119
|
+
ipcGetFeatureFlags: async () => ({}),
|
|
120
|
+
ipcClassifyRisk: async () => undefined,
|
|
121
|
+
}));
|
|
122
|
+
|
|
103
123
|
// ---------------------------------------------------------------------------
|
|
104
124
|
// Import module under test (after mocks are registered)
|
|
105
125
|
// ---------------------------------------------------------------------------
|
|
@@ -158,11 +178,11 @@ describe("assistant platform status", () => {
|
|
|
158
178
|
isPlatform: false,
|
|
159
179
|
platformBaseUrl: "",
|
|
160
180
|
assistantId: "",
|
|
161
|
-
hasInternalApiKey: false,
|
|
162
181
|
hasAssistantApiKey: false,
|
|
163
182
|
authHeader: null,
|
|
164
183
|
enabled: false,
|
|
165
184
|
});
|
|
185
|
+
mockIpcGetVelayStatus = async () => null;
|
|
166
186
|
process.exitCode = 0;
|
|
167
187
|
});
|
|
168
188
|
|
|
@@ -179,9 +199,8 @@ describe("assistant platform status", () => {
|
|
|
179
199
|
isPlatform: true,
|
|
180
200
|
platformBaseUrl: "https://platform.vellum.ai",
|
|
181
201
|
assistantId: "asst-abc-123",
|
|
182
|
-
hasInternalApiKey: true,
|
|
183
202
|
hasAssistantApiKey: true,
|
|
184
|
-
authHeader: "
|
|
203
|
+
authHeader: "Api-Key assistant-key",
|
|
185
204
|
enabled: true,
|
|
186
205
|
});
|
|
187
206
|
|
|
@@ -209,12 +228,91 @@ describe("assistant platform status", () => {
|
|
|
209
228
|
expect(parsed.isPlatform).toBe(true);
|
|
210
229
|
expect(parsed.baseUrl).toBe("https://platform.vellum.ai");
|
|
211
230
|
expect(parsed.assistantId).toBe("asst-abc-123");
|
|
212
|
-
expect(parsed.hasInternalApiKey).toBe(true);
|
|
213
231
|
expect(parsed.hasAssistantApiKey).toBe(true);
|
|
214
232
|
expect(parsed.hasWebhookSecret).toBe(true);
|
|
215
233
|
expect(parsed.available).toBe(true);
|
|
216
234
|
expect(parsed.organizationId).toBe("org-456");
|
|
217
235
|
expect(parsed.userId).toBe("user-789");
|
|
236
|
+
// velayTunnel is null when gateway is unreachable
|
|
237
|
+
expect(parsed.velayTunnel).toBeNull();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test("velayTunnel connected with publicUrl is returned when gateway is live", async () => {
|
|
241
|
+
/**
|
|
242
|
+
* When the gateway is running and the Velay tunnel is connected, the
|
|
243
|
+
* status command includes velayTunnel.connected=true and the public URL.
|
|
244
|
+
*/
|
|
245
|
+
|
|
246
|
+
// GIVEN a connected Velay tunnel reported by the gateway IPC
|
|
247
|
+
mockIpcGetVelayStatus = async () => ({
|
|
248
|
+
connected: true,
|
|
249
|
+
publicUrl: "https://abc123.vellum.ai",
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// WHEN the status command is run with --json
|
|
253
|
+
const { exitCode, stdout } = await runCommand([
|
|
254
|
+
"platform",
|
|
255
|
+
"status",
|
|
256
|
+
"--json",
|
|
257
|
+
]);
|
|
258
|
+
|
|
259
|
+
// THEN the command succeeds
|
|
260
|
+
expect(exitCode).toBe(0);
|
|
261
|
+
|
|
262
|
+
// AND velayTunnel reflects the live connection
|
|
263
|
+
const parsed = JSON.parse(stdout);
|
|
264
|
+
expect(parsed.velayTunnel).toEqual({
|
|
265
|
+
connected: true,
|
|
266
|
+
publicUrl: "https://abc123.vellum.ai",
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("velayTunnel disconnected when gateway reports no active connection", async () => {
|
|
271
|
+
/**
|
|
272
|
+
* When the gateway is running but Velay is not connected (e.g.
|
|
273
|
+
* reconnecting after disconnect), velayTunnel.connected is false.
|
|
274
|
+
*/
|
|
275
|
+
|
|
276
|
+
// GIVEN a disconnected Velay tunnel
|
|
277
|
+
mockIpcGetVelayStatus = async () => ({
|
|
278
|
+
connected: false,
|
|
279
|
+
publicUrl: null,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// WHEN the status command is run with --json
|
|
283
|
+
const { exitCode, stdout } = await runCommand([
|
|
284
|
+
"platform",
|
|
285
|
+
"status",
|
|
286
|
+
"--json",
|
|
287
|
+
]);
|
|
288
|
+
|
|
289
|
+
expect(exitCode).toBe(0);
|
|
290
|
+
const parsed = JSON.parse(stdout);
|
|
291
|
+
expect(parsed.velayTunnel).toEqual({ connected: false, publicUrl: null });
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("velayTunnel is null when gateway IPC is unreachable", async () => {
|
|
295
|
+
/**
|
|
296
|
+
* When the gateway IPC socket is not available (assistant not running),
|
|
297
|
+
* velayTunnel is null rather than causing the status command to fail.
|
|
298
|
+
*/
|
|
299
|
+
|
|
300
|
+
// GIVEN the gateway IPC throws
|
|
301
|
+
mockIpcGetVelayStatus = async () => {
|
|
302
|
+
throw new Error("ENOENT");
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// WHEN the status command is run with --json
|
|
306
|
+
const { exitCode, stdout } = await runCommand([
|
|
307
|
+
"platform",
|
|
308
|
+
"status",
|
|
309
|
+
"--json",
|
|
310
|
+
]);
|
|
311
|
+
|
|
312
|
+
// THEN the command still succeeds (graceful fallback)
|
|
313
|
+
expect(exitCode).toBe(0);
|
|
314
|
+
const parsed = JSON.parse(stdout);
|
|
315
|
+
expect(parsed.velayTunnel).toBeNull();
|
|
218
316
|
});
|
|
219
317
|
|
|
220
318
|
test("plain text mode does not emit JSON to stdout", async () => {
|
|
@@ -229,7 +327,6 @@ describe("assistant platform status", () => {
|
|
|
229
327
|
isPlatform: false,
|
|
230
328
|
platformBaseUrl: "",
|
|
231
329
|
assistantId: "",
|
|
232
|
-
hasInternalApiKey: false,
|
|
233
330
|
hasAssistantApiKey: false,
|
|
234
331
|
authHeader: null,
|
|
235
332
|
enabled: false,
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
registerCallbackRoute,
|
|
5
5
|
resolvePlatformCallbackRegistrationContext,
|
|
6
6
|
} from "../../../inbound/platform-callback-registration.js";
|
|
7
|
+
import { ipcGetVelayStatus } from "../../../ipc/gateway-client.js";
|
|
7
8
|
import { credentialKey } from "../../../security/credential-key.js";
|
|
8
9
|
import { getSecureKeyAsync } from "../../../security/secure-keys.js";
|
|
9
10
|
import { log } from "../../logger.js";
|
|
@@ -62,14 +63,14 @@ Fields:
|
|
|
62
63
|
isPlatform Whether IS_PLATFORM is set (boolean)
|
|
63
64
|
baseUrl VELLUM_PLATFORM_URL — the platform gateway base URL
|
|
64
65
|
assistantId This assistant's platform UUID
|
|
65
|
-
hasInternalApiKey Whether PLATFORM_INTERNAL_API_KEY is set (boolean,
|
|
66
|
-
value not disclosed)
|
|
67
66
|
hasAssistantApiKey Whether a stored assistant API key is available
|
|
68
67
|
hasWebhookSecret Whether a stored webhook secret is available (needed
|
|
69
68
|
for email and other inbound webhook channels)
|
|
70
69
|
available Whether callback registration prerequisites are satisfied
|
|
71
70
|
organizationId The platform organization ID (from stored credentials)
|
|
72
71
|
userId The platform user ID (from stored credentials)
|
|
72
|
+
velayTunnel Live Velay tunnel status from the gateway IPC socket
|
|
73
|
+
(null when the gateway is not running)
|
|
73
74
|
|
|
74
75
|
Examples:
|
|
75
76
|
$ assistant platform status
|
|
@@ -77,7 +78,10 @@ Examples:
|
|
|
77
78
|
)
|
|
78
79
|
.action(async (_opts: Record<string, unknown>, cmd: Command) => {
|
|
79
80
|
try {
|
|
80
|
-
const context = await
|
|
81
|
+
const [context, velayTunnel] = await Promise.all([
|
|
82
|
+
resolvePlatformCallbackRegistrationContext(),
|
|
83
|
+
ipcGetVelayStatus().catch(() => null),
|
|
84
|
+
]);
|
|
81
85
|
|
|
82
86
|
const organizationId =
|
|
83
87
|
(
|
|
@@ -106,12 +110,12 @@ Examples:
|
|
|
106
110
|
isPlatform: context.isPlatform,
|
|
107
111
|
baseUrl: context.platformBaseUrl,
|
|
108
112
|
assistantId: context.assistantId,
|
|
109
|
-
hasInternalApiKey: context.hasInternalApiKey,
|
|
110
113
|
hasAssistantApiKey: context.hasAssistantApiKey,
|
|
111
114
|
hasWebhookSecret,
|
|
112
115
|
available: context.enabled,
|
|
113
116
|
organizationId: organizationId || null,
|
|
114
117
|
userId: userId || null,
|
|
118
|
+
velayTunnel,
|
|
115
119
|
};
|
|
116
120
|
|
|
117
121
|
if (shouldOutputJson(cmd)) {
|
|
@@ -120,9 +124,6 @@ Examples:
|
|
|
120
124
|
log.info(`Platform: ${result.isPlatform}`);
|
|
121
125
|
log.info(`Base URL: ${result.baseUrl || "(not set)"}`);
|
|
122
126
|
log.info(`Assistant ID: ${result.assistantId || "(not set)"}`);
|
|
123
|
-
log.info(
|
|
124
|
-
`Internal API key: ${result.hasInternalApiKey ? "set" : "not set"}`,
|
|
125
|
-
);
|
|
126
127
|
log.info(
|
|
127
128
|
`Assistant API key: ${result.hasAssistantApiKey ? "set" : "not set"}`,
|
|
128
129
|
);
|
|
@@ -134,6 +135,14 @@ Examples:
|
|
|
134
135
|
);
|
|
135
136
|
log.info(`Organization ID: ${organizationId || "(not set)"}`);
|
|
136
137
|
log.info(`User ID: ${userId || "(not set)"}`);
|
|
138
|
+
if (result.velayTunnel !== null) {
|
|
139
|
+
const tunnelState = result.velayTunnel.connected
|
|
140
|
+
? `connected${result.velayTunnel.publicUrl ? ` (${result.velayTunnel.publicUrl})` : ""}`
|
|
141
|
+
: "disconnected";
|
|
142
|
+
log.info(`Velay tunnel: ${tunnelState}`);
|
|
143
|
+
} else {
|
|
144
|
+
log.info(`Velay tunnel: (gateway not running)`);
|
|
145
|
+
}
|
|
137
146
|
}
|
|
138
147
|
} catch (err) {
|
|
139
148
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
|
|
3
|
+
import { cliIpcCall } from "../../ipc/cli-client.js";
|
|
4
|
+
import { getWorkspaceDirDisplay } from "../../util/platform.js";
|
|
5
|
+
import { log } from "../logger.js";
|
|
6
|
+
|
|
7
|
+
interface HealthResponse {
|
|
8
|
+
version: string;
|
|
9
|
+
memory: { currentMb: number; maxMb: number };
|
|
10
|
+
disk: { freeMb: number; totalMb: number } | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function fmtMb(mb: number): string {
|
|
14
|
+
if (mb >= 1024) return `${(mb / 1024).toFixed(1)} GB`;
|
|
15
|
+
return `${Math.round(mb)} MB`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function registerStatusCommand(program: Command): void {
|
|
19
|
+
program
|
|
20
|
+
.command("status")
|
|
21
|
+
.description("Show assistant version, workspace, and runtime health")
|
|
22
|
+
.action(async () => {
|
|
23
|
+
const result = await cliIpcCall<HealthResponse>("health");
|
|
24
|
+
|
|
25
|
+
if (!result.ok || !result.result) {
|
|
26
|
+
log.error(
|
|
27
|
+
result.error ??
|
|
28
|
+
"Assistant not running — could not connect to IPC socket.",
|
|
29
|
+
);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const h = result.result;
|
|
34
|
+
const workspace = getWorkspaceDirDisplay();
|
|
35
|
+
|
|
36
|
+
const rows: [string, string][] = [
|
|
37
|
+
["Version", h.version],
|
|
38
|
+
["Workspace", workspace],
|
|
39
|
+
["", ""],
|
|
40
|
+
["Memory", `${fmtMb(h.memory.currentMb)} / ${fmtMb(h.memory.maxMb)}`],
|
|
41
|
+
...(h.disk
|
|
42
|
+
? ([["Disk", `${fmtMb(h.disk.freeMb)} free`]] as [string, string][])
|
|
43
|
+
: []),
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
const labelWidth = Math.max(
|
|
47
|
+
...rows.filter(([l]) => l).map(([l]) => l.length),
|
|
48
|
+
);
|
|
49
|
+
for (const [label, value] of rows) {
|
|
50
|
+
if (!label) {
|
|
51
|
+
log.info("");
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
log.info(`${label.padEnd(labelWidth)} ${value}`);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
package/src/cli/program.ts
CHANGED
|
@@ -41,6 +41,7 @@ import { registerPlatformCommand } from "./commands/platform/index.js";
|
|
|
41
41
|
import { registerRoutesCommand } from "./commands/routes.js";
|
|
42
42
|
import { registerSequenceCommand } from "./commands/sequence.js";
|
|
43
43
|
import { registerSkillsCommand } from "./commands/skills.js";
|
|
44
|
+
import { registerStatusCommand } from "./commands/status.js";
|
|
44
45
|
import { registerSttCommand } from "./commands/stt.js";
|
|
45
46
|
import { registerTaskCommand } from "./commands/task.js";
|
|
46
47
|
import { registerTrustCommand } from "./commands/trust.js";
|
|
@@ -56,7 +57,7 @@ import { log } from "./logger.js";
|
|
|
56
57
|
* the gateway so flag-gated commands are registered correctly.
|
|
57
58
|
*/
|
|
58
59
|
export async function buildCliProgram(): Promise<Command> {
|
|
59
|
-
await initFeatureFlagOverrides();
|
|
60
|
+
await initFeatureFlagOverrides({ retryBackoffsMs: [], callTimeoutMs: 200 });
|
|
60
61
|
const program = new Command();
|
|
61
62
|
|
|
62
63
|
program
|
|
@@ -110,6 +111,7 @@ Examples:
|
|
|
110
111
|
registerPlatformCommand(program);
|
|
111
112
|
registerRoutesCommand(program);
|
|
112
113
|
registerSequenceCommand(program);
|
|
114
|
+
registerStatusCommand(program);
|
|
113
115
|
registerSkillsCommand(program);
|
|
114
116
|
registerSttCommand(program);
|
|
115
117
|
registerTaskCommand(program);
|
|
@@ -126,7 +128,7 @@ Examples:
|
|
|
126
128
|
// remain available even without a workspace.
|
|
127
129
|
// Workspace-independent commands are exempt:
|
|
128
130
|
// completions — pure shell-script generation, no workspace files needed
|
|
129
|
-
const workspaceExemptCommands = new Set(["completions"]);
|
|
131
|
+
const workspaceExemptCommands = new Set(["completions", "status"]);
|
|
130
132
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
131
133
|
if (workspaceExemptCommands.has(actionCommand.name())) {
|
|
132
134
|
return;
|
|
@@ -135,9 +135,11 @@ let cachedOverridesFromGateway = false;
|
|
|
135
135
|
* timeout, parse error). No auth needed — the IPC socket is
|
|
136
136
|
* access-controlled by file-system permissions on the shared volume.
|
|
137
137
|
*/
|
|
138
|
-
async function fetchOverridesFromGateway(
|
|
138
|
+
async function fetchOverridesFromGateway(
|
|
139
|
+
timeoutMs?: number,
|
|
140
|
+
): Promise<Record<string, boolean>> {
|
|
139
141
|
try {
|
|
140
|
-
return await ipcGetFeatureFlags();
|
|
142
|
+
return await ipcGetFeatureFlags(timeoutMs);
|
|
141
143
|
} catch {
|
|
142
144
|
return {};
|
|
143
145
|
}
|
|
@@ -182,10 +184,18 @@ const DEFAULT_INIT_RETRY_BACKOFFS_MS: readonly number[] = [
|
|
|
182
184
|
*/
|
|
183
185
|
export async function initFeatureFlagOverrides(options?: {
|
|
184
186
|
retryBackoffsMs?: readonly number[];
|
|
187
|
+
/**
|
|
188
|
+
* Timeout (ms) for each IPC call to the gateway. When omitted the
|
|
189
|
+
* transport defaults apply (3 s connect + 5 s call). CLI callers should
|
|
190
|
+
* pass a small value (e.g. 200) so a slow/absent gateway fails fast
|
|
191
|
+
* instead of blocking startup.
|
|
192
|
+
*/
|
|
193
|
+
callTimeoutMs?: number;
|
|
185
194
|
}): Promise<void> {
|
|
186
195
|
if (cachedOverridesFromGateway) return;
|
|
187
196
|
|
|
188
197
|
const backoffs = options?.retryBackoffsMs ?? DEFAULT_INIT_RETRY_BACKOFFS_MS;
|
|
198
|
+
const callTimeoutMs = options?.callTimeoutMs;
|
|
189
199
|
|
|
190
200
|
// First attempt has no preceding delay; subsequent attempts wait per the
|
|
191
201
|
// backoff schedule. An empty result is treated as a transient miss
|
|
@@ -201,7 +211,7 @@ export async function initFeatureFlagOverrides(options?: {
|
|
|
201
211
|
if (cachedOverridesFromGateway) return;
|
|
202
212
|
}
|
|
203
213
|
|
|
204
|
-
const gatewayOverrides = await fetchOverridesFromGateway();
|
|
214
|
+
const gatewayOverrides = await fetchOverridesFromGateway(callTimeoutMs);
|
|
205
215
|
if (Object.keys(gatewayOverrides).length > 0) {
|
|
206
216
|
cachedOverrides = gatewayOverrides;
|
|
207
217
|
cachedOverridesFromGateway = true;
|
|
@@ -6,8 +6,6 @@ metadata:
|
|
|
6
6
|
emoji: "🏗️"
|
|
7
7
|
vellum:
|
|
8
8
|
display-name: "App Builder"
|
|
9
|
-
includes:
|
|
10
|
-
- "frontend-design"
|
|
11
9
|
activation-hints:
|
|
12
10
|
- "User asks to build an app, landing page, website, dashboard, tool, calculator, game, tracker, or interactive page"
|
|
13
11
|
- "User asks to visualize data or says 'let's visualize this' — use the app sandbox to build interactive visualizations"
|
|
@@ -20,7 +18,7 @@ You are an expert app builder and visual designer. When the user asks you to cre
|
|
|
20
18
|
|
|
21
19
|
**Your default behavior:** Build immediately. The user types "build me a habit tracker" and you deliver a complete, polished app with a domain-matched color palette, atmospheric background, and thoughtful interactions. Don't ask what colors they want. Don't show wireframes. Just build something stunning and let them refine from there.
|
|
22
20
|
|
|
23
|
-
**Design quality is delegated to the `frontend-design` skill.** That skill defines your aesthetic principles: typography, color strategy, motion, spatial composition, and visual detail. Follow it completely for every build. This skill (app-builder) handles the technical infrastructure: sandbox constraints, data bridge, widget API, app lifecycle, and interaction patterns.
|
|
21
|
+
**Design quality is delegated to the `frontend-design` skill, so you must also load/install that before proceeding.** That skill defines your aesthetic principles: typography, color strategy, motion, spatial composition, and visual detail. Follow it completely for every build. This skill (app-builder) handles the technical infrastructure: sandbox constraints, data bridge, widget API, app lifecycle, and interaction patterns.
|
|
24
22
|
|
|
25
23
|
## Filesystem Layout
|
|
26
24
|
|