stitchkit 0.103.8 → 0.103.10

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 (79) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/agent-runtime/sandbox-process.d.ts +2 -2
  3. package/dist/agent-runtime/sandbox-process.d.ts.map +1 -1
  4. package/dist/agent-runtime/store-conformance-lifecycle.d.ts +10 -10
  5. package/dist/durability/contract.d.ts +3 -2
  6. package/dist/durability/contract.d.ts.map +1 -1
  7. package/dist/durability/effect.d.ts.map +1 -1
  8. package/dist/durability/engine.d.ts.map +1 -1
  9. package/dist/durability/json.d.ts.map +1 -1
  10. package/dist/entrypoints/agent-runtime/harness.js +2 -2
  11. package/dist/entrypoints/agent-runtime/sandbox.js +29 -81
  12. package/dist/entrypoints/agent-runtime.js +2 -2
  13. package/dist/entrypoints/application/diagnostic-journal.js +2 -1
  14. package/dist/entrypoints/cli.js +1 -1
  15. package/dist/entrypoints/files.d.ts +2 -0
  16. package/dist/entrypoints/files.d.ts.map +1 -1
  17. package/dist/entrypoints/files.js +14 -6
  18. package/dist/entrypoints/primitives.js +77 -57
  19. package/dist/entrypoints/process.d.ts +5 -0
  20. package/dist/entrypoints/process.d.ts.map +1 -0
  21. package/dist/entrypoints/process.js +20 -0
  22. package/dist/entrypoints/tools.js +12 -3
  23. package/dist/files/boundary.d.ts +19 -1
  24. package/dist/files/boundary.d.ts.map +1 -1
  25. package/dist/files/file-io.d.ts +5 -1
  26. package/dist/files/file-io.d.ts.map +1 -1
  27. package/dist/files/read.d.ts +13 -0
  28. package/dist/files/read.d.ts.map +1 -0
  29. package/dist/index-12m2v2a9.js +98 -0
  30. package/dist/index-6m6a9ty4.js +285 -0
  31. package/dist/index-8a9ksm91.js +95 -0
  32. package/dist/{index-90ppdx3q.js → index-9jb61j5a.js} +1 -1
  33. package/dist/{index-5vyd0fkb.js → index-ary5s7k0.js} +40 -61
  34. package/dist/index-c1jmtxa0.js +74 -0
  35. package/dist/index-ga597cvy.js +184 -0
  36. package/dist/index-mbnnw3f2.js +25 -0
  37. package/dist/index-nybn2err.js +213 -0
  38. package/dist/{index-v4a2fjtk.js → index-sybedc8b.js} +124 -55
  39. package/dist/internal/abort-race.d.ts +3 -0
  40. package/dist/internal/abort-race.d.ts.map +1 -0
  41. package/dist/internal/atomic-file.d.ts +20 -2
  42. package/dist/internal/atomic-file.d.ts.map +1 -1
  43. package/dist/internal/atomic-publication.d.ts +19 -0
  44. package/dist/internal/atomic-publication.d.ts.map +1 -0
  45. package/dist/internal/exclusive-lock.d.ts +2 -1
  46. package/dist/internal/exclusive-lock.d.ts.map +1 -1
  47. package/dist/internal/json-data.d.ts +8 -0
  48. package/dist/internal/json-data.d.ts.map +1 -0
  49. package/dist/internal/process-instance.d.ts +46 -9
  50. package/dist/internal/process-instance.d.ts.map +1 -1
  51. package/dist/internal/with-exclusive-lock.d.ts +4 -3
  52. package/dist/internal/with-exclusive-lock.d.ts.map +1 -1
  53. package/dist/primitives/canonical-json.d.ts +11 -0
  54. package/dist/primitives/canonical-json.d.ts.map +1 -0
  55. package/dist/primitives/index.d.ts +1 -0
  56. package/dist/primitives/index.d.ts.map +1 -1
  57. package/dist/process/command-owner.d.ts +13 -0
  58. package/dist/process/command-owner.d.ts.map +1 -0
  59. package/dist/process/command.d.ts +3 -0
  60. package/dist/process/command.d.ts.map +1 -0
  61. package/dist/process/contract.d.ts +45 -0
  62. package/dist/process/contract.d.ts.map +1 -0
  63. package/dist/process/group.d.ts +6 -0
  64. package/dist/process/group.d.ts.map +1 -0
  65. package/dist/process/leader-settlement.d.ts +4 -0
  66. package/dist/process/leader-settlement.d.ts.map +1 -0
  67. package/dist/tools/transfer/managed-file-error.d.ts.map +1 -1
  68. package/llms/files.txt +9 -1
  69. package/llms/primitives.txt +31 -1
  70. package/llms/process.txt +242 -0
  71. package/llms/stitchkit.txt +1 -0
  72. package/llms/tools.txt +18 -0
  73. package/llms-full.txt +292 -2
  74. package/llms.txt +6 -4
  75. package/native/darwin-arm64.node +0 -0
  76. package/native/darwin-x64.node +0 -0
  77. package/package.json +5 -1
  78. package/dist/index-7kxw1awn.js +0 -328
  79. package/dist/index-aan6hqb9.js +0 -65
