@pellux/goodvibes-sdk 0.37.1 → 0.38.0

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 (165) hide show
  1. package/dist/contracts/artifacts/operator-contract.json +1 -1
  2. package/dist/events/communication.d.ts +21 -1
  3. package/dist/events/communication.d.ts.map +1 -1
  4. package/dist/platform/agents/message-bus-core.d.ts +7 -0
  5. package/dist/platform/agents/message-bus-core.d.ts.map +1 -1
  6. package/dist/platform/agents/message-bus-core.js +2 -1
  7. package/dist/platform/agents/orchestrator-runner.d.ts +37 -1
  8. package/dist/platform/agents/orchestrator-runner.d.ts.map +1 -1
  9. package/dist/platform/agents/orchestrator-runner.js +158 -7
  10. package/dist/platform/agents/orchestrator.d.ts +40 -0
  11. package/dist/platform/agents/orchestrator.d.ts.map +1 -1
  12. package/dist/platform/agents/orchestrator.js +29 -0
  13. package/dist/platform/agents/turn-knowledge-injection.d.ts +107 -0
  14. package/dist/platform/agents/turn-knowledge-injection.d.ts.map +1 -0
  15. package/dist/platform/agents/turn-knowledge-injection.js +152 -0
  16. package/dist/platform/agents/wrfc-controller.d.ts +23 -0
  17. package/dist/platform/agents/wrfc-controller.d.ts.map +1 -1
  18. package/dist/platform/agents/wrfc-controller.js +55 -0
  19. package/dist/platform/config/manager.d.ts +9 -0
  20. package/dist/platform/config/manager.d.ts.map +1 -1
  21. package/dist/platform/config/manager.js +15 -0
  22. package/dist/platform/control-plane/approval-broker.d.ts +1 -0
  23. package/dist/platform/control-plane/approval-broker.d.ts.map +1 -1
  24. package/dist/platform/control-plane/approval-broker.js +2 -0
  25. package/dist/platform/core/adaptive-planner.d.ts +50 -0
  26. package/dist/platform/core/adaptive-planner.d.ts.map +1 -1
  27. package/dist/platform/core/adaptive-planner.js +49 -0
  28. package/dist/platform/core/index.d.ts +1 -0
  29. package/dist/platform/core/index.d.ts.map +1 -1
  30. package/dist/platform/core/index.js +1 -0
  31. package/dist/platform/core/orchestrator-runtime.d.ts +11 -0
  32. package/dist/platform/core/orchestrator-runtime.d.ts.map +1 -1
  33. package/dist/platform/core/orchestrator-turn-loop.d.ts +28 -1
  34. package/dist/platform/core/orchestrator-turn-loop.d.ts.map +1 -1
  35. package/dist/platform/core/orchestrator-turn-loop.js +115 -3
  36. package/dist/platform/core/orchestrator.d.ts +61 -0
  37. package/dist/platform/core/orchestrator.d.ts.map +1 -1
  38. package/dist/platform/core/orchestrator.js +74 -1
  39. package/dist/platform/core/plan-proposal.d.ts +165 -0
  40. package/dist/platform/core/plan-proposal.d.ts.map +1 -0
  41. package/dist/platform/core/plan-proposal.js +296 -0
  42. package/dist/platform/knowledge/knowledge-api.d.ts +26 -0
  43. package/dist/platform/knowledge/knowledge-api.d.ts.map +1 -1
  44. package/dist/platform/knowledge/knowledge-api.js +11 -0
  45. package/dist/platform/orchestration/budget.d.ts +22 -0
  46. package/dist/platform/orchestration/budget.d.ts.map +1 -0
  47. package/dist/platform/orchestration/budget.js +35 -0
  48. package/dist/platform/orchestration/cancellation.d.ts +31 -0
  49. package/dist/platform/orchestration/cancellation.d.ts.map +1 -0
  50. package/dist/platform/orchestration/cancellation.js +25 -0
  51. package/dist/platform/orchestration/controller-compat.d.ts +32 -0
  52. package/dist/platform/orchestration/controller-compat.d.ts.map +1 -0
  53. package/dist/platform/orchestration/controller-compat.js +13 -0
  54. package/dist/platform/orchestration/dirty-guard.d.ts +32 -0
  55. package/dist/platform/orchestration/dirty-guard.d.ts.map +1 -0
  56. package/dist/platform/orchestration/dirty-guard.js +147 -0
  57. package/dist/platform/orchestration/engine.d.ts +57 -0
  58. package/dist/platform/orchestration/engine.d.ts.map +1 -0
  59. package/dist/platform/orchestration/engine.js +418 -0
  60. package/dist/platform/orchestration/index.d.ts +18 -0
  61. package/dist/platform/orchestration/index.d.ts.map +1 -0
  62. package/dist/platform/orchestration/index.js +10 -0
  63. package/dist/platform/orchestration/persistence.d.ts +24 -0
  64. package/dist/platform/orchestration/persistence.d.ts.map +1 -0
  65. package/dist/platform/orchestration/persistence.js +176 -0
  66. package/dist/platform/orchestration/phase-runner.d.ts +87 -0
  67. package/dist/platform/orchestration/phase-runner.d.ts.map +1 -0
  68. package/dist/platform/orchestration/phase-runner.js +274 -0
  69. package/dist/platform/orchestration/scheduler.d.ts +45 -0
  70. package/dist/platform/orchestration/scheduler.d.ts.map +1 -0
  71. package/dist/platform/orchestration/scheduler.js +57 -0
  72. package/dist/platform/orchestration/types.d.ts +250 -0
  73. package/dist/platform/orchestration/types.d.ts.map +1 -0
  74. package/dist/platform/orchestration/types.js +15 -0
  75. package/dist/platform/runtime/emitters/communication.d.ts +11 -0
  76. package/dist/platform/runtime/emitters/communication.d.ts.map +1 -1
  77. package/dist/platform/runtime/emitters/communication.js +9 -0
  78. package/dist/platform/runtime/feature-flags/flags.d.ts.map +1 -1
  79. package/dist/platform/runtime/feature-flags/flags.js +17 -0
  80. package/dist/platform/runtime/fleet/adapters/agent.d.ts +56 -0
  81. package/dist/platform/runtime/fleet/adapters/agent.d.ts.map +1 -0
  82. package/dist/platform/runtime/fleet/adapters/agent.js +143 -0
  83. package/dist/platform/runtime/fleet/adapters/automation.d.ts +30 -0
  84. package/dist/platform/runtime/fleet/adapters/automation.d.ts.map +1 -0
  85. package/dist/platform/runtime/fleet/adapters/automation.js +41 -0
  86. package/dist/platform/runtime/fleet/adapters/background-process.d.ts +12 -0
  87. package/dist/platform/runtime/fleet/adapters/background-process.d.ts.map +1 -0
  88. package/dist/platform/runtime/fleet/adapters/background-process.js +46 -0
  89. package/dist/platform/runtime/fleet/adapters/code-index.d.ts +21 -0
  90. package/dist/platform/runtime/fleet/adapters/code-index.d.ts.map +1 -0
  91. package/dist/platform/runtime/fleet/adapters/code-index.js +44 -0
  92. package/dist/platform/runtime/fleet/adapters/orchestration.d.ts +47 -0
  93. package/dist/platform/runtime/fleet/adapters/orchestration.d.ts.map +1 -0
  94. package/dist/platform/runtime/fleet/adapters/orchestration.js +225 -0
  95. package/dist/platform/runtime/fleet/adapters/schedule.d.ts +14 -0
  96. package/dist/platform/runtime/fleet/adapters/schedule.d.ts.map +1 -0
  97. package/dist/platform/runtime/fleet/adapters/schedule.js +28 -0
  98. package/dist/platform/runtime/fleet/adapters/trigger.d.ts +12 -0
  99. package/dist/platform/runtime/fleet/adapters/trigger.d.ts.map +1 -0
  100. package/dist/platform/runtime/fleet/adapters/trigger.js +22 -0
  101. package/dist/platform/runtime/fleet/adapters/watcher.d.ts +13 -0
  102. package/dist/platform/runtime/fleet/adapters/watcher.d.ts.map +1 -0
  103. package/dist/platform/runtime/fleet/adapters/watcher.js +47 -0
  104. package/dist/platform/runtime/fleet/adapters/workflow.d.ts +11 -0
  105. package/dist/platform/runtime/fleet/adapters/workflow.d.ts.map +1 -0
  106. package/dist/platform/runtime/fleet/adapters/workflow.js +34 -0
  107. package/dist/platform/runtime/fleet/adapters/wrfc.d.ts +22 -0
  108. package/dist/platform/runtime/fleet/adapters/wrfc.d.ts.map +1 -0
  109. package/dist/platform/runtime/fleet/adapters/wrfc.js +228 -0
  110. package/dist/platform/runtime/fleet/index.d.ts +10 -0
  111. package/dist/platform/runtime/fleet/index.d.ts.map +1 -0
  112. package/dist/platform/runtime/fleet/index.js +6 -0
  113. package/dist/platform/runtime/fleet/registry.d.ts +113 -0
  114. package/dist/platform/runtime/fleet/registry.d.ts.map +1 -0
  115. package/dist/platform/runtime/fleet/registry.js +630 -0
  116. package/dist/platform/runtime/fleet/types.d.ts +199 -0
  117. package/dist/platform/runtime/fleet/types.d.ts.map +1 -0
  118. package/dist/platform/runtime/fleet/types.js +2 -0
  119. package/dist/platform/runtime/runtime-knowledge-api.d.ts +1 -1
  120. package/dist/platform/runtime/runtime-knowledge-api.d.ts.map +1 -1
  121. package/dist/platform/runtime/runtime-knowledge-api.js +1 -0
  122. package/dist/platform/runtime/services.d.ts +46 -0
  123. package/dist/platform/runtime/services.d.ts.map +1 -1
  124. package/dist/platform/runtime/services.js +78 -0
  125. package/dist/platform/state/code-index-chunking.d.ts +49 -0
  126. package/dist/platform/state/code-index-chunking.d.ts.map +1 -0
  127. package/dist/platform/state/code-index-chunking.js +111 -0
  128. package/dist/platform/state/code-index-db.d.ts +53 -0
  129. package/dist/platform/state/code-index-db.d.ts.map +1 -0
  130. package/dist/platform/state/code-index-db.js +101 -0
  131. package/dist/platform/state/code-index-store.d.ts +175 -0
  132. package/dist/platform/state/code-index-store.d.ts.map +1 -0
  133. package/dist/platform/state/code-index-store.js +612 -0
  134. package/dist/platform/state/index.d.ts +4 -2
  135. package/dist/platform/state/index.d.ts.map +1 -1
  136. package/dist/platform/state/index.js +2 -1
  137. package/dist/platform/state/knowledge-injection.d.ts +23 -0
  138. package/dist/platform/state/knowledge-injection.d.ts.map +1 -1
  139. package/dist/platform/state/knowledge-injection.js +34 -19
  140. package/dist/platform/state/memory-vector-store.d.ts +1 -13
  141. package/dist/platform/state/memory-vector-store.d.ts.map +1 -1
  142. package/dist/platform/state/memory-vector-store.js +6 -38
  143. package/dist/platform/state/sqlite-vec-loader.d.ts +25 -0
  144. package/dist/platform/state/sqlite-vec-loader.d.ts.map +1 -0
  145. package/dist/platform/state/sqlite-vec-loader.js +43 -0
  146. package/dist/platform/tools/agent/manager.d.ts +128 -1
  147. package/dist/platform/tools/agent/manager.d.ts.map +1 -1
  148. package/dist/platform/tools/agent/manager.js +137 -1
  149. package/dist/platform/tools/exec/runtime.d.ts.map +1 -1
  150. package/dist/platform/tools/exec/runtime.js +139 -44
  151. package/dist/platform/tools/exec/schema.d.ts +2 -0
  152. package/dist/platform/tools/exec/schema.d.ts.map +1 -1
  153. package/dist/platform/tools/fetch/runtime.d.ts +7 -0
  154. package/dist/platform/tools/fetch/runtime.d.ts.map +1 -1
  155. package/dist/platform/tools/fetch/runtime.js +8 -6
  156. package/dist/platform/tools/registry.d.ts +9 -3
  157. package/dist/platform/tools/registry.d.ts.map +1 -1
  158. package/dist/platform/tools/registry.js +9 -3
  159. package/dist/platform/types/tools.d.ts +14 -1
  160. package/dist/platform/types/tools.d.ts.map +1 -1
  161. package/dist/platform/version.js +1 -1
  162. package/dist/platform/workspace/checkpoint/side-git.d.ts +20 -8
  163. package/dist/platform/workspace/checkpoint/side-git.d.ts.map +1 -1
  164. package/dist/platform/workspace/checkpoint/side-git.js +21 -11
  165. package/package.json +17 -9
