Files
openclaw/src/agents/bash-tools.exec-task-tracking.ts
2026-07-18 09:35:14 +01:00

120 lines
3.5 KiB
TypeScript

// Projects detached exec processes into the durable task ledger used by clients.
import { createSubsystemLogger } from "../logging/subsystem.js";
import { BACKGROUND_EXEC_TASK_KIND } from "../tasks/background-exec-task-contract.js";
import { createRunningTaskRun, finalizeTaskRunByRunId } from "../tasks/detached-task-runtime.js";
import type { ExecProcessOutcome } from "./bash-tools.exec-runtime.js";
const log = createSubsystemLogger("agents/bash-exec-task-tracking");
export type BackgroundExecTaskHandle = {
taskId: string;
runId: string;
sessionKey: string;
};
export function createBackgroundExecTask(params: {
processSessionId: string;
sessionKey?: string;
agentId?: string;
startedAt: number;
}): BackgroundExecTaskHandle | null {
const sessionKey = params.sessionKey?.trim();
if (!sessionKey) {
return null;
}
const runId = `exec:${params.processSessionId}`;
try {
const task = createRunningTaskRun({
runtime: "cli",
taskKind: BACKGROUND_EXEC_TASK_KIND,
sourceId: params.processSessionId,
requesterSessionKey: sessionKey,
ownerKey: sessionKey,
scopeKind: "session",
agentId: params.agentId,
requesterAgentId: params.agentId,
runId,
label: "CLI command",
task: "Background CLI command",
notifyPolicy: "silent",
deliveryStatus: "not_applicable",
startedAt: params.startedAt,
lastEventAt: params.startedAt,
progressSummary: "Command running",
});
if (!task) {
return null;
}
return { taskId: task.taskId, runId, sessionKey };
} catch (error) {
log.warn("Failed to register background exec task", {
processSessionId: params.processSessionId,
error,
});
return null;
}
}
export function finalizeBackgroundExecTask(params: {
handle: BackgroundExecTaskHandle | null;
outcome: ExecProcessOutcome;
}): void {
if (!params.handle) {
return;
}
const endedAt = Date.now();
const status =
params.outcome.status === "completed"
? params.outcome.exitCode === 0
? "succeeded"
: "failed"
: params.outcome.timedOut
? "timed_out"
: params.outcome.exitReason === "manual-cancel"
? "cancelled"
: "failed";
try {
finalizeTaskRunByRunId({
runId: params.handle.runId,
runtime: "cli",
sessionKey: params.handle.sessionKey,
status,
endedAt,
lastEventAt: endedAt,
terminalSummary:
status === "succeeded"
? "Command completed"
: status === "failed"
? "Command failed"
: "Command stopped",
...(status === "succeeded" ? { clearError: true } : { error: execTaskError(params.outcome) }),
detail: {
exitCode: params.outcome.exitCode,
...(params.outcome.exitSignal != null
? { exitSignal: String(params.outcome.exitSignal) }
: {}),
...(params.outcome.status === "failed" ? { failureKind: params.outcome.failureKind } : {}),
},
});
} catch (error) {
log.warn("Failed to finalize background exec task", {
taskId: params.handle.taskId,
runId: params.handle.runId,
error,
});
}
}
function execTaskError(outcome: ExecProcessOutcome): string {
if (outcome.status === "completed") {
return `Command failed (exit code ${outcome.exitCode ?? "unknown"})`;
}
if (outcome.timedOut) {
return "Command timed out";
}
if (outcome.exitReason === "manual-cancel") {
return "Cancelled by operator";
}
return `Command failed (${outcome.failureKind})`;
}