stitchkit 0.111.0 → 0.111.2

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 (38) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/application/grammy-update-admission.d.ts.map +1 -1
  3. package/dist/entrypoints/agent-runtime/sandbox.js +1 -1
  4. package/dist/entrypoints/application/grammy.js +11 -2
  5. package/dist/entrypoints/cli/publish.js +1 -1
  6. package/dist/entrypoints/cli.js +2 -2
  7. package/dist/entrypoints/http.js +2 -2
  8. package/dist/entrypoints/index.js +2 -2
  9. package/dist/entrypoints/node.js +1 -1
  10. package/dist/entrypoints/process.d.ts +1 -0
  11. package/dist/entrypoints/process.d.ts.map +1 -1
  12. package/dist/entrypoints/process.js +76 -1
  13. package/dist/entrypoints/remote.js +1 -1
  14. package/dist/entrypoints/server.js +2 -2
  15. package/dist/entrypoints/telegram.js +3 -3
  16. package/dist/entrypoints/testing.js +1 -1
  17. package/dist/entrypoints/tools/connections.js +1 -1
  18. package/dist/entrypoints/tools/mcp.js +1 -1
  19. package/dist/entrypoints/tools.js +4 -4
  20. package/dist/entrypoints/voice.js +17 -10
  21. package/dist/{index-krng2a5j.js → index-2qymcsq2.js} +6 -15
  22. package/dist/{index-s2eybn7b.js → index-c5374vpt.js} +10 -9
  23. package/dist/{index-t65erk4z.js → index-cxpcdswt.js} +1 -1
  24. package/dist/{index-kdnfe0sr.js → index-erv5shjq.js} +1 -1
  25. package/dist/{index-syzngt7v.js → index-v2eyj2ws.js} +1 -1
  26. package/dist/process/stream.d.ts +27 -0
  27. package/dist/process/stream.d.ts.map +1 -0
  28. package/dist/server/contract-stream.d.ts.map +1 -1
  29. package/dist/tools/mcp/handler.d.ts.map +1 -1
  30. package/dist/voice/speech-queue.d.ts +6 -1
  31. package/dist/voice/speech-queue.d.ts.map +1 -1
  32. package/llms/process.2.txt +32 -0
  33. package/llms/process.txt +26 -23
  34. package/llms-full.txt +24 -0
  35. package/llms.txt +1 -1
  36. package/package.json +1 -1
  37. package/dist/{index-qndsr7f0.js → index-5efrpfmj.js} +3 -3
  38. /package/dist/{index-1y0arq4z.js → index-c5at3ykd.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -15,6 +15,25 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
15
15
 
16
16
  ## [Unreleased]
17
17
 
18
+ ## [0.111.2] - 2026-10-17
19
+
20
+ ### Added
21
+
22
+ - `stitchkit/process`: `streamNativeCommand` starts a command like `runNativeCommand` and returns its stdout and stderr as `ReadableStream<Uint8Array>` while it runs, with `result` and `stop()`. It uses the same owner, so a stream keeps the group ownership, the `stop` policy, the bounds and every group-stop fix of `runNativeCommand`; a slow reader slows the child through pipe backpressure. The `stitchkit/process` surface ceiling rises from 12 to 15 for `streamNativeCommand` and its two types.
23
+
24
+ ## [0.111.1] - 2026-10-17
25
+
26
+ ### Fixed
27
+
28
+ - `stitchkit/server` — **an open contract stream keeps a constant number of objects however many frames it sends.** Each frame raced the source against a promise that lived as long as the stream, so every frame left a reaction and the frame itself retained until the stream ended; a subscription sending a frame a second grew by about 1.6 MB a minute. The stream now waits for each frame through a listener it removes itself. A source that never has to wait is also stopped at the stream's `lifetimeMs`, which it used to outlive. → ADR 0264
29
+ **Affects:** `stitchkit/server` createServer, createHandler, implement
30
+ - `stitchkit/voice` — **`SpeechQueue` no longer keeps a reaction and the spoken clip for every sentence it has said.** → ADR 0264
31
+ **Affects:** `stitchkit/voice` SpeechQueue
32
+ - `stitchkit/tools/mcp` — **`createMcpHandler` no longer keeps the authenticated principal of every request it has answered until it is closed.** → ADR 0264
33
+ **Affects:** `stitchkit/tools/mcp` createMcpHandler
34
+ - `stitchkit/application/grammy` — **the update gate follows grammY's polling signal once instead of adding a listener on every `getUpdates`.** → ADR 0264
35
+ **Affects:** `stitchkit/application/grammy` behaviour
36
+
18
37
  ## [0.111.0] - 2026-10-17
19
38
 
20
39
  ### ⚠️ Breaking changes
@@ -1 +1 @@
1
- {"version":3,"file":"grammy-update-admission.d.ts","sourceRoot":"","sources":["../../src/application/grammy-update-admission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAE3C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,mBAAmB;IAClC,gGAAgG;IAChG,IAAI,CAAC,SAAS,EAAE,wBAAwB,GAAG,SAAS,GAAG,IAAI,CAAC;IAC5D,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,6EAA6E;IAC7E,OAAO,IAAI,IAAI,CAAC;CACjB;AAsDD,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAmDvF"}
1
+ {"version":3,"file":"grammy-update-admission.d.ts","sourceRoot":"","sources":["../../src/application/grammy-update-admission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAE3C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,mBAAmB;IAClC,gGAAgG;IAChG,IAAI,CAAC,SAAS,EAAE,wBAAwB,GAAG,SAAS,GAAG,IAAI,CAAC;IAC5D,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,6EAA6E;IAC7E,OAAO,IAAI,IAAI,CAAC;CACjB;AAiED,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAmDvF"}
@@ -21,7 +21,7 @@ import {
21
21
  import"../../index-zrtrjkbf.js";
22
22
  import {
23
23
  readBoundedBody2
24
- } from "../../index-qndsr7f0.js";
24
+ } from "../../index-5efrpfmj.js";
25
25
  import {
26
26
  MAX_TIMER_MS
27
27
  } from "../../index-eemvahkc.js";
@@ -15,7 +15,7 @@ function batchSize(result) {
15
15
  function abortError() {
16
16
  return new DOMException("Telegram polling stopped", "AbortError");
17
17
  }
18
- function untilAborted(signal) {
18
+ function untilNoLongerAdmitted(signal) {
19
19
  return new Promise((_, reject) => {
20
20
  if (signal.aborted)
21
21
  reject(abortError());
@@ -29,10 +29,19 @@ async function admitted(admission, signal) {
29
29
  } catch {
30
30
  if (signal.aborted)
31
31
  throw abortError();
32
- return untilAborted(signal);
32
+ return untilNoLongerAdmitted(signal);
33
33
  }
34
34
  }
35
+ var followers = new WeakMap;
35
36
  function followed(signal) {
37
+ const known = followers.get(signal);
38
+ if (known)
39
+ return known;
40
+ const follower = follow(signal);
41
+ followers.set(signal, follower);
42
+ return follower;
43
+ }
44
+ function follow(signal) {
36
45
  const controller = new AbortController;
37
46
  if (signal.aborted)
38
47
  controller.abort(abortError());
@@ -15,7 +15,7 @@ import {
15
15
  gzipCliAsset,
16
16
  decodeCliAsset,
17
17
  compareCliVersions2
18
- } from "../../index-kdnfe0sr.js";
18
+ } from "../../index-erv5shjq.js";
19
19
  import {
20
20
  withExclusiveLock2
21
21
  } from "../../index-w9mqgnz9.js";
@@ -16,7 +16,7 @@ import {
16
16
  renderCliView2,
17
17
  pollUntilDone2,
18
18
  createCli2
19
- } from "../index-syzngt7v.js";
19
+ } from "../index-v2eyj2ws.js";
20
20
  import"../index-fynr93w4.js";
21
21
  import"../index-ct75c3at.js";
22
22
  import"../index-q6crhe1n.js";
@@ -40,7 +40,7 @@ import {
40
40
  checkCliUpdate2,
41
41
  applyCliUpdate2,
42
42
  rollbackCliUpdate2
43
- } from "../index-kdnfe0sr.js";
43
+ } from "../index-erv5shjq.js";
44
44
  import {
45
45
  writeFileAtomicSync2
46
46
  } from "../index-07dcf2yn.js";
@@ -14,13 +14,13 @@ import {
14
14
  createUrlBuilder2,
15
15
  createUrlBuilders2,
16
16
  createScopedUrlBuilders2
17
- } from "../index-1y0arq4z.js";
17
+ } from "../index-c5at3ykd.js";
18
18
  import {
19
19
  ApiError2
20
20
  } from "../index-kfjkb396.js";
21
21
  import {
22
22
  readBoundedBody2
23
- } from "../index-qndsr7f0.js";
23
+ } from "../index-5efrpfmj.js";
24
24
  export {
25
25
  ApiError2 as ApiError,
26
26
  contractEndpointMatchers2 as contractEndpointMatchers,
@@ -71,7 +71,7 @@ import {
71
71
  createUrlBuilder2,
72
72
  createUrlBuilders2,
73
73
  createScopedUrlBuilders2
74
- } from "../index-1y0arq4z.js";
74
+ } from "../index-c5at3ykd.js";
75
75
  import {
76
76
  createRetainedTopics2,
77
77
  createRealtimeClientWithPeer,
@@ -85,7 +85,7 @@ import {
85
85
  } from "../index-kfjkb396.js";
86
86
  import {
87
87
  readBoundedBody2
88
- } from "../index-qndsr7f0.js";
88
+ } from "../index-5efrpfmj.js";
89
89
  import {
90
90
  sleep
91
91
  } from "../index-eemvahkc.js";
@@ -23,7 +23,7 @@ import {
23
23
  createSocketIOServer2,
24
24
  UnixClientTransportError2,
25
25
  createUnixClientTransport2
26
- } from "../index-krng2a5j.js";
26
+ } from "../index-2qymcsq2.js";
27
27
  import {
28
28
  bindRealtimeServer2
29
29
  } from "../index-xmgjtkvr.js";
@@ -2,4 +2,5 @@
2
2
  export { observeProcessInstance, type ProcessInstance, type ProcessInstanceObservation, ProcessInstanceSchema, type ProcessOwnerEvidence, probeProcessOwner, } from '../internal/process-instance.js';
3
3
  export { runNativeCommand } from '../process/command.js';
4
4
  export { NativeCommandError, type NativeCommandOptions, type NativeCommandResult, type NativeCommandSettlement, type NativeCommandStopPolicy, } from '../process/contract.js';
5
+ export { type NativeCommandStream, type NativeCommandStreamOptions, streamNativeCommand, } from '../process/stream.js';
5
6
  //# sourceMappingURL=process.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/entrypoints/process.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAE/E,OAAO,EACL,sBAAsB,EACtB,KAAK,eAAe,EACpB,KAAK,0BAA0B,EAC/B,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,iBAAiB,GAClB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EACL,kBAAkB,EAClB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,GAC7B,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/entrypoints/process.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAE/E,OAAO,EACL,sBAAsB,EACtB,KAAK,eAAe,EACpB,KAAK,0BAA0B,EAC/B,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,iBAAiB,GAClB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EACL,kBAAkB,EAClB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,GAC7B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,EAC/B,mBAAmB,GACpB,MAAM,mBAAmB,CAAC"}
@@ -10,14 +10,89 @@ import {
10
10
  import {
11
11
  startNativeCommand
12
12
  } from "../index-7r4knwf8.js";
13
+ import {
14
+ untilAborted
15
+ } from "../index-a5qr2rj6.js";
13
16
  // src/process/command.ts
14
17
  function runNativeCommand(input) {
15
18
  return startNativeCommand(input).result;
16
19
  }
20
+ // src/process/stream.ts
21
+ function streamNativeCommand(input) {
22
+ for (const owned of ["capture", "onOutput", "stdio"])
23
+ if (owned in input)
24
+ throw new TypeError(`streamNativeCommand owns ${owned}`);
25
+ const channels = { stdout: channel(), stderr: channel() };
26
+ const owner = startNativeCommand({
27
+ ...input,
28
+ onOutput: async (bytes, name, signal) => {
29
+ await channels[name].push(Uint8Array.from(bytes), signal);
30
+ }
31
+ });
32
+ owner.result.then(() => {
33
+ channels.stdout.close();
34
+ channels.stderr.close();
35
+ }, (error) => {
36
+ channels.stdout.fail(error);
37
+ channels.stderr.fail(error);
38
+ });
39
+ return {
40
+ stdout: channels.stdout.stream,
41
+ stderr: channels.stderr.stream,
42
+ result: owner.result,
43
+ stop: () => owner.stop()
44
+ };
45
+ }
46
+ var QUEUED_BYTES = 1024 * 1024;
47
+ function channel() {
48
+ let controller;
49
+ let cancelled = false;
50
+ let settled = false;
51
+ let resume = Promise.withResolvers();
52
+ const stream = new ReadableStream({
53
+ start(c) {
54
+ controller = c;
55
+ },
56
+ pull() {
57
+ resume.resolve();
58
+ },
59
+ cancel() {
60
+ cancelled = true;
61
+ resume.resolve();
62
+ }
63
+ }, new ByteLengthQueuingStrategy({ highWaterMark: QUEUED_BYTES }));
64
+ return {
65
+ stream,
66
+ async push(bytes, signal) {
67
+ if (cancelled || settled)
68
+ return;
69
+ controller.enqueue(bytes);
70
+ while (!cancelled && !signal.aborted && (controller.desiredSize ?? 0) <= 0) {
71
+ resume = Promise.withResolvers();
72
+ await untilAborted(resume.promise, signal).catch(() => {
73
+ return;
74
+ });
75
+ }
76
+ },
77
+ close() {
78
+ if (cancelled || settled)
79
+ return;
80
+ settled = true;
81
+ controller.close();
82
+ },
83
+ fail(error) {
84
+ if (cancelled || settled)
85
+ return;
86
+ settled = true;
87
+ controller.error(error);
88
+ }
89
+ };
90
+ }
17
91
  export {
18
92
  NativeCommandError2 as NativeCommandError,
19
93
  ProcessInstanceSchema2 as ProcessInstanceSchema,
20
94
  observeProcessInstance2 as observeProcessInstance,
21
95
  probeProcessOwner2 as probeProcessOwner,
22
- runNativeCommand
96
+ runNativeCommand,
97
+ streamNativeCommand
23
98
  };
@@ -4,7 +4,7 @@ import"../index-p6apg0jg.js";
4
4
  import {
5
5
  endpointHasArguments,
6
6
  createClient2
7
- } from "../index-1y0arq4z.js";
7
+ } from "../index-c5at3ykd.js";
8
8
  import {
9
9
  ApiError2
10
10
  } from "../index-kfjkb396.js";
@@ -49,7 +49,7 @@ import {
49
49
  socketIoLane2,
50
50
  UnixClientTransportError2,
51
51
  createUnixClientTransport2
52
- } from "../index-krng2a5j.js";
52
+ } from "../index-2qymcsq2.js";
53
53
  import {
54
54
  createFileStateStore2
55
55
  } from "../index-nx49x0tw.js";
@@ -86,7 +86,7 @@ import"../index-1bx1xv1k.js";
86
86
  import {
87
87
  readBoundedChunks,
88
88
  concatChunks
89
- } from "../index-qndsr7f0.js";
89
+ } from "../index-5efrpfmj.js";
90
90
  import {
91
91
  assertPositiveSafeInteger
92
92
  } from "../index-0s4xebbg.js";
