@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
|
@@ -29,15 +29,22 @@
|
|
|
29
29
|
* through untouched).
|
|
30
30
|
* 6. Creates `<workspaceDir>/plugins-data/<plugin>/` on demand for per-plugin
|
|
31
31
|
* writable state and exposes it via {@link PluginInitContext.pluginStorageDir}.
|
|
32
|
-
* 7.
|
|
32
|
+
* 7. For each surviving plugin, registers its contributed tools, routes,
|
|
33
|
+
* and skills into their global registries via
|
|
34
|
+
* {@link registerPluginTools}, {@link registerSkillRoute}, and
|
|
35
|
+
* {@link registerPluginSkills}. Contributions land BEFORE `init()` so
|
|
36
|
+
* the plugin's hook can observe a registry where its own model-visible
|
|
37
|
+
* surface is already wired — useful for plugins that want to attach
|
|
38
|
+
* metadata, warm caches, or otherwise interact with their own
|
|
39
|
+
* contributions during initialization.
|
|
40
|
+
* 8. Awaits `plugin.init(ctx)` sequentially. An init failure surfaces as a
|
|
33
41
|
* {@link PluginExecutionError} naming the offending plugin and aborts
|
|
34
42
|
* bootstrap — later plugins' `init()` never runs and the daemon fails
|
|
35
|
-
* startup cleanly rather than coming up in a half-wired state.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* behind.
|
|
43
|
+
* startup cleanly rather than coming up in a half-wired state. The
|
|
44
|
+
* failing plugin's already-registered tools, routes, and skills are
|
|
45
|
+
* rolled back before the error propagates so the registry never
|
|
46
|
+
* carries state contributed by a plugin that never finished
|
|
47
|
+
* initializing.
|
|
41
48
|
*
|
|
42
49
|
* A single shutdown hook is registered via
|
|
43
50
|
* {@link registerShutdownHook} that walks the plugin list in **reverse
|
|
@@ -54,21 +61,24 @@ import { mkdirSync } from "node:fs";
|
|
|
54
61
|
import { join } from "node:path";
|
|
55
62
|
|
|
56
63
|
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
64
|
+
import { getConfig } from "../config/loader.js";
|
|
57
65
|
import type { AssistantConfig } from "../config/schema.js";
|
|
66
|
+
import { HOOKS } from "../plugin-api/constants.js";
|
|
58
67
|
import { registerDefaultPlugins } from "../plugins/defaults/index.js";
|
|
68
|
+
import { buildExternalPlugin } from "../plugins/external-plugin-loader.js";
|
|
59
69
|
import {
|
|
60
70
|
registerPluginSkills,
|
|
61
71
|
unregisterPluginSkills,
|
|
62
72
|
} from "../plugins/plugin-skill-contributions.js";
|
|
63
73
|
import {
|
|
64
|
-
|
|
74
|
+
getRegisteredPlugin,
|
|
65
75
|
getRegisteredPlugins,
|
|
76
|
+
setRegisteredPlugin,
|
|
66
77
|
unregisterPlugin,
|
|
67
78
|
} from "../plugins/registry.js";
|
|
68
79
|
import {
|
|
69
80
|
type Plugin,
|
|
70
81
|
PluginExecutionError,
|
|
71
|
-
type PluginInitContext,
|
|
72
82
|
type PluginShutdownContext,
|
|
73
83
|
type PluginSkillRegistration,
|
|
74
84
|
} from "../plugins/types.js";
|
|
@@ -84,6 +94,7 @@ import {
|
|
|
84
94
|
} from "../tools/registry.js";
|
|
85
95
|
import { getLogger } from "../util/logger.js";
|
|
86
96
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
97
|
+
import { APP_VERSION } from "../version.js";
|
|
87
98
|
import { registerShutdownHook } from "./shutdown-registry.js";
|
|
88
99
|
|
|
89
100
|
const log = getLogger("plugins-bootstrap");
|
|
@@ -170,6 +181,16 @@ function ensurePluginStorageDir(pluginName: string): string {
|
|
|
170
181
|
return dir;
|
|
171
182
|
}
|
|
172
183
|
|
|
184
|
+
function getDisabledPluginFlag(
|
|
185
|
+
plugin: Plugin,
|
|
186
|
+
config: AssistantConfig,
|
|
187
|
+
): string | undefined {
|
|
188
|
+
for (const flagKey of plugin.manifest.requiresFlag ?? []) {
|
|
189
|
+
if (!isAssistantFeatureFlagEnabled(flagKey, config)) return flagKey;
|
|
190
|
+
}
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
|
|
173
194
|
/**
|
|
174
195
|
* Run every registered plugin's `init()` hook sequentially and install a
|
|
175
196
|
* reverse-order shutdown hook. See the module docstring for full semantics.
|
|
@@ -245,187 +266,20 @@ export async function bootstrapPlugins(ctx: DaemonContext): Promise<void> {
|
|
|
245
266
|
|
|
246
267
|
for (const plugin of plugins) {
|
|
247
268
|
const name = plugin.manifest.name;
|
|
248
|
-
|
|
249
|
-
// Feature-flag gating — if any key in `manifest.requiresFlag` is
|
|
250
|
-
// disabled, skip this plugin entirely. Skipping means: no `init()`, no
|
|
251
|
-
// tool / route / skill contributions, and no shutdown hook entry. A
|
|
252
|
-
// later boot with the flag flipped ON picks up the plugin cleanly.
|
|
253
|
-
const requiredFlags = plugin.manifest.requiresFlag ?? [];
|
|
254
|
-
let disabledFlag: string | undefined;
|
|
255
|
-
for (const flagKey of requiredFlags) {
|
|
256
|
-
if (!isAssistantFeatureFlagEnabled(flagKey, ctx.config)) {
|
|
257
|
-
disabledFlag = flagKey;
|
|
258
|
-
break;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
269
|
+
const disabledFlag = getDisabledPluginFlag(plugin, ctx.config);
|
|
261
270
|
if (disabledFlag !== undefined) {
|
|
262
271
|
log.info(
|
|
263
272
|
{ plugin: name, flag: disabledFlag },
|
|
264
273
|
`skipping plugin ${name}: feature flag ${disabledFlag} is disabled`,
|
|
265
274
|
);
|
|
266
|
-
// Drop the plugin from the registry too. `registerPlugin()` added it at
|
|
267
|
-
// import time, and `getMiddlewaresFor()` / `getInjectors()` iterate over
|
|
268
|
-
// every registered entry — without this call, the gated plugin's
|
|
269
|
-
// middleware and injectors would still participate in every pipeline
|
|
270
|
-
// run and system-prompt assembly despite `init()` never firing.
|
|
271
275
|
unregisterPlugin(name);
|
|
272
276
|
continue;
|
|
273
277
|
}
|
|
274
278
|
|
|
275
|
-
// Collected as routes are accepted so the catch block can revoke exactly
|
|
276
|
-
// the routes this plugin contributed if a later contribution step throws.
|
|
277
|
-
// Hoisted above the try so it's in scope for the error path.
|
|
278
|
-
const routeHandles: SkillRouteHandle[] = [];
|
|
279
|
-
|
|
280
|
-
// Tracks whether `plugin.init()` ran to completion (or the plugin has no
|
|
281
|
-
// init at all). The catch block consults this to decide whether the
|
|
282
|
-
// currently-failing plugin's `onShutdown()` may run: onShutdown is paired
|
|
283
|
-
// with init, so a plugin that never completed init never set up the state
|
|
284
|
-
// onShutdown is meant to tear down. Calling onShutdown in that case would
|
|
285
|
-
// surprise plugin authors (their teardown runs against an uninitialized
|
|
286
|
-
// self) and breaks the documented lifecycle contract.
|
|
287
|
-
let initCompleted = false;
|
|
288
|
-
|
|
289
279
|
try {
|
|
290
|
-
|
|
291
|
-
// before calling `init()` so the plugin receives a fully-populated map.
|
|
292
|
-
const credentials: Record<string, string> = {};
|
|
293
|
-
const required = plugin.manifest.requiresCredential ?? [];
|
|
294
|
-
for (const key of required) {
|
|
295
|
-
credentials[key] = await resolveCredentialOrThrow(name, key);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Per-plugin config block, validated against the manifest's parser-like
|
|
299
|
-
// validator when one is declared.
|
|
300
|
-
const rawConfig = getPluginConfigRaw(ctx.config, name);
|
|
301
|
-
const config = validatePluginConfig(
|
|
302
|
-
name,
|
|
303
|
-
plugin.manifest.config,
|
|
304
|
-
rawConfig,
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
// Per-plugin writable data directory. Created lazily during bootstrap
|
|
308
|
-
// rather than at registration time so the side effect is isolated to
|
|
309
|
-
// the boot path.
|
|
310
|
-
const pluginStorageDir = ensurePluginStorageDir(name);
|
|
311
|
-
|
|
312
|
-
const initContext: PluginInitContext = {
|
|
313
|
-
config,
|
|
314
|
-
credentials,
|
|
315
|
-
logger: log.child({ plugin: name }),
|
|
316
|
-
pluginStorageDir,
|
|
317
|
-
assistantVersion: ctx.assistantVersion,
|
|
318
|
-
apiVersions: ASSISTANT_API_VERSIONS,
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
if (plugin.hooks?.init) {
|
|
322
|
-
try {
|
|
323
|
-
await plugin.hooks.init(initContext);
|
|
324
|
-
} catch (err) {
|
|
325
|
-
throw new PluginExecutionError(
|
|
326
|
-
`plugin ${name} init() failed: ${
|
|
327
|
-
err instanceof Error ? err.message : String(err)
|
|
328
|
-
}`,
|
|
329
|
-
name,
|
|
330
|
-
{ cause: err },
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
// Reached when init() succeeded or the plugin has no init hook. The
|
|
335
|
-
// catch block reads this to decide whether onShutdown may run.
|
|
336
|
-
initCompleted = true;
|
|
337
|
-
|
|
338
|
-
// After init succeeds, wire in the plugin's model-visible capabilities.
|
|
339
|
-
// Tool contributions (PR 31) register only after `init()` succeeds so a
|
|
340
|
-
// plugin that fails mid-init never leaves partially-wired tools behind.
|
|
341
|
-
// Tool registration failures are wrapped in a PluginExecutionError so
|
|
342
|
-
// the offending plugin name surfaces in the abort — matching the
|
|
343
|
-
// strict-fail semantics of `init()` errors.
|
|
344
|
-
if (plugin.tools && plugin.tools.length > 0) {
|
|
345
|
-
try {
|
|
346
|
-
const accepted = registerPluginTools(name, plugin.tools);
|
|
347
|
-
log.info(
|
|
348
|
-
{ plugin: name, count: accepted.length },
|
|
349
|
-
"plugin tools registered",
|
|
350
|
-
);
|
|
351
|
-
} catch (err) {
|
|
352
|
-
throw new PluginExecutionError(
|
|
353
|
-
`plugin ${name} tool registration failed: ${
|
|
354
|
-
err instanceof Error ? err.message : String(err)
|
|
355
|
-
}`,
|
|
356
|
-
name,
|
|
357
|
-
{ cause: err },
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// Route contributions (PR 32) — registered after init() succeeds so a
|
|
363
|
-
// plugin that fails to initialize never exposes a half-wired HTTP
|
|
364
|
-
// surface. Mirrors the skill-route registry shape; see
|
|
365
|
-
// {@link PluginRouteRegistration}. Retain every returned handle so the
|
|
366
|
-
// teardown path unregisters by identity rather than pattern text — two
|
|
367
|
-
// plugins (or a plugin and a skill) that happen to register the same
|
|
368
|
-
// regex must not evict each other's routes during shutdown.
|
|
369
|
-
if (plugin.routes && plugin.routes.length > 0) {
|
|
370
|
-
for (const route of plugin.routes) {
|
|
371
|
-
routeHandles.push(registerSkillRoute(route));
|
|
372
|
-
}
|
|
373
|
-
log.info(
|
|
374
|
-
{ plugin: name, count: plugin.routes.length },
|
|
375
|
-
"plugin routes registered",
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Skills register into the in-memory plugin-skill catalog so
|
|
380
|
-
// `skill_load` / `skill_execute` can resolve them alongside filesystem
|
|
381
|
-
// skills. A registration failure aborts bootstrap with the plugin named
|
|
382
|
-
// — same strict-fail posture as init() throws.
|
|
383
|
-
if (plugin.skills && plugin.skills.length > 0) {
|
|
384
|
-
try {
|
|
385
|
-
// `plugin.skills` is typed as `PluginSkillRegistration[]` at the
|
|
386
|
-
// Plugin interface — the type assertion here is a narrowing from
|
|
387
|
-
// that generic slot into the concrete shape the registry expects.
|
|
388
|
-
registerPluginSkills(
|
|
389
|
-
name,
|
|
390
|
-
plugin.skills as readonly PluginSkillRegistration[],
|
|
391
|
-
);
|
|
392
|
-
} catch (err) {
|
|
393
|
-
throw new PluginExecutionError(
|
|
394
|
-
`plugin ${name} skill registration failed: ${
|
|
395
|
-
err instanceof Error ? err.message : String(err)
|
|
396
|
-
}`,
|
|
397
|
-
name,
|
|
398
|
-
{ cause: err },
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
activePlugins.push({ plugin, routeHandles });
|
|
404
|
-
|
|
405
|
-
log.info({ plugin: name }, "plugin initialized");
|
|
280
|
+
activePlugins.push(await initializePlugin(plugin, ctx));
|
|
406
281
|
} catch (err) {
|
|
407
|
-
|
|
408
|
-
// `activePlugins` yet (that push happens only after every contribution
|
|
409
|
-
// step succeeds), so `teardownPartialInit()` alone would leave its
|
|
410
|
-
// already-registered tools, routes, and skills live.
|
|
411
|
-
//
|
|
412
|
-
// Branching on `initCompleted` keeps the init/onShutdown pairing
|
|
413
|
-
// intact. When init succeeded but a later contribution step (tools,
|
|
414
|
-
// routes, skills) threw, the plugin has live init-side state that
|
|
415
|
-
// `onShutdown()` is responsible for cleaning up, so the full
|
|
416
|
-
// rollbackPlugin() path runs. When init itself failed (or a step
|
|
417
|
-
// before init — credential resolution, config validation — threw),
|
|
418
|
-
// onShutdown must not run: calling it would invoke the plugin's
|
|
419
|
-
// teardown against an uninitialized self, violating the lifecycle
|
|
420
|
-
// contract documented on `Plugin.onShutdown`. In the init-failed case
|
|
421
|
-
// there is also nothing to unregister — tools, routes, and skills are
|
|
422
|
-
// all registered after init — so just drop the plugin from the
|
|
423
|
-
// registry (idempotent if already removed).
|
|
424
|
-
if (initCompleted) {
|
|
425
|
-
await rollbackPlugin({ plugin, routeHandles });
|
|
426
|
-
} else {
|
|
427
|
-
unregisterPlugin(name);
|
|
428
|
-
}
|
|
282
|
+
unregisterPlugin(name);
|
|
429
283
|
await teardownPartialInit();
|
|
430
284
|
throw err;
|
|
431
285
|
}
|
|
@@ -470,6 +324,114 @@ interface ActivePlugin {
|
|
|
470
324
|
readonly routeHandles: readonly SkillRouteHandle[];
|
|
471
325
|
}
|
|
472
326
|
|
|
327
|
+
async function initializePlugin(
|
|
328
|
+
plugin: Plugin,
|
|
329
|
+
ctx: DaemonContext,
|
|
330
|
+
): Promise<ActivePlugin> {
|
|
331
|
+
const name = plugin.manifest.name;
|
|
332
|
+
const routeHandles: SkillRouteHandle[] = [];
|
|
333
|
+
let initCompleted = false;
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
const credentials: Record<string, string> = {};
|
|
337
|
+
for (const key of plugin.manifest.requiresCredential ?? []) {
|
|
338
|
+
credentials[key] = await resolveCredentialOrThrow(name, key);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const config = validatePluginConfig(
|
|
342
|
+
name,
|
|
343
|
+
plugin.manifest.config,
|
|
344
|
+
getPluginConfigRaw(ctx.config, name),
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
const initContext = {
|
|
348
|
+
config,
|
|
349
|
+
credentials,
|
|
350
|
+
logger: log.child({ plugin: name }),
|
|
351
|
+
pluginStorageDir: ensurePluginStorageDir(name),
|
|
352
|
+
assistantVersion: ctx.assistantVersion,
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
if (plugin.tools && plugin.tools.length > 0) {
|
|
356
|
+
try {
|
|
357
|
+
const accepted = registerPluginTools(name, plugin.tools);
|
|
358
|
+
log.info(
|
|
359
|
+
{ plugin: name, count: accepted.length },
|
|
360
|
+
"plugin tools registered",
|
|
361
|
+
);
|
|
362
|
+
} catch (err) {
|
|
363
|
+
throw new PluginExecutionError(
|
|
364
|
+
`plugin ${name} tool registration failed: ${
|
|
365
|
+
err instanceof Error ? err.message : String(err)
|
|
366
|
+
}`,
|
|
367
|
+
name,
|
|
368
|
+
{ cause: err },
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (plugin.routes && plugin.routes.length > 0) {
|
|
374
|
+
for (const route of plugin.routes) {
|
|
375
|
+
routeHandles.push(registerSkillRoute(route));
|
|
376
|
+
}
|
|
377
|
+
log.info(
|
|
378
|
+
{ plugin: name, count: plugin.routes.length },
|
|
379
|
+
"plugin routes registered",
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (plugin.skills && plugin.skills.length > 0) {
|
|
384
|
+
try {
|
|
385
|
+
registerPluginSkills(
|
|
386
|
+
name,
|
|
387
|
+
plugin.skills as readonly PluginSkillRegistration[],
|
|
388
|
+
);
|
|
389
|
+
} catch (err) {
|
|
390
|
+
throw new PluginExecutionError(
|
|
391
|
+
`plugin ${name} skill registration failed: ${
|
|
392
|
+
err instanceof Error ? err.message : String(err)
|
|
393
|
+
}`,
|
|
394
|
+
name,
|
|
395
|
+
{ cause: err },
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (plugin.hooks?.[HOOKS.INIT]) {
|
|
401
|
+
try {
|
|
402
|
+
await plugin.hooks[HOOKS.INIT](initContext);
|
|
403
|
+
} catch (err) {
|
|
404
|
+
throw new PluginExecutionError(
|
|
405
|
+
`plugin ${name} init() failed: ${
|
|
406
|
+
err instanceof Error ? err.message : String(err)
|
|
407
|
+
}`,
|
|
408
|
+
name,
|
|
409
|
+
{ cause: err },
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
initCompleted = true;
|
|
414
|
+
|
|
415
|
+
log.info({ plugin: name }, "plugin initialized");
|
|
416
|
+
return { plugin, routeHandles };
|
|
417
|
+
} catch (err) {
|
|
418
|
+
if (initCompleted) {
|
|
419
|
+
await teardownPlugin({ plugin, routeHandles }, "bootstrap-failed", {
|
|
420
|
+
assistantVersion: ctx.assistantVersion,
|
|
421
|
+
});
|
|
422
|
+
} else {
|
|
423
|
+
for (const handle of routeHandles) {
|
|
424
|
+
unregisterSkillRoute(handle);
|
|
425
|
+
}
|
|
426
|
+
unregisterPluginTools(name);
|
|
427
|
+
if (plugin.skills && plugin.skills.length > 0) {
|
|
428
|
+
unregisterPluginSkills(name);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
throw err;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
473
435
|
/**
|
|
474
436
|
* Tear down a single fully-initialized plugin: unregister routes, unregister
|
|
475
437
|
* tools, invoke `onShutdown()` if present, then unregister skills. Every step
|
|
@@ -514,9 +476,9 @@ async function teardownPlugin(
|
|
|
514
476
|
);
|
|
515
477
|
}
|
|
516
478
|
|
|
517
|
-
if (plugin.hooks?.
|
|
479
|
+
if (plugin.hooks?.[HOOKS.SHUTDOWN]) {
|
|
518
480
|
try {
|
|
519
|
-
await plugin.hooks.
|
|
481
|
+
await plugin.hooks[HOOKS.SHUTDOWN](shutdownContext);
|
|
520
482
|
} catch (err) {
|
|
521
483
|
// Swallow — we want every plugin's shutdown to get a chance to run
|
|
522
484
|
// even when an earlier one throws. The outer runShutdownHooks already
|
|
@@ -540,3 +502,77 @@ async function teardownPlugin(
|
|
|
540
502
|
}
|
|
541
503
|
}
|
|
542
504
|
}
|
|
505
|
+
|
|
506
|
+
/** Rebuild a changed external plugin and swap it into the live registry. */
|
|
507
|
+
export async function reregisterExternalPlugin(
|
|
508
|
+
pluginName: string,
|
|
509
|
+
): Promise<void> {
|
|
510
|
+
const pluginDir = join(getWorkspaceDir(), "plugins", pluginName);
|
|
511
|
+
const plugin = await buildExternalPlugin(pluginDir);
|
|
512
|
+
if (plugin === undefined) return;
|
|
513
|
+
|
|
514
|
+
if (plugin.manifest.name !== pluginName) {
|
|
515
|
+
log.warn(
|
|
516
|
+
{ plugin: pluginName, manifestName: plugin.manifest.name, pluginDir },
|
|
517
|
+
`external plugin reload skipped: directory name "${pluginName}" does not match manifest.name "${plugin.manifest.name}"`,
|
|
518
|
+
);
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const ctx: DaemonContext = {
|
|
523
|
+
config: getConfig(),
|
|
524
|
+
assistantVersion: APP_VERSION,
|
|
525
|
+
};
|
|
526
|
+
const disabledFlag = getDisabledPluginFlag(plugin, ctx.config);
|
|
527
|
+
if (disabledFlag !== undefined) {
|
|
528
|
+
log.info(
|
|
529
|
+
{ plugin: pluginName, flag: disabledFlag },
|
|
530
|
+
`external plugin reload skipped: feature flag ${disabledFlag} is disabled`,
|
|
531
|
+
);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const existing = getRegisteredPlugin(pluginName);
|
|
536
|
+
if (existing === undefined) {
|
|
537
|
+
try {
|
|
538
|
+
await initializePlugin(plugin, ctx);
|
|
539
|
+
setRegisteredPlugin(plugin);
|
|
540
|
+
log.info({ plugin: pluginName }, "external plugin registered post-boot");
|
|
541
|
+
} catch (err) {
|
|
542
|
+
log.error(
|
|
543
|
+
{ err, plugin: pluginName },
|
|
544
|
+
"external plugin post-boot registration failed",
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
try {
|
|
551
|
+
unregisterPluginTools(pluginName);
|
|
552
|
+
} catch (err) {
|
|
553
|
+
log.warn(
|
|
554
|
+
{ err, plugin: pluginName },
|
|
555
|
+
"external plugin reload: tool unregister failed (continuing)",
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
setRegisteredPlugin(plugin);
|
|
560
|
+
|
|
561
|
+
if (plugin.tools && plugin.tools.length > 0) {
|
|
562
|
+
try {
|
|
563
|
+
const accepted = registerPluginTools(pluginName, plugin.tools);
|
|
564
|
+
log.info(
|
|
565
|
+
{ plugin: pluginName, count: accepted.length },
|
|
566
|
+
"external plugin reloaded",
|
|
567
|
+
);
|
|
568
|
+
} catch (err) {
|
|
569
|
+
log.error(
|
|
570
|
+
{ err, plugin: pluginName },
|
|
571
|
+
"external plugin reload: tool registration failed",
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
log.info({ plugin: pluginName }, "external plugin reloaded");
|
|
578
|
+
}
|
|
@@ -11,6 +11,7 @@ export interface OnboardingGreetingContext {
|
|
|
11
11
|
tone: string;
|
|
12
12
|
userName?: string;
|
|
13
13
|
assistantName?: string;
|
|
14
|
+
googleConnected?: boolean;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export const CANNED_FIRST_GREETING = [
|
|
@@ -76,6 +77,16 @@ const TONE_INVITE: Record<Tone, string> = {
|
|
|
76
77
|
"We can start with whatever's in front of you, or just talk for a bit first. Either way.",
|
|
77
78
|
};
|
|
78
79
|
|
|
80
|
+
const TONE_GOOGLE_SCAN: Record<Tone, string> = {
|
|
81
|
+
grounded:
|
|
82
|
+
"I can scan your email, calendar, and drive in the background while we talk — just say the word.",
|
|
83
|
+
warm: "Also — I can scan your email, calendar, and drive in the background while we chat, if you'd like. Just let me know.",
|
|
84
|
+
energetic:
|
|
85
|
+
"Oh, and I can scan your email, calendar, and drive in the background right now — want me to?",
|
|
86
|
+
poetic:
|
|
87
|
+
"I can also look through your email, calendar, and drive quietly in the background — say the word.",
|
|
88
|
+
};
|
|
89
|
+
|
|
79
90
|
function buildInvite(tone: Tone = "grounded"): string {
|
|
80
91
|
return TONE_INVITE[tone];
|
|
81
92
|
}
|
|
@@ -96,11 +107,20 @@ function buildPersonalizedGreeting(ctx: OnboardingGreetingContext): string {
|
|
|
96
107
|
const assistant = ctx.assistantName?.trim();
|
|
97
108
|
const tone = resolveTone(ctx.tone);
|
|
98
109
|
|
|
99
|
-
if (
|
|
110
|
+
if (
|
|
111
|
+
!name &&
|
|
112
|
+
!assistant &&
|
|
113
|
+
!VALID_TONES.has(ctx.tone) &&
|
|
114
|
+
!ctx.googleConnected
|
|
115
|
+
) {
|
|
100
116
|
return CANNED_FIRST_GREETING;
|
|
101
117
|
}
|
|
102
118
|
|
|
103
119
|
const intro = buildIntroLine(name, assistant, tone);
|
|
104
120
|
const invite = buildInvite(tone);
|
|
105
|
-
|
|
121
|
+
const parts = [intro, "", invite];
|
|
122
|
+
if (ctx.googleConnected) {
|
|
123
|
+
parts.push("", TONE_GOOGLE_SCAN[tone]);
|
|
124
|
+
}
|
|
125
|
+
return parts.join("\n");
|
|
106
126
|
}
|
|
@@ -9,12 +9,7 @@ import {
|
|
|
9
9
|
GUARDIAN_ACTION_COPY_TIMEOUT_MS,
|
|
10
10
|
includesRequiredKeywords,
|
|
11
11
|
} from "../runtime/guardian-action-message-composer.js";
|
|
12
|
-
import type {
|
|
13
|
-
GuardianActionCopyGenerator,
|
|
14
|
-
GuardianFollowUpConversationGenerator,
|
|
15
|
-
GuardianFollowUpDisposition,
|
|
16
|
-
GuardianFollowUpTurnResult,
|
|
17
|
-
} from "../runtime/http-types.js";
|
|
12
|
+
import type { GuardianActionCopyGenerator } from "../runtime/http-types.js";
|
|
18
13
|
|
|
19
14
|
/**
|
|
20
15
|
* Create the daemon-owned guardian action copy generator that resolves
|
|
@@ -74,122 +69,3 @@ export function createGuardianActionCopyGenerator(): GuardianActionCopyGenerator
|
|
|
74
69
|
return cleaned;
|
|
75
70
|
};
|
|
76
71
|
}
|
|
77
|
-
|
|
78
|
-
// ---------------------------------------------------------------------------
|
|
79
|
-
// Guardian follow-up conversation generator
|
|
80
|
-
// ---------------------------------------------------------------------------
|
|
81
|
-
|
|
82
|
-
const FOLLOWUP_CONVERSATION_TIMEOUT_MS = 8_000;
|
|
83
|
-
const FOLLOWUP_CONVERSATION_MAX_TOKENS = 300;
|
|
84
|
-
|
|
85
|
-
const FOLLOWUP_CONVERSATION_SYSTEM_PROMPT =
|
|
86
|
-
"You are an assistant helping route a guardian's reply to a post-timeout follow-up message. " +
|
|
87
|
-
"A voice caller asked a question, but the call timed out before the guardian could answer. " +
|
|
88
|
-
"The guardian has now replied late, and was asked whether they want to call the caller back " +
|
|
89
|
-
"or skip it. " +
|
|
90
|
-
"Analyze the guardian's latest reply to determine their intent. " +
|
|
91
|
-
"When uncertain, default to keep_pending and ask a clarifying question. " +
|
|
92
|
-
"Always provide a natural, helpful reply along with your decision.";
|
|
93
|
-
|
|
94
|
-
const FOLLOWUP_CONVERSATION_TOOL_NAME = "followup_decision";
|
|
95
|
-
|
|
96
|
-
const FOLLOWUP_CONVERSATION_TOOL_SCHEMA = {
|
|
97
|
-
name: FOLLOWUP_CONVERSATION_TOOL_NAME,
|
|
98
|
-
description:
|
|
99
|
-
"Record the guardian's follow-up decision and a natural reply. " +
|
|
100
|
-
"Call this tool with the determined disposition and a reply to the guardian.",
|
|
101
|
-
input_schema: {
|
|
102
|
-
type: "object" as const,
|
|
103
|
-
properties: {
|
|
104
|
-
disposition: {
|
|
105
|
-
type: "string",
|
|
106
|
-
enum: ["call_back", "decline", "keep_pending"],
|
|
107
|
-
description:
|
|
108
|
-
"The guardian's intent: call_back to call the original caller, " +
|
|
109
|
-
"decline to skip the follow-up, " +
|
|
110
|
-
"keep_pending if the intent is unclear (ask for clarification).",
|
|
111
|
-
},
|
|
112
|
-
replyText: {
|
|
113
|
-
type: "string",
|
|
114
|
-
description: "A natural language reply to send back to the guardian.",
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
|
-
required: ["disposition", "replyText"],
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const VALID_FOLLOWUP_DISPOSITIONS: ReadonlySet<string> = new Set([
|
|
122
|
-
"call_back",
|
|
123
|
-
"decline",
|
|
124
|
-
"keep_pending",
|
|
125
|
-
]);
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Create the daemon-owned guardian follow-up conversation generator.
|
|
129
|
-
* Uses tool/function calling to produce structured dispositions alongside
|
|
130
|
-
* natural reply text. Follows the same pattern as
|
|
131
|
-
* createApprovalConversationGenerator().
|
|
132
|
-
*/
|
|
133
|
-
export function createGuardianFollowUpConversationGenerator(): GuardianFollowUpConversationGenerator {
|
|
134
|
-
return async (context) => {
|
|
135
|
-
const baseProvider = await getConfiguredProvider("guardianQuestionCopy");
|
|
136
|
-
if (!baseProvider) {
|
|
137
|
-
throw new Error("No configured provider available for follow-up conversation");
|
|
138
|
-
}
|
|
139
|
-
const provider = wrapWithCallSiteRouting(baseProvider, loadConfig());
|
|
140
|
-
|
|
141
|
-
const userPrompt = [
|
|
142
|
-
`Original question from the voice call: "${context.questionText}"`,
|
|
143
|
-
`Guardian's late answer: "${context.lateAnswerText}"`,
|
|
144
|
-
`\nGuardian's latest reply: ${context.guardianReply}`,
|
|
145
|
-
].join("\n");
|
|
146
|
-
|
|
147
|
-
const response = await provider.sendMessage(
|
|
148
|
-
[{ role: "user", content: [{ type: "text", text: userPrompt }] }],
|
|
149
|
-
[FOLLOWUP_CONVERSATION_TOOL_SCHEMA],
|
|
150
|
-
FOLLOWUP_CONVERSATION_SYSTEM_PROMPT,
|
|
151
|
-
{
|
|
152
|
-
config: {
|
|
153
|
-
max_tokens: FOLLOWUP_CONVERSATION_MAX_TOKENS,
|
|
154
|
-
callSite: "guardianQuestionCopy",
|
|
155
|
-
},
|
|
156
|
-
signal: AbortSignal.timeout(FOLLOWUP_CONVERSATION_TIMEOUT_MS),
|
|
157
|
-
},
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
// Extract the tool_use block from the response
|
|
161
|
-
const toolUseBlock = response.content.find(
|
|
162
|
-
(block) =>
|
|
163
|
-
block.type === "tool_use" &&
|
|
164
|
-
block.name === FOLLOWUP_CONVERSATION_TOOL_NAME,
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
if (!toolUseBlock || toolUseBlock.type !== "tool_use") {
|
|
168
|
-
throw new Error(
|
|
169
|
-
"Provider did not return a tool_use block for follow-up decision",
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const input = toolUseBlock.input as Record<string, unknown>;
|
|
174
|
-
|
|
175
|
-
// Strict validation of the structured output
|
|
176
|
-
const disposition = input.disposition;
|
|
177
|
-
if (
|
|
178
|
-
typeof disposition !== "string" ||
|
|
179
|
-
!VALID_FOLLOWUP_DISPOSITIONS.has(disposition)
|
|
180
|
-
) {
|
|
181
|
-
throw new Error(`Invalid disposition: ${String(disposition)}`);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const replyText = input.replyText;
|
|
185
|
-
if (typeof replyText !== "string" || replyText.trim().length === 0) {
|
|
186
|
-
throw new Error("Missing or empty replyText in tool_use response");
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const result: GuardianFollowUpTurnResult = {
|
|
190
|
-
disposition: disposition as GuardianFollowUpDisposition,
|
|
191
|
-
replyText: replyText.trim(),
|
|
192
|
-
};
|
|
193
|
-
return result;
|
|
194
|
-
};
|
|
195
|
-
}
|