@flowdesk/opencode-plugin 0.1.14 → 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.
Files changed (55) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-task-output.d.ts +12 -0
  3. package/dist/agent-task-output.d.ts.map +1 -1
  4. package/dist/agent-task-output.js +110 -4
  5. package/dist/agent-task-output.js.map +1 -1
  6. package/dist/agent-task-runner.d.ts +12 -1
  7. package/dist/agent-task-runner.d.ts.map +1 -1
  8. package/dist/agent-task-runner.js +237 -16
  9. package/dist/agent-task-runner.js.map +1 -1
  10. package/dist/completion-ui-cache.d.ts.map +1 -1
  11. package/dist/completion-ui-cache.js +159 -29
  12. package/dist/completion-ui-cache.js.map +1 -1
  13. package/dist/event-hook-observer.d.ts.map +1 -1
  14. package/dist/event-hook-observer.js +66 -2
  15. package/dist/event-hook-observer.js.map +1 -1
  16. package/dist/managed-dispatch-adapter.d.ts +62 -0
  17. package/dist/managed-dispatch-adapter.d.ts.map +1 -1
  18. package/dist/managed-dispatch-adapter.js +465 -1
  19. package/dist/managed-dispatch-adapter.js.map +1 -1
  20. package/dist/model-selection-engine.d.ts +15 -2
  21. package/dist/model-selection-engine.d.ts.map +1 -1
  22. package/dist/model-selection-engine.js +109 -42
  23. package/dist/model-selection-engine.js.map +1 -1
  24. package/dist/provider-usage-live-tool.d.ts.map +1 -1
  25. package/dist/provider-usage-live-tool.js +135 -33
  26. package/dist/provider-usage-live-tool.js.map +1 -1
  27. package/dist/server.d.ts +1 -0
  28. package/dist/server.d.ts.map +1 -1
  29. package/dist/server.js +52 -3
  30. package/dist/server.js.map +1 -1
  31. package/dist/stall-recovery.d.ts +4 -3
  32. package/dist/stall-recovery.d.ts.map +1 -1
  33. package/dist/stall-recovery.js +245 -25
  34. package/dist/stall-recovery.js.map +1 -1
  35. package/dist/status-live-tool.d.ts.map +1 -1
  36. package/dist/status-live-tool.js +1 -0
  37. package/dist/status-live-tool.js.map +1 -1
  38. package/dist/tui-subtask-activity.d.ts +4 -0
  39. package/dist/tui-subtask-activity.d.ts.map +1 -1
  40. package/dist/tui-subtask-activity.js +29 -24
  41. package/dist/tui-subtask-activity.js.map +1 -1
  42. package/dist/tui-usage-snapshot.d.ts.map +1 -1
  43. package/dist/tui-usage-snapshot.js +92 -6
  44. package/dist/tui-usage-snapshot.js.map +1 -1
  45. package/dist/tui.d.ts.map +1 -1
  46. package/dist/tui.js +43 -16
  47. package/dist/tui.js.map +1 -1
  48. package/dist/workflow-assign-tool.d.ts.map +1 -1
  49. package/dist/workflow-assign-tool.js +21 -3
  50. package/dist/workflow-assign-tool.js.map +1 -1
  51. package/dist/workflow-dispatch-tool.d.ts +12 -0
  52. package/dist/workflow-dispatch-tool.d.ts.map +1 -1
  53. package/dist/workflow-dispatch-tool.js +24 -26
  54. package/dist/workflow-dispatch-tool.js.map +1 -1
  55. package/package.json +2 -2
package/README.md CHANGED
@@ -6,7 +6,7 @@ registration plus opt-in description-driven natural-language tools.
6
6
  ## Install
7
7
 
8
8
  ```bash
9
- npm install @flowdesk/opencode-plugin@^0.1.14
9
+ npm install @flowdesk/opencode-plugin@^0.1.15
10
10
  ```
11
11
 
12
12
  ## Configure
@@ -10,7 +10,19 @@ export interface FlowDeskAgentTaskOutputObservationV1 {
10
10
  messageCount: number;
11
11
  outputKind: FlowDeskAgentTaskOutputKindV1;
12
12
  usableForSynthesis: boolean;
13
+ /**
14
+ * ADVISORY ONLY. The captured text superficially resembles a refusal or an
15
+ * error message. This is a hint for the coordinator's substance judgement; it
16
+ * NEVER causes the capture layer to drop or fail the result.
17
+ */
18
+ looksLikeRefusalOrError: boolean;
13
19
  }
20
+ /**
21
+ * ADVISORY heuristic for the coordinator: does this text look like a refusal or
22
+ * a bare error message rather than a substantive answer? Capture never gates on
23
+ * this; the main coordinator uses it to decide whether to re-select and retry.
24
+ */
25
+ export declare function flowDeskTextLooksLikeRefusalOrErrorV1(text: string | undefined): boolean;
14
26
  export declare function flowDeskAgentTaskMessageItems(value: unknown): unknown[];
15
27
  export declare function classifyFlowDeskAgentTaskOutputKindV1(text: string | undefined): FlowDeskAgentTaskOutputKindV1;
16
28
  export declare function observeFlowDeskAgentTaskOutputV1(response: unknown): FlowDeskAgentTaskOutputObservationV1;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-task-output.d.ts","sourceRoot":"","sources":["../src/agent-task-output.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mCAAmC,GAAG,OAAO,GAAG,SAAS,CAAC;AACtE,MAAM,MAAM,6BAA6B,GACtC,cAAc,GACd,kBAAkB,GAClB,eAAe,GACf,iBAAiB,GACjB,OAAO,CAAC;AAEX,MAAM,WAAW,oCAAoC;IACpD,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,6BAA6B,CAAC;IAC1C,kBAAkB,EAAE,OAAO,CAAC;CAC5B;AAUD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,CAMvE;AA4BD,wBAAgB,qCAAqC,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,6BAA6B,CAqB7G;AAED,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,OAAO,GAAG,oCAAoC,CAoDxG"}
