@vellumai/assistant 0.8.6 → 0.8.7
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/AGENTS.md +4 -4
- package/Dockerfile +1 -0
- package/bun.lock +11 -2
- package/docker-entrypoint.sh +8 -6
- package/docs/plugins.md +63 -28
- package/examples/plugins/echo/register.ts +4 -7
- package/knip.json +1 -0
- package/node_modules/@vellumai/environments/bun.lock +24 -0
- package/node_modules/@vellumai/environments/package.json +18 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +95 -0
- package/node_modules/@vellumai/environments/src/index.ts +11 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +73 -0
- package/node_modules/@vellumai/environments/src/types.ts +70 -0
- package/node_modules/@vellumai/environments/tsconfig.json +20 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +11 -0
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +3 -4
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +6 -2
- package/openapi.yaml +3735 -353
- package/package.json +7 -3
- package/scripts/generate-openapi.ts +20 -13
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +42 -80
- package/src/__tests__/agent-loop-exit-reason.test.ts +240 -39
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +141 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +19 -32
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +6 -4
- package/src/__tests__/agent-loop-thinking.test.ts +17 -12
- package/src/__tests__/agent-loop.test.ts +207 -341
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +4 -2
- package/src/__tests__/agent-wake-override-profile.test.ts +22 -40
- package/src/__tests__/anthropic-provider.test.ts +201 -55
- package/src/__tests__/app-builder-skill-instructions.test.ts +22 -0
- package/src/__tests__/app-control-flow.test.ts +5 -0
- package/src/__tests__/approval-cascade.test.ts +4 -11
- package/src/__tests__/approval-routes-http.test.ts +4 -2
- package/src/__tests__/assistant-event.test.ts +15 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/avatar-e2e.test.ts +7 -37
- package/src/__tests__/avatar-generator.test.ts +12 -42
- package/src/__tests__/avatar-identity-sync.test.ts +28 -3
- package/src/__tests__/background-shell-bash.test.ts +3 -7
- package/src/__tests__/btw-routes.test.ts +7 -12
- package/src/__tests__/call-pointer-messages.test.ts +5 -3
- package/src/__tests__/call-site-routing-provider.test.ts +22 -40
- package/src/__tests__/catalog-files.test.ts +1 -0
- package/src/__tests__/channel-approval-routes.test.ts +48 -20
- package/src/__tests__/channel-approvals.test.ts +3 -1
- package/src/__tests__/channel-invite-transport.test.ts +1 -5
- package/src/__tests__/channel-readiness-routes.test.ts +0 -4
- package/src/__tests__/channel-readiness-slack-remote.test.ts +2 -7
- package/src/__tests__/channel-retry-sweep.test.ts +71 -79
- package/src/__tests__/circuit-breaker-pipeline.test.ts +3 -3
- package/src/__tests__/clawhub-files.test.ts +1 -0
- package/src/__tests__/compaction-events.test.ts +5 -17
- package/src/__tests__/compaction-pipeline.test.ts +1 -1
- package/src/__tests__/compaction-timeout-recovery.test.ts +37 -48
- package/src/__tests__/compaction-trail-store.test.ts +1 -79
- package/src/__tests__/compactor-image-manifest-trust.test.ts +112 -0
- package/src/__tests__/computer-use-tools.test.ts +2 -2
- package/src/__tests__/config-watcher.test.ts +28 -0
- package/src/__tests__/context-search-agent-runner.test.ts +6 -3
- package/src/__tests__/context-token-estimator.test.ts +34 -0
- package/src/__tests__/context-window-manager-compact-retry.test.ts +291 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +14 -7
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +3 -2
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +12 -27
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +430 -90
- package/src/__tests__/conversation-agent-loop.test.ts +581 -62
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clear-safety.test.ts +20 -10
- package/src/__tests__/conversation-confirmation-signals.test.ts +15 -45
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-disk-view.test.ts +10 -17
- package/src/__tests__/conversation-fork-crud.test.ts +86 -172
- package/src/__tests__/conversation-fork-route.test.ts +16 -14
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -6
- package/src/__tests__/conversation-lifecycle.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +3 -2
- package/src/__tests__/conversation-load-history-stripped.test.ts +1 -1
- package/src/__tests__/conversation-message-sync-tags.test.ts +3 -4
- package/src/__tests__/conversation-pairing.test.ts +34 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -0
- package/src/__tests__/conversation-process-callsite.test.ts +27 -30
- package/src/__tests__/conversation-provider-retry-repair.test.ts +53 -44
- package/src/__tests__/conversation-queue.test.ts +270 -164
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -2
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -2
- package/src/__tests__/conversation-runtime-assembly.test.ts +20 -22
- package/src/__tests__/conversation-runtime-workspace.test.ts +19 -1
- package/src/__tests__/conversation-slash-queue.test.ts +37 -31
- package/src/__tests__/conversation-slash-unknown.test.ts +13 -15
- package/src/__tests__/conversation-speed-override.test.ts +8 -22
- package/src/__tests__/conversation-stream-state.test.ts +484 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +6 -15
- package/src/__tests__/conversation-surfaces-app-control.test.ts +32 -4
- package/src/__tests__/conversation-surfaces-state-update.test.ts +5 -2
- package/src/__tests__/conversation-surfaces-table-action.test.ts +6 -15
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +23 -11
- package/src/__tests__/conversation-unread-route.test.ts +14 -2
- package/src/__tests__/conversation-usage.test.ts +0 -2
- package/src/__tests__/conversation-wipe.test.ts +1 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +3 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +48 -22
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +27 -7
- package/src/__tests__/credential-execution-tools.test.ts +1 -2
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- package/src/__tests__/cross-provider-web-search.test.ts +6 -2
- package/src/__tests__/cu-unified-flow.test.ts +26 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +11 -0
- package/src/__tests__/disk-pressure-guard.test.ts +66 -0
- package/src/__tests__/disk-pressure-routes.test.ts +9 -2
- package/src/__tests__/dm-persistence.test.ts +7 -2
- package/src/__tests__/dynamic-page-surface.test.ts +68 -0
- package/src/__tests__/edit-propagation.test.ts +1 -2
- package/src/__tests__/empty-response-pipeline.test.ts +127 -5
- package/src/__tests__/filing-service.test.ts +2 -2
- package/src/__tests__/first-greeting.test.ts +55 -14
- package/src/__tests__/gemini-inline-media.test.ts +78 -0
- package/src/__tests__/gemini-provider.test.ts +351 -28
- package/src/__tests__/guardian-routing-state.test.ts +60 -71
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +9 -7
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +2 -1
- package/src/__tests__/history-repair-hook.test.ts +161 -0
- package/src/__tests__/history-repair-observability.test.ts +1 -1
- package/src/__tests__/history-repair.test.ts +2 -1
- package/src/__tests__/host-app-control-proxy.test.ts +2 -0
- package/src/__tests__/host-cu-proxy.test.ts +2 -0
- package/src/__tests__/host-file-edit-tool.test.ts +4 -2
- package/src/__tests__/host-file-proxy.test.ts +31 -0
- package/src/__tests__/host-file-read-tool.test.ts +4 -2
- package/src/__tests__/host-file-write-tool.test.ts +9 -3
- package/src/__tests__/host-proxy-preactivation.test.ts +53 -14
- package/src/__tests__/host-shell-tool.test.ts +9 -4
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/identity-intro-cache.test.ts +35 -14
- package/src/__tests__/inbound-slack-persistence.test.ts +7 -2
- package/src/__tests__/injector-background-turn.test.ts +1 -1
- package/src/__tests__/injector-chain.test.ts +1 -1
- package/src/__tests__/injector-disk-pressure.test.ts +1 -1
- package/src/__tests__/injector-document-comments.test.ts +1 -1
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +1 -1
- package/src/__tests__/injector-v3-suppression.test.ts +220 -0
- package/src/__tests__/list-messages-attachments.test.ts +7 -8
- package/src/__tests__/list-messages-hidden-metadata.test.ts +17 -15
- package/src/__tests__/list-messages-page-latest.test.ts +0 -1
- package/src/__tests__/list-messages-tool-merge.test.ts +36 -6
- package/src/__tests__/llm-call-pipeline.test.ts +21 -15
- package/src/__tests__/llm-request-log-turn-query.test.ts +42 -86
- package/src/__tests__/llm-resolver.test.ts +23 -47
- package/src/__tests__/llm-usage-store.test.ts +45 -0
- package/src/__tests__/log-export-routes.test.ts +59 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -8
- package/src/__tests__/mcp-auth-routes.test.ts +15 -10
- package/src/__tests__/mcp-health-check.test.ts +18 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +1 -1
- package/src/__tests__/memory-v2-static-injector.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +8 -4
- package/src/__tests__/migration-export-http.test.ts +12 -12
- package/src/__tests__/migration-import-commit-http.test.ts +8 -8
- package/src/__tests__/migration-import-preflight-http.test.ts +7 -7
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +14 -20
- package/src/__tests__/notification-decision-identity.test.ts +9 -18
- package/src/__tests__/notification-decision-recipient-context.test.ts +3 -6
- package/src/__tests__/oauth-commands-routes.test.ts +1 -1
- package/src/__tests__/onboarding-template-contract.test.ts +10 -0
- package/src/__tests__/openai-provider.test.ts +66 -70
- package/src/__tests__/openai-responses-provider.test.ts +21 -77
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -1
- package/src/__tests__/overflow-reduce-pipeline.test.ts +2 -4
- package/src/__tests__/parallel-tool.benchmark.test.ts +24 -36
- package/src/__tests__/persistence-pipeline.test.ts +15 -26
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -1
- package/src/__tests__/pipeline-runner.test.ts +2 -3
- package/src/__tests__/plugin-bootstrap.test.ts +51 -25
- package/src/__tests__/plugin-route-contribution.test.ts +6 -16
- package/src/__tests__/plugin-skill-contribution.test.ts +7 -17
- package/src/__tests__/plugin-tool-contribution.test.ts +10 -26
- package/src/__tests__/plugin-types.test.ts +7 -14
- package/src/__tests__/prechat-onboarding-contract.test.ts +23 -0
- package/src/__tests__/process-message-background-slack.test.ts +17 -16
- package/src/__tests__/process-message-display-content.test.ts +30 -42
- package/src/__tests__/provider-commit-message-generator.test.ts +19 -14
- package/src/__tests__/provider-error-scenarios.test.ts +7 -6
- package/src/__tests__/provider-platform-proxy-integration.test.ts +3 -8
- package/src/__tests__/provider-send-message-override-profile.test.ts +9 -25
- package/src/__tests__/provider-streaming.benchmark.test.ts +12 -22
- package/src/__tests__/provider-usage-tracking.test.ts +0 -6
- package/src/__tests__/ratelimit.test.ts +9 -4
- package/src/__tests__/relay-server.test.ts +20 -13
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +5 -8
- package/src/__tests__/retry-thinking-tool-choice.test.ts +10 -13
- package/src/__tests__/retry-verbosity-normalization.test.ts +5 -8
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +353 -0
- package/src/__tests__/schedule-routes.test.ts +80 -10
- package/src/__tests__/schedule-store.test.ts +67 -0
- package/src/__tests__/schedule-tools.test.ts +125 -0
- package/src/__tests__/secret-ingress-http.test.ts +2 -2
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +11 -7
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +11 -9
- package/src/__tests__/secret-response-routing.test.ts +13 -11
- package/src/__tests__/send-endpoint-busy.test.ts +2 -1
- package/src/__tests__/shell-observability.test.ts +249 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +11 -11
- package/src/__tests__/skill-feature-flags.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +10 -10
- package/src/__tests__/skills-files-catalog-fallback.test.ts +10 -0
- package/src/__tests__/skillssh-files.test.ts +1 -0
- package/src/__tests__/starter-task-flow.test.ts +6 -6
- package/src/__tests__/strip-memory-injections.test.ts +102 -14
- package/src/__tests__/subagent-call-site-routing.test.ts +2 -2
- package/src/__tests__/suggestion-routes.test.ts +3 -3
- package/src/__tests__/sync-message-contract.test.ts +19 -16
- package/src/__tests__/system-prompt.test.ts +54 -0
- package/src/__tests__/terminal-tools.test.ts +3 -24
- package/src/__tests__/thread-backfill.test.ts +4 -9
- package/src/__tests__/title-generate-pipeline.test.ts +1 -1
- package/src/__tests__/token-estimate-pipeline.test.ts +2 -4
- package/src/__tests__/tool-error-pipeline.test.ts +2 -2
- package/src/__tests__/tool-execute-pipeline.test.ts +1 -1
- package/src/__tests__/tool-preview-lifecycle.test.ts +13 -11
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +9 -12
- package/src/__tests__/tool-result-truncation.test.ts +3 -1
- package/src/__tests__/tools-audio-read.test.ts +113 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +44 -84
- package/src/__tests__/turn-events-store.test.ts +11 -7
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +8 -6
- package/src/__tests__/voice-session-bridge.test.ts +13 -7
- package/src/acp/__tests__/prepare-agent-env.test.ts +143 -31
- package/src/acp/prepare-agent-env.ts +52 -11
- package/src/agent/compaction-circuit.ts +140 -0
- package/src/agent/loop.ts +409 -85
- package/src/api/README.md +19 -17
- package/src/api/constants/tool-execution.ts +21 -0
- package/src/api/events/assistant-activity-state.ts +75 -0
- package/src/api/events/assistant-outbound-attachment.ts +25 -27
- package/src/api/events/assistant-text-delta.ts +6 -8
- package/src/api/events/assistant-turn-start.ts +5 -7
- package/src/api/events/avatar-updated.ts +24 -0
- package/src/api/events/compaction-circuit-closed.ts +26 -0
- package/src/api/events/compaction-circuit-open.ts +28 -0
- package/src/api/events/confirmation-request.ts +114 -0
- package/src/api/events/contact-request.ts +33 -0
- package/src/api/events/conversation-error.ts +77 -0
- package/src/api/events/conversation-list-invalidated.ts +38 -0
- package/src/api/events/conversation-title-updated.ts +24 -0
- package/src/api/events/disk-pressure-status-changed.ts +61 -0
- package/src/api/events/document-comment-created.ts +24 -28
- package/src/api/events/document-comment-deleted.ts +6 -8
- package/src/api/events/document-comment-reopened.ts +6 -8
- package/src/api/events/document-comment-resolved.ts +8 -10
- package/src/api/events/document-editor-update.ts +27 -0
- package/src/api/events/error.ts +32 -0
- package/src/api/events/generation-cancelled.ts +4 -6
- package/src/api/events/generation-handoff.ts +13 -15
- package/src/api/events/home-feed-updated.ts +26 -0
- package/src/api/events/identity-changed.ts +32 -0
- package/src/api/events/interaction-resolved.ts +50 -0
- package/src/api/events/message-complete.ts +10 -12
- package/src/api/events/message-dequeued.ts +21 -0
- package/src/api/events/message-queued-deleted.ts +23 -0
- package/src/api/events/message-queued.ts +22 -0
- package/src/api/events/message-request-complete.ts +29 -0
- package/src/api/events/navigate-settings.ts +20 -0
- package/src/api/events/notification-intent.ts +33 -0
- package/src/api/events/open-url.ts +6 -8
- package/src/api/events/question-request.ts +67 -0
- package/src/api/events/relationship-state-updated.ts +4 -6
- package/src/api/events/secret-request.ts +42 -0
- package/src/api/events/subagent-event.ts +79 -0
- package/src/api/events/subagent-spawned.ts +40 -0
- package/src/api/events/subagent-status-changed.ts +65 -0
- package/src/api/events/sync-changed.ts +29 -0
- package/src/api/events/tool-result.ts +129 -0
- package/src/api/events/tool-use-start.ts +8 -10
- package/src/api/events/turn-profile-auto-routed.ts +28 -0
- package/src/api/events/ui-surface-complete.ts +30 -0
- package/src/api/events/ui-surface-dismiss.ts +22 -0
- package/src/api/events/ui-surface-show.ts +67 -0
- package/src/api/events/ui-surface-update.ts +26 -0
- package/src/api/events/usage-update.ts +34 -0
- package/src/api/events/user-message-echo.ts +35 -0
- package/src/api/index.ts +354 -0
- package/src/api/requests/dictation.ts +45 -0
- package/src/api/responses/disk-pressure-status.ts +26 -0
- package/src/api/responses/home.ts +217 -0
- package/src/api/responses/llm-context-response.ts +2 -0
- package/src/api/responses/memory-v3-selection-log.ts +50 -0
- package/src/api/responses/subagent-detail.ts +48 -0
- package/src/approvals/guardian-decision-primitive.ts +7 -15
- package/src/approvals/guardian-request-resolvers.ts +6 -9
- package/src/avatar/__tests__/avatar-manifest.test.ts +236 -0
- package/src/avatar/__tests__/avatar-store.test.ts +193 -0
- package/src/avatar/avatar-manifest.ts +195 -0
- package/src/avatar/avatar-store.ts +113 -0
- package/src/avatar/traits-png-sync.ts +8 -2
- package/src/background-wake/next-wake.test.ts +31 -1
- package/src/background-wake/next-wake.ts +4 -1
- package/src/calls/call-conversation-messages.ts +6 -4
- package/src/calls/guardian-action-sweep.ts +6 -4
- package/src/calls/relay-server.ts +12 -8
- package/src/calls/voice-session-bridge.ts +13 -27
- package/src/cli/commands/__tests__/memory-v3.test.ts +245 -0
- package/src/cli/commands/avatar.ts +17 -11
- package/src/cli/commands/conversations.ts +15 -1
- package/src/cli/commands/db/__tests__/repair.test.ts +540 -0
- package/src/cli/commands/db/__tests__/status.test.ts +253 -0
- package/src/cli/commands/db/format.ts +48 -0
- package/src/cli/commands/db/index.ts +29 -0
- package/src/cli/commands/db/repair-step-conversation-backfill.ts +345 -0
- package/src/cli/commands/db/repair-step-integrity.ts +146 -0
- package/src/cli/commands/db/repair-steps.ts +164 -0
- package/src/cli/commands/db/repair.ts +141 -0
- package/src/cli/commands/db/status.ts +366 -0
- package/src/cli/commands/memory-v3.ts +159 -445
- package/src/cli/lib/cli-colors.ts +24 -6
- package/src/cli/program.ts +4 -5
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/assistant-feature-flags.ts +2 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +14 -3
- package/src/config/bundled-skills/media-processing/services/reduce.ts +6 -9
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +7 -2
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/call-site-defaults.ts +2 -7
- package/src/config/feature-flag-registry.json +25 -9
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -226
- package/src/config/schemas/call-site-catalog.ts +8 -15
- package/src/config/schemas/llm.ts +2 -3
- package/src/config/schemas/memory-lifecycle.ts +24 -0
- package/src/config/schemas/memory-v2.ts +0 -253
- package/src/config/schemas/memory-v3.ts +39 -0
- package/src/config/schemas/memory.ts +6 -1
- package/src/config/schemas/timeouts.ts +3 -1
- package/src/context/compactor.ts +54 -31
- package/src/context/token-estimator.ts +19 -0
- package/src/context/tool-result-truncation.ts +1 -43
- package/src/context/window-manager.ts +138 -20
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +2 -2
- package/src/daemon/__tests__/web-search-status-text.test.ts +10 -6
- package/src/daemon/approval-generators.ts +4 -4
- package/src/daemon/config-watcher.ts +7 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +225 -88
- package/src/daemon/conversation-agent-loop.ts +284 -584
- package/src/daemon/conversation-error.ts +7 -7
- package/src/daemon/conversation-history.ts +22 -6
- package/src/daemon/conversation-launch.ts +4 -8
- package/src/daemon/conversation-lifecycle.ts +10 -38
- package/src/daemon/conversation-messaging.ts +1 -3
- package/src/daemon/conversation-notifiers.ts +7 -5
- package/src/daemon/conversation-process.ts +100 -79
- package/src/daemon/conversation-runtime-assembly.ts +47 -21
- package/src/daemon/conversation-store.ts +6 -5
- package/src/daemon/conversation-surfaces.ts +55 -69
- package/src/daemon/conversation-tool-setup.ts +3 -0
- package/src/daemon/conversation.ts +91 -126
- package/src/daemon/daemon-skill-host.ts +2 -6
- package/src/daemon/disk-pressure-guard.ts +35 -29
- package/src/daemon/external-plugins-bootstrap.ts +46 -24
- package/src/daemon/first-greeting.ts +26 -4
- package/src/daemon/guardian-action-generators.ts +2 -2
- package/src/daemon/handlers/conversations.ts +6 -22
- package/src/daemon/handlers/shared.ts +4 -0
- package/src/daemon/handlers/skills.ts +15 -14
- package/src/daemon/host-app-control-proxy.ts +54 -1
- package/src/daemon/host-cu-proxy.ts +46 -22
- package/src/daemon/host-file-proxy.ts +25 -1
- package/src/daemon/host-proxy-preactivation.ts +25 -6
- package/src/daemon/lifecycle.ts +28 -55
- package/src/daemon/message-protocol.ts +2 -3
- package/src/daemon/message-provenance.ts +49 -0
- package/src/daemon/message-types/contacts.ts +3 -20
- package/src/daemon/message-types/conversations.ts +13 -111
- package/src/daemon/message-types/documents.ts +3 -9
- package/src/daemon/message-types/home.ts +4 -17
- package/src/daemon/message-types/integrations.ts +2 -6
- package/src/daemon/message-types/messages.ts +28 -343
- package/src/daemon/message-types/notifications.ts +2 -32
- package/src/daemon/message-types/settings.ts +3 -8
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/message-types/sync.ts +12 -25
- package/src/daemon/message-types/workspace.ts +3 -11
- package/src/daemon/process-message.ts +49 -46
- package/src/daemon/server.ts +12 -0
- package/src/daemon/tool-side-effects.ts +10 -7
- package/src/daemon/trust-context.ts +13 -0
- package/src/daemon/wake-target-adapter.ts +11 -1
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -1
- package/src/heartbeat/heartbeat-run-store.ts +31 -0
- package/src/heartbeat/heartbeat-service.ts +16 -0
- package/src/home/feature-gate.ts +22 -0
- package/src/home/feed-types.ts +36 -221
- package/src/ipc/__tests__/email-ipc.test.ts +0 -9
- package/src/ipc/routes/__tests__/route-adapter.test.ts +244 -0
- package/src/ipc/routes/route-adapter.ts +45 -6
- package/src/ipc/skill-routes/__tests__/memory.test.ts +18 -9
- package/src/ipc/skill-routes/__tests__/providers.test.ts +10 -10
- package/src/ipc/skill-routes/__tests__/registries.test.ts +28 -18
- package/src/ipc/skill-routes/memory.ts +26 -13
- package/src/ipc/skill-routes/providers.ts +5 -6
- package/src/ipc/skill-routes/registries.ts +13 -61
- package/src/live-voice/__tests__/live-voice-archive.test.ts +24 -11
- package/src/memory/__tests__/conversation-queries.test.ts +192 -8
- package/src/memory/__tests__/db-maintenance.test.ts +128 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +5 -4
- package/src/memory/__tests__/memory-retrospective-job.test.ts +10 -6
- package/src/memory/__tests__/memory-v3-selections-migration.test.ts +103 -0
- package/src/memory/context-search/agent-runner.ts +2 -4
- package/src/memory/conversation-crud.ts +39 -8
- package/src/memory/conversation-queries.ts +78 -22
- package/src/memory/db-init.ts +8 -0
- package/src/memory/db-maintenance.ts +18 -2
- package/src/memory/graph/consolidation.ts +8 -11
- package/src/memory/graph/conversation-graph-memory.ts +41 -8
- package/src/memory/graph/extraction.ts +6 -9
- package/src/memory/graph/narrative.ts +2 -2
- package/src/memory/graph/pattern-scan.ts +2 -2
- package/src/memory/graph/retriever.ts +20 -26
- package/src/memory/graph/tools.ts +4 -4
- package/src/memory/job-handlers/conversation-starters.ts +32 -32
- package/src/memory/job-handlers/summarization.ts +1 -2
- package/src/memory/jobs-store.ts +3 -1
- package/src/memory/jobs-worker.ts +51 -39
- package/src/memory/llm-request-log-source-clickhouse.ts +5 -31
- package/src/memory/llm-request-log-source-local.ts +0 -11
- package/src/memory/llm-request-log-source.ts +9 -25
- package/src/memory/llm-request-log-store.ts +0 -41
- package/src/memory/llm-usage-store.ts +10 -0
- package/src/memory/memory-marker.ts +17 -0
- package/src/memory/memory-retrospective-job.ts +6 -2
- package/src/memory/memory-v2-activation-log-store.ts +1 -83
- package/src/memory/migrations/267-llm-usage-events-add-assistant-version.ts +46 -0
- package/src/memory/migrations/268-add-memory-v3-selections.ts +28 -0
- package/src/memory/migrations/269-schedule-script-timeout.ts +11 -0
- package/src/memory/migrations/270-messages-role-created-at-index.ts +18 -0
- package/src/memory/migrations/__tests__/267-llm-usage-events-add-assistant-version.test.ts +117 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/schema/infrastructure.ts +11 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +124 -0
- package/src/memory/v2/__tests__/migration.test.ts +11 -3
- package/src/memory/v2/__tests__/page-index.test.ts +37 -1
- package/src/memory/v2/__tests__/router.test.ts +14 -4
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -5
- package/src/memory/v2/backfill-jobs.ts +6 -0
- package/src/memory/v2/consolidation-job.ts +89 -9
- package/src/memory/v2/migration.ts +5 -3
- package/src/memory/v2/page-index.ts +11 -0
- package/src/memory/v2/router.ts +8 -11
- package/src/memory/v2/sweep-job.ts +8 -11
- package/src/memory/v2/types.ts +1 -0
- package/src/memory/v3/__tests__/assign.test.ts +242 -0
- package/src/memory/v3/__tests__/capabilities.test.ts +118 -0
- package/src/memory/v3/__tests__/core.test.ts +39 -0
- package/src/memory/v3/__tests__/fixtures/eval-turns.json +36 -0
- package/src/memory/v3/__tests__/fixtures/live-turns.json +37 -0
- package/src/memory/v3/__tests__/health.test.ts +203 -0
- package/src/memory/v3/__tests__/live-integration.test.ts +330 -0
- package/src/memory/v3/__tests__/maintain-job.test.ts +288 -0
- package/src/memory/v3/__tests__/needle.test.ts +107 -0
- package/src/memory/v3/__tests__/orchestrate.test.ts +400 -0
- package/src/memory/v3/__tests__/reconcile.test.ts +274 -0
- package/src/memory/v3/__tests__/render-injection.test.ts +61 -0
- package/src/memory/v3/__tests__/router.test.ts +260 -0
- package/src/memory/v3/__tests__/selection-log-store.test.ts +179 -0
- package/src/memory/v3/__tests__/selector.test.ts +404 -0
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +414 -0
- package/src/memory/v3/__tests__/snapshot.test.ts +168 -0
- package/src/memory/v3/__tests__/tree.test.ts +192 -0
- package/src/memory/v3/__tests__/types.test.ts +54 -0
- package/src/memory/v3/__tests__/working-set-eviction.test.ts +106 -0
- package/src/memory/v3/__tests__/working-set-skeleton.test.ts +44 -0
- package/src/memory/v3/assign.ts +268 -0
- package/src/memory/v3/capabilities.ts +124 -0
- package/src/memory/v3/core.ts +26 -0
- package/src/memory/v3/data/README.md +84 -0
- package/src/memory/v3/data/assignments.json +5 -0
- package/src/memory/v3/data/core.json +1 -0
- package/src/memory/v3/data/leaves/domain-a/topic-x.md +9 -0
- package/src/memory/v3/data/leaves/domain-a/topic-y.md +9 -0
- package/src/memory/v3/data/leaves/domain-b/topic-z.md +9 -0
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/maintain-job.ts +314 -0
- package/src/memory/v3/needle.ts +115 -0
- package/src/memory/v3/orchestrate.ts +114 -0
- package/src/memory/v3/page-content.ts +34 -0
- package/src/memory/v3/provider-blocks.ts +16 -0
- package/src/memory/v3/reconcile.ts +523 -0
- package/src/memory/v3/render-injection.ts +32 -0
- package/src/memory/v3/router.ts +184 -0
- package/src/memory/v3/selection-log-store.ts +84 -0
- package/src/memory/v3/selector.ts +211 -0
- package/src/memory/v3/shadow-plugin.ts +379 -0
- package/src/memory/v3/snapshot.ts +209 -0
- package/src/memory/v3/tree.ts +174 -0
- package/src/memory/v3/types.ts +46 -60
- package/src/memory/v3/working-set.ts +88 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +1 -1
- package/src/messaging/providers/slack/render-transcript.ts +2 -2
- package/src/messaging/style-analyzer.ts +8 -11
- package/src/notifications/conversation-pairing.ts +8 -6
- package/src/notifications/decision-engine.ts +10 -13
- package/src/notifications/preference-extractor.ts +11 -14
- package/src/permissions/prompter.ts +42 -36
- package/src/permissions/question-prompter.test.ts +35 -26
- package/src/permissions/question-prompter.ts +6 -10
- package/src/plugin-api/index.ts +2 -0
- package/src/plugin-api/types.ts +25 -3
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +93 -0
- package/src/plugins/defaults/circuit-breaker/package.json +15 -0
- package/src/plugins/defaults/circuit-breaker/register.ts +39 -0
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +25 -0
- package/src/plugins/defaults/compaction/package.json +15 -0
- package/src/plugins/defaults/compaction/register.ts +35 -0
- package/src/plugins/defaults/compaction/terminal.ts +73 -0
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +22 -0
- package/src/plugins/defaults/empty-response/package.json +15 -0
- package/src/plugins/defaults/empty-response/register.ts +28 -0
- package/src/plugins/defaults/empty-response/terminal.ts +106 -0
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/history-repair/package.json +15 -0
- package/src/plugins/defaults/history-repair/register.ts +24 -0
- package/src/{daemon/history-repair.ts → plugins/defaults/history-repair/terminal.ts} +48 -35
- package/src/plugins/defaults/index.ts +29 -40
- package/src/plugins/defaults/injectors/package.json +15 -0
- package/src/plugins/defaults/{injectors.ts → injectors/register.ts} +14 -38
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +17 -0
- package/src/plugins/defaults/llm-call/package.json +15 -0
- package/src/plugins/defaults/{llm-call.ts → llm-call/register.ts} +6 -38
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +17 -0
- package/src/plugins/defaults/memory-retrieval/package.json +15 -0
- package/src/plugins/defaults/{memory-retrieval.ts → memory-retrieval/register.ts} +10 -48
- package/src/plugins/defaults/{overflow-reduce.ts → overflow-reduce/middlewares/overflowReduce.ts} +18 -77
- package/src/plugins/defaults/overflow-reduce/package.json +15 -0
- package/src/plugins/defaults/overflow-reduce/register.ts +42 -0
- package/src/plugins/defaults/persistence/middlewares/persistence.ts +19 -0
- package/src/plugins/defaults/persistence/package.json +15 -0
- package/src/plugins/defaults/persistence/register.ts +38 -0
- package/src/plugins/defaults/persistence/terminal.ts +83 -0
- package/src/plugins/defaults/title-generate/package.json +15 -0
- package/src/plugins/defaults/title-generate/register.ts +35 -0
- package/src/plugins/defaults/title-generate/terminal.ts +31 -0
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +23 -0
- package/src/plugins/defaults/token-estimate/package.json +15 -0
- package/src/plugins/defaults/token-estimate/register.ts +34 -0
- package/src/plugins/defaults/token-estimate/terminal.ts +40 -0
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +21 -0
- package/src/plugins/defaults/tool-error/package.json +15 -0
- package/src/plugins/defaults/tool-error/register.ts +35 -0
- package/src/plugins/defaults/tool-error/terminal.ts +47 -0
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +23 -0
- package/src/plugins/defaults/tool-execute/package.json +15 -0
- package/src/plugins/defaults/{tool-execute.ts → tool-execute/register.ts} +8 -46
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +23 -0
- package/src/plugins/defaults/tool-result-truncate/package.json +15 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +35 -0
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +113 -0
- package/src/plugins/defaults/tool-result-truncate/types.ts +22 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/pipeline.ts +0 -12
- package/src/plugins/types.ts +51 -90
- package/src/plugins/user-loader.ts +4 -3
- package/src/proactive-artifact/aux-message-injector.ts +0 -1
- package/src/proactive-artifact/job.test.ts +20 -8
- package/src/proactive-artifact/job.ts +3 -1
- package/src/prompts/sections.ts +20 -7
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +2 -2
- package/src/prompts/templates/BOOTSTRAP.md +5 -1
- package/src/prompts/templates/system-sections.ts +6 -0
- package/src/providers/__tests__/retry-callsite.test.ts +25 -25
- package/src/providers/__tests__/satellite-connection-routing.test.ts +7 -21
- package/src/providers/anthropic/client.ts +24 -5
- package/src/providers/call-site-routing.ts +1 -9
- package/src/providers/gemini/client.ts +152 -34
- package/src/providers/gemini/inline-media.ts +74 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +0 -2
- package/src/providers/openai/chat-completions-provider.ts +1 -4
- package/src/providers/openai/responses-provider.ts +1 -4
- package/src/providers/openrouter/client.ts +1 -6
- package/src/providers/provider-send-message.ts +6 -6
- package/src/providers/ratelimit.ts +1 -9
- package/src/providers/retry.ts +0 -5
- package/src/providers/types.ts +11 -2
- package/src/providers/usage-tracking.ts +1 -9
- package/src/runtime/__tests__/agent-wake.test.ts +131 -26
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -3
- package/src/runtime/agent-wake.ts +93 -18
- package/src/runtime/assistant-event-hub.ts +2 -2
- package/src/runtime/auth/__tests__/guard-tests.test.ts +75 -109
- package/src/runtime/auth/__tests__/route-policy.test.ts +153 -170
- package/src/runtime/auth/route-policy.ts +42 -1079
- package/src/runtime/background-job-runner.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -1
- package/src/runtime/channel-approvals.ts +3 -14
- package/src/runtime/channel-invite-transport.ts +5 -6
- package/src/runtime/channel-readiness-service.ts +2 -5
- package/src/runtime/channel-retry-sweep.ts +12 -16
- package/src/runtime/conversation-stream-state.ts +294 -0
- package/src/runtime/http-router.ts +19 -22
- package/src/runtime/http-types.ts +12 -6
- package/src/runtime/invite-instruction-generator.ts +3 -3
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +565 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +4 -4
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +62 -32
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +237 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -22
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +7 -2
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +6 -6
- package/src/runtime/routes/__tests__/stt-routes.test.ts +3 -3
- package/src/runtime/routes/__tests__/suggest-trust-rule-routes.test.ts +5 -2
- package/src/runtime/routes/__tests__/tts-routes.test.ts +3 -3
- package/src/runtime/routes/acp-routes.test.ts +97 -75
- package/src/runtime/routes/acp-routes.ts +29 -6
- package/src/runtime/routes/app-management-routes.ts +97 -24
- package/src/runtime/routes/app-routes.ts +25 -5
- package/src/runtime/routes/approval-routes.ts +16 -4
- package/src/runtime/routes/attachment-routes.ts +25 -1
- package/src/runtime/routes/audio-routes.ts +1 -0
- package/src/runtime/routes/audit-routes.ts +5 -0
- package/src/runtime/routes/auth-routes.ts +5 -0
- package/src/runtime/routes/avatar-routes.ts +238 -59
- package/src/runtime/routes/background-tool-routes.ts +9 -0
- package/src/runtime/routes/background-wake-routes.ts +13 -3
- package/src/runtime/routes/backup-routes.ts +45 -0
- package/src/runtime/routes/bookmark-routes.ts +13 -0
- package/src/runtime/routes/brain-graph-routes.ts +9 -0
- package/src/runtime/routes/browser-routes.ts +5 -0
- package/src/runtime/routes/browser-tabs-routes.ts +5 -0
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/cache-routes.ts +13 -0
- package/src/runtime/routes/call-routes.ts +21 -10
- package/src/runtime/routes/channel-availability-routes.ts +5 -1
- package/src/runtime/routes/channel-readiness-routes.ts +37 -4
- package/src/runtime/routes/channel-route-definitions.ts +21 -0
- package/src/runtime/routes/channel-verification-routes.ts +21 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +9 -2
- package/src/runtime/routes/client-routes.ts +9 -0
- package/src/runtime/routes/consolidation-routes.ts +13 -5
- package/src/runtime/routes/contact-prompt-routes.ts +9 -0
- package/src/runtime/routes/contact-routes.ts +90 -23
- package/src/runtime/routes/content-source-routes.ts +5 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +5 -1
- package/src/runtime/routes/conversation-attention-routes.ts +5 -0
- package/src/runtime/routes/conversation-cli-routes.ts +54 -7
- package/src/runtime/routes/conversation-compaction-routes.ts +54 -25
- package/src/runtime/routes/conversation-list-routes.ts +81 -12
- package/src/runtime/routes/conversation-management-routes.ts +57 -14
- package/src/runtime/routes/conversation-query-routes.ts +88 -41
- package/src/runtime/routes/conversation-routes.ts +74 -19
- package/src/runtime/routes/conversation-starter-routes.ts +22 -13
- package/src/runtime/routes/conversations-import-routes.ts +6 -1
- package/src/runtime/routes/credential-prompt-routes.ts +5 -0
- package/src/runtime/routes/credential-routes.ts +25 -6
- package/src/runtime/routes/debug-bash-routes.ts +5 -0
- package/src/runtime/routes/debug-routes.ts +11 -2
- package/src/runtime/routes/defer-routes.ts +13 -0
- package/src/runtime/routes/diagnostics-routes.ts +37 -46
- package/src/runtime/routes/disk-pressure-routes.ts +17 -31
- package/src/runtime/routes/document-comments-routes.ts +46 -27
- package/src/runtime/routes/documents-routes.ts +21 -10
- package/src/runtime/routes/domain-routes.ts +61 -28
- package/src/runtime/routes/email-routes.ts +33 -0
- package/src/runtime/routes/events-routes.ts +114 -9
- package/src/runtime/routes/filing-routes.ts +9 -4
- package/src/runtime/routes/gateway-log-routes.ts +5 -0
- package/src/runtime/routes/global-search-routes.ts +53 -50
- package/src/runtime/routes/group-routes.ts +21 -5
- package/src/runtime/routes/guardian-action-routes.ts +9 -0
- package/src/runtime/routes/guardian-approval-interception.ts +0 -31
- package/src/runtime/routes/heartbeat-routes.ts +25 -9
- package/src/runtime/routes/home-feed-routes.ts +23 -19
- package/src/runtime/routes/home-state-routes.ts +8 -40
- package/src/runtime/routes/host-app-control-routes.ts +5 -0
- package/src/runtime/routes/host-bash-routes.ts +5 -0
- package/src/runtime/routes/host-browser-routes.ts +13 -0
- package/src/runtime/routes/host-cu-routes.ts +5 -0
- package/src/runtime/routes/host-file-routes.ts +26 -6
- package/src/runtime/routes/host-transfer-routes.ts +13 -2
- package/src/runtime/routes/http-adapter.ts +1 -2
- package/src/runtime/routes/identity-intro-cache.ts +17 -6
- package/src/runtime/routes/identity-routes.ts +12 -2
- package/src/runtime/routes/image-generation-routes.ts +5 -0
- package/src/runtime/routes/inbound-message-handler.ts +15 -11
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +0 -12
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +15 -19
- package/src/runtime/routes/inference-profile-session-routes.ts +13 -3
- package/src/runtime/routes/inference-provider-connection-routes.ts +21 -5
- package/src/runtime/routes/inference-send-routes.ts +11 -11
- package/src/runtime/routes/integrations/a2a.ts +30 -7
- package/src/runtime/routes/integrations/slack/channel.ts +19 -3
- package/src/runtime/routes/integrations/slack/share.ts +9 -2
- package/src/runtime/routes/integrations/telegram.ts +28 -9
- package/src/runtime/routes/integrations/twilio.ts +35 -7
- package/src/runtime/routes/integrations/vercel.ts +3 -3
- package/src/runtime/routes/internal-oauth-routes.ts +5 -0
- package/src/runtime/routes/internal-twilio-routes.ts +13 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +39 -4
- package/src/runtime/routes/log-export-routes.ts +28 -10
- package/src/runtime/routes/mcp-auth-routes.ts +25 -0
- package/src/runtime/routes/memory-item-routes.ts +21 -10
- package/src/runtime/routes/memory-v2-routes.ts +90 -36
- package/src/runtime/routes/memory-v3-routes.ts +273 -407
- package/src/runtime/routes/migration-rollback-routes.ts +5 -1
- package/src/runtime/routes/migration-routes.ts +29 -0
- package/src/runtime/routes/notification-routes.ts +17 -1
- package/src/runtime/routes/oauth-apps.ts +33 -11
- package/src/runtime/routes/oauth-commands-routes.ts +37 -14
- package/src/runtime/routes/oauth-connect-routes.ts +9 -0
- package/src/runtime/routes/oauth-lifecycle-routes.ts +5 -1
- package/src/runtime/routes/oauth-providers.ts +35 -10
- package/src/runtime/routes/platform-routes.ts +21 -0
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +3 -2
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +37 -16
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +7 -3
- package/src/runtime/routes/playground/__tests__/state.test.ts +10 -3
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +0 -1
- package/src/runtime/routes/playground/inject-failures.ts +13 -8
- package/src/runtime/routes/playground/reset-circuit.ts +14 -9
- package/src/runtime/routes/playground/seed-conversation.ts +1 -1
- package/src/runtime/routes/playground/seeded-conversations.ts +3 -3
- package/src/runtime/routes/playground/state.ts +4 -3
- package/src/runtime/routes/plugins-routes.ts +22 -19
- package/src/runtime/routes/profiler-routes.ts +17 -4
- package/src/runtime/routes/ps-routes.ts +5 -0
- package/src/runtime/routes/publish-routes.ts +13 -3
- package/src/runtime/routes/question-routes.ts +5 -0
- package/src/runtime/routes/recording-routes.ts +25 -12
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/sanity-routes.ts +9 -2
- package/src/runtime/routes/schedule-routes.ts +137 -47
- package/src/runtime/routes/secret-routes.ts +17 -4
- package/src/runtime/routes/sequence-routes.ts +33 -0
- package/src/runtime/routes/settings-routes.ts +65 -19
- package/src/runtime/routes/skills-routes.ts +133 -69
- package/src/runtime/routes/slack-channel-routes.ts +5 -0
- package/src/runtime/routes/stt-routes.ts +13 -6
- package/src/runtime/routes/subagents-routes.ts +24 -18
- package/src/runtime/routes/suggest-trust-rule-routes.ts +7 -2
- package/src/runtime/routes/surface-action-routes.ts +9 -0
- package/src/runtime/routes/surface-content-routes.ts +10 -2
- package/src/runtime/routes/task-routes.ts +37 -0
- package/src/runtime/routes/telemetry-routes.ts +9 -0
- package/src/runtime/routes/trace-event-routes.ts +42 -1
- package/src/runtime/routes/trust-rules-routes.ts +5 -0
- package/src/runtime/routes/tts-routes.ts +13 -6
- package/src/runtime/routes/types.ts +17 -8
- package/src/runtime/routes/ui-request-routes.ts +5 -0
- package/src/runtime/routes/upgrade-broadcast-routes.ts +5 -0
- package/src/runtime/routes/usage-routes.ts +71 -3
- package/src/runtime/routes/user-routes-cli.ts +9 -0
- package/src/runtime/routes/user-routes.ts +5 -1
- package/src/runtime/routes/wake-conversation-routes.ts +5 -0
- package/src/runtime/routes/watcher-routes.ts +21 -0
- package/src/runtime/routes/webhook-routes.ts +9 -0
- package/src/runtime/routes/wipe-conversation-routes.ts +5 -0
- package/src/runtime/routes/work-items-routes.ts +47 -19
- package/src/runtime/routes/workspace-commit-routes.ts +5 -0
- package/src/runtime/routes/workspace-routes.test.ts +42 -0
- package/src/runtime/routes/workspace-routes.ts +120 -9
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -4
- package/src/runtime/services/analyze-conversation.ts +3 -6
- package/src/runtime/services/conversation-serializer.ts +24 -2
- package/src/runtime/sync/resource-sync-events.ts +16 -2
- package/src/runtime/sync/sync-publisher.ts +2 -2
- package/src/schedule/run-script.ts +28 -3
- package/src/schedule/schedule-store.ts +8 -0
- package/src/schedule/scheduler.ts +3 -1
- package/src/signals/user-message.ts +5 -8
- package/src/skills/catalog-files.ts +4 -1
- package/src/skills/clawhub-files.ts +2 -0
- package/src/skills/skillssh-files.ts +2 -0
- package/src/subagent/manager.ts +3 -6
- package/src/telemetry/types.ts +26 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +138 -1
- package/src/telemetry/usage-telemetry-reporter.ts +31 -0
- package/src/tools/acp/spawn.test.ts +88 -38
- package/src/tools/apps/definitions.ts +8 -4
- package/src/tools/ask-question/ask-question-tool.test.ts +120 -105
- package/src/tools/ask-question/ask-question-tool.ts +85 -90
- package/src/tools/computer-use/definitions.ts +28 -24
- package/src/tools/credential-execution/make-authenticated-request.ts +56 -51
- package/src/tools/credential-execution/manage-secure-command-tool.ts +2 -2
- package/src/tools/credential-execution/run-authenticated-command.ts +82 -77
- package/src/tools/credentials/vault.ts +112 -111
- package/src/tools/execution-target.ts +1 -1
- package/src/tools/execution-timeout.ts +3 -4
- package/src/tools/filesystem/edit.ts +45 -42
- package/src/tools/filesystem/list.ts +33 -30
- package/src/tools/filesystem/read.ts +54 -35
- package/src/tools/filesystem/write.ts +34 -31
- package/src/tools/host-filesystem/edit.ts +44 -42
- package/src/tools/host-filesystem/read.ts +49 -35
- package/src/tools/host-filesystem/transfer.ts +121 -108
- package/src/tools/host-filesystem/write.ts +33 -31
- package/src/tools/host-terminal/host-shell.ts +50 -48
- package/src/tools/memory/register.ts +23 -24
- package/src/tools/network/web-fetch.ts +49 -46
- package/src/tools/network/web-search.ts +16 -13
- package/src/tools/registry.ts +39 -16
- package/src/tools/schedule/create.ts +11 -0
- package/src/tools/schedule/update.ts +16 -0
- package/src/tools/shared/filesystem/audio-read.ts +122 -0
- package/src/tools/shared/filesystem/image-read.ts +1 -1
- package/src/tools/skills/execute.ts +34 -31
- package/src/tools/skills/load.ts +29 -23
- package/src/tools/subagent/notify-parent.ts +35 -32
- package/src/tools/system/avatar-generator.ts +13 -22
- package/src/tools/system/request-permission.ts +30 -27
- package/src/tools/terminal/shell.ts +190 -61
- package/src/tools/tool-defaults.ts +20 -9
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/types.ts +74 -23
- package/src/tools/ui-surface/definitions.ts +69 -9
- package/src/usage/types.ts +10 -0
- package/src/util/errors.ts +2 -2
- package/src/util/map-limit.ts +27 -0
- package/src/util/platform.ts +15 -12
- package/src/work-items/work-item-runner.ts +7 -2
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +7 -20
- package/src/workspace/migrations/092-backfill-v3-leaves.ts +169 -0
- package/src/workspace/migrations/093-backfill-leaf-ids.ts +144 -0
- package/src/workspace/migrations/094-seed-avatar-manifest.ts +155 -0
- package/src/workspace/migrations/__tests__/094-seed-avatar-manifest.test.ts +136 -0
- package/src/workspace/migrations/__tests__/backfill-leaf-ids.test.ts +175 -0
- package/src/workspace/migrations/__tests__/backfill-v3-leaves.test.ts +124 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/provider-commit-message-generator.ts +15 -17
- package/tsconfig.json +4 -1
- package/src/__tests__/history-repair-pipeline.test.ts +0 -396
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +0 -340
- package/src/cli/commands/memory-v3-render.ts +0 -491
- package/src/daemon/message-types/disk-pressure.ts +0 -9
- package/src/email/feature-gate.ts +0 -23
- package/src/memory/v3/__tests__/coactivation-store.test.ts +0 -422
- package/src/memory/v3/__tests__/consolidation-job.test.ts +0 -466
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +0 -270
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +0 -324
- package/src/memory/v3/__tests__/edges.test.ts +0 -706
- package/src/memory/v3/__tests__/filter.test.ts +0 -560
- package/src/memory/v3/__tests__/gate.test.ts +0 -637
- package/src/memory/v3/__tests__/index-composition.test.ts +0 -291
- package/src/memory/v3/__tests__/loop.test.ts +0 -775
- package/src/memory/v3/__tests__/retriever.test.ts +0 -226
- package/src/memory/v3/__tests__/scouts.test.ts +0 -489
- package/src/memory/v3/__tests__/shadow-diff.test.ts +0 -225
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +0 -398
- package/src/memory/v3/__tests__/system-prompts.test.ts +0 -154
- package/src/memory/v3/__tests__/traversal.test.ts +0 -508
- package/src/memory/v3/__tests__/tree-index.test.ts +0 -280
- package/src/memory/v3/__tests__/tree-store.test.ts +0 -529
- package/src/memory/v3/__tests__/tree-walk.test.ts +0 -784
- package/src/memory/v3/__tests__/validate.test.ts +0 -277
- package/src/memory/v3/auto-edges.ts +0 -223
- package/src/memory/v3/coactivation-store.ts +0 -124
- package/src/memory/v3/consolidation-job.ts +0 -323
- package/src/memory/v3/coretrieval-seed.ts +0 -240
- package/src/memory/v3/edge-learning-job.ts +0 -160
- package/src/memory/v3/edges.ts +0 -286
- package/src/memory/v3/filter.ts +0 -286
- package/src/memory/v3/gate.ts +0 -349
- package/src/memory/v3/index-composition.ts +0 -126
- package/src/memory/v3/llm-capture.ts +0 -46
- package/src/memory/v3/loop.ts +0 -430
- package/src/memory/v3/maintenance.ts +0 -144
- package/src/memory/v3/prompt-context.ts +0 -33
- package/src/memory/v3/prompts/consolidation.ts +0 -458
- package/src/memory/v3/prompts/system-prompts.ts +0 -196
- package/src/memory/v3/retriever.ts +0 -33
- package/src/memory/v3/scouts.ts +0 -431
- package/src/memory/v3/shadow-diff.ts +0 -287
- package/src/memory/v3/shadow-middleware.ts +0 -347
- package/src/memory/v3/traversal.ts +0 -211
- package/src/memory/v3/tree-index.ts +0 -237
- package/src/memory/v3/tree-store.ts +0 -394
- package/src/memory/v3/tree-walk.ts +0 -356
- package/src/memory/v3/validate.ts +0 -323
- package/src/plugins/defaults/circuit-breaker.ts +0 -141
- package/src/plugins/defaults/compaction.ts +0 -141
- package/src/plugins/defaults/empty-response.ts +0 -124
- package/src/plugins/defaults/history-repair.ts +0 -83
- package/src/plugins/defaults/persistence.ts +0 -146
- package/src/plugins/defaults/title-generate.ts +0 -90
- package/src/plugins/defaults/token-estimate.ts +0 -101
- package/src/plugins/defaults/tool-error.ts +0 -119
- package/src/plugins/defaults/tool-result-truncate.ts +0 -84
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +0 -35
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
3
3
|
|
|
4
|
+
import { CompactionCircuit } from "../agent/compaction-circuit.js";
|
|
4
5
|
import type {
|
|
5
6
|
AgentEvent,
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
AgentLoopRunOptions,
|
|
8
|
+
AgentLoopRunResult,
|
|
9
|
+
MidLoopCompaction,
|
|
8
10
|
} from "../agent/loop.js";
|
|
11
|
+
import type { ContextWindowResult } from "../context/window-manager.js";
|
|
9
12
|
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
13
|
+
import { defaultCompactionTerminal } from "../plugins/defaults/compaction/terminal.js";
|
|
10
14
|
import { resetPluginRegistryAndRegisterDefaults } from "../plugins/defaults/index.js";
|
|
15
|
+
import { DEFAULT_TIMEOUTS, runPipeline } from "../plugins/pipeline.js";
|
|
16
|
+
import { getMiddlewaresFor } from "../plugins/registry.js";
|
|
17
|
+
import type {
|
|
18
|
+
CompactionArgs,
|
|
19
|
+
CompactionResult,
|
|
20
|
+
TurnContext,
|
|
21
|
+
} from "../plugins/types.js";
|
|
22
|
+
import { PluginTimeoutError } from "../plugins/types.js";
|
|
11
23
|
import type { ContentBlock, Message } from "../providers/types.js";
|
|
12
24
|
|
|
13
25
|
const conversationCrudRealSnapshot = {
|
|
@@ -407,7 +419,7 @@ mock.module("../daemon/date-context.js", () => ({
|
|
|
407
419
|
resolveTurnTimezoneContext: resolveTurnTimezoneContextMock,
|
|
408
420
|
}));
|
|
409
421
|
|
|
410
|
-
mock.module("../
|
|
422
|
+
mock.module("../plugins/defaults/history-repair/terminal.js", () => ({
|
|
411
423
|
repairHistory: (msgs: Message[]) => ({
|
|
412
424
|
messages: msgs,
|
|
413
425
|
stats: {
|
|
@@ -548,13 +560,149 @@ import {
|
|
|
548
560
|
type AgentLoopRun = (
|
|
549
561
|
messages: Message[],
|
|
550
562
|
onEvent: (event: AgentEvent) => void | Promise<void>,
|
|
551
|
-
|
|
552
|
-
requestId?: string,
|
|
553
|
-
onCheckpoint?: (
|
|
554
|
-
checkpoint: CheckpointInfo,
|
|
555
|
-
) => CheckpointDecision | Promise<CheckpointDecision>,
|
|
563
|
+
options?: AgentLoopRunOptions,
|
|
556
564
|
) => Promise<Message[]>;
|
|
557
565
|
|
|
566
|
+
/**
|
|
567
|
+
* Faithful re-implementation of `AgentLoop.compact()` for the mock loop: run
|
|
568
|
+
* the compaction pipeline against the supplied turn context (which carries the
|
|
569
|
+
* test's `contextWindowManager`), invoke the orchestrator-supplied hooks, and
|
|
570
|
+
* return the continuation history — or `null` on timeout/exhaustion so the
|
|
571
|
+
* caller yields "budget".
|
|
572
|
+
*/
|
|
573
|
+
async function simulateInlineCompaction(
|
|
574
|
+
compaction: MidLoopCompaction,
|
|
575
|
+
history: Message[],
|
|
576
|
+
turnContext: TurnContext | undefined,
|
|
577
|
+
signal: AbortSignal | undefined,
|
|
578
|
+
onEvent: (event: AgentEvent) => void | Promise<void>,
|
|
579
|
+
compactionCircuit: CompactionCircuit,
|
|
580
|
+
): Promise<Message[] | null> {
|
|
581
|
+
await onEvent({ type: "context_compacting" });
|
|
582
|
+
const { rawHistory, options } = compaction.prepare(history);
|
|
583
|
+
let result: CompactionResult;
|
|
584
|
+
try {
|
|
585
|
+
result = await runPipeline<CompactionArgs, CompactionResult>(
|
|
586
|
+
"compaction",
|
|
587
|
+
getMiddlewaresFor("compaction"),
|
|
588
|
+
(args) => defaultCompactionTerminal(args, turnContext as TurnContext),
|
|
589
|
+
{ messages: rawHistory, signal, options },
|
|
590
|
+
turnContext as TurnContext,
|
|
591
|
+
DEFAULT_TIMEOUTS.compaction,
|
|
592
|
+
);
|
|
593
|
+
} catch (error) {
|
|
594
|
+
if (error instanceof PluginTimeoutError) {
|
|
595
|
+
await compactionCircuit.recordOutcome(
|
|
596
|
+
{
|
|
597
|
+
currentRequestId: turnContext?.requestId,
|
|
598
|
+
currentTurnTrustContext: turnContext?.trust,
|
|
599
|
+
turnCount: turnContext?.turnIndex ?? 0,
|
|
600
|
+
},
|
|
601
|
+
true,
|
|
602
|
+
onEvent,
|
|
603
|
+
);
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
throw error;
|
|
607
|
+
}
|
|
608
|
+
const compactResult = result as ContextWindowResult;
|
|
609
|
+
if (compactResult.summaryFailed !== undefined) {
|
|
610
|
+
await compactionCircuit.recordOutcome(
|
|
611
|
+
{
|
|
612
|
+
currentRequestId: turnContext?.requestId,
|
|
613
|
+
currentTurnTrustContext: turnContext?.trust,
|
|
614
|
+
turnCount: turnContext?.turnIndex ?? 0,
|
|
615
|
+
},
|
|
616
|
+
compactResult.summaryFailed,
|
|
617
|
+
onEvent,
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
if (compactResult.compacted) {
|
|
621
|
+
await compaction.applyResult(compactResult, rawHistory);
|
|
622
|
+
}
|
|
623
|
+
if (compactResult.exhausted ?? false) {
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
626
|
+
return compaction.reinject();
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Adapt a `Message[]`-returning mock loop body into `run()`'s real result
|
|
631
|
+
* shape. Mirrors the production loop: the pause-reason carried back is
|
|
632
|
+
* whatever the most recent `onCheckpoint` call yielded with (null when it
|
|
633
|
+
* never yielded), so the orchestrator derives its yield bookkeeping the same
|
|
634
|
+
* way it does against the real loop.
|
|
635
|
+
*/
|
|
636
|
+
const asAgentLoopRun = (
|
|
637
|
+
fn: AgentLoopRun,
|
|
638
|
+
compactionCircuit: CompactionCircuit,
|
|
639
|
+
): ((
|
|
640
|
+
messages: Message[],
|
|
641
|
+
onEvent: (event: AgentEvent) => void | Promise<void>,
|
|
642
|
+
options?: AgentLoopRunOptions,
|
|
643
|
+
) => Promise<AgentLoopRunResult>) => {
|
|
644
|
+
return async (messages, onEvent, options) => {
|
|
645
|
+
let exitReason: AgentLoopRunResult["exitReason"] = null;
|
|
646
|
+
let wrapped = options;
|
|
647
|
+
if (options?.onCheckpoint) {
|
|
648
|
+
const inner = options.onCheckpoint;
|
|
649
|
+
wrapped = {
|
|
650
|
+
...options,
|
|
651
|
+
onCheckpoint: async (info) => {
|
|
652
|
+
// Handoff is offered first, mirroring the loop's ordering.
|
|
653
|
+
const decision = await inner(info);
|
|
654
|
+
if (decision !== "continue") {
|
|
655
|
+
exitReason = decision;
|
|
656
|
+
return decision;
|
|
657
|
+
}
|
|
658
|
+
// The mid-loop budget gate and inline compaction both live inside
|
|
659
|
+
// `AgentLoop.run`. Replicate them here — same formula, stubbed
|
|
660
|
+
// estimator, and the loop's own `compact()` ceremony — so these
|
|
661
|
+
// orchestrator tests drive the real escalation path now that the
|
|
662
|
+
// orchestrator's `onCheckpoint` is handoff-only and compaction runs
|
|
663
|
+
// inline rather than via an orchestrator re-entry loop.
|
|
664
|
+
const contextWindow = options.resolveContextWindow?.();
|
|
665
|
+
if (contextWindow?.overflowRecovery.enabled) {
|
|
666
|
+
const { maxInputTokens, overflowRecovery } = contextWindow;
|
|
667
|
+
const safetyMargin =
|
|
668
|
+
info.history.length > 50
|
|
669
|
+
? Math.max(overflowRecovery.safetyMarginRatio, 0.15)
|
|
670
|
+
: overflowRecovery.safetyMarginRatio;
|
|
671
|
+
const preflightBudget = Math.floor(
|
|
672
|
+
maxInputTokens * (1 - safetyMargin),
|
|
673
|
+
);
|
|
674
|
+
if (mockEstimateTokens > preflightBudget * 0.85) {
|
|
675
|
+
// Mirror `AgentLoop.compact()`: when a compaction path is
|
|
676
|
+
// supplied, run it in place and continue; on timeout or
|
|
677
|
+
// exhaustion it returns null, so the loop yields "budget".
|
|
678
|
+
const compacted = options.compaction
|
|
679
|
+
? await simulateInlineCompaction(
|
|
680
|
+
options.compaction,
|
|
681
|
+
info.history,
|
|
682
|
+
options.turnContext,
|
|
683
|
+
options.signal,
|
|
684
|
+
onEvent,
|
|
685
|
+
compactionCircuit,
|
|
686
|
+
)
|
|
687
|
+
: null;
|
|
688
|
+
if (compacted) {
|
|
689
|
+
exitReason = null;
|
|
690
|
+
return "continue";
|
|
691
|
+
}
|
|
692
|
+
exitReason = "budget";
|
|
693
|
+
return "budget";
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
exitReason = null;
|
|
697
|
+
return "continue";
|
|
698
|
+
},
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
const history = await fn(messages, onEvent, wrapped);
|
|
702
|
+
return { history, exitReason };
|
|
703
|
+
};
|
|
704
|
+
};
|
|
705
|
+
|
|
558
706
|
function makeCtx(
|
|
559
707
|
overrides?: Partial<AgentLoopConversationContext> & {
|
|
560
708
|
agentLoopRun?: AgentLoopRun;
|
|
@@ -570,6 +718,8 @@ function makeCtx(
|
|
|
570
718
|
},
|
|
571
719
|
]);
|
|
572
720
|
|
|
721
|
+
const compactionCircuit = new CompactionCircuit("test-conv");
|
|
722
|
+
|
|
573
723
|
return {
|
|
574
724
|
conversationId: "test-conv",
|
|
575
725
|
messages: [
|
|
@@ -580,12 +730,13 @@ function makeCtx(
|
|
|
580
730
|
currentRequestId: "test-req",
|
|
581
731
|
|
|
582
732
|
agentLoop: {
|
|
583
|
-
run: agentLoopRun,
|
|
733
|
+
run: asAgentLoopRun(agentLoopRun, compactionCircuit),
|
|
584
734
|
getToolTokenBudget: () => 0,
|
|
585
735
|
getResolvedTools: () => [],
|
|
586
736
|
// Tests here don't exercise calibration; returning undefined makes
|
|
587
737
|
// the estimator use the per-provider aggregate key.
|
|
588
738
|
getActiveModel: () => undefined,
|
|
739
|
+
compactionCircuit,
|
|
589
740
|
} as unknown as AgentLoopConversationContext["agentLoop"],
|
|
590
741
|
provider: {
|
|
591
742
|
name: "mock-provider",
|
|
@@ -805,7 +956,7 @@ describe("session-agent-loop", () => {
|
|
|
805
956
|
});
|
|
806
957
|
|
|
807
958
|
describe("proactive artifact trigger", () => {
|
|
808
|
-
test("
|
|
959
|
+
test("does not start proactive artifact jobs after foreground user turns", async () => {
|
|
809
960
|
mockConversationRow = {
|
|
810
961
|
...mockConversationRow,
|
|
811
962
|
id: "test-conv",
|
|
@@ -819,13 +970,7 @@ describe("session-agent-loop", () => {
|
|
|
819
970
|
mockHasProactiveArtifactCompleted = false;
|
|
820
971
|
mockTryClaimProactiveArtifactTrigger = true;
|
|
821
972
|
|
|
822
|
-
const agentLoopRun: AgentLoopRun = async (
|
|
823
|
-
messages,
|
|
824
|
-
onEvent,
|
|
825
|
-
_signal,
|
|
826
|
-
_requestId,
|
|
827
|
-
onCheckpoint,
|
|
828
|
-
) => {
|
|
973
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent, options) => {
|
|
829
974
|
// Prime the assistant row anchor for LLM call 1 — production code
|
|
830
975
|
// emits this from `AgentLoop.run` just before `provider.sendMessage`.
|
|
831
976
|
await onEvent({ type: "llm_call_started" });
|
|
@@ -848,7 +993,7 @@ describe("session-agent-loop", () => {
|
|
|
848
993
|
content: "{}",
|
|
849
994
|
isError: false,
|
|
850
995
|
});
|
|
851
|
-
await onCheckpoint?.({
|
|
996
|
+
await options?.onCheckpoint?.({
|
|
852
997
|
turnIndex: 0,
|
|
853
998
|
toolCount: 1,
|
|
854
999
|
hasToolUse: true,
|
|
@@ -888,11 +1033,7 @@ describe("session-agent-loop", () => {
|
|
|
888
1033
|
);
|
|
889
1034
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
890
1035
|
|
|
891
|
-
expect(runProactiveArtifactJobMock).toHaveBeenCalledTimes(
|
|
892
|
-
expect(runProactiveArtifactJobMock.mock.calls[0]?.[0]).toMatchObject({
|
|
893
|
-
conversationId: "test-conv",
|
|
894
|
-
suppressAppBuild: true,
|
|
895
|
-
});
|
|
1036
|
+
expect(runProactiveArtifactJobMock).toHaveBeenCalledTimes(0);
|
|
896
1037
|
});
|
|
897
1038
|
});
|
|
898
1039
|
|
|
@@ -1030,7 +1171,10 @@ describe("session-agent-loop", () => {
|
|
|
1030
1171
|
},
|
|
1031
1172
|
} as unknown as AgentLoopConversationContext["traceEmitter"],
|
|
1032
1173
|
});
|
|
1033
|
-
ctx.agentLoop.run =
|
|
1174
|
+
ctx.agentLoop.run = asAgentLoopRun(
|
|
1175
|
+
agentLoopRun,
|
|
1176
|
+
ctx.agentLoop.compactionCircuit,
|
|
1177
|
+
);
|
|
1034
1178
|
|
|
1035
1179
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
1036
1180
|
|
|
@@ -1039,8 +1183,7 @@ describe("session-agent-loop", () => {
|
|
|
1039
1183
|
expect(activityStates).toContainEqual([
|
|
1040
1184
|
"idle",
|
|
1041
1185
|
"error_terminal",
|
|
1042
|
-
"global",
|
|
1043
|
-
"test-req",
|
|
1186
|
+
{ anchor: "global", requestId: "test-req" },
|
|
1044
1187
|
]);
|
|
1045
1188
|
expect(traceEvents[0]).toEqual([
|
|
1046
1189
|
"request_error",
|
|
@@ -1102,8 +1245,7 @@ describe("session-agent-loop", () => {
|
|
|
1102
1245
|
expect(activityStates).toContainEqual([
|
|
1103
1246
|
"idle",
|
|
1104
1247
|
"error_terminal",
|
|
1105
|
-
"global",
|
|
1106
|
-
"test-req",
|
|
1248
|
+
{ anchor: "global", requestId: "test-req" },
|
|
1107
1249
|
]);
|
|
1108
1250
|
});
|
|
1109
1251
|
});
|
|
@@ -2150,13 +2292,7 @@ describe("session-agent-loop", () => {
|
|
|
2150
2292
|
// call). 90k satisfies both so the path reaches call 3.
|
|
2151
2293
|
mockEstimateTokens = 90_000;
|
|
2152
2294
|
|
|
2153
|
-
const agentLoopRun: AgentLoopRun = async (
|
|
2154
|
-
messages,
|
|
2155
|
-
onEvent,
|
|
2156
|
-
_signal,
|
|
2157
|
-
_reqId,
|
|
2158
|
-
onCheckpoint,
|
|
2159
|
-
) => {
|
|
2295
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent, options) => {
|
|
2160
2296
|
callCount++;
|
|
2161
2297
|
if (callCount <= 2) {
|
|
2162
2298
|
// Calls 1 (initial) and 2 (convergence rerun): error so
|
|
@@ -2180,8 +2316,8 @@ describe("session-agent-loop", () => {
|
|
|
2180
2316
|
// flips `yieldedForBudget` to true, then return without
|
|
2181
2317
|
// finishing — mirroring what AgentLoop.run does when its
|
|
2182
2318
|
// checkpoint returns "yield".
|
|
2183
|
-
if (onCheckpoint) {
|
|
2184
|
-
await onCheckpoint({
|
|
2319
|
+
if (options?.onCheckpoint) {
|
|
2320
|
+
await options.onCheckpoint({
|
|
2185
2321
|
turnIndex: 0,
|
|
2186
2322
|
toolCount: 1,
|
|
2187
2323
|
hasToolUse: true,
|
|
@@ -2461,13 +2597,7 @@ describe("session-agent-loop", () => {
|
|
|
2461
2597
|
test("yields at checkpoint when canHandoffAtCheckpoint returns true", async () => {
|
|
2462
2598
|
const events: ServerMessage[] = [];
|
|
2463
2599
|
|
|
2464
|
-
const agentLoopRun: AgentLoopRun = async (
|
|
2465
|
-
messages,
|
|
2466
|
-
onEvent,
|
|
2467
|
-
_signal,
|
|
2468
|
-
_reqId,
|
|
2469
|
-
onCheckpoint,
|
|
2470
|
-
) => {
|
|
2600
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent, options) => {
|
|
2471
2601
|
// Prime the assistant row anchor — production code emits this from
|
|
2472
2602
|
// `AgentLoop.run` just before `provider.sendMessage`. Retry branches
|
|
2473
2603
|
// need this on every invocation: each agent-loop iteration reserves
|
|
@@ -2495,14 +2625,14 @@ describe("session-agent-loop", () => {
|
|
|
2495
2625
|
model: "test-model",
|
|
2496
2626
|
providerDurationMs: 100,
|
|
2497
2627
|
});
|
|
2498
|
-
if (onCheckpoint) {
|
|
2499
|
-
const decision = await onCheckpoint({
|
|
2628
|
+
if (options?.onCheckpoint) {
|
|
2629
|
+
const decision = await options.onCheckpoint({
|
|
2500
2630
|
turnIndex: 0,
|
|
2501
2631
|
toolCount: 1,
|
|
2502
2632
|
hasToolUse: true,
|
|
2503
2633
|
history: messages,
|
|
2504
2634
|
});
|
|
2505
|
-
if (decision
|
|
2635
|
+
if (decision !== "continue") {
|
|
2506
2636
|
return [
|
|
2507
2637
|
...messages,
|
|
2508
2638
|
{
|
|
@@ -2539,13 +2669,7 @@ describe("session-agent-loop", () => {
|
|
|
2539
2669
|
test("continues when canHandoffAtCheckpoint returns false", async () => {
|
|
2540
2670
|
const events: ServerMessage[] = [];
|
|
2541
2671
|
|
|
2542
|
-
const agentLoopRun: AgentLoopRun = async (
|
|
2543
|
-
messages,
|
|
2544
|
-
onEvent,
|
|
2545
|
-
_signal,
|
|
2546
|
-
_reqId,
|
|
2547
|
-
onCheckpoint,
|
|
2548
|
-
) => {
|
|
2672
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent, options) => {
|
|
2549
2673
|
// Prime the assistant row anchor — production code emits this from
|
|
2550
2674
|
// `AgentLoop.run` just before `provider.sendMessage`. Retry branches
|
|
2551
2675
|
// need this on every invocation: each agent-loop iteration reserves
|
|
@@ -2572,8 +2696,8 @@ describe("session-agent-loop", () => {
|
|
|
2572
2696
|
model: "test-model",
|
|
2573
2697
|
providerDurationMs: 100,
|
|
2574
2698
|
});
|
|
2575
|
-
if (onCheckpoint) {
|
|
2576
|
-
await onCheckpoint({
|
|
2699
|
+
if (options?.onCheckpoint) {
|
|
2700
|
+
await options.onCheckpoint({
|
|
2577
2701
|
turnIndex: 0,
|
|
2578
2702
|
toolCount: 1,
|
|
2579
2703
|
hasToolUse: true,
|
|
@@ -3444,6 +3568,400 @@ describe("session-agent-loop", () => {
|
|
|
3444
3568
|
});
|
|
3445
3569
|
});
|
|
3446
3570
|
|
|
3571
|
+
describe("partial persistence", () => {
|
|
3572
|
+
// The legacy flow reserves an empty assistant row at `llm_call_started`
|
|
3573
|
+
// (`content: "[]"`) and never touches it again until
|
|
3574
|
+
// `handleMessageComplete` fires the single authoritative
|
|
3575
|
+
// `updateContent`. Between those events the row is empty for the full
|
|
3576
|
+
// duration of a turn — a browser refresh mid-turn sees nothing where
|
|
3577
|
+
// the in-progress assistant reply should be.
|
|
3578
|
+
//
|
|
3579
|
+
// Partial persistence closes that durability gap with a debounced
|
|
3580
|
+
// flush from `handleTextDelta` (250ms timer). `handleToolUse`
|
|
3581
|
+
// intentionally does NOT flush — `AgentLoop.run` emits `tool_use`
|
|
3582
|
+
// strictly AFTER `message_complete`, so any flush from that handler
|
|
3583
|
+
// would land after the authoritative finalize and overwrite the
|
|
3584
|
+
// finalized row. The indexer + projector still fire ONLY at
|
|
3585
|
+
// `message_complete` — partial rows are never indexed.
|
|
3586
|
+
//
|
|
3587
|
+
// These tests pin down the wire-level contract by counting
|
|
3588
|
+
// `updateMessageContent` calls and inspecting the JSON payload of the
|
|
3589
|
+
// partial-flush writes. The indexing / sync-invalidation paths are
|
|
3590
|
+
// covered by the pre-allocation block above.
|
|
3591
|
+
|
|
3592
|
+
test("debounced time gate flushes one partial write after PARTIAL_PERSIST_DEBOUNCE_MS", async () => {
|
|
3593
|
+
mockMessageById = {
|
|
3594
|
+
id: "msg-reserve",
|
|
3595
|
+
conversationId: "test-conv",
|
|
3596
|
+
createdAt: 1234567,
|
|
3597
|
+
role: "assistant",
|
|
3598
|
+
content: "[]",
|
|
3599
|
+
metadata: null,
|
|
3600
|
+
};
|
|
3601
|
+
|
|
3602
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
3603
|
+
await onEvent({ type: "llm_call_started" });
|
|
3604
|
+
// Two small deltas — well under the 1024-char size gate — should
|
|
3605
|
+
// schedule a single debounced flush.
|
|
3606
|
+
onEvent({ type: "text_delta", text: "Hello, " });
|
|
3607
|
+
onEvent({ type: "text_delta", text: "world." });
|
|
3608
|
+
// Wait long enough for the 250ms debounce to fire.
|
|
3609
|
+
await new Promise((resolve) => setTimeout(resolve, 1100));
|
|
3610
|
+
await onEvent({
|
|
3611
|
+
type: "message_complete",
|
|
3612
|
+
message: {
|
|
3613
|
+
role: "assistant",
|
|
3614
|
+
content: [{ type: "text", text: "Hello, world." }],
|
|
3615
|
+
},
|
|
3616
|
+
});
|
|
3617
|
+
onEvent({
|
|
3618
|
+
type: "usage",
|
|
3619
|
+
inputTokens: 10,
|
|
3620
|
+
outputTokens: 5,
|
|
3621
|
+
model: "test",
|
|
3622
|
+
providerDurationMs: 50,
|
|
3623
|
+
});
|
|
3624
|
+
return [
|
|
3625
|
+
...messages,
|
|
3626
|
+
{
|
|
3627
|
+
role: "assistant" as const,
|
|
3628
|
+
content: [
|
|
3629
|
+
{ type: "text", text: "Hello, world." },
|
|
3630
|
+
] as ContentBlock[],
|
|
3631
|
+
},
|
|
3632
|
+
];
|
|
3633
|
+
};
|
|
3634
|
+
|
|
3635
|
+
const ctx = makeCtx({ agentLoopRun });
|
|
3636
|
+
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
3637
|
+
|
|
3638
|
+
// Exactly two `updateContent` calls land:
|
|
3639
|
+
// 1. the debounced partial flush after both deltas accumulated, and
|
|
3640
|
+
// 2. the final authoritative flush in `handleMessageComplete`.
|
|
3641
|
+
// Without the debounce gate this would be one-per-delta + one final
|
|
3642
|
+
// (3). Without the partial flush at all it would be just 1.
|
|
3643
|
+
expect(updateMessageContentMock).toHaveBeenCalledTimes(2);
|
|
3644
|
+
const calls = updateMessageContentMock.mock.calls as unknown as Array<
|
|
3645
|
+
[string, string]
|
|
3646
|
+
>;
|
|
3647
|
+
const partialFlush = calls[0];
|
|
3648
|
+
expect(partialFlush?.[0]).toBe("msg-reserve");
|
|
3649
|
+
const partialBlocks = JSON.parse(partialFlush?.[1] ?? "[]") as Array<{
|
|
3650
|
+
type: string;
|
|
3651
|
+
text?: string;
|
|
3652
|
+
}>;
|
|
3653
|
+
expect(partialBlocks).toEqual([{ type: "text", text: "Hello, world." }]);
|
|
3654
|
+
});
|
|
3655
|
+
|
|
3656
|
+
test("handleToolUse does NOT trigger a partial flush of its own", async () => {
|
|
3657
|
+
// `AgentLoop.run` emits `tool_use` strictly AFTER `message_complete`,
|
|
3658
|
+
// so a flush from the tool_use handler would land after the
|
|
3659
|
+
// authoritative final `updateContent` and overwrite the finalized
|
|
3660
|
+
// row (Codex P1 / Vargas review feedback). The handler must be a
|
|
3661
|
+
// no-op for the partial-persist accumulator.
|
|
3662
|
+
mockMessageById = {
|
|
3663
|
+
id: "msg-reserve",
|
|
3664
|
+
conversationId: "test-conv",
|
|
3665
|
+
createdAt: 1234567,
|
|
3666
|
+
role: "assistant",
|
|
3667
|
+
content: "[]",
|
|
3668
|
+
metadata: null,
|
|
3669
|
+
};
|
|
3670
|
+
|
|
3671
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
3672
|
+
await onEvent({ type: "llm_call_started" });
|
|
3673
|
+
// No text delta — only a tool_use. If `handleToolUse` were
|
|
3674
|
+
// flushing, this would land a partial write before
|
|
3675
|
+
// `message_complete`.
|
|
3676
|
+
onEvent({
|
|
3677
|
+
type: "tool_use",
|
|
3678
|
+
id: "tu-no-flush",
|
|
3679
|
+
name: "file_read",
|
|
3680
|
+
input: { path: "/foo" },
|
|
3681
|
+
});
|
|
3682
|
+
// Yield a microtask so any (incorrectly) fire-and-forget
|
|
3683
|
+
// pipeline call has a chance to land before message_complete.
|
|
3684
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
3685
|
+
onEvent({
|
|
3686
|
+
type: "tool_result",
|
|
3687
|
+
toolUseId: "tu-no-flush",
|
|
3688
|
+
content: "ok",
|
|
3689
|
+
isError: false,
|
|
3690
|
+
});
|
|
3691
|
+
await onEvent({
|
|
3692
|
+
type: "message_complete",
|
|
3693
|
+
message: {
|
|
3694
|
+
role: "assistant",
|
|
3695
|
+
content: [
|
|
3696
|
+
{
|
|
3697
|
+
type: "tool_use",
|
|
3698
|
+
id: "tu-no-flush",
|
|
3699
|
+
name: "file_read",
|
|
3700
|
+
input: { path: "/foo" },
|
|
3701
|
+
},
|
|
3702
|
+
],
|
|
3703
|
+
},
|
|
3704
|
+
});
|
|
3705
|
+
onEvent({
|
|
3706
|
+
type: "usage",
|
|
3707
|
+
inputTokens: 10,
|
|
3708
|
+
outputTokens: 5,
|
|
3709
|
+
model: "test",
|
|
3710
|
+
providerDurationMs: 50,
|
|
3711
|
+
});
|
|
3712
|
+
return [
|
|
3713
|
+
...messages,
|
|
3714
|
+
{
|
|
3715
|
+
role: "assistant" as const,
|
|
3716
|
+
content: [
|
|
3717
|
+
{
|
|
3718
|
+
type: "tool_use",
|
|
3719
|
+
id: "tu-no-flush",
|
|
3720
|
+
name: "file_read",
|
|
3721
|
+
input: { path: "/foo" },
|
|
3722
|
+
},
|
|
3723
|
+
] as ContentBlock[],
|
|
3724
|
+
},
|
|
3725
|
+
];
|
|
3726
|
+
};
|
|
3727
|
+
|
|
3728
|
+
const ctx = makeCtx({ agentLoopRun });
|
|
3729
|
+
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
3730
|
+
|
|
3731
|
+
// Only the authoritative final flush from `handleMessageComplete`
|
|
3732
|
+
// lands. A partial flush from `handleToolUse` would have made this
|
|
3733
|
+
// 2; that's the regression this test guards against.
|
|
3734
|
+
expect(updateMessageContentMock).toHaveBeenCalledTimes(1);
|
|
3735
|
+
});
|
|
3736
|
+
|
|
3737
|
+
test("handleMessageComplete clears any pending debounce timer before the final flush", async () => {
|
|
3738
|
+
mockMessageById = {
|
|
3739
|
+
id: "msg-reserve",
|
|
3740
|
+
conversationId: "test-conv",
|
|
3741
|
+
createdAt: 1234567,
|
|
3742
|
+
role: "assistant",
|
|
3743
|
+
content: "[]",
|
|
3744
|
+
metadata: null,
|
|
3745
|
+
};
|
|
3746
|
+
|
|
3747
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
3748
|
+
await onEvent({ type: "llm_call_started" });
|
|
3749
|
+
// Short delta — schedules a debounce timer but does NOT trip the
|
|
3750
|
+
// size gate. message_complete then arrives immediately after,
|
|
3751
|
+
// before the 250ms timer can fire.
|
|
3752
|
+
onEvent({ type: "text_delta", text: "Quick reply." });
|
|
3753
|
+
await onEvent({
|
|
3754
|
+
type: "message_complete",
|
|
3755
|
+
message: {
|
|
3756
|
+
role: "assistant",
|
|
3757
|
+
content: [{ type: "text", text: "Quick reply." }],
|
|
3758
|
+
},
|
|
3759
|
+
});
|
|
3760
|
+
onEvent({
|
|
3761
|
+
type: "usage",
|
|
3762
|
+
inputTokens: 10,
|
|
3763
|
+
outputTokens: 5,
|
|
3764
|
+
model: "test",
|
|
3765
|
+
providerDurationMs: 50,
|
|
3766
|
+
});
|
|
3767
|
+
// Wait past the original debounce window to prove a late timer
|
|
3768
|
+
// does NOT fire a stray partial flush.
|
|
3769
|
+
await new Promise((resolve) => setTimeout(resolve, 1100));
|
|
3770
|
+
return [
|
|
3771
|
+
...messages,
|
|
3772
|
+
{
|
|
3773
|
+
role: "assistant" as const,
|
|
3774
|
+
content: [{ type: "text", text: "Quick reply." }] as ContentBlock[],
|
|
3775
|
+
},
|
|
3776
|
+
];
|
|
3777
|
+
};
|
|
3778
|
+
|
|
3779
|
+
const ctx = makeCtx({ agentLoopRun });
|
|
3780
|
+
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
3781
|
+
|
|
3782
|
+
// Only the final flush from `handleMessageComplete` lands. The
|
|
3783
|
+
// debounced partial would have fired around T+250ms; the timer-clear
|
|
3784
|
+
// at the top of `handleMessageComplete` cancels it.
|
|
3785
|
+
expect(updateMessageContentMock).toHaveBeenCalledTimes(1);
|
|
3786
|
+
});
|
|
3787
|
+
|
|
3788
|
+
test("partial flushes never trigger the indexer or attention projector", async () => {
|
|
3789
|
+
mockMessageById = {
|
|
3790
|
+
id: "msg-reserve",
|
|
3791
|
+
conversationId: "test-conv",
|
|
3792
|
+
createdAt: 1234567,
|
|
3793
|
+
role: "assistant",
|
|
3794
|
+
content: "[]",
|
|
3795
|
+
metadata: null,
|
|
3796
|
+
};
|
|
3797
|
+
|
|
3798
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
3799
|
+
await onEvent({ type: "llm_call_started" });
|
|
3800
|
+
onEvent({ type: "text_delta", text: "hello world" });
|
|
3801
|
+
// Wait past the 250ms debounce so the partial flush definitely
|
|
3802
|
+
// lands BEFORE message_complete fires.
|
|
3803
|
+
await new Promise((resolve) => setTimeout(resolve, 1100));
|
|
3804
|
+
// Snapshot the indexer/projector call counts AFTER the partial
|
|
3805
|
+
// flush has run but BEFORE message_complete. They must be zero.
|
|
3806
|
+
const indexerCallsBeforeComplete =
|
|
3807
|
+
indexMessageNowMock.mock.calls.length;
|
|
3808
|
+
const projectorCallsBeforeComplete =
|
|
3809
|
+
projectAssistantMessageMock.mock.calls.length;
|
|
3810
|
+
// Stash on a side channel the assertion phase can read.
|
|
3811
|
+
(
|
|
3812
|
+
ctx as unknown as { __partialSnapshot?: [number, number] }
|
|
3813
|
+
).__partialSnapshot = [
|
|
3814
|
+
indexerCallsBeforeComplete,
|
|
3815
|
+
projectorCallsBeforeComplete,
|
|
3816
|
+
];
|
|
3817
|
+
await onEvent({
|
|
3818
|
+
type: "message_complete",
|
|
3819
|
+
message: {
|
|
3820
|
+
role: "assistant",
|
|
3821
|
+
content: [{ type: "text", text: "hello world" }],
|
|
3822
|
+
},
|
|
3823
|
+
});
|
|
3824
|
+
onEvent({
|
|
3825
|
+
type: "usage",
|
|
3826
|
+
inputTokens: 10,
|
|
3827
|
+
outputTokens: 5,
|
|
3828
|
+
model: "test",
|
|
3829
|
+
providerDurationMs: 50,
|
|
3830
|
+
});
|
|
3831
|
+
return [
|
|
3832
|
+
...messages,
|
|
3833
|
+
{
|
|
3834
|
+
role: "assistant" as const,
|
|
3835
|
+
content: [{ type: "text", text: "hello world" }] as ContentBlock[],
|
|
3836
|
+
},
|
|
3837
|
+
];
|
|
3838
|
+
};
|
|
3839
|
+
|
|
3840
|
+
const ctx = makeCtx({ agentLoopRun });
|
|
3841
|
+
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
3842
|
+
|
|
3843
|
+
const snapshot = (
|
|
3844
|
+
ctx as unknown as { __partialSnapshot?: [number, number] }
|
|
3845
|
+
).__partialSnapshot;
|
|
3846
|
+
expect(snapshot).toBeDefined();
|
|
3847
|
+
// Indexer + projector were both ZERO during the mid-turn partial
|
|
3848
|
+
// flush — they only fire from `handleMessageComplete` after the
|
|
3849
|
+
// authoritative `updateContent`.
|
|
3850
|
+
expect(snapshot![0]).toBe(0);
|
|
3851
|
+
expect(snapshot![1]).toBe(0);
|
|
3852
|
+
// After the loop completes the indexer + projector each ran exactly
|
|
3853
|
+
// once (the pre-allocation finalize path).
|
|
3854
|
+
expect(indexMessageNowMock).toHaveBeenCalledTimes(1);
|
|
3855
|
+
expect(projectAssistantMessageMock).toHaveBeenCalledTimes(1);
|
|
3856
|
+
});
|
|
3857
|
+
|
|
3858
|
+
test("partial flushes redact secrets from text blocks before writing", async () => {
|
|
3859
|
+
mockMessageById = {
|
|
3860
|
+
id: "msg-reserve",
|
|
3861
|
+
conversationId: "test-conv",
|
|
3862
|
+
createdAt: 1234567,
|
|
3863
|
+
role: "assistant",
|
|
3864
|
+
content: "[]",
|
|
3865
|
+
metadata: null,
|
|
3866
|
+
};
|
|
3867
|
+
// A GitHub PAT-shaped token mid-stream — the redaction discipline
|
|
3868
|
+
// mirrors `handleMessageComplete`'s final flush so a refresh mid-turn
|
|
3869
|
+
// never sees plaintext credentials in the persisted row.
|
|
3870
|
+
const ghToken = "ghp_" + "a".repeat(36);
|
|
3871
|
+
const payload = "Here's the key: " + ghToken + " enjoy.";
|
|
3872
|
+
|
|
3873
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
3874
|
+
await onEvent({ type: "llm_call_started" });
|
|
3875
|
+
onEvent({ type: "text_delta", text: payload });
|
|
3876
|
+
// Wait past the 250ms debounce so the partial flush lands.
|
|
3877
|
+
await new Promise((resolve) => setTimeout(resolve, 1100));
|
|
3878
|
+
await onEvent({
|
|
3879
|
+
type: "message_complete",
|
|
3880
|
+
message: {
|
|
3881
|
+
role: "assistant",
|
|
3882
|
+
content: [{ type: "text", text: payload }],
|
|
3883
|
+
},
|
|
3884
|
+
});
|
|
3885
|
+
onEvent({
|
|
3886
|
+
type: "usage",
|
|
3887
|
+
inputTokens: 10,
|
|
3888
|
+
outputTokens: 5,
|
|
3889
|
+
model: "test",
|
|
3890
|
+
providerDurationMs: 50,
|
|
3891
|
+
});
|
|
3892
|
+
return [
|
|
3893
|
+
...messages,
|
|
3894
|
+
{
|
|
3895
|
+
role: "assistant" as const,
|
|
3896
|
+
content: [{ type: "text", text: payload }] as ContentBlock[],
|
|
3897
|
+
},
|
|
3898
|
+
];
|
|
3899
|
+
};
|
|
3900
|
+
|
|
3901
|
+
const ctx = makeCtx({ agentLoopRun });
|
|
3902
|
+
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
3903
|
+
|
|
3904
|
+
expect(updateMessageContentMock).toHaveBeenCalledTimes(2);
|
|
3905
|
+
const partialPayload = (
|
|
3906
|
+
updateMessageContentMock.mock.calls[0] as unknown as [string, string]
|
|
3907
|
+
)[1];
|
|
3908
|
+
// The raw PAT must never appear in the persisted snapshot. The
|
|
3909
|
+
// redaction substitute is implementation-defined; the contract here
|
|
3910
|
+
// is "the literal token string is gone".
|
|
3911
|
+
expect(partialPayload).not.toContain(ghToken);
|
|
3912
|
+
});
|
|
3913
|
+
|
|
3914
|
+
test("provider-error cleanup deletes a row that has accumulated partial content", async () => {
|
|
3915
|
+
// Regression check: the pre-allocation orphan-cleanup branch
|
|
3916
|
+
// already deletes the reserved row when the LLM call exits via
|
|
3917
|
+
// `provider_error`. Partial-persist writes content to that row
|
|
3918
|
+
// mid-turn; the cleanup must still fire and the row (along with
|
|
3919
|
+
// its partial content) must still be deleted before the synthetic
|
|
3920
|
+
// error message lands.
|
|
3921
|
+
reserveMessageMock.mockImplementationOnce(async () => ({
|
|
3922
|
+
id: "msg-orphan-with-partial",
|
|
3923
|
+
}));
|
|
3924
|
+
|
|
3925
|
+
const agentLoopRun: AgentLoopRun = async (messages, onEvent) => {
|
|
3926
|
+
await onEvent({ type: "llm_call_started" });
|
|
3927
|
+
// A debounced delta lands a partial flush BEFORE the provider
|
|
3928
|
+
// error fires.
|
|
3929
|
+
onEvent({ type: "text_delta", text: "hello world" });
|
|
3930
|
+
await new Promise((resolve) => setTimeout(resolve, 1100));
|
|
3931
|
+
onEvent({
|
|
3932
|
+
type: "provider_error",
|
|
3933
|
+
error: new Error("upstream 500"),
|
|
3934
|
+
rawRequest: { model: "gpt-4.1", messages: [] },
|
|
3935
|
+
actualProvider: "openai",
|
|
3936
|
+
});
|
|
3937
|
+
onEvent({
|
|
3938
|
+
type: "error",
|
|
3939
|
+
error: new Error("upstream 500"),
|
|
3940
|
+
});
|
|
3941
|
+
return messages;
|
|
3942
|
+
};
|
|
3943
|
+
|
|
3944
|
+
const ctx = makeCtx({ agentLoopRun });
|
|
3945
|
+
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
3946
|
+
|
|
3947
|
+
// Partial flush fired exactly once (before the provider error).
|
|
3948
|
+
// The orphan row was then deleted; the synthetic error message is
|
|
3949
|
+
// inserted separately via `addMessage` (`mock-msg-id`) and never
|
|
3950
|
+
// touched by `updateContent`.
|
|
3951
|
+
const partialFlushes = (
|
|
3952
|
+
updateMessageContentMock.mock.calls as unknown as Array<
|
|
3953
|
+
[string, string]
|
|
3954
|
+
>
|
|
3955
|
+
).filter(([id]) => id === "msg-orphan-with-partial");
|
|
3956
|
+
expect(partialFlushes).toHaveLength(1);
|
|
3957
|
+
expect(deleteMessageByIdMock).toHaveBeenCalledTimes(1);
|
|
3958
|
+
const deleteCall = deleteMessageByIdMock.mock.calls[0] as unknown as [
|
|
3959
|
+
string,
|
|
3960
|
+
];
|
|
3961
|
+
expect(deleteCall[0]).toBe("msg-orphan-with-partial");
|
|
3962
|
+
});
|
|
3963
|
+
});
|
|
3964
|
+
|
|
3447
3965
|
describe("pkbSystemReminderBlock metadata persistence", () => {
|
|
3448
3966
|
test("persists pkbSystemReminderBlock in full mode with PKB active", async () => {
|
|
3449
3967
|
const reminder = "<system_reminder>\npkb content\n</system_reminder>";
|
|
@@ -3939,21 +4457,22 @@ describe("session-agent-loop", () => {
|
|
|
3939
4457
|
const agentLoopRun: AgentLoopRun = async (
|
|
3940
4458
|
messages,
|
|
3941
4459
|
_onEvent,
|
|
3942
|
-
|
|
3943
|
-
_reqId,
|
|
3944
|
-
onCheckpoint,
|
|
4460
|
+
options,
|
|
3945
4461
|
) => {
|
|
3946
4462
|
runCount++;
|
|
3947
4463
|
if (runCount === 1) {
|
|
4464
|
+
// The loop reaches its mid-loop budget checkpoint with the raw
|
|
4465
|
+
// persistent basis as its in-loop history; the wrapped onCheckpoint
|
|
4466
|
+
// trips the gate and runs inline compaction over that basis.
|
|
3948
4467
|
mockEstimateTokens = 90_000;
|
|
3949
|
-
const decision = await onCheckpoint?.({
|
|
4468
|
+
const decision = await options?.onCheckpoint?.({
|
|
3950
4469
|
turnIndex: 0,
|
|
3951
4470
|
toolCount: 1,
|
|
3952
4471
|
hasToolUse: true,
|
|
3953
|
-
history:
|
|
4472
|
+
history: rawMidLoopBasis,
|
|
3954
4473
|
});
|
|
3955
4474
|
mockEstimateTokens = 1000;
|
|
3956
|
-
if (decision
|
|
4475
|
+
if (decision !== "continue") {
|
|
3957
4476
|
return rawMidLoopBasis;
|
|
3958
4477
|
}
|
|
3959
4478
|
}
|