chainlesschain 0.162.125 → 0.162.126

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 (190) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-DcqNhZhA.js → AIOps-BrTOcqIJ.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-BigtmPoq.js → ActionButton-na9cocmf.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-1J_X_HF4.js → Analytics-BtDygkpt.js} +2 -2
  5. package/src/assets/web-panel/assets/{AppLayout-DlcDG_a_.js → AppLayout-D_t5CS18.js} +5 -5
  6. package/src/assets/web-panel/assets/{Audit-Do-y8_3w.js → Audit-BYMZccCy.js} +1 -1
  7. package/src/assets/web-panel/assets/{Backup-S2Df-50Y.js → Backup-D2qDfAdL.js} +1 -1
  8. package/src/assets/web-panel/assets/{BaseInput-D5kgWJfk.js → BaseInput-kj456Ju9.js} +1 -1
  9. package/src/assets/web-panel/assets/{Chat-aUcp3kN1.js → Chat-BY0A0ZLE.js} +5 -5
  10. package/src/assets/web-panel/assets/ChatBubbleRenderer-D1VvrOPQ.js +1 -0
  11. package/src/assets/web-panel/assets/{Checkbox-DTg1U7Xs.js → Checkbox-DiBhtXsh.js} +1 -1
  12. package/src/assets/web-panel/assets/{Codegen-YO9ZZ4Ky.js → Codegen-B1ER0L0j.js} +1 -1
  13. package/src/assets/web-panel/assets/{Col-BHonE9fU.js → Col-CjLmza7D.js} +1 -1
  14. package/src/assets/web-panel/assets/{Community-DA2AlEFh.js → Community-BNId05U7.js} +1 -1
  15. package/src/assets/web-panel/assets/{Compact-DULxLRNg.js → Compact-BuuMj7K1.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compliance-BZqfuuZL.js → Compliance-DUuDycaJ.js} +1 -1
  17. package/src/assets/web-panel/assets/{Cowork-CJe3vyMS.js → Cowork-BKHiF6uL.js} +2 -2
  18. package/src/assets/web-panel/assets/{Cron-CeV8AtRu.js → Cron-C7p-lHnC.js} +2 -2
  19. package/src/assets/web-panel/assets/{Crosschain-CAg66zS5.js → Crosschain-BvSOx8a6.js} +1 -1
  20. package/src/assets/web-panel/assets/{DID-Dtup5JZm.js → DID-D1k9jgHv.js} +2 -2
  21. package/src/assets/web-panel/assets/{Dashboard-DAR_8PNy.js → Dashboard-B9mCCnqY.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dropdown-K5bTaCYN.js → Dropdown-ge9UHSXF.js} +1 -1
  23. package/src/assets/web-panel/assets/{EmailListRenderer-C890uErx.js → EmailListRenderer-TZO7ppXi.js} +1 -1
  24. package/src/assets/web-panel/assets/{FamilyGuardDashboard-B-Tj_8yM.js → FamilyGuardDashboard-DkhLJmzx.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-C6WZ98ni.js → Federation-JLuA_8zp.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-D-LTfGWd.js → FormItemContext-C4w-rzNg.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-C80DK4W7.js → GenericCardRenderer-Btns4sV9.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-Cjvvv3zW.js → Git-BheXMjzC.js} +2 -2
  29. package/src/assets/web-panel/assets/{Governance-C0BND77H.js → Governance-D9rLlatH.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-BL9kTtSu.js → Inference-DiklIMcd.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-CnVTBmJf.js → KnowledgeGraph-C70EWL03.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-CbCbg7K6.js → Logs-Zt_MLI10.js} +2 -2
  33. package/src/assets/web-panel/assets/{Marketplace-CG5On-fH.js → Marketplace-suQxES99.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-AYYDZZK7.js → McpTools-CclpCDpY.js} +5 -5
  35. package/src/assets/web-panel/assets/{Memory-tMWbMDQq.js → Memory-B2lHOUwF.js} +2 -2
  36. package/src/assets/web-panel/assets/{MobileBridge-BAOsf4wB.js → MobileBridge-BMXKoDAD.js} +1 -1
  37. package/src/assets/web-panel/assets/{MobileProjects-BT-2komQ.js → MobileProjects-a6mWVdBV.js} +1 -1
  38. package/src/assets/web-panel/assets/{Mtc-BFwfC63n.js → Mtc-BFpM4Fkj.js} +5 -5
  39. package/src/assets/web-panel/assets/{MtcAudit-DYG3CWdH.js → MtcAudit-DDQkxkbS.js} +2 -2
  40. package/src/assets/web-panel/assets/{Multisig-KJwd0yWT.js → Multisig-Crkd_zKQ.js} +3 -3
  41. package/src/assets/web-panel/assets/{NLProgramming-BmL5dNWm.js → NLProgramming-Dpk5An7B.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-DmkaVaMc.js → Notes-BrFnOMNz.js} +3 -3
  43. package/src/assets/web-panel/assets/{NotificationSettings-tmVQszDZ.js → NotificationSettings-CM0iApFM.js} +1 -1
  44. package/src/assets/web-panel/assets/{OrderTableRenderer-BtrR7anf.js → OrderTableRenderer-XXjeqkdz.js} +1 -1
  45. package/src/assets/web-panel/assets/{Organization-pppktQyW.js → Organization-CHFW_38T.js} +4 -4
  46. package/src/assets/web-panel/assets/{Overflow-D3lBoQDA.js → Overflow-C-vl3EjV.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-BgE2qGlZ.js → P2P-BT7Slavq.js} +2 -2
  48. package/src/assets/web-panel/assets/PdhVaultBrowser-DRUaULap.js +7 -0
  49. package/src/assets/web-panel/assets/{Permissions-BR8no2Xe.js → Permissions-DkhOAvMz.js} +3 -3
  50. package/src/assets/web-panel/assets/{PersonalDataHub-DabbyQEF.js → PersonalDataHub-Dl7dSOvV.js} +2 -2
  51. package/src/assets/web-panel/assets/{Pipeline-n4sNpEz8.js → Pipeline-B3nVI4p6.js} +1 -1
  52. package/src/assets/web-panel/assets/{Privacy-CGNp4n8M.js → Privacy-Dv1SXx1Q.js} +1 -1
  53. package/src/assets/web-panel/assets/{ProjectInit-CAVA5VsX.js → ProjectInit-DrAhVi5p.js} +2 -2
  54. package/src/assets/web-panel/assets/{ProjectSettings-GCgkfM7A.js → ProjectSettings-h-ggCYN_.js} +2 -2
  55. package/src/assets/web-panel/assets/{Projects-BYK17p52.js → Projects-CM91hYdr.js} +1 -1
  56. package/src/assets/web-panel/assets/{Providers-DpUT5W-d.js → Providers-rfkZel3N.js} +1 -1
  57. package/src/assets/web-panel/assets/{QuickAsk-ZJxTb7vi.js → QuickAsk-XepZB7h_.js} +1 -1
  58. package/src/assets/web-panel/assets/{Recommend-CIzFRDk1.js → Recommend-DzQWsh1a.js} +1 -1
  59. package/src/assets/web-panel/assets/{Reputation-lQ_WDNZn.js → Reputation-CDeaal0j.js} +1 -1
  60. package/src/assets/web-panel/assets/{Row-1MEtrgtF.js → Row-BN4aKhFH.js} +1 -1
  61. package/src/assets/web-panel/assets/{RssFeed-SSgcPPl4.js → RssFeed-INP6VYAA.js} +2 -2
  62. package/src/assets/web-panel/assets/{Search-CcTMOGNY.js → Search-ChPbwatu.js} +1 -1
  63. package/src/assets/web-panel/assets/{Security-BA1KMaDx.js → Security-C97ZCY8N.js} +3 -3
  64. package/src/assets/web-panel/assets/{Services-_aj7czZn.js → Services-Cbtl2Ajs.js} +2 -2
  65. package/src/assets/web-panel/assets/{Skeleton-BWgg_0Zr.js → Skeleton-B13sFxBQ.js} +1 -1
  66. package/src/assets/web-panel/assets/{Skills-C2ZIziYM.js → Skills-vI9zSIKX.js} +1 -1
  67. package/src/assets/web-panel/assets/{Sla-CYxp4axY.js → Sla-Cr3oBH39.js} +1 -1
  68. package/src/assets/web-panel/assets/{SpeechSettings-C7Csp1jV.js → SpeechSettings-BwemqPPV.js} +1 -1
  69. package/src/assets/web-panel/assets/{SyncSettings-svGeswiw.js → SyncSettings-CLA78P-Z.js} +2 -2
  70. package/src/assets/web-panel/assets/{Tasks-ClISj6oK.js → Tasks-DBjQ_DOM.js} +1 -1
  71. package/src/assets/web-panel/assets/{Templates-CmO-Fp7r.js → Templates-BkSLDkBs.js} +1 -1
  72. package/src/assets/web-panel/assets/{Tenant-a3Ac3lxz.js → Tenant-Dn0nSlib.js} +1 -1
  73. package/src/assets/web-panel/assets/{Terminal-DyJIRh81.js → Terminal-C8f4boru.js} +2 -2
  74. package/src/assets/web-panel/assets/{TimelineRenderer-acXS5N5h.js → TimelineRenderer-DW_rMtJR.js} +1 -1
  75. package/src/assets/web-panel/assets/{Tokens-DJW0KqV4.js → Tokens-BdffrKYV.js} +1 -1
  76. package/src/assets/web-panel/assets/{Trigger-0HeTi4r6.js → Trigger-CX4CUg-Q.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trust-aFym34eo.js → Trust-Cq9Bl7Od.js} +1 -1
  78. package/src/assets/web-panel/assets/{UkeySign-CYhszHJv.js → UkeySign-BQy18_VY.js} +1 -1
  79. package/src/assets/web-panel/assets/{VideoEditing-BbJxPF9X.js → VideoEditing-h5hzK7Vw.js} +1 -1
  80. package/src/assets/web-panel/assets/{Wallet-DDthEwiu.js → Wallet-BLotT3gw.js} +2 -2
  81. package/src/assets/web-panel/assets/{WebAuthn-DQ6McYPg.js → WebAuthn-8cCANYLE.js} +2 -2
  82. package/src/assets/web-panel/assets/{WorkflowEditor-CnF8zRsi.js → WorkflowEditor-Db6NwtAv.js} +1 -1
  83. package/src/assets/web-panel/assets/{chat-Dzkx2gTP.js → chat-BPk1LbpL.js} +1 -1
  84. package/src/assets/web-panel/assets/{colors-35T51Oo5.js → colors-BnNal71p.js} +1 -1
  85. package/src/assets/web-panel/assets/{compact-item-TtzNrlu1.js → compact-item-W2VC0tcy.js} +1 -1
  86. package/src/assets/web-panel/assets/{createContext-Y1LkWrYb.js → createContext-DceVsgnZ.js} +1 -1
  87. package/src/assets/web-panel/assets/devWarning--rwFDBhx.js +1 -0
  88. package/src/assets/web-panel/assets/{hasIn-BOYw1BgS.js → hasIn-BEq6EDcp.js} +1 -1
  89. package/src/assets/web-panel/assets/{index-BtlJWaSP.js → index-5gIBUFBn.js} +1 -1
  90. package/src/assets/web-panel/assets/{index-CLqC2sRT.js → index-APCHxOkM.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-C7Wt5feN.js → index-B2dMhwZi.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-CqhwG1ox.js → index-B8cPjrEH.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-BxWUEFKy.js → index-BDC5HsxZ.js} +1 -1
  94. package/src/assets/web-panel/assets/index-BPFuPGKi.js +1 -0
  95. package/src/assets/web-panel/assets/{index-NM9Oke2k.js → index-BUkDcIwJ.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BWgNn9As.js → index-BWcCyH6-.js} +3 -3
  97. package/src/assets/web-panel/assets/{index-CVrUs6rf.js → index-BgZikQoh.js} +1 -1
  98. package/src/assets/web-panel/assets/index-Bwjus13Y.js +1 -0
  99. package/src/assets/web-panel/assets/{index-D6tG4B25.js → index-BzSzRWsw.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DjeUZxE5.js → index-C1K9CsGr.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DmkG479D.js → index-CBZVISK6.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Bw1iOGhM.js → index-CEJN-R2L.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-G4ClSmJc.js → index-CG5dGC9E.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-D47robkX.js → index-CMGLpstm.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BKRlWHUp.js → index-CWrGCndd.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DhMSOd_2.js → index-CbcvfpCV.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-Dx0hIfC-.js → index-Ceqv4lI2.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CdyFg4FI.js → index-CsT6frT8.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-B7aKAZzS.js → index-D8PQKsDT.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-8jffdb6b.js → index-DGncdA-j.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-XIuZV9by.js → index-DHanQHO0.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Dhfw-BXs.js → index-DaUBk28E.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-hyqEKkBT.js → index-Di2J4b4V.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DAizH273.js → index-DtuLvWZN.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CPGNc2tF.js → index-DvS1J8xc.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-KgXrlfNF.js → index-DxePJdwP.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DFhlelzN.js → index-Et4XvL49.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-zF77jnI2.js → index-GlFxgcj4.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-Cs82BHAj.js → index-HV119Oui.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Dof7lWA_.js → index-InGW87pj.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-Bs69SI96.js → index-P1-s8JR7.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-D4BgCBa3.js → index-SdABCNoi.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BSpFe6j5.js → index-WTAZbN-c.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-D7dCBw_M.js → index-_2I-KzOj.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BGPEaeB1.js → index-m1sVaWVU.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CZ16GlOs.js → index-s37wwyeN.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-D2od7Bgx.js → index-tyWABqp9.js} +1 -1
  128. package/src/assets/web-panel/assets/{initDefaultProps-DD5y5msp.js → initDefaultProps-DzrCDb3j.js} +1 -1
  129. package/src/assets/web-panel/assets/{motion-BuoutMia.js → motion-BQERVnE3.js} +1 -1
  130. package/src/assets/web-panel/assets/{move-DkpjqOXg.js → move-I9ACA5XJ.js} +1 -1
  131. package/src/assets/web-panel/assets/{mtc-parser-BmGJD-v-.js → mtc-parser-Dd2Pw-tr.js} +1 -1
  132. package/src/assets/web-panel/assets/{omit-DftUE0Sz.js → omit-DwzYRzWV.js} +1 -1
  133. package/src/assets/web-panel/assets/{pickAttrs-DDRb9FIu.js → pickAttrs-BSGULyIL.js} +1 -1
  134. package/src/assets/web-panel/assets/{placementArrow-DPgtxOOD.js → placementArrow-tSYYjAts.js} +1 -1
  135. package/src/assets/web-panel/assets/{responsiveObserve-BQFRzFeZ.js → responsiveObserve-Dy2lqikT.js} +1 -1
  136. package/src/assets/web-panel/assets/{slide-DQ4lmUSz.js → slide-RaB4Uq0c.js} +1 -1
  137. package/src/assets/web-panel/assets/{statusUtils-C1TR4ZiQ.js → statusUtils-CBpC_wZg.js} +1 -1
  138. package/src/assets/web-panel/assets/{styleChecker-B-Suj978.js → styleChecker-BEKwPWt5.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFlexGapSupport-Q7bDZ3EI.js → useFlexGapSupport-C4CnYJH1.js} +1 -1
  140. package/src/assets/web-panel/assets/{useFs-DHjRLyQH.js → useFs-n24jutw5.js} +1 -1
  141. package/src/assets/web-panel/assets/{usePersonalDataHub-lQOvCvCr.js → usePersonalDataHub-DMgS8F6G.js} +1 -1
  142. package/src/assets/web-panel/assets/{vnode-Ie0g4-p3.js → vnode-BSp2KYcj.js} +1 -1
  143. package/src/assets/web-panel/assets/{zoom-CHP0YEoH.js → zoom-7UfQtTPh.js} +1 -1
  144. package/src/assets/web-panel/index.html +1 -1
  145. package/src/commands/agent.js +41 -2
  146. package/src/commands/compliance.js +3 -1
  147. package/src/commands/review.js +47 -17
  148. package/src/commands/session.js +11 -2
  149. package/src/gateways/ws/message-dispatcher.js +145 -165
  150. package/src/gateways/ws/ws-session-gateway.js +39 -5
  151. package/src/harness/jsonl-session-store.js +39 -12
  152. package/src/harness/worktree-isolator.js +5 -0
  153. package/src/lib/agent-core.js +1 -0
  154. package/src/lib/audit-logger.js +3 -1
  155. package/src/lib/chat-core.js +5 -2
  156. package/src/lib/chat-intent-service.js +68 -21
  157. package/src/lib/config-manager.js +25 -1
  158. package/src/lib/credential-guard.js +43 -1
  159. package/src/lib/git-integration.js +5 -0
  160. package/src/lib/interaction-adapter.js +32 -11
  161. package/src/lib/jsonl-session-store.js +1 -0
  162. package/src/lib/llm-config-defaults.js +37 -0
  163. package/src/lib/mcp-oauth.js +76 -10
  164. package/src/lib/permission-engine.js +4 -1
  165. package/src/lib/pipeline-orchestrator.js +20 -2
  166. package/src/lib/project-instructions.js +13 -0
  167. package/src/lib/repl-denials.js +137 -0
  168. package/src/lib/safe-json.js +22 -0
  169. package/src/lib/sandbox-v2.js +7 -6
  170. package/src/lib/search-command.js +65 -0
  171. package/src/lib/settings-hooks.cjs +133 -0
  172. package/src/lib/settings-loader.cjs +16 -7
  173. package/src/lib/social-graph.js +3 -1
  174. package/src/lib/stream-retry.js +27 -0
  175. package/src/lib/turn-context.js +15 -5
  176. package/src/repl/agent-repl.js +81 -8
  177. package/src/runtime/__tests__/message-roles.test.js +36 -3
  178. package/src/runtime/agent-core.js +175 -50
  179. package/src/runtime/coding-agent-contract-shared.cjs +9 -2
  180. package/src/runtime/coding-agent-shell-policy.cjs +23 -2
  181. package/src/runtime/file-ref-expander.js +51 -7
  182. package/src/runtime/headless-runner.js +83 -2
  183. package/src/runtime/headless-stream.js +69 -3
  184. package/src/runtime/mcp-config.js +42 -1
  185. package/src/runtime/message-roles.js +14 -1
  186. package/src/assets/web-panel/assets/ChatBubbleRenderer-Pg7dIsiE.js +0 -1
  187. package/src/assets/web-panel/assets/PdhVaultBrowser-D26Bz5gS.js +0 -7
  188. package/src/assets/web-panel/assets/devWarning-D_lwLZ6O.js +0 -1
  189. package/src/assets/web-panel/assets/index-CEwuCM44.js +0 -1
  190. package/src/assets/web-panel/assets/index-DzzgU4IU.js +0 -1
