@akon-labs/nexus-cli 0.1.8 → 0.1.10

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 (320) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +1 -0
  3. package/README.md +3 -2
  4. package/dist/app-bridge.bundle.js +67 -0
  5. package/dist/cli.js +19934 -1869
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +1 -1
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/auto-model.d.ts +1 -1
  10. package/dist/core/auto-model.d.ts.map +1 -1
  11. package/dist/core/auto-model.js +1 -1
  12. package/dist/core/auto-model.js.map +1 -1
  13. package/dist/core/model-resolver.d.ts +2 -2
  14. package/dist/core/model-resolver.d.ts.map +1 -1
  15. package/dist/core/model-resolver.js +3 -3
  16. package/dist/core/model-resolver.js.map +1 -1
  17. package/dist/core/package-manager.d.ts +15 -0
  18. package/dist/core/package-manager.d.ts.map +1 -1
  19. package/dist/core/package-manager.js +50 -0
  20. package/dist/core/package-manager.js.map +1 -1
  21. package/dist/core/sdk.d.ts.map +1 -1
  22. package/dist/core/sdk.js +1 -1
  23. package/dist/core/sdk.js.map +1 -1
  24. package/dist/core/slash-commands.d.ts.map +1 -1
  25. package/dist/core/slash-commands.js +3 -3
  26. package/dist/core/slash-commands.js.map +1 -1
  27. package/dist/core/system-prompt.d.ts.map +1 -1
  28. package/dist/core/system-prompt.js +1 -1
  29. package/dist/core/system-prompt.js.map +1 -1
  30. package/dist/core/tools/index.d.ts +1 -1
  31. package/dist/core/tools/index.d.ts.map +1 -1
  32. package/dist/core/tools/index.js +5 -5
  33. package/dist/core/tools/index.js.map +1 -1
  34. package/dist/core/tools/websearch.d.ts.map +1 -1
  35. package/dist/core/tools/websearch.js +3 -3
  36. package/dist/core/tools/websearch.js.map +1 -1
  37. package/dist/extensions/gitnexus/gateway.d.ts +6 -6
  38. package/dist/extensions/gitnexus/gateway.d.ts.map +1 -1
  39. package/dist/extensions/gitnexus/gateway.js +4 -4
  40. package/dist/extensions/gitnexus/gateway.js.map +1 -1
  41. package/dist/extensions/gitnexus/index.d.ts.map +1 -1
  42. package/dist/extensions/gitnexus/index.js +4 -2
  43. package/dist/extensions/gitnexus/index.js.map +1 -1
  44. package/dist/extensions/mcp/abort.d.ts +3 -0
  45. package/dist/extensions/mcp/abort.d.ts.map +1 -0
  46. package/dist/extensions/mcp/abort.js +41 -0
  47. package/dist/extensions/mcp/abort.js.map +1 -0
  48. package/dist/extensions/mcp/agent-dir.d.ts +6 -0
  49. package/dist/extensions/mcp/agent-dir.d.ts.map +1 -0
  50. package/dist/extensions/mcp/agent-dir.js +18 -0
  51. package/dist/extensions/mcp/agent-dir.js.map +1 -0
  52. package/dist/extensions/mcp/agent-plugin-loader.d.ts +9 -0
  53. package/dist/extensions/mcp/agent-plugin-loader.d.ts.map +1 -0
  54. package/dist/extensions/mcp/agent-plugin-loader.js +384 -0
  55. package/dist/extensions/mcp/agent-plugin-loader.js.map +1 -0
  56. package/dist/extensions/mcp/app-bridge.bundle.js +67 -0
  57. package/dist/extensions/mcp/commands.d.ts +27 -0
  58. package/dist/extensions/mcp/commands.d.ts.map +1 -0
  59. package/dist/extensions/mcp/commands.js +629 -0
  60. package/dist/extensions/mcp/commands.js.map +1 -0
  61. package/dist/extensions/mcp/config.d.ts +121 -38
  62. package/dist/extensions/mcp/config.d.ts.map +1 -1
  63. package/dist/extensions/mcp/config.js +1030 -70
  64. package/dist/extensions/mcp/config.js.map +1 -1
  65. package/dist/extensions/mcp/consent-manager.d.ts +14 -0
  66. package/dist/extensions/mcp/consent-manager.d.ts.map +1 -0
  67. package/dist/extensions/mcp/consent-manager.js +60 -0
  68. package/dist/extensions/mcp/consent-manager.js.map +1 -0
  69. package/dist/extensions/mcp/direct-tools.d.ts +11 -0
  70. package/dist/extensions/mcp/direct-tools.d.ts.map +1 -0
  71. package/dist/extensions/mcp/direct-tools.js +562 -0
  72. package/dist/extensions/mcp/direct-tools.js.map +1 -0
  73. package/dist/extensions/mcp/elicitation-handler.d.ts +18 -0
  74. package/dist/extensions/mcp/elicitation-handler.d.ts.map +1 -0
  75. package/dist/extensions/mcp/elicitation-handler.js +320 -0
  76. package/dist/extensions/mcp/elicitation-handler.js.map +1 -0
  77. package/dist/extensions/mcp/error-signal.d.ts +16 -0
  78. package/dist/extensions/mcp/error-signal.d.ts.map +1 -0
  79. package/dist/extensions/mcp/error-signal.js +22 -0
  80. package/dist/extensions/mcp/error-signal.js.map +1 -0
  81. package/dist/extensions/mcp/errors.d.ts +104 -0
  82. package/dist/extensions/mcp/errors.d.ts.map +1 -0
  83. package/dist/extensions/mcp/errors.js +171 -0
  84. package/dist/extensions/mcp/errors.js.map +1 -0
  85. package/dist/extensions/mcp/glimpse-ui.d.ts +10 -0
  86. package/dist/extensions/mcp/glimpse-ui.d.ts.map +1 -0
  87. package/dist/extensions/mcp/glimpse-ui.js +67 -0
  88. package/dist/extensions/mcp/glimpse-ui.js.map +1 -0
  89. package/dist/extensions/mcp/host-html-template.d.ts +17 -0
  90. package/dist/extensions/mcp/host-html-template.d.ts.map +1 -0
  91. package/dist/extensions/mcp/host-html-template.js +442 -0
  92. package/dist/extensions/mcp/host-html-template.js.map +1 -0
  93. package/dist/extensions/mcp/index.d.ts +20 -15
  94. package/dist/extensions/mcp/index.d.ts.map +1 -1
  95. package/dist/extensions/mcp/index.js +1064 -82
  96. package/dist/extensions/mcp/index.js.map +1 -1
  97. package/dist/extensions/mcp/init.d.ts +28 -0
  98. package/dist/extensions/mcp/init.d.ts.map +1 -0
  99. package/dist/extensions/mcp/init.js +631 -0
  100. package/dist/extensions/mcp/init.js.map +1 -0
  101. package/dist/extensions/mcp/json-schema-validator.d.ts +3 -0
  102. package/dist/extensions/mcp/json-schema-validator.d.ts.map +1 -0
  103. package/dist/extensions/mcp/json-schema-validator.js +53 -0
  104. package/dist/extensions/mcp/json-schema-validator.js.map +1 -0
  105. package/dist/extensions/mcp/lifecycle.d.ts +56 -0
  106. package/dist/extensions/mcp/lifecycle.d.ts.map +1 -0
  107. package/dist/extensions/mcp/lifecycle.js +395 -0
  108. package/dist/extensions/mcp/lifecycle.js.map +1 -0
  109. package/dist/extensions/mcp/logger.d.ts +52 -0
  110. package/dist/extensions/mcp/logger.d.ts.map +1 -0
  111. package/dist/extensions/mcp/logger.js +134 -0
  112. package/dist/extensions/mcp/logger.js.map +1 -0
  113. package/dist/extensions/mcp/mcp-auth-flow.d.ts +117 -0
  114. package/dist/extensions/mcp/mcp-auth-flow.d.ts.map +1 -0
  115. package/dist/extensions/mcp/mcp-auth-flow.js +869 -0
  116. package/dist/extensions/mcp/mcp-auth-flow.js.map +1 -0
  117. package/dist/extensions/mcp/mcp-auth.d.ts +162 -0
  118. package/dist/extensions/mcp/mcp-auth.d.ts.map +1 -0
  119. package/dist/extensions/mcp/mcp-auth.js +897 -0
  120. package/dist/extensions/mcp/mcp-auth.js.map +1 -0
  121. package/dist/extensions/mcp/mcp-bearer-store.d.ts +25 -0
  122. package/dist/extensions/mcp/mcp-bearer-store.d.ts.map +1 -0
  123. package/dist/extensions/mcp/mcp-bearer-store.js +350 -0
  124. package/dist/extensions/mcp/mcp-bearer-store.js.map +1 -0
  125. package/dist/extensions/mcp/mcp-callback-server.d.ts +54 -0
  126. package/dist/extensions/mcp/mcp-callback-server.d.ts.map +1 -0
  127. package/dist/extensions/mcp/mcp-callback-server.js +454 -0
  128. package/dist/extensions/mcp/mcp-callback-server.js.map +1 -0
  129. package/dist/extensions/mcp/mcp-code.d.ts +48 -0
  130. package/dist/extensions/mcp/mcp-code.d.ts.map +1 -0
  131. package/dist/extensions/mcp/mcp-code.js +358 -0
  132. package/dist/extensions/mcp/mcp-code.js.map +1 -0
  133. package/dist/extensions/mcp/mcp-keyring-helper.cjs +89 -0
  134. package/dist/extensions/mcp/mcp-oauth-provider.d.ts +135 -0
  135. package/dist/extensions/mcp/mcp-oauth-provider.d.ts.map +1 -0
  136. package/dist/extensions/mcp/mcp-oauth-provider.js +491 -0
  137. package/dist/extensions/mcp/mcp-oauth-provider.js.map +1 -0
  138. package/dist/extensions/mcp/mcp-output-guard.d.ts +59 -0
  139. package/dist/extensions/mcp/mcp-output-guard.d.ts.map +1 -0
  140. package/dist/extensions/mcp/mcp-output-guard.js +343 -0
  141. package/dist/extensions/mcp/mcp-output-guard.js.map +1 -0
  142. package/dist/extensions/mcp/mcp-panel.d.ts +71 -0
  143. package/dist/extensions/mcp/mcp-panel.d.ts.map +1 -0
  144. package/dist/extensions/mcp/mcp-panel.js +973 -0
  145. package/dist/extensions/mcp/mcp-panel.js.map +1 -0
  146. package/dist/extensions/mcp/mcp-probe.d.ts +7 -0
  147. package/dist/extensions/mcp/mcp-probe.d.ts.map +1 -0
  148. package/dist/extensions/mcp/mcp-probe.js +155 -0
  149. package/dist/extensions/mcp/mcp-probe.js.map +1 -0
  150. package/dist/extensions/mcp/mcp-script-worker.mjs +130 -0
  151. package/dist/extensions/mcp/mcp-setup-panel.d.ts +85 -0
  152. package/dist/extensions/mcp/mcp-setup-panel.d.ts.map +1 -0
  153. package/dist/extensions/mcp/mcp-setup-panel.js +618 -0
  154. package/dist/extensions/mcp/mcp-setup-panel.js.map +1 -0
  155. package/dist/extensions/mcp/mcp-status.d.ts +12 -0
  156. package/dist/extensions/mcp/mcp-status.d.ts.map +1 -0
  157. package/dist/extensions/mcp/mcp-status.js +98 -0
  158. package/dist/extensions/mcp/mcp-status.js.map +1 -0
  159. package/dist/extensions/mcp/mcp-trace.d.ts +95 -0
  160. package/dist/extensions/mcp/mcp-trace.d.ts.map +1 -0
  161. package/dist/extensions/mcp/mcp-trace.js +252 -0
  162. package/dist/extensions/mcp/mcp-trace.js.map +1 -0
  163. package/dist/extensions/mcp/metadata-cache.d.ts +20 -0
  164. package/dist/extensions/mcp/metadata-cache.d.ts.map +1 -0
  165. package/dist/extensions/mcp/metadata-cache.js +318 -0
  166. package/dist/extensions/mcp/metadata-cache.js.map +1 -0
  167. package/dist/extensions/mcp/npx-resolver.d.ts +7 -0
  168. package/dist/extensions/mcp/npx-resolver.d.ts.map +1 -0
  169. package/dist/extensions/mcp/npx-resolver.js +505 -0
  170. package/dist/extensions/mcp/npx-resolver.js.map +1 -0
  171. package/dist/extensions/mcp/oauth.d.ts +21 -0
  172. package/dist/extensions/mcp/oauth.d.ts.map +1 -0
  173. package/dist/extensions/mcp/oauth.js +15 -0
  174. package/dist/extensions/mcp/oauth.js.map +1 -0
  175. package/dist/extensions/mcp/onboarding-state.d.ts +13 -0
  176. package/dist/extensions/mcp/onboarding-state.d.ts.map +1 -0
  177. package/dist/extensions/mcp/onboarding-state.js +65 -0
  178. package/dist/extensions/mcp/onboarding-state.js.map +1 -0
  179. package/dist/extensions/mcp/package-mcp-loader.d.ts +3 -0
  180. package/dist/extensions/mcp/package-mcp-loader.d.ts.map +1 -0
  181. package/dist/extensions/mcp/package-mcp-loader.js +170 -0
  182. package/dist/extensions/mcp/package-mcp-loader.js.map +1 -0
  183. package/dist/extensions/mcp/panel-keys.d.ts +21 -0
  184. package/dist/extensions/mcp/panel-keys.d.ts.map +1 -0
  185. package/dist/extensions/mcp/panel-keys.js +29 -0
  186. package/dist/extensions/mcp/panel-keys.js.map +1 -0
  187. package/dist/extensions/mcp/prompts.d.ts +63 -0
  188. package/dist/extensions/mcp/prompts.d.ts.map +1 -0
  189. package/dist/extensions/mcp/prompts.js +297 -0
  190. package/dist/extensions/mcp/prompts.js.map +1 -0
  191. package/dist/extensions/mcp/proxy-modes.d.ts +15 -0
  192. package/dist/extensions/mcp/proxy-modes.d.ts.map +1 -0
  193. package/dist/extensions/mcp/proxy-modes.js +1421 -0
  194. package/dist/extensions/mcp/proxy-modes.js.map +1 -0
  195. package/dist/extensions/mcp/request-headers-command.d.ts +11 -0
  196. package/dist/extensions/mcp/request-headers-command.d.ts.map +1 -0
  197. package/dist/extensions/mcp/request-headers-command.js +334 -0
  198. package/dist/extensions/mcp/request-headers-command.js.map +1 -0
  199. package/dist/extensions/mcp/resource-tools.d.ts +2 -0
  200. package/dist/extensions/mcp/resource-tools.d.ts.map +1 -0
  201. package/dist/extensions/mcp/resource-tools.js +15 -0
  202. package/dist/extensions/mcp/resource-tools.js.map +1 -0
  203. package/dist/extensions/mcp/runtime-owner.d.ts +14 -0
  204. package/dist/extensions/mcp/runtime-owner.d.ts.map +1 -0
  205. package/dist/extensions/mcp/runtime-owner.js +84 -0
  206. package/dist/extensions/mcp/runtime-owner.js.map +1 -0
  207. package/dist/extensions/mcp/sampling-handler.d.ts +18 -0
  208. package/dist/extensions/mcp/sampling-handler.d.ts.map +1 -0
  209. package/dist/extensions/mcp/sampling-handler.js +229 -0
  210. package/dist/extensions/mcp/sampling-handler.js.map +1 -0
  211. package/dist/extensions/mcp/search-ranking.d.ts +25 -0
  212. package/dist/extensions/mcp/search-ranking.d.ts.map +1 -0
  213. package/dist/extensions/mcp/search-ranking.js +234 -0
  214. package/dist/extensions/mcp/search-ranking.js.map +1 -0
  215. package/dist/extensions/mcp/server-manager.d.ts +116 -0
  216. package/dist/extensions/mcp/server-manager.d.ts.map +1 -0
  217. package/dist/extensions/mcp/server-manager.js +1052 -0
  218. package/dist/extensions/mcp/server-manager.js.map +1 -0
  219. package/dist/extensions/mcp/session-recovery.d.ts +27 -0
  220. package/dist/extensions/mcp/session-recovery.d.ts.map +1 -0
  221. package/dist/extensions/mcp/session-recovery.js +140 -0
  222. package/dist/extensions/mcp/session-recovery.js.map +1 -0
  223. package/dist/extensions/mcp/skills/mcp-scripting/SKILL.md +38 -0
  224. package/dist/extensions/mcp/state.d.ts +62 -0
  225. package/dist/extensions/mcp/state.d.ts.map +1 -0
  226. package/dist/extensions/mcp/state.js +2 -0
  227. package/dist/extensions/mcp/state.js.map +1 -0
  228. package/dist/extensions/mcp/tool-approval.d.ts +11 -0
  229. package/dist/extensions/mcp/tool-approval.d.ts.map +1 -0
  230. package/dist/extensions/mcp/tool-approval.js +156 -0
  231. package/dist/extensions/mcp/tool-approval.js.map +1 -0
  232. package/dist/extensions/mcp/tool-metadata.d.ts +11 -0
  233. package/dist/extensions/mcp/tool-metadata.d.ts.map +1 -0
  234. package/dist/extensions/mcp/tool-metadata.js +264 -0
  235. package/dist/extensions/mcp/tool-metadata.js.map +1 -0
  236. package/dist/extensions/mcp/tool-registrar.d.ts +21 -0
  237. package/dist/extensions/mcp/tool-registrar.d.ts.map +1 -0
  238. package/dist/extensions/mcp/tool-registrar.js +229 -0
  239. package/dist/extensions/mcp/tool-registrar.js.map +1 -0
  240. package/dist/extensions/mcp/tool-result-renderer.d.ts +83 -0
  241. package/dist/extensions/mcp/tool-result-renderer.d.ts.map +1 -0
  242. package/dist/extensions/mcp/tool-result-renderer.js +345 -0
  243. package/dist/extensions/mcp/tool-result-renderer.js.map +1 -0
  244. package/dist/extensions/mcp/ts-shape.d.ts +3 -0
  245. package/dist/extensions/mcp/ts-shape.d.ts.map +1 -0
  246. package/dist/extensions/mcp/ts-shape.js +160 -0
  247. package/dist/extensions/mcp/ts-shape.js.map +1 -0
  248. package/dist/extensions/mcp/types.d.ts +521 -0
  249. package/dist/extensions/mcp/types.d.ts.map +1 -0
  250. package/dist/extensions/mcp/types.js +294 -0
  251. package/dist/extensions/mcp/types.js.map +1 -0
  252. package/dist/extensions/mcp/ui-app-bridge-helpers.d.ts +7 -0
  253. package/dist/extensions/mcp/ui-app-bridge-helpers.d.ts.map +1 -0
  254. package/dist/extensions/mcp/ui-app-bridge-helpers.js +37 -0
  255. package/dist/extensions/mcp/ui-app-bridge-helpers.js.map +1 -0
  256. package/dist/extensions/mcp/ui-resource-handler.d.ts +18 -0
  257. package/dist/extensions/mcp/ui-resource-handler.d.ts.map +1 -0
  258. package/dist/extensions/mcp/ui-resource-handler.js +210 -0
  259. package/dist/extensions/mcp/ui-resource-handler.js.map +1 -0
  260. package/dist/extensions/mcp/ui-server.d.ts +52 -0
  261. package/dist/extensions/mcp/ui-server.d.ts.map +1 -0
  262. package/dist/extensions/mcp/ui-server.js +740 -0
  263. package/dist/extensions/mcp/ui-server.js.map +1 -0
  264. package/dist/extensions/mcp/ui-session.d.ts +40 -0
  265. package/dist/extensions/mcp/ui-session.d.ts.map +1 -0
  266. package/dist/extensions/mcp/ui-session.js +477 -0
  267. package/dist/extensions/mcp/ui-session.js.map +1 -0
  268. package/dist/extensions/mcp/ui-stream-types.d.ts +105 -0
  269. package/dist/extensions/mcp/ui-stream-types.d.ts.map +1 -0
  270. package/dist/extensions/mcp/ui-stream-types.js +61 -0
  271. package/dist/extensions/mcp/ui-stream-types.js.map +1 -0
  272. package/dist/extensions/mcp/ui-tool-visibility.d.ts +5 -0
  273. package/dist/extensions/mcp/ui-tool-visibility.d.ts.map +1 -0
  274. package/dist/extensions/mcp/ui-tool-visibility.js +27 -0
  275. package/dist/extensions/mcp/ui-tool-visibility.js.map +1 -0
  276. package/dist/extensions/mcp/unix-socket-transport.d.ts +15 -0
  277. package/dist/extensions/mcp/unix-socket-transport.d.ts.map +1 -0
  278. package/dist/extensions/mcp/unix-socket-transport.js +85 -0
  279. package/dist/extensions/mcp/unix-socket-transport.js.map +1 -0
  280. package/dist/extensions/mcp/utils.d.ts +32 -0
  281. package/dist/extensions/mcp/utils.d.ts.map +1 -0
  282. package/dist/extensions/mcp/utils.js +336 -0
  283. package/dist/extensions/mcp/utils.js.map +1 -0
  284. package/dist/extensions/subagent/index.d.ts +62 -3
  285. package/dist/extensions/subagent/index.d.ts.map +1 -1
  286. package/dist/extensions/subagent/index.js +193 -58
  287. package/dist/extensions/subagent/index.js.map +1 -1
  288. package/dist/extensions/web-access.d.ts.map +1 -1
  289. package/dist/extensions/web-access.js +1 -1
  290. package/dist/extensions/web-access.js.map +1 -1
  291. package/dist/index.d.ts +2 -0
  292. package/dist/index.d.ts.map +1 -1
  293. package/dist/index.js +20074 -2004
  294. package/dist/index.js.map +1 -1
  295. package/dist/mcp-keyring-helper.cjs +89 -0
  296. package/dist/mcp-script-worker.mjs +130 -0
  297. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  298. package/dist/modes/interactive/components/model-selector.js +1 -1
  299. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  300. package/dist/modes/interactive/components/response-separator.d.ts +1 -1
  301. package/dist/modes/interactive/components/response-separator.d.ts.map +1 -1
  302. package/dist/modes/interactive/components/response-separator.js +1 -1
  303. package/dist/modes/interactive/components/response-separator.js.map +1 -1
  304. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  305. package/dist/modes/interactive/interactive-mode.js +7 -5
  306. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  307. package/dist/rpc-entry.js +19934 -1869
  308. package/dist/skills/mcp-scripting/SKILL.md +38 -0
  309. package/dist/vendor/ai.js +282 -207
  310. package/docs/mcp.md +178 -0
  311. package/docs/pi-subagents.md +75 -0
  312. package/docs/quickstart.md +1 -1
  313. package/docs/usage.md +2 -1
  314. package/examples/extensions/pi-subagents-worker-policy.ts +86 -0
  315. package/npm-shrinkwrap.json +763 -6
  316. package/package.json +15 -4
  317. package/dist/extensions/mcp/client.d.ts +0 -86
  318. package/dist/extensions/mcp/client.d.ts.map +0 -1
  319. package/dist/extensions/mcp/client.js +0 -275
  320. package/dist/extensions/mcp/client.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Internal delegated-worker runtime.\n *\n * This file deliberately does not register a user-facing tool by default. The\n * authenticated Hub policy must advertise delegation before the GitNexus\n * extension calls registerDelegateTaskTool(). The CLI owns process isolation\n * and streaming UX; the Hub owns one-time worker authorization and routing.\n */\n\nimport { execFile, spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { AgentToolResult } from \"@earendil-works/pi-agent-core\";\nimport { Text } from \"@earendil-works/pi-tui\";\nimport { Type } from \"typebox\";\nimport { AUTO_MODEL_ID, AUTO_PROVIDER_ID, isAutoModel } from \"../../core/auto-model.ts\";\nimport type { ExtensionAPI } from \"../../core/extensions/types.ts\";\nimport { withFileMutationQueue } from \"../../core/tools/index.ts\";\nimport type { GitNexusConfig } from \"../gitnexus/config.ts\";\nimport { resolveGitNexusConfig } from \"../gitnexus/config.ts\";\nimport { hubApiBase } from \"../gitnexus/device-auth.ts\";\n\nexport const DELEGATE_TASK_TOOL_NAME = \"delegate_task\";\nexport const INTERNAL_WORKER_MARKER_ENV = \"NEXUS_INTERNAL_DELEGATED_WORKER\";\nexport const INTERNAL_WORKER_SESSION_ENV = \"NEXUS_INTERNAL_WORKER_SESSION_ID\";\nexport const INTERNAL_WORKER_TASK_ENV = \"NEXUS_INTERNAL_WORKER_TASK_ID\";\nexport const INTERNAL_WORKER_RUN_ENV = \"NEXUS_INTERNAL_WORKER_RUN_ID\";\nexport const INTERNAL_WORKER_ROOT_REQUEST_ENV = \"NEXUS_INTERNAL_WORKER_ROOT_REQUEST_ID\";\nexport const INTERNAL_WORKER_PARENT_REQUEST_ENV = \"NEXUS_INTERNAL_WORKER_PARENT_REQUEST_ID\";\nexport const INTERNAL_WORKER_GRANT_HEADER = \"x-nexus-worker-grant\";\nexport const INTERNAL_WORKER_TASK_HEADER = \"x-nexus-worker-task-id\";\nexport const SESSION_AFFINITY_HEADER = \"x-session-affinity\";\nexport const RUN_ID_HEADER = \"x-nexus-run-id\";\nexport const REQUEST_ID_HEADER = \"x-nexus-request-id\";\nexport const ROOT_REQUEST_ID_HEADER = \"x-nexus-root-request-id\";\nexport const PARENT_REQUEST_ID_HEADER = \"x-nexus-parent-request-id\";\nexport const DELEGATION_ID_HEADER = \"x-nexus-delegation-id\";\nexport const REQUEST_SCOPE_HEADER = \"x-nexus-request-scope\";\nexport const MAX_DELEGATED_TURNS = 12;\nexport const MAX_DELEGATIONS_PER_RUN = 3;\nexport const DELEGATED_WORKER_TIMEOUT_MS = 5 * 60 * 1000;\nexport const DELEGATED_OUTPUT_CAP_BYTES = 32 * 1024;\nexport const DELEGATED_EVENT_CAP_BYTES = 1024 * 1024;\nexport const MAX_DELEGATED_TASK_BYTES = 32 * 1024;\n\nconst WORKER_GRANT_TIMEOUT_MS = 10_000;\nconst INVALID_WORKER_GRANT = \"unavailable\";\nconst MAX_ROOT_REVIEW_TOOL_CALLS = 8;\nconst MAX_PARALLEL_MUTATING_WORKERS = 1;\n\nconst ROOT_REVIEW_TOOLS = new Set([\n\t\"read\",\n\t\"grep\",\n\t\"find\",\n\t\"ls\",\n\t\"gitnexus_query\",\n\t\"gitnexus_context\",\n\t\"gitnexus_impact\",\n\t\"gitnexus_detect_changes\",\n\t\"gitnexus_cypher\",\n\t\"gitnexus_list_repos\",\n\t\"web_search\",\n\t\"web_fetch\",\n]);\n\nexport const DELEGATED_WORKER_TOOLS = [\n\t\"read\",\n\t\"grep\",\n\t\"find\",\n\t\"ls\",\n\t\"bash\",\n\t\"write\",\n\t\"edit\",\n\t\"gitnexus_query\",\n\t\"gitnexus_context\",\n\t\"gitnexus_impact\",\n\t\"gitnexus_detect_changes\",\n\t\"gitnexus_cypher\",\n\t\"gitnexus_list_repos\",\n] as const;\n\nconst WORKER_SYSTEM_PROMPT = `You are an internal implementation worker for Nexus Agent.\n\nComplete only the narrowly delegated task. Inspect the repository before editing, follow its local instructions, and verify the changed behavior. Do not delegate work or broaden scope. Do not discuss model names, providers, routing, credits, or internal architecture.\n\nFinish with a concise report containing:\n- changes made\n- verification run and results\n- blockers or remaining risks`;\n\nexport type DelegationStatus =\n\t| \"running\"\n\t| \"completed\"\n\t| \"failed\"\n\t| \"aborted\"\n\t| \"timeout\"\n\t| \"turn_limit\"\n\t| \"event_limit\";\n\nexport interface DelegationDetails {\n\tstatus: DelegationStatus;\n\tturns: number;\n\texitCode?: number;\n\toutputTruncated?: boolean;\n}\n\nexport interface WorkerGrant {\n\t/** Opaque, short-lived grant minted by the Hub for one worker completion. */\n\ttoken: string;\n\texpiresAt: string;\n}\n\nexport interface DelegationCapability {\n\tenabled: true;\n\tversion: 1;\n\tmaxParallel: number;\n\tmaxTaskBytes: number;\n\tworkerTimeoutMs: number;\n}\n\nexport interface WorkerGrantRequest {\n\tsessionId: string;\n\ttaskId: string;\n\trunId: string;\n\trootRequestId: string;\n\tparentRequestId: string;\n\trequestId: string;\n\tsignal: AbortSignal | undefined;\n}\n\nexport interface DelegationController {\n\tcreateWorkerGrant(request: WorkerGrantRequest): Promise<WorkerGrant>;\n}\n\nexport interface DelegationRequest {\n\ttask: string;\n\tacceptanceCriteria: string[];\n\trelevantPaths: string[];\n\tcwd: string;\n\tsignal: AbortSignal | undefined;\n}\n\nexport interface WorkerContext {\n\tsessionId: string;\n\ttaskId: string;\n\trunId: string;\n\trootRequestId: string;\n\tparentRequestId: string;\n}\n\nexport interface DelegatedWorkerOptions extends DelegationRequest {\n\tworkerContext: WorkerContext;\n\ttimeoutMs?: number;\n\tonUpdate?: (result: AgentToolResult<DelegationDetails>) => void;\n}\n\nexport interface DelegatedWorkerResult {\n\tstatus: Exclude<DelegationStatus, \"running\">;\n\tturns: number;\n\texitCode: number;\n\toutput: string;\n\toutputTruncated: boolean;\n}\n\ninterface AssistantSnapshot {\n\ttext: string;\n\tstopReason?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction parseAssistantSnapshot(value: unknown): AssistantSnapshot | undefined {\n\tif (!isRecord(value) || value.role !== \"assistant\" || !Array.isArray(value.content)) return undefined;\n\tconst text = value.content\n\t\t.filter(isRecord)\n\t\t.filter((part) => part.type === \"text\" && typeof part.text === \"string\")\n\t\t.map((part) => part.text as string)\n\t\t.join(\"\\n\");\n\treturn {\n\t\ttext,\n\t\t...(typeof value.stopReason === \"string\" ? { stopReason: value.stopReason } : {}),\n\t};\n}\n\nexport function composeDelegatedTask(\n\ttask: string,\n\tacceptanceCriteria: readonly string[],\n\trelevantPaths: readonly string[],\n): string {\n\tconst sections = [`Task:\\n${task.trim()}`];\n\tif (acceptanceCriteria.length > 0) {\n\t\tsections.push(`Acceptance criteria:\\n${acceptanceCriteria.map((criterion) => `- ${criterion}`).join(\"\\n\")}`);\n\t}\n\tif (relevantPaths.length > 0) {\n\t\tsections.push(`Relevant paths:\\n${relevantPaths.map((filePath) => `- ${filePath}`).join(\"\\n\")}`);\n\t}\n\treturn sections.join(\"\\n\\n\");\n}\n\nexport function buildDelegatedWorkerArgs(task: string, promptPath: string): string[] {\n\treturn [\n\t\t\"--mode\",\n\t\t\"json\",\n\t\t\"-p\",\n\t\t\"--no-session\",\n\t\t\"--model\",\n\t\t`${AUTO_PROVIDER_ID}/${AUTO_MODEL_ID}`,\n\t\t\"--tools\",\n\t\tDELEGATED_WORKER_TOOLS.join(\",\"),\n\t\t\"--exclude-tools\",\n\t\t`${DELEGATE_TASK_TOOL_NAME},subagent`,\n\t\t\"--append-system-prompt\",\n\t\tpromptPath,\n\t\ttask,\n\t];\n}\n\nfunction getNexusInvocation(args: string[]): { command: string; args: string[] } {\n\tconst currentScript = process.argv[1];\n\tconst isBunVirtualScript = currentScript?.startsWith(\"/$bunfs/root/\");\n\tif (currentScript && !isBunVirtualScript && fs.existsSync(currentScript)) {\n\t\treturn { command: process.execPath, args: [currentScript, ...args] };\n\t}\n\n\tconst execName = path.basename(process.execPath).toLowerCase();\n\tconst isGenericRuntime = /^(node|bun)(\\.exe)?$/.test(execName);\n\treturn isGenericRuntime ? { command: \"nexus\", args } : { command: process.execPath, args };\n}\n\nasync function writeWorkerPrompt(): Promise<{ dir: string; filePath: string }> {\n\tconst dir = await fs.promises.mkdtemp(path.join(os.tmpdir(), \"nexus-worker-\"));\n\tconst filePath = path.join(dir, \"system.md\");\n\tawait withFileMutationQueue(filePath, async () => {\n\t\tawait fs.promises.writeFile(filePath, WORKER_SYSTEM_PROMPT, { encoding: \"utf8\", mode: 0o600 });\n\t});\n\treturn { dir, filePath };\n}\n\nfunction truncateUtf8(value: string, cap: number): { value: string; truncated: boolean } {\n\tif (Buffer.byteLength(value, \"utf8\") <= cap) return { value, truncated: false };\n\tconst suffix = \"\\n\\n[Report truncated]\";\n\tlet truncated = value.slice(0, Math.max(0, cap - Buffer.byteLength(suffix, \"utf8\")));\n\twhile (Buffer.byteLength(`${truncated}${suffix}`, \"utf8\") > cap) truncated = truncated.slice(0, -1);\n\treturn { value: `${truncated}${suffix}`, truncated: true };\n}\n\nexport function createHubDelegationController(\n\tconfig: GitNexusConfig,\n\toptions: { fetchImpl?: typeof fetch; timeoutMs?: number } = {},\n): DelegationController {\n\tconst fetchImpl = options.fetchImpl ?? fetch;\n\treturn {\n\t\tasync createWorkerGrant(request) {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst onAbort = (): void => controller.abort();\n\t\t\tif (request.signal?.aborted) controller.abort();\n\t\t\telse request.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\tconst timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? WORKER_GRANT_TIMEOUT_MS);\n\t\t\ttry {\n\t\t\t\tconst response = await fetchImpl(`${hubApiBase(config.hubUrl)}/llm/worker-grants`, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t\t\taccept: \"application/json\",\n\t\t\t\t\t\tauthorization: `Bearer ${config.token}`,\n\t\t\t\t\t\t[RUN_ID_HEADER]: request.runId,\n\t\t\t\t\t\t[REQUEST_ID_HEADER]: request.requestId,\n\t\t\t\t\t\t[ROOT_REQUEST_ID_HEADER]: request.rootRequestId,\n\t\t\t\t\t\t[PARENT_REQUEST_ID_HEADER]: request.parentRequestId,\n\t\t\t\t\t\t[DELEGATION_ID_HEADER]: request.taskId,\n\t\t\t\t\t\t[REQUEST_SCOPE_HEADER]: \"worker\",\n\t\t\t\t\t\t...(config.deviceFingerprint ? { \"x-device-fingerprint\": config.deviceFingerprint } : {}),\n\t\t\t\t\t\t...(config.deviceName ? { \"x-device-name\": config.deviceName } : {}),\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({ sessionId: request.sessionId, taskId: request.taskId }),\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\t\t\tthrow new Error(\"Worker authorization unavailable\");\n\t\t\t\t}\n\t\t\t\tconst body: unknown = await response.json();\n\t\t\t\tif (\n\t\t\t\t\t!isRecord(body) ||\n\t\t\t\t\ttypeof body.grant !== \"string\" ||\n\t\t\t\t\t!body.grant.trim() ||\n\t\t\t\t\ttypeof body.expiresAt !== \"string\" ||\n\t\t\t\t\t!body.expiresAt.trim() ||\n\t\t\t\t\t!Number.isFinite(Date.parse(body.expiresAt))\n\t\t\t\t) {\n\t\t\t\t\tthrow new Error(\"Invalid worker authorization response\");\n\t\t\t\t}\n\t\t\t\treturn { token: body.grant, expiresAt: body.expiresAt };\n\t\t\t} finally {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\trequest.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport function isDelegatedWorkerProcess(env: NodeJS.ProcessEnv = process.env): boolean {\n\treturn env[INTERNAL_WORKER_MARKER_ENV] === \"1\";\n}\n\n/** Capture task routing once, then remove it before worker tools can inherit it. */\nexport function consumeWorkerContext(env: NodeJS.ProcessEnv = process.env): WorkerContext | undefined {\n\tif (!isDelegatedWorkerProcess(env)) return undefined;\n\tconst sessionId = env[INTERNAL_WORKER_SESSION_ENV]?.trim();\n\tconst taskId = env[INTERNAL_WORKER_TASK_ENV]?.trim();\n\tconst runId = env[INTERNAL_WORKER_RUN_ENV]?.trim();\n\tconst rootRequestId = env[INTERNAL_WORKER_ROOT_REQUEST_ENV]?.trim();\n\tconst parentRequestId = env[INTERNAL_WORKER_PARENT_REQUEST_ENV]?.trim();\n\tdelete env[INTERNAL_WORKER_SESSION_ENV];\n\tdelete env[INTERNAL_WORKER_TASK_ENV];\n\tdelete env[INTERNAL_WORKER_RUN_ENV];\n\tdelete env[INTERNAL_WORKER_ROOT_REQUEST_ENV];\n\tdelete env[INTERNAL_WORKER_PARENT_REQUEST_ENV];\n\tif (!sessionId || !taskId || !runId || !rootRequestId || !parentRequestId) return undefined;\n\treturn { sessionId, taskId, runId, rootRequestId, parentRequestId };\n}\n\nfunction killProcessTree(pid: number | undefined, signal: NodeJS.Signals): void {\n\tif (pid === undefined) return;\n\tif (process.platform === \"win32\") {\n\t\texecFile(\"taskkill\", [\"/T\", \"/F\", \"/PID\", String(pid)], () => undefined);\n\t\treturn;\n\t}\n\ttry {\n\t\tprocess.kill(-pid, signal);\n\t} catch {\n\t\t// The detached process group may already have exited.\n\t}\n}\n\nexport async function runDelegatedWorker(options: DelegatedWorkerOptions): Promise<DelegatedWorkerResult> {\n\tconst prompt = await writeWorkerPrompt();\n\tconst task = composeDelegatedTask(options.task, options.acceptanceCriteria, options.relevantPaths);\n\tconst args = buildDelegatedWorkerArgs(task, prompt.filePath);\n\tlet output = \"\";\n\tlet turns = 0;\n\tlet terminalStatus: Exclude<DelegationStatus, \"running\" | \"completed\" | \"failed\"> | undefined;\n\tlet assistantFailed = false;\n\tlet eventBytes = 0;\n\n\ttry {\n\t\tconst exitCode = await new Promise<number>((resolve) => {\n\t\t\tconst invocation = getNexusInvocation(args);\n\t\t\tconst child = spawn(invocation.command, invocation.args, {\n\t\t\t\tcwd: options.cwd,\n\t\t\t\tdetached: true,\n\t\t\t\tenv: {\n\t\t\t\t\t...process.env,\n\t\t\t\t\t[INTERNAL_WORKER_MARKER_ENV]: \"1\",\n\t\t\t\t\t[INTERNAL_WORKER_SESSION_ENV]: options.workerContext.sessionId,\n\t\t\t\t\t[INTERNAL_WORKER_TASK_ENV]: options.workerContext.taskId,\n\t\t\t\t\t[INTERNAL_WORKER_RUN_ENV]: options.workerContext.runId,\n\t\t\t\t\t[INTERNAL_WORKER_ROOT_REQUEST_ENV]: options.workerContext.rootRequestId,\n\t\t\t\t\t[INTERNAL_WORKER_PARENT_REQUEST_ENV]: options.workerContext.parentRequestId,\n\t\t\t\t},\n\t\t\t\tshell: false,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tlet buffer = \"\";\n\t\t\tlet settled = false;\n\t\t\tlet stopped = false;\n\t\t\tlet forceKillTimer: NodeJS.Timeout | undefined;\n\n\t\t\tconst finish = (code: number): void => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tclearTimeout(timeoutTimer);\n\t\t\t\tclearTimeout(forceKillTimer);\n\t\t\t\toptions.signal?.removeEventListener(\"abort\", abortHandler);\n\t\t\t\tresolve(code);\n\t\t\t};\n\n\t\t\tconst stop = (status: \"aborted\" | \"timeout\" | \"turn_limit\" | \"event_limit\"): void => {\n\t\t\t\tif (stopped) return;\n\t\t\t\tstopped = true;\n\t\t\t\tterminalStatus = status;\n\t\t\t\tkillProcessTree(child.pid, \"SIGTERM\");\n\t\t\t\tforceKillTimer = setTimeout(() => {\n\t\t\t\t\tif (process.platform !== \"win32\") killProcessTree(child.pid, \"SIGKILL\");\n\t\t\t\t\tfinish(1);\n\t\t\t\t}, 5000);\n\t\t\t};\n\n\t\t\tconst emitProgress = (): void => {\n\t\t\t\toptions.onUpdate?.({\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Delegated work is in progress.\" }],\n\t\t\t\t\tdetails: { status: \"running\", turns },\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst processLine = (line: string): void => {\n\t\t\t\tif (!line.trim()) return;\n\t\t\t\tlet parsed: unknown;\n\t\t\t\ttry {\n\t\t\t\t\tparsed = JSON.parse(line);\n\t\t\t\t} catch {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!isRecord(parsed) || parsed.type !== \"message_end\") return;\n\t\t\t\tconst snapshot = parseAssistantSnapshot(parsed.message);\n\t\t\t\tif (!snapshot) return;\n\t\t\t\tturns += 1;\n\t\t\t\tif (snapshot.text) output = snapshot.text;\n\t\t\t\tif (snapshot.stopReason === \"error\" || snapshot.stopReason === \"aborted\") assistantFailed = true;\n\t\t\t\temitProgress();\n\t\t\t\tif (turns >= MAX_DELEGATED_TURNS) stop(\"turn_limit\");\n\t\t\t};\n\n\t\t\tconst abortHandler = (): void => stop(\"aborted\");\n\t\t\tconst timeoutTimer = setTimeout(\n\t\t\t\t() => stop(\"timeout\"),\n\t\t\t\tMath.min(Math.max(1_000, options.timeoutMs ?? DELEGATED_WORKER_TIMEOUT_MS), DELEGATED_WORKER_TIMEOUT_MS),\n\t\t\t);\n\t\t\tchild.stdout.on(\"data\", (chunk: Buffer | string) => {\n\t\t\t\teventBytes += Buffer.byteLength(chunk);\n\t\t\t\tif (eventBytes > DELEGATED_EVENT_CAP_BYTES) {\n\t\t\t\t\tassistantFailed = true;\n\t\t\t\t\tstop(\"event_limit\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tbuffer += chunk.toString();\n\t\t\t\tif (Buffer.byteLength(buffer, \"utf8\") > DELEGATED_EVENT_CAP_BYTES) {\n\t\t\t\t\tbuffer = \"\";\n\t\t\t\t\tassistantFailed = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() ?? \"\";\n\t\t\t\tfor (const line of lines) processLine(line);\n\t\t\t});\n\t\t\t// Consume stderr so a noisy child cannot block. Raw provider/process output\n\t\t\t// is intentionally not retained or exposed to the orchestrator or user.\n\t\t\tchild.stderr.on(\"data\", () => undefined);\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tif (buffer.trim()) processLine(buffer);\n\t\t\t\tfinish(code ?? (terminalStatus ? 1 : 0));\n\t\t\t});\n\t\t\tchild.on(\"error\", () => finish(1));\n\t\t\tif (options.signal?.aborted) abortHandler();\n\t\t\telse options.signal?.addEventListener(\"abort\", abortHandler, { once: true });\n\t\t});\n\n\t\tconst limitedOutput = truncateUtf8(output, DELEGATED_OUTPUT_CAP_BYTES);\n\t\tconst status = terminalStatus ?? (exitCode === 0 && !assistantFailed ? \"completed\" : \"failed\");\n\t\treturn {\n\t\t\tstatus,\n\t\t\tturns,\n\t\t\texitCode,\n\t\t\toutput: limitedOutput.value,\n\t\t\toutputTruncated: limitedOutput.truncated,\n\t\t};\n\t} finally {\n\t\tawait fs.promises.rm(prompt.dir, { recursive: true, force: true });\n\t}\n}\n\nconst DelegateTaskParameters = Type.Object({\n\ttask: Type.String({ description: \"A narrow implementation task with explicit boundaries\", maxLength: 32_768 }),\n\tacceptanceCriteria: Type.Optional(Type.Array(Type.String({ maxLength: 2000 }), { maxItems: 10 })),\n\trelevantPaths: Type.Optional(Type.Array(Type.String({ maxLength: 1000 }), { maxItems: 20 })),\n});\n\nfunction resultContent(result: DelegatedWorkerResult): string {\n\tconst report = result.output.trim() || \"No implementation report was returned.\";\n\tif (result.status === \"completed\") return `Delegated implementation completed.\\n\\n${report}`;\n\treturn `Delegated implementation did not complete (${result.status}).\\n\\nPartial report:\\n${report}`;\n}\n\n/** Register the tool only after the Hub explicitly advertises delegation. */\nexport function registerDelegateTaskTool(pi: ExtensionAPI, capability: DelegationCapability): void {\n\tconst maxTaskBytes = Math.min(Math.max(1, Math.floor(capability.maxTaskBytes)), MAX_DELEGATED_TASK_BYTES);\n\tconst workerTimeoutMs = Math.min(\n\t\tMath.max(1_000, Math.floor(capability.workerTimeoutMs)),\n\t\tDELEGATED_WORKER_TIMEOUT_MS,\n\t);\n\tconst runId = randomUUID();\n\tlet rootRequestId: string | undefined;\n\tlet latestRootRequestId: string | undefined;\n\tlet activeWorkers = 0;\n\tlet invocationCount = 0;\n\tlet rootReviewToolCalls = 0;\n\tpi.on(\"before_agent_start\", (event) => {\n\t\tinvocationCount = 0;\n\t\trootReviewToolCalls = 0;\n\t\treturn {\n\t\t\tsystemPrompt: `${event.systemPrompt}\\n\\nImplementation policy: perform implementation, mutation, shell, and repeated tool loops through delegate_task. Keep this parent conversation focused on planning and final review. Use only bounded read-only tools to inspect worker changes, verify graph impact, and reach the final answer.`,\n\t\t};\n\t});\n\tpi.on(\"before_provider_headers\", (event, ctx) => {\n\t\tif (!isAutoModel(ctx.model) || isDelegatedWorkerProcess()) return;\n\t\tconst requestId = randomUUID();\n\t\trootRequestId ??= requestId;\n\t\tlatestRootRequestId = requestId;\n\t\tevent.headers[RUN_ID_HEADER] = runId;\n\t\tevent.headers[REQUEST_ID_HEADER] = requestId;\n\t\tevent.headers[ROOT_REQUEST_ID_HEADER] = rootRequestId;\n\t\tevent.headers[REQUEST_SCOPE_HEADER] = \"root\";\n\t});\n\tpi.on(\"tool_call\", (event, ctx) => {\n\t\tif (!isAutoModel(ctx.model) || isDelegatedWorkerProcess() || event.toolName === DELEGATE_TASK_TOOL_NAME) {\n\t\t\treturn;\n\t\t}\n\t\tif (!ROOT_REVIEW_TOOLS.has(event.toolName)) {\n\t\t\treturn {\n\t\t\t\tblock: true,\n\t\t\t\treason:\n\t\t\t\t\t\"This parent run is review-only. Use delegate_task for implementation, mutation, shell commands, tests, or other execution.\",\n\t\t\t};\n\t\t}\n\t\tif (rootReviewToolCalls >= MAX_ROOT_REVIEW_TOOL_CALLS) {\n\t\t\treturn {\n\t\t\t\tblock: true,\n\t\t\t\treason:\n\t\t\t\t\t\"The bounded parent review budget is exhausted. Delegate further investigation or finalize the review.\",\n\t\t\t};\n\t\t}\n\t\trootReviewToolCalls += 1;\n\t});\n\n\tpi.registerTool({\n\t\tname: DELEGATE_TASK_TOOL_NAME,\n\t\tlabel: \"Work\",\n\t\tdescription:\n\t\t\t\"Delegate one narrow implementation task to an isolated internal worker. Keep planning, user communication, and final verification in this conversation.\",\n\t\tpromptGuidelines: [\n\t\t\t\"Use delegate_task for bounded implementation, mutation, command execution, tests, and repeated tool work after you have formed a plan.\",\n\t\t\t\"You remain responsible for reviewing the returned report, verifying the result, and communicating with the user.\",\n\t\t],\n\t\tparameters: DelegateTaskParameters,\n\t\texecutionMode: \"sequential\",\n\t\tasync execute(_toolCallId, params, signal, onUpdate, ctx) {\n\t\t\tif (!isAutoModel(ctx.model)) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Delegated work is unavailable for the active mode.\" }],\n\t\t\t\t\tdetails: { status: \"failed\", turns: 0 } satisfies DelegationDetails,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (!ctx.isProjectTrusted()) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Delegated work requires a trusted project.\" }],\n\t\t\t\t\tdetails: { status: \"failed\", turns: 0 } satisfies DelegationDetails,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (activeWorkers >= MAX_PARALLEL_MUTATING_WORKERS || invocationCount >= MAX_DELEGATIONS_PER_RUN) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Delegated work is temporarily unavailable for this run.\" }],\n\t\t\t\t\tdetails: { status: \"failed\", turns: 0 } satisfies DelegationDetails,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst request: DelegationRequest = {\n\t\t\t\ttask: params.task,\n\t\t\t\tacceptanceCriteria: params.acceptanceCriteria ?? [],\n\t\t\t\trelevantPaths: params.relevantPaths ?? [],\n\t\t\t\tcwd: ctx.cwd,\n\t\t\t\tsignal,\n\t\t\t};\n\t\t\tif (\n\t\t\t\tBuffer.byteLength(\n\t\t\t\t\tcomposeDelegatedTask(request.task, request.acceptanceCriteria, request.relevantPaths),\n\t\t\t\t\t\"utf8\",\n\t\t\t\t) > maxTaskBytes\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Delegated work is too large for one bounded task.\" }],\n\t\t\t\t\tdetails: { status: \"failed\", turns: 0 } satisfies DelegationDetails,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tactiveWorkers += 1;\n\t\t\tinvocationCount += 1;\n\t\t\ttry {\n\t\t\t\tconst delegationId = randomUUID();\n\t\t\t\trootRequestId ??= randomUUID();\n\t\t\t\tconst result = await runDelegatedWorker({\n\t\t\t\t\t...request,\n\t\t\t\t\tworkerContext: {\n\t\t\t\t\t\tsessionId: ctx.sessionManager.getSessionId(),\n\t\t\t\t\t\ttaskId: delegationId,\n\t\t\t\t\t\trunId,\n\t\t\t\t\t\trootRequestId,\n\t\t\t\t\t\tparentRequestId: latestRootRequestId ?? rootRequestId,\n\t\t\t\t\t},\n\t\t\t\t\ttimeoutMs: workerTimeoutMs,\n\t\t\t\t\tonUpdate,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: resultContent(result) }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tstatus: result.status,\n\t\t\t\t\t\tturns: result.turns,\n\t\t\t\t\t\texitCode: result.exitCode,\n\t\t\t\t\t\toutputTruncated: result.outputTruncated,\n\t\t\t\t\t} satisfies DelegationDetails,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Delegated work could not be started.\" }],\n\t\t\t\t\tdetails: { status: \"failed\", turns: 0 } satisfies DelegationDetails,\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\tactiveWorkers -= 1;\n\t\t\t}\n\t\t},\n\t\trenderCall(_args, theme) {\n\t\t\treturn new Text(theme.fg(\"toolTitle\", theme.bold(\"Working…\")), 0, 0);\n\t\t},\n\t\trenderResult(result, _options, theme) {\n\t\t\tconst details = result.details as DelegationDetails | undefined;\n\t\t\tif (details?.status === \"running\") return new Text(theme.fg(\"muted\", \"Working…\"), 0, 0);\n\t\t\tif (details?.status === \"completed\") return new Text(theme.fg(\"success\", \"Work completed\"), 0, 0);\n\t\t\treturn new Text(theme.fg(\"error\", \"Work needs attention\"), 0, 0);\n\t\t},\n\t});\n}\n\n/**\n * Install a fail-closed header hook for one worker task. A fresh one-time grant\n * is minted for each model turn; no grant is inherited by worker tools.\n */\nexport function registerWorkerGrantHeaders(\n\tpi: ExtensionAPI,\n\tworkerContext: WorkerContext,\n\tcontroller: DelegationController | undefined,\n): void {\n\tpi.on(\"before_provider_headers\", async (event, ctx) => {\n\t\tif (!isAutoModel(ctx.model)) {\n\t\t\tctx.abort();\n\t\t\treturn;\n\t\t}\n\n\t\t// Set an invalid sentinel first. Extension handler errors are reported and\n\t\t// swallowed by the runner, so this prevents a mint failure from silently\n\t\t// falling back to an ungranted root request.\n\t\tconst requestId = randomUUID();\n\t\tevent.headers[INTERNAL_WORKER_GRANT_HEADER] = INVALID_WORKER_GRANT;\n\t\tevent.headers[INTERNAL_WORKER_TASK_HEADER] = workerContext.taskId;\n\t\tevent.headers[SESSION_AFFINITY_HEADER] = workerContext.sessionId;\n\t\tevent.headers[RUN_ID_HEADER] = workerContext.runId;\n\t\tevent.headers[REQUEST_ID_HEADER] = requestId;\n\t\tevent.headers[ROOT_REQUEST_ID_HEADER] = workerContext.rootRequestId;\n\t\tevent.headers[PARENT_REQUEST_ID_HEADER] = workerContext.parentRequestId;\n\t\tevent.headers[DELEGATION_ID_HEADER] = workerContext.taskId;\n\t\tevent.headers[REQUEST_SCOPE_HEADER] = \"worker\";\n\t\tif (!controller) return;\n\n\t\tconst grant = await controller.createWorkerGrant({\n\t\t\t...workerContext,\n\t\t\trequestId,\n\t\t\tsignal: ctx.signal,\n\t\t});\n\t\tevent.headers[INTERNAL_WORKER_GRANT_HEADER] = grant.token;\n\t});\n}\n\nexport default function delegatedWorkerRuntime(pi: ExtensionAPI): void {\n\tconst isWorker = isDelegatedWorkerProcess();\n\tconst workerContext = consumeWorkerContext();\n\tif (!isWorker) return;\n\tconst config = resolveGitNexusConfig();\n\tconst failClosedContext: WorkerContext = {\n\t\tsessionId: \"unavailable\",\n\t\ttaskId: \"unavailable\",\n\t\trunId: \"unavailable\",\n\t\trootRequestId: \"unavailable\",\n\t\tparentRequestId: \"unavailable\",\n\t};\n\tregisterWorkerGrantHeaders(\n\t\tpi,\n\t\tworkerContext ?? failClosedContext,\n\t\tworkerContext && config ? createHubDelegationController(config) : undefined,\n\t);\n}\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Internal delegated-worker runtime.\n *\n * This file deliberately does not register a user-facing tool by default. The\n * authenticated Hub policy must advertise delegation before the GitNexus\n * extension calls registerDelegateTaskTool(). The CLI owns process isolation\n * and streaming UX; the Hub owns one-time worker authorization and routing.\n */\n\nimport { execFile, spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport type { AgentToolResult } from \"@earendil-works/pi-agent-core\";\nimport { Text } from \"@earendil-works/pi-tui\";\nimport { Type } from \"typebox\";\nimport { AUTO_MODEL_ID, AUTO_PROVIDER_ID, isAutoModel } from \"../../core/auto-model.ts\";\nimport type { ExtensionAPI } from \"../../core/extensions/types.ts\";\nimport { withFileMutationQueue } from \"../../core/tools/index.ts\";\nimport type { GitNexusConfig } from \"../gitnexus/config.ts\";\nimport { resolveGitNexusConfig } from \"../gitnexus/config.ts\";\nimport { hubApiBase } from \"../gitnexus/device-auth.ts\";\n\nexport const DELEGATE_TASK_TOOL_NAME = \"delegate_task\";\nexport const INTERNAL_WORKER_MARKER_ENV = \"NEXUS_INTERNAL_DELEGATED_WORKER\";\nexport const INTERNAL_WORKER_SESSION_ENV = \"NEXUS_INTERNAL_WORKER_SESSION_ID\";\nexport const INTERNAL_WORKER_TASK_ENV = \"NEXUS_INTERNAL_WORKER_TASK_ID\";\nexport const INTERNAL_WORKER_RUN_ENV = \"NEXUS_INTERNAL_WORKER_RUN_ID\";\nexport const INTERNAL_WORKER_ROOT_REQUEST_ENV = \"NEXUS_INTERNAL_WORKER_ROOT_REQUEST_ID\";\nexport const INTERNAL_WORKER_PARENT_REQUEST_ENV = \"NEXUS_INTERNAL_WORKER_PARENT_REQUEST_ID\";\nexport const INTERNAL_WORKER_GRANT_HEADER = \"x-nexus-worker-grant\";\nexport const INTERNAL_WORKER_TASK_HEADER = \"x-nexus-worker-task-id\";\nexport const SESSION_AFFINITY_HEADER = \"x-session-affinity\";\nexport const RUN_ID_HEADER = \"x-nexus-run-id\";\nexport const REQUEST_ID_HEADER = \"x-nexus-request-id\";\nexport const ROOT_REQUEST_ID_HEADER = \"x-nexus-root-request-id\";\nexport const PARENT_REQUEST_ID_HEADER = \"x-nexus-parent-request-id\";\nexport const DELEGATION_ID_HEADER = \"x-nexus-delegation-id\";\nexport const REQUEST_SCOPE_HEADER = \"x-nexus-request-scope\";\nexport const MAX_DELEGATED_TURNS = 50;\nexport const DELEGATED_WORKER_TIMEOUT_MS = 5 * 60 * 1000;\nexport const DELEGATED_OUTPUT_CAP_BYTES = 32 * 1024;\nexport const DELEGATED_EVENT_CAP_BYTES = 1024 * 1024;\nexport const MAX_DELEGATED_TASK_BYTES = 32 * 1024;\n\nconst WORKER_GRANT_TIMEOUT_MS = 10_000;\nconst INVALID_WORKER_GRANT = \"unavailable\";\nconst MAX_PARALLEL_MUTATING_WORKERS = 1;\n\nexport const DELEGATED_WORKER_TOOLS = [\n\t\"read\",\n\t\"grep\",\n\t\"find\",\n\t\"ls\",\n\t\"bash\",\n\t\"write\",\n\t\"edit\",\n\t\"web_search\",\n\t\"gitnexus_query\",\n\t\"gitnexus_context\",\n\t\"gitnexus_impact\",\n\t\"gitnexus_detect_changes\",\n\t\"gitnexus_cypher\",\n\t\"gitnexus_list_repos\",\n] as const;\n\nconst WORKER_SYSTEM_PROMPT = `You are an internal delegated worker for Nexus Agent.\n\nComplete only the self-contained task you were delegated. Investigate before editing, follow local instructions, and verify any changed behavior. Do not delegate work or broaden scope. Do not discuss model names, providers, routing, credits, or internal architecture.\n\nFinish with a concise report containing:\n- work completed or findings\n- verification performed and results\n- blockers or remaining risks`;\n\nexport type DelegationStatus =\n\t| \"running\"\n\t| \"completed\"\n\t| \"failed\"\n\t| \"aborted\"\n\t| \"timeout\"\n\t| \"turn_limit\"\n\t| \"event_limit\";\n\nexport interface DelegationDetails {\n\tstatus: DelegationStatus;\n\tturns: number;\n\texitCode?: number;\n\toutputTruncated?: boolean;\n}\n\nexport interface WorkerGrant {\n\t/** Opaque, short-lived grant minted by the Hub for one worker completion. */\n\ttoken: string;\n\texpiresAt: string;\n}\n\nexport interface DelegationCapability {\n\tenabled: true;\n\tversion: 1;\n\tmaxParallel: number;\n\tmaxTaskBytes: number;\n\tworkerTimeoutMs: number;\n}\n\nexport interface WorkerGrantRequest {\n\tsessionId: string;\n\ttaskId: string;\n\trunId: string;\n\trootRequestId: string;\n\tparentRequestId: string;\n\trequestId: string;\n\tsignal: AbortSignal | undefined;\n}\n\nexport interface DelegationController {\n\tcreateWorkerGrant(request: WorkerGrantRequest): Promise<WorkerGrant>;\n}\n\nexport interface DelegationRequest {\n\ttask: string;\n\tacceptanceCriteria: string[];\n\trelevantPaths: string[];\n\tcwd: string;\n\tsignal: AbortSignal | undefined;\n}\n\nexport interface WorkerContext {\n\tsessionId: string;\n\ttaskId: string;\n\trunId: string;\n\trootRequestId: string;\n\tparentRequestId: string;\n}\n\nexport interface DelegatedWorkerOptions extends DelegationRequest {\n\tworkerContext: WorkerContext;\n\ttimeoutMs?: number;\n\tonUpdate?: (result: AgentToolResult<DelegationDetails>) => void;\n}\n\nexport interface DelegatedWorkerResult {\n\tstatus: Exclude<DelegationStatus, \"running\">;\n\tturns: number;\n\texitCode: number;\n\toutput: string;\n\toutputTruncated: boolean;\n}\n\ninterface AssistantSnapshot {\n\ttext: string;\n\tstopReason?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction parseAssistantSnapshot(value: unknown): AssistantSnapshot | undefined {\n\tif (!isRecord(value) || value.role !== \"assistant\" || !Array.isArray(value.content)) return undefined;\n\tconst text = value.content\n\t\t.filter(isRecord)\n\t\t.filter((part) => part.type === \"text\" && typeof part.text === \"string\")\n\t\t.map((part) => part.text as string)\n\t\t.join(\"\\n\");\n\treturn {\n\t\ttext,\n\t\t...(typeof value.stopReason === \"string\" ? { stopReason: value.stopReason } : {}),\n\t};\n}\n\nexport function composeDelegatedTask(\n\ttask: string,\n\tacceptanceCriteria: readonly string[],\n\trelevantPaths: readonly string[],\n): string {\n\tconst sections = [`Task:\\n${task.trim()}`];\n\tif (acceptanceCriteria.length > 0) {\n\t\tsections.push(`Acceptance criteria:\\n${acceptanceCriteria.map((criterion) => `- ${criterion}`).join(\"\\n\")}`);\n\t}\n\tif (relevantPaths.length > 0) {\n\t\tsections.push(`Relevant paths:\\n${relevantPaths.map((filePath) => `- ${filePath}`).join(\"\\n\")}`);\n\t}\n\treturn sections.join(\"\\n\\n\");\n}\n\nexport function buildDelegatedWorkerArgs(task: string, promptPath: string): string[] {\n\treturn [\n\t\t\"--mode\",\n\t\t\"json\",\n\t\t\"-p\",\n\t\t\"--no-session\",\n\t\t\"--model\",\n\t\t`${AUTO_PROVIDER_ID}/${AUTO_MODEL_ID}`,\n\t\t\"--tools\",\n\t\tDELEGATED_WORKER_TOOLS.join(\",\"),\n\t\t\"--exclude-tools\",\n\t\t`${DELEGATE_TASK_TOOL_NAME},subagent`,\n\t\t\"--append-system-prompt\",\n\t\tpromptPath,\n\t\ttask,\n\t];\n}\n\nfunction getNexusInvocation(args: string[]): { command: string; args: string[] } {\n\tconst currentScript = process.argv[1];\n\tconst isBunVirtualScript = currentScript?.startsWith(\"/$bunfs/root/\");\n\tif (currentScript && !isBunVirtualScript && fs.existsSync(currentScript)) {\n\t\treturn { command: process.execPath, args: [currentScript, ...args] };\n\t}\n\n\tconst execName = path.basename(process.execPath).toLowerCase();\n\tconst isGenericRuntime = /^(node|bun)(\\.exe)?$/.test(execName);\n\treturn isGenericRuntime ? { command: \"nexus\", args } : { command: process.execPath, args };\n}\n\nasync function writeWorkerPrompt(): Promise<{ dir: string; filePath: string }> {\n\tconst dir = await fs.promises.mkdtemp(path.join(os.tmpdir(), \"nexus-worker-\"));\n\tconst filePath = path.join(dir, \"system.md\");\n\tawait withFileMutationQueue(filePath, async () => {\n\t\tawait fs.promises.writeFile(filePath, WORKER_SYSTEM_PROMPT, { encoding: \"utf8\", mode: 0o600 });\n\t});\n\treturn { dir, filePath };\n}\n\nfunction truncateUtf8(value: string, cap: number): { value: string; truncated: boolean } {\n\tif (Buffer.byteLength(value, \"utf8\") <= cap) return { value, truncated: false };\n\tconst suffix = \"\\n\\n[Report truncated]\";\n\tlet truncated = value.slice(0, Math.max(0, cap - Buffer.byteLength(suffix, \"utf8\")));\n\twhile (Buffer.byteLength(`${truncated}${suffix}`, \"utf8\") > cap) truncated = truncated.slice(0, -1);\n\treturn { value: `${truncated}${suffix}`, truncated: true };\n}\n\nexport function createHubDelegationController(\n\tconfig: GitNexusConfig,\n\toptions: { fetchImpl?: typeof fetch; timeoutMs?: number } = {},\n): DelegationController {\n\tconst fetchImpl = options.fetchImpl ?? fetch;\n\treturn {\n\t\tasync createWorkerGrant(request) {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst onAbort = (): void => controller.abort();\n\t\t\tif (request.signal?.aborted) controller.abort();\n\t\t\telse request.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\tconst timeout = setTimeout(() => controller.abort(), options.timeoutMs ?? WORKER_GRANT_TIMEOUT_MS);\n\t\t\ttry {\n\t\t\t\tconst response = await fetchImpl(`${hubApiBase(config.hubUrl)}/llm/worker-grants`, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"content-type\": \"application/json\",\n\t\t\t\t\t\taccept: \"application/json\",\n\t\t\t\t\t\tauthorization: `Bearer ${config.token}`,\n\t\t\t\t\t\t[RUN_ID_HEADER]: request.runId,\n\t\t\t\t\t\t[REQUEST_ID_HEADER]: request.requestId,\n\t\t\t\t\t\t[ROOT_REQUEST_ID_HEADER]: request.rootRequestId,\n\t\t\t\t\t\t[PARENT_REQUEST_ID_HEADER]: request.parentRequestId,\n\t\t\t\t\t\t[DELEGATION_ID_HEADER]: request.taskId,\n\t\t\t\t\t\t[REQUEST_SCOPE_HEADER]: \"worker\",\n\t\t\t\t\t\t...(config.deviceFingerprint ? { \"x-device-fingerprint\": config.deviceFingerprint } : {}),\n\t\t\t\t\t\t...(config.deviceName ? { \"x-device-name\": config.deviceName } : {}),\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({ sessionId: request.sessionId, taskId: request.taskId }),\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\t\t\tthrow new Error(\"Worker authorization unavailable\");\n\t\t\t\t}\n\t\t\t\tconst body: unknown = await response.json();\n\t\t\t\tif (\n\t\t\t\t\t!isRecord(body) ||\n\t\t\t\t\ttypeof body.grant !== \"string\" ||\n\t\t\t\t\t!body.grant.trim() ||\n\t\t\t\t\ttypeof body.expiresAt !== \"string\" ||\n\t\t\t\t\t!body.expiresAt.trim() ||\n\t\t\t\t\t!Number.isFinite(Date.parse(body.expiresAt))\n\t\t\t\t) {\n\t\t\t\t\tthrow new Error(\"Invalid worker authorization response\");\n\t\t\t\t}\n\t\t\t\treturn { token: body.grant, expiresAt: body.expiresAt };\n\t\t\t} finally {\n\t\t\t\tclearTimeout(timeout);\n\t\t\t\trequest.signal?.removeEventListener(\"abort\", onAbort);\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport function isDelegatedWorkerProcess(env: NodeJS.ProcessEnv = process.env): boolean {\n\treturn env[INTERNAL_WORKER_MARKER_ENV] === \"1\";\n}\n\nexport const DELEGATION_MODE_ENV = \"NEXUS_DELEGATION_MODE\";\n\n/**\n * `NEXUS_DELEGATION_MODE=external` hands delegation to an external\n * orchestrator (e.g. the pi-subagents package): delegate_task is not\n * registered, so the model sees exactly one delegation surface. Worker-scope\n * billing still works — children marked via the nexus-subagent-worker wrapper\n * self-mint grants exactly like delegate_task workers do.\n */\nexport const BIG_MODEL_AGENTS_ENV = \"NEXUS_BIG_MODEL_AGENTS\";\nexport const WORKER_SCOPE_ENV = \"NEXUS_WORKER_SCOPE\";\nexport const PARENT_POLICY_ENV = \"NEXUS_PARENT_POLICY\";\nexport const BUNDLED_SUBAGENTS_ENV = \"NEXUS_BUNDLED_SUBAGENTS\";\nexport const BUNDLED_PACKAGES_ENV = \"NEXUS_BUNDLED_PACKAGES\";\nexport const PI_SUBAGENT_CHILD_AGENT_ENV = \"PI_SUBAGENT_CHILD_AGENT\";\nexport const PI_SUBAGENT_BINARY_ENV = \"PI_SUBAGENT_PI_BINARY\";\nexport const DEFAULT_BIG_MODEL_AGENTS = [\"oracle\"] as const;\n\n/** pi-subagents ships inside the CLI unless bundled packages are switched off. */\nexport function bundledSubagentsAvailable(env: NodeJS.ProcessEnv = process.env): boolean {\n\tconst value = env[BUNDLED_PACKAGES_ENV]?.trim().toLowerCase();\n\treturn !(value === \"off\" || value === \"0\" || value === \"false\");\n}\n\n/**\n * Delegation runs through the bundled pi-subagents tool by default; the\n * legacy delegate_task worker is opt-in via NEXUS_DELEGATION_MODE=internal.\n */\nexport function isExternalDelegationMode(env: NodeJS.ProcessEnv = process.env): boolean {\n\tconst mode = env[DELEGATION_MODE_ENV]?.trim().toLowerCase();\n\tif (mode === \"internal\") return false;\n\tif (mode === \"external\") return true;\n\treturn bundledSubagentsAvailable(env);\n}\n\n/** Agents that keep billing the orchestrator deployment (big model). */\nexport function bigModelAgents(env: NodeJS.ProcessEnv = process.env): Set<string> {\n\tconst raw = env[BIG_MODEL_AGENTS_ENV];\n\tif (raw === undefined) return new Set(DEFAULT_BIG_MODEL_AGENTS);\n\treturn new Set(\n\t\traw\n\t\t\t.split(\",\")\n\t\t\t.map((name) => name.trim())\n\t\t\t.filter(Boolean),\n\t);\n}\n\n/**\n * A process spawned by pi-subagents announces its agent in PI_SUBAGENT_CHILD_AGENT.\n * Mark it as a delegated worker so every model turn self-mints a worker grant\n * and bills the worker deployment. Big-model agents are explicitly unmarked,\n * even when a marker was inherited from a worker parent (nested fan-out).\n * Context is always regenerated: a worker deletes its own context before\n * spawning tools, so nothing inherited is trustworthy.\n */\nexport function applyPiSubagentChildWorkerScope(\n\tenv: NodeJS.ProcessEnv = process.env,\n\tuuid: () => string = randomUUID,\n): boolean {\n\tconst agent = env[PI_SUBAGENT_CHILD_AGENT_ENV]?.trim();\n\tif (!agent) return false;\n\tconst contextKeys = [\n\t\tINTERNAL_WORKER_SESSION_ENV,\n\t\tINTERNAL_WORKER_TASK_ENV,\n\t\tINTERNAL_WORKER_RUN_ENV,\n\t\tINTERNAL_WORKER_ROOT_REQUEST_ENV,\n\t\tINTERNAL_WORKER_PARENT_REQUEST_ENV,\n\t];\n\tconst scopeOff = env[WORKER_SCOPE_ENV]?.trim().toLowerCase() === \"off\";\n\tif (scopeOff || bigModelAgents(env).has(agent)) {\n\t\tdelete env[INTERNAL_WORKER_MARKER_ENV];\n\t\tfor (const key of contextKeys) delete env[key];\n\t\treturn false;\n\t}\n\tenv[INTERNAL_WORKER_MARKER_ENV] = \"1\";\n\tfor (const key of contextKeys) env[key] = uuid();\n\treturn true;\n}\n\n/**\n * The executable pi-subagents should spawn for children: this CLI itself.\n * A compiled standalone binary is its own executable; under node/bun the\n * built cli.js carries a shebang and the executable bit from the build.\n */\nexport function resolveBundledChildBinary(\n\targv1: string | undefined = process.argv[1],\n\texecPath: string = process.execPath,\n\tfsImpl: Pick<typeof fs, \"realpathSync\" | \"accessSync\" | \"constants\"> = fs,\n): string | undefined {\n\tconst execName = path.basename(execPath).toLowerCase();\n\tif (!/^(node|bun)(\\.exe)?$/.test(execName)) return execPath;\n\tif (!argv1) return undefined;\n\ttry {\n\t\tconst real = fsImpl.realpathSync(argv1);\n\t\tif (!/cli\\.(js|mjs|cjs)$/.test(real)) return undefined;\n\t\tfsImpl.accessSync(real, fsImpl.constants.X_OK);\n\t\treturn real;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function applyBundledChildBinaryDefault(\n\tenv: NodeJS.ProcessEnv = process.env,\n\tresolved: string | undefined = resolveBundledChildBinary(),\n): void {\n\tif (env[PI_SUBAGENT_BINARY_ENV]?.trim() || !resolved) return;\n\tenv[PI_SUBAGENT_BINARY_ENV] = resolved;\n}\n\nexport const BLOCKED_ORCHESTRATOR_TOOLS = new Set([\"bash\", \"write\", \"edit\", DELEGATE_TASK_TOOL_NAME]);\n\nexport const ORCHESTRATOR_GUIDANCE = [\n\t\"Delegation policy: you are the orchestrator. Perform implementation, mutation,\",\n\t\"shell commands, tests, and long repetitive tool loops through the `subagent`\",\n\t\"tool (worker for implementation, reviewer for checks, scout for codebase\",\n\t\"exploration, researcher for web research). Direct bash/write/edit are disabled\",\n\t\"in this session. Reading files and querying the GitNexus graph directly is fine\",\n\t\"and unlimited — prefer gitnexus_query/context/impact to locate code before\",\n\t\"delegating. For broad or multi-source research, fan out scout or researcher\",\n\t\"subagents (or use /parallel-research) and run long investigations in the\",\n\t\"background (async) instead of one long foreground run. Give each subagent a\",\n\t\"narrow, self-contained task rather than one sprawling one. Keep planning,\",\n\t\"user communication, and final review here.\",\n].join(\" \");\n\nexport function isOrchestratorPolicyDisabled(env: NodeJS.ProcessEnv = process.env): boolean {\n\treturn (\n\t\tenv[PARENT_POLICY_ENV]?.trim().toLowerCase() === \"off\" ||\n\t\tenv[INTERNAL_WORKER_MARKER_ENV] === \"1\" ||\n\t\tBoolean(env[PI_SUBAGENT_CHILD_AGENT_ENV]?.trim())\n\t);\n}\n\n/**\n * Orchestrator-side policy for bundled subagents: hard-block mutating tools\n * (the cost guarantee — implementation always bills the worker deployment)\n * and steer everything else by prompt. No read budget: reads and graph calls\n * are cheap on cached input and capping them measurably hurt output quality.\n */\nexport function registerOrchestratorPolicy(pi: ExtensionAPI, env: NodeJS.ProcessEnv = process.env): void {\n\tpi.on(\"before_agent_start\", (event, ctx) => {\n\t\tif (isOrchestratorPolicyDisabled(env) || !isAutoModel(ctx.model)) return;\n\t\treturn { systemPrompt: `${event.systemPrompt}\\n\\n${ORCHESTRATOR_GUIDANCE}` };\n\t});\n\tpi.on(\"tool_call\", (event, ctx) => {\n\t\tif (isOrchestratorPolicyDisabled(env) || !isAutoModel(ctx.model)) return;\n\t\tif (!BLOCKED_ORCHESTRATOR_TOOLS.has(event.toolName)) return;\n\t\treturn {\n\t\t\tblock: true,\n\t\t\treason:\n\t\t\t\t\"This orchestrator session does not run mutating tools directly. Use the `subagent` tool \" +\n\t\t\t\t\"(worker for implementation, reviewer for checks, scout for exploration), then review its report.\",\n\t\t};\n\t});\n}\n\n/**\n * Nexus-branded aliases for the upstream pi-subagents env knobs: users set\n * NEXUS_SUBAGENT_*, we copy onto the PI_SUBAGENT_* names the package reads.\n * NEXUS_SUBAGENT_BINARY maps to PI_SUBAGENT_PI_BINARY (the child-spawn\n * wrapper hook); every other NEXUS_SUBAGENT_X maps to PI_SUBAGENT_X.\n * Explicit PI_* values always win — aliases never overwrite them.\n */\nexport function applyNexusSubagentEnvAliases(env: NodeJS.ProcessEnv = process.env): void {\n\tconst binary = env.NEXUS_SUBAGENT_BINARY?.trim();\n\tif (binary && !env.PI_SUBAGENT_PI_BINARY?.trim()) {\n\t\tenv.PI_SUBAGENT_PI_BINARY = binary;\n\t}\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tif (!key.startsWith(\"NEXUS_SUBAGENT_\") || key === \"NEXUS_SUBAGENT_BINARY\") continue;\n\t\tif (!value?.trim()) continue;\n\t\tconst target = `PI_SUBAGENT_${key.slice(\"NEXUS_SUBAGENT_\".length)}`;\n\t\tif (!env[target]?.trim()) env[target] = value;\n\t}\n}\n\n/** Capture task routing once, then remove it before worker tools can inherit it. */\nexport function consumeWorkerContext(env: NodeJS.ProcessEnv = process.env): WorkerContext | undefined {\n\tif (!isDelegatedWorkerProcess(env)) return undefined;\n\tconst sessionId = env[INTERNAL_WORKER_SESSION_ENV]?.trim();\n\tconst taskId = env[INTERNAL_WORKER_TASK_ENV]?.trim();\n\tconst runId = env[INTERNAL_WORKER_RUN_ENV]?.trim();\n\tconst rootRequestId = env[INTERNAL_WORKER_ROOT_REQUEST_ENV]?.trim();\n\tconst parentRequestId = env[INTERNAL_WORKER_PARENT_REQUEST_ENV]?.trim();\n\tdelete env[INTERNAL_WORKER_SESSION_ENV];\n\tdelete env[INTERNAL_WORKER_TASK_ENV];\n\tdelete env[INTERNAL_WORKER_RUN_ENV];\n\tdelete env[INTERNAL_WORKER_ROOT_REQUEST_ENV];\n\tdelete env[INTERNAL_WORKER_PARENT_REQUEST_ENV];\n\tif (!sessionId || !taskId || !runId || !rootRequestId || !parentRequestId) return undefined;\n\treturn { sessionId, taskId, runId, rootRequestId, parentRequestId };\n}\n\nfunction killProcessTree(pid: number | undefined, signal: NodeJS.Signals): void {\n\tif (pid === undefined) return;\n\tif (process.platform === \"win32\") {\n\t\texecFile(\"taskkill\", [\"/T\", \"/F\", \"/PID\", String(pid)], () => undefined);\n\t\treturn;\n\t}\n\ttry {\n\t\tprocess.kill(-pid, signal);\n\t} catch {\n\t\t// The detached process group may already have exited.\n\t}\n}\n\nexport async function runDelegatedWorker(options: DelegatedWorkerOptions): Promise<DelegatedWorkerResult> {\n\tconst prompt = await writeWorkerPrompt();\n\tconst task = composeDelegatedTask(options.task, options.acceptanceCriteria, options.relevantPaths);\n\tconst args = buildDelegatedWorkerArgs(task, prompt.filePath);\n\tlet output = \"\";\n\tlet turns = 0;\n\tlet terminalStatus: Exclude<DelegationStatus, \"running\" | \"completed\" | \"failed\"> | undefined;\n\tlet assistantFailed = false;\n\tlet eventBytes = 0;\n\n\ttry {\n\t\tconst exitCode = await new Promise<number>((resolve) => {\n\t\t\tconst invocation = getNexusInvocation(args);\n\t\t\tconst child = spawn(invocation.command, invocation.args, {\n\t\t\t\tcwd: options.cwd,\n\t\t\t\tdetached: true,\n\t\t\t\tenv: {\n\t\t\t\t\t...process.env,\n\t\t\t\t\t[INTERNAL_WORKER_MARKER_ENV]: \"1\",\n\t\t\t\t\t[INTERNAL_WORKER_SESSION_ENV]: options.workerContext.sessionId,\n\t\t\t\t\t[INTERNAL_WORKER_TASK_ENV]: options.workerContext.taskId,\n\t\t\t\t\t[INTERNAL_WORKER_RUN_ENV]: options.workerContext.runId,\n\t\t\t\t\t[INTERNAL_WORKER_ROOT_REQUEST_ENV]: options.workerContext.rootRequestId,\n\t\t\t\t\t[INTERNAL_WORKER_PARENT_REQUEST_ENV]: options.workerContext.parentRequestId,\n\t\t\t\t},\n\t\t\t\tshell: false,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tlet buffer = \"\";\n\t\t\tlet settled = false;\n\t\t\tlet stopped = false;\n\t\t\tlet forceKillTimer: NodeJS.Timeout | undefined;\n\n\t\t\tconst finish = (code: number): void => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tclearTimeout(timeoutTimer);\n\t\t\t\tclearTimeout(forceKillTimer);\n\t\t\t\toptions.signal?.removeEventListener(\"abort\", abortHandler);\n\t\t\t\tresolve(code);\n\t\t\t};\n\n\t\t\tconst stop = (status: \"aborted\" | \"timeout\" | \"turn_limit\" | \"event_limit\"): void => {\n\t\t\t\tif (stopped) return;\n\t\t\t\tstopped = true;\n\t\t\t\tterminalStatus = status;\n\t\t\t\tkillProcessTree(child.pid, \"SIGTERM\");\n\t\t\t\tforceKillTimer = setTimeout(() => {\n\t\t\t\t\tif (process.platform !== \"win32\") killProcessTree(child.pid, \"SIGKILL\");\n\t\t\t\t\tfinish(1);\n\t\t\t\t}, 5000);\n\t\t\t};\n\n\t\t\tconst emitProgress = (): void => {\n\t\t\t\toptions.onUpdate?.({\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Delegated work is in progress.\" }],\n\t\t\t\t\tdetails: { status: \"running\", turns },\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst processLine = (line: string): void => {\n\t\t\t\tif (!line.trim()) return;\n\t\t\t\tlet parsed: unknown;\n\t\t\t\ttry {\n\t\t\t\t\tparsed = JSON.parse(line);\n\t\t\t\t} catch {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (!isRecord(parsed) || parsed.type !== \"message_end\") return;\n\t\t\t\tconst snapshot = parseAssistantSnapshot(parsed.message);\n\t\t\t\tif (!snapshot) return;\n\t\t\t\tturns += 1;\n\t\t\t\tif (snapshot.text) output = snapshot.text;\n\t\t\t\tif (snapshot.stopReason === \"error\" || snapshot.stopReason === \"aborted\") assistantFailed = true;\n\t\t\t\temitProgress();\n\t\t\t\tif (turns >= MAX_DELEGATED_TURNS && snapshot.stopReason === \"toolUse\") stop(\"turn_limit\");\n\t\t\t};\n\n\t\t\tconst abortHandler = (): void => stop(\"aborted\");\n\t\t\tconst timeoutTimer = setTimeout(\n\t\t\t\t() => stop(\"timeout\"),\n\t\t\t\tMath.min(Math.max(1_000, options.timeoutMs ?? DELEGATED_WORKER_TIMEOUT_MS), DELEGATED_WORKER_TIMEOUT_MS),\n\t\t\t);\n\t\t\tchild.stdout.on(\"data\", (chunk: Buffer | string) => {\n\t\t\t\teventBytes += Buffer.byteLength(chunk);\n\t\t\t\tif (eventBytes > DELEGATED_EVENT_CAP_BYTES) {\n\t\t\t\t\tassistantFailed = true;\n\t\t\t\t\tstop(\"event_limit\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tbuffer += chunk.toString();\n\t\t\t\tif (Buffer.byteLength(buffer, \"utf8\") > DELEGATED_EVENT_CAP_BYTES) {\n\t\t\t\t\tbuffer = \"\";\n\t\t\t\t\tassistantFailed = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() ?? \"\";\n\t\t\t\tfor (const line of lines) processLine(line);\n\t\t\t});\n\t\t\t// Consume stderr so a noisy child cannot block. Raw provider/process output\n\t\t\t// is intentionally not retained or exposed to the orchestrator or user.\n\t\t\tchild.stderr.on(\"data\", () => undefined);\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tif (buffer.trim()) processLine(buffer);\n\t\t\t\tfinish(code ?? (terminalStatus ? 1 : 0));\n\t\t\t});\n\t\t\tchild.on(\"error\", () => finish(1));\n\t\t\tif (options.signal?.aborted) abortHandler();\n\t\t\telse options.signal?.addEventListener(\"abort\", abortHandler, { once: true });\n\t\t});\n\n\t\tconst limitedOutput = truncateUtf8(output, DELEGATED_OUTPUT_CAP_BYTES);\n\t\tconst status = terminalStatus ?? (exitCode === 0 && !assistantFailed ? \"completed\" : \"failed\");\n\t\treturn {\n\t\t\tstatus,\n\t\t\tturns,\n\t\t\texitCode,\n\t\t\toutput: limitedOutput.value,\n\t\t\toutputTruncated: limitedOutput.truncated,\n\t\t};\n\t} finally {\n\t\tawait fs.promises.rm(prompt.dir, { recursive: true, force: true });\n\t}\n}\n\nconst DelegateTaskParameters = Type.Object({\n\ttask: Type.String({ description: \"A self-contained task with explicit boundaries\", maxLength: 32_768 }),\n\tacceptanceCriteria: Type.Optional(Type.Array(Type.String({ maxLength: 2000 }), { maxItems: 10 })),\n\trelevantPaths: Type.Optional(Type.Array(Type.String({ maxLength: 1000 }), { maxItems: 20 })),\n});\n\nfunction resultContent(result: DelegatedWorkerResult): string {\n\tconst report = result.output.trim() || \"No worker report was returned.\";\n\tif (result.status === \"completed\") return `Delegated work completed.\\n\\n${report}`;\n\treturn `Delegated work did not complete (${result.status}).\\n\\nPartial report:\\n${report}`;\n}\n\n/** Register the tool only after the Hub explicitly advertises delegation. */\nexport function registerDelegateTaskTool(pi: ExtensionAPI, capability: DelegationCapability): void {\n\tconst maxTaskBytes = Math.min(Math.max(1, Math.floor(capability.maxTaskBytes)), MAX_DELEGATED_TASK_BYTES);\n\tconst workerTimeoutMs = Math.min(\n\t\tMath.max(1_000, Math.floor(capability.workerTimeoutMs)),\n\t\tDELEGATED_WORKER_TIMEOUT_MS,\n\t);\n\tconst runId = randomUUID();\n\tlet rootRequestId: string | undefined;\n\tlet latestRootRequestId: string | undefined;\n\tlet activeWorkers = 0;\n\tpi.on(\"before_agent_start\", (event, ctx) => {\n\t\tif (!isAutoModel(ctx.model)) return;\n\t\treturn {\n\t\t\tsystemPrompt: `${event.systemPrompt}\\n\\nDelegation guidance: prioritize delegate_task for self-contained, context-heavy investigation, broad searches, audits, repeated tool loops, or substantial implementation so the main conversation stays focused and efficient. Use your own tools directly when the work is small, depends on context already established in this conversation, requires direct verification, or delegation fails or reaches a limit. You remain responsible for the final result.`,\n\t\t};\n\t});\n\tpi.on(\"before_provider_headers\", (event, ctx) => {\n\t\tif (!isAutoModel(ctx.model) || isDelegatedWorkerProcess()) return;\n\t\tconst requestId = randomUUID();\n\t\trootRequestId ??= requestId;\n\t\tlatestRootRequestId = requestId;\n\t\tevent.headers[RUN_ID_HEADER] = runId;\n\t\tevent.headers[REQUEST_ID_HEADER] = requestId;\n\t\tevent.headers[ROOT_REQUEST_ID_HEADER] = rootRequestId;\n\t\tevent.headers[REQUEST_SCOPE_HEADER] = \"root\";\n\t});\n\tpi.registerTool({\n\t\tname: DELEGATE_TASK_TOOL_NAME,\n\t\tlabel: \"Work\",\n\t\tdescription:\n\t\t\t\"Delegate one self-contained task to an isolated internal worker. Keep user communication and final responsibility in this conversation.\",\n\t\tparameters: DelegateTaskParameters,\n\t\texecutionMode: \"sequential\",\n\t\tasync execute(_toolCallId, params, signal, onUpdate, ctx) {\n\t\t\tif (!isAutoModel(ctx.model)) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Delegated work is unavailable for the active mode.\" }],\n\t\t\t\t\tdetails: { status: \"failed\", turns: 0 } satisfies DelegationDetails,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (!ctx.isProjectTrusted()) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Delegated work requires a trusted project.\" }],\n\t\t\t\t\tdetails: { status: \"failed\", turns: 0 } satisfies DelegationDetails,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (activeWorkers >= MAX_PARALLEL_MUTATING_WORKERS) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Delegated work is temporarily unavailable for this run.\" }],\n\t\t\t\t\tdetails: { status: \"failed\", turns: 0 } satisfies DelegationDetails,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst request: DelegationRequest = {\n\t\t\t\ttask: params.task,\n\t\t\t\tacceptanceCriteria: params.acceptanceCriteria ?? [],\n\t\t\t\trelevantPaths: params.relevantPaths ?? [],\n\t\t\t\tcwd: ctx.cwd,\n\t\t\t\tsignal,\n\t\t\t};\n\t\t\tif (\n\t\t\t\tBuffer.byteLength(\n\t\t\t\t\tcomposeDelegatedTask(request.task, request.acceptanceCriteria, request.relevantPaths),\n\t\t\t\t\t\"utf8\",\n\t\t\t\t) > maxTaskBytes\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Delegated work is too large for one bounded task.\" }],\n\t\t\t\t\tdetails: { status: \"failed\", turns: 0 } satisfies DelegationDetails,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tactiveWorkers += 1;\n\t\t\ttry {\n\t\t\t\tconst delegationId = randomUUID();\n\t\t\t\trootRequestId ??= randomUUID();\n\t\t\t\tconst result = await runDelegatedWorker({\n\t\t\t\t\t...request,\n\t\t\t\t\tworkerContext: {\n\t\t\t\t\t\tsessionId: ctx.sessionManager.getSessionId(),\n\t\t\t\t\t\ttaskId: delegationId,\n\t\t\t\t\t\trunId,\n\t\t\t\t\t\trootRequestId,\n\t\t\t\t\t\tparentRequestId: latestRootRequestId ?? rootRequestId,\n\t\t\t\t\t},\n\t\t\t\t\ttimeoutMs: workerTimeoutMs,\n\t\t\t\t\tonUpdate,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: resultContent(result) }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tstatus: result.status,\n\t\t\t\t\t\tturns: result.turns,\n\t\t\t\t\t\texitCode: result.exitCode,\n\t\t\t\t\t\toutputTruncated: result.outputTruncated,\n\t\t\t\t\t} satisfies DelegationDetails,\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Delegated work could not be started.\" }],\n\t\t\t\t\tdetails: { status: \"failed\", turns: 0 } satisfies DelegationDetails,\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\tactiveWorkers -= 1;\n\t\t\t}\n\t\t},\n\t\trenderCall(_args, theme) {\n\t\t\treturn new Text(theme.fg(\"toolTitle\", theme.bold(\"Working…\")), 0, 0);\n\t\t},\n\t\trenderResult(result, _options, theme) {\n\t\t\tconst details = result.details as DelegationDetails | undefined;\n\t\t\tif (details?.status === \"running\") return new Text(theme.fg(\"muted\", \"Working…\"), 0, 0);\n\t\t\tif (details?.status === \"completed\") return new Text(theme.fg(\"success\", \"Work completed\"), 0, 0);\n\t\t\treturn new Text(theme.fg(\"error\", \"Work needs attention\"), 0, 0);\n\t\t},\n\t});\n}\n\n/**\n * Install a fail-closed header hook for one worker task. A fresh one-time grant\n * is minted for each model turn; no grant is inherited by worker tools.\n */\nexport function registerWorkerGrantHeaders(\n\tpi: ExtensionAPI,\n\tworkerContext: WorkerContext,\n\tcontroller: DelegationController | undefined,\n): void {\n\tpi.on(\"before_provider_headers\", async (event, ctx) => {\n\t\tif (!isAutoModel(ctx.model)) {\n\t\t\tctx.abort();\n\t\t\treturn;\n\t\t}\n\n\t\t// Set an invalid sentinel first. Extension handler errors are reported and\n\t\t// swallowed by the runner, so this prevents a mint failure from silently\n\t\t// falling back to an ungranted root request.\n\t\tconst requestId = randomUUID();\n\t\tevent.headers[INTERNAL_WORKER_GRANT_HEADER] = INVALID_WORKER_GRANT;\n\t\tevent.headers[INTERNAL_WORKER_TASK_HEADER] = workerContext.taskId;\n\t\tevent.headers[SESSION_AFFINITY_HEADER] = workerContext.sessionId;\n\t\tevent.headers[RUN_ID_HEADER] = workerContext.runId;\n\t\tevent.headers[REQUEST_ID_HEADER] = requestId;\n\t\tevent.headers[ROOT_REQUEST_ID_HEADER] = workerContext.rootRequestId;\n\t\tevent.headers[PARENT_REQUEST_ID_HEADER] = workerContext.parentRequestId;\n\t\tevent.headers[DELEGATION_ID_HEADER] = workerContext.taskId;\n\t\tevent.headers[REQUEST_SCOPE_HEADER] = \"worker\";\n\t\tif (!controller) return;\n\n\t\tconst grant = await controller.createWorkerGrant({\n\t\t\t...workerContext,\n\t\t\trequestId,\n\t\t\tsignal: ctx.signal,\n\t\t});\n\t\tevent.headers[INTERNAL_WORKER_GRANT_HEADER] = grant.token;\n\t});\n}\n\nexport default function delegatedWorkerRuntime(pi: ExtensionAPI): void {\n\tapplyNexusSubagentEnvAliases();\n\tif (bundledSubagentsAvailable()) process.env[BUNDLED_SUBAGENTS_ENV] = \"1\";\n\tapplyPiSubagentChildWorkerScope();\n\tconst isWorker = isDelegatedWorkerProcess();\n\tconst workerContext = consumeWorkerContext();\n\tif (!isWorker) {\n\t\tif (isExternalDelegationMode()) {\n\t\t\tapplyBundledChildBinaryDefault();\n\t\t\tregisterOrchestratorPolicy(pi);\n\t\t}\n\t\treturn;\n\t}\n\tconst config = resolveGitNexusConfig();\n\tconst failClosedContext: WorkerContext = {\n\t\tsessionId: \"unavailable\",\n\t\ttaskId: \"unavailable\",\n\t\trunId: \"unavailable\",\n\t\trootRequestId: \"unavailable\",\n\t\tparentRequestId: \"unavailable\",\n\t};\n\tregisterWorkerGrantHeaders(\n\t\tpi,\n\t\tworkerContext ?? failClosedContext,\n\t\tworkerContext && config ? createHubDelegationController(config) : undefined,\n\t);\n}\n"]}
