comisai 1.0.36 → 1.0.37

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.
Files changed (239) hide show
  1. package/node_modules/@comis/agent/dist/background/auto-background-middleware.js +9 -0
  2. package/node_modules/@comis/agent/dist/background/background-task-manager.d.ts +22 -2
  3. package/node_modules/@comis/agent/dist/background/background-task-manager.js +48 -41
  4. package/node_modules/@comis/agent/dist/background/background-task-persistence.js +28 -5
  5. package/node_modules/@comis/agent/dist/background/background-task-types.d.ts +49 -0
  6. package/node_modules/@comis/agent/dist/background/completion-dispatcher.d.ts +130 -0
  7. package/node_modules/@comis/agent/dist/background/completion-dispatcher.js +215 -0
  8. package/node_modules/@comis/agent/dist/background/completion-runner.d.ts +10 -1
  9. package/node_modules/@comis/agent/dist/background/completion-runner.js +98 -15
  10. package/node_modules/@comis/agent/dist/background/index.d.ts +6 -1
  11. package/node_modules/@comis/agent/dist/background/index.js +2 -0
  12. package/node_modules/@comis/agent/dist/background/session-resolver.d.ts +85 -0
  13. package/node_modules/@comis/agent/dist/background/session-resolver.js +78 -0
  14. package/node_modules/@comis/agent/dist/bootstrap/sections/messaging-sections.js +1 -0
  15. package/node_modules/@comis/agent/dist/bootstrap/sections/tool-descriptions.js +3 -3
  16. package/node_modules/@comis/agent/dist/bootstrap/sections/tooling-sections.d.ts +30 -2
  17. package/node_modules/@comis/agent/dist/bootstrap/sections/tooling-sections.js +51 -2
  18. package/node_modules/@comis/agent/dist/bootstrap/system-prompt-assembler.d.ts +22 -0
  19. package/node_modules/@comis/agent/dist/bootstrap/system-prompt-assembler.js +2 -2
  20. package/node_modules/@comis/agent/dist/bridge/bridge-event-handlers.d.ts +1 -5
  21. package/node_modules/@comis/agent/dist/bridge/bridge-event-handlers.js +2 -14
  22. package/node_modules/@comis/agent/dist/bridge/bridge-metrics.d.ts +26 -0
  23. package/node_modules/@comis/agent/dist/bridge/bridge-metrics.js +3 -0
  24. package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.d.ts +9 -0
  25. package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.js +73 -2
  26. package/node_modules/@comis/agent/dist/context-engine/signature-surrogate-guard.d.ts +10 -10
  27. package/node_modules/@comis/agent/dist/context-engine/signature-surrogate-guard.js +14 -14
  28. package/node_modules/@comis/agent/dist/context-engine/thinking-block-cleaner.d.ts +11 -13
  29. package/node_modules/@comis/agent/dist/context-engine/thinking-block-cleaner.js +14 -15
  30. package/node_modules/@comis/agent/dist/executor/capability-index-context.d.ts +72 -0
  31. package/node_modules/@comis/agent/dist/executor/capability-index-context.js +329 -0
  32. package/node_modules/@comis/agent/dist/executor/drain-helper.d.ts +122 -0
  33. package/node_modules/@comis/agent/dist/executor/drain-helper.js +173 -0
  34. package/node_modules/@comis/agent/dist/executor/error-classifier.js +2 -2
  35. package/node_modules/@comis/agent/dist/executor/executor-post-execution.d.ts +48 -4
  36. package/node_modules/@comis/agent/dist/executor/executor-post-execution.js +134 -31
  37. package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.d.ts +7 -0
  38. package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.js +25 -4
  39. package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.d.ts +18 -1
  40. package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.js +19 -16
  41. package/node_modules/@comis/agent/dist/executor/jit-guide-injector.d.ts +11 -2
  42. package/node_modules/@comis/agent/dist/executor/jit-guide-injector.js +16 -2
  43. package/node_modules/@comis/agent/dist/executor/pi-executor.d.ts +8 -2
  44. package/node_modules/@comis/agent/dist/executor/pi-executor.js +25 -12
  45. package/node_modules/@comis/agent/dist/executor/prompt-assembly.d.ts +9 -1
  46. package/node_modules/@comis/agent/dist/executor/prompt-assembly.js +15 -1
  47. package/node_modules/@comis/agent/dist/executor/tool-deferral.d.ts +18 -27
  48. package/node_modules/@comis/agent/dist/executor/tool-deferral.js +29 -38
  49. package/node_modules/@comis/agent/dist/model/model-registry-adapter.js +1 -1
  50. package/node_modules/@comis/agent/dist/model/model-scanner.js +1 -1
  51. package/node_modules/@comis/agent/dist/safety/tool-retry-breaker.d.ts +11 -1
  52. package/node_modules/@comis/agent/dist/safety/tool-retry-breaker.js +19 -22
  53. package/node_modules/@comis/agent/dist/session/comis-session-manager.d.ts +16 -2
  54. package/node_modules/@comis/agent/dist/spawn/pi-mono-adapters.d.ts +1 -1
  55. package/node_modules/@comis/agent/dist/spawn/pi-mono-adapters.js +5 -5
  56. package/node_modules/@comis/agent/dist/workspace/data-env.d.ts +38 -0
  57. package/node_modules/@comis/agent/dist/workspace/data-env.js +56 -0
  58. package/node_modules/@comis/agent/dist/workspace/index.d.ts +1 -0
  59. package/node_modules/@comis/agent/dist/workspace/index.js +1 -0
  60. package/node_modules/@comis/agent/dist/workspace/templates.js +5 -1
  61. package/node_modules/@comis/agent/package.json +1 -1
  62. package/node_modules/@comis/channels/dist/index.d.ts +1 -1
  63. package/node_modules/@comis/channels/dist/index.js +1 -1
  64. package/node_modules/@comis/channels/dist/shared/channel-manager.d.ts +9 -3
  65. package/node_modules/@comis/channels/dist/shared/inbound-gate.d.ts +1 -1
  66. package/node_modules/@comis/channels/dist/shared/inbound-gate.js +22 -7
  67. package/node_modules/@comis/channels/dist/shared/inbound-pipeline.d.ts +10 -3
  68. package/node_modules/@comis/channels/dist/shared/inbound-route.d.ts +1 -1
  69. package/node_modules/@comis/channels/dist/shared/inbound-route.js +13 -2
  70. package/node_modules/@comis/channels/dist/shared/response-filter.d.ts +11 -24
  71. package/node_modules/@comis/channels/dist/shared/response-filter.js +25 -53
  72. package/node_modules/@comis/channels/package.json +1 -1
  73. package/node_modules/@comis/cli/dist/commands/providers.d.ts +1 -2
  74. package/node_modules/@comis/cli/dist/commands/providers.js +5 -6
  75. package/node_modules/@comis/cli/package.json +1 -1
  76. package/node_modules/@comis/core/dist/config/field-metadata.js +2 -0
  77. package/node_modules/@comis/core/dist/config/immutable-keys.js +4 -1
  78. package/node_modules/@comis/core/dist/config/index.d.ts +4 -0
  79. package/node_modules/@comis/core/dist/config/index.js +2 -0
  80. package/node_modules/@comis/core/dist/config/schema-agent.d.ts +0 -792
  81. package/node_modules/@comis/core/dist/config/schema-approvals.d.ts +0 -14
  82. package/node_modules/@comis/core/dist/config/schema-auto-reply-engine.d.ts +0 -6
  83. package/node_modules/@comis/core/dist/config/schema-background-tasks.d.ts +0 -12
  84. package/node_modules/@comis/core/dist/config/schema-browser.d.ts +0 -18
  85. package/node_modules/@comis/core/dist/config/schema-channel.d.ts +0 -158
  86. package/node_modules/@comis/core/dist/config/schema-coalescer.d.ts +0 -5
  87. package/node_modules/@comis/core/dist/config/schema-daemon.d.ts +0 -32
  88. package/node_modules/@comis/core/dist/config/schema-delivery.d.ts +0 -18
  89. package/node_modules/@comis/core/dist/config/schema-documentation.d.ts +0 -12
  90. package/node_modules/@comis/core/dist/config/schema-embedding.d.ts +0 -20
  91. package/node_modules/@comis/core/dist/config/schema-envelope.d.ts +0 -15
  92. package/node_modules/@comis/core/dist/config/schema-gateway.d.ts +0 -37
  93. package/node_modules/@comis/core/dist/config/schema-gemini-cache.d.ts +0 -2
  94. package/node_modules/@comis/core/dist/config/schema-integrations.d.ts +0 -318
  95. package/node_modules/@comis/core/dist/config/schema-lifecycle-reactions.d.ts +0 -18
  96. package/node_modules/@comis/core/dist/config/schema-memory-review.d.ts +0 -7
  97. package/node_modules/@comis/core/dist/config/schema-memory.d.ts +0 -16
  98. package/node_modules/@comis/core/dist/config/schema-messages.d.ts +0 -8
  99. package/node_modules/@comis/core/dist/config/schema-models.d.ts +0 -15
  100. package/node_modules/@comis/core/dist/config/schema-notification.d.ts +0 -5
  101. package/node_modules/@comis/core/dist/config/schema-oauth.d.ts +0 -5
  102. package/node_modules/@comis/core/dist/config/schema-observability.d.ts +0 -38
  103. package/node_modules/@comis/core/dist/config/schema-output-retention.d.ts +34 -0
  104. package/node_modules/@comis/core/dist/config/schema-output-retention.js +48 -0
  105. package/node_modules/@comis/core/dist/config/schema-plugins.d.ts +0 -8
  106. package/node_modules/@comis/core/dist/config/schema-providers.d.ts +0 -64
  107. package/node_modules/@comis/core/dist/config/schema-queue.d.ts +0 -58
  108. package/node_modules/@comis/core/dist/config/schema-response-prefix.d.ts +0 -2
  109. package/node_modules/@comis/core/dist/config/schema-retry.d.ts +0 -6
  110. package/node_modules/@comis/core/dist/config/schema-scheduler.d.ts +0 -39
  111. package/node_modules/@comis/core/dist/config/schema-secrets.d.ts +0 -3
  112. package/node_modules/@comis/core/dist/config/schema-security.d.ts +0 -18
  113. package/node_modules/@comis/core/dist/config/schema-send-policy.d.ts +0 -13
  114. package/node_modules/@comis/core/dist/config/schema-sender-trust-display.d.ts +0 -5
  115. package/node_modules/@comis/core/dist/config/schema-serializer.js +2 -0
  116. package/node_modules/@comis/core/dist/config/schema-skills.d.ts +0 -61
  117. package/node_modules/@comis/core/dist/config/schema-streaming.d.ts +0 -38
  118. package/node_modules/@comis/core/dist/config/schema-telegram-file-guard.d.ts +0 -3
  119. package/node_modules/@comis/core/dist/config/schema-tooling.d.ts +87 -0
  120. package/node_modules/@comis/core/dist/config/schema-tooling.js +152 -0
  121. package/node_modules/@comis/core/dist/config/schema-verbosity.d.ts +0 -12
  122. package/node_modules/@comis/core/dist/config/schema-webhooks.d.ts +0 -40
  123. package/node_modules/@comis/core/dist/config/schema.d.ts +41 -38
  124. package/node_modules/@comis/core/dist/config/schema.js +6 -0
  125. package/node_modules/@comis/core/dist/context/context.d.ts +0 -4
  126. package/node_modules/@comis/core/dist/domain/approval-request.d.ts +0 -17
  127. package/node_modules/@comis/core/dist/domain/background-task-origin.d.ts +0 -10
  128. package/node_modules/@comis/core/dist/domain/delivery-origin.d.ts +0 -5
  129. package/node_modules/@comis/core/dist/domain/execution-graph.d.ts +0 -48
  130. package/node_modules/@comis/core/dist/domain/memory-entry.d.ts +0 -3
  131. package/node_modules/@comis/core/dist/domain/model-compat.d.ts +0 -4
  132. package/node_modules/@comis/core/dist/domain/normalized-message.d.ts +0 -15
  133. package/node_modules/@comis/core/dist/domain/provider-capabilities.d.ts +0 -6
  134. package/node_modules/@comis/core/dist/domain/rich-message.d.ts +0 -14
  135. package/node_modules/@comis/core/dist/domain/subagent-context-config.d.ts +0 -22
  136. package/node_modules/@comis/core/dist/domain/subagent-context-types.d.ts +0 -8
  137. package/node_modules/@comis/core/dist/event-bus/events-agent.d.ts +31 -0
  138. package/node_modules/@comis/core/dist/event-bus/events-infra.d.ts +5 -0
  139. package/node_modules/@comis/core/dist/exports/config.d.ts +2 -2
  140. package/node_modules/@comis/core/dist/exports/config.js +3 -1
  141. package/node_modules/@comis/core/dist/exports/hooks.d.ts +1 -1
  142. package/node_modules/@comis/core/dist/exports/ports.d.ts +2 -2
  143. package/node_modules/@comis/core/dist/exports/ports.js +1 -1
  144. package/node_modules/@comis/core/dist/ports/channel-plugin.d.ts +0 -13
  145. package/node_modules/@comis/core/dist/ports/index.d.ts +2 -0
  146. package/node_modules/@comis/core/dist/ports/index.js +4 -0
  147. package/node_modules/@comis/core/dist/ports/no-op-tool-capability.d.ts +30 -0
  148. package/node_modules/@comis/core/dist/ports/no-op-tool-capability.js +47 -0
  149. package/node_modules/@comis/core/dist/ports/tool-capability.d.ts +165 -0
  150. package/node_modules/@comis/core/dist/ports/tool-capability.js +15 -0
  151. package/node_modules/@comis/core/dist/security/audit.d.ts +0 -11
  152. package/node_modules/@comis/core/dist/tool-metadata.d.ts +21 -1
  153. package/node_modules/@comis/core/dist/tool-metadata.js +1 -1
  154. package/node_modules/@comis/core/package.json +1 -1
  155. package/node_modules/@comis/daemon/bundled-skills/skill-creator/scripts/validate-skill.py +1 -1
  156. package/node_modules/@comis/daemon/dist/daemon.js +89 -14
  157. package/node_modules/@comis/daemon/dist/rpc/agent-inline-workspace.d.ts +1 -1
  158. package/node_modules/@comis/daemon/dist/rpc/agent-inline-workspace.js +1 -1
  159. package/node_modules/@comis/daemon/dist/rpc/builtin-provider-guard.js +2 -2
  160. package/node_modules/@comis/daemon/dist/rpc/credential-resolver.js +1 -1
  161. package/node_modules/@comis/daemon/dist/rpc/model-handlers.d.ts +1 -1
  162. package/node_modules/@comis/daemon/dist/rpc/model-handlers.js +2 -2
  163. package/node_modules/@comis/daemon/dist/sub-agent-runner.d.ts +18 -0
  164. package/node_modules/@comis/daemon/dist/sub-agent-runner.js +41 -9
  165. package/node_modules/@comis/daemon/dist/wiring/index.d.ts +2 -0
  166. package/node_modules/@comis/daemon/dist/wiring/index.js +1 -0
  167. package/node_modules/@comis/daemon/dist/wiring/setup-agents.d.ts +36 -2
  168. package/node_modules/@comis/daemon/dist/wiring/setup-agents.js +45 -8
  169. package/node_modules/@comis/daemon/dist/wiring/setup-background-completion-runner.d.ts +28 -9
  170. package/node_modules/@comis/daemon/dist/wiring/setup-background-completion-runner.js +36 -9
  171. package/node_modules/@comis/daemon/dist/wiring/setup-background-tasks.js +2 -2
  172. package/node_modules/@comis/daemon/dist/wiring/setup-channels.d.ts +9 -2
  173. package/node_modules/@comis/daemon/dist/wiring/setup-channels.js +15 -9
  174. package/node_modules/@comis/daemon/dist/wiring/setup-cross-session.d.ts +20 -5
  175. package/node_modules/@comis/daemon/dist/wiring/setup-cross-session.js +20 -15
  176. package/node_modules/@comis/daemon/dist/wiring/setup-delivery.js +14 -2
  177. package/node_modules/@comis/daemon/dist/wiring/setup-gateway.d.ts +4 -6
  178. package/node_modules/@comis/daemon/dist/wiring/setup-gateway.js +3 -5
  179. package/node_modules/@comis/daemon/dist/wiring/setup-heartbeat.d.ts +20 -5
  180. package/node_modules/@comis/daemon/dist/wiring/setup-heartbeat.js +11 -2
  181. package/node_modules/@comis/daemon/dist/wiring/setup-output-retention.d.ts +89 -0
  182. package/node_modules/@comis/daemon/dist/wiring/setup-output-retention.js +212 -0
  183. package/node_modules/@comis/daemon/dist/wiring/setup-tools.d.ts +18 -4
  184. package/node_modules/@comis/daemon/dist/wiring/setup-tools.js +29 -10
  185. package/node_modules/@comis/daemon/dist/wiring/tool-capability-adapter.d.ts +75 -0
  186. package/node_modules/@comis/daemon/dist/wiring/tool-capability-adapter.js +253 -0
  187. package/node_modules/@comis/daemon/package.json +1 -1
  188. package/node_modules/@comis/gateway/dist/webhook/webhook-endpoint.d.ts +0 -4
  189. package/node_modules/@comis/gateway/package.json +1 -1
  190. package/node_modules/@comis/infra/package.json +1 -1
  191. package/node_modules/@comis/memory/package.json +1 -1
  192. package/node_modules/@comis/scheduler/dist/cron/cron-types.d.ts +0 -42
  193. package/node_modules/@comis/scheduler/dist/heartbeat/agent-heartbeat-source.d.ts +29 -8
  194. package/node_modules/@comis/scheduler/dist/heartbeat/agent-heartbeat-source.js +19 -7
  195. package/node_modules/@comis/scheduler/dist/system-events/system-event-types.d.ts +0 -3
  196. package/node_modules/@comis/scheduler/dist/tasks/task-types.d.ts +0 -17
  197. package/node_modules/@comis/scheduler/package.json +1 -1
  198. package/node_modules/@comis/shared/dist/index.d.ts +3 -0
  199. package/node_modules/@comis/shared/dist/index.js +4 -0
  200. package/node_modules/@comis/shared/dist/mcp-tool-name.d.ts +78 -0
  201. package/node_modules/@comis/shared/dist/mcp-tool-name.js +92 -0
  202. package/node_modules/@comis/shared/dist/silent-tokens.d.ts +38 -0
  203. package/node_modules/@comis/shared/dist/silent-tokens.js +51 -0
  204. package/node_modules/@comis/shared/dist/visible-delivery.d.ts +28 -0
  205. package/node_modules/@comis/shared/dist/visible-delivery.js +16 -0
  206. package/node_modules/@comis/shared/package.json +1 -1
  207. package/node_modules/@comis/skills/dist/bridge/mcp-tool-bridge.d.ts +2 -13
  208. package/node_modules/@comis/skills/dist/bridge/mcp-tool-bridge.js +3 -21
  209. package/node_modules/@comis/skills/dist/bridge/tool-metadata-enforcement.js +1 -1
  210. package/node_modules/@comis/skills/dist/bridge/tool-metadata-registry.js +4 -4
  211. package/node_modules/@comis/skills/dist/builtin/exec-tool.d.ts +55 -9
  212. package/node_modules/@comis/skills/dist/builtin/exec-tool.js +383 -19
  213. package/node_modules/@comis/skills/dist/builtin/install-detour.d.ts +67 -0
  214. package/node_modules/@comis/skills/dist/builtin/install-detour.js +342 -0
  215. package/node_modules/@comis/skills/dist/builtin/platform/admin-manage-factory.js +5 -5
  216. package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.d.ts +2 -2
  217. package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.js +2 -2
  218. package/node_modules/@comis/skills/dist/builtin/platform/message-tool.js +18 -0
  219. package/node_modules/@comis/skills/dist/builtin/platform/messaging-factory.d.ts +18 -1
  220. package/node_modules/@comis/skills/dist/builtin/platform/messaging-factory.js +18 -2
  221. package/node_modules/@comis/skills/dist/builtin/platform/models-manage-tool.js +3 -3
  222. package/node_modules/@comis/skills/dist/builtin/process-registry.d.ts +14 -0
  223. package/node_modules/@comis/skills/dist/builtin/process-tool.d.ts +24 -4
  224. package/node_modules/@comis/skills/dist/builtin/process-tool.js +25 -7
  225. package/node_modules/@comis/skills/dist/builtin/sandbox/bwrap-provider.d.ts +1 -1
  226. package/node_modules/@comis/skills/dist/builtin/sandbox/bwrap-provider.js +9 -0
  227. package/node_modules/@comis/skills/dist/index.d.ts +4 -1
  228. package/node_modules/@comis/skills/dist/index.js +3 -1
  229. package/node_modules/@comis/skills/dist/manifest/capability-parser.d.ts +44 -0
  230. package/node_modules/@comis/skills/dist/manifest/capability-parser.js +68 -0
  231. package/node_modules/@comis/skills/dist/manifest/schema.d.ts +44 -37
  232. package/node_modules/@comis/skills/dist/manifest/schema.js +35 -0
  233. package/node_modules/@comis/skills/dist/registry/discovery.d.ts +8 -0
  234. package/node_modules/@comis/skills/dist/registry/discovery.js +10 -3
  235. package/node_modules/@comis/skills/dist/registry/skill-registry.d.ts +45 -1
  236. package/node_modules/@comis/skills/dist/registry/skill-registry.js +70 -7
  237. package/node_modules/@comis/skills/package.json +1 -1
  238. package/node_modules/@comis/web/package.json +1 -1
  239. package/package.json +21 -21
