@genesislcap/foundation-ai 14.469.0 → 14.470.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.
@@ -266,6 +266,12 @@ export declare class AnthropicTransport implements AITransport, ChatTransport, C
266
266
  * fields instead so its session total stays attributed to chat turns only.
267
267
  */
268
268
  private lifetimeCostUsd;
269
+ /**
270
+ * Estimated USD saved by prompt caching vs paying full input price, accumulated across
271
+ * every request. Net of cache-write premiums, so it can be briefly negative before reads
272
+ * accrue. Surfaced alongside `getLifetimeCost`.
273
+ */
274
+ private lifetimeSavingsUsd;
269
275
  constructor(config?: AnthropicTransportConfig);
270
276
  getConfig(): {
271
277
  provider: 'anthropic';
@@ -274,10 +280,35 @@ export declare class AnthropicTransport implements AITransport, ChatTransport, C
274
280
  };
275
281
  /** Estimated USD cost accumulated across every successful request on this transport instance. */
276
282
  getLifetimeCost(): number;
277
- /** Reset the lifetime cost counter. Intended for chat-clear / new-session flows. */
283
+ /** Estimated USD saved by prompt caching (net of write premiums) across this instance. */
284
+ getLifetimeSavings(): number;
285
+ /** Reset the lifetime cost + savings counters. Intended for chat-clear / new-session flows. */
278
286
  resetLifetimeCost(): void;
279
287
  sendStructuredPrompt(options: StructuredPromptOptions): Promise<string>;
280
288
  sendChatMessage(history: ChatMessage[], userMessage: string, options?: ChatRequestOptions): Promise<ChatMessage>;
289
+ /**
290
+ * Place prompt-cache breakpoints on the outgoing request per the resolved {@link CachePolicy}.
291
+ * A breakpoint is `cache_control: {type:'ephemeral'}` on a content block / tool / system block;
292
+ * the API caches the byte-exact prefix up to it. Below a model's minimum cacheable prefix the
293
+ * marker is silently ignored (no error), so placing one is always safe.
294
+ *
295
+ * Render order is tools → system → messages, so a breakpoint's cached prefix covers everything
296
+ * before it:
297
+ * - `'tools'` — last tool (caches the tool block; survives a later system-prompt change).
298
+ * - `'prompt'` — system block (caches tools + system); falls back to the last tool when there
299
+ * is no system prompt.
300
+ * - `'history'` — last stored message block (caches tools + system + the whole stored history),
301
+ * keeping the system-tier breakpoint as a cheaper fallback. The injected, never-stored tail
302
+ * context (if any) sits after this and is intentionally left uncached.
303
+ */
304
+ private applyCacheControl;
305
+ /**
306
+ * Append never-stored tail context (already framed by the caller) as the final block of the
307
+ * outbound messages. Called AFTER `applyCacheControl`, so any `'history'` breakpoint sits
308
+ * on the last *stored* block and this per-turn volatile block stays outside the cached prefix.
309
+ * At a model-call the last message is user-role; if not (defensive), start a fresh user turn.
310
+ */
311
+ private appendTailContext;
281
312
  private static readonly TOKENS_PER_MILLION;
282
313
  private static readonly COST_DECIMAL_PLACES;
