@loopingai/core 0.3.0 → 0.4.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/README.md +83 -28
- package/dist/a2a/caller.d.ts +24 -0
- package/dist/a2a/caller.d.ts.map +1 -0
- package/dist/a2a/caller.js +34 -0
- package/dist/a2a/caller.js.map +1 -0
- package/dist/a2a/card.d.ts +7 -2
- package/dist/a2a/card.d.ts.map +1 -1
- package/dist/a2a/card.js +16 -7
- package/dist/a2a/card.js.map +1 -1
- package/dist/a2a/index.d.ts +15 -0
- package/dist/a2a/index.d.ts.map +1 -1
- package/dist/a2a/index.js +15 -0
- package/dist/a2a/index.js.map +1 -1
- package/dist/a2a/notify.d.ts +27 -5
- package/dist/a2a/notify.d.ts.map +1 -1
- package/dist/a2a/notify.js +18 -8
- package/dist/a2a/notify.js.map +1 -1
- package/dist/a2a/push.d.ts +71 -0
- package/dist/a2a/push.d.ts.map +1 -0
- package/dist/a2a/push.js +54 -0
- package/dist/a2a/push.js.map +1 -0
- package/dist/a2a/verify.d.ts +28 -36
- package/dist/a2a/verify.d.ts.map +1 -1
- package/dist/a2a/verify.js +20 -25
- package/dist/a2a/verify.js.map +1 -1
- package/dist/agent/index.d.ts +16 -5
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +16 -5
- package/dist/agent/index.js.map +1 -1
- package/dist/config.d.ts +49 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +25 -3
- package/dist/config.js.map +1 -1
- package/dist/db/models/tasks.d.ts +19 -5
- package/dist/db/models/tasks.d.ts.map +1 -1
- package/dist/db/models/tasks.js +38 -8
- package/dist/db/models/tasks.js.map +1 -1
- package/dist/host/agent.d.ts +217 -0
- package/dist/host/agent.d.ts.map +1 -0
- package/dist/host/agent.js +306 -0
- package/dist/host/agent.js.map +1 -0
- package/dist/host/index.d.ts +21 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +20 -0
- package/dist/host/index.js.map +1 -0
- package/dist/host/plugin-host.d.ts +47 -0
- package/dist/host/plugin-host.d.ts.map +1 -0
- package/dist/host/plugin-host.js +2 -0
- package/dist/host/plugin-host.js.map +1 -0
- package/dist/round/agent.d.ts +267 -0
- package/dist/round/agent.d.ts.map +1 -0
- package/dist/round/agent.js +691 -0
- package/dist/round/agent.js.map +1 -0
- package/dist/round/index.d.ts +25 -0
- package/dist/round/index.d.ts.map +1 -0
- package/dist/round/index.js +24 -0
- package/dist/round/index.js.map +1 -0
- package/dist/round/policy.d.ts +99 -0
- package/dist/round/policy.d.ts.map +1 -0
- package/dist/round/policy.js +2 -0
- package/dist/round/policy.js.map +1 -0
- package/dist/round/subagent.d.ts +67 -0
- package/dist/round/subagent.d.ts.map +1 -0
- package/dist/round/subagent.js +91 -0
- package/dist/round/subagent.js.map +1 -0
- package/dist/round/turn.d.ts +231 -0
- package/dist/round/turn.d.ts.map +1 -0
- package/dist/round/turn.js +497 -0
- package/dist/round/turn.js.map +1 -0
- package/dist/round/workflow.d.ts +109 -0
- package/dist/round/workflow.d.ts.map +1 -0
- package/dist/round/workflow.js +316 -0
- package/dist/round/workflow.js.map +1 -0
- package/dist/runtime/index.d.ts +5 -1
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/testing/auth.d.ts.map +1 -1
- package/dist/testing/auth.js +10 -7
- package/dist/testing/auth.js.map +1 -1
- package/dist/testing/fixtures.d.ts +17 -0
- package/dist/testing/fixtures.d.ts.map +1 -1
- package/dist/testing/fixtures.js +17 -0
- package/dist/testing/fixtures.js.map +1 -1
- package/dist/testing/harness.d.ts +98 -0
- package/dist/testing/harness.d.ts.map +1 -0
- package/dist/testing/harness.js +139 -0
- package/dist/testing/harness.js.map +1 -0
- package/dist/testing/index.d.ts +2 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +2 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/vcr-store.d.ts +14 -18
- package/dist/testing/vcr-store.d.ts.map +1 -1
- package/dist/testing/vcr-store.js +30 -30
- package/dist/testing/vcr-store.js.map +1 -1
- package/dist/worker/define-agent.d.ts +124 -0
- package/dist/worker/define-agent.d.ts.map +1 -0
- package/dist/worker/define-agent.js +21 -0
- package/dist/worker/define-agent.js.map +1 -0
- package/dist/worker/index.d.ts +29 -4
- package/dist/worker/index.d.ts.map +1 -1
- package/dist/worker/index.js +70 -12
- package/dist/worker/index.js.map +1 -1
- package/package.json +20 -6
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import { generateText, hasToolCall, isStepCount } from "ai";
|
|
2
|
+
import { appendOnce } from "../agent/session.js";
|
|
3
|
+
import { deterministicSessionMessage, finalReplyMessageId, parseRoundAckMessageId, roundAckMessageId, sessionText, taskUserMessageId } from "../agent/history.js";
|
|
4
|
+
import { buildIntermediateContentHandler, isTransientAiError } from "../agent/inference.js";
|
|
5
|
+
import { controlTools, controlToolSet } from "../agent/control.js";
|
|
6
|
+
import { FINAL_REPLY_TOOL_NAME } from "../agent/final-reply.js";
|
|
7
|
+
import { stepAllowance } from "../agent/budget.js";
|
|
8
|
+
import { DELEGATE_TOOL_NAME, delegateCallInput, delegateCallOutput, delegateToolCallId } from "../subtasks/delegate.js";
|
|
9
|
+
import { isCatalogEligible } from "../subtasks/catalog.js";
|
|
10
|
+
/**
|
|
11
|
+
* Build both prompt suffixes for one configured agent.
|
|
12
|
+
*
|
|
13
|
+
* The agent's contract, then whatever the delegable types have to say about being
|
|
14
|
+
* delegated — each declared by the type that owns it (`SubtaskTypeSpec`) and
|
|
15
|
+
* collected by the runtime's registry, so no domain is named by the policy. That
|
|
16
|
+
* is the rule the two prompt fields on a subtask type exist to hold: everything
|
|
17
|
+
* the main agent is told about a domain is declared by the plugin that owns it,
|
|
18
|
+
* never written inside the loop.
|
|
19
|
+
*/
|
|
20
|
+
export function buildTurnInstructions(policy, types, maxSubtasks, limits) {
|
|
21
|
+
const guidance = types.renderDelegationGuidance({
|
|
22
|
+
delegateTool: DELEGATE_TOOL_NAME,
|
|
23
|
+
finalReplyTool: FINAL_REPLY_TOOL_NAME
|
|
24
|
+
});
|
|
25
|
+
const open = policy.roundContract({ typeKeys: types.keys, maxSubtasks }) +
|
|
26
|
+
(guidance ? `\n\n${guidance}` : "");
|
|
27
|
+
return { open, final: open + policy.finalRoundNote(limits) };
|
|
28
|
+
}
|
|
29
|
+
/** Join one branch's parts into its text block. */
|
|
30
|
+
function branchText(branch) {
|
|
31
|
+
return (branch.resultParts ?? []).map((p) => p.text).join("\n");
|
|
32
|
+
}
|
|
33
|
+
/** Group every branch by the round that delegated it, preserving ordinal order. */
|
|
34
|
+
function byRound(branches) {
|
|
35
|
+
const rounds = new Map();
|
|
36
|
+
for (const branch of branches) {
|
|
37
|
+
const existing = rounds.get(branch.round);
|
|
38
|
+
if (existing)
|
|
39
|
+
existing.push(branch);
|
|
40
|
+
else
|
|
41
|
+
rounds.set(branch.round, [branch]);
|
|
42
|
+
}
|
|
43
|
+
return rounds;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Rebuild one round's `delegate` call and pair it with its result.
|
|
47
|
+
*
|
|
48
|
+
* Both halves are real. That round's model genuinely emitted this call; the
|
|
49
|
+
* subagents genuinely produced these outcomes. All that separates them is a
|
|
50
|
+
* Workflow boundary and, often, hours — so the pair is reconstructed here rather
|
|
51
|
+
* than carried, from the durable rows that are the record of what happened.
|
|
52
|
+
*
|
|
53
|
+
* Failed and skipped branches are included so the model can disclose them rather
|
|
54
|
+
* than quietly answering as if the work had been done; their diagnostics are not
|
|
55
|
+
* (see `delegateCallOutput`).
|
|
56
|
+
*/
|
|
57
|
+
function delegationPair(taskId, round, replyText, branches) {
|
|
58
|
+
const toolCallId = delegateToolCallId(taskId, round);
|
|
59
|
+
const content = [];
|
|
60
|
+
// The acknowledgment the user already saw, if it is still in history.
|
|
61
|
+
if (replyText)
|
|
62
|
+
content.push({ type: "text", text: replyText });
|
|
63
|
+
content.push({
|
|
64
|
+
type: "tool-call",
|
|
65
|
+
toolCallId,
|
|
66
|
+
toolName: DELEGATE_TOOL_NAME,
|
|
67
|
+
input: delegateCallInput(replyText ?? "", branches)
|
|
68
|
+
});
|
|
69
|
+
return [
|
|
70
|
+
{ role: "assistant", content },
|
|
71
|
+
{
|
|
72
|
+
role: "tool",
|
|
73
|
+
content: [
|
|
74
|
+
{
|
|
75
|
+
type: "tool-result",
|
|
76
|
+
toolCallId,
|
|
77
|
+
toolName: DELEGATE_TOOL_NAME,
|
|
78
|
+
output: { type: "json", value: delegateCallOutput(branches) }
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Render the model's view for one round: the conversation, with every earlier
|
|
86
|
+
* round's delegation restored as the call-and-result it actually was, and every
|
|
87
|
+
* referenceable turn marked with the catalog index the model selects it by.
|
|
88
|
+
*
|
|
89
|
+
* One pass, two jobs, because they have to agree. The `[ref N]` markers use
|
|
90
|
+
* `isCatalogEligible` — the same predicate the catalog is numbered with — so a
|
|
91
|
+
* marker and its entry can never drift: compaction summaries (`assistant` role,
|
|
92
|
+
* generated) stay in the messages unmarked, readable for context but structurally
|
|
93
|
+
* uncitable as conversation evidence, which is exactly the intent.
|
|
94
|
+
*
|
|
95
|
+
* A round's acknowledgment is stored as plain assistant text (history is
|
|
96
|
+
* text-only, and stays that way — `sessionText`, the catalog, compaction, and
|
|
97
|
+
* recall all read text parts). So its `delegate` call is re-attached to that
|
|
98
|
+
* message here, and the result appended after it, for this one inference call. The
|
|
99
|
+
* pair is emitted together, anchored on the ack's deterministic id, so a `tool`
|
|
100
|
+
* message can never be orphaned from its call — and an ack that has been compacted
|
|
101
|
+
* away still gets its pair, appended at the end minus the acknowledgment text: a
|
|
102
|
+
* result the model cannot place beats a malformed history.
|
|
103
|
+
*
|
|
104
|
+
* Acks are deliberately **not** catalog-eligible: they are the agent's own
|
|
105
|
+
* scaffolding, and a subtask referencing "I'm on it" as verbatim conversation
|
|
106
|
+
* evidence would be noise. That holds for every ack in the Session, not only the
|
|
107
|
+
* ones this render can pair with branches — see `parseRoundAckMessageId`.
|
|
108
|
+
*
|
|
109
|
+
* Everything here is ephemeral — scaffolding for this call only. Reference text is
|
|
110
|
+
* snapshotted from the catalog, so no `[ref N]` prefix ever reaches a Subtask, and
|
|
111
|
+
* the Session never sees any of this markup.
|
|
112
|
+
*/
|
|
113
|
+
export function renderTurnMessages(history, taskId, branches) {
|
|
114
|
+
const rounds = byRound(branches);
|
|
115
|
+
const ackIds = new Map([...rounds.keys()].map((round) => [roundAckMessageId(taskId, round), round]));
|
|
116
|
+
const catalog = [];
|
|
117
|
+
const messages = [];
|
|
118
|
+
const anchored = new Set();
|
|
119
|
+
for (const message of history) {
|
|
120
|
+
if (message.role !== "user" && message.role !== "assistant")
|
|
121
|
+
continue;
|
|
122
|
+
const role = message.role;
|
|
123
|
+
const text = sessionText(message);
|
|
124
|
+
const round = ackIds.get(message.id);
|
|
125
|
+
if (round !== undefined) {
|
|
126
|
+
messages.push(...delegationPair(taskId, round, text, rounds.get(round) ?? []));
|
|
127
|
+
anchored.add(round);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
// An acknowledgment with no branches behind it — recognized by id, since
|
|
131
|
+
// nothing in the message body distinguishes an ack from ordinary assistant
|
|
132
|
+
// prose. This Task's own is the crash-window leftover: the ack landed, the
|
|
133
|
+
// rows did not, and this render belongs to the retry that will decide the
|
|
134
|
+
// round again. Dropping it is what makes that retry a clean re-decision —
|
|
135
|
+
// left in, it reads as "already delegated" and invites the model to answer
|
|
136
|
+
// instead of delegating, ending the Task with no work done. Another Task's
|
|
137
|
+
// ack is real history the user saw, so it stays as context, but uncitable:
|
|
138
|
+
// no round of *this* Task can hold it up as conversation evidence.
|
|
139
|
+
const ack = parseRoundAckMessageId(message.id);
|
|
140
|
+
if (ack) {
|
|
141
|
+
if (ack.taskId !== taskId)
|
|
142
|
+
messages.push({ role, content: text });
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (!isCatalogEligible(message)) {
|
|
146
|
+
// Not referenceable (a compaction summary): still context for reasoning.
|
|
147
|
+
messages.push({ role, content: text });
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const index = catalog.length + 1;
|
|
151
|
+
catalog.push({ index, role, text });
|
|
152
|
+
messages.push({ role, content: `[ref ${index}] ${text}` });
|
|
153
|
+
}
|
|
154
|
+
// Rounds whose acknowledgment is no longer in history (compacted away by a
|
|
155
|
+
// concurrent task), in round order so the results still read chronologically.
|
|
156
|
+
const orphaned = [...rounds.entries()]
|
|
157
|
+
.filter(([round]) => !anchored.has(round))
|
|
158
|
+
.sort(([a], [b]) => a - b);
|
|
159
|
+
for (const [round, roundBranches] of orphaned) {
|
|
160
|
+
messages.push(...delegationPair(taskId, round, null, roundBranches));
|
|
161
|
+
}
|
|
162
|
+
return { messages, catalog };
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Deterministic fallback reply: the successful branches' text in ordinal order,
|
|
166
|
+
* plus the policy's short note when some branches did not succeed.
|
|
167
|
+
*
|
|
168
|
+
* Used when a round's inference is unavailable but its predecessors' work is
|
|
169
|
+
* durable. Failing the whole Task because the answering model is down would throw
|
|
170
|
+
* away good results the user asked for.
|
|
171
|
+
*/
|
|
172
|
+
export function joinSuccessfulBranches(branches, partialNote) {
|
|
173
|
+
const successes = branches.filter((b) => b.status === "completed");
|
|
174
|
+
const body = successes.map(branchText).join("\n\n");
|
|
175
|
+
const incomplete = branches.length > successes.length;
|
|
176
|
+
return incomplete ? `${body}\n\n${partialNote}` : body;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* One attempt against a single model: let it work, and take whichever ending it
|
|
180
|
+
* lands on.
|
|
181
|
+
*
|
|
182
|
+
* Takes the model **factory**, not a model: resolving it can throw (a missing
|
|
183
|
+
* binding, a bad id), and that has to count as this attempt failing so the other
|
|
184
|
+
* model still gets its turn.
|
|
185
|
+
*
|
|
186
|
+
* The model uses its work tools freely — answering well can genuinely need a
|
|
187
|
+
* lookup or a recall — and the loop ends by calling a control tool. None has an
|
|
188
|
+
* `execute`, so there is nothing to continue from and the loop halts on the call.
|
|
189
|
+
* Two endings in one step are resolved by `ControlTool.precedence`.
|
|
190
|
+
*
|
|
191
|
+
* Every control call is then parsed by the tool that owns it, because nothing else
|
|
192
|
+
* has: an execute-less tool's input never passes through the SDK's validation. A
|
|
193
|
+
* parse failure comes back as `rejected` — a repairable failure, not a dead
|
|
194
|
+
* attempt.
|
|
195
|
+
*
|
|
196
|
+
* Charges the budget as it goes, whether it succeeds or not: a failed attempt cost
|
|
197
|
+
* exactly as much as a successful one, and a call that died on its fourth step
|
|
198
|
+
* still spent four turns.
|
|
199
|
+
*/
|
|
200
|
+
async function attempt(args, control, model, instructions, messages) {
|
|
201
|
+
const final = args.mode === "final";
|
|
202
|
+
// A `final` round is handed nothing to work with, only the way out. Leaving the
|
|
203
|
+
// work tools on would invite it to spend a budget it has already spent — and
|
|
204
|
+
// the composing round has every branch result in its messages already, so the
|
|
205
|
+
// thing it needs is not a lookup but an ending.
|
|
206
|
+
const workTools = final ? {} : args.tools;
|
|
207
|
+
// One step per attempt for a `final` round: it exists to produce the answer, and
|
|
208
|
+
// that answer is deliberately spent *beyond* the budget rather than out of it.
|
|
209
|
+
//
|
|
210
|
+
// An `open` round gets whatever the shared budget still holds, read here rather
|
|
211
|
+
// than at the top of the round — so the fallback sees what the primary spent
|
|
212
|
+
// without anyone having to subtract it. See `stepAllowance` for the floor.
|
|
213
|
+
const stepBudget = final
|
|
214
|
+
? 1
|
|
215
|
+
: stepAllowance(args.budget.allowance, args.budget.spent);
|
|
216
|
+
const content = args.onContent
|
|
217
|
+
? buildIntermediateContentHandler(args.onContent, [
|
|
218
|
+
DELEGATE_TOOL_NAME,
|
|
219
|
+
FINAL_REPLY_TOOL_NAME
|
|
220
|
+
])
|
|
221
|
+
: undefined;
|
|
222
|
+
try {
|
|
223
|
+
const result = await generateText({
|
|
224
|
+
model: model(),
|
|
225
|
+
instructions,
|
|
226
|
+
messages,
|
|
227
|
+
// Control tools are declared *first*: tool order is part of the prompt, and
|
|
228
|
+
// the two endings are the thing every round has to reach. Work tool names
|
|
229
|
+
// are compile-time constants and none collides with a control name, so the
|
|
230
|
+
// spread order costs nothing.
|
|
231
|
+
tools: { ...controlToolSet(control), ...workTools },
|
|
232
|
+
// Every ending is a control call, so the model must always call something.
|
|
233
|
+
// Work tools stay freely available — `required` constrains the *shape* of a
|
|
234
|
+
// step's output, not which tool is chosen.
|
|
235
|
+
toolChoice: "required",
|
|
236
|
+
maxOutputTokens: args.maxOutputTokens,
|
|
237
|
+
stopWhen: [
|
|
238
|
+
isStepCount(stepBudget),
|
|
239
|
+
// Halt on any ending this round declares, so a new control tool needs no
|
|
240
|
+
// change here.
|
|
241
|
+
...control.map((c) => hasToolCall(c.name))
|
|
242
|
+
],
|
|
243
|
+
// We do our own primary → fallback recovery, so disable the SDK's
|
|
244
|
+
// per-model backoff (it would only add latency and duplicate the fallback).
|
|
245
|
+
maxRetries: 0,
|
|
246
|
+
// Charged here rather than from `result.steps` so a throw mid-loop still
|
|
247
|
+
// bills the steps already spent — the `catch` below has no `result` to read.
|
|
248
|
+
onStepEnd: async (step) => {
|
|
249
|
+
args.budget.spent += 1;
|
|
250
|
+
if (content)
|
|
251
|
+
await content(step);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
// The most committal ending the model reached, and every call it made to that
|
|
255
|
+
// tool. Ranking by precedence rather than by position keeps "which ending
|
|
256
|
+
// wins" a property the tools declare, not a chain of ifs here.
|
|
257
|
+
const reached = control
|
|
258
|
+
.map((c) => ({
|
|
259
|
+
control: c,
|
|
260
|
+
inputs: result.toolCalls
|
|
261
|
+
.filter((call) => call.toolName === c.name)
|
|
262
|
+
.map((call) => call.input)
|
|
263
|
+
}))
|
|
264
|
+
.filter((c) => c.inputs.length > 0)
|
|
265
|
+
.sort((a, b) => b.control.precedence - a.control.precedence)[0];
|
|
266
|
+
if (reached) {
|
|
267
|
+
// The call that counts, per the tool — a repeated `final_reply` means its
|
|
268
|
+
// last one. Held outside the `try` so a rejection shows the model *that*
|
|
269
|
+
// call and not whichever came first, which it may already have moved past.
|
|
270
|
+
// Only a `select` that throws leaves it unresolved, and that error is about
|
|
271
|
+
// the repeats themselves, so the first call represents them as well as any.
|
|
272
|
+
let subject = reached.inputs[0];
|
|
273
|
+
try {
|
|
274
|
+
subject = reached.control.select(reached.inputs);
|
|
275
|
+
return { ok: true, decision: reached.control.parse(subject) };
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
// The model ended the round but the call cannot be used. Repairable: it is
|
|
279
|
+
// handed this error and asked again, rather than costing the whole slot.
|
|
280
|
+
return {
|
|
281
|
+
ok: false,
|
|
282
|
+
error,
|
|
283
|
+
rejected: {
|
|
284
|
+
toolName: reached.control.name,
|
|
285
|
+
input: subject
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// No control call. Either the model ran out of steps mid-tool-use, or it
|
|
291
|
+
// ignored `toolChoice: "required"` and narrated an action instead of taking
|
|
292
|
+
// one — the failure this whole design exists to catch. Failing the attempt
|
|
293
|
+
// hands the round to the fallback model rather than shipping the narration to
|
|
294
|
+
// the user as if it were an answer.
|
|
295
|
+
if (result.finishReason === "length") {
|
|
296
|
+
console.warn("[turn] model output truncated", {
|
|
297
|
+
taskId: args.taskId,
|
|
298
|
+
round: args.round,
|
|
299
|
+
maxOutputTokens: args.maxOutputTokens
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
ok: false,
|
|
304
|
+
error: new Error(`round produced no decision (finishReason=${result.finishReason}, textLength=${result.text.trim().length})`)
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
return { ok: false, error };
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* How many times one model may be shown its own rejected control call and asked
|
|
313
|
+
* again, before the round gives up on that slot.
|
|
314
|
+
*
|
|
315
|
+
* Repair belongs to the **slot**, not the round. A rejected call is not evidence
|
|
316
|
+
* that a model is unavailable — it is a model that understood the request and got
|
|
317
|
+
* the shape wrong, which is the one failure it can actually fix once it is shown
|
|
318
|
+
* the rejection. Falling straight through to the fallback instead spends a whole
|
|
319
|
+
* second model on a fresh guess that has no idea the first one failed: that is how
|
|
320
|
+
* two slots produced the identical missing-param error and killed a round either
|
|
321
|
+
* of them could have repaired.
|
|
322
|
+
*
|
|
323
|
+
* The fallback keeps its real job — covering a primary that could not answer at
|
|
324
|
+
* all — and is still reached once repairs run out, since a model that cannot get
|
|
325
|
+
* the shape right in four tries has earned a second opinion.
|
|
326
|
+
*/
|
|
327
|
+
const MAX_REPAIR_ATTEMPTS = 3;
|
|
328
|
+
/**
|
|
329
|
+
* The id a repaired exchange is anchored on, derived from the Task and round like
|
|
330
|
+
* every other id here. Suffixed per repair, so several rejected calls can sit in
|
|
331
|
+
* one attempt's messages without colliding.
|
|
332
|
+
*/
|
|
333
|
+
function controlCallId(taskId, round) {
|
|
334
|
+
return `task:${taskId}:round:${round}:control`;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* A rejected control call paired with its rejection, as the exchange the model has
|
|
338
|
+
* to see in order to fix it.
|
|
339
|
+
*
|
|
340
|
+
* This is deliberately the same shape the SDK produces for a work tool that failed
|
|
341
|
+
* — the call, then an `error-text` result carrying the reason. A work tool gets
|
|
342
|
+
* this for free and models already know how to read it; a control tool halts the
|
|
343
|
+
* loop before the SDK can, so the round builds it by hand. Nothing here is
|
|
344
|
+
* specific to which control tool was refused.
|
|
345
|
+
*
|
|
346
|
+
* Shaped as a real tool exchange rather than a prose "that was wrong" user turn,
|
|
347
|
+
* because that is what it is — and an assistant tool-call with no matching result
|
|
348
|
+
* is a malformed message list to every provider.
|
|
349
|
+
*
|
|
350
|
+
* Entirely ephemeral. These messages exist for the next `generateText` call and are
|
|
351
|
+
* never appended to the Session: the durable record of a round is the ending it
|
|
352
|
+
* landed on, and a call that was thrown out is not something a later round should
|
|
353
|
+
* be able to read back as history.
|
|
354
|
+
*/
|
|
355
|
+
function repairExchange(toolCallId, rejected, error) {
|
|
356
|
+
return [
|
|
357
|
+
{
|
|
358
|
+
role: "assistant",
|
|
359
|
+
content: [
|
|
360
|
+
{
|
|
361
|
+
type: "tool-call",
|
|
362
|
+
toolCallId,
|
|
363
|
+
toolName: rejected.toolName,
|
|
364
|
+
input: rejected.input
|
|
365
|
+
}
|
|
366
|
+
]
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
role: "tool",
|
|
370
|
+
content: [
|
|
371
|
+
{
|
|
372
|
+
type: "tool-result",
|
|
373
|
+
toolCallId,
|
|
374
|
+
toolName: rejected.toolName,
|
|
375
|
+
output: {
|
|
376
|
+
type: "error-text",
|
|
377
|
+
value: `${String(error)}\n\n` +
|
|
378
|
+
`The round did not end and nothing was started. Call ${rejected.toolName} ` +
|
|
379
|
+
`again, keeping the parts that were fine and fixing only what the error names.`
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
];
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Run one round against the continuous Session: append the user turn (round 0),
|
|
388
|
+
* let the model decide over the indexed history, validate any delegation against
|
|
389
|
+
* this round's catalog, and persist what the user will see.
|
|
390
|
+
*
|
|
391
|
+
* Every append uses a deterministic id, so a Workflow-step re-run neither
|
|
392
|
+
* duplicates the turn nor changes an already-delivered reply.
|
|
393
|
+
*
|
|
394
|
+
* Two nested recoveries, and they answer different failures. Within a slot, a
|
|
395
|
+
* decomposition the catalog rejects is handed back to the *same* model as a failed
|
|
396
|
+
* tool result, up to {@link MAX_REPAIR_ATTEMPTS} times — a shape error is the one
|
|
397
|
+
* thing a model can fix once it sees it. Across slots, an attempt that produced no
|
|
398
|
+
* decision at all moves to the fallback model, which is what that slot is for.
|
|
399
|
+
*
|
|
400
|
+
* Throws only on a transient platform fault (for the Workflow step to retry).
|
|
401
|
+
* A deterministic failure that outlasts every repair on both slots, with durable
|
|
402
|
+
* work behind it, degrades to {@link joinSuccessfulBranches} rather than discarding
|
|
403
|
+
* completed branches; with nothing behind it, it resolves to `{ status: "failed" }`.
|
|
404
|
+
*/
|
|
405
|
+
export async function runTurn(args) {
|
|
406
|
+
const { session, taskId, round, text, systemSuffix, models, branches } = args;
|
|
407
|
+
if (round === 0) {
|
|
408
|
+
await appendOnce(session, deterministicSessionMessage(taskUserMessageId(taskId), "user", text));
|
|
409
|
+
}
|
|
410
|
+
const history = await session.getHistory();
|
|
411
|
+
const { messages, catalog } = renderTurnMessages(history, taskId, branches);
|
|
412
|
+
const system = (await session.refreshSystemPrompt()) +
|
|
413
|
+
systemSuffix +
|
|
414
|
+
(args.mode === "final" ? args.instructions.final : args.instructions.open);
|
|
415
|
+
// This round's endings, built with the catalog a `delegate` is checked against.
|
|
416
|
+
const control = controlTools({
|
|
417
|
+
catalog,
|
|
418
|
+
delegable: args.mode !== "final",
|
|
419
|
+
types: args.types,
|
|
420
|
+
maxSubtasks: args.maxSubtasks
|
|
421
|
+
});
|
|
422
|
+
const diagnostics = [];
|
|
423
|
+
const errors = [];
|
|
424
|
+
for (const slot of ["primary", "fallback"]) {
|
|
425
|
+
const modelId = slot === "primary" ? models.primaryId() : models.fallbackId();
|
|
426
|
+
const model = slot === "primary" ? models.primary : models.fallback;
|
|
427
|
+
// This slot's own view: the round's messages plus whatever repair exchange it
|
|
428
|
+
// accumulates. A fresh copy per slot, so a fallback that is reached is never
|
|
429
|
+
// handed the primary's rejected calls to be confused by.
|
|
430
|
+
const slotMessages = [...messages];
|
|
431
|
+
for (let repair = 0; repair <= MAX_REPAIR_ATTEMPTS; repair += 1) {
|
|
432
|
+
// Both slots draw on the one `args.budget`, which each attempt reads on entry
|
|
433
|
+
// and charges as it works. A fallback attempt is spend, not a free retry —
|
|
434
|
+
// and so is a repair.
|
|
435
|
+
const outcome = await attempt(args, control, model, system, slotMessages);
|
|
436
|
+
if (!outcome.ok) {
|
|
437
|
+
errors.push(outcome.error);
|
|
438
|
+
const { rejected } = outcome;
|
|
439
|
+
diagnostics.push(rejected
|
|
440
|
+
? `${slot} (${modelId}, attempt ${repair + 1}): ${String(outcome.error)}`
|
|
441
|
+
: `${slot} (${modelId}): ${String(outcome.error)}`);
|
|
442
|
+
console.warn(rejected
|
|
443
|
+
? "[turn] control call rejected"
|
|
444
|
+
: "[turn] model attempt failed", {
|
|
445
|
+
taskId,
|
|
446
|
+
round,
|
|
447
|
+
model: modelId,
|
|
448
|
+
...(rejected ? { tool: rejected.toolName, repair } : {}),
|
|
449
|
+
error: String(outcome.error)
|
|
450
|
+
});
|
|
451
|
+
// No rejected call means no ending to correct — the attempt produced
|
|
452
|
+
// nothing, which is the failure the fallback slot exists for.
|
|
453
|
+
//
|
|
454
|
+
// Otherwise repair, while this slot has both attempts and turns left. Past
|
|
455
|
+
// the allowance every attempt gets `stepAllowance`'s one-step floor, which
|
|
456
|
+
// is enough to reach an ending but not to reconsider one — so retrying
|
|
457
|
+
// there buys a worse call at a real cost.
|
|
458
|
+
if (rejected &&
|
|
459
|
+
repair < MAX_REPAIR_ATTEMPTS &&
|
|
460
|
+
args.budget.spent < args.budget.allowance) {
|
|
461
|
+
slotMessages.push(...repairExchange(`${controlCallId(taskId, round)}:repair:${repair}`, rejected, outcome.error));
|
|
462
|
+
continue;
|
|
463
|
+
}
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
if (outcome.decision.kind === "reply") {
|
|
467
|
+
// A throw here is a storage fault: it propagates so the step retries.
|
|
468
|
+
const reply = await appendOnce(session, deterministicSessionMessage(finalReplyMessageId(taskId), "assistant", outcome.decision.text));
|
|
469
|
+
return { status: "replied", reply };
|
|
470
|
+
}
|
|
471
|
+
const stored = await appendOnce(session, deterministicSessionMessage(roundAckMessageId(taskId, round), "assistant", outcome.decision.reply));
|
|
472
|
+
return {
|
|
473
|
+
status: "delegated",
|
|
474
|
+
reply: stored,
|
|
475
|
+
drafts: outcome.decision.drafts
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
// A transient fault is not a decision failure — let the step retry rather than
|
|
480
|
+
// failing the user's Task over Workers-AI capacity.
|
|
481
|
+
const transient = errors.find((e) => isTransientAiError(e));
|
|
482
|
+
if (transient)
|
|
483
|
+
throw transient;
|
|
484
|
+
const detail = `round ${round} exhausted both models — ${diagnostics.join("; ")}`;
|
|
485
|
+
// Both models failed deterministically. Any branch results behind us are durable
|
|
486
|
+
// and useful; deliver them joined rather than failing a Task whose work is done.
|
|
487
|
+
if (branches.some((b) => b.status === "completed")) {
|
|
488
|
+
console.warn("[turn] falling back to deterministic join", {
|
|
489
|
+
taskId,
|
|
490
|
+
round
|
|
491
|
+
});
|
|
492
|
+
const reply = await appendOnce(session, deterministicSessionMessage(finalReplyMessageId(taskId), "assistant", joinSuccessfulBranches(branches, args.partialNote)));
|
|
493
|
+
return { status: "replied", reply };
|
|
494
|
+
}
|
|
495
|
+
return { status: "failed", error: detail };
|
|
496
|
+
}
|
|
497
|
+
//# sourceMappingURL=turn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn.js","sourceRoot":"","sources":["../../src/round/turn.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { WorkflowStep } from "cloudflare:workers";
|
|
2
|
+
import type { CoreConfig } from "../config.js";
|
|
3
|
+
import type { GatewayIdentity } from "../a2a/verify.js";
|
|
4
|
+
import type { RoundAgentBase } from "./agent.js";
|
|
5
|
+
import type { RoundPolicy } from "./policy.js";
|
|
6
|
+
/**
|
|
7
|
+
* The async task controller. The gateway does not wait for a synchronous reply:
|
|
8
|
+
* the Worker accepts a turn (returns a `submitted` Task) and hands the actual work
|
|
9
|
+
* to this durable Workflow, which orchestrates it end to end and delivers the
|
|
10
|
+
* reply to the gateway's push-notification webhook.
|
|
11
|
+
*
|
|
12
|
+
* The shape is a **round loop**, not a fixed sequence of phases:
|
|
13
|
+
*
|
|
14
|
+
* 0. **Pre-work** — resolve the caller's agent, mark the Task working.
|
|
15
|
+
* 1. **Round** — one main-agent inference that either answers the user (the Task
|
|
16
|
+
* is done) or delegates durable Subtasks plus the acknowledgment the user sees
|
|
17
|
+
* while they run.
|
|
18
|
+
* 2. **Execute** — a delegating round's Subtask DAG runs in waves, every
|
|
19
|
+
* dependency-ready node concurrently, each in an isolated managed subagent.
|
|
20
|
+
* Then the loop returns to 1, where the model sees the results and decides
|
|
21
|
+
* again — answer, or delegate once more.
|
|
22
|
+
* 3. **Deliver** — persist the terminal Task, then POST a signed callback.
|
|
23
|
+
*
|
|
24
|
+
* The main agent is never forced either way. A round that has run out of budget —
|
|
25
|
+
* `mainAgentLimits`, in turns or in wall clock — is handed no tools but the
|
|
26
|
+
* answer, so it has to give one; every other round chooses. That is the whole
|
|
27
|
+
* reason this is a loop, and the whole termination argument.
|
|
28
|
+
*
|
|
29
|
+
* Why a Workflow (not a DO alarm or `waitUntil`): `step.do(...)` gives durable,
|
|
30
|
+
* independently-retried steps that survive isolate eviction, and a future
|
|
31
|
+
* `escalate` decision (ask the human, then continue) slots in cleanly as another
|
|
32
|
+
* branch of the loop built on `step.waitForEvent(...)`.
|
|
33
|
+
*
|
|
34
|
+
* A Workflow is a separate entrypoint and cannot touch the agent DO's SQLite
|
|
35
|
+
* directly, so: the task inputs travel as the workflow **payload**, and the agent
|
|
36
|
+
* runtime plus task state are reached only through **native DO RPC**.
|
|
37
|
+
*
|
|
38
|
+
* Idempotency: the instance id is derived from the gateway's `messageId`
|
|
39
|
+
* (deterministic across dispatch retries), so a re-dispatch never starts a second
|
|
40
|
+
* run. Within a run, every step is re-runnable: the Subtask rows and the Session
|
|
41
|
+
* are the source of truth, and each round recovers from them rather than
|
|
42
|
+
* re-inferring.
|
|
43
|
+
*/
|
|
44
|
+
export interface HandleTaskParams {
|
|
45
|
+
/** The accepted task id (echoed back to the gateway on the callback). */
|
|
46
|
+
taskId: string;
|
|
47
|
+
/** The user turn text to answer. */
|
|
48
|
+
text: string;
|
|
49
|
+
/** The verified calling gateway-agent identity (keys the DO + the Session). */
|
|
50
|
+
identity: GatewayIdentity;
|
|
51
|
+
/** A2A context id, echoed on the completed Task. */
|
|
52
|
+
contextId: string;
|
|
53
|
+
/** Gateway push-notification webhook (also the callback JWT `aud`). */
|
|
54
|
+
pushUrl: string;
|
|
55
|
+
/** Per-task validation token the gateway set; echoed in the callback header. */
|
|
56
|
+
pushToken: string;
|
|
57
|
+
/** This agent's card-signing JWKS URL — the callback JWT `jku` (pinned key). */
|
|
58
|
+
jku: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* What distinguishes one agent's use of this loop from another's.
|
|
62
|
+
*
|
|
63
|
+
* The whole body below is agent-agnostic — it names no soul, no plugin and no
|
|
64
|
+
* model. Two delegating agents differ only in these values, which is why they
|
|
65
|
+
* share one workflow body behind two thin entrypoints rather than two copies of a
|
|
66
|
+
* 250-line orchestration.
|
|
67
|
+
*/
|
|
68
|
+
export interface HandleTaskDeps {
|
|
69
|
+
/** Route to the right DO class for the verified caller. */
|
|
70
|
+
resolveAgent: (identity: GatewayIdentity) => AgentStub;
|
|
71
|
+
/** Resolved config — the loop reads `mainAgentLimits` and `maxSubtasks`. */
|
|
72
|
+
config: CoreConfig;
|
|
73
|
+
/** The user-facing copy. Only `copy.taskFailed` is read out here. */
|
|
74
|
+
policy: RoundPolicy;
|
|
75
|
+
/**
|
|
76
|
+
* The deployment's Ed25519 private JWK, for the terminal callback. Passed
|
|
77
|
+
* rather than read off a module-scope `env` so this stays a pure function of
|
|
78
|
+
* its arguments — and so a Worker whose secret is named something else works
|
|
79
|
+
* with no change here.
|
|
80
|
+
*/
|
|
81
|
+
signingKey: string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The caller's agent DO stub — every phase runs through it.
|
|
85
|
+
*
|
|
86
|
+
* Typed on the abstract base rather than a concrete class: the orchestration
|
|
87
|
+
* below calls only methods the base declares, and every delegating agent's stub
|
|
88
|
+
* satisfies it.
|
|
89
|
+
*/
|
|
90
|
+
type AgentStub = DurableObjectStub<RoundAgentBase>;
|
|
91
|
+
/**
|
|
92
|
+
* The orchestration itself, split from the `WorkflowEntrypoint` wiring so it can
|
|
93
|
+
* be driven with a fake `step` in tests (workerd forbids constructing a
|
|
94
|
+
* `WorkflowEntrypoint` outside the runtime) — and so a second agent can reuse it
|
|
95
|
+
* with different deps.
|
|
96
|
+
*
|
|
97
|
+
* Every `step.do` return here is a small projection — a status, an id, a reply.
|
|
98
|
+
* Never a Subtask row: a step return is capped at 1 MiB and a Subtask carries
|
|
99
|
+
* verbatim history snapshots, so the rows stay in the DO and the Workflow carries
|
|
100
|
+
* references to them.
|
|
101
|
+
*
|
|
102
|
+
* **Step names are durable cache keys.** Everything inside the round loop carries
|
|
103
|
+
* its round for that reason: `turn:<round>`, `deadline:<round>`,
|
|
104
|
+
* `scan:<round>:<wave>`, `cancel:<round>:<wave>`. Renaming one silently re-runs
|
|
105
|
+
* its effect on replay.
|
|
106
|
+
*/
|
|
107
|
+
export declare function runHandleTask(p: HandleTaskParams, step: WorkflowStep, deps: HandleTaskDeps): Promise<void>;
|
|
108
|
+
export {};
|
|
109
|
+
//# sourceMappingURL=workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/round/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,QAAQ,EAAE,eAAe,CAAC;IAC1B,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,2DAA2D;IAC3D,YAAY,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,SAAS,CAAC;IACvD,4EAA4E;IAC5E,MAAM,EAAE,UAAU,CAAC;IACnB,qEAAqE;IACrE,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,KAAK,SAAS,GAAG,iBAAiB,CAAC,cAAc,CAAC,CAAC;AAEnD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CACjC,CAAC,EAAE,gBAAgB,EACnB,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,IAAI,CAAC,CAqIf"}
|