@caupulican/pi-adaptative 0.81.26 → 0.81.29

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 (286) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/cli/args.d.ts.map +1 -1
  3. package/dist/cli/args.js +3 -2
  4. package/dist/cli/args.js.map +1 -1
  5. package/dist/core/agent-session-services.d.ts +4 -0
  6. package/dist/core/agent-session-services.d.ts.map +1 -1
  7. package/dist/core/agent-session-services.js +19 -2
  8. package/dist/core/agent-session-services.js.map +1 -1
  9. package/dist/core/agent-session.d.ts +30 -12
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +128 -36
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/auth-storage.d.ts +6 -0
  14. package/dist/core/auth-storage.d.ts.map +1 -1
  15. package/dist/core/auth-storage.js +35 -0
  16. package/dist/core/auth-storage.js.map +1 -1
  17. package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
  18. package/dist/core/autonomy/bounded-completion.js +59 -19
  19. package/dist/core/autonomy/bounded-completion.js.map +1 -1
  20. package/dist/core/autonomy/contracts.d.ts +2 -0
  21. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  22. package/dist/core/autonomy/contracts.js.map +1 -1
  23. package/dist/core/autonomy/lane-tool-surface.d.ts +31 -0
  24. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -0
  25. package/dist/core/autonomy/lane-tool-surface.js +96 -0
  26. package/dist/core/autonomy/lane-tool-surface.js.map +1 -0
  27. package/dist/core/autonomy/lane-tracker.d.ts +5 -0
  28. package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
  29. package/dist/core/autonomy/lane-tracker.js +14 -3
  30. package/dist/core/autonomy/lane-tracker.js.map +1 -1
  31. package/dist/core/background-lane-controller.d.ts +28 -4
  32. package/dist/core/background-lane-controller.d.ts.map +1 -1
  33. package/dist/core/background-lane-controller.js +237 -28
  34. package/dist/core/background-lane-controller.js.map +1 -1
  35. package/dist/core/billing-failover-controller.d.ts.map +1 -1
  36. package/dist/core/billing-failover-controller.js +1 -1
  37. package/dist/core/billing-failover-controller.js.map +1 -1
  38. package/dist/core/context/current-work-memory.d.ts +9 -0
  39. package/dist/core/context/current-work-memory.d.ts.map +1 -0
  40. package/dist/core/context/current-work-memory.js +61 -0
  41. package/dist/core/context/current-work-memory.js.map +1 -0
  42. package/dist/core/context/file-store-memory-provider.d.ts +9 -0
  43. package/dist/core/context/file-store-memory-provider.d.ts.map +1 -0
  44. package/dist/core/context/file-store-memory-provider.js +106 -0
  45. package/dist/core/context/file-store-memory-provider.js.map +1 -0
  46. package/dist/core/context/long-term-memory-trigger.d.ts +15 -0
  47. package/dist/core/context/long-term-memory-trigger.d.ts.map +1 -0
  48. package/dist/core/context/long-term-memory-trigger.js +39 -0
  49. package/dist/core/context/long-term-memory-trigger.js.map +1 -0
  50. package/dist/core/context/memory-prompt-block.d.ts +3 -1
  51. package/dist/core/context/memory-prompt-block.d.ts.map +1 -1
  52. package/dist/core/context/memory-prompt-block.js +20 -3
  53. package/dist/core/context/memory-prompt-block.js.map +1 -1
  54. package/dist/core/context/memory-prompt-budget.d.ts +17 -0
  55. package/dist/core/context/memory-prompt-budget.d.ts.map +1 -0
  56. package/dist/core/context/memory-prompt-budget.js +64 -0
  57. package/dist/core/context/memory-prompt-budget.js.map +1 -0
  58. package/dist/core/context/memory-provider-contract.d.ts.map +1 -1
  59. package/dist/core/context/memory-provider-contract.js +2 -5
  60. package/dist/core/context/memory-provider-contract.js.map +1 -1
  61. package/dist/core/context/memory-tier-composer.d.ts +27 -0
  62. package/dist/core/context/memory-tier-composer.d.ts.map +1 -0
  63. package/dist/core/context/memory-tier-composer.js +79 -0
  64. package/dist/core/context/memory-tier-composer.js.map +1 -0
  65. package/dist/core/context-gc.d.ts.map +1 -1
  66. package/dist/core/context-gc.js +2 -0
  67. package/dist/core/context-gc.js.map +1 -1
  68. package/dist/core/context-pipeline.d.ts +5 -3
  69. package/dist/core/context-pipeline.d.ts.map +1 -1
  70. package/dist/core/context-pipeline.js +15 -5
  71. package/dist/core/context-pipeline.js.map +1 -1
  72. package/dist/core/cost/cost-summary.d.ts +3 -1
  73. package/dist/core/cost/cost-summary.d.ts.map +1 -1
  74. package/dist/core/cost/cost-summary.js +3 -3
  75. package/dist/core/cost/cost-summary.js.map +1 -1
  76. package/dist/core/cost-guard.d.ts +17 -7
  77. package/dist/core/cost-guard.d.ts.map +1 -1
  78. package/dist/core/cost-guard.js +33 -10
  79. package/dist/core/cost-guard.js.map +1 -1
  80. package/dist/core/default-tool-surface.d.ts +9 -0
  81. package/dist/core/default-tool-surface.d.ts.map +1 -0
  82. package/dist/core/default-tool-surface.js +18 -0
  83. package/dist/core/default-tool-surface.js.map +1 -0
  84. package/dist/core/delegation/worker-result.d.ts.map +1 -1
  85. package/dist/core/delegation/worker-result.js +3 -0
  86. package/dist/core/delegation/worker-result.js.map +1 -1
  87. package/dist/core/delegation/worker-runner.d.ts +11 -5
  88. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  89. package/dist/core/delegation/worker-runner.js +77 -16
  90. package/dist/core/delegation/worker-runner.js.map +1 -1
  91. package/dist/core/extensions/loader.d.ts.map +1 -1
  92. package/dist/core/extensions/loader.js +15 -0
  93. package/dist/core/extensions/loader.js.map +1 -1
  94. package/dist/core/extensions/runner.d.ts +2 -0
  95. package/dist/core/extensions/runner.d.ts.map +1 -1
  96. package/dist/core/extensions/runner.js +7 -0
  97. package/dist/core/extensions/runner.js.map +1 -1
  98. package/dist/core/extensions/types.d.ts +9 -0
  99. package/dist/core/extensions/types.d.ts.map +1 -1
  100. package/dist/core/extensions/types.js.map +1 -1
  101. package/dist/core/failure-corpus.d.ts.map +1 -1
  102. package/dist/core/failure-corpus.js +2 -4
  103. package/dist/core/failure-corpus.js.map +1 -1
  104. package/dist/core/footer-data-provider.d.ts.map +1 -1
  105. package/dist/core/footer-data-provider.js +9 -1
  106. package/dist/core/footer-data-provider.js.map +1 -1
  107. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
  108. package/dist/core/goals/goal-continuation-prompt.js +4 -3
  109. package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
  110. package/dist/core/http-dispatcher.d.ts +12 -0
  111. package/dist/core/http-dispatcher.d.ts.map +1 -1
  112. package/dist/core/http-dispatcher.js +42 -4
  113. package/dist/core/http-dispatcher.js.map +1 -1
  114. package/dist/core/local-runtime-controller.d.ts +16 -0
  115. package/dist/core/local-runtime-controller.d.ts.map +1 -1
  116. package/dist/core/local-runtime-controller.js +107 -28
  117. package/dist/core/local-runtime-controller.js.map +1 -1
  118. package/dist/core/memory/memory-manager.d.ts +8 -3
  119. package/dist/core/memory/memory-manager.d.ts.map +1 -1
  120. package/dist/core/memory/memory-manager.js +27 -11
  121. package/dist/core/memory/memory-manager.js.map +1 -1
  122. package/dist/core/memory/memory-provider.d.ts +5 -1
  123. package/dist/core/memory/memory-provider.d.ts.map +1 -1
  124. package/dist/core/memory/memory-provider.js.map +1 -1
  125. package/dist/core/memory/providers/file-store.d.ts +3 -1
  126. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  127. package/dist/core/memory/providers/file-store.js +7 -2
  128. package/dist/core/memory/providers/file-store.js.map +1 -1
  129. package/dist/core/memory/providers/transcript-recall.d.ts +1 -0
  130. package/dist/core/memory/providers/transcript-recall.d.ts.map +1 -1
  131. package/dist/core/memory/providers/transcript-recall.js +4 -7
  132. package/dist/core/memory/providers/transcript-recall.js.map +1 -1
  133. package/dist/core/memory-controller.d.ts +36 -10
  134. package/dist/core/memory-controller.d.ts.map +1 -1
  135. package/dist/core/memory-controller.js +137 -21
  136. package/dist/core/memory-controller.js.map +1 -1
  137. package/dist/core/model-registry.d.ts +25 -0
  138. package/dist/core/model-registry.d.ts.map +1 -1
  139. package/dist/core/model-registry.js +91 -8
  140. package/dist/core/model-registry.js.map +1 -1
  141. package/dist/core/model-resolver.d.ts.map +1 -1
  142. package/dist/core/model-resolver.js +30 -11
  143. package/dist/core/model-resolver.js.map +1 -1
  144. package/dist/core/model-router-controller.d.ts.map +1 -1
  145. package/dist/core/model-router-controller.js +33 -29
  146. package/dist/core/model-router-controller.js.map +1 -1
  147. package/dist/core/model-selection-controller.d.ts +3 -0
  148. package/dist/core/model-selection-controller.d.ts.map +1 -1
  149. package/dist/core/model-selection-controller.js +8 -1
  150. package/dist/core/model-selection-controller.js.map +1 -1
  151. package/dist/core/models/local-runtime.d.ts +2 -0
  152. package/dist/core/models/local-runtime.d.ts.map +1 -1
  153. package/dist/core/models/local-runtime.js +10 -4
  154. package/dist/core/models/local-runtime.js.map +1 -1
  155. package/dist/core/models/perf-profile.d.ts.map +1 -1
  156. package/dist/core/models/perf-profile.js +95 -33
  157. package/dist/core/models/perf-profile.js.map +1 -1
  158. package/dist/core/models/runtime-arbiter.d.ts +11 -0
  159. package/dist/core/models/runtime-arbiter.d.ts.map +1 -1
  160. package/dist/core/models/runtime-arbiter.js +35 -13
  161. package/dist/core/models/runtime-arbiter.js.map +1 -1
  162. package/dist/core/profile-filter-controller.d.ts +11 -0
  163. package/dist/core/profile-filter-controller.d.ts.map +1 -1
  164. package/dist/core/profile-filter-controller.js +56 -11
  165. package/dist/core/profile-filter-controller.js.map +1 -1
  166. package/dist/core/profile-registry.d.ts +2 -2
  167. package/dist/core/profile-registry.d.ts.map +1 -1
  168. package/dist/core/profile-registry.js +58 -7
  169. package/dist/core/profile-registry.js.map +1 -1
  170. package/dist/core/reflection-controller.d.ts +9 -7
  171. package/dist/core/reflection-controller.d.ts.map +1 -1
  172. package/dist/core/reflection-controller.js +81 -12
  173. package/dist/core/reflection-controller.js.map +1 -1
  174. package/dist/core/research/research-runner.d.ts +2 -2
  175. package/dist/core/research/research-runner.d.ts.map +1 -1
  176. package/dist/core/research/research-runner.js +8 -6
  177. package/dist/core/research/research-runner.js.map +1 -1
  178. package/dist/core/resource-loader.d.ts +4 -2
  179. package/dist/core/resource-loader.d.ts.map +1 -1
  180. package/dist/core/resource-loader.js +11 -1
  181. package/dist/core/resource-loader.js.map +1 -1
  182. package/dist/core/runtime-builder.d.ts +30 -3
  183. package/dist/core/runtime-builder.d.ts.map +1 -1
  184. package/dist/core/runtime-builder.js +140 -40
  185. package/dist/core/runtime-builder.js.map +1 -1
  186. package/dist/core/sdk.d.ts +8 -7
  187. package/dist/core/sdk.d.ts.map +1 -1
  188. package/dist/core/sdk.js +38 -5
  189. package/dist/core/sdk.js.map +1 -1
  190. package/dist/core/security/secret-text.d.ts +3 -0
  191. package/dist/core/security/secret-text.d.ts.map +1 -0
  192. package/dist/core/security/secret-text.js +26 -0
  193. package/dist/core/security/secret-text.js.map +1 -0
  194. package/dist/core/settings-manager.d.ts +59 -17
  195. package/dist/core/settings-manager.d.ts.map +1 -1
  196. package/dist/core/settings-manager.js +300 -51
  197. package/dist/core/settings-manager.js.map +1 -1
  198. package/dist/core/skills.d.ts.map +1 -1
  199. package/dist/core/skills.js +1 -1
  200. package/dist/core/skills.js.map +1 -1
  201. package/dist/core/system-prompt-builder.d.ts +7 -0
  202. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  203. package/dist/core/system-prompt-builder.js +23 -1
  204. package/dist/core/system-prompt-builder.js.map +1 -1
  205. package/dist/core/tool-gate-controller.d.ts +3 -0
  206. package/dist/core/tool-gate-controller.d.ts.map +1 -1
  207. package/dist/core/tool-gate-controller.js +20 -15
  208. package/dist/core/tool-gate-controller.js.map +1 -1
  209. package/dist/core/tool-selection/expected-utility.d.ts +51 -0
  210. package/dist/core/tool-selection/expected-utility.d.ts.map +1 -0
  211. package/dist/core/tool-selection/expected-utility.js +85 -0
  212. package/dist/core/tool-selection/expected-utility.js.map +1 -0
  213. package/dist/core/tool-selection/tool-performance-store.d.ts +82 -0
  214. package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -0
  215. package/dist/core/tool-selection/tool-performance-store.js +249 -0
  216. package/dist/core/tool-selection/tool-performance-store.js.map +1 -0
  217. package/dist/core/tool-selection/tool-selection-controller.d.ts +41 -0
  218. package/dist/core/tool-selection/tool-selection-controller.d.ts.map +1 -0
  219. package/dist/core/tool-selection/tool-selection-controller.js +199 -0
  220. package/dist/core/tool-selection/tool-selection-controller.js.map +1 -0
  221. package/dist/core/tools/delegate-status.d.ts +9 -0
  222. package/dist/core/tools/delegate-status.d.ts.map +1 -0
  223. package/dist/core/tools/delegate-status.js +52 -0
  224. package/dist/core/tools/delegate-status.js.map +1 -0
  225. package/dist/core/tools/delegate.d.ts +10 -0
  226. package/dist/core/tools/delegate.d.ts.map +1 -1
  227. package/dist/core/tools/delegate.js +36 -13
  228. package/dist/core/tools/delegate.js.map +1 -1
  229. package/dist/core/tools/run-toolkit-script.d.ts +3 -0
  230. package/dist/core/tools/run-toolkit-script.d.ts.map +1 -1
  231. package/dist/core/tools/run-toolkit-script.js +17 -7
  232. package/dist/core/tools/run-toolkit-script.js.map +1 -1
  233. package/dist/main.d.ts.map +1 -1
  234. package/dist/main.js +2 -0
  235. package/dist/main.js.map +1 -1
  236. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  237. package/dist/modes/interactive/components/footer.js +8 -1
  238. package/dist/modes/interactive/components/footer.js.map +1 -1
  239. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  240. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  241. package/dist/modes/interactive/components/settings-selector.js +69 -29
  242. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  243. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  244. package/dist/modes/interactive/components/thinking-selector.js +2 -0
  245. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  246. package/dist/modes/interactive/config-backup.d.ts +7 -1
  247. package/dist/modes/interactive/config-backup.d.ts.map +1 -1
  248. package/dist/modes/interactive/config-backup.js +196 -32
  249. package/dist/modes/interactive/config-backup.js.map +1 -1
  250. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  251. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  252. package/dist/modes/interactive/interactive-mode.js +22 -28
  253. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  254. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  255. package/dist/modes/interactive/local-model-commands.js +2 -0
  256. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  257. package/dist/modes/interactive/profile-menu-controller.d.ts +6 -4
  258. package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -1
  259. package/dist/modes/interactive/profile-menu-controller.js +248 -65
  260. package/dist/modes/interactive/profile-menu-controller.js.map +1 -1
  261. package/dist/modes/interactive/report-commands.d.ts.map +1 -1
  262. package/dist/modes/interactive/report-commands.js +8 -2
  263. package/dist/modes/interactive/report-commands.js.map +1 -1
  264. package/dist/modes/interactive/settings-selector-flow.d.ts.map +1 -1
  265. package/dist/modes/interactive/settings-selector-flow.js +8 -0
  266. package/dist/modes/interactive/settings-selector-flow.js.map +1 -1
  267. package/dist/modes/interactive/theme/theme-schema.json +1 -1
  268. package/dist/modes/interactive/theme/theme.d.ts +1 -1
  269. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  270. package/dist/modes/interactive/theme/theme.js +3 -1
  271. package/dist/modes/interactive/theme/theme.js.map +1 -1
  272. package/docs/extensions.md +32 -0
  273. package/docs/integration-sweep-builder-blueprint-2026-07-09.md +365 -0
  274. package/docs/integration-sweep-resume-2026-07-09.md +407 -0
  275. package/docs/resources.md +15 -7
  276. package/docs/settings.md +56 -5
  277. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  278. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  279. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  280. package/examples/extensions/preset.ts +2 -2
  281. package/examples/extensions/sandbox/package-lock.json +2 -2
  282. package/examples/extensions/sandbox/package.json +1 -1
  283. package/examples/extensions/with-deps/package-lock.json +2 -2
  284. package/examples/extensions/with-deps/package.json +1 -1
  285. package/npm-shrinkwrap.json +44 -35
  286. package/package.json +4 -4
