comisai 1.0.27 → 1.0.30

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 (246) hide show
  1. package/node_modules/@comis/agent/dist/bootstrap/sections/tool-descriptions.js +62 -8
  2. package/node_modules/@comis/agent/dist/bootstrap/sections/tooling-sections.js +3 -1
  3. package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.d.ts +7 -0
  4. package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.js +26 -0
  5. package/node_modules/@comis/agent/dist/context-engine/signature-replay-scrubber.d.ts +21 -0
  6. package/node_modules/@comis/agent/dist/context-engine/signature-replay-scrubber.js +29 -9
  7. package/node_modules/@comis/agent/dist/context-engine/signature-surrogate-guard.d.ts +10 -2
  8. package/node_modules/@comis/agent/dist/context-engine/signature-surrogate-guard.js +15 -9
  9. package/node_modules/@comis/agent/dist/context-engine/thinking-block-cleaner.d.ts +17 -2
  10. package/node_modules/@comis/agent/dist/context-engine/thinking-block-cleaner.js +19 -8
  11. package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.js +28 -0
  12. package/node_modules/@comis/agent/dist/executor/executor-response-filter.js +3 -0
  13. package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.js +3 -1
  14. package/node_modules/@comis/agent/dist/executor/phase-filter.d.ts +20 -4
  15. package/node_modules/@comis/agent/dist/executor/phase-filter.js +62 -19
  16. package/node_modules/@comis/agent/dist/executor/pi-executor.js +6 -0
  17. package/node_modules/@comis/agent/dist/executor/stream-wrappers/config-resolver.js +2 -3
  18. package/node_modules/@comis/agent/dist/executor/stream-wrappers/request-body-injector.js +2 -3
  19. package/node_modules/@comis/agent/dist/executor/ttl-guard.js +2 -3
  20. package/node_modules/@comis/agent/dist/index.d.ts +4 -2
  21. package/node_modules/@comis/agent/dist/index.js +3 -2
  22. package/node_modules/@comis/agent/dist/model/compaction-model-resolver.d.ts +41 -0
  23. package/node_modules/@comis/agent/dist/model/compaction-model-resolver.js +51 -0
  24. package/node_modules/@comis/agent/dist/model/model-registry-adapter.js +113 -26
  25. package/node_modules/@comis/agent/dist/model/model-scanner.d.ts +27 -0
  26. package/node_modules/@comis/agent/dist/model/model-scanner.js +64 -23
  27. package/node_modules/@comis/agent/dist/model/operation-model-defaults.d.ts +37 -15
  28. package/node_modules/@comis/agent/dist/model/operation-model-defaults.js +70 -25
  29. package/node_modules/@comis/agent/dist/model/operation-model-resolver.d.ts +2 -2
  30. package/node_modules/@comis/agent/dist/model/operation-model-resolver.js +12 -8
  31. package/node_modules/@comis/agent/dist/provider/capabilities.d.ts +21 -0
  32. package/node_modules/@comis/agent/dist/provider/capabilities.js +28 -0
  33. package/node_modules/@comis/agent/dist/session/orphaned-message-repair.js +61 -1
  34. package/node_modules/@comis/agent/dist/workspace/templates.js +1 -1
  35. package/node_modules/@comis/agent/package.json +1 -1
  36. package/node_modules/@comis/channels/dist/shared/channel-manager.d.ts +19 -1
  37. package/node_modules/@comis/channels/dist/shared/channel-manager.js +59 -3
  38. package/node_modules/@comis/channels/dist/shared/deliver-to-channel.d.ts +10 -0
  39. package/node_modules/@comis/channels/dist/shared/deliver-to-channel.js +25 -10
  40. package/node_modules/@comis/channels/dist/shared/execution-deliver.d.ts +1 -1
  41. package/node_modules/@comis/channels/dist/shared/execution-deliver.js +1 -1
  42. package/node_modules/@comis/channels/dist/shared/execution-pipeline.d.ts +8 -0
  43. package/node_modules/@comis/channels/dist/shared/inbound-gate.js +21 -3
  44. package/node_modules/@comis/channels/dist/shared/inbound-pipeline.d.ts +8 -0
  45. package/node_modules/@comis/channels/dist/shared/inbound-route.d.ts +1 -1
  46. package/node_modules/@comis/channels/dist/shared/inbound-route.js +1 -0
  47. package/node_modules/@comis/channels/dist/telegram/message-mapper.d.ts +18 -1
  48. package/node_modules/@comis/channels/dist/telegram/message-mapper.js +95 -1
  49. package/node_modules/@comis/channels/dist/telegram/telegram-adapter.js +7 -1
  50. package/node_modules/@comis/channels/package.json +1 -1
  51. package/node_modules/@comis/cli/package.json +1 -1
  52. package/node_modules/@comis/core/dist/config/schema-agent.d.ts +15 -3
  53. package/node_modules/@comis/core/dist/config/schema-agent.js +6 -2
  54. package/node_modules/@comis/core/dist/config/schema-integrations.d.ts +4 -4
  55. package/node_modules/@comis/core/dist/config/schema-integrations.js +3 -3
  56. package/node_modules/@comis/core/dist/config/schema-models.d.ts +4 -2
  57. package/node_modules/@comis/core/dist/config/schema-models.js +4 -2
  58. package/node_modules/@comis/core/package.json +1 -1
  59. package/node_modules/@comis/daemon/dist/daemon.js +74 -9
  60. package/node_modules/@comis/daemon/dist/rpc/agent-handlers.js +40 -9
  61. package/node_modules/@comis/daemon/dist/rpc/builtin-provider-guard.d.ts +16 -0
  62. package/node_modules/@comis/daemon/dist/rpc/builtin-provider-guard.js +60 -0
  63. package/node_modules/@comis/daemon/dist/rpc/config-handlers.js +59 -0
  64. package/node_modules/@comis/daemon/dist/rpc/credential-resolver.d.ts +17 -0
  65. package/node_modules/@comis/daemon/dist/rpc/credential-resolver.js +99 -0
  66. package/node_modules/@comis/daemon/dist/rpc/message-handlers.d.ts +5 -0
  67. package/node_modules/@comis/daemon/dist/rpc/message-handlers.js +25 -4
  68. package/node_modules/@comis/daemon/dist/rpc/model-handlers.d.ts +4 -3
  69. package/node_modules/@comis/daemon/dist/rpc/model-handlers.js +21 -3
  70. package/node_modules/@comis/daemon/dist/rpc/provider-handlers.js +82 -6
  71. package/node_modules/@comis/daemon/dist/rpc/rpc-dispatch.d.ts +4 -0
  72. package/node_modules/@comis/daemon/dist/wiring/inbound-message-id-resolver.d.ts +48 -0
  73. package/node_modules/@comis/daemon/dist/wiring/inbound-message-id-resolver.js +58 -0
  74. package/node_modules/@comis/daemon/dist/wiring/restart-continuation.d.ts +10 -0
  75. package/node_modules/@comis/daemon/dist/wiring/setup-agents.d.ts +18 -6
  76. package/node_modules/@comis/daemon/dist/wiring/setup-agents.js +98 -46
  77. package/node_modules/@comis/daemon/dist/wiring/setup-channels.d.ts +13 -1
  78. package/node_modules/@comis/daemon/dist/wiring/setup-channels.js +2 -1
  79. package/node_modules/@comis/daemon/dist/wiring/setup-gateway-rpc.js +1 -1
  80. package/node_modules/@comis/daemon/package.json +2 -2
  81. package/node_modules/@comis/gateway/package.json +1 -1
  82. package/node_modules/@comis/infra/package.json +1 -1
  83. package/node_modules/@comis/memory/package.json +1 -1
  84. package/node_modules/@comis/scheduler/package.json +1 -1
  85. package/node_modules/@comis/shared/package.json +1 -1
  86. package/node_modules/@comis/skills/dist/bridge/mcp-tool-bridge.d.ts +1 -1
  87. package/node_modules/@comis/skills/dist/bridge/mcp-tool-bridge.js +1 -1
  88. package/node_modules/@comis/skills/dist/bridge/tool-audit.js +1 -1
  89. package/node_modules/@comis/skills/dist/builtin/exec-tool.d.ts +12 -11
  90. package/node_modules/@comis/skills/dist/builtin/exec-tool.js +1 -1
  91. package/node_modules/@comis/skills/dist/builtin/file/apply-patch-tool.d.ts +3 -2
  92. package/node_modules/@comis/skills/dist/builtin/file/apply-patch-tool.js +1 -1
  93. package/node_modules/@comis/skills/dist/builtin/file-tools/edit-tool.d.ts +7 -6
  94. package/node_modules/@comis/skills/dist/builtin/file-tools/edit-tool.js +1 -1
  95. package/node_modules/@comis/skills/dist/builtin/file-tools/find-tool.d.ts +6 -5
  96. package/node_modules/@comis/skills/dist/builtin/file-tools/find-tool.js +1 -1
  97. package/node_modules/@comis/skills/dist/builtin/file-tools/grep-tool.d.ts +16 -15
  98. package/node_modules/@comis/skills/dist/builtin/file-tools/grep-tool.js +1 -1
  99. package/node_modules/@comis/skills/dist/builtin/file-tools/ls-tool.d.ts +4 -3
  100. package/node_modules/@comis/skills/dist/builtin/file-tools/ls-tool.js +1 -1
  101. package/node_modules/@comis/skills/dist/builtin/file-tools/notebook-edit-tool.d.ts +7 -6
  102. package/node_modules/@comis/skills/dist/builtin/file-tools/notebook-edit-tool.js +1 -1
  103. package/node_modules/@comis/skills/dist/builtin/file-tools/read-tool.d.ts +6 -5
  104. package/node_modules/@comis/skills/dist/builtin/file-tools/read-tool.js +1 -1
  105. package/node_modules/@comis/skills/dist/builtin/file-tools/write-tool.d.ts +5 -4
  106. package/node_modules/@comis/skills/dist/builtin/file-tools/write-tool.js +1 -1
  107. package/node_modules/@comis/skills/dist/builtin/platform/admin-manage-factory.d.ts +1 -1
  108. package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.d.ts +28 -27
  109. package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.js +1 -1
  110. package/node_modules/@comis/skills/dist/builtin/platform/background-tasks-tool.d.ts +4 -3
  111. package/node_modules/@comis/skills/dist/builtin/platform/background-tasks-tool.js +1 -1
  112. package/node_modules/@comis/skills/dist/builtin/platform/browser-tool-schema.d.ts +67 -66
  113. package/node_modules/@comis/skills/dist/builtin/platform/browser-tool-schema.js +1 -1
  114. package/node_modules/@comis/skills/dist/builtin/platform/channels-manage-tool.d.ts +6 -5
  115. package/node_modules/@comis/skills/dist/builtin/platform/channels-manage-tool.js +1 -1
  116. package/node_modules/@comis/skills/dist/builtin/platform/cron-tool.d.ts +20 -19
  117. package/node_modules/@comis/skills/dist/builtin/platform/cron-tool.js +1 -1
  118. package/node_modules/@comis/skills/dist/builtin/platform/ctx-expand-tool.d.ts +4 -3
  119. package/node_modules/@comis/skills/dist/builtin/platform/ctx-expand-tool.js +1 -1
  120. package/node_modules/@comis/skills/dist/builtin/platform/ctx-inspect-tool.d.ts +3 -2
  121. package/node_modules/@comis/skills/dist/builtin/platform/ctx-inspect-tool.js +1 -1
  122. package/node_modules/@comis/skills/dist/builtin/platform/ctx-recall-tool.d.ts +6 -5
  123. package/node_modules/@comis/skills/dist/builtin/platform/ctx-recall-tool.js +1 -1
  124. package/node_modules/@comis/skills/dist/builtin/platform/ctx-search-tool.d.ts +6 -5
  125. package/node_modules/@comis/skills/dist/builtin/platform/ctx-search-tool.js +1 -1
  126. package/node_modules/@comis/skills/dist/builtin/platform/describe-video-tool.d.ts +4 -3
  127. package/node_modules/@comis/skills/dist/builtin/platform/describe-video-tool.js +1 -1
  128. package/node_modules/@comis/skills/dist/builtin/platform/discord-action-tool.d.ts +2 -1
  129. package/node_modules/@comis/skills/dist/builtin/platform/discord-action-tool.js +1 -1
  130. package/node_modules/@comis/skills/dist/builtin/platform/extract-document-tool.d.ts +4 -3
  131. package/node_modules/@comis/skills/dist/builtin/platform/extract-document-tool.js +1 -1
  132. package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.d.ts +12 -11
  133. package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.js +1 -1
  134. package/node_modules/@comis/skills/dist/builtin/platform/heartbeat-manage-tool.d.ts +23 -22
  135. package/node_modules/@comis/skills/dist/builtin/platform/heartbeat-manage-tool.js +1 -1
  136. package/node_modules/@comis/skills/dist/builtin/platform/image-generate-tool.d.ts +4 -3
  137. package/node_modules/@comis/skills/dist/builtin/platform/image-generate-tool.js +1 -1
  138. package/node_modules/@comis/skills/dist/builtin/platform/image-tool.d.ts +8 -7
  139. package/node_modules/@comis/skills/dist/builtin/platform/image-tool.js +1 -1
  140. package/node_modules/@comis/skills/dist/builtin/platform/mcp-manage-tool.d.ts +9 -8
  141. package/node_modules/@comis/skills/dist/builtin/platform/mcp-manage-tool.js +1 -1
  142. package/node_modules/@comis/skills/dist/builtin/platform/memory-get-tool.d.ts +5 -4
  143. package/node_modules/@comis/skills/dist/builtin/platform/memory-get-tool.js +1 -1
  144. package/node_modules/@comis/skills/dist/builtin/platform/memory-manage-tool.d.ts +12 -11
  145. package/node_modules/@comis/skills/dist/builtin/platform/memory-manage-tool.js +1 -1
  146. package/node_modules/@comis/skills/dist/builtin/platform/memory-search-tool.d.ts +4 -3
  147. package/node_modules/@comis/skills/dist/builtin/platform/memory-search-tool.js +1 -1
  148. package/node_modules/@comis/skills/dist/builtin/platform/memory-store-tool.d.ts +4 -3
  149. package/node_modules/@comis/skills/dist/builtin/platform/memory-store-tool.js +1 -1
  150. package/node_modules/@comis/skills/dist/builtin/platform/message-tool.d.ts +32 -31
  151. package/node_modules/@comis/skills/dist/builtin/platform/message-tool.js +1 -1
  152. package/node_modules/@comis/skills/dist/builtin/platform/messaging-factory.d.ts +1 -1
  153. package/node_modules/@comis/skills/dist/builtin/platform/models-manage-tool.d.ts +4 -3
  154. package/node_modules/@comis/skills/dist/builtin/platform/models-manage-tool.js +11 -4
  155. package/node_modules/@comis/skills/dist/builtin/platform/notify-tool.d.ts +6 -5
  156. package/node_modules/@comis/skills/dist/builtin/platform/notify-tool.js +1 -1
  157. package/node_modules/@comis/skills/dist/builtin/platform/obs-query-tool.d.ts +11 -10
  158. package/node_modules/@comis/skills/dist/builtin/platform/obs-query-tool.js +1 -1
  159. package/node_modules/@comis/skills/dist/builtin/platform/pipeline-tool.d.ts +37 -36
  160. package/node_modules/@comis/skills/dist/builtin/platform/pipeline-tool.js +1 -1
  161. package/node_modules/@comis/skills/dist/builtin/platform/platform-action-tool.d.ts +1 -1
  162. package/node_modules/@comis/skills/dist/builtin/platform/providers-manage-tool.d.ts +21 -20
  163. package/node_modules/@comis/skills/dist/builtin/platform/providers-manage-tool.js +1 -1
  164. package/node_modules/@comis/skills/dist/builtin/platform/session-search-tool.d.ts +6 -5
  165. package/node_modules/@comis/skills/dist/builtin/platform/session-search-tool.js +1 -1
  166. package/node_modules/@comis/skills/dist/builtin/platform/session-status-tool.d.ts +3 -2
  167. package/node_modules/@comis/skills/dist/builtin/platform/session-status-tool.js +1 -1
  168. package/node_modules/@comis/skills/dist/builtin/platform/sessions-history-tool.d.ts +5 -4
  169. package/node_modules/@comis/skills/dist/builtin/platform/sessions-history-tool.js +1 -1
  170. package/node_modules/@comis/skills/dist/builtin/platform/sessions-list-tool.d.ts +4 -3
  171. package/node_modules/@comis/skills/dist/builtin/platform/sessions-list-tool.js +1 -1
  172. package/node_modules/@comis/skills/dist/builtin/platform/sessions-manage-tool.d.ts +5 -4
  173. package/node_modules/@comis/skills/dist/builtin/platform/sessions-manage-tool.js +1 -1
  174. package/node_modules/@comis/skills/dist/builtin/platform/sessions-send-tool.d.ts +7 -6
  175. package/node_modules/@comis/skills/dist/builtin/platform/sessions-send-tool.js +1 -1
  176. package/node_modules/@comis/skills/dist/builtin/platform/sessions-spawn-tool.d.ts +15 -14
  177. package/node_modules/@comis/skills/dist/builtin/platform/sessions-spawn-tool.js +1 -1
  178. package/node_modules/@comis/skills/dist/builtin/platform/skills-manage-tool.d.ts +8 -7
  179. package/node_modules/@comis/skills/dist/builtin/platform/skills-manage-tool.js +1 -1
  180. package/node_modules/@comis/skills/dist/builtin/platform/slack-action-tool.d.ts +2 -1
  181. package/node_modules/@comis/skills/dist/builtin/platform/slack-action-tool.js +1 -1
  182. package/node_modules/@comis/skills/dist/builtin/platform/subagents-tool.d.ts +7 -6
  183. package/node_modules/@comis/skills/dist/builtin/platform/subagents-tool.js +1 -1
  184. package/node_modules/@comis/skills/dist/builtin/platform/telegram-action-tool.d.ts +2 -1
  185. package/node_modules/@comis/skills/dist/builtin/platform/telegram-action-tool.js +1 -1
  186. package/node_modules/@comis/skills/dist/builtin/platform/tokens-manage-tool.d.ts +5 -4
  187. package/node_modules/@comis/skills/dist/builtin/platform/tokens-manage-tool.js +1 -1
  188. package/node_modules/@comis/skills/dist/builtin/platform/transcribe-audio-tool.d.ts +4 -3
  189. package/node_modules/@comis/skills/dist/builtin/platform/transcribe-audio-tool.js +1 -1
  190. package/node_modules/@comis/skills/dist/builtin/platform/tts-tool.d.ts +6 -5
  191. package/node_modules/@comis/skills/dist/builtin/platform/tts-tool.js +1 -1
  192. package/node_modules/@comis/skills/dist/builtin/platform/unified-context-tool.d.ts +13 -12
  193. package/node_modules/@comis/skills/dist/builtin/platform/unified-context-tool.js +1 -1
  194. package/node_modules/@comis/skills/dist/builtin/platform/unified-memory-tool.d.ts +18 -17
  195. package/node_modules/@comis/skills/dist/builtin/platform/unified-memory-tool.js +1 -1
  196. package/node_modules/@comis/skills/dist/builtin/platform/unified-session-tool.d.ts +11 -10
  197. package/node_modules/@comis/skills/dist/builtin/platform/unified-session-tool.js +1 -1
  198. package/node_modules/@comis/skills/dist/builtin/platform/whatsapp-action-tool.d.ts +2 -1
  199. package/node_modules/@comis/skills/dist/builtin/platform/whatsapp-action-tool.js +1 -1
  200. package/node_modules/@comis/skills/dist/builtin/process-tool.d.ts +6 -5
  201. package/node_modules/@comis/skills/dist/builtin/process-tool.js +1 -1
  202. package/node_modules/@comis/skills/dist/builtin/web-fetch-tool.d.ts +5 -4
  203. package/node_modules/@comis/skills/dist/builtin/web-fetch-tool.js +1 -1
  204. package/node_modules/@comis/skills/dist/builtin/web-search-tool.d.ts +9 -8
  205. package/node_modules/@comis/skills/dist/builtin/web-search-tool.js +1 -1
  206. package/node_modules/@comis/skills/package.json +1 -1
  207. package/node_modules/@comis/web/dist/assets/{agent-detail-DqL6Artv.js → agent-detail-71BSbSfD.js} +1 -1
  208. package/node_modules/@comis/web/dist/assets/{agent-editor-CNM_h94Y.js → agent-editor-CTSDZhwT.js} +1 -1
  209. package/node_modules/@comis/web/dist/assets/{agent-list-Dbh-xD_F.js → agent-list-BEhni2ea.js} +1 -1
  210. package/node_modules/@comis/web/dist/assets/{billing-view-C1DmtyzK.js → billing-view-DVP1IvVs.js} +1 -1
  211. package/node_modules/@comis/web/dist/assets/{channel-detail-CtCH22N1.js → channel-detail-N_YK74xC.js} +1 -1
  212. package/node_modules/@comis/web/dist/assets/{channel-list-C7xXn-60.js → channel-list-DRk6ZJaF.js} +1 -1
  213. package/node_modules/@comis/web/dist/assets/{chat-console-C51pjFwk.js → chat-console-Dm-GtSf9.js} +1 -1
  214. package/node_modules/@comis/web/dist/assets/{config-editor-BLArYRB7.js → config-editor-CIferYX6.js} +1 -1
  215. package/node_modules/@comis/web/dist/assets/{context-dag-browser-fuyMinNI.js → context-dag-browser-CL84rXXM.js} +1 -1
  216. package/node_modules/@comis/web/dist/assets/{context-engine-Bngf2bH0.js → context-engine-B1HOTEZv.js} +1 -1
  217. package/node_modules/@comis/web/dist/assets/{delivery-view-C80hucxX.js → delivery-view-Y6JKYVFw.js} +1 -1
  218. package/node_modules/@comis/web/dist/assets/{diagnostics-view-Cl4VbHZ6.js → diagnostics-view-DWV1UQjz.js} +1 -1
  219. package/node_modules/@comis/web/dist/assets/{ic-chat-message-ByFUoMm6.js → ic-chat-message-DfSERzzg.js} +1 -1
  220. package/node_modules/@comis/web/dist/assets/{ic-connection-dot-C4nDHgY2.js → ic-connection-dot-CXyhlJup.js} +1 -1
  221. package/node_modules/@comis/web/dist/assets/{ic-tool-call-Bh5kq-yY.js → ic-tool-call-DNmwTjek.js} +1 -1
  222. package/node_modules/@comis/web/dist/assets/{index-BBkuC-EU.js → index-CBr0Tm9_.js} +2 -2
  223. package/node_modules/@comis/web/dist/assets/{mcp-management-DB-phOo7.js → mcp-management-BaH2-vox.js} +1 -1
  224. package/node_modules/@comis/web/dist/assets/{media-config-CRqZ1ZUH.js → media-config-CZLshJoN.js} +1 -1
  225. package/node_modules/@comis/web/dist/assets/{media-test-C9vE20Oy.js → media-test-C9NUWgo_.js} +1 -1
  226. package/node_modules/@comis/web/dist/assets/{memory-inspector-CeqfnxMZ.js → memory-inspector-D_fmTcRN.js} +1 -1
  227. package/node_modules/@comis/web/dist/assets/{message-center-Daup7Mof.js → message-center-BBFlNCZn.js} +1 -1
  228. package/node_modules/@comis/web/dist/assets/{models-DLYnEU8E.js → models-BytGLm99.js} +1 -1
  229. package/node_modules/@comis/web/dist/assets/{observe-view-BTSt_PO5.js → observe-view-VXtHqaqq.js} +1 -1
  230. package/node_modules/@comis/web/dist/assets/{pipeline-builder-DknfzyLt.js → pipeline-builder-CfXczlfJ.js} +1 -1
  231. package/node_modules/@comis/web/dist/assets/{pipeline-history-JnHZdeU_.js → pipeline-history-CPmXFnbe.js} +1 -1
  232. package/node_modules/@comis/web/dist/assets/{pipeline-history-detail-Dg4knsEb.js → pipeline-history-detail-DcueTMs9.js} +1 -1
  233. package/node_modules/@comis/web/dist/assets/{pipeline-list-AEnibjsp.js → pipeline-list-B-xG5WZh.js} +1 -1
  234. package/node_modules/@comis/web/dist/assets/{pipeline-monitor-DG7RbIOO.js → pipeline-monitor-pnIOYaSY.js} +1 -1
  235. package/node_modules/@comis/web/dist/assets/{scheduler-uL1fYKAT.js → scheduler-BtUIFHhA.js} +1 -1
  236. package/node_modules/@comis/web/dist/assets/{security-C3DywRLH.js → security-C8mWRq2y.js} +1 -1
  237. package/node_modules/@comis/web/dist/assets/{session-detail-BtqCNWXV.js → session-detail-DgdkO5ka.js} +1 -1
  238. package/node_modules/@comis/web/dist/assets/{session-list-CJXWa2XT.js → session-list-DcylcfTn.js} +1 -1
  239. package/node_modules/@comis/web/dist/assets/{setup-wizard-ywn7oJvu.js → setup-wizard-BP5yjsuL.js} +75 -39
  240. package/node_modules/@comis/web/dist/assets/{skills-DX0KYnWD.js → skills-DXt1bX8Z.js} +1 -1
  241. package/node_modules/@comis/web/dist/assets/{subagents-B8p5YJEB.js → subagents-C7YbUHXY.js} +1 -1
  242. package/node_modules/@comis/web/dist/assets/{workspace-manager-CgzNIrw1.js → workspace-manager-DP6pW4wa.js} +1 -1
  243. package/node_modules/@comis/web/dist/index.html +1 -1
  244. package/node_modules/@comis/web/package.json +1 -1
  245. package/npm-shrinkwrap.json +6126 -0
  246. package/package.json +74 -74