@@ -107,6 +107,40 @@ function applyExpectations(result, expect, exitCode) {
107
107
  function buildTimedOutResult(cmdStr, cwd, durationMs, progressFile) {
108
108
  return { cmd: cmdStr, exit_code: null, stdout: '', stderr: '', success: false, timed_out: true, duration_ms: durationMs, cwd, ...(progressFile ? { progress_file: progressFile } : {}) };
109
109
  }
110
+ /** Wave-4 cooperative cancellation (wo701): mirrors buildTimedOutResult's shape for the AbortSignal path. */
111
+ function buildCancelledResult(cmdStr, cwd, durationMs) {
112
+ return { cmd: cmdStr, exit_code: null, stdout: '', stderr: '', success: false, cancelled: true, duration_ms: durationMs, cwd };
113
+ }
114
+ /**
115
+ * Grace window for draining a killed child's stdout/stderr after a
116
+ * timeout/abort stop before returning regardless. Kept well under the
117
+ * cancellation-latency budget the cooperative-cancellation tests assert
118
+ * (< 5s).
119
+ */
120
+ const POST_STOP_DRAIN_GRACE_MS = 500;
121
+ /**
122
+ * Wave-4 cooperative cancellation (wo701): after a timeout/abort kill the
123
+ * child has already been SIGKILL'd, but a grandchild that inherited the
124
+ * stdout/stderr pipe can hold it open, so reading those streams to EOF may
125
+ * never settle. Awaiting the full IO promise unbounded here could therefore
126
+ * block until the grandchild's own duration elapses (observed as a hang under
127
+ * loaded CI). A cancelled/timed-out result never depends on fully-drained
128
+ * content, so bound the wait to a short grace window and return regardless.
129
+ * The still-pending IO promise is defused so it can never surface as an
130
+ * unhandled rejection; the grace timer is unref'd so it cannot keep the event
131
+ * loop (or a test runner) alive.
132
+ */
133
+ async function drainAfterStop(io, graceMs) {
134
+ const settled = io.then(() => undefined, () => undefined);
135
+ let timer;
136
+ const grace = new Promise((resolve) => {
137
+ timer = setTimeout(resolve, graceMs);
138
+ timer.unref?.();
139
+ });
140
+ await Promise.race([settled, grace]);
141
+ if (timer)
142
+ clearTimeout(timer);
143
+ }
110
144
  function getProgressDirectory(workingDirectory) {
111
145
  return join(workingDirectory, ...OVERFLOW_SUBDIR);
112
146
  }
@@ -144,7 +178,7 @@ async function spawnBackground(processManager, cmd, cwd, env) {
144
178
  function handleBgSpecialCommand(processManager, cmd) {
145
179
  return processManager.handleCommand(cmd);
146
180
  }
147
- async function runCommand(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout) {
181
+ async function runCommand(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout, signal) {
148
182
  const guardResult = await guardExecCommand(cmdStr, DEFAULT_ALLOWED_CLASSES, featureFlags);
149
183
  if (!guardResult.allowed) {
150
184
  const denial = formatDenialResponse(guardResult, cmdStr);
@@ -163,18 +197,25 @@ async function runCommand(processManager, overflowHandler, featureFlags, cmdStr,
163
197
  const timeoutMs = cmdInput.timeout_ms ?? globalTimeout;
164
198
  const mergedEnv = { ...buildCleanEnv(), ...cmdInput.env };
165
199
  const startTime = Date.now();
200
+ // Wave-4 cooperative cancellation (wo701) is wired for the foreground and
201
+ // progress-streamed paths (the common cases — progress auto-engages once
202
+ // timeout_ms exceeds PROGRESS_AUTO_THRESHOLD_MS, which the 120s default
203
+ // timeout always does). `until`-pattern commands are explicitly deferred
204
+ // (see the WO report) — the timeout kill-timer they already have still
205
+ // applies, just not an external AbortSignal.
166
206
  if (cmdInput.until) {
167
207
  return runUntil(processManager, overflowHandler, cmdStr, cmdInput, cwd, mergedEnv, timeoutMs, startTime);
168
208
  }
169
209
  const useProgress = cmdInput.progress === true || timeoutMs > PROGRESS_AUTO_THRESHOLD_MS;
170
210
  if (useProgress) {
171
- return runCommandWithProgress(processManager, overflowHandler, cmdStr, cmdInput, workingDirectory, cwd, mergedEnv, timeoutMs, startTime);
211
+ return runCommandWithProgress(processManager, overflowHandler, cmdStr, cmdInput, workingDirectory, cwd, mergedEnv, timeoutMs, startTime, signal);
172
212
  }
173
213
  const proc = Bun.spawn(['/bin/sh', '-c', cmdStr], { ...(cwd !== undefined ? { cwd } : {}), env: mergedEnv, stdout: 'pipe', stderr: 'pipe' });
174
214
  let timedOut = false;
215
+ let cancelled = false;
175
216
  let killTimer;
176
- let timeoutResolve;
177
- const timeoutSentinel = new Promise((res) => { timeoutResolve = res; });
217
+ let stopResolve;
218
+ const stopSentinel = new Promise((res) => { stopResolve = res; });
178
219
  killTimer = setTimeout(async () => {
179
220
  timedOut = true;
180
221
  try {
@@ -186,9 +227,31 @@ async function runCommand(processManager, overflowHandler, featureFlags, cmdStr,
186
227
  // The process may have already exited before kill.
187
228
  logger.debug('[ExecRuntime] kill on timeout failed (process may have exited)', { error: String(err) });
188
229
  }
189
- timeoutResolve();
230
+ stopResolve();
190
231
  }, timeoutMs);
191
232
  killTimer.unref?.();
233
+ const onAbort = () => {
234
+ if (timedOut || cancelled)
235
+ return;
236
+ cancelled = true;
237
+ void (async () => {
238
+ try {
239
+ proc.kill('SIGTERM');
240
+ await sleep(200);
241
+ proc.kill('SIGKILL');
242
+ }
243
+ catch (err) {
244
+ logger.debug('[ExecRuntime] kill on cancellation failed (process may have exited)', { error: String(err) });
245
+ }
246
+ stopResolve();
247
+ })();
248
+ };
249
+ if (signal) {
250
+ if (signal.aborted)
251
+ onAbort();
252
+ else
253
+ signal.addEventListener('abort', onAbort, { once: true });
254
+ }
192
255
  try {
193
256
  const procPromise = Promise.all([
194
257
  new Response(proc.stdout).text(),
@@ -198,20 +261,18 @@ async function runCommand(processManager, overflowHandler, featureFlags, cmdStr,
198
261
  let procResult;
199
262
  await Promise.race([
200
263
  procPromise.then((r) => { procResult = r; }),
201
- timeoutSentinel,
264
+ stopSentinel,
202
265
  ]);
203
266
  clearTimeout(killTimer);
204
- if (timedOut) {
205
- try {
206
- await procPromise;
207
- }
208
- catch (error) {
209
- logger.debug('exec foreground command collection failed after timeout', {
210
- command: cmdStr,
211
- error: summarizeError(error),
212
- });
213
- }
214
- return buildTimedOutResult(cmdStr, cwd, Date.now() - startTime);
267
+ if (signal)
268
+ signal.removeEventListener('abort', onAbort);
269
+ if (timedOut || cancelled) {
270
+ // Bounded drain: the child is already killed; never block past a short
271
+ // grace window on streams an orphaned grandchild may keep open.
272
+ await drainAfterStop(procPromise, POST_STOP_DRAIN_GRACE_MS);
273
+ return timedOut
274
+ ? buildTimedOutResult(cmdStr, cwd, Date.now() - startTime)
275
+ : buildCancelledResult(cmdStr, cwd, Date.now() - startTime);
215
276
  }
216
277
  const [stdoutRaw, stderrRaw, exitCode] = procResult;
217
278
  const stdoutResult = truncate(overflowHandler, stdoutRaw, 'stdout');
@@ -233,16 +294,19 @@ async function runCommand(processManager, overflowHandler, featureFlags, cmdStr,
233
294
  }
234
295
  catch (err) {
235
296
  clearTimeout(killTimer);
297
+ if (signal)
298
+ signal.removeEventListener('abort', onAbort);
236
299
  throw err;
237
300
  }
238
301
  }
239
- async function runCommandWithProgress(_processManager, overflowHandler, cmdStr, cmdInput, workingDirectory, cwd, mergedEnv, timeoutMs, startTime) {
302
+ async function runCommandWithProgress(_processManager, overflowHandler, cmdStr, cmdInput, workingDirectory, cwd, mergedEnv, timeoutMs, startTime, signal) {
240
303
  const progressFile = initProgressFile(cmdStr, workingDirectory);
241
304
  const proc = Bun.spawn(['/bin/sh', '-c', cmdStr], { ...(cwd !== undefined ? { cwd } : {}), env: mergedEnv, stdout: 'pipe', stderr: 'pipe' });
242
305
  let timedOut = false;
306
+ let cancelled = false;
243
307
  let killTimer;
244
- let timeoutResolve;
245
- const timeoutSentinel = new Promise((res) => { timeoutResolve = res; });
308
+ let stopResolve;
309
+ const stopSentinel = new Promise((res) => { stopResolve = res; });
246
310
  killTimer = setTimeout(async () => {
247
311
  timedOut = true;
248
312
  try {
@@ -254,9 +318,32 @@ async function runCommandWithProgress(_processManager, overflowHandler, cmdStr,
254
318
  logger.debug('[ExecRuntime] kill on streamed timeout failed (process may have exited)', { error: String(err) });
255
319
  }
256
320
  progressFile.append('# Timed out\n');
257
- timeoutResolve();
321
+ stopResolve();
258
322
  }, timeoutMs);
259
323
  killTimer.unref?.();
324
+ const onAbort = () => {
325
+ if (timedOut || cancelled)
326
+ return;
327
+ cancelled = true;
328
+ void (async () => {
329
+ try {
330
+ proc.kill('SIGTERM');
331
+ await sleep(200);
332
+ proc.kill('SIGKILL');
333
+ }
334
+ catch (err) {
335
+ logger.debug('[ExecRuntime] kill on cancellation failed (process may have exited)', { error: String(err) });
336
+ }
337
+ progressFile.append('# Cancelled\n');
338
+ stopResolve();
339
+ })();
340
+ };
341
+ if (signal) {
342
+ if (signal.aborted)
343
+ onAbort();
344
+ else
345
+ signal.addEventListener('abort', onAbort, { once: true });
346
+ }
260
347
  let stdoutBuf = '';
261
348
  let stderrBuf = '';
262
349
  const warnings = [];
@@ -304,19 +391,18 @@ async function runCommandWithProgress(_processManager, overflowHandler, cmdStr,
304
391
  }
305
392
  };
306
393
  const ioPromise = Promise.all([readStdout(), readStderr(), proc.exited]);
307
- await Promise.race([ioPromise, timeoutSentinel]);
394
+ await Promise.race([ioPromise, stopSentinel]);
308
395
  clearTimeout(killTimer);
309
- if (timedOut) {
310
- try {
311
- await ioPromise;
312
- }
313
- catch (error) {
314
- logger.debug('exec progress command IO collection failed after timeout', {
315
- command: cmdStr,
316
- error: summarizeError(error),
317
- });
318
- }
319
- return { ...buildTimedOutResult(cmdStr, cwd, Date.now() - startTime, progressFile.path), stdout: stdoutBuf, stderr: stderrBuf };
396
+ if (signal)
397
+ signal.removeEventListener('abort', onAbort);
398
+ if (timedOut || cancelled) {
399
+ // Bounded drain: the child is already killed; never block past a short
400
+ // grace window on streams an orphaned grandchild may keep open. stdoutBuf/
401
+ // stderrBuf already hold whatever the background readers captured so far.
402
+ await drainAfterStop(ioPromise, POST_STOP_DRAIN_GRACE_MS);
403
+ return timedOut
404
+ ? { ...buildTimedOutResult(cmdStr, cwd, Date.now() - startTime, progressFile.path), stdout: stdoutBuf, stderr: stderrBuf }
405
+ : { ...buildCancelledResult(cmdStr, cwd, Date.now() - startTime), stdout: stdoutBuf, stderr: stderrBuf, progress_file: progressFile.path };
320
406
  }
321
407
  const ioResult = await ioPromise.catch((error) => {
322
408
  logger.warn('exec progress command IO collection failed', {
@@ -440,6 +526,10 @@ export function isRetryableExecResult(result, allowed) {
440
526
  // Timed-out commands are never auto-retried — callers must decide
441
527
  if (result.timed_out)
442
528
  return false;
529
+ // Cancelled commands (Wave 4, wo701) must never be retried — retrying
530
+ // after an operator/engine kill would defeat the cancellation entirely.
531
+ if (result.cancelled)
532
+ return false;
443
533
  const combined = `${result.stdout}\n${result.stderr}`;
444
534
  // Terminal errors — always skip retry
445
535
  const TERMINAL_PATTERNS = [
@@ -476,9 +566,9 @@ export function isRetryableExecResult(result, allowed) {
476
566
  }
477
567
  return false;
478
568
  }
479
- async function runWithRetry(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout) {
569
+ async function runWithRetry(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout, signal) {
480
570
  if (!cmdInput.retry) {
481
- return runCommand(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout);
571
+ return runCommand(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout, signal);
482
572
  }
483
573
  const maxRetries = Math.min(cmdInput.retry.max ?? 3, 10);
484
574
  const delayMs = cmdInput.retry.delay_ms ?? 1000;
@@ -487,7 +577,7 @@ async function runWithRetry(processManager, overflowHandler, featureFlags, cmdSt
487
577
  const retryOn = cmdInput.retry.on;
488
578
  let lastResult;
489
579
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
490
- lastResult = await runCommand(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout);
580
+ lastResult = await runCommand(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout, signal);
491
581
  if (lastResult.success) {
492
582
  return { ...lastResult, retries: attempt };
493
583
  }
@@ -504,18 +594,21 @@ async function runWithRetry(processManager, overflowHandler, featureFlags, cmdSt
504
594
  }
505
595
  return { ...lastResult, retries: maxRetries };
506
596
  }
507
- async function executeResolvedCommand(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout) {
597
+ async function executeResolvedCommand(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout, signal) {
508
598
  const bgSpecial = handleBgSpecialCommand(processManager, cmdStr);
509
599
  if (bgSpecial)
510
600
  return bgSpecial;
511
601
  if (cmdInput.background) {
602
+ // Background processes intentionally outlive this tool call — cancelling
603
+ // the caller's item/agent must not kill a process the user asked to
604
+ // detach, so `signal` is deliberately not threaded here.
512
605
  return spawnBackground(processManager, cmdStr, resolveCwd(cmdInput.cwd, workingDirectory), cmdInput.env);
513
606
  }
514
- return runWithRetry(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout);
607
+ return runWithRetry(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout, signal);
515
608
  }
516
- async function executeResolvedCommands(processManager, overflowHandler, featureFlags, resolvedCmds, parallel, workingDirectory, globalTimeout, failFast) {
609
+ async function executeResolvedCommands(processManager, overflowHandler, featureFlags, resolvedCmds, parallel, workingDirectory, globalTimeout, failFast, signal) {
517
610
  if (parallel) {
518
- return mapWithConcurrency(resolvedCmds, MAX_PARALLEL_EXEC_COMMANDS, ({ cmdStr, cmdInput }) => executeResolvedCommand(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout));
611
+ return mapWithConcurrency(resolvedCmds, MAX_PARALLEL_EXEC_COMMANDS, ({ cmdStr, cmdInput }) => executeResolvedCommand(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout, signal));
519
612
  }
520
613
  const results = [];
521
614
  let stopped = false;
@@ -524,7 +617,7 @@ async function executeResolvedCommands(processManager, overflowHandler, featureF
524
617
  results.push({ cmd: cmdStr, exit_code: null, stdout: '', stderr: '', success: false, skipped: true });
525
618
  continue;
526
619
  }
527
- const result = await executeResolvedCommand(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout);
620
+ const result = await executeResolvedCommand(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout, signal);
528
621
  results.push(result);
529
622
  if (failFast && !result.success) {
530
623
  stopped = true;
@@ -550,6 +643,7 @@ function formatResult(result, verbosity) {
550
643
  stderr: firstStderr,
551
644
  ...(result.expectation_error && { expectation_error: result.expectation_error }),
552
645
  ...(result.timed_out && { timed_out: true }),
646
+ ...(result.cancelled && { cancelled: true }),
553
647
  ...(result.process_id && { process_id: result.process_id, pid: result.pid }),
554
648
  ...(result.progress_file && { progress_file: result.progress_file }),
555
649
  ...(result.warnings && { warnings: result.warnings }),
@@ -567,6 +661,7 @@ function formatResult(result, verbosity) {
567
661
  stderr: result.stderr,
568
662
  ...(result.expectation_error && { expectation_error: result.expectation_error }),
569
663
  ...(result.timed_out && { timed_out: true }),
664
+ ...(result.cancelled && { cancelled: true }),
570
665
  ...(result.process_id && { process_id: result.process_id, pid: result.pid }),
571
666
  ...(result.stdout_truncated && { stdout_truncated: true }),
572
667
  ...(result.stderr_truncated && { stderr_truncated: true }),
@@ -593,7 +688,7 @@ export function createExecTool(processManager, options = {}) {
593
688
  supportsProgress: true,
594
689
  supportsStreamingOutput: true,
595
690
  },
596
- async execute(args) {
691
+ async execute(args, opts) {
597
692
  try {
598
693
  if (!Array.isArray(args['commands']) || args['commands'].length === 0) {
599
694
  return { success: false, error: 'commands must be a non-empty array' };
@@ -627,7 +722,7 @@ export function createExecTool(processManager, options = {}) {
627
722
  }
628
723
  resolvedCmds.push({ cmdStr, cmdInput });
629
724
  }
630
- const results = await executeResolvedCommands(processManager, overflowHandler, featureFlags, resolvedCmds, input.parallel === true, workingDirectory, globalTimeout, failFast);
725
+ const results = await executeResolvedCommands(processManager, overflowHandler, featureFlags, resolvedCmds, input.parallel === true, workingDirectory, globalTimeout, failFast, opts?.signal);
631
726
  const formatted = results.map((r) => formatResult(r, verbosity));
632
727
  const allSuccess = results.every((r) => r.success);
633
728
  const responseData = formatted.length === 1 ? { ...formatted[0] } : { commands: formatted, total: formatted.length };
@@ -641,7 +736,7 @@ export function createExecTool(processManager, options = {}) {
641
736
  const failed = results.filter((r) => !r.success);
642
737
  const errorSummary = failed.length > 0
643
738
  ? `${failed.length} of ${results.length} command(s) failed: `
644
- + failed.map((r) => `'${r.cmd}'${r.skipped ? ' (skipped)' : ` exit ${r.exit_code}${r.timed_out ? ' (timed out)' : ''}`}`).join('; ')
739
+ + failed.map((r) => `'${r.cmd}'${r.skipped ? ' (skipped)' : ` exit ${r.exit_code}${r.timed_out ? ' (timed out)' : r.cancelled ? ' (cancelled)' : ''}`}`).join('; ')
645
740
  : undefined;
646
741
  return {
647
742
  success: allSuccess,
@@ -269,6 +269,8 @@ export interface ExecCommandResult {
269
269
  expectation_error?: string | undefined;
270
270
  /** Set when command exceeded timeout. */
271
271
  timed_out?: boolean | undefined;
272
+ /** Set when an external AbortSignal cancelled the command (Wave 4, wo701 — orchestration engine cancellation). Never combined with timed_out. */
273
+ cancelled?: boolean | undefined;
272
274
  /** Set when command ran in background. */
273
275
  process_id?: string | undefined;
274
276
  pid?: number | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/platform/tools/exec/schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkNnB,CAAC;AAIX,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAE9E,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAAC;IAC9C,0EAA0E;IAC1E,EAAE,CAAC,EAAE,aAAa,CAAC,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,GAAG,SAAS,CAAC;CACrE;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,oFAAoF;IACpF,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,yEAAyE;IACzE,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACzC,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,uFAAuF;IACvF,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,EAAE,UAAU,EAAE,GAAG,SAAS,CAAC;IACpC;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,2BAA2B;IAC3B,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACrC;AAID,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,0CAA0C;IAC1C,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,yCAAyC;IACzC,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACzC,uBAAuB;IACvB,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,6EAA6E;IAC7E,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CACjC;AAGD,YAAY,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/platform/tools/exec/schema.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,13 @@ import type { FeatureFlagManager } from '../../runtime/feature-flags/index.js';
6
6
  export interface FetchRuntimeDeps {
7
7
  readonly serviceRegistry?: Pick<ServiceRegistry, 'resolveAuth'> | null | undefined;
8
8
  readonly featureFlags?: Pick<FeatureFlagManager, 'isEnabled'> | null | undefined;
9
+ /**
10
+ * Wave-4 cooperative cancellation (wo701): an externally-supplied signal,
11
+ * combined with each request's own per-URL timeout signal via
12
+ * `AbortSignal.any`. Optional and additive — omitted, behavior is
13
+ * unchanged from before this field existed.
14
+ */
15
+ readonly signal?: AbortSignal | undefined;
9
16
  }
10
17
  interface CacheEntry {
11
18
  data: FetchUrlResult;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../src/platform/tools/fetch/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAkB,MAAM,sBAAsB,CAAC;AAEjE,OAAO,KAAK,EAAE,UAAU,EAAsF,MAAM,aAAa,CAAC;AAClI,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAUxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAM/E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,eAAe,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACnF,QAAQ,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;CAClF;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AASD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiC;IAC/D,OAAO,CAAC,eAAe,CAAK;IAE5B,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAOtE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;IAiBxC,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,WAAW,CAAC;CAkFpF;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,WAAW,CAAC,CAE5G;AAseD,wBAAgB,eAAe,CAC7B,IAAI,GAAE,gBAAqB,EAC3B,OAAO,sBAA4B,GAClC,IAAI,CAiCN"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../src/platform/tools/fetch/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAkB,MAAM,sBAAsB,CAAC;AAEjE,OAAO,KAAK,EAAE,UAAU,EAAsF,MAAM,aAAa,CAAC;AAClI,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAUxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAM/E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,eAAe,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACnF,QAAQ,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACjF;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAC3C;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb;AASD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiC;IAC/D,OAAO,CAAC,eAAe,CAAK;IAE5B,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI;IAOtE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,IAAI;IAiBxC,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,WAAW,CAAC;CAkFpF;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,WAAW,CAAC,CAE5G;AA0eD,wBAAgB,eAAe,CAC7B,IAAI,GAAE,gBAAqB,EAC3B,OAAO,sBAA4B,GAClC,IAAI,CAqCN"}
@@ -165,8 +165,9 @@ function encodeFormBodyData(bodyData) {
165
165
  }
166
166
  return params.toString();
167
167
  }
168
- async function fetchOneRaw(urlInput, headers, method, body, effectiveUrl, trustTierConfig, sanitizationEnabled) {
169
- const { signal, dispose } = createTimeoutController(urlInput.timeout_ms ?? DEFAULT_TIMEOUT_MS);
168
+ async function fetchOneRaw(urlInput, headers, method, body, effectiveUrl, trustTierConfig, sanitizationEnabled, externalSignal) {
169
+ const { signal: timeoutSignal, dispose } = createTimeoutController(urlInput.timeout_ms ?? DEFAULT_TIMEOUT_MS);
170
+ const signal = externalSignal ? AbortSignal.any([timeoutSignal, externalSignal]) : timeoutSignal;
170
171
  try {
171
172
  return sanitizationEnabled
172
173
  ? await fetchWithValidatedRedirects({
@@ -353,14 +354,14 @@ async function fetchOne(urlInput, opts, runtime) {
353
354
  const { headers, body: requestBody } = await prepareFetchRequest(urlInput, deps);
354
355
  const startTime = performance.now();
355
356
  try {
356
- let response = await fetchOneRaw(urlInput, headers, method, requestBody, effectiveUrl, trustTierConfig, sanitizationEnabled);
357
+ let response = await fetchOneRaw(urlInput, headers, method, requestBody, effectiveUrl, trustTierConfig, sanitizationEnabled, deps.signal);
357
358
  const retryOnAuth = urlInput.retry_on_auth ?? (urlInput.service !== undefined);
358
359
  if (response.status === 401 && retryOnAuth && urlInput.service && !(requestBody instanceof FormData)) {
359
360
  const refreshedHeaders = await deps.serviceRegistry?.resolveAuth(urlInput.service);
360
361
  if (refreshedHeaders) {
361
362
  const retryHeaders = { ...headers };
362
363
  Object.assign(retryHeaders, refreshedHeaders);
363
- response = await fetchOneRaw(urlInput, retryHeaders, method, requestBody, effectiveUrl, trustTierConfig, sanitizationEnabled);
364
+ response = await fetchOneRaw(urlInput, retryHeaders, method, requestBody, effectiveUrl, trustTierConfig, sanitizationEnabled, deps.signal);
364
365
  }
365
366
  }
366
367
  const durationMs = Math.round(performance.now() - startTime);
@@ -503,13 +504,14 @@ export function createFetchTool(deps = {}, runtime = new FetchRuntimeService())
503
504
  supportsProgress: true,
504
505
  supportsStreamingOutput: true,
505
506
  },
506
- async execute(args) {
507
+ async execute(args, opts) {
507
508
  if (!Array.isArray(args.urls) || args.urls.length === 0) {
508
509
  return { success: false, error: 'Missing or empty "urls" array' };
509
510
  }
510
511
  try {
511
512
  const input = { ...args, urls: args.urls };
512
- const output = await runtime.execute(input, deps);
513
+ const effectiveDeps = opts?.signal ? { ...deps, signal: opts.signal } : deps;
514
+ const output = await runtime.execute(input, effectiveDeps);
513
515
  return { success: true, output: JSON.stringify(output) };
514
516
  }
515
517
  catch (err) {
@@ -1,4 +1,4 @@
1
- import type { Tool, ToolDefinition, ToolResult } from '../types/tools.js';
1
+ import type { Tool, ToolDefinition, ToolExecuteOptions, ToolResult } from '../types/tools.js';
2
2
  import type { ContractVerificationResult, ContractVerifierOptions } from '../runtime/tools/contract-verifier.js';
3
3
  /**
4
4
  * ToolRegistry - Central registry for all tools available to the LLM.
@@ -41,8 +41,14 @@ export declare class ToolRegistry {
41
41
  verifyAllContracts(opts?: ContractVerifierOptions): Map<string, ContractVerificationResult>;
42
42
  /** Returns the ToolDefinition array formatted for LLM function calling. */
43
43
  getToolDefinitions(): ToolDefinition[];
44
- /** Execute a named tool with the given arguments. Wraps errors in ToolResult. */
45
- execute(callId: string, name: string, args: Record<string, unknown>): Promise<ToolResult>;
44
+ /**
45
+ * Execute a named tool with the given arguments. Wraps errors in ToolResult.
46
+ *
47
+ * `opts.signal` (Wave 4, wo701) is an additive pass-through to
48
+ * `tool.execute` — only tools that opt in (exec, fetch) read it. Callers
49
+ * that don't have a cancellation signal (the common case) omit `opts`.
50
+ */
51
+ execute(callId: string, name: string, args: Record<string, unknown>, opts?: ToolExecuteOptions): Promise<ToolResult>;
46
52
  /** Returns true if a tool with the given name is registered. */
47
53
  has(name: string): boolean;
48
54
  /** Returns all registered tools. */
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/platform/tools/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAI1E,OAAO,KAAK,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAGjH;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAA2B;IAExC,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAO1B;;;;;;;;;;;;;;OAcG;IACH,oBAAoB,CAClB,IAAI,EAAE,IAAI,EACV,IAAI,CAAC,EAAE,uBAAuB,GAC7B,0BAA0B;IAiB7B;;;;;;OAMG;IACH,cAAc,CACZ,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,uBAAuB,GAC7B,0BAA0B,GAAG,SAAS;IAOzC;;;;;OAKG;IACH,kBAAkB,CAChB,IAAI,CAAC,EAAE,uBAAuB,GAC7B,GAAG,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAK1C,2EAA2E;IAC3E,kBAAkB,IAAI,cAAc,EAAE;IAItC,iFAAiF;IAC3E,OAAO,CACX,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,UAAU,CAAC;IA0CtB,gEAAgE;IAChE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B,oCAAoC;IACpC,IAAI,IAAI,IAAI,EAAE;CAGf"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/platform/tools/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAI9F,OAAO,KAAK,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAC;AAGjH;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAA2B;IAExC,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAO1B;;;;;;;;;;;;;;OAcG;IACH,oBAAoB,CAClB,IAAI,EAAE,IAAI,EACV,IAAI,CAAC,EAAE,uBAAuB,GAC7B,0BAA0B;IAiB7B;;;;;;OAMG;IACH,cAAc,CACZ,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,uBAAuB,GAC7B,0BAA0B,GAAG,SAAS;IAOzC;;;;;OAKG;IACH,kBAAkB,CAChB,IAAI,CAAC,EAAE,uBAAuB,GAC7B,GAAG,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAK1C,2EAA2E;IAC3E,kBAAkB,IAAI,cAAc,EAAE;IAItC;;;;;;OAMG;IACG,OAAO,CACX,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,UAAU,CAAC;IA0CtB,gEAAgE;IAChE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B,oCAAoC;IACpC,IAAI,IAAI,IAAI,EAAE;CAGf"}
@@ -70,8 +70,14 @@ export class ToolRegistry {
70
70
  getToolDefinitions() {
71
71
  return Array.from(this.tools.values()).map((t) => t.definition);
72
72
  }
73
- /** Execute a named tool with the given arguments. Wraps errors in ToolResult. */
74
- async execute(callId, name, args) {
73
+ /**
74
+ * Execute a named tool with the given arguments. Wraps errors in ToolResult.
75
+ *
76
+ * `opts.signal` (Wave 4, wo701) is an additive pass-through to
77
+ * `tool.execute` — only tools that opt in (exec, fetch) read it. Callers
78
+ * that don't have a cancellation signal (the common case) omit `opts`.
79
+ */
80
+ async execute(callId, name, args, opts) {
75
81
  const tool = this.tools.get(name);
76
82
  if (!tool) {
77
83
  return {
@@ -85,7 +91,7 @@ export class ToolRegistry {
85
91
  // Premium models that send correct calls pass through unchanged.
86
92
  const repairResult = repairToolCall(name, args, tool.definition);
87
93
  const effectiveArgs = repairResult.repaired ? repairResult.fixed : args;
88
- const result = await tool.execute(effectiveArgs);
94
+ const result = await tool.execute(effectiveArgs, opts);
89
95
  // Tool.execute returns ToolResult without callId — inject it here
90
96
  const toolResult = { ...result, callId };
91
97
  if (repairResult.warnings && repairResult.warnings.length > 0) {
@@ -30,10 +30,23 @@ export interface ToolResult {
30
30
  /** Non-failing issues the caller should surface with the result. */
31
31
  warnings?: readonly string[] | undefined;
32
32
  }
33
+ /**
34
+ * Optional per-call execution options.
35
+ *
36
+ * `signal` (Wave 4, wo701) is ADDITIVE: it was not present before, and every
37
+ * existing tool implementation that declares `execute(args)` with a single
38
+ * parameter remains a valid implementation of this interface (structural
39
+ * typing — an unused trailing optional parameter is simply never read). Only
40
+ * tools that opt in (exec, fetch) read it to propagate cooperative
41
+ * cancellation into a spawned child process or an in-flight request.
42
+ */
43
+ export interface ToolExecuteOptions {
44
+ readonly signal?: AbortSignal | undefined;
45
+ }
33
46
  /** A registered tool with its definition and executor. */
34
47
  export interface Tool {
35
48
  definition: ToolDefinition;
36
49
  /** Tools return a result without callId — the registry injects callId when wrapping. */
37
- execute(args: Record<string, unknown>): Promise<Omit<ToolResult, 'callId'>>;
50
+ execute(args: Record<string, unknown>, opts?: ToolExecuteOptions): Promise<Omit<ToolResult, 'callId'>>;
38
51
  }
39
52
  //# sourceMappingURL=tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/platform/types/tools.ts"],"names":[],"mappings":"AAAA,qFAAqF;AAErF,2EAA2E;AAC3E,MAAM,MAAM,cAAc,GACtB,SAAS,GACT,UAAU,GACV,SAAS,GACT,MAAM,GACN,OAAO,GACP,UAAU,GACV,OAAO,CAAC;AAEZ,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;AAEtE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,oEAAoE;IACpE,WAAW,CAAC,EAAE,SAAS,cAAc,EAAE,GAAG,SAAS,CAAC;IACpD,yDAAyD;IACzD,WAAW,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAC9C,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,kEAAkE;IAClE,uBAAuB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC/C;AAED,8CAA8C;AAC9C,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,uCAAuC;AACvC,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,oEAAoE;IACpE,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CAC1C;AAED,0DAA0D;AAC1D,MAAM,WAAW,IAAI;IACnB,UAAU,EAAE,cAAc,CAAC;IAC3B,wFAAwF;IACxF,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;CAC7E"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/platform/types/tools.ts"],"names":[],"mappings":"AAAA,qFAAqF;AAErF,2EAA2E;AAC3E,MAAM,MAAM,cAAc,GACtB,SAAS,GACT,UAAU,GACV,SAAS,GACT,MAAM,GACN,OAAO,GACP,UAAU,GACV,OAAO,CAAC;AAEZ,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,CAAC;AAEtE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,oEAAoE;IACpE,WAAW,CAAC,EAAE,SAAS,cAAc,EAAE,GAAG,SAAS,CAAC;IACpD,yDAAyD;IACzD,WAAW,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAC9C,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,kEAAkE;IAClE,uBAAuB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC/C;AAED,8CAA8C;AAC9C,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,uCAAuC;AACvC,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,oEAAoE;IACpE,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CAC1C;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAC3C;AAED,0DAA0D;AAC1D,MAAM,WAAW,IAAI;IACnB,UAAU,EAAE,cAAc,CAAC;IAC3B,wFAAwF;IACxF,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;CACxG"}
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- let version = '0.37.1';
3
+ let version = '0.38.0';
4
4
  try {
5
5
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', '..', 'package.json'), 'utf-8'));
6
6
  version = pkg.version ?? version;
@@ -72,14 +72,26 @@ export declare class SideGitRunner {
72
72
  * Stage changes into the side index.
73
73
  *
74
74
  * @param paths When provided (non-empty), stage only these pathspecs
75
- * (scoped snapshot). Otherwise sweep the whole work tree, always excluding
76
- * `.goodvibes` (our own storage) via pathspec magic the same
77
- * `:(exclude)` pattern AgentWorktree already uses for the same reason.
78
- * Beyond that exclusion, git's normal `.gitignore` handling already applies
79
- * here: `.gitignore` matching is a work-tree-relative feature of git and
80
- * works identically regardless of where GIT_DIR points, so the workspace's
81
- * own `.gitignore` (node_modules, build output, etc.) is honored with no
82
- * extra configuration.
75
+ * (scoped snapshot). Otherwise sweep the whole work tree with a plain `.`
76
+ * pathspec. `.goodvibes` (our own storage) is kept out of that sweep by
77
+ * `.goodvibes/.gitignore`'s own `*` self-ignore line, written by `init()`
78
+ * (via `ensureGoodvibesIgnored()`) before this method can ever run — NOT by
79
+ * naming `.goodvibes` explicitly here. An earlier version of this method
80
+ * did pass an explicit `:(exclude).goodvibes` / `:(exclude).goodvibes/**`
81
+ * pathspec (mirroring the pattern AgentWorktree uses for the same reason),
82
+ * but that breaks the moment the WORKSPACE's own top-level `.gitignore`
83
+ * also happens to contain a `.goodvibes/` rule (exactly what this project's
84
+ * own TUI writes at startup): git aborts the entire `add -A` with "The
85
+ * following paths are ignored by one of your .gitignore files: .goodvibes
86
+ * ... Use -f if you really want to add them," because naming an
87
+ * already-ignored path in ANY pathspec — exclude magic or not — triggers
88
+ * that check. A path git discovers and skips implicitly while walking a
89
+ * wildcard `.` sweep never hits that check. Beyond `.goodvibes`, git's
90
+ * normal `.gitignore` handling already applies here: `.gitignore` matching
91
+ * is a work-tree-relative feature of git and works identically regardless
92
+ * of where GIT_DIR points, so the workspace's own `.gitignore`
93
+ * (node_modules, build output, etc.) is honored with no extra
94
+ * configuration.
83
95
  */
84
96
  stageAll(paths?: string[]): Promise<void>;
85
97
  /** Write the currently-staged index out as a tree object, without committing. Returns the tree hash. */
@@ -1 +1 @@
1
- {"version":3,"file":"side-git.d.ts","sourceRoot":"","sources":["../../../../src/platform/workspace/checkpoint/side-git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAQH,0EAA0E;AAC1E,eAAO,MAAM,eAAe,6CAA6C,CAAC;AAE1E,0DAA0D;AAC1D,eAAO,MAAM,qBAAqB,gCAAgC,CAAC;AA8CnE,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAY;gBAEpB,IAAI,EAAE,oBAAoB;IAUtC,gFAAgF;IAC1E,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAI1C;;;;;OAKG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB3B;;;;;;;;;OASG;IACH,OAAO,CAAC,sBAAsB;IAuB9B;;;;;;;;;;;;OAYG;IACG,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/C,wGAAwG;IAClG,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IAIlC,6DAA6D;IACvD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI7C;;;;;;;;;;;;;;;;;OAiBG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI9D,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzD,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,sEAAsE;IAChE,kBAAkB,IAAI,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAmBrE,+DAA+D;IACzD,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAK/D,+FAA+F;IACzF,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,wGAAwG;IAClG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvC,iGAAiG;IAC3F,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAItD,wGAAwG;IAClG,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI1D,6GAA6G;IACvG,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAKhE,6CAA6C;IACvC,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;CAG1B"}
1
+ {"version":3,"file":"side-git.d.ts","sourceRoot":"","sources":["../../../../src/platform/workspace/checkpoint/side-git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAQH,0EAA0E;AAC1E,eAAO,MAAM,eAAe,6CAA6C,CAAC;AAE1E,0DAA0D;AAC1D,eAAO,MAAM,qBAAqB,gCAAgC,CAAC;AA8CnE,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAY;gBAEpB,IAAI,EAAE,oBAAoB;IAUtC,gFAAgF;IAC1E,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAI1C;;;;;OAKG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB3B;;;;;;;;;OASG;IACH,OAAO,CAAC,sBAAsB;IAuB9B;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/C,wGAAwG;IAClG,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IAIlC,6DAA6D;IACvD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI7C;;;;;;;;;;;;;;;;;OAiBG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI9D,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzD,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,sEAAsE;IAChE,kBAAkB,IAAI,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAmBrE,+DAA+D;IACzD,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAK/D,+FAA+F;IACzF,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,wGAAwG;IAClG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvC,iGAAiG;IAC3F,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAItD,wGAAwG;IAClG,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI1D,6GAA6G;IACvG,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAKhE,6CAA6C;IACvC,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;CAG1B"}
@@ -161,19 +161,29 @@ export class SideGitRunner {
161
161
  * Stage changes into the side index.
162
162
  *
163
163
  * @param paths When provided (non-empty), stage only these pathspecs
164
- * (scoped snapshot). Otherwise sweep the whole work tree, always excluding
165
- * `.goodvibes` (our own storage) via pathspec magic the same
166
- * `:(exclude)` pattern AgentWorktree already uses for the same reason.
167
- * Beyond that exclusion, git's normal `.gitignore` handling already applies
168
- * here: `.gitignore` matching is a work-tree-relative feature of git and
169
- * works identically regardless of where GIT_DIR points, so the workspace's
170
- * own `.gitignore` (node_modules, build output, etc.) is honored with no
171
- * extra configuration.
164
+ * (scoped snapshot). Otherwise sweep the whole work tree with a plain `.`
165
+ * pathspec. `.goodvibes` (our own storage) is kept out of that sweep by
166
+ * `.goodvibes/.gitignore`'s own `*` self-ignore line, written by `init()`
167
+ * (via `ensureGoodvibesIgnored()`) before this method can ever run — NOT by
168
+ * naming `.goodvibes` explicitly here. An earlier version of this method
169
+ * did pass an explicit `:(exclude).goodvibes` / `:(exclude).goodvibes/**`
170
+ * pathspec (mirroring the pattern AgentWorktree uses for the same reason),
171
+ * but that breaks the moment the WORKSPACE's own top-level `.gitignore`
172
+ * also happens to contain a `.goodvibes/` rule (exactly what this project's
173
+ * own TUI writes at startup): git aborts the entire `add -A` with "The
174
+ * following paths are ignored by one of your .gitignore files: .goodvibes
175
+ * ... Use -f if you really want to add them," because naming an
176
+ * already-ignored path in ANY pathspec — exclude magic or not — triggers
177
+ * that check. A path git discovers and skips implicitly while walking a
178
+ * wildcard `.` sweep never hits that check. Beyond `.goodvibes`, git's
179
+ * normal `.gitignore` handling already applies here: `.gitignore` matching
180
+ * is a work-tree-relative feature of git and works identically regardless
181
+ * of where GIT_DIR points, so the workspace's own `.gitignore`
182
+ * (node_modules, build output, etc.) is honored with no extra
183
+ * configuration.
172
184
  */
173
185
  async stageAll(paths) {
174
- const pathspecs = paths && paths.length > 0
175
- ? paths
176
- : ['.', ':(exclude).goodvibes', ':(exclude).goodvibes/**'];
186
+ const pathspecs = paths && paths.length > 0 ? paths : ['.'];
177
187
  await this.git.raw(['add', '-A', '--', ...pathspecs]);
178
188
  }
179
189
  /** Write the currently-staged index out as a tree object, without committing. Returns the tree hash. */