@modelprofile.com/flexharness 13.1.1 → 13.2.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.
Files changed (47) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/classes.flexharness.cachewarm.d.ts +64 -0
  3. package/dist_ts/classes.flexharness.cachewarm.js +353 -0
  4. package/dist_ts/classes.flexharness.callbacks.js +2 -1
  5. package/dist_ts/classes.flexharness.core.d.ts +9 -1
  6. package/dist_ts/classes.flexharness.core.js +55 -6
  7. package/dist_ts/classes.flexharness.d.ts +10 -1
  8. package/dist_ts/classes.flexharness.deletion.js +2 -1
  9. package/dist_ts/classes.flexharness.js +14 -1
  10. package/dist_ts/classes.flexharness.lifecycle.js +4 -1
  11. package/dist_ts/classes.flexharness.prompts.d.ts +1 -1
  12. package/dist_ts/classes.flexharness.prompts.js +25 -5
  13. package/dist_ts/classes.flexharness.reversions.d.ts +2 -0
  14. package/dist_ts/classes.flexharness.reversions.js +7 -2
  15. package/dist_ts/classes.flexharness.runs.js +23 -2
  16. package/dist_ts/interfaces.d.ts +87 -1
  17. package/dist_ts/interfaces.flexharness.d.ts +15 -0
  18. package/dist_ts/interfaces.js +1 -1
  19. package/dist_ts/plugins.d.ts +1 -1
  20. package/dist_ts/plugins.js +1 -1
  21. package/dist_ts/utils.harnessoptions.d.ts +8 -1
  22. package/dist_ts/utils.harnessoptions.js +50 -6
  23. package/dist_ts/utils.json.js +11 -2
  24. package/dist_ts/utils.sessionpreview.d.ts +3 -3
  25. package/dist_ts/utils.sessionpreview.js +5 -5
  26. package/dist_ts_chat_harness/classes.transcript.js +17 -4
  27. package/package.json +3 -3
  28. package/readme.md +27 -0
  29. package/ts/00_commitinfo_data.ts +1 -1
  30. package/ts/classes.flexharness.cachewarm.ts +433 -0
  31. package/ts/classes.flexharness.callbacks.ts +1 -0
  32. package/ts/classes.flexharness.core.ts +50 -4
  33. package/ts/classes.flexharness.deletion.ts +1 -0
  34. package/ts/classes.flexharness.lifecycle.ts +3 -0
  35. package/ts/classes.flexharness.prompts.ts +22 -3
  36. package/ts/classes.flexharness.reversions.ts +7 -1
  37. package/ts/classes.flexharness.runs.ts +19 -1
  38. package/ts/classes.flexharness.ts +21 -0
  39. package/ts/interfaces.flexharness.ts +16 -0
  40. package/ts/interfaces.ts +98 -1
  41. package/ts/plugins.ts +1 -0
  42. package/ts/readme.md +27 -0
  43. package/ts/utils.harnessoptions.ts +74 -5
  44. package/ts/utils.json.ts +10 -1
  45. package/ts/utils.sessionpreview.ts +4 -4
  46. package/ts_chat_harness/classes.transcript.ts +14 -3
  47. package/ts_chat_harness/readme.md +1 -0
