feat: auto-suspend tasks on quota errors, add Claude progress tracking
- Auto-suspend tasks after 3 consecutive quota/auth errors with retry-after date parsing and Discord notification - Add tool_progress and tool_use_summary streaming from Claude Agent SDK - Prefix progress messages with invisible marker so bots ignore them - Fix misleading provider label in codex service logs
This commit is contained in:
@@ -32,6 +32,7 @@ interface ContainerInput {
|
||||
|
||||
interface ContainerOutput {
|
||||
status: 'success' | 'error';
|
||||
phase?: 'progress' | 'final';
|
||||
result: string | null;
|
||||
newSessionId?: string;
|
||||
error?: string;
|
||||
@@ -59,6 +60,11 @@ interface SDKUserMessage {
|
||||
session_id: string;
|
||||
}
|
||||
|
||||
interface AssistantContentBlock {
|
||||
type?: string;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
// Paths configurable via env vars.
|
||||
const GROUP_DIR = process.env.EJCLAW_GROUP_DIR || '/workspace/group';
|
||||
const IPC_DIR = process.env.EJCLAW_IPC_DIR || '/workspace/ipc';
|
||||
@@ -174,6 +180,25 @@ function log(message: string): void {
|
||||
console.error(`[agent-runner] ${message}`);
|
||||
}
|
||||
|
||||
function extractAssistantText(message: unknown): string | null {
|
||||
const assistant = message as {
|
||||
message?: {
|
||||
content?: AssistantContentBlock[];
|
||||
};
|
||||
};
|
||||
const blocks = assistant.message?.content;
|
||||
if (!Array.isArray(blocks)) return null;
|
||||
|
||||
const text = blocks
|
||||
.filter((block) => block?.type === 'text' && typeof block.text === 'string')
|
||||
.map((block) => block.text!.trim())
|
||||
.filter(Boolean)
|
||||
.join('\n\n')
|
||||
.trim();
|
||||
|
||||
return text || null;
|
||||
}
|
||||
|
||||
function getSessionSummary(sessionId: string, transcriptPath: string): string | null {
|
||||
const projectDir = path.dirname(transcriptPath);
|
||||
const indexPath = path.join(projectDir, 'sessions-index.json');
|
||||
@@ -525,6 +550,32 @@ async function runQuery(
|
||||
log(`Task notification: task=${tn.task_id} status=${tn.status} summary=${tn.summary}`);
|
||||
}
|
||||
|
||||
if (message.type === 'tool_progress') {
|
||||
const tp = message as {
|
||||
tool_name: string;
|
||||
elapsed_time_seconds: number;
|
||||
};
|
||||
const label = `${tp.tool_name} (${Math.round(tp.elapsed_time_seconds)}s)`;
|
||||
log(`Tool progress: ${label}`);
|
||||
writeOutput({
|
||||
status: 'success',
|
||||
phase: 'progress',
|
||||
result: label,
|
||||
newSessionId,
|
||||
});
|
||||
}
|
||||
|
||||
if (message.type === 'tool_use_summary') {
|
||||
const ts = message as { summary: string };
|
||||
log(`Tool use summary: ${ts.summary.slice(0, 200)}`);
|
||||
writeOutput({
|
||||
status: 'success',
|
||||
phase: 'progress',
|
||||
result: ts.summary,
|
||||
newSessionId,
|
||||
});
|
||||
}
|
||||
|
||||
if (message.type === 'result') {
|
||||
resultCount++;
|
||||
const textResult = 'result' in message ? (message as { result?: string }).result : null;
|
||||
@@ -569,6 +620,26 @@ async function runQuery(
|
||||
log('Terminal result observed, ending query stream');
|
||||
break;
|
||||
}
|
||||
|
||||
if (message.type === 'assistant') {
|
||||
const stopReason = (message as { stop_reason?: string }).stop_reason;
|
||||
const textResult = extractAssistantText(message);
|
||||
if (stopReason === 'end_turn' && textResult) {
|
||||
resultCount++;
|
||||
log(
|
||||
`Terminal assistant turn observed without result event (${textResult.length} chars), ending query stream`,
|
||||
);
|
||||
writeOutput({
|
||||
status: 'success',
|
||||
result: textResult,
|
||||
newSessionId,
|
||||
});
|
||||
terminalResultObserved = true;
|
||||
ipcPolling = false;
|
||||
stream.end();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ipcPolling = false;
|
||||
|
||||
Reference in New Issue
Block a user