Skip to content

Commit 02c69f7

Browse files
authored
Fix Pi standard session mapping and shared evidence preservation (#67393)
1 parent 77bb415 commit 02c69f7

24 files changed

Lines changed: 1113 additions & 137 deletions

‎actions/setup/js/agent_execution.cjs‎

Lines changed: 82 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const { detectNonRetryableHarnessGuard, isAuthenticationFailedError } = require(
55
const { crashSignalNameForExitCode } = require("./harness_crash_signals.cjs");
66
const { ERR_VALIDATION } = require("./error_codes.cjs");
77
const { sessionContext } = require("./agent_session.cjs");
8+
const { stripVTControlCharacters } = require("node:util");
9+
const { applyAddMaskRedaction, collectAddMaskedValues } = require("./add_mask_redaction.cjs");
810

911
/** @typedef {import("./types/agent_session").AgentExecutionData} AgentExecutionData */
1012
/** @typedef {import("./types/agent_session").SessionEvent} SessionEvent */
@@ -49,7 +51,8 @@ function isAgentExecutionEvent(event) {
4951
function recordErrors(record) {
5052
if (!record || typeof record !== "object" || Array.isArray(record)) return [];
5153
const { agentId, parentToolUseId } = sessionContext(record);
52-
if (agentId || parentToolUseId) return [];
54+
const parentToolCallId = record.data?.parentToolCallId !== undefined ? record.data.parentToolCallId : record.parentToolCallId;
55+
if (agentId || parentToolUseId || (typeof parentToolCallId === "string" && parentToolCallId.length > 0)) return [];
5356
const data = record.data ?? record;
5457
if (["error", "session.error"].includes(record.type) && ["warning", "info"].includes(data.severity)) return [];
5558
if (["session.error", "claude.assistant_error", "claude.api_retry", "error", "turn.failed"].includes(record.type)) return [data];
@@ -79,16 +82,69 @@ function isAgentDiagnosticLine(line) {
7982
* Plain transcript blocks are not diagnostics, even if their text starts with
8083
* an error signature. A source-prefixed runtime line restores attribution.
8184
* @param {string} content
85+
* @param {SessionEvent[]} [events]
8286
* @param {string} [rawSourceEngine] Required attribution for raw structured errors.
8387
* @returns {{diagnostics: string[], errors: any[]}}
8488
*/
85-
function collectAgentErrorEvidence(content, rawSourceEngine) {
89+
function collectAgentErrorEvidence(content, events = [], rawSourceEngine) {
8690
const diagnostics = [];
8791
const errors = [];
88-
const attributedErrors = value => (rawSourceEngine !== undefined && value?.data?.sourceEngine !== rawSourceEngine ? [] : recordErrors(value));
92+
let conversationEvents = events;
93+
if (!events.length && content.includes("[deepseek-harness]")) {
94+
// Load lazily: the native parser also uses execution collection for failures.
95+
const { isDeepSeekLog, parseDeepSeekLog } = require("./parse_deepseek_log.cjs");
96+
if (isDeepSeekLog(content)) conversationEvents = parseDeepSeekLog(content).logEntries;
97+
}
98+
const maskedValues = collectAddMaskedValues(content);
99+
/** @param {string} value */
100+
const conversationKey = value => applyAddMaskRedaction(stripVTControlCharacters(value), maskedValues).trim();
101+
/** @param {unknown} value */
102+
const conversationDocumentKey = value =>
103+
JSON.stringify(value, (_, item) => {
104+
if (typeof item === "string") return applyAddMaskRedaction(stripVTControlCharacters(item), maskedValues);
105+
if (item && typeof item === "object" && !Array.isArray(item))
106+
return Object.fromEntries(
107+
Object.keys(item)
108+
.sort()
109+
.map(key => [key, item[key]])
110+
);
111+
return item;
112+
});
113+
const conversationLines = new Set();
114+
const conversationTexts = new Set();
115+
const conversationDocuments = new Set();
116+
/** @param {unknown} value @returns {void} */
117+
const attributeConversationValue = value => {
118+
if (typeof value === "string") {
119+
conversationTexts.add(conversationKey(value));
120+
for (const line of value.split(/\r?\n/)) conversationLines.add(conversationKey(line));
121+
} else if (value && typeof value === "object") {
122+
conversationDocuments.add(conversationDocumentKey(value));
123+
for (const item of Object.values(value)) attributeConversationValue(item);
124+
}
125+
};
126+
for (const event of conversationEvents) {
127+
if (["assistant.message", "assistant.reasoning", "assistant.refusal", "user.message"].includes(event?.type)) attributeConversationValue(event.data?.content);
128+
else if (["tool.execution_complete", "tool.execution_update"].includes(event?.type)) {
129+
for (const field of ["output", "result", "error"]) attributeConversationValue(event.data?.[field]);
130+
}
131+
}
132+
if (conversationTexts.has(conversationKey(content))) return { diagnostics, errors };
133+
/** @param {unknown} record */
134+
const recordEvidence = record => {
135+
if (rawSourceEngine !== undefined) {
136+
if (!record || typeof record !== "object" || !("data" in record)) return [];
137+
const data = record.data;
138+
if (!data || typeof data !== "object" || !("sourceEngine" in data) || data.sourceEngine !== rawSourceEngine) return [];
139+
}
140+
const evidence = recordErrors(record);
141+
return evidence.length && conversationDocuments.size && conversationDocuments.has(conversationDocumentKey(record)) ? [] : evidence;
142+
};
89143
try {
90144
const document = JSON.parse(content);
91-
for (const value of Array.isArray(document) ? document : [document]) errors.push(...attributedErrors(value));
145+
const evidence = (Array.isArray(document) ? document : [document]).flatMap(recordEvidence);
146+
if (evidence.length && Array.isArray(document) && conversationDocuments.has(conversationDocumentKey(document))) return { diagnostics, errors };
147+
errors.push(...evidence);
92148
return { diagnostics, errors };
93149
} catch {
94150
// Mixed stdio is parsed as individual records and attributed diagnostic lines.
@@ -107,6 +163,7 @@ function collectAgentErrorEvidence(content, rawSourceEngine) {
107163
transcriptBlock = true;
108164
continue;
109165
}
166+
if (conversationLines.has(conversationKey(line))) continue;
110167
let record;
111168
try {
112169
record = JSON.parse(line);
@@ -121,16 +178,25 @@ function collectAgentErrorEvidence(content, rawSourceEngine) {
121178
continue;
122179
}
123180
if (!transcriptBlock) {
124-
for (const value of Array.isArray(record) ? record : [record]) errors.push(...attributedErrors(value));
181+
const evidence = (Array.isArray(record) ? record : [record]).flatMap(recordEvidence);
182+
if (evidence.length && Array.isArray(record) && conversationDocuments.has(conversationDocumentKey(record))) continue;
183+
errors.push(...evidence);
125184
}
126185
}
127186
return { diagnostics, errors };
128187
}
129188

130-
/** @param {string} content @param {string} [rawSourceEngine] @returns {string} */
131-
function agentErrorDiagnosticText(content, rawSourceEngine) {
132-
const { diagnostics, errors } = collectAgentErrorEvidence(content, rawSourceEngine);
133-
return [...diagnostics, ...collectNativeErrorEvidence(errors).diagnostics].join("\n");
189+
/**
190+
* @param {string} content
191+
* @param {SessionEvent[] | string} [eventsOrSourceEngine]
192+
* @param {string} [rawSourceEngine]
193+
* @returns {string}
194+
*/
195+
function agentErrorDiagnosticText(content, eventsOrSourceEngine = [], rawSourceEngine) {
196+
const events = Array.isArray(eventsOrSourceEngine) ? eventsOrSourceEngine : [];
197+
const sourceEngine = typeof eventsOrSourceEngine === "string" ? eventsOrSourceEngine : rawSourceEngine;
198+
const { diagnostics, errors } = collectAgentErrorEvidence(content, events, sourceEngine);
199+
return [...diagnostics, ...collectNativeErrorEvidence([...events.flatMap(recordErrors), ...errors]).diagnostics].join("\n");
134200
}
135201

136202
const ENGINE_ERROR_SUMMARIES = Object.freeze({
@@ -158,11 +224,14 @@ const ENGINE_ERROR_SUMMARIES = Object.freeze({
158224
/**
159225
* Preserve actionable error classes without publishing raw log lines or payloads.
160226
* @param {string} content
227+
* @param {SessionEvent[] | string} [eventsOrSourceEngine]
161228
* @param {string} [rawSourceEngine]
162229
* @returns {string}
163230
*/
164-
function agentErrorSummaryText(content, rawSourceEngine) {
165-
const execution = collectAgentExecution({ content, rawSourceEngine });
231+
function agentErrorSummaryText(content, eventsOrSourceEngine = [], rawSourceEngine) {
232+
const events = Array.isArray(eventsOrSourceEngine) ? eventsOrSourceEngine : [];
233+
const sourceEngine = typeof eventsOrSourceEngine === "string" ? eventsOrSourceEngine : rawSourceEngine;
234+
const execution = collectAgentExecution({ content, events, rawSourceEngine: sourceEngine });
166235
return (execution?.data.categories || [])
167236
.filter(category => Object.hasOwn(ENGINE_ERROR_SUMMARIES, category))
168237
.map(category => `Engine error: ${ENGINE_ERROR_SUMMARIES[category]} (${category})`)
@@ -191,7 +260,7 @@ function collectNativeErrorEvidence(errors) {
191260
continue;
192261
}
193262
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
194-
for (const key of ["code", "error_code", "errorCode", "status", "error_status", "api_error_status"]) {
263+
for (const key of ["code", "error_code", "errorCode", "statusCode", "status_code", "status", "error_status", "api_error_status"]) {
195264
const code = value[key];
196265
if (key === "status" && typeof code === "string" && !/^\d{3}$/.test(code)) continue;
197266
if ((typeof code === "string" && code.length > 0) || Number.isSafeInteger(code)) codes.add(code);
@@ -219,7 +288,7 @@ function collectNativeErrorEvidence(errors) {
219288
*/
220289
function collectAgentExecution({ content = "", events = [], categories = [], exitCode, observations = [], rawSourceEngine } = {}) {
221290
const categorySet = new Set(categories);
222-
const { diagnostics, errors: rawErrors } = collectAgentErrorEvidence(content, rawSourceEngine);
291+
const { diagnostics, errors: rawErrors } = collectAgentErrorEvidence(content, events, rawSourceEngine);
223292
const errors = [...events.flatMap(recordErrors), ...rawErrors];
224293
const { diagnostics: nativeDiagnostics, codes, types } = collectNativeErrorEvidence(errors);
225294
let observedExit = exitCode;

0 commit comments

Comments
 (0)