stitchkit 0.96.0 → 0.97.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 (35) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/agent-runtime/schedule-contract.d.ts +52 -0
  3. package/dist/agent-runtime/schedule-contract.d.ts.map +1 -0
  4. package/dist/agent-runtime/schedule-dispatch.d.ts +26 -0
  5. package/dist/agent-runtime/schedule-dispatch.d.ts.map +1 -0
  6. package/dist/agent-runtime/schedule-records.d.ts +8 -14
  7. package/dist/agent-runtime/schedule-records.d.ts.map +1 -1
  8. package/dist/agent-runtime/schedule-tools.d.ts +1 -1
  9. package/dist/agent-runtime/schedule-tools.d.ts.map +1 -1
  10. package/dist/agent-runtime/schedules.d.ts +7 -92
  11. package/dist/agent-runtime/schedules.d.ts.map +1 -1
  12. package/dist/agent-runtime/sqlite-schema.d.ts.map +1 -1
  13. package/dist/agent-runtime/store-migrations/v3-to-v4.d.ts +4 -0
  14. package/dist/agent-runtime/store-migrations/v3-to-v4.d.ts.map +1 -0
  15. package/dist/entrypoints/agent-runtime/sqlite/bun.js +1 -1
  16. package/dist/entrypoints/agent-runtime/sqlite/node.js +1 -1
  17. package/dist/entrypoints/agent-runtime.d.ts +2 -1
  18. package/dist/entrypoints/agent-runtime.d.ts.map +1 -1
  19. package/dist/entrypoints/agent-runtime.js +289 -214
  20. package/dist/entrypoints/telegram.js +15 -2
  21. package/dist/{index-p9e0w8hs.js → index-1m2thnq0.js} +25 -1
  22. package/dist/telegram/operator-channel.d.ts +6 -0
  23. package/dist/telegram/operator-channel.d.ts.map +1 -1
  24. package/llms/agent-runtime.2.txt +224 -0
  25. package/llms/agent-runtime.3.txt +1 -1
  26. package/llms/agent-runtime.txt +18 -225
  27. package/llms/stitchkit.txt +3 -3
  28. package/llms/telegram.txt +5 -2
  29. package/llms/{upgrading-0.37.0-0.56.0.txt → upgrading-0.37.0-0.58.0.txt} +82 -3
  30. package/llms/{upgrading-0.57.0-0.69.0.txt → upgrading-0.59.0-0.71.0.txt} +108 -82
  31. package/llms/{upgrading-0.70.0-0.96.0.txt → upgrading-0.72.0-0.97.0.txt} +39 -108
  32. package/llms/upgrading.txt +3 -3
  33. package/llms-full.txt +63 -7
  34. package/llms.txt +5 -5
  35. package/package.json +1 -1
package/llms/telegram.txt CHANGED
@@ -77,7 +77,10 @@ const operators = createTelegramOperatorChannel<'users' | 'payments' | 'errors'>
77
77
  operators.post(`New user ${user.id}`, 'users')
