@ai-agent-forge/agent-forge 0.86.3 → 0.86.5

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 (238) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/bundle/.build-manifest.json +500 -434
  3. package/dist/bundle/chunks/anthropic-messages-ALDZBUKI.js +6 -0
  4. package/dist/bundle/chunks/anthropic.js +2 -2
  5. package/dist/bundle/chunks/{azure-openai-responses-WQCZWFAH.js → azure-openai-responses-A2WJWZFB.js} +1 -1
  6. package/dist/bundle/chunks/bedrock-converse-stream.js +1 -1
  7. package/dist/bundle/chunks/chunk-A7GUOX2H.js +141 -0
  8. package/dist/bundle/chunks/{chunk-WGLBM2BP.js → chunk-CIX3FSBK.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-RK2X2KU2.js → chunk-EALJ5DDX.js} +111 -101
  10. package/dist/bundle/chunks/chunk-OO6DJ3CL.js +31 -0
  11. package/dist/bundle/chunks/{chunk-N6BQJSGH.js → chunk-SQFG22WN.js} +3 -3
  12. package/dist/bundle/chunks/{chunk-V46XUTK3.js → chunk-Y7HXTASS.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-3WE3W52L.js → chunk-YJCQVXW5.js} +1 -1
  14. package/dist/bundle/chunks/github-copilot.js +1 -1
  15. package/dist/bundle/chunks/google-generative-ai-NIB4WONV.js +2 -0
  16. package/dist/bundle/chunks/google-vertex-DCPYUEUO.js +2 -0
  17. package/dist/bundle/chunks/mistral-conversations-OC6LA5XL.js +5 -0
  18. package/dist/bundle/chunks/{openai-codex-responses-YDJSZOIH.js → openai-codex-responses-IO2X4Y7L.js} +1 -1
  19. package/dist/bundle/chunks/openai-codex.js +2 -2
  20. package/dist/bundle/chunks/{openai-completions-UPRWTQPT.js → openai-completions-XYQQHETU.js} +1 -1
  21. package/dist/bundle/chunks/{openai-responses-O63W6HQ3.js → openai-responses-DLIRN3CI.js} +1 -1
  22. package/dist/bundle/chunks/{openrouter-images-G3GT5BVT.js → openrouter-images-G4GQRUSG.js} +1 -1
  23. package/dist/bundle/chunks/openrouter.js +2 -2
  24. package/dist/bundle/chunks/{pi-messages-SLF355GF.js → pi-messages-E6RRYBO7.js} +1 -1
  25. package/dist/bundle/chunks/radius.js +2 -2
  26. package/dist/bundle/chunks/{virtual-modules-O72ICTMQ.js → virtual-modules-3IZ4MYSQ.js} +1 -1
  27. package/dist/bundle/cli.js +1 -1
  28. package/dist/bundle/index.js +1 -1
  29. package/dist/bundle/rpc-entry.js +1 -1
  30. package/dist/capabilities/builtin.d.ts +4 -2
  31. package/dist/capabilities/builtin.d.ts.map +1 -1
  32. package/dist/capabilities/builtin.js +5 -1
  33. package/dist/capabilities/builtin.js.map +1 -1
  34. package/dist/cli/plugin-command.d.ts.map +1 -1
  35. package/dist/cli/plugin-command.js +51 -5
  36. package/dist/cli/plugin-command.js.map +1 -1
  37. package/dist/core/agent-session-compaction.d.ts +2 -2
  38. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  39. package/dist/core/agent-session-compaction.js +54 -6
  40. package/dist/core/agent-session-compaction.js.map +1 -1
  41. package/dist/core/agent-session-internal.d.ts +2 -4
  42. package/dist/core/agent-session-internal.d.ts.map +1 -1
  43. package/dist/core/agent-session-internal.js.map +1 -1
  44. package/dist/core/agent-session-paths.d.ts +2 -0
  45. package/dist/core/agent-session-paths.d.ts.map +1 -1
  46. package/dist/core/agent-session-paths.js +2 -1
  47. package/dist/core/agent-session-paths.js.map +1 -1
  48. package/dist/core/agent-session-projection.d.ts +10 -4
  49. package/dist/core/agent-session-projection.d.ts.map +1 -1
  50. package/dist/core/agent-session-projection.js +17 -4
  51. package/dist/core/agent-session-projection.js.map +1 -1
  52. package/dist/core/agent-session.d.ts +19 -5
  53. package/dist/core/agent-session.d.ts.map +1 -1
  54. package/dist/core/agent-session.js +30 -4
  55. package/dist/core/agent-session.js.map +1 -1
  56. package/dist/core/extensions/bootstrap-compaction-policy.d.ts.map +1 -1
  57. package/dist/core/extensions/bootstrap-compaction-policy.js +1 -2
  58. package/dist/core/extensions/bootstrap-compaction-policy.js.map +1 -1
  59. package/dist/core/extensions/bootstrap-lifecycle.d.ts +1 -1
  60. package/dist/core/extensions/bootstrap-lifecycle.d.ts.map +1 -1
  61. package/dist/core/extensions/bootstrap-lifecycle.js +5 -2
  62. package/dist/core/extensions/bootstrap-lifecycle.js.map +1 -1
  63. package/dist/core/extensions/capability-runtime-store.d.ts.map +1 -1
  64. package/dist/core/extensions/capability-runtime-store.js +1 -0
  65. package/dist/core/extensions/capability-runtime-store.js.map +1 -1
  66. package/dist/core/extensions/capability-runtime-types.d.ts +16 -1
  67. package/dist/core/extensions/capability-runtime-types.d.ts.map +1 -1
  68. package/dist/core/extensions/capability-runtime-types.js.map +1 -1
  69. package/dist/core/extensions/capability-runtime.d.ts.map +1 -1
  70. package/dist/core/extensions/capability-runtime.js +38 -2
  71. package/dist/core/extensions/capability-runtime.js.map +1 -1
  72. package/dist/core/extensions/index.d.ts +1 -1
  73. package/dist/core/extensions/index.d.ts.map +1 -1
  74. package/dist/core/extensions/index.js.map +1 -1
  75. package/dist/core/extensions/lifecycle-catalog.d.ts +46 -0
  76. package/dist/core/extensions/lifecycle-catalog.d.ts.map +1 -1
  77. package/dist/core/extensions/lifecycle-catalog.js +101 -0
  78. package/dist/core/extensions/lifecycle-catalog.js.map +1 -1
  79. package/dist/core/extensions/microcompact.d.ts +16 -8
  80. package/dist/core/extensions/microcompact.d.ts.map +1 -1
  81. package/dist/core/extensions/microcompact.js +9 -7
  82. package/dist/core/extensions/microcompact.js.map +1 -1
  83. package/dist/core/extensions/provider-failover.d.ts +106 -0
  84. package/dist/core/extensions/provider-failover.d.ts.map +1 -0
  85. package/dist/core/extensions/provider-failover.js +692 -0
  86. package/dist/core/extensions/provider-failover.js.map +1 -0
  87. package/dist/core/facade/coding-agent-facade.d.ts +13 -6
  88. package/dist/core/facade/coding-agent-facade.d.ts.map +1 -1
  89. package/dist/core/facade/coding-agent-facade.js +3 -1
  90. package/dist/core/facade/coding-agent-facade.js.map +1 -1
  91. package/dist/core/keybindings.d.ts +6 -6
  92. package/dist/core/keybindings.d.ts.map +1 -1
  93. package/dist/core/keybindings.js +23 -6
  94. package/dist/core/keybindings.js.map +1 -1
  95. package/dist/core/model-config.d.ts +102 -0
  96. package/dist/core/model-config.d.ts.map +1 -1
  97. package/dist/core/model-config.js +46 -14
  98. package/dist/core/model-config.js.map +1 -1
  99. package/dist/core/model-resolver.d.ts.map +1 -1
  100. package/dist/core/model-resolver.js +1 -1
  101. package/dist/core/model-resolver.js.map +1 -1
  102. package/dist/core/model-runtime.d.ts +15 -0
  103. package/dist/core/model-runtime.d.ts.map +1 -1
  104. package/dist/core/model-runtime.js +30 -18
  105. package/dist/core/model-runtime.js.map +1 -1
  106. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  107. package/dist/core/remote-catalog-provider.js +4 -9
  108. package/dist/core/remote-catalog-provider.js.map +1 -1
  109. package/dist/core/sdk.d.ts.map +1 -1
  110. package/dist/core/sdk.js +107 -24
  111. package/dist/core/sdk.js.map +1 -1
  112. package/dist/core/session-manager.d.ts +2 -0
  113. package/dist/core/session-manager.d.ts.map +1 -1
  114. package/dist/core/session-manager.js +10 -5
  115. package/dist/core/session-manager.js.map +1 -1
  116. package/dist/core/settings-manager.d.ts +74 -5
  117. package/dist/core/settings-manager.d.ts.map +1 -1
  118. package/dist/core/settings-manager.js +143 -10
  119. package/dist/core/settings-manager.js.map +1 -1
  120. package/dist/core/slash-commands.d.ts.map +1 -1
  121. package/dist/core/slash-commands.js +17 -2
  122. package/dist/core/slash-commands.js.map +1 -1
  123. package/dist/core/tools/bash.d.ts.map +1 -1
  124. package/dist/core/tools/bash.js +9 -10
  125. package/dist/core/tools/bash.js.map +1 -1
  126. package/dist/core/tools/render-utils.d.ts +6 -0
  127. package/dist/core/tools/render-utils.d.ts.map +1 -1
  128. package/dist/core/tools/render-utils.js +26 -0
  129. package/dist/core/tools/render-utils.js.map +1 -1
  130. package/dist/core/tools/tool-definition.d.ts +21 -2
  131. package/dist/core/tools/tool-definition.d.ts.map +1 -1
  132. package/dist/core/tools/tool-definition.js.map +1 -1
  133. package/dist/main.d.ts.map +1 -1
  134. package/dist/main.js +3 -6
  135. package/dist/main.js.map +1 -1
  136. package/dist/modes/interactive/components/agents-view.d.ts +57 -0
  137. package/dist/modes/interactive/components/agents-view.d.ts.map +1 -0
  138. package/dist/modes/interactive/components/agents-view.js +260 -0
  139. package/dist/modes/interactive/components/agents-view.js.map +1 -0
  140. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  141. package/dist/modes/interactive/components/custom-editor.js +2 -2
  142. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  143. package/dist/modes/interactive/components/footer.d.ts +15 -0
  144. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  145. package/dist/modes/interactive/components/footer.js +90 -9
  146. package/dist/modes/interactive/components/footer.js.map +1 -1
  147. package/dist/modes/interactive/components/scoped-models-selector.d.ts +1 -1
  148. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  149. package/dist/modes/interactive/components/scoped-models-selector.js +1 -1
  150. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  151. package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  152. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  153. package/dist/modes/interactive/components/settings-selector.js +12 -2
  154. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  155. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  156. package/dist/modes/interactive/components/thinking-selector.js +1 -1
  157. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  158. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  159. package/dist/modes/interactive/components/tool-execution.js +89 -10
  160. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  161. package/dist/modes/interactive/components/user-message-selector.d.ts +3 -0
  162. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  163. package/dist/modes/interactive/components/user-message-selector.js +9 -3
  164. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  165. package/dist/modes/interactive/components/workflow-view.d.ts +119 -0
  166. package/dist/modes/interactive/components/workflow-view.d.ts.map +1 -0
  167. package/dist/modes/interactive/components/workflow-view.js +638 -0
  168. package/dist/modes/interactive/components/workflow-view.js.map +1 -0
  169. package/dist/modes/interactive/interactive-commands.d.ts +22 -0
  170. package/dist/modes/interactive/interactive-commands.d.ts.map +1 -1
  171. package/dist/modes/interactive/interactive-commands.js +56 -0
  172. package/dist/modes/interactive/interactive-commands.js.map +1 -1
  173. package/dist/modes/interactive/interactive-mode.d.ts +51 -1
  174. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  175. package/dist/modes/interactive/interactive-mode.js +218 -37
  176. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  177. package/dist/modes/interactive/interactive-selectors.d.ts +2 -4
  178. package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
  179. package/dist/modes/interactive/interactive-selectors.js +10 -1
  180. package/dist/modes/interactive/interactive-selectors.js.map +1 -1
  181. package/dist/modes/print-mode.d.ts.map +1 -1
  182. package/dist/modes/print-mode.js +5 -0
  183. package/dist/modes/print-mode.js.map +1 -1
  184. package/dist/modes/rpc/rpc-client.d.ts +3 -1
  185. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  186. package/dist/modes/rpc/rpc-client.js +4 -2
  187. package/dist/modes/rpc/rpc-client.js.map +1 -1
  188. package/dist/modes/rpc/rpc-types.d.ts +1 -0
  189. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  190. package/dist/modes/rpc/rpc-types.js.map +1 -1
  191. package/dist/plugins/ecosystem/bootstrap-factory-manifest.d.ts.map +1 -1
  192. package/dist/plugins/ecosystem/bootstrap-factory-manifest.js +7 -0
  193. package/dist/plugins/ecosystem/bootstrap-factory-manifest.js.map +1 -1
  194. package/dist/plugins/ecosystem/cli.d.ts.map +1 -1
  195. package/dist/plugins/ecosystem/cli.js +1 -1
  196. package/dist/plugins/ecosystem/cli.js.map +1 -1
  197. package/dist/plugins/ecosystem/install-store-paths.d.ts +15 -0
  198. package/dist/plugins/ecosystem/install-store-paths.d.ts.map +1 -0
  199. package/dist/plugins/ecosystem/install-store-paths.js +58 -0
  200. package/dist/plugins/ecosystem/install-store-paths.js.map +1 -0
  201. package/dist/plugins/ecosystem/node-disk-registry-source.d.ts.map +1 -1
  202. package/dist/plugins/ecosystem/node-disk-registry-source.js +3 -8
  203. package/dist/plugins/ecosystem/node-disk-registry-source.js.map +1 -1
  204. package/dist/plugins/ecosystem/node-package-install-host.d.ts.map +1 -1
  205. package/dist/plugins/ecosystem/node-package-install-host.js +9 -1
  206. package/dist/plugins/ecosystem/node-package-install-host.js.map +1 -1
  207. package/dist/plugins/ecosystem/node-package-install-persistence.d.ts.map +1 -1
  208. package/dist/plugins/ecosystem/node-package-install-persistence.js +3 -8
  209. package/dist/plugins/ecosystem/node-package-install-persistence.js.map +1 -1
  210. package/dist/profile/plugin-profiles.d.ts +2 -0
  211. package/dist/profile/plugin-profiles.d.ts.map +1 -1
  212. package/dist/profile/plugin-profiles.js +4 -0
  213. package/dist/profile/plugin-profiles.js.map +1 -1
  214. package/dist/profiles/builtin-suites.d.ts +19 -2
  215. package/dist/profiles/builtin-suites.d.ts.map +1 -1
  216. package/dist/profiles/builtin-suites.js +20 -2
  217. package/dist/profiles/builtin-suites.js.map +1 -1
  218. package/dist/profiles/suite-loader.d.ts.map +1 -1
  219. package/dist/profiles/suite-loader.js +4 -1
  220. package/dist/profiles/suite-loader.js.map +1 -1
  221. package/dist/utils/clipboard.d.ts.map +1 -1
  222. package/dist/utils/clipboard.js +10 -10
  223. package/dist/utils/clipboard.js.map +1 -1
  224. package/dist/utils/shell.d.ts +1 -3
  225. package/dist/utils/shell.d.ts.map +1 -1
  226. package/dist/utils/shell.js +3 -30
  227. package/dist/utils/shell.js.map +1 -1
  228. package/docs/keybindings.md +8 -8
  229. package/docs/models.md +1 -1
  230. package/docs/rpc.md +3 -1
  231. package/docs/settings.md +2 -1
  232. package/package.json +4 -4
  233. package/dist/bundle/chunks/anthropic-messages-EVFQ6MLR.js +0 -6
  234. package/dist/bundle/chunks/chunk-F6SBZUJG.js +0 -141
  235. package/dist/bundle/chunks/chunk-NUHFSC37.js +0 -25
  236. package/dist/bundle/chunks/google-generative-ai-ISTTUZMW.js +0 -2
  237. package/dist/bundle/chunks/google-vertex-NS7ZF7DK.js +0 -2
  238. package/dist/bundle/chunks/mistral-conversations-GINO6Z4I.js +0 -5
