stitchkit 0.89.0 → 0.90.1

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 (65) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/dist/agent-runtime/coding-tool-refusals.d.ts +31 -10
  3. package/dist/agent-runtime/coding-tool-refusals.d.ts.map +1 -1
  4. package/dist/agent-runtime/spill.d.ts.map +1 -1
  5. package/dist/agent-runtime-coding-tools.js +4 -4
  6. package/dist/agent-runtime-harness.js +5 -5
  7. package/dist/agent-runtime-sandbox.js +5 -5
  8. package/dist/agent-runtime.js +17 -29
  9. package/dist/application/watch-hub.d.ts +24 -2
  10. package/dist/application/watch-hub.d.ts.map +1 -1
  11. package/dist/application-grammy.js +1 -1
  12. package/dist/application.js +103 -13
  13. package/dist/cli.js +7 -7
  14. package/dist/contract/errors.d.ts +3 -0
  15. package/dist/contract/errors.d.ts.map +1 -1
  16. package/dist/contract/index.js +2 -2
  17. package/dist/{index-qmgs7y68.js → index-1txvygak.js} +1 -1
  18. package/dist/{index-8c3a7m4g.js → index-2hrfpw2c.js} +1 -1
  19. package/dist/{index-7evdsyez.js → index-2hryh65w.js} +1 -1
  20. package/dist/index-2na0qrfn.js +214 -0
  21. package/dist/{index-zyvby92n.js → index-44ht2790.js} +2 -2
  22. package/dist/{index-27gm3d6g.js → index-6q3zjwaz.js} +2 -2
  23. package/dist/{index-mnm4qp0p.js → index-79hb1wyh.js} +3 -3
  24. package/dist/{index-apn8repm.js → index-7gqx71yd.js} +2 -2
  25. package/dist/{index-7601qfg1.js → index-8dph3pw6.js} +4 -4
  26. package/dist/{index-90d7h2cr.js → index-bg8ypxzn.js} +3 -3
  27. package/dist/{index-vchzar7e.js → index-db51n3xr.js} +5 -5
  28. package/dist/{index-hw2chdvy.js → index-f475yj00.js} +1 -1
  29. package/dist/{index-q3ve2d1k.js → index-gbqjt8jz.js} +3 -3
  30. package/dist/index-gte38nbm.js +101 -0
  31. package/dist/{index-cp20z0n6.js → index-j7q0xj6d.js} +7 -55
  32. package/dist/{index-2bxcmqkb.js → index-kazec06k.js} +1 -1
  33. package/dist/{index-0w9abg87.js → index-scs3f1eg.js} +3 -0
  34. package/dist/{index-31jw8fjq.js → index-wb15909q.js} +1 -1
  35. package/dist/{index-p18f59zy.js → index-z78bjcxj.js} +1 -1
  36. package/dist/{index-t7fkyxcr.js → index-zkcqhxx8.js} +1 -1
  37. package/dist/index.js +6 -6
  38. package/dist/live/watch-client.d.ts +2 -1
  39. package/dist/live/watch-client.d.ts.map +1 -1
  40. package/dist/live/watch-contract.d.ts +70 -4
  41. package/dist/live/watch-contract.d.ts.map +1 -1
  42. package/dist/live/watch-delta.d.ts +110 -0
  43. package/dist/live/watch-delta.d.ts.map +1 -0
  44. package/dist/live.d.ts +2 -1
  45. package/dist/live.d.ts.map +1 -1
  46. package/dist/live.js +56 -4
  47. package/dist/node.js +6 -6
  48. package/dist/observability/changes.d.ts +55 -0
  49. package/dist/observability/changes.d.ts.map +1 -0
  50. package/dist/observability/index.d.ts +2 -0
  51. package/dist/observability/index.d.ts.map +1 -1
  52. package/dist/observability/index.js +130 -3
  53. package/dist/observability/spool.d.ts +53 -0
  54. package/dist/observability/spool.d.ts.map +1 -0
  55. package/dist/primitives.js +2 -2
  56. package/dist/remote.js +5 -5
  57. package/dist/server/index.js +8 -8
  58. package/dist/testing.js +6 -6
  59. package/dist/tool-invoker.js +6 -6
  60. package/dist/tools.js +11 -11
  61. package/dist/tracking.js +2 -2
  62. package/llms-full.txt +156 -15
  63. package/package.json +1 -1
  64. package/dist/index-dre2ywck.js +0 -48
  65. package/dist/index-gskm1k5a.js +0 -50