78
78
  ```
79
79
 
80
- What to post stays the bot's. On the way down, drain it within the grace period
80
+ Every message is masked before it leaves: a bot token always — bots post their
81
+ errors here, and an error about a local Bot API file carries the token in its
82
+ path — plus any `sensitivePatterns` the bot adds; `telegramOperatorSender` also
83
+ masks its own exact token. What to post stays the bot's. On the way down, drain it within the grace period
81
84
  and close it: `drain: (context) => operators.drain(context.signal)`,
82
85
  `close: () => operators.close()` in a resource.
83
86
 
@@ -176,7 +179,7 @@ injected grammY bot. → ADR 0143, ADR 0201 — [guide](../guide/telegram.md)
176
179
  | `TelegramBroadcastConfig` / `TelegramBroadcastSend` / `TelegramBroadcastReport` / `TelegramBroadcastRunOutcome` / `TelegramBroadcastOutcome` / `TelegramBroadcastRecipient` | _type_ | `name`, `directory`, `recipients()`, `send({ recipient, attempt })`, `maxAttempts`, `signal`, `onProgress`; counts `delivered` / `unreachable` / `failed` / `uncertain` / `pending` and `finished` / `stopped` / `halted` / `dry-run` |
177
180
  | `telegramBroadcastSender` / `TelegramBroadcastSenderConfig` / `TelegramBroadcastMessage` | function / _type_ | the standard `send`: `sendMessage` with a text or `copyMessage` of a prepared message, through the Bot API |
178
181
  | `createTelegramOperatorChannel` | function | the operator chat, apart from the journal: `post(text, topic?)` returns at once and never throws; paced sends into forum topics, 429 honoured, oldest dropped on overflow, `drain(signal)` and `close()` for the way down |
179
- | `TelegramOperatorChannelConfig` / `TelegramOperatorChannel` / `TelegramOperatorMessage` / `TelegramOperatorDrop` / `TelegramChatId` | _type_ | `chatId`, `topics`, `send`, `maxQueued`, `minIntervalMs`, `maxAttempts`, `onDropped`; a drop is `overflow` / `refused` / `attempts` / `closed` with its classification |
182
+ | `TelegramOperatorChannelConfig` / `TelegramOperatorChannel` / `TelegramOperatorMessage` / `TelegramOperatorDrop` / `TelegramChatId` | _type_ | `chatId`, `topics`, `send`, `maxQueued`, `minIntervalMs`, `maxAttempts`, `onDropped`, `sensitivePatterns` (a bot token is always masked); a drop is `overflow` / `refused` / `attempts` / `closed` with its classification |
180
183
  | `telegramOperatorSender` / `TelegramOperatorSenderConfig` | function / _type_ | the standard `send`: `sendMessage` into the chat and topic, plain text unless `parseMode` |
181
184
  | `createTelegramLocalFiles` | function | files the local Bot API server wrote under `<root>/<token>/`: `resolve(file_path)` and `remove(file_path)` only inside the bot's directory, judged on real paths so a link is judged by where it leads; `check()` for readiness. Recommended root variable: `BOT_API_FILES_ROOT` |
182
185
  | `TelegramLocalFilesConfig` / `TelegramLocalFiles` / `TelegramLocalFilesCheck` / `TelegramLocalFileError` / `TelegramLocalFileRefusal` | _type_ / class | `root`, `token`; refusals `root-not-absolute` / `bot-directory-unavailable` / `outside-bot-directory` / `missing` / `not-a-file`, never carrying the path or the token |
@@ -1,14 +1,93 @@
1
- # Upgrading 0.37.0 → 0.56.0 — stitchkit docs slice
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.56.0 (docs/guide/upgrading.md)
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.56.0 (docs/guide/upgrading.md)
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.57.0 → 0.69.0 — stitchkit docs slice
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.57.0 → 0.69.0 (docs/guide/upgrading.md)
6
+ Contains: Guide: Upgrading, 0.59.0 → 0.71.0 (docs/guide/upgrading.md)
7
7
 
8
8
  ==============================================================================
9
- # Guide: Upgrading, 0.57.0 → 0.69.0 (docs/guide/upgrading.md)
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.
@@ -1,14 +1,50 @@
1
- # Upgrading 0.70.0 → 0.96.0 — stitchkit docs slice
1
+ # Upgrading 0.72.0 → 0.97.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.70.0 → 0.96.0 (docs/guide/upgrading.md)
6
+ Contains: Guide: Upgrading, 0.72.0 → 0.97.0 (docs/guide/upgrading.md)
7
7
 
8
8
  ==============================================================================
9
- # Guide: Upgrading, 0.70.0 → 0.96.0 (docs/guide/upgrading.md)
9
+ # Guide: Upgrading, 0.72.0 → 0.97.0 (docs/guide/upgrading.md)
10
10
  ==============================================================================
11
11
 
12
+ ## Released migration: 0.97.0
13
+
14
+ ### schedule delivery
15
+
16
+ **Who must act:** consumers of `createAgentScheduleService`, exhaustive switches on
17
+ `AgentSchedule.state`, and operators opening existing agent-runtime SQLite stores.
18
+
19
+ - Stop old scheduler processes and back up SQLite before startup. The store migrates
20
+ transactionally to v4; old binaries cannot open it. Rollback uses the backup.
21
+ - Handle `failed` as a terminal schedule state. To refuse future delivery (including
22
+ recurring delivery), return `{ status: 'terminal', reason: 'destination closed' }`.
23
+ The occurrence remains unadvanced; `schedule/failed` carries `terminal: true`.
24
+ - A throw or `{ status: 'retry', reason: 'temporarily unavailable' }` retries the same
25
+ key after 1, 2, 4, 8, 16, 32, then 60 seconds. `nextAt` remains the original due time;
26
+ `retryAt`, `attempts` and `lastError` expose retry diagnostics. Success clears them.
27
+ - Resolve `void` only after durable consumer admission. Honor the supplied `signal`:
28
+ dispatch has a 30-second deadline and a 60-second lease. Durable deduplication by
29
+ `idempotencyKey` is required even after abort or crash; do not implement sleeps in dispatch.
30
+ - Stale/cancelled attempts emit no `schedule/fired` or `schedule/late`. Observe the
31
+ cancellation event instead of relying on a synthetic cancelled firing.
32
+
33
+ ```ts
34
+ // before
35
+ async function dispatch(request) {
36
+ if (await destinationClosed(request.conversationId)) throw new Error('closed')
37
+ await admit(request)
38
+ }
39
+ // after: use this callback as createAgentScheduleService({ dispatch, ... })
40
+ async function dispatch(request) {
41
+ if (await destinationClosed(request.conversationId)) {
42
+ return { status: 'terminal', reason: 'destination closed' }
43
+ }
44
+ await admit({ ...request, signal: request.signal }) // durably deduplicates idempotencyKey
45
+ }
46
+ ```
47
+
12
48
  ## Released migration: 0.96.0
13
49
 
14
50
  ### resource-context-admission
@@ -1040,108 +1076,3 @@ const snapshot: CreditWindowSnapshot = { state: 'open', capacityBytes: 100, /*
1040
1076
  // after
1041
1077
  const snapshot: CreditWindowSnapshot = { state: 'open', capacityBytes: 100, /* … */ waiting: 0 }
1042
1078
  ```