@@ -0,0 +1,638 @@
1
+ /**
2
+ * Workflow overlay view (§9 L2/L3 of docs/design/工作流编排插件设计.md).
3
+ *
4
+ * Read-only panorama (L2) and transition stream (L3) rendered from the
5
+ * session's `workflow.state` / `workflow.transition` custom entries — the
6
+ * same isomorphic read the footer performs on `goal.state`, so the plugin
7
+ * does not need to be present and this module never imports plugin code.
8
+ * Waiting-condition labels mirror the engine's `computeWaitingOn` order
9
+ * (active-limit precedes write-scope conflict); plan lookups read the latest
10
+ * `plan.state` snapshot the same way.
11
+ *
12
+ * Operations are never executed here: approve/reject/skip/cancel forward an
13
+ * action text (e.g. `approve stage s2-review`) to the conversation input and
14
+ * the main agent drives it through workflow_advance — rendering and execution
15
+ * stay separated (设计 §9 L2 "转发到对话").
16
+ */
17
+ import { Container, getKeybindings, Text, truncateToWidth } from "@agent-forge/tui";
18
+ import { theme } from "../theme/theme.js";
19
+ import { DynamicBorder } from "./dynamic-border.js";
20
+ import { keyHint, rawKeyHint } from "./keybinding-hints.js";
21
+ const STAGE_STATUSES = ["pending", "active", "awaiting_review", "completed", "failed", "skipped"];
22
+ function isRecord(value) {
23
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24
+ }
25
+ function stringOrUndefined(value) {
26
+ return typeof value === "string" ? value : undefined;
27
+ }
28
+ function parseStageSpec(value) {
29
+ if (!isRecord(value))
30
+ return undefined;
31
+ const id = stringOrUndefined(value.id);
32
+ if (id === undefined)
33
+ return undefined;
34
+ const dependsOn = Array.isArray(value.dependsOn) ? value.dependsOn.filter(isString) : [];
35
+ const acceptance = isRecord(value.objective) && Array.isArray(value.objective.acceptance) ? value.objective.acceptance : [];
36
+ const scope = Array.isArray(value.scope) ? value.scope.filter(isString) : undefined;
37
+ return {
38
+ id,
39
+ title: stringOrUndefined(value.title) ?? id,
40
+ dependsOn,
41
+ planId: stringOrUndefined(value.planId),
42
+ kind: value.kind === "read" ? "read" : "write",
43
+ scope: scope !== undefined && scope.length > 0 ? scope : undefined,
44
+ acceptanceIds: acceptance
45
+ .map((item) => (isRecord(item) ? stringOrUndefined(item.id) : undefined))
46
+ .filter((item) => item !== undefined),
47
+ };
48
+ }
49
+ function isString(value) {
50
+ return typeof value === "string";
51
+ }
52
+ function parseStageRuntime(value) {
53
+ if (!isRecord(value))
54
+ return undefined;
55
+ const stageId = stringOrUndefined(value.stageId);
56
+ const status = stringOrUndefined(value.status);
57
+ if (stageId === undefined || status === undefined || !STAGE_STATUSES.includes(status))
58
+ return undefined;
59
+ const evidence = Array.isArray(value.evidence) ? value.evidence : [];
60
+ const covered = new Set();
61
+ let evidenceCount = 0;
62
+ for (const item of evidence) {
63
+ if (!isRecord(item))
64
+ continue;
65
+ evidenceCount++;
66
+ const acceptanceId = stringOrUndefined(item.acceptanceId);
67
+ if (acceptanceId !== undefined)
68
+ covered.add(acceptanceId);
69
+ }
70
+ return {
71
+ stageId,
72
+ status: status,
73
+ attempts: typeof value.attempts === "number" ? value.attempts : 0,
74
+ evidenceCount,
75
+ coveredAcceptanceIds: covered,
76
+ };
77
+ }
78
+ function parseWorkflowRecord(value) {
79
+ if (!isRecord(value))
80
+ return undefined;
81
+ const id = stringOrUndefined(value.id);
82
+ const title = stringOrUndefined(value.title);
83
+ const status = stringOrUndefined(value.status);
84
+ if (id === undefined || title === undefined || status === undefined)
85
+ return undefined;
86
+ const stages = [];
87
+ for (const item of Array.isArray(value.stages) ? value.stages : []) {
88
+ const spec = parseStageSpec(item);
89
+ if (spec !== undefined)
90
+ stages.push(spec);
91
+ }
92
+ const runtime = new Map();
93
+ for (const item of Array.isArray(value.runtime) ? value.runtime : []) {
94
+ const parsed = parseStageRuntime(item);
95
+ if (parsed !== undefined)
96
+ runtime.set(parsed.stageId, parsed);
97
+ }
98
+ const diagnostics = Array.isArray(value.diagnostics) ? value.diagnostics.filter(isString) : [];
99
+ return {
100
+ id,
101
+ title,
102
+ status,
103
+ revision: typeof value.revision === "number" ? value.revision : 0,
104
+ goalId: stringOrUndefined(value.goalId),
105
+ maxActiveStages: typeof value.maxActiveStages === "number" ? value.maxActiveStages : 4,
106
+ maxRetreats: typeof value.maxRetreats === "number" ? value.maxRetreats : 3,
107
+ retreatCount: typeof value.retreatCount === "number" ? value.retreatCount : 0,
108
+ diagnostics,
109
+ stages,
110
+ runtime,
111
+ };
112
+ }
113
+ /** Latest `plan.state` snapshot reduced to id -> status (plan-dangling / plan-incomplete labels). */
114
+ function parsePlanStatuses(data) {
115
+ const statuses = new Map();
116
+ if (!isRecord(data) || !Array.isArray(data.plans))
117
+ return statuses;
118
+ for (const item of data.plans) {
119
+ if (!isRecord(item))
120
+ continue;
121
+ const id = stringOrUndefined(item.id);
122
+ const status = stringOrUndefined(item.status);
123
+ if (id !== undefined && status !== undefined)
124
+ statuses.set(id, status);
125
+ }
126
+ return statuses;
127
+ }
128
+ function parseTransition(value) {
129
+ if (!isRecord(value))
130
+ return undefined;
131
+ const workflowId = stringOrUndefined(value.workflowId);
132
+ const from = stringOrUndefined(value.from);
133
+ const to = stringOrUndefined(value.to);
134
+ const reason = isRecord(value.reason) ? value.reason : undefined;
135
+ const code = reason !== undefined ? stringOrUndefined(reason.code) : undefined;
136
+ if (workflowId === undefined ||
137
+ from === undefined ||
138
+ to === undefined ||
139
+ code === undefined ||
140
+ typeof value.revision !== "number") {
141
+ return undefined;
142
+ }
143
+ return {
144
+ revision: value.revision,
145
+ workflowId,
146
+ stageId: stringOrUndefined(value.stageId),
147
+ from,
148
+ to,
149
+ trigger: stringOrUndefined(value.trigger) ?? "tool",
150
+ code,
151
+ message: reason !== undefined ? (stringOrUndefined(reason.message) ?? "") : "",
152
+ detail: reason !== undefined ? stringOrUndefined(reason.detail) : undefined,
153
+ };
154
+ }
155
+ function scopesIntersect(a, b) {
156
+ if (a === undefined || a.length === 0 || b === undefined || b.length === 0)
157
+ return true;
158
+ return a.some((tag) => b.includes(tag));
159
+ }
160
+ function findScopeConflict(workflow, spec) {
161
+ if (spec.kind !== "write")
162
+ return null;
163
+ for (const other of workflow.stages) {
164
+ if (other.id === spec.id || other.kind !== "write")
165
+ continue;
166
+ if (workflow.runtime.get(other.id)?.status !== "active")
167
+ continue;
168
+ if (scopesIntersect(spec.scope, other.scope))
169
+ return other.id;
170
+ }
171
+ return null;
172
+ }
173
+ function uncoveredAcceptanceIds(spec, runtime) {
174
+ if (runtime === undefined)
175
+ return [...spec.acceptanceIds];
176
+ return spec.acceptanceIds.filter((id) => !runtime.coveredAcceptanceIds.has(id));
177
+ }
178
+ /**
179
+ * §8.1 waiting-condition label, mirroring the engine's `computeWaitingOn`
180
+ * check order (active-limit precedes write-scope conflict) so the overlay
181
+ * names the same condition the audit stream recorded. Plan lookups use the
182
+ * latest `plan.state` snapshot instead of the engine's live dependency.
183
+ */
184
+ function computeWaitingOn(workflow, spec, runtime, planStatuses) {
185
+ const status = runtime?.status ?? "pending";
186
+ if (status === "awaiting_review")
187
+ return "review";
188
+ if (status === "pending") {
189
+ if (workflow.status === "draft")
190
+ return "not-yet-active";
191
+ if ((runtime?.attempts ?? 0) > 0) {
192
+ if (spec.acceptanceIds.length === 0)
193
+ return "retry-wait";
194
+ return `acceptance-missing:[${uncoveredAcceptanceIds(spec, runtime).join(", ")}]`;
195
+ }
196
+ const incomplete = spec.dependsOn.filter((dep) => {
197
+ const dependency = workflow.runtime.get(dep)?.status;
198
+ return dependency !== "completed" && dependency !== "skipped";
199
+ });
200
+ if (incomplete.length > 0)
201
+ return `deps-incomplete:[${incomplete.join(", ")}]`;
202
+ if (spec.planId !== undefined && !planStatuses.has(spec.planId))
203
+ return "plan-dangling";
204
+ const activeCount = [...workflow.runtime.values()].filter((item) => item.status === "active").length;
205
+ if (activeCount >= workflow.maxActiveStages)
206
+ return "active-limit";
207
+ const conflict = findScopeConflict(workflow, spec);
208
+ if (conflict !== null)
209
+ return `write-scope-conflict:[${conflict}]`;
210
+ return "not-yet-active";
211
+ }
212
+ if (status === "active") {
213
+ if (spec.planId === undefined)
214
+ return null;
215
+ const planStatus = planStatuses.get(spec.planId);
216
+ if (planStatus === undefined)
217
+ return "plan-dangling";
218
+ if (planStatus !== "completed")
219
+ return "plan-incomplete";
220
+ return null;
221
+ }
222
+ return null;
223
+ }
224
+ function buildStageRows(workflow, planStatuses) {
225
+ const rows = [];
226
+ const visited = new Set();
227
+ const walk = (spec, depth) => {
228
+ if (visited.has(spec.id)) {
229
+ rows.push({
230
+ workflowId: workflow.id,
231
+ stageId: spec.id,
232
+ title: spec.title,
233
+ depth,
234
+ status: workflow.runtime.get(spec.id)?.status ?? "pending",
235
+ waitingOn: null,
236
+ attempts: 0,
237
+ evidenceCount: 0,
238
+ scopeConflict: null,
239
+ backReference: true,
240
+ });
241
+ return;
242
+ }
243
+ visited.add(spec.id);
244
+ const runtime = workflow.runtime.get(spec.id);
245
+ rows.push({
246
+ workflowId: workflow.id,
247
+ stageId: spec.id,
248
+ title: spec.title,
249
+ depth,
250
+ status: runtime?.status ?? "pending",
251
+ waitingOn: computeWaitingOn(workflow, spec, runtime, planStatuses),
252
+ attempts: runtime?.attempts ?? 0,
253
+ evidenceCount: runtime?.evidenceCount ?? 0,
254
+ scopeConflict: findScopeConflict(workflow, spec),
255
+ backReference: false,
256
+ });
257
+ for (const child of workflow.stages.filter((item) => item.dependsOn.includes(spec.id))) {
258
+ walk(child, depth + 1);
259
+ }
260
+ };
261
+ for (const root of workflow.stages.filter((item) => item.dependsOn.length === 0)) {
262
+ walk(root, 0);
263
+ }
264
+ // Stages unreachable from a root (defensive; the engine validates acyclic
265
+ // graphs) still render at the top level, mirroring the L1 text panorama.
266
+ for (const spec of workflow.stages) {
267
+ if (!visited.has(spec.id))
268
+ walk(spec, 0);
269
+ }
270
+ return rows;
271
+ }
272
+ /**
273
+ * Build the L2/L3 view model from session entries: the latest
274
+ * `workflow.state` snapshot plus the `workflow.transition` audit stream
275
+ * (newest first, capped). Malformed records are skipped, never thrown.
276
+ */
277
+ export function buildWorkflowViewModel(entries, transitionLimit = 30) {
278
+ let stateData;
279
+ let hasStateEntry = false;
280
+ let planData;
281
+ const transitions = [];
282
+ for (const entry of entries) {
283
+ if (entry.type !== "custom")
284
+ continue;
285
+ if (entry.customType === "workflow.state") {
286
+ hasStateEntry = true;
287
+ stateData = entry.data;
288
+ }
289
+ else if (entry.customType === "plan.state") {
290
+ planData = entry.data;
291
+ }
292
+ else if (entry.customType === "workflow.transition") {
293
+ const parsed = parseTransition(entry.data);
294
+ if (parsed !== undefined)
295
+ transitions.push(parsed);
296
+ }
297
+ }
298
+ const planStatuses = parsePlanStatuses(planData);
299
+ let blocks;
300
+ if (hasStateEntry) {
301
+ const workflows = isRecord(stateData) && Array.isArray(stateData.workflows) ? stateData.workflows : [];
302
+ blocks = workflows
303
+ .map((item) => {
304
+ const parsed = parseWorkflowRecord(item);
305
+ if (parsed === undefined)
306
+ return undefined;
307
+ return {
308
+ workflowId: parsed.id,
309
+ title: parsed.title,
310
+ status: parsed.status,
311
+ revision: parsed.revision,
312
+ goalId: parsed.goalId,
313
+ activeCount: [...parsed.runtime.values()].filter((item) => item.status === "active").length,
314
+ maxActiveStages: parsed.maxActiveStages,
315
+ retreatCount: parsed.retreatCount,
316
+ maxRetreats: parsed.maxRetreats,
317
+ diagnostics: [...parsed.diagnostics],
318
+ stages: buildStageRows(parsed, planStatuses),
319
+ };
320
+ })
321
+ .filter((block) => block !== undefined);
322
+ }
323
+ const activeRecord = blocks?.filter((block) => block.status === "draft" || block.status === "running").at(-1);
324
+ const activeWorkflowId = activeRecord?.workflowId ?? blocks?.at(-1)?.workflowId;
325
+ return {
326
+ panorama: blocks,
327
+ transitions: transitions.slice(-transitionLimit).reverse(),
328
+ activeWorkflowId,
329
+ };
330
+ }
331
+ /** Action text injected into the conversation input, e.g. `approve stage s2-review`. */
332
+ export function buildWorkflowActionText(action, workflowId, stageId) {
333
+ if (action === "cancel" || stageId === undefined)
334
+ return `cancel workflow ${workflowId}`;
335
+ return `${action} stage ${stageId}`;
336
+ }
337
+ function stageStatusColor(status) {
338
+ switch (status) {
339
+ case "active":
340
+ return (text) => theme.fg("accent", text);
341
+ case "awaiting_review":
342
+ return (text) => theme.fg("warning", text);
343
+ case "completed":
344
+ return (text) => theme.fg("success", text);
345
+ case "failed":
346
+ return (text) => theme.fg("error", text);
347
+ case "skipped":
348
+ return (text) => theme.fg("dim", text);
349
+ case "pending":
350
+ return (text) => theme.fg("muted", text);
351
+ }
352
+ }
353
+ function workflowStatusColor(status) {
354
+ if (status === "running")
355
+ return (text) => theme.fg("accent", text);
356
+ if (status === "completed")
357
+ return (text) => theme.fg("success", text);
358
+ if (status === "failed")
359
+ return (text) => theme.fg("error", text);
360
+ return (text) => theme.fg("dim", text);
361
+ }
362
+ function transitionCodeColor(code) {
363
+ if (code.includes("failed") || code.includes("retreat") || code.includes("error") || code.includes("limit")) {
364
+ return (text) => theme.fg("error", text);
365
+ }
366
+ if (code.includes("held") || code.includes("retry") || code.includes("amended")) {
367
+ return (text) => theme.fg("warning", text);
368
+ }
369
+ return (text) => theme.fg("muted", text);
370
+ }
371
+ /** Renders the visible row window; reads live state from the owning component. */
372
+ class WorkflowRowsView {
373
+ view;
374
+ constructor(view) {
375
+ this.view = view;
376
+ }
377
+ invalidate() { }
378
+ render(width) {
379
+ return this.view.renderRows(width);
380
+ }
381
+ }
382
+ export class WorkflowViewComponent extends Container {
383
+ onClose;
384
+ onForwardAction;
385
+ config;
386
+ mode = "panorama";
387
+ rows = [];
388
+ selectedIndex = 0;
389
+ offset = 0;
390
+ titleText;
391
+ modeHintText;
392
+ helpText;
393
+ flashText;
394
+ cache;
395
+ flashTimeout = null;
396
+ constructor(config) {
397
+ super();
398
+ this.config = config;
399
+ this.titleText = new Text(theme.fg("accent", theme.bold(" Workflow Overview")), 1, 0);
400
+ this.modeHintText = new Text("", 1, 0);
401
+ this.helpText = new Text("", 1, 0);
402
+ this.flashText = new Text("", 1, 0);
403
+ this.updateModeTexts();
404
+ this.addChild(new DynamicBorder());
405
+ this.addChild(this.titleText);
406
+ this.addChild(this.modeHintText);
407
+ this.addChild(new WorkflowRowsView(this));
408
+ this.addChild(this.flashText);
409
+ this.addChild(this.helpText);
410
+ this.addChild(new DynamicBorder());
411
+ }
412
+ dispose() {
413
+ if (this.flashTimeout !== null) {
414
+ clearTimeout(this.flashTimeout);
415
+ this.flashTimeout = null;
416
+ }
417
+ }
418
+ updateModeTexts() {
419
+ const label = this.mode === "panorama" ? "panorama" : "transition log (newest first)";
420
+ this.modeHintText.setText(theme.fg("dim", ` ${label}`));
421
+ this.helpText.setText(this.buildHelpLine());
422
+ }
423
+ buildHelpLine() {
424
+ const parts = [rawKeyHint("↑↓", "move")];
425
+ if (this.mode === "panorama") {
426
+ parts.push(rawKeyHint("tab/l", "log"), rawKeyHint("a", "approve"), rawKeyHint("r", "reject"), rawKeyHint("s", "skip"), rawKeyHint("c", "cancel"));
427
+ }
428
+ else {
429
+ parts.push(rawKeyHint("tab/l", "panorama"));
430
+ }
431
+ parts.push(keyHint("tui.select.cancel", "close"));
432
+ return ` ${parts.join(" ")}`;
433
+ }
434
+ getModel() {
435
+ const entries = this.config.getEntries();
436
+ if (this.cache && this.cache.entries === entries && this.cache.length === entries.length) {
437
+ return this.cache.model;
438
+ }
439
+ const model = buildWorkflowViewModel(entries);
440
+ this.cache = { entries, length: entries.length, model };
441
+ return model;
442
+ }
443
+ buildRows(model) {
444
+ const rows = [];
445
+ if (this.mode === "log") {
446
+ if (model.transitions.length === 0) {
447
+ rows.push({ text: " no workflow transitions recorded", stage: undefined });
448
+ return rows;
449
+ }
450
+ for (const transition of model.transitions) {
451
+ const stage = transition.stageId !== undefined ? `/${transition.stageId}` : "";
452
+ const detail = transition.detail !== undefined ? ` — ${transition.detail}` : "";
453
+ const code = transitionCodeColor(transition.code)(transition.code);
454
+ rows.push({
455
+ text: ` #${transition.revision} ${transition.workflowId}${stage} ${transition.from} -> ${transition.to} [${transition.trigger}] ${code}: ${transition.message}${detail}`,
456
+ stage: undefined,
457
+ });
458
+ }
459
+ return rows;
460
+ }
461
+ const blocks = model.panorama;
462
+ if (blocks === undefined) {
463
+ rows.push({ text: " no workflow.state entry in this session", stage: undefined });
464
+ return rows;
465
+ }
466
+ if (blocks.length === 0) {
467
+ rows.push({ text: " no workflows in this session; create one with workflow_set", stage: undefined });
468
+ return rows;
469
+ }
470
+ for (const block of blocks) {
471
+ const status = workflowStatusColor(block.status)(block.status);
472
+ rows.push({
473
+ text: ` workflow "${block.title}" (${block.workflowId}) — ${status} · revision ${block.revision} · active ${block.activeCount}/${block.maxActiveStages} · retreats ${block.retreatCount}/${block.maxRetreats}`,
474
+ stage: undefined,
475
+ });
476
+ if (block.goalId !== undefined) {
477
+ rows.push({ text: ` goal: ${block.goalId}`, stage: undefined });
478
+ }
479
+ for (const stage of block.stages) {
480
+ rows.push({
481
+ text: this.formatStageRow(stage),
482
+ stage: { workflowId: block.workflowId, stageId: stage.stageId },
483
+ });
484
+ }
485
+ if (block.diagnostics.length > 0) {
486
+ rows.push({ text: " diagnostics:", stage: undefined });
487
+ for (const diagnostic of block.diagnostics) {
488
+ rows.push({ text: theme.fg("warning", ` - ${diagnostic}`), stage: undefined });
489
+ }
490
+ }
491
+ rows.push({ text: "", stage: undefined });
492
+ }
493
+ return rows;
494
+ }
495
+ formatStageRow(stage) {
496
+ const pad = " ".repeat(stage.depth + 1);
497
+ if (stage.backReference) {
498
+ return `${pad} -> ${stage.stageId} (shown above)`;
499
+ }
500
+ const bits = [`- ${stage.stageId} [${stageStatusColor(stage.status)(stage.status)}] ${stage.title}`];
501
+ if (stage.waitingOn !== null)
502
+ bits.push(theme.fg("dim", `— waiting: ${stage.waitingOn}`));
503
+ if (stage.attempts > 0)
504
+ bits.push(theme.fg("dim", `· attempts ${stage.attempts}`));
505
+ if (stage.evidenceCount > 0)
506
+ bits.push(theme.fg("dim", `· evidence ${stage.evidenceCount}`));
507
+ if (stage.scopeConflict !== null) {
508
+ bits.push(theme.fg("warning", `⚠ scope:${stage.scopeConflict}`));
509
+ }
510
+ return `${pad}${bits.join(" ")}`;
511
+ }
512
+ /** Rows are rebuilt lazily on access; the key couples rebuild + selection restore. */
513
+ syncRows() {
514
+ const model = this.getModel();
515
+ const previous = this.rows[this.selectedIndex]?.stage;
516
+ this.rows = this.buildRows(model);
517
+ if (previous !== undefined) {
518
+ const restored = this.rows.findIndex((row) => row.stage?.stageId === previous.stageId);
519
+ if (restored !== -1)
520
+ this.selectedIndex = restored;
521
+ }
522
+ if (this.selectedIndex >= this.rows.length)
523
+ this.selectedIndex = Math.max(0, this.rows.length - 1);
524
+ this.clampOffset();
525
+ }
526
+ clampOffset() {
527
+ const maxRows = Math.max(1, this.config.maxRows);
528
+ if (this.selectedIndex < this.offset)
529
+ this.offset = this.selectedIndex;
530
+ if (this.selectedIndex >= this.offset + maxRows)
531
+ this.offset = this.selectedIndex - maxRows + 1;
532
+ if (this.offset < 0)
533
+ this.offset = 0;
534
+ }
535
+ renderRows(width) {
536
+ this.syncRows();
537
+ const maxRows = Math.max(1, this.config.maxRows);
538
+ const lines = [];
539
+ for (let index = this.offset; index < Math.min(this.rows.length, this.offset + maxRows); index++) {
540
+ const row = this.rows[index];
541
+ if (row === undefined)
542
+ continue;
543
+ const selected = index === this.selectedIndex;
544
+ const prefix = selected ? theme.fg("accent", "→ ") : " ";
545
+ lines.push(truncateToWidth(`${prefix}${row.text}`, width));
546
+ }
547
+ const hiddenAbove = this.offset;
548
+ const hiddenBelow = this.rows.length - this.offset - maxRows;
549
+ const hidden = [];
550
+ if (hiddenAbove > 0)
551
+ hidden.push(`↑${hiddenAbove}`);
552
+ if (hiddenBelow > 0)
553
+ hidden.push(`↓${hiddenBelow}`);
554
+ // Fixed output height (exactly maxRows content rows plus this final
555
+ // indicator row), the same contract as agents-view renderRows: the
556
+ // overlay's grid footprint must not change with mode switches or row
557
+ // counts, or the chat/editor layout boundary moves between frames.
558
+ while (lines.length < maxRows)
559
+ lines.push(truncateToWidth(" ", width));
560
+ lines.push(truncateToWidth(hidden.length > 0 ? theme.fg("dim", ` ${hidden.join(" ")}`) : " ", width));
561
+ return lines;
562
+ }
563
+ move(delta) {
564
+ if (this.rows.length === 0)
565
+ return;
566
+ this.selectedIndex = Math.min(this.rows.length - 1, Math.max(0, this.selectedIndex + delta));
567
+ this.clampOffset();
568
+ }
569
+ flash(message) {
570
+ this.flashText.setText(theme.fg("warning", ` ${message}`));
571
+ if (this.flashTimeout !== null)
572
+ clearTimeout(this.flashTimeout);
573
+ this.flashTimeout = setTimeout(() => {
574
+ this.flashText.setText("");
575
+ this.flashTimeout = null;
576
+ this.config.requestRender?.();
577
+ }, 2500);
578
+ this.config.requestRender?.();
579
+ }
580
+ toggleMode() {
581
+ this.mode = this.mode === "panorama" ? "log" : "panorama";
582
+ this.selectedIndex = 0;
583
+ this.offset = 0;
584
+ this.updateModeTexts();
585
+ this.config.requestRender?.();
586
+ }
587
+ forward(action) {
588
+ const selected = this.rows[this.selectedIndex];
589
+ if (selected?.stage === undefined) {
590
+ if (action === "cancel") {
591
+ const workflowId = this.getModel().activeWorkflowId;
592
+ if (workflowId === undefined) {
593
+ this.flash("No workflow to cancel");
594
+ return;
595
+ }
596
+ this.onForwardAction?.(buildWorkflowActionText(action, workflowId, undefined));
597
+ return;
598
+ }
599
+ this.flash("Select a stage row first");
600
+ return;
601
+ }
602
+ this.onForwardAction?.(buildWorkflowActionText(action, selected.stage.workflowId, selected.stage.stageId));
603
+ }
604
+ handleInput(keyData) {
605
+ const kb = getKeybindings();
606
+ if (kb.matches(keyData, "tui.select.cancel")) {
607
+ this.onClose?.();
608
+ }
609
+ else if (kb.matches(keyData, "tui.input.tab") || keyData === "l") {
610
+ this.toggleMode();
611
+ }
612
+ else if (kb.matches(keyData, "tui.select.up") || keyData === "k") {
613
+ this.move(-1);
614
+ }
615
+ else if (kb.matches(keyData, "tui.select.down") || keyData === "j") {
616
+ this.move(1);
617
+ }
618
+ else if (kb.matches(keyData, "tui.select.pageUp")) {
619
+ this.move(-Math.max(1, this.config.maxRows));
620
+ }
621
+ else if (kb.matches(keyData, "tui.select.pageDown")) {
622
+ this.move(Math.max(1, this.config.maxRows));
623
+ }
624
+ else if (keyData === "a") {
625
+ this.forward("approve");
626
+ }
627
+ else if (keyData === "r") {
628
+ this.forward("reject");
629
+ }
630
+ else if (keyData === "s") {
631
+ this.forward("skip");
632
+ }
633
+ else if (keyData === "c") {
634
+ this.forward("cancel");
635
+ }
636
+ }
637
+ }
638
+ //# sourceMappingURL=workflow-view.js.map