package/llms-full.txt CHANGED
@@ -48,7 +48,7 @@ own, recorded as an ADR.
48
48
  |--------|--------|----------|-------|
49
49
  | `stitchkit` | browser **and** server | stable | `defineContract`, `createClient`, `createHttpClient`, `createSocketIOClient`, `parseSSE`, the error model |
50
50
  | `stitchkit/contract` | browser **and** server | stable | the contract layer alone — `defineContract`, errors, pagination |
51
- | `stitchkit/live` | browser **and** server | evolving | `defineEvents` — topic declarations beside the operation contract, projected onto the realtime contract |
51
+ | `stitchkit/live` | browser **and** server | evolving | `defineEvents` — topic declarations beside the operation contract and watched reads: `createWatchClient`, the value/difference frame and its reassembly |
52
52
  | `stitchkit/primitives` | browser **and** server | evolving | generic values and declarations for lifecycle, access, audit, delivery and exports |
53
53
  | `stitchkit/server` | server (Bun) | stable | `createServer`, `implement`, hooks, auth, Socket.IO server, server primitives |
54
54
  | `stitchkit/node` | server (Node ≥ 22) | stable | `serveNode` + the runtime-agnostic core — the Node mirror of `/server` |
@@ -64,11 +64,11 @@ own, recorded as an ADR.
64
64
  | `stitchkit/tracking/server` | server (Bun or Node) | evolving | the decisions a tracking backend makes — dispositions, visit lease over an application-owned store, active intervals, presence; no database |
65
65
  | `stitchkit/release` | browser **and** server | evolving | a page follows the release it was built for — `createReleaseMarker` on the server, `createReleaseWatcher` in the browser, the `X-Build-Id` header and a socket event between them |
66
66
  | `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
67
- | `stitchkit/observability` | server | stable<br>_redefined in 1 of the 34 minors since 0.56.2, most recently 0.83.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
67
+ | `stitchkit/observability` | server | stable<br>_redefined in 1 of the 35 minors since 0.56.2, most recently 0.83.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
68
68
  | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the store and managed-resource conformance kits |
69
69
  | `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
70
70
  | `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
71
- | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 20 of the 34 minors since 0.56.2, most recently 0.89.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
71
+ | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 20 of the 35 minors since 0.56.2, most recently 0.89.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
72
72
  | `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
73
73
  | `stitchkit/agent-runtime/testing` | tests on Bun or Node | evolving | credential-free replay, scripted provider faults and deterministic race controls |
74
74
  | `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
@@ -78,7 +78,7 @@ own, recorded as an ADR.
78
78
  | `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
79
79
  | `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
80
80
  | `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
81
- | `stitchkit/application` | browser + server | evolving<br>_redefined in 7 of the 34 minors since 0.56.2, most recently 0.83.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
81
+ | `stitchkit/application` | browser + server | evolving<br>_redefined in 7 of the 35 minors since 0.56.2, most recently 0.83.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
82
82
  | `stitchkit/application/grammy` | server | evolving | isolated grammY polling and webhook lifecycle adapters |
83
83
  | `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
84
84
  | `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
@@ -4765,8 +4765,11 @@ authorization payload, so no directory is created before a host approves it.
4765
4765
 
4766
4766
  **Every ordinary outcome is a refusal a model can act on** — a missing file is `NOT_FOUND`, an
4767
4767
  existing file without `overwrite` is `CONFLICT`, an ambiguous snippet is `CONFLICT` carrying its
