@seanhogg/builderforce-agents 2026.9.5 → 2026.9.6

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 (205) hide show
  1. package/dist/{agent-CBRDuLO-.js → agent-59_cdTZl.js} +1 -1
  2. package/dist/{agent-CCY1r5YW.js → agent-CvMM49lm.js} +1 -1
  3. package/dist/{agents-DW3hte-y.js → agents-2d7A-Uwz.js} +1 -1
  4. package/dist/{agents-Ce4bN10e.js → agents-BdOgv61S.js} +1 -1
  5. package/dist/{audio-preflight-BvzGdNYy.js → audio-preflight-CmX2XJ3N.js} +1 -1
  6. package/dist/{audio-preflight-BYMboYmW.js → audio-preflight-Dnw3oDJr.js} +1 -1
  7. package/dist/{audio-preflight-CEOxJosQ.js → audio-preflight-HcRdB0oH.js} +1 -1
  8. package/dist/{audio-preflight-CswNxppe.js → audio-preflight-Sh649dxR.js} +1 -1
  9. package/dist/{auth-choice-DSLN0TLU.js → auth-choice-D06_TWBf.js} +1 -1
  10. package/dist/{auth-choice-BTrUlYmE.js → auth-choice-DyxxR025.js} +1 -1
  11. package/dist/{auth-choice.apply-BZ7E08Yb.js → auth-choice.apply-CUuLdlQd.js} +1 -1
  12. package/dist/{auth-choice.apply-D3EY1pZ3.js → auth-choice.apply-CfFhu_Vk.js} +1 -1
  13. package/dist/{auth-choice.apply-5dpCNO0l.js → auth-choice.apply-D57XA_d9.js} +1 -1
  14. package/dist/{auth-choice.apply-3tPFL-eH.js → auth-choice.apply-s_rOXFVz.js} +1 -1
  15. package/dist/{auth-choice.apply.transformers-B_Q6e8Yk.js → auth-choice.apply.transformers-CqQSUnsx.js} +1 -1
  16. package/dist/{auth-choice.apply.transformers-D1EzC906.js → auth-choice.apply.transformers-DXBWOilg.js} +1 -1
  17. package/dist/{auth-choice.apply.transformers-BJbpyCWs.js → auth-choice.apply.transformers-DizfkAmz.js} +1 -1
  18. package/dist/{auth-choice.apply.transformers-vLzCo5ew.js → auth-choice.apply.transformers-vyvtAxcX.js} +1 -1
  19. package/dist/{banner-DVNoUX5I.js → banner-BXpp_pEd.js} +1 -1
  20. package/dist/build-info.json +3 -3
  21. package/dist/{builderforce-Bs_G8kBN.js → builderforce-BdJAOfvE.js} +2 -2
  22. package/dist/{builderforce-BN_So1di.js → builderforce-BrFUSx35.js} +2 -2
  23. package/dist/{builderforce-directory-sync-BCZK4FYJ.js → builderforce-directory-sync-CFiQew6N.js} +1 -1
  24. package/dist/{builderforce-directory-sync-DHfiUf2g.js → builderforce-directory-sync-DIQQT09l.js} +1 -1
  25. package/dist/{builderforce-onboarding-Bbjz8F-r.js → builderforce-onboarding-CoGJY95Q.js} +1 -1
  26. package/dist/{builderforce-onboarding-8lL2bgtL.js → builderforce-onboarding-DyYM_cib.js} +1 -1
  27. package/dist/{builderforcellm-syscheck-CyZiwcdF.js → builderforcellm-syscheck-CfFrfeV2.js} +1 -1
  28. package/dist/{builderforcellm-syscheck-DMjMJNFI.js → builderforcellm-syscheck-DGZKIwPC.js} +1 -1
  29. package/dist/bundled/boot-md/handler.js +1 -1
  30. package/dist/bundled/session-memory/handler.js +1 -1
  31. package/dist/{channel-options-Zk5QnNPR.js → channel-options--DZ4jYy9.js} +1 -1
  32. package/dist/{channel-options-Cbz6vcgo.js → channel-options-xVK4amr4.js} +1 -1
  33. package/dist/{channel-web-1xD-Ad2T.js → channel-web-9vO41fn2.js} +1 -1
  34. package/dist/{channels-cli-CiVlP8_q.js → channels-cli-Ch1sZ58k.js} +1 -1
  35. package/dist/{channels-cli-BOcpgY_W.js → channels-cli-YMSOJUmp.js} +1 -1
  36. package/dist/chrome-Bi-3xZj-.js +4 -0
  37. package/dist/chrome-C6oWWmHX.js +4 -0
  38. package/dist/{cli-6znoaKr2.js → cli-gsxv7DrZ.js} +1 -1
  39. package/dist/{cli-BPwpRE1j.js → cli-zC5qoRAp.js} +1 -1
  40. package/dist/{command-registry-BTxAGdlS.js → command-registry-BXLjR3kb.js} +1 -1
  41. package/dist/{command-registry-BZINHmI9.js → command-registry-I83E6HLw.js} +1 -1
  42. package/dist/{completion-cli-BwCfUaHz.js → completion-cli-BQCxFDqY.js} +1 -1
  43. package/dist/{completion-cli-vR4Sx9zB.js → completion-cli-CDy8f_Fg.js} +1 -1
  44. package/dist/{completion-cli-C8kMJ401.js → completion-cli-DbIxXK1B.js} +1 -1
  45. package/dist/{completion-cli-D8WkqtSU.js → completion-cli-ydxyAE7J.js} +1 -1
  46. package/dist/{config-cli-BYFA4Nin.js → config-cli-5ZSrACPR.js} +1 -1
  47. package/dist/{config-cli-DVpEv0oj.js → config-cli-BvJso6op.js} +1 -1
  48. package/dist/{configure-COeSbyyP.js → configure-1e4C38zp.js} +1 -1
  49. package/dist/{configure-CW2bJ91q.js → configure-B_oI_bg-.js} +1 -1
  50. package/dist/{configure-B_juJzVi.js → configure-Dp71zeJI.js} +1 -1
  51. package/dist/{configure-CgkUUUpM.js → configure-NxmuXXEd.js} +1 -1
  52. package/dist/{deliver-qM1Zkf4i.js → deliver-C6HTqnzY.js} +1 -1
  53. package/dist/{deliver-BNMupwo6.js → deliver-CdwCHH01.js} +1 -1
  54. package/dist/{deliver-Bs-FVPQ4.js → deliver-DFhj1d_t.js} +1 -1
  55. package/dist/{deliver-C5lVQcrs.js → deliver-XGIFv80r.js} +1 -1
  56. package/dist/{deps-DWFMK9Xg.js → deps-C4YVdrEi.js} +1 -1
  57. package/dist/{doctor-completion-s5vcMc-C.js → doctor-completion-DhIR9uqB.js} +1 -1
  58. package/dist/{doctor-completion-BdMxCat-.js → doctor-completion-Dpy-8HV9.js} +1 -1
  59. package/dist/{embedded-CRoX1qTh.js → embedded-Cw4g0mzl.js} +7 -7
  60. package/dist/{embedded-DpSS2q2a.js → embedded-DG3sEMyg.js} +7 -7
  61. package/dist/{embedded-helpers-BrfnCpCe.js → embedded-helpers-3FEGD_4b.js} +1 -1
  62. package/dist/{embedded-helpers-Q5h1lA6D.js → embedded-helpers-DGDYymYx.js} +1 -1
  63. package/dist/entry.js +1 -1
  64. package/dist/extensionAPI.js +1 -1
  65. package/dist/{gateway-cli-oSSOT67p.js → gateway-cli-BBu6KI7r.js} +4 -4
  66. package/dist/{gateway-cli-DzxNTkvL.js → gateway-cli-CSmbpS6n.js} +4 -4
  67. package/dist/{health-_L8Avf4v.js → health-AOM42u9C.js} +1 -1
  68. package/dist/{health-hXlukqbf.js → health-CRIXA8je.js} +1 -1
  69. package/dist/{health-B_qWRpsT.js → health-Cuh0tZl2.js} +1 -1
  70. package/dist/{hooks-cli-C-vbyL2y.js → hooks-cli-BeqvyPiX.js} +1 -1
  71. package/dist/{hooks-cli-7gBXRQ81.js → hooks-cli-CYI9ZvNI.js} +1 -1
  72. package/dist/index.js +2 -2
  73. package/dist/{ink-session-DpO3VOYc.js → ink-session-MNcfJzij.js} +1 -1
  74. package/dist/{ink-session-CZj35xTR.js → ink-session-VqYNJYzq.js} +1 -1
  75. package/dist/llm-slug-generator.js +1 -1
  76. package/dist/{mamba-state-engine-CFwb8DcP.js → mamba-state-engine-BYmUnYwn.js} +1 -1
  77. package/dist/{mamba-state-engine-Ck2JCfL6.js → mamba-state-engine-BloJzrxU.js} +1 -1
  78. package/dist/{models-PPv9KRbb.js → models-B6kkCBLd.js} +1 -1
  79. package/dist/{models-cli-btaamH96.js → models-cli-C7u8eLls.js} +1 -1
  80. package/dist/{models-cli-BUDSwmIY.js → models-cli-DpbpZwjy.js} +1 -1
  81. package/dist/{models-DMrs_Zsc.js → models-hEa3avo7.js} +1 -1
  82. package/dist/{onboard-4JByma4r.js → onboard-BHlI_UVY.js} +2 -2
  83. package/dist/{onboard-Dq_XQQXY.js → onboard-BW5J0Ym8.js} +2 -2
  84. package/dist/{onboard-channels-Dzyeem1L.js → onboard-channels-BkrhZ79I.js} +1 -1
  85. package/dist/{onboard-channels-D4g6xuSC.js → onboard-channels-DXIqdoto.js} +1 -1
  86. package/dist/{onboard-channels-Dir2EguP.js → onboard-channels-V51s9X-h.js} +1 -1
  87. package/dist/{onboard-channels-UZOCuxN7.js → onboard-channels-fyGQ1v4S.js} +1 -1
  88. package/dist/{onboard-interactive-DvNhkCNr.js → onboard-interactive-ClPMlCIl.js} +1 -1
  89. package/dist/{onboard-interactive-DyyEDd9a.js → onboard-interactive-D6IBBU8m.js} +1 -1
  90. package/dist/{onboard-interactive-CwNfYxGe.js → onboard-interactive-DEeqmoeW.js} +1 -1
  91. package/dist/{onboard-interactive-Co2bcd7g.js → onboard-interactive-DSZak3YX.js} +1 -1
  92. package/dist/{onboarding-DBL-H1Qu.js → onboarding-CYOfGpd5.js} +3 -3
  93. package/dist/{onboarding-CIxLpuFP.js → onboarding-DIxm_q7P.js} +3 -3
  94. package/dist/{onboarding.finalize-CGGbkAOL.js → onboarding.finalize-Co5L0rNp.js} +1 -1
  95. package/dist/{onboarding.finalize-R3TfibWY.js → onboarding.finalize-DAwpQa7n.js} +1 -1
  96. package/dist/{outbound-send-deps-CsJgGsii.js → outbound-send-deps-Cys_MtmC.js} +1 -1
  97. package/dist/{plugin-registry-Ty9w94Rd.js → plugin-registry-BM1-sG-e.js} +1 -1
  98. package/dist/{plugin-registry-BkGhcUDV.js → plugin-registry-CAy3I1K1.js} +1 -1
  99. package/dist/{plugin-registry-BenvgIsJ.js → plugin-registry-DjmzOS3n.js} +1 -1
  100. package/dist/{plugin-registry-DZirturB.js → plugin-registry-l2iN7_1l.js} +1 -1
  101. package/dist/plugin-sdk/agent-runtime/src/builderforce/ports.d.ts +1 -1
  102. package/dist/plugin-sdk/agent-runtime/src/infra/limbic-system-service.d.ts +2 -3
  103. package/dist/plugin-sdk/{audio-preflight-lhP946UF.js → audio-preflight-BbtfbLzo.js} +1 -1
  104. package/dist/plugin-sdk/{channel-web-CzXHQXgb.js → channel-web-BHose9yJ.js} +1 -1
  105. package/dist/plugin-sdk/index.js +1 -1
  106. package/dist/plugin-sdk/packages/agent-tools/src/index.d.ts +0 -1
  107. package/dist/plugin-sdk/packages/agent-tools/src/psychometric-dims.d.ts +2 -2
  108. package/dist/plugin-sdk/packages/agent-tools/src/psychometrics.d.ts +38 -5
  109. package/dist/plugin-sdk/packages/agent-tools/src/spec.d.ts +3 -1
  110. package/dist/plugin-sdk/{reply-DkmZ32IH.js → reply-V98uAjNN.js} +5 -5
  111. package/dist/plugin-sdk/{runner-DeqVi2ay.js → runner-BpFNG45j.js} +1 -1
  112. package/dist/plugin-sdk/{src-yVjde9hC.js → src-pvdIxmTY.js} +2 -2
  113. package/dist/plugin-sdk/{tool-loop-detection-AO2R09bJ.js → tool-loop-detection-Bjiit8CZ.js} +1 -1
  114. package/dist/plugin-sdk/{web-D-h3WpSq.js → web-Bq6nGRsB.js} +1 -1
  115. package/dist/{plugins-r-hGfiMK.js → plugins-BbovMu2g.js} +1 -1
  116. package/dist/{plugins-ChYVZuHy.js → plugins-DrkC1ANW.js} +1 -1
  117. package/dist/{plugins-cli-CKQ2X7tA.js → plugins-cli-CqtTn-Gv.js} +1 -1
  118. package/dist/{plugins-cli-CB7e9eCK.js → plugins-cli-DWlQnRpV.js} +1 -1
  119. package/dist/{program-D71K23KC.js → program-DOGUG9Uz.js} +2 -2
  120. package/dist/{program-context-B9V62ZJg.js → program-context-DBtTOKkf.js} +1 -1
  121. package/dist/{project-context-Cnqfr64Z.js → project-context-C5KLsMTc.js} +1 -1
  122. package/dist/{project-context-DXP12xhk.js → project-context-Cg0S9eFT.js} +1 -1
  123. package/dist/project-context-DbBD_r4v.js +1 -0
  124. package/dist/project-context-nIzFswHk.js +1 -0
  125. package/dist/project-context-store-B96x1Lfk.js +1 -0
  126. package/dist/{project-context-store-C8TROXjr.js → project-context-store-BS994lZX.js} +1 -1
  127. package/dist/{project-context-store-ClFt-YDL.js → project-context-store-BSIWJD6S.js} +1 -1
  128. package/dist/project-context-store-BzX1YdMP.js +1 -0
  129. package/dist/{prompt-select-styled-Cect8vJu.js → prompt-select-styled-2q_0LOx1.js} +1 -1
  130. package/dist/{prompt-select-styled-CJBJZiX_.js → prompt-select-styled-D31D9EIl.js} +1 -1
  131. package/dist/{provider-auth-helpers-DItSj71N.js → provider-auth-helpers-9Ut-MHV9.js} +1 -1
  132. package/dist/{provider-auth-helpers-ByvJdQT3.js → provider-auth-helpers-u1GAP4Bi.js} +1 -1
  133. package/dist/{provider-dispatcher-E-RjX6WG.js → provider-dispatcher-4KsB_bCw.js} +1 -1
  134. package/dist/{provider-dispatcher-D6-kUWf8.js → provider-dispatcher-CG7kSp65.js} +1 -1
  135. package/dist/{push-apns-B6YfFR5s.js → push-apns-CEzi4ars.js} +1 -1
  136. package/dist/{push-apns-n8P3oclR.js → push-apns-oUAHr48A.js} +1 -1
  137. package/dist/{pw-ai-BpY-eMJT.js → pw-ai-CfV_5FWo.js} +1 -1
  138. package/dist/{pw-ai-Bu1w-1rC.js → pw-ai-Dx8XRyPq.js} +1 -1
  139. package/dist/{register.agent-CAsV5dRf.js → register.agent-C84ud-cS.js} +1 -1
  140. package/dist/{register.agent-DQ4zQymV.js → register.agent-Db9SzkZg.js} +1 -1
  141. package/dist/{register.configure-HcPL-Na2.js → register.configure-BfPiEcFk.js} +1 -1
  142. package/dist/{register.configure-B-xRsB-s.js → register.configure-WYMz_fwM.js} +1 -1
  143. package/dist/{register.connect-BcZeAJ1s.js → register.connect-CMN7lqC5.js} +1 -1
  144. package/dist/{register.connect-ChZQOiYF.js → register.connect-Dqhd81kK.js} +1 -1
  145. package/dist/{register.maintenance-C-7Arpam.js → register.maintenance-BpQE-3eC.js} +1 -1
  146. package/dist/{register.maintenance-Jdfg2LKA.js → register.maintenance-DjKxt-jl.js} +1 -1
  147. package/dist/{register.message-D1IU3K0a.js → register.message-C4Gf388m.js} +1 -1
  148. package/dist/{register.message-C3cFBIXY.js → register.message-D5A7-_wN.js} +1 -1
  149. package/dist/{register.onboard-DJ2QNnOi.js → register.onboard-BF5tXr7K.js} +1 -1
  150. package/dist/{register.onboard-DjrhC3jh.js → register.onboard-BO_OtYg9.js} +1 -1
  151. package/dist/{register.setup-BNUqkiVa.js → register.setup-BzDbYjEw.js} +1 -1
  152. package/dist/{register.setup-C8XgG5Zt.js → register.setup-CaWt1bhf.js} +1 -1
  153. package/dist/{register.status-health-sessions-DcqlqAhs.js → register.status-health-sessions-BbYzFw1X.js} +1 -1
  154. package/dist/{register.status-health-sessions-CBTZZ0Rh.js → register.status-health-sessions-CDD6Eoxf.js} +1 -1
  155. package/dist/{register.subclis-CqVl0uO4.js → register.subclis-Cfb5FUTI.js} +1 -1
  156. package/dist/{register.subclis-Meewq2dj.js → register.subclis-yYbEKj9a.js} +1 -1
  157. package/dist/{reply-BsRbeUhd.js → reply-CpGH-3If.js} +8 -8
  158. package/dist/{run-main-CFVoPcgN.js → run-main-B82VAQTQ.js} +1 -1
  159. package/dist/{runner-68-X3KHf.js → runner-BLCTET8O.js} +2 -2
  160. package/dist/{runner-CfNJMPWu.js → runner-C651oVfk.js} +2 -2
  161. package/dist/{runner-BgUG2-RV.js → runner-DCn9sBTn.js} +3 -3
  162. package/dist/{runner-BKHYCYMH.js → runner-xF3_-mDL.js} +3 -3
  163. package/dist/{server-node-events-DusCxrVm.js → server-node-events-Bam08x5E.js} +1 -1
  164. package/dist/{server-node-events-BahizW3Z.js → server-node-events-kbTUUaAA.js} +1 -1
  165. package/dist/{session-dirs-RIA98OsG.js → session-dirs-BBGuanHN.js} +1 -1
  166. package/dist/{session-dirs-Cutn9srI.js → session-dirs-DV-c-o7M.js} +1 -1
  167. package/dist/{src-BRo9ywDM.js → src-BM8G-tIl.js} +3 -3
  168. package/dist/{src-DxNC0IHE.js → src-D2-7sOAX.js} +3 -3
  169. package/dist/{src-NJhXretA.js → src-D5vOHX9i.js} +2 -2
  170. package/dist/{src-Enbbh3hH.js → src-Dptpvd0V.js} +2 -2
  171. package/dist/{status-DPza6sZ1.js → status-BcT4AI8l.js} +1 -1
  172. package/dist/{status-CMdPdaSM.js → status-C9D1ex2G.js} +1 -1
  173. package/dist/{status-CFDh5snF.js → status-D_YsiWpP.js} +1 -1
  174. package/dist/{status-SyaizSN6.js → status-V8zvPBOv.js} +1 -1
  175. package/dist/{status-C1toWVvJ.js → status-i_sJ6Ikx.js} +1 -1
  176. package/dist/{subagent-registry-oB_F2U2W.js → subagent-registry-BUKplZi1.js} +8 -8
  177. package/dist/{subagent-registry-DFzwOGge.js → subagent-registry-BgUoQjBB.js} +1 -1
  178. package/dist/{subagent-registry-C1opJIrJ.js → subagent-registry-DrrF3JoH.js} +1 -1
  179. package/dist/{tool-loop-detection-DxqiqYam.js → tool-loop-detection-Bv0aRyd6.js} +1 -1
  180. package/dist/{tool-loop-detection-3W6bLEza.js → tool-loop-detection-CgUkrbCZ.js} +1 -1
  181. package/dist/{tool-loop-detection-C3z1wqgV.js → tool-loop-detection-CshVZIOE.js} +1 -1
  182. package/dist/{tool-loop-detection-DNM8YCNW.js → tool-loop-detection-bpZ-S3A1.js} +1 -1
  183. package/dist/{tui-Dy1FnApe.js → tui-CdYI5f3k.js} +2 -2
  184. package/dist/{tui-lng2vIp3.js → tui-DgBGTw2s.js} +2 -2
  185. package/dist/{tui-cli-BTFEL897.js → tui-cli-BNeRzoEe.js} +1 -1
  186. package/dist/{tui-cli-D-cDh9DV.js → tui-cli-BZ--mp9s.js} +1 -1
  187. package/dist/{tui-stream-assembler-B_AQD4Kg.js → tui-stream-assembler-BNJE9PoS.js} +1 -1
  188. package/dist/{tui-stream-assembler-Bn1WXUGG.js → tui-stream-assembler-xFcJqFJ9.js} +1 -1
  189. package/dist/{update-cli-BdT4-P_D.js → update-cli-BATUCF7p.js} +1 -1
  190. package/dist/{update-cli-DMMSWPXh.js → update-cli-BRBoX_97.js} +1 -1
  191. package/dist/{web-BiSkmACr.js → web-BdYyXjsX.js} +1 -1
  192. package/dist/{web-CXrAweDC.js → web-C5hfXVCy.js} +1 -1
  193. package/dist/{web-BS6WUf7c.js → web-CsWqmV_E.js} +1 -1
  194. package/dist/{web-CfYUd-lX.js → web-fg3nA4Qq.js} +1 -1
  195. package/dist/{workflow-cli-Du4IfE1A.js → workflow-cli-D-o_-jfU.js} +1 -1
  196. package/dist/{workflow-cli-BuEKJ4ut.js → workflow-cli-DDWSXyi0.js} +1 -1
  197. package/package.json +6 -5
  198. package/dist/chrome-C6fODzE5.js +0 -4
  199. package/dist/chrome-Cm-kVyuA.js +0 -4
  200. package/dist/plugin-sdk/agent-runtime/src/builderforce/limbic.d.ts +0 -14
  201. package/dist/plugin-sdk/packages/agent-tools/src/limbic.d.ts +0 -104
  202. package/dist/project-context-DS52UwJZ.js +0 -1
  203. package/dist/project-context-PiiAmwVd.js +0 -1
  204. package/dist/project-context-store-B2GYSwld.js +0 -1
  205. package/dist/project-context-store-DNx9ScQ_.js +0 -1
