@vellumai/assistant 0.6.4 → 0.6.5
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/.prettierignore +5 -0
- package/ARCHITECTURE.md +32 -36
- package/Dockerfile +12 -0
- package/README.md +3 -4
- package/bun.lock +8 -3
- package/docs/architecture/integrations.md +1 -20
- package/docs/architecture/security.md +16 -16
- package/docs/error-handling.md +111 -0
- package/docs/skills.md +10 -10
- package/docs/stt-provider-onboarding.md +2 -1
- package/knip.json +9 -2
- package/node_modules/@vellumai/ces-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
- package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
- package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
- package/node_modules/@vellumai/credential-storage/package.json +2 -2
- package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
- package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
- package/node_modules/@vellumai/egress-proxy/package.json +2 -2
- package/openapi.yaml +123 -11
- package/package.json +6 -3
- package/scripts/generate-openapi.ts +50 -11
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
- package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
- package/src/__tests__/agent-loop.test.ts +112 -1
- package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
- package/src/__tests__/anthropic-provider.test.ts +171 -2
- package/src/__tests__/approval-cascade.test.ts +31 -10
- package/src/__tests__/approval-routes-http.test.ts +134 -10
- package/src/__tests__/assistant-attachments.test.ts +44 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
- package/src/__tests__/browser-skill-endstate.test.ts +51 -182
- package/src/__tests__/btw-routes.test.ts +47 -1
- package/src/__tests__/call-controller.test.ts +1 -2
- package/src/__tests__/call-site-routing-provider.test.ts +214 -0
- package/src/__tests__/catalog-cache.test.ts +27 -4
- package/src/__tests__/channel-approval-routes.test.ts +4 -4
- package/src/__tests__/channel-reply-delivery.test.ts +300 -2
- package/src/__tests__/checker.test.ts +428 -501
- package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
- package/src/__tests__/compaction-circuit-breaker.test.ts +336 -0
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/config-analysis.test.ts +11 -28
- package/src/__tests__/config-loader-backfill.test.ts +174 -0
- package/src/__tests__/config-loader-corrupt.test.ts +183 -0
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
- package/src/__tests__/config-schema-cmd.test.ts +11 -5
- package/src/__tests__/config-schema.test.ts +427 -114
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +72 -73
- package/src/__tests__/contacts-write.test.ts +4 -4
- package/src/__tests__/context-token-estimator.test.ts +191 -1
- package/src/__tests__/context-window-manager.test.ts +530 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +30 -16
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +61 -17
- package/src/__tests__/conversation-agent-loop.test.ts +412 -82
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +30 -9
- package/src/__tests__/conversation-error.test.ts +37 -6
- package/src/__tests__/conversation-history-web-search.test.ts +6 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +36 -0
- package/src/__tests__/conversation-lifecycle.test.ts +336 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +30 -16
- package/src/__tests__/conversation-process-callsite.test.ts +306 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -16
- package/src/__tests__/conversation-queue.test.ts +41 -26
- package/src/__tests__/conversation-routes-disk-view.test.ts +29 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +2735 -55
- package/src/__tests__/conversation-runtime-workspace.test.ts +12 -12
- package/src/__tests__/conversation-skill-tools.test.ts +12 -146
- package/src/__tests__/conversation-slash-queue.test.ts +34 -19
- package/src/__tests__/conversation-slash-unknown.test.ts +30 -16
- package/src/__tests__/conversation-speed-override.test.ts +30 -11
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -2
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
- package/src/__tests__/conversation-unread-route.test.ts +2 -2
- package/src/__tests__/conversation-usage.test.ts +3 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +31 -10
- package/src/__tests__/conversation-workspace-injection.test.ts +43 -15
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +44 -16
- package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
- package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
- package/src/__tests__/credential-vault-unit.test.ts +135 -19
- package/src/__tests__/credentials-cli.test.ts +1 -9
- package/src/__tests__/cross-provider-web-search.test.ts +84 -0
- package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
- package/src/__tests__/delete-propagation.test.ts +437 -0
- package/src/__tests__/dm-backfill.test.ts +417 -0
- package/src/__tests__/dm-persistence.test.ts +227 -0
- package/src/__tests__/edit-propagation.test.ts +280 -0
- package/src/__tests__/ephemeral-permissions.test.ts +93 -3
- package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
- package/src/__tests__/estimator-calibration.test.ts +213 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +26 -7
- package/src/__tests__/file-write-tool.test.ts +151 -1
- package/src/__tests__/filing-service.test.ts +255 -0
- package/src/__tests__/gemini-provider.test.ts +0 -3
- package/src/__tests__/guardian-grant-minting.test.ts +8 -0
- package/src/__tests__/headless-browser-interactions.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +96 -15
- package/src/__tests__/host-shell-tool.test.ts +124 -18
- package/src/__tests__/http-user-message-parity.test.ts +29 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
- package/src/__tests__/intent-routing.test.ts +1 -40
- package/src/__tests__/llm-catalog-parity.test.ts +174 -0
- package/src/__tests__/llm-context-normalization.test.ts +121 -0
- package/src/__tests__/llm-resolver.test.ts +214 -0
- package/src/__tests__/llm-schema.test.ts +223 -0
- package/src/__tests__/managed-proxy-context.test.ts +6 -2
- package/src/__tests__/messaging-skill-split.test.ts +3 -34
- package/src/__tests__/migration-import-from-url.test.ts +684 -0
- package/src/__tests__/model-intents.test.ts +9 -83
- package/src/__tests__/notification-decision-fallback.test.ts +0 -10
- package/src/__tests__/notification-decision-identity.test.ts +0 -9
- package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
- package/src/__tests__/oauth-store.test.ts +10 -7
- package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
- package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
- package/src/__tests__/openai-provider.test.ts +7 -0
- package/src/__tests__/openai-responses-provider.test.ts +396 -0
- package/src/__tests__/openrouter-provider-only.test.ts +135 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +1 -1
- package/src/__tests__/permission-mode.test.ts +16 -0
- package/src/__tests__/permission-types.test.ts +0 -1
- package/src/__tests__/persona-resolver.test.ts +13 -13
- package/src/__tests__/pkb-autoinject.test.ts +37 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
- package/src/__tests__/pricing.test.ts +50 -3
- package/src/__tests__/profiler-routes.test.ts +1 -1
- package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
- package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
- package/src/__tests__/provider-error-scenarios.test.ts +135 -6
- package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
- package/src/__tests__/provider-registry-ollama.test.ts +1 -2
- package/src/__tests__/proxy-approval-callback.test.ts +0 -1
- package/src/__tests__/reaction-persistence.test.ts +560 -0
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/require-fresh-approval.test.ts +1 -1
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
- package/src/__tests__/risk-classifier-parity.test.ts +230 -0
- package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
- package/src/__tests__/secret-ingress-http.test.ts +28 -0
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
- package/src/__tests__/secret-scanner-executor.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +29 -1
- package/src/__tests__/server-history-render.test.ts +31 -0
- package/src/__tests__/shell-parser-property.test.ts +13 -13
- package/src/__tests__/skill-cache-store.test.ts +182 -0
- package/src/__tests__/skills.test.ts +19 -33
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-skill.test.ts +3 -8
- package/src/__tests__/starter-bundle.test.ts +35 -0
- package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
- package/src/__tests__/suggestion-routes.test.ts +160 -3
- package/src/__tests__/system-prompt.test.ts +22 -35
- package/src/__tests__/task-runner.test.ts +3 -1
- package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
- package/src/__tests__/terminal-tools.test.ts +8 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
- package/src/__tests__/thread-backfill.test.ts +941 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
- package/src/__tests__/tool-executor.test.ts +60 -94
- package/src/__tests__/trust-store.test.ts +442 -109
- package/src/__tests__/update-bulletin-job.test.ts +389 -0
- package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
- package/src/__tests__/voice-session-bridge.test.ts +39 -0
- package/src/__tests__/volume-security-guard.test.ts +3 -2
- package/src/__tests__/web-search-history.test.ts +337 -0
- package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
- package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
- package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
- package/src/__tests__/workspace-policy.test.ts +1 -13
- package/src/acp/client-handler.ts +1 -2
- package/src/agent/loop.ts +209 -17
- package/src/avatar/resvg-lazy.test.ts +136 -0
- package/src/avatar/resvg-lazy.ts +82 -9
- package/src/avatar/traits-png-sync.ts +21 -1
- package/src/browser/__tests__/operations.test.ts +163 -0
- package/src/browser/identifiers.ts +51 -0
- package/src/browser/operations.ts +660 -0
- package/src/browser/types.ts +81 -0
- package/src/calls/guardian-question-copy.ts +2 -2
- package/src/calls/telephony-stt-routing.ts +1 -1
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/__tests__/attachment.test.ts +438 -0
- package/src/cli/commands/__tests__/browser.test.ts +554 -0
- package/src/cli/commands/__tests__/cache.test.ts +623 -0
- package/src/cli/commands/__tests__/email-list.test.ts +6 -0
- package/src/cli/commands/__tests__/email-send.test.ts +93 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +666 -0
- package/src/cli/commands/__tests__/inference-send.test.ts +451 -0
- package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
- package/src/cli/commands/__tests__/task.test.ts +913 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +594 -0
- package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
- package/src/cli/commands/__tests__/ui.test.ts +1215 -0
- package/src/cli/commands/__tests__/watchers.test.ts +716 -0
- package/src/cli/commands/attachment.ts +182 -0
- package/src/cli/commands/browser.ts +350 -0
- package/src/cli/commands/cache.ts +341 -0
- package/src/cli/commands/completions.ts +0 -3
- package/src/cli/commands/config.ts +6 -6
- package/src/cli/commands/conversations-import.ts +347 -0
- package/src/cli/commands/conversations.ts +14 -1
- package/src/cli/commands/email.ts +234 -194
- package/src/cli/commands/image-generation.ts +300 -0
- package/src/cli/commands/inference.ts +200 -0
- package/src/cli/commands/memory.ts +127 -17
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
- package/src/cli/commands/stt.ts +339 -0
- package/src/cli/commands/task.ts +795 -0
- package/src/cli/commands/trust.ts +50 -19
- package/src/cli/commands/tts.ts +273 -0
- package/src/cli/commands/ui.ts +670 -0
- package/src/cli/commands/watchers.ts +509 -0
- package/src/cli/lib/daemon-credential-client.ts +0 -19
- package/src/cli/program.ts +23 -4
- package/src/cli.ts +0 -37
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +23 -1
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +2 -2
- package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +8 -1
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +11 -12
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-tool-registry.ts +0 -175
- package/src/config/env.ts +7 -2
- package/src/config/feature-flag-registry.json +25 -9
- package/src/config/llm-resolver.ts +128 -0
- package/src/config/loader.ts +194 -10
- package/src/config/raw-config-utils.ts +30 -2
- package/src/config/sanitize-for-transfer.ts +35 -0
- package/src/config/schema.ts +30 -41
- package/src/config/schemas/analysis.ts +3 -22
- package/src/config/schemas/calls.ts +0 -4
- package/src/config/schemas/filing.ts +2 -7
- package/src/config/schemas/heartbeat.ts +0 -5
- package/src/config/schemas/inference.ts +3 -23
- package/src/config/schemas/llm.ts +318 -0
- package/src/config/schemas/memory-processing.ts +1 -9
- package/src/config/schemas/notifications.ts +4 -11
- package/src/config/schemas/platform.ts +3 -9
- package/src/config/schemas/security.ts +33 -0
- package/src/config/schemas/services.ts +9 -4
- package/src/config/schemas/stt.ts +1 -0
- package/src/config/schemas/tts.ts +53 -0
- package/src/config/schemas/updates.ts +1 -1
- package/src/config/schemas/workspace-git.ts +3 -40
- package/src/config/skills.ts +2 -2
- package/src/context/__tests__/compact-prompt.test.ts +45 -0
- package/src/context/__tests__/microcompact.test.ts +805 -0
- package/src/context/estimator-calibration.ts +136 -0
- package/src/context/microcompact.ts +443 -0
- package/src/context/prompts/compact.md +12 -0
- package/src/context/token-estimator.ts +61 -3
- package/src/context/window-manager.ts +229 -25
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/credential-execution/executable-discovery.ts +19 -8
- package/src/credential-execution/process-manager.test.ts +109 -0
- package/src/credential-execution/process-manager.ts +65 -2
- package/src/daemon/approval-generators.ts +29 -4
- package/src/daemon/assistant-attachments.ts +24 -13
- package/src/daemon/classifier.ts +2 -2
- package/src/daemon/config-watcher.ts +0 -1
- package/src/daemon/context-overflow-reducer.ts +4 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +79 -12
- package/src/daemon/conversation-agent-loop.ts +462 -80
- package/src/daemon/conversation-attachments.ts +2 -6
- package/src/daemon/conversation-error.ts +36 -1
- package/src/daemon/conversation-lifecycle.ts +30 -6
- package/src/daemon/conversation-messaging.ts +73 -4
- package/src/daemon/conversation-process.ts +10 -4
- package/src/daemon/conversation-queue-manager.ts +3 -0
- package/src/daemon/conversation-runtime-assembly.ts +760 -29
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +389 -1
- package/src/daemon/conversation-tool-setup.ts +10 -5
- package/src/daemon/conversation-usage.ts +1 -1
- package/src/daemon/conversation.ts +118 -30
- package/src/daemon/external-skills-bootstrap.ts +41 -0
- package/src/daemon/guardian-action-generators.ts +34 -14
- package/src/daemon/handlers/config-model.test.ts +86 -0
- package/src/daemon/handlers/config-model.ts +54 -12
- package/src/daemon/handlers/conversations.ts +9 -2
- package/src/daemon/handlers/shared.ts +39 -11
- package/src/daemon/handlers/skills.ts +2 -2
- package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
- package/src/daemon/lifecycle.ts +76 -14
- package/src/daemon/message-types/conversations.ts +14 -0
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/trust.ts +0 -2
- package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
- package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
- package/src/daemon/pkb-context-tracker.test.ts +169 -0
- package/src/daemon/pkb-context-tracker.ts +125 -0
- package/src/daemon/pkb-reminder-builder.test.ts +70 -0
- package/src/daemon/pkb-reminder-builder.ts +31 -0
- package/src/daemon/providers-setup.ts +6 -0
- package/src/daemon/server.ts +117 -9
- package/src/daemon/tool-side-effects.ts +0 -9
- package/src/daemon/watch-handler.ts +4 -4
- package/src/daemon/web-search-history.ts +126 -0
- package/src/events/domain-events.ts +0 -1
- package/src/filing/filing-service.ts +9 -10
- package/src/heartbeat/heartbeat-service.ts +76 -28
- package/src/home/__tests__/feed-scheduler.test.ts +39 -11
- package/src/home/__tests__/rollup-producer.test.ts +44 -0
- package/src/home/assistant-feed-authoring.ts +4 -0
- package/src/home/emit-feed-event.ts +4 -0
- package/src/home/feed-scheduler.ts +20 -4
- package/src/home/feed-types.ts +56 -2
- package/src/home/relationship-state-writer.ts +2 -2
- package/src/home/rollup-producer.ts +34 -5
- package/src/home/suggested-prompts.ts +101 -0
- package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
- package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
- package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
- package/src/ipc/__tests__/socket-path.test.ts +73 -0
- package/src/ipc/__tests__/task-ipc.test.ts +577 -0
- package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
- package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
- package/src/ipc/cli-client.ts +2 -1
- package/src/ipc/cli-server.ts +26 -8
- package/src/ipc/gateway-client.ts +4 -4
- package/src/ipc/routes/attachment.ts +114 -0
- package/src/ipc/routes/browser-context.ts +61 -0
- package/src/ipc/routes/browser.ts +96 -0
- package/src/ipc/routes/cache.ts +96 -0
- package/src/ipc/routes/index.ts +17 -1
- package/src/ipc/routes/task-queue.ts +226 -0
- package/src/ipc/routes/task.ts +173 -0
- package/src/ipc/routes/ui-request.ts +50 -0
- package/src/ipc/routes/watcher.ts +203 -0
- package/src/ipc/socket-path.ts +100 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
- package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
- package/src/memory/admin.ts +18 -0
- package/src/memory/conversation-analyze-job.ts +14 -13
- package/src/memory/conversation-attention-store.ts +13 -6
- package/src/memory/conversation-crud.ts +103 -3
- package/src/memory/conversation-group-migration.ts +38 -6
- package/src/memory/conversation-title-service.ts +7 -4
- package/src/memory/db-init.ts +2 -0
- package/src/memory/embedding-backend.ts +1 -1
- package/src/memory/graph/compaction.ts +299 -0
- package/src/memory/graph/consolidation.ts +4 -4
- package/src/memory/graph/conversation-graph-memory.ts +89 -29
- package/src/memory/graph/extraction.test.ts +272 -2
- package/src/memory/graph/extraction.ts +173 -51
- package/src/memory/graph/graph-search.test.ts +92 -0
- package/src/memory/graph/graph-search.ts +4 -1
- package/src/memory/graph/narrative.ts +2 -2
- package/src/memory/graph/pattern-scan.ts +2 -2
- package/src/memory/graph/retriever.test.ts +459 -0
- package/src/memory/graph/retriever.ts +230 -48
- package/src/memory/graph/store.ts +41 -0
- package/src/memory/graph/tool-handlers.ts +27 -0
- package/src/memory/graph/tools.ts +6 -1
- package/src/memory/indexer.ts +5 -5
- package/src/memory/job-handlers/conversation-starters.ts +23 -20
- package/src/memory/job-handlers/summarization.ts +2 -2
- package/src/memory/job-utils.ts +7 -1
- package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
- package/src/memory/jobs/embed-pkb-file.ts +54 -0
- package/src/memory/jobs-store.ts +44 -3
- package/src/memory/jobs-worker.ts +4 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/pkb/pkb-index.test.ts +368 -0
- package/src/memory/pkb/pkb-index.ts +255 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +251 -0
- package/src/memory/pkb/pkb-reconcile.ts +148 -0
- package/src/memory/pkb/pkb-search.test.ts +438 -0
- package/src/memory/pkb/pkb-search.ts +137 -0
- package/src/memory/pkb/types.ts +53 -0
- package/src/memory/qdrant-client.ts +122 -1
- package/src/memory/slack-thread-store.ts +37 -0
- package/src/messaging/providers/gmail/adapter.ts +6 -16
- package/src/messaging/providers/gmail/client.ts +22 -0
- package/src/messaging/providers/gmail/types.ts +7 -0
- package/src/messaging/providers/slack/adapter.ts +14 -2
- package/src/messaging/providers/slack/backfill.test.ts +257 -0
- package/src/messaging/providers/slack/backfill.ts +101 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
- package/src/messaging/providers/slack/message-metadata.ts +123 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +1373 -0
- package/src/messaging/providers/slack/render-transcript.ts +443 -0
- package/src/messaging/style-analyzer.ts +5 -2
- package/src/notifications/README.md +9 -5
- package/src/notifications/decision-engine.ts +3 -9
- package/src/notifications/preference-extractor.ts +2 -6
- package/src/oauth/oauth-store.ts +1 -0
- package/src/oauth/platform-connection.test.ts +47 -0
- package/src/oauth/platform-connection.ts +15 -5
- package/src/oauth/seed-providers.ts +4 -2
- package/src/permissions/approval-policy.test.ts +948 -0
- package/src/permissions/approval-policy.ts +257 -0
- package/src/permissions/bash-risk-classifier.test.ts +1208 -0
- package/src/permissions/bash-risk-classifier.ts +707 -0
- package/src/permissions/checker.ts +217 -708
- package/src/permissions/command-registry.test.ts +535 -0
- package/src/permissions/command-registry.ts +825 -0
- package/src/permissions/defaults.ts +26 -78
- package/src/permissions/file-risk-classifier.test.ts +535 -0
- package/src/permissions/file-risk-classifier.ts +274 -0
- package/src/permissions/risk-types.ts +205 -0
- package/src/permissions/secret-prompter.ts +53 -2
- package/src/permissions/skill-risk-classifier.test.ts +311 -0
- package/src/permissions/skill-risk-classifier.ts +214 -0
- package/src/permissions/trust-client.ts +52 -25
- package/src/permissions/trust-store-interface.ts +1 -6
- package/src/permissions/trust-store.ts +161 -62
- package/src/permissions/types.ts +23 -14
- package/src/permissions/web-risk-classifier.test.ts +170 -0
- package/src/permissions/web-risk-classifier.ts +89 -0
- package/src/permissions/workspace-policy.ts +1 -16
- package/src/platform/client.ts +19 -1
- package/src/prompts/persona-resolver.ts +3 -3
- package/src/prompts/system-prompt.ts +19 -20
- package/src/prompts/templates/SOUL.md +2 -2
- package/src/prompts/update-bulletin-job.ts +190 -0
- package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
- package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
- package/src/providers/__tests__/retry-callsite.test.ts +424 -0
- package/src/providers/anthropic/client.ts +183 -14
- package/src/providers/call-site-routing.ts +71 -0
- package/src/providers/gemini/client.ts +65 -2
- package/src/providers/managed-proxy/constants.ts +2 -1
- package/src/providers/model-catalog.ts +501 -33
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/openai/chat-completions-provider.ts +57 -1
- package/src/providers/openai/responses-provider.ts +86 -9
- package/src/providers/openrouter/client.ts +76 -9
- package/src/providers/provider-env-vars.ts +56 -0
- package/src/providers/provider-send-message.ts +22 -5
- package/src/providers/ratelimit.ts +4 -0
- package/src/providers/registry.ts +19 -8
- package/src/providers/retry.ts +174 -39
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
- package/src/providers/speech-to-text/provider-catalog.ts +17 -0
- package/src/providers/speech-to-text/resolve.ts +7 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +578 -0
- package/src/providers/speech-to-text/xai-realtime.ts +796 -0
- package/src/providers/speech-to-text/xai.test.ts +155 -0
- package/src/providers/speech-to-text/xai.ts +97 -0
- package/src/providers/types.ts +93 -3
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/__tests__/agent-wake.test.ts +43 -2
- package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
- package/src/runtime/agent-wake.ts +63 -22
- package/src/runtime/auth/route-policy.ts +4 -0
- package/src/runtime/btw-sidechain.ts +13 -3
- package/src/runtime/channel-reply-delivery.ts +106 -2
- package/src/runtime/decision-token.ts +116 -0
- package/src/runtime/gateway-client.ts +2 -2
- package/src/runtime/http-router.ts +32 -0
- package/src/runtime/http-server.ts +52 -1
- package/src/runtime/http-types.ts +23 -1
- package/src/runtime/interactive-ui.ts +362 -0
- package/src/runtime/invite-instruction-generator.ts +2 -2
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
- package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
- package/src/runtime/migrations/gcs-signed-url.ts +162 -0
- package/src/runtime/migrations/vbundle-importer.ts +154 -9
- package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
- package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
- package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
- package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
- package/src/runtime/migrations/vbundle-validator.ts +15 -6
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
- package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +58 -0
- package/src/runtime/routes/approval-routes.ts +12 -17
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
- package/src/runtime/routes/avatar-routes.ts +20 -4
- package/src/runtime/routes/btw-routes.ts +1 -4
- package/src/runtime/routes/conversation-management-routes.ts +20 -2
- package/src/runtime/routes/conversation-routes.ts +133 -27
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/diagnostics-routes.ts +6 -4
- package/src/runtime/routes/events-routes.ts +16 -0
- package/src/runtime/routes/guardian-approval-interception.ts +33 -3
- package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
- package/src/runtime/routes/home-feed-routes.ts +120 -2
- package/src/runtime/routes/inbound-message-handler.ts +912 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
- package/src/runtime/routes/integrations/slack/channel.ts +25 -3
- package/src/runtime/routes/llm-context-normalization.ts +23 -1
- package/src/runtime/routes/migration-routes.ts +720 -124
- package/src/runtime/routes/settings-routes.ts +4 -2
- package/src/runtime/routes/trust-rules-routes.ts +30 -14
- package/src/runtime/routes/work-items-routes.test.ts +1 -1
- package/src/runtime/routes/work-items-routes.ts +3 -2
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
- package/src/runtime/services/analyze-conversation.ts +12 -16
- package/src/runtime/skill-route-registry.ts +28 -6
- package/src/schedule/scheduler.ts +8 -0
- package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
- package/src/security/__tests__/untrusted-content.test.ts +109 -0
- package/src/security/oauth2.ts +98 -35
- package/src/security/secure-keys.ts +7 -8
- package/src/security/token-manager.ts +27 -13
- package/src/security/untrusted-content.ts +102 -0
- package/src/skills/catalog-cache.ts +26 -7
- package/src/skills/catalog-install.ts +31 -3
- package/src/skills/skill-cache-store.ts +97 -0
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
- package/src/stt/daemon-batch-transcriber.ts +33 -0
- package/src/stt/stt-stream-session.ts +8 -1
- package/src/stt/types.ts +5 -1
- package/src/subagent/manager.ts +41 -13
- package/src/tasks/ephemeral-permissions.ts +9 -4
- package/src/telemetry/usage-telemetry-reporter.ts +27 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +45 -2
- package/src/tools/browser/browser-execution.ts +65 -38
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
- package/src/tools/credentials/tool-policy.ts +39 -5
- package/src/tools/credentials/vault.ts +9 -4
- package/src/tools/executor.ts +4 -0
- package/src/tools/filesystem/write.ts +52 -0
- package/src/tools/host-terminal/host-shell.ts +45 -5
- package/src/tools/memory/register.test.ts +185 -0
- package/src/tools/memory/register.ts +3 -1
- package/src/tools/network/web-fetch.ts +20 -10
- package/src/tools/network/web-search.ts +19 -4
- package/src/tools/permission-checker.ts +36 -15
- package/src/tools/policy-context.ts +25 -8
- package/src/tools/registry.ts +55 -3
- package/src/tools/side-effects.ts +0 -11
- package/src/tools/skills/execute.ts +2 -2
- package/src/tools/skills/sandbox-runner.ts +5 -2
- package/src/tools/terminal/backends/native.ts +51 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +1 -0
- package/src/tools/tool-manifest.ts +6 -21
- package/src/tools/types.ts +12 -3
- package/src/tools/verification-control-plane-policy.ts +1 -1
- package/src/tts/__tests__/provider-adapters.test.ts +240 -13
- package/src/tts/provider-catalog.ts +18 -0
- package/src/tts/providers/index.ts +2 -0
- package/src/tts/providers/xai-provider.ts +224 -0
- package/src/tts/types.ts +46 -0
- package/src/types/tar-stream.d.ts +66 -0
- package/src/util/json.ts +17 -0
- package/src/util/platform.ts +2 -2
- package/src/util/pricing.ts +15 -5
- package/src/watcher/engine.ts +1 -1
- package/src/watcher/providers/google-calendar.ts +134 -8
- package/src/watcher/providers/outlook-calendar.ts +42 -2
- package/src/workspace/git-service.ts +23 -4
- package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
- package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
- package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +57 -0
- package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
- package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
- package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
- package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/provider-commit-message-generator.ts +19 -38
- package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
- package/src/__tests__/gmail-archive-gate.test.ts +0 -246
- package/src/__tests__/gmail-preferences.test.ts +0 -117
- package/src/__tests__/outlook-attachments.test.ts +0 -301
- package/src/__tests__/outlook-automation-tools.test.ts +0 -425
- package/src/__tests__/outlook-categories.test.ts +0 -212
- package/src/__tests__/outlook-compose-tools.test.ts +0 -325
- package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
- package/src/__tests__/outlook-follow-up.test.ts +0 -196
- package/src/__tests__/outlook-trash.test.ts +0 -77
- package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
- package/src/__tests__/update-bulletin-format.test.ts +0 -181
- package/src/__tests__/update-bulletin-state.test.ts +0 -135
- package/src/__tests__/update-bulletin.test.ts +0 -478
- package/src/__tests__/update-template-contract.test.ts +0 -29
- package/src/cli/commands/doctor.ts +0 -341
- package/src/config/bundled-skills/browser/SKILL.md +0 -88
- package/src/config/bundled-skills/browser/TOOLS.json +0 -516
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
- package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
- package/src/config/bundled-skills/gmail/SKILL.md +0 -221
- package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
- package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
- package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
- package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
- package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
- package/src/config/bundled-skills/google-calendar/types.ts +0 -97
- package/src/config/bundled-skills/outlook/SKILL.md +0 -196
- package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
- package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
- package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
- package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
- package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
- package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
- package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
- package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
- package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
- package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
- package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
- package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
- package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
- package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
- package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
- package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
- package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
- package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
- package/src/config/bundled-skills/slack/SKILL.md +0 -108
- package/src/config/bundled-skills/tasks/SKILL.md +0 -37
- package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
- package/src/config/bundled-skills/tasks/icon.svg +0 -34
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
- package/src/config/bundled-skills/watcher/SKILL.md +0 -31
- package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
- package/src/prompts/templates/UPDATES.md +0 -50
- package/src/prompts/update-bulletin-format.ts +0 -85
- package/src/prompts/update-bulletin-state.ts +0 -58
- package/src/prompts/update-bulletin-template-path.ts +0 -13
- package/src/prompts/update-bulletin.ts +0 -139
- package/src/shared/provider-env-vars.ts +0 -19
- package/src/tools/watcher/create.ts +0 -86
- package/src/tools/watcher/delete.ts +0 -36
- package/src/tools/watcher/digest.ts +0 -54
- package/src/tools/watcher/list.ts +0 -83
- package/src/tools/watcher/update.ts +0 -71
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import type { ContentBlock, Message } from "../providers/types.js";
|
|
5
|
+
import type { Conversation } from "./conversation.js";
|
|
6
|
+
import { getInContextPkbPaths } from "./pkb-context-tracker.js";
|
|
7
|
+
|
|
8
|
+
const WORKING_DIR = path.resolve("/tmp/test-pkb-root");
|
|
9
|
+
const PKB_ROOT = path.join(WORKING_DIR, "pkb");
|
|
10
|
+
|
|
11
|
+
// The helper only reads `conversation.messages`. Constructing a real
|
|
12
|
+
// `Conversation` instance would require a full daemon setup; casting a
|
|
13
|
+
// minimal object through `unknown` keeps the test isolated and pure while
|
|
14
|
+
// still exercising the public type.
|
|
15
|
+
function makeConversation(messages: Message[]): Conversation {
|
|
16
|
+
return { messages } as unknown as Conversation;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function fileReadToolUse(filePath: string): ContentBlock {
|
|
20
|
+
return {
|
|
21
|
+
type: "tool_use",
|
|
22
|
+
id: `toolu_${Math.random().toString(36).slice(2, 10)}`,
|
|
23
|
+
name: "file_read",
|
|
24
|
+
input: { path: filePath },
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function assistantMessageWithBlocks(blocks: ContentBlock[]): Message {
|
|
29
|
+
return { role: "assistant", content: blocks };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("getInContextPkbPaths", () => {
|
|
33
|
+
test("auto-inject paths are always present (even with empty conversation)", () => {
|
|
34
|
+
const conversation = makeConversation([]);
|
|
35
|
+
const result = getInContextPkbPaths(
|
|
36
|
+
conversation,
|
|
37
|
+
["notes/index.md", "journal/2026-04-18.md"],
|
|
38
|
+
PKB_ROOT,
|
|
39
|
+
WORKING_DIR,
|
|
40
|
+
);
|
|
41
|
+
expect(result).toEqual(
|
|
42
|
+
new Set([
|
|
43
|
+
path.join(PKB_ROOT, "notes/index.md"),
|
|
44
|
+
path.join(PKB_ROOT, "journal/2026-04-18.md"),
|
|
45
|
+
]),
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("includes a file_read tool_use with a path inside pkbRoot", () => {
|
|
50
|
+
const insidePath = path.join(PKB_ROOT, "notes/thoughts.md");
|
|
51
|
+
const conversation = makeConversation([
|
|
52
|
+
assistantMessageWithBlocks([fileReadToolUse(insidePath)]),
|
|
53
|
+
]);
|
|
54
|
+
const result = getInContextPkbPaths(conversation, [], PKB_ROOT, WORKING_DIR);
|
|
55
|
+
expect(result).toEqual(new Set([insidePath]));
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("excludes a file_read tool_use whose path is outside pkbRoot", () => {
|
|
59
|
+
const conversation = makeConversation([
|
|
60
|
+
assistantMessageWithBlocks([fileReadToolUse("/etc/hosts")]),
|
|
61
|
+
]);
|
|
62
|
+
const result = getInContextPkbPaths(conversation, [], PKB_ROOT, WORKING_DIR);
|
|
63
|
+
expect(result).toEqual(new Set());
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("excludes a non-file_read tool_use with a PKB-like path", () => {
|
|
67
|
+
const insidePath = path.join(PKB_ROOT, "notes/thoughts.md");
|
|
68
|
+
const bogus: ContentBlock = {
|
|
69
|
+
type: "tool_use",
|
|
70
|
+
id: "toolu_bogus",
|
|
71
|
+
name: "file_write",
|
|
72
|
+
input: { path: insidePath },
|
|
73
|
+
};
|
|
74
|
+
const conversation = makeConversation([
|
|
75
|
+
assistantMessageWithBlocks([bogus]),
|
|
76
|
+
]);
|
|
77
|
+
const result = getInContextPkbPaths(conversation, [], PKB_ROOT, WORKING_DIR);
|
|
78
|
+
expect(result).toEqual(new Set());
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("post-compaction context-summary user message returns only auto-inject paths", () => {
|
|
82
|
+
// After compaction, the structured tool_use blocks have been serialized
|
|
83
|
+
// away and the conversation is just a user-role text message containing
|
|
84
|
+
// the summary.
|
|
85
|
+
const summaryMessage: Message = {
|
|
86
|
+
role: "user",
|
|
87
|
+
content: [
|
|
88
|
+
{
|
|
89
|
+
type: "text",
|
|
90
|
+
text: "[Context summary] Previously you read notes/thoughts.md and journal/2026-04-18.md...",
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
const conversation = makeConversation([summaryMessage]);
|
|
95
|
+
const autoInject = ["profile/identity.md"];
|
|
96
|
+
const result = getInContextPkbPaths(
|
|
97
|
+
conversation,
|
|
98
|
+
autoInject,
|
|
99
|
+
PKB_ROOT,
|
|
100
|
+
WORKING_DIR,
|
|
101
|
+
);
|
|
102
|
+
expect(result).toEqual(
|
|
103
|
+
new Set([path.join(PKB_ROOT, "profile/identity.md")]),
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("path-traversal attempt via ../../etc/passwd is excluded", () => {
|
|
108
|
+
const conversation = makeConversation([
|
|
109
|
+
assistantMessageWithBlocks([
|
|
110
|
+
fileReadToolUse("../../etc/passwd"),
|
|
111
|
+
fileReadToolUse("notes/../../../etc/shadow"),
|
|
112
|
+
]),
|
|
113
|
+
]);
|
|
114
|
+
const result = getInContextPkbPaths(conversation, [], PKB_ROOT, WORKING_DIR);
|
|
115
|
+
expect(result).toEqual(new Set());
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("relative file_read path outside pkbRoot is excluded", () => {
|
|
119
|
+
// These are resolved against `workingDir` (matching `file_read`'s own
|
|
120
|
+
// rule), land outside `pkbRoot`, and are correctly ignored.
|
|
121
|
+
const conversation = makeConversation([
|
|
122
|
+
assistantMessageWithBlocks([
|
|
123
|
+
fileReadToolUse("notes.md"),
|
|
124
|
+
fileReadToolUse("./deep/subdir/file.md"),
|
|
125
|
+
]),
|
|
126
|
+
]);
|
|
127
|
+
const result = getInContextPkbPaths(conversation, [], PKB_ROOT, WORKING_DIR);
|
|
128
|
+
expect(result).toEqual(new Set());
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("workspace-relative file_read path inside pkb/ is recognized", () => {
|
|
132
|
+
// The model emits workspace-relative paths like `pkb/threads.md`.
|
|
133
|
+
// The tracker resolves them against `workingDir` (matching `file_read`'s
|
|
134
|
+
// own rule) and verifies the result falls inside `pkbRoot`.
|
|
135
|
+
const conversation = makeConversation([
|
|
136
|
+
assistantMessageWithBlocks([fileReadToolUse("pkb/threads.md")]),
|
|
137
|
+
]);
|
|
138
|
+
const result = getInContextPkbPaths(conversation, [], PKB_ROOT, WORKING_DIR);
|
|
139
|
+
expect(result).toEqual(new Set([path.join(PKB_ROOT, "threads.md")]));
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("resolves relative auto-inject paths against pkbRoot", () => {
|
|
143
|
+
const conversation = makeConversation([]);
|
|
144
|
+
const result = getInContextPkbPaths(
|
|
145
|
+
conversation,
|
|
146
|
+
["./notes/relative.md"],
|
|
147
|
+
PKB_ROOT,
|
|
148
|
+
WORKING_DIR,
|
|
149
|
+
);
|
|
150
|
+
expect(result).toEqual(
|
|
151
|
+
new Set([path.join(PKB_ROOT, "notes/relative.md")]),
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("auto-inject and file_read paths union (no duplicates)", () => {
|
|
156
|
+
const insidePath = path.join(PKB_ROOT, "notes/shared.md");
|
|
157
|
+
const conversation = makeConversation([
|
|
158
|
+
assistantMessageWithBlocks([fileReadToolUse(insidePath)]),
|
|
159
|
+
]);
|
|
160
|
+
const result = getInContextPkbPaths(
|
|
161
|
+
conversation,
|
|
162
|
+
["notes/shared.md"],
|
|
163
|
+
PKB_ROOT,
|
|
164
|
+
WORKING_DIR,
|
|
165
|
+
);
|
|
166
|
+
expect(result.size).toBe(1);
|
|
167
|
+
expect(result.has(insidePath)).toBe(true);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pkb-context-tracker
|
|
3
|
+
*
|
|
4
|
+
* Pure helper that reports which PKB file paths are already "in context" for
|
|
5
|
+
* a given conversation. A path is considered in context if either:
|
|
6
|
+
*
|
|
7
|
+
* 1. It was explicitly auto-injected (caller supplies `autoInjectPaths`),
|
|
8
|
+
* typically via a system-reminder that embeds the file contents.
|
|
9
|
+
* 2. The conversation history contains a structured `file_read` tool_use
|
|
10
|
+
* block whose `input.path` — resolved the same way `file_read` itself
|
|
11
|
+
* resolves it (absolute as-is, relative against `workingDir`) — lands
|
|
12
|
+
* inside `pkbRoot`.
|
|
13
|
+
*
|
|
14
|
+
* Used by the PKB system reminder so we don't suggest files the model has
|
|
15
|
+
* already loaded.
|
|
16
|
+
*
|
|
17
|
+
* Post-compaction note: structured `tool_use` blocks get serialized into a
|
|
18
|
+
* plain-text summary and dropped from the live message array. After a
|
|
19
|
+
* compaction, this helper will naturally only see the `autoInjectPaths` —
|
|
20
|
+
* which is the desired semantics.
|
|
21
|
+
*
|
|
22
|
+
* No I/O, no globals, no side effects.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import path from "node:path";
|
|
26
|
+
|
|
27
|
+
import type { ContentBlock, Message } from "../providers/types.js";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Minimal shape this helper needs from a `Conversation`. Defining it as an
|
|
31
|
+
* interface (rather than importing the full `Conversation` class) keeps the
|
|
32
|
+
* helper pure and trivial to unit-test without constructing a real daemon
|
|
33
|
+
* conversation.
|
|
34
|
+
*/
|
|
35
|
+
export interface PkbContextConversation {
|
|
36
|
+
messages: Message[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The structured tool_use block name the assistant emits when reading files
|
|
41
|
+
* from the workspace (see `assistant/src/tools/filesystem/read.ts`).
|
|
42
|
+
*/
|
|
43
|
+
const FILE_READ_TOOL_NAME = "file_read";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolve `candidate` against `pkbRoot` and return the absolute path ONLY if
|
|
47
|
+
* it stays inside `pkbRoot`. Otherwise return `undefined`. Guards against
|
|
48
|
+
* `..`-style path traversal.
|
|
49
|
+
*/
|
|
50
|
+
function resolveInsidePkbRoot(
|
|
51
|
+
candidate: string,
|
|
52
|
+
pkbRoot: string,
|
|
53
|
+
): string | undefined {
|
|
54
|
+
if (typeof candidate !== "string" || candidate.length === 0) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
const resolved = path.resolve(pkbRoot, candidate);
|
|
58
|
+
// `path.resolve` normalizes any `..` segments in `candidate`. We still need
|
|
59
|
+
// to verify the result is inside `pkbRoot`. Comparing with a trailing
|
|
60
|
+
// separator avoids treating `<pkbRoot>somethingElse` as inside the root.
|
|
61
|
+
if (resolved === pkbRoot) {
|
|
62
|
+
return resolved;
|
|
63
|
+
}
|
|
64
|
+
const rootWithSep = pkbRoot.endsWith(path.sep) ? pkbRoot : pkbRoot + path.sep;
|
|
65
|
+
if (resolved.startsWith(rootWithSep)) {
|
|
66
|
+
return resolved;
|
|
67
|
+
}
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Returns the set of absolute PKB file paths already in the conversation's
|
|
73
|
+
* in-memory context. This is the union of `autoInjectPaths` (resolved into
|
|
74
|
+
* `pkbRoot`) and any `file_read` tool_use block inputs found in
|
|
75
|
+
* `conversation.messages` that resolve inside `pkbRoot`.
|
|
76
|
+
*
|
|
77
|
+
* `file_read` resolves its `path` argument against `workingDir` (the tool's
|
|
78
|
+
* working directory), so this helper mirrors that rule: relative tool paths
|
|
79
|
+
* are resolved against `workingDir`, absolute paths are taken as-is, and the
|
|
80
|
+
* resulting absolute path is then accepted only if it falls inside `pkbRoot`.
|
|
81
|
+
*
|
|
82
|
+
* Paths outside `pkbRoot` (including `..`-traversal attempts) are excluded.
|
|
83
|
+
* Tool uses whose `name` is not `file_read` are ignored.
|
|
84
|
+
*/
|
|
85
|
+
export function getInContextPkbPaths(
|
|
86
|
+
conversation: PkbContextConversation,
|
|
87
|
+
autoInjectPaths: string[],
|
|
88
|
+
pkbRoot: string,
|
|
89
|
+
workingDir: string,
|
|
90
|
+
): Set<string> {
|
|
91
|
+
const normalizedRoot = path.resolve(pkbRoot);
|
|
92
|
+
const normalizedWorkingDir = path.resolve(workingDir);
|
|
93
|
+
const inContext = new Set<string>();
|
|
94
|
+
|
|
95
|
+
for (const candidate of autoInjectPaths) {
|
|
96
|
+
const resolved = resolveInsidePkbRoot(candidate, normalizedRoot);
|
|
97
|
+
if (resolved !== undefined) {
|
|
98
|
+
inContext.add(resolved);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (const message of conversation.messages) {
|
|
103
|
+
if (!Array.isArray(message.content)) continue;
|
|
104
|
+
for (const block of message.content as ContentBlock[]) {
|
|
105
|
+
if (block.type !== "tool_use") continue;
|
|
106
|
+
if (block.name !== FILE_READ_TOOL_NAME) continue;
|
|
107
|
+
const rawPath = block.input?.path;
|
|
108
|
+
if (typeof rawPath !== "string" || rawPath.length === 0) continue;
|
|
109
|
+
// Mirror `file_read`'s own path resolution: absolute paths are taken
|
|
110
|
+
// as-is; relative paths resolve against the tool's working directory
|
|
111
|
+
// (NOT `pkbRoot`). Resolving relative paths against `pkbRoot` would
|
|
112
|
+
// double-prefix workspace-relative inputs like `pkb/threads.md` into
|
|
113
|
+
// `<pkbRoot>/pkb/threads.md` and miss the actually-loaded file.
|
|
114
|
+
const absolute = path.isAbsolute(rawPath)
|
|
115
|
+
? path.resolve(rawPath)
|
|
116
|
+
: path.resolve(normalizedWorkingDir, rawPath);
|
|
117
|
+
const resolved = resolveInsidePkbRoot(absolute, normalizedRoot);
|
|
118
|
+
if (resolved !== undefined) {
|
|
119
|
+
inContext.add(resolved);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return inContext;
|
|
125
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { buildPkbReminder } from "./pkb-reminder-builder.js";
|
|
4
|
+
|
|
5
|
+
// Byte-for-byte fixture of the original PKB_SYSTEM_REMINDER from
|
|
6
|
+
// conversation-runtime-assembly.ts. If this ever needs to change, the
|
|
7
|
+
// matching string in conversation-runtime-assembly.ts must change too.
|
|
8
|
+
const ORIGINAL_REMINDER =
|
|
9
|
+
"<system_reminder>" +
|
|
10
|
+
"\nRead any unread Personal Knowledge Base files that might be even partially relevant to this conversation" +
|
|
11
|
+
"\nUse `remember` for anything you learn immediately" +
|
|
12
|
+
"\n</system_reminder>";
|
|
13
|
+
|
|
14
|
+
describe("buildPkbReminder", () => {
|
|
15
|
+
test("empty hints returns exact original reminder byte-for-byte", () => {
|
|
16
|
+
expect(buildPkbReminder([])).toBe(ORIGINAL_REMINDER);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("single hint renders one bullet with no duplicates or trailing blank line", () => {
|
|
20
|
+
const out = buildPkbReminder(["projects/alpha.md"]);
|
|
21
|
+
const expected =
|
|
22
|
+
"<system_reminder>" +
|
|
23
|
+
"\nRead any unread Personal Knowledge Base files that might be even partially relevant to this conversation." +
|
|
24
|
+
"\nBased on the current context, these files look especially relevant:" +
|
|
25
|
+
"\n- projects/alpha.md" +
|
|
26
|
+
"\nUse `remember` for anything you learn immediately" +
|
|
27
|
+
"\n</system_reminder>";
|
|
28
|
+
expect(out).toBe(expected);
|
|
29
|
+
|
|
30
|
+
// Exactly one bullet.
|
|
31
|
+
const bulletCount = (out.match(/^- /gm) ?? []).length;
|
|
32
|
+
expect(bulletCount).toBe(1);
|
|
33
|
+
|
|
34
|
+
// No blank line before closing tag.
|
|
35
|
+
expect(out.includes("\n\n</system_reminder>")).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("three hints render all three in order", () => {
|
|
39
|
+
const hints = ["a.md", "sub/b.md", "c/d/e.md"];
|
|
40
|
+
const out = buildPkbReminder(hints);
|
|
41
|
+
const expected =
|
|
42
|
+
"<system_reminder>" +
|
|
43
|
+
"\nRead any unread Personal Knowledge Base files that might be even partially relevant to this conversation." +
|
|
44
|
+
"\nBased on the current context, these files look especially relevant:" +
|
|
45
|
+
"\n- a.md" +
|
|
46
|
+
"\n- sub/b.md" +
|
|
47
|
+
"\n- c/d/e.md" +
|
|
48
|
+
"\nUse `remember` for anything you learn immediately" +
|
|
49
|
+
"\n</system_reminder>";
|
|
50
|
+
expect(out).toBe(expected);
|
|
51
|
+
|
|
52
|
+
// Order check — each should appear after the previous.
|
|
53
|
+
const idxA = out.indexOf("- a.md");
|
|
54
|
+
const idxB = out.indexOf("- sub/b.md");
|
|
55
|
+
const idxC = out.indexOf("- c/d/e.md");
|
|
56
|
+
expect(idxA).toBeGreaterThan(-1);
|
|
57
|
+
expect(idxB).toBeGreaterThan(idxA);
|
|
58
|
+
expect(idxC).toBeGreaterThan(idxB);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("hints with special chars (< and &) are emitted verbatim (no escaping)", () => {
|
|
62
|
+
const hints = ["weird<name>.md", "foo&bar.md"];
|
|
63
|
+
const out = buildPkbReminder(hints);
|
|
64
|
+
expect(out).toContain("- weird<name>.md");
|
|
65
|
+
expect(out).toContain("- foo&bar.md");
|
|
66
|
+
// Ensure no HTML-style escaping happened.
|
|
67
|
+
expect(out).not.toContain("<");
|
|
68
|
+
expect(out).not.toContain("&");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render the PKB system_reminder text, optionally with a bulleted list of
|
|
3
|
+
* hint paths that look especially relevant to the current conversation.
|
|
4
|
+
*
|
|
5
|
+
* When `hints` is empty, returns the legacy two-line reminder byte-for-byte.
|
|
6
|
+
* When `hints` is non-empty, renders an extended reminder with a bullet per
|
|
7
|
+
* hint. Hints are emitted verbatim — they are trusted internal paths, not
|
|
8
|
+
* user input, so no escaping is performed.
|
|
9
|
+
*
|
|
10
|
+
* Caller is responsible for capping the hints array at 3 entries.
|
|
11
|
+
*/
|
|
12
|
+
export function buildPkbReminder(hints: ReadonlyArray<string>): string {
|
|
13
|
+
if (hints.length === 0) {
|
|
14
|
+
return (
|
|
15
|
+
"<system_reminder>" +
|
|
16
|
+
"\nRead any unread Personal Knowledge Base files that might be even partially relevant to this conversation" +
|
|
17
|
+
"\nUse `remember` for anything you learn immediately" +
|
|
18
|
+
"\n</system_reminder>"
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const bullets = hints.map((h) => `- ${h}`).join("\n");
|
|
23
|
+
return (
|
|
24
|
+
"<system_reminder>" +
|
|
25
|
+
"\nRead any unread Personal Knowledge Base files that might be even partially relevant to this conversation." +
|
|
26
|
+
"\nBased on the current context, these files look especially relevant:" +
|
|
27
|
+
`\n${bullets}` +
|
|
28
|
+
"\nUse `remember` for anything you learn immediately" +
|
|
29
|
+
"\n</system_reminder>"
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -27,6 +27,12 @@ import { googleCalendarProvider } from "../watcher/providers/google-calendar.js"
|
|
|
27
27
|
import { linearProvider } from "../watcher/providers/linear.js";
|
|
28
28
|
import { outlookProvider } from "../watcher/providers/outlook.js";
|
|
29
29
|
import { outlookCalendarProvider } from "../watcher/providers/outlook-calendar.js";
|
|
30
|
+
|
|
31
|
+
// Side-effect import: runs each bundled first-party skill's tool
|
|
32
|
+
// registration before `initializeTools()` so external tools are visible
|
|
33
|
+
// to the LLM. See `external-skills-bootstrap.ts` for the rationale.
|
|
34
|
+
import "./external-skills-bootstrap.js";
|
|
35
|
+
|
|
30
36
|
const log = getLogger("lifecycle");
|
|
31
37
|
|
|
32
38
|
export async function initializeProvidersAndTools(
|
package/src/daemon/server.ts
CHANGED
|
@@ -27,6 +27,7 @@ import type { CesProcessManager } from "../credential-execution/process-manager.
|
|
|
27
27
|
import type { FilingService } from "../filing/filing-service.js";
|
|
28
28
|
import type { HeartbeatService } from "../heartbeat/heartbeat-service.js";
|
|
29
29
|
import { CliIpcServer } from "../ipc/cli-server.js";
|
|
30
|
+
import { registerBrowserIpcContextResolver } from "../ipc/routes/browser-context.js";
|
|
30
31
|
import { getApp, getAppDirPath, isMultifileApp } from "../memory/app-store.js";
|
|
31
32
|
import * as attachmentsStore from "../memory/attachments-store.js";
|
|
32
33
|
import {
|
|
@@ -49,6 +50,7 @@ import {
|
|
|
49
50
|
import { getOrCreateConversation } from "../memory/conversation-key-store.js";
|
|
50
51
|
import { syncIdentityNameToPlatform } from "../platform/sync-identity.js";
|
|
51
52
|
import { buildSystemPrompt } from "../prompts/system-prompt.js";
|
|
53
|
+
import { CallSiteRoutingProvider } from "../providers/call-site-routing.js";
|
|
52
54
|
import { RateLimitProvider } from "../providers/ratelimit.js";
|
|
53
55
|
import { getProvider, initializeProviders } from "../providers/registry.js";
|
|
54
56
|
import {
|
|
@@ -60,6 +62,7 @@ import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
|
60
62
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
61
63
|
import { getSigningKeyFingerprint } from "../runtime/auth/token-service.js";
|
|
62
64
|
import { bridgeConfirmationRequestToGuardian } from "../runtime/confirmation-request-guardian-bridge.js";
|
|
65
|
+
import { registerInteractiveUiResolver } from "../runtime/interactive-ui.js";
|
|
63
66
|
import * as pendingInteractions from "../runtime/pending-interactions.js";
|
|
64
67
|
import { checkIngressForSecrets } from "../security/secret-ingress.js";
|
|
65
68
|
import { redactSecrets } from "../security/secret-scanner.js";
|
|
@@ -90,10 +93,14 @@ import {
|
|
|
90
93
|
} from "./conversation.js";
|
|
91
94
|
import { ConversationEvictor } from "./conversation-evictor.js";
|
|
92
95
|
import { registerLaunchConversationDeps } from "./conversation-launch.js";
|
|
96
|
+
import { buildSlackMetaForPersistence } from "./conversation-messaging.js";
|
|
93
97
|
import { formatCompactResult } from "./conversation-process.js";
|
|
94
98
|
import { resolveChannelCapabilities } from "./conversation-runtime-assembly.js";
|
|
95
99
|
import { resolveSlash, type SlashContext } from "./conversation-slash.js";
|
|
96
|
-
import {
|
|
100
|
+
import {
|
|
101
|
+
refreshSurfacesForApp,
|
|
102
|
+
showStandaloneSurface,
|
|
103
|
+
} from "./conversation-surfaces.js";
|
|
97
104
|
import { undoLastMessage } from "./handlers/conversations.js";
|
|
98
105
|
import { parseIdentityFields } from "./handlers/identity.js";
|
|
99
106
|
import type {
|
|
@@ -813,6 +820,11 @@ export class DaemonServer {
|
|
|
813
820
|
// DB, exposing only the narrow surface the wake helper needs.
|
|
814
821
|
registerDefaultWakeResolver(async (conversationId) => {
|
|
815
822
|
try {
|
|
823
|
+
// Only resolve existing conversations — don't create ghost
|
|
824
|
+
// conversations for stale targets (e.g. meetings that ended
|
|
825
|
+
// but a delayed opportunity callback still fires).
|
|
826
|
+
const existing = getConversation(conversationId);
|
|
827
|
+
if (!existing) return null;
|
|
816
828
|
const conversation = await this.getOrCreateConversation(conversationId);
|
|
817
829
|
return conversationToWakeTarget(conversation);
|
|
818
830
|
} catch (err) {
|
|
@@ -824,6 +836,59 @@ export class DaemonServer {
|
|
|
824
836
|
}
|
|
825
837
|
});
|
|
826
838
|
|
|
839
|
+
// Install the interactive UI resolver so skills and IPC handlers can
|
|
840
|
+
// present ad-hoc UI surfaces (confirmations, forms) to the user via
|
|
841
|
+
// `requestInteractiveUi()`. Interactive UI requires a client to be
|
|
842
|
+
// actively connected to the conversation (via SSE), which means the
|
|
843
|
+
// conversation must be in the in-memory map. If the conversation was
|
|
844
|
+
// evicted from memory the client is definitely disconnected, so
|
|
845
|
+
// hydration from persistent storage is pointless — the hydrated
|
|
846
|
+
// conversation would have hasNoClient=true, causing
|
|
847
|
+
// canShowInteractiveUi() to return false and the surface to be
|
|
848
|
+
// cancelled with no_interactive_surface. We skip that wasted work
|
|
849
|
+
// and return conversation_not_found directly.
|
|
850
|
+
registerInteractiveUiResolver(async (request) => {
|
|
851
|
+
const conversation = this.conversations.get(request.conversationId);
|
|
852
|
+
|
|
853
|
+
if (!conversation) {
|
|
854
|
+
log.warn(
|
|
855
|
+
{
|
|
856
|
+
conversationId: request.conversationId,
|
|
857
|
+
surfaceType: request.surfaceType,
|
|
858
|
+
},
|
|
859
|
+
"interactive-ui resolver: conversation not in memory (client not connected); failing closed",
|
|
860
|
+
);
|
|
861
|
+
return {
|
|
862
|
+
status: "cancelled" as const,
|
|
863
|
+
surfaceId: `ui-resolver-${Date.now()}`,
|
|
864
|
+
cancellationReason: "conversation_not_found" as const,
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// Generate a unique surface ID and delegate to the conversation's
|
|
869
|
+
// standalone surface lifecycle. The returned Promise blocks until
|
|
870
|
+
// the user submits, cancels, or the timeout elapses.
|
|
871
|
+
const surfaceId = `ui-standalone-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
872
|
+
return showStandaloneSurface(conversation, request, surfaceId);
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
// Allow `browser_execute` IPC calls to reuse live conversation browser
|
|
876
|
+
// proxy wiring (when a caller passes a conversationId from
|
|
877
|
+
// __CONVERSATION_ID / __SKILL_CONTEXT_JSON). This keeps nested
|
|
878
|
+
// `assistant browser status` checks consistent with the parent turn's
|
|
879
|
+
// extension connectivity instead of always falling back to a synthetic
|
|
880
|
+
// browser-cli session that has no hostBrowserProxy.
|
|
881
|
+
registerBrowserIpcContextResolver((conversationId) => {
|
|
882
|
+
const conversation = this.conversations.get(conversationId);
|
|
883
|
+
if (!conversation) return null;
|
|
884
|
+
return {
|
|
885
|
+
conversationId,
|
|
886
|
+
trustClass: conversation.trustContext?.trustClass ?? "guardian",
|
|
887
|
+
hostBrowserProxy: conversation.hostBrowserProxy,
|
|
888
|
+
transportInterface: conversation.transportInterface,
|
|
889
|
+
};
|
|
890
|
+
});
|
|
891
|
+
|
|
827
892
|
// Start the CLI IPC server. Built-in methods (wake_conversation) are
|
|
828
893
|
// registered by the constructor; CLI commands connect to this socket to
|
|
829
894
|
// invoke daemon-side operations that require in-process state.
|
|
@@ -1033,7 +1098,19 @@ export class DaemonServer {
|
|
|
1033
1098
|
|
|
1034
1099
|
const createPromise = (async () => {
|
|
1035
1100
|
const config = getConfig();
|
|
1036
|
-
let provider = getProvider(config.
|
|
1101
|
+
let provider = getProvider(config.llm.default.provider);
|
|
1102
|
+
// Per-call `options.config.callSite` can resolve to a provider name
|
|
1103
|
+
// that differs from `llm.default.provider`. Wrap the default
|
|
1104
|
+
// provider so the actual transport routes correctly per call,
|
|
1105
|
+
// rather than only forwarding metadata to the default's HTTP
|
|
1106
|
+
// client. See `providers/call-site-routing.ts`.
|
|
1107
|
+
provider = new CallSiteRoutingProvider(provider, (name) => {
|
|
1108
|
+
try {
|
|
1109
|
+
return getProvider(name);
|
|
1110
|
+
} catch {
|
|
1111
|
+
return undefined;
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1037
1114
|
const { rateLimit } = config;
|
|
1038
1115
|
if (rateLimit.maxRequestsPerMinute > 0) {
|
|
1039
1116
|
provider = new RateLimitProvider(
|
|
@@ -1046,7 +1123,8 @@ export class DaemonServer {
|
|
|
1046
1123
|
|
|
1047
1124
|
const systemPrompt =
|
|
1048
1125
|
storedOptions?.systemPromptOverride ?? buildSystemPrompt();
|
|
1049
|
-
const maxTokens =
|
|
1126
|
+
const maxTokens =
|
|
1127
|
+
storedOptions?.maxResponseTokens ?? config.llm.default.maxTokens;
|
|
1050
1128
|
|
|
1051
1129
|
const memoryPolicy = this.deriveMemoryPolicy(conversationId);
|
|
1052
1130
|
// Resolve the shared CES client (may still be initializing).
|
|
@@ -1065,7 +1143,6 @@ export class DaemonServer {
|
|
|
1065
1143
|
sharedCesClient,
|
|
1066
1144
|
storedOptions?.speed,
|
|
1067
1145
|
undefined,
|
|
1068
|
-
storedOptions?.modelIntent,
|
|
1069
1146
|
storedOptions?.modelOverride,
|
|
1070
1147
|
);
|
|
1071
1148
|
newConversation.updateClient(sendToClient, true);
|
|
@@ -1397,6 +1474,7 @@ export class DaemonServer {
|
|
|
1397
1474
|
.runAgentLoop(content, messageId, onEvent, {
|
|
1398
1475
|
isInteractive: options?.isInteractive ?? false,
|
|
1399
1476
|
isUserMessage: true,
|
|
1477
|
+
...(options?.callSite ? { callSite: options.callSite } : {}),
|
|
1400
1478
|
})
|
|
1401
1479
|
.finally(() => {
|
|
1402
1480
|
if (
|
|
@@ -1437,14 +1515,26 @@ export class DaemonServer {
|
|
|
1437
1515
|
messageCount: conversation.getMessages().length,
|
|
1438
1516
|
inputTokens: conversation.usageStats.inputTokens,
|
|
1439
1517
|
outputTokens: conversation.usageStats.outputTokens,
|
|
1440
|
-
maxInputTokens: config.contextWindow.maxInputTokens,
|
|
1441
|
-
model: config.
|
|
1442
|
-
provider: config.
|
|
1518
|
+
maxInputTokens: config.llm.default.contextWindow.maxInputTokens,
|
|
1519
|
+
model: config.llm.default.model,
|
|
1520
|
+
provider: config.llm.default.provider,
|
|
1443
1521
|
estimatedCost: conversation.usageStats.estimatedCost,
|
|
1444
1522
|
userMessageInterface: serverInterfaceCtx?.userMessageInterface,
|
|
1445
1523
|
};
|
|
1446
1524
|
const slashResult = await resolveSlash(content, slashContext);
|
|
1447
1525
|
|
|
1526
|
+
// Slack inbound metadata is materialized once here for the slash-command
|
|
1527
|
+
// bypass paths (unknown-slash and /compact), which persist the user row
|
|
1528
|
+
// directly via `addMessage` and would otherwise drop the envelope. The
|
|
1529
|
+
// agent-loop path does not consume this variable — it forwards
|
|
1530
|
+
// `options.slackInbound` through `persistMetadata` and the envelope is
|
|
1531
|
+
// built internally by `buildSlackMetaForPersistence` inside
|
|
1532
|
+
// `persistQueuedMessageBody`.
|
|
1533
|
+
const slackMeta = buildSlackMetaForPersistence({
|
|
1534
|
+
slackInbound: options?.slackInbound,
|
|
1535
|
+
turnChannel: conversation.getTurnChannelContext()?.userMessageChannel,
|
|
1536
|
+
});
|
|
1537
|
+
|
|
1448
1538
|
if (slashResult.kind === "unknown") {
|
|
1449
1539
|
const serverTurnCtx = conversation.getTurnChannelContext();
|
|
1450
1540
|
const serverProvenance = provenanceFromTrustContext(
|
|
@@ -1476,13 +1566,20 @@ export class DaemonServer {
|
|
|
1476
1566
|
? { imageSourcePaths }
|
|
1477
1567
|
: {}),
|
|
1478
1568
|
};
|
|
1569
|
+
// slackMeta encodes the inbound user message's ts/thread — it attaches
|
|
1570
|
+
// to the user row only. The assistant's slash-command response does not
|
|
1571
|
+
// originate from Slack and must not inherit the user's channelTs, which
|
|
1572
|
+
// would break ordering in the chronological renderer.
|
|
1573
|
+
const userMetaWithSlack = slackMeta
|
|
1574
|
+
? { ...serverChannelMeta, slackMeta }
|
|
1575
|
+
: serverChannelMeta;
|
|
1479
1576
|
const cleanMsg = createUserMessage(content, attachments);
|
|
1480
1577
|
const llmMsg = enrichMessageWithSourcePaths(cleanMsg, attachments);
|
|
1481
1578
|
const persisted = await addMessage(
|
|
1482
1579
|
conversationId,
|
|
1483
1580
|
"user",
|
|
1484
1581
|
JSON.stringify(cleanMsg.content),
|
|
1485
|
-
|
|
1582
|
+
userMetaWithSlack,
|
|
1486
1583
|
);
|
|
1487
1584
|
conversation.getMessages().push(llmMsg);
|
|
1488
1585
|
|
|
@@ -1560,12 +1657,15 @@ export class DaemonServer {
|
|
|
1560
1657
|
}
|
|
1561
1658
|
: {}),
|
|
1562
1659
|
};
|
|
1660
|
+
const compactUserMeta = slackMeta
|
|
1661
|
+
? { ...compactChannelMeta, slackMeta }
|
|
1662
|
+
: compactChannelMeta;
|
|
1563
1663
|
const cleanMsg = createUserMessage(content, attachments);
|
|
1564
1664
|
const persisted = await addMessage(
|
|
1565
1665
|
conversationId,
|
|
1566
1666
|
"user",
|
|
1567
1667
|
JSON.stringify(cleanMsg.content),
|
|
1568
|
-
|
|
1668
|
+
compactUserMeta,
|
|
1569
1669
|
);
|
|
1570
1670
|
conversation.getMessages().push(cleanMsg);
|
|
1571
1671
|
|
|
@@ -1590,10 +1690,17 @@ export class DaemonServer {
|
|
|
1590
1690
|
const resolvedContent = slashResult.content;
|
|
1591
1691
|
|
|
1592
1692
|
const requestId = crypto.randomUUID();
|
|
1693
|
+
// Slack inbound metadata captured at the channel ingress boundary is
|
|
1694
|
+
// forwarded into the persistence call so `persistQueuedMessageBody` can
|
|
1695
|
+
// emit a `slackMeta` envelope on the row's metadata column.
|
|
1696
|
+
const persistMetadata = options?.slackInbound
|
|
1697
|
+
? { slackInbound: options.slackInbound }
|
|
1698
|
+
: undefined;
|
|
1593
1699
|
const messageId = await conversation.persistUserMessage(
|
|
1594
1700
|
resolvedContent,
|
|
1595
1701
|
attachments,
|
|
1596
1702
|
requestId,
|
|
1703
|
+
persistMetadata,
|
|
1597
1704
|
);
|
|
1598
1705
|
|
|
1599
1706
|
// Register pending interactions so channel approval interception can
|
|
@@ -1623,6 +1730,7 @@ export class DaemonServer {
|
|
|
1623
1730
|
await conversation.runAgentLoop(resolvedContent, messageId, onEvent, {
|
|
1624
1731
|
isInteractive: options?.isInteractive ?? false,
|
|
1625
1732
|
isUserMessage: true,
|
|
1733
|
+
...(options?.callSite ? { callSite: options.callSite } : {}),
|
|
1626
1734
|
});
|
|
1627
1735
|
} finally {
|
|
1628
1736
|
if (
|
|
@@ -195,15 +195,6 @@ registerHook(
|
|
|
195
195
|
},
|
|
196
196
|
);
|
|
197
197
|
|
|
198
|
-
// Broadcast tasks_changed so connected clients (e.g. macOS Tasks window)
|
|
199
|
-
// auto-refresh when the LLM mutates the task queue via tools
|
|
200
|
-
registerHook(
|
|
201
|
-
["task_list_add", "task_list_update", "task_list_remove", "task_queue_run"],
|
|
202
|
-
(_name, _input, _result, { broadcastToAllClients }) => {
|
|
203
|
-
broadcastToAllClients?.({ type: "tasks_changed" });
|
|
204
|
-
},
|
|
205
|
-
);
|
|
206
|
-
|
|
207
198
|
// Broadcast voice config changes to all connected clients so every window
|
|
208
199
|
// picks up the updated UserDefaults value immediately.
|
|
209
200
|
registerHook(
|