@genesislcap/ai-assistant 15.22.0 → 15.22.1-GENC-1582.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.
@@ -2780,6 +2780,39 @@
2780
2780
  ],
2781
2781
  "name": "getCompactionPlan"
2782
2782
  },
2783
+ {
2784
+ "kind": "MethodSignature",
2785
+ "canonicalReference": "@genesislcap/ai-assistant!AiDriver#getContextReserve:member(1)",
2786
+ "docComment": "/**\n * Headroom the next turn could need, and which agent set that requirement (GENC-1582). `undefined` when no agent the turn could land on declares a `contextReserve`, which leaves the ambient default in force.\n *\n * The host cannot work this out for itself: it depends on the user pin, the flow-owner lock and the classifier's candidate set, none of which it can see. Reading the ACTIVE agent instead — which is what the gate did first — asks the wrong question, because the agent that last ran is not the agent about to run. Under automatic routing they are routinely different, and the turn that hands over is exactly the one that needs the room.\n *\n * Optional — only drivers that route implement it.\n */\n",
2787
+ "excerptTokens": [
2788
+ {
2789
+ "kind": "Content",
2790
+ "text": "getContextReserve?(): "
2791
+ },
2792
+ {
2793
+ "kind": "Reference",
2794
+ "text": "ContextReserveResolution",
2795
+ "canonicalReference": "@genesislcap/ai-assistant!~ContextReserveResolution:interface"
2796
+ },
2797
+ {
2798
+ "kind": "Content",
2799
+ "text": " | undefined"
2800
+ },
2801
+ {
2802
+ "kind": "Content",
2803
+ "text": ";"
2804
+ }
2805
+ ],
2806
+ "isOptional": true,
2807
+ "returnTypeTokenRange": {
2808
+ "startIndex": 1,
2809
+ "endIndex": 3
2810
+ },
2811
+ "releaseTag": "Beta",
2812
+ "overloadIndex": 1,
2813
+ "parameters": [],
2814
+ "name": "getContextReserve"
2815
+ },
2783
2816
  {
2784
2817
  "kind": "MethodSignature",
2785
2818
  "canonicalReference": "@genesislcap/ai-assistant!AiDriver#getExternalDiagnostics:member(1)",
@@ -6555,10 +6588,82 @@
6555
6588
  "endIndex": 7
6556
6589
  }
6557
6590
  },