1043
-
1044
- ## Released migration: 0.71.0
1045
-
1046
- The Agent coding tools. Two of the three changes are visible to the compiler;
1047
- the third is the one to read carefully, because nothing will point at it.
1048
-
1049
- ### If you implement `authorize`
1050
-
1051
- The operation union gained `edit`, `list` and `glob` and lost `patch`. An
1052
- exhaustive matcher stops compiling and the compiler shows you every arm. **A
1053
- matcher with a default branch does not**, and that is the dangerous case:
1054
-
1055
- ```ts
1056
- // before — and after this upgrade, silently wrong in both directions
1057
- if (request.operation === 'patch') return reviewPatch(request)
1058
- return true // now also authorizes edit, list and glob
1059
- // …or: return false // now also kills edit_file
1060
- ```
1061
-
1062
- ```ts
1063
- // after
1064
- switch (request.operation) {
1065
- case 'edit': return reviewEdit(request) // the old `patch` payload, unchanged
1066
- case 'list':
1067
- case 'glob': return true // or your own policy
1068
- // …existing read / write / search / shell / artifact-read arms
1069
- }
1070
- ```
1071
-
1072
- `write` gained `createsDirectories` — the workspace-relative directories the
1073
- call would create, outermost first, reported **before** anything is created. A
1074
- host that wants to refuse implicit directory creation now can.
1075
-
1076
- ### If you call `apply_patch`
1077
-
1078
- It is `edit_file`, and it is one call:
1079
-
1080
- ```ts
1081
- // before
1082
- const read = await readFile({ path })
1083
- await applyPatch({ path, baseSha256: read.sha256, oldText, newText, dryRun: true })
1084
- await applyPatch({ path, baseSha256: read.sha256, oldText, newText, dryRun: false })
1085
-
1086
- // after
1087
- await editFile({ path, oldText, newText })
1088
- ```
1089
-
1090
- `expectedSha256` is optional and still refuses a stale base with `CONFLICT` when
1091
- you pass it; `edit_file` returns the resulting `sha256`, so a chain of edits
1092
- never needs to re-read. If you key an approval policy or a UI label on the string
1093
- `apply_patch`, update the key — nothing will fail loudly.
1094
-
1095
- ### If you match on `INTERNAL_SERVER_ERROR` from a coding tool
1096
-
1097
- Ordinary outcomes no longer arrive that way. A missing file is `NOT_FOUND`, an
1098
- existing file without `overwrite` is `CONFLICT`, an ambiguous snippet is
1099
- `CONFLICT` with the occurrence count, a path outside the root is `FORBIDDEN`.
1100
- Code that treated any coding-tool failure as an internal fault will now see
1101
- codes it did not before; code that showed the model an empty error now has a
1102
- sentence and a `hint` to show it. Host-level causes are unchanged and still
1103
- scrubbed.
1104
-
1105
- ### If you write files into new directories
1106
-
1107
- Nothing to change: `write_file` creates missing parents inside the root. Read
1108
- `createdDirectories` in the result if you want to notice a typo — a path that
1109
- was a failure before is now a successful write into a new tree.
1110
-
1111
- ### If you want a step to know its context budget
1112
-
1113
- Opt in by reading it; nothing is injected for you:
1114
-
1115
- ```ts
1116
- loop: {
1117
- prepareStep: ({ contextUsage }) => {
1118
- const used = contextUsage?.usedTokens
1119
- if (used?.provenance === 'unavailable') return {} // no step has landed yet
1120
- const fraction = (used?.value ?? 0) / (contextUsage?.contextWindow ?? 1)
1121
- // …render it wherever you put it
1122
- return {}
1123
- },
1124
- }
1125
- ```
1126
-
1127
- Put it at the **tail** of the conversation rather than in the system
1128
- instructions unless you have a reason: changing the system prompt on every step
1129
- invalidates the provider's prefix cache for the whole conversation, and on a long
1130
- run that is a multiple of the input cost.
1131
-
1132
- ## Released migration: 0.70.0
1133
-
1134
- ### Descriptor-backed Agent filesystem containment
1135
-
1136
- Built-in coding file/search tools and `createAgentHarnessFileResources` now require a runtime
1137
- that can address an opened directory descriptor: Linux `/proc/self/fd`, or the packaged macOS
1138
- Node-API backend added in 0.70.1. This is what keeps a mutable parent rename or outside-symlink replacement from changing
1139
- the authorized target between validation and the actual effect.
1140
-
1141
- No call-site change is needed on those platforms. The 0.70.0 notes incorrectly inferred macOS and
1142
- FreeBSD support from `/dev/fd`; macOS is restored by 0.70.1 and FreeBSD remains unsupported. On
1143
- Windows or another platform without that
1144
- boundary, move the built-in filesystem operations to a supported worker or replace them with
1145
- application-owned tools backed by an equivalent native handle API. They fail closed rather than
1146
- falling back to path spelling. `run_command` remains separately available under its explicit
1147
- executable alias and authorization policy; this change does not claim an executable sandbox.
@@ -11,9 +11,9 @@ Contains: Guide: Upgrading, general (docs/guide/upgrading.md)
11
11
 
