@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
|
@@ -240,6 +240,99 @@ export function deleteDocument(surfaceId: string): boolean {
|
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
// In-document search (grep)
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
|
|
247
|
+
export interface FindMatch {
|
|
248
|
+
lineNumber: number;
|
|
249
|
+
lineContent: string;
|
|
250
|
+
matchStart: number;
|
|
251
|
+
matchEnd: number;
|
|
252
|
+
matchText: string;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export interface FindResult {
|
|
256
|
+
surfaceId: string;
|
|
257
|
+
totalMatches: number;
|
|
258
|
+
matches: FindMatch[];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const MAX_FIND_MATCHES = 50;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Search for text or a regex pattern within a document's content.
|
|
265
|
+
* Returns matching lines with line numbers and match positions.
|
|
266
|
+
* Results are capped at {@link MAX_FIND_MATCHES} to avoid oversized responses.
|
|
267
|
+
*/
|
|
268
|
+
export function findInDocument(
|
|
269
|
+
surfaceId: string,
|
|
270
|
+
query: string,
|
|
271
|
+
options: { regex?: boolean; caseSensitive?: boolean } = {},
|
|
272
|
+
): FindResult | null {
|
|
273
|
+
try {
|
|
274
|
+
const row = rawGet<{ content: string }>(
|
|
275
|
+
/*sql*/ `SELECT content FROM documents WHERE surface_id = ?`,
|
|
276
|
+
surfaceId,
|
|
277
|
+
);
|
|
278
|
+
if (!row) return null;
|
|
279
|
+
|
|
280
|
+
const lines = row.content.split("\n");
|
|
281
|
+
const matches: FindMatch[] = [];
|
|
282
|
+
let uncappedTotal = 0;
|
|
283
|
+
|
|
284
|
+
if (options.regex) {
|
|
285
|
+
const flags = options.caseSensitive ? "g" : "gi";
|
|
286
|
+
const re = new RegExp(query, flags);
|
|
287
|
+
for (let i = 0; i < lines.length; i++) {
|
|
288
|
+
const line = lines[i];
|
|
289
|
+
re.lastIndex = 0;
|
|
290
|
+
let m: RegExpExecArray | null;
|
|
291
|
+
while ((m = re.exec(line)) !== null) {
|
|
292
|
+
uncappedTotal++;
|
|
293
|
+
if (matches.length < MAX_FIND_MATCHES) {
|
|
294
|
+
matches.push({
|
|
295
|
+
lineNumber: i + 1,
|
|
296
|
+
lineContent: line,
|
|
297
|
+
matchStart: m.index,
|
|
298
|
+
matchEnd: m.index + m[0].length,
|
|
299
|
+
matchText: m[0],
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
if (m[0].length === 0) re.lastIndex++;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
const needle = options.caseSensitive ? query : query.toLowerCase();
|
|
307
|
+
for (let i = 0; i < lines.length; i++) {
|
|
308
|
+
const line = lines[i];
|
|
309
|
+
const haystack = options.caseSensitive ? line : line.toLowerCase();
|
|
310
|
+
let startPos = 0;
|
|
311
|
+
while (startPos <= haystack.length) {
|
|
312
|
+
const idx = haystack.indexOf(needle, startPos);
|
|
313
|
+
if (idx === -1) break;
|
|
314
|
+
uncappedTotal++;
|
|
315
|
+
if (matches.length < MAX_FIND_MATCHES) {
|
|
316
|
+
matches.push({
|
|
317
|
+
lineNumber: i + 1,
|
|
318
|
+
lineContent: line,
|
|
319
|
+
matchStart: idx,
|
|
320
|
+
matchEnd: idx + needle.length,
|
|
321
|
+
matchText: line.slice(idx, idx + needle.length),
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
startPos = idx + Math.max(needle.length, 1);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return { surfaceId, totalMatches: uncappedTotal, matches };
|
|
330
|
+
} catch (error) {
|
|
331
|
+
log.error({ err: error, surfaceId }, "Find-in-document error");
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
243
336
|
// ---------------------------------------------------------------------------
|
|
244
337
|
// Document persistence
|
|
245
338
|
// ---------------------------------------------------------------------------
|
|
@@ -297,6 +390,115 @@ export function saveDocument(params: {
|
|
|
297
390
|
}
|
|
298
391
|
}
|
|
299
392
|
|
|
393
|
+
// ---------------------------------------------------------------------------
|
|
394
|
+
// Find-and-replace
|
|
395
|
+
// ---------------------------------------------------------------------------
|
|
396
|
+
|
|
397
|
+
export interface ReplaceInDocumentOptions {
|
|
398
|
+
regex?: boolean;
|
|
399
|
+
caseSensitive?: boolean;
|
|
400
|
+
maxReplacements?: number;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export type ReplaceInDocumentResult =
|
|
404
|
+
| { success: true; replacements_made: number; content_changed: boolean }
|
|
405
|
+
| { success: false; error: string };
|
|
406
|
+
|
|
407
|
+
function escapeRegExpChars(str: string): string {
|
|
408
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Find and replace text within a document — like sed.
|
|
413
|
+
* Supports literal text and regex patterns with optional backreferences.
|
|
414
|
+
*/
|
|
415
|
+
export function replaceInDocument(
|
|
416
|
+
surfaceId: string,
|
|
417
|
+
find: string,
|
|
418
|
+
replace: string,
|
|
419
|
+
options: ReplaceInDocumentOptions = {},
|
|
420
|
+
): ReplaceInDocumentResult {
|
|
421
|
+
try {
|
|
422
|
+
const row = rawGet<{ content: string }>(
|
|
423
|
+
/*sql*/ `SELECT content FROM documents WHERE surface_id = ?`,
|
|
424
|
+
surfaceId,
|
|
425
|
+
);
|
|
426
|
+
if (!row) {
|
|
427
|
+
return { success: false, error: "Document not found" };
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const flags = "g" + (options.caseSensitive === true ? "" : "i");
|
|
431
|
+
const pattern = options.regex
|
|
432
|
+
? new RegExp(find, flags)
|
|
433
|
+
: new RegExp(escapeRegExpChars(find), flags);
|
|
434
|
+
|
|
435
|
+
const totalMatches = [...row.content.matchAll(pattern)].length;
|
|
436
|
+
if (
|
|
437
|
+
totalMatches === 0 ||
|
|
438
|
+
(options.maxReplacements != null && options.maxReplacements <= 0)
|
|
439
|
+
) {
|
|
440
|
+
return { success: true, replacements_made: 0, content_changed: false };
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
let newContent: string;
|
|
444
|
+
let replacementsMade: number;
|
|
445
|
+
|
|
446
|
+
if (
|
|
447
|
+
options.maxReplacements != null &&
|
|
448
|
+
options.maxReplacements < totalMatches
|
|
449
|
+
) {
|
|
450
|
+
// Iterative replacement up to maxReplacements using manual exec loop
|
|
451
|
+
// so backreferences in the replacement string work correctly.
|
|
452
|
+
const limit = options.maxReplacements;
|
|
453
|
+
let count = 0;
|
|
454
|
+
let result = "";
|
|
455
|
+
let lastIndex = 0;
|
|
456
|
+
let m: RegExpExecArray | null;
|
|
457
|
+
while ((m = pattern.exec(row.content)) !== null) {
|
|
458
|
+
if (count >= limit) break;
|
|
459
|
+
result += row.content.slice(lastIndex, m.index);
|
|
460
|
+
const singleMatchPattern = new RegExp(
|
|
461
|
+
pattern.source,
|
|
462
|
+
pattern.flags.replace("g", ""),
|
|
463
|
+
);
|
|
464
|
+
result += m[0].replace(singleMatchPattern, replace);
|
|
465
|
+
lastIndex = m.index + m[0].length;
|
|
466
|
+
count++;
|
|
467
|
+
if (m[0].length === 0) pattern.lastIndex++;
|
|
468
|
+
}
|
|
469
|
+
result += row.content.slice(lastIndex);
|
|
470
|
+
newContent = result;
|
|
471
|
+
replacementsMade = count;
|
|
472
|
+
} else {
|
|
473
|
+
newContent = row.content.replace(pattern, replace);
|
|
474
|
+
replacementsMade = totalMatches;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const wordCount = newContent
|
|
478
|
+
.split(/\s+/)
|
|
479
|
+
.filter((w) => w.length > 0).length;
|
|
480
|
+
rawRun(
|
|
481
|
+
/*sql*/ `UPDATE documents SET content = ?, word_count = ?, updated_at = ? WHERE surface_id = ?`,
|
|
482
|
+
newContent,
|
|
483
|
+
wordCount,
|
|
484
|
+
Date.now(),
|
|
485
|
+
surfaceId,
|
|
486
|
+
);
|
|
487
|
+
log.info({ surfaceId, replacementsMade }, "Replaced text in document");
|
|
488
|
+
return {
|
|
489
|
+
success: true,
|
|
490
|
+
replacements_made: replacementsMade,
|
|
491
|
+
content_changed: true,
|
|
492
|
+
};
|
|
493
|
+
} catch (error) {
|
|
494
|
+
log.error({ err: error, surfaceId }, "Replace-in-document error");
|
|
495
|
+
return {
|
|
496
|
+
success: false,
|
|
497
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
300
502
|
/** Update persisted document content (append or replace). */
|
|
301
503
|
export function updateDocumentContent(
|
|
302
504
|
surfaceId: string,
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
type TestMessage = {
|
|
6
|
+
id: string;
|
|
7
|
+
conversationId: string;
|
|
8
|
+
role: string;
|
|
9
|
+
content: string;
|
|
10
|
+
createdAt: number;
|
|
11
|
+
metadata: string | null;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const parentMessages: TestMessage[] = [
|
|
15
|
+
{
|
|
16
|
+
id: "msg-parent-1",
|
|
17
|
+
conversationId: "parent-conv",
|
|
18
|
+
role: "user",
|
|
19
|
+
content: JSON.stringify([{ type: "text", text: "go research foo" }]),
|
|
20
|
+
createdAt: 1_700_000_000_000,
|
|
21
|
+
metadata: null,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "msg-parent-2",
|
|
25
|
+
conversationId: "parent-conv",
|
|
26
|
+
role: "assistant",
|
|
27
|
+
content: JSON.stringify([{ type: "text", text: "spawning subagent" }]),
|
|
28
|
+
createdAt: 1_700_000_001_000,
|
|
29
|
+
metadata: JSON.stringify({
|
|
30
|
+
subagentNotification: {
|
|
31
|
+
subagentId: "sa-1",
|
|
32
|
+
label: "research-foo",
|
|
33
|
+
status: "completed",
|
|
34
|
+
conversationId: "child-conv-1",
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
const childMessages: TestMessage[] = [
|
|
41
|
+
{
|
|
42
|
+
id: "msg-child-1",
|
|
43
|
+
conversationId: "child-conv-1",
|
|
44
|
+
role: "user",
|
|
45
|
+
content: JSON.stringify([
|
|
46
|
+
{ type: "text", text: "Objective: research foo and report back." },
|
|
47
|
+
]),
|
|
48
|
+
createdAt: 1_700_000_002_000,
|
|
49
|
+
metadata: null,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "msg-child-2",
|
|
53
|
+
conversationId: "child-conv-1",
|
|
54
|
+
role: "assistant",
|
|
55
|
+
content: JSON.stringify([
|
|
56
|
+
{ type: "text", text: "I found that foo is a bar." },
|
|
57
|
+
]),
|
|
58
|
+
createdAt: 1_700_000_003_000,
|
|
59
|
+
metadata: null,
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const messageMetadataSchema = z.object({
|
|
64
|
+
subagentNotification: z
|
|
65
|
+
.object({
|
|
66
|
+
subagentId: z.string(),
|
|
67
|
+
label: z.string(),
|
|
68
|
+
status: z.enum(["running", "completed", "failed", "aborted"]),
|
|
69
|
+
conversationId: z.string().optional(),
|
|
70
|
+
})
|
|
71
|
+
.optional(),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
mock.module("../../memory/conversation-crud.js", () => ({
|
|
75
|
+
getConversation: (_id: string) => null,
|
|
76
|
+
getMessages: (id: string) =>
|
|
77
|
+
id === "child-conv-1" ? childMessages : parentMessages,
|
|
78
|
+
messageMetadataSchema,
|
|
79
|
+
}));
|
|
80
|
+
|
|
81
|
+
mock.module("../../util/truncate.js", () => ({
|
|
82
|
+
truncate: (s: string) => s,
|
|
83
|
+
}));
|
|
84
|
+
|
|
85
|
+
mock.module("../../daemon/date-context.js", () => ({
|
|
86
|
+
formatLocalTimestamp: (_ts: number, _tz?: string) => "TIME",
|
|
87
|
+
}));
|
|
88
|
+
|
|
89
|
+
const { formatMessageSliceForTranscript } =
|
|
90
|
+
await import("../transcript-formatter.js");
|
|
91
|
+
|
|
92
|
+
describe("formatMessageSliceForTranscript subagent labels", () => {
|
|
93
|
+
test("embedded subagent transcripts render with generic role labels even when parent display names are provided", () => {
|
|
94
|
+
const out = formatMessageSliceForTranscript(parentMessages, {
|
|
95
|
+
assistantName: "Bob",
|
|
96
|
+
userName: "Alice",
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Parent messages use the provided display names.
|
|
100
|
+
expect(out).toContain("## Alice (TIME)");
|
|
101
|
+
expect(out).toContain("## Bob (TIME)");
|
|
102
|
+
|
|
103
|
+
// Subagent block headers must use generic labels — the child "user" message
|
|
104
|
+
// is actually the parent assistant's objective, so labeling it "Alice"
|
|
105
|
+
// would misattribute the assistant's tasking text to the human user.
|
|
106
|
+
expect(out).toContain("### Subagent: research-foo (completed)");
|
|
107
|
+
expect(out).toContain("> **User** (TIME)");
|
|
108
|
+
expect(out).toContain("> **Assistant** (TIME)");
|
|
109
|
+
expect(out).not.toContain("> **Alice**");
|
|
110
|
+
expect(out).not.toContain("> **Bob**");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("without display-name options, parent and subagent both use generic labels", () => {
|
|
114
|
+
const out = formatMessageSliceForTranscript(parentMessages);
|
|
115
|
+
|
|
116
|
+
expect(out).toContain("## User (TIME)");
|
|
117
|
+
expect(out).toContain("## Assistant (TIME)");
|
|
118
|
+
expect(out).toContain("> **User** (TIME)");
|
|
119
|
+
expect(out).toContain("> **Assistant** (TIME)");
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* subagent conversation sections when present in message metadata.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { formatLocalTimestamp } from "../daemon/date-context.js";
|
|
8
9
|
import {
|
|
9
10
|
getConversation,
|
|
10
11
|
getMessages,
|
|
@@ -23,10 +24,6 @@ interface ContentBlock {
|
|
|
23
24
|
source?: { media_type?: string; filename?: string };
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
function formatTimestamp(ms: number): string {
|
|
27
|
-
return new Date(ms).toISOString().replace("T", " ").slice(0, 19);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
27
|
function extractAnalysisText(blocks: ContentBlock[]): string {
|
|
31
28
|
const parts: string[] = [];
|
|
32
29
|
for (const block of blocks) {
|
|
@@ -67,15 +64,36 @@ function extractAnalysisText(blocks: ContentBlock[]): string {
|
|
|
67
64
|
return parts.join("\n");
|
|
68
65
|
}
|
|
69
66
|
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
export interface TranscriptFormatOptions {
|
|
68
|
+
timeZone?: string;
|
|
69
|
+
assistantName?: string | null;
|
|
70
|
+
userName?: string | null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function resolveName(
|
|
74
|
+
name: string | null | undefined,
|
|
75
|
+
fallback: string,
|
|
76
|
+
): string {
|
|
77
|
+
return name && name.length > 0 ? name : fallback;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function formatRole(
|
|
81
|
+
role: string,
|
|
82
|
+
options: TranscriptFormatOptions = {},
|
|
83
|
+
): string {
|
|
84
|
+
return role === "user"
|
|
85
|
+
? resolveName(options.userName, "User")
|
|
86
|
+
: resolveName(options.assistantName, "Assistant");
|
|
72
87
|
}
|
|
73
88
|
|
|
74
|
-
function formatSubagentMessages(
|
|
89
|
+
function formatSubagentMessages(
|
|
90
|
+
msgs: ReturnType<typeof getMessages>,
|
|
91
|
+
options: TranscriptFormatOptions = {},
|
|
92
|
+
): string {
|
|
75
93
|
const lines: string[] = [];
|
|
76
94
|
for (const msg of msgs) {
|
|
77
|
-
const role = formatRole(msg.role);
|
|
78
|
-
const time =
|
|
95
|
+
const role = formatRole(msg.role, options);
|
|
96
|
+
const time = formatLocalTimestamp(msg.createdAt, options.timeZone);
|
|
79
97
|
const content = parseContent(msg.content);
|
|
80
98
|
const text = extractAnalysisText(content);
|
|
81
99
|
if (text) {
|
|
@@ -103,24 +121,33 @@ type TranscriptMessage = ReturnType<typeof getMessages>[number];
|
|
|
103
121
|
* Format a slice of messages as a transcript body (no top-of-conversation
|
|
104
122
|
* header). Used by background jobs that process incremental slices — the
|
|
105
123
|
* memory-retrospective job re-renders only the messages added since its
|
|
106
|
-
* last successful run rather than the whole conversation. The
|
|
107
|
-
* matches `buildAnalysisTranscript`
|
|
108
|
-
*
|
|
109
|
-
*
|
|
124
|
+
* last successful run rather than the whole conversation. The per-message
|
|
125
|
+
* structural shape matches `buildAnalysisTranscript` (header line, body,
|
|
126
|
+
* optional subagent block) so downstream agents see consistent framing.
|
|
127
|
+
* The participant *labels*, however, intentionally diverge: this function
|
|
128
|
+
* honors `TranscriptFormatOptions` so the memory-retrospective prompt can
|
|
129
|
+
* render the conversation under the assistant and user display names,
|
|
130
|
+
* while `buildAnalysisTranscript` always uses generic "User"/"Assistant"
|
|
131
|
+
* labels for the analyze-conversation flow.
|
|
110
132
|
*/
|
|
111
133
|
export function formatMessageSliceForTranscript(
|
|
112
134
|
messages: TranscriptMessage[],
|
|
135
|
+
options: TranscriptFormatOptions = {},
|
|
113
136
|
): string {
|
|
114
137
|
const lines: string[] = [];
|
|
115
138
|
for (const msg of messages) {
|
|
116
|
-
appendMessageBlock(lines, msg);
|
|
139
|
+
appendMessageBlock(lines, msg, options);
|
|
117
140
|
}
|
|
118
141
|
return lines.join("\n");
|
|
119
142
|
}
|
|
120
143
|
|
|
121
|
-
function appendMessageBlock(
|
|
122
|
-
|
|
123
|
-
|
|
144
|
+
function appendMessageBlock(
|
|
145
|
+
lines: string[],
|
|
146
|
+
msg: TranscriptMessage,
|
|
147
|
+
options: TranscriptFormatOptions = {},
|
|
148
|
+
): void {
|
|
149
|
+
const role = formatRole(msg.role, options);
|
|
150
|
+
const time = formatLocalTimestamp(msg.createdAt, options.timeZone);
|
|
124
151
|
const content = parseContent(msg.content);
|
|
125
152
|
const text = extractAnalysisText(content);
|
|
126
153
|
|
|
@@ -142,7 +169,14 @@ function appendMessageBlock(lines: string[], msg: TranscriptMessage): void {
|
|
|
142
169
|
const subMessages = getMessages(notif.conversationId);
|
|
143
170
|
lines.push(`### Subagent: ${notif.label} (${notif.status})`);
|
|
144
171
|
lines.push("");
|
|
145
|
-
|
|
172
|
+
// Subagent conversations persist the parent assistant's objective
|
|
173
|
+
// as a `user` message (see subagent/manager.ts), so reusing the
|
|
174
|
+
// parent's display-name options would render the assistant's
|
|
175
|
+
// tasking text under the human user's name. Keep child transcripts
|
|
176
|
+
// on generic role labels — and only pass through the time zone.
|
|
177
|
+
lines.push(
|
|
178
|
+
formatSubagentMessages(subMessages, { timeZone: options.timeZone }),
|
|
179
|
+
);
|
|
146
180
|
lines.push("");
|
|
147
181
|
}
|
|
148
182
|
}
|
|
@@ -163,12 +197,12 @@ export function buildAnalysisTranscript(conversationId: string): string {
|
|
|
163
197
|
const lines: string[] = [];
|
|
164
198
|
|
|
165
199
|
lines.push(`# Conversation: ${title}`);
|
|
166
|
-
lines.push(`Created: ${
|
|
200
|
+
lines.push(`Created: ${formatLocalTimestamp(conversation.createdAt)}`);
|
|
167
201
|
lines.push("");
|
|
168
202
|
|
|
169
203
|
for (const msg of allMessages) {
|
|
170
204
|
const role = formatRole(msg.role);
|
|
171
|
-
const time =
|
|
205
|
+
const time = formatLocalTimestamp(msg.createdAt);
|
|
172
206
|
const content = parseContent(msg.content);
|
|
173
207
|
const text = extractAnalysisText(content);
|
|
174
208
|
|
|
@@ -35,7 +35,6 @@ mock.module("../../util/platform.js", () => ({
|
|
|
35
35
|
getEmbeddingModelsDir: () => join(workspaceDir ?? fallbackDir, "models"),
|
|
36
36
|
getSandboxRootDir: () => join(workspaceDir ?? fallbackDir, "sandbox"),
|
|
37
37
|
getSandboxWorkingDir: () => join(workspaceDir ?? fallbackDir, "sandbox/work"),
|
|
38
|
-
getInterfacesDir: () => join(workspaceDir ?? fallbackDir, "interfaces"),
|
|
39
38
|
getSoundsDir: () => join(workspaceDir ?? fallbackDir, "sounds"),
|
|
40
39
|
getAvatarDir: () => join(workspaceDir ?? fallbackDir, "avatar"),
|
|
41
40
|
AVATAR_IMAGE_FILENAME: "avatar-image.png",
|
|
@@ -55,6 +54,7 @@ const stubConfig: {
|
|
|
55
54
|
activeHoursStart: number | null;
|
|
56
55
|
activeHoursEnd: number | null;
|
|
57
56
|
maxConsecutiveRuns: number | null;
|
|
57
|
+
disposition: string;
|
|
58
58
|
};
|
|
59
59
|
} = {
|
|
60
60
|
heartbeat: {
|
|
@@ -63,6 +63,7 @@ const stubConfig: {
|
|
|
63
63
|
activeHoursStart: null,
|
|
64
64
|
activeHoursEnd: null,
|
|
65
65
|
maxConsecutiveRuns: null,
|
|
66
|
+
disposition: "Default disposition text.",
|
|
66
67
|
},
|
|
67
68
|
};
|
|
68
69
|
mock.module("../../config/loader.js", () => ({
|
|
@@ -395,6 +396,48 @@ describe("HeartbeatService", () => {
|
|
|
395
396
|
}
|
|
396
397
|
});
|
|
397
398
|
|
|
399
|
+
test("resetTimer() during an in-flight run is not undone by that run's increment", async () => {
|
|
400
|
+
// Regression: if a guardian message arrives mid-run, `resetTimer()`
|
|
401
|
+
// zeroes the counter but the in-flight run's `finally` block used to
|
|
402
|
+
// unconditionally `_consecutiveRuns++`, leaving the counter at 1 and
|
|
403
|
+
// tripping the cap-at-1 path one auto run too early.
|
|
404
|
+
stubConfig.heartbeat.maxConsecutiveRuns = 1;
|
|
405
|
+
|
|
406
|
+
let releaseInflight: () => void = () => {};
|
|
407
|
+
const inflight = new Promise<void>((resolve) => {
|
|
408
|
+
releaseInflight = resolve;
|
|
409
|
+
});
|
|
410
|
+
runBackgroundJobImpl = async () => {
|
|
411
|
+
await inflight;
|
|
412
|
+
return { conversationId: STUB_CONVERSATION_ID, ok: true };
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
const service = new HeartbeatService({ alerter: () => {} });
|
|
416
|
+
service.start();
|
|
417
|
+
try {
|
|
418
|
+
const runPromise = service.runOnce({ force: false });
|
|
419
|
+
// Guardian message arrives while the run is still executing.
|
|
420
|
+
service.resetTimer();
|
|
421
|
+
releaseInflight();
|
|
422
|
+
expect(await runPromise).toBe(true);
|
|
423
|
+
|
|
424
|
+
// The reset during the in-flight run must survive: the next auto run
|
|
425
|
+
// should proceed because the counter is still 0, not 1.
|
|
426
|
+
runBackgroundJobImpl = async () => ({
|
|
427
|
+
conversationId: STUB_CONVERSATION_ID,
|
|
428
|
+
ok: true,
|
|
429
|
+
});
|
|
430
|
+
expect(await service.runOnce({ force: false })).toBe(true);
|
|
431
|
+
expect(
|
|
432
|
+
skipHeartbeatRunCalls.some(
|
|
433
|
+
(c) => c.reason === "max_consecutive_runs",
|
|
434
|
+
),
|
|
435
|
+
).toBe(false);
|
|
436
|
+
} finally {
|
|
437
|
+
await service.stop();
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
|
|
398
441
|
test("null disables the cap entirely", async () => {
|
|
399
442
|
stubConfig.heartbeat.maxConsecutiveRuns = null;
|
|
400
443
|
const service = new HeartbeatService({ alerter: () => {} });
|