@vellumai/assistant 0.5.13 → 0.5.15
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/.env.example +1 -6
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +0 -1
- package/bunfig.toml +1 -0
- package/docs/architecture/memory.md +3 -3
- package/openapi.yaml +127 -22
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +2 -32
- package/src/__tests__/actor-token-service.test.ts +1 -31
- package/src/__tests__/anthropic-provider.test.ts +53 -40
- package/src/__tests__/app-git-history.test.ts +9 -17
- package/src/__tests__/app-git-service.test.ts +14 -20
- package/src/__tests__/app-store-dir-names.test.ts +10 -20
- package/src/__tests__/approval-cascade.test.ts +2 -19
- package/src/__tests__/approval-primitive.test.ts +2 -27
- package/src/__tests__/approval-routes-http.test.ts +2 -30
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
- package/src/__tests__/attachments-store.test.ts +5 -32
- package/src/__tests__/audit-log-rotation.test.ts +5 -36
- package/src/__tests__/avatar-e2e.test.ts +1 -9
- package/src/__tests__/avatar-generator.test.ts +1 -7
- package/src/__tests__/browser-fill-credential.test.ts +0 -4
- package/src/__tests__/browser-manager.test.ts +0 -6
- package/src/__tests__/call-controller.test.ts +1 -22
- package/src/__tests__/call-conversation-messages.test.ts +0 -21
- package/src/__tests__/call-domain.test.ts +0 -25
- package/src/__tests__/call-pointer-messages.test.ts +0 -21
- package/src/__tests__/call-recovery.test.ts +0 -22
- package/src/__tests__/call-routes-http.test.ts +0 -24
- package/src/__tests__/call-store.test.ts +0 -21
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
- package/src/__tests__/canonical-guardian-store.test.ts +48 -21
- package/src/__tests__/channel-approval-routes.test.ts +6 -26
- package/src/__tests__/channel-approvals.test.ts +1 -38
- package/src/__tests__/channel-delivery-store.test.ts +0 -21
- package/src/__tests__/channel-guardian.test.ts +0 -26
- package/src/__tests__/channel-reply-delivery.test.ts +5 -0
- package/src/__tests__/channel-retry-sweep.test.ts +0 -21
- package/src/__tests__/checker.test.ts +26 -61
- package/src/__tests__/clawhub.test.ts +9 -25
- package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
- package/src/__tests__/config-loader-backfill.test.ts +9 -28
- package/src/__tests__/config-schema-cmd.test.ts +5 -25
- package/src/__tests__/config-schema.test.ts +21 -40
- package/src/__tests__/config-watcher.test.ts +4 -91
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
- package/src/__tests__/contacts-tools.test.ts +0 -21
- package/src/__tests__/context-memory-e2e.test.ts +0 -21
- package/src/__tests__/context-window-manager.test.ts +130 -3
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop.test.ts +0 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -24
- package/src/__tests__/conversation-attention-store.test.ts +0 -21
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
- package/src/__tests__/conversation-clear-safety.test.ts +0 -22
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
- package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
- package/src/__tests__/conversation-disk-view.test.ts +5 -27
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +1 -33
- package/src/__tests__/conversation-fork-route.test.ts +0 -27
- package/src/__tests__/conversation-history-web-search.test.ts +23 -16
- package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
- package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
- package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
- package/src/__tests__/conversation-queue.test.ts +8 -8
- package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
- package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
- package/src/__tests__/conversation-slash-commands.test.ts +5 -0
- package/src/__tests__/conversation-slash-queue.test.ts +0 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
- package/src/__tests__/conversation-speed-override.test.ts +326 -0
- package/src/__tests__/conversation-starter-routes.test.ts +0 -23
- package/src/__tests__/conversation-store.test.ts +0 -21
- package/src/__tests__/conversation-unread-route.test.ts +0 -24
- package/src/__tests__/conversation-usage.test.ts +56 -21
- package/src/__tests__/conversation-wipe.test.ts +0 -21
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
- package/src/__tests__/credential-vault-unit.test.ts +9 -428
- package/src/__tests__/credentials-cli.test.ts +10 -10
- package/src/__tests__/daemon-assistant-events.test.ts +0 -19
- package/src/__tests__/date-context.test.ts +77 -97
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
- package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
- package/src/__tests__/email-cli.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +5 -17
- package/src/__tests__/first-greeting.test.ts +4 -32
- package/src/__tests__/followup-tools.test.ts +0 -21
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
- package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
- package/src/__tests__/guardian-action-store.test.ts +0 -21
- package/src/__tests__/guardian-action-sweep.test.ts +0 -21
- package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
- package/src/__tests__/guardian-dispatch.test.ts +0 -21
- package/src/__tests__/guardian-grant-minting.test.ts +0 -22
- package/src/__tests__/guardian-outbound-http.test.ts +0 -22
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
- package/src/__tests__/guardian-routing-state.test.ts +0 -22
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
- package/src/__tests__/headless-browser-interactions.test.ts +0 -4
- package/src/__tests__/headless-browser-navigate.test.ts +0 -4
- package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
- package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
- package/src/__tests__/heartbeat-service.test.ts +99 -26
- package/src/__tests__/hooks-blocking.test.ts +3 -3
- package/src/__tests__/hooks-config.test.ts +7 -7
- package/src/__tests__/hooks-discovery.test.ts +3 -3
- package/src/__tests__/hooks-integration.test.ts +5 -5
- package/src/__tests__/hooks-manager.test.ts +3 -3
- package/src/__tests__/hooks-runner.test.ts +5 -23
- package/src/__tests__/hooks-settings.test.ts +3 -3
- package/src/__tests__/hooks-templates.test.ts +3 -3
- package/src/__tests__/http-conversation-lineage.test.ts +0 -27
- package/src/__tests__/identity-intro-cache.test.ts +0 -4
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
- package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
- package/src/__tests__/intent-routing.test.ts +2 -55
- package/src/__tests__/invite-redemption-service.test.ts +0 -21
- package/src/__tests__/invite-routes-http.test.ts +0 -21
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
- package/src/__tests__/journal-context.test.ts +8 -75
- package/src/__tests__/list-messages-attachments.test.ts +0 -22
- package/src/__tests__/llm-context-route-provider.test.ts +0 -21
- package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
- package/src/__tests__/llm-usage-store.test.ts +0 -21
- package/src/__tests__/log-export-workspace.test.ts +1 -1
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
- package/src/__tests__/managed-store.test.ts +1 -1
- package/src/__tests__/mcp-cli.test.ts +7 -10
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
- package/src/__tests__/memory-recall-log-store.test.ts +0 -27
- package/src/__tests__/memory-recall-quality.test.ts +0 -21
- package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
- package/src/__tests__/memory-regressions.test.ts +282 -70
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
- package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
- package/src/__tests__/messaging-send-tool.test.ts +201 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
- package/src/__tests__/migration-export-http.test.ts +7 -1
- package/src/__tests__/migration-import-commit-http.test.ts +16 -14
- package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
- package/src/__tests__/migration-validate-http.test.ts +1 -28
- package/src/__tests__/native-web-search.test.ts +25 -22
- package/src/__tests__/non-member-access-request.test.ts +0 -22
- package/src/__tests__/notification-guardian-path.test.ts +0 -21
- package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
- package/src/__tests__/oauth-apps-routes.test.ts +103 -2
- package/src/__tests__/oauth-cli.test.ts +52 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
- package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
- package/src/__tests__/oauth-providers-routes.test.ts +257 -0
- package/src/__tests__/oauth-store.test.ts +0 -21
- package/src/__tests__/onboarding-template-contract.test.ts +2 -2
- package/src/__tests__/openai-provider.test.ts +261 -0
- package/src/__tests__/pairing-concurrent.test.ts +6 -6
- package/src/__tests__/pairing-routes.test.ts +7 -1
- package/src/__tests__/path-policy.test.ts +1 -1
- package/src/__tests__/platform.test.ts +64 -88
- package/src/__tests__/playbook-execution.test.ts +0 -21
- package/src/__tests__/playbook-tools.test.ts +0 -21
- package/src/__tests__/pricing.test.ts +100 -0
- package/src/__tests__/relay-server.test.ts +1 -25
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
- package/src/__tests__/runtime-events-sse.test.ts +0 -24
- package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
- package/src/__tests__/schedule-store.test.ts +0 -21
- package/src/__tests__/schedule-tools.test.ts +0 -21
- package/src/__tests__/scheduler-recurrence.test.ts +0 -21
- package/src/__tests__/scoped-approval-grants.test.ts +0 -21
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
- package/src/__tests__/secret-allowlist.test.ts +1 -1
- package/src/__tests__/secret-ingress-channel.test.ts +0 -5
- package/src/__tests__/secret-ingress-cli.test.ts +0 -6
- package/src/__tests__/secret-ingress-http.test.ts +0 -5
- package/src/__tests__/secret-ingress.test.ts +0 -5
- package/src/__tests__/send-endpoint-busy.test.ts +0 -24
- package/src/__tests__/sequence-store.test.ts +0 -21
- package/src/__tests__/server-history-render.test.ts +0 -24
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
- package/src/__tests__/skill-load-inline-command.test.ts +9 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
- package/src/__tests__/skill-load-tool.test.ts +11 -0
- package/src/__tests__/skills-uninstall.test.ts +10 -8
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-channel-config.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +0 -22
- package/src/__tests__/starter-bundle.test.ts +4 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/test-preload.ts +31 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +0 -20
- package/src/__tests__/tool-input-summary.test.ts +124 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
- package/src/__tests__/trusted-contact-verification.test.ts +1 -1
- package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/update-bulletin.test.ts +1 -1
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
- package/src/__tests__/voice-session-bridge.test.ts +1 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
- package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
- package/src/agent/loop.ts +6 -9
- package/src/approvals/guardian-decision-primitive.ts +46 -18
- package/src/approvals/guardian-request-resolvers.ts +19 -2
- package/src/calls/active-call-lease.ts +2 -2
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/doctor.ts +9 -9
- package/src/cli/commands/memory.ts +142 -0
- package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
- package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +13 -12
- package/src/cli/commands/oauth/index.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +47 -62
- package/src/cli/commands/platform/__tests__/connect.test.ts +72 -46
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +36 -0
- package/src/cli/commands/platform/connect.ts +17 -7
- package/src/cli/commands/platform/disconnect.ts +28 -3
- package/src/cli/commands/platform/index.ts +3 -3
- package/src/cli.ts +1 -299
- package/src/config/assistant-feature-flags.ts +23 -15
- package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
- package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +7 -7
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
- package/src/config/bundled-skills/slack/SKILL.md +18 -0
- package/src/config/env-registry.ts +15 -11
- package/src/config/env.ts +1 -11
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/heartbeat.ts +6 -1
- package/src/config/schemas/inference.ts +14 -3
- package/src/config/schemas/memory-processing.ts +16 -8
- package/src/config/schemas/memory-retrieval.ts +3 -3
- package/src/config/skills.ts +1 -1
- package/src/context/window-manager.ts +174 -51
- package/src/credential-execution/executable-discovery.ts +2 -2
- package/src/daemon/approved-devices-store.ts +2 -2
- package/src/daemon/assistant-attachments.ts +2 -0
- package/src/daemon/config-watcher.ts +4 -50
- package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
- package/src/daemon/conversation-agent-loop.ts +12 -0
- package/src/daemon/conversation-error.ts +3 -5
- package/src/daemon/conversation-history.ts +7 -3
- package/src/daemon/conversation-lifecycle.ts +16 -0
- package/src/daemon/conversation-messaging.ts +1 -0
- package/src/daemon/conversation-notifiers.ts +67 -30
- package/src/daemon/conversation-process.ts +161 -2
- package/src/daemon/conversation-queue-manager.ts +2 -0
- package/src/daemon/conversation-runtime-assembly.ts +33 -11
- package/src/daemon/conversation-slash.ts +14 -3
- package/src/daemon/conversation-tool-setup.ts +2 -0
- package/src/daemon/conversation-usage.ts +32 -4
- package/src/daemon/conversation.ts +33 -1
- package/src/daemon/daemon-control.ts +32 -16
- package/src/daemon/date-context.ts +47 -45
- package/src/daemon/dictation-profile-store.ts +2 -2
- package/src/daemon/handlers/conversations.ts +19 -0
- package/src/daemon/handlers/shared.ts +14 -21
- package/src/daemon/lifecycle.ts +5 -7
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/guardian-actions.ts +3 -17
- package/src/daemon/message-types/integrations.ts +11 -1
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/pairing-store.ts +2 -79
- package/src/daemon/server.ts +154 -8
- package/src/daemon/watch-handler.ts +65 -21
- package/src/email/guardrails.ts +3 -3
- package/src/heartbeat/heartbeat-service.ts +14 -7
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/config.ts +2 -2
- package/src/hooks/discovery.ts +2 -2
- package/src/hooks/manager.ts +2 -2
- package/src/hooks/runner.ts +5 -2
- package/src/hooks/templates.ts +2 -2
- package/src/memory/admin.ts +181 -2
- package/src/memory/app-git-service.ts +61 -4
- package/src/memory/attachments-store.ts +2 -0
- package/src/memory/canonical-guardian-store.ts +16 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-local.ts +5 -2
- package/src/memory/indexer.ts +44 -26
- package/src/memory/items-extractor.ts +34 -82
- package/src/memory/job-handlers/batch-extraction.ts +741 -0
- package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
- package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
- package/src/memory/jobs-store.ts +28 -0
- package/src/memory/jobs-worker.ts +56 -9
- package/src/memory/lifecycle-events-store.ts +4 -2
- package/src/memory/llm-request-log-store.ts +40 -2
- package/src/memory/llm-usage-store.ts +4 -3
- package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
- package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/query-expansion.ts +83 -0
- package/src/memory/retriever.test.ts +119 -0
- package/src/memory/retriever.ts +513 -105
- package/src/memory/schema/guardian.ts +4 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/search/formatting.test.ts +140 -0
- package/src/memory/search/formatting.ts +143 -198
- package/src/memory/search/mmr.ts +136 -0
- package/src/memory/search/staleness.ts +0 -15
- package/src/memory/search/tier-classifier.ts +10 -21
- package/src/memory/search/types.ts +17 -0
- package/src/messaging/providers/slack/adapter.ts +51 -5
- package/src/notifications/broadcaster.ts +13 -0
- package/src/notifications/copy-composer.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +1 -1
- package/src/oauth/connection-resolver.ts +2 -2
- package/src/oauth/provider-serializer.ts +116 -0
- package/src/permissions/trust-store.ts +24 -7
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
- package/src/prompts/journal-context.ts +50 -35
- package/src/prompts/persona-resolver.ts +1 -1
- package/src/prompts/system-prompt.ts +27 -28
- package/src/prompts/templates/BOOTSTRAP.md +14 -1
- package/src/prompts/templates/HEARTBEAT.md +10 -0
- package/src/prompts/templates/NOW.md +19 -25
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/update-bulletin.ts +1 -1
- package/src/providers/anthropic/client.ts +89 -18
- package/src/providers/model-catalog.ts +22 -2
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/client.ts +40 -1
- package/src/providers/retry.ts +23 -4
- package/src/providers/types.ts +2 -0
- package/src/runtime/assistant-scope.ts +1 -1
- package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
- package/src/runtime/auth/route-policy.ts +1 -0
- package/src/runtime/auth/token-service.ts +51 -29
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
- package/src/runtime/guardian-decision-types.ts +16 -10
- package/src/runtime/http-server.ts +3 -14
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +7 -4
- package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
- package/src/runtime/migrations/vbundle-importer.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +40 -8
- package/src/runtime/routes/conversation-routes.ts +125 -3
- package/src/runtime/routes/guardian-action-routes.ts +9 -3
- package/src/runtime/routes/identity-routes.ts +25 -4
- package/src/runtime/routes/llm-context-normalization.ts +1 -0
- package/src/runtime/routes/log-export-routes.ts +34 -12
- package/src/runtime/routes/migration-routes.ts +6 -10
- package/src/runtime/routes/oauth-apps.ts +2 -9
- package/src/runtime/routes/oauth-providers.ts +60 -0
- package/src/runtime/routes/pairing-routes.ts +0 -8
- package/src/runtime/routes/settings-routes.ts +0 -1
- package/src/runtime/routes/telemetry-routes.ts +16 -4
- package/src/security/encrypted-store.ts +2 -2
- package/src/security/secret-allowlist.ts +3 -3
- package/src/signals/emit-event.ts +42 -0
- package/src/signals/user-message.ts +37 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
- package/src/telemetry/usage-telemetry-reporter.ts +23 -17
- package/src/tools/browser/runtime-check.ts +2 -2
- package/src/tools/credentials/vault.ts +2 -249
- package/src/tools/memory/definitions.ts +1 -1
- package/src/tools/memory/handlers.test.ts +50 -8
- package/src/tools/memory/handlers.ts +3 -1
- package/src/tools/side-effects.ts +1 -6
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +11 -14
- package/src/tools/tool-approval-handler.ts +20 -1
- package/src/tools/tool-input-summary.ts +66 -0
- package/src/tools/types.ts +4 -0
- package/src/usage/types.ts +4 -0
- package/src/util/device-id.ts +10 -10
- package/src/util/platform.ts +71 -33
- package/src/util/pricing.ts +19 -6
- package/src/util/strip-comment-lines.ts +28 -0
- package/src/workspace/git-service.ts +8 -18
- package/src/workspace/migrations/003-seed-device-id.ts +6 -4
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
- package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/signals/confirm.ts +0 -82
- package/src/signals/trust-rule.ts +0 -174
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import { createUserMessage } from "../agent/message-types.js";
|
|
2
1
|
import type { ContextWindowConfig } from "../config/types.js";
|
|
3
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
ContentBlock,
|
|
4
|
+
ImageContent,
|
|
5
|
+
Message,
|
|
6
|
+
Provider,
|
|
7
|
+
} from "../providers/types.js";
|
|
4
8
|
import { getLogger } from "../util/logger.js";
|
|
5
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
estimateContentBlockTokens,
|
|
11
|
+
estimatePromptTokens,
|
|
12
|
+
estimateTextTokens,
|
|
13
|
+
} from "./token-estimator.js";
|
|
6
14
|
import { truncateToolResultsAcrossHistory } from "./tool-result-truncation.js";
|
|
7
15
|
|
|
8
16
|
const log = getLogger("context-window");
|
|
@@ -399,13 +407,13 @@ export class ContextWindowManager {
|
|
|
399
407
|
};
|
|
400
408
|
}
|
|
401
409
|
|
|
402
|
-
const
|
|
403
|
-
|
|
410
|
+
const transcriptBlocks = this.capTranscriptBlocksToTokenBudget(
|
|
411
|
+
serializeMessagesToContentBlocks(compactableMessages),
|
|
404
412
|
existingSummary ?? "No previous summary.",
|
|
405
413
|
);
|
|
406
414
|
const summaryUpdate = await this.updateSummary(
|
|
407
415
|
existingSummary ?? "No previous summary.",
|
|
408
|
-
|
|
416
|
+
transcriptBlocks,
|
|
409
417
|
signal,
|
|
410
418
|
);
|
|
411
419
|
const summary = summaryUpdate.summary;
|
|
@@ -426,7 +434,8 @@ export class ContextWindowManager {
|
|
|
426
434
|
// Media (images, files) in kept turns is preserved naturally — those
|
|
427
435
|
// turns are carried forward as-is and their token cost is already
|
|
428
436
|
// accounted for by pickKeepBoundary's estimatePromptTokens call.
|
|
429
|
-
//
|
|
437
|
+
// Images in compacted turns are passed to the summarizer so it can
|
|
438
|
+
// describe their visual content in the summary text.
|
|
430
439
|
const summaryMessage = createContextSummaryMessage(summary);
|
|
431
440
|
|
|
432
441
|
const { messages: truncatedKeptMessages } =
|
|
@@ -555,18 +564,20 @@ export class ContextWindowManager {
|
|
|
555
564
|
}
|
|
556
565
|
|
|
557
566
|
/**
|
|
558
|
-
* Trim the serialized transcript so that the summary prompt
|
|
559
|
-
* existing summary + transcript + scaffolding) fits within
|
|
560
|
-
* input token limit, minus the output budget reserved for the
|
|
561
|
-
*
|
|
562
|
-
*
|
|
567
|
+
* Trim the serialized transcript content blocks so that the summary prompt
|
|
568
|
+
* (system prompt + existing summary + transcript + scaffolding) fits within
|
|
569
|
+
* the provider's input token limit, minus the output budget reserved for the
|
|
570
|
+
* summary itself.
|
|
571
|
+
*
|
|
572
|
+
* When the transcript exceeds the budget, blocks are dropped from the
|
|
573
|
+
* beginning (oldest messages first) to preserve recent context. Image blocks
|
|
574
|
+
* are dropped before text blocks within each pass since they are expensive
|
|
575
|
+
* and their surrounding text context already captures the conversation flow.
|
|
563
576
|
*/
|
|
564
|
-
private
|
|
565
|
-
|
|
577
|
+
private capTranscriptBlocksToTokenBudget(
|
|
578
|
+
blocks: ContentBlock[],
|
|
566
579
|
currentSummary: string,
|
|
567
|
-
):
|
|
568
|
-
// Reserve tokens for: system prompt, summary prompt scaffolding, existing
|
|
569
|
-
// summary, message overhead, and the output (summaryMaxTokens).
|
|
580
|
+
): ContentBlock[] {
|
|
570
581
|
const overheadTokens =
|
|
571
582
|
estimateTextTokens(SUMMARY_SYSTEM_PROMPT) +
|
|
572
583
|
estimateTextTokens(currentSummary) +
|
|
@@ -580,26 +591,59 @@ export class ContextWindowManager {
|
|
|
580
591
|
this.config.maxInputTokens - overheadTokens,
|
|
581
592
|
);
|
|
582
593
|
|
|
583
|
-
const
|
|
584
|
-
|
|
594
|
+
const estimateBlockTokens = (b: ContentBlock): number =>
|
|
595
|
+
estimateContentBlockTokens(b, { providerName: this.provider.name });
|
|
596
|
+
|
|
597
|
+
let totalTokens = 0;
|
|
598
|
+
for (const block of blocks) {
|
|
599
|
+
totalTokens += estimateBlockTokens(block);
|
|
600
|
+
}
|
|
601
|
+
if (totalTokens <= maxTranscriptTokens) return blocks;
|
|
602
|
+
|
|
603
|
+
// First pass: drop images from the beginning until we fit or run out of
|
|
604
|
+
// images to drop. Images are high-cost and their text context (message
|
|
605
|
+
// headers, surrounding tool_use/tool_result serializations) is preserved.
|
|
606
|
+
const result = [...blocks];
|
|
607
|
+
for (let i = 0; i < result.length && totalTokens > maxTranscriptTokens; i++) {
|
|
608
|
+
if (result[i].type === "image") {
|
|
609
|
+
totalTokens -= estimateBlockTokens(result[i]);
|
|
610
|
+
const stub: ContentBlock = {
|
|
611
|
+
type: "text",
|
|
612
|
+
text: `[image omitted from summary context]`,
|
|
613
|
+
};
|
|
614
|
+
totalTokens += estimateBlockTokens(stub);
|
|
615
|
+
result[i] = stub;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
if (totalTokens <= maxTranscriptTokens) return result;
|
|
619
|
+
|
|
620
|
+
// Second pass: drop text blocks from the beginning (oldest) until we fit.
|
|
621
|
+
let dropUntil = 0;
|
|
622
|
+
let droppedTokens = 0;
|
|
623
|
+
for (let i = 0; i < result.length && totalTokens > maxTranscriptTokens; i++) {
|
|
624
|
+
droppedTokens += estimateBlockTokens(result[i]);
|
|
625
|
+
totalTokens -= estimateBlockTokens(result[i]);
|
|
626
|
+
dropUntil = i + 1;
|
|
627
|
+
}
|
|
585
628
|
|
|
586
|
-
// Truncate from the beginning (older messages) to preserve recent context.
|
|
587
|
-
const maxChars = maxTranscriptTokens * 4; // inverse of estimateTextTokens
|
|
588
|
-
const truncated = transcript.slice(transcript.length - maxChars);
|
|
589
629
|
log.info(
|
|
590
630
|
{
|
|
591
|
-
originalTokens:
|
|
631
|
+
originalTokens: totalTokens + droppedTokens,
|
|
592
632
|
cappedTokens: maxTranscriptTokens,
|
|
593
|
-
droppedTokens
|
|
633
|
+
droppedTokens,
|
|
594
634
|
},
|
|
595
|
-
"Capped summary transcript to fit provider input limit",
|
|
635
|
+
"Capped summary transcript blocks to fit provider input limit",
|
|
596
636
|
);
|
|
597
|
-
|
|
637
|
+
|
|
638
|
+
return [
|
|
639
|
+
{ type: "text", text: "[earlier messages truncated]" } as ContentBlock,
|
|
640
|
+
...result.slice(dropUntil),
|
|
641
|
+
];
|
|
598
642
|
}
|
|
599
643
|
|
|
600
644
|
private async updateSummary(
|
|
601
645
|
currentSummary: string,
|
|
602
|
-
|
|
646
|
+
transcriptBlocks: ContentBlock[],
|
|
603
647
|
signal?: AbortSignal,
|
|
604
648
|
): Promise<{
|
|
605
649
|
summary: string;
|
|
@@ -610,10 +654,14 @@ export class ContextWindowManager {
|
|
|
610
654
|
cacheReadInputTokens: number;
|
|
611
655
|
rawResponse?: unknown;
|
|
612
656
|
}> {
|
|
613
|
-
const
|
|
657
|
+
const contentBlocks = buildSummaryContentBlocks(
|
|
658
|
+
currentSummary,
|
|
659
|
+
transcriptBlocks,
|
|
660
|
+
);
|
|
661
|
+
const summaryMessage: Message = { role: "user", content: contentBlocks };
|
|
614
662
|
try {
|
|
615
663
|
const response = await this.provider.sendMessage(
|
|
616
|
-
[
|
|
664
|
+
[summaryMessage],
|
|
617
665
|
undefined,
|
|
618
666
|
SUMMARY_SYSTEM_PROMPT,
|
|
619
667
|
{
|
|
@@ -639,8 +687,14 @@ export class ContextWindowManager {
|
|
|
639
687
|
log.warn({ err }, "Summary generation failed, using local fallback");
|
|
640
688
|
}
|
|
641
689
|
|
|
690
|
+
// Fallback: extract text-only transcript for local summary generation.
|
|
691
|
+
const textTranscript = transcriptBlocks
|
|
692
|
+
.filter((b): b is Extract<ContentBlock, { type: "text" }> => b.type === "text")
|
|
693
|
+
.map((b) => b.text)
|
|
694
|
+
.join("\n\n");
|
|
695
|
+
|
|
642
696
|
return {
|
|
643
|
-
summary: fallbackSummary(currentSummary,
|
|
697
|
+
summary: fallbackSummary(currentSummary, textTranscript),
|
|
644
698
|
inputTokens: 0,
|
|
645
699
|
outputTokens: 0,
|
|
646
700
|
model: "",
|
|
@@ -789,35 +843,102 @@ export function createContextSummaryMessage(summary: string): Message {
|
|
|
789
843
|
return message;
|
|
790
844
|
}
|
|
791
845
|
|
|
792
|
-
|
|
846
|
+
/**
|
|
847
|
+
* Build content blocks for the summary prompt. Returns a mix of text blocks
|
|
848
|
+
* (for the scaffolding, existing summary, and serialized non-image content)
|
|
849
|
+
* and image blocks (preserved from the original messages so the summarizer
|
|
850
|
+
* can describe what was in them).
|
|
851
|
+
*/
|
|
852
|
+
function buildSummaryContentBlocks(
|
|
793
853
|
currentSummary: string,
|
|
794
|
-
|
|
795
|
-
):
|
|
854
|
+
transcriptBlocks: ContentBlock[],
|
|
855
|
+
): ContentBlock[] {
|
|
796
856
|
return [
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
857
|
+
{
|
|
858
|
+
type: "text",
|
|
859
|
+
text: [
|
|
860
|
+
"Update the summary with new transcript data.",
|
|
861
|
+
"If new information conflicts with older notes, keep the most recent and explicit detail.",
|
|
862
|
+
"Keep all unresolved asks and next steps.",
|
|
863
|
+
"For any images included below, describe their visual content in the summary so the information is preserved after compaction.",
|
|
864
|
+
"",
|
|
865
|
+
"### Existing Summary",
|
|
866
|
+
currentSummary.trim().length > 0 ? currentSummary.trim() : "None.",
|
|
867
|
+
"",
|
|
868
|
+
"### Transcript",
|
|
869
|
+
].join("\n"),
|
|
870
|
+
} as ContentBlock,
|
|
871
|
+
...transcriptBlocks,
|
|
872
|
+
];
|
|
807
873
|
}
|
|
808
874
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
875
|
+
/**
|
|
876
|
+
* Serialize messages into a sequence of content blocks. Text-based content
|
|
877
|
+
* (tool calls, tool results, thinking, etc.) is serialized into text blocks.
|
|
878
|
+
* Image blocks — both top-level and nested inside tool_result contentBlocks —
|
|
879
|
+
* are preserved as-is so the summarizer LLM can see them.
|
|
880
|
+
*/
|
|
881
|
+
function serializeMessagesToContentBlocks(messages: Message[]): ContentBlock[] {
|
|
882
|
+
const blocks: ContentBlock[] = [];
|
|
883
|
+
for (let i = 0; i < messages.length; i++) {
|
|
884
|
+
const msg = messages[i];
|
|
885
|
+
const textLines: string[] = [`Message #${i + 1} (${msg.role})`];
|
|
812
886
|
|
|
813
|
-
|
|
814
|
-
|
|
887
|
+
for (const block of msg.content) {
|
|
888
|
+
if (block.type === "image") {
|
|
889
|
+
// Flush accumulated text lines before the image.
|
|
890
|
+
if (textLines.length > 0) {
|
|
891
|
+
blocks.push({ type: "text", text: textLines.join("\n") });
|
|
892
|
+
textLines.length = 0;
|
|
893
|
+
}
|
|
894
|
+
blocks.push(block);
|
|
895
|
+
} else if (block.type === "web_search_tool_result") {
|
|
896
|
+
textLines.push(
|
|
897
|
+
`web_search_tool_result ${block.tool_use_id}: [opaque]`,
|
|
898
|
+
);
|
|
899
|
+
} else if (block.type === "tool_result") {
|
|
900
|
+
// Extract images from tool_result contentBlocks before serializing.
|
|
901
|
+
const collectedImages: ImageContent[] = [];
|
|
902
|
+
textLines.push(serializeToolResultBlock(block, collectedImages));
|
|
903
|
+
if (collectedImages.length > 0) {
|
|
904
|
+
// Flush text, emit collected images, then continue.
|
|
905
|
+
if (textLines.length > 0) {
|
|
906
|
+
blocks.push({ type: "text", text: textLines.join("\n") });
|
|
907
|
+
textLines.length = 0;
|
|
908
|
+
}
|
|
909
|
+
blocks.push(...collectedImages);
|
|
910
|
+
}
|
|
911
|
+
} else {
|
|
912
|
+
textLines.push(serializeBlock(block));
|
|
913
|
+
}
|
|
914
|
+
}
|
|
815
915
|
|
|
816
|
-
|
|
817
|
-
|
|
916
|
+
// Flush remaining text lines for this message.
|
|
917
|
+
if (textLines.length > 0) {
|
|
918
|
+
blocks.push({ type: "text", text: textLines.join("\n") });
|
|
919
|
+
}
|
|
818
920
|
}
|
|
921
|
+
return blocks;
|
|
922
|
+
}
|
|
819
923
|
|
|
820
|
-
|
|
924
|
+
/**
|
|
925
|
+
* Serialize images nested inside tool_result contentBlocks, returning them
|
|
926
|
+
* as separate content blocks to preserve for the summarizer.
|
|
927
|
+
*/
|
|
928
|
+
function serializeToolResultBlock(
|
|
929
|
+
block: Extract<ContentBlock, { type: "tool_result" }>,
|
|
930
|
+
collectedImages: ImageContent[],
|
|
931
|
+
): string {
|
|
932
|
+
if (block.contentBlocks) {
|
|
933
|
+
for (const cb of block.contentBlocks) {
|
|
934
|
+
if (cb.type === "image") {
|
|
935
|
+
collectedImages.push(cb);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
return `tool_result ${block.tool_use_id}${
|
|
940
|
+
block.is_error ? " (error)" : ""
|
|
941
|
+
}: ${clampText(block.content)}`;
|
|
821
942
|
}
|
|
822
943
|
|
|
823
944
|
function serializeBlock(block: ContentBlock): string {
|
|
@@ -831,6 +952,8 @@ function serializeBlock(block: ContentBlock): string {
|
|
|
831
952
|
block.is_error ? " (error)" : ""
|
|
832
953
|
}: ${clampText(block.content)}`;
|
|
833
954
|
case "image":
|
|
955
|
+
// Top-level images are handled by serializeMessagesToContentBlocks.
|
|
956
|
+
// This path is only hit for images in unexpected positions.
|
|
834
957
|
return `image: ${block.source.media_type}, ${
|
|
835
958
|
Math.ceil(block.source.data.length / 4) * 3
|
|
836
959
|
} bytes(base64)`;
|
|
@@ -21,7 +21,7 @@ import { join } from "node:path";
|
|
|
21
21
|
|
|
22
22
|
import { getIsContainerized } from "../config/env-registry.js";
|
|
23
23
|
import { getLogger } from "../util/logger.js";
|
|
24
|
-
import {
|
|
24
|
+
import { getBinDir } from "../util/platform.js";
|
|
25
25
|
|
|
26
26
|
const log = getLogger("ces-discovery");
|
|
27
27
|
|
|
@@ -67,7 +67,7 @@ function getManagedBootstrapSocketPath(): string {
|
|
|
67
67
|
* a malicious binary there. Removed to close the sandbox-escape vector.
|
|
68
68
|
*/
|
|
69
69
|
function getLocalBinarySearchPaths(): string[] {
|
|
70
|
-
return [join(
|
|
70
|
+
return [join(getBinDir(), "credential-executor")];
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// ---------------------------------------------------------------------------
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import { dirname, join } from "node:path";
|
|
18
18
|
|
|
19
19
|
import { getLogger } from "../util/logger.js";
|
|
20
|
-
import {
|
|
20
|
+
import { getProtectedDir } from "../util/platform.js";
|
|
21
21
|
|
|
22
22
|
const log = getLogger("approved-devices-store");
|
|
23
23
|
|
|
@@ -33,7 +33,7 @@ interface ApprovedDevicesFile {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
function getStorePath(): string {
|
|
36
|
-
return join(
|
|
36
|
+
return join(getProtectedDir(), "approved-devices.json");
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/** Hash a raw deviceId for storage. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* File watchers and config reload logic extracted from DaemonServer.
|
|
3
|
-
* Watches workspace files (config, prompts)
|
|
4
|
-
*
|
|
3
|
+
* Watches workspace files (config, prompts) and skills directories
|
|
4
|
+
* for changes.
|
|
5
5
|
*/
|
|
6
6
|
import {
|
|
7
7
|
existsSync,
|
|
@@ -12,27 +12,20 @@ import {
|
|
|
12
12
|
} from "node:fs";
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
|
|
15
|
-
import { clearFeatureFlagOverridesCache } from "../config/assistant-feature-flags.js";
|
|
16
15
|
import { getConfig, invalidateConfigCache } from "../config/loader.js";
|
|
17
16
|
import { clearEmbeddingBackendCache } from "../memory/embedding-backend.js";
|
|
18
17
|
import { clearCache as clearTrustCache } from "../permissions/trust-store.js";
|
|
19
18
|
import { initializeProviders } from "../providers/registry.js";
|
|
20
|
-
import {
|
|
21
|
-
resetAllowlist,
|
|
22
|
-
validateAllowlistFile,
|
|
23
|
-
} from "../security/secret-allowlist.js";
|
|
24
19
|
import { handleBashSignal } from "../signals/bash.js";
|
|
25
20
|
import { handleCancelSignal } from "../signals/cancel.js";
|
|
26
|
-
import { handleConfirmationSignal } from "../signals/confirm.js";
|
|
27
21
|
import { handleConversationUndoSignal } from "../signals/conversation-undo.js";
|
|
22
|
+
import { handleEmitEventSignal } from "../signals/emit-event.js";
|
|
28
23
|
import { handleMcpReloadSignal } from "../signals/mcp-reload.js";
|
|
29
24
|
import { handleShotgunSignal } from "../signals/shotgun.js";
|
|
30
|
-
import { handleTrustRuleSignal } from "../signals/trust-rule.js";
|
|
31
25
|
import { handleUserMessageSignal } from "../signals/user-message.js";
|
|
32
26
|
import { DebouncerMap } from "../util/debounce.js";
|
|
33
27
|
import { getLogger } from "../util/logger.js";
|
|
34
28
|
import {
|
|
35
|
-
getRootDir,
|
|
36
29
|
getSignalsDir,
|
|
37
30
|
getWorkspaceDir,
|
|
38
31
|
getWorkspaceSkillsDir,
|
|
@@ -117,7 +110,6 @@ export class ConfigWatcher {
|
|
|
117
110
|
*/
|
|
118
111
|
start(onConversationEvict: () => void, onIdentityChanged?: () => void): void {
|
|
119
112
|
const workspaceDir = getWorkspaceDir();
|
|
120
|
-
const protectedDir = join(getRootDir(), "protected");
|
|
121
113
|
|
|
122
114
|
const workspaceHandlers: Record<string, () => void> = {
|
|
123
115
|
"config.json": async () => {
|
|
@@ -150,36 +142,6 @@ export class ConfigWatcher {
|
|
|
150
142
|
"UPDATES.md": () => onConversationEvict(),
|
|
151
143
|
};
|
|
152
144
|
|
|
153
|
-
const protectedHandlers: Record<string, () => void> = {
|
|
154
|
-
"trust.json": () => {
|
|
155
|
-
clearTrustCache();
|
|
156
|
-
},
|
|
157
|
-
"feature-flags.json": () => {
|
|
158
|
-
clearFeatureFlagOverridesCache();
|
|
159
|
-
onConversationEvict();
|
|
160
|
-
},
|
|
161
|
-
"feature-flags-remote.json": () => {
|
|
162
|
-
clearFeatureFlagOverridesCache();
|
|
163
|
-
onConversationEvict();
|
|
164
|
-
},
|
|
165
|
-
"secret-allowlist.json": () => {
|
|
166
|
-
resetAllowlist();
|
|
167
|
-
try {
|
|
168
|
-
const errors = validateAllowlistFile();
|
|
169
|
-
if (errors && errors.length > 0) {
|
|
170
|
-
for (const e of errors) {
|
|
171
|
-
log.warn(
|
|
172
|
-
{ index: e.index, pattern: e.pattern },
|
|
173
|
-
`Invalid regex in secret-allowlist.json: ${e.message}`,
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
} catch (err) {
|
|
178
|
-
log.warn({ err }, "Failed to validate secret-allowlist.json");
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
|
-
|
|
183
145
|
const watchDir = (
|
|
184
146
|
dir: string,
|
|
185
147
|
handlers: Record<string, () => void>,
|
|
@@ -210,13 +172,6 @@ export class ConfigWatcher {
|
|
|
210
172
|
workspaceHandlers,
|
|
211
173
|
"workspace directory for config/prompt changes",
|
|
212
174
|
);
|
|
213
|
-
if (existsSync(protectedDir)) {
|
|
214
|
-
watchDir(
|
|
215
|
-
protectedDir,
|
|
216
|
-
protectedHandlers,
|
|
217
|
-
"protected directory for trust/allowlist changes",
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
175
|
|
|
221
176
|
this.startSignalsWatcher();
|
|
222
177
|
this.startSkillsWatchers(onConversationEvict);
|
|
@@ -242,10 +197,9 @@ export class ConfigWatcher {
|
|
|
242
197
|
|
|
243
198
|
const exactSignalHandlers: Record<string, () => void | Promise<void>> = {
|
|
244
199
|
cancel: handleCancelSignal,
|
|
245
|
-
confirm: handleConfirmationSignal,
|
|
246
200
|
"mcp-reload": handleMcpReloadSignal,
|
|
247
|
-
"trust-rule": handleTrustRuleSignal,
|
|
248
201
|
"conversation-undo": handleConversationUndoSignal,
|
|
202
|
+
"emit-event": handleEmitEventSignal,
|
|
249
203
|
};
|
|
250
204
|
|
|
251
205
|
const prefixSignalHandlers: Record<
|
|
@@ -60,6 +60,8 @@ export interface EventHandlerState {
|
|
|
60
60
|
exchangeCacheCreationInputTokens: number;
|
|
61
61
|
exchangeCacheReadInputTokens: number;
|
|
62
62
|
exchangeOutputTokens: number;
|
|
63
|
+
/** Number of actual LLM API calls within this exchange. */
|
|
64
|
+
exchangeLlmCallCount: number;
|
|
63
65
|
readonly exchangeRawResponses: unknown[];
|
|
64
66
|
model: string;
|
|
65
67
|
orderingErrorDetected: boolean;
|
|
@@ -100,6 +102,8 @@ export interface EventHandlerState {
|
|
|
100
102
|
>;
|
|
101
103
|
/** tool_use_ids emitted in the current turn (populated in handleToolUse, cleared after annotation). */
|
|
102
104
|
currentTurnToolUseIds: string[];
|
|
105
|
+
/** Wall-clock time (ms since epoch) when the agent loop turn started, used as the display timestamp for assistant messages. */
|
|
106
|
+
turnStartedAt: number;
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
/** Immutable context shared across event handlers within a single agent loop run. */
|
|
@@ -127,6 +131,7 @@ export function createEventHandlerState(): EventHandlerState {
|
|
|
127
131
|
exchangeCacheCreationInputTokens: 0,
|
|
128
132
|
exchangeCacheReadInputTokens: 0,
|
|
129
133
|
exchangeOutputTokens: 0,
|
|
134
|
+
exchangeLlmCallCount: 0,
|
|
130
135
|
exchangeRawResponses: [],
|
|
131
136
|
model: "",
|
|
132
137
|
orderingErrorDetected: false,
|
|
@@ -151,6 +156,7 @@ export function createEventHandlerState(): EventHandlerState {
|
|
|
151
156
|
requestIdToToolUseId: new Map(),
|
|
152
157
|
toolConfirmationOutcomes: new Map(),
|
|
153
158
|
currentTurnToolUseIds: [],
|
|
159
|
+
turnStartedAt: Date.now(),
|
|
154
160
|
};
|
|
155
161
|
}
|
|
156
162
|
|
|
@@ -267,7 +273,7 @@ export function handleThinkingDelta(
|
|
|
267
273
|
}
|
|
268
274
|
if (!deps.ctx.streamThinking) return;
|
|
269
275
|
emitLlmCallStartedIfNeeded(state, deps);
|
|
270
|
-
deps.onEvent({ type: "assistant_thinking_delta", thinking: event.thinking });
|
|
276
|
+
deps.onEvent({ type: "assistant_thinking_delta", thinking: event.thinking, conversationId: deps.ctx.conversationId });
|
|
271
277
|
}
|
|
272
278
|
|
|
273
279
|
export function handleToolUse(
|
|
@@ -719,6 +725,7 @@ export async function handleMessageComplete(
|
|
|
719
725
|
userMessageInterface: deps.turnInterfaceContext.userMessageInterface,
|
|
720
726
|
assistantMessageInterface:
|
|
721
727
|
deps.turnInterfaceContext.assistantMessageInterface,
|
|
728
|
+
sentAt: state.turnStartedAt,
|
|
722
729
|
};
|
|
723
730
|
const assistantMsg = await addMessage(
|
|
724
731
|
deps.ctx.conversationId,
|
|
@@ -778,6 +785,7 @@ export function handleUsage(
|
|
|
778
785
|
): void {
|
|
779
786
|
const providerName = event.actualProvider ?? deps.ctx.provider.name;
|
|
780
787
|
state.exchangeProviderName = providerName;
|
|
788
|
+
state.exchangeLlmCallCount += 1;
|
|
781
789
|
state.exchangeInputTokens += event.inputTokens;
|
|
782
790
|
state.exchangeCacheCreationInputTokens += event.cacheCreationInputTokens ?? 0;
|
|
783
791
|
state.exchangeCacheReadInputTokens += event.cacheReadInputTokens ?? 0;
|
|
@@ -1589,6 +1589,12 @@ export async function runAgentLoopImpl(
|
|
|
1589
1589
|
const restoredHistory = [...preRepairMessages, ...newMessages];
|
|
1590
1590
|
ctx.messages = stripInjectedContext(restoredHistory);
|
|
1591
1591
|
|
|
1592
|
+
const postLoopContextEstimate = estimatePromptTokens(
|
|
1593
|
+
ctx.messages,
|
|
1594
|
+
ctx.systemPrompt,
|
|
1595
|
+
{ providerName: ctx.provider.name, toolTokenBudget },
|
|
1596
|
+
);
|
|
1597
|
+
|
|
1592
1598
|
emitUsage(
|
|
1593
1599
|
ctx,
|
|
1594
1600
|
state.exchangeInputTokens,
|
|
@@ -1601,6 +1607,8 @@ export async function runAgentLoopImpl(
|
|
|
1601
1607
|
state.exchangeCacheReadInputTokens,
|
|
1602
1608
|
collapseRawResponses(state.exchangeRawResponses),
|
|
1603
1609
|
state.exchangeProviderName,
|
|
1610
|
+
state.exchangeLlmCallCount,
|
|
1611
|
+
{ tokens: postLoopContextEstimate, maxTokens: config.contextWindow.maxInputTokens },
|
|
1604
1612
|
);
|
|
1605
1613
|
|
|
1606
1614
|
void getHookManager().trigger("post-message", {
|
|
@@ -1868,6 +1876,8 @@ function emitUsage(
|
|
|
1868
1876
|
cacheReadInputTokens = 0,
|
|
1869
1877
|
rawResponse?: unknown,
|
|
1870
1878
|
providerName?: string,
|
|
1879
|
+
llmCallCount = 1,
|
|
1880
|
+
contextWindow?: { tokens: number; maxTokens: number },
|
|
1871
1881
|
): void {
|
|
1872
1882
|
recordUsage(
|
|
1873
1883
|
{
|
|
@@ -1884,6 +1894,8 @@ function emitUsage(
|
|
|
1884
1894
|
cacheCreationInputTokens,
|
|
1885
1895
|
cacheReadInputTokens,
|
|
1886
1896
|
rawResponse,
|
|
1897
|
+
llmCallCount,
|
|
1898
|
+
contextWindow,
|
|
1887
1899
|
);
|
|
1888
1900
|
}
|
|
1889
1901
|
|
|
@@ -458,15 +458,13 @@ function classifyByMessage(
|
|
|
458
458
|
.split("\n")
|
|
459
459
|
.map((l) => l.trim())
|
|
460
460
|
.find((l) => l.length > 0) ?? "";
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
const userMessage = summary
|
|
464
|
-
? `Processing failed: ${summary}`
|
|
461
|
+
const userMessage = firstLine
|
|
462
|
+
? `Processing failed: ${firstLine}`
|
|
465
463
|
: "Something went wrong processing your message. Please try again.";
|
|
466
464
|
return {
|
|
467
465
|
code: "CONVERSATION_PROCESSING_FAILED",
|
|
468
466
|
userMessage,
|
|
469
|
-
retryable:
|
|
467
|
+
retryable: true,
|
|
470
468
|
errorCategory: "processing_failed",
|
|
471
469
|
};
|
|
472
470
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "../memory/conversation-crud.js";
|
|
11
11
|
import { isLastUserMessageToolResult } from "../memory/conversation-queries.js";
|
|
12
12
|
import { enqueueMemoryJob } from "../memory/jobs-store.js";
|
|
13
|
+
import { relinkLlmRequestLogs } from "../memory/llm-request-log-store.js";
|
|
13
14
|
import { withQdrantBreaker } from "../memory/qdrant-circuit-breaker.js";
|
|
14
15
|
import { getQdrantClient } from "../memory/qdrant-client.js";
|
|
15
16
|
import type { ContentBlock, Message } from "../providers/types.js";
|
|
@@ -295,9 +296,10 @@ export function consolidateAssistantMessages(
|
|
|
295
296
|
JSON.stringify(consolidatedContent),
|
|
296
297
|
);
|
|
297
298
|
|
|
298
|
-
// Re-link attachments from messages about to be
|
|
299
|
-
// message. Without this, ON DELETE CASCADE on
|
|
300
|
-
// the attachment links, and
|
|
299
|
+
// Re-link attachments and LLM request logs from messages about to be
|
|
300
|
+
// deleted to the consolidated message. Without this, ON DELETE CASCADE on
|
|
301
|
+
// message_attachments destroys the attachment links, and LLM call logs
|
|
302
|
+
// become orphaned (invisible in the context inspector).
|
|
301
303
|
const messageIdsToDelete = [
|
|
302
304
|
...messagesToConsolidate.slice(1).map((m) => m.id),
|
|
303
305
|
...messagesToDelete,
|
|
@@ -313,6 +315,8 @@ export function consolidateAssistantMessages(
|
|
|
313
315
|
"Re-linked attachments to consolidated message",
|
|
314
316
|
);
|
|
315
317
|
}
|
|
318
|
+
|
|
319
|
+
relinkLlmRequestLogs(messageIdsToDelete, firstAssistantMsg.id);
|
|
316
320
|
}
|
|
317
321
|
|
|
318
322
|
// Delete the other assistant messages and internal tool_result messages,
|
|
@@ -9,11 +9,13 @@ import type { EventBus } from "../events/bus.js";
|
|
|
9
9
|
import type { AssistantDomainEvents } from "../events/domain-events.js";
|
|
10
10
|
import type { ToolProfiler } from "../events/tool-profiling-listener.js";
|
|
11
11
|
import { getHookManager } from "../hooks/manager.js";
|
|
12
|
+
import { getMemoryCheckpoint } from "../memory/checkpoints.js";
|
|
12
13
|
import {
|
|
13
14
|
getConversation,
|
|
14
15
|
getMessages,
|
|
15
16
|
type MessageRow,
|
|
16
17
|
} from "../memory/conversation-crud.js";
|
|
18
|
+
import { enqueueMemoryJob } from "../memory/jobs-store.js";
|
|
17
19
|
import type { PermissionPrompter } from "../permissions/prompter.js";
|
|
18
20
|
import type { SecretPrompter } from "../permissions/secret-prompter.js";
|
|
19
21
|
import type { ContentBlock, Message } from "../providers/types.js";
|
|
@@ -266,6 +268,20 @@ export function disposeConversation(ctx: DisposeContext): void {
|
|
|
266
268
|
void getHookManager().trigger("conversation-end", {
|
|
267
269
|
conversationId: ctx.conversationId,
|
|
268
270
|
});
|
|
271
|
+
|
|
272
|
+
// Trigger batch extraction for any remaining unextracted messages
|
|
273
|
+
try {
|
|
274
|
+
const pendingKey = `batch_extract:${ctx.conversationId}:pending_count`;
|
|
275
|
+
const pending = getMemoryCheckpoint(pendingKey);
|
|
276
|
+
if (pending && parseInt(pending, 10) > 0) {
|
|
277
|
+
enqueueMemoryJob("batch_extract", {
|
|
278
|
+
conversationId: ctx.conversationId,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
} catch {
|
|
282
|
+
// Best-effort — don't block conversation disposal
|
|
283
|
+
}
|
|
284
|
+
|
|
269
285
|
ctx.abort();
|
|
270
286
|
unregisterCallNotifiers(ctx.conversationId);
|
|
271
287
|
unregisterConversationSender(ctx.conversationId);
|