acpus 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,2 +1,243 @@
1
- import { $ as ReduceStageSchema, A as renderStagePrompt, B as DecisionRuleSchema, C as collectWorkflowOutputCandidates, D as repairFailedEnvelope, E as isRepairableOutputFailure, F as ArtifactSchema, G as FindingSchema, H as FanoutLaneGroupSchema, I as BaseOutputSchema, J as ImplementationOutputSchema, K as GateOutputSchema, L as CheckSchema, M as topologicalOrder, N as EXECUTION_PLAN_VERSION, O as compileExecutionPlan, P as AgentTaskStageSchema, Q as LoopStageSchema, R as ConditionSchema, S as syncRun, T as formatRepairPrompt, U as FanoutLaneSchema, V as DiscoverStageSchema, W as FanoutStageSchema, X as InputTypeSchema, Y as InputDeclarationSchema, Z as LoopBodyStageSchema, _t as RUN_DIAGNOSTICS_VIEW_VERSION, a as previewRunView, at as StageLimitsSchema, b as issue, bt as readNdjsonTail, c as TASK_DETAIL_VIEW_VERSION, ct as TransformSchema, dt as WorkflowLimitsSchema, et as RoleCategorySchema, ft as WorkflowSpecSchema, g as lintWorkflowSpec, h as loadWorkflowSpec, ht as OutputContractNameSchema, i as estimateFanoutWork, it as SeverityCountsSchema, j as stageRoleName, k as renderPromptMap, l as buildRunMonitorView, lt as ValidationOutputSchema, mt as getOutputContract, n as startPreparedRun, nt as RoleSchema, o as runViewFromIndex, ot as StageSchema, pt as contractNameForStage, q as GateStageSchema, r as estimateAgentCalls, rt as SCHEMA_VERSION, s as RUN_MONITOR_VIEW_VERSION, st as SummarizeStageSchema, t as prepareRun, tt as RoleModeSchema, u as buildTaskDetailView, ut as VariableSchema, vt as RunDiagnosticCodes, w as parseWorkflowOutput, x as resultFromIssues, y as OrchestratorError, yt as buildRunDiagnosticsView, z as DecisionGateStageSchema } from "./run-workflow-CbxKhAqF.mjs";
2
- export { AgentTaskStageSchema, ArtifactSchema, BaseOutputSchema, CheckSchema, ConditionSchema, DecisionGateStageSchema, DecisionRuleSchema, DiscoverStageSchema, EXECUTION_PLAN_VERSION, FanoutLaneGroupSchema, FanoutLaneSchema, FanoutStageSchema, FindingSchema, GateOutputSchema, GateStageSchema, ImplementationOutputSchema, InputDeclarationSchema, InputTypeSchema, LoopBodyStageSchema, LoopStageSchema, OrchestratorError, OutputContractNameSchema, RUN_DIAGNOSTICS_VIEW_VERSION, RUN_MONITOR_VIEW_VERSION, ReduceStageSchema, RoleCategorySchema, RoleModeSchema, RoleSchema, RunDiagnosticCodes, SCHEMA_VERSION, SeverityCountsSchema, StageLimitsSchema, StageSchema, SummarizeStageSchema, TASK_DETAIL_VIEW_VERSION, TransformSchema, ValidationOutputSchema, VariableSchema, WorkflowLimitsSchema, WorkflowSpecSchema, buildRunDiagnosticsView, buildRunMonitorView, buildTaskDetailView, collectWorkflowOutputCandidates, compileExecutionPlan, contractNameForStage, estimateAgentCalls, estimateFanoutWork, formatRepairPrompt, getOutputContract, isRepairableOutputFailure, issue, lintWorkflowSpec, loadWorkflowSpec, parseWorkflowOutput, prepareRun, previewRunView, readNdjsonTail, renderPromptMap, renderStagePrompt, repairFailedEnvelope, resultFromIssues, runViewFromIndex, stageRoleName, startPreparedRun, syncRun, topologicalOrder };
1
+ import { $ as ConditionSchema, B as retryCountByReason, C as RUN_MONITOR_VIEW_VERSION, Ct as TaskStageSchema, Dt as WorkflowLimitsSchema, E as buildTaskDetailView, Et as WorkflowInputSchema, F as parseWorkflowOutput, Ft as runDir, G as compileSchemaDsl, H as retryableOutputFailure, I as AGENT_TASK_RETRY_BUDGET, J as outputSchemaFooter, K as defaultAgentOutputZod, L as AGENT_TASK_RETRY_DELAY_MS, N as syncRun, Ot as WorkflowSpecSchema, P as collectWorkflowOutputCandidates, Q as AgentFaninSchema, R as agentTaskRetryDelayMs, S as resultFromIssues, St as TaskProgramStageSchema, T as buildRunMonitorView, Tt as VariableSchema, U as setAgentTaskRetryDelayForTests, V as retryExhaustedEnvelope, W as DEFAULT_AGENT_OUTPUT_SCHEMA, X as ActorModeSchema, Y as zodForCompiledSchema, Z as ActorSchema, _t as RouteStageSchema, a as renderStagePrompt, at as GateAgentStageSchema, b as OrchestratorError, bt as StageSchema, c as EXECUTION_PLAN_VERSION, ct as LimitBindingSchema, d as previewRunView, dt as LoopStageSchema, et as FaninSchema, f as runViewFromIndex, ft as PositiveIntegerLimitSchema, g as lintWorkflowSpec, gt as RouteRuleSchema, h as stringifyWorkflowSpec, ht as RouteProgramStageSchema, i as renderPromptMap, it as FanoutStageSchema, kt as RuntimeErrorCodes, l as estimateAgentCalls, lt as LimitValueSchema, m as loadWorkflowSpec, mt as RouteAgentStageSchema, n as startPreparedRun, nt as FanoutPolicySchema, o as stageActorLabel, ot as GateProgramStageSchema, p as isWorkflowYamlPath, pt as ProgramFaninSchema, q as formatSchema, r as compileExecutionPlan, rt as FanoutStageLimitsSchema, s as topologicalOrder, st as GateStageSchema, t as prepareRun, tt as FanoutLaneSchema, u as estimateFanoutWork, ut as LoopBodyStageSchema, vt as SCHEMA_VERSION, w as TASK_DETAIL_VIEW_VERSION, wt as TransformSchema, x as issue, xt as TaskAgentStageSchema, yt as StageLimitsSchema, z as formatContinuationPrompt } from "./run-workflow-DdIAC8Zu.mjs";
2
+ import path from "node:path";
3
+ import fs from "node:fs/promises";
4
+ //#region src/projections/run-diagnostics.ts
5
+ const RUN_DIAGNOSTICS_VIEW_VERSION = "acpus.diagnostics/v1";
6
+ const RUN_LEVEL_RUNTIME_CODES = new Set([
7
+ RuntimeErrorCodes.EVENT_APPEND_LOCK_TIMEOUT,
8
+ RuntimeErrorCodes.RUN_INDEX_LOCK_TIMEOUT,
9
+ RuntimeErrorCodes.AGENT_TASK_RETRY_EXHAUSTED,
10
+ RuntimeErrorCodes.FANOUT_ITEM_UNSTARTED_TIMEOUT,
11
+ RuntimeErrorCodes.FANOUT_ITEM_BLOCKED,
12
+ RuntimeErrorCodes.FANOUT_ITEM_CASCADE_BLOCKED,
13
+ RuntimeErrorCodes.FANOUT_LANE_RESULT_MISMATCH,
14
+ RuntimeErrorCodes.NO_SELECTED_LANES,
15
+ RuntimeErrorCodes.MISSING_FANOUT_ITEM_OUTPUT,
16
+ RuntimeErrorCodes.FANOUT_STAGE_STUCK_PENDING_BATCH,
17
+ RuntimeErrorCodes.RUN_INDEX_OUTPUT_MISMATCH,
18
+ RuntimeErrorCodes.LOOP_EXHAUSTED,
19
+ RuntimeErrorCodes.LOOP_BODY_STAGE_BLOCKED,
20
+ RuntimeErrorCodes.LOOP_BODY_STAGE_FAILED,
21
+ RuntimeErrorCodes.LOOP_BODY_OUTPUT_MISSING,
22
+ RuntimeErrorCodes.VARIABLE_RESOLUTION_FAILED,
23
+ RuntimeErrorCodes.AGENT_RUNTIME_ERROR,
24
+ RuntimeErrorCodes.AGENT_TURN_FAILED,
25
+ RuntimeErrorCodes.AGENT_TURN_CANCELLED,
26
+ RuntimeErrorCodes.AGENT_STAGE_STALE_RECOVERY,
27
+ RuntimeErrorCodes.GATE_CONDITION_FAILED,
28
+ RuntimeErrorCodes.GATE_VERDICT_BLOCKED,
29
+ RuntimeErrorCodes.GATE_VERDICT_FAILED,
30
+ RuntimeErrorCodes.GATE_VERDICT_UNKNOWN
31
+ ]);
32
+ const RunDiagnosticCodes = { SCHEDULER_RECOVERY_SUCCEEDED_WITH_BLOCKED_VERDICT: "SCHEDULER_RECOVERY_SUCCEEDED_WITH_BLOCKED_VERDICT" };
33
+ const DEFAULT_EVENT_TAIL_LIMIT = 50;
34
+ const DEFAULT_EVENT_TAIL_MAX_BYTES = 256 * 1024;
35
+ async function buildRunDiagnosticsView(cwd, index, options = {}) {
36
+ const dir = runDir(index.logicalRunId, cwd);
37
+ const eventTail = await readEventTail(path.join(dir, "events.ndjson"), {
38
+ limit: options.eventTailLimit ?? DEFAULT_EVENT_TAIL_LIMIT,
39
+ maxBytes: options.eventTailMaxBytes ?? DEFAULT_EVENT_TAIL_MAX_BYTES
40
+ });
41
+ const diagnostics = [...await buildRuntimeDiagnostics(dir, index), ...buildEventTailDiagnostics(path.join(dir, "events.ndjson"), eventTail)];
42
+ return {
43
+ version: RUN_DIAGNOSTICS_VIEW_VERSION,
44
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
45
+ run: {
46
+ logicalRunId: index.logicalRunId,
47
+ workflowName: index.workflowName,
48
+ status: index.status,
49
+ blockedReason: index.blockedReason,
50
+ gateVerdict: index.gateVerdict,
51
+ runDir: dir
52
+ },
53
+ diagnostics,
54
+ eventTail
55
+ };
56
+ }
57
+ async function readNdjsonTail(filePath, maxLines, maxBytes = DEFAULT_EVENT_TAIL_MAX_BYTES) {
58
+ if (maxLines <= 0 || maxBytes <= 0) return [];
59
+ let handle;
60
+ try {
61
+ const stat = await fs.stat(filePath);
62
+ if (stat.size === 0) return [];
63
+ const bytesToRead = Math.min(stat.size, maxBytes);
64
+ const start = stat.size - bytesToRead;
65
+ const buffer = Buffer.alloc(bytesToRead);
66
+ handle = await fs.open(filePath, "r");
67
+ await handle.read(buffer, 0, bytesToRead, start);
68
+ let lines = buffer.toString("utf8").split("\n");
69
+ if (start > 0) lines = lines.slice(1);
70
+ return lines.filter((line) => line.trim().length > 0).slice(-maxLines);
71
+ } catch {
72
+ return [];
73
+ } finally {
74
+ await handle?.close().catch(() => void 0);
75
+ }
76
+ }
77
+ async function readEventTail(filePath, options) {
78
+ return (await readNdjsonTail(filePath, options.limit, options.maxBytes)).map(parseEventLine).filter((event) => event !== void 0);
79
+ }
80
+ async function buildRuntimeDiagnostics(dir, index) {
81
+ const diagnostics = [];
82
+ if (index.blockedReason && isRunLevelRuntimeCode(index.blockedReason)) diagnostics.push(runtimeDiagnostic({
83
+ code: index.blockedReason,
84
+ path: path.join(dir, "run.json"),
85
+ summary: runLevelBlockedSummary(index),
86
+ source: "run_index"
87
+ }));
88
+ for (const stage of Object.values(index.stages)) {
89
+ if (!stage.fanout && (stage.blockedReason === RuntimeErrorCodes.AGENT_RUNTIME_ERROR || stage.blockedReason === RuntimeErrorCodes.AGENT_STAGE_STALE_RECOVERY || stage.blockedReason === RuntimeErrorCodes.AGENT_TASK_RETRY_EXHAUSTED)) {
90
+ const outputPath = stage.outputPath ? path.join(dir, stage.outputPath) : path.join(dir, "outputs", `${stage.stageId}.json`);
91
+ const code = stage.blockedReason;
92
+ diagnostics.push(runtimeDiagnostic({
93
+ code,
94
+ stageId: stage.stageId,
95
+ path: outputPath,
96
+ summary: stageRetryDiagnosticSummary(code),
97
+ source: "run_index"
98
+ }));
99
+ }
100
+ if (!stage.fanout) continue;
101
+ const hasRunningItems = stage.fanout.items.some((item) => item.status === "running");
102
+ const queuedItems = stage.fanout.items.filter((item) => item.status === "pending" || item.status === "ready");
103
+ if (stage.status === "running" && !hasRunningItems && queuedItems.length > 0) diagnostics.push(runtimeDiagnostic({
104
+ code: RuntimeErrorCodes.FANOUT_STAGE_STUCK_PENDING_BATCH,
105
+ stageId: stage.stageId,
106
+ path: path.join(dir, "run.json"),
107
+ summary: `Fanout stage ${stage.stageId} is running with no running items and ${queuedItems.length} queued item(s).`,
108
+ source: "run_index"
109
+ }));
110
+ for (const item of stage.fanout.items) {
111
+ const outputPath = item.outputPath ? path.join(dir, item.outputPath) : path.join(dir, "outputs", stage.stageId, `${safeFileName(item.id)}.json`);
112
+ if (item.status === "running" && await fileExists(outputPath)) diagnostics.push(runtimeDiagnostic({
113
+ code: RuntimeErrorCodes.RUN_INDEX_OUTPUT_MISMATCH,
114
+ stageId: stage.stageId,
115
+ itemId: item.id,
116
+ path: outputPath,
117
+ summary: `Fanout item ${stage.stageId}/${item.id} is running in run.json but has an output file.`,
118
+ source: "stage_output"
119
+ }));
120
+ if (item.errorCode) diagnostics.push(runtimeDiagnostic({
121
+ code: item.errorCode,
122
+ stageId: stage.stageId,
123
+ itemId: item.id,
124
+ path: outputPath,
125
+ summary: item.errorMessage ?? item.blockedReason ?? item.errorCode,
126
+ source: "run_index"
127
+ }));
128
+ }
129
+ }
130
+ const recoveryVerdictDiagnostic = buildRecoverySucceededWithBlockedVerdictDiagnostic(dir, index);
131
+ if (recoveryVerdictDiagnostic) diagnostics.push(recoveryVerdictDiagnostic);
132
+ return diagnostics;
133
+ }
134
+ function stageRetryDiagnosticSummary(code) {
135
+ if (code === RuntimeErrorCodes.AGENT_STAGE_STALE_RECOVERY) return "Agent task retry exhausted after scheduler stale recovery.";
136
+ if (code === RuntimeErrorCodes.AGENT_TASK_RETRY_EXHAUSTED) return "Agent task retry budget exhausted.";
137
+ return "Agent runtime failed after retry exhaustion.";
138
+ }
139
+ function buildEventTailDiagnostics(eventPath, eventTail) {
140
+ const diagnostics = [];
141
+ for (const event of eventTail) {
142
+ if (!(event.summary?.includes("Lock file is already being held") || event.errorCode === RuntimeErrorCodes.RUN_INDEX_LOCK_TIMEOUT || event.errorCode === RuntimeErrorCodes.EVENT_APPEND_LOCK_TIMEOUT)) continue;
143
+ diagnostics.push(runtimeDiagnostic({
144
+ code: event.errorCode ?? "LOCK_CONTENTION",
145
+ stageId: event.stageId,
146
+ itemId: event.itemId,
147
+ attemptId: event.attemptId,
148
+ path: eventPath,
149
+ summary: "Runtime lock contention was observed in recent events.",
150
+ source: "event_tail",
151
+ status: "blocked"
152
+ }));
153
+ }
154
+ return diagnostics;
155
+ }
156
+ function buildRecoverySucceededWithBlockedVerdictDiagnostic(dir, index) {
157
+ if (!isBlockedGateVerdictCode(index.blockedReason) || !index.gateVerdict) return void 0;
158
+ const recoveredFanoutStages = Object.values(index.stages).filter((stage) => {
159
+ if (!stage.fanout) return false;
160
+ if (stage.fanout.items.some((item) => item.status === "running" || item.status === "pending" || item.status === "ready")) return false;
161
+ return stage.fanout.items.some((item) => item.errorCode === RuntimeErrorCodes.AGENT_TASK_RETRY_EXHAUSTED || item.errorCode === RuntimeErrorCodes.FANOUT_ITEM_CASCADE_BLOCKED || item.errorCode === RuntimeErrorCodes.FANOUT_ITEM_UNSTARTED_TIMEOUT || item.errorCode === RuntimeErrorCodes.RUN_INDEX_OUTPUT_MISMATCH);
162
+ });
163
+ if (recoveredFanoutStages.length === 0) return void 0;
164
+ const stageIds = recoveredFanoutStages.map((stage) => stage.stageId);
165
+ return {
166
+ id: `runtime-${RunDiagnosticCodes.SCHEDULER_RECOVERY_SUCCEEDED_WITH_BLOCKED_VERDICT}-run-all`,
167
+ code: RunDiagnosticCodes.SCHEDULER_RECOVERY_SUCCEEDED_WITH_BLOCKED_VERDICT,
168
+ status: "completed",
169
+ summary: `Scheduler recovery completed for fanout stage(s) ${stageIds.join(", ")}, but workflow gate verdict remains ${index.gateVerdict}.`,
170
+ path: path.join(dir, "run.json"),
171
+ source: "run_index"
172
+ };
173
+ }
174
+ function runtimeDiagnostic(input) {
175
+ return {
176
+ id: `runtime-${input.code}-${input.stageId ?? "run"}-${input.itemId ?? input.attemptId ?? "all"}`,
177
+ code: input.code,
178
+ stageId: input.stageId,
179
+ itemId: input.itemId,
180
+ attemptId: input.attemptId,
181
+ status: input.status ?? "blocked",
182
+ summary: input.summary,
183
+ path: input.path,
184
+ source: input.source
185
+ };
186
+ }
187
+ function isRunLevelRuntimeCode(value) {
188
+ return RUN_LEVEL_RUNTIME_CODES.has(value);
189
+ }
190
+ function isBlockedGateVerdictCode(value) {
191
+ return value === RuntimeErrorCodes.GATE_VERDICT_BLOCKED || value === RuntimeErrorCodes.GATE_VERDICT_FAILED || value === RuntimeErrorCodes.GATE_VERDICT_UNKNOWN;
192
+ }
193
+ function runLevelBlockedSummary(index) {
194
+ if (index.blockedReason === RuntimeErrorCodes.AGENT_TASK_RETRY_EXHAUSTED) return "Agent Task Retry budget exhausted.";
195
+ if (index.blockedReason === RuntimeErrorCodes.AGENT_RUNTIME_ERROR) return "Agent runtime failed after Agent Task Retry budget exhaustion.";
196
+ if (index.blockedReason === RuntimeErrorCodes.AGENT_STAGE_STALE_RECOVERY) return "Agent stage stale recovery exhausted Agent Task Retry budget.";
197
+ if (index.blockedReason === RuntimeErrorCodes.FANOUT_ITEM_BLOCKED) return "Fanout stage blocked because one or more items did not complete.";
198
+ if (index.blockedReason === RuntimeErrorCodes.GATE_CONDITION_FAILED) return "Program gate condition failed.";
199
+ if (index.gateVerdict === "blocked") return "Gate returned verdict=blocked.";
200
+ if (index.gateVerdict === "failed") return "Gate returned verdict=failed.";
201
+ return "Gate returned verdict=unknown.";
202
+ }
203
+ function parseEventLine(line) {
204
+ const record = objectRecord(safeJson(line));
205
+ if (!record) return void 0;
206
+ const errorCode = stringField(record, "errorCode") ?? stringField(record, "code");
207
+ return {
208
+ at: stringField(record, "at"),
209
+ type: stringField(record, "type"),
210
+ stageId: stringField(record, "stageId"),
211
+ itemId: stringField(record, "itemId"),
212
+ attemptId: stringField(record, "attemptId"),
213
+ errorCode,
214
+ summary: stringField(record, "summary") ?? stringField(record, "error") ?? stringField(record, "errorMessage")
215
+ };
216
+ }
217
+ async function fileExists(filePath) {
218
+ try {
219
+ await fs.access(filePath);
220
+ return true;
221
+ } catch {
222
+ return false;
223
+ }
224
+ }
225
+ function safeJson(value) {
226
+ try {
227
+ return JSON.parse(value);
228
+ } catch {
229
+ return;
230
+ }
231
+ }
232
+ function objectRecord(value) {
233
+ return value && typeof value === "object" ? value : void 0;
234
+ }
235
+ function stringField(value, key) {
236
+ const field = value?.[key];
237
+ return typeof field === "string" ? field : void 0;
238
+ }
239
+ function safeFileName(value) {
240
+ return String(value || "item").replace(/[^A-Za-z0-9_.-]/g, "_");
241
+ }
242
+ //#endregion
243
+ export { AGENT_TASK_RETRY_BUDGET, AGENT_TASK_RETRY_DELAY_MS, ActorModeSchema, ActorSchema, AgentFaninSchema, ConditionSchema, DEFAULT_AGENT_OUTPUT_SCHEMA, EXECUTION_PLAN_VERSION, FaninSchema, FanoutLaneSchema, FanoutPolicySchema, FanoutStageLimitsSchema, FanoutStageSchema, GateAgentStageSchema, GateProgramStageSchema, GateStageSchema, LimitBindingSchema, LimitValueSchema, LoopBodyStageSchema, LoopStageSchema, OrchestratorError, PositiveIntegerLimitSchema, ProgramFaninSchema, RUN_DIAGNOSTICS_VIEW_VERSION, RUN_MONITOR_VIEW_VERSION, RouteAgentStageSchema, RouteProgramStageSchema, RouteRuleSchema, RouteStageSchema, RunDiagnosticCodes, SCHEMA_VERSION, StageLimitsSchema, StageSchema, TASK_DETAIL_VIEW_VERSION, TaskAgentStageSchema, TaskProgramStageSchema, TaskStageSchema, TransformSchema, VariableSchema, WorkflowInputSchema, WorkflowLimitsSchema, WorkflowSpecSchema, agentTaskRetryDelayMs, buildRunDiagnosticsView, buildRunMonitorView, buildTaskDetailView, collectWorkflowOutputCandidates, compileExecutionPlan, compileSchemaDsl, defaultAgentOutputZod, estimateAgentCalls, estimateFanoutWork, formatContinuationPrompt, formatSchema, isWorkflowYamlPath, issue, lintWorkflowSpec, loadWorkflowSpec, outputSchemaFooter, parseWorkflowOutput, prepareRun, previewRunView, readNdjsonTail, renderPromptMap, renderStagePrompt, resultFromIssues, retryCountByReason, retryExhaustedEnvelope, retryableOutputFailure, runViewFromIndex, setAgentTaskRetryDelayForTests, stageActorLabel, startPreparedRun, stringifyWorkflowSpec, syncRun, topologicalOrder, zodForCompiledSchema };
@@ -1,6 +1,9 @@
1
- import { Dt as runDir, S as syncRun, ft as WorkflowSpecSchema, gt as resolveRunLocator, l as buildRunMonitorView, u as buildTaskDetailView } from "./run-workflow-CbxKhAqF.mjs";
1
+ import { E as buildTaskDetailView, Ft as runDir, N as syncRun, Ot as WorkflowSpecSchema, T as buildRunMonitorView } from "./run-workflow-DdIAC8Zu.mjs";
2
+ import { n as resolveRunLocator } from "./cli.mjs";
3
+ import { a as nextIndex, c as shorten, d as tasksForStage, i as formatDuration, l as stageProgressLabel, n as defaultStageIndex, o as runProgressLabel, r as detailSummary, s as runStatusLabel, t as clampIndex, u as statusMark } from "./monitor-rendering-LGr9Ebd_.mjs";
2
4
  import path from "node:path";
