stitchkit 0.70.0 → 0.70.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 (67) hide show
  1. package/dist/agent-runtime/coding-tool-files.d.ts.map +1 -1
  2. package/dist/agent-runtime/coding-tool-paths.d.ts +0 -1
  3. package/dist/agent-runtime/coding-tool-paths.d.ts.map +1 -1
  4. package/dist/agent-runtime/coding-tool-search-patch.d.ts.map +1 -1
  5. package/dist/agent-runtime/contained-files.d.ts +30 -7
  6. package/dist/agent-runtime/contained-files.d.ts.map +1 -1
  7. package/dist/agent-runtime/harness-control.d.ts +2 -0
  8. package/dist/agent-runtime/harness-control.d.ts.map +1 -1
  9. package/dist/agent-runtime/history-chronology.d.ts +19 -0
  10. package/dist/agent-runtime/history-chronology.d.ts.map +1 -0
  11. package/dist/agent-runtime/history.d.ts.map +1 -1
  12. package/dist/agent-runtime/run-execution.d.ts.map +1 -1
  13. package/dist/agent-runtime/terminal-status.d.ts.map +1 -1
  14. package/dist/agent-runtime-browser.js +51 -51
  15. package/dist/agent-runtime-coding-tools.js +31 -34
  16. package/dist/agent-runtime-harness.js +61 -17
  17. package/dist/agent-runtime-openrouter.js +2 -2
  18. package/dist/agent-runtime-sqlite-bun.js +5 -5
  19. package/dist/agent-runtime-sqlite-node.js +5 -5
  20. package/dist/agent-runtime.js +110 -110
  21. package/dist/application/server-resource.d.ts +1 -0
  22. package/dist/application/server-resource.d.ts.map +1 -1
  23. package/dist/application-grammy.js +2 -2
  24. package/dist/application.js +68 -65
  25. package/dist/browser/live-state.d.ts +126 -0
  26. package/dist/browser/live-state.d.ts.map +1 -0
  27. package/dist/cli.js +5 -5
  28. package/dist/contract/index.js +20 -20
  29. package/dist/declaration.d.ts +4 -0
  30. package/dist/declaration.d.ts.map +1 -1
  31. package/dist/declaration.js +22 -22
  32. package/dist/files.js +3 -3
  33. package/dist/index-1z5xxfst.js +397 -0
  34. package/dist/{index-vy5bjy07.js → index-4qfqy0m6.js} +21 -31
  35. package/dist/{index-dk6e56g0.js → index-7wpgrqa8.js} +37 -2
  36. package/dist/{index-10gbbbaa.js → index-9sx8tbz2.js} +1 -1
  37. package/dist/{index-38hs3a58.js → index-a01jky8m.js} +73 -8
  38. package/dist/{index-hsabxjz0.js → index-jw81xr75.js} +1 -1
  39. package/dist/{index-6wzr93cg.js → index-w0445741.js} +1 -1
  40. package/dist/index-y2s6h5bf.js +83 -0
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +667 -56
  44. package/dist/node.js +47 -27
  45. package/dist/observability/index.js +22 -22
  46. package/dist/react.js +2 -2
  47. package/dist/realtime/contract.d.ts +30 -11
  48. package/dist/realtime/contract.d.ts.map +1 -1
  49. package/dist/realtime/index.d.ts +1 -1
  50. package/dist/realtime/index.d.ts.map +1 -1
  51. package/dist/server/bun.d.ts.map +1 -1
  52. package/dist/server/index.js +88 -80
  53. package/dist/server/multipart.d.ts.map +1 -1
  54. package/dist/server/node.d.ts.map +1 -1
  55. package/dist/server/process-signals.d.ts.map +1 -1
  56. package/dist/server/shutdown.d.ts +2 -0
  57. package/dist/server/shutdown.d.ts.map +1 -1
  58. package/dist/testing/surface-conformance.d.ts +4 -2
  59. package/dist/testing/surface-conformance.d.ts.map +1 -1
  60. package/dist/testing.js +124 -113
  61. package/dist/tools.js +57 -57
  62. package/llms-full.txt +246 -17
  63. package/native/darwin-arm64.node +0 -0
  64. package/native/darwin-x64.node +0 -0
  65. package/package.json +3 -1
  66. package/dist/index-es0h4w26.js +0 -63
  67. package/dist/index-xbtcszs7.js +0 -202
package/llms-full.txt CHANGED
@@ -953,21 +953,26 @@ const server = createServer({ services, socket })
953
953
 
954
954
  const result = await server.shutdown({
955
955
  gracePeriodMs: 30_000,
956
+ realtimeCloseTimeoutMs: 1_000,
956
957
  forceTimeoutMs: 5_000,
957
958
  retryAfterSeconds: 5,
958
959
  signal: shutdownController.signal,
959
960
  })