@@ -22,6 +22,7 @@
22
22
  */
23
23
 
24
24
  import { EventEmitter } from "events";
25
+ import { safeJsonParse } from "./safe-json.js";
25
26
 
26
27
  /* ── Edge types ────────────────────────────────────────────── */
27
28
 
@@ -325,7 +326,8 @@ export function loadFromDb(db) {
325
326
  )
326
327
  .all();
327
328
  for (const row of rows) {
328
- const metadata = row.metadata ? JSON.parse(row.metadata) : null;
329
+ // A single corrupt metadata cell must not crash the whole graph load.
330
+ const metadata = safeJsonParse(row.metadata, null);
329
331
  // Hydrate without re-emitting events (bulk load).
330
332
  _hydrateEdge({
331
333
  sourceDid: row.source_did,
@@ -18,6 +18,33 @@ import { isAbortError } from "./abort-utils.js";
18
18
  export const STREAM_RETRY_MAX = 2;
19
19
  export const STREAM_RETRY_BASE_MS = 400;
20
20
 
21
+ /**
22
+ * Hard ceiling on the *configurable* retry budget (Claude-Code 2.1.186:
23
+ * "CLAUDE_CODE_MAX_RETRIES capped at 15") — keeps a stray huge value from
24
+ * turning a dead endpoint into a multi-minute exponential-backoff hang.
25
+ */
26
+ export const STREAM_RETRY_MAX_CAP = 15;
27
+
28
+ /**
29
+ * Resolve the streaming-retry budget. Users can raise (or lower) the default
30
+ * via `CC_MAX_RETRIES` (native) or `CLAUDE_CODE_MAX_RETRIES` (Claude-Code
31
+ * parity); the value is clamped to `[0, STREAM_RETRY_MAX_CAP]`. Unset / blank /
32
+ * non-numeric falls back to the default `STREAM_RETRY_MAX`; a negative value
33
+ * clamps to 0 (retries disabled). `CC_MAX_RETRIES` wins when both are set.
34
+ *
35
+ * @param {Record<string,string|undefined>} [env=process.env]
36
+ * @returns {number}
37
+ */
38
+ export function resolveStreamRetryMax(env = process.env) {
39
+ const raw = env.CC_MAX_RETRIES ?? env.CLAUDE_CODE_MAX_RETRIES;
40
+ if (raw == null || String(raw).trim() === "") return STREAM_RETRY_MAX;
41
+ const n = Number.parseInt(String(raw).trim(), 10);
42
+ if (!Number.isFinite(n)) return STREAM_RETRY_MAX;
43
+ if (n < 0) return 0;
44
+ if (n > STREAM_RETRY_MAX_CAP) return STREAM_RETRY_MAX_CAP;
45
+ return n;
46
+ }
47
+
21
48
  /**
22
49
  * Is this error from a streaming chat request a transient API CONNECTION drop
23
50
  * that is safe to retry? True only for genuine network failures (reset /
@@ -30,6 +30,9 @@ function _git(cmd, cwd) {
30
30
  encoding: "utf-8",
31
31
  stdio: ["ignore", "pipe", "ignore"],
32
32
  timeout: 1500,
33
+ // `status --porcelain` on a dirty repo with many files can exceed the
34
+ // 1 MB execSync default → ENOBUFS → null → "(clean)" misreport below.
35
+ maxBuffer: 16 * 1024 * 1024,
33
36
  })
34
37
  .trim();
35
38
  } catch (_e) {
@@ -61,11 +64,18 @@ export function buildTurnContext({
61
64
  if (branch) {
62
65
  const head = _git("rev-parse --short HEAD", cwd);
63
66
  const status = _git("status --porcelain", cwd);
64
- const dirty = status && status.length > 0;
65
- const fileCount = dirty ? status.split("\n").filter(Boolean).length : 0;
66
- lines.push(
67
- `- git: ${branch}@${head || "?"}${dirty ? ` (${fileCount} uncommitted)` : " (clean)"}`,
68
- );
67
+ // Distinguish "command failed" (null timeout / huge output) from "" (truly
68
+ // clean): the old `status && …` reported a repo as "(clean)" whenever status
69
+ // couldn't be determined, which is a wrong signal to the model.
70
+ let stateLabel;
71
+ if (status === null) {
72
+ stateLabel = " (status unknown)";
73
+ } else if (status.length > 0) {
74
+ stateLabel = ` (${status.split("\n").filter(Boolean).length} uncommitted)`;
75
+ } else {
76
+ stateLabel = " (clean)";
77
+ }
78
+ lines.push(`- git: ${branch}@${head || "?"}${stateLabel}`);
69
79
  }
70
80
 
71
81
  if (sessionId) {
@@ -29,6 +29,11 @@ import {
29
29
  analyzeContinuation,
30
30
  joinContinuation,
31
31
  } from "../lib/repl-multiline.js";
32
+ import {
33
+ classifyDenial,
34
+ recordDenial,
35
+ formatDenials,
36
+ } from "../lib/repl-denials.js";
32
37
  import { bootstrap, shutdown } from "../runtime/bootstrap.js";
33
38
  import {
34
39
  createSession,
@@ -118,6 +123,14 @@ let _settingsHooks = null;
118
123
  // `!command` auto-triggers an assistant response to its output. undefined =
119
124
  // unset → defaults OFF (opt-in) in shouldRespondToBashCommands.
120
125
  let _respondToBash;
126
+ // .claude/settings.json `autoMode.classifyAllShell` (Claude-Code 2.1.193): route
127
+ // the built-in verification allowlist through the shell-policy classifier (→
128
+ // ApprovalGate confirm) instead of fast-pathing it. false = unset → off.
129
+ let _classifyAllShell = false;
130
+ // Bounded log of tool calls the agent was BLOCKED from running this session
131
+ // (shell-policy / ApprovalGate / settings rule / hook). Surfaced by
132
+ // `/permissions denials` (Claude-Code 2.1.193 "recent denials"). In-memory only.
133
+ const _recentDenials = [];
121
134
 
122
135
  /**
123
136
  * Fire settings.json Notification hooks (observe-only) — the agent needs the
@@ -180,13 +193,17 @@ async function executeTool(name, args) {
180
193
  permissionRules: _permissionRules,
181
194
  permissionConfirm: _permissionConfirm,
182
195
  settingsHooks: _settingsHooks,
196
+ classifyAllShell: _classifyAllShell,
183
197
  });
184
198
  }
185
199
 
186
200
  /**
187
201
  * Agentic loop — wraps agent-core's async generator with REPL display output.
202
+ * Exported so its event-translation contract (checkpoint-mark accuracy for
203
+ * `/rewind`, content/usage extraction, forced-off in-loop compaction) is
204
+ * unit-testable via the `options._coreLoop` injection seam.
188
205
  */
189
- async function agentLoop(messages, options) {
206
+ export async function agentLoop(messages, options) {
190
207
  // Resume-degenerate role merge (Claude Code 2.1.187 parity), gated by the
191
208
  // one-shot `mergeRoles` flag so it fires only on the first model call after
192
209
  // resuming a session whose prior run produced no assistant response. Collapse
@@ -212,12 +229,19 @@ async function agentLoop(messages, options) {
212
229
  `\n ⚠️ ${info.message || `已从 "${info.from}" 切换到 "${info.to}"`}\n`,
213
230
  ),
214
231
  ));
215
- // The REPL runs its own auto-compaction (after each turn, with metrics +
216
- // persisted compact events), so opt out of the agent loop's in-loop
217
- // compaction to avoid compacting the same history twice.
218
- for await (const event of coreAgentLoop(messages, {
219
- autoCompact: false,
232
+ // `_coreLoop` is an injectable seam (defaults to agent-core's loop) so the
233
+ // wrapper's event translation can be unit-tested without a live model.
234
+ const runCoreLoop = options._coreLoop || coreAgentLoop;
235
+ // The tool-result event carries no args; remember the last tool-executing
236
+ // pair so a recorded denial can show what was attempted (/permissions denials).
237
+ let _lastExec = null;
238
+ for await (const event of runCoreLoop(messages, {
220
239
  ...options,
240
+ // FORCE in-loop compaction off — the REPL runs its OWN post-turn compaction
241
+ // (with metrics + persisted compact events), so letting agent-core compact
242
+ // too would trim the same history twice. Placed AFTER the spread so a
243
+ // caller's options.autoCompact can never silently re-enable the double pass.
244
+ autoCompact: false,
221
245
  onProviderFallback,
222
246
  })) {
223
247
  if (event.type === "checkpoint") {
@@ -236,6 +260,7 @@ async function agentLoop(messages, options) {
236
260
  chalk.gray(` ⎌ checkpoint ${event.id} (before ${event.tool})\n`),
237
261
  );
238
262
  } else if (event.type === "tool-executing") {
263
+ _lastExec = { tool: event.tool, args: event.args };
239
264
  process.stdout.write(
240
265
  chalk.gray(
241
266
  ` [${event.tool}] ${formatToolArgs(event.tool, event.args)}\n`,
@@ -246,6 +271,24 @@ async function agentLoop(messages, options) {
246
271
  process.stdout.write(
247
272
  chalk.red(` Error: ${event.error || event.result?.error}\n`),
248
273
  );
274
+ // Record policy denials (not plain tool failures) into the caller's
275
+ // denial log for review via `/permissions denials` (Claude-Code 2.1.193
276
+ // recent denials). Caller passes the session log (mirrors checkpointMarks)
277
+ // so the wrapper stays unit-testable.
278
+ if (Array.isArray(options.denialLog)) {
279
+ const denial = classifyDenial({
280
+ tool: event.tool,
281
+ result: event.result,
282
+ error: event.error,
283
+ argsSummary:
284
+ _lastExec && _lastExec.tool === event.tool
285
+ ? formatToolArgs(event.tool, _lastExec.args)
286
+ : "",
287
+ });
288
+ if (denial) {
289
+ recordDenial(options.denialLog, { ...denial, at: Date.now() });
290
+ }
291
+ }
249
292
  // Parity with Desktop AIChatPage's `Switch to Trusted` button:
250
293
  // when the deny came from ApprovalGate (not shell-policy), surface
251
294
  // the exact CLI command the user can run to relax the per-session
@@ -503,6 +546,11 @@ export async function startAgentRepl(options = {}) {
503
546
  _respondToBash = readBooleanSetting("respondToBashCommands", {
504
547
  cwd: process.cwd(),
505
548
  });
549
+ // Claude-Code 2.1.193 autoMode.classifyAllShell (default OFF when unset).
550
+ _classifyAllShell =
551
+ readBooleanSetting("autoMode.classifyAllShell", {
552
+ cwd: process.cwd(),
553
+ }) === true;
506
554
  const total =
507
555
  loaded.rules.allow.length +
508
556
  loaded.rules.ask.length +
@@ -538,12 +586,21 @@ export async function startAgentRepl(options = {}) {
538
586
  // PostToolUse). The interactive _permissionConfirm above doubles as the
539
587
  // confirmer for a hook `ask` decision.
540
588
  try {
541
- const { loadHooks } = await import("../lib/settings-hooks.cjs");
589
+ const { loadHooks, projectHookTrustNotice } =
590
+ await import("../lib/settings-hooks.cjs");
542
591
  const loaded = loadHooks({ cwd: process.cwd() });
543
592
  _settingsHooks =
544
593
  loaded.hooks && Object.keys(loaded.hooks).length > 0
545
594
  ? loaded.hooks
546
595
  : null;
596
+ // First-run trust notice for an untrusted/cloned repo's shell-running
597
+ // hooks (Claude-Code 2.1.195 parity). Best-effort, stderr-only.
598
+ try {
599
+ const notice = projectHookTrustNotice({ cwd: process.cwd() });
600
+ if (notice) process.stderr.write(notice + "\n");
601
+ } catch {
602
+ /* trust notice is best-effort */
603
+ }
547
604
  } catch (_err) {
548
605
  _settingsHooks = null;
549
606
  }
@@ -1482,7 +1539,7 @@ export async function startAgentRepl(options = {}) {
1482
1539
  ` ${chalk.cyan("/cost")} Session token spend + estimated $ (per model & category)`,
1483
1540
  );
1484
1541
  logger.log(
1485
- ` ${chalk.cyan("/permissions")} Allow/ask/deny rules; set/cycle tier (/permissions <tier> · Shift+Tab cycles)`,
1542
+ ` ${chalk.cyan("/permissions")} Allow/ask/deny rules; set/cycle tier (/permissions <tier> · Shift+Tab); /permissions denials to review blocked calls`,
1486
1543
  );
1487
1544
  logger.log(
1488
1545
  ` ${chalk.cyan("/export")} Save this conversation to a Markdown file (/export [path])`,
@@ -3034,6 +3091,13 @@ export async function startAgentRepl(options = {}) {
3034
3091
  // parity): what the agent runs unprompted, asks about, or is blocked from.
3035
3092
  if (trimmed === "/permissions" || trimmed.startsWith("/permissions ")) {
3036
3093
  const arg = trimmed.slice("/permissions".length).trim();
3094
+ if (arg === "denials" || arg === "denied") {
3095
+ // Review what the agent was BLOCKED from running this session
3096
+ // (Claude-Code 2.1.193 "recent denials").
3097
+ logger.log(formatDenials(_recentDenials));
3098
+ prompt();
3099
+ return;
3100
+ }
3037
3101
  if (arg) {
3038
3102
  // Set this session's approval tier mid-session (Claude-Code
3039
3103
  // permission-mode / Shift+Tab parity; mirrors `cc session policy --set`).
@@ -3079,6 +3143,13 @@ export async function startAgentRepl(options = {}) {
3079
3143
  " Set tier mid-session: /permissions <strict|trusted|autopilot>",
3080
3144
  ),
3081
3145
  );
3146
+ if (_recentDenials.length) {
3147
+ logger.log(
3148
+ chalk.gray(
3149
+ ` ${_recentDenials.length} tool call(s) denied this session — /permissions denials to review`,
3150
+ ),
3151
+ );
3152
+ }
3082
3153
  prompt();
3083
3154
  return;
3084
3155
  }
@@ -3511,11 +3582,13 @@ export async function startAgentRepl(options = {}) {
3511
3582
  autoCheckpoint,
3512
3583
  checkpointSession: sessionId,
3513
3584
  checkpointMarks: _checkpointMarks,
3585
+ denialLog: _recentDenials,
3514
3586
  prepareCall,
3515
3587
  approvalGate: _approvalGate,
3516
3588
  permissionRules: _permissionRules,
3517
3589
  permissionConfirm: _permissionConfirm,
3518
3590
  settingsHooks: _settingsHooks,
3591
+ classifyAllShell: _classifyAllShell,
3519
3592
  // Interactive session: gate run_code through the ApprovalGate (like
3520
3593
  // run_shell) so a strict tier prompts before arbitrary code runs.
3521
3594
  interactiveApproval: true,
@@ -51,13 +51,38 @@ describe("mergeConsecutiveMessages", () => {
51
51
  // A multi-tool turn produces back-to-back `tool` results — folding them
52
52
  // would drop tool_call_ids and corrupt the tool-call pairing.
53
53
  const msgs = [
54
- { role: "assistant", content: "", tool_calls: [{ id: "a" }, { id: "b" }] },
54
+ {
55
+ role: "assistant",
56
+ content: "",
57
+ tool_calls: [{ id: "a" }, { id: "b" }],
58
+ },
55
59
  { role: "tool", content: "r1", tool_call_id: "a" },
56
60
  { role: "tool", content: "r2", tool_call_id: "b" },
57
61
  ];
58
62
  expect(mergeConsecutiveMessages(msgs)).toEqual(msgs);
59
63
  });
60
64
 
65
+ it("never merges a structured assistant tool-call turn (tool_calls preserved)", () => {
66
+ // Two consecutive assistants where the SECOND carries tool_calls — folding
67
+ // would drop them (orphaning the result) and the strict API would 400.
68
+ const msgs = [
69
+ { role: "assistant", content: "let me check" },
70
+ { role: "assistant", content: "", tool_calls: [{ id: "c1" }] },
71
+ { role: "tool", content: "result", tool_call_id: "c1" },
72
+ ];
73
+ expect(mergeConsecutiveMessages(msgs)).toEqual(msgs);
74
+ });
75
+
76
+ it("never folds two consecutive assistants when the FIRST has tool_calls", () => {
77
+ // e.g. after a compaction dropped the tool results — merging would mutate
78
+ // the structured turn's content and keep its now-unanswered tool_calls.
79
+ const msgs = [
80
+ { role: "assistant", content: "", tool_calls: [{ id: "a" }] },
81
+ { role: "assistant", content: "fallback text" },
82
+ ];
83
+ expect(mergeConsecutiveMessages(msgs)).toEqual(msgs);
84
+ });
85
+
61
86
  it("never merges system messages (leading base + context pair kept distinct)", () => {
62
87
  const msgs = [
63
88
  { role: "system", content: "base" },
@@ -202,14 +227,22 @@ describe("collapseConsecutiveMessagesInPlace", () => {
202
227
  const messages = [
203
228
  { role: "user", content: "u1" },
204
229
  { role: "user", content: "u2" },
205
- { role: "assistant", content: "", tool_calls: [{ id: "a" }, { id: "b" }] },
230
+ {
231
+ role: "assistant",
232
+ content: "",
233
+ tool_calls: [{ id: "a" }, { id: "b" }],
234
+ },
206
235
  { role: "tool", content: "r1", tool_call_id: "a" },
207
236
  { role: "tool", content: "r2", tool_call_id: "b" },
208
237
  ];
209
238
  expect(collapseConsecutiveMessagesInPlace(messages)).toBe(true);
210
239
  expect(messages).toEqual([
211
240
  { role: "user", content: "u1\n\nu2" },
212
- { role: "assistant", content: "", tool_calls: [{ id: "a" }, { id: "b" }] },
241
+ {
242
+ role: "assistant",
243
+ content: "",
244
+ tool_calls: [{ id: "a" }, { id: "b" }],
245
+ },
213
246
  { role: "tool", content: "r1", tool_call_id: "a" },
214
247
  { role: "tool", content: "r2", tool_call_id: "b" },
215
248
  ]);