stitchkit 0.103.10 → 0.103.12

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 (60) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/agent-runtime/coding-shell-process.d.ts.map +1 -1
  3. package/dist/agent-runtime/sandbox-process-owner.d.ts +1 -1
  4. package/dist/agent-runtime/sandbox-process-owner.d.ts.map +1 -1
  5. package/dist/agent-runtime/store-conformance-lifecycle.d.ts +10 -10
  6. package/dist/durability/json.d.ts.map +1 -1
  7. package/dist/entrypoints/agent-runtime/coding-tools.js +2 -1
  8. package/dist/entrypoints/agent-runtime/harness.js +2 -2
  9. package/dist/entrypoints/agent-runtime/sandbox.js +37 -19
  10. package/dist/entrypoints/agent-runtime.js +2 -2
  11. package/dist/entrypoints/application/diagnostic-journal.js +2 -2
  12. package/dist/entrypoints/files.js +8 -2
  13. package/dist/entrypoints/primitives.js +4 -3
  14. package/dist/entrypoints/process.js +1 -1
  15. package/dist/entrypoints/tools.js +1 -1
  16. package/dist/{index-ary5s7k0.js → index-69nvze1q.js} +1 -2
  17. package/dist/{index-a4ajxwmb.js → index-806d5phm.js} +114 -131
  18. package/dist/index-f330qqyg.js +653 -0
  19. package/dist/index-fm1c1a08.js +138 -0
  20. package/dist/index-xvdd2rbb.js +265 -0
  21. package/dist/{index-9jb61j5a.js → index-zp4jpkp0.js} +1 -1
  22. package/dist/internal/exclusive-lock-read.d.ts +20 -0
  23. package/dist/internal/exclusive-lock-read.d.ts.map +1 -0
  24. package/dist/internal/exclusive-lock.d.ts +2 -1
  25. package/dist/internal/exclusive-lock.d.ts.map +1 -1
  26. package/dist/internal/json-data.d.ts +8 -2
  27. package/dist/internal/json-data.d.ts.map +1 -1
  28. package/dist/internal/json-string-size.d.ts +3 -0
  29. package/dist/internal/json-string-size.d.ts.map +1 -0
  30. package/dist/internal/with-exclusive-lock.d.ts.map +1 -1
  31. package/dist/primitives/canonical-json.d.ts.map +1 -1
  32. package/dist/process/cleanup.d.ts +12 -0
  33. package/dist/process/cleanup.d.ts.map +1 -0
  34. package/dist/process/command-lifetime.d.ts +19 -0
  35. package/dist/process/command-lifetime.d.ts.map +1 -0
  36. package/dist/process/command-owner.d.ts +4 -2
  37. package/dist/process/command-owner.d.ts.map +1 -1
  38. package/dist/process/contract.d.ts +12 -1
  39. package/dist/process/contract.d.ts.map +1 -1
  40. package/dist/process/group.d.ts +6 -1
  41. package/dist/process/group.d.ts.map +1 -1
  42. package/dist/process/launch.d.ts +74 -0
  43. package/dist/process/launch.d.ts.map +1 -0
  44. package/dist/process/leader-settlement.d.ts +2 -0
  45. package/dist/process/leader-settlement.d.ts.map +1 -1
  46. package/dist/process/owned-child.d.ts +10 -0
  47. package/dist/process/owned-child.d.ts.map +1 -0
  48. package/dist/process/start-failure.d.ts +4 -0
  49. package/dist/process/start-failure.d.ts.map +1 -0
  50. package/dist/process/transport.d.ts +30 -0
  51. package/dist/process/transport.d.ts.map +1 -0
  52. package/llms/agent-runtime-sandbox.txt +9 -1
  53. package/llms/agent-runtime.txt +7 -1
  54. package/llms/process.txt +70 -7
  55. package/llms-full.txt +86 -9
  56. package/llms.txt +3 -3
  57. package/package.json +2 -2
  58. package/dist/index-6m6a9ty4.js +0 -285
  59. package/dist/index-c1jmtxa0.js +0 -74
  60. package/dist/index-ga597cvy.js +0 -184