960
961
  ```
961
962
 
962
- The first call closes HTTP and Socket.IO admission, then gives the complete
963
- graceful request/realtime/runtime chain one `gracePeriodMs` budget. If that
964
- budget or the external signal forces destructive teardown, `forceTimeoutMs`
965
- bounds physical completion separately. Repeated calls return the same Promise;
963
+ The first call closes HTTP and Socket.IO admission, then gives admitted HTTP/application work the
964
+ full `gracePeriodMs` budget. Once that work drains, `realtimeCloseTimeoutMs` (default `1_000`)
965
+ bounds WebSocket close handshakes inside the same outer deadline. Any upgraded sockets still open
966
+ at that boundary are terminated without shortening HTTP grace, and graceful runtime shutdown
967
+ continues. If the outer grace budget or external signal forces destructive teardown,
968
+ `forceTimeoutMs` bounds physical completion separately. Repeated calls return the same Promise;
966
969
  the first options win. New
967
970
  ordinary HTTP work receives `503`, `Retry-After` and `Connection: close` outside
968
971
  `wrapFetch`. `result.outcome` is `clean` or `forced`; a forced result preserves
969
972
  the pending snapshot and reason while final pending counters describe the
970
- post-close transport state. A graceful phase error still runs forced cleanup and
973
+ post-close transport state. `forcedWebSockets` counts both sockets terminated at the dedicated
974
+ realtime bound and sockets terminated by outer force; `pendingWebSocketsAtForce` is only the latter
975
+ snapshot, so a clean result can truthfully report a bounded realtime termination. A graceful phase error still runs forced cleanup and
971
976
  then rejects with the original error; a forced transport that cannot confirm
972
977
  completion before `forceTimeoutMs` rejects instead of reporting a false zero.
973
978
  `runtime` is a diagnostics escape hatch, not a second canonical stop path.
@@ -1774,6 +1779,14 @@ for a custom raw transport. It uses the same descriptor and returns
1774
1779
  `{ files, fields, rollback }`; contract endpoints should prefer the automatic
1775
1780
  dispatcher path.
1776
1781
 
1782
+ Both delivery modes preserve UTF-8 `filename` metadata, including browser `FormData` names. For
1783
+ interoperability, a UTF-8 percent-encoded `filename*` parameter takes precedence over `filename`;
1784
+ unsupported charsets or malformed encoding are rejected rather than falling back silently. Literal
1785
+ percent sequences in ordinary `filename` are not decoded. Filenames remain untrusted metadata,
1786
+ not safe filesystem paths. Part headers have a 64 KiB byte limit, are case-insensitive and reject
1787
+ duplicates, folding, invalid header names and control characters. Disposition parameters reject
1788
+ duplicates and unsafe field names; declared part sizes must be nonnegative safe decimal integers.
1789
+
1777
1790
  ### Rate limiting
1778
1791
 
1779
1792
  ```ts
@@ -4063,13 +4076,21 @@ revalidates the signature/tool call/input before the original direct tool reache
4063
4076
  fence. Reconnect and SQLite reopen require no promise registry. Remembered policy and a stronger
4064
4077
  cross-crash exactly-once guarantee remain application concerns.
4065
4078
 
4079
+ Approval requests suspend a call; they do not settle its result. History carries exact call and
4080
+ approval identity across assistant/tool records, so an approved result can precede the next signed
4081
+ request in a successor run. Automatic decisions follow the same chronology. Unknown/duplicate
4082
+ responses and results with a different call or tool name are invalid; dropping an invalid active
4083
+ approval input fails the run with a private diagnostic rather than starting a fresh model turn.
4084
+
4066
4085
  `stitchkit/agent-runtime/coding-tools` returns ordinary direct runtime tools named `read_file`,
4067
4086
  `write_file`, `search_files`, `apply_patch`, `run_command` and optional `read_output`. Every call passes a
4068
4087
  required host authorization callback. File paths are relative, bounded and contained after
4069
4088
  descriptor-relative resolution: each ancestor is opened without following symlinks and remains
4070
4089
  pinned through authorization and the filesystem effect. Reads revalidate the pinned file identity;
4071
4090
  writes and patches revalidate the pinned parent identity; search and resource discovery descend
4072
- only through opened directories. Linux uses `/proc/self/fd`; macOS and FreeBSD use `/dev/fd`.
4091
+ only through opened directories. Linux traverses `/proc/self/fd`; macOS uses the package's
4092
+ architecture-specific Node-API `openat` backend. Unsupported platforms, including FreeBSD until a
4093
+ real backend is qualified, fail closed. The package needs no install-time compiler or script.
4073
4094
  Other platforms fail these filesystem operations closed because Node exposes no equivalent