@@ -18,6 +18,15 @@
18
18
  * @module
19
19
  */
20
20
  import { getToolMetadata } from "@comis/core";
21
+ import { getProviders } from "@mariozechner/pi-ai";
22
+ // ---------------------------------------------------------------------------
23
+ // Layer 1D (260430-vwt) -- live native-catalog provider list
24
+ //
25
+ // Computed once at module load time. Used by the providers_manage TOOL_GUIDE
26
+ // "Built-in vs Custom Provider Check" block below so the text reflects
27
+ // pi-ai's current native catalog without per-version source patches.
28
+ // ---------------------------------------------------------------------------
29
+ const _builtInProvidersList = [...getProviders()].sort().join(", ");
21
30
  // ---------------------------------------------------------------------------
22
31
  // TOOL_SUMMARIES: 5-8 word terse summaries (system prompt orientation)
23
32
  // ---------------------------------------------------------------------------
@@ -272,24 +281,68 @@ Present a plan to the user before creating agents in batch.
272
281
  Multiple agents can be created in one turn. Customize ALL workspace files for each agent after creation — or use single-call creation above to inline ROLE.md/IDENTITY.md and skip the post-create writes entirely.`,
273
282
  providers_manage: `## Provider Configuration Guide
274
283
 
284
+ ### Credential Pre-Check (MANDATORY before any provider switch)
285
+ BEFORE storing or switching, you MUST verify the credential exists. Never patch agents.*.provider, agents.*.model, call agents_manage update, call providers_manage create, or use gateway.patch on any agents.*.{provider,model} key without first running this 4-step flow:
286
+
287
+ 1. **List existing keys** — gateway({ action: "env_list", filter: "<PROVIDER>*" })
288
+ Examples: filter "OPENROUTER*" / "ANTHROPIC*" / "GROQ*" / "DEEPSEEK*".
289
+ env_list returns only NAMES, never values — safe to call proactively.
290
+ Use the canonical name: <PROVIDER_UPPER>_API_KEY for cloud providers
291
+ (OPENROUTER_API_KEY, ANTHROPIC_API_KEY, GROQ_API_KEY, DEEPSEEK_API_KEY,
292
+ GEMINI_API_KEY for google, etc.). If env_list returns a non-canonical
293
+ name (e.g. "OR_KEY", "MY_OPENROUTER_KEY"), use that matched name verbatim
294
+ as apiKeyName. Local providers (ollama, lm-studio, vLLM) skip this step
295
+ entirely (no API key needed).
296
+
297
+ 2. **Decide based on env_list result:**
298
+ a. **Match found** — use the matching env name as apiKeyName. Skip to step 4.
299
+ b. **No match** — ASK THE USER for the API key. Phrase it explicitly:
300
+ "I don't see a <KEY_NAME> configured. Please share your <Provider>
301
+ API key (signup link if relevant) and I'll store it before switching."
302
+ Do NOT proceed without the key. Do NOT invent a fake key. Do NOT
303
+ silently skip the switch.
304
+
305
+ 3. **Store the key (only after the user supplies it)** —
306
+ gateway({ action: "env_set", env_key: "<KEY_NAME>", env_value: "<USER_PROVIDED_KEY>" })
307
+
308
+ 4. **Now safe to switch** — call providers_manage create (for non-built-in)
309
+ or proceed directly to agents_manage update (for built-in). See the
310
+ "Built-in vs Custom Provider Check" and "Switching an Agent's Provider
311
+ or Model" sections below.
312
+
313
+ This rule applies UNCONDITIONALLY across ALL provider-switch paths:
314
+ - agents_manage update with new {provider, model}
315
+ - providers_manage create (the apiKeyName must reference a key already in env)
316
+ - gateway.patch agents.*.provider or agents.*.model
317
+ - providers_manage update changing apiKeyName
318
+
319
+ If the agent's primary will use a credential, you verify the credential exists FIRST. Skipping this check is the bug that causes "No API key found for <provider>" failures at the next chat turn — the user sees a generic "An error occurred" message and the bot silently breaks.
320
+
275
321
  ### Built-in vs Custom Provider Check (MANDATORY first step)
