stitchkit 0.77.0 → 0.78.0

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/dist/agent-runtime-coding-tools.js +1 -1
  2. package/dist/agent-runtime-harness.js +6 -6
  3. package/dist/agent-runtime.js +11 -11
  4. package/dist/application/decisions.d.ts +28 -25
  5. package/dist/application/decisions.d.ts.map +1 -1
  6. package/dist/application/kernel.d.ts +2 -0
  7. package/dist/application/kernel.d.ts.map +1 -1
  8. package/dist/application/keyspace.d.ts +11 -1
  9. package/dist/application/keyspace.d.ts.map +1 -1
  10. package/dist/application/schedule.d.ts.map +1 -1
  11. package/dist/application/server-resource.d.ts.map +1 -1
  12. package/dist/application-diagnostic-journal.d.ts +21 -0
  13. package/dist/application-diagnostic-journal.d.ts.map +1 -0
  14. package/dist/application-diagnostic-journal.js +714 -0
  15. package/dist/application-opentelemetry.js +92 -5
  16. package/dist/application-schemas.js +13 -98
  17. package/dist/application.d.ts +1 -2
  18. package/dist/application.d.ts.map +1 -1
  19. package/dist/application.js +1775 -858
  20. package/dist/cli.js +7 -7
  21. package/dist/contract/index.js +9 -7
  22. package/dist/files/boundary.d.ts.map +1 -1
  23. package/dist/files.js +1 -1
  24. package/dist/{index-3z73fh2c.js → index-1ckavx4h.js} +49 -1
  25. package/dist/{index-s2rchahr.js → index-31k1ns1s.js} +4 -4
  26. package/dist/{index-m668wzyc.js → index-4ntp63ps.js} +4 -4
  27. package/dist/{index-1rxswfbv.js → index-56fm73re.js} +1 -1
  28. package/dist/{index-ezmn6ac6.js → index-5r0ak8fr.js} +3 -3
  29. package/dist/{index-f6n5n7nz.js → index-7z69kyrh.js} +10 -87
  30. package/dist/{index-k2zczx1g.js → index-93xp5tgg.js} +1 -1
  31. package/dist/{index-bfcpjw20.js → index-actkayzn.js} +7 -3
  32. package/dist/index-anyqnqcv.js +79 -0
  33. package/dist/{index-7qy2ex0m.js → index-cwp38wxn.js} +1 -1
  34. package/dist/{index-35z5h2ty.js → index-dmfn9m4x.js} +125 -4
  35. package/dist/index-h4pj6fta.js +87 -0
  36. package/dist/{index-jqtsc9mj.js → index-j6mfaw26.js} +2 -2
  37. package/dist/{index-58jzmnn4.js → index-k45qqrdh.js} +513 -39
  38. package/dist/{index-8eywc9zv.js → index-kw12fsbc.js} +0 -1
  39. package/dist/index-n1g2xm6a.js +203 -0
  40. package/dist/{index-t8qyvrvg.js → index-n7apa6sy.js} +12 -12
  41. package/dist/{index-s4c8wy8m.js → index-qaajjfp6.js} +8 -8
  42. package/dist/{index-nemjkxjp.js → index-s5tn4kdv.js} +14 -10
  43. package/dist/index-wfq8zerm.js +92 -0
  44. package/dist/index.js +53 -1215
  45. package/dist/internal/deadline.d.ts +17 -0
  46. package/dist/internal/deadline.d.ts.map +1 -0
  47. package/dist/live.js +12 -89
  48. package/dist/node.js +5 -6
  49. package/dist/observability/context.d.ts.map +1 -1
  50. package/dist/observability/index.js +3 -3
  51. package/dist/primitives.js +2 -1
  52. package/dist/remote.js +3 -6
  53. package/dist/server/event-bus.d.ts.map +1 -1
  54. package/dist/server/index.js +31 -30
  55. package/dist/testing.js +1562 -87
  56. package/dist/tool-invoker.js +6 -6
  57. package/dist/tools.js +66 -23
  58. package/llms-full.txt +110 -13
  59. package/package.json +8 -4
  60. package/dist/index-3cwck0rm.js +0 -897
  61. package/dist/index-7b188kmz.js +0 -49
  62. package/dist/index-a916km3r.js +0 -45
  63. package/dist/index-vc1b0b1b.js +0 -825
  64. package/dist/{index-da1aqnhb.js → index-aewy3x5j.js} +3 -3
  65. package/dist/{index-nt1mp8km.js → index-efm026pw.js} +3 -3
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  createToolInvoker
3
- } from "./index-jqtsc9mj.js";
4
- import"./index-7qy2ex0m.js";
5
- import"./index-s4c8wy8m.js";
6
- import"./index-nemjkxjp.js";
3
+ } from "./index-j6mfaw26.js";
4
+ import"./index-cwp38wxn.js";
5
+ import"./index-qaajjfp6.js";
7
6
  import"./index-22by16v6.js";