@@ -4,8 +4,36 @@
4
4
  */
5
5
  /** Model size tier — determines prompt verbosity for tool descriptions. */
6
6
  export type ModelTier = "small" | "medium" | "large";
7
- export declare function buildToolingSection(toolNames: string[], _modelTier: ModelTier, toolSummaries?: Record<string, string>): string[];
8
- export declare function buildToolCallStyleSection(isMinimal: boolean, toolNames?: string[]): string[];
7
+ /**
8
+ * Build the static `## Available Tools` block (legacy path, gate-off) OR a
9
+ * single residual one-liner pointing the model at the per-turn `## Capabilities`
10
+ * block (gate-on path).
11
+ *
12
+ * @param capabilityIndexEnabled - When `true`, emits ONLY the residual
13
+ * one-liner. When `false` or undefined, emits the legacy flat block
14
+ * BYTE-IDENTICALLY to the pre-feature baseline. The two paths are MUTUALLY
15
+ * EXCLUSIVE.
16
+ *
17
+ * Restart-required: this gate selects between two cached system-prompt
18
+ * shapes; toggling at runtime is forbidden. Operator-facing constraint is
19
+ * documented in config docs.
20
+ */
21
+ export declare function buildToolingSection(toolNames: string[], _modelTier: ModelTier, toolSummaries?: Record<string, string>, capabilityIndexEnabled?: boolean): string[];
22
+ /**
23
+ * Build the static "## Tool Call Style" section with conditional coding guidelines.
24
+ *
25
+ * @param capabilityIndexEnabled - When `true`, AND when `exec` is in
26
+ * `toolNames`, the rendered output includes the dual-gated "Tool-first
27
+ * principle" bullet immediately before the existing Python-virtualenv rule.
28
+ * When `false` or `undefined`, the bullet is omitted; the existing venv
29
+ * rule emits unchanged when `exec` is present.
30
+ *
31
+ * Restart-required: the gate value flows from `tooling.capabilityIndex.enabled`
32
+ * via `AssemblerParams.capabilityIndexEnabled`, populated at the prompt
33
+ * assembly site from `port.isCapabilityIndexEnabled()`. The value is
34
+ * config-derived and stable per session — SAFE inside the cache fence.
35
+ */
36
+ export declare function buildToolCallStyleSection(isMinimal: boolean, toolNames?: string[], capabilityIndexEnabled?: boolean): string[];
9
37
  /** Tool names that trigger the confirmation flow / self-update gating section. */
