Skip to content

Commit b3688d4

Browse files
committed
Append pending sub-agent nudges onto existing ephemeral turns
1 parent 440b9cb commit b3688d4

2 files changed

Lines changed: 46 additions & 3 deletions

File tree

src/subagent/nudge-director.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,45 @@ describe("SubAgentDirector tool failure recovery", () => {
270270
expect(ephemeralTexts(later)).toBeUndefined();
271271
});
272272

273+
test("recovery nudge appends to ephemeral turns already on the infer", async () => {
274+
const director = new SubAgentDirector("system", [], undefined, 30);
275+
const caps = capabilities();
276+
// Seed an ephemeral turn only when the caller did not supply any, so the
277+
// infer action applyPendingNudge rewrites already carries ephemeral turns.
278+
const seeding: ReactorCapabilities = {
279+
...caps,
280+
infer: (options) => {
281+
const existing = (options as { ephemeralTurns?: unknown[] } | undefined)
282+
?.ephemeralTurns;
283+
return caps.infer({
284+
...(options ?? {}),
285+
...(existing === undefined
286+
? {
287+
ephemeralTurns: [
288+
{
289+
role: "user",
290+
content: [{ type: "text", text: "PRE-SEEDED-EPHEMERAL" }],
291+
timestamp: 0,
292+
},
293+
],
294+
}
295+
: {}),
296+
});
297+
},
298+
};
299+
300+
await director.decide(inferenceDone(["seeded-fail"]), state, seeding);
301+
const texts = ephemeralTexts(
302+
inferAction(
303+
await director.decide(toolDone("seeded-fail", true), state, seeding),
304+
),
305+
);
306+
307+
expect(texts).toHaveLength(2);
308+
expect(texts?.[0]).toBe("PRE-SEEDED-EPHEMERAL");
309+
expect(texts?.[1]).toContain("A tool call failed");
310+
});
311+
273312
test("failed-tool recovery supersedes soft re-read guidance", async () => {
274313
const director = new SubAgentDirector("system", [], undefined, 30);
275314
const caps = capabilities();

src/subagent/nudge-director.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import type {
1414
ReactorAction,
1515
ToolDefinition,
1616
ConversationTurn,
17-
InferenceOptions,
1817
RetryPolicy,
1918
} from "@intx/types/runtime";
2019
import {
@@ -83,10 +82,15 @@ function inferWithSubAgentNudge(
8382
* once the pending tool calls have actually executed.
8483
*/
8584
function withEphemeralNudge(
86-
options: InferenceOptions | undefined,
85+
options: ExtendedInferenceOptions | undefined,
8786
text: string,
8887
): ExtendedInferenceOptions {
89-
return { ...(options ?? {}), ephemeralTurns: [ephemeralNudgeTurn(text)] };
88+
const turn = ephemeralNudgeTurn(text);
89+
const existing = options?.ephemeralTurns;
90+
if (existing === undefined || existing.length === 0) {
91+
return { ...(options ?? {}), ephemeralTurns: [turn] };
92+
}
93+
return { ...(options ?? {}), ephemeralTurns: [...existing, turn] };
9094
}
9195

9296
function isEmptyContinuation(event: ReactorInboundEvent): boolean {

0 commit comments

Comments
 (0)