4074
4095
  portable directory-handle-relative API. Writes/edits reject symlink targets, content is strict
4075
4096
  UTF-8 and retained bytes are finite. Shell accepts a finite alias mapped by the host to an absolute executable plus an
@@ -6325,6 +6346,171 @@ The bound handle intentionally has no `connect()` or `disconnect()`. Its
6325
6346
  `on`/`emit`/`request`, rejection and timeout semantics are exactly the path used
6326
6347
  by `createRealtimeClient`; only transport construction/lifecycle differs.
6327
6348
 
6349
+ ## Snapshot + event state synchronization
6350
+
6351
+ `createLiveStateController` is the optional browser-safe state machine between a
6352
+ validated transport binding and any renderer. It solves one problem: install a
6353
+ snapshot and every event after that snapshot's consistency point without a race.
6354
+ It does not create a socket, choose a cursor, retry a transport, store history or
6355
+ invent ordering for the application.
6356
+
6357
+ ```ts
6358
+ import {
6359
+ createLiveStateController,
6360
+ type LiveStateEventDecision,
6361
+ type LiveStateSource,
6362
+ } from 'stitchkit'
6363
+
6364
+ type View = { revision: number; rows: readonly Row[] }
6365
+ type Change = { revision: number; row: Row }
6366
+
6367
+ const applyChange = (state: View, event: Change): LiveStateEventDecision<View> => {
6368
+ if (event.revision <= state.revision) return { outcome: 'duplicate' }
6369
+ if (event.revision !== state.revision + 1) return { outcome: 'gap' }
6370
+ return {
6371
+ outcome: 'applied',
6372
+ state: { revision: event.revision, rows: [...state.rows, event.row] },
6373
+ }
6374
+ }
6375
+
6376
+ const live = createLiveStateController({
6377
+ source,
6378
+ applyEvent: applyChange,
6379
+ maxBufferedEvents: 128,
6380
+ maxBufferedBytes: 256 * 1024,
6381
+ sizeOfEvent: encodedChangeBytes,
6382
+ })
6383
+
6384
+ const unsubscribe = live.subscribe(render)
6385
+ await live.start()
6386
+ // On a gap/overflow or source loss, choose when the UI should resync.
6387
+ const status = live.getSnapshot()
6388
+ if (status.phase === 'resync-required' || status.phase === 'unavailable') {
6389
+ await live.resync()
6390
+ }
6391
+
6392
+ unsubscribe()
6393
+ await live.close()
6394
+ ```
6395
+
6396
+ The source boundary is the important part:
6397
+
6398
+ ```ts
6399
+ interface LiveStateSource<State, Event> {
6400
+ open(input: {
6401
+ signal: AbortSignal
6402
+ onEvent(event: Event): void
6403
+ onUnavailable(): void
6404
+ }): Promise<{ snapshot: State; close(): void | Promise<void> }>
6405
+ }
6406
+ ```
6407
+
6408
+ `onEvent` is available before `open()` begins asynchronous work. By the time
6409
+ `open()` resolves, the source guarantees that every event after the returned
6410
+ snapshot's consistency point has already been or will be passed to that callback.
6411
+ The controller buffers early events within both explicit limits, installs the
6412
+ snapshot, drains in order, then becomes `live`. A late result from an earlier
6413
+ `resync()` generation is fenced. Non-cooperative caller-owned cleanup is asked to
6414
+ stop but cannot hold controller settlement.
6415
+
6416
+ `subscribe()` listeners are synchronous external-store notifications: read the
6417
+ published snapshot and schedule rendering elsewhere. A listener that returns a
6418
+ Promise is removed after its first call, preventing unresolved UI work from
6419
+ accumulating per event. Source `close()` must be idempotent because an abort-aware
6420
+ binding may have started cleanup before the controller calls it.
6421
+
6422
+ At most two physical source `open()` / `close()` operations in total may remain
6423
+ unsettled. If a caller-owned source ignores cancellation beyond that operation
6424
+ bound, `resync()` returns
6425
+ `unavailable/controller-capacity` without opening another generation. When a slot
6426
+ settles, the controller publishes `resync-required/controller-capacity`; the host
6427
+ may retry explicitly. This bounds controller-retained work without inventing a
6428
+ transport retry loop.
6429
+
6430
+ ### Socket.IO binding
6431
+
6432
+ Use an acknowledged operation whose server handler establishes the subscription
6433
+ before it captures/returns the snapshot. For example, the server can join the
6434
+ socket to the resource room, capture revision `N`, then acknowledge that snapshot;
6435
+ ordered Socket.IO frames after that point reach the already-installed handler:
6436
+
6437
+ ```ts
6438
+ const source: LiveStateSource<View, Change> = {
6439
+ async open({ signal, onEvent, onUnavailable }) {
6440
+ const offEvent = socket.on('view:changed', onEvent)
6441
+ const offConnection = socket.onConnectionChange((connected) => {
6442
+ if (!connected) onUnavailable()
6443
+ })
6444
+ let closed = false
6445
+ const close = () => {
6446
+ if (closed) return
6447
+ closed = true
6448
+ offEvent()
6449
+ offConnection()
6450
+ }
6451
+ signal.addEventListener('abort', close, { once: true })
6452
+
6453
+ try {
6454
+ const snapshot = await socket.request('view:open', { timeoutMs: 5_000 })
6455
+ return { snapshot, close }
6456
+ } catch (error) {
6457
+ close()
6458
+ throw error
6459
+ }
6460
+ },
6461
+ }
6462
+ ```
6463
+
6464
+ Socket.IO still owns physical reconnect. A reconnected transport only means the
6465
+ connection is open; call `resync()` when application state needs a fresh
6466
+ generation. If the application has replay, its source may resume from its opaque
6467
+ cursor and return an accepted consistency point. If history expired or a cursor is
6468
+ incompatible, the source must acquire a fresh authoritative snapshot or reject the
6469
+ open; the controller does not classify or compare opaque cursors itself.
6470
+
6471
+ ### One-way HTTP stream binding
6472
+
6473
+ NDJSON/SSE can use the same receiver semantics when **one response generation**
6474
+ starts with a schema-validated snapshot frame and every later frame is a validated
6475
+ event. Attach `parseNDJSON` or the typed contract-stream reader, parse the first
6476
+ frame before resolving `open()`, and pump remaining frames into `onEvent`. Abort
6477
+ that response in `close()`.
6478
+
6479
+ A separate `GET /snapshot` followed by `GET /events` is not this boundary: a
6480
+ change can land between the two requests and disappear unless the application
6481
+ supplies a watermark/replay protocol. The controller intentionally cannot make
6482
+ that uncoordinated recipe safe.
6483
+
6484
+ ### Rendering, cache and process lifecycle
6485
+
6486
+ For replaceable progress, let the reducer replace the absolute view at each
6487
+ accepted revision. For ordered records, append only the exact next revision and
6488
+ return `gap` otherwise. Both use the same controller; their ordering policy stays
6489
+ in their reducers. `getSnapshot()` + `subscribe()` works headlessly and with
6490
+ `useSyncExternalStore`. A React Query application can update its existing query
6491
+ cache from a subscriber after `phase === 'live'`; no second hook or store adapter
6492
+ is required. Cache `markFresh` windows suppress local echoes, while revision/cursor
6493
+ classification detects duplicates—those are different policies.
6494
+
6495
+ A server process that owns such a receiver can place `start()` and `close()` in
6496
+ an existing `defineManagedResource` and include it in `createApplication`.
6497
+ Readiness follows a successful `live` snapshot; shutdown calls `close()`. Stitchkit
6498
+ does not add another supervisor, reconnect loop or durable event database.
6499
+
6500
+ When migrating a hand-written receiver, remove only the superseded attach/snapshot
6501
+ race loop, retry timer and listener bookkeeping. Keep the application's schemas,
6502
+ authorization, reducer, cursor/replay policy and durable storage. Development
6503
+ proxying and Vite HMR remain frontend tooling; they are described in
6504
+ [frontend integrations](./frontend-integrations.md) and never travel through live
6505
+ application event envelopes.
6506
+
6507
+ The Agent harness control server follows the same ordering: it installs the
6508
+ conversation attachment before awaiting the authoritative snapshot and rolls the
6509
+ attachment back if that read fails. A host adapter installs its delivery callback,
6510
+ issues `attach`, and supplies the returned snapshot through its live-state source;
6511
+ the existing Agent cursor and view reducers still own runtime epochs, durable
6512
+ versions and transcript projection.
6513
+
6328
6514
  ### Request-response over realtime