@@ -26,15 +26,15 @@ import {
26
26
  MAX_TIMER_MS,
27
27
  sleep
28
28
  } from "../index-eemvahkc.js";
29
- import {
30
- untilAborted
31
- } from "../index-a5qr2rj6.js";
32
29
  import {
33
30
  assertPositiveSafeInteger
34
31
  } from "../index-0s4xebbg.js";
35
32
  import {
36
33
  BOT_TOKEN_SECRET
37
34
  } from "../index-sznqf8fx.js";
35
+ import {
36
+ untilAborted
37
+ } from "../index-a5qr2rj6.js";
38
38
  import {
39
39
  isRecord,
40
40
  transportResult
@@ -35,7 +35,7 @@ import"../index-fynr93w4.js";
35
35
  import {
36
36
  createClient2,
37
37
  createClients2
38
- } from "../index-1y0arq4z.js";
38
+ } from "../index-c5at3ykd.js";
39
39
  import {
40
40
  assertPositiveSafeInteger
41
41
  } from "../index-0s4xebbg.js";
@@ -16,7 +16,7 @@ import {
16
16
  import {
17
17
  readBoundedChunks,
18
18
  concatChunks
19
- } from "../../index-qndsr7f0.js";
19
+ } from "../../index-5efrpfmj.js";
20
20
  import {
21
21
  MAX_TIMER_MS
22
22
  } from "../../index-eemvahkc.js";