@@ -14,7 +14,10 @@
14
14
  * delegations; goal auto-continue itself only ever asks the session to `continueGoalLoop`, so this
15
15
  * controller never touches `prompt()`, the last-assistant-message, retry, or streaming state.
16
16
  */
17
+ import path from "node:path";
18
+ import { createLaneToolSurface } from "./autonomy/lane-tool-surface.js";
17
19
  import { LaneTracker } from "./autonomy/lane-tracker.js";
20
+ import { safeRealpathSync } from "./autonomy/path-scope.js";
18
21
  import { appendLaneRecordSnapshot, getLaneRecordSnapshots } from "./autonomy/session-lane-record.js";
19
22
  import { composeSubagentSystemPrompt } from "./autonomy/subagent-prompt.js";
20
23
  import { AUTONOMY_TELEMETRY_EVENT_TYPES } from "./autonomy/telemetry-events.js";
@@ -45,7 +48,37 @@ export class BackgroundLaneController {
45
48
  _researchLaneAbort = new AbortController();
46
49
  /** Session-lifetime abort for in-flight delegated workers. */
47
50
  _workerDelegationAbort = new AbortController();
51
+ /** Session-local de-duplication for fail-closed profile grants that cannot bind to lane tools. */
52
+ _warnedUnboundLaneToolGrants = new Set();
53
+ /** Every background execution is retained until its terminal result is observed. */
54
+ _workerPromises = new Map();
55
+ _queuedWorkers = new Map();
56
+ _workerNotificationTimer;
57
+ _workersCompletedSinceFlush = 0;
58
+ _workersFailedSinceFlush = 0;
48
59
  deps;
60
+ _scheduleWorkerNotification() {
61
+ if (this._workerNotificationTimer !== undefined)
62
+ return;
63
+ this._workerNotificationTimer = setTimeout(() => {
64
+ this._workerNotificationTimer = undefined;
65
+ this.deps.emit({
66
+ type: "delegate_workers",
67
+ active: this._laneTracker.getActiveCount("worker"),
68
+ completedSinceFlush: this._workersCompletedSinceFlush,
69
+ failedSinceFlush: this._workersFailedSinceFlush,
70
+ });
71
+ this._workersCompletedSinceFlush = 0;
72
+ this._workersFailedSinceFlush = 0;
73
+ }, 75);
74
+ }
75
+ _recordWorkerTerminal(status) {
76
+ if (status === "succeeded")
77
+ this._workersCompletedSinceFlush++;
78
+ else
79
+ this._workersFailedSinceFlush++;
80
+ this._scheduleWorkerNotification();
81
+ }
49
82
  constructor(deps) {
50
83
  this.deps = deps;
51
84
  }
@@ -174,10 +207,6 @@ export class BackgroundLaneController {
174
207
  scheduleResearchLaneFromIdle() {
175
208
  if (this._isResearchLaneRunning || this.deps.isDisposed() || this.deps.isChildSession())
176
209
  return;
177
- if (!this.deps.getModelCapabilityProfile().backgroundLanesEnabled) {
178
- this._lastResearchLaneSkipReason = "model_context_too_small";
179
- return;
180
- }
181
210
  const research = this.deps.getSettingsManager().getResearchLaneSettings();
182
211
  if (!research.enabled) {
183
212
  this._lastResearchLaneSkipReason = "research_lane_disabled";
@@ -195,6 +224,15 @@ export class BackgroundLaneController {
195
224
  }
196
225
  if (!this._buildResearchLaneDemand())
197
226
  return;
227
+ const shipment = this._resolveLaneShipment(research, "no_research_model");
228
+ if (!shipment.ok) {
229
+ this._lastResearchLaneSkipReason = shipment.skipReason;
230
+ return;
231
+ }
232
+ if (!this._laneCapabilityProfile(shipment.model).backgroundLanesEnabled) {
233
+ this._lastResearchLaneSkipReason = "model_research_unsupported";
234
+ return;
235
+ }
198
236
  this.clearResearchLaneTimer();
199
237
  this._researchLaneTimer = setTimeout(() => {
200
238
  this._researchLaneTimer = undefined;
@@ -252,7 +290,12 @@ export class BackgroundLaneController {
252
290
  _resolveLaneShipment(laneSettings, missingModelReason) {
253
291
  let laneProfile;
254
292
  if (laneSettings.profile) {
255
- laneProfile = this.deps.getSettingsManager().getProfileRegistry().getProfile(laneSettings.profile);
293
+ const profileRef = laneSettings.profile.trim();
294
+ const registry = this.deps.getSettingsManager().getProfileRegistry();
295
+ laneProfile =
296
+ profileRef.startsWith("./") || profileRef.startsWith("../")
297
+ ? registry.resolveProfileRef(profileRef, this.deps.getCwd())
298
+ : registry.getProfile(profileRef);
256
299
  if (!laneProfile) {
257
300
  return { ok: false, skipReason: "lane_profile_not_found" };
258
301
  }
@@ -278,21 +321,27 @@ export class BackgroundLaneController {
278
321
  }
279
322
  return { ok: true, model, laneProfile };
280
323
  }
281
- /** UAC tool grants from a shipped lane profile, recorded on the lane envelope. */
282
- _laneProfileToolGrants(laneProfile) {
283
- const toolsFilter = laneProfile?.resources.tools;
284
- return {
285
- ...(toolsFilter?.allow && toolsFilter.allow.length > 0 ? { allowedTools: [...toolsFilter.allow] } : {}),
286
- ...(toolsFilter?.block && toolsFilter.block.length > 0 ? { deniedTools: [...toolsFilter.block] } : {}),
287
- };
324
+ _warnUnboundLaneToolGrants(laneProfile, surface) {
325
+ if (!laneProfile || surface.unboundAllowPatterns.length === 0)
326
+ return;
327
+ const warningKey = `${laneProfile.name}\0${[...surface.unboundAllowPatterns].sort().join("\0")}`;
328
+ if (this._warnedUnboundLaneToolGrants.has(warningKey))
329
+ return;
330
+ this._warnedUnboundLaneToolGrants.add(warningKey);
331
+ this.deps.emit({
332
+ type: "warning",
333
+ message: `Lane profile '${laneProfile.name}' grants unavailable isolated-lane tools: ${surface.unboundAllowPatterns.join(", ")}. Only classified lane tools can execute.`,
334
+ });
288
335
  }
289
336
  /** Stripped research envelope — never the foreground/architect envelope. */
290
- _buildResearchLaneEnvelope(maxUsd, laneProfile) {
337
+ _buildResearchLaneEnvelope(maxUsd, laneProfile, surface) {
291
338
  return {
292
339
  id: `research-${this.deps.getSessionId()}-${Date.now()}`,
293
340
  profileId: laneProfile?.name,
294
341
  capabilities: ["research", "read_files", "memory_read"],
295
- ...this._laneProfileToolGrants(laneProfile),
342
+ allowedTools: [...surface.allowedTools],
343
+ deniedTools: [...surface.deniedTools],
344
+ allowedPaths: [this.deps.getCwd()],
296
345
  maxEstimatedUsd: clampLaneMaxUsd(maxUsd, this.deps.getCapabilityEnvelope()?.maxEstimatedUsd),
297
346
  createdAt: new Date().toISOString(),
298
347
  };
@@ -323,6 +372,11 @@ export class BackgroundLaneController {
323
372
  return { started: false, skipReason: shipment.skipReason };
324
373
  }
325
374
  const { model, laneProfile } = shipment;
375
+ const laneCapability = this._laneCapabilityProfile(model);
376
+ if (!laneCapability.backgroundLanesEnabled) {
377
+ this._lastResearchLaneSkipReason = "model_research_unsupported";
378
+ return { started: false, skipReason: "model_research_unsupported" };
379
+ }
326
380
  this._isResearchLaneRunning = true;
327
381
  this._laneTracker.ensureCounterAtLeast(getLaneRecordSnapshots(this.deps.getSessionManager().getEntries()).length + 1);
328
382
  const startedRecord = this._laneTracker.start({ type: "research", goalId: demand.goalId });
@@ -336,11 +390,13 @@ export class BackgroundLaneController {
336
390
  maxSources: settings.maxSources,
337
391
  });
338
392
  const maxUsd = clampLaneMaxUsd(settings.maxUsd, this.deps.getCapabilityEnvelope()?.maxEstimatedUsd);
393
+ const toolSurface = createLaneToolSurface({ cwd: this.deps.getCwd(), profile: laneProfile });
394
+ this._warnUnboundLaneToolGrants(laneProfile, toolSurface);
339
395
  const result = await runResearch({
340
396
  query: demand.query,
341
397
  context: demand.context,
342
398
  sources: workspaceSources,
343
- envelope: this._buildResearchLaneEnvelope(maxUsd, laneProfile),
399
+ envelope: this._buildResearchLaneEnvelope(maxUsd, laneProfile, toolSurface),
344
400
  maxUsd,
345
401
  maxSources: settings.maxSources,
346
402
  maxFindings: settings.maxFindings,
@@ -358,7 +414,10 @@ export class BackgroundLaneController {
358
414
  messages: [{ role: "user", content: [{ type: "text", text: userPrompt }], timestamp: Date.now() }],
359
415
  model,
360
416
  thinkingLevel: laneProfile?.thinking ?? "off",
361
- maxTokens: this._laneCapabilityProfile(model).laneMaxOutputTokens,
417
+ maxTokens: laneCapability.laneMaxOutputTokens,
418
+ tools: toolSurface.tools,
419
+ maxTurns: 6,
420
+ beforeToolCall: toolSurface.beforeToolCall,
362
421
  signal,
363
422
  // Core/soul/role are all static per configuration — the provider can cache the prefix.
364
423
  cacheRetention: "short",
@@ -449,9 +508,38 @@ export class BackgroundLaneController {
449
508
  * {@link validateWorkerResult} before acceptance, and persist result + lane record + spawned
450
509
  * usage (idempotent per-lane reportId). Consumed by the `delegate` tool.
451
510
  */
452
- async runWorkerDelegationOnce(request) {
511
+ startWorkerDelegation(request) {
512
+ const settings = this.deps.getSettingsManager().getWorkerDelegationSettings();
513
+ const foreground = this.deps.getModel();
514
+ const managedLocal = foreground?.provider === "ollama" || foreground?.provider === "transformers";
515
+ if (managedLocal) {
516
+ if (this._queuedWorkers.size >= 8 ||
517
+ this._laneTracker.getActiveCount("worker") >= settings.maxConcurrent + 8) {
518
+ return { started: false, skipReason: "worker_delegation_queue_full" };
519
+ }
520
+ const record = this._laneTracker.enqueue({ type: "worker" });
521
+ this._queuedWorkers.set(record.laneId, request);
522
+ this._scheduleWorkerNotification();
523
+ return { started: true, record };
524
+ }
525
+ let startedRecord;
526
+ const promise = this.runWorkerDelegationOnce(request, (record) => {
527
+ startedRecord = record;
528
+ });
529
+ if (!startedRecord) {
530
+ // Preparation is synchronous up to the first isolated completion await. A promise that
531
+ // rejected before producing a lane is still observed below, so it cannot become unhandled.
532
+ void promise.catch(() => undefined);
533
+ return { started: false, skipReason: "worker_not_started" };
534
+ }
535
+ this._workerPromises.set(startedRecord.laneId, promise);
536
+ void promise.then(() => this._workerPromises.delete(startedRecord?.laneId ?? ""), () => this._workerPromises.delete(startedRecord?.laneId ?? ""));
537
+ return { started: true, record: startedRecord };
538
+ }
539
+ async runWorkerDelegationOnce(request, onStarted, existingRecord) {
453
540
  const delegationSettings = this.deps.getSettingsManager().getWorkerDelegationSettings();
454
- if (this._laneTracker.getActiveCount("worker") >= delegationSettings.maxConcurrent) {
541
+ const activeWorkers = this._laneTracker.getActiveCount("worker") - (existingRecord ? 1 : 0);
542
+ if (activeWorkers >= delegationSettings.maxConcurrent) {
455
543
  return { started: false, skipReason: "worker_delegation_already_running" };
456
544
  }
457
545
  if (this.deps.isDisposed()) {
@@ -462,6 +550,9 @@ export class BackgroundLaneController {
462
550
  return { started: false, skipReason: "missing_instructions" };
463
551
  }
464
552
  const settings = delegationSettings;
553
+ if (!this.deps.isDelegateToolActive()) {
554
+ return { started: false, skipReason: "delegate_tool_inactive" };
555
+ }
465
556
  if (!settings.enabled) {
466
557
  return { started: false, skipReason: "worker_delegation_disabled" };
467
558
  }
@@ -470,29 +561,45 @@ export class BackgroundLaneController {
470
561
  return { started: false, skipReason: shipment.skipReason };
471
562
  }
472
563
  const { model, laneProfile } = shipment;
564
+ const laneCapability = this._laneCapabilityProfile(model);
565
+ if (!laneCapability.backgroundLanesEnabled) {
566
+ return { started: false, skipReason: "model_delegation_unsupported" };
567
+ }
473
568
  this._laneTracker.ensureCounterAtLeast(getLaneRecordSnapshots(this.deps.getSessionManager().getEntries()).length + 1);
474
- const startedRecord = this._laneTracker.start({ type: "worker" });
569
+ const startedRecord = existingRecord ?? this._laneTracker.start({ type: "worker" });
570
+ if (existingRecord)
571
+ this._laneTracker.markRunning(existingRecord.laneId);
572
+ onStarted?.(startedRecord);
475
573
  const maxUsd = Math.min(settings.maxUsd, this.deps.getCapabilityEnvelope()?.maxEstimatedUsd ?? Number.POSITIVE_INFINITY);
574
+ const writeEnabled = settings.writeEnabled && settings.writePaths.length > 0;
575
+ const toolSurface = createLaneToolSurface({
576
+ cwd: this.deps.getCwd(),
577
+ profile: laneProfile,
578
+ writeEnabled,
579
+ writePaths: settings.writePaths,
580
+ });
581
+ this._warnUnboundLaneToolGrants(laneProfile, toolSurface);
582
+ const allowedActionOps = new Set(toolSurface.allowedTools.filter((name) => name === "write" || name === "edit"));
583
+ const writeGranted = writeEnabled && allowedActionOps.size > 0;
476
584
  const workerRequest = {
477
585
  id: startedRecord.laneId,
478
586
  instructions,
479
587
  route: {
480
588
  tier: "cheap",
481
- risk: "read-only",
589
+ risk: writeGranted ? "scoped-write" : "read-only",
482
590
  confidence: 1,
483
591
  reasonCode: "scout_worker",
484
- reasons: ["Read-only scout delegation"],
592
+ reasons: [writeGranted ? "Path-scoped worker delegation" : "Read-only scout delegation"],
485
593
  },
486
594
  envelope: {
487
595
  id: `worker-${this.deps.getSessionId()}-${startedRecord.laneId}`,
488
596
  profileId: laneProfile?.name,
489
597
  // write_files requires BOTH the opt-in AND an explicit non-empty path scope —
490
598
  // an unscoped write grant is refused here, not discovered at validation time.
491
- capabilities: settings.writeEnabled && settings.writePaths.length > 0 ? ["read_files", "write_files"] : ["read_files"],
492
- ...(settings.writeEnabled && settings.writePaths.length > 0
493
- ? { allowedPaths: [...settings.writePaths] }
494
- : {}),
495
- ...this._laneProfileToolGrants(laneProfile),
599
+ capabilities: writeGranted ? ["read_files", "write_files"] : ["read_files"],
600
+ ...(writeGranted ? { allowedPaths: [...settings.writePaths] } : {}),
601
+ allowedTools: [...toolSurface.allowedTools],
602
+ deniedTools: [...toolSurface.deniedTools],
496
603
  maxEstimatedUsd: maxUsd,
497
604
  createdAt: new Date().toISOString(),
498
605
  },
@@ -513,17 +620,39 @@ export class BackgroundLaneController {
513
620
  const usageReportId = `worker:${this.deps.getSessionId()}:${startedRecord.laneId}`;
514
621
  try {
515
622
  let spentUsage;
623
+ const toolChangedFiles = new Set();
624
+ const toolIssues = new Set();
516
625
  const outcome = await runWorker({
517
626
  request: workerRequest,
518
627
  maxUsd,
519
628
  maxWallClockMs: settings.maxWallClockMs,
520
629
  usageReportId,
630
+ getChangedFiles: () => [...toolChangedFiles],
521
631
  signal: this._workerDelegationAbort.signal,
522
632
  // Parent validation must use the same relative-path baseline the runner reports in.
523
633
  cwd: this.deps.getCwd(),
524
634
  // Write lane (G2): runner-side action application through the envelope path scope.
525
635
  applyActions: workerRequest.envelope.capabilities.includes("write_files")
526
- ? (actions) => applyWorkerActions({ actions, envelope: workerRequest.envelope, cwd: this.deps.getCwd() })
636
+ ? (actions) => {
637
+ const permitted = actions.filter((action) => allowedActionOps.has(action.op));
638
+ const applied = applyWorkerActions({
639
+ actions: permitted,
640
+ envelope: workerRequest.envelope,
641
+ cwd: this.deps.getCwd(),
642
+ });
643
+ return {
644
+ ...applied,
645
+ refused: [
646
+ ...actions
647
+ .filter((action) => !allowedActionOps.has(action.op))
648
+ .map((action) => ({
649
+ path: action.path,
650
+ reason: `${action.op} is not granted by the lane profile`,
651
+ })),
652
+ ...applied.refused,
653
+ ],
654
+ };
655
+ }
527
656
  : undefined,
528
657
  complete: async ({ systemPrompt, userPrompt, signal }) => {
529
658
  const completion = await this.deps.runIsolatedCompletion({
@@ -537,7 +666,45 @@ export class BackgroundLaneController {
537
666
  messages: [{ role: "user", content: [{ type: "text", text: userPrompt }], timestamp: Date.now() }],
538
667
  model,
539
668
  thinkingLevel: laneProfile?.thinking ?? "off",
540
- maxTokens: this._laneCapabilityProfile(model).laneMaxOutputTokens,
669
+ maxTokens: laneCapability.laneMaxOutputTokens,
670
+ tools: toolSurface.tools,
671
+ maxTurns: 6,
672
+ beforeToolCall: async (context, toolSignal) => {
673
+ const decision = await toolSurface.beforeToolCall(context, toolSignal);
674
+ if (decision?.block) {
675
+ toolIssues.add(`${context.toolCall.name} blocked: ${decision.reason ?? "capability denied"}`);
676
+ }
677
+ return decision;
678
+ },
679
+ afterToolCall: async ({ toolCall, args, isError }) => {
680
+ // This hook runs only for a validated, gate-approved tool that actually entered
681
+ // execution. Record a direct mutation target before inspecting `isError`: write/edit
682
+ // may have changed disk and then observed cancellation, timeout, or a late I/O error.
683
+ // Pre-gate/profile/path refusals never reach afterToolCall, so they remain unreported.
684
+ if (toolCall.name === "write" || toolCall.name === "edit") {
685
+ if (args && typeof args === "object" && !Array.isArray(args)) {
686
+ const rawPath = args.path;
687
+ if (typeof rawPath === "string" && rawPath.length > 0) {
688
+ const absolutePath = path.isAbsolute(rawPath)
689
+ ? path.resolve(rawPath)
690
+ : path.resolve(this.deps.getCwd(), rawPath);
691
+ let canonicalPath = absolutePath;
692
+ try {
693
+ canonicalPath = safeRealpathSync(absolutePath);
694
+ }
695
+ catch {
696
+ // Execution was attempted; preserve conservative accounting with the lexical path.
697
+ }
698
+ toolChangedFiles.add(path.relative(this.deps.getCwd(), canonicalPath));
699
+ }
700
+ }
701
+ }
702
+ if (isError) {
703
+ toolIssues.add(`${toolCall.name} failed during isolated execution`);
704
+ return undefined;
705
+ }
706
+ return undefined;
707
+ },
541
708
  signal,
542
709
  // Core/soul/role are all static per configuration — the provider can cache the prefix.
543
710
  cacheRetention: "short",
@@ -547,6 +714,8 @@ export class BackgroundLaneController {
547
714
  text: completion.text,
548
715
  costUsd: completion.usage.cost.total,
549
716
  stopReason: String(completion.stopReason),
717
+ changedFiles: [...toolChangedFiles],
718
+ blockers: [...toolIssues],
550
719
  };
551
720
  },
552
721
  });
@@ -556,6 +725,8 @@ export class BackgroundLaneController {
556
725
  status: "canceled",
557
726
  reasonCode: "session_disposed",
558
727
  });
728
+ if (record)
729
+ this._recordWorkerTerminal(record.status);
559
730
  return { started: true, record, outcome };
560
731
  }
561
732
  this.deps.saveWorkerResultSnapshot(outcome.result, workerRequest);
@@ -568,6 +739,7 @@ export class BackgroundLaneController {
568
739
  costUsd: outcome.costUsd,
569
740
  });
570
741
  if (record) {
742
+ this._recordWorkerTerminal(record.status);
571
743
  appendLaneRecordSnapshot(this.deps.getSessionManager(), record);
572
744
  // G3: worker lane terminal record -> worker_result event. Lane outcome only
573
745
  // (status/reasonCode/cost) — never the worker's summary/changed-file text.
@@ -590,6 +762,8 @@ export class BackgroundLaneController {
590
762
  status: "failed",
591
763
  reasonCode: "worker_delegation_error",
592
764
  });
765
+ if (record)
766
+ this._recordWorkerTerminal(record.status);
593
767
  if (record && !this.deps.isDisposed()) {
594
768
  appendLaneRecordSnapshot(this.deps.getSessionManager(), record);
595
769
  this.deps.emitAutonomyTelemetry({
@@ -688,6 +862,41 @@ export class BackgroundLaneController {
688
862
  }
689
863
  return { started: true, model: modelRef, report };
690
864
  }
865
+ /** Start queued local workers at the owner session's foreground-idle boundary. */
866
+ drainQueuedWorkerDelegations() {
867
+ for (const [laneId, request] of [...this._queuedWorkers]) {
868
+ if (this._laneTracker.getActiveCount("worker") >
869
+ this.deps.getSettingsManager().getWorkerDelegationSettings().maxConcurrent)
870
+ break;
871
+ const record = this._laneTracker.getRecords().find((candidate) => candidate.laneId === laneId);
872
+ if (!record) {
873
+ this._queuedWorkers.delete(laneId);
874
+ continue;
875
+ }
876
+ this._queuedWorkers.delete(laneId);
877
+ const promise = this.runWorkerDelegationOnce(request, undefined, record);
878
+ this._workerPromises.set(laneId, promise);
879
+ void promise.then((outcome) => {
880
+ if (!outcome.started) {
881
+ const terminal = this._laneTracker.complete(laneId, {
882
+ status: "canceled",
883
+ reasonCode: outcome.skipReason,
884
+ });
885
+ if (terminal)
886
+ this._recordWorkerTerminal(terminal.status);
887
+ }
888
+ this._workerPromises.delete(laneId);
889
+ }, () => {
890
+ const terminal = this._laneTracker.complete(laneId, {
891
+ status: "failed",
892
+ reasonCode: "worker_background_error",
893
+ });
894
+ if (terminal)
895
+ this._recordWorkerTerminal(terminal.status);
896
+ this._workerPromises.delete(laneId);
897
+ });
898
+ }
899
+ }
691
900
  /** Fitness reports persisted for THIS host (measured evidence for architect/profile decisions). */
692
901
  getStoredFitnessReports() {
693
902
  try {