package/CHANGELOG.md CHANGED
@@ -15,6 +15,38 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
15
15
 
16
16
  ## [Unreleased]
17
17
 
18
+ ## [0.103.12] - 2026-10-03
19
+
20
+ ### Fixed
21
+
22
+ - `stitchkit/primitives`: bounded canonical JSON serializes one admitted snapshot, preserving
23
+ historical key order and undefined handling while refusing unstable Proxy reads and oversized
24
+ escaped UTF-8 output before unbounded encoding. Durable JSON predicates avoid copying or encoding.
25
+ - `stitchkit/process`: post-spawn launch failures retain cleanup ownership; process-group cleanup
26
+ uses proven ownership and monotonic deadlines. Exclusive-lock stale guards have bounded depth.
27
+ - Direct sandbox launchers retain process-group cleanup through leader settlement.
28
+ - Release tooling requires every selected evidence job and remote exact-SHA push CI before tags;
29
+ CI covers the complete affected tree independently of which packages are published, preserving
30
+ package paths across renames and Unicode or control characters in filenames.
31
+ - Gate reuse includes tracked inputs matched by ignore rules, fingerprints only selected external
32
+ inputs, rechecks them after execution and serializes memo mutations. Memory admission reads exact
33
+ v1/v2 controller paths, including colons and trailing spaces; registry polling
34
+ bounds requests, bodies and waits with one elapsed deadline.
35
+
36
+
37
+ ## [0.103.11] - 2026-10-02
38
+
39
+ ### Added
40
+
41
+ - `stitchkit/process`: native command limit errors expose a typed `reason` (`deadline` or `output-budget`) independent of their message; existing codes, causes, cancellation and cleanup guarantees are preserved.
42
+
43
+ ### Fixed
44
+
45
+ - `stitchkit/files`: cancellation after acquiring an exclusive lock refuses the callback and releases ownership. Lock and reclaim-guard records use bounded descriptor reads and refuse FIFO, nonregular, linked, oversized or changing records.
46
+ - `stitchkit/process`: synchronous native launch failures settle external resources once before rejecting, with bounded cleanup and the original syscall cause.
47
+ - `stitchkit/agent-runtime`: coding commands share the native execution owner while retaining forced descendant cleanup, structural sandbox launchers, bounded artifacts and UTF-8 previews.
48
+ - Local gate evidence is saved only for stable inputs, including content changed and restored during a run. PostgreSQL fingerprint probes have finite budgets and distinguish URI connection overrides; unknown external inputs cannot authorize heavy memo reuse. Heavy concurrency accounts for visible cgroup memory limits.
49
+
18
50
  ## [0.103.10] - 2026-10-01
19
51
 
20
52
  ### Added
@@ -1 +1 @@
1
- {"version":3,"file":"coding-shell-process.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-shell-process.ts"],"names":[],"mappings":"AAEA,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;;;;;;;;;;;;;;;GAsKA"}
1
+ {"version":3,"file":"coding-shell-process.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/coding-shell-process.ts"],"names":[],"mappings":"AAMA,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;;;;;;;;;;;;;;;GAkKA"}
@@ -1,4 +1,4 @@
1
- import { type ChildProcessWithoutNullStreams } from 'node:child_process';
1
+ import type { ChildProcessWithoutNullStreams } from 'node:child_process';
2
2
  import type { AgentProcessSandbox } from './sandbox.js';
3
3
  /** All entry paths share one command admission limit and one shutdown barrier. */