@@ -20,7 +20,7 @@ import {
20
20
  createMcpHttpRoute2,
21
21
  createStdioMcpServer2,
22
22
  bindStdioProcessSignals2
23
- } from "../../index-s2eybn7b.js";
23
+ } from "../../index-c5374vpt.js";
24
24
  import"../../index-0d8m0kee.js";
25
25
  import"../../index-636mggad.js";
26
26
  export {
@@ -60,7 +60,7 @@ import {
60
60
  createMcpHttpRoute2,
61
61
  createStdioMcpServer2,
62
62
  bindStdioProcessSignals2
63
- } from "../index-s2eybn7b.js";
63
+ } from "../index-c5374vpt.js";
64
64
  import {
65
65
  AsyncOperationCancelResultSchema2,
66
66
  createAsyncOperationSnapshotSchema2,
@@ -74,7 +74,7 @@ import {
74
74
  WaitTimeoutError,
75
75
  runWaitOperation,
76
76
  createCli2
77
- } from "../index-syzngt7v.js";
77
+ } from "../index-v2eyj2ws.js";
78
78
  import {
79
79
  ManagedFilePathSchema2,
80
80
  ManagedFileRefSchema2
@@ -97,12 +97,12 @@ import {
97
97
  fetchGuarded,
98
98
  fetchPinnedDocument,
99
99
  readCapped
100
- } from "../index-t65erk4z.js";
100
+ } from "../index-cxpcdswt.js";
101
101
  import"../index-1bx1xv1k.js";
102
102
  import {
103
103
  readBoundedChunks,
104
104
  concatChunks
105
- } from "../index-qndsr7f0.js";
105
+ } from "../index-5efrpfmj.js";
106
106
  import {
107
107
  assertPositiveSafeInteger
108
108
  } from "../index-0s4xebbg.js";
@@ -1,3 +1,7 @@
1
+ import {
2
+ untilAborted
3
+ } from "../index-a5qr2rj6.js";
4
+
1
5
  // src/voice/phases.ts
2
6
  var LIVE_VOICE_PHASES = [
3
7
  "opening",
@@ -99,10 +103,7 @@ class SpeechQueue {
99
103
  closed = false;
100
104
  cancelled = false;
101
105
  wake = null;
102
- stop;
103
- stopped = new Promise((resolve) => {
104
- this.stop = resolve;
105
- });
106
+ stopped = new AbortController;
106
107
  constructor(options) {
107
108
  this.options = options;
108
109
  const lookahead = options.lookahead ?? 1;
@@ -146,9 +147,16 @@ class SpeechQueue {
146
147
  this.release(outcome.value);
147
148
  });
148
149
  }
149
- this.stop(CANCELLED);
150
+ this.stopped.abort();
150
151
  this.notify();
151
152
  }