1
+ {"version":3,"file":"agent-task-output.d.ts","sourceRoot":"","sources":["../src/agent-task-output.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mCAAmC,GAAG,OAAO,GAAG,SAAS,CAAC;AACtE,MAAM,MAAM,6BAA6B,GACtC,cAAc,GACd,kBAAkB,GAClB,eAAe,GACf,iBAAiB,GACjB,OAAO,CAAC;AAEX,MAAM,WAAW,oCAAoC;IACpD,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,6BAA6B,CAAC;IAC1C,kBAAkB,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,uBAAuB,EAAE,OAAO,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAgB,qCAAqC,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CA6BvF;AAUD,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,CAmBvE;AA+CD,wBAAgB,qCAAqC,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,6BAA6B,CAqB7G;AAED,wBAAgB,gCAAgC,CAAC,QAAQ,EAAE,OAAO,GAAG,oCAAoC,CA4FxG"}
@@ -1,3 +1,39 @@
1
+ /**
2
+ * ADVISORY heuristic for the coordinator: does this text look like a refusal or
3
+ * a bare error message rather than a substantive answer? Capture never gates on
4
+ * this; the main coordinator uses it to decide whether to re-select and retry.
5
+ */
6
+ export function flowDeskTextLooksLikeRefusalOrErrorV1(text) {
7
+ const normalized = text?.trim().toLowerCase() ?? "";
8
+ if (normalized.length === 0)
9
+ return false;
10
+ const patterns = [
11
+ "i cannot",
12
+ "i can't",
13
+ "i can not",
14
+ "i'm unable",
15
+ "i am unable",
16
+ "i'm sorry, but i can",
17
+ "i am sorry, but i can",
18
+ "i won't be able",
19
+ "i will not be able",
20
+ "as an ai",
21
+ "i'm not able to",
22
+ "i am not able to",
23
+ "error:",
24
+ "exception:",
25
+ "traceback (most recent call last)",
26
+ "rate limit",
27
+ "rate-limited",
28
+ "request failed",
29
+ "failed to",
30
+ "unable to complete",
31
+ ];
32
+ // Only treat as refusal/error when the text is short-ish and dominated by the
33
+ // pattern, to avoid false positives on long answers that merely mention them.
34
+ const head = normalized.slice(0, 240);
35
+ return patterns.some(pattern => head.includes(pattern));
36
+ }
1
37
  function isRecord(value) {
2
38
  return typeof value === "object" && value !== null && !Array.isArray(value);
3
39
  }
@@ -12,7 +48,22 @@ export function flowDeskAgentTaskMessageItems(value) {
12
48
  return [];
13
49
  if (Array.isArray(data.items))
14
50
  return data.items;
15
- return Array.isArray(data.messages) ? data.messages : [];
51
+ if (Array.isArray(data.messages))
52
+ return data.messages;
53
+ // Gemini candidates wrapper: { candidates: [{ content: { role, parts }, finishReason }] }
54
+ if (Array.isArray(data.candidates)) {
55
+ const msgs = [];
56
+ for (const candidate of data.candidates) {
57
+ if (!isRecord(candidate))
58
+ continue;
59
+ const content = isRecord(candidate.content) ? candidate.content : candidate;
60
+ const finishReason = typeof candidate.finishReason === "string" ? candidate.finishReason
61
+ : typeof candidate.finish_reason === "string" ? candidate.finish_reason : undefined;
62
+ msgs.push({ ...content, _finishReason: finishReason });
63
+ }
64
+ return msgs;
65
+ }
66
+ return [];
16
67
  }
17
68
  function partText(part) {
18
69
  if (typeof part.text === "string")
@@ -25,6 +76,24 @@ function isTerminalPart(part) {
25
76
  return ((type === "step-finish" || type === "step_finish" || type === "finish") &&
26
77
  (reason === "stop" || reason === "complete" || reason === "completed"));
27
78
  }
79
+ /** Check message-level terminal signals (OpenAI finish_reason, Gemini finishReason). */
80
+ function isTerminalMessage(msg) {
81
+ // OpenAI: finish_reason at message or choice level
82
+ const fr = typeof msg.finish_reason === "string" ? msg.finish_reason
83
+ : typeof msg.finishReason === "string" ? msg.finishReason
84
+ : typeof msg._finishReason === "string" ? msg._finishReason : undefined;
85
+ if (fr !== undefined) {
86
+ const normalized = fr.toLowerCase();
87
+ if (normalized === "stop" || normalized === "end_turn" || normalized === "complete" || normalized === "completed") {
88
+ return { terminal: true, reason: fr };
89
+ }
90
+ }
91
+ // OpenAI status field
92
+ if (msg.status === "completed" || msg.status === "complete") {
93
+ return { terminal: true, reason: String(msg.status) };
94
+ }
95
+ return { terminal: false, reason: undefined };
96
+ }
28
97
  function terminalReason(part) {
29
98
  return typeof part.reason === "string" ? part.reason : undefined;
30
99
  }
@@ -73,11 +142,43 @@ export function observeFlowDeskAgentTaskOutputV1(response) {
73
142
  const msgRec = isRecord(msg) ? msg : undefined;
74
143
  const info = isRecord(msgRec?.info) ? msgRec.info : msgRec;
75
144
  const role = info?.role;
76
- const parts = Array.isArray(msgRec?.parts)
145
+ // Accept "assistant" and "model" (Gemini uses "model" for assistant role)
146
+ const isAssistantRole = role === "assistant" || role === "model";
147
+ // Check message-level terminal signals (OpenAI/Gemini)
148
+ if (msgRec !== undefined) {
149
+ const msgTerminal = isTerminalMessage(msgRec);
150
+ if (!msgTerminal.terminal && info !== undefined && info !== msgRec) {
151
+ const infoTerminal = isTerminalMessage(info);
152
+ if (infoTerminal.terminal) {
153
+ terminalObserved = true;
154
+ observedTerminalReason = infoTerminal.reason;
155
+ }
156
+ }
157
+ else if (msgTerminal.terminal) {
158
+ terminalObserved = true;
159
+ observedTerminalReason = msgTerminal.reason;
160
+ }
161
+ }
162
+ // Collect parts from msg.parts, msg.info.parts, or msg.content (when content is array of parts)
163
+ let parts = Array.isArray(msgRec?.parts)
77
164
  ? msgRec.parts
78
165
  : Array.isArray(info?.parts)
79
166
  ? info.parts
80
167
  : [];
168
+ // OpenAI multi-part content: content is array of part objects
169
+ if (parts.length === 0 && msgRec !== undefined && Array.isArray(msgRec.content)) {
170
+ parts = msgRec.content;
171
+ }
172
+ // Message-level content string (OpenAI Chat Completions: { role: "assistant", content: "..." })
173
+ if (parts.length === 0 && isAssistantRole && msgRec !== undefined && typeof msgRec.content === "string" && msgRec.content.trim().length > 0) {
174
+ latestText = msgRec.content;
175
+ textPartCount++;
176
+ }
177
+ // Also check info-level content string
178
+ if (parts.length === 0 && isAssistantRole && info !== undefined && info !== msgRec && typeof info.content === "string" && info.content.trim().length > 0) {
179
+ latestText = info.content;
180
+ textPartCount++;
181
+ }
81
182
  for (const rawPart of parts) {
82
183
  const part = isRecord(rawPart) ? rawPart : undefined;
83
184
  if (part === undefined)
@@ -92,9 +193,10 @@ export function observeFlowDeskAgentTaskOutputV1(response) {
92
193
  reasoningPartCount++;
93
194
  continue;
94
195
  }
95
- if (role !== "assistant")
196
+ if (!isAssistantRole)
96
197
  continue;
97
- if (part.type !== undefined && part.type !== "text")
198
+ // Accept text, output_text (OpenAI Responses API), or undefined type
199
+ if (part.type !== undefined && part.type !== "text" && part.type !== "output_text")
98
200
  continue;
99
201
  const text = partText(part);
100
202
  if (typeof text === "string" && text.trim().length > 0) {
@@ -113,7 +215,11 @@ export function observeFlowDeskAgentTaskOutputV1(response) {
113
215
  reasoningPartCount,
114
216
  messageCount: items.length,
115
217
  outputKind,
218
+ // Capture-side usability = "there is some text to keep". This is advisory
219
+ // transport metadata, NOT a substance/quality judgement (that is the
220
+ // coordinator's job). Only genuinely empty/tool-only captures are unusable.
116
221
  usableForSynthesis: outputKind !== "empty" && outputKind !== "tool_trace_only",
222
+ looksLikeRefusalOrError: flowDeskTextLooksLikeRefusalOrErrorV1(latestText),
117
223
  };
118
224
  }
119
225
  //# sourceMappingURL=agent-task-output.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-task-output.js","sourceRoot":"","sources":["../src/agent-task-output.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-task-output.js","sourceRoot":"","sources":["../src/agent-task-output.ts"],"names":[],"mappings":"AA0BA;;;;GAIG;AACH,MAAM,UAAU,qCAAqC,CAAC,IAAwB;IAC7E,MAAM,UAAU,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;IACpD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,QAAQ,GAAG;QAChB,UAAU;QACV,SAAS;QACT,WAAW;QACX,YAAY;QACZ,aAAa;QACb,sBAAsB;QACtB,uBAAuB;QACvB,iBAAiB;QACjB,oBAAoB;QACpB,UAAU;QACV,iBAAiB;QACjB,kBAAkB;QAClB,QAAQ;QACR,YAAY;QACZ,mCAAmC;QACnC,YAAY;QACZ,cAAc;QACd,gBAAgB;QAChB,WAAW;QACX,oBAAoB;KACpB,CAAC;IACF,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACtC,OAAO,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC/B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IACnC,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,KAAc;IAC3D,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC;IACjD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvD,0FAA0F;IAC1F,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,GAAc,EAAE,CAAC;QAC3B,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACzC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAAE,SAAS;YACnC,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;YAC5E,MAAM,YAAY,GAAG,OAAO,SAAS,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,YAAY;gBACvF,CAAC,CAAC,OAAO,SAAS,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC;YACrF,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,EAAE,CAAC;AACX,CAAC;AAED,SAAS,QAAQ,CAAC,IAA6B;IAC9C,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IACpD,OAAO,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACpE,CAAC;AAED,SAAS,cAAc,CAAC,IAA6B;IACpD,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5D,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAClE,OAAO,CACN,CAAC,IAAI,KAAK,aAAa,IAAI,IAAI,KAAK,aAAa,IAAI,IAAI,KAAK,QAAQ,CAAC;QACvE,CAAC,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,UAAU,IAAI,MAAM,KAAK,WAAW,CAAC,CACtE,CAAC;AACH,CAAC;AAED,wFAAwF;AACxF,SAAS,iBAAiB,CAAC,GAA4B;IACtD,mDAAmD;IACnD,MAAM,EAAE,GAAG,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa;QACnE,CAAC,CAAC,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY;YACzD,CAAC,CAAC,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC;IACzE,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,UAAU,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;QACpC,IAAI,UAAU,KAAK,MAAM,IAAI,UAAU,KAAK,UAAU,IAAI,UAAU,KAAK,UAAU,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;YACnH,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACvC,CAAC;IACF,CAAC;IACD,sBAAsB;IACtB,IAAI,GAAG,CAAC,MAAM,KAAK,WAAW,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAC7D,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;IACvD,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC/C,CAAC;AAED,SAAS,cAAc,CAAC,IAA6B;IACpD,OAAO,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAClE,CAAC;AAED,SAAS,kBAAkB,CAAC,IAA6B;IACxD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACrD,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5D,OAAO,KAAK,EAAE,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC;IACjE,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,MAAM,UAAU,qCAAqC,CAAC,IAAwB;IAC7E,MAAM,UAAU,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;IACpD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IAC5C,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,cAAc,CAAC;IAChF,MAAM,gBAAgB,GAAG;QACxB,WAAW;QACX,UAAU;QACV,OAAO;QACP,SAAS;QACT,QAAQ;QACR,UAAU;QACV,UAAU;QACV,eAAe;QACf,YAAY;QACZ,aAAa;QACb,cAAc;KACd,CAAC;IACF,IAAI,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAAE,OAAO,eAAe,CAAC;IAC7F,MAAM,gBAAgB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IACxG,IAAI,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAAE,OAAO,kBAAkB,CAAC;IAChG,OAAO,cAAc,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,gCAAgC,CAAC,QAAiB;IACjE,MAAM,KAAK,GAAG,6BAA6B,CAAC,QAAQ,CAAC,CAAC;IACtD,IAAI,UAA8B,CAAC;IACnC,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,IAAI,sBAA0C,CAAC;IAC/C,IAAI,cAAc,GAAG,KAAK,CAAC;IAC3B,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,kBAAkB,GAAG,CAAC,CAAC;IAE3B,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/C,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;QAC3D,MAAM,IAAI,GAAG,IAAI,EAAE,IAAI,CAAC;QACxB,0EAA0E;QAC1E,MAAM,eAAe,GAAG,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,OAAO,CAAC;QAEjE,uDAAuD;QACvD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,WAAW,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAC9C,IAAI,CAAC,WAAW,CAAC,QAAQ,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;gBACpE,MAAM,YAAY,GAAG,iBAAiB,CAAC,IAA+B,CAAC,CAAC;gBACxE,IAAI,YAAY,CAAC,QAAQ,EAAE,CAAC;oBAC3B,gBAAgB,GAAG,IAAI,CAAC;oBACxB,sBAAsB,GAAG,YAAY,CAAC,MAAM,CAAC;gBAC9C,CAAC;YACF,CAAC;iBAAM,IAAI,WAAW,CAAC,QAAQ,EAAE,CAAC;gBACjC,gBAAgB,GAAG,IAAI,CAAC;gBACxB,sBAAsB,GAAG,WAAW,CAAC,MAAM,CAAC;YAC7C,CAAC;QACF,CAAC;QAED,gGAAgG;QAChG,IAAI,KAAK,GAAc,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC;YAClD,CAAC,CAAC,MAAO,CAAC,KAAK;YACf,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;gBAC3B,CAAC,CAAC,IAAK,CAAC,KAAK;gBACb,CAAC,CAAC,EAAE,CAAC;QACP,8DAA8D;QAC9D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACjF,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC;QACxB,CAAC;QAED,gGAAgG;QAChG,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,eAAe,IAAI,MAAM,KAAK,SAAS,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7I,UAAU,GAAG,MAAM,CAAC,OAAiB,CAAC;YACtC,aAAa,EAAE,CAAC;QACjB,CAAC;QACD,uCAAuC;QACvC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,eAAe,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,MAAM,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAK,IAAI,CAAC,OAAkB,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtK,UAAU,GAAG,IAAI,CAAC,OAAiB,CAAC;YACpC,aAAa,EAAE,CAAC;QACjB,CAAC;QAED,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;YACrD,IAAI,IAAI,KAAK,SAAS;gBAAE,SAAS;YACjC,IAAI,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,gBAAgB,GAAG,IAAI,CAAC;gBACxB,sBAAsB,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;YAC/C,CAAC;YACD,IAAI,kBAAkB,CAAC,IAAI,CAAC;gBAAE,cAAc,GAAG,IAAI,CAAC;YACpD,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBAC/B,kBAAkB,EAAE,CAAC;gBACrB,SAAS;YACV,CAAC;YACD,IAAI,CAAC,eAAe;gBAAE,SAAS;YAC/B,qEAAqE;YACrE,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa;gBAAE,SAAS;YAC7F,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC5B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxD,UAAU,GAAG,IAAI,CAAC;gBAClB,aAAa,EAAE,CAAC;YACjB,CAAC;QACF,CAAC;IACF,CAAC;IAED,MAAM,UAAU,GAAG,qCAAqC,CAAC,UAAU,CAAC,CAAC;IACrE,OAAO;QACN,UAAU;QACV,gBAAgB;QAChB,cAAc,EAAE,sBAAsB;QACtC,cAAc;QACd,aAAa;QACb,kBAAkB;QAClB,YAAY,EAAE,KAAK,CAAC,MAAM;QAC1B,UAAU;QACV,0EAA0E;QAC1E,qEAAqE;QACrE,4EAA4E;QAC5E,kBAAkB,EAAE,UAAU,KAAK,OAAO,IAAI,UAAU,KAAK,iBAAiB;QAC9E,uBAAuB,EAAE,qCAAqC,CAAC,UAAU,CAAC;KAC1E,CAAC;AACH,CAAC"}
@@ -1,4 +1,8 @@
1
1
  import { type FlowDeskManagedDispatchBetaOpenCodeClientV1 } from "./managed-dispatch-adapter.js";
2
+ export interface FlowDeskAgentTaskFallbackBindingV1 {
3
+ agentRef: string;
4
+ providerQualifiedModelId: string;
5
+ }
2
6
  export interface FlowDeskAgentTaskInputV1 {
3
7
  workflowId: string;
4
8
  taskId: string;
@@ -17,12 +21,19 @@ export interface FlowDeskAgentTaskInputV1 {
17
21
  * The coordinator polls flowdesk_status_live to detect terminal state.
18
22
  */
19
23
  asyncMode?: boolean;
24
+ /**
25
+ * When provided and the primary attempt fails with no_response,
26
+ * automatically retry once with this fallback agent/model binding.
27
+ */
28
+ fallbackBinding?: FlowDeskAgentTaskFallbackBindingV1;
20
29
  /** Override quiet period before nudge — for testing only */
21
30
  _nudgeQuietPeriodMs?: number;
22
31
  /** Override messages poll timeout — for testing only (default 3000ms in prod) */
23
32
  _messagesTimeoutMs?: number;
24
- /** Override launch timeout — for testing only (default 300000ms = 5min in prod) */
33
+ /** Override launch timeout — for testing only (default 30000ms in prod) */
25
34
  _launchTimeoutMs?: number;
35
+ /** Internal: true when this is already a fallback retry (prevents infinite retry) */
36
+ _isFallbackRetry?: boolean;
26
37
  }
27
38
  export type FlowDeskAgentTaskResultV1 = {
28
39
  status: "task_completed";
@@ -1 +1 @@
1
- {"version":3,"file":"agent-task-runner.d.ts","sourceRoot":"","sources":["../src/agent-task-runner.ts"],"names":[],"mappings":"AAWA,OAAO,EACN,KAAK,2CAA2C,EAGhD,MAAM,+BAA+B,CAAC;AASvC,MAAM,WAAW,wBAAwB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,wBAAwB,EAAE,MAAM,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,2CAA2C,CAAC;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4DAA4D;IAC5D,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iFAAiF;IACjF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,yBAAyB,GAClC;IAAE,MAAM,EAAE,gBAAgB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,MAAM,CAAA;CAAE,GAC9F;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GACnE;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9F,+DAA+D;AAC/D,eAAO,MAAM,uCAAuC,EAAG,sCAA+C,CAAC;AAEvG,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAMrH;AAqVD,wBAAsB,0BAA0B,CAC/C,KAAK,EAAE,wBAAwB,GAC7B,OAAO,CAAC,yBAAyB,CAAC,CA8bpC"}
1
+ {"version":3,"file":"agent-task-runner.d.ts","sourceRoot":"","sources":["../src/agent-task-runner.ts"],"names":[],"mappings":"AAcA,OAAO,EACN,KAAK,2CAA2C,EAGhD,MAAM,+BAA+B,CAAC;AASvC,MAAM,WAAW,kCAAkC;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,wBAAwB,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,wBAAwB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,wBAAwB,EAAE,MAAM,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,2CAA2C,CAAC;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,eAAe,CAAC,EAAE,kCAAkC,CAAC;IACrD,4DAA4D;IAC5D,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iFAAiF;IACjF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,MAAM,yBAAyB,GAClC;IAAE,MAAM,EAAE,gBAAgB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,MAAM,CAAA;CAAE,GAC9F;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GACnE;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9F,+DAA+D;AAC/D,eAAO,MAAM,uCAAuC,EAAG,sCAA+C,CAAC;AAsBvG,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAMrH;AA+bD,wBAAsB,0BAA0B,CAC/C,KAAK,EAAE,wBAAwB,GAC7B,OAAO,CAAC,yBAAyB,CAAC,CAmjBpC"}
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- import { applyFlowDeskSessionEvidenceWriteIntentsV1, prepareFlowDeskSessionEvidenceWriteIntentV1, } from "@flowdesk/core";
2
+ import { applyFlowDeskSessionEvidenceWriteIntentsV1, prepareFlowDeskSessionEvidenceWriteIntentV1, reloadFlowDeskSessionEvidenceV1, validateTopTierReviewVerdictV1, } from "@flowdesk/core";
3
3
  import { launchFlowDeskInjectedSdkRuntimeLaneFromPlanV1, materializeFlowDeskRuntimeLaneLaunchLifecycleEvidenceV1, } from "./managed-dispatch-adapter.js";
4
4
  import { observeFlowDeskAgentTaskOutputV1 } from "./agent-task-output.js";
5
5
  import { refreshFlowDeskCompletionUiCachesV1 } from "./completion-ui-cache.js";
@@ -9,6 +9,10 @@ const AGENT_TASK_CONTEXT_MAX_PROMPT_TEXT = 32_768;
9
9
  const INVALID_PARENT_SESSION_REF = "ses-invalid-parent-session-binding";
10
10
  /** Schema version for async child session tracking evidence */
11
11
  export const AGENT_TASK_CHILD_SESSION_SCHEMA_VERSION = "flowdesk.agent_task_child_session.v1";
12
+ /** Stable-idle finalization thresholds for non-terminal captured text. */
13
+ const STABLE_IDLE_MIN_CYCLES = 3;
14
+ const STABLE_IDLE_MIN_MS = 12_000;
15
+ const STABLE_IDLE_MIN_LEN = 16;
12
16
  export function sanitizeFlowDeskTaskResultTextV1(text) {
13
17
  return {
14
18
  text: text.length > TASK_RESULT_MAX_TEXT ? text.slice(0, TASK_RESULT_MAX_TEXT) : text,
@@ -77,7 +81,7 @@ async function extractAssistantTextFromResponse(client, childSessionId, opts) {
77
81
  const messages = client.session.messages;
78
82
  if (messages === undefined)
79
83
  return undefined;
80
- const quietPeriodMs = opts?.quietPeriodMs ?? 30_000;
84
+ const quietPeriodMs = opts?.quietPeriodMs ?? 10_000;
81
85
  const maxNudges = opts?.maxNudges ?? 2;
82
86
  const MESSAGES_TIMEOUT_MS = opts?.messagesTimeoutMs ?? 3_000; // per-call cap — handles both snapshot and long-poll
83
87
  const method = messages;
@@ -139,6 +143,12 @@ async function extractAssistantTextFromResponse(client, childSessionId, opts) {
139
143
  let lastHeartbeatMs = startMs;
140
144
  let nudgeCount = 0;
141
145
  let latestCandidate;
146
+ // Stable-idle tracking: capture non-terminal text once it has settled, so a
147
+ // good answer is not lost just because the SDK shape never surfaced an
148
+ // explicit terminal/finish marker.
149
+ let stableText;
150
+ let stableCount = 0;
151
+ let firstStableMs = 0;
142
152
  try {
143
153
  while (true) {
144
154
  const response = await callMessages();
@@ -160,10 +170,36 @@ async function extractAssistantTextFromResponse(client, childSessionId, opts) {
160
170
  lastHeartbeatMs = nowMs;
161
171
  }
162
172
  const observed = observe(response);
163
- if (observed?.latestText !== undefined && observed.latestText.trim().length > 0)
173
+ if (observed?.latestText !== undefined && observed.latestText.trim().length > 0) {
164
174
  latestCandidate = observed;
175
+ // Track text stability for idle finalization. Active tool runs reset
176
+ // stability so we never finalize mid tool-call.
177
+ if (observed.hasRunningTool) {
178
+ stableText = undefined;
179
+ stableCount = 0;
180
+ }
181
+ else if (observed.latestText === stableText) {
182
+ stableCount++;
183
+ }
184
+ else {
185
+ stableText = observed.latestText;
186
+ stableCount = 1;
187
+ firstStableMs = nowMs;
188
+ }
189
+ }
165
190
  if (observed?.terminalObserved === true && observed.latestText !== undefined && observed.latestText.trim().length > 0) {
166
- return { text: observed.latestText, completionStatus: "final", outputKind: observed.outputKind, usableForSynthesis: observed.usableForSynthesis };
191
+ return { text: observed.latestText, completionStatus: "final", outputKind: observed.outputKind, usableForSynthesis: observed.usableForSynthesis, finalizationReason: "terminal_marker", looksLikeRefusalOrError: observed.looksLikeRefusalOrError };
192
+ }
193
+ // Stable-idle: non-terminal text that has been unchanged across several
194
+ // poll cycles and a minimum interval is treated as captured (not a
195
+ // semantic success claim — completion_status stays "final" but the
196
+ // finalization_reason records that this was idle-based capture).
197
+ if (latestCandidate?.latestText !== undefined &&
198
+ stableText !== undefined &&
199
+ stableText.trim().length >= STABLE_IDLE_MIN_LEN &&
200
+ stableCount >= STABLE_IDLE_MIN_CYCLES &&
201
+ nowMs - firstStableMs >= STABLE_IDLE_MIN_MS) {
202
+ return { text: latestCandidate.latestText, completionStatus: "final", outputKind: latestCandidate.outputKind, usableForSynthesis: latestCandidate.usableForSynthesis, finalizationReason: "stable_idle", looksLikeRefusalOrError: latestCandidate.looksLikeRefusalOrError };
167
203
  }
168
204
  const silenceMs = nowMs - lastActivityMs;
169
205
  if (silenceMs >= quietPeriodMs) {
@@ -183,7 +219,7 @@ async function extractAssistantTextFromResponse(client, childSessionId, opts) {
183
219
  else {
184
220
  // Exhausted all nudges. Preserve usable candidate text as partial output.
185
221
  if (latestCandidate?.latestText !== undefined && latestCandidate.latestText.trim().length > 0) {
186
- return { text: latestCandidate.latestText, completionStatus: "partial", outputKind: latestCandidate.outputKind, usableForSynthesis: latestCandidate.usableForSynthesis };
222
+ return { text: latestCandidate.latestText, completionStatus: "partial", outputKind: latestCandidate.outputKind, usableForSynthesis: latestCandidate.usableForSynthesis, finalizationReason: "nudge_exhausted_partial", looksLikeRefusalOrError: latestCandidate.looksLikeRefusalOrError };
187
223
  }
188
224
  return undefined;
189
225
  }
@@ -260,6 +296,7 @@ function writeAgentTaskProgress(input) {
260
296
  }
261
297
  function writeAgentTaskTerminalLifecycle(input) {
262
298
  const childSessionRef = input.childSessionRef === input.parentSessionRef ? undefined : input.childSessionRef;
299
+ const messageRef = input.messageRef ?? (input.state === "complete" ? `msg-${input.laneId}` : undefined);
263
300
  const record = {
264
301
  schema_version: "flowdesk.lane_lifecycle_record.v1",
265
302
  lane_id: input.laneId,
@@ -267,11 +304,13 @@ function writeAgentTaskTerminalLifecycle(input) {
267
304
  attempt_id: input.attemptId,
268
305
  parent_session_ref: input.parentSessionRef,
269
306
  ...(childSessionRef === undefined ? {} : { child_session_ref: childSessionRef }),
270
- ...(input.messageRef === undefined ? {} : { message_ref: input.messageRef }),
307
+ ...(messageRef === undefined ? {} : { message_ref: messageRef }),
271
308
  agent_ref: input.agentRef,
272
309
  provider_qualified_model_id: input.providerQualifiedModelId,
273
310
  state: input.state,
311
+ ...(input.verdictRef === undefined ? {} : { verdict_ref: input.verdictRef }),
274
312
  ...(input.outputRef === undefined ? {} : { output_ref: input.outputRef }),
313
+ ...(input.state === "complete" ? { runtime_echo_ref: `runtime-echo-${input.laneId}`, telemetry_ref: `telemetry-${input.laneId}` } : {}),
275
314
  timeout_ms: input.timeoutMs ?? 0,
276
315
  orphan_max_age_ms: 0,
277
316
  retry_count: 0,
@@ -289,6 +328,72 @@ function writeAgentTaskTerminalLifecycle(input) {
289
328
  record: record,
290
329
  });
291
330
  }
331
+ function extractJsonBlocksFromText(raw) {
332
+ const trimmed = raw.trim();
333
+ const results = [];
334
+ if (trimmed.startsWith("{") && trimmed.endsWith("}"))
335
+ return [trimmed];
336
+ const fencePattern = /```(?:json)?\s*\n?(\{[\s\S]*?\})\s*\n?```/g;
337
+ for (const match of trimmed.matchAll(fencePattern)) {
338
+ if (match[1])
339
+ results.push(match[1].trim());
340
+ }
341
+ if (results.length > 0)
342
+ return results;
343
+ let depth = 0;
344
+ let start = -1;
345
+ let lastBlock;
346
+ for (let i = 0; i < trimmed.length; i++) {
347
+ const ch = trimmed[i];
348
+ if (ch === "{") {
349
+ if (depth === 0)
350
+ start = i;
351
+ depth++;
352
+ }
353
+ else if (ch === "}") {
354
+ depth--;
355
+ if (depth === 0 && start !== -1) {
356
+ lastBlock = trimmed.slice(start, i + 1).trim();
357
+ start = -1;
358
+ }
359
+ }
360
+ }
361
+ return lastBlock === undefined ? [] : [lastBlock];
362
+ }
363
+ function observedTopTierReviewerVerdictFromText(input) {
364
+ for (const block of extractJsonBlocksFromText(input.text)) {
365
+ try {
366
+ const candidate = JSON.parse(block);
367
+ const validation = validateTopTierReviewVerdictV1(candidate);
368
+ if (!validation.ok)
369
+ continue;
370
+ const verdict = candidate;
371
+ if (verdict.workflow_id === input.workflowId)
372
+ return verdict;
373
+ }
374
+ catch {
375
+ // Keep scanning candidates.
376
+ }
377
+ }
378
+ return undefined;
379
+ }
380
+ function persistObservedReviewerVerdict(input) {
381
+ const evidenceId = input.verdict.verdict_id;
382
+ if (!writeSessionEvidence({
383
+ rootDir: input.rootDir,
384
+ workflowId: input.workflowId,
385
+ evidenceId,
386
+ record: input.verdict,
387
+ }))
388
+ return false;
389
+ const reloaded = reloadFlowDeskSessionEvidenceV1({
390
+ rootDir: input.rootDir,
391
+ workflowId: input.workflowId,
392
+ });
393
+ return reloaded.ok && reloaded.blocked.length === 0 && reloaded.entries.some((entry) => entry.evidenceClass === "reviewer_verdict" &&
394
+ entry.evidenceId === evidenceId &&
395
+ entry.record.verdict_id === input.verdict.verdict_id);
396
+ }
292
397
  export async function executeFlowDeskAgentTaskV1(input) {
293
398
  const observedAt = new Date().toISOString();
294
399
  const token = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -376,9 +481,8 @@ export async function executeFlowDeskAgentTaskV1(input) {
376
481
  observedAt,
377
482
  });
378
483
  // Launch the lane — wrap in absolute timeout so session.prompt blocking doesn't hang forever.
379
- // The launch phase timeout is longer (5 min) since promptAsync may queue work before responding.
380
- // 1 min default — if session.prompt blocks for more than 1 min with no activity, give up
381
- const LAUNCH_TIMEOUT_MS = input._launchTimeoutMs ?? 60_000;
484
+ // 30s default if session.prompt blocks for more than 30s with no activity, give up.
485
+ const LAUNCH_TIMEOUT_MS = input._launchTimeoutMs ?? 30_000;
382
486
  const launchTimeoutHandle = setTimeout(() => { }, LAUNCH_TIMEOUT_MS);
383
487
  const dispatchMethod = input.client.session.promptAsync !== undefined ? "promptAsync" : "prompt";
384
488
  const launchResult = await Promise.race([
@@ -488,6 +592,11 @@ export async function executeFlowDeskAgentTaskV1(input) {
488
592
  updatedAt: new Date().toISOString(),
489
593
  timeoutMs: input.timeoutMs,
490
594
  });
595
+ refreshFlowDeskCompletionUiCachesV1({
596
+ rootDir: input.rootDir,
597
+ workflowId: input.workflowId,
598
+ observedAt,
599
+ });
491
600
  return {
492
601
  status: "task_failed",
493
602
  failureCategory,
@@ -508,6 +617,11 @@ export async function executeFlowDeskAgentTaskV1(input) {
508
617
  observedAt,
509
618
  progressSummaryLabel: `agent task lane launch heartbeat`,
510
619
  });
620
+ refreshFlowDeskCompletionUiCachesV1({
621
+ rootDir: input.rootDir,
622
+ workflowId: input.workflowId,
623
+ observedAt,
624
+ });
511
625
  // Extract child session ID
512
626
  const childSessionId = launchResult.childSessionRef?.startsWith("ses-")
513
627
  ? launchResult.childSessionRef.slice("ses-".length)
@@ -546,6 +660,11 @@ export async function executeFlowDeskAgentTaskV1(input) {
546
660
  progressSeq: 2,
547
661
  progressLabel: "agent task waiting for async child result",
548
662
  });
663
+ refreshFlowDeskCompletionUiCachesV1({
664
+ rootDir: input.rootDir,
665
+ workflowId: input.workflowId,
666
+ observedAt: new Date().toISOString(),
667
+ });
549
668
  return { status: "task_launched", laneId: input.laneId, childSessionId: resolvedChildId };
550
669
  }
551
670
  let resultObservation;
@@ -555,7 +674,7 @@ export async function executeFlowDeskAgentTaskV1(input) {
555
674
  const agentName = launchResult.status === "lane_launch_started" && typeof launchResult.agent === "string"
556
675
  ? launchResult.agent : undefined;
557
676
  resultObservation = await extractAssistantTextFromResponse(input.client, childSessionId, {
558
- quietPeriodMs: input._nudgeQuietPeriodMs ?? 20_000, // default 20s per policy
677
+ quietPeriodMs: input._nudgeQuietPeriodMs ?? 10_000, // default 10s per policy
559
678
  maxNudges: 2,
560
679
  runtimeModel,
561
680
  agentName,
@@ -628,6 +747,37 @@ export async function executeFlowDeskAgentTaskV1(input) {
628
747
  updatedAt: new Date().toISOString(),
629
748
  timeoutMs: input.timeoutMs,
630
749
  });
750
+ refreshFlowDeskCompletionUiCachesV1({
751
+ rootDir: input.rootDir,
752
+ workflowId: input.workflowId,
753
+ observedAt: new Date().toISOString(),
754
+ });
755
+ // Auto-retry with fallback binding if configured and this is not already a retry
756
+ if (input.fallbackBinding !== undefined && !input._isFallbackRetry) {
757
+ const retryToken = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
758
+ const retryTaskId = `${input.taskId}-retry-${retryToken.slice(0, 6)}`;
759
+ const retryLaneId = `${input.laneId}-retry`;
760
+ writeAgentTaskProgress({
761
+ rootDir: input.rootDir,
762
+ workflowId: input.workflowId,
763
+ laneId: retryLaneId,
764
+ taskId: retryTaskId,
765
+ agentRef: input.fallbackBinding.agentRef,
766
+ providerQualifiedModelId: input.fallbackBinding.providerQualifiedModelId,
767
+ phase: "retrying",
768
+ progressSeq: 0,
769
+ progressLabel: `auto-retry with ${input.fallbackBinding.providerQualifiedModelId} after ${failureCategory}`,
770
+ });
771
+ return executeFlowDeskAgentTaskV1({
772
+ ...input,
773
+ taskId: retryTaskId,
774
+ laneId: retryLaneId,
775
+ agentRef: input.fallbackBinding.agentRef,
776
+ providerQualifiedModelId: input.fallbackBinding.providerQualifiedModelId,
777
+ fallbackBinding: undefined,
778
+ _isFallbackRetry: true,
779
+ });
780
+ }
631
781
  return {
632
782
  status: "task_failed",
633
783
  failureCategory,
@@ -656,9 +806,17 @@ export async function executeFlowDeskAgentTaskV1(input) {
656
806
  completion_status: resultObservation?.completionStatus ?? "final",
657
807
  output_kind: resultObservation?.outputKind ?? "final_answer",
658
808
  usable_for_synthesis: resultObservation?.usableForSynthesis ?? true,
659
- missing_contract: input.outputContract === "final_assistant_text" &&
660
- (resultObservation?.completionStatus !== "final" ||
661
- ["empty", "process_notes", "tool_trace_only"].includes(String(resultObservation?.outputKind ?? ""))),
809
+ // Capture/judgement separation: text was captured, so this is NOT a
810
+ // contract failure. output_kind/completion_status/looks_like_refusal_or_error
811
+ // are advisory inputs for the coordinator's substance judgement, never a
812
+ // capture-side drop. missing_contract is only ever true when an explicit
813
+ // contract was requested AND no text was captured (that path returns
814
+ // task_failed above, so here it is always false).
815
+ missing_contract: false,
816
+ ...(resultObservation?.finalizationReason === undefined
817
+ ? {}
818
+ : { finalization_reason: resultObservation.finalizationReason }),
819
+ looks_like_refusal_or_error: resultObservation?.looksLikeRefusalOrError ?? false,
662
820
  created_at: observedAt,
663
821
  dispatch_authority_enabled: false,
664
822
  };
@@ -669,13 +827,73 @@ export async function executeFlowDeskAgentTaskV1(input) {
669
827
  record: taskResultRecord,
670
828
  });
671
829
  if (!taskResultWritten) {
830
+ const taskFailedEvidenceId = `task-failed-${input.taskId}-${token}-result-write`;
831
+ const redactedReason = "task_result evidence persistence failed";
832
+ writeSessionEvidence({
833
+ rootDir: input.rootDir,
834
+ workflowId: input.workflowId,
835
+ evidenceId: taskFailedEvidenceId,
836
+ record: {
837
+ schema_version: "flowdesk.task_failed.v1",
838
+ workflow_id: input.workflowId,
839
+ lane_id: input.laneId,
840
+ task_id: input.taskId,
841
+ agent_ref: input.agentRef,
842
+ provider_qualified_model_id: input.providerQualifiedModelId,
843
+ failure_category: "unknown",
844
+ redacted_reason: redactedReason,
845
+ created_at: observedAt,
846
+ dispatch_authority_enabled: false,
847
+ },
848
+ });
849
+ writeAgentTaskProgress({
850
+ rootDir: input.rootDir,
851
+ workflowId: input.workflowId,
852
+ laneId: input.laneId,
853
+ taskId: input.taskId,
854
+ agentRef: input.agentRef,
855
+ providerQualifiedModelId: input.providerQualifiedModelId,
856
+ phase: "failed",
857
+ progressSeq: 4,
858
+ progressLabel: "agent task result persistence failed",
859
+ });
860
+ writeAgentTaskTerminalLifecycle({
861
+ rootDir: input.rootDir,
862
+ workflowId: input.workflowId,
863
+ laneId: input.laneId,
864
+ attemptId,
865
+ parentSessionRef,
866
+ agentRef: input.agentRef,
867
+ providerQualifiedModelId: input.providerQualifiedModelId,
868
+ state: "invocation_failed",
869
+ evidenceId: `lifecycle-task-terminal-${input.laneId}-${token}-result-write`,
870
+ createdAt: observedAt,
871
+ updatedAt: new Date().toISOString(),
872
+ timeoutMs: input.timeoutMs,
873
+ });
874
+ refreshFlowDeskCompletionUiCachesV1({
875
+ rootDir: input.rootDir,
876
+ workflowId: input.workflowId,
877
+ observedAt,
878
+ });
672
879
  return {
673
880
  status: "task_failed",
674
881
  failureCategory: "unknown",
675
- redactedReason: "task_result evidence persistence failed",
882
+ redactedReason,
676
883
  laneId: input.laneId,
677
884
  };
678
885
  }
886
+ const observedReviewerVerdict = observedTopTierReviewerVerdictFromText({
887
+ text: fullResultText,
888
+ workflowId: input.workflowId,
889
+ });
890
+ const reviewerVerdictPersisted = observedReviewerVerdict === undefined
891
+ ? false
892
+ : persistObservedReviewerVerdict({
893
+ rootDir: input.rootDir,
894
+ workflowId: input.workflowId,
895
+ verdict: observedReviewerVerdict,
896
+ });
679
897
  writeAgentTaskProgress({
680
898
  rootDir: input.rootDir,
681
899
  workflowId: input.workflowId,
@@ -685,7 +903,9 @@ export async function executeFlowDeskAgentTaskV1(input) {
685
903
  providerQualifiedModelId: input.providerQualifiedModelId,
686
904
  phase: "finalizing",
687
905
  progressSeq: 3,
688
- progressLabel: "agent task result captured",
906
+ progressLabel: reviewerVerdictPersisted
907
+ ? "agent task result captured with reviewer verdict evidence"
908
+ : "agent task result captured",
689
909
  });
690
910
  writeAgentTaskTerminalLifecycle({
691
911
  rootDir: input.rootDir,
@@ -697,7 +917,8 @@ export async function executeFlowDeskAgentTaskV1(input) {
697
917
  messageRef: launchResult.messageRef?.startsWith("msg-") ? launchResult.messageRef : undefined,
698
918
  agentRef: input.agentRef,
699
919
  providerQualifiedModelId: input.providerQualifiedModelId,
700
- state: "incomplete",
920
+ state: reviewerVerdictPersisted ? "complete" : "incomplete",
921
+ verdictRef: reviewerVerdictPersisted ? observedReviewerVerdict?.verdict_id : undefined,
701
922
  outputRef: `output-${taskResultEvidenceId}`,
702
923
  evidenceId: `lifecycle-task-terminal-${input.laneId}-${token}`,
703
924
  createdAt: observedAt,