@vellumai/assistant 0.6.4 → 0.6.6
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/AGENTS.md +9 -1
- package/ARCHITECTURE.md +43 -49
- package/Dockerfile +17 -3
- package/README.md +3 -4
- package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
- package/bun.lock +8 -3
- package/docs/architecture/integrations.md +33 -59
- package/docs/architecture/memory.md +25 -30
- package/docs/architecture/security.md +19 -18
- package/docs/browser-use-architecture-phase2.md +63 -20
- package/docs/error-handling.md +111 -0
- package/docs/plugins.md +761 -0
- package/docs/skills.md +10 -10
- package/docs/stt-provider-onboarding.md +2 -1
- package/examples/plugins/echo/README.md +132 -0
- package/examples/plugins/echo/package.json +17 -0
- package/examples/plugins/echo/register.ts +187 -0
- 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/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
- package/openapi.yaml +334 -78
- 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__/app-compiler.test.ts +57 -0
- package/src/__tests__/approval-cascade.test.ts +36 -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__/auto-analysis-end-to-end.test.ts +1 -0
- package/src/__tests__/avatar-generator.test.ts +4 -2
- 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__/bundled-asset.test.ts +6 -6
- 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 +96 -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 +870 -655
- package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
- package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
- package/src/__tests__/compaction-events.test.ts +501 -0
- package/src/__tests__/compaction-pipeline.test.ts +210 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
- package/src/__tests__/compaction-timeout-recovery.test.ts +262 -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-model-image-provider.test.ts +110 -0
- package/src/__tests__/config-schema-cmd.test.ts +11 -5
- package/src/__tests__/config-schema.test.ts +440 -114
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +72 -73
- package/src/__tests__/contacts-tools.test.ts +26 -0
- package/src/__tests__/contacts-write.test.ts +4 -4
- package/src/__tests__/context-overflow-policy.test.ts +7 -7
- package/src/__tests__/context-token-estimator.test.ts +191 -1
- package/src/__tests__/context-window-manager.test.ts +883 -4
- package/src/__tests__/conversation-abort-tool-results.test.ts +32 -15
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +86 -46
- package/src/__tests__/conversation-agent-loop.test.ts +435 -216
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +36 -10
- package/src/__tests__/conversation-error.test.ts +37 -6
- package/src/__tests__/conversation-history-web-search.test.ts +7 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +34 -12
- package/src/__tests__/conversation-lifecycle.test.ts +336 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
- package/src/__tests__/conversation-pairing.test.ts +174 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +32 -15
- package/src/__tests__/conversation-process-callsite.test.ts +309 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +44 -21
- package/src/__tests__/conversation-queue.test.ts +68 -38
- package/src/__tests__/conversation-routes-disk-view.test.ts +36 -7
- package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +2877 -152
- package/src/__tests__/conversation-runtime-workspace.test.ts +35 -50
- package/src/__tests__/conversation-seed-composer.test.ts +2 -2
- package/src/__tests__/conversation-skill-tools.test.ts +12 -146
- package/src/__tests__/conversation-slash-queue.test.ts +39 -19
- package/src/__tests__/conversation-slash-unknown.test.ts +53 -16
- package/src/__tests__/conversation-speed-override.test.ts +36 -12
- 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 +118 -2
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -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 +4 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +33 -9
- package/src/__tests__/conversation-workspace-injection.test.ts +46 -15
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +46 -15
- package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
- package/src/__tests__/credential-health-service.test.ts +78 -9
- package/src/__tests__/credential-security-invariants.test.ts +5 -2
- 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__/db-schedule-syntax-migration.test.ts +1 -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__/empty-response-pipeline.test.ts +305 -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 +29 -10
- package/src/__tests__/file-write-tool.test.ts +151 -1
- package/src/__tests__/filing-service.test.ts +255 -0
- package/src/__tests__/first-greeting.test.ts +247 -5
- 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__/headless-browser-mode.test.ts +57 -0
- package/src/__tests__/heartbeat-service.test.ts +96 -15
- package/src/__tests__/history-repair-pipeline.test.ts +399 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
- package/src/__tests__/host-proxy-interface.test.ts +36 -2
- package/src/__tests__/host-shell-tool.test.ts +124 -18
- package/src/__tests__/http-user-message-parity.test.ts +29 -1
- package/src/__tests__/image-credentials.test.ts +137 -0
- package/src/__tests__/image-service-dispatcher.test.ts +186 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
- package/src/__tests__/injector-chain.test.ts +526 -0
- package/src/__tests__/intent-routing.test.ts +1 -66
- package/src/__tests__/llm-call-pipeline.test.ts +285 -0
- 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__/media-generate-image.test.ts +119 -13
- package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
- package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
- package/src/__tests__/messaging-skill-split.test.ts +3 -34
- package/src/__tests__/migration-import-from-url.test.ts +621 -0
- package/src/__tests__/model-intents.test.ts +11 -83
- package/src/__tests__/notification-broadcaster.test.ts +3 -3
- 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__/notification-decision-strategy.test.ts +0 -11
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
- package/src/__tests__/oauth-apps-routes.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +14 -12
- package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
- package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
- package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
- package/src/__tests__/oauth-providers-routes.test.ts +3 -2
- package/src/__tests__/oauth-store.test.ts +46 -78
- package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
- package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
- package/src/__tests__/onboarding-template-contract.test.ts +16 -64
- package/src/__tests__/openai-image-service.test.ts +368 -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__/overflow-reduce-pipeline.test.ts +676 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +1 -25
- package/src/__tests__/permission-mode.test.ts +16 -0
- package/src/__tests__/permission-types.test.ts +0 -1
- package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
- package/src/__tests__/persistence-pipeline.test.ts +377 -0
- package/src/__tests__/persona-resolver.test.ts +13 -13
- package/src/__tests__/pipeline-runner.test.ts +565 -0
- package/src/__tests__/pkb-autoinject.test.ts +37 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
- package/src/__tests__/platform.test.ts +5 -2
- package/src/__tests__/plugin-bootstrap.test.ts +483 -0
- package/src/__tests__/plugin-registry.test.ts +273 -0
- package/src/__tests__/plugin-route-contribution.test.ts +288 -0
- package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
- package/src/__tests__/plugin-types.test.ts +320 -0
- package/src/__tests__/pricing.test.ts +93 -14
- 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 +69 -9
- package/src/__tests__/reaction-persistence.test.ts +561 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +0 -2
- 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__/schedule-routes.test.ts +131 -1
- package/src/__tests__/scheduler-recurrence.test.ts +14 -70
- package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
- package/src/__tests__/secret-detection-handler.test.ts +0 -10
- 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-identity.test.ts +0 -134
- 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 +259 -3
- package/src/__tests__/system-prompt.test.ts +22 -35
- package/src/__tests__/task-memory-cleanup.test.ts +1 -0
- package/src/__tests__/task-runner.test.ts +3 -1
- package/src/__tests__/task-scheduler.test.ts +3 -15
- package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
- package/src/__tests__/terminal-tools.test.ts +8 -0
- package/src/__tests__/test-preload.ts +11 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
- package/src/__tests__/thread-backfill.test.ts +941 -0
- package/src/__tests__/title-generate-pipeline.test.ts +224 -0
- package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
- package/src/__tests__/tool-error-pipeline.test.ts +244 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -8
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
- package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
- package/src/__tests__/tool-executor.test.ts +201 -94
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -110
- 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__/user-plugin-loader.test.ts +191 -0
- 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-046-seed-conversation-starters-callsite.test.ts +185 -0
- package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
- package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
- package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
- package/src/__tests__/workspace-policy.test.ts +22 -16
- package/src/acp/client-handler.ts +1 -2
- package/src/agent/loop.ts +545 -115
- package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
- package/src/approvals/guardian-request-resolvers.ts +80 -0
- 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/backup/__tests__/backup-worker.test.ts +2 -13
- package/src/backup/backup-worker.ts +3 -15
- 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/bundler/app-compiler.ts +84 -1
- package/src/calls/call-state.ts +2 -2
- 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/channels/__tests__/types.test.ts +3 -3
- package/src/channels/types.ts +6 -4
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/__tests__/notifications.test.ts +87 -211
- package/src/cli/commands/__tests__/attachment.test.ts +438 -0
- package/src/cli/commands/__tests__/backup.test.ts +1 -1
- 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 +886 -0
- package/src/cli/commands/__tests__/inference-send.test.ts +463 -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 +606 -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/backup.ts +2 -2
- package/src/cli/commands/browser.ts +350 -0
- package/src/cli/commands/cache.ts +341 -0
- package/src/cli/commands/clients.ts +138 -0
- package/src/cli/commands/completions.ts +2 -12
- package/src/cli/commands/config.ts +6 -6
- package/src/cli/commands/conversations-import.ts +347 -0
- package/src/cli/commands/conversations.ts +69 -8
- package/src/cli/commands/email.ts +234 -194
- package/src/cli/commands/image-generation.ts +299 -0
- package/src/cli/commands/inference.ts +200 -0
- package/src/cli/commands/memory.ts +127 -17
- package/src/cli/commands/notifications.ts +68 -103
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +2 -2
- package/src/cli/commands/oauth/providers.ts +176 -8
- package/src/cli/commands/oauth/status.ts +46 -36
- 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/skills.ts +3 -4
- 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 +39 -24
- package/src/cli.ts +0 -37
- package/src/config/__tests__/backup-schema.test.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +5 -5
- package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
- package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +20 -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 +69 -12
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
- package/src/config/bundled-skills/schedule/SKILL.md +8 -3
- package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
- package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-tool-registry.ts +0 -190
- package/src/config/env.ts +7 -2
- package/src/config/feature-flag-registry.json +42 -10
- 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 +49 -41
- package/src/config/schemas/analysis.ts +3 -22
- package/src/config/schemas/backup.ts +1 -1
- package/src/config/schemas/calls.ts +0 -4
- package/src/config/schemas/conversations.ts +16 -0
- 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 +317 -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 +64 -0
- package/src/config/schemas/updates.ts +1 -1
- package/src/config/schemas/workspace-git.ts +3 -40
- package/src/config/skill-state.ts +6 -2
- package/src/config/skills.ts +96 -7
- package/src/context/__tests__/compact-prompt.test.ts +63 -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 +26 -0
- package/src/context/token-estimator.ts +61 -3
- package/src/context/tool-result-truncation.ts +3 -63
- package/src/context/window-manager.ts +417 -39
- 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/credential-health/credential-health-service.ts +19 -6
- package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
- 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 -3
- package/src/daemon/context-overflow-policy.ts +4 -13
- package/src/daemon/context-overflow-reducer.ts +4 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +162 -34
- package/src/daemon/conversation-agent-loop.ts +1282 -599
- package/src/daemon/conversation-attachments.ts +2 -6
- package/src/daemon/conversation-error.ts +36 -1
- package/src/daemon/conversation-history.ts +10 -19
- package/src/daemon/conversation-lifecycle.ts +59 -17
- package/src/daemon/conversation-messaging.ts +73 -4
- package/src/daemon/conversation-notifiers.ts +2 -110
- package/src/daemon/conversation-process.ts +24 -11
- package/src/daemon/conversation-queue-manager.ts +3 -0
- package/src/daemon/conversation-runtime-assembly.ts +1063 -211
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +389 -1
- package/src/daemon/conversation-tool-setup.ts +51 -9
- package/src/daemon/conversation-usage.ts +1 -1
- package/src/daemon/conversation.ts +197 -64
- package/src/daemon/external-plugins-bootstrap.ts +478 -0
- package/src/daemon/external-skills-bootstrap.ts +41 -0
- package/src/daemon/first-greeting.ts +191 -14
- 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 +65 -12
- package/src/daemon/handlers/conversations.ts +9 -2
- package/src/daemon/handlers/shared.ts +39 -11
- package/src/daemon/handlers/skills.ts +7 -3
- package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
- package/src/daemon/lifecycle.ts +109 -82
- package/src/daemon/message-types/computer-use.ts +2 -34
- package/src/daemon/message-types/conversations.ts +63 -0
- package/src/daemon/message-types/messages.ts +21 -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 +122 -12
- package/src/daemon/shutdown-handlers.ts +2 -12
- package/src/daemon/tool-side-effects.ts +14 -65
- 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/__tests__/heartbeat-feed-event.test.ts +160 -0
- package/src/heartbeat/heartbeat-service.ts +99 -28
- package/src/home/__tests__/feed-population-integration.test.ts +312 -0
- 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 +11 -0
- package/src/home/feed-scheduler.ts +20 -4
- package/src/home/feed-types.ts +97 -4
- package/src/home/relationship-state-writer.ts +2 -2
- package/src/home/rewrite-command-preview.ts +66 -0
- 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 +34 -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 +6 -3
- package/src/ipc/routes/attachment.ts +114 -0
- package/src/ipc/routes/browser-context.ts +63 -0
- package/src/ipc/routes/browser.ts +97 -0
- package/src/ipc/routes/cache.ts +96 -0
- package/src/ipc/routes/get-contact.ts +16 -0
- package/src/ipc/routes/index.ts +31 -1
- package/src/ipc/routes/list-clients.ts +31 -0
- package/src/ipc/routes/merge-contacts.ts +17 -0
- package/src/ipc/routes/notification.ts +133 -0
- package/src/ipc/routes/rename-conversation.ts +59 -0
- package/src/ipc/routes/search-contacts.ts +19 -0
- 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/upsert-contact.ts +25 -0
- package/src/ipc/routes/watcher.ts +203 -0
- package/src/ipc/socket-path.ts +76 -0
- package/src/media/app-icon-generator.ts +23 -46
- package/src/media/avatar-router.ts +26 -41
- package/src/media/gemini-image-service.ts +8 -41
- package/src/media/image-credentials.ts +73 -0
- package/src/media/image-service.ts +85 -0
- package/src/media/openai-image-service.ts +131 -0
- package/src/media/types.ts +46 -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 +133 -3
- package/src/memory/conversation-group-migration.ts +38 -6
- package/src/memory/conversation-queries.ts +57 -4
- package/src/memory/conversation-title-service.ts +32 -4
- package/src/memory/db-init.ts +10 -0
- package/src/memory/embedding-backend.ts +1 -1
- package/src/memory/embedding-gemini.test.ts +41 -2
- package/src/memory/embedding-gemini.ts +6 -1
- package/src/memory/graph/bootstrap.test.ts +282 -0
- package/src/memory/graph/bootstrap.ts +8 -5
- 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 +183 -53
- package/src/memory/graph/graph-search.test.ts +93 -0
- package/src/memory/graph/graph-search.ts +4 -1
- package/src/memory/graph/inspect.ts +2 -2
- 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 +237 -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/041-approval-prompt-ts-tracker.ts +26 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
- package/src/memory/migrations/149-oauth-tables.ts +1 -0
- 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/223-schedule-script-column.ts +11 -0
- package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
- package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/pkb/pkb-index.test.ts +369 -0
- package/src/memory/pkb/pkb-index.ts +255 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +252 -0
- package/src/memory/pkb/pkb-reconcile.ts +148 -0
- package/src/memory/pkb/pkb-search.test.ts +499 -0
- package/src/memory/pkb/pkb-search.ts +159 -0
- package/src/memory/pkb/types.ts +53 -0
- package/src/memory/qdrant-client.test.ts +60 -0
- package/src/memory/qdrant-client.ts +147 -1
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/schema/oauth.ts +4 -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 +1421 -0
- package/src/messaging/providers/slack/render-transcript.ts +501 -0
- package/src/messaging/style-analyzer.ts +5 -2
- package/src/notifications/README.md +9 -5
- package/src/notifications/conversation-pairing.ts +78 -19
- package/src/notifications/copy-composer.ts +0 -5
- package/src/notifications/decision-engine.ts +3 -9
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/preference-extractor.ts +2 -6
- package/src/notifications/signal.ts +1 -2
- package/src/oauth/AGENTS.md +1 -1
- package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
- package/src/oauth/connect-orchestrator.ts +8 -34
- package/src/oauth/connect-types.ts +6 -10
- package/src/oauth/manual-token-connection.ts +23 -0
- package/src/oauth/oauth-store.ts +31 -14
- package/src/oauth/platform-connection.test.ts +47 -0
- package/src/oauth/platform-connection.ts +15 -5
- package/src/oauth/provider-serializer.ts +6 -1
- package/src/oauth/seed-providers.ts +56 -106
- package/src/outbound-proxy/http-forwarder.ts +9 -0
- package/src/permissions/approval-policy.test.ts +1223 -0
- package/src/permissions/approval-policy.ts +309 -0
- package/src/permissions/arg-parser.test.ts +161 -0
- package/src/permissions/arg-parser.ts +141 -0
- package/src/permissions/bash-risk-classifier.test.ts +1620 -0
- package/src/permissions/bash-risk-classifier.ts +950 -0
- package/src/permissions/checker.ts +348 -711
- package/src/permissions/command-registry.test.ts +774 -0
- package/src/permissions/command-registry.ts +1005 -0
- package/src/permissions/defaults.ts +28 -79
- package/src/permissions/file-risk-classifier.test.ts +535 -0
- package/src/permissions/file-risk-classifier.ts +274 -0
- package/src/permissions/gateway-threshold-reader.ts +196 -0
- package/src/permissions/prompter.ts +4 -0
- package/src/permissions/risk-types.ts +262 -0
- package/src/permissions/schedule-risk-classifier.test.ts +129 -0
- package/src/permissions/schedule-risk-classifier.ts +85 -0
- package/src/permissions/secret-prompter.ts +53 -2
- package/src/permissions/shell-identity.ts +2 -42
- 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 +25 -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 +9 -19
- package/src/platform/client.ts +19 -1
- package/src/plugins/defaults/circuit-breaker.ts +146 -0
- package/src/plugins/defaults/compaction.ts +145 -0
- package/src/plugins/defaults/empty-response.ts +126 -0
- package/src/plugins/defaults/history-repair.ts +85 -0
- package/src/plugins/defaults/index.ts +116 -0
- package/src/plugins/defaults/injectors.ts +491 -0
- package/src/plugins/defaults/llm-call.ts +82 -0
- package/src/plugins/defaults/memory-retrieval.ts +226 -0
- package/src/plugins/defaults/overflow-reduce.ts +181 -0
- package/src/plugins/defaults/persistence.ts +129 -0
- package/src/plugins/defaults/title-generate.ts +95 -0
- package/src/plugins/defaults/token-estimate.ts +104 -0
- package/src/plugins/defaults/tool-error.ts +126 -0
- package/src/plugins/defaults/tool-execute.ts +89 -0
- package/src/plugins/defaults/tool-result-truncate.ts +88 -0
- package/src/plugins/pipeline.ts +316 -0
- package/src/plugins/plugin-skill-contributions.ts +292 -0
- package/src/plugins/registry.ts +241 -0
- package/src/plugins/types.ts +1134 -0
- package/src/plugins/user-loader.ts +177 -0
- package/src/prompts/persona-resolver.ts +3 -3
- package/src/prompts/system-prompt.ts +19 -20
- package/src/prompts/templates/BOOTSTRAP.md +27 -77
- 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 +524 -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 +80 -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/deepgram-realtime.test.ts +61 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +57 -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 +646 -0
- package/src/providers/speech-to-text/xai-realtime.ts +821 -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 +27 -18
- package/src/runtime/__tests__/agent-wake.test.ts +43 -2
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
- package/src/runtime/__tests__/client-registry.test.ts +293 -0
- 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/client-registry.ts +261 -0
- 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 +129 -9
- package/src/runtime/http-types.ts +23 -3
- 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-builder.ts +1 -22
- 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 +78 -0
- package/src/runtime/routes/approval-routes.ts +29 -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/browser-extension-pair-routes.ts +27 -8
- 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 +351 -138
- 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 +987 -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/memory-item-routes.test.ts +1 -0
- package/src/runtime/routes/migration-routes.ts +720 -127
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
- package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
- package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
- package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
- package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
- package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
- package/src/runtime/routes/playground/deps.ts +56 -0
- package/src/runtime/routes/playground/force-compact.ts +73 -0
- package/src/runtime/routes/playground/guard.ts +37 -0
- package/src/runtime/routes/playground/index.ts +28 -0
- package/src/runtime/routes/playground/inject-failures.ts +159 -0
- package/src/runtime/routes/playground/reset-circuit.ts +115 -0
- package/src/runtime/routes/playground/seed-conversation.ts +139 -0
- package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
- package/src/runtime/routes/playground/state.ts +78 -0
- package/src/runtime/routes/schedule-routes.ts +89 -8
- 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 +97 -15
- package/src/schedule/run-script.ts +68 -0
- package/src/schedule/schedule-store.ts +7 -1
- package/src/schedule/scheduler.ts +56 -8
- 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 +35 -9
- 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 +234 -2
- package/src/tools/browser/browser-execution.ts +150 -54
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
- package/src/tools/browser/cdp-client/factory.ts +15 -4
- package/src/tools/credentials/tool-policy.ts +39 -5
- package/src/tools/credentials/vault.ts +9 -4
- package/src/tools/executor.ts +129 -73
- 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/script-proxy/session-manager.ts +37 -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 +116 -46
- package/src/tools/policy-context.ts +29 -8
- package/src/tools/registry.ts +195 -6
- package/src/tools/schedule/create.ts +23 -8
- package/src/tools/schedule/update.ts +3 -1
- package/src/tools/secret-detection-handler.ts +0 -51
- 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/system/avatar-generator.ts +6 -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 +40 -5
- 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 +9 -4
- package/src/util/pricing.ts +41 -8
- 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/006-services-config.ts +2 -4
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
- 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 +56 -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/046-seed-conversation-starters-callsite.ts +108 -0
- package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
- package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/registry.ts +28 -0
- package/src/workspace/provider-commit-message-generator.ts +19 -38
- package/tsconfig.json +1 -1
- package/hook-templates/debug-prompt-logger/hook.json +0 -7
- package/hook-templates/debug-prompt-logger/run.sh +0 -66
- package/src/__tests__/context-overflow-approval.test.ts +0 -156
- 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__/hooks-blocking.test.ts +0 -178
- package/src/__tests__/hooks-cli.test.ts +0 -182
- package/src/__tests__/hooks-config.test.ts +0 -108
- package/src/__tests__/hooks-discovery.test.ts +0 -211
- package/src/__tests__/hooks-integration.test.ts +0 -196
- package/src/__tests__/hooks-manager.test.ts +0 -226
- package/src/__tests__/hooks-runner.test.ts +0 -175
- package/src/__tests__/hooks-settings.test.ts +0 -160
- package/src/__tests__/hooks-templates.test.ts +0 -169
- package/src/__tests__/hooks-ts-runner.test.ts +0 -170
- package/src/__tests__/hooks-watch.test.ts +0 -112
- package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
- package/src/__tests__/oauth-scope-policy.test.ts +0 -180
- 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__/send-notification-tool.test.ts +0 -83
- 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/cli/commands/shotgun.ts +0 -266
- 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/conversations/SKILL.md +0 -20
- package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -66
- 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/heartbeat/SKILL.md +0 -43
- package/src/config/bundled-skills/notifications/SKILL.md +0 -40
- package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
- package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
- 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/screen-watch/SKILL.md +0 -27
- package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
- package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
- package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
- 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/daemon/context-overflow-approval.ts +0 -52
- package/src/daemon/watch-handler.ts +0 -399
- package/src/hooks/cli.ts +0 -253
- package/src/hooks/config.ts +0 -100
- package/src/hooks/discovery.ts +0 -135
- package/src/hooks/manager.ts +0 -179
- package/src/hooks/runner.ts +0 -117
- package/src/hooks/templates.ts +0 -77
- package/src/hooks/types.ts +0 -75
- package/src/oauth/scope-policy.ts +0 -89
- 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/runtime/gateway-internal-client.ts +0 -94
- package/src/runtime/routes/watch-routes.ts +0 -156
- package/src/shared/provider-env-vars.ts +0 -19
- package/src/signals/shotgun.ts +0 -203
- package/src/tools/watch/screen-watch.ts +0 -144
- package/src/tools/watch/watch-state.ts +0 -142
- 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
|
@@ -10,14 +10,43 @@ import { join, resolve } from "node:path";
|
|
|
10
10
|
|
|
11
11
|
import { type ChannelId, parseInterfaceId } from "../channels/types.js";
|
|
12
12
|
import { getAppDirPath, listAppFiles } from "../memory/app-store.js";
|
|
13
|
+
import {
|
|
14
|
+
getMessages as defaultGetMessages,
|
|
15
|
+
type MessageRow,
|
|
16
|
+
} from "../memory/conversation-crud.js";
|
|
17
|
+
import {
|
|
18
|
+
countMemoryPrefixBlocks,
|
|
19
|
+
extractMemoryPrefixBlocks,
|
|
20
|
+
} from "../memory/graph/conversation-graph-memory.js";
|
|
21
|
+
import type { QdrantSparseVector } from "../memory/qdrant-client.js";
|
|
22
|
+
import { readSlackMetadata } from "../messaging/providers/slack/message-metadata.js";
|
|
23
|
+
import {
|
|
24
|
+
extractTagLineTexts,
|
|
25
|
+
isReactionTagLine,
|
|
26
|
+
type RenderableSlackMessage,
|
|
27
|
+
renderSlackTranscript,
|
|
28
|
+
} from "../messaging/providers/slack/render-transcript.js";
|
|
13
29
|
import { isPermissionControlsV2Enabled } from "../permissions/v2-consent-policy.js";
|
|
14
|
-
import
|
|
15
|
-
import type {
|
|
30
|
+
import { getInjectors } from "../plugins/registry.js";
|
|
31
|
+
import type {
|
|
32
|
+
InjectionBlock,
|
|
33
|
+
InjectionPlacement,
|
|
34
|
+
TurnContext,
|
|
35
|
+
TurnInjectionInputs,
|
|
36
|
+
} from "../plugins/types.js";
|
|
37
|
+
import type { ContentBlock, Message } from "../providers/types.js";
|
|
38
|
+
import {
|
|
39
|
+
type ActorTrustContext,
|
|
40
|
+
isUntrustedTrustClass,
|
|
41
|
+
type TrustClass,
|
|
42
|
+
} from "../runtime/actor-trust-resolver.js";
|
|
16
43
|
import { channelStatusToMemberStatus } from "../runtime/routes/inbound-stages/acl-enforcement.js";
|
|
17
44
|
import type { SubagentState } from "../subagent/types.js";
|
|
18
45
|
import { TERMINAL_STATUSES } from "../subagent/types.js";
|
|
19
46
|
import { getWorkspaceDir, getWorkspacePromptPath } from "../util/platform.js";
|
|
20
47
|
import { stripCommentLines } from "../util/strip-comment-lines.js";
|
|
48
|
+
import { filterMessagesForUntrustedActor } from "./conversation-lifecycle.js";
|
|
49
|
+
import { type PkbContextConversation } from "./pkb-context-tracker.js";
|
|
21
50
|
|
|
22
51
|
/**
|
|
23
52
|
* Describes the capabilities of the channel through which the user is
|
|
@@ -493,16 +522,11 @@ export function buildSubagentStatusBlock(
|
|
|
493
522
|
return lines.join("\n");
|
|
494
523
|
}
|
|
495
524
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
return {
|
|
502
|
-
...message,
|
|
503
|
-
content: [...message.content, { type: "text" as const, text: statusBlock }],
|
|
504
|
-
};
|
|
505
|
-
}
|
|
525
|
+
// The `<active_subagents>` block is emitted by the `subagent-status` default
|
|
526
|
+
// injector (`plugins/defaults/injectors.ts`) as an `append-user-tail`
|
|
527
|
+
// placement. Use {@link applyRuntimeInjections} with
|
|
528
|
+
// `options.subagentStatusBlock` set, or drive the injector chain directly
|
|
529
|
+
// via `collectInjectorBlocks`.
|
|
506
530
|
|
|
507
531
|
/**
|
|
508
532
|
* Append voice call-control protocol instructions to the last user
|
|
@@ -546,47 +570,17 @@ export function readNowScratchpad(): string | null {
|
|
|
546
570
|
}
|
|
547
571
|
|
|
548
572
|
/**
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
* thing the model reads.
|
|
573
|
+
* The `<NOW.md>` block is emitted by the `now-md` default injector
|
|
574
|
+
* (`plugins/defaults/injectors.ts`) as an `after-memory-prefix` placement.
|
|
575
|
+
* Use {@link applyRuntimeInjections} with `options.nowScratchpad` set.
|
|
553
576
|
*/
|
|
554
|
-
export function injectNowScratchpad(
|
|
555
|
-
message: Message,
|
|
556
|
-
content: string,
|
|
557
|
-
): Message {
|
|
558
|
-
const scratchpadBlock = {
|
|
559
|
-
type: "text" as const,
|
|
560
|
-
text: `<NOW.md Always keep this up to date>\n${content}\n</NOW.md>`,
|
|
561
|
-
};
|
|
562
|
-
|
|
563
|
-
// Find insertion point: skip any leading injected-context text blocks
|
|
564
|
-
// (e.g. memory_context) so the scratchpad lands between injected context
|
|
565
|
-
// and the user's original content.
|
|
566
|
-
let insertIdx = 0;
|
|
567
|
-
for (let i = 0; i < message.content.length; i++) {
|
|
568
|
-
const block = message.content[i];
|
|
569
|
-
if (block.type === "text" && block.text.startsWith("<memory_context")) {
|
|
570
|
-
insertIdx = i + 1;
|
|
571
|
-
} else {
|
|
572
|
-
break;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
return {
|
|
577
|
-
...message,
|
|
578
|
-
content: [
|
|
579
|
-
...message.content.slice(0, insertIdx),
|
|
580
|
-
scratchpadBlock,
|
|
581
|
-
...message.content.slice(insertIdx),
|
|
582
|
-
],
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
577
|
|
|
586
578
|
/** Strip `<NOW.md>` blocks injected by `injectNowScratchpad`. */
|
|
587
579
|
export function stripNowScratchpad(messages: Message[]): Message[] {
|
|
588
580
|
return stripUserTextBlocksByPrefix(messages, [
|
|
589
|
-
|
|
581
|
+
// Shared prefix catches both the current tag and any pre-line-limit
|
|
582
|
+
// variant that may linger in in-flight histories during a rolling deploy.
|
|
583
|
+
"<NOW.md Always keep this up to date",
|
|
590
584
|
"<now_scratchpad>", // backward-compat: strip legacy blocks from pre-rename history
|
|
591
585
|
]);
|
|
592
586
|
}
|
|
@@ -607,14 +601,6 @@ const AUTOINJECT_FILENAME = "_autoinject.md";
|
|
|
607
601
|
/** Max buffer.md lines injected into prompts — keeps context bounded even when filing is off. */
|
|
608
602
|
const MAX_BUFFER_LINES = 50;
|
|
609
603
|
|
|
610
|
-
const PKB_SYSTEM_REMINDER =
|
|
611
|
-
"<system_reminder>" +
|
|
612
|
-
"\n**CRITICAL:** you MUST read any PKB files that might be relevant to this conversation — " +
|
|
613
|
-
"INDEX.md is your table of contents. Don't wait to be asked. " +
|
|
614
|
-
"Use `remember` OFTEN for EVERY new fact you learn IMMEDIATELY, don't wait for the next turn. " +
|
|
615
|
-
"Corrections to things you had wrong are the highest-priority remembers — never skip them." +
|
|
616
|
-
"\n</system_reminder>";
|
|
617
|
-
|
|
618
604
|
/**
|
|
619
605
|
* Read `_autoinject.md` from the PKB directory and return the list of
|
|
620
606
|
* filenames to inject.
|
|
@@ -639,6 +625,21 @@ export function readAutoinjectList(pkbDir: string): string[] | null {
|
|
|
639
625
|
}
|
|
640
626
|
}
|
|
641
627
|
|
|
628
|
+
/**
|
|
629
|
+
* Resolve the effective list of auto-inject filenames for a PKB directory.
|
|
630
|
+
*
|
|
631
|
+
* This is the single source of truth used both by `readPkbContext` (which
|
|
632
|
+
* actually injects the files) and by the PKB reminder-hint tracker in
|
|
633
|
+
* `conversation-agent-loop.ts` (which needs to know what's already in
|
|
634
|
+
* context so it doesn't redundantly recommend those files).
|
|
635
|
+
*
|
|
636
|
+
* Returns `PKB_DEFAULT_FILES` when `_autoinject.md` is missing/unreadable,
|
|
637
|
+
* or the parsed list (possibly empty) when it is present.
|
|
638
|
+
*/
|
|
639
|
+
export function getPkbAutoInjectList(pkbRoot: string): string[] {
|
|
640
|
+
return readAutoinjectList(pkbRoot) ?? PKB_DEFAULT_FILES;
|
|
641
|
+
}
|
|
642
|
+
|
|
642
643
|
/**
|
|
643
644
|
* Read the always-loaded PKB files and append a nudge encouraging the
|
|
644
645
|
* assistant to proactively read topic files and use `remember` aggressively.
|
|
@@ -653,7 +654,7 @@ export function readPkbContext(): string | null {
|
|
|
653
654
|
const pkbDir = join(getWorkspaceDir(), "pkb");
|
|
654
655
|
if (!existsSync(pkbDir)) return null;
|
|
655
656
|
|
|
656
|
-
const filesToInject =
|
|
657
|
+
const filesToInject = getPkbAutoInjectList(pkbDir);
|
|
657
658
|
|
|
658
659
|
const parts: string[] = [];
|
|
659
660
|
for (const file of filesToInject) {
|
|
@@ -680,50 +681,17 @@ export function readPkbContext(): string | null {
|
|
|
680
681
|
return parts.length > 0 ? parts.join("\n\n") : null;
|
|
681
682
|
}
|
|
682
683
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
export function injectPkbContext(message: Message, content: string): Message {
|
|
688
|
-
// Escape closing tags that could break out of the XML wrapper
|
|
689
|
-
const escaped = content.replace(/<\/pkb\s*>/gi, "</pkb>");
|
|
690
|
-
const pkbBlock = {
|
|
691
|
-
type: "text" as const,
|
|
692
|
-
text: `<pkb>\n${escaped}\n</pkb>`,
|
|
693
|
-
};
|
|
684
|
+
// The `<knowledge_base>` block is emitted by the `pkb-context` default
|
|
685
|
+
// injector (`plugins/defaults/injectors.ts`) as an `after-memory-prefix`
|
|
686
|
+
// placement, splicing immediately after any leading memory-prefix blocks.
|
|
687
|
+
// Use {@link applyRuntimeInjections} with `options.pkbContext` set.
|
|
694
688
|
|
|
695
|
-
|
|
696
|
-
let insertIdx = 0;
|
|
697
|
-
for (let i = 0; i < message.content.length; i++) {
|
|
698
|
-
const block = message.content[i];
|
|
699
|
-
if (
|
|
700
|
-
block.type === "text" &&
|
|
701
|
-
(block.text.startsWith("<memory") ||
|
|
702
|
-
block.text.startsWith("</memory_image>") ||
|
|
703
|
-
block.text.startsWith("<memory_context"))
|
|
704
|
-
) {
|
|
705
|
-
insertIdx = i + 1;
|
|
706
|
-
} else if (block.type === "image") {
|
|
707
|
-
// Memory images precede the memory text block
|
|
708
|
-
insertIdx = i + 1;
|
|
709
|
-
} else {
|
|
710
|
-
break;
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
return {
|
|
715
|
-
...message,
|
|
716
|
-
content: [
|
|
717
|
-
...message.content.slice(0, insertIdx),
|
|
718
|
-
pkbBlock,
|
|
719
|
-
...message.content.slice(insertIdx),
|
|
720
|
-
],
|
|
721
|
-
};
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
/** Strip `<pkb>` blocks injected by `injectPkbContext`. */
|
|
689
|
+
/** Strip `<knowledge_base>` blocks injected by `injectPkbContext`. */
|
|
725
690
|
export function stripPkbContext(messages: Message[]): Message[] {
|
|
726
|
-
return stripUserTextBlocksByPrefix(messages, [
|
|
691
|
+
return stripUserTextBlocksByPrefix(messages, [
|
|
692
|
+
"<knowledge_base>",
|
|
693
|
+
"<pkb>", // backward-compat: strip legacy blocks from pre-rename history
|
|
694
|
+
]);
|
|
727
695
|
}
|
|
728
696
|
|
|
729
697
|
/**
|
|
@@ -909,6 +877,10 @@ export function buildUnifiedTurnContextBlock(
|
|
|
909
877
|
.replace(/[\r\n\u0085\u2028\u2029]+/g, " ")
|
|
910
878
|
// Replace remaining ASCII C0/C1 control characters and DEL.
|
|
911
879
|
.replace(/[\x00-\x1F\x7F-\x9F]/g, " ")
|
|
880
|
+
// Escape XML special characters to prevent turn_context breakout.
|
|
881
|
+
.replace(/&/g, "&")
|
|
882
|
+
.replace(/</g, "<")
|
|
883
|
+
.replace(/>/g, ">")
|
|
912
884
|
.trim();
|
|
913
885
|
return singleLine.length > 0 ? singleLine : "unknown";
|
|
914
886
|
};
|
|
@@ -1088,18 +1060,10 @@ export function stripChannelCapabilityContext(messages: Message[]): Message[] {
|
|
|
1088
1060
|
return stripUserTextBlocksByPrefix(messages, ["<channel_capabilities>"]);
|
|
1089
1061
|
}
|
|
1090
1062
|
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
message: Message,
|
|
1096
|
-
contextText: string,
|
|
1097
|
-
): Message {
|
|
1098
|
-
return {
|
|
1099
|
-
...message,
|
|
1100
|
-
content: [{ type: "text", text: contextText }, ...message.content],
|
|
1101
|
-
};
|
|
1102
|
-
}
|
|
1063
|
+
// The workspace top-level context block is emitted by the
|
|
1064
|
+
// `workspace-context` default injector (`plugins/defaults/injectors.ts`)
|
|
1065
|
+
// as a `prepend-user-tail` placement. Use {@link applyRuntimeInjections}
|
|
1066
|
+
// with `options.workspaceTopLevelContext` set.
|
|
1103
1067
|
|
|
1104
1068
|
/**
|
|
1105
1069
|
* Strip `<active_workspace>` (and legacy `<active_dynamic_page>`) blocks
|
|
@@ -1138,6 +1102,445 @@ export function stripTransportHints(messages: Message[]): Message[] {
|
|
|
1138
1102
|
return stripUserTextBlocksByPrefix(messages, ["<transport_hints>"]);
|
|
1139
1103
|
}
|
|
1140
1104
|
|
|
1105
|
+
// ---------------------------------------------------------------------------
|
|
1106
|
+
// Slack chronological transcript assembly
|
|
1107
|
+
// ---------------------------------------------------------------------------
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* True when the channel capabilities describe a Slack non-DM conversation
|
|
1111
|
+
* (group/channel/mpim). Used to gate thread-only behavior such as the
|
|
1112
|
+
* `<active_thread>` focus block. DMs are excluded because they have no
|
|
1113
|
+
* threads.
|
|
1114
|
+
*
|
|
1115
|
+
* The gateway normalizer sets `chatType: "channel"` for every non-DM Slack
|
|
1116
|
+
* conversation (public, private, and mpim alike — see
|
|
1117
|
+
* `gateway/src/slack/normalize.ts`) and omits the field entirely for DMs.
|
|
1118
|
+
* We therefore accept only `chatType === "channel"` — when the gateway
|
|
1119
|
+
* omits `chatType` (as it does for DMs), the check correctly returns
|
|
1120
|
+
* `false`.
|
|
1121
|
+
*
|
|
1122
|
+
* The chronological-transcript override applies to ALL Slack
|
|
1123
|
+
* conversations (channels and DMs) — gate that on
|
|
1124
|
+
* `channelCapabilities.channel === "slack"` rather than this helper.
|
|
1125
|
+
*/
|
|
1126
|
+
export function isSlackChannelConversation(
|
|
1127
|
+
channelCapabilities?: ChannelCapabilities | null,
|
|
1128
|
+
): boolean {
|
|
1129
|
+
return (
|
|
1130
|
+
channelCapabilities?.channel === "slack" &&
|
|
1131
|
+
channelCapabilities.chatType === "channel"
|
|
1132
|
+
);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
/**
|
|
1136
|
+
* Minimal structural shape of a persisted message row used by the Slack
|
|
1137
|
+
* chronological-transcript assembly path. Decouples the assembly logic from
|
|
1138
|
+
* the DB-row type so it can be unit-tested with plain literals.
|
|
1139
|
+
*/
|
|
1140
|
+
export interface SlackTranscriptInputRow {
|
|
1141
|
+
role: "user" | "assistant";
|
|
1142
|
+
/** Raw persisted content column. JSON-encoded `ContentBlock[]` in production. */
|
|
1143
|
+
content: string;
|
|
1144
|
+
/** Epoch ms when the row was created. */
|
|
1145
|
+
createdAt: number;
|
|
1146
|
+
/** Raw `metadata` column value (JSON string with optional `slackMeta` sub-key). */
|
|
1147
|
+
metadata: string | null;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* Extract the user-facing plain text from an already-parsed `ContentBlock[]`.
|
|
1152
|
+
* Only `text` blocks contribute to the rendered transcript line. Tool-use /
|
|
1153
|
+
* tool-result / thinking blocks are intentionally elided — they would clutter
|
|
1154
|
+
* the Slack-style transcript and the model can already recall them from the
|
|
1155
|
+
* surrounding turn structure.
|
|
1156
|
+
*
|
|
1157
|
+
* Rows with no text blocks (e.g. images, file uploads, pure tool turns) would
|
|
1158
|
+
* otherwise render as an empty transcript line like `[14:25 @alice]: `;
|
|
1159
|
+
* surface the attachment/tool context instead so the model can tell something
|
|
1160
|
+
* was actually said on that turn.
|
|
1161
|
+
*/
|
|
1162
|
+
function extractPlainTextFromBlocks(blocks: ContentBlock[]): string {
|
|
1163
|
+
const parts: string[] = [];
|
|
1164
|
+
const placeholderLabels: string[] = [];
|
|
1165
|
+
for (const block of blocks) {
|
|
1166
|
+
if (!block || typeof block !== "object") continue;
|
|
1167
|
+
if (block.type === "text") {
|
|
1168
|
+
parts.push(block.text);
|
|
1169
|
+
continue;
|
|
1170
|
+
}
|
|
1171
|
+
const label = placeholderForBlockType(block.type);
|
|
1172
|
+
if (label && !placeholderLabels.includes(label)) {
|
|
1173
|
+
placeholderLabels.push(label);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
if (parts.length > 0) {
|
|
1177
|
+
return parts.join("\n");
|
|
1178
|
+
}
|
|
1179
|
+
return placeholderLabels.join(" ");
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
function placeholderForBlockType(type: ContentBlock["type"]): string | null {
|
|
1183
|
+
switch (type) {
|
|
1184
|
+
case "image":
|
|
1185
|
+
return "[image]";
|
|
1186
|
+
case "file":
|
|
1187
|
+
return "[file]";
|
|
1188
|
+
case "tool_use":
|
|
1189
|
+
case "server_tool_use":
|
|
1190
|
+
return "[tool call]";
|
|
1191
|
+
case "tool_result":
|
|
1192
|
+
case "web_search_tool_result":
|
|
1193
|
+
return "[tool result]";
|
|
1194
|
+
case "thinking":
|
|
1195
|
+
case "redacted_thinking":
|
|
1196
|
+
case "text":
|
|
1197
|
+
return null;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* Convert a persisted row into the {@link RenderableSlackMessage} shape
|
|
1203
|
+
* consumed by `renderSlackTranscript`.
|
|
1204
|
+
*
|
|
1205
|
+
* Legacy pre-upgrade rows (no `slackMeta` sub-key, malformed metadata, etc.)
|
|
1206
|
+
* yield `metadata: null`; the renderer then takes its flat-render fallback
|
|
1207
|
+
* path and the row stays in chronological order via `createdAt`.
|
|
1208
|
+
*
|
|
1209
|
+
* Sender labels are emitted only when they add information beyond the role
|
|
1210
|
+
* slot:
|
|
1211
|
+
* - Reaction rows: always labeled — `@assistant` for the assistant, the real
|
|
1212
|
+
* `slackMeta.displayName` for a known user, or `@user` as a last-resort
|
|
1213
|
+
* subject so the rendered `[time X reacted ...]` line still parses.
|
|
1214
|
+
* - Assistant message rows: `null` — the role slot already says "assistant".
|
|
1215
|
+
* - User message rows: real `slackMeta.displayName` when available (to
|
|
1216
|
+
* disambiguate speakers in multi-party channels); `null` otherwise so the
|
|
1217
|
+
* renderer drops the redundant `@user` placeholder.
|
|
1218
|
+
*/
|
|
1219
|
+
function rowToRenderable(row: SlackTranscriptInputRow): RenderableSlackMessage {
|
|
1220
|
+
let slackMeta: ReturnType<typeof readSlackMetadata> = null;
|
|
1221
|
+
if (row.metadata) {
|
|
1222
|
+
try {
|
|
1223
|
+
const outer = JSON.parse(row.metadata) as { slackMeta?: unknown };
|
|
1224
|
+
if (typeof outer.slackMeta === "string") {
|
|
1225
|
+
slackMeta = readSlackMetadata(outer.slackMeta);
|
|
1226
|
+
}
|
|
1227
|
+
} catch {
|
|
1228
|
+
// Malformed metadata — fall through to legacy/null treatment.
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
const isReaction = slackMeta?.eventKind === "reaction";
|
|
1233
|
+
let senderLabel: string | null;
|
|
1234
|
+
if (isReaction) {
|
|
1235
|
+
senderLabel =
|
|
1236
|
+
row.role === "assistant"
|
|
1237
|
+
? "@assistant"
|
|
1238
|
+
: (slackMeta?.displayName ?? "@user");
|
|
1239
|
+
} else if (row.role === "assistant") {
|
|
1240
|
+
senderLabel = null;
|
|
1241
|
+
} else {
|
|
1242
|
+
senderLabel = slackMeta?.displayName ?? null;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
// Parse `row.content` once and derive both the structured `contentBlocks`
|
|
1246
|
+
// view (for downstream tool-block preservation) and the flattened
|
|
1247
|
+
// `plainText` view (used for tag-line rendering) from the same parsed
|
|
1248
|
+
// result. Large Slack histories with many tool payloads would otherwise
|
|
1249
|
+
// pay a double JSON-parse cost per row.
|
|
1250
|
+
let contentBlocks: ContentBlock[] = [];
|
|
1251
|
+
let plainText: string;
|
|
1252
|
+
try {
|
|
1253
|
+
const parsed = JSON.parse(row.content);
|
|
1254
|
+
if (Array.isArray(parsed)) {
|
|
1255
|
+
contentBlocks = parsed as ContentBlock[];
|
|
1256
|
+
plainText = extractPlainTextFromBlocks(contentBlocks);
|
|
1257
|
+
} else if (typeof parsed === "string") {
|
|
1258
|
+
plainText = parsed;
|
|
1259
|
+
} else {
|
|
1260
|
+
plainText = row.content;
|
|
1261
|
+
}
|
|
1262
|
+
} catch {
|
|
1263
|
+
// Plain string row (legacy) — no structured blocks to preserve.
|
|
1264
|
+
plainText = row.content;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// Attachment-only rows (images, files) carry no text block, so the
|
|
1268
|
+
// transcript renderer would normally emit them *without* a tag line —
|
|
1269
|
+
// the model sees the image but loses sender/timestamp attribution.
|
|
1270
|
+
// Synthesize a leading text block carrying the placeholder so the
|
|
1271
|
+
// renderer emits `[14:25 @alice]: [image]` and then the image itself.
|
|
1272
|
+
// Pure tool-only rows (tool_use / tool_result) are intentionally
|
|
1273
|
+
// excluded — those are synthetic turn continuations that should stay
|
|
1274
|
+
// tag-line-free, matching the documented behaviour in
|
|
1275
|
+
// `buildMessageContentBlocks`.
|
|
1276
|
+
const hasTextBlock = contentBlocks.some((b) => b?.type === "text");
|
|
1277
|
+
const hasAttachmentBlock = contentBlocks.some(
|
|
1278
|
+
(b) => b?.type === "image" || b?.type === "file",
|
|
1279
|
+
);
|
|
1280
|
+
if (!hasTextBlock && hasAttachmentBlock && plainText !== "") {
|
|
1281
|
+
contentBlocks = [{ type: "text", text: plainText }, ...contentBlocks];
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
return {
|
|
1285
|
+
role: row.role,
|
|
1286
|
+
content: plainText,
|
|
1287
|
+
metadata: slackMeta,
|
|
1288
|
+
senderLabel,
|
|
1289
|
+
createdAt: row.createdAt,
|
|
1290
|
+
contentBlocks,
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
/**
|
|
1295
|
+
* Build a chronological Slack transcript for Slack conversations (both DMs
|
|
1296
|
+
* and group/channel/mpim) and project it onto the LLM-facing `Message[]`
|
|
1297
|
+
* shape.
|
|
1298
|
+
*
|
|
1299
|
+
* Returns `null` when the channel is not Slack (caller should fall through
|
|
1300
|
+
* to the default message history). Legacy pre-upgrade rows without
|
|
1301
|
+
* `slackMeta` are tolerated: the renderer's flat fallback orders them by
|
|
1302
|
+
* `createdAt` alongside post-upgrade rows.
|
|
1303
|
+
*
|
|
1304
|
+
* For ALL Slack conversations (channels and DMs), `<transport_hints>`
|
|
1305
|
+
* injection is suppressed by `applyRuntimeInjections` so the model sees
|
|
1306
|
+
* one consistent persisted view instead of a duplicated gateway hint.
|
|
1307
|
+
*/
|
|
1308
|
+
export function assembleSlackChronologicalMessages(
|
|
1309
|
+
rows: SlackTranscriptInputRow[],
|
|
1310
|
+
capabilities: ChannelCapabilities,
|
|
1311
|
+
): Message[] | null {
|
|
1312
|
+
if (capabilities.channel !== "slack") {
|
|
1313
|
+
return null;
|
|
1314
|
+
}
|
|
1315
|
+
const renderable = rows.map(rowToRenderable);
|
|
1316
|
+
return renderSlackTranscript(renderable);
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
/**
|
|
1320
|
+
* Load DB rows for a Slack conversation and project them onto the
|
|
1321
|
+
* chronological transcript shape.
|
|
1322
|
+
*
|
|
1323
|
+
* Convenience wrapper over `getMessages` + `assembleSlackChronologicalMessages`.
|
|
1324
|
+
* The loader is exposed as a parameter so tests can substitute a stub. In
|
|
1325
|
+
* production it defaults to `getMessages` from `conversation-crud.ts`.
|
|
1326
|
+
*
|
|
1327
|
+
* When `trustClass` identifies an untrusted actor (guardian-scoped rows
|
|
1328
|
+
* must not leak into the model context), rows are passed through
|
|
1329
|
+
* `filterMessagesForUntrustedActor` before assembly — mirroring the
|
|
1330
|
+
* filtering applied in `loadFromDb` so the chronological transcript
|
|
1331
|
+
* respects the same per-actor scoping as the default history path.
|
|
1332
|
+
*
|
|
1333
|
+
* Returns `null` when the channel is not Slack — callers should fall
|
|
1334
|
+
* through to the default in-memory message history.
|
|
1335
|
+
*/
|
|
1336
|
+
export function loadSlackChronologicalMessages(
|
|
1337
|
+
conversationId: string,
|
|
1338
|
+
capabilities: ChannelCapabilities,
|
|
1339
|
+
options: {
|
|
1340
|
+
loader?: (id: string) => MessageRow[];
|
|
1341
|
+
trustClass?: TrustClass;
|
|
1342
|
+
} = {},
|
|
1343
|
+
): Message[] | null {
|
|
1344
|
+
if (capabilities.channel !== "slack") {
|
|
1345
|
+
return null;
|
|
1346
|
+
}
|
|
1347
|
+
const loader = options.loader ?? defaultGetMessages;
|
|
1348
|
+
const allRows = loader(conversationId);
|
|
1349
|
+
const scopedRows = isUntrustedTrustClass(options.trustClass)
|
|
1350
|
+
? filterMessagesForUntrustedActor(allRows)
|
|
1351
|
+
: allRows;
|
|
1352
|
+
// Coerce MessageRow.role (string) to the structural row's stricter union.
|
|
1353
|
+
const rows: SlackTranscriptInputRow[] = scopedRows.map((row) => ({
|
|
1354
|
+
role: row.role === "assistant" ? "assistant" : "user",
|
|
1355
|
+
content: row.content,
|
|
1356
|
+
createdAt: row.createdAt,
|
|
1357
|
+
metadata: row.metadata,
|
|
1358
|
+
}));
|
|
1359
|
+
return assembleSlackChronologicalMessages(rows, capabilities);
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// ---------------------------------------------------------------------------
|
|
1363
|
+
// Active-thread focus block (non-persisted; appended to current user turn)
|
|
1364
|
+
// ---------------------------------------------------------------------------
|
|
1365
|
+
|
|
1366
|
+
/**
|
|
1367
|
+
* Detect the "active" Slack thread ts for the current turn.
|
|
1368
|
+
*
|
|
1369
|
+
* The active thread is the thread the current inbound user message belongs
|
|
1370
|
+
* to: scan from newest to oldest and return the `slackMeta.threadTs` of the
|
|
1371
|
+
* most recent user row that carries one. Returns `null` when no recent user
|
|
1372
|
+
* row sits inside a thread (e.g. the inbound was a top-level channel post,
|
|
1373
|
+
* or the conversation has no Slack-tagged user rows yet).
|
|
1374
|
+
*
|
|
1375
|
+
* Pure: takes pre-mapped renderable rows and returns the ts string only.
|
|
1376
|
+
*/
|
|
1377
|
+
function detectActiveThreadTs(rows: RenderableSlackMessage[]): string | null {
|
|
1378
|
+
for (let i = rows.length - 1; i >= 0; i--) {
|
|
1379
|
+
const row = rows[i];
|
|
1380
|
+
if (row.role !== "user") continue;
|
|
1381
|
+
const meta = row.metadata;
|
|
1382
|
+
if (!meta) continue;
|
|
1383
|
+
if (meta.eventKind !== "message") continue;
|
|
1384
|
+
if (typeof meta.threadTs === "string" && meta.threadTs.length > 0) {
|
|
1385
|
+
return meta.threadTs;
|
|
1386
|
+
}
|
|
1387
|
+
// First non-thread user row wins: the inbound is top-level, no active
|
|
1388
|
+
// thread to focus on.
|
|
1389
|
+
return null;
|
|
1390
|
+
}
|
|
1391
|
+
return null;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
/**
|
|
1395
|
+
* Build a focus block listing every message belonging to the active thread:
|
|
1396
|
+
* the parent (whose `channelTs` equals `activeThreadTs`) plus every reply
|
|
1397
|
+
* (whose `threadTs` equals `activeThreadTs`). Reactions targeting any of
|
|
1398
|
+
* those messages are also pulled in via their `targetChannelTs`. Edits and
|
|
1399
|
+
* deletions surface through the existing renderer markers.
|
|
1400
|
+
*
|
|
1401
|
+
* Returns `null` when no rows match (e.g. parent backfill hasn't run yet
|
|
1402
|
+
* AND the thread has no replies in storage either) so the caller can skip
|
|
1403
|
+
* the empty block. Otherwise returns the rendered XML block ready to append
|
|
1404
|
+
* to the user's tail message.
|
|
1405
|
+
*
|
|
1406
|
+
* Pure: takes pre-mapped renderable rows + a thread ts, returns text only.
|
|
1407
|
+
*/
|
|
1408
|
+
function buildActiveThreadBlockFromRenderable(
|
|
1409
|
+
rows: RenderableSlackMessage[],
|
|
1410
|
+
activeThreadTs: string,
|
|
1411
|
+
): string | null {
|
|
1412
|
+
const members: RenderableSlackMessage[] = [];
|
|
1413
|
+
for (const row of rows) {
|
|
1414
|
+
const meta = row.metadata;
|
|
1415
|
+
if (!meta) continue;
|
|
1416
|
+
if (meta.eventKind === "message") {
|
|
1417
|
+
if (
|
|
1418
|
+
meta.channelTs === activeThreadTs ||
|
|
1419
|
+
meta.threadTs === activeThreadTs
|
|
1420
|
+
) {
|
|
1421
|
+
members.push(row);
|
|
1422
|
+
}
|
|
1423
|
+
continue;
|
|
1424
|
+
}
|
|
1425
|
+
if (
|
|
1426
|
+
meta.eventKind === "reaction" &&
|
|
1427
|
+
meta.reaction &&
|
|
1428
|
+
meta.reaction.targetChannelTs === activeThreadTs
|
|
1429
|
+
) {
|
|
1430
|
+
members.push(row);
|
|
1431
|
+
continue;
|
|
1432
|
+
}
|
|
1433
|
+
// Reactions targeting a reply within the thread also belong in the
|
|
1434
|
+
// focus block — collect them by checking the reaction target against
|
|
1435
|
+
// any thread reply's channelTs we've already accepted. We do this in a
|
|
1436
|
+
// second pass below to avoid an O(n^2) inner scan here.
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// Second pass: pull in reactions whose target is one of the already-
|
|
1440
|
+
// collected reply messages. Using a Set keeps this O(n).
|
|
1441
|
+
const memberChannelTs = new Set(
|
|
1442
|
+
members
|
|
1443
|
+
.map((m) => m.metadata?.channelTs)
|
|
1444
|
+
.filter((v): v is string => typeof v === "string"),
|
|
1445
|
+
);
|
|
1446
|
+
for (const row of rows) {
|
|
1447
|
+
const meta = row.metadata;
|
|
1448
|
+
if (!meta || meta.eventKind !== "reaction" || !meta.reaction) continue;
|
|
1449
|
+
if (meta.reaction.targetChannelTs === activeThreadTs) continue; // already added
|
|
1450
|
+
if (memberChannelTs.has(meta.reaction.targetChannelTs)) {
|
|
1451
|
+
members.push(row);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
if (members.length === 0) return null;
|
|
1456
|
+
|
|
1457
|
+
// The active-thread block is flattened to plain text below, which discards
|
|
1458
|
+
// `Message.role`. Assistant rows are relabeled in the post-render step:
|
|
1459
|
+
// `renderSlackTranscript` emits assistant content with no tag-line wrapper
|
|
1460
|
+
// (to prevent the model mimicking `[MM/DD/YY HH:MM]:` prefixes in outbound
|
|
1461
|
+
// replies), so we prepend an explicit `@assistant:` label to the flattened
|
|
1462
|
+
// line. Unnamed user rows (no real Slack displayName) get a `@user`
|
|
1463
|
+
// senderLabel here so their tag line carries attribution through the
|
|
1464
|
+
// renderer. Labeled user rows and assistant rows pass through unchanged.
|
|
1465
|
+
const labeledMembers = members.map((m) => {
|
|
1466
|
+
if (m.role === "assistant") return m;
|
|
1467
|
+
if (m.senderLabel !== null) return m;
|
|
1468
|
+
return { ...m, senderLabel: "@user" };
|
|
1469
|
+
});
|
|
1470
|
+
|
|
1471
|
+
const rendered = renderSlackTranscript(labeledMembers);
|
|
1472
|
+
if (rendered.length === 0) return null;
|
|
1473
|
+
// Reaction / overflow-trailer lines already embed `@assistant` inline, so
|
|
1474
|
+
// `isReactionTagLine` is used to skip those and avoid double-attribution
|
|
1475
|
+
// (`@assistant: [... @assistant reacted ...]`). Regular content and the
|
|
1476
|
+
// `[deleted]` sentinel get the prefix so attribution survives flattening.
|
|
1477
|
+
const lines = rendered
|
|
1478
|
+
.map((msg) => {
|
|
1479
|
+
const text = extractTagLineTexts([msg])[0] ?? "";
|
|
1480
|
+
return msg.role === "assistant" && !isReactionTagLine(text)
|
|
1481
|
+
? `@assistant: ${text}`
|
|
1482
|
+
: text;
|
|
1483
|
+
})
|
|
1484
|
+
.join("\n");
|
|
1485
|
+
return `<active_thread>\n${lines}\n</active_thread>`;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
/**
|
|
1489
|
+
* Build the Slack active-thread focus block from raw rows.
|
|
1490
|
+
*
|
|
1491
|
+
* Pure assembly entrypoint mirroring `assembleSlackChronologicalMessages`.
|
|
1492
|
+
* Returns the rendered `<active_thread>` block as a string, or `null` when:
|
|
1493
|
+
* - the channel is not Slack, OR
|
|
1494
|
+
* - the channel is a Slack DM (DMs do not have threads), OR
|
|
1495
|
+
* - the latest user row is top-level (not in a thread), OR
|
|
1496
|
+
* - no rows belong to the active thread.
|
|
1497
|
+
*/
|
|
1498
|
+
export function assembleSlackActiveThreadFocusBlock(
|
|
1499
|
+
rows: SlackTranscriptInputRow[],
|
|
1500
|
+
capabilities: ChannelCapabilities,
|
|
1501
|
+
): string | null {
|
|
1502
|
+
if (capabilities.channel !== "slack") return null;
|
|
1503
|
+
// DMs do not have threads, so the focus block is always a no-op.
|
|
1504
|
+
// The gateway sets `chatType: "channel"` for every non-DM Slack
|
|
1505
|
+
// conversation and omits the field for DMs, so gate the focus block
|
|
1506
|
+
// on the positive `"channel"` match.
|
|
1507
|
+
if (capabilities.chatType !== "channel") return null;
|
|
1508
|
+
const renderable = rows.map(rowToRenderable);
|
|
1509
|
+
const activeThreadTs = detectActiveThreadTs(renderable);
|
|
1510
|
+
if (!activeThreadTs) return null;
|
|
1511
|
+
return buildActiveThreadBlockFromRenderable(renderable, activeThreadTs);
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
/**
|
|
1515
|
+
* Loader convenience over `assembleSlackActiveThreadFocusBlock` mirroring
|
|
1516
|
+
* `loadSlackChronologicalMessages`. Returns `null` when the channel is not
|
|
1517
|
+
* Slack, or when it is a Slack DM (DMs have no threads), so callers can
|
|
1518
|
+
* skip the injection entirely without paying for a DB read.
|
|
1519
|
+
*/
|
|
1520
|
+
export function loadSlackActiveThreadFocusBlock(
|
|
1521
|
+
conversationId: string,
|
|
1522
|
+
capabilities: ChannelCapabilities,
|
|
1523
|
+
options: {
|
|
1524
|
+
loader?: (id: string) => MessageRow[];
|
|
1525
|
+
trustClass?: TrustClass;
|
|
1526
|
+
} = {},
|
|
1527
|
+
): string | null {
|
|
1528
|
+
if (capabilities.channel !== "slack") return null;
|
|
1529
|
+
if (capabilities.chatType !== "channel") return null;
|
|
1530
|
+
const loader = options.loader ?? defaultGetMessages;
|
|
1531
|
+
const allRows = loader(conversationId);
|
|
1532
|
+
const scopedRows = isUntrustedTrustClass(options.trustClass)
|
|
1533
|
+
? filterMessagesForUntrustedActor(allRows)
|
|
1534
|
+
: allRows;
|
|
1535
|
+
const rows: SlackTranscriptInputRow[] = scopedRows.map((row) => ({
|
|
1536
|
+
role: row.role === "assistant" ? "assistant" : "user",
|
|
1537
|
+
content: row.content,
|
|
1538
|
+
createdAt: row.createdAt,
|
|
1539
|
+
metadata: row.metadata,
|
|
1540
|
+
}));
|
|
1541
|
+
return assembleSlackActiveThreadFocusBlock(rows, capabilities);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1141
1544
|
/** Prefixes stripped by the pipeline (order doesn't matter — single pass). */
|
|
1142
1545
|
const RUNTIME_INJECTION_PREFIXES = [
|
|
1143
1546
|
"<channel_capabilities>",
|
|
@@ -1164,11 +1567,18 @@ const RUNTIME_INJECTION_PREFIXES = [
|
|
|
1164
1567
|
"<active_workspace>",
|
|
1165
1568
|
"<active_dynamic_page>",
|
|
1166
1569
|
"<non_interactive_context>",
|
|
1167
|
-
|
|
1570
|
+
// Shared prefix catches both the current NOW.md tag and any pre-line-limit
|
|
1571
|
+
// variant that may linger in in-flight histories during a rolling deploy.
|
|
1572
|
+
"<NOW.md Always keep this up to date",
|
|
1168
1573
|
"<now_scratchpad>", // backward-compat: strip legacy blocks from pre-rename history
|
|
1169
|
-
"<
|
|
1574
|
+
"<knowledge_base>",
|
|
1575
|
+
"<pkb>", // backward-compat: strip legacy tag from pre-rename history
|
|
1170
1576
|
"<system_reminder>",
|
|
1171
1577
|
"<transport_hints>",
|
|
1578
|
+
// The Slack active-thread focus block is non-persisted and injected on
|
|
1579
|
+
// the FINAL user turn only. Strip it here so re-assembly during compaction
|
|
1580
|
+
// and overflow recovery does not duplicate it across turns.
|
|
1581
|
+
"<active_thread>",
|
|
1172
1582
|
"<system_notice>One or more tool calls returned an error.",
|
|
1173
1583
|
];
|
|
1174
1584
|
|
|
@@ -1189,16 +1599,23 @@ export function stripInjectionsForCompaction(messages: Message[]): Message[] {
|
|
|
1189
1599
|
* Returns null if no NOW.md injection is found.
|
|
1190
1600
|
*/
|
|
1191
1601
|
export function findLastInjectedNowContent(messages: Message[]): string | null {
|
|
1192
|
-
|
|
1602
|
+
// Matches every NOW.md opening tag we emit (the tag text may evolve over
|
|
1603
|
+
// time, e.g. adding a line-limit hint), so in-flight histories with older
|
|
1604
|
+
// tag variants remain discoverable during a rolling deploy.
|
|
1605
|
+
const openTagPrefix = "<NOW.md Always keep this up to date";
|
|
1193
1606
|
const suffix = "\n</NOW.md>";
|
|
1194
1607
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1195
1608
|
const msg = messages[i];
|
|
1196
1609
|
if (msg.role !== "user") continue;
|
|
1197
1610
|
for (const block of msg.content) {
|
|
1198
|
-
if (block.type
|
|
1199
|
-
|
|
1200
|
-
if (end > prefix.length) return block.text.slice(prefix.length, end);
|
|
1611
|
+
if (block.type !== "text" || !block.text.startsWith(openTagPrefix)) {
|
|
1612
|
+
continue;
|
|
1201
1613
|
}
|
|
1614
|
+
const tagEnd = block.text.indexOf(">\n");
|
|
1615
|
+
if (tagEnd < 0) continue;
|
|
1616
|
+
const contentStart = tagEnd + ">\n".length;
|
|
1617
|
+
const end = block.text.lastIndexOf(suffix);
|
|
1618
|
+
if (end > contentStart) return block.text.slice(contentStart, end);
|
|
1202
1619
|
}
|
|
1203
1620
|
}
|
|
1204
1621
|
return null;
|
|
@@ -1216,76 +1633,519 @@ export function findLastInjectedNowContent(messages: Message[]): string | null {
|
|
|
1216
1633
|
export type InjectionMode = "full" | "minimal";
|
|
1217
1634
|
|
|
1218
1635
|
/**
|
|
1219
|
-
*
|
|
1636
|
+
* Per-turn injection bytes captured so `loadFromDb` can rehydrate historical
|
|
1637
|
+
* user messages byte-for-byte after a daemon restart or conversation
|
|
1638
|
+
* eviction. Persisting the exact injected text onto message metadata keeps
|
|
1639
|
+
* Anthropic's prefix cache anchored to msg[0] instead of invalidating every
|
|
1640
|
+
* turn on reload. Any field left `undefined` means that block was not
|
|
1641
|
+
* injected on this turn.
|
|
1642
|
+
*/
|
|
1643
|
+
export interface RuntimeInjectionBlocks {
|
|
1644
|
+
unifiedTurnContext?: string;
|
|
1645
|
+
pkbSystemReminder?: string;
|
|
1646
|
+
workspaceBlock?: string;
|
|
1647
|
+
nowScratchpadBlock?: string;
|
|
1648
|
+
pkbContextBlock?: string;
|
|
1649
|
+
/**
|
|
1650
|
+
* Composed output of every plugin-registered {@link Injector}, concatenated
|
|
1651
|
+
* in ascending `order`. Empty string when every injector opted out (returned
|
|
1652
|
+
* `null`). Today the default injectors (`default-injectors` plugin)
|
|
1653
|
+
* placeholder-return `null`, so this is only non-empty when a third-party
|
|
1654
|
+
* plugin registers an injector that emits content.
|
|
1655
|
+
*
|
|
1656
|
+
* Populated by {@link composeInjectorChain} during
|
|
1657
|
+
* {@link applyRuntimeInjections}. Distinct from the other `blocks` fields
|
|
1658
|
+
* because those track specific hardcoded injections today; this field is
|
|
1659
|
+
* the extensibility seam for {@link Injector} plugins.
|
|
1660
|
+
*/
|
|
1661
|
+
injectorChainBlock?: string;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
export interface RuntimeInjectionResult {
|
|
1665
|
+
messages: Message[];
|
|
1666
|
+
blocks: RuntimeInjectionBlocks;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
/**
|
|
1670
|
+
* Run every registered {@link Injector}'s `produce()` in ascending `order`
|
|
1671
|
+
* and return every non-null block the chain produced.
|
|
1672
|
+
*
|
|
1673
|
+
* Injectors returning `null` are omitted from the result. The returned array
|
|
1674
|
+
* preserves ascending-`order` sort so downstream callers (notably
|
|
1675
|
+
* {@link applyRuntimeInjections}) can group blocks by `placement` and apply
|
|
1676
|
+
* them declaratively without losing per-injector ordering within each slot.
|
|
1677
|
+
*/
|
|
1678
|
+
export async function collectInjectorBlocks(
|
|
1679
|
+
ctx: TurnContext,
|
|
1680
|
+
): Promise<InjectionBlock[]> {
|
|
1681
|
+
const injectors = getInjectors();
|
|
1682
|
+
if (injectors.length === 0) return [];
|
|
1683
|
+
const out: InjectionBlock[] = [];
|
|
1684
|
+
for (const injector of injectors) {
|
|
1685
|
+
const block = await injector.produce(ctx);
|
|
1686
|
+
if (block) out.push(block);
|
|
1687
|
+
}
|
|
1688
|
+
return out;
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
/**
|
|
1692
|
+
* Run every registered {@link Injector}'s `produce()` in ascending
|
|
1693
|
+
* `order`, concatenate the non-null results into a single block of text,
|
|
1694
|
+
* and return it.
|
|
1695
|
+
*
|
|
1696
|
+
* Separator: blank line between blocks. Injectors returning `null` are
|
|
1697
|
+
* skipped entirely (no leading/trailing blank lines). When no injector
|
|
1698
|
+
* contributes, the function returns an empty string.
|
|
1220
1699
|
*
|
|
1221
|
-
*
|
|
1222
|
-
*
|
|
1700
|
+
* Used by tests that assert the concatenation contract and by callers that
|
|
1701
|
+
* want a single informational string view of the chain. The canonical
|
|
1702
|
+
* integration point is {@link applyRuntimeInjections}, which uses
|
|
1703
|
+
* {@link collectInjectorBlocks} + placement-aware application to splice
|
|
1704
|
+
* each block into the per-turn message array.
|
|
1223
1705
|
*/
|
|
1224
|
-
export function
|
|
1706
|
+
export async function composeInjectorChain(ctx: TurnContext): Promise<string> {
|
|
1707
|
+
const blocks = await collectInjectorBlocks(ctx);
|
|
1708
|
+
const pieces: string[] = [];
|
|
1709
|
+
for (const block of blocks) {
|
|
1710
|
+
if (block.text.length > 0) pieces.push(block.text);
|
|
1711
|
+
}
|
|
1712
|
+
return pieces.join("\n\n");
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
/**
|
|
1716
|
+
* Default block placement. Kept in sync with {@link InjectionBlock} so
|
|
1717
|
+
* blocks produced without an explicit `placement` (e.g. third-party
|
|
1718
|
+
* injectors written against the pre-G2.1 API) behave predictably.
|
|
1719
|
+
*/
|
|
1720
|
+
const DEFAULT_PLACEMENT: InjectionPlacement = "append-user-tail";
|
|
1721
|
+
|
|
1722
|
+
/**
|
|
1723
|
+
* Count leading memory-prefix blocks on a user message's `content`.
|
|
1724
|
+
*
|
|
1725
|
+
* Delegates to {@link countMemoryPrefixBlocks} from
|
|
1726
|
+
* `memory/graph/conversation-graph-memory.js` — the same state-machine the
|
|
1727
|
+
* pre-migration PKB-reminder branch used to find its splice point. The
|
|
1728
|
+
* pre-migration `injectPkbContext` and `injectNowScratchpad` helpers used
|
|
1729
|
+
* slightly simpler rules inline; reusing the canonical counter here
|
|
1730
|
+
* collapses the three near-identical splice rules into one source of truth
|
|
1731
|
+
* so the ordering of PKB-context / PKB-reminder / NOW blocks relative to
|
|
1732
|
+
* any memory prefix is stable and testable. For the common case (just
|
|
1733
|
+
* `<memory __injected>` text, no images), the output is byte-identical to
|
|
1734
|
+
* the pre-migration helpers.
|
|
1735
|
+
*/
|
|
1736
|
+
function countMemoryPrefixBlocksOnContent(content: ContentBlock[]): number {
|
|
1737
|
+
return countMemoryPrefixBlocks(content);
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
/**
|
|
1741
|
+
* Apply one injector block to a `runMessages` array according to its
|
|
1742
|
+
* declared {@link InjectionPlacement}.
|
|
1743
|
+
*
|
|
1744
|
+
* Preserves the byte-for-byte positional semantics of the pre-migration
|
|
1745
|
+
* `inject*` helpers:
|
|
1746
|
+
* - `"prepend-user-tail"` — prepend to the tail user message's content.
|
|
1747
|
+
* - `"append-user-tail"` — append to the tail user message's content.
|
|
1748
|
+
* - `"after-memory-prefix"` — splice immediately after any leading memory
|
|
1749
|
+
* prefix blocks (mirrors `injectPkbContext` / `injectNowScratchpad`).
|
|
1750
|
+
* - `"replace-run-messages"` — replace `runMessages` wholesale with
|
|
1751
|
+
* `block.messagesOverride`.
|
|
1752
|
+
*
|
|
1753
|
+
* Blocks with empty `text` on non-replace placements are no-ops (the
|
|
1754
|
+
* pre-migration branches also short-circuited on empty strings).
|
|
1755
|
+
*/
|
|
1756
|
+
function applyInjectionBlock(
|
|
1225
1757
|
runMessages: Message[],
|
|
1226
|
-
|
|
1227
|
-
activeSurface?: ActiveSurfaceContext | null;
|
|
1228
|
-
workspaceTopLevelContext?: string | null;
|
|
1229
|
-
channelCapabilities?: ChannelCapabilities | null;
|
|
1230
|
-
channelCommandContext?: ChannelCommandContext | null;
|
|
1231
|
-
unifiedTurnContext?: string | null;
|
|
1232
|
-
voiceCallControlPrompt?: string | null;
|
|
1233
|
-
pkbContext?: string | null;
|
|
1234
|
-
pkbActive?: boolean;
|
|
1235
|
-
nowScratchpad?: string | null;
|
|
1236
|
-
subagentStatusBlock?: string | null;
|
|
1237
|
-
isNonInteractive?: boolean;
|
|
1238
|
-
transportHints?: string[] | null;
|
|
1239
|
-
mode?: InjectionMode;
|
|
1240
|
-
},
|
|
1758
|
+
block: InjectionBlock,
|
|
1241
1759
|
): Message[] {
|
|
1242
|
-
const
|
|
1243
|
-
let result = runMessages;
|
|
1760
|
+
const placement = block.placement ?? DEFAULT_PLACEMENT;
|
|
1244
1761
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1762
|
+
if (placement === "replace-run-messages") {
|
|
1763
|
+
if (!block.messagesOverride) return runMessages;
|
|
1764
|
+
return block.messagesOverride;
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
if (block.text.length === 0) return runMessages;
|
|
1768
|
+
|
|
1769
|
+
const userTail = runMessages[runMessages.length - 1];
|
|
1770
|
+
if (!userTail || userTail.role !== "user") return runMessages;
|
|
1771
|
+
|
|
1772
|
+
const textBlock = { type: "text" as const, text: block.text };
|
|
1773
|
+
|
|
1774
|
+
switch (placement) {
|
|
1775
|
+
case "prepend-user-tail":
|
|
1776
|
+
return [
|
|
1777
|
+
...runMessages.slice(0, -1),
|
|
1778
|
+
{ ...userTail, content: [textBlock, ...userTail.content] },
|
|
1779
|
+
];
|
|
1780
|
+
case "append-user-tail":
|
|
1781
|
+
return [
|
|
1782
|
+
...runMessages.slice(0, -1),
|
|
1783
|
+
{ ...userTail, content: [...userTail.content, textBlock] },
|
|
1784
|
+
];
|
|
1785
|
+
case "after-memory-prefix": {
|
|
1786
|
+
const memoryPrefixCount = countMemoryPrefixBlocksOnContent(
|
|
1787
|
+
userTail.content,
|
|
1788
|
+
);
|
|
1789
|
+
return [
|
|
1790
|
+
...runMessages.slice(0, -1),
|
|
1252
1791
|
{
|
|
1253
1792
|
...userTail,
|
|
1254
1793
|
content: [
|
|
1255
|
-
...userTail.content,
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
text: "<non_interactive_context>\nNon-interactive scheduled task — do not ask for clarification or confirmation. Follow the instructions exactly using your best judgment. If recalled memory contains conflicting notes, prefer the explicit instruction in this message.\n</non_interactive_context>",
|
|
1259
|
-
},
|
|
1794
|
+
...userTail.content.slice(0, memoryPrefixCount),
|
|
1795
|
+
textBlock,
|
|
1796
|
+
...userTail.content.slice(memoryPrefixCount),
|
|
1260
1797
|
],
|
|
1261
1798
|
},
|
|
1262
1799
|
];
|
|
1263
1800
|
}
|
|
1264
1801
|
}
|
|
1802
|
+
}
|
|
1265
1803
|
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1804
|
+
/**
|
|
1805
|
+
* Per-turn options accepted by {@link applyRuntimeInjections}.
|
|
1806
|
+
*
|
|
1807
|
+
* Most fields flow through to the per-injector {@link TurnInjectionInputs}
|
|
1808
|
+
* bag attached to the {@link TurnContext} the caller provides (or to an
|
|
1809
|
+
* ephemeral {@link TurnContext} synthesized for test call sites). A small
|
|
1810
|
+
* number of fields drive hardcoded branches that live outside the injector
|
|
1811
|
+
* chain — `activeSurface`, `channelCapabilities`, `channelCommandContext`,
|
|
1812
|
+
* `voiceCallControlPrompt`, `transportHints`, and `isNonInteractive` —
|
|
1813
|
+
* because they are orchestrator-owned content that never made sense as
|
|
1814
|
+
* plugin-overridable default injectors.
|
|
1815
|
+
*/
|
|
1816
|
+
export interface RuntimeInjectionOptions {
|
|
1817
|
+
/**
|
|
1818
|
+
* Active dashboard-surface context (read from `<active_workspace>`). Kept
|
|
1819
|
+
* on the options bag rather than an injector because it is a
|
|
1820
|
+
* channel-capability concern that has never been gated as a default
|
|
1821
|
+
* injector.
|
|
1822
|
+
*/
|
|
1823
|
+
activeSurface?: ActiveSurfaceContext | null;
|
|
1824
|
+
workspaceTopLevelContext?: string | null;
|
|
1825
|
+
channelCapabilities?: ChannelCapabilities | null;
|
|
1826
|
+
channelCommandContext?: ChannelCommandContext | null;
|
|
1827
|
+
unifiedTurnContext?: string | null;
|
|
1828
|
+
voiceCallControlPrompt?: string | null;
|
|
1829
|
+
pkbContext?: string | null;
|
|
1830
|
+
pkbActive?: boolean;
|
|
1831
|
+
/**
|
|
1832
|
+
* Dense query vector surfaced from the graph memory retriever.
|
|
1833
|
+
* When present together with `pkbActive`, used to run `searchPkbFiles`
|
|
1834
|
+
* to surface relevance hints in the PKB system reminder. When missing,
|
|
1835
|
+
* the reminder falls back to the flat static text.
|
|
1836
|
+
*/
|
|
1837
|
+
pkbQueryVector?: number[];
|
|
1838
|
+
/** Optional sparse vector accompanying `pkbQueryVector`. */
|
|
1839
|
+
pkbSparseVector?: QdrantSparseVector;
|
|
1840
|
+
/** Memory scope id used to filter PKB search results. */
|
|
1841
|
+
pkbScopeId?: string;
|
|
1842
|
+
/**
|
|
1843
|
+
* The live conversation (or a minimal shape containing `messages`) used
|
|
1844
|
+
* to compute which PKB paths are already "in context" and therefore
|
|
1845
|
+
* suppressed from hint suggestions.
|
|
1846
|
+
*/
|
|
1847
|
+
pkbConversation?: PkbContextConversation;
|
|
1848
|
+
/** Auto-injected PKB filenames (resolved relative to `pkbRoot`). */
|
|
1849
|
+
pkbAutoInjectList?: string[];
|
|
1850
|
+
/** Absolute path to the PKB directory (e.g. `<workspace>/pkb`). */
|
|
1851
|
+
pkbRoot?: string;
|
|
1852
|
+
/**
|
|
1853
|
+
* Working directory against which relative `file_read` tool paths
|
|
1854
|
+
* resolve, used to detect workspace-relative reads like
|
|
1855
|
+
* `pkb/threads.md`. Falls back to `pkbRoot` when omitted.
|
|
1856
|
+
*/
|
|
1857
|
+
pkbWorkingDir?: string;
|
|
1858
|
+
nowScratchpad?: string | null;
|
|
1859
|
+
subagentStatusBlock?: string | null;
|
|
1860
|
+
isNonInteractive?: boolean;
|
|
1861
|
+
transportHints?: string[] | null;
|
|
1862
|
+
/**
|
|
1863
|
+
* Pre-rendered Slack chronological transcript that replaces the
|
|
1864
|
+
* default `runMessages` history for any Slack conversation (channels
|
|
1865
|
+
* and DMs alike).
|
|
1866
|
+
*
|
|
1867
|
+
* When `channelCapabilities` describes a Slack conversation and this
|
|
1868
|
+
* array is non-empty, the `slack-messages` default injector emits a
|
|
1869
|
+
* `replace-run-messages` block that swaps `runMessages` with this
|
|
1870
|
+
* transcript. Channel renders include sibling-thread tags; DM renders
|
|
1871
|
+
* are flat (DMs have no threads). The `transportHints` pipeline is
|
|
1872
|
+
* skipped for any Slack conversation so the persisted view isn't
|
|
1873
|
+
* duplicated by gateway-side hints.
|
|
1874
|
+
*
|
|
1875
|
+
* Callers build this via `loadSlackChronologicalMessages` (or the
|
|
1876
|
+
* underlying `assembleSlackChronologicalMessages`) before invoking
|
|
1877
|
+
* this function so the assembly path stays free of direct DB calls
|
|
1878
|
+
* and remains easy to test.
|
|
1879
|
+
*/
|
|
1880
|
+
slackChronologicalMessages?: Message[] | null;
|
|
1881
|
+
/**
|
|
1882
|
+
* Pre-rendered `<active_thread>` focus block listing the messages of
|
|
1883
|
+
* the thread the current inbound user message belongs to.
|
|
1884
|
+
*
|
|
1885
|
+
* Appended to the FINAL user message ONLY when `channelCapabilities`
|
|
1886
|
+
* describes a Slack non-DM channel. The block is non-persisted: history
|
|
1887
|
+
* rebuilds re-derive it from storage on each turn, and
|
|
1888
|
+
* `RUNTIME_INJECTION_PREFIXES` strips any `<active_thread>` blocks from
|
|
1889
|
+
* prior turns so they do not accumulate.
|
|
1890
|
+
*
|
|
1891
|
+
* Callers build this via `loadSlackActiveThreadFocusBlock` (or the
|
|
1892
|
+
* underlying `assembleSlackActiveThreadFocusBlock`). Pass `null` /
|
|
1893
|
+
* `undefined` when the inbound is a top-level (non-thread) post.
|
|
1894
|
+
*/
|
|
1895
|
+
slackActiveThreadFocusBlock?: string | null;
|
|
1896
|
+
mode?: InjectionMode;
|
|
1897
|
+
/**
|
|
1898
|
+
* Per-turn {@link TurnContext} forwarded to plugin-registered
|
|
1899
|
+
* {@link Injector}s via {@link collectInjectorBlocks}. When omitted,
|
|
1900
|
+
* `applyRuntimeInjections` synthesizes an ephemeral context (with a
|
|
1901
|
+
* fallback `trust` classification) so the default-injector chain still
|
|
1902
|
+
* runs — call sites that build the options bag without holding a full
|
|
1903
|
+
* `TurnContext` get the same chain output.
|
|
1904
|
+
*
|
|
1905
|
+
* When provided, the caller's `trust`, `conversationId`, `turnIndex`,
|
|
1906
|
+
* etc. are preserved; the function layers its per-turn
|
|
1907
|
+
* {@link TurnInjectionInputs} onto a shallow clone so the caller's
|
|
1908
|
+
* `TurnContext` is not mutated.
|
|
1909
|
+
*/
|
|
1910
|
+
turnContext?: TurnContext;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
/**
|
|
1914
|
+
* Build the {@link TurnInjectionInputs} bag from the options bag.
|
|
1915
|
+
*
|
|
1916
|
+
* Exposed so callers that already hold a {@link TurnContext} can layer the
|
|
1917
|
+
* same per-turn inputs onto it before handing control to
|
|
1918
|
+
* {@link collectInjectorBlocks} directly — useful for tests and for the
|
|
1919
|
+
* overflow-reducer reinject path.
|
|
1920
|
+
*/
|
|
1921
|
+
export function buildTurnInjectionInputs(
|
|
1922
|
+
options: RuntimeInjectionOptions,
|
|
1923
|
+
): TurnInjectionInputs {
|
|
1924
|
+
return {
|
|
1925
|
+
mode: options.mode,
|
|
1926
|
+
workspaceTopLevelContext: options.workspaceTopLevelContext,
|
|
1927
|
+
unifiedTurnContext: options.unifiedTurnContext,
|
|
1928
|
+
pkbContext: options.pkbContext,
|
|
1929
|
+
pkbActive: options.pkbActive,
|
|
1930
|
+
pkbQueryVector: options.pkbQueryVector,
|
|
1931
|
+
pkbSparseVector: options.pkbSparseVector,
|
|
1932
|
+
pkbScopeId: options.pkbScopeId,
|
|
1933
|
+
pkbConversation: options.pkbConversation,
|
|
1934
|
+
pkbAutoInjectList: options.pkbAutoInjectList,
|
|
1935
|
+
pkbRoot: options.pkbRoot,
|
|
1936
|
+
pkbWorkingDir: options.pkbWorkingDir,
|
|
1937
|
+
nowScratchpad: options.nowScratchpad,
|
|
1938
|
+
subagentStatusBlock: options.subagentStatusBlock,
|
|
1939
|
+
channelCapabilities: options.channelCapabilities,
|
|
1940
|
+
slackChronologicalMessages: options.slackChronologicalMessages,
|
|
1941
|
+
slackActiveThreadFocusBlock: options.slackActiveThreadFocusBlock,
|
|
1942
|
+
activeSurface: options.activeSurface,
|
|
1943
|
+
channelCommandContext: options.channelCommandContext,
|
|
1944
|
+
voiceCallControlPrompt: options.voiceCallControlPrompt,
|
|
1945
|
+
transportHints: options.transportHints,
|
|
1946
|
+
isNonInteractive: options.isNonInteractive,
|
|
1947
|
+
};
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
/** Minimal synthetic TurnContext used when the caller omits one. */
|
|
1951
|
+
function synthesizeFallbackTurnContext(
|
|
1952
|
+
inputs: TurnInjectionInputs,
|
|
1953
|
+
): TurnContext {
|
|
1954
|
+
return {
|
|
1955
|
+
requestId: "runtime-assembly-fallback",
|
|
1956
|
+
conversationId: "runtime-assembly-fallback",
|
|
1957
|
+
turnIndex: 0,
|
|
1958
|
+
trust: {
|
|
1959
|
+
sourceChannel: inputs.channelCapabilities?.channel
|
|
1960
|
+
? (inputs.channelCapabilities.channel as TrustContext["sourceChannel"])
|
|
1961
|
+
: "vellum",
|
|
1962
|
+
trustClass: "unknown",
|
|
1963
|
+
},
|
|
1964
|
+
injectionInputs: inputs,
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
/**
|
|
1969
|
+
* Apply the runtime-injection chain to `runMessages`.
|
|
1970
|
+
*
|
|
1971
|
+
* The canonical per-turn assembly pipeline for every provider call:
|
|
1972
|
+
*
|
|
1973
|
+
* 1. Build the per-turn {@link TurnInjectionInputs} bag from `options`.
|
|
1974
|
+
* 2. Layer it onto a {@link TurnContext} — either the one the caller
|
|
1975
|
+
* supplies via `options.turnContext` (preserving its `requestId`,
|
|
1976
|
+
* trust, and other fields) or an ephemeral fallback synthesized here.
|
|
1977
|
+
* 3. Drive the default + third-party {@link Injector} chain via
|
|
1978
|
+
* {@link collectInjectorBlocks}.
|
|
1979
|
+
* 4. Apply the chain's `"replace-run-messages"` block (Slack chronological
|
|
1980
|
+
* transcript) first so subsequent branches operate on the replaced
|
|
1981
|
+
* tail. When replacement fires, re-prepend any memory-prefix blocks
|
|
1982
|
+
* that `graphMemory.prepareMemory` had attached to the original tail —
|
|
1983
|
+
* the Slack transcript is rendered fresh from persisted rows and
|
|
1984
|
+
* carries no memory prefix of its own.
|
|
1985
|
+
* 5. Apply the chain's `"after-memory-prefix"` blocks in ascending
|
|
1986
|
+
* `order`. This runs BEFORE step 6's hardcoded prepends so the
|
|
1987
|
+
* memory-prefix counter sees only the memory blocks on the tail —
|
|
1988
|
+
* any `<channel_capabilities>` / `<channel_command_context>` /
|
|
1989
|
+
* `<transport_hints>` prepended first would push the count to zero
|
|
1990
|
+
* and force PKB / NOW to splice at the top of the tail. Within the
|
|
1991
|
+
* after-memory block, each successive splice lands at the memory
|
|
1992
|
+
* boundary, pushing earlier splices further from memory — so
|
|
1993
|
+
* higher-`order` blocks end up closer to the memory prefix.
|
|
1994
|
+
* 6. Run the remaining hardcoded branches (`isNonInteractive`,
|
|
1995
|
+
* `voiceCallControlPrompt`, `activeSurface`, `channelCapabilities`,
|
|
1996
|
+
* `channelCommandContext`, `transportHints`) in their historical order.
|
|
1997
|
+
* 7. Finally, apply the chain's remaining blocks by placement:
|
|
1998
|
+
* `"append-user-tail"` in ascending `order`, then `"prepend-user-tail"`
|
|
1999
|
+
* in descending `order` so the lowest-`order` prepend lands topmost in
|
|
2000
|
+
* the user tail content.
|
|
2001
|
+
*
|
|
2002
|
+
* Returns the final message array plus a `blocks` object holding the exact
|
|
2003
|
+
* injected text for each captured block — callers persist those bytes to
|
|
2004
|
+
* message metadata for later byte-exact rehydration.
|
|
2005
|
+
*/
|
|
2006
|
+
export async function applyRuntimeInjections(
|
|
2007
|
+
runMessages: Message[],
|
|
2008
|
+
options: RuntimeInjectionOptions,
|
|
2009
|
+
): Promise<RuntimeInjectionResult> {
|
|
2010
|
+
const mode = options.mode ?? "full";
|
|
2011
|
+
const slackConversation = options.channelCapabilities?.channel === "slack";
|
|
2012
|
+
|
|
2013
|
+
// Build the per-injector inputs and attach them to the caller's
|
|
2014
|
+
// TurnContext (without mutating it). When the caller didn't supply one,
|
|
2015
|
+
// synthesize a minimal fallback so the chain still runs — test call sites
|
|
2016
|
+
// that drive injection via `options` without constructing a full context
|
|
2017
|
+
// continue to work.
|
|
2018
|
+
const injectionInputs = buildTurnInjectionInputs(options);
|
|
2019
|
+
const turnCtx: TurnContext = options.turnContext
|
|
2020
|
+
? { ...options.turnContext, injectionInputs }
|
|
2021
|
+
: synthesizeFallbackTurnContext(injectionInputs);
|
|
2022
|
+
|
|
2023
|
+
const chainBlocks = await collectInjectorBlocks(turnCtx);
|
|
2024
|
+
|
|
2025
|
+
// Split the chain output by placement so the downstream assembly can
|
|
2026
|
+
// process each slot with the correct ordering rule.
|
|
2027
|
+
const prepends: InjectionBlock[] = [];
|
|
2028
|
+
const appends: InjectionBlock[] = [];
|
|
2029
|
+
const afterMemory: InjectionBlock[] = [];
|
|
2030
|
+
let replaceBlock: InjectionBlock | null = null;
|
|
2031
|
+
for (const block of chainBlocks) {
|
|
2032
|
+
switch (block.placement ?? "append-user-tail") {
|
|
2033
|
+
case "replace-run-messages":
|
|
2034
|
+
// Later replace-run-messages blocks would overwrite earlier ones;
|
|
2035
|
+
// the default chain only registers one (the Slack transcript).
|
|
2036
|
+
replaceBlock = block;
|
|
2037
|
+
break;
|
|
2038
|
+
case "after-memory-prefix":
|
|
2039
|
+
afterMemory.push(block);
|
|
2040
|
+
break;
|
|
2041
|
+
case "prepend-user-tail":
|
|
2042
|
+
prepends.push(block);
|
|
2043
|
+
break;
|
|
2044
|
+
case "append-user-tail":
|
|
2045
|
+
appends.push(block);
|
|
2046
|
+
break;
|
|
1273
2047
|
}
|
|
1274
2048
|
}
|
|
1275
2049
|
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
2050
|
+
// Track captured text for metadata persistence. Each field corresponds
|
|
2051
|
+
// to a specific default-injector block id so the loop below can pick up
|
|
2052
|
+
// the right capture without re-rendering.
|
|
2053
|
+
//
|
|
2054
|
+
// The capture is gated on the tail actually being a user message — if it
|
|
2055
|
+
// isn't, `applyInjectionBlock` no-ops the block and no content is actually
|
|
2056
|
+
// injected, so the persisted metadata must be undefined (matches
|
|
2057
|
+
// pre-migration behaviour where the `inject*` helpers short-circuited the
|
|
2058
|
+
// same way).
|
|
2059
|
+
let turnContextCaptured: string | undefined;
|
|
2060
|
+
let workspaceCaptured: string | undefined;
|
|
2061
|
+
let nowScratchpadCaptured: string | undefined;
|
|
2062
|
+
let pkbContextCaptured: string | undefined;
|
|
2063
|
+
let pkbSystemReminderCaptured: string | undefined;
|
|
2064
|
+
const initialTail = runMessages[runMessages.length - 1];
|
|
2065
|
+
const initialTailIsUser = !!initialTail && initialTail.role === "user";
|
|
2066
|
+
if (initialTailIsUser) {
|
|
2067
|
+
for (const block of chainBlocks) {
|
|
2068
|
+
switch (block.id) {
|
|
2069
|
+
case "unified-turn-context":
|
|
2070
|
+
turnContextCaptured = block.text;
|
|
2071
|
+
break;
|
|
2072
|
+
case "workspace-context":
|
|
2073
|
+
workspaceCaptured = block.text;
|
|
2074
|
+
break;
|
|
2075
|
+
case "now-md":
|
|
2076
|
+
nowScratchpadCaptured = block.text;
|
|
2077
|
+
break;
|
|
2078
|
+
case "pkb-context":
|
|
2079
|
+
pkbContextCaptured = block.text;
|
|
2080
|
+
break;
|
|
2081
|
+
case "pkb-reminder":
|
|
2082
|
+
pkbSystemReminderCaptured = block.text;
|
|
2083
|
+
break;
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
// Compose the block text into a single informational string for
|
|
2089
|
+
// `injectorChainBlock`. Matches the pre-migration behaviour where the
|
|
2090
|
+
// field captured the composed view of every third-party injector on
|
|
2091
|
+
// the turn. We include default injectors here too so downstream
|
|
2092
|
+
// observers see the full set.
|
|
2093
|
+
const injectorChainPieces: string[] = [];
|
|
2094
|
+
for (const block of chainBlocks) {
|
|
2095
|
+
if (block.text.length > 0) injectorChainPieces.push(block.text);
|
|
2096
|
+
}
|
|
2097
|
+
const injectorChainBlock =
|
|
2098
|
+
injectorChainPieces.length > 0
|
|
2099
|
+
? injectorChainPieces.join("\n\n")
|
|
2100
|
+
: undefined;
|
|
2101
|
+
|
|
2102
|
+
let result = runMessages;
|
|
2103
|
+
|
|
2104
|
+
// ── Step 1: Slack chronological replacement (chain "replace" block) ──
|
|
2105
|
+
if (replaceBlock && replaceBlock.messagesOverride) {
|
|
2106
|
+
// `graphMemory.prepareMemory` prepends a `<memory __injected>` block
|
|
2107
|
+
// (and any memory-image groups) to the last user message before
|
|
2108
|
+
// runtime assembly runs. The Slack transcript is freshly rendered
|
|
2109
|
+
// from persisted rows and has no such prefix, so swap it in and then
|
|
2110
|
+
// re-prepend the captured prefix onto the new tail user message.
|
|
2111
|
+
const carriedMemoryBlocks = extractMemoryPrefixBlocks(runMessages);
|
|
2112
|
+
result = replaceBlock.messagesOverride;
|
|
2113
|
+
if (carriedMemoryBlocks.length > 0) {
|
|
2114
|
+
const slackTail = result[result.length - 1];
|
|
2115
|
+
if (slackTail && slackTail.role === "user") {
|
|
2116
|
+
result = [
|
|
2117
|
+
...result.slice(0, -1),
|
|
2118
|
+
{
|
|
2119
|
+
...slackTail,
|
|
2120
|
+
content: [...carriedMemoryBlocks, ...slackTail.content],
|
|
2121
|
+
},
|
|
2122
|
+
];
|
|
2123
|
+
}
|
|
1283
2124
|
}
|
|
1284
2125
|
}
|
|
1285
2126
|
|
|
1286
|
-
//
|
|
1287
|
-
//
|
|
1288
|
-
|
|
2127
|
+
// ── Step 2: after-memory-prefix chain blocks ──
|
|
2128
|
+
// These splice relative to the memory-prefix count on the tail content,
|
|
2129
|
+
// so they must run BEFORE the hardcoded prepends in step 3. Otherwise
|
|
2130
|
+
// any prepended `<channel_capabilities>` / `<channel_command_context>` /
|
|
2131
|
+
// `<transport_hints>` (none of which are memory-prefix blocks) would
|
|
2132
|
+
// drop the count to 0 and PKB / NOW would splice at the very top of
|
|
2133
|
+
// the tail instead of immediately after memory.
|
|
2134
|
+
//
|
|
2135
|
+
// Ascending `order`: each splice lands at the memory-prefix boundary,
|
|
2136
|
+
// pushing any previously-spliced block one slot further from memory.
|
|
2137
|
+
// So higher-`order` blocks end up closer to the memory prefix.
|
|
2138
|
+
for (const block of afterMemory) {
|
|
2139
|
+
result = applyInjectionBlock(result, block);
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
// ── Step 3: hardcoded branches that stayed outside the injector chain ──
|
|
2143
|
+
// These run in the same historical order as before G2.1 so their
|
|
2144
|
+
// interleaving with any prior tail content stays stable.
|
|
2145
|
+
|
|
2146
|
+
// For non-interactive conversations (scheduled jobs, work items), instruct the
|
|
2147
|
+
// model to never ask for clarification — there is no human present to answer.
|
|
2148
|
+
if (options.isNonInteractive) {
|
|
1289
2149
|
const userTail = result[result.length - 1];
|
|
1290
2150
|
if (userTail && userTail.role === "user") {
|
|
1291
2151
|
result = [
|
|
@@ -1294,19 +2154,22 @@ export function applyRuntimeInjections(
|
|
|
1294
2154
|
...userTail,
|
|
1295
2155
|
content: [
|
|
1296
2156
|
...userTail.content,
|
|
1297
|
-
{
|
|
2157
|
+
{
|
|
2158
|
+
type: "text" as const,
|
|
2159
|
+
text: "<non_interactive_context>\nNon-interactive scheduled task — do not ask for clarification or confirmation. Follow the instructions exactly using your best judgment. If recalled memory contains conflicting notes, prefer the explicit instruction in this message.\n</non_interactive_context>",
|
|
2160
|
+
},
|
|
1298
2161
|
],
|
|
1299
2162
|
},
|
|
1300
2163
|
];
|
|
1301
2164
|
}
|
|
1302
2165
|
}
|
|
1303
2166
|
|
|
1304
|
-
if (
|
|
2167
|
+
if (options.voiceCallControlPrompt) {
|
|
1305
2168
|
const userTail = result[result.length - 1];
|
|
1306
2169
|
if (userTail && userTail.role === "user") {
|
|
1307
2170
|
result = [
|
|
1308
2171
|
...result.slice(0, -1),
|
|
1309
|
-
|
|
2172
|
+
injectVoiceCallControlContext(userTail, options.voiceCallControlPrompt),
|
|
1310
2173
|
];
|
|
1311
2174
|
}
|
|
1312
2175
|
}
|
|
@@ -1341,34 +2204,15 @@ export function applyRuntimeInjections(
|
|
|
1341
2204
|
}
|
|
1342
2205
|
}
|
|
1343
2206
|
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
];
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
if (options.unifiedTurnContext) {
|
|
1355
|
-
const userTail = result[result.length - 1];
|
|
1356
|
-
if (userTail && userTail.role === "user") {
|
|
1357
|
-
result = [
|
|
1358
|
-
...result.slice(0, -1),
|
|
1359
|
-
{
|
|
1360
|
-
...userTail,
|
|
1361
|
-
content: [
|
|
1362
|
-
{ type: "text" as const, text: options.unifiedTurnContext },
|
|
1363
|
-
...userTail.content,
|
|
1364
|
-
],
|
|
1365
|
-
},
|
|
1366
|
-
];
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
|
|
2207
|
+
// Slack conversations (both channels and DMs) build their own
|
|
2208
|
+
// chronological transcript from persisted messages and intentionally do
|
|
2209
|
+
// not receive the per-turn `<transport_hints>` block — the rendered
|
|
2210
|
+
// history already covers the active thread / DM, so duplicating it
|
|
2211
|
+
// would confuse the model. Other channels (telegram, email, etc.) keep
|
|
2212
|
+
// the existing injection.
|
|
1370
2213
|
if (
|
|
1371
2214
|
mode === "full" &&
|
|
2215
|
+
!slackConversation &&
|
|
1372
2216
|
options.transportHints &&
|
|
1373
2217
|
options.transportHints.length > 0
|
|
1374
2218
|
) {
|
|
@@ -1381,21 +2225,29 @@ export function applyRuntimeInjections(
|
|
|
1381
2225
|
}
|
|
1382
2226
|
}
|
|
1383
2227
|
|
|
1384
|
-
//
|
|
1385
|
-
//
|
|
1386
|
-
//
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
];
|
|
1397
|
-
}
|
|
2228
|
+
// ── Step 4: apply remaining chain blocks by placement ──
|
|
2229
|
+
// append-user-tail: ascending `order` so lower-order blocks come first
|
|
2230
|
+
// in the append sequence.
|
|
2231
|
+
for (const block of appends) {
|
|
2232
|
+
result = applyInjectionBlock(result, block);
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
// prepend-user-tail: descending `order` so the lowest-order block lands
|
|
2236
|
+
// topmost in the tail content (each successive prepend pushes the
|
|
2237
|
+
// previous one further down).
|
|
2238
|
+
for (let i = prepends.length - 1; i >= 0; i--) {
|
|
2239
|
+
result = applyInjectionBlock(result, prepends[i]);
|
|
1398
2240
|
}
|
|
1399
2241
|
|
|
1400
|
-
return
|
|
2242
|
+
return {
|
|
2243
|
+
messages: result,
|
|
2244
|
+
blocks: {
|
|
2245
|
+
unifiedTurnContext: turnContextCaptured,
|
|
2246
|
+
pkbSystemReminder: pkbSystemReminderCaptured,
|
|
2247
|
+
workspaceBlock: workspaceCaptured,
|
|
2248
|
+
nowScratchpadBlock: nowScratchpadCaptured,
|
|
2249
|
+
pkbContextBlock: pkbContextCaptured,
|
|
2250
|
+
injectorChainBlock,
|
|
2251
|
+
},
|
|
2252
|
+
};
|
|
1401
2253
|
}
|