6329
6515
 
6330
6516
  For an event with an `ack` schema, `request()` is the Promise form of the same
@@ -8899,11 +9085,13 @@ current one *up to* your target, and apply each snippet.
8899
9085
  ### Descriptor-backed Agent filesystem containment
8900
9086
 
8901
9087
  Built-in coding file/search tools and `createAgentHarnessFileResources` now require a runtime
8902
- that can address an opened directory descriptor: Linux `/proc/self/fd`, or macOS/FreeBSD
8903
- `/dev/fd`. This is what keeps a mutable parent rename or outside-symlink replacement from changing
9088
+ that can address an opened directory descriptor: Linux `/proc/self/fd`, or the packaged macOS
9089
+ Node-API backend added in 0.70.1. This is what keeps a mutable parent rename or outside-symlink replacement from changing
8904
9090
  the authorized target between validation and the actual effect.
8905
9091
 
8906
- No call-site change is needed on those platforms. On Windows or another platform without that
9092
+ No call-site change is needed on those platforms. The 0.70.0 notes incorrectly inferred macOS and
9093
+ FreeBSD support from `/dev/fd`; macOS is restored by 0.70.1 and FreeBSD remains unsupported. On
9094
+ Windows or another platform without that
8907
9095
  boundary, move the built-in filesystem operations to a supported worker or replace them with