@@ -83,7 +83,7 @@ export class FlexHarnessTranscript {
83
83
  /** A copy of the component inputs; the transcript keeps its own objects. */
84
84
  view() {
85
85
  return structuredClone({
86
- messages: this.flexMessages.flatMap((message, index) => flexMessageToHarnessRows(message, index, this.timings)),
86
+ messages: this.flexMessages.flatMap((message, index) => message.cacheWarm ? [] : flexMessageToHarnessRows(message, index, this.timings)),
87
87
  permissions: [...this.permissionRows.values()],
88
88
  questions: [],
89
89
  status: this.status,
@@ -117,6 +117,10 @@ export class FlexHarnessTranscript {
117
117
  if (event.scopeId !== this.scopeId || event.sessionId !== this.sessionId)
118
118
  return change;
119
119
  const timestamp = Date.parse(event.timestamp);
120
+ // A cache-warm turn's messages are held but not shown, so their events change no row.
121
+ const warmMessage = 'message' in event
122
+ ? event.message.cacheWarm === true
123
+ : 'messageId' in event && this.flexMessages.some((message) => message.messageId === event.messageId && message.cacheWarm);
120
124
  switch (event.type) {
121
125
  case 'session.created':
122
126
  case 'session.updated':
@@ -127,7 +131,8 @@ export class FlexHarnessTranscript {
127
131
  change.messages = change.permissions = change.status = change.usage = true;
128
132
  break;
129
133
  case 'run.started':
130
- this.setStatus(change, { type: 'busy' });
134
+ if (!event.cacheWarm)
135
+ this.setStatus(change, { type: 'busy' });
131
136
  break;
132
137
  case 'message.created':
133
138
  case 'message.updated':
@@ -182,13 +187,17 @@ export class FlexHarnessTranscript {
182
187
  this.usage = sumUsage(this.usage, flexUsageToHarness(event.message.usage));
183
188
  change.usage = true;
184
189
  }
190
+ if (event.cacheWarm)
191
+ break;
185
192
  this.setStatus(change, event.status === 'failed'
186
193
  ? { type: 'error', ...(event.error ? { message: event.error.message } : {}) }
187
194
  : { type: 'idle' });
188
195
  break;
189
196
  case 'prompt.started':
190
197
  case 'prompt.running':
191
- this.runningQueueId = event.queueId;
198
+ // A steer cannot join a cache-warm turn.
199
+ if (!event.entry.cacheWarm)
200
+ this.runningQueueId = event.queueId;
192
201
  break;
193
202
  case 'prompt.finished':
194
203
  if (this.runningQueueId === event.queueId)
@@ -200,6 +209,10 @@ export class FlexHarnessTranscript {
200
209
  default:
201
210
  break;
202
211
  }
212
+ if (warmMessage) {
213
+ change.messages = false;
214
+ change.deltas = [];
215
+ }
203
216
  return change;
204
217
  }
205
218
  addPermission(request) {
@@ -308,4 +321,4 @@ export class FlexHarnessTranscript {
308
321
  change.messages = true;
309
322
  }
310
323
  }
311
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy50cmFuc2NyaXB0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfY2hhdF9oYXJuZXNzL2NsYXNzZXMudHJhbnNjcmlwdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFTQSxPQUFPLEVBQ0wsd0JBQXdCLEVBQ3hCLGFBQWEsRUFDYixpQkFBaUIsRUFDakIsdUJBQXVCLEVBQ3ZCLDhCQUE4QixFQUM5Qix5QkFBeUIsRUFDekIsMEJBQTBCLEVBQzFCLHFCQUFxQixFQUNyQixrQkFBa0IsR0FFbkIsTUFBTSx3QkFBd0IsQ0FBQztBQWlFaEMsTUFBTSxJQUFJLEdBQUcsSUFBSSxXQUFXLEVBQUUsQ0FBQztBQUUvQixtRkFBbUY7QUFDbkYsU0FBUyxpQkFBaUIsQ0FBQyxJQUFZLEVBQUUsS0FBYTtJQUNwRCxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDbEQsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNwQyxNQUFNLGtCQUFrQixHQUFHLFFBQVEsSUFBSSxNQUFNLElBQUksUUFBUSxJQUFJLE1BQU07V0FDOUQsT0FBTyxJQUFJLE1BQU0sSUFBSSxPQUFPLElBQUksTUFBTSxDQUFDO0lBQzVDLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNsRSxDQUFDO0FBRUQsU0FBUyxRQUFRLENBQ2YsS0FBNEIsRUFDNUIsS0FBNEI7SUFFNUIsTUFBTSxHQUFHLEdBQUcsQ0FBQyxHQUFnQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFB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
324
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy50cmFuc2NyaXB0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfY2hhdF9oYXJuZXNzL2NsYXNzZXMudHJhbnNjcmlwdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFTQSxPQUFPLEVBQ0wsd0JBQXdCLEVBQ3hCLGFBQWEsRUFDYixpQkFBaUIsRUFDakIsdUJBQXVCLEVBQ3ZCLDhCQUE4QixFQUM5Qix5QkFBeUIsRUFDekIsMEJBQTBCLEVBQzFCLHFCQUFxQixFQUNyQixrQkFBa0IsR0FFbkIsTUFBTSx3QkFBd0IsQ0FBQztBQWlFaEMsTUFBTSxJQUFJLEdBQUcsSUFBSSxXQUFXLEVBQUUsQ0FBQztBQUUvQixtRkFBbUY7QUFDbkYsU0FBUyxpQkFBaUIsQ0FBQyxJQUFZLEVBQUUsS0FBYTtJQUNwRCxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDbEQsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNwQyxNQUFNLGtCQUFrQixHQUFHLFFBQVEsSUFBSSxNQUFNLElBQUksUUFBUSxJQUFJLE1BQU07V0FDOUQsT0FBTyxJQUFJLE1BQU0sSUFBSSxPQUFPLElBQUksTUFBTSxDQUFDO0lBQzVDLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNsRSxDQUFDO0FBRUQsU0FBUyxRQUFRLENBQ2YsS0FBNEIsRUFDNUIsS0FBNEI7SUFFNUIsTUFBTSxHQUFHLEdBQUcsQ0FBQyxHQUFnQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsSUFB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
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "Task Venture Capital GmbH",
3
3
  "license": "MIT",
4
4
  "name": "@modelprofile.com/flexharness",
5
- "version": "13.1.1",
5
+ "version": "13.2.1",
6
6
  "type": "module",
7
7
  "description": "Managed agent sessions with tools, MCP, chat, OCR, documents and browser control through focused subpath imports.",
8
8
  "main": "./dist_ts/index.js",
@@ -91,8 +91,8 @@
91
91
  },
92
92
  "dependencies": {
93
93
  "@cfworker/json-schema": "^4.1.1",
94
- "@modelprofile.com/flexharness-agent": "13.1.1",
95
- "@modelprofile.com/flexharness-models": "13.1.1",
94
+ "@modelprofile.com/flexharness-agent": "13.2.1",
95
+ "@modelprofile.com/flexharness-models": "13.2.1",
96
96
  "@push.rocks/smartrequest": "^5.5.1",
97
97
  "ai": "^7.0.111",
98
98
  "zod": "^4.6.5"
package/readme.md CHANGED
@@ -418,6 +418,7 @@ The tools use strict action-discriminated inputs:
418
418
  - `goal`: `get`, `set`, or `clear`.
419
419
  - `scratchpad`: `get`, `set`, `append`, or `clear`. Append concatenates the supplied content exactly.
420
420
  - `rename_session`: sets the active session title and returns the authoritative session.
421
+ - `keep_cache_warm` (`cacheWarm: true`, root sessions only): keeps the session's prompt cache warm; see [Keeping the Prompt Cache Warm](#keeping-the-prompt-cache-warm).
421
422
 
422
423
  Every project action returns the authoritative revision and state; task mutations also return the affected task, and clear returns the removed tasks. Reads never save. A set, clear, append, update, idempotent create, or empty task clear that makes no state change returns the current revision without writing. Mutations load once, apply once, validate the complete next snapshot, and issue one compare-and-swap save at `revision + 1`. FlexHarness never retries or merges an external conflict.
423
424
 
@@ -729,6 +730,32 @@ The run takes a steer in at its next step boundary: after the tool results of it
729
730
 
730
731
  `steerPrompt()` resolves with `{ steerId, queueId, runId }` once the run accepted the steer and `prompt.steer.accepted` was emitted; `prompt.steer.applied` carries the `messageId` of the user message the steer became. Every accepted steer ends in exactly one of two ways: it is in the model context of later turns, or it is listed in `unappliedSteerIds`, in arrival order, on `IFlexPromptResult` and `run.finished`, and on the finished queue entry when that list is not empty. A steer is listed when the run did not take it in before it ended, because its last model step was already running or because it was stopped or failed, and also when the run itself does not stay in context. `keptInContext` on `run.finished` and on the finished queue entry says whether it does: it is `false` for a failed run and for a run stopped before any model output, and such a run lists every steer it accepted, including the ones it took in and reported with `prompt.steer.applied`. Send its prompt and its unapplied steers again to have them answered. The run stops taking steers atomically when its generation ends or `abort()`/`cancelPrompt()` stops it. A steer for a prompt without a running run, or whose run is ending, is refused with `FlexHarnessSteerRejectedError` reason `not-running`; the caller sends it as the next turn instead. Reusing a `steerId` within a run is refused with reason `duplicate`, an unknown `queueId` throws `FlexHarnessNotFoundError`, and child sessions reject this general API (their exact parent run uses `sendSubagentInput()` or `delegate_send` instead). A run holds at most `maxOutstandingPromptsPerSession` pending steers, and their bytes count against the session's outstanding prompt byte limit until the run takes them in or ends, so `startPrompt()` and `enqueuePrompt()` are refused with `FlexHarnessQueueFullError` while pending steers fill it. They are process-local like queue entries: a process restart ends the run and drops its pending steers, while steers it took in are durable parts of the turn.
731
732
 
733
+ ### Keeping the Prompt Cache Warm
734
+
735
+ A provider's prompt cache expires when nothing reads it for its TTL, and the next turn then writes the whole prefix again. `keepCacheWarm()` keeps a root session's cache warm until a deadline:
736
+
737
+ ```typescript
738
+ await harness.keepCacheWarm(scopeId, sessionId, { durationMs: 60 * 60 * 1000 }); // warm until 1 h from now
739
+ await harness.keepCacheWarm(scopeId, sessionId, { durationMs: 0 }); // stop
740
+ const state = await harness.getCacheWarm(scopeId, sessionId); // { sessionId, warmUntil?, nextWarmAt?, idleMs?, basis? }
741
+ ```
742
+
743
+ Each call replaces the deadline with `durationMs` from now, capped at `cacheWarm.maxDurationMs` (default 4 h, at most 24 h); `0` stops it. While the deadline has not passed and the session is idle, the harness sends a cache-warm prompt once the session has had no model call for the cache TTL less one minute: 4 minutes for a 5-minute cache, 29 for a 30-minute one, 59 for a 1-hour one. The TTL comes from the request the provider actually sent last, its `basis` in `getCacheWarm()`:
744
+
745
+ | `basis` | TTL | Sent request |
746
+ | --- | --- | --- |
747
+ | `anthropic-5m`, `anthropic-1h` | 5 min, 1 h | Anthropic: the shortest `cache_control` lifetime of the request |
748
+ | `openai-30m` | 30 min | OpenAI's API, GPT-5.6 and later: `prompt_cache_options.ttl` is 30 minutes after the last write or reuse |
749
+ | `openai-24h` | 30 min | OpenAI's API, an earlier model with `prompt_cache_retention: '24h'` (`cache: { retention: '24h' }`): OpenAI keeps the prefix typically around 30 minutes and up to 24 hours, without promising more |
750
+ | `openai-in-memory` | 5 min | OpenAI's API, an earlier model with `in_memory` or no retention; or the ChatGPT backend of ChatGPT accounts, which takes no retention and documents none. In-memory entries last 5 to 10 minutes of inactivity |
751
+ | `configured` | `cache.warmDurationMs` | any |
752
+
753
+ With no basis (another provider or endpoint, or a custom transport that may change the request) the TTL is 5 minutes. No OpenAI retention promises a hit beyond about 30 minutes, so none makes warming unnecessary; extended retention only spaces warm turns further apart. Source: OpenAI's [prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching), read 2026-10-11. A prompt, a steer or a running turn resets the wait, and no warm prompt is sent while a turn runs, a prompt is queued, an undo's redo history is open, a reversion is pending or a slash command runs. The warm prompt is the fixed text `FLEX_CACHE_WARM_PROMPT`, starting with `[cache-warm]`, which asks for the single word `ok`. Its turn uses the model and system of the session's last turn and offers the same tools, so it reads the cached prefix, but it refuses every tool call, takes at most two steps, caps each model call at `cacheWarm.maxOutputTokens` (default 1024) and takes no steer: `steerPrompt()` refuses one with reason `not-running`. Keeping a session warm costs a cache read of its prefix and a few new tokens per warm turn; the warm exchanges stay in the model context.
754
+
755
+ Warm turns are marked so hosts can leave them out of what they show and of what they count as work: both messages, the session `activity`, the prompt queue entry, `run.started`, `run.finished` and `model.usage` carry `cacheWarm: true`. A warm turn changes neither the session's `updatedAt` nor its `preview`, and adds no undo step. Three events report the keeper: `cache.warm.changed` (`source` `host` or `agent`, `warmUntil` absent once stopped), `cache.warm.completed` with the warm turn's `usage`, whose `cacheReadTokens` and `cacheWriteTokens` are the provider's own counts and show whether it hit the cache (OpenAI's `input_tokens_details.cached_tokens` and `cache_write_tokens`, Anthropic's `cache_read_input_tokens` and `cache_creation_input_tokens`), its `idleForMs` since the session's previous model call and its `basis`, and `cache.warm.stopped` with `reason` `deadline`, `cleared`, `failed` (the warm turn failed; `error` set) or `unavailable` (the session or its scope went away).
756
+
757
+ `builtInTools: { cacheWarm: true }` gives root sessions the `keep_cache_warm` tool, so the agent can keep its own cache warm: `{ duration: '1h' }` sets the deadline (amounts of `s`, `m` or `h`), `'off'` or `'0'` stops it. The keeper is process-local: it ends with `dispose()` and does not survive a restart. It warms only the loaded scope it was set in: retiring the scope or deleting the session stops it with `unavailable`, and it never loads a scope again. Tests pass `cacheWarm.clock` (`now`, `setTimeout`, `clearTimeout`) to drive it with a fake clock.
758
+
732
759
  ## Slash Commands
733
760
 
734
761
  `parseSlashCommand()` is the public strict pure parser. It accepts at most 768 KiB, returns `not-command` for input not starting with `/`, `malformed` for invalid slash syntax, and `parsed` with the exact input, lowercase command name, separator-stripped raw argument text, and OpenCode-compatible quoted tokenization. Command names match `[a-z][a-z0-9_-]{0,63}`. The public `isValidSlashCommandName(name)` and `isReservedSlashCommandName(name)` helpers let applications validate registration names against the same rules. Single and double quotes group tokens and are stripped; escapes are not interpreted.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@modelprofile.com/flexharness',
6
- version: '13.1.1',
6
+ version: '13.2.1',
7
7
  description: 'The FlexHarness toolbox source repository and shared dependency catalog.'
8
8
  }
@@ -0,0 +1,433 @@
1
+ import * as plugins from './plugins.js';
2
+ import {
3
+ FlexHarnessNotFoundError,
4
+ FlexHarnessSessionBusyError,
5
+ FlexHarnessValidationError,
6
+ errorToInfo,
7
+ } from './errors.js';
8
+ import type {
9
+ IFlexCacheWarmInput,
10
+ IFlexCacheWarmState,
11
+ IFlexPromptResult,
12
+ IFlexSession,
13
+ } from './interfaces.js';
14
+ import type {
15
+ IActiveRun,
16
+ IQueuedPrompt,
17
+ IStoredSessionState,
18
+ IStorageState,
19
+ TEventDetails,
20
+ } from './interfaces.flexharness.js';
21
+ import type { INormalizedCacheWarmOptions } from './utils.harnessoptions.js';
22
+ import { validateIdentifier } from './utils.validation.js';
23
+ import type { FlexHarnessCore } from './classes.flexharness.core.js';
24
+
25
+ /** The prompt of a cache-warm turn. It only has the model read the cached prefix again. */
26
+ export const FLEX_CACHE_WARM_PROMPT =
27
+ '[cache-warm] This message only keeps the prompt cache warm. No action is needed: do not call '
28
+ + 'any tool and reply with the single word ok.';
29
+
30
+ /** The cache TTL assumed when the model call does not report its own. */
31
+ const defaultCacheTtlMs = 5 * 60 * 1000;
32
+ /** How long before the cache expires a warm turn reads it. */
33
+ const cacheWarmMarginMs = 60 * 1000;
34
+ // A stray tool call is refused in the first step and answered in the second.
35
+ const cacheWarmMaxSteps = 2;
36
+
37
+ interface ISessionCacheWarm {
38
+ key: string;
39
+ scopeId: string;
40
+ sessionId: string;
41
+ /**
42
+ * The loaded scope state and session the keeper warms. The keeper never loads a scope: once
43
+ * these are retired, deleted or replaced, it stops.
44
+ */
45
+ state: IStorageState;
46
+ stored: IStoredSessionState;
47
+ /** The keeper's stop time, on its clock. */
48
+ warmUntil: number;
49
+ /** When the keeper was set, on its clock: the idle baseline before any model call. */
50
+ setAt: number;
51
+ deadlineTimer?: unknown;
52
+ warmTimer?: unknown;
53
+ nextWarmAt?: number;
54
+ firing: boolean;
55
+ /** The session's idle time since its previous model call when the last warm turn was due. */
56
+ firedAfterIdleMs?: number;
57
+ }
58
+
59
+ /** The idle time after a model call at which a warm turn reads the cache, from its TTL. */
60
+ export function cacheWarmIdleMs(ttlMs: number | undefined): number {
61
+ const ttl = ttlMs ?? defaultCacheTtlMs;
62
+ return Math.max(ttl - cacheWarmMarginMs, Math.floor(ttl / 2));
63
+ }
64
+
65
+ /** Replaces each tool's `execute` so a cache-warm turn refuses any tool call it makes. */
66
+ export function refuseToolExecution(tools: Record<string, unknown>): void {
67
+ for (const [name, tool] of Object.entries(tools)) {
68
+ if (!tool || typeof tool !== 'object' || typeof (tool as { execute?: unknown }).execute !== 'function') continue;
69
+ tools[name] = {
70
+ ...tool,
71
+ execute: async () => {
72
+ throw new FlexHarnessValidationError('A cache-warm turn runs no tools.');
73
+ },
74
+ };
75
+ }
76
+ }
77
+
78
+ /** Parses the agent tool's duration: `off`, `0`, or an amount of `s`, `m` or `h`, such as `1h`. */
79
+ export function parseCacheWarmDuration(duration: string): number {
80
+ const normalized = duration.trim().toLowerCase();
81
+ if (normalized === 'off' || normalized === '0') return 0;
82
+ const match = /^(\d{1,6})\s*(s|m|h)$/u.exec(normalized);
83
+ if (!match) {
84
+ throw new FlexHarnessValidationError('duration must be "off", "0" or an amount of s, m or h, such as "1h".');
85
+ }
86
+ const unitMs = match[2] === 's' ? 1000 : match[2] === 'm' ? 60_000 : 3_600_000;
87
+ return Number(match[1]) * unitMs;
88
+ }
89
+
90
+ /**
91
+ * Keeps a session's prompt cache warm until a deadline. Whenever the session has been idle for
92
+ * the cache TTL less a minute, it sends a marked cache-warm prompt, whose short turn reads the
93
+ * cached prefix again and so refreshes it. Process-local: it ends with the harness.
94
+ */
95
+ export class FlexHarnessCacheWarm<TScope> {
96
+ private readonly entries = new Map<string, ISessionCacheWarm>();
97
+
98
+ constructor(private readonly core: FlexHarnessCore<TScope>) {}
99
+
100
+ private get options(): INormalizedCacheWarmOptions {
101
+ return this.core.cacheWarmOptions;
102
+ }
103
+
104
+ private key(storageKey: string, sessionId: string): string {
105
+ return `${storageKey}\u0000${sessionId}`;
106
+ }
107
+
108
+ /** Sets the session's warm deadline to `durationMs` from now, capped; 0 stops the keeper. */
109
+ public async keepCacheWarm(
110
+ scopeId: string,
111
+ sessionId: string,
112
+ input: IFlexCacheWarmInput,
113
+ ): Promise<IFlexCacheWarmState> {
114
+ this.core.assertOpen();
115
+ validateIdentifier(sessionId, 'sessionId');
116
+ if (!input || typeof input !== 'object' || !Number.isSafeInteger(input.durationMs) || input.durationMs < 0) {
117
+ throw new FlexHarnessValidationError('durationMs must be a non-negative integer.');
118
+ }
119
+ const { state } = await this.core.resolveState(scopeId);
120
+ this.core.assertStateAcceptingWork(state);
121
+ const stored = this.core.requireSession(state, sessionId);
122
+ return this.set(scopeId, state, stored, input.durationMs, 'host');
123
+ }
124
+
125
+ public async getCacheWarm(scopeId: string, sessionId: string): Promise<IFlexCacheWarmState> {
126
+ validateIdentifier(sessionId, 'sessionId');
127
+ const { state } = await this.core.resolveState(scopeId);
128
+ const stored = this.core.requireSession(state, sessionId);
129
+ return this.project(stored, this.entries.get(this.key(state.storageKey, sessionId)));
130
+ }
131
+
132
+ /** The shared setter of the host API and the agent tool. */
133
+ public set(
134
+ scopeId: string,
135
+ state: IStorageState,
136
+ stored: IStoredSessionState,
137
+ durationMs: number,
138
+ source: 'host' | 'agent',
139
+ ): IFlexCacheWarmState {
140
+ this.core.assertSessionWritable(stored);
141
+ if (stored.session.agent !== undefined) {
142
+ throw new FlexHarnessValidationError('Only a root session keeps its cache warm.');
143
+ }
144
+ const key = this.key(state.storageKey, stored.session.sessionId);
145
+ const existing = this.entries.get(key);
146
+ if (durationMs === 0) {
147
+ if (existing) {
148
+ this.remove(existing);
149
+ this.emit(scopeId, stored.session, { type: 'cache.warm.changed', source });
150
+ this.emit(scopeId, stored.session, { type: 'cache.warm.stopped', reason: 'cleared' });
151
+ }
152
+ return this.project(stored, undefined);
153
+ }
154
+ const now = this.options.clock.now();
155
+ const entry: ISessionCacheWarm = existing ?? {
156
+ key,
157
+ scopeId,
158
+ sessionId: stored.session.sessionId,
159
+ state,
160
+ stored,
161
+ warmUntil: now,
162
+ setAt: now,
163
+ firing: false,
164
+ };
165
+ entry.scopeId = scopeId;
166
+ entry.state = state;
167
+ entry.stored = stored;
168
+ entry.warmUntil = now + Math.min(durationMs, this.options.maxDurationMs);
169
+ this.entries.set(key, entry);
170
+ if (entry.deadlineTimer !== undefined) this.options.clock.clearTimeout(entry.deadlineTimer);
171
+ entry.deadlineTimer = this.options.clock.setTimeout(
172
+ () => this.stopAtDeadline(entry),
173
+ entry.warmUntil - now,
174
+ );
175
+ this.emit(scopeId, stored.session, {
176
+ type: 'cache.warm.changed',
177
+ source,
178
+ warmUntil: new Date(this.toWallTime(entry.warmUntil)).toISOString(),
179
+ });
180
+ if (!entry.firing) this.arm(entry, state, stored);
181
+ return this.project(stored, entry);
182
+ }
183
+
184
+ /** The `keep_cache_warm` built-in tool of a root session. */
185
+ public createKeepCacheWarmTool(run: IActiveRun): unknown {
186
+ const maxHours = this.options.maxDurationMs / 3_600_000;
187
+ return plugins.tool({
188
+ description: 'Keep this session\'s prompt cache warm while you expect further work, so the next turn '
189
+ + 'reads the cached context instead of writing it again. While the session is idle, the harness then '
190
+ + 'sends a short "[cache-warm]" message shortly before the cache would expire. A duration such as '
191
+ + `"30m" or "1h" sets the deadline to that long from now, at most ${maxHours}h; calling the tool again `
192
+ + 'replaces the deadline; "off" or "0" stops it.',
193
+ inputSchema: plugins.z.object({
194
+ duration: plugins.z.string().min(1).max(32),
195
+ }).strict(),
196
+ execute: async ({ duration }: { duration: string }) => {
197
+ const durationMs = parseCacheWarmDuration(duration);
198
+ this.core.assertStateAcceptingWork(run.state);
199
+ if (run.state.sessions.get(run.sessionId) !== run.stored) {
200
+ throw new FlexHarnessNotFoundError('Session', run.sessionId);
201
+ }
202
+ return this.set(run.scopeId, run.state, run.stored, durationMs, 'agent');
203
+ },
204
+ });
205
+ }
206
+
207
+ /** A prompt or steer arrived: the session is active, so no warm turn is due. */
208
+ public onActivity(storageKey: string, sessionId: string): void {
209
+ const entry = this.entries.get(this.key(storageKey, sessionId));
210
+ if (entry) this.disarm(entry);
211
+ }
212
+
213
+ /** A root run starts: it is activity, and its model options are what warm turns repeat. */
214
+ public onRunStarted(run: IActiveRun, queued: IQueuedPrompt): void {
215
+ if (run.stored.session.agent !== undefined) return;
216
+ this.onActivity(run.state.storageKey, run.sessionId);
217
+ if (run.cacheWarm) return;
218
+ const options = queued.options ?? {};
219
+ run.stored.cacheWarmPromptOptions = {
220
+ ...(options.modelHint === undefined ? {} : { modelHint: options.modelHint }),
221
+ ...(options.system === undefined ? {} : { system: options.system }),
222
+ };
223
+ }
224
+
225
+ /** A model call of the session was dispatched: the cache's TTL counts from now. */
226
+ public onModelDispatch(run: IActiveRun, dispatch: plugins.ISmartAiCacheDispatch): void {
227
+ run.stored.lastCacheDispatch = {
228
+ at: this.options.clock.now(),
229
+ idleMs: cacheWarmIdleMs(dispatch.warmDurationMs),
230
+ ...(dispatch.basis === undefined ? {} : { basis: dispatch.basis }),
231
+ };
232
+ }
233
+
234
+ /** A prompt of the session finished, whether it ran or not. */
235
+ public onPromptFinished(
236
+ queued: IQueuedPrompt,
237
+ status: 'completed' | 'failed' | 'cancelled',
238
+ error?: unknown,
239
+ result?: IFlexPromptResult,
240
+ ): void {
241
+ const entry = this.entries.get(this.key(queued.state.storageKey, queued.sessionId));
242
+ if (queued.cacheWarm && status === 'completed' && result) {
243
+ const basis = queued.stored.lastCacheDispatch?.basis;
244
+ this.emit(queued.scopeId, queued.stored.session, {
245
+ type: 'cache.warm.completed',
246
+ runId: result.runId,
247
+ usage: result.usage,
248
+ ...(entry?.firedAfterIdleMs === undefined ? {} : { idleForMs: entry.firedAfterIdleMs }),
249
+ ...(basis === undefined ? {} : { basis }),
250
+ });
251
+ }
252
+ if (!entry) return;
253
+ entry.firing = false;
254
+ if (queued.cacheWarm && status === 'failed') {
255
+ this.remove(entry);
256
+ this.emit(queued.scopeId, queued.stored.session, {
257
+ type: 'cache.warm.stopped',
258
+ reason: 'failed',
259
+ ...(queued.runId === undefined ? {} : { runId: queued.runId }),
260
+ ...(error === undefined ? {} : { error: errorToInfo(error) }),
261
+ });
262
+ return;
263
+ }
264
+ if (!this.holdsSession(entry)) return;
265
+ this.arm(entry, entry.state, entry.stored);
266
+ }
267
+
268
+ /** The scope is being retired: its keepers stop, and none loads the scope again. */
269
+ public onStateRetiring(state: IStorageState): void {
270
+ for (const entry of [...this.entries.values()]) {
271
+ if (entry.state === state) this.stopUnavailable(entry);
272
+ }
273
+ }
274
+
275
+ /** The sessions were deleted: their keepers stop. */
276
+ public onSessionsDeleted(state: IStorageState, sessionIds: Iterable<string>): void {
277
+ for (const sessionId of sessionIds) {
278
+ const entry = this.entries.get(this.key(state.storageKey, sessionId));
279
+ if (entry?.state === state) this.stopUnavailable(entry);
280
+ }
281
+ }
282
+
283
+ public dispose(): void {
284
+ for (const entry of [...this.entries.values()]) this.remove(entry);
285
+ }
286
+
287
+ private project(stored: IStoredSessionState, entry: ISessionCacheWarm | undefined): IFlexCacheWarmState {
288
+ return Object.freeze({
289
+ sessionId: stored.session.sessionId,
290
+ ...(entry ? { warmUntil: new Date(this.toWallTime(entry.warmUntil)).toISOString() } : {}),
291
+ ...(entry?.nextWarmAt === undefined
292
+ ? {}
293
+ : { nextWarmAt: new Date(this.toWallTime(entry.nextWarmAt)).toISOString() }),
294
+ ...(entry ? { idleMs: stored.lastCacheDispatch?.idleMs ?? cacheWarmIdleMs(undefined) } : {}),
295
+ ...(entry && stored.lastCacheDispatch?.basis !== undefined ? { basis: stored.lastCacheDispatch.basis } : {}),
296
+ });
297
+ }
298
+
299
+ /** Projects a time of the keeper's clock onto the wall clock. */
300
+ private toWallTime(clockTime: number): number {
301
+ return Date.now() + (clockTime - this.options.clock.now());
302
+ }
303
+
304
+ private isBusy(state: IStorageState, stored: IStoredSessionState): boolean {
305
+ return state.activeRuns.has(stored.session.sessionId)
306
+ || stored.outstandingPromptsById.size > 0
307
+ || ['running', 'scheduled', 'waiting_permission'].includes(stored.session.status);
308
+ }
309
+
310
+ /**
311
+ * Whether a warm prompt now would change the session beyond the warm turn: it would end an
312
+ * undo's redo history, or wait for a reversion or a slash command to settle.
313
+ */
314
+ private isHeld(state: IStorageState, stored: IStoredSessionState): boolean {
315
+ return stored.pendingReversion !== undefined
316
+ || this.core.reversions.hasHiddenReversionSegments(stored)
317
+ || this.core.commands.activeSlashCommandExecutions.has(
318
+ this.core.commands.slashCommandSessionKey(state.storageKey, stored.session.sessionId),
319
+ );
320
+ }
321
+
322
+ /** Arms the warm timer of an idle session; a busy one arms it again when its run ends. */
323
+ private arm(entry: ISessionCacheWarm, state: IStorageState, stored: IStoredSessionState): void {
324
+ this.disarm(entry);
325
+ if (this.isBusy(state, stored)) return;
326
+ const mark = stored.lastCacheDispatch;
327
+ const base = mark?.at ?? entry.setAt;
328
+ const dueAt = base + (mark?.idleMs ?? cacheWarmIdleMs(undefined));
329
+ this.armAt(entry, dueAt);
330
+ }
331
+
332
+ private armAt(entry: ISessionCacheWarm, dueAt: number): void {
333
+ if (dueAt >= entry.warmUntil) return;
334
+ const now = this.options.clock.now();
335
+ entry.nextWarmAt = dueAt;
336
+ entry.warmTimer = this.options.clock.setTimeout(() => {
337
+ entry.warmTimer = undefined;
338
+ void this.fire(entry);
339
+ }, Math.max(0, dueAt - now));
340
+ }
341
+
342
+ private disarm(entry: ISessionCacheWarm): void {
343
+ if (entry.warmTimer !== undefined) this.options.clock.clearTimeout(entry.warmTimer);
344
+ entry.warmTimer = undefined;
345
+ delete entry.nextWarmAt;
346
+ }
347
+
348
+ private remove(entry: ISessionCacheWarm): void {
349
+ this.disarm(entry);
350
+ if (entry.deadlineTimer !== undefined) this.options.clock.clearTimeout(entry.deadlineTimer);
351
+ entry.deadlineTimer = undefined;
352
+ if (this.entries.get(entry.key) === entry) this.entries.delete(entry.key);
353
+ }
354
+
355
+ /** Whether the keeper's scope state is active and still holds its session. */
356
+ private holdsSession(entry: ISessionCacheWarm): boolean {
357
+ return entry.state.lifecycle === 'active'
358
+ && entry.state.sessions.get(entry.sessionId) === entry.stored
359
+ && !entry.state.tombstones.has(entry.sessionId);
360
+ }
361
+
362
+ private stopUnavailable(entry: ISessionCacheWarm, error?: unknown): void {
363
+ if (this.entries.get(entry.key) !== entry) return;
364
+ entry.firing = false;
365
+ this.remove(entry);
366
+ this.emit(entry.scopeId, entry.stored.session, {
367
+ type: 'cache.warm.stopped',
368
+ reason: 'unavailable',
369
+ ...(error === undefined ? {} : { error: errorToInfo(error) }),
370
+ });
371
+ }
372
+
373
+ private stopAtDeadline(entry: ISessionCacheWarm): void {
374
+ entry.deadlineTimer = undefined;
375
+ if (this.entries.get(entry.key) !== entry) return;
376
+ if (!this.holdsSession(entry)) {
377
+ this.stopUnavailable(entry);
378
+ return;
379
+ }
380
+ this.remove(entry);
381
+ this.emit(entry.scopeId, entry.stored.session, { type: 'cache.warm.stopped', reason: 'deadline' });
382
+ }
383
+
384
+ private async fire(entry: ISessionCacheWarm): Promise<void> {
385
+ delete entry.nextWarmAt;
386
+ if (this.entries.get(entry.key) !== entry || entry.firing || this.core.closed) return;
387
+ if (this.options.clock.now() >= entry.warmUntil) {
388
+ this.stopAtDeadline(entry);
389
+ return;
390
+ }
391
+ if (!this.holdsSession(entry)) {
392
+ this.stopUnavailable(entry);
393
+ return;
394
+ }
395
+ const { state, stored } = entry;
396
+ // A busy session arms the timer again when its run ends, through `onPromptFinished`.
397
+ if (this.isBusy(state, stored)) return;
398
+ const idleMs = stored.lastCacheDispatch?.idleMs ?? cacheWarmIdleMs(undefined);
399
+ if (this.isHeld(state, stored)) {
400
+ this.armAt(entry, this.options.clock.now() + idleMs);
401
+ return;
402
+ }
403
+ entry.firing = true;
404
+ entry.firedAfterIdleMs = this.options.clock.now() - (stored.lastCacheDispatch?.at ?? entry.setAt);
405
+ try {
406
+ await this.core.prompts.enqueuePromptInternal(
407
+ entry.scopeId,
408
+ entry.sessionId,
409
+ FLEX_CACHE_WARM_PROMPT,
410
+ { ...(stored.cacheWarmPromptOptions ?? {}), maxSteps: cacheWarmMaxSteps },
411
+ undefined,
412
+ undefined,
413
+ undefined,
414
+ undefined,
415
+ false,
416
+ true,
417
+ );
418
+ } catch (error) {
419
+ if (this.entries.get(entry.key) !== entry) return;
420
+ entry.firing = false;
421
+ if (error instanceof FlexHarnessSessionBusyError) {
422
+ // Work arrived meanwhile: its run end arms the timer again, as does a later idle check.
423
+ this.armAt(entry, this.options.clock.now() + idleMs);
424
+ return;
425
+ }
426
+ this.stopUnavailable(entry, error);
427
+ }
428
+ }
429
+
430
+ private emit(scopeId: string, session: IFlexSession, details: TEventDetails): void {
431
+ this.core.emitEvent(scopeId, session, details);
432
+ }
433
+ }
@@ -455,6 +455,7 @@ export class FlexHarnessRunCallbacks<TScope> {
455
455
  ...(generationId === undefined ? {} : { runId: generationId }),
456
456
  source,
457
457
  ...call,
458
+ ...(run?.cacheWarm ? { cacheWarm: true } : {}),
458
459
  });
459
460
  } catch (error) {
460
461
  // AgentSession isolates the entire callback, including event/context failures. Join it later.