12
12
  Load this slice, then every range slice between your installed version and the target:
13
13
 
14
- - 0.70.0 → 0.96.0: `llms/upgrading-0.70.0-0.96.0.txt`
15
- - 0.57.0 → 0.69.0: `llms/upgrading-0.57.0-0.69.0.txt`
16
- - 0.37.0 → 0.56.0: `llms/upgrading-0.37.0-0.56.0.txt`
14
+ - 0.72.0 → 0.97.0: `llms/upgrading-0.72.0-0.97.0.txt`
15
+ - 0.59.0 → 0.71.0: `llms/upgrading-0.59.0-0.71.0.txt`
16
+ - 0.37.0 → 0.58.0: `llms/upgrading-0.37.0-0.58.0.txt`
17
17
 
18
18
  # Upgrading stitchkit
19
19
 
package/llms-full.txt CHANGED
@@ -68,11 +68,11 @@ own, recorded as an ADR.
68
68
  | `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
69
69
  | `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
70
70
  | `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
71
- | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 41 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
71
+ | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 42 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
72
72
  | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the managed-resource conformance kit |
73
73
  | `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
74
74
  | `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
75
- | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 21 of the 41 minors since 0.56.2, most recently 0.94.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
75
+ | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 22 of the 42 minors since 0.56.2, most recently 0.97.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
76
76
  | `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
