Skip to content

Commit 290c1e8

Browse files
fix(prompts): let mid-run operator text steer running workers (#1085)
Answer-first Skywalker copy and Do not resubmit token notices made a steer look like a finish instead of inbound send_input.
1 parent d0b814e commit 290c1e8

8 files changed

Lines changed: 22 additions & 6 deletions

File tree

‎src/agent/directors/skywalker/package.test.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,15 @@ describe("skywalkerPackage", () => {
156156
expect(p).toContain("reply to the operator");
157157
expect(p).toContain("end the turn");
158158
expect(p).toContain("mailbox mail");
159-
expect(p).toContain("answer them first");
160159
expect(p).toContain("Enter mid-run");
161160
});
162161

162+
test("systemPrompt does not forbid steering workers when the operator messages mid-run", () => {
163+
const p = skywalkerPackage.systemPrompt;
164+
expect(p).not.toContain("answer them first");
165+
expect(p).not.toContain("Do not hold the reply on fleet collection");
166+
});
167+
163168
test("systemPrompt anti-cascade keeps digs out of fleets", () => {
164169
const p = skywalkerPackage.systemPrompt;
165170
expect(p).toContain("Anti-cascade");

‎src/agent/directors/skywalker/package.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Idle-orchestrator: fire one or more spawn_agent calls in a turn — each returns
2121
You are the chat surface. Workers cannot ask_operator; they ask_director. A parked question arrives as an idle-send wake — answer with send_input using target = that worker's session id. Do not poll list_agents. Escalate with ask_operator only when you cannot resolve it. While any specialist is running:
2222
- After every spawn wave: short status (who, goal, what you are waiting on) then end the turn.
2323
- On mailbox mail or a finished report: short update — do not go silent.
24-
- When the operator messages mid-run: answer them first (COMMUNICATION). Do not hold the reply on fleet collection — answer now and fold worker results in on the next turn.
24+
- Operator text while a specialist is running: send_input (soft) to that agent_id, then a short ack.
2525
- Keep updates short; no wall of task dumps. manage_tasks is the checklist; chat is the narrative.
2626
2727
Example chains:

‎src/agent/prompts.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -545,8 +545,8 @@ export function buildSubAgentReportContract(
545545
'Key file paths you read or changed (one per line). Write "None." if none.',
546546
"",
547547
askDirector
548-
? "- This message is the only thing returned to the parent. You cannot reach the operator. If the brief is ambiguous, ask_director before finishing; otherwise make the best-judgment call and note assumptions under Blockers."
549-
: "- This message is the only thing returned to the parent. Do not ask the parent questions; you cannot receive answers. Make the best-judgment call, act, and note assumptions under Blockers.",
548+
? "- This message is the only thing returned to the parent. If the brief is ambiguous, ask_director before finishing; otherwise make the best-judgment call and note assumptions under Blockers. Inbound send_input supersedes the brief."
549+
: "- This message is the only thing returned to the parent. Make the best-judgment call, act, and note assumptions under Blockers. Inbound send_input supersedes the brief.",
550550
].join("\n");
551551
}
552552

‎src/prompts.test.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,15 @@ test("when ask_director is in toolNames, the worker prompt mentions ask_director
434434
expect(prompt).not.toContain("ask_operator");
435435
});
436436

437+
test("sub-agent report contract does not claim the worker cannot receive answers", () => {
438+
const withoutAsk = buildSubAgentReportContract();
439+
const withAsk = buildSubAgentReportContract({ askDirector: true });
440+
expect(withoutAsk).not.toContain("you cannot receive answers");
441+
expect(withoutAsk).not.toContain("Do not ask the parent questions");
442+
expect(withAsk).not.toContain("you cannot receive answers");
443+
expect(withAsk).not.toContain("You cannot reach the operator");
444+
});
445+
437446
test("sub-agent report contract treats Success criteria as completion gate", () => {
438447
const contract = buildSubAgentReportContract();
439448
expect(contract).toContain("Success criteria");

‎src/subagent/index.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,6 +1312,7 @@ describe("submit_result turn token notice", () => {
13121312
expect(formatTurnTokenNotice(token)).toContain(
13131313
"A mismatched token means this turn was superseded",
13141314
);
1315+
expect(formatTurnTokenNotice(token)).not.toMatch(/do not resubmit/i);
13151316
// Non-leaf dispatches state no token.
13161317
expect(
13171318
buildDispatchBrief({ description: "plain", prompt: "do the thing" }),

‎src/subagent/report.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export function formatTurnTokenNotice(turnToken: string): string {
6969
return [
7070
"## Turn token",
7171
turnToken,
72-
`If you call submit_result, pass turn_token="${turnToken}" exactly. A mismatched token means this turn was superseded — do not resubmit under it.`,
72+
`If you call submit_result, pass turn_token="${turnToken}" exactly. A mismatched token means this turn was superseded.`,
7373
].join("\n");
7474
}
7575

‎src/subagent/submit-result.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ describe("evaluateSubmitResult", () => {
6161
});
6262
expect(outcome.ok).toBe(false);
6363
expect(outcome.message).toContain("turn_token does not match");
64+
expect(outcome.message).not.toMatch(/do not resubmit/i);
6465
expect(state.corrections).toBe(0);
6566
});
6667

‎src/subagent/submit-result.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export function evaluateSubmitResult(input: SubmitResultInput): {
5353
return {
5454
ok: false,
5555
message:
56-
"Error: turn_token does not match this turn — this dispatch was superseded. Do not resubmit.",
56+
"Error: turn_token does not match this turn — this dispatch was superseded.",
5757
};
5858
}
5959
if (input.state.corrections >= cap) {

0 commit comments

Comments
 (0)