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
19 changes: 13 additions & 6 deletions src/subagent/fleet-dry-drive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,13 @@ function clipDigestSection(text: string): string {
return `${text.slice(0, MAILBOX_DIGEST_SECTION_CHARS - 1).trimEnd()}…`;
}

function mailboxReportSpillNotice(text: string, uri: string): string {
function mailboxSpillNotice(text: string, uri: string): string {
// Digest inlines the notice only, so remaining is the omitted body — not
// length minus a 2048-char prefix that was never kept. A short spill that
// claimed "0 more chars omitted" looks complete and the parent skips read_file.
return truncationNotice({
maxChars: MAILBOX_DIGEST_SECTION_CHARS,
remaining: Math.max(0, text.length - MAILBOX_DIGEST_SECTION_CHARS),
remaining: text.length,
fullLength: text.length,
contentType: "text/plain",
uri,
Expand Down Expand Up @@ -287,8 +290,14 @@ export async function digestCollectedReport(
report.report === undefined
? undefined
: reportUri !== undefined
? mailboxReportSpillNotice(report.report, reportUri)
? mailboxSpillNotice(report.report, reportUri)
: await clipField(report.report, report.agent_id, "report");
const errorInline =
report.error === undefined
? undefined
: errorUri !== undefined
? mailboxSpillNotice(report.error, errorUri)
: clipDigestSection(report.error);
return {
agent_id: report.agent_id,
status: report.status,
Expand All @@ -300,9 +309,7 @@ export async function digestCollectedReport(
...(blockers !== undefined ? { blockers } : {}),
...(reportInline !== undefined ? { report: reportInline } : {}),
...(reportUri !== undefined ? { report_uri: reportUri } : {}),
...(report.error !== undefined
? { error: clipDigestSection(report.error) }
: {}),
...(errorInline !== undefined ? { error: errorInline } : {}),
...(errorUri !== undefined ? { error_uri: errorUri } : {}),
...(report.hint !== undefined ? { hint: report.hint } : {}),
...(report.provider_failure === true ? { provider_failure: true } : {}),
Expand Down
79 changes: 79 additions & 0 deletions src/subagent/mailbox-mail-drive.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
driveMailboxMail,
latchMailboxMailDrive,
MAILBOX_MAIL_WAKE_PREFIX,
mailboxMailErrorUriHint,
mailboxMailReportUriHint,
mailboxMailWakeLine,
occupancyShouldYieldWait,
Expand All @@ -14,6 +15,7 @@ import {
digestCollectedReports,
fleetDrySpillKey,
FLEET_DRY_REPORT_CHARS,
MAILBOX_DIGEST_SECTION_CHARS,
type FleetDryMailboxRecord,
} from "./fleet-dry-drive.js";
import {
Expand Down Expand Up @@ -125,6 +127,22 @@ describe("buildMailboxMailPrompt", () => {
expect(prompt).toContain(uri);
});

test("names error_uri with a read_file truncation notice", () => {
const uri = `tool-output:///${fleetDrySpillKey("worker-1", "error")}`;
const prompt = buildMailboxMailPrompt([
{
agent_id: "worker-1",
status: "failed",
error_uri: uri,
},
]);
expect(prompt).toContain(mailboxMailErrorUriHint());
expect(prompt).toContain(
"use read_file with that URI (offset/limit supported)",
);
expect(prompt).toContain(uri);
});

test("does not paste a duplicate agent_id", () => {
const prompt = buildMailboxMailPrompt([
{ agent_id: "w1", status: "done" },
Expand Down Expand Up @@ -163,6 +181,10 @@ describe("digestCollectedReports", () => {
"use read_file with that URI (offset/limit supported)",
);
expect(digest?.report).not.toContain("SECRET_FINDINGS_BODY");
expect(digest?.report).toContain(
`${ENVELOPE_REPORT.length.toLocaleString()} more chars omitted here`,
);
expect(digest?.report).not.toMatch(/— 0 more chars omitted/);
expect(
new TextDecoder().decode(
store.blobs.get(fleetDrySpillKey("worker-1", "report"))?.bytes ??
Expand Down Expand Up @@ -222,6 +244,63 @@ describe("digestCollectedReports", () => {
expect(digest?.report).toContain("SECRET_FINDINGS_BODY");
expect(digest?.summary).toBe("Shipped the digest.");
});

test("spill notice remaining is the omitted body when no prefix is kept", async () => {
const store = fakeBlobStore();
const short = "short spilled body";
const [digest] = await digestCollectedReports(
[{ agent_id: "short", status: "done", report: short }],
store.writeBlob,
);
expect(short.length).toBeLessThan(MAILBOX_DIGEST_SECTION_CHARS);
expect(digest?.report).toContain(
`${short.length.toLocaleString()} more chars omitted here`,
);
expect(digest?.report).not.toMatch(/— 0 more chars omitted/);
expect(digest?.report).not.toContain(short);
expect(digest?.report_uri).toBe(
`tool-output:///${fleetDrySpillKey("short", "report")}`,
);
});

test("oversized spilled report remaining is the full body, not length minus the digest cap", async () => {
const store = fakeBlobStore();
const original = "x".repeat(MAILBOX_DIGEST_SECTION_CHARS + 80);
const [digest] = await digestCollectedReports(
[{ agent_id: "long", status: "done", report: original }],
store.writeBlob,
);
expect(digest?.report).toContain(
`${original.length.toLocaleString()} more chars omitted here`,
);
expect(digest?.report).not.toContain(
`${(original.length - MAILBOX_DIGEST_SECTION_CHARS).toLocaleString()} more chars omitted here`,
);
});

test("spilled error names error_uri with truncation-notice language", async () => {
const store = fakeBlobStore();
const error = "provider boom";
const [digest] = await digestCollectedReports(
[{ agent_id: "fail", status: "failed", error }],
store.writeBlob,
);
const errorUri = `tool-output:///${fleetDrySpillKey("fail", "error")}`;
expect(digest?.error_uri).toBe(errorUri);
expect(digest?.error).toContain("[output truncated");
expect(digest?.error).toContain(errorUri);
expect(digest?.error).toContain(
`${error.length.toLocaleString()} more chars omitted here`,
);
expect(digest?.error).not.toMatch(/— 0 more chars omitted/);
expect(digest?.error).not.toContain("provider boom");
expect(
new TextDecoder().decode(
store.blobs.get(fleetDrySpillKey("fail", "error"))?.bytes ??
new Uint8Array(),
),
).toBe(error);
});
});

describe("driveMailboxMail", () => {
Expand Down
10 changes: 9 additions & 1 deletion src/subagent/mailbox-mail-drive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export function mailboxMailReportUriHint(): string {
return "report_uri names a spilled full report — use read_file with that URI (offset/limit supported) to see the rest.";
}

/** Names error_uri with truncation-notice language so the parent may fetch it. */
export function mailboxMailErrorUriHint(): string {
return "error_uri names a spilled full error — use read_file with that URI (offset/limit supported) to see the rest.";
}

/**
* Whether inbound text is occupancy's mailbox mail. Internal runtime→agent
* traffic — the fleet board already owns worker status and the payload is
Expand Down Expand Up @@ -85,13 +90,16 @@ export function occupancyShouldYieldWait(
}

export function buildMailboxMailPrompt<
T extends { agent_id: string; report_uri?: string },
T extends { agent_id: string; report_uri?: string; error_uri?: string },
>(reports: readonly T[]): string {
const unique = dedupeByAgentId(reports);
const lines = [mailboxMailWakeLine()];
if (unique.some((report) => report.report_uri !== undefined)) {
lines.push(mailboxMailReportUriHint());
}
if (unique.some((report) => report.error_uri !== undefined)) {
lines.push(mailboxMailErrorUriHint());
}
lines.push(JSON.stringify(unique));
return lines.join("\n");
}
Expand Down
Loading