283
314
  /**
@@ -334,6 +365,50 @@ declare interface AnthropicTransportConfig {
334
365
  maxTokens?: number;
335
366
  }
336
367
 
368
+ /**
369
+ * Prompt-cache policy for a chat request, resolved per turn. **Provider-neutral intent,
370
+ * provider-specific effect** — read this carefully, the two providers differ sharply:
371
+ *
372
+ * - **Anthropic** — `scope` controls where explicit `cache_control` breakpoints are placed
373
+ * (see each variant). This is the *only* way Anthropic caches; no scope → no caching.
374
+ * - **Gemini** — `scope` is effectively a **no-op**. Gemini 2.5+ does *implicit* caching
375
+ * automatically and always-on, caching as much of the stable prefix as it can (everything up
376
+ * to the volatile tail) on every request — regardless of `scope`, **including under
377
+ * `'default'`**. We issue no explicit `cachedContent`, so nothing here changes the Gemini
378
+ * request. Do NOT expect `'tools'`/`'prompt'` to *limit* Gemini caching, nor `'default'` to
379
+ * *disable* it; the most you control on Gemini is keeping the prefix byte-stable so implicit
380
+ * caching can engage.
381
+ *
382
+ * Omit, or use `{ scope: 'default' }`, to add no breakpoints of our own.
383
+ *
384
+ * @beta
385
+ */
386
+ export declare type CachePolicy = {
387
+ /**
388
+ * No breakpoints from us — you get the provider's own default behaviour (Anthropic: no
389
+ * caching; Gemini: automatic implicit caching). Equivalent to omitting `cachePolicy`.
390
+ */
391
+ scope: 'default';
392
+ } | {
393
+ /**
394
+ * How far up the request the (Anthropic) cache prefix extends — slide it to the last
395
+ * point that is byte-stable turn-to-turn:
396
+ * - `'tools'` — cache the tool definitions only. Use when the system prompt is *volatile*
397
+ * but tools are stable; tools render first, so this cache survives a system change.
398
+ * - `'prompt'` — cache tools + system prompt. For agents whose system prompt is stable.
399
+ * - `'history'` — cache tools + system + the full *stored* conversation. The breakpoint
400
+ * sits on the last stored message block, so it caches as much as possible without
401
+ * busting on the volatile tail; only the injected, never-stored tail context is left
402
+ * uncached. Needs an append-only, byte-stable history to actually hit.
403
+ */
404
+ scope: 'tools' | 'prompt' | 'history';
405
+ /**
406
+ * Cache time-to-live (Anthropic only). `'5m'` (default) suits back-to-back turns; `'1h'`
407
+ * keeps the entry warm across longer gaps at a higher write cost. Ignored on Gemini.
408
+ */
409
+ ttl?: '5m' | '1h';
410
+ };
411
+
337
412
  /**
338
413
  * Agent engine configuration for the chat assistant.
339
414
  *
@@ -591,6 +666,25 @@ export declare interface ChatRequestOptions {
591
666
  * @beta
592
667
  */
593
668
  temperature?: number;
669
+ /**
670
+ * Prompt-cache policy for this turn. See {@link CachePolicy} — Anthropic places explicit
671
+ * `cache_control` breakpoints per `scope`; Gemini caches implicitly regardless. Omit to add
672
+ * no breakpoints of our own.
673
+ *
674
+ * @beta
675
+ */
676
+ cachePolicy?: CachePolicy;
677
+ /**
678
+ * Operator/volatile context to inject at the **very tail** of the outbound messages — after the
679
+ * stored history *and* after any cache breakpoint — so the model sees it every turn without
680
+ * invalidating the cached prefix. **Never persisted to stored history**; it is re-supplied per
681
+ * request. Anthropic appends it as a text block on the final user turn; Gemini as a trailing
682
+ * `user` content. The caller is responsible for any framing (e.g. a `<system-reminder>` /
683
+ * `<current_context>` marker) so the model reads it as ambient context, not user input.
684
+ *
685
+ * @beta
686
+ */
687
+ tailContext?: string;
594
688
  }
595
689
 
596
690
  /**
@@ -874,6 +968,20 @@ export declare type ChatToolHandlers<TSubAgent extends {
874
968
  * guards the flow can release.
875
969
  */
876
970
  releaseAgent?: () => void;
971
+ /**
972
+ * Mark the agent's current *phase* as finished, collapsing every payload
973
+ * registered with `condenseWhen({ on: { kind: 'phaseEnd' } })` during that
974
+ * phase out of the to-model history (stored history is untouched). Advances
975
+ * an app-controlled phase epoch — repeatable, with no terminal effect
976
+ * (unlike `releaseAgent` / `completeSubAgent`): call it at each phase
977
+ * boundary and the next phase's reads accumulate against a fresh epoch.
978
+ *
979
+ * Intended for long multi-phase stateful flows that want to trim a phase's
980
+ * grounding reads (entity dumps, validations) as they advance, without
981
+ * ending the agent. Deliberately decoupled from any state-machine library:
982
+ * call it from whichever tool handlers advance the agent's own state.
983
+ */
984
+ endPhase?: () => void;
877
985
  /**
878
986
  * Declare how THIS tool call's payload(s) should be condensed out of the
879
987
  * history sent to the model once they go stale (see {@link CondensePolicy}).
@@ -1100,6 +1208,17 @@ export declare type CondenseResult = 'pointer' | 'drop' | {
1100
1208
  * re-selects the same agent on the next turn it is treated as the same
1101
1209
  * activation, so use `turnEnd` (not `agentEnd`) when you want per-request
1102
1210
  * collapse on a non-stateful agent.
1211
+ * - `phaseEnd` — collapse once the agent declares the current *phase* finished by
1212
+ * calling `endPhase()` from a tool handler. A phase is an app-defined span
1213
+ * *within* one activation — narrower than `agentEnd` (which only fires at whole-
1214
+ * agent disposal) and phase-aware in a way `age:N` is not. The intended use is a
1215
+ * long, multi-phase stateful flow (e.g. a bootstrap journey) that wants to trim
1216
+ * each phase's grounding reads as it moves on, without waiting for the whole
1217
+ * agent to end. It is NOT tied to any state-machine library: the author fires
1218
+ * `endPhase()` from whichever tool handlers advance their own notion of state,
1219
+ * exactly as `condenseWhen` is itself called from tool handlers. If `endPhase()`
1220
+ * is never called, the payload still collapses at `agentEnd` — so `phaseEnd`
1221
+ * degrades to `agentEnd` rather than never firing.
1103
1222
  *
1104
1223
  * @beta
1105
1224
  */
