{"version":3,"file":"workflow-graph.d.ts","sourceRoot":"","sources":["../../../../src/runs/shared/workflow-graph.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,SAAS,EAKd,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EACX,YAAY,EACZ,eAAe,EAEf,qBAAqB,EACrB,kBAAkB,EAClB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,uBAAuB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,UAAU,GAAG,UAAU,GAAG,aAAa,GAAG,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC;IACtG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1D,eAAe,CAAC,EAAE,MAAM,CACvB,MAAM,EACN,KAAK,CAAC;QACL,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC;KACf,CAAC,CACF,CAAC;IACF,oBAAoB,CAAC,EAAE,MAAM,CAC5B,MAAM,EACN;QAAE,MAAM,EAAE,kBAAkB,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC,CAAA;KAAE,CACvF,CAAC;CACF;AAkED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,uBAAuB,GAAG,qBAAqB,CA+KhG","sourcesContent":["import {\n\ttype ChainStep,\n\tisCheckpointStep,\n\tisDynamicParallelStep,\n\tisParallelStep,\n\ttype SequentialStep,\n} from \"../../shared/settings.ts\";\nimport type {\n\tSingleResult,\n\tSubagentRunMode,\n\tWorkflowGraphNode,\n\tWorkflowGraphSnapshot,\n\tWorkflowNodeStatus,\n} from \"../../shared/types.ts\";\n\nexport interface WorkflowGraphBuildInput {\n\trunId: string;\n\tmode?: SubagentRunMode;\n\tsteps: ChainStep[];\n\tresults?: Array<Pick<SingleResult, \"exitCode\" | \"detached\" | \"interrupted\" | \"error\" | \"acceptance\">>;\n\tcurrentFlatIndex?: number;\n\tcurrentStepIndex?: number;\n\tstepStatuses?: Array<{ status?: string; error?: string }>;\n\tdynamicChildren?: Record<\n\t\tnumber,\n\t\tArray<{\n\t\t\tagent: string;\n\t\t\tlabel?: string;\n\t\t\tflatIndex: number;\n\t\t\titemKey: string;\n\t\t\toutputName?: string;\n\t\t\tstructured?: boolean;\n\t\t\terror?: string;\n\t\t}>\n\t>;\n\tdynamicGroupStatuses?: Record<\n\t\tnumber,\n\t\t{ status: WorkflowNodeStatus; error?: string; acceptance?: SingleResult[\"acceptance\"] }\n\t>;\n}\n\nfunction normalizeStatus(status: string | undefined): WorkflowNodeStatus | undefined {\n\tswitch (status) {\n\t\tcase \"complete\":\n\t\tcase \"completed\":\n\t\t\treturn \"completed\";\n\t\tcase \"running\":\n\t\t\treturn \"running\";\n\t\tcase \"failed\":\n\t\t\treturn \"failed\";\n\t\tcase \"paused\":\n\t\t\treturn \"paused\";\n\t\tcase \"detached\":\n\t\t\treturn \"detached\";\n\t\tcase \"rejected\":\n\t\t\treturn \"rejected\";\n\t\tcase \"pending\":\n\t\t\treturn \"pending\";\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nfunction resultStatus(\n\tresult: Pick<SingleResult, \"exitCode\" | \"detached\" | \"interrupted\"> | undefined,\n): WorkflowNodeStatus | undefined {\n\tif (!result) return undefined;\n\tif (result.detached) return \"detached\";\n\tif (result.interrupted) return \"paused\";\n\treturn result.exitCode === 0 ? \"completed\" : \"failed\";\n}\n\nfunction nodeStatus(input: WorkflowGraphBuildInput, flatIndex: number): WorkflowNodeStatus {\n\treturn (\n\t\tnormalizeStatus(input.stepStatuses?.[flatIndex]?.status) ??\n\t\tresultStatus(input.results?.[flatIndex]) ??\n\t\t(input.currentFlatIndex === flatIndex ? \"running\" : \"pending\")\n\t);\n}\n\nfunction pushPhase(phases: WorkflowGraphSnapshot[\"phases\"], phase: string | undefined, nodeId: string): void {\n\tif (!phase) return;\n\tlet group = phases.find((candidate) => candidate.title === phase);\n\tif (!group) {\n\t\tgroup = { title: phase, nodeIds: [] };\n\t\tphases.push(group);\n\t}\n\tgroup.nodeIds.push(nodeId);\n}\n\nfunction seqLabel(step: SequentialStep, stepIndex: number): string {\n\treturn step.label?.trim() || step.agent || `Step ${stepIndex + 1}`;\n}\n\nfunction summarizeParallelStatuses(statuses: WorkflowNodeStatus[]): WorkflowNodeStatus {\n\tif (statuses.some((status) => status === \"running\")) return \"running\";\n\tif (statuses.some((status) => status === \"rejected\")) return \"rejected\";\n\tif (statuses.some((status) => status === \"failed\")) return \"failed\";\n\tif (statuses.some((status) => status === \"paused\")) return \"paused\";\n\tif (statuses.some((status) => status === \"detached\")) return \"detached\";\n\tif (statuses.length > 0 && statuses.every((status) => status === \"completed\")) return \"completed\";\n\tif (statuses.some((status) => status === \"completed\")) return \"running\";\n\treturn \"pending\";\n}\n\nexport function buildWorkflowGraphSnapshot(input: WorkflowGraphBuildInput): WorkflowGraphSnapshot {\n\tconst nodes: WorkflowGraphNode[] = [];\n\tconst phases: WorkflowGraphSnapshot[\"phases\"] = [];\n\tlet flatIndex = 0;\n\tlet currentNodeId: string | undefined;\n\n\tfor (let stepIndex = 0; stepIndex < input.steps.length; stepIndex++) {\n\t\tconst step = input.steps[stepIndex]!;\n\t\tif (isCheckpointStep(step)) {\n\t\t\tconst status =\n\t\t\t\tnormalizeStatus(input.stepStatuses?.[flatIndex]?.status) ??\n\t\t\t\t(input.currentStepIndex === stepIndex ? \"paused\" : \"pending\");\n\t\t\tconst checkpoint = {\n\t\t\t\tname: step.checkpoint,\n\t\t\t\t...(step.message ? { message: step.message } : {}),\n\t\t\t\tstatus:\n\t\t\t\t\tstatus === \"rejected\"\n\t\t\t\t\t\t? (\"rejected\" as const)\n\t\t\t\t\t\t: status === \"completed\"\n\t\t\t\t\t\t\t? (\"approved\" as const)\n\t\t\t\t\t\t\t: (\"pending\" as const),\n\t\t\t\tstepIndex,\n\t\t\t};\n\t\t\tconst id = `step-${stepIndex}`;\n\t\t\tnodes.push({\n\t\t\t\tid,\n\t\t\t\tkind: \"checkpoint\",\n\t\t\t\tphase: step.phase,\n\t\t\t\tlabel: step.label?.trim() || step.checkpoint,\n\t\t\t\tstatus,\n\t\t\t\tflatIndex,\n\t\t\t\tstepIndex,\n\t\t\t\tcheckpoint,\n\t\t\t\terror: input.stepStatuses?.[flatIndex]?.error,\n\t\t\t});\n\t\t\tpushPhase(phases, step.phase, id);\n\t\t\tif (status === \"paused\" || input.currentStepIndex === stepIndex) currentNodeId = id;\n\t\t\tflatIndex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (isParallelStep(step)) {\n\t\t\tconst groupId = `step-${stepIndex}`;\n\t\t\tconst children: WorkflowGraphNode[] = [];\n\t\t\tconst childStatuses: WorkflowNodeStatus[] = [];\n\t\t\tfor (let taskIndex = 0; taskIndex < step.parallel.length; taskIndex++) {\n\t\t\t\tconst task = step.parallel[taskIndex]!;\n\t\t\t\tconst status = nodeStatus(input, flatIndex);\n\t\t\t\tchildStatuses.push(status);\n\t\t\t\tconst childId = `step-${stepIndex}-agent-${taskIndex}`;\n\t\t\t\tconst child: WorkflowGraphNode = {\n\t\t\t\t\tid: childId,\n\t\t\t\t\tkind: \"agent\",\n\t\t\t\t\tagent: task.agent,\n\t\t\t\t\tphase: task.phase,\n\t\t\t\t\tlabel: task.label?.trim() || task.agent || `Agent ${taskIndex + 1}`,\n\t\t\t\t\tstatus,\n\t\t\t\t\tflatIndex,\n\t\t\t\t\tstepIndex,\n\t\t\t\t\toutputName: task.as,\n\t\t\t\t\tstructured: Boolean(task.outputSchema),\n\t\t\t\t\tacceptanceStatus: input.results?.[flatIndex]?.acceptance?.status,\n\t\t\t\t\terror: input.stepStatuses?.[flatIndex]?.error ?? input.results?.[flatIndex]?.error,\n\t\t\t\t};\n\t\t\t\tchildren.push(child);\n\t\t\t\tpushPhase(phases, task.phase, childId);\n\t\t\t\tif (status === \"running\" || input.currentFlatIndex === flatIndex) currentNodeId = childId;\n\t\t\t\tflatIndex++;\n\t\t\t}\n\t\t\tconst groupStatus = summarizeParallelStatuses(childStatuses);\n\t\t\tif (input.currentStepIndex === stepIndex && !currentNodeId) currentNodeId = groupId;\n\t\t\tnodes.push({\n\t\t\t\tid: groupId,\n\t\t\t\tkind: \"parallel-group\",\n\t\t\t\tlabel: step.parallel.length === 1 ? \"Parallel task\" : `Parallel group (${step.parallel.length})`,\n\t\t\t\tstatus: groupStatus,\n\t\t\t\tstepIndex,\n\t\t\t\tchildren,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (isDynamicParallelStep(step)) {\n\t\t\tconst groupId = `step-${stepIndex}`;\n\t\t\tconst materialized = input.dynamicChildren?.[stepIndex] ?? [];\n\t\t\tconst groupOverride = input.dynamicGroupStatuses?.[stepIndex];\n\t\t\tconst children: WorkflowGraphNode[] = [];\n\t\t\tconst childStatuses: WorkflowNodeStatus[] = [];\n\t\t\tfor (let taskIndex = 0; taskIndex < materialized.length; taskIndex++) {\n\t\t\t\tconst task = materialized[taskIndex]!;\n\t\t\t\tconst status = nodeStatus(input, task.flatIndex);\n\t\t\t\tchildStatuses.push(status);\n\t\t\t\tconst childId = `step-${stepIndex}-item-${task.itemKey.replace(/[^a-zA-Z0-9_-]/g, \"-\")}`;\n\t\t\t\tconst child: WorkflowGraphNode = {\n\t\t\t\t\tid: childId,\n\t\t\t\t\tkind: \"agent\",\n\t\t\t\t\tagent: task.agent,\n\t\t\t\t\tphase: step.parallel.phase ?? step.phase,\n\t\t\t\t\tlabel: task.label?.trim() || step.parallel.label?.trim() || `${task.agent} ${task.itemKey}`,\n\t\t\t\t\tstatus,\n\t\t\t\t\tflatIndex: task.flatIndex,\n\t\t\t\t\tstepIndex,\n\t\t\t\t\titemKey: task.itemKey,\n\t\t\t\t\toutputName: task.outputName,\n\t\t\t\t\tstructured: task.structured,\n\t\t\t\t\tacceptanceStatus: input.results?.[task.flatIndex]?.acceptance?.status,\n\t\t\t\t\terror:\n\t\t\t\t\t\tinput.stepStatuses?.[task.flatIndex]?.error ?? input.results?.[task.flatIndex]?.error ?? task.error,\n\t\t\t\t};\n\t\t\t\tchildren.push(child);\n\t\t\t\tpushPhase(phases, child.phase, childId);\n\t\t\t\tif (status === \"running\" || input.currentFlatIndex === task.flatIndex) currentNodeId = childId;\n\t\t\t}\n\t\t\tconst groupStatus =\n\t\t\t\tgroupOverride?.status ??\n\t\t\t\t(children.length > 0\n\t\t\t\t\t? summarizeParallelStatuses(childStatuses)\n\t\t\t\t\t: input.currentStepIndex === stepIndex\n\t\t\t\t\t\t? \"running\"\n\t\t\t\t\t\t: \"pending\");\n\t\t\tif (input.currentStepIndex === stepIndex && !currentNodeId) currentNodeId = groupId;\n\t\t\tnodes.push({\n\t\t\t\tid: groupId,\n\t\t\t\tkind: \"dynamic-parallel-group\",\n\t\t\t\tlabel: step.label?.trim() || step.parallel.label?.trim() || `Dynamic fanout (${step.collect.as})`,\n\t\t\t\tstatus: groupStatus,\n\t\t\t\tstepIndex,\n\t\t\t\toutputName: step.collect.as,\n\t\t\t\tstructured: Boolean(step.collect.outputSchema),\n\t\t\t\tacceptanceStatus: groupOverride?.acceptance?.status,\n\t\t\t\terror: groupOverride?.error,\n\t\t\t\tdynamic: {\n\t\t\t\t\tsourceOutput: step.expand.from.output,\n\t\t\t\t\tsourcePath: step.expand.from.path,\n\t\t\t\t\titemName: step.expand.item ?? \"item\",\n\t\t\t\t\tmaxItems: step.expand.maxItems,\n\t\t\t\t\tcollectAs: step.collect.as,\n\t\t\t\t},\n\t\t\t\tchildren,\n\t\t\t});\n\t\t\tif (materialized.length > 0)\n\t\t\t\tflatIndex = Math.max(flatIndex, ...materialized.map((child) => child.flatIndex + 1));\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst seq = step as SequentialStep;\n\t\tconst status = nodeStatus(input, flatIndex);\n\t\tconst id = `step-${stepIndex}`;\n\t\tnodes.push({\n\t\t\tid,\n\t\t\tkind: \"step\",\n\t\t\tagent: seq.agent,\n\t\t\tphase: seq.phase,\n\t\t\tlabel: seqLabel(seq, stepIndex),\n\t\t\tstatus,\n\t\t\tflatIndex,\n\t\t\tstepIndex,\n\t\t\toutputName: seq.as,\n\t\t\tstructured: Boolean(seq.outputSchema),\n\t\t\tacceptanceStatus: input.results?.[flatIndex]?.acceptance?.status,\n\t\t\terror: input.stepStatuses?.[flatIndex]?.error ?? input.results?.[flatIndex]?.error,\n\t\t});\n\t\tpushPhase(phases, seq.phase, id);\n\t\tif (status === \"running\" || input.currentFlatIndex === flatIndex || input.currentStepIndex === stepIndex)\n\t\t\tcurrentNodeId = id;\n\t\tflatIndex++;\n\t}\n\n\treturn {\n\t\trunId: input.runId,\n\t\tmode: input.mode ?? \"chain\",\n\t\tphases,\n\t\tnodes,\n\t\tcurrentNodeId,\n\t};\n}\n"]}