@vellumai/assistant 0.5.1 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +163 -54
- package/docs/architecture/integrations.md +62 -67
- package/docs/credential-execution-service.md +3 -3
- package/docs/skills.md +100 -0
- package/package.json +1 -1
- package/src/__tests__/agent-loop.test.ts +111 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +3 -4
- package/src/__tests__/app-builder-tool-scripts.test.ts +13 -151
- package/src/__tests__/app-dir-path-guard.test.ts +78 -0
- package/src/__tests__/app-executors.test.ts +1 -291
- package/src/__tests__/app-git-history.test.ts +4 -4
- package/src/__tests__/app-routes-csp.test.ts +1 -0
- package/src/__tests__/app-store-dir-names.test.ts +426 -0
- package/src/__tests__/attachments-store.test.ts +169 -21
- package/src/__tests__/attachments.test.ts +115 -1
- package/src/__tests__/btw-routes.test.ts +1 -0
- package/src/__tests__/canonical-guardian-store.test.ts +38 -0
- package/src/__tests__/channel-reply-delivery.test.ts +55 -0
- package/src/__tests__/checker.test.ts +54 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
- package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -1
- package/src/__tests__/config-schema-cmd.test.ts +68 -21
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +156 -5
- package/src/__tests__/conversation-agent-loop.test.ts +297 -2
- package/src/__tests__/conversation-attachments.test.ts +17 -19
- package/src/__tests__/conversation-disk-view-integration.test.ts +277 -0
- package/src/__tests__/conversation-disk-view.test.ts +810 -0
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +551 -0
- package/src/__tests__/conversation-fork-route.test.ts +386 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-key-store-disk-view.test.ts +130 -0
- package/src/__tests__/conversation-media-retry.test.ts +8 -2
- package/src/__tests__/conversation-memory-dirty-tail.test.ts +150 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +7 -0
- package/src/__tests__/conversation-queue.test.ts +36 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
- package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
- package/src/__tests__/conversation-skill-tools.test.ts +4 -9
- package/src/__tests__/conversation-slash-commands.test.ts +149 -0
- package/src/__tests__/conversation-store.test.ts +24 -21
- package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
- package/src/__tests__/conversation-title-service.test.ts +137 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/conversation-wipe.test.ts +226 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
- package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/credential-vault-unit.test.ts +5 -10
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
- package/src/__tests__/db-memory-archive-migration.test.ts +372 -0
- package/src/__tests__/db-memory-brief-state-migration.test.ts +213 -0
- package/src/__tests__/db-memory-reducer-checkpoints.test.ts +273 -0
- package/src/__tests__/diagnostics-export.test.ts +70 -1
- package/src/__tests__/first-greeting.test.ts +80 -0
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
- package/src/__tests__/history-repair.test.ts +32 -10
- package/src/__tests__/http-conversation-lineage.test.ts +251 -0
- package/src/__tests__/image-source-path-reinject.test.ts +136 -0
- package/src/__tests__/inline-command-runner.test.ts +311 -0
- package/src/__tests__/inline-skill-authoring-guard.test.ts +220 -0
- package/src/__tests__/inline-skill-load-permissions.test.ts +435 -0
- package/src/__tests__/list-messages-attachments.test.ts +96 -0
- package/src/__tests__/llm-context-normalization.test.ts +1116 -0
- package/src/__tests__/llm-context-route-provider.test.ts +217 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
- package/src/__tests__/media-generate-image.test.ts +47 -94
- package/src/__tests__/memory-brief-open-loops.test.ts +530 -0
- package/src/__tests__/memory-brief-time.test.ts +285 -0
- package/src/__tests__/memory-brief-wrapper.test.ts +311 -0
- package/src/__tests__/memory-chunk-archive.test.ts +400 -0
- package/src/__tests__/memory-chunk-dual-write.test.ts +453 -0
- package/src/__tests__/memory-episode-archive.test.ts +370 -0
- package/src/__tests__/memory-episode-dual-write.test.ts +626 -0
- package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
- package/src/__tests__/memory-observation-archive.test.ts +375 -0
- package/src/__tests__/memory-observation-dual-write.test.ts +318 -0
- package/src/__tests__/memory-recall-quality.test.ts +7 -7
- package/src/__tests__/memory-reducer-store.test.ts +728 -0
- package/src/__tests__/memory-reducer-types.test.ts +699 -0
- package/src/__tests__/memory-reducer.test.ts +698 -0
- package/src/__tests__/memory-regressions.test.ts +6 -4
- package/src/__tests__/memory-simplified-config.test.ts +281 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
- package/src/__tests__/migration-export-http.test.ts +3 -1
- package/src/__tests__/migration-import-commit-http.test.ts +18 -4
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
- package/src/__tests__/mime-builder.test.ts +3 -2
- package/src/__tests__/non-member-access-request.test.ts +12 -1
- package/src/__tests__/notification-decision-identity.test.ts +52 -0
- package/src/__tests__/oauth-apps-routes.test.ts +103 -0
- package/src/__tests__/oauth-store.test.ts +115 -0
- package/src/__tests__/parse-identity-fields.test.ts +129 -0
- package/src/__tests__/provider-error-scenarios.test.ts +1 -3
- package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
- package/src/__tests__/recording-handler.test.ts +17 -0
- package/src/__tests__/registry.test.ts +3 -8
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
- package/src/__tests__/schema-transforms.test.ts +165 -5
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/skill-load-inline-command.test.ts +598 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +644 -0
- package/src/__tests__/skills-inline-command-expansions.test.ts +301 -0
- package/src/__tests__/skills-transitive-hash.test.ts +333 -0
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -2
- package/src/__tests__/starter-task-flow.test.ts +1 -0
- package/src/__tests__/suggestion-routes.test.ts +443 -0
- package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
- package/src/__tests__/swarm-recursion.test.ts +1 -0
- package/src/__tests__/swarm-tool.test.ts +1 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
- package/src/__tests__/top-level-renderer.test.ts +22 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +320 -0
- package/src/__tests__/web-fetch.test.ts +6 -2
- package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
- package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
- package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
- package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
- package/src/agent/attachments.ts +27 -1
- package/src/agent/loop.ts +29 -1
- package/src/avatar/traits-png-sync.ts +80 -25
- package/src/bundler/app-bundler.ts +4 -4
- package/src/calls/call-domain.ts +1 -0
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/commands/auth.ts +92 -0
- package/src/cli/commands/avatar.ts +7 -6
- package/src/cli/commands/config.ts +2 -0
- package/src/cli/commands/oauth/providers.ts +29 -0
- package/src/cli/program.ts +12 -0
- package/src/cli.ts +15 -48
- package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
- package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
- package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
- package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
- package/src/config/bundled-tool-registry.ts +2 -14
- package/src/config/feature-flag-registry.json +24 -0
- package/src/config/loader.ts +65 -0
- package/src/config/raw-config-utils.ts +58 -0
- package/src/config/schema-utils.ts +28 -7
- package/src/config/schema.ts +20 -0
- package/src/config/schemas/elevenlabs.ts +18 -0
- package/src/config/schemas/memory-lifecycle.ts +4 -2
- package/src/config/schemas/memory-simplified.ts +101 -0
- package/src/config/schemas/memory-storage.ts +1 -1
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/services.ts +8 -6
- package/src/config/skills.ts +50 -4
- package/src/contacts/contact-store.ts +13 -6
- package/src/contacts/contacts-write.ts +0 -1
- package/src/context/window-manager.ts +13 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +54 -8
- package/src/daemon/conversation-agent-loop.ts +127 -20
- package/src/daemon/conversation-attachments.ts +18 -36
- package/src/daemon/conversation-error.ts +2 -1
- package/src/daemon/conversation-history.ts +18 -4
- package/src/daemon/conversation-lifecycle.ts +50 -16
- package/src/daemon/conversation-messaging.ts +70 -26
- package/src/daemon/conversation-process.ts +58 -34
- package/src/daemon/conversation-runtime-assembly.ts +22 -38
- package/src/daemon/conversation-slash.ts +121 -256
- package/src/daemon/conversation-surfaces.ts +170 -24
- package/src/daemon/conversation-tool-setup.ts +0 -6
- package/src/daemon/conversation-workspace.ts +21 -1
- package/src/daemon/conversation.ts +69 -30
- package/src/daemon/first-greeting.ts +35 -0
- package/src/daemon/handlers/config-embeddings.ts +156 -0
- package/src/daemon/handlers/config-model.ts +62 -26
- package/src/daemon/handlers/conversations.ts +0 -23
- package/src/daemon/handlers/identity.ts +12 -1
- package/src/daemon/handlers/recording.ts +26 -21
- package/src/daemon/host-cu-proxy.ts +2 -2
- package/src/daemon/lifecycle.ts +115 -65
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +18 -0
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/shared.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/message-types/upgrades.ts +23 -0
- package/src/daemon/server.ts +83 -12
- package/src/daemon/shutdown-handlers.ts +8 -5
- package/src/daemon/startup-error.ts +9 -0
- package/src/daemon/tool-side-effects.ts +11 -28
- package/src/events/tool-permission-telemetry-listener.ts +1 -3
- package/src/followups/followup-store.ts +47 -1
- package/src/instrument.ts +0 -4
- package/src/media/app-icon-generator.ts +2 -2
- package/src/memory/app-git-service.ts +28 -16
- package/src/memory/app-store.ts +230 -41
- package/src/memory/archive-store.ts +400 -0
- package/src/memory/attachments-store.ts +558 -130
- package/src/memory/brief-formatting.ts +33 -0
- package/src/memory/brief-open-loops.ts +266 -0
- package/src/memory/brief-time.ts +161 -0
- package/src/memory/brief.ts +75 -0
- package/src/memory/conversation-attention-store.ts +70 -0
- package/src/memory/conversation-crud.ts +591 -8
- package/src/memory/conversation-directories.ts +125 -0
- package/src/memory/conversation-disk-view.ts +390 -0
- package/src/memory/conversation-key-store.ts +17 -5
- package/src/memory/conversation-queries.ts +5 -1
- package/src/memory/conversation-title-service.ts +21 -49
- package/src/memory/db-init.ts +40 -0
- package/src/memory/embedding-backend.ts +42 -53
- package/src/memory/embedding-gemini.test.ts +4 -4
- package/src/memory/embedding-local.ts +1 -3
- package/src/memory/embedding-ollama.ts +1 -3
- package/src/memory/embedding-openai.ts +1 -3
- package/src/memory/indexer.ts +114 -21
- package/src/memory/items-extractor.ts +42 -13
- package/src/memory/job-handlers/conversation-starters.ts +6 -1
- package/src/memory/job-handlers/embedding.test.ts +2 -4
- package/src/memory/job-handlers/embedding.ts +83 -0
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +6 -0
- package/src/memory/jobs-worker.ts +12 -0
- package/src/memory/llm-request-log-store.ts +100 -1
- package/src/memory/migrations/102-alter-table-columns.ts +5 -0
- package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
- package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
- package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
- package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
- package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
- package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
- package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
- package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
- package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
- package/src/memory/migrations/185-memory-brief-state.ts +52 -0
- package/src/memory/migrations/186-memory-archive.ts +109 -0
- package/src/memory/migrations/187-memory-reducer-checkpoints.ts +19 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/qdrant-client.ts +23 -4
- package/src/memory/reducer-store.ts +271 -0
- package/src/memory/reducer-types.ts +99 -0
- package/src/memory/reducer.ts +453 -0
- package/src/memory/retriever.test.ts +601 -2
- package/src/memory/retriever.ts +85 -9
- package/src/memory/schema/conversations.ts +9 -0
- package/src/memory/schema/index.ts +2 -0
- package/src/memory/schema/infrastructure.ts +13 -7
- package/src/memory/schema/memory-archive.ts +121 -0
- package/src/memory/schema/memory-brief.ts +55 -0
- package/src/memory/schema/oauth.ts +6 -0
- package/src/memory/search/semantic.ts +17 -4
- package/src/messaging/providers/gmail/mime-builder.ts +3 -1
- package/src/notifications/copy-composer.ts +26 -0
- package/src/notifications/decision-engine.ts +14 -1
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +36 -0
- package/src/oauth/byo-connection.test.ts +1 -45
- package/src/oauth/byo-connection.ts +2 -8
- package/src/oauth/connect-orchestrator.ts +15 -11
- package/src/oauth/connection-resolver.test.ts +191 -0
- package/src/oauth/connection-resolver.ts +66 -38
- package/src/oauth/connection.ts +0 -1
- package/src/oauth/oauth-store.ts +99 -47
- package/src/oauth/platform-connection.test.ts +0 -1
- package/src/oauth/platform-connection.ts +11 -3
- package/src/oauth/seed-providers.ts +78 -3
- package/src/oauth/token-persistence.ts +16 -10
- package/src/permissions/checker.ts +160 -14
- package/src/permissions/defaults.ts +14 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -0
- package/src/providers/anthropic/client.ts +8 -1
- package/src/providers/failover.ts +4 -1
- package/src/providers/gemini/client.ts +50 -0
- package/src/providers/model-catalog.ts +92 -0
- package/src/providers/model-intents.ts +29 -20
- package/src/providers/openai/client.ts +49 -0
- package/src/providers/types.ts +2 -0
- package/src/runtime/access-request-helper.ts +16 -7
- package/src/runtime/auth/credential-service.ts +3 -1
- package/src/runtime/auth/route-policy.ts +14 -1
- package/src/runtime/btw-sidechain.ts +101 -0
- package/src/runtime/channel-reply-delivery.ts +17 -1
- package/src/runtime/http-router.ts +3 -1
- package/src/runtime/http-server.ts +196 -141
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +5 -1
- package/src/runtime/routes/access-request-decision.ts +41 -0
- package/src/runtime/routes/app-management-routes.ts +6 -3
- package/src/runtime/routes/app-routes.ts +7 -3
- package/src/runtime/routes/approval-routes.ts +1 -0
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
- package/src/runtime/routes/attachment-routes.ts +45 -15
- package/src/runtime/routes/btw-routes.ts +21 -61
- package/src/runtime/routes/conversation-management-routes.ts +74 -0
- package/src/runtime/routes/conversation-query-routes.ts +187 -10
- package/src/runtime/routes/conversation-routes.ts +269 -28
- package/src/runtime/routes/conversation-starter-routes.ts +9 -11
- package/src/runtime/routes/diagnostics-routes.ts +1 -0
- package/src/runtime/routes/identity-routes.ts +2 -35
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
- package/src/runtime/routes/llm-context-normalization.ts +1212 -0
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/memory-item-routes.test.ts +34 -0
- package/src/runtime/routes/memory-item-routes.ts +94 -5
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/oauth-apps.ts +291 -0
- package/src/runtime/routes/secret-routes.ts +30 -1
- package/src/runtime/routes/settings-routes.ts +14 -0
- package/src/runtime/routes/surface-action-routes.ts +68 -1
- package/src/runtime/routes/trace-event-routes.ts +4 -1
- package/src/schedule/schedule-store.ts +30 -21
- package/src/security/secure-keys.ts +21 -0
- package/src/signals/bash.ts +1 -1
- package/src/skills/inline-command-expansions.ts +204 -0
- package/src/skills/inline-command-render.ts +127 -0
- package/src/skills/inline-command-runner.ts +242 -0
- package/src/skills/transitive-version-hash.ts +88 -0
- package/src/swarm/backend-claude-code.ts +3 -6
- package/src/tasks/task-store.ts +43 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +3 -1
- package/src/tools/AGENTS.md +6 -10
- package/src/tools/apps/executors.ts +17 -232
- package/src/tools/claude-code/claude-code.ts +2 -3
- package/src/tools/credentials/vault.ts +7 -12
- package/src/tools/host-filesystem/read.ts +13 -10
- package/src/tools/network/__tests__/web-search.test.ts +4 -2
- package/src/tools/permission-checker.ts +8 -1
- package/src/tools/schedule/list.ts +2 -7
- package/src/tools/schema-transforms.ts +5 -0
- package/src/tools/shared/filesystem/format-diff.ts +2 -7
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +140 -6
- package/src/tools/tool-manifest.ts +0 -6
- package/src/tools/ui-surface/definitions.ts +2 -2
- package/src/util/device-id.ts +28 -5
- package/src/util/platform.ts +24 -0
- package/src/util/pricing.ts +1 -0
- package/src/util/retry.ts +1 -3
- package/src/workspace/migrations/003-seed-device-id.ts +3 -4
- package/src/workspace/migrations/006-services-config.ts +5 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
- package/src/workspace/migrations/{002-backfill-installation-id.ts → 011-backfill-installation-id.ts} +24 -13
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
- package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
- package/src/workspace/migrations/registry.ts +11 -1
- package/src/workspace/top-level-renderer.ts +12 -0
- package/src/__tests__/asset-materialize-tool.test.ts +0 -523
- package/src/__tests__/asset-search-tool.test.ts +0 -536
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
- package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
- package/src/__tests__/media-visibility-policy.test.ts +0 -190
- package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
- package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
- package/src/daemon/media-visibility-policy.ts +0 -59
- package/src/tools/assets/materialize.ts +0 -248
- package/src/tools/assets/search.ts +0 -400
|
@@ -15,11 +15,16 @@ import type {
|
|
|
15
15
|
} from "../channels/types.js";
|
|
16
16
|
import {
|
|
17
17
|
addMessage,
|
|
18
|
+
getConversation,
|
|
18
19
|
getMessageById,
|
|
19
20
|
provenanceFromTrustContext,
|
|
20
21
|
updateMessageContent,
|
|
21
22
|
} from "../memory/conversation-crud.js";
|
|
22
|
-
import {
|
|
23
|
+
import { syncMessageToDisk } from "../memory/conversation-disk-view.js";
|
|
24
|
+
import {
|
|
25
|
+
backfillMessageIdOnLogs,
|
|
26
|
+
recordRequestLog,
|
|
27
|
+
} from "../memory/llm-request-log-store.js";
|
|
23
28
|
import type { ContentBlock, ImageContent } from "../providers/types.js";
|
|
24
29
|
import type { DirectiveRequest } from "./assistant-attachments.js";
|
|
25
30
|
import {
|
|
@@ -48,6 +53,8 @@ export interface EventHandlerState {
|
|
|
48
53
|
llmCallStartedEmitted: boolean;
|
|
49
54
|
pendingDirectiveDisplayBuffer: string;
|
|
50
55
|
firstAssistantText: string;
|
|
56
|
+
/** Most recent resolved provider for the current exchange's usage accounting. */
|
|
57
|
+
exchangeProviderName: string | undefined;
|
|
51
58
|
exchangeInputTokens: number;
|
|
52
59
|
exchangeCacheCreationInputTokens: number;
|
|
53
60
|
exchangeCacheReadInputTokens: number;
|
|
@@ -114,6 +121,7 @@ export function createEventHandlerState(): EventHandlerState {
|
|
|
114
121
|
llmCallStartedEmitted: false,
|
|
115
122
|
pendingDirectiveDisplayBuffer: "",
|
|
116
123
|
firstAssistantText: "",
|
|
124
|
+
exchangeProviderName: undefined,
|
|
117
125
|
exchangeInputTokens: 0,
|
|
118
126
|
exchangeCacheCreationInputTokens: 0,
|
|
119
127
|
exchangeCacheReadInputTokens: 0,
|
|
@@ -167,6 +175,12 @@ export function emitLlmCallStartedIfNeeded(
|
|
|
167
175
|
);
|
|
168
176
|
}
|
|
169
177
|
|
|
178
|
+
// ── Client Payload Size Caps ─────────────────────────────────────────
|
|
179
|
+
// tool_input_delta streams accumulated JSON as tools run. For non-app
|
|
180
|
+
// tools the client discards it (extractCodePreview only handles app tools),
|
|
181
|
+
// so we skip forwarding entirely to avoid transport/decode overhead.
|
|
182
|
+
const APP_TOOL_NAMES = new Set(["app_create"]);
|
|
183
|
+
|
|
170
184
|
// ── Friendly Tool Names ──────────────────────────────────────────────
|
|
171
185
|
|
|
172
186
|
const TOOL_FRIENDLY_NAMES: Record<string, string> = {
|
|
@@ -183,11 +197,9 @@ const TOOL_FRIENDLY_NAMES: Record<string, string> = {
|
|
|
183
197
|
browser_scroll: "browser",
|
|
184
198
|
browser_wait: "browser",
|
|
185
199
|
app_create: "app",
|
|
186
|
-
|
|
200
|
+
app_refresh: "app refresh",
|
|
187
201
|
skill_load: "skill",
|
|
188
202
|
skill_execute: "skill",
|
|
189
|
-
app_file_edit: "app file",
|
|
190
|
-
app_file_write: "app file",
|
|
191
203
|
};
|
|
192
204
|
|
|
193
205
|
function friendlyToolName(name: string): string {
|
|
@@ -267,7 +279,12 @@ export function handleToolUse(
|
|
|
267
279
|
state.toolCallTimestamps.set(event.id, { startedAt: Date.now() });
|
|
268
280
|
state.currentToolUseId = event.id;
|
|
269
281
|
state.currentTurnToolUseIds.push(event.id);
|
|
270
|
-
const statusText =
|
|
282
|
+
const statusText =
|
|
283
|
+
event.name === "skill_execute" &&
|
|
284
|
+
typeof event.input.activity === "string" &&
|
|
285
|
+
event.input.activity.length > 0
|
|
286
|
+
? event.input.activity
|
|
287
|
+
: `Running ${friendlyToolName(event.name)}`;
|
|
271
288
|
deps.ctx.emitActivityState(
|
|
272
289
|
"tool_running",
|
|
273
290
|
"tool_use_start",
|
|
@@ -385,6 +402,10 @@ export function handleInputJsonDelta(
|
|
|
385
402
|
deps: EventHandlerDeps,
|
|
386
403
|
event: Extract<AgentEvent, { type: "input_json_delta" }>,
|
|
387
404
|
): void {
|
|
405
|
+
// Only forward input deltas for app tools — the client only uses this
|
|
406
|
+
// stream for app_create code previews. Non-app tools would send large
|
|
407
|
+
// cumulative JSON on every delta with no benefit.
|
|
408
|
+
if (!APP_TOOL_NAMES.has(event.toolName)) return;
|
|
388
409
|
deps.onEvent({
|
|
389
410
|
type: "tool_input_delta",
|
|
390
411
|
toolName: event.toolName,
|
|
@@ -627,12 +648,21 @@ export async function handleMessageComplete(
|
|
|
627
648
|
assistantMessageInterface:
|
|
628
649
|
deps.turnInterfaceContext.assistantMessageInterface,
|
|
629
650
|
};
|
|
630
|
-
await addMessage(
|
|
651
|
+
const toolResultMsg = await addMessage(
|
|
631
652
|
deps.ctx.conversationId,
|
|
632
653
|
"user",
|
|
633
654
|
JSON.stringify(toolResultBlocks),
|
|
634
655
|
toolResultMetadata,
|
|
635
656
|
);
|
|
657
|
+
// Sync tool-result user message to disk view
|
|
658
|
+
const convForToolResult = getConversation(deps.ctx.conversationId);
|
|
659
|
+
if (convForToolResult) {
|
|
660
|
+
syncMessageToDisk(
|
|
661
|
+
deps.ctx.conversationId,
|
|
662
|
+
toolResultMsg.id,
|
|
663
|
+
convForToolResult.createdAt,
|
|
664
|
+
);
|
|
665
|
+
}
|
|
636
666
|
for (const id of state.pendingToolResults.keys()) {
|
|
637
667
|
state.persistedToolUseIds.add(id);
|
|
638
668
|
}
|
|
@@ -697,6 +727,18 @@ export async function handleMessageComplete(
|
|
|
697
727
|
);
|
|
698
728
|
state.lastAssistantMessageId = assistantMsg.id;
|
|
699
729
|
|
|
730
|
+
// Backfill message_id on all LLM request logs from this turn.
|
|
731
|
+
// The agent loop is single-threaded per conversation, so all rows with
|
|
732
|
+
// message_id IS NULL belong to the current turn.
|
|
733
|
+
try {
|
|
734
|
+
backfillMessageIdOnLogs(deps.ctx.conversationId, assistantMsg.id);
|
|
735
|
+
} catch (err) {
|
|
736
|
+
deps.rlog.warn(
|
|
737
|
+
{ err },
|
|
738
|
+
"Failed to backfill message_id on LLM request logs (non-fatal)",
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
|
|
700
742
|
deps.ctx.currentTurnSurfaces = [];
|
|
701
743
|
|
|
702
744
|
// Emit trace event
|
|
@@ -724,6 +766,8 @@ export function handleUsage(
|
|
|
724
766
|
deps: EventHandlerDeps,
|
|
725
767
|
event: Extract<AgentEvent, { type: "usage" }>,
|
|
726
768
|
): void {
|
|
769
|
+
const providerName = event.actualProvider ?? deps.ctx.provider.name;
|
|
770
|
+
state.exchangeProviderName = providerName;
|
|
727
771
|
state.exchangeInputTokens += event.inputTokens;
|
|
728
772
|
state.exchangeCacheCreationInputTokens += event.cacheCreationInputTokens ?? 0;
|
|
729
773
|
state.exchangeCacheReadInputTokens += event.cacheReadInputTokens ?? 0;
|
|
@@ -739,6 +783,8 @@ export function handleUsage(
|
|
|
739
783
|
deps.ctx.conversationId,
|
|
740
784
|
JSON.stringify(event.rawRequest),
|
|
741
785
|
JSON.stringify(event.rawResponse),
|
|
786
|
+
undefined,
|
|
787
|
+
providerName,
|
|
742
788
|
);
|
|
743
789
|
} catch (err) {
|
|
744
790
|
deps.rlog.warn({ err }, "Failed to persist LLM request log (non-fatal)");
|
|
@@ -749,12 +795,12 @@ export function handleUsage(
|
|
|
749
795
|
|
|
750
796
|
deps.ctx.traceEmitter.emit(
|
|
751
797
|
"llm_call_finished",
|
|
752
|
-
`LLM call to ${
|
|
798
|
+
`LLM call to ${providerName} finished`,
|
|
753
799
|
{
|
|
754
800
|
requestId: deps.reqId,
|
|
755
801
|
status: "success",
|
|
756
802
|
attributes: {
|
|
757
|
-
provider:
|
|
803
|
+
provider: providerName,
|
|
758
804
|
model: event.model,
|
|
759
805
|
inputTokens: event.inputTokens,
|
|
760
806
|
outputTokens: event.outputTokens,
|
|
@@ -32,7 +32,8 @@ import {
|
|
|
32
32
|
setSentryConversationContext,
|
|
33
33
|
} from "../instrument.js";
|
|
34
34
|
import { commitAppTurnChanges } from "../memory/app-git-service.js";
|
|
35
|
-
import { getApp, listAppFiles } from "../memory/app-store.js";
|
|
35
|
+
import { getApp, listAppFiles, resolveAppDir } from "../memory/app-store.js";
|
|
36
|
+
import { insertCompactionEpisode } from "../memory/archive-store.js";
|
|
36
37
|
import {
|
|
37
38
|
addMessage,
|
|
38
39
|
deleteMessageById,
|
|
@@ -43,6 +44,10 @@ import {
|
|
|
43
44
|
updateConversationContextWindow,
|
|
44
45
|
updateConversationTitle,
|
|
45
46
|
} from "../memory/conversation-crud.js";
|
|
47
|
+
import {
|
|
48
|
+
rebuildConversationDiskViewFromDbState,
|
|
49
|
+
syncMessageToDisk,
|
|
50
|
+
} from "../memory/conversation-disk-view.js";
|
|
46
51
|
import {
|
|
47
52
|
isReplaceableTitle,
|
|
48
53
|
queueGenerateConversationTitle,
|
|
@@ -77,7 +82,6 @@ import {
|
|
|
77
82
|
} from "./conversation-agent-loop-handlers.js";
|
|
78
83
|
import {
|
|
79
84
|
approveHostAttachmentRead,
|
|
80
|
-
formatAttachmentWarnings,
|
|
81
85
|
resolveAssistantAttachments,
|
|
82
86
|
} from "./conversation-attachments.js";
|
|
83
87
|
import {
|
|
@@ -173,11 +177,9 @@ const TOOL_FRIENDLY_LABEL: Record<string, string> = {
|
|
|
173
177
|
browser_scroll: "Browser",
|
|
174
178
|
browser_wait: "Browser",
|
|
175
179
|
app_create: "Create App",
|
|
176
|
-
|
|
180
|
+
app_refresh: "Refresh App",
|
|
177
181
|
skill_load: "Load Skill",
|
|
178
182
|
skill_execute: "Run Skill Tool",
|
|
179
|
-
app_file_edit: "Edit App File",
|
|
180
|
-
app_file_write: "Write App File",
|
|
181
183
|
};
|
|
182
184
|
|
|
183
185
|
type GitServiceInitializer = {
|
|
@@ -207,7 +209,17 @@ export interface AgentLoopConversationContext {
|
|
|
207
209
|
currentPage?: string;
|
|
208
210
|
readonly surfaceState: Map<
|
|
209
211
|
string,
|
|
210
|
-
{
|
|
212
|
+
{
|
|
213
|
+
surfaceType: SurfaceType;
|
|
214
|
+
data: SurfaceData;
|
|
215
|
+
title?: string;
|
|
216
|
+
actions?: Array<{
|
|
217
|
+
id: string;
|
|
218
|
+
label: string;
|
|
219
|
+
style?: string;
|
|
220
|
+
data?: Record<string, unknown>;
|
|
221
|
+
}>;
|
|
222
|
+
}
|
|
211
223
|
>;
|
|
212
224
|
pendingSurfaceActions: Map<string, { surfaceType: SurfaceType }>;
|
|
213
225
|
surfaceActionRequestIds: Set<string>;
|
|
@@ -502,6 +514,12 @@ export async function runAgentLoopImpl(
|
|
|
502
514
|
compacted.summaryText,
|
|
503
515
|
ctx.contextCompactedMessageCount,
|
|
504
516
|
);
|
|
517
|
+
dualWriteCompactionEpisode(
|
|
518
|
+
ctx.conversationId,
|
|
519
|
+
ctx.memoryPolicy.scopeId,
|
|
520
|
+
compacted.summaryText,
|
|
521
|
+
compacted.summaryOutputTokens,
|
|
522
|
+
);
|
|
505
523
|
onEvent({
|
|
506
524
|
type: "context_compacted",
|
|
507
525
|
previousEstimatedInputTokens: compacted.previousEstimatedInputTokens,
|
|
@@ -604,6 +622,7 @@ export async function runAgentLoopImpl(
|
|
|
604
622
|
if (app) {
|
|
605
623
|
activeSurface.appId = app.id;
|
|
606
624
|
activeSurface.appName = app.name;
|
|
625
|
+
activeSurface.appDirName = resolveAppDir(app.id).dirName;
|
|
607
626
|
activeSurface.appSchemaJson = app.schemaJson;
|
|
608
627
|
activeSurface.appFiles = listAppFiles(app.id);
|
|
609
628
|
if (app.pages && Object.keys(app.pages).length > 0) {
|
|
@@ -768,6 +787,12 @@ export async function runAgentLoopImpl(
|
|
|
768
787
|
step.compactionResult.summaryText,
|
|
769
788
|
ctx.contextCompactedMessageCount,
|
|
770
789
|
);
|
|
790
|
+
dualWriteCompactionEpisode(
|
|
791
|
+
ctx.conversationId,
|
|
792
|
+
ctx.memoryPolicy.scopeId,
|
|
793
|
+
step.compactionResult.summaryText,
|
|
794
|
+
step.compactionResult.summaryOutputTokens,
|
|
795
|
+
);
|
|
771
796
|
onEvent({
|
|
772
797
|
type: "context_compacted",
|
|
773
798
|
previousEstimatedInputTokens:
|
|
@@ -801,7 +826,16 @@ export async function runAgentLoopImpl(
|
|
|
801
826
|
mode: currentInjectionMode,
|
|
802
827
|
});
|
|
803
828
|
|
|
804
|
-
|
|
829
|
+
// Re-estimate with injections included — step.estimatedTokens was
|
|
830
|
+
// computed on bare history (ctx.messages) and doesn't account for
|
|
831
|
+
// tokens added by runtime injections.
|
|
832
|
+
const postInjectionTokens = estimatePromptTokens(
|
|
833
|
+
runMessages,
|
|
834
|
+
ctx.systemPrompt,
|
|
835
|
+
{ providerName: ctx.provider.name, toolTokenBudget },
|
|
836
|
+
);
|
|
837
|
+
|
|
838
|
+
if (postInjectionTokens <= preflightBudget) break;
|
|
805
839
|
}
|
|
806
840
|
}
|
|
807
841
|
|
|
@@ -943,6 +977,12 @@ export async function runAgentLoopImpl(
|
|
|
943
977
|
midLoopCompact.summaryText,
|
|
944
978
|
ctx.contextCompactedMessageCount,
|
|
945
979
|
);
|
|
980
|
+
dualWriteCompactionEpisode(
|
|
981
|
+
ctx.conversationId,
|
|
982
|
+
ctx.memoryPolicy.scopeId,
|
|
983
|
+
midLoopCompact.summaryText,
|
|
984
|
+
midLoopCompact.summaryOutputTokens,
|
|
985
|
+
);
|
|
946
986
|
onEvent({
|
|
947
987
|
type: "context_compacted",
|
|
948
988
|
previousEstimatedInputTokens:
|
|
@@ -1139,6 +1179,12 @@ export async function runAgentLoopImpl(
|
|
|
1139
1179
|
step.compactionResult.summaryText,
|
|
1140
1180
|
ctx.contextCompactedMessageCount,
|
|
1141
1181
|
);
|
|
1182
|
+
dualWriteCompactionEpisode(
|
|
1183
|
+
ctx.conversationId,
|
|
1184
|
+
ctx.memoryPolicy.scopeId,
|
|
1185
|
+
step.compactionResult.summaryText,
|
|
1186
|
+
step.compactionResult.summaryOutputTokens,
|
|
1187
|
+
);
|
|
1142
1188
|
onEvent({
|
|
1143
1189
|
type: "context_compacted",
|
|
1144
1190
|
previousEstimatedInputTokens:
|
|
@@ -1173,6 +1219,7 @@ export async function runAgentLoopImpl(
|
|
|
1173
1219
|
preRepairMessages = runMessages;
|
|
1174
1220
|
preRunHistoryLength = runMessages.length;
|
|
1175
1221
|
state.contextTooLargeDetected = false;
|
|
1222
|
+
yieldedForBudget = false;
|
|
1176
1223
|
|
|
1177
1224
|
updatedHistory = await ctx.agentLoop.run(
|
|
1178
1225
|
runMessages,
|
|
@@ -1195,6 +1242,15 @@ export async function runAgentLoopImpl(
|
|
|
1195
1242
|
"Post-convergence rerun still yielded at checkpoint — continuing reduction",
|
|
1196
1243
|
);
|
|
1197
1244
|
state.contextTooLargeDetected = true;
|
|
1245
|
+
|
|
1246
|
+
// Fold rerun progress into ctx.messages so the next reducer
|
|
1247
|
+
// tier operates on up-to-date history instead of stale
|
|
1248
|
+
// pre-rerun messages.
|
|
1249
|
+
if (updatedHistory.length > preRunHistoryLength) {
|
|
1250
|
+
ctx.messages = stripInjectedContext(updatedHistory);
|
|
1251
|
+
preRepairMessages = updatedHistory;
|
|
1252
|
+
preRunHistoryLength = updatedHistory.length;
|
|
1253
|
+
}
|
|
1198
1254
|
}
|
|
1199
1255
|
}
|
|
1200
1256
|
|
|
@@ -1236,6 +1292,12 @@ export async function runAgentLoopImpl(
|
|
|
1236
1292
|
emergencyCompact.summaryText,
|
|
1237
1293
|
ctx.contextCompactedMessageCount,
|
|
1238
1294
|
);
|
|
1295
|
+
dualWriteCompactionEpisode(
|
|
1296
|
+
ctx.conversationId,
|
|
1297
|
+
ctx.memoryPolicy.scopeId,
|
|
1298
|
+
emergencyCompact.summaryText,
|
|
1299
|
+
emergencyCompact.summaryOutputTokens,
|
|
1300
|
+
);
|
|
1239
1301
|
onEvent({
|
|
1240
1302
|
type: "context_compacted",
|
|
1241
1303
|
previousEstimatedInputTokens:
|
|
@@ -1340,6 +1402,12 @@ export async function runAgentLoopImpl(
|
|
|
1340
1402
|
emergencyCompact.summaryText,
|
|
1341
1403
|
ctx.contextCompactedMessageCount,
|
|
1342
1404
|
);
|
|
1405
|
+
dualWriteCompactionEpisode(
|
|
1406
|
+
ctx.conversationId,
|
|
1407
|
+
ctx.memoryPolicy.scopeId,
|
|
1408
|
+
emergencyCompact.summaryText,
|
|
1409
|
+
emergencyCompact.summaryOutputTokens,
|
|
1410
|
+
);
|
|
1343
1411
|
onEvent({
|
|
1344
1412
|
type: "context_compacted",
|
|
1345
1413
|
previousEstimatedInputTokens:
|
|
@@ -1514,16 +1582,29 @@ export async function runAgentLoopImpl(
|
|
|
1514
1582
|
state.exchangeCacheCreationInputTokens,
|
|
1515
1583
|
state.exchangeCacheReadInputTokens,
|
|
1516
1584
|
collapseRawResponses(state.exchangeRawResponses),
|
|
1585
|
+
state.exchangeProviderName,
|
|
1517
1586
|
);
|
|
1518
1587
|
|
|
1519
1588
|
void getHookManager().trigger("post-message", {
|
|
1520
1589
|
conversationId: ctx.conversationId,
|
|
1521
1590
|
});
|
|
1522
1591
|
|
|
1592
|
+
const syncLastAssistantMessageToDisk = (): void => {
|
|
1593
|
+
if (!state.lastAssistantMessageId) return;
|
|
1594
|
+
const convForDisk = getConversation(ctx.conversationId);
|
|
1595
|
+
if (!convForDisk) return;
|
|
1596
|
+
syncMessageToDisk(
|
|
1597
|
+
ctx.conversationId,
|
|
1598
|
+
state.lastAssistantMessageId,
|
|
1599
|
+
convForDisk.createdAt,
|
|
1600
|
+
);
|
|
1601
|
+
};
|
|
1602
|
+
|
|
1523
1603
|
// Fast-path: when the user cancelled, skip expensive post-loop work
|
|
1524
1604
|
// (attachment resolution) and emit the cancellation event immediately
|
|
1525
1605
|
// so the client can re-enable the UI without delay.
|
|
1526
1606
|
if (abortController.signal.aborted) {
|
|
1607
|
+
syncLastAssistantMessageToDisk();
|
|
1527
1608
|
ctx.emitActivityState("idle", "generation_cancelled", "global", reqId);
|
|
1528
1609
|
ctx.traceEmitter.emit(
|
|
1529
1610
|
"generation_cancelled",
|
|
@@ -1559,17 +1640,7 @@ export async function runAgentLoopImpl(
|
|
|
1559
1640
|
|
|
1560
1641
|
ctx.lastAssistantAttachments = assistantAttachments;
|
|
1561
1642
|
ctx.lastAttachmentWarnings = attachmentResult.directiveWarnings;
|
|
1562
|
-
|
|
1563
|
-
const warningText = formatAttachmentWarnings(
|
|
1564
|
-
attachmentResult.directiveWarnings,
|
|
1565
|
-
);
|
|
1566
|
-
if (warningText) {
|
|
1567
|
-
onEvent({
|
|
1568
|
-
type: "assistant_text_delta",
|
|
1569
|
-
text: warningText,
|
|
1570
|
-
conversationId: ctx.conversationId,
|
|
1571
|
-
});
|
|
1572
|
-
}
|
|
1643
|
+
syncLastAssistantMessageToDisk();
|
|
1573
1644
|
|
|
1574
1645
|
// Re-check: the user may have cancelled during attachment resolution
|
|
1575
1646
|
if (abortController.signal.aborted) {
|
|
@@ -1604,6 +1675,9 @@ export async function runAgentLoopImpl(
|
|
|
1604
1675
|
...(emittedAttachments.length > 0
|
|
1605
1676
|
? { attachments: emittedAttachments }
|
|
1606
1677
|
: {}),
|
|
1678
|
+
...(ctx.lastAttachmentWarnings.length > 0
|
|
1679
|
+
? { attachmentWarnings: ctx.lastAttachmentWarnings }
|
|
1680
|
+
: {}),
|
|
1607
1681
|
...(state.lastAssistantMessageId
|
|
1608
1682
|
? { messageId: state.lastAssistantMessageId }
|
|
1609
1683
|
: {}),
|
|
@@ -1624,6 +1698,9 @@ export async function runAgentLoopImpl(
|
|
|
1624
1698
|
...(emittedAttachments.length > 0
|
|
1625
1699
|
? { attachments: emittedAttachments }
|
|
1626
1700
|
: {}),
|
|
1701
|
+
...(ctx.lastAttachmentWarnings.length > 0
|
|
1702
|
+
? { attachmentWarnings: ctx.lastAttachmentWarnings }
|
|
1703
|
+
: {}),
|
|
1627
1704
|
...(state.lastAssistantMessageId
|
|
1628
1705
|
? { messageId: state.lastAssistantMessageId }
|
|
1629
1706
|
: {}),
|
|
@@ -1739,7 +1816,13 @@ export async function runAgentLoopImpl(
|
|
|
1739
1816
|
ctx.commandIntent = undefined;
|
|
1740
1817
|
|
|
1741
1818
|
if (userMessageId) {
|
|
1742
|
-
consolidateAssistantMessages(
|
|
1819
|
+
const didMutateHistory = consolidateAssistantMessages(
|
|
1820
|
+
ctx.conversationId,
|
|
1821
|
+
userMessageId,
|
|
1822
|
+
);
|
|
1823
|
+
if (didMutateHistory) {
|
|
1824
|
+
rebuildConversationDiskViewFromDbState(ctx.conversationId);
|
|
1825
|
+
}
|
|
1743
1826
|
}
|
|
1744
1827
|
|
|
1745
1828
|
ctx.drainQueue(yieldedForHandoff ? "checkpoint_handoff" : "loop_complete");
|
|
@@ -1766,11 +1849,12 @@ function emitUsage(
|
|
|
1766
1849
|
cacheCreationInputTokens = 0,
|
|
1767
1850
|
cacheReadInputTokens = 0,
|
|
1768
1851
|
rawResponse?: unknown,
|
|
1852
|
+
providerName?: string,
|
|
1769
1853
|
): void {
|
|
1770
1854
|
recordUsage(
|
|
1771
1855
|
{
|
|
1772
1856
|
conversationId: ctx.conversationId,
|
|
1773
|
-
providerName: ctx.provider.name,
|
|
1857
|
+
providerName: providerName ?? ctx.provider.name,
|
|
1774
1858
|
usageStats: ctx.usageStats,
|
|
1775
1859
|
},
|
|
1776
1860
|
inputTokens,
|
|
@@ -1789,3 +1873,26 @@ function collapseRawResponses(rawResponses?: unknown[]): unknown | undefined {
|
|
|
1789
1873
|
if (!rawResponses || rawResponses.length === 0) return undefined;
|
|
1790
1874
|
return rawResponses.length === 1 ? rawResponses[0] : rawResponses;
|
|
1791
1875
|
}
|
|
1876
|
+
|
|
1877
|
+
/**
|
|
1878
|
+
* Dual-write a compaction summary as an archive episode so it becomes
|
|
1879
|
+
* searchable via vector recall. Called after each successful compaction
|
|
1880
|
+
* that produces a new summary.
|
|
1881
|
+
*/
|
|
1882
|
+
function dualWriteCompactionEpisode(
|
|
1883
|
+
conversationId: string,
|
|
1884
|
+
scopeId: string,
|
|
1885
|
+
summaryText: string,
|
|
1886
|
+
summaryOutputTokens: number,
|
|
1887
|
+
): void {
|
|
1888
|
+
const now = Date.now();
|
|
1889
|
+
insertCompactionEpisode({
|
|
1890
|
+
conversationId,
|
|
1891
|
+
scopeId,
|
|
1892
|
+
title: truncate(summaryText, 120, ""),
|
|
1893
|
+
summary: summaryText,
|
|
1894
|
+
tokenEstimate: summaryOutputTokens,
|
|
1895
|
+
startAt: now,
|
|
1896
|
+
endAt: now,
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
+
attachInlineAttachmentToMessage,
|
|
2
3
|
AttachmentUploadError,
|
|
3
|
-
|
|
4
|
-
linkAttachmentToMessage,
|
|
4
|
+
getFilePathForAttachment,
|
|
5
5
|
setAttachmentThumbnail,
|
|
6
|
-
uploadAttachment,
|
|
7
|
-
uploadFileBackedAttachment,
|
|
8
|
-
writeAttachmentToDisk,
|
|
9
6
|
} from "../memory/attachments-store.js";
|
|
10
7
|
import {
|
|
11
8
|
check,
|
|
@@ -107,12 +104,6 @@ export async function approveHostAttachmentRead(
|
|
|
107
104
|
return isAllowDecision(response.decision);
|
|
108
105
|
}
|
|
109
106
|
|
|
110
|
-
export function formatAttachmentWarnings(warnings: string[]): string | null {
|
|
111
|
-
if (warnings.length === 0) return null;
|
|
112
|
-
const lines = warnings.map((warning) => `Attachment warning: ${warning}`);
|
|
113
|
-
return `\n\n${lines.join("\n")}`;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
107
|
export interface AttachmentResolutionResult {
|
|
117
108
|
assistantAttachments: AssistantAttachmentDraft[];
|
|
118
109
|
emittedAttachments: UserMessageAttachment[];
|
|
@@ -211,28 +202,16 @@ export async function resolveAssistantAttachments(
|
|
|
211
202
|
if (assistantAttachments.length > 0 && lastAssistantMessageId) {
|
|
212
203
|
for (let i = 0; i < assistantAttachments.length; i++) {
|
|
213
204
|
const draft = assistantAttachments[i];
|
|
214
|
-
const isFileBacked = draft.sizeBytes > FILE_BACKED_THRESHOLD_BYTES;
|
|
215
205
|
let stored;
|
|
216
|
-
let diskFilePath: string | undefined;
|
|
217
206
|
try {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
diskFilePath,
|
|
227
|
-
draft.sizeBytes,
|
|
228
|
-
);
|
|
229
|
-
} else {
|
|
230
|
-
stored = uploadAttachment(
|
|
231
|
-
draft.filename,
|
|
232
|
-
draft.mimeType,
|
|
233
|
-
draft.dataBase64,
|
|
234
|
-
);
|
|
235
|
-
}
|
|
207
|
+
stored = attachInlineAttachmentToMessage(
|
|
208
|
+
lastAssistantMessageId,
|
|
209
|
+
i,
|
|
210
|
+
draft.filename,
|
|
211
|
+
draft.mimeType,
|
|
212
|
+
draft.dataBase64,
|
|
213
|
+
{ skipSizeLimit: true },
|
|
214
|
+
);
|
|
236
215
|
} catch (err) {
|
|
237
216
|
if (err instanceof AttachmentUploadError) {
|
|
238
217
|
log.warn(
|
|
@@ -246,11 +225,11 @@ export async function resolveAssistantAttachments(
|
|
|
246
225
|
}
|
|
247
226
|
throw err;
|
|
248
227
|
}
|
|
249
|
-
linkAttachmentToMessage(lastAssistantMessageId, stored.id, i);
|
|
250
228
|
const isVideo = draft.mimeType.startsWith("video/");
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
229
|
+
// Only omit data for videos — they have an end-to-end lazy-load path
|
|
230
|
+
// via /v1/attachments/:id/content. Other types (images, PDFs) still need
|
|
231
|
+
// inline data for thumbnails, preview, and file-save in the client.
|
|
232
|
+
const omitData = isVideo && draft.dataBase64.length > MAX_INLINE_B64_SIZE;
|
|
254
233
|
|
|
255
234
|
// Generate and persist a thumbnail for video attachments.
|
|
256
235
|
let thumbnailData: string | undefined;
|
|
@@ -259,6 +238,7 @@ export async function resolveAssistantAttachments(
|
|
|
259
238
|
if (existing) {
|
|
260
239
|
thumbnailData = existing;
|
|
261
240
|
} else {
|
|
241
|
+
const diskFilePath = getFilePathForAttachment(stored.id);
|
|
262
242
|
const generated = diskFilePath
|
|
263
243
|
? await generateVideoThumbnailFromPath(diskFilePath)
|
|
264
244
|
: await generateVideoThumbnail(draft.dataBase64);
|
|
@@ -274,8 +254,9 @@ export async function resolveAssistantAttachments(
|
|
|
274
254
|
filename: draft.filename,
|
|
275
255
|
mimeType: draft.mimeType,
|
|
276
256
|
data: omitData ? "" : draft.dataBase64,
|
|
257
|
+
sourceType: draft.sourceType,
|
|
277
258
|
...(omitData ? { sizeBytes: draft.sizeBytes } : {}),
|
|
278
|
-
|
|
259
|
+
fileBacked: true,
|
|
279
260
|
...(thumbnailData ? { thumbnailData } : {}),
|
|
280
261
|
});
|
|
281
262
|
}
|
|
@@ -285,6 +266,7 @@ export async function resolveAssistantAttachments(
|
|
|
285
266
|
filename: draft.filename,
|
|
286
267
|
mimeType: draft.mimeType,
|
|
287
268
|
data: draft.dataBase64,
|
|
269
|
+
sourceType: draft.sourceType,
|
|
288
270
|
});
|
|
289
271
|
}
|
|
290
272
|
}
|
|
@@ -390,7 +390,8 @@ function classifyByMessage(
|
|
|
390
390
|
if (isStreamingError(message)) {
|
|
391
391
|
return {
|
|
392
392
|
code: "PROVIDER_API",
|
|
393
|
-
userMessage:
|
|
393
|
+
userMessage:
|
|
394
|
+
"The AI provider's response was interrupted. Please try again.",
|
|
394
395
|
retryable: true,
|
|
395
396
|
errorCategory: "stream_corruption",
|
|
396
397
|
};
|
|
@@ -29,6 +29,16 @@ function isToolResultBlock(
|
|
|
29
29
|
);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
function isSystemNoticeBlock(
|
|
33
|
+
block: ContentBlock | Record<string, unknown>,
|
|
34
|
+
): boolean {
|
|
35
|
+
if (block.type !== "text") return false;
|
|
36
|
+
const text = (block as { text?: string }).text ?? "";
|
|
37
|
+
return (
|
|
38
|
+
text.startsWith("<system_notice>") && text.endsWith("</system_notice>")
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
32
42
|
function isUndoableUserMessage(message: Message): boolean {
|
|
33
43
|
if (message.role !== "user") return false;
|
|
34
44
|
if (getSummaryFromContextMessage(message) != null) return false;
|
|
@@ -37,8 +47,9 @@ function isUndoableUserMessage(message: Message): boolean {
|
|
|
37
47
|
// responses) are not undoable. Messages that have both tool_result and text blocks
|
|
38
48
|
// (e.g. after repairHistory merges a tool_result turn with a user prompt) are still
|
|
39
49
|
// undoable because they contain real user content.
|
|
50
|
+
// System notice text blocks (retry nudges, progress checks) are not user content.
|
|
40
51
|
const hasNonToolResultContent = message.content.some(
|
|
41
|
-
(block) => !isToolResultBlock(block),
|
|
52
|
+
(block) => !isToolResultBlock(block) && !isSystemNoticeBlock(block),
|
|
42
53
|
);
|
|
43
54
|
if (!hasNonToolResultContent) return false;
|
|
44
55
|
return true;
|
|
@@ -131,10 +142,10 @@ export async function cleanupQdrantVectors(
|
|
|
131
142
|
export function consolidateAssistantMessages(
|
|
132
143
|
conversationId: string,
|
|
133
144
|
userMessageId: string,
|
|
134
|
-
):
|
|
145
|
+
): boolean {
|
|
135
146
|
const allMessages = getMessages(conversationId);
|
|
136
147
|
const userMsgIndex = allMessages.findIndex((m) => m.id === userMessageId);
|
|
137
|
-
if (userMsgIndex === -1) return;
|
|
148
|
+
if (userMsgIndex === -1) return false;
|
|
138
149
|
|
|
139
150
|
const messagesToConsolidate: typeof allMessages = [];
|
|
140
151
|
const internalToolResultMessages: typeof allMessages = [];
|
|
@@ -171,12 +182,14 @@ export function consolidateAssistantMessages(
|
|
|
171
182
|
|
|
172
183
|
// Only consolidate if there are multiple assistant messages
|
|
173
184
|
if (messagesToConsolidate.length <= 1) {
|
|
185
|
+
let didMutate = false;
|
|
174
186
|
// Still delete internal tool_result messages even if only one assistant message,
|
|
175
187
|
// and collect IDs for vector cleanup
|
|
176
188
|
const allSegmentIds: string[] = [];
|
|
177
189
|
const allOrphanedItemIds: string[] = [];
|
|
178
190
|
for (const id of messagesToDelete) {
|
|
179
191
|
const deleted = deleteMessageById(id);
|
|
192
|
+
didMutate = true;
|
|
180
193
|
allSegmentIds.push(...deleted.segmentIds);
|
|
181
194
|
allOrphanedItemIds.push(...deleted.orphanedItemIds);
|
|
182
195
|
}
|
|
@@ -194,7 +207,7 @@ export function consolidateAssistantMessages(
|
|
|
194
207
|
);
|
|
195
208
|
});
|
|
196
209
|
}
|
|
197
|
-
return;
|
|
210
|
+
return didMutate;
|
|
198
211
|
}
|
|
199
212
|
|
|
200
213
|
log.info(
|
|
@@ -339,6 +352,7 @@ export function consolidateAssistantMessages(
|
|
|
339
352
|
},
|
|
340
353
|
"Assistant messages consolidated",
|
|
341
354
|
);
|
|
355
|
+
return true;
|
|
342
356
|
}
|
|
343
357
|
|
|
344
358
|
// ── Undo ─────────────────────────────────────────────────────────────
|