3
5
  import fs from "node:fs/promises";
6
+ import YAML from "yaml";
4
7
  import { useEffect, useMemo, useRef, useState } from "react";
5
8
  import { Box, Text, useApp, useInput, useStdout } from "ink";
6
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -8,7 +11,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
11
  async function loadMonitorSnapshot(runArg) {
9
12
  const locator = await resolveRunLocator(runArg);
10
13
  const index = await syncRun(locator.cwd, locator.runId, { startPending: false });
11
- const spec = WorkflowSpecSchema.parse(JSON.parse(await fs.readFile(path.join(runDir(locator.runId, locator.cwd), "workflow.spec.json"), "utf8")));
14
+ const spec = WorkflowSpecSchema.parse(YAML.parse(await fs.readFile(path.join(runDir(locator.runId, locator.cwd), "workflow.spec.yaml"), "utf8")));
12
15
  return {
13
16
  locator,
14
17
  view: await buildRunMonitorView(locator.cwd, spec, index)
@@ -16,78 +19,10 @@ async function loadMonitorSnapshot(runArg) {
16
19
  }
17
20
  async function loadTaskDetail(locator, taskId) {
18
21
  const index = await syncRun(locator.cwd, locator.runId, { startPending: false });
19
- const spec = WorkflowSpecSchema.parse(JSON.parse(await fs.readFile(path.join(runDir(locator.runId, locator.cwd), "workflow.spec.json"), "utf8")));
22
+ const spec = WorkflowSpecSchema.parse(YAML.parse(await fs.readFile(path.join(runDir(locator.runId, locator.cwd), "workflow.spec.yaml"), "utf8")));
20
23
  return buildTaskDetailView(locator.cwd, spec, index, taskId);
21
24
  }
22
25
  //#endregion
23
- //#region src/tui/monitor-rendering.ts
24
- function defaultStageIndex(stages) {
25
- const running = stages.findIndex((stage) => stage.status === "running");
26
- if (running >= 0) return running;
27
- const blocked = stages.findIndex((stage) => stage.status === "blocked" || stage.status === "failed");
28
- if (blocked >= 0) return blocked;
29
- const open = stages.findIndex((stage) => stage.status !== "completed" && stage.status !== "skipped");
30
- return open >= 0 ? open : 0;
31
- }
32
- function clampIndex(index, length) {
33
- if (length <= 0) return 0;
34
- return Math.min(Math.max(index, 0), length - 1);
35
- }
36
- function tasksForStage(view, stageId) {
37
- if (!view || !stageId) return [];
38
- return view.tasks.filter((task) => task.stageId === stageId);
39
- }
40
- function stageProgressLabel(stage) {
41
- const counts = stage.taskCounts;
42
- return `${counts.completed}/${counts.total}`;
43
- }
44
- function runProgressLabel(view) {
45
- return `${view.progress.completedTasks}/${view.progress.knownTasks} tasks`;
46
- }
47
- function statusMark(status) {
48
- if (status === "completed") return "✔";
49
- if (status === "running" || status === "raw_received" || status === "parsing" || status === "repairing") return "●";
50
- if (status === "blocked" || status === "failed" || status === "cancelled" || status === "timed_out") return "!";
51
- if (status === "skipped") return "-";
52
- return " ";
53
- }
54
- function shorten(value, width) {
55
- if (!value) return "";
56
- if (width <= 0) return "";
57
- if (value.length <= width) return value;
58
- if (width <= 3) return value.slice(0, width);
59
- return `${value.slice(0, width - 3)}...`;
60
- }
61
- function detailSummary(detail) {
62
- if (!detail) return ["No task selected"];
63
- return [
64
- `${statusMark(detail.task.status)} ${detail.task.status} - ${detail.task.execution}${detail.task.agent ? ` - ${detail.task.agent}` : ""}`,
65
- detail.task.durationMs !== void 0 || detail.task.elapsedMs !== void 0 ? `Time: ${formatDuration(detail.task.durationMs ?? detail.task.elapsedMs ?? 0)}` : void 0,
66
- detail.task.blockedReason ? `Reason: ${detail.task.blockedReason}` : void 0,
67
- detail.outcome?.summary ? `Outcome: ${detail.outcome.summary}` : void 0,
68
- detail.outcome?.path ? `Output: ${detail.outcome.path}` : void 0,
69
- detail.prompt ? `Prompt: ${detail.prompt.lines} line(s)` : void 0,
70
- detail.prompt?.preview ? detail.prompt.preview : void 0,
71
- detail.activity.totalAttempts > 0 ? `Attempts: ${detail.activity.totalAttempts}` : "No agent attempts",
72
- ...detail.activity.attempts.map((attempt) => `${attempt.id} ${attempt.status} ${attempt.path}`),
73
- ...(detail.outcome?.artifacts ?? []).map((artifact) => `Artifact: ${artifact.label ?? artifact.kind ?? "artifact"} ${artifact.path ?? artifact.url ?? ""}`)
74
- ].filter((line) => typeof line === "string" && line.length > 0);
75
- }
76
- function nextIndex(current, delta, length) {
77
- return clampIndex(current + delta, length);
78
- }
79
- function formatDuration(milliseconds) {
80
- if (milliseconds === void 0 || !Number.isFinite(milliseconds)) return "";
81
- const totalSeconds = Math.max(0, Math.floor(milliseconds / 1e3));
82
- const seconds = totalSeconds % 60;
83
- const totalMinutes = Math.floor(totalSeconds / 60);
84
- const minutes = totalMinutes % 60;
85
- const hours = Math.floor(totalMinutes / 60);
86
- if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`;
87
- if (minutes > 0) return `${minutes}m ${seconds}s`;
88
- return `${seconds}s`;
89
- }
90
- //#endregion
91
26
  //#region src/tui/monitor-app.tsx
92
27
  function MonitorApp({ runArg, pollMs = 1e3, initialView, initialLocator, initialFocus = "stages", loadSnapshot = loadMonitorSnapshot, loadDetail = loadTaskDetail }) {
93
28
  const { exit } = useApp();
@@ -247,7 +182,7 @@ function Header({ view }) {
247
182
  const title = `${view.run.workflowName}`;
248
183
  const worker = view.run.worker ? ` - worker ${view.run.worker.status}` : "";
249
184
  const runTime = formatDuration(view.run.durationMs ?? view.run.elapsedMs);
250
- const meta = `${runProgressLabel(view)} - ${view.run.status}${runTime ? ` - ${runTime}` : ""}${worker}`;
185
+ const meta = `${runProgressLabel(view)} - ${runStatusLabel(view)}${runTime ? ` - ${runTime}` : ""}${worker}`;
251
186
  return /* @__PURE__ */ jsxs(Box, {
252
187
  flexDirection: "column",
253
188
  children: [/* @__PURE__ */ jsx(Text, {
@@ -413,7 +348,7 @@ function StatusMark({ status }) {
413
348
  color: "green",
414
349
  children: mark
415
350
  });
416
- if (status === "running" || status === "raw_received" || status === "parsing" || status === "repairing") return /* @__PURE__ */ jsx(Text, {
351
+ if (status === "running" || status === "raw_received" || status === "parsing") return /* @__PURE__ */ jsx(Text, {
417
352
  color: "yellow",
418
353
  children: mark
419
354
  });
@@ -0,0 +1,78 @@
1
+ //#region src/tui/monitor-rendering.ts
2
+ function defaultStageIndex(stages) {
3
+ const running = stages.findIndex((stage) => stage.status === "running");
4
+ if (running >= 0) return running;
5
+ const blocked = stages.findIndex((stage) => stage.status === "blocked" || stage.status === "failed");
6
+ if (blocked >= 0) return blocked;
7
+ const open = stages.findIndex((stage) => stage.status !== "completed" && stage.status !== "skipped");
8
+ return open >= 0 ? open : 0;
9
+ }
10
+ function clampIndex(index, length) {
11
+ if (length <= 0) return 0;
12
+ return Math.min(Math.max(index, 0), length - 1);
13
+ }
14
+ function tasksForStage(view, stageId) {
15
+ if (!view || !stageId) return [];
16
+ return view.tasks.filter((task) => task.stageId === stageId);
17
+ }
18
+ function stageProgressLabel(stage) {
19
+ const counts = stage.taskCounts;
20
+ return `${counts.completed}/${counts.total}`;
21
+ }
22
+ function runProgressLabel(view) {
23
+ return `${view.progress.completedTasks}/${view.progress.knownTasks} tasks`;
24
+ }
25
+ function runStatusLabel(view) {
26
+ if ((view.run.status === "running" || view.run.status === "pending") && view.run.worker?.status === "stale") return "stale";
27
+ return view.run.status;
28
+ }
29
+ function statusMark(status) {
30
+ if (status === "completed") return "✔";
31
+ if (status === "running" || status === "raw_received" || status === "parsing") return "●";
32
+ if (status === "blocked" || status === "failed" || status === "cancelled" || status === "timed_out") return "!";
33
+ if (status === "skipped") return "-";
34
+ return " ";
35
+ }
36
+ function shorten(value, width) {
37
+ if (!value) return "";
38
+ if (width <= 0) return "";
39
+ if (value.length <= width) return value;
40
+ if (width <= 3) return value.slice(0, width);
41
+ return `${value.slice(0, width - 3)}...`;
42
+ }
43
+ function detailSummary(detail) {
44
+ if (!detail) return ["No task selected"];
45
+ return [
46
+ `${statusMark(detail.task.status)} ${detail.task.status} - ${detail.task.execution}${detail.task.agent ? ` - ${detail.task.agent}` : ""}`,
47
+ detail.task.durationMs !== void 0 || detail.task.elapsedMs !== void 0 ? `Time: ${formatDuration(detail.task.durationMs ?? detail.task.elapsedMs ?? 0)}` : void 0,
48
+ detail.task.blockedReason ? `Reason: ${detail.task.blockedReason}` : void 0,
49
+ detail.task.lastRetryReason ? `Retry: ${detail.task.lastRetryReason} ${detail.task.retryBudgetUsed ?? 0}/${detail.task.retryBudgetLimit ?? "?"}` : void 0,
50
+ detail.task.lastFailureCode ? `Last failure: ${detail.task.lastFailureCode}` : void 0,
51
+ detail.outcome?.summary ? `Outcome: ${detail.outcome.summary}` : void 0,
52
+ detail.outcome?.path ? `Output: ${detail.outcome.path}` : void 0,
53
+ detail.prompt ? `Prompt: ${detail.prompt.lines} line(s)` : void 0,
54
+ detail.prompt?.preview ? detail.prompt.preview : void 0,
55
+ detail.activity.totalAttempts > 0 ? `Attempts: ${detail.activity.totalAttempts}` : "No agent attempts",
56
+ ...detail.activity.attempts.map((attempt) => {
57
+ const retry = attempt.isRetry ? ` retry=${attempt.retryReason ?? "unknown"}#${attempt.retryOrdinal ?? "?"}` : "";
58
+ const failure = attempt.lastFailureCode ? ` last=${attempt.lastFailureCode}` : "";
59
+ return `${attempt.id} ${attempt.status}${retry}${failure} ${attempt.path}`;
60
+ })
61
+ ].filter((line) => typeof line === "string" && line.length > 0);
62
+ }
63
+ function nextIndex(current, delta, length) {
64
+ return clampIndex(current + delta, length);
65
+ }
66
+ function formatDuration(milliseconds) {
67
+ if (milliseconds === void 0 || !Number.isFinite(milliseconds)) return "";
68
+ const totalSeconds = Math.max(0, Math.floor(milliseconds / 1e3));
69
+ const seconds = totalSeconds % 60;
70
+ const totalMinutes = Math.floor(totalSeconds / 60);
71
+ const minutes = totalMinutes % 60;
72
+ const hours = Math.floor(totalMinutes / 60);
73
+ if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`;
74
+ if (minutes > 0) return `${minutes}m ${seconds}s`;
75
+ return `${seconds}s`;
76
+ }
77
+ //#endregion
78
+ export { nextIndex as a, shorten as c, tasksForStage as d, formatDuration as i, stageProgressLabel as l, defaultStageIndex as n, runProgressLabel as o, detailSummary as r, runStatusLabel as s, clampIndex as t, statusMark as u };
@@ -0,0 +1,104 @@
1
+ import { t as listRunSummaries } from "./cli.mjs";
2
+ import { a as nextIndex, c as shorten, i as formatDuration, u as statusMark } from "./monitor-rendering-LGr9Ebd_.mjs";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import { Box, Text, useApp, useInput, useStdout } from "ink";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/tui/run-picker-app.tsx
7
+ function RunPickerApp({ title, pollMs = 1e3, initialList, loadRuns = listRunSummaries, onSelect }) {
8
+ const { exit } = useApp();
9
+ const { stdout } = useStdout();
10
+ const [list, setList] = useState(initialList);
11
+ const [selectedIndex, setSelectedIndex] = useState(0);
12
+ const [error, setError] = useState();
13
+ const loadRunsRef = useRef(loadRuns);
14
+ useEffect(() => {
15
+ loadRunsRef.current = loadRuns;
16
+ }, [loadRuns]);
17
+ const refresh = useCallback(async () => {
18
+ try {
19
+ const next = await loadRunsRef.current();
20
+ setList(next);
21
+ setSelectedIndex((current) => Math.max(0, Math.min(current, Math.max(0, next.entries.length - 1))));
22
+ setError(void 0);
23
+ } catch (loadError) {
24
+ setError(loadError instanceof Error ? loadError.message : String(loadError));
25
+ }
26
+ }, []);
27
+ useEffect(() => {
28
+ refresh();
29
+ const timer = setInterval(() => void refresh(), pollMs);
30
+ return () => clearInterval(timer);
31
+ }, [pollMs, refresh]);
32
+ useInput((input, key) => {
33
+ if (input === "q" || key.ctrl && input === "c") {
34
+ onSelect(void 0);
35
+ exit();
36
+ return;
37
+ }
38
+ if (input === "r") refresh();
39
+ if (key.upArrow) setSelectedIndex((current) => nextIndex(current, -1, list?.entries.length ?? 0));
40
+ if (key.downArrow) setSelectedIndex((current) => nextIndex(current, 1, list?.entries.length ?? 0));
41
+ if (key.return) {
42
+ const selected = list?.entries[selectedIndex];
43
+ if (!selected || selected.invalid) return;
44
+ onSelect(selected.runId);
45
+ exit();
46
+ }
47
+ });
48
+ const width = Math.max(80, stdout.columns ?? 120);
49
+ const entries = useMemo(() => list?.entries ?? [], [list?.entries]);
50
+ return /* @__PURE__ */ jsxs(Box, {
51
+ flexDirection: "column",
52
+ children: [
53
+ /* @__PURE__ */ jsx(Text, {
54
+ color: "blue",
55
+ bold: true,
56
+ children: title
57
+ }),
58
+ /* @__PURE__ */ jsx(Text, {
59
+ dimColor: true,
60
+ children: list ? `runs in ${list.dir}` : "Loading runs..."
61
+ }),
62
+ error ? /* @__PURE__ */ jsxs(Text, {
63
+ color: "red",
64
+ children: ["Error: ", error]
65
+ }) : null,
66
+ entries.length === 0 ? /* @__PURE__ */ jsx(Text, {
67
+ dimColor: true,
68
+ children: "No runs found."
69
+ }) : null,
70
+ /* @__PURE__ */ jsx(Box, {
71
+ flexDirection: "column",
72
+ marginTop: 1,
73
+ children: entries.map((entry, index) => /* @__PURE__ */ jsxs(Box, { children: [
74
+ /* @__PURE__ */ jsxs(Text, { children: [index === selectedIndex ? ">" : " ", " "] }),
75
+ /* @__PURE__ */ jsxs(Text, {
76
+ dimColor: entry.invalid,
77
+ children: [statusMark(entry.status ?? "invalid"), " "]
78
+ }),
79
+ /* @__PURE__ */ jsxs(Text, { children: [shorten(entry.runId, 40), " "] }),
80
+ /* @__PURE__ */ jsxs(Text, { children: [shorten(entry.status ?? "invalid", 10), " "] }),
81
+ /* @__PURE__ */ jsxs(Text, {
82
+ dimColor: true,
83
+ children: [shorten(entry.progress?.label ?? "-", 12), " "]
84
+ }),
85
+ /* @__PURE__ */ jsxs(Text, {
86
+ dimColor: true,
87
+ children: [shorten(entry.worker ? `worker ${entry.worker.status}` : "", 16), " "]
88
+ }),
89
+ /* @__PURE__ */ jsxs(Text, { children: [shorten(entry.workflowName ?? "", Math.max(12, width - 100)), " "] }),
90
+ /* @__PURE__ */ jsx(Text, {
91
+ dimColor: true,
92
+ children: formatDuration(entry.durationMs ?? entry.elapsedMs)
93
+ })
94
+ ] }, entry.runId))
95
+ }),
96
+ /* @__PURE__ */ jsx(Text, {
97
+ dimColor: true,
98
+ children: "up/down move - enter select - r refresh - q quit"
99
+ })
100
+ ]
101
+ });
102
+ }
103
+ //#endregion
104
+ export { RunPickerApp };