stitchkit 0.105.1 → 0.106.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 (35) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/agent-runtime/coding-shell-process.d.ts.map +1 -1
  3. package/dist/agent-runtime/store-conformance-lifecycle.d.ts +10 -10
  4. package/dist/agent-runtime/store-conformance-support.d.ts +5 -5
  5. package/dist/entrypoints/agent-runtime/coding-tools.js +2 -2
  6. package/dist/entrypoints/agent-runtime/sandbox.js +2 -2
  7. package/dist/entrypoints/process.js +1 -1
  8. package/dist/{index-5c26631f.js → index-4q9jvqg7.js} +222 -52
  9. package/dist/{index-y7k2cdgv.js → index-9bskshe3.js} +2 -4
  10. package/dist/process/cleanup.d.ts.map +1 -1
  11. package/dist/process/command-owner.d.ts.map +1 -1
  12. package/dist/process/contract.d.ts +86 -7
  13. package/dist/process/contract.d.ts.map +1 -1
  14. package/dist/process/group.d.ts +12 -0
  15. package/dist/process/group.d.ts.map +1 -1
  16. package/dist/process/launch.d.ts +5 -0
  17. package/dist/process/launch.d.ts.map +1 -1
  18. package/dist/process/output-drain.d.ts +14 -0
  19. package/dist/process/output-drain.d.ts.map +1 -0
  20. package/dist/process/owned-child.d.ts +9 -0
  21. package/dist/process/owned-child.d.ts.map +1 -1
  22. package/dist/process/terminate.d.ts +2 -0
  23. package/dist/process/terminate.d.ts.map +1 -1
  24. package/dist/process/transport.d.ts +5 -0
  25. package/dist/process/transport.d.ts.map +1 -1
  26. package/llms/process.txt +64 -15
  27. package/llms/stitchkit.txt +3 -3
  28. package/llms/{upgrading-0.37.0-0.50.0.txt → upgrading-0.37.0-0.53.0.txt} +25 -3
  29. package/llms/{upgrading-0.53.0-0.67.0.txt → upgrading-0.55.0-0.68.0.txt} +53 -25
  30. package/llms/{upgrading-0.68.0-0.94.0.txt → upgrading-0.69.0-0.95.0.txt} +45 -53
  31. package/llms/{upgrading-0.95.0-0.105.0.txt → upgrading-0.96.0-0.106.0.txt} +40 -45
  32. package/llms/upgrading.txt +4 -4
  33. package/llms-full.txt +104 -18
  34. package/llms.txt +5 -5
  35. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -15,6 +15,51 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
15
15
 
16
16
  ## [Unreleased]
17
17
 
