@vellumai/assistant 0.6.0 → 0.6.2
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 +68 -15
- package/Dockerfile +2 -2
- package/bun.lock +6 -2
- package/docker-entrypoint.sh +42 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
- package/openapi.yaml +539 -4
- package/package.json +5 -1
- package/src/__tests__/anthropic-provider.test.ts +160 -95
- 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-event-hub.test.ts +30 -0
- package/src/__tests__/checker.test.ts +138 -172
- package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
- package/src/__tests__/config-schema.test.ts +5 -0
- package/src/__tests__/context-overflow-approval.test.ts +5 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -6
- package/src/__tests__/conversation-agent-loop.test.ts +4 -51
- package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
- package/src/__tests__/conversation-directories-parse.test.ts +105 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
- package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
- package/src/__tests__/conversation-wipe.test.ts +2 -6
- 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-approval-bridge.test.ts +0 -2
- 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__/heartbeat-service.test.ts +180 -3
- package/src/__tests__/identity-routes.test.ts +328 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
- package/src/__tests__/injection-block.test.ts +24 -0
- package/src/__tests__/inline-command-runner.test.ts +7 -5
- package/src/__tests__/install-skill-routing.test.ts +7 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +15 -14
- 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 +101 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +162 -0
- package/src/__tests__/log-export-workspace.test.ts +257 -100
- package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
- package/src/__tests__/mcp-abort-signal.test.ts +5 -0
- package/src/__tests__/mcp-client-auth.test.ts +5 -0
- package/src/__tests__/memory-recall-log-store.test.ts +132 -0
- 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__/navigate-settings-tab.test.ts +14 -1
- package/src/__tests__/notification-broadcaster.test.ts +65 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/onboarding-template-contract.test.ts +63 -14
- 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__/pkb-autoinject.test.ts +96 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
- package/src/__tests__/profiler-routes.test.ts +502 -0
- package/src/__tests__/profiler-run-store.test.ts +441 -0
- package/src/__tests__/proxy-approval-callback.test.ts +4 -75
- package/src/__tests__/registry.test.ts +1 -1
- package/src/__tests__/require-fresh-approval.test.ts +0 -2
- package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
- package/src/__tests__/sandbox-host-parity.test.ts +5 -4
- package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
- package/src/__tests__/search-skills-unified.test.ts +4 -3
- package/src/__tests__/send-endpoint-busy.test.ts +42 -3
- package/src/__tests__/set-permission-mode.test.ts +274 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +12 -0
- package/src/__tests__/skill-memory.test.ts +2 -783
- 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-memory-cleanup.test.ts +12 -12
- package/src/__tests__/terminal-sandbox.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +16 -29
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
- package/src/__tests__/tool-executor.test.ts +4 -27
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
- package/src/__tests__/top-level-renderer.test.ts +10 -13
- package/src/__tests__/transport-hints-queue.test.ts +77 -0
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +116 -2
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
- package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
- package/src/__tests__/workspace-policy.test.ts +2 -7
- package/src/agent/loop.ts +6 -29
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/channels/types.ts +5 -0
- package/src/cli/__tests__/run-assistant-command.ts +56 -0
- package/src/cli/__tests__/unknown-command.test.ts +33 -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/conversations.ts +1 -8
- package/src/cli/commands/default-action.ts +68 -1
- package/src/cli/commands/email.ts +584 -835
- package/src/cli/commands/memory.ts +1 -34
- package/src/cli/commands/notifications.ts +7 -2
- package/src/cli/commands/oauth/__tests__/connect.test.ts +27 -0
- package/src/cli/commands/oauth/connect.ts +25 -5
- 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 +1 -1
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +130 -20
- package/src/cli/program.ts +11 -2
- package/src/cli.ts +1 -120
- package/src/config/assistant-feature-flags.ts +59 -55
- package/src/config/bundled-skills/app-builder/SKILL.md +91 -5
- package/src/config/bundled-skills/gmail/SKILL.md +13 -8
- package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +7 -0
- 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.json +1 -1
- 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/settings/tools/navigate-settings-tab.ts +8 -3
- package/src/config/bundled-skills/slack/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/SKILL.md +43 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
- package/src/config/env-registry.ts +63 -0
- package/src/config/feature-flag-registry.json +17 -1
- 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/security.ts +14 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/credential-execution/managed-catalog.ts +3 -7
- package/src/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +85 -3
- package/src/daemon/context-overflow-approval.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +20 -0
- package/src/daemon/conversation-agent-loop.ts +179 -65
- package/src/daemon/conversation-attachments.ts +0 -1
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +8 -14
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +30 -8
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +359 -308
- package/src/daemon/conversation-surfaces.ts +65 -0
- package/src/daemon/conversation-tool-setup.ts +44 -17
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +19 -3
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +5 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +70 -5
- package/src/daemon/handlers/skills.ts +11 -18
- package/src/daemon/lifecycle.ts +220 -158
- package/src/daemon/message-types/conversations.ts +29 -6
- package/src/daemon/message-types/messages.ts +9 -2
- package/src/daemon/message-types/notifications.ts +12 -0
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +18 -0
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/server.ts +87 -10
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +23 -3
- package/src/daemon/transport-hints.ts +33 -0
- package/src/export/transcript-formatter.ts +148 -0
- package/src/filing/filing-service.ts +228 -0
- package/src/heartbeat/heartbeat-service.ts +96 -7
- package/src/index.ts +1 -1
- package/src/mcp/client.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +149 -27
- package/src/memory/admin.ts +33 -32
- package/src/memory/app-store.ts +69 -0
- package/src/memory/conversation-bootstrap.ts +1 -1
- package/src/memory/conversation-crud.ts +151 -117
- package/src/memory/conversation-directories.ts +39 -0
- package/src/memory/conversation-group-migration.ts +66 -6
- package/src/memory/conversation-queries.ts +58 -12
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/db-init.ts +182 -376
- package/src/memory/embedding-local.ts +1 -1
- package/src/memory/graph/bootstrap.ts +75 -66
- package/src/memory/graph/capability-seed.ts +167 -17
- package/src/memory/graph/consolidation.ts +38 -4
- package/src/memory/graph/conversation-graph-memory.ts +133 -104
- package/src/memory/graph/extraction-job.ts +9 -4
- package/src/memory/graph/extraction.ts +66 -23
- package/src/memory/graph/graph-memory-state-store.ts +37 -0
- package/src/memory/graph/graph-search.ts +29 -15
- package/src/memory/graph/injection.ts +38 -8
- package/src/memory/graph/inspect.ts +12 -3
- package/src/memory/graph/retriever.ts +365 -262
- package/src/memory/graph/store.test.ts +48 -0
- package/src/memory/graph/store.ts +150 -11
- package/src/memory/graph/tool-handlers.ts +84 -209
- package/src/memory/graph/tools.ts +8 -52
- package/src/memory/graph/types.ts +24 -0
- package/src/memory/group-crud.ts +25 -9
- package/src/memory/job-handlers/cleanup.ts +44 -1
- package/src/memory/jobs-store.ts +70 -60
- package/src/memory/jobs-worker.ts +44 -28
- package/src/memory/llm-request-log-store.ts +96 -12
- package/src/memory/memory-recall-log-store.ts +49 -5
- package/src/memory/migrations/203-drop-memory-items-tables.ts +33 -1
- 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 +8 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +14 -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 +15 -0
- package/src/memory/task-memory-cleanup.ts +30 -11
- package/src/messaging/provider.ts +1 -1
- package/src/notifications/broadcaster.ts +6 -0
- package/src/notifications/conversation-pairing.ts +12 -4
- package/src/notifications/copy-composer.ts +86 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/notifications/emit-signal.ts +14 -0
- package/src/notifications/signal.ts +11 -0
- package/src/oauth/platform-connection.test.ts +2 -2
- package/src/oauth/seed-providers.ts +1 -0
- package/src/permissions/checker.ts +15 -4
- package/src/permissions/defaults.ts +7 -8
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/prompter.ts +0 -2
- package/src/permissions/workspace-policy.ts +9 -0
- package/src/platform/client.ts +1 -1
- package/src/prompts/system-prompt.ts +59 -7
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +76 -162
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +30 -9
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +107 -219
- package/src/runtime/assistant-event-hub.ts +22 -0
- package/src/runtime/auth/route-policy.ts +23 -0
- package/src/runtime/auth/token-service.ts +8 -0
- package/src/runtime/http-server.ts +32 -2
- package/src/runtime/http-types.ts +12 -1
- 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/avatar-routes.ts +2 -9
- package/src/runtime/routes/btw-routes.ts +14 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +185 -0
- package/src/runtime/routes/conversation-management-routes.ts +1 -14
- package/src/runtime/routes/conversation-query-routes.ts +49 -3
- package/src/runtime/routes/conversation-routes.ts +270 -44
- package/src/runtime/routes/group-routes.ts +22 -8
- package/src/runtime/routes/heartbeat-routes.ts +4 -10
- package/src/runtime/routes/identity-routes.ts +53 -18
- package/src/runtime/routes/llm-context-normalization.ts +14 -10
- package/src/runtime/routes/log-export/AGENTS.md +104 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
- package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
- package/src/runtime/routes/log-export-routes.ts +41 -278
- package/src/runtime/routes/memory-item-routes.test.ts +168 -233
- 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 +27 -12
- package/src/runtime/routes/settings-routes.ts +95 -8
- 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/workspace-routes.ts +0 -1
- package/src/schedule/schedule-store.ts +30 -0
- package/src/schedule/scheduler.ts +45 -18
- package/src/skills/catalog-install.ts +10 -2
- package/src/skills/inline-command-runner.ts +12 -14
- package/src/skills/managed-store.ts +2 -2
- package/src/skills/skill-memory.ts +1 -293
- 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 +4 -4
- package/src/tools/apps/executors.ts +29 -4
- package/src/tools/filesystem/list.ts +93 -0
- package/src/tools/permission-checker.ts +78 -18
- 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/secret-detection-handler.ts +0 -1
- 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/types.ts +17 -0
- package/src/tools/shared/shell-output.ts +31 -2
- package/src/tools/skills/sandbox-runner.ts +3 -6
- 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 +16 -1
- package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
- package/src/tools/terminal/sandbox.ts +4 -1
- package/src/tools/terminal/shell.ts +3 -5
- package/src/tools/tool-manifest.ts +6 -0
- package/src/tools/types.ts +2 -3
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- package/src/watcher/provider-types.ts +1 -1
- 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/028-recover-conversations-from-disk-view.ts +270 -0
- package/src/workspace/migrations/029-seed-pkb.ts +85 -0
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/top-level-renderer.ts +5 -9
- package/src/__tests__/cli-memory.test.ts +0 -377
- package/src/__tests__/clipboard.test.ts +0 -88
- package/src/cli/cli-memory.ts +0 -179
- package/src/util/clipboard.ts +0 -34
|
@@ -206,143 +206,6 @@ function hasOrderedToolResultPrefix(
|
|
|
206
206
|
* regular content — they are self-paired within the assistant message and must
|
|
207
207
|
* not be separated by the cross-message pairing logic.
|
|
208
208
|
*/
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Expand collapsed multi-turn assistant messages. During agentic tool use, the
|
|
212
|
-
* daemon stores multiple thinking→tool_use→tool_result cycles in a single
|
|
213
|
-
* assistant message. The Anthropic API rejects thinking blocks between
|
|
214
|
-
* tool_use blocks ("tool_use without tool_result immediately after") and
|
|
215
|
-
* requires thinking blocks in the latest assistant message to remain exactly
|
|
216
|
-
* as generated.
|
|
217
|
-
*
|
|
218
|
-
* This function splits collapsed messages at each "thinking/redacted_thinking
|
|
219
|
-
* after tool_use" boundary, recreating the original multi-turn structure.
|
|
220
|
-
* It also distributes tool_result blocks from the following user message to
|
|
221
|
-
* match each segment's tool_use blocks, creating proper assistant→user pairs.
|
|
222
|
-
*/
|
|
223
|
-
function expandCollapsedAssistantTurns(
|
|
224
|
-
messages: Anthropic.MessageParam[],
|
|
225
|
-
): Anthropic.MessageParam[] {
|
|
226
|
-
const result: Anthropic.MessageParam[] = [];
|
|
227
|
-
|
|
228
|
-
for (let mi = 0; mi < messages.length; mi++) {
|
|
229
|
-
const msg = messages[mi];
|
|
230
|
-
if (msg.role !== "assistant") {
|
|
231
|
-
result.push(msg);
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const content = Array.isArray(msg.content) ? msg.content : [];
|
|
236
|
-
|
|
237
|
-
// Check if this message has thinking blocks between tool_use blocks
|
|
238
|
-
let hasThinkingAfterToolUse = false;
|
|
239
|
-
let seenToolUse = false;
|
|
240
|
-
for (const block of content) {
|
|
241
|
-
if (isToolUseBlock(block)) {
|
|
242
|
-
seenToolUse = true;
|
|
243
|
-
} else if (seenToolUse) {
|
|
244
|
-
const type = (block as { type: string }).type;
|
|
245
|
-
if (type === "thinking" || type === "redacted_thinking") {
|
|
246
|
-
hasThinkingAfterToolUse = true;
|
|
247
|
-
break;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (!hasThinkingAfterToolUse) {
|
|
253
|
-
result.push(msg);
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Split at each "thinking after tool_use" boundary into separate segments
|
|
258
|
-
const segments: Anthropic.ContentBlockParam[][] = [];
|
|
259
|
-
let current: Anthropic.ContentBlockParam[] = [];
|
|
260
|
-
let segmentHasToolUse = false;
|
|
261
|
-
|
|
262
|
-
for (const block of content) {
|
|
263
|
-
const type = (block as { type: string }).type;
|
|
264
|
-
const isThinking = type === "thinking" || type === "redacted_thinking";
|
|
265
|
-
|
|
266
|
-
if (isThinking && segmentHasToolUse) {
|
|
267
|
-
segments.push(current);
|
|
268
|
-
current = [block];
|
|
269
|
-
segmentHasToolUse = false;
|
|
270
|
-
} else {
|
|
271
|
-
current.push(block);
|
|
272
|
-
if (isToolUseBlock(block)) {
|
|
273
|
-
segmentHasToolUse = true;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
if (current.length > 0) {
|
|
278
|
-
segments.push(current);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Build a map of tool_results from the following user message (if any)
|
|
282
|
-
const nextMsg = messages[mi + 1];
|
|
283
|
-
const nextIsUser = nextMsg && nextMsg.role === "user";
|
|
284
|
-
const nextContent =
|
|
285
|
-
nextIsUser && Array.isArray(nextMsg.content) ? nextMsg.content : [];
|
|
286
|
-
const toolResultMap = new Map<string, Anthropic.ContentBlockParam>();
|
|
287
|
-
const nonToolResultContent: Anthropic.ContentBlockParam[] = [];
|
|
288
|
-
for (const block of nextContent) {
|
|
289
|
-
if (isToolResultBlock(block)) {
|
|
290
|
-
toolResultMap.set(block.tool_use_id, block);
|
|
291
|
-
} else {
|
|
292
|
-
nonToolResultContent.push(block);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Emit each segment as assistant→user pairs, distributing tool_results
|
|
297
|
-
for (let si = 0; si < segments.length; si++) {
|
|
298
|
-
const segment = segments[si];
|
|
299
|
-
const segToolUseIds = getOrderedToolUseIds(segment);
|
|
300
|
-
const isLastSegment = si === segments.length - 1;
|
|
301
|
-
|
|
302
|
-
result.push({ role: "assistant" as const, content: segment });
|
|
303
|
-
|
|
304
|
-
if (segToolUseIds.length > 0 && !isLastSegment) {
|
|
305
|
-
// Intermediate segment: pair with matching tool_results
|
|
306
|
-
const segResults = segToolUseIds.map(
|
|
307
|
-
(id) => toolResultMap.get(id) ?? buildSyntheticToolResult(id),
|
|
308
|
-
);
|
|
309
|
-
// Remove matched results from the map
|
|
310
|
-
for (const id of segToolUseIds) toolResultMap.delete(id);
|
|
311
|
-
result.push({ role: "user" as const, content: segResults });
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// For the last segment, let ensureToolPairing handle pairing with the
|
|
316
|
-
// (now reduced) user message. Rebuild the user message without the
|
|
317
|
-
// tool_results that were already distributed to intermediate segments.
|
|
318
|
-
if (nextIsUser) {
|
|
319
|
-
const remainingResults = Array.from(toolResultMap.values());
|
|
320
|
-
const rebuiltUserContent = [...remainingResults, ...nonToolResultContent];
|
|
321
|
-
// Replace the original user message with the rebuilt one. When all
|
|
322
|
-
// tool_results were distributed to intermediate segments (empty rebuilt
|
|
323
|
-
// content), skip the synthetic placeholder if the next message is already
|
|
324
|
-
// a user turn — ensureToolPairing will pair the last assistant segment
|
|
325
|
-
// with that next user message naturally.
|
|
326
|
-
if (rebuiltUserContent.length > 0) {
|
|
327
|
-
result.push({ role: "user" as const, content: rebuiltUserContent });
|
|
328
|
-
} else {
|
|
329
|
-
const nextAfterUser = messages[mi + 2];
|
|
330
|
-
if (!nextAfterUser || nextAfterUser.role !== "user") {
|
|
331
|
-
result.push({
|
|
332
|
-
role: "user" as const,
|
|
333
|
-
content: [
|
|
334
|
-
{ type: "text" as const, text: SYNTHETIC_CONTINUATION_TEXT },
|
|
335
|
-
],
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
mi++; // skip the original user message
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
return result;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
209
|
function splitAssistantForToolPairing(content: Anthropic.ContentBlockParam[]): {
|
|
347
210
|
pairedContent: Anthropic.ContentBlockParam[];
|
|
348
211
|
carryoverContent: Anthropic.ContentBlockParam[];
|
|
@@ -716,6 +579,7 @@ export class AnthropicProvider implements Provider {
|
|
|
716
579
|
options?: SendMessageOptions,
|
|
717
580
|
): Promise<ProviderResponse> {
|
|
718
581
|
const { config, onEvent, signal } = options ?? {};
|
|
582
|
+
const cacheTtl: "5m" | "1h" = ((config as Record<string, unknown> | undefined)?.cacheTtl as "5m" | "1h") ?? "1h";
|
|
719
583
|
let sentMessages: Anthropic.MessageParam[] | undefined;
|
|
720
584
|
try {
|
|
721
585
|
const formatted = messages
|
|
@@ -819,58 +683,13 @@ export class AnthropicProvider implements Provider {
|
|
|
819
683
|
}
|
|
820
684
|
}
|
|
821
685
|
|
|
822
|
-
//
|
|
823
|
-
// messages
|
|
824
|
-
//
|
|
825
|
-
//
|
|
826
|
-
// validation fail with "thinking blocks cannot be modified". Stripping is
|
|
827
|
-
// safe: the API allows it for all historical messages, and new responses
|
|
828
|
-
// generate fresh thinking blocks.
|
|
829
|
-
//
|
|
830
|
-
// The latest assistant turn is preserved: the API requires the most recent
|
|
831
|
-
// assistant message's thinking blocks to be passed back unmodified when
|
|
832
|
-
// sending tool results during in-progress tool-use loops.
|
|
833
|
-
let lastAssistantIdx = -1;
|
|
834
|
-
for (let i = formatted.length - 1; i >= 0; i--) {
|
|
835
|
-
if (formatted[i].role === "assistant") {
|
|
836
|
-
lastAssistantIdx = i;
|
|
837
|
-
break;
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
for (let i = 0; i < formatted.length; i++) {
|
|
841
|
-
const msg = formatted[i];
|
|
842
|
-
if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
|
|
843
|
-
if (i === lastAssistantIdx) continue;
|
|
844
|
-
const stripped = msg.content.filter(
|
|
845
|
-
(b) =>
|
|
846
|
-
(b as { type: string }).type !== "thinking" &&
|
|
847
|
-
(b as { type: string }).type !== "redacted_thinking",
|
|
848
|
-
);
|
|
849
|
-
if (stripped.length < msg.content.length) {
|
|
850
|
-
// Ensure the message isn't empty after stripping
|
|
851
|
-
msg.content =
|
|
852
|
-
stripped.length > 0
|
|
853
|
-
? stripped
|
|
854
|
-
: [
|
|
855
|
-
{
|
|
856
|
-
type: "text" as const,
|
|
857
|
-
text: PLACEHOLDER_BLOCKS_OMITTED,
|
|
858
|
-
},
|
|
859
|
-
];
|
|
860
|
-
}
|
|
861
|
-
}
|
|
686
|
+
// Thinking blocks are stripped at rest by DB migration 209 so
|
|
687
|
+
// historical messages are clean when loaded. Within a turn,
|
|
688
|
+
// assistant messages have original thinking with valid signatures
|
|
689
|
+
// — the API accepts them. No provider-side stripping needed.
|
|
862
690
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
// the API rejects thinking blocks between tool_use blocks. Split such
|
|
866
|
-
// messages at each "thinking after tool_use" boundary to recreate the
|
|
867
|
-
// original multi-turn structure. With thinking blocks stripped above, the
|
|
868
|
-
// expansion is typically a no-op, but is kept as a safety net for edge
|
|
869
|
-
// cases where stripping is incomplete.
|
|
870
|
-
const expanded = expandCollapsedAssistantTurns(formatted);
|
|
871
|
-
|
|
872
|
-
sentMessages = ensureToolPairing(repairOrphanedServerToolUse(expanded));
|
|
873
|
-
const { effort, speed, output_config, ...restConfig } = (config ??
|
|
691
|
+
sentMessages = ensureToolPairing(repairOrphanedServerToolUse(formatted));
|
|
692
|
+
const { effort, speed, output_config, cacheTtl: _cacheTtl, ...restConfig } = (config ??
|
|
874
693
|
{}) as Record<string, unknown> & {
|
|
875
694
|
effort?: Anthropic.OutputConfig["effort"];
|
|
876
695
|
speed?: "standard" | "fast";
|
|
@@ -914,12 +733,12 @@ export class AnthropicProvider implements Provider {
|
|
|
914
733
|
{
|
|
915
734
|
type: "text" as const,
|
|
916
735
|
text: staticBlock,
|
|
917
|
-
cache_control: { type: "ephemeral" as const, ttl:
|
|
736
|
+
cache_control: { type: "ephemeral" as const, ttl: cacheTtl },
|
|
918
737
|
},
|
|
919
738
|
{
|
|
920
739
|
type: "text" as const,
|
|
921
740
|
text: dynamicBlock,
|
|
922
|
-
cache_control: { type: "ephemeral" as const, ttl:
|
|
741
|
+
cache_control: { type: "ephemeral" as const, ttl: cacheTtl },
|
|
923
742
|
},
|
|
924
743
|
];
|
|
925
744
|
} else {
|
|
@@ -927,7 +746,7 @@ export class AnthropicProvider implements Provider {
|
|
|
927
746
|
{
|
|
928
747
|
type: "text" as const,
|
|
929
748
|
text: systemPrompt,
|
|
930
|
-
cache_control: { type: "ephemeral" as const, ttl:
|
|
749
|
+
cache_control: { type: "ephemeral" as const, ttl: cacheTtl },
|
|
931
750
|
},
|
|
932
751
|
];
|
|
933
752
|
}
|
|
@@ -944,7 +763,12 @@ export class AnthropicProvider implements Provider {
|
|
|
944
763
|
description: t.description,
|
|
945
764
|
input_schema: t.input_schema as Anthropic.Tool["input_schema"],
|
|
946
765
|
...(i === otherTools.length - 1
|
|
947
|
-
? {
|
|
766
|
+
? {
|
|
767
|
+
cache_control: {
|
|
768
|
+
type: "ephemeral" as const,
|
|
769
|
+
ttl: cacheTtl,
|
|
770
|
+
},
|
|
771
|
+
}
|
|
948
772
|
: {}),
|
|
949
773
|
}));
|
|
950
774
|
const webSearchTool: Anthropic.WebSearchTool20250305 = {
|
|
@@ -959,41 +783,98 @@ export class AnthropicProvider implements Provider {
|
|
|
959
783
|
description: t.description,
|
|
960
784
|
input_schema: t.input_schema as Anthropic.Tool["input_schema"],
|
|
961
785
|
...(i === tools.length - 1
|
|
962
|
-
? {
|
|
786
|
+
? {
|
|
787
|
+
cache_control: {
|
|
788
|
+
type: "ephemeral" as const,
|
|
789
|
+
ttl: cacheTtl,
|
|
790
|
+
},
|
|
791
|
+
}
|
|
963
792
|
: {}),
|
|
964
793
|
}));
|
|
965
794
|
}
|
|
966
795
|
}
|
|
967
796
|
|
|
968
|
-
//
|
|
969
|
-
//
|
|
970
|
-
//
|
|
971
|
-
//
|
|
972
|
-
//
|
|
973
|
-
//
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
797
|
+
// Manual cache breakpoint on the turn-starting user message.
|
|
798
|
+
// This is the stable anchor for the current turn — everything up to
|
|
799
|
+
// and including it won't change during tool-use iterations, so a long
|
|
800
|
+
// TTL is appropriate. Walk backwards to find the last user message
|
|
801
|
+
// with a real text block (skipping tool_result-only messages and
|
|
802
|
+
// synthetic continuation placeholders injected by ensureToolPairing).
|
|
803
|
+
let turnStartIdx = -1;
|
|
804
|
+
for (let i = sentMessages.length - 1; i >= 0; i--) {
|
|
805
|
+
const msg = sentMessages[i];
|
|
806
|
+
if (msg.role !== "user" || !Array.isArray(msg.content)) continue;
|
|
807
|
+
const hasText = msg.content.some(
|
|
808
|
+
(b) =>
|
|
809
|
+
typeof b !== "string" &&
|
|
810
|
+
b.type === "text" &&
|
|
811
|
+
b.text !== SYNTHETIC_CONTINUATION_TEXT,
|
|
812
|
+
);
|
|
813
|
+
if (!hasText) continue;
|
|
814
|
+
const lastBlock = msg.content[msg.content.length - 1];
|
|
815
|
+
if (typeof lastBlock !== "string") {
|
|
816
|
+
(lastBlock as unknown as Record<string, unknown>).cache_control = {
|
|
817
|
+
type: "ephemeral",
|
|
818
|
+
ttl: cacheTtl,
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
turnStartIdx = i;
|
|
822
|
+
break;
|
|
984
823
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
824
|
+
|
|
825
|
+
// Advancing tail: place a short-lived 5m cache breakpoint on the last
|
|
826
|
+
// block of the last message when it falls after the turn-starting user
|
|
827
|
+
// message (i.e. tool-use loop content). This caches the growing tail
|
|
828
|
+
// cheaply without conflicting with the 1h breakpoints above.
|
|
829
|
+
// Skip thinking/redacted_thinking blocks — Anthropic doesn't allow
|
|
830
|
+
// cache_control on those types.
|
|
831
|
+
let tailBreakpointApplied = false;
|
|
832
|
+
if (turnStartIdx >= 0 && turnStartIdx < sentMessages.length - 1) {
|
|
833
|
+
const lastMsg = sentMessages[sentMessages.length - 1];
|
|
834
|
+
if (Array.isArray(lastMsg.content) && lastMsg.content.length > 0) {
|
|
835
|
+
const NON_CACHEABLE_TYPES = new Set(["thinking", "redacted_thinking"]);
|
|
836
|
+
let tailBlock: (typeof lastMsg.content)[number] | undefined;
|
|
837
|
+
for (let j = lastMsg.content.length - 1; j >= 0; j--) {
|
|
838
|
+
const block = lastMsg.content[j];
|
|
839
|
+
if (
|
|
840
|
+
typeof block !== "string" &&
|
|
841
|
+
!NON_CACHEABLE_TYPES.has((block as { type: string }).type)
|
|
842
|
+
) {
|
|
843
|
+
tailBlock = block;
|
|
844
|
+
break;
|
|
992
845
|
}
|
|
993
|
-
|
|
846
|
+
}
|
|
847
|
+
if (tailBlock && typeof tailBlock !== "string") {
|
|
848
|
+
(tailBlock as unknown as Record<string, unknown>).cache_control = {
|
|
849
|
+
type: "ephemeral",
|
|
850
|
+
ttl: "5m",
|
|
851
|
+
};
|
|
852
|
+
tailBreakpointApplied = true;
|
|
853
|
+
}
|
|
994
854
|
}
|
|
995
855
|
}
|
|
996
856
|
|
|
857
|
+
// Enforce Anthropic API maximum of 4 cache_control blocks.
|
|
858
|
+
// When the system prompt boundary splits into 2 cached blocks AND
|
|
859
|
+
// tools + turn-start + advancing-tail breakpoints are all present,
|
|
860
|
+
// we'd have 5. Drop the static system block's breakpoint — it's
|
|
861
|
+
// small (<1K tokens) so the re-read cost is negligible, while the
|
|
862
|
+
// dynamic block (workspace context) rarely changes mid-session and
|
|
863
|
+
// benefits more from caching.
|
|
864
|
+
const hasTailBreakpoint = tailBreakpointApplied;
|
|
865
|
+
const hasToolCacheBreakpoint =
|
|
866
|
+
params.tools?.some(
|
|
867
|
+
(t) => "cache_control" in t && t.cache_control != null,
|
|
868
|
+
) ?? false;
|
|
869
|
+
if (
|
|
870
|
+
hasTailBreakpoint &&
|
|
871
|
+
Array.isArray(params.system) &&
|
|
872
|
+
params.system.length === 2 &&
|
|
873
|
+
hasToolCacheBreakpoint
|
|
874
|
+
) {
|
|
875
|
+
delete (params.system[0] as unknown as Record<string, unknown>).cache_control;
|
|
876
|
+
}
|
|
877
|
+
|
|
997
878
|
const { signal: timeoutSignal, cleanup: cleanupTimeout } =
|
|
998
879
|
createStreamTimeout(this.streamTimeoutMs, signal);
|
|
999
880
|
|
|
@@ -1010,8 +891,7 @@ export class AnthropicProvider implements Provider {
|
|
|
1010
891
|
}
|
|
1011
892
|
|
|
1012
893
|
// Fast mode: use the beta endpoint with speed: "fast" for Opus 4.6
|
|
1013
|
-
const useFastMode =
|
|
1014
|
-
speed === "fast" && effectiveModel.includes("opus");
|
|
894
|
+
const useFastMode = speed === "fast" && effectiveModel.includes("opus");
|
|
1015
895
|
|
|
1016
896
|
// Collect required betas: extended cache TTL for 1h system prompt caching,
|
|
1017
897
|
// 1M context window, and fast-mode when applicable.
|
|
@@ -1198,6 +1078,14 @@ export class AnthropicProvider implements Provider {
|
|
|
1198
1078
|
"Anthropic 400: tool_use/tool_result pairing error — dumping message structure",
|
|
1199
1079
|
);
|
|
1200
1080
|
}
|
|
1081
|
+
log.error(
|
|
1082
|
+
{
|
|
1083
|
+
status: error.status,
|
|
1084
|
+
message: error.message,
|
|
1085
|
+
headers: Object.fromEntries(error.headers?.entries() ?? []),
|
|
1086
|
+
},
|
|
1087
|
+
`Anthropic API error (${error.status})`,
|
|
1088
|
+
);
|
|
1201
1089
|
const retryAfterMs = extractRetryAfterMs(error.headers);
|
|
1202
1090
|
throw new ProviderError(
|
|
1203
1091
|
`Anthropic API error (${error.status}): ${error.message}`,
|
|
@@ -156,6 +156,28 @@ export class AssistantEventHub {
|
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Returns true when at least one active subscriber would receive the given
|
|
161
|
+
* event based on the same assistant/conversation matching rules as publish().
|
|
162
|
+
*/
|
|
163
|
+
hasSubscribersForEvent(
|
|
164
|
+
event: Pick<AssistantEvent, "assistantId" | "conversationId">,
|
|
165
|
+
): boolean {
|
|
166
|
+
for (const entry of this.subscribers) {
|
|
167
|
+
if (!entry.active) continue;
|
|
168
|
+
if (entry.filter.assistantId !== event.assistantId) continue;
|
|
169
|
+
if (
|
|
170
|
+
event.conversationId != null &&
|
|
171
|
+
entry.filter.conversationId != null &&
|
|
172
|
+
entry.filter.conversationId !== event.conversationId
|
|
173
|
+
) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
159
181
|
/** Number of currently active subscribers (useful for tests and caps). */
|
|
160
182
|
subscriberCount(): number {
|
|
161
183
|
return this.subscribers.size;
|
|
@@ -130,6 +130,7 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
|
|
|
130
130
|
{ endpoint: "conversations", scopes: ["chat.read"] },
|
|
131
131
|
{ endpoint: "conversations:POST", scopes: ["chat.write"] },
|
|
132
132
|
{ endpoint: "conversations/fork", scopes: ["chat.write"] },
|
|
133
|
+
{ endpoint: "conversations/analyze", scopes: ["chat.write"] },
|
|
133
134
|
{ endpoint: "conversations/switch", scopes: ["chat.write"] },
|
|
134
135
|
{ endpoint: "conversations/name", scopes: ["chat.write"] },
|
|
135
136
|
{ endpoint: "conversations/cancel", scopes: ["chat.write"] },
|
|
@@ -374,6 +375,7 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
|
|
|
374
375
|
// Message content
|
|
375
376
|
{ endpoint: "messages/content", scopes: ["chat.read"] },
|
|
376
377
|
{ endpoint: "messages/llm-context", scopes: ["chat.read"] },
|
|
378
|
+
{ endpoint: "llm-request-logs/payload", scopes: ["chat.read"] },
|
|
377
379
|
{ endpoint: "messages/tts", scopes: ["chat.read"] },
|
|
378
380
|
|
|
379
381
|
// Queued message deletion
|
|
@@ -479,6 +481,10 @@ const ACTOR_ENDPOINTS: Array<{ endpoint: string; scopes: Scope[] }> = [
|
|
|
479
481
|
// Tools
|
|
480
482
|
{ endpoint: "tools", scopes: ["settings.read"] },
|
|
481
483
|
{ endpoint: "tools/simulate-permission", scopes: ["settings.read"] },
|
|
484
|
+
|
|
485
|
+
// Permission mode
|
|
486
|
+
{ endpoint: "permission-mode:GET", scopes: ["settings.read"] },
|
|
487
|
+
{ endpoint: "permission-mode", scopes: ["settings.write"] },
|
|
482
488
|
];
|
|
483
489
|
|
|
484
490
|
for (const { endpoint, scopes } of ACTOR_ENDPOINTS) {
|
|
@@ -531,3 +537,20 @@ registerPolicy("admin/rollback-migrations", {
|
|
|
531
537
|
requiredScopes: ["internal.write"],
|
|
532
538
|
allowedPrincipalTypes: ["svc_gateway"],
|
|
533
539
|
});
|
|
540
|
+
|
|
541
|
+
// Profiler management: gateway-only control-plane endpoints
|
|
542
|
+
registerPolicy("profiler/runs", {
|
|
543
|
+
requiredScopes: ["internal.write"],
|
|
544
|
+
allowedPrincipalTypes: ["svc_gateway"],
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
registerPolicy("profiler/runs/export", {
|
|
548
|
+
requiredScopes: ["internal.write"],
|
|
549
|
+
allowedPrincipalTypes: ["svc_gateway"],
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
// User-defined routes under /x/*
|
|
553
|
+
registerPolicy("x", {
|
|
554
|
+
requiredScopes: ["settings.read"],
|
|
555
|
+
allowedPrincipalTypes: ["actor", "svc_gateway", "svc_daemon", "local"],
|
|
556
|
+
});
|
|
@@ -171,6 +171,14 @@ export function isSigningKeyInitialized(): boolean {
|
|
|
171
171
|
return _authSigningKey !== undefined;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Reset the signing key to undefined. **Test-only** — used to simulate a
|
|
176
|
+
* fresh CLI subprocess where initAuthSigningKey() was never called.
|
|
177
|
+
*/
|
|
178
|
+
export function _resetSigningKeyForTesting(): void {
|
|
179
|
+
_authSigningKey = undefined;
|
|
180
|
+
}
|
|
181
|
+
|
|
174
182
|
/**
|
|
175
183
|
* Returns a short hex fingerprint of the current signing key.
|
|
176
184
|
* Used by assistant_status to let clients detect instance switches.
|
|
@@ -52,6 +52,7 @@ import { resolveConversationId } from "../memory/conversation-key-store.js";
|
|
|
52
52
|
import {
|
|
53
53
|
countConversations,
|
|
54
54
|
listConversations,
|
|
55
|
+
listPinnedConversations,
|
|
55
56
|
} from "../memory/conversation-queries.js";
|
|
56
57
|
import type { ExternalConversationBinding } from "../memory/external-conversation-store.js";
|
|
57
58
|
import * as externalConversationStore from "../memory/external-conversation-store.js";
|
|
@@ -126,6 +127,7 @@ import {
|
|
|
126
127
|
contactCatchAllRouteDefinitions,
|
|
127
128
|
contactRouteDefinitions,
|
|
128
129
|
} from "./routes/contact-routes.js";
|
|
130
|
+
import { conversationAnalysisRouteDefinitions } from "./routes/conversation-analysis-routes.js";
|
|
129
131
|
import { conversationAttentionRouteDefinitions } from "./routes/conversation-attention-routes.js";
|
|
130
132
|
import {
|
|
131
133
|
type ConversationManagementDeps,
|
|
@@ -171,6 +173,7 @@ import {
|
|
|
171
173
|
handlePairingStatus,
|
|
172
174
|
pairingRouteDefinitions,
|
|
173
175
|
} from "./routes/pairing-routes.js";
|
|
176
|
+
import { profilerRouteDefinitions } from "./routes/profiler-routes.js";
|
|
174
177
|
import { recordingRouteDefinitions } from "./routes/recording-routes.js";
|
|
175
178
|
import { scheduleRouteDefinitions } from "./routes/schedule-routes.js";
|
|
176
179
|
import { secretRouteDefinitions } from "./routes/secret-routes.js";
|
|
@@ -185,6 +188,7 @@ import { trustRulesRouteDefinitions } from "./routes/trust-rules-routes.js";
|
|
|
185
188
|
import { ttsRouteDefinitions } from "./routes/tts-routes.js";
|
|
186
189
|
import { upgradeBroadcastRouteDefinitions } from "./routes/upgrade-broadcast-routes.js";
|
|
187
190
|
import { usageRouteDefinitions } from "./routes/usage-routes.js";
|
|
191
|
+
import { userRouteDefinitions } from "./routes/user-routes.js";
|
|
188
192
|
import { watchRouteDefinitions } from "./routes/watch-routes.js";
|
|
189
193
|
import { workItemRouteDefinitions } from "./routes/work-items-routes.js";
|
|
190
194
|
import { workspaceCommitRouteDefinitions } from "./routes/workspace-commit-routes.js";
|
|
@@ -826,6 +830,7 @@ export class RuntimeHttpServer {
|
|
|
826
830
|
title: conversation.title ?? "Untitled",
|
|
827
831
|
createdAt: conversation.createdAt,
|
|
828
832
|
updatedAt: conversation.updatedAt,
|
|
833
|
+
lastMessageAt: conversation.lastMessageAt,
|
|
829
834
|
conversationType: conversation.conversationType ?? "standard",
|
|
830
835
|
source: conversation.source ?? "user",
|
|
831
836
|
...(conversation.scheduleJobId
|
|
@@ -961,6 +966,7 @@ export class RuntimeHttpServer {
|
|
|
961
966
|
...notificationRouteDefinitions(),
|
|
962
967
|
...diagnosticsRouteDefinitions(),
|
|
963
968
|
...logExportRouteDefinitions(),
|
|
969
|
+
...profilerRouteDefinitions(),
|
|
964
970
|
...documentRouteDefinitions(),
|
|
965
971
|
...workItemRouteDefinitions(
|
|
966
972
|
this.sendMessageDeps
|
|
@@ -1025,8 +1031,18 @@ export class RuntimeHttpServer {
|
|
|
1025
1031
|
const offset = Number(url.searchParams.get("offset") ?? 0);
|
|
1026
1032
|
const backgroundOnly =
|
|
1027
1033
|
url.searchParams.get("conversationType") === "background";
|
|
1028
|
-
|
|
1034
|
+
let rows = listConversations(limit, backgroundOnly, offset);
|
|
1029
1035
|
const totalCount = countConversations(backgroundOnly);
|
|
1036
|
+
// On the first page, ensure all pinned conversations are included
|
|
1037
|
+
// even if they fall outside the paginated window.
|
|
1038
|
+
if (offset === 0 && !backgroundOnly) {
|
|
1039
|
+
const pinned = listPinnedConversations();
|
|
1040
|
+
const seen = new Set(rows.map((c) => c.id));
|
|
1041
|
+
const missing = pinned.filter((c) => !seen.has(c.id));
|
|
1042
|
+
if (missing.length > 0) {
|
|
1043
|
+
rows = [...rows, ...missing];
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1030
1046
|
const conversationIds = rows.map((c) => c.id);
|
|
1031
1047
|
const displayMeta = getDisplayMetaForConversations(conversationIds);
|
|
1032
1048
|
const bindings =
|
|
@@ -1036,6 +1052,7 @@ export class RuntimeHttpServer {
|
|
|
1036
1052
|
const attentionStates =
|
|
1037
1053
|
getAttentionStateByConversationIds(conversationIds);
|
|
1038
1054
|
const parentCache = new Map<string, ConversationRow | null>();
|
|
1055
|
+
const nextOffset = offset + limit;
|
|
1039
1056
|
const response: Record<string, unknown> = {
|
|
1040
1057
|
conversations: rows.map((conversation) =>
|
|
1041
1058
|
this.serializeConversationSummary({
|
|
@@ -1046,7 +1063,8 @@ export class RuntimeHttpServer {
|
|
|
1046
1063
|
parentCache,
|
|
1047
1064
|
}),
|
|
1048
1065
|
),
|
|
1049
|
-
|
|
1066
|
+
nextOffset,
|
|
1067
|
+
hasMore: nextOffset < totalCount,
|
|
1050
1068
|
};
|
|
1051
1069
|
// Include groups array on first page only
|
|
1052
1070
|
if (offset === 0) {
|
|
@@ -1067,6 +1085,14 @@ export class RuntimeHttpServer {
|
|
|
1067
1085
|
? conversationManagementRouteDefinitions(conversationManagementDeps)
|
|
1068
1086
|
: []),
|
|
1069
1087
|
|
|
1088
|
+
...(this.sendMessageDeps
|
|
1089
|
+
? conversationAnalysisRouteDefinitions({
|
|
1090
|
+
sendMessageDeps: this.sendMessageDeps,
|
|
1091
|
+
buildConversationDetailResponse: (id) =>
|
|
1092
|
+
this.buildConversationDetailResponse(id),
|
|
1093
|
+
})
|
|
1094
|
+
: []),
|
|
1095
|
+
|
|
1070
1096
|
...groupRouteDefinitions(),
|
|
1071
1097
|
|
|
1072
1098
|
{
|
|
@@ -1305,6 +1331,10 @@ export class RuntimeHttpServer {
|
|
|
1305
1331
|
...traceEventRouteDefinitions(),
|
|
1306
1332
|
...migrationRouteDefinitions(),
|
|
1307
1333
|
|
|
1334
|
+
// User-defined routes under /x/* — must be LAST so built-in routes
|
|
1335
|
+
// always take priority.
|
|
1336
|
+
...userRouteDefinitions(),
|
|
1337
|
+
|
|
1308
1338
|
// Internal OAuth callback (gateway -> runtime)
|
|
1309
1339
|
{
|
|
1310
1340
|
endpoint: "internal/oauth/callback",
|
|
@@ -5,6 +5,7 @@ import type { ChannelId, InterfaceId } from "../channels/types.js";
|
|
|
5
5
|
import type { CesClient } from "../credential-execution/client.js";
|
|
6
6
|
import type { Conversation } from "../daemon/conversation.js";
|
|
7
7
|
import type { TrustContext } from "../daemon/conversation-runtime-assembly.js";
|
|
8
|
+
import type { ConversationCreateOptions } from "../daemon/handlers/shared.js";
|
|
8
9
|
import type { SkillOperationContext } from "../daemon/handlers/skills.js";
|
|
9
10
|
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
10
11
|
import type {
|
|
@@ -150,7 +151,10 @@ export type MessageProcessor = (
|
|
|
150
151
|
* Hub publishing wires outbound events to the SSE stream.
|
|
151
152
|
*/
|
|
152
153
|
export interface SendMessageDeps {
|
|
153
|
-
getOrCreateConversation: (
|
|
154
|
+
getOrCreateConversation: (
|
|
155
|
+
conversationId: string,
|
|
156
|
+
options?: ConversationCreateOptions,
|
|
157
|
+
) => Promise<Conversation>;
|
|
154
158
|
assistantEventHub: AssistantEventHub;
|
|
155
159
|
resolveAttachments: (attachmentIds: string[]) => Array<{
|
|
156
160
|
id: string;
|
|
@@ -274,4 +278,11 @@ export interface RuntimeMessagePayload {
|
|
|
274
278
|
textSegments?: string[];
|
|
275
279
|
thinkingSegments?: string[];
|
|
276
280
|
contentOrder?: string[];
|
|
281
|
+
subagentNotification?: {
|
|
282
|
+
subagentId: string;
|
|
283
|
+
label: string;
|
|
284
|
+
status: string;
|
|
285
|
+
error?: string;
|
|
286
|
+
conversationId?: string;
|
|
287
|
+
};
|
|
277
288
|
}
|