4768
- occurrence count, a path outside the root is `FORBIDDEN` each with a `hint` naming the next move.
4768
+ occurrence count, a path outside the root is `FORBIDDEN`, an unknown spilled-output reference is
4769
+ `SPILL_REFERENCE_UNKNOWN` — each with a `hint` naming the next move.
4769
4770
  Host-level causes stay scrubbed to `INTERNAL_SERVER_ERROR` and name nothing outside the workspace.
4771
+ Every refusal code is a member of `STITCH_ERROR_STATUS`, so its status survives an envelope that
4772
+ crosses a process boundary and is rebuilt from the wire.
4770
4773
  → ADR 0139
4771
4774
 
4772
4775
  `list_directory` marks excluded directories rather than hiding them, and `glob` reports
@@ -5032,7 +5035,11 @@ chat messages.
5032
5035
  `createSqliteAgentSpillStore` persists oversized output separately and records
5033
5036
  its locator, hash and lifecycle in the ledger. `read_output` and
5034
5037
  `search_output` re-run both locator authorization and the originating tool
5035
- authorization before reading bytes. `createSqliteAgentEventSearch` returns the
5038
+ authorization before reading bytes. A reference the store does not hold is a
5039
+ refusal like any other — `SPILL_REFERENCE_UNKNOWN` / 404, naming the reference
5040
+ and pointing at the one the spilling command returned. The lookup is scoped by
5041
+ conversation, so a reference belonging to another conversation is absent rather
5042
+ than forbidden, and the refusal cannot tell a model that it exists elsewhere. `createSqliteAgentEventSearch` returns the
5036
5043
  exact conversation and event sequence; cross-conversation results are denied
5037
5044
  unless `authorizeConversation` approves each target.
5038
5045
  `createAgentEventSearchTools` supplies `session_search`, `session_trace` and
@@ -8810,13 +8817,49 @@ Nothing an open can fail with escapes as a rejected promise: a disconnected
8810
8817
  socket, a timeout, a refusal all arrive as `unavailable` carrying the error's own
8811
8818
  code and message, and the next connection retries.
8812
8819
 
8820
+ The re-open carries what the client still holds, so a reconnection is usually
8821
+ cheap: the hub answers with a difference, or with nothing at all when the answer
8822
+ has not moved. That needs the key to still exist on the server — set `holdMs`
8823
+ past your reconnect delay, or the last detach releases the source and the page
8824
+ pays the whole value once.
8825
+
8826
+ ### Large answers cross as differences
8827
+
8828
+ A frame carries the value, a **difference** to a revision this subscriber already
8829
+ holds, or `unchanged`. The hub chooses per subscriber and sends a difference only
8830
+ when it is genuinely smaller; your `value` listener sees the rebuilt value either
8831
+ way and needs no code for this.
8832
+
8833
+ It matters when an answer is large and moves a little: a ~75 KB list in which two
8834
+ timestamps change, republished every fifteen seconds, was a megabyte per
8835
+ subscriber per minute before and is a frame under a kilobyte now.
8836
+
8837
+ ```ts
8838
+ createWatchHub({
8839
+ // Superseded values kept per key, so a difference has something to be taken
8840
+ // against. Default 262144. Set 0 to send whole values only.
8841
+ deltaMemoryBytes: 256 * 1024,
8842
+ holdMs: 30_000,
8843
+ // …
8844
+ });
8845
+ ```
8846
+
8847
+ Reassembly is checked against the server's fingerprint on every frame. If a
8848
+ difference will not apply — a client that missed a revision, a hub that restarted
8849
+ — that **one key** resynchronises: your `state` listener sees `resync-required`
8850
+ and the whole value follows. Other keys on the same socket are untouched.
8851
+
8852
+ Both ends must come from the same major release: a client older than 0.90 reads a
8853
+ difference frame as a value of `undefined`.
8854
+
8813
8855
  ### `watch` or `createLiveStateController`?
8814
8856
 
8815
8857
  > **If you would have written `applyEvent` as `(_, next) => next`, you want
