fix: 增强终态失败分类可见性
This commit is contained in:
+115
-4
@@ -96,7 +96,7 @@ async function dispatch(args: ParsedArgs): Promise<CliResult> {
|
||||
if (group === "queue" && command === "dispatch" && id) return dispatchQueueTask(args, id);
|
||||
if (group === "queue" && command === "refresh" && id) return refreshQueueTask(args, id);
|
||||
if (group === "runs" && command === "create") return client(args).post("/api/v1/runs", await jsonFile(args));
|
||||
if (group === "runs" && command === "show" && id) return client(args).get(`/api/v1/runs/${encodeURIComponent(id)}`);
|
||||
if (group === "runs" && command === "show" && id) return showRun(args, id);
|
||||
if (group === "runs" && command === "events" && id) return runEvents(args, id);
|
||||
if (group === "runs" && command === "result" && id) {
|
||||
const commandId = optionalFlag(args, "command-id");
|
||||
@@ -113,7 +113,7 @@ async function dispatch(args: ParsedArgs): Promise<CliResult> {
|
||||
if (group === "commands" && command === "show" && id) {
|
||||
const runId = flag(args, "run-id", "");
|
||||
if (!runId) throw new AgentRunError("schema-invalid", "commands show requires --run-id", { httpStatus: 2 });
|
||||
return client(args).get(`/api/v1/runs/${encodeURIComponent(runId)}/commands/${encodeURIComponent(id)}`);
|
||||
return showCommand(args, runId, id);
|
||||
}
|
||||
if (group === "commands" && command === "result" && id) {
|
||||
const runId = flag(args, "run-id", "");
|
||||
@@ -159,6 +159,102 @@ async function listRunnerJobs(args: ParsedArgs): Promise<JsonValue> {
|
||||
return client(args).get(`/api/v1/runs/${encodeURIComponent(runId)}/runner-jobs${commandId ? `?commandId=${encodeURIComponent(commandId)}` : ""}`);
|
||||
}
|
||||
|
||||
async function showRun(args: ParsedArgs, runId: string): Promise<JsonValue> {
|
||||
const run = await client(args).get(`/api/v1/runs/${encodeURIComponent(runId)}`);
|
||||
if (wantsExpandedOutput(args)) return run;
|
||||
const commandId = optionalFlag(args, "command-id");
|
||||
const result = await client(args).get(`/api/v1/runs/${encodeURIComponent(runId)}/result${commandId ? `?commandId=${encodeURIComponent(commandId)}` : ""}`);
|
||||
return summarizeRunShowResult(run, result, runId, commandId);
|
||||
}
|
||||
|
||||
async function showCommand(args: ParsedArgs, runId: string, commandId: string): Promise<JsonValue> {
|
||||
const command = await client(args).get(`/api/v1/runs/${encodeURIComponent(runId)}/commands/${encodeURIComponent(commandId)}`);
|
||||
if (wantsExpandedOutput(args)) return command;
|
||||
const result = await client(args).get(`/api/v1/runs/${encodeURIComponent(runId)}/commands/${encodeURIComponent(commandId)}/result`);
|
||||
return summarizeCommandShowResult(command, result, runId, commandId);
|
||||
}
|
||||
|
||||
function summarizeRunShowResult(run: JsonValue, result: JsonValue, runId: string, commandId: string | null): JsonRecord {
|
||||
const resultRecord = jsonRecordValue(result);
|
||||
const resolvedCommandId = commandId ?? stringValue(resultRecord?.commandId);
|
||||
const sessionId = stringValue(jsonRecordValue(resultRecord?.sessionRef)?.sessionId);
|
||||
const lastSeq = numberValue(resultRecord?.lastSeq) ?? 0;
|
||||
return {
|
||||
action: "runs-show-summary",
|
||||
run: summarizeRunRecord(jsonRecordValue(run)),
|
||||
result: summarizeResultEnvelope(resultRecord),
|
||||
terminalClassification: summarizeTerminalClassificationIfPresent(resultRecord),
|
||||
liveness: summarizeLivenessRecord(jsonRecordValue(resultRecord?.liveness)),
|
||||
finalResponse: summarizeFinalResponseRecord(jsonRecordValue(resultRecord?.finalResponse)),
|
||||
fullResponseBytes: jsonByteLength({ run, result }),
|
||||
valuesPrinted: false,
|
||||
drillDownCommands: runCommandDrillDown(runId, resolvedCommandId, sessionId, lastSeq),
|
||||
};
|
||||
}
|
||||
|
||||
function summarizeCommandShowResult(command: JsonValue, result: JsonValue, runId: string, commandId: string): JsonRecord {
|
||||
const resultRecord = jsonRecordValue(result);
|
||||
const sessionId = stringValue(jsonRecordValue(resultRecord?.sessionRef)?.sessionId);
|
||||
const lastSeq = numberValue(resultRecord?.lastSeq) ?? 0;
|
||||
return {
|
||||
action: "commands-show-summary",
|
||||
command: summarizeCommandRecord(jsonRecordValue(command)),
|
||||
result: summarizeResultEnvelope(resultRecord),
|
||||
terminalClassification: summarizeTerminalClassificationIfPresent(resultRecord),
|
||||
liveness: summarizeLivenessRecord(jsonRecordValue(resultRecord?.liveness)),
|
||||
finalResponse: summarizeFinalResponseRecord(jsonRecordValue(resultRecord?.finalResponse)),
|
||||
fullResponseBytes: jsonByteLength({ command, result }),
|
||||
valuesPrinted: false,
|
||||
drillDownCommands: runCommandDrillDown(runId, commandId, sessionId, lastSeq),
|
||||
};
|
||||
}
|
||||
|
||||
function summarizeResultEnvelope(record: JsonRecord | null): JsonRecord | null {
|
||||
if (!record) return null;
|
||||
return withoutFullRecordBytes(compactRecord(record, {
|
||||
keys: ["runId", "commandId", "attemptId", "runnerId", "jobName", "namespace", "status", "runStatus", "commandState", "terminalStatus", "terminalSource", "completed", "failureKind", "failureMessage", "lastSeq", "eventCount", "scopedEventCount", "scopedLastSeq", "runnerJobCount"],
|
||||
}));
|
||||
}
|
||||
|
||||
function summarizeTerminalClassificationIfPresent(record: JsonRecord | null): JsonRecord | null {
|
||||
const liveness = jsonRecordValue(record?.liveness);
|
||||
const classification = jsonRecordValue(record?.terminalClassification) ?? jsonRecordValue(liveness?.terminalClassification);
|
||||
return classification ? summarizeTerminalClassification(classification) : null;
|
||||
}
|
||||
|
||||
function summarizeLivenessRecord(record: JsonRecord | null): JsonRecord | null {
|
||||
if (!record) return null;
|
||||
const lastActivity = jsonRecordValue(record.lastActivity ?? record.lastCommandActivity);
|
||||
const timeoutBudget = jsonRecordValue(record.timeoutBudget);
|
||||
const transportDisconnect = jsonRecordValue(record.transportDisconnect);
|
||||
return {
|
||||
...withoutFullRecordBytes(compactRecord(record, { keys: ["phase", "active", "runStatus", "commandId", "commandType", "commandState", "lastSeq", "lastEventAt", "lastEventAgeMs"] })),
|
||||
terminalClassification: summarizeTerminalClassificationIfPresent({ terminalClassification: record.terminalClassification ?? null }),
|
||||
lastActivity: lastActivity ? withoutFullRecordBytes(compactRecord(lastActivity, { keys: ["sourceSeq", "eventId", "activityKind", "type", "status", "toolName", "itemId", "ageMs", "summary"] })) : null,
|
||||
timeoutBudget: timeoutBudget ? withoutFullRecordBytes(compactRecord(timeoutBudget, { keys: ["state", "timeoutMs", "elapsedMs", "remainingMs", "startedAt", "source"] })) : null,
|
||||
transportDisconnect: transportDisconnect ? withoutFullRecordBytes(compactRecord(transportDisconnect, { keys: ["sourceSeq", "eventId", "activityKind", "type", "status", "ageMs", "summary"] })) : null,
|
||||
recoveryActions: summarizeRecoveryActions(record.recoveryActions),
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
function summarizeFinalResponseRecord(record: JsonRecord | null): JsonRecord | null {
|
||||
if (!record) return null;
|
||||
return withoutFullRecordBytes(compactRecord(record, { keys: ["seq", "source", "final", "replyAuthority", "textTruncated", "outputTruncated", "text"] }));
|
||||
}
|
||||
|
||||
function runCommandDrillDown(runId: string, commandId: string | null, sessionId: string | null, lastSeq: number): JsonRecord {
|
||||
return {
|
||||
run: `./scripts/agentrun runs show ${runId}`,
|
||||
runFull: `./scripts/agentrun runs show ${runId} --full`,
|
||||
result: `./scripts/agentrun runs result ${runId}${commandId ? ` --command-id ${commandId}` : ""}`,
|
||||
events: `./scripts/agentrun runs events ${runId} --after-seq ${lastSeq} --limit 100 --tail-summary`,
|
||||
...(commandId ? { command: `./scripts/agentrun commands show ${commandId} --run-id ${runId}`, commandResult: `./scripts/agentrun commands result ${commandId} --run-id ${runId}` } : {}),
|
||||
...(sessionId ? { trace: `./scripts/agentrun sessions trace ${sessionId} --after-seq ${lastSeq} --limit 100 --run-id ${runId}`, output: `./scripts/agentrun sessions output ${sessionId} --after-seq ${lastSeq} --limit 100 --run-id ${runId}` } : {}),
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
async function runEvents(args: ParsedArgs, runId: string): Promise<CliResult> {
|
||||
const afterSeq = integerFlag(args, "after-seq", 0, { min: 0 });
|
||||
const limit = integerFlag(args, "limit", 100, { min: 1, max: 500 });
|
||||
@@ -593,7 +689,11 @@ export function summarizeQueueTaskListResult(result: JsonValue, options: QueueSu
|
||||
export function summarizeQueueTaskShowResult(result: JsonValue, taskId: string): JsonRecord {
|
||||
const record = jsonRecordValue(result);
|
||||
if (!record) throw new AgentRunError("schema-invalid", "queue show response must be an object", { httpStatus: 2 });
|
||||
const sessionId = stringValue(jsonRecordValue(record.sessionRef)?.sessionId) ?? stringValue(jsonRecordValue(record.latestAttempt)?.sessionId);
|
||||
const latestAttempt = jsonRecordValue(record.latestAttempt);
|
||||
const runId = stringValue(latestAttempt?.runId);
|
||||
const commandId = stringValue(latestAttempt?.commandId);
|
||||
const sessionId = stringValue(jsonRecordValue(record.sessionRef)?.sessionId) ?? stringValue(latestAttempt?.sessionId);
|
||||
const afterSeq = numberValue(jsonRecordValue(record.supervisor)?.lastSeq) ?? 0;
|
||||
return {
|
||||
action: "queue-show-summary",
|
||||
task: summarizeQueueTaskWithAttempt(record, taskId),
|
||||
@@ -605,6 +705,8 @@ export function summarizeQueueTaskShowResult(result: JsonValue, taskId: string):
|
||||
valuesPrinted: false,
|
||||
pollCommands: {
|
||||
full: `./scripts/agentrun queue show ${taskId} --full`,
|
||||
...(runId ? { run: `./scripts/agentrun runs show ${runId}`, events: `./scripts/agentrun runs events ${runId} --after-seq ${afterSeq} --limit 100 --tail-summary` } : {}),
|
||||
...(runId && commandId ? { command: `./scripts/agentrun commands show ${commandId} --run-id ${runId}` } : {}),
|
||||
...(sessionId ? { trace: `./scripts/agentrun sessions trace ${sessionId} --after-seq 0 --limit 100`, output: `./scripts/agentrun sessions output ${sessionId} --after-seq 0 --limit 100` } : {}),
|
||||
},
|
||||
};
|
||||
@@ -633,6 +735,9 @@ export function summarizeQueueCommanderSnapshot(result: JsonValue, options: Queu
|
||||
full: "./scripts/agentrun queue commander --reader-id cli --full",
|
||||
raw: "./scripts/agentrun queue commander --reader-id cli --raw",
|
||||
item: "./scripts/agentrun queue show <taskId>",
|
||||
run: "./scripts/agentrun runs show <runId>",
|
||||
events: "./scripts/agentrun runs events <runId> --after-seq <lastSeq> --limit 100 --tail-summary",
|
||||
command: "./scripts/agentrun commands show <commandId> --run-id <runId>",
|
||||
trace: "./scripts/agentrun sessions trace <sessionId> --after-seq 0 --limit 100",
|
||||
output: "./scripts/agentrun sessions output <sessionId> --after-seq 0 --limit 100",
|
||||
},
|
||||
@@ -795,8 +900,10 @@ function summarizeSupervisorRecord(record: JsonRecord | null): JsonRecord | null
|
||||
if (!record) return null;
|
||||
const lastActivity = jsonRecordValue(record.lastActivity);
|
||||
const timeoutBudget = jsonRecordValue(record.timeoutBudget);
|
||||
const terminalClassification = jsonRecordValue(record.terminalClassification);
|
||||
return {
|
||||
...withoutFullRecordBytes(compactRecord(record, { keys: ["phase", "active", "status", "terminalStatus", "failureKind", "runId", "commandId", "lastSeq"] })),
|
||||
terminalClassification: terminalClassification ? summarizeTerminalClassification(terminalClassification) : null,
|
||||
lastActivity: lastActivity ? withoutFullRecordBytes(compactRecord(lastActivity, { keys: ["sourceSeq", "eventId", "activityKind", "type", "status", "toolName", "itemId", "ageMs", "summary"] })) : null,
|
||||
timeoutBudget: timeoutBudget ? withoutFullRecordBytes(compactRecord(timeoutBudget, { keys: ["state", "timeoutMs", "elapsedMs", "remainingMs", "startedAt", "source"] })) : null,
|
||||
recoveryActions: summarizeRecoveryActions(record.recoveryActions),
|
||||
@@ -804,6 +911,10 @@ function summarizeSupervisorRecord(record: JsonRecord | null): JsonRecord | null
|
||||
};
|
||||
}
|
||||
|
||||
function summarizeTerminalClassification(record: JsonRecord): JsonRecord {
|
||||
return withoutFullRecordBytes(compactRecord(record, { keys: ["category", "confidence", "providerEvidence", "providerInterruption", "providerInterruptionKnown", "providerInterruptionReason", "hardTimeout", "transportDisconnectObserved", "transportDisconnectSeq", "reason"] }));
|
||||
}
|
||||
|
||||
function summarizeRecoveryActions(value: JsonValue | undefined): JsonValue[] {
|
||||
if (!Array.isArray(value)) return [];
|
||||
return value.slice(0, 5).map((item) => withoutFullRecordBytes(compactRecord(jsonRecordValue(item), { keys: ["action", "reason", "runId", "commandId", "sessionId", "afterSeq", "hint"] })));
|
||||
@@ -821,7 +932,7 @@ function summarizeRunRecord(record: JsonRecord | null): JsonRecord | null {
|
||||
|
||||
function summarizeCommandRecord(record: JsonRecord | null): JsonRecord | null {
|
||||
if (!record) return null;
|
||||
return compactRecord(record, { keys: ["id", "runId", "seq", "type", "state", "createdAt", "updatedAt", "acknowledgedAt"] });
|
||||
return compactRecord(record, { keys: ["id", "runId", "seq", "type", "state", "terminalStatus", "failureKind", "failureMessage", "createdAt", "updatedAt", "acknowledgedAt"] });
|
||||
}
|
||||
|
||||
function summarizeRunnerJobRecord(record: JsonRecord | null): JsonRecord | null {
|
||||
|
||||
Reference in New Issue
Block a user