@loopingai/core 0.1.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.
- package/LICENSE +674 -0
- package/README.md +279 -0
- package/dist/a2a/agent-stub.d.ts +61 -0
- package/dist/a2a/agent-stub.d.ts.map +1 -0
- package/dist/a2a/agent-stub.js +2 -0
- package/dist/a2a/agent-stub.js.map +1 -0
- package/dist/a2a/card.d.ts +119 -0
- package/dist/a2a/card.d.ts.map +1 -0
- package/dist/a2a/card.js +115 -0
- package/dist/a2a/card.js.map +1 -0
- package/dist/a2a/context.d.ts +14 -0
- package/dist/a2a/context.d.ts.map +1 -0
- package/dist/a2a/context.js +55 -0
- package/dist/a2a/context.js.map +1 -0
- package/dist/a2a/executor.d.ts +85 -0
- package/dist/a2a/executor.d.ts.map +1 -0
- package/dist/a2a/executor.js +107 -0
- package/dist/a2a/executor.js.map +1 -0
- package/dist/a2a/index.d.ts +18 -0
- package/dist/a2a/index.d.ts.map +1 -0
- package/dist/a2a/index.js +17 -0
- package/dist/a2a/index.js.map +1 -0
- package/dist/a2a/notify.d.ts +75 -0
- package/dist/a2a/notify.d.ts.map +1 -0
- package/dist/a2a/notify.js +161 -0
- package/dist/a2a/notify.js.map +1 -0
- package/dist/a2a/parts.d.ts +46 -0
- package/dist/a2a/parts.d.ts.map +1 -0
- package/dist/a2a/parts.js +95 -0
- package/dist/a2a/parts.js.map +1 -0
- package/dist/a2a/task-store.d.ts +45 -0
- package/dist/a2a/task-store.d.ts.map +1 -0
- package/dist/a2a/task-store.js +100 -0
- package/dist/a2a/task-store.js.map +1 -0
- package/dist/a2a/task.d.ts +86 -0
- package/dist/a2a/task.d.ts.map +1 -0
- package/dist/a2a/task.js +16 -0
- package/dist/a2a/task.js.map +1 -0
- package/dist/a2a/verify.d.ts +64 -0
- package/dist/a2a/verify.d.ts.map +1 -0
- package/dist/a2a/verify.js +122 -0
- package/dist/a2a/verify.js.map +1 -0
- package/dist/agent/budget.d.ts +47 -0
- package/dist/agent/budget.d.ts.map +1 -0
- package/dist/agent/budget.js +43 -0
- package/dist/agent/budget.js.map +1 -0
- package/dist/agent/control.d.ts +110 -0
- package/dist/agent/control.d.ts.map +1 -0
- package/dist/agent/control.js +115 -0
- package/dist/agent/control.js.map +1 -0
- package/dist/agent/final-reply.d.ts +49 -0
- package/dist/agent/final-reply.d.ts.map +1 -0
- package/dist/agent/final-reply.js +52 -0
- package/dist/agent/final-reply.js.map +1 -0
- package/dist/agent/history.d.ts +83 -0
- package/dist/agent/history.d.ts.map +1 -0
- package/dist/agent/history.js +117 -0
- package/dist/agent/history.js.map +1 -0
- package/dist/agent/index.d.ts +17 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +17 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/inference.d.ts +52 -0
- package/dist/agent/inference.d.ts.map +1 -0
- package/dist/agent/inference.js +79 -0
- package/dist/agent/inference.js.map +1 -0
- package/dist/agent/model.d.ts +75 -0
- package/dist/agent/model.d.ts.map +1 -0
- package/dist/agent/model.js +62 -0
- package/dist/agent/model.js.map +1 -0
- package/dist/agent/session.d.ts +92 -0
- package/dist/agent/session.d.ts.map +1 -0
- package/dist/agent/session.js +80 -0
- package/dist/agent/session.js.map +1 -0
- package/dist/config.d.ts +159 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +92 -0
- package/dist/config.js.map +1 -0
- package/dist/contract/index.d.ts +10 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +9 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/plugin.d.ts +163 -0
- package/dist/contract/plugin.d.ts.map +1 -0
- package/dist/contract/plugin.js +39 -0
- package/dist/contract/plugin.js.map +1 -0
- package/dist/contract/recipe.d.ts +170 -0
- package/dist/contract/recipe.d.ts.map +1 -0
- package/dist/contract/recipe.js +2 -0
- package/dist/contract/recipe.js.map +1 -0
- package/dist/contract/validation.d.ts +86 -0
- package/dist/contract/validation.d.ts.map +1 -0
- package/dist/contract/validation.js +76 -0
- package/dist/contract/validation.js.map +1 -0
- package/dist/db/db.d.ts +131 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +91 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/index.d.ts +9 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +9 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrations/index.d.ts +21 -0
- package/dist/db/migrations/index.d.ts.map +1 -0
- package/dist/db/migrations/index.js +54 -0
- package/dist/db/migrations/index.js.map +1 -0
- package/dist/db/models/subtasks.d.ts +102 -0
- package/dist/db/models/subtasks.d.ts.map +1 -0
- package/dist/db/models/subtasks.js +285 -0
- package/dist/db/models/subtasks.js.map +1 -0
- package/dist/db/models/tasks.d.ts +92 -0
- package/dist/db/models/tasks.d.ts.map +1 -0
- package/dist/db/models/tasks.js +212 -0
- package/dist/db/models/tasks.js.map +1 -0
- package/dist/db/schema.d.ts +488 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +91 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/env.d.ts +46 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +40 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/platform.d.ts +47 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +47 -0
- package/dist/platform.js.map +1 -0
- package/dist/runtime/index.d.ts +86 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +123 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/tool-families.d.ts +33 -0
- package/dist/runtime/tool-families.d.ts.map +1 -0
- package/dist/runtime/tool-families.js +62 -0
- package/dist/runtime/tool-families.js.map +1 -0
- package/dist/subagent/fingerprint.d.ts +38 -0
- package/dist/subagent/fingerprint.d.ts.map +1 -0
- package/dist/subagent/fingerprint.js +97 -0
- package/dist/subagent/fingerprint.js.map +1 -0
- package/dist/subagent/index.d.ts +147 -0
- package/dist/subagent/index.d.ts.map +1 -0
- package/dist/subagent/index.js +299 -0
- package/dist/subagent/index.js.map +1 -0
- package/dist/subagent/prompt.d.ts +34 -0
- package/dist/subagent/prompt.d.ts.map +1 -0
- package/dist/subagent/prompt.js +46 -0
- package/dist/subagent/prompt.js.map +1 -0
- package/dist/subagent/run.d.ts +151 -0
- package/dist/subagent/run.d.ts.map +1 -0
- package/dist/subagent/run.js +491 -0
- package/dist/subagent/run.js.map +1 -0
- package/dist/subagent/workspace.d.ts +65 -0
- package/dist/subagent/workspace.d.ts.map +1 -0
- package/dist/subagent/workspace.js +53 -0
- package/dist/subagent/workspace.js.map +1 -0
- package/dist/subtasks/catalog.d.ts +40 -0
- package/dist/subtasks/catalog.d.ts.map +1 -0
- package/dist/subtasks/catalog.js +36 -0
- package/dist/subtasks/catalog.js.map +1 -0
- package/dist/subtasks/decomposition.d.ts +90 -0
- package/dist/subtasks/decomposition.d.ts.map +1 -0
- package/dist/subtasks/decomposition.js +205 -0
- package/dist/subtasks/decomposition.js.map +1 -0
- package/dist/subtasks/delegate.d.ts +77 -0
- package/dist/subtasks/delegate.d.ts.map +1 -0
- package/dist/subtasks/delegate.js +102 -0
- package/dist/subtasks/delegate.js.map +1 -0
- package/dist/subtasks/index.d.ts +11 -0
- package/dist/subtasks/index.d.ts.map +1 -0
- package/dist/subtasks/index.js +11 -0
- package/dist/subtasks/index.js.map +1 -0
- package/dist/subtasks/scheduler.d.ts +49 -0
- package/dist/subtasks/scheduler.d.ts.map +1 -0
- package/dist/subtasks/scheduler.js +48 -0
- package/dist/subtasks/scheduler.js.map +1 -0
- package/dist/subtasks/subtask-types.d.ts +100 -0
- package/dist/subtasks/subtask-types.d.ts.map +1 -0
- package/dist/subtasks/subtask-types.js +111 -0
- package/dist/subtasks/subtask-types.js.map +1 -0
- package/dist/subtasks/types.d.ts +321 -0
- package/dist/subtasks/types.d.ts.map +1 -0
- package/dist/subtasks/types.js +16 -0
- package/dist/subtasks/types.js.map +1 -0
- package/dist/testing/auth.d.ts +12 -0
- package/dist/testing/auth.d.ts.map +1 -0
- package/dist/testing/auth.js +25 -0
- package/dist/testing/auth.js.map +1 -0
- package/dist/testing/do.d.ts +30 -0
- package/dist/testing/do.d.ts.map +1 -0
- package/dist/testing/do.js +26 -0
- package/dist/testing/do.js.map +1 -0
- package/dist/testing/fake-session.d.ts +27 -0
- package/dist/testing/fake-session.d.ts.map +1 -0
- package/dist/testing/fake-session.js +38 -0
- package/dist/testing/fake-session.js.map +1 -0
- package/dist/testing/fixtures.d.ts +27 -0
- package/dist/testing/fixtures.d.ts.map +1 -0
- package/dist/testing/fixtures.js +67 -0
- package/dist/testing/fixtures.js.map +1 -0
- package/dist/testing/index.d.ts +31 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +31 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/mock-model.d.ts +43 -0
- package/dist/testing/mock-model.d.ts.map +1 -0
- package/dist/testing/mock-model.js +62 -0
- package/dist/testing/mock-model.js.map +1 -0
- package/dist/testing/node.d.ts +24 -0
- package/dist/testing/node.d.ts.map +1 -0
- package/dist/testing/node.js +24 -0
- package/dist/testing/node.js.map +1 -0
- package/dist/testing/vcr-global-setup.d.ts +3 -0
- package/dist/testing/vcr-global-setup.d.ts.map +1 -0
- package/dist/testing/vcr-global-setup.js +9 -0
- package/dist/testing/vcr-global-setup.js.map +1 -0
- package/dist/testing/vcr-shared.d.ts +21 -0
- package/dist/testing/vcr-shared.d.ts.map +1 -0
- package/dist/testing/vcr-shared.js +21 -0
- package/dist/testing/vcr-shared.js.map +1 -0
- package/dist/testing/vcr-spec.d.ts +17 -0
- package/dist/testing/vcr-spec.d.ts.map +1 -0
- package/dist/testing/vcr-spec.js +72 -0
- package/dist/testing/vcr-spec.js.map +1 -0
- package/dist/testing/vcr.d.ts +81 -0
- package/dist/testing/vcr.d.ts.map +1 -0
- package/dist/testing/vcr.js +211 -0
- package/dist/testing/vcr.js.map +1 -0
- package/dist/worker/index.d.ts +62 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +194 -0
- package/dist/worker/index.js.map +1 -0
- package/eslint-rules/index.js +31 -0
- package/eslint-rules/no-deprecated-object-properties.js +81 -0
- package/package.json +147 -0
- package/scripts/generate-keys.mjs +53 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { ModelMessage, ToolSet } from "ai";
|
|
2
|
+
import { type RecipePolicy } from "../contract/validation.js";
|
|
3
|
+
import type { ModelPair } from "../agent/model.js";
|
|
4
|
+
import type { ProgressEvent, RecipeChunkResult, RecipeExecutionRequest, RecipeExecutionResult } from "../subtasks/types.js";
|
|
5
|
+
import type { RecipeLimits } from "../contract/recipe.js";
|
|
6
|
+
/**
|
|
7
|
+
* The resumable execution runner — ONE loop for every Recipe, from a single-shot
|
|
8
|
+
* general Subtask to a long game. It runs the model/tool loop in durable
|
|
9
|
+
* **chunks**: each call advances as far as `chunkSoftMs` allows (or until the run
|
|
10
|
+
* spends its budget, or until a tool emits progress), checkpoints its rolling state
|
|
11
|
+
* after every turn, and returns either a terminal result or a "not done" yield. The
|
|
12
|
+
* facet persists the state between chunks and the Workflow runs each chunk as its
|
|
13
|
+
* own durable, retryable step — so no single step ever approaches the platform
|
|
14
|
+
* step timeout, and a crash loses at most the in-flight turn.
|
|
15
|
+
*
|
|
16
|
+
* Domain behavior lives entirely in the tool families; this runner is agnostic of
|
|
17
|
+
* what work happens beneath it. State that must outlive the small rolling context
|
|
18
|
+
* window is the recipe's responsibility to persist to its workspace.
|
|
19
|
+
*/
|
|
20
|
+
/** The rolling state carried across a run's chunks (persisted by the facet). */
|
|
21
|
+
export interface ChunkRunState {
|
|
22
|
+
/** Windowed conversation so far (system is supplied separately, not stored here). */
|
|
23
|
+
messages: ModelMessage[];
|
|
24
|
+
/** Total model turns (tool-loop steps) across every chunk — bounds `maxTurns`. */
|
|
25
|
+
turns: number;
|
|
26
|
+
/** Total `generateText` invocations (including fallbacks and summarization). */
|
|
27
|
+
llmCalls: number;
|
|
28
|
+
/** Wall-clock start of the whole execution (for the metrics footer). */
|
|
29
|
+
startedAtMs: number;
|
|
30
|
+
}
|
|
31
|
+
/** Everything one chunk needs, assembled by the facet (or a test) each call. */
|
|
32
|
+
export interface ChunkRunDeps {
|
|
33
|
+
system: string;
|
|
34
|
+
/** The rendered initial user message; seeds a fresh run's first chunk. */
|
|
35
|
+
seedPrompt: string;
|
|
36
|
+
models: ModelPair;
|
|
37
|
+
tools: ToolSet;
|
|
38
|
+
/** The run's budget: turns and wall clock. Nothing else. */
|
|
39
|
+
limits: RecipeLimits;
|
|
40
|
+
/**
|
|
41
|
+
* How long this chunk may run before it checkpoints and yields a fresh durable
|
|
42
|
+
* step — the Workers step-timeout guard, not a budget, and identical for every
|
|
43
|
+
* Recipe (`CHUNK_SOFT_MS` in `platform.ts`). Injected rather than imported so
|
|
44
|
+
* the runner stays testable with a fake clock.
|
|
45
|
+
*/
|
|
46
|
+
chunkSoftMs: number;
|
|
47
|
+
historyWindow: number;
|
|
48
|
+
/**
|
|
49
|
+
* How many of the most recent assistant turns keep their tool results in full;
|
|
50
|
+
* older ones are stubbed by {@link elideToolOutputs}. A mechanic of the window
|
|
51
|
+
* rather than a property of a domain — see `TOOL_OUTPUT_WINDOW` in `config.ts`.
|
|
52
|
+
*/
|
|
53
|
+
toolOutputWindow: number;
|
|
54
|
+
reportMetrics: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Output-token ceiling for every model call in this chunk. Injected rather
|
|
57
|
+
* than imported: it is host config, and a published runner must not carry a
|
|
58
|
+
* hardcoded one.
|
|
59
|
+
*/
|
|
60
|
+
maxOutputTokens: number;
|
|
61
|
+
now: () => number;
|
|
62
|
+
/** Shared sink the tool families push progress events into (fresh per chunk). */
|
|
63
|
+
progress: ProgressEvent[];
|
|
64
|
+
/** Persist rolling state after every model turn — the crash-safety checkpoint. */
|
|
65
|
+
checkpoint: (state: ChunkRunState) => void | Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Interrupts the in-flight model call when the parent Task is canceled. Without
|
|
68
|
+
* it a cancellation is only observed at the next chunk boundary — up to
|
|
69
|
+
* `chunkSoftMs` of unwanted play. See {@link ChunkAttempt}'s `aborted` case for
|
|
70
|
+
* why an abort is emphatically *not* a model failure.
|
|
71
|
+
*/
|
|
72
|
+
abortSignal?: AbortSignal;
|
|
73
|
+
}
|
|
74
|
+
export interface ChunkRunOutput {
|
|
75
|
+
outcome: RecipeChunkResult;
|
|
76
|
+
state: ChunkRunState;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Trim the conversation to the most recent `window` turns (plus the seed message),
|
|
80
|
+
* cutting at an assistant boundary so no tool-result message is left orphaned.
|
|
81
|
+
* Older turns fall out of context — the recipe's soul directs the model to persist
|
|
82
|
+
* anything durable to its workspace, which the window never touches.
|
|
83
|
+
*/
|
|
84
|
+
export declare function windowMessages(messages: ModelMessage[], window: number): ModelMessage[];
|
|
85
|
+
/** What replaces a tool result that has aged out of the detail window. */
|
|
86
|
+
export declare const ELIDED_TOOL_OUTPUT = "[output from an earlier turn, trimmed to save context]";
|
|
87
|
+
/**
|
|
88
|
+
* Shrink the window without shortening it: stub out the *payloads* of tool
|
|
89
|
+
* results the model has moved past, leaving every message, every tool call — and
|
|
90
|
+
* therefore every note the model wrote itself — exactly where it was.
|
|
91
|
+
*
|
|
92
|
+
* This is the other half of {@link windowMessages}, and it exists because the two
|
|
93
|
+
* things a rolling window holds have opposite value curves. A recipe's own
|
|
94
|
+
* reasoning and its tool-call *inputs* stay useful for as long as the run does; a
|
|
95
|
+
* tool *result* is a snapshot of a world that has since moved, and it is also
|
|
96
|
+
* where nearly all the tokens are. Dropping whole turns to bound context throws
|
|
97
|
+
* both away together, which is why a short window makes a model re-derive what it
|
|
98
|
+
* already knew.
|
|
99
|
+
*
|
|
100
|
+
* A result's output survives if any of:
|
|
101
|
+
*
|
|
102
|
+
* 1. it is within the last `keepRecent` assistant messages — the same unit
|
|
103
|
+
* `windowMessages` counts in, so the two knobs are commensurable;
|
|
104
|
+
* 2. **it is the newest result for its tool**, at any age. Tools may hold state
|
|
105
|
+
* about what they have already shown this chunk and answer a repeat with
|
|
106
|
+
* "unchanged since you last looked" — a real optimization that becomes a lie
|
|
107
|
+
* the moment the render it points at is gone. Keeping the newest per tool is
|
|
108
|
+
* what makes "your latest view" a thing the model can still see, and it is why
|
|
109
|
+
* `keepRecent` can be small;
|
|
110
|
+
* 3. it reports a failure or a denial — short, diagnostic, and losing *why* a
|
|
111
|
+
* call failed costs a retry to rediscover for no saving;
|
|
112
|
+
* 4. it is already small enough that stubbing it saves nothing.
|
|
113
|
+
*
|
|
114
|
+
* Idempotent: a stubbed part is small, so a later pass leaves it alone.
|
|
115
|
+
*
|
|
116
|
+
* `keepRecent` is normalized rather than trusted — see the note on `keep` below.
|
|
117
|
+
* Zero is a legal, meaningful value: no turn is recent, so rule 1 protects
|
|
118
|
+
* nothing and rules 2-4 carry the whole of what survives.
|
|
119
|
+
*/
|
|
120
|
+
export declare function elideToolOutputs(messages: ModelMessage[], keepRecent: number): ModelMessage[];
|
|
121
|
+
/**
|
|
122
|
+
* Run one durable chunk. Returns a terminal result (natural completion, budget
|
|
123
|
+
* exhaustion, or exhausted models) or a `done: false` yield with the progress
|
|
124
|
+
* emitted this chunk. Throws only on a transient platform fault, so the Workflow
|
|
125
|
+
* step retries and resumes from the last checkpoint.
|
|
126
|
+
*/
|
|
127
|
+
export declare function runResumableChunk(prev: ChunkRunState | null, deps: ChunkRunDeps): Promise<ChunkRunOutput>;
|
|
128
|
+
/** Everything a whole-run (non-chunked) execution needs — for tests and callers
|
|
129
|
+
* that want a single terminal result rather than driving chunks themselves. */
|
|
130
|
+
export interface RecipeRunDeps {
|
|
131
|
+
models: ModelPair;
|
|
132
|
+
tools: ToolSet;
|
|
133
|
+
now?: () => number;
|
|
134
|
+
/** The capability boundary a recipe is re-validated against inside the child. */
|
|
135
|
+
policy: RecipePolicy;
|
|
136
|
+
/** `CoreConfig.toolOutputWindow`. */
|
|
137
|
+
toolOutputWindow: number;
|
|
138
|
+
/** `CoreConfig.model.maxOutputTokens`. */
|
|
139
|
+
maxOutputTokens: number;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Run one recipe execution to a terminal result, driving {@link runResumableChunk}
|
|
143
|
+
* chunk by chunk in memory. A run that fits its budget finishes in one chunk;
|
|
144
|
+
* otherwise it loops until the budget yields a summary. Used by tests and any
|
|
145
|
+
* caller wanting the whole outcome; the facet drives chunks durably instead, for
|
|
146
|
+
* crash-safety across the Workflow.
|
|
147
|
+
*
|
|
148
|
+
* Throws only on a transient platform fault (as {@link runResumableChunk} does).
|
|
149
|
+
*/
|
|
150
|
+
export declare function runRecipeExecution(request: RecipeExecutionRequest, deps: RecipeRunDeps): Promise<RecipeExecutionResult>;
|
|
151
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/subagent/run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,YAAY,EAGZ,OAAO,EACR,MAAM,IAAI,CAAC;AAKZ,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG1D;;;;;;;;;;;;;GAaG;AAEH,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,qFAAqF;IACrF,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,kFAAkF;IAClF,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,gFAAgF;AAChF,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,4DAA4D;IAC5D,MAAM,EAAE,YAAY,CAAC;IACrB;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,iFAAiF;IACjF,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,kFAAkF;IAClF,UAAU,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,KAAK,EAAE,aAAa,CAAC;CACtB;AAiBD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,YAAY,EAAE,EACxB,MAAM,EAAE,MAAM,GACb,YAAY,EAAE,CAShB;AAED,0EAA0E;AAC1E,eAAO,MAAM,kBAAkB,2DAC2B,CAAC;AAoB3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,YAAY,EAAE,EACxB,UAAU,EAAE,MAAM,GACjB,YAAY,EAAE,CA0EhB;AA2DD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,aAAa,GAAG,IAAI,EAC1B,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,cAAc,CAAC,CA4KzB;AAyFD;+EAC+E;AAC/E,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,iFAAiF;IACjF,MAAM,EAAE,YAAY,CAAC;IACrB,qCAAqC;IACrC,gBAAgB,EAAE,MAAM,CAAC;IACzB,0CAA0C;IAC1C,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,sBAAsB,EAC/B,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,qBAAqB,CAAC,CAyChC"}
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
import { generateText, isStepCount } from "ai";
|
|
2
|
+
import { CHUNK_SOFT_MS } from "../platform.js";
|
|
3
|
+
import { stepAllowance } from "../agent/budget.js";
|
|
4
|
+
import { isTransientAiError } from "../agent/inference.js";
|
|
5
|
+
import { validateRecipe } from "../contract/validation.js";
|
|
6
|
+
import { renderSubagentPrompt } from "./prompt.js";
|
|
7
|
+
/**
|
|
8
|
+
* Trim the conversation to the most recent `window` turns (plus the seed message),
|
|
9
|
+
* cutting at an assistant boundary so no tool-result message is left orphaned.
|
|
10
|
+
* Older turns fall out of context — the recipe's soul directs the model to persist
|
|
11
|
+
* anything durable to its workspace, which the window never touches.
|
|
12
|
+
*/
|
|
13
|
+
export function windowMessages(messages, window) {
|
|
14
|
+
if (messages.length <= 1)
|
|
15
|
+
return messages;
|
|
16
|
+
const assistantIdx = [];
|
|
17
|
+
for (let i = 1; i < messages.length; i++) {
|
|
18
|
+
if (messages[i].role === "assistant")
|
|
19
|
+
assistantIdx.push(i);
|
|
20
|
+
}
|
|
21
|
+
if (assistantIdx.length <= window)
|
|
22
|
+
return messages;
|
|
23
|
+
const start = assistantIdx[assistantIdx.length - window];
|
|
24
|
+
return [messages[0], ...messages.slice(start)];
|
|
25
|
+
}
|
|
26
|
+
/** What replaces a tool result that has aged out of the detail window. */
|
|
27
|
+
export const ELIDED_TOOL_OUTPUT = "[output from an earlier turn, trimmed to save context]";
|
|
28
|
+
/**
|
|
29
|
+
* Below this many serialized characters a result is not worth stubbing — the
|
|
30
|
+
* stub would be most of what it replaced.
|
|
31
|
+
*/
|
|
32
|
+
const MIN_ELIDABLE_OUTPUT = 200;
|
|
33
|
+
/** Serialized size of a tool result's output, for the "worth stubbing" test. */
|
|
34
|
+
function outputSize(output) {
|
|
35
|
+
if (output.type === "text" || output.type === "error-text")
|
|
36
|
+
return output.value.length;
|
|
37
|
+
if (output.type === "execution-denied")
|
|
38
|
+
return (output.reason ?? "").length;
|
|
39
|
+
try {
|
|
40
|
+
return JSON.stringify(output.value).length;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return Number.POSITIVE_INFINITY;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Shrink the window without shortening it: stub out the *payloads* of tool
|
|
48
|
+
* results the model has moved past, leaving every message, every tool call — and
|
|
49
|
+
* therefore every note the model wrote itself — exactly where it was.
|
|
50
|
+
*
|
|
51
|
+
* This is the other half of {@link windowMessages}, and it exists because the two
|
|
52
|
+
* things a rolling window holds have opposite value curves. A recipe's own
|
|
53
|
+
* reasoning and its tool-call *inputs* stay useful for as long as the run does; a
|
|
54
|
+
* tool *result* is a snapshot of a world that has since moved, and it is also
|
|
55
|
+
* where nearly all the tokens are. Dropping whole turns to bound context throws
|
|
56
|
+
* both away together, which is why a short window makes a model re-derive what it
|
|
57
|
+
* already knew.
|
|
58
|
+
*
|
|
59
|
+
* A result's output survives if any of:
|
|
60
|
+
*
|
|
61
|
+
* 1. it is within the last `keepRecent` assistant messages — the same unit
|
|
62
|
+
* `windowMessages` counts in, so the two knobs are commensurable;
|
|
63
|
+
* 2. **it is the newest result for its tool**, at any age. Tools may hold state
|
|
64
|
+
* about what they have already shown this chunk and answer a repeat with
|
|
65
|
+
* "unchanged since you last looked" — a real optimization that becomes a lie
|
|
66
|
+
* the moment the render it points at is gone. Keeping the newest per tool is
|
|
67
|
+
* what makes "your latest view" a thing the model can still see, and it is why
|
|
68
|
+
* `keepRecent` can be small;
|
|
69
|
+
* 3. it reports a failure or a denial — short, diagnostic, and losing *why* a
|
|
70
|
+
* call failed costs a retry to rediscover for no saving;
|
|
71
|
+
* 4. it is already small enough that stubbing it saves nothing.
|
|
72
|
+
*
|
|
73
|
+
* Idempotent: a stubbed part is small, so a later pass leaves it alone.
|
|
74
|
+
*
|
|
75
|
+
* `keepRecent` is normalized rather than trusted — see the note on `keep` below.
|
|
76
|
+
* Zero is a legal, meaningful value: no turn is recent, so rule 1 protects
|
|
77
|
+
* nothing and rules 2-4 carry the whole of what survives.
|
|
78
|
+
*/
|
|
79
|
+
export function elideToolOutputs(messages, keepRecent) {
|
|
80
|
+
/**
|
|
81
|
+
* Rule 1's width, normalized to a non-negative integer.
|
|
82
|
+
*
|
|
83
|
+
* Not defensive habit: `keepRecent` is a config value reaching a function whose
|
|
84
|
+
* arithmetic indexes an array with it, and every out-of-contract value used to
|
|
85
|
+
* land on the *same* wrong branch. `assistantIdx[len - 0]` is `undefined`, as is
|
|
86
|
+
* any negative or `NaN` index, and `i >= undefined` is false for every `i` — so
|
|
87
|
+
* `cutoff` silently meant "no turn is recent" instead of throwing or clamping.
|
|
88
|
+
* That happens to be right for 0 and wrong for everything else, which is the
|
|
89
|
+
* worst way for a guard to fail: correct until the day someone passes -1.
|
|
90
|
+
*
|
|
91
|
+
* So 0 now says it explicitly (see `cutoff`), and nothing else can reach it by
|
|
92
|
+
* accident. `NaN` is the one value with no reading at all, and it takes the
|
|
93
|
+
* conservative floor — still bounded by rules 2-4, which keep the newest result
|
|
94
|
+
* per tool, every failure, and everything already small. Both infinities keep
|
|
95
|
+
* the reading they plainly have: an unboundedly wide window keeps everything, a
|
|
96
|
+
* negative one keeps nothing.
|
|
97
|
+
*/
|
|
98
|
+
const keep = Number.isNaN(keepRecent)
|
|
99
|
+
? 0
|
|
100
|
+
: Math.max(0, Math.trunc(keepRecent));
|
|
101
|
+
// Where the detail window starts, counted in assistant messages so it lines up
|
|
102
|
+
// with `historyWindow`. Everything before it is a candidate for elision, so
|
|
103
|
+
// `messages.length` is the honest spelling of an empty window and 0 the honest
|
|
104
|
+
// spelling of a window covering everything (fewer assistant messages than the
|
|
105
|
+
// window ⇒ keep all).
|
|
106
|
+
const assistantIdx = [];
|
|
107
|
+
for (const [i, m] of messages.entries()) {
|
|
108
|
+
if (m.role === "assistant")
|
|
109
|
+
assistantIdx.push(i);
|
|
110
|
+
}
|
|
111
|
+
const cutoff = keep === 0
|
|
112
|
+
? messages.length
|
|
113
|
+
: assistantIdx.length <= keep
|
|
114
|
+
? 0
|
|
115
|
+
: assistantIdx[assistantIdx.length - keep];
|
|
116
|
+
if (cutoff === 0)
|
|
117
|
+
return messages;
|
|
118
|
+
// The newest result per tool, so rule 2 can be a lookup rather than a scan.
|
|
119
|
+
const newestPerTool = new Map();
|
|
120
|
+
for (const [i, message] of messages.entries()) {
|
|
121
|
+
if (message.role !== "tool")
|
|
122
|
+
continue;
|
|
123
|
+
for (const part of message.content) {
|
|
124
|
+
if (part.type === "tool-result")
|
|
125
|
+
newestPerTool.set(part.toolName, i);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
let changed = false;
|
|
129
|
+
const out = messages.map((message, i) => {
|
|
130
|
+
if (message.role !== "tool" || i >= cutoff)
|
|
131
|
+
return message;
|
|
132
|
+
let messageChanged = false;
|
|
133
|
+
const content = message.content.map((part) => {
|
|
134
|
+
if (part.type !== "tool-result")
|
|
135
|
+
return part;
|
|
136
|
+
if (newestPerTool.get(part.toolName) === i)
|
|
137
|
+
return part;
|
|
138
|
+
if (part.output.type === "error-text" ||
|
|
139
|
+
part.output.type === "error-json" ||
|
|
140
|
+
part.output.type === "execution-denied")
|
|
141
|
+
return part;
|
|
142
|
+
if (outputSize(part.output) < MIN_ELIDABLE_OUTPUT)
|
|
143
|
+
return part;
|
|
144
|
+
messageChanged = true;
|
|
145
|
+
return {
|
|
146
|
+
...part,
|
|
147
|
+
output: { type: "text", value: ELIDED_TOOL_OUTPUT }
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
if (!messageChanged)
|
|
151
|
+
return message;
|
|
152
|
+
changed = true;
|
|
153
|
+
return { ...message, content };
|
|
154
|
+
});
|
|
155
|
+
return changed ? out : messages;
|
|
156
|
+
}
|
|
157
|
+
/** Human-readable elapsed time for the metrics footer. */
|
|
158
|
+
function formatDuration(ms) {
|
|
159
|
+
const total = Math.round(ms / 1000);
|
|
160
|
+
const h = Math.floor(total / 3600);
|
|
161
|
+
const m = Math.floor((total % 3600) / 60);
|
|
162
|
+
const s = total % 60;
|
|
163
|
+
if (h > 0)
|
|
164
|
+
return `${h}h ${m}m ${s}s`;
|
|
165
|
+
if (m > 0)
|
|
166
|
+
return `${m}m ${s}s`;
|
|
167
|
+
return `${s}s`;
|
|
168
|
+
}
|
|
169
|
+
function metricsFooter(state, now) {
|
|
170
|
+
return (`\n\n---\nRan ${state.turns} model turn(s) across ${state.llmCalls} model ` +
|
|
171
|
+
`call(s) in ${formatDuration(now - state.startedAtMs)}.`);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Has this execution spent its whole-run budget? Turns and wall-clock are one
|
|
175
|
+
* predicate because they mean the same thing to the runner — the run is over and
|
|
176
|
+
* owes a report — and because checking only turns is what let a slow-turning
|
|
177
|
+
* recipe run for hours while its turn counter looked healthy.
|
|
178
|
+
*
|
|
179
|
+
* `startedAtMs` rides in the checkpoint, so the deadline survives chunk
|
|
180
|
+
* boundaries, step retries and isolate restarts without any storage of its own.
|
|
181
|
+
*/
|
|
182
|
+
function budgetSpent(state, deps) {
|
|
183
|
+
return (state.turns >= deps.limits.maxTurns ||
|
|
184
|
+
deps.now() - state.startedAtMs >= deps.limits.maxWallMs);
|
|
185
|
+
}
|
|
186
|
+
function completed(state, deps, text, modelId) {
|
|
187
|
+
const finalText = deps.reportMetrics
|
|
188
|
+
? text + metricsFooter(state, deps.now())
|
|
189
|
+
: text;
|
|
190
|
+
return {
|
|
191
|
+
outcome: {
|
|
192
|
+
done: true,
|
|
193
|
+
result: {
|
|
194
|
+
status: "completed",
|
|
195
|
+
resultParts: [{ kind: "text", text: finalText }],
|
|
196
|
+
modelId
|
|
197
|
+
},
|
|
198
|
+
progress: deps.progress
|
|
199
|
+
},
|
|
200
|
+
state
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Run one durable chunk. Returns a terminal result (natural completion, budget
|
|
205
|
+
* exhaustion, or exhausted models) or a `done: false` yield with the progress
|
|
206
|
+
* emitted this chunk. Throws only on a transient platform fault, so the Workflow
|
|
207
|
+
* step retries and resumes from the last checkpoint.
|
|
208
|
+
*/
|
|
209
|
+
export async function runResumableChunk(prev, deps) {
|
|
210
|
+
const state = prev ?? {
|
|
211
|
+
messages: [{ role: "user", content: deps.seedPrompt }],
|
|
212
|
+
turns: 0,
|
|
213
|
+
llmCalls: 0,
|
|
214
|
+
startedAtMs: deps.now()
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* Hand the chunk back with no terminal result. An abort takes this exit too —
|
|
218
|
+
* that is the whole point: nothing terminal is produced, so the facet caches
|
|
219
|
+
* nothing and no bogus failure can replay on a later retry.
|
|
220
|
+
*/
|
|
221
|
+
const yielded = () => ({
|
|
222
|
+
outcome: { done: false, progress: deps.progress },
|
|
223
|
+
state
|
|
224
|
+
});
|
|
225
|
+
// Already canceled before this chunk started: don't call a model at all.
|
|
226
|
+
if (deps.abortSignal?.aborted)
|
|
227
|
+
return yielded();
|
|
228
|
+
// The durable enforcement point for the whole-run budget: it reads persisted
|
|
229
|
+
// state before any model call, so every chunk re-checks it however the previous
|
|
230
|
+
// one ended. It also covers the retry that resumes from a checkpoint taken on
|
|
231
|
+
// the final allowed turn before the chunk returned — e.g. summarizeBudget's own
|
|
232
|
+
// call threw a transient fault and the Workflow step retried. The budget is
|
|
233
|
+
// already spent, so summarize now instead of running another unbudgeted,
|
|
234
|
+
// side-effecting turn (which `stopWhen`'s `Math.max(1, …)` would otherwise force).
|
|
235
|
+
if (budgetSpent(state, deps)) {
|
|
236
|
+
return summarizeBudget(state, deps);
|
|
237
|
+
}
|
|
238
|
+
const chunkStartMs = deps.now();
|
|
239
|
+
const onStepEnd = async (step) => {
|
|
240
|
+
state.turns += 1;
|
|
241
|
+
// Window first (drops whole turns at an assistant boundary), then elide what
|
|
242
|
+
// survived. Both before the checkpoint, so the durable `run_state` row
|
|
243
|
+
// shrinks with the context rather than tracking the untrimmed run.
|
|
244
|
+
state.messages = elideToolOutputs(windowMessages([...state.messages, ...step.response.messages], deps.historyWindow), deps.toolOutputWindow);
|
|
245
|
+
await deps.checkpoint(state);
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* Four boundaries, and only the first two are budgets. A chunk ends on
|
|
249
|
+
* whichever comes first; the run ends only on a budget.
|
|
250
|
+
*
|
|
251
|
+
* Rebuilt per attempt rather than once per chunk, and that is the whole point:
|
|
252
|
+
* `isStepCount` counts within one `generateText` call, so a `stopWhen` shared
|
|
253
|
+
* with the fallback would hand it the turn allowance the primary already spent.
|
|
254
|
+
* `onStepEnd` has moved `state.turns` by then, so recomputing here charges the
|
|
255
|
+
* fallback for what the run has actually used.
|
|
256
|
+
*/
|
|
257
|
+
const boundaries = () => [
|
|
258
|
+
// The turn budget — all of what is left of it. There is deliberately no
|
|
259
|
+
// per-chunk turn allowance: a turn count cannot bound a step's *duration*,
|
|
260
|
+
// which is the only thing the step timeout cares about, so the wall-clock
|
|
261
|
+
// predicate below owns that job alone.
|
|
262
|
+
isStepCount(stepAllowance(deps.limits.maxTurns, state.turns)),
|
|
263
|
+
// The run-wide deadline. Without it the entry guard would only observe the
|
|
264
|
+
// deadline at the next chunk boundary, up to `chunkSoftMs` past it.
|
|
265
|
+
() => deps.now() - state.startedAtMs >= deps.limits.maxWallMs,
|
|
266
|
+
// Not a budget: the platform's step timeout, which needs this chunk to
|
|
267
|
+
// checkpoint and hand back a fresh step long before ~10 minutes.
|
|
268
|
+
() => deps.now() - chunkStartMs >= deps.chunkSoftMs,
|
|
269
|
+
// Not a budget either: publish progress to the user promptly.
|
|
270
|
+
() => deps.progress.length > 0
|
|
271
|
+
];
|
|
272
|
+
const attempt = async (model, modelId) => {
|
|
273
|
+
state.llmCalls += 1;
|
|
274
|
+
let result;
|
|
275
|
+
try {
|
|
276
|
+
result = await generateText({
|
|
277
|
+
model: model(),
|
|
278
|
+
instructions: deps.system,
|
|
279
|
+
messages: state.messages,
|
|
280
|
+
tools: deps.tools,
|
|
281
|
+
stopWhen: boundaries(),
|
|
282
|
+
maxOutputTokens: deps.maxOutputTokens,
|
|
283
|
+
// Primary → fallback recovery is manual; SDK backoff would only add latency.
|
|
284
|
+
maxRetries: 0,
|
|
285
|
+
abortSignal: deps.abortSignal,
|
|
286
|
+
onStepEnd
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
// Check the signal before the error: an abort surfaces as a rejection, and
|
|
291
|
+
// reading it as bad model output would spend the fallback and cache a
|
|
292
|
+
// failure for work that was cancelled on purpose.
|
|
293
|
+
if (deps.abortSignal?.aborted)
|
|
294
|
+
return { kind: "aborted" };
|
|
295
|
+
return { kind: "failed", diagnostic: String(error), error, modelId };
|
|
296
|
+
}
|
|
297
|
+
if (deps.abortSignal?.aborted)
|
|
298
|
+
return { kind: "aborted" };
|
|
299
|
+
if (result.finishReason === "length") {
|
|
300
|
+
// Its own warning, not just a diagnostic string: hitting the output ceiling
|
|
301
|
+
// is a tuning signal about MAX_OUTPUT_TOKENS, distinct from the model
|
|
302
|
+
// producing bad output, and the two are indistinguishable once folded into
|
|
303
|
+
// the "recipe exhausted" message.
|
|
304
|
+
console.warn("[recipe-runner] model output truncated", {
|
|
305
|
+
model: modelId,
|
|
306
|
+
maxOutputTokens: deps.maxOutputTokens
|
|
307
|
+
});
|
|
308
|
+
return {
|
|
309
|
+
kind: "failed",
|
|
310
|
+
diagnostic: "truncated (finish_reason=length)",
|
|
311
|
+
modelId
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
if (result.finishReason === "stop") {
|
|
315
|
+
const text = result.text.trim();
|
|
316
|
+
return text === ""
|
|
317
|
+
? { kind: "failed", diagnostic: "empty final reply", modelId }
|
|
318
|
+
: { kind: "completed", text, modelId };
|
|
319
|
+
}
|
|
320
|
+
// Not a final answer (e.g. finish_reason=tool-calls): a stop condition fired
|
|
321
|
+
// mid-loop — the chunk yielded a durable boundary with more work to do.
|
|
322
|
+
return { kind: "yield" };
|
|
323
|
+
};
|
|
324
|
+
let a = await attempt(deps.models.primary, deps.models.primaryId());
|
|
325
|
+
if (a.kind === "aborted")
|
|
326
|
+
return yielded();
|
|
327
|
+
if (a.kind === "failed") {
|
|
328
|
+
console.warn("[recipe-runner] primary attempt failed, trying fallback", {
|
|
329
|
+
model: a.modelId,
|
|
330
|
+
diagnostic: a.diagnostic
|
|
331
|
+
});
|
|
332
|
+
const primaryFailure = a;
|
|
333
|
+
a = await attempt(deps.models.fallback, deps.models.fallbackId());
|
|
334
|
+
if (a.kind === "aborted")
|
|
335
|
+
return yielded();
|
|
336
|
+
if (a.kind === "failed") {
|
|
337
|
+
// Both attempts failed. A transient fault anywhere means a retry could
|
|
338
|
+
// succeed — throw it for the Workflow step (most recent first).
|
|
339
|
+
for (const failed of [a, primaryFailure]) {
|
|
340
|
+
if (failed.error !== undefined && isTransientAiError(failed.error)) {
|
|
341
|
+
throw failed.error;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
outcome: {
|
|
346
|
+
done: true,
|
|
347
|
+
result: {
|
|
348
|
+
status: "failed",
|
|
349
|
+
error: `recipe exhausted: primary (${primaryFailure.modelId}): ` +
|
|
350
|
+
`${primaryFailure.diagnostic}; fallback (${a.modelId}): ${a.diagnostic}`,
|
|
351
|
+
modelId: a.modelId
|
|
352
|
+
},
|
|
353
|
+
progress: deps.progress
|
|
354
|
+
},
|
|
355
|
+
state
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (a.kind === "completed")
|
|
360
|
+
return completed(state, deps, a.text, a.modelId);
|
|
361
|
+
// The chunk yielded. If the run budget is spent, force a final summary so the
|
|
362
|
+
// run still returns useful output; otherwise ask the Workflow for another chunk.
|
|
363
|
+
if (budgetSpent(state, deps)) {
|
|
364
|
+
return summarizeBudget(state, deps);
|
|
365
|
+
}
|
|
366
|
+
return yielded();
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* The run budget — turns or wall-clock — is exhausted mid-loop: run one final
|
|
370
|
+
* no-tools call asking the model to produce its answer/report from the work so
|
|
371
|
+
* far. Primary → fallback, same transient/deterministic split. This is what makes
|
|
372
|
+
* "uncapped but bounded" safe — the ceiling yields a report instead of a dropped
|
|
373
|
+
* run.
|
|
374
|
+
*
|
|
375
|
+
* The message deliberately does not name *which* budget ran out. The model can
|
|
376
|
+
* do nothing differently either way, and the one instruction that matters —
|
|
377
|
+
* report now, take no more actions — is the same.
|
|
378
|
+
*/
|
|
379
|
+
async function summarizeBudget(state, deps) {
|
|
380
|
+
const messages = [
|
|
381
|
+
...state.messages,
|
|
382
|
+
{
|
|
383
|
+
role: "user",
|
|
384
|
+
content: "You have reached your execution budget and can take no more actions. " +
|
|
385
|
+
"Write your final answer or report now, based on the work so far."
|
|
386
|
+
}
|
|
387
|
+
];
|
|
388
|
+
const summarize = async (model, modelId) => {
|
|
389
|
+
state.llmCalls += 1;
|
|
390
|
+
let result;
|
|
391
|
+
try {
|
|
392
|
+
result = await generateText({
|
|
393
|
+
model: model(),
|
|
394
|
+
instructions: deps.system,
|
|
395
|
+
messages,
|
|
396
|
+
stopWhen: isStepCount(1),
|
|
397
|
+
maxOutputTokens: deps.maxOutputTokens,
|
|
398
|
+
maxRetries: 0,
|
|
399
|
+
abortSignal: deps.abortSignal
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
if (deps.abortSignal?.aborted)
|
|
404
|
+
return { kind: "aborted" };
|
|
405
|
+
return { kind: "failed", diagnostic: String(error), error, modelId };
|
|
406
|
+
}
|
|
407
|
+
if (deps.abortSignal?.aborted)
|
|
408
|
+
return { kind: "aborted" };
|
|
409
|
+
const text = result.text.trim();
|
|
410
|
+
return text === ""
|
|
411
|
+
? { kind: "failed", diagnostic: "empty summary", modelId }
|
|
412
|
+
: { kind: "completed", text, modelId };
|
|
413
|
+
};
|
|
414
|
+
// An abort yields with no terminal result, exactly as in the main loop: the
|
|
415
|
+
// budget summary is output, and cancelled work publishes none.
|
|
416
|
+
const yielded = () => ({
|
|
417
|
+
outcome: { done: false, progress: deps.progress },
|
|
418
|
+
state
|
|
419
|
+
});
|
|
420
|
+
let a = await summarize(deps.models.primary, deps.models.primaryId());
|
|
421
|
+
if (a.kind === "aborted")
|
|
422
|
+
return yielded();
|
|
423
|
+
if (a.kind === "failed") {
|
|
424
|
+
const primaryFailure = a;
|
|
425
|
+
a = await summarize(deps.models.fallback, deps.models.fallbackId());
|
|
426
|
+
if (a.kind === "aborted")
|
|
427
|
+
return yielded();
|
|
428
|
+
if (a.kind === "failed") {
|
|
429
|
+
for (const failed of [a, primaryFailure]) {
|
|
430
|
+
if (failed.error !== undefined && isTransientAiError(failed.error)) {
|
|
431
|
+
throw failed.error;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
// Even the summary failed: return a plain budget-exhausted notice.
|
|
435
|
+
const text = "Reached the execution budget without producing a final report.";
|
|
436
|
+
return completed(state, deps, text, a.modelId);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return a.kind === "completed"
|
|
440
|
+
? completed(state, deps, a.text, a.modelId)
|
|
441
|
+
: completed(state, deps, "Reached the execution budget.", deps.models.fallbackId());
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Run one recipe execution to a terminal result, driving {@link runResumableChunk}
|
|
445
|
+
* chunk by chunk in memory. A run that fits its budget finishes in one chunk;
|
|
446
|
+
* otherwise it loops until the budget yields a summary. Used by tests and any
|
|
447
|
+
* caller wanting the whole outcome; the facet drives chunks durably instead, for
|
|
448
|
+
* crash-safety across the Workflow.
|
|
449
|
+
*
|
|
450
|
+
* Throws only on a transient platform fault (as {@link runResumableChunk} does).
|
|
451
|
+
*/
|
|
452
|
+
export async function runRecipeExecution(request, deps) {
|
|
453
|
+
if (request.prompt.trim() === "") {
|
|
454
|
+
return { status: "failed", error: "empty subtask prompt", modelId: null };
|
|
455
|
+
}
|
|
456
|
+
const recipe = validateRecipe(request.recipe, deps.policy);
|
|
457
|
+
const { system, prompt } = renderSubagentPrompt({ ...request, recipe });
|
|
458
|
+
const now = deps.now ?? Date.now;
|
|
459
|
+
let state = null;
|
|
460
|
+
// A chunk always advances ≥1 turn unless it completes, so `maxTurns` chunks is
|
|
461
|
+
// the ceiling and this can never spin. Turn-derived on purpose: `maxWallMs` and
|
|
462
|
+
// `chunkSoftMs` only ever end a run *sooner*, so neither can loosen the bound.
|
|
463
|
+
const maxChunks = recipe.limits.maxTurns + 2;
|
|
464
|
+
for (let chunk = 0; chunk < maxChunks; chunk++) {
|
|
465
|
+
const chunkDeps = {
|
|
466
|
+
system,
|
|
467
|
+
seedPrompt: prompt,
|
|
468
|
+
models: deps.models,
|
|
469
|
+
tools: deps.tools,
|
|
470
|
+
limits: recipe.limits,
|
|
471
|
+
chunkSoftMs: CHUNK_SOFT_MS,
|
|
472
|
+
historyWindow: recipe.historyWindow,
|
|
473
|
+
toolOutputWindow: deps.toolOutputWindow,
|
|
474
|
+
reportMetrics: recipe.reportMetrics,
|
|
475
|
+
maxOutputTokens: deps.maxOutputTokens,
|
|
476
|
+
now,
|
|
477
|
+
progress: [],
|
|
478
|
+
checkpoint: () => { }
|
|
479
|
+
};
|
|
480
|
+
const { outcome, state: next } = await runResumableChunk(state, chunkDeps);
|
|
481
|
+
if (outcome.done)
|
|
482
|
+
return outcome.result;
|
|
483
|
+
state = next;
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
status: "failed",
|
|
487
|
+
error: `recipe did not terminate within ${maxChunks} chunks`,
|
|
488
|
+
modelId: null
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/subagent/run.ts"],"names":[],"mappings":"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"}
|