@vellumai/assistant 0.10.2 → 0.10.3-dev.202606252046.9075fd5
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/bun.lock +20 -0
- package/docs/workspace-tools.md +42 -33
- package/eslint-rules/cli-no-daemon-internals.js +6 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +44 -0
- package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +28 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +41 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/openapi.yaml +196 -56
- package/package.json +4 -1
- package/scripts/test.sh +36 -15
- package/src/__tests__/actor-token-service.test.ts +36 -14
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -0
- package/src/__tests__/approval-cascade.test.ts +2 -0
- package/src/__tests__/assistant-attachments.test.ts +42 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
- package/src/__tests__/btw-routes.test.ts +2 -0
- package/src/__tests__/build-persisted-content.test.ts +2 -0
- package/src/__tests__/call-controller.test.ts +19 -0
- package/src/__tests__/channel-approval-routes.test.ts +21 -26
- package/src/__tests__/channel-delivery-store.test.ts +28 -0
- package/src/__tests__/channel-guardian.test.ts +164 -78
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
- package/src/__tests__/channel-reply-delivery.test.ts +2 -0
- package/src/__tests__/code-search-tool.test.ts +585 -0
- package/src/__tests__/compaction-events.test.ts +2 -0
- package/src/__tests__/compaction-ledger-store.test.ts +128 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
- package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
- package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
- package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
- package/src/__tests__/config-loader-backfill.test.ts +123 -10
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
- package/src/__tests__/consult-deadline.test.ts +60 -0
- package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
- package/src/__tests__/contact-store-user-file.test.ts +7 -10
- package/src/__tests__/contacts-relay-reads.test.ts +19 -24
- package/src/__tests__/contacts-write.test.ts +0 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop.test.ts +134 -0
- package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +9 -11
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
- package/src/__tests__/conversation-fork-crud.test.ts +354 -24
- package/src/__tests__/conversation-history-web-search.test.ts +2 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
- package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
- package/src/__tests__/conversation-pairing.test.ts +2 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
- package/src/__tests__/conversation-process-callsite.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +91 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
- package/src/__tests__/conversation-slash-queue.test.ts +2 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-speed-override.test.ts +2 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +29 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
- package/src/__tests__/db-migration-rollback.test.ts +205 -171
- package/src/__tests__/db-test-helpers.ts +5 -4
- package/src/__tests__/delete-propagation.test.ts +5 -3
- package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
- package/src/__tests__/disk-pressure-guard.test.ts +41 -0
- package/src/__tests__/dm-backfill.test.ts +6 -4
- package/src/__tests__/dm-persistence.test.ts +2 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +7 -6
- package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
- package/src/__tests__/exploration-drift-hook.test.ts +3 -2
- package/src/__tests__/filing-service.test.ts +2 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +104 -19
- package/src/__tests__/guardian-dispatch.test.ts +140 -1
- package/src/__tests__/guardian-expiry-notifier.test.ts +282 -0
- package/src/__tests__/guardian-outbound-http.test.ts +13 -0
- package/src/__tests__/guardian-routing-state.test.ts +6 -10
- package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
- package/src/__tests__/heartbeat-service.test.ts +2 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +46 -19
- package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
- package/src/__tests__/helpers/mock-logger.ts +1 -0
- package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
- package/src/__tests__/host-app-control-routes.test.ts +24 -30
- package/src/__tests__/host-bash-routes.test.ts +31 -41
- package/src/__tests__/host-browser-routes.test.ts +26 -32
- package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
- package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
- package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
- package/src/__tests__/http-user-message-parity.test.ts +290 -8
- package/src/__tests__/inbound-invite-redemption.test.ts +115 -10
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/invite-redemption-service.test.ts +471 -53
- package/src/__tests__/invite-routes-http.test.ts +34 -0
- package/src/__tests__/invite-service-ipc.test.ts +65 -2
- package/src/__tests__/llm-context-normalization.test.ts +105 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +71 -9
- package/src/__tests__/llm-usage-store.test.ts +25 -0
- package/src/__tests__/mcp-config-secret-boundary.test.ts +392 -0
- package/src/__tests__/mcp-health-check.test.ts +2 -1
- package/src/__tests__/media-stream-server-integration.test.ts +127 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
- package/src/__tests__/messaging-send-tool.test.ts +2 -0
- package/src/__tests__/migration-import-from-url.test.ts +2 -2
- package/src/__tests__/mtime-cache.test.ts +275 -5
- package/src/__tests__/native-web-search.test.ts +2 -0
- package/src/__tests__/non-member-access-request.test.ts +191 -17
- package/src/__tests__/notification-broadcaster.test.ts +4 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
- package/src/__tests__/notification-deep-link.test.ts +6 -0
- package/src/__tests__/notification-guardian-path.test.ts +19 -0
- package/src/__tests__/onboarding-persona-write.test.ts +52 -22
- package/src/__tests__/openai-provider.test.ts +22 -12
- package/src/__tests__/openai-responses-provider.test.ts +12 -2
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
- package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/persona-resolver.test.ts +75 -45
- package/src/__tests__/plugin-bootstrap.test.ts +21 -83
- package/src/__tests__/plugin-disabled-state.test.ts +190 -0
- package/src/__tests__/plugin-pipeline.test.ts +96 -0
- package/src/__tests__/plugin-route-contribution.test.ts +6 -19
- package/src/__tests__/plugin-tool-contribution.test.ts +5 -20
- package/src/__tests__/plugin-types.test.ts +2 -4
- package/src/__tests__/process-message-background-slack.test.ts +2 -0
- package/src/__tests__/process-message-display-content.test.ts +2 -0
- package/src/__tests__/provider-error-scenarios.test.ts +5 -4
- package/src/__tests__/provider-usage-tracking.test.ts +39 -0
- package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
- package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
- package/src/__tests__/reaction-persistence.test.ts +51 -4
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
- package/src/__tests__/registry.test.ts +4 -1
- package/src/__tests__/relay-server.test.ts +758 -32
- package/src/__tests__/runtime-attachment-metadata.test.ts +9 -12
- package/src/__tests__/secret-ingress-http.test.ts +14 -0
- package/src/__tests__/send-endpoint-busy.test.ts +30 -8
- package/src/__tests__/settings-routes.test.ts +32 -0
- package/src/__tests__/skills.test.ts +44 -0
- package/src/__tests__/slack-inbound-verification.test.ts +47 -2
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +79 -0
- package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
- package/src/__tests__/stt-hints.test.ts +49 -15
- package/src/__tests__/subagent-detail.test.ts +27 -0
- package/src/__tests__/subagent-disposal.test.ts +65 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-role-registry.test.ts +24 -6
- package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
- package/src/__tests__/subagent-tools.test.ts +398 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/thread-backfill.test.ts +3 -3
- package/src/__tests__/title-generate-hook.test.ts +2 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
- package/src/__tests__/tool-executor.test.ts +16 -11
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
- package/src/__tests__/tool-start-timestamp.test.ts +2 -0
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +37 -51
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +12 -12
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
- package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
- package/src/__tests__/trusted-contact-verification.test.ts +79 -54
- package/src/__tests__/twilio-routes.test.ts +96 -0
- package/src/__tests__/ui-file-upload-surface.test.ts +86 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
- package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
- package/src/__tests__/voice-invite-redemption.test.ts +216 -20
- package/src/__tests__/web-search-backend-failure.test.ts +2 -0
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
- package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +208 -0
- package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +14 -35
- package/src/__tests__/workspace-tool-loader.test.ts +195 -2
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
- package/src/agent/loop-exclusive-tool.test.ts +154 -0
- package/src/agent/loop-native-web-search.test.ts +200 -0
- package/src/agent/loop.ts +164 -1
- package/src/api/constants/sse-replay.ts +41 -0
- package/src/api/events/conversation-notice.ts +26 -0
- package/src/api/index.ts +19 -1
- package/src/api/responses/llm-request-log-entry.ts +29 -0
- package/src/api/responses/subagent-detail.ts +17 -0
- package/src/api/surfaces.ts +39 -3
- package/src/approvals/guardian-channel-delivery.ts +30 -0
- package/src/approvals/guardian-expiry-notifier.ts +148 -0
- package/src/approvals/guardian-request-resolvers.ts +17 -15
- package/src/calls/__tests__/relay-setup-router.test.ts +267 -17
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +14 -9
- package/src/calls/inbound-trust-reader.ts +24 -2
- package/src/calls/media-stream-server.ts +21 -0
- package/src/calls/relay-access-wait.ts +6 -6
- package/src/calls/relay-server.ts +188 -51
- package/src/calls/relay-setup-router.ts +47 -17
- package/src/calls/relay-verification.ts +4 -4
- package/src/calls/stt-hints.ts +9 -12
- package/src/calls/twilio-routes.ts +14 -4
- package/src/channels/types.ts +10 -20
- package/src/cli/commands/__tests__/cache.test.ts +8 -1
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
- package/src/cli/commands/cache.ts +194 -181
- package/src/cli/commands/contacts.ts +10 -7
- package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
- package/src/cli/commands/db/status.ts +37 -1
- package/src/cli/commands/mcp.ts +252 -218
- package/src/cli/commands/memory/__tests__/worker.test.ts +432 -0
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/worker.ts +242 -0
- package/src/cli/commands/plugins.ts +125 -13
- package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
- package/src/cli/lib/__tests__/publish-plugin.test.ts +404 -0
- package/src/cli/lib/list-installed-plugins.ts +179 -1
- package/src/cli/lib/publish-plugin.ts +633 -0
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
- package/src/config/__tests__/sync-gated-profiles.test.ts +16 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -2
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
- package/src/config/call-site-defaults.ts +0 -6
- package/src/config/feature-flag-registry.json +5 -13
- package/src/config/llm-resolver.ts +0 -3
- package/src/config/loader.ts +38 -5
- package/src/config/prune-seeded-callsite-defaults.ts +110 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +0 -7
- package/src/config/schemas/heartbeat.ts +2 -5
- package/src/config/schemas/llm.ts +3 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/memory-v3.ts +7 -0
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/timeouts.ts +8 -0
- package/src/config/seed-inference-profiles.ts +30 -34
- package/src/config/skills.ts +27 -5
- package/src/config/sync-gated-profiles.ts +12 -16
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +128 -0
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
- package/src/contacts/__tests__/member-write-relay.test.ts +226 -0
- package/src/contacts/contact-store.ts +27 -237
- package/src/contacts/contacts-write.ts +18 -55
- package/src/contacts/gateway-channel-read.ts +51 -0
- package/src/contacts/guardian-delivery-reader.ts +223 -0
- package/src/contacts/member-write-relay.ts +183 -0
- package/src/contacts/types.ts +3 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +35 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
- package/src/daemon/assistant-attachments.ts +27 -4
- package/src/daemon/conversation-agent-loop.ts +28 -0
- package/src/daemon/conversation-notices.ts +60 -0
- package/src/daemon/conversation-process.ts +35 -16
- package/src/daemon/conversation-surfaces.ts +111 -38
- package/src/daemon/conversation-tool-setup.ts +71 -30
- package/src/daemon/conversation.ts +23 -1
- package/src/daemon/disk-pressure-guard.ts +12 -2
- package/src/daemon/event-loop-watchdog.ts +28 -1
- package/src/daemon/external-plugins-bootstrap.ts +17 -41
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -2
- package/src/daemon/handlers/__tests__/config-channels.test.ts +220 -0
- package/src/daemon/handlers/config-a2a.ts +6 -14
- package/src/daemon/handlers/config-channels.ts +67 -26
- package/src/daemon/handlers/conversations.ts +77 -0
- package/src/daemon/lifecycle.ts +16 -0
- package/src/daemon/mcp-reload-service.ts +10 -0
- package/src/daemon/memory-v2-startup.test.ts +72 -0
- package/src/daemon/memory-v2-startup.ts +87 -19
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +12 -12
- package/src/daemon/server.ts +0 -4
- package/src/daemon/shutdown-handlers.ts +20 -0
- package/src/daemon/tool-setup-types.ts +9 -0
- package/src/heartbeat/heartbeat-service.ts +5 -0
- package/src/home/relationship-state-writer.ts +5 -0
- package/src/hooks/hook-loader.ts +341 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -0
- package/src/mcp/client.ts +15 -1
- package/src/mcp/mcp-auth-orchestrator.ts +6 -1
- package/src/mcp/mcp-header-store.ts +134 -0
- package/src/mcp/mcp-oauth-provider.ts +19 -8
- package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
- package/src/memory/__tests__/prompt-override.test.ts +192 -0
- package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
- package/src/memory/compaction-ledger-store.ts +107 -0
- package/src/memory/conversation-crud.ts +129 -61
- package/src/memory/db-connection.ts +22 -3
- package/src/memory/db-init.ts +37 -503
- package/src/memory/db-singleton.ts +6 -4
- package/src/memory/jobs-worker.ts +104 -0
- package/src/memory/llm-request-log-store.ts +26 -1
- package/src/memory/llm-usage-store.ts +48 -20
- package/src/memory/memory-retrospective-job.ts +14 -8
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +152 -56
- package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
- package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
- package/src/memory/migrations/302-create-compaction-events.ts +107 -0
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +297 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/run-migrations.ts +106 -10
- package/src/memory/migrations/validate-migration-state.ts +105 -67
- package/src/memory/prompt-override.ts +129 -0
- package/src/memory/schema/contacts.ts +6 -2
- package/src/memory/schema/conversations.ts +37 -0
- package/src/memory/schema/infrastructure.ts +20 -0
- package/src/memory/steps.ts +1294 -0
- package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
- package/src/memory/v2/cli-command-store.ts +75 -38
- package/src/memory/v2/prompts/consolidation.ts +13 -82
- package/src/memory/v2/prompts/router.ts +21 -93
- package/src/memory/v2/skill-store.ts +68 -31
- package/src/memory/watchdog-events-store.ts +87 -0
- package/src/memory/worker-control.ts +204 -0
- package/src/memory/worker-process.ts +89 -0
- package/src/messaging/channel-binding-metadata.ts +31 -0
- package/src/messaging/provider-types.ts +8 -0
- package/src/messaging/providers/slack/binding-metadata.ts +60 -0
- package/src/notifications/__tests__/broadcaster.test.ts +8 -8
- package/src/notifications/__tests__/connected-channels.test.ts +86 -0
- package/src/notifications/__tests__/decision-engine.test.ts +78 -9
- package/src/notifications/__tests__/destination-resolver.test.ts +151 -0
- package/src/notifications/broadcaster.ts +8 -1
- package/src/notifications/decision-engine.ts +15 -7
- package/src/notifications/destination-resolver.ts +53 -25
- package/src/notifications/emit-signal.ts +34 -15
- package/src/onboarding/checkin-event.test.ts +222 -0
- package/src/onboarding/checkin-event.ts +321 -0
- package/src/onboarding/schedule-checkin.ts +190 -0
- package/src/permissions/question-prompter.test.ts +1 -1
- package/src/permissions/question-prompter.ts +7 -4
- package/src/plugin-api/index.ts +12 -12
- package/src/plugin-api/types.ts +12 -14
- package/src/plugin-api/vision-support.test.ts +28 -4
- package/src/plugin-api/vision-support.ts +66 -31
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/empty-response/hooks/stop.ts +2 -2
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +4 -3
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/stop.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +12 -13
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +14 -22
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +2 -2
- package/src/plugins/defaults/index.ts +0 -35
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +33 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +48 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +4 -8
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +43 -15
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
- package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +77 -13
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +12 -11
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +2 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/stop.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +2 -2
- package/src/plugins/disabled-state.ts +31 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/mtime-cache.ts +186 -330
- package/src/plugins/pipeline.ts +111 -13
- package/src/plugins/registry.ts +59 -16
- package/src/plugins/surface-import.ts +121 -0
- package/src/plugins/types.ts +7 -9
- package/src/prompts/persona-resolver.ts +43 -11
- package/src/providers/anthropic/client.ts +5 -0
- package/src/providers/call-site-routing.ts +45 -0
- package/src/providers/model-catalog.ts +16 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +321 -0
- package/src/providers/openai/api-error-normalization.ts +270 -0
- package/src/providers/openai/chat-completions-provider.ts +37 -83
- package/src/providers/openai/responses-provider.ts +50 -46
- package/src/providers/openrouter/client.ts +5 -0
- package/src/providers/provider-send-message.ts +10 -0
- package/src/providers/ratelimit.ts +10 -0
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +10 -0
- package/src/providers/types.ts +22 -0
- package/src/providers/usage-tracking.ts +10 -0
- package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +184 -0
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
- package/src/runtime/__tests__/local-principal-trust.test.ts +162 -0
- package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +402 -123
- package/src/runtime/access-request-helper.ts +18 -39
- package/src/runtime/actor-trust-resolver.ts +46 -19
- package/src/runtime/anchored-guardian.test.ts +109 -0
- package/src/runtime/anchored-guardian.ts +86 -0
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/assistant-stream-state.ts +9 -2
- package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
- package/src/runtime/auth/require-bound-guardian.ts +21 -11
- package/src/runtime/channel-verification-service.ts +56 -31
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
- package/src/runtime/guardian-vellum-migration.ts +69 -8
- package/src/runtime/invite-redemption-service.ts +213 -187
- package/src/runtime/local-actor-identity.test.ts +108 -0
- package/src/runtime/local-actor-identity.ts +85 -13
- package/src/runtime/local-principal-trust.ts +52 -0
- package/src/runtime/member-verdict-cache.ts +0 -0
- package/src/runtime/pending-interactions.ts +11 -1
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/contact-routes.test.ts +305 -0
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +92 -0
- package/src/runtime/routes/__tests__/http-adapter-actor-header.test.ts +129 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +188 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +13 -5
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +48 -57
- package/src/runtime/routes/conversation-cli-routes.ts +4 -5
- package/src/runtime/routes/conversation-list-routes.ts +4 -7
- package/src/runtime/routes/conversation-query-routes.ts +72 -0
- package/src/runtime/routes/conversation-routes.ts +84 -85
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/global-search-routes.ts +3 -1
- package/src/runtime/routes/guardian-action-routes.ts +4 -5
- package/src/runtime/routes/host-app-control-routes.ts +5 -4
- package/src/runtime/routes/host-bash-routes.ts +5 -4
- package/src/runtime/routes/host-browser-routes.ts +9 -11
- package/src/runtime/routes/host-cu-routes.ts +5 -4
- package/src/runtime/routes/host-file-routes.ts +5 -4
- package/src/runtime/routes/host-transfer-routes.ts +6 -6
- package/src/runtime/routes/http-adapter.ts +16 -1
- package/src/runtime/routes/identity-routes.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -59
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +13 -5
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +19 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/llm-context-normalization.ts +83 -0
- package/src/runtime/routes/mcp-auth-routes.ts +171 -19
- package/src/runtime/routes/migration-rollback-routes.ts +4 -3
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
- package/src/runtime/routes/settings-routes.ts +8 -3
- package/src/runtime/routes/subagents-routes.ts +5 -0
- package/src/runtime/routes/surface-action-routes.ts +42 -56
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +13 -58
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +111 -40
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +16 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
- package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
- package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +22 -32
- package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
- package/src/subagent/index.ts +1 -1
- package/src/subagent/manager.ts +254 -33
- package/src/subagent/types.ts +11 -4
- package/src/telemetry/types.ts +34 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +87 -3
- package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
- package/src/tools/ask-question/ask-question-tool.ts +13 -0
- package/src/tools/executor.ts +4 -4
- package/src/tools/filesystem/search.ts +543 -0
- package/src/tools/registry.ts +38 -0
- package/src/tools/shared/filesystem/path-policy.ts +12 -5
- package/src/tools/subagent/consult-deadline.ts +49 -0
- package/src/tools/subagent/spawn.ts +234 -5
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +9 -2
- package/src/tools/tool-manifest.ts +3 -0
- package/src/tools/types.ts +131 -25
- package/src/tools/workspace-tools/loader.ts +348 -244
- package/src/util/errors.ts +26 -1
- package/src/util/logger.ts +9 -0
- package/src/util/platform.ts +19 -0
- package/src/util/telemetry-db-path.ts +24 -0
- package/src/workflows/library.test.ts +140 -0
- package/src/workflows/library.ts +82 -28
- package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
- package/src/workspace/migrations/031-drop-user-md.ts +232 -148
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +14 -66
- package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +134 -0
- package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
- package/src/daemon/workspace-tools-watcher.ts +0 -328
- package/src/memory/migrations/registry.ts +0 -573
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -153
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
- package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
- package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
- package/src/plugins/defaults/advisor/config.ts +0 -21
- package/src/plugins/defaults/advisor/consult.ts +0 -93
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
- package/src/plugins/defaults/advisor/package.json +0 -14
- package/src/plugins/defaults/advisor/tools/advisor.ts +0 -65
- package/src/providers/openai/__tests__/api-error-detail.test.ts +0 -120
|
@@ -1,34 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Per-surface mtime cache for user plugins.
|
|
2
|
+
* Per-surface mtime cache for user plugins (discovery + tools).
|
|
3
3
|
*
|
|
4
|
-
* Instead of caching whole `Plugin` objects,
|
|
5
|
-
*
|
|
4
|
+
* Instead of caching whole `Plugin` objects, the user-plugin system caches
|
|
5
|
+
* individual surfaces keyed by their source file's mtime. This module owns
|
|
6
|
+
* plugin **discovery** (which plugin directories exist, in what order) and the
|
|
7
|
+
* **tool** cache; the **hook** cache and every hook operation live in
|
|
8
|
+
* `../hooks/hook-loader.ts`. This module is the boot orchestrator: it scans the
|
|
9
|
+
* plugins directory, registers tools, and drives hook pre-import / init /
|
|
10
|
+
* shutdown by handing the discovered directories to the hook loader.
|
|
6
11
|
*
|
|
7
|
-
* - A changed
|
|
8
|
-
* plugin rebuild.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* and hooks into the global registries, then cache-bust them using mtime
|
|
13
|
-
* on reads.
|
|
12
|
+
* - A changed tool file triggers a re-import of just that tool, not a full
|
|
13
|
+
* plugin rebuild. Added and removed surface files are picked up live, since
|
|
14
|
+
* discovery is by directory listing.
|
|
15
|
+
* - Plugins are never "registered" as a unit — we register their tools into
|
|
16
|
+
* the global tool registry and cache-bust them using mtime on reads.
|
|
14
17
|
*
|
|
15
18
|
* The cache is populated at boot by `loadUserPlugins()` and read on every
|
|
16
|
-
* `getHooksFor` / `getAllTools` call.
|
|
17
|
-
* the next read detects the mismatch, re-imports the file, and swaps the
|
|
18
|
-
* cached entry.
|
|
19
|
+
* `getHooksFor` / `getAllTools` call.
|
|
19
20
|
*/
|
|
20
21
|
|
|
21
|
-
import { existsSync,
|
|
22
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
22
23
|
import { join } from "node:path";
|
|
23
24
|
|
|
24
|
-
import { getConfig } from "../config/loader.js";
|
|
25
25
|
import { registerShutdownHook } from "../daemon/shutdown-registry.js";
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
import {
|
|
27
|
+
clearPluginHooks,
|
|
28
|
+
collectUserHooks,
|
|
29
|
+
evictHooksForOwner,
|
|
30
|
+
hasWorkspaceHooks,
|
|
31
|
+
preImportHooksDir,
|
|
32
|
+
preImportWorkspaceHooks,
|
|
33
|
+
resetHookCacheForTests,
|
|
34
|
+
runInitHook,
|
|
35
|
+
runShutdownHook,
|
|
36
|
+
WORKSPACE_HOOKS_OWNER,
|
|
37
|
+
} from "../hooks/hook-loader.js";
|
|
38
|
+
import type { HookFunction, ShutdownContext } from "../plugin-api/types.js";
|
|
32
39
|
import {
|
|
33
40
|
registerPluginTools,
|
|
34
41
|
unregisterPluginTools,
|
|
@@ -36,28 +43,26 @@ import {
|
|
|
36
43
|
import { finalizeTool } from "../tools/tool-defaults.js";
|
|
37
44
|
import type { Tool, ToolDefinition } from "../tools/types.js";
|
|
38
45
|
import { getLogger } from "../util/logger.js";
|
|
39
|
-
import {
|
|
46
|
+
import { getWorkspacePluginsDir } from "../util/platform.js";
|
|
40
47
|
import { APP_VERSION } from "../version.js";
|
|
41
48
|
import {
|
|
42
49
|
deriveToolName,
|
|
43
|
-
importDefault,
|
|
44
50
|
listSurfaceDir,
|
|
45
51
|
parsePluginManifest,
|
|
46
52
|
} from "./external-plugin-loader.js";
|
|
53
|
+
import {
|
|
54
|
+
clearSurfaceImportInflight,
|
|
55
|
+
getMtime,
|
|
56
|
+
importWithTimeout,
|
|
57
|
+
setSurfaceImportTimeout,
|
|
58
|
+
} from "./surface-import.js";
|
|
47
59
|
|
|
48
|
-
// Re-export for type compat — consumers that import
|
|
60
|
+
// Re-export for type compat — consumers that import HookFunction from
|
|
49
61
|
// the mtime cache module still resolve.
|
|
50
|
-
export type {
|
|
62
|
+
export type { HookFunction } from "./types.js";
|
|
51
63
|
|
|
52
64
|
const log = getLogger("plugin-mtime-cache");
|
|
53
65
|
|
|
54
|
-
/**
|
|
55
|
-
* Import timeout for surface file imports. Set by `populateCacheAtBoot` from
|
|
56
|
-
* the value passed by `loadUserPlugins`, and used by `getUserHooksFor` and
|
|
57
|
-
* `reconcilePluginTools` for runtime re-imports. Defaults to 10s.
|
|
58
|
-
*/
|
|
59
|
-
let importTimeoutMs = 10_000;
|
|
60
|
-
|
|
61
66
|
/**
|
|
62
67
|
* Cached install-date timestamps per plugin directory, so `scanPlugins`
|
|
63
68
|
* doesn't re-read `install-meta.json` on every turn. Populated on first
|
|
@@ -130,16 +135,6 @@ function getInstallDate(pluginDir: string): number {
|
|
|
130
135
|
|
|
131
136
|
// ─── Cache entries ───────────────────────────────────────────────────────────
|
|
132
137
|
|
|
133
|
-
/**
|
|
134
|
-
* A cached hook function plus the mtime of its source file. When the on-disk
|
|
135
|
-
* mtime changes, the hook is re-imported and the entry is replaced.
|
|
136
|
-
*/
|
|
137
|
-
interface CachedHook {
|
|
138
|
-
readonly hook: PluginHookFn;
|
|
139
|
-
/** mtimeMs of the source file this hook was imported from. */
|
|
140
|
-
readonly sourceMtime: number;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
138
|
/**
|
|
144
139
|
* A cached tool plus the mtime of its source file. When the on-disk mtime
|
|
145
140
|
* changes, the tool is re-imported, the old tool is unregistered from the
|
|
@@ -155,25 +150,12 @@ interface CachedTool {
|
|
|
155
150
|
|
|
156
151
|
// ─── Internal state ──────────────────────────────────────────────────────────
|
|
157
152
|
|
|
158
|
-
/**
|
|
159
|
-
* Cached hooks keyed by `${pluginName}/${hookName}`. The key includes the
|
|
160
|
-
* plugin name so hooks from different plugins don't collide.
|
|
161
|
-
*/
|
|
162
|
-
const hookCache = new Map<string, CachedHook>();
|
|
163
|
-
|
|
164
153
|
/**
|
|
165
154
|
* Cached tools keyed by `${pluginName}/${toolName}`. The key includes the
|
|
166
155
|
* plugin name so tools from different plugins don't collide.
|
|
167
156
|
*/
|
|
168
157
|
const toolCache = new Map<string, CachedTool>();
|
|
169
158
|
|
|
170
|
-
/**
|
|
171
|
-
* In-flight import promises, keyed by file path. Prevents duplicate
|
|
172
|
-
* `import()` calls when multiple readers request the same surface
|
|
173
|
-
* concurrently.
|
|
174
|
-
*/
|
|
175
|
-
const inflight = new Map<string, Promise<unknown>>();
|
|
176
|
-
|
|
177
159
|
/**
|
|
178
160
|
* Plugin directories discovered at boot, in discovery order. Maps directory
|
|
179
161
|
* path to the plugin's scope-stripped manifest name so eviction can find
|
|
@@ -189,94 +171,19 @@ const discoveredPluginDirs = new Map<string, string>();
|
|
|
189
171
|
*/
|
|
190
172
|
const disabledPluginDirs = new Set<string>();
|
|
191
173
|
|
|
192
|
-
// ───
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Get the mtimeMs of a file, or 0 if the file doesn't exist or can't be
|
|
196
|
-
* stat'd.
|
|
197
|
-
*/
|
|
198
|
-
function getMtime(filePath: string): number {
|
|
199
|
-
try {
|
|
200
|
-
return statSync(filePath).mtimeMs;
|
|
201
|
-
} catch {
|
|
202
|
-
return 0;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// ─── Hook cache ──────────────────────────────────────────────────────────────
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Cache key for a hook: `${pluginName}/${hookName}`.
|
|
210
|
-
*/
|
|
211
|
-
function hookKey(pluginName: string, hookName: string): string {
|
|
212
|
-
return `${pluginName}/${hookName}`;
|
|
213
|
-
}
|
|
174
|
+
// ─── Hook reads ──────────────────────────────────────────────────────────────
|
|
214
175
|
|
|
215
176
|
/**
|
|
216
|
-
* Get all hooks for a given event name from user plugins
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
* Deleted plugins/hooks are skipped naturally (their directories/files
|
|
221
|
-
* no longer appear in the listing).
|
|
177
|
+
* Get all hooks for a given event name from user plugins and standalone
|
|
178
|
+
* workspace hooks. Refreshes plugin discovery first, then delegates the actual
|
|
179
|
+
* hook resolution to the hook loader. Plugin hooks run in install-date order,
|
|
180
|
+
* the workspace hook runs last.
|
|
222
181
|
*/
|
|
223
182
|
export async function getUserHooksFor<TCtx = unknown>(
|
|
224
183
|
hookName: string,
|
|
225
|
-
): Promise<
|
|
184
|
+
): Promise<HookFunction<TCtx>[]> {
|
|
226
185
|
await scanPlugins();
|
|
227
|
-
|
|
228
|
-
const out: PluginHookFn<TCtx>[] = [];
|
|
229
|
-
|
|
230
|
-
for (const [pluginDir, pluginName] of discoveredPluginDirs) {
|
|
231
|
-
const hooksDir = join(pluginDir, "hooks");
|
|
232
|
-
const surfaceFiles = listSurfaceDir(hooksDir);
|
|
233
|
-
const hookFile = surfaceFiles.find((f) => f.name === hookName);
|
|
234
|
-
if (hookFile === undefined) continue;
|
|
235
|
-
|
|
236
|
-
const key = hookKey(pluginName, hookName);
|
|
237
|
-
const currentMtime = getMtime(hookFile.path);
|
|
238
|
-
|
|
239
|
-
// Cache hit — same mtime.
|
|
240
|
-
const cached = hookCache.get(key);
|
|
241
|
-
if (
|
|
242
|
-
cached !== undefined &&
|
|
243
|
-
cached.sourceMtime === currentMtime &&
|
|
244
|
-
currentMtime > 0
|
|
245
|
-
) {
|
|
246
|
-
out.push(cached.hook as PluginHookFn<TCtx>);
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Cache miss — re-import.
|
|
251
|
-
if (currentMtime === 0) {
|
|
252
|
-
// File was deleted — evict cache entry.
|
|
253
|
-
hookCache.delete(key);
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
try {
|
|
258
|
-
const hook = await importWithTimeout<PluginHookFn>(
|
|
259
|
-
hookFile.path,
|
|
260
|
-
importTimeoutMs,
|
|
261
|
-
);
|
|
262
|
-
if (hook === undefined || typeof hook !== "function") {
|
|
263
|
-
log.error(
|
|
264
|
-
{ plugin: pluginName, hook: hookName, path: hookFile.path },
|
|
265
|
-
`hook ${hookName} default export must be a function (got ${typeof hook}) — skipping`,
|
|
266
|
-
);
|
|
267
|
-
continue;
|
|
268
|
-
}
|
|
269
|
-
hookCache.set(key, { hook, sourceMtime: currentMtime });
|
|
270
|
-
out.push(hook as PluginHookFn<TCtx>);
|
|
271
|
-
} catch (err) {
|
|
272
|
-
log.error(
|
|
273
|
-
{ err, plugin: pluginName, hook: hookName, path: hookFile.path },
|
|
274
|
-
`Failed to import hook ${hookName} from ${hookFile.path}`,
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return out;
|
|
186
|
+
return collectUserHooks<TCtx>(hookName, discoveredPluginDirs);
|
|
280
187
|
}
|
|
281
188
|
|
|
282
189
|
// ─── Tool cache ──────────────────────────────────────────────────────────────
|
|
@@ -326,10 +233,7 @@ async function reconcilePluginTools(
|
|
|
326
233
|
}
|
|
327
234
|
|
|
328
235
|
try {
|
|
329
|
-
const toolSpec = await importWithTimeout<ToolDefinition>(
|
|
330
|
-
file.path,
|
|
331
|
-
importTimeoutMs,
|
|
332
|
-
);
|
|
236
|
+
const toolSpec = await importWithTimeout<ToolDefinition>(file.path);
|
|
333
237
|
if (
|
|
334
238
|
toolSpec === undefined ||
|
|
335
239
|
toolSpec === null ||
|
|
@@ -416,6 +320,7 @@ async function scanPlugins(): Promise<void> {
|
|
|
416
320
|
const manifest = await parsePluginManifest(pluginDir);
|
|
417
321
|
const pluginName = manifest?.name ?? entry;
|
|
418
322
|
if (discoveredPluginDirs.has(pluginDir)) {
|
|
323
|
+
await deactivatePlugin(pluginName);
|
|
419
324
|
await evictPlugin(pluginDir, pluginName);
|
|
420
325
|
}
|
|
421
326
|
if (!disabledPluginDirs.has(pluginDir)) {
|
|
@@ -443,9 +348,10 @@ async function scanPlugins(): Promise<void> {
|
|
|
443
348
|
await reconcilePluginTools(pluginDir, pluginName);
|
|
444
349
|
}
|
|
445
350
|
|
|
446
|
-
//
|
|
351
|
+
// Deactivate and evict cache entries for deleted plugins.
|
|
447
352
|
for (const [pluginDir, pluginName] of discoveredPluginDirs) {
|
|
448
353
|
if (!currentDirs.has(pluginDir)) {
|
|
354
|
+
await deactivatePlugin(pluginName);
|
|
449
355
|
await evictPlugin(pluginDir, pluginName);
|
|
450
356
|
}
|
|
451
357
|
}
|
|
@@ -459,6 +365,15 @@ async function scanPlugins(): Promise<void> {
|
|
|
459
365
|
for (const [dir, name] of sorted) {
|
|
460
366
|
discoveredPluginDirs.set(dir, name);
|
|
461
367
|
}
|
|
368
|
+
|
|
369
|
+
// Activate any plugin not yet brought up. Idempotent: already-active plugins
|
|
370
|
+
// are skipped by the `activatedNames` guard, so steady-state scans (one per
|
|
371
|
+
// hook dispatch) cost only a membership check per plugin. Tools were imported
|
|
372
|
+
// into `toolCache` by `reconcilePluginTools` above, so they are ready to
|
|
373
|
+
// register here.
|
|
374
|
+
for (const [dir, name] of discoveredPluginDirs) {
|
|
375
|
+
await activatePlugin(dir, name);
|
|
376
|
+
}
|
|
462
377
|
}
|
|
463
378
|
|
|
464
379
|
/**
|
|
@@ -470,17 +385,13 @@ async function evictPlugin(
|
|
|
470
385
|
pluginDir: string,
|
|
471
386
|
pluginName: string,
|
|
472
387
|
): Promise<void> {
|
|
473
|
-
// Evict hooks.
|
|
474
|
-
|
|
475
|
-
for (const key of hookCache.keys()) {
|
|
476
|
-
if (key.startsWith(hookPrefix)) {
|
|
477
|
-
hookCache.delete(key);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
388
|
+
// Evict hooks (owned by the hook loader).
|
|
389
|
+
evictHooksForOwner(pluginName);
|
|
480
390
|
|
|
481
391
|
// Evict tools.
|
|
392
|
+
const toolPrefix = `${pluginName}/`;
|
|
482
393
|
for (const key of toolCache.keys()) {
|
|
483
|
-
if (key.startsWith(
|
|
394
|
+
if (key.startsWith(toolPrefix)) {
|
|
484
395
|
toolCache.delete(key);
|
|
485
396
|
}
|
|
486
397
|
}
|
|
@@ -494,187 +405,170 @@ async function evictPlugin(
|
|
|
494
405
|
}
|
|
495
406
|
|
|
496
407
|
/**
|
|
497
|
-
* Evict all cache entries (when the plugins directory is gone
|
|
408
|
+
* Evict all plugin-owned cache entries (when the plugins directory is gone
|
|
409
|
+
* entirely). Standalone workspace hooks are preserved by the hook loader:
|
|
410
|
+
* they live outside the plugins directory, so the absence of any plugin must
|
|
411
|
+
* not evict them.
|
|
498
412
|
*/
|
|
499
413
|
async function evictAll(): Promise<void> {
|
|
500
|
-
|
|
414
|
+
clearPluginHooks();
|
|
501
415
|
toolCache.clear();
|
|
502
416
|
discoveredPluginDirs.clear();
|
|
503
417
|
installDateCache.clear();
|
|
504
418
|
disabledPluginDirs.clear();
|
|
505
419
|
}
|
|
506
420
|
|
|
507
|
-
// ───
|
|
421
|
+
// ─── Activation lifecycle ────────────────────────────────────────────────────
|
|
508
422
|
|
|
509
423
|
/**
|
|
510
|
-
*
|
|
511
|
-
*
|
|
512
|
-
*
|
|
424
|
+
* Plugins (and the workspace-hooks pseudo-owner) fully activated (tools
|
|
425
|
+
* registered + `init` hook run) within this process, in activation order. The
|
|
426
|
+
* process shutdown hook walks this list in reverse to tear everything down.
|
|
513
427
|
*/
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
428
|
+
const activatedPlugins: Array<{ name: string }> = [];
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Names in {@link activatedPlugins}, kept as a set for O(1) membership and —
|
|
432
|
+
* critically — reserved *synchronously* at the top of `activatePlugin`. The
|
|
433
|
+
* per-turn hook dispatch reaches `scanPlugins` on every turn (sometimes
|
|
434
|
+
* concurrently), so the synchronous reservation is what prevents a second scan
|
|
435
|
+
* from double-activating a plugin while its async `init()` is still in flight.
|
|
436
|
+
*/
|
|
437
|
+
const activatedNames = new Set<string>();
|
|
438
|
+
|
|
439
|
+
const shutdownContext: ShutdownContext = {
|
|
440
|
+
assistantVersion: APP_VERSION,
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Activate a single discovered plugin: pre-import its hooks, register its tools
|
|
445
|
+
* into the global tool registry, and run its `init` hook. Idempotent — a plugin
|
|
446
|
+
* already activated (or mid-activation) is skipped. Never throws; per-surface
|
|
447
|
+
* failures are logged and the plugin still counts as activated so the shutdown
|
|
448
|
+
* hook tears down whatever came up (mirrors boot semantics).
|
|
449
|
+
*
|
|
450
|
+
* Called from `scanPlugins`, which runs both at boot and on every subsequent
|
|
451
|
+
* scan — so a plugin whose files appear at runtime (installed via the CLI or
|
|
452
|
+
* provisioned out-of-band) becomes live without a daemon restart.
|
|
453
|
+
*/
|
|
454
|
+
async function activatePlugin(
|
|
455
|
+
pluginDir: string,
|
|
456
|
+
pluginName: string,
|
|
457
|
+
): Promise<void> {
|
|
458
|
+
if (activatedNames.has(pluginName)) return;
|
|
459
|
+
// Reserve synchronously, before any await, so a re-entrant or concurrent
|
|
460
|
+
// scan observes this plugin as already handled.
|
|
461
|
+
activatedNames.add(pluginName);
|
|
462
|
+
|
|
463
|
+
// Pre-import all hooks so the first dispatch doesn't pay the import cost.
|
|
464
|
+
await preImportHooksDir(join(pluginDir, "hooks"), pluginName);
|
|
465
|
+
|
|
466
|
+
// Register this plugin's tools into the global tool registry so
|
|
467
|
+
// `getAllTools()` and `getTool()` can find them. Tools were already imported
|
|
468
|
+
// and cached by `reconcilePluginTools` during the scan.
|
|
469
|
+
const pluginTools = Array.from(toolCache.values())
|
|
470
|
+
.filter((c) => c.pluginName === pluginName)
|
|
471
|
+
.map((c) => c.tool);
|
|
472
|
+
if (pluginTools.length > 0) {
|
|
473
|
+
try {
|
|
474
|
+
registerPluginTools(pluginName, pluginTools);
|
|
475
|
+
log.info(
|
|
476
|
+
{ plugin: pluginName, count: pluginTools.length },
|
|
477
|
+
"user plugin tools registered",
|
|
478
|
+
);
|
|
479
|
+
} catch (err) {
|
|
480
|
+
log.error(
|
|
481
|
+
{ err, plugin: pluginName },
|
|
482
|
+
`Failed to register tools for user plugin ${pluginName}`,
|
|
533
483
|
);
|
|
534
|
-
return undefined;
|
|
535
484
|
}
|
|
536
|
-
return result as T;
|
|
537
|
-
} finally {
|
|
538
|
-
if (timeoutHandle !== undefined) clearTimeout(timeoutHandle);
|
|
539
485
|
}
|
|
486
|
+
|
|
487
|
+
// Run the `init` hook if present.
|
|
488
|
+
await runInitHook(pluginName);
|
|
489
|
+
|
|
490
|
+
activatedPlugins.push({ name: pluginName });
|
|
540
491
|
}
|
|
541
492
|
|
|
542
493
|
/**
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
* `
|
|
546
|
-
*
|
|
547
|
-
* Note: Bun caches `import()` by URL within a process, so the dedup is
|
|
548
|
-
* primarily about avoiding redundant async work, not about cache-busting.
|
|
494
|
+
* Deactivate a plugin whose directory was removed or disabled at runtime:
|
|
495
|
+
* unregister its tools and run its `shutdown` hook. Must run *before*
|
|
496
|
+
* `evictPlugin` clears the hook cache, since the shutdown hook is read from it.
|
|
497
|
+
* Idempotent — a plugin that was never activated is a no-op.
|
|
549
498
|
*/
|
|
550
|
-
async function
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
499
|
+
async function deactivatePlugin(pluginName: string): Promise<void> {
|
|
500
|
+
if (!activatedNames.has(pluginName)) return;
|
|
501
|
+
activatedNames.delete(pluginName);
|
|
502
|
+
const idx = activatedPlugins.findIndex((p) => p.name === pluginName);
|
|
503
|
+
if (idx >= 0) activatedPlugins.splice(idx, 1);
|
|
504
|
+
|
|
505
|
+
// Unregister tools before running shutdown so the model-visible surface is
|
|
506
|
+
// clean before teardown.
|
|
556
507
|
try {
|
|
557
|
-
|
|
558
|
-
}
|
|
559
|
-
|
|
508
|
+
unregisterPluginTools(pluginName);
|
|
509
|
+
} catch (err) {
|
|
510
|
+
log.warn(
|
|
511
|
+
{ err, plugin: pluginName },
|
|
512
|
+
"user plugin tool unregister failed (continuing)",
|
|
513
|
+
);
|
|
560
514
|
}
|
|
515
|
+
|
|
516
|
+
await runShutdownHook(pluginName, shutdownContext, "plugin-removed");
|
|
561
517
|
}
|
|
562
518
|
|
|
563
519
|
// ─── Boot population ─────────────────────────────────────────────────────────
|
|
564
520
|
|
|
565
521
|
/**
|
|
566
|
-
*
|
|
567
|
-
*
|
|
568
|
-
*
|
|
569
|
-
|
|
570
|
-
const activatedPlugins: Array<{ name: string }> = [];
|
|
571
|
-
|
|
572
|
-
/**
|
|
573
|
-
* Populate the cache at boot by scanning the plugins directory once,
|
|
574
|
-
* importing all surfaces, registering tools into the tool registry,
|
|
575
|
-
* running `init` hooks, and installing a shutdown hook.
|
|
522
|
+
* Populate the caches at boot by scanning the plugins directory once (which
|
|
523
|
+
* imports surfaces, registers tools, and runs `init` hooks via `activatePlugin`
|
|
524
|
+
* inside `scanPlugins`), activating standalone workspace hooks, and installing
|
|
525
|
+
* the process shutdown hook.
|
|
576
526
|
*
|
|
577
527
|
* This replaces the old `loadExternalPlugin` → `registerPlugin` →
|
|
578
528
|
* `bootstrapPlugins` path for user plugins. Instead of registering whole
|
|
579
|
-
* `Plugin` objects into the plugin registry, we register individual tools
|
|
580
|
-
*
|
|
581
|
-
* `shutdown` hooks are still run exactly once per boot, preserving the
|
|
582
|
-
* activation lifecycle that plugins rely on.
|
|
529
|
+
* `Plugin` objects into the plugin registry, we register individual tools into
|
|
530
|
+
* the tool registry and cache hooks by mtime.
|
|
583
531
|
*
|
|
584
|
-
* Called by `loadUserPlugins()` during daemon startup.
|
|
532
|
+
* Called by `loadUserPlugins()` during daemon startup. After boot, the same
|
|
533
|
+
* `scanPlugins` → `activatePlugin`/`deactivatePlugin` reconciliation runs on
|
|
534
|
+
* every turn via `getUserHooksFor` (plugin hook dispatch), so plugins whose
|
|
535
|
+
* files appear or disappear at runtime are picked up without a restart.
|
|
585
536
|
*/
|
|
586
537
|
export async function populateCacheAtBoot(
|
|
587
538
|
opts: { importTimeoutMs?: number } = {},
|
|
588
539
|
): Promise<void> {
|
|
589
540
|
if (opts.importTimeoutMs !== undefined) {
|
|
590
|
-
|
|
541
|
+
setSurfaceImportTimeout(opts.importTimeoutMs);
|
|
591
542
|
}
|
|
592
543
|
|
|
544
|
+
// Scans + activates every discovered plugin (tools registered + `init` run).
|
|
593
545
|
await scanPlugins();
|
|
594
546
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
for
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
const currentMtime = getMtime(file.path);
|
|
606
|
-
if (currentMtime === 0) continue;
|
|
607
|
-
|
|
608
|
-
try {
|
|
609
|
-
const hook = await importWithTimeout<PluginHookFn>(
|
|
610
|
-
file.path,
|
|
611
|
-
importTimeoutMs,
|
|
612
|
-
);
|
|
613
|
-
if (hook !== undefined && typeof hook === "function") {
|
|
614
|
-
hookCache.set(key, { hook, sourceMtime: currentMtime });
|
|
615
|
-
}
|
|
616
|
-
} catch (err) {
|
|
617
|
-
log.error(
|
|
618
|
-
{ err, plugin: pluginName, hook: file.name, path: file.path },
|
|
619
|
-
`Failed to pre-import hook ${file.name}`,
|
|
620
|
-
);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// Register user plugin tools into the global tool registry so
|
|
625
|
-
// `getAllTools()` and `getTool()` can find them. Tools were already
|
|
626
|
-
// imported and cached by `reconcilePluginTools` during `scanPlugins`.
|
|
627
|
-
const pluginTools = Array.from(toolCache.values())
|
|
628
|
-
.filter((c) => c.pluginName === pluginName)
|
|
629
|
-
.map((c) => c.tool);
|
|
630
|
-
if (pluginTools.length > 0) {
|
|
631
|
-
try {
|
|
632
|
-
registerPluginTools(pluginName, pluginTools);
|
|
633
|
-
log.info(
|
|
634
|
-
{ plugin: pluginName, count: pluginTools.length },
|
|
635
|
-
"user plugin tools registered",
|
|
636
|
-
);
|
|
637
|
-
} catch (err) {
|
|
638
|
-
log.error(
|
|
639
|
-
{ err, plugin: pluginName },
|
|
640
|
-
`Failed to register tools for user plugin ${pluginName}`,
|
|
641
|
-
);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
// Run the `init` hook if present.
|
|
646
|
-
const initHookEntry = hookCache.get(hookKey(pluginName, HOOKS.INIT));
|
|
647
|
-
if (initHookEntry !== undefined) {
|
|
648
|
-
try {
|
|
649
|
-
const initContext: PluginInitContext = {
|
|
650
|
-
config: getConfig().plugins?.[pluginName],
|
|
651
|
-
credentials: {},
|
|
652
|
-
logger: log.child({ plugin: pluginName }),
|
|
653
|
-
pluginStorageDir: ensurePluginStorageDir(pluginName),
|
|
654
|
-
assistantVersion: APP_VERSION,
|
|
655
|
-
};
|
|
656
|
-
await initHookEntry.hook(initContext);
|
|
657
|
-
log.info({ plugin: pluginName }, "user plugin initialized");
|
|
658
|
-
} catch (err) {
|
|
659
|
-
log.error(
|
|
660
|
-
{ err, plugin: pluginName },
|
|
661
|
-
`User plugin ${pluginName} init() failed — continuing`,
|
|
662
|
-
);
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
activatedPlugins.push({ name: pluginName });
|
|
547
|
+
// Activate standalone workspace hooks under `<workspace>/hooks/`. These
|
|
548
|
+
// carry no package.json, no tools, and no install-date ordering — just hook
|
|
549
|
+
// files. Pre-import them and run their `init` hook so a workspace-wide
|
|
550
|
+
// `init`/`shutdown` lifecycle works the same way a plugin's does. Only
|
|
551
|
+
// register for teardown when at least one hook file is actually present, so
|
|
552
|
+
// an empty/absent directory adds no shutdown work.
|
|
553
|
+
if (hasWorkspaceHooks()) {
|
|
554
|
+
await preImportWorkspaceHooks();
|
|
555
|
+
await runInitHook(WORKSPACE_HOOKS_OWNER);
|
|
556
|
+
activatedPlugins.push({ name: WORKSPACE_HOOKS_OWNER });
|
|
667
557
|
}
|
|
668
558
|
|
|
669
|
-
// Register a single shutdown hook that walks all activated
|
|
670
|
-
//
|
|
671
|
-
|
|
559
|
+
// Register a single shutdown hook that walks all activated owners in reverse
|
|
560
|
+
// order, unregistering tools and running shutdown hooks. It reads the live
|
|
561
|
+
// `activatedPlugins` array at teardown time, so plugins activated after boot
|
|
562
|
+
// (runtime installs) are torn down too.
|
|
672
563
|
registerShutdownHook("user-plugins", async (reason) => {
|
|
673
|
-
for (let i =
|
|
674
|
-
const
|
|
564
|
+
for (let i = activatedPlugins.length - 1; i >= 0; i--) {
|
|
565
|
+
const entry = activatedPlugins[i];
|
|
566
|
+
if (entry === undefined) continue;
|
|
567
|
+
const { name } = entry;
|
|
675
568
|
|
|
676
569
|
// Unregister tools before running shutdown so onShutdown sees a
|
|
677
|
-
// clean model-visible surface.
|
|
570
|
+
// clean model-visible surface. (No-op for the workspace-hooks owner,
|
|
571
|
+
// which registers no tools.)
|
|
678
572
|
try {
|
|
679
573
|
unregisterPluginTools(name);
|
|
680
574
|
} catch (err) {
|
|
@@ -685,30 +579,11 @@ export async function populateCacheAtBoot(
|
|
|
685
579
|
}
|
|
686
580
|
|
|
687
581
|
// Run the `shutdown` hook if present.
|
|
688
|
-
|
|
689
|
-
if (shutdownHookEntry !== undefined) {
|
|
690
|
-
try {
|
|
691
|
-
await shutdownHookEntry.hook(shutdownContext);
|
|
692
|
-
} catch (err) {
|
|
693
|
-
log.warn(
|
|
694
|
-
{ err, plugin: name, reason },
|
|
695
|
-
"user plugin shutdown hook failed (continuing)",
|
|
696
|
-
);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
582
|
+
await runShutdownHook(name, shutdownContext, reason);
|
|
699
583
|
}
|
|
700
584
|
});
|
|
701
585
|
}
|
|
702
586
|
|
|
703
|
-
/**
|
|
704
|
-
* Ensure `<workspaceDir>/plugins-data/<name>/` exists and return its path.
|
|
705
|
-
*/
|
|
706
|
-
function ensurePluginStorageDir(pluginName: string): string {
|
|
707
|
-
const dir = join(getWorkspaceDir(), "plugins-data", pluginName);
|
|
708
|
-
mkdirSync(dir, { recursive: true });
|
|
709
|
-
return dir;
|
|
710
|
-
}
|
|
711
|
-
|
|
712
587
|
// ─── Test hooks ──────────────────────────────────────────────────────────────
|
|
713
588
|
|
|
714
589
|
/**
|
|
@@ -722,35 +597,16 @@ export function resetPluginCacheForTests(): void {
|
|
|
722
597
|
"resetPluginCacheForTests may only be called in test environments",
|
|
723
598
|
);
|
|
724
599
|
}
|
|
725
|
-
|
|
600
|
+
resetHookCacheForTests();
|
|
601
|
+
clearSurfaceImportInflight();
|
|
726
602
|
toolCache.clear();
|
|
727
|
-
inflight.clear();
|
|
728
603
|
discoveredPluginDirs.clear();
|
|
729
604
|
installDateCache.clear();
|
|
730
605
|
activatedPlugins.length = 0;
|
|
606
|
+
activatedNames.clear();
|
|
731
607
|
disabledPluginDirs.clear();
|
|
732
608
|
}
|
|
733
609
|
|
|
734
|
-
/**
|
|
735
|
-
* Test-only: inspect the hook cache.
|
|
736
|
-
*/
|
|
737
|
-
export function _inspectHookCacheForTests(): Array<{
|
|
738
|
-
key: string;
|
|
739
|
-
sourceMtime: number;
|
|
740
|
-
}> {
|
|
741
|
-
const isTest =
|
|
742
|
-
process.env.BUN_TEST === "1" || process.env.NODE_ENV === "test";
|
|
743
|
-
if (!isTest) {
|
|
744
|
-
throw new Error(
|
|
745
|
-
"_inspectHookCacheForTests may only be called in test environments",
|
|
746
|
-
);
|
|
747
|
-
}
|
|
748
|
-
return Array.from(hookCache.entries()).map(([key, c]) => ({
|
|
749
|
-
key,
|
|
750
|
-
sourceMtime: c.sourceMtime,
|
|
751
|
-
}));
|
|
752
|
-
}
|
|
753
|
-
|
|
754
610
|
/**
|
|
755
611
|
* Test-only: inspect the tool cache.
|
|
756
612
|
*/
|