@vellumai/assistant 0.8.1 → 0.8.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 +13 -19
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +17 -0
- package/docker-init-apt-root.sh +167 -0
- package/docker-kata-apt-env.sh +39 -0
- package/docs/plugins.md +88 -47
- package/docs/skills.md +9 -7
- package/examples/plugins/echo/README.md +27 -27
- package/examples/plugins/echo/package.json +3 -0
- package/examples/plugins/echo/register.ts +31 -31
- package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
- package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
- package/openapi.yaml +642 -5
- package/package.json +3 -1
- package/scripts/generate-openapi.ts +83 -10
- package/scripts/sync-llm-catalog.ts +2 -2
- package/scripts/sync-web-search-catalog.ts +47 -25
- package/src/__tests__/agent-image-optimize.test.ts +11 -3
- package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
- package/src/__tests__/anthropic-provider.test.ts +45 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
- package/src/__tests__/app-executors.test.ts +220 -4
- package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/channel-availability-routes.test.ts +206 -0
- package/src/__tests__/channel-delivery-store.test.ts +289 -1
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
- package/src/__tests__/clawhub.test.ts +75 -16
- package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-schema.test.ts +21 -0
- package/src/__tests__/config-set-route.test.ts +80 -0
- package/src/__tests__/config-sounds-sync.test.ts +97 -0
- package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
- package/src/__tests__/context-search-conversations-source.test.ts +117 -2
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +7 -0
- package/src/__tests__/context-token-estimator.test.ts +31 -65
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
- package/src/__tests__/conversation-agent-loop.test.ts +59 -1
- package/src/__tests__/conversation-error.test.ts +42 -3
- package/src/__tests__/conversation-fork-crud.test.ts +82 -0
- package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
- package/src/__tests__/conversation-lifecycle.test.ts +173 -0
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +4 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
- package/src/__tests__/conversation-queue.test.ts +4 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
- package/src/__tests__/conversation-slash-queue.test.ts +59 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
- package/src/__tests__/conversation-sync-tags.test.ts +235 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +3 -2
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
- package/src/__tests__/disk-pressure-tools.test.ts +1 -0
- package/src/__tests__/dm-backfill.test.ts +121 -10
- package/src/__tests__/document-tool-security.test.ts +258 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/edit-propagation.test.ts +33 -0
- package/src/__tests__/empty-response-pipeline.test.ts +0 -4
- package/src/__tests__/external-plugin-loader.test.ts +151 -55
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/helpers/tar-fixtures.ts +39 -0
- package/src/__tests__/helpers/wait-for.ts +21 -0
- package/src/__tests__/history-repair-pipeline.test.ts +0 -3
- package/src/__tests__/history-repair.test.ts +73 -0
- package/src/__tests__/host-app-control-proxy.test.ts +507 -10
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/image-credentials.test.ts +1 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
- package/src/__tests__/inference-profile-reaper.test.ts +4 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
- package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +15 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
- package/src/__tests__/list-messages-page-latest.test.ts +55 -0
- package/src/__tests__/llm-call-pipeline.test.ts +0 -3
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +58 -13
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-resolver.test.ts +255 -2
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +41 -29
- package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
- package/src/__tests__/managed-store.test.ts +84 -192
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
- package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +242 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
- package/src/__tests__/persistence-pipeline.test.ts +0 -2
- package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
- package/src/__tests__/platform.test.ts +2 -0
- package/src/__tests__/plugin-api-shim.test.ts +125 -0
- package/src/__tests__/plugin-bootstrap.test.ts +10 -36
- package/src/__tests__/plugin-external-api.test.ts +68 -0
- package/src/__tests__/plugin-registry.test.ts +0 -77
- package/src/__tests__/plugin-route-contribution.test.ts +0 -1
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
- package/src/__tests__/plugin-types.test.ts +3 -13
- package/src/__tests__/process-message-background-slack.test.ts +8 -1
- package/src/__tests__/process-message-display-content.test.ts +421 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
- package/src/__tests__/provider-error-scenarios.test.ts +111 -0
- package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
- package/src/__tests__/schedule-routes.test.ts +50 -3
- package/src/__tests__/schedule-store.test.ts +94 -0
- package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
- package/src/__tests__/schema-transforms.test.ts +20 -0
- package/src/__tests__/search-skills-unified.test.ts +0 -5
- package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
- package/src/__tests__/server-history-render.test.ts +43 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
- package/src/__tests__/skill-load-tool.test.ts +27 -89
- package/src/__tests__/skill-memory.test.ts +23 -3
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
- package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
- package/src/__tests__/skills-install-extract.test.ts +49 -38
- package/src/__tests__/skills-install-staging.test.ts +159 -0
- package/src/__tests__/skills-uninstall.test.ts +9 -41
- package/src/__tests__/skills.test.ts +51 -58
- package/src/__tests__/slack-channel-config.test.ts +9 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/system-prompt.test.ts +670 -63
- package/src/__tests__/terminal-tools.test.ts +28 -1
- package/src/__tests__/thread-backfill.test.ts +557 -27
- package/src/__tests__/title-generate-pipeline.test.ts +0 -13
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
- package/src/__tests__/tool-error-pipeline.test.ts +0 -3
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +16 -4
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
- package/src/__tests__/turn-events-store.test.ts +256 -0
- package/src/__tests__/twilio-routes.test.ts +4 -0
- package/src/__tests__/user-plugin-loader.test.ts +0 -7
- package/src/__tests__/voice-session-bridge.test.ts +198 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
- package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
- package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
- package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/agent/loop.ts +167 -18
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +122 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +960 -0
- package/src/cli/commands/changelog.ts +106 -42
- package/src/cli/commands/conversations.ts +102 -17
- package/src/cli/commands/default-action.ts +10 -53
- package/src/cli/commands/notifications.ts +388 -346
- package/src/cli/commands/plugins.ts +252 -0
- package/src/cli/commands/schedules.ts +683 -0
- package/src/cli/commands/telemetry.ts +40 -0
- package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
- package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
- package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
- package/src/cli/lib/cli-colors.ts +12 -0
- package/src/cli/lib/confirm-prompt.ts +79 -0
- package/src/cli/lib/install-from-github.ts +303 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +52 -2
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
- package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
- package/src/config/bundled-skills/document/SKILL.md +23 -3
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
- package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
- package/src/config/bundled-tool-registry.ts +6 -0
- package/src/config/call-site-defaults.ts +105 -0
- package/src/config/feature-flag-registry.json +41 -9
- package/src/config/llm-resolver.ts +52 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +9 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
- package/src/config/schemas/channels.ts +17 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +23 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +4 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1107 -0
- package/src/context/token-estimator.ts +34 -36
- package/src/context/window-manager.ts +197 -1520
- package/src/credential-execution/managed-catalog.ts +37 -0
- package/src/credential-health/credential-health-service.ts +280 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
- package/src/daemon/approval-generators.ts +8 -6
- package/src/daemon/config-watcher.ts +94 -31
- package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
- package/src/daemon/conversation-agent-loop.ts +198 -11
- package/src/daemon/conversation-error.ts +171 -37
- package/src/daemon/conversation-lifecycle.ts +53 -40
- package/src/daemon/conversation-messaging.ts +25 -6
- package/src/daemon/conversation-process.ts +49 -12
- package/src/daemon/conversation-runtime-assembly.ts +25 -1
- package/src/daemon/conversation-slash.ts +12 -5
- package/src/daemon/conversation-store.ts +11 -4
- package/src/daemon/conversation-tool-setup.ts +39 -7
- package/src/daemon/conversation.ts +33 -8
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +289 -0
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- package/src/daemon/handlers/conversations.ts +1 -0
- package/src/daemon/handlers/shared.ts +14 -5
- package/src/daemon/handlers/skills.ts +111 -108
- package/src/daemon/history-repair.ts +28 -1
- package/src/daemon/host-app-control-proxy.ts +153 -27
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/lifecycle.ts +89 -91
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +85 -0
- package/src/daemon/message-protocol.ts +1 -0
- package/src/daemon/message-types/conversations.ts +25 -0
- package/src/daemon/message-types/messages.ts +61 -0
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +11 -54
- package/src/daemon/pkb-reminder-builder.ts +5 -20
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +24 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +33 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +73 -189
- package/src/home/__tests__/feed-types.test.ts +80 -0
- package/src/home/feed-types.ts +36 -2
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- package/src/ipc/cli-client.ts +147 -45
- package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
- package/src/mcp/client.ts +20 -4
- package/src/media/image-credentials.ts +3 -3
- package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
- package/src/memory/__tests__/conversation-queries.test.ts +483 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
- package/src/memory/__tests__/message-content.test.ts +35 -0
- package/src/memory/bookmark-crud.ts +42 -10
- package/src/memory/context-search/sources/conversations.ts +62 -2
- package/src/memory/context-search/sources/workspace.ts +4 -0
- package/src/memory/conversation-crud.ts +63 -19
- package/src/memory/conversation-queries.ts +197 -11
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +12 -0
- package/src/memory/delivery-crud.ts +152 -5
- package/src/memory/embedding-backend.ts +4 -4
- package/src/memory/external-conversation-store.ts +66 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
- package/src/memory/graph/conversation-graph-memory.ts +49 -21
- package/src/memory/graph/tools.ts +9 -40
- package/src/memory/indexer.ts +34 -29
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
- package/src/memory/jobs/embed-concept-page.ts +20 -11
- package/src/memory/jobs-worker.ts +6 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/llm-usage-store.ts +125 -5
- package/src/memory/memory-retrospective-enqueue.ts +1 -20
- package/src/memory/memory-retrospective-job.ts +33 -6
- package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
- package/src/memory/message-content.ts +1 -1
- package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
- package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
- package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
- package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
- package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/index.ts +9 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +3 -3
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection.test.ts +288 -11
- package/src/memory/v2/__tests__/migration.test.ts +87 -0
- package/src/memory/v2/__tests__/page-index.test.ts +83 -0
- package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
- package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
- package/src/memory/v2/__tests__/router.test.ts +15 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection.ts +81 -26
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +63 -8
- package/src/memory/v2/prompts/router.ts +11 -8
- package/src/memory/v2/prompts/sweep.ts +2 -2
- package/src/memory/v2/qdrant.ts +135 -7
- package/src/memory/v2/router.ts +9 -8
- package/src/memory/v2/skill-store.ts +120 -35
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +11 -2
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
- package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
- package/src/messaging/providers/slack/adapter.ts +43 -5
- package/src/messaging/providers/slack/client.ts +27 -0
- package/src/messaging/providers/slack/deep-link.ts +65 -0
- package/src/messaging/providers/slack/download.ts +104 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
- package/src/messaging/providers/slack/message-metadata.ts +27 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
- package/src/messaging/providers/slack/render-transcript.ts +69 -5
- package/src/messaging/providers/slack/types.ts +20 -1
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-pairing.ts +2 -1
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +113 -45
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +21 -1
- package/src/notifications/home-feed-side-effect.ts +138 -5
- package/src/notifications/signal.ts +3 -5
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +19 -6
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/question-prompter.test.ts +416 -0
- package/src/permissions/question-prompter.ts +294 -0
- package/src/platform/client.test.ts +1 -1
- package/src/platform/client.ts +1 -1
- package/src/plugin-api/constants.ts +26 -0
- package/src/plugin-api/index.ts +34 -1
- package/src/plugin-api/types.ts +104 -22
- package/src/plugins/defaults/circuit-breaker.ts +0 -5
- package/src/plugins/defaults/compaction.ts +0 -4
- package/src/plugins/defaults/empty-response.ts +0 -2
- package/src/plugins/defaults/history-repair.ts +0 -2
- package/src/plugins/defaults/injectors.ts +74 -22
- package/src/plugins/defaults/llm-call.ts +0 -2
- package/src/plugins/defaults/memory-retrieval.ts +0 -1
- package/src/plugins/defaults/overflow-reduce.ts +0 -1
- package/src/plugins/defaults/persistence.ts +0 -2
- package/src/plugins/defaults/title-generate.ts +0 -5
- package/src/plugins/defaults/token-estimate.ts +0 -2
- package/src/plugins/defaults/tool-error.ts +0 -7
- package/src/plugins/defaults/tool-execute.ts +0 -2
- package/src/plugins/defaults/tool-result-truncate.ts +0 -4
- package/src/plugins/ensure-plugin-api-shim.ts +96 -0
- package/src/plugins/external-api.ts +104 -0
- package/src/plugins/external-plugin-loader.ts +187 -42
- package/src/plugins/feature-gate.ts +22 -0
- package/src/plugins/pipeline.ts +37 -0
- package/src/plugins/registry.ts +48 -80
- package/src/plugins/types.ts +40 -26
- package/src/plugins/user-loader.ts +21 -2
- package/src/proactive-artifact/aux-message-injector.ts +11 -0
- package/src/proactive-artifact/job.test.ts +37 -5
- package/src/prompts/__tests__/system-prompt.test.ts +10 -43
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -174
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +164 -0
- package/src/providers/__tests__/inference.test.ts +24 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +68 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +32 -6
- package/src/providers/inference/auth.ts +12 -0
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/connections.ts +159 -34
- package/src/providers/inference/resolve-auth.ts +14 -4
- package/src/providers/model-catalog.ts +249 -12
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +169 -8
- package/src/providers/openrouter/client.ts +49 -4
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
- package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
- package/src/providers/provider-availability.ts +17 -2
- package/src/providers/provider-catalog-visibility.ts +38 -0
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +52 -15
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +103 -15
- package/src/runtime/auth/route-policy.ts +21 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +19 -47
- package/src/runtime/migrations/origin-mode.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
- package/src/runtime/routes/acp-routes-list.test.ts +143 -0
- package/src/runtime/routes/acp-routes.ts +5 -3
- package/src/runtime/routes/auth-routes.ts +1 -1
- package/src/runtime/routes/bookmark-routes.ts +5 -3
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/channel-availability-routes.ts +126 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/conversation-cli-routes.ts +44 -3
- package/src/runtime/routes/conversation-list-routes.ts +3 -20
- package/src/runtime/routes/conversation-management-routes.ts +17 -42
- package/src/runtime/routes/conversation-query-routes.ts +99 -35
- package/src/runtime/routes/conversation-routes.ts +97 -11
- package/src/runtime/routes/documents-routes.ts +25 -86
- package/src/runtime/routes/group-routes.ts +5 -0
- package/src/runtime/routes/inbound-conversation.ts +28 -8
- package/src/runtime/routes/inbound-message-handler.ts +236 -41
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
- package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
- package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
- package/src/runtime/routes/integrations/a2a.ts +235 -0
- package/src/runtime/routes/integrations/slack/share.ts +4 -52
- package/src/runtime/routes/integrations/slack/token.ts +43 -0
- package/src/runtime/routes/integrations/twilio.ts +6 -13
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/notification-routes.ts +1 -1
- package/src/runtime/routes/oauth-commands-routes.ts +105 -15
- package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
- package/src/runtime/routes/question-routes.ts +259 -0
- package/src/runtime/routes/rename-conversation-routes.ts +2 -33
- package/src/runtime/routes/schedule-routes.ts +4 -7
- package/src/runtime/routes/subagents-routes.ts +98 -18
- package/src/runtime/routes/telemetry-routes.ts +27 -0
- package/src/runtime/routes/tts-routes.ts +27 -2
- package/src/runtime/routes/workspace-routes.test.ts +43 -0
- package/src/runtime/routes/workspace-routes.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +39 -7
- package/src/runtime/sync/resource-sync-events.ts +93 -1
- package/src/schedule/schedule-store.ts +27 -2
- package/src/schedule/scheduler.ts +9 -1
- package/src/security/__tests__/untrusted-content.test.ts +86 -0
- package/src/security/untrusted-content.ts +93 -8
- package/src/skills/catalog-files.ts +1 -1
- package/src/skills/catalog-install.ts +233 -116
- package/src/skills/clawhub.ts +70 -13
- package/src/skills/managed-store.ts +4 -119
- package/src/skills/skillssh-registry.ts +27 -48
- package/src/subagent/manager.ts +17 -7
- package/src/telemetry/types.ts +113 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
- package/src/telemetry/usage-telemetry-reporter.ts +113 -7
- package/src/tools/apps/executors.ts +58 -7
- package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
- package/src/tools/ask-question/ask-question-tool.ts +304 -0
- package/src/tools/browser/browser-execution.ts +15 -11
- package/src/tools/computer-use/definitions.ts +3 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/document/document-tool.ts +124 -1
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +5 -2
- package/src/tools/host-filesystem/transfer.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +1 -1
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/permission-checker.ts +1 -1
- package/src/tools/registry.ts +17 -7
- package/src/tools/schedule/create.ts +2 -2
- package/src/tools/schema-transforms.ts +7 -2
- package/src/tools/side-effects.ts +1 -0
- package/src/tools/skills/delete-managed.ts +4 -4
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/scaffold-managed.ts +3 -2
- package/src/tools/subagent/notify-parent.ts +1 -1
- package/src/tools/system/request-permission.ts +2 -2
- package/src/tools/terminal/safe-env.ts +60 -1
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/types.ts +107 -21
- package/src/tools/ui-surface/definitions.ts +6 -5
- package/src/tts/__tests__/provider-adapters.test.ts +76 -2
- package/src/tts/providers/elevenlabs-provider.ts +75 -1
- package/src/types/onboarding-context.ts +2 -0
- package/src/util/errors.ts +17 -0
- package/src/util/platform.ts +10 -0
- package/src/watcher/__tests__/engine.test.ts +22 -0
- package/src/watcher/engine.ts +6 -2
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
- package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
- package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
- package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/migrations/runner.ts +39 -9
- package/src/workspace/migrations/types.ts +4 -0
- package/examples/plugins/echo/bun.lock +0 -25
- package/src/__tests__/context-window-manager.test.ts +0 -2481
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
|
@@ -31,8 +31,12 @@ import { extractPreferences } from "../notifications/preference-extractor.js";
|
|
|
31
31
|
import { createPreference } from "../notifications/preferences-store.js";
|
|
32
32
|
import type { Message } from "../providers/types.js";
|
|
33
33
|
import { routeGuardianReply } from "../runtime/guardian-reply-router.js";
|
|
34
|
+
import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
|
|
34
35
|
import { getLogger } from "../util/logger.js";
|
|
35
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
persistQueuedMessageBody,
|
|
38
|
+
serializePersistedUserMessageContent,
|
|
39
|
+
} from "./conversation-messaging.js";
|
|
36
40
|
import type {
|
|
37
41
|
MessageQueue,
|
|
38
42
|
QueuedMessage,
|
|
@@ -513,11 +517,11 @@ async function drainSingleMessage(
|
|
|
513
517
|
// When displayContent is provided (e.g. original text before recording
|
|
514
518
|
// intent stripping), persist that to DB so users see the full message.
|
|
515
519
|
// The in-memory userMessage (sent to the LLM) still uses the stripped content.
|
|
516
|
-
const contentToPersist =
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
520
|
+
const contentToPersist = serializePersistedUserMessageContent(
|
|
521
|
+
next.content,
|
|
522
|
+
next.attachments,
|
|
523
|
+
next.displayContent,
|
|
524
|
+
);
|
|
521
525
|
await addMessage(
|
|
522
526
|
conversation.conversationId,
|
|
523
527
|
"user",
|
|
@@ -570,6 +574,7 @@ async function drainSingleMessage(
|
|
|
570
574
|
type: "message_complete",
|
|
571
575
|
conversationId: conversation.conversationId,
|
|
572
576
|
});
|
|
577
|
+
publishConversationMessagesChanged(conversation.conversationId);
|
|
573
578
|
} catch (err) {
|
|
574
579
|
const message = err instanceof Error ? err.message : String(err);
|
|
575
580
|
log.error(
|
|
@@ -602,6 +607,7 @@ async function drainSingleMessage(
|
|
|
602
607
|
|
|
603
608
|
// /compact — force context compaction, persist exchange, continue draining.
|
|
604
609
|
if (slashResult.kind === "compact") {
|
|
610
|
+
let persistedCompactMessage = false;
|
|
605
611
|
try {
|
|
606
612
|
const drainProvenance = provenanceFromTrustContext(
|
|
607
613
|
conversation.trustContext,
|
|
@@ -627,9 +633,14 @@ async function drainSingleMessage(
|
|
|
627
633
|
await addMessage(
|
|
628
634
|
conversation.conversationId,
|
|
629
635
|
"user",
|
|
630
|
-
|
|
636
|
+
serializePersistedUserMessageContent(
|
|
637
|
+
next.content,
|
|
638
|
+
next.attachments,
|
|
639
|
+
next.displayContent,
|
|
640
|
+
),
|
|
631
641
|
drainChannelMeta,
|
|
632
642
|
);
|
|
643
|
+
persistedCompactMessage = true;
|
|
633
644
|
conversation.messages.push(cleanUserMsg);
|
|
634
645
|
|
|
635
646
|
conversation.emitActivityState(
|
|
@@ -666,7 +677,11 @@ async function drainSingleMessage(
|
|
|
666
677
|
type: "message_complete",
|
|
667
678
|
conversationId: conversation.conversationId,
|
|
668
679
|
});
|
|
680
|
+
publishConversationMessagesChanged(conversation.conversationId);
|
|
669
681
|
} catch (err) {
|
|
682
|
+
if (persistedCompactMessage) {
|
|
683
|
+
publishConversationMessagesChanged(conversation.conversationId);
|
|
684
|
+
}
|
|
670
685
|
const message = err instanceof Error ? err.message : String(err);
|
|
671
686
|
log.error(
|
|
672
687
|
{
|
|
@@ -762,6 +777,7 @@ async function drainSingleMessage(
|
|
|
762
777
|
requestId: next.requestId,
|
|
763
778
|
clientMessageId: next.clientMessageId,
|
|
764
779
|
});
|
|
780
|
+
publishConversationMessagesChanged(conversation.conversationId);
|
|
765
781
|
|
|
766
782
|
// Set the active surface for the dequeued message so runAgentLoop can inject context
|
|
767
783
|
conversation.currentActiveSurfaceId = next.activeSurfaceId;
|
|
@@ -1087,6 +1103,7 @@ async function drainBatch(
|
|
|
1087
1103
|
requestId: qm.requestId,
|
|
1088
1104
|
clientMessageId: qm.clientMessageId,
|
|
1089
1105
|
});
|
|
1106
|
+
publishConversationMessagesChanged(conversation.conversationId);
|
|
1090
1107
|
|
|
1091
1108
|
// Persist succeeded. Update last-successful markers so a later tail
|
|
1092
1109
|
// failure won't overwrite them.
|
|
@@ -1306,7 +1323,11 @@ export async function processMessage(
|
|
|
1306
1323
|
const persisted = await addMessage(
|
|
1307
1324
|
conversation.conversationId,
|
|
1308
1325
|
"user",
|
|
1309
|
-
|
|
1326
|
+
serializePersistedUserMessageContent(
|
|
1327
|
+
content,
|
|
1328
|
+
attachments,
|
|
1329
|
+
displayContent,
|
|
1330
|
+
),
|
|
1310
1331
|
routerChannelMeta,
|
|
1311
1332
|
);
|
|
1312
1333
|
conversation.messages.push(llmUserMsg);
|
|
@@ -1391,9 +1412,11 @@ export async function processMessage(
|
|
|
1391
1412
|
// When displayContent is provided (e.g. original text before recording
|
|
1392
1413
|
// intent stripping), persist that to DB so users see the full message.
|
|
1393
1414
|
// The in-memory userMessage (sent to the LLM) still uses the stripped content.
|
|
1394
|
-
const contentToPersist =
|
|
1395
|
-
|
|
1396
|
-
|
|
1415
|
+
const contentToPersist = serializePersistedUserMessageContent(
|
|
1416
|
+
content,
|
|
1417
|
+
attachments,
|
|
1418
|
+
displayContent,
|
|
1419
|
+
);
|
|
1397
1420
|
const persisted = await addMessage(
|
|
1398
1421
|
conversation.conversationId,
|
|
1399
1422
|
"user",
|
|
@@ -1446,12 +1469,14 @@ export async function processMessage(
|
|
|
1446
1469
|
type: "message_complete",
|
|
1447
1470
|
conversationId: conversation.conversationId,
|
|
1448
1471
|
});
|
|
1472
|
+
publishConversationMessagesChanged(conversation.conversationId);
|
|
1449
1473
|
return persisted.id;
|
|
1450
1474
|
}
|
|
1451
1475
|
|
|
1452
1476
|
// /compact — force context compaction, persist exchange, return message ID.
|
|
1453
1477
|
if (slashResult.kind === "compact") {
|
|
1454
1478
|
conversation.processing = true;
|
|
1479
|
+
let persistedCompactMessage = false;
|
|
1455
1480
|
try {
|
|
1456
1481
|
const pmTurnCtx = conversation.getTurnChannelContext();
|
|
1457
1482
|
const pmInterfaceCtx = conversation.getTurnInterfaceContext();
|
|
@@ -1478,9 +1503,14 @@ export async function processMessage(
|
|
|
1478
1503
|
const persisted = await addMessage(
|
|
1479
1504
|
conversation.conversationId,
|
|
1480
1505
|
"user",
|
|
1481
|
-
|
|
1506
|
+
serializePersistedUserMessageContent(
|
|
1507
|
+
content,
|
|
1508
|
+
attachments,
|
|
1509
|
+
displayContent,
|
|
1510
|
+
),
|
|
1482
1511
|
pmChannelMeta,
|
|
1483
1512
|
);
|
|
1513
|
+
persistedCompactMessage = true;
|
|
1484
1514
|
conversation.messages.push(cleanUserMsg);
|
|
1485
1515
|
|
|
1486
1516
|
conversation.emitActivityState(
|
|
@@ -1517,7 +1547,13 @@ export async function processMessage(
|
|
|
1517
1547
|
type: "message_complete",
|
|
1518
1548
|
conversationId: conversation.conversationId,
|
|
1519
1549
|
});
|
|
1550
|
+
publishConversationMessagesChanged(conversation.conversationId);
|
|
1520
1551
|
return persisted.id;
|
|
1552
|
+
} catch (err) {
|
|
1553
|
+
if (persistedCompactMessage) {
|
|
1554
|
+
publishConversationMessagesChanged(conversation.conversationId);
|
|
1555
|
+
}
|
|
1556
|
+
throw err;
|
|
1521
1557
|
} finally {
|
|
1522
1558
|
conversation.processing = false;
|
|
1523
1559
|
await drainQueue(conversation);
|
|
@@ -1557,6 +1593,7 @@ export async function processMessage(
|
|
|
1557
1593
|
undefined,
|
|
1558
1594
|
displayContent,
|
|
1559
1595
|
);
|
|
1596
|
+
publishConversationMessagesChanged(conversation.conversationId);
|
|
1560
1597
|
} catch (err) {
|
|
1561
1598
|
const message = err instanceof Error ? err.message : String(err);
|
|
1562
1599
|
onEvent({
|
|
@@ -1182,12 +1182,23 @@ function placeholderForBlockType(type: ContentBlock["type"]): string | null {
|
|
|
1182
1182
|
*/
|
|
1183
1183
|
function rowToRenderable(row: SlackTranscriptInputRow): RenderableSlackMessage {
|
|
1184
1184
|
let slackMeta: ReturnType<typeof readSlackMetadata> = null;
|
|
1185
|
+
let provenanceTrustClass: TrustClass | undefined;
|
|
1185
1186
|
if (row.metadata) {
|
|
1186
1187
|
try {
|
|
1187
|
-
const outer = JSON.parse(row.metadata) as {
|
|
1188
|
+
const outer = JSON.parse(row.metadata) as {
|
|
1189
|
+
slackMeta?: unknown;
|
|
1190
|
+
provenanceTrustClass?: unknown;
|
|
1191
|
+
};
|
|
1188
1192
|
if (typeof outer.slackMeta === "string") {
|
|
1189
1193
|
slackMeta = readSlackMetadata(outer.slackMeta);
|
|
1190
1194
|
}
|
|
1195
|
+
if (
|
|
1196
|
+
outer.provenanceTrustClass === "guardian" ||
|
|
1197
|
+
outer.provenanceTrustClass === "trusted_contact" ||
|
|
1198
|
+
outer.provenanceTrustClass === "unknown"
|
|
1199
|
+
) {
|
|
1200
|
+
provenanceTrustClass = outer.provenanceTrustClass;
|
|
1201
|
+
}
|
|
1191
1202
|
} catch {
|
|
1192
1203
|
// Malformed metadata — fall through to legacy/null treatment.
|
|
1193
1204
|
}
|
|
@@ -1252,6 +1263,8 @@ function rowToRenderable(row: SlackTranscriptInputRow): RenderableSlackMessage {
|
|
|
1252
1263
|
senderLabel,
|
|
1253
1264
|
createdAt: row.createdAt,
|
|
1254
1265
|
contentBlocks,
|
|
1266
|
+
wrapContentForModel:
|
|
1267
|
+
row.role === "user" && !isReaction && provenanceTrustClass !== "guardian",
|
|
1255
1268
|
};
|
|
1256
1269
|
}
|
|
1257
1270
|
|
|
@@ -1639,6 +1652,7 @@ const RUNTIME_INJECTION_PREFIXES = [
|
|
|
1639
1652
|
"<interface_turn_context>", // backward-compat: strip legacy separate interface blocks
|
|
1640
1653
|
// NOTE: <turn_context> is intentionally NOT stripped — unified turn context
|
|
1641
1654
|
// blocks persist in history so the assistant retains temporal/actor grounding.
|
|
1655
|
+
"<background_turn>",
|
|
1642
1656
|
"<memory_context __injected>",
|
|
1643
1657
|
"<memory_context>", // backward-compat: strip legacy blocks from pre-__injected history
|
|
1644
1658
|
// The static `memory-v2-static` block (opens `<memory>\n…`) IS stripped
|
|
@@ -1953,6 +1967,13 @@ export interface RuntimeInjectionOptions {
|
|
|
1953
1967
|
nowScratchpad?: string | null;
|
|
1954
1968
|
subagentStatusBlock?: string | null;
|
|
1955
1969
|
isNonInteractive?: boolean;
|
|
1970
|
+
/**
|
|
1971
|
+
* True when the active conversation's type is "background" or "scheduled".
|
|
1972
|
+
* Forwarded to {@link TurnInjectionInputs.isBackgroundConversation} so the
|
|
1973
|
+
* `background-turn` injector can wrap the tail user message with the
|
|
1974
|
+
* configured reminder.
|
|
1975
|
+
*/
|
|
1976
|
+
isBackgroundConversation?: boolean;
|
|
1956
1977
|
transportHints?: string[] | null;
|
|
1957
1978
|
slackRuntimeContextNotice?: string | null;
|
|
1958
1979
|
/**
|
|
@@ -1989,6 +2010,7 @@ export interface RuntimeInjectionOptions {
|
|
|
1989
2010
|
* `undefined` when the inbound is a top-level (non-thread) post.
|
|
1990
2011
|
*/
|
|
1991
2012
|
slackActiveThreadFocusBlock?: string | null;
|
|
2013
|
+
activeDocuments?: TurnInjectionInputs["activeDocuments"];
|
|
1992
2014
|
mode?: InjectionMode;
|
|
1993
2015
|
/**
|
|
1994
2016
|
* Per-turn {@link TurnContext} forwarded to plugin-registered
|
|
@@ -2042,6 +2064,8 @@ function buildTurnInjectionInputs(
|
|
|
2042
2064
|
voiceCallControlPrompt: options.voiceCallControlPrompt,
|
|
2043
2065
|
transportHints: options.transportHints,
|
|
2044
2066
|
isNonInteractive: options.isNonInteractive,
|
|
2067
|
+
isBackgroundConversation: options.isBackgroundConversation,
|
|
2068
|
+
activeDocuments: options.activeDocuments,
|
|
2045
2069
|
};
|
|
2046
2070
|
}
|
|
2047
2071
|
|
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
saveRawConfig,
|
|
9
9
|
} from "../config/loader.js";
|
|
10
10
|
import { getConversationOverrideProfile } from "../memory/conversation-crud.js";
|
|
11
|
-
import { PROVIDER_CATALOG } from "../providers/model-catalog.js";
|
|
12
11
|
import { getConfiguredProviders } from "../providers/provider-availability.js";
|
|
12
|
+
import { getVisibleProviderCatalog } from "../providers/provider-catalog-visibility.js";
|
|
13
13
|
|
|
14
14
|
export type SlashResolution =
|
|
15
15
|
| { kind: "passthrough"; content: string }
|
|
@@ -132,7 +132,10 @@ function parseModelCommand(trimmed: string): ModelCommandParse | null {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
function orderedProfileNames(
|
|
135
|
-
profiles: Record<
|
|
135
|
+
profiles: Record<
|
|
136
|
+
string,
|
|
137
|
+
{ label?: string; description?: string; status?: "active" | "disabled" }
|
|
138
|
+
>,
|
|
136
139
|
profileOrder: readonly string[] | undefined,
|
|
137
140
|
): string[] {
|
|
138
141
|
const order = profileOrder ?? [];
|
|
@@ -177,8 +180,12 @@ async function resolveModelCommand(
|
|
|
177
180
|
const isDisabled = profile.status === "disabled";
|
|
178
181
|
const marker = isCurrent ? " **[current]**" : "";
|
|
179
182
|
const disabled = isDisabled ? " *(disabled)*" : "";
|
|
180
|
-
const description = profile.description
|
|
181
|
-
|
|
183
|
+
const description = profile.description
|
|
184
|
+
? ` — ${profile.description}`
|
|
185
|
+
: "";
|
|
186
|
+
lines.push(
|
|
187
|
+
` - \`${name}\` (${label})${marker}${disabled}${description}`,
|
|
188
|
+
);
|
|
182
189
|
}
|
|
183
190
|
lines.push("\nSwitch with `/model <name>`.");
|
|
184
191
|
return { kind: "unknown", message: lines.join("\n") };
|
|
@@ -240,7 +247,7 @@ async function resolveModelList(): Promise<SlashResolution> {
|
|
|
240
247
|
id: provider,
|
|
241
248
|
displayName: providerName,
|
|
242
249
|
models,
|
|
243
|
-
} of
|
|
250
|
+
} of getVisibleProviderCatalog(config)) {
|
|
244
251
|
const hasKey = configuredProviders.has(provider);
|
|
245
252
|
const status = hasKey ? "✓" : "✗";
|
|
246
253
|
lines.push(`**${providerName}** ${status}`);
|
|
@@ -21,7 +21,9 @@ import { buildSystemPrompt } from "../prompts/system-prompt.js";
|
|
|
21
21
|
import { wrapWithCallSiteRouting } from "../providers/call-site-routing.js";
|
|
22
22
|
import { resolveDefaultProvider } from "../providers/connection-resolution.js";
|
|
23
23
|
import { RateLimitProvider } from "../providers/ratelimit.js";
|
|
24
|
+
import { listProviders } from "../providers/registry.js";
|
|
24
25
|
import { getSubagentManager } from "../subagent/index.js";
|
|
26
|
+
import { ProviderNotConfiguredError } from "../util/errors.js";
|
|
25
27
|
import { getSandboxWorkingDir } from "../util/platform.js";
|
|
26
28
|
import { Conversation } from "./conversation.js";
|
|
27
29
|
import type { ConversationEvictor } from "./conversation-evictor.js";
|
|
@@ -226,12 +228,17 @@ export async function getOrCreateConversation(
|
|
|
226
228
|
// Connection-aware default-provider resolution. Throws
|
|
227
229
|
// `ConnectionResolutionError` when the default profile's
|
|
228
230
|
// `provider_connection` is unset / unknown / mismatched (config
|
|
229
|
-
// bugs). Returns null on soft credential failures (
|
|
230
|
-
//
|
|
231
|
+
// bugs). Returns null on soft credential failures (missing
|
|
232
|
+
// credential, platform auth unavailable).
|
|
231
233
|
const baseProvider = await resolveDefaultProvider(config);
|
|
232
234
|
if (!baseProvider) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
+
const resolved = resolveCallSiteConfig("mainAgent", config.llm);
|
|
236
|
+
throw new ProviderNotConfiguredError(
|
|
237
|
+
resolved.provider,
|
|
238
|
+
listProviders(),
|
|
239
|
+
{
|
|
240
|
+
connectionName: resolved.provider_connection,
|
|
241
|
+
},
|
|
235
242
|
);
|
|
236
243
|
}
|
|
237
244
|
// Per-call `callSite` routing layered on top, with connection-awareness
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "../channels/types.js";
|
|
14
14
|
import { isHttpAuthDisabled } from "../config/env.js";
|
|
15
15
|
import { getIsPlatform } from "../config/env-registry.js";
|
|
16
|
+
import { getConfig } from "../config/loader.js";
|
|
16
17
|
import { getBindingByConversation } from "../memory/external-conversation-store.js";
|
|
17
18
|
import type { PermissionPrompter } from "../permissions/prompter.js";
|
|
18
19
|
import type { SecretPrompter } from "../permissions/secret-prompter.js";
|
|
@@ -392,7 +393,8 @@ const CROSS_CLIENT_EXPOSED_CAPABILITIES = new Set<HostProxyCapability>([
|
|
|
392
393
|
"host_file",
|
|
393
394
|
"host_browser",
|
|
394
395
|
]);
|
|
395
|
-
|
|
396
|
+
// Tools that require a connected client but no specific host proxy capability.
|
|
397
|
+
const CLIENT_CAPABILITY_TOOL_NAMES = new Set(["app_open", "ask_question"]);
|
|
396
398
|
const PLATFORM_TOOL_NAMES = new Set(["request_system_permission"]);
|
|
397
399
|
|
|
398
400
|
/**
|
|
@@ -406,16 +408,34 @@ export const SUBAGENT_ONLY_TOOL_NAMES = new Set<string>([
|
|
|
406
408
|
]);
|
|
407
409
|
|
|
408
410
|
/**
|
|
409
|
-
* Determine whether a tool
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
* fail.
|
|
411
|
+
* Determine whether a tool is part of the final exposed tool set for the
|
|
412
|
+
* current turn. This helper mirrors the filtering applied by
|
|
413
|
+
* `createResolveToolsCallback` — including the subagent allowlist,
|
|
414
|
+
* `toolsDisabledDepth`, and disk-pressure cleanup restrictions.
|
|
414
415
|
*/
|
|
415
416
|
export function isToolActiveForContext(
|
|
416
417
|
name: string,
|
|
417
418
|
ctx: SkillProjectionContext,
|
|
418
419
|
): boolean {
|
|
420
|
+
// When the conversation is acting as a subagent, the parent orchestrator
|
|
421
|
+
// restricts the tool list. A tool that isn't on the allowlist is not
|
|
422
|
+
// available for this turn, so short-circuit before any capability checks.
|
|
423
|
+
if (ctx.subagentAllowedTools && !ctx.subagentAllowedTools.has(name)) {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
// `createResolveToolsCallback` returns an empty tool list when tools are
|
|
427
|
+
// disabled (e.g. pointer-generation turns) and restricts to cleanup-safe
|
|
428
|
+
// tools under disk pressure. Mirror both here so this helper reports the
|
|
429
|
+
// same final tool set the LLM receives.
|
|
430
|
+
if (ctx.toolsDisabledDepth > 0) {
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
if (
|
|
434
|
+
ctx.diskPressureCleanupModeActive === true &&
|
|
435
|
+
!isDiskPressureCleanupToolName(name)
|
|
436
|
+
) {
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
419
439
|
if (UI_SURFACE_TOOL_NAMES.has(name)) {
|
|
420
440
|
return ctx.channelCapabilities?.supportsDynamicUi ?? !ctx.hasNoClient;
|
|
421
441
|
}
|
|
@@ -463,6 +483,14 @@ export function isToolActiveForContext(
|
|
|
463
483
|
return !ctx.hasNoClient;
|
|
464
484
|
}
|
|
465
485
|
if (CLIENT_CAPABILITY_TOOL_NAMES.has(name)) {
|
|
486
|
+
if (
|
|
487
|
+
name === "ask_question" &&
|
|
488
|
+
ctx.channelCapabilities?.clientOS === "macos"
|
|
489
|
+
) {
|
|
490
|
+
// macOS has no UI handler for question_request yet; hiding the tool
|
|
491
|
+
// avoids a 5-minute prompter timeout when the LLM would otherwise call it.
|
|
492
|
+
return false;
|
|
493
|
+
}
|
|
466
494
|
return !ctx.hasNoClient;
|
|
467
495
|
}
|
|
468
496
|
if (PLATFORM_TOOL_NAMES.has(name)) {
|
|
@@ -544,7 +572,10 @@ export function createResolveToolsCallback(
|
|
|
544
572
|
const scopedMcpDefs = ctx.subagentAllowedTools
|
|
545
573
|
? currentMcpDefs.filter((d) => ctx.subagentAllowedTools!.has(d.name))
|
|
546
574
|
: currentMcpDefs;
|
|
547
|
-
const
|
|
575
|
+
const excluded = new Set(getConfig().tools.exclude);
|
|
576
|
+
const allBaseDefs = [...scopedCoreDefs, ...scopedMcpDefs].filter(
|
|
577
|
+
(d) => !excluded.has(d.name),
|
|
578
|
+
);
|
|
548
579
|
|
|
549
580
|
const effectivePreactivated = [
|
|
550
581
|
...DEFAULT_PREACTIVATED_SKILL_IDS,
|
|
@@ -561,6 +592,7 @@ export function createResolveToolsCallback(
|
|
|
561
592
|
if (ctx.subagentAllowedTools && !ctx.subagentAllowedTools.has(name)) {
|
|
562
593
|
continue;
|
|
563
594
|
}
|
|
595
|
+
if (excluded.has(name)) continue;
|
|
564
596
|
turnAllowed.add(name);
|
|
565
597
|
}
|
|
566
598
|
if (ctx.diskPressureCleanupModeActive === true) {
|
|
@@ -54,8 +54,8 @@ import {
|
|
|
54
54
|
getConversationOriginChannel,
|
|
55
55
|
getConversationOverrideProfileFromRow,
|
|
56
56
|
} from "../memory/conversation-crud.js";
|
|
57
|
-
import { isBackgroundConversationType } from "../memory/conversation-types.js";
|
|
58
57
|
import { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
|
|
58
|
+
import { shouldExposePersonalMemory } from "../memory/v2/static-context.js";
|
|
59
59
|
import { PermissionPrompter } from "../permissions/prompter.js";
|
|
60
60
|
import { SecretPrompter } from "../permissions/secret-prompter.js";
|
|
61
61
|
import type { UserDecision } from "../permissions/types.js";
|
|
@@ -118,6 +118,7 @@ import {
|
|
|
118
118
|
buildToolDefinitions,
|
|
119
119
|
createResolveToolsCallback,
|
|
120
120
|
createToolExecutor,
|
|
121
|
+
resolveTrustClass,
|
|
121
122
|
} from "./conversation-tool-setup.js";
|
|
122
123
|
import { refreshWorkspaceTopLevelContextIfNeeded as refreshWorkspaceImpl } from "./conversation-workspace.js";
|
|
123
124
|
import { canonicalizeTimeZone } from "./date-context.js";
|
|
@@ -180,6 +181,17 @@ export class Conversation {
|
|
|
180
181
|
/** @internal */ contextWindowManager: ContextWindowManager;
|
|
181
182
|
/** @internal */ contextCompactedMessageCount = 0;
|
|
182
183
|
/** @internal */ contextCompactedAt: number | null = null;
|
|
184
|
+
/**
|
|
185
|
+
* Set true by `applyCompactionResult` when compaction strips runtime
|
|
186
|
+
* injections from the tail. The next agent loop turn reads this flag at
|
|
187
|
+
* entry, treats it as a `compactedThisTurn` trigger (re-injecting NOW.md,
|
|
188
|
+
* PKB, and the v2 essentials/threads/recent/buffer block), and clears it.
|
|
189
|
+
*
|
|
190
|
+
* Required because `/compact` runs outside the agent loop — without this
|
|
191
|
+
* signal, the next turn cannot tell that the static blocks were just
|
|
192
|
+
* stripped and never re-emits them.
|
|
193
|
+
*/
|
|
194
|
+
/** @internal */ pendingPostCompactReinject = false;
|
|
183
195
|
/**
|
|
184
196
|
* Tracks consecutive compaction failures (summary LLM call threw). In-memory
|
|
185
197
|
* only — resets to 0 on process restart, which is the intended "one free
|
|
@@ -225,6 +237,7 @@ export class Conversation {
|
|
|
225
237
|
/** @internal */ currentTurnOverrideProfile?: string;
|
|
226
238
|
/** @internal */ authContext?: AuthContext;
|
|
227
239
|
/** @internal */ loadedHistoryTrustClass?: TrustClass;
|
|
240
|
+
/** @internal */ loadedHistoryPersonalMemoryAllowed?: boolean;
|
|
228
241
|
/** @internal */ voiceCallControlPrompt?: string;
|
|
229
242
|
/** @internal */ transportHints?: string[];
|
|
230
243
|
/** @internal */ slackRuntimeContextNotice?: string;
|
|
@@ -475,9 +488,6 @@ export class Conversation {
|
|
|
475
488
|
channelPersona: persona.channelPersona,
|
|
476
489
|
userSlug: persona.userSlug,
|
|
477
490
|
onboardingContext: this.getOnboardingContext(),
|
|
478
|
-
isBackgroundConversation: isBackgroundConversationType(
|
|
479
|
-
getConversation(this.conversationId)?.conversationType,
|
|
480
|
-
),
|
|
481
491
|
});
|
|
482
492
|
})(),
|
|
483
493
|
};
|
|
@@ -527,6 +537,10 @@ export class Conversation {
|
|
|
527
537
|
systemPrompt: () => resolveSystemPromptCallback([]).systemPrompt,
|
|
528
538
|
config: initialContextWindowConfig,
|
|
529
539
|
toolTokenBudget: this.agentLoop.getToolTokenBudget(),
|
|
540
|
+
conversationId: this.conversationId,
|
|
541
|
+
resolveTools: resolveTools
|
|
542
|
+
? () => resolveTools(this.messages)
|
|
543
|
+
: undefined,
|
|
530
544
|
});
|
|
531
545
|
}
|
|
532
546
|
|
|
@@ -565,9 +579,6 @@ export class Conversation {
|
|
|
565
579
|
channelPersona: persona.channelPersona,
|
|
566
580
|
userSlug: persona.userSlug,
|
|
567
581
|
onboardingContext: this.getOnboardingContext(),
|
|
568
|
-
isBackgroundConversation: isBackgroundConversationType(
|
|
569
|
-
getConversation(this.conversationId)?.conversationType,
|
|
570
|
-
),
|
|
571
582
|
});
|
|
572
583
|
})();
|
|
573
584
|
const tools = buildToolDefinitions();
|
|
@@ -647,7 +658,21 @@ export class Conversation {
|
|
|
647
658
|
|
|
648
659
|
async ensureActorScopedHistory(): Promise<void> {
|
|
649
660
|
const currentTrustClass = this.trustContext?.trustClass;
|
|
650
|
-
|
|
661
|
+
// `loadFromDb` gates personal-memory rehydration on `sourceChannel` too
|
|
662
|
+
// (via `shouldExposePersonalMemory`), so a same-trust-class reuse from a
|
|
663
|
+
// different channel (e.g. internal `vellum` → remote channel) must also
|
|
664
|
+
// trigger a reload. Otherwise stale personal-memory blocks can leak to
|
|
665
|
+
// an untrusted remote turn, or be hidden when they should be present.
|
|
666
|
+
const currentPersonalMemoryAllowed = shouldExposePersonalMemory({
|
|
667
|
+
sourceChannel: this.trustContext?.sourceChannel,
|
|
668
|
+
isTrustedActor: resolveTrustClass(this.trustContext) === "guardian",
|
|
669
|
+
});
|
|
670
|
+
if (
|
|
671
|
+
this.loadedHistoryTrustClass === currentTrustClass &&
|
|
672
|
+
this.loadedHistoryPersonalMemoryAllowed === currentPersonalMemoryAllowed
|
|
673
|
+
) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
651
676
|
await this.loadFromDb();
|
|
652
677
|
}
|
|
653
678
|
|
|
@@ -326,6 +326,46 @@ function formatLocalDate(date: Date, timeZone: string): string {
|
|
|
326
326
|
).padStart(2, "0")}`;
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
+
const localTimestampFormatterCache = new Map<string, Intl.DateTimeFormat>();
|
|
330
|
+
|
|
331
|
+
function getLocalTimestampFormatter(timeZone: string): Intl.DateTimeFormat {
|
|
332
|
+
let fmt = localTimestampFormatterCache.get(timeZone);
|
|
333
|
+
if (!fmt) {
|
|
334
|
+
fmt = new Intl.DateTimeFormat("en-CA", {
|
|
335
|
+
timeZone,
|
|
336
|
+
year: "numeric",
|
|
337
|
+
month: "2-digit",
|
|
338
|
+
day: "2-digit",
|
|
339
|
+
hour: "2-digit",
|
|
340
|
+
minute: "2-digit",
|
|
341
|
+
second: "2-digit",
|
|
342
|
+
hourCycle: "h23",
|
|
343
|
+
});
|
|
344
|
+
localTimestampFormatterCache.set(timeZone, fmt);
|
|
345
|
+
}
|
|
346
|
+
return fmt;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Format an epoch-millis instant as `YYYY-MM-DD HH:MM:SS` in the given
|
|
351
|
+
* IANA timezone. When `timeZone` is omitted or `"UTC"`, falls back to a
|
|
352
|
+
* pure-UTC `toISOString` slice so callers can opt in incrementally.
|
|
353
|
+
*
|
|
354
|
+
* The internal `Intl.DateTimeFormat` is memoized by `timeZone` because
|
|
355
|
+
* constructing it is ~1ms in V8 — material when rendering long transcripts.
|
|
356
|
+
*/
|
|
357
|
+
export function formatLocalTimestamp(ms: number, timeZone?: string): string {
|
|
358
|
+
if (!timeZone || timeZone === "UTC") {
|
|
359
|
+
return new Date(ms).toISOString().replace("T", " ").slice(0, 19);
|
|
360
|
+
}
|
|
361
|
+
const parts = getLocalTimestampFormatter(timeZone).formatToParts(
|
|
362
|
+
new Date(ms),
|
|
363
|
+
);
|
|
364
|
+
const v: Record<string, string> = {};
|
|
365
|
+
for (const p of parts) v[p.type] = p.value;
|
|
366
|
+
return `${v.year}-${v.month}-${v.day} ${v.hour}:${v.minute}:${v.second}`;
|
|
367
|
+
}
|
|
368
|
+
|
|
329
369
|
export function resolveTurnTimezoneContext(
|
|
330
370
|
options: TemporalContextOptions = {},
|
|
331
371
|
): TurnTimezoneContext {
|