@flowdesk/opencode-plugin 0.1.13 → 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 +23 -0
- package/dist/agent-task-runner.d.ts.map +1 -1
- package/dist/agent-task-runner.js +410 -81
- 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/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.map +1 -1
- package/dist/managed-dispatch-adapter.js +7 -3
- 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 +10 -0
- package/dist/provider-usage-live-tool.d.ts.map +1 -1
- package/dist/provider-usage-live-tool.js +145 -18
- package/dist/provider-usage-live-tool.js.map +1 -1
- package/dist/server.d.ts +35 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +447 -19
- package/dist/server.js.map +1 -1
- package/dist/stall-recovery.d.ts +33 -0
- package/dist/stall-recovery.d.ts.map +1 -1
- package/dist/stall-recovery.js +459 -2
- 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 +448 -44
- 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 +14 -0
- package/dist/tui-usage-snapshot.d.ts.map +1 -1
- package/dist/tui-usage-snapshot.js +189 -8
- package/dist/tui-usage-snapshot.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +72 -41
- 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 +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-tool.d.ts.map +1 -1
- package/dist/workflow-dispatch-tool.js +32 -2
- 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
|
}
|
|
@@ -434,47 +772,82 @@ export async function executeFlowDeskStatusLiveV1(input) {
|
|
|
434
772
|
rootDir,
|
|
435
773
|
now: new Date(observedAt),
|
|
436
774
|
});
|
|
437
|
-
|
|
775
|
+
let reload = reloadFlowDeskSessionEvidenceV1({
|
|
438
776
|
workflowId,
|
|
439
777
|
rootDir,
|
|
440
778
|
});
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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({
|
|
444
814
|
workflowId,
|
|
445
|
-
|
|
815
|
+
rootDir,
|
|
816
|
+
reload,
|
|
446
817
|
observedAt,
|
|
447
|
-
|
|
448
|
-
? {}
|
|
449
|
-
: { lateThresholdMs: input.config.laneHeartbeatLateThresholdMs }),
|
|
450
|
-
...(input.config.laneHeartbeatStallThresholdMs === undefined
|
|
451
|
-
? {}
|
|
452
|
-
: { stallThresholdMs: input.config.laneHeartbeatStallThresholdMs }),
|
|
818
|
+
graceMs,
|
|
453
819
|
});
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
820
|
+
if (wroteInconsistency) {
|
|
821
|
+
reload = reloadFlowDeskSessionEvidenceV1({
|
|
822
|
+
workflowId,
|
|
823
|
+
rootDir,
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
buildWorkflowSummary();
|
|
459
827
|
workflows.push(summary);
|
|
460
828
|
}
|
|
461
829
|
const observed = workflows.some((summary) => Object.values(summary.evidenceCounts).some((count) => typeof count === "number" && count > 0));
|
|
462
830
|
const totalStalled = workflows.reduce((sum, summary) => sum + (summary.stalledLaneCount ?? 0), 0);
|
|
463
831
|
const totalLate = workflows.reduce((sum, summary) => sum + (summary.progressingLateLaneCount ?? 0), 0);
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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";
|
|
473
845
|
const summaryForUser = buildStatusLiveSummaryForUser({
|
|
474
846
|
workflows,
|
|
475
847
|
worstLaneStallClassification,
|
|
476
848
|
totalStalled,
|
|
477
849
|
totalLate,
|
|
850
|
+
totalInconsistent,
|
|
478
851
|
requestedWorkflowId,
|
|
479
852
|
});
|
|
480
853
|
return {
|
|
@@ -487,8 +860,10 @@ export async function executeFlowDeskStatusLiveV1(input) {
|
|
|
487
860
|
worstLaneStallClassification,
|
|
488
861
|
totalStalledLaneCount: totalStalled,
|
|
489
862
|
totalProgressingLateLaneCount: totalLate,
|
|
863
|
+
totalInconsistentFinalizingWithoutTerminalLaneCount: totalInconsistent,
|
|
490
864
|
summaryForUser,
|
|
491
865
|
safeNextActions: safeNextActions(),
|
|
866
|
+
authorityCapabilitySummary: authorityCapabilitySummary(),
|
|
492
867
|
authority: {
|
|
493
868
|
realOpenCodeDispatch: false,
|
|
494
869
|
providerCall: false,
|
|
@@ -508,29 +883,46 @@ function buildStatusLiveSummaryForUser(input) {
|
|
|
508
883
|
? `FlowDesk status: no durable evidence for workflow ${input.requestedWorkflowId}.`
|
|
509
884
|
: "FlowDesk status: no durable workflows found.";
|
|
510
885
|
}
|
|
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
|
-
:
|
|
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}.`;
|
|
520
897
|
const perWorkflow = input.workflows.slice(0, 3).map((workflow) => {
|
|
521
898
|
const verdictLabels = workflow.latestReviewerVerdictLabels;
|
|
522
899
|
const verdictText = verdictLabels.length > 0
|
|
523
900
|
? `verdicts=${verdictLabels.join("/")}`
|
|
524
901
|
: "verdicts=(none)";
|
|
525
|
-
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;
|
|
526
906
|
const lifecycleText = lifecycleStates.length > 0
|
|
527
|
-
?
|
|
528
|
-
: "
|
|
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
|
+
: "";
|
|
529
919
|
const planText = workflow.latestWorkflowDispatchPlanRevisionId !== undefined
|
|
530
|
-
? `workflow_plan=${workflow.latestWorkflowDispatchPlanRevisionId} tasks=${workflow.latestWorkflowDispatchPlanTaskCount ?? "unknown"}`
|
|
920
|
+
? `workflow_plan=${workflow.latestWorkflowDispatchPlanRevisionId} tasks=${workflow.latestWorkflowDispatchPlanTaskCount ?? "unknown"}${synthesisText}${autoNextText}${nextActionText}`
|
|
531
921
|
: workflow.latestTaskGraphTaskCount !== undefined
|
|
532
|
-
? `planning_slice=${workflow.latestWorkflowAuthoringStatus ?? "unknown"} tasks=${workflow.latestTaskGraphTaskCount} assignments=${workflow.latestTaskAgentAssignmentCount ?? 0} model=${workflow.latestTaskModelSelectionStatus ?? "unknown"}${
|
|
533
|
-
:
|
|
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}`;
|
|
534
926
|
const classification = workflow.worstLaneStallClassification ?? "unknown";
|
|
535
927
|
const lanePreview = (workflow.laneProgressCards ?? [])
|
|
536
928
|
.slice(0, 2)
|
|
@@ -541,8 +933,20 @@ function buildStatusLiveSummaryForUser(input) {
|
|
|
541
933
|
return `${lane.laneId}:${lane.state ?? "unknown"}/${lane.classification}/last=${age}`;
|
|
542
934
|
})
|
|
543
935
|
.join(", ");
|
|
544
|
-
const
|
|
545
|
-
|
|
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}`;
|
|
546
950
|
});
|
|
547
951
|
return [headline, ...perWorkflow].join("\n");
|
|
548
952
|
}
|