@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,147 @@
|
|
|
1
|
+
import { Agent } from "agents";
|
|
2
|
+
import type { ModelPair, ModelRuntime } from "../agent/model.js";
|
|
3
|
+
import type { ToolFamilyBuilder } from "../contract/plugin.js";
|
|
4
|
+
import { type RecipePolicy } from "../contract/validation.js";
|
|
5
|
+
import type { RecipeChunkResult, RecipeExecutionRequest, SubtaskId, SubtaskRuntime } from "../subtasks/types.js";
|
|
6
|
+
import { type SubtaskTypeRegistry } from "../subtasks/subtask-types.js";
|
|
7
|
+
import { type WorkspaceBacking } from "./workspace.js";
|
|
8
|
+
/**
|
|
9
|
+
* Everything the facet needs that comes from the *host*: resolved config, the
|
|
10
|
+
* capability policy, the installed subtask types and tool families, the model
|
|
11
|
+
* runtime, and a workspace backend.
|
|
12
|
+
*
|
|
13
|
+
* A Durable Object class is constructed by the runtime, so it cannot take
|
|
14
|
+
* constructor arguments — which is exactly the problem a library version of this
|
|
15
|
+
* facet has to solve. {@link RecipeSubagentBase} takes it from an abstract
|
|
16
|
+
* method instead: the consumer subclasses once, and everything host-specific
|
|
17
|
+
* arrives through that one seam.
|
|
18
|
+
*/
|
|
19
|
+
export interface SubagentRuntime {
|
|
20
|
+
policy: RecipePolicy;
|
|
21
|
+
types: SubtaskTypeRegistry;
|
|
22
|
+
models: ModelRuntime;
|
|
23
|
+
toolFamilies: ReadonlyMap<string, ToolFamilyBuilder>;
|
|
24
|
+
/** `CoreConfig.toolOutputWindow`. */
|
|
25
|
+
toolOutputWindow: number;
|
|
26
|
+
/** `CoreConfig.model.maxOutputTokens`. */
|
|
27
|
+
maxOutputTokens: number;
|
|
28
|
+
/**
|
|
29
|
+
* Build the durable file store over this facet's own SQLite.
|
|
30
|
+
*
|
|
31
|
+
* Supplied by the host because the backend is a plugin's: core declares the
|
|
32
|
+
* {@link WorkspaceBacking} shape and the caps, `@loopingai/plugins/workspace`
|
|
33
|
+
* supplies the `@cloudflare/shell` implementation, and an agent that never
|
|
34
|
+
* delegates file work installs neither.
|
|
35
|
+
*/
|
|
36
|
+
workspaceBacking(sql: SqlStorage, name: () => string | undefined): WorkspaceBacking;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Message prefix of the error thrown when a child that already holds a cached
|
|
40
|
+
* terminal result receives a *different* request. Custom error classes don't
|
|
41
|
+
* survive DO RPC, so this prefix is the cross-boundary contract: it signals a
|
|
42
|
+
* parent lifecycle bug — stale children must be deleted before a genuinely new
|
|
43
|
+
* execution — and a Workflow retry after the parent's cleanup will succeed.
|
|
44
|
+
*/
|
|
45
|
+
export declare const FINGERPRINT_MISMATCH = "recipe-subagent: request fingerprint mismatch";
|
|
46
|
+
/** Deterministic managed-child name for one Subtask execution (shared with C3). */
|
|
47
|
+
export declare function subagentName(taskId: string, subtaskId: SubtaskId): string;
|
|
48
|
+
/**
|
|
49
|
+
* `RecipeSubagent` — the isolated, stateless managed child that executes one
|
|
50
|
+
* Subtask under a resolved Recipe. Created as an Agents SDK sub-agent (facet)
|
|
51
|
+
* beneath the caller's `ReactiveAgent`, so it needs no wrangler Durable Object
|
|
52
|
+
* binding and no `new_sqlite_classes` entry; it must only be exported from the
|
|
53
|
+
* worker entry (`src/index.ts`) so `ctx.exports` can resolve it by class name.
|
|
54
|
+
*
|
|
55
|
+
* It never constructs a Session, never reads parent history beyond the
|
|
56
|
+
* references supplied on its request, never uses recall or durable memory, and
|
|
57
|
+
* never resolves a Recipe itself — it defensively re-validates the resolved
|
|
58
|
+
* Recipe the parent sends and accepts no configuration beyond it.
|
|
59
|
+
*
|
|
60
|
+
* Retry safety: the child persists at most one terminal result in its own
|
|
61
|
+
* SQLite, keyed by the deterministic request fingerprint, plus the rolling
|
|
62
|
+
* `run_state` of an in-progress multi-chunk run. A retry with the same
|
|
63
|
+
* fingerprint replays the terminal result or resumes the run without repeating
|
|
64
|
+
* completed work; a different request for the same child name is rejected
|
|
65
|
+
* ({@link FINGERPRINT_MISMATCH}). Transient platform faults throw and cache
|
|
66
|
+
* nothing, so the enclosing Workflow step can retry. The parent deletes the child
|
|
67
|
+
* (`deleteSubAgent`) only after its durable copy of the result succeeds, which
|
|
68
|
+
* wipes this storage — the workspace and run state included.
|
|
69
|
+
*
|
|
70
|
+
* Not "stateless" like the single-shot original: it owns per-execution durable
|
|
71
|
+
* state (the workspace and the run checkpoint), scoped to one execution and swept
|
|
72
|
+
* with the child.
|
|
73
|
+
*/
|
|
74
|
+
export declare abstract class RecipeSubagentBase<TEnv extends Cloudflare.Env = Cloudflare.Env> extends Agent<TEnv> {
|
|
75
|
+
/**
|
|
76
|
+
* Supply the host runtime. Called per RPC, not memoized here — an
|
|
77
|
+
* implementation that builds something expensive should memoize its own.
|
|
78
|
+
*
|
|
79
|
+
* ```ts
|
|
80
|
+
* export class RecipeSubagent extends RecipeSubagentBase<Env> {
|
|
81
|
+
* protected subagentRuntime() {
|
|
82
|
+
* return (this._rt ??= buildSubagentRuntime(this.env));
|
|
83
|
+
* }
|
|
84
|
+
* }
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
protected abstract subagentRuntime(): SubagentRuntime;
|
|
88
|
+
/**
|
|
89
|
+
* Test-only `ModelPair` injection (a field, so never on the RPC stub).
|
|
90
|
+
* A whole pair — rather than model instances — so error-path tests can throw
|
|
91
|
+
* synchronously from the pair's factories, the repo convention (a rejecting
|
|
92
|
+
* `doGenerate` inside `generateText` leaks an unhandled rejection through
|
|
93
|
+
* the AI SDK telemetry span that workerd flags as a failure).
|
|
94
|
+
*/
|
|
95
|
+
modelsOverride?: ModelPair;
|
|
96
|
+
private _workspace?;
|
|
97
|
+
/**
|
|
98
|
+
* The chunk currently executing here, if any. In memory only — it exists to be
|
|
99
|
+
* interrupted mid-call, and an isolate that lost it has no in-flight call left
|
|
100
|
+
* to interrupt. See {@link abortRun}.
|
|
101
|
+
*/
|
|
102
|
+
private inflight?;
|
|
103
|
+
onStart(): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Idempotent schema bootstrap. Also called lazily from the RPCs so
|
|
106
|
+
* `runInDurableObject`-style tests reach ready tables without RPC dispatch
|
|
107
|
+
* (mirroring how `AgentDB` migrates on construction).
|
|
108
|
+
*/
|
|
109
|
+
private ensureTables;
|
|
110
|
+
/** The recipe's durable workspace, backed by this facet's own SQLite storage. */
|
|
111
|
+
private workspace;
|
|
112
|
+
/**
|
|
113
|
+
* Execute one durable chunk of a Subtask under the parent's resolved Recipe.
|
|
114
|
+
*
|
|
115
|
+
* A terminal outcome (completed / failed) is cached and replayed on retry. A
|
|
116
|
+
* mid-run chunk persists its rolling state to `run_state` and returns a
|
|
117
|
+
* `done: false` yield for the Workflow to run another chunk. `chunk` is a
|
|
118
|
+
* separate argument — never part of `request` — so every chunk fingerprints
|
|
119
|
+
* identically and the cache/resume keys line up. Only transient platform faults
|
|
120
|
+
* throw (nothing cached), so a Workflow retry resumes from the last checkpoint.
|
|
121
|
+
*/
|
|
122
|
+
executeChunk(request: RecipeExecutionRequest, _chunk: number, runtime?: SubtaskRuntime): Promise<RecipeChunkResult>;
|
|
123
|
+
/**
|
|
124
|
+
* Interrupt the chunk running here right now, so a cancellation lands on the
|
|
125
|
+
* current model call instead of at the next chunk boundary (up to `chunkSoftMs`
|
|
126
|
+
* later — minutes, for a long recipe). Returns whether there was one to stop.
|
|
127
|
+
*
|
|
128
|
+
* Distinct from {@link abortExecution}, which releases *external* state after
|
|
129
|
+
* the fact; this only stops local work. An aborted run yields rather than
|
|
130
|
+
* producing a terminal result, so nothing is cached and the parent resolves the
|
|
131
|
+
* row itself. Reaching a facet mid-`executeChunk` works because it is awaiting
|
|
132
|
+
* a model `fetch` at the time, which does not hold the input gate closed.
|
|
133
|
+
*/
|
|
134
|
+
abortRun(): Promise<boolean>;
|
|
135
|
+
/**
|
|
136
|
+
* Best-effort cleanup on cancellation: rebuild the recipe's tool families and
|
|
137
|
+
* run their `abort` hooks (e.g. release an external resource recorded in the
|
|
138
|
+
* workspace session file). Reconstructible from the workspace, so it is safe on a fresh
|
|
139
|
+
* isolate. The parent supplies the validated tool families it resolved.
|
|
140
|
+
*/
|
|
141
|
+
abortExecution(toolFamilies: string[]): Promise<void>;
|
|
142
|
+
/** Persist a terminal result to the cache and return it as a done chunk. */
|
|
143
|
+
private cacheTerminal;
|
|
144
|
+
/** Persist the rolling run state (called after every model turn). */
|
|
145
|
+
private saveRunState;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/subagent/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAG/B,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAEV,iBAAiB,EACjB,sBAAsB,EAEtB,SAAS,EACT,cAAc,EACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAI5E;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IACrD,qCAAqC;IACrC,gBAAgB,EAAE,MAAM,CAAC;IACzB,0CAA0C;IAC1C,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;;;OAOG;IACH,gBAAgB,CACd,GAAG,EAAE,UAAU,EACf,IAAI,EAAE,MAAM,MAAM,GAAG,SAAS,GAC7B,gBAAgB,CAAC;CACrB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,kDACgB,CAAC;AAElD,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,CAEzE;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,8BAAsB,kBAAkB,CACtC,IAAI,SAAS,UAAU,CAAC,GAAG,GAAG,UAAU,CAAC,GAAG,CAC5C,SAAQ,KAAK,CAAC,IAAI,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,QAAQ,CAAC,eAAe,IAAI,eAAe;IAErD;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,SAAS,CAAC;IAE3B,OAAO,CAAC,UAAU,CAAC,CAAmB;IAEtC;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,CAAkB;IAE7B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAmBpB,iFAAiF;IACjF,OAAO,CAAC,SAAS;IAOjB;;;;;;;;;OASG;IACG,YAAY,CAChB,OAAO,EAAE,sBAAsB,EAC/B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,iBAAiB,CAAC;IAmH7B;;;;;;;;;;OAUG;IACG,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC;IAMlC;;;;;OAKG;IACG,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAa3D,4EAA4E;IAC5E,OAAO,CAAC,aAAa;IAgBrB,qEAAqE;IACrE,OAAO,CAAC,YAAY;CAUrB"}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { Agent } from "agents";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { CHUNK_SOFT_MS } from "../platform.js";
|
|
4
|
+
import { buildRecipeTools } from "../runtime/tool-families.js";
|
|
5
|
+
import { RecipeValidationError, validateRecipe } from "../contract/validation.js";
|
|
6
|
+
import { SubtaskParamsError } from "../subtasks/subtask-types.js";
|
|
7
|
+
import { renderSubagentPrompt } from "./prompt.js";
|
|
8
|
+
import { makeWorkspaceHandle } from "./workspace.js";
|
|
9
|
+
import { fingerprintRequest } from "./fingerprint.js";
|
|
10
|
+
import { runResumableChunk } from "./run.js";
|
|
11
|
+
/**
|
|
12
|
+
* Message prefix of the error thrown when a child that already holds a cached
|
|
13
|
+
* terminal result receives a *different* request. Custom error classes don't
|
|
14
|
+
* survive DO RPC, so this prefix is the cross-boundary contract: it signals a
|
|
15
|
+
* parent lifecycle bug — stale children must be deleted before a genuinely new
|
|
16
|
+
* execution — and a Workflow retry after the parent's cleanup will succeed.
|
|
17
|
+
*/
|
|
18
|
+
export const FINGERPRINT_MISMATCH = "recipe-subagent: request fingerprint mismatch";
|
|
19
|
+
/** Deterministic managed-child name for one Subtask execution (shared with C3). */
|
|
20
|
+
export function subagentName(taskId, subtaskId) {
|
|
21
|
+
return `subtask:${taskId}:${subtaskId}`;
|
|
22
|
+
}
|
|
23
|
+
/** Zod mirror of {@link RecipeExecutionResult} for parsing the cached JSON. */
|
|
24
|
+
const cachedResultSchema = z.discriminatedUnion("status", [
|
|
25
|
+
z.object({
|
|
26
|
+
status: z.literal("completed"),
|
|
27
|
+
resultParts: z
|
|
28
|
+
.array(z.object({ kind: z.literal("text"), text: z.string() }))
|
|
29
|
+
.min(1),
|
|
30
|
+
modelId: z.string()
|
|
31
|
+
}),
|
|
32
|
+
z.object({
|
|
33
|
+
status: z.literal("failed"),
|
|
34
|
+
error: z.string(),
|
|
35
|
+
modelId: z.string().nullable()
|
|
36
|
+
})
|
|
37
|
+
]);
|
|
38
|
+
/**
|
|
39
|
+
* `RecipeSubagent` — the isolated, stateless managed child that executes one
|
|
40
|
+
* Subtask under a resolved Recipe. Created as an Agents SDK sub-agent (facet)
|
|
41
|
+
* beneath the caller's `ReactiveAgent`, so it needs no wrangler Durable Object
|
|
42
|
+
* binding and no `new_sqlite_classes` entry; it must only be exported from the
|
|
43
|
+
* worker entry (`src/index.ts`) so `ctx.exports` can resolve it by class name.
|
|
44
|
+
*
|
|
45
|
+
* It never constructs a Session, never reads parent history beyond the
|
|
46
|
+
* references supplied on its request, never uses recall or durable memory, and
|
|
47
|
+
* never resolves a Recipe itself — it defensively re-validates the resolved
|
|
48
|
+
* Recipe the parent sends and accepts no configuration beyond it.
|
|
49
|
+
*
|
|
50
|
+
* Retry safety: the child persists at most one terminal result in its own
|
|
51
|
+
* SQLite, keyed by the deterministic request fingerprint, plus the rolling
|
|
52
|
+
* `run_state` of an in-progress multi-chunk run. A retry with the same
|
|
53
|
+
* fingerprint replays the terminal result or resumes the run without repeating
|
|
54
|
+
* completed work; a different request for the same child name is rejected
|
|
55
|
+
* ({@link FINGERPRINT_MISMATCH}). Transient platform faults throw and cache
|
|
56
|
+
* nothing, so the enclosing Workflow step can retry. The parent deletes the child
|
|
57
|
+
* (`deleteSubAgent`) only after its durable copy of the result succeeds, which
|
|
58
|
+
* wipes this storage — the workspace and run state included.
|
|
59
|
+
*
|
|
60
|
+
* Not "stateless" like the single-shot original: it owns per-execution durable
|
|
61
|
+
* state (the workspace and the run checkpoint), scoped to one execution and swept
|
|
62
|
+
* with the child.
|
|
63
|
+
*/
|
|
64
|
+
export class RecipeSubagentBase extends Agent {
|
|
65
|
+
/**
|
|
66
|
+
* Test-only `ModelPair` injection (a field, so never on the RPC stub).
|
|
67
|
+
* A whole pair — rather than model instances — so error-path tests can throw
|
|
68
|
+
* synchronously from the pair's factories, the repo convention (a rejecting
|
|
69
|
+
* `doGenerate` inside `generateText` leaks an unhandled rejection through
|
|
70
|
+
* the AI SDK telemetry span that workerd flags as a failure).
|
|
71
|
+
*/
|
|
72
|
+
modelsOverride;
|
|
73
|
+
_workspace;
|
|
74
|
+
/**
|
|
75
|
+
* The chunk currently executing here, if any. In memory only — it exists to be
|
|
76
|
+
* interrupted mid-call, and an isolate that lost it has no in-flight call left
|
|
77
|
+
* to interrupt. See {@link abortRun}.
|
|
78
|
+
*/
|
|
79
|
+
inflight;
|
|
80
|
+
async onStart() {
|
|
81
|
+
this.ensureTables();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Idempotent schema bootstrap. Also called lazily from the RPCs so
|
|
85
|
+
* `runInDurableObject`-style tests reach ready tables without RPC dispatch
|
|
86
|
+
* (mirroring how `AgentDB` migrates on construction).
|
|
87
|
+
*/
|
|
88
|
+
ensureTables() {
|
|
89
|
+
this.sql `
|
|
90
|
+
CREATE TABLE IF NOT EXISTS execution_cache (
|
|
91
|
+
slot INTEGER PRIMARY KEY CHECK (slot = 1),
|
|
92
|
+
fingerprint TEXT NOT NULL,
|
|
93
|
+
result_json TEXT NOT NULL,
|
|
94
|
+
created_at INTEGER NOT NULL
|
|
95
|
+
)
|
|
96
|
+
`;
|
|
97
|
+
this.sql `
|
|
98
|
+
CREATE TABLE IF NOT EXISTS run_state (
|
|
99
|
+
slot INTEGER PRIMARY KEY CHECK (slot = 1),
|
|
100
|
+
fingerprint TEXT NOT NULL,
|
|
101
|
+
state_json TEXT NOT NULL,
|
|
102
|
+
updated_at INTEGER NOT NULL
|
|
103
|
+
)
|
|
104
|
+
`;
|
|
105
|
+
}
|
|
106
|
+
/** The recipe's durable workspace, backed by this facet's own SQLite storage. */
|
|
107
|
+
workspace() {
|
|
108
|
+
return (this._workspace ??= this.subagentRuntime().workspaceBacking(this.ctx.storage.sql, () => this.name));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Execute one durable chunk of a Subtask under the parent's resolved Recipe.
|
|
112
|
+
*
|
|
113
|
+
* A terminal outcome (completed / failed) is cached and replayed on retry. A
|
|
114
|
+
* mid-run chunk persists its rolling state to `run_state` and returns a
|
|
115
|
+
* `done: false` yield for the Workflow to run another chunk. `chunk` is a
|
|
116
|
+
* separate argument — never part of `request` — so every chunk fingerprints
|
|
117
|
+
* identically and the cache/resume keys line up. Only transient platform faults
|
|
118
|
+
* throw (nothing cached), so a Workflow retry resumes from the last checkpoint.
|
|
119
|
+
*/
|
|
120
|
+
async executeChunk(request, _chunk, runtime = {}) {
|
|
121
|
+
this.ensureTables();
|
|
122
|
+
const rt = this.subagentRuntime();
|
|
123
|
+
const fingerprint = await fingerprintRequest(request);
|
|
124
|
+
// A terminal result already exists → replay it (idempotent retry).
|
|
125
|
+
const cached = this.sql `
|
|
126
|
+
SELECT fingerprint, result_json FROM execution_cache WHERE slot = 1
|
|
127
|
+
`[0];
|
|
128
|
+
if (cached) {
|
|
129
|
+
if (cached.fingerprint !== fingerprint)
|
|
130
|
+
throw mismatch("terminal");
|
|
131
|
+
return {
|
|
132
|
+
done: true,
|
|
133
|
+
result: cachedResultSchema.parse(JSON.parse(cached.result_json)),
|
|
134
|
+
progress: []
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// Validate the recipe up front; an unusable recipe (disabled, or with no
|
|
138
|
+
// soul) and an empty prompt are deterministic, cacheable terminal failures
|
|
139
|
+
// with no model call.
|
|
140
|
+
let recipe;
|
|
141
|
+
try {
|
|
142
|
+
recipe = validateRecipe(request.recipe, rt.policy);
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
if (!(error instanceof RecipeValidationError))
|
|
146
|
+
throw error;
|
|
147
|
+
return this.cacheTerminal(fingerprint, {
|
|
148
|
+
status: "failed",
|
|
149
|
+
error: error.message,
|
|
150
|
+
modelId: null
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
if (request.prompt.trim() === "") {
|
|
154
|
+
return this.cacheTerminal(fingerprint, {
|
|
155
|
+
status: "failed",
|
|
156
|
+
error: "empty subtask prompt",
|
|
157
|
+
modelId: null
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
// Re-check the type's param contract, the same defensive posture as
|
|
161
|
+
// `validateRecipe`: a play with no scorecard cannot succeed, and failing
|
|
162
|
+
// here costs no model call and gives the parent a real diagnostic.
|
|
163
|
+
try {
|
|
164
|
+
rt.types.validateParams(request.type, request.params);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
if (!(error instanceof SubtaskParamsError))
|
|
168
|
+
throw error;
|
|
169
|
+
return this.cacheTerminal(fingerprint, {
|
|
170
|
+
status: "failed",
|
|
171
|
+
error: error.message,
|
|
172
|
+
modelId: null
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
// Resume an in-progress run, guarding against a stale child holding a
|
|
176
|
+
// *different* run (the same reuse hazard the terminal cache guards).
|
|
177
|
+
const saved = this.sql `
|
|
178
|
+
SELECT fingerprint, state_json FROM run_state WHERE slot = 1
|
|
179
|
+
`[0];
|
|
180
|
+
if (saved && saved.fingerprint !== fingerprint)
|
|
181
|
+
throw mismatch("in-progress");
|
|
182
|
+
const prev = saved
|
|
183
|
+
? JSON.parse(saved.state_json)
|
|
184
|
+
: null;
|
|
185
|
+
const models = this.modelsOverride ??
|
|
186
|
+
rt.models.createModelPair({
|
|
187
|
+
primaryModelId: recipe.primaryModelId,
|
|
188
|
+
fallbackModelId: recipe.fallbackModelId,
|
|
189
|
+
// AI Gateway correlation: tie this child's model calls to its Subtask.
|
|
190
|
+
metadata: { taskId: request.taskId, subtaskId: request.subtaskId }
|
|
191
|
+
});
|
|
192
|
+
const workspace = makeWorkspaceHandle(this.workspace());
|
|
193
|
+
const progress = [];
|
|
194
|
+
const { tools } = buildRecipeTools(recipe.toolFamilies, rt.toolFamilies, {
|
|
195
|
+
workspace,
|
|
196
|
+
emitProgress: (event) => progress.push(event),
|
|
197
|
+
params: request.params,
|
|
198
|
+
runtime
|
|
199
|
+
});
|
|
200
|
+
const { system, prompt } = renderSubagentPrompt({ ...request, recipe });
|
|
201
|
+
const controller = new AbortController();
|
|
202
|
+
this.inflight = controller;
|
|
203
|
+
let outcome, state;
|
|
204
|
+
try {
|
|
205
|
+
({ outcome, state } = await runResumableChunk(prev, {
|
|
206
|
+
system,
|
|
207
|
+
seedPrompt: prompt,
|
|
208
|
+
models,
|
|
209
|
+
tools,
|
|
210
|
+
limits: recipe.limits,
|
|
211
|
+
chunkSoftMs: CHUNK_SOFT_MS,
|
|
212
|
+
historyWindow: recipe.historyWindow,
|
|
213
|
+
toolOutputWindow: rt.toolOutputWindow,
|
|
214
|
+
reportMetrics: recipe.reportMetrics,
|
|
215
|
+
maxOutputTokens: rt.maxOutputTokens,
|
|
216
|
+
now: () => Date.now(),
|
|
217
|
+
progress,
|
|
218
|
+
checkpoint: (s) => this.saveRunState(fingerprint, s),
|
|
219
|
+
abortSignal: controller.signal
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
this.inflight = undefined;
|
|
224
|
+
}
|
|
225
|
+
// The per-step checkpoint already ran; persist the final state too so a chunk
|
|
226
|
+
// that yielded without a completed step still advances durably.
|
|
227
|
+
this.saveRunState(fingerprint, state);
|
|
228
|
+
if (outcome.done) {
|
|
229
|
+
return this.cacheTerminal(fingerprint, outcome.result, outcome.progress);
|
|
230
|
+
}
|
|
231
|
+
return { done: false, progress: outcome.progress };
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Interrupt the chunk running here right now, so a cancellation lands on the
|
|
235
|
+
* current model call instead of at the next chunk boundary (up to `chunkSoftMs`
|
|
236
|
+
* later — minutes, for a long recipe). Returns whether there was one to stop.
|
|
237
|
+
*
|
|
238
|
+
* Distinct from {@link abortExecution}, which releases *external* state after
|
|
239
|
+
* the fact; this only stops local work. An aborted run yields rather than
|
|
240
|
+
* producing a terminal result, so nothing is cached and the parent resolves the
|
|
241
|
+
* row itself. Reaching a facet mid-`executeChunk` works because it is awaiting
|
|
242
|
+
* a model `fetch` at the time, which does not hold the input gate closed.
|
|
243
|
+
*/
|
|
244
|
+
async abortRun() {
|
|
245
|
+
if (!this.inflight)
|
|
246
|
+
return false;
|
|
247
|
+
this.inflight.abort();
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Best-effort cleanup on cancellation: rebuild the recipe's tool families and
|
|
252
|
+
* run their `abort` hooks (e.g. release an external resource recorded in the
|
|
253
|
+
* workspace session file). Reconstructible from the workspace, so it is safe on a fresh
|
|
254
|
+
* isolate. The parent supplies the validated tool families it resolved.
|
|
255
|
+
*/
|
|
256
|
+
async abortExecution(toolFamilies) {
|
|
257
|
+
this.ensureTables();
|
|
258
|
+
const rt = this.subagentRuntime();
|
|
259
|
+
const ctx = {
|
|
260
|
+
workspace: makeWorkspaceHandle(this.workspace()),
|
|
261
|
+
emitProgress: () => { },
|
|
262
|
+
params: {},
|
|
263
|
+
runtime: {}
|
|
264
|
+
};
|
|
265
|
+
const { abort } = buildRecipeTools(toolFamilies, rt.toolFamilies, ctx);
|
|
266
|
+
if (abort)
|
|
267
|
+
await abort(ctx);
|
|
268
|
+
}
|
|
269
|
+
/** Persist a terminal result to the cache and return it as a done chunk. */
|
|
270
|
+
cacheTerminal(fingerprint, result, progress = []) {
|
|
271
|
+
this.sql `
|
|
272
|
+
INSERT INTO execution_cache (slot, fingerprint, result_json, created_at)
|
|
273
|
+
VALUES (1, ${fingerprint}, ${JSON.stringify(result)}, ${Date.now()})
|
|
274
|
+
ON CONFLICT (slot) DO UPDATE SET
|
|
275
|
+
fingerprint = excluded.fingerprint,
|
|
276
|
+
result_json = excluded.result_json,
|
|
277
|
+
created_at = excluded.created_at
|
|
278
|
+
`;
|
|
279
|
+
return { done: true, result, progress };
|
|
280
|
+
}
|
|
281
|
+
/** Persist the rolling run state (called after every model turn). */
|
|
282
|
+
saveRunState(fingerprint, state) {
|
|
283
|
+
this.sql `
|
|
284
|
+
INSERT INTO run_state (slot, fingerprint, state_json, updated_at)
|
|
285
|
+
VALUES (1, ${fingerprint}, ${JSON.stringify(state)}, ${Date.now()})
|
|
286
|
+
ON CONFLICT (slot) DO UPDATE SET
|
|
287
|
+
fingerprint = excluded.fingerprint,
|
|
288
|
+
state_json = excluded.state_json,
|
|
289
|
+
updated_at = excluded.updated_at
|
|
290
|
+
`;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/** The cross-RPC stale-child error (see {@link FINGERPRINT_MISMATCH}). */
|
|
294
|
+
function mismatch(phase) {
|
|
295
|
+
return new Error(`${FINGERPRINT_MISMATCH}: this child already holds a ${phase} state for a ` +
|
|
296
|
+
"different request; the parent must delete a stale child before starting a " +
|
|
297
|
+
"genuinely new execution");
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/subagent/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { RecipeExecutionRequest } from "../subtasks/types.js";
|
|
2
|
+
import type { ValidatedRecipe } from "../contract/recipe.js";
|
|
3
|
+
/**
|
|
4
|
+
* Deterministic rendering of one subagent invocation. Pure — no model, no
|
|
5
|
+
* Session, no lookups: everything comes verbatim from the request. The four
|
|
6
|
+
* sections stay clearly separated and labeled so tests (and the model) can tell
|
|
7
|
+
* them apart: the execution's budget, the main-agent instruction, the verbatim
|
|
8
|
+
* conversation reference snapshots, and generated dependency output — which is
|
|
9
|
+
* explicitly marked as generated and never presented as conversation evidence.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* A request whose Recipe has been through `validateRecipe`, so its limits are
|
|
13
|
+
* merged rather than partial. Required rather than convenient: the budget line
|
|
14
|
+
* below states a number to the model, and stating "up to undefined turns" — or
|
|
15
|
+
* quietly rendering a Recipe's override as the whole budget — is worse than not
|
|
16
|
+
* telling it at all.
|
|
17
|
+
*/
|
|
18
|
+
export type RenderableExecution = RecipeExecutionRequest & {
|
|
19
|
+
recipe: ValidatedRecipe;
|
|
20
|
+
};
|
|
21
|
+
export interface RenderedInvocation {
|
|
22
|
+
/** The validated Recipe soul, verbatim — the invocation's system prompt. */
|
|
23
|
+
system: string;
|
|
24
|
+
/** The sectioned user message (instruction, references, dependency results). */
|
|
25
|
+
prompt: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Render the sectioned user message for one execution. Reference snapshots are
|
|
29
|
+
* emitted exactly as captured at decomposition — `[ref N]` labels with the
|
|
30
|
+
* message role, no summarizing, rewriting, or interpolation. Sections with no
|
|
31
|
+
* content are omitted entirely.
|
|
32
|
+
*/
|
|
33
|
+
export declare function renderSubagentPrompt(request: RenderableExecution): RenderedInvocation;
|
|
34
|
+
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/subagent/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D;;;;;;;GAOG;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG,sBAAsB,GAAG;IACzD,MAAM,EAAE,eAAe,CAAC;CACzB,CAAC;AAsBF,MAAM,WAAW,kBAAkB;IACjC,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,mBAAmB,GAC3B,kBAAkB,CA8BpB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The budget, in the terms the runner actually enforces.
|
|
3
|
+
*
|
|
4
|
+
* Worth its own section because a subagent that does not know its budget invents
|
|
5
|
+
* one, and a *main* agent writing the prompt invents one for it: in one logged
|
|
6
|
+
* play the instruction said "you have up to 20 actions total" — 20 was the turn
|
|
7
|
+
* budget — and the subagent stopped after eleven game moves reasoning that it
|
|
8
|
+
* must be out. Turns and actions are not the same currency and this is the line
|
|
9
|
+
* that says so.
|
|
10
|
+
*/
|
|
11
|
+
function renderBudget(limits) {
|
|
12
|
+
const minutes = Math.round(limits.maxWallMs / 60_000);
|
|
13
|
+
return (`# Budget\nUp to ${limits.maxTurns} turns and about ${minutes} minutes. ` +
|
|
14
|
+
"One turn is one tool call, however much that call does — anything a tool " +
|
|
15
|
+
"counts internally is its own budget, not this one. Reaching either ceiling " +
|
|
16
|
+
"does not drop your work: you get one last turn, with no tools, to write up " +
|
|
17
|
+
"what you have.");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Render the sectioned user message for one execution. Reference snapshots are
|
|
21
|
+
* emitted exactly as captured at decomposition — `[ref N]` labels with the
|
|
22
|
+
* message role, no summarizing, rewriting, or interpolation. Sections with no
|
|
23
|
+
* content are omitted entirely.
|
|
24
|
+
*/
|
|
25
|
+
export function renderSubagentPrompt(request) {
|
|
26
|
+
const sections = [renderBudget(request.recipe.limits)];
|
|
27
|
+
const prompt = request.prompt.trim();
|
|
28
|
+
if (prompt !== "") {
|
|
29
|
+
sections.push(`# Task\n${prompt}`);
|
|
30
|
+
}
|
|
31
|
+
if (request.references.length > 0) {
|
|
32
|
+
const refs = request.references.map((ref, i) => `[ref ${i + 1}] (${ref.role}): ${ref.text}`);
|
|
33
|
+
sections.push("# Conversation references (verbatim snapshots of the caller's conversation)\n" +
|
|
34
|
+
refs.join("\n"));
|
|
35
|
+
}
|
|
36
|
+
if (request.dependencyResults.length > 0) {
|
|
37
|
+
const deps = request.dependencyResults.map((dep) => {
|
|
38
|
+
const text = dep.resultParts.map((part) => part.text).join("\n");
|
|
39
|
+
return `[dependency ${dep.subtaskId}] (${dep.type}): ${text}`;
|
|
40
|
+
});
|
|
41
|
+
sections.push("# Dependency results (generated output from prerequisite subtasks — not conversation evidence)\n" +
|
|
42
|
+
deps.join("\n"));
|
|
43
|
+
}
|
|
44
|
+
return { system: request.recipe.soul, prompt: sections.join("\n\n") };
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/subagent/prompt.ts"],"names":[],"mappings":"AAuBA;;;;;;;;;GASG;AACH,SAAS,YAAY,CAAC,MAAiC;IACrD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,CAAC;IACtD,OAAO,CACL,mBAAmB,MAAM,CAAC,QAAQ,oBAAoB,OAAO,YAAY;QACzE,2EAA2E;QAC3E,6EAA6E;QAC7E,6EAA6E;QAC7E,gBAAgB,CACjB,CAAC;AACJ,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAA4B;IAE5B,MAAM,QAAQ,GAAa,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAEjE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACrC,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;QAClB,QAAQ,CAAC,IAAI,CAAC,WAAW,MAAM,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CACjC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,EAAE,CACxD,CAAC;QACF,QAAQ,CAAC,IAAI,CACX,+EAA+E;YAC7E,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAClB,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,OAAO,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACjD,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjE,OAAO,eAAe,GAAG,CAAC,SAAS,MAAM,GAAG,CAAC,IAAI,MAAM,IAAI,EAAE,CAAC;QAChE,CAAC,CAAC,CAAC;QACH,QAAQ,CAAC,IAAI,CACX,kGAAkG;YAChG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAClB,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;AACxE,CAAC"}
|