@vellumai/assistant 0.5.16 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +69 -16
- package/Dockerfile +2 -5
- package/bun.lock +6 -2
- package/docker-entrypoint.sh +32 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/knip.json +2 -1
- package/openapi.yaml +1198 -83
- package/package.json +5 -1
- package/src/__tests__/actor-token-service.test.ts +68 -0
- package/src/__tests__/agent-loop.test.ts +0 -32
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/anthropic-provider.test.ts +217 -98
- package/src/__tests__/app-compiler.test.ts +120 -0
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +47 -1
- package/src/__tests__/app-source-watcher.test.ts +159 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/call-conversation-messages.test.ts +2 -6
- package/src/__tests__/call-domain.test.ts +2 -6
- package/src/__tests__/call-pointer-messages.test.ts +2 -14
- package/src/__tests__/call-recovery.test.ts +2 -6
- package/src/__tests__/call-routes-http.test.ts +2 -6
- package/src/__tests__/call-store.test.ts +2 -6
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
- package/src/__tests__/canonical-guardian-store.test.ts +2 -6
- package/src/__tests__/channel-delivery-store.test.ts +2 -6
- package/src/__tests__/channel-retry-sweep.test.ts +2 -6
- package/src/__tests__/checker.test.ts +63 -9
- package/src/__tests__/clawhub.test.ts +54 -24
- package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
- package/src/__tests__/config-schema.test.ts +6 -1
- package/src/__tests__/config-set-platform-guard.test.ts +302 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
- package/src/__tests__/contacts-tools.test.ts +31 -0
- package/src/__tests__/context-overflow-reducer.test.ts +86 -0
- package/src/__tests__/context-token-estimator.test.ts +175 -10
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +13 -6
- package/src/__tests__/conversation-agent-loop.test.ts +13 -51
- package/src/__tests__/conversation-attachments.test.ts +2 -6
- package/src/__tests__/conversation-attention-store.test.ts +2 -6
- package/src/__tests__/conversation-clear-safety.test.ts +2 -6
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
- package/src/__tests__/conversation-disk-view.test.ts +2 -6
- package/src/__tests__/conversation-error.test.ts +33 -2
- package/src/__tests__/conversation-fork-crud.test.ts +2 -6
- package/src/__tests__/conversation-history-web-search.test.ts +6 -1
- package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
- package/src/__tests__/conversation-media-retry.test.ts +91 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
- package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
- package/src/__tests__/conversation-starter-routes.test.ts +20 -11
- package/src/__tests__/conversation-store.test.ts +2 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
- package/src/__tests__/conversation-usage.test.ts +2 -6
- package/src/__tests__/conversation-wipe.test.ts +13 -414
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
- package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
- package/src/__tests__/credential-security-e2e.test.ts +2 -0
- package/src/__tests__/date-context.test.ts +76 -210
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
- package/src/__tests__/file-list-tool.test.ts +219 -0
- package/src/__tests__/first-greeting.test.ts +1 -1
- package/src/__tests__/followup-tools.test.ts +2 -6
- package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
- package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
- package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
- package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
- package/src/__tests__/guardian-action-store.test.ts +2 -6
- package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
- package/src/__tests__/guardian-dispatch.test.ts +2 -6
- package/src/__tests__/guardian-grant-minting.test.ts +2 -14
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
- package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
- package/src/__tests__/guardian-routing-state.test.ts +2 -6
- package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
- package/src/__tests__/heartbeat-service.test.ts +180 -3
- package/src/__tests__/identity-routes.test.ts +328 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
- package/src/__tests__/injection-block.test.ts +178 -0
- package/src/__tests__/install-meta.test.ts +506 -0
- package/src/__tests__/install-skill-routing.test.ts +293 -0
- package/src/__tests__/invite-redemption-service.test.ts +2 -6
- package/src/__tests__/invite-routes-http.test.ts +2 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +17 -28
- package/src/__tests__/list-messages-attachments.test.ts +2 -6
- package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
- package/src/__tests__/llm-context-normalization.test.ts +18 -18
- package/src/__tests__/llm-context-route-provider.test.ts +103 -6
- package/src/__tests__/llm-request-log-turn-query.test.ts +164 -6
- package/src/__tests__/llm-usage-store.test.ts +2 -6
- package/src/__tests__/log-export-workspace.test.ts +74 -111
- package/src/__tests__/managed-store.test.ts +38 -11
- package/src/__tests__/mcp-abort-signal.test.ts +5 -0
- package/src/__tests__/mcp-client-auth.test.ts +5 -0
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
- package/src/__tests__/memory-recall-log-store.test.ts +134 -6
- package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
- package/src/__tests__/migration-export-streaming.test.ts +304 -0
- package/src/__tests__/migration-import-commit-http.test.ts +11 -10
- package/src/__tests__/mock-fetch.ts +87 -0
- package/src/__tests__/non-member-access-request.test.ts +2 -6
- package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/notification-guardian-path.test.ts +2 -6
- package/src/__tests__/oauth-cli.test.ts +364 -2
- package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
- package/src/__tests__/onboarding-template-contract.test.ts +62 -14
- package/src/__tests__/outlook-attachments.test.ts +301 -0
- package/src/__tests__/outlook-automation-tools.test.ts +425 -0
- package/src/__tests__/outlook-categories.test.ts +212 -0
- package/src/__tests__/outlook-client-automation.test.ts +246 -0
- package/src/__tests__/outlook-compose-tools.test.ts +325 -0
- package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
- package/src/__tests__/outlook-email-watcher.test.ts +322 -0
- package/src/__tests__/outlook-follow-up.test.ts +196 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
- package/src/__tests__/outlook-trash.test.ts +77 -0
- package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
- package/src/__tests__/parser.test.ts +32 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
- package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
- package/src/__tests__/permission-mode-sse.test.ts +418 -0
- package/src/__tests__/permission-mode-store.test.ts +277 -0
- package/src/__tests__/permission-mode.test.ts +101 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
- package/src/__tests__/platform-callback-registration.test.ts +4 -4
- package/src/__tests__/playbook-execution.test.ts +76 -80
- package/src/__tests__/playbook-tools.test.ts +5 -7
- package/src/__tests__/profiler-routes.test.ts +502 -0
- package/src/__tests__/profiler-run-store.test.ts +441 -0
- package/src/__tests__/provider-error-scenarios.test.ts +21 -0
- package/src/__tests__/proxy-approval-callback.test.ts +4 -75
- package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
- package/src/__tests__/registry.test.ts +3 -3
- package/src/__tests__/require-fresh-approval.test.ts +64 -2
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
- package/src/__tests__/runtime-events-sse.test.ts +2 -6
- package/src/__tests__/sandbox-host-parity.test.ts +5 -4
- package/src/__tests__/schedule-store.test.ts +2 -6
- package/src/__tests__/schedule-tools.test.ts +2 -6
- package/src/__tests__/scheduler-recurrence.test.ts +1 -5
- package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
- package/src/__tests__/scoped-approval-grants.test.ts +2 -6
- package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
- package/src/__tests__/search-skills-unified.test.ts +422 -0
- package/src/__tests__/secret-onetime-send.test.ts +2 -0
- package/src/__tests__/send-endpoint-busy.test.ts +44 -9
- package/src/__tests__/sequence-store.test.ts +2 -6
- package/src/__tests__/server-history-render.test.ts +2 -6
- package/src/__tests__/set-permission-mode.test.ts +274 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
- package/src/__tests__/skill-feature-flags.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +23 -11
- package/src/__tests__/skill-memory.test.ts +2 -741
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -6
- package/src/__tests__/strip-memory-injections.test.ts +187 -0
- package/src/__tests__/subagent-detail.test.ts +84 -0
- package/src/__tests__/subagent-disposal.test.ts +308 -0
- package/src/__tests__/subagent-manager-notify.test.ts +19 -10
- package/src/__tests__/subagent-notify-parent.test.ts +390 -0
- package/src/__tests__/subagent-role-registry.test.ts +108 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
- package/src/__tests__/subagent-tools.test.ts +464 -4
- package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
- package/src/__tests__/task-compiler.test.ts +2 -6
- package/src/__tests__/task-management-tools.test.ts +2 -6
- package/src/__tests__/task-memory-cleanup.test.ts +185 -241
- package/src/__tests__/task-runner.test.ts +2 -6
- package/src/__tests__/task-scheduler.test.ts +2 -6
- package/src/__tests__/terminal-tools.test.ts +17 -27
- package/src/__tests__/test-preload.ts +7 -0
- package/src/__tests__/tool-approval-handler.test.ts +2 -6
- package/src/__tests__/tool-executor.test.ts +4 -26
- package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +277 -0
- package/src/__tests__/top-level-renderer.test.ts +10 -13
- package/src/__tests__/trust-store.test.ts +1 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +118 -8
- package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
- package/src/__tests__/trusted-contact-verification.test.ts +2 -6
- package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
- package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
- package/src/__tests__/usage-routes.test.ts +2 -6
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/voice-invite-redemption.test.ts +2 -6
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
- package/src/__tests__/voice-session-bridge.test.ts +2 -6
- package/src/__tests__/volume-security-guard.test.ts +2 -0
- package/src/__tests__/workspace-lifecycle.test.ts +29 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
- package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
- package/src/__tests__/workspace-policy.test.ts +1 -1
- package/src/agent/attachments.ts +7 -2
- package/src/agent/image-optimize.ts +165 -0
- package/src/agent/loop.ts +7 -15
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/bundler/app-compiler.ts +179 -2
- package/src/bundler/package-resolver.ts +3 -5
- package/src/cli/__tests__/notifications.test.ts +1 -2
- package/src/cli/__tests__/run-assistant-command.ts +29 -0
- package/src/cli/commands/__tests__/email-download.test.ts +245 -0
- package/src/cli/commands/__tests__/email-list.test.ts +192 -0
- package/src/cli/commands/__tests__/email-register.test.ts +186 -0
- package/src/cli/commands/__tests__/email-send.test.ts +291 -0
- package/src/cli/commands/__tests__/email-status.test.ts +181 -0
- package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
- package/src/cli/commands/__tests__/routes.test.ts +562 -0
- package/src/cli/commands/avatar.ts +3 -3
- package/src/cli/commands/config.ts +26 -13
- package/src/cli/commands/conversations.ts +1 -8
- package/src/cli/commands/doctor.ts +2 -2
- package/src/cli/commands/email.ts +584 -835
- package/src/cli/commands/memory.ts +37 -84
- package/src/cli/commands/notifications.ts +7 -2
- package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
- package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
- package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
- package/src/cli/commands/oauth/connect.ts +25 -11
- package/src/cli/commands/oauth/mode.ts +7 -0
- package/src/cli/commands/oauth/shared.ts +39 -3
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
- package/src/cli/commands/platform/index.ts +16 -16
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +218 -36
- package/src/cli/commands/trust.ts +2 -2
- package/src/cli/lib/daemon-credential-client.ts +2 -3
- package/src/cli/program.ts +2 -0
- package/src/cli.ts +1 -120
- package/src/config/bundled-skills/acp/TOOLS.json +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
- package/src/config/bundled-skills/contacts/SKILL.md +0 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
- package/src/config/bundled-skills/gmail/SKILL.md +4 -12
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
- package/src/config/bundled-skills/messaging/SKILL.md +17 -18
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
- package/src/config/bundled-skills/outlook/SKILL.md +189 -0
- package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
- package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
- package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
- package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
- package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
- package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
- package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
- package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
- package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
- package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
- package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
- package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
- package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
- package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
- package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
- package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
- package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
- package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
- package/src/config/bundled-skills/schedule/SKILL.md +22 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
- package/src/config/bundled-skills/slack/SKILL.md +3 -7
- package/src/config/bundled-skills/subagent/SKILL.md +43 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
- package/src/config/bundled-tool-registry.ts +56 -4
- package/src/config/env-registry.ts +78 -8
- package/src/config/feature-flag-registry.json +38 -125
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/filing.ts +51 -0
- package/src/config/schemas/heartbeat.ts +15 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/security.ts +14 -0
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/config/skills.ts +18 -7
- package/src/context/token-estimator.ts +25 -18
- package/src/context/window-manager.ts +6 -2
- package/src/credential-execution/process-manager.ts +3 -1
- package/src/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +79 -1
- package/src/daemon/context-overflow-reducer.ts +46 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +143 -82
- package/src/daemon/conversation-agent-loop.ts +236 -108
- package/src/daemon/conversation-error.ts +31 -8
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +36 -9
- package/src/daemon/conversation-media-retry.ts +85 -7
- package/src/daemon/conversation-notifiers.ts +4 -1
- package/src/daemon/conversation-process.ts +13 -7
- package/src/daemon/conversation-runtime-assembly.ts +305 -306
- package/src/daemon/conversation-tool-setup.ts +44 -14
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +59 -2
- package/src/daemon/daemon-control.ts +8 -2
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +4 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +85 -17
- package/src/daemon/handlers/skills.ts +416 -209
- package/src/daemon/lifecycle.ts +212 -131
- package/src/daemon/main.ts +5 -1
- package/src/daemon/message-types/conversations.ts +29 -7
- package/src/daemon/message-types/messages.ts +12 -2
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +6 -0
- package/src/daemon/message-types/skills.ts +97 -36
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/providers-setup.ts +5 -0
- package/src/daemon/server.ts +100 -11
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +50 -8
- package/src/export/transcript-formatter.ts +148 -0
- package/src/filing/filing-service.ts +228 -0
- package/src/heartbeat/heartbeat-service.ts +97 -7
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/runner.ts +15 -38
- package/src/inbound/platform-callback-registration.ts +14 -14
- package/src/mcp/client.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +149 -27
- package/src/memory/admin.ts +42 -75
- package/src/memory/app-store.ts +69 -0
- package/src/memory/conversation-bootstrap.ts +3 -1
- package/src/memory/conversation-crud.ts +211 -288
- package/src/memory/conversation-group-migration.ts +157 -0
- package/src/memory/conversation-queries.ts +61 -13
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/db-init.ts +194 -361
- package/src/memory/embed.ts +73 -0
- package/src/memory/embedding-backend.ts +8 -14
- package/src/memory/embedding-runtime-manager.ts +12 -114
- package/src/memory/fingerprint.ts +2 -2
- package/src/memory/graph/bootstrap.ts +521 -0
- package/src/memory/graph/capability-seed.ts +449 -0
- package/src/memory/graph/consolidation.ts +725 -0
- package/src/memory/graph/conversation-graph-memory.ts +659 -0
- package/src/memory/graph/decay.test.ts +208 -0
- package/src/memory/graph/decay.ts +195 -0
- package/src/memory/graph/extraction-job.ts +74 -0
- package/src/memory/graph/extraction.test.ts +936 -0
- package/src/memory/graph/extraction.ts +1297 -0
- package/src/memory/graph/graph-memory-state-store.ts +37 -0
- package/src/memory/graph/graph-search.ts +280 -0
- package/src/memory/graph/image-ref-utils.ts +29 -0
- package/src/memory/graph/injection.test.ts +513 -0
- package/src/memory/graph/injection.ts +469 -0
- package/src/memory/graph/inspect.ts +543 -0
- package/src/memory/graph/narrative.ts +267 -0
- package/src/memory/graph/pattern-scan.ts +269 -0
- package/src/memory/graph/retriever.ts +1111 -0
- package/src/memory/graph/scoring.test.ts +548 -0
- package/src/memory/graph/scoring.ts +232 -0
- package/src/memory/graph/serendipity.ts +65 -0
- package/src/memory/graph/store.test.ts +1098 -0
- package/src/memory/graph/store.ts +838 -0
- package/src/memory/graph/tool-handlers.ts +301 -0
- package/src/memory/graph/tools.ts +97 -0
- package/src/memory/graph/triggers.test.ts +487 -0
- package/src/memory/graph/triggers.ts +223 -0
- package/src/memory/graph/types.ts +295 -0
- package/src/memory/group-crud.ts +191 -0
- package/src/memory/indexer.ts +37 -19
- package/src/memory/job-handlers/cleanup.ts +32 -42
- package/src/memory/job-handlers/conversation-starters.ts +91 -53
- package/src/memory/job-handlers/embedding.ts +5 -31
- package/src/memory/job-handlers/index-maintenance.ts +23 -11
- package/src/memory/job-handlers/summarization.ts +32 -17
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +21 -31
- package/src/memory/jobs-worker.ts +180 -129
- package/src/memory/llm-request-log-store.ts +96 -12
- package/src/memory/memory-recall-log-store.ts +49 -5
- package/src/memory/message-content.ts +1 -0
- package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
- package/src/memory/migrations/203-drop-memory-items-tables.ts +55 -0
- package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
- package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
- package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
- package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
- package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
- package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
- package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
- package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
- package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
- package/src/memory/migrations/index.ts +12 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/qdrant-client.ts +44 -17
- package/src/memory/schema/conversations.ts +14 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/infrastructure.ts +8 -1
- package/src/memory/schema/memory-core.ts +0 -51
- package/src/memory/schema/memory-graph.ts +154 -0
- package/src/memory/search/semantic.ts +47 -91
- package/src/memory/task-memory-cleanup.ts +58 -61
- package/src/messaging/providers/outlook/adapter.ts +8 -1
- package/src/messaging/providers/outlook/client.ts +299 -0
- package/src/messaging/providers/outlook/types.ts +118 -0
- package/src/notifications/adapters/macos.ts +1 -0
- package/src/notifications/copy-composer.ts +95 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/notifications/signal.ts +16 -0
- package/src/oauth/seed-providers.ts +2 -1
- package/src/permissions/checker.ts +36 -4
- package/src/permissions/defaults.ts +4 -4
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/workspace-policy.ts +10 -1
- package/src/playbooks/playbook-compiler.ts +19 -18
- package/src/playbooks/types.ts +4 -3
- package/src/prompts/system-prompt.ts +62 -36
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +70 -165
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +25 -4
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +136 -220
- package/src/providers/gemini/client.ts +1 -1
- package/src/providers/openai/client.ts +1 -1
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +19 -3
- package/src/runtime/actor-trust-resolver.ts +5 -1
- package/src/runtime/auth/route-policy.ts +30 -0
- package/src/runtime/guardian-reply-router.ts +5 -1
- package/src/runtime/http-server.ts +55 -5
- package/src/runtime/http-types.ts +12 -1
- package/src/runtime/middleware/auth.ts +20 -0
- package/src/runtime/migrations/vbundle-builder.ts +389 -3
- package/src/runtime/migrations/vbundle-importer.ts +8 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
- package/src/runtime/routes/app-management-routes.ts +1 -11
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
- package/src/runtime/routes/archive-utils.ts +29 -0
- package/src/runtime/routes/attachment-routes.test.ts +106 -0
- package/src/runtime/routes/attachment-routes.ts +106 -16
- package/src/runtime/routes/avatar-routes.ts +2 -9
- package/src/runtime/routes/brain-graph-routes.ts +21 -22
- package/src/runtime/routes/btw-routes.ts +22 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
- package/src/runtime/routes/conversation-management-routes.ts +3 -14
- package/src/runtime/routes/conversation-query-routes.ts +49 -3
- package/src/runtime/routes/conversation-routes.ts +264 -44
- package/src/runtime/routes/conversation-starter-routes.ts +2 -2
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/global-search-routes.ts +21 -19
- package/src/runtime/routes/group-routes.ts +207 -0
- package/src/runtime/routes/guardian-action-routes.ts +21 -10
- package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
- package/src/runtime/routes/heartbeat-routes.ts +4 -10
- package/src/runtime/routes/identity-routes.ts +53 -18
- package/src/runtime/routes/inbound-message-handler.ts +19 -0
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
- package/src/runtime/routes/llm-context-normalization.ts +14 -10
- package/src/runtime/routes/log-export-routes.ts +23 -275
- package/src/runtime/routes/memory-item-routes.test.ts +170 -247
- package/src/runtime/routes/memory-item-routes.ts +341 -388
- package/src/runtime/routes/migration-routes.ts +18 -7
- package/src/runtime/routes/profiler-routes.ts +350 -0
- package/src/runtime/routes/schedule-routes.ts +28 -11
- package/src/runtime/routes/settings-routes.ts +95 -8
- package/src/runtime/routes/skills-routes.ts +103 -37
- package/src/runtime/routes/subagents-routes.ts +28 -7
- package/src/runtime/routes/user-route-dispatcher.ts +223 -0
- package/src/runtime/routes/user-routes.ts +41 -0
- package/src/runtime/routes/work-items-routes.test.ts +2 -6
- package/src/runtime/routes/workspace-routes.ts +0 -1
- package/src/schedule/schedule-store.ts +30 -0
- package/src/schedule/scheduler.ts +52 -18
- package/src/security/oauth2.ts +1 -1
- package/src/security/secure-keys.ts +4 -8
- package/src/shared/provider-env-vars.ts +19 -0
- package/src/skills/catalog-cache.ts +5 -0
- package/src/skills/catalog-install.ts +25 -16
- package/src/skills/clawhub.ts +134 -154
- package/src/skills/install-meta.ts +208 -0
- package/src/skills/managed-store.ts +29 -18
- package/src/skills/skill-memory.ts +12 -229
- package/src/skills/skillssh-registry.ts +19 -17
- package/src/subagent/index.ts +13 -3
- package/src/subagent/manager.ts +308 -29
- package/src/subagent/types.ts +68 -0
- package/src/tasks/task-runner.ts +7 -5
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
- package/src/tools/apps/executors.ts +29 -4
- package/src/tools/browser/runtime-check.ts +3 -1
- package/src/tools/filesystem/list.ts +93 -0
- package/src/tools/memory/register.ts +63 -46
- package/src/tools/permission-checker.ts +85 -1
- package/src/tools/registry.ts +4 -0
- package/src/tools/schedule/create.ts +3 -0
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +6 -0
- package/src/tools/shared/filesystem/errors.ts +5 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
- package/src/tools/shared/filesystem/image-read.ts +22 -85
- package/src/tools/shared/filesystem/types.ts +17 -0
- package/src/tools/shared/shell-output.ts +31 -2
- package/src/tools/subagent/abort.ts +12 -2
- package/src/tools/subagent/message.ts +9 -2
- package/src/tools/subagent/notify-parent.ts +79 -0
- package/src/tools/subagent/read.ts +29 -8
- package/src/tools/subagent/resolve.ts +21 -0
- package/src/tools/subagent/spawn.ts +2 -0
- package/src/tools/subagent/status.ts +11 -1
- package/src/tools/system/avatar-generator.ts +3 -3
- package/src/tools/system/register.ts +23 -0
- package/src/tools/system/set-permission-mode.ts +103 -0
- package/src/tools/terminal/parser.ts +30 -5
- package/src/tools/terminal/safe-env.ts +17 -1
- package/src/tools/tool-manifest.ts +9 -3
- package/src/tools/types.ts +2 -0
- package/src/util/browser.ts +25 -10
- package/src/util/bun-runtime.ts +172 -0
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- package/src/watcher/providers/outlook-calendar.ts +343 -0
- package/src/watcher/providers/outlook.ts +198 -0
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
- package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
- package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
- package/src/workspace/migrations/029-seed-pkb.ts +84 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/top-level-renderer.ts +5 -9
- package/src/__tests__/cli-memory.test.ts +0 -372
- package/src/__tests__/clipboard.test.ts +0 -88
- package/src/__tests__/context-memory-e2e.test.ts +0 -415
- package/src/__tests__/journal-context.test.ts +0 -268
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
- package/src/__tests__/memory-query-builder.test.ts +0 -59
- package/src/__tests__/memory-recall-quality.test.ts +0 -1046
- package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
- package/src/__tests__/memory-regressions.test.ts +0 -3696
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
- package/src/cli/cli-memory.ts +0 -176
- package/src/daemon/conversation-memory.ts +0 -207
- package/src/memory/conversation-starters-cadence.ts +0 -74
- package/src/memory/items-extractor.ts +0 -860
- package/src/memory/job-handlers/batch-extraction.ts +0 -753
- package/src/memory/job-handlers/extraction.ts +0 -40
- package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
- package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
- package/src/memory/journal-memory.ts +0 -224
- package/src/memory/query-builder.ts +0 -47
- package/src/memory/query-expansion.ts +0 -83
- package/src/memory/retriever.test.ts +0 -1592
- package/src/memory/retriever.ts +0 -1331
- package/src/memory/search/formatting.test.ts +0 -140
- package/src/memory/search/formatting.ts +0 -262
- package/src/memory/search/mmr.ts +0 -139
- package/src/memory/search/ranking.ts +0 -15
- package/src/memory/search/staleness.ts +0 -40
- package/src/memory/search/tier-classifier.ts +0 -18
- package/src/memory/search/types.ts +0 -121
- package/src/prompts/journal-context.ts +0 -154
- package/src/tools/memory/definitions.ts +0 -69
- package/src/tools/memory/handlers.test.ts +0 -562
- package/src/tools/memory/handlers.ts +0 -434
- package/src/util/clipboard.ts +0 -34
package/src/config/schema.ts
CHANGED
|
@@ -3,6 +3,11 @@ import { z } from "zod";
|
|
|
3
3
|
import { getDataDir } from "../util/platform.js";
|
|
4
4
|
|
|
5
5
|
// Re-export all domain schemas
|
|
6
|
+
export type { PermissionMode } from "../permissions/permission-mode.js";
|
|
7
|
+
export {
|
|
8
|
+
DEFAULT_PERMISSION_MODE,
|
|
9
|
+
PermissionModeSchema,
|
|
10
|
+
} from "../permissions/permission-mode.js";
|
|
6
11
|
export type { AcpAgentConfig, AcpConfig } from "./acp-schema.js";
|
|
7
12
|
export { AcpAgentConfigSchema, AcpConfigSchema } from "./acp-schema.js";
|
|
8
13
|
export type {
|
|
@@ -143,6 +148,7 @@ export type {
|
|
|
143
148
|
export {
|
|
144
149
|
PermissionsConfigSchema,
|
|
145
150
|
SecretDetectionConfigSchema,
|
|
151
|
+
VALID_PERMISSIONS_MODES,
|
|
146
152
|
} from "./schemas/security.js";
|
|
147
153
|
export type {
|
|
148
154
|
ImageGenerationService,
|
|
@@ -197,6 +203,7 @@ import {
|
|
|
197
203
|
WhatsAppConfigSchema,
|
|
198
204
|
} from "./schemas/channels.js";
|
|
199
205
|
import { ElevenLabsConfigSchema } from "./schemas/elevenlabs.js";
|
|
206
|
+
import { FilingConfigSchema } from "./schemas/filing.js";
|
|
200
207
|
import { FishAudioConfigSchema } from "./schemas/fish-audio.js";
|
|
201
208
|
import { HeartbeatConfigSchema } from "./schemas/heartbeat.js";
|
|
202
209
|
import {
|
|
@@ -272,6 +279,7 @@ export const AssistantConfigSchema = z
|
|
|
272
279
|
.describe(
|
|
273
280
|
"Custom pricing overrides for specific provider/model combinations",
|
|
274
281
|
),
|
|
282
|
+
filing: FilingConfigSchema.default(FilingConfigSchema.parse({})),
|
|
275
283
|
heartbeat: HeartbeatConfigSchema.default(HeartbeatConfigSchema.parse({})),
|
|
276
284
|
journal: JournalConfigSchema.default(JournalConfigSchema.parse({})),
|
|
277
285
|
mcp: McpConfigSchema.default(McpConfigSchema.parse({})),
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { SpeedSchema } from "./inference.js";
|
|
4
|
+
|
|
5
|
+
export const FilingConfigSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
enabled: z
|
|
8
|
+
.boolean({ error: "filing.enabled must be a boolean" })
|
|
9
|
+
.default(true)
|
|
10
|
+
.describe(
|
|
11
|
+
"Whether periodic PKB filing is enabled — processes buffer.md into topic files and reviews knowledge base organization",
|
|
12
|
+
),
|
|
13
|
+
intervalMs: z
|
|
14
|
+
.number({ error: "filing.intervalMs must be a number" })
|
|
15
|
+
.int("filing.intervalMs must be an integer")
|
|
16
|
+
.positive("filing.intervalMs must be a positive integer")
|
|
17
|
+
.default(4 * 3_600_000)
|
|
18
|
+
.describe("Time between filing runs in milliseconds"),
|
|
19
|
+
speed: SpeedSchema.default("standard").describe(
|
|
20
|
+
"Inference speed mode for filing conversations",
|
|
21
|
+
),
|
|
22
|
+
activeHoursStart: z
|
|
23
|
+
.number({ error: "filing.activeHoursStart must be a number" })
|
|
24
|
+
.int("filing.activeHoursStart must be an integer")
|
|
25
|
+
.min(0, "filing.activeHoursStart must be >= 0")
|
|
26
|
+
.max(23, "filing.activeHoursStart must be <= 23")
|
|
27
|
+
.default(8)
|
|
28
|
+
.describe("Hour of the day (0-23) when filing runs begin"),
|
|
29
|
+
activeHoursEnd: z
|
|
30
|
+
.number({ error: "filing.activeHoursEnd must be a number" })
|
|
31
|
+
.int("filing.activeHoursEnd must be an integer")
|
|
32
|
+
.min(0, "filing.activeHoursEnd must be >= 0")
|
|
33
|
+
.max(23, "filing.activeHoursEnd must be <= 23")
|
|
34
|
+
.default(22)
|
|
35
|
+
.describe("Hour of the day (0-23) when filing runs stop"),
|
|
36
|
+
})
|
|
37
|
+
.describe(
|
|
38
|
+
"Periodic PKB (personal knowledge base) filing — processes the buffer into topic files and maintains knowledge organization",
|
|
39
|
+
)
|
|
40
|
+
.superRefine((config, ctx) => {
|
|
41
|
+
if (config.activeHoursStart === config.activeHoursEnd) {
|
|
42
|
+
ctx.addIssue({
|
|
43
|
+
code: z.ZodIssueCode.custom,
|
|
44
|
+
path: ["activeHoursEnd"],
|
|
45
|
+
message:
|
|
46
|
+
"filing.activeHoursStart and filing.activeHoursEnd must not be equal (would create an empty window)",
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export type FilingConfig = z.infer<typeof FilingConfigSchema>;
|
|
@@ -12,7 +12,7 @@ export const HeartbeatConfigSchema = z
|
|
|
12
12
|
.number({ error: "heartbeat.intervalMs must be a number" })
|
|
13
13
|
.int("heartbeat.intervalMs must be an integer")
|
|
14
14
|
.positive("heartbeat.intervalMs must be a positive integer")
|
|
15
|
-
.default(3_600_000)
|
|
15
|
+
.default(6 * 3_600_000)
|
|
16
16
|
.describe("Time between heartbeat checks in milliseconds"),
|
|
17
17
|
speed: SpeedSchema.default("standard").describe(
|
|
18
18
|
"Inference speed mode for heartbeat conversations — defaults to standard to avoid inheriting the global fast mode multiplier",
|
|
@@ -22,35 +22,38 @@ export const HeartbeatConfigSchema = z
|
|
|
22
22
|
.int("heartbeat.activeHoursStart must be an integer")
|
|
23
23
|
.min(0, "heartbeat.activeHoursStart must be >= 0")
|
|
24
24
|
.max(23, "heartbeat.activeHoursStart must be <= 23")
|
|
25
|
-
.
|
|
25
|
+
.nullable()
|
|
26
|
+
.default(8)
|
|
26
27
|
.describe(
|
|
27
|
-
"Hour of the day (0-23) when heartbeat checks begin
|
|
28
|
+
"Hour of the day (0-23) when heartbeat checks begin, or null to disable active hours restriction",
|
|
28
29
|
),
|
|
29
30
|
activeHoursEnd: z
|
|
30
31
|
.number({ error: "heartbeat.activeHoursEnd must be a number" })
|
|
31
32
|
.int("heartbeat.activeHoursEnd must be an integer")
|
|
32
33
|
.min(0, "heartbeat.activeHoursEnd must be >= 0")
|
|
33
34
|
.max(23, "heartbeat.activeHoursEnd must be <= 23")
|
|
34
|
-
.
|
|
35
|
+
.nullable()
|
|
36
|
+
.default(22)
|
|
35
37
|
.describe(
|
|
36
|
-
"Hour of the day (0-23) when heartbeat checks stop
|
|
38
|
+
"Hour of the day (0-23) when heartbeat checks stop, or null to disable active hours restriction",
|
|
37
39
|
),
|
|
38
40
|
})
|
|
39
41
|
.describe("Periodic heartbeat configuration for health monitoring")
|
|
40
42
|
.superRefine((config, ctx) => {
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
if (
|
|
43
|
+
const startNull = config.activeHoursStart == null;
|
|
44
|
+
const endNull = config.activeHoursEnd == null;
|
|
45
|
+
if (startNull !== endNull) {
|
|
44
46
|
ctx.addIssue({
|
|
45
47
|
code: z.ZodIssueCode.custom,
|
|
46
|
-
path: [
|
|
48
|
+
path: [startNull ? "activeHoursStart" : "activeHoursEnd"],
|
|
47
49
|
message:
|
|
48
|
-
"heartbeat.activeHoursStart and heartbeat.activeHoursEnd must both be set or both be
|
|
50
|
+
"heartbeat.activeHoursStart and heartbeat.activeHoursEnd must both be set or both be null",
|
|
49
51
|
});
|
|
52
|
+
return;
|
|
50
53
|
}
|
|
51
54
|
if (
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
config.activeHoursStart != null &&
|
|
56
|
+
config.activeHoursEnd != null &&
|
|
54
57
|
config.activeHoursStart === config.activeHoursEnd
|
|
55
58
|
) {
|
|
56
59
|
ctx.addIssue({
|
|
@@ -72,6 +72,18 @@ export const MemoryCleanupConfigSchema = z
|
|
|
72
72
|
.describe(
|
|
73
73
|
"Number of days to retain conversation data before cleanup (0 disables pruning)",
|
|
74
74
|
),
|
|
75
|
+
llmRequestLogRetentionMs: z
|
|
76
|
+
.number({
|
|
77
|
+
error: "memory.cleanup.llmRequestLogRetentionMs must be a number",
|
|
78
|
+
})
|
|
79
|
+
.int("memory.cleanup.llmRequestLogRetentionMs must be an integer")
|
|
80
|
+
.nonnegative(
|
|
81
|
+
"memory.cleanup.llmRequestLogRetentionMs must be non-negative",
|
|
82
|
+
)
|
|
83
|
+
.default(7 * 24 * 60 * 60 * 1000)
|
|
84
|
+
.describe(
|
|
85
|
+
"Retention period for LLM request/response logs in milliseconds (0 disables pruning)",
|
|
86
|
+
),
|
|
75
87
|
})
|
|
76
88
|
.describe("Automatic memory cleanup and garbage collection settings");
|
|
77
89
|
|
|
@@ -56,6 +56,14 @@ export const UiConfigSchema = z
|
|
|
56
56
|
.describe(
|
|
57
57
|
"IANA timezone identifier for displaying dates and times (e.g. 'America/New_York')",
|
|
58
58
|
),
|
|
59
|
+
greetingModelIntent: z
|
|
60
|
+
.enum(["latency-optimized", "quality-optimized"], {
|
|
61
|
+
error: "ui.greetingModelIntent must be 'latency-optimized' or 'quality-optimized'",
|
|
62
|
+
})
|
|
63
|
+
.default("latency-optimized")
|
|
64
|
+
.describe(
|
|
65
|
+
"Model intent for empty-state greeting generation (latency-optimized = fast/small model, quality-optimized = primary model)",
|
|
66
|
+
),
|
|
59
67
|
})
|
|
60
68
|
.describe("User interface display settings");
|
|
61
69
|
|
|
@@ -79,6 +79,20 @@ export const PermissionsConfigSchema = z
|
|
|
79
79
|
.describe(
|
|
80
80
|
"Permission mode — 'strict' requires explicit approval for all operations, 'workspace' allows operations within the workspace",
|
|
81
81
|
),
|
|
82
|
+
askBeforeActing: z
|
|
83
|
+
.boolean({
|
|
84
|
+
error: "permissions.askBeforeActing must be a boolean",
|
|
85
|
+
})
|
|
86
|
+
.default(true)
|
|
87
|
+
.describe("Whether the assistant should check in before taking actions"),
|
|
88
|
+
hostAccess: z
|
|
89
|
+
.boolean({
|
|
90
|
+
error: "permissions.hostAccess must be a boolean",
|
|
91
|
+
})
|
|
92
|
+
.default(false)
|
|
93
|
+
.describe(
|
|
94
|
+
"Whether the assistant can execute commands on the host machine without prompting",
|
|
95
|
+
),
|
|
82
96
|
})
|
|
83
97
|
.describe("Permission enforcement mode for tool operations");
|
|
84
98
|
|
|
@@ -36,7 +36,7 @@ export const TimeoutConfigSchema = z
|
|
|
36
36
|
.number({ error: "timeouts.providerStreamTimeoutSec must be a number" })
|
|
37
37
|
.finite("timeouts.providerStreamTimeoutSec must be finite")
|
|
38
38
|
.positive("timeouts.providerStreamTimeoutSec must be a positive number")
|
|
39
|
-
.default(
|
|
39
|
+
.default(1800)
|
|
40
40
|
.describe(
|
|
41
41
|
"Timeout for waiting on the LLM provider's streaming response (seconds)",
|
|
42
42
|
),
|
package/src/config/skills.ts
CHANGED
|
@@ -73,7 +73,6 @@ export interface SkillSummary {
|
|
|
73
73
|
bundled?: boolean;
|
|
74
74
|
icon?: string;
|
|
75
75
|
emoji?: string;
|
|
76
|
-
homepage?: string;
|
|
77
76
|
source: SkillSource;
|
|
78
77
|
/** Parsed tool manifest metadata, if the skill has a valid TOOLS.json. */
|
|
79
78
|
toolManifest?: SkillToolManifestMeta;
|
|
@@ -962,8 +961,9 @@ function isEscapingSymlink(filePath: string, rootDir: string): boolean {
|
|
|
962
961
|
/**
|
|
963
962
|
* Check for a `references/` subdirectory within a skill directory and return
|
|
964
963
|
* a formatted listing of available `.md` reference files with full absolute
|
|
965
|
-
* paths. Returns `null` if no references exist. Files are
|
|
966
|
-
*
|
|
964
|
+
* paths. Returns `null` if no references exist. Files are discovered
|
|
965
|
+
* recursively through subdirectories and listed alphabetically within each
|
|
966
|
+
* directory level. Non-`.md` files are ignored. Symlinks that resolve
|
|
967
967
|
* outside the skill directory are skipped.
|
|
968
968
|
*/
|
|
969
969
|
export function listReferenceFiles(directoryPath: string): string | null {
|
|
@@ -977,10 +977,21 @@ export function listReferenceFiles(directoryPath: string): string | null {
|
|
|
977
977
|
return null;
|
|
978
978
|
}
|
|
979
979
|
|
|
980
|
-
const entries = readdirSync(refsDir);
|
|
980
|
+
const entries = readdirSync(refsDir, { recursive: true }) as string[];
|
|
981
981
|
const mdFiles = entries
|
|
982
982
|
.filter((f) => f.toLowerCase().endsWith(".md"))
|
|
983
|
-
.filter((f) =>
|
|
983
|
+
.filter((f) => {
|
|
984
|
+
// Check the file itself
|
|
985
|
+
if (isEscapingSymlink(join(refsDir, f), directoryPath)) return false;
|
|
986
|
+
// Check all intermediate directory components (e.g. for "sub/dir/file.md"
|
|
987
|
+
// check "sub" and "sub/dir") to prevent traversal through symlinked dirs.
|
|
988
|
+
const parts = f.split("/");
|
|
989
|
+
for (let i = 1; i < parts.length; i++) {
|
|
990
|
+
const ancestor = join(refsDir, ...parts.slice(0, i));
|
|
991
|
+
if (isEscapingSymlink(ancestor, directoryPath)) return false;
|
|
992
|
+
}
|
|
993
|
+
return true;
|
|
994
|
+
})
|
|
984
995
|
.sort((a, b) => a.localeCompare(b));
|
|
985
996
|
|
|
986
997
|
if (mdFiles.length === 0) return null;
|
|
@@ -991,8 +1002,8 @@ export function listReferenceFiles(directoryPath: string): string | null {
|
|
|
991
1002
|
"The following reference files are available in this skill's directory. Use `file_read` to load any that are relevant to the current task:",
|
|
992
1003
|
"",
|
|
993
1004
|
];
|
|
994
|
-
for (const
|
|
995
|
-
lines.push(`- \`${join(refsDir,
|
|
1005
|
+
for (const filepath of mdFiles) {
|
|
1006
|
+
lines.push(`- \`${join(refsDir, filepath)}\` (references/${filepath})`);
|
|
996
1007
|
}
|
|
997
1008
|
|
|
998
1009
|
return lines.join("\n");
|
|
@@ -9,7 +9,6 @@ const CHARS_PER_TOKEN = 4;
|
|
|
9
9
|
const MESSAGE_OVERHEAD_TOKENS = 4;
|
|
10
10
|
const TEXT_BLOCK_OVERHEAD_TOKENS = 2;
|
|
11
11
|
const TOOL_BLOCK_OVERHEAD_TOKENS = 16;
|
|
12
|
-
const IMAGE_BLOCK_TOKENS = 1024;
|
|
13
12
|
const IMAGE_BLOCK_OVERHEAD_TOKENS = 16;
|
|
14
13
|
const FILE_BLOCK_OVERHEAD_TOKENS = 48;
|
|
15
14
|
const WEB_SEARCH_RESULT_TOKENS = 800;
|
|
@@ -20,12 +19,15 @@ const GEMINI_INLINE_FILE_MIME_TYPES = new Set(["application/pdf"]);
|
|
|
20
19
|
// Anthropic scales images to fit within 1568x1568 maintaining aspect ratio,
|
|
21
20
|
// then charges ~(width * height) / 750 tokens.
|
|
22
21
|
const ANTHROPIC_IMAGE_MAX_DIMENSION = 1568;
|
|
22
|
+
// Anthropic caps images at ~1.2 megapixels in addition to the 1568px dimension limit.
|
|
23
|
+
// Images exceeding this are further scaled down. The docs state images above ~1,600 tokens
|
|
24
|
+
// are resized. 1,200,000 / 750 = 1,600 tokens, matching the documented threshold.
|
|
25
|
+
// Reference table (max sizes that won't be resized):
|
|
26
|
+
// 1:1 → 1092x1092 (~1,590 tokens) 1:2 → 784x1568 (~1,639 tokens)
|
|
27
|
+
// See: https://platform.claude.com/docs/en/build-with-claude/vision#evaluate-image-size
|
|
28
|
+
const ANTHROPIC_IMAGE_MAX_PIXELS = 1_200_000;
|
|
23
29
|
const ANTHROPIC_IMAGE_TOKENS_PER_PIXEL = 1 / 750;
|
|
24
|
-
const ANTHROPIC_IMAGE_MAX_TOKENS =
|
|
25
|
-
ANTHROPIC_IMAGE_MAX_DIMENSION *
|
|
26
|
-
ANTHROPIC_IMAGE_MAX_DIMENSION *
|
|
27
|
-
ANTHROPIC_IMAGE_TOKENS_PER_PIXEL,
|
|
28
|
-
); // ~3,277 tokens
|
|
30
|
+
const ANTHROPIC_IMAGE_MAX_TOKENS = 1_600;
|
|
29
31
|
|
|
30
32
|
// Anthropic renders each PDF page as an image (~1,568 tokens at standard
|
|
31
33
|
// resolution) plus any extracted text. Typical PDF pages are 50-150 KB.
|
|
@@ -85,17 +87,23 @@ function estimateFileDataTokens(
|
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
function estimateAnthropicImageTokens(width: number, height: number): number {
|
|
88
|
-
// Scale
|
|
89
|
-
const
|
|
90
|
+
// Step 1: Scale to fit within 1568px bounding box
|
|
91
|
+
const dimScale = Math.min(
|
|
90
92
|
1,
|
|
91
93
|
ANTHROPIC_IMAGE_MAX_DIMENSION / Math.max(width, height),
|
|
92
94
|
);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
)
|
|
95
|
+
let scaledWidth = Math.round(width * dimScale);
|
|
96
|
+
let scaledHeight = Math.round(height * dimScale);
|
|
97
|
+
|
|
98
|
+
// Step 2: Scale further if exceeds megapixel budget
|
|
99
|
+
const pixels = scaledWidth * scaledHeight;
|
|
100
|
+
if (pixels > ANTHROPIC_IMAGE_MAX_PIXELS) {
|
|
101
|
+
const mpScale = Math.sqrt(ANTHROPIC_IMAGE_MAX_PIXELS / pixels);
|
|
102
|
+
scaledWidth = Math.round(scaledWidth * mpScale);
|
|
103
|
+
scaledHeight = Math.round(scaledHeight * mpScale);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return Math.ceil(scaledWidth * scaledHeight * ANTHROPIC_IMAGE_TOKENS_PER_PIXEL);
|
|
99
107
|
}
|
|
100
108
|
|
|
101
109
|
function estimateImageTokens(
|
|
@@ -143,11 +151,10 @@ export function estimateContentBlockTokens(
|
|
|
143
151
|
return tokens;
|
|
144
152
|
}
|
|
145
153
|
case "image":
|
|
146
|
-
return
|
|
147
|
-
IMAGE_BLOCK_TOKENS,
|
|
154
|
+
return (
|
|
148
155
|
IMAGE_BLOCK_OVERHEAD_TOKENS +
|
|
149
|
-
|
|
150
|
-
|
|
156
|
+
estimateTextTokens(block.source.media_type) +
|
|
157
|
+
estimateImageTokens(block, options)
|
|
151
158
|
);
|
|
152
159
|
case "file":
|
|
153
160
|
return (
|
|
@@ -628,14 +628,18 @@ export class ContextWindowManager {
|
|
|
628
628
|
const excess = totalTokens - maxTranscriptTokens;
|
|
629
629
|
if (blockTokens > excess && result[i].type === "text") {
|
|
630
630
|
// Truncate this block to shed exactly the excess tokens.
|
|
631
|
-
|
|
631
|
+
// Subtract the cost of the "[...truncated] " prefix so the final
|
|
632
|
+
// block (prefix + kept text) stays within budget.
|
|
633
|
+
const truncationPrefix = "[...truncated] ";
|
|
634
|
+
const prefixTokens = estimateTextTokens(truncationPrefix);
|
|
635
|
+
const keepTokens = Math.max(1, blockTokens - excess - prefixTokens);
|
|
632
636
|
const text = (result[i] as { type: "text"; text: string }).text;
|
|
633
637
|
// Approximate: 1 token ≈ 4 characters for truncation purposes.
|
|
634
638
|
const keepChars = Math.max(1, Math.floor(keepTokens * 4));
|
|
635
639
|
const truncatedText = text.slice(-keepChars);
|
|
636
640
|
const truncatedBlock: ContentBlock = {
|
|
637
641
|
type: "text",
|
|
638
|
-
text:
|
|
642
|
+
text: `${truncationPrefix}${truncatedText}`,
|
|
639
643
|
};
|
|
640
644
|
const newBlockTokens = estimateBlockTokens(truncatedBlock);
|
|
641
645
|
droppedTokens += blockTokens - newBlockTokens;
|
|
@@ -31,6 +31,7 @@ import { StringDecoder } from "node:string_decoder";
|
|
|
31
31
|
import type { Subprocess } from "bun";
|
|
32
32
|
|
|
33
33
|
import type { AssistantConfig } from "../config/schema.js";
|
|
34
|
+
import { ensureBun } from "../util/bun-runtime.js";
|
|
34
35
|
import { getLogger } from "../util/logger.js";
|
|
35
36
|
import type { CesTransport } from "./client.js";
|
|
36
37
|
import {
|
|
@@ -266,8 +267,9 @@ export function createCesProcessManager(
|
|
|
266
267
|
"Spawning CES child process from source",
|
|
267
268
|
);
|
|
268
269
|
|
|
270
|
+
const bunPath = await ensureBun();
|
|
269
271
|
const proc = Bun.spawn({
|
|
270
|
-
cmd: [
|
|
272
|
+
cmd: [bunPath, "run", discovery.sourcePath],
|
|
271
273
|
stdin: "pipe",
|
|
272
274
|
stdout: "pipe",
|
|
273
275
|
stderr: "ignore",
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem watcher for app source directories.
|
|
3
|
+
*
|
|
4
|
+
* Watches the apps root directory recursively using fs.watch (FSEvents on
|
|
5
|
+
* macOS). When a source file changes, debounces per app ID and calls the
|
|
6
|
+
* provided callback so the server can recompile + refresh surfaces.
|
|
7
|
+
*
|
|
8
|
+
* This catches ALL modification sources (file_edit, file_write, bash, etc.)
|
|
9
|
+
* without relying on individual tool hooks.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, type FSWatcher, watch } from "node:fs";
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
getAppsDir,
|
|
16
|
+
resolveAppIdByDirName,
|
|
17
|
+
} from "../memory/app-store.js";
|
|
18
|
+
import { DebouncerMap } from "../util/debounce.js";
|
|
19
|
+
import { getLogger } from "../util/logger.js";
|
|
20
|
+
|
|
21
|
+
const log = getLogger("app-source-watcher");
|
|
22
|
+
|
|
23
|
+
const APP_REFRESH_DEBOUNCE_MS = 500;
|
|
24
|
+
|
|
25
|
+
export type AppSourceChangeCallback = (appId: string) => void;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resolve app ID from a relative path within the apps directory.
|
|
29
|
+
* Returns null if the path is not an app source file (e.g. dist/, records/).
|
|
30
|
+
*/
|
|
31
|
+
function resolveAppIdFromRelPath(relPath: string): string | null {
|
|
32
|
+
const slashIdx = relPath.indexOf("/");
|
|
33
|
+
if (slashIdx === -1) return null; // file directly in apps/ (e.g. .json definition)
|
|
34
|
+
|
|
35
|
+
const dirName = relPath.slice(0, slashIdx);
|
|
36
|
+
const innerPath = relPath.slice(slashIdx + 1);
|
|
37
|
+
|
|
38
|
+
// Skip non-source directories (include bare directory names for fs.watch events)
|
|
39
|
+
if (
|
|
40
|
+
innerPath === "records" || innerPath.startsWith("records/") ||
|
|
41
|
+
innerPath === "dist" || innerPath.startsWith("dist/")
|
|
42
|
+
) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return resolveAppIdByDirName(dirName);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class AppSourceWatcher {
|
|
50
|
+
private watcher: FSWatcher | null = null;
|
|
51
|
+
private debouncer = new DebouncerMap({
|
|
52
|
+
defaultDelayMs: APP_REFRESH_DEBOUNCE_MS,
|
|
53
|
+
maxEntries: 50,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
start(onChange: AppSourceChangeCallback): void {
|
|
57
|
+
let appsDir: string;
|
|
58
|
+
try {
|
|
59
|
+
appsDir = getAppsDir();
|
|
60
|
+
} catch {
|
|
61
|
+
log.warn("Could not resolve apps directory; app source watching disabled");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!existsSync(appsDir)) {
|
|
66
|
+
log.info("Apps directory does not exist yet; skipping source watcher");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
this.watcher = watch(appsDir, { recursive: true }, (_eventType, filename) => {
|
|
72
|
+
if (!filename) return;
|
|
73
|
+
|
|
74
|
+
const appId = resolveAppIdFromRelPath(filename);
|
|
75
|
+
if (!appId) return;
|
|
76
|
+
|
|
77
|
+
this.debouncer.schedule(`app:${appId}`, () => {
|
|
78
|
+
onChange(appId);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
} catch (err) {
|
|
82
|
+
log.warn({ err }, "Failed to watch apps directory; source watching disabled");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
stop(): void {
|
|
87
|
+
this.debouncer.cancelAll();
|
|
88
|
+
if (this.watcher) {
|
|
89
|
+
this.watcher.close();
|
|
90
|
+
this.watcher = null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -28,7 +28,10 @@ import { handleUserMessageSignal } from "../signals/user-message.js";
|
|
|
28
28
|
import { DebouncerMap } from "../util/debounce.js";
|
|
29
29
|
import { getLogger } from "../util/logger.js";
|
|
30
30
|
import {
|
|
31
|
+
AVATAR_IMAGE_FILENAME,
|
|
32
|
+
getAvatarDir,
|
|
31
33
|
getSignalsDir,
|
|
34
|
+
getSoundsDir,
|
|
32
35
|
getWorkspaceDir,
|
|
33
36
|
getWorkspaceSkillsDir,
|
|
34
37
|
} from "../util/platform.js";
|
|
@@ -110,7 +113,12 @@ export class ConfigWatcher {
|
|
|
110
113
|
* files change and conversations need to be evicted for reload.
|
|
111
114
|
* `onIdentityChanged` is called when IDENTITY.md changes on disk.
|
|
112
115
|
*/
|
|
113
|
-
start(
|
|
116
|
+
start(
|
|
117
|
+
onConversationEvict: () => void,
|
|
118
|
+
onIdentityChanged?: () => void,
|
|
119
|
+
onSoundsConfigChanged?: () => void,
|
|
120
|
+
onAvatarChanged?: () => void,
|
|
121
|
+
): void {
|
|
114
122
|
const workspaceDir = getWorkspaceDir();
|
|
115
123
|
|
|
116
124
|
const workspaceHandlers: Record<string, () => void> = {
|
|
@@ -175,6 +183,13 @@ export class ConfigWatcher {
|
|
|
175
183
|
"workspace directory for config/prompt changes",
|
|
176
184
|
);
|
|
177
185
|
|
|
186
|
+
if (onSoundsConfigChanged) {
|
|
187
|
+
this.startSoundsWatcher(onSoundsConfigChanged);
|
|
188
|
+
}
|
|
189
|
+
if (onAvatarChanged) {
|
|
190
|
+
this.startAvatarWatcher(onAvatarChanged);
|
|
191
|
+
}
|
|
192
|
+
|
|
178
193
|
this.startFeatureFlagsWatcher();
|
|
179
194
|
this.startSignalsWatcher();
|
|
180
195
|
this.startSkillsWatchers(onConversationEvict);
|
|
@@ -188,6 +203,69 @@ export class ConfigWatcher {
|
|
|
188
203
|
this.watchers = [];
|
|
189
204
|
}
|
|
190
205
|
|
|
206
|
+
private startSoundsWatcher(onSoundsConfigChanged: () => void): void {
|
|
207
|
+
const soundsDir = getSoundsDir();
|
|
208
|
+
try {
|
|
209
|
+
if (!existsSync(soundsDir)) {
|
|
210
|
+
mkdirSync(soundsDir, { recursive: true });
|
|
211
|
+
}
|
|
212
|
+
} catch {
|
|
213
|
+
// If we can't create it, watching will also fail — handled below.
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
const watcher = watch(soundsDir, (_eventType, filename) => {
|
|
218
|
+
if (!filename) return;
|
|
219
|
+
this.debounceTimers.schedule("file:sounds", () => {
|
|
220
|
+
log.info(
|
|
221
|
+
{ file: String(filename) },
|
|
222
|
+
"Sounds directory changed, notifying clients",
|
|
223
|
+
);
|
|
224
|
+
onSoundsConfigChanged();
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
this.watchers.push(watcher);
|
|
228
|
+
log.info({ dir: soundsDir }, "Watching sounds directory for changes");
|
|
229
|
+
} catch (err) {
|
|
230
|
+
log.warn(
|
|
231
|
+
{ err, dir: soundsDir },
|
|
232
|
+
"Failed to watch sounds directory. Sound config changes will require a restart.",
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private startAvatarWatcher(onAvatarChanged: () => void): void {
|
|
238
|
+
const avatarDir = getAvatarDir();
|
|
239
|
+
try {
|
|
240
|
+
if (!existsSync(avatarDir)) {
|
|
241
|
+
mkdirSync(avatarDir, { recursive: true });
|
|
242
|
+
}
|
|
243
|
+
} catch {
|
|
244
|
+
// If we can't create it, watching will also fail — handled below.
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
const watcher = watch(avatarDir, (_eventType, filename) => {
|
|
249
|
+
if (!filename) return;
|
|
250
|
+
if (String(filename) !== AVATAR_IMAGE_FILENAME) return;
|
|
251
|
+
this.debounceTimers.schedule("file:avatar", () => {
|
|
252
|
+
log.info(
|
|
253
|
+
{ file: String(filename) },
|
|
254
|
+
"Avatar image changed, notifying clients",
|
|
255
|
+
);
|
|
256
|
+
onAvatarChanged();
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
this.watchers.push(watcher);
|
|
260
|
+
log.info({ dir: avatarDir }, "Watching avatar directory for changes");
|
|
261
|
+
} catch (err) {
|
|
262
|
+
log.warn(
|
|
263
|
+
{ err, dir: avatarDir },
|
|
264
|
+
"Failed to watch avatar directory. Avatar changes will require a restart.",
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
191
269
|
private startFeatureFlagsWatcher(): void {
|
|
192
270
|
const protectedDir = process.env.GATEWAY_SECURITY_DIR
|
|
193
271
|
? process.env.GATEWAY_SECURITY_DIR
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import type { ContextWindowConfig } from "../config/types.js";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
estimateContentBlockTokens,
|
|
22
|
+
estimatePromptTokens,
|
|
23
|
+
} from "../context/token-estimator.js";
|
|
21
24
|
import { truncateToolResultsAcrossHistory } from "../context/tool-result-truncation.js";
|
|
22
25
|
import type {
|
|
23
26
|
ContextWindowCompactOptions,
|
|
@@ -26,6 +29,7 @@ import type {
|
|
|
26
29
|
import type { Message } from "../providers/types.js";
|
|
27
30
|
import {
|
|
28
31
|
countMediaBlocks,
|
|
32
|
+
estimateUnconditionalStubTokens,
|
|
29
33
|
stripMediaPayloadsForRetry,
|
|
30
34
|
} from "./conversation-media-retry.js";
|
|
31
35
|
import type { InjectionMode } from "./conversation-runtime-assembly.js";
|
|
@@ -240,7 +244,47 @@ function applyMediaStubbing(
|
|
|
240
244
|
let nextMessages = messages;
|
|
241
245
|
|
|
242
246
|
if (mediaCount > 0) {
|
|
243
|
-
|
|
247
|
+
// Compute the token budget available for media content.
|
|
248
|
+
const totalTokens = estimatePromptTokens(messages, config.systemPrompt, {
|
|
249
|
+
providerName: config.providerName,
|
|
250
|
+
toolTokenBudget: config.toolTokenBudget,
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Sum tokens for all image and file blocks (top-level and nested in tool_result).
|
|
254
|
+
let mediaTokens = 0;
|
|
255
|
+
for (const msg of messages) {
|
|
256
|
+
for (const block of msg.content) {
|
|
257
|
+
if (block.type === "image" || block.type === "file") {
|
|
258
|
+
mediaTokens += estimateContentBlockTokens(block, {
|
|
259
|
+
providerName: config.providerName,
|
|
260
|
+
});
|
|
261
|
+
} else if (block.type === "tool_result" && block.contentBlocks) {
|
|
262
|
+
for (const cb of block.contentBlocks) {
|
|
263
|
+
if (cb.type === "image" || cb.type === "file") {
|
|
264
|
+
mediaTokens += estimateContentBlockTokens(cb, {
|
|
265
|
+
providerName: config.providerName,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const nonMediaTokens = totalTokens - mediaTokens;
|
|
274
|
+
|
|
275
|
+
// Account for the token cost of text stubs that replace unconditionally
|
|
276
|
+
// stubbed media (non-latest-user images/files, tool_result-nested media).
|
|
277
|
+
// Without this adjustment the budget is systematically over-allocated.
|
|
278
|
+
const estimatedStubTokens = estimateUnconditionalStubTokens(messages, {
|
|
279
|
+
providerName: config.providerName,
|
|
280
|
+
});
|
|
281
|
+
const adjustedNonMediaTokens = nonMediaTokens + estimatedStubTokens;
|
|
282
|
+
const mediaTokenBudget = Math.max(0, config.targetTokens - adjustedNonMediaTokens);
|
|
283
|
+
|
|
284
|
+
const stripped = stripMediaPayloadsForRetry(messages, {
|
|
285
|
+
mediaTokenBudget,
|
|
286
|
+
providerName: config.providerName,
|
|
287
|
+
});
|
|
244
288
|
if (stripped.modified) {
|
|
245
289
|
nextMessages = stripped.messages;
|
|
246
290
|
}
|