10
38
  export declare const CONFIRMATION_TOOL_NAMES: string[];
11
39
  /**
@@ -5,9 +5,36 @@
5
5
  */
6
6
  import { TOOL_SUMMARIES, TOOL_ORDER } from "./tool-descriptions.js";
7
7
  import { getProviders } from "@mariozechner/pi-ai";
8
- export function buildToolingSection(toolNames, _modelTier, toolSummaries) {
8
+ /**
9
+ * Build the static `## Available Tools` block (legacy path, gate-off) OR a
10
+ * single residual one-liner pointing the model at the per-turn `## Capabilities`
11
+ * block (gate-on path).
12
+ *
13
+ * @param capabilityIndexEnabled - When `true`, emits ONLY the residual
14
+ * one-liner. When `false` or undefined, emits the legacy flat block
15
+ * BYTE-IDENTICALLY to the pre-feature baseline. The two paths are MUTUALLY
16
+ * EXCLUSIVE.
17
+ *
18
+ * Restart-required: this gate selects between two cached system-prompt
19
+ * shapes; toggling at runtime is forbidden. Operator-facing constraint is
20
+ * documented in config docs.
21
+ */
22
+ export function buildToolingSection(toolNames, _modelTier, toolSummaries, capabilityIndexEnabled) {
9
23
  if (toolNames.length === 0)
10
24
  return [];
25
+ // Gate-on path: residual one-liner only. The per-turn `## Capabilities`
26
+ // block is rendered into the dynamic preamble by `executor-prompt-runner.ts`.
27
+ // The wording below is normative.
28
+ if (capabilityIndexEnabled === true) {
29
+ return [
30
+ "When this turn includes a `Capabilities` context, refer to it for grouped tool guidance " +
31
+ "before invoking tools or running installs. Tool schemas in your active toolspace are " +
32
+ "authoritative for parameter shapes.",
33
+ ];
34
+ }
35
+ // Gate-off path: legacy flat block BYTE-IDENTICAL to pre-feature baseline.
36
+ // DO NOT modify the body below — the byte-identity assertion in
37
+ // tooling-sections.test.ts depends on this shape staying exact.
11
38
  const summaries = { ...TOOL_SUMMARIES, ...toolSummaries };
12
39
  const ordered = TOOL_ORDER.filter((t) => toolNames.includes(t));
13
40
  const extras = toolNames.filter((t) => !TOOL_ORDER.includes(t)).sort();
@@ -23,7 +50,21 @@ export function buildToolingSection(toolNames, _modelTier, toolSummaries) {
23
50
  // ---------------------------------------------------------------------------
24
51
  // 4. Tool Call Style (skip if minimal)
25
52
  // ---------------------------------------------------------------------------
26
- export function buildToolCallStyleSection(isMinimal, toolNames = []) {
53
+ /**
54
+ * Build the static "## Tool Call Style" section with conditional coding guidelines.
55
+ *
56
+ * @param capabilityIndexEnabled - When `true`, AND when `exec` is in
57
+ * `toolNames`, the rendered output includes the dual-gated "Tool-first
58
+ * principle" bullet immediately before the existing Python-virtualenv rule.
59
+ * When `false` or `undefined`, the bullet is omitted; the existing venv
60
+ * rule emits unchanged when `exec` is present.
61
+ *
62
+ * Restart-required: the gate value flows from `tooling.capabilityIndex.enabled`
63
+ * via `AssemblerParams.capabilityIndexEnabled`, populated at the prompt
64
+ * assembly site from `port.isCapabilityIndexEnabled()`. The value is
65
+ * config-derived and stable per session — SAFE inside the cache fence.
66
+ */
67
+ export function buildToolCallStyleSection(isMinimal, toolNames = [], capabilityIndexEnabled) {
27
68
  if (isMinimal)
28
69
  return [];
29
70
  const lines = [
@@ -64,6 +105,14 @@ export function buildToolCallStyleSection(isMinimal, toolNames = []) {
64
105
  guidelines.push("- Show file paths clearly when working with files.");
65
106
  }
66
107
  if (has("exec")) {
108
+ // Dual-gated counterweight to the venv rule below. The bullet precedes
109
+ // the venv rule because the first read sets the default; the second is
110
+ // the install fallback. Restart-required: capabilityIndexEnabled flows
111
+ // from `tooling.capabilityIndex.enabled` via AssemblerParams →
112
+ // SECTIONS["tool-call-style"].
113
+ if (capabilityIndexEnabled === true) {
114
+ guidelines.push("- **Tool-first principle.** When this turn includes a `Capabilities` context and the task can be satisfied by a connected tool or available skill, prefer that capability over installing a Python or Node package. Use installs only for capabilities not covered by active tools, deferred tools, or visible prompt skills.");
115
+ }
67
116
  guidelines.push("- **Python projects:** Always create a virtualenv per project (`python3 -m venv .venv`). "
68
117
  + "Install packages into the project venv (`source .venv/bin/activate && pip install ...`). "
69
118
  + "Never use `--break-system-packages` — it pollutes the system Python. "
@@ -79,6 +79,28 @@ export interface AssemblerParams {
79
79
  excludeBootstrapFromContext?: boolean;
80
80
  /** Workspace profile controlling platform instruction verbosity ('full' or 'specialist'). */
81
81
  workspaceProfile?: "full" | "specialist";
82
+ /**
83
+ * Capability index gate.
84
+ *
85
+ * When `true`, `buildToolingSection` emits the residual one-liner instead
86
+ * of the legacy `## Available Tools` flat block; the per-turn
87
+ * `## Capabilities` block is rendered into the dynamic preamble in
88
+ * `executor-prompt-runner.ts`.
89
+ * When `false` or `undefined`, the static prompt is byte-identical to
90
+ * the pre-feature baseline.
91
+ *
92
+ * RESTART-REQUIRED: this flag selects between two cached system-prompt
93
+ * shapes. Daemon wiring documents the operator-facing constraint.
94
+ *
95
+ * SAFE INSIDE THE CACHE FENCE: this value is config-derived
96
+ * (operator-only, restart-required) and stable across all turns of a
97
+ * session. Plumbing it through `assemblerParams` does NOT create a
98
+ * cache-thrash regression. Live-runtime accessors
99
+ * (getPromptSkillCapabilities, getConnectedMcpServers) are forbidden
100
+ * inside this interface and enforced by an architecture-grep test.
101
+ * Adding a config-derived flag to the cache fence is safe.
102
+ */
103
+ capabilityIndexEnabled?: boolean;
82
104
  /** Whether Silent Execution Planner (SEP) is enabled for this agent. */
83
105
  sepEnabled?: boolean;
84
106
  }
@@ -82,8 +82,8 @@ export const SECTIONS = [
82
82
  { id: "safety", includeIn: MODES_ALL, build: (p, m) => buildSafetySection(m === "minimal") },
83
83
  { id: "language", includeIn: MODES_ALL, build: (p) => buildLanguageSection(p.userLanguage) },
84
84
  // --- Semi-stable body: operational-kept sections (MODES_ALL -- builders self-filter for minimal) ---
85
- { id: "tooling", includeIn: MODES_ALL, build: (p, m) => buildToolingSection(p.toolNames ?? [], m === "minimal" ? "small" : "large", p.toolSummaries) },
86
- { id: "tool-call-style", includeIn: MODES_ALL, build: (p, m) => buildToolCallStyleSection(m === "minimal", p.toolNames ?? []) },
85
+ { id: "tooling", includeIn: MODES_ALL, build: (p, m) => buildToolingSection(p.toolNames ?? [], m === "minimal" ? "small" : "large", p.toolSummaries, p.capabilityIndexEnabled) },
86
+ { id: "tool-call-style", includeIn: MODES_ALL, build: (p, m) => buildToolCallStyleSection(m === "minimal", p.toolNames ?? [], p.capabilityIndexEnabled) },
87
87
  // --- Operational-stripped sections (MODES_FULL_MIN -- dropped in "operational") ---
88
88
  { id: "self-update", includeIn: MODES_FULL_MIN, build: (p, m) => buildSelfUpdateGatingSection(p.toolNames ?? [], m === "minimal", true) },
89
89
  { id: "config-secret", includeIn: MODES_FULL_MIN, build: (p, m) => buildConfigSecretIntegritySection(p.toolNames ?? [], m === "minimal") },
@@ -10,11 +10,7 @@
10
10
  *
11
11
  * @module
12
12
  */
13
- /**
14
- * Extract the MCP server name from a sanitized tool name.
15
- * Format: `mcp__serverName--toolName`. Returns undefined for non-MCP tools.
16
- */
17
- export declare function extractMcpServerName(toolName: string): string | undefined;
13
+ export { extractMcpServerName } from "@comis/shared";
18
14
  /**
19
15
  * Classify an MCP error message into a category for observability.
20
16
  */
@@ -13,21 +13,9 @@
13
13
  */
14
14
  // ---------------------------------------------------------------------------
15
15
  // MCP attribution helpers
16
- // Defined inline to avoid cross-package import from @comis/skills.
16
+ // Re-exported from @comis/shared (canonical home).
17
17
  // ---------------------------------------------------------------------------
18
- /**
19
- * Extract the MCP server name from a sanitized tool name.
20
- * Format: `mcp__serverName--toolName`. Returns undefined for non-MCP tools.
21
- */
22
- export function extractMcpServerName(toolName) {
23
- if (!toolName.startsWith("mcp__"))
24
- return undefined;
25
- const rest = toolName.slice(5);
26
- const sepIdx = rest.indexOf("--");
27
- if (sepIdx <= 0)
28
- return undefined;
29
- return rest.slice(0, sepIdx);
30
- }
18
+ export { extractMcpServerName } from "@comis/shared";
31
19
  /**
32
20
  * Classify an MCP error message into a category for observability.
33
21
  */
@@ -26,6 +26,16 @@ export interface BridgeMetricsState {
26
26
  lastContextUsage: ContextUsageData | undefined;
27
27
  textEmitted: boolean;
28
28
  lastLlmErrorMessage: string | undefined;
29
+ /** Per-turn capture of outbound delivery events. Populated by pi-event-bridge
30
+ * on tool_execution_end for `message(action='send'|'reply'|'attach')`.
31
+ * Read by executor-post-execution.ts to make sentinel-aware decisions.
32
+ * Reset at turn start. */
33
+ outboundLog: Array<{
34
+ action: string;
35
+ channelType: string;
36
+ channelId: string;
37
+ timestamp: number;
38
+ }>;
29
39
  toolStartTimes: Map<string, number>;
30
40
  toolCallHistory: string[];
31
41
  lastActiveToolName: string | undefined;
@@ -75,6 +85,22 @@ export interface BridgeMetricsState {
75
85
  /** Total tool calls across all signature-replay scrubs whose thoughtSignature
76
86
  * was stripped (post-incident-visibility metric). */
77
87
  signatureScrubsToolCallsAffected: number;
88
+ /**
89
+ * Per-composite-key drain inflight gate. Owned by the bridge; passed into
90
+ * `drainAt(...)` at the `tool_execution_end` call site (inline-consumption +
91
+ * composite drain).
92
+ *
93
+ * Map keyed by `${agentId}:${channelType}:${channelId}` (composite key).
94
+ * Concurrent calls for the SAME composite key return immediately
95
+ * (single-tick gate). Concurrent calls for DIFFERENT composite keys drain
96
+ * independently (multi-agent isolation).
97
+ *
98
+ * Entry cleanup: `.delete(formatted)` runs in `.finally(...)` of the
99
+ * drain promise so the Map size remains bounded across long-running
100
+ * sessions (entry removed within one event-loop tick of drain
101
+ * resolution).
102
+ */
103
+ drainInflightByKey: Map<string, Promise<void>>;
78
104
  }
79
105
  /**
80
106
  * Create a fresh metrics state with all counters zeroed.
@@ -29,6 +29,7 @@ export function createBridgeMetrics() {
29
29
  lastContextUsage: undefined,
30
30
  textEmitted: false,
31
31
  lastLlmErrorMessage: undefined,
32
+ outboundLog: [],
32
33
  toolStartTimes: new Map(),
33
34
  toolCallHistory: [],
34
35
  lastActiveToolName: undefined,
@@ -58,6 +59,8 @@ export function createBridgeMetrics() {
58
59
  hashAssertionMismatches: 0,
59
60
  signatureScrubs: 0,
60
61
  signatureScrubsToolCallsAffected: 0,
62
+ // per-composite-key drain inflight gate.
63
+ drainInflightByKey: new Map(),
61
64
  };
62
65
  }
63
66
  /**
@@ -20,6 +20,7 @@ import type { ProviderHealthMonitor } from "../safety/provider-health-monitor.js
20
20
  import type { ContextWindowGuard, ContextUsageData } from "../safety/context-window-guard.js";
21
21
  import type { ExecutionResult } from "../executor/types.js";
22
22
  import type { ExecutionPlan } from "../planner/types.js";
23
+ import { type DrainInflightState } from "../executor/drain-helper.js";
23
24
  import { type ThinkingBlockHash } from "./thinking-block-hash-invariant.js";
24
25
  /** Per-call TTL split estimate, populated by requestBodyInjector's onPayload.
25
26
  * Shared mutable object — written by the stream wrapper, read by the bridge. */
@@ -203,6 +204,14 @@ export interface PiEventBridgeResult {
203
204
  hashes: ReadonlyMap<string, ReadonlyArray<ThinkingBlockHash>>;
204
205
  canonical: ReadonlyMap<string, ReadonlyArray<unknown>>;
205
206
  };
207
+ /**
208
+ * Expose the bridge-owned drain inflight gate so executor-post-execution
209
+ * can fire an end-of-turn backstop drainAt sharing the same composite-key
210
+ * gate map. Returns the live `BridgeMetricsState` slice -- callers MUST
211
+ * treat this as read-mostly (the only mutation contract is `drainAt`
212
+ * adding/removing entries).
213
+ */
214
+ getDrainState: () => DrainInflightState;
206
215
  }
207
216
  export { sanitizeToolArgs, extractErrorText } from "./bridge-event-handlers.js";
208
217
  /**
@@ -18,8 +18,10 @@ import { getCacheProviderInfo } from "../executor/cache-usage-helpers.js";
18
18
  import { sanitizeMcpToolNameForAnalytics } from "../executor/cache-break-detection.js";
19
19
  import { classifyError } from "../executor/error-classifier.js";
20
20
  import { extractPlanFromResponse } from "../planner/plan-extractor.js";
21
- import { extractMcpServerName, classifyMcpErrorType, sanitizeToolArgs, extractErrorText } from "./bridge-event-handlers.js";
21
+ import { extractMcpServerName } from "@comis/shared";
22
+ import { classifyMcpErrorType, sanitizeToolArgs, extractErrorText } from "./bridge-event-handlers.js";
22
23
  import { createBridgeMetrics, buildBridgeResult } from "./bridge-metrics.js";
24
+ import { drainAt } from "../executor/drain-helper.js";
23
25
  import { checkStepLimit, emitStepLimitAbort, checkBudgetLimit, emitBudgetAbort, checkBudgetTrajectory, checkContextWindow, emitContextAbort, checkCircuitBreaker, emitCircuitBreakerAbort } from "./bridge-safety-controls.js";
24
26
  import { computeThinkingBlockHashes, diffThinkingBlocksAgainstPersisted, WIRE_DIFF_HINT_FILE_MISSING, WIRE_DIFF_HINT_NOT_FOUND, } from "./thinking-block-hash-invariant.js";
25
27
  // Re-export helper functions for backward compatibility with existing imports
@@ -171,6 +173,62 @@ export function createPiEventBridge(deps) {
171
173
  durationMs,
172
174
  ...(errorText && { errorText }),
173
175
  });
176
+ // Capture outbound deliveries. The post-execution silent-sentinel
177
+ // gate reads this per-turn log to make sentinel-aware decisions
178
+ // about paired memory persistence. Reset at turn_start; bounded by
179
+ // per-turn outbound message count.
180
+ //
181
+ // On the SAME tool_execution_end event, fire
182
+ // `drainAt({agentId, channelType, channelId})` (the composite-keyed
183
+ // inline-consumption drain). The gate state lives in bridge-metrics
184
+ // (`m.drainInflightByKey`) so concurrent drains for the same
185
+ // composite key collapse to a single in-flight Promise; concurrent
186
+ // drains for different composite keys (multi-agent) run
187
+ // independently. Failures inside drainAt are suppressed with WARN
188
+ // logging -- the bridge's tool_execution_end propagation is NEVER
189
+ // aborted by drain misbehavior.
190
+ if (endEvent.toolName === "message" && toolSuccess && sanitizedArgs) {
191
+ const action = typeof sanitizedArgs.action === "string" ? sanitizedArgs.action : "";
192
+ if (action === "send" || action === "reply" || action === "attach") {
193
+ const channelType = typeof sanitizedArgs.channel_type === "string" ? sanitizedArgs.channel_type : "";
194
+ const channelId = typeof sanitizedArgs.channel_id === "string" ? sanitizedArgs.channel_id : "";
195
+ m.outboundLog.push({
196
+ action,
197
+ channelType,
198
+ channelId,
199
+ timestamp: Date.now(),
200
+ });
201
+ // Composite-key drain at the bridge call site. The composite
202
+ // (agentId, channelType, channelId) prevents cross-agent
203
+ // contamination of the inline-consumption queue. Use the
204
+ // message tool's own channel_type / channel_id when present
205
+ // (the tool resolved them); fall back to the bridge's bound
206
+ // deps.channelId when the tool args were sanitized away
207
+ // (defensive). A drain trigger with empty channelType OR empty
208
+ // channelId is skipped -- formatDrainKey would otherwise
209
+ // produce ambiguous keys.
210
+ //
211
+ // drainAt is fire-and-forget: it spawns the drain Promise and
212
+ // wraps it in suppressError internally. The kickoffDrain
213
+ // wrapper below double-wraps the synchronous invocation in
214
+ // suppressError (Promise.resolve adapter) so a (impossible)
215
+ // synchronous throw inside drainAt cannot abort the bridge's
216
+ // tool_execution_end propagation -- fire-and-forget contract /
217
+ // non-fatal drain failure.
218
+ const drainChannelType = channelType.length > 0 ? channelType : "";
219
+ const drainChannelId = channelId.length > 0 ? channelId : deps.channelId;
220
+ if (drainChannelType.length > 0 && drainChannelId.length > 0) {
221
+ const kickoffDrain = Promise.resolve().then(() => {
222
+ drainAt({
223
+ agentId: deps.agentId,
224
+ channelType: drainChannelType,
225
+ channelId: drainChannelId,
226
+ }, { drainInflightByKey: m.drainInflightByKey }, deps.logger);
227
+ });
228
+ suppressError(kickoffDrain, "bridge tool_use_complete drainAt kickoff");
229
+ }
230
+ }
231
+ }
174
232
  // Look up truncation metadata from stream wrapper registry
175
233
  const truncMeta = deps.getTruncationMeta?.(endEvent.toolCallId);
176
234
  deps.eventBus.emit("tool:executed", {
@@ -222,6 +280,10 @@ export function createPiEventBridge(deps) {
222
280
  // LLM turn about to start (pre-serialize hook for assert+restore)
223
281
  // -----------------------------------------------------------------
224
282
  case "turn_start": {
283
+ // Reset per-turn outbound capture. The log accumulates
284
+ // message(send/reply/attach) calls across the turn so the
285
+ // post-execution gate can make sentinel-aware decisions.
286
+ m.outboundLog.length = 0;
225
287
  // Run the executor-supplied pre-call closure once per turn, before
226
288
  // pi-ai reads `session.agent.state.messages` to serialize the next
227
289
  // API request. The closure performs the assert-then-restore pass
@@ -1050,5 +1112,14 @@ export function createPiEventBridge(deps) {
1050
1112
  hashes: m.thinkingBlockHashes,
1051
1113
  canonical: m.thinkingBlockCanonical,
1052
1114
  });
1053
- return { listener, getResult, addGhostCost, getThinkingBlockStores };
1115
+ // Expose the per-composite-key drain inflight gate so executor-post-
1116
+ // execution can fire an end-of-turn backstop drainAt that shares the SAME
1117
+ // gate map as the bridge's tool_execution_end call site. The returned
1118
+ // object has a live reference to the underlying Map -- mutations from the
1119
+ // bridge AND the executor post-execution path land in the same state
1120
+ // container, satisfying the single-tick gate contract.
1121
+ const getDrainState = () => ({
1122
+ drainInflightByKey: m.drainInflightByKey,
1123
+ });
1124
+ return { listener, getResult, addGhostCost, getThinkingBlockStores, getDrainState };
1054
1125
  }
@@ -21,16 +21,16 @@
21
21
  * plain text rather than sending sanitized-text + original-signature
22
22
  * mismatch. Skips `redacted: true` blocks (no readable text to taint).
23
23
  *
24
- * 260430-anthropic-400-thinking-block: cacheFenceIndex is intentionally
25
- * NOT consulted to gate guarding. The guard is pure/deterministic — input
26
- * messages → same guarded output every time — so iter 1 strips,
27
- * Anthropic caches the guarded prefix, iter 2 strips identically, and the
28
- * cache hits. The prior fence-skip caused per-execution divergence
29
- * symmetric to the bug found in `signature-replay-scrubber.ts` and
30
- * `thinking-block-cleaner.ts`: iter 1 stripped (fence=-1) and built a
31
- * surrogate-safe cached prefix, iter 2 preserved fence-protected messages
32
- * (fence>0) and re-introduced surrogate-tainted-with-original-signature
33
- * blocks at positions Anthropic had cached without them.
24
+ * cacheFenceIndex is intentionally NOT consulted to gate guarding. The
25
+ * guard is pure/deterministic — input messages → same guarded output every
26
+ * time — so iter 1 strips, Anthropic caches the guarded prefix, iter 2
27
+ * strips identically, and the cache hits. The prior fence-skip caused
28
+ * per-execution divergence symmetric to the bug found in
29
+ * `signature-replay-scrubber.ts` and `thinking-block-cleaner.ts`: iter 1
30
+ * stripped (fence=-1) and built a surrogate-safe cached prefix, iter 2
31
+ * preserved fence-protected messages (fence>0) and re-introduced
32
+ * surrogate-tainted-with-original-signature blocks at positions Anthropic
33
+ * had cached without them.
34
34
  *
35
35
  * Immutability: never mutates input; shallow-copies the block and the
36
36
  * containing message only when scrubbing is needed. When no scrub fires,
@@ -22,16 +22,16 @@
22
22
  * plain text rather than sending sanitized-text + original-signature
23
23
  * mismatch. Skips `redacted: true` blocks (no readable text to taint).
24
24
  *
25
- * 260430-anthropic-400-thinking-block: cacheFenceIndex is intentionally
26
- * NOT consulted to gate guarding. The guard is pure/deterministic — input
27
- * messages → same guarded output every time — so iter 1 strips,
28
- * Anthropic caches the guarded prefix, iter 2 strips identically, and the
29
- * cache hits. The prior fence-skip caused per-execution divergence
30
- * symmetric to the bug found in `signature-replay-scrubber.ts` and
31
- * `thinking-block-cleaner.ts`: iter 1 stripped (fence=-1) and built a
32
- * surrogate-safe cached prefix, iter 2 preserved fence-protected messages
33
- * (fence>0) and re-introduced surrogate-tainted-with-original-signature
34
- * blocks at positions Anthropic had cached without them.
25
+ * cacheFenceIndex is intentionally NOT consulted to gate guarding. The
26
+ * guard is pure/deterministic — input messages → same guarded output every
27
+ * time — so iter 1 strips, Anthropic caches the guarded prefix, iter 2
28
+ * strips identically, and the cache hits. The prior fence-skip caused
29
+ * per-execution divergence symmetric to the bug found in
30
+ * `signature-replay-scrubber.ts` and `thinking-block-cleaner.ts`: iter 1
31
+ * stripped (fence=-1) and built a surrogate-safe cached prefix, iter 2
32
+ * preserved fence-protected messages (fence>0) and re-introduced
33
+ * surrogate-tainted-with-original-signature blocks at positions Anthropic
34
+ * had cached without them.
35
35
  *
36
36
  * Immutability: never mutates input; shallow-copies the block and the
37
37
  * containing message only when scrubbing is needed. When no scrub fires,
@@ -69,10 +69,10 @@ export function createSignatureSurrogateGuard(deps) {
69
69
  for (let i = 0; i < messages.length; i++) {
70
70
  // eslint-disable-next-line security/detect-object-injection -- numeric index
71
71
  const original = messages[i];
72
- // 260430-anthropic-400-thinking-block: cacheFenceIndex is intentionally
73
- // NOT consulted here. Stripping uniformly across the array keeps the
74
- // guarded prefix identical across iterations of the same execution,
75
- // which is what Anthropic's prompt-cache validator requires.
72
+ // cacheFenceIndex is intentionally NOT consulted here. Stripping
73
+ // uniformly across the array keeps the guarded prefix identical
74
+ // across iterations of the same execution, which is what
75
+ // Anthropic's prompt-cache validator requires.
76
76
  const msg = original;
77
77
  if (msg.role !== "assistant" || !Array.isArray(msg.content)) {
78
78
  // eslint-disable-next-line security/detect-object-injection -- numeric index
@@ -5,17 +5,16 @@
5
5
  * keep-window, measured in assistant turns (not turn pairs). Redacted thinking
6
6
  * blocks (containing encrypted signatures for API continuity) are always preserved.
7
7
  *
8
- * 260430-anthropic-400-thinking-block: cacheFenceIndex is intentionally NOT
9
- * consulted to gate stripping. The cleaner is pure/deterministic — input
10
- * messages → same cleaned output every time — so iteration 1 strips,
11
- * Anthropic caches the cleaned prefix, iteration 2 strips identically, and
12
- * the cache hits. The prior fence-skip caused per-execution divergence:
13
- * iter 1 stripped (fence=-1) and built a thinking-free cached prefix,
14
- * iter 2 preserved fence-protected messages (fence>0) and re-introduced
15
- * thinking blocks at positions Anthropic had cached without them, which
16
- * the prompt-cache validator rejected with `400 ... blocks cannot be
17
- * modified`. The cacheFenceIndex on the budget is read for diagnostic
18
- * stats only and never gates the strip decision.
8
+ * cacheFenceIndex is intentionally NOT consulted to gate stripping. The
9
+ * cleaner is pure/deterministic — input messages → same cleaned output
10
+ * every time — so iteration 1 strips, Anthropic caches the cleaned prefix,
11
+ * iteration 2 strips identically, and the cache hits. The prior fence-skip
12
+ * caused per-execution divergence: iter 1 stripped (fence=-1) and built a
13
+ * thinking-free cached prefix, iter 2 preserved fence-protected messages
14
+ * (fence>0) and re-introduced thinking blocks at positions Anthropic had
15
+ * cached without them, which the prompt-cache validator rejected with
16
+ * `400 ... blocks cannot be modified`. The cacheFenceIndex on the budget
17
+ * is read for diagnostic stats only and never gates the strip decision.
19
18
  *
20
19
  * Immutability: never mutates input messages or arrays. Returns new arrays and
21
20
  * shallow-copied messages only when changes are needed. When no changes are
@@ -39,8 +38,7 @@ import type { ContextLayer } from "./types.js";
39
38
  export declare function createThinkingBlockCleaner(keepTurns: number, onCleaned?: (stats: {
40
39
  blocksRemoved: number;
41
40
  /** Cache fence index when present on the budget; reported for diagnostics
42
- * only. Stripping is no longer gated on the fence (260430-anthropic-400-
43
- * thinking-block). */
41
+ * only. Stripping is no longer gated on the fence. */
44
42
  cacheFenceIndex?: number;
45
43
  /** Number of messages protected by the cache fence. Always undefined now
46
44
  * because the fence does not protect any messages from stripping. */
@@ -6,17 +6,16 @@
6
6
  * keep-window, measured in assistant turns (not turn pairs). Redacted thinking
7
7
  * blocks (containing encrypted signatures for API continuity) are always preserved.
8
8
  *
9
- * 260430-anthropic-400-thinking-block: cacheFenceIndex is intentionally NOT
10
- * consulted to gate stripping. The cleaner is pure/deterministic — input
11
- * messages → same cleaned output every time — so iteration 1 strips,
12
- * Anthropic caches the cleaned prefix, iteration 2 strips identically, and
13
- * the cache hits. The prior fence-skip caused per-execution divergence:
14
- * iter 1 stripped (fence=-1) and built a thinking-free cached prefix,
15
- * iter 2 preserved fence-protected messages (fence>0) and re-introduced
16
- * thinking blocks at positions Anthropic had cached without them, which
17
- * the prompt-cache validator rejected with `400 ... blocks cannot be
18
- * modified`. The cacheFenceIndex on the budget is read for diagnostic
19
- * stats only and never gates the strip decision.
9
+ * cacheFenceIndex is intentionally NOT consulted to gate stripping. The
10
+ * cleaner is pure/deterministic — input messages → same cleaned output
11
+ * every time — so iteration 1 strips, Anthropic caches the cleaned prefix,
12
+ * iteration 2 strips identically, and the cache hits. The prior fence-skip
13
+ * caused per-execution divergence: iter 1 stripped (fence=-1) and built a
14
+ * thinking-free cached prefix, iter 2 preserved fence-protected messages
15
+ * (fence>0) and re-introduced thinking blocks at positions Anthropic had
16
+ * cached without them, which the prompt-cache validator rejected with
17
+ * `400 ... blocks cannot be modified`. The cacheFenceIndex on the budget
18
+ * is read for diagnostic stats only and never gates the strip decision.
20
19
  *
21
20
  * Immutability: never mutates input messages or arrays. Returns new arrays and
22
21
  * shallow-copied messages only when changes are needed. When no changes are
@@ -77,10 +76,10 @@ export function createThinkingBlockCleaner(keepTurns, onCleaned, getKeepTurnsOve
77
76
  let blocksRemoved = 0;
78
77
  const result = new Array(messages.length);
79
78
  for (let i = 0; i < messages.length; i++) {
80
- // 260430-anthropic-400-thinking-block: cacheFenceIndex is intentionally
81
- // NOT consulted here. Stripping uniformly across the array keeps the
82
- // cleaned prefix identical across iterations of the same execution,
83
- // which is what Anthropic's prompt-cache validator requires.
79
+ // cacheFenceIndex is intentionally NOT consulted here. Stripping
80
+ // uniformly across the array keeps the cleaned prefix identical
81
+ // across iterations of the same execution, which is what
82
+ // Anthropic's prompt-cache validator requires.
84
83
  const msg = messages[i];
85
84
  if (!oldAssistantIndices.has(i)) {
86
85
  // Within keep-window or not an assistant message -- pass through unchanged
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Per-turn capability-index renderer.
3
+ *
4
+ * Renders the `## Capabilities` block for the dynamic preamble. Lives
5
+ * post-deferral in the executor lifecycle; consumes a `ToolCapabilityPort`
6
+ * for cluster/skill resolution and an `ExcludeDeferralResult` for active
7
+ * vs deferred tool partitioning.
8
+ *
9
+ * Pure-function builder: no logger, no IO, no `Result` envelope, no mutable
10
+ * module state beyond frozen module-level constants. Mirrors the shape of
11
+ * `buildDeferredToolsContext` in `tool-deferral.ts`.
12
+ *
13
+ * IMPORTANT -- cache fence:
14
+ * This module is consumed ONLY by `executor-tool-assembly.ts`. It MUST NOT
15
+ * be imported by `prompt-assembly.ts` -- the static prompt cache prefix MUST
16
+ * stay byte-identical when the skill registry reloads between turns.
17
+ * An architecture-grep enforces this invariant.
18
+ *
19
+ * @module
20
+ */
21
+ import type { ToolCapabilityPort } from "@comis/core";
22
+ import type { ExcludeDeferralResult } from "./tool-deferral.js";
23
+ /**
24
+ * Output struct from {@link buildCapabilityIndexContext}. All counts are
25
+ * post-elision -- they reflect what the renderer surfaces, not the upstream
26
+ * input cardinality.
27
+ */
28
+ export interface CapabilityIndexRenderResult {
29
+ /** Rendered text block. Empty string when the index is gated off or all-zero counts. */
30
+ readonly text: string;
31
+ /** Estimated tokens for the rendered text (`Math.ceil(text.length / CHARS_PER_TOKEN_RATIO)`). */
32
+ readonly capabilityIndexTokens: number;
33
+ /** Number of distinct clusters surfaced. */
34
+ readonly clusterCount: number;
35
+ /** Number of active tools (builtins + active MCP tools). */
36
+ readonly activeToolCount: number;
37
+ /** Number of deferred MCP tools after the orphan-drop (server connected). */
38
+ readonly deferredToolCount: number;
39
+ /** Number of visible eligible prompt skills (port-reported, not rendered count). */
40
+ readonly promptSkillCount: number;
41
+ }
42
+ /**
43
+ * Build the per-turn capability-index render result.
44
+ *
45
+ * Behavior:
46
+ * - Gate respect: returns {@link EMPTY} when `port.isCapabilityIndexEnabled()` is false.
47
+ * - Empty-input fast path: returns {@link EMPTY} when all three surface counts are zero.
48
+ * - Cluster bucketing: builtins -> `getBuiltinCluster()` (or `"other-tools"`),
49
+ * MCP -> `getMcpServerHint().cluster` (or `"external-integrations"`),
50
+ * skills -> `skill.cluster` (or `"prompt-skills"`).
51
+ * - Orphan-drop: deferred MCP tools whose server is not in the live
52
+ * `getConnectedMcpServers()` snapshot are dropped silently.
53
+ * - Sort: `(priority asc, clusterId asc)` for clusters; `TOOL_ORDER` for
54
+ * builtins (alphabetical fallback for unknowns); alphabetical for MCP
55
+ * servers and skills.
56
+ * - Per-group cap: 8 names + `+N more`.
57
+ * - >32 elision: drop ALL per-cluster name lists; keep headers + counts only.
58
+ * - Forbidden-literal discipline: the rendered text names neither the
59
+ * client-side discovery tool nor the server-side tool search regex tool.
60
+ * The deferred-tools preamble bullet uses the mechanism-neutral
61
+ * `"discovery mechanism available in your active toolspace"` wording.
62
+ * An architecture-grep enforces the file-level invariant.
63
+ *
64
+ * Restart-required note: `tooling.capabilityIndex.enabled` requires a daemon
65
+ * restart to take effect. The renderer respects the port's reported value at
66
+ * render time but does not enforce the restart constraint.
67
+ *
68
+ * @param deferralResult - Output of `applyToolDeferral` (active + deferred tool partition).
69
+ * @param port - The capability port (gate flag, cluster/skill resolution, live runtime view).
70
+ * @returns Frozen {@link CapabilityIndexRenderResult}; identity-stable {@link EMPTY} for the no-op path.
71
+ */
72
+ export declare function buildCapabilityIndexContext(deferralResult: ExcludeDeferralResult, port: ToolCapabilityPort): CapabilityIndexRenderResult;