stitchkit 0.76.2 → 0.77.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.
- package/dist/application/decisions.d.ts +86 -0
- package/dist/application/decisions.d.ts.map +1 -0
- package/dist/application/kernel.d.ts +33 -1
- package/dist/application/kernel.d.ts.map +1 -1
- package/dist/application-schemas.d.ts +15 -0
- package/dist/application-schemas.d.ts.map +1 -0
- package/dist/application-schemas.js +111 -0
- package/dist/application.d.ts +3 -2
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +181 -110
- package/dist/{index-hvftzz91.js → index-vc1b0b1b.js} +221 -117
- package/dist/internal/decision.d.ts +31 -0
- package/dist/internal/decision.d.ts.map +1 -0
- package/dist/live/events.d.ts +8 -13
- package/dist/live/events.d.ts.map +1 -1
- package/dist/live.d.ts +1 -1
- package/dist/live.d.ts.map +1 -1
- package/dist/server/event-bus.d.ts +3 -2
- package/dist/server/event-bus.d.ts.map +1 -1
- package/dist/testing.js +1 -1
- package/llms-full.txt +177 -4
- package/package.json +6 -2
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
|
|
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 |
|
|
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,9 +70,10 @@ 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
|
|
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 |
|
|
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
77
|
|
|
77
78
|
Rule of thumb: browser code imports `stitchkit` and `stitchkit/react`; server
|
|
78
79
|
code adds `stitchkit/server` (or `stitchkit/node` on Node) and opts into
|
|
@@ -5478,6 +5479,105 @@ Every instrument uses unit `1` and reports an absolute current/lifetime value:
|
|
|
5478
5479
|
| `stitchkit.application.schedule.accepting`, `.active`, `.queued`, `.runs_started`, `.runs_completed`, `.runs_failed`, `.ticks_skipped` | current schedule state and absolute run/tick counts |
|
|
5479
5480
|
| `stitchkit.application.activity.active`, `.queued`, `.completed`, `.failed` | absolute stage projections for declared activity sources |
|
|
5480
5481
|
|
|
5482
|
+
## Replacing part of the graph without stopping the process
|
|
5483
|
+
|
|
5484
|
+
`restart` takes down one resource **and everything that depends on it**, then
|
|
5485
|
+
brings that subtree back. Everything else keeps running, and the process epoch
|
|
5486
|
+
does not move.
|
|
5487
|
+
|
|
5488
|
+
```ts
|
|
5489
|
+
const result = await app.restart({ resourceId: 'database' })
|
|
5490
|
+
|
|
5491
|
+
result.outcome // 'restarted' | 'failed' | 'refused'
|
|
5492
|
+
result.affected // ['database', 'repository', 'api'] — in start order
|
|
5493
|
+
```
|
|
5494
|
+
|
|
5495
|
+
The dependants come down with it because they are holding what the resource
|
|
5496
|
+
published. A repository that kept running across a database replacement holds a
|
|
5497
|
+
pool that has been closed: it still typechecks, still has methods, and fails at
|
|
5498
|
+
whatever moment the first call happens. There is no version of this where a
|
|
5499
|
+
dependant keeps a live handle, so the subtree — not the resource — is the unit.
|
|
5500
|
+
|
|
5501
|
+
A leaf restart affects one resource, and an independent neighbour is never
|
|
5502
|
+
touched:
|
|
5503
|
+
|
|
5504
|
+
```ts
|
|
5505
|
+
await app.restart({ resourceId: 'cache' })
|
|
5506
|
+
// mailer and database were not stopped, not started, not activated
|
|
5507
|
+
```
|
|
5508
|
+
|
|
5509
|
+
**Refused is not failed.** An unknown id, a restart during shutdown, and a
|
|
5510
|
+
restart before the application is ready all return `refused` with a reason and
|
|
5511
|
+
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.
|
|
5515
|
+
|
|
5516
|
+
Restarts of overlapping subtrees queue behind each other rather than being
|
|
5517
|
+
refused. Two callers asking at once is ordinary; two generations of one resource
|
|
5518
|
+
alive at once is the thing this must make impossible.
|
|
5519
|
+
|
|
5520
|
+
```ts
|
|
5521
|
+
// Both succeed, one complete pass after the other.
|
|
5522
|
+
await Promise.all([
|
|
5523
|
+
app.restart({ resourceId: 'database' }),
|
|
5524
|
+
app.restart({ resourceId: 'database' }),
|
|
5525
|
+
])
|
|
5526
|
+
```
|
|
5527
|
+
|
|
5528
|
+
What a restart is **not** is a process restart: it replaces resources, and does
|
|
5529
|
+
not re-read configuration the kernel captured when it was constructed.
|
|
5530
|
+
|
|
5531
|
+
→ [ADR 0154](../decisions/0154-the-unit-of-a-restart-is-the-subtree.md).
|
|
5532
|
+
|
|
5533
|
+
## Decisions a policy set makes together
|
|
5534
|
+
|
|
5535
|
+
`createDecisionPipeline` runs an ordered list of policies that each vote
|
|
5536
|
+
`allow`, `deny` (with a reason) or `defer`. The first terminal verdict wins and
|
|
5537
|
+
the rest do not run.
|
|
5538
|
+
|
|
5539
|
+
```ts
|
|
5540
|
+
import { createDecisionPipeline } from 'stitchkit/application'
|
|
5541
|
+
|
|
5542
|
+
const pipeline = createDecisionPipeline<{ userId: string; scope: string }>([
|
|
5543
|
+
{ id: 'banned', decide: (r) => (isBanned(r.userId)
|
|
5544
|
+
? { outcome: 'deny', reason: 'account suspended' }
|
|
5545
|
+
: { outcome: 'defer' }) },
|
|
5546
|
+
{ id: 'scope', decide: (r) => (r.scope === 'admin'
|
|
5547
|
+
? { outcome: 'allow' }
|
|
5548
|
+
: { outcome: 'defer' }) },
|
|
5549
|
+
{ id: 'default', decide: () => ({ outcome: 'deny', reason: 'no policy allowed this' }) },
|
|
5550
|
+
])
|
|
5551
|
+
|
|
5552
|
+
const result = await pipeline.decide(request)
|
|
5553
|
+
result.outcome // 'allow' | 'deny' — never 'defer'
|
|
5554
|
+
result.trace // the policies that actually ran, in order
|
|
5555
|
+
```
|
|
5556
|
+
|
|
5557
|
+
Three things are deliberate.
|
|
5558
|
+
|
|
5559
|
+
**A deny carries a reason, by schema.** `{ outcome: 'deny' }` alone does not
|
|
5560
|
+
typecheck. A refusal whose cause exists only in the log of whoever refused is a
|
|
5561
|
+
support ticket.
|
|
5562
|
+
|
|
5563
|
+
**The trace is what ran, not what was declared.** It stops at the terminal
|
|
5564
|
+
verdict. When something was denied the question is which policy denied it and
|
|
5565
|
+
what the ones before it said, and a trace listing policies that never ran would
|
|
5566
|
+
answer that wrongly while looking complete.
|
|
5567
|
+
|
|
5568
|
+
**Every policy deferring raises.** `DecisionUndecidedError`, not a default —
|
|
5569
|
+
defaulting to `allow` turns an incomplete policy set into an open door, and
|
|
5570
|
+
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.
|
|
5573
|
+
|
|
5574
|
+
The same `allow`/`deny`/`defer` type is what an event topic declared
|
|
5575
|
+
`mode: 'decision'` uses in `stitchkit/live` — one vocabulary, because a listener
|
|
5576
|
+
voting on an event and a policy voting on a request are answering the same
|
|
5577
|
+
question.
|
|
5578
|
+
|
|
5579
|
+
→ [ADR 0155](../decisions/0155-one-decision-vocabulary-and-an-unanswered-question-is-an-error.md).
|
|
5580
|
+
|
|
5481
5581
|
## Admission and graceful shutdown
|
|
5482
5582
|
|
|
5483
5583
|
Use the application operation lease for work that is not already counted by a
|
|
@@ -10001,6 +10101,29 @@ makes one thing your job rather than the resolver's:
|
|
|
10001
10101
|
The mechanical part is identical either way. Only the *noticing* differs, and an
|
|
10002
10102
|
exact pin moves it onto you.
|
|
10003
10103
|
|
|
10104
|
+
## Released migration: 0.77.0
|
|
10105
|
+
|
|
10106
|
+
Two type renames, and only if you named them. Nothing runtime moved.
|
|
10107
|
+
|
|
10108
|
+
```bash
|
|
10109
|
+
rg -n "EventDecision|EventUndecided" --glob '*.ts' --glob '*.tsx'
|
|
10110
|
+
```
|
|
10111
|
+
|
|
10112
|
+
```ts
|
|
10113
|
+
// before
|
|
10114
|
+
import type { EventDecision, EventUndecided } from 'stitchkit/live'
|
|
10115
|
+
// after
|
|
10116
|
+
import type { PolicyDecision, UndecidedOutcome } from 'stitchkit/live'
|
|
10117
|
+
```
|
|
10118
|
+
|
|
10119
|
+
The shapes are identical — `{ outcome: 'allow' } | { outcome: 'deny', reason } | { outcome: 'defer' }`
|
|
10120
|
+
and `'allow' | 'deny'`. If you only ever *return* decisions from listeners and never named the
|
|
10121
|
+
type, the search above finds nothing and there is nothing to do.
|
|
10122
|
+
|
|
10123
|
+
They were renamed because `createDecisionPipeline` (new in this release) votes with exactly the
|
|
10124
|
+
same three outcomes, and two identical types under two names is the thing that makes a search for
|
|
10125
|
+
either one return half the truth.
|
|
10126
|
+
|
|
10004
10127
|
## Released migration: 0.76.0
|
|
10005
10128
|
|
|
10006
10129
|
One change, and only if you hand `createWatchClient` a transport you wrote yourself.
|
|
@@ -13150,7 +13273,8 @@ realtime contract from `stitchkit`, and the server halves live in `stitchkit/app
|
|
|
13150
13273
|
|--------|------|---------|
|
|
13151
13274
|
| `defineEvents` | function | declare topics: a wire name, one payload schema and how the topic is delivered to in-process listeners |
|
|
13152
13275
|
| `toRealtimeContract` | function | project a declaration onto `RealtimeContract`, so the existing validated socket carries it |
|
|
13153
|
-
| `EventDeliveryMode`
|
|
13276
|
+
| `EventDeliveryMode` | _type_ | `emit` / `serial` / `decision` — how a topic reaches its in-process listeners |
|
|
13277
|
+
| `PolicyDecision` / `UndecidedOutcome` | _types_ | one voter's `allow` / `deny` / `defer`, and what a run settles on when every voter defers — the same two types the decision pipeline in `stitchkit/application` uses, because a listener voting on an event and a policy voting on a request are answering the same question |
|
|
13154
13278
|
| `EventTopicDeclaration` / `EventTopicRegistry` / `EventsConfig` / `EventsDeclaration` | _types_ | one topic's schema, mode, `whenAllDefer` and `listenerTimeoutMs`, and the declaration they compose into |
|
|
13155
13279
|
| `EventPayloads` / `EventTopicsOfMode` / `WireTopic` | _types_ | payload map keyed by wire topic, the topics of one mode, and the `prefix.name` a topic is addressed by |
|
|
13156
13280
|
| `createWatchClient` | function | contract-shaped watch client: `watch.action(args)` returns a ref-counted handle sharing one subscription |
|
|
@@ -13444,7 +13568,11 @@ cutovers are covered by the executable
|
|
|
13444
13568
|
|
|
13445
13569
|
| Export | Kind | Summary |
|
|
13446
13570
|
|--------|------|---------|
|
|
13447
|
-
| `createApplication` | function | compose a validated resource DAG into one
|
|
13571
|
+
| `createApplication` | function | compose a validated resource DAG into one startup, readiness, admission and shutdown state machine |
|
|
13572
|
+
| `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 |
|
|
13574
|
+
| `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
|
+
| `ApplicationRestartOutcomeSchema` / `ApplicationRestartOutcome` | schema / _type_ | `restarted`, `failed` or `refused` — a refusal (unknown id, shutting down, not yet ready) is not a failure and touches nothing |
|
|
13448
13576
|
| `ApplicationResourceFailure` | _type_ | one resource failure with the cause its phase label cannot carry — delivered to `onResourceFailure` |
|
|
13449
13577
|
| `ApplicationResourcePhase` | _type_ | the phase a managed resource failed in — the vocabulary of `ApplicationResourceShutdown.failures` |
|
|
13450
13578
|
| `ApplicationShutdownOptionsSchema` / `ApplicationShutdownOptions` | schema / _type_ | the two shutdown budgets and an abort signal — without the HTTP-only `retryAfterSeconds` |
|
|
@@ -13568,6 +13696,10 @@ and `ApplicationEventSinkConfig`.
|
|
|
13568
13696
|
|
|
13569
13697
|
### Canonical application records
|
|
13570
13698
|
|
|
13699
|
+
These are also published on their own as
|
|
13700
|
+
[`stitchkit/application/schemas`](#stitchkitapplicationschemas), which carries
|
|
13701
|
+
no server runtime and can therefore be imported from a browser bundle.
|
|
13702
|
+
|
|
13571
13703
|
The entrypoint exports each Zod schema beside its inferred type:
|
|
13572
13704
|
`ApplicationIdSchema` / `ApplicationId`, `ApplicationLifecycleSchema` /
|
|
13573
13705
|
`ApplicationLifecycle`, `ApplicationHealthSchema` / `ApplicationHealth`,
|
|
@@ -13581,6 +13713,20 @@ the function that derives it from a snapshot,
|
|
|
13581
13713
|
`ApplicationResourceShutdownSchema` / `ApplicationResourceShutdown`, and
|
|
13582
13714
|
`ApplicationShutdownResultSchema` / `ApplicationShutdownResult`.
|
|
13583
13715
|
|
|
13716
|
+
### Decisions
|
|
13717
|
+
|
|
13718
|
+
A pipeline of policies that each vote `allow`, `deny` or `defer`, sharing its
|
|
13719
|
+
vocabulary with an event topic declared `mode: 'decision'`.
|
|
13720
|
+
|
|
13721
|
+
| Export | Kind | Summary |
|
|
13722
|
+
|--------|------|---------|
|
|
13723
|
+
| `createDecisionPipeline` | function | run policies in declaration order; the first terminal verdict wins and the rest do not run |
|
|
13724
|
+
| `PolicyDecisionSchema` / `PolicyDecision` | schema / _type_ | `allow`, `deny` with a reason, or `defer` — a deny cannot be silent |
|
|
13725
|
+
| `DecisionPolicy` / `DecisionPipeline` | _types_ | one named voter over the caller's subject, and the pipeline it composes into |
|
|
13726
|
+
| `DecisionResult` / `DecisionTraceEntry` | _types_ | the verdict, and the trace of what actually ran — not what was declared |
|
|
13727
|
+
| `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 |
|
|
13729
|
+
|
|
13584
13730
|
### Keyspace and watched reads
|
|
13585
13731
|
|
|
13586
13732
|
| Export | Kind | Summary |
|
|
@@ -13624,6 +13770,33 @@ snapshots; the adapter owns no SDK lifecycle, polling or delta state.
|
|
|
13624
13770
|
| `ApplicationTelemetryMeter` | _type_ | minimal structural `Meter.createObservableGauge` boundary compatible with `@opentelemetry/api` |
|
|
13625
13771
|
| `ApplicationOpenTelemetryCollectionError` | _type_ | isolated instrument-name/error diagnostic without product/provider attributes |
|
|
13626
13772
|
|
|
13773
|
+
## `stitchkit/application/schemas`
|
|
13774
|
+
|
|
13775
|
+
The canonical application records — and nothing else. Every export here is also
|
|
13776
|
+
reachable from `stitchkit/application`; the difference is what comes with it.
|
|
13777
|
+
|
|
13778
|
+
`stitchkit/application` is the server runtime: it reaches `node:child_process`,
|
|
13779
|
+
`node:fs` and `node:crypto`. A browser bundler does not omit those, it
|
|
13780
|
+
substitutes stubs — so a module that merely *names* an application schema fails
|
|
13781
|
+
while it is initialising, and the page never mounts, on every route rather than
|
|
13782
|
+
the one that wanted the schema. A contract whose `output` is an application
|
|
13783
|
+
snapshot could be declared and never consumed, which is the one thing a contract
|
|
13784
|
+
is for.
|
|
13785
|
+
|
|
13786
|
+
So the same schemas ship a second way, from a module with nothing behind them:
|
|
13787
|
+
|
|
13788
|
+
```ts
|
|
13789
|
+
// browser and server alike
|
|
13790
|
+
import { ApplicationSnapshotSchema } from 'stitchkit/application/schemas';
|
|
13791
|
+
```
|
|
13792
|
+
|
|
13793
|
+
The exports are exactly those listed under
|
|
13794
|
+
[canonical application records](#canonical-application-records) above.
|
|
13795
|
+
`packages/core/scripts/check-browser-clean.mjs` holds the promise against the
|
|
13796
|
+
built artifact: it walks the browser lane's real `dist` output and refuses a
|
|
13797
|
+
`node:` import reachable from any of it, and refuses an entry built for the
|
|
13798
|
+
browser that no `exports` path leads to — which is how this one was missing.
|
|
13799
|
+
|
|
13627
13800
|
---
|
|
13628
13801
|
|
|
13629
13802
|
## `stitchkit/agent-runtime`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stitchkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.77.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/schemas": {
|
|
133
|
+
"types": "./dist/application-schemas.d.ts",
|
|
134
|
+
"import": "./dist/application-schemas.js"
|
|
135
|
+
},
|
|
132
136
|
"./testing": {
|
|
133
137
|
"types": "./dist/testing.d.ts",
|
|
134
138
|
"import": "./dist/testing.js"
|
|
@@ -156,7 +160,7 @@
|
|
|
156
160
|
"scripts": {
|
|
157
161
|
"build:native-contained-files": "node scripts/build-contained-files-native.mjs",
|
|
158
162
|
"check": "bun x tsc --noEmit",
|
|
159
|
-
"build:browser": "bun build src/index.ts src/live.ts src/react.ts src/contract/index.ts src/primitives.ts src/declaration.ts src/agent-runtime-browser.ts --outdir dist --target node --packages external --splitting --root src",
|
|
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",
|
|
160
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",
|
|
161
165
|
"build:js": "bun run build:browser && bun run build:server && bun scripts/preserve-webpack-ignore.mjs",
|
|
162
166
|
"build:types": "bun x tsc -p tsconfig.build.json --emitDeclarationOnly && bun scripts/rewrite-declaration-specifiers.mjs",
|