@vellumai/assistant 0.10.2 → 0.10.3-dev.202606252046.9075fd5
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/bun.lock +20 -0
- package/docs/workspace-tools.md +42 -33
- package/eslint-rules/cli-no-daemon-internals.js +6 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +44 -0
- package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +28 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +41 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/openapi.yaml +196 -56
- package/package.json +4 -1
- package/scripts/test.sh +36 -15
- package/src/__tests__/actor-token-service.test.ts +36 -14
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -0
- package/src/__tests__/approval-cascade.test.ts +2 -0
- package/src/__tests__/assistant-attachments.test.ts +42 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
- package/src/__tests__/btw-routes.test.ts +2 -0
- package/src/__tests__/build-persisted-content.test.ts +2 -0
- package/src/__tests__/call-controller.test.ts +19 -0
- package/src/__tests__/channel-approval-routes.test.ts +21 -26
- package/src/__tests__/channel-delivery-store.test.ts +28 -0
- package/src/__tests__/channel-guardian.test.ts +164 -78
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
- package/src/__tests__/channel-reply-delivery.test.ts +2 -0
- package/src/__tests__/code-search-tool.test.ts +585 -0
- package/src/__tests__/compaction-events.test.ts +2 -0
- package/src/__tests__/compaction-ledger-store.test.ts +128 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
- package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
- package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
- package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
- package/src/__tests__/config-loader-backfill.test.ts +123 -10
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
- package/src/__tests__/consult-deadline.test.ts +60 -0
- package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
- package/src/__tests__/contact-store-user-file.test.ts +7 -10
- package/src/__tests__/contacts-relay-reads.test.ts +19 -24
- package/src/__tests__/contacts-write.test.ts +0 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop.test.ts +134 -0
- package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +9 -11
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
- package/src/__tests__/conversation-fork-crud.test.ts +354 -24
- package/src/__tests__/conversation-history-web-search.test.ts +2 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
- package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
- package/src/__tests__/conversation-pairing.test.ts +2 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
- package/src/__tests__/conversation-process-callsite.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +91 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
- package/src/__tests__/conversation-slash-queue.test.ts +2 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-speed-override.test.ts +2 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +29 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
- package/src/__tests__/db-migration-rollback.test.ts +205 -171
- package/src/__tests__/db-test-helpers.ts +5 -4
- package/src/__tests__/delete-propagation.test.ts +5 -3
- package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
- package/src/__tests__/disk-pressure-guard.test.ts +41 -0
- package/src/__tests__/dm-backfill.test.ts +6 -4
- package/src/__tests__/dm-persistence.test.ts +2 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +7 -6
- package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
- package/src/__tests__/exploration-drift-hook.test.ts +3 -2
- package/src/__tests__/filing-service.test.ts +2 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +104 -19
- package/src/__tests__/guardian-dispatch.test.ts +140 -1
- package/src/__tests__/guardian-expiry-notifier.test.ts +282 -0
- package/src/__tests__/guardian-outbound-http.test.ts +13 -0
- package/src/__tests__/guardian-routing-state.test.ts +6 -10
- package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
- package/src/__tests__/heartbeat-service.test.ts +2 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +46 -19
- package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
- package/src/__tests__/helpers/mock-logger.ts +1 -0
- package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
- package/src/__tests__/host-app-control-routes.test.ts +24 -30
- package/src/__tests__/host-bash-routes.test.ts +31 -41
- package/src/__tests__/host-browser-routes.test.ts +26 -32
- package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
- package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
- package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
- package/src/__tests__/http-user-message-parity.test.ts +290 -8
- package/src/__tests__/inbound-invite-redemption.test.ts +115 -10
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/invite-redemption-service.test.ts +471 -53
- package/src/__tests__/invite-routes-http.test.ts +34 -0
- package/src/__tests__/invite-service-ipc.test.ts +65 -2
- package/src/__tests__/llm-context-normalization.test.ts +105 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +71 -9
- package/src/__tests__/llm-usage-store.test.ts +25 -0
- package/src/__tests__/mcp-config-secret-boundary.test.ts +392 -0
- package/src/__tests__/mcp-health-check.test.ts +2 -1
- package/src/__tests__/media-stream-server-integration.test.ts +127 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
- package/src/__tests__/messaging-send-tool.test.ts +2 -0
- package/src/__tests__/migration-import-from-url.test.ts +2 -2
- package/src/__tests__/mtime-cache.test.ts +275 -5
- package/src/__tests__/native-web-search.test.ts +2 -0
- package/src/__tests__/non-member-access-request.test.ts +191 -17
- package/src/__tests__/notification-broadcaster.test.ts +4 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
- package/src/__tests__/notification-deep-link.test.ts +6 -0
- package/src/__tests__/notification-guardian-path.test.ts +19 -0
- package/src/__tests__/onboarding-persona-write.test.ts +52 -22
- package/src/__tests__/openai-provider.test.ts +22 -12
- package/src/__tests__/openai-responses-provider.test.ts +12 -2
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
- package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/persona-resolver.test.ts +75 -45
- package/src/__tests__/plugin-bootstrap.test.ts +21 -83
- package/src/__tests__/plugin-disabled-state.test.ts +190 -0
- package/src/__tests__/plugin-pipeline.test.ts +96 -0
- package/src/__tests__/plugin-route-contribution.test.ts +6 -19
- package/src/__tests__/plugin-tool-contribution.test.ts +5 -20
- package/src/__tests__/plugin-types.test.ts +2 -4
- package/src/__tests__/process-message-background-slack.test.ts +2 -0
- package/src/__tests__/process-message-display-content.test.ts +2 -0
- package/src/__tests__/provider-error-scenarios.test.ts +5 -4
- package/src/__tests__/provider-usage-tracking.test.ts +39 -0
- package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
- package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
- package/src/__tests__/reaction-persistence.test.ts +51 -4
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
- package/src/__tests__/registry.test.ts +4 -1
- package/src/__tests__/relay-server.test.ts +758 -32
- package/src/__tests__/runtime-attachment-metadata.test.ts +9 -12
- package/src/__tests__/secret-ingress-http.test.ts +14 -0
- package/src/__tests__/send-endpoint-busy.test.ts +30 -8
- package/src/__tests__/settings-routes.test.ts +32 -0
- package/src/__tests__/skills.test.ts +44 -0
- package/src/__tests__/slack-inbound-verification.test.ts +47 -2
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +79 -0
- package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
- package/src/__tests__/stt-hints.test.ts +49 -15
- package/src/__tests__/subagent-detail.test.ts +27 -0
- package/src/__tests__/subagent-disposal.test.ts +65 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-role-registry.test.ts +24 -6
- package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
- package/src/__tests__/subagent-tools.test.ts +398 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/thread-backfill.test.ts +3 -3
- package/src/__tests__/title-generate-hook.test.ts +2 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
- package/src/__tests__/tool-executor.test.ts +16 -11
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
- package/src/__tests__/tool-start-timestamp.test.ts +2 -0
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +37 -51
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +12 -12
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
- package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
- package/src/__tests__/trusted-contact-verification.test.ts +79 -54
- package/src/__tests__/twilio-routes.test.ts +96 -0
- package/src/__tests__/ui-file-upload-surface.test.ts +86 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
- package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
- package/src/__tests__/voice-invite-redemption.test.ts +216 -20
- package/src/__tests__/web-search-backend-failure.test.ts +2 -0
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
- package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +208 -0
- package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +14 -35
- package/src/__tests__/workspace-tool-loader.test.ts +195 -2
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
- package/src/agent/loop-exclusive-tool.test.ts +154 -0
- package/src/agent/loop-native-web-search.test.ts +200 -0
- package/src/agent/loop.ts +164 -1
- package/src/api/constants/sse-replay.ts +41 -0
- package/src/api/events/conversation-notice.ts +26 -0
- package/src/api/index.ts +19 -1
- package/src/api/responses/llm-request-log-entry.ts +29 -0
- package/src/api/responses/subagent-detail.ts +17 -0
- package/src/api/surfaces.ts +39 -3
- package/src/approvals/guardian-channel-delivery.ts +30 -0
- package/src/approvals/guardian-expiry-notifier.ts +148 -0
- package/src/approvals/guardian-request-resolvers.ts +17 -15
- package/src/calls/__tests__/relay-setup-router.test.ts +267 -17
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +14 -9
- package/src/calls/inbound-trust-reader.ts +24 -2
- package/src/calls/media-stream-server.ts +21 -0
- package/src/calls/relay-access-wait.ts +6 -6
- package/src/calls/relay-server.ts +188 -51
- package/src/calls/relay-setup-router.ts +47 -17
- package/src/calls/relay-verification.ts +4 -4
- package/src/calls/stt-hints.ts +9 -12
- package/src/calls/twilio-routes.ts +14 -4
- package/src/channels/types.ts +10 -20
- package/src/cli/commands/__tests__/cache.test.ts +8 -1
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
- package/src/cli/commands/cache.ts +194 -181
- package/src/cli/commands/contacts.ts +10 -7
- package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
- package/src/cli/commands/db/status.ts +37 -1
- package/src/cli/commands/mcp.ts +252 -218
- package/src/cli/commands/memory/__tests__/worker.test.ts +432 -0
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/worker.ts +242 -0
- package/src/cli/commands/plugins.ts +125 -13
- package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
- package/src/cli/lib/__tests__/publish-plugin.test.ts +404 -0
- package/src/cli/lib/list-installed-plugins.ts +179 -1
- package/src/cli/lib/publish-plugin.ts +633 -0
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
- package/src/config/__tests__/sync-gated-profiles.test.ts +16 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -2
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
- package/src/config/call-site-defaults.ts +0 -6
- package/src/config/feature-flag-registry.json +5 -13
- package/src/config/llm-resolver.ts +0 -3
- package/src/config/loader.ts +38 -5
- package/src/config/prune-seeded-callsite-defaults.ts +110 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +0 -7
- package/src/config/schemas/heartbeat.ts +2 -5
- package/src/config/schemas/llm.ts +3 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/memory-v3.ts +7 -0
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/timeouts.ts +8 -0
- package/src/config/seed-inference-profiles.ts +30 -34
- package/src/config/skills.ts +27 -5
- package/src/config/sync-gated-profiles.ts +12 -16
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +128 -0
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
- package/src/contacts/__tests__/member-write-relay.test.ts +226 -0
- package/src/contacts/contact-store.ts +27 -237
- package/src/contacts/contacts-write.ts +18 -55
- package/src/contacts/gateway-channel-read.ts +51 -0
- package/src/contacts/guardian-delivery-reader.ts +223 -0
- package/src/contacts/member-write-relay.ts +183 -0
- package/src/contacts/types.ts +3 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +35 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
- package/src/daemon/assistant-attachments.ts +27 -4
- package/src/daemon/conversation-agent-loop.ts +28 -0
- package/src/daemon/conversation-notices.ts +60 -0
- package/src/daemon/conversation-process.ts +35 -16
- package/src/daemon/conversation-surfaces.ts +111 -38
- package/src/daemon/conversation-tool-setup.ts +71 -30
- package/src/daemon/conversation.ts +23 -1
- package/src/daemon/disk-pressure-guard.ts +12 -2
- package/src/daemon/event-loop-watchdog.ts +28 -1
- package/src/daemon/external-plugins-bootstrap.ts +17 -41
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -2
- package/src/daemon/handlers/__tests__/config-channels.test.ts +220 -0
- package/src/daemon/handlers/config-a2a.ts +6 -14
- package/src/daemon/handlers/config-channels.ts +67 -26
- package/src/daemon/handlers/conversations.ts +77 -0
- package/src/daemon/lifecycle.ts +16 -0
- package/src/daemon/mcp-reload-service.ts +10 -0
- package/src/daemon/memory-v2-startup.test.ts +72 -0
- package/src/daemon/memory-v2-startup.ts +87 -19
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +12 -12
- package/src/daemon/server.ts +0 -4
- package/src/daemon/shutdown-handlers.ts +20 -0
- package/src/daemon/tool-setup-types.ts +9 -0
- package/src/heartbeat/heartbeat-service.ts +5 -0
- package/src/home/relationship-state-writer.ts +5 -0
- package/src/hooks/hook-loader.ts +341 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -0
- package/src/mcp/client.ts +15 -1
- package/src/mcp/mcp-auth-orchestrator.ts +6 -1
- package/src/mcp/mcp-header-store.ts +134 -0
- package/src/mcp/mcp-oauth-provider.ts +19 -8
- package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
- package/src/memory/__tests__/prompt-override.test.ts +192 -0
- package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
- package/src/memory/compaction-ledger-store.ts +107 -0
- package/src/memory/conversation-crud.ts +129 -61
- package/src/memory/db-connection.ts +22 -3
- package/src/memory/db-init.ts +37 -503
- package/src/memory/db-singleton.ts +6 -4
- package/src/memory/jobs-worker.ts +104 -0
- package/src/memory/llm-request-log-store.ts +26 -1
- package/src/memory/llm-usage-store.ts +48 -20
- package/src/memory/memory-retrospective-job.ts +14 -8
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +152 -56
- package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
- package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
- package/src/memory/migrations/302-create-compaction-events.ts +107 -0
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +297 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/run-migrations.ts +106 -10
- package/src/memory/migrations/validate-migration-state.ts +105 -67
- package/src/memory/prompt-override.ts +129 -0
- package/src/memory/schema/contacts.ts +6 -2
- package/src/memory/schema/conversations.ts +37 -0
- package/src/memory/schema/infrastructure.ts +20 -0
- package/src/memory/steps.ts +1294 -0
- package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
- package/src/memory/v2/cli-command-store.ts +75 -38
- package/src/memory/v2/prompts/consolidation.ts +13 -82
- package/src/memory/v2/prompts/router.ts +21 -93
- package/src/memory/v2/skill-store.ts +68 -31
- package/src/memory/watchdog-events-store.ts +87 -0
- package/src/memory/worker-control.ts +204 -0
- package/src/memory/worker-process.ts +89 -0
- package/src/messaging/channel-binding-metadata.ts +31 -0
- package/src/messaging/provider-types.ts +8 -0
- package/src/messaging/providers/slack/binding-metadata.ts +60 -0
- package/src/notifications/__tests__/broadcaster.test.ts +8 -8
- package/src/notifications/__tests__/connected-channels.test.ts +86 -0
- package/src/notifications/__tests__/decision-engine.test.ts +78 -9
- package/src/notifications/__tests__/destination-resolver.test.ts +151 -0
- package/src/notifications/broadcaster.ts +8 -1
- package/src/notifications/decision-engine.ts +15 -7
- package/src/notifications/destination-resolver.ts +53 -25
- package/src/notifications/emit-signal.ts +34 -15
- package/src/onboarding/checkin-event.test.ts +222 -0
- package/src/onboarding/checkin-event.ts +321 -0
- package/src/onboarding/schedule-checkin.ts +190 -0
- package/src/permissions/question-prompter.test.ts +1 -1
- package/src/permissions/question-prompter.ts +7 -4
- package/src/plugin-api/index.ts +12 -12
- package/src/plugin-api/types.ts +12 -14
- package/src/plugin-api/vision-support.test.ts +28 -4
- package/src/plugin-api/vision-support.ts +66 -31
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/empty-response/hooks/stop.ts +2 -2
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +4 -3
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/stop.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +12 -13
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +14 -22
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +2 -2
- package/src/plugins/defaults/index.ts +0 -35
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +33 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +48 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +4 -8
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +43 -15
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
- package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +77 -13
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +12 -11
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +2 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/stop.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +2 -2
- package/src/plugins/disabled-state.ts +31 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/mtime-cache.ts +186 -330
- package/src/plugins/pipeline.ts +111 -13
- package/src/plugins/registry.ts +59 -16
- package/src/plugins/surface-import.ts +121 -0
- package/src/plugins/types.ts +7 -9
- package/src/prompts/persona-resolver.ts +43 -11
- package/src/providers/anthropic/client.ts +5 -0
- package/src/providers/call-site-routing.ts +45 -0
- package/src/providers/model-catalog.ts +16 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +321 -0
- package/src/providers/openai/api-error-normalization.ts +270 -0
- package/src/providers/openai/chat-completions-provider.ts +37 -83
- package/src/providers/openai/responses-provider.ts +50 -46
- package/src/providers/openrouter/client.ts +5 -0
- package/src/providers/provider-send-message.ts +10 -0
- package/src/providers/ratelimit.ts +10 -0
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +10 -0
- package/src/providers/types.ts +22 -0
- package/src/providers/usage-tracking.ts +10 -0
- package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +184 -0
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
- package/src/runtime/__tests__/local-principal-trust.test.ts +162 -0
- package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +402 -123
- package/src/runtime/access-request-helper.ts +18 -39
- package/src/runtime/actor-trust-resolver.ts +46 -19
- package/src/runtime/anchored-guardian.test.ts +109 -0
- package/src/runtime/anchored-guardian.ts +86 -0
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/assistant-stream-state.ts +9 -2
- package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
- package/src/runtime/auth/require-bound-guardian.ts +21 -11
- package/src/runtime/channel-verification-service.ts +56 -31
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
- package/src/runtime/guardian-vellum-migration.ts +69 -8
- package/src/runtime/invite-redemption-service.ts +213 -187
- package/src/runtime/local-actor-identity.test.ts +108 -0
- package/src/runtime/local-actor-identity.ts +85 -13
- package/src/runtime/local-principal-trust.ts +52 -0
- package/src/runtime/member-verdict-cache.ts +0 -0
- package/src/runtime/pending-interactions.ts +11 -1
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/contact-routes.test.ts +305 -0
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +92 -0
- package/src/runtime/routes/__tests__/http-adapter-actor-header.test.ts +129 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +188 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +13 -5
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +48 -57
- package/src/runtime/routes/conversation-cli-routes.ts +4 -5
- package/src/runtime/routes/conversation-list-routes.ts +4 -7
- package/src/runtime/routes/conversation-query-routes.ts +72 -0
- package/src/runtime/routes/conversation-routes.ts +84 -85
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/global-search-routes.ts +3 -1
- package/src/runtime/routes/guardian-action-routes.ts +4 -5
- package/src/runtime/routes/host-app-control-routes.ts +5 -4
- package/src/runtime/routes/host-bash-routes.ts +5 -4
- package/src/runtime/routes/host-browser-routes.ts +9 -11
- package/src/runtime/routes/host-cu-routes.ts +5 -4
- package/src/runtime/routes/host-file-routes.ts +5 -4
- package/src/runtime/routes/host-transfer-routes.ts +6 -6
- package/src/runtime/routes/http-adapter.ts +16 -1
- package/src/runtime/routes/identity-routes.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -59
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +13 -5
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +19 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/llm-context-normalization.ts +83 -0
- package/src/runtime/routes/mcp-auth-routes.ts +171 -19
- package/src/runtime/routes/migration-rollback-routes.ts +4 -3
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
- package/src/runtime/routes/settings-routes.ts +8 -3
- package/src/runtime/routes/subagents-routes.ts +5 -0
- package/src/runtime/routes/surface-action-routes.ts +42 -56
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +13 -58
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +111 -40
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +16 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
- package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
- package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +22 -32
- package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
- package/src/subagent/index.ts +1 -1
- package/src/subagent/manager.ts +254 -33
- package/src/subagent/types.ts +11 -4
- package/src/telemetry/types.ts +34 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +87 -3
- package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
- package/src/tools/ask-question/ask-question-tool.ts +13 -0
- package/src/tools/executor.ts +4 -4
- package/src/tools/filesystem/search.ts +543 -0
- package/src/tools/registry.ts +38 -0
- package/src/tools/shared/filesystem/path-policy.ts +12 -5
- package/src/tools/subagent/consult-deadline.ts +49 -0
- package/src/tools/subagent/spawn.ts +234 -5
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +9 -2
- package/src/tools/tool-manifest.ts +3 -0
- package/src/tools/types.ts +131 -25
- package/src/tools/workspace-tools/loader.ts +348 -244
- package/src/util/errors.ts +26 -1
- package/src/util/logger.ts +9 -0
- package/src/util/platform.ts +19 -0
- package/src/util/telemetry-db-path.ts +24 -0
- package/src/workflows/library.test.ts +140 -0
- package/src/workflows/library.ts +82 -28
- package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
- package/src/workspace/migrations/031-drop-user-md.ts +232 -148
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +14 -66
- package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +134 -0
- package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
- package/src/daemon/workspace-tools-watcher.ts +0 -328
- package/src/memory/migrations/registry.ts +0 -573
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -153
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
- package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
- package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
- package/src/plugins/defaults/advisor/config.ts +0 -21
- package/src/plugins/defaults/advisor/consult.ts +0 -93
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
- package/src/plugins/defaults/advisor/package.json +0 -14
- package/src/plugins/defaults/advisor/tools/advisor.ts +0 -65
- package/src/providers/openai/__tests__/api-error-detail.test.ts +0 -120
package/src/util/errors.ts
CHANGED
|
@@ -102,6 +102,17 @@ export class AssistantError extends VellumError {
|
|
|
102
102
|
export class ProviderError extends AssistantError {
|
|
103
103
|
/** Delay (in ms) suggested by the server's Retry-After header, if present. */
|
|
104
104
|
public readonly retryAfterMs?: number;
|
|
105
|
+
/** Upstream provider error metadata, parsed from the raw non-2xx body. */
|
|
106
|
+
public readonly apiErrorCode?: string;
|
|
107
|
+
public readonly apiErrorType?: string;
|
|
108
|
+
public readonly apiErrorParam?: string;
|
|
109
|
+
public readonly requestId?: string;
|
|
110
|
+
/**
|
|
111
|
+
* Verbatim upstream non-2xx body (possibly truncated). Persisted so the
|
|
112
|
+
* inspector's Raw tab can show the actual provider error payload, not just
|
|
113
|
+
* the extracted fields.
|
|
114
|
+
*/
|
|
115
|
+
public readonly rawBody?: string;
|
|
105
116
|
/**
|
|
106
117
|
* Tagged daemon-owned abort reason carried over from the AbortSignal that
|
|
107
118
|
* triggered this error. Untyped here to avoid a daemon→util import cycle;
|
|
@@ -114,11 +125,25 @@ export class ProviderError extends AssistantError {
|
|
|
114
125
|
message: string,
|
|
115
126
|
public readonly provider: string,
|
|
116
127
|
public readonly statusCode?: number,
|
|
117
|
-
options?: {
|
|
128
|
+
options?: {
|
|
129
|
+
cause?: unknown;
|
|
130
|
+
retryAfterMs?: number;
|
|
131
|
+
abortReason?: unknown;
|
|
132
|
+
apiErrorCode?: string;
|
|
133
|
+
apiErrorType?: string;
|
|
134
|
+
apiErrorParam?: string;
|
|
135
|
+
requestId?: string;
|
|
136
|
+
rawBody?: string;
|
|
137
|
+
},
|
|
118
138
|
) {
|
|
119
139
|
super(message, ErrorCode.PROVIDER_ERROR, options);
|
|
120
140
|
this.name = "ProviderError";
|
|
121
141
|
this.retryAfterMs = options?.retryAfterMs;
|
|
142
|
+
this.apiErrorCode = options?.apiErrorCode;
|
|
143
|
+
this.apiErrorType = options?.apiErrorType;
|
|
144
|
+
this.apiErrorParam = options?.apiErrorParam;
|
|
145
|
+
this.requestId = options?.requestId;
|
|
146
|
+
this.rawBody = options?.rawBody;
|
|
122
147
|
this.abortReason = options?.abortReason;
|
|
123
148
|
}
|
|
124
149
|
}
|
package/src/util/logger.ts
CHANGED
|
@@ -49,6 +49,15 @@ function logFilePathForDate(dir: string, date: Date): string {
|
|
|
49
49
|
return join(dir, `${LOG_FILE_PREFIX}${formatDate(date)}${LOG_FILE_SUFFIX}`);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Returns the path to today's log file (`<logsDir>/assistant-YYYY-MM-DD.log`).
|
|
54
|
+
* Used by callers that need to open the same file the logger writes to, e.g.
|
|
55
|
+
* the memory worker spawner piping the child's stderr into the log file.
|
|
56
|
+
*/
|
|
57
|
+
export function getCurrentLogFilePath(): string {
|
|
58
|
+
return logFilePathForDate(getLogsDir(), new Date());
|
|
59
|
+
}
|
|
60
|
+
|
|
52
61
|
export function pruneOldLogFiles(dir: string, retentionDays: number): number {
|
|
53
62
|
if (!existsSync(dir)) return 0;
|
|
54
63
|
|
package/src/util/platform.ts
CHANGED
|
@@ -242,6 +242,25 @@ export function getEmbedWorkerPidPath(): string {
|
|
|
242
242
|
return join(getWorkspaceDir(), "embed-worker.pid");
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
+
/** Returns the path to the memory-worker PID file ($VELLUM_WORKSPACE_DIR/memory-worker.pid). */
|
|
246
|
+
export function getMemoryWorkerPidPath(): string {
|
|
247
|
+
return join(getWorkspaceDir(), "memory-worker.pid");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Returns the path to the memory sync-runner marker file
|
|
252
|
+
* ($VELLUM_WORKSPACE_DIR/memory-sync-runner.pid).
|
|
253
|
+
*
|
|
254
|
+
* The daemon's in-process memory-jobs supervisor writes this marker (with its
|
|
255
|
+
* own PID) while the synchronous in-process runner is actively draining the
|
|
256
|
+
* queue, and removes it when it stands down for an out-of-process worker. It
|
|
257
|
+
* lets `assistant memory worker status` report whether the synchronous runner
|
|
258
|
+
* is still going without an IPC round-trip to the daemon.
|
|
259
|
+
*/
|
|
260
|
+
export function getMemorySyncRunnerMarkerPath(): string {
|
|
261
|
+
return join(getWorkspaceDir(), "memory-sync-runner.pid");
|
|
262
|
+
}
|
|
263
|
+
|
|
245
264
|
/**
|
|
246
265
|
* Returns the workspace root for user-facing state.
|
|
247
266
|
*
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { getDataDir } from "./platform.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Path to the dedicated SQLite file that houses telemetry event tables
|
|
7
|
+
* (starting with `watchdog_events`). It lives in the same `data/db` directory
|
|
8
|
+
* as the main DB and is opened on its own connection (see `getTelemetryDb()`
|
|
9
|
+
* in `memory/db-connection.ts`). Splitting telemetry tables into their own
|
|
10
|
+
* file keeps the main DB — and its WAL — small and lets the two files
|
|
11
|
+
* VACUUM/checkpoint independently, so a burst of watchdog events can no longer
|
|
12
|
+
* bloat the main database.
|
|
13
|
+
*
|
|
14
|
+
* Kept in its own leaf module rather than alongside `getDbPath()` in
|
|
15
|
+
* `platform.ts`: `platform.ts` is imported very early and widely, and adding an
|
|
16
|
+
* export to it that low-level consumers (e.g. `db-connection.ts`) pull in
|
|
17
|
+
* across the daemon's large, cyclic import graph trips a Bun link-order bug
|
|
18
|
+
* ("Export named 'getTelemetryDbPath' not found"). Isolating it here keeps
|
|
19
|
+
* `platform.ts`'s module shape stable — same reasoning as `logs-db-path.ts`
|
|
20
|
+
* and `memory-db-path.ts`.
|
|
21
|
+
*/
|
|
22
|
+
export function getTelemetryDbPath(): string {
|
|
23
|
+
return join(getDataDir(), "db", "assistant-telemetry.db");
|
|
24
|
+
}
|
|
@@ -37,6 +37,13 @@ function writeWorkflow(file: string, source: string): void {
|
|
|
37
37
|
writeFileSync(join(dir, file), source, "utf8");
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/** Write `<workspace>/workflows/<name>/workflow.ts` (directory-style). */
|
|
41
|
+
function writeDirWorkflow(name: string, source: string): void {
|
|
42
|
+
const dir = join(workspaceDir, "workflows", name);
|
|
43
|
+
mkdirSync(dir, { recursive: true });
|
|
44
|
+
writeFileSync(join(dir, "workflow.ts"), source, "utf8");
|
|
45
|
+
}
|
|
46
|
+
|
|
40
47
|
describe("listWorkflows", () => {
|
|
41
48
|
test("returns [] and does not create the dir when none exists", () => {
|
|
42
49
|
expect(listWorkflows()).toEqual([]);
|
|
@@ -84,6 +91,51 @@ describe("listWorkflows", () => {
|
|
|
84
91
|
expect(entries.map((e) => e.name)).toEqual(["real"]);
|
|
85
92
|
});
|
|
86
93
|
|
|
94
|
+
test("includes both flat and directory-style workflows", () => {
|
|
95
|
+
writeWorkflow(
|
|
96
|
+
"flat.workflow.ts",
|
|
97
|
+
`export const meta = { name: "flat-wf", description: "flat" };\nreturn 1;`,
|
|
98
|
+
);
|
|
99
|
+
writeDirWorkflow(
|
|
100
|
+
"dir-wf",
|
|
101
|
+
`export const meta = { name: "dir-wf", description: "dir" };\nreturn 1;`,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const entries = listWorkflows().sort((a, b) =>
|
|
105
|
+
a.name.localeCompare(b.name),
|
|
106
|
+
);
|
|
107
|
+
expect(entries.map((e) => e.name)).toEqual(["dir-wf", "flat-wf"]);
|
|
108
|
+
expect(entries[0]!.path).toContain(join("dir-wf", "workflow.ts"));
|
|
109
|
+
expect(entries[1]!.path).toContain("flat.workflow.ts");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("ignores a subdir without a workflow.ts and does not recurse deeper", () => {
|
|
113
|
+
writeWorkflow(
|
|
114
|
+
"real.workflow.ts",
|
|
115
|
+
`export const meta = { name: "real", description: "d" };\nreturn 1;`,
|
|
116
|
+
);
|
|
117
|
+
// Subdir with no entry-point workflow.ts.
|
|
118
|
+
mkdirSync(join(workspaceDir, "workflows", "empty-dir"), {
|
|
119
|
+
recursive: true,
|
|
120
|
+
});
|
|
121
|
+
writeFileSync(
|
|
122
|
+
join(workspaceDir, "workflows", "empty-dir", "notes.ts"),
|
|
123
|
+
`export const meta = { name: "ignored", description: "d" };`,
|
|
124
|
+
"utf8",
|
|
125
|
+
);
|
|
126
|
+
// A nested workflow.ts one level deeper than supported must not be picked up.
|
|
127
|
+
mkdirSync(join(workspaceDir, "workflows", "outer", "inner"), {
|
|
128
|
+
recursive: true,
|
|
129
|
+
});
|
|
130
|
+
writeFileSync(
|
|
131
|
+
join(workspaceDir, "workflows", "outer", "inner", "workflow.ts"),
|
|
132
|
+
`export const meta = { name: "too-deep", description: "d" };`,
|
|
133
|
+
"utf8",
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
expect(listWorkflows().map((e) => e.name)).toEqual(["real"]);
|
|
137
|
+
});
|
|
138
|
+
|
|
87
139
|
test("skips a file whose meta is computed/non-literal (cannot be statically extracted)", () => {
|
|
88
140
|
writeWorkflow(
|
|
89
141
|
"ok.workflow.ts",
|
|
@@ -131,4 +183,92 @@ describe("getWorkflow", () => {
|
|
|
131
183
|
);
|
|
132
184
|
expect(getWorkflow("ghost")).toBeNull();
|
|
133
185
|
});
|
|
186
|
+
|
|
187
|
+
test("resolves a directory-style workflow by meta.name", () => {
|
|
188
|
+
const source = `export const meta = { name: "dir-digest", description: "d" };\nreturn agent("go");`;
|
|
189
|
+
writeDirWorkflow("dir-digest", source);
|
|
190
|
+
|
|
191
|
+
const resolved = getWorkflow("dir-digest");
|
|
192
|
+
expect(resolved).not.toBeNull();
|
|
193
|
+
expect(resolved!.source).toBe(source);
|
|
194
|
+
expect(resolved!.path).toContain(join("dir-digest", "workflow.ts"));
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("resolves a directory-style workflow by directory name when meta.name differs", () => {
|
|
198
|
+
// meta.name is "canonical" but the dir (and caller's name) is "by-dir".
|
|
199
|
+
const source = `export const meta = { name: "canonical", description: "d" };\nreturn 1;`;
|
|
200
|
+
writeDirWorkflow("by-dir", source);
|
|
201
|
+
|
|
202
|
+
expect(getWorkflow("by-dir")!.source).toBe(source);
|
|
203
|
+
// The canonical meta.name also resolves.
|
|
204
|
+
expect(getWorkflow("canonical")!.source).toBe(source);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("prefers a meta.name match over a base-name match", () => {
|
|
208
|
+
// Asking for "wanted" should hit the file whose meta.name is "wanted",
|
|
209
|
+
// not the file whose base name is "wanted" (whose meta.name differs).
|
|
210
|
+
writeWorkflow(
|
|
211
|
+
"wanted.workflow.ts",
|
|
212
|
+
`export const meta = { name: "other", description: "by base name" };\nreturn 1;`,
|
|
213
|
+
);
|
|
214
|
+
const metaSource = `export const meta = { name: "wanted", description: "by meta name" };\nreturn 1;`;
|
|
215
|
+
writeWorkflow("real.workflow.ts", metaSource);
|
|
216
|
+
|
|
217
|
+
expect(getWorkflow("wanted")!.source).toBe(metaSource);
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe("name collisions", () => {
|
|
222
|
+
test("a directory wins over a same-base-name flat file", () => {
|
|
223
|
+
writeWorkflow(
|
|
224
|
+
"foo.workflow.ts",
|
|
225
|
+
`export const meta = { name: "foo", description: "flat" };\nreturn "FLAT";`,
|
|
226
|
+
);
|
|
227
|
+
writeDirWorkflow(
|
|
228
|
+
"foo",
|
|
229
|
+
`export const meta = { name: "foo", description: "dir" };\nreturn "DIR";`,
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
const resolved = getWorkflow("foo")!;
|
|
233
|
+
expect(resolved.source).toContain("DIR");
|
|
234
|
+
expect(resolved.path.endsWith(join("foo", "workflow.ts"))).toBe(true);
|
|
235
|
+
|
|
236
|
+
const foos = listWorkflows().filter((e) => e.name === "foo");
|
|
237
|
+
expect(foos).toHaveLength(1);
|
|
238
|
+
expect(foos[0]!.description).toBe("dir");
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("the shadowed flat file's own meta.name no longer resolves", () => {
|
|
242
|
+
writeWorkflow(
|
|
243
|
+
"bar.workflow.ts",
|
|
244
|
+
`export const meta = { name: "flat-name", description: "flat" };\nreturn "FLAT";`,
|
|
245
|
+
);
|
|
246
|
+
writeDirWorkflow(
|
|
247
|
+
"bar",
|
|
248
|
+
`export const meta = { name: "dir-name", description: "dir" };\nreturn "DIR";`,
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
expect(getWorkflow("dir-name")!.source).toContain("DIR");
|
|
252
|
+
expect(getWorkflow("flat-name")).toBeNull();
|
|
253
|
+
// Base-name fallback also lands on the directory.
|
|
254
|
+
expect(getWorkflow("bar")!.source).toContain("DIR");
|
|
255
|
+
expect(listWorkflows().map((e) => e.name)).toEqual(["dir-name"]);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("listWorkflows dedups a duplicate meta.name across different files", () => {
|
|
259
|
+
writeWorkflow(
|
|
260
|
+
"a.workflow.ts",
|
|
261
|
+
`export const meta = { name: "dup", description: "from-a" };\nreturn "A";`,
|
|
262
|
+
);
|
|
263
|
+
writeDirWorkflow(
|
|
264
|
+
"b",
|
|
265
|
+
`export const meta = { name: "dup", description: "from-b" };\nreturn "B";`,
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
// One entry; the directory (yielded first) wins, consistent with getWorkflow.
|
|
269
|
+
const dups = listWorkflows().filter((e) => e.name === "dup");
|
|
270
|
+
expect(dups).toHaveLength(1);
|
|
271
|
+
expect(dups[0]!.description).toBe("from-b");
|
|
272
|
+
expect(getWorkflow("dup")!.source).toContain("B");
|
|
273
|
+
});
|
|
134
274
|
});
|
package/src/workflows/library.ts
CHANGED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
* Saved-workflow LIBRARY — named workflows the assistant can invoke by name and
|
|
3
3
|
* the scheduler can trigger.
|
|
4
4
|
*
|
|
5
|
-
* Saved workflows live at `<workspace>/workflows
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
5
|
+
* Saved workflows live at `<workspace>/workflows/`. The preferred layout is
|
|
6
|
+
* directory-style `<name>/workflow.ts` (so a workflow and its state can sit in
|
|
7
|
+
* one folder); flat `<name>.workflow.ts` is still supported but discouraged. On
|
|
8
|
+
* a base-name collision the directory wins. Each is a normal workflow script: a
|
|
9
|
+
* leading literal
|
|
10
|
+
* `export const meta = { name, description }` followed by the script body. The
|
|
11
|
+
* library only reads the STATIC `meta` (via {@link extractWorkflowMeta}, a
|
|
12
|
+
* pure-literal extractor that never executes the untrusted source — only the
|
|
13
|
+
* QuickJS sandbox may run it) and the raw source. Resolution to an actual run
|
|
14
|
+
* goes through {@link executeWorkflow} / {@link WorkflowRunManager}, which run
|
|
15
|
+
* the source in the sandbox.
|
|
12
16
|
*
|
|
13
17
|
* The directory is read lazily and is NOT created eagerly: if it does not exist,
|
|
14
18
|
* {@link listWorkflows} returns `[]` and {@link getWorkflow} returns `null`.
|
|
@@ -19,7 +23,7 @@ import { join } from "node:path";
|
|
|
19
23
|
|
|
20
24
|
import { getLogger } from "../util/logger.js";
|
|
21
25
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
22
|
-
import { extractWorkflowMeta } from "./engine.js";
|
|
26
|
+
import { extractWorkflowMeta, type WorkflowMeta } from "./engine.js";
|
|
23
27
|
|
|
24
28
|
const log = getLogger("workflow-library");
|
|
25
29
|
|
|
@@ -49,31 +53,66 @@ function workflowsDir(): string {
|
|
|
49
53
|
return join(getWorkspaceDir(), "workflows");
|
|
50
54
|
}
|
|
51
55
|
|
|
56
|
+
/** Filename of the entry-point script inside a directory-style workflow. */
|
|
57
|
+
const DIRECTORY_ENTRY = "workflow.ts";
|
|
58
|
+
|
|
52
59
|
/** The filename (sans `.workflow.ts`) for `<workspace>/workflows/<base>.workflow.ts`. */
|
|
53
60
|
function fileBaseName(file: string): string {
|
|
54
61
|
return file.slice(0, -WORKFLOW_SUFFIX.length);
|
|
55
62
|
}
|
|
56
63
|
|
|
57
|
-
/** A
|
|
64
|
+
/** A saved workflow read off disk, before meta extraction. */
|
|
58
65
|
interface RawWorkflowFile {
|
|
59
|
-
|
|
66
|
+
/** Base identity for the filename/dir fallback match (see {@link getWorkflow}). */
|
|
67
|
+
baseName: string;
|
|
60
68
|
path: string;
|
|
61
69
|
source: string;
|
|
62
70
|
}
|
|
63
71
|
|
|
64
72
|
/**
|
|
65
|
-
* Yield every readable
|
|
66
|
-
*
|
|
67
|
-
*
|
|
73
|
+
* Yield every readable saved workflow in a STABLE order (the raw filesystem
|
|
74
|
+
* listing order is not guaranteed, so callers that take "the first match" stay
|
|
75
|
+
* deterministic). Directory-style `<name>/workflow.ts` is yielded first and
|
|
76
|
+
* WINS: a flat `<name>.workflow.ts` with the same base name is shadowed (skipped
|
|
77
|
+
* with a warning). Yields nothing if the directory does not exist — it is never
|
|
78
|
+
* created. An unreadable entry is skipped with a logged warning.
|
|
68
79
|
*/
|
|
69
80
|
function* readWorkflowFiles(): Generator<RawWorkflowFile> {
|
|
70
81
|
const dir = workflowsDir();
|
|
71
82
|
if (!existsSync(dir)) return;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
const entries = readdirSync(dir).sort();
|
|
84
|
+
const seen = new Set<string>();
|
|
85
|
+
|
|
86
|
+
// Directory-style first, so it wins over a same-base-name flat file.
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
if (entry.endsWith(WORKFLOW_SUFFIX)) continue;
|
|
89
|
+
const path = join(dir, entry, DIRECTORY_ENTRY);
|
|
90
|
+
if (!existsSync(path)) continue;
|
|
91
|
+
try {
|
|
92
|
+
const source = readFileSync(path, "utf8");
|
|
93
|
+
seen.add(entry);
|
|
94
|
+
yield { baseName: entry, path, source };
|
|
95
|
+
} catch (err) {
|
|
96
|
+
log.warn({ err, path }, "Failed to read saved workflow; skipping");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Flat files second; skip any shadowed by a directory of the same base name.
|
|
101
|
+
for (const entry of entries) {
|
|
102
|
+
if (!entry.endsWith(WORKFLOW_SUFFIX)) continue;
|
|
103
|
+
const baseName = fileBaseName(entry);
|
|
104
|
+
const path = join(dir, entry);
|
|
105
|
+
if (seen.has(baseName)) {
|
|
106
|
+
log.warn(
|
|
107
|
+
{ path },
|
|
108
|
+
`Flat workflow "${entry}" is shadowed by directory "${baseName}/"; skipping`,
|
|
109
|
+
);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
75
112
|
try {
|
|
76
|
-
|
|
113
|
+
const source = readFileSync(path, "utf8");
|
|
114
|
+
seen.add(baseName);
|
|
115
|
+
yield { baseName, path, source };
|
|
77
116
|
} catch (err) {
|
|
78
117
|
log.warn({ err, path }, "Failed to read saved workflow; skipping");
|
|
79
118
|
}
|
|
@@ -81,42 +120,57 @@ function* readWorkflowFiles(): Generator<RawWorkflowFile> {
|
|
|
81
120
|
}
|
|
82
121
|
|
|
83
122
|
/**
|
|
84
|
-
* List every saved workflow with a statically-extractable `meta
|
|
85
|
-
* `meta`
|
|
86
|
-
*
|
|
123
|
+
* List every saved workflow with a statically-extractable `meta`, deduplicated
|
|
124
|
+
* by `meta.name` — the first in {@link readWorkflowFiles}' stable order wins, a
|
|
125
|
+
* later duplicate is skipped with a warning (matching {@link getWorkflow}'s
|
|
126
|
+
* winner). A file whose `meta` cannot be statically extracted is skipped with a
|
|
127
|
+
* warning rather than failing the whole listing.
|
|
87
128
|
*/
|
|
88
129
|
export function listWorkflows(): SavedWorkflowEntry[] {
|
|
89
130
|
const entries: SavedWorkflowEntry[] = [];
|
|
131
|
+
const seenNames = new Set<string>();
|
|
90
132
|
for (const { path, source } of readWorkflowFiles()) {
|
|
133
|
+
let meta: WorkflowMeta;
|
|
91
134
|
try {
|
|
92
|
-
|
|
93
|
-
entries.push({ name: meta.name, description: meta.description, path });
|
|
135
|
+
meta = extractWorkflowMeta(source);
|
|
94
136
|
} catch (err) {
|
|
95
137
|
log.warn(
|
|
96
138
|
{ err, path },
|
|
97
139
|
"Saved workflow has no statically-extractable meta; skipping",
|
|
98
140
|
);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (seenNames.has(meta.name)) {
|
|
144
|
+
log.warn(
|
|
145
|
+
{ path, name: meta.name },
|
|
146
|
+
`Duplicate workflow name "${meta.name}"; skipping shadowed entry`,
|
|
147
|
+
);
|
|
148
|
+
continue;
|
|
99
149
|
}
|
|
150
|
+
seenNames.add(meta.name);
|
|
151
|
+
entries.push({ name: meta.name, description: meta.description, path });
|
|
100
152
|
}
|
|
101
153
|
return entries;
|
|
102
154
|
}
|
|
103
155
|
|
|
104
156
|
/**
|
|
105
|
-
* Resolve a saved workflow by name
|
|
106
|
-
*
|
|
107
|
-
*
|
|
157
|
+
* Resolve a saved workflow by name, deterministically — {@link readWorkflowFiles}
|
|
158
|
+
* yields in a stable order with directory-over-flat precedence, so "the first
|
|
159
|
+
* match" is well-defined. Matches `meta.name` first (the canonical identity),
|
|
160
|
+
* then falls back to the base name (directory or flat-file). Returns the source
|
|
161
|
+
* + path, or `null` if nothing matches.
|
|
108
162
|
*/
|
|
109
163
|
export function getWorkflow(name: string): SavedWorkflowSource | null {
|
|
110
164
|
let fileMatch: SavedWorkflowSource | null = null;
|
|
111
|
-
for (const {
|
|
165
|
+
for (const { baseName, path, source } of readWorkflowFiles()) {
|
|
112
166
|
// Prefer a `meta.name` match — it is the canonical identity.
|
|
113
167
|
try {
|
|
114
168
|
if (extractWorkflowMeta(source).name === name) return { source, path };
|
|
115
169
|
} catch {
|
|
116
170
|
// Fall through: a file with non-extractable meta can still match by name.
|
|
117
171
|
}
|
|
118
|
-
// Remember the first
|
|
119
|
-
if (fileMatch === null &&
|
|
172
|
+
// Remember the first base-name match as a fallback.
|
|
173
|
+
if (fileMatch === null && baseName === name) {
|
|
120
174
|
fileMatch = { source, path };
|
|
121
175
|
}
|
|
122
176
|
}
|
|
@@ -8,11 +8,6 @@ import {
|
|
|
8
8
|
} from "node:fs";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
|
|
11
|
-
import { desc, eq } from "drizzle-orm";
|
|
12
|
-
|
|
13
|
-
import { generateUserFileSlug } from "../../contacts/contact-store.js";
|
|
14
|
-
import { getDb } from "../../memory/db-connection.js";
|
|
15
|
-
import { contacts } from "../../memory/schema/contacts.js";
|
|
16
11
|
import type { WorkspaceMigration } from "./types.js";
|
|
17
12
|
|
|
18
13
|
// ── Inlined helpers ───────────────────────────────────────────────
|
|
@@ -122,35 +117,9 @@ export const seedPersonaDirsMigration: WorkspaceMigration = {
|
|
|
122
117
|
// template from disk, since migration 031 deletes that template file.
|
|
123
118
|
if (content === LEGACY_USER_MD_TEMPLATE_STRIPPED) return;
|
|
124
119
|
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const db = getDb();
|
|
129
|
-
const guardian = db
|
|
130
|
-
.select()
|
|
131
|
-
.from(contacts)
|
|
132
|
-
.where(eq(contacts.role, "guardian"))
|
|
133
|
-
.orderBy(desc(contacts.createdAt))
|
|
134
|
-
.limit(1)
|
|
135
|
-
.get();
|
|
136
|
-
|
|
137
|
-
if (guardian) {
|
|
138
|
-
if (guardian.userFile) {
|
|
139
|
-
destFilename = guardian.userFile;
|
|
140
|
-
} else {
|
|
141
|
-
const slug = generateUserFileSlug(guardian.displayName);
|
|
142
|
-
db.update(contacts)
|
|
143
|
-
.set({ userFile: slug })
|
|
144
|
-
.where(eq(contacts.id, guardian.id))
|
|
145
|
-
.run();
|
|
146
|
-
destFilename = slug;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
} catch {
|
|
150
|
-
// DB might not be initialized yet — fall back to guardian.md
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const destPath = join(workspaceDir, "users", destFilename);
|
|
120
|
+
// Seed the canonical `users/guardian.md` — the runtime persona resolver
|
|
121
|
+
// falls back to this name, so no assistant-DB lookup is needed.
|
|
122
|
+
const destPath = join(workspaceDir, "users", "guardian.md");
|
|
154
123
|
if (!existsSync(destPath)) {
|
|
155
124
|
copyFileSync(userMdPath, destPath);
|
|
156
125
|
}
|
|
@@ -8,10 +8,6 @@ import {
|
|
|
8
8
|
} from "node:fs";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
|
|
11
|
-
import { desc, eq } from "drizzle-orm";
|
|
12
|
-
|
|
13
|
-
import { getDb } from "../../memory/db-connection.js";
|
|
14
|
-
import { contacts } from "../../memory/schema/contacts.js";
|
|
15
11
|
import type { WorkspaceMigration } from "./types.js";
|
|
16
12
|
|
|
17
13
|
export const scopeJournalToGuardianMigration: WorkspaceMigration = {
|
|
@@ -40,26 +36,9 @@ export const scopeJournalToGuardianMigration: WorkspaceMigration = {
|
|
|
40
36
|
});
|
|
41
37
|
if (mdFiles.length === 0) return;
|
|
42
38
|
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const db = getDb();
|
|
47
|
-
const guardian = db
|
|
48
|
-
.select()
|
|
49
|
-
.from(contacts)
|
|
50
|
-
.where(eq(contacts.role, "guardian"))
|
|
51
|
-
.orderBy(desc(contacts.createdAt))
|
|
52
|
-
.limit(1)
|
|
53
|
-
.get();
|
|
54
|
-
if (guardian?.userFile) {
|
|
55
|
-
slug = guardian.userFile.replace(/\.md$/, "");
|
|
56
|
-
}
|
|
57
|
-
} catch {
|
|
58
|
-
// DB not ready — use fallback "guardian"
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Create per-user directory and move files (renameSync preserves birthtimes)
|
|
62
|
-
const destDir = join(journalDir, slug);
|
|
39
|
+
// Scope under the canonical `guardian` slug — the runtime resolver falls
|
|
40
|
+
// back to it, so no assistant-DB lookup is needed.
|
|
41
|
+
const destDir = join(journalDir, "guardian");
|
|
63
42
|
mkdirSync(destDir, { recursive: true });
|
|
64
43
|
for (const f of mdFiles) {
|
|
65
44
|
const src = join(journalDir, f);
|