chainlesschain 0.162.30 → 0.162.32

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 (179) hide show
  1. package/package.json +2 -2
  2. package/src/assets/web-panel/assets/{AIOps-CsNttUU7.js → AIOps-Cg_uWAVl.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-lgohjckQ.js → ActionButton-DSFtQ1c2.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-ccV3LAca.js → Analytics-BMxpkw8y.js} +3 -3
  5. package/src/assets/web-panel/assets/AppLayout-tgVxlmsx.js +9 -0
  6. package/src/assets/web-panel/assets/{Audit-B1gFM5U9.js → Audit-DwzGllcp.js} +1 -1
  7. package/src/assets/web-panel/assets/{Backup-BeWE3ERo.js → Backup-BG28Y2MV.js} +1 -1
  8. package/src/assets/web-panel/assets/{BaseInput-CDkPsNG2.js → BaseInput-TXthbazl.js} +1 -1
  9. package/src/assets/web-panel/assets/{Chat-ztb9ia6e.js → Chat-D096SxaD.js} +4 -4
  10. package/src/assets/web-panel/assets/{ChatBubbleRenderer-Dlw_6n3M.js → ChatBubbleRenderer-PIx0Eu9I.js} +1 -1
  11. package/src/assets/web-panel/assets/{Checkbox-BcfRBlIY.js → Checkbox-Czttw1JS.js} +1 -1
  12. package/src/assets/web-panel/assets/{Codegen-DOs99xkr.js → Codegen-DZtMgv4q.js} +1 -1
  13. package/src/assets/web-panel/assets/{Col-D1X6tYlj.js → Col-D3DnfExY.js} +1 -1
  14. package/src/assets/web-panel/assets/{Community-DTksIWtz.js → Community-Bj5AdwqY.js} +1 -1
  15. package/src/assets/web-panel/assets/{Compact-DIJtAYBO.js → Compact-BQ8Zszub.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compliance-BBf7LF_k.js → Compliance-DXacb34n.js} +1 -1
  17. package/src/assets/web-panel/assets/{Cowork-UBPXQ40s.js → Cowork-BgMUBTkw.js} +2 -2
  18. package/src/assets/web-panel/assets/{Cron-CkRm1jPB.js → Cron-fqBWOqlN.js} +2 -2
  19. package/src/assets/web-panel/assets/{Crosschain-qALlTl7e.js → Crosschain-E4oa1MWy.js} +1 -1
  20. package/src/assets/web-panel/assets/{DID-CqyqVS6E.js → DID-pwgfYZaV.js} +2 -2
  21. package/src/assets/web-panel/assets/Dashboard-n8mdLFIR.js +3 -0
  22. package/src/assets/web-panel/assets/{Dropdown-Cb5UzbSZ.js → Dropdown--6DYqxk7.js} +1 -1
  23. package/src/assets/web-panel/assets/{EmailListRenderer-CarBq8Fk.js → EmailListRenderer-CkjQluz3.js} +1 -1
  24. package/src/assets/web-panel/assets/{FamilyGuardDashboard-CSiGXaZz.js → FamilyGuardDashboard-u-QTQ-OC.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-DUxhVoBN.js → Federation-D219M5Qc.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-BoMQpkhx.js → FormItemContext-BBU_aopC.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-DTVqC_CX.js → GenericCardRenderer-pTMCIHcM.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-C_XuPtK5.js → Git-ClcCARWt.js} +2 -2
  29. package/src/assets/web-panel/assets/{Governance-BZyqlqz-.js → Governance-CvUi3I93.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-DdZVUimI.js → Inference-DT-a4pVg.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-IzZ-jnCn.js → KnowledgeGraph-DHMs2LY8.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-koTK6eNc.js → Logs-D2s4eV1N.js} +2 -2
  33. package/src/assets/web-panel/assets/{Marketplace-6zpJ1L8n.js → Marketplace-YC5-fx-6.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-Ywc4IVks.js → McpTools-7JHTEC4T.js} +3 -3
  35. package/src/assets/web-panel/assets/{Memory-C_zB9dUa.js → Memory-BudotVLD.js} +2 -2
  36. package/src/assets/web-panel/assets/{MobileBridge-Nc05r24L.js → MobileBridge-CAiRyLVU.js} +2 -2
  37. package/src/assets/web-panel/assets/{MobileProjects-BJGxL526.js → MobileProjects-CrJJOCFw.js} +1 -1
  38. package/src/assets/web-panel/assets/{Mtc-Im7SIcz1.js → Mtc-d0iY0CeK.js} +5 -5
  39. package/src/assets/web-panel/assets/{MtcAudit-BFFzvzMD.js → MtcAudit-aI2cG1UP.js} +4 -4
  40. package/src/assets/web-panel/assets/{Multisig-CcNEbycq.js → Multisig-4bF70khG.js} +3 -3
  41. package/src/assets/web-panel/assets/{NLProgramming-CDH6OTXN.js → NLProgramming-CwLib1S7.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-Dqg3QXcU.js → Notes-Wt7AuFRU.js} +3 -3
  43. package/src/assets/web-panel/assets/{NotificationSettings-CDVmK1eU.js → NotificationSettings-D081vV_7.js} +1 -1
  44. package/src/assets/web-panel/assets/OrderTableRenderer-DCPei1L9.js +1 -0
  45. package/src/assets/web-panel/assets/{Organization-DJb9bRQS.js → Organization-BNEsUNdP.js} +4 -4
  46. package/src/assets/web-panel/assets/{Overflow-CK7Q5dje.js → Overflow-B_1iUXDD.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-CJIyYfwc.js → P2P-Dbc-kNwJ.js} +2 -2
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-uqRULcuw.js → PdhVaultBrowser-D8Xh289k.js} +3 -3
  49. package/src/assets/web-panel/assets/{Permissions-Crvwt6bq.js → Permissions-C77mM6-n.js} +4 -4
  50. package/src/assets/web-panel/assets/{PersonalDataHub-DcN5OWzg.js → PersonalDataHub-Dj0J3r_K.js} +3 -3
  51. package/src/assets/web-panel/assets/{Pipeline-DfWJvvJW.js → Pipeline-B6F0WQ2C.js} +1 -1
  52. package/src/assets/web-panel/assets/{Privacy-DepD0S3v.js → Privacy-eDKOkyyq.js} +1 -1
  53. package/src/assets/web-panel/assets/{ProjectInit-B7OKhH27.js → ProjectInit-DAWwhr5_.js} +2 -2
  54. package/src/assets/web-panel/assets/{ProjectSettings-BJ4ueRFv.js → ProjectSettings-DwdK8k6I.js} +2 -2
  55. package/src/assets/web-panel/assets/Projects-Cb3p5QAP.js +1 -0
  56. package/src/assets/web-panel/assets/{Providers-Dl0FT1S3.js → Providers--DcYxQfN.js} +1 -1
  57. package/src/assets/web-panel/assets/{QuickAsk-V2hYLhfp.js → QuickAsk-DU268niT.js} +1 -1
  58. package/src/assets/web-panel/assets/{Recommend-8Kaiodgv.js → Recommend-ChnflhV1.js} +1 -1
  59. package/src/assets/web-panel/assets/{Reputation-CsxB3JGg.js → Reputation-DSsY3bQG.js} +1 -1
  60. package/src/assets/web-panel/assets/{Row-6-x7tEYq.js → Row-Zb-EjmgQ.js} +1 -1
  61. package/src/assets/web-panel/assets/{RssFeed-Buv6f5tw.js → RssFeed-CGLiixZB.js} +3 -3
  62. package/src/assets/web-panel/assets/{Search-ABrDz84n.js → Search-Dhr_po-U.js} +1 -1
  63. package/src/assets/web-panel/assets/{Security-DqOJmz18.js → Security-GMYNhGsR.js} +4 -4
  64. package/src/assets/web-panel/assets/{Services-Cq4Tda3q.js → Services-DiOpnVY0.js} +2 -2
  65. package/src/assets/web-panel/assets/{Skeleton-n74QlyYq.js → Skeleton-DG3ez6ME.js} +1 -1
  66. package/src/assets/web-panel/assets/{Skills-CC0iozL5.js → Skills-DZGptytP.js} +1 -1
  67. package/src/assets/web-panel/assets/{Sla-hwRgJ99Z.js → Sla-CtGpE3xA.js} +1 -1
  68. package/src/assets/web-panel/assets/{SpeechSettings-B6Bs6_-8.js → SpeechSettings-DQFw6Cf9.js} +1 -1
  69. package/src/assets/web-panel/assets/{SyncSettings-CTp2dZ0z.js → SyncSettings-C8X78RpX.js} +2 -2
  70. package/src/assets/web-panel/assets/{Tasks-D70Lis6S.js → Tasks-DtVkhWCV.js} +1 -1
  71. package/src/assets/web-panel/assets/{Templates-Cags0ssw.js → Templates-SF9_ZWsV.js} +1 -1
  72. package/src/assets/web-panel/assets/{Tenant-BxCMzzGt.js → Tenant-BbIQSVZz.js} +1 -1
  73. package/src/assets/web-panel/assets/{Terminal-v05SDqHd.js → Terminal-DKr5zDwu.js} +2 -2
  74. package/src/assets/web-panel/assets/{TimelineRenderer-BLUDHbBL.js → TimelineRenderer-BtLaNaWr.js} +1 -1
  75. package/src/assets/web-panel/assets/{Tokens-D-xKLJYv.js → Tokens-CfYbk2NG.js} +1 -1
  76. package/src/assets/web-panel/assets/{Trigger-B47tVIbH.js → Trigger-BLX_XDP0.js} +1 -1
  77. package/src/assets/web-panel/assets/{Trust-DmRU9kfs.js → Trust-BWxUv9PR.js} +1 -1
  78. package/src/assets/web-panel/assets/{UkeySign-DzgSGs-c.js → UkeySign-DRwTyQD4.js} +1 -1
  79. package/src/assets/web-panel/assets/{VideoEditing-C6qu58up.js → VideoEditing-BsC4VOSo.js} +1 -1
  80. package/src/assets/web-panel/assets/{Wallet-Dh8ZWx8f.js → Wallet-CSsO1NJU.js} +4 -4
  81. package/src/assets/web-panel/assets/{WebAuthn-DFHOVuAY.js → WebAuthn-z1MxiFzS.js} +4 -4
  82. package/src/assets/web-panel/assets/{WorkflowEditor-B_fyQ3Y_.js → WorkflowEditor-B1vV7uuJ.js} +1 -1
  83. package/src/assets/web-panel/assets/{chat-BR-WxnCQ.js → chat-C0NJRaL2.js} +1 -1
  84. package/src/assets/web-panel/assets/{colors-C-6RysQe.js → colors-CHRiteWF.js} +1 -1
  85. package/src/assets/web-panel/assets/{compact-item-B_9_SCKN.js → compact-item-2XmBBKPD.js} +1 -1
  86. package/src/assets/web-panel/assets/{createContext-D6rklIbE.js → createContext-DkedHC38.js} +1 -1
  87. package/src/assets/web-panel/assets/devWarning-DmNpkOdC.js +1 -0
  88. package/src/assets/web-panel/assets/{hasIn-BrotgSvd.js → hasIn-Bpn9Xrlw.js} +1 -1
  89. package/src/assets/web-panel/assets/index-7nAysteg.js +1 -0
  90. package/src/assets/web-panel/assets/{index-MCmNzIC7.js → index-B5NGWgHp.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-GzuCTHVZ.js → index-BItcSqan.js} +3 -3
  92. package/src/assets/web-panel/assets/index-BKWSQilQ.js +1 -0
  93. package/src/assets/web-panel/assets/{index-DTCUOKu9.js → index-BN068mCR.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-Bv9BrnD2.js → index-BOsIgPge.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DfqUsPl2.js → index-BYUd69vM.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-Cn21XmDt.js → index-BYmwEaIk.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CWmJukRW.js → index-BZ1gOoiG.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Bwkg_EJk.js → index-BfY9U3X5.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-MBOwmoOi.js → index-BveL_4n3.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CJ70GAW2.js → index-CCg6ZY4t.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-B85rQNYG.js → index-CJOoo72F.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cn5ghmbB.js → index-CToQxpWz.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-rWiOF7Iu.js → index-CWgWrrWs.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-PzM_GlKb.js → index-CdR7RfRP.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-ZehgEQYa.js → index-Cljnfuxu.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BsDNNDBN.js → index-CxvA72CP.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-D6KqyxG1.js → index-CyJpmSHZ.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-E_5VXq8H.js → index-D7U411hK.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CJgp_QFo.js → index-D9mNfpxi.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DTpElYJs.js → index-DAFLFMXQ.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DMnomft7.js → index-DAeHmElB.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-B2yXH6vy.js → index-DDy_RDjs.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-kkjq_hwC.js → index-DE5Qm9UI.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DTh0fWI4.js → index-DM9JrnYi.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DigjvHuo.js → index-DMbF-Euw.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DkpDFJRn.js → index-DUBsq_1G.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BIiCIC2j.js → index-De49R7TX.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CsWVDOd2.js → index-De5vOO9V.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CAfRNHna.js → index-Dk7P-q3n.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CdDmzoPE.js → index-DryKGM_t.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CTQkYbir.js → index-DtU4qZRF.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CK8YwdNd.js → index-NuBsCRaR.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BaLhL3Tj.js → index-Sk3-3tKa.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CTpxOc5s.js → index-alGjpoM1.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-CrGp-4E2.js → index-cfSUlOfY.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BbRl_gIW.js → index-i4W_EAuh.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CCWzUY8K.js → index-uHGxyZtQ.js} +1 -1
  128. package/src/assets/web-panel/assets/{initDefaultProps-C2v_L5na.js → initDefaultProps-DlDE-QgI.js} +1 -1
  129. package/src/assets/web-panel/assets/{motion-DNDqGbfr.js → motion-CodUbIRF.js} +1 -1
  130. package/src/assets/web-panel/assets/{move-xvpQ_6hJ.js → move-DaLwsHeR.js} +1 -1
  131. package/src/assets/web-panel/assets/{omit-Cb0FsfrO.js → omit-DdVg-3rL.js} +1 -1
  132. package/src/assets/web-panel/assets/{pickAttrs-BxhYpnum.js → pickAttrs-KLR1EVCo.js} +1 -1
  133. package/src/assets/web-panel/assets/{placementArrow-B3soaW4h.js → placementArrow-ChV7HvNw.js} +1 -1
  134. package/src/assets/web-panel/assets/{responsiveObserve-B-eRSLvd.js → responsiveObserve-BB_A8dBt.js} +1 -1
  135. package/src/assets/web-panel/assets/{slide--cM2ZOx-.js → slide-Bc1tQnIK.js} +1 -1
  136. package/src/assets/web-panel/assets/{statusUtils-DjBhfi8Q.js → statusUtils-CgrveSb0.js} +1 -1
  137. package/src/assets/web-panel/assets/{styleChecker-C30mMh8o.js → styleChecker-vXAYhhjz.js} +1 -1
  138. package/src/assets/web-panel/assets/{useFlexGapSupport-f7y2Qlzs.js → useFlexGapSupport-BCIMPfq9.js} +1 -1
  139. package/src/assets/web-panel/assets/{useFs-iTCXoLoZ.js → useFs-DMZGdr6G.js} +1 -1
  140. package/src/assets/web-panel/assets/{usePersonalDataHub-BH0RXmVF.js → usePersonalDataHub-118tWI_Z.js} +1 -1
  141. package/src/assets/web-panel/assets/{vnode-DQtmeDXM.js → vnode-Z7O2Y7JP.js} +1 -1
  142. package/src/assets/web-panel/assets/{zoom-vw50zkLZ.js → zoom-BXym6zmD.js} +1 -1
  143. package/src/assets/web-panel/index.html +1 -1
  144. package/src/commands/agent.js +333 -1
  145. package/src/commands/ask.js +35 -1
  146. package/src/commands/checkpoint.js +439 -0
  147. package/src/commands/compact.js +150 -0
  148. package/src/commands/cost.js +114 -0
  149. package/src/commands/goal.js +417 -0
  150. package/src/commands/hub.js +7 -0
  151. package/src/commands/session.js +22 -2
  152. package/src/harness/prompt-compressor.js +71 -1
  153. package/src/index.js +8 -0
  154. package/src/lib/agent-core.js +1 -0
  155. package/src/lib/checkpoint-store.js +523 -0
  156. package/src/lib/file-checkpoint.js +300 -0
  157. package/src/lib/goal-context.js +87 -0
  158. package/src/lib/goal-store.js +308 -0
  159. package/src/lib/llm-pricing.js +227 -0
  160. package/src/lib/personal-data-hub-wiring.js +30 -0
  161. package/src/lib/recent-session.js +72 -0
  162. package/src/lib/session-picker.js +68 -0
  163. package/src/repl/agent-repl.js +101 -9
  164. package/src/repl/chat-repl.js +16 -1
  165. package/src/runtime/agent-core.js +313 -32
  166. package/src/runtime/fallback-model.js +109 -0
  167. package/src/runtime/file-ref-expander.js +258 -0
  168. package/src/runtime/headless-runner.js +601 -0
  169. package/src/runtime/headless-stream.js +315 -0
  170. package/src/runtime/policies/agent-policy.js +7 -0
  171. package/src/runtime/quiet-stdout.js +35 -0
  172. package/src/runtime/system-prompt.js +60 -0
  173. package/src/assets/web-panel/assets/AppLayout-B0hl5cPk.js +0 -9
  174. package/src/assets/web-panel/assets/Dashboard-XlMpT7K_.js +0 -3
  175. package/src/assets/web-panel/assets/OrderTableRenderer-Bg0bkfjR.js +0 -1
  176. package/src/assets/web-panel/assets/Projects-Dl_hPdhU.js +0 -1
  177. package/src/assets/web-panel/assets/devWarning-BiN5HELJ.js +0 -1
  178. package/src/assets/web-panel/assets/index-BhxiT2LJ.js +0 -1
  179. package/src/assets/web-panel/assets/index-DBNSZ2oz.js +0 -1