@@ -1 +1 @@
1
- {"version":3,"file":"managed-file-error.d.ts","sourceRoot":"","sources":["../../../src/tools/transfer/managed-file-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAuB,MAAM,uBAAuB,CAAC;AAgBtE,yFAAyF;AACzF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAkBnE;AAED,kGAAkG;AAClG,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAE/D"}
1
+ {"version":3,"file":"managed-file-error.d.ts","sourceRoot":"","sources":["../../../src/tools/transfer/managed-file-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAuB,MAAM,uBAAuB,CAAC;AAmBtE,yFAAyF;AACzF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAyBnE;AAED,kGAAkG;AAClG,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAE/D"}
package/llms/files.txt CHANGED
@@ -5,6 +5,9 @@
5
5
 
6
6
  Contains: API reference: `stitchkit/files` (docs/api/reference.md)
7
7
 
8
+ Also read:
9
+ - see `llms/process.txt` for the Native IO guide
10
+
8
11
  ==============================================================================
9
12
  # API reference: `stitchkit/files` (docs/api/reference.md)
10
13
  ==============================================================================
@@ -27,7 +30,7 @@ available from `stitchkit/contract`.
27
30
  | `ManagedFileInspector` | _type_ | bounded-prefix read/write inspection callback with a finite cancellation signal that cannot own path or size |
28
31
  | `ManagedFileInspectionInput` / `ManagedFileInspection` | _type_ | inspector prefix/name/declared media/signal input and validated metadata-only result |
29
32
  | `writeFileAtomic` / `writeFileAtomicSync` / `WriteFileAtomicOptions` | function / _type_ | replace a file atomically: a random staging name created exclusively (never through a planted link), the mode set on the descriptor before the file is visible (default `0o600`, not masked by the umask), `fsync`, rename; a failure leaves the target and no staging file. The asynchronous form keeps the event loop running |
30
- | `withExclusiveLock` / `ExclusiveLockOptions` / `ExclusiveLock` / `ExclusiveLockOwner` | function / _type_ | run work under an exclusive lock between processes — a file recording its owner (pid, host, machine identity, time and nullable kernel process identity); waits up to `timeoutMs` (default 10 s) and stops on `signal`; a dead owner on this machine is taken over, a live, slow or foreign one never is; an ownerless lock only after `ownerlessGraceMs` |
33
+ | `withExclusiveLock` / `ExclusiveLockOptions` / `ExclusiveLock` / `ExclusiveLockOwner` | function / _type_ | run work under an exclusive lock between processes — a file recording its owner (pid, host, machine identity, time and nullable kernel process identity); waits up to `timeoutMs` (default 10 s) and stops on `signal`; a dead owner on this machine is taken over, a live, slow or foreign one never is; an ownerless lock only after `ownerlessGraceMs` (default 5000 ms, `null` disables age-based reclaim including guards); exact `mode` is independent of umask; shared readers need a common group and traversable directories |
31
34
  | `ExclusiveLockError` | class | `LOCK_TIMEOUT` naming the resource and its holder, or `LOCK_ABORTED` with the signal's reason |
32
35
  | `createChunkSpool` | function | the server half of a chunked upload: parts on disk under `owner` + client-minted `uploadId`, each with a size+sha256 receipt; idempotent `open` and `put`, the first writer of a part wins across processes, `assemble` in order once every part is in, `sweep` of untouched uploads — [guide](../guide/client.md#chunked-uploads) |
33
36
  | `ChunkSpool` / `ChunkSpoolConfig` / `ChunkSpoolKey` / `ChunkSpoolOpen` / `ChunkSpoolPart` / `ChunkSpoolAssembly` | _type_ | the spool, its directory, part size, file limit, meta schema and age; an upload's key; its declaration; one part; the assembled parts with a concatenating `stream()` |
@@ -35,3 +38,8 @@ available from `stitchkit/contract`.
35
38
  | `CHUNK_SPOOL_ERROR_CODES` / `isChunkSpoolErrorCode` | const / function | every `ChunkSpoolErrorCode` as a frozen list, and the guard for one — map them onto the app's own error codes, or a spool refusal reaches its client as an unknown 500 |
36
39
 
37
40
  ---
41
+
42
+ | Export | Kind | Purpose |
43
+ |---|---|---|
44
+ | `AtomicFilePublicationError` | class | published=true with cleanup/directory-sync/directory-close phase |
45
+ | `FileObservation` | type | observed descriptor dev/ino/size/nlink/mtimeMs/ctimeMs |
@@ -15,7 +15,7 @@ description: Declare lifecycle, access, exact values, audit, delivery and export
15
15
  type: guide
16
16
  status: active
17
17
  created: 2026-09-01
18
- updated: 2026-09-01
18
+ updated: 2026-10-01 21:03 +07:00
19
19
  ---
20
20
 
21
21
  # Generic application primitives
@@ -108,6 +108,31 @@ of the application's existing async operation. The input, scope, metadata and to
108
108
  change. The application creates bytes and owns operation persistence; the managed-file boundary
109
109
  streams them.
110
110
 
111
+ ## Canonical JSON
112
+
113
+ ```ts
114
+ import { canonicalJson } from 'stitchkit/primitives'
115
+
116
+ const bytes = canonicalJson({ '2': 'two', '10': 'ten', optional: undefined, nested: [1, null] }, {
117
+ maxDepth: 20, maxNodes: 10_000, maxBytes: 64 * 1024,
118
+ })
119
+ // {"10":"ten","2":"two","nested":[1,null]}
120
+ // Native crypto or browser WebCrypto hashes these same UTF-8 bytes.
121
+ ```
122
+
123
+ `canonicalJson` использует единый serializer исторических digest: object keys в UTF-16 code-unit
124
+ order, включая integer-like keys; array order сохраняется. Optional object members с undefined
125
+ опускаются. Top-level/array undefined, sparse arrays, functions/symbols/BigInt, non-finite numbers,
126
+ negative zero, cycles, user prototypes, accessors, non-enumerable/symbol members отказаны до
127
+ serialization. Приложение валидирует свою schema первым; canonical boundary не заменяет её.
128
+
129
+ Defaults: depth 100 (maximum 100), nodes 100 000, UTF-8 bytes 1 MiB. Превышение или не-JSON data —
130
+ TypeError; неверные options — Zod validation error. Это browser-safe leaf без Node/Bun/crypto,
131
+ agent-runtime/testing или optional peers. Native и browser используют один algorithm; hashing
132
+ остаётся platform adapter. Переносите только serializer recursion, сохраняя proof schemas и
133
+ verification boundary. Существующие внутренние historical normalization/digest fixtures не меняются;
134
+ новый public boundary сознательно отказывает неоднозначным values вместо фиктивной совместимости.
135
+
111
136
  ==============================================================================
112
137
  # API reference: `stitchkit/primitives` (docs/api/reference.md)
113
138
  ==============================================================================
@@ -156,3 +181,8 @@ schedules, transports and file generation remain application-owned.
156
181
  | `SourceText` / `SourceRisk` | _types_ | migration scanner input and exact path/line evidence |
157
182
 
158
183
  ---
184
+
185
+ | Export | Kind | Purpose |
186
+ |---|---|---|
187
+ | `canonicalJson` | function | bounded plain JSON, UTF-16 key sort, optional object members omitted |
188
+ | `CanonicalJsonOptions` | type | inferred depth/node/UTF-8 bytes limits |
@@ -0,0 +1,242 @@
1
+ # stitchkit/process — stitchkit docs slice
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
+ Contains: Guide: Native IO (docs/guide/native-io.md) · API reference: `stitchkit/process` (docs/api/reference.md)
7
+
8
+ ==============================================================================
9
+ # Guide: Native IO (docs/guide/native-io.md)
10
+ ==============================================================================
11
+
12
+ ---
13
+ title: Конечные команды и файловые гарантии
14
+ description: Native IO для Bun и Node, с явно выбранной durability, безопасными bounds и caller lifetime.
15
+ type: guide
16
+ status: active
17
+ created: 2026-10-01 20:44 +07:00
18
+ updated: 2026-10-02 00:37 +07:00
19
+ participants:
20
+ - role: authored
21
+ harness: Codex
22
+ model: GPT-6
23
+ at: 2026-10-01 20:44 +07:00
24
+ - role: implemented
25
+ harness: Codex
26
+ model: GPT-6
27
+ at: 2026-10-02 00:08 +07:00
28
+ ---
29
+
30
+ # Native IO
31
+
32
+ Примеры используют публичные imports и одинаково исполняются в Bun и Node ≥22.
33
+
34
+ ## Запись файла
35
+
36
+ ```ts
37
+ import { writeFileAtomic, AtomicFilePublicationError } from 'stitchkit/files'
38
+
39
+ try {
40
+ await writeFileAtomic('/srv/app/receipt.json', JSON.stringify({ id: 'receipt-1' }), {
41
+ replace: false,
42
+ mode: 0o600,
43
+ durability: 'directory',
44
+ })
45
+ } catch (error) {
46
+ if (error instanceof AtomicFilePublicationError) {
47
+ // target уже виден. Проверьте receipt, прежде чем решать о повторе.
48
+ console.error(error.phase, error.cause)
49
+ } else throw error
50
+ }
51
+ await writeFileAtomic('/srv/app/state.json', '{"version":2}', {
52
+ replace: true, durability: 'directory',
53
+ })
54
+ ```
55
+
56
+ По умолчанию `replace: true`, `durability: 'file'`, `mode: 0o600`: existing вызовы сохраняют
57
+ file-fsync и atomic replacement. `durability: 'none'` отключает sync; `'directory'` выполняет
58
+ полную запись → exact descriptor chmod → file fsync → rename (replace) либо link (create) →
59
+ удаление staging link → parent fsync. `writeFileAtomicSync` имеет те же options и порядок.
60
+ Create отказывает с native `EEXIST` при существующем файле, symlink или concurrent winner.
61
+ Никогда не переводите этот отказ в replace автоматически.
62
+
63
+ `AtomicFilePublicationError.published === true` отличает видимость от неподтверждённого
64
+ acknowledgement. `phase` — `cleanup`, `directory-sync` или `directory-close`; первичная причина
65
+ сохранена в `cause`. Ошибка до publish оставляет прежний target. Cleanup вторичен к первичной ошибке.
66
+ При create кратко существует второй hardlink; strict single-link reader вправе отказать в этом окне.
67
+
68
+ Directory fsync — native POSIX/filesystem capability. Windows явно отказывает; иной filesystem
69
+ может отказать при open/sync. Проверка на Linux не подтверждает поведение macOS, сетевой FS или
70
+ сохранность при отключении питания. Состояние диска определяется гарантиями самой FS/ОС.
71
+ `createManagedFileBoundary.write(..., { durable: true })` использует тот же publish owner и parent sync;
72
+ его create по умолчанию остаётся create, а `replace: true` явно разрешает replacement.
73
+
74
+ ## Строгое чтение
75
+
76
+ ```ts
77
+ import { createManagedFileBoundary } from 'stitchkit/files'
78
+
79
+ const files = await createManagedFileBoundary({ root: '/srv/app', maxReadBytes: 64 * 1024 })
80
+ const source = await files.read('receipt.json', {
81
+ rejectSymlinks: true, singleLink: true, stable: true, observe: true,
82
+ })
83
+ console.log(source.observation) // dev, ino, size, nlink, mtimeMs, ctimeMs
84
+ const receipt = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(source.bytes))
85
+ // schema.parse(receipt) и проверка digest принадлежат приложению.
86
+ ```
87
+
88
+ Без новых options ответ сохраняет `{ ref, bytes }`, допускает trusted in-root symlink и hardlink.
89
+ `rejectSymlinks` запрещает requested leaf, `singleLink` проверяет `nlink` открытого descriptor,
90
+ `stable` сравнивает dev/ino/size/mtime/ctime/nlink вокруг byte read; `observe` добавляет metadata.
91
+ `FILE_NOT_FOUND`, `FILE_NOT_REGULAR`, `FILE_UNSAFE_LINK`, `FILE_TOO_LARGE`, `FILE_CHANGED`,
92
+ `FILE_UNSUPPORTED` и `FILE_IO_ERROR` различаются. Abort сохраняет caller reason.
93
+ Nonblocking open позволяет отказать FIFO без ожидания writer. Bytes ограничены во время чтения,
94
+ включая рост файла, не только initial stat. Chunk allocation ≤ maxBytes+1+64 KiB, flatten ≤ maxBytes;
95
+ FD закрывается при success, отказе, abort и IO error.
96
+ Ошибка close сохраняет первичную ошибку чтения; после успешного чтения ошибка close возвращается caller.
97
+
98
+ Root и ancestors принадлежат trusted actor. Эти options не дают descriptor-relative containment
99
+ при hostile concurrent ancestor replacement. Отдельного переключателя «считать ancestors безопасными»
100
+ нет: portable owner не предоставляет такую capability. Metadata stability не доказывает immutable
101
+ snapshot против произвольного hostile writer; используйте application lock/immutable publication.
102
+
103
+ ## Команды
104
+
105
+ ```ts
106
+ import { runNativeCommand } from 'stitchkit/process'
107
+
108
+ const captured = await runNativeCommand({
109
+ executable: '/usr/bin/git', args: ['rev-parse', 'HEAD'], cwd: '/srv/app',
110
+ envPolicy: 'declared-only', env: { PATH: '/usr/bin:/bin' },
111
+ capture: true, maxOutputBytes: 4096, timeoutMs: 5000,
112
+ })
113
+ console.log(captured.exitCode, new TextDecoder().decode(captured.stdout))
114
+
115
+ const lifetime = new AbortController()
116
+ await runNativeCommand({
117
+ executable: '/usr/bin/git', args: ['status'], signal: lifetime.signal,
118
+ onOutput: async (bytes, channel, outputSignal) => {
119
+ outputSignal.throwIfAborted()
120
+ await output.write(bytes, channel, outputSignal) // application-owned bounded sink
121
+ },
122
+ })
123
+ ```
124
+
125
+ Команда требует caller signal или finite `timeoutMs` (1–2147483647 ms: native timer range; большее значение отказывается, не превращается в 1 ms). Streaming имеет caller lifetime, без скрытого
126
+ 60-second timeout или 1-MiB output limit. `capture` по умолчанию false; true требует
127
+ `maxOutputBytes` для суммарных stdout+stderr. Лимит считает исходные bytes; API возвращает
128
+ `Uint8Array`, не преобразует invalid UTF-8. Result содержит наблюдаемый `exitCode: number | null`
129
+ и `signal: string | null`; signal termination не превращается в выдуманный exit code. `maxOutputBytes` может ограничивать и streaming.
130
+ `stdin` — bytes с `maxStdinBytes` (default 1 MiB). `envPolicy` default `declared-only`: отсутствие env
131
+ означает пустой environment; `ambient` явно разрешает наследование. Cwd/executable policy остаётся
132
+ приложению; этот primitive запускает native executable и сам не создаёт shell.
133
+
134
+ Вывод читается с backpressure отдельно для stdout/stderr: один выполняющийся sink на канал плюс
135
+ bounded native Readable buffer; накопления всей истории при streaming нет. Callback получает
136
+ lifetime signal. Abort освобождает ожидание даже игнорирующего signal callback, но не способен
137
+ отменить сторонние действия, которые сам callback продолжит после abort. Sink обязан отменять свои
138
+ IO, не сохранять бесконечную историю и не изменять shared descriptor flags. После abort новых
139
+ callbacks owner не запускает. Normal completion ждёт всех bytes и sinks.
140
+
141
+ Cleanup: POSIX process group, TERM, `killGraceMs` (default 100, maximum 10 000), затем KILL;
142
+ `cleanupTimeoutMs` (default 2000, maximum 30 000) ограничивает ожидание close после сигналов.
143
+ На Darwin временный `EPERM` при сигнале группе может означать zombies, ещё не убранные
144
+ OS reaper. Повтор ограничен `cleanupTimeoutMs`; успех требует доставленного сигнала или
145
+ `ESRCH`, постоянный отказ сохраняется в cause. Закрытие каждого owned pipe и выход leader
146
+ наблюдаются отдельно, включая отсутствие общего `child.close` при teardown.
147
+ Сохраняется caller/sink причина; failure cleanup — `NativeCommandError` с `COMMAND_CLEANUP` и
148
+ AggregateError cause. Даже вышедший parent не исключает helper, удерживающий pipe. Windows
149
+ отказывает с `COMMAND_UNAVAILABLE`; потомок, самостоятельно покинувший группу через setsid,
150
+ лежит за границей этой capability. Это one-shot execution, не PTY, supervisor или daemon host.
151
+
152
+ ## Механическая адаптация
153
+
154
+ Atomic file wrapper заменяется одним вызовом `writeFileAtomic` с выбранными options. Bounded
155
+ regular-file read заменяется `files.read`; JSON/schema, receipt conflicts, journal rotation и
156
+ application locks сохраняются. Spawn/drain/capture/cancellation wrapper заменяется `runNativeCommand`;
157
+ shell policy, command admission, логирование, redaction, bytes→text decoder и operator sink сохраняются.
158
+ Длительные signal-only команды остаются streaming; bounded tar/capture получают свои явные limits.
159
+ Импорт leaf не делает установку всего npm package маленькой; выбирайте dependency после измерения
160
+ packed размера и dependency closure.
161
+ ## Resource-scoped launchers and shared owners
162
+
163
+ `onLeaderSettled(event, signal)` observes leader exit before inherited stdout/stderr pipes
164
+ close. A resource owner can stop its external scope there, allowing pipe drain to complete.
165
+ The callback runs once: `event.kind === 'exit'` carries observed `exitCode`/`signal`; a terminal
166
+ failure uses `kind: 'error'` and preserves its cause. Unavailable executables, output sink
167
+ failure and caller cancellation settle through the same owner. `cleanupTimeoutMs` bounds
168
+ the callback after settlement starts; it never becomes a deadline for signal-only execution.
169
+ Honor the settlement signal; arbitrary user promises cannot be forcibly cancelled.
170
+
171
+ ```ts
172
+ await runNativeCommand({
173
+ executable: launcher,
174
+ args,
175
+ signal: scope.signal,
176
+ onOutput: (bytes, channel, signal) => scope.write(bytes, channel, signal),
177
+ onLeaderSettled: (event, signal) => scope.settle(event, signal),
178
+ });
179
+ ```
180
+
181
+ `observeProcessInstance(pid)` returns `observed` with a `ProcessInstance`, or `unavailable`
182
+ with its cause. `probeProcessOwner(pid, recorded)` compares that evidence on the machine the
183
+ caller established: `matched`, `different-boot`, `reused-pid`, `pid-gone`, `legacy` or
184
+ `unavailable`. Missing/partial/denied evidence is not death. Supervisor tree fencing remains
185
+ the caller's job; a PID lifetime is not proof that its whole workload stopped.
186
+
187
+ An exclusive lock applies exact requested permissions through its creating descriptor,
188
+ independent of umask. Default `0600` stays private. Shared `0640` requires a common group and
189
+ traversable directories; another UID also needs directory write permission to reclaim a
190
+ proven-dead owner's file. Set `ownerlessGraceMs: null` to refuse every age-only reclaim,
191
+ including ownerless reclaim guards. The default remains 5000 ms for existing clients.
192
+ Восстановление stale reclaim guard использует тот же lock owner и следующий guard:
193
+ проверка inode и удаление выполняются под ним. Конкурирующий writer не обходит guard,
194
+ пока прежнее удаление приостановлено; live и unknown lifetime evidence остаются отказом.
195
+
196
+ ## Libraries with a Zod-only runtime
197
+
198
+ Public leaves keep optional peers out of their import closure. Installing `stitchkit` still
199
+ installs the complete package and ky; selective imports alone do not avoid that installation.
200
+ A library can use Stitchkit as a build dependency, bundle its selected public native imports,
201
+ keep Zod external, and publish the resulting implementation with its library artifact.
202
+
203
+ ```ts
204
+ // The library's build entry; all mechanisms remain owned by Stitchkit.
205
+ export { canonicalJson } from 'stitchkit/primitives';
206
+ export { writeFileAtomic, withExclusiveLock } from 'stitchkit/files';
207
+ export { observeProcessInstance, probeProcessOwner, runNativeCommand } from 'stitchkit/process';
208
+ ```
209
+
210
+ Bundle for Node-compatible execution with Zod external. Publish declarations from the same
211
+ version: carry their reachable relative `.d.ts` closure, rather than emitting references to
212
+ `stitchkit` that force clients to install it for types. A library exposing its own DTOs may
213
+ instead emit declarations for only that DTO contract. Keep the library's manifest at runtime
214
+ `dependencies: {}` and `peerDependencies: { zod: ... }` when that is its declared promise.
215
+
216
+ Darwin consumers need the published native binaries at the bundled loader's relative native
217
+ directory. A JS-only bundle is not portable evidence. Qualify the packed library outside its
218
+ build tree in Bun and Node, test its declarations, and check that importing an unbundled
219
+ Stitchkit leaf fails there. A separate schema-only entry must not import the native entry.
220
+
221
+ This delivery removes a runtime kernel installation; it does not remove the build-time
222
+ Stitchkit installation or create an independent lightweight npm package.
223
+
224
+ ==============================================================================
225
+ # API reference: `stitchkit/process` (docs/api/reference.md)
226
+ ==============================================================================
227
+
228
+ ## `stitchkit/process`
229
+
230
+ Native POSIX Bun/Node one-shot command execution. See [native IO](../guide/native-io.md).
231
+
232
+ | Export | Kind | Purpose |
233
+ |---|---|---|
234
+ | `runNativeCommand` | function | caller-lifetime streaming or explicitly bounded binary capture |
235
+ | `NativeCommandOptions` | type | executable/args, env policy, signal/deadline, bytes sink and finite cleanup limits |
236
+ | `NativeCommandResult` | type | observed nullable exit code/signal and captured stdout/stderr bytes |
237
+ | `NativeCommandError` | class | COMMAND_LIMIT, COMMAND_UNAVAILABLE or COMMAND_CLEANUP with internal cause |
238
+
239
+ | `NativeCommandSettlement` | type | observed leader `exit` or terminal `error`; `onLeaderSettled` runs once before drain, bounded by cleanupTimeoutMs, with a cancellable settlement signal |
240
+ | `observeProcessInstance` / `ProcessInstanceObservation` | function / type | Linux/Darwin lifetime observation; observed identity or unavailable with its original cause, never guessed absence |
241
+ | `ProcessInstanceSchema` / `ProcessInstance` | schema / type | platform, boot ID, namespace and process start identity from the shared native reader |
242
+ | `probeProcessOwner` / `ProcessOwnerEvidence` | function / type | compare lifetimes on one caller-established machine; matched, boot change, reused PID, gone, legacy or unavailable with cause |
@@ -68,6 +68,7 @@ own, recorded as an ADR.
68
68
  | `stitchkit/tools/connections` | server (Bun or Node) | evolving | consume external MCP servers and a bounded OpenAPI subset — typed reauthorization, a per-request host fence and per-call credentials |
69
69
  | `stitchkit/cli` | server | stable | `createCli` — the CLI transport, light (no MCP SDK / `ai`) |
70
70
  | `stitchkit/remote` | browser **and** server | stable | peer-free `implementRemote` for thin HTTP proxy processes |
71
+ | `stitchkit/process` | server (Bun or Node, POSIX) | evolving | finite native commands, byte sinks, bounded capture and process-group cancellation |
71
72
  | `stitchkit/files` | server (Bun or Node) | stable | peer-free managed local-file boundary |
72
73
  | `stitchkit/telegram` | server (Bun or Node) | evolving | peer-free Telegram platform primitives — Mini App `initData` verification, send-failure classification, resumable broadcasts, the operator channel with dedupe, local Bot API files, webhook ownership and durable webhook update intake |
73
74
  | `stitchkit/telegram/html` | browser and server | evolving | Telegram's HTML as a tree — clean any markup to what Telegram accepts, cut long text into valid parts by what the reader sees, check markup before it is sent |
package/llms/tools.txt CHANGED
@@ -409,6 +409,10 @@ where both exist the runtime's wins, because the run is recorded in its ledger.
409
409
 
410
410
  #### An effect in another system — `effect`, at most once
411
411
 
412
+ Standalone callers must hold an application-owned exclusive execution lease for the conversation/run
413
+ through the whole effect call. Sharing a ledger or separate ledger adapters is not an atomic claim;
414
+ the runtime supplies its run lease. Process-local coalescing only covers the same ledger object.
415
+
412
416
  `step` records its result **after** the body, so a body cut short by a crash
413
417
  runs again. For sending a message or starting a turn at a provider, a repeat is
414
418
  worse than a loss. The engine's `effect` records the intent **before** it runs:
@@ -431,6 +435,20 @@ throws, or a `reconcile` that throws or overruns `reconcileTimeoutMs` (default
431
435
  call. The proof is JSON of at most 64 KiB — the identity the recipient assigned,
432
436
  not a payload. → [ADR 0200](../decisions/0200-an-effect-is-recorded-before-it-runs.md)
433
437
 
438
+ Caller abort during reconcile releases the wait with `StepAbortedError`, leaves the intent and ignores
439
+ a late result. A coalesced caller can cancel its own await without cancelling the shared owner;
440
+ the first caller owns reconciliation handlers/deadline. `reconcileTimeoutMs` cannot exceed the native
441
+ timer range (2147483647 ms). It does not turn cancellation into proof of absence. Neither `null` nor an absence digest
442
+ proves that an earlier recipient request cannot complete later. Safe retries require recipient-side
443
+ idempotency or a verified fence. See [ADR 0224](../decisions/0224-native-primitives-have-one-owner.md).
444
+
445
+ Ledger reads are paged at 10 000 and incremental in one engine; a fresh engine replays the conversation
446
+ and retains its durable keys. This is not a bounded outstanding index/direct terminal receipt store.
447
+ For application delivery, `defineDomainEventDelivery` in `stitchkit/primitives` composes a caller-owned
448
+ atomic outbox claim with delivered/retryable/terminal/unknown states, bounded dispatch count (default
449
+ 100). Storage retention, per-key lookup, recipient proof and transactions remain in that adapter;
450
+ it is not an automatic cutover for a different reconciliation protocol.
451
+
434
452
  ### Typed MCP call metadata
435
453
 
436
454
  Contract handlers, runtime-tool handlers/factories, lifecycle and tool hooks all