@vellumai/assistant 0.8.2 → 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +11 -12
- package/docker-entrypoint.sh +13 -2
- package/docker-init-apt-root.sh +79 -6
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +945 -36
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +112 -57
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +77 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/external-plugin-loader.test.ts +91 -19
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/host-app-control-proxy.test.ts +241 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +7 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +3 -0
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
- package/src/__tests__/llm-resolver.test.ts +340 -3
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/managed-profile-guard.test.ts +10 -0
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/openai-provider.test.ts +323 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform-proxy-context.test.ts +6 -1
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
- package/src/__tests__/plugin-types.test.ts +2 -2
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +57 -101
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +208 -22
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +14 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +469 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +89 -37
- package/src/cli/commands/plugins.ts +67 -0
- package/src/cli/commands/schedules.ts +297 -5
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/install-from-github.ts +8 -9
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/program.ts +14 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +124 -0
- package/src/config/feature-flag-registry.json +111 -23
- package/src/config/llm-resolver.ts +66 -1
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/channels.ts +9 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +14 -0
- package/src/config/schemas/llm.ts +4 -3
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +51 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/context/compactor.ts +80 -13
- package/src/context/token-estimator.ts +72 -31
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
- package/src/daemon/conversation-agent-loop.ts +252 -56
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +144 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -12
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +449 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +80 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-app-control-proxy.ts +69 -18
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +67 -65
- package/src/daemon/memory-v2-startup.ts +49 -13
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/pkb-reminder-builder.test.ts +10 -53
- package/src/daemon/pkb-reminder-builder.ts +4 -19
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +72 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/skill-memory-refresh.ts +5 -1
- package/src/daemon/trust-context.ts +6 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
- package/src/heartbeat/heartbeat-service.ts +35 -191
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +20 -3
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/ipc/cli-client.ts +147 -45
- package/src/memory/__tests__/conversation-queries.test.ts +220 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/conversation-queries.ts +87 -1
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +22 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
- package/src/memory/graph/conversation-graph-memory.ts +18 -6
- package/src/memory/graph/tools.ts +6 -37
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +4 -22
- package/src/memory/memory-retrospective-job.ts +438 -21
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +20 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +2 -0
- package/src/memory/schema/infrastructure.ts +2 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +221 -17
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +69 -29
- package/src/memory/v2/page-index.ts +246 -19
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +18 -3
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +5 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +111 -44
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +10 -1
- package/src/notifications/home-feed-side-effect.ts +136 -27
- package/src/notifications/signal.ts +0 -4
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +13 -4
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +118 -26
- package/src/plugins/external-plugin-loader.ts +82 -10
- package/src/plugins/types.ts +16 -7
- package/src/prompts/__tests__/system-prompt.test.ts +44 -45
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -76
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +51 -10
- package/src/providers/__tests__/inference.test.ts +2 -0
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +63 -13
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +24 -21
- package/src/providers/inference/auth.ts +15 -3
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/connections.ts +85 -5
- package/src/providers/inference/resolve-auth.ts +50 -5
- package/src/providers/model-catalog.ts +244 -242
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +215 -25
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +46 -4
- package/src/providers/platform-proxy/constants.ts +3 -4
- package/src/providers/provider-catalog-visibility.ts +3 -1
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +30 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +212 -57
- package/src/runtime/auth/route-policy.ts +20 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +7 -51
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/channel-availability-routes.ts +5 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +130 -12
- package/src/runtime/routes/conversation-routes.ts +288 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +14 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
- package/src/runtime/routes/integrations/a2a.ts +294 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/routes/subagents-routes.ts +41 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/subagent/manager.ts +2 -0
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/registry.ts +2 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +41 -2
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -9,6 +9,7 @@ import { existsSync, readFileSync, statSync } from "node:fs";
|
|
|
9
9
|
import { join, resolve } from "node:path";
|
|
10
10
|
|
|
11
11
|
import { type ChannelId, parseInterfaceId } from "../channels/types.js";
|
|
12
|
+
import { getConfig } from "../config/loader.js";
|
|
12
13
|
import { createContextSummaryMessage } from "../context/window-manager.js";
|
|
13
14
|
import { getAppDirPath, listAppFiles } from "../memory/app-store.js";
|
|
14
15
|
import {
|
|
@@ -20,15 +21,16 @@ import {
|
|
|
20
21
|
extractMemoryPrefixBlocks,
|
|
21
22
|
} from "../memory/graph/conversation-graph-memory.js";
|
|
22
23
|
import type { QdrantSparseVector } from "../memory/qdrant-client.js";
|
|
23
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
readSlackMetadata,
|
|
26
|
+
readSlackMetadataFromMessageMetadata,
|
|
27
|
+
} from "../messaging/providers/slack/message-metadata.js";
|
|
24
28
|
import {
|
|
25
29
|
compareSlackTs,
|
|
26
30
|
extractTagLineTexts,
|
|
27
|
-
isReactionTagLine,
|
|
28
31
|
isSlackTsAfter,
|
|
29
32
|
type RenderableSlackMessage,
|
|
30
33
|
type RenderedSlackTranscriptMessage,
|
|
31
|
-
renderSlackTranscript,
|
|
32
34
|
renderSlackTranscriptWithProvenance,
|
|
33
35
|
} from "../messaging/providers/slack/render-transcript.js";
|
|
34
36
|
import { getInjectors } from "../plugins/registry.js";
|
|
@@ -48,6 +50,7 @@ import {
|
|
|
48
50
|
import { channelStatusToMemberStatus } from "../runtime/routes/inbound-stages/acl-enforcement.js";
|
|
49
51
|
import type { SubagentState } from "../subagent/types.js";
|
|
50
52
|
import { TERMINAL_STATUSES } from "../subagent/types.js";
|
|
53
|
+
import { canonicalizeInboundIdentity } from "../util/canonicalize-identity.js";
|
|
51
54
|
import { getWorkspaceDir, getWorkspacePromptPath } from "../util/platform.js";
|
|
52
55
|
import { stripCommentLines } from "../util/strip-comment-lines.js";
|
|
53
56
|
import { filterMessagesForUntrustedActor } from "./conversation-lifecycle.js";
|
|
@@ -680,18 +683,19 @@ export function injectChannelCapabilityContext(
|
|
|
680
683
|
"- Do NOT reference the dashboard UI, settings panels, or visual preference pickers.",
|
|
681
684
|
);
|
|
682
685
|
if (!caps.supportsDynamicUi) {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
+
if (caps.channel === "slack") {
|
|
687
|
+
lines.push(
|
|
688
|
+
'- Do NOT use app_create. Only use ui_show/ui_update for card surfaces with template: "task_progress"; present all other information as text.',
|
|
689
|
+
);
|
|
690
|
+
} else {
|
|
691
|
+
lines.push(
|
|
692
|
+
"- Do NOT use ui_show, ui_update, or app_create — this channel cannot render them.",
|
|
693
|
+
);
|
|
694
|
+
}
|
|
686
695
|
lines.push(
|
|
687
696
|
"- Present information as well-formatted text instead of dynamic UI.",
|
|
688
697
|
);
|
|
689
698
|
}
|
|
690
|
-
lines.push(
|
|
691
|
-
"- Defer dashboard-specific actions (e.g. accent color selection) by telling the user",
|
|
692
|
-
);
|
|
693
|
-
lines.push(" they can complete those steps later from the desktop app.");
|
|
694
|
-
|
|
695
699
|
if (caps.channel === "whatsapp") {
|
|
696
700
|
lines.push(
|
|
697
701
|
"- Do NOT use markdown tables — use bullet lists instead. No markdown headers — use **bold** or CAPS for emphasis.",
|
|
@@ -699,10 +703,6 @@ export function injectChannelCapabilityContext(
|
|
|
699
703
|
}
|
|
700
704
|
}
|
|
701
705
|
|
|
702
|
-
if (!caps.supportsVoiceInput) {
|
|
703
|
-
lines.push("- Do NOT ask the user to use voice or microphone input.");
|
|
704
|
-
}
|
|
705
|
-
|
|
706
706
|
// Inject group chat etiquette only when the chat type indicates a multi-party
|
|
707
707
|
// conversation, avoiding misconditioned "stay silent" guidance in 1:1 DMs.
|
|
708
708
|
if (isGroupChatType(caps.chatType)) {
|
|
@@ -798,6 +798,13 @@ export interface UnifiedTurnContextOptions {
|
|
|
798
798
|
* the model can acknowledge long absences; otherwise omitted.
|
|
799
799
|
*/
|
|
800
800
|
timeSinceLastMessage?: string | null;
|
|
801
|
+
/**
|
|
802
|
+
* Human-readable model profile description. Only populated when the active
|
|
803
|
+
* inference profile changed since the last turn (or on the first turn of a
|
|
804
|
+
* conversation) so the model knows which profile/model it is using without
|
|
805
|
+
* paying per-turn token cost.
|
|
806
|
+
*/
|
|
807
|
+
modelProfile?: string | null;
|
|
801
808
|
}
|
|
802
809
|
|
|
803
810
|
/**
|
|
@@ -856,6 +863,9 @@ export function buildUnifiedTurnContextBlock(
|
|
|
856
863
|
if (options.timeSinceLastMessage) {
|
|
857
864
|
lines.push(`time_since_last_message: ${options.timeSinceLastMessage}`);
|
|
858
865
|
}
|
|
866
|
+
if (options.modelProfile) {
|
|
867
|
+
lines.push(`model_profile: ${options.modelProfile}`);
|
|
868
|
+
}
|
|
859
869
|
if (options.interfaceName) {
|
|
860
870
|
lines.push(`interface: ${options.interfaceName}`);
|
|
861
871
|
}
|
|
@@ -973,6 +983,9 @@ export function buildUnifiedTurnContextBlock(
|
|
|
973
983
|
lines.push(
|
|
974
984
|
`response_discretion: Not every message in a channel thread requires your response. If a message is clearly not directed at you (e.g. people talking among themselves, acknowledgements, reactions), output exactly <no_response/> as your entire reply to stay silent.`,
|
|
975
985
|
);
|
|
986
|
+
if (options.channelName === "slack") {
|
|
987
|
+
lines.push("if you are going to do work, use task_progress");
|
|
988
|
+
}
|
|
976
989
|
}
|
|
977
990
|
|
|
978
991
|
lines.push("</turn_context>");
|
|
@@ -1032,17 +1045,6 @@ function injectTransportHints(message: Message, hints: string[]): Message {
|
|
|
1032
1045
|
};
|
|
1033
1046
|
}
|
|
1034
1047
|
|
|
1035
|
-
function injectSlackRuntimeContextNotice(
|
|
1036
|
-
message: Message,
|
|
1037
|
-
notice: string,
|
|
1038
|
-
): Message {
|
|
1039
|
-
const block = `<slack_context_notice>\n${notice}\n</slack_context_notice>`;
|
|
1040
|
-
return {
|
|
1041
|
-
...message,
|
|
1042
|
-
content: [{ type: "text", text: block }, ...message.content],
|
|
1043
|
-
};
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
1048
|
// ---------------------------------------------------------------------------
|
|
1047
1049
|
// Slack chronological transcript assembly
|
|
1048
1050
|
// ---------------------------------------------------------------------------
|
|
@@ -1111,6 +1113,27 @@ function messageRowsToSlackTranscriptRows(
|
|
|
1111
1113
|
}));
|
|
1112
1114
|
}
|
|
1113
1115
|
|
|
1116
|
+
function hasSlackMetadata(row: MessageRow): boolean {
|
|
1117
|
+
return (
|
|
1118
|
+
readSlackMetadataFromMessageMetadata(row.metadata, {
|
|
1119
|
+
allowFlatLegacy: true,
|
|
1120
|
+
}) !== null
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
function filterSlackConversationRowsForActor(
|
|
1125
|
+
rows: MessageRow[],
|
|
1126
|
+
trustClass: TrustClass | undefined,
|
|
1127
|
+
): MessageRow[] {
|
|
1128
|
+
if (!isUntrustedTrustClass(trustClass)) return rows;
|
|
1129
|
+
const nonSlackVisibleRows = filterMessagesForUntrustedActor(rows);
|
|
1130
|
+
const nonSlackVisibleIds = new Set(nonSlackVisibleRows.map((row) => row.id));
|
|
1131
|
+
return rows.filter((row) => {
|
|
1132
|
+
if (hasSlackMetadata(row)) return true;
|
|
1133
|
+
return nonSlackVisibleIds.has(row.id);
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1114
1137
|
/**
|
|
1115
1138
|
* Extract the user-facing plain text from an already-parsed `ContentBlock[]`.
|
|
1116
1139
|
* Only `text` blocks contribute to the rendered transcript line. Tool-use /
|
|
@@ -1268,6 +1291,60 @@ function rowToRenderable(row: SlackTranscriptInputRow): RenderableSlackMessage {
|
|
|
1268
1291
|
};
|
|
1269
1292
|
}
|
|
1270
1293
|
|
|
1294
|
+
const SLACK_ASSISTANT_THREAD_PLACEHOLDER_TEXT = "New Assistant Thread";
|
|
1295
|
+
|
|
1296
|
+
function isSlackAssistantThreadPlaceholder(
|
|
1297
|
+
message: RenderableSlackMessage,
|
|
1298
|
+
canonicalConfiguredBotUserId: string | null,
|
|
1299
|
+
): boolean {
|
|
1300
|
+
if (!canonicalConfiguredBotUserId) return false;
|
|
1301
|
+
const metadata = message.metadata;
|
|
1302
|
+
if (!metadata || metadata.eventKind !== "message") return false;
|
|
1303
|
+
const actorExternalUserId = metadata.actorExternalUserId?.trim();
|
|
1304
|
+
if (!actorExternalUserId) return false;
|
|
1305
|
+
|
|
1306
|
+
const canonicalActor =
|
|
1307
|
+
canonicalizeInboundIdentity("slack", actorExternalUserId) ??
|
|
1308
|
+
actorExternalUserId;
|
|
1309
|
+
const isThreadRoot =
|
|
1310
|
+
metadata.threadTs === undefined || metadata.threadTs === metadata.channelTs;
|
|
1311
|
+
const hasSlackFiles =
|
|
1312
|
+
Array.isArray(metadata.slackFiles) && metadata.slackFiles.length > 0;
|
|
1313
|
+
|
|
1314
|
+
return (
|
|
1315
|
+
message.role === "user" &&
|
|
1316
|
+
canonicalActor === canonicalConfiguredBotUserId &&
|
|
1317
|
+
isThreadRoot &&
|
|
1318
|
+
!hasSlackFiles &&
|
|
1319
|
+
message.content.replace(/\s+/g, " ").trim() ===
|
|
1320
|
+
SLACK_ASSISTANT_THREAD_PLACEHOLDER_TEXT
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
function getCanonicalConfiguredSlackBotUserId(): string | null {
|
|
1325
|
+
const configuredBotUserId = getConfig().slack.botUserId.trim();
|
|
1326
|
+
if (!configuredBotUserId) return null;
|
|
1327
|
+
return (
|
|
1328
|
+
canonicalizeInboundIdentity("slack", configuredBotUserId) ??
|
|
1329
|
+
configuredBotUserId
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
function rowsToRenderableSlackMessages(
|
|
1334
|
+
rows: SlackTranscriptInputRow[],
|
|
1335
|
+
): RenderableSlackMessage[] {
|
|
1336
|
+
const canonicalConfiguredBotUserId = getCanonicalConfiguredSlackBotUserId();
|
|
1337
|
+
return rows
|
|
1338
|
+
.map(rowToRenderable)
|
|
1339
|
+
.filter(
|
|
1340
|
+
(message) =>
|
|
1341
|
+
!isSlackAssistantThreadPlaceholder(
|
|
1342
|
+
message,
|
|
1343
|
+
canonicalConfiguredBotUserId,
|
|
1344
|
+
),
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1271
1348
|
/**
|
|
1272
1349
|
* Compatibility projection for callers that still need the legacy
|
|
1273
1350
|
* `Message[] | null` shape. New runtime callers should use
|
|
@@ -1363,7 +1440,7 @@ function assembleSlackChronologicalContext(
|
|
|
1363
1440
|
if (capabilities.channel !== "slack") {
|
|
1364
1441
|
return null;
|
|
1365
1442
|
}
|
|
1366
|
-
const renderable = rows
|
|
1443
|
+
const renderable = rowsToRenderableSlackMessages(rows);
|
|
1367
1444
|
const rendered = renderSlackTranscriptWithProvenance(renderable);
|
|
1368
1445
|
const contextSummary = options.contextSummary?.trim();
|
|
1369
1446
|
const renderedMessages = rendered.renderedMessages;
|
|
@@ -1372,6 +1449,7 @@ function assembleSlackChronologicalContext(
|
|
|
1372
1449
|
{
|
|
1373
1450
|
message: createContextSummaryMessage(contextSummary),
|
|
1374
1451
|
sourceChannelTs: null,
|
|
1452
|
+
tagLineProvenance: "none",
|
|
1375
1453
|
},
|
|
1376
1454
|
...renderedMessages,
|
|
1377
1455
|
];
|
|
@@ -1392,11 +1470,10 @@ function assembleSlackChronologicalContext(
|
|
|
1392
1470
|
* Compatibility wrapper over `loadSlackChronologicalContext` for callers that
|
|
1393
1471
|
* still need only the legacy `Message[] | null` projection.
|
|
1394
1472
|
*
|
|
1395
|
-
* When `trustClass` identifies an untrusted actor
|
|
1396
|
-
*
|
|
1397
|
-
*
|
|
1398
|
-
*
|
|
1399
|
-
* respects the same per-actor scoping as the default history path.
|
|
1473
|
+
* When `trustClass` identifies an untrusted actor, non-Slack/private rows
|
|
1474
|
+
* are passed through the default trust filter. Slack-tagged rows stay visible
|
|
1475
|
+
* because the transcript is scoped to the external Slack chat/thread, which
|
|
1476
|
+
* the inbound actor can already read in Slack.
|
|
1400
1477
|
*
|
|
1401
1478
|
* Returns `null` when the channel is not Slack — callers should fall
|
|
1402
1479
|
* through to the default in-memory message history.
|
|
@@ -1445,9 +1522,10 @@ export function loadSlackChronologicalContext(
|
|
|
1445
1522
|
}
|
|
1446
1523
|
const loader = options.loader ?? defaultGetMessages;
|
|
1447
1524
|
const allRows = loader(conversationId);
|
|
1448
|
-
const scopedRows =
|
|
1449
|
-
|
|
1450
|
-
|
|
1525
|
+
const scopedRows = filterSlackConversationRowsForActor(
|
|
1526
|
+
allRows,
|
|
1527
|
+
options.trustClass,
|
|
1528
|
+
);
|
|
1451
1529
|
const rows = filterRowsAfterSlackCompactionBoundary(
|
|
1452
1530
|
messageRowsToSlackTranscriptRows(scopedRows),
|
|
1453
1531
|
options,
|
|
@@ -1555,29 +1633,29 @@ function buildActiveThreadBlockFromRenderable(
|
|
|
1555
1633
|
if (members.length === 0) return null;
|
|
1556
1634
|
|
|
1557
1635
|
// The active-thread block is flattened to plain text below, which discards
|
|
1558
|
-
// `Message.role`. Assistant rows are relabeled in
|
|
1559
|
-
//
|
|
1560
|
-
//
|
|
1561
|
-
//
|
|
1562
|
-
//
|
|
1563
|
-
//
|
|
1564
|
-
// renderer. Labeled user rows and assistant rows pass through unchanged.
|
|
1636
|
+
// `Message.role`. Assistant rows that render content-only are relabeled in
|
|
1637
|
+
// the post-render step. Timezone-aware assistant rows are already
|
|
1638
|
+
// bracket-tagged by the renderer and must not receive another prefix.
|
|
1639
|
+
// Unnamed user rows (no real Slack displayName) get a `@user` senderLabel
|
|
1640
|
+
// here so their tag line carries attribution through the renderer. Labeled
|
|
1641
|
+
// user rows and assistant rows pass through unchanged.
|
|
1565
1642
|
const labeledMembers = members.map((m) => {
|
|
1566
1643
|
if (m.role === "assistant") return m;
|
|
1567
1644
|
if (m.senderLabel !== null) return m;
|
|
1568
1645
|
return { ...m, senderLabel: "@user" };
|
|
1569
1646
|
});
|
|
1570
1647
|
|
|
1571
|
-
const rendered =
|
|
1572
|
-
if (rendered.length === 0) return null;
|
|
1573
|
-
// Reaction / overflow-trailer lines
|
|
1574
|
-
//
|
|
1575
|
-
//
|
|
1576
|
-
//
|
|
1577
|
-
const lines = rendered
|
|
1578
|
-
.map((
|
|
1579
|
-
const text = extractTagLineTexts([
|
|
1580
|
-
return
|
|
1648
|
+
const rendered = renderSlackTranscriptWithProvenance(labeledMembers);
|
|
1649
|
+
if (rendered.renderedMessages.length === 0) return null;
|
|
1650
|
+
// Reaction / overflow-trailer lines are renderer-owned Slack event lines,
|
|
1651
|
+
// and timezone-aware assistant rows already carry metadata-backed compact
|
|
1652
|
+
// attribution. Regular assistant content and the `[deleted]` sentinel get
|
|
1653
|
+
// the prefix so attribution survives flattening.
|
|
1654
|
+
const lines = rendered.renderedMessages
|
|
1655
|
+
.map((entry) => {
|
|
1656
|
+
const text = extractTagLineTexts([entry.message])[0] ?? "";
|
|
1657
|
+
return entry.message.role === "assistant" &&
|
|
1658
|
+
entry.tagLineProvenance === "none"
|
|
1581
1659
|
? `@assistant: ${text}`
|
|
1582
1660
|
: text;
|
|
1583
1661
|
})
|
|
@@ -1605,7 +1683,7 @@ export function assembleSlackActiveThreadFocusBlock(
|
|
|
1605
1683
|
// conversation and omits the field for DMs, so gate the focus block
|
|
1606
1684
|
// on the positive `"channel"` match.
|
|
1607
1685
|
if (capabilities.chatType !== "channel") return null;
|
|
1608
|
-
const renderable = rows
|
|
1686
|
+
const renderable = rowsToRenderableSlackMessages(rows);
|
|
1609
1687
|
const activeThreadTs = detectActiveThreadTs(renderable);
|
|
1610
1688
|
if (!activeThreadTs) return null;
|
|
1611
1689
|
return buildActiveThreadBlockFromRenderable(renderable, activeThreadTs);
|
|
@@ -1631,9 +1709,10 @@ export function loadSlackActiveThreadFocusBlock(
|
|
|
1631
1709
|
if (capabilities.chatType !== "channel") return null;
|
|
1632
1710
|
const loader = options.loader ?? defaultGetMessages;
|
|
1633
1711
|
const allRows = loader(conversationId);
|
|
1634
|
-
const scopedRows =
|
|
1635
|
-
|
|
1636
|
-
|
|
1712
|
+
const scopedRows = filterSlackConversationRowsForActor(
|
|
1713
|
+
allRows,
|
|
1714
|
+
options.trustClass,
|
|
1715
|
+
);
|
|
1637
1716
|
const rows = filterRowsAfterSlackCompactionBoundary(
|
|
1638
1717
|
messageRowsToSlackTranscriptRows(scopedRows),
|
|
1639
1718
|
options,
|
|
@@ -1652,6 +1731,7 @@ const RUNTIME_INJECTION_PREFIXES = [
|
|
|
1652
1731
|
"<interface_turn_context>", // backward-compat: strip legacy separate interface blocks
|
|
1653
1732
|
// NOTE: <turn_context> is intentionally NOT stripped — unified turn context
|
|
1654
1733
|
// blocks persist in history so the assistant retains temporal/actor grounding.
|
|
1734
|
+
"<background_turn>",
|
|
1655
1735
|
"<memory_context __injected>",
|
|
1656
1736
|
"<memory_context>", // backward-compat: strip legacy blocks from pre-__injected history
|
|
1657
1737
|
// The static `memory-v2-static` block (opens `<memory>\n…`) IS stripped
|
|
@@ -1680,7 +1760,6 @@ const RUNTIME_INJECTION_PREFIXES = [
|
|
|
1680
1760
|
"<pkb>", // backward-compat: strip legacy tag from pre-rename history
|
|
1681
1761
|
"<system_reminder>",
|
|
1682
1762
|
"<transport_hints>",
|
|
1683
|
-
"<slack_context_notice>",
|
|
1684
1763
|
// The Slack active-thread focus block is non-persisted and injected on
|
|
1685
1764
|
// the FINAL user turn only. Strip it here so re-assembly during compaction
|
|
1686
1765
|
// and overflow recovery does not duplicate it across turns.
|
|
@@ -1966,8 +2045,14 @@ export interface RuntimeInjectionOptions {
|
|
|
1966
2045
|
nowScratchpad?: string | null;
|
|
1967
2046
|
subagentStatusBlock?: string | null;
|
|
1968
2047
|
isNonInteractive?: boolean;
|
|
2048
|
+
/**
|
|
2049
|
+
* True when the active conversation's type is "background" or "scheduled".
|
|
2050
|
+
* Forwarded to {@link TurnInjectionInputs.isBackgroundConversation} so the
|
|
2051
|
+
* `background-turn` injector can wrap the tail user message with the
|
|
2052
|
+
* configured reminder.
|
|
2053
|
+
*/
|
|
2054
|
+
isBackgroundConversation?: boolean;
|
|
1969
2055
|
transportHints?: string[] | null;
|
|
1970
|
-
slackRuntimeContextNotice?: string | null;
|
|
1971
2056
|
/**
|
|
1972
2057
|
* Pre-rendered Slack chronological transcript that replaces the
|
|
1973
2058
|
* default `runMessages` history for any Slack conversation (channels
|
|
@@ -2056,6 +2141,7 @@ function buildTurnInjectionInputs(
|
|
|
2056
2141
|
voiceCallControlPrompt: options.voiceCallControlPrompt,
|
|
2057
2142
|
transportHints: options.transportHints,
|
|
2058
2143
|
isNonInteractive: options.isNonInteractive,
|
|
2144
|
+
isBackgroundConversation: options.isBackgroundConversation,
|
|
2059
2145
|
activeDocuments: options.activeDocuments,
|
|
2060
2146
|
};
|
|
2061
2147
|
}
|
|
@@ -2307,23 +2393,6 @@ export async function applyRuntimeInjections(
|
|
|
2307
2393
|
}
|
|
2308
2394
|
}
|
|
2309
2395
|
|
|
2310
|
-
if (
|
|
2311
|
-
mode === "full" &&
|
|
2312
|
-
slackConversation &&
|
|
2313
|
-
options.slackRuntimeContextNotice
|
|
2314
|
-
) {
|
|
2315
|
-
const userTail = result[result.length - 1];
|
|
2316
|
-
if (userTail && userTail.role === "user") {
|
|
2317
|
-
result = [
|
|
2318
|
-
...result.slice(0, -1),
|
|
2319
|
-
injectSlackRuntimeContextNotice(
|
|
2320
|
-
userTail,
|
|
2321
|
-
options.slackRuntimeContextNotice,
|
|
2322
|
-
),
|
|
2323
|
-
];
|
|
2324
|
-
}
|
|
2325
|
-
}
|
|
2326
|
-
|
|
2327
2396
|
if (mode === "full" && options.channelCommandContext) {
|
|
2328
2397
|
const userTail = result[result.length - 1];
|
|
2329
2398
|
if (userTail && userTail.role === "user") {
|
|
@@ -14,7 +14,8 @@ import { getVisibleProviderCatalog } from "../providers/provider-catalog-visibil
|
|
|
14
14
|
export type SlashResolution =
|
|
15
15
|
| { kind: "passthrough"; content: string }
|
|
16
16
|
| { kind: "unknown"; message: string }
|
|
17
|
-
| { kind: "compact"; targetInputTokensOverride?: number }
|
|
17
|
+
| { kind: "compact"; targetInputTokensOverride?: number }
|
|
18
|
+
| { kind: "clean" };
|
|
18
19
|
|
|
19
20
|
const COMPACT_USAGE_HINT =
|
|
20
21
|
"Usage: `/compact [<tokens>]` (e.g. `/compact 30000`, `/compact 30k`, `/compact 1m`).";
|
|
@@ -52,6 +53,23 @@ function parseCompactCommand(trimmed: string): CompactParse | null {
|
|
|
52
53
|
return { kind: "compact", targetInputTokensOverride: tokens };
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
type CleanParse = { kind: "clean" } | { kind: "unknown"; message: string };
|
|
57
|
+
|
|
58
|
+
const CLEAN_COMMAND_PATTERN = /^\/clean(?:\s+(.+?))?\s*$/i;
|
|
59
|
+
|
|
60
|
+
function parseCleanCommand(trimmed: string): CleanParse | null {
|
|
61
|
+
const match = trimmed.match(CLEAN_COMMAND_PATTERN);
|
|
62
|
+
if (!match) return null;
|
|
63
|
+
const rest = match[1]?.trim();
|
|
64
|
+
if (rest) {
|
|
65
|
+
return {
|
|
66
|
+
kind: "unknown",
|
|
67
|
+
message: `\`/clean\` does not take arguments. Usage: \`/clean\`.`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return { kind: "clean" };
|
|
71
|
+
}
|
|
72
|
+
|
|
55
73
|
// ── /context and /status commands ────────────────────────────────────
|
|
56
74
|
|
|
57
75
|
export interface SlashContext {
|
|
@@ -302,10 +320,14 @@ function resolveStatusCommand(context: SlashContext): SlashResolution {
|
|
|
302
320
|
return { kind: "unknown", message: lines.join("\n") };
|
|
303
321
|
}
|
|
304
322
|
|
|
323
|
+
const CLEAN_HELP_LINE =
|
|
324
|
+
"/clean — Strip injected runtime context and reset memory injection state (no summarization)";
|
|
325
|
+
|
|
305
326
|
function resolveCommandsList(context?: SlashContext): string[] {
|
|
306
327
|
const fallbackLines = [
|
|
307
328
|
"/commands — List all available commands",
|
|
308
329
|
"/compact — Force context compaction immediately",
|
|
330
|
+
CLEAN_HELP_LINE,
|
|
309
331
|
];
|
|
310
332
|
if (context) {
|
|
311
333
|
fallbackLines.push("/context — Show conversation context usage");
|
|
@@ -322,6 +344,7 @@ function resolveCommandsList(context?: SlashContext): string[] {
|
|
|
322
344
|
return [
|
|
323
345
|
"/commands — List all available commands",
|
|
324
346
|
"/compact — Force context compaction immediately",
|
|
347
|
+
CLEAN_HELP_LINE,
|
|
325
348
|
"/context — Show conversation context usage",
|
|
326
349
|
"/model — List or switch inference profile",
|
|
327
350
|
"/models — List all available models",
|
|
@@ -335,6 +358,7 @@ function resolveCommandsList(context?: SlashContext): string[] {
|
|
|
335
358
|
return [
|
|
336
359
|
"/commands — List all available commands",
|
|
337
360
|
"/compact — Force context compaction immediately",
|
|
361
|
+
CLEAN_HELP_LINE,
|
|
338
362
|
"/context — Show conversation context usage",
|
|
339
363
|
"/model — List or switch inference profile",
|
|
340
364
|
"/models — List all available models",
|
|
@@ -347,6 +371,7 @@ function resolveCommandsList(context?: SlashContext): string[] {
|
|
|
347
371
|
return [
|
|
348
372
|
"/commands — List all available commands",
|
|
349
373
|
"/compact — Force context compaction immediately",
|
|
374
|
+
CLEAN_HELP_LINE,
|
|
350
375
|
"/context — Show conversation context usage",
|
|
351
376
|
"/model — List or switch inference profile",
|
|
352
377
|
"/models — List all available models",
|
|
@@ -366,7 +391,7 @@ function resolveCommandsList(context?: SlashContext): string[] {
|
|
|
366
391
|
*/
|
|
367
392
|
export function classifySlash(
|
|
368
393
|
content: string,
|
|
369
|
-
): "passthrough" | "compact" | "unknown" {
|
|
394
|
+
): "passthrough" | "compact" | "clean" | "unknown" {
|
|
370
395
|
const trimmed = content.trim();
|
|
371
396
|
if (parseModelCommand(trimmed) != null) {
|
|
372
397
|
return "unknown";
|
|
@@ -381,6 +406,8 @@ export function classifySlash(
|
|
|
381
406
|
if (trimmed === "/models") return "unknown";
|
|
382
407
|
const compactParse = parseCompactCommand(trimmed);
|
|
383
408
|
if (compactParse) return compactParse.kind;
|
|
409
|
+
const cleanParse = parseCleanCommand(trimmed);
|
|
410
|
+
if (cleanParse) return cleanParse.kind;
|
|
384
411
|
if (trimmed === "/context") return "unknown";
|
|
385
412
|
if (trimmed === "/status") return "unknown";
|
|
386
413
|
if (trimmed === "/commands") return "unknown";
|
|
@@ -388,9 +415,10 @@ export function classifySlash(
|
|
|
388
415
|
}
|
|
389
416
|
|
|
390
417
|
/**
|
|
391
|
-
* Resolve built-in slash commands (/models, /context, /status, /commands,
|
|
392
|
-
* Returns `unknown` with a deterministic message,
|
|
393
|
-
*
|
|
418
|
+
* Resolve built-in slash commands (/models, /context, /status, /commands,
|
|
419
|
+
* /compact, /clean). Returns `unknown` with a deterministic message,
|
|
420
|
+
* `compact` for forced compaction, `clean` for injection stripping, or the
|
|
421
|
+
* (possibly rewritten) content as `passthrough`.
|
|
394
422
|
*/
|
|
395
423
|
export async function resolveSlash(
|
|
396
424
|
content: string,
|
|
@@ -424,6 +452,10 @@ export async function resolveSlash(
|
|
|
424
452
|
const compactParse = parseCompactCommand(trimmed);
|
|
425
453
|
if (compactParse) return compactParse;
|
|
426
454
|
|
|
455
|
+
// Handle /clean command (strip injections, no summarization).
|
|
456
|
+
const cleanParse = parseCleanCommand(trimmed);
|
|
457
|
+
if (cleanParse) return cleanParse;
|
|
458
|
+
|
|
427
459
|
// Handle /context and legacy /status commands
|
|
428
460
|
if (trimmed === "/context" || trimmed === "/status") {
|
|
429
461
|
if (!context) {
|
|
@@ -390,6 +390,40 @@ function normalizeTaskProgressCardPatch(
|
|
|
390
390
|
return normalizedPatch;
|
|
391
391
|
}
|
|
392
392
|
|
|
393
|
+
function isTaskProgressCardData(data: SurfaceData | Record<string, unknown>) {
|
|
394
|
+
return (data as Record<string, unknown>).template === "task_progress";
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function isSlackTaskProgressUiException(
|
|
398
|
+
ctx: SurfaceConversationContext,
|
|
399
|
+
toolName: string,
|
|
400
|
+
input: Record<string, unknown>,
|
|
401
|
+
): boolean {
|
|
402
|
+
if (ctx.channelCapabilities?.channel !== "slack") return false;
|
|
403
|
+
if (toolName === "ui_show") {
|
|
404
|
+
const surfaceType = input.surface_type as SurfaceType;
|
|
405
|
+
if (surfaceType !== "card") return false;
|
|
406
|
+
const rawData = isPlainObject(input.data) ? input.data : {};
|
|
407
|
+
const data = normalizeCardShowData(input, rawData);
|
|
408
|
+
return isTaskProgressCardData(data);
|
|
409
|
+
}
|
|
410
|
+
if (toolName === "ui_update") {
|
|
411
|
+
const surfaceId = input.surface_id;
|
|
412
|
+
if (typeof surfaceId !== "string") return false;
|
|
413
|
+
const stored = ctx.surfaceState.get(surfaceId);
|
|
414
|
+
if (!stored || stored.surfaceType !== "card") return false;
|
|
415
|
+
if (!isTaskProgressCardData(stored.data)) return false;
|
|
416
|
+
const rawPatch = isPlainObject(input.data) ? input.data : {};
|
|
417
|
+
const patch = normalizeTaskProgressCardPatch(
|
|
418
|
+
stored.data as CardSurfaceData,
|
|
419
|
+
rawPatch,
|
|
420
|
+
);
|
|
421
|
+
const mergedData = { ...stored.data, ...patch } as SurfaceData;
|
|
422
|
+
return isTaskProgressCardData(mergedData);
|
|
423
|
+
}
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
|
|
393
427
|
/**
|
|
394
428
|
* Subset of Conversation state that surface helpers need access to.
|
|
395
429
|
* The Conversation class implements this interface so its instances can be
|
|
@@ -1638,7 +1672,12 @@ export async function handleSurfaceAction(
|
|
|
1638
1672
|
// One-shot interactive surfaces — auto-complete now that the message has
|
|
1639
1673
|
// been accepted. Deferred until after rejection check so the surface stays
|
|
1640
1674
|
// active and retryable if the queue was full.
|
|
1641
|
-
const ONE_SHOT_SURFACE_TYPES = [
|
|
1675
|
+
const ONE_SHOT_SURFACE_TYPES = [
|
|
1676
|
+
"form",
|
|
1677
|
+
"confirmation",
|
|
1678
|
+
"file_upload",
|
|
1679
|
+
"task_preferences",
|
|
1680
|
+
];
|
|
1642
1681
|
if (ONE_SHOT_SURFACE_TYPES.includes(pending.surfaceType)) {
|
|
1643
1682
|
broadcastMessage({
|
|
1644
1683
|
type: "ui_surface_complete",
|
|
@@ -2135,7 +2174,11 @@ export async function surfaceProxyResolver(
|
|
|
2135
2174
|
|
|
2136
2175
|
if (toolName === "ui_show" || toolName === "ui_update") {
|
|
2137
2176
|
const caps = ctx.channelCapabilities;
|
|
2138
|
-
if (
|
|
2177
|
+
if (
|
|
2178
|
+
caps &&
|
|
2179
|
+
!caps.supportsDynamicUi &&
|
|
2180
|
+
!isSlackTaskProgressUiException(ctx, toolName, input)
|
|
2181
|
+
) {
|
|
2139
2182
|
log.info(
|
|
2140
2183
|
{ toolName, channel: caps.channel, conversationId: ctx.conversationId },
|
|
2141
2184
|
"Blocked UI surface tool on channel without dynamic UI support",
|
|
@@ -331,6 +331,7 @@ export interface SkillProjectionContext {
|
|
|
331
331
|
// ── Conditional tool sets ────────────────────────────────────────────
|
|
332
332
|
|
|
333
333
|
const UI_SURFACE_TOOL_NAMES = new Set(["ui_show", "ui_update", "ui_dismiss"]);
|
|
334
|
+
const SLACK_TASK_PROGRESS_UI_TOOL_NAMES = new Set(["ui_show", "ui_update"]);
|
|
334
335
|
/**
|
|
335
336
|
* Single source of truth for which tools are host tools and the capability
|
|
336
337
|
* each one requires from the connected client interface. Adding a tool here
|
|
@@ -437,6 +438,12 @@ export function isToolActiveForContext(
|
|
|
437
438
|
return false;
|
|
438
439
|
}
|
|
439
440
|
if (UI_SURFACE_TOOL_NAMES.has(name)) {
|
|
441
|
+
if (
|
|
442
|
+
ctx.channelCapabilities?.channel === "slack" &&
|
|
443
|
+
SLACK_TASK_PROGRESS_UI_TOOL_NAMES.has(name)
|
|
444
|
+
) {
|
|
445
|
+
return !ctx.hasNoClient;
|
|
446
|
+
}
|
|
440
447
|
return ctx.channelCapabilities?.supportsDynamicUi ?? !ctx.hasNoClient;
|
|
441
448
|
}
|
|
442
449
|
if (HOST_TOOL_NAMES.has(name)) {
|