276
- Before creating a custom provider, check if the model already exists in the built-in catalog. Built-in providers (anthropic, google, openai, groq, mistral, deepseek, cerebras, xai, openrouter) already have their models registered — creating a redundant custom entry is wrong and will be ignored. Use models_manage({ action: "list" }) to see available built-in models.
322
+ Before creating a custom provider, check if the model already exists in the built-in catalog. Built-in providers (${_builtInProvidersList}) already have their models registered — creating a redundant custom entry is wrong and will be ignored. Call models_manage({ action: "list_providers" }) for the live native-catalog list, or models_manage({ action: "list" }) for available models. Prefer list_providers over the static list above when you need an up-to-date roster.
277
323
 
278
- If the model IS built-in: skip provider creation. Just store the API key (gateway env_set) and switch the agent directly.
324
+ If the model IS built-in: skip provider creation. After credential pre-check passes (above), go straight to agents_manage update with the new provider/model pair (the credential is already in env from pre-check step 3, so apiKeyName resolution succeeds at the next session).
279
325
  If the model is NOT built-in: you need a custom provider. Proceed to the steps below, but first gather ALL required configuration.
280
326
 
327
+ ### Choosing the \`type\` Field (POST AUTO-PROMOTE FLOW)
328
+ After Layer 1C of the catalog-driven providers redesign, the \`type\` field follows two distinct rules depending on the provider name:
329
+ - **If \`provider_id\` matches a built-in name** (use models_manage list_providers to verify): OMIT \`type\` entirely from the create config. The daemon auto-promotes \`type\` to the native catalog name when \`provider_id\` matches a native entry AND no custom \`baseUrl\` is supplied. Setting \`type:"openai"\` for a built-in name still works (auto-promoted), but omitting it is cleaner.
330
+ - **If \`provider_id\` is a custom OpenAI-compatible proxy** (NVIDIA NIM, Together, Fireworks, etc.) NOT in the native catalog: set \`type:"openai"\` (or whatever wire-format API matches). Auto-promotion does not fire for non-catalog names.
331
+ - **If \`baseUrl\` differs from the native catalog URL** for a built-in name: this signals you want the OpenAI-passthrough shape (custom proxy that masquerades as the built-in). Auto-promotion is suppressed; the entry stays as \`type:"openai"\`.
332
+
281
333
  ### Information Gathering for Custom Providers
282
334
  When creating a non-built-in provider, you MUST have: (1) the API base URL, (2) the exact model ID string, (3) the API protocol type. If the user did not supply all three:
283
335
  1. Use web_search to look up the provider's API documentation (search for "<provider name> API base URL" or "<provider name> API docs").
284
336
  2. If web search finds the information, use it to fill in the missing fields.
285
337
  3. If web search does NOT find the information, ask the user to supply the missing fields before proceeding. Do NOT guess or invent URLs.
286
338
 
287
- ### Credential Workflow
288
- API keys are NEVER stored in provider config. Always use this two-step process:
289
- 1. Store the API key: gateway({ action: "env_set", env_key: "<KEY_NAME>", env_value: "<key>" })
290
- 2. Create the provider: providers_manage({ action: "create", provider_id: "<name>", config: { type: "openai", baseUrl: "<url>", apiKeyName: "<KEY_NAME>", models: [{ id: "<model>" }] } })
339
+ ### Credential Workflow Summary
340
+ API keys are NEVER stored in provider config they live in env (set via Credential Pre-Check step 3 above) and are referenced by name via apiKeyName. The Credential Pre-Check above is the canonical entry point; this section just documents what gets stored where:
341
+ - Env (~/.comis/.env): the API key value, keyed by name (e.g. OPENROUTER_API_KEY=sk-or-v1-...)
342
+ - providers.entries.<id>.apiKeyName: the env NAME (not the value)
343
+ - SecretManager / setRuntimeApiKey: comis populates this from the env at daemon boot and on hot-reload after env_set; agents never set it directly.
291
344
 
292
- For local providers (Ollama, LM Studio, vLLM) that don't need API keys, omit apiKeyName.
345
+ For local providers (Ollama, LM Studio, vLLM) that don't need API keys, the Credential Pre-Check is skipped (step 1 noted local providers skip); just call providers_manage create with \`apiKeyName\` omitted.
293
346
 
294
347
  ### After Creating a Provider
295
348
  Switch an agent to use the new provider:
@@ -301,9 +354,10 @@ To switch an agent to a different provider/model, call agents_manage update with
301
354
 
302
355
  \`agents.*.model\` and \`agents.*.provider\` are listed in MUTABLE_CONFIG_OVERRIDES, so the immutability guard does not block the patch.
303
356
 
304
- **Two preconditions the LLM MUST verify before issuing the update:**
357
+ **Three preconditions the LLM MUST verify before issuing the update:**
305
358
  1. The target provider exists as a \`providers.entries.<provider_id>\` key. If it does not, call providers_manage create FIRST (and gateway env_set for the API key if needed). Patching an agent to a provider that has no entry resolves under the wrong provider family at the next session — the original bug.
306
359
  2. The model id matches a \`models[].id\` in that provider entry (or is a built-in known to the pi-ai catalog for that provider type). Otherwise \`registry.find(provider, model)\` returns undefined and the next session falls back with a "Model not found" message.
360
+ 3. **Credential pre-check passed** (see top of this guide). The target provider's apiKeyName is non-empty AND \`gateway env_list filter:"<PROVIDER>*"\` confirmed the named secret exists in env. Skipping this step is the bug that causes "No API key found" failures at the next chat turn — verified production repro on 2026-05-01.
307
361
 
308
362
  **Timing — the change is NOT hot-applied to the active session.**
309
363
  agents_manage update writes through persistToConfig WITHOUT a hot-update callback, which triggers a SIGUSR2 daemon restart (2-second debounce). The new provider/model takes effect on the next session, not the currently-running prompt. Tell the user the switch is queued and will take effect after the daemon settles.
@@ -4,6 +4,7 @@
4
4
  * and compacted output recovery.
5
5
  */
6
6
  import { TOOL_SUMMARIES, TOOL_ORDER } from "./tool-descriptions.js";
7
+ import { getProviders } from "@mariozechner/pi-ai";
7
8
  export function buildToolingSection(toolNames, _modelTier, toolSummaries) {
8
9
  if (toolNames.length === 0)
9
10
  return [];
@@ -360,7 +361,8 @@ export function buildPrivilegedToolsSection(toolNames, isMinimal, deferred) {
360
361
  "- **Reset vs delete session**: Reset clears messages but keeps the session identity (good for \"start fresh\"). Delete archives the transcript and removes the session entirely.",
361
362
  "- **Memory delete vs flush**: Delete removes specific entries by ID (surgical). Flush removes all entries for a scope (nuclear -- use with caution, requires approval).",
362
363
  "- **Token rotation**: Prefer rotate over revoke+create -- rotation is atomic and prevents downtime.",
363
- "- **Built-in first**: Before creating a custom provider, check if the model is already built-in (models_manage list). Built-in providers (anthropic, google, openai, groq, mistral, deepseek, cerebras, xai, openrouter) need only an API key — no custom provider entry. Only create a custom provider for models NOT in the built-in catalog.",
364
+ `- **Built-in first**: Before creating a custom provider, check if the model is already built-in (models_manage list). Built-in providers (${[...getProviders()].sort().join(", ")}) need only an API key — no custom provider entry. Only create a custom provider for models NOT in the built-in catalog.`,
365
+ `- **Discover providers at runtime**: Call \`models_manage({ action: "list_providers" })\` for the live native-catalog list — preferred over relying on the static text above when the SDK is upgraded.`,
364
366
  "- **Provider then agent**: When adding a custom (non-built-in) provider, first create the provider entry (providers_manage create), store the API key if needed (gateway env_set -- skip for keyless providers like Ollama), then switch the agent (agents_manage update). Never set an agent's model to a name that has no matching provider. If you lack the provider's base URL or model ID, use web_search to find it; if that fails, ask the user.",