8
7
  import"./index-cby4ar3v.js";
9
- import"./index-nt1mp8km.js";
8
+ import"./index-s5tn4kdv.js";
9
+ import"./index-efm026pw.js";
10
10
  import"./index-vj3vvpaa.js";
11
- import"./index-0w9abg87.js";
12
11
  import"./index-qyrqwr4c.js";
12
+ import"./index-0w9abg87.js";
13
13
  import"./index-6k1937bx.js";
14
14
  export {
15
15
  createToolInvoker
package/dist/tools.js CHANGED
@@ -1,18 +1,12 @@
1
- import {
2
- redact
3
- } from "./index-aczggrty.js";
4
1
  import {
5
2
  buildToolManifest,
6
3
  mountAgent
7
- } from "./index-ezmn6ac6.js";
4
+ } from "./index-5r0ak8fr.js";
8
5
  import"./index-3xnq72rz.js";
9
- import {
10
- argumentsDigest
11
- } from "./index-a916km3r.js";
12
6
  import {
13
7
  signJwt,
14
8
  verifyPkce
15
- } from "./index-da1aqnhb.js";
9
+ } from "./index-aewy3x5j.js";
16
10
  import {
17
11
  DEFAULT_CORS_ALLOW_HEADERS,
18
12
  DEFAULT_PROCESS_SIGNALS,
@@ -23,7 +17,7 @@ import {
23
17
  } from "./index-vbf2p6me.js";
24
18
  import {
25
19
  createToolInvoker
26
- } from "./index-jqtsc9mj.js";
20
+ } from "./index-j6mfaw26.js";
27
21
  import {
28
22
  WaitTimeoutError,
29
23
  createCli,
@@ -32,10 +26,10 @@ import {
32
26
  fetchPinnedDocument,
33
27
  readCapped,
34
28
  runWaitOperation
35
- } from "./index-s2rchahr.js";
29
+ } from "./index-31k1ns1s.js";
36
30
  import {
37
31
  collectToolSurface
38
- } from "./index-k2zczx1g.js";
32
+ } from "./index-93xp5tgg.js";
39
33
  import {
40
34
  createRuntimeToolFactory,
41
35
  defineRuntimeTool
@@ -44,22 +38,17 @@ import {
44
38
  collectTools,
45
39
  createToolRunner,
46
40
  formatToolError
47
- } from "./index-7qy2ex0m.js";
41
+ } from "./index-cwp38wxn.js";
48
42
  import {
49
43
  ToolExecutionControlError,
50
44
  coerceJsonArgs,
51
45
  executeToolMethod,
52
46
  isToolExecutionControlError,
53
47
  toolResultFromError
54
- } from "./index-s4c8wy8m.js";
55
- import {
56
- getRequestContext,
57
- getTraceId,
58
- runWithRequestContext
59
- } from "./index-nemjkxjp.js";
48
+ } from "./index-qaajjfp6.js";
60
49
  import {
61
50
  ManagedFileError
62
- } from "./index-bfcpjw20.js";
51
+ } from "./index-actkayzn.js";
63
52
  import"./index-sbdmyz75.js";
64
53
  import {
65
54
  PORTABLE_JSON_SCHEMA_FORMATS,
@@ -73,20 +62,28 @@ import {
73
62
  validateMcpRoundPolicy
74
63
  } from "./index-22by16v6.js";
75
64
  import"./index-cby4ar3v.js";
65
+ import {
66
+ redact
67
+ } from "./index-aczggrty.js";
68
+ import {
69
+ getRequestContext,
70
+ getTraceId,
71
+ runWithRequestContext
72
+ } from "./index-s5tn4kdv.js";
76
73
  import {
77
74
  normalizeError,
78
75
  resolvePropagationContext
79
- } from "./index-nt1mp8km.js";
76
+ } from "./index-efm026pw.js";
80
77
  import {
81
78
  defineContract
82
79
  } from "./index-vj3vvpaa.js";