@@ -338,9 +338,12 @@ function _buildPersonaPrompt(persona, envLines, cwd) {
338
338
  * 4. Default hardcoded prompt → fallback when no persona
339
339
  *
340
340
  * @param {string} [cwd] - working directory
341
+ * @param {object} [opts]
342
+ * @param {string[]} [opts.additionalDirectories] - extra workspace roots
343
+ * (absolute paths) the agent may read/search/edit beyond `cwd`.
341
344
  * @returns {string} complete system prompt
342
345
  */
343
- export function buildSystemPrompt(cwd) {
346
+ export function buildSystemPrompt(cwd, opts = {}) {
344
347
  const dir = cwd || process.cwd();
345
348
 
346
349
  // Check for project persona
@@ -393,6 +396,19 @@ export function buildSystemPrompt(cwd) {
393
396
  // Non-critical
394
397
  }
395
398
 
399
+ // Advertise extra workspace roots (--add-dir) so the model knows it may
400
+ // reach beyond cwd and which absolute paths to use.
401
+ const extraDirs = Array.isArray(opts.additionalDirectories)
402
+ ? opts.additionalDirectories.filter(Boolean)
403
+ : [];
404
+ if (extraDirs.length > 0) {
405
+ prompt +=
406
+ `\n\n## Additional working directories\n` +
407
+ `Beyond the current working directory, you may read, search, and edit ` +
408
+ `files under these absolute roots. Pass absolute paths to access them:\n` +
409
+ extraDirs.map((d) => `- ${d}`).join("\n");
410
+ }
411
+
396
412
  return prompt;
397
413
  }
398
414
 
@@ -529,6 +545,7 @@ export async function executeTool(name, args, context = {}) {
529
545
  shellPolicyOverrides: context.shellPolicyOverrides || null,
530
546
  approvalGate: context.approvalGate || null,
531
547
  shellConfirm: context.shellConfirm || null,
548
+ additionalDirectories: context.additionalDirectories || null,
532
549
  });
533
550
  } catch (err) {
534
551
  if (hookDb) {
@@ -598,6 +615,7 @@ async function executeToolInner(
598
615
  shellPolicyOverrides,
599
616
  approvalGate,
600
617
  shellConfirm,
618
+ additionalDirectories,
601
619
  },
602
620
  ) {
603
621
  const localToolDescriptor =
@@ -962,41 +980,52 @@ async function executeToolInner(
962
980
  }
963
981
 
964
982
  case "search_files": {
965
- const dir = args.directory ? path.resolve(cwd, args.directory) : cwd;
966
- try {
967
- if (args.content_search) {
968
- const cmd =
969
- process.platform === "win32"
970
- ? `findstr /s /i /n "${args.pattern}" *`
971
- : `grep -r -l -i "${args.pattern}" . --include="*" 2>/dev/null | head -20`;
972
- const output = execSync(cmd, {
973
- cwd: dir,
974
- encoding: "utf8",
975
- timeout: 10000,
976
- });
977
- return attachDescriptor({
978
- matches: output.trim().split("\n").slice(0, 20),
979
- });
980
- } else {
981
- const cmd =
982
- process.platform === "win32"
983
- ? `dir /s /b *${args.pattern}* 2>NUL`
984
- : `find . -name "*${args.pattern}*" -type f 2>/dev/null | head -20`;
983
+ // An explicit directory scopes the search to one root; otherwise span
984
+ // cwd plus any --add-dir roots so cross-package searches find matches.
985
+ const extraRoots = Array.isArray(additionalDirectories)
986
+ ? additionalDirectories.filter(Boolean)
987
+ : [];
988
+ const roots = args.directory
989
+ ? [path.resolve(cwd, args.directory)]
990
+ : [cwd, ...extraRoots];
991
+ const isContent = Boolean(args.content_search);
992
+ const cmd = isContent
993
+ ? process.platform === "win32"
994
+ ? `findstr /s /i /n "${args.pattern}" *`
995
+ : `grep -r -l -i "${args.pattern}" . --include="*" 2>/dev/null | head -20`
996
+ : process.platform === "win32"
997
+ ? `dir /s /b *${args.pattern}* 2>NUL`
998
+ : `find . -name "*${args.pattern}*" -type f 2>/dev/null | head -20`;
999
+
1000
+ const hits = [];
1001
+ const seen = new Set();
1002
+ for (const root of roots) {
1003
+ if (hits.length >= 20) break;
1004
+ try {
1005
+ if (!fs.existsSync(root)) continue;
985
1006
  const output = execSync(cmd, {
986
- cwd: dir,
1007
+ cwd: root,
987
1008
  encoding: "utf8",
988
1009
  timeout: 10000,
989
1010
  });
990
- return attachDescriptor({
991
- files: output.trim().split("\n").filter(Boolean).slice(0, 20),
992
- });
1011
+ for (const line of output.trim().split("\n")) {
1012
+ const v = line.trim();
1013
+ if (!v || seen.has(v)) continue;
1014
+ // Qualify with the root so multi-root results stay unambiguous.
1015
+ const labeled = roots.length > 1 ? `${root}: ${v}` : v;
1016
+ seen.add(v);
1017
+ hits.push(labeled);
1018
+ if (hits.length >= 20) break;
1019
+ }
1020
+ } catch {
1021
+ // No matches in this root — continue to the next.
993
1022
  }
994
- } catch {
995
- return attachDescriptor({
996
- files: [],
997
- message: "No matches found",
998
- });
999
1023
  }
1024
+
1025
+ if (hits.length === 0) {
1026
+ return attachDescriptor({ files: [], message: "No matches found" });
1027
+ }
1028
+ return attachDescriptor(isContent ? { matches: hits } : { files: hits });
1000
1029
  }
1001
1030
 
1002
1031
  case "list_dir": {
@@ -1608,9 +1637,16 @@ export async function chatWithTools(rawMessages, options) {
1608
1637
  } = options;
1609
1638
 
1610
1639
  const persona = _loadProjectPersona(options.cwd);
1640
+ // Merge the project-persona deny-list with any caller-supplied deny-list
1641
+ // (e.g. headless `--disallowed-tools`). Without this merge the caller's
1642
+ // deny-list is silently dropped and the tool stays callable.
1643
+ const mergedDisabledTools = [
1644
+ ...(Array.isArray(persona?.toolsDisabled) ? persona.toolsDisabled : []),
1645
+ ...(Array.isArray(options.disabledTools) ? options.disabledTools : []),
1646
+ ];
1611
1647
  const tools = getAgentToolDefinitions({
1612
1648
  names: options.enabledToolNames,
1613
- disabledTools: persona?.toolsDisabled,
1649
+ disabledTools: mergedDisabledTools,
1614
1650
  extraTools: [
1615
1651
  ...(options.hostManagedToolPolicy?.toolDefinitions || []),
1616
1652
  ...(options.extraToolDefinitions || []),
@@ -1627,7 +1663,21 @@ export async function chatWithTools(rawMessages, options) {
1627
1663
  throwIfAborted(signal);
1628
1664
 
1629
1665
  if (provider === "ollama") {
1630
- const response = await fetch(`${baseUrl}/api/chat`, {
1666
+ const apiUrl = `${baseUrl}/api/chat`;
1667
+ // Real-time token deltas (Claude-Code `--include-partial-messages`): when
1668
+ // the caller supplies an onToken hook, stream the response and forward each
1669
+ // content chunk as it arrives. Tool calls + usage are accumulated and the
1670
+ // same {message, usage} shape is returned, so the agent loop is unchanged.
1671
+ // Without onToken we keep the cheaper single-shot non-streaming request.
1672
+ if (typeof options.onToken === "function") {
1673
+ return await _chatOllamaStreaming(
1674
+ apiUrl,
1675
+ { model, messages, tools },
1676
+ options.onToken,
1677
+ signal,
1678
+ );
1679
+ }
1680
+ const response = await fetch(apiUrl, {
1631
1681
  method: "POST",
1632
1682
  headers: { "Content-Type": "application/json" },
1633
1683
  signal,
@@ -1781,6 +1831,109 @@ export async function chatWithTools(rawMessages, options) {
1781
1831
  return out;
1782
1832
  }
1783
1833
 
1834
+ // ─── Ollama streaming (token deltas for --include-partial-messages) ─────────
1835
+ //
1836
+ // Ollama `/api/chat` with `stream:true` returns NDJSON: one JSON object per
1837
+ // line, each carrying an incremental `message.content` chunk, optional
1838
+ // `message.tool_calls` (emitted whole, not byte-streamed), and a final line
1839
+ // with `done:true` + `prompt_eval_count`/`eval_count` token totals. We reduce
1840
+ // the stream line-by-line so onToken fires live, then finalize into the same
1841
+ // {message, usage} shape the non-streaming branch returns.
1842
+
1843
+ function _ollamaInitState() {
1844
+ return {
1845
+ role: "assistant",
1846
+ content: "",
1847
+ toolCalls: null,
1848
+ promptEval: 0,
1849
+ evalCount: 0,
1850
+ };
1851
+ }
1852
+
1853
+ function _ollamaReduceLine(state, line, onToken) {
1854
+ const s = (line || "").trim();
1855
+ if (!s) return state;
1856
+ let obj;
1857
+ try {
1858
+ obj = JSON.parse(s);
1859
+ } catch {
1860
+ return state; // tolerate partial/garbage lines mid-stream
1861
+ }
1862
+ const msg = obj.message;
1863
+ if (msg) {
1864
+ if (msg.role) state.role = msg.role;
1865
+ if (typeof msg.content === "string" && msg.content) {
1866
+ state.content += msg.content;
1867
+ if (typeof onToken === "function") {
1868
+ try {
1869
+ onToken(msg.content);
1870
+ } catch {
1871
+ // A failing UI hook must never break the agent run.
1872
+ }
1873
+ }
1874
+ }
1875
+ if (Array.isArray(msg.tool_calls) && msg.tool_calls.length) {
1876
+ state.toolCalls = (state.toolCalls || []).concat(msg.tool_calls);
1877
+ }
1878
+ }
1879
+ if (obj.prompt_eval_count) state.promptEval = obj.prompt_eval_count;
1880
+ if (obj.eval_count) state.evalCount = obj.eval_count;
1881
+ return state;
1882
+ }
1883
+
1884
+ function _ollamaFinalize(state) {
1885
+ const message = { role: state.role, content: state.content };
1886
+ if (state.toolCalls && state.toolCalls.length) {
1887
+ message.tool_calls = state.toolCalls;
1888
+ }
1889
+ const data = { message };
1890
+ if (state.promptEval || state.evalCount) {
1891
+ data.usage = {
1892
+ input_tokens: state.promptEval,
1893
+ output_tokens: state.evalCount,
1894
+ };
1895
+ }
1896
+ return data;
1897
+ }
1898
+
1899
+ /**
1900
+ * Pure reducer over an iterable of Ollama NDJSON lines. Exported for tests so
1901
+ * the parse/accumulate logic can be exercised without a live HTTP stream.
1902
+ */
1903
+ export function _accumulateOllamaStream(lines, onToken) {
1904
+ const state = _ollamaInitState();
1905
+ for (const line of lines) _ollamaReduceLine(state, line, onToken);
1906
+ return _ollamaFinalize(state);
1907
+ }
1908
+
1909
+ async function _chatOllamaStreaming(apiUrl, body, onToken, signal) {
1910
+ const response = await fetch(apiUrl, {
1911
+ method: "POST",
1912
+ headers: { "Content-Type": "application/json" },
1913
+ signal,
1914
+ body: JSON.stringify({ ...body, stream: true }),
1915
+ });
1916
+ if (!response.ok) {
1917
+ throw new Error(`Ollama error: ${response.status}`);
1918
+ }
1919
+ const state = _ollamaInitState();
1920
+ const reader = response.body.getReader();
1921
+ const decoder = new TextDecoder();
1922
+ let buf = "";
1923
+ for (;;) {
1924
+ const { done, value } = await reader.read();
1925
+ if (done) break;
1926
+ buf += decoder.decode(value, { stream: true });
1927
+ let idx;
1928
+ while ((idx = buf.indexOf("\n")) >= 0) {
1929
+ _ollamaReduceLine(state, buf.slice(0, idx), onToken);
1930
+ buf = buf.slice(idx + 1);
1931
+ }
1932
+ }
1933
+ if (buf.trim()) _ollamaReduceLine(state, buf, onToken);
1934
+ return _ollamaFinalize(state);
1935
+ }
1936
+
1784
1937
  function _normalizeAnthropicResponse(data) {
1785
1938
  const content = data.content || [];
1786
1939
  const textBlocks = content.filter((b) => b.type === "text");
@@ -1807,11 +1960,58 @@ function _normalizeAnthropicResponse(data) {
1807
1960
 
1808
1961
  // ─── Agent loop (async generator) ─────────────────────────────────────────
1809
1962
 
1963
+ // Tools that never mutate the workspace — auto-checkpoint skips these.
1964
+ const _CHECKPOINT_READ_ONLY = new Set([
1965
+ "read_file",
1966
+ "search_files",
1967
+ "list_dir",
1968
+ "list_skills",
1969
+ "search_sessions",
1970
+ ]);
1971
+
1972
+ let _checkpointStoreP = null;
1973
+ function _loadCheckpointStore() {
1974
+ if (!_checkpointStoreP) {
1975
+ _checkpointStoreP = import("../lib/checkpoint-store.js");
1976
+ }
1977
+ return _checkpointStoreP;
1978
+ }
1979
+
1980
+ /**
1981
+ * Best-effort auto-checkpoint of the working tree BEFORE a mutating tool runs,
1982
+ * so a later `cc checkpoint restore` can roll back to just before that tool.
1983
+ * Enabled via toolContext.autoCheckpoint; uses the git engine only (no-op
1984
+ * outside a git work tree). Never throws — checkpointing must not block a tool.
1985
+ *
1986
+ * @returns {Promise<string|null>} the checkpoint id, or null when skipped
1987
+ */
1988
+ async function _autoCheckpointBeforeTool(toolContext, toolName, toolArgs) {
1989
+ if (!toolContext?.autoCheckpoint) return null;
1990
+ if (_CHECKPOINT_READ_ONLY.has(toolName)) return null;
1991
+ const cwd = toolContext.cwd || process.cwd();
1992
+ try {
1993
+ const store = await _loadCheckpointStore();
1994
+ if (!store.isCheckpointAvailable(cwd)) return null;
1995
+ const res = store.createCheckpoint(cwd, {
1996
+ session: toolContext.checkpointSession || "agent",
1997
+ label: `before ${toolName}: ${formatToolArgs(toolName, toolArgs)}`.slice(
1998
+ 0,
1999
+ 120,
2000
+ ),
2001
+ skipIfUnchanged: true,
2002
+ });
2003
+ return res?.id || null;
2004
+ } catch {
2005
+ return null; // checkpoint failure must never block the tool
2006
+ }
2007
+ }
2008
+
1810
2009
  /**
1811
2010
  * Async generator that drives the agentic tool-use loop.
1812
2011
  *
1813
2012
  * Yields events:
1814
2013
  * { type: "slot-filling", slot, question } — when asking user for missing info
2014
+ * { type: "checkpoint", id, tool } — auto-checkpoint before a mutating tool
1815
2015
  * { type: "tool-executing", tool, args }
1816
2016
  * { type: "tool-result", tool, result, error }
1817
2017
  * { type: "response-complete", content }
@@ -1819,6 +2019,38 @@ function _normalizeAnthropicResponse(data) {
1819
2019
  * @param {Array} messages - mutable messages array (will be appended to)
1820
2020
  * @param {object} options - provider, model, baseUrl, apiKey, contextEngine, hookDb, skillLoader, cwd, slotFiller, interaction
1821
2021
  */
2022
+ /**
2023
+ * Lazily build (and cache on `options`) the PromptCompressor used for in-loop
2024
+ * auto-compaction. Returns null when the feature is off or the module can't be
2025
+ * loaded — callers treat that as "don't compact". Cached (including null) so we
2026
+ * import once per run, not once per iteration.
2027
+ */
2028
+ async function _getAutoCompactor(options) {
2029
+ if (Object.prototype.hasOwnProperty.call(options, "_autoCompactor")) {
2030
+ return options._autoCompactor;
2031
+ }
2032
+ let compressor = null;
2033
+ try {
2034
+ const { feature } = await import("../lib/feature-flags.js");
2035
+ if (feature("PROMPT_COMPRESSOR")) {
2036
+ const { PromptCompressor } =
2037
+ await import("../harness/prompt-compressor.js");
2038
+ compressor = new PromptCompressor({
2039
+ model: options.model,
2040
+ provider: options.provider,
2041
+ });
2042
+ }
2043
+ } catch {
2044
+ compressor = null;
2045
+ }
2046
+ try {
2047
+ options._autoCompactor = compressor;
2048
+ } catch {
2049
+ // options may be frozen — fine, we just re-import next iteration
2050
+ }
2051
+ return compressor;
2052
+ }
2053
+
1822
2054
  export async function* agentLoop(messages, options) {
1823
2055
  // Shared iteration budget — replaces hardcoded MAX_ITERATIONS.
1824
2056
  // When options.iterationBudget is provided (e.g. from parent agent),
@@ -1842,6 +2074,10 @@ export async function* agentLoop(messages, options) {
1842
2074
  shellPolicyOverrides: options.shellPolicyOverrides || null,
1843
2075
  approvalGate: options.approvalGate || null,
1844
2076
  shellConfirm: options.shellConfirm || null,
2077
+ additionalDirectories: options.additionalDirectories || null,
2078
+ autoCheckpoint: options.autoCheckpoint || false,
2079
+ checkpointSession:
2080
+ options.checkpointSession || options.sessionId || "agent",
1845
2081
  };
1846
2082
 
1847
2083
  throwIfAborted(signal);
@@ -1942,6 +2178,42 @@ export async function* agentLoop(messages, options) {
1942
2178
  };
1943
2179
  }
1944
2180
 
2181
+ // Headless auto-compaction (Claude-Code `--print` parity). Keeps long
2182
+ // `-p` / `--resume` runs under the model's context window instead of
2183
+ // growing until the provider rejects the request. Opt-out with
2184
+ // `autoCompact: false` (the interactive REPL does this — it compacts on its
2185
+ // own schedule). Default-on, gated by the PROMPT_COMPRESSOR flag + a size
2186
+ // threshold inside the compressor, so it only fires for genuinely large
2187
+ // contexts. Safe to compact here: the previous iteration always finishes
2188
+ // its full tool_call→tool_result cycle before we loop, so `messages` has no
2189
+ // dangling call; `preserveToolPairs` then guarantees compaction never
2190
+ // orphans a tool result. Best-effort — a failure never aborts the run.
2191
+ if (options.autoCompact !== false && messages.length > 4) {
2192
+ try {
2193
+ const compactor = await _getAutoCompactor(options);
2194
+ if (compactor && compactor.shouldAutoCompact(messages)) {
2195
+ const { messages: compacted, stats } = await compactor.compress(
2196
+ messages,
2197
+ { preserveToolPairs: true },
2198
+ );
2199
+ if (stats.saved > 0 && compacted.length < messages.length) {
2200
+ messages.splice(0, messages.length, ...compacted);
2201
+ if (typeof options.onCompaction === "function") {
2202
+ try {
2203
+ options.onCompaction(stats, compacted);
2204
+ } catch {
2205
+ // persistence is best-effort
2206
+ }
2207
+ }
2208
+ yield { type: "compaction", stats, runId };
2209
+ }
2210
+ }
2211
+ } catch (_e) {
2212
+ if (isAbortError(_e) || signal?.aborted) throw _e;
2213
+ // Compaction is best-effort — proceed with the uncompacted messages.
2214
+ }
2215
+ }
2216
+
1945
2217
  // Turn-scoped context injection (open-agents prepareCall parity).
1946
2218
  // prepareCall runs fresh each iteration and returns an ephemeral
1947
2219
  // system-message supplement that is NOT persisted to messages history.
@@ -2013,6 +2285,15 @@ export async function* agentLoop(messages, options) {
2013
2285
  toolArgs = {};
2014
2286
  }
2015
2287
 
2288
+ // Auto-checkpoint the work tree before a mutating tool (opt-in), so the
2289
+ // user can `cc checkpoint restore` back to just before this call.
2290
+ const cpId = await _autoCheckpointBeforeTool(
2291
+ toolContext,
2292
+ toolName,
2293
+ toolArgs,
2294
+ );
2295
+ if (cpId) yield { type: "checkpoint", id: cpId, tool: toolName };
2296
+
2016
2297
  yield { type: "tool-executing", tool: toolName, args: toolArgs };
2017
2298
 
2018
2299
  let toolResult;
@@ -0,0 +1,109 @@
1
+ /**
2
+ * `--fallback-model` support — Claude-Code parity for unattended runs.
3
+ *
4
+ * Wraps the agent loop's LLM call so a single request that fails with a
5
+ * *retryable* error (overload / rate-limit / transient network) is transparently
6
+ * re-issued once with a backup model. Because it sits at the chatFn seam
7
+ * (`agentLoop` uses `options.chatFn || chatWithTools`), fallback needs no changes
8
+ * to the runners — the wrapped fn is passed in via `options.chatFn`.
9
+ *
10
+ * Same-provider only: the fallback swaps `options.model`, keeping the configured
11
+ * provider / baseUrl / apiKey. Cross-provider fallback is a larger feature.
12
+ */
13
+
14
+ import { chatWithTools } from "./agent-core.js";
15
+
16
+ // Heuristics for "try again on a different model" — overloaded backends,
17
+ // rate limits, and transient connectivity. Deliberately conservative: a 4xx
18
+ // that is not 429 (bad request / auth) is NOT retried.
19
+ const RETRYABLE_PATTERNS = [
20
+ /overload/i,
21
+ /rate.?limit/i,
22
+ /too many requests/i,
23
+ /temporarily unavailable/i,
24
+ /\b429\b/,
25
+ /\b50[0234]\b/,
26
+ /\b529\b/,
27
+ /timeout/i,
28
+ /timed out/i,
29
+ /ETIMEDOUT/i,
30
+ /ECONNREFUSED/i,
31
+ /ECONNRESET/i,
32
+ /ENOTFOUND/i,
33
+ /EAI_AGAIN/i,
34
+ /socket hang up/i,
35
+ /fetch failed/i,
36
+ /network error/i,
37
+ ];
38
+
39
+ /**
40
+ * Decide whether an error from an LLM call warrants a fallback retry.
41
+ * @param {any} err
42
+ * @returns {boolean}
43
+ */
44
+ export function isRetryableModelError(err) {
45
+ if (!err) return false;
46
+ const status =
47
+ typeof err.status === "number"
48
+ ? err.status
49
+ : typeof err.statusCode === "number"
50
+ ? err.statusCode
51
+ : null;
52
+ if (status === 429) return true;
53
+ if (status !== null && status >= 500 && status <= 599) return true;
54
+
55
+ const parts = [
56
+ err.message,
57
+ typeof err.code === "string" ? err.code : "",
58
+ err.cause?.message,
59
+ err.cause?.code,
60
+ ]
61
+ .filter(Boolean)
62
+ .join(" ");
63
+ return RETRYABLE_PATTERNS.some((re) => re.test(parts));
64
+ }
65
+
66
+ /**
67
+ * Build a chatFn that retries once on the fallback model.
68
+ *
69
+ * @param {object} opts
70
+ * @param {string} opts.fallbackModel backup model name (required)
71
+ * @param {Function} [opts.baseChatFn=chatWithTools] underlying LLM call
72
+ * @param {Function} [opts.isRetryable] error predicate (testing seam)
73
+ * @param {Function} [opts.onFallback] notified ({from,to,error}) on retry
74
+ * @returns {Function} a (messages, options) => Promise<result> chatFn
75
+ */
76
+ export function makeFallbackChatFn(opts = {}) {
77
+ const fallbackModel = opts.fallbackModel;
78
+ const baseChatFn = opts.baseChatFn || chatWithTools;
79
+ const isRetryable = opts.isRetryable || isRetryableModelError;
80
+ const onFallback = opts.onFallback;
81
+
82
+ return async function chatWithFallback(messages, options = {}) {
83
+ try {
84
+ return await baseChatFn(messages, options);
85
+ } catch (err) {
86
+ const primaryModel = options.model;
87
+ // Skip a no-op retry when the fallback is the same model as the primary.
88
+ if (
89
+ !fallbackModel ||
90
+ fallbackModel === primaryModel ||
91
+ !isRetryable(err)
92
+ ) {
93
+ throw err;
94
+ }
95
+ if (typeof onFallback === "function") {
96
+ try {
97
+ onFallback({
98
+ from: primaryModel,
99
+ to: fallbackModel,
100
+ error: err?.message || String(err),
101
+ });
102
+ } catch {
103
+ // Notification is best-effort — never mask the retry.
104
+ }
105
+ }
106
+ return await baseChatFn(messages, { ...options, model: fallbackModel });
107
+ }
108
+ };
109
+ }