6591
+ {
6592
+ "kind": "Interface",
6593
+ "canonicalReference": "@genesislcap/ai-assistant!ContextReserve:interface",
6594
+ "docComment": "/**\n * Context headroom an agent reserves for itself (GENC-1582).\n *\n * Two figures, because a turn has two costs and only one of them is permanent.\n *\n * @beta\n */\n",
6595
+ "excerptTokens": [
6596
+ {
6597
+ "kind": "Content",
6598
+ "text": "export interface ContextReserve "
6599
+ }
6600
+ ],
6601
+ "fileUrlPath": "src/config/config.ts",
6602
+ "releaseTag": "Beta",
6603
+ "name": "ContextReserve",
6604
+ "preserveMemberOrder": false,
6605
+ "members": [
6606
+ {
6607
+ "kind": "PropertySignature",
6608
+ "canonicalReference": "@genesislcap/ai-assistant!ContextReserve#tokens:member",
6609
+ "docComment": "/**\n * Tokens that must be free before one of this agent's turns may START — the agent's PEAK growth, not its lasting cost.\n *\n * The peak is the binding figure even for an agent that sheds most of it again: `condenseWhen` stubs a spent payload at the end of the turn, so the room has to be there while the turn is running, before any of that fires.\n */\n",
6610
+ "excerptTokens": [
6611
+ {
6612
+ "kind": "Content",
6613
+ "text": "tokens: "
6614
+ },
6615
+ {
6616
+ "kind": "Content",
6617
+ "text": "number"
6618
+ },
6619
+ {
6620
+ "kind": "Content",
6621
+ "text": ";"
6622
+ }
6623
+ ],
6624
+ "isReadonly": false,
6625
+ "isOptional": false,
6626
+ "releaseTag": "Beta",
6627
+ "name": "tokens",
6628
+ "propertyTypeTokenRange": {
6629
+ "startIndex": 1,
6630
+ "endIndex": 2
6631
+ }
6632
+ },
6633
+ {
6634
+ "kind": "PropertySignature",
6635
+ "canonicalReference": "@genesislcap/ai-assistant!ContextReserve#warnAheadTokens:member",
6636
+ "docComment": "/**\n * Extra tokens ABOVE {@link ContextReserve.tokens} at which the warning appears. Omit to fall back to `chatConfig.context.warnMultiplier`.\n *\n * The figure that belongs here is what one of this agent's turns costs PERMANENTLY — its growth measured after condensation, not at its peak. That makes the warning land exactly two turns before the block: at the warning line there is room for a full turn plus the residue it leaves, which is one turn, and then room for one more.\n *\n * It exists because the multiplier cannot express that. `warnMultiplier` scales the whole reserve, so an agent that peaks at 400k and keeps 30k gets a warning 800k below the window — a line every one of its turns crosses transiently and then clears, which is noise rather than a signal.\n */\n",
6637
+ "excerptTokens": [
6638
+ {
6639
+ "kind": "Content",
6640
+ "text": "warnAheadTokens?: "
6641
+ },
6642
+ {
6643
+ "kind": "Content",
6644
+ "text": "number"
6645
+ },
6646
+ {
6647
+ "kind": "Content",
6648
+ "text": ";"
6649
+ }
6650
+ ],
6651
+ "isReadonly": false,
6652
+ "isOptional": true,
6653
+ "releaseTag": "Beta",
6654
+ "name": "warnAheadTokens",
6655
+ "propertyTypeTokenRange": {
6656
+ "startIndex": 1,
6657
+ "endIndex": 2
6658
+ }
6659
+ }
6660
+ ],
6661
+ "extendsTokenRanges": []
6662
+ },
6558
6663
  {
6559
6664
  "kind": "TypeAlias",
6560
6665
  "canonicalReference": "@genesislcap/ai-assistant!ContextReserveInput:type",
6561
- "docComment": "/**\n * Context headroom an agent reserves for itself, in tokens.\n *\n * A plain number, unlike the value-or-resolver shape `provider` / `temperature` / `resumable` use. The resolver form was drafted for it and removed before release: the gate is recomputed from a synchronous getter with no turn context to resolve against, so no path could ever have invoked one. Accepting a type-correct option that is guaranteed to do nothing is worse than not offering it — a warning after the fact still leaves the configuration inert.\n *\n * It can widen to a resolver later without breaking a caller, since every existing `number` stays valid.\n *\n * @beta\n */\n",
6666
+ "docComment": "/**\n * Context headroom an agent reserves for itself: a token count, or a {@link ContextReserve} when the agent also wants to say where the warning goes.\n *\n * NOT the value-or-resolver shape `provider` / `temperature` / `resumable` use. The resolver form was drafted and removed before release: the gate is recomputed from a synchronous getter with no turn context to resolve against, so no path could ever have invoked one. Accepting a type-correct option guaranteed to do nothing is worse than not offering it.\n *\n * The object arm was added the way the plain form's docblock anticipated — additively, so every existing `number` stays valid and means what it did.\n *\n * @beta\n */\n",
6562
6667
  "excerptTokens": [
6563
6668
  {
6564
6669
  "kind": "Content",
@@ -6566,7 +6671,12 @@
6566
6671
  },
6567
6672
  {
6568
6673
  "kind": "Content",
6569
- "text": "number"
6674
+ "text": "number | "
6675
+ },
6676
+ {
6677
+ "kind": "Reference",
6678
+ "text": "ContextReserve",
6679
+ "canonicalReference": "@genesislcap/ai-assistant!ContextReserve:interface"
6570
6680
  },
6571
6681
  {
6572
6682
  "kind": "Content",
@@ -6578,7 +6688,7 @@
6578
6688
  "name": "ContextReserveInput",
6579
6689
  "typeTokenRange": {
6580
6690
  "startIndex": 1,
6581
- "endIndex": 2
6691
+ "endIndex": 3
6582
6692
  }
6583
6693
  },
6584
6694
  {
@@ -8855,7 +8965,7 @@
8855
8965
  {
8856
8966
  "kind": "Property",
8857
8967
  "canonicalReference": "@genesislcap/ai-assistant!FoundationAiAssistant#contextBudget:member",
8858
- "docComment": "/**\n * Context-headroom thresholds for the active model (GENC-1567) — the warning and block lines, plus the tail budget compaction aims at.\n *\n * The agent's own `contextReserve` is deliberately NOT folded in here. This is the AMBIENT budget, which protects any turn from any agent; a per-agent reserve is an additional, larger requirement checked when that agent takes ownership of a flow, where there is a turn context to resolve its function form against.\n *\n * Overhead is read off `messages` (the Redux mirror) rather than the driver's own history, which the compaction plan uses. That is safe rather than sloppy: `estimateSystemOverhead` only looks at messages BEFORE the first usage-bearing one, and the two lists agree completely that early — the drift between mirror and driver history is only ever at the tail.\n */\n",
8968
+ "docComment": "/**\n * Context-headroom thresholds for the active model (GENC-1567) — the warning and block lines, plus the tail budget compaction aims at.\n *\n * The per-agent `contextReserve` IS folded in, via the driver (GENC-1582). It used to be described here as a separate, later check made when an agent took ownership of a flow — but there is no such check, and by flow entry the send has already been allowed. Resolving it here, before the send, is what makes it a gate rather than a label.\n *\n * Overhead is read off `messages` (the Redux mirror) rather than the driver's own history, which the compaction plan uses. That is safe rather than sloppy: `estimateSystemOverhead` only looks at messages BEFORE the first usage-bearing one, and the two lists agree completely that early — the drift between mirror and driver history is only ever at the tail.\n */\n",
8859
8969
  "excerptTokens": [
8860
8970
  {
8861
8971
  "kind": "Content",
@@ -15622,6 +15732,42 @@
15622
15732
  "isAbstract": false,
15623
15733
  "name": "getCompactionPlan"
15624
15734
  },
15735
+ {
15736
+ "kind": "Method",
15737
+ "canonicalReference": "@genesislcap/ai-assistant!OrchestratingDriver#getContextReserve:member(1)",
15738
+ "docComment": "/**\n * Headroom the next turn could need, and which agent set that requirement (GENC-1582). `undefined` when no agent the turn could land on declares a `contextReserve`, which leaves the ambient default in force.\n *\n * The host cannot work this out for itself: it depends on the user pin, the flow-owner lock and the classifier's candidate set, none of which it can see. Reading the ACTIVE agent instead — which is what the gate did first — asks the wrong question, because the agent that last ran is not the agent about to run. Under automatic routing they are routinely different, and the turn that hands over is exactly the one that needs the room.\n *\n * Optional — only drivers that route implement it.\n */\n",
15739
+ "excerptTokens": [
15740
+ {
15741
+ "kind": "Content",
15742
+ "text": "getContextReserve(): "
15743
+ },
15744
+ {
15745
+ "kind": "Reference",
15746
+ "text": "ContextReserveResolution",
15747
+ "canonicalReference": "@genesislcap/ai-assistant!~ContextReserveResolution:interface"
15748
+ },
15749
+ {
15750
+ "kind": "Content",
15751
+ "text": " | undefined"
15752
+ },
15753
+ {
15754
+ "kind": "Content",
15755
+ "text": ";"
15756
+ }
15757
+ ],
15758
+ "isStatic": false,
15759
+ "returnTypeTokenRange": {
15760
+ "startIndex": 1,
15761
+ "endIndex": 3
15762
+ },
15763
+ "releaseTag": "Beta",
15764
+ "isProtected": false,
15765
+ "overloadIndex": 1,
15766
+ "parameters": [],
15767
+ "isOptional": false,
15768
+ "isAbstract": false,
15769
+ "name": "getContextReserve"
15770
+ },
15625
15771
  {
15626
15772
  "kind": "Method",
15627
15773
  "canonicalReference": "@genesislcap/ai-assistant!OrchestratingDriver#getExternalDiagnostics:member(1)",
@@ -18631,6 +18777,34 @@
18631
18777
  "endIndex": 2
18632
18778
  }
18633
18779
  },
18780
+ {
18781
+ "kind": "PropertySignature",
18782
+ "canonicalReference": "@genesislcap/ai-assistant!StatefulAgentInit#contextReserve:member",
18783
+ "docComment": "/**\n * Tokens of context headroom to keep free for one of this agent's turns — see `AgentConfig.contextReserve`, which this is passed straight through to.\n *\n * The third member of the flow-safety group with {@link StatefulAgentInit.resumable} and {@link StatefulAgentInit.notResumableMessage}, and the one that was missing (GENC-1582): a stateful flow is the case the reserve was designed for — it is what runs the long tool loop — and yet the helper for building one could not express it. Both agents that needed it in the field are built here.\n *\n * Set it from measurement. Per-turn `inputTokens` is recorded for every call, so one real run of the flow gives you its peak growth; leaving it unset inherits the global default, which is the right starting position.\n */\n",
18784
+ "excerptTokens": [
18785
+ {
18786
+ "kind": "Content",
18787
+ "text": "contextReserve?: "
18788
+ },
18789
+ {
18790
+ "kind": "Reference",
18791
+ "text": "ContextReserveInput",
18792
+ "canonicalReference": "@genesislcap/ai-assistant!ContextReserveInput:type"
18793
+ },
18794
+ {
18795
+ "kind": "Content",
18796
+ "text": ";"
18797
+ }
18798
+ ],
18799
+ "isReadonly": false,
18800
+ "isOptional": true,
18801
+ "releaseTag": "Beta",
18802
+ "name": "contextReserve",
18803
+ "propertyTypeTokenRange": {
18804
+ "startIndex": 1,
18805
+ "endIndex": 2
18806
+ }
18807
+ },
18634
18808
  {
18635
18809
  "kind": "PropertySignature",
18636
18810
  "canonicalReference": "@genesislcap/ai-assistant!StatefulAgentInit#description:member",
@@ -645,6 +645,21 @@ export declare interface AiDriver extends EventTarget {
645
645
  * retain the active config implement it.
646
646
  */
647
647
  getActiveAgent?(): AgentConfig | undefined;
648
+ /**
649
+ * Headroom the next turn could need, and which agent set that requirement
650
+ * (GENC-1582). `undefined` when no agent the turn could land on declares a
651
+ * `contextReserve`, which leaves the ambient default in force.
652
+ *
653
+ * The host cannot work this out for itself: it depends on the user pin, the
654
+ * flow-owner lock and the classifier's candidate set, none of which it can see.
655
+ * Reading the ACTIVE agent instead — which is what the gate did first — asks the
656
+ * wrong question, because the agent that last ran is not the agent about to run.
657
+ * Under automatic routing they are routinely different, and the turn that hands
658
+ * over is exactly the one that needs the room.
659
+ *
660
+ * Optional — only drivers that route implement it.
661
+ */
662
+ getContextReserve?(): ContextReserveResolution | undefined;
648
663
  /**
649
664
  * Set the mid-loop context guard (GENC-1567). `undefined` disables it.
650
665
  *
@@ -1175,13 +1190,15 @@ declare interface BaseAgentConfig {
1175
1190
  * room it needs so that it does not have to be. An agent whose flow runs a long
1176
1191
  * tool loop, or returns large payloads, needs more than the global default.
1177
1192
  *
1178
- * **When it takes effect.** The reserve is read from the agent that is already
1179
- * ACTIVE, so it governs the gate from that agent's next send onwards — one turn
1180
- * later than a true pre-flight. It is deliberately not enforced at flow entry:
1181
- * `flow-owner-changed` fires while the driver is already routing the turn, so
1182
- * gating there would mean aborting a turn in flight. An agent that activates
1183
- * mid-turn therefore gets its first turn under the global reserve; the mid-loop
1184
- * context guard is what stops that turn running away.
1193
+ * **When it takes effect.** Before the send, as a real pre-flight (GENC-1582).
1194
+ * The driver resolves it against how routing is currently constrained — the
1195
+ * pinned or flow-owning agent when there is one, otherwise the largest reserve
1196
+ * among the agents the classifier could route to — so a turn is refused before
1197
+ * it can start rather than after. This used to read the agent that was already
1198
+ * ACTIVE, which is a different agent from the one about to run whenever the
1199
+ * orchestrator is doing its job, and the handover turn is exactly the one that
1200
+ * needs the room. The mid-loop context guard remains the backstop for a turn
1201
+ * that outgrows what it reserved.
1185
1202
  *
1186
1203
  * **Set this from measurement, not from a guess.** Per-turn `inputTokens` is
1187
1204
  * recorded for every call, so a single real run of the flow tells you its peak
@@ -2310,21 +2327,87 @@ export declare interface ContextGuardPolicy {
2310
2327
  }
2311
2328
 
2312
2329
  /**
2313
- * Context headroom an agent reserves for itself, in tokens.
2330
+ * Context headroom an agent reserves for itself (GENC-1582).
2314
2331
  *
2315
- * A plain number, unlike the value-or-resolver shape `provider` / `temperature` /
2316
- * `resumable` use. The resolver form was drafted for it and removed before
2317
- * release: the gate is recomputed from a synchronous getter with no turn context
2318
- * to resolve against, so no path could ever have invoked one. Accepting a
2319
- * type-correct option that is guaranteed to do nothing is worse than not
2320
- * offering it — a warning after the fact still leaves the configuration inert.
2332
+ * Two figures, because a turn has two costs and only one of them is permanent.
2321
2333
  *
2322
- * It can widen to a resolver later without breaking a caller, since every
2323
- * existing `number` stays valid.
2334
+ * @beta
2335
+ */
2336
+ export declare interface ContextReserve {
2337
+ /**
2338
+ * Tokens that must be free before one of this agent's turns may START — the
2339
+ * agent's PEAK growth, not its lasting cost.
2340
+ *
2341
+ * The peak is the binding figure even for an agent that sheds most of it again:
2342
+ * `condenseWhen` stubs a spent payload at the end of the turn, so the room has
2343
+ * to be there while the turn is running, before any of that fires.
2344
+ */
2345
+ tokens: number;
2346
+ /**
2347
+ * Extra tokens ABOVE {@link ContextReserve.tokens} at which the warning
2348
+ * appears. Omit to fall back to `chatConfig.context.warnMultiplier`.
2349
+ *
2350
+ * The figure that belongs here is what one of this agent's turns costs
2351
+ * PERMANENTLY — its growth measured after condensation, not at its peak. That
2352
+ * makes the warning land exactly two turns before the block: at the warning
2353
+ * line there is room for a full turn plus the residue it leaves, which is one
2354
+ * turn, and then room for one more.
2355
+ *
2356
+ * It exists because the multiplier cannot express that. `warnMultiplier` scales
2357
+ * the whole reserve, so an agent that peaks at 400k and keeps 30k gets a warning
2358
+ * 800k below the window — a line every one of its turns crosses transiently and
2359
+ * then clears, which is noise rather than a signal.
2360
+ */
2361
+ warnAheadTokens?: number;
2362
+ }
2363
+
2364
+ /**
2365
+ * Context headroom an agent reserves for itself: a token count, or a
2366
+ * {@link ContextReserve} when the agent also wants to say where the warning goes.
2367
+ *
2368
+ * NOT the value-or-resolver shape `provider` / `temperature` / `resumable` use.
2369
+ * The resolver form was drafted and removed before release: the gate is recomputed
2370
+ * from a synchronous getter with no turn context to resolve against, so no path
2371
+ * could ever have invoked one. Accepting a type-correct option guaranteed to do
2372
+ * nothing is worse than not offering it.
2373
+ *
2374
+ * The object arm was added the way the plain form's docblock anticipated —
2375
+ * additively, so every existing `number` stays valid and means what it did.
2324
2376
  *
2325
2377
  * @beta
2326
2378
  */
2327
- export declare type ContextReserveInput = number;
2379
+ export declare type ContextReserveInput = number | ContextReserve;
2380
+
2381
+ /**
2382
+ * How much context headroom the next turn needs, and where that figure came from
2383
+ * (GENC-1582).
2384
+ *
2385
+ * @internal
2386
+ */
2387
+ declare interface ContextReserveResolution {
2388
+ /** Tokens to keep free for one turn — the winning agent's peak growth. */
2389
+ tokens: number;
2390
+ /**
2391
+ * Extra tokens above {@link ContextReserveResolution.tokens} at which the
2392
+ * warning appears, when the winning agent declared one. Travels WITH the winner
2393
+ * rather than being resolved separately: the two figures describe the same
2394
+ * agent's turn, and pairing a lean agent's warning with a hungry agent's block
2395
+ * would put the warning on the wrong side of it.
2396
+ */
2397
+ warnAheadTokens?: number;
2398
+ /** The agent whose `contextReserve` this is. */
2399
+ agentName: string;
2400
+ /**
2401
+ * Why this agent's reserve governs — which is what lets a blocked composer say
2402
+ * something TRUE about an agent the user has not invoked yet.
2403
+ *
2404
+ * `locked`: a user pin or a flow-owner lock means the next turn can only go to
2405
+ * this agent, so its reserve is the whole requirement. `reachable-max`: routing
2406
+ * is automatic, so the turn could land on any agent the classifier can pick, and
2407
+ * the hungriest of them is the one that has to fit.
2408
+ */
2409
+ basis: 'locked' | 'reachable-max';
2410
+ }
2328
2411
 
2329
2412
  /** A model used during a recorded cost session. */
2330
2413
  export declare interface CostSessionModelEntry {
@@ -2734,6 +2817,37 @@ export declare class FoundationAiAssistant extends GenesisElement {
2734
2817
  * redux actions re-renders without this; a store *swap* needs it.)
2735
2818
  */
2736
2819
  private _sessionRef?;
2820
+ /**
2821
+ * {@link FoundationAiAssistant.contextTokens} as of the last moment no turn was
2822
+ * running — the figure {@link FoundationAiAssistant.contextGate} is judged
2823
+ * against (GENC-1582).
2824
+ *
2825
+ * **Why the gate reads a held figure and the gauge does not.** They answer
2826
+ * different questions. The gauge is a meter: "how much context is in play right
2827
+ * now", and a build that momentarily fills 900k of a 1M window should say so.
2828
+ * The gate answers "may the next turn START", which is only meaningful when no
2829
+ * turn is running — the composer is disabled by `busy` throughout one anyway, so
2830
+ * a warning raised mid-turn is unactionable by construction.
2831
+ *
2832
+ * Reading the live figure meant the pill went amber part-way through every large
2833
+ * build and cleared itself at the end, because a `condenseWhen` agent's context
2834
+ * peaks mid-turn and is shed before the turn finishes. A signal that fires and
2835
+ * withdraws on its own each time teaches people to ignore it.
2836
+ *
2837
+ * **Held, not latched.** The distinction matters, because `contextGate` is
2838
+ * documented as derived-never-latched and the reason is a real dead end: a figure
2839
+ * pinned to a conversation that has since shrunk blocks the user out of the very
2840
+ * actions that would fix it. This holds only for the duration of a turn and is
2841
+ * refreshed the moment one settles, so no shrink can outlive it — a compaction
2842
+ * runs under `compacting` rather than `busy` and therefore updates it as it goes,
2843
+ * and Clear drops it outright. What it costs is one turn of lag: a turn that
2844
+ * balloons the resting size does not warn until it ends, which is the same trade
2845
+ * the flow-owner soften already makes deliberately.
2846
+ *
2847
+ * Unset falls back to the live figure, which is correct wherever there is no held
2848
+ * value yet — first paint, and a remount, both of which happen at rest.
2849
+ */
2850
+ private _gateContextTokens?;
2737
2851
  get messages(): ChatMessage[];
2738
2852
  set messages(value: ChatMessage[]);
2739
2853
  get state(): AiAssistantState;
@@ -4053,11 +4167,11 @@ export declare class FoundationAiAssistant extends GenesisElement {
4053
4167
  * Context-headroom thresholds for the active model (GENC-1567) — the warning
4054
4168
  * and block lines, plus the tail budget compaction aims at.
4055
4169
  *
4056
- * The agent's own `contextReserve` is deliberately NOT folded in here. This is
4057
- * the AMBIENT budget, which protects any turn from any agent; a per-agent
4058
- * reserve is an additional, larger requirement checked when that agent takes
4059
- * ownership of a flow, where there is a turn context to resolve its function
4060
- * form against.
4170
+ * The per-agent `contextReserve` IS folded in, via the driver (GENC-1582). It
4171
+ * used to be described here as a separate, later check made when an agent took
4172
+ * ownership of a flow — but there is no such check, and by flow entry the send
4173
+ * has already been allowed. Resolving it here, before the send, is what makes it
4174
+ * a gate rather than a label.
4061
4175
  *
4062
4176
  * Overhead is read off `messages` (the Redux mirror) rather than the driver's
4063
4177
  * own history, which the compaction plan uses. That is safe rather than sloppy:
@@ -4067,22 +4181,26 @@ export declare class FoundationAiAssistant extends GenesisElement {
4067
4181
  */
4068
4182
  get contextBudget(): ResolvedContextBudget;
4069
4183
  /**
4070
- * The active agent's declared `contextReserve`, when it is a plain number.
4184
+ * Headroom the next turn needs, resolved against how routing is currently
4185
+ * constrained (GENC-1582), and which agent set it.
4071
4186
  *
4072
- * Read off the LIVE active agent rather than checked at flow entry, which is
4073
- * where the design first put it. `flow-owner-changed` turned out to fire while
4074
- * the driver is already routing the turn — after the send was allowed — so it
4075
- * is not a pre-flight hook, and gating there would mean aborting a turn in
4076
- * flight. Reading the active agent instead means a hungry agent's reserve
4077
- * governs the gate from its next send onwards: one turn later than a true
4078
- * pre-flight, but synchronous, and it never has to interrupt anything.
4187
+ * The driver owns the answer because the driver owns routing: whether a pin or a
4188
+ * flow-owner lock is in force, which agents the classifier can reach, and which
4189
+ * one is active. This used to read `getActiveAgent()?.contextReserve` — the agent
4190
+ * that LAST ran — which is a different agent from the one about to run whenever
4191
+ * the orchestrator is doing its job, and the handover turn is precisely the one
4192
+ * that needs the room.
4193
+ *
4194
+ * Falls back to the active agent for a host-supplied driver that does not
4195
+ * implement the resolution, so an existing single-agent driver keeps the old
4196
+ * behaviour rather than silently losing its reserve.
4079
4197
  *
4080
4198
  * `contextReserve` is a plain number by design — see `ContextReserveInput` for
4081
4199
  * why a resolver form was dropped rather than accepted and ignored.
4082
4200
  *
4083
4201
  * @internal
4084
4202
  */
4085
- private get activeAgentReserveTokens();
4203
+ private get contextReserve();
4086
4204
  /**
4087
4205
  * What compacting right now would reclaim, or `null` when it would not be
4088
4206
  * worth it (GENC-1567). Sized against {@link FoundationAiAssistant.contextBudget},
@@ -5020,6 +5138,12 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
5020
5138
  continueFromHistory(transientPrimer?: ChatMessage[]): Promise<ChatDriverResult>;
5021
5139
  /** {@inheritDoc AiDriver.primeRestoredAgentState} */
5022
5140
  primeRestoredAgentState(snapshots: Record<string, unknown>): void;
5141
+ /**
5142
+ * {@inheritDoc AiDriver.getContextReserve}
5143
+ *
5144
+ * Two cases, because routing has two modes and they need opposite answers.
5145
+ */
5146
+ getContextReserve(): ContextReserveResolution | undefined;
5023
5147
  /** {@inheritDoc AiDriver.getActiveAgent} */
5024
5148
  getActiveAgent(): AgentConfig | undefined;
5025
5149
  private applyAgent;
@@ -5745,6 +5869,21 @@ export declare interface StatefulAgentInit<S> {
5745
5869
  * (fail-safe — never drop a resumable chat on a predicate bug).
5746
5870
  */
5747
5871
  resumable?: (ctx: StatefulAgentPersistenceContext<S>) => boolean | Promise<boolean>;
5872
+ /**
5873
+ * Tokens of context headroom to keep free for one of this agent's turns —
5874
+ * see `AgentConfig.contextReserve`, which this is passed straight through to.
5875
+ *
5876
+ * The third member of the flow-safety group with {@link StatefulAgentInit.resumable}
5877
+ * and {@link StatefulAgentInit.notResumableMessage}, and the one that was missing
5878
+ * (GENC-1582): a stateful flow is the case the reserve was designed for — it is
5879
+ * what runs the long tool loop — and yet the helper for building one could not
5880
+ * express it. Both agents that needed it in the field are built here.
5881
+ *
5882
+ * Set it from measurement. Per-turn `inputTokens` is recorded for every call, so
5883
+ * one real run of the flow gives you its peak growth; leaving it unset inherits
5884
+ * the global default, which is the right starting position.
5885
+ */
5886
+ contextReserve?: ContextReserveInput;
5748
5887
  /**
5749
5888
  * Message shown on return when {@link StatefulAgentInit.resumable} returned
5750
5889
  * `false`. Falls back to a built-in default when omitted.