stitchkit 0.96.1 → 0.98.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/CHANGELOG.md +50 -0
- package/dist/agent-runtime/schedule-contract.d.ts +52 -0
- package/dist/agent-runtime/schedule-contract.d.ts.map +1 -0
- package/dist/agent-runtime/schedule-dispatch.d.ts +26 -0
- package/dist/agent-runtime/schedule-dispatch.d.ts.map +1 -0
- package/dist/agent-runtime/schedule-records.d.ts +8 -14
- package/dist/agent-runtime/schedule-records.d.ts.map +1 -1
- package/dist/agent-runtime/schedule-tools.d.ts +1 -1
- package/dist/agent-runtime/schedule-tools.d.ts.map +1 -1
- package/dist/agent-runtime/schedules.d.ts +7 -92
- package/dist/agent-runtime/schedules.d.ts.map +1 -1
- package/dist/agent-runtime/sqlite-schema.d.ts.map +1 -1
- package/dist/agent-runtime/store-migrations/v3-to-v4.d.ts +4 -0
- package/dist/agent-runtime/store-migrations/v3-to-v4.d.ts.map +1 -0
- package/dist/entrypoints/agent-runtime/sqlite/bun.js +1 -1
- package/dist/entrypoints/agent-runtime/sqlite/node.js +1 -1
- package/dist/entrypoints/agent-runtime.d.ts +2 -1
- package/dist/entrypoints/agent-runtime.d.ts.map +1 -1
- package/dist/entrypoints/agent-runtime.js +289 -214
- package/dist/entrypoints/cli.js +1 -1
- package/dist/entrypoints/tools.js +1 -1
- package/dist/{index-p9e0w8hs.js → index-1m2thnq0.js} +25 -1
- package/dist/{index-t9rhjr9f.js → index-f1nb70ae.js} +45 -9
- package/dist/tools/cli/config.d.ts +5 -1
- package/dist/tools/cli/config.d.ts.map +1 -1
- package/dist/tools/cli/io.d.ts.map +1 -1
- package/dist/tools/cli/stdin.d.ts +6 -0
- package/dist/tools/cli/stdin.d.ts.map +1 -0
- package/llms/agent-runtime.2.txt +224 -0
- package/llms/agent-runtime.3.txt +1 -1
- package/llms/agent-runtime.txt +18 -225
- package/llms/cli.txt +15 -0
- package/llms/stitchkit.txt +3 -3
- package/llms/{upgrading-0.37.0-0.56.0.txt → upgrading-0.37.0-0.58.0.txt} +82 -3
- package/llms/{upgrading-0.57.0-0.69.0.txt → upgrading-0.59.0-0.71.0.txt} +108 -82
- package/llms/{upgrading-0.70.0-0.96.0.txt → upgrading-0.72.0-0.98.0.txt} +64 -108
- package/llms/upgrading.txt +3 -3
- package/llms-full.txt +98 -5
- package/llms.txt +5 -5
- package/package.json +1 -1
package/llms/agent-runtime.txt
CHANGED
|
@@ -19,7 +19,7 @@ description: Configure Stitchkit's optional durable history, stream loop, run co
|
|
|
19
19
|
type: architecture
|
|
20
20
|
status: active
|
|
21
21
|
created: 2026-08-22
|
|
22
|
-
updated: 2026-
|
|
22
|
+
updated: 2026-09-26 16:00 +07:00
|
|
23
23
|
---
|
|
24
24
|
|
|
25
25
|
# Agent application runtime
|
|
@@ -636,6 +636,23 @@ has invoked a tool is never automatically replayed.
|
|
|
636
636
|
`createAgentScheduleService` persists `at`, `after` and `every` input. Repeating
|
|
637
637
|
schedules require an explicit IANA time zone. Dispatch uses a stable
|
|
638
638
|
`schedule:<id>:<occurrence>` idempotency key, and restart lateness is recorded.
|
|
639
|
+
Resolve `void` after durable admission; return `{ status: 'retry', reason }` for a
|
|
640
|
+
transient refusal or `{ status: 'terminal', reason }` to stop the entire schedule in
|
|
641
|
+
`failed` state. Throws retry too. Backoff is 1, 2, 4, 8, 16, 32, then 60 seconds,
|
|
642
|
+
capped at 60 seconds and persisted across restart. `retryAt`, `attempts` and `lastError`
|
|
643
|
+
show retry diagnostics; success resets them. `nextAt` and `lateByMs` retain the original
|
|
644
|
+
occurrence time. Recurring schedules fire once after an idle stretch and advance to the
|
|
645
|
+
next future slot on their original cadence.
|
|
646
|
+
|
|
647
|
+
Dispatch receives an `AbortSignal`, has a 30-second deadline and holds a unique
|
|
648
|
+
60-second lease. A late or cancelled attempt cannot settle or emit firing events.
|
|
649
|
+
Honor the signal and durably deduplicate the key: a crash after admission but before
|
|
650
|
+
settlement can replay delivery. This is at-least-once, not exactly-once.
|
|
651
|
+
Due selection is indexed, limited to 32 parallel deliveries per tick; one hung batch
|
|
652
|
+
can delay newly due work by at most its deadline. The service keeps one nearest-due
|
|
653
|
+
timer plus per-attempt deadline timers. Storage failures use a bounded recovery timer;
|
|
654
|
+
idle ticks do not write. SQLite v4 migration preserves due times and claims; stop older
|
|
655
|
+
scheduler processes before migration. See [ADR 0202](../decisions/0202-schedule-retries-preserve-occurrence-identity.md).
|
|
639
656
|
`createAgentScheduleTools` exposes the exact `schedule_after`, `schedule_at`,
|
|
640
657
|
`schedule_every`, `schedule_list` and `schedule_cancel` surface.
|
|
641
658
|
|
|
@@ -667,227 +684,3 @@ a successor input into the run already in flight.
|
|
|
667
684
|
```text
|
|
668
685
|
input + queued run → running → execution settled → terminal CAS → successor
|
|
669
686
|
```
|
|
670
|
-
|
|
671
|
-
## What happens to a run when new input arrives
|
|
672
|
-
|
|
673
|
-
`runs.inputPolicy` decides. It takes five values, or a function returning one:
|
|
674
|
-
|
|
675
|
-
| policy | the run in flight | what it already produced |
|
|
676
|
-
|--------|-------------------|--------------------------|
|
|
677
|
-
| `queue` (default) | finishes first | kept |
|
|
678
|
-
| `inject` | continues, and answers the new input too | kept, and built on |
|
|
679
|
-
| `interrupt` | ends | kept, and marked as cut off |
|
|
680
|
-
| `interrupt-next` | ends; this input runs before ordinary queued work | kept, and marked as cut off |
|
|
681
|
-
| `supersede` | ends | discarded from the prompt, kept in the record |
|
|
682
|
-
|
|
683
|
-
`interrupt-next` is the explicit priority path for an urgent input. If A is
|
|
684
|
-
running, ordinary B is queued, and urgent C arrives, the coordinator aborts A,
|
|
685
|
-
waits for A to settle and then runs C before B. B keeps its durable identity and
|
|
686
|
-
eventually runs. Urgent inputs remain FIFO among themselves. They do not
|
|
687
|
-
coalesce into an ordinary pending run: that would erase the priority boundary.
|
|
688
|
-
|
|
689
|
-
`interrupt` and `supersede` differ in exactly one thing, and the question that
|
|
690
|
-
picks between them is **not** "was the run interrupted" but **"did anyone see
|
|
691
|
-
what it produced"**:
|
|
692
|
-
|
|
693
|
-
- The user pressed **stop**. The partial answer was streamed to their screen and
|
|
694
|
-
they read it. It belongs in the conversation — dropping it makes the history
|
|
695
|
-
lie to the model about what the human has seen. That is `interrupt`.
|
|
696
|
-
- A newer message **superseded** the run. Whether the partial reached anyone
|
|
697
|
-
depends on the delivery surface: a token stream shows it as it is produced, a
|
|
698
|
-
surface that sends nothing until the run is done never showed it at all. When
|
|
699
|
-
it reached nobody, it is not part of the conversation. That is `supersede`.
|
|
700
|
-
|
|
701
|
-
**Stitchkit cannot answer that question for you** — delivery belongs to the
|
|
702
|
-
transport, and the runtime sees an abort, not a screen. Hence a declared policy
|
|
703
|
-
(→ ADR 0108), and hence `inputPolicy` accepting a function, so one application
|
|
704
|
-
can hold two surfaces with different rules without the core learning which is
|
|
705
|
-
which:
|
|
706
|
-
|
|
707
|
-
```ts
|
|
708
|
-
runs: {
|
|
709
|
-
inputPolicy: (input) =>
|
|
710
|
-
protocol.parseContext(input.context).surface === 'operator' ? 'queue' : 'supersede',
|
|
711
|
-
}
|
|
712
|
-
```
|
|
713
|
-
|
|
714
|
-
`input.context` is the **raw** context here — admission runs before the runtime
|
|
715
|
-
parses it, so the callback narrows it itself with the protocol it already has.
|
|
716
|
-
|
|
717
|
-
A superseded run ends with `terminalReason: 'superseded'`, run state
|
|
718
|
-
`'superseded'` and an assistant message of status `'superseded'`. **The record
|
|
719
|
-
is kept** — excluded from the projection, not deleted — so an operator can see
|
|
720
|
-
what was thrown away, and run identity, admission receipts and the terminal CAS
|
|
721
|
-
keep the row they depend on. Compaction leaves it alone for the same reason: a
|
|
722
|
-
turn whose answer is never spoken is not a turn that may be summarised into one,
|
|
723
|
-
because that would both feed the discarded text to the summariser and drop the
|
|
724
|
-
record in `replacedMessageIds`.
|
|
725
|
-
|
|
726
|
-
It is also outside the token budget. `selectAgentHistory` removes it with reason
|
|
727
|
-
`'superseded'` and does not count it, so an abandoned fragment cannot push a
|
|
728
|
-
real turn out of a context it never occupies.
|
|
729
|
-
|
|
730
|
-
### How an interrupted turn reaches the model
|
|
731
|
-
|
|
732
|
-
An interrupted turn is projected, and says so:
|
|
733
|
-
|
|
734
|
-
```text
|
|
735
|
-
{ role: 'assistant', content: [
|
|
736
|
-
{ type: 'text', text: 'We are the team, where would you like' },
|
|
737
|
-
{ type: 'text', text: '[interrupted: this turn was cut off before it finished]' },
|
|
738
|
-
]}
|
|
739
|
-
```
|
|
740
|
-
|
|
741
|
-
`history.interruptedAssistant` chooses the form, and the difference between the
|
|
742
|
-
first two is structural rather than cosmetic. **An assistant turn in provider
|
|
743
|
-
history is a commitment**: the model reads its own previous turn as something it
|
|
744
|
-
said and stays consistent with it. A system line is context.
|
|
745
|
-
|
|
746
|
-
| value | form | right when |
|
|
747
|
-
|-------|------|-----------|
|
|
748
|
-
| `assistant-marked` (default) | assistant turn plus a marker | the human read the text |
|
|
749
|
-
| `system-note` | `[interrupted] partial response: …` as a system line | the fragment reached nobody |
|
|
750
|
-
| `omit` | not projected at all | you want it gone from the request |
|
|
751
|
-
|
|
752
|
-
There is deliberately no value that reproduces what the projection used to do,
|
|
753
|
-
which was to send the partial as an ordinary assistant turn and drop its
|
|
754
|
-
`control` marker on the way. That was the defect, not a behaviour to stay
|
|
755
|
-
compatible with.
|
|
756
|
-
|
|
757
|
-
`projectAgentHistoryDetailed` reports what reached the provider, including part
|
|
758
|
-
types that no projected content stands for:
|
|
759
|
-
|
|
760
|
-
```ts
|
|
761
|
-
const { decisions } = await projectAgentHistoryDetailed(snapshot.messages)
|
|
762
|
-
// → { messageId: 'assistant-2', action: 'projected', reason: 'projected',
|
|
763
|
-
// omittedParts: ['source', 'provider'] }
|
|
764
|
-
// → { messageId: 'assistant-1', action: 'omitted', reason: 'superseded' }
|
|
765
|
-
```
|
|
766
|
-
|
|
767
|
-
`runtime.stop(key, 'supersede')` is the same decision taken by hand: the
|
|
768
|
-
process-local escape hatch chooses the reason, so a caller that knows the answer
|
|
769
|
-
was never delivered can discard it without a newer input arriving.
|
|
770
|
-
|
|
771
|
-
### An input that joins a run in flight
|
|
772
|
-
|
|
773
|
-
`inject` is right when the new input **refines** rather than redirects, and the
|
|
774
|
-
steps already taken are still worth something: *"summarise this thread… actually,
|
|
775
|
-
in bullet points"* should not throw away the reading the first message paid for.
|
|
776
|
-
|
|
777
|
-
What happens:
|
|
778
|
-
|
|
779
|
-
1. The input is admitted exactly like any other — a committed user message and a
|
|
780
|
-
**queued run**, durable before anything else happens.
|
|
781
|
-
2. At the running loop's next step boundary, that input joins its prompt. Only
|
|
782
|
-
that input: an unrelated queued submission is never carried in.
|
|
783
|
-
3. When the run finishes, its terminal commit — **one transaction** — records
|
|
784
|
-
the input as one the run answered and settles the queued successor with
|
|
785
|
-
`terminalReason: 'absorbed'` and `absorbedIntoRunId` naming the run that
|
|
786
|
-
answered it. Every ticket for that successor resolves to the same answer.
|
|
787
|
-
|
|
788
|
-
Nothing durable happens between 1 and 3, and that is the design (→ ADR 0113).
|
|
789
|
-
A run that crashes, is closed, or is interrupted after taking an input on
|
|
790
|
-
commits no absorption at all, so what is left behind is an ordinary queued
|
|
791
|
-
successor — the state every other policy already produces and recovery already
|
|
792
|
-
handles. **There is no ordering in which an accepted input becomes
|
|
793
|
-
unanswerable.** Only a run that *completes* may absorb; an interrupted one took
|
|
794
|
-
the input into its prompt and then stopped, and does not get to say it answered
|
|
795
|
-
it.
|
|
796
|
-
|
|
797
|
-
An absorbed run has **no assistant message of its own** — it produced none, and
|
|
798
|
-
writing an empty one would be a record claiming otherwise. Its answer is
|
|
799
|
-
reachable through `absorbedIntoRunId`, and the store follows that pointer itself
|
|
800
|
-
when a submission arrives on the absorbed run's idempotency key, so a retry
|
|
801
|
-
after a restart returns the answer rather than an empty terminal record.
|
|
802
|
-
|
|
803
|
-
It does publish one more `run-state` event, carrying `'superseded'`. It never
|
|
804
|
-
enters the run executor, so that event is the only thing that tells a delivery
|
|
805
|
-
surface following its `runId` that it is no longer queued.
|
|
806
|
-
|
|
807
|
-
With `coalescePending`, one successor can carry several inputs, and an
|
|
808
|
-
absorption covers a successor **whole or not at all** — a partial one would
|
|
809
|
-
leave a terminal run with inputs nobody answered. Inputs that coalesce before
|
|
810
|
-
the absorbing run's last step boundary join the same absorption. One that
|
|
811
|
-
arrives after it cancels the absorption: the successor then answers all of its
|
|
812
|
-
inputs itself, and the absorbing run has answered one of them too. A duplicate
|
|
813
|
-
answer, never a missing one.
|
|
814
|
-
|
|
815
|
-
(0.63.0 shipped a version of this that committed the absorption at the step
|
|
816
|
-
boundary, before the answer existed, and it was withdrawn in 0.65.0. ADR 0113
|
|
817
|
-
records what that ordering broke.)
|
|
818
|
-
|
|
819
|
-
With `runs.coalescePending: true`, an active lane has at most one queued
|
|
820
|
-
successor. Every later accepted input is atomically appended to that successor;
|
|
821
|
-
its `AgentRun.inputMessageIds` records the whole batch and every input ticket
|
|
822
|
-
resolves to the same terminal run. Coalescing never mutates the active run.
|
|
823
|
-
|
|
824
|
-
`AbortSignal.aborted` is only a cooperation request. A successor does not begin
|
|
825
|
-
while the predecessor still owns managed callbacks. A hung predecessor blocks
|
|
826
|
-
the lane in the first version.
|
|
827
|
-
|
|
828
|
-
Shutdown is two-phase, and both budgets carry the names they carry everywhere
|
|
829
|
-
else in Stitchkit:
|
|
830
|
-
|
|
831
|
-
```ts
|
|
832
|
-
const closed = await runtime.close({ gracePeriodMs: 30_000, forceTimeoutMs: 5_000 })
|
|
833
|
-
if (!closed.settled) {
|
|
834
|
-
console.warn(`exiting with ${closed.remaining} run(s) still in flight`)
|
|
835
|
-
}
|
|
836
|
-
```
|
|
837
|
-
|
|
838
|
-
`close` first rejects new process-local admissions and gives active runs
|
|
839
|
-
`gracePeriodMs` to finish on their own. Only after that budget expires does it
|
|
840
|
-
abort them with reason `shutdown`; `forceTimeoutMs` then bounds the settlement
|
|
841
|
-
wait for a non-cooperative model or tool, measured from the abort — the two
|
|
842
|
-
budgets add up rather than overlapping.
|
|
843
|
-
|
|
844
|
-
**`close()` reports what it achieved rather than promising an outcome it cannot
|
|
845
|
-
reach.** The result is `{ settled, timedOut, remaining }`: `settled` when every
|
|
846
|
-
in-flight run finished, `timedOut` with a `remaining` count when the force
|
|
847
|
-
budget expired first. There is no combination of budgets that is both bounded
|
|
848
|
-
and guaranteed to leave nothing in flight — that is the trade the budgets exist
|
|
849
|
-
to make, and the result is where you read which side of it you got:
|
|
850
|
-
|
|
851
|
-
| budgets | behaviour | can return with a run in flight |
|
|
852
|
-
|---------|-----------|---------------------------------|
|
|
853
|
-
| neither | aborts immediately, waits for settlement | no — unbounded wait |
|
|
854
|
-
| `gracePeriodMs` only | waits, aborts, then waits for settlement | no — unbounded wait |
|
|
855
|
-
| `forceTimeoutMs` only | aborts immediately, waits at most that long | **yes** — `timedOut` |
|
|
856
|
-
| both | waits, aborts, waits at most that long | **yes** — `timedOut` |
|
|
857
|
-
|
|
858
|
-
A caller that wants the old shape still writes `await runtime.close(…)` and
|
|
859
|
-
ignores the result. A caller deciding whether to exit the process reads it.
|
|
860
|
-
Durably queued records rejected from the local queue remain recoverable through
|
|
861
|
-
`scanRecoverable`; close never marks them terminal on its own.
|
|
862
|
-
|
|
863
|
-
Use `await runtime.interrupt({ conversationId, runId })` when the interruption
|
|
864
|
-
must be durable: it first commits `interrupt_requested`, then aborts the local
|
|
865
|
-
coordinator signal. If provider completion races that revision change, the terminal path reloads
|
|
866
|
-
the canonical snapshot. An already-terminal winner settles the ticket directly; a still-owned
|
|
867
|
-
`interrupt_requested` run is committed as `interrupted`, and unrelated aggregate-head conflicts
|
|
868
|
-
remain retriable while the run is active with the same owner and fencing token. A stale owner or
|
|
869
|
-
fencing token remains a conflict. Only the execution that applies the terminal mutation publishes the
|
|
870
|
-
**delivery** `terminal` event — a loser settles from canonical state without republishing the turn,
|
|
871
|
-
and its `AgentRuntimeResult.metrics` is `undefined`. The **operator** `run-terminal` event is not
|
|
872
|
-
gated that way: a losing execution still ran, and still spent whatever it spent, so it reports its
|
|
873
|
-
own usage. The two channels answer to different readers — delivering a turn twice is a user's
|
|
874
|
-
problem, and omitting a run's cost is an operator's.
|
|
875
|
-
`runtime.stop(key)` is the process-local signal-only escape hatch.
|
|
876
|
-
|
|
877
|
-
When an operator has positive evidence that another process owning one known
|
|
878
|
-
run is gone, close that run through the runtime boundary rather than updating
|
|
879
|
-
an adapter table or JSON payload directly:
|
|
880
|
-
|
|
881
|
-
```ts
|
|
882
|
-
await runtime.abandon({
|
|
883
|
-
conversationId,
|
|
884
|
-
runId,
|
|
885
|
-
expectedRevision: run.revision,
|
|
886
|
-
staleOwner: true,
|
|
887
|
-
})
|
|
888
|
-
```
|
|
889
|
-
|
|
890
|
-
The revision protects against stale operator evidence. The mutation updates the
|
|
891
|
-
canonical run and failed assistant atomically, removes the run from recovery
|
|
892
|
-
indexes, and publishes the resulting `run-state`. A run that moved since it was
|
|
893
|
-
read returns a conflict; `staleOwner: true` is mandatory evidence, not a default.
|
package/llms/cli.txt
CHANGED
|
@@ -205,6 +205,21 @@ fields in declaration order, so `myapp generate "a fox"` is
|
|
|
205
205
|
`--prompt "a fox"`. A piped value fills the first required unset field:
|
|
206
206
|
`echo "a fox" | myapp generate`.
|
|
207
207
|
|
|
208
|
+
Automatic stdin routing skips TTYs and waits at most **250 ms for the first byte**.
|
|
209
|
+
An empty open pipe or EOF without data leaves the field unset, so normal schema
|
|
210
|
+
validation reports the missing field instead of waiting indefinitely. Once any
|
|
211
|
+
byte arrives, the reader collects the entire stream until EOF, including chunks
|
|
212
|
+
separated by long pauses; it decodes UTF-8 after collecting all bytes and trims
|
|
213
|
+
surrounding whitespace. Read errors or a close before EOF fail the invocation.
|
|
214
|
+
Explicit arguments and commands without required unset fields never read stdin.
|
|
215
|
+
|
|
216
|
+
A producer that takes longer than 250 ms to start must supply its value as an
|
|
217
|
+
argument (for example, `myapp generate --prompt "$(slow-producer)"`) or the
|
|
218
|
+
application must provide an explicit `CliConfig.stdin` reader that waits for it.
|
|
219
|
+
That hook is not wrapped in the availability timeout. `-` remains literal data,
|
|
220
|
+
not a special stdin marker. JSONL stream/batch commands own their input and wait
|
|
221
|
+
for lines as usual; MCP/serve and long-running handlers have no new deadline.
|
|
222
|
+
|
|
208
223
|
A token is an option only when it is a long form (`--name`, `--name=value`,
|
|
209
224
|
`--`) or `-` followed by a letter. Anything else that starts with `-` is data:
|
|
210
225
|
`myapp note a "- item"` fills a positional, and after an option that expects a
|
package/llms/stitchkit.txt
CHANGED
|
@@ -77,11 +77,11 @@ own, recorded as an ADR.
|
|
|
77
77
|
| `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
|
|
78
78
|
| `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
|
|
79
79
|
| `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
|
|
80
|
-
| `stitchkit/observability` | server | stable<br>_redefined in 2 of the
|
|
80
|
+
| `stitchkit/observability` | server | stable<br>_redefined in 2 of the 43 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
|
|
81
81
|
| `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the managed-resource conformance kit |
|
|
82
82
|
| `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
|
|
83
83
|
| `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
|
|
84
|
-
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in
|
|
84
|
+
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 22 of the 43 minors since 0.56.2, most recently 0.97.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
|
|
85
85
|
| `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
|
|
86
86
|
| `stitchkit/agent-runtime/testing` | tests on Bun or Node | evolving | credential-free replay, scripted provider faults, deterministic race controls and the agent-store conformance kit |
|
|
87
87
|
| `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
|
|
@@ -91,7 +91,7 @@ own, recorded as an ADR.
|
|
|
91
91
|
| `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
|
|
92
92
|
| `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
|
|
93
93
|
| `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
|
|
94
|
-
| `stitchkit/application` | browser + server | evolving<br>_redefined in 9 of the
|
|
94
|
+
| `stitchkit/application` | browser + server | evolving<br>_redefined in 9 of the 43 minors since 0.56.2, most recently 0.96.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
|
|
95
95
|
| `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
|
|
96
96
|
| `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
|
|
97
97
|
| `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
|
|
@@ -1,14 +1,93 @@
|
|
|
1
|
-
# Upgrading 0.37.0 → 0.
|
|
1
|
+
# Upgrading 0.37.0 → 0.58.0 — stitchkit docs slice
|
|
2
2
|
|
|
3
3
|
> Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
|
|
4
4
|
> `llms.txt` in this package.
|
|
5
5
|
|
|
6
|
-
Contains: Guide: Upgrading, 0.37.0 → 0.
|
|
6
|
+
Contains: Guide: Upgrading, 0.37.0 → 0.58.0 (docs/guide/upgrading.md)
|
|
7
7
|
|
|
8
8
|
==============================================================================
|
|
9
|
-
# Guide: Upgrading, 0.37.0 → 0.
|
|
9
|
+
# Guide: Upgrading, 0.37.0 → 0.58.0 (docs/guide/upgrading.md)
|
|
10
10
|
==============================================================================
|
|
11
11
|
|
|
12
|
+
## Released migration: 0.58.0
|
|
13
|
+
|
|
14
|
+
### The default history projection rejects invalid chronology
|
|
15
|
+
|
|
16
|
+
`projectAgentHistory` no longer forwards records a provider contract cannot
|
|
17
|
+
accept. A completed assistant record before the first user message, and an
|
|
18
|
+
assistant record whose tool calls have no matching results, are omitted with an
|
|
19
|
+
inspectable decision instead of being sent upstream:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
// before — a leading assistant record was forwarded as-is
|
|
23
|
+
projectAgentHistory(messages)
|
|
24
|
+
// after — opt in explicitly, and only where the provider contract permits it
|
|
25
|
+
projectAgentHistory(messages, { leadingAssistant: 'allow' })
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Two consequences the changelog does not spell out. First, the projection can now
|
|
29
|
+
return **fewer** messages than the history holds, so any assertion or metric
|
|
30
|
+
that compared projected length against stored length will move; read the
|
|
31
|
+
detailed projection instead — it reports what was omitted and why, which is the
|
|
32
|
+
supported way to see the difference. Second, the omission is silent to the
|
|
33
|
+
provider but not to you: if a conversation suddenly loses its leading context,
|
|
34
|
+
the decision record is where that shows up, not the transcript.
|
|
35
|
+
|
|
36
|
+
### Operator events redact `internalCause` by default
|
|
37
|
+
|
|
38
|
+
Raw provider and tool failures no longer travel in operator events unless the
|
|
39
|
+
sink asks for them:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
// before — internalCause was present
|
|
43
|
+
createAgentObservability({ write })
|
|
44
|
+
// after — an explicit operator-only opt-in
|
|
45
|
+
createAgentObservability({ write, includeInternalCause: true })
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The consequence to check before upgrading: any dashboard, alert or log
|
|
49
|
+
processor keyed on `internalCause` goes blind the moment you upgrade, and it
|
|
50
|
+
goes blind quietly — the field is absent, not empty. Set the flag on the
|
|
51
|
+
operator sink you own. Product delivery stays redacted regardless of the flag;
|
|
52
|
+
this option cannot widen what reaches a user.
|
|
53
|
+
|
|
54
|
+
## Released migration: 0.57.0
|
|
55
|
+
|
|
56
|
+
### Duplicate admission results carry the complete identity
|
|
57
|
+
|
|
58
|
+
Custom `AgentRuntimeStore` adapters must persist and return the input and
|
|
59
|
+
assistant identities associated with an idempotency key:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
// before
|
|
63
|
+
return { outcome: 'duplicate', runId, snapshot }
|
|
64
|
+
|
|
65
|
+
// after
|
|
66
|
+
return { outcome: 'duplicate', input, inputMessageId, runId, assistantMessageId, snapshot }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Prefer replacing the custom aggregate reducer with `createAgentRuntimeStore()`;
|
|
70
|
+
its admission record and transaction driver implement this contract
|
|
71
|
+
automatically. Historical note: 0.59.0 reshaped this driver again, so an adapter
|
|
72
|
+
crossing both versions should read that section first and migrate once.
|
|
73
|
+
|
|
74
|
+
### `AgentRuntimeEvent` adds a post-commit `admission` variant
|
|
75
|
+
|
|
76
|
+
Add it to any exhaustive publisher switch. Its `assistant` is either the pending
|
|
77
|
+
placeholder for a new assignment or the canonical persisted assistant for a
|
|
78
|
+
duplicate:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
case 'admission':
|
|
82
|
+
await persistProductProjection(event.input, event.run, event.assistant)
|
|
83
|
+
break
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The consequence for an exhaustive switch written without a `default` branch is a
|
|
87
|
+
compile error, which is the point. The consequence for a switch that *has* a
|
|
88
|
+
`default` is worse and silent: post-commit admissions fall into it and are
|
|
89
|
+
projected as an unknown event. Grep for publisher switches before upgrading.
|
|
90
|
+
|
|
12
91
|
## Released migration: 0.56.0
|
|
13
92
|
|
|
14
93
|
### Surface manifests are version 2
|
|
@@ -1,14 +1,119 @@
|
|
|
1
|
-
# Upgrading 0.
|
|
1
|
+
# Upgrading 0.59.0 → 0.71.0 — stitchkit docs slice
|
|
2
2
|
|
|
3
3
|
> Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
|
|
4
4
|
> `llms.txt` in this package.
|
|
5
5
|
|
|
6
|
-
Contains: Guide: Upgrading, 0.
|
|
6
|
+
Contains: Guide: Upgrading, 0.59.0 → 0.71.0 (docs/guide/upgrading.md)
|
|
7
7
|
|
|
8
8
|
==============================================================================
|
|
9
|
-
# Guide: Upgrading, 0.
|
|
9
|
+
# Guide: Upgrading, 0.59.0 → 0.71.0 (docs/guide/upgrading.md)
|
|
10
10
|
==============================================================================
|
|
11
11
|
|
|
12
|
+
## Released migration: 0.71.0
|
|
13
|
+
|
|
14
|
+
The Agent coding tools. Two of the three changes are visible to the compiler;
|
|
15
|
+
the third is the one to read carefully, because nothing will point at it.
|
|
16
|
+
|
|
17
|
+
### If you implement `authorize`
|
|
18
|
+
|
|
19
|
+
The operation union gained `edit`, `list` and `glob` and lost `patch`. An
|
|
20
|
+
exhaustive matcher stops compiling and the compiler shows you every arm. **A
|
|
21
|
+
matcher with a default branch does not**, and that is the dangerous case:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// before — and after this upgrade, silently wrong in both directions
|
|
25
|
+
if (request.operation === 'patch') return reviewPatch(request)
|
|
26
|
+
return true // now also authorizes edit, list and glob
|
|
27
|
+
// …or: return false // now also kills edit_file
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
// after
|
|
32
|
+
switch (request.operation) {
|
|
33
|
+
case 'edit': return reviewEdit(request) // the old `patch` payload, unchanged
|
|
34
|
+
case 'list':
|
|
35
|
+
case 'glob': return true // or your own policy
|
|
36
|
+
// …existing read / write / search / shell / artifact-read arms
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`write` gained `createsDirectories` — the workspace-relative directories the
|
|
41
|
+
call would create, outermost first, reported **before** anything is created. A
|
|
42
|
+
host that wants to refuse implicit directory creation now can.
|
|
43
|
+
|
|
44
|
+
### If you call `apply_patch`
|
|
45
|
+
|
|
46
|
+
It is `edit_file`, and it is one call:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
// before
|
|
50
|
+
const read = await readFile({ path })
|
|
51
|
+
await applyPatch({ path, baseSha256: read.sha256, oldText, newText, dryRun: true })
|
|
52
|
+
await applyPatch({ path, baseSha256: read.sha256, oldText, newText, dryRun: false })
|
|
53
|
+
|
|
54
|
+
// after
|
|
55
|
+
await editFile({ path, oldText, newText })
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`expectedSha256` is optional and still refuses a stale base with `CONFLICT` when
|
|
59
|
+
you pass it; `edit_file` returns the resulting `sha256`, so a chain of edits
|
|
60
|
+
never needs to re-read. If you key an approval policy or a UI label on the string
|
|
61
|
+
`apply_patch`, update the key — nothing will fail loudly.
|
|
62
|
+
|
|
63
|
+
### If you match on `INTERNAL_SERVER_ERROR` from a coding tool
|
|
64
|
+
|
|
65
|
+
Ordinary outcomes no longer arrive that way. A missing file is `NOT_FOUND`, an
|
|
66
|
+
existing file without `overwrite` is `CONFLICT`, an ambiguous snippet is
|
|
67
|
+
`CONFLICT` with the occurrence count, a path outside the root is `FORBIDDEN`.
|
|
68
|
+
Code that treated any coding-tool failure as an internal fault will now see
|
|
69
|
+
codes it did not before; code that showed the model an empty error now has a
|
|
70
|
+
sentence and a `hint` to show it. Host-level causes are unchanged and still
|
|
71
|
+
scrubbed.
|
|
72
|
+
|
|
73
|
+
### If you write files into new directories
|
|
74
|
+
|
|
75
|
+
Nothing to change: `write_file` creates missing parents inside the root. Read
|
|
76
|
+
`createdDirectories` in the result if you want to notice a typo — a path that
|
|
77
|
+
was a failure before is now a successful write into a new tree.
|
|
78
|
+
|
|
79
|
+
### If you want a step to know its context budget
|
|
80
|
+
|
|
81
|
+
Opt in by reading it; nothing is injected for you:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
loop: {
|
|
85
|
+
prepareStep: ({ contextUsage }) => {
|
|
86
|
+
const used = contextUsage?.usedTokens
|
|
87
|
+
if (used?.provenance === 'unavailable') return {} // no step has landed yet
|
|
88
|
+
const fraction = (used?.value ?? 0) / (contextUsage?.contextWindow ?? 1)
|
|
89
|
+
// …render it wherever you put it
|
|
90
|
+
return {}
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Put it at the **tail** of the conversation rather than in the system
|
|
96
|
+
instructions unless you have a reason: changing the system prompt on every step
|
|
97
|
+
invalidates the provider's prefix cache for the whole conversation, and on a long
|
|
98
|
+
run that is a multiple of the input cost.
|
|
99
|
+
|
|
100
|
+
## Released migration: 0.70.0
|
|
101
|
+
|
|
102
|
+
### Descriptor-backed Agent filesystem containment
|
|
103
|
+
|
|
104
|
+
Built-in coding file/search tools and `createAgentHarnessFileResources` now require a runtime
|
|
105
|
+
that can address an opened directory descriptor: Linux `/proc/self/fd`, or the packaged macOS
|
|
106
|
+
Node-API backend added in 0.70.1. This is what keeps a mutable parent rename or outside-symlink replacement from changing
|
|
107
|
+
the authorized target between validation and the actual effect.
|
|
108
|
+
|
|
109
|
+
No call-site change is needed on those platforms. The 0.70.0 notes incorrectly inferred macOS and
|
|
110
|
+
FreeBSD support from `/dev/fd`; macOS is restored by 0.70.1 and FreeBSD remains unsupported. On
|
|
111
|
+
Windows or another platform without that
|
|
112
|
+
boundary, move the built-in filesystem operations to a supported worker or replace them with
|
|
113
|
+
application-owned tools backed by an equivalent native handle API. They fail closed rather than
|
|
114
|
+
falling back to path spelling. `run_command` remains separately available under its explicit
|
|
115
|
+
executable alias and authorization policy; this change does not claim an executable sandbox.
|
|
116
|
+
|
|
12
117
|
## Released migration: 0.69.0
|
|
13
118
|
|
|
14
119
|
### Direct coding-tool operation names
|
|
@@ -1035,82 +1140,3 @@ createAgentRuntimeStore({
|
|
|
1035
1140
|
`runAgentStoreConformance()` against the migrated adapter before switching production traffic.
|
|
1036
1141
|
If an application implements `AgentRuntimeStore` directly, its duplicate result must also include
|
|
1037
1142
|
the canonical `run` and the retained `assistant` for a terminal run.
|
|
1038
|
-
|
|
1039
|
-
## Released migration: 0.58.0
|
|
1040
|
-
|
|
1041
|
-
### The default history projection rejects invalid chronology
|
|
1042
|
-
|
|
1043
|
-
`projectAgentHistory` no longer forwards records a provider contract cannot
|
|
1044
|
-
accept. A completed assistant record before the first user message, and an
|
|
1045
|
-
assistant record whose tool calls have no matching results, are omitted with an
|
|
1046
|
-
inspectable decision instead of being sent upstream:
|
|
1047
|
-
|
|
1048
|
-
```ts
|
|
1049
|
-
// before — a leading assistant record was forwarded as-is
|
|
1050
|
-
projectAgentHistory(messages)
|
|
1051
|
-
// after — opt in explicitly, and only where the provider contract permits it
|
|
1052
|
-
projectAgentHistory(messages, { leadingAssistant: 'allow' })
|
|
1053
|
-
```
|
|
1054
|
-
|
|
1055
|
-
Two consequences the changelog does not spell out. First, the projection can now
|
|
1056
|
-
return **fewer** messages than the history holds, so any assertion or metric
|
|
1057
|
-
that compared projected length against stored length will move; read the
|
|
1058
|
-
detailed projection instead — it reports what was omitted and why, which is the
|
|
1059
|
-
supported way to see the difference. Second, the omission is silent to the
|
|
1060
|
-
provider but not to you: if a conversation suddenly loses its leading context,
|
|
1061
|
-
the decision record is where that shows up, not the transcript.
|
|
1062
|
-
|
|
1063
|
-
### Operator events redact `internalCause` by default
|
|
1064
|
-
|
|
1065
|
-
Raw provider and tool failures no longer travel in operator events unless the
|
|
1066
|
-
sink asks for them:
|
|
1067
|
-
|
|
1068
|
-
```ts
|
|
1069
|
-
// before — internalCause was present
|
|
1070
|
-
createAgentObservability({ write })
|
|
1071
|
-
// after — an explicit operator-only opt-in
|
|
1072
|
-
createAgentObservability({ write, includeInternalCause: true })
|
|
1073
|
-
```
|
|
1074
|
-
|
|
1075
|
-
The consequence to check before upgrading: any dashboard, alert or log
|
|
1076
|
-
processor keyed on `internalCause` goes blind the moment you upgrade, and it
|
|
1077
|
-
goes blind quietly — the field is absent, not empty. Set the flag on the
|
|
1078
|
-
operator sink you own. Product delivery stays redacted regardless of the flag;
|
|
1079
|
-
this option cannot widen what reaches a user.
|
|
1080
|
-
|
|
1081
|
-
## Released migration: 0.57.0
|
|
1082
|
-
|
|
1083
|
-
### Duplicate admission results carry the complete identity
|
|
1084
|
-
|
|
1085
|
-
Custom `AgentRuntimeStore` adapters must persist and return the input and
|
|
1086
|
-
assistant identities associated with an idempotency key:
|
|
1087
|
-
|
|
1088
|
-
```ts
|
|
1089
|
-
// before
|
|
1090
|
-
return { outcome: 'duplicate', runId, snapshot }
|
|
1091
|
-
|
|
1092
|
-
// after
|
|
1093
|
-
return { outcome: 'duplicate', input, inputMessageId, runId, assistantMessageId, snapshot }
|
|
1094
|
-
```
|
|
1095
|
-
|
|
1096
|
-
Prefer replacing the custom aggregate reducer with `createAgentRuntimeStore()`;
|
|
1097
|
-
its admission record and transaction driver implement this contract
|
|
1098
|
-
automatically. Historical note: 0.59.0 reshaped this driver again, so an adapter
|
|
1099
|
-
crossing both versions should read that section first and migrate once.
|
|
1100
|
-
|
|
1101
|
-
### `AgentRuntimeEvent` adds a post-commit `admission` variant
|
|
1102
|
-
|
|
1103
|
-
Add it to any exhaustive publisher switch. Its `assistant` is either the pending
|
|
1104
|
-
placeholder for a new assignment or the canonical persisted assistant for a
|
|
1105
|
-
duplicate:
|
|
1106
|
-
|
|
1107
|
-
```ts
|
|
1108
|
-
case 'admission':
|
|
1109
|
-
await persistProductProjection(event.input, event.run, event.assistant)
|
|
1110
|
-
break
|
|
1111
|
-
```
|
|
1112
|
-
|
|
1113
|
-
The consequence for an exhaustive switch written without a `default` branch is a
|
|
1114
|
-
compile error, which is the point. The consequence for a switch that *has* a
|
|
1115
|
-
`default` is worse and silent: post-commit admissions fall into it and are
|
|
1116
|
-
projected as an unknown event. Grep for publisher switches before upgrading.
|