77
77
  | `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 |
78
78
  | `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
@@ -82,7 +82,7 @@ own, recorded as an ADR.
82
82
  | `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
83
83
  | `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
84
84
  | `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
85
- | `stitchkit/application` | browser + server | evolving<br>_redefined in 9 of the 41 minors since 0.56.2, most recently 0.96.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
85
+ | `stitchkit/application` | browser + server | evolving<br>_redefined in 9 of the 42 minors since 0.56.2, most recently 0.96.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
86
86
  | `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
87
87
  | `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
88
88
  | `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
@@ -4846,7 +4846,7 @@ description: Configure Stitchkit's optional durable history, stream loop, run co
4846
4846
  type: architecture
4847
4847
  status: active
4848
4848
  created: 2026-08-22
4849
- updated: 2026-08-29
4849
+ updated: 2026-09-26 16:00 +07:00
4850
4850
  ---
4851
4851
 
4852
4852
  # Agent application runtime
@@ -5463,6 +5463,23 @@ has invoked a tool is never automatically replayed.
5463
5463
  `createAgentScheduleService` persists `at`, `after` and `every` input. Repeating
5464
5464
  schedules require an explicit IANA time zone. Dispatch uses a stable
5465
5465
  `schedule:<id>:<occurrence>` idempotency key, and restart lateness is recorded.
5466
+ Resolve `void` after durable admission; return `{ status: 'retry', reason }` for a
5467
+ transient refusal or `{ status: 'terminal', reason }` to stop the entire schedule in
5468
+ `failed` state. Throws retry too. Backoff is 1, 2, 4, 8, 16, 32, then 60 seconds,
5469
+ capped at 60 seconds and persisted across restart. `retryAt`, `attempts` and `lastError`
5470
+ show retry diagnostics; success resets them. `nextAt` and `lateByMs` retain the original
5471
+ occurrence time. Recurring schedules fire once after an idle stretch and advance to the
5472
+ next future slot on their original cadence.
5473
+
5474
+ Dispatch receives an `AbortSignal`, has a 30-second deadline and holds a unique
5475
+ 60-second lease. A late or cancelled attempt cannot settle or emit firing events.
5476
+ Honor the signal and durably deduplicate the key: a crash after admission but before
5477
+ settlement can replay delivery. This is at-least-once, not exactly-once.
5478
+ Due selection is indexed, limited to 32 parallel deliveries per tick; one hung batch
5479
+ can delay newly due work by at most its deadline. The service keeps one nearest-due
5480
+ timer plus per-attempt deadline timers. Storage failures use a bounded recovery timer;
5481
+ idle ticks do not write. SQLite v4 migration preserves due times and claims; stop older
5482
+ scheduler processes before migration. See [ADR 0202](../decisions/0202-schedule-retries-preserve-occurrence-identity.md).
5466
5483
  `createAgentScheduleTools` exposes the exact `schedule_after`, `schedule_at`,
5467
5484
  `schedule_every`, `schedule_list` and `schedule_cancel` surface.
5468
5485
 
@@ -12829,7 +12846,10 @@ const operators = createTelegramOperatorChannel<'users' | 'payments' | 'errors'>
12829
12846
  operators.post(`New user ${user.id}`, 'users')