365
367
  "- **Failover chain**: After creating multiple providers, configure automatic model failover on the agent (agents_manage update with modelFailover.fallbackModels). Each fallback entry is a {provider, modelId} pair referencing a configured provider. Failover order: primary > cache-aware retry > auth key rotation > fallback models in order. Never add a fallback model whose provider does not exist.",
366
368
  "- **Add vs replace fallback**: modelFailover.fallbackModels and authProfiles are REPLACED wholesale on update (scalar fields deep-merge; arrays do not). When the user says 'add' / 'also' / 'in addition', call agents_manage get FIRST to read the current array, append, then update with the full list. When the user says 'set' / 'use' / 'switch to', overwrite directly.",
@@ -49,6 +49,13 @@ export interface PiEventBridgeDeps {
49
49
  onDelta?: (delta: string) => void;
50
50
  /** Called when a safety control triggers -- PiExecutor uses this to call session.abort(). */
51
51
  onAbort?: () => void;
52
+ /** Called when a `rate_limited` error fires inside the SDK's auto-retry loop --
53
+ * PiExecutor wires this to `session.abortRetry()` to cancel the SDK's
54
+ * internal retry. Rate-limit windows are per-minute (longer than the SDK's
55
+ * ~30s retry budget), so retrying within the window cannot succeed.
56
+ * Non-`rate_limited` retryable errors (overloaded, network, 5xx) bypass this
57
+ * hook -- the SDK's normal retry-with-backoff proceeds. (260501-dkl) */
58
+ onAbortRetry?: () => void;
52
59
  /** SDK context usage accessor -- returns live context metrics from AgentSession. */
53
60
  getContextUsage?: () => ContextUsageData | undefined;
54
61
  /** Context window guard for percent-based warn/block checks. */
@@ -16,6 +16,7 @@ import { randomUUID } from "node:crypto";
16
16
  import { resolveModelPricing } from "../model/model-catalog.js";
17
17
  import { getCacheProviderInfo } from "../executor/cache-usage-helpers.js";
18
18
  import { sanitizeMcpToolNameForAnalytics } from "../executor/cache-break-detection.js";
19
+ import { classifyError } from "../executor/error-classifier.js";
19
20
  import { extractPlanFromResponse } from "../planner/plan-extractor.js";
20
21
  import { extractMcpServerName, classifyMcpErrorType, sanitizeToolArgs, extractErrorText } from "./bridge-event-handlers.js";
21
22
  import { createBridgeMetrics, buildBridgeResult } from "./bridge-metrics.js";
@@ -843,6 +844,31 @@ export function createPiEventBridge(deps) {
843
844
  break;
844
845
  }
845
846
  // -----------------------------------------------------------------
847
+ // SDK auto-retry loop: abort on rate_limited (260501-dkl)
848
+ // -----------------------------------------------------------------
849
+ case "auto_retry_start": {
850
+ const errorMessage = event.errorMessage ?? "";
851
+ const attempt = event.attempt;
852
+ const maxAttempts = event.maxAttempts;
853
+ const delayMs = event.delayMs;
854
+ const classification = classifyError(new Error(errorMessage));
855
+ if (classification.category === "rate_limited") {
856
+ deps.logger.info({
857
+ module: "agent.bridge.auto-retry-abort",
858
+ attempt,
859
+ maxAttempts,
860
+ delayMs,
861
+ errorMessage,
862
+ hint: "Rate-limit windows are per-minute; SDK retry budget cannot bridge the window -- aborting retry to surface terminal failure",
863
+ errorKind: "rate_limited",
864
+ }, "Aborting SDK auto-retry on rate-limited error");
865
+ deps.onAbortRetry?.();
866
+ }
867
+ // Non-rate_limited categories (overloaded, network, server_error, etc.)
868
+ // fall through -- let the SDK's normal retry-with-backoff proceed.
869
+ break;
870
+ }
871
+ // -----------------------------------------------------------------
846
872
  // Default: ignore unknown event types (future SDK events)
847
873
  // -----------------------------------------------------------------
848
874
  default:
@@ -20,6 +20,27 @@
20
20
  * guarantees the surrounding context changes turn-to-turn. So the latest's
21
21
  * signatures get invalidated too. Drop them all.
22
22
  *
23
+ * 260430-anthropic-400-thinking-block: the prior cache-fence skip
24
+ * (`if (i <= budget.cacheFenceIndex) preserve`) caused a per-execution
25
+ * regression. In iteration 1 of an execution the fence is -1 so all signed
26
+ * thinking blocks are stripped and the wire body establishes a cached
27
+ * prefix WITHOUT signatures. In subsequent iterations the fence becomes
28
+ * positive (= the breakpoint placed in iter 1) and the skip preserved
29
+ * messages 0…fence as-is. But `buildSessionContext()` reloads from on-disk
30
+ * JSONL where signatures are intact, so the wire body re-introduced signed
31
+ * thinking blocks at fence-protected positions that Anthropic had cached
32
+ * as unsigned. The cache-prefix validator detected the divergence and
33
+ * rejected with `400 invalid_request_error: ... blocks cannot be modified`.
34
+ *
35
+ * Fix: scrub uniformly across the array, regardless of cacheFenceIndex.
36
+ * The scrubber is pure/deterministic — input messages → same scrubbed
37
+ * output every time — so iter 1 strips, Anthropic caches the stripped
38
+ * prefix, iter 2 strips identically, and the cache hits. There is NO
39
+ * per-iteration cache penalty: the rebuild only happens once per session
40
+ * (when iter 1 first establishes the cached prefix). The cacheFenceIndex
41
+ * is read from the budget for diagnostic stats only and never gates
42
+ * stripping.
43
+ *
23
44
  * Provider coverage: NOT gated on `model.reasoning` because Gemini's
24
45
  * `thoughtSignature` lives on toolCall blocks even when the model itself
25
46
  * is not flagged as reasoning. Cost is one walk over assistant messages,
@@ -21,6 +21,27 @@
21
21
  * guarantees the surrounding context changes turn-to-turn. So the latest's
22
22
  * signatures get invalidated too. Drop them all.
23
23
  *
24
+ * 260430-anthropic-400-thinking-block: the prior cache-fence skip
25
+ * (`if (i <= budget.cacheFenceIndex) preserve`) caused a per-execution
26
+ * regression. In iteration 1 of an execution the fence is -1 so all signed
27
+ * thinking blocks are stripped and the wire body establishes a cached
28
+ * prefix WITHOUT signatures. In subsequent iterations the fence becomes
29
+ * positive (= the breakpoint placed in iter 1) and the skip preserved
30
+ * messages 0…fence as-is. But `buildSessionContext()` reloads from on-disk
31
+ * JSONL where signatures are intact, so the wire body re-introduced signed
32
+ * thinking blocks at fence-protected positions that Anthropic had cached
33
+ * as unsigned. The cache-prefix validator detected the divergence and
34
+ * rejected with `400 invalid_request_error: ... blocks cannot be modified`.
35
+ *
36
+ * Fix: scrub uniformly across the array, regardless of cacheFenceIndex.
37
+ * The scrubber is pure/deterministic — input messages → same scrubbed
38
+ * output every time — so iter 1 strips, Anthropic caches the stripped
39
+ * prefix, iter 2 strips identically, and the cache hits. There is NO
40
+ * per-iteration cache penalty: the rebuild only happens once per session
41
+ * (when iter 1 first establishes the cached prefix). The cacheFenceIndex
42
+ * is read from the budget for diagnostic stats only and never gates
43
+ * stripping.
44
+ *
24
45
  * Provider coverage: NOT gated on `model.reasoning` because Gemini's
25
46
  * `thoughtSignature` lives on toolCall blocks even when the model itself
26
47
  * is not flagged as reasoning. Cost is one walk over assistant messages,
@@ -43,7 +64,7 @@
43
64
  export function createSignatureReplayScrubber(deps) {
44
65
  return {
45
66
  name: "signature-replay-scrubber",
46
- async apply(messages, budget) {
67
+ async apply(messages, _budget) {
47
68
  if (messages.length === 0)
48
69
  return messages;
49
70
  // Find the latest assistant message index. If none, no scrub.
@@ -64,20 +85,19 @@ export function createSignatureReplayScrubber(deps) {
64
85
  for (let i = 0; i < messages.length; i++) {
65
86
  // eslint-disable-next-line security/detect-object-injection -- numeric index
66
87
  const original = messages[i];
67
- // Cache fence: messages at or below the fence must not be modified.
68
- if (i <= budget.cacheFenceIndex) {
69
- // eslint-disable-next-line security/detect-object-injection -- numeric index
70
- result[i] = original;
71
- continue;
72
- }
88
+ // 260430-anthropic-400-thinking-block: cacheFenceIndex is intentionally
89
+ // NOT consulted here. Stripping uniformly across the array keeps the
90
+ // scrubbed prefix identical across iterations of the same execution,
91
+ // which is what Anthropic's prompt-cache validator requires. See
92
+ // module docstring for the full rationale.
73
93
  const msg = original;
74
94
  if (msg.role !== "assistant" || !Array.isArray(msg.content)) {
75
95
  // eslint-disable-next-line security/detect-object-injection -- numeric index
76
96
  result[i] = original;
77
97
  continue;
78
98
  }
79
- // Assistant message past the fence — walk content blocks. Latest
80
- // included: cross-turn signature validation invalidates it too.
99
+ // Walk content blocks. Latest included: cross-turn signature
100
+ // validation invalidates it too.
81
101
  const content = msg.content;
82
102
  let messageChanged = false;
83
103
  const newContent = new Array(content.length);
@@ -21,8 +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
- * Cache fence respected exactly like `thinking-block-cleaner` and
25
- * `signature-replay-scrubber`.
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.
26
34
  *
27
35
  * Immutability: never mutates input; shallow-copies the block and the
28
36
  * containing message only when scrubbing is needed. When no scrub fires,
@@ -22,8 +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
- * Cache fence respected exactly like `thinking-block-cleaner` and
26
- * `signature-replay-scrubber`.
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.
27
35
  *
28
36
  * Immutability: never mutates input; shallow-copies the block and the
29
37
  * containing message only when scrubbing is needed. When no scrub fires,
@@ -50,7 +58,7 @@ const UNPAIRED_LOW_SURROGATE = /(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
50
58
  export function createSignatureSurrogateGuard(deps) {
51
59
  return {
52
60
  name: "signature-surrogate-guard",
53
- async apply(messages, budget) {
61
+ async apply(messages, _budget) {
54
62
  if (messages.length === 0) {
55
63
  deps?.onGuarded?.({ signaturesStripped: 0 });
56
64
  return messages;
@@ -61,12 +69,10 @@ export function createSignatureSurrogateGuard(deps) {
61
69
  for (let i = 0; i < messages.length; i++) {
62
70
  // eslint-disable-next-line security/detect-object-injection -- numeric index
63
71
  const original = messages[i];
64
- // Cache fence: messages at or below the fence must not be modified.
65
- if (i <= budget.cacheFenceIndex) {
66
- // eslint-disable-next-line security/detect-object-injection -- numeric index
67
- result[i] = original;
68
- continue;
69
- }
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.
70
76
  const msg = original;
71
77
  if (msg.role !== "assistant" || !Array.isArray(msg.content)) {
72
78
  // eslint-disable-next-line security/detect-object-injection -- numeric index
@@ -5,6 +5,18 @@
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.
19
+ *
8
20
  * Immutability: never mutates input messages or arrays. Returns new arrays and
9
21
  * shallow-copied messages only when changes are needed. When no changes are
10
22
  * required, returns the original array reference (zero allocation).
@@ -26,9 +38,12 @@ import type { ContextLayer } from "./types.js";
26
38
  */
27
39
  export declare function createThinkingBlockCleaner(keepTurns: number, onCleaned?: (stats: {
28
40
  blocksRemoved: number;
29
- /** Cache fence index when blocks were removed with fence active. */
41
+ /** 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). */
30
44
  cacheFenceIndex?: number;
31
- /** Number of messages protected by the cache fence. */
45
+ /** Number of messages protected by the cache fence. Always undefined now
46
+ * because the fence does not protect any messages from stripping. */
32
47
  messagesProtected?: number;
33
48
  /** Total messages in the conversation. */
34
49
  totalMessages?: number;
@@ -6,6 +6,18 @@
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.
20
+ *
9
21
  * Immutability: never mutates input messages or arrays. Returns new arrays and
10
22
  * shallow-copied messages only when changes are needed. When no changes are
11
23
  * required, returns the original array reference (zero allocation).
@@ -65,11 +77,10 @@ export function createThinkingBlockCleaner(keepTurns, onCleaned, getKeepTurnsOve
65
77
  let blocksRemoved = 0;
66
78
  const result = new Array(messages.length);
67
79
  for (let i = 0; i < messages.length; i++) {
68
- // Messages at or before the cache fence must not be modified
69
- if (i <= budget.cacheFenceIndex) {
70
- result[i] = messages[i];
71
- continue;
72
- }
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.
73
84
  const msg = messages[i];
74
85
  if (!oldAssistantIndices.has(i)) {
75
86
  // Within keep-window or not an assistant message -- pass through unchanged
@@ -103,13 +114,13 @@ export function createThinkingBlockCleaner(keepTurns, onCleaned, getKeepTurnsOve
103
114
  // If no changes were made to any message, return original array reference
104
115
  if (!anyChanged)
105
116
  return messages;
106
- // Report cleaning stats via callback
107
- // Include cache fence impact when blocks were skipped due to fence
117
+ // Report cleaning stats via callback. cacheFenceIndex is reported for
118
+ // diagnostic visibility but is no longer gating stripping. messagesProtected
119
+ // is intentionally omitted because no messages are fence-protected anymore.
108
120
  onCleaned?.({
109
121
  blocksRemoved,
110
122
  ...(budget.cacheFenceIndex >= 0 && blocksRemoved > 0 && {
111
123
  cacheFenceIndex: budget.cacheFenceIndex,
112
- messagesProtected: budget.cacheFenceIndex + 1,
113
124
  totalMessages: messages.length,
114
125
  }),
115
126
  });
@@ -363,6 +363,34 @@ export async function runPrompt(params) {
363
363
  // eslint-disable-next-line no-useless-assignment
364
364
  silentRetryAttempted = true;
365
365
  }
366
+ else if (earlyClassification.category === "rate_limited") {
367
+ // Provider-side time-based throttle (429/529). Retrying within the
368
+ // same runPrompt invocation cannot succeed — the rate-limit window
369
+ // hasn't rolled. The model-retry layer's cache-aware short retry
370
+ // (model-retry.ts:261-294) is the correct retry point for 429 with
371
+ // a parseable Retry-After header < SHORT_RETRY_THRESHOLD_MS. If we
372
+ // got here, that retry was either skipped (no Retry-After) or
373
+ // exhausted, AND the SDK didn't throw the 429 out (caught inside
374
+ // pi-ai's stream wrapper, surfaced as empty response). Re-entering
375
+ // runWithModelRetry from this layer would do another N retries that
376
+ // all hit the same rate-limit window — observed in production as
377
+ // 1 user message → 8 LLM calls (daemon.1.log:23:35:06-23:35:52,
378
+ // OpenRouter qwen/qwen3-coder:free 8 RPM cap). Short-circuit.
379
+ deps.logger.warn({
380
+ llmCalls: earlyBridgeResult.llmCalls,
381
+ finishReason: earlyBridgeResult.finishReason,
382
+ providerError: llmErrSource,
383
+ hint: "Provider returned a rate-limit error; retrying within the same window cannot succeed — surfacing terminal failure to caller",
384
+ errorKind: "rate_limited",
385
+ }, "Rate-limit error — skipping silent-retry and declaring terminal failure");
386
+ promptSucceeded = false;
387
+ const llmDetail = llmErrSource ? ` — ${llmErrSource}` : "";
388
+ promptError = new Error(`Rate limit exceeded: ${earlyBridgeResult.llmCalls} LLM call(s) produced empty response (finishReason: ${earlyBridgeResult.finishReason ?? "unknown"})${llmDetail}`);
389
+ // Defensive invariant: close the gate so a future refactor that
390
+ // re-enters this region cannot run a second silent-retry cycle.
391
+ // eslint-disable-next-line no-useless-assignment
392
+ silentRetryAttempted = true;
393
+ }
366
394
  else if (earlyClassification.category === "client_request") {
367
395
  // Plain client_request: deterministic failure (e.g. unprocessable_entity,
368
396
  // bare "cannot be modified" without signature noun). Retrying would
@@ -274,6 +274,9 @@ function summarizeToolCall(call) {
274
274
  case "gateway": {
275
275
  const action = typeof args.action === "string" ? args.action : undefined;
276
276
  const section = typeof args.section === "string" ? args.section : undefined;
277
+ const key = typeof args.key === "string" ? args.key : undefined;
278
+ if (action && section && key)
279
+ return `gateway({action: "${action}", section: "${section}", key: "${key}"})`;
277
280
  if (action && section)
278
281
  return `gateway({action: "${action}", section: "${section}"})`;
279
282
  if (action)
@@ -93,7 +93,9 @@ export async function assembleTools(params) {
93
93
  enabled: config.sdkRetry?.enabled ?? true,
94
94
  maxRetries: config.sdkRetry?.maxRetries ?? 5,
95
95
  baseDelayMs: config.sdkRetry?.baseDelayMs ?? 4000,
96
- maxDelayMs: config.sdkRetry?.maxDelayMs ?? 60000,
96
+ provider: {
97
+ maxRetryDelayMs: config.sdkRetry?.maxDelayMs ?? 60000,
98
+ },
97
99
  },
98
100
  };
99
101
  // Selective override: directive takes precedence over config
@@ -12,10 +12,26 @@ export declare function parsePhase(textSignature: unknown): string | undefined;
12
12
  /** True if a content block is user-visible text (not commentary). */
13
13
  export declare function isVisibleTextBlock(block: any): boolean;
14
14
  /**
15
- * Extract user-visible text from the last assistant message in a session.
15
+ * Extract user-visible text from the last "real" assistant message.
16
16
  *
17
- * When the last assistant message contains commentary-phase text blocks,
18
- * filters them out and returns only visible text. Otherwise delegates to
19
- * the SDK's getLastAssistantText() method.
17
+ * Filters non-real assistants from the tail walk:
18
+ * - aborted-empty (stopReason "aborted" + empty content) original.
19
+ * - error-empty (stopReason "error" + empty content) — sibling of
20
+ * aborted-empty, marks failed LLM calls (e.g. 429 / 5xx swallowed
21
+ * inside pi-ai's stream wrapper, surfaced as empty content).
22
+ * - synthetic-injected (model === "synthetic") — appended by
23
+ * orphaned-message-repair.ts to restore role alternation after a
24
+ * daemon restart; not user-visible LLM output.
25
+ * - cross-turn boundary (role === "user" encountered before a
26
+ * qualifying assistant) — return "" because the user message marks
27
+ * the start of the current execution window; assistants before it
28
+ * belong to prior turns (260501-gyy).
29
+ *
30
+ * When the resulting last assistant contains commentary-phase text
31
+ * blocks, drops them and returns only visible text. Otherwise returns
32
+ * the visible (non-commentary) text blocks of the last assistant
33
+ * directly — does NOT delegate to session.getLastAssistantText(),
34
+ * which walks past empty messages and would re-introduce the
35
+ * synthetic-leak (260501-egj).
20
36
  */
21
37
  export declare function getVisibleAssistantText(session: any): string;
@@ -28,28 +28,61 @@ export function isVisibleTextBlock(block) {
28
28
  parsePhase(block.textSignature) !== "commentary");
29
29
  }
30
30
  /**
31
- * Extract user-visible text from the last assistant message in a session.
31
+ * Extract user-visible text from the last "real" assistant message.
32
32
  *
33
- * When the last assistant message contains commentary-phase text blocks,
34
- * filters them out and returns only visible text. Otherwise delegates to
35
- * the SDK's getLastAssistantText() method.
33
+ * Filters non-real assistants from the tail walk:
34
+ * - aborted-empty (stopReason "aborted" + empty content) original.
35
+ * - error-empty (stopReason "error" + empty content) — sibling of
36
+ * aborted-empty, marks failed LLM calls (e.g. 429 / 5xx swallowed
37
+ * inside pi-ai's stream wrapper, surfaced as empty content).
38
+ * - synthetic-injected (model === "synthetic") — appended by
39
+ * orphaned-message-repair.ts to restore role alternation after a
40
+ * daemon restart; not user-visible LLM output.
41
+ * - cross-turn boundary (role === "user" encountered before a
42
+ * qualifying assistant) — return "" because the user message marks
43
+ * the start of the current execution window; assistants before it
44
+ * belong to prior turns (260501-gyy).
45
+ *
46
+ * When the resulting last assistant contains commentary-phase text
47
+ * blocks, drops them and returns only visible text. Otherwise returns
48
+ * the visible (non-commentary) text blocks of the last assistant
49
+ * directly — does NOT delegate to session.getLastAssistantText(),
50
+ * which walks past empty messages and would re-introduce the
51
+ * synthetic-leak (260501-egj).
36
52
  */
37
53
  export function getVisibleAssistantText(session) {
38
54
  const messages = session?.messages;
39
- // Find last non-aborted assistant message
40
- const lastAssistant = Array.isArray(messages)
41
- ? messages
42
- .slice()
43
- .reverse()
44
- .find((m) => {
45
- if (m.role !== "assistant")
46
- return false;
55
+ // Find last "real" assistant message in the CURRENT execution window —
56
+ // skip aborted-empty, error-empty, and synthetic-injected; stop at the
57
+ // first user message (turn boundary) to avoid leaking prior-turn text
58
+ // (260501-gyy).
59
+ const lastAssistant = (() => {
60
+ if (!Array.isArray(messages))
61
+ return undefined;
62
+ for (let i = messages.length - 1; i >= 0; i--) {
63
+ const m = messages[i]; // eslint-disable-line security/detect-object-injection
64
+ // Crossed turn boundary — assistants before this user message belong
65
+ // to a prior turn and must not be returned.
66
+ if (m?.role === "user")
67
+ return undefined;
68
+ // toolResult / tool / other roles — keep walking within current turn.
69
+ if (m?.role !== "assistant")
70
+ continue;
71
+ // Skip aborted-empty (existing behavior — preserved).
47
72
  if (m.stopReason === "aborted" && m.content?.length === 0)
48
- return false;
49
- return true;
50
- })
51
- : undefined;
52
- // Only activate phase filtering when commentary blocks are present
73
+ continue;
74
+ // Skip error-empty — failed LLM calls (e.g. 429 swallowed inside
75
+ // pi-ai's stream wrapper).
76
+ if (m.stopReason === "error" && m.content?.length === 0)
77
+ continue;
78
+ // Skip synthetic-injected — orphaned-message-repair scaffolding.
79
+ if (m.model === "synthetic")
80
+ continue;
81
+ return m;
82
+ }
83
+ return undefined;
84
+ })();
85
+ // Only activate phase filtering when commentary blocks are present.
53
86
  const hasCommentary = lastAssistant?.content?.some((b) => b?.type === "text" && parsePhase(b.textSignature) === "commentary") ?? false;
54
87
  if (hasCommentary) {
55
88
  return lastAssistant.content
@@ -57,7 +90,17 @@ export function getVisibleAssistantText(session) {
57
90
  .map((b) => b.text)
58
91
  .join("");
59
92
  }
60
- // No commentary — delegate to SDK method
61
- return session?.getLastAssistantText?.() ?? "";
93
+ // No commentary — return lastAssistant's visible text directly.
94
+ // Do NOT delegate to session.getLastAssistantText() because it walks
95
+ // past empty messages (aborted/error/etc.) and re-introduces the
96
+ // synthetic-leak (production bug 260501-egj: post-restart-resumption
97
+ // rate-limit returned synthetic placeholder instead of the
98
+ // 260501-cur "Rate limit exceeded" terminal error).
99
+ if (!lastAssistant?.content || !Array.isArray(lastAssistant.content))
100
+ return "";
101
+ return lastAssistant.content
102
+ .filter(isVisibleTextBlock)
103
+ .map((b) => b.text)
104
+ .join("");
62
105
  }
63
106
  /* eslint-enable @typescript-eslint/no-explicit-any */