153
+ async unlessStopped(pending) {
154
+ try {
155
+ return await untilAborted(pending, this.stopped.signal);
156
+ } catch {
157
+ return CANCELLED;
158
+ }
159
+ }
152
160
  notify() {
153
161
  this.wake?.();
154
162
  }
@@ -179,19 +187,18 @@ class SpeechQueue {
179
187
  }
180
188
  this.current = slot;
181
189
  try {
182
- const prepared = await Promise.race([slot.result, this.stopped]);
190
+ const prepared = await this.unlessStopped(slot.result);
183
191
  if (prepared === CANCELLED)
184
192
  return;
185
193
  if (!prepared.ok) {
186
194
  this.report(prepared.error, slot.text);
187
195
  continue;
188
196
  }
197
+ if (this.cancelled)
198
+ return;
189
199
  this.playing = true;
190
200
  this.fill();
191
- const played = await Promise.race([
192
- settle(() => this.options.play(prepared.value, slot.controller.signal)),
193
- this.stopped
194
- ]);
201
+ const played = await this.unlessStopped(settle(() => this.options.play(prepared.value, slot.controller.signal)));
195
202
  if (played === CANCELLED)
196
203
  return;
197
204
  if (!played.ok)
@@ -31,10 +31,7 @@ import {
31
31
  readBoundedChunks,
32
32
  concatChunks,
33
33
  readBoundedBody2
34
- } from "./index-qndsr7f0.js";
35
- import {
36
- abortReason
37
- } from "./index-a5qr2rj6.js";
34
+ } from "./index-5efrpfmj.js";
38
35
  import {
39
36
  assertPositiveSafeInteger
40
37
  } from "./index-0s4xebbg.js";
@@ -62,6 +59,10 @@ import {
62
59
  getClientInfo2,
63
60
  parseQueryParams
64
61
  } from "./index-kw1ws0cs.js";