8816
- > `watch`: the server sends the value whole.** `createLiveStateController` is for
8817
- > a server that sends *deltas* you have to fold. `watch` is that controller with
8818
- > the fold fixed to replacement, plus the key sharing and the retention so
8819
- > applying both to one value is always a mistake.
8858
+ > `watch`: whatever crosses the wire, what your `value` listener receives is the
8859
+ > whole answer.** `createLiveStateController` is for a server whose *protocol* is
8860
+ > deltas that your code folds. `watch` may send a difference too, but it folds it
8861
+ > for you and hands you the value — so applying both to one value is always a
8862
+ > mistake.
8820
8863
 
8821
8864
  ### What it cannot promise
8822
8865
 
@@ -9495,7 +9538,10 @@ core never models them (ADR 0002). But stitchkit itself emits a set of its own:
9495
9538
  `FILE_INSPECTION_REJECTED`, `FILE_TOO_LARGE`, `FILE_EXISTS`,
9496
9539
  `REALTIME_CONTRACT_VIOLATION`, `APPLICATION_NOT_ACCEPTING`, `WAIT_TIMEOUT`,
9497
9540
  `WAIT_FAILED`, `DOWNLOAD_NOT_FOUND`, `VIEW_HTTP_ERROR`,
9498
- `OPERATION_NOT_SUCCEEDED`, `INTERNAL_SERVER_ERROR` — a set that grows in
9541
+ `OPERATION_NOT_SUCCEEDED`, `STREAM_ITEM_INVALID`, `STREAM_FRAME_TOO_LARGE`,
9542
+ `STREAM_TERMINAL_MISSING`, `STREAM_LIFETIME_EXCEEDED`,
9543
+ `GRAMMY_WEBHOOK_NOT_ACCEPTING`, `SANDBOX_UNAVAILABLE`, `SANDBOX_INSUFFICIENT`,
9544
+ `SPILL_REFERENCE_UNKNOWN`, `INTERNAL_SERVER_ERROR` — a set that grows in
9499
9545
  ordinary releases, each addition named in the changelog. They are
9500
9546
  published as **`STITCH_ERROR_STATUS`** (the `code → status` map) and
9501
9547
  **`StitchErrorCode`** (its `keyof`), with **`isStitchErrorCode()`** (→ ADR 0026).
@@ -9746,9 +9792,10 @@ stitchkit answers this at two levels.
9746
9792
  sanitisation and `createObservability` with independent request/tool sinks.
