@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
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route handler for resolving pending question prompts.
|
|
3
|
+
*
|
|
4
|
+
* POST /v1/question-response — a client (UI or remote channel) submits the
|
|
5
|
+
* user's selection for a pending ask-question interaction registered by
|
|
6
|
+
* {@link QuestionPrompter}. Two top-level shapes are accepted:
|
|
7
|
+
*
|
|
8
|
+
* - `kind: "submit"` carries a `responses` array — one entry per question in
|
|
9
|
+
* the original batch. The web client builds this locally and POSTs it once
|
|
10
|
+
* the user is done revising the card.
|
|
11
|
+
* - `kind: "close"` records that the user dismissed the card without
|
|
12
|
+
* answering; every entry is reported as `skipped`.
|
|
13
|
+
*
|
|
14
|
+
* For backwards-compat we also accept the prior single-question shape
|
|
15
|
+
* (`{ kind: "option" | "free_text", ... }`) as syntactic sugar for a
|
|
16
|
+
* one-element batch. That branch only succeeds against a single-question
|
|
17
|
+
* batch — multi-question batches reject it with a helpful error.
|
|
18
|
+
*
|
|
19
|
+
* Cross-talk safety: pending interactions of other kinds (`confirmation`,
|
|
20
|
+
* `secret`, host_*, etc.) return 404 here rather than being mis-resolved.
|
|
21
|
+
*/
|
|
22
|
+
import { z } from "zod";
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
buildBatchEntries,
|
|
26
|
+
type QuestionBatchMetadata,
|
|
27
|
+
type QuestionBatchSubmission,
|
|
28
|
+
QuestionBatchValidationError,
|
|
29
|
+
type QuestionPromptResult,
|
|
30
|
+
} from "../../permissions/question-prompter.js";
|
|
31
|
+
import { getLogger } from "../../util/logger.js";
|
|
32
|
+
import * as pendingInteractions from "../pending-interactions.js";
|
|
33
|
+
import { BadRequestError, NotFoundError } from "./errors.js";
|
|
34
|
+
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
35
|
+
|
|
36
|
+
const log = getLogger("question-routes");
|
|
37
|
+
|
|
38
|
+
// ── Batched (current) body shape ────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
const SubmitEntry = z.discriminatedUnion("kind", [
|
|
41
|
+
z.object({
|
|
42
|
+
questionId: z.string(),
|
|
43
|
+
kind: z.literal("option"),
|
|
44
|
+
optionId: z.string(),
|
|
45
|
+
}),
|
|
46
|
+
z.object({
|
|
47
|
+
questionId: z.string(),
|
|
48
|
+
kind: z.literal("free_text"),
|
|
49
|
+
text: z.string(),
|
|
50
|
+
}),
|
|
51
|
+
z.object({
|
|
52
|
+
questionId: z.string(),
|
|
53
|
+
kind: z.literal("skip"),
|
|
54
|
+
}),
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
const SubmitBody = z.object({
|
|
58
|
+
requestId: z.string(),
|
|
59
|
+
kind: z.literal("submit"),
|
|
60
|
+
responses: z.array(SubmitEntry).min(1),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const CloseBody = z.object({
|
|
64
|
+
requestId: z.string(),
|
|
65
|
+
kind: z.literal("close"),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// ── Legacy single-question body shape (sugar for one-element batch) ──
|
|
69
|
+
|
|
70
|
+
const LegacyOptionBody = z.object({
|
|
71
|
+
requestId: z.string(),
|
|
72
|
+
kind: z.literal("option"),
|
|
73
|
+
optionId: z.string(),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const LegacyFreeTextBody = z.object({
|
|
77
|
+
requestId: z.string(),
|
|
78
|
+
kind: z.literal("free_text"),
|
|
79
|
+
text: z.string(),
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// All four variants are mutually exclusive by their `kind` literal, so use
|
|
83
|
+
// `discriminatedUnion` rather than plain `union`. The generated OpenAPI then
|
|
84
|
+
// emits `oneOf` for the body (matching the pre-batched-shape spec) instead
|
|
85
|
+
// of the looser `anyOf` that `z.union` produces.
|
|
86
|
+
const QuestionResponseBody = z.discriminatedUnion("kind", [
|
|
87
|
+
SubmitBody,
|
|
88
|
+
CloseBody,
|
|
89
|
+
LegacyOptionBody,
|
|
90
|
+
LegacyFreeTextBody,
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
type SubmitBody = z.infer<typeof SubmitBody>;
|
|
94
|
+
type CloseBody = z.infer<typeof CloseBody>;
|
|
95
|
+
type LegacyOptionBody = z.infer<typeof LegacyOptionBody>;
|
|
96
|
+
type LegacyFreeTextBody = z.infer<typeof LegacyFreeTextBody>;
|
|
97
|
+
type QuestionResponseBody = z.infer<typeof QuestionResponseBody>;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* POST /v1/question-response — resolve a pending ask-question interaction.
|
|
101
|
+
*/
|
|
102
|
+
function handleQuestionResponse({ body }: RouteHandlerArgs) {
|
|
103
|
+
const parsed = QuestionResponseBody.safeParse(body);
|
|
104
|
+
if (!parsed.success) {
|
|
105
|
+
throw new BadRequestError(
|
|
106
|
+
`Invalid question response body: ${parsed.error.message}`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const response: QuestionResponseBody = parsed.data;
|
|
111
|
+
const { requestId } = response;
|
|
112
|
+
|
|
113
|
+
const interaction = pendingInteractions.get(requestId);
|
|
114
|
+
if (!interaction || interaction.kind !== "question") {
|
|
115
|
+
log.warn(
|
|
116
|
+
{ requestId, foundKind: interaction?.kind },
|
|
117
|
+
"Question response for unknown or wrong-kind requestId",
|
|
118
|
+
);
|
|
119
|
+
throw new NotFoundError(
|
|
120
|
+
"No pending question interaction found for this requestId",
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Build + validate the result BEFORE touching `pendingInteractions`, so a
|
|
125
|
+
// bad payload leaves the pending interaction (and its timer) intact and the
|
|
126
|
+
// user gets another chance to submit a correct batch.
|
|
127
|
+
let result: QuestionPromptResult;
|
|
128
|
+
try {
|
|
129
|
+
if (response.kind === "close") {
|
|
130
|
+
const { orderedIds } = readBatchMetadata(interaction);
|
|
131
|
+
result = {
|
|
132
|
+
entries: orderedIds.map((id) => ({
|
|
133
|
+
questionId: id,
|
|
134
|
+
decision: "skipped" as const,
|
|
135
|
+
})),
|
|
136
|
+
overall: "closed",
|
|
137
|
+
};
|
|
138
|
+
} else {
|
|
139
|
+
const submissions = buildSubmissions(response, interaction);
|
|
140
|
+
result = buildCompletedResult(submissions, interaction);
|
|
141
|
+
}
|
|
142
|
+
} catch (err) {
|
|
143
|
+
if (err instanceof QuestionBatchValidationError) {
|
|
144
|
+
throw new BadRequestError(err.message);
|
|
145
|
+
}
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Validation passed — deregister now to clear the prompter timer, then
|
|
150
|
+
// hand the result to the prompter's caller via rpcResolve.
|
|
151
|
+
pendingInteractions.resolve(requestId);
|
|
152
|
+
|
|
153
|
+
log.info(
|
|
154
|
+
{
|
|
155
|
+
requestId,
|
|
156
|
+
overall: result.overall,
|
|
157
|
+
conversationId: interaction.conversationId,
|
|
158
|
+
},
|
|
159
|
+
"Question resolved",
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
(interaction.rpcResolve as
|
|
163
|
+
| ((value: QuestionPromptResult) => void)
|
|
164
|
+
| undefined)?.(result);
|
|
165
|
+
|
|
166
|
+
return { success: true };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Normalize the incoming body to a `QuestionBatchSubmission[]` for the
|
|
171
|
+
* submit/legacy paths. Returns `null` for the `close` path (no submissions).
|
|
172
|
+
*/
|
|
173
|
+
function buildSubmissions(
|
|
174
|
+
body: SubmitBody | LegacyOptionBody | LegacyFreeTextBody,
|
|
175
|
+
interaction: ReturnType<typeof pendingInteractions.get>,
|
|
176
|
+
): QuestionBatchSubmission[] {
|
|
177
|
+
if (body.kind === "submit") return body.responses;
|
|
178
|
+
|
|
179
|
+
// Legacy single-question shim: synthesize a one-element batch. The
|
|
180
|
+
// prompter stashed the ordered ids on the interaction metadata so we can
|
|
181
|
+
// pick the (single) target questionId here.
|
|
182
|
+
const { orderedIds } = readBatchMetadata(interaction);
|
|
183
|
+
if (orderedIds.length === 0) {
|
|
184
|
+
throw new QuestionBatchValidationError(
|
|
185
|
+
"Legacy single-question payload requires a registered batch with at least one question",
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
if (orderedIds.length > 1) {
|
|
189
|
+
throw new QuestionBatchValidationError(
|
|
190
|
+
'Legacy single-question payload cannot answer a multi-question batch; submit `{ kind: "submit", responses: [...] }` covering every question instead.',
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
const questionId = orderedIds[0]!;
|
|
194
|
+
if (body.kind === "option") {
|
|
195
|
+
return [{ questionId, kind: "option", optionId: body.optionId }];
|
|
196
|
+
}
|
|
197
|
+
return [{ questionId, kind: "free_text", text: body.text }];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Build a `completed` QuestionPromptResult from a batched submission and the
|
|
202
|
+
* per-question metadata the prompter stashed on the interaction. Delegates
|
|
203
|
+
* the validation + ordering loop to {@link buildBatchEntries} so the
|
|
204
|
+
* prompter and the route share a single implementation.
|
|
205
|
+
*/
|
|
206
|
+
function buildCompletedResult(
|
|
207
|
+
submissions: QuestionBatchSubmission[],
|
|
208
|
+
interaction: ReturnType<typeof pendingInteractions.get>,
|
|
209
|
+
): QuestionPromptResult {
|
|
210
|
+
const { orderedIds, optionsById } = readBatchMetadata(interaction);
|
|
211
|
+
if (orderedIds.length === 0) {
|
|
212
|
+
throw new QuestionBatchValidationError(
|
|
213
|
+
"No registered question ids for this batch",
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
const entries = buildBatchEntries(
|
|
217
|
+
orderedIds,
|
|
218
|
+
(qid, oid) => (optionsById[qid] ?? []).includes(oid),
|
|
219
|
+
new Set(Object.keys(optionsById)),
|
|
220
|
+
submissions,
|
|
221
|
+
);
|
|
222
|
+
return { entries, overall: "completed" };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Pull the prompter-stashed batch bookkeeping off a pending interaction.
|
|
227
|
+
* Returns empty defaults if the metadata is absent.
|
|
228
|
+
*/
|
|
229
|
+
function readBatchMetadata(
|
|
230
|
+
interaction: ReturnType<typeof pendingInteractions.get>,
|
|
231
|
+
): QuestionBatchMetadata {
|
|
232
|
+
const meta = interaction?.metadata as Partial<QuestionBatchMetadata> | undefined;
|
|
233
|
+
return {
|
|
234
|
+
orderedIds: meta?.orderedIds ?? [],
|
|
235
|
+
optionsById: meta?.optionsById ?? {},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
// Route definitions
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
|
|
243
|
+
export const ROUTES: RouteDefinition[] = [
|
|
244
|
+
{
|
|
245
|
+
operationId: "question_response",
|
|
246
|
+
endpoint: "question-response",
|
|
247
|
+
method: "POST",
|
|
248
|
+
handler: handleQuestionResponse,
|
|
249
|
+
requireGuardian: true,
|
|
250
|
+
summary: "Resolve a pending ask-question prompt",
|
|
251
|
+
description:
|
|
252
|
+
"Submit the user's batched response (or close the card) for a pending question prompt by requestId. Legacy single-question payloads remain accepted as syntactic sugar for a one-element batch.",
|
|
253
|
+
tags: ["approvals"],
|
|
254
|
+
requestBody: QuestionResponseBody,
|
|
255
|
+
responseBody: z.object({
|
|
256
|
+
success: z.boolean(),
|
|
257
|
+
}),
|
|
258
|
+
},
|
|
259
|
+
];
|
|
@@ -13,9 +13,7 @@ import {
|
|
|
13
13
|
getConversation,
|
|
14
14
|
updateConversationTitle,
|
|
15
15
|
} from "../../memory/conversation-crud.js";
|
|
16
|
-
import {
|
|
17
|
-
import { buildAssistantEvent } from "../assistant-event.js";
|
|
18
|
-
import { assistantEventHub } from "../assistant-event-hub.js";
|
|
16
|
+
import { publishConversationTitleChanged } from "../sync/resource-sync-events.js";
|
|
19
17
|
import { BadRequestError, NotFoundError } from "./errors.js";
|
|
20
18
|
import type { RouteDefinition } from "./types.js";
|
|
21
19
|
|
|
@@ -24,8 +22,6 @@ const RenameConversationBody = z.object({
|
|
|
24
22
|
title: z.string().min(1),
|
|
25
23
|
});
|
|
26
24
|
|
|
27
|
-
const log = getLogger("rename-conversation-routes");
|
|
28
|
-
|
|
29
25
|
export const ROUTES: RouteDefinition[] = [
|
|
30
26
|
{
|
|
31
27
|
operationId: "rename_conversation",
|
|
@@ -50,34 +46,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
50
46
|
|
|
51
47
|
updateConversationTitle(conversationId, title, 0);
|
|
52
48
|
|
|
53
|
-
|
|
54
|
-
.publish(
|
|
55
|
-
buildAssistantEvent(
|
|
56
|
-
{
|
|
57
|
-
type: "conversation_title_updated",
|
|
58
|
-
conversationId,
|
|
59
|
-
title,
|
|
60
|
-
},
|
|
61
|
-
conversationId,
|
|
62
|
-
),
|
|
63
|
-
)
|
|
64
|
-
.catch((err) => {
|
|
65
|
-
log.warn({ err }, "Failed to publish conversation_title_updated");
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
assistantEventHub
|
|
69
|
-
.publish(
|
|
70
|
-
buildAssistantEvent({
|
|
71
|
-
type: "conversation_list_invalidated",
|
|
72
|
-
reason: "renamed",
|
|
73
|
-
}),
|
|
74
|
-
)
|
|
75
|
-
.catch((err) => {
|
|
76
|
-
log.warn(
|
|
77
|
-
{ err },
|
|
78
|
-
"Failed to publish conversation_list_invalidated for rename",
|
|
79
|
-
);
|
|
80
|
-
});
|
|
49
|
+
publishConversationTitleChanged(conversationId, title);
|
|
81
50
|
|
|
82
51
|
return { ok: true };
|
|
83
52
|
},
|
|
@@ -78,7 +78,8 @@ function handleCreateSchedule(body: Record<string, unknown>) {
|
|
|
78
78
|
const expression =
|
|
79
79
|
typeof body.expression === "string" ? body.expression.trim() : "";
|
|
80
80
|
const message = typeof body.message === "string" ? body.message : "";
|
|
81
|
-
const timezoneRaw =
|
|
81
|
+
const timezoneRaw =
|
|
82
|
+
typeof body.timezone === "string" ? body.timezone.trim() : "";
|
|
82
83
|
const timezone = timezoneRaw === "" ? null : timezoneRaw;
|
|
83
84
|
const enabled = body.enabled !== false;
|
|
84
85
|
const mode = (body.mode as string | undefined) ?? "execute";
|
|
@@ -298,16 +299,12 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
298
299
|
.boolean()
|
|
299
300
|
.describe("Whether the schedule starts active (default true)")
|
|
300
301
|
.optional(),
|
|
301
|
-
mode: z
|
|
302
|
-
.string()
|
|
303
|
-
.describe("Currently must be 'execute'")
|
|
304
|
-
.optional(),
|
|
302
|
+
mode: z.string().describe("Currently must be 'execute'").optional(),
|
|
305
303
|
}),
|
|
306
304
|
responseBody: z.object({
|
|
307
305
|
schedules: z.array(z.unknown()).describe("Updated schedule list"),
|
|
308
306
|
}),
|
|
309
|
-
handler: ({ body }: RouteHandlerArgs) =>
|
|
310
|
-
handleCreateSchedule(body ?? {}),
|
|
307
|
+
handler: ({ body }: RouteHandlerArgs) => handleCreateSchedule(body ?? {}),
|
|
311
308
|
},
|
|
312
309
|
{
|
|
313
310
|
operationId: "listScheduleRuns",
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getMessages,
|
|
12
12
|
type MessageRow,
|
|
13
13
|
} from "../../memory/conversation-crud.js";
|
|
14
|
+
import { getConversationUsageTotals } from "../../memory/llm-usage-store.js";
|
|
14
15
|
import { getSubagentManager } from "../../subagent/index.js";
|
|
15
16
|
import { getLogger } from "../../util/logger.js";
|
|
16
17
|
import { BadRequestError, NotFoundError } from "./errors.js";
|
|
@@ -29,6 +30,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
29
30
|
export interface SubagentDetailResult {
|
|
30
31
|
subagentId: string;
|
|
31
32
|
objective?: string;
|
|
33
|
+
usage?: { inputTokens: number; outputTokens: number; estimatedCost: number };
|
|
32
34
|
events: Array<{
|
|
33
35
|
type: string;
|
|
34
36
|
content: string;
|
|
@@ -94,7 +96,11 @@ export function parseSubagentMessages(
|
|
|
94
96
|
typeof block.text === "string"
|
|
95
97
|
) {
|
|
96
98
|
events.push({ type: "text", content: block.text, messageId: m.id });
|
|
97
|
-
} else if (
|
|
99
|
+
} else if (
|
|
100
|
+
block.type === "tool_use" ||
|
|
101
|
+
block.type === "server_tool_use" ||
|
|
102
|
+
block.type === "mcp_tool_use"
|
|
103
|
+
) {
|
|
98
104
|
const name = typeof block.name === "string" ? block.name : "unknown";
|
|
99
105
|
const input = isRecord(block.input)
|
|
100
106
|
? (block.input as Record<string, unknown>)
|
|
@@ -106,7 +112,11 @@ export function parseSubagentMessages(
|
|
|
106
112
|
toolName: name,
|
|
107
113
|
});
|
|
108
114
|
if (id) pendingTools.set(id, name);
|
|
109
|
-
} else if (
|
|
115
|
+
} else if (
|
|
116
|
+
block.type === "tool_result" ||
|
|
117
|
+
block.type === "web_search_tool_result" ||
|
|
118
|
+
block.type === "mcp_tool_result"
|
|
119
|
+
) {
|
|
110
120
|
const toolUseId =
|
|
111
121
|
typeof block.tool_use_id === "string" ? block.tool_use_id : "";
|
|
112
122
|
const resultContent =
|
|
@@ -114,13 +124,18 @@ export function parseSubagentMessages(
|
|
|
114
124
|
? block.content
|
|
115
125
|
: Array.isArray(block.content)
|
|
116
126
|
? (block.content as unknown[])
|
|
117
|
-
.filter(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
.filter((b): b is Record<string, unknown> => isRecord(b))
|
|
128
|
+
.map((b) => {
|
|
129
|
+
if (b.type === "text" && typeof b.text === "string")
|
|
130
|
+
return b.text;
|
|
131
|
+
if (
|
|
132
|
+
b.type === "web_search_result" &&
|
|
133
|
+
typeof b.title === "string"
|
|
134
|
+
)
|
|
135
|
+
return `${b.title}\n${typeof b.url === "string" ? b.url : ""}`;
|
|
136
|
+
return null;
|
|
137
|
+
})
|
|
138
|
+
.filter((s): s is string => s != null)
|
|
124
139
|
.join("\n")
|
|
125
140
|
: "";
|
|
126
141
|
const isError = block.is_error === true;
|
|
@@ -142,7 +157,30 @@ function getSubagentDetail(
|
|
|
142
157
|
subagentId: string,
|
|
143
158
|
conversationId: string,
|
|
144
159
|
): SubagentDetailResult {
|
|
145
|
-
|
|
160
|
+
const messages = getMessages(conversationId);
|
|
161
|
+
log.info(
|
|
162
|
+
{
|
|
163
|
+
subagentId,
|
|
164
|
+
conversationId,
|
|
165
|
+
messageCount: messages.length,
|
|
166
|
+
roles: messages.map((m) => m.role),
|
|
167
|
+
},
|
|
168
|
+
"getSubagentDetail: raw messages from DB",
|
|
169
|
+
);
|
|
170
|
+
const result = parseSubagentMessages(subagentId, messages);
|
|
171
|
+
log.info(
|
|
172
|
+
{
|
|
173
|
+
subagentId,
|
|
174
|
+
eventCount: result.events.length,
|
|
175
|
+
eventTypes: result.events.map((e) => `${e.type}:${e.toolName ?? ""}`),
|
|
176
|
+
},
|
|
177
|
+
"getSubagentDetail: parsed events",
|
|
178
|
+
);
|
|
179
|
+
const usage = getConversationUsageTotals(conversationId);
|
|
180
|
+
if (usage.inputTokens > 0 || usage.outputTokens > 0) {
|
|
181
|
+
result.usage = usage;
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
146
184
|
}
|
|
147
185
|
|
|
148
186
|
// ---------------------------------------------------------------------------
|
|
@@ -150,6 +188,47 @@ function getSubagentDetail(
|
|
|
150
188
|
// ---------------------------------------------------------------------------
|
|
151
189
|
|
|
152
190
|
export const ROUTES: RouteDefinition[] = [
|
|
191
|
+
{
|
|
192
|
+
operationId: "reconcileSubagents",
|
|
193
|
+
endpoint: "subagents/reconcile",
|
|
194
|
+
method: "GET",
|
|
195
|
+
policyKey: "subagents",
|
|
196
|
+
summary: "Reconcile subagent live status",
|
|
197
|
+
description:
|
|
198
|
+
"Returns the live in-memory status of all subagents known to the daemon for a given parent conversation. Subagents not in the response are orphaned.",
|
|
199
|
+
tags: ["subagents"],
|
|
200
|
+
queryParams: [
|
|
201
|
+
{
|
|
202
|
+
name: "parentConversationId",
|
|
203
|
+
schema: { type: "string" },
|
|
204
|
+
description: "Parent conversation ID",
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
responseBody: z.object({
|
|
208
|
+
subagents: z.record(
|
|
209
|
+
z.string(),
|
|
210
|
+
z.object({
|
|
211
|
+
status: z.string(),
|
|
212
|
+
}),
|
|
213
|
+
),
|
|
214
|
+
}),
|
|
215
|
+
handler: ({ queryParams }) => {
|
|
216
|
+
const parentConversationId = queryParams?.parentConversationId;
|
|
217
|
+
if (!parentConversationId) {
|
|
218
|
+
throw new BadRequestError(
|
|
219
|
+
"parentConversationId query parameter is required",
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
const manager = getSubagentManager();
|
|
223
|
+
const children = manager.getChildrenOf(parentConversationId);
|
|
224
|
+
const subagents: Record<string, { status: string }> = {};
|
|
225
|
+
for (const child of children) {
|
|
226
|
+
subagents[child.config.id] = { status: child.status };
|
|
227
|
+
}
|
|
228
|
+
return { subagents };
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
|
|
153
232
|
{
|
|
154
233
|
operationId: "getSubagentDetail",
|
|
155
234
|
endpoint: "subagents/:id",
|
|
@@ -168,14 +247,19 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
168
247
|
responseBody: z.object({
|
|
169
248
|
subagentId: z.string(),
|
|
170
249
|
objective: z.string(),
|
|
250
|
+
usage: z
|
|
251
|
+
.object({
|
|
252
|
+
inputTokens: z.number(),
|
|
253
|
+
outputTokens: z.number(),
|
|
254
|
+
estimatedCost: z.number(),
|
|
255
|
+
})
|
|
256
|
+
.optional(),
|
|
171
257
|
events: z.array(z.unknown()).describe("Subagent event objects"),
|
|
172
258
|
}),
|
|
173
259
|
handler: ({ pathParams, queryParams }) => {
|
|
174
260
|
const conversationId = queryParams?.conversationId;
|
|
175
261
|
if (!conversationId) {
|
|
176
|
-
throw new BadRequestError(
|
|
177
|
-
"conversationId query parameter is required",
|
|
178
|
-
);
|
|
262
|
+
throw new BadRequestError("conversationId query parameter is required");
|
|
179
263
|
}
|
|
180
264
|
|
|
181
265
|
const manager = getSubagentManager();
|
|
@@ -209,11 +293,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
209
293
|
}
|
|
210
294
|
|
|
211
295
|
const manager = getSubagentManager();
|
|
212
|
-
const aborted = manager.abort(
|
|
213
|
-
pathParams!.id,
|
|
214
|
-
() => {},
|
|
215
|
-
conversationId,
|
|
216
|
-
);
|
|
296
|
+
const aborted = manager.abort(pathParams!.id, () => {}, conversationId);
|
|
217
297
|
|
|
218
298
|
if (!aborted) {
|
|
219
299
|
log.warn(
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
|
|
9
9
|
import { recordLifecycleEvent } from "../../memory/lifecycle-events-store.js";
|
|
10
|
+
import { getUsageTelemetryReporter } from "../../telemetry/usage-telemetry-reporter.js";
|
|
10
11
|
import { getLogger } from "../../util/logger.js";
|
|
11
12
|
import { BadRequestError } from "./errors.js";
|
|
12
13
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
@@ -32,6 +33,15 @@ function handleRecordLifecycleEvent({ body }: RouteHandlerArgs) {
|
|
|
32
33
|
return { id: event.id, event_name: event.eventName };
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
async function handleTelemetryFlush() {
|
|
37
|
+
const reporter = getUsageTelemetryReporter();
|
|
38
|
+
if (!reporter) {
|
|
39
|
+
return { flushed: false, reason: "disabled" };
|
|
40
|
+
}
|
|
41
|
+
await reporter.flush();
|
|
42
|
+
return { flushed: true };
|
|
43
|
+
}
|
|
44
|
+
|
|
35
45
|
export const ROUTES: RouteDefinition[] = [
|
|
36
46
|
{
|
|
37
47
|
operationId: "telemetry_lifecycle",
|
|
@@ -58,4 +68,21 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
58
68
|
]),
|
|
59
69
|
handler: handleRecordLifecycleEvent,
|
|
60
70
|
},
|
|
71
|
+
{
|
|
72
|
+
operationId: "telemetry_flush",
|
|
73
|
+
endpoint: "telemetry/flush",
|
|
74
|
+
method: "POST",
|
|
75
|
+
summary: "Flush pending telemetry events",
|
|
76
|
+
description:
|
|
77
|
+
"Force-flush all pending usage, turn, and lifecycle telemetry events to the platform.",
|
|
78
|
+
tags: ["telemetry"],
|
|
79
|
+
responseBody: z.union([
|
|
80
|
+
z.object({ flushed: z.literal(true) }),
|
|
81
|
+
z.object({
|
|
82
|
+
flushed: z.literal(false),
|
|
83
|
+
reason: z.string(),
|
|
84
|
+
}),
|
|
85
|
+
]),
|
|
86
|
+
handler: handleTelemetryFlush,
|
|
87
|
+
},
|
|
61
88
|
];
|
|
@@ -94,10 +94,35 @@ async function doSynthesize(
|
|
|
94
94
|
throw new ServiceUnavailableError("TTS provider is not configured");
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
throw new BadGatewayError(
|
|
97
|
+
throw new BadGatewayError(formatTtsFailureMessage(err));
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Build a user-facing error message for a failed TTS synthesis, embedding the
|
|
103
|
+
* upstream provider's message when available.
|
|
104
|
+
*
|
|
105
|
+
* The provider adapters surface a clean upstream message (e.g. "Free users
|
|
106
|
+
* cannot use library voices via the API…") and `synthesize-text` already
|
|
107
|
+
* prefixes those with `"TTS synthesis failed (provider: <id>): "`. We pass
|
|
108
|
+
* pre-prefixed messages through verbatim and only add the base prefix for
|
|
109
|
+
* raw provider errors, so users never see double- or triple-prefixed
|
|
110
|
+
* messages on the desktop / channels.
|
|
111
|
+
*
|
|
112
|
+
* Exported for unit testing.
|
|
113
|
+
*/
|
|
114
|
+
export function formatTtsFailureMessage(err: unknown): string {
|
|
115
|
+
const base = "TTS synthesis failed";
|
|
116
|
+
if (err instanceof Error && err.message && err.message.trim()) {
|
|
117
|
+
const trimmed = err.message.trim();
|
|
118
|
+
if (/^TTS synthesis failed\b/i.test(trimmed)) {
|
|
119
|
+
return trimmed;
|
|
120
|
+
}
|
|
121
|
+
return `${base}: ${trimmed}`;
|
|
122
|
+
}
|
|
123
|
+
return base;
|
|
124
|
+
}
|
|
125
|
+
|
|
101
126
|
// ---------------------------------------------------------------------------
|
|
102
127
|
// Response headers — shared by both routes
|
|
103
128
|
// ---------------------------------------------------------------------------
|
|
@@ -191,7 +216,7 @@ async function handleSynthesizeCliTts({ body }: RouteHandlerArgs) {
|
|
|
191
216
|
throw new ServiceUnavailableError("TTS provider is not configured");
|
|
192
217
|
}
|
|
193
218
|
|
|
194
|
-
throw new BadGatewayError(
|
|
219
|
+
throw new BadGatewayError(formatTtsFailureMessage(err));
|
|
195
220
|
}
|
|
196
221
|
}
|
|
197
222
|
|
|
@@ -9,6 +9,10 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { beforeAll, describe, expect, test } from "bun:test";
|
|
11
11
|
|
|
12
|
+
import { SYNC_TAGS } from "../../daemon/message-types/sync.js";
|
|
13
|
+
import type { AssistantEvent } from "../assistant-event.js";
|
|
14
|
+
import { assistantEventHub } from "../assistant-event-hub.js";
|
|
15
|
+
|
|
12
16
|
// ---------------------------------------------------------------------------
|
|
13
17
|
// Create a temp workspace directory for isolation
|
|
14
18
|
// ---------------------------------------------------------------------------
|
|
@@ -59,6 +63,15 @@ function getRoute(operationId: string): RouteDefinition {
|
|
|
59
63
|
return route;
|
|
60
64
|
}
|
|
61
65
|
|
|
66
|
+
async function waitFor(predicate: () => boolean): Promise<void> {
|
|
67
|
+
const deadline = Date.now() + 500;
|
|
68
|
+
while (Date.now() < deadline) {
|
|
69
|
+
if (predicate()) return;
|
|
70
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
71
|
+
}
|
|
72
|
+
throw new Error("Timed out waiting for workspace route event");
|
|
73
|
+
}
|
|
74
|
+
|
|
62
75
|
// ===========================================================================
|
|
63
76
|
// resolveWorkspacePath
|
|
64
77
|
// ===========================================================================
|
|
@@ -492,6 +505,36 @@ describe("POST /v1/workspace/write", () => {
|
|
|
492
505
|
handler({ body: { path: "subdir", content: "should fail" } }),
|
|
493
506
|
).toThrow(ConflictError);
|
|
494
507
|
});
|
|
508
|
+
|
|
509
|
+
test("publishes sounds sync events when writing sounds config", async () => {
|
|
510
|
+
const received: AssistantEvent[] = [];
|
|
511
|
+
const subscription = assistantEventHub.subscribe({
|
|
512
|
+
type: "process",
|
|
513
|
+
callback: (event) => {
|
|
514
|
+
received.push(event);
|
|
515
|
+
},
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
try {
|
|
519
|
+
handler({
|
|
520
|
+
body: {
|
|
521
|
+
path: "data/sounds/config.json",
|
|
522
|
+
content: "{}",
|
|
523
|
+
},
|
|
524
|
+
});
|
|
525
|
+
await waitFor(() => received.length === 2);
|
|
526
|
+
expect(received.map((event) => event.message.type)).toEqual([
|
|
527
|
+
"sounds_config_updated",
|
|
528
|
+
"sync_changed",
|
|
529
|
+
]);
|
|
530
|
+
expect(received[1]!.message).toEqual({
|
|
531
|
+
type: "sync_changed",
|
|
532
|
+
tags: [SYNC_TAGS.assistantSounds],
|
|
533
|
+
});
|
|
534
|
+
} finally {
|
|
535
|
+
subscription.dispose();
|
|
536
|
+
}
|
|
537
|
+
});
|
|
495
538
|
});
|
|
496
539
|
|
|
497
540
|
// ===========================================================================
|