80
+ import {
81
+ isRecord
82
+ } from "./index-qyrqwr4c.js";
83
83
  import {
84
84
  AppError,
85
85
  STITCH_ERROR_STATUS
86
86
  } from "./index-0w9abg87.js";
87
- import {
88
- isRecord
89
- } from "./index-qyrqwr4c.js";
90
87
  import {
91
88
  ManagedFilePathSchema,
92
89
  ManagedFileRefSchema
@@ -1070,6 +1067,52 @@ import {
1070
1067
  inputResponse,
1071
1068
  PROTOCOL_VERSION_META_KEY
1072
1069
  } from "@modelcontextprotocol/server";
1070
+
1071
+ // src/internal/stable-digest.ts
1072
+ function stableValue(value) {
1073
+ if (Array.isArray(value))
1074
+ return value.map(stableValue);
1075
+ if (value === null || typeof value !== "object")
1076
+ return value;
1077
+ const result = {};
1078
+ for (const key of Object.keys(value).sort()) {
1079
+ result[key] = stableValue(Reflect.get(value, key));
1080
+ }
1081
+ return result;
1082
+ }
1083
+ function mix128(input) {
1084
+ let a = 2654435769;
1085
+ let b = 2246822507;
1086
+ let c = 3266489909;
1087
+ let d = 668265263;
1088
+ for (let index = 0;index < input.length; index += 1) {
1089
+ const code = input.charCodeAt(index);
1090
+ a = Math.imul(a ^ code, 2246822519);
1091
+ b = Math.imul(b ^ code, 3266489917);
1092
+ c = Math.imul(c ^ code, 668265263);
1093
+ d = Math.imul(d ^ code, 374761393);
1094
+ a = a << 13 | a >>> 19;
1095
+ b = b << 17 | b >>> 15;
1096
+ c = c << 7 | c >>> 25;
1097
+ d = d << 11 | d >>> 21;
1098
+ }
1099
+ a ^= b >>> 15;
1100
+ b ^= c >>> 13;
1101
+ c ^= d >>> 11;
1102
+ d ^= a >>> 9;
1103
+ return [
1104
+ Math.imul(a ^ a >>> 16, 575075507) >>> 0,
1105
+ Math.imul(b ^ b >>> 13, 2654435761) >>> 0,
1106
+ Math.imul(c ^ c >>> 16, 2246822507) >>> 0,
1107
+ Math.imul(d ^ d >>> 15, 3266489909) >>> 0
1108
+ ];
1109
+ }
1110
+ function argumentsDigest(args) {
1111
+ const lanes = mix128(JSON.stringify(stableValue(args)) ?? "undefined");
1112
+ return lanes.map((lane) => lane.toString(16).padStart(8, "0")).join("");
1113
+ }
1114
+
1115
+ // src/tools/mcp-round.ts
1073
1116
  function transportContext(context, toolName, outcome, round) {
1074
1117
  const protocolVersionValue = isRecord(context.mcpReq.envelope) ? Reflect.get(context.mcpReq.envelope, PROTOCOL_VERSION_META_KEY) : undefined;
1075
1118
  const protocolVersion = typeof protocolVersionValue === "string" ? protocolVersionValue : undefined;
package/llms-full.txt CHANGED
@@ -62,7 +62,7 @@ own, recorded as an ADR.
62
62
  | `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 |
63
63
  | `stitchkit/declaration` | build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
64
64
  | `stitchkit/react` | browser | stable | `createCursorQuery`, `createCacheBridge` |
65
- | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 11 of the 22 minors since 0.56.2, most recently 0.75.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
65
+ | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 11 of the 23 minors since 0.56.2, most recently 0.75.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
66
66
  | `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
67
67
  | `stitchkit/agent-runtime/coding-tools` | server (Bun or Node) | evolving | bounded host-authorized direct file and shell tools; a root boundary, not an OS sandbox |
68
68
  | `stitchkit/agent-runtime/openrouter` | server | evolving | isolated OpenRouter language-model adapter |
@@ -70,10 +70,11 @@ own, recorded as an ADR.
70
70
  | `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
71
71
  | `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
72
72
  | `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
73
- | `stitchkit/application` | server | evolving<br>_redefined in 4 of the 22 minors since 0.56.2, most recently 0.72.0_ | managed resource graph, readiness, admission, schedules and bounded shutdown |
73
+ | `stitchkit/application` | browser + server | evolving<br>_redefined in 5 of the 23 minors since 0.56.2, most recently 0.78.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
74
74
  | `stitchkit/application/grammy` | server | evolving | isolated grammY polling and webhook lifecycle adapters |
75
75
  | `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
76
- | `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas with no server runtime behind them |
76
+ | `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
77
+ | `stitchkit/application/diagnostic-journal` | server | evolving | the local diagnostic journal — the one part of the kernel that spawns, locks and writes files |
77
78
 
78
79
  Rule of thumb: browser code imports `stitchkit` and `stitchkit/react`; server
79
80
  code adds `stitchkit/server` (or `stitchkit/node` on Node) and opts into
@@ -5509,9 +5510,26 @@ await app.restart({ resourceId: 'cache' })
5509
5510
  **Refused is not failed.** An unknown id, a restart during shutdown, and a
5510
5511
  restart before the application is ready all return `refused` with a reason and
5511
5512
  touch nothing. `failed` means the subtree came down and the new generation did
5512
- not come up — the snapshot says so too, with the failing resource marked
5513
- `failed` / `unhealthy`, because the restart records a failure the same way a
5514
- startup does.
5513
+ not come up — and the result agrees with the snapshot: if any affected resource
5514
+ ends the restart in `failed`, so does the restart, including an optional one
5515
+ whose failure the start loop does not re-throw.
5516
+
5517
+ **The close phase is bounded**, by the application's shutdown budget or by one
5518
+ this call names:
5519
+
5520
+ ```ts
5521
+ await app.restart({ resourceId: 'database', gracePeriodMs: 5_000, forceTimeoutMs: 2_000 })
5522
+ ```
5523
+
5524
+ **A resource has to be able to start twice.** The kernel calls `start` again, so
5525
+ a resource holding state across its own lifetime rebuilds it there. The ones this
5526
+ framework ships do — a schedule re-arms, a keyspace opens a new generation and
5527
+ re-loads, a managed server built from a factory gets a fresh server. A managed
5528
+ server given a server *instance* cannot, and says so by name rather than
5529
+ republishing something it has shut down. Give a keyspace a
5530
+ `backend: () => …` factory when its backend cannot be re-opened after `close()`.
5531
+
5532
+ → [ADR 0157](../decisions/0157-a-restartable-resource-begins-a-generation.md).
5515
5533
 
5516
5534
  Restarts of overlapping subtrees queue behind each other rather than being
5517
5535
  refused. Two callers asking at once is ordinary; two generations of one resource
@@ -5547,7 +5565,7 @@ const pipeline = createDecisionPipeline<{ userId: string; scope: string }>([
5547
5565
  ? { outcome: 'allow' }
5548
5566
  : { outcome: 'defer' }) },
5549
5567
  { id: 'default', decide: () => ({ outcome: 'deny', reason: 'no policy allowed this' }) },
5550
- ])
5568
+ ], { policyTimeoutMs: 2_000 })
5551
5569
 
