@sema-agent/core 7.32.1 → 7.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -137,6 +137,15 @@ export interface WiringManifestMcpEntry {
137
137
  * both. A leg whose `LSP` tool was excluded can still be pushing diagnostics. */
138
138
  export declare const LSP_LANES: readonly ["tool", "diagnostics", "tool_and_diagnostics"];
139
139
  export type LspLane = (typeof LSP_LANES)[number];
140
+ /** Why a leg's hand band is NOT mounted ({@link WiringManifest.hands}`.reason`), one word per false branch of the
141
+ * hands decision (`prepare-execution-env.ts`); it has one: `no_execution_env` = neither env seat supplied one. A
142
+ * literal, not a registered closed set (the gate admits sets of two or more) until a second branch exists. */
143
+ export type HandsUnmountedReason = "no_execution_env";
144
+ /** The manifest's hands seam from the leg's one hands fact (see {@link WiringManifest.hands}). */
145
+ export declare function deriveHandsSeam(handsMounted: boolean): {
146
+ mounted: boolean;
147
+ reason?: HandsUnmountedReason;
148
+ };
140
149
  /** The contract id the LSP tool mounts under (`createLspTool`) — the roster's structural discriminator, which
141
150
  * a disclosure keys on instead of re-spelling the wire name. */
142
151
  export declare const LSP_CONTRACT_ID = "core.lsp@1";
@@ -347,6 +356,19 @@ export interface WiringManifest {
347
356
  mounted: boolean;
348
357
  lane?: LspLane;
349
358
  };
359
+ /**
360
+ * EFFECTIVE half only, ALWAYS present on an engine-minted effective manifest: is this leg's HAND band (the file
361
+ * tools, and the shell tools where the env has a shell) mounted — the value the band's mount reads, stated
362
+ * positively instead of inferred from `tools[]` or the write-protection word. `true` ⇔ the factory minted an env
363
+ * or the static `executionEnv` seat is not `undefined` (a JS caller's `null` and a `StubExecutionEnv` count: the
364
+ * band mounts over them, without a shell); `false` carries `reason` ({@link HandsUnmountedReason}), which is absent
365
+ * beside `true`. Whether a full shell is reachable is `tools[]`'s fact. ABSENT = an older mint or an external
366
+ * derivation, never a state. IN {@link configFingerprint}: an ASSEMBLY fact (the {@link writeProtection} class).
367
+ */
368
+ hands?: {
369
+ mounted: boolean;
370
+ reason?: HandsUnmountedReason;
371
+ };
350
372
  /**
351
373
  * EFFECTIVE half only, PRESENT IFF this leg compiled a write-protection judge — a deployment that
352
374
  * replaced the table with `[]` opted the layer out entirely, and reports nothing here rather than a
@@ -497,6 +519,11 @@ export interface WiringFacts {
497
519
  mounted: boolean;
498
520
  lane?: LspLane;
499
521
  };
522
+ /** Effective half only — the hands seam ({@link WiringManifest.hands}, {@link deriveHandsSeam}); the engine always passes it. */
523
+ hands?: {
524
+ mounted: boolean;
525
+ reason?: HandsUnmountedReason;
526
+ };
500
527
  /** Effective half only — the compiled write-protection judge's OWN target-view word
501
528
  * ({@link WiringManifest.writeProtection}); absent ⇒ this leg compiled no judge (an emptied table). */
502
529
  writeProtectionTargetView?: WriteProtectionTargetView;
@@ -5,6 +5,9 @@ import { isLiveQuestionFace } from "./ask-question.js";
5
5
  import { AUTO_MODE_DENY_SOURCES, isAutoModeDenySource } from "./runtime-caps.js";
6
6
  export const AUTO_MODE_ARM_REASONS = ["armed", "no_intent", "no_face", "denied", "resolver_fault"];
7
7
  export const LSP_LANES = ["tool", "diagnostics", "tool_and_diagnostics"];
8
+ export function deriveHandsSeam(handsMounted) {
9
+ return handsMounted ? { mounted: true } : { mounted: false, reason: "no_execution_env" };
10
+ }
8
11
  export const LSP_CONTRACT_ID = "core.lsp@1";
9
12
  export function deriveLspSeam(mounted, diagnosticsLive) {
10
13
  const lane = mounted ? (diagnosticsLive ? "tool_and_diagnostics" : "tool") : diagnosticsLive ? "diagnostics" : undefined;
@@ -84,6 +87,13 @@ function readAutoModeFact(fact) {
84
87
  }
85
88
  return { armed, reason, ...(deniedSource !== undefined ? { deniedSource } : {}) };
86
89
  }
90
+ function readHandsFact(fact) {
91
+ const { mounted, reason } = typeof fact === "object" && fact !== null ? fact : { mounted: undefined, reason: undefined };
92
+ if (typeof mounted !== "boolean" || (mounted ? reason !== undefined : reason !== "no_execution_env")) {
93
+ throw new Error(`the hands wiring fact is inconsistent (${JSON.stringify(fact)}) — mounted is a boolean, and reason stands exactly when it is false, as "no_execution_env"`);
94
+ }
95
+ return mounted ? { mounted } : { mounted, reason };
96
+ }
87
97
  function readFileHistoryFact(word) {
88
98
  if (word !== "on" && word !== "disabled")
89
99
  throw new Error(`the file-history wiring fact ${JSON.stringify(word)} is not one of on|disabled`);
@@ -146,6 +156,7 @@ export function deriveWiringManifest(facts) {
146
156
  ...(facts.half === "effective" && facts.tools !== undefined ? { tools: facts.tools } : {}),
147
157
  ...(facts.half === "effective" && facts.hooks !== undefined && facts.hooks.length > 0 ? { hooks: facts.hooks.map((h) => ({ ...h })) } : {}),
148
158
  ...(facts.half === "effective" && facts.lsp !== undefined ? { lsp: { ...facts.lsp } } : {}),
159
+ ...(facts.half === "effective" && facts.hands !== undefined ? { hands: readHandsFact(facts.hands) } : {}),
149
160
  ...(facts.half === "effective" && facts.writeProtectionTargetView !== undefined ? { writeProtection: { targetView: facts.writeProtectionTargetView } } : {}),
150
161
  ...(facts.half === "effective" && facts.readDenyBuiltinTiersActive !== undefined && facts.readDenyBuiltinTiersActive.length > 0
151
162
  ? { readDeny: { builtinTiers: [...facts.readDenyBuiltinTiersActive] } }
@@ -39,12 +39,12 @@ export interface ResilienceOptions {
39
39
  * exists to protect the provider; circumvention only where true failure shape matters (benchmarks). */
40
40
  bypassBreaker?: boolean;
41
41
  }
42
- /** 1.296 件A — per-call advisory stall-watchdog values (see {@link StreamOptions.stallTimeouts}). */
42
+ /** 1.296 件A — per-call advisory stall-watchdog values (see {@link StreamOptions.stallTimeouts}); the
43
+ * connect clock is the transport's, fixed at brain construction, so it is not among them. */
43
44
  export interface StallTimeouts {
44
- /** Advisory connect watchdog (ms) — bounds the fetch→response-headers wait (件1a: a gateway that
45
- * never returns headers is invisible to every post-headers watchdog, including the absolute
46
- * deadline timer). Fires as the brain's existing retryable `network` connect timeout. */
47
- connectMs?: number;
45
+ /** Advisory first-byte watchdog (ms) — bounds the fetch→response-headers wait; fires as a retryable
46
+ * `stall` out of its own small budget. */
47
+ firstByteMs?: number;
48
48
  /** Advisory first-token watchdog (ms) — first thinking delta counts as the first token. */
49
49
  firstTokenMs?: number;
50
50
  /** Advisory mid-stream idle watchdog (ms) — re-armed on every delta after the first. */
@@ -588,8 +588,8 @@ export declare const MAX_WORKFLOW_ITEMS = 4096;
588
588
  * own. This watchdog does not re-send a model call — it aborts the attempt and restarts the whole agent
589
589
  * on a FRESH session, so tool effects from before the stall can be repeated. The engine, one layer down,
590
590
  * already answers a silent provider call the cheap way: it re-sends that ONE call. So the floor here is
591
- * what a single provider call may legitimately occupy while producing NOTHING — the connect wait plus the
592
- * first-token wait ({@link STALL_CONNECT_MS} + {@link STALL_FIRST_TOKEN_MS}). It was a hand-written
591
+ * what a single provider call may legitimately occupy while producing NOTHING — the whole first-byte lane at the default knob
592
+ * ({@link firstByteLaneKnobMs}) plus the first-token wait; a floor (upload allowance and backoff between attempts not counted). It was a hand-written
593
593
  * 180_000 and CC-same until the first-token bound rose to 600s past it (measured: the same silent call
594
594
  * then produced two agent runs on two sessions instead of one run with a re-sent call), which is the
595
595
  * systematic inversion a derived value cannot re-open: raise or lower the engine's bound and this follows.
@@ -599,8 +599,8 @@ export declare const MAX_WORKFLOW_ITEMS = 4096;
599
599
  * this timer starts before the Runner prepares the attempt (an execution-env factory, a hook, a store
600
600
  * read all run inside this window and outside the engine's), and the first-token timer only starts once
601
601
  * headers arrive — so the margin by which the inner deadline can land AFTER this one is preparation plus
602
- * the connect time actually spent, less the {@link STALL_CONNECT_MS} this window already budgets for it
603
- * (40s of preparation and instant headers puts the first inner deadline 10s past this timer). An engine retry does not re-arm this timer either, so two silent provider attempts
602
+ * the header wait actually spent, less the {@link STALL_FIRST_BYTE_MS} this window already budgets for it
603
+ * (instant headers and more than 300s of preparation put the first inner deadline past this timer). An engine retry does not re-arm this timer either, so two silent provider attempts
604
604
  * exceed it by construction. The derivation removes the case that was guaranteed to invert; it does not
605
605
  * make inversion impossible. A deployment that wants the old aggression passes `stallMs` explicitly, and
606
606
  * a deployment that wants a real guarantee must set it from its own preparation budget.
@@ -22,7 +22,7 @@ import { OUTPUT_TOOL_NAME } from "../core/runner/synthetic-tools.js";
22
22
  import { compileOutputSchema } from "../core/runner/strict-output-schema.js";
23
23
  import { WorkflowBudgetExceededError, WorkflowNestingError, WorkflowAgentBlockedError, WorkflowAgentParkedError, WorkflowAgentSchemaError, WorkflowAgentStalledError, WorkflowMaxAgentsError, WORKFLOW_SPAWN_BLOCKED_ERROR_CODE } from "./workflow-types.js";
24
24
  import { WorkflowScriptError } from "./workflow-meta.js";
25
- import { STALL_CONNECT_MS, STALL_FIRST_TOKEN_MS } from "../brain/timeout.js";
25
+ import { firstByteLaneKnobMs, STALL_FIRST_BYTE_MS, STALL_FIRST_TOKEN_MS } from "../brain/timeout.js";
26
26
  import { asThrownError, isErrorLike, readMember, thrownIs, thrownName, thrownText } from "../core/thrown-value.js";
27
27
  export * from "./workflow-types.js";
28
28
  const MAX_TRANSCRIPT_CHARS = 4000;
@@ -310,7 +310,7 @@ function assertFanOutOptionsShape(v, position) {
310
310
  `Nothing was run.`);
311
311
  }
312
312
  }
313
- export const WORKFLOW_AGENT_STALL_MS = STALL_CONNECT_MS + STALL_FIRST_TOKEN_MS;
313
+ export const WORKFLOW_AGENT_STALL_MS = firstByteLaneKnobMs(STALL_FIRST_BYTE_MS) + STALL_FIRST_TOKEN_MS;
314
314
  export const WORKFLOW_THROTTLE_SLOW_MS = 90_000;
315
315
  export const WORKFLOW_AGENT_MAX_RETRIES = 5;
316
316
  export const WORKFLOW_AGENT_THROTTLE_BACKOFF_MS = 45_000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "7.32.1",
3
+ "version": "7.33.1",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -44,7 +44,7 @@
44
44
  "wire-changes/"
45
45
  ],
46
46
  "engines": {
47
- "node": ">=20.3.0"
47
+ "node": ">=20.18.1"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "rm -rf dist && tsc -p tsconfig.build.json && tsc -p tsconfig.build.json --emitDeclarationOnly --removeComments false",
@@ -95,7 +95,8 @@
95
95
  },
96
96
  "dependencies": {
97
97
  "@modelcontextprotocol/sdk": "1.30.0",
98
- "typebox": "1.1.39"
98
+ "typebox": "1.1.39",
99
+ "undici": "7.30.0"
99
100
  },
100
101
  "optionalDependencies": {
101
102
  "sharp": "^0.35.0"
@@ -107,6 +108,8 @@
107
108
  "pg-mem": "^3.0.14",
108
109
  "tsx": "4.19.2",
109
110
  "typescript": "5.7.2",
111
+ "undici6": "npm:undici@^6.29.0",
112
+ "undici8": "npm:undici@^8.11.2",
110
113
  "vitest": "^2.1.8"
111
114
  }
112
115
  }
@@ -0,0 +1,351 @@
1
+ {
2
+ "repo": "core",
3
+ "version": "7.33.0",
4
+ "from": "a36eb21ebe39f5191f30102623e1570c3eb77f41",
5
+ "to": "v7.33.0",
6
+ "added": [],
7
+ "removed": [],
8
+ "changed": [],
9
+ "narrowed": [
10
+ {
11
+ "id": "F4",
12
+ "source": "behaviorFlips.summary",
13
+ "text": "Behaviour change with a NARROWING (#1121; +0 exports): ONE resolution chain for every model-call watchdog (`resolveWatchdogMs`): construction config (an explicit `0` is OFF) > per-call advisory > the engine default constant (`STALL_FIRST_BYTE_MS` 300 000 + the upload allowance / `STALL_FIRST_TOKEN_MS` 600 000 / `STALL_IDLE_MS` 300 000, F5). The old third level \"both absent ⇒ off\" is retired: a first-party brain built with no timeout fields and driven without the Runner's advisory (a bare SDK brain; compaction and side-query calls) now arms all three — a call that never gets headers ends after 301 s + a widened 601 s retry, one with no delta after 600 s, silence after a delta after 300 s (the same 300 s the transport's body clock used to give such a call, now the engine's own watchdog announcing a `stall` instead of a transport failure; an Anthropic `ping` is not a content delta, so with text already streamed the call is committed by tier A, \"Response stalled mid-stream. The response above may be incomplete.\"). `0` on a watchdog now means no bound at all on that segment, the transport's included (F2). NARROWING: such a brain whose fetch keeps no clock of its own (a `fetchImpl` that is not undici's, a test double) waited indefinitely on every segment and is now cut at those defaults; on Node's fetch it was bounded by the transport's 300 s clocks — the header cut reported `transport` and re-sent through the shared ladder up to `maxRetries` times — and is now bounded by the engine's (headers 301 s + one widened 601 s retry; first token 600 s, wider; idle 300 s, the same bound under a new announcer). WHO IS HURT: a deployment that constructed a first-party brain with no timeout fields and relied on it having none. COMPENSATION: `0` on the field it wants off, or a larger value."
14
+ }
15
+ ],
16
+ "closedSetMembers": [],
17
+ "closedSetsUnreadable": [],
18
+ "typeKeys": [
19
+ {
20
+ "type": "AnthropicBrainConfig",
21
+ "at": [
22
+ "src/brain/anthropic.ts:25"
23
+ ],
24
+ "added": [
25
+ "firstByteTimeoutMs"
26
+ ],
27
+ "addedDetail": [
28
+ {
29
+ "key": "firstByteTimeoutMs",
30
+ "optional": true
31
+ }
32
+ ],
33
+ "addedRequired": [],
34
+ "removed": [
35
+ "connectTimeoutMs"
36
+ ],
37
+ "breaking": true,
38
+ "criteriaCovered": true
39
+ },
40
+ {
41
+ "type": "BrainTimeoutConfig",
42
+ "at": [
43
+ "src/brain/timeout.ts:166"
44
+ ],
45
+ "added": [
46
+ "firstByteTimeoutMs"
47
+ ],
48
+ "addedDetail": [
49
+ {
50
+ "key": "firstByteTimeoutMs",
51
+ "optional": true
52
+ }
53
+ ],
54
+ "addedRequired": [],
55
+ "removed": [
56
+ "connectTimeoutMs"
57
+ ],
58
+ "breaking": true,
59
+ "criteriaCovered": true
60
+ },
61
+ {
62
+ "type": "OpenAIBrainConfig",
63
+ "at": [
64
+ "src/brain/openai.ts:27"
65
+ ],
66
+ "added": [
67
+ "firstByteTimeoutMs"
68
+ ],
69
+ "addedDetail": [
70
+ {
71
+ "key": "firstByteTimeoutMs",
72
+ "optional": true
73
+ }
74
+ ],
75
+ "addedRequired": [],
76
+ "removed": [
77
+ "connectTimeoutMs"
78
+ ],
79
+ "breaking": true,
80
+ "criteriaCovered": true
81
+ },
82
+ {
83
+ "type": "OpenResponsesBrainConfig",
84
+ "at": [
85
+ "src/brain/open-responses.ts:101"
86
+ ],
87
+ "added": [
88
+ "firstByteTimeoutMs"
89
+ ],
90
+ "addedDetail": [
91
+ {
92
+ "key": "firstByteTimeoutMs",
93
+ "optional": true
94
+ }
95
+ ],
96
+ "addedRequired": [],
97
+ "removed": [
98
+ "connectTimeoutMs"
99
+ ],
100
+ "breaking": true,
101
+ "criteriaCovered": true
102
+ },
103
+ {
104
+ "type": "StallTimeouts",
105
+ "at": [
106
+ "src/engine/llm/types.ts:70"
107
+ ],
108
+ "via": "CompleteSimpleFn.SimpleStreamOptions.StreamOptions.stallTimeouts",
109
+ "added": [
110
+ "firstByteMs"
111
+ ],
112
+ "addedDetail": [
113
+ {
114
+ "key": "firstByteMs",
115
+ "optional": true
116
+ }
117
+ ],
118
+ "addedRequired": [],
119
+ "removed": [
120
+ "connectMs"
121
+ ],
122
+ "breaking": true,
123
+ "criteriaCovered": false
124
+ },
125
+ {
126
+ "type": "StreamEngineConfig",
127
+ "at": [
128
+ "src/brain/stream-engine.ts:45"
129
+ ],
130
+ "via": "AnthropicBrainConfig.StreamEngineConfig",
131
+ "added": [
132
+ "firstByteTimeoutMs"
133
+ ],
134
+ "addedDetail": [
135
+ {
136
+ "key": "firstByteTimeoutMs",
137
+ "optional": true
138
+ }
139
+ ],
140
+ "addedRequired": [],
141
+ "removed": [
142
+ "connectTimeoutMs"
143
+ ],
144
+ "breaking": true,
145
+ "criteriaCovered": false
146
+ }
147
+ ],
148
+ "typeKeysUnreadableArms": [],
149
+ "typeKeysFlattenedCount": 217,
150
+ "declarationMoves": [],
151
+ "mintMoves": [],
152
+ "criteriaObserves": [
153
+ {
154
+ "kind": "taskEventField",
155
+ "name": "status.attempt",
156
+ "assertions": [
157
+ "G1.b"
158
+ ]
159
+ },
160
+ {
161
+ "kind": "taskEventField",
162
+ "name": "status.detail",
163
+ "assertions": [
164
+ "G1.a"
165
+ ]
166
+ },
167
+ {
168
+ "kind": "taskEventField",
169
+ "name": "status.errClass",
170
+ "assertions": [
171
+ "G1.a"
172
+ ]
173
+ },
174
+ {
175
+ "kind": "taskEventField",
176
+ "name": "status.maxRetries",
177
+ "assertions": [
178
+ "G1.b"
179
+ ]
180
+ },
181
+ {
182
+ "kind": "taskEventField",
183
+ "name": "status.timeoutMs",
184
+ "assertions": [
185
+ "G1.a",
186
+ "G2.a"
187
+ ]
188
+ },
189
+ {
190
+ "kind": "typeMember",
191
+ "name": "AnthropicBrainConfig.firstByteTimeoutMs",
192
+ "assertions": [
193
+ "G3.a"
194
+ ]
195
+ },
196
+ {
197
+ "kind": "typeMember",
198
+ "name": "AssistantMessage.errorMessage",
199
+ "assertions": [
200
+ "G3.a",
201
+ "G4.a"
202
+ ]
203
+ },
204
+ {
205
+ "kind": "typeMember",
206
+ "name": "AssistantMessage.partialFinalized",
207
+ "assertions": [
208
+ "G4.c"
209
+ ]
210
+ },
211
+ {
212
+ "kind": "typeMember",
213
+ "name": "AssistantMessage.stopReason",
214
+ "assertions": [
215
+ "G3.a",
216
+ "G4.a",
217
+ "G4.b"
218
+ ]
219
+ },
220
+ {
221
+ "kind": "typeMember",
222
+ "name": "Brain.stream",
223
+ "assertions": [
224
+ "G3.a",
225
+ "G4.a",
226
+ "G4.c"
227
+ ]
228
+ },
229
+ {
230
+ "kind": "typeMember",
231
+ "name": "BrainTimeoutConfig.firstByteTimeoutMs",
232
+ "assertions": [
233
+ "G1.a",
234
+ "G2.a",
235
+ "G3.a",
236
+ "G4.a",
237
+ "G4.b"
238
+ ]
239
+ },
240
+ {
241
+ "kind": "typeMember",
242
+ "name": "BrainTimeoutConfig.firstTokenTimeoutMs",
243
+ "assertions": [
244
+ "G2.a",
245
+ "G4.a",
246
+ "G4.b"
247
+ ]
248
+ },
249
+ {
250
+ "kind": "typeMember",
251
+ "name": "BrainTimeoutConfig.idleTimeoutMs",
252
+ "assertions": [
253
+ "G4.b",
254
+ "G4.c"
255
+ ]
256
+ },
257
+ {
258
+ "kind": "typeMember",
259
+ "name": "OpenAIBrainConfig.fetchImpl",
260
+ "assertions": [
261
+ "G1.b",
262
+ "G2.a",
263
+ "G2.b"
264
+ ]
265
+ },
266
+ {
267
+ "kind": "typeMember",
268
+ "name": "OpenAIBrainConfig.firstByteTimeoutMs",
269
+ "assertions": [
270
+ "G1.a"
271
+ ]
272
+ },
273
+ {
274
+ "kind": "typeMember",
275
+ "name": "OpenAIBrainConfig.maxRetries",
276
+ "assertions": [
277
+ "G1.b"
278
+ ]
279
+ },
280
+ {
281
+ "kind": "typeMember",
282
+ "name": "OpenResponsesBrainConfig.firstByteTimeoutMs",
283
+ "assertions": [
284
+ "G3.a"
285
+ ]
286
+ },
287
+ {
288
+ "kind": "typeMember",
289
+ "name": "RunWorkflowOptions.stallMs",
290
+ "assertions": [
291
+ "G5.a"
292
+ ]
293
+ },
294
+ {
295
+ "kind": "typeMember",
296
+ "name": "RunWorkflowOptions.timers",
297
+ "assertions": [
298
+ "G5.a"
299
+ ]
300
+ },
301
+ {
302
+ "kind": "typeMember",
303
+ "name": "TaskRegistry.list",
304
+ "assertions": [
305
+ "G3.b"
306
+ ]
307
+ },
308
+ {
309
+ "kind": "typeMember",
310
+ "name": "TaskRegistry.runningShellIds",
311
+ "assertions": [
312
+ "G3.b"
313
+ ]
314
+ },
315
+ {
316
+ "kind": "typeMember",
317
+ "name": "TaskResult.result",
318
+ "assertions": [
319
+ "G2.b",
320
+ "G4.c"
321
+ ]
322
+ },
323
+ {
324
+ "kind": "typeMember",
325
+ "name": "TaskResult.terminal",
326
+ "assertions": [
327
+ "G1.a",
328
+ "G1.b",
329
+ "G2.a",
330
+ "G2.b",
331
+ "G4.c"
332
+ ]
333
+ }
334
+ ],
335
+ "consumersHint": {
336
+ "cli": [],
337
+ "server": [],
338
+ "sdk": [],
339
+ "note": "scanned cli@4e593722 server@907e1c76 sdk@4fe310e — a name listed is MENTIONED there today, which is evidence of consumption, not proof of it. Ledger files (**/docs/core-consumption/**, **/docs/wire-consumption/**, **/*LEDGER*.json) are not evidence of use — server: 4 name(s) matched ONLY in bookkeeping files and were set aside (AnthropicBrainConfig, BrainTimeoutConfig, OpenAIBrainConfig, OpenResponsesBrainConfig)"
340
+ },
341
+ "breakingCount": 6,
342
+ "breakingItems": [
343
+ "type lost keys: AnthropicBrainConfig -connectTimeoutMs",
344
+ "type lost keys: BrainTimeoutConfig -connectTimeoutMs",
345
+ "type lost keys: OpenAIBrainConfig -connectTimeoutMs",
346
+ "type lost keys: OpenResponsesBrainConfig -connectTimeoutMs",
347
+ "type lost keys: StallTimeouts -connectMs",
348
+ "type lost keys: StreamEngineConfig -connectTimeoutMs"
349
+ ],
350
+ "toResolvedAt": "0ce5bb4b0d729b70f84bdd3ed2ea93bbdaedb68a"
351
+ }