@vellumai/assistant 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +2 -7
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +5 -0
- package/docker-init-apt-root.sh +94 -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 +325 -3
- 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-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 +41 -0
- 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 +1 -0
- 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 +2 -0
- 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-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 +76 -9
- 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__/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 +60 -36
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- 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 +266 -10
- 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-chain.test.ts +10 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -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-catalog-parity.test.ts +55 -13
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +31 -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__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +24 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- 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} +1 -1
- 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 +142 -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} +8 -8
- 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__/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 +737 -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-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/types.ts +108 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/commands/__tests__/schedules.test.ts +491 -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 +329 -317
- package/src/cli/commands/plugins.ts +185 -0
- package/src/cli/commands/schedules.ts +391 -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__/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 +304 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +38 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
- 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/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/feature-flag-registry.json +41 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +7 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/channels.ts +8 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1047 -0
- package/src/context/token-estimator.ts +2 -2
- 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 +34 -0
- 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.ts +169 -9
- 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 +16 -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 -1
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- 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 +98 -23
- package/src/daemon/lifecycle.ts +45 -35
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +49 -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/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +1 -1
- package/src/daemon/pkb-reminder-builder.ts +1 -1
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +21 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +29 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +41 -0
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/feed-types.ts +22 -0
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- 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 +263 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- 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 +110 -10
- package/src/memory/db-init.ts +6 -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 +66 -9
- package/src/memory/graph/conversation-graph-memory.ts +31 -15
- package/src/memory/graph/tools.ts +3 -3
- package/src/memory/indexer.ts +34 -29
- 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 +17 -10
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-usage-store.ts +125 -5
- 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/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/inference.ts +1 -3
- package/src/memory/schema/infrastructure.ts +12 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/injection.test.ts +98 -8
- 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/injection.ts +32 -6
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +35 -5
- 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/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/conversation-pairing.ts +2 -1
- package/src/notifications/decision-engine.ts +2 -1
- package/src/notifications/emit-signal.ts +20 -1
- package/src/notifications/home-feed-side-effect.ts +54 -0
- package/src/notifications/signal.ts +3 -1
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.ts +6 -2
- 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 +36 -3
- 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 +105 -32
- 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 +31 -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 +12 -0
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -166
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +173 -0
- package/src/providers/__tests__/inference.test.ts +22 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/connection-resolution.ts +7 -0
- package/src/providers/inference/adapter-factory.ts +41 -4
- package/src/providers/inference/connections.ts +74 -29
- package/src/providers/inference/resolve-auth.ts +12 -4
- package/src/providers/model-catalog.ts +294 -12
- package/src/providers/openai/chat-completions-provider.ts +10 -2
- package/src/providers/openrouter/client.ts +7 -0
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
- 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 +36 -0
- package/src/providers/registry.ts +22 -14
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +42 -14
- package/src/runtime/auth/route-policy.ts +8 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-types.ts +19 -0
- 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__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
- 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 +121 -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 +40 -35
- package/src/runtime/routes/conversation-routes.ts +90 -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 +6 -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 +65 -21
- 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/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 +57 -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 +15 -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/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 +72 -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/registry.ts +8 -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/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
|
@@ -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
|
}
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
import { setServiceField } from "../../config/raw-config-utils.js";
|
|
8
8
|
import { providerForImageModelPrefix } from "../../media/types.js";
|
|
9
9
|
import type { ProviderCatalogEntry } from "../../providers/model-catalog.js";
|
|
10
|
-
import { PROVIDER_CATALOG } from "../../providers/model-catalog.js";
|
|
11
10
|
import { getConfiguredProviders } from "../../providers/provider-availability.js";
|
|
11
|
+
import { getVisibleProviderCatalog } from "../../providers/provider-catalog-visibility.js";
|
|
12
12
|
import { CONFIG_RELOAD_DEBOUNCE_MS, log } from "./shared.js";
|
|
13
13
|
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
@@ -65,15 +65,16 @@ export async function getModelInfo(): Promise<ModelInfo> {
|
|
|
65
65
|
const config = getConfig();
|
|
66
66
|
const resolved = resolveCallSiteConfig("mainAgent", config.llm);
|
|
67
67
|
const provider = resolved.provider;
|
|
68
|
+
const visibleCatalog = getVisibleProviderCatalog(config);
|
|
68
69
|
|
|
69
70
|
return {
|
|
70
71
|
model: resolved.model,
|
|
71
72
|
provider,
|
|
72
73
|
configuredProviders: await getConfiguredProviders(),
|
|
73
|
-
availableModels:
|
|
74
|
-
(p) => p.id === provider
|
|
75
|
-
|
|
76
|
-
allProviders:
|
|
74
|
+
availableModels: visibleCatalog
|
|
75
|
+
.find((p) => p.id === provider)
|
|
76
|
+
?.models?.map((m) => ({ id: m.id, displayName: m.displayName })),
|
|
77
|
+
allProviders: visibleCatalog.map(projectProviderForWire),
|
|
77
78
|
};
|
|
78
79
|
}
|
|
79
80
|
|
|
@@ -34,6 +34,7 @@ export interface SlackChannelConfigResult {
|
|
|
34
34
|
connected: boolean;
|
|
35
35
|
teamId?: string;
|
|
36
36
|
teamName?: string;
|
|
37
|
+
teamUrl?: string;
|
|
37
38
|
botUserId?: string;
|
|
38
39
|
botUsername?: string;
|
|
39
40
|
error?: string;
|
|
@@ -93,7 +94,8 @@ export function backfillSlackInjectionTemplates(): void {
|
|
|
93
94
|
// -- Business logic --
|
|
94
95
|
|
|
95
96
|
export async function getSlackChannelConfig(): Promise<SlackChannelConfigResult> {
|
|
96
|
-
const { teamId, teamName, botUserId, botUsername } =
|
|
97
|
+
const { teamId, teamName, teamUrl, botUserId, botUsername } =
|
|
98
|
+
getConfig().slack;
|
|
97
99
|
const accountInfo = teamName
|
|
98
100
|
? `${teamName}${botUsername ? ` (@${botUsername})` : ""}`
|
|
99
101
|
: undefined;
|
|
@@ -129,6 +131,7 @@ export async function getSlackChannelConfig(): Promise<SlackChannelConfigResult>
|
|
|
129
131
|
connected,
|
|
130
132
|
...(teamId ? { teamId } : {}),
|
|
131
133
|
...(teamName ? { teamName } : {}),
|
|
134
|
+
...(teamUrl ? { teamUrl } : {}),
|
|
132
135
|
...(botUserId ? { botUserId } : {}),
|
|
133
136
|
...(botUsername ? { botUsername } : {}),
|
|
134
137
|
};
|
|
@@ -169,6 +172,7 @@ export async function setSlackChannelConfig(
|
|
|
169
172
|
let metadata: {
|
|
170
173
|
teamId?: string;
|
|
171
174
|
teamName?: string;
|
|
175
|
+
teamUrl?: string;
|
|
172
176
|
botUserId?: string;
|
|
173
177
|
botUsername?: string;
|
|
174
178
|
} = {};
|
|
@@ -187,6 +191,7 @@ export async function setSlackChannelConfig(
|
|
|
187
191
|
error?: string;
|
|
188
192
|
team_id?: string;
|
|
189
193
|
team?: string;
|
|
194
|
+
url?: string;
|
|
190
195
|
user_id?: string;
|
|
191
196
|
user?: string;
|
|
192
197
|
};
|
|
@@ -198,6 +203,7 @@ export async function setSlackChannelConfig(
|
|
|
198
203
|
metadata = {
|
|
199
204
|
teamId: data.team_id,
|
|
200
205
|
teamName: data.team,
|
|
206
|
+
teamUrl: data.url,
|
|
201
207
|
botUserId: data.user_id,
|
|
202
208
|
botUsername: data.user,
|
|
203
209
|
};
|
|
@@ -221,6 +227,7 @@ export async function setSlackChannelConfig(
|
|
|
221
227
|
const raw = loadRawConfig();
|
|
222
228
|
setNestedValue(raw, "slack.teamId", metadata.teamId ?? "");
|
|
223
229
|
setNestedValue(raw, "slack.teamName", metadata.teamName ?? "");
|
|
230
|
+
setNestedValue(raw, "slack.teamUrl", metadata.teamUrl ?? "");
|
|
224
231
|
setNestedValue(raw, "slack.botUserId", metadata.botUserId ?? "");
|
|
225
232
|
setNestedValue(raw, "slack.botUsername", metadata.botUsername ?? "");
|
|
226
233
|
saveRawConfig(raw);
|
|
@@ -293,10 +300,12 @@ export async function setSlackChannelConfig(
|
|
|
293
300
|
}
|
|
294
301
|
} else {
|
|
295
302
|
// Use existing metadata from config if no new bot token provided
|
|
296
|
-
const { teamId, teamName, botUserId, botUsername } =
|
|
303
|
+
const { teamId, teamName, teamUrl, botUserId, botUsername } =
|
|
304
|
+
getConfig().slack;
|
|
297
305
|
metadata = {
|
|
298
306
|
...(teamId ? { teamId } : {}),
|
|
299
307
|
...(teamName ? { teamName } : {}),
|
|
308
|
+
...(teamUrl ? { teamUrl } : {}),
|
|
300
309
|
...(botUserId ? { botUserId } : {}),
|
|
301
310
|
...(botUsername ? { botUsername } : {}),
|
|
302
311
|
};
|
|
@@ -468,7 +477,8 @@ export async function clearSlackUserToken(): Promise<SlackChannelConfigResult> {
|
|
|
468
477
|
credentialKey("slack_channel", "app_token"),
|
|
469
478
|
));
|
|
470
479
|
const conn = getConnectionByProvider("slack_channel");
|
|
471
|
-
const { teamId, teamName, botUserId, botUsername } =
|
|
480
|
+
const { teamId, teamName, teamUrl, botUserId, botUsername } =
|
|
481
|
+
getConfig().slack;
|
|
472
482
|
|
|
473
483
|
return {
|
|
474
484
|
success: true,
|
|
@@ -479,6 +489,7 @@ export async function clearSlackUserToken(): Promise<SlackChannelConfigResult> {
|
|
|
479
489
|
!!(conn && conn.status === "active") && hasBotToken && hasAppToken,
|
|
480
490
|
...(teamId ? { teamId } : {}),
|
|
481
491
|
...(teamName ? { teamName } : {}),
|
|
492
|
+
...(teamUrl ? { teamUrl } : {}),
|
|
482
493
|
...(botUserId ? { botUserId } : {}),
|
|
483
494
|
...(botUsername ? { botUsername } : {}),
|
|
484
495
|
};
|
|
@@ -528,6 +539,7 @@ export async function clearSlackChannelConfig(): Promise<SlackChannelConfigResul
|
|
|
528
539
|
const raw = loadRawConfig();
|
|
529
540
|
setNestedValue(raw, "slack.teamId", "");
|
|
530
541
|
setNestedValue(raw, "slack.teamName", "");
|
|
542
|
+
setNestedValue(raw, "slack.teamUrl", "");
|
|
531
543
|
setNestedValue(raw, "slack.botUserId", "");
|
|
532
544
|
setNestedValue(raw, "slack.botUsername", "");
|
|
533
545
|
saveRawConfig(raw);
|