5552
5570
  const result = await pipeline.decide(request)
5553
5571
  result.outcome // 'allow' | 'deny' — never 'defer'
@@ -5568,8 +5586,19 @@ answer that wrongly while looking complete.
5568
5586
  **Every policy deferring raises.** `DecisionUndecidedError`, not a default —
5569
5587
  defaulting to `allow` turns an incomplete policy set into an open door, and
5570
5588
  defaulting to `deny` turns it into an outage whose cause reads as a legitimate
5571
- refusal. A policy that throws or times out denies with the policy named
5572
- (`DecisionPolicyError`): broken must not mean skipped.
5589
+ refusal.
5590
+
5591
+ **A policy that does not answer raises too.** A non-decision, a throw, and running
5592
+ past `policyTimeoutMs` are one error — `DecisionPolicyError`, naming the policy
5593
+ and carrying the trace so far — because they are indistinguishable downstream and
5594
+ handling one but not the others is how a broken policy becomes a skipped one. The
5595
+ chain stops there rather than falling through to whatever the next policy would
5596
+ have said.
5597
+
5598
+ `policyTimeoutMs` is required and has no default. Pick a number your slowest
5599
+ policy comfortably beats. The framework will not choose it for you: a policy that
5600
+ never settles hangs every caller of the operation it guards, and a default here
5601
+ is a number nobody chose applied to code the framework has never seen.
5573
5602
 