4
4
  export declare function sandboxProcessOwner(assertActive: () => void, maximum: number): {
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox-process-owner.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/sandbox-process-owner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,8BAA8B,EAAS,MAAM,oBAAoB,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAGrD,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM;;mBAOrD,8BAA8B;;iBAerC,UAAU,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;;EA0B7D"}
1
+ {"version":3,"file":"sandbox-process-owner.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/sandbox-process-owner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAKzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAGrD,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM;;mBAiBrD,8BAA8B;;iBAmCrC,UAAU,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;;EAmB7D"}
@@ -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;
@@ -1 +1 @@
1
- {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/durability/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,iBAAiB,qCAS7B,CAAC"}
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/durability/json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,iBAAiB,qCAQ7B,CAAC"}
@@ -7,7 +7,8 @@ import {
7
7
  AgentCodingToolAuthorizationSchema2,
8
8
  AgentCodingToolPathAuthorizationSchema2,
9
9
  createAgentCodingTools2
10
- } from "../../index-a4ajxwmb.js";
10
+ } from "../../index-806d5phm.js";
11
+ import"../../index-f330qqyg.js";
11
12
  import"../../index-ahvsksw5.js";
12
13
  import"../../index-tgzhq07n.js";
13
14
  export {
@@ -5,7 +5,7 @@ import"../../index-8kzct1d6.js";
5
5
  import {
6
6
  composeAgentPrompt2,
7
7
  createAgentRuntime2
8
- } from "../../index-9jb61j5a.js";
8
+ } from "../../index-zp4jpkp0.js";
9
9
  import"../../index-h97tqqp1.js";
10
10
  import"../../index-mnkk1ffz.js";
11
11
  import"../../index-n53mswt3.js";
@@ -15,7 +15,7 @@ import"../../index-9pw0a51a.js";
15
15
  import"../../index-ct75c3at.js";
16
16
  import"../../index-x87j7csh.js";
17
17
  import"../../index-m2xqkv55.js";
18
- import"../../index-ary5s7k0.js";
18
+ import"../../index-69nvze1q.js";
19
19
  import"../../index-nggfa2yn.js";
20
20
  import"../../index-r5hsgkz5.js";
21
21
  import"../../index-n5yqxd3d.js";
@@ -9,11 +9,13 @@ import"../../index-x87j7csh.js";
9
9
  import"../../index-b2bz0kdm.js";
10
10
  import {
11
11
  createAgentCodingTools2
12
- } from "../../index-a4ajxwmb.js";
12
+ } from "../../index-806d5phm.js";
13
13
  import {
14
14
  NativeCommandError2,
15
+ spawnOwnedCommand,
16
+ nativeCommandOwner,
15
17
  startNativeCommand
16
- } from "../../index-6m6a9ty4.js";
18
+ } from "../../index-f330qqyg.js";
17
19
  import"../../index-tgzhq07n.js";
18
20
 
19
21
  // src/agent-runtime/sandbox-bubblewrap.ts
@@ -539,7 +541,6 @@ function spawnSandboxProcess(executable, args, limits, options = {}, onSpawn) {
539
541
  }
540
542
 
541
543
  // src/agent-runtime/sandbox-process-owner.ts
