stitchkit 0.70.1 → 0.70.3

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 (53) hide show
  1. package/dist/agent-runtime/harness-control.d.ts +2 -0
  2. package/dist/agent-runtime/harness-control.d.ts.map +1 -1
  3. package/dist/agent-runtime/history-chronology.d.ts +19 -0
  4. package/dist/agent-runtime/history-chronology.d.ts.map +1 -0
  5. package/dist/agent-runtime/history.d.ts.map +1 -1
  6. package/dist/agent-runtime/run-execution.d.ts.map +1 -1
  7. package/dist/agent-runtime/terminal-status.d.ts.map +1 -1
  8. package/dist/agent-runtime-browser.js +51 -51
  9. package/dist/agent-runtime-coding-tools.js +2 -2
  10. package/dist/agent-runtime-harness.js +60 -16
  11. package/dist/agent-runtime-openrouter.js +2 -2
  12. package/dist/agent-runtime-sqlite-bun.js +5 -5
  13. package/dist/agent-runtime-sqlite-node.js +5 -5
  14. package/dist/agent-runtime.js +110 -110
  15. package/dist/application-grammy.js +2 -2
  16. package/dist/application.js +63 -63
  17. package/dist/browser/live-state.d.ts +126 -0
  18. package/dist/browser/live-state.d.ts.map +1 -0
  19. package/dist/cli.js +5 -5
  20. package/dist/contract/index.js +20 -20
  21. package/dist/declaration.d.ts +4 -0
  22. package/dist/declaration.d.ts.map +1 -1
  23. package/dist/declaration.js +22 -22
  24. package/dist/files.js +3 -3
  25. package/dist/{index-4g196jmf.js → index-1zxf9x2z.js} +112 -20
  26. package/dist/{index-vy5bjy07.js → index-4qfqy0m6.js} +21 -31
  27. package/dist/{index-10gbbbaa.js → index-9sx8tbz2.js} +1 -1
  28. package/dist/{index-hsabxjz0.js → index-jw81xr75.js} +1 -1
  29. package/dist/index-y2s6h5bf.js +83 -0
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +667 -56
  33. package/dist/node.js +26 -26
  34. package/dist/observability/index.js +22 -22
  35. package/dist/react.js +2 -2
  36. package/dist/realtime/contract.d.ts +30 -11
  37. package/dist/realtime/contract.d.ts.map +1 -1
  38. package/dist/realtime/index.d.ts +1 -1
  39. package/dist/realtime/index.d.ts.map +1 -1
  40. package/dist/server/create.d.ts.map +1 -1
  41. package/dist/server/error-dispatch.d.ts +13 -0
  42. package/dist/server/error-dispatch.d.ts.map +1 -0
  43. package/dist/server/index.js +79 -79
  44. package/dist/server/multipart.d.ts.map +1 -1
  45. package/dist/server/types.d.ts +6 -0
  46. package/dist/server/types.d.ts.map +1 -1
  47. package/dist/testing/surface-conformance.d.ts +4 -2
  48. package/dist/testing/surface-conformance.d.ts.map +1 -1
  49. package/dist/testing.js +122 -111
  50. package/dist/tools.js +57 -57
  51. package/llms-full.txt +237 -9
  52. package/package.json +1 -1
  53. package/dist/index-es0h4w26.js +0 -63