5574
5603
  The same `allow`/`deny`/`defer` type is what an event topic declared
5575
5604
  `mode: 'decision'` uses in `stitchkit/live` — one vocabulary, because a listener
@@ -5671,8 +5700,14 @@ changing its revision or status contract. → ADR 0119.
5671
5700
  Use `createDiagnosticJournal` when a process needs finite, ordered local metadata evidence and the
5672
5701
  deployment log pipeline is not the right boundary:
5673
5702
 
5703
+ It lives at `stitchkit/application/diagnostic-journal`, not in the main barrel:
5704
+ it is the one part of the kernel that spawns, locks and writes files, and while it
5705
+ was exported from `stitchkit/application` that single line made the whole
5706
+ entrypoint unusable in a browser bundle. Its schemas stay in
5707
+ `stitchkit/application`.
5708
+
5674
5709
  ```ts
5675
- import { createDiagnosticJournal } from 'stitchkit/application'
5710
+ import { createDiagnosticJournal } from 'stitchkit/application/diagnostic-journal'
5676
5711
  import { z } from 'zod'
5677
5712
 
5678
5713
  const journal = await createDiagnosticJournal({
@@ -10101,6 +10136,50 @@ makes one thing your job rather than the resolver's:
10101
10136
  The mechanical part is identical either way. Only the *noticing* differs, and an
10102
10137
  exact pin moves it onto you.
10103
10138
 
10139
+ ## Released migration: 0.78.0
10140
+
10141
+ Two mechanical edits. Find them both:
10142
+
10143
+ ```bash
10144
+ rg -n "createDecisionPipeline|createDiagnosticJournal" --glob '*.ts' --glob '*.tsx'
10145
+ ```
10146
+
10147
+ **1. `createDecisionPipeline` takes a deadline.**
10148
+
10149
+ ```ts
10150
+ // before
10151
+ createDecisionPipeline([policyA, policyB])
10152
+ // after
10153
+ createDecisionPipeline([policyA, policyB], { policyTimeoutMs: 2_000 })
10154
+ ```
10155
+
10156
+ Pick a number your slowest policy comfortably beats. There is no default on
10157
+ purpose: a policy that never settles hangs every caller of the operation it
10158
+ guards, and the framework has no basis for choosing that number for you.
10159
+
10160
+ Past the deadline — and on a throw, and on a non-decision — the pipeline raises
10161
+ `DecisionPolicyError` naming the policy, with the trace so far. If you already
10162
+ catch that for a bad return value, you now catch two more cases with it.
10163
+
10164
+ **2. `createDiagnosticJournal` moved.**
10165
+
10166
+ ```ts
10167
+ // before
10168
+ import { createDiagnosticJournal } from 'stitchkit/application'
10169
+ // after
10170
+ import { createDiagnosticJournal } from 'stitchkit/application/diagnostic-journal'
10171
+ ```
10172
+
10173
+ Only the factory moved. `DiagnosticJournalConfig`, every
10174
+ `DiagnosticJournal*Schema` and `readDiagnosticJournalLockDiagnosis` stay where
10175
+ they were.
10176
+
10177
+ **If you restart resources**, nothing to change — but three things that did not
10178
+ work now do: a managed schedule can be restarted, a restarted keyspace accepts
10179
+ writes again, and a restarted managed server is actually shut down at exit. If
10180
+ you pass `managedServerResource` a server *instance*, a restart is now refused
10181
+ by name; pass a factory (`server: (context) => …`) to restart it.
10182
+
10104
10183
  ## Released migration: 0.77.0
10105
10184
 
10106
10185
  Two type renames, and only if you named them. Nothing runtime moved.
@@ -13570,7 +13649,7 @@ cutovers are covered by the executable
13570
13649
  |--------|------|---------|
13571
13650
  | `createApplication` | function | compose a validated resource DAG into one startup, readiness, admission and shutdown state machine |
13572
13651
  | `ApplicationHandle.restart` | method | replace one resource and everything that depends on it, leaving the rest of the graph running and the process epoch unchanged |
13573
- | `ApplicationRestartInputSchema` / `ApplicationRestartInput` | schema / _type_ | the resource to replace, by id |
13652
+ | `ApplicationRestartInputSchema` / `ApplicationRestartInput` | schema / _type_ | the resource to replace, by id, and optionally `gracePeriodMs` / `forceTimeoutMs` for this restart — the application's own shutdown budget otherwise |
13574
13653
  | `ApplicationRestartResultSchema` / `ApplicationRestartResult` | schema / _type_ | the subtree that was actually taken down and brought back, in start order, with the outcome, the reason on anything but success, and how long it took |
13575
13654
  | `ApplicationRestartOutcomeSchema` / `ApplicationRestartOutcome` | schema / _type_ | `restarted`, `failed` or `refused` — a refusal (unknown id, shutting down, not yet ready) is not a failure and touches nothing |
13576
13655
  | `ApplicationResourceFailure` | _type_ | one resource failure with the cause its phase label cannot carry — delivered to `onResourceFailure` |
@@ -13721,11 +13800,12 @@ vocabulary with an event topic declared `mode: 'decision'`.
13721
13800
  | Export | Kind | Summary |
13722
13801
  |--------|------|---------|
13723
13802
  | `createDecisionPipeline` | function | run policies in declaration order; the first terminal verdict wins and the rest do not run |
13803
+ | `DecisionPipelineConfig` | _type_ | `policyTimeoutMs` — how long one policy has to answer. Required, no default: a policy that never settles hangs every caller of the operation it guards |
13724
13804
  | `PolicyDecisionSchema` / `PolicyDecision` | schema / _type_ | `allow`, `deny` with a reason, or `defer` — a deny cannot be silent |
13725
13805
  | `DecisionPolicy` / `DecisionPipeline` | _types_ | one named voter over the caller's subject, and the pipeline it composes into |
13726
13806
  | `DecisionResult` / `DecisionTraceEntry` | _types_ | the verdict, and the trace of what actually ran — not what was declared |
13727
13807
  | `DecisionUndecidedError` | class | every policy deferred and no undecided outcome was declared: an unanswered question, raised rather than guessed |
13728
- | `DecisionPolicyError` | class | a policy threw or timed out; the pipeline denies with the policy named |
13808
+ | `DecisionPolicyError` | class | a policy returned a non-decision, threw, or ran past `policyTimeoutMs` one error for all three, carrying the policy id and the trace so far |
13729
13809
 
13730
13810
  ### Keyspace and watched reads
13731
13811
 
@@ -13733,7 +13813,7 @@ vocabulary with an event topic declared `mode: 'decision'`.
13733
13813
  |--------|------|---------|
13734
13814
  | `defineKeyspace` / `keyspaceResource` | function | a named record set read synchronously from memory and written through one serialised chain; backend first, then memory, then the change event |
13735
13815
  | `openKeyspace` / `OpenedKeyspace` | function / type | the same keyspace opened directly, for an application that owns its own lifecycle rather than declaring resources to a kernel |
13736
- | `KeyspaceBackend` / `KeyspaceDeclaration` / `KeyspaceChange` / `KeyspaceResourceConfig` / `OpenKeyspace` | _types_ | the four-method durability port, the declaration, the announced change, the resource's options and the published handle |
13816
+ | `KeyspaceBackend` / `KeyspaceDeclaration` / `KeyspaceChange` / `KeyspaceResourceConfig` / `OpenKeyspace` | _types_ | the four-method durability port, the declaration, the announced change, the resource's options and the published handle. `backend` takes a value or a **factory** — a factory is called once per generation, so a restarted keyspace gets a backend that was never closed |
13737
13817
  | `memoryKeyspaceBackend` / `sqliteKeyspaceBackend` / `SqliteKeyspaceBackendConfig` | function / type | a disposable in-process backend, and one table with a key and a JSON payload over a caller-owned database |
13738
13818
  | `SqliteDatabase` / `SqliteStatement` / `SqliteValue` | _types_ | the minimal synchronous SQLite boundary the framework types against |
13739
13819
  | `createWatchHub` / `watchKey` | function | one read per question however many are asking: single-flight per key, re-read on a declared topic, publish only what changed |
@@ -13770,6 +13850,23 @@ snapshots; the adapter owns no SDK lifecycle, polling or delta state.
13770
13850
  | `ApplicationTelemetryMeter` | _type_ | minimal structural `Meter.createObservableGauge` boundary compatible with `@opentelemetry/api` |
13771
13851
  | `ApplicationOpenTelemetryCollectionError` | _type_ | isolated instrument-name/error diagnostic without product/provider attributes |
13772
13852
 
13853
+ ## `stitchkit/application/diagnostic-journal`
13854
+
13855
+ | Export | Kind | Summary |
13856
+ |--------|------|---------|
13857
+ | `createDiagnosticJournal` | function | the bounded local journal: a file lock, a spawn to diagnose a stale one, and framed writes |
13858
+
13859
+ Its own entrypoint because of what it reaches — `node:child_process`,
13860
+ `node:fs`, `node:os`, `node:util` — with `promisify(execFile)` evaluated while
13861
+ the module initialises. Exported from `stitchkit/application`, that one line made
13862
+ the entire barrel unusable in a browser bundle: not by failing at the call, but
13863
+ by throwing during module initialisation, on every route.
13864
+
13865
+ Its **contract** stays in `stitchkit/application` — every
13866
+ `DiagnosticJournal*Schema`, the states and the refusal reasons touch nothing but
13867
+ Zod, and a client reading a journal's status has as much right to them as the
13868
+ server writing it.
13869
+
13773
13870
  ## `stitchkit/application/schemas`
13774
13871
 
13775
13872
  The canonical application records — and nothing else. Every export here is also
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.77.0",
3
+ "version": "0.78.0",
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",
@@ -129,6 +129,10 @@
129
129
  "types": "./dist/application-opentelemetry.d.ts",
130
130
  "import": "./dist/application-opentelemetry.js"
131
131
  },
132
+ "./application/diagnostic-journal": {
133
+ "types": "./dist/application-diagnostic-journal.d.ts",
134
+ "import": "./dist/application-diagnostic-journal.js"
135
+ },
132
136
  "./application/schemas": {
133
137
  "types": "./dist/application-schemas.d.ts",
134
138
  "import": "./dist/application-schemas.js"
@@ -160,11 +164,11 @@
160
164
  "scripts": {
161
165
  "build:native-contained-files": "node scripts/build-contained-files-native.mjs",
162
166
  "check": "bun x tsc --noEmit",
163
- "build:browser": "bun build src/index.ts src/live.ts src/react.ts src/contract/index.ts src/primitives.ts src/declaration.ts src/application-schemas.ts src/agent-runtime-browser.ts --outdir dist --target node --packages external --splitting --root src",
164
- "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 src/telegram.ts --outdir dist --target node --packages external --splitting --root src",
167
+ "build:browser": "bun build src/index.ts src/live.ts src/react.ts src/contract/index.ts src/primitives.ts src/declaration.ts src/application-schemas.ts src/application.ts src/remote.ts src/agent-runtime-browser.ts --outdir dist --target node --packages external --splitting --root src",
168
+ "build:server": "bun build src/server/index.ts src/node.ts src/tools.ts src/tool-invoker.ts src/cli.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-diagnostic-journal.ts src/application-grammy.ts src/application-opentelemetry.ts src/telegram.ts --outdir dist --target node --packages external --splitting --root src",
165
169
  "build:js": "bun run build:browser && bun run build:server && bun scripts/preserve-webpack-ignore.mjs",
166
170
  "build:types": "bun x tsc -p tsconfig.build.json --emitDeclarationOnly && bun scripts/rewrite-declaration-specifiers.mjs",
167
- "build": "rm -rf dist && bun run build:js && bun run build:types && bun scripts/check-browser-clean.mjs && bun scripts/check-env-live.mjs && bun scripts/check-public-types.mjs",
171
+ "build": "rm -rf dist && bun run build:js && bun run build:types && bun scripts/check-browser-clean.mjs && bun scripts/check-browser-executes.mjs && bun scripts/check-env-live.mjs && bun scripts/check-public-types.mjs",
168
172
  "dev": "bun run build:js -- --watch",
169
173
  "prepack": "cp ../../README.md ./README.md && bun ../../scripts/gen-llms.ts && bun run build",
170
174
  "test": "bun test",