@@ -3,7 +3,7 @@ const MODEL_ROLES=[`plan`,`code`,`verify`,`explore`,`chat`,`utility`],MODEL_ROLE
3
3
  `),countNewlines=s=>s.split(`
4
4
  `).length-1;function regionAt(content,offset,newS){let startLine=countNewlines(content.slice(0,offset))+1;return{startLine,endLine:startLine+countNewlines(newS)}}function findNearest(content,oldString){let needle=oldString.split(/\r?\n/).map(l=>l.trim()).find(l=>l.length>0);if(!needle)return;let lines=content.split(/\r?\n/),index=lines.findIndex(l=>l.trim()===needle);if(index===-1&&needle.length>=8&&(index=lines.findIndex(l=>l.includes(needle))),index===-1)return;let from=Math.max(0,index-3),to=Math.min(lines.length,index+3+1),excerpt=lines.slice(from,to).map((l,i)=>`${from+i+1}: ${l}`).join(`
5
5
  `);return{line:index+1,excerpt}}function applyStringEdit(content,oldString,newString,replaceAll=!1){if(typeof oldString!=`string`||oldString.length===0)return{ok:!1,error:`oldString is required`};let ordered=content.includes(`\r
6
- `)?[{oldS:oldString,newS:newString},{oldS:toCRLF(oldString),newS:toCRLF(newString)},{oldS:toLF(oldString),newS:toLF(newString)}]:[{oldS:oldString,newS:newString},{oldS:toLF(oldString),newS:toLF(newString)},{oldS:toCRLF(oldString),newS:toCRLF(newString)}],candidates=ordered.filter((c,i)=>ordered.findIndex(d=>d.oldS===c.oldS)===i);for(let{oldS,newS}of candidates){let first=content.indexOf(oldS);if(first!==-1)return!replaceAll&&content.indexOf(oldS,first+oldS.length)!==-1?{ok:!1,error:`oldString is not unique; add more surrounding context or set replaceAll`}:{ok:!0,content:replaceAll?content.split(oldS).join(newS):content.replace(oldS,newS),replaced:replaceAll?content.split(oldS).length-1:1,region:regionAt(content,first,newS)}}let nearest=findNearest(content,oldString);return{ok:!1,error:`oldString not found in file — read_file and copy the exact text (including indentation)`,...nearest?{nearest}:{}}}const REGEX_SPECIALS=new Set([`.`,`+`,`^`,`$`,`{`,`}`,`(`,`)`,`|`,`[`,`]`,`\\`,`/`]);function globToRegExp(pattern){let re=``;for(let i=0;i<pattern.length;i++){let c=pattern[i];c===`*`?pattern[i+1]===`*`?pattern[i+2]===`/`?(re+=`(?:.*/)?`,i+=2):(re+=`.*`,i++):re+=`[^/]*`:c===`?`?re+=`[^/]`:re+=REGEX_SPECIALS.has(c)?`\\`+c:c}return RegExp(`^`+re+`$`,`i`)}function matchGlob(pathPosix,pattern){let re=globToRegExp(pattern);if(!pattern.includes(`/`)){let base=pathPosix.slice(pathPosix.lastIndexOf(`/`)+1);return re.test(base)}return re.test(pathPosix)}function filterByGlob(paths,pattern){return paths.filter(p=>matchGlob(p,pattern))}function normalizeScopeDir(raw){return(raw??``).split(`\\`).join(`/`).trim().replace(/^\.\/+/,``).replace(/^\/+|\/+$/g,``)}function isUnderScopeDir(pathPosix,scopeDir){if(!scopeDir)return!0;let p=pathPosix.split(`\\`).join(`/`);return p===scopeDir||p.startsWith(`${scopeDir}/`)}function defineTool(def){return{name:def.name,requires:def.requires??[],schema:{type:`function`,function:{name:def.name,description:def.description,parameters:def.parameters}},execute:def.execute}}const TOOL_NAME_ALIASES={list_dir:`list_files`,listdir:`list_files`,list_directory:`list_files`,list_directories:`list_files`,ls_dir:`list_files`,bash:`run_command`,shell:`run_command`,run_terminal_cmd:`run_command`,execute_command:`run_command`,run_shell_command:`run_command`,grep:`search_code`,codebase_search:`search_code`,search_files:`search_code`,find_files:`list_files`,glob_file_search:`list_files`,str_replace:`edit_file`,search_replace:`edit_file`,apply_patch:`edit_file`,write_to_file:`write_file`,create_file:`write_file`,delete:`delete_file`};function resolveToolAlias(name){return TOOL_NAME_ALIASES[name.trim().toLowerCase()]??name}var ToolRegistry=class{constructor(defs=[]){this.tools=new Map;for(let d of defs)this.register(d)}register(def){if(this.tools.has(def.name))throw Error(`duplicate tool '${def.name}'`);return this.tools.set(def.name,def),this}has(name){return this.tools.has(name)}get(name){return this.tools.get(name)}satisfies(def,caps){return def.requires.every(c=>caps.has(c))}toolsForCapabilities(caps){return[...this.tools.values()].filter(d=>this.satisfies(d,caps))}toolsFor(provider){return this.toolsForCapabilities(provider.capabilities)}schemasForCapabilities(caps){return this.toolsForCapabilities(caps).map(d=>d.schema)}schemasFor(provider){return this.schemasForCapabilities(provider.capabilities)}availableNames(provider){return this.toolsFor(provider).map(d=>d.name)}async dispatch(name,args,ctx){let resolved=resolveToolAlias(name),def=this.tools.get(resolved);return def?this.satisfies(def,ctx.caps.capabilities)?def.execute(args,ctx):{data:{ok:!1,error:`tool '${resolved}' is not available on this surface (missing capability: ${def.requires.filter(c=>!ctx.caps.capabilities.has(c)).join(`, `)}).`}}:{data:{ok:!1,error:`unknown tool '${name}'. Available tools: ${this.availableNames(ctx.caps).join(`, `)}.`}}}};const CURRENT_ENGINE_ID=`builderforce-v3`;function resolveEngineById(registry,id,defaultId=`builderforce-v3`){return registry[id??``]??registry[defaultId]}function coercePolicyGates(raw){if(!Array.isArray(raw))return[];let out=[];for(let g of raw){if(!g||typeof g!=`object`)continue;let{id,tool,effect,directive,reason}=g;typeof id==`string`&&(effect!==`inject-directive`&&effect!==`require-approval`&&effect!==`block`||out.push({id,effect,...typeof tool==`string`?{tool}:{},...typeof directive==`string`?{directive}:{},...typeof reason==`string`?{reason}:{}}))}return out}function bulletBlock(header,items){return[header,...items.map(x=>`- ${x}`)].join(`
6
+ `)?[{oldS:oldString,newS:newString},{oldS:toCRLF(oldString),newS:toCRLF(newString)},{oldS:toLF(oldString),newS:toLF(newString)}]:[{oldS:oldString,newS:newString},{oldS:toLF(oldString),newS:toLF(newString)},{oldS:toCRLF(oldString),newS:toCRLF(newString)}],candidates=ordered.filter((c,i)=>ordered.findIndex(d=>d.oldS===c.oldS)===i);for(let{oldS,newS}of candidates){let first=content.indexOf(oldS);if(first!==-1)return!replaceAll&&content.indexOf(oldS,first+oldS.length)!==-1?{ok:!1,error:`oldString is not unique; add more surrounding context or set replaceAll`}:{ok:!0,content:replaceAll?content.split(oldS).join(newS):content.replace(oldS,newS),replaced:replaceAll?content.split(oldS).length-1:1,region:regionAt(content,first,newS)}}let nearest=findNearest(content,oldString);return{ok:!1,error:`oldString not found in file — read_file and copy the exact text (including indentation)`,...nearest?{nearest}:{}}}const REGEX_SPECIALS=new Set([`.`,`+`,`^`,`$`,`{`,`}`,`(`,`)`,`|`,`[`,`]`,`\\`,`/`]);function globToRegExp(pattern){let re=``;for(let i=0;i<pattern.length;i++){let c=pattern[i];c===`*`?pattern[i+1]===`*`?pattern[i+2]===`/`?(re+=`(?:.*/)?`,i+=2):(re+=`.*`,i++):re+=`[^/]*`:c===`?`?re+=`[^/]`:re+=REGEX_SPECIALS.has(c)?`\\`+c:c}return RegExp(`^`+re+`$`,`i`)}function matchGlob(pathPosix,pattern){let re=globToRegExp(pattern);if(!pattern.includes(`/`)){let base=pathPosix.slice(pathPosix.lastIndexOf(`/`)+1);return re.test(base)}return re.test(pathPosix)}function filterByGlob(paths,pattern){return paths.filter(p=>matchGlob(p,pattern))}function normalizeScopeDir(raw){return(raw??``).split(`\\`).join(`/`).trim().replace(/^\.\/+/,``).replace(/^\/+|\/+$/g,``)}function isUnderScopeDir(pathPosix,scopeDir){if(!scopeDir)return!0;let p=pathPosix.split(`\\`).join(`/`);return p===scopeDir||p.startsWith(`${scopeDir}/`)}function defineTool(def){return{name:def.name,requires:def.requires??[],schema:{type:`function`,function:{name:def.name,description:def.description,parameters:def.parameters}},execute:def.execute}}const TOOL_NAME_ALIASES={list_dir:`list_files`,listdir:`list_files`,list_directory:`list_files`,list_directories:`list_files`,ls_dir:`list_files`,bash:`run_command`,shell:`run_command`,run_terminal_cmd:`run_command`,execute_command:`run_command`,run_shell_command:`run_command`,grep:`search_code`,codebase_search:`search_code`,search_files:`search_code`,find_files:`list_files`,glob_file_search:`list_files`,str_replace:`edit_file`,search_replace:`edit_file`,apply_patch:`edit_file`,write_to_file:`write_file`,create_file:`write_file`,delete:`delete_file`};function resolveToolAlias(name){return TOOL_NAME_ALIASES[name.trim().toLowerCase()]??name}var ToolRegistry=class{constructor(defs=[]){this.tools=new Map;for(let d of defs)this.register(d)}register(def){if(this.tools.has(def.name))throw Error(`duplicate tool '${def.name}'`);return this.tools.set(def.name,def),this}has(name){return this.tools.has(name)}get(name){return this.tools.get(name)}satisfies(def,caps){return def.requires.every(c=>caps.has(c))}toolsForCapabilities(caps){return[...this.tools.values()].filter(d=>this.satisfies(d,caps))}toolsFor(provider){return this.toolsForCapabilities(provider.capabilities)}schemasForCapabilities(caps){return this.toolsForCapabilities(caps).map(d=>d.schema)}schemasFor(provider){return this.schemasForCapabilities(provider.capabilities)}availableNames(provider){return this.toolsFor(provider).map(d=>d.name)}async dispatch(name,args,ctx){let resolved=resolveToolAlias(name),def=this.tools.get(resolved);return def?this.satisfies(def,ctx.caps.capabilities)?def.execute(args,ctx):{data:{ok:!1,error:`tool '${resolved}' is not available on this surface (missing capability: ${def.requires.filter(c=>!ctx.caps.capabilities.has(c)).join(`, `)}).`}}:{data:{ok:!1,error:`unknown tool '${name}'. Available tools: ${this.availableNames(ctx.caps).join(`, `)}.`}}}};const CURRENT_ENGINE_ID=`builderforce-v3`;function resolveEngineById(registry,id,defaultId=`builderforce-v3`){return registry[id??``]??registry[defaultId]}const THINK_ORDER=[`off`,`minimal`,`low`,`medium`,`high`,`xhigh`];function maxThink(a,b){return a&&THINK_ORDER.indexOf(a)>=THINK_ORDER.indexOf(b)?a:b}function coercePolicyGates(raw){if(!Array.isArray(raw))return[];let out=[];for(let g of raw){if(!g||typeof g!=`object`)continue;let{id,tool,effect,directive,reason}=g;typeof id==`string`&&(effect!==`inject-directive`&&effect!==`require-approval`&&effect!==`block`||out.push({id,effect,...typeof tool==`string`?{tool}:{},...typeof directive==`string`?{directive}:{},...typeof reason==`string`?{reason}:{}}))}return out}function bulletBlock(header,items){return[header,...items.map(x=>`- ${x}`)].join(`
7
7
  `)}function policyGateDirective(g){let scope=!g.tool||g.tool===`*`?`any tool`:`the \`${g.tool}\` tool`;switch(g.effect){case`inject-directive`:return(g.directive??``).trim();case`require-approval`:return`Before using ${scope}, pause and request explicit human approval${g.reason?` (${g.reason})`:``}.`;case`block`:return`Never use ${scope}${g.reason?` — ${g.reason}`:``}. Refuse and explain instead.`}}function renderPolicyDirectives(gates){let lines=(gates??[]).map(policyGateDirective).filter(Boolean);return lines.length===0?``:bulletBlock(`Governance (these gates are binding):`,lines)}function safeGitArg(v){return typeof v==`string`&&/^[\w./@-]+$/.test(v)?v:null}function safeRepoArg(repo){let arg=safeGitArg(repo);return arg&&!arg.split(/[\\/]/).includes(`..`)&&!arg.startsWith(`/`)?arg:null}function repoScopedScript(script,repo){let dir=safeRepoArg(repo);return dir?`cd "${dir}" || exit 1\n${script}`:script}function notARepoResult(action){return{data:{ok:!1,action,error:'not a git repository at the workspace root — this usually means the open folder CONTAINS the repositories rather than being one (several checkouts side by side). Do not conclude git is unavailable: call `list_files` to see the top-level directories, then re-run this tool with `repo` set to the one holding the code you are working on (e.g. { "repo": "my-project" }). If none of them is a checkout, say so plainly — file edits still work, only the git tools need a repository.'}}}const RESOLVE_BASE=`BASE="$(git remote show origin 2>/dev/null | sed -n 's/.*HEAD branch: //p')"; [ -n "$BASE" ] || BASE=main`;function buildGitCommand(action,opts){let path=safeGitArg(opts?.path),pathArg=path?` -- "${path}"`:``,repo=safeRepoArg(opts?.repo),scoped=cmd=>repo?`cd "${repo}" && ${cmd}`:cmd,scopedScript=lines=>repoScopedScript(lines.join(`
8
8
  `),opts?.repo);switch(action){case`status`:return scoped(`git status --short --branch`);case`diff`:return scoped(`git --no-pager diff${pathArg}`);case`history`:return scoped(`git --no-pager log --oneline -n ${Number.isFinite(opts?.limit)&&opts.limit>0?Math.min(Math.floor(opts.limit),200):30}${pathArg}`);case`sync_latest`:{let base=safeGitArg(opts?.baseBranch);return scopedScript([`set -e`,base?`BASE="${base}"`:RESOLVE_BASE,`git config user.email >/dev/null 2>&1 || git config user.email "agent@builderforce.ai"`,`git config user.name >/dev/null 2>&1 || git config user.name "Builderforce Agent"`,`git fetch origin "$BASE"`,`git merge --no-edit "origin/$BASE" || { git merge --abort; echo MERGE_CONFLICT; exit 3; }`,`echo "Synced with origin/$BASE"`])}case`undo`:return scopedScript([`[ -z "$(git status --porcelain)" ] || { echo DIRTY; exit 4; }`,`git reset --hard HEAD~1`,`echo "Undid the last commit (use git_redo to reapply)"`]);case`redo`:return scopedScript([`[ -z "$(git status --porcelain)" ] || { echo DIRTY; exit 4; }`,`git reset --hard "HEAD@{1}"`,`echo "Reapplied the last undone change"`])}}function gitToolResult(action,r){let out=(r.stdout??``).trim();return r.exitCode===3||/MERGE_CONFLICT/.test(out)?{data:{ok:!1,action,error:`merge conflict — the base branch has changes that conflict with your branch; the merge was aborted (working tree is clean). Resolve by editing the conflicting files, or ask a human.`,output:out}}:r.exitCode===4||/\bDIRTY\b/.test(out)?{data:{ok:!1,action,error:`you have uncommitted changes — commit or discard them before git_`+action+` (it refuses to discard uncommitted work).`}}:{data:{ok:r.ok,action,output:out.slice(0,2e4),...r.error?{error:r.error}:{}}}}const NOT_A_REPO=/not a git repository/i;function isNotARepo(r){return NOT_A_REPO.test(`${r.stdout??``} ${r.error??``}`)}async function runGitTool(action,opts,ctx){let r=await ctx.caps.shell.run(buildGitCommand(action,opts));if(isNotARepo(r)&&!opts.repo)return notARepoResult(action);let result=gitToolResult(action,r);return opts.repo&&result.data.ok&&(result.data.repo=opts.repo),result}const REPO_PARAM={type:`string`,description:`Optional subdirectory holding the repository, when the open folder CONTAINS checkouts rather than being one (e.g. "my-project"). Omit when the workspace root is itself the repo.`},gitStatusTool=defineTool({name:`git_status`,description:"Show the current branch and any uncommitted changes (git status). Use it to see what you have modified before committing, syncing, or finishing. If the open folder contains several checkouts rather than being one repo, pass `repo` to name the one you mean.",parameters:{type:`object`,properties:{repo:REPO_PARAM}},requires:[`shell`],execute:(args,ctx)=>runGitTool(`status`,{repo:typeof args.repo==`string`?args.repo:void 0},ctx)}),gitDiffTool=defineTool({name:`git_diff`,description:`Show the uncommitted diff of your working tree (optionally for one path). Use it to review exactly what you changed before finishing.`,parameters:{type:`object`,properties:{path:{type:`string`,description:`Optional repo-relative file/dir to scope the diff to.`},repo:REPO_PARAM}},requires:[`shell`],execute:(args,ctx)=>runGitTool(`diff`,{path:typeof args.path==`string`?args.path:void 0,repo:typeof args.repo==`string`?args.repo:void 0},ctx)}),gitHistoryTool=defineTool({name:`git_history`,description:`Show recent commit history (git log --oneline), optionally scoped to a path. Use it to understand how a file evolved before changing it.`,parameters:{type:`object`,properties:{path:{type:`string`,description:`Optional repo-relative file/dir to scope history to.`},limit:{type:`number`,description:`Max commits to return (default 30, max 200).`},repo:REPO_PARAM}},requires:[`shell`],execute:(args,ctx)=>runGitTool(`history`,{path:typeof args.path==`string`?args.path:void 0,limit:typeof args.limit==`number`?args.limit:void 0,repo:typeof args.repo==`string`?args.repo:void 0},ctx)}),gitSyncLatestTool=defineTool({name:`git_sync_latest`,description:`Fetch the latest base branch (e.g. main) and merge it into your working branch so you are NOT building on stale code. Run this FIRST, before editing — a branch created earlier can be far behind main, so its build fails against old dependencies and its pull request would revert newer work. On a merge conflict it safely aborts and tells you which to resolve.`,parameters:{type:`object`,properties:{baseBranch:{type:`string`,description:`Base branch to sync from. Defaults to the remote's default branch (usually main).`},repo:REPO_PARAM}},requires:[`shell`],execute:(args,ctx)=>runGitTool(`sync_latest`,{baseBranch:typeof args.baseBranch==`string`?args.baseBranch:void 0,repo:typeof args.repo==`string`?args.repo:void 0},ctx)}),gitUndoTool=defineTool({name:`git_undo`,description:`Undo your most recent commit (keeps the change recoverable — use git_redo to reapply). Refuses if you have uncommitted changes, so it can never discard unsaved work. Use it to back out a change that was wrong.`,parameters:{type:`object`,properties:{repo:REPO_PARAM}},requires:[`shell`],execute:(args,ctx)=>runGitTool(`undo`,{repo:typeof args.repo==`string`?args.repo:void 0},ctx)}),gitRedoTool=defineTool({name:`git_redo`,description:`Reapply the change you most recently undid with git_undo (reflog redo). Refuses if you have uncommitted changes.`,parameters:{type:`object`,properties:{repo:REPO_PARAM}},requires:[`shell`],execute:(args,ctx)=>runGitTool(`redo`,{repo:typeof args.repo==`string`?args.repo:void 0},ctx)});function shellQuote(v){return`'${v.replace(/'/g,`'\\''`)}'`}function commitPathCandidates(path,repo){let out=[path],dir=safeRepoArg(repo);if(dir){let prefix=`${dir.replace(/\/+$/,``)}/`;path.startsWith(prefix)&&out.push(path.slice(prefix.length));let leaf=dir.split(`/`).filter(Boolean).pop();leaf&&path.startsWith(`${leaf}/`)&&out.push(path.slice(leaf.length+1))}return[...new Set(out)].filter(p=>p.trim()!==``)}function buildCommitCommand(opts){let branch=safeGitArg(opts.branch),resolveLines=opts.paths.map((p,i)=>`P${i}="$(pick ${commitPathCandidates(p,opts.repo).map(shellQuote).join(` `)})" || MISSING="$MISSING ${shellQuote(p).slice(1,-1)}"`),paths=opts.paths.map((_,i)=>`"$P${i}"`).join(` `);return[`set -e`,RESOLVE_BASE,`git config user.email >/dev/null 2>&1 || git config user.email "agent@builderforce.ai"`,`git config user.name >/dev/null 2>&1 || git config user.name "Builderforce Agent"`,`CUR="$(git rev-parse --abbrev-ref HEAD)"`,...branch?[`git rev-parse --verify --quiet "${branch}" >/dev/null && git checkout "${branch}" || git checkout -b "${branch}"`]:opts.allowBaseBranch?[]:[`[ "$CUR" != "$BASE" ] || { echo ON_BASE_BRANCH; exit 5; }`],`pick() { for c in "$@"; do if [ -e "$c" ] || git ls-files --error-unmatch -- "$c" >/dev/null 2>&1; then printf %s "$c"; return 0; fi; done; return 1; }`,`MISSING=""`,...resolveLines,`[ -z "$MISSING" ] || { echo "MISSING_PATHS:$MISSING"; exit 8; }`,`git add -- ${paths}`,`git diff --cached --quiet && { echo NOTHING_STAGED; exit 6; }`,`git commit -m ${shellQuote(opts.message)}`,`echo "Committed on $(git rev-parse --abbrev-ref HEAD): $(git rev-parse --short HEAD)"`].join(`
9
9
  `)}function buildPushCommand(opts){return[`set -e`,RESOLVE_BASE,`CUR="$(git rev-parse --abbrev-ref HEAD)"`,...opts.allowBaseBranch?[]:[`[ "$CUR" != "$BASE" ] || { echo ON_BASE_BRANCH; exit 5; }`],`git push -u origin "$CUR"`,`echo "Pushed $CUR to origin"`,`git status --short --branch 2>/dev/null || true`].join(`
@@ -12,5 +12,5 @@ const MODEL_ROLES=[`plan`,`code`,`verify`,`explore`,`chat`,`utility`],MODEL_ROLE
12
12
  `)}function publishToolResult(action,r){let out=(r.stdout??``).trim(),fail=error=>({data:{ok:!1,action,error,output:out}});if(r.exitCode===5||/\bON_BASE_BRANCH\b/.test(out))return fail(action===`push`?"you are on the BASE branch (main/master) and `allowBaseBranch` was not set — pushing here bypasses pull-request review. Open a pull request instead (git_commit with a `branch`, then open_pull_request). If the human has explicitly asked you to push the base branch, or your session instructions make you the reviewer of your own change and you have self-reviewed it, re-call with allowBaseBranch:true; they will be prompted to approve it.":"you are on the BASE branch (main/master) and `allowBaseBranch` was not set — committing here bypasses pull-request review. Pass `branch` to git_commit to work on a ticket branch (it is created for you), then open_pull_request. If the human has explicitly asked you to commit to the base branch directly, or your session instructions make you the reviewer of your own change and you have self-reviewed it, re-call with allowBaseBranch:true; they will be prompted to approve it.");if(r.exitCode===6||/\bNOTHING_STAGED\b/.test(out))return fail(`none of the named paths have uncommitted changes — nothing was committed. Run git_status to see what actually differs; do not report a commit that did not happen.`);let missing=/MISSING_PATHS:([^\n]*)/.exec(out);if(r.exitCode===8||missing){let named=(missing?.[1]??``).trim();return fail(`these paths do not exist in the repository, so nothing was committed:${named?` ${named}`:``}. \`paths\` are relative to the REPOSITORY root — when you pass \`repo\`, that means relative to the repo directory, NOT to the workspace root your file tools use. Run git_status (with the same \`repo\`) and copy the paths it prints.`)}return r.exitCode===9||/\bNOTHING_TO_CLEAN\b/.test(out)?fail("you are already on the base branch and no ticket branch was named — there is nothing to clean up. Pass `branch` to name the merged branch to delete."):r.exitCode===10||/\bNOT_MERGED\b/.test(out)?fail(`that branch's commits are NOT in the base branch, so it was left alone — deleting it would destroy unmerged work. If the pull request was SQUASH-merged (the commits are in main under a new hash and the remote branch still exists), re-call with force:true to delete it anyway.`):r.exitCode===11||/\bNO_SUCH_BRANCH\b/.test(out)?fail(`no local branch by that name — it has already been deleted. Nothing to do.`):r.exitCode===4||/\bDIRTY\b/.test(out)?fail(`you have uncommitted changes — commit or discard them before cleaning up (this refuses to discard uncommitted work).`):r.exitCode===7||/\bNO_GH_CLI\b/.test(out)?fail("the GitHub CLI (`gh`) is not installed or not on PATH, so a pull request cannot be opened from here. The branch is committed and pushed; tell the human to open the PR, and give them the branch name."):{data:{ok:r.ok,action,output:out.slice(0,2e4),...r.error?{error:r.error}:{}}}}async function runPublishTool(action,command,repo,ctx){let scoped=repoScopedScript(command,repo),r=await ctx.caps.shell.run(scoped);if(isNotARepo(r)&&!repo)return notARepoResult(action);let result=publishToolResult(action,r);return repo&&result.data.ok&&(result.data.repo=repo),result}const GIT_TOOLS=[gitStatusTool,gitDiffTool,gitHistoryTool,gitSyncLatestTool,gitUndoTool,gitRedoTool,defineTool({name:`git_commit`,description:"Commit the files you changed. You must list the exact `paths` to commit — the working tree is shared with the human using it, so committing everything would sweep up their unrelated in-flight work; run git_status/git_diff first if you are unsure what you touched. The DEFAULT route is a TICKET BRANCH: pass `branch` to name it (created for you if it does not exist), then use open_pull_request so the work is reviewed. Without `branch`, a commit is refused while you are on the base branch (main/master) — unless the human has EXPLICITLY asked you to commit to main directly, or your session instructions make you the reviewer of your own change (a local editor session, after you have verified and self-reviewed it); then pass allowBaseBranch:true (the human is prompted to approve it) and follow with git_push allowBaseBranch:true.",parameters:{type:`object`,properties:{message:{type:`string`,description:`Commit message. One line saying what changed and why.`},paths:{type:`array`,items:{type:`string`},description:`Repo-relative paths to commit. Exactly the files YOU changed — never a catch-all.`},branch:{type:`string`,description:`Ticket branch to commit on, created if new (e.g. "ticket/2394-mobile-board-height"). The default route; required when on the base branch unless allowBaseBranch is set.`},allowBaseBranch:{type:`boolean`,description:`Set ONLY when the human explicitly asked to commit to the base branch (main/master) directly. Default false, which refuses on the base branch and tells you to use a ticket branch.`},repo:REPO_PARAM},required:[`message`,`paths`]},requires:[`git.write`],execute:(args,ctx)=>{let message=typeof args.message==`string`?args.message.trim():``;if(!message)return Promise.resolve({data:{ok:!1,action:`commit`,error:`message is required`}});let paths=Array.isArray(args.paths)?args.paths.filter(p=>typeof p==`string`&&p.trim()!==``):[];if(paths.length===0)return Promise.resolve({data:{ok:!1,action:`commit`,error:`paths is required — list the exact files you changed. Run git_status to see them. Do not pass '.' or '-A': the working tree may hold changes that are not yours.`}});let repo=typeof args.repo==`string`?args.repo:void 0;return runPublishTool(`commit`,buildCommitCommand({message,paths,branch:typeof args.branch==`string`?args.branch:void 0,allowBaseBranch:args.allowBaseBranch===!0,repo}),repo,ctx)}}),defineTool({name:`git_push`,description:"Push the current branch to origin (setting its upstream on the first push), then report where it landed (`git status --short --branch`). Pushing the BASE branch (main/master) is refused unless you pass allowBaseBranch — that path skips pull-request review, so only set it when the human has explicitly asked for it, or when your session instructions make you the reviewer of your own change (a local editor session, after verifying and self-reviewing it); the human is prompted to approve it either way. Otherwise the route is a ticket branch: git_commit with a `branch`, git_push, then open_pull_request.",parameters:{type:`object`,properties:{allowBaseBranch:{type:`boolean`,description:`Set ONLY when the human explicitly asked to push the base branch directly. Default false, which refuses and tells you to open a pull request.`},repo:REPO_PARAM}},requires:[`git.write`],execute:(args,ctx)=>{let repo=typeof args.repo==`string`?args.repo:void 0;return runPublishTool(`push`,buildPushCommand({allowBaseBranch:args.allowBaseBranch===!0,repo}),repo,ctx)}}),defineTool({name:`open_pull_request`,description:"Open a pull request for the current ticket branch against the base branch, pushing it first if it has no upstream yet. This is how a change gets REVIEWED — prefer it over pushing the base branch, and say so when someone asks you to push directly. Pass `reviewers` to request review from specific people or teams. Returns the pull request URL; report that URL rather than claiming the work is shipped, because it is not until the PR is merged.",parameters:{type:`object`,properties:{title:{type:`string`,description:`Pull request title — what this change does, in one line.`},body:{type:`string`,description:`Pull request description: what changed, why, and how a reviewer can verify it.`},base:{type:`string`,description:`Base branch to target. Defaults to the remote's default branch (usually main).`},reviewers:{type:`array`,items:{type:`string`},description:`GitHub usernames or org/team slugs to request review from.`},repo:REPO_PARAM},required:[`title`,`body`]},requires:[`git.write`],execute:(args,ctx)=>{let title=typeof args.title==`string`?args.title.trim():``,body=typeof args.body==`string`?args.body:``;if(!title)return Promise.resolve({data:{ok:!1,action:`pull_request`,error:`title is required`}});let repo=typeof args.repo==`string`?args.repo:void 0,reviewers=Array.isArray(args.reviewers)?args.reviewers.filter(r=>typeof r==`string`):void 0;return runPublishTool(`pull_request`,buildPullRequestCommand({title,body,base:typeof args.base==`string`?args.base:void 0,reviewers,repo}),repo,ctx)}}),defineTool({name:`git_cleanup_merged`,description:`Clean up after work that has LANDED: switch to the base branch, fast-forward it to origin, and delete the merged ticket branch locally and on origin. Call it once the pull request is merged (or once you have pushed the base branch directly) so the checkout is not left sitting on a dead branch with a stale base — do not hand-roll this with run_command. It is IDEMPOTENT: a remote branch the host already deleted on merge is the expected state, not an error. It REFUSES to delete a branch whose commits are not in the base branch, and refuses to run on a dirty working tree, so it can never destroy unmerged or uncommitted work. If the pull request was SQUASH-merged and the remote branch still exists, git cannot see the merge — re-call with force:true.`,parameters:{type:`object`,properties:{branch:{type:`string`,description:`The merged branch to delete. Defaults to the branch you are currently on.`},baseBranch:{type:`string`,description:`Branch to return to and update. Defaults to the remote's default branch (usually main).`},force:{type:`boolean`,description:`Delete the branch even though git cannot see its commits in the base branch. ONLY for a squash-merged pull request you have confirmed is merged.`},repo:REPO_PARAM}},requires:[`git.write`],execute:(args,ctx)=>{let repo=typeof args.repo==`string`?args.repo:void 0;return runPublishTool(`cleanup`,buildCleanupCommand({branch:typeof args.branch==`string`?args.branch:void 0,baseBranch:typeof args.baseBranch==`string`?args.baseBranch:void 0,force:args.force===!0,repo}),repo,ctx)}})],SYMBOL_KINDS=[`function`,`method`,`class`,`interface`,`type`,`enum`,`const`,`struct`,`trait`,`module`,`table`,`heading`],LANGUAGE_BY_EXT={ts:`js`,tsx:`js`,js:`js`,jsx:`js`,mjs:`js`,cjs:`js`,mts:`js`,cts:`js`,py:`python`,go:`go`,rs:`rust`,java:`oo`,kt:`oo`,kts:`oo`,cs:`oo`,swift:`oo`,php:`oo`,scala:`oo`,rb:`ruby`,sql:`sql`,md:`markdown`,mdx:`markdown`};function extensionOf(path){let base=path.slice(path.lastIndexOf(`/`)+1),dot=base.lastIndexOf(`.`);return dot>0?base.slice(dot+1).toLowerCase():``}function languageOf(path){return LANGUAGE_BY_EXT[extensionOf(path.replace(/\\/g,`/`))]}function isSymbolIndexable(path){return languageOf(path)!==void 0}function group(m,n){return m[n]??``}function eachLine(lines,out,visit){for(let i=0;i<lines.length&&out.length<400;i+=1)visit(lines[i]??``,i+1)}const ID=`[A-Za-z_$][\\w$]*`,JS_RULES=[{re:RegExp(`^(\\s*export\\s+)?(?:declare\\s+)?(?:default\\s+)?(?:async\\s+)?function\\s*\\*?\\s*(${ID})`),kind:`function`},{re:RegExp(`^(\\s*export\\s+)?(?:declare\\s+)?(?:default\\s+)?(?:abstract\\s+)?class\\s+(${ID})`),kind:`class`},{re:RegExp(`^(\\s*export\\s+)?(?:declare\\s+)?interface\\s+(${ID})`),kind:`interface`},{re:RegExp(`^(\\s*export\\s+)?(?:declare\\s+)?type\\s+(${ID})\\s*[<=]`),kind:`type`},{re:RegExp(`^(\\s*export\\s+)?(?:declare\\s+)?(?:const\\s+)?enum\\s+(${ID})`),kind:`enum`},{re:RegExp(`^(\\s*export\\s+)?(?:declare\\s+)?(?:const|let|var)\\s+(${ID})`),kind:`const`}],JS_METHOD=RegExp(`^(?:\\t| | )(?:(?:public|private|protected|static|readonly|override|abstract|async|get|set)\\s+)*\\*?\\s*(${ID})\\s*(?:<[^>]*>)?\\s*\\(.*\\)?[^;]*\\{\\s*$`),JS_KEYWORDS=new Set([`if`,`for`,`while`,`switch`,`catch`,`return`,`function`,`with`,`do`,`else`,`try`]);function extractJs(lines,out){let inClass=!1;eachLine(lines,out,(line,lineNo)=>{/^\}/.test(line)&&(inClass=!1);for(let rule of JS_RULES){let m=rule.re.exec(line);if(!m)continue;let exported=!!m[1];if(!(!exported&&/^\s/.test(line))){out.push({name:group(m,2),kind:rule.kind,line:lineNo,exported}),rule.kind===`class`&&(inClass=!0);return}}if(!inClass)return;let method=JS_METHOD.exec(line),name=method?group(method,1):``;name&&!JS_KEYWORDS.has(name)&&out.push({name,kind:`method`,line:lineNo,exported:!1})})}function extractPython(lines,out){eachLine(lines,out,(line,lineNo)=>{let m=/^(\s*)(?:async\s+)?(def|class)\s+([A-Za-z_]\w*)/.exec(line);if(!m)return;let indent=group(m,1);if(indent.length>4&&!indent.startsWith(` `))return;let name=group(m,3),kind=group(m,2)===`class`?`class`:indent.length>0?`method`:`function`;out.push({name,kind,line:lineNo,exported:!name.startsWith(`_`)})})}function extractGo(lines,out){eachLine(lines,out,(line,lineNo)=>{let fn=/^func\s+(\([^)]*\)\s*)?([A-Za-z_]\w*)/.exec(line);if(fn){let name=group(fn,2);out.push({name,kind:fn[1]?`method`:`function`,line:lineNo,exported:/^[A-Z]/.test(name)});return}let ty=/^type\s+([A-Za-z_]\w*)\s+(struct|interface)?/.exec(line);if(!ty)return;let name=group(ty,1),word=group(ty,2),kind=word===`struct`?`struct`:word===`interface`?`interface`:`type`;out.push({name,kind,line:lineNo,exported:/^[A-Z]/.test(name)})})}function extractRust(lines,out){eachLine(lines,out,(line,lineNo)=>{let m=/^(\s*)(pub(?:\([^)]*\))?\s+)?(?:(?:async|unsafe|const|extern(?:\s+"[^"]*")?)\s+)*(fn|struct|enum|trait|mod|type)\s+([A-Za-z_]\w*)/.exec(line);if(!m)return;let word=group(m,3),kind=word===`fn`?group(m,1).length>0?`method`:`function`:word===`mod`?`module`:word;out.push({name:group(m,4),kind,line:lineNo,exported:!!m[2]})})}const OO_TYPE=/^\s*(?:(?:public|private|protected|internal|static|final|abstract|sealed|open|data|partial|export|inline|value)\s+)*(class|interface|enum|record|object|struct|protocol|trait)\s+([A-Za-z_]\w*)/,OO_FUNC=/^\s*(?:(?:public|private|protected|internal|static|final|override|open|suspend|inline|abstract)\s+)*(?:fun|func|function)\s+([A-Za-z_]\w*)/;function extractOo(lines,out){eachLine(lines,out,(line,lineNo)=>{let exported=!/\bprivate\b/.test(line),ty=OO_TYPE.exec(line);if(ty){let word=group(ty,1),kind=word===`interface`||word===`protocol`?`interface`:word===`enum`?`enum`:word===`struct`?`struct`:word===`trait`?`trait`:`class`;out.push({name:group(ty,2),kind,line:lineNo,exported});return}let fn=OO_FUNC.exec(line);fn&&out.push({name:group(fn,1),kind:/^\s/.test(line)?`method`:`function`,line:lineNo,exported})})}function extractRuby(lines,out){eachLine(lines,out,(line,lineNo)=>{let ty=/^\s*(class|module)\s+([A-Z]\w*(?:::\w+)*)/.exec(line);if(ty){out.push({name:group(ty,2),kind:group(ty,1)===`module`?`module`:`class`,line:lineNo,exported:!0});return}let fn=/^(\s*)def\s+(?:self\.)?([A-Za-z_]\w*[?!=]?)/.exec(line);fn&&out.push({name:group(fn,2),kind:group(fn,1).length>0?`method`:`function`,line:lineNo,exported:!0})})}const SQL_CREATE=/^\s*create\s+(?:or\s+replace\s+)?(?:unique\s+)?(table|view|materialized\s+view|function|procedure|index|type|trigger)\s+(?:concurrently\s+)?(?:if\s+not\s+exists\s+)?([\w."]+)/i;function extractSql(lines,out){eachLine(lines,out,(line,lineNo)=>{let m=SQL_CREATE.exec(line);if(!m)return;let word=group(m,1).toLowerCase(),kind=word===`function`||word===`procedure`||word===`trigger`?`function`:word===`index`||word===`type`?`type`:`table`;out.push({name:group(m,2).replace(/"/g,``),kind,line:lineNo,exported:!0})})}function extractMarkdown(lines,out){let fenced=!1;eachLine(lines,out,(line,lineNo)=>{if(/^\s*(```|~~~)/.test(line)){fenced=!fenced;return}if(fenced)return;let m=/^(#{1,4})\s+(.+?)\s*#*\s*$/.exec(line);m&&out.push({name:`${group(m,1)} ${group(m,2).slice(0,120)}`,kind:`heading`,line:lineNo,exported:!0})})}const EXTRACTORS={js:extractJs,python:extractPython,go:extractGo,rust:extractRust,oo:extractOo,ruby:extractRuby,sql:extractSql,markdown:extractMarkdown};function extractSymbols(path,content){let language=languageOf(path);if(!language)return[];let out=[];return EXTRACTORS[language](content.split(/\r?\n/),out),out}function formatSymbol(symbol){return`L${symbol.line} ${symbol.kind} ${symbol.name}${symbol.exported&&symbol.kind!==`heading`?` (export)`:``}`}function asKind(v){return typeof v==`string`&&SYMBOL_KINDS.includes(v)?v:void 0}const SYMBOL_TOOLS=[defineTool({name:`find_symbol`,description:"Find where a function, class, method, type, constant, SQL table or Markdown heading is DEFINED, from the workspace's symbol index — one instant call instead of search_code plus reading files to tell the definition from its call sites. Pass `query` as the symbol name or part of it (case-insensitive; exact matches rank first). Each match is `path:line kind name`; then read_file with `offset` a few lines above that line and a small `limit`. Narrow with `path` (a subdirectory) or `kind`. For USAGES, string literals or config values (not definitions), use search_code instead.",parameters:{type:`object`,properties:{query:{type:`string`,description:`Symbol name or a distinctive part of it, e.g. "buildGitCommand" or "GitCommand".`},path:{type:`string`,description:`Optional repo-relative subdirectory to restrict to, e.g. "api/src".`},kind:{type:`string`,enum:[...SYMBOL_KINDS],description:`Optional: only this kind of definition.`},limit:{type:`number`,description:`Max matches (default 20, max 50).`}},required:[`query`]},requires:[`repo.symbols`],async execute(args,ctx){let query=typeof args.query==`string`?args.query.trim():``;if(!query)return{data:{ok:!1,error:`query is required`}};let scope=typeof args.path==`string`&&args.path.trim()?args.path.trim():void 0,kind=asKind(args.kind),requested=typeof args.limit==`number`&&Number.isFinite(args.limit)?Math.floor(args.limit):20,limit=Math.min(50,Math.max(1,requested)),r=await ctx.caps.symbols.find(query,{scope,kind,limit});if(!r.ok)return{data:r};let data={ok:!0,query,total:r.total??0,truncated:r.truncated===!0,matches:(r.matches??[]).map(m=>`${m.path}:${m.line} ${m.kind} ${m.name}${m.exported&&m.kind!==`heading`?` (export)`:``}`),indexedFiles:r.indexedFiles};return(r.total??0)===0?data.note=r.partialIndex?`No definition named like "${query}" in the indexed files — but the index is PARTIAL (file cap reached), so this is not proof it does not exist. Try search_code${scope?``:" with a `path`"}.`:`No definition named like "${query}"${scope?` under "${scope}"`:``}. It may be defined in a form the index does not recognise (a re-export, an object property, a generated file) — use search_code for the exact text.`:r.truncated&&(data.note=`Showing ${r.matches?.length??0} of ${r.total} matches — pass a longer \`query\`, a \`path\` or a \`kind\` to narrow.`),{data}}}),defineTool({name:`file_outline`,description:"List what a file DEFINES — functions, classes, methods, types, constants, or a Markdown file's headings — each with its line number, without reading the file's contents. Call this BEFORE paging through a large file: find the symbol you need, then read_file with `offset` at its line and a small `limit`, instead of reading 2,000-line windows until you reach it. Pass `kind` to list only one kind of definition.",parameters:{type:`object`,properties:{path:{type:`string`,description:`Repo-relative file path, e.g. "api/src/service.ts".`},kind:{type:`string`,enum:[...SYMBOL_KINDS],description:`Optional: only this kind of definition.`}},required:[`path`]},requires:[`repo.read`,`repo.symbols`],async execute(args,ctx){let path=typeof args.path==`string`?args.path.trim():``;if(!path)return{data:{ok:!1,error:`path is required`}};let kind=asKind(args.kind),file=await ctx.caps.repoRead.readFile(path);if(!file.ok)return{data:file};let content=file.content??``,all=extractSymbols(path,content).filter(s=>!kind||s.kind===kind),shown=all.slice(0,150),data={ok:!0,path:file.path??path,totalLines:content.split(`
13
13
  `).length,total:all.length,symbols:shown.map(formatSymbol)};if(all.length===0)data.note=kind?`No ${kind} definitions found in ${path}.`:`No definitions recognised in ${path} (unsupported language, or a data/config file) — read_file it directly.`;else if(all.length>shown.length){let lastLine=shown.at(-1)?.line??0;data.note=`Showing the first ${shown.length} of ${all.length} definitions (through line ${lastLine}). Pass \`kind\` to list one kind, or find_symbol for a specific name.`}return{data}}})],MAP_DEFAULT_TOKENS=1500,MAP_MAX_TOKENS=8e3,SEMANTIC_TOOLS=[defineTool({name:`semantic_search`,description:`Find code by MEANING or by name in one call — returns whole functions/classes (path, line range, symbol, snippet) ranked by identifier-aware keyword match (resolveMembership matches "membership") plus local embeddings. Use it FIRST for questions phrased in words — "where is X handled", "how does Y work", "what validates Z" — where search_code needs an exact substring you do not know yet. Then read_file only the hit you will edit, at its line range. For exact strings, usages and config values use search_code; for a known symbol's definition use find_symbol.`,parameters:{type:`object`,properties:{query:{type:`string`,description:`What you are looking for, in words or identifiers, e.g. "stripe webhook to ledger".`},path:{type:`string`,description:`Optional repo-relative subdirectory to restrict to, e.g. "api/src".`},limit:{type:`number`,description:`Max results (default 8, max 25).`}},required:[`query`]},requires:[`repo.semantic`],async execute(args,ctx){let query=typeof args.query==`string`?args.query.trim():``;if(!query)return{data:{ok:!1,error:`query is required`}};let scope=typeof args.path==`string`&&args.path.trim()?args.path.trim():void 0,requested=typeof args.limit==`number`&&Number.isFinite(args.limit)?Math.floor(args.limit):8,limit=Math.min(25,Math.max(1,requested)),r=await ctx.caps.semantic.search(query,{scope,limit});if(!r.ok)return{data:r};let results=(r.results??[]).map(h=>({at:`${h.path}:${h.startLine}-${h.endLine}`,symbol:h.symbol??void 0,kind:h.kind,via:h.source,snippet:h.snippet})),data={ok:!0,query,total:results.length,results};return r.indexing?data.note=`The index is still being built — these results cover only the files scanned so far. A miss is not proof of absence yet; fall back to search_code if needed.`:results.length===0&&(data.note=`Nothing matched "${query}"${scope?` under "${scope}"`:``}. Rephrase with likely identifier words, drop \`path\`, or use search_code for an exact string.`),{data}}}),defineTool({name:`repo_map`,description:"The repository's SHAPE in one call: files ordered by how much the rest of the code depends on them, each with its most-referenced definitions as `line: signature`. Call it once at the start of unfamiliar work instead of exploring with list_files and reads; pass `focus` (paths you are working in) to rank that area first.",parameters:{type:`object`,properties:{focus:{type:`array`,items:{type:`string`},description:`Optional repo-relative paths to rank first, e.g. ["api/src/billing"].`},maxTokens:{type:`number`,description:`Budget for the map (default ${MAP_DEFAULT_TOKENS}, max ${MAP_MAX_TOKENS}).`}}},requires:[`repo.semantic`],async execute(args,ctx){let focus=Array.isArray(args.focus)?args.focus.filter(f=>typeof f==`string`&&f.trim()!==``):[],requested=typeof args.maxTokens==`number`&&Number.isFinite(args.maxTokens)?Math.floor(args.maxTokens):MAP_DEFAULT_TOKENS,maxTokens=Math.min(MAP_MAX_TOKENS,Math.max(200,requested)),r=await ctx.caps.semantic.repoMap({maxTokens,focus});if(!r.ok)return{data:r};let data={ok:!0,map:r.map??``};return r.indexing&&(data.note=`The index is still being built — the map covers only the files scanned so far.`),{data}}})],listFilesTool=defineTool({name:`list_files`,description:"List repo files (recursively) on the ticket branch so you can discover the existing codebase before editing. Optionally pass `path` to scope to a subdirectory. To FIND A FILE BY NAME, pass `glob` — e.g. `ROADMAP.md` (matches that filename at any depth, case-insensitive) or `src/**/*.test.ts`. Use `glob` instead of concluding a file is missing: a large repo's unfiltered listing is summarized to directories, but a `glob` always returns the matching files in full.",parameters:{type:`object`,properties:{path:{type:`string`,description:`Optional repo-relative subdirectory to scope to, e.g. "src/components".`},glob:{type:`string`,description:`Optional filename/glob filter, e.g. "ROADMAP.md", "*.md", or "src/**/*.ts". Case-insensitive; a name with no "/" matches the basename at any depth.`}}},requires:[`repo.read`],async execute(args,ctx){let sub=typeof args.path==`string`?args.path:void 0,glob=typeof args.glob==`string`&&args.glob.trim()?args.glob.trim():void 0,r=await ctx.caps.repoRead.listFiles(sub,glob);return glob&&r.ok&&(r.paths?.length??0)===0?{data:{...r,note:`No file matches glob "${glob}". Try a broader pattern (e.g. "*${glob.replace(/[*?/]/g,``)}*"), or list_files without a glob to see the tree. 0 matches means no such file exists — do not claim one is missing without trying a broader glob first.`}}:{data:r}}});defineTool({name:`search_code`,description:'Search the repo for a string/symbol in one call — use this FIRST to find where something is referenced instead of reading files one by one. Returns matching file paths with line fragments. Pass `query` as an EXACT substring/regex (a symbol, import path, or config key), NOT a natural-language phrase — a multi-word phrase rarely appears verbatim on one line and will match nothing. On a large monorepo, scope the search with `path` (a subdirectory) to search just that subtree. 0 results with `truncated:false` means the term does not appear (so "remove all references to X" then means there is nothing to remove — say so, do not invent a change); 0 results with `truncated:true` means the search was cut short before scanning everything — narrow it with `path` or a more specific `query` and try again, do NOT conclude the term is absent. Then read_file the matches you intend to edit.',parameters:{type:`object`,properties:{query:{type:`string`,description:`Exact text or symbol to find, e.g. a model id, function name, import path, or config key. NOT a natural-language phrase.`},path:{type:`string`,description:`Optional repo-relative subdirectory to restrict the search to, e.g. "packages/brain-ui". Use this to avoid truncation on a big repo.`}},required:[`query`]},requires:[`repo.search`],async execute(args,ctx){let query=typeof args.query==`string`?args.query:``;if(!query.trim())return{data:{ok:!1,error:`query is required`}};let scope=typeof args.path==`string`&&args.path.trim()?args.path.trim():void 0,r=await ctx.caps.repoRead.searchCode(query,scope);if(r.ok&&r.total===0){let note=r.truncated?`Search was truncated before scanning the whole${scope?` subtree`:` repo`} — this is NOT proof the term is absent. Re-run scoped to a subdirectory via \`path\`${scope?` (a narrower one)`:``}, or use a more specific \`query\`.`:`No matches${scope?` under "${scope}"`:``} — the term is not referenced${scope?" there (try without `path` to search the whole repo)":``}. If the task was to remove/replace it, there is nothing to change; say so instead of inventing an edit.`;return{data:{...r,note}}}return{data:r}}});const READ_DEFAULT_LINE_LIMIT=2e3;function windowFileContent(content,opts){let lines=content.split(`
14
14
  `),totalLines=lines.length,start=opts?.offset&&opts.offset>1?Math.min(Math.floor(opts.offset),totalLines+1):1,limit=opts?.limit&&opts.limit>0?Math.floor(opts.limit):READ_DEFAULT_LINE_LIMIT,slice=lines.slice(start-1,start-1+limit),end=start-1+slice.length;return{content:slice.join(`
15
- `),truncated:end<totalLines,totalLines,offset:start,returnedLines:slice.length}}defineTool({name:`read_file`,description:`Read a repo file on the ticket branch. Returns up to `+READ_DEFAULT_LINE_LIMIT+" lines at a time: a large file comes back as a paginated line window (never a hard failure), and the result's `truncated`/`totalLines` tell you when more remains — read the next chunk by calling again with `offset`. Always read a file before editing it so you preserve existing code and only change what is needed.",parameters:{type:`object`,properties:{path:{type:`string`,description:`Repo-relative path, e.g. "src/feature.ts".`},offset:{type:`number`,description:`1-based line to start reading from (for paging through a large file). Default 1.`},limit:{type:`number`,description:`Max lines to return. Default ${READ_DEFAULT_LINE_LIMIT}. Read the next window with offset = previous offset + returned lines.`}},required:[`path`]},requires:[`repo.read`],async execute(args,ctx){let path=typeof args.path==`string`?args.path:``;if(!path)return{data:{ok:!1,error:`path is required`}};let offset=typeof args.offset==`number`&&args.offset>0?Math.floor(args.offset):void 0,limit=typeof args.limit==`number`&&args.limit>0?Math.floor(args.limit):void 0,r=await ctx.caps.repoRead.readFile(path);if(!r.ok)return{data:r};let win=windowFileContent(r.content??``,{offset,limit}),data={ok:!0,path:r.path??path,content:win.content,truncated:win.truncated||r.truncated===!0,totalLines:win.totalLines,offset:win.offset};if(win.truncated){let lastLine=win.offset+win.returnedLines-1;data.note=`Showing lines ${win.offset}–${lastLine} of ${win.totalLines}. To continue, call read_file again with offset ${lastLine+1}.`}return{data}}});const writeFileTool=defineTool({name:`write_file`,description:`Create or update a file, writing its complete contents. How the write lands depends on the surface: in an editor/on-prem workspace it edits the file in place; in a cloud/review run it is staged on the ticket branch as a reviewable pending change. Do NOT narrate a specific mechanism (e.g. "opened a PR") — just state what the file now contains. Use once per deliverable file. Provide the FULL file content.`,parameters:{type:`object`,properties:{path:{type:`string`,description:`Repo-relative path, e.g. "src/feature.ts".`},content:{type:`string`,description:`Complete file content (no placeholders).`},summary:{type:`string`,description:`One-line description of the change.`}},required:[`path`,`content`]},requires:[`repo.write`],async execute(args,ctx){let path=typeof args.path==`string`?args.path:``,content=typeof args.content==`string`?args.content:``,summary=typeof args.summary==`string`?args.summary:void 0;if(!path||!content)return{data:{ok:!1,error:`path and content are both required`}};let r=await ctx.caps.repoWrite.writeFile(path,content,summary);return{data:r.ok?{ok:!0,branch:r.branch,commitUrl:r.commitUrl}:{ok:!1,error:r.error}}}}),deleteFileTool=defineTool({name:`delete_file`,description:`Remove a file from the ticket branch so it does NOT ship in the pull request. Use this to clean up dead code: a stub/placeholder, an unreferenced file, or a file a PRIOR pass on this branch created that should not be part of the final change. The "Files already on this branch" list in your context shows what a prior pass left — reconcile against it. Verify the file is genuinely unused (search_code for its exports) before deleting. Deleting a file not on the branch is a no-op (reported back), not an error.`,parameters:{type:`object`,properties:{path:{type:`string`,description:`Repo-relative path to remove, e.g. "src/utils/email.ts".`},reason:{type:`string`,description:`One-line why this file should not ship (e.g. "stub superseded by existing email infra").`}},required:[`path`]},requires:[`repo.delete`],async execute(args,ctx){let path=typeof args.path==`string`?args.path:``;if(!path)return{data:{ok:!1,error:`path is required`}};let reason=typeof args.reason==`string`?args.reason:void 0,r=await ctx.caps.repoWrite.deleteFile(path,reason);return r.ok&&r.deleted===!1?{data:{ok:!0,deleted:!1,note:r.note}}:{data:r.ok?{ok:!0,deleted:!0,branch:r.branch,commitUrl:r.commitUrl}:{ok:!1,error:r.error}}}}),editFileTool=defineTool({name:`edit_file`,description:"Make a surgical in-place edit to an existing file on the ticket branch: replace an exact snippet with new text, without rewriting the whole file. Read the file first so `old_string` matches EXACTLY (including indentation). `old_string` must be unique in the file unless you set `replace_all`. Prefer this over write_file for small changes to large files.",parameters:{type:`object`,properties:{path:{type:`string`,description:`Repo-relative path, e.g. "src/feature.ts".`},old_string:{type:`string`,description:`The exact text to replace (must match the file byte-for-byte).`},new_string:{type:`string`,description:`The replacement text.`},replace_all:{type:`boolean`,description:`Replace every occurrence instead of requiring a unique match. Default false.`}},required:[`path`,`old_string`,`new_string`]},requires:[`repo.edit`],async execute(args,ctx){let path=typeof args.path==`string`?args.path:``,oldString=typeof args.old_string==`string`?args.old_string:``,newString=typeof args.new_string==`string`?args.new_string:``,replaceAll=args.replace_all===!0;if(!path||!oldString)return{data:{ok:!1,error:`path and old_string are required`}};let r=await ctx.caps.repoWrite.editFile(path,oldString,newString,replaceAll);return{data:r.ok?{ok:!0,branch:r.branch,commitUrl:r.commitUrl,replaced:r.replaced}:{ok:!1,error:r.error}}}}),memoryRecallTool=defineTool({name:`memory_recall`,description:`Recall durable facts from cross-run memory that are relevant to a query — decisions, fixes, project conventions, user preferences you (or another run) stored earlier. Call this FIRST when a task touches an area you may have worked before, instead of re-reading large files or history. Returns the most relevant stored entries (key + content); 0 results means nothing relevant is stored yet.`,parameters:{type:`object`,properties:{query:{type:`string`,description:`What you want to remember about, e.g. a subsystem, decision, or convention.`},limit:{type:`number`,description:`Max entries to return (default 5).`}},required:[`query`]},requires:[`memory`],async execute(args,ctx){let query=typeof args.query==`string`?args.query:``;if(!query.trim())return{data:{ok:!1,error:`query is required`}};let limit=typeof args.limit==`number`&&Number.isFinite(args.limit)?args.limit:void 0;return{data:await ctx.caps.memory.recall(query,limit)}}}),memoryRememberTool=defineTool({name:`memory_remember`,description:`Store ONE durable fact in cross-run memory so a future run can recall it instead of re-deriving it — a decision, a non-obvious fix, a project constraint, or a user preference. Keep content to one tight line. Use a stable, descriptive key (e.g. 'release-checklist', 'auth-flow'); reusing a key overwrites it. Do NOT store things the repo/git already records or facts that only matter to the current turn.`,parameters:{type:`object`,properties:{key:{type:`string`,description:`Stable, descriptive identifier for the fact, e.g. 'deploy-command'.`},content:{type:`string`,description:`The fact, as one concise line.`},tags:{type:`array`,items:{type:`string`},description:`Optional tags for grouping/filtering.`},importance:{type:`number`,description:`0–1; higher surfaces earlier. Default 0.5.`},scope:{type:`string`,enum:[`tenant`,`project`,`ticket`],description:`How widely this fact should be visible. 'ticket' = only this ticket's runs; 'project' (default) = every run on this project; 'tenant' = the whole workspace. Prefer the NARROWEST scope that is still true — a project convention is 'project', not 'tenant'.`},ttl_days:{type:`number`,description:`Forget automatically after this many days. Use it for anything time-bound (a release date, a temporary workaround, an in-flight migration). Omit only for facts that stay true indefinitely.`}},required:[`key`,`content`]},requires:[`memory`],async execute(args,ctx){let key=typeof args.key==`string`?args.key:``,content=typeof args.content==`string`?args.content:``;if(!key.trim()||!content.trim())return{data:{ok:!1,error:`key and content are required`}};let tags=Array.isArray(args.tags)?args.tags.filter(t=>typeof t==`string`):void 0,importance=typeof args.importance==`number`&&Number.isFinite(args.importance)?args.importance:void 0,scope=MEMORY_SCOPES.includes(args.scope)?args.scope:void 0,ttlDays=typeof args.ttl_days==`number`&&Number.isFinite(args.ttl_days)&&args.ttl_days>0?args.ttl_days:void 0;return{data:await ctx.caps.memory.remember(key,content,{tags,importance,scope,ttlDays})}}}),MEMORY_SCOPES=[`tenant`,`project`,`ticket`];defineTool({name:`memory_forget`,description:`Delete one stored fact from cross-run memory by its key. Use when a fact you (or an earlier run) stored has become WRONG — a decision was reversed, a workaround was removed, a convention changed. Correcting a fact is memory_remember with the same key; this is for facts that should no longer exist at all.`,parameters:{type:`object`,properties:{key:{type:`string`,description:`The key of the fact to delete.`}},required:[`key`]},requires:[`memory`,`memory.forget`],async execute(args,ctx){let key=typeof args.key==`string`?args.key:``;return key.trim()?{data:await ctx.caps.memory.forget(key)}:{data:{ok:!1,error:`key is required`}}}}),defineTool({name:`claim_resource`,description:`Reserve a shared resource before you work on it, so a peer agent working the same ticket does not change it underneath you. Pass a file path ('src/app.ts'), a directory ('src/api/'), or 'repo' for the whole tree. Returns granted:false with the current holder when someone else has it — then work on something else, or leave a workspace_note explaining what you need. Writes to a path held by another agent are refused whether or not you claim first.`,parameters:{type:`object`,properties:{resource:{type:`string`,description:`What to reserve: a repo-relative file path, a directory, or 'repo'.`},mode:{type:`string`,enum:[`exclusive`,`shared`],description:`'exclusive' (default) to write it; 'shared' to signal you are reading it and block others' exclusive claims.`},reason:{type:`string`,description:`One line on why you need it — shown to the peer agent that gets refused.`}},required:[`resource`]},requires:[`coordinate`],async execute(args,ctx){let resource=typeof args.resource==`string`?args.resource:``;if(!resource.trim())return{data:{ok:!1,error:`resource is required`}};let mode=args.mode===`shared`||args.mode===`exclusive`?args.mode:void 0,reason=typeof args.reason==`string`?args.reason:void 0;return{data:await ctx.caps.coordination.claim(resource,{mode,reason})}}}),defineTool({name:`release_resource`,description:`Release a resource you claimed, so a peer agent can take it. Do this as soon as you are finished with it rather than holding it to the end of the run. Every lease this run holds is released automatically when the run ends, so this is an optimisation, not a requirement.`,parameters:{type:`object`,properties:{resource:{type:`string`,description:`The resource string you claimed.`}},required:[`resource`]},requires:[`coordinate`],async execute(args,ctx){let resource=typeof args.resource==`string`?args.resource:``;return resource.trim()?{data:await ctx.caps.coordination.release(resource)}:{data:{ok:!1,error:`resource is required`}}}}),defineTool({name:`workspace_note`,description:`Publish a short note on the shared workspace for this ticket, readable by every agent working it (now or later in the ticket's lifecycle). Use it to declare intent ('I own the DB migration'), hand off a finding, or record a decision a peer must not contradict. Reusing a key overwrites that note. This is WORKING state for the current ticket — durable cross-ticket knowledge belongs in memory_remember.`,parameters:{type:`object`,properties:{key:{type:`string`,description:`Short stable identifier, e.g. 'owns-migration' or 'api-contract'.`},content:{type:`string`,description:`The note, in one or two lines.`}},required:[`key`,`content`]},requires:[`coordinate`],async execute(args,ctx){let key=typeof args.key==`string`?args.key:``,content=typeof args.content==`string`?args.content:``;return!key.trim()||!content.trim()?{data:{ok:!1,error:`key and content are required`}}:{data:await ctx.caps.coordination.postNote(key,content)}}}),defineTool({name:`workspace_read`,description:`Read the shared workspace for this ticket — notes posted by peer agents plus the resources they currently hold. Call this EARLY when a ticket may be staffed by more than one agent, so you plan around what others already own instead of colliding with them.`,parameters:{type:`object`,properties:{query:{type:`string`,description:`Optional filter; omit to read everything.`},limit:{type:`number`,description:`Max notes to return (default 20).`}}},requires:[`coordinate`],async execute(args,ctx){let query=typeof args.query==`string`&&args.query.trim()?args.query:void 0,limit=typeof args.limit==`number`&&Number.isFinite(args.limit)?args.limit:void 0,[notes,leases]=await Promise.all([ctx.caps.coordination.readNotes(query,limit),ctx.caps.coordination.listClaims()]);return notes.ok?{data:{ok:!0,notes:notes.notes??[],heldResources:leases.ok?leases.leases??[]:[]}}:{data:notes}}}),defineTool({name:`web_fetch`,description:`Fetch a single URL and return its readable text content (HTML is reduced to text/markdown). Use to read documentation, an API spec, an issue, or any page you have an exact URL for. Returns the status and the (possibly truncated) content.`,parameters:{type:`object`,properties:{url:{type:`string`,description:`The absolute http(s) URL to fetch.`}},required:[`url`]},requires:[`web`],async execute(args,ctx){let url=typeof args.url==`string`?args.url:``;return url.trim()?{data:await ctx.caps.web.fetch(url)}:{data:{ok:!1,error:`url is required`}}}}),defineTool({name:`web_search`,description:'Search the public web for a query and return ranked results (title, url, snippet) plus `coverage` and `attribution`. Use to discover sources/docs when you don\'t have an exact URL; then web_fetch the most relevant result. `coverage: "owned_index"` means this workspace\'s own previously-crawled corpus answered directly; `"web"` or `"encyclopedic"` means a vendor answered and the found pages are being indexed for next time. When `coverage` is "encyclopedic" the index behind this workspace is narrower than a general web engine — report what you actually found and say what you could not find, rather than filling the gap from memory.',parameters:{type:`object`,properties:{query:{type:`string`,description:`The search query.`}},required:[`query`]},requires:[`web.search`],async execute(args,ctx){let query=typeof args.query==`string`?args.query:``;return query.trim()?ctx.caps.web?.search?{data:await ctx.caps.web.search(query)}:{data:{ok:!1,error:`web search is not available on this surface`}}:{data:{ok:!1,error:`query is required`}}}}),defineTool({name:`run_checks`,description:`Statically validate the files you have written: it parses committed JSON/YAML and runs the platform's shell-free changed-source quality policies, returning structured path/line/rule diagnostics to fix BEFORE finishing. The same validation runs automatically at finish, so it cannot be skipped. IMPORTANT: this serverless executor has NO shell, so it does NOT run the full build, project-wide type-check, lint, or tests — those run in CI on the pull request (the source of truth). Never claim those checks passed.`,parameters:{type:`object`,properties:{}},requires:[`static-check`],async execute(_args,ctx){return{data:await ctx.caps.staticCheck.verify()}}}),defineTool({name:`run_command`,description:`Run a shell command in the checked-out repository (real shell). Use it to install dependencies and run the build, type-check, lint, and tests. Returns combined stdout/stderr and the exit code. Verify your changes this way BEFORE calling finish.`,parameters:{type:`object`,properties:{command:{type:`string`,description:`The shell command to run, e.g. "npm install" or "npm test".`}},required:[`command`]},requires:[`shell`],async execute(args,ctx){let command=typeof args.command==`string`?args.command:``;return command.trim()?{data:await ctx.caps.shell.run(command)}:{data:{ok:!1,error:`command is required`}}}});const askHumanTool=defineTool({name:`ask_human`,description:`Pause and ask a human for input when you are genuinely BLOCKED — a requirement is ambiguous, you cannot find an expected file/system after searching, a decision needs product/business judgement, or you would otherwise have to guess. The run pauses (no further token spend) and the question goes to the team's human-requests queue with a notification; when someone answers, you resume automatically with their answer and continue. Prefer this over guessing or finishing with a "could not proceed" summary — a blocked task that asks gets unblocked; one that gives up silently does not. Do NOT use it for things you can determine yourself with list_files/search_code/read_file.`,parameters:{type:`object`,properties:{question:{type:`string`,description:`The specific question for the human. Be concrete and self-contained — they may not have the full task context.`},context:{type:`string`,description:`Optional: what you have tried / why you are blocked, so the human can answer well.`}},required:[`question`]},requires:[`human`],async execute(args,ctx){let question=typeof args.question==`string`?args.question.trim():``,context=typeof args.context==`string`?args.context:void 0;if(!question)return{data:{ok:!1,error:`question is required to ask a human`}};let r=await ctx.caps.human.ask(question,context);return r.paused?{control:{kind:`ask_human`,approvalId:r.approvalId,question},data:{ok:!0,paused:!0,note:r.note??`Question sent to a human. The run is paused until it is answered; you will resume with the answer.`}}:{data:{ok:!0,paused:!1,answer:r.answer??null,note:r.note}}}});defineTool({name:`update_prd`,description:`Record a change on THIS TICKET'S PRD — the shared spec you were given in your context and that every other agent on this ticket reads. Use mode "append" (the default, and the safe one) to add a dated, signed note: a decision you made, a constraint you discovered, an assumption you had to take, or work you deliberately left out of scope. Use mode "section" ONLY to correct a section that is actually WRONG — it replaces that section's whole body, so pass the full replacement text, not a fragment; name the section by its exact heading (e.g. "Acceptance criteria", "Implementation Notes"). If the heading does not exist the call fails and returns the headings that do — retry with one of those, or append instead. This is not a substitute for doing the work: keep it to what a later run genuinely needs to know.`,parameters:{type:`object`,properties:{mode:{type:`string`,enum:[`append`,`section`],description:`"append" adds a dated, attributed note at the end (nothing already written is lost). "section" REPLACES the named section's body — only for correcting something wrong.`},section:{type:`string`,description:`Required when mode is "section": the exact heading to replace, without the leading "##" (e.g. "Acceptance criteria").`},content:{type:`string`,description:`The markdown to record. For mode "append", the note. For mode "section", the section's COMPLETE new body.`}},required:[`mode`,`content`]},requires:[`prd.write`],async execute(args,ctx){let mode=args.mode===`section`?`section`:`append`,content=typeof args.content==`string`?args.content.trim():``;if(!content)return{data:{ok:!1,error:`content is required`}};if(mode===`section`){let heading=typeof args.section==`string`?args.section.trim():``;return heading?{data:await ctx.caps.prd.editSection(heading,content)}:{data:{ok:!1,error:`section is required when mode is "section" — pass the exact heading to replace, or use mode "append" to add a note instead.`}}}return{data:await ctx.caps.prd.append(content)}}}),defineTool({name:`finish`,description:`Call ONLY when the task is fully complete — every deliverable file written with real, working content (no stubs/placeholders) and every task/PRD requirement implemented. Your changes open a pull request for human review, so a partial scaffold is not "done". Provide a concise summary of what was delivered. Do NOT assert that a build/type-check/lint/test passed — you cannot run those here (CI on the PR verifies). If you are blocked rather than done, call ask_human instead of finishing with a "could not proceed" summary.`,parameters:{type:`object`,properties:{summary:{type:`string`,description:`What was delivered.`}},required:[`summary`]},async execute(args){return{control:{kind:`finish`,summary:typeof args.summary==`string`?args.summary.trim():``},data:{ok:!0}}}}),[...SYMBOL_TOOLS,...SEMANTIC_TOOLS,...GIT_TOOLS];const PSYCH_DIM={honesty:`hexaco.honesty`,emotionality:`hexaco.emotionality`,extraversion:`hexaco.extraversion`,agreeableness:`hexaco.agreeableness`,conscientiousness:`hexaco.conscientiousness`,openness:`hexaco.openness`,regulatoryFocus:`regfocus.orientation`,needForCognition:`cognition.need_for_cognition`,reflection:`cognition.reflection`,decisionRational:`decision.rational`,decisionIntuitive:`decision.intuitive`,decisionDependent:`decision.dependent`,decisionSpontaneous:`decision.spontaneous`,maximizing:`decision.maximizing`,moralCare:`moral.care`,moralFairness:`moral.fairness`,moralLoyalty:`moral.loyalty`,moralAuthority:`moral.authority`,moralSanctity:`moral.sanctity`,moralLiberty:`moral.liberty`,conflictAssertiveness:`conflict.assertiveness`,conflictCooperativeness:`conflict.cooperativeness`,valSelfDirection:`values.self_direction`,valStimulation:`values.stimulation`,valHedonism:`values.hedonism`,valAchievement:`values.achievement`,valPower:`values.power`,valSecurity:`values.security`,valConformity:`values.conformity`,valTradition:`values.tradition`,valBenevolence:`values.benevolence`,valUniversalism:`values.universalism`,grit:`disp.grit`,locusInternal:`disp.locus_internal`,riskTolerance:`disp.risk_tolerance`};function score(vector,id){let raw=vector?.[id];return typeof raw!=`number`||Number.isNaN(raw)?50:Math.max(0,Math.min(100,raw))}const LIMBIC_DIM_NAMES=[`valence`,`arousal`,`driveCuriosity`,`driveCaution`,`driveEffort`,`driveSocial`,`attention`,`exploration`],LIMBIC_STATE_DIM=8,BOUNDS={valence:[-1,1],arousal:[0,1],driveCuriosity:[0,1],driveCaution:[0,1],driveEffort:[0,1],driveSocial:[0,1],attention:[0,1],exploration:[0,1]},NEUTRAL_STATE={valence:0,arousal:.2,driveCuriosity:.5,driveCaution:.5,driveEffort:.8,driveSocial:.5,attention:.7,exploration:.5};function clamp(name,v){let[lo,hi]=BOUNDS[name];return Number.isNaN(v)?lo:Math.max(lo,Math.min(hi,v))}function neutralState(){return{...NEUTRAL_STATE}}function applyDelta(state,delta){let out={...state};for(let name of LIMBIC_DIM_NAMES){let d=delta[name];typeof d==`number`&&!Number.isNaN(d)&&(out[name]=clamp(name,out[name]+d))}return out}function stateToArray(state){return LIMBIC_DIM_NAMES.map(n=>state[n])}function infl(s){return(s-50)/50}function deriveLimbicSetpoints(profile){let sp=neutralState();if(!profile)return sp;let open=infl(score(profile.vector,PSYCH_DIM.openness)),emo=infl(score(profile.vector,PSYCH_DIM.emotionality)),consc=infl(score(profile.vector,PSYCH_DIM.conscientiousness)),extra=infl(score(profile.vector,PSYCH_DIM.extraversion)),reg=infl(score(profile.vector,PSYCH_DIM.regulatoryFocus)),risk=infl(score(profile.vector,PSYCH_DIM.riskTolerance)),grit=infl(score(profile.vector,PSYCH_DIM.grit)),stim=infl(score(profile.vector,PSYCH_DIM.valStimulation));return sp.driveCuriosity=clamp(`driveCuriosity`,.5+.35*open+.15*stim),sp.exploration=clamp(`exploration`,.4+.3*open+.25*risk+.15*reg),sp.driveCaution=clamp(`driveCaution`,.5+.3*consc-.3*risk-.2*reg+.15*emo),sp.arousal=clamp(`arousal`,.2+.2*emo+.1*extra),sp.driveSocial=clamp(`driveSocial`,.5+.35*extra),sp.driveEffort=clamp(`driveEffort`,.8+.15*grit+.1*consc),sp.valence=clamp(`valence`,0+.1*reg-.1*emo),sp.attention=clamp(`attention`,.7+.1*consc),sp}const I=e=>Math.max(0,Math.min(1,e.intensity??.5));function appraiseAmygdala(event){let k=I(event);switch(event.kind){case`success`:return{valence:.5*k,arousal:-.15*k,driveCaution:-.1*k,driveEffort:-.05*k};case`progress`:return{valence:.25*k,arousal:.05*k,driveEffort:-.03*k};case`error`:return{valence:-.45*k,arousal:.4*k,driveCaution:.3*k,attention:.15*k};case`blocked`:return{valence:-.35*k,arousal:.3*k,driveEffort:-.15*k,driveSocial:.2*k};case`risk`:return{arousal:.35*k,driveCaution:.4*k,attention:.2*k,exploration:-.15*k};case`feedback`:{let s=Math.sign(event.sign??1)||1;return{valence:.4*k*s,arousal:.1*k,driveSocial:.1*k}}case`idle`:return{arousal:-.2*k,driveEffort:.15*k};default:return{}}}function homeostasis(state,setpoints,opts={}){let rate=opts.rate??.1,fatigue=opts.fatigue??0,out={...state};for(let name of LIMBIC_DIM_NAMES)out[name]=clamp(name,out[name]+rate*(setpoints[name]-out[name]));return fatigue>0&&(out.driveEffort=clamp(`driveEffort`,out.driveEffort-fatigue)),out}function thalamusGate(state,optimalArousal=.5){let d=state.arousal-optimalArousal,gain=1-2.8*d*d;return Math.max(.1,Math.min(1,gain))}function basalGangliaExploreBias(state){let b=.5*state.exploration+.3*state.driveCuriosity;return b+=.15*state.valence,b-=.25*(1-state.driveEffort),b-=.2*(state.driveCaution-.5),Math.max(0,Math.min(1,b))}function basalGangliaSelect(state,options){if(options.length===0)return{choice:null,exploreBias:basalGangliaExploreBias(state)};let bias=basalGangliaExploreBias(state),best=options[0],bestScore=-1/0;for(let o of options){let nov=Math.max(0,Math.min(1,o.novelty)),sc=-Math.abs(nov-bias);sc>bestScore&&(bestScore=sc,best=o)}return{choice:best,exploreBias:bias}}const THINK_ORDER=[`off`,`minimal`,`low`,`medium`,`high`,`xhigh`];function maxThink(a,b){return a&&THINK_ORDER.indexOf(a)>=THINK_ORDER.indexOf(b)?a:b}const HI_F=.6,LO_F=.4;function compileLimbicState(state){let directives=[],params={};state.valence<=-.4?(directives.push(`Affect (negative): recent steps have gone badly — slow down, avoid rash or destructive actions, and re-verify your assumptions before proceeding.`),params.thinkLevel=maxThink(params.thinkLevel,`high`),params.reasoningLevel=`on`):state.valence>=.4&&directives.push(`Affect (positive): things are going well — keep momentum, but don't get sloppy.`),state.arousal>=.7?(directives.push(`Arousal (heightened): you are highly activated — resist the urge to rush; double-check before any irreversible step.`),params.thinkLevel=maxThink(params.thinkLevel,`high`)):state.arousal<=.15&&directives.push(`Arousal (low): this is routine — keep it lightweight and efficient.`),state.driveCaution>=HI_F?(directives.push(`Drive (caution): heightened — prefer reversible steps, add guardrails, and confirm risky or destructive operations.`),params.thinkLevel=maxThink(params.thinkLevel,`medium`)):state.driveCaution<=LO_F&&directives.push(`Drive (caution): relaxed — bias to action on low-risk work.`),state.driveCuriosity>=HI_F&&directives.push(`Drive (curiosity): high — it is appropriate to investigate and consider alternative approaches.`),state.driveEffort<=LO_F&&directives.push(`Drive (effort): energy is low — keep scope tight, avoid over-engineering, checkpoint progress, and consider escalating rather than grinding.`),state.driveSocial>=HI_F?directives.push(`Drive (social): communicate proactively — surface progress and ask for input when blocked.`):state.driveSocial<=LO_F&&directives.push(`Drive (social): work heads-down — narrate only what matters.`),thalamusGate(state)<=.5&&(directives.push(`Attention (degraded): your attention gate is low — re-read the task carefully and do not skip verification steps.`),params.thinkLevel=maxThink(params.thinkLevel,`medium`));let explore=basalGangliaExploreBias(state);explore>=.7?directives.push(`Action selection: lean exploratory — try a novel approach before settling on the obvious one.`):explore<=.3&&directives.push(`Action selection: lean exploitative — use the proven, known approach and avoid unnecessary detours.`);let dTemp=0;dTemp+=.2*(state.exploration-.5)*2,dTemp+=.06*(state.driveCuriosity-.5)*2,dTemp+=.08*state.valence,dTemp-=.16*(state.driveCaution-.5)*2,dTemp-=.08*Math.max(0,state.arousal-.7);let clampedTemp=Math.max(-.3,Math.min(.3,Math.round(dTemp*100)/100));return Math.abs(clampedTemp)>=.02&&(params.temperatureDelta=clampedTemp),{directives,params}}function mergeLimbicWithPsychometric(psych,limbic){let out={...psych};if(limbic.thinkLevel&&(out.thinkLevel=maxThink(psych.thinkLevel,limbic.thinkLevel)),(limbic.reasoningLevel===`on`||psych.reasoningLevel===`on`)&&(out.reasoningLevel=`on`),typeof limbic.temperatureDelta==`number`&&limbic.temperatureDelta!==0){let baseline=psych.temperature??.6;out.temperature=Math.round(Math.max(.1,Math.min(1,baseline+limbic.temperatureDelta))*100)/100}return out}function buildLimbicBlock(state){if(!state)return``;let{directives}=compileLimbicState(state);return directives.length===0?``:bulletBlock(`Current affective state (execute accordingly):`,directives)}const ENNEAGRAM_MOTIVATION={1:`Core drive: be correct and principled — avoid mistakes and sloppiness; hold work to a high standard.`,2:`Core drive: be helpful and needed — anticipate what others require and support the team.`,3:`Core drive: achieve and be effective — optimise for visible results and momentum.`,4:`Core drive: be authentic and distinctive — favour original, considered solutions over rote ones.`,5:`Core drive: understand deeply — gather sufficient information and reason before committing.`,6:`Core drive: be secure and prepared — anticipate failure modes and build in safeguards.`,7:`Core drive: explore options and keep momentum — stay flexible and avoid getting stuck.`,8:`Core drive: take charge and protect — act decisively and own outcomes directly.`,9:`Core drive: keep things stable and harmonious — reduce friction and find common ground.`};function compilePsychometricProfile(profile){let v=profile.vector??{},directives=[],params={},temp=.6,tempMoved=!1,nudgeTemp=delta=>{temp+=delta,tempMoved=!0},consc=score(v,PSYCH_DIM.conscientiousness);consc>=65?(directives.push(`Conscientiousness: plan before acting, work methodically, write and run tests, and double-check before declaring anything done.`),params.thinkLevel=maxThink(params.thinkLevel,`high`)):consc<=35&&directives.push(`Conscientiousness: bias to action — keep things lightweight and avoid over-process.`);let open=score(v,PSYCH_DIM.openness);open>=65?(directives.push(`Openness: explore novel approaches and unconventional ideas before settling on the obvious one.`),nudgeTemp(.15)):open<=35&&(directives.push(`Openness: prefer proven, conventional patterns over novel experiments.`),nudgeTemp(-.15));let emo=score(v,PSYCH_DIM.emotionality);emo>=65?directives.push(`Emotionality: surface risks and uncertainties early, and escalate when genuinely unsure rather than guessing.`):emo<=35&&directives.push(`Emotionality: stay calm under ambiguity — avoid over-escalating; only flag material risks.`);let extra=score(v,PSYCH_DIM.extraversion);extra>=65?directives.push(`Extraversion: be proactive and communicative — volunteer progress and options.`):extra<=35&&directives.push(`Extraversion: be concise — act first, narrate only what matters.`);let agree=score(v,PSYCH_DIM.agreeableness);agree>=65?directives.push(`Agreeableness: seek consensus and accommodate others' constraints where reasonable.`):agree<=35&&directives.push(`Agreeableness: push back directly and challenge weak reasoning rather than deferring.`),score(v,PSYCH_DIM.honesty)>=65&&directives.push(`Honesty-Humility: never fabricate, admit uncertainty plainly, resist sycophancy, and refuse to overstate results even when asked.`);let reg=score(v,PSYCH_DIM.regulatoryFocus);reg>=65?(directives.push(`Regulatory focus (promotion): optimise for opportunity and speed; accept calculated, reversible risk to move faster.`),nudgeTemp(.1)):reg<=35&&(directives.push(`Regulatory focus (prevention): optimise for safety and correctness; add guardrails and avoid errors over chasing upside.`),nudgeTemp(-.1),params.thinkLevel=maxThink(params.thinkLevel,`medium`));let nfc=score(v,PSYCH_DIM.needForCognition);nfc>=65?(directives.push(`Cognition: reason step-by-step and analyse deeply before concluding; show your working on hard problems.`),params.thinkLevel=maxThink(params.thinkLevel,`high`),params.reasoningLevel=`on`):nfc<=35&&(directives.push(`Cognition: trust pattern-matching and act decisively; do not over-analyse routine work.`),params.thinkLevel=maxThink(params.thinkLevel,`low`)),score(v,PSYCH_DIM.reflection)>=65&&directives.push(`Reflection: distrust the first intuitive answer — verify your reasoning before acting on it.`),score(v,PSYCH_DIM.decisionRational)>=65&&directives.push(`Decision style: decide analytically, making trade-offs explicit.`),score(v,PSYCH_DIM.decisionIntuitive)>=65&&directives.push(`Decision style: trust well-earned intuition when evidence is thin.`),score(v,PSYCH_DIM.decisionDependent)>=65&&directives.push(`Decision style: seek input or confirmation from a human or peer before committing to consequential choices.`),score(v,PSYCH_DIM.decisionSpontaneous)>=65&&directives.push(`Decision style: decide quickly and keep momentum; do not belabour reversible calls.`);let maxim=score(v,PSYCH_DIM.maximizing);maxim>=65?directives.push(`Thoroughness: compare alternatives and optimise before settling — aim for the best option, not the first workable one.`):maxim<=35&&directives.push(`Thoroughness: stop at good-enough; do not gold-plate or over-polish working solutions.`);let strongMoral=[[`care`,score(v,PSYCH_DIM.moralCare),`prioritise user wellbeing and avoid harm`],[`fairness`,score(v,PSYCH_DIM.moralFairness),`treat stakeholders equitably and proportionately`],[`loyalty`,score(v,PSYCH_DIM.moralLoyalty),`protect the team's and project's interests`],[`authority`,score(v,PSYCH_DIM.moralAuthority),`respect established policy and ownership boundaries`],[`sanctity`,score(v,PSYCH_DIM.moralSanctity),`uphold code, data, and process integrity`],[`liberty`,score(v,PSYCH_DIM.moralLiberty),`preserve user autonomy and avoid over-constraining`]].filter(([,s])=>s>=65).map(([,,phrase])=>phrase);strongMoral.length>0&&directives.push(`Values lens: when resolving trade-offs, ${strongMoral.join(`; `)}.`);let assertive=score(v,PSYCH_DIM.conflictAssertiveness),coop=score(v,PSYCH_DIM.conflictCooperativeness);(assertive!==50||coop!==50)&&(assertive>=65&&coop>=65?directives.push(`Conflict mode (collaborating): in disagreement, integrate views and seek a win-win.`):assertive>=65&&coop<=35?directives.push(`Conflict mode (competing): stand firm on your position when you are confident it is right.`):assertive<=35&&coop>=65?directives.push(`Conflict mode (accommodating): yield to others' preferences to preserve a working relationship.`):assertive<=35&&coop<=35?directives.push(`Conflict mode (avoiding): sidestep low-value conflicts and pick your battles.`):directives.push(`Conflict mode (compromising): look for a fair middle ground when views differ.`));let topValues=[[`self-direction`,score(v,PSYCH_DIM.valSelfDirection)],[`stimulation`,score(v,PSYCH_DIM.valStimulation)],[`hedonism`,score(v,PSYCH_DIM.valHedonism)],[`achievement`,score(v,PSYCH_DIM.valAchievement)],[`power`,score(v,PSYCH_DIM.valPower)],[`security`,score(v,PSYCH_DIM.valSecurity)],[`conformity`,score(v,PSYCH_DIM.valConformity)],[`tradition`,score(v,PSYCH_DIM.valTradition)],[`benevolence`,score(v,PSYCH_DIM.valBenevolence)],[`universalism`,score(v,PSYCH_DIM.valUniversalism)]].filter(([,s])=>s>=65).sort((a,b)=>b[1]-a[1]).slice(0,3).map(([name])=>name);topValues.length>0&&directives.push(`Guiding values: prioritise ${topValues.join(`, `)} when goals conflict.`);let grit=score(v,PSYCH_DIM.grit);grit>=65?directives.push(`Grit: persist through obstacles — retry intelligently and exhaust reasonable approaches before giving up or escalating.`):grit<=35&&directives.push(`Grit: if an approach stalls, escalate or ask for direction rather than grinding.`),score(v,PSYCH_DIM.locusInternal)>=65&&directives.push(`Ownership: treat outcomes as within your control — own failures and drive them to resolution.`);let risk=score(v,PSYCH_DIM.riskTolerance);return risk>=65?(directives.push(`Risk: accept calculated risks for speed, but keep changes reversible.`),nudgeTemp(.1)):risk<=35&&(directives.push(`Risk: prefer safe, reversible steps and confirm before any destructive or hard-to-undo action.`),nudgeTemp(-.1)),typeof profile.enneagramType==`number`&&ENNEAGRAM_MOTIVATION[profile.enneagramType]&&directives.push(ENNEAGRAM_MOTIVATION[profile.enneagramType]),tempMoved&&(params.temperature=Math.round(Math.max(.1,Math.min(1,temp))*100)/100),{directives,params}}function buildPsychometricBlock(profile){if(!profile)return``;let{directives}=compilePsychometricProfile(profile);return directives.length===0?``:bulletBlock(`Personality (execute under these traits):`,directives)}function mergeExecParams(profiles){let merged={},temps=[];for(let profile of profiles){let{params}=compilePsychometricProfile(profile);params.thinkLevel&&(merged.thinkLevel=maxThink(merged.thinkLevel,params.thinkLevel)),params.reasoningLevel===`on`&&(merged.reasoningLevel=`on`),typeof params.temperature==`number`&&temps.push(params.temperature)}return temps.length>0&&(merged.temperature=Math.round(temps.reduce((a,b)=>a+b,0)/temps.length*100)/100),merged}const KNOWLEDGE_DIRS={root:`.builderforce`,memory:`memory`,sessions:`sessions`};function deriveActivitySummary(activity){let created=[...new Set(activity.created??[])],edited=[...new Set(activity.edited??[])],tools=[...new Set(activity.tools??[])],hasCreate=created.length>0,hasEdit=edited.length>0,toolSet=new Set(tools),isTest=[...created,...edited].some(f=>f.includes(`.test.`)||f.includes(`.spec.`)||f.includes(`__tests__`))||toolSet.has(`test`),isAnalysis=!hasCreate&&!hasEdit&&(toolSet.has(`grep`)||toolSet.has(`glob`)||toolSet.has(`view`))&&!toolSet.has(`bash`),isReview=toolSet.has(`git_history`)||toolSet.has(`code_analysis`)||toolSet.has(`project_knowledge`);return toolSet.has(`orchestrate`)||toolSet.has(`workflow_status`)?`Multi-agent workflow execution`:isReview?`Code review / analysis`:isTest&&hasCreate?`Test suite created`:isTest&&hasEdit?`Tests updated`:isAnalysis?`Codebase exploration / read-only analysis`:hasCreate&&hasEdit?`Feature implementation: new files + edits`:hasCreate?`New file(s) created: ${created.length}`:hasEdit?`Code modifications: ${edited.length} file(s) changed`:tools.length>0?`Agent activity (no file changes)`:``}function buildKnowledgeMemoryEntry(params){let lines=[`\n## [${params.ts}] session:${params.sessionKey}`,``],meaningful=!1;if(params.activity){let created=[...new Set(params.activity.created??[])],edited=[...new Set(params.activity.edited??[])],tools=[...new Set(params.activity.tools??[])];created.length>0&&(lines.push(`**Created**: ${created.join(`, `)}`),meaningful=!0),edited.length>0&&(lines.push(`**Edited**: ${edited.join(`, `)}`),meaningful=!0),tools.length>0&&(lines.push(`**Tools**: ${tools.join(`, `)}`),meaningful=!0);let summary=deriveActivitySummary({created,edited,tools});summary&&(lines.push(`**Summary**: ${summary}`),meaningful=!0)}return meaningful?(lines.push(``),lines.join(`
16
- `)):null}defineTool({name:`skill_propose`,description:`Propose a reusable SKILL — a procedure a future agent can follow — drafted from work you just completed and verified. Use it when you worked out a repeatable way to do something non-obvious in this codebase (a migration + guard + test sequence, a release path, a debugging route) and a future run would otherwise rediscover it. Do NOT propose a skill for a one-off fix, for something the repo already documents, or for a procedure you did not actually complete. The draft goes to a human for review; it does not take effect until approved.`,parameters:{type:`object`,properties:{slug:{type:`string`,description:`Stable kebab-case id, e.g. 'add-a-schema-column'. Re-using one revises your existing draft.`},name:{type:`string`,description:`Short human title, e.g. 'Add a schema column end to end'.`},description:{type:`string`,description:`One line saying WHEN to use this skill — a future agent matches on this, so name the situation, not the steps.`},body:{type:`string`,description:`The procedure as Markdown: ordered steps, exact commands, and how to tell it worked.`},evidence:{type:`string`,description:`What proves this procedure works — the graded proof, the merged PR, the passing check.`}},required:[`slug`,`name`,`description`,`body`]},requires:[`skill.author`],async execute(args,ctx){let str=v=>typeof v==`string`?v.trim():``,slug=str(args.slug),name=str(args.name),description=str(args.description),body=str(args.body);if(!slug||!name||!description||!body)return{data:{ok:!1,error:`slug, name, description and body are all required`}};let evidence=str(args.evidence);return{data:await ctx.caps.skillAuthor.propose({slug,name,description,body,...evidence?{evidence}:{}})}}}),defineTool({name:`skill_list`,description:`List the skills this workspace already has — approved ones you can follow, and drafts awaiting review. Call it before proposing, so you revise an existing draft instead of adding a near-duplicate.`,parameters:{type:`object`,properties:{}},requires:[`skill.author`],async execute(_args,ctx){return{data:await ctx.caps.skillAuthor.list()}}});const ROLE_ENUM_DESCRIPTION=MODEL_ROLES.map(role=>`${role} — ${MODEL_ROLE_DESCRIPTIONS[role]}`).join(` · `);defineTool({name:`spawn_agent`,description:"Delegate a self-contained sub-task to a child agent that works in its OWN context and reports back a single answer. Use it when finding something out would take many turns you do not want to carry — locating where a behaviour lives across an unfamiliar tree, checking whether a pattern is used anywhere else, summarising a large file you only need one fact from. The child sees NOTHING of this conversation, so `task` must state everything it needs to know, and it answers in prose — it cannot hand you files or tool output. Do NOT delegate work you can do in a turn or two, and do NOT delegate the actual writing of the deliverable: you are accountable for what ships.",parameters:{type:`object`,properties:{label:{type:`string`,description:`A few words naming the delegation, e.g. 'locate the auth middleware'. Shown on the run timeline.`},task:{type:`string`,description:`The child's complete brief: what to find out or do, where to look, and exactly what to report back. Assume it knows nothing about the ticket beyond what you write here.`},read_only:{type:`boolean`,description:`Default true — the child may read, search and reason but not modify the working tree. Pass false ONLY when the delegated work is itself an edit you want it to make.`},role:{type:`string`,enum:[...MODEL_ROLES],description:`What kind of call the child's turns are — lets the surface pick a model suited to the work rather than reusing yours. Defaults to 'explore' when read_only, else 'code'. ${ROLE_ENUM_DESCRIPTION}`},as_agent:{type:`string`,description:`Run the child AS one of the workspace's agents — its id or name (e.g. 'Ada'). The child adopts that agent's role, bio, skills and personality so the delegated slice is done in that agent's voice and expertise. Prefer an agent already in this chat (builtin_chats_list_agents) or from builtin_cloud_agents_list_mine.`}},required:[`label`,`task`]},requires:[`orchestrate`],async execute(args,ctx){let str=v=>typeof v==`string`?v.trim():``,label=str(args.label),task=str(args.task);if(!task)return{data:{ok:!1,error:`task is required — the child sees none of your conversation`}};let readOnly=args.read_only!==!1,role=delegationRole(args.role,readOnly),asAgent=str(args.as_agent),r=await ctx.caps.orchestration.spawn({label:label||task.slice(0,60),task,readOnly,role,...asAgent?{asAgent}:{}});return{data:r,...r.ok?{}:{isError:!0}}}});function stableStringify(value){if(value==null)return`null`;if(typeof value==`number`&&!Number.isFinite(value))return JSON.stringify(String(value));if(typeof value==`bigint`)return JSON.stringify(value.toString());if(typeof value!=`object`)return JSON.stringify(value)??`null`;if(value instanceof Error)return stableStringify({name:value.name,message:value.message,stack:value.stack});if(value instanceof Uint8Array)return stableStringify({type:`Uint8Array`,data:Array.from(value)});if(Array.isArray(value))return`[${value.map(stableStringify).join(`,`)}]`;let record=value;return`{${Object.keys(record).filter(k=>record[k]!==void 0).sort().map(k=>`${JSON.stringify(k)}:${stableStringify(record[k])}`).join(`,`)}}`}export{isSymbolIndexable as A,editFileTool as C,writeFileTool as D,memoryRememberTool as E,ToolRegistry as F,filterByGlob as I,isUnderScopeDir as L,renderPolicyDirectives as M,CURRENT_ENGINE_ID as N,SYMBOL_KINDS as O,resolveEngineById as P,normalizeScopeDir as R,deleteFileTool as S,memoryRecallTool as T,mergeLimbicWithPsychometric as _,buildPsychometricBlock as a,thalamusGate as b,LIMBIC_STATE_DIM as c,basalGangliaSelect as d,buildLimbicBlock as f,maxThink as g,homeostasis as h,deriveActivitySummary as i,coercePolicyGates as j,extractSymbols as k,applyDelta as l,deriveLimbicSetpoints as m,KNOWLEDGE_DIRS as n,mergeExecParams as o,compileLimbicState as p,buildKnowledgeMemoryEntry as r,LIMBIC_DIM_NAMES as s,stableStringify as t,appraiseAmygdala as u,neutralState as v,listFilesTool as w,askHumanTool as x,stateToArray as y,applyStringEdit as z};
15
+ `),truncated:end<totalLines,totalLines,offset:start,returnedLines:slice.length}}defineTool({name:`read_file`,description:`Read a repo file on the ticket branch. Returns up to `+READ_DEFAULT_LINE_LIMIT+" lines at a time: a large file comes back as a paginated line window (never a hard failure), and the result's `truncated`/`totalLines` tell you when more remains — read the next chunk by calling again with `offset`. Always read a file before editing it so you preserve existing code and only change what is needed.",parameters:{type:`object`,properties:{path:{type:`string`,description:`Repo-relative path, e.g. "src/feature.ts".`},offset:{type:`number`,description:`1-based line to start reading from (for paging through a large file). Default 1.`},limit:{type:`number`,description:`Max lines to return. Default ${READ_DEFAULT_LINE_LIMIT}. Read the next window with offset = previous offset + returned lines.`}},required:[`path`]},requires:[`repo.read`],async execute(args,ctx){let path=typeof args.path==`string`?args.path:``;if(!path)return{data:{ok:!1,error:`path is required`}};let offset=typeof args.offset==`number`&&args.offset>0?Math.floor(args.offset):void 0,limit=typeof args.limit==`number`&&args.limit>0?Math.floor(args.limit):void 0,r=await ctx.caps.repoRead.readFile(path);if(!r.ok)return{data:r};let win=windowFileContent(r.content??``,{offset,limit}),data={ok:!0,path:r.path??path,content:win.content,truncated:win.truncated||r.truncated===!0,totalLines:win.totalLines,offset:win.offset};if(win.truncated){let lastLine=win.offset+win.returnedLines-1;data.note=`Showing lines ${win.offset}–${lastLine} of ${win.totalLines}. To continue, call read_file again with offset ${lastLine+1}.`}return{data}}});const writeFileTool=defineTool({name:`write_file`,description:`Create or update a file, writing its complete contents. How the write lands depends on the surface: in an editor/on-prem workspace it edits the file in place; in a cloud/review run it is staged on the ticket branch as a reviewable pending change. Do NOT narrate a specific mechanism (e.g. "opened a PR") — just state what the file now contains. Use once per deliverable file. Provide the FULL file content.`,parameters:{type:`object`,properties:{path:{type:`string`,description:`Repo-relative path, e.g. "src/feature.ts".`},content:{type:`string`,description:`Complete file content (no placeholders).`},summary:{type:`string`,description:`One-line description of the change.`}},required:[`path`,`content`]},requires:[`repo.write`],async execute(args,ctx){let path=typeof args.path==`string`?args.path:``,content=typeof args.content==`string`?args.content:``,summary=typeof args.summary==`string`?args.summary:void 0;if(!path||!content)return{data:{ok:!1,error:`path and content are both required`}};let r=await ctx.caps.repoWrite.writeFile(path,content,summary);return{data:r.ok?{ok:!0,branch:r.branch,commitUrl:r.commitUrl}:{ok:!1,error:r.error}}}}),deleteFileTool=defineTool({name:`delete_file`,description:`Remove a file from the ticket branch so it does NOT ship in the pull request. Use this to clean up dead code: a stub/placeholder, an unreferenced file, or a file a PRIOR pass on this branch created that should not be part of the final change. The "Files already on this branch" list in your context shows what a prior pass left — reconcile against it. Verify the file is genuinely unused (search_code for its exports) before deleting. Deleting a file not on the branch is a no-op (reported back), not an error.`,parameters:{type:`object`,properties:{path:{type:`string`,description:`Repo-relative path to remove, e.g. "src/utils/email.ts".`},reason:{type:`string`,description:`One-line why this file should not ship (e.g. "stub superseded by existing email infra").`}},required:[`path`]},requires:[`repo.delete`],async execute(args,ctx){let path=typeof args.path==`string`?args.path:``;if(!path)return{data:{ok:!1,error:`path is required`}};let reason=typeof args.reason==`string`?args.reason:void 0,r=await ctx.caps.repoWrite.deleteFile(path,reason);return r.ok&&r.deleted===!1?{data:{ok:!0,deleted:!1,note:r.note}}:{data:r.ok?{ok:!0,deleted:!0,branch:r.branch,commitUrl:r.commitUrl}:{ok:!1,error:r.error}}}}),editFileTool=defineTool({name:`edit_file`,description:"Make a surgical in-place edit to an existing file on the ticket branch: replace an exact snippet with new text, without rewriting the whole file. Read the file first so `old_string` matches EXACTLY (including indentation). `old_string` must be unique in the file unless you set `replace_all`. Prefer this over write_file for small changes to large files.",parameters:{type:`object`,properties:{path:{type:`string`,description:`Repo-relative path, e.g. "src/feature.ts".`},old_string:{type:`string`,description:`The exact text to replace (must match the file byte-for-byte).`},new_string:{type:`string`,description:`The replacement text.`},replace_all:{type:`boolean`,description:`Replace every occurrence instead of requiring a unique match. Default false.`}},required:[`path`,`old_string`,`new_string`]},requires:[`repo.edit`],async execute(args,ctx){let path=typeof args.path==`string`?args.path:``,oldString=typeof args.old_string==`string`?args.old_string:``,newString=typeof args.new_string==`string`?args.new_string:``,replaceAll=args.replace_all===!0;if(!path||!oldString)return{data:{ok:!1,error:`path and old_string are required`}};let r=await ctx.caps.repoWrite.editFile(path,oldString,newString,replaceAll);return{data:r.ok?{ok:!0,branch:r.branch,commitUrl:r.commitUrl,replaced:r.replaced}:{ok:!1,error:r.error}}}}),memoryRecallTool=defineTool({name:`memory_recall`,description:`Recall durable facts from cross-run memory that are relevant to a query — decisions, fixes, project conventions, user preferences you (or another run) stored earlier. Call this FIRST when a task touches an area you may have worked before, instead of re-reading large files or history. Returns the most relevant stored entries (key + content); 0 results means nothing relevant is stored yet.`,parameters:{type:`object`,properties:{query:{type:`string`,description:`What you want to remember about, e.g. a subsystem, decision, or convention.`},limit:{type:`number`,description:`Max entries to return (default 5).`}},required:[`query`]},requires:[`memory`],async execute(args,ctx){let query=typeof args.query==`string`?args.query:``;if(!query.trim())return{data:{ok:!1,error:`query is required`}};let limit=typeof args.limit==`number`&&Number.isFinite(args.limit)?args.limit:void 0;return{data:await ctx.caps.memory.recall(query,limit)}}}),memoryRememberTool=defineTool({name:`memory_remember`,description:`Store ONE durable fact in cross-run memory so a future run can recall it instead of re-deriving it — a decision, a non-obvious fix, a project constraint, or a user preference. Keep content to one tight line. Use a stable, descriptive key (e.g. 'release-checklist', 'auth-flow'); reusing a key overwrites it. Do NOT store things the repo/git already records or facts that only matter to the current turn.`,parameters:{type:`object`,properties:{key:{type:`string`,description:`Stable, descriptive identifier for the fact, e.g. 'deploy-command'.`},content:{type:`string`,description:`The fact, as one concise line.`},tags:{type:`array`,items:{type:`string`},description:`Optional tags for grouping/filtering.`},importance:{type:`number`,description:`0–1; higher surfaces earlier. Default 0.5.`},scope:{type:`string`,enum:[`tenant`,`project`,`ticket`],description:`How widely this fact should be visible. 'ticket' = only this ticket's runs; 'project' (default) = every run on this project; 'tenant' = the whole workspace. Prefer the NARROWEST scope that is still true — a project convention is 'project', not 'tenant'.`},ttl_days:{type:`number`,description:`Forget automatically after this many days. Use it for anything time-bound (a release date, a temporary workaround, an in-flight migration). Omit only for facts that stay true indefinitely.`}},required:[`key`,`content`]},requires:[`memory`],async execute(args,ctx){let key=typeof args.key==`string`?args.key:``,content=typeof args.content==`string`?args.content:``;if(!key.trim()||!content.trim())return{data:{ok:!1,error:`key and content are required`}};let tags=Array.isArray(args.tags)?args.tags.filter(t=>typeof t==`string`):void 0,importance=typeof args.importance==`number`&&Number.isFinite(args.importance)?args.importance:void 0,scope=MEMORY_SCOPES.includes(args.scope)?args.scope:void 0,ttlDays=typeof args.ttl_days==`number`&&Number.isFinite(args.ttl_days)&&args.ttl_days>0?args.ttl_days:void 0;return{data:await ctx.caps.memory.remember(key,content,{tags,importance,scope,ttlDays})}}}),MEMORY_SCOPES=[`tenant`,`project`,`ticket`];defineTool({name:`memory_forget`,description:`Delete one stored fact from cross-run memory by its key. Use when a fact you (or an earlier run) stored has become WRONG — a decision was reversed, a workaround was removed, a convention changed. Correcting a fact is memory_remember with the same key; this is for facts that should no longer exist at all.`,parameters:{type:`object`,properties:{key:{type:`string`,description:`The key of the fact to delete.`}},required:[`key`]},requires:[`memory`,`memory.forget`],async execute(args,ctx){let key=typeof args.key==`string`?args.key:``;return key.trim()?{data:await ctx.caps.memory.forget(key)}:{data:{ok:!1,error:`key is required`}}}}),defineTool({name:`claim_resource`,description:`Reserve a shared resource before you work on it, so a peer agent working the same ticket does not change it underneath you. Pass a file path ('src/app.ts'), a directory ('src/api/'), or 'repo' for the whole tree. Returns granted:false with the current holder when someone else has it — then work on something else, or leave a workspace_note explaining what you need. Writes to a path held by another agent are refused whether or not you claim first.`,parameters:{type:`object`,properties:{resource:{type:`string`,description:`What to reserve: a repo-relative file path, a directory, or 'repo'.`},mode:{type:`string`,enum:[`exclusive`,`shared`],description:`'exclusive' (default) to write it; 'shared' to signal you are reading it and block others' exclusive claims.`},reason:{type:`string`,description:`One line on why you need it — shown to the peer agent that gets refused.`}},required:[`resource`]},requires:[`coordinate`],async execute(args,ctx){let resource=typeof args.resource==`string`?args.resource:``;if(!resource.trim())return{data:{ok:!1,error:`resource is required`}};let mode=args.mode===`shared`||args.mode===`exclusive`?args.mode:void 0,reason=typeof args.reason==`string`?args.reason:void 0;return{data:await ctx.caps.coordination.claim(resource,{mode,reason})}}}),defineTool({name:`release_resource`,description:`Release a resource you claimed, so a peer agent can take it. Do this as soon as you are finished with it rather than holding it to the end of the run. Every lease this run holds is released automatically when the run ends, so this is an optimisation, not a requirement.`,parameters:{type:`object`,properties:{resource:{type:`string`,description:`The resource string you claimed.`}},required:[`resource`]},requires:[`coordinate`],async execute(args,ctx){let resource=typeof args.resource==`string`?args.resource:``;return resource.trim()?{data:await ctx.caps.coordination.release(resource)}:{data:{ok:!1,error:`resource is required`}}}}),defineTool({name:`workspace_note`,description:`Publish a short note on the shared workspace for this ticket, readable by every agent working it (now or later in the ticket's lifecycle). Use it to declare intent ('I own the DB migration'), hand off a finding, or record a decision a peer must not contradict. Reusing a key overwrites that note. This is WORKING state for the current ticket — durable cross-ticket knowledge belongs in memory_remember.`,parameters:{type:`object`,properties:{key:{type:`string`,description:`Short stable identifier, e.g. 'owns-migration' or 'api-contract'.`},content:{type:`string`,description:`The note, in one or two lines.`}},required:[`key`,`content`]},requires:[`coordinate`],async execute(args,ctx){let key=typeof args.key==`string`?args.key:``,content=typeof args.content==`string`?args.content:``;return!key.trim()||!content.trim()?{data:{ok:!1,error:`key and content are required`}}:{data:await ctx.caps.coordination.postNote(key,content)}}}),defineTool({name:`workspace_read`,description:`Read the shared workspace for this ticket — notes posted by peer agents plus the resources they currently hold. Call this EARLY when a ticket may be staffed by more than one agent, so you plan around what others already own instead of colliding with them.`,parameters:{type:`object`,properties:{query:{type:`string`,description:`Optional filter; omit to read everything.`},limit:{type:`number`,description:`Max notes to return (default 20).`}}},requires:[`coordinate`],async execute(args,ctx){let query=typeof args.query==`string`&&args.query.trim()?args.query:void 0,limit=typeof args.limit==`number`&&Number.isFinite(args.limit)?args.limit:void 0,[notes,leases]=await Promise.all([ctx.caps.coordination.readNotes(query,limit),ctx.caps.coordination.listClaims()]);return notes.ok?{data:{ok:!0,notes:notes.notes??[],heldResources:leases.ok?leases.leases??[]:[]}}:{data:notes}}}),defineTool({name:`web_fetch`,description:`Fetch a single URL and return its readable text content (HTML is reduced to text/markdown). Use to read documentation, an API spec, an issue, or any page you have an exact URL for. Returns the status and the (possibly truncated) content.`,parameters:{type:`object`,properties:{url:{type:`string`,description:`The absolute http(s) URL to fetch.`}},required:[`url`]},requires:[`web`],async execute(args,ctx){let url=typeof args.url==`string`?args.url:``;return url.trim()?{data:await ctx.caps.web.fetch(url)}:{data:{ok:!1,error:`url is required`}}}}),defineTool({name:`web_search`,description:'Search the public web for a query and return ranked results (title, url, snippet) plus `coverage` and `attribution`. Use to discover sources/docs when you don\'t have an exact URL; then web_fetch the most relevant result. `coverage: "owned_index"` means this workspace\'s own previously-crawled corpus answered directly; `"web"` or `"encyclopedic"` means a vendor answered and the found pages are being indexed for next time. When `coverage` is "encyclopedic" the index behind this workspace is narrower than a general web engine — report what you actually found and say what you could not find, rather than filling the gap from memory.',parameters:{type:`object`,properties:{query:{type:`string`,description:`The search query.`}},required:[`query`]},requires:[`web.search`],async execute(args,ctx){let query=typeof args.query==`string`?args.query:``;return query.trim()?ctx.caps.web?.search?{data:await ctx.caps.web.search(query)}:{data:{ok:!1,error:`web search is not available on this surface`}}:{data:{ok:!1,error:`query is required`}}}}),defineTool({name:`run_checks`,description:`Statically validate the files you have written: it parses committed JSON/YAML and runs the platform's shell-free changed-source quality policies, returning structured path/line/rule diagnostics to fix BEFORE finishing. The same validation runs automatically at finish, so it cannot be skipped. IMPORTANT: this serverless executor has NO shell, so it does NOT run the full build, project-wide type-check, lint, or tests — those run in CI on the pull request (the source of truth). Never claim those checks passed.`,parameters:{type:`object`,properties:{}},requires:[`static-check`],async execute(_args,ctx){return{data:await ctx.caps.staticCheck.verify()}}}),defineTool({name:`run_command`,description:`Run a shell command in the checked-out repository (real shell). Use it to install dependencies and run the build, type-check, lint, and tests. Returns combined stdout/stderr and the exit code. Verify your changes this way BEFORE calling finish.`,parameters:{type:`object`,properties:{command:{type:`string`,description:`The shell command to run, e.g. "npm install" or "npm test".`}},required:[`command`]},requires:[`shell`],async execute(args,ctx){let command=typeof args.command==`string`?args.command:``;return command.trim()?{data:await ctx.caps.shell.run(command)}:{data:{ok:!1,error:`command is required`}}}});const askHumanTool=defineTool({name:`ask_human`,description:`Pause and ask a human for input when you are genuinely BLOCKED — a requirement is ambiguous, you cannot find an expected file/system after searching, a decision needs product/business judgement, or you would otherwise have to guess. The run pauses (no further token spend) and the question goes to the team's human-requests queue with a notification; when someone answers, you resume automatically with their answer and continue. Prefer this over guessing or finishing with a "could not proceed" summary — a blocked task that asks gets unblocked; one that gives up silently does not. Do NOT use it for things you can determine yourself with list_files/search_code/read_file.`,parameters:{type:`object`,properties:{question:{type:`string`,description:`The specific question for the human. Be concrete and self-contained — they may not have the full task context.`},context:{type:`string`,description:`Optional: what you have tried / why you are blocked, so the human can answer well.`}},required:[`question`]},requires:[`human`],async execute(args,ctx){let question=typeof args.question==`string`?args.question.trim():``,context=typeof args.context==`string`?args.context:void 0;if(!question)return{data:{ok:!1,error:`question is required to ask a human`}};let r=await ctx.caps.human.ask(question,context);return r.paused?{control:{kind:`ask_human`,approvalId:r.approvalId,question},data:{ok:!0,paused:!0,note:r.note??`Question sent to a human. The run is paused until it is answered; you will resume with the answer.`}}:{data:{ok:!0,paused:!1,answer:r.answer??null,note:r.note}}}});defineTool({name:`update_prd`,description:`Record a change on THIS TICKET'S PRD — the shared spec you were given in your context and that every other agent on this ticket reads. Use mode "append" (the default, and the safe one) to add a dated, signed note: a decision you made, a constraint you discovered, an assumption you had to take, or work you deliberately left out of scope. Use mode "section" ONLY to correct a section that is actually WRONG — it replaces that section's whole body, so pass the full replacement text, not a fragment; name the section by its exact heading (e.g. "Acceptance criteria", "Implementation Notes"). If the heading does not exist the call fails and returns the headings that do — retry with one of those, or append instead. This is not a substitute for doing the work: keep it to what a later run genuinely needs to know.`,parameters:{type:`object`,properties:{mode:{type:`string`,enum:[`append`,`section`],description:`"append" adds a dated, attributed note at the end (nothing already written is lost). "section" REPLACES the named section's body — only for correcting something wrong.`},section:{type:`string`,description:`Required when mode is "section": the exact heading to replace, without the leading "##" (e.g. "Acceptance criteria").`},content:{type:`string`,description:`The markdown to record. For mode "append", the note. For mode "section", the section's COMPLETE new body.`}},required:[`mode`,`content`]},requires:[`prd.write`],async execute(args,ctx){let mode=args.mode===`section`?`section`:`append`,content=typeof args.content==`string`?args.content.trim():``;if(!content)return{data:{ok:!1,error:`content is required`}};if(mode===`section`){let heading=typeof args.section==`string`?args.section.trim():``;return heading?{data:await ctx.caps.prd.editSection(heading,content)}:{data:{ok:!1,error:`section is required when mode is "section" — pass the exact heading to replace, or use mode "append" to add a note instead.`}}}return{data:await ctx.caps.prd.append(content)}}}),defineTool({name:`finish`,description:`Call ONLY when the task is fully complete — every deliverable file written with real, working content (no stubs/placeholders) and every task/PRD requirement implemented. Your changes open a pull request for human review, so a partial scaffold is not "done". Provide a concise summary of what was delivered. Do NOT assert that a build/type-check/lint/test passed — you cannot run those here (CI on the PR verifies). If you are blocked rather than done, call ask_human instead of finishing with a "could not proceed" summary.`,parameters:{type:`object`,properties:{summary:{type:`string`,description:`What was delivered.`}},required:[`summary`]},async execute(args){return{control:{kind:`finish`,summary:typeof args.summary==`string`?args.summary.trim():``},data:{ok:!0}}}}),[...SYMBOL_TOOLS,...SEMANTIC_TOOLS,...GIT_TOOLS];const PSYCH_DIM={honesty:`hexaco.honesty`,emotionality:`hexaco.emotionality`,extraversion:`hexaco.extraversion`,agreeableness:`hexaco.agreeableness`,conscientiousness:`hexaco.conscientiousness`,openness:`hexaco.openness`,regulatoryFocus:`regfocus.orientation`,needForCognition:`cognition.need_for_cognition`,reflection:`cognition.reflection`,decisionRational:`decision.rational`,decisionIntuitive:`decision.intuitive`,decisionDependent:`decision.dependent`,decisionSpontaneous:`decision.spontaneous`,maximizing:`decision.maximizing`,moralCare:`moral.care`,moralFairness:`moral.fairness`,moralLoyalty:`moral.loyalty`,moralAuthority:`moral.authority`,moralSanctity:`moral.sanctity`,moralLiberty:`moral.liberty`,conflictAssertiveness:`conflict.assertiveness`,conflictCooperativeness:`conflict.cooperativeness`,valSelfDirection:`values.self_direction`,valStimulation:`values.stimulation`,valHedonism:`values.hedonism`,valAchievement:`values.achievement`,valPower:`values.power`,valSecurity:`values.security`,valConformity:`values.conformity`,valTradition:`values.tradition`,valBenevolence:`values.benevolence`,valUniversalism:`values.universalism`,grit:`disp.grit`,locusInternal:`disp.locus_internal`,riskTolerance:`disp.risk_tolerance`};function score(vector,id){let raw=vector?.[id];return typeof raw!=`number`||Number.isNaN(raw)?50:Math.max(0,Math.min(100,raw))}const ENNEAGRAM_MOTIVATION={1:`Core drive: be correct and principled — avoid mistakes and sloppiness; hold work to a high standard.`,2:`Core drive: be helpful and needed — anticipate what others require and support the team.`,3:`Core drive: achieve and be effective — optimise for visible results and momentum.`,4:`Core drive: be authentic and distinctive — favour original, considered solutions over rote ones.`,5:`Core drive: understand deeply — gather sufficient information and reason before committing.`,6:`Core drive: be secure and prepared — anticipate failure modes and build in safeguards.`,7:`Core drive: explore options and keep momentum — stay flexible and avoid getting stuck.`,8:`Core drive: take charge and protect — act decisively and own outcomes directly.`,9:`Core drive: keep things stable and harmonious — reduce friction and find common ground.`};function compilePsychometricProfile(profile){let v=profile.vector??{},directives=[],params={},temp=.6,tempMoved=!1,nudgeTemp=delta=>{temp+=delta,tempMoved=!0},consc=score(v,PSYCH_DIM.conscientiousness);consc>=65?(directives.push(`Conscientiousness: plan before acting, work methodically, write and run tests, and double-check before declaring anything done.`),params.thinkLevel=maxThink(params.thinkLevel,`high`)):consc<=35&&directives.push(`Conscientiousness: bias to action — keep things lightweight and avoid over-process.`);let open=score(v,PSYCH_DIM.openness);open>=65?(directives.push(`Openness: explore novel approaches and unconventional ideas before settling on the obvious one.`),nudgeTemp(.15)):open<=35&&(directives.push(`Openness: prefer proven, conventional patterns over novel experiments.`),nudgeTemp(-.15));let emo=score(v,PSYCH_DIM.emotionality);emo>=65?directives.push(`Emotionality: surface risks and uncertainties early, and escalate when genuinely unsure rather than guessing.`):emo<=35&&directives.push(`Emotionality: stay calm under ambiguity — avoid over-escalating; only flag material risks.`);let extra=score(v,PSYCH_DIM.extraversion);extra>=65?directives.push(`Extraversion: be proactive and communicative — volunteer progress and options.`):extra<=35&&directives.push(`Extraversion: be concise — act first, narrate only what matters.`);let agree=score(v,PSYCH_DIM.agreeableness);agree>=65?directives.push(`Agreeableness: seek consensus and accommodate others' constraints where reasonable.`):agree<=35&&directives.push(`Agreeableness: push back directly and challenge weak reasoning rather than deferring.`),score(v,PSYCH_DIM.honesty)>=65&&directives.push(`Honesty-Humility: never fabricate, admit uncertainty plainly, resist sycophancy, and refuse to overstate results even when asked.`);let reg=score(v,PSYCH_DIM.regulatoryFocus);reg>=65?(directives.push(`Regulatory focus (promotion): optimise for opportunity and speed; accept calculated, reversible risk to move faster.`),nudgeTemp(.1)):reg<=35&&(directives.push(`Regulatory focus (prevention): optimise for safety and correctness; add guardrails and avoid errors over chasing upside.`),nudgeTemp(-.1),params.thinkLevel=maxThink(params.thinkLevel,`medium`));let nfc=score(v,PSYCH_DIM.needForCognition);nfc>=65?(directives.push(`Cognition: reason step-by-step and analyse deeply before concluding; show your working on hard problems.`),params.thinkLevel=maxThink(params.thinkLevel,`high`),params.reasoningLevel=`on`):nfc<=35&&(directives.push(`Cognition: trust pattern-matching and act decisively; do not over-analyse routine work.`),params.thinkLevel=maxThink(params.thinkLevel,`low`)),score(v,PSYCH_DIM.reflection)>=65&&directives.push(`Reflection: distrust the first intuitive answer — verify your reasoning before acting on it.`),score(v,PSYCH_DIM.decisionRational)>=65&&directives.push(`Decision style: decide analytically, making trade-offs explicit.`),score(v,PSYCH_DIM.decisionIntuitive)>=65&&directives.push(`Decision style: trust well-earned intuition when evidence is thin.`),score(v,PSYCH_DIM.decisionDependent)>=65&&directives.push(`Decision style: seek input or confirmation from a human or peer before committing to consequential choices.`),score(v,PSYCH_DIM.decisionSpontaneous)>=65&&directives.push(`Decision style: decide quickly and keep momentum; do not belabour reversible calls.`);let maxim=score(v,PSYCH_DIM.maximizing);maxim>=65?directives.push(`Thoroughness: compare alternatives and optimise before settling — aim for the best option, not the first workable one.`):maxim<=35&&directives.push(`Thoroughness: stop at good-enough; do not gold-plate or over-polish working solutions.`);let strongMoral=[[`care`,score(v,PSYCH_DIM.moralCare),`prioritise user wellbeing and avoid harm`],[`fairness`,score(v,PSYCH_DIM.moralFairness),`treat stakeholders equitably and proportionately`],[`loyalty`,score(v,PSYCH_DIM.moralLoyalty),`protect the team's and project's interests`],[`authority`,score(v,PSYCH_DIM.moralAuthority),`respect established policy and ownership boundaries`],[`sanctity`,score(v,PSYCH_DIM.moralSanctity),`uphold code, data, and process integrity`],[`liberty`,score(v,PSYCH_DIM.moralLiberty),`preserve user autonomy and avoid over-constraining`]].filter(([,s])=>s>=65).map(([,,phrase])=>phrase);strongMoral.length>0&&directives.push(`Values lens: when resolving trade-offs, ${strongMoral.join(`; `)}.`);let assertive=score(v,PSYCH_DIM.conflictAssertiveness),coop=score(v,PSYCH_DIM.conflictCooperativeness);(assertive!==50||coop!==50)&&(assertive>=65&&coop>=65?directives.push(`Conflict mode (collaborating): in disagreement, integrate views and seek a win-win.`):assertive>=65&&coop<=35?directives.push(`Conflict mode (competing): stand firm on your position when you are confident it is right.`):assertive<=35&&coop>=65?directives.push(`Conflict mode (accommodating): yield to others' preferences to preserve a working relationship.`):assertive<=35&&coop<=35?directives.push(`Conflict mode (avoiding): sidestep low-value conflicts and pick your battles.`):directives.push(`Conflict mode (compromising): look for a fair middle ground when views differ.`));let topValues=[[`self-direction`,score(v,PSYCH_DIM.valSelfDirection)],[`stimulation`,score(v,PSYCH_DIM.valStimulation)],[`hedonism`,score(v,PSYCH_DIM.valHedonism)],[`achievement`,score(v,PSYCH_DIM.valAchievement)],[`power`,score(v,PSYCH_DIM.valPower)],[`security`,score(v,PSYCH_DIM.valSecurity)],[`conformity`,score(v,PSYCH_DIM.valConformity)],[`tradition`,score(v,PSYCH_DIM.valTradition)],[`benevolence`,score(v,PSYCH_DIM.valBenevolence)],[`universalism`,score(v,PSYCH_DIM.valUniversalism)]].filter(([,s])=>s>=65).sort((a,b)=>b[1]-a[1]).slice(0,3).map(([name])=>name);topValues.length>0&&directives.push(`Guiding values: prioritise ${topValues.join(`, `)} when goals conflict.`);let grit=score(v,PSYCH_DIM.grit);grit>=65?directives.push(`Grit: persist through obstacles — retry intelligently and exhaust reasonable approaches before giving up or escalating.`):grit<=35&&directives.push(`Grit: if an approach stalls, escalate or ask for direction rather than grinding.`),score(v,PSYCH_DIM.locusInternal)>=65&&directives.push(`Ownership: treat outcomes as within your control — own failures and drive them to resolution.`);let risk=score(v,PSYCH_DIM.riskTolerance);return risk>=65?(directives.push(`Risk: accept calculated risks for speed, but keep changes reversible.`),nudgeTemp(.1)):risk<=35&&(directives.push(`Risk: prefer safe, reversible steps and confirm before any destructive or hard-to-undo action.`),nudgeTemp(-.1)),typeof profile.enneagramType==`number`&&ENNEAGRAM_MOTIVATION[profile.enneagramType]&&directives.push(ENNEAGRAM_MOTIVATION[profile.enneagramType]),tempMoved&&(params.temperature=Math.round(Math.max(.1,Math.min(1,temp))*100)/100),{directives,params}}function buildPsychometricBlock(profile){if(!profile)return``;let{directives}=compilePsychometricProfile(profile);return directives.length===0?``:bulletBlock(`Personality (execute under these traits):`,directives)}function mergeExecParams(profiles){let merged={},temps=[];for(let profile of profiles){let{params}=compilePsychometricProfile(profile);params.thinkLevel&&(merged.thinkLevel=maxThink(merged.thinkLevel,params.thinkLevel)),params.reasoningLevel===`on`&&(merged.reasoningLevel=`on`),typeof params.temperature==`number`&&temps.push(params.temperature)}return temps.length>0&&(merged.temperature=Math.round(temps.reduce((a,b)=>a+b,0)/temps.length*100)/100),merged}function limbicTraits(profile){let v=profile?.vector;return{openness:score(v,PSYCH_DIM.openness),emotionality:score(v,PSYCH_DIM.emotionality),conscientiousness:score(v,PSYCH_DIM.conscientiousness),extraversion:score(v,PSYCH_DIM.extraversion),regulatoryFocus:score(v,PSYCH_DIM.regulatoryFocus),riskTolerance:score(v,PSYCH_DIM.riskTolerance),grit:score(v,PSYCH_DIM.grit),stimulation:score(v,PSYCH_DIM.valStimulation)}}function mergeLimbicWithPsychometric(psych,limbic){let out={...psych};if(limbic.thinkLevel&&(out.thinkLevel=maxThink(psych.thinkLevel,limbic.thinkLevel)),(limbic.reasoningLevel===`on`||psych.reasoningLevel===`on`)&&(out.reasoningLevel=`on`),typeof limbic.temperatureDelta==`number`&&limbic.temperatureDelta!==0){let baseline=psych.temperature??.6;out.temperature=Math.round(Math.max(.1,Math.min(1,baseline+limbic.temperatureDelta))*100)/100}return out}const KNOWLEDGE_DIRS={root:`.builderforce`,memory:`memory`,sessions:`sessions`};function deriveActivitySummary(activity){let created=[...new Set(activity.created??[])],edited=[...new Set(activity.edited??[])],tools=[...new Set(activity.tools??[])],hasCreate=created.length>0,hasEdit=edited.length>0,toolSet=new Set(tools),isTest=[...created,...edited].some(f=>f.includes(`.test.`)||f.includes(`.spec.`)||f.includes(`__tests__`))||toolSet.has(`test`),isAnalysis=!hasCreate&&!hasEdit&&(toolSet.has(`grep`)||toolSet.has(`glob`)||toolSet.has(`view`))&&!toolSet.has(`bash`),isReview=toolSet.has(`git_history`)||toolSet.has(`code_analysis`)||toolSet.has(`project_knowledge`);return toolSet.has(`orchestrate`)||toolSet.has(`workflow_status`)?`Multi-agent workflow execution`:isReview?`Code review / analysis`:isTest&&hasCreate?`Test suite created`:isTest&&hasEdit?`Tests updated`:isAnalysis?`Codebase exploration / read-only analysis`:hasCreate&&hasEdit?`Feature implementation: new files + edits`:hasCreate?`New file(s) created: ${created.length}`:hasEdit?`Code modifications: ${edited.length} file(s) changed`:tools.length>0?`Agent activity (no file changes)`:``}function buildKnowledgeMemoryEntry(params){let lines=[`\n## [${params.ts}] session:${params.sessionKey}`,``],meaningful=!1;if(params.activity){let created=[...new Set(params.activity.created??[])],edited=[...new Set(params.activity.edited??[])],tools=[...new Set(params.activity.tools??[])];created.length>0&&(lines.push(`**Created**: ${created.join(`, `)}`),meaningful=!0),edited.length>0&&(lines.push(`**Edited**: ${edited.join(`, `)}`),meaningful=!0),tools.length>0&&(lines.push(`**Tools**: ${tools.join(`, `)}`),meaningful=!0);let summary=deriveActivitySummary({created,edited,tools});summary&&(lines.push(`**Summary**: ${summary}`),meaningful=!0)}return meaningful?(lines.push(``),lines.join(`
16
+ `)):null}defineTool({name:`skill_propose`,description:`Propose a reusable SKILL — a procedure a future agent can follow — drafted from work you just completed and verified. Use it when you worked out a repeatable way to do something non-obvious in this codebase (a migration + guard + test sequence, a release path, a debugging route) and a future run would otherwise rediscover it. Do NOT propose a skill for a one-off fix, for something the repo already documents, or for a procedure you did not actually complete. The draft goes to a human for review; it does not take effect until approved.`,parameters:{type:`object`,properties:{slug:{type:`string`,description:`Stable kebab-case id, e.g. 'add-a-schema-column'. Re-using one revises your existing draft.`},name:{type:`string`,description:`Short human title, e.g. 'Add a schema column end to end'.`},description:{type:`string`,description:`One line saying WHEN to use this skill — a future agent matches on this, so name the situation, not the steps.`},body:{type:`string`,description:`The procedure as Markdown: ordered steps, exact commands, and how to tell it worked.`},evidence:{type:`string`,description:`What proves this procedure works — the graded proof, the merged PR, the passing check.`}},required:[`slug`,`name`,`description`,`body`]},requires:[`skill.author`],async execute(args,ctx){let str=v=>typeof v==`string`?v.trim():``,slug=str(args.slug),name=str(args.name),description=str(args.description),body=str(args.body);if(!slug||!name||!description||!body)return{data:{ok:!1,error:`slug, name, description and body are all required`}};let evidence=str(args.evidence);return{data:await ctx.caps.skillAuthor.propose({slug,name,description,body,...evidence?{evidence}:{}})}}}),defineTool({name:`skill_list`,description:`List the skills this workspace already has — approved ones you can follow, and drafts awaiting review. Call it before proposing, so you revise an existing draft instead of adding a near-duplicate.`,parameters:{type:`object`,properties:{}},requires:[`skill.author`],async execute(_args,ctx){return{data:await ctx.caps.skillAuthor.list()}}});const ROLE_ENUM_DESCRIPTION=MODEL_ROLES.map(role=>`${role} — ${MODEL_ROLE_DESCRIPTIONS[role]}`).join(` · `);defineTool({name:`spawn_agent`,description:"Delegate a self-contained sub-task to a child agent that works in its OWN context and reports back a single answer. Use it when finding something out would take many turns you do not want to carry — locating where a behaviour lives across an unfamiliar tree, checking whether a pattern is used anywhere else, summarising a large file you only need one fact from. The child sees NOTHING of this conversation, so `task` must state everything it needs to know, and it answers in prose — it cannot hand you files or tool output. Do NOT delegate work you can do in a turn or two, and do NOT delegate the actual writing of the deliverable: you are accountable for what ships.",parameters:{type:`object`,properties:{label:{type:`string`,description:`A few words naming the delegation, e.g. 'locate the auth middleware'. Shown on the run timeline.`},task:{type:`string`,description:`The child's complete brief: what to find out or do, where to look, and exactly what to report back. Assume it knows nothing about the ticket beyond what you write here.`},read_only:{type:`boolean`,description:`Default true — the child may read, search and reason but not modify the working tree. Pass false ONLY when the delegated work is itself an edit you want it to make.`},role:{type:`string`,enum:[...MODEL_ROLES],description:`What kind of call the child's turns are — lets the surface pick a model suited to the work rather than reusing yours. Defaults to 'explore' when read_only, else 'code'. ${ROLE_ENUM_DESCRIPTION}`},as_agent:{type:`string`,description:`Run the child AS one of the workspace's agents — its id or name (e.g. 'Ada'). The child adopts that agent's role, bio, skills and personality so the delegated slice is done in that agent's voice and expertise. Prefer an agent already in this chat (builtin_chats_list_agents) or from builtin_cloud_agents_list_mine.`}},required:[`label`,`task`]},requires:[`orchestrate`],async execute(args,ctx){let str=v=>typeof v==`string`?v.trim():``,label=str(args.label),task=str(args.task);if(!task)return{data:{ok:!1,error:`task is required — the child sees none of your conversation`}};let readOnly=args.read_only!==!1,role=delegationRole(args.role,readOnly),asAgent=str(args.as_agent),r=await ctx.caps.orchestration.spawn({label:label||task.slice(0,60),task,readOnly,role,...asAgent?{asAgent}:{}});return{data:r,...r.ok?{}:{isError:!0}}}});function stableStringify(value){if(value==null)return`null`;if(typeof value==`number`&&!Number.isFinite(value))return JSON.stringify(String(value));if(typeof value==`bigint`)return JSON.stringify(value.toString());if(typeof value!=`object`)return JSON.stringify(value)??`null`;if(value instanceof Error)return stableStringify({name:value.name,message:value.message,stack:value.stack});if(value instanceof Uint8Array)return stableStringify({type:`Uint8Array`,data:Array.from(value)});if(Array.isArray(value))return`[${value.map(stableStringify).join(`,`)}]`;let record=value;return`{${Object.keys(record).filter(k=>record[k]!==void 0).sort().map(k=>`${JSON.stringify(k)}:${stableStringify(record[k])}`).join(`,`)}}`}export{resolveEngineById as C,normalizeScopeDir as D,isUnderScopeDir as E,applyStringEdit as O,CURRENT_ENGINE_ID as S,filterByGlob as T,extractSymbols as _,buildPsychometricBlock as a,maxThink as b,mergeLimbicWithPsychometric as c,editFileTool as d,listFilesTool as f,SYMBOL_KINDS as g,writeFileTool as h,deriveActivitySummary as i,askHumanTool as l,memoryRememberTool as m,KNOWLEDGE_DIRS as n,limbicTraits as o,memoryRecallTool as p,buildKnowledgeMemoryEntry as r,mergeExecParams as s,stableStringify as t,deleteFileTool as u,isSymbolIndexable as v,ToolRegistry as w,renderPolicyDirectives as x,coercePolicyGates as y};