@@ -1113,6 +1232,8 @@ export declare type CondenseTrigger = {
1113
1232
  kind: 'turnEnd';
1114
1233
  } | {
1115
1234
  kind: 'agentEnd';
1235
+ } | {
1236
+ kind: 'phaseEnd';
1116
1237
  };
1117
1238
 
1118
1239
  /**
@@ -1134,7 +1255,12 @@ export declare type CondenseTrigger = {
1134
1255
  export declare interface CostReportingTransport {
1135
1256
  /** Estimated USD cost accumulated across every successful request on this transport instance. */
1136
1257
  getLifetimeCost(): number;
1137
- /** Reset the lifetime cost counter. Intended for chat-clear / new-session flows. */
1258
+ /**
1259
+ * Estimated USD saved by prompt caching vs paying full input price, accumulated across this
1260
+ * instance. Net of cache-write premiums, so it can be briefly negative before reads accrue.
1261
+ */
1262
+ getLifetimeSavings(): number;
1263
+ /** Reset the lifetime cost + savings counters. Intended for chat-clear / new-session flows. */
1138
1264
  resetLifetimeCost(): void;
1139
1265
  }
1140
1266
 
@@ -1268,6 +1394,12 @@ export declare class GeminiTransport implements AITransport, ChatTransport, Cost
1268
1394
  * fields instead so its session total stays attributed to chat turns only.
1269
1395
  */
1270
1396
  private lifetimeCostUsd;
1397
+ /**
1398
+ * Estimated USD saved by context caching vs paying full input price, accumulated across
1399
+ * every request. Gemini implicit caching has no write premium, so this is always ≥ 0.
1400
+ * Surfaced alongside `getLifetimeCost`.
1401
+ */
1402
+ private lifetimeSavingsUsd;
1271
1403
  constructor(config?: GeminiTransportConfig);
1272
1404
  getConfig(): {
1273
1405
  provider: 'gemini';
@@ -1276,7 +1408,9 @@ export declare class GeminiTransport implements AITransport, ChatTransport, Cost
1276
1408
  };
1277
1409
  /** Estimated USD cost accumulated across every successful request on this transport instance. */
1278
1410
  getLifetimeCost(): number;
1279
- /** Reset the lifetime cost counter. Intended for chat-clear / new-session flows. */
1411
+ /** Estimated USD saved by context caching across this instance (implicit caching → always ≥ 0). */
1412
+ getLifetimeSavings(): number;
1413
+ /** Reset the lifetime cost + savings counters. Intended for chat-clear / new-session flows. */
1280
1414
  resetLifetimeCost(): void;
1281
1415
  sendStructuredPrompt(options: StructuredPromptOptions): Promise<string>;
1282
1416
  sendChatMessage(history: ChatMessage[], userMessage: string, options?: ChatRequestOptions): Promise<ChatMessage>;
@@ -1643,6 +1777,42 @@ export declare interface ResolveAIConfigOptions {
1643
1777
  preferChrome?: boolean;
1644
1778
  }
1645
1779
 