@@ -33,30 +33,14 @@ export const ROOT_REQUEST_ID_HEADER = "x-nexus-root-request-id";
33
33
  export const PARENT_REQUEST_ID_HEADER = "x-nexus-parent-request-id";
34
34
  export const DELEGATION_ID_HEADER = "x-nexus-delegation-id";
35
35
  export const REQUEST_SCOPE_HEADER = "x-nexus-request-scope";
36
- export const MAX_DELEGATED_TURNS = 12;
37
- export const MAX_DELEGATIONS_PER_RUN = 3;
36
+ export const MAX_DELEGATED_TURNS = 50;
38
37
  export const DELEGATED_WORKER_TIMEOUT_MS = 5 * 60 * 1000;
39
38
  export const DELEGATED_OUTPUT_CAP_BYTES = 32 * 1024;
40
39
  export const DELEGATED_EVENT_CAP_BYTES = 1024 * 1024;
41
40
  export const MAX_DELEGATED_TASK_BYTES = 32 * 1024;
42
41
  const WORKER_GRANT_TIMEOUT_MS = 10_000;
43
42
  const INVALID_WORKER_GRANT = "unavailable";
44
- const MAX_ROOT_REVIEW_TOOL_CALLS = 8;
45
43
  const MAX_PARALLEL_MUTATING_WORKERS = 1;
