Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/subagent/nudge-director.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,45 @@ describe("SubAgentDirector tool failure recovery", () => {
expect(ephemeralTexts(later)).toBeUndefined();
});

test("recovery nudge appends to ephemeral turns already on the infer", async () => {
const director = new SubAgentDirector("system", [], undefined, 30);
const caps = capabilities();
// Seed an ephemeral turn only when the caller did not supply any, so the
// infer action applyPendingNudge rewrites already carries ephemeral turns.
const seeding: ReactorCapabilities = {
...caps,
infer: (options) => {
const existing = (options as { ephemeralTurns?: unknown[] } | undefined)
?.ephemeralTurns;
return caps.infer({
...(options ?? {}),
...(existing === undefined
? {
ephemeralTurns: [
{
role: "user",
content: [{ type: "text", text: "PRE-SEEDED-EPHEMERAL" }],
timestamp: 0,
},
],
}
: {}),
});
},
};

await director.decide(inferenceDone(["seeded-fail"]), state, seeding);
const texts = ephemeralTexts(
inferAction(
await director.decide(toolDone("seeded-fail", true), state, seeding),
),
);

expect(texts).toHaveLength(2);
expect(texts?.[0]).toBe("PRE-SEEDED-EPHEMERAL");
expect(texts?.[1]).toContain("A tool call failed");
});

test("failed-tool recovery supersedes soft re-read guidance", async () => {
const director = new SubAgentDirector("system", [], undefined, 30);
const caps = capabilities();
Expand Down
10 changes: 7 additions & 3 deletions src/subagent/nudge-director.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import type {
ReactorAction,
ToolDefinition,
ConversationTurn,
InferenceOptions,
RetryPolicy,
} from "@intx/types/runtime";
import {
Expand Down Expand Up @@ -83,10 +82,15 @@ function inferWithSubAgentNudge(
* once the pending tool calls have actually executed.
*/
function withEphemeralNudge(
options: InferenceOptions | undefined,
options: ExtendedInferenceOptions | undefined,
text: string,
): ExtendedInferenceOptions {
return { ...(options ?? {}), ephemeralTurns: [ephemeralNudgeTurn(text)] };
const turn = ephemeralNudgeTurn(text);
const existing = options?.ephemeralTurns;
if (existing === undefined || existing.length === 0) {
return { ...(options ?? {}), ephemeralTurns: [turn] };
}
return { ...(options ?? {}), ephemeralTurns: [...existing, turn] };
}

function isEmptyContinuation(event: ReactorInboundEvent): boolean {
Expand Down
Loading