9747
9793
  [Start here ↓](#the-observability-module)
9748
9794
 
9749
- stitchkit still ships no logger and no audit store those are the app's choice.
9750
- What it ships is the machinery that turns a completed call into a clean,
9751
- normalised record.
9795
+ stitchkit still ships no logger and no audit store, and auditing stays opt-in
9796
+ those are the app's choice (→ ADR 0012, reaffirmed in ADR 0184). What it ships is
9797
+ the machinery that turns a completed call into a clean, normalised record, keeps
9798
+ the ones worth keeping, and does not lose them when the store is down.
9752
9799
 
9753
9800
  ## The observability module
9754
9801
 
@@ -9985,6 +10032,69 @@ queryable across all three:
9985
10032
  | `resultSize` / `responseBytes` | result item count + serialised size |
9986
10033
  | `userId` / `ipAddress` / `userAgent` | identity |
9987
10034
 
10035
+ ### Write the calls that changed something
10036
+
10037
+ `auditChanges` is the filter most projects end up writing, shipped so they do not
10038
+ have to write it six ways:
10039
+
10040
+ ```ts
10041
+ import { auditChanges, createObservability } from 'stitchkit/observability';
10042
+
10043
+ createObservability({
10044
+ request: { write: saveAuditRow, filter: auditChanges },
10045
+ tools: { write: saveAuditRow, filter: auditChanges },
10046
+ });
10047
+ ```
10048
+
10049
+ It drops `GET`, `HEAD` and `OPTIONS`, keeps everything else, and keeps `401` and
10050
+ `403` **whatever the verb was** — a refused read is the row an audit exists to
10051
+ hold, and a filter that drops every `GET` drops exactly that. An unrecognised
10052
+ verb is kept: an extra row costs bytes, a missing one costs the answer to "who
10053
+ changed this", silently and only later.
10054
+
10055
+ One filter across HTTP, MCP and agent calls, because a tool call carries its
10056
+ contract verb in `httpMethod` while its `method` is the literal `TOOL`.
10057
+
10058
+ Narrower policy stays yours — compose it:
10059
+
10060
+ ```ts
10061
+ filter: (event) => auditChanges(event) && event.serviceName !== 'health',
10062
+ ```
10063
+
10064
+ ### Keeping the row when the store is down
10065
+
10066
+ A sink is fire-and-forget, so an unreachable store means the event is simply
10067
+ gone — and a store is most likely to be unreachable during an incident, which is
10068
+ the window whose rows someone will later want most.
10069
+
10070
+ `createSpooledSink` writes the row to a local append-only file first, offers it
10071
+ to the store second, and marks it delivered third. What a previous process left
10072
+ unmarked is replayed:
10073
+
10074
+ ```ts
10075
+ import { createSpooledSink } from 'stitchkit/observability';
10076
+
10077
+ const audit = createSpooledSink({
10078
+ path: '/var/lib/app/audit.ndjson',
10079
+ write: saveAuditRow,
10080
+ });
10081
+
10082
+ // Once, at startup, before the sink is wired — and await it.
10083
+ const { replayed, failed } = await audit.recover();
10084
+
10085
+ createObservability({ request: { write: audit.write, filter: auditChanges } });
10086
+ ```
10087
+
10088
+ The guarantee is **at least once**. A crash between the store accepting a row and
10089
+ the file recording that it did replays the row, so **the store must be idempotent
10090
+ on the record key** — `event.spanId` by default, unique per call. A unique index
10091
+ on it turns the duplicate into a no-op; without one, replay writes the row twice.
10092
+ Exactly-once would need the file and the database to share a transaction. They do
10093
+ not.
10094
+
10095
+ One process, one path. Two processes pointed at the same file replay each other's
10096
+ records: harmless against an idempotent store, wasteful always.
10097
+
9988
10098
  ### Request context
9989
10099
 
9990
10100
  When request observability is configured, `createHandler` establishes the
@@ -11382,6 +11492,32 @@ analytics policy belong to the application.
11382
11492
 
11383
11493
  # Upgrading stitchkit
11384
11494
 
11495
+ ## Released migration: 0.90.0
11496
+
11497
+ 1. `stitchkit.watch.value` is a discriminated union on `kind` — `full`, `delta`,
11498
+ `unchanged` — and every frame carries `fingerprint`. **Both ends must come from
11499
+ the same major.** A client older than this release reads a `delta` frame as a
11500
+ value of `undefined`, silently; upgrade the hub and the pages that talk to it
11501
+ together, or set `deltaMemoryBytes: 0` on the hub until they are.
11502
+
11503
+ Applications using `createWatchClient` need no code change: it rebuilds the
11504
+ value and hands the listener the whole answer as before. Code that reads
11505
+ `WatchValueFrame.value` directly — a hand-written subscriber, a test double —
11506
+ narrows on `kind` first.
11507
+
11508
+ ```ts
11509
+ // before: frame.value
11510
+ // after:
11511
+ if (frame.kind === 'full') hold(frame.value);
11512
+ else if (frame.kind === 'delta') hold(applyWatchDelta(held, frame.delta));
11513
+ // 'unchanged' leaves what you hold standing
11514
+ ```
11515
+
11516
+ 2. `AttachedWatcher.open` takes an optional third argument, `have`. A custom
11517
+ server binding that forwards `stitchkit.watch.open` should pass the payload's
11518
+ `have` through; omitting it costs a whole value on every reconnection and is
11519
+ otherwise harmless.
11520
+
11385
11521
  ## Released migration: 0.89.0
11386
11522
 
11387
11523
  1. Custom `AgentRuntimeStore` adapters implement `seedConversationInput`; custom
@@ -15121,7 +15257,10 @@ realtime contract from `stitchkit`, and the server halves live in `stitchkit/app
15121
15257
  | `watchContract` | const | the four-event realtime contract a watched read travels on |
15122
15258
  | `WATCH_OPEN` / `WATCH_CLOSE` / `WATCH_VALUE` / `WATCH_STATE` | const | the event names of that contract |
15123
15259
  | `WatchKey` / `WatchKeySchema` / `watchKeyString` | type / schema / function | `(service, action, arguments digest)` — the identity both ends compute the same way |
15124
- | `WatchValueFrame` / `WatchValueSchema` / `WatchStateFrame` / `WatchStateSchema` | _types_ | a value with its monotonic revision, and a phase from `LiveStatePhase` with the read's own code and message |
15260
+ | `WatchValueFrame` / `WatchValueSchema` / `WatchStateFrame` / `WatchStateSchema` | _types_ | one answer — `full`, `delta` or `unchanged`, with its monotonic revision and fingerprint — and a phase from `LiveStatePhase` with the read's own code and message |
15261
+ | `WatchHave` / `WatchHaveSchema` | type / schema | what a reconnecting subscriber already holds, offered on `open`: a revision and the fingerprint that proves it |
15262
+ | `WatchDelta` / `WatchDeltaSchema` / `WatchArrayOp` / `WatchArrayOpSchema` | _types_ / schemas | a structural difference over parsed JSON — objects by changed and dropped key, arrays by runs copied from the previous value |
15263
+ | `watchDiff` / `applyWatchDelta` / `watchDeltaWins` | functions | build a difference (`undefined` when equal), rebuild the value from one (throws rather than inventing), and whether it is actually smaller than the value |
15125
15264
 
15126
15265
  ---
15127
15266
 
@@ -16142,6 +16281,8 @@ audit event. See the [Observability guide](../guide/observability.md).
16142
16281
  | Export | Kind | Summary |
16143
16282
  |--------|------|---------|
16144
16283
  | `createObservability` | function | configure framework-owned request completion and canonical tool event sinks — [guide](../guide/observability.md#createobservability) |
16284
+ | `auditChanges` | function | the sink `filter` most projects write themselves — drops `GET`/`HEAD`/`OPTIONS`, keeps `401`/`403` whatever the verb, keeps an unrecognised verb |
16285
+ | `createSpooledSink` / `SpooledSink` / `SpooledSinkConfig` / `SpoolRecovery` | function / _types_ | spool the row to an append-only file before the store and replay what a previous process left undelivered — at least once, keyed on `spanId` |
16145
16286
  | `createDimensionsProjector` / `DimensionsProjector` / `DimensionsProjectorConfig` / `ProjectedDimensions` | function / _type_ | typed request/result/error attribution projected into the existing request context |
16146
16287
  | `DimensionCollision` / `SetRequestDimensionsOptions` | _type_ | explicit overwrite, preserve or error policy for dimension keys |
16147
16288
  | `createBoundedLogger` | function | decorate a `StitchLogger` with request context, shared sanitisation, redaction and total bounds |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.89.0",
3
+ "version": "0.90.1",
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,48 +0,0 @@
1
- import {
2
- LiveStatePhaseSchema,
3
- LiveStateStopReasonSchema
4
- } from "./index-ywd0y6m1.js";
5
-
6
- // src/live/watch-contract.ts
7
- import { z } from "zod";
8
- var WatchKeySchema = z.object({
9
- service: z.string().min(1),
10
- action: z.string().min(1),
11
- digest: z.string().min(1)
12
- }).strict().readonly();
13
- var WatchOpenSchema = z.object({ key: WatchKeySchema, args: z.unknown() }).readonly();
14
- var WatchAcceptedSchema = z.object({
15
- accepted: z.boolean(),
16
- reason: z.string().optional()
17
- }).strict().readonly();
18
- var WatchValueSchema = z.object({
19
- key: WatchKeySchema,
20
- revision: z.number().int().nonnegative(),
21
- value: z.unknown()
22
- }).readonly();
23
- var WatchStateSchema = z.object({
24
- key: WatchKeySchema,
25
- phase: LiveStatePhaseSchema,
26
- reason: LiveStateStopReasonSchema.optional(),
27
- code: z.string().optional(),
28
- message: z.string().optional()
29
- }).readonly();
30
- var WATCH_OPEN = "stitchkit.watch.open";
31
- var WATCH_CLOSE = "stitchkit.watch.close";
32
- var WATCH_VALUE = "stitchkit.watch.value";
33
- var WATCH_STATE = "stitchkit.watch.state";
34
- var watchContract = {
35
- serverToClient: {
36
- [WATCH_VALUE]: { args: z.tuple([WatchValueSchema]) },
37
- [WATCH_STATE]: { args: z.tuple([WatchStateSchema]) }
38
- },
39
- clientToServer: {
40
- [WATCH_OPEN]: { args: z.tuple([WatchOpenSchema]), ack: WatchAcceptedSchema },
41
- [WATCH_CLOSE]: { args: z.tuple([z.object({ key: WatchKeySchema }).readonly()]) }
42
- }
43
- };
44
- function watchKeyString(key) {
45
- return `${key.service}/${key.action}/${key.digest}`;
46
- }
47
-
48
- export { WatchKeySchema, WatchValueSchema, WatchStateSchema, WATCH_OPEN, WATCH_CLOSE, WATCH_VALUE, WATCH_STATE, watchContract, watchKeyString };
@@ -1,50 +0,0 @@
1
- // src/agent-runtime/sandbox.ts
2
- import { z } from "zod";
3
- var AgentSandboxRestrictionSchema = z.enum([
4
- "network-denied",
5
- "write-contained",
6
- "secrets-hidden",
7
- "process-contained"
8
- ]);
9
- var AgentSandboxGradeSchema = z.discriminatedUnion("grade", [
10
- z.object({ grade: z.literal("full"), restrictions: z.array(AgentSandboxRestrictionSchema) }).strict(),
11
- z.object({
12
- grade: z.literal("partial"),
13
- restrictions: z.array(AgentSandboxRestrictionSchema),
14
- gaps: z.array(AgentSandboxRestrictionSchema)
15
- }).strict(),
16
- z.object({ grade: z.literal("unavailable"), reason: z.string().min(1) }).strict()
17
- ]);
18
- var probes = new WeakMap;
19
- function probeAgentProcessSandbox(sandbox, options = {}) {
20
- if (options.refresh)
21
- probes.delete(sandbox);
22
- const present = probes.get(sandbox);
23
- if (present)
24
- return present;
25
- const pending = Promise.resolve(sandbox.probe()).then((grade) => AgentSandboxGradeSchema.parse(grade));
26
- probes.set(sandbox, pending);
27
- return pending;
28
- }
29
- function missingSandboxRestrictions(grade, required) {
30
- if (grade.grade === "unavailable")
31
- return required;
32
- const present = new Set(grade.restrictions);
33
- return required.filter((restriction) => !present.has(restriction));
34
- }
35
- async function recordAgentSandboxProbe(input) {
36
- const grade = await probeAgentProcessSandbox(input.sandbox);
37
- const missing = missingSandboxRestrictions(grade, input.required);
38
- await input.store.appendEvent({
39
- conversationId: input.conversationId,
40
- kind: "sandbox/probed",
41
- payload: {
42
- result: grade,
43
- required: [...input.required],
44
- missing: [...missing]
45
- }
46
- });
47
- return grade;
48
- }
49
-
50
- export { AgentSandboxRestrictionSchema, AgentSandboxGradeSchema, probeAgentProcessSandbox, missingSandboxRestrictions, recordAgentSandboxProbe };