46
- const ROOT_REVIEW_TOOLS = new Set([
47
- "read",
48
- "grep",
49
- "find",
50
- "ls",
51
- "gitnexus_query",
52
- "gitnexus_context",
53
- "gitnexus_impact",
54
- "gitnexus_detect_changes",
55
- "gitnexus_cypher",
56
- "gitnexus_list_repos",
57
- "web_search",
58
- "web_fetch",
59
- ]);
60
44
  export const DELEGATED_WORKER_TOOLS = [
61
45
  "read",
62
46
  "grep",
@@ -65,6 +49,7 @@ export const DELEGATED_WORKER_TOOLS = [
65
49
  "bash",
66
50
  "write",
67
51
  "edit",
52
+ "web_search",
68
53
  "gitnexus_query",
69
54
  "gitnexus_context",
70
55
  "gitnexus_impact",
@@ -72,13 +57,13 @@ export const DELEGATED_WORKER_TOOLS = [
72
57
  "gitnexus_cypher",
73
58
  "gitnexus_list_repos",
74
59
  ];
75
- const WORKER_SYSTEM_PROMPT = `You are an internal implementation worker for Nexus Agent.
60
+ const WORKER_SYSTEM_PROMPT = `You are an internal delegated worker for Nexus Agent.
76
61
 
77
- Complete only the narrowly delegated task. Inspect the repository before editing, follow its local instructions, and verify the changed behavior. Do not delegate work or broaden scope. Do not discuss model names, providers, routing, credits, or internal architecture.
62
+ Complete only the self-contained task you were delegated. Investigate before editing, follow local instructions, and verify any changed behavior. Do not delegate work or broaden scope. Do not discuss model names, providers, routing, credits, or internal architecture.
78
63
 
79
64
  Finish with a concise report containing:
80
- - changes made
81
- - verification run and results
65
+ - work completed or findings
66
+ - verification performed and results
82
67
  - blockers or remaining risks`;
83
68
  function isRecord(value) {
84
69
  return typeof value === "object" && value !== null;
@@ -205,6 +190,172 @@ export function createHubDelegationController(config, options = {}) {
205
190
  export function isDelegatedWorkerProcess(env = process.env) {
206
191
  return env[INTERNAL_WORKER_MARKER_ENV] === "1";
207
192
  }
193
+ export const DELEGATION_MODE_ENV = "NEXUS_DELEGATION_MODE";
194
+ /**
195
+ * `NEXUS_DELEGATION_MODE=external` hands delegation to an external
196
+ * orchestrator (e.g. the pi-subagents package): delegate_task is not
197
+ * registered, so the model sees exactly one delegation surface. Worker-scope
198
+ * billing still works — children marked via the nexus-subagent-worker wrapper
199
+ * self-mint grants exactly like delegate_task workers do.
200
+ */
201
+ export const BIG_MODEL_AGENTS_ENV = "NEXUS_BIG_MODEL_AGENTS";
202
+ export const WORKER_SCOPE_ENV = "NEXUS_WORKER_SCOPE";
203
+ export const PARENT_POLICY_ENV = "NEXUS_PARENT_POLICY";
204
+ export const BUNDLED_SUBAGENTS_ENV = "NEXUS_BUNDLED_SUBAGENTS";
205
+ export const BUNDLED_PACKAGES_ENV = "NEXUS_BUNDLED_PACKAGES";
206
+ export const PI_SUBAGENT_CHILD_AGENT_ENV = "PI_SUBAGENT_CHILD_AGENT";
207
+ export const PI_SUBAGENT_BINARY_ENV = "PI_SUBAGENT_PI_BINARY";
208
+ export const DEFAULT_BIG_MODEL_AGENTS = ["oracle"];
209
+ /** pi-subagents ships inside the CLI unless bundled packages are switched off. */
210
+ export function bundledSubagentsAvailable(env = process.env) {
211
+ const value = env[BUNDLED_PACKAGES_ENV]?.trim().toLowerCase();
212
+ return !(value === "off" || value === "0" || value === "false");
213
+ }
214
+ /**
215
+ * Delegation runs through the bundled pi-subagents tool by default; the
216
+ * legacy delegate_task worker is opt-in via NEXUS_DELEGATION_MODE=internal.
217
+ */
218
+ export function isExternalDelegationMode(env = process.env) {
219
+ const mode = env[DELEGATION_MODE_ENV]?.trim().toLowerCase();
220
+ if (mode === "internal")
221
+ return false;
222
+ if (mode === "external")
223
+ return true;
224
+ return bundledSubagentsAvailable(env);
225
+ }
226
+ /** Agents that keep billing the orchestrator deployment (big model). */
227
+ export function bigModelAgents(env = process.env) {
228
+ const raw = env[BIG_MODEL_AGENTS_ENV];
229
+ if (raw === undefined)
230
+ return new Set(DEFAULT_BIG_MODEL_AGENTS);
231
+ return new Set(raw
232
+ .split(",")
233
+ .map((name) => name.trim())
234
+ .filter(Boolean));
235
+ }
236
+ /**
237
+ * A process spawned by pi-subagents announces its agent in PI_SUBAGENT_CHILD_AGENT.
238
+ * Mark it as a delegated worker so every model turn self-mints a worker grant
239
+ * and bills the worker deployment. Big-model agents are explicitly unmarked,
240
+ * even when a marker was inherited from a worker parent (nested fan-out).
241
+ * Context is always regenerated: a worker deletes its own context before
242
+ * spawning tools, so nothing inherited is trustworthy.
243
+ */
244
+ export function applyPiSubagentChildWorkerScope(env = process.env, uuid = randomUUID) {
245
+ const agent = env[PI_SUBAGENT_CHILD_AGENT_ENV]?.trim();
246
+ if (!agent)
247
+ return false;
248
+ const contextKeys = [
249
+ INTERNAL_WORKER_SESSION_ENV,
250
+ INTERNAL_WORKER_TASK_ENV,
251
+ INTERNAL_WORKER_RUN_ENV,
252
+ INTERNAL_WORKER_ROOT_REQUEST_ENV,
253
+ INTERNAL_WORKER_PARENT_REQUEST_ENV,
254
+ ];
255
+ const scopeOff = env[WORKER_SCOPE_ENV]?.trim().toLowerCase() === "off";
256
+ if (scopeOff || bigModelAgents(env).has(agent)) {
257
+ delete env[INTERNAL_WORKER_MARKER_ENV];
258
+ for (const key of contextKeys)
259
+ delete env[key];
260
+ return false;
261
+ }
262
+ env[INTERNAL_WORKER_MARKER_ENV] = "1";
263
+ for (const key of contextKeys)
264
+ env[key] = uuid();
265
+ return true;
266
+ }
267
+ /**
268
+ * The executable pi-subagents should spawn for children: this CLI itself.
269
+ * A compiled standalone binary is its own executable; under node/bun the
270
+ * built cli.js carries a shebang and the executable bit from the build.
271
+ */
272
+ export function resolveBundledChildBinary(argv1 = process.argv[1], execPath = process.execPath, fsImpl = fs) {
273
+ const execName = path.basename(execPath).toLowerCase();
274
+ if (!/^(node|bun)(\.exe)?$/.test(execName))
275
+ return execPath;
276
+ if (!argv1)
277
+ return undefined;
278
+ try {
279
+ const real = fsImpl.realpathSync(argv1);
280
+ if (!/cli\.(js|mjs|cjs)$/.test(real))
281
+ return undefined;
282
+ fsImpl.accessSync(real, fsImpl.constants.X_OK);
283
+ return real;
284
+ }
285
+ catch {
286
+ return undefined;
287
+ }
288
+ }
289
+ export function applyBundledChildBinaryDefault(env = process.env, resolved = resolveBundledChildBinary()) {
290
+ if (env[PI_SUBAGENT_BINARY_ENV]?.trim() || !resolved)
291
+ return;
292
+ env[PI_SUBAGENT_BINARY_ENV] = resolved;
293
+ }
294
+ export const BLOCKED_ORCHESTRATOR_TOOLS = new Set(["bash", "write", "edit", DELEGATE_TASK_TOOL_NAME]);
295
+ export const ORCHESTRATOR_GUIDANCE = [
296
+ "Delegation policy: you are the orchestrator. Perform implementation, mutation,",
297
+ "shell commands, tests, and long repetitive tool loops through the `subagent`",
298
+ "tool (worker for implementation, reviewer for checks, scout for codebase",
299
+ "exploration, researcher for web research). Direct bash/write/edit are disabled",
300
+ "in this session. Reading files and querying the GitNexus graph directly is fine",
301
+ "and unlimited — prefer gitnexus_query/context/impact to locate code before",
302
+ "delegating. For broad or multi-source research, fan out scout or researcher",
303
+ "subagents (or use /parallel-research) and run long investigations in the",
304
+ "background (async) instead of one long foreground run. Give each subagent a",
305
+ "narrow, self-contained task rather than one sprawling one. Keep planning,",
306
+ "user communication, and final review here.",
307
+ ].join(" ");
308
+ export function isOrchestratorPolicyDisabled(env = process.env) {
309
+ return (env[PARENT_POLICY_ENV]?.trim().toLowerCase() === "off" ||
310
+ env[INTERNAL_WORKER_MARKER_ENV] === "1" ||
311
+ Boolean(env[PI_SUBAGENT_CHILD_AGENT_ENV]?.trim()));
312
+ }
313
+ /**
314
+ * Orchestrator-side policy for bundled subagents: hard-block mutating tools
315
+ * (the cost guarantee — implementation always bills the worker deployment)
316
+ * and steer everything else by prompt. No read budget: reads and graph calls
317
+ * are cheap on cached input and capping them measurably hurt output quality.
318
+ */
319
+ export function registerOrchestratorPolicy(pi, env = process.env) {
320
+ pi.on("before_agent_start", (event, ctx) => {
321
+ if (isOrchestratorPolicyDisabled(env) || !isAutoModel(ctx.model))
322
+ return;
323
+ return { systemPrompt: `${event.systemPrompt}\n\n${ORCHESTRATOR_GUIDANCE}` };
324
+ });
325
+ pi.on("tool_call", (event, ctx) => {
326
+ if (isOrchestratorPolicyDisabled(env) || !isAutoModel(ctx.model))
327
+ return;
328
+ if (!BLOCKED_ORCHESTRATOR_TOOLS.has(event.toolName))
329
+ return;
330
+ return {
331
+ block: true,
332
+ reason: "This orchestrator session does not run mutating tools directly. Use the `subagent` tool " +
333
+ "(worker for implementation, reviewer for checks, scout for exploration), then review its report.",
334
+ };
335
+ });
336
+ }
337
+ /**
338
+ * Nexus-branded aliases for the upstream pi-subagents env knobs: users set
339
+ * NEXUS_SUBAGENT_*, we copy onto the PI_SUBAGENT_* names the package reads.
340
+ * NEXUS_SUBAGENT_BINARY maps to PI_SUBAGENT_PI_BINARY (the child-spawn
341
+ * wrapper hook); every other NEXUS_SUBAGENT_X maps to PI_SUBAGENT_X.
342
+ * Explicit PI_* values always win — aliases never overwrite them.
343
+ */
344
+ export function applyNexusSubagentEnvAliases(env = process.env) {
345
+ const binary = env.NEXUS_SUBAGENT_BINARY?.trim();
346
+ if (binary && !env.PI_SUBAGENT_PI_BINARY?.trim()) {
347
+ env.PI_SUBAGENT_PI_BINARY = binary;
348
+ }
349
+ for (const [key, value] of Object.entries(env)) {
350
+ if (!key.startsWith("NEXUS_SUBAGENT_") || key === "NEXUS_SUBAGENT_BINARY")
351
+ continue;
352
+ if (!value?.trim())
353
+ continue;
354
+ const target = `PI_SUBAGENT_${key.slice("NEXUS_SUBAGENT_".length)}`;
355
+ if (!env[target]?.trim())
356
+ env[target] = value;
357
+ }
358
+ }
208
359
  /** Capture task routing once, then remove it before worker tools can inherit it. */
209
360
  export function consumeWorkerContext(env = process.env) {
210
361
  if (!isDelegatedWorkerProcess(env))
@@ -317,7 +468,7 @@ export async function runDelegatedWorker(options) {
317
468
  if (snapshot.stopReason === "error" || snapshot.stopReason === "aborted")
318
469
  assistantFailed = true;
319
470
  emitProgress();
320
- if (turns >= MAX_DELEGATED_TURNS)
471
+ if (turns >= MAX_DELEGATED_TURNS && snapshot.stopReason === "toolUse")
321
472
  stop("turn_limit");
322
473
  };
323
474
  const abortHandler = () => stop("aborted");
@@ -369,15 +520,15 @@ export async function runDelegatedWorker(options) {
369
520
  }
370
521
  }
371
522
  const DelegateTaskParameters = Type.Object({
372
- task: Type.String({ description: "A narrow implementation task with explicit boundaries", maxLength: 32_768 }),
523
+ task: Type.String({ description: "A self-contained task with explicit boundaries", maxLength: 32_768 }),
373
524
  acceptanceCriteria: Type.Optional(Type.Array(Type.String({ maxLength: 2000 }), { maxItems: 10 })),
374
525
  relevantPaths: Type.Optional(Type.Array(Type.String({ maxLength: 1000 }), { maxItems: 20 })),
375
526
  });
376
527
  function resultContent(result) {
377
- const report = result.output.trim() || "No implementation report was returned.";
528
+ const report = result.output.trim() || "No worker report was returned.";
378
529
  if (result.status === "completed")
379
- return `Delegated implementation completed.\n\n${report}`;
380
- return `Delegated implementation did not complete (${result.status}).\n\nPartial report:\n${report}`;
530
+ return `Delegated work completed.\n\n${report}`;
531
+ return `Delegated work did not complete (${result.status}).\n\nPartial report:\n${report}`;
381
532
  }
382
533
  /** Register the tool only after the Hub explicitly advertises delegation. */
383
534
  export function registerDelegateTaskTool(pi, capability) {
@@ -387,13 +538,11 @@ export function registerDelegateTaskTool(pi, capability) {
387
538
  let rootRequestId;
388
539
  let latestRootRequestId;
389
540
  let activeWorkers = 0;
390
- let invocationCount = 0;
391
- let rootReviewToolCalls = 0;
392
- pi.on("before_agent_start", (event) => {
393
- invocationCount = 0;
394
- rootReviewToolCalls = 0;
541
+ pi.on("before_agent_start", (event, ctx) => {
542
+ if (!isAutoModel(ctx.model))
543
+ return;
395
544
  return {
396
- systemPrompt: `${event.systemPrompt}\n\nImplementation policy: perform implementation, mutation, shell, and repeated tool loops through delegate_task. Keep this parent conversation focused on planning and final review. Use only bounded read-only tools to inspect worker changes, verify graph impact, and reach the final answer.`,
545
+ systemPrompt: `${event.systemPrompt}\n\nDelegation guidance: prioritize delegate_task for self-contained, context-heavy investigation, broad searches, audits, repeated tool loops, or substantial implementation so the main conversation stays focused and efficient. Use your own tools directly when the work is small, depends on context already established in this conversation, requires direct verification, or delegation fails or reaches a limit. You remain responsible for the final result.`,
397
546
  };
398
547
  });
399
548
  pi.on("before_provider_headers", (event, ctx) => {
@@ -407,32 +556,10 @@ export function registerDelegateTaskTool(pi, capability) {
407
556
  event.headers[ROOT_REQUEST_ID_HEADER] = rootRequestId;
408
557
  event.headers[REQUEST_SCOPE_HEADER] = "root";
409
558
  });
410
- pi.on("tool_call", (event, ctx) => {
411
- if (!isAutoModel(ctx.model) || isDelegatedWorkerProcess() || event.toolName === DELEGATE_TASK_TOOL_NAME) {
412
- return;
413
- }
414
- if (!ROOT_REVIEW_TOOLS.has(event.toolName)) {
415
- return {
416
- block: true,
417
- reason: "This parent run is review-only. Use delegate_task for implementation, mutation, shell commands, tests, or other execution.",
418
- };
419
- }
420
- if (rootReviewToolCalls >= MAX_ROOT_REVIEW_TOOL_CALLS) {
421
- return {
422
- block: true,
423
- reason: "The bounded parent review budget is exhausted. Delegate further investigation or finalize the review.",
424
- };
425
- }
426
- rootReviewToolCalls += 1;
427
- });
428
559
  pi.registerTool({
429
560
  name: DELEGATE_TASK_TOOL_NAME,
430
561
  label: "Work",
431
- description: "Delegate one narrow implementation task to an isolated internal worker. Keep planning, user communication, and final verification in this conversation.",
432
- promptGuidelines: [
433
- "Use delegate_task for bounded implementation, mutation, command execution, tests, and repeated tool work after you have formed a plan.",
434
- "You remain responsible for reviewing the returned report, verifying the result, and communicating with the user.",
435
- ],
562
+ description: "Delegate one self-contained task to an isolated internal worker. Keep user communication and final responsibility in this conversation.",
436
563
  parameters: DelegateTaskParameters,
437
564
  executionMode: "sequential",
438
565
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
@@ -448,7 +575,7 @@ export function registerDelegateTaskTool(pi, capability) {
448
575
  details: { status: "failed", turns: 0 },
449
576
  };
450
577
  }
451
- if (activeWorkers >= MAX_PARALLEL_MUTATING_WORKERS || invocationCount >= MAX_DELEGATIONS_PER_RUN) {
578
+ if (activeWorkers >= MAX_PARALLEL_MUTATING_WORKERS) {
452
579
  return {
453
580
  content: [{ type: "text", text: "Delegated work is temporarily unavailable for this run." }],
454
581
  details: { status: "failed", turns: 0 },
@@ -468,7 +595,6 @@ export function registerDelegateTaskTool(pi, capability) {
468
595
  };
469
596
  }
470
597
  activeWorkers += 1;
471
- invocationCount += 1;
472
598
  try {
473
599
  const delegationId = randomUUID();
474
600
  rootRequestId ??= randomUUID();
@@ -551,10 +677,19 @@ export function registerWorkerGrantHeaders(pi, workerContext, controller) {
551
677
  });
552
678
  }
553
679
  export default function delegatedWorkerRuntime(pi) {
680
+ applyNexusSubagentEnvAliases();
681
+ if (bundledSubagentsAvailable())
682
+ process.env[BUNDLED_SUBAGENTS_ENV] = "1";
683
+ applyPiSubagentChildWorkerScope();
554
684
  const isWorker = isDelegatedWorkerProcess();
555
685
  const workerContext = consumeWorkerContext();
556
- if (!isWorker)
686
+ if (!isWorker) {
687
+ if (isExternalDelegationMode()) {
688
+ applyBundledChildBinaryDefault();
689
+ registerOrchestratorPolicy(pi);
690
+ }
557
691
  return;
692
+ }
558
693
  const config = resolveGitNexusConfig();
559
694
  const failClosedContext = {
560
695
  sessionId: "unavailable",