542
- import { spawn } from "node:child_process";
543
544
  function sandboxProcessOwner(assertActive, maximum) {
544
545
  const children = new Map;
545
546
  const admit = () => {
@@ -547,13 +548,38 @@ function sandboxProcessOwner(assertActive, maximum) {
547
548
  if (children.size >= maximum)
548
549
  throw new SandboxError("SANDBOX_BUSY", "Sandbox command concurrency limit reached");
549
550
  };
551
+ const terminate = (child) => {
552
+ const owner = nativeCommandOwner(child) ?? startNativeCommand({ executable: "host-owned-command", signal: new AbortController().signal }, undefined, { launch: () => child, group: process.platform !== "win32", force: true });
553
+ return owner.terminate();
554
+ };
550
555
  const track = (child) => {
551
- const closed = new Promise((resolve) => {
556
+ const settleDirect = () => {
557
+ if (!nativeCommandOwner(child))
558
+ terminate(child).catch(() => {
559
+ return;
560
+ });
561
+ };
562
+ child.once("exit", settleDirect);
563
+ child.once("error", settleDirect);
564
+ const closed = new Promise((resolve, reject) => {
552
565
  child.once("close", () => {
553
- children.delete(child);
554
- resolve();
566
+ const settled = nativeCommandOwner(child)?.result ?? Promise.resolve();
567
+ settled.then(() => {
568
+ children.delete(child);
569
+ resolve();
570
+ }, (error) => {
571
+ if (error instanceof NativeCommandError2 && error.code === "COMMAND_CLEANUP")
572
+ reject(error);
573
+ else {
574
+ children.delete(child);
575
+ resolve();
576
+ }
577
+ });
555
578
  });
556
579
  });
580
+ closed.catch(() => {
581
+ return;
582
+ });
557
583
  children.set(child, closed);
558
584
  };
559
585
  return {
@@ -564,11 +590,12 @@ function sandboxProcessOwner(assertActive, maximum) {
564
590
  },
565
591
  spawn(input) {
566
592
  admit();
567
- const child = spawn(input.executable, [...input.args], {
593
+ const child = spawnOwnedCommand({
594
+ executable: input.executable,
595
+ args: input.args,
568
596
  cwd: input.cwd,
569
597
  env: input.environment,
570
- detached: true,
571
- stdio: ["pipe", "pipe", "pipe"]
598
+ group: process.platform !== "win32"
572
599
  });
573
600
  child.stdin.end();
574
601
  track(child);
@@ -576,16 +603,7 @@ function sandboxProcessOwner(assertActive, maximum) {
576
603
  },
577
604
  async stop() {
578
605
  const pending = [...children];
579
- for (const [child] of pending) {
580
- if (child.pid === undefined)
581
- continue;
582
- try {
583
- process.kill(-child.pid, "SIGKILL");
584
- } catch (error) {
585
- if (!(error instanceof Error && ("code" in error) && error.code === "ESRCH"))
586
- throw error;
587
- }
588
- }
606
+ await Promise.all(pending.map(([child]) => terminate(child)));
589
607
  await Promise.all(pending.map(([, closed]) => closed));
590
608
  }
591
609
  };
@@ -110,7 +110,7 @@ import {
110
110
  agentGoalStateSlot2,
111
111
  agentTodoStateSlot2,
112
112
  createAgentRuntime2
113
- } from "../index-9jb61j5a.js";
113
+ } from "../index-zp4jpkp0.js";
114
114
  import {
115
115
  AgentConversationSummarySchema2,
116
116
  AgentConversationPageSchema2,
@@ -175,7 +175,7 @@ import {
175
175
  StepAbortedError2,
176
176
  ParkRecordDecodeError2,
177
177
  ParkAbortedError2
178
- } from "../index-ary5s7k0.js";
178
+ } from "../index-69nvze1q.js";
179
179
  import {
180
180
  AgentSandboxRestrictionSchema2,
181
181
  AgentSandboxGradeSchema2,
@@ -14,10 +14,10 @@ import {
14
14
  import {
15
15
  createBoundedChannel2
16
16
  } from "../../index-w0eqdhyd.js";
17
- import"../../index-nybn2err.js";
18
17
  import {
19
18
  attemptExclusiveLock
20
- } from "../../index-ga597cvy.js";
19
+ } from "../../index-xvdd2rbb.js";
20
+ import"../../index-nybn2err.js";
21
21
  import {
22
22
  isRecord
23
23
  } from "../../index-9jfdm5tg.js";
@@ -9,10 +9,10 @@ import {
9
9
  import {
10
10
  AppError2
11
11
  } from "../index-n53mswt3.js";
12
- import"../index-nybn2err.js";
13
12
  import {
14
13
  attemptExclusiveLock
15
- } from "../index-ga597cvy.js";
14
+ } from "../index-xvdd2rbb.js";
15
+ import"../index-nybn2err.js";
16
16
  import {
17
17
  writeFileAtomic2,
18
18
  writeFileAtomicSync2
@@ -315,6 +315,7 @@ async function withExclusiveLock(path, run, options = {}) {
315
315
  const attempt = await attemptExclusiveLock(path, {
316
316
  mode,
317
317
  reclaim: true,
318
+ signal,
318
319
  ownerlessGraceMs,
319
320
  ...options.machineIdentity !== undefined && {
320
321
  machineIdentity: options.machineIdentity
@@ -324,6 +325,9 @@ async function withExclusiveLock(path, run, options = {}) {
324
325
  const { held } = attempt;
325
326
  let result;
326
327
  try {
328
+ if (signal?.aborted) {
329
+ throw new ExclusiveLockError("LOCK_ABORTED", label, null, `[stitchkit] waiting for the lock on "${label}" was cancelled`, { cause: signal.reason });
330
+ }
327
331
  result = await run({ path: held.path, owner: held.owner, reclaimed: held.reclaimed });
328
332
  } catch (error) {
329
333
  await held.release().catch(() => {
@@ -334,6 +338,8 @@ async function withExclusiveLock(path, run, options = {}) {
334
338
  await held.release();
335
339
  return result;
336
340
  }
341
+ if (signal?.aborted)
342
+ continue;
337
343
  const remaining = deadline - performance.now();
338
344
  if (remaining <= 0) {
339
345
  const holder = attempt.diagnosis?.owner ?? null;
@@ -3,7 +3,7 @@ import {
3
3
  } from "../index-xqhede6x.js";
4
4
  import {
5
5
  isJsonData
6
- } from "../index-c1jmtxa0.js";
6
+ } from "../index-fm1c1a08.js";
7
7
  import {
8
8
  serializeCanonicalJson
9
9
  } from "../index-zsvzhjtk.js";
@@ -91,9 +91,10 @@ var CanonicalJsonOptionsSchema = z3.strictObject({
91
91
  });
92
92
  function canonicalJson(value, options = {}) {
93
93
  const limits = CanonicalJsonOptionsSchema.parse(options);
94
- if (!isJsonData(value, { ...limits, omitObjectUndefined: true }))
94
+ const snapshot = {};
95
+ if (!isJsonData(value, { ...limits, omitObjectUndefined: true }, snapshot))
95
96
  throw new TypeError("Expected bounded plain JSON: dense arrays, finite numbers, no -0, cycles or accessors");
96
- return serializeCanonicalJson(value);
97
+ return serializeCanonicalJson(snapshot.value);
97
98
  }
98
99
  // src/primitives/deadline.ts
99
100
  import { z as z4 } from "zod";
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  NativeCommandError2,
8
8
  startNativeCommand
9
- } from "../index-6m6a9ty4.js";
9
+ } from "../index-f330qqyg.js";
10
10
  // src/process/command.ts
11
11
  function runNativeCommand(input) {
12
12
  return startNativeCommand(input).result;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  EffectUnresolvedError2,
3
3
  createLocalStepDurability2
4
- } from "../index-ary5s7k0.js";
4
+ } from "../index-69nvze1q.js";
5
5
  import {
6
6
  AppError2,
7
7
  STITCH_ERROR_STATUS2
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isJsonData
3
- } from "./index-c1jmtxa0.js";
3
+ } from "./index-fm1c1a08.js";
4
4
  import {
5
5
  raceAbort
6
6
  } from "./index-mbnnw3f2.js";
@@ -103,7 +103,6 @@ import { z as z2 } from "zod";
103
103
  var DurableJsonSchema = z2.custom((value) => isJsonData(value, {
104
104
  maxDepth: 100,
105
105
  maxNodes: Number.MAX_SAFE_INTEGER,
106
- maxBytes: Number.MAX_SAFE_INTEGER,
107
106
  omitObjectUndefined: false
108
107
  }), "Expected lossless JSON data (finite numbers, plain objects and dense arrays; no accessors)");
109
108
 
@@ -2,6 +2,12 @@ import {
2
2
  forbidden2,
3
3
  conflict2
4
4
  } from "./index-n53mswt3.js";
5
+ import {
6
+ NativeCommandError2,
7
+ stopCommandGroup,
8
+ ownsCommandGroup,
9
+ startNativeCommand
10
+ } from "./index-f330qqyg.js";
5
11
  import {
6
12
  probeAgentProcessSandbox2,
7
13
  missingSandboxRestrictions2,
@@ -981,7 +987,6 @@ function createSearchAndPatchCodingTools(config, limits) {
981
987
  import { realpath as realpath5, stat } from "node:fs/promises";
982
988
 
983
989
  // src/agent-runtime/coding-shell-process.ts
984
- import { spawn } from "node:child_process";
985
990
  function preview(data, budget) {
986
991
  if (data.byteLength <= budget) {
987
992
  return { data, headBytes: data.byteLength, tailBytes: 0, omittedBytes: 0 };
@@ -1024,140 +1029,118 @@ async function runCodingShell(input) {
1024
1029
  outcome: "cancelled"
1025
1030
  });
1026
1031
  }
1027
- return await new Promise((resolve, reject) => {
1028
- const ownsProcessGroup = process.platform !== "win32";
1029
- const child = input.spawn ? input.spawn() : spawn(input.executable, input.args, {
1030
- cwd: input.cwd,
1031
- env: input.environment,
1032
- stdio: ["ignore", "pipe", "pipe"],
1033
- detached: ownsProcessGroup
1034
- });
1035
- const stdout = [];
1036
- const stderr = [];
1037
- const artifactStdout = [];
1038
- const artifactStderr = [];
1039
- let retained = 0;
1040
- let artifactBytes = 0;
1041
- let outcome = "exited";
1042
- let settled = false;
1043
- let settlementTimer;
1044
- const cancel = () => terminate("cancelled");
1045
- const cleanup = () => {
1046
- clearTimeout(timer);
1047
- if (settlementTimer)
1048
- clearTimeout(settlementTimer);
1049
- input.signal?.removeEventListener("abort", cancel);
1050
- };
1051
- const persistAndResolve = async (exitCode, signal) => {
1052
- if (settled)
1053
- return;
1054
- settled = true;
1055
- cleanup();
1056
- try {
1057
- const hasArtifact = input.artifacts && artifactBytes > input.maxOutputBytes;
1058
- const completeStdout = Buffer.concat(artifactStdout);
1059
- const completeStderr = Buffer.concat(artifactStderr);
1060
- const artifactData = hasArtifact ? Buffer.concat([stdoutHeader, completeStdout, stderrHeader, completeStderr]) : undefined;
1061
- const stdoutBudget = completeStderr.byteLength === 0 ? input.maxOutputBytes : Math.ceil(input.maxOutputBytes / 2);
1062
- const stderrBudget = completeStdout.byteLength === 0 ? input.maxOutputBytes : Math.floor(input.maxOutputBytes / 2);
1063
- const stdoutPreview = hasArtifact ? preview(completeStdout, stdoutBudget) : retainedPreview(Buffer.concat(stdout));
1064
- const stderrPreview = hasArtifact ? preview(completeStderr, stderrBudget) : retainedPreview(Buffer.concat(stderr));
1065
- const persisted = hasArtifact ? await input.artifacts?.write({
1066
- mediaType: "text/plain; charset=utf-8",
1067
- data: artifactData ?? new Uint8Array,
1068
- ...input.authorization && { authorization: input.authorization }
1069
- }) : undefined;
1070
- resolve(ShellOutputSchema.parse({
1071
- executable: input.executableName,
1072
- exitCode,
1073
- signal,
1074
- stdout: stdoutPreview.data.toString("utf8"),
1075
- stderr: stderrPreview.data.toString("utf8"),
1076
- outcome,
1077
- ...persisted && {
1078
- artifact: {
1079
- reference: persisted.reference,
1080
- bytes: artifactData?.byteLength ?? 0,
1081
- truncated: outcome === "output-limit",
1082
- headBytes: stdoutPreview.headBytes + stderrPreview.headBytes,
1083
- tailBytes: stdoutPreview.tailBytes + stderrPreview.tailBytes,
1084
- omittedBytes: stdoutPreview.omittedBytes + stderrPreview.omittedBytes
1085
- }
1086
- }
1087
- }));
1088
- } catch (error) {
1089
- reject(error);
1090
- }
1091
- };
1092
- const killOwnedProcessGroup = () => {
1093
- if (!ownsProcessGroup || child.pid === undefined)
1094
- return false;
1095
- try {
1096
- process.kill(-child.pid, "SIGKILL");
1097
- return true;
1098
- } catch {
1099
- return false;
1032
+ const stdout = [];
1033
+ const stderr = [];
1034
+ const artifactStdout = [];
1035
+ const artifactStderr = [];
1036
+ let retained = 0;
1037
+ let artifactBytes = 0;
1038
+ let outcome = "exited";
1039
+ let child;
1040
+ let exitCode = null;
1041
+ let signal = null;
1042
+ const outputLimit = new Error("Coding command output retention budget exceeded");
1043
+ const launch = input.spawn;
1044
+ const retain = (target, artifactTarget, chunk) => {
1045
+ const remaining = Math.max(0, input.maxOutputBytes - retained);
1046
+ if (remaining > 0) {
1047
+ const kept = Buffer.from(chunk.subarray(0, remaining));
1048
+ target.push(kept);
1049
+ retained += kept.byteLength;
1050
+ }
1051
+ if (input.artifacts) {
1052
+ const artifactRemaining = Math.max(0, artifactPayloadLimit - artifactBytes);
1053
+ if (artifactRemaining > 0) {
1054
+ const kept = Buffer.from(chunk.subarray(0, artifactRemaining));
1055
+ artifactTarget.push(kept);
1056
+ artifactBytes += kept.byteLength;
1100
1057
  }
1101
- };
1102
- const boundStreamSettlement = () => {
1103
- if (settlementTimer)
1104
- return;
1105
- settlementTimer = setTimeout(() => {
1106
- child.stdout.destroy();
1107
- child.stderr.destroy();
1108
- persistAndResolve(child.exitCode, child.signalCode);
1109
- }, input.terminationGraceMs);
1110
- };
1111
- const terminate = (reason) => {
1112
- if (settled || outcome !== "exited")
1058
+ if (chunk.byteLength > artifactRemaining)
1059
+ throw outputLimit;
1060
+ } else if (chunk.byteLength > remaining)
1061
+ throw outputLimit;
1062
+ };
1063
+ const command = startNativeCommand({
1064
+ executable: input.executable,
1065
+ args: [...input.args],
1066
+ cwd: input.cwd,
1067
+ env: { ...input.environment },
1068
+ envPolicy: "declared-only",
1069
+ signal: input.signal ?? new AbortController().signal,
1070
+ killGraceMs: 0,
1071
+ onOutput: (bytes, channel) => retain(channel === "stdout" ? stdout : stderr, channel === "stdout" ? artifactStdout : artifactStderr, bytes),
1072
+ onLeaderSettled: async (event) => {
1073
+ if (event.kind !== "exit")
1113
1074
  return;
1114
- outcome = reason;
1115
- if (!killOwnedProcessGroup())
1116
- child.kill("SIGKILL");
1117
- boundStreamSettlement();
1118
- };
1119
- const timer = setTimeout(() => terminate("timeout"), input.timeoutMs);
1120
- timer.unref();
1121
- const retain = (target, artifactTarget, chunk) => {
1122
- const remaining = Math.max(0, input.maxOutputBytes - retained);
1123
- if (remaining > 0) {
1124
- const kept = chunk.subarray(0, remaining);
1125
- target.push(kept);
1126
- retained += kept.byteLength;
1075
+ exitCode = event.exitCode;
1076
+ signal = event.signal;
1077
+ if (process.platform !== "win32" && child && ownsCommandGroup(child))
1078
+ await stopCommandGroup(child?.pid, 0, input.terminationGraceMs, true);
1079
+ }
1080
+ }, (started) => {
1081
+ child = started;
1082
+ }, {
1083
+ group: process.platform !== "win32",
1084
+ force: true,
1085
+ timeoutMs: input.timeoutMs,
1086
+ cleanupTimeoutMs: input.terminationGraceMs,
1087
+ closeTimeoutMs: input.terminationGraceMs,
1088
+ ...launch ? {
1089
+ launch: () => {
1090
+ child = launch();
1091
+ return child;
1127
1092
  }
1128
- if (input.artifacts) {
1129
- const artifactRemaining = Math.max(0, artifactPayloadLimit - artifactBytes);
1130
- if (artifactRemaining > 0) {
1131
- const kept = chunk.subarray(0, artifactRemaining);
1132
- artifactTarget.push(kept);
1133
- artifactBytes += kept.byteLength;
1134
- }
1135
- if (chunk.byteLength > artifactRemaining)
1136
- terminate("output-limit");
1137
- } else if (chunk.byteLength > remaining)
1138
- terminate("output-limit");
1139
- };
1140
- child.stdout.on("data", (chunk) => retain(stdout, artifactStdout, Buffer.from(chunk)));
1141
- child.stderr.on("data", (chunk) => retain(stderr, artifactStderr, Buffer.from(chunk)));
1142
- child.on("error", (error) => {
1143
- if (settled)
1144
- return;
1145
- settled = true;
1146
- cleanup();
1147
- reject(error);
1148
- });
1149
- child.on("exit", (exitCode, signal) => {
1150
- if (outcome === "exited")
1151
- killOwnedProcessGroup();
1152
- boundStreamSettlement();
1153
- if (child.stdout.destroyed && child.stderr.destroyed) {
1154
- persistAndResolve(exitCode, signal);
1093
+ } : {},
1094
+ maxBufferedBytes: Math.max(64 * 1024, input.maxOutputBytes, input.artifacts ? artifactPayloadLimit : 0)
1095
+ });
1096
+ try {
1097
+ const result = await command.result;
1098
+ exitCode = result.exitCode;
1099
+ signal = result.signal;
1100
+ } catch (error) {
1101
+ if (error === outputLimit)
1102
+ outcome = "output-limit";
1103
+ else if (input.signal?.aborted && error === input.signal.reason)
1104
+ outcome = "cancelled";
1105
+ else if (error instanceof NativeCommandError2 && error.code === "COMMAND_LIMIT" && (error.reason === "deadline" || error.reason === "output-budget"))
1106
+ outcome = error.reason === "deadline" ? "timeout" : "output-limit";
1107
+ else if (error instanceof DOMException && error.name === "AbortError" && error.message === "Command stopped")
1108
+ outcome = "cancelled";
1109
+ else
1110
+ throw error;
1111
+ exitCode = child?.exitCode ?? exitCode;
1112
+ signal = child?.signalCode ?? signal;
1113
+ }
1114
+ const hasArtifact = input.artifacts && artifactBytes > input.maxOutputBytes;
1115
+ const completeStdout = Buffer.concat(artifactStdout);
1116
+ const completeStderr = Buffer.concat(artifactStderr);
1117
+ const artifactData = hasArtifact ? Buffer.concat([stdoutHeader, completeStdout, stderrHeader, completeStderr]) : undefined;
1118
+ const stdoutBudget = completeStderr.byteLength === 0 ? input.maxOutputBytes : Math.ceil(input.maxOutputBytes / 2);
1119
+ const stderrBudget = completeStdout.byteLength === 0 ? input.maxOutputBytes : Math.floor(input.maxOutputBytes / 2);
1120
+ const stdoutPreview = hasArtifact ? preview(completeStdout, stdoutBudget) : retainedPreview(Buffer.concat(stdout));
1121
+ const stderrPreview = hasArtifact ? preview(completeStderr, stderrBudget) : retainedPreview(Buffer.concat(stderr));
1122
+ const persisted = hasArtifact ? await input.artifacts?.write({
1123
+ mediaType: "text/plain; charset=utf-8",
1124
+ data: artifactData ?? new Uint8Array,
1125
+ ...input.authorization && { authorization: input.authorization }
1126
+ }) : undefined;
1127
+ return ShellOutputSchema.parse({
1128
+ executable: input.executableName,
1129
+ exitCode,
1130
+ signal,
1131
+ stdout: stdoutPreview.data.toString("utf8"),
1132
+ stderr: stderrPreview.data.toString("utf8"),
1133
+ outcome,
1134
+ ...persisted && {
1135
+ artifact: {
1136
+ reference: persisted.reference,
1137
+ bytes: artifactData?.byteLength ?? 0,
1138
+ truncated: outcome === "output-limit",
1139
+ headBytes: stdoutPreview.headBytes + stderrPreview.headBytes,
1140
+ tailBytes: stdoutPreview.tailBytes + stderrPreview.tailBytes,
1141
+ omittedBytes: stdoutPreview.omittedBytes + stderrPreview.omittedBytes
1155
1142
  }
1156
- });
1157
- child.on("close", (exitCode, signal) => void persistAndResolve(exitCode, signal));
1158
- input.signal?.addEventListener("abort", cancel, { once: true });
1159
- if (input.signal?.aborted)
1160
- cancel();
1143
+ }
1161
1144
  });
1162
1145
  }
1163
1146