@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
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `skill-history.ts`, run against a REAL git repository rather than
|
|
3
|
+
* a mocked git.
|
|
4
|
+
*
|
|
5
|
+
* The behavior worth protecting is entirely about how git actually responds to
|
|
6
|
+
* a pathspec: that a commit touching 100 unrelated files still yields a diff
|
|
7
|
+
* scoped to one skill, that `:(exclude)` really drops the usage stamp, and
|
|
8
|
+
* that a commit whose only in-skill change was excluded disappears from the
|
|
9
|
+
* list. A stubbed git would let all three regress while the tests stayed
|
|
10
|
+
* green, so each test builds commits in a temp repo and reads them back.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { execFileSync } from "node:child_process";
|
|
14
|
+
import {
|
|
15
|
+
existsSync,
|
|
16
|
+
mkdirSync,
|
|
17
|
+
mkdtempSync,
|
|
18
|
+
readdirSync,
|
|
19
|
+
rmSync,
|
|
20
|
+
writeFileSync,
|
|
21
|
+
} from "node:fs";
|
|
22
|
+
import { tmpdir } from "node:os";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
25
|
+
|
|
26
|
+
let repoDir = "";
|
|
27
|
+
|
|
28
|
+
// The module resolves the workspace through `getWorkspaceDir()`; point it at
|
|
29
|
+
// the temp repository. `getWorkspaceGitService` is left real so the git
|
|
30
|
+
// invocations under test are the ones that ship.
|
|
31
|
+
mock.module("../util/platform.js", () => ({
|
|
32
|
+
getWorkspaceDir: () => repoDir,
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
import { getSkillHistory } from "./skill-history.js";
|
|
36
|
+
|
|
37
|
+
function git(...args: string[]): string {
|
|
38
|
+
return execFileSync("git", args, {
|
|
39
|
+
cwd: repoDir,
|
|
40
|
+
encoding: "utf-8",
|
|
41
|
+
env: {
|
|
42
|
+
...process.env,
|
|
43
|
+
GIT_AUTHOR_NAME: "Test",
|
|
44
|
+
GIT_AUTHOR_EMAIL: "test@example.com",
|
|
45
|
+
GIT_COMMITTER_NAME: "Test",
|
|
46
|
+
GIT_COMMITTER_EMAIL: "test@example.com",
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Write a file under the repo, creating parents. */
|
|
52
|
+
function write(relPath: string, content: string): void {
|
|
53
|
+
const full = join(repoDir, relPath);
|
|
54
|
+
mkdirSync(join(full, ".."), { recursive: true });
|
|
55
|
+
writeFileSync(full, content, "utf-8");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function commit(message: string): void {
|
|
59
|
+
git("add", "-A");
|
|
60
|
+
git("commit", "--no-verify", "-m", message);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
repoDir = mkdtempSync(join(tmpdir(), "skill-history-"));
|
|
65
|
+
git("init", "-b", "main");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
afterEach(() => {
|
|
69
|
+
rmSync(repoDir, { recursive: true, force: true });
|
|
70
|
+
repoDir = "";
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("getSkillHistory", () => {
|
|
74
|
+
test("returns one entry per update, with the diff scoped to that skill", async () => {
|
|
75
|
+
write("skills/alpha/SKILL.md", "# Alpha\n\nStep one.\n");
|
|
76
|
+
write("skills/beta/SKILL.md", "# Beta\n\nUnrelated.\n");
|
|
77
|
+
commit("initial");
|
|
78
|
+
|
|
79
|
+
// A batched commit, the way the workspace heartbeat writes them: this
|
|
80
|
+
// skill, an unrelated skill, and a conversation file all at once.
|
|
81
|
+
write("skills/alpha/SKILL.md", "# Alpha\n\nStep one, corrected.\n");
|
|
82
|
+
write("skills/beta/SKILL.md", "# Beta\n\nAlso changed.\n");
|
|
83
|
+
write("conversations/whatever.jsonl", "{}\n");
|
|
84
|
+
commit("auto-commit: heartbeat safety net (3 files)");
|
|
85
|
+
|
|
86
|
+
const history = await getSkillHistory("alpha");
|
|
87
|
+
|
|
88
|
+
expect(history.skillId).toBe("alpha");
|
|
89
|
+
expect(history.revisions).toHaveLength(2);
|
|
90
|
+
// Newest first.
|
|
91
|
+
const [latest] = history.revisions;
|
|
92
|
+
expect(latest!.files).toEqual(["SKILL.md"]);
|
|
93
|
+
expect(latest!.diff).toContain("Step one, corrected.");
|
|
94
|
+
// The other skill and the conversation file are absent even though the
|
|
95
|
+
// same commit changed them.
|
|
96
|
+
expect(latest!.diff).not.toContain("Also changed.");
|
|
97
|
+
expect(latest!.diff).not.toContain("conversations/");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("combines SKILL.md and companion changes from one update into a single entry", async () => {
|
|
101
|
+
write("skills/alpha/SKILL.md", "# Alpha\n");
|
|
102
|
+
commit("initial");
|
|
103
|
+
|
|
104
|
+
write("skills/alpha/SKILL.md", "# Alpha\n\nRun the script.\n");
|
|
105
|
+
write("skills/alpha/scripts/export.py", "print('v1')\n");
|
|
106
|
+
write("skills/alpha/references/gotchas.md", "Watch the rate limit.\n");
|
|
107
|
+
commit("auto-commit: heartbeat safety net (3 files)");
|
|
108
|
+
|
|
109
|
+
const history = await getSkillHistory("alpha");
|
|
110
|
+
|
|
111
|
+
// One update, not three per-file entries.
|
|
112
|
+
const [latest] = history.revisions;
|
|
113
|
+
expect(latest!.files.sort()).toEqual([
|
|
114
|
+
"SKILL.md",
|
|
115
|
+
"references/gotchas.md",
|
|
116
|
+
"scripts/export.py",
|
|
117
|
+
]);
|
|
118
|
+
expect(latest!.diff).toContain("Run the script.");
|
|
119
|
+
expect(latest!.diff).toContain("print('v1')");
|
|
120
|
+
expect(latest!.diff).toContain("Watch the rate limit.");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("a load-only commit does not appear as an update", async () => {
|
|
124
|
+
write("skills/alpha/SKILL.md", "# Alpha\n");
|
|
125
|
+
write(
|
|
126
|
+
"skills/alpha/install-meta.json",
|
|
127
|
+
JSON.stringify({ origin: "custom", lastUsedAt: "2026-08-04" }),
|
|
128
|
+
);
|
|
129
|
+
commit("initial");
|
|
130
|
+
|
|
131
|
+
// The skill was loaded, so only the usage stamp moved. Roughly half of a
|
|
132
|
+
// real skill's commits look like this.
|
|
133
|
+
write(
|
|
134
|
+
"skills/alpha/install-meta.json",
|
|
135
|
+
JSON.stringify({ origin: "custom", lastUsedAt: "2026-08-05" }),
|
|
136
|
+
);
|
|
137
|
+
commit("auto-commit: heartbeat safety net (1 file)");
|
|
138
|
+
|
|
139
|
+
const history = await getSkillHistory("alpha");
|
|
140
|
+
|
|
141
|
+
// Only the creating commit counts; the stamp bump is not an update.
|
|
142
|
+
expect(history.revisions).toHaveLength(1);
|
|
143
|
+
expect(history.revisions[0]!.files).toEqual(["SKILL.md"]);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("a real edit alongside a stamp bump keeps the edit and drops the stamp", async () => {
|
|
147
|
+
write("skills/alpha/SKILL.md", "# Alpha\n");
|
|
148
|
+
write(
|
|
149
|
+
"skills/alpha/install-meta.json",
|
|
150
|
+
JSON.stringify({ lastUsedAt: "1" }),
|
|
151
|
+
);
|
|
152
|
+
commit("initial");
|
|
153
|
+
|
|
154
|
+
write("skills/alpha/SKILL.md", "# Alpha\n\nRefined.\n");
|
|
155
|
+
write(
|
|
156
|
+
"skills/alpha/install-meta.json",
|
|
157
|
+
JSON.stringify({ lastUsedAt: "2" }),
|
|
158
|
+
);
|
|
159
|
+
commit("auto-commit: heartbeat safety net (2 files)");
|
|
160
|
+
|
|
161
|
+
const history = await getSkillHistory("alpha");
|
|
162
|
+
|
|
163
|
+
const [latest] = history.revisions;
|
|
164
|
+
expect(latest!.files).toEqual(["SKILL.md"]);
|
|
165
|
+
expect(latest!.diff).toContain("Refined.");
|
|
166
|
+
expect(latest!.diff).not.toContain("lastUsedAt");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("respects the limit after filtering, not before", async () => {
|
|
170
|
+
write("skills/alpha/SKILL.md", "v0\n");
|
|
171
|
+
commit("initial");
|
|
172
|
+
// Interleave real edits with load-only commits, so a naive
|
|
173
|
+
// `--max-count=limit` would return mostly stamps.
|
|
174
|
+
for (let i = 1; i <= 4; i++) {
|
|
175
|
+
write(
|
|
176
|
+
"skills/alpha/install-meta.json",
|
|
177
|
+
JSON.stringify({ lastUsedAt: `stamp-${i}` }),
|
|
178
|
+
);
|
|
179
|
+
commit(`stamp ${i}`);
|
|
180
|
+
write("skills/alpha/SKILL.md", `v${i}\n`);
|
|
181
|
+
commit(`edit ${i}`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const history = await getSkillHistory("alpha", { limit: 3 });
|
|
185
|
+
|
|
186
|
+
expect(history.revisions).toHaveLength(3);
|
|
187
|
+
// Every returned entry is a real edit.
|
|
188
|
+
for (const revision of history.revisions) {
|
|
189
|
+
expect(revision.files).toEqual(["SKILL.md"]);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("reports when older history was squashed away", async () => {
|
|
194
|
+
write("skills/alpha/SKILL.md", "# Alpha\n");
|
|
195
|
+
commit("Compacted workspace history (14667 commits squashed)");
|
|
196
|
+
write("skills/alpha/SKILL.md", "# Alpha\n\nMore.\n");
|
|
197
|
+
commit("auto-commit: heartbeat safety net (1 file)");
|
|
198
|
+
|
|
199
|
+
const history = await getSkillHistory("alpha");
|
|
200
|
+
|
|
201
|
+
// The caller needs this to avoid presenting the oldest entry as creation.
|
|
202
|
+
expect(history.truncatedByCompaction).toBe(true);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("an untracked skill has empty history rather than an error", async () => {
|
|
206
|
+
write("skills/alpha/SKILL.md", "# Alpha\n");
|
|
207
|
+
commit("initial");
|
|
208
|
+
|
|
209
|
+
const history = await getSkillHistory("never-committed");
|
|
210
|
+
|
|
211
|
+
expect(history.revisions).toEqual([]);
|
|
212
|
+
expect(history.skillId).toBe("never-committed");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("a repository-controlled textconv driver is never executed", async () => {
|
|
216
|
+
// `.gitattributes` selects a diff driver and git config names the program
|
|
217
|
+
// for it. Both are writable through ordinary workspace paths, so rendering
|
|
218
|
+
// a patch must not hand control to them (ATL-1238).
|
|
219
|
+
write("skills/alpha/SKILL.md", "# Alpha\n");
|
|
220
|
+
write(".gitattributes", "*.md diff=evil\n");
|
|
221
|
+
commit("initial");
|
|
222
|
+
write("skills/alpha/SKILL.md", "# Alpha\n\nEdited.\n");
|
|
223
|
+
commit("auto-commit: heartbeat safety net (1 file)");
|
|
224
|
+
|
|
225
|
+
const sentinel = join(repoDir, "textconv-executed");
|
|
226
|
+
git("config", "diff.evil.textconv", `sh -c "touch ${sentinel}; cat"`);
|
|
227
|
+
|
|
228
|
+
const history = await getSkillHistory("alpha");
|
|
229
|
+
|
|
230
|
+
// The driver stays unrun...
|
|
231
|
+
expect(existsSync(sentinel)).toBe(false);
|
|
232
|
+
// ...and the diff still renders, so the hardening did not cost the feature.
|
|
233
|
+
expect(history.revisions[0]!.diff).toContain("Edited.");
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// `--no-ext-diff` guards the sibling vector, `diff.<driver>.command`, but has
|
|
237
|
+
// no test here: an external diff driver does not fire in this environment
|
|
238
|
+
// even without the flag, so an assertion about it would pass whether or not
|
|
239
|
+
// the guard were present.
|
|
240
|
+
|
|
241
|
+
test("a traversal-shaped id is rejected before it reaches a pathspec", async () => {
|
|
242
|
+
write("skills/alpha/SKILL.md", "# Alpha\n");
|
|
243
|
+
commit("initial");
|
|
244
|
+
|
|
245
|
+
await expect(getSkillHistory("../../etc")).rejects.toThrow(
|
|
246
|
+
/Invalid skill id/,
|
|
247
|
+
);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("a workspace that is not a repository yields empty history, and stays not a repository", async () => {
|
|
251
|
+
const bare = mkdtempSync(join(tmpdir(), "skill-history-norepo-"));
|
|
252
|
+
const previous = repoDir;
|
|
253
|
+
repoDir = bare;
|
|
254
|
+
try {
|
|
255
|
+
const history = await getSkillHistory("alpha");
|
|
256
|
+
|
|
257
|
+
expect(history.revisions).toEqual([]);
|
|
258
|
+
// The empty result is not enough on its own: reading through
|
|
259
|
+
// `runReadOnlyGit` would ALSO return empty here, having quietly created
|
|
260
|
+
// the repository first. This route is a GET, so the absence of the
|
|
261
|
+
// write is the actual invariant.
|
|
262
|
+
expect(existsSync(join(bare, ".git"))).toBe(false);
|
|
263
|
+
expect(readdirSync(bare)).toEqual([]);
|
|
264
|
+
} finally {
|
|
265
|
+
repoDir = previous;
|
|
266
|
+
rmSync(bare, { recursive: true, force: true });
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
});
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Skill revision history, read from the workspace git repository.
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
//
|
|
5
|
+
// The workspace is a git repo with an auto-committing heartbeat, and `skills/`
|
|
6
|
+
// is tracked, so every managed-skill write already has its prior content
|
|
7
|
+
// retained. This module reads that history rather than storing a second copy:
|
|
8
|
+
// `createManagedSkill` writes through an atomic rename and keeps no version of
|
|
9
|
+
// its own, but the commit underneath it does.
|
|
10
|
+
//
|
|
11
|
+
// A revision here is ONE UPDATE TO THE WHOLE SKILL, not a per-file log. A
|
|
12
|
+
// single commit is diffed against the skill's directory, so `SKILL.md`,
|
|
13
|
+
// `scripts/`, and `references/` changes made together appear as one entry with
|
|
14
|
+
// one combined diff, which is what "what changed the last time this skill was
|
|
15
|
+
// updated" means. The commit itself usually touches many unrelated files (the
|
|
16
|
+
// heartbeat batches by count and age), so the pathspec is what makes the diff
|
|
17
|
+
// legible.
|
|
18
|
+
//
|
|
19
|
+
// Two properties of that history shape the output, and both are load-bearing:
|
|
20
|
+
//
|
|
21
|
+
// - `install-meta.json` is EXCLUDED. It carries a `lastUsedAt` stamp
|
|
22
|
+
// refreshed every time a skill is loaded, so roughly half of a skill's
|
|
23
|
+
// commits touch nothing else. Including it would report a skill as
|
|
24
|
+
// "updated" on days nobody edited it, which is worse than showing no
|
|
25
|
+
// history at all. Revisions left empty by the exclusion are dropped.
|
|
26
|
+
// - History is BOUNDED. The workspace periodically squashes
|
|
27
|
+
// ("Compacted workspace history"), so the oldest revision available is not
|
|
28
|
+
// necessarily the skill's creation. Callers should present the list as
|
|
29
|
+
// "recent changes", never as a complete record.
|
|
30
|
+
//
|
|
31
|
+
// Commit subjects are deliberately not surfaced: they read
|
|
32
|
+
// `auto-commit: heartbeat safety net (164 files, changes older than 900s)`,
|
|
33
|
+
// which describes the batch rather than the skill and would actively mislead.
|
|
34
|
+
//
|
|
35
|
+
// Every git call goes through `runReadOnlyGitWithoutInit`, NOT
|
|
36
|
+
// `runReadOnlyGit`. The latter awaits `ensureInitialized()`, which creates the
|
|
37
|
+
// repository, writes `.gitignore` and the hooks directory, makes an initial
|
|
38
|
+
// commit, and schedules a history compaction when the workspace is not yet a
|
|
39
|
+
// repo. This module is reached from an HTTP GET, which `src/runtime/AGENTS.md`
|
|
40
|
+
// requires to be side-effect-free, so observing history must never be the
|
|
41
|
+
// thing that brings a repository into existence.
|
|
42
|
+
|
|
43
|
+
import { getLogger } from "../util/logger.js";
|
|
44
|
+
import { getWorkspaceDir } from "../util/platform.js";
|
|
45
|
+
import { getWorkspaceGitService } from "../workspace/git-service.js";
|
|
46
|
+
import { validateManagedSkillId } from "./managed-store.js";
|
|
47
|
+
|
|
48
|
+
const log = getLogger("skill-history");
|
|
49
|
+
|
|
50
|
+
/** How many revisions to return when the caller does not specify. */
|
|
51
|
+
export const DEFAULT_SKILL_HISTORY_LIMIT = 20;
|
|
52
|
+
|
|
53
|
+
/** Hard ceiling, so one request cannot walk an entire repository. */
|
|
54
|
+
export const MAX_SKILL_HISTORY_LIMIT = 100;
|
|
55
|
+
|
|
56
|
+
/** One update to a skill: a commit, diffed to that skill's directory. */
|
|
57
|
+
export interface SkillRevision {
|
|
58
|
+
/** Abbreviated commit hash. Opaque to callers; useful for support. */
|
|
59
|
+
id: string;
|
|
60
|
+
/** ISO-8601 commit timestamp. */
|
|
61
|
+
changedAt: string;
|
|
62
|
+
/** Paths changed inside the skill, relative to the skill directory. */
|
|
63
|
+
files: string[];
|
|
64
|
+
/** Unified diff of this commit, restricted to the skill's directory. */
|
|
65
|
+
diff: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface SkillHistory {
|
|
69
|
+
skillId: string;
|
|
70
|
+
revisions: SkillRevision[];
|
|
71
|
+
/**
|
|
72
|
+
* Whether history may be incomplete because the workspace squashed older
|
|
73
|
+
* commits. When true the oldest revision listed is a floor, not the
|
|
74
|
+
* skill's creation.
|
|
75
|
+
*/
|
|
76
|
+
truncatedByCompaction: boolean;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Field separator for the `git log` format: an ASCII unit separator, written
|
|
81
|
+
* as an escape so the source stays copy-paste safe. It cannot occur in a
|
|
82
|
+
* commit hash or an ISO timestamp.
|
|
83
|
+
*/
|
|
84
|
+
const FIELD = "\u001f";
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Diff options that must accompany EVERY git read here.
|
|
88
|
+
*
|
|
89
|
+
* `git show` and `git diff` honour repository-controlled helpers by default:
|
|
90
|
+
* a `.gitattributes` entry can select a diff driver, and a `diff.<driver>.
|
|
91
|
+
* textconv` or `diff.<driver>.command` setting names a program git then
|
|
92
|
+
* EXECUTES while rendering the patch. Workspace files and git metadata are
|
|
93
|
+
* writable by model and tool paths, so without these flags merely viewing a
|
|
94
|
+
* skill's history would run whatever the workspace asked for, inside the
|
|
95
|
+
* daemon, on behalf of any caller holding `settings.read` (ATL-1238).
|
|
96
|
+
*
|
|
97
|
+
* Applied through `readGit` rather than at each call site, so a future reader
|
|
98
|
+
* cannot add a git invocation that quietly omits them.
|
|
99
|
+
*/
|
|
100
|
+
const DIFF_HARDENING = ["--no-textconv", "--no-ext-diff"];
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Run one hardened, non-initializing git read. The subcommand is passed
|
|
104
|
+
* separately because the hardening flags have to follow it.
|
|
105
|
+
*/
|
|
106
|
+
async function readGit(
|
|
107
|
+
git: ReturnType<typeof getWorkspaceGitService>,
|
|
108
|
+
subcommand: "log" | "show",
|
|
109
|
+
args: string[],
|
|
110
|
+
): Promise<{ stdout: string; stderr: string }> {
|
|
111
|
+
return git.runReadOnlyGitWithoutInit([
|
|
112
|
+
subcommand,
|
|
113
|
+
...DIFF_HARDENING,
|
|
114
|
+
...args,
|
|
115
|
+
]);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Read a skill's recent revisions from workspace git.
|
|
120
|
+
*
|
|
121
|
+
* Returns an empty history rather than throwing when git is unavailable, the
|
|
122
|
+
* workspace is not a repository, or the skill has no tracked changes: history
|
|
123
|
+
* is a read-only enrichment and must never be the reason a caller fails.
|
|
124
|
+
* An invalid skill id DOES throw, because it means the caller passed
|
|
125
|
+
* unvalidated input into a pathspec.
|
|
126
|
+
*/
|
|
127
|
+
export async function getSkillHistory(
|
|
128
|
+
skillId: string,
|
|
129
|
+
options: { limit?: number } = {},
|
|
130
|
+
): Promise<SkillHistory> {
|
|
131
|
+
const invalid = validateManagedSkillId(skillId);
|
|
132
|
+
if (invalid) {
|
|
133
|
+
throw new Error(`Invalid skill id: ${invalid}`);
|
|
134
|
+
}
|
|
135
|
+
const limit = Math.min(
|
|
136
|
+
Math.max(1, options.limit ?? DEFAULT_SKILL_HISTORY_LIMIT),
|
|
137
|
+
MAX_SKILL_HISTORY_LIMIT,
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const empty: SkillHistory = {
|
|
141
|
+
skillId,
|
|
142
|
+
revisions: [],
|
|
143
|
+
truncatedByCompaction: false,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
let git: ReturnType<typeof getWorkspaceGitService>;
|
|
147
|
+
try {
|
|
148
|
+
git = getWorkspaceGitService(getWorkspaceDir());
|
|
149
|
+
} catch (err) {
|
|
150
|
+
log.warn({ err, skillId }, "workspace git unavailable; no skill history");
|
|
151
|
+
return empty;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const skillPath = `skills/${skillId}/`;
|
|
155
|
+
// Exclude the usage stamp so a skill that was merely LOADED does not read as
|
|
156
|
+
// edited. `:(exclude)` is git pathspec magic, applied to both the log and
|
|
157
|
+
// the per-commit diff so the two can never disagree about what counts.
|
|
158
|
+
const pathspec = [skillPath, `:(exclude)${skillPath}install-meta.json`];
|
|
159
|
+
|
|
160
|
+
let shas: Array<{ sha: string; changedAt: string }>;
|
|
161
|
+
try {
|
|
162
|
+
const { stdout } = await readGit(git, "log", [
|
|
163
|
+
// Committer date, not author date. `git log` orders by commit date, so
|
|
164
|
+
// reporting the author date could label the list newest-first while its
|
|
165
|
+
// displayed dates disagree with that order.
|
|
166
|
+
`--format=%h${FIELD}%cI`,
|
|
167
|
+
// Ask for more than `limit`: commits whose only in-skill change is
|
|
168
|
+
// excluded by the pathspec still appear here, so the list is filtered
|
|
169
|
+
// down afterwards and would otherwise come up short.
|
|
170
|
+
`--max-count=${limit * 4}`,
|
|
171
|
+
"--",
|
|
172
|
+
...pathspec,
|
|
173
|
+
]);
|
|
174
|
+
shas = stdout
|
|
175
|
+
.split("\n")
|
|
176
|
+
.map((line) => line.trim())
|
|
177
|
+
.filter((line) => line.length > 0)
|
|
178
|
+
.map((line) => {
|
|
179
|
+
const [sha, changedAt] = line.split(FIELD);
|
|
180
|
+
return { sha: sha ?? "", changedAt: changedAt ?? "" };
|
|
181
|
+
})
|
|
182
|
+
.filter((entry) => entry.sha.length > 0);
|
|
183
|
+
} catch (err) {
|
|
184
|
+
log.warn({ err, skillId }, "git log failed; no skill history");
|
|
185
|
+
return empty;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const revisions: SkillRevision[] = [];
|
|
189
|
+
for (const { sha, changedAt } of shas) {
|
|
190
|
+
if (revisions.length >= limit) {
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
let diff: string;
|
|
194
|
+
let files: string[];
|
|
195
|
+
try {
|
|
196
|
+
const [diffResult, nameResult] = await Promise.all([
|
|
197
|
+
readGit(git, "show", ["--format=", sha, "--", ...pathspec]),
|
|
198
|
+
readGit(git, "show", [
|
|
199
|
+
"--format=",
|
|
200
|
+
"--name-only",
|
|
201
|
+
sha,
|
|
202
|
+
"--",
|
|
203
|
+
...pathspec,
|
|
204
|
+
]),
|
|
205
|
+
]);
|
|
206
|
+
diff = diffResult.stdout.trim();
|
|
207
|
+
files = nameResult.stdout
|
|
208
|
+
.split("\n")
|
|
209
|
+
.map((line) => line.trim())
|
|
210
|
+
.filter((line) => line.startsWith(skillPath))
|
|
211
|
+
.map((line) => line.slice(skillPath.length))
|
|
212
|
+
.filter((line) => line.length > 0);
|
|
213
|
+
} catch (err) {
|
|
214
|
+
log.warn({ err, skillId, sha }, "git show failed; skipping revision");
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
// Nothing left once the usage stamp is excluded: the skill was loaded,
|
|
218
|
+
// not changed.
|
|
219
|
+
if (diff.length === 0 || files.length === 0) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
revisions.push({ id: sha, changedAt, files, diff });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
skillId,
|
|
227
|
+
revisions,
|
|
228
|
+
truncatedByCompaction: await hasCompactedHistory(git),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Whether the workspace has squashed older commits, which caps how far back
|
|
234
|
+
* any skill's history can reach. Best-effort: an error here only means the
|
|
235
|
+
* caller loses the caveat, never the history.
|
|
236
|
+
*/
|
|
237
|
+
async function hasCompactedHistory(
|
|
238
|
+
git: ReturnType<typeof getWorkspaceGitService>,
|
|
239
|
+
): Promise<boolean> {
|
|
240
|
+
try {
|
|
241
|
+
const { stdout } = await readGit(git, "log", [
|
|
242
|
+
"--format=%s",
|
|
243
|
+
"--max-count=1",
|
|
244
|
+
"--grep=Compacted workspace history",
|
|
245
|
+
]);
|
|
246
|
+
return stdout.trim().length > 0;
|
|
247
|
+
} catch {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
@@ -480,7 +480,7 @@ export async function installExternalSkill(
|
|
|
480
480
|
contentHash: computeSkillHash(stagedDir) ?? undefined,
|
|
481
481
|
});
|
|
482
482
|
|
|
483
|
-
installSkillDependenciesIfPresent(stagedDir);
|
|
483
|
+
await installSkillDependenciesIfPresent(stagedDir);
|
|
484
484
|
commitStagedSkillInstall(skillSlug, stagedDir);
|
|
485
485
|
} catch (err) {
|
|
486
486
|
rmSync(stagedDir, { recursive: true, force: true });
|
|
@@ -214,6 +214,8 @@ class VellumManagedBatchTranscriber implements BatchTranscriber {
|
|
|
214
214
|
readonly providerId = "vellum" as const;
|
|
215
215
|
readonly boundaryId = "daemon-batch" as const;
|
|
216
216
|
|
|
217
|
+
constructor(private readonly language: string | undefined) {}
|
|
218
|
+
|
|
217
219
|
async transcribe(
|
|
218
220
|
request: SttTranscribeRequest,
|
|
219
221
|
): Promise<SttTranscribeResult> {
|
|
@@ -223,6 +225,7 @@ class VellumManagedBatchTranscriber implements BatchTranscriber {
|
|
|
223
225
|
request.audio,
|
|
224
226
|
request.mimeType,
|
|
225
227
|
request.signal,
|
|
228
|
+
this.language,
|
|
226
229
|
);
|
|
227
230
|
}
|
|
228
231
|
}
|
|
@@ -237,12 +240,11 @@ class VellumManagedBatchTranscriber implements BatchTranscriber {
|
|
|
237
240
|
* is unavailable.
|
|
238
241
|
*
|
|
239
242
|
* `language` is the spoken language, forwarded to providers whose batch API
|
|
240
|
-
* accepts one: Deepgram (where `"multi"` also pins nova-3)
|
|
243
|
+
* accepts one: Deepgram (where `"multi"` also pins nova-3), xAI (where
|
|
241
244
|
* `"multi"` is dropped because it is a Deepgram-specific value, not a
|
|
242
|
-
* language code)
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
* see the deferral note in `resolveBatchTranscriber`).
|
|
245
|
+
* language code), and the vellum managed path, whose platform proxy passes
|
|
246
|
+
* it to Deepgram server-side. Whisper and Gemini auto-detect natively and
|
|
247
|
+
* take no language parameter, so it is silently ignored for them.
|
|
246
248
|
*/
|
|
247
249
|
export function createDaemonBatchTranscriber(
|
|
248
250
|
apiKey: string | null | undefined,
|
|
@@ -251,7 +253,7 @@ export function createDaemonBatchTranscriber(
|
|
|
251
253
|
): BatchTranscriber | null {
|
|
252
254
|
// vellum authenticates via the platform connection, not an API key.
|
|
253
255
|
if (providerId === "vellum") {
|
|
254
|
-
return new VellumManagedBatchTranscriber();
|
|
256
|
+
return new VellumManagedBatchTranscriber(language);
|
|
255
257
|
}
|
|
256
258
|
if (!apiKey) {
|
|
257
259
|
return null;
|
|
@@ -20,6 +20,31 @@ describe("buildAdvisorSystem", () => {
|
|
|
20
20
|
expect(prompt).not.toContain("<agent_system_prompt>");
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
+
test("tells the advisor it has read-only tools for verifying decisive facts", () => {
|
|
24
|
+
const prompt = buildAdvisorSystem(null);
|
|
25
|
+
expect(prompt).toContain("read-only tools");
|
|
26
|
+
expect(prompt).toContain("read files");
|
|
27
|
+
expect(prompt).toContain("verification, not exploration");
|
|
28
|
+
expect(prompt).toContain("You cannot change anything");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("does not offer the advisor a memory or conversation search", () => {
|
|
32
|
+
// The advisor's read tools stop at the workspace. Naming `recall` here
|
|
33
|
+
// would advertise a search the role allowlist does not grant, and would
|
|
34
|
+
// contradict the scope the consult framing promises the user.
|
|
35
|
+
const prompt = buildAdvisorSystem(null);
|
|
36
|
+
expect(prompt).not.toContain("recall");
|
|
37
|
+
expect(prompt).toContain("you cannot see other conversations");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("does not claim the advisor is tool-less", () => {
|
|
41
|
+
// The advisor can open a file to check a fact; a prompt that says otherwise
|
|
42
|
+
// suppresses the read it was given tools for.
|
|
43
|
+
const prompt = buildAdvisorSystem(null);
|
|
44
|
+
expect(prompt).not.toContain("You have no tools");
|
|
45
|
+
expect(prompt).not.toContain("cannot search, read files, or run commands");
|
|
46
|
+
});
|
|
47
|
+
|
|
23
48
|
test("keeps the situational context pack out of the system prompt", () => {
|
|
24
49
|
// System Prompt Minimalism: the pack rides in the request turn instead.
|
|
25
50
|
expect(buildAdvisorSystem("parent")).not.toContain("<agent_environment>");
|
|
@@ -30,11 +30,13 @@ Evaluate the work along these dimensions, and lead with whatever matters most ri
|
|
|
30
30
|
- Verification: If the agent has no clear way to confirm its work is correct, tell it how it will know.
|
|
31
31
|
|
|
32
32
|
How to advise:
|
|
33
|
-
- Be specific and grounded. Cite what you actually see in the transcript
|
|
33
|
+
- Be specific and grounded. Cite what you actually see in the transcript: a particular result, a line of reasoning, a command that failed. Never invent details that aren't there; if a decisive fact is missing, either check it yourself with your read tools or say what the agent should go find out.
|
|
34
34
|
- Be decisive. Give a clear recommendation, not a menu of equally weighted options. When genuinely uncertain, say so and state what would resolve it.
|
|
35
35
|
- Prioritize ruthlessly. Lead with the highest-leverage point. Don't restate at length what the agent already did well, and don't pad the response with minor nitpicks — a focused, well-reasoned critique beats an exhaustive one.
|
|
36
36
|
- Stay in your lane. Advise the agent; do not role-play as it, write its final deliverable, or take its next action for it. If the agent is already on the right track, confirm it and sharpen the plan rather than manufacturing objections.
|
|
37
37
|
|
|
38
|
+
You have read-only tools: you may read files, list them, and search code to check a decisive fact before you advise. Use them with restraint. Answer from the inherited conversation whenever it already tells you what you need, and read only when a specific fact would change your advice: reading is for verification, not exploration. You cannot change anything and you cannot see other conversations, and the agent is waiting on you, so every call you make delays the guidance it gets.
|
|
39
|
+
|
|
38
40
|
Write as much as the guidance genuinely needs, and no more.`;
|
|
39
41
|
let prompt = base;
|
|
40
42
|
if (originalSystemPrompt) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Sanitize an inherited parent transcript before it is injected into a
|
|
3
|
-
*
|
|
3
|
+
* advisor consult subagent.
|
|
4
4
|
*
|
|
5
5
|
* `Conversation.injectInheritedContext` injects the parent's messages VERBATIM
|
|
6
6
|
* with no sanitization, so the advisor path must run this over the parent
|