* Terminal settlement for a child task whose stream failed with a * non-retryable error: mark interrupted with a descriptive launch error, * persist a durable failure artifact in every ancestor session dir (so * background children, restarts, and post-cleanup task_awaits observe the * ty
(
workspaceId: string,
entry: { projectPath: string; workspace: WorkspaceConfigEntry },
failure: { errorType: string; errorMessage: string }
)
| 8502 | * typed failure), then reject pending waiters with the failure message. |
| 8503 | */ |
| 8504 | private async failAgentTaskTerminally( |
| 8505 | workspaceId: string, |
| 8506 | entry: { projectPath: string; workspace: WorkspaceConfigEntry }, |
| 8507 | failure: { errorType: string; errorMessage: string } |
| 8508 | ): Promise<void> { |
| 8509 | assert(workspaceId.length > 0, "failAgentTaskTerminally: workspaceId must be non-empty"); |
| 8510 | assert( |
| 8511 | failure.errorMessage.length > 0, |
| 8512 | "failAgentTaskTerminally: errorMessage must be non-empty" |
| 8513 | ); |
| 8514 | |
| 8515 | await this.editWorkspaceEntry( |
| 8516 | workspaceId, |
| 8517 | (ws) => { |
| 8518 | ws.taskStatus = "interrupted"; |
| 8519 | ws.taskLaunchError = failure.errorMessage; |
| 8520 | }, |
| 8521 | { allowMissing: true } |
| 8522 | ); |
| 8523 | await this.emitWorkspaceMetadata(workspaceId); |
| 8524 | |
| 8525 | const parentWorkspaceId = entry.workspace.parentWorkspaceId; |
| 8526 | if (parentWorkspaceId) { |
| 8527 | const cfg = this.config.loadConfigOrDefault(); |
| 8528 | const index = this.buildAgentTaskIndex(cfg); |
| 8529 | const ancestorWorkspaceIds = this.listAncestorWorkspaceIdsUsingParentById( |
| 8530 | index.parentById, |
| 8531 | workspaceId |
| 8532 | ); |
| 8533 | const workflowOwnedAncestorWorkspaceIds = ancestorWorkspaceIds.filter( |
| 8534 | (ancestorWorkspaceId) => |
| 8535 | this.getWorkflowOwnedDescendantAgentTaskUsingIndex( |
| 8536 | index, |
| 8537 | ancestorWorkspaceId, |
| 8538 | workspaceId |
| 8539 | ) === true |
| 8540 | ); |
| 8541 | |
| 8542 | const persistedAtMs = Date.now(); |
| 8543 | for (const ancestorWorkspaceId of ancestorWorkspaceIds) { |
| 8544 | try { |
| 8545 | await upsertSubagentFailureArtifact({ |
| 8546 | workspaceId: ancestorWorkspaceId, |
| 8547 | workspaceSessionDir: this.config.getSessionDir(ancestorWorkspaceId), |
| 8548 | childTaskId: workspaceId, |
| 8549 | parentWorkspaceId, |
| 8550 | ancestorWorkspaceIds, |
| 8551 | workflowOwnedAncestorWorkspaceIds, |
| 8552 | errorType: failure.errorType, |
| 8553 | errorMessage: failure.errorMessage, |
| 8554 | model: entry.workspace.taskModelString, |
| 8555 | nowMs: persistedAtMs, |
| 8556 | }); |
| 8557 | } catch (error: unknown) { |
| 8558 | log.error("Failed to persist subagent failure artifact", { |
| 8559 | workspaceId: ancestorWorkspaceId, |
| 8560 | childTaskId: workspaceId, |
| 8561 | error, |
no test coverage detected