@flowdesk/opencode-plugin 0.1.12 → 0.1.14
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/README.md +1 -1
- package/dist/agent-task-output.d.ts +17 -0
- package/dist/agent-task-output.d.ts.map +1 -0
- package/dist/agent-task-output.js +119 -0
- package/dist/agent-task-output.js.map +1 -0
- package/dist/agent-task-runner.d.ts +24 -0
- package/dist/agent-task-runner.d.ts.map +1 -1
- package/dist/agent-task-runner.js +536 -61
- package/dist/agent-task-runner.js.map +1 -1
- package/dist/auto-continue-preview-tool.d.ts +36 -0
- package/dist/auto-continue-preview-tool.d.ts.map +1 -0
- package/dist/auto-continue-preview-tool.js +119 -0
- package/dist/auto-continue-preview-tool.js.map +1 -0
- package/dist/completion-ui-cache.d.ts +6 -0
- package/dist/completion-ui-cache.d.ts.map +1 -0
- package/dist/completion-ui-cache.js +260 -0
- package/dist/completion-ui-cache.js.map +1 -0
- package/dist/controlled-write-tool.d.ts +49 -0
- package/dist/controlled-write-tool.d.ts.map +1 -0
- package/dist/controlled-write-tool.js +296 -0
- package/dist/controlled-write-tool.js.map +1 -0
- package/dist/event-hook-observer.d.ts +14 -0
- package/dist/event-hook-observer.d.ts.map +1 -0
- package/dist/event-hook-observer.js +193 -0
- package/dist/event-hook-observer.js.map +1 -0
- package/dist/managed-dispatch-adapter.d.ts +1 -0
- package/dist/managed-dispatch-adapter.d.ts.map +1 -1
- package/dist/managed-dispatch-adapter.js +100 -29
- package/dist/managed-dispatch-adapter.js.map +1 -1
- package/dist/model-selection-engine.d.ts +47 -0
- package/dist/model-selection-engine.d.ts.map +1 -0
- package/dist/model-selection-engine.js +175 -0
- package/dist/model-selection-engine.js.map +1 -0
- package/dist/provider-usage-live-tool.d.ts +27 -0
- package/dist/provider-usage-live-tool.d.ts.map +1 -1
- package/dist/provider-usage-live-tool.js +443 -4
- package/dist/provider-usage-live-tool.js.map +1 -1
- package/dist/quick-reviewer-run.d.ts +3 -0
- package/dist/quick-reviewer-run.d.ts.map +1 -1
- package/dist/quick-reviewer-run.js +20 -8
- package/dist/quick-reviewer-run.js.map +1 -1
- package/dist/runtime-reviewer-execution-bridge.d.ts +21 -0
- package/dist/runtime-reviewer-execution-bridge.d.ts.map +1 -1
- package/dist/runtime-reviewer-execution-bridge.js +238 -0
- package/dist/runtime-reviewer-execution-bridge.js.map +1 -1
- package/dist/server.d.ts +60 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +800 -41
- package/dist/server.js.map +1 -1
- package/dist/stall-recovery.d.ts +60 -0
- package/dist/stall-recovery.d.ts.map +1 -1
- package/dist/stall-recovery.js +763 -11
- package/dist/stall-recovery.js.map +1 -1
- package/dist/status-live-tool.d.ts +81 -0
- package/dist/status-live-tool.d.ts.map +1 -1
- package/dist/status-live-tool.js +620 -38
- package/dist/status-live-tool.js.map +1 -1
- package/dist/tui-subtask-activity.d.ts +69 -0
- package/dist/tui-subtask-activity.d.ts.map +1 -0
- package/dist/tui-subtask-activity.js +266 -0
- package/dist/tui-subtask-activity.js.map +1 -0
- package/dist/tui-usage-snapshot.d.ts +44 -0
- package/dist/tui-usage-snapshot.d.ts.map +1 -0
- package/dist/tui-usage-snapshot.js +397 -0
- package/dist/tui-usage-snapshot.js.map +1 -0
- package/dist/tui.d.ts +7 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/tui.js +134 -0
- package/dist/tui.js.map +1 -0
- package/dist/workflow-assign-tool.d.ts +23 -0
- package/dist/workflow-assign-tool.d.ts.map +1 -0
- package/dist/workflow-assign-tool.js +117 -0
- package/dist/workflow-assign-tool.js.map +1 -0
- package/dist/workflow-author-tool.d.ts +29 -0
- package/dist/workflow-author-tool.d.ts.map +1 -0
- package/dist/workflow-author-tool.js +227 -0
- package/dist/workflow-author-tool.js.map +1 -0
- package/dist/workflow-dispatch-plan-tool.d.ts +47 -0
- package/dist/workflow-dispatch-plan-tool.d.ts.map +1 -0
- package/dist/workflow-dispatch-plan-tool.js +251 -0
- package/dist/workflow-dispatch-plan-tool.js.map +1 -0
- package/dist/workflow-dispatch-tool.d.ts +56 -0
- package/dist/workflow-dispatch-tool.d.ts.map +1 -0
- package/dist/workflow-dispatch-tool.js +306 -0
- package/dist/workflow-dispatch-tool.js.map +1 -0
- package/dist/workflow-orchestrator.d.ts +31 -0
- package/dist/workflow-orchestrator.d.ts.map +1 -0
- package/dist/workflow-orchestrator.js +160 -0
- package/dist/workflow-orchestrator.js.map +1 -0
- package/dist/workflow-scheduler.d.ts +19 -0
- package/dist/workflow-scheduler.d.ts.map +1 -0
- package/dist/workflow-scheduler.js +45 -0
- package/dist/workflow-scheduler.js.map +1 -0
- package/dist/workflow-synthesis-tool.d.ts +31 -0
- package/dist/workflow-synthesis-tool.d.ts.map +1 -0
- package/dist/workflow-synthesis-tool.js +194 -0
- package/dist/workflow-synthesis-tool.js.map +1 -0
- package/package.json +10 -2
package/dist/status-live-tool.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { FLOWDESK_SESSION_EVIDENCE_CLASSES, projectFlowDeskLaneStallV1, reloadFlowDeskSessionEvidenceV1, } from "@flowdesk/core";
|
|
3
|
+
import { applyFlowDeskSessionEvidenceWriteIntentsV1, FLOWDESK_SESSION_EVIDENCE_CLASSES, prepareFlowDeskSessionEvidenceWriteIntentV1, projectFlowDeskLaneStallV1, reloadFlowDeskSessionEvidenceV1, } from "@flowdesk/core";
|
|
4
|
+
import { backfillTerminalAgentTaskFailedLanesV1 } from "./stall-recovery.js";
|
|
4
5
|
const FLOWDESK_LANE_STALL_TERMINAL_STATES = new Set([
|
|
5
6
|
"complete",
|
|
6
7
|
"incomplete",
|
|
@@ -89,6 +90,27 @@ function pruneNonTerminalLifecycleSnapshotsNoLongerValid(reload) {
|
|
|
89
90
|
return reload;
|
|
90
91
|
return { ...reload, entries };
|
|
91
92
|
}
|
|
93
|
+
function pruneInconsistencySnapshotsNoLongerValid(reload) {
|
|
94
|
+
const terminalLaneIds = new Set();
|
|
95
|
+
for (const entry of reload.entries) {
|
|
96
|
+
if (entry.evidenceClass === "task_result" || entry.evidenceClass === "task_failed") {
|
|
97
|
+
const laneId = getLaneLifecycleRecordField(entry.record, "lane_id");
|
|
98
|
+
if (laneId !== undefined)
|
|
99
|
+
terminalLaneIds.add(laneId);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (terminalLaneIds.size === 0)
|
|
103
|
+
return reload;
|
|
104
|
+
const entries = reload.entries.filter((entry) => {
|
|
105
|
+
if (entry.evidenceClass !== "agent_task_inconsistency")
|
|
106
|
+
return true;
|
|
107
|
+
const laneId = getLaneLifecycleRecordField(entry.record, "lane_id");
|
|
108
|
+
return laneId === undefined || !terminalLaneIds.has(laneId);
|
|
109
|
+
});
|
|
110
|
+
if (entries.length === reload.entries.length)
|
|
111
|
+
return reload;
|
|
112
|
+
return { ...reload, entries };
|
|
113
|
+
}
|
|
92
114
|
const FLOWDESK_SESSION_RECORD_ROOT = ".flowdesk/sessions";
|
|
93
115
|
function blockedAuthority() {
|
|
94
116
|
return {
|
|
@@ -105,6 +127,14 @@ function blockedAuthority() {
|
|
|
105
127
|
function safeNextActions() {
|
|
106
128
|
return ["/flowdesk-status", "/flowdesk-doctor", "/flowdesk-export-debug"];
|
|
107
129
|
}
|
|
130
|
+
function authorityCapabilitySummary() {
|
|
131
|
+
return {
|
|
132
|
+
availableNow: ["display_only", "local_preview", "command_backed_guarded"],
|
|
133
|
+
explicitDevBeta: ["provider_task_lane", "controlled_workspace_write"],
|
|
134
|
+
laterGated: ["managed_dispatch", "managed_fallback", "tui_actions", "hard_chat_control"],
|
|
135
|
+
unsupportedByDefault: ["auto_provider_execution", "automatic_reselection"],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
108
138
|
function listSessionWorkflowIds(rootDir, max) {
|
|
109
139
|
const sessionsDir = join(rootDir, FLOWDESK_SESSION_RECORD_ROOT);
|
|
110
140
|
if (!existsSync(sessionsDir))
|
|
@@ -137,6 +167,165 @@ function getStringField(record, key) {
|
|
|
137
167
|
const value = record[key];
|
|
138
168
|
return typeof value === "string" ? value : undefined;
|
|
139
169
|
}
|
|
170
|
+
function getPositiveIntegerField(record, key) {
|
|
171
|
+
const value = record[key];
|
|
172
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0
|
|
173
|
+
? value
|
|
174
|
+
: undefined;
|
|
175
|
+
}
|
|
176
|
+
const FORBIDDEN_SYNTHESIS_SUMMARY_MARKERS = /system prompt|provider payload|raw token|hidden injection|opencode\srun|dispatch|fallback|reselect/i;
|
|
177
|
+
function synthesisSummaryPreview(value) {
|
|
178
|
+
if (value === undefined)
|
|
179
|
+
return undefined;
|
|
180
|
+
const compact = value.replace(/\s+/g, " ").trim();
|
|
181
|
+
if (compact.length === 0 || FORBIDDEN_SYNTHESIS_SUMMARY_MARKERS.test(compact))
|
|
182
|
+
return undefined;
|
|
183
|
+
return compact.length > 160 ? `${compact.slice(0, 159)}…` : compact;
|
|
184
|
+
}
|
|
185
|
+
const DEFAULT_AGENT_TASK_FINALIZING_INCONSISTENCY_GRACE_MS = 90_000;
|
|
186
|
+
const MIN_AGENT_TASK_FINALIZING_INCONSISTENCY_GRACE_MS = 30_000;
|
|
187
|
+
const MAX_AGENT_TASK_FINALIZING_INCONSISTENCY_GRACE_MS = 600_000;
|
|
188
|
+
function clampFinalizingInconsistencyGraceMs(value) {
|
|
189
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0)
|
|
190
|
+
return DEFAULT_AGENT_TASK_FINALIZING_INCONSISTENCY_GRACE_MS;
|
|
191
|
+
return Math.min(MAX_AGENT_TASK_FINALIZING_INCONSISTENCY_GRACE_MS, Math.max(MIN_AGENT_TASK_FINALIZING_INCONSISTENCY_GRACE_MS, Math.floor(value)));
|
|
192
|
+
}
|
|
193
|
+
function finalizingInconsistencyEvidenceId(laneId) {
|
|
194
|
+
return `agent-task-inconsistency-${laneId}-finalizing-without-terminal`;
|
|
195
|
+
}
|
|
196
|
+
function materializeFinalizingWithoutTerminalInconsistencies(input) {
|
|
197
|
+
const observedAtMs = Date.parse(input.observedAt);
|
|
198
|
+
if (!Number.isFinite(observedAtMs))
|
|
199
|
+
return false;
|
|
200
|
+
const terminalLaneIds = new Set();
|
|
201
|
+
const existingInconsistencyIds = new Set();
|
|
202
|
+
const attemptIdByLane = new Map();
|
|
203
|
+
const taskIdByLane = new Map();
|
|
204
|
+
const latestActiveSignalByLane = new Map();
|
|
205
|
+
const latestFinalizingByLane = new Map();
|
|
206
|
+
for (const entry of input.reload.entries) {
|
|
207
|
+
const laneId = getStringField(entry.record, "lane_id");
|
|
208
|
+
if (laneId !== undefined) {
|
|
209
|
+
if (entry.evidenceClass === "lane_lifecycle" || entry.evidenceClass === "lane_heartbeat") {
|
|
210
|
+
const attemptId = getStringField(entry.record, "attempt_id");
|
|
211
|
+
if (attemptId !== undefined)
|
|
212
|
+
attemptIdByLane.set(laneId, attemptId);
|
|
213
|
+
const state = getStringField(entry.record, "state");
|
|
214
|
+
const observedAt = getStringField(entry.record, "updated_at") ??
|
|
215
|
+
getStringField(entry.record, "observed_at") ??
|
|
216
|
+
getStringField(entry.record, "created_at");
|
|
217
|
+
const observedAtMs = observedAt === undefined ? NaN : Date.parse(observedAt);
|
|
218
|
+
if ((state === "created" ||
|
|
219
|
+
state === "running" ||
|
|
220
|
+
state === "awaiting_dependency" ||
|
|
221
|
+
state === "cooldown") &&
|
|
222
|
+
Number.isFinite(observedAtMs)) {
|
|
223
|
+
const current = latestActiveSignalByLane.get(laneId);
|
|
224
|
+
if (current === undefined || observedAtMs > current)
|
|
225
|
+
latestActiveSignalByLane.set(laneId, observedAtMs);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (entry.evidenceClass === "agent_task_context") {
|
|
229
|
+
const taskId = getStringField(entry.record, "task_id");
|
|
230
|
+
if (taskId !== undefined)
|
|
231
|
+
taskIdByLane.set(laneId, taskId);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (entry.evidenceClass === "task_result" || entry.evidenceClass === "task_failed") {
|
|
235
|
+
if (laneId !== undefined)
|
|
236
|
+
terminalLaneIds.add(laneId);
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (entry.evidenceClass === "agent_task_inconsistency") {
|
|
240
|
+
existingInconsistencyIds.add(entry.evidenceId);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (entry.evidenceClass !== "agent_task_progress")
|
|
244
|
+
continue;
|
|
245
|
+
if (laneId === undefined)
|
|
246
|
+
continue;
|
|
247
|
+
if (getStringField(entry.record, "phase") !== "finalizing")
|
|
248
|
+
continue;
|
|
249
|
+
const progressObservedAt = getStringField(entry.record, "observed_at");
|
|
250
|
+
if (progressObservedAt === undefined)
|
|
251
|
+
continue;
|
|
252
|
+
const progressObservedAtMs = Date.parse(progressObservedAt);
|
|
253
|
+
if (!Number.isFinite(progressObservedAtMs))
|
|
254
|
+
continue;
|
|
255
|
+
const current = latestFinalizingByLane.get(laneId);
|
|
256
|
+
if (current !== undefined && current.observedAtMs > progressObservedAtMs)
|
|
257
|
+
continue;
|
|
258
|
+
latestFinalizingByLane.set(laneId, {
|
|
259
|
+
laneId,
|
|
260
|
+
attemptId: attemptIdByLane.get(laneId),
|
|
261
|
+
taskId: getStringField(entry.record, "task_id"),
|
|
262
|
+
progressSeq: getPositiveIntegerField(entry.record, "progress_seq"),
|
|
263
|
+
observedAt: progressObservedAt,
|
|
264
|
+
observedAtMs: progressObservedAtMs,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
const intents = [];
|
|
268
|
+
for (const progress of latestFinalizingByLane.values()) {
|
|
269
|
+
if (terminalLaneIds.has(progress.laneId))
|
|
270
|
+
continue;
|
|
271
|
+
const activeSignalMs = latestActiveSignalByLane.get(progress.laneId);
|
|
272
|
+
const finalizingAgeMs = observedAtMs - progress.observedAtMs;
|
|
273
|
+
const activeSignalAgeMs = activeSignalMs === undefined ? 0 : observedAtMs - activeSignalMs;
|
|
274
|
+
if (finalizingAgeMs < input.graceMs && activeSignalAgeMs < input.graceMs)
|
|
275
|
+
continue;
|
|
276
|
+
const attemptId = progress.attemptId ?? attemptIdByLane.get(progress.laneId);
|
|
277
|
+
const taskId = progress.taskId ?? taskIdByLane.get(progress.laneId);
|
|
278
|
+
const progressSeq = progress.progressSeq;
|
|
279
|
+
if (attemptId === undefined || taskId === undefined || progressSeq === undefined)
|
|
280
|
+
continue;
|
|
281
|
+
const evidenceId = finalizingInconsistencyEvidenceId(progress.laneId);
|
|
282
|
+
if (existingInconsistencyIds.has(evidenceId))
|
|
283
|
+
continue;
|
|
284
|
+
const record = {
|
|
285
|
+
schema_version: "flowdesk.agent_task_inconsistency.v1",
|
|
286
|
+
workflow_id: input.workflowId,
|
|
287
|
+
attempt_id: attemptId,
|
|
288
|
+
lane_id: progress.laneId,
|
|
289
|
+
task_id: taskId,
|
|
290
|
+
last_progress_seq: progressSeq,
|
|
291
|
+
last_progress_observed_at: progress.observedAt,
|
|
292
|
+
inconsistency_kind: "finalizing_without_terminal",
|
|
293
|
+
grace_window_ms: input.graceMs,
|
|
294
|
+
grace_source_label: input.graceMs === DEFAULT_AGENT_TASK_FINALIZING_INCONSISTENCY_GRACE_MS
|
|
295
|
+
? "default_status_live_finalizing_inconsistency_grace"
|
|
296
|
+
: "configured_status_live_finalizing_inconsistency_grace",
|
|
297
|
+
observed_at: input.observedAt,
|
|
298
|
+
safe_next_actions: [
|
|
299
|
+
"/flowdesk-status",
|
|
300
|
+
"/flowdesk-abort",
|
|
301
|
+
"/flowdesk-retry",
|
|
302
|
+
"/flowdesk-doctor",
|
|
303
|
+
"/flowdesk-export-debug",
|
|
304
|
+
],
|
|
305
|
+
redaction_version: "v1",
|
|
306
|
+
dispatch_authority_enabled: false,
|
|
307
|
+
};
|
|
308
|
+
const prepared = prepareFlowDeskSessionEvidenceWriteIntentV1({
|
|
309
|
+
workflowId: input.workflowId,
|
|
310
|
+
evidenceId,
|
|
311
|
+
record,
|
|
312
|
+
});
|
|
313
|
+
if (prepared.ok && prepared.writeIntent !== undefined)
|
|
314
|
+
intents.push(prepared.writeIntent);
|
|
315
|
+
}
|
|
316
|
+
if (intents.length === 0)
|
|
317
|
+
return false;
|
|
318
|
+
const applied = applyFlowDeskSessionEvidenceWriteIntentsV1(input.rootDir, intents);
|
|
319
|
+
return applied.ok && applied.writtenPaths.length > 0;
|
|
320
|
+
}
|
|
321
|
+
function compactPromptPreview(value, max = 96) {
|
|
322
|
+
if (value === undefined)
|
|
323
|
+
return undefined;
|
|
324
|
+
const compact = value.replace(/\s+/g, " ").trim();
|
|
325
|
+
if (compact.length === 0)
|
|
326
|
+
return undefined;
|
|
327
|
+
return compact.length > max ? `${compact.slice(0, Math.max(0, max - 1))}…` : compact;
|
|
328
|
+
}
|
|
140
329
|
function summarizeWorkflow(workflowId, reload, maxRecent) {
|
|
141
330
|
const counts = {};
|
|
142
331
|
const recent = {};
|
|
@@ -144,6 +333,19 @@ function summarizeWorkflow(workflowId, reload, maxRecent) {
|
|
|
144
333
|
const laneLifecycleStates = [];
|
|
145
334
|
let regatePlanState;
|
|
146
335
|
let providerAcquisitionStatus;
|
|
336
|
+
let workflowAuthoringStatus;
|
|
337
|
+
let workflowAuthoringGoalSummary;
|
|
338
|
+
let taskGraphTaskCount;
|
|
339
|
+
let taskGraphEdgeCount;
|
|
340
|
+
let taskAgentAssignmentCount;
|
|
341
|
+
let taskModelSelectionStatus;
|
|
342
|
+
let taskModelSelectionBlockedLabels;
|
|
343
|
+
let workflowSynthesisId;
|
|
344
|
+
let workflowSynthesisTasksSummarized;
|
|
345
|
+
let workflowSynthesisConflictDetected;
|
|
346
|
+
let workflowSynthesisSummaryPreview;
|
|
347
|
+
let workflowDispatchPlanRevisionId;
|
|
348
|
+
let workflowDispatchPlanTaskCount;
|
|
147
349
|
for (const evidenceClass of FLOWDESK_SESSION_EVIDENCE_CLASSES) {
|
|
148
350
|
const classEntries = reload.entries.filter((entry) => entry.evidenceClass === evidenceClass);
|
|
149
351
|
if (classEntries.length === 0)
|
|
@@ -187,6 +389,57 @@ function summarizeWorkflow(workflowId, reload, maxRecent) {
|
|
|
187
389
|
providerAcquisitionStatus = acquisitionStatus;
|
|
188
390
|
}
|
|
189
391
|
}
|
|
392
|
+
if (evidenceClass === "workflow_authoring_result") {
|
|
393
|
+
const last = classEntries[classEntries.length - 1];
|
|
394
|
+
if (last !== undefined) {
|
|
395
|
+
workflowAuthoringStatus = getStringField(last.record, "status");
|
|
396
|
+
workflowAuthoringGoalSummary = getStringField(last.record, "goal_summary");
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (evidenceClass === "task_graph") {
|
|
400
|
+
const last = classEntries[classEntries.length - 1];
|
|
401
|
+
if (last !== undefined) {
|
|
402
|
+
const nodes = last.record.nodes;
|
|
403
|
+
const edges = last.record.edges;
|
|
404
|
+
if (Array.isArray(nodes))
|
|
405
|
+
taskGraphTaskCount = nodes.length;
|
|
406
|
+
if (Array.isArray(edges))
|
|
407
|
+
taskGraphEdgeCount = edges.length;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (evidenceClass === "task_agent_assignment") {
|
|
411
|
+
taskAgentAssignmentCount = classEntries.length;
|
|
412
|
+
}
|
|
413
|
+
if (evidenceClass === "task_model_selection") {
|
|
414
|
+
const last = classEntries[classEntries.length - 1];
|
|
415
|
+
if (last !== undefined) {
|
|
416
|
+
taskModelSelectionStatus = getStringField(last.record, "selection_status");
|
|
417
|
+
const blockedLabels = last.record.blocked_labels;
|
|
418
|
+
if (Array.isArray(blockedLabels)) {
|
|
419
|
+
taskModelSelectionBlockedLabels = blockedLabels.filter((label) => typeof label === "string");
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (evidenceClass === "workflow_synthesis_result") {
|
|
424
|
+
const last = classEntries[classEntries.length - 1];
|
|
425
|
+
if (last !== undefined) {
|
|
426
|
+
workflowSynthesisId = getStringField(last.record, "synthesis_id") ?? last.evidenceId;
|
|
427
|
+
const tasks = last.record.tasks_summarized;
|
|
428
|
+
if (typeof tasks === "number")
|
|
429
|
+
workflowSynthesisTasksSummarized = tasks;
|
|
430
|
+
workflowSynthesisConflictDetected = last.record.conflict_detected === true;
|
|
431
|
+
workflowSynthesisSummaryPreview = synthesisSummaryPreview(getStringField(last.record, "synthesis_summary"));
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (evidenceClass === "workflow_dispatch_plan") {
|
|
435
|
+
const last = classEntries[classEntries.length - 1];
|
|
436
|
+
if (last !== undefined) {
|
|
437
|
+
workflowDispatchPlanRevisionId = getStringField(last.record, "plan_revision_id");
|
|
438
|
+
const tasks = last.record.tasks;
|
|
439
|
+
if (Array.isArray(tasks))
|
|
440
|
+
workflowDispatchPlanTaskCount = tasks.length;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
190
443
|
}
|
|
191
444
|
const providerUsageSummary = summarizeLatestProviderUsageSnapshot(reload.entries);
|
|
192
445
|
return {
|
|
@@ -217,8 +470,248 @@ function summarizeWorkflow(workflowId, reload, maxRecent) {
|
|
|
217
470
|
...(providerUsageSummary.count > 0
|
|
218
471
|
? { providerUsageSnapshotCount: providerUsageSummary.count }
|
|
219
472
|
: {}),
|
|
473
|
+
...(workflowAuthoringStatus !== undefined
|
|
474
|
+
? { latestWorkflowAuthoringStatus: workflowAuthoringStatus }
|
|
475
|
+
: {}),
|
|
476
|
+
...(workflowAuthoringGoalSummary !== undefined
|
|
477
|
+
? { latestWorkflowAuthoringGoalSummary: workflowAuthoringGoalSummary }
|
|
478
|
+
: {}),
|
|
479
|
+
...(taskGraphTaskCount !== undefined
|
|
480
|
+
? { latestTaskGraphTaskCount: taskGraphTaskCount }
|
|
481
|
+
: {}),
|
|
482
|
+
...(taskGraphEdgeCount !== undefined
|
|
483
|
+
? { latestTaskGraphEdgeCount: taskGraphEdgeCount }
|
|
484
|
+
: {}),
|
|
485
|
+
...(taskAgentAssignmentCount !== undefined
|
|
486
|
+
? { latestTaskAgentAssignmentCount: taskAgentAssignmentCount }
|
|
487
|
+
: {}),
|
|
488
|
+
...(taskModelSelectionStatus !== undefined
|
|
489
|
+
? { latestTaskModelSelectionStatus: taskModelSelectionStatus }
|
|
490
|
+
: {}),
|
|
491
|
+
...(taskModelSelectionBlockedLabels !== undefined
|
|
492
|
+
? { latestTaskModelSelectionBlockedLabels: taskModelSelectionBlockedLabels }
|
|
493
|
+
: {}),
|
|
494
|
+
...(workflowSynthesisId !== undefined
|
|
495
|
+
? { latestWorkflowSynthesisId: workflowSynthesisId }
|
|
496
|
+
: {}),
|
|
497
|
+
...(workflowSynthesisTasksSummarized !== undefined
|
|
498
|
+
? { latestWorkflowSynthesisTasksSummarized: workflowSynthesisTasksSummarized }
|
|
499
|
+
: {}),
|
|
500
|
+
...(workflowSynthesisConflictDetected !== undefined
|
|
501
|
+
? { latestWorkflowSynthesisConflictDetected: workflowSynthesisConflictDetected }
|
|
502
|
+
: {}),
|
|
503
|
+
...(workflowSynthesisSummaryPreview !== undefined
|
|
504
|
+
? { latestWorkflowSynthesisSummaryPreview: workflowSynthesisSummaryPreview }
|
|
505
|
+
: {}),
|
|
506
|
+
...(workflowDispatchPlanRevisionId !== undefined
|
|
507
|
+
? {
|
|
508
|
+
latestWorkflowDispatchPlanRevisionId: workflowDispatchPlanRevisionId,
|
|
509
|
+
}
|
|
510
|
+
: {}),
|
|
511
|
+
...(workflowDispatchPlanTaskCount !== undefined
|
|
512
|
+
? { latestWorkflowDispatchPlanTaskCount: workflowDispatchPlanTaskCount }
|
|
513
|
+
: {}),
|
|
220
514
|
};
|
|
221
515
|
}
|
|
516
|
+
function buildLaneProgressCards(workflowId, reload, projection) {
|
|
517
|
+
const lifecycleMeta = new Map();
|
|
518
|
+
const verdictLabels = new Map();
|
|
519
|
+
const taskResultLaneIds = new Set();
|
|
520
|
+
const taskResultByLane = new Map();
|
|
521
|
+
const agentTaskContextByLane = new Map();
|
|
522
|
+
const childSessionByLane = new Map();
|
|
523
|
+
const agentTaskProgressByLane = new Map();
|
|
524
|
+
for (const entry of reload.entries) {
|
|
525
|
+
if (entry.evidenceClass === "agent_task_progress") {
|
|
526
|
+
const laneId = getStringField(entry.record, "lane_id");
|
|
527
|
+
if (laneId !== undefined) {
|
|
528
|
+
const observedAt = getStringField(entry.record, "observed_at");
|
|
529
|
+
const observedAtMs = observedAt === undefined ? 0 : Date.parse(observedAt);
|
|
530
|
+
const current = agentTaskProgressByLane.get(laneId);
|
|
531
|
+
if (current === undefined || current.observedAtMs <= observedAtMs) {
|
|
532
|
+
agentTaskProgressByLane.set(laneId, {
|
|
533
|
+
observedAtMs: Number.isFinite(observedAtMs) ? observedAtMs : 0,
|
|
534
|
+
phase: getStringField(entry.record, "phase"),
|
|
535
|
+
label: getStringField(entry.record, "progress_label"),
|
|
536
|
+
observedAt,
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
if (entry.evidenceClass === "agent_task_context") {
|
|
543
|
+
const laneId = getStringField(entry.record, "lane_id");
|
|
544
|
+
if (laneId !== undefined) {
|
|
545
|
+
agentTaskContextByLane.set(laneId, {
|
|
546
|
+
taskId: getStringField(entry.record, "task_id"),
|
|
547
|
+
promptPreview: compactPromptPreview(getStringField(entry.record, "prompt_text")),
|
|
548
|
+
promptTextTruncated: entry.record.prompt_text_truncated === true,
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
if (entry.evidenceClass === "agent_task_child_session") {
|
|
554
|
+
const laneId = getStringField(entry.record, "lane_id");
|
|
555
|
+
const nudgeCount = entry.record.nudge_count;
|
|
556
|
+
if (laneId !== undefined) {
|
|
557
|
+
childSessionByLane.set(laneId, {
|
|
558
|
+
...(typeof nudgeCount === "number" && Number.isFinite(nudgeCount)
|
|
559
|
+
? { nudgeCount }
|
|
560
|
+
: {}),
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
if (entry.evidenceClass === "task_result") {
|
|
566
|
+
const laneId = getStringField(entry.record, "lane_id");
|
|
567
|
+
if (laneId !== undefined) {
|
|
568
|
+
taskResultLaneIds.add(laneId);
|
|
569
|
+
taskResultByLane.set(laneId, {
|
|
570
|
+
taskId: getStringField(entry.record, "task_id"),
|
|
571
|
+
completionStatus: getStringField(entry.record, "completion_status"),
|
|
572
|
+
outputKind: getStringField(entry.record, "output_kind"),
|
|
573
|
+
...(typeof entry.record.usable_for_synthesis === "boolean"
|
|
574
|
+
? { usableForSynthesis: entry.record.usable_for_synthesis }
|
|
575
|
+
: {}),
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
if (entry.evidenceClass === "reviewer_verdict") {
|
|
581
|
+
const label = getStringField(entry.record, "verdict_label");
|
|
582
|
+
if (label === "pass" ||
|
|
583
|
+
label === "changes_required" ||
|
|
584
|
+
label === "blocked" ||
|
|
585
|
+
label === "inconclusive")
|
|
586
|
+
verdictLabels.set(entry.evidenceId, label);
|
|
587
|
+
}
|
|
588
|
+
if (entry.evidenceClass !== "lane_lifecycle")
|
|
589
|
+
continue;
|
|
590
|
+
const laneId = getStringField(entry.record, "lane_id");
|
|
591
|
+
if (laneId === undefined)
|
|
592
|
+
continue;
|
|
593
|
+
const updatedAtRaw = getStringField(entry.record, "updated_at") ??
|
|
594
|
+
getStringField(entry.record, "created_at");
|
|
595
|
+
const updatedAtMs = updatedAtRaw === undefined ? 0 : Date.parse(updatedAtRaw);
|
|
596
|
+
const current = lifecycleMeta.get(laneId);
|
|
597
|
+
if (current !== undefined && current.updatedAtMs > updatedAtMs)
|
|
598
|
+
continue;
|
|
599
|
+
lifecycleMeta.set(laneId, {
|
|
600
|
+
updatedAtMs: Number.isFinite(updatedAtMs) ? updatedAtMs : 0,
|
|
601
|
+
state: getStringField(entry.record, "state"),
|
|
602
|
+
agentRef: getStringField(entry.record, "agent_ref"),
|
|
603
|
+
providerQualifiedModelId: getStringField(entry.record, "provider_qualified_model_id"),
|
|
604
|
+
verdictRef: getStringField(entry.record, "verdict_ref"),
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
return projection.entries.slice(0, 6).map((entry) => {
|
|
608
|
+
const meta = lifecycleMeta.get(entry.laneId);
|
|
609
|
+
const context = agentTaskContextByLane.get(entry.laneId);
|
|
610
|
+
const childSession = childSessionByLane.get(entry.laneId);
|
|
611
|
+
const progress = agentTaskProgressByLane.get(entry.laneId);
|
|
612
|
+
const taskResult = taskResultByLane.get(entry.laneId);
|
|
613
|
+
const projectedState = meta?.state === "incomplete" && taskResultLaneIds.has(entry.laneId)
|
|
614
|
+
? "task_result"
|
|
615
|
+
: (meta?.state ?? entry.lifecycleState);
|
|
616
|
+
const taskId = context?.taskId ?? taskResult?.taskId;
|
|
617
|
+
const verdictLabel = meta?.verdictRef === undefined
|
|
618
|
+
? undefined
|
|
619
|
+
: verdictLabels.get(meta.verdictRef);
|
|
620
|
+
return {
|
|
621
|
+
workflowId,
|
|
622
|
+
laneId: entry.laneId,
|
|
623
|
+
...(taskId === undefined ? {} : { taskId }),
|
|
624
|
+
attemptId: entry.attemptId,
|
|
625
|
+
state: projectedState,
|
|
626
|
+
classification: entry.classification,
|
|
627
|
+
...(entry.secondsSinceLastSignal === undefined
|
|
628
|
+
? {}
|
|
629
|
+
: { secondsSinceLastSignal: entry.secondsSinceLastSignal }),
|
|
630
|
+
...(entry.lastSignalSource === undefined
|
|
631
|
+
? {}
|
|
632
|
+
: { lastSignalSource: entry.lastSignalSource }),
|
|
633
|
+
...(meta?.agentRef === undefined ? {} : { agentRef: meta.agentRef }),
|
|
634
|
+
...(meta?.providerQualifiedModelId === undefined
|
|
635
|
+
? {}
|
|
636
|
+
: { providerQualifiedModelId: meta.providerQualifiedModelId }),
|
|
637
|
+
...(context?.promptPreview === undefined ? {} : { promptPreview: context.promptPreview }),
|
|
638
|
+
...(context?.promptTextTruncated === undefined ? {} : { promptTextTruncated: context.promptTextTruncated }),
|
|
639
|
+
...(childSession?.nudgeCount === undefined ? {} : { nudgeCount: childSession.nudgeCount }),
|
|
640
|
+
...(progress?.phase === undefined ? {} : { progressPhase: progress.phase }),
|
|
641
|
+
...(progress?.label === undefined ? {} : { progressLabel: progress.label }),
|
|
642
|
+
...(progress?.observedAt === undefined ? {} : { progressObservedAt: progress.observedAt }),
|
|
643
|
+
...(verdictLabel === undefined ? {} : { verdictLabel }),
|
|
644
|
+
...(taskResult?.completionStatus === undefined
|
|
645
|
+
? {}
|
|
646
|
+
: { completionStatus: taskResult.completionStatus }),
|
|
647
|
+
...(taskResult?.outputKind === undefined
|
|
648
|
+
? {}
|
|
649
|
+
: { outputKind: taskResult.outputKind }),
|
|
650
|
+
...(taskResult?.usableForSynthesis === undefined
|
|
651
|
+
? {}
|
|
652
|
+
: { usableForSynthesis: taskResult.usableForSynthesis }),
|
|
653
|
+
...(entry.failureHint === undefined
|
|
654
|
+
? {}
|
|
655
|
+
: { failureHint: entry.failureHint }),
|
|
656
|
+
statusCommandRef: "/flowdesk-status",
|
|
657
|
+
debugCommandRef: "/flowdesk-export-debug",
|
|
658
|
+
};
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
function buildLaneProgressAggregate(cards, synthesisAlreadyRecorded = false) {
|
|
662
|
+
const expected = cards.length;
|
|
663
|
+
const terminal = cards.filter(card => card.classification === "terminal").length;
|
|
664
|
+
const taskResult = cards.filter(card => card.state === "task_result").length;
|
|
665
|
+
const failed = cards.filter(card => card.failureHint !== undefined || card.state === "invocation_failed" || card.state === "task_failed").length;
|
|
666
|
+
const awaitingPermission = cards.filter(card => card.progressPhase === "awaiting_permission").length;
|
|
667
|
+
const normalCompleted = cards.filter(card => card.state === "task_result" &&
|
|
668
|
+
card.classification === "terminal" &&
|
|
669
|
+
card.completionStatus !== "partial" &&
|
|
670
|
+
card.usableForSynthesis !== false &&
|
|
671
|
+
card.failureHint === undefined).length;
|
|
672
|
+
const nextActionReady = expected > 0 && normalCompleted === expected && !synthesisAlreadyRecorded;
|
|
673
|
+
return {
|
|
674
|
+
expected,
|
|
675
|
+
terminal,
|
|
676
|
+
taskResult,
|
|
677
|
+
failed,
|
|
678
|
+
awaitingPermission,
|
|
679
|
+
normalCompleted,
|
|
680
|
+
autoNextStepEligible: nextActionReady,
|
|
681
|
+
nextActionAvailable: nextActionReady,
|
|
682
|
+
...(nextActionReady ? { nextActionKind: "synthesis" } : {}),
|
|
683
|
+
nextActionRefs: ["/flowdesk-status", "/flowdesk-export-debug"],
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
function buildSubtaskActivityRows(cards) {
|
|
687
|
+
return cards.map((card) => {
|
|
688
|
+
const recoveryActionRefs = card.failureHint !== undefined ||
|
|
689
|
+
card.classification === "stalled" ||
|
|
690
|
+
card.classification === "progressing_late"
|
|
691
|
+
? ["/flowdesk-status", "/flowdesk-retry", "/flowdesk-resume", "/flowdesk-abort", "/flowdesk-export-debug"]
|
|
692
|
+
: ["/flowdesk-status", "/flowdesk-export-debug"];
|
|
693
|
+
return {
|
|
694
|
+
workflowId: card.workflowId,
|
|
695
|
+
laneId: card.laneId,
|
|
696
|
+
...(card.taskId === undefined ? {} : { taskId: card.taskId }),
|
|
697
|
+
...(card.attemptId === undefined ? {} : { attemptId: card.attemptId }),
|
|
698
|
+
...(card.state === undefined ? {} : { state: card.state }),
|
|
699
|
+
classification: card.classification,
|
|
700
|
+
...(card.progressPhase === undefined ? {} : { progressPhase: card.progressPhase }),
|
|
701
|
+
...(card.progressLabel === undefined ? {} : { progressLabel: card.progressLabel }),
|
|
702
|
+
...(card.progressObservedAt === undefined ? {} : { lastObservedAt: card.progressObservedAt }),
|
|
703
|
+
...(card.nudgeCount === undefined ? {} : { nudgeCount: card.nudgeCount }),
|
|
704
|
+
...(card.completionStatus === undefined ? {} : { completionStatus: card.completionStatus }),
|
|
705
|
+
...(card.outputKind === undefined ? {} : { outputKind: card.outputKind }),
|
|
706
|
+
...(card.usableForSynthesis === undefined ? {} : { usableForSynthesis: card.usableForSynthesis }),
|
|
707
|
+
...(card.verdictLabel === undefined ? {} : { verdictLabel: card.verdictLabel }),
|
|
708
|
+
...(card.failureHint === undefined ? {} : { failureHint: card.failureHint }),
|
|
709
|
+
recoveryActionRefs,
|
|
710
|
+
statusCommandRef: "/flowdesk-status",
|
|
711
|
+
debugCommandRef: "/flowdesk-export-debug",
|
|
712
|
+
};
|
|
713
|
+
});
|
|
714
|
+
}
|
|
222
715
|
function summarizeLatestProviderUsageSnapshot(entries) {
|
|
223
716
|
const snapshots = entries.filter((entry) => entry.evidenceClass === "provider_usage_snapshot");
|
|
224
717
|
if (snapshots.length === 0)
|
|
@@ -249,6 +742,7 @@ export async function executeFlowDeskStatusLiveV1(input) {
|
|
|
249
742
|
workflows: [],
|
|
250
743
|
redactedBlockReason: "status live tool requires a durable state root directory",
|
|
251
744
|
safeNextActions: safeNextActions(),
|
|
745
|
+
authorityCapabilitySummary: authorityCapabilitySummary(),
|
|
252
746
|
authority: blockedAuthority(),
|
|
253
747
|
};
|
|
254
748
|
}
|
|
@@ -267,51 +761,93 @@ export async function executeFlowDeskStatusLiveV1(input) {
|
|
|
267
761
|
? `no durable session evidence for workflow ${requestedWorkflowId}`
|
|
268
762
|
: "no durable session workflows found under the configured durable state root",
|
|
269
763
|
safeNextActions: safeNextActions(),
|
|
764
|
+
authorityCapabilitySummary: authorityCapabilitySummary(),
|
|
270
765
|
authority: blockedAuthority(),
|
|
271
766
|
};
|
|
272
767
|
}
|
|
273
768
|
const workflows = [];
|
|
274
769
|
for (const workflowId of workflowIds) {
|
|
275
|
-
|
|
770
|
+
backfillTerminalAgentTaskFailedLanesV1({
|
|
771
|
+
workflowId,
|
|
772
|
+
rootDir,
|
|
773
|
+
now: new Date(observedAt),
|
|
774
|
+
});
|
|
775
|
+
let reload = reloadFlowDeskSessionEvidenceV1({
|
|
276
776
|
workflowId,
|
|
277
777
|
rootDir,
|
|
278
778
|
});
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
779
|
+
let summary;
|
|
780
|
+
let projectionReload;
|
|
781
|
+
let projection;
|
|
782
|
+
const buildWorkflowSummary = () => {
|
|
783
|
+
summary = summarizeWorkflow(workflowId, reload, maxRecentEvidencePerClass);
|
|
784
|
+
projectionReload = pruneInconsistencySnapshotsNoLongerValid(pruneNonTerminalLifecycleSnapshotsNoLongerValid(reload));
|
|
785
|
+
projection = projectFlowDeskLaneStallV1({
|
|
786
|
+
workflowId,
|
|
787
|
+
reload: projectionReload,
|
|
788
|
+
observedAt,
|
|
789
|
+
...(input.config.laneHeartbeatLateThresholdMs === undefined
|
|
790
|
+
? {}
|
|
791
|
+
: { lateThresholdMs: input.config.laneHeartbeatLateThresholdMs }),
|
|
792
|
+
...(input.config.laneHeartbeatStallThresholdMs === undefined
|
|
793
|
+
? {}
|
|
794
|
+
: { stallThresholdMs: input.config.laneHeartbeatStallThresholdMs }),
|
|
795
|
+
});
|
|
796
|
+
summary.laneStallProjection = projection;
|
|
797
|
+
const projectedLifecycleStates = projection.entries
|
|
798
|
+
.map((entry) => entry.lifecycleState)
|
|
799
|
+
.filter((state) => typeof state === "string" && state.length > 0);
|
|
800
|
+
if (projectedLifecycleStates.length > 0) {
|
|
801
|
+
summary.latestLaneLifecycleStates = projectedLifecycleStates;
|
|
802
|
+
}
|
|
803
|
+
summary.worstLaneStallClassification = projection.worstClassification;
|
|
804
|
+
summary.stalledLaneCount = projection.totalStalledLanes;
|
|
805
|
+
summary.progressingLateLaneCount = projection.totalLateLanes;
|
|
806
|
+
summary.inconsistentFinalizingWithoutTerminalLaneCount =
|
|
807
|
+
projection.totalInconsistentLanes;
|
|
808
|
+
summary.laneProgressCards = buildLaneProgressCards(workflowId, projectionReload, projection);
|
|
809
|
+
summary.subtaskActivityRows = buildSubtaskActivityRows(summary.laneProgressCards);
|
|
810
|
+
summary.laneProgressAggregate = buildLaneProgressAggregate(summary.laneProgressCards, summary.latestWorkflowSynthesisTasksSummarized !== undefined);
|
|
811
|
+
};
|
|
812
|
+
const graceMs = clampFinalizingInconsistencyGraceMs(input.config.agentTaskFinalizingInconsistencyGraceMs);
|
|
813
|
+
const wroteInconsistency = materializeFinalizingWithoutTerminalInconsistencies({
|
|
282
814
|
workflowId,
|
|
283
|
-
|
|
815
|
+
rootDir,
|
|
816
|
+
reload,
|
|
284
817
|
observedAt,
|
|
285
|
-
|
|
286
|
-
? {}
|
|
287
|
-
: { lateThresholdMs: input.config.laneHeartbeatLateThresholdMs }),
|
|
288
|
-
...(input.config.laneHeartbeatStallThresholdMs === undefined
|
|
289
|
-
? {}
|
|
290
|
-
: { stallThresholdMs: input.config.laneHeartbeatStallThresholdMs }),
|
|
818
|
+
graceMs,
|
|
291
819
|
});
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
820
|
+
if (wroteInconsistency) {
|
|
821
|
+
reload = reloadFlowDeskSessionEvidenceV1({
|
|
822
|
+
workflowId,
|
|
823
|
+
rootDir,
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
buildWorkflowSummary();
|
|
296
827
|
workflows.push(summary);
|
|
297
828
|
}
|
|
298
829
|
const observed = workflows.some((summary) => Object.values(summary.evidenceCounts).some((count) => typeof count === "number" && count > 0));
|
|
299
830
|
const totalStalled = workflows.reduce((sum, summary) => sum + (summary.stalledLaneCount ?? 0), 0);
|
|
300
831
|
const totalLate = workflows.reduce((sum, summary) => sum + (summary.progressingLateLaneCount ?? 0), 0);
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
832
|
+
const totalInconsistent = workflows.reduce((sum, summary) => sum + (summary.inconsistentFinalizingWithoutTerminalLaneCount ?? 0), 0);
|
|
833
|
+
const worstLaneStallClassification = workflows.some((summary) => summary.worstLaneStallClassification ===
|
|
834
|
+
"inconsistent_finalizing_without_terminal")
|
|
835
|
+
? "inconsistent_finalizing_without_terminal"
|
|
836
|
+
: workflows.some((summary) => summary.worstLaneStallClassification === "stalled")
|
|
837
|
+
? "stalled"
|
|
838
|
+
: workflows.some((summary) => summary.worstLaneStallClassification === "progressing_late")
|
|
839
|
+
? "progressing_late"
|
|
840
|
+
: workflows.some((summary) => summary.worstLaneStallClassification === "progressing_normal")
|
|
841
|
+
? "progressing_normal"
|
|
842
|
+
: workflows.some((summary) => summary.worstLaneStallClassification === "terminal")
|
|
843
|
+
? "terminal"
|
|
844
|
+
: "unknown";
|
|
310
845
|
const summaryForUser = buildStatusLiveSummaryForUser({
|
|
311
846
|
workflows,
|
|
312
847
|
worstLaneStallClassification,
|
|
313
848
|
totalStalled,
|
|
314
849
|
totalLate,
|
|
850
|
+
totalInconsistent,
|
|
315
851
|
requestedWorkflowId,
|
|
316
852
|
});
|
|
317
853
|
return {
|
|
@@ -324,8 +860,10 @@ export async function executeFlowDeskStatusLiveV1(input) {
|
|
|
324
860
|
worstLaneStallClassification,
|
|
325
861
|
totalStalledLaneCount: totalStalled,
|
|
326
862
|
totalProgressingLateLaneCount: totalLate,
|
|
863
|
+
totalInconsistentFinalizingWithoutTerminalLaneCount: totalInconsistent,
|
|
327
864
|
summaryForUser,
|
|
328
865
|
safeNextActions: safeNextActions(),
|
|
866
|
+
authorityCapabilitySummary: authorityCapabilitySummary(),
|
|
329
867
|
authority: {
|
|
330
868
|
realOpenCodeDispatch: false,
|
|
331
869
|
providerCall: false,
|
|
@@ -345,26 +883,70 @@ function buildStatusLiveSummaryForUser(input) {
|
|
|
345
883
|
? `FlowDesk status: no durable evidence for workflow ${input.requestedWorkflowId}.`
|
|
346
884
|
: "FlowDesk status: no durable workflows found.";
|
|
347
885
|
}
|
|
348
|
-
const headline = input.
|
|
349
|
-
? `FlowDesk status: ${workflowsCount} workflow(s);
|
|
350
|
-
: input.
|
|
351
|
-
? `FlowDesk status: ${workflowsCount} workflow(s); ${input.totalLate} progressing-late
|
|
352
|
-
: input.
|
|
353
|
-
? `FlowDesk status: ${workflowsCount} workflow(s);
|
|
354
|
-
: input.worstLaneStallClassification === "
|
|
355
|
-
? `FlowDesk status: ${workflowsCount} workflow(s);
|
|
356
|
-
:
|
|
886
|
+
const headline = input.totalInconsistent > 0
|
|
887
|
+
? `FlowDesk status: ${workflowsCount} workflow(s); ${input.totalInconsistent} finalizing-without-terminal inconsistent lane(s) require manual recovery.`
|
|
888
|
+
: input.totalStalled > 0
|
|
889
|
+
? `FlowDesk status: ${workflowsCount} workflow(s); worst classification stalled (${input.totalStalled} stalled, ${input.totalLate} progressing-late).`
|
|
890
|
+
: input.totalLate > 0
|
|
891
|
+
? `FlowDesk status: ${workflowsCount} workflow(s); ${input.totalLate} progressing-late, no stalled lanes.`
|
|
892
|
+
: input.worstLaneStallClassification === "terminal"
|
|
893
|
+
? `FlowDesk status: ${workflowsCount} workflow(s); all observed lanes terminal/complete.`
|
|
894
|
+
: input.worstLaneStallClassification === "progressing_normal"
|
|
895
|
+
? `FlowDesk status: ${workflowsCount} workflow(s); active lanes progressing_normal.`
|
|
896
|
+
: `FlowDesk status: ${workflowsCount} workflow(s); worst classification ${input.worstLaneStallClassification}.`;
|
|
357
897
|
const perWorkflow = input.workflows.slice(0, 3).map((workflow) => {
|
|
358
898
|
const verdictLabels = workflow.latestReviewerVerdictLabels;
|
|
359
899
|
const verdictText = verdictLabels.length > 0
|
|
360
900
|
? `verdicts=${verdictLabels.join("/")}`
|
|
361
901
|
: "verdicts=(none)";
|
|
362
|
-
const
|
|
902
|
+
const projectedLaneStates = (workflow.laneProgressCards ?? [])
|
|
903
|
+
.slice(0, 3)
|
|
904
|
+
.map((lane) => `${lane.state ?? "unknown"}/${lane.classification}`);
|
|
905
|
+
const lifecycleStates = projectedLaneStates.length > 0 ? projectedLaneStates : workflow.latestLaneLifecycleStates;
|
|
363
906
|
const lifecycleText = lifecycleStates.length > 0
|
|
364
|
-
?
|
|
365
|
-
: "
|
|
907
|
+
? `${projectedLaneStates.length > 0 ? "lane_state" : "lifecycle"}=${lifecycleStates.slice(0, 3).join("/")}`
|
|
908
|
+
: "lane_state=(none)";
|
|
909
|
+
const synthesisPreview = workflow.latestWorkflowSynthesisSummaryPreview === undefined
|
|
910
|
+
? ""
|
|
911
|
+
: ` preview="${workflow.latestWorkflowSynthesisSummaryPreview.replace(/"/g, "'").slice(0, 96)}"`;
|
|
912
|
+
const synthesisText = workflow.latestWorkflowSynthesisTasksSummarized !== undefined
|
|
913
|
+
? ` synthesis=${workflow.latestWorkflowSynthesisTasksSummarized} tasks${workflow.latestWorkflowSynthesisConflictDetected ? " conflict=detected" : ""}${synthesisPreview}`
|
|
914
|
+
: "";
|
|
915
|
+
const autoNextText = workflow.laneProgressAggregate?.autoNextStepEligible === true ? " auto_next=ready" : "";
|
|
916
|
+
const nextActionText = workflow.laneProgressAggregate?.nextActionAvailable === true
|
|
917
|
+
? ` next_action=${workflow.laneProgressAggregate.nextActionKind ?? "available"}_ready`
|
|
918
|
+
: "";
|
|
919
|
+
const planText = workflow.latestWorkflowDispatchPlanRevisionId !== undefined
|
|
920
|
+
? `workflow_plan=${workflow.latestWorkflowDispatchPlanRevisionId} tasks=${workflow.latestWorkflowDispatchPlanTaskCount ?? "unknown"}${synthesisText}${autoNextText}${nextActionText}`
|
|
921
|
+
: workflow.latestTaskGraphTaskCount !== undefined
|
|
922
|
+
? `planning_slice=${workflow.latestWorkflowAuthoringStatus ?? "unknown"} tasks=${workflow.latestTaskGraphTaskCount} assignments=${workflow.latestTaskAgentAssignmentCount ?? 0} model=${workflow.latestTaskModelSelectionStatus ?? "unknown"}${synthesisText}${autoNextText}${nextActionText}`
|
|
923
|
+
: synthesisText.length > 0
|
|
924
|
+
? `${synthesisText.trim()}${autoNextText}${nextActionText}`
|
|
925
|
+
: `workflow_plan=(none)${autoNextText}${nextActionText}`;
|
|
366
926
|
const classification = workflow.worstLaneStallClassification ?? "unknown";
|
|
367
|
-
|
|
927
|
+
const lanePreview = (workflow.laneProgressCards ?? [])
|
|
928
|
+
.slice(0, 2)
|
|
929
|
+
.map((lane) => {
|
|
930
|
+
const age = lane.secondsSinceLastSignal === undefined
|
|
931
|
+
? "unknown"
|
|
932
|
+
: `~${Math.floor(lane.secondsSinceLastSignal / 60)}m`;
|
|
933
|
+
return `${lane.laneId}:${lane.state ?? "unknown"}/${lane.classification}/last=${age}`;
|
|
934
|
+
})
|
|
935
|
+
.join(", ");
|
|
936
|
+
const subtaskPreview = (workflow.subtaskActivityRows ?? [])
|
|
937
|
+
.slice(0, 3)
|
|
938
|
+
.map((row) => {
|
|
939
|
+
const actions = (row.recoveryActionRefs ?? [])
|
|
940
|
+
.slice(0, 5)
|
|
941
|
+
.map((action) => action.replace("/flowdesk-", ""))
|
|
942
|
+
.join("|");
|
|
943
|
+
const compact = `${row.laneId}:${row.state ?? "unknown"}/${row.classification}${actions.length > 0 ? `[${actions}]` : ""}`;
|
|
944
|
+
return compact.length > 96 ? `${compact.slice(0, 95)}…` : compact;
|
|
945
|
+
})
|
|
946
|
+
.join("; ");
|
|
947
|
+
const laneText = lanePreview.length > 0 && subtaskPreview.length === 0 ? `, lanes=${lanePreview}` : "";
|
|
948
|
+
const subtaskText = subtaskPreview.length > 0 ? `, subtasks=${subtaskPreview}` : "";
|
|
949
|
+
return `- ${workflow.workflowId}: ${classification}, ${verdictText}, ${lifecycleText}, ${planText}${laneText}${subtaskText}`;
|
|
368
950
|
});
|
|
369
951
|
return [headline, ...perWorkflow].join("\n");
|
|
370
952
|
}
|