@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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vellumai/assistant",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -70,6 +70,10 @@
|
|
|
70
70
|
"@vellumai/credential-storage",
|
|
71
71
|
"@vellumai/egress-proxy"
|
|
72
72
|
],
|
|
73
|
+
"overrides": {
|
|
74
|
+
"lodash": "^4.18.0",
|
|
75
|
+
"path-to-regexp": "^8.4.0"
|
|
76
|
+
},
|
|
73
77
|
"devDependencies": {
|
|
74
78
|
"@types/archiver": "^7.0.0",
|
|
75
79
|
"@types/bun": "^1.2.4",
|
|
@@ -75,6 +75,9 @@ const loopbackServer = mockServer("127.0.0.1");
|
|
|
75
75
|
/** Mock non-loopback server -- returns a public IP for all requests. */
|
|
76
76
|
const nonLoopbackServer = mockServer("203.0.113.50");
|
|
77
77
|
|
|
78
|
+
/** Mock LAN peer -- returns a private RFC 1918 IP (not loopback). */
|
|
79
|
+
const lanPeerServer = mockServer("192.168.1.100");
|
|
80
|
+
|
|
78
81
|
initializeDb();
|
|
79
82
|
|
|
80
83
|
beforeEach(() => {
|
|
@@ -649,6 +652,25 @@ describe("pairing credential flow", () => {
|
|
|
649
652
|
// ---------------------------------------------------------------------------
|
|
650
653
|
|
|
651
654
|
describe("bootstrap private-network guard", () => {
|
|
655
|
+
test("rejects bootstrap request with private X-Forwarded-For", async () => {
|
|
656
|
+
const { handleGuardianBootstrap } =
|
|
657
|
+
await import("../runtime/routes/guardian-bootstrap-routes.js");
|
|
658
|
+
|
|
659
|
+
const req = new Request("http://localhost/v1/guardian/init", {
|
|
660
|
+
method: "POST",
|
|
661
|
+
headers: {
|
|
662
|
+
"Content-Type": "application/json",
|
|
663
|
+
"X-Forwarded-For": "192.168.1.10",
|
|
664
|
+
},
|
|
665
|
+
body: JSON.stringify({ platform: "macos", deviceId: "test-device" }),
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
const res = await handleGuardianBootstrap(req, loopbackServer);
|
|
669
|
+
expect(res.status).toBe(403);
|
|
670
|
+
const body = (await res.json()) as { error: { message: string } };
|
|
671
|
+
expect(body.error.message).toContain("local-only");
|
|
672
|
+
});
|
|
673
|
+
|
|
652
674
|
test("rejects bootstrap request with public X-Forwarded-For", async () => {
|
|
653
675
|
const { handleGuardianBootstrap } =
|
|
654
676
|
await import("../runtime/routes/guardian-bootstrap-routes.js");
|
|
@@ -697,4 +719,50 @@ describe("bootstrap private-network guard", () => {
|
|
|
697
719
|
const res = await handleGuardianBootstrap(req, loopbackServer);
|
|
698
720
|
expect(res.status).toBe(200);
|
|
699
721
|
});
|
|
722
|
+
|
|
723
|
+
test("rejects LAN peer in non-containerized mode", async () => {
|
|
724
|
+
// Default IS_CONTAINERIZED is unset (non-containerized).
|
|
725
|
+
delete process.env.IS_CONTAINERIZED;
|
|
726
|
+
|
|
727
|
+
const { handleGuardianBootstrap } =
|
|
728
|
+
await import("../runtime/routes/guardian-bootstrap-routes.js");
|
|
729
|
+
|
|
730
|
+
const req = new Request("http://localhost/v1/guardian/init", {
|
|
731
|
+
method: "POST",
|
|
732
|
+
headers: { "Content-Type": "application/json" },
|
|
733
|
+
body: JSON.stringify({ platform: "macos", deviceId: "test-device-lan" }),
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
const res = await handleGuardianBootstrap(req, lanPeerServer);
|
|
737
|
+
expect(res.status).toBe(403);
|
|
738
|
+
const body = (await res.json()) as { error: { message: string } };
|
|
739
|
+
expect(body.error.message).toContain("local-only");
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
test("accepts LAN peer in containerized mode", async () => {
|
|
743
|
+
const prev = process.env.IS_CONTAINERIZED;
|
|
744
|
+
process.env.IS_CONTAINERIZED = "true";
|
|
745
|
+
try {
|
|
746
|
+
const { handleGuardianBootstrap } =
|
|
747
|
+
await import("../runtime/routes/guardian-bootstrap-routes.js");
|
|
748
|
+
|
|
749
|
+
const req = new Request("http://localhost/v1/guardian/init", {
|
|
750
|
+
method: "POST",
|
|
751
|
+
headers: { "Content-Type": "application/json" },
|
|
752
|
+
body: JSON.stringify({
|
|
753
|
+
platform: "macos",
|
|
754
|
+
deviceId: "test-device-docker",
|
|
755
|
+
}),
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
const res = await handleGuardianBootstrap(req, lanPeerServer);
|
|
759
|
+
expect(res.status).toBe(200);
|
|
760
|
+
} finally {
|
|
761
|
+
if (prev === undefined) {
|
|
762
|
+
delete process.env.IS_CONTAINERIZED;
|
|
763
|
+
} else {
|
|
764
|
+
process.env.IS_CONTAINERIZED = prev;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
});
|
|
700
768
|
});
|
|
@@ -468,38 +468,6 @@ describe("AgentLoop", () => {
|
|
|
468
468
|
).toBe(false);
|
|
469
469
|
});
|
|
470
470
|
|
|
471
|
-
// 8. Progress reminder injection every 5 tool-use turns
|
|
472
|
-
test("injects progress reminder after every 5 tool-use turns", async () => {
|
|
473
|
-
// Create 6 tool responses followed by a text response
|
|
474
|
-
const responses: ProviderResponse[] = [];
|
|
475
|
-
for (let i = 0; i < 6; i++) {
|
|
476
|
-
responses.push(
|
|
477
|
-
toolUseResponse(`t${i}`, "read_file", { path: `/file${i}.txt` }),
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
responses.push(textResponse("Finally done"));
|
|
481
|
-
|
|
482
|
-
const { provider, calls } = createMockProvider(responses);
|
|
483
|
-
const toolExecutor = async () => ({ content: "data", isError: false });
|
|
484
|
-
const loop = new AgentLoop(
|
|
485
|
-
provider,
|
|
486
|
-
"system",
|
|
487
|
-
{},
|
|
488
|
-
dummyTools,
|
|
489
|
-
toolExecutor,
|
|
490
|
-
);
|
|
491
|
-
|
|
492
|
-
await loop.run([userMessage], () => {});
|
|
493
|
-
|
|
494
|
-
// After the 5th tool-use turn, the user message should contain a progress reminder
|
|
495
|
-
// calls[5] is the 6th provider call; its messages[-1] should have the reminder
|
|
496
|
-
const fifthTurnResultMsg = calls[5].messages[calls[5].messages.length - 1];
|
|
497
|
-
const reminderBlock = fifthTurnResultMsg.content.find(
|
|
498
|
-
(b): b is Extract<ContentBlock, { type: "text" }> =>
|
|
499
|
-
b.type === "text" && b.text.includes("making meaningful progress"),
|
|
500
|
-
);
|
|
501
|
-
expect(reminderBlock).toBeDefined();
|
|
502
|
-
});
|
|
503
471
|
|
|
504
472
|
// 9. Tool executor error results are forwarded correctly
|
|
505
473
|
test("forwards tool error results to provider", async () => {
|
|
@@ -187,6 +187,58 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
187
187
|
expect(system[1].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
188
188
|
});
|
|
189
189
|
|
|
190
|
+
test("drops static system block cache_control when total would exceed 4", async () => {
|
|
191
|
+
const staticBlock = "You are a helpful assistant.";
|
|
192
|
+
const dynamicBlock = "User workspace files here.";
|
|
193
|
+
const prompt = staticBlock + SYSTEM_PROMPT_CACHE_BOUNDARY + dynamicBlock;
|
|
194
|
+
|
|
195
|
+
// Boundary (2 system) + tools (1) + turn-start (1) + tail (1) = 5 → must cap at 4
|
|
196
|
+
const messages: Message[] = [
|
|
197
|
+
userMsg("Do something"),
|
|
198
|
+
toolUseMsg("tu_1", "bash"),
|
|
199
|
+
toolResultMsg("tu_1", "output"),
|
|
200
|
+
];
|
|
201
|
+
await provider.sendMessage(messages, sampleTools, prompt);
|
|
202
|
+
|
|
203
|
+
const system = lastStreamParams!.system as Array<{
|
|
204
|
+
type: string;
|
|
205
|
+
text: string;
|
|
206
|
+
cache_control?: { type: string; ttl?: string };
|
|
207
|
+
}>;
|
|
208
|
+
expect(system).toHaveLength(2);
|
|
209
|
+
// Static block's cache_control dropped (small, cheap to re-read)
|
|
210
|
+
expect(system[0].cache_control).toBeUndefined();
|
|
211
|
+
// Dynamic block keeps its cache_control
|
|
212
|
+
expect(system[1].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
213
|
+
|
|
214
|
+
// Tools breakpoint still present
|
|
215
|
+
const tools = lastStreamParams!.tools as Array<{
|
|
216
|
+
cache_control?: { type: string; ttl?: string };
|
|
217
|
+
}>;
|
|
218
|
+
expect(tools[tools.length - 1].cache_control).toEqual({
|
|
219
|
+
type: "ephemeral",
|
|
220
|
+
ttl: "1h",
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Turn-start + tail breakpoints still present
|
|
224
|
+
const sent = lastStreamParams!.messages as Array<{
|
|
225
|
+
role: string;
|
|
226
|
+
content: Array<{
|
|
227
|
+
type: string;
|
|
228
|
+
cache_control?: { type: string; ttl?: string };
|
|
229
|
+
}>;
|
|
230
|
+
}>;
|
|
231
|
+
const turnStart = sent[0];
|
|
232
|
+
expect(
|
|
233
|
+
turnStart.content[turnStart.content.length - 1].cache_control,
|
|
234
|
+
).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
235
|
+
const lastMsg = sent[sent.length - 1];
|
|
236
|
+
expect(lastMsg.content[lastMsg.content.length - 1].cache_control).toEqual({
|
|
237
|
+
type: "ephemeral",
|
|
238
|
+
ttl: "5m",
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
190
242
|
// -----------------------------------------------------------------------
|
|
191
243
|
// Tool cache control
|
|
192
244
|
// -----------------------------------------------------------------------
|
|
@@ -225,33 +277,22 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
225
277
|
});
|
|
226
278
|
|
|
227
279
|
// -----------------------------------------------------------------------
|
|
228
|
-
//
|
|
280
|
+
// Advancing tail — 5m cache on last block after turn-starting message
|
|
229
281
|
// -----------------------------------------------------------------------
|
|
230
|
-
test("
|
|
282
|
+
test("no advancing tail cache when turn-starting user message is last", async () => {
|
|
231
283
|
await provider.sendMessage([userMsg("Hello")]);
|
|
232
284
|
|
|
233
|
-
|
|
234
|
-
role: string;
|
|
235
|
-
content: Array<{
|
|
236
|
-
type: string;
|
|
237
|
-
text: string;
|
|
238
|
-
cache_control?: { type: string; ttl?: string };
|
|
239
|
-
}>;
|
|
240
|
-
}>;
|
|
241
|
-
const lastUser = messages[messages.length - 1];
|
|
242
|
-
expect(lastUser.role).toBe("user");
|
|
285
|
+
// No top-level cache_control — would conflict with the 1h block breakpoint
|
|
243
286
|
expect(
|
|
244
|
-
|
|
287
|
+
(lastStreamParams as Record<string, unknown>).cache_control,
|
|
245
288
|
).toBeUndefined();
|
|
246
289
|
});
|
|
247
290
|
|
|
248
|
-
test("
|
|
291
|
+
test("advancing tail: 5m cache on last block when tool results follow turn-starting message", async () => {
|
|
249
292
|
const messages: Message[] = [
|
|
250
|
-
userMsg("
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
assistantMsg("Response 2"),
|
|
254
|
-
userMsg("Turn 3"), // user turn 2 — no cache (last)
|
|
293
|
+
userMsg("Do something"),
|
|
294
|
+
toolUseMsg("tu_1", "bash"),
|
|
295
|
+
toolResultMsg("tu_1", "output"),
|
|
255
296
|
];
|
|
256
297
|
await provider.sendMessage(messages);
|
|
257
298
|
|
|
@@ -259,33 +300,30 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
259
300
|
role: string;
|
|
260
301
|
content: Array<{
|
|
261
302
|
type: string;
|
|
262
|
-
text: string;
|
|
263
303
|
cache_control?: { type: string; ttl?: string };
|
|
264
304
|
}>;
|
|
265
305
|
}>;
|
|
266
306
|
|
|
267
|
-
//
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
// Second user turn (second-to-last): cache_control ephemeral
|
|
277
|
-
const secondUserLastBlock =
|
|
278
|
-
userMessages[1].content[userMessages[1].content.length - 1];
|
|
279
|
-
expect(secondUserLastBlock.cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
280
|
-
|
|
281
|
-
// Third user turn (last): no cache_control
|
|
282
|
-
const thirdUserLastBlock =
|
|
283
|
-
userMessages[2].content[userMessages[2].content.length - 1];
|
|
284
|
-
expect(thirdUserLastBlock.cache_control).toBeUndefined();
|
|
307
|
+
// Turn-starting user message (first) keeps 1h
|
|
308
|
+
const turnStart = sent[0];
|
|
309
|
+
const turnStartLast = turnStart.content[turnStart.content.length - 1];
|
|
310
|
+
expect(turnStartLast.cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
311
|
+
|
|
312
|
+
// Last message (tool_result) gets 5m advancing tail
|
|
313
|
+
const lastMessage = sent[sent.length - 1];
|
|
314
|
+
const lastBlock = lastMessage.content[lastMessage.content.length - 1];
|
|
315
|
+
expect(lastBlock.cache_control).toEqual({ type: "ephemeral", ttl: "5m" });
|
|
285
316
|
});
|
|
286
317
|
|
|
287
|
-
test("
|
|
288
|
-
|
|
318
|
+
test("turn-starting user message gets 1h cache on last block", async () => {
|
|
319
|
+
const messages: Message[] = [
|
|
320
|
+
userMsg("Turn 1"),
|
|
321
|
+
assistantMsg("Response 1"),
|
|
322
|
+
userMsg("Turn 2"),
|
|
323
|
+
assistantMsg("Response 2"),
|
|
324
|
+
userMsg("Turn 3"),
|
|
325
|
+
];
|
|
326
|
+
await provider.sendMessage(messages);
|
|
289
327
|
|
|
290
328
|
const sent = lastStreamParams!.messages as Array<{
|
|
291
329
|
role: string;
|
|
@@ -295,35 +333,17 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
295
333
|
cache_control?: { type: string; ttl?: string };
|
|
296
334
|
}>;
|
|
297
335
|
}>;
|
|
298
|
-
const userMessages = sent.filter((m) => m.role === "user");
|
|
299
|
-
expect(userMessages).toHaveLength(1);
|
|
300
|
-
expect(
|
|
301
|
-
userMessages[0].content[userMessages[0].content.length - 1].cache_control,
|
|
302
|
-
).toBeUndefined();
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
// -----------------------------------------------------------------------
|
|
306
|
-
// User turn with tool_result — cache breakpoint on second-to-last
|
|
307
|
-
// -----------------------------------------------------------------------
|
|
308
|
-
test("user turn containing tool_result gets cache_control on second-to-last user turn only", async () => {
|
|
309
|
-
const messages: Message[] = [
|
|
310
|
-
userMsg("Read file"),
|
|
311
|
-
toolUseMsg("tu_1", "file_read"),
|
|
312
|
-
toolResultMsg("tu_1", "file contents here"),
|
|
313
|
-
];
|
|
314
|
-
await provider.sendMessage(messages);
|
|
315
336
|
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
expect(secondLast.cache_control).toBeUndefined();
|
|
337
|
+
const userMessages = sent.filter((m) => m.role === "user");
|
|
338
|
+
// Only the last user message (turn-starting) gets cache_control
|
|
339
|
+
for (const user of userMessages.slice(0, -1)) {
|
|
340
|
+
for (const block of user.content) {
|
|
341
|
+
expect(block.cache_control).toBeUndefined();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
const lastUser = userMessages[userMessages.length - 1];
|
|
345
|
+
const lastBlock = lastUser.content[lastUser.content.length - 1];
|
|
346
|
+
expect(lastBlock.cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
327
347
|
});
|
|
328
348
|
|
|
329
349
|
// -----------------------------------------------------------------------
|
|
@@ -358,7 +378,7 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
358
378
|
// -----------------------------------------------------------------------
|
|
359
379
|
// Multi-block user message: cache lands on LAST block
|
|
360
380
|
// -----------------------------------------------------------------------
|
|
361
|
-
test("multi-block single user message
|
|
381
|
+
test("multi-block single user message gets cache on last block", async () => {
|
|
362
382
|
const multiBlockUser: Message = {
|
|
363
383
|
role: "user",
|
|
364
384
|
content: [
|
|
@@ -378,7 +398,7 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
378
398
|
}>;
|
|
379
399
|
const user = sent[0];
|
|
380
400
|
expect(user.content[0].cache_control).toBeUndefined();
|
|
381
|
-
expect(user.content[1].cache_control).
|
|
401
|
+
expect(user.content[1].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
382
402
|
});
|
|
383
403
|
|
|
384
404
|
// -----------------------------------------------------------------------
|
|
@@ -395,14 +415,14 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
395
415
|
// -----------------------------------------------------------------------
|
|
396
416
|
// Cache compatibility with workspace context injection
|
|
397
417
|
// -----------------------------------------------------------------------
|
|
398
|
-
test("workspace-prepended single user message
|
|
418
|
+
test("workspace-prepended single user message gets cache on last block", async () => {
|
|
399
419
|
// Simulates what applyRuntimeInjections does: prepend workspace block, keep user text as trailing
|
|
400
420
|
const workspaceInjectedUser: Message = {
|
|
401
421
|
role: "user",
|
|
402
422
|
content: [
|
|
403
423
|
{
|
|
404
424
|
type: "text",
|
|
405
|
-
text: "<
|
|
425
|
+
text: "<workspace>\nRoot: /sandbox\nDirectories: src, tests\n</workspace>",
|
|
406
426
|
},
|
|
407
427
|
{ type: "text", text: "What files are in src?" },
|
|
408
428
|
],
|
|
@@ -421,18 +441,18 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
421
441
|
expect(user.content).toHaveLength(2);
|
|
422
442
|
// Workspace block (first): no cache_control
|
|
423
443
|
expect(user.content[0].cache_control).toBeUndefined();
|
|
424
|
-
// User text (last):
|
|
425
|
-
expect(user.content[1].cache_control).
|
|
444
|
+
// User text (last): cache_control with 1h TTL
|
|
445
|
+
expect(user.content[1].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
426
446
|
});
|
|
427
447
|
|
|
428
|
-
test("workspace + multi-block single user message:
|
|
448
|
+
test("workspace + multi-block single user message: cache on last block only", async () => {
|
|
429
449
|
// Simulates workspace prepended + extra context block appended
|
|
430
450
|
const injectedUser: Message = {
|
|
431
451
|
role: "user",
|
|
432
452
|
content: [
|
|
433
453
|
{
|
|
434
454
|
type: "text",
|
|
435
|
-
text: "<
|
|
455
|
+
text: "<workspace>\nRoot: /sandbox\nDirectories: src, tests\n</workspace>",
|
|
436
456
|
},
|
|
437
457
|
{ type: "text", text: "Help me debug this" },
|
|
438
458
|
{
|
|
@@ -453,10 +473,10 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
453
473
|
}>;
|
|
454
474
|
const user = sent[0];
|
|
455
475
|
expect(user.content).toHaveLength(3);
|
|
456
|
-
//
|
|
476
|
+
// Only last block gets cache_control
|
|
457
477
|
expect(user.content[0].cache_control).toBeUndefined();
|
|
458
478
|
expect(user.content[1].cache_control).toBeUndefined();
|
|
459
|
-
expect(user.content[2].cache_control).
|
|
479
|
+
expect(user.content[2].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
460
480
|
});
|
|
461
481
|
|
|
462
482
|
// -----------------------------------------------------------------------
|
|
@@ -550,7 +570,7 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
550
570
|
content: [
|
|
551
571
|
{
|
|
552
572
|
type: "text",
|
|
553
|
-
text: "<
|
|
573
|
+
text: "<workspace>\nRoot: /sandbox\n</workspace>",
|
|
554
574
|
},
|
|
555
575
|
{
|
|
556
576
|
type: "tool_result",
|
|
@@ -931,7 +951,7 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
931
951
|
|
|
932
952
|
// Assistant message should have tool_use in paired portion, server_tool_use in carryover
|
|
933
953
|
// ensureToolPairing splits: paired = [tool_use(tu_a)], carryover = [server_tool_use(srvtoolu_b)]
|
|
934
|
-
// Result: assistant(tool_use) → user(tool_result) → assistant(server_tool_use) → user(
|
|
954
|
+
// Result: assistant(tool_use) → user(tool_result) → assistant(server_tool_use) → user(synthetic_continuation)
|
|
935
955
|
const assistantMsg = sent[1];
|
|
936
956
|
expect(assistantMsg.role).toBe("assistant");
|
|
937
957
|
expect(assistantMsg.content[0].type).toBe("tool_use");
|
|
@@ -1258,7 +1278,7 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
1258
1278
|
// 2. assistant(tool_use)
|
|
1259
1279
|
// 3. user(tool_result)
|
|
1260
1280
|
// 4. assistant(Checking the file now.)
|
|
1261
|
-
// 5. user(
|
|
1281
|
+
// 5. user(<synthetic_continuation __injected />) <-- synthetic user message to maintain alternation
|
|
1262
1282
|
// 6. assistant(Next response)
|
|
1263
1283
|
expect(sent).toHaveLength(6);
|
|
1264
1284
|
expect(sent[0].role).toBe("user");
|
|
@@ -1271,44 +1291,95 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
1271
1291
|
expect(sent[3].content[0].text).toBe("Checking the file now.");
|
|
1272
1292
|
expect(sent[4].role).toBe("user");
|
|
1273
1293
|
expect(sent[4].content[0].type).toBe("text");
|
|
1274
|
-
expect(sent[4].content[0].text).toBe(
|
|
1294
|
+
expect(sent[4].content[0].text).toBe(
|
|
1295
|
+
"<synthetic_continuation __injected />",
|
|
1296
|
+
);
|
|
1275
1297
|
expect(sent[5].role).toBe("assistant");
|
|
1276
1298
|
expect(sent[5].content[0].text).toBe("Next response");
|
|
1277
1299
|
});
|
|
1278
1300
|
|
|
1279
|
-
test("
|
|
1301
|
+
test("carryover with tool_result-only user turn skips synthetic when next message is user", async () => {
|
|
1302
|
+
// When the user turn after the consumed pair is already a user message,
|
|
1303
|
+
// the synthetic continuation is unnecessary — the next user message
|
|
1304
|
+
// naturally maintains alternation after the carryover assistant message.
|
|
1305
|
+
const messages: Message[] = [
|
|
1306
|
+
userMsg("Read file"),
|
|
1307
|
+
{
|
|
1308
|
+
role: "assistant",
|
|
1309
|
+
content: [
|
|
1310
|
+
{ type: "tool_use", id: "tu_1", name: "file_read", input: {} },
|
|
1311
|
+
{ type: "text", text: "Checking the file now." }, // carryover content
|
|
1312
|
+
],
|
|
1313
|
+
},
|
|
1314
|
+
{
|
|
1315
|
+
role: "user",
|
|
1316
|
+
content: [
|
|
1317
|
+
// ONLY tool_result, no other content
|
|
1318
|
+
{
|
|
1319
|
+
type: "tool_result",
|
|
1320
|
+
tool_use_id: "tu_1",
|
|
1321
|
+
content: "file contents",
|
|
1322
|
+
is_error: false,
|
|
1323
|
+
},
|
|
1324
|
+
],
|
|
1325
|
+
},
|
|
1326
|
+
userMsg("Follow-up question"), // next message is user — no synthetic needed
|
|
1327
|
+
];
|
|
1328
|
+
await provider.sendMessage(messages);
|
|
1329
|
+
|
|
1330
|
+
const sent = lastStreamParams!.messages as Array<{
|
|
1331
|
+
role: string;
|
|
1332
|
+
content: Array<{ type: string; text?: string; tool_use_id?: string }>;
|
|
1333
|
+
}>;
|
|
1334
|
+
|
|
1335
|
+
// Expected structure:
|
|
1336
|
+
// 1. user(Read file)
|
|
1337
|
+
// 2. assistant(tool_use)
|
|
1338
|
+
// 3. user(tool_result)
|
|
1339
|
+
// 4. assistant(Checking the file now.)
|
|
1340
|
+
// 5. user(Follow-up question) <-- real user message, NO synthetic continuation
|
|
1341
|
+
expect(sent).toHaveLength(5);
|
|
1342
|
+
expect(sent[0].role).toBe("user");
|
|
1343
|
+
expect(sent[1].role).toBe("assistant");
|
|
1344
|
+
expect(sent[1].content[0].type).toBe("tool_use");
|
|
1345
|
+
expect(sent[2].role).toBe("user");
|
|
1346
|
+
expect(sent[2].content[0].type).toBe("tool_result");
|
|
1347
|
+
expect(sent[3].role).toBe("assistant");
|
|
1348
|
+
expect(sent[3].content[0].text).toBe("Checking the file now.");
|
|
1349
|
+
expect(sent[4].role).toBe("user");
|
|
1350
|
+
expect(sent[4].content[0].text).toBe("Follow-up question");
|
|
1351
|
+
});
|
|
1352
|
+
|
|
1353
|
+
test("multi-turn with workspace injection: only last user message gets 1h cache", async () => {
|
|
1280
1354
|
const messages: Message[] = [
|
|
1281
|
-
// Turn 1: workspace + user text (no cache - 3rd-to-last)
|
|
1282
1355
|
{
|
|
1283
1356
|
role: "user",
|
|
1284
1357
|
content: [
|
|
1285
1358
|
{
|
|
1286
1359
|
type: "text",
|
|
1287
|
-
text: "<
|
|
1360
|
+
text: "<workspace>\nRoot: /sandbox\nDirectories: src\n</workspace>",
|
|
1288
1361
|
},
|
|
1289
1362
|
{ type: "text", text: "Turn 1" },
|
|
1290
1363
|
],
|
|
1291
1364
|
},
|
|
1292
1365
|
assistantMsg("Response 1"),
|
|
1293
|
-
// Turn 2: workspace + user text (cache - second-to-last)
|
|
1294
1366
|
{
|
|
1295
1367
|
role: "user",
|
|
1296
1368
|
content: [
|
|
1297
1369
|
{
|
|
1298
1370
|
type: "text",
|
|
1299
|
-
text: "<
|
|
1371
|
+
text: "<workspace>\nRoot: /sandbox\nDirectories: src, lib\n</workspace>",
|
|
1300
1372
|
},
|
|
1301
1373
|
{ type: "text", text: "Turn 2" },
|
|
1302
1374
|
],
|
|
1303
1375
|
},
|
|
1304
1376
|
assistantMsg("Response 2"),
|
|
1305
|
-
// Turn 3: workspace + user text (no cache - last)
|
|
1306
1377
|
{
|
|
1307
1378
|
role: "user",
|
|
1308
1379
|
content: [
|
|
1309
1380
|
{
|
|
1310
1381
|
type: "text",
|
|
1311
|
-
text: "<
|
|
1382
|
+
text: "<workspace>\nRoot: /sandbox\nDirectories: src, lib, docs\n</workspace>",
|
|
1312
1383
|
},
|
|
1313
1384
|
{ type: "text", text: "Turn 3" },
|
|
1314
1385
|
],
|
|
@@ -1327,17 +1398,65 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
1327
1398
|
const userMsgs = sent.filter((m) => m.role === "user");
|
|
1328
1399
|
expect(userMsgs).toHaveLength(3);
|
|
1329
1400
|
|
|
1330
|
-
//
|
|
1331
|
-
|
|
1332
|
-
|
|
1401
|
+
// Earlier user messages: no cache_control
|
|
1402
|
+
for (const user of userMsgs.slice(0, -1)) {
|
|
1403
|
+
for (const block of user.content) {
|
|
1404
|
+
expect(block.cache_control).toBeUndefined();
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// Last user message (turn 3): 1h cache on last block only
|
|
1409
|
+
const lastUser = userMsgs[userMsgs.length - 1];
|
|
1410
|
+
expect(lastUser.content[0].cache_control).toBeUndefined();
|
|
1411
|
+
expect(lastUser.content[1].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
1412
|
+
|
|
1413
|
+
// No top-level cache_control — breakpoints are set directly on blocks
|
|
1414
|
+
expect(
|
|
1415
|
+
(lastStreamParams as Record<string, unknown>).cache_control,
|
|
1416
|
+
).toBeUndefined();
|
|
1417
|
+
});
|
|
1418
|
+
|
|
1419
|
+
test("tool loop: turn-starting user message gets 1h cache, last tool_result gets 5m advancing tail", async () => {
|
|
1420
|
+
const messages: Message[] = [
|
|
1421
|
+
userMsg("Read the config file"),
|
|
1422
|
+
toolUseMsg("tu_1", "file_read"),
|
|
1423
|
+
toolResultMsg("tu_1", "config contents here"),
|
|
1424
|
+
toolUseMsg("tu_2", "file_read"),
|
|
1425
|
+
toolResultMsg("tu_2", "more contents"),
|
|
1426
|
+
];
|
|
1427
|
+
await provider.sendMessage(messages);
|
|
1428
|
+
|
|
1429
|
+
const sent = lastStreamParams!.messages as Array<{
|
|
1430
|
+
role: string;
|
|
1431
|
+
content: Array<{
|
|
1432
|
+
type: string;
|
|
1433
|
+
text?: string;
|
|
1434
|
+
cache_control?: { type: string; ttl?: string };
|
|
1435
|
+
}>;
|
|
1436
|
+
}>;
|
|
1333
1437
|
|
|
1334
|
-
//
|
|
1335
|
-
expect(
|
|
1336
|
-
expect(
|
|
1438
|
+
// First message is the turn-starting user text — gets 1h cache
|
|
1439
|
+
expect(sent[0].role).toBe("user");
|
|
1440
|
+
expect(sent[0].content[0].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
1441
|
+
|
|
1442
|
+
// Non-last tool result messages do NOT get cache_control
|
|
1443
|
+
const toolResultMsgs = sent.filter(
|
|
1444
|
+
(m) =>
|
|
1445
|
+
m.role === "user" &&
|
|
1446
|
+
Array.isArray(m.content) &&
|
|
1447
|
+
m.content.every((b) => typeof b !== "string" && b.type === "tool_result"),
|
|
1448
|
+
);
|
|
1449
|
+
expect(toolResultMsgs.length).toBeGreaterThan(0);
|
|
1450
|
+
for (const tr of toolResultMsgs.slice(0, -1)) {
|
|
1451
|
+
for (const block of tr.content) {
|
|
1452
|
+
expect(block.cache_control).toBeUndefined();
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1337
1455
|
|
|
1338
|
-
//
|
|
1339
|
-
|
|
1340
|
-
|
|
1456
|
+
// Last message gets 5m advancing tail cache on its last block
|
|
1457
|
+
const lastMsg = sent[sent.length - 1];
|
|
1458
|
+
const lastBlock = lastMsg.content[lastMsg.content.length - 1];
|
|
1459
|
+
expect(lastBlock.cache_control).toEqual({ type: "ephemeral", ttl: "5m" });
|
|
1341
1460
|
});
|
|
1342
1461
|
|
|
1343
1462
|
// -----------------------------------------------------------------------
|