@vellumai/assistant 0.11.2 → 0.11.3-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +58 -3
- package/docs/trusted-contact-access.md +9 -5
- package/docs/vellum-doctor.md +195 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
- package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
- package/node_modules/@vellumai/environments/src/index.ts +17 -6
- package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
- package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
- package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
- package/openapi.yaml +589 -30
- package/package.json +1 -1
- package/src/__tests__/agent-image-optimize.test.ts +4 -4
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
- package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
- package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/answered-question-persistence.test.ts +240 -0
- package/src/__tests__/anthropic-provider.test.ts +165 -30
- package/src/__tests__/app-control-flow.test.ts +9 -8
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
- package/src/__tests__/attachments-store.test.ts +130 -68
- package/src/__tests__/attachments.test.ts +36 -36
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
- package/src/__tests__/canned-reply-release.test.ts +86 -0
- package/src/__tests__/channel-guardian.test.ts +59 -29
- package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
- package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
- package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
- package/src/__tests__/consult-deadline.test.ts +65 -0
- package/src/__tests__/conversation-agent-loop.test.ts +438 -2
- package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
- package/src/__tests__/conversation-disk-view.test.ts +4 -4
- package/src/__tests__/conversation-error.test.ts +22 -3
- package/src/__tests__/conversation-fork-crud.test.ts +5 -1
- package/src/__tests__/conversation-fork-referential.test.ts +352 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
- package/src/__tests__/conversation-group-tools.test.ts +32 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
- package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
- package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
- package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
- package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
- package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
- package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
- package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
- package/src/__tests__/conversation-queue.test.ts +433 -1
- package/src/__tests__/conversation-retry-route.test.ts +3 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
- package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
- package/src/__tests__/conversation-store.test.ts +26 -10
- package/src/__tests__/conversation-summarize-route.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
- package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
- package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
- package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
- package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
- package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
- package/src/__tests__/credential-prompt-route.test.ts +57 -7
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +89 -28
- package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
- package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
- package/src/__tests__/disk-pressure-guard.test.ts +55 -55
- package/src/__tests__/disk-pressure-routes.test.ts +10 -10
- package/src/__tests__/disk-pressure-tools.test.ts +2 -2
- package/src/__tests__/disk-usage.test.ts +35 -20
- package/src/__tests__/document-sync-tags.test.ts +379 -0
- package/src/__tests__/document-update-default-surface.test.ts +28 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
- package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
- package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
- package/src/__tests__/file-list-tool.test.ts +12 -10
- package/src/__tests__/file-ops-service.test.ts +151 -44
- package/src/__tests__/file-read-container-boundary.test.ts +137 -0
- package/src/__tests__/file-read-tool.test.ts +21 -9
- package/src/__tests__/file-url-path-guard.test.ts +83 -0
- package/src/__tests__/filesystem-tools.test.ts +82 -66
- package/src/__tests__/guardian-outbound-http.test.ts +8 -8
- package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
- package/src/__tests__/helpers/hub-clients.ts +42 -0
- package/src/__tests__/helpers/mock-conversation.ts +46 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
- package/src/__tests__/history-repair.test.ts +112 -12
- package/src/__tests__/host-bash-proxy.test.ts +10 -5
- package/src/__tests__/host-bash-routes.test.ts +67 -0
- package/src/__tests__/host-browser-proxy.test.ts +2 -2
- package/src/__tests__/host-cu-proxy.test.ts +3 -7
- package/src/__tests__/host-file-proxy.test.ts +3 -7
- package/src/__tests__/host-file-read-tool.test.ts +5 -1
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
- package/src/__tests__/http-conversation-lineage.test.ts +2 -5
- package/src/__tests__/http-user-message-parity.test.ts +44 -0
- package/src/__tests__/identity-routes.test.ts +13 -13
- package/src/__tests__/image-conversion.test.ts +29 -26
- package/src/__tests__/image-source-path-reinject.test.ts +2 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
- package/src/__tests__/introduction-card-resolver.test.ts +6 -1
- package/src/__tests__/list-messages-attachments.test.ts +56 -32
- package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
- package/src/__tests__/list-messages-page-latest.test.ts +61 -61
- package/src/__tests__/list-messages-provider-error.test.ts +6 -6
- package/src/__tests__/list-messages-queued.test.ts +49 -6
- package/src/__tests__/list-messages-system-card.test.ts +4 -4
- package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
- package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
- package/src/__tests__/llm-usage-store.test.ts +55 -0
- package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
- package/src/__tests__/media-stream-server-integration.test.ts +36 -0
- package/src/__tests__/migration-export-http.test.ts +1 -1
- package/src/__tests__/migration-import-commit-http.test.ts +1 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
- package/src/__tests__/migration-validate-http.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +65 -3
- package/src/__tests__/notification-platform-adapter.test.ts +104 -0
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
- package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
- package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
- package/src/__tests__/path-policy.test.ts +164 -1
- package/src/__tests__/persist-media-references.test.ts +2 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
- package/src/__tests__/persist-unsendable-image.test.ts +23 -23
- package/src/__tests__/persona-resolver.test.ts +154 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
- package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
- package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
- package/src/__tests__/profiler-routes.test.ts +13 -13
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
- package/src/__tests__/proxy-approval-callback.test.ts +3 -3
- package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
- package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
- package/src/__tests__/resolve-trust-class.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
- package/src/__tests__/run-due-schedules.test.ts +174 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
- package/src/__tests__/schedule-routes.test.ts +730 -8
- package/src/__tests__/schedule-store.test.ts +540 -0
- package/src/__tests__/schedule-tools.test.ts +103 -0
- package/src/__tests__/scheduler-wake.test.ts +5 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
- package/src/__tests__/secure-keys.test.ts +91 -0
- package/src/__tests__/server-history-render.test.ts +97 -7
- package/src/__tests__/size-guard.test.ts +8 -8
- package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
- package/src/__tests__/skill-tool-factory.test.ts +247 -0
- package/src/__tests__/skills-install-staging.test.ts +15 -6
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
- package/src/__tests__/slack-inbound-verification.test.ts +4 -1
- package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
- package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
- package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
- package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
- package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
- package/src/__tests__/subagent-disposal.test.ts +10 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
- package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
- package/src/__tests__/subagent-manager-notify.test.ts +148 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/subagent-persistence.test.ts +141 -0
- package/src/__tests__/subagent-role-registry.test.ts +172 -63
- package/src/__tests__/subagent-role-resolution.test.ts +88 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
- package/src/__tests__/subagent-terminal-message.test.ts +88 -1
- package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
- package/src/__tests__/subagent-tools.test.ts +1901 -108
- package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
- package/src/__tests__/system-prompt.test.ts +82 -7
- package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
- package/src/__tests__/tool-result-spool.test.ts +74 -18
- package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
- package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
- package/src/__tests__/tools-audio-read.test.ts +10 -10
- package/src/__tests__/tools-get-route.test.ts +85 -12
- package/src/__tests__/tools-list-cli.test.ts +171 -0
- package/src/__tests__/top-level-renderer.test.ts +54 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
- package/src/__tests__/turn-tail-chain.test.ts +135 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
- package/src/__tests__/ui-visual-surface.test.ts +5 -4
- package/src/__tests__/ui-work-result-surface.test.ts +5 -4
- package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
- package/src/__tests__/usage-routes.test.ts +62 -0
- package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
- package/src/__tests__/visible-app-context.test.ts +20 -4
- package/src/__tests__/web-search-history.test.ts +96 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
- package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
- package/src/acp/failure-error.ts +3 -5
- package/src/agent/attachments.ts +26 -24
- package/src/agent/history-repair/history-repair.ts +27 -26
- package/src/agent/image-optimize.ts +6 -6
- package/src/agent/loop.ts +40 -22
- package/src/agent/message-types.ts +3 -3
- package/src/api/constants/document-tools.ts +55 -0
- package/src/api/events/question-answered.ts +53 -0
- package/src/api/events/tool-result.ts +7 -0
- package/src/api/index.ts +13 -0
- package/src/api/responses/conversation-message.ts +11 -0
- package/src/api/surfaces-oauth-connect.test.ts +96 -0
- package/src/api/surfaces.ts +36 -8
- package/src/approvals/guardian-channel-delivery.ts +83 -0
- package/src/approvals/guardian-expiry-notifier.ts +13 -11
- package/src/approvals/guardian-request-resolvers.ts +222 -135
- package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
- package/src/calls/media-stream-server.ts +9 -1
- package/src/calls/voice-session-bridge.ts +38 -0
- package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
- package/src/channels/config.ts +7 -2
- package/src/channels/gateway-verification-sessions.ts +7 -2
- package/src/channels/types.ts +7 -1
- package/src/cli/commands/__tests__/cache.test.ts +7 -4
- package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
- package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
- package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
- package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
- package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
- package/src/cli/commands/__tests__/plugins.test.ts +692 -0
- package/src/cli/commands/__tests__/schedules.test.ts +136 -37
- package/src/cli/commands/conversations.ts +7 -3
- package/src/cli/commands/credentials.help.ts +7 -0
- package/src/cli/commands/inference-profiles.ts +9 -3
- package/src/cli/commands/inference.help.ts +16 -2
- package/src/cli/commands/mcp.help.ts +4 -2
- package/src/cli/commands/memory/memory-retrospective.ts +18 -0
- package/src/cli/commands/notifications.ts +3 -6
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
- package/src/cli/commands/oauth/connect.test.ts +26 -1
- package/src/cli/commands/oauth/connect.ts +4 -1
- package/src/cli/commands/oauth/index.help.ts +3 -1
- package/src/cli/commands/oauth/providers.ts +3 -0
- package/src/cli/commands/plugins.help.ts +25 -2
- package/src/cli/commands/plugins.ts +289 -10
- package/src/cli/commands/schedules.help.ts +14 -12
- package/src/cli/commands/schedules.ts +19 -5
- package/src/cli/commands/tools.help.ts +4 -2
- package/src/cli/commands/tools.ts +38 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
- package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
- package/src/cli/lib/install-from-github.ts +61 -0
- package/src/cli/lib/install-from-platform.ts +13 -2
- package/src/cli/lib/plugin-surfaces.ts +124 -4
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/cli.ts +1 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
- package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
- package/src/config/__tests__/profile-tool-support.test.ts +131 -0
- package/src/config/bundled-skills/AGENTS.md +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
- package/src/config/bundled-skills/schedule/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
- package/src/config/bundled-skills/subagent/SKILL.md +53 -22
- package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
- package/src/config/bundled-skills/visualize/SKILL.md +1 -1
- package/src/config/default-profile-catalog.ts +6 -2
- package/src/config/feature-flag-registry.json +25 -17
- package/src/config/llm-resolver.ts +45 -15
- package/src/config/loader.ts +70 -1
- package/src/config/profile-tool-support.ts +110 -0
- package/src/config/sanitize-for-transfer.ts +11 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/__tests__/stt.test.ts +20 -5
- package/src/config/schemas/channels.ts +13 -0
- package/src/config/schemas/memory-retrospective.ts +25 -0
- package/src/config/schemas/plugin-updates.ts +55 -0
- package/src/config/schemas/services.ts +8 -4
- package/src/config/schemas/stt.ts +17 -5
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
- package/src/contacts/guardian-delivery-reader.ts +29 -2
- package/src/context/compactor.ts +4 -4
- package/src/context/post-turn-tool-result-truncation.ts +32 -16
- package/src/context/strip-injections.ts +42 -0
- package/src/context/tool-result-spool.ts +32 -18
- package/src/credential-execution/ces-runtime.ts +3 -0
- package/src/credential-execution/client.ts +22 -1
- package/src/credential-execution/process-manager.test.ts +99 -0
- package/src/credential-execution/process-manager.ts +62 -16
- package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
- package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
- package/src/daemon/channel-ui-capability.ts +4 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
- package/src/daemon/conversation-agent-loop.ts +318 -148
- package/src/daemon/conversation-attachments.ts +1 -1
- package/src/daemon/conversation-error.ts +8 -1
- package/src/daemon/conversation-initial-prompt.ts +16 -38
- package/src/daemon/conversation-lifecycle.ts +154 -32
- package/src/daemon/conversation-messaging.ts +106 -43
- package/src/daemon/conversation-process.ts +131 -33
- package/src/daemon/conversation-queue-manager.ts +10 -0
- package/src/daemon/conversation-runtime-assembly.ts +9 -4
- package/src/daemon/conversation-store.ts +52 -3
- package/src/daemon/conversation-surface-state.ts +9 -0
- package/src/daemon/conversation-surfaces.ts +403 -210
- package/src/daemon/conversation-tool-setup.ts +56 -7
- package/src/daemon/conversation-turn-finalize.ts +134 -89
- package/src/daemon/conversation.ts +111 -27
- package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
- package/src/daemon/disk-pressure-guard.ts +3 -3
- package/src/daemon/doordash-steps.ts +5 -5
- package/src/daemon/external-plugins-bootstrap.ts +7 -1
- package/src/daemon/handlers/config-channels.ts +48 -5
- package/src/daemon/handlers/conversation-history.ts +1 -1
- package/src/daemon/handlers/conversations.ts +62 -17
- package/src/daemon/handlers/shared.ts +12 -0
- package/src/daemon/handlers/skills.ts +11 -1
- package/src/daemon/identity-helpers.ts +38 -10
- package/src/daemon/install-assistant-command.ts +322 -0
- package/src/daemon/lifecycle.ts +14 -3
- package/src/daemon/message-types/conversations.ts +6 -7
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/persist-media-references.ts +23 -14
- package/src/daemon/process-message.ts +6 -6
- package/src/daemon/tool-setup-types.ts +19 -122
- package/src/daemon/tool-side-effects.ts +17 -94
- package/src/daemon/trust-context-types.ts +39 -0
- package/src/daemon/trust-context.ts +14 -6
- package/src/daemon/turn-tail-chain.ts +73 -0
- package/src/daemon/web-search-history.ts +47 -33
- package/src/heartbeat/heartbeat-service.ts +6 -6
- package/src/ipc/__tests__/socket-path.test.ts +21 -0
- package/src/ipc/assistant-server.ts +17 -10
- package/src/ipc/cli-client.ts +10 -1
- package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
- package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
- package/src/ipc/socket-cleanup.ts +9 -7
- package/src/ipc/socket-path.ts +4 -96
- package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
- package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
- package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
- package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
- package/src/live-voice/__tests__/protocol.test.ts +52 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
- package/src/live-voice/live-voice-archive.ts +13 -13
- package/src/live-voice/live-voice-connection.ts +7 -1
- package/src/live-voice/live-voice-photo.ts +169 -0
- package/src/live-voice/live-voice-session.ts +108 -4
- package/src/live-voice/protocol.ts +89 -1
- package/src/mcp/client.ts +10 -0
- package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
- package/src/messaging/providers/callback-routing.ts +1 -0
- package/src/messaging/providers/discord/api.ts +312 -0
- package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
- package/src/messaging/providers/discord/render.test.ts +204 -0
- package/src/messaging/providers/discord/render.ts +195 -0
- package/src/messaging/providers/discord/send.test.ts +179 -0
- package/src/messaging/providers/discord/send.ts +212 -0
- package/src/messaging/providers/discord/transport.ts +81 -0
- package/src/messaging/providers/index.ts +3 -1
- package/src/messaging/providers/retry-policy.test.ts +247 -0
- package/src/messaging/providers/retry-policy.ts +206 -0
- package/src/messaging/providers/slack/mention-source.test.ts +285 -0
- package/src/messaging/providers/slack/mention-source.ts +287 -0
- package/src/messaging/providers/telegram-bot/api.ts +47 -130
- package/src/messaging/providers/whatsapp/api.ts +47 -136
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
- package/src/monitoring/db-integrity-sample.ts +6 -1
- package/src/monitoring/plugin-auto-update.ts +359 -0
- package/src/monitoring/resource-sampler.ts +21 -5
- package/src/monitoring/worker.ts +13 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
- package/src/notifications/__tests__/broadcaster.test.ts +37 -1
- package/src/notifications/__tests__/copy-composer.test.ts +165 -0
- package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
- package/src/notifications/__tests__/notification-utils.test.ts +249 -0
- package/src/notifications/adapters/platform.ts +31 -2
- package/src/notifications/assistant-reply-producer.ts +79 -32
- package/src/notifications/broadcaster.ts +30 -10
- package/src/notifications/copy-composer.ts +69 -0
- package/src/notifications/emit-signal.ts +82 -15
- package/src/notifications/events-store.ts +10 -2
- package/src/notifications/home-feed-side-effect.ts +6 -3
- package/src/notifications/notification-utils.ts +173 -5
- package/src/notifications/signal.ts +12 -0
- package/src/oauth/AGENTS.md +4 -2
- package/src/oauth/byo-connection.ts +6 -1
- package/src/oauth/credential-token-resolver.ts +3 -10
- package/src/oauth/manual-token-connection.ts +29 -64
- package/src/oauth/manual-token-providers.ts +62 -0
- package/src/oauth/provider-serializer.ts +13 -0
- package/src/oauth/seed-providers.ts +62 -11
- package/src/onboarding/onboarding-events-store.ts +91 -1
- package/src/permissions/question-prompter.test.ts +4 -0
- package/src/permissions/question-prompter.ts +21 -4
- package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
- package/src/persistence/attachments-store.ts +15 -15
- package/src/persistence/conversation-attention-store.ts +30 -10
- package/src/persistence/conversation-bootstrap.ts +17 -0
- package/src/persistence/conversation-crud.ts +363 -97
- package/src/persistence/conversation-group-migration.ts +1 -1
- package/src/persistence/conversation-key-store.ts +38 -10
- package/src/persistence/conversation-lineage.ts +226 -0
- package/src/persistence/conversation-plugin-facade.ts +28 -3
- package/src/persistence/conversation-queries.ts +207 -33
- package/src/persistence/conversation-types.ts +107 -1
- package/src/persistence/jobs-store.ts +32 -8
- package/src/persistence/llm-usage-store.ts +22 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
- package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
- package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
- package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
- package/src/persistence/schema/conversations.ts +32 -0
- package/src/persistence/schema/infrastructure.ts +3 -0
- package/src/persistence/steps.ts +30 -0
- package/src/persistence/subagent-store.ts +148 -6
- package/src/platform/managed-speech.test.ts +20 -0
- package/src/platform/managed-speech.ts +9 -0
- package/src/plugin-api/index.ts +8 -0
- package/src/plugin-api/webhook-url.ts +151 -0
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
- package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
- package/src/plugins/collect-source-versions.ts +4 -27
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
- package/src/plugins/defaults/exploration-drift/package.json +1 -1
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
- package/src/plugins/defaults/image-recovery/recover.ts +67 -57
- package/src/plugins/defaults/index.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
- package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
- package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
- package/src/plugins/defaults/memory/job-handlers.ts +87 -5
- package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
- package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
- package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
- package/src/plugins/defaults/memory/v2/router.ts +8 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
- package/src/plugins/external-plugin-loader.ts +17 -8
- package/src/plugins/installed-plugin-dirs.ts +101 -0
- package/src/plugins/mtime-cache.ts +34 -22
- package/src/plugins/types.ts +69 -8
- package/src/prompts/persona-resolver.ts +6 -2
- package/src/prompts/system-prompt.ts +72 -17
- package/src/providers/__tests__/inference.test.ts +40 -0
- package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
- package/src/providers/__tests__/retry-callsite.test.ts +67 -0
- package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
- package/src/providers/anthropic/client.ts +112 -95
- package/src/providers/connection-resolution.ts +35 -1
- package/src/providers/gemini/client.ts +5 -5
- package/src/providers/inference/adapter-factory.ts +16 -10
- package/src/providers/inference/auth.ts +16 -0
- package/src/providers/inference/connection-availability.ts +164 -12
- package/src/providers/inference/connections.ts +25 -1
- package/src/providers/media-resolve.ts +21 -11
- package/src/providers/model-catalog.ts +15 -0
- package/src/providers/openai/chat-completions-provider.ts +4 -4
- package/src/providers/openai/responses-provider.ts +12 -25
- package/src/providers/registry.ts +9 -4
- package/src/providers/retry.ts +25 -0
- package/src/providers/server-tool-pairing.ts +134 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
- package/src/providers/speech-to-text/deepgram.ts +1 -0
- package/src/providers/speech-to-text/resolve.ts +80 -10
- package/src/providers/speech-to-text/vellum-managed.ts +7 -1
- package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
- package/src/providers/types.ts +26 -6
- package/src/providers/vellum-model-routing.ts +6 -7
- package/src/runtime/AGENTS.md +2 -0
- package/src/runtime/__tests__/agent-wake.test.ts +16 -16
- package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
- package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
- package/src/runtime/agent-wake.ts +63 -24
- package/src/runtime/assistant-event-hub.ts +87 -12
- package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
- package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
- package/src/runtime/auth/same-actor.ts +56 -8
- package/src/runtime/auth/scopes.ts +12 -0
- package/src/runtime/auth/types.ts +2 -0
- package/src/runtime/background-job-runner.ts +5 -27
- package/src/runtime/channel-readiness-service.ts +59 -3
- package/src/runtime/channel-readiness-types.ts +18 -0
- package/src/runtime/desktop-presence.ts +69 -0
- package/src/runtime/http-server.ts +12 -0
- package/src/runtime/local-actor-identity.ts +11 -9
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
- package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
- package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
- package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
- package/src/runtime/routes/archive-utils.ts +16 -12
- package/src/runtime/routes/attachment-routes.ts +13 -6
- package/src/runtime/routes/canned-reply-release.ts +54 -0
- package/src/runtime/routes/channel-verification-routes.ts +2 -27
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
- package/src/runtime/routes/client-routes.ts +114 -2
- package/src/runtime/routes/conversation-cli-routes.ts +10 -12
- package/src/runtime/routes/conversation-list-routes.ts +59 -10
- package/src/runtime/routes/conversation-management-routes.ts +47 -18
- package/src/runtime/routes/conversation-query-routes.ts +32 -53
- package/src/runtime/routes/conversation-routes.ts +360 -293
- package/src/runtime/routes/credential-prompt-routes.ts +23 -2
- package/src/runtime/routes/debug-routes.ts +4 -1
- package/src/runtime/routes/default-provider-routes.ts +7 -11
- package/src/runtime/routes/documents-routes.ts +44 -1
- package/src/runtime/routes/events-routes.ts +28 -1
- package/src/runtime/routes/host-app-control-routes.ts +2 -0
- package/src/runtime/routes/host-bash-routes.ts +2 -0
- package/src/runtime/routes/host-browser-routes.ts +2 -0
- package/src/runtime/routes/host-cu-routes.ts +2 -0
- package/src/runtime/routes/host-file-routes.ts +2 -0
- package/src/runtime/routes/host-transfer-routes.ts +4 -0
- package/src/runtime/routes/identity-routes.ts +6 -6
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
- package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
- package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
- package/src/runtime/routes/inference-profiles-routes.ts +152 -73
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
- package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
- package/src/runtime/routes/log-export-routes.ts +1 -1
- package/src/runtime/routes/notification-routes.ts +0 -14
- package/src/runtime/routes/oauth-providers.ts +1 -0
- package/src/runtime/routes/plugins-routes.ts +106 -1
- package/src/runtime/routes/profiler-routes.ts +4 -2
- package/src/runtime/routes/question-routes.ts +28 -10
- package/src/runtime/routes/retrospective-routes.ts +9 -3
- package/src/runtime/routes/schedule-routes.ts +210 -12
- package/src/runtime/routes/secret-routes.ts +40 -23
- package/src/runtime/routes/settings-routes.ts +41 -13
- package/src/runtime/routes/skills-routes.ts +79 -0
- package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
- package/src/runtime/routes/wake-conversation-routes.ts +23 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +24 -9
- package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
- package/src/runtime/sync/resource-sync-events.ts +79 -1
- package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
- package/src/runtime/sync/worker-daemon-notify.ts +44 -6
- package/src/runtime/verification-outbound-actions.ts +250 -222
- package/src/runtime/verification-templates.ts +41 -30
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
- package/src/schedule/inference-profile.ts +64 -3
- package/src/schedule/plugin-schedule-declarations.ts +518 -0
- package/src/schedule/plugin-schedule-reconciler.ts +605 -0
- package/src/schedule/plugin-schedules-gate.ts +23 -0
- package/src/schedule/schedule-store.ts +520 -26
- package/src/schedule/scheduler.ts +38 -0
- package/src/security/credential-backend.ts +51 -4
- package/src/security/encrypted-store.ts +64 -42
- package/src/security/secure-keys.ts +55 -7
- package/src/skills/catalog-install.ts +20 -7
- package/src/skills/inline-command-runner.ts +2 -7
- package/src/skills/skill-history.test.ts +269 -0
- package/src/skills/skill-history.ts +250 -0
- package/src/skills/skillssh-registry.ts +1 -1
- package/src/stt/daemon-batch-transcriber.ts +8 -6
- package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
- package/src/subagent/consult-prompt.ts +3 -1
- package/src/subagent/consult-transcript.ts +1 -1
- package/src/subagent/manager.ts +304 -44
- package/src/subagent/progress-events.ts +35 -0
- package/src/subagent/role-resolution.ts +88 -0
- package/src/subagent/types.ts +302 -67
- package/src/subagent/validate-allowlists.ts +3 -4
- package/src/telegram/__tests__/webhook-health.test.ts +73 -1
- package/src/telegram/webhook-health.ts +101 -3
- package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
- package/src/telemetry/activation-funnel.ts +6 -1
- package/src/telemetry/live-voice-funnel.ts +75 -0
- package/src/telemetry/telemetry-event-sources.ts +6 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +2 -0
- package/src/telemetry/types.ts +23 -4
- package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
- package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
- package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
- package/src/tools/ask-question/ask-question-tool.ts +56 -2
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/calls/call-start.ts +8 -5
- package/src/tools/conversation-groups/move_to_group.ts +10 -12
- package/src/tools/document/document-tool.ts +11 -4
- package/src/tools/execution-timeout.ts +9 -2
- package/src/tools/executor.ts +7 -6
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +7 -13
- package/src/tools/filesystem/search.ts +23 -21
- package/src/tools/filesystem/write.ts +4 -1
- package/src/tools/host-filesystem/edit.ts +7 -2
- package/src/tools/host-filesystem/read.ts +21 -8
- package/src/tools/host-filesystem/write.ts +10 -2
- package/src/tools/host-terminal/host-shell.ts +2 -2
- package/src/tools/mcp/mcp-tool-factory.ts +25 -1
- package/src/tools/permission-checker.ts +8 -3
- package/src/tools/schedule/delete.ts +13 -1
- package/src/tools/schedule/list.ts +14 -3
- package/src/tools/schedule/update.ts +56 -33
- package/src/tools/shared/filesystem/audio-read.ts +6 -4
- package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
- package/src/tools/shared/filesystem/image-read.ts +12 -12
- package/src/tools/shared/filesystem/path-policy.ts +70 -5
- package/src/tools/shared/filesystem/size-guard.ts +6 -6
- package/src/tools/shared/input-misuse.ts +72 -0
- package/src/tools/skills/scaffold-managed.ts +82 -0
- package/src/tools/skills/skill-tool-factory.ts +14 -1
- package/src/tools/subagent/consult-deadline.ts +12 -7
- package/src/tools/subagent/message.ts +3 -1
- package/src/tools/subagent/read.ts +66 -4
- package/src/tools/subagent/spawn.ts +500 -78
- package/src/tools/tool-approval-handler.ts +45 -4
- package/src/tools/types.ts +22 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
- package/src/tools/ui-surface/visual-validation.ts +2 -0
- package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
- package/src/usage/subagent-attribution.ts +108 -0
- package/src/util/__tests__/sqlite-retry.test.ts +112 -0
- package/src/util/abort-reasons.ts +16 -0
- package/src/util/ansi.ts +44 -0
- package/src/util/disk-usage.ts +17 -13
- package/src/util/image-conversion.ts +20 -13
- package/src/util/schedule-source-key.ts +23 -0
- package/src/util/worker-process.ts +7 -1
- package/src/workspace/git-service.ts +33 -9
- package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
- package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/top-level-renderer.ts +15 -2
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
- package/src/daemon/install-symlink.ts +0 -207
- package/src/notifications/deferred-emit.ts +0 -147
|
@@ -29,7 +29,6 @@ export interface UnifiedTurnContextOptions {
|
|
|
29
29
|
name: string;
|
|
30
30
|
/** Directory stem: the handle a human would recognize the app by. */
|
|
31
31
|
slug: string;
|
|
32
|
-
sourceDir: string;
|
|
33
32
|
/** Set when the app is bundled by an installed plugin rather than built here. */
|
|
34
33
|
pluginName?: string;
|
|
35
34
|
} | null;
|
|
@@ -127,7 +126,7 @@ export function buildUnifiedTurnContextBlock(
|
|
|
127
126
|
? ` It is bundled by the "${sanitizeInlineContextValue(app.pluginName)}" plugin, not built in the sandbox.`
|
|
128
127
|
: "";
|
|
129
128
|
lines.push(
|
|
130
|
-
`visible_app: "${sanitizeInlineContextValue(app.name)}" (app_id: "${sanitizeInlineContextValue(app.appId)}", slug: "${sanitizeInlineContextValue(app.slug)}"
|
|
129
|
+
`visible_app: "${sanitizeInlineContextValue(app.name)}" (app_id: "${sanitizeInlineContextValue(app.appId)}", slug: "${sanitizeInlineContextValue(app.slug)}"). The user has this app open on screen; unqualified references to "the app" mean this one.${provenance}`,
|
|
131
130
|
);
|
|
132
131
|
}
|
|
133
132
|
|
|
@@ -461,9 +461,14 @@ export async function loadExternalPlugin(
|
|
|
461
461
|
* Exported so the mtime cache can discover plugin identity without going
|
|
462
462
|
* through the full `buildExternalPlugin` path. The identity mirrors
|
|
463
463
|
* {@link buildPluginFromDir}: the directory name, not `package.json` `name`.
|
|
464
|
+
*
|
|
465
|
+
* `quiet` suppresses the failure logs, for callers that poll on a timer and
|
|
466
|
+
* surface the failure through their own channel (e.g. the schedule
|
|
467
|
+
* reconciler's per-day deduped notification).
|
|
464
468
|
*/
|
|
465
469
|
export async function parsePluginManifest(
|
|
466
470
|
pluginDir: string,
|
|
471
|
+
opts: { quiet?: boolean } = {},
|
|
467
472
|
): Promise<
|
|
468
473
|
Pick<PluginManifest, "name" | "version" | "credentialKeyPatterns"> | undefined
|
|
469
474
|
> {
|
|
@@ -473,18 +478,22 @@ export async function parsePluginManifest(
|
|
|
473
478
|
rawPkg = JSON.parse(await readFile(pkgPath, "utf8"));
|
|
474
479
|
} catch (err) {
|
|
475
480
|
const reason = err instanceof Error ? err.message : String(err);
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
481
|
+
if (!opts.quiet) {
|
|
482
|
+
log.error(
|
|
483
|
+
{ err, pluginDir },
|
|
484
|
+
`package.json at ${pluginDir} could not be read or parsed: ${reason}`,
|
|
485
|
+
);
|
|
486
|
+
}
|
|
480
487
|
return undefined;
|
|
481
488
|
}
|
|
482
489
|
const parsed = PluginPackageJsonSchema.safeParse(rawPkg);
|
|
483
490
|
if (!parsed.success) {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
491
|
+
if (!opts.quiet) {
|
|
492
|
+
log.error(
|
|
493
|
+
{ err: parsed.error, pluginDir },
|
|
494
|
+
`package.json at ${pluginDir} failed schema validation: ${parsed.error.message}`,
|
|
495
|
+
);
|
|
496
|
+
}
|
|
488
497
|
return undefined;
|
|
489
498
|
}
|
|
490
499
|
const pkg: PluginPackageJson = parsed.data;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enumerate installed plugin directories under the workspace plugins dir.
|
|
3
|
+
*
|
|
4
|
+
* An installed plugin is a non-hidden directory carrying a `package.json`,
|
|
5
|
+
* whose realpath stays under the realpath of the plugins directory. This walk
|
|
6
|
+
* is shared by the plugin source-version collector
|
|
7
|
+
* (`./collect-source-versions.ts`) and the schedule reconciler
|
|
8
|
+
* (`../schedule/plugin-schedule-reconciler.ts`) so both agree on what counts
|
|
9
|
+
* as an installed plugin. Disabled-state and manifest validation are caller
|
|
10
|
+
* concerns: the source collector fingerprints disabled plugins too, while the
|
|
11
|
+
* reconciler skips them and additionally gates on `parsePluginManifest`.
|
|
12
|
+
*
|
|
13
|
+
* The containment half of that definition is exported on its own
|
|
14
|
+
* ({@link isInsidePluginRoot}) for the plugin loader, which applies the same
|
|
15
|
+
* boundary at boot discovery and before it imports a plugin directory, and for
|
|
16
|
+
* the schedule fire-time probe, which applies it before answering that a
|
|
17
|
+
* declaration is available to run.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { existsSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
21
|
+
import { join, sep } from "node:path";
|
|
22
|
+
|
|
23
|
+
import { getWorkspacePluginsDir } from "../util/platform.js";
|
|
24
|
+
|
|
25
|
+
export interface InstalledPluginDir {
|
|
26
|
+
/** Directory basename: the plugin's install identity. */
|
|
27
|
+
readonly name: string;
|
|
28
|
+
/** Absolute path to the plugin directory. */
|
|
29
|
+
readonly dir: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* True when `dir` resolves to a location strictly inside `root`, where `root`
|
|
34
|
+
* is the tree the caller requires `dir` to stay in: an allowed plugin root
|
|
35
|
+
* (the workspace plugins directory, or the standalone workspace hooks
|
|
36
|
+
* directory) for a plugin directory, or a plugin directory for something
|
|
37
|
+
* installed under it.
|
|
38
|
+
*
|
|
39
|
+
* Both sides are resolved before the comparison. Resolving the candidate is
|
|
40
|
+
* what judges a symlinked entry by where it points rather than by where it
|
|
41
|
+
* sits; resolving the root is what keeps a root that itself sits behind a
|
|
42
|
+
* symlinked path component (macOS `/tmp` to `/private/tmp`, a workspace under
|
|
43
|
+
* a linked home) matching its own children. A resolved candidate compared
|
|
44
|
+
* against an unresolved root reports every entry as outside.
|
|
45
|
+
*
|
|
46
|
+
* This is the one containment boundary the plugin loader applies: enumeration
|
|
47
|
+
* here, boot discovery, and the pre-import check before a plugin directory is
|
|
48
|
+
* dynamic-imported (`scanPlugins` and `isAllowedPluginDir` in
|
|
49
|
+
* `./mtime-cache.ts`) all go through it, so a root the loader refuses to
|
|
50
|
+
* activate is never reported as installed either. A link pointing at the root
|
|
51
|
+
* itself is outside the boundary: it aliases the root, not a plugin. The
|
|
52
|
+
* schedule fire-time probe
|
|
53
|
+
* (`../schedule/plugin-schedule-declarations.ts`) applies the same boundary,
|
|
54
|
+
* so such a root cannot be run by a declared schedule either.
|
|
55
|
+
*/
|
|
56
|
+
export function isInsidePluginRoot(dir: string, root: string): boolean {
|
|
57
|
+
try {
|
|
58
|
+
return realpathSync(dir).startsWith(realpathSync(root) + sep);
|
|
59
|
+
} catch {
|
|
60
|
+
// Unresolvable (dangling link, races with an uninstall, unreadable): not
|
|
61
|
+
// provably contained, so it is not inside.
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* List every installed plugin directory, sorted by the underlying readdir
|
|
68
|
+
* order. A missing plugins directory yields `[]`.
|
|
69
|
+
*/
|
|
70
|
+
export function listInstalledPluginDirs(): InstalledPluginDir[] {
|
|
71
|
+
const pluginsDir = getWorkspacePluginsDir();
|
|
72
|
+
let entries: string[] = [];
|
|
73
|
+
try {
|
|
74
|
+
entries = readdirSync(pluginsDir);
|
|
75
|
+
} catch {
|
|
76
|
+
// No plugins directory yet, so nothing installed.
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
const out: InstalledPluginDir[] = [];
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
if (entry.startsWith(".")) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const dir = join(pluginsDir, entry);
|
|
85
|
+
try {
|
|
86
|
+
if (!statSync(dir).isDirectory()) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
} catch {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (!isInsidePluginRoot(dir, pluginsDir)) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (!existsSync(join(dir, "package.json"))) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
out.push({ name: entry, dir });
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
@@ -31,13 +31,7 @@
|
|
|
31
31
|
* each hook through the hook loader on demand.
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
|
-
import {
|
|
35
|
-
existsSync,
|
|
36
|
-
readdirSync,
|
|
37
|
-
readFileSync,
|
|
38
|
-
realpathSync,
|
|
39
|
-
statSync,
|
|
40
|
-
} from "node:fs";
|
|
34
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
41
35
|
import { join } from "node:path";
|
|
42
36
|
|
|
43
37
|
import type { Logger } from "pino";
|
|
@@ -72,6 +66,7 @@ import {
|
|
|
72
66
|
listSurfaceDir,
|
|
73
67
|
parsePluginManifest,
|
|
74
68
|
} from "./external-plugin-loader.js";
|
|
69
|
+
import { isInsidePluginRoot } from "./installed-plugin-dirs.js";
|
|
75
70
|
import { snapshotPluginSource } from "./source-fingerprint.js";
|
|
76
71
|
import type { PluginSourceVersion } from "./source-versions.js";
|
|
77
72
|
import {
|
|
@@ -293,6 +288,18 @@ export async function reconcilePluginSourcesNow(): Promise<void> {
|
|
|
293
288
|
} catch (err) {
|
|
294
289
|
log.error({ err }, "imperative plugin reconcile failed");
|
|
295
290
|
}
|
|
291
|
+
// Converge plugin-declared schedules against the plugin set this apply
|
|
292
|
+
// just settled, so install/uninstall/upgrade arm and disarm rows in the
|
|
293
|
+
// same poke. Imported lazily to keep the notification pipeline out of
|
|
294
|
+
// this module's static graph (sidecar workers import it for hook reads).
|
|
295
|
+
// Self-contained: never throws and checks DB readiness itself.
|
|
296
|
+
try {
|
|
297
|
+
const { reconcilePluginSchedules } =
|
|
298
|
+
await import("../schedule/plugin-schedule-reconciler.js");
|
|
299
|
+
await reconcilePluginSchedules();
|
|
300
|
+
} catch (err) {
|
|
301
|
+
log.error({ err }, "plugin schedule reconcile failed");
|
|
302
|
+
}
|
|
296
303
|
})().finally(() => {
|
|
297
304
|
reconcileInFlight = null;
|
|
298
305
|
});
|
|
@@ -306,32 +313,26 @@ export async function reconcilePluginSourcesNow(): Promise<void> {
|
|
|
306
313
|
* never dynamically imports code from outside the designated plugin roots,
|
|
307
314
|
* regardless of what the collector walked.
|
|
308
315
|
*
|
|
309
|
-
*
|
|
310
|
-
* symlinked path that looks like it's under the
|
|
311
|
-
* elsewhere is rejected
|
|
316
|
+
* Containment runs through {@link isInsidePluginRoot}, which resolves the
|
|
317
|
+
* candidate and the root, so a symlinked path that looks like it's under the
|
|
318
|
+
* plugins dir but points elsewhere is rejected while a plugins dir reached
|
|
319
|
+
* through a symlinked path component still accepts its own children.
|
|
312
320
|
*/
|
|
313
321
|
function isAllowedPluginDir(
|
|
314
322
|
dir: string,
|
|
315
323
|
pluginsDir: string,
|
|
316
324
|
hooksDir: string,
|
|
317
325
|
): boolean {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
// Directory doesn't exist or is inaccessible — allow it through so
|
|
323
|
-
// bringUpPlugin/parsePluginManifest can log the normal failure. The
|
|
326
|
+
if (!existsSync(dir)) {
|
|
327
|
+
// Directory doesn't exist or is inaccessible: allow it through so
|
|
328
|
+
// bringUpPlugin/parsePluginManifest can log the normal failure, and so a
|
|
329
|
+
// directory that just went away still reaches its teardown branch. The
|
|
324
330
|
// danger is importing code from an unexpected location, not a missing
|
|
325
331
|
// directory.
|
|
326
332
|
return true;
|
|
327
333
|
}
|
|
328
|
-
const normalizedPluginsDir = pluginsDir + "/";
|
|
329
|
-
const normalizedHooksDir = hooksDir + "/";
|
|
330
334
|
return (
|
|
331
|
-
|
|
332
|
-
resolved.startsWith(normalizedPluginsDir) ||
|
|
333
|
-
resolved === hooksDir ||
|
|
334
|
-
resolved.startsWith(normalizedHooksDir)
|
|
335
|
+
isInsidePluginRoot(dir, pluginsDir) || isInsidePluginRoot(dir, hooksDir)
|
|
335
336
|
);
|
|
336
337
|
}
|
|
337
338
|
|
|
@@ -754,6 +755,17 @@ async function scanPlugins(): Promise<void> {
|
|
|
754
755
|
} catch {
|
|
755
756
|
continue;
|
|
756
757
|
}
|
|
758
|
+
// Judge the entry by where it resolves. A symlinked plugin root pointing
|
|
759
|
+
// out of the plugins directory is not an install, and activating one at
|
|
760
|
+
// boot would run code that enumeration and the reload path both refuse to
|
|
761
|
+
// touch.
|
|
762
|
+
if (!isInsidePluginRoot(pluginDir, pluginsDir)) {
|
|
763
|
+
log.warn(
|
|
764
|
+
{ pluginDir },
|
|
765
|
+
"plugin root resolves outside the plugins directory, skipping",
|
|
766
|
+
);
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
757
769
|
if (!existsSync(join(pluginDir, "package.json"))) {
|
|
758
770
|
continue;
|
|
759
771
|
}
|
package/src/plugins/types.ts
CHANGED
|
@@ -147,9 +147,9 @@ export interface TurnContext {
|
|
|
147
147
|
/** Human-readable interface label (e.g. "vellum", "telegram"). */
|
|
148
148
|
readonly interfaceName?: string;
|
|
149
149
|
/**
|
|
150
|
-
* Client OS surface
|
|
151
|
-
*
|
|
152
|
-
*
|
|
150
|
+
* Client OS surface, reported independently of the transport interface.
|
|
151
|
+
* Rendered as the `client_os:` line so the model knows the platform even
|
|
152
|
+
* though browser, mobile, and desktop apps share one `"web"`
|
|
153
153
|
* transport interface.
|
|
154
154
|
*/
|
|
155
155
|
readonly clientOs?: string;
|
|
@@ -163,10 +163,13 @@ export interface TurnContext {
|
|
|
163
163
|
/** Canonical id the app tools key off (an opaque UUID for workspace apps). */
|
|
164
164
|
appId: string;
|
|
165
165
|
name: string;
|
|
166
|
-
/**
|
|
166
|
+
/**
|
|
167
|
+
* Directory stem: the readable handle for an otherwise opaque id, and the
|
|
168
|
+
* piece the model joins with the workspace root and the app-builder skill's
|
|
169
|
+
* `data/apps/<slug>/` layout to reach the app's files. The resolved
|
|
170
|
+
* directory itself is not sent, being derivable from those three.
|
|
171
|
+
*/
|
|
167
172
|
slug: string;
|
|
168
|
-
/** Absolute path of the app's source directory. */
|
|
169
|
-
sourceDir: string;
|
|
170
173
|
/** Owning plugin, when the app is plugin-bundled rather than sandbox-built. */
|
|
171
174
|
pluginName?: string;
|
|
172
175
|
} | null;
|
|
@@ -350,10 +353,68 @@ export interface Injector {
|
|
|
350
353
|
// surfaces (e.g. `routes/`) are served from their workspace location rather
|
|
351
354
|
// than wired through the loader, so they carry no `Plugin` contribution slot.
|
|
352
355
|
|
|
356
|
+
/**
|
|
357
|
+
* How the worker resolves a claimed job row after its handler returns
|
|
358
|
+
* without throwing. Handlers that report failure through a returned domain
|
|
359
|
+
* outcome (rather than a throw) translate that outcome into one of these at
|
|
360
|
+
* the registration site, so the persisted `memory_jobs.status` reflects what
|
|
361
|
+
* actually happened instead of unconditionally reading `completed`.
|
|
362
|
+
*
|
|
363
|
+
* - `completed`: the work succeeded (or was a legitimate no-op).
|
|
364
|
+
* - `failed`: terminal failure; the row is dead-lettered with
|
|
365
|
+
* `errorMessage` as `last_error`. Use when retry is owned elsewhere
|
|
366
|
+
* (event-driven re-enqueue, durable backoff checkpoints).
|
|
367
|
+
* - `retryable`: transient failure; the row re-enters the queue's
|
|
368
|
+
* attempt-budgeted retry machinery with `errorMessage` recorded.
|
|
369
|
+
* - `deferred`: the work could not run yet for a reason external to the
|
|
370
|
+
* job (e.g. its source conversation is mid-turn); the row goes back to
|
|
371
|
+
* pending on the deferral counter's backoff curve and dead-letters with
|
|
372
|
+
* `deferralExhaustedMessage` once the deferral budget is spent.
|
|
373
|
+
*/
|
|
374
|
+
export interface JobQueueResolution {
|
|
375
|
+
queueResolution: "completed" | "failed" | "retryable" | "deferred";
|
|
376
|
+
/** Persisted to `memory_jobs.last_error` for `failed` / `retryable`. */
|
|
377
|
+
errorMessage?: string;
|
|
378
|
+
/** Retry delay override for `retryable`. */
|
|
379
|
+
retryDelayMs?: number;
|
|
380
|
+
/** Terminal `last_error` when a `deferred` job exhausts its budget. */
|
|
381
|
+
deferralExhaustedMessage?: string;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const QUEUE_RESOLUTIONS = new Set([
|
|
385
|
+
"completed",
|
|
386
|
+
"failed",
|
|
387
|
+
"retryable",
|
|
388
|
+
"deferred",
|
|
389
|
+
]);
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Narrow an arbitrary handler return value to a {@link JobQueueResolution}.
|
|
393
|
+
* Anything else (including `undefined` and legacy domain outcomes) resolves
|
|
394
|
+
* as `completed`, preserving the historical contract for handlers that only
|
|
395
|
+
* signal failure by throwing.
|
|
396
|
+
*/
|
|
397
|
+
export function isJobQueueResolution(
|
|
398
|
+
value: unknown,
|
|
399
|
+
): value is JobQueueResolution {
|
|
400
|
+
return (
|
|
401
|
+
typeof value === "object" &&
|
|
402
|
+
value !== null &&
|
|
403
|
+
"queueResolution" in value &&
|
|
404
|
+
typeof (value as { queueResolution: unknown }).queueResolution ===
|
|
405
|
+
"string" &&
|
|
406
|
+
QUEUE_RESOLUTIONS.has(
|
|
407
|
+
(value as { queueResolution: string }).queueResolution,
|
|
408
|
+
)
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
353
412
|
/**
|
|
354
413
|
* Processes one claimed background job: receives the claimed job row and the
|
|
355
|
-
* live assistant config.
|
|
356
|
-
*
|
|
414
|
+
* live assistant config. A handler signals failure by throwing (recorded
|
|
415
|
+
* against the job's attempt budget) or by returning a
|
|
416
|
+
* {@link JobQueueResolution}; any other return value resolves the row as
|
|
417
|
+
* `completed`.
|
|
357
418
|
*/
|
|
358
419
|
export type JobHandler = (job: MemoryJob, config: AssistantConfig) => unknown;
|
|
359
420
|
|
|
@@ -82,7 +82,10 @@ function readPersonaFile(filePath: string): string | null {
|
|
|
82
82
|
* (`guardianExternalUserId`): the info read is keyed on that address so it
|
|
83
83
|
* matches the gateway's guardian binding. When the context carries no guardian
|
|
84
84
|
* address (desktop / native turns), fall back to the channel's guardian
|
|
85
|
-
* contact
|
|
85
|
+
* contact, then any guardian: the guardian may be bound on a channel other
|
|
86
|
+
* than the turn's source, and the construction-time warm populates both the
|
|
87
|
+
* channel-filtered and unfiltered cache keys. Returns `"guardian.md"` when the
|
|
88
|
+
* resolved guardian has no userFile.
|
|
86
89
|
*/
|
|
87
90
|
function resolveGuardianUserFile(trustContext: TrustContext): string | null {
|
|
88
91
|
if (trustContext.guardianExternalUserId) {
|
|
@@ -94,7 +97,8 @@ function resolveGuardianUserFile(trustContext: TrustContext): string | null {
|
|
|
94
97
|
return guardianContact.userFile ?? "guardian.md";
|
|
95
98
|
}
|
|
96
99
|
}
|
|
97
|
-
const guardian =
|
|
100
|
+
const guardian =
|
|
101
|
+
peekGuardianForChannel(trustContext.sourceChannel) ?? peekAnyGuardian();
|
|
98
102
|
return guardian
|
|
99
103
|
? (guardianDeliveryUserFile(guardian) ?? "guardian.md")
|
|
100
104
|
: null;
|
|
@@ -48,13 +48,61 @@ function hasPopulatedUsersDir(): boolean {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Conversation types that are internal side channels rather than threads the
|
|
53
|
+
* user opens: onboarding research, personality/identity rewrites, heartbeat
|
|
54
|
+
* and scheduled runs, plus legacy private rows.
|
|
55
|
+
*/
|
|
56
|
+
const HIDDEN_CONVERSATION_TYPES = new Set([
|
|
57
|
+
"background",
|
|
58
|
+
"scheduled",
|
|
59
|
+
"private",
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Whether the workspace carries at least one standard (user-visible)
|
|
64
|
+
* conversation.
|
|
65
|
+
*
|
|
66
|
+
* Classifies each disk-view directory by the `type` in its `meta.json` rather
|
|
67
|
+
* than counting raw directory entries, because hidden side threads get a
|
|
68
|
+
* disk-view directory too and must not be mistaken for the user's own history.
|
|
69
|
+
* The type read here is the same value `countConversations("standard")` filters
|
|
70
|
+
* on, but the answer comes off disk rather than out of the database:
|
|
71
|
+
* `ensurePromptFiles()` runs before `initializeDb()` settles in
|
|
72
|
+
* `daemon/lifecycle.ts`, so a query here would hit a partially-migrated schema.
|
|
73
|
+
*
|
|
74
|
+
* Only directories are classified. The layout is browsable, so stray files such
|
|
75
|
+
* as `.DS_Store` sit beside the conversations, and a `meta.json` read under one
|
|
76
|
+
* fails indistinguishably from an unreadable conversation. Directory names are
|
|
77
|
+
* not filtered further, because both the canonical `<ISO>_<id>` layout and the
|
|
78
|
+
* legacy `<id>_<ISO>` one name real conversations.
|
|
79
|
+
*
|
|
80
|
+
* A directory whose `meta.json` is missing or unreadable counts as standard: a
|
|
81
|
+
* directory the daemon cannot classify is far more likely to be legacy history
|
|
82
|
+
* than a side thread, and over-counting only keeps onboarding from re-running.
|
|
83
|
+
*/
|
|
84
|
+
function hasStandardConversations(): boolean {
|
|
52
85
|
try {
|
|
53
86
|
const convDir = getConversationsDir();
|
|
54
87
|
if (!existsSync(convDir)) {
|
|
55
88
|
return false;
|
|
56
89
|
}
|
|
57
|
-
return readdirSync(convDir).
|
|
90
|
+
return readdirSync(convDir, { withFileTypes: true }).some((entry) => {
|
|
91
|
+
if (!entry.isDirectory()) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const meta = JSON.parse(
|
|
96
|
+
readFileSync(join(convDir, entry.name, "meta.json"), "utf-8"),
|
|
97
|
+
) as { type?: unknown };
|
|
98
|
+
return (
|
|
99
|
+
typeof meta.type !== "string" ||
|
|
100
|
+
!HIDDEN_CONVERSATION_TYPES.has(meta.type)
|
|
101
|
+
);
|
|
102
|
+
} catch {
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
58
106
|
} catch {
|
|
59
107
|
return false;
|
|
60
108
|
}
|
|
@@ -78,14 +126,14 @@ export function ensurePromptFiles(): void {
|
|
|
78
126
|
|
|
79
127
|
// Track whether this is a fresh workspace. A workspace counts as fresh
|
|
80
128
|
// only when none of these signals are present: core prompt files, a
|
|
81
|
-
// populated `users/` directory, or existing conversations.
|
|
82
|
-
// workspaces that dropped USER.md but still carry personas or
|
|
83
|
-
// would otherwise be mistaken for fresh installs and re-trigger
|
|
129
|
+
// populated `users/` directory, or existing standard conversations.
|
|
130
|
+
// Upgraded workspaces that dropped USER.md but still carry personas or
|
|
131
|
+
// history would otherwise be mistaken for fresh installs and re-trigger
|
|
84
132
|
// onboarding.
|
|
85
133
|
const isFirstRun =
|
|
86
134
|
PROMPT_FILES.every((file) => !existsSync(getWorkspacePromptPath(file))) &&
|
|
87
135
|
!hasPopulatedUsersDir() &&
|
|
88
|
-
!
|
|
136
|
+
!hasStandardConversations();
|
|
89
137
|
|
|
90
138
|
for (const file of PROMPT_FILES) {
|
|
91
139
|
const dest = getWorkspacePromptPath(file);
|
|
@@ -152,18 +200,25 @@ export function ensurePromptFiles(): void {
|
|
|
152
200
|
// Auto-delete stale BOOTSTRAP.md at startup. The model is instructed to
|
|
153
201
|
// delete it at the end of the first conversation, but if the user closes
|
|
154
202
|
// the app or starts a new thread before the model gets another turn, it
|
|
155
|
-
// never gets the chance. If BOOTSTRAP.md still exists but prior
|
|
156
|
-
// conversations are present, the onboarding window has passed
|
|
203
|
+
// never gets the chance. If BOOTSTRAP.md still exists but prior standard
|
|
204
|
+
// conversations are present, the onboarding window has passed, so clean up.
|
|
205
|
+
//
|
|
206
|
+
// Only standard conversations count. Onboarding mints hidden side threads
|
|
207
|
+
// before the user's first visible chat, so counting those would delete
|
|
208
|
+
// BOOTSTRAP.md out from under that chat on the next daemon restart.
|
|
209
|
+
//
|
|
210
|
+
// This gate is one conversation stricter than the in-process gate in
|
|
211
|
+
// `persistence/conversation-key-store.ts`, which keeps BOOTSTRAP.md through
|
|
212
|
+
// the whole first standard conversation and deletes it when a second one is
|
|
213
|
+
// created. A restart while the user is still inside that first conversation
|
|
214
|
+
// deletes BOOTSTRAP.md from under it.
|
|
157
215
|
const bootstrapCleanup = getWorkspacePromptPath("BOOTSTRAP.md");
|
|
158
|
-
if (
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
} catch (err) {
|
|
165
|
-
log.warn({ err }, "Failed to auto-delete stale BOOTSTRAP.md");
|
|
166
|
-
}
|
|
216
|
+
if (
|
|
217
|
+
!isFirstRun &&
|
|
218
|
+
existsSync(bootstrapCleanup) &&
|
|
219
|
+
hasStandardConversations()
|
|
220
|
+
) {
|
|
221
|
+
cleanupBootstrapFiles("prior conversations exist");
|
|
167
222
|
}
|
|
168
223
|
|
|
169
224
|
// Seed HEARTBEAT.md — always created if missing so the heartbeat service
|
|
@@ -181,6 +181,46 @@ describe("Connection CRUD", () => {
|
|
|
181
181
|
expect(fetched?.auth.type).toBe("api_key");
|
|
182
182
|
});
|
|
183
183
|
|
|
184
|
+
test("updateConnection: provider correction rides an auth rewrite", () => {
|
|
185
|
+
// The ChatGPT sign-in flow stamps provider "openai" when it writes
|
|
186
|
+
// subscription auth, so a claiming row with another provider cannot
|
|
187
|
+
// strand the fresh token behind derived platform auth.
|
|
188
|
+
const { db } = setupDb();
|
|
189
|
+
createConnection(db, {
|
|
190
|
+
name: "chatgpt-subscription",
|
|
191
|
+
provider: "vellum",
|
|
192
|
+
auth: { type: "platform" },
|
|
193
|
+
});
|
|
194
|
+
const result = updateConnection(db, "chatgpt-subscription", {
|
|
195
|
+
auth: {
|
|
196
|
+
type: "oauth_subscription",
|
|
197
|
+
credential: "credential/chatgpt/access_token",
|
|
198
|
+
},
|
|
199
|
+
provider: "openai",
|
|
200
|
+
});
|
|
201
|
+
expect(result.ok).toBe(true);
|
|
202
|
+
const fetched = getConnection(db, "chatgpt-subscription");
|
|
203
|
+
expect(fetched?.provider).toBe("openai");
|
|
204
|
+
expect(fetched?.auth.type).toBe("oauth_subscription");
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("updateConnection: rejects an unknown provider", () => {
|
|
208
|
+
const { db } = setupDb();
|
|
209
|
+
createConnection(db, {
|
|
210
|
+
name: "updatable-provider",
|
|
211
|
+
provider: "anthropic",
|
|
212
|
+
auth: { type: "api_key", credential: "credential/anthropic/api_key" },
|
|
213
|
+
});
|
|
214
|
+
const result = updateConnection(db, "updatable-provider", {
|
|
215
|
+
auth: { type: "api_key", credential: "credential/anthropic/api_key" },
|
|
216
|
+
provider: "not-a-provider",
|
|
217
|
+
});
|
|
218
|
+
expect(result.ok).toBe(false);
|
|
219
|
+
if (!result.ok) {
|
|
220
|
+
expect(result.error.code).toBe("invalid_provider");
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
184
224
|
test("updateConnection — rejects unknown name", () => {
|
|
185
225
|
const { db } = setupDb();
|
|
186
226
|
const result = updateConnection(db, "ghost", { auth: { type: "none" } });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
3
|
let connectionsByName: Record<string, unknown> = {};
|
|
4
4
|
let secureKeys: Record<string, string | undefined> = {};
|
|
@@ -74,6 +74,11 @@ beforeEach(() => {
|
|
|
74
74
|
secureKeys = { "credential/anthropic/api_key": "sk-ant" };
|
|
75
75
|
cesUnreachable = false;
|
|
76
76
|
platformLoggedIn = false;
|
|
77
|
+
delete process.env.IS_PLATFORM;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
afterEach(() => {
|
|
81
|
+
delete process.env.IS_PLATFORM;
|
|
77
82
|
});
|
|
78
83
|
|
|
79
84
|
describe("preflightResolvedConfig", () => {
|
|
@@ -300,3 +305,55 @@ describe("preflightResolvedConfig", () => {
|
|
|
300
305
|
expect(await preflightError(resolved())).toBeUndefined();
|
|
301
306
|
});
|
|
302
307
|
});
|
|
308
|
+
|
|
309
|
+
describe("preflightResolvedConfig platform-managed credential messaging", () => {
|
|
310
|
+
const platformManaged = () => {
|
|
311
|
+
connectionsByName = {
|
|
312
|
+
vellum: {
|
|
313
|
+
name: "vellum",
|
|
314
|
+
provider: "vellum",
|
|
315
|
+
auth: { type: "platform" },
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
process.env.IS_PLATFORM = "true";
|
|
319
|
+
};
|
|
320
|
+
const managedRoute = () => resolved({ provider_connection: "vellum" });
|
|
321
|
+
|
|
322
|
+
test("store unreachable surfaces a retriable message with no login text", async () => {
|
|
323
|
+
platformManaged();
|
|
324
|
+
cesUnreachable = true;
|
|
325
|
+
const err = await preflightError(managedRoute());
|
|
326
|
+
expect(err?.reason).toBe("platform_unauthenticated");
|
|
327
|
+
expect(err?.message).toContain("temporarily unavailable");
|
|
328
|
+
expect(err?.message).not.toContain("log in");
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("credential absent asks for platform re-provisioning, not a login", async () => {
|
|
332
|
+
platformManaged();
|
|
333
|
+
// Logged out with a reachable store means the credential is genuinely absent.
|
|
334
|
+
const err = await preflightError(managedRoute());
|
|
335
|
+
expect(err?.reason).toBe("platform_unauthenticated");
|
|
336
|
+
expect(err?.message).toContain("must be re-provisioned");
|
|
337
|
+
expect(err?.message).not.toContain("log in");
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test("a healthy platform login passes silently", async () => {
|
|
341
|
+
platformManaged();
|
|
342
|
+
platformLoggedIn = true;
|
|
343
|
+
expect(await preflightError(managedRoute())).toBeUndefined();
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test("non-platform install keeps the log-in-or-switch wording", async () => {
|
|
347
|
+
connectionsByName = {
|
|
348
|
+
vellum: {
|
|
349
|
+
name: "vellum",
|
|
350
|
+
provider: "vellum",
|
|
351
|
+
auth: { type: "platform" },
|
|
352
|
+
},
|
|
353
|
+
};
|
|
354
|
+
// IS_PLATFORM left unset covers both local and self-hosted Docker installs.
|
|
355
|
+
const err = await preflightError(managedRoute());
|
|
356
|
+
expect(err?.reason).toBe("platform_unauthenticated");
|
|
357
|
+
expect(err?.message).toContain("log in or pick a different provider");
|
|
358
|
+
});
|
|
359
|
+
});
|