62
+ import {
63
+ abortReason,
64
+ untilAborted
65
+ } from "./index-a5qr2rj6.js";
65
66
  import {
66
67
  isUnsafeKey,
67
68
  safeJsonParse
@@ -1001,15 +1002,6 @@ function encodedBytes(value, format) {
1001
1002
  `;
1002
1003
  return new TextEncoder().encode(framing).byteLength;
1003
1004
  }
1004
- function waitForAbort(signal) {
1005
- return new Promise((_resolve, reject) => {
1006
- const rejectAborted = () => reject(signal.reason ?? new Error("Stream aborted"));
1007
- if (signal.aborted)
1008
- rejectAborted();
1009
- else
1010
- signal.addEventListener("abort", rejectAborted, { once: true });
1011
- });
1012
- }
1013
1005
  function contractStreamResponse(request, context, source, descriptor, operationAbort) {
1014
1006
  const format = descriptor.format ?? "ndjson";
1015
1007
  const maxFrameBytes = descriptor.maxFrameBytes ?? DEFAULT_CONTRACT_STREAM_FRAME_BYTES2;
@@ -1048,12 +1040,11 @@ function contractStreamResponse(request, context, source, descriptor, operationA
1048
1040
  const frames = async function* () {
1049
1041
  if (closing)
1050
1042
  return;
1051
- const aborted = waitForAbort(operationAbort.signal);
1052
1043
  let terminalSeen = descriptor.terminal === undefined;
1053
1044
  try {
1054
1045
  for (;; ) {
1055
1046
  pendingNext = Promise.resolve(iterator.next());
1056
- const next = await Promise.race([pendingNext, aborted]);
1047
+ const next = await untilAborted(pendingNext, operationAbort.signal);
1057
1048
  if (next.done)
1058
1049
  break;
1059
1050
  const parsed = descriptor.item.safeParse(next.value);
@@ -36,6 +36,9 @@ import {
36
36
  runWithRequestContext2,
37
37
  getRequestContext2
38
38
  } from "./index-r6v4mc67.js";
39
+ import {
40
+ untilAborted
41
+ } from "./index-a5qr2rj6.js";
39
42
  import {
40
43
  AppError2
41
44
  } from "./index-j9gxbztg.js";
@@ -971,10 +974,7 @@ function createMcpHandler2(config) {
971
974
  const buildPrepared = prepareStaticSurface(config);
972
975
  const authByCarrier = new WeakMap;
973
976
  let closed = false;
974
- let resolveClosed;
975
- const closedSignal = new Promise((resolve) => {
976
- resolveClosed = resolve;
977
- });
977
+ const closing = new AbortController;
978
978
  const sdkHandler = createSdkMcpHandler(({ authInfo }) => {
979
979
  const resolved = authInfo ? authByCarrier.get(authInfo) : undefined;
980
980
  if (!resolved) {
@@ -1011,10 +1011,11 @@ function createMcpHandler2(config) {
1011
1011
  const originRejection = originValidationResponse(request, [...allowedOrigins]);
1012
1012
  if (originRejection)
1013
1013
  return observeRejection(request, originRejection);
1014
- const auth = await Promise.race([
1015
- Promise.resolve(config.auth(request)),
1016
- closedSignal.then(() => null)
1017
- ]);
1014
+ const auth = await untilAborted(Promise.resolve(config.auth(request)), closing.signal).catch((error) => {
1015
+ if (closing.signal.aborted)
1016
+ return null;
1017
+ throw error;
1018
+ });
1018
1019
  if (closed) {
1019
1020
  return observeRejection(request, jsonRpcError(-32000, "MCP handler is closed", 503));
1020
1021
  }
@@ -1036,7 +1037,7 @@ function createMcpHandler2(config) {
1036
1037
  if (closed)
1037
1038
  return;
1038
1039
  closed = true;
1039
- resolveClosed?.();
1040
+ closing.abort();
1040
1041
  await sdkHandler.close();
1041
1042
  }
1042
1043
  };
@@ -3,7 +3,7 @@ import {
3
3
  } from "./index-b0kddvzw.js";
4
4
  import {
5
5
  readBoundedChunks
6
- } from "./index-qndsr7f0.js";
6
+ } from "./index-5efrpfmj.js";
7
7
 
8
8
  // src/internal/secure-fetch.ts
9
9
  import { lookup } from "node:dns/promises";
@@ -5,7 +5,7 @@ import {
5
5
  fetchGuarded,
6
6
  PrivateAddressRefusal,
7
7
  readCapped
8
- } from "./index-t65erk4z.js";
8
+ } from "./index-cxpcdswt.js";
9
9
  import {
10
10
  writeFileAtomicSync2
11
11
  } from "./index-07dcf2yn.js";
@@ -26,7 +26,7 @@ import {
26
26
  import {
27
27
  fetchGuarded,
28
28
  readCapped
29
- } from "./index-t65erk4z.js";
29
+ } from "./index-cxpcdswt.js";
30
30
  import {
31
31
  isWithinDir2
32
32
  } from "./index-5cc6r990.js";
@@ -0,0 +1,27 @@
1
+ import type { NativeCommandOptions, NativeCommandResult } from './contract.js';
2
+ /** Options the stream owns: its readers are the capture, the sink and the pipes. */
3
+ export type NativeCommandStreamOptions = Omit<NativeCommandOptions, 'capture' | 'onOutput' | 'stdio'>;
4
+ /** A started command whose output is read as it appears. */
5
+ export interface NativeCommandStream {
6
+ /** The leader's stdout; closes once the command finished and every byte was queued. */
7
+ readonly stdout: ReadableStream<Uint8Array>;
8
+ readonly stderr: ReadableStream<Uint8Array>;
9
+ /**
10
+ * Settles like `runNativeCommand` once the command ended and both streams were read to the end
11
+ * or cancelled: a stream nobody reads holds the child back, and with it this result. Its
12
+ * `stdout` and `stderr` are empty, the streams hold the bytes.
13
+ */
14
+ readonly result: Promise<NativeCommandResult>;
15
+ /** Stops the command with its stop policy and resolves once its resources are proven closed. */
16
+ stop(): Promise<void>;
17
+ }
18
+ /**
19
+ * Starts one command directly (no shell) like `runNativeCommand`, with the same group ownership,
20
+ * stop policy and bounds, and hands its output over as `ReadableStream`s instead of buffering it
21
+ * for the end. A slow reader slows the child (pipe backpressure); a cancelled stream drops what
22
+ * follows without stopping the command. When the command fails or is stopped, both streams error and
23
+ * the bytes still queued are dropped. A stream nobody reads holds the child back
24
+ * once it queued 1 MiB, so read large outputs concurrently or cancel the stream.
25
+ */
26
+ export declare function streamNativeCommand(input: NativeCommandStreamOptions): NativeCommandStream;
27
+ //# sourceMappingURL=stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/process/stream.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE5E,oFAAoF;AACpF,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAC3C,oBAAoB,EACpB,SAAS,GAAG,UAAU,GAAG,OAAO,CACjC,CAAC;AAEF,4DAA4D;AAC5D,MAAM,WAAW,mBAAmB;IAClC,uFAAuF;IACvF,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IAC5C;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC9C,gGAAgG;IAChG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,0BAA0B,GAAG,mBAAmB,CA0B1F"}
@@ -1 +1 @@
1
- {"version":3,"file":"contract-stream.d.ts","sourceRoot":"","sources":["../../src/server/contract-stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAUnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAiB/C,gEAAgE;AAChE,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,EAC9B,UAAU,EAAE,wBAAwB,EACpC,cAAc,EAAE,eAAe,GAC9B,OAAO,CAAC,QAAQ,CAAC,CAgHnB"}
1
+ {"version":3,"file":"contract-stream.d.ts","sourceRoot":"","sources":["../../src/server/contract-stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAWnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAS/C,gEAAgE;AAChE,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,EAC9B,UAAU,EAAE,wBAAwB,EACpC,cAAc,EAAE,eAAe,GAC9B,OAAO,CAAC,QAAQ,CAAC,CA+GnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp/handler.ts"],"names":[],"mappings":"AASA,OAAO,EACL,KAAK,uBAAuB,EAE7B,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,KAAK,kBAAkB,EAGxB,MAAM,WAAW,CAAC;AAEnB,yEAAyE;AACzE,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEjD,2EAA2E;AAC3E,MAAM,WAAW,qBAAqB;IACpC,4EAA4E;IAC5E,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,+EAA+E;IAC/E,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa,CAAC,KAAK;IAClC,uEAAuE;IACvE,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAC7D,uEAAuE;IACvE,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;IAC5C,wEAAwE;IACxE,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,sFAAsF;IACtF,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE;QAC5B,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,QAAQ,CAAC;KACpB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,CAC1B,KAAK,EACL,SAAS,SAAS,kBAAkB,GAAG,kBAAkB,IACvD,oBAAoB,CAAC,KAAK,EAAE,SAAS,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;AAElE,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAgFD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EACL,KAAK,CAAC,SAAS,SAAS,kBAAkB,GAAG,kBAAkB,EAC/D,MAAM,EAAE,gBAAgB,CAAC,KAAK,EAAE,SAAS,CAAC,GAAG,cAAc,CAkF5D;AAED,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,cAAc,CAAC;CACzB,GAAG,QAAQ,CAMX"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/tools/mcp/handler.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,KAAK,uBAAuB,EAE7B,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,KAAK,kBAAkB,EAGxB,MAAM,WAAW,CAAC;AAEnB,yEAAyE;AACzE,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEjD,2EAA2E;AAC3E,MAAM,WAAW,qBAAqB;IACpC,4EAA4E;IAC5E,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,+EAA+E;IAC/E,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa,CAAC,KAAK;IAClC,uEAAuE;IACvE,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAC7D,uEAAuE;IACvE,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;IAC5C,wEAAwE;IACxE,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,sFAAsF;IACtF,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE;QAC5B,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,QAAQ,CAAC;KACpB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,CAC1B,KAAK,EACL,SAAS,SAAS,kBAAkB,GAAG,kBAAkB,IACvD,oBAAoB,CAAC,KAAK,EAAE,SAAS,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;AAElE,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC3C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAgFD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EACL,KAAK,CAAC,SAAS,SAAS,kBAAkB,GAAG,kBAAkB,EAC/D,MAAM,EAAE,gBAAgB,CAAC,KAAK,EAAE,SAAS,CAAC,GAAG,cAAc,CAoF5D;AAED,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,cAAc,CAAC;CACzB,GAAG,QAAQ,CAMX"}
@@ -21,7 +21,6 @@ export declare class SpeechQueue<Prepared> {
21
21
  private closed;
22
22
  private cancelled;
23
23
  private wake;
24
- private stop;
25
24
  private readonly stopped;
26
25
  constructor(options: SpeechQueueOptions<Prepared>);
27
26
  get isCancelled(): boolean;
@@ -32,6 +31,12 @@ export declare class SpeechQueue<Prepared> {
32
31
  close(): void;
33
32
  /** Interrupted: abort the sentence playing and every synthesis, drop the rest. */
34
33
  cancel(): void;
34
+ /**
35
+ * `pending`, or `CANCELLED` once the queue is cancelled. The wait owns its listener and
36
+ * releases it when `pending` settles: a promise shared by every sentence would keep one
37
+ * reaction per sentence for as long as the queue lives. `pending` never rejects.
38
+ */
39
+ private unlessStopped;
35
40
  private notify;
36
41
  /** Start synthesis up to the lookahead: the sentence playing or next, plus `lookahead`. */
37
42
  private fill;
@@ -1 +1 @@
1
- {"version":3,"file":"speech-queue.d.ts","sourceRoot":"","sources":["../../src/voice/speech-queue.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,kBAAkB,CAAC,QAAQ;IAC1C,oEAAoE;IACpE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClE,kFAAkF;IAClF,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,6FAA6F;IAC7F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,uFAAuF;IACvF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;CACxC;AAqBD,qBAAa,WAAW,CAAC,QAAQ;IAenB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAdpC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwB;IAC9C,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,IAAI,CAA6B;IACzC,OAAO,CAAC,IAAI,CAAqC;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAErB;IAEH,YAA6B,OAAO,EAAE,kBAAkB,CAAC,QAAQ,CAAC,EAOjE;IAED,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,sFAAsF;IACtF,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAKvB;IAED,yDAAyD;IACzD,KAAK,IAAI,IAAI,CAGZ;IAED,kFAAkF;IAClF,MAAM,IAAI,IAAI,CAgBb;IAED,OAAO,CAAC,MAAM;IAId,2FAA2F;IAC3F,OAAO,CAAC,IAAI;YAaE,GAAG;IAmCjB,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,OAAO;CAOhB"}
1
+ {"version":3,"file":"speech-queue.d.ts","sourceRoot":"","sources":["../../src/voice/speech-queue.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,kBAAkB,CAAC,QAAQ;IAC1C,oEAAoE;IACpE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClE,kFAAkF;IAClF,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,6FAA6F;IAC7F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,uFAAuF;IACvF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;CACxC;AAqBD,qBAAa,WAAW,CAAC,QAAQ;IAYnB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAXpC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwB;IAC9C,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,IAAI,CAA6B;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IAEjD,YAA6B,OAAO,EAAE,kBAAkB,CAAC,QAAQ,CAAC,EAOjE;IAED,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,sFAAsF;IACtF,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAKvB;IAED,yDAAyD;IACzD,KAAK,IAAI,IAAI,CAGZ;IAED,kFAAkF;IAClF,MAAM,IAAI,IAAI,CAgBb;IAED;;;;OAIG;YACW,aAAa;IAQ3B,OAAO,CAAC,MAAM;IAId,2FAA2F;IAC3F,OAAO,CAAC,IAAI;YAaE,GAAG;IAoCjB,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,OAAO;CAOhB"}
@@ -0,0 +1,32 @@
1
+ # stitchkit/process — stitchkit docs slice, part 2 of 2
2
+
3
+ > Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
4
+ > `llms.txt` in this package.
5
+
6
+ Parts of this slice:
7
+ - `llms/process.txt`: Guide: Native IO (docs/guide/native-io.md)
8
+ - `llms/process.2.txt`: API reference: `stitchkit/process` (docs/api/reference.md)
9
+
10
+ ==============================================================================
11
+ # API reference: `stitchkit/process` (docs/api/reference.md)
12
+ ==============================================================================
13
+
14
+ ## `stitchkit/process`
15
+
16
+ Native POSIX Bun/Node one-shot command execution. See [native IO](../guide/native-io.md).
17
+
18
+ | Export | Kind | Purpose |
19
+ |---|---|---|
20
+ | `runNativeCommand` | function | caller-lifetime streaming or explicitly bounded binary capture |
21
+ | `streamNativeCommand` | function | starts a command under the same group ownership, stop policy and bounds as `runNativeCommand` and returns `stdout` / `stderr` as `ReadableStream<Uint8Array>`, `result` and `stop()`; a slow reader slows the child, a cancelled stream drops its bytes, a failed command errors both streams |
22
+ | `NativeCommandStream` | type | what `streamNativeCommand` returns: `stdout`, `stderr`, `result` (its buffers are empty) and `stop()` |
23
+ | `NativeCommandStreamOptions` | type | `NativeCommandOptions` without `capture`, `onOutput` and `stdio`, which the streams own |
24
+ | `NativeCommandOptions` | type | executable/args, env policy, signal/deadline, bytes sink, finite cleanup limits, the `stop` policy and `descendants` (`'terminate-after-leader'`, the default: stop the group after the leader exits, or `'leave'`); `stdio` (`'pipe'`, or `'inherit'` for the caller's descriptors, refusing the pipe-only options) and `group` (`'own'`, or `'caller'`: joins the caller's process group, stops only the leader, refuses `descendants`, `ownerLoss` and a group stop); `ownerLoss: 'terminate'` (optional Linux/Darwin Bun/Node guard: kernel channel EOF after abrupt caller death KILLs its owned group, unsupported platforms refuse); `onLeaderStarted({ pid })` (once after the group leader exists; the package guard's PID under `ownerLoss`), `drainTimeoutMs` (bound on the pipes staying open after the leader exited, `COMMAND_CLEANUP`); an unknown key is refused by name, a removed one (`killGraceMs`) with its replacement |
25
+ | `NativeCommandStopPolicy` | type | how a cancelled command stops: `target` `'group'` (signal every member) or `'leader'` (signal only the leader; its exit ends the grace), `signal` (default `SIGTERM`), `graceMs` (at most one hour) before KILL to the group, and `killOn`, an `AbortSignal` that skips the grace; default `{ target: 'group', signal: 'SIGTERM', graceMs: 100 }` |
26
+ | `NativeCommandResult` | type | observed nullable exit code/signal, `descendantsStopped` (members left in the group after the leader exited were stopped) and captured stdout/stderr bytes |
27
+ | `NativeCommandError` | class | COMMAND_LIMIT with machine reason `deadline` or `output-budget`; COMMAND_UNAVAILABLE / COMMAND_CLEANUP have no limit reason; COMMAND_CLEANUP means the command's resources were not proven closed, either past `cleanupTimeoutMs` after a stop or past `drainTimeoutMs` after the leader exited, and `cause` holds that bound's failure. Preserves internal cause and existing constructor; caller-created limits without supplied reason remain unclassified |
28
+
29
+ | `NativeCommandSettlement` | type | `exit` (the leader ended on its own), `stopped` (the command was stopped and the leader's exit after the stop observed: the stop's `cause` with `exitCode`/`signal`) or `error` (no exit observed), including synchronous native launch failure; `onLeaderSettled` runs once before drain, bounded by cleanupTimeoutMs, with a cancellable settlement signal; schema/pre-abort refusal creates no settlement obligation |
30
+ | `observeProcessInstance` / `ProcessInstanceObservation` | function / type | Linux/Darwin lifetime observation; observed identity or unavailable with its original cause, never guessed absence |
31
+ | `ProcessInstanceSchema` / `ProcessInstance` | schema / type | platform, boot ID, namespace and process start identity from the shared native reader |
32
+ | `probeProcessOwner` / `ProcessOwnerEvidence` | function / type | compare lifetimes on one caller-established machine; matched, boot change, reused PID, gone, legacy or unavailable with cause |
package/llms/process.txt CHANGED
@@ -1,9 +1,12 @@
1
- # stitchkit/process — stitchkit docs slice
1
+ # stitchkit/process — stitchkit docs slice, part 1 of 2
2
2
 
3
3
  > Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
4
4
  > `llms.txt` in this package.
5
+ > Continues in `llms/process.2.txt`.
5
6
 
6
- Contains: Guide: Native IO (docs/guide/native-io.md) · API reference: `stitchkit/process` (docs/api/reference.md)
7
+ Parts of this slice:
8
+ - `llms/process.txt`: Guide: Native IO (docs/guide/native-io.md)
9
+ - `llms/process.2.txt`: API reference: `stitchkit/process` (docs/api/reference.md)
7
10
 
8
11
  ==============================================================================
9
12
  # Guide: Native IO (docs/guide/native-io.md)
@@ -147,6 +150,27 @@ await runNativeCommand({
147
150
  })
148
151
  ```
149
152
 
153
+ To read output as it appears, use `streamNativeCommand`: the same owner, group ownership, `stop`
154
+ policy and bounds, with the output as `ReadableStream<Uint8Array>`.
155
+
156
+ ```ts
157
+ import { streamNativeCommand } from 'stitchkit/process'
158
+
159
+ const lifetime = new AbortController()
160
+ const command = streamNativeCommand({
161
+ executable: '/usr/bin/tail', args: ['-f', '/var/log/app.log'], signal: lifetime.signal,
162
+ })
163
+ void command.stderr.cancel() // not interested: dropped, the command keeps running
164
+ for await (const bytes of command.stdout) handle(bytes) // a slow reader slows the child
165
+ await command.result // settles like `runNativeCommand`; `stop()` ends the group and waits for it
166
+ ```
167
+
168
+ The streams own `capture`, `onOutput` and `stdio`; passing one is refused. Each stream queues up to 1 MiB, so
169
+ a small output can be read in any order, even after `result`; past that an unread stream fills its pipe
170
+ and holds the command, and its `result`, until the signal or `timeoutMs`, so read large outputs
171
+ concurrently or cancel the stream you ignore. Cancelling a stream drops its later bytes and leaves the command running. When the
172
+ command fails or is stopped, both streams error and the bytes still queued are dropped.
173
+
150
174
  A command needs a caller `signal` or a finite `timeoutMs` (1 to 2147483647 ms, the native timer
151
175
  range; a larger value is refused, never turned into 1 ms). Streaming runs for the caller's lifetime,
152
176
  with no hidden 60-second timeout or 1 MiB output limit. `capture` defaults to false; `true` requires
@@ -698,24 +722,3 @@ A sandbox admission slot is released once a direct command's pipes have closed a
698
722
  A `COMMAND_CLEANUP` outcome means the death of its group was not proven, so the slot stays
699
723
  occupied, `stop` keeps rejecting with the cleanup error, and the sandbox session is recreated to
700
724
  recover.
701
-
702
- ==============================================================================
703
- # API reference: `stitchkit/process` (docs/api/reference.md)
704
- ==============================================================================
705
-
706
- ## `stitchkit/process`
707
-
708
- Native POSIX Bun/Node one-shot command execution. See [native IO](../guide/native-io.md).
709
-
710
- | Export | Kind | Purpose |
711
- |---|---|---|
712
- | `runNativeCommand` | function | caller-lifetime streaming or explicitly bounded binary capture |
713
- | `NativeCommandOptions` | type | executable/args, env policy, signal/deadline, bytes sink, finite cleanup limits, the `stop` policy and `descendants` (`'terminate-after-leader'`, the default: stop the group after the leader exits, or `'leave'`); `stdio` (`'pipe'`, or `'inherit'` for the caller's descriptors, refusing the pipe-only options) and `group` (`'own'`, or `'caller'`: joins the caller's process group, stops only the leader, refuses `descendants`, `ownerLoss` and a group stop); `ownerLoss: 'terminate'` (optional Linux/Darwin Bun/Node guard: kernel channel EOF after abrupt caller death KILLs its owned group, unsupported platforms refuse); `onLeaderStarted({ pid })` (once after the group leader exists; the package guard's PID under `ownerLoss`), `drainTimeoutMs` (bound on the pipes staying open after the leader exited, `COMMAND_CLEANUP`); an unknown key is refused by name, a removed one (`killGraceMs`) with its replacement |
714
- | `NativeCommandStopPolicy` | type | how a cancelled command stops: `target` `'group'` (signal every member) or `'leader'` (signal only the leader; its exit ends the grace), `signal` (default `SIGTERM`), `graceMs` (at most one hour) before KILL to the group, and `killOn`, an `AbortSignal` that skips the grace; default `{ target: 'group', signal: 'SIGTERM', graceMs: 100 }` |
715
- | `NativeCommandResult` | type | observed nullable exit code/signal, `descendantsStopped` (members left in the group after the leader exited were stopped) and captured stdout/stderr bytes |
716
- | `NativeCommandError` | class | COMMAND_LIMIT with machine reason `deadline` or `output-budget`; COMMAND_UNAVAILABLE / COMMAND_CLEANUP have no limit reason; COMMAND_CLEANUP means the command's resources were not proven closed, either past `cleanupTimeoutMs` after a stop or past `drainTimeoutMs` after the leader exited, and `cause` holds that bound's failure. Preserves internal cause and existing constructor; caller-created limits without supplied reason remain unclassified |
717
-
718
- | `NativeCommandSettlement` | type | `exit` (the leader ended on its own), `stopped` (the command was stopped and the leader's exit after the stop observed: the stop's `cause` with `exitCode`/`signal`) or `error` (no exit observed), including synchronous native launch failure; `onLeaderSettled` runs once before drain, bounded by cleanupTimeoutMs, with a cancellable settlement signal; schema/pre-abort refusal creates no settlement obligation |
719
- | `observeProcessInstance` / `ProcessInstanceObservation` | function / type | Linux/Darwin lifetime observation; observed identity or unavailable with its original cause, never guessed absence |
720
- | `ProcessInstanceSchema` / `ProcessInstance` | schema / type | platform, boot ID, namespace and process start identity from the shared native reader |
721
- | `probeProcessOwner` / `ProcessOwnerEvidence` | function / type | compare lifetimes on one caller-established machine; matched, boot change, reused PID, gone, legacy or unavailable with cause |
package/llms-full.txt CHANGED
@@ -8586,6 +8586,27 @@ await runNativeCommand({
8586
8586
  })
8587
8587
  ```
8588
8588
 
8589
+ To read output as it appears, use `streamNativeCommand`: the same owner, group ownership, `stop`
8590
+ policy and bounds, with the output as `ReadableStream<Uint8Array>`.
8591
+
8592
+ ```ts
8593
+ import { streamNativeCommand } from 'stitchkit/process'
8594
+
8595
+ const lifetime = new AbortController()
8596
+ const command = streamNativeCommand({
8597
+ executable: '/usr/bin/tail', args: ['-f', '/var/log/app.log'], signal: lifetime.signal,
8598
+ })
8599
+ void command.stderr.cancel() // not interested: dropped, the command keeps running
8600
+ for await (const bytes of command.stdout) handle(bytes) // a slow reader slows the child
8601
+ await command.result // settles like `runNativeCommand`; `stop()` ends the group and waits for it
8602
+ ```
8603
+
8604
+ The streams own `capture`, `onOutput` and `stdio`; passing one is refused. Each stream queues up to 1 MiB, so
8605
+ a small output can be read in any order, even after `result`; past that an unread stream fills its pipe
8606
+ and holds the command, and its `result`, until the signal or `timeoutMs`, so read large outputs
8607
+ concurrently or cancel the stream you ignore. Cancelling a stream drops its later bytes and leaves the command running. When the
8608
+ command fails or is stopped, both streams error and the bytes still queued are dropped.
8609
+
8589
8610
  A command needs a caller `signal` or a finite `timeoutMs` (1 to 2147483647 ms, the native timer
8590
8611
  range; a larger value is refused, never turned into 1 ms). Streaming runs for the caller's lifetime,
8591
8612
  with no hidden 60-second timeout or 1 MiB output limit. `capture` defaults to false; `true` requires
@@ -23566,6 +23587,9 @@ Native POSIX Bun/Node one-shot command execution. See [native IO](../guide/nativ
23566
23587
  | Export | Kind | Purpose |
23567
23588
  |---|---|---|
23568
23589
  | `runNativeCommand` | function | caller-lifetime streaming or explicitly bounded binary capture |
23590
+ | `streamNativeCommand` | function | starts a command under the same group ownership, stop policy and bounds as `runNativeCommand` and returns `stdout` / `stderr` as `ReadableStream<Uint8Array>`, `result` and `stop()`; a slow reader slows the child, a cancelled stream drops its bytes, a failed command errors both streams |
23591
+ | `NativeCommandStream` | type | what `streamNativeCommand` returns: `stdout`, `stderr`, `result` (its buffers are empty) and `stop()` |
23592
+ | `NativeCommandStreamOptions` | type | `NativeCommandOptions` without `capture`, `onOutput` and `stdio`, which the streams own |
23569
23593
  | `NativeCommandOptions` | type | executable/args, env policy, signal/deadline, bytes sink, finite cleanup limits, the `stop` policy and `descendants` (`'terminate-after-leader'`, the default: stop the group after the leader exits, or `'leave'`); `stdio` (`'pipe'`, or `'inherit'` for the caller's descriptors, refusing the pipe-only options) and `group` (`'own'`, or `'caller'`: joins the caller's process group, stops only the leader, refuses `descendants`, `ownerLoss` and a group stop); `ownerLoss: 'terminate'` (optional Linux/Darwin Bun/Node guard: kernel channel EOF after abrupt caller death KILLs its owned group, unsupported platforms refuse); `onLeaderStarted({ pid })` (once after the group leader exists; the package guard's PID under `ownerLoss`), `drainTimeoutMs` (bound on the pipes staying open after the leader exited, `COMMAND_CLEANUP`); an unknown key is refused by name, a removed one (`killGraceMs`) with its replacement |
23570
23594
  | `NativeCommandStopPolicy` | type | how a cancelled command stops: `target` `'group'` (signal every member) or `'leader'` (signal only the leader; its exit ends the grace), `signal` (default `SIGTERM`), `graceMs` (at most one hour) before KILL to the group, and `killOn`, an `AbortSignal` that skips the grace; default `{ target: 'group', signal: 'SIGTERM', graceMs: 100 }` |
23571
23595
  | `NativeCommandResult` | type | observed nullable exit code/signal, `descendantsStopped` (members left in the group after the leader exited were stopped) and captured stdout/stderr bytes |
package/llms.txt CHANGED
@@ -56,7 +56,7 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
56
56
  - **stitchkit/node**: [llms/node.txt](llms/node.txt) 3.1 KB
57
57
  - **stitchkit/agent-runtime/react**: [llms/agent-runtime-react.txt](llms/agent-runtime-react.txt) 1.0 KB
58
58
  - **stitchkit/agent-runtime/realtime**: [llms/agent-runtime-realtime.txt](llms/agent-runtime-realtime.txt) 2.6 KB
59
- - **stitchkit/process**: [llms/process.txt](llms/process.txt) 45.8 KB
59
+ - **stitchkit/process**: [llms/process.txt](llms/process.txt) 43.7 KB → [llms/process.2.txt](llms/process.2.txt) 4.3 KB
60
60
  - **stitchkit/process/owner-loss**: [llms/process-owner-loss.txt](llms/process-owner-loss.txt) 0.9 KB
61
61
  - **stitchkit/files/packaging**: [llms/files-packaging.txt](llms/files-packaging.txt) 3.5 KB
62
62
  - **stitchkit/http**: [llms/http.txt](llms/http.txt) 0.9 KB
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.111.0",
3
+ "version": "0.111.2",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  chunksOf
3
3
  } from "./index-cjbn98zt.js";
4
- import {
5
- abortReason
6
- } from "./index-a5qr2rj6.js";
7
4
  import {
8
5
  assertPositiveSafeInteger
9
6
  } from "./index-0s4xebbg.js";
7
+ import {
8
+ abortReason
9
+ } from "./index-a5qr2rj6.js";
10
10
 
11
11
  // src/internal/bounded-body.ts
12
12
  async function readBoundedChunks(source, options) {
File without changes