18
+ ## [0.106.0] - 2026-10-06
19
+
20
+ ### ⚠️ Breaking changes
21
+
22
+ **Who must act:** code passed to `runNativeCommand`'s `onLeaderSettled` that treats
23
+ `kind: 'error'` as "the command was stopped" (a caller abort, `timeoutMs`, an exceeded output budget
24
+ or a failing sink).
25
+
26
+ - `stitchkit/process` — **a stopped leader settles as `'stopped'`, with its exit.**
27
+ `NativeCommandSettlement` gains `{ kind: 'stopped', cause, exitCode, signal }`: a stopped command
28
+ waits for the stop signals and the leader's exit, then settles with the stop's `cause` and the
29
+ exit code and signal the kernel reported (`SIGTERM`, or `SIGKILL` once the grace ended), which
30
+ were lost before. `'error'` now means only that no exit of the leader was observed (it could not
31
+ start, or its exit did not arrive within `cleanupTimeoutMs`); `'exit'` is unchanged. The hook
32
+ still runs once, after the stop sequence instead of beside it.
33
+ `// before: onLeaderSettled: (e) => { if (e.kind === 'error') stopped(e.cause) }` →
34
+ `// after: onLeaderSettled: (e) => { if (e.kind === 'stopped') stopped(e.cause, e.signal) }`
35
+ **Who must act:** code that handles a stop in `kind === 'error'`. See
36
+ [migration](docs/guide/upgrading.md#a-stopped-leader-settles-as-stopped). → ADR 0248
37
+
38
+ ### Added
39
+
40
+ - `stitchkit/process` — `runNativeCommand` takes `stdio: 'inherit'`: the command gets the caller's
41
+ stdin, stdout and stderr, so a terminal stays a terminal (colour, prompts, `isatty`); `capture`,
42
+ `onOutput`, `stdin`, `maxOutputBytes` and `drainTimeoutMs` are refused by name with it.
43
+ - `stitchkit/process` — `runNativeCommand` takes `group: 'caller'`: the command joins the caller's
44
+ process group, so a Ctrl-C at the terminal reaches it; only its leader is stopped (default stop
45
+ `{ target: 'leader', signal: 'SIGTERM', graceMs: 100 }`), and `descendants` and a group stop are
46
+ refused, since its group is the caller's. → ADR 0248
47
+
48
+ ## [0.105.2] - 2026-10-06
49
+
50
+ ### Added
51
+
52
+ - `stitchkit/process` — `runNativeCommand` takes `onLeaderStarted({ pid })`, called once right
53
+ after the leader exists (a throw stops the command with that error), and `drainTimeoutMs`,
54
+ which bounds how long the output pipes may stay open after the leader exited: a holder that
55
+ left the group with `setsid` otherwise keeps the command waiting until `timeoutMs` or the
56
+ signal, and with the bound it ends with `COMMAND_CLEANUP`.
57
+
58
+ ### Documentation
59
+
60
+ - `stitchkit/process` — the native IO guide no longer says that `cleanupTimeoutMs` bounds the drain
61
+ after the leader exited; it bounds the wait after a stop, and `drainTimeoutMs` bounds the drain.
62
+
18
63
  ## [0.105.1] - 2026-10-06
19
64
 
20
65
  ### Added
@@ -1 +1 @@
1
- {"version":3,"file":"coding-shell-process.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-shell-process.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,qBAAqB,EAAqB,MAAM,wBAAwB,CAAC;AAEvF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAuCrD,wBAAsB,cAAc,CAAC,KAAK,EAAE;IAC1C,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAC/C,aAAa,CAAC,EAAE,UAAU,CAAC,WAAW,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/E,KAAK,CAAC,EAAE,MAAM,UAAU,CAAC,WAAW,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;CACrE;;;;;;;;;;;;;;;GA4JA"}
1
+ {"version":3,"file":"coding-shell-process.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-shell-process.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,qBAAqB,EAAqB,MAAM,wBAAwB,CAAC;AAEvF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAuCrD,wBAAsB,cAAc,CAAC,KAAK,EAAE;IAC1C,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAC/C,aAAa,CAAC,EAAE,UAAU,CAAC,WAAW,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/E,KAAK,CAAC,EAAE,MAAM,UAAU,CAAC,WAAW,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;CACrE;;;;;;;;;;;;;;;GA2JA"}
@@ -133,11 +133,6 @@ export declare function fencingPhase(store: AgentRuntimeStore, conversationId: s
133
133
  parts: ({
134
134
  type: "text";
135
135
  text: string;
136
- } | {
137
- type: "file";
138
- mediaType: string;
139
- reference: string;
140
- filename?: string | undefined;
141
136
  } | {
142
137
  type: "reasoning";
143
138
  text: string;
@@ -146,6 +141,11 @@ export declare function fencingPhase(store: AgentRuntimeStore, conversationId: s
146
141
  provider: string;
147
142
  data: Record<string, import("zod").JSONType>;
148
143
  } | undefined;
144
+ } | {
145
+ type: "file";
146
+ mediaType: string;
147
+ reference: string;
148
+ filename?: string | undefined;
149
149
  } | {
150
150
  type: "source";
151
151
  sourceId: string;
@@ -313,11 +313,6 @@ export declare function fencingPhase(store: AgentRuntimeStore, conversationId: s
313
313
  parts: ({
314
314
  type: "text";
315
315
  text: string;
316
- } | {
317
- type: "file";
318
- mediaType: string;
319
- reference: string;
320
- filename?: string | undefined;
321
316
  } | {
322
317
  type: "reasoning";
323
318
  text: string;
@@ -326,6 +321,11 @@ export declare function fencingPhase(store: AgentRuntimeStore, conversationId: s
326
321
  provider: string;
327
322
  data: Record<string, import("zod").JSONType>;
328
323
  } | undefined;
324
+ } | {
325
+ type: "file";
326
+ mediaType: string;
327
+ reference: string;
328
+ filename?: string | undefined;
329
329
  } | {
330
330
  type: "source";
331
331
  sourceId: string;
@@ -8,6 +8,11 @@ export declare function userMessage(conversationId: string, id: string): {
8
8
  parts: ({
9
9
  type: "text";
10
10
  text: string;
11
+ } | {
12
+ type: "file";
13
+ mediaType: string;
14
+ reference: string;
15
+ filename?: string | undefined;
11
16
  } | {
12
17
  type: "reasoning";
13
18
  text: string;
@@ -16,11 +21,6 @@ export declare function userMessage(conversationId: string, id: string): {
16
21
  provider: string;
17
22
  data: Record<string, import("zod").JSONType>;
18
23
  } | undefined;
19
- } | {
20
- type: "file";
21
- mediaType: string;
22
- reference: string;
23
- filename?: string | undefined;
24
24
  } | {
25
25
  type: "source";
26
26
  sourceId: string;
@@ -7,10 +7,10 @@ import {
7
7
  AgentCodingToolAuthorizationSchema2,
8
8
  AgentCodingToolPathAuthorizationSchema2,
9
9
  createAgentCodingTools2
10
- } from "../../index-y7k2cdgv.js";
10
+ } from "../../index-9bskshe3.js";
11
11
  import"../../index-v5ee8ath.js";
12
12
  import"../../index-j4zh647a.js";
13
- import"../../index-5c26631f.js";
13
+ import"../../index-4q9jvqg7.js";
14
14
  import"../../index-8ks9b85e.js";
15
15
  import"../../index-gxm9rc97.js";
16
16
  import"../../index-yvfk3y36.js";
@@ -9,7 +9,7 @@ import"../../index-r393tnfp.js";
9
9
  import"../../index-rpzewktb.js";
10
10
  import {
11
11
  createAgentCodingTools2
12
- } from "../../index-y7k2cdgv.js";
12
+ } from "../../index-9bskshe3.js";
13
13
  import"../../index-v5ee8ath.js";
14
14
  import"../../index-j4zh647a.js";
15
15
  import {
@@ -19,7 +19,7 @@ import {
19
19
  nativeCommandOwner,
20
20
  terminateOwnedGroup,
21
21
  startNativeCommand
22
- } from "../../index-5c26631f.js";
22
+ } from "../../index-4q9jvqg7.js";
23
23
  import"../../index-gxm9rc97.js";
24
24
  import {
25
25
  MAX_TIMER_MS
@@ -7,7 +7,7 @@ import {
7
7
  import {
8
8
  NativeCommandError2,
9
9
  startNativeCommand
10
- } from "../index-5c26631f.js";
10
+ } from "../index-4q9jvqg7.js";
11
11
  // src/process/command.ts
12
12
  function runNativeCommand(input) {
13
13
  return startNativeCommand(input).result;
@@ -17,6 +17,12 @@ var NativeCommandSettlementSchema = z.discriminatedUnion("kind", [
17
17
  exitCode: z.number().int().nullable(),
18
18
  signal: z.string().nullable()
19
19
  }),
20
+ z.object({
21
+ kind: z.literal("stopped"),
22
+ cause: z.unknown(),
23
+ exitCode: z.number().int().nullable(),
24
+ signal: z.string().nullable()
25
+ }),
20
26
  z.object({ kind: z.literal("error"), cause: z.unknown() })
21
27
  ]);
22
28
  var COMMAND_CLEANUP_TIMEOUT_MS = 2000;
@@ -37,6 +43,16 @@ function unknownOptionMessage(keys) {
37
43
  return replacement === undefined ? `Unknown runNativeCommand option ${name}` : `Unknown runNativeCommand option ${name}; use ${replacement}`;
38
44
  }).join("; ");
39
45
  }
46
+ var DEFAULT_GROUP_STOP = {
47
+ target: "group",
48
+ signal: "SIGTERM",
49
+ graceMs: 100
50
+ };
51
+ var DEFAULT_LEADER_STOP = {
52
+ target: "leader",
53
+ signal: "SIGTERM",
54
+ graceMs: 100
55
+ };
40
56
  var NativeCommandOptionsSchema = z.strictObject({
41
57
  executable: z.string().min(1),
42
58
  args: z.array(z.string()).default([]),
@@ -51,12 +67,12 @@ var NativeCommandOptionsSchema = z.strictObject({
51
67
  maxStdinBytes: z.number().int().positive().default(1024 * 1024),
52
68
  onOutput: z.custom((v) => typeof v === "function").optional(),
53
69
  onLeaderSettled: z.custom((v) => typeof v === "function").optional(),
54
- stop: NativeCommandStopPolicySchema.default({
55
- target: "group",
56
- signal: "SIGTERM",
57
- graceMs: 100
58
- }),
59
- descendants: z.enum(["terminate-after-leader", "leave"]).default("terminate-after-leader"),
70
+ onLeaderStarted: z.custom((v) => typeof v === "function").optional(),
71
+ drainTimeoutMs: z.number().int().positive().max(MAX_TIMER_MS).optional(),
72
+ group: z.enum(["own", "caller"]).default("own"),
73
+ stdio: z.enum(["pipe", "inherit"]).default("pipe"),
74
+ stop: NativeCommandStopPolicySchema.optional(),
75
+ descendants: z.enum(["terminate-after-leader", "leave"]).optional(),
60
76
  cleanupTimeoutMs: z.number().int().positive().max(30000).default(COMMAND_CLEANUP_TIMEOUT_MS)
61
77
  }, {
62
78
  error: (issue) => issue.code === "unrecognized_keys" ? unknownOptionMessage(issue.keys) : undefined
@@ -80,7 +96,38 @@ var NativeCommandOptionsSchema = z.strictObject({
80
96
  input: ctx.value,
81
97
  message: "stdin exceeds maxStdinBytes"
82
98
  });
83
- });
99
+ if (input.group === "caller" && input.stop?.target === "group")
100
+ ctx.issues.push({
101
+ code: "custom",
102
+ input: ctx.value,
103
+ message: "group: 'caller' stops only the leader; use stop.target 'leader'"
104
+ });
105
+ if (input.group === "caller" && input.descendants !== undefined)
106
+ ctx.issues.push({
107
+ code: "custom",
108
+ input: ctx.value,
109
+ message: "group: 'caller' has no group of its own, so descendants does not apply"
110
+ });
111
+ if (input.stdio === "inherit") {
112
+ const piped = [
113
+ input.capture && "capture",
114
+ input.onOutput && "onOutput",
115
+ input.stdin && "stdin",
116
+ input.maxOutputBytes !== undefined && "maxOutputBytes",
117
+ input.drainTimeoutMs !== undefined && "drainTimeoutMs"
118
+ ].filter((name) => typeof name === "string");
119
+ for (const name of piped)
120
+ ctx.issues.push({
121
+ code: "custom",
122
+ input: ctx.value,
123
+ message: `stdio: 'inherit' leaves no pipe for ${name}`
124
+ });
125
+ }
126
+ }).transform((input) => ({
127
+ ...input,
128
+ stop: input.stop ?? (input.group === "caller" ? DEFAULT_LEADER_STOP : DEFAULT_GROUP_STOP),
129
+ descendants: input.descendants ?? "terminate-after-leader"
130
+ }));
84
131
  var NativeCommandResultSchema = z.object({
85
132
  exitCode: z.number().int().nullable(),
86
133
  signal: z.string().nullable(),
@@ -214,6 +261,37 @@ async function stopCommandLeader(stop) {
214
261
  }
215
262
  return await signalUntilSettled(pid, "SIGKILL", deadline) || signalled;
216
263
  }
264
+ async function stopLeaderAlone(stop) {
265
+ const { leader, policy } = stop;
266
+ const running = () => leader.exitCode === null && leader.signalCode === null;
267
+ const send = (signal) => {
268
+ if (leader.pid === undefined || !running())
269
+ return false;
270
+ try {
271
+ process.kill(leader.pid, signal);
272
+ return true;
273
+ } catch (error) {
274
+ if (isErrno(error, "ESRCH"))
275
+ return false;
276
+ throw error;
277
+ }
278
+ };
279
+ if (stop.force || policy.killOn?.aborted)
280
+ return send("SIGKILL");
281
+ const signalled = send(policy.signal);
282
+ if (signalled) {
283
+ const grace = new AbortController;
284
+ const end = () => grace.abort();
285
+ policy.killOn?.addEventListener("abort", end, { once: true });
286
+ stop.leaderExit.then(end, end);
287
+ await sleep(policy.graceMs, grace.signal).catch(() => {
288
+ return;
289
+ });
290
+ policy.killOn?.removeEventListener("abort", end);
291
+ grace.abort();
292
+ }
293
+ return send("SIGKILL") || signalled;
294
+ }
217
295
  function commandCleanupError(cause) {
218
296
  return new NativeCommandError2("COMMAND_CLEANUP", "Command cleanup did not complete", {
219
297
  cause
@@ -241,8 +319,25 @@ async function cleanupNativeCommand(input) {
241
319
  const released = signalled.then(awaitRelease, () => {
242
320
  return;
243
321
  });
322
+ const stopped = async () => {
323
+ const failed = { kind: "error", cause: input.error };
324
+ if (!input.transport)
325
+ return failed;
326
+ await signalled.catch(() => {
327
+ return;
328
+ });
329
+ const leader = await waitForCommandClose(input.transport.leader, input.timeoutMs).catch(() => {
330
+ return;
331
+ });
332
+ return leader?.kind === "exit" ? {
333
+ kind: "stopped",
334
+ cause: input.error,
335
+ exitCode: leader.exitCode,
336
+ signal: leader.signal
337
+ } : failed;
338
+ };
244
339
  const cleanup = await Promise.allSettled([
245
- input.settle(input.event ?? { kind: "error", cause: input.error }),
340
+ input.event ? input.settle(input.event) : stopped().then(input.settle),
246
341
  destroyed,
247
342
  signalled,
248
343
  released
@@ -316,6 +411,50 @@ function spawnOwnedCommand(input) {
316
411
  groups.add(child);
317
412
  return child;
318
413
  }
414
+ var INHERITED_OUTPUT = {
415
+ destroyed: true,
416
+ on: () => {
417
+ return;
418
+ },
419
+ destroy: () => {
420
+ return;
421
+ }
422
+ };
423
+
424
+ class InheritedChild {
425
+ child;
426
+ stdout = INHERITED_OUTPUT;
427
+ stderr = INHERITED_OUTPUT;
428
+ constructor(child) {
429
+ this.child = child;
430
+ }
431
+ get pid() {
432
+ return this.child.pid;
433
+ }
434
+ get exitCode() {
435
+ return this.child.exitCode;
436
+ }
437
+ get signalCode() {
438
+ return this.child.signalCode;
439
+ }
440
+ kill(signal) {
441
+ return this.child.kill(signal);
442
+ }
443
+ on(event, listener) {
444
+ return this.child.on(event, listener);
445
+ }
446
+ }
447
+ function spawnInheritedCommand(input) {
448
+ const child = new InheritedChild(spawn(input.executable, [...input.args], {
449
+ cwd: input.cwd,
450
+ env: input.env,
451
+ detached: input.group,
452
+ stdio: "inherit"
453
+ }));
454
+ if (input.group)
455
+ groups.add(child);
456
+ return child;
457
+ }
319
458
  function ownsCommandGroup(child) {
320
459
  return groups.has(child);
321
460
  }
@@ -517,15 +656,21 @@ function launchNativeCommand({
517
656
  }) {
518
657
  let native;
519
658
  const launch = driver?.launch;
520
- if (!launch)
521
- native = spawnOwnedCommand({
522
- executable: options.executable,
523
- args: options.args,
524
- cwd: options.cwd,
525
- env: options.envPolicy === "ambient" ? { ...process.env, ...options.env } : options.env ?? {},
526
- group: driver?.group ?? true
527
- });
528
- const child = launch ? launch() : native;
659
+ const spawnInput = {
660
+ executable: options.executable,
661
+ args: options.args,
662
+ cwd: options.cwd,
663
+ env: options.envPolicy === "ambient" ? { ...process.env, ...options.env } : options.env ?? {},
664
+ group: options.group === "own" && (driver?.group ?? true)
665
+ };
666
+ let inherited;
667
+ if (!launch) {
668
+ if (options.stdio === "inherit")
669
+ inherited = spawnInheritedCommand(spawnInput);
670
+ else
671
+ native = spawnOwnedCommand(spawnInput);
672
+ }
673
+ const child = launch ? launch() : native ?? inherited;
529
674
  if (!child)
530
675
  throw new Error("Native command launcher did not supply a process");
531
676
  const transport = createCommandTransport(child);
@@ -609,6 +754,44 @@ function createLeaderSettlement(options, signal) {
609
754
  };
610
755
  }
611
756
 
757
+ // src/process/output-drain.ts
758
+ function createOutputDrain(options, signal) {
759
+ const captured = { stdout: [], stderr: [] };
760
+ let total = 0;
761
+ const drain = async (stream, channel) => {
762
+ for await (const chunk of stream) {
763
+ if (signal.aborted)
764
+ continue;
765
+ if (!(chunk instanceof Uint8Array))
766
+ throw new TypeError("Expected binary command output");
767
+ total += chunk.byteLength;
768
+ if (options.maxOutputBytes !== undefined && total > options.maxOutputBytes)
769
+ throw new NativeCommandError2("COMMAND_LIMIT", "Command output budget exceeded", {
770
+ reason: "output-budget"
771
+ });
772
+ if (options.capture)
773
+ captured[channel].push(Uint8Array.from(chunk));
774
+ if (options.onOutput)
775
+ try {
776
+ await raceAbort(Promise.resolve().then(() => {
777
+ signal.throwIfAborted();
778
+ return options.onOutput?.(chunk, channel, signal);
779
+ }), signal);
780
+ } catch (error) {
781
+ if (!signal.aborted || error !== signal.reason)
782
+ throw error;
783
+ }
784
+ }
785
+ };
786
+ return {
787
+ drain,
788
+ captured: () => ({
789
+ stdout: Buffer.concat(captured.stdout),
790
+ stderr: Buffer.concat(captured.stderr)
791
+ })
792
+ };
793
+ }
794
+
612
795
  // src/process/start-failure.ts
613
796
  async function failedCommandStart(cause, cleanup) {
614
797
  const error = new NativeCommandError2("COMMAND_UNAVAILABLE", "Command could not start", {
@@ -629,6 +812,13 @@ function signalCommandChild(child, input) {
629
812
  };
630
813
  return input.policy.target === "leader" ? stopCommandLeader({ ...stop, leader: child, leaderExit: input.leaderExit }) : stopCommandGroup(stop);
631
814
  }
815
+ if (input.callerGroup)
816
+ return stopLeaderAlone({
817
+ leader: child,
818
+ policy: input.policy,
819
+ leaderExit: input.leaderExit,
820
+ force: input.force
821
+ });
632
822
  return Promise.resolve().then(() => {
633
823
  if (child.exitCode !== null || child.signalCode !== null)
634
824
  return false;
@@ -696,7 +886,8 @@ function startNativeCommand(input, onSpawn, driver) {
696
886
  if (!acquired)
697
887
  return Promise.resolve(false);
698
888
  return signalCommandChild(acquired.child, {
699
- group: driver?.group !== false,
889
+ group: driver?.group !== false && options.group === "own",
890
+ callerGroup: options.group === "caller",
700
891
  policy: options.stop,
701
892
  leaderExit: acquired.leader,
702
893
  cleanupTimeoutMs,
@@ -744,41 +935,18 @@ function startNativeCommand(input, onSpawn, driver) {
744
935
  })).then(completion.resolve, completion.reject);
745
936
  return owner;
746
937
  }
747
- const stdout = [];
748
- const stderr = [];
749
- let total = 0;
750
- const { stdout: output, stderr: errorOutput, leader, closed } = launched;
751
- const cancelCloseDeadline = createLeaderCloseDeadline(leader, driver?.closeTimeoutMs, controller);
752
- const drain = async (stream, channel, captured) => {
753
- for await (const chunk of stream) {
754
- if (controller.signal.aborted)
755
- continue;
756
- if (!(chunk instanceof Uint8Array))
757
- throw new TypeError("Expected binary command output");
758
- total += chunk.byteLength;
759
- if (options.maxOutputBytes !== undefined && total > options.maxOutputBytes)
760
- throw new NativeCommandError2("COMMAND_LIMIT", "Command output budget exceeded", {
761
- reason: "output-budget"
762
- });
763
- if (options.capture)
764
- captured.push(Uint8Array.from(chunk));
765
- if (options.onOutput)
766
- try {
767
- await raceAbort(Promise.resolve().then(() => {
768
- controller.signal.throwIfAborted();
769
- return options.onOutput?.(chunk, channel, controller.signal);
770
- }), controller.signal);
771
- } catch (error) {
772
- if (!controller.signal.aborted || error !== controller.signal.reason)
773
- throw error;
774
- }
775
- }
776
- };
938
+ const output = createOutputDrain(options, controller.signal);
939
+ const { stdout, stderr, leader, closed } = launched;
940
+ const cancelCloseDeadline = createLeaderCloseDeadline(leader, driver?.closeTimeoutMs ?? options.drainTimeoutMs, controller);
777
941
  let descendantsStopped = false;
778
942
  const settleLeader = async (event) => {
943
+ if (controller.signal.aborted)
944
+ return;
779
945
  await settle(event);
780
946
  if (controller.signal.aborted)
781
947
  return;
948
+ if (options.group === "caller")
949
+ return;
782
950
  if (options.descendants !== "terminate-after-leader" || event.kind !== "exit")
783
951
  return;
784
952
  try {
@@ -790,9 +958,12 @@ function startNativeCommand(input, onSpawn, driver) {
790
958
  const run = (async () => {
791
959
  try {
792
960
  launched.start();
961
+ const pid = acquired?.child.pid;
962
+ if (pid !== undefined)
963
+ options.onLeaderStarted?.({ pid });
793
964
  const drains = Promise.all([
794
- drain(output, "stdout", stdout),
795
- drain(errorOutput, "stderr", stderr)
965
+ output.drain(stdout, "stdout"),
966
+ output.drain(stderr, "stderr")
796
967
  ]);
797
968
  await raceAbort(Promise.all([drains, leader.then(settleLeader)]), controller.signal);
798
969
  const status = await raceAbort(closed, controller.signal);
@@ -801,8 +972,7 @@ function startNativeCommand(input, onSpawn, driver) {
801
972
  return {
802
973
  ...status,
803
974
  descendantsStopped,
804
- stdout: Buffer.concat(stdout),
805
- stderr: Buffer.concat(stderr)
975
+ ...output.captured()
806
976
  };
807
977
  } catch (error) {
808
978
  controller.abort(error);
@@ -12,7 +12,7 @@ import {
12
12
  NativeCommandError2,
13
13
  CommandStopped,
14
14
  startNativeCommand
15
- } from "./index-5c26631f.js";
15
+ } from "./index-4q9jvqg7.js";
16
16
  import {
17
17
  probeAgentProcessSandboxOutcome,
18
18
  missingSandboxRestrictions2,
@@ -1073,7 +1073,7 @@ async function runCodingShell(input) {
1073
1073
  descendants: "terminate-after-leader",
1074
1074
  onOutput: (bytes, channel) => retain(channel === "stdout" ? stdout : stderr, channel === "stdout" ? artifactStdout : artifactStderr, bytes),
1075
1075
  onLeaderSettled: (event) => {
1076
- if (event.kind !== "exit")
1076
+ if (event.kind === "error")
1077
1077
  return;
1078
1078
  exitCode = event.exitCode;
1079
1079
  signal = event.signal;
@@ -1109,8 +1109,6 @@ async function runCodingShell(input) {
1109
1109
  outcome = "cancelled";
1110
1110
  else
1111
1111
  throw error;
1112
- exitCode = child?.exitCode ?? exitCode;
1113
- signal = child?.signalCode ?? signal;
1114
1112
  }
1115
1113
  const hasArtifact = input.artifacts && artifactBytes > input.maxOutputBytes;
1116
1114
  const completeStdout = Buffer.concat(artifactStdout);
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/process/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE1D;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,KAAK,EAAE;IAChD,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,MAAM,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,KAAK,CAAC,EAAE,uBAAuB,CAAC;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BhB"}
1
+ {"version":3,"file":"cleanup.d.ts","sourceRoot":"","sources":["../../src/process/cleanup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE1D;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,KAAK,EAAE;IAChD,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,sBAAsB,CAAC;IACnC,MAAM,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,KAAK,CAAC,EAAE,uBAAuB,CAAC;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgDhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"command-owner.d.ts","sourceRoot":"","sources":["../../src/process/command-owner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAIzE,OAAO,EAEL,KAAK,oBAAoB,EAI1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAEL,KAAK,yBAAyB,EAG/B,MAAM,UAAU,CAAC;AAMlB,wFAAwF;AACxF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,oBAAoB,EAC3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,8BAA8B,KAAK,IAAI,EACzD,MAAM,CAAC,EAAE,yBAAyB;;;;;;;;;;EA+LnC"}
1
+ {"version":3,"file":"command-owner.d.ts","sourceRoot":"","sources":["../../src/process/command-owner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAIzE,OAAO,EAEL,KAAK,oBAAoB,EAI1B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAEL,KAAK,yBAAyB,EAG/B,MAAM,UAAU,CAAC;AAOlB,wFAAwF;AACxF,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,oBAAoB,EAC3B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,8BAA8B,KAAK,IAAI,EACzD,MAAM,CAAC,EAAE,yBAAyB;;;;;;;;;;EAkKnC"}