package/llms-full.txt CHANGED
@@ -1218,10 +1218,18 @@ createServer({
1218
1218
  preconditions here, not authentication.
1219
1219
  - **`afterHandle`** — receives the handler result; return a replacement to
1220
1220
  transform it.
1221
- - **`onError`** — receives any thrown error; return a `Response` to customise
1222
- the error body. Without it, errors render through the standard envelope. A
1223
- confirmed client disconnect is a transport cancellation rather than an
1224
- application error and deliberately bypasses this hook.
1221
+ - **`onError`** — for a matched contract route, the group's hook runs first,
1222
+ then the global hook, then the standard error envelope. The first returned
1223
+ `Response` wins (including its status and headers); an absent hook, `undefined`
1224
+ return or thrown/rejected hook continues to the next level with the **original
1225
+ error** and the same context and endpoint. A failing hook is diagnosed through
1226
+ `logging.logger.error` (or the internal console fallback), never substituted
1227
+ into the client response. This covers path/body validation, authorization,
1228
+ before/after hooks and handler errors, including raw-response contract handlers.
1229
+ Global `onRequest` failures, `rawRoutes` and unmatched 404/405 have no matched
1230
+ contract group and use only the global hook. A confirmed client disconnect
1231
+ deliberately bypasses both hooks; errors after streaming headers have been
1232
+ sent keep their existing stream-error path.
1225
1233
 
1226
1234
  Hooks see `RuntimeContext` (loose types); handlers see `HandlerContext` (typed).
1227
1235
  That split is deliberate — see [ADR 0003](../decisions/0003-two-context-types.md).
@@ -1779,6 +1787,14 @@ for a custom raw transport. It uses the same descriptor and returns
1779
1787
  `{ files, fields, rollback }`; contract endpoints should prefer the automatic
1780
1788
  dispatcher path.
1781
1789
 
1790
+ Both delivery modes preserve UTF-8 `filename` metadata, including browser `FormData` names. For
1791
+ interoperability, a UTF-8 percent-encoded `filename*` parameter takes precedence over `filename`;
1792
+ unsupported charsets or malformed encoding are rejected rather than falling back silently. Literal
1793
+ percent sequences in ordinary `filename` are not decoded. Filenames remain untrusted metadata,
1794
+ not safe filesystem paths. Part headers have a 64 KiB byte limit, are case-insensitive and reject
1795
+ duplicates, folding, invalid header names and control characters. Disposition parameters reject
1796
+ duplicates and unsafe field names; declared part sizes must be nonnegative safe decimal integers.
1797
+
1782
1798
  ### Rate limiting
1783
1799
 
1784
1800
  ```ts
@@ -4068,6 +4084,12 @@ revalidates the signature/tool call/input before the original direct tool reache
4068
4084
  fence. Reconnect and SQLite reopen require no promise registry. Remembered policy and a stronger
4069
4085
  cross-crash exactly-once guarantee remain application concerns.
4070
4086
 
4087
+ Approval requests suspend a call; they do not settle its result. History carries exact call and
4088
+ approval identity across assistant/tool records, so an approved result can precede the next signed
4089
+ request in a successor run. Automatic decisions follow the same chronology. Unknown/duplicate
4090
+ responses and results with a different call or tool name are invalid; dropping an invalid active
4091
+ approval input fails the run with a private diagnostic rather than starting a fresh model turn.
4092
+
4071
4093
  `stitchkit/agent-runtime/coding-tools` returns ordinary direct runtime tools named `read_file`,
4072
4094
  `write_file`, `search_files`, `apply_patch`, `run_command` and optional `read_output`. Every call passes a
4073
4095
  required host authorization callback. File paths are relative, bounded and contained after
@@ -6332,6 +6354,171 @@ The bound handle intentionally has no `connect()` or `disconnect()`. Its
6332
6354
  `on`/`emit`/`request`, rejection and timeout semantics are exactly the path used
6333
6355
  by `createRealtimeClient`; only transport construction/lifecycle differs.
6334
6356
 
6357
+ ## Snapshot + event state synchronization
6358
+
6359
+ `createLiveStateController` is the optional browser-safe state machine between a
6360
+ validated transport binding and any renderer. It solves one problem: install a
6361
+ snapshot and every event after that snapshot's consistency point without a race.
6362
+ It does not create a socket, choose a cursor, retry a transport, store history or
6363
+ invent ordering for the application.
6364
+
6365
+ ```ts
6366
+ import {
6367
+ createLiveStateController,
6368
+ type LiveStateEventDecision,
6369
+ type LiveStateSource,
6370
+ } from 'stitchkit'
6371
+
6372
+ type View = { revision: number; rows: readonly Row[] }
6373
+ type Change = { revision: number; row: Row }
6374
+
6375
+ const applyChange = (state: View, event: Change): LiveStateEventDecision<View> => {
6376
+ if (event.revision <= state.revision) return { outcome: 'duplicate' }
6377
+ if (event.revision !== state.revision + 1) return { outcome: 'gap' }
6378
+ return {
6379
+ outcome: 'applied',
6380
+ state: { revision: event.revision, rows: [...state.rows, event.row] },
6381
+ }
6382
+ }
6383
+
6384
+ const live = createLiveStateController({
6385
+ source,
6386
+ applyEvent: applyChange,
6387
+ maxBufferedEvents: 128,
6388
+ maxBufferedBytes: 256 * 1024,
6389
+ sizeOfEvent: encodedChangeBytes,
6390
+ })
6391
+
6392
+ const unsubscribe = live.subscribe(render)
6393
+ await live.start()
6394
+ // On a gap/overflow or source loss, choose when the UI should resync.
6395
+ const status = live.getSnapshot()
6396
+ if (status.phase === 'resync-required' || status.phase === 'unavailable') {
6397
+ await live.resync()
6398
+ }
6399
+
6400
+ unsubscribe()
6401
+ await live.close()
6402
+ ```
6403
+
6404
+ The source boundary is the important part:
6405
+
6406
+ ```ts
6407
+ interface LiveStateSource<State, Event> {
6408
+ open(input: {
6409
+ signal: AbortSignal
6410
+ onEvent(event: Event): void
6411
+ onUnavailable(): void
6412
+ }): Promise<{ snapshot: State; close(): void | Promise<void> }>
6413
+ }
6414
+ ```
6415
+
6416
+ `onEvent` is available before `open()` begins asynchronous work. By the time
6417
+ `open()` resolves, the source guarantees that every event after the returned
6418
+ snapshot's consistency point has already been or will be passed to that callback.
6419
+ The controller buffers early events within both explicit limits, installs the
6420
+ snapshot, drains in order, then becomes `live`. A late result from an earlier
6421
+ `resync()` generation is fenced. Non-cooperative caller-owned cleanup is asked to
6422
+ stop but cannot hold controller settlement.
6423
+
6424
+ `subscribe()` listeners are synchronous external-store notifications: read the
6425
+ published snapshot and schedule rendering elsewhere. A listener that returns a
6426
+ Promise is removed after its first call, preventing unresolved UI work from
6427
+ accumulating per event. Source `close()` must be idempotent because an abort-aware
6428
+ binding may have started cleanup before the controller calls it.
6429
+
6430
+ At most two physical source `open()` / `close()` operations in total may remain
6431
+ unsettled. If a caller-owned source ignores cancellation beyond that operation
6432
+ bound, `resync()` returns
6433
+ `unavailable/controller-capacity` without opening another generation. When a slot
6434
+ settles, the controller publishes `resync-required/controller-capacity`; the host
6435
+ may retry explicitly. This bounds controller-retained work without inventing a
6436
+ transport retry loop.
6437
+
6438
+ ### Socket.IO binding
6439
+
6440
+ Use an acknowledged operation whose server handler establishes the subscription
6441
+ before it captures/returns the snapshot. For example, the server can join the
6442
+ socket to the resource room, capture revision `N`, then acknowledge that snapshot;
6443
+ ordered Socket.IO frames after that point reach the already-installed handler:
6444
+
6445
+ ```ts
6446
+ const source: LiveStateSource<View, Change> = {
6447
+ async open({ signal, onEvent, onUnavailable }) {
6448
+ const offEvent = socket.on('view:changed', onEvent)
6449
+ const offConnection = socket.onConnectionChange((connected) => {
6450
+ if (!connected) onUnavailable()
6451
+ })
6452
+ let closed = false
6453
+ const close = () => {
6454
+ if (closed) return
6455
+ closed = true
6456
+ offEvent()
6457
+ offConnection()
6458
+ }
6459
+ signal.addEventListener('abort', close, { once: true })
6460
+
6461
+ try {
6462
+ const snapshot = await socket.request('view:open', { timeoutMs: 5_000 })
6463
+ return { snapshot, close }
6464
+ } catch (error) {
6465
+ close()
6466
+ throw error
6467
+ }
6468
+ },
6469
+ }
6470
+ ```
6471
+
6472
+ Socket.IO still owns physical reconnect. A reconnected transport only means the
6473
+ connection is open; call `resync()` when application state needs a fresh
6474
+ generation. If the application has replay, its source may resume from its opaque
6475
+ cursor and return an accepted consistency point. If history expired or a cursor is
6476
+ incompatible, the source must acquire a fresh authoritative snapshot or reject the
6477
+ open; the controller does not classify or compare opaque cursors itself.
6478
+
6479
+ ### One-way HTTP stream binding
6480
+
6481
+ NDJSON/SSE can use the same receiver semantics when **one response generation**
6482
+ starts with a schema-validated snapshot frame and every later frame is a validated
6483
+ event. Attach `parseNDJSON` or the typed contract-stream reader, parse the first
6484
+ frame before resolving `open()`, and pump remaining frames into `onEvent`. Abort
6485
+ that response in `close()`.
6486
+
6487
+ A separate `GET /snapshot` followed by `GET /events` is not this boundary: a
6488
+ change can land between the two requests and disappear unless the application
6489
+ supplies a watermark/replay protocol. The controller intentionally cannot make
6490
+ that uncoordinated recipe safe.
6491
+
6492
+ ### Rendering, cache and process lifecycle
6493
+
6494
+ For replaceable progress, let the reducer replace the absolute view at each
6495
+ accepted revision. For ordered records, append only the exact next revision and
6496
+ return `gap` otherwise. Both use the same controller; their ordering policy stays
6497
+ in their reducers. `getSnapshot()` + `subscribe()` works headlessly and with
6498
+ `useSyncExternalStore`. A React Query application can update its existing query
6499
+ cache from a subscriber after `phase === 'live'`; no second hook or store adapter
6500
+ is required. Cache `markFresh` windows suppress local echoes, while revision/cursor
6501
+ classification detects duplicates—those are different policies.
6502
+
6503
+ A server process that owns such a receiver can place `start()` and `close()` in
6504
+ an existing `defineManagedResource` and include it in `createApplication`.
6505
+ Readiness follows a successful `live` snapshot; shutdown calls `close()`. Stitchkit
6506
+ does not add another supervisor, reconnect loop or durable event database.
6507
+
6508
+ When migrating a hand-written receiver, remove only the superseded attach/snapshot
6509
+ race loop, retry timer and listener bookkeeping. Keep the application's schemas,
6510
+ authorization, reducer, cursor/replay policy and durable storage. Development
6511
+ proxying and Vite HMR remain frontend tooling; they are described in
6512
+ [frontend integrations](./frontend-integrations.md) and never travel through live
6513
+ application event envelopes.
6514
+
6515
+ The Agent harness control server follows the same ordering: it installs the
6516
+ conversation attachment before awaiting the authoritative snapshot and rolls the
6517
+ attachment back if that read fails. A host adapter installs its delivery callback,
6518
+ issues `attach`, and supplies the returned snapshot through its live-state source;
6519
+ the existing Agent cursor and view reducers still own runtime epochs, durable
6520
+ versions and transcript projection.
6521
+
6335
6522
  ### Request-response over realtime
6336
6523
 
6337
6524
  For an event with an `ack` schema, `request()` is the Promise form of the same
@@ -11271,7 +11458,7 @@ description: One machine-readable statement a repository makes about itself —
11271
11458
  type: architecture
11272
11459
  status: active
11273
11460
  created: 2026-08-25
11274
- updated: 2026-08-25
11461
+ updated: 2026-08-30
11275
11462
  ---
11276
11463
 
11277
11464
  # Project declaration
@@ -11306,6 +11493,32 @@ mode that produces a running, wrong deployment rather than an error.
11306
11493
 
11307
11494
  ## Why declare yourself
11308
11495
 
11496
+ ### Identity is not product membership
11497
+
11498
+ The exported names `ProjectDeclaration` and `ProjectIdentity` describe the buildable source/artifact
11499
+ declared by a repository. A singular `identity` does **not** make a product project and a repository
11500
+ the same entity, nor does it identify a local checkout or a harness session.
11501
+
11502
+ | Entity | Meaning and owner |
11503
+ | --- | --- |
11504
+ | Product project | A product boundary whose repository membership is explicitly maintained outside this declaration |
11505
+ | Repository | Versioned source; its declaration describes roles, build outputs and release requirements |
11506
+ | Checkout | A local working copy of a repository revision; local paths and credentials belong to its host |
11507
+ | Harness workspace | The host-selected working scope for a session, not an implied product or membership registry |
11508
+
11509
+ Membership is many-to-many. For example, an external registry may declare product A includes
11510
+ repositories `service-a` and `shared-library`, while product B includes `service-b` and the same
11511
+ `shared-library`. Both products can read the library's unchanged declaration. Installing that library
11512
+ as a dependency, placing a checkout beside another, or naming a harness workspace does not create
11513
+ membership. The embedding product/registry owns these explicit relationships and their access policy.
11514
+
11515
+ A private companion repository can be part of a product without becoming a separate product. Its
11516
+ relationship and working context stay in an authorized private registry, never in a potentially public
11517
+ library declaration. No registry or membership fields are required here; the declaration remains
11518
+ optional. Existing exports and schema version 1 are unchanged.
11519
+
11520
+ ### One statement, several readers
11521
+
11309
11522
  Because the statements exist either way, and without a schema they exist three
11310
11523
  times. A repository already says how many roles it runs (in a process file),
11311
11524
  which variables it needs (in a Zod schema), what it builds (in a script) and
@@ -11501,6 +11714,16 @@ The browser-and-server entrypoint. Re-exports everything from
11501
11714
  | `defineRealtimeContract` | function | Zod-first shared Socket.IO event contract — [guide](../guide/realtime.md#zod-first-event-contract) |
11502
11715
  | `createRealtimeClient` | function | inferred, runtime-validated Socket.IO client — [guide](../guide/realtime.md#client--createrealtimeclient) |
11503
11716
  | `bindRealtimeClient` | function | bind contract validation and typed acknowledgements to an existing Stitchkit client transport without owning its lifecycle |
11717
+ | `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) |
11718
+ | `LiveStateController` | _type_ | renderer-neutral `start` / `resync` / `getSnapshot` / `subscribe` / `close` handle |
11719
+ | `LiveStateControllerConfig` | _type_ | typed source, reducer, explicit event/byte bounds, event sizing and isolated error hooks |
11720
+ | `LiveStateControllerSnapshot` / `LiveStateControllerStatus` | _types_ | current value plus phase, generation, buffer and application/duplicate/gap/refusal counters |
11721
+ | `LiveStateControllerStatusSchema` | schema | strict runtime validation for controller status metadata |
11722
+ | `LiveStatePhaseSchema` / `LiveStatePhase` | schema / _type_ | `idle`, `opening`, `live`, `resync-required`, `unavailable` or `closed` |
11723
+ | `LiveStateStopReasonSchema` / `LiveStateStopReason` | schema / _type_ | explicit gap, overflow, source loss, controller failure and bounded `controller-capacity` reasons |
11724
+ | `LiveStateEventDecision` | _type_ | provider-owned reducer result: applied state, duplicate or gap |
11725
+ | `LiveStateSource` / `LiveStateSourceOpenInput` / `LiveStateSourceOpenResult` | _types_ | host binding for one continuous snapshot/event boundary; transport retry and cursor semantics remain host-owned |
11726
+ | `LiveStateControllerError` / `LiveStateSubscriberError` | _types_ | isolated observer failure payloads that do not change source or subscriber truth |
11504
11727
  | `createRetainedTopics` | function | retained last-value store for sticky events — [guide](../guide/realtime.md#sticky-events) |
11505
11728
  | `parseSSE` | function | parse an SSE `Response` into an async generator — [guide](../guide/client.md#sse) |
11506
11729
  | `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) |
@@ -11540,7 +11763,7 @@ The browser-and-server entrypoint. Re-exports everything from
11540
11763
  | `RealtimeEmitArguments` | _type_ | emit arguments including an inferred acknowledgement callback |
11541
11764
  | `RealtimeEventHandler` | _type_ | handler inferred from an event definition |
11542
11765
  | `InferRealtimeEventMap` | _type_ | inferred Socket.IO-compatible event map |
11543
- | `RealtimeRejectDirection` | _type_ | server/client inbound/outbound rejection direction |
11766
+ | `RealtimeRejectDirection` / `RealtimeRejectPhase` / `RealtimeRejectReason` / `RealtimeRejectFault` | _types_ | canonical inferred rejection direction, validation phase, reason and fault classification |
11544
11767
  | `RealtimeRejectedEvent` | _type_ | structured rejected event with event, direction, phase, reason and fault |
11545
11768
  | `RealtimeRejectedEventHook` | _type_ | sync/async observer for structured realtime rejections |
11546
11769
  | `ValidatedRealtimeSocket` | _type_ | runtime-validating `on`/`emit` surface inferred from registries; `emit` returns "accepted by the transport" (`false` only for a client-side disconnected drop) |
@@ -11717,7 +11940,7 @@ Also re-exports the error helpers from `stitchkit/contract`.
11717
11940
  | `LifecycleHooks` | _type_ | `onRequest` / pre-body `authorize` / `beforeHandle` / `afterHandle` / `onError` |
11718
11941
  | `composeLifecycleHooks` | function | compose HTTP lifecycle phases in declaration order with short-circuit/fallthrough semantics |
11719
11942
  | `AuthorizationContext` | _type_ | HTTP pre-body context with validated params, `input: undefined` and no files |
11720
- | `RouteGroup` | _type_ | a prefixed group of services with its own hooks |
11943
+ | `RouteGroup` | _type_ | a prefixed group of services with its own hooks; matched errors try group `onError` → global `onError` → standard envelope, keeping the original error on fallback — [precedence](../guide/server.md#lifecycle-hooks) |
11721
11944
  | `RawRoute` | _type_ | a non-contract `Request → Response` route with a concrete `BunServer` context |
11722
11945
  | `RawRouteContext` | _type_ | the Bun-bound routing context a raw handler receives |
11723
11946
  | `BunServer` | _type_ | the `Bun.serve` instance type |
@@ -12238,7 +12461,7 @@ and introduces no store, queue or model-provider implementation of its own.
12238
12461
  | `AgentHarnessFileResources` | _type_ | loader plus direct `read_resource` definition for lazy exact content |
12239
12462
  | `createAgentHarnessControlServer` | function | transport-neutral correlated requests, observer attachments and exclusive controller leases |
12240
12463
  | `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 |
12241
- | `AgentHarnessControlServerConfig` | _type_ | explicit per-connection pending-event bound for failure-isolated control delivery |
12464
+ | `AgentHarnessControlServerConfig` | _type_ | explicit per-connection pending-event and server-wide concurrent attachment-snapshot bounds for failure-isolated control delivery |
12242
12465
  | `AgentHarnessPendingApproval` / `AgentHarnessApprovalDecision` | _type_ | exact durable pending request and allow/deny successor input |
12243
12466
 
12244
12467
  Resources default to at most 64 entries, 1 MiB of total UTF-8 text and 128 diagnostics. Duplicate
@@ -12733,7 +12956,7 @@ handler pipeline without opening a TCP port.
12733
12956
  | `DefineRealtimeProbeConfig` | _type_ | name, canonical scenario, explicit fixture and expected realtime outcome |
12734
12957
  | `CreateRealtimeProbeDriverConfig` | _type_ | per-scenario foreign-transport binder and optional handler-call counter |
12735
12958
  | `RealtimeProbeAdapter` | _type_ | connected-state observation, scenario invocation and subscription-only cleanup |
12736
- | `RealtimeProbeFixture` / `RealtimeProbeScenario` | _type_ | driver input and supported event/ack/invalid/disconnect/timeout scenario vocabulary |
12959
+ | `RealtimeProbeFixture` / `RealtimeProbeScenario` | _type_ | driver input and supported event/ack/local-invalid/peer-refusal/disconnect/timeout scenario vocabulary |
12737
12960
  | `RealtimeRejectionObservation` | _type_ | parsed structured realtime rejection observation |
12738
12961
  | `RealtimeDisconnectObservation` | _type_ | normalized physical timing of a realtime disconnect |
12739
12962
  | `TransportObservation` | _type_ | validated normalized driver result |
@@ -12784,6 +13007,11 @@ by the scaffolder that writes the first copy, and by whatever builds a source an
12784
13007
  binds the artifact into a deployment. It ships from the framework so those
12785
13008
  readers cannot hold different copies of the same schema.
12786
13009
 
13010
+ `identity` identifies the repository-local buildable source/artifact, not a product project, checkout
13011
+ or harness workspace. Product↔repository membership is explicit and many-to-many, owned by an
13012
+ external registry; dependency edges do not establish membership. Private companion context is never
13013
+ required in this public schema. See [identity boundaries](../guide/declaration.md#identity-is-not-product-membership).
13014
+
12787
13015
  **Declaring yourself is optional.** A project with no `project.json` is a
12788
13016
  complete project: nothing else in the framework imports this entrypoint, no
12789
13017
  build, test or start path looks for a declaration, and its absence is never an
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.70.1",
3
+ "version": "0.70.3",
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,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 };