1780
+ /**
1781
+ * Thrown when a response stops at `stop_reason: 'max_tokens'` while it still
1782
+ * carries tool calls. The model ran out of its output-token budget mid-stream,
1783
+ * so the truncated tool call's argument JSON was cut off before it closed — the
1784
+ * arguments are incomplete and the call is unusable (e.g. a `vfs_write` whose
1785
+ * `content` never finished serializing). Rather than hand a corrupt tool call to
1786
+ * the caller — which silently runs with missing args and tends to be retried into
1787
+ * an identical wall — the transport raises this so the failure is loud and
1788
+ * diagnosable.
1789
+ *
1790
+ * It is deterministic: re-issuing the same request hits the same cap. The remedy
1791
+ * is to raise the provider's `maxTokens` (see {@link AnthropicAIConfig.maxTokens})
1792
+ * or to split the work into smaller outputs — not to retry verbatim.
1793
+ *
1794
+ * @beta
1795
+ */
1796
+ export declare class ResponseTruncatedError extends Error {
1797
+ /** The model that produced the truncated response. */
1798
+ readonly model: string;
1799
+ /** The `max_tokens` cap the request was sent with. */
1800
+ readonly maxTokens: number;
1801
+ /** Output tokens generated before truncation, when the usage block is present. */
1802
+ readonly outputTokens: number | undefined;
1803
+ /** Names of the tool call(s) on the truncated turn (the last is the cut-off one). */
1804
+ readonly toolNames: string[];
1805
+ constructor(
1806
+ /** The model that produced the truncated response. */
1807
+ model: string,
1808
+ /** The `max_tokens` cap the request was sent with. */
1809
+ maxTokens: number,
1810
+ /** Output tokens generated before truncation, when the usage block is present. */
1811
+ outputTokens: number | undefined,
1812
+ /** Names of the tool call(s) on the truncated turn (the last is the cut-off one). */
1813
+ toolNames: string[]);
1814
+ }
1815
+
1646
1816
  /**
1647
1817
  * Server-proxy AI configuration (client calls your server; server calls the provider).
1648
1818
  *
@@ -1698,10 +1868,12 @@ declare interface StructuredPromptOptions {
1698
1868
  * available to it.
1699
1869
  * - `timeout` — the sub-agent did not finish within
1700
1870
  * {@link SubAgentRequestOptions.timeoutMs}; its run was aborted.
1871
+ * - `response_truncated` — a turn stopped at the provider's output-token cap with
1872
+ * an incomplete tool call; deterministic, so it is not retried.
1701
1873
  *
1702
1874
  * @beta
1703
1875
  */
1704
- export declare type SubAgentFailureReason = 'max_iterations' | 'malformed_tool_call' | 'empty_response' | 'unknown_tool_limit' | 'timeout';
1876
+ export declare type SubAgentFailureReason = 'max_iterations' | 'malformed_tool_call' | 'empty_response' | 'unknown_tool_limit' | 'timeout' | 'response_truncated';
1705
1877
 
1706
1878
  /**
1707
1879
  * Options passed to `requestSubAgent` at call time.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/foundation-ai",
3
3
  "description": "Genesis Foundation AI - Provider-agnostic AI configuration and shared utilities",
4
- "version": "14.469.0",
4
+ "version": "14.470.0",
5
5
  "sideEffects": false,
6
6
  "license": "SEE LICENSE IN license.txt",
7
7
  "main": "dist/esm/index.js",
@@ -52,17 +52,17 @@
52
52
  }
53
53
  },
54
54
  "devDependencies": {
55
- "@genesislcap/foundation-testing": "14.469.0",
56
- "@genesislcap/genx": "14.469.0",
57
- "@genesislcap/rollup-builder": "14.469.0",
58
- "@genesislcap/ts-builder": "14.469.0",
59
- "@genesislcap/uvu-playwright-builder": "14.469.0",
60
- "@genesislcap/vite-builder": "14.469.0",
61
- "@genesislcap/webpack-builder": "14.469.0"
55
+ "@genesislcap/foundation-testing": "14.470.0",
56
+ "@genesislcap/genx": "14.470.0",
57
+ "@genesislcap/rollup-builder": "14.470.0",
58
+ "@genesislcap/ts-builder": "14.470.0",
59
+ "@genesislcap/uvu-playwright-builder": "14.470.0",
60
+ "@genesislcap/vite-builder": "14.470.0",
61
+ "@genesislcap/webpack-builder": "14.470.0"
62
62
  },
63
63
  "dependencies": {
64
- "@genesislcap/foundation-logger": "14.469.0",
65
- "@genesislcap/foundation-utils": "14.469.0",
64
+ "@genesislcap/foundation-logger": "14.470.0",
65
+ "@genesislcap/foundation-utils": "14.470.0",
66
66
  "@microsoft/fast-foundation": "2.50.0"
67
67
  },
68
68
  "repository": {
@@ -73,5 +73,5 @@
73
73
  "publishConfig": {
74
74
  "access": "public"
75
75
  },
76
- "gitHead": "2b4460d5b9f20c36baddc55084e658bbcbd18c89"
76
+ "gitHead": "c222968b05dd3bb5db608222696b1f59a1c65913"
77
77
  }