@flowdesk/opencode-plugin 0.1.13 → 0.1.15
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 +29 -0
- package/dist/agent-task-output.d.ts.map +1 -0
- package/dist/agent-task-output.js +225 -0
- package/dist/agent-task-output.js.map +1 -0
- package/dist/agent-task-runner.d.ts +34 -0
- package/dist/agent-task-runner.d.ts.map +1 -1
- package/dist/agent-task-runner.js +634 -84
- 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 +390 -0
- package/dist/completion-ui-cache.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 +257 -0
- package/dist/event-hook-observer.js.map +1 -0
- package/dist/managed-dispatch-adapter.d.ts +62 -0
- package/dist/managed-dispatch-adapter.d.ts.map +1 -1
- package/dist/managed-dispatch-adapter.js +472 -4
- package/dist/managed-dispatch-adapter.js.map +1 -1
- package/dist/model-selection-engine.d.ts +60 -0
- package/dist/model-selection-engine.d.ts.map +1 -0
- package/dist/model-selection-engine.js +242 -0
- package/dist/model-selection-engine.js.map +1 -0
- package/dist/provider-usage-live-tool.d.ts +10 -0
- package/dist/provider-usage-live-tool.d.ts.map +1 -1
- package/dist/provider-usage-live-tool.js +262 -33
- package/dist/provider-usage-live-tool.js.map +1 -1
- package/dist/server.d.ts +36 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +497 -20
- package/dist/server.js.map +1 -1
- package/dist/stall-recovery.d.ts +34 -0
- package/dist/stall-recovery.d.ts.map +1 -1
- package/dist/stall-recovery.js +680 -3
- package/dist/stall-recovery.js.map +1 -1
- package/dist/status-live-tool.d.ts +54 -0
- package/dist/status-live-tool.d.ts.map +1 -1
- package/dist/status-live-tool.js +449 -44
- package/dist/status-live-tool.js.map +1 -1
- package/dist/tui-subtask-activity.d.ts +73 -0
- package/dist/tui-subtask-activity.d.ts.map +1 -0
- package/dist/tui-subtask-activity.js +271 -0
- package/dist/tui-subtask-activity.js.map +1 -0
- package/dist/tui-usage-snapshot.d.ts +14 -0
- package/dist/tui-usage-snapshot.d.ts.map +1 -1
- package/dist/tui-usage-snapshot.js +275 -8
- package/dist/tui-usage-snapshot.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +102 -44
- package/dist/tui.js.map +1 -1
- 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 +135 -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-tool.d.ts +12 -0
- package/dist/workflow-dispatch-tool.d.ts.map +1 -1
- package/dist/workflow-dispatch-tool.js +31 -3
- package/dist/workflow-dispatch-tool.js.map +1 -1
- 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.map +1 -1
- package/dist/workflow-scheduler.js +3 -1
- package/dist/workflow-scheduler.js.map +1 -1
- 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 +2 -2
package/dist/status-live-tool.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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
4
|
import { backfillTerminalAgentTaskFailedLanesV1 } from "./stall-recovery.js";
|
|
5
5
|
const FLOWDESK_LANE_STALL_TERMINAL_STATES = new Set([
|
|
6
6
|
"complete",
|
|
@@ -90,6 +90,27 @@ function pruneNonTerminalLifecycleSnapshotsNoLongerValid(reload) {
|
|
|
90
90
|
return reload;
|
|
91
91
|
return { ...reload, entries };
|
|
92
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
|
+
}
|
|
93
114
|
const FLOWDESK_SESSION_RECORD_ROOT = ".flowdesk/sessions";
|
|
94
115
|
function blockedAuthority() {
|
|
95
116
|
return {
|
|
@@ -106,6 +127,14 @@ function blockedAuthority() {
|
|
|
106
127
|
function safeNextActions() {
|
|
107
128
|
return ["/flowdesk-status", "/flowdesk-doctor", "/flowdesk-export-debug"];
|
|
108
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
|
+
}
|
|
109
138
|
function listSessionWorkflowIds(rootDir, max) {
|
|
110
139
|
const sessionsDir = join(rootDir, FLOWDESK_SESSION_RECORD_ROOT);
|
|
111
140
|
if (!existsSync(sessionsDir))
|
|
@@ -138,6 +167,165 @@ function getStringField(record, key) {
|
|
|
138
167
|
const value = record[key];
|
|
139
168
|
return typeof value === "string" ? value : undefined;
|
|
140
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
|
+
}
|
|
141
329
|
function summarizeWorkflow(workflowId, reload, maxRecent) {
|
|
142
330
|
const counts = {};
|
|
143
331
|
const recent = {};
|
|
@@ -152,8 +340,10 @@ function summarizeWorkflow(workflowId, reload, maxRecent) {
|
|
|
152
340
|
let taskAgentAssignmentCount;
|
|
153
341
|
let taskModelSelectionStatus;
|
|
154
342
|
let taskModelSelectionBlockedLabels;
|
|
343
|
+
let workflowSynthesisId;
|
|
155
344
|
let workflowSynthesisTasksSummarized;
|
|
156
345
|
let workflowSynthesisConflictDetected;
|
|
346
|
+
let workflowSynthesisSummaryPreview;
|
|
157
347
|
let workflowDispatchPlanRevisionId;
|
|
158
348
|
let workflowDispatchPlanTaskCount;
|
|
159
349
|
for (const evidenceClass of FLOWDESK_SESSION_EVIDENCE_CLASSES) {
|
|
@@ -233,10 +423,12 @@ function summarizeWorkflow(workflowId, reload, maxRecent) {
|
|
|
233
423
|
if (evidenceClass === "workflow_synthesis_result") {
|
|
234
424
|
const last = classEntries[classEntries.length - 1];
|
|
235
425
|
if (last !== undefined) {
|
|
426
|
+
workflowSynthesisId = getStringField(last.record, "synthesis_id") ?? last.evidenceId;
|
|
236
427
|
const tasks = last.record.tasks_summarized;
|
|
237
428
|
if (typeof tasks === "number")
|
|
238
429
|
workflowSynthesisTasksSummarized = tasks;
|
|
239
430
|
workflowSynthesisConflictDetected = last.record.conflict_detected === true;
|
|
431
|
+
workflowSynthesisSummaryPreview = synthesisSummaryPreview(getStringField(last.record, "synthesis_summary"));
|
|
240
432
|
}
|
|
241
433
|
}
|
|
242
434
|
if (evidenceClass === "workflow_dispatch_plan") {
|
|
@@ -299,12 +491,18 @@ function summarizeWorkflow(workflowId, reload, maxRecent) {
|
|
|
299
491
|
...(taskModelSelectionBlockedLabels !== undefined
|
|
300
492
|
? { latestTaskModelSelectionBlockedLabels: taskModelSelectionBlockedLabels }
|
|
301
493
|
: {}),
|
|
494
|
+
...(workflowSynthesisId !== undefined
|
|
495
|
+
? { latestWorkflowSynthesisId: workflowSynthesisId }
|
|
496
|
+
: {}),
|
|
302
497
|
...(workflowSynthesisTasksSummarized !== undefined
|
|
303
498
|
? { latestWorkflowSynthesisTasksSummarized: workflowSynthesisTasksSummarized }
|
|
304
499
|
: {}),
|
|
305
500
|
...(workflowSynthesisConflictDetected !== undefined
|
|
306
501
|
? { latestWorkflowSynthesisConflictDetected: workflowSynthesisConflictDetected }
|
|
307
502
|
: {}),
|
|
503
|
+
...(workflowSynthesisSummaryPreview !== undefined
|
|
504
|
+
? { latestWorkflowSynthesisSummaryPreview: workflowSynthesisSummaryPreview }
|
|
505
|
+
: {}),
|
|
308
506
|
...(workflowDispatchPlanRevisionId !== undefined
|
|
309
507
|
? {
|
|
310
508
|
latestWorkflowDispatchPlanRevisionId: workflowDispatchPlanRevisionId,
|
|
@@ -318,7 +516,67 @@ function summarizeWorkflow(workflowId, reload, maxRecent) {
|
|
|
318
516
|
function buildLaneProgressCards(workflowId, reload, projection) {
|
|
319
517
|
const lifecycleMeta = new Map();
|
|
320
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();
|
|
321
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
|
+
}
|
|
322
580
|
if (entry.evidenceClass === "reviewer_verdict") {
|
|
323
581
|
const label = getStringField(entry.record, "verdict_label");
|
|
324
582
|
if (label === "pass" ||
|
|
@@ -348,14 +606,23 @@ function buildLaneProgressCards(workflowId, reload, projection) {
|
|
|
348
606
|
}
|
|
349
607
|
return projection.entries.slice(0, 6).map((entry) => {
|
|
350
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;
|
|
351
617
|
const verdictLabel = meta?.verdictRef === undefined
|
|
352
618
|
? undefined
|
|
353
619
|
: verdictLabels.get(meta.verdictRef);
|
|
354
620
|
return {
|
|
355
621
|
workflowId,
|
|
356
622
|
laneId: entry.laneId,
|
|
623
|
+
...(taskId === undefined ? {} : { taskId }),
|
|
357
624
|
attemptId: entry.attemptId,
|
|
358
|
-
state:
|
|
625
|
+
state: projectedState,
|
|
359
626
|
classification: entry.classification,
|
|
360
627
|
...(entry.secondsSinceLastSignal === undefined
|
|
361
628
|
? {}
|
|
@@ -367,7 +634,22 @@ function buildLaneProgressCards(workflowId, reload, projection) {
|
|
|
367
634
|
...(meta?.providerQualifiedModelId === undefined
|
|
368
635
|
? {}
|
|
369
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 }),
|
|
370
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 }),
|
|
371
653
|
...(entry.failureHint === undefined
|
|
372
654
|
? {}
|
|
373
655
|
: { failureHint: entry.failureHint }),
|
|
@@ -376,6 +658,60 @@ function buildLaneProgressCards(workflowId, reload, projection) {
|
|
|
376
658
|
};
|
|
377
659
|
});
|
|
378
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
|
+
}
|
|
379
715
|
function summarizeLatestProviderUsageSnapshot(entries) {
|
|
380
716
|
const snapshots = entries.filter((entry) => entry.evidenceClass === "provider_usage_snapshot");
|
|
381
717
|
if (snapshots.length === 0)
|
|
@@ -406,6 +742,7 @@ export async function executeFlowDeskStatusLiveV1(input) {
|
|
|
406
742
|
workflows: [],
|
|
407
743
|
redactedBlockReason: "status live tool requires a durable state root directory",
|
|
408
744
|
safeNextActions: safeNextActions(),
|
|
745
|
+
authorityCapabilitySummary: authorityCapabilitySummary(),
|
|
409
746
|
authority: blockedAuthority(),
|
|
410
747
|
};
|
|
411
748
|
}
|
|
@@ -424,6 +761,7 @@ export async function executeFlowDeskStatusLiveV1(input) {
|
|
|
424
761
|
? `no durable session evidence for workflow ${requestedWorkflowId}`
|
|
425
762
|
: "no durable session workflows found under the configured durable state root",
|
|
426
763
|
safeNextActions: safeNextActions(),
|
|
764
|
+
authorityCapabilitySummary: authorityCapabilitySummary(),
|
|
427
765
|
authority: blockedAuthority(),
|
|
428
766
|
};
|
|
429
767
|
}
|
|
@@ -433,48 +771,84 @@ export async function executeFlowDeskStatusLiveV1(input) {
|
|
|
433
771
|
workflowId,
|
|
434
772
|
rootDir,
|
|
435
773
|
now: new Date(observedAt),
|
|
774
|
+
refreshCompletionUiCaches: false,
|
|
436
775
|
});
|
|
437
|
-
|
|
776
|
+
let reload = reloadFlowDeskSessionEvidenceV1({
|
|
438
777
|
workflowId,
|
|
439
778
|
rootDir,
|
|
440
779
|
});
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
780
|
+
let summary;
|
|
781
|
+
let projectionReload;
|
|
782
|
+
let projection;
|
|
783
|
+
const buildWorkflowSummary = () => {
|
|
784
|
+
summary = summarizeWorkflow(workflowId, reload, maxRecentEvidencePerClass);
|
|
785
|
+
projectionReload = pruneInconsistencySnapshotsNoLongerValid(pruneNonTerminalLifecycleSnapshotsNoLongerValid(reload));
|
|
786
|
+
projection = projectFlowDeskLaneStallV1({
|
|
787
|
+
workflowId,
|
|
788
|
+
reload: projectionReload,
|
|
789
|
+
observedAt,
|
|
790
|
+
...(input.config.laneHeartbeatLateThresholdMs === undefined
|
|
791
|
+
? {}
|
|
792
|
+
: { lateThresholdMs: input.config.laneHeartbeatLateThresholdMs }),
|
|
793
|
+
...(input.config.laneHeartbeatStallThresholdMs === undefined
|
|
794
|
+
? {}
|
|
795
|
+
: { stallThresholdMs: input.config.laneHeartbeatStallThresholdMs }),
|
|
796
|
+
});
|
|
797
|
+
summary.laneStallProjection = projection;
|
|
798
|
+
const projectedLifecycleStates = projection.entries
|
|
799
|
+
.map((entry) => entry.lifecycleState)
|
|
800
|
+
.filter((state) => typeof state === "string" && state.length > 0);
|
|
801
|
+
if (projectedLifecycleStates.length > 0) {
|
|
802
|
+
summary.latestLaneLifecycleStates = projectedLifecycleStates;
|
|
803
|
+
}
|
|
804
|
+
summary.worstLaneStallClassification = projection.worstClassification;
|
|
805
|
+
summary.stalledLaneCount = projection.totalStalledLanes;
|
|
806
|
+
summary.progressingLateLaneCount = projection.totalLateLanes;
|
|
807
|
+
summary.inconsistentFinalizingWithoutTerminalLaneCount =
|
|
808
|
+
projection.totalInconsistentLanes;
|
|
809
|
+
summary.laneProgressCards = buildLaneProgressCards(workflowId, projectionReload, projection);
|
|
810
|
+
summary.subtaskActivityRows = buildSubtaskActivityRows(summary.laneProgressCards);
|
|
811
|
+
summary.laneProgressAggregate = buildLaneProgressAggregate(summary.laneProgressCards, summary.latestWorkflowSynthesisTasksSummarized !== undefined);
|
|
812
|
+
};
|
|
813
|
+
const graceMs = clampFinalizingInconsistencyGraceMs(input.config.agentTaskFinalizingInconsistencyGraceMs);
|
|
814
|
+
const wroteInconsistency = materializeFinalizingWithoutTerminalInconsistencies({
|
|
444
815
|
workflowId,
|
|
445
|
-
|
|
816
|
+
rootDir,
|
|
817
|
+
reload,
|
|
446
818
|
observedAt,
|
|
447
|
-
|
|
448
|
-
? {}
|
|
449
|
-
: { lateThresholdMs: input.config.laneHeartbeatLateThresholdMs }),
|
|
450
|
-
...(input.config.laneHeartbeatStallThresholdMs === undefined
|
|
451
|
-
? {}
|
|
452
|
-
: { stallThresholdMs: input.config.laneHeartbeatStallThresholdMs }),
|
|
819
|
+
graceMs,
|
|
453
820
|
});
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
821
|
+
if (wroteInconsistency) {
|
|
822
|
+
reload = reloadFlowDeskSessionEvidenceV1({
|
|
823
|
+
workflowId,
|
|
824
|
+
rootDir,
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
buildWorkflowSummary();
|
|
459
828
|
workflows.push(summary);
|
|
460
829
|
}
|
|
461
830
|
const observed = workflows.some((summary) => Object.values(summary.evidenceCounts).some((count) => typeof count === "number" && count > 0));
|
|
462
831
|
const totalStalled = workflows.reduce((sum, summary) => sum + (summary.stalledLaneCount ?? 0), 0);
|
|
463
832
|
const totalLate = workflows.reduce((sum, summary) => sum + (summary.progressingLateLaneCount ?? 0), 0);
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
833
|
+
const totalInconsistent = workflows.reduce((sum, summary) => sum + (summary.inconsistentFinalizingWithoutTerminalLaneCount ?? 0), 0);
|
|
834
|
+
const worstLaneStallClassification = workflows.some((summary) => summary.worstLaneStallClassification ===
|
|
835
|
+
"inconsistent_finalizing_without_terminal")
|
|
836
|
+
? "inconsistent_finalizing_without_terminal"
|
|
837
|
+
: workflows.some((summary) => summary.worstLaneStallClassification === "stalled")
|
|
838
|
+
? "stalled"
|
|
839
|
+
: workflows.some((summary) => summary.worstLaneStallClassification === "progressing_late")
|
|
840
|
+
? "progressing_late"
|
|
841
|
+
: workflows.some((summary) => summary.worstLaneStallClassification === "progressing_normal")
|
|
842
|
+
? "progressing_normal"
|
|
843
|
+
: workflows.some((summary) => summary.worstLaneStallClassification === "terminal")
|
|
844
|
+
? "terminal"
|
|
845
|
+
: "unknown";
|
|
473
846
|
const summaryForUser = buildStatusLiveSummaryForUser({
|
|
474
847
|
workflows,
|
|
475
848
|
worstLaneStallClassification,
|
|
476
849
|
totalStalled,
|
|
477
850
|
totalLate,
|
|
851
|
+
totalInconsistent,
|
|
478
852
|
requestedWorkflowId,
|
|
479
853
|
});
|
|
480
854
|
return {
|
|
@@ -487,8 +861,10 @@ export async function executeFlowDeskStatusLiveV1(input) {
|
|
|
487
861
|
worstLaneStallClassification,
|
|
488
862
|
totalStalledLaneCount: totalStalled,
|
|
489
863
|
totalProgressingLateLaneCount: totalLate,
|
|
864
|
+
totalInconsistentFinalizingWithoutTerminalLaneCount: totalInconsistent,
|
|
490
865
|
summaryForUser,
|
|
491
866
|
safeNextActions: safeNextActions(),
|
|
867
|
+
authorityCapabilitySummary: authorityCapabilitySummary(),
|
|
492
868
|
authority: {
|
|
493
869
|
realOpenCodeDispatch: false,
|
|
494
870
|
providerCall: false,
|
|
@@ -508,29 +884,46 @@ function buildStatusLiveSummaryForUser(input) {
|
|
|
508
884
|
? `FlowDesk status: no durable evidence for workflow ${input.requestedWorkflowId}.`
|
|
509
885
|
: "FlowDesk status: no durable workflows found.";
|
|
510
886
|
}
|
|
511
|
-
const headline = input.
|
|
512
|
-
? `FlowDesk status: ${workflowsCount} workflow(s);
|
|
513
|
-
: input.
|
|
514
|
-
? `FlowDesk status: ${workflowsCount} workflow(s); ${input.totalLate} progressing-late
|
|
515
|
-
: input.
|
|
516
|
-
? `FlowDesk status: ${workflowsCount} workflow(s);
|
|
517
|
-
: input.worstLaneStallClassification === "
|
|
518
|
-
? `FlowDesk status: ${workflowsCount} workflow(s);
|
|
519
|
-
:
|
|
887
|
+
const headline = input.totalInconsistent > 0
|
|
888
|
+
? `FlowDesk status: ${workflowsCount} workflow(s); ${input.totalInconsistent} finalizing-without-terminal inconsistent lane(s) require manual recovery.`
|
|
889
|
+
: input.totalStalled > 0
|
|
890
|
+
? `FlowDesk status: ${workflowsCount} workflow(s); worst classification stalled (${input.totalStalled} stalled, ${input.totalLate} progressing-late).`
|
|
891
|
+
: input.totalLate > 0
|
|
892
|
+
? `FlowDesk status: ${workflowsCount} workflow(s); ${input.totalLate} progressing-late, no stalled lanes.`
|
|
893
|
+
: input.worstLaneStallClassification === "terminal"
|
|
894
|
+
? `FlowDesk status: ${workflowsCount} workflow(s); all observed lanes terminal/complete.`
|
|
895
|
+
: input.worstLaneStallClassification === "progressing_normal"
|
|
896
|
+
? `FlowDesk status: ${workflowsCount} workflow(s); active lanes progressing_normal.`
|
|
897
|
+
: `FlowDesk status: ${workflowsCount} workflow(s); worst classification ${input.worstLaneStallClassification}.`;
|
|
520
898
|
const perWorkflow = input.workflows.slice(0, 3).map((workflow) => {
|
|
521
899
|
const verdictLabels = workflow.latestReviewerVerdictLabels;
|
|
522
900
|
const verdictText = verdictLabels.length > 0
|
|
523
901
|
? `verdicts=${verdictLabels.join("/")}`
|
|
524
902
|
: "verdicts=(none)";
|
|
525
|
-
const
|
|
903
|
+
const projectedLaneStates = (workflow.laneProgressCards ?? [])
|
|
904
|
+
.slice(0, 3)
|
|
905
|
+
.map((lane) => `${lane.state ?? "unknown"}/${lane.classification}`);
|
|
906
|
+
const lifecycleStates = projectedLaneStates.length > 0 ? projectedLaneStates : workflow.latestLaneLifecycleStates;
|
|
526
907
|
const lifecycleText = lifecycleStates.length > 0
|
|
527
|
-
?
|
|
528
|
-
: "
|
|
908
|
+
? `${projectedLaneStates.length > 0 ? "lane_state" : "lifecycle"}=${lifecycleStates.slice(0, 3).join("/")}`
|
|
909
|
+
: "lane_state=(none)";
|
|
910
|
+
const synthesisPreview = workflow.latestWorkflowSynthesisSummaryPreview === undefined
|
|
911
|
+
? ""
|
|
912
|
+
: ` preview="${workflow.latestWorkflowSynthesisSummaryPreview.replace(/"/g, "'").slice(0, 96)}"`;
|
|
913
|
+
const synthesisText = workflow.latestWorkflowSynthesisTasksSummarized !== undefined
|
|
914
|
+
? ` synthesis=${workflow.latestWorkflowSynthesisTasksSummarized} tasks${workflow.latestWorkflowSynthesisConflictDetected ? " conflict=detected" : ""}${synthesisPreview}`
|
|
915
|
+
: "";
|
|
916
|
+
const autoNextText = workflow.laneProgressAggregate?.autoNextStepEligible === true ? " auto_next=ready" : "";
|
|
917
|
+
const nextActionText = workflow.laneProgressAggregate?.nextActionAvailable === true
|
|
918
|
+
? ` next_action=${workflow.laneProgressAggregate.nextActionKind ?? "available"}_ready`
|
|
919
|
+
: "";
|
|
529
920
|
const planText = workflow.latestWorkflowDispatchPlanRevisionId !== undefined
|
|
530
|
-
? `workflow_plan=${workflow.latestWorkflowDispatchPlanRevisionId} tasks=${workflow.latestWorkflowDispatchPlanTaskCount ?? "unknown"}`
|
|
921
|
+
? `workflow_plan=${workflow.latestWorkflowDispatchPlanRevisionId} tasks=${workflow.latestWorkflowDispatchPlanTaskCount ?? "unknown"}${synthesisText}${autoNextText}${nextActionText}`
|
|
531
922
|
: workflow.latestTaskGraphTaskCount !== undefined
|
|
532
|
-
? `planning_slice=${workflow.latestWorkflowAuthoringStatus ?? "unknown"} tasks=${workflow.latestTaskGraphTaskCount} assignments=${workflow.latestTaskAgentAssignmentCount ?? 0} model=${workflow.latestTaskModelSelectionStatus ?? "unknown"}${
|
|
533
|
-
:
|
|
923
|
+
? `planning_slice=${workflow.latestWorkflowAuthoringStatus ?? "unknown"} tasks=${workflow.latestTaskGraphTaskCount} assignments=${workflow.latestTaskAgentAssignmentCount ?? 0} model=${workflow.latestTaskModelSelectionStatus ?? "unknown"}${synthesisText}${autoNextText}${nextActionText}`
|
|
924
|
+
: synthesisText.length > 0
|
|
925
|
+
? `${synthesisText.trim()}${autoNextText}${nextActionText}`
|
|
926
|
+
: `workflow_plan=(none)${autoNextText}${nextActionText}`;
|
|
534
927
|
const classification = workflow.worstLaneStallClassification ?? "unknown";
|
|
535
928
|
const lanePreview = (workflow.laneProgressCards ?? [])
|
|
536
929
|
.slice(0, 2)
|
|
@@ -541,8 +934,20 @@ function buildStatusLiveSummaryForUser(input) {
|
|
|
541
934
|
return `${lane.laneId}:${lane.state ?? "unknown"}/${lane.classification}/last=${age}`;
|
|
542
935
|
})
|
|
543
936
|
.join(", ");
|
|
544
|
-
const
|
|
545
|
-
|
|
937
|
+
const subtaskPreview = (workflow.subtaskActivityRows ?? [])
|
|
938
|
+
.slice(0, 3)
|
|
939
|
+
.map((row) => {
|
|
940
|
+
const actions = (row.recoveryActionRefs ?? [])
|
|
941
|
+
.slice(0, 5)
|
|
942
|
+
.map((action) => action.replace("/flowdesk-", ""))
|
|
943
|
+
.join("|");
|
|
944
|
+
const compact = `${row.laneId}:${row.state ?? "unknown"}/${row.classification}${actions.length > 0 ? `[${actions}]` : ""}`;
|
|
945
|
+
return compact.length > 96 ? `${compact.slice(0, 95)}…` : compact;
|
|
946
|
+
})
|
|
947
|
+
.join("; ");
|
|
948
|
+
const laneText = lanePreview.length > 0 && subtaskPreview.length === 0 ? `, lanes=${lanePreview}` : "";
|
|
949
|
+
const subtaskText = subtaskPreview.length > 0 ? `, subtasks=${subtaskPreview}` : "";
|
|
950
|
+
return `- ${workflow.workflowId}: ${classification}, ${verdictText}, ${lifecycleText}, ${planText}${laneText}${subtaskText}`;
|
|
546
951
|
});
|
|
547
952
|
return [headline, ...perWorkflow].join("\n");
|
|
548
953
|
}
|