@vellumai/assistant 0.8.2 → 0.8.4
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 +11 -12
- package/docker-entrypoint.sh +13 -2
- package/docker-init-apt-root.sh +79 -6
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +945 -36
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +112 -57
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +77 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- 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 +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/external-plugin-loader.test.ts +91 -19
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/host-app-control-proxy.test.ts +241 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +7 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +3 -0
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
- package/src/__tests__/llm-resolver.test.ts +340 -3
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/managed-profile-guard.test.ts +10 -0
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/openai-provider.test.ts +323 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform-proxy-context.test.ts +6 -1
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
- package/src/__tests__/plugin-types.test.ts +2 -2
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +57 -101
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +208 -22
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +14 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +469 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +89 -37
- package/src/cli/commands/plugins.ts +67 -0
- package/src/cli/commands/schedules.ts +297 -5
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/install-from-github.ts +8 -9
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/program.ts +14 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +124 -0
- package/src/config/feature-flag-registry.json +111 -23
- package/src/config/llm-resolver.ts +66 -1
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/channels.ts +9 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +14 -0
- package/src/config/schemas/llm.ts +4 -3
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +51 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/context/compactor.ts +80 -13
- package/src/context/token-estimator.ts +72 -31
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
- package/src/daemon/conversation-agent-loop.ts +252 -56
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +144 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -12
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +449 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +80 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-app-control-proxy.ts +69 -18
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +67 -65
- package/src/daemon/memory-v2-startup.ts +49 -13
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/pkb-reminder-builder.test.ts +10 -53
- package/src/daemon/pkb-reminder-builder.ts +4 -19
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +72 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/skill-memory-refresh.ts +5 -1
- package/src/daemon/trust-context.ts +6 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
- package/src/heartbeat/heartbeat-service.ts +35 -191
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +20 -3
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/ipc/cli-client.ts +147 -45
- package/src/memory/__tests__/conversation-queries.test.ts +220 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/conversation-queries.ts +87 -1
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +22 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
- package/src/memory/graph/conversation-graph-memory.ts +18 -6
- package/src/memory/graph/tools.ts +6 -37
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +4 -22
- package/src/memory/memory-retrospective-job.ts +438 -21
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +20 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +2 -0
- package/src/memory/schema/infrastructure.ts +2 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +221 -17
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +69 -29
- package/src/memory/v2/page-index.ts +246 -19
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +18 -3
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +5 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +111 -44
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +10 -1
- package/src/notifications/home-feed-side-effect.ts +136 -27
- package/src/notifications/signal.ts +0 -4
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +13 -4
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +118 -26
- package/src/plugins/external-plugin-loader.ts +82 -10
- package/src/plugins/types.ts +16 -7
- package/src/prompts/__tests__/system-prompt.test.ts +44 -45
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -76
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +51 -10
- package/src/providers/__tests__/inference.test.ts +2 -0
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +63 -13
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +24 -21
- package/src/providers/inference/auth.ts +15 -3
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/connections.ts +85 -5
- package/src/providers/inference/resolve-auth.ts +50 -5
- package/src/providers/model-catalog.ts +244 -242
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +215 -25
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +46 -4
- package/src/providers/platform-proxy/constants.ts +3 -4
- package/src/providers/provider-catalog-visibility.ts +3 -1
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +30 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +212 -57
- package/src/runtime/auth/route-policy.ts +20 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +7 -51
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/channel-availability-routes.ts +5 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +130 -12
- package/src/runtime/routes/conversation-routes.ts +288 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +14 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
- package/src/runtime/routes/integrations/a2a.ts +294 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/routes/subagents-routes.ts +41 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/subagent/manager.ts +2 -0
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/registry.ts +2 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +41 -2
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -2,32 +2,24 @@ import { describe, expect, test } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import { buildPkbReminder } from "./pkb-reminder-builder.js";
|
|
4
4
|
|
|
5
|
-
// Byte-for-byte fixture of the
|
|
6
|
-
//
|
|
7
|
-
const
|
|
8
|
-
"<system_reminder>" +
|
|
9
|
-
"\n**CRITICAL:** Call `remember` this turn for anything concrete the user said — facts, preferences, plans, names, dates, decisions, corrections, felt moments. Default to remembering; skip only obvious noise. This should be your most frequently used tool." +
|
|
10
|
-
"\nIf you're unsure about something that may live in the workspace — past decisions, prior conversations, files — use `recall` before asking or guessing." +
|
|
11
|
-
"\n</system_reminder>";
|
|
12
|
-
|
|
13
|
-
// Byte-for-byte fixture of the relaxed PKB reminder used when the
|
|
14
|
-
// `memory-retrospective` feature flag is on.
|
|
15
|
-
const BASE_REMINDER_RELAXED =
|
|
5
|
+
// Byte-for-byte fixture of the PKB reminder. Asserted verbatim so that any
|
|
6
|
+
// future edit to the BODY text is caught by tests.
|
|
7
|
+
const BASE_REMINDER =
|
|
16
8
|
"<system_reminder>" +
|
|
17
9
|
"\nStay present in this conversation. Use `remember` when something feels worth pausing to mark — corrections (highest priority), plans, decisions, felt moments. You don't have to capture everything in the moment — a retrospective pass reviews this conversation in the background and saves what you didn't capture." +
|
|
18
10
|
"\nIf you're unsure about something that may live in the workspace — past decisions, prior conversations, files — use `recall` before asking or guessing." +
|
|
19
11
|
"\n</system_reminder>";
|
|
20
12
|
|
|
21
|
-
describe("buildPkbReminder
|
|
13
|
+
describe("buildPkbReminder", () => {
|
|
22
14
|
test("empty hints returns exact base reminder byte-for-byte", () => {
|
|
23
|
-
expect(buildPkbReminder([]
|
|
15
|
+
expect(buildPkbReminder([])).toBe(BASE_REMINDER);
|
|
24
16
|
});
|
|
25
17
|
|
|
26
18
|
test("single hint renders one bullet with no duplicates or trailing blank line", () => {
|
|
27
|
-
const out = buildPkbReminder(["projects/alpha.md"]
|
|
19
|
+
const out = buildPkbReminder(["projects/alpha.md"]);
|
|
28
20
|
const expected =
|
|
29
21
|
"<system_reminder>" +
|
|
30
|
-
"\
|
|
22
|
+
"\nStay present in this conversation. Use `remember` when something feels worth pausing to mark — corrections (highest priority), plans, decisions, felt moments. You don't have to capture everything in the moment — a retrospective pass reviews this conversation in the background and saves what you didn't capture." +
|
|
31
23
|
"\nIf you're unsure about something that may live in the workspace — past decisions, prior conversations, files — use `recall` before asking or guessing." +
|
|
32
24
|
"\nBased on the current context, these files look especially relevant:" +
|
|
33
25
|
"\n- projects/alpha.md" +
|
|
@@ -44,10 +36,10 @@ describe("buildPkbReminder — default body (relaxed=false)", () => {
|
|
|
44
36
|
|
|
45
37
|
test("three hints render all three in order", () => {
|
|
46
38
|
const hints = ["a.md", "sub/b.md", "c/d/e.md"];
|
|
47
|
-
const out = buildPkbReminder(hints
|
|
39
|
+
const out = buildPkbReminder(hints);
|
|
48
40
|
const expected =
|
|
49
41
|
"<system_reminder>" +
|
|
50
|
-
"\
|
|
42
|
+
"\nStay present in this conversation. Use `remember` when something feels worth pausing to mark — corrections (highest priority), plans, decisions, felt moments. You don't have to capture everything in the moment — a retrospective pass reviews this conversation in the background and saves what you didn't capture." +
|
|
51
43
|
"\nIf you're unsure about something that may live in the workspace — past decisions, prior conversations, files — use `recall` before asking or guessing." +
|
|
52
44
|
"\nBased on the current context, these files look especially relevant:" +
|
|
53
45
|
"\n- a.md" +
|
|
@@ -67,7 +59,7 @@ describe("buildPkbReminder — default body (relaxed=false)", () => {
|
|
|
67
59
|
|
|
68
60
|
test("hints with special chars (< and &) are emitted verbatim (no escaping)", () => {
|
|
69
61
|
const hints = ["weird<name>.md", "foo&bar.md"];
|
|
70
|
-
const out = buildPkbReminder(hints
|
|
62
|
+
const out = buildPkbReminder(hints);
|
|
71
63
|
expect(out).toContain("- weird<name>.md");
|
|
72
64
|
expect(out).toContain("- foo&bar.md");
|
|
73
65
|
// Ensure no HTML-style escaping happened.
|
|
@@ -75,38 +67,3 @@ describe("buildPkbReminder — default body (relaxed=false)", () => {
|
|
|
75
67
|
expect(out).not.toContain("&");
|
|
76
68
|
});
|
|
77
69
|
});
|
|
78
|
-
|
|
79
|
-
describe("buildPkbReminder — relaxed body (relaxed=true)", () => {
|
|
80
|
-
test("empty hints returns the relaxed base reminder byte-for-byte", () => {
|
|
81
|
-
expect(buildPkbReminder([], true)).toBe(BASE_REMINDER_RELAXED);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test("relaxed BODY does NOT contain the default high-pressure phrasing", () => {
|
|
85
|
-
const out = buildPkbReminder([], true);
|
|
86
|
-
expect(out).not.toContain("**CRITICAL:**");
|
|
87
|
-
expect(out).not.toContain("most frequently used tool");
|
|
88
|
-
expect(out).not.toContain("Default to remembering");
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test("relaxed BODY mentions the retrospective backstop framing", () => {
|
|
92
|
-
const out = buildPkbReminder([], true);
|
|
93
|
-
expect(out).toContain("Stay present");
|
|
94
|
-
expect(out).toContain("retrospective pass");
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test("hints render below the relaxed body in the same shape as default", () => {
|
|
98
|
-
const out = buildPkbReminder(["projects/alpha.md"], true);
|
|
99
|
-
expect(out).toContain("- projects/alpha.md");
|
|
100
|
-
expect(out).toContain(
|
|
101
|
-
"Based on the current context, these files look especially relevant:",
|
|
102
|
-
);
|
|
103
|
-
// Should still close cleanly with no double newline before the tag.
|
|
104
|
-
expect(out.includes("\n\n</system_reminder>")).toBe(false);
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
describe("buildPkbReminder — relaxed vs default differ", () => {
|
|
109
|
-
test("the two BODY variants are NOT byte-identical", () => {
|
|
110
|
-
expect(buildPkbReminder([], false)).not.toBe(buildPkbReminder([], true));
|
|
111
|
-
});
|
|
112
|
-
});
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
const
|
|
2
|
-
"\n**CRITICAL:** Call `remember` this turn for anything concrete the user said — facts, preferences, plans, names, dates, decisions, corrections, felt moments. Default to remembering; skip only obvious noise. This should be your most frequently used tool." +
|
|
3
|
-
"\nIf you're unsure about something that may live in the workspace — past decisions, prior conversations, files — use `recall` before asking or guessing.";
|
|
4
|
-
|
|
5
|
-
const BODY_RELAXED =
|
|
1
|
+
const BODY =
|
|
6
2
|
"\nStay present in this conversation. Use `remember` when something feels worth pausing to mark — corrections (highest priority), plans, decisions, felt moments. You don't have to capture everything in the moment — a retrospective pass reviews this conversation in the background and saves what you didn't capture." +
|
|
7
3
|
"\nIf you're unsure about something that may live in the workspace — past decisions, prior conversations, files — use `recall` before asking or guessing.";
|
|
8
4
|
|
|
@@ -15,23 +11,12 @@ const BODY_RELAXED =
|
|
|
15
11
|
* hint. Hints are emitted verbatim — they are trusted internal paths, not
|
|
16
12
|
* user input, so no escaping is performed.
|
|
17
13
|
*
|
|
18
|
-
* The `relaxed` flag selects between the default high-pressure body and the
|
|
19
|
-
* relaxed "judgment framing" body used when the `memory-retrospective`
|
|
20
|
-
* feature flag is on. With the flag on, the in-conversation remember pressure
|
|
21
|
-
* eases because the retrospective is the backstop. Callers must pass
|
|
22
|
-
* `relaxed` explicitly — no default — so the contract is visible at every
|
|
23
|
-
* call site.
|
|
24
|
-
*
|
|
25
14
|
* Caller is responsible for capping the hints array at 3 entries.
|
|
26
15
|
*/
|
|
27
|
-
export function buildPkbReminder(
|
|
28
|
-
hints: ReadonlyArray<string>,
|
|
29
|
-
relaxed: boolean,
|
|
30
|
-
): string {
|
|
31
|
-
const body = relaxed ? BODY_RELAXED : BODY_DEFAULT;
|
|
16
|
+
export function buildPkbReminder(hints: ReadonlyArray<string>): string {
|
|
32
17
|
if (hints.length === 0) {
|
|
33
|
-
return `<system_reminder>${
|
|
18
|
+
return `<system_reminder>${BODY}\n</system_reminder>`;
|
|
34
19
|
}
|
|
35
20
|
const bullets = hints.map((h) => `- ${h}`).join("\n");
|
|
36
|
-
return `<system_reminder>${
|
|
21
|
+
return `<system_reminder>${BODY}\nBased on the current context, these files look especially relevant:\n${bullets}\n</system_reminder>`;
|
|
37
22
|
}
|
|
@@ -12,10 +12,32 @@
|
|
|
12
12
|
* through a static singleton so tool side-effects can call
|
|
13
13
|
* `PluginSourceWatcher.getInstance().ensureStarted()` directly without an
|
|
14
14
|
* intermediate module-level injection.
|
|
15
|
+
*
|
|
16
|
+
* ## Linux/Bun recursive-watch caveat
|
|
17
|
+
*
|
|
18
|
+
* `fs.watch(dir, { recursive: true })` on Linux (and the equivalent under
|
|
19
|
+
* Bun, which uses the platform inotify path) does **not** dynamically
|
|
20
|
+
* attach to subdirectories created after the watch was established. The
|
|
21
|
+
* kernel fires exactly one event when the new top-level entry appears,
|
|
22
|
+
* and any writes inside that new subtree are silently dropped.
|
|
23
|
+
*
|
|
24
|
+
* Reproducer (against an empty watched root):
|
|
25
|
+
*
|
|
26
|
+
* mkdir plugin/ → event "plugin"
|
|
27
|
+
* mkdir plugin/hooks/ → no event delivered
|
|
28
|
+
* echo x > plugin/hooks/file.ts → no event delivered
|
|
29
|
+
*
|
|
30
|
+
* To work around this we close + reopen the watcher on every event (with
|
|
31
|
+
* a coalescing debounce). Reopening walks the tree from scratch and
|
|
32
|
+
* re-subscribes to every directory that currently exists, so subsequent
|
|
33
|
+
* writes anywhere under the new subtree are delivered normally. We also
|
|
34
|
+
* rescan top-level entries after the swap and dispatch a rebuild for any
|
|
35
|
+
* plugin not yet in the registry, which closes the close→reopen gap.
|
|
15
36
|
*/
|
|
16
37
|
|
|
17
|
-
import { type FSWatcher, mkdirSync, watch } from "node:fs";
|
|
38
|
+
import { type FSWatcher, mkdirSync, readdirSync, watch } from "node:fs";
|
|
18
39
|
|
|
40
|
+
import { getRegisteredPlugin } from "../plugins/registry.js";
|
|
19
41
|
import { DebouncerMap } from "../util/debounce.js";
|
|
20
42
|
import { getLogger } from "../util/logger.js";
|
|
21
43
|
import { getWorkspacePluginsDir } from "../util/platform.js";
|
|
@@ -25,6 +47,12 @@ const log = getLogger("plugin-source-watcher");
|
|
|
25
47
|
|
|
26
48
|
const PLUGIN_SOURCE_DEBOUNCE_MS = 500;
|
|
27
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Single shared debouncer key for the "restart the watcher" path. Bursty
|
|
52
|
+
* events for many plugin names all collapse to one restart.
|
|
53
|
+
*/
|
|
54
|
+
const WATCHER_RESTART_KEY = "__restart__";
|
|
55
|
+
|
|
28
56
|
/**
|
|
29
57
|
* Extract the plugin's top-level directory name from a relative path within
|
|
30
58
|
* the plugins root. Returns null for a stray file directly in `plugins/`
|
|
@@ -36,10 +64,12 @@ function resolvePluginNameFromRelPath(relPath: string): string | null {
|
|
|
36
64
|
// Bare entry under plugins/ — fs.watch reports these when a new
|
|
37
65
|
// directory is first created. We treat the name as the plugin name and
|
|
38
66
|
// let the install path decide whether it's a real plugin.
|
|
39
|
-
|
|
67
|
+
if (relPath.length === 0 || relPath.startsWith(".")) return null;
|
|
68
|
+
return relPath;
|
|
40
69
|
}
|
|
41
70
|
const dirName = relPath.slice(0, slashIdx);
|
|
42
|
-
|
|
71
|
+
if (dirName.length === 0 || dirName.startsWith(".")) return null;
|
|
72
|
+
return dirName;
|
|
43
73
|
}
|
|
44
74
|
|
|
45
75
|
export class PluginSourceWatcher {
|
|
@@ -70,6 +100,14 @@ export class PluginSourceWatcher {
|
|
|
70
100
|
defaultDelayMs: PLUGIN_SOURCE_DEBOUNCE_MS,
|
|
71
101
|
maxEntries: 50,
|
|
72
102
|
});
|
|
103
|
+
/**
|
|
104
|
+
* Coalesces watcher-restart requests across bursty events. See
|
|
105
|
+
* {@link restartWatcher} for why we restart on every event.
|
|
106
|
+
*/
|
|
107
|
+
private restartDebouncer = new DebouncerMap({
|
|
108
|
+
defaultDelayMs: PLUGIN_SOURCE_DEBOUNCE_MS,
|
|
109
|
+
maxEntries: 1,
|
|
110
|
+
});
|
|
73
111
|
|
|
74
112
|
start(): void {
|
|
75
113
|
this.started = true;
|
|
@@ -89,6 +127,7 @@ export class PluginSourceWatcher {
|
|
|
89
127
|
stop(): void {
|
|
90
128
|
this.started = false;
|
|
91
129
|
this.debouncer.cancelAll();
|
|
130
|
+
this.restartDebouncer.cancelAll();
|
|
92
131
|
if (this.watcher) {
|
|
93
132
|
this.watcher.close();
|
|
94
133
|
this.watcher = null;
|
|
@@ -99,6 +138,89 @@ export class PluginSourceWatcher {
|
|
|
99
138
|
return reregisterExternalPlugin(pluginName);
|
|
100
139
|
}
|
|
101
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Close the current FSWatcher and open a fresh one on the same root.
|
|
143
|
+
*
|
|
144
|
+
* Workaround for the Linux/Bun recursive-watch limitation documented at
|
|
145
|
+
* the top of this file. Reopening re-walks the tree and re-subscribes
|
|
146
|
+
* to every directory that exists at that moment, including any subtree
|
|
147
|
+
* that grew under the watched root since the previous watcher started.
|
|
148
|
+
*
|
|
149
|
+
* Belt-and-suspenders: after the swap, we rescan top-level entries and
|
|
150
|
+
* dispatch a rebuild for any plugin not yet in the registry — this
|
|
151
|
+
* closes the (sub-millisecond) gap between close and reopen during
|
|
152
|
+
* which a brand-new plugin's first event could be lost.
|
|
153
|
+
*
|
|
154
|
+
* Failure mode: if the reopen fails, keep the previous watcher active.
|
|
155
|
+
* It may still miss newly-created subtrees, but it preserves existing
|
|
156
|
+
* plugin source coverage instead of degrading to no watcher at all.
|
|
157
|
+
*/
|
|
158
|
+
private restartWatcher(): void {
|
|
159
|
+
if (!this.started) return;
|
|
160
|
+
|
|
161
|
+
const oldWatcher = this.watcher;
|
|
162
|
+
this.watcher = null; // tryWatch returns early when non-null
|
|
163
|
+
this.tryWatch();
|
|
164
|
+
|
|
165
|
+
if (this.watcher === null) {
|
|
166
|
+
// Keep the previous watcher alive if the replacement failed. It may not
|
|
167
|
+
// cover newly-created subtrees, but it is still better than dropping all
|
|
168
|
+
// plugin source coverage.
|
|
169
|
+
this.watcher = oldWatcher;
|
|
170
|
+
log.warn(
|
|
171
|
+
"Plugin source watcher restart failed; keeping previous watcher active",
|
|
172
|
+
);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (oldWatcher) {
|
|
177
|
+
try {
|
|
178
|
+
oldWatcher.close();
|
|
179
|
+
} catch (err) {
|
|
180
|
+
log.warn({ err }, "Failed to close previous plugin watcher");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
this.rescanPlugins();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Schedule a rebuild for every top-level entry under the plugins dir
|
|
189
|
+
* that isn't already in the registry. Called after a successful
|
|
190
|
+
* {@link restartWatcher} to catch new-plugin events that may have been
|
|
191
|
+
* lost during the close→reopen swap.
|
|
192
|
+
*
|
|
193
|
+
* Existing plugins are skipped — their normal change events are
|
|
194
|
+
* delivered through the freshly-attached watcher.
|
|
195
|
+
*/
|
|
196
|
+
private rescanPlugins(): void {
|
|
197
|
+
let pluginsDir: string;
|
|
198
|
+
try {
|
|
199
|
+
pluginsDir = getWorkspacePluginsDir();
|
|
200
|
+
} catch {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let entries: string[];
|
|
205
|
+
try {
|
|
206
|
+
entries = readdirSync(pluginsDir);
|
|
207
|
+
} catch (err) {
|
|
208
|
+
log.warn({ err, pluginsDir }, "Failed to rescan plugins directory");
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
for (const entry of entries) {
|
|
213
|
+
// Skip dotfiles / dot-directories (e.g. macOS `.DS_Store`, npm cache).
|
|
214
|
+
if (entry.startsWith(".")) continue;
|
|
215
|
+
// Existing plugins ride the watcher's normal event delivery. The
|
|
216
|
+
// rescan is purely to catch installs whose first event was lost.
|
|
217
|
+
if (getRegisteredPlugin(entry) !== undefined) continue;
|
|
218
|
+
this.debouncer.schedule(`plugin:${entry}`, () => {
|
|
219
|
+
void this.onChange(entry);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
102
224
|
private tryWatch(): void {
|
|
103
225
|
if (this.watcher) return;
|
|
104
226
|
|
|
@@ -130,9 +252,19 @@ export class PluginSourceWatcher {
|
|
|
130
252
|
if (!filename) return;
|
|
131
253
|
const pluginName = resolvePluginNameFromRelPath(filename);
|
|
132
254
|
if (!pluginName) return;
|
|
255
|
+
|
|
256
|
+
// Per-plugin rebuild — debounced under the plugin name so bursty
|
|
257
|
+
// edits collapse to a single rebuild.
|
|
133
258
|
this.debouncer.schedule(`plugin:${pluginName}`, () => {
|
|
134
259
|
void this.onChange(pluginName);
|
|
135
260
|
});
|
|
261
|
+
|
|
262
|
+
// Refresh the watcher to pick up any subtree that grew under us.
|
|
263
|
+
// Coalesced across all plugin names; single watcher restart per
|
|
264
|
+
// event burst. See restartWatcher for the rationale.
|
|
265
|
+
this.restartDebouncer.schedule(WATCHER_RESTART_KEY, () => {
|
|
266
|
+
this.restartWatcher();
|
|
267
|
+
});
|
|
136
268
|
},
|
|
137
269
|
);
|
|
138
270
|
log.info({ pluginsDir }, "Plugin source watcher started");
|
|
@@ -31,13 +31,17 @@ import { updateMetaFile } from "../memory/conversation-disk-view.js";
|
|
|
31
31
|
import { broadcastMessage } from "../runtime/assistant-event-hub.js";
|
|
32
32
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
33
33
|
import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
|
|
34
|
+
import { getSubagentManager } from "../subagent/index.js";
|
|
34
35
|
import { getLogger } from "../util/logger.js";
|
|
35
36
|
import type { Conversation } from "./conversation.js";
|
|
36
37
|
import {
|
|
37
38
|
buildSlackMetaForPersistence,
|
|
38
39
|
serializePersistedUserMessageContent,
|
|
39
40
|
} from "./conversation-messaging.js";
|
|
40
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
formatCleanResult,
|
|
43
|
+
formatCompactResult,
|
|
44
|
+
} from "./conversation-process.js";
|
|
41
45
|
import { resolveChannelCapabilities } from "./conversation-runtime-assembly.js";
|
|
42
46
|
import {
|
|
43
47
|
buildSlashContextForContent,
|
|
@@ -253,7 +257,7 @@ export async function processMessage(
|
|
|
253
257
|
options?: ProcessMessageOptions,
|
|
254
258
|
sourceChannel?: string,
|
|
255
259
|
sourceInterface?: string,
|
|
256
|
-
): Promise<{ messageId: string }> {
|
|
260
|
+
): Promise<{ messageId: string; assistantMessageId?: string }> {
|
|
257
261
|
const conversationOptions = stripPerTurnObservers(options);
|
|
258
262
|
const { conversation, attachments } = await prepareConversationForMessage(
|
|
259
263
|
conversationId,
|
|
@@ -369,7 +373,7 @@ export async function processMessage(
|
|
|
369
373
|
}
|
|
370
374
|
|
|
371
375
|
const assistantMsg = createAssistantMessage(slashResult.message);
|
|
372
|
-
await addMessage(
|
|
376
|
+
const persistedAssistant = await addMessage(
|
|
373
377
|
conversationId,
|
|
374
378
|
"assistant",
|
|
375
379
|
JSON.stringify(assistantMsg.content),
|
|
@@ -377,7 +381,10 @@ export async function processMessage(
|
|
|
377
381
|
);
|
|
378
382
|
conversation.getMessages().push(assistantMsg);
|
|
379
383
|
publishConversationMessagesChanged(conversationId);
|
|
380
|
-
return {
|
|
384
|
+
return {
|
|
385
|
+
messageId: persisted.id,
|
|
386
|
+
assistantMessageId: persistedAssistant.id,
|
|
387
|
+
};
|
|
381
388
|
}
|
|
382
389
|
|
|
383
390
|
if (slashResult.kind === "compact") {
|
|
@@ -427,7 +434,7 @@ export async function processMessage(
|
|
|
427
434
|
});
|
|
428
435
|
const responseText = formatCompactResult(result);
|
|
429
436
|
const assistantMsg = createAssistantMessage(responseText);
|
|
430
|
-
await addMessage(
|
|
437
|
+
const persistedAssistant = await addMessage(
|
|
431
438
|
conversationId,
|
|
432
439
|
"assistant",
|
|
433
440
|
JSON.stringify(assistantMsg.content),
|
|
@@ -435,7 +442,64 @@ export async function processMessage(
|
|
|
435
442
|
);
|
|
436
443
|
conversation.getMessages().push(assistantMsg);
|
|
437
444
|
publishConversationMessagesChanged(conversationId);
|
|
438
|
-
return {
|
|
445
|
+
return {
|
|
446
|
+
messageId: persisted.id,
|
|
447
|
+
assistantMessageId: persistedAssistant.id,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (slashResult.kind === "clean") {
|
|
452
|
+
const serverTurnCtx = conversation.getTurnChannelContext();
|
|
453
|
+
const serverProvenance = provenanceFromTrustContext(
|
|
454
|
+
conversation.trustContext,
|
|
455
|
+
);
|
|
456
|
+
const cleanChannelMeta = {
|
|
457
|
+
...serverProvenance,
|
|
458
|
+
...(serverTurnCtx
|
|
459
|
+
? {
|
|
460
|
+
userMessageChannel: serverTurnCtx.userMessageChannel,
|
|
461
|
+
assistantMessageChannel: serverTurnCtx.assistantMessageChannel,
|
|
462
|
+
}
|
|
463
|
+
: {}),
|
|
464
|
+
...(serverInterfaceCtx
|
|
465
|
+
? {
|
|
466
|
+
userMessageInterface: serverInterfaceCtx.userMessageInterface,
|
|
467
|
+
assistantMessageInterface:
|
|
468
|
+
serverInterfaceCtx.assistantMessageInterface,
|
|
469
|
+
}
|
|
470
|
+
: {}),
|
|
471
|
+
};
|
|
472
|
+
const cleanUserMeta = slackMeta
|
|
473
|
+
? { ...cleanChannelMeta, slackMeta }
|
|
474
|
+
: cleanChannelMeta;
|
|
475
|
+
const cleanMsg = createUserMessage(content, attachments);
|
|
476
|
+
const persisted = await addMessage(
|
|
477
|
+
conversationId,
|
|
478
|
+
"user",
|
|
479
|
+
serializePersistedUserMessageContent(
|
|
480
|
+
content,
|
|
481
|
+
attachments,
|
|
482
|
+
options?.displayContent,
|
|
483
|
+
),
|
|
484
|
+
cleanUserMeta,
|
|
485
|
+
);
|
|
486
|
+
conversation.getMessages().push(cleanMsg);
|
|
487
|
+
|
|
488
|
+
const result = await conversation.forceClean();
|
|
489
|
+
const responseText = formatCleanResult(result);
|
|
490
|
+
const assistantMsg = createAssistantMessage(responseText);
|
|
491
|
+
const persistedAssistant = await addMessage(
|
|
492
|
+
conversationId,
|
|
493
|
+
"assistant",
|
|
494
|
+
JSON.stringify(assistantMsg.content),
|
|
495
|
+
cleanChannelMeta,
|
|
496
|
+
);
|
|
497
|
+
conversation.getMessages().push(assistantMsg);
|
|
498
|
+
publishConversationMessagesChanged(conversationId);
|
|
499
|
+
return {
|
|
500
|
+
messageId: persisted.id,
|
|
501
|
+
assistantMessageId: persistedAssistant.id,
|
|
502
|
+
};
|
|
439
503
|
}
|
|
440
504
|
|
|
441
505
|
const resolvedContent = slashResult.content;
|
|
@@ -455,19 +519,16 @@ export async function processMessage(
|
|
|
455
519
|
|
|
456
520
|
if (options?.isInteractive === true) {
|
|
457
521
|
conversation.updateClient(broadcastMessage, false);
|
|
522
|
+
getSubagentManager().updateParentSender(conversationId, broadcastMessage);
|
|
458
523
|
}
|
|
459
524
|
|
|
460
525
|
try {
|
|
461
|
-
conversation.setSlackRuntimeContextNotice(
|
|
462
|
-
options?.slackRuntimeContextNotice,
|
|
463
|
-
);
|
|
464
526
|
await conversation.runAgentLoop(resolvedContent, messageId, emitEvent, {
|
|
465
527
|
isInteractive: options?.isInteractive ?? false,
|
|
466
528
|
isUserMessage: true,
|
|
467
529
|
...(options?.callSite ? { callSite: options.callSite } : {}),
|
|
468
530
|
});
|
|
469
531
|
} finally {
|
|
470
|
-
conversation.setSlackRuntimeContextNotice(undefined);
|
|
471
532
|
if (
|
|
472
533
|
options?.isInteractive === true &&
|
|
473
534
|
conversation.getCurrentSender() === broadcastMessage
|
|
@@ -521,9 +582,9 @@ export async function processMessageInBackground(
|
|
|
521
582
|
|
|
522
583
|
if (options?.isInteractive === true) {
|
|
523
584
|
conversation.updateClient(broadcastMessage, false);
|
|
585
|
+
getSubagentManager().updateParentSender(conversationId, broadcastMessage);
|
|
524
586
|
}
|
|
525
587
|
|
|
526
|
-
conversation.setSlackRuntimeContextNotice(options?.slackRuntimeContextNotice);
|
|
527
588
|
conversation
|
|
528
589
|
.runAgentLoop(content, messageId, emitEvent, {
|
|
529
590
|
isInteractive: options?.isInteractive ?? false,
|
|
@@ -531,7 +592,6 @@ export async function processMessageInBackground(
|
|
|
531
592
|
...(options?.callSite ? { callSite: options.callSite } : {}),
|
|
532
593
|
})
|
|
533
594
|
.finally(() => {
|
|
534
|
-
conversation.setSlackRuntimeContextNotice(undefined);
|
|
535
595
|
if (
|
|
536
596
|
options?.isInteractive === true &&
|
|
537
597
|
conversation.getCurrentSender() === broadcastMessage
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createTimeout,
|
|
3
|
+
extractText,
|
|
4
|
+
getConfiguredProvider,
|
|
5
|
+
userMessage,
|
|
6
|
+
} from "../providers/provider-send-message.js";
|
|
7
|
+
import { getLogger } from "../util/logger.js";
|
|
8
|
+
|
|
9
|
+
const log = getLogger("query-complexity-router");
|
|
10
|
+
|
|
11
|
+
export type ComplexityTier = "speed" | "balanced" | "quality";
|
|
12
|
+
|
|
13
|
+
const CLASSIFICATION_TIMEOUT_MS = 5_000;
|
|
14
|
+
|
|
15
|
+
const SYSTEM_PROMPT = `You are a query complexity classifier. Given a user message, classify its complexity into exactly one tier.
|
|
16
|
+
|
|
17
|
+
Reply with a single word — one of: speed, balanced, quality
|
|
18
|
+
|
|
19
|
+
- speed: trivial queries — greetings, acknowledgements, simple yes/no questions, basic factual lookups, short commands, small talk
|
|
20
|
+
- balanced: moderate queries — explanations, summaries, standard coding tasks, general conversation, most everyday requests
|
|
21
|
+
- quality: complex queries — deep analysis, long-form creative writing, complex multi-step reasoning, debugging intricate code, architectural design, research synthesis
|
|
22
|
+
|
|
23
|
+
When uncertain, reply "balanced".`;
|
|
24
|
+
|
|
25
|
+
export async function classifyQueryComplexity(
|
|
26
|
+
messageText: string,
|
|
27
|
+
): Promise<ComplexityTier | null> {
|
|
28
|
+
const provider = await getConfiguredProvider("queryComplexityRouter");
|
|
29
|
+
if (!provider) {
|
|
30
|
+
log.warn("No provider available for query complexity routing");
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const truncated =
|
|
35
|
+
messageText.length > 2000 ? messageText.slice(0, 2000) : messageText;
|
|
36
|
+
|
|
37
|
+
const { signal, cleanup } = createTimeout(CLASSIFICATION_TIMEOUT_MS);
|
|
38
|
+
try {
|
|
39
|
+
const response = await provider.sendMessage(
|
|
40
|
+
[userMessage(truncated)],
|
|
41
|
+
undefined,
|
|
42
|
+
SYSTEM_PROMPT,
|
|
43
|
+
{ signal },
|
|
44
|
+
);
|
|
45
|
+
const text = extractText(response).toLowerCase().trim();
|
|
46
|
+
if (text === "speed" || text === "balanced" || text === "quality") {
|
|
47
|
+
return text;
|
|
48
|
+
}
|
|
49
|
+
// Parse partial matches (model might say "speed." or "quality - because...")
|
|
50
|
+
if (text.startsWith("speed")) return "speed";
|
|
51
|
+
if (text.startsWith("quality")) return "quality";
|
|
52
|
+
if (text.startsWith("balanced")) return "balanced";
|
|
53
|
+
log.warn({ raw: text }, "Unexpected classifier output, defaulting to null");
|
|
54
|
+
return null;
|
|
55
|
+
} catch (err) {
|
|
56
|
+
if (signal.aborted) {
|
|
57
|
+
log.warn("Query complexity classification timed out");
|
|
58
|
+
} else {
|
|
59
|
+
log.warn({ err }, "Query complexity classification failed");
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
} finally {
|
|
63
|
+
cleanup();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const PROFILE_MAP: Record<ComplexityTier, string> = {
|
|
68
|
+
speed: "cost-optimized",
|
|
69
|
+
balanced: "balanced",
|
|
70
|
+
quality: "quality-optimized",
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export function complexityTierToProfileKey(tier: ComplexityTier): string {
|
|
74
|
+
return PROFILE_MAP[tier];
|
|
75
|
+
}
|
|
@@ -5,7 +5,10 @@ import {
|
|
|
5
5
|
seedUninstalledCatalogSkillMemories,
|
|
6
6
|
} from "../memory/graph/capability-seed.js";
|
|
7
7
|
import { getLogger } from "../util/logger.js";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
maybeSeedMemoryV2CliCommands,
|
|
10
|
+
maybeSeedMemoryV2Skills,
|
|
11
|
+
} from "./memory-v2-startup.js";
|
|
9
12
|
|
|
10
13
|
const log = getLogger("skill-memory-refresh");
|
|
11
14
|
|
|
@@ -14,6 +17,7 @@ export function refreshSkillCapabilityMemories(
|
|
|
14
17
|
): void {
|
|
15
18
|
seedSkillGraphNodes();
|
|
16
19
|
maybeSeedMemoryV2Skills(config);
|
|
20
|
+
maybeSeedMemoryV2CliCommands(config);
|
|
17
21
|
void seedUninstalledCatalogSkillMemories()
|
|
18
22
|
.then(() => {
|
|
19
23
|
// Re-run after the async catalog fetch populates the cache so stale
|
|
@@ -25,6 +25,12 @@ export interface TrustContext {
|
|
|
25
25
|
requesterSenderDisplayName?: string;
|
|
26
26
|
/** Guardian-managed display name from the contact record. */
|
|
27
27
|
requesterMemberDisplayName?: string;
|
|
28
|
+
/** Raw timezone for the requester, when supplied by the source channel. */
|
|
29
|
+
requesterTimezone?: string;
|
|
30
|
+
/** Compact timezone label for the requester, when supplied by the source channel. */
|
|
31
|
+
requesterTimezoneLabel?: string;
|
|
32
|
+
/** Raw timezone offset in seconds for the requester, when supplied by the source channel. */
|
|
33
|
+
requesterTimezoneOffsetSeconds?: number;
|
|
28
34
|
/** Canonical external user ID of the requester (the current actor). */
|
|
29
35
|
requesterExternalUserId?: string;
|
|
30
36
|
/** Chat/conversation ID the requester is interacting through. */
|