summaryrefslogtreecommitdiffhomepage
path: root/packages/kernel/src/contracts/runtime.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/kernel/src/contracts/runtime.ts')
-rw-r--r--packages/kernel/src/contracts/runtime.ts45
1 files changed, 39 insertions, 6 deletions
diff --git a/packages/kernel/src/contracts/runtime.ts b/packages/kernel/src/contracts/runtime.ts
index 71d2211..deae126 100644
--- a/packages/kernel/src/contracts/runtime.ts
+++ b/packages/kernel/src/contracts/runtime.ts
@@ -121,13 +121,19 @@ export interface RunTurnInput {
* results. When omitted or returning an empty array, no injection happens
* (the runtime is unchanged).
*
- * Injected (not ambient) so the kernel stays pure: it owns no queue and
- * names no feature — it just calls the callback and appends what it gets.
- * Only invoked when a step PRODUCED tool calls (the tool-result boundary);
- * a step that ends without tool calls does not drain (the caller decides
- * what to do with any pending messages after the turn ends).
+ * May return a Promise (the runtime `await`s it): the shell uses this to
+ * PERSIST the injected messages to the store as part of the same critical
+ * section as the injection, so they are never lost (a fire-and-forget
+ * persist would race with the next step's `onStepComplete` append and
+ * collide on the store's seq counter). A sync return is still supported
+ * (backward-compatible). Injected (not ambient) so the kernel stays pure:
+ * it owns no queue and names no feature — it just calls the callback,
+ * awaits it, and appends what it gets. Only invoked when a step PRODUCED
+ * tool calls (the tool-result boundary); a step that ends without tool
+ * calls does not drain (the caller decides what to do with any pending
+ * messages after the turn ends).
*/
- readonly drainSteering?: () => readonly ChatMessage[];
+ readonly drainSteering?: () => readonly ChatMessage[] | Promise<readonly ChatMessage[]>;
/**
* Optional. Called by the runtime after each step's messages are finalized
@@ -142,6 +148,33 @@ export interface RunTurnInput {
readonly onStepComplete?: (messages: readonly ChatMessage[]) => Promise<void> | void;
/**
+ * Optional. Called by the runtime at each tool-result boundary — after a
+ * step that produced tool calls has been finalized (`onStepComplete` has run
+ * and any `drainSteering` messages have been appended), before the next step
+ * begins — giving the caller a chance to REPLACE the running message
+ * history. The caller returns a new message list which the runtime adopts
+ * as its working history for all subsequent steps, or `undefined`/an empty
+ * array to keep the history unchanged.
+ *
+ * The runtime receives the step's token `usage` (so the caller can check a
+ * threshold against the model's context window) and the current `messages`
+ * (the full prompt the next step would otherwise see). Generic and
+ * feature-agnostic: the runtime calls it and adopts whatever message list it
+ * returns — it names no feature, owns no threshold policy, and performs no
+ * I/O, mirroring `drainSteering`. The shell uses this to compact the history
+ * in-flight when the context window nears capacity, so a long-running turn
+ * (e.g. left overnight) does not run out of context mid-turn: it summarizes
+ * the old history and continues with the summary + recent messages. Only
+ * invoked when a step PRODUCED tool calls (there is a "next step" to compact
+ * before); a step that ends without tool calls ends the turn, so there is no
+ * boundary to compact at. Injected (not ambient) so the kernel stays pure.
+ */
+ readonly onStepBoundary?: (ctx: {
+ readonly stepUsage: Usage;
+ readonly messages: readonly ChatMessage[];
+ }) => Promise<readonly ChatMessage[] | undefined> | (readonly ChatMessage[] | undefined);
+
+ /**
* Optional injected retry strategy for retryable provider errors (e.g. HTTP
* 429 / 5xx "overloaded"). When omitted, a retryable error ends the step
* exactly as before (backward-compatible). When provided, the runtime wraps