8908
9096
  application-owned tools backed by an equivalent native handle API. They fail closed rather than
8909
9097
  falling back to path spelling. `run_command` remains separately available under its explicit
@@ -11262,7 +11450,7 @@ description: One machine-readable statement a repository makes about itself —
11262
11450
  type: architecture
11263
11451
  status: active
11264
11452
  created: 2026-08-25
11265
- updated: 2026-08-25
11453
+ updated: 2026-08-30
11266
11454
  ---
11267
11455
 
11268
11456
  # Project declaration
@@ -11297,6 +11485,32 @@ mode that produces a running, wrong deployment rather than an error.
11297
11485
 
11298
11486
  ## Why declare yourself
11299
11487
 
11488
+ ### Identity is not product membership
11489
+
11490
+ The exported names `ProjectDeclaration` and `ProjectIdentity` describe the buildable source/artifact
11491
+ declared by a repository. A singular `identity` does **not** make a product project and a repository
11492
+ the same entity, nor does it identify a local checkout or a harness session.
11493
+
11494
+ | Entity | Meaning and owner |
11495
+ | --- | --- |
11496
+ | Product project | A product boundary whose repository membership is explicitly maintained outside this declaration |
11497
+ | Repository | Versioned source; its declaration describes roles, build outputs and release requirements |
11498
+ | Checkout | A local working copy of a repository revision; local paths and credentials belong to its host |
11499
+ | Harness workspace | The host-selected working scope for a session, not an implied product or membership registry |
11500
+
11501
+ Membership is many-to-many. For example, an external registry may declare product A includes
11502
+ repositories `service-a` and `shared-library`, while product B includes `service-b` and the same
11503
+ `shared-library`. Both products can read the library's unchanged declaration. Installing that library
11504
+ as a dependency, placing a checkout beside another, or naming a harness workspace does not create
11505
+ membership. The embedding product/registry owns these explicit relationships and their access policy.
11506
+
11507
+ A private companion repository can be part of a product without becoming a separate product. Its
11508
+ relationship and working context stay in an authorized private registry, never in a potentially public
11509
+ library declaration. No registry or membership fields are required here; the declaration remains
11510
+ optional. Existing exports and schema version 1 are unchanged.
11511
+
11512
+ ### One statement, several readers
11513
+
11300
11514
  Because the statements exist either way, and without a schema they exist three
11301
11515
  times. A repository already says how many roles it runs (in a process file),
11302
11516
  which variables it needs (in a Zod schema), what it builds (in a script) and
