@vellumai/assistant 0.8.2 → 0.8.4
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 +11 -12
- package/docker-entrypoint.sh +13 -2
- package/docker-init-apt-root.sh +79 -6
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +945 -36
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +112 -57
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +77 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/external-plugin-loader.test.ts +91 -19
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/host-app-control-proxy.test.ts +241 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +7 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +3 -0
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
- package/src/__tests__/llm-resolver.test.ts +340 -3
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/managed-profile-guard.test.ts +10 -0
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/openai-provider.test.ts +323 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform-proxy-context.test.ts +6 -1
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
- package/src/__tests__/plugin-types.test.ts +2 -2
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +57 -101
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +208 -22
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +14 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +469 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +89 -37
- package/src/cli/commands/plugins.ts +67 -0
- package/src/cli/commands/schedules.ts +297 -5
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/install-from-github.ts +8 -9
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/program.ts +14 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +124 -0
- package/src/config/feature-flag-registry.json +111 -23
- package/src/config/llm-resolver.ts +66 -1
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/channels.ts +9 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +14 -0
- package/src/config/schemas/llm.ts +4 -3
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +51 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/context/compactor.ts +80 -13
- package/src/context/token-estimator.ts +72 -31
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
- package/src/daemon/conversation-agent-loop.ts +252 -56
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +144 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -12
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +449 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +80 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-app-control-proxy.ts +69 -18
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +67 -65
- package/src/daemon/memory-v2-startup.ts +49 -13
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/pkb-reminder-builder.test.ts +10 -53
- package/src/daemon/pkb-reminder-builder.ts +4 -19
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +72 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/skill-memory-refresh.ts +5 -1
- package/src/daemon/trust-context.ts +6 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
- package/src/heartbeat/heartbeat-service.ts +35 -191
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +20 -3
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/ipc/cli-client.ts +147 -45
- package/src/memory/__tests__/conversation-queries.test.ts +220 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/conversation-queries.ts +87 -1
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +22 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
- package/src/memory/graph/conversation-graph-memory.ts +18 -6
- package/src/memory/graph/tools.ts +6 -37
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +4 -22
- package/src/memory/memory-retrospective-job.ts +438 -21
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +20 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +2 -0
- package/src/memory/schema/infrastructure.ts +2 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +221 -17
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +69 -29
- package/src/memory/v2/page-index.ts +246 -19
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +18 -3
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +5 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +111 -44
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +10 -1
- package/src/notifications/home-feed-side-effect.ts +136 -27
- package/src/notifications/signal.ts +0 -4
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +13 -4
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +118 -26
- package/src/plugins/external-plugin-loader.ts +82 -10
- package/src/plugins/types.ts +16 -7
- package/src/prompts/__tests__/system-prompt.test.ts +44 -45
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -76
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +51 -10
- package/src/providers/__tests__/inference.test.ts +2 -0
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +63 -13
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +24 -21
- package/src/providers/inference/auth.ts +15 -3
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/connections.ts +85 -5
- package/src/providers/inference/resolve-auth.ts +50 -5
- package/src/providers/model-catalog.ts +244 -242
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +215 -25
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +46 -4
- package/src/providers/platform-proxy/constants.ts +3 -4
- package/src/providers/provider-catalog-visibility.ts +3 -1
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +30 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +212 -57
- package/src/runtime/auth/route-policy.ts +20 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +7 -51
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/channel-availability-routes.ts +5 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +130 -12
- package/src/runtime/routes/conversation-routes.ts +288 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +14 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
- package/src/runtime/routes/integrations/a2a.ts +294 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/routes/subagents-routes.ts +41 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/subagent/manager.ts +2 -0
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/registry.ts +2 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +41 -2
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -84,6 +84,82 @@ describe("renderHistoryContent", () => {
|
|
|
84
84
|
);
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
+
test("emits attachment:N entries in contentOrder for interleaved file blocks", () => {
|
|
88
|
+
const output = renderHistoryContent([
|
|
89
|
+
{ type: "text", text: "first paragraph" },
|
|
90
|
+
{
|
|
91
|
+
type: "file",
|
|
92
|
+
source: {
|
|
93
|
+
type: "base64",
|
|
94
|
+
media_type: "text/markdown",
|
|
95
|
+
filename: "dream-015.md",
|
|
96
|
+
data: Buffer.from("a").toString("base64"),
|
|
97
|
+
},
|
|
98
|
+
_attachmentId: "att-015",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
type: "file",
|
|
102
|
+
source: {
|
|
103
|
+
type: "base64",
|
|
104
|
+
media_type: "text/markdown",
|
|
105
|
+
filename: "dream-016.md",
|
|
106
|
+
data: Buffer.from("b").toString("base64"),
|
|
107
|
+
},
|
|
108
|
+
_attachmentId: "att-016",
|
|
109
|
+
},
|
|
110
|
+
{ type: "text", text: "trailing paragraph" },
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
expect(output.contentOrder).toEqual([
|
|
114
|
+
"text:0",
|
|
115
|
+
"attachment:0",
|
|
116
|
+
"attachment:1",
|
|
117
|
+
"text:1",
|
|
118
|
+
]);
|
|
119
|
+
expect(output.attachments).toEqual([
|
|
120
|
+
{
|
|
121
|
+
attachmentId: "att-015",
|
|
122
|
+
filename: "dream-015.md",
|
|
123
|
+
mimeType: "text/markdown",
|
|
124
|
+
sizeBytes: 1,
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
attachmentId: "att-016",
|
|
128
|
+
filename: "dream-016.md",
|
|
129
|
+
mimeType: "text/markdown",
|
|
130
|
+
sizeBytes: 1,
|
|
131
|
+
},
|
|
132
|
+
]);
|
|
133
|
+
// Trailing-segment summary is preserved for legacy channel-reply
|
|
134
|
+
// delivery (Slack/Telegram outbound text) and iOS rehydrate.
|
|
135
|
+
expect(output.textSegments[1]).toContain("[File attachment] dream-015.md");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("omits attachmentId when file block has no _attachmentId", () => {
|
|
139
|
+
const output = renderHistoryContent([
|
|
140
|
+
{
|
|
141
|
+
type: "file",
|
|
142
|
+
source: {
|
|
143
|
+
type: "base64",
|
|
144
|
+
media_type: "application/pdf",
|
|
145
|
+
filename: "legacy.pdf",
|
|
146
|
+
data: Buffer.from("x").toString("base64"),
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
// attachment:0 marks the file's inline position; text:0 follows because
|
|
152
|
+
// the legacy summary append (for Slack/iOS) opens a trailing segment.
|
|
153
|
+
expect(output.contentOrder).toEqual(["attachment:0", "text:0"]);
|
|
154
|
+
expect(output.attachments).toEqual([
|
|
155
|
+
{
|
|
156
|
+
filename: "legacy.pdf",
|
|
157
|
+
mimeType: "application/pdf",
|
|
158
|
+
sizeBytes: 1,
|
|
159
|
+
},
|
|
160
|
+
]);
|
|
161
|
+
});
|
|
162
|
+
|
|
87
163
|
test("falls back to string conversion for non-array content", () => {
|
|
88
164
|
expect(renderHistoryContent("raw string").text).toBe("raw string");
|
|
89
165
|
expect(renderHistoryContent(null).text).toBe("");
|
|
@@ -320,14 +396,17 @@ describe("renderHistoryContent", () => {
|
|
|
320
396
|
expect(output.toolCallsBeforeText).toBe(false);
|
|
321
397
|
});
|
|
322
398
|
|
|
323
|
-
test("
|
|
399
|
+
test("drops orphan tool_result without matching tool_use", () => {
|
|
324
400
|
const output = renderHistoryContent([
|
|
325
401
|
{ type: "tool_result", tool_use_id: "missing", content: "some result" },
|
|
326
402
|
]);
|
|
327
403
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
]);
|
|
404
|
+
// Orphans are dropped — without the parent tool_use we can't tell the user
|
|
405
|
+
// what tool ran, so the result is meaningless. See shared.ts comment.
|
|
406
|
+
expect(output.toolCalls).toEqual([]);
|
|
407
|
+
expect(output.text).toBe("");
|
|
408
|
+
expect(output.textSegments).toEqual([]);
|
|
409
|
+
expect(output.contentOrder).toEqual([]);
|
|
331
410
|
});
|
|
332
411
|
|
|
333
412
|
test("produces textSegments for text-tool-text interleaving", () => {
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the tool-result repair logic that runs during queue drain
|
|
3
|
+
* after a steer-to-message abort interrupts an in-flight tool call.
|
|
4
|
+
*
|
|
5
|
+
* Uses the exported drainQueue indirectly by testing the repair helper's
|
|
6
|
+
* observable effects on conversation.messages. We import the
|
|
7
|
+
* repairPendingToolUseBlocks behavior through drainQueue's contract.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, expect, test } from "bun:test";
|
|
11
|
+
|
|
12
|
+
import type { Message } from "../providers/types.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Minimal reproduction of repairPendingToolUseBlocks logic for direct
|
|
16
|
+
* unit testing. The real implementation lives in conversation-process.ts.
|
|
17
|
+
* This mirrors it exactly to avoid needing the full ProcessConversationContext.
|
|
18
|
+
*/
|
|
19
|
+
function repairPendingToolUseBlocks(messages: Message[]): Message[] {
|
|
20
|
+
if (messages.length === 0) return messages;
|
|
21
|
+
|
|
22
|
+
const resolvedToolUseIds = new Set<string>();
|
|
23
|
+
const pendingToolUseIds: string[] = [];
|
|
24
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
25
|
+
const msg = messages[i];
|
|
26
|
+
if (msg.role === "user") {
|
|
27
|
+
for (const block of msg.content) {
|
|
28
|
+
if (block.type === "tool_result") {
|
|
29
|
+
resolvedToolUseIds.add(block.tool_use_id);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
} else if (msg.role === "assistant") {
|
|
33
|
+
for (const block of msg.content) {
|
|
34
|
+
if (block.type === "tool_use" && !resolvedToolUseIds.has(block.id)) {
|
|
35
|
+
pendingToolUseIds.push(block.id);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (pendingToolUseIds.length === 0) return messages;
|
|
43
|
+
|
|
44
|
+
const syntheticContent = pendingToolUseIds.map((toolUseId) => ({
|
|
45
|
+
type: "tool_result" as const,
|
|
46
|
+
tool_use_id: toolUseId,
|
|
47
|
+
content: "Tool execution was interrupted by user steering.",
|
|
48
|
+
is_error: true,
|
|
49
|
+
}));
|
|
50
|
+
messages.push({ role: "user", content: syntheticContent });
|
|
51
|
+
return messages;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
describe("steer tool-result repair", () => {
|
|
55
|
+
test("no-op when messages are empty", () => {
|
|
56
|
+
const messages: Message[] = [];
|
|
57
|
+
repairPendingToolUseBlocks(messages);
|
|
58
|
+
expect(messages).toHaveLength(0);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("no-op when last message is a user message", () => {
|
|
62
|
+
const messages: Message[] = [
|
|
63
|
+
{
|
|
64
|
+
role: "user",
|
|
65
|
+
content: [{ type: "text", text: "hello" }],
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
repairPendingToolUseBlocks(messages);
|
|
69
|
+
expect(messages).toHaveLength(1);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("no-op when last assistant message has no tool_use blocks", () => {
|
|
73
|
+
const messages: Message[] = [
|
|
74
|
+
{
|
|
75
|
+
role: "user",
|
|
76
|
+
content: [{ type: "text", text: "hello" }],
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
role: "assistant",
|
|
80
|
+
content: [{ type: "text", text: "hi there" }],
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
repairPendingToolUseBlocks(messages);
|
|
84
|
+
expect(messages).toHaveLength(2);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("no-op when tool_use has a matching tool_result", () => {
|
|
88
|
+
const messages: Message[] = [
|
|
89
|
+
{
|
|
90
|
+
role: "user",
|
|
91
|
+
content: [{ type: "text", text: "hello" }],
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
role: "assistant",
|
|
95
|
+
content: [
|
|
96
|
+
{ type: "text", text: "let me run a command" },
|
|
97
|
+
{
|
|
98
|
+
type: "tool_use",
|
|
99
|
+
id: "tu_1",
|
|
100
|
+
name: "bash",
|
|
101
|
+
input: { command: "ls" },
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
role: "user",
|
|
107
|
+
content: [
|
|
108
|
+
{
|
|
109
|
+
type: "tool_result",
|
|
110
|
+
tool_use_id: "tu_1",
|
|
111
|
+
content: "file1.txt\nfile2.txt",
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
},
|
|
115
|
+
];
|
|
116
|
+
repairPendingToolUseBlocks(messages);
|
|
117
|
+
// No synthetic message added
|
|
118
|
+
expect(messages).toHaveLength(3);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("injects synthetic tool_result for a single pending tool_use", () => {
|
|
122
|
+
const messages: Message[] = [
|
|
123
|
+
{
|
|
124
|
+
role: "user",
|
|
125
|
+
content: [{ type: "text", text: "hello" }],
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
role: "assistant",
|
|
129
|
+
content: [
|
|
130
|
+
{ type: "text", text: "let me run a command" },
|
|
131
|
+
{
|
|
132
|
+
type: "tool_use",
|
|
133
|
+
id: "tu_1",
|
|
134
|
+
name: "bash",
|
|
135
|
+
input: { command: "ls" },
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
];
|
|
140
|
+
repairPendingToolUseBlocks(messages);
|
|
141
|
+
expect(messages).toHaveLength(3);
|
|
142
|
+
|
|
143
|
+
const synthetic = messages[2];
|
|
144
|
+
expect(synthetic.role).toBe("user");
|
|
145
|
+
expect(synthetic.content).toHaveLength(1);
|
|
146
|
+
expect(synthetic.content[0].type).toBe("tool_result");
|
|
147
|
+
if (synthetic.content[0].type === "tool_result") {
|
|
148
|
+
expect(synthetic.content[0].tool_use_id).toBe("tu_1");
|
|
149
|
+
expect(synthetic.content[0].is_error).toBe(true);
|
|
150
|
+
expect(synthetic.content[0].content).toContain("interrupted");
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("injects synthetic tool_results for multiple pending tool_use blocks", () => {
|
|
155
|
+
const messages: Message[] = [
|
|
156
|
+
{
|
|
157
|
+
role: "user",
|
|
158
|
+
content: [{ type: "text", text: "hello" }],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
role: "assistant",
|
|
162
|
+
content: [
|
|
163
|
+
{
|
|
164
|
+
type: "tool_use",
|
|
165
|
+
id: "tu_1",
|
|
166
|
+
name: "bash",
|
|
167
|
+
input: { command: "ls" },
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
type: "tool_use",
|
|
171
|
+
id: "tu_2",
|
|
172
|
+
name: "read_file",
|
|
173
|
+
input: { path: "/tmp/foo" },
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
];
|
|
178
|
+
repairPendingToolUseBlocks(messages);
|
|
179
|
+
expect(messages).toHaveLength(3);
|
|
180
|
+
|
|
181
|
+
const synthetic = messages[2];
|
|
182
|
+
expect(synthetic.role).toBe("user");
|
|
183
|
+
expect(synthetic.content).toHaveLength(2);
|
|
184
|
+
|
|
185
|
+
const ids = synthetic.content
|
|
186
|
+
.filter((b) => b.type === "tool_result")
|
|
187
|
+
.map((b) => (b as { tool_use_id: string }).tool_use_id);
|
|
188
|
+
expect(ids).toContain("tu_1");
|
|
189
|
+
expect(ids).toContain("tu_2");
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("handles partial resolution — one of two tool_use blocks resolved", () => {
|
|
193
|
+
const messages: Message[] = [
|
|
194
|
+
{
|
|
195
|
+
role: "user",
|
|
196
|
+
content: [{ type: "text", text: "hello" }],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
role: "assistant",
|
|
200
|
+
content: [
|
|
201
|
+
{
|
|
202
|
+
type: "tool_use",
|
|
203
|
+
id: "tu_1",
|
|
204
|
+
name: "bash",
|
|
205
|
+
input: { command: "ls" },
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
type: "tool_use",
|
|
209
|
+
id: "tu_2",
|
|
210
|
+
name: "read_file",
|
|
211
|
+
input: { path: "/tmp/foo" },
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
role: "user",
|
|
217
|
+
content: [
|
|
218
|
+
{
|
|
219
|
+
type: "tool_result",
|
|
220
|
+
tool_use_id: "tu_1",
|
|
221
|
+
content: "file1.txt",
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
role: "assistant",
|
|
227
|
+
content: [
|
|
228
|
+
{ type: "text", text: "now reading the file" },
|
|
229
|
+
{
|
|
230
|
+
type: "tool_use",
|
|
231
|
+
id: "tu_3",
|
|
232
|
+
name: "write_file",
|
|
233
|
+
input: { path: "/tmp/bar", content: "test" },
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
},
|
|
237
|
+
];
|
|
238
|
+
repairPendingToolUseBlocks(messages);
|
|
239
|
+
expect(messages).toHaveLength(5);
|
|
240
|
+
|
|
241
|
+
const synthetic = messages[4];
|
|
242
|
+
expect(synthetic.role).toBe("user");
|
|
243
|
+
expect(synthetic.content).toHaveLength(1);
|
|
244
|
+
if (synthetic.content[0].type === "tool_result") {
|
|
245
|
+
expect(synthetic.content[0].tool_use_id).toBe("tu_3");
|
|
246
|
+
expect(synthetic.content[0].is_error).toBe(true);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
});
|
|
@@ -78,13 +78,14 @@ const {
|
|
|
78
78
|
} = await import("../prompts/system-prompt.js");
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
|
-
* Extract
|
|
82
|
-
*
|
|
83
|
-
*
|
|
81
|
+
* Extract BOOTSTRAP.md content + the user persona from the dynamic block
|
|
82
|
+
* of the system prompt, stripping configuration, skills catalog, and
|
|
83
|
+
* connected services.
|
|
84
84
|
*
|
|
85
|
-
* SOUL.md
|
|
86
|
-
* `09-soul` workspace-backed
|
|
87
|
-
* Tests that assert on
|
|
85
|
+
* Neither SOUL.md nor IDENTITY.md flows through this helper — they
|
|
86
|
+
* render as the `09-soul` and `08-identity` workspace-backed sections in
|
|
87
|
+
* the static (cached) prefix. Tests that assert on their content slice
|
|
88
|
+
* the static block directly.
|
|
88
89
|
*/
|
|
89
90
|
function basePrompt(result: string): string {
|
|
90
91
|
// The workspace files are in the dynamic block after the cache boundary.
|
|
@@ -154,18 +155,30 @@ describe("buildSystemPrompt", () => {
|
|
|
154
155
|
"# My Identity\n\nI am Vellum.",
|
|
155
156
|
);
|
|
156
157
|
const result = buildSystemPrompt();
|
|
157
|
-
|
|
158
|
+
// IDENTITY.md renders as the `08-identity` workspace-backed section
|
|
159
|
+
// in the static (cached) prefix before SYSTEM_PROMPT_CACHE_BOUNDARY.
|
|
160
|
+
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
161
|
+
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
162
|
+
expect(result.slice(0, boundaryIdx)).toContain(
|
|
163
|
+
"# My Identity\n\nI am Vellum.",
|
|
164
|
+
);
|
|
158
165
|
});
|
|
159
166
|
|
|
160
167
|
test("composes IDENTITY.md + SOUL.md when both exist", () => {
|
|
161
168
|
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "# Identity\n\nI am Vellum.");
|
|
162
169
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "# Soul\n\nBe thoughtful.");
|
|
163
170
|
const result = buildSystemPrompt();
|
|
164
|
-
//
|
|
165
|
-
//
|
|
171
|
+
// Both render in the static (cached) prefix, IDENTITY before SOUL
|
|
172
|
+
// (sections `08-identity` and `09-soul`).
|
|
166
173
|
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
167
|
-
expect(
|
|
168
|
-
|
|
174
|
+
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
175
|
+
const staticBlock = result.slice(0, boundaryIdx);
|
|
176
|
+
expect(staticBlock).toContain("# Identity\n\nI am Vellum.");
|
|
177
|
+
expect(staticBlock).toContain("# Soul\n\nBe thoughtful.");
|
|
178
|
+
const identityIdx = staticBlock.indexOf("# Identity\n\nI am Vellum.");
|
|
179
|
+
const soulIdx = staticBlock.indexOf("# Soul\n\nBe thoughtful.");
|
|
180
|
+
expect(identityIdx).toBeLessThan(soulIdx);
|
|
181
|
+
expect(basePrompt(result)).toBe("");
|
|
169
182
|
});
|
|
170
183
|
|
|
171
184
|
test("ignores empty SOUL.md", () => {
|
|
@@ -217,10 +230,9 @@ describe("buildSystemPrompt", () => {
|
|
|
217
230
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "Soul content");
|
|
218
231
|
|
|
219
232
|
const result = buildSystemPrompt();
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
// and the skills catalog is still suppressed.
|
|
233
|
+
// Both files render in the static prefix via `08-identity` /
|
|
234
|
+
// `09-soul`. Verify both are present and the skills catalog is
|
|
235
|
+
// still suppressed.
|
|
224
236
|
expect(result).toContain("Identity content");
|
|
225
237
|
expect(result).toContain("Soul content");
|
|
226
238
|
expect(result).not.toContain("## Available Skills");
|
|
@@ -262,11 +274,10 @@ describe("buildSystemPrompt", () => {
|
|
|
262
274
|
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "Identity");
|
|
263
275
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "Soul");
|
|
264
276
|
const result = buildSystemPrompt();
|
|
265
|
-
// SOUL
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
expect(basePrompt(result)).toBe("Identity");
|
|
277
|
+
// Both IDENTITY and SOUL render in the static (cached) prefix; the
|
|
278
|
+
// dynamic block sliced by basePrompt is empty here.
|
|
279
|
+
expect(basePrompt(result)).toBe("");
|
|
280
|
+
expect(result).toContain("Identity");
|
|
270
281
|
expect(result).toContain("Soul");
|
|
271
282
|
});
|
|
272
283
|
|
|
@@ -280,7 +291,8 @@ describe("buildSystemPrompt", () => {
|
|
|
280
291
|
);
|
|
281
292
|
const result = buildSystemPrompt();
|
|
282
293
|
expect(result).not.toContain("stale user content");
|
|
283
|
-
expect(
|
|
294
|
+
expect(result).toContain("Identity");
|
|
295
|
+
expect(basePrompt(result)).toBe("");
|
|
284
296
|
});
|
|
285
297
|
|
|
286
298
|
test("uses options.userPersona instead of USER.md", () => {
|
|
@@ -289,11 +301,10 @@ describe("buildSystemPrompt", () => {
|
|
|
289
301
|
const result = buildSystemPrompt({
|
|
290
302
|
userPersona: "# User persona\n\nName: Alice",
|
|
291
303
|
});
|
|
292
|
-
// SOUL
|
|
293
|
-
//
|
|
294
|
-
expect(basePrompt(result)).toBe(
|
|
295
|
-
|
|
296
|
-
);
|
|
304
|
+
// IDENTITY and SOUL both render in the static (cached) prefix; only
|
|
305
|
+
// the user persona ends up in the dynamic block.
|
|
306
|
+
expect(basePrompt(result)).toBe("# User persona\n\nName: Alice");
|
|
307
|
+
expect(result).toContain("Identity");
|
|
297
308
|
expect(result).toContain("Soul");
|
|
298
309
|
});
|
|
299
310
|
|
|
@@ -480,7 +491,11 @@ describe("buildSystemPrompt", () => {
|
|
|
480
491
|
"# Identity\n_ This is a comment\nI am Vellum.\n_ Another comment",
|
|
481
492
|
);
|
|
482
493
|
const result = buildSystemPrompt();
|
|
483
|
-
|
|
494
|
+
// IDENTITY.md renders in the static prefix via the 08-identity section,
|
|
495
|
+
// so we assert against the full prompt rather than basePrompt.
|
|
496
|
+
expect(result).toContain("# Identity\nI am Vellum.");
|
|
497
|
+
expect(result).not.toContain("_ This is a comment");
|
|
498
|
+
expect(result).not.toContain("_ Another comment");
|
|
484
499
|
});
|
|
485
500
|
|
|
486
501
|
test("collapses whitespace around stripped comment lines", () => {
|
|
@@ -610,7 +625,15 @@ describe("buildSystemPrompt", () => {
|
|
|
610
625
|
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "I am Vellum.");
|
|
611
626
|
const result = buildSystemPrompt();
|
|
612
627
|
expect(result.startsWith("Custom prefix")).toBe(true);
|
|
613
|
-
|
|
628
|
+
// IDENTITY.md renders via 08-identity in the static prefix after
|
|
629
|
+
// the 00-prefix slot.
|
|
630
|
+
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
631
|
+
const staticBlock = result.slice(0, boundaryIdx);
|
|
632
|
+
const prefixIdx = staticBlock.indexOf("Custom prefix");
|
|
633
|
+
const identityIdx = staticBlock.indexOf("I am Vellum.");
|
|
634
|
+
expect(prefixIdx).toBeGreaterThan(-1);
|
|
635
|
+
expect(identityIdx).toBeGreaterThan(prefixIdx);
|
|
636
|
+
expect(basePrompt(result)).toBe("");
|
|
614
637
|
});
|
|
615
638
|
|
|
616
639
|
test("parallel tool calls section is sourced from workspace when present", () => {
|
|
@@ -675,10 +698,7 @@ describe("buildSystemPrompt", () => {
|
|
|
675
698
|
// so the bundled body must not leak into the rendered output. This is
|
|
676
699
|
// the explicit "user silenced this section" path.
|
|
677
700
|
mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
|
|
678
|
-
writeFileSync(
|
|
679
|
-
PARALLEL_FILE,
|
|
680
|
-
"---\nenabled: false\n---\nIgnored body.\n",
|
|
681
|
-
);
|
|
701
|
+
writeFileSync(PARALLEL_FILE, "---\nenabled: false\n---\nIgnored body.\n");
|
|
682
702
|
const result = buildSystemPrompt();
|
|
683
703
|
expect(result).not.toContain("<use_parallel_tool_calls>");
|
|
684
704
|
expect(result).not.toContain("Batch independent tool calls");
|
|
@@ -713,7 +733,10 @@ describe("buildSystemPrompt", () => {
|
|
|
713
733
|
});
|
|
714
734
|
|
|
715
735
|
describe("containerized section (slot 02)", () => {
|
|
716
|
-
const CONTAINERIZED_FILE = join(
|
|
736
|
+
const CONTAINERIZED_FILE = join(
|
|
737
|
+
SYSTEM_PROMPTS_DIR,
|
|
738
|
+
"02-containerized.md",
|
|
739
|
+
);
|
|
717
740
|
|
|
718
741
|
// The runtime gate is `isContainerized` on the render context, sourced
|
|
719
742
|
// from `getIsContainerized()` which reads `process.env.IS_CONTAINERIZED`.
|
|
@@ -1093,10 +1116,7 @@ describe("buildSystemPrompt", () => {
|
|
|
1093
1116
|
});
|
|
1094
1117
|
|
|
1095
1118
|
describe("external-content section (slot 07)", () => {
|
|
1096
|
-
const EXTERNAL_FILE = join(
|
|
1097
|
-
SYSTEM_PROMPTS_DIR,
|
|
1098
|
-
"07-external-content.md",
|
|
1099
|
-
);
|
|
1119
|
+
const EXTERNAL_FILE = join(SYSTEM_PROMPTS_DIR, "07-external-content.md");
|
|
1100
1120
|
|
|
1101
1121
|
test("workspace external-content file is rendered into the static block", () => {
|
|
1102
1122
|
mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
|
|
@@ -1139,69 +1159,6 @@ describe("buildSystemPrompt", () => {
|
|
|
1139
1159
|
});
|
|
1140
1160
|
});
|
|
1141
1161
|
|
|
1142
|
-
describe("background-conversation section (slot 08)", () => {
|
|
1143
|
-
const BACKGROUND_FILE = join(
|
|
1144
|
-
SYSTEM_PROMPTS_DIR,
|
|
1145
|
-
"08-background-conversation.md",
|
|
1146
|
-
);
|
|
1147
|
-
|
|
1148
|
-
test("bundled default renders when isBackgroundConversation is true", () => {
|
|
1149
|
-
mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
|
|
1150
|
-
const result = buildSystemPrompt({ isBackgroundConversation: true });
|
|
1151
|
-
expect(result).toContain("## Background Conversation");
|
|
1152
|
-
expect(result).toContain("non-interactive background job");
|
|
1153
|
-
expect(result).toContain("`notifications` skill");
|
|
1154
|
-
});
|
|
1155
|
-
|
|
1156
|
-
test("bundled default is gated out when isBackgroundConversation is false", () => {
|
|
1157
|
-
// Mustache `{{#isBackgroundConversation}}...{{/isBackgroundConversation}}`
|
|
1158
|
-
// wraps the entire heading + body so the slot interpolates to empty
|
|
1159
|
-
// in foreground conversations and the renderer drops it.
|
|
1160
|
-
mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
|
|
1161
|
-
const result = buildSystemPrompt({ isBackgroundConversation: false });
|
|
1162
|
-
expect(result).not.toContain("## Background Conversation");
|
|
1163
|
-
});
|
|
1164
|
-
|
|
1165
|
-
test("bundled default is gated out when isBackgroundConversation is omitted", () => {
|
|
1166
|
-
// `ctx.isBackgroundConversation` is normalized to `false` when the
|
|
1167
|
-
// caller omits the flag, so the gated section drops out cleanly.
|
|
1168
|
-
mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
|
|
1169
|
-
const result = buildSystemPrompt();
|
|
1170
|
-
expect(result).not.toContain("## Background Conversation");
|
|
1171
|
-
});
|
|
1172
|
-
|
|
1173
|
-
test("workspace override is also gated by isBackgroundConversation", () => {
|
|
1174
|
-
// Workspace overrides flow through the same mustache interpolation,
|
|
1175
|
-
// so authors can rely on the same `{{#isBackgroundConversation}}`
|
|
1176
|
-
// gate in their custom body.
|
|
1177
|
-
mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
|
|
1178
|
-
writeFileSync(
|
|
1179
|
-
BACKGROUND_FILE,
|
|
1180
|
-
"{{#isBackgroundConversation}}## Background Conversation\n\nWorkspace override marker COMET_3K.\n{{/isBackgroundConversation}}\n",
|
|
1181
|
-
);
|
|
1182
|
-
|
|
1183
|
-
const offResult = buildSystemPrompt({ isBackgroundConversation: false });
|
|
1184
|
-
expect(offResult).not.toContain("## Background Conversation");
|
|
1185
|
-
expect(offResult).not.toContain("Workspace override marker COMET_3K.");
|
|
1186
|
-
|
|
1187
|
-
const onResult = buildSystemPrompt({ isBackgroundConversation: true });
|
|
1188
|
-
expect(onResult).toContain("## Background Conversation");
|
|
1189
|
-
expect(onResult).toContain("Workspace override marker COMET_3K.");
|
|
1190
|
-
});
|
|
1191
|
-
|
|
1192
|
-
test("renders after the external-content section when both render", () => {
|
|
1193
|
-
// Numeric prefix `08-` > `07-` so the background-conversation
|
|
1194
|
-
// section trails the external-content section in the static block.
|
|
1195
|
-
mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
|
|
1196
|
-
const result = buildSystemPrompt({ isBackgroundConversation: true });
|
|
1197
|
-
const externalIdx = result.indexOf("## External Content");
|
|
1198
|
-
const backgroundIdx = result.indexOf("## Background Conversation");
|
|
1199
|
-
expect(externalIdx).toBeGreaterThan(-1);
|
|
1200
|
-
expect(backgroundIdx).toBeGreaterThan(-1);
|
|
1201
|
-
expect(externalIdx).toBeLessThan(backgroundIdx);
|
|
1202
|
-
});
|
|
1203
|
-
});
|
|
1204
|
-
|
|
1205
1162
|
test("unresolved {{variable}} is left as a literal in the body", () => {
|
|
1206
1163
|
mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
|
|
1207
1164
|
writeFileSync(
|
|
@@ -1211,7 +1168,6 @@ describe("buildSystemPrompt", () => {
|
|
|
1211
1168
|
const result = buildSystemPrompt();
|
|
1212
1169
|
expect(result).toContain("Has {{somethingMissing}} in body.");
|
|
1213
1170
|
});
|
|
1214
|
-
|
|
1215
1171
|
});
|
|
1216
1172
|
});
|
|
1217
1173
|
|
|
@@ -67,6 +67,7 @@ mock.module("../tools/network/script-proxy/index.js", () => ({
|
|
|
67
67
|
import {
|
|
68
68
|
ALWAYS_INJECTED_ENV_VARS,
|
|
69
69
|
buildSanitizedEnv,
|
|
70
|
+
KATA_INJECTED_ENV_VARS,
|
|
70
71
|
KATA_SAFE_ENV_VARS,
|
|
71
72
|
SAFE_ENV_VARS,
|
|
72
73
|
} from "../tools/terminal/safe-env.js";
|
|
@@ -145,7 +146,7 @@ describe("buildSanitizedEnv", () => {
|
|
|
145
146
|
process.env.VELLUM_SANDBOX_RUNTIME = "gvisor";
|
|
146
147
|
process.env.PATH = "/usr/bin";
|
|
147
148
|
process.env.VELLUM_APT_DATA_ROOT = "/data/system";
|
|
148
|
-
process.env.LD_LIBRARY_PATH = "/
|
|
149
|
+
process.env.LD_LIBRARY_PATH = "/host/lib";
|
|
149
150
|
|
|
150
151
|
let env = buildSanitizedEnv();
|
|
151
152
|
expect(env.VELLUM_APT_DATA_ROOT).toBeUndefined();
|
|
@@ -161,6 +162,7 @@ describe("buildSanitizedEnv", () => {
|
|
|
161
162
|
expect(env.LD_LIBRARY_PATH.split(":")).toContain(
|
|
162
163
|
"/data/system/usr/local/lib",
|
|
163
164
|
);
|
|
165
|
+
expect(env.LD_LIBRARY_PATH.split(":")).not.toContain("/host/lib");
|
|
164
166
|
});
|
|
165
167
|
|
|
166
168
|
test("defaults LANG and LC_ALL to UTF-8 when unset", () => {
|
|
@@ -181,12 +183,20 @@ describe("buildSanitizedEnv", () => {
|
|
|
181
183
|
delete process.env.GATEWAY_PORT;
|
|
182
184
|
});
|
|
183
185
|
|
|
186
|
+
test("Kata entrypoint initializes apt root without inheriting apt loader paths", () => {
|
|
187
|
+
const entrypoint = readFileSync("docker-entrypoint.sh", "utf8");
|
|
188
|
+
|
|
189
|
+
expect(entrypoint).toContain("/app/assistant/docker-init-apt-root.sh");
|
|
190
|
+
expect(entrypoint).not.toContain(". /app/assistant/docker-kata-apt-env.sh");
|
|
191
|
+
});
|
|
192
|
+
|
|
184
193
|
test("result is a plain object with no prototype-inherited secrets", () => {
|
|
185
194
|
const env = buildSanitizedEnv();
|
|
186
195
|
const keys = Object.keys(env);
|
|
187
196
|
const safeKeys: string[] = [
|
|
188
197
|
...SAFE_ENV_VARS,
|
|
189
198
|
...KATA_SAFE_ENV_VARS,
|
|
199
|
+
...KATA_INJECTED_ENV_VARS,
|
|
190
200
|
...ALWAYS_INJECTED_ENV_VARS,
|
|
191
201
|
];
|
|
192
202
|
for (const key of keys) {
|