Skip to content
3 changes: 1 addition & 2 deletions actions/setup/js/agent_session_render.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@ function messageSourceScope(source) {
* @param {Array<any>} events
* @returns {Array<any>}
*/
function collapseStreamedMessages(events) {
function collapseStreamedMessages(events, secrets = collectArtifactSecretValues()) {
const output = [];
const projections = new Set();
const secrets = collectArtifactSecretValues();
const claudeMessages = new Map();
let sequence = 0;
let geminiSnapshot;
Expand Down
28 changes: 17 additions & 11 deletions actions/setup/js/copilot_session_bootstrap.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ describe("Copilot retry session bootstrap", () => {
const summary = core.summary.addRaw.mock.calls.flat().join("\n");
expect(summary).toContain("first attempt result");
expect(summary).toContain("final attempt result");
expect(summary).toContain("Agent conversation: agent/sandbox/agent/logs/copilot-session-state/first/events.jsonl");
expect(summary).toContain("Agent conversation: agent/sandbox/agent/logs/copilot-session-state/last/events.jsonl");
expect(summary).toContain("Agent source: agent/sandbox/agent/logs/copilot-session-state/first/events.jsonl");
expect(summary).toContain("Agent source: agent/sandbox/agent/logs/copilot-session-state/last/events.jsonl");

const reconstructed = sessionCLI(["reconstruct", root, "copilot"])
.trim()
Expand All @@ -150,15 +150,21 @@ describe("Copilot retry session bootstrap", () => {
expect(new Set(starts.map(event => event.provenance.path)).size).toBe(2);
expect(reconstructed.filter(event => event.type === "tool.execution_complete")).toHaveLength(2);
const unified = generateCopilotCliStyleSummary(reconstructed);
const firstGroup = unified.indexOf("Agent conversation: agent/sandbox/agent/logs/copilot-session-state/first/events.jsonl");
const lastGroup = unified.indexOf("Agent conversation: agent/sandbox/agent/logs/copilot-session-state/last/events.jsonl");
expect(firstGroup).toBeGreaterThanOrEqual(0);
expect(lastGroup).toBeGreaterThan(firstGroup);
expect(unified.slice(firstGroup, lastGroup)).toContain("first attempt result");
expect(unified.slice(firstGroup, lastGroup)).not.toContain("final attempt result");
const finalConversation = unified.slice(lastGroup, unified.indexOf("Chronological trace"));
expect(finalConversation).toContain("final attempt result");
expect(finalConversation).not.toContain("first attempt result");
for (const output of [summary, unified]) {
const timeline = output.slice(output.indexOf("Chronological trace"));
const completions = timeline.split(/(?=^\[\d+\] )/m).filter(entry => /^\[\d+\] agent\/agent tool\.execution_complete /m.test(entry));
expect(completions).toHaveLength(2);
expect(completions[0]).toContain("path=sandbox/agent/logs/copilot-session-state/first/events.jsonl");
expect(completions[0]).toContain("✓ $ echo retained");
expect(completions[0]).toContain("first attempt result");
expect(completions[0]).not.toContain("final attempt result");
expect(completions[0]).not.toContain("[start unavailable]");
expect(completions[1]).toContain("path=sandbox/agent/logs/copilot-session-state/last/events.jsonl");
expect(completions[1]).toContain("✓ $ echo retained");
expect(completions[1]).toContain("final attempt result");
expect(completions[1]).not.toContain("first attempt result");
expect(completions[1]).not.toContain("[start unavailable]");
}
});

it("applies masks from earlier sessions to every published retry conversation", async () => {
Expand Down
21 changes: 12 additions & 9 deletions actions/setup/js/log_parser_format.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,12 @@ function createLogParserFormatters(deps) {
return "`".repeat(Math.max(3, maxBacktickRun + 1));
}

function normalizeEntriesForRendering(logEntries) {
function normalizeEntriesForRendering(logEntries, secrets) {
if (!Array.isArray(logEntries)) return [];
logEntries = collapseStreamedMessages(logEntries.filter(entry => entry && typeof entry === "object"));
logEntries = collapseStreamedMessages(
logEntries.filter(entry => entry && typeof entry === "object"),
secrets
);
if (isCopilotEventLogEntries(logEntries)) {
return convertCopilotEventsToLegacyLogEntries(logEntries);
}
Expand Down Expand Up @@ -670,12 +673,12 @@ function createLogParserFormatters(deps) {
}
}

function generateSummaryLines(logEntries, includeStatistics = true) {
const groups = publicationAgentSessions(logEntries);
function generateSummaryLines(logEntries, includeStatistics = true, publicationReady = false, secrets) {
const groups = publicationReady ? [] : publicationAgentSessions(logEntries);
if (groups.length > 1 && groups.some(group => group.events.some(event => sessionContext(event).parentToolUseId))) {
return groups.flatMap(group => [`Agent conversation: ${group.label.replace(/[\r\n]/g, " ")}`, "", ...generateSummaryLines(group.events, includeStatistics), ""]);
}
const renderEntries = normalizeEntriesForRendering(logEntries);
const renderEntries = normalizeEntriesForRendering(logEntries, secrets);
const lines = [];
const toolUsePairs = collectToolUsePairs(renderEntries);

Expand Down Expand Up @@ -805,15 +808,15 @@ function createLogParserFormatters(deps) {
markdown,
maxBytes: Math.min(maxBytes, MAX_STEP_SUMMARY_SIZE),
maxLineBytes: 4 * MAX_AGENT_TEXT_LENGTH + 128,
agentStatistics: entries => {
const projected = normalizeEntriesForRendering(entries);
agentStatistics: (entries, secrets) => {
const projected = normalizeEntriesForRendering(entries, secrets);
const model = observedSessionModel(entries);
const lines = [...(model ? [`Model: ${model}`] : []), ...renderInitializationLines(projected.find(entry => entry.type === "system" && entry.subtype === "init"))];
appendStatistics(lines, projected, collectToolUsePairs(projected), true);
lines.push(...renderSubagentSummary(entries));
lines.push(...renderSubagentSummary(entries, secrets));
return lines;
},
agentConversation: entries => generateSummaryLines(entries, false),
agentConversation: (entries, secrets) => generateSummaryLines(entries, false, true, secrets),
});
}

Expand Down
4 changes: 2 additions & 2 deletions actions/setup/js/subagent_session_render.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ const object = value => value !== null && typeof value === "object" && !Array.is
* @param {Array<any>} events
* @returns {string[]}
*/
function renderSubagentSummary(events) {
const redacted = redactManifestValue(events, collectArtifactSecretValues());
function renderSubagentSummary(events, secrets = collectArtifactSecretValues()) {
const redacted = redactManifestValue(events, secrets);
if (!Array.isArray(redacted)) throw new Error(`${ERR_VALIDATION}: Expected subagent session events`);
const agents = new Map();
let metrics;
Expand Down
4 changes: 2 additions & 2 deletions actions/setup/js/subagent_session_render.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ describe("subagent session summaries", () => {
];
const output = generateCopilotCliStyleSummary(events, { maxBytes: 8192 });
expect(Buffer.byteLength(output)).toBeLessThanOrEqual(8192);
expect(output).toContain("Unified session summary omitted: remaining step-summary byte limit reached.");
expect(output).toContain("summary truncated: byte limit reached");
const bounded = generateCopilotCliStyleSummary(events, { maxBytes: 512 * 1024 });
expect(Buffer.byteLength(bounded)).toBeLessThanOrEqual(512 * 1024);
expect(bounded).toContain("Subagents");
Expand All @@ -121,7 +121,7 @@ describe("subagent session summaries", () => {
await publishUnifiedSessionSummary(path.join(root, "usage/aw_session.jsonl"));
const output = fs.readFileSync(summaryPath, "utf8");
expect(output).toContain("Existing summary");
expect(output).toContain("Research &lt;routing&gt;");
expect(output).toContain("Research <routing>");
expect(output).toContain("child credits: 3.000 (75.0% of agent credits)");
expect(output).toContain("reasoningEffort=xhigh executionMode=background");
expect(output).toContain("cacheWriteTokens=0");
Expand Down
3 changes: 2 additions & 1 deletion actions/setup/js/unified_session_payload.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,8 @@ describe("essential unified session payloads", () => {
const projected = collapseStreamedMessages(compact);
expect(projected.filter(event => event.type === "assistant.message").map(event => event.data.content)).toEqual(["Hello world."]);
const output = generatePlainTextSummary(compact);
expect(output.match(/agent\/agent assistant\.message Hello world\./g)).toHaveLength(1);
expect(output.match(/agent\/agent assistant\.message /g)).toHaveLength(1);
expect(output.match(/◆ Hello world\./g)).toHaveLength(1);
}
});

Expand Down
Loading
Loading