| 164 | } |
| 165 | |
| 166 | export function writeHandoff(args: { |
| 167 | handoffsDir: string; |
| 168 | task: TaskState; |
| 169 | body: string; |
| 170 | resultStatus: string; |
| 171 | finishedAt: string; |
| 172 | }): string { |
| 173 | const s = args.task; |
| 174 | const path = join(args.handoffsDir, `${s.name}.md`); |
| 175 | const header = `<!-- orchestrate handoff |
| 176 | task: ${s.name} |
| 177 | branch: ${s.branch} |
| 178 | agentId: ${s.agentId} |
| 179 | runId: ${s.runId} |
| 180 | resultStatus: ${args.resultStatus} |
| 181 | finishedAt: ${args.finishedAt} |
| 182 | -->\n\n`; |
| 183 | // Non-"finished" runs don't produce the structured template; flag it so |
| 184 | // downstream readers don't mis-parse the raw narrative. |
| 185 | const banner = |
| 186 | args.resultStatus === "finished" |
| 187 | ? "" |
| 188 | : `> ⚠️ Run ended with \`status=${args.resultStatus}\`. No structured handoff produced — the content below is the worker's raw output up to the point of failure.\n\n`; |
| 189 | const tmp = `${path}.tmp`; |
| 190 | writeFileSync( |
| 191 | tmp, |
| 192 | header + banner + (args.body || "_(empty final message)_\n") |
| 193 | ); |
| 194 | renameSync(tmp, path); |
| 195 | return path; |
| 196 | } |
| 197 | |
| 198 | export function emptyErrorHandoffBody(args: { |
| 199 | task: TaskState; |