@@ -11492,6 +11706,16 @@ The browser-and-server entrypoint. Re-exports everything from
11492
11706
  | `defineRealtimeContract` | function | Zod-first shared Socket.IO event contract — [guide](../guide/realtime.md#zod-first-event-contract) |
11493
11707
  | `createRealtimeClient` | function | inferred, runtime-validated Socket.IO client — [guide](../guide/realtime.md#client--createrealtimeclient) |
11494
11708
  | `bindRealtimeClient` | function | bind contract validation and typed acknowledgements to an existing Stitchkit client transport without owning its lifecycle |
11709
+ | `createLiveStateController` | function | keep typed application state current across one source-owned snapshot/event generation with finite pre-snapshot buffering, generation fencing and explicit resync — [guide](../guide/realtime.md#snapshot--event-state-synchronization) |
11710
+ | `LiveStateController` | _type_ | renderer-neutral `start` / `resync` / `getSnapshot` / `subscribe` / `close` handle |
11711
+ | `LiveStateControllerConfig` | _type_ | typed source, reducer, explicit event/byte bounds, event sizing and isolated error hooks |
11712
+ | `LiveStateControllerSnapshot` / `LiveStateControllerStatus` | _types_ | current value plus phase, generation, buffer and application/duplicate/gap/refusal counters |
11713
+ | `LiveStateControllerStatusSchema` | schema | strict runtime validation for controller status metadata |
11714
+ | `LiveStatePhaseSchema` / `LiveStatePhase` | schema / _type_ | `idle`, `opening`, `live`, `resync-required`, `unavailable` or `closed` |
11715
+ | `LiveStateStopReasonSchema` / `LiveStateStopReason` | schema / _type_ | explicit gap, overflow, source loss, controller failure and bounded `controller-capacity` reasons |
11716
+ | `LiveStateEventDecision` | _type_ | provider-owned reducer result: applied state, duplicate or gap |
11717
+ | `LiveStateSource` / `LiveStateSourceOpenInput` / `LiveStateSourceOpenResult` | _types_ | host binding for one continuous snapshot/event boundary; transport retry and cursor semantics remain host-owned |
11718
+ | `LiveStateControllerError` / `LiveStateSubscriberError` | _types_ | isolated observer failure payloads that do not change source or subscriber truth |
11495
11719
  | `createRetainedTopics` | function | retained last-value store for sticky events — [guide](../guide/realtime.md#sticky-events) |
11496
11720
  | `parseSSE` | function | parse an SSE `Response` into an async generator — [guide](../guide/client.md#sse) |
11497
11721
  | `parseNDJSON` | function | parse bounded fatal-UTF-8 NDJSON; blank keep-alives are skipped and `finalLine: 'require-newline'` can make the delimiter mandatory — [guide](../guide/client.md#ndjson) |
@@ -11531,7 +11755,7 @@ The browser-and-server entrypoint. Re-exports everything from
11531
11755
  | `RealtimeEmitArguments` | _type_ | emit arguments including an inferred acknowledgement callback |
11532
11756
  | `RealtimeEventHandler` | _type_ | handler inferred from an event definition |
11533
11757
  | `InferRealtimeEventMap` | _type_ | inferred Socket.IO-compatible event map |
11534
- | `RealtimeRejectDirection` | _type_ | server/client inbound/outbound rejection direction |
11758
+ | `RealtimeRejectDirection` / `RealtimeRejectPhase` / `RealtimeRejectReason` / `RealtimeRejectFault` | _types_ | canonical inferred rejection direction, validation phase, reason and fault classification |
11535
11759
  | `RealtimeRejectedEvent` | _type_ | structured rejected event with event, direction, phase, reason and fault |
11536
11760
  | `RealtimeRejectedEventHook` | _type_ | sync/async observer for structured realtime rejections |
11537
11761
  | `ValidatedRealtimeSocket` | _type_ | runtime-validating `on`/`emit` surface inferred from registries; `emit` returns "accepted by the transport" (`false` only for a client-side disconnected drop) |
@@ -11697,9 +11921,9 @@ Also re-exports the error helpers from `stitchkit/contract`.
11697
11921
  | `UnixListenConfig` | _type_ | unix listener — a socket path, or `{ path, mode }` to tighten the file mode after listen |
11698
11922
  | `BunServerHandle` | _type_ | managed Bun handle (`url`, `port`, `runtime`, `status`, `shutdown`) |
11699
11923
  | `ManagedServerHandle` | _type_ | shared lifecycle shape generic over the runtime escape hatch |
11700
- | `ShutdownOptionsSchema` / `ShutdownOptions` | schema / _type_ | one graceful budget, bounded forced-completion timeout, retry hint and optional external abort signal |
11924
+ | `ShutdownOptionsSchema` / `ShutdownOptions` | schema / _type_ | HTTP/application grace, a separate WebSocket close-handshake bound, bounded forced-completion timeout, retry hint and optional external abort signal |
11701
11925
  | `ShutdownStatusSchema` / `ShutdownStatus` | schema / _type_ | live state and request/WebSocket counters |
11702
- | `ShutdownResultSchema` / `ShutdownResult` | schema / _type_ | clean/forced result with final counters and at-force snapshots |
11926
+ | `ShutdownResultSchema` / `ShutdownResult` | schema / _type_ | clean/forced result with final counters, outer-force snapshots and `forcedWebSockets` including bounded realtime terminations |
11703
11927
  | `ShutdownStateSchema` / `ShutdownState` | schema / _type_ | managed lifecycle state machine |
11704
11928
  | `ServiceDef` | _type_ | the result of `implement` |
11705
11929
  | `MethodDef` | _type_ | one resolved endpoint inside a service |
@@ -11887,7 +12111,7 @@ cutovers are covered by the executable
11887
12111
  | `ManagedResourceDependency` | _type_ | a dependency named by id or given as the resource itself — the second form is what `context.use(...)` can type |
11888
12112
  | `ManagedResourcePublished` | _type_ | the value type `context.use(resource)` returns, recovered from that resource's own `start` |
11889
12113
  | `ManagedResourcePublishesNoValue` | _type_ | what `context.use(...)` returns for a resource that publishes nothing — a branded refusal rather than `never`, so reading it does not silently compile |
11890
- | `ManagedServerResourceConfig` | _type_ | the server or a sync/async factory receiving `ManagedResourceContext`, plus stable ID, dependencies and shutdown policy for `managedServerResource` |
12114
+ | `ManagedServerResourceConfig` | _type_ | the server or a sync/async factory receiving `ManagedResourceContext`, plus stable ID, dependencies and server shutdown policy (`retryAfterSeconds`, `realtimeCloseTimeoutMs`) |
11891
12115
  | `ManagedServerResource` | _type_ | the resource `managedServerResource` returns, whose `start` publishes the `ManagedServerHandle` |
11892
12116
  | `ApplicationHealthHandlerOptions` / `ApplicationHealthHandlerOptionsSchema` | _type_ / schema | liveness/readiness selection and sanitized `Retry-After` policy |
11893
12117
  | `ApplicationOperationalHandlers` / `ApplicationOperationalHandlersOptions` / `ApplicationOperationalHandlersOptionsSchema` | _type_ / schema | conventional status/readiness/liveness route surface and shared retry policy |
@@ -12229,7 +12453,7 @@ and introduces no store, queue or model-provider implementation of its own.
12229
12453
  | `AgentHarnessFileResources` | _type_ | loader plus direct `read_resource` definition for lazy exact content |
12230
12454
  | `createAgentHarnessControlServer` | function | transport-neutral correlated requests, observer attachments and exclusive controller leases |
12231
12455
  | `AgentHarnessControlServer` / `AgentHarnessControlConnection` | _type_ | host server and detachable connection lifecycle; `deliver` is serialized, while required out-of-band `onOverflow` closes/aborts a slow transport before reconnect |
12232
- | `AgentHarnessControlServerConfig` | _type_ | explicit per-connection pending-event bound for failure-isolated control delivery |
12456
+ | `AgentHarnessControlServerConfig` | _type_ | explicit per-connection pending-event and server-wide concurrent attachment-snapshot bounds for failure-isolated control delivery |
12233
12457
  | `AgentHarnessPendingApproval` / `AgentHarnessApprovalDecision` | _type_ | exact durable pending request and allow/deny successor input |
12234
12458
 
12235
12459
  Resources default to at most 64 entries, 1 MiB of total UTF-8 text and 128 diagnostics. Duplicate
@@ -12244,7 +12468,7 @@ artifact store is supplied, `read_output`.
12244
12468
 
12245
12469
  | Export | Kind | Summary |
12246
12470
  |--------|------|---------|
12247
- | `createAgentCodingTools` | function | construct direct host-authorized bounded file, search, guarded patch, shell and artifact runtime-tool definitions; filesystem operations require Linux `/proc/self/fd` or macOS/FreeBSD `/dev/fd` descriptor paths and otherwise fail closed |
12471
+ | `createAgentCodingTools` | function | construct direct host-authorized bounded file, search, guarded patch, shell and artifact runtime-tool definitions; filesystem operations use Linux `/proc/self/fd` or the packaged macOS Node-API backend and otherwise fail closed |
12248
12472
  | `AgentCodingToolDefinition` | _type_ | peer-free structural direct-tool shape accepted by the canonical runtime-tool surface |
12249
12473
  | `AgentCodingToolConfig` | _type_ | absolute root, required authorization callback, finite executable alias map, exact child environment and optional limits |
12250
12474
  | `AgentCodingToolAuthorizationSchema` / `AgentCodingToolAuthorization` | schema / _type_ | discriminated read/write/search/patch/shell/artifact decision presented to host policy before effect |
@@ -12724,7 +12948,7 @@ handler pipeline without opening a TCP port.
12724
12948
  | `DefineRealtimeProbeConfig` | _type_ | name, canonical scenario, explicit fixture and expected realtime outcome |
12725
12949
  | `CreateRealtimeProbeDriverConfig` | _type_ | per-scenario foreign-transport binder and optional handler-call counter |
12726
12950
  | `RealtimeProbeAdapter` | _type_ | connected-state observation, scenario invocation and subscription-only cleanup |
12727
- | `RealtimeProbeFixture` / `RealtimeProbeScenario` | _type_ | driver input and supported event/ack/invalid/disconnect/timeout scenario vocabulary |
12951
+ | `RealtimeProbeFixture` / `RealtimeProbeScenario` | _type_ | driver input and supported event/ack/local-invalid/peer-refusal/disconnect/timeout scenario vocabulary |
12728
12952
  | `RealtimeRejectionObservation` | _type_ | parsed structured realtime rejection observation |
12729
12953
  | `RealtimeDisconnectObservation` | _type_ | normalized physical timing of a realtime disconnect |
12730
12954
  | `TransportObservation` | _type_ | validated normalized driver result |
@@ -12775,6 +12999,11 @@ by the scaffolder that writes the first copy, and by whatever builds a source an
12775
12999
  binds the artifact into a deployment. It ships from the framework so those
12776
13000
  readers cannot hold different copies of the same schema.
12777
13001
 
13002
+ `identity` identifies the repository-local buildable source/artifact, not a product project, checkout
13003
+ or harness workspace. Product↔repository membership is explicit and many-to-many, owned by an
13004
+ external registry; dependency edges do not establish membership. Private companion context is never
13005
+ required in this public schema. See [identity boundaries](../guide/declaration.md#identity-is-not-product-membership).
13006
+
12778
13007
  **Declaring yourself is optional.** A project with no `project.json` is a
12779
13008
  complete project: nothing else in the framework imports this entrypoint, no
12780
13009
  build, test or start path looks for a declaration, and its absence is never an
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.70.0",
3
+ "version": "0.70.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",
@@ -38,6 +38,7 @@
38
38
  "types": "./dist/index.d.ts",
39
39
  "files": [
40
40
  "dist",
41
+ "native/*.node",
41
42
  "llms.txt",
42
43
  "llms-full.txt",
43
44
  "README.md",
@@ -141,6 +142,7 @@
141
142
  "access": "public"
142
143
  },
143
144
  "scripts": {
145
+ "build:native-contained-files": "node scripts/build-contained-files-native.mjs",
144
146
  "check": "bun x tsc --noEmit",
145
147
  "build:browser": "bun build src/index.ts src/react.ts src/contract/index.ts src/declaration.ts src/agent-runtime-browser.ts --outdir dist --target node --packages external --splitting --root src",
146
148
  "build:server": "bun build src/server/index.ts src/node.ts src/tools.ts src/tool-invoker.ts src/cli.ts src/remote.ts src/files.ts src/testing.ts src/observability/index.ts src/agent-runtime.ts src/agent-runtime-harness.ts src/agent-runtime-coding-tools.ts src/agent-runtime-openrouter.ts src/agent-runtime-sqlite-bun.ts src/agent-runtime-sqlite-node.ts src/application.ts src/application-grammy.ts src/application-opentelemetry.ts --outdir dist --target node --packages external --splitting --root src",
@@ -1,63 +0,0 @@
1
- // src/agent-runtime/terminal-status.ts
2
- function isSpeakableAssistantStatus(status) {
3
- return status === "completed" || status === "interrupted" || status === "committed";
4
- }
5
- function isAssistantHistoryEvidence(status, policy) {
6
- return isSpeakableAssistantStatus(status) || status === "failed" && policy?.failedAssistant === "assistant-marked";
7
- }
8
- function isCompleteAgentHistoryTurn(messages, policy) {
9
- if (messages[0]?.role !== "user")
10
- return false;
11
- const calls = new Set;
12
- const completed = new Set;
13
- const approvals = new Map;
14
- let assistantCount = 0;
15
- for (const message of messages) {
16
- if (message.role === "assistant") {
17
- if (!isAssistantHistoryEvidence(message.status, policy))
18
- return false;
19
- assistantCount += 1;
20
- }
21
- for (const part of message.parts) {
22
- if (part.type === "tool-call") {
23
- if (calls.has(part.callId))
24
- return false;
25
- calls.add(part.callId);
26
- } else if (part.type === "tool-approval-request") {
27
- if (!calls.has(part.callId) || completed.has(part.callId) || approvals.has(part.approvalId))
28
- return false;
29
- if ([...approvals.values()].some((approval) => approval.callId === part.callId))
30
- return false;
31
- approvals.set(part.approvalId, { callId: part.callId, answered: false });
32
- } else if (part.type === "tool-approval-response") {
33
- const approval = approvals.get(part.approvalId);
34
- if (!approval || approval.answered)
35
- return false;
36
- approval.answered = true;
37
- } else if (part.type === "tool-result") {
38
- if (!calls.has(part.callId) || completed.has(part.callId))
39
- return false;
40
- const approval = [...approvals.values()].find((entry) => entry.callId === part.callId);
41
- if (approval && !approval.answered)
42
- return false;
43
- completed.add(part.callId);
44
- }
45
- }
46
- }
47
- return assistantCount > 0 && calls.size === completed.size && [...approvals.values()].every((approval) => approval.answered);
48
- }
49
- function assistantStatus(reason) {
50
- if (reason === "success" || reason === "policy_stop" || reason === "provider_stop") {
51
- return "completed";
52
- }
53
- if (reason === "superseded")
54
- return "superseded";
55
- if (reason === "absorbed")
56
- return "superseded";
57
- if (reason === "interrupted" || reason === "cancelled" || reason === "shutdown") {
58
- return "interrupted";
59
- }
60
- return "failed";
61
- }
62
-
63
- export { isAssistantHistoryEvidence, isCompleteAgentHistoryTurn, assistantStatus };