@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
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
addMessage,
|
|
23
23
|
provenanceFromTrustContext,
|
|
24
24
|
} from "../memory/conversation-crud.js";
|
|
25
|
+
import { setMessageIdOnLogs } from "../memory/llm-request-log-store.js";
|
|
25
26
|
import type { Message } from "../providers/types.js";
|
|
26
27
|
import type { WatchSession } from "../tools/watch/watch-state.js";
|
|
27
28
|
import {
|
|
@@ -69,37 +70,73 @@ export function registerConversationNotifiers(
|
|
|
69
70
|
});
|
|
70
71
|
});
|
|
71
72
|
|
|
72
|
-
registerWatchCommentaryNotifier(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
lastCommentaryByConversation.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
text: commentary,
|
|
79
|
-
conversationId: conversationId,
|
|
80
|
-
});
|
|
81
|
-
ctx.sendToClient({
|
|
82
|
-
type: "message_complete",
|
|
83
|
-
conversationId: conversationId,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
});
|
|
73
|
+
registerWatchCommentaryNotifier(
|
|
74
|
+
conversationId,
|
|
75
|
+
async (_session: WatchSession) => {
|
|
76
|
+
const result = lastCommentaryByConversation.get(conversationId);
|
|
77
|
+
if (result) {
|
|
78
|
+
lastCommentaryByConversation.delete(conversationId);
|
|
87
79
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
text
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
80
|
+
const msg = await addMessage(
|
|
81
|
+
conversationId,
|
|
82
|
+
"assistant",
|
|
83
|
+
JSON.stringify([{ type: "text", text: result.text }]),
|
|
84
|
+
provenanceFromTrustContext(ctx.trustContext),
|
|
85
|
+
);
|
|
86
|
+
ctx.messages.push(createAssistantMessage(result.text));
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
setMessageIdOnLogs(result.logIds, msg.id);
|
|
90
|
+
} catch {
|
|
91
|
+
// non-fatal — message is persisted even if log linkage fails
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ctx.sendToClient({
|
|
95
|
+
type: "assistant_text_delta",
|
|
96
|
+
text: result.text,
|
|
97
|
+
conversationId: conversationId,
|
|
98
|
+
});
|
|
99
|
+
ctx.sendToClient({
|
|
100
|
+
type: "message_complete",
|
|
101
|
+
conversationId: conversationId,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
registerWatchCompletionNotifier(
|
|
108
|
+
conversationId,
|
|
109
|
+
async (_session: WatchSession) => {
|
|
110
|
+
const result = lastSummaryByConversation.get(conversationId);
|
|
111
|
+
if (result) {
|
|
112
|
+
lastSummaryByConversation.delete(conversationId);
|
|
113
|
+
|
|
114
|
+
const msg = await addMessage(
|
|
115
|
+
conversationId,
|
|
116
|
+
"assistant",
|
|
117
|
+
JSON.stringify([{ type: "text", text: result.text }]),
|
|
118
|
+
provenanceFromTrustContext(ctx.trustContext),
|
|
119
|
+
);
|
|
120
|
+
ctx.messages.push(createAssistantMessage(result.text));
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
setMessageIdOnLogs(result.logIds, msg.id);
|
|
124
|
+
} catch {
|
|
125
|
+
// non-fatal — message is persisted even if log linkage fails
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
ctx.sendToClient({
|
|
129
|
+
type: "assistant_text_delta",
|
|
130
|
+
text: result.text,
|
|
131
|
+
conversationId: conversationId,
|
|
132
|
+
});
|
|
133
|
+
ctx.sendToClient({
|
|
134
|
+
type: "message_complete",
|
|
135
|
+
conversationId: conversationId,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
);
|
|
103
140
|
|
|
104
141
|
registerCallQuestionNotifier(
|
|
105
142
|
conversationId,
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
} from "../channels/types.js";
|
|
18
18
|
import { parseChannelId, parseInterfaceId } from "../channels/types.js";
|
|
19
19
|
import { getConfig } from "../config/loader.js";
|
|
20
|
+
import type { ContextWindowResult } from "../context/window-manager.js";
|
|
20
21
|
import { listPendingRequestsByConversationScope } from "../memory/canonical-guardian-store.js";
|
|
21
22
|
import {
|
|
22
23
|
addMessage,
|
|
@@ -47,6 +48,20 @@ import { resolveVerificationSessionIntent } from "./verification-session-intent.
|
|
|
47
48
|
|
|
48
49
|
const log = getLogger("conversation-process");
|
|
49
50
|
|
|
51
|
+
/** Format the result of a forced compaction into a user-facing message. */
|
|
52
|
+
export function formatCompactResult(result: ContextWindowResult): string {
|
|
53
|
+
const fmt = (n: number) => n.toLocaleString("en-US");
|
|
54
|
+
if (!result.compacted) {
|
|
55
|
+
return `Context compaction skipped — ${result.reason ?? "nothing to compact"}.`;
|
|
56
|
+
}
|
|
57
|
+
const saved = result.previousEstimatedInputTokens - result.estimatedInputTokens;
|
|
58
|
+
return [
|
|
59
|
+
"Context Compacted\n",
|
|
60
|
+
`Tokens: ${fmt(result.previousEstimatedInputTokens)} → ${fmt(result.estimatedInputTokens)} (${fmt(saved)} saved)`,
|
|
61
|
+
`Messages: ${fmt(result.compactedMessages)} compacted`,
|
|
62
|
+
].join("\n");
|
|
63
|
+
}
|
|
64
|
+
|
|
50
65
|
/** Build a model_info event with fresh config data. */
|
|
51
66
|
export async function buildModelInfoEvent(): Promise<ServerMessage> {
|
|
52
67
|
return { type: "model_info", ...(await getModelInfo()) };
|
|
@@ -139,6 +154,8 @@ export interface ProcessConversationContext {
|
|
|
139
154
|
requestId?: string,
|
|
140
155
|
statusText?: string,
|
|
141
156
|
): void;
|
|
157
|
+
/** Force context compaction regardless of threshold/cooldown. */
|
|
158
|
+
forceCompact(): Promise<ContextWindowResult>;
|
|
142
159
|
}
|
|
143
160
|
|
|
144
161
|
function resolveQueuedTurnContext(
|
|
@@ -335,6 +352,7 @@ export async function drainQueue(
|
|
|
335
352
|
...(Object.keys(drainImageSourcePaths).length > 0
|
|
336
353
|
? { imageSourcePaths: drainImageSourcePaths }
|
|
337
354
|
: {}),
|
|
355
|
+
sentAt: next.sentAt,
|
|
338
356
|
};
|
|
339
357
|
const cleanUserMsg = createUserMessage(next.content, next.attachments);
|
|
340
358
|
const llmUserMsg = enrichMessageWithSourcePaths(
|
|
@@ -362,7 +380,7 @@ export async function drainQueue(
|
|
|
362
380
|
conversation.conversationId,
|
|
363
381
|
"assistant",
|
|
364
382
|
JSON.stringify(assistantMsg.content),
|
|
365
|
-
drainChannelMeta,
|
|
383
|
+
{ ...drainChannelMeta, sentAt: Date.now() },
|
|
366
384
|
);
|
|
367
385
|
conversation.messages.push(assistantMsg);
|
|
368
386
|
|
|
@@ -423,6 +441,82 @@ export async function drainQueue(
|
|
|
423
441
|
return;
|
|
424
442
|
}
|
|
425
443
|
|
|
444
|
+
// /compact — force context compaction, persist exchange, continue draining.
|
|
445
|
+
if (slashResult.kind === "compact") {
|
|
446
|
+
try {
|
|
447
|
+
const drainProvenance = provenanceFromTrustContext(
|
|
448
|
+
conversation.trustContext,
|
|
449
|
+
);
|
|
450
|
+
const drainChannelMeta = {
|
|
451
|
+
...drainProvenance,
|
|
452
|
+
...(queuedTurnCtx
|
|
453
|
+
? {
|
|
454
|
+
userMessageChannel: queuedTurnCtx.userMessageChannel,
|
|
455
|
+
assistantMessageChannel: queuedTurnCtx.assistantMessageChannel,
|
|
456
|
+
}
|
|
457
|
+
: {}),
|
|
458
|
+
...(queuedInterfaceCtx
|
|
459
|
+
? {
|
|
460
|
+
userMessageInterface: queuedInterfaceCtx.userMessageInterface,
|
|
461
|
+
assistantMessageInterface:
|
|
462
|
+
queuedInterfaceCtx.assistantMessageInterface,
|
|
463
|
+
}
|
|
464
|
+
: {}),
|
|
465
|
+
sentAt: next.sentAt,
|
|
466
|
+
};
|
|
467
|
+
const cleanUserMsg = createUserMessage(next.content, next.attachments);
|
|
468
|
+
await addMessage(
|
|
469
|
+
conversation.conversationId,
|
|
470
|
+
"user",
|
|
471
|
+
JSON.stringify(cleanUserMsg.content),
|
|
472
|
+
drainChannelMeta,
|
|
473
|
+
);
|
|
474
|
+
conversation.messages.push(cleanUserMsg);
|
|
475
|
+
|
|
476
|
+
conversation.emitActivityState(
|
|
477
|
+
"thinking",
|
|
478
|
+
"context_compacting",
|
|
479
|
+
"assistant_turn",
|
|
480
|
+
next.requestId,
|
|
481
|
+
);
|
|
482
|
+
const result = await conversation.forceCompact();
|
|
483
|
+
const responseText = formatCompactResult(result);
|
|
484
|
+
|
|
485
|
+
const assistantMsg = createAssistantMessage(responseText);
|
|
486
|
+
await addMessage(
|
|
487
|
+
conversation.conversationId,
|
|
488
|
+
"assistant",
|
|
489
|
+
JSON.stringify(assistantMsg.content),
|
|
490
|
+
{ ...drainChannelMeta, sentAt: Date.now() },
|
|
491
|
+
);
|
|
492
|
+
conversation.messages.push(assistantMsg);
|
|
493
|
+
|
|
494
|
+
next.onEvent({ type: "assistant_text_delta", text: responseText });
|
|
495
|
+
conversation.traceEmitter.emit(
|
|
496
|
+
"message_complete",
|
|
497
|
+
"Compact slash command handled",
|
|
498
|
+
{ requestId: next.requestId, status: "success" },
|
|
499
|
+
);
|
|
500
|
+
next.onEvent({
|
|
501
|
+
type: "message_complete",
|
|
502
|
+
conversationId: conversation.conversationId,
|
|
503
|
+
});
|
|
504
|
+
} catch (err) {
|
|
505
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
506
|
+
log.error(
|
|
507
|
+
{
|
|
508
|
+
err,
|
|
509
|
+
conversationId: conversation.conversationId,
|
|
510
|
+
requestId: next.requestId,
|
|
511
|
+
},
|
|
512
|
+
"Failed to execute /compact",
|
|
513
|
+
);
|
|
514
|
+
next.onEvent({ type: "error", message });
|
|
515
|
+
}
|
|
516
|
+
await drainQueue(conversation);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
|
|
426
520
|
const resolvedContent = slashResult.content;
|
|
427
521
|
|
|
428
522
|
// Guardian verification intent interception for queued messages.
|
|
@@ -455,7 +549,7 @@ export async function drainQueue(
|
|
|
455
549
|
resolvedContent,
|
|
456
550
|
next.attachments,
|
|
457
551
|
next.requestId,
|
|
458
|
-
next.metadata,
|
|
552
|
+
{ ...next.metadata, sentAt: next.sentAt },
|
|
459
553
|
next.displayContent,
|
|
460
554
|
);
|
|
461
555
|
} catch (err) {
|
|
@@ -780,6 +874,71 @@ export async function processMessage(
|
|
|
780
874
|
return persisted.id;
|
|
781
875
|
}
|
|
782
876
|
|
|
877
|
+
// /compact — force context compaction, persist exchange, return message ID.
|
|
878
|
+
if (slashResult.kind === "compact") {
|
|
879
|
+
conversation.processing = true;
|
|
880
|
+
try {
|
|
881
|
+
const pmTurnCtx = conversation.getTurnChannelContext();
|
|
882
|
+
const pmInterfaceCtx = conversation.getTurnInterfaceContext();
|
|
883
|
+
const pmProvenance = provenanceFromTrustContext(conversation.trustContext);
|
|
884
|
+
const pmChannelMeta = {
|
|
885
|
+
...pmProvenance,
|
|
886
|
+
...(pmTurnCtx
|
|
887
|
+
? {
|
|
888
|
+
userMessageChannel: pmTurnCtx.userMessageChannel,
|
|
889
|
+
assistantMessageChannel: pmTurnCtx.assistantMessageChannel,
|
|
890
|
+
}
|
|
891
|
+
: {}),
|
|
892
|
+
...(pmInterfaceCtx
|
|
893
|
+
? {
|
|
894
|
+
userMessageInterface: pmInterfaceCtx.userMessageInterface,
|
|
895
|
+
assistantMessageInterface: pmInterfaceCtx.assistantMessageInterface,
|
|
896
|
+
}
|
|
897
|
+
: {}),
|
|
898
|
+
};
|
|
899
|
+
const cleanUserMsg = createUserMessage(content, attachments);
|
|
900
|
+
const persisted = await addMessage(
|
|
901
|
+
conversation.conversationId,
|
|
902
|
+
"user",
|
|
903
|
+
JSON.stringify(cleanUserMsg.content),
|
|
904
|
+
pmChannelMeta,
|
|
905
|
+
);
|
|
906
|
+
conversation.messages.push(cleanUserMsg);
|
|
907
|
+
|
|
908
|
+
conversation.emitActivityState(
|
|
909
|
+
"thinking",
|
|
910
|
+
"context_compacting",
|
|
911
|
+
"assistant_turn",
|
|
912
|
+
requestId,
|
|
913
|
+
);
|
|
914
|
+
const result = await conversation.forceCompact();
|
|
915
|
+
const responseText = formatCompactResult(result);
|
|
916
|
+
|
|
917
|
+
const assistantMsg = createAssistantMessage(responseText);
|
|
918
|
+
await addMessage(
|
|
919
|
+
conversation.conversationId,
|
|
920
|
+
"assistant",
|
|
921
|
+
JSON.stringify(assistantMsg.content),
|
|
922
|
+
pmChannelMeta,
|
|
923
|
+
);
|
|
924
|
+
conversation.messages.push(assistantMsg);
|
|
925
|
+
|
|
926
|
+
onEvent({ type: "assistant_text_delta", text: responseText });
|
|
927
|
+
conversation.traceEmitter.emit(
|
|
928
|
+
"message_complete",
|
|
929
|
+
"Compact slash command handled",
|
|
930
|
+
{ requestId, status: "success" },
|
|
931
|
+
);
|
|
932
|
+
onEvent({
|
|
933
|
+
type: "message_complete",
|
|
934
|
+
conversationId: conversation.conversationId,
|
|
935
|
+
});
|
|
936
|
+
return persisted.id;
|
|
937
|
+
} finally {
|
|
938
|
+
conversation.processing = false;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
783
942
|
const resolvedContent = slashResult.content;
|
|
784
943
|
|
|
785
944
|
// Guardian verification intent interception — force direct guardian
|
|
@@ -31,6 +31,8 @@ export interface QueuedMessage {
|
|
|
31
31
|
isInteractive?: boolean;
|
|
32
32
|
/** Original user message text to persist to DB when recording intent stripping produced a different `content`. */
|
|
33
33
|
displayContent?: string;
|
|
34
|
+
/** Wall-clock time (ms since epoch) when the message was enqueued, used as the display timestamp. */
|
|
35
|
+
sentAt: number;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
/**
|
|
@@ -16,11 +16,11 @@ import {
|
|
|
16
16
|
type TurnInterfaceContext,
|
|
17
17
|
} from "../channels/types.js";
|
|
18
18
|
import { getAppDirPath, listAppFiles } from "../memory/app-store.js";
|
|
19
|
-
import { stripCommentLines } from "../prompts/system-prompt.js";
|
|
20
19
|
import type { Message } from "../providers/types.js";
|
|
21
20
|
import type { ActorTrustContext } from "../runtime/actor-trust-resolver.js";
|
|
22
21
|
import { channelStatusToMemberStatus } from "../runtime/routes/inbound-stages/acl-enforcement.js";
|
|
23
22
|
import { getWorkspacePromptPath } from "../util/platform.js";
|
|
23
|
+
import { stripCommentLines } from "../util/strip-comment-lines.js";
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Describes the capabilities of the channel through which the user is
|
|
@@ -487,28 +487,49 @@ export function readNowScratchpad(): string | null {
|
|
|
487
487
|
}
|
|
488
488
|
|
|
489
489
|
/**
|
|
490
|
-
*
|
|
491
|
-
*
|
|
490
|
+
* Insert NOW.md scratchpad content into the user message, after any
|
|
491
|
+
* injected context blocks (e.g. memory_context) but before the user's
|
|
492
|
+
* original content. This keeps the user's actual message as the last
|
|
493
|
+
* thing the model reads.
|
|
492
494
|
*/
|
|
493
495
|
export function injectNowScratchpad(
|
|
494
496
|
message: Message,
|
|
495
497
|
content: string,
|
|
496
498
|
): Message {
|
|
499
|
+
const scratchpadBlock = {
|
|
500
|
+
type: "text" as const,
|
|
501
|
+
text: `<NOW.md Always keep this up to date>\n${content}\n</NOW.md>`,
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
// Find insertion point: skip any leading injected-context text blocks
|
|
505
|
+
// (e.g. memory_context) so the scratchpad lands between injected context
|
|
506
|
+
// and the user's original content.
|
|
507
|
+
let insertIdx = 0;
|
|
508
|
+
for (let i = 0; i < message.content.length; i++) {
|
|
509
|
+
const block = message.content[i];
|
|
510
|
+
if (block.type === "text" && block.text.startsWith("<memory_context")) {
|
|
511
|
+
insertIdx = i + 1;
|
|
512
|
+
} else {
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
497
517
|
return {
|
|
498
518
|
...message,
|
|
499
519
|
content: [
|
|
500
|
-
...message.content,
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
text: `<now_scratchpad>\n${content}\n</now_scratchpad>`,
|
|
504
|
-
},
|
|
520
|
+
...message.content.slice(0, insertIdx),
|
|
521
|
+
scratchpadBlock,
|
|
522
|
+
...message.content.slice(insertIdx),
|
|
505
523
|
],
|
|
506
524
|
};
|
|
507
525
|
}
|
|
508
526
|
|
|
509
|
-
/** Strip `<
|
|
527
|
+
/** Strip `<NOW.md>` blocks injected by `injectNowScratchpad`. */
|
|
510
528
|
export function stripNowScratchpad(messages: Message[]): Message[] {
|
|
511
|
-
return stripUserTextBlocksByPrefix(messages, [
|
|
529
|
+
return stripUserTextBlocksByPrefix(messages, [
|
|
530
|
+
"<NOW.md Always keep this up to date>",
|
|
531
|
+
"<now_scratchpad>", // backward-compat: strip legacy blocks from pre-rename history
|
|
532
|
+
]);
|
|
512
533
|
}
|
|
513
534
|
|
|
514
535
|
/**
|
|
@@ -1034,7 +1055,8 @@ const RUNTIME_INJECTION_PREFIXES = [
|
|
|
1034
1055
|
"<active_workspace>",
|
|
1035
1056
|
"<active_dynamic_page>",
|
|
1036
1057
|
"<non_interactive_context>",
|
|
1037
|
-
"<
|
|
1058
|
+
"<NOW.md Always keep this up to date>",
|
|
1059
|
+
"<now_scratchpad>", // backward-compat: strip legacy blocks from pre-rename history
|
|
1038
1060
|
"<transport_hints>",
|
|
1039
1061
|
];
|
|
1040
1062
|
|
|
@@ -16,7 +16,8 @@ import type { PairingStore } from "./pairing-store.js";
|
|
|
16
16
|
|
|
17
17
|
export type SlashResolution =
|
|
18
18
|
| { kind: "passthrough"; content: string }
|
|
19
|
-
| { kind: "unknown"; message: string; qrFilename?: string }
|
|
19
|
+
| { kind: "unknown"; message: string; qrFilename?: string }
|
|
20
|
+
| { kind: "compact" };
|
|
20
21
|
|
|
21
22
|
// ── /pair command — module-level pairing context ────────────────────
|
|
22
23
|
|
|
@@ -129,6 +130,7 @@ function resolveStatusCommand(context: SlashContext): SlashResolution {
|
|
|
129
130
|
function resolveCommandsList(context?: SlashContext): string[] {
|
|
130
131
|
const fallbackLines = [
|
|
131
132
|
"/commands — List all available commands",
|
|
133
|
+
"/compact — Force context compaction immediately",
|
|
132
134
|
"/models — List all available models",
|
|
133
135
|
"/pair — Generate pairing info for connecting a mobile device",
|
|
134
136
|
];
|
|
@@ -141,6 +143,7 @@ function resolveCommandsList(context?: SlashContext): string[] {
|
|
|
141
143
|
if (context.userMessageInterface === "macos") {
|
|
142
144
|
return [
|
|
143
145
|
"/commands — List all available commands",
|
|
146
|
+
"/compact — Force context compaction immediately",
|
|
144
147
|
"/models — List all available models",
|
|
145
148
|
"/status — Show conversation status and context usage",
|
|
146
149
|
"/btw — Ask a side question while the assistant is working",
|
|
@@ -152,6 +155,7 @@ function resolveCommandsList(context?: SlashContext): string[] {
|
|
|
152
155
|
if (context.userMessageInterface === "ios") {
|
|
153
156
|
return [
|
|
154
157
|
"/commands — List all available commands",
|
|
158
|
+
"/compact — Force context compaction immediately",
|
|
155
159
|
"/models — List all available models",
|
|
156
160
|
"/status — Show conversation status and context usage",
|
|
157
161
|
"/btw — Ask a side question while the assistant is working",
|
|
@@ -161,6 +165,7 @@ function resolveCommandsList(context?: SlashContext): string[] {
|
|
|
161
165
|
|
|
162
166
|
return [
|
|
163
167
|
"/commands — List all available commands",
|
|
168
|
+
"/compact — Force context compaction immediately",
|
|
164
169
|
"/models — List all available models",
|
|
165
170
|
"/status — Show conversation status and context usage",
|
|
166
171
|
"/btw — Ask a side question while the assistant is working",
|
|
@@ -168,8 +173,9 @@ function resolveCommandsList(context?: SlashContext): string[] {
|
|
|
168
173
|
}
|
|
169
174
|
|
|
170
175
|
/**
|
|
171
|
-
* Resolve built-in slash commands (/models, /status, /commands, /pair).
|
|
172
|
-
* Returns `unknown` with a deterministic message,
|
|
176
|
+
* Resolve built-in slash commands (/models, /status, /commands, /compact, /pair).
|
|
177
|
+
* Returns `unknown` with a deterministic message, `compact` for forced compaction,
|
|
178
|
+
* or the (possibly rewritten) content as `passthrough`.
|
|
173
179
|
*/
|
|
174
180
|
export async function resolveSlash(
|
|
175
181
|
content: string,
|
|
@@ -209,6 +215,11 @@ export async function resolveSlash(
|
|
|
209
215
|
const pairResult = resolvePairCommand(content, context);
|
|
210
216
|
if (pairResult) return pairResult;
|
|
211
217
|
|
|
218
|
+
// Handle /compact command
|
|
219
|
+
if (trimmed === "/compact") {
|
|
220
|
+
return { kind: "compact" };
|
|
221
|
+
}
|
|
222
|
+
|
|
212
223
|
// Handle /status command
|
|
213
224
|
if (trimmed === "/status") {
|
|
214
225
|
if (!context) {
|
|
@@ -177,6 +177,8 @@ export function createToolExecutor(
|
|
|
177
177
|
ctx.surfaceActionRequestIds?.has(ctx.currentRequestId ?? "") ?? false,
|
|
178
178
|
requesterExternalUserId: ctx.trustContext?.requesterExternalUserId,
|
|
179
179
|
requesterChatId: ctx.trustContext?.requesterChatId,
|
|
180
|
+
requesterIdentifier: ctx.trustContext?.requesterIdentifier,
|
|
181
|
+
requesterDisplayName: ctx.trustContext?.requesterDisplayName,
|
|
180
182
|
channelPermissionChannelId:
|
|
181
183
|
ctx.trustContext?.sourceChannel === "slack"
|
|
182
184
|
? getBindingByConversation(ctx.conversationId)?.externalChatId
|
|
@@ -75,6 +75,26 @@ function extractAnthropicCacheCreation(
|
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Extract the speed indicator from Anthropic fast mode API responses.
|
|
80
|
+
* The API returns `usage.speed: "fast" | "standard"` when using the
|
|
81
|
+
* fast-mode beta. For multi-response arrays, returns "fast" if any
|
|
82
|
+
* response used fast mode.
|
|
83
|
+
*/
|
|
84
|
+
function extractAnthropicSpeed(
|
|
85
|
+
rawResponse: unknown,
|
|
86
|
+
): "fast" | "standard" | null {
|
|
87
|
+
const responses = Array.isArray(rawResponse) ? rawResponse : [rawResponse];
|
|
88
|
+
let foundStandard = false;
|
|
89
|
+
for (const response of responses) {
|
|
90
|
+
const rec = asRecord(response);
|
|
91
|
+
const usage = asRecord(rec?.usage);
|
|
92
|
+
if (usage?.speed === "fast") return "fast";
|
|
93
|
+
if (usage?.speed === "standard") foundStandard = true;
|
|
94
|
+
}
|
|
95
|
+
return foundStandard ? "standard" : null;
|
|
96
|
+
}
|
|
97
|
+
|
|
78
98
|
function resolveStructuredPricing(
|
|
79
99
|
providerName: string,
|
|
80
100
|
model: string,
|
|
@@ -105,6 +125,8 @@ export function recordUsage(
|
|
|
105
125
|
cacheCreationInputTokens = 0,
|
|
106
126
|
cacheReadInputTokens = 0,
|
|
107
127
|
rawResponse?: unknown,
|
|
128
|
+
llmCallCount = 1,
|
|
129
|
+
contextWindow?: { tokens: number; maxTokens: number },
|
|
108
130
|
): void {
|
|
109
131
|
if (inputTokens <= 0 && outputTokens <= 0) return;
|
|
110
132
|
|
|
@@ -120,15 +142,16 @@ export function recordUsage(
|
|
|
120
142
|
0,
|
|
121
143
|
);
|
|
122
144
|
|
|
145
|
+
const isAnthropic = ctx.providerName === "anthropic";
|
|
123
146
|
const pricingUsage: PricingUsage = {
|
|
124
147
|
directInputTokens,
|
|
125
148
|
outputTokens,
|
|
126
149
|
cacheCreationInputTokens: normalizedCacheCreationInputTokens,
|
|
127
150
|
cacheReadInputTokens: normalizedCacheReadInputTokens,
|
|
128
|
-
anthropicCacheCreation:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
151
|
+
anthropicCacheCreation: isAnthropic
|
|
152
|
+
? extractAnthropicCacheCreation(rawResponse)
|
|
153
|
+
: null,
|
|
154
|
+
speed: isAnthropic ? extractAnthropicSpeed(rawResponse) : null,
|
|
132
155
|
};
|
|
133
156
|
const pricing = resolveStructuredPricing(
|
|
134
157
|
ctx.providerName,
|
|
@@ -158,6 +181,10 @@ export function recordUsage(
|
|
|
158
181
|
totalOutputTokens: ctx.usageStats.outputTokens,
|
|
159
182
|
estimatedCost,
|
|
160
183
|
model,
|
|
184
|
+
...(contextWindow && {
|
|
185
|
+
contextWindowTokens: contextWindow.tokens,
|
|
186
|
+
contextWindowMaxTokens: contextWindow.maxTokens,
|
|
187
|
+
}),
|
|
161
188
|
});
|
|
162
189
|
|
|
163
190
|
// Dual-write: persist per-turn usage event to the new ledger table
|
|
@@ -174,6 +201,7 @@ export function recordUsage(
|
|
|
174
201
|
conversationId: ctx.conversationId,
|
|
175
202
|
runId: null,
|
|
176
203
|
requestId,
|
|
204
|
+
llmCallCount,
|
|
177
205
|
},
|
|
178
206
|
pricing,
|
|
179
207
|
);
|
|
@@ -21,8 +21,13 @@ import type {
|
|
|
21
21
|
TurnChannelContext,
|
|
22
22
|
TurnInterfaceContext,
|
|
23
23
|
} from "../channels/types.js";
|
|
24
|
+
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
24
25
|
import { getConfig } from "../config/loader.js";
|
|
25
|
-
import {
|
|
26
|
+
import type { Speed } from "../config/schemas/inference.js";
|
|
27
|
+
import {
|
|
28
|
+
ContextWindowManager,
|
|
29
|
+
type ContextWindowResult,
|
|
30
|
+
} from "../context/window-manager.js";
|
|
26
31
|
import type { CesClient } from "../credential-execution/client.js";
|
|
27
32
|
import { EventBus } from "../events/bus.js";
|
|
28
33
|
import type { AssistantDomainEvents } from "../events/domain-events.js";
|
|
@@ -38,6 +43,7 @@ import {
|
|
|
38
43
|
import { registerToolTraceListener } from "../events/tool-trace-listener.js";
|
|
39
44
|
import { getHookManager } from "../hooks/manager.js";
|
|
40
45
|
import { resolveCanonicalGuardianRequest } from "../memory/canonical-guardian-store.js";
|
|
46
|
+
import { updateConversationContextWindow } from "../memory/conversation-crud.js";
|
|
41
47
|
import { PermissionPrompter } from "../permissions/prompter.js";
|
|
42
48
|
import { SecretPrompter } from "../permissions/secret-prompter.js";
|
|
43
49
|
import { patternMatchesCandidate } from "../permissions/trust-store.js";
|
|
@@ -263,6 +269,7 @@ export class Conversation {
|
|
|
263
269
|
broadcastToAllClients?: (msg: ServerMessage) => void,
|
|
264
270
|
memoryPolicy?: ConversationMemoryPolicy,
|
|
265
271
|
sharedCesClient?: CesClient,
|
|
272
|
+
speedOverride?: Speed,
|
|
266
273
|
) {
|
|
267
274
|
this.conversationId = conversationId;
|
|
268
275
|
this.systemPrompt = systemPrompt;
|
|
@@ -383,6 +390,9 @@ export class Conversation {
|
|
|
383
390
|
return resolved;
|
|
384
391
|
};
|
|
385
392
|
|
|
393
|
+
const fastModeEnabled = isAssistantFeatureFlagEnabled("fast-mode", config);
|
|
394
|
+
const resolvedSpeed = speedOverride ?? config.speed;
|
|
395
|
+
|
|
386
396
|
this.agentLoop = new AgentLoop(
|
|
387
397
|
provider,
|
|
388
398
|
systemPrompt,
|
|
@@ -391,6 +401,9 @@ export class Conversation {
|
|
|
391
401
|
maxInputTokens: config.contextWindow.maxInputTokens,
|
|
392
402
|
thinking: config.thinking,
|
|
393
403
|
effort: config.effort,
|
|
404
|
+
...(fastModeEnabled && resolvedSpeed === "fast"
|
|
405
|
+
? { speed: resolvedSpeed }
|
|
406
|
+
: {}),
|
|
394
407
|
},
|
|
395
408
|
toolDefs.length > 0 ? toolDefs : undefined,
|
|
396
409
|
toolDefs.length > 0 ? toolExecutor : undefined,
|
|
@@ -873,6 +886,25 @@ export class Conversation {
|
|
|
873
886
|
this.sendToClient(msg);
|
|
874
887
|
}
|
|
875
888
|
|
|
889
|
+
async forceCompact(): Promise<ContextWindowResult> {
|
|
890
|
+
const result = await this.contextWindowManager.maybeCompact(
|
|
891
|
+
this.messages,
|
|
892
|
+
this.abortController?.signal ?? undefined,
|
|
893
|
+
{ force: true, lastCompactedAt: this.contextCompactedAt ?? undefined },
|
|
894
|
+
);
|
|
895
|
+
if (result.compacted) {
|
|
896
|
+
this.messages = result.messages;
|
|
897
|
+
this.contextCompactedMessageCount += result.compactedPersistedMessages;
|
|
898
|
+
this.contextCompactedAt = Date.now();
|
|
899
|
+
updateConversationContextWindow(
|
|
900
|
+
this.conversationId,
|
|
901
|
+
result.summaryText,
|
|
902
|
+
this.contextCompactedMessageCount,
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
return result;
|
|
906
|
+
}
|
|
907
|
+
|
|
876
908
|
setChannelCapabilities(caps: ChannelCapabilities | null): void {
|
|
877
909
|
this.channelCapabilities = caps ?? undefined;
|
|
878
910
|
}
|