12830
12847
  ```
12831
12848
 
12832
- What to post stays the bot's. On the way down, drain it within the grace period
12849
+ Every message is masked before it leaves: a bot token always — bots post their
12850
+ errors here, and an error about a local Bot API file carries the token in its
12851
+ path — plus any `sensitivePatterns` the bot adds; `telegramOperatorSender` also
12852
+ masks its own exact token. What to post stays the bot's. On the way down, drain it within the grace period
12833
12853
  and close it: `drain: (context) => operators.drain(context.signal)`,
12834
12854
  `close: () => operators.close()` in a resource.
12835
12855
 
@@ -12999,6 +13019,42 @@ analytics policy belong to the application.
12999
13019
 
13000
13020
  # Upgrading stitchkit
13001
13021
 
13022
+ ## Released migration: 0.97.0
13023
+
13024
+ ### schedule delivery
13025
+
13026
+ **Who must act:** consumers of `createAgentScheduleService`, exhaustive switches on
13027
+ `AgentSchedule.state`, and operators opening existing agent-runtime SQLite stores.
13028
+
13029
+ - Stop old scheduler processes and back up SQLite before startup. The store migrates
13030
+ transactionally to v4; old binaries cannot open it. Rollback uses the backup.
13031
+ - Handle `failed` as a terminal schedule state. To refuse future delivery (including
13032
+ recurring delivery), return `{ status: 'terminal', reason: 'destination closed' }`.
13033
+ The occurrence remains unadvanced; `schedule/failed` carries `terminal: true`.
13034
+ - A throw or `{ status: 'retry', reason: 'temporarily unavailable' }` retries the same
13035
+ key after 1, 2, 4, 8, 16, 32, then 60 seconds. `nextAt` remains the original due time;
13036
+ `retryAt`, `attempts` and `lastError` expose retry diagnostics. Success clears them.
13037
+ - Resolve `void` only after durable consumer admission. Honor the supplied `signal`:
13038
+ dispatch has a 30-second deadline and a 60-second lease. Durable deduplication by
13039
+ `idempotencyKey` is required even after abort or crash; do not implement sleeps in dispatch.
13040
+ - Stale/cancelled attempts emit no `schedule/fired` or `schedule/late`. Observe the
13041
+ cancellation event instead of relying on a synthetic cancelled firing.
13042
+
13043
+ ```ts
13044
+ // before
13045
+ async function dispatch(request) {
13046
+ if (await destinationClosed(request.conversationId)) throw new Error('closed')
13047
+ await admit(request)
13048
+ }
13049
+ // after: use this callback as createAgentScheduleService({ dispatch, ... })
13050
+ async function dispatch(request) {
13051
+ if (await destinationClosed(request.conversationId)) {
13052
+ return { status: 'terminal', reason: 'destination closed' }
13053
+ }
13054
+ await admit({ ...request, signal: request.signal }) // durably deduplicates idempotencyKey
13055
+ }
13056
+ ```
13057
+
13002
13058
  ## Released migration: 0.96.0
13003
13059
 
13004
13060
  ### resource-context-admission
@@ -17682,7 +17738,7 @@ Server-only optional application runtime. See the
17682
17738
  | `AgentSandboxProcess` / `AgentSandboxOutputStream` | _types_ | structural child/output interface for optional lifecycle-owned `AgentProcessSandbox.spawn`, without Node ambient type dependencies |
17683
17739
  | `AgentEventSearchResultSchema` / `AgentEventSearchResult` / `createSqliteAgentEventSearch` / `createAgentEventSearchTools` | schema / _type_ / functions | authorized FTS5 search with exact event addresses and `session_*` tools |
17684
17740
  | `createSqliteAgentSpillStore` | function | durable content-address-checked artifact storage, bounded read/search, retention facts and archive participation |
17685
- | `AgentScheduleSchema` / `AgentSchedule` / `AgentScheduleService` / `createAgentScheduleService` / `createAgentScheduleTools` | schema / _types_ / functions | durable `at`/`after`/timezone-explicit `every`, stable dispatch identity and Agent tools |
17741
+ | `AgentScheduleSchema` / `AgentSchedule` / `AgentScheduleService` / `AgentScheduleDeliveryOutcomeSchema` / `AgentScheduleDeliveryOutcome` / `createAgentScheduleService` / `createAgentScheduleTools` | schemas / _types_ / functions | durable `at`/`after`/timezone-explicit `every`, indexed retry eligibility, typed retry/terminal failure, bounded dispatch and fenced stable occurrence identity (ADR 0202) |
17686
17742
  | `AgentChildBudgetSchema` / `AgentChildBudget` / `AgentChildStateSchema` / `AgentChildState` / `AgentChildRecordSchema` / `AgentChildRecord` | schema / _type_ | durable child graph, bounded seed and measured budget state |
17687
17743
  | `AgentChildBlockingKindSchema` / `AgentChildBlockingKind` / `AgentChildBlockingSourceSchema` / `AgentChildBlockingSource` / `AgentChildBlockingEventSchema` / `AgentChildBlockingEvent` / `AgentChildBlockingDecision` | schemas / _types_ | parent-owned request identities; responses discriminate approval decisions from JSON input values |
17688
17744
  | `AgentChildHandle` / `AgentChildManager` / `SqliteAgentChildManagerConfig` / `createSqliteAgentChildManager` / `createAgentChildTools` / `agentChildBudgetStopPolicy` | _type_ / functions | host execution port, child lifecycle, cascade (given to `createAgentRuntime` as `children`), messaging, Agent tools, and the child runtime's own budget stop policy — `recordStepUsage` at every step boundary, `policy_stop` as `child-budget` when spent |
@@ -18540,7 +18596,7 @@ injected grammY bot. → ADR 0143, ADR 0201 — [guide](../guide/telegram.md)
18540
18596
  | `TelegramBroadcastConfig` / `TelegramBroadcastSend` / `TelegramBroadcastReport` / `TelegramBroadcastRunOutcome` / `TelegramBroadcastOutcome` / `TelegramBroadcastRecipient` | _type_ | `name`, `directory`, `recipients()`, `send({ recipient, attempt })`, `maxAttempts`, `signal`, `onProgress`; counts `delivered` / `unreachable` / `failed` / `uncertain` / `pending` and `finished` / `stopped` / `halted` / `dry-run` |
18541
18597
  | `telegramBroadcastSender` / `TelegramBroadcastSenderConfig` / `TelegramBroadcastMessage` | function / _type_ | the standard `send`: `sendMessage` with a text or `copyMessage` of a prepared message, through the Bot API |
18542
18598
  | `createTelegramOperatorChannel` | function | the operator chat, apart from the journal: `post(text, topic?)` returns at once and never throws; paced sends into forum topics, 429 honoured, oldest dropped on overflow, `drain(signal)` and `close()` for the way down |
18543
- | `TelegramOperatorChannelConfig` / `TelegramOperatorChannel` / `TelegramOperatorMessage` / `TelegramOperatorDrop` / `TelegramChatId` | _type_ | `chatId`, `topics`, `send`, `maxQueued`, `minIntervalMs`, `maxAttempts`, `onDropped`; a drop is `overflow` / `refused` / `attempts` / `closed` with its classification |
18599
+ | `TelegramOperatorChannelConfig` / `TelegramOperatorChannel` / `TelegramOperatorMessage` / `TelegramOperatorDrop` / `TelegramChatId` | _type_ | `chatId`, `topics`, `send`, `maxQueued`, `minIntervalMs`, `maxAttempts`, `onDropped`, `sensitivePatterns` (a bot token is always masked); a drop is `overflow` / `refused` / `attempts` / `closed` with its classification |
18544
18600
  | `telegramOperatorSender` / `TelegramOperatorSenderConfig` | function / _type_ | the standard `send`: `sendMessage` into the chat and topic, plain text unless `parseMode` |
18545
18601
  | `createTelegramLocalFiles` | function | files the local Bot API server wrote under `<root>/<token>/`: `resolve(file_path)` and `remove(file_path)` only inside the bot's directory, judged on real paths so a link is judged by where it leads; `check()` for readiness. Recommended root variable: `BOT_API_FILES_ROOT` |
18546
18602
  | `TelegramLocalFilesConfig` / `TelegramLocalFiles` / `TelegramLocalFilesCheck` / `TelegramLocalFileError` / `TelegramLocalFileRefusal` | _type_ / class | `root`, `token`; refusals `root-not-absolute` / `bot-directory-unavailable` / `outside-bot-directory` / `missing` / `not-a-file`, never carrying the path or the token |