@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
|
@@ -8,6 +8,8 @@ import { join } from "node:path";
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
10
|
import { loadConfig } from "../../config/loader.js";
|
|
11
|
+
import type { AssistantConfig } from "../../config/types.js";
|
|
12
|
+
import { getDb } from "../../memory/db-connection.js";
|
|
11
13
|
import {
|
|
12
14
|
enqueueMemoryJob,
|
|
13
15
|
type MemoryJobType,
|
|
@@ -21,12 +23,16 @@ import {
|
|
|
21
23
|
totalEdgeCount,
|
|
22
24
|
validateEdgeTargets,
|
|
23
25
|
} from "../../memory/v2/edge-index.js";
|
|
26
|
+
import { computeInjectionScores } from "../../memory/v2/injection-events.js";
|
|
27
|
+
import { loadNowText } from "../../memory/v2/now-text.js";
|
|
28
|
+
import { getPageIndex } from "../../memory/v2/page-index.js";
|
|
24
29
|
import {
|
|
25
30
|
getConceptsDir,
|
|
26
31
|
listPages,
|
|
27
32
|
readPage,
|
|
28
33
|
renderPageContent,
|
|
29
34
|
} from "../../memory/v2/page-store.js";
|
|
35
|
+
import { type RouterSource, runRouter } from "../../memory/v2/router.js";
|
|
30
36
|
import { seedV2SkillEntries } from "../../memory/v2/skill-store.js";
|
|
31
37
|
import { getLogger } from "../../util/logger.js";
|
|
32
38
|
import { getWorkspaceDir } from "../../util/platform.js";
|
|
@@ -290,6 +296,195 @@ async function handleConceptFrequency({
|
|
|
290
296
|
return getConceptFrequencySummary(workspaceDir, { conversationId, sinceMs });
|
|
291
297
|
}
|
|
292
298
|
|
|
299
|
+
// ── EMA scores ──────────────────────────────────────────────────────────
|
|
300
|
+
|
|
301
|
+
const MemoryV2EmaScoresParams = z.object({}).strict();
|
|
302
|
+
|
|
303
|
+
export interface MemoryV2EmaScoresEntry {
|
|
304
|
+
slug: string;
|
|
305
|
+
/** Time-decayed injection frequency; 0 when no events in the read window. */
|
|
306
|
+
score: number;
|
|
307
|
+
/** File mtime in epoch ms; 0 for synthetic entries (skills, CLI commands). */
|
|
308
|
+
modifiedAt: number;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export interface MemoryV2EmaScoresResult {
|
|
312
|
+
/** Every page index entry, sorted by score descending then slug ASCII. */
|
|
313
|
+
entries: MemoryV2EmaScoresEntry[];
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function handleEmaScores({
|
|
317
|
+
body = {},
|
|
318
|
+
}: RouteHandlerArgs): Promise<MemoryV2EmaScoresResult> {
|
|
319
|
+
// Intentionally NOT gated on `memory.v2.enabled` — operators inspecting
|
|
320
|
+
// EMA data before flipping tier-2 routing on is a legitimate dry-run
|
|
321
|
+
// use case, mirroring `memory_v2_validate`.
|
|
322
|
+
MemoryV2EmaScoresParams.parse(body);
|
|
323
|
+
|
|
324
|
+
const pageIndex = await getPageIndex(getWorkspaceDir());
|
|
325
|
+
const slugs = pageIndex.entries.map((e) => e.slug);
|
|
326
|
+
const scores = computeInjectionScores(getDb(), slugs, Date.now());
|
|
327
|
+
|
|
328
|
+
const entries: MemoryV2EmaScoresEntry[] = pageIndex.entries.map((entry) => ({
|
|
329
|
+
slug: entry.slug,
|
|
330
|
+
score: scores.get(entry.slug) ?? 0,
|
|
331
|
+
modifiedAt: entry.modifiedAt,
|
|
332
|
+
}));
|
|
333
|
+
entries.sort((a, b) => {
|
|
334
|
+
if (a.score !== b.score) return b.score - a.score;
|
|
335
|
+
return a.slug < b.slug ? -1 : a.slug > b.slug ? 1 : 0;
|
|
336
|
+
});
|
|
337
|
+
return { entries };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ── Simulate router (dry-run playground) ────────────────────────────────
|
|
341
|
+
|
|
342
|
+
const SimulateRouterOverridesSchema = z
|
|
343
|
+
.object({
|
|
344
|
+
tier1_size: z.number().int().min(1).nullable().optional(),
|
|
345
|
+
tier2_size: z.number().int().min(1).nullable().optional(),
|
|
346
|
+
batch_size: z.number().int().min(1).nullable().optional(),
|
|
347
|
+
})
|
|
348
|
+
.strict();
|
|
349
|
+
|
|
350
|
+
const MemoryV2SimulateRouterParams = z
|
|
351
|
+
.object({
|
|
352
|
+
query: z.string().min(1, "query must be non-empty"),
|
|
353
|
+
configOverrides: SimulateRouterOverridesSchema.optional(),
|
|
354
|
+
})
|
|
355
|
+
.strict();
|
|
356
|
+
|
|
357
|
+
export interface MemoryV2SimulateRouterEffectiveConfig {
|
|
358
|
+
tier1_size: number | null;
|
|
359
|
+
tier2_size: number | null;
|
|
360
|
+
batch_size: number | null;
|
|
361
|
+
max_page_ids: number;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export interface MemoryV2SimulateRouterResult {
|
|
365
|
+
/** Slugs the router would select, in model-returned order. */
|
|
366
|
+
selectedSlugs: string[];
|
|
367
|
+
/** Per-slug provenance: `"tier1"`, `"tier2"`, or `"tier3:<bucket>"`. */
|
|
368
|
+
sourceBySlug: Record<string, RouterSource>;
|
|
369
|
+
/** EMA scores for the selected slugs (0 when the slug has no events). */
|
|
370
|
+
scores: Record<string, number>;
|
|
371
|
+
/** `null` on success; otherwise one of the router failure reasons. */
|
|
372
|
+
failureReason: string | null;
|
|
373
|
+
/** The router config that actually ran (live merged with overrides). */
|
|
374
|
+
effectiveConfig: MemoryV2SimulateRouterEffectiveConfig;
|
|
375
|
+
/** The overrides the caller submitted, for display. */
|
|
376
|
+
overrides: {
|
|
377
|
+
tier1_size?: number | null;
|
|
378
|
+
tier2_size?: number | null;
|
|
379
|
+
batch_size?: number | null;
|
|
380
|
+
};
|
|
381
|
+
/** Page index size the router was given (post-tier-carve, all batches). */
|
|
382
|
+
totalCandidatePages: number;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Build the config the router will see by overlaying override values on top
|
|
387
|
+
* of the live workspace config. Only the three new tier knobs are exposed —
|
|
388
|
+
* everything else (provider, prompts, weights) stays exactly as it would on
|
|
389
|
+
* a real turn. `undefined` means "inherit live"; `null` is a valid override
|
|
390
|
+
* value (meaning "disable this tier").
|
|
391
|
+
*/
|
|
392
|
+
function applySimulateOverrides(
|
|
393
|
+
live: AssistantConfig,
|
|
394
|
+
overrides: z.infer<typeof SimulateRouterOverridesSchema> | undefined,
|
|
395
|
+
): AssistantConfig {
|
|
396
|
+
if (!overrides) return live;
|
|
397
|
+
const liveRouter = live.memory.v2.router;
|
|
398
|
+
const mergedRouter = {
|
|
399
|
+
...liveRouter,
|
|
400
|
+
...("tier1_size" in overrides && overrides.tier1_size !== undefined
|
|
401
|
+
? { tier1_size: overrides.tier1_size }
|
|
402
|
+
: {}),
|
|
403
|
+
...("tier2_size" in overrides && overrides.tier2_size !== undefined
|
|
404
|
+
? { tier2_size: overrides.tier2_size }
|
|
405
|
+
: {}),
|
|
406
|
+
...("batch_size" in overrides && overrides.batch_size !== undefined
|
|
407
|
+
? { batch_size: overrides.batch_size }
|
|
408
|
+
: {}),
|
|
409
|
+
};
|
|
410
|
+
return {
|
|
411
|
+
...live,
|
|
412
|
+
memory: {
|
|
413
|
+
...live.memory,
|
|
414
|
+
v2: {
|
|
415
|
+
...live.memory.v2,
|
|
416
|
+
router: mergedRouter,
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
export async function handleSimulateRouter({
|
|
423
|
+
body = {},
|
|
424
|
+
}: RouteHandlerArgs): Promise<MemoryV2SimulateRouterResult> {
|
|
425
|
+
requireMemoryV2Enabled();
|
|
426
|
+
const { query, configOverrides } = MemoryV2SimulateRouterParams.parse(body);
|
|
427
|
+
|
|
428
|
+
const liveConfig = loadConfig();
|
|
429
|
+
const mergedConfig = applySimulateOverrides(liveConfig, configOverrides);
|
|
430
|
+
const effectiveRouter = mergedConfig.memory.v2.router;
|
|
431
|
+
|
|
432
|
+
const workspaceDir = getWorkspaceDir();
|
|
433
|
+
const nowText = await loadNowText(workspaceDir);
|
|
434
|
+
|
|
435
|
+
const routerResult = await runRouter({
|
|
436
|
+
workspaceDir,
|
|
437
|
+
userMessage: query,
|
|
438
|
+
assistantMessage: "",
|
|
439
|
+
nowText,
|
|
440
|
+
priorEverInjected: [],
|
|
441
|
+
config: mergedConfig,
|
|
442
|
+
database: getDb(),
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
const pageIndex = await getPageIndex(workspaceDir);
|
|
446
|
+
const scores = computeInjectionScores(
|
|
447
|
+
getDb(),
|
|
448
|
+
routerResult.selectedSlugs,
|
|
449
|
+
Date.now(),
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
const sourceBySlug: Record<string, RouterSource> = {};
|
|
453
|
+
for (const [slug, source] of routerResult.sourceBySlug.entries()) {
|
|
454
|
+
sourceBySlug[slug] = source;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const scoresOut: Record<string, number> = {};
|
|
458
|
+
for (const slug of routerResult.selectedSlugs) {
|
|
459
|
+
scoresOut[slug] = scores.get(slug) ?? 0;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
selectedSlugs: routerResult.selectedSlugs,
|
|
464
|
+
sourceBySlug,
|
|
465
|
+
scores: scoresOut,
|
|
466
|
+
failureReason: routerResult.failureReason,
|
|
467
|
+
effectiveConfig: {
|
|
468
|
+
tier1_size: effectiveRouter.tier1_size,
|
|
469
|
+
tier2_size: effectiveRouter.tier2_size,
|
|
470
|
+
batch_size: effectiveRouter.batch_size,
|
|
471
|
+
max_page_ids: effectiveRouter.max_page_ids,
|
|
472
|
+
},
|
|
473
|
+
overrides: {
|
|
474
|
+
...(configOverrides?.tier1_size !== undefined
|
|
475
|
+
? { tier1_size: configOverrides.tier1_size }
|
|
476
|
+
: {}),
|
|
477
|
+
...(configOverrides?.tier2_size !== undefined
|
|
478
|
+
? { tier2_size: configOverrides.tier2_size }
|
|
479
|
+
: {}),
|
|
480
|
+
...(configOverrides?.batch_size !== undefined
|
|
481
|
+
? { batch_size: configOverrides.batch_size }
|
|
482
|
+
: {}),
|
|
483
|
+
},
|
|
484
|
+
totalCandidatePages: pageIndex.entries.length,
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
293
488
|
// ── Route definitions ───────────────────────────────────────────────────
|
|
294
489
|
|
|
295
490
|
export const ROUTES: RouteDefinition[] = [
|
|
@@ -359,4 +554,26 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
359
554
|
tags: ["memory"],
|
|
360
555
|
requestBody: MemoryV2ConceptFrequencyParams,
|
|
361
556
|
},
|
|
557
|
+
{
|
|
558
|
+
operationId: "memory_v2_ema_scores",
|
|
559
|
+
method: "POST",
|
|
560
|
+
endpoint: "memory/v2/ema-scores",
|
|
561
|
+
handler: handleEmaScores,
|
|
562
|
+
summary: "List every concept page with its injection-frequency EMA score",
|
|
563
|
+
description:
|
|
564
|
+
"Computes the time-decayed injection frequency (3-day half-life) for every entry in the current page index by reading memory_v2_injection_events. Returns entries sorted by score descending then slug ASCII, including zero-score pages so callers can decide whether to filter. Read-only; tier 2 of the v4 router uses the same computation to pick its top-M.",
|
|
565
|
+
tags: ["memory"],
|
|
566
|
+
requestBody: MemoryV2EmaScoresParams,
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
operationId: "memory_v2_simulate_router",
|
|
570
|
+
method: "POST",
|
|
571
|
+
endpoint: "memory/v2/simulate-router",
|
|
572
|
+
handler: handleSimulateRouter,
|
|
573
|
+
summary: "Dry-run the v4 router with config overrides (read-only)",
|
|
574
|
+
description:
|
|
575
|
+
"Runs the memory router against the live page index + EMA scores with optional tier_size / batch_size overrides, without recording an injection event or writing an activation log. Returns the slugs that would have been selected, per-slug tier provenance, EMA scores, and the effective router config so operators can validate knob changes before flipping them in workspace config.",
|
|
576
|
+
tags: ["memory"],
|
|
577
|
+
requestBody: MemoryV2SimulateRouterParams,
|
|
578
|
+
},
|
|
362
579
|
];
|
|
@@ -7,6 +7,7 @@ import { z } from "zod";
|
|
|
7
7
|
|
|
8
8
|
import { getDb } from "../../memory/db-connection.js";
|
|
9
9
|
import { notificationDeliveries } from "../../memory/schema.js";
|
|
10
|
+
import { bufferIfDeferred } from "../../notifications/deferred-emit.js";
|
|
10
11
|
import { emitNotificationSignal } from "../../notifications/emit-signal.js";
|
|
11
12
|
import { listEvents } from "../../notifications/events-store.js";
|
|
12
13
|
import type { AttentionHints } from "../../notifications/signal.js";
|
|
@@ -78,6 +79,9 @@ const EmitSignalParams = z.object({
|
|
|
78
79
|
conversationAffinityHint: z.record(z.string(), z.string()).optional(),
|
|
79
80
|
dedupeKey: z.string().optional(),
|
|
80
81
|
throwOnError: z.boolean().optional(),
|
|
82
|
+
// Conversation that originated this signal — used by `deferred-emit` to
|
|
83
|
+
// buffer notifications during in-band background-job tool calls.
|
|
84
|
+
originatingConversationId: z.string().optional(),
|
|
81
85
|
});
|
|
82
86
|
|
|
83
87
|
const ListNotificationEventsParams = z.object({
|
|
@@ -89,7 +93,7 @@ const ListNotificationEventsParams = z.object({
|
|
|
89
93
|
|
|
90
94
|
async function handleEmitSignal({ body = {} }: RouteHandlerArgs) {
|
|
91
95
|
const validated = EmitSignalParams.parse(body);
|
|
92
|
-
const
|
|
96
|
+
const params = {
|
|
93
97
|
sourceEventName: validated.sourceEventName,
|
|
94
98
|
sourceChannel: validated.sourceChannel,
|
|
95
99
|
sourceContextId: validated.sourceContextId,
|
|
@@ -99,7 +103,20 @@ async function handleEmitSignal({ body = {} }: RouteHandlerArgs) {
|
|
|
99
103
|
conversationAffinityHint: validated.conversationAffinityHint,
|
|
100
104
|
dedupeKey: validated.dedupeKey,
|
|
101
105
|
throwOnError: validated.throwOnError,
|
|
102
|
-
}
|
|
106
|
+
};
|
|
107
|
+
const buffered = bufferIfDeferred(
|
|
108
|
+
validated.originatingConversationId,
|
|
109
|
+
params,
|
|
110
|
+
);
|
|
111
|
+
if (buffered) {
|
|
112
|
+
return {
|
|
113
|
+
signalId: buffered.signalId,
|
|
114
|
+
dispatched: buffered.dispatched,
|
|
115
|
+
deduplicated: buffered.deduplicated,
|
|
116
|
+
reason: buffered.reason,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const result = await emitNotificationSignal(params);
|
|
103
120
|
return {
|
|
104
121
|
signalId: result.signalId,
|
|
105
122
|
dispatched: result.dispatched,
|
|
@@ -148,7 +148,10 @@ function handleQuestionResponse({ body }: RouteHandlerArgs) {
|
|
|
148
148
|
|
|
149
149
|
// Validation passed — deregister now to clear the prompter timer, then
|
|
150
150
|
// hand the result to the prompter's caller via rpcResolve.
|
|
151
|
-
pendingInteractions.resolve(
|
|
151
|
+
pendingInteractions.resolve(
|
|
152
|
+
requestId,
|
|
153
|
+
response.kind === "close" ? "cancelled" : "answered",
|
|
154
|
+
);
|
|
152
155
|
|
|
153
156
|
log.info(
|
|
154
157
|
{
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route definitions for Sanity CMS connection management.
|
|
3
|
+
*
|
|
4
|
+
* POST /v1/sanity/discover — project/dataset discovery using the stored API token
|
|
5
|
+
* POST /v1/sanity/connect — finalise connection by writing the sidecar file
|
|
6
|
+
*
|
|
7
|
+
* Both routes use policyKey: "secrets" — they read credentials and write
|
|
8
|
+
* workspace data, the same policy tier as the existing secrets routes.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
|
|
16
|
+
import { credentialKey } from "../../security/credential-key.js";
|
|
17
|
+
import { getSecureKeyAsync } from "../../security/secure-keys.js";
|
|
18
|
+
import { getWorkspaceDir } from "../../util/platform.js";
|
|
19
|
+
import { BadRequestError } from "./errors.js";
|
|
20
|
+
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Helpers
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
async function getStoredToken(): Promise<string | undefined> {
|
|
27
|
+
return getSecureKeyAsync(credentialKey("sanity", "api_token"));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function writeSidecar(relPath: string, data: Record<string, unknown>): void {
|
|
31
|
+
const workspaceDir = getWorkspaceDir();
|
|
32
|
+
const absPath = join(workspaceDir, relPath);
|
|
33
|
+
mkdirSync(dirname(absPath), { recursive: true });
|
|
34
|
+
writeFileSync(absPath, JSON.stringify(data, null, 2), "utf-8");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// POST /v1/sanity/discover
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
async function handleDiscover(
|
|
42
|
+
args: RouteHandlerArgs,
|
|
43
|
+
): Promise<Record<string, unknown>> {
|
|
44
|
+
const token = await getStoredToken();
|
|
45
|
+
if (!token) {
|
|
46
|
+
return { error: "no_token" };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const projectId =
|
|
50
|
+
typeof args.body?.projectId === "string" ? args.body.projectId : undefined;
|
|
51
|
+
|
|
52
|
+
if (!projectId) {
|
|
53
|
+
// List all projects this token has access to
|
|
54
|
+
const response = await fetch("https://api.sanity.io/v2021-06-07/projects", {
|
|
55
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (response.status === 401 || response.status === 403) {
|
|
59
|
+
return { error: "token_scope_limited" };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
return { error: "discovery_failed" };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const raw = (await response.json()) as Array<{
|
|
67
|
+
id: string;
|
|
68
|
+
displayName?: string;
|
|
69
|
+
}>;
|
|
70
|
+
const projects = raw.map((p) => ({
|
|
71
|
+
id: p.id,
|
|
72
|
+
displayName: p.displayName ?? p.id,
|
|
73
|
+
}));
|
|
74
|
+
return { projects };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// List datasets for a specific project
|
|
78
|
+
const response = await fetch(
|
|
79
|
+
`https://api.sanity.io/v2021-06-07/projects/${projectId}/datasets`,
|
|
80
|
+
{
|
|
81
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (response.status === 401 || response.status === 403) {
|
|
86
|
+
return { error: "token_scope_limited" };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
return { error: "discovery_failed" };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const raw = (await response.json()) as Array<{ name: string }>;
|
|
94
|
+
const datasets = raw.map((d) => d.name);
|
|
95
|
+
return { projectId, datasets };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// POST /v1/sanity/connect
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
async function handleConnect(
|
|
103
|
+
args: RouteHandlerArgs,
|
|
104
|
+
): Promise<Record<string, unknown>> {
|
|
105
|
+
const token = await getStoredToken();
|
|
106
|
+
if (!token) {
|
|
107
|
+
throw new BadRequestError(
|
|
108
|
+
"Sanity API token not found. Store it first via POST /v1/secrets.",
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const projectId =
|
|
113
|
+
typeof args.body?.projectId === "string" ? args.body.projectId.trim() : "";
|
|
114
|
+
const dataset =
|
|
115
|
+
typeof args.body?.dataset === "string" ? args.body.dataset.trim() : "";
|
|
116
|
+
|
|
117
|
+
if (!projectId) {
|
|
118
|
+
throw new BadRequestError(
|
|
119
|
+
"projectId is required and must be a non-empty string",
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
if (!dataset) {
|
|
123
|
+
throw new BadRequestError(
|
|
124
|
+
"dataset is required and must be a non-empty string",
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
writeSidecar("data/sanity-connection.json", { projectId, dataset });
|
|
129
|
+
|
|
130
|
+
return { ok: true };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// Route definitions
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
export const ROUTES: RouteDefinition[] = [
|
|
138
|
+
{
|
|
139
|
+
operationId: "sanity_discover",
|
|
140
|
+
endpoint: "sanity/discover",
|
|
141
|
+
method: "POST",
|
|
142
|
+
policyKey: "secrets",
|
|
143
|
+
summary: "Discover Sanity projects and datasets using the stored API token",
|
|
144
|
+
requestBody: z.object({ projectId: z.string().optional() }).optional(),
|
|
145
|
+
handler: handleDiscover,
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
operationId: "sanity_connect",
|
|
149
|
+
endpoint: "sanity/connect",
|
|
150
|
+
method: "POST",
|
|
151
|
+
policyKey: "secrets",
|
|
152
|
+
summary: "Finalise Sanity connection and write sidecar file",
|
|
153
|
+
requestBody: z.object({
|
|
154
|
+
projectId: z.string(),
|
|
155
|
+
dataset: z.string(),
|
|
156
|
+
}),
|
|
157
|
+
handler: handleConnect,
|
|
158
|
+
},
|
|
159
|
+
];
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
conversationMetadataSyncTag,
|
|
5
|
+
SYNC_TAGS,
|
|
6
|
+
} from "../../daemon/message-types/sync.js";
|
|
7
|
+
import {
|
|
8
|
+
getBindingByConversation,
|
|
9
|
+
updateExternalChatName,
|
|
10
|
+
} from "../../memory/external-conversation-store.js";
|
|
11
|
+
import {
|
|
12
|
+
getSlackConversationInfo,
|
|
13
|
+
SlackApiError,
|
|
14
|
+
} from "../../messaging/providers/slack/api.js";
|
|
15
|
+
import { publishSyncInvalidation } from "../sync/sync-publisher.js";
|
|
16
|
+
import { NotFoundError } from "./errors.js";
|
|
17
|
+
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
18
|
+
|
|
19
|
+
type ResolveReason =
|
|
20
|
+
| "auth"
|
|
21
|
+
| "dm"
|
|
22
|
+
| "no_name"
|
|
23
|
+
| "not_found"
|
|
24
|
+
| "permission"
|
|
25
|
+
| "rate_limit"
|
|
26
|
+
| "slack_error";
|
|
27
|
+
|
|
28
|
+
interface SlackChannelResolveResponse {
|
|
29
|
+
channelId: string;
|
|
30
|
+
channelName?: string;
|
|
31
|
+
cached: boolean;
|
|
32
|
+
resolved: boolean;
|
|
33
|
+
reason?: ResolveReason;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const SlackChannelResolveResponseSchema = z.object({
|
|
37
|
+
channelId: z.string(),
|
|
38
|
+
channelName: z.string().optional(),
|
|
39
|
+
cached: z.boolean(),
|
|
40
|
+
resolved: z.boolean(),
|
|
41
|
+
reason: z
|
|
42
|
+
.enum([
|
|
43
|
+
"auth",
|
|
44
|
+
"dm",
|
|
45
|
+
"no_name",
|
|
46
|
+
"not_found",
|
|
47
|
+
"permission",
|
|
48
|
+
"rate_limit",
|
|
49
|
+
"slack_error",
|
|
50
|
+
])
|
|
51
|
+
.optional(),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
function friendlyCachedName(
|
|
55
|
+
externalChatId: string,
|
|
56
|
+
externalChatName?: string | null,
|
|
57
|
+
): string | undefined {
|
|
58
|
+
const trimmed = externalChatName?.trim();
|
|
59
|
+
if (!trimmed || trimmed === externalChatId) return undefined;
|
|
60
|
+
return trimmed;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function usableResolvedName(
|
|
64
|
+
externalChatId: string,
|
|
65
|
+
name?: string,
|
|
66
|
+
nameNormalized?: string,
|
|
67
|
+
): string | undefined {
|
|
68
|
+
for (const candidate of [name, nameNormalized]) {
|
|
69
|
+
const trimmed = candidate?.trim();
|
|
70
|
+
if (trimmed && trimmed !== externalChatId) return trimmed;
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function reasonForSlackError(err: unknown): ResolveReason {
|
|
76
|
+
if (err instanceof SlackApiError) {
|
|
77
|
+
switch (err.category) {
|
|
78
|
+
case "auth":
|
|
79
|
+
return "auth";
|
|
80
|
+
case "channel_not_found":
|
|
81
|
+
case "not_found":
|
|
82
|
+
return "not_found";
|
|
83
|
+
case "permission":
|
|
84
|
+
return "permission";
|
|
85
|
+
case "rate_limit":
|
|
86
|
+
return "rate_limit";
|
|
87
|
+
default:
|
|
88
|
+
return "slack_error";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return "slack_error";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function handleSlackChannelNameResolve({
|
|
95
|
+
pathParams = {},
|
|
96
|
+
}: RouteHandlerArgs): Promise<SlackChannelResolveResponse> {
|
|
97
|
+
const conversationId = pathParams.conversationId?.trim();
|
|
98
|
+
if (!conversationId) {
|
|
99
|
+
throw new NotFoundError("Conversation not found");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const binding = getBindingByConversation(conversationId);
|
|
103
|
+
|
|
104
|
+
if (!binding || binding.sourceChannel !== "slack") {
|
|
105
|
+
throw new NotFoundError("Conversation not found");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const channelId = binding.externalChatId;
|
|
109
|
+
const cachedName = friendlyCachedName(channelId, binding.externalChatName);
|
|
110
|
+
if (cachedName) {
|
|
111
|
+
return {
|
|
112
|
+
channelId,
|
|
113
|
+
channelName: cachedName,
|
|
114
|
+
cached: true,
|
|
115
|
+
resolved: true,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (channelId.startsWith("D")) {
|
|
120
|
+
return {
|
|
121
|
+
channelId,
|
|
122
|
+
cached: false,
|
|
123
|
+
resolved: false,
|
|
124
|
+
reason: "dm",
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let info;
|
|
129
|
+
try {
|
|
130
|
+
info = await getSlackConversationInfo(channelId);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
return {
|
|
133
|
+
channelId,
|
|
134
|
+
cached: false,
|
|
135
|
+
resolved: false,
|
|
136
|
+
reason: reasonForSlackError(err),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const channelName = usableResolvedName(
|
|
141
|
+
channelId,
|
|
142
|
+
info?.name,
|
|
143
|
+
info?.nameNormalized,
|
|
144
|
+
);
|
|
145
|
+
if (!channelName) {
|
|
146
|
+
return {
|
|
147
|
+
channelId,
|
|
148
|
+
cached: false,
|
|
149
|
+
resolved: false,
|
|
150
|
+
reason: "no_name",
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
updateExternalChatName(conversationId, channelName);
|
|
155
|
+
await publishSyncInvalidation([
|
|
156
|
+
SYNC_TAGS.conversationsList,
|
|
157
|
+
conversationMetadataSyncTag(conversationId),
|
|
158
|
+
]);
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
channelId,
|
|
162
|
+
channelName,
|
|
163
|
+
cached: false,
|
|
164
|
+
resolved: true,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const ROUTES: RouteDefinition[] = [
|
|
169
|
+
{
|
|
170
|
+
operationId: "slack_channel_name_resolve",
|
|
171
|
+
endpoint: "conversations/:conversationId/slack-channel/resolve",
|
|
172
|
+
method: "POST",
|
|
173
|
+
handler: handleSlackChannelNameResolve,
|
|
174
|
+
summary: "Resolve Slack channel name",
|
|
175
|
+
description:
|
|
176
|
+
"Resolve and persist a friendly Slack channel name for an external conversation binding.",
|
|
177
|
+
tags: ["conversations", "slack"],
|
|
178
|
+
pathParams: [
|
|
179
|
+
{
|
|
180
|
+
name: "conversationId",
|
|
181
|
+
type: "string",
|
|
182
|
+
description: "Conversation ID whose Slack channel name should resolve.",
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
responseBody: SlackChannelResolveResponseSchema,
|
|
186
|
+
},
|
|
187
|
+
];
|
|
@@ -188,6 +188,47 @@ function getSubagentDetail(
|
|
|
188
188
|
// ---------------------------------------------------------------------------
|
|
189
189
|
|
|
190
190
|
export const ROUTES: RouteDefinition[] = [
|
|
191
|
+
{
|
|
192
|
+
operationId: "reconcileSubagents",
|
|
193
|
+
endpoint: "subagents/reconcile",
|
|
194
|
+
method: "GET",
|
|
195
|
+
policyKey: "subagents",
|
|
196
|
+
summary: "Reconcile subagent live status",
|
|
197
|
+
description:
|
|
198
|
+
"Returns the live in-memory status of all subagents known to the daemon for a given parent conversation. Subagents not in the response are orphaned.",
|
|
199
|
+
tags: ["subagents"],
|
|
200
|
+
queryParams: [
|
|
201
|
+
{
|
|
202
|
+
name: "parentConversationId",
|
|
203
|
+
schema: { type: "string" },
|
|
204
|
+
description: "Parent conversation ID",
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
responseBody: z.object({
|
|
208
|
+
subagents: z.record(
|
|
209
|
+
z.string(),
|
|
210
|
+
z.object({
|
|
211
|
+
status: z.string(),
|
|
212
|
+
}),
|
|
213
|
+
),
|
|
214
|
+
}),
|
|
215
|
+
handler: ({ queryParams }) => {
|
|
216
|
+
const parentConversationId = queryParams?.parentConversationId;
|
|
217
|
+
if (!parentConversationId) {
|
|
218
|
+
throw new BadRequestError(
|
|
219
|
+
"parentConversationId query parameter is required",
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
const manager = getSubagentManager();
|
|
223
|
+
const children = manager.getChildrenOf(parentConversationId);
|
|
224
|
+
const subagents: Record<string, { status: string }> = {};
|
|
225
|
+
for (const child of children) {
|
|
226
|
+
subagents[child.config.id] = { status: child.status };
|
|
227
|
+
}
|
|
228
|
+
return { subagents };
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
|
|
191
232
|
{
|
|
192
233
|
operationId: "getSubagentDetail",
|
|
193
234
|
endpoint: "subagents/:id",
|