openfox 2.0.112 → 2.0.113

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 (85) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +1 -0
  3. package/dist/CHANGELOG.md +27 -0
  4. package/dist/{chat-handler-BYXZIIV3.js → chat-handler-PEK5JDHO.js} +24 -24
  5. package/dist/{chunk-3UILDGOO.js → chunk-3R43CMLS.js} +8 -8
  6. package/dist/{chunk-ERYUSG5C.js → chunk-43M7QZMX.js} +2 -2
  7. package/dist/{chunk-SYJMUIRC.js → chunk-4NMGX5F2.js} +2 -2
  8. package/dist/{chunk-RAKPI4OE.js → chunk-5VYSKVZT.js} +28 -25
  9. package/dist/{chunk-FFPCVN5M.js → chunk-6UPBZ2ZR.js} +4 -4
  10. package/dist/{chunk-C5NQF2K4.js → chunk-AZFZE36I.js} +15 -6
  11. package/dist/chunk-B2PHPBFU.js +7 -0
  12. package/dist/{chunk-P45RJGOT.js → chunk-BKCW3JLL.js} +1 -1
  13. package/dist/{chunk-N4RTAGFY.js → chunk-BNFGDTEU.js} +2 -2
  14. package/dist/{chunk-JNHKKUGJ.js → chunk-BYSUSSSC.js} +2 -2
  15. package/dist/{chunk-NB2D6I64.js → chunk-CP44U7BT.js} +5 -5
  16. package/dist/{chunk-SVJFMZ2Q.js → chunk-EC5T5A6O.js} +11 -4
  17. package/dist/{chunk-RKMP74BB.js → chunk-EMI3YYLP.js} +3 -3
  18. package/dist/{chunk-BOSKQI62.js → chunk-FF2EKUQO.js} +4 -4
  19. package/dist/{chunk-XCK5IBO4.js → chunk-HGOTZWON.js} +41 -19
  20. package/dist/{chunk-XWNCKIPO.js → chunk-HKD4IMOE.js} +2 -2
  21. package/dist/{chunk-C372UH7N.js → chunk-KSXCOHHW.js} +18 -20
  22. package/dist/{chunk-Z56SEG4Z.js → chunk-O2YSYW2F.js} +6 -6
  23. package/dist/{chunk-UAF2FOI5.js → chunk-ONONXV3N.js} +7 -7
  24. package/dist/{chunk-LCQ5AN6Y.js → chunk-PWXBIUEY.js} +5 -1
  25. package/dist/{chunk-33Q7LCVD.js → chunk-R2MTWC7C.js} +2 -2
  26. package/dist/{chunk-JDT63SSP.js → chunk-SNLLOXCM.js} +3 -3
  27. package/dist/{chunk-FSYRANYR.js → chunk-TCGWZBTS.js} +3 -4
  28. package/dist/{chunk-IYTDCPEQ.js → chunk-TEQEDOWI.js} +206 -143
  29. package/dist/{chunk-PCC4DBPF.js → chunk-V4664YKR.js} +2 -2
  30. package/dist/{chunk-FR5LUTLQ.js → chunk-VGRVPYZ5.js} +10 -6
  31. package/dist/{chunk-RT2A2RRL.js → chunk-WWTYWLDM.js} +2 -2
  32. package/dist/{chunk-6FPGJR7O.js → chunk-XQR76MQ5.js} +2 -2
  33. package/dist/{chunk-RISQXU32.js → chunk-Y64PEWKP.js} +2 -2
  34. package/dist/{chunk-QGXLZZBS.js → chunk-YB6WYVNY.js} +2 -2
  35. package/dist/{chunk-SDGNV45R.js → chunk-YGPPFVLJ.js} +10 -10
  36. package/dist/cli/dev.js +1 -1
  37. package/dist/cli/index.js +1 -1
  38. package/dist/{client-QNQTX5EA.js → client-3LJGD6KY.js} +5 -5
  39. package/dist/{compactor-X2KZ3XFU.js → compactor-ZBNKANHV.js} +10 -10
  40. package/dist/{config-TW5FHQEM.js → config-EAB3V4QK.js} +2 -2
  41. package/dist/{dynamic-context-ZRQO2FJN.js → dynamic-context-F2XTQJR5.js} +9 -9
  42. package/dist/{events-Y6XXQYFG.js → events-224IO5NJ.js} +7 -7
  43. package/dist/{folding-Z4GNWRC3.js → folding-YTTTVS56.js} +5 -5
  44. package/dist/http-client-YC3UA5OZ.js +11 -0
  45. package/dist/{inspect-proxy-N42Z5TOG.js → inspect-proxy-B7LGGTM7.js} +4 -4
  46. package/dist/{manager-HCMDFEQH.js → manager-4QGXI6WZ.js} +5 -5
  47. package/dist/{model-overrides-O3ZYKFP7.js → model-overrides-OY2YKUNV.js} +4 -4
  48. package/dist/{orchestrator-P5EUMC7E.js → orchestrator-JKBY7B3P.js} +23 -23
  49. package/dist/package.json +5 -4
  50. package/dist/{path-security-525YLND3.js → path-security-PFMZY7IA.js} +11 -11
  51. package/dist/{platform-MEQUWG6U.js → platform-QNGCKX4L.js} +4 -4
  52. package/dist/{processor-UN4H4V7F.js → processor-NYPJ5PFA.js} +23 -23
  53. package/dist/{project-creator-BVA55HY6.js → project-creator-AWP2JUFS.js} +3 -3
  54. package/dist/{projects-PQXME5S4.js → projects-C4353IQK.js} +3 -3
  55. package/dist/{protocol-D6MISQyz.d.ts → protocol-CBT1d98i.d.ts} +3 -1
  56. package/dist/{protocol-FTHAR2YL.js → protocol-LZZ7C4VQ.js} +3 -3
  57. package/dist/provider/index.d.ts +4 -4
  58. package/dist/{provider-3R4X62LW.js → provider-PIRPN2CC.js} +8 -8
  59. package/dist/{provider-manager-W5N4XRE5.js → provider-manager-GNEPR6UN.js} +6 -6
  60. package/dist/{registry-74KPZ5NV.js → registry-VCKDSVKA.js} +4 -4
  61. package/dist/{serve-WHFUNQ6Z.js → serve-6DRHQLCG.js} +31 -31
  62. package/dist/server/index.d.ts +7 -4
  63. package/dist/server/index.js +30 -30
  64. package/dist/server-HUHFJW7U.js +48 -0
  65. package/dist/{session-overrides-XH6I3COP.js → session-overrides-RNKV35MU.js} +6 -6
  66. package/dist/{sessions-D772YTWL.js → sessions-D5CEPQGU.js} +7 -5
  67. package/dist/{settings-KO6DSJLD.js → settings-BNRNX6IW.js} +3 -3
  68. package/dist/shared/index.d.ts +3 -3
  69. package/dist/shared/index.js +1 -1
  70. package/dist/skill-defaults/workflows/SKILL.md +499 -0
  71. package/dist/{tools-HTUTYMKI.js → tools-2B363F7L.js} +21 -21
  72. package/dist/{types-BMEOlLT0.d.ts → types-BAxh20ZL.d.ts} +8 -1
  73. package/dist/{types-D0TgGL3t.d.ts → types-cVfqOj6M.d.ts} +1 -1
  74. package/dist/{update-BRUFGGTW.js → update-IQMRYN7J.js} +2 -2
  75. package/dist/web/assets/{index-nS1p0g8O.css → index-CjWC6EQ4.css} +1 -1
  76. package/dist/web/assets/index-DBUtUSHK.js +323 -0
  77. package/dist/web/index.html +2 -2
  78. package/dist/web/sw.js +1 -1
  79. package/dist/workflow-defaults/default.workflow.json +41 -3
  80. package/dist/{workspace-YDUQZ3HC.js → workspace-YUNHV4WS.js} +4 -4
  81. package/package.json +5 -4
  82. package/dist/chunk-P5AA7OYJ.js +0 -7
  83. package/dist/http-client-2GDKHZIG.js +0 -11
  84. package/dist/server-7MOOJR2N.js +0 -48
  85. package/dist/web/assets/index-Dukvfqm-.js +0 -323
@@ -0,0 +1,499 @@
1
+ ---
2
+ name: workflows
3
+ description: 'Author and manage OpenFox workflow files (.workflow.json): step types, transition conditions, template variables, and storage locations.'
4
+ metadata:
5
+ version: 1.0.0
6
+ openfox:
7
+ displayName: Workflows
8
+ ---
9
+
10
+ # OpenFox Workflows — Authoring Reference
11
+
12
+ Authoritative reference for creating and editing OpenFox workflow files (`.workflow.json`).
13
+ Executor implementation: `src/server/workflows/` (`types.ts`, `executor.ts`, `registry.ts`)
14
+ and `src/server/routes/workflows.ts`.
15
+
16
+ A workflow is a declarative **state machine**: a sequence of steps (agent turns, sub-agent
17
+ calls, shell commands, user pauses) wired together by **transitions** with **conditions**.
18
+ The executor walks the graph until it reaches a terminal state (`$done` or `$blocked`).
19
+
20
+ When a user asks you to create or edit a workflow, follow this document.
21
+
22
+ ---
23
+
24
+ ## 1. Storage Locations & Precedence
25
+
26
+ Workflows are plain JSON files with extension `.workflow.json` (never markdown). Three
27
+ tiers, merged **by `metadata.id`** with later tiers overriding earlier ones:
28
+
29
+ | Tier | Location | Notes |
30
+ | ----------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
31
+ | **Default** | `src/server/workflows/defaults/{id}.workflow.json` (bundled → `dist/workflow-defaults/`) | Ships with the product. Not editable, not deletable. |
32
+ | **User** | `{configDir}/workflows/{id}.workflow.json` | Per-user, machine-local. |
33
+ | **Project** | `{projectDir}/.openfox/workflows/{id}.workflow.json` | Committed to the repo, shared with the team. **Recommended for agent-authored workflows.** |
34
+
35
+ **Precedence (highest wins):** `project > user > default`. A project workflow with the same
36
+ `metadata.id` as a bundled default replaces it everywhere.
37
+
38
+ **Config dirs:** production `~/.config/openfox/`, development `~/.config/openfox-dev/`
39
+ (other platforms: `XDG_CONFIG_HOME`/`~/.config` on Linux, `~/Library/Application Support`
40
+ on macOS, `%APPDATA%` on Windows).
41
+
42
+ **Filename convention:** `{id}.workflow.json` — the loader keys on the embedded
43
+ `metadata.id` (any `*.workflow.json` is read), but every writer uses the ID as the
44
+ filename, so keep them in sync: one workflow per file. `id` is a lowercase slug
45
+ `[a-z0-9-]` (e.g. `build-test-fix`).
46
+
47
+ **Minimum validity** (files failing this are **silently skipped** with a warning — no hard
48
+ error at startup): `metadata.id` is set **and** `steps` is a non-empty array. Malformed
49
+ JSON is also skipped.
50
+
51
+ **API surface:** CRUD routes at `/api/workflows` (`src/server/routes/workflows.ts`) — list,
52
+ get/create/update/delete, duplicate, template-variables. Creating via file is preferred for
53
+ agent-authored workflows because the file is reviewable and committable.
54
+
55
+ ---
56
+
57
+ ## 2. Overall Shape
58
+
59
+ ```jsonc
60
+ {
61
+ "metadata": {
62
+ "id": "build-test-fix",
63
+ "name": "Build → Test → Fix",
64
+ "description": "Loop: build, run tests, fix failures.",
65
+ "version": "1.0.0",
66
+ "color": "#3b82f6", // optional, UI accent
67
+ "parameters": [
68
+ // optional, prompted at launch
69
+ {
70
+ "id": "feature",
71
+ "label": "Feature name",
72
+ "description": "What to implement",
73
+ "position": 0, // optional, ordering
74
+ "required": true, // optional, default false
75
+ },
76
+ ],
77
+ },
78
+ "entryStep": "build", // ID of the first step to execute
79
+ "settings": {
80
+ "maxIterations": 50, // safety cap on state-machine iterations
81
+ },
82
+ "steps": [/* see §3 */],
83
+ "startCondition": { "type": "always" }, // optional, gates workflow start
84
+ }
85
+ ```
86
+
87
+ ### Field reference
88
+
89
+ | Field | Type | Required | Description |
90
+ | ------------------------ | --------------------- | -------- | -------------------------------------------------------------------------------------------- |
91
+ | `metadata.id` | string | yes | Unique slug; filename stem. Lowercase `[a-z0-9-]`. |
92
+ | `metadata.name` | string | yes | Human-readable display name. |
93
+ | `metadata.description` | string | yes | What the workflow does. |
94
+ | `metadata.version` | string | yes | Semver-ish version string. |
95
+ | `metadata.color` | string | no | Hex color for UI badges. |
96
+ | `metadata.parameters` | `WorkflowParameter[]` | no | User inputs requested at launch (see §6). |
97
+ | `entryStep` | string | yes | ID of the step the workflow starts at. |
98
+ | `settings.maxIterations` | number | yes | Hard cap on executor loop iterations. Exceeding it ⇒ `BLOCKED`. Default in the editor is 50. |
99
+ | `steps` | `WorkflowStep[]` | yes | Non-empty. Order is display-only — execution follows transitions. |
100
+ | `startCondition` | `TransitionCondition` | no | Gates workflow start on session metadata (default: `always`). |
101
+
102
+ ---
103
+
104
+ ## 3. Steps
105
+
106
+ Every step shares these base fields:
107
+
108
+ | Field | Type | Required | Description |
109
+ | ------------- | -------------- | -------- | -------------------------------------------------------------------------------------------------- |
110
+ | `id` | string | yes | Unique within the workflow. Referenced by `entryStep`, `goto`, `{{stepOutput.id}}`. |
111
+ | `name` | string | yes | Display name. |
112
+ | `phase` | string | yes | Maps to the session phase for UI: `"build"`, `"verification"`, `"waiting"`, `"blocked"`, `"done"`. |
113
+ | `transitions` | `Transition[]` | yes | Evaluated **in order, first match wins**. See §4. |
114
+ | `subGroup` | string | no | Groups steps for running a subset in isolation. See §7. |
115
+
116
+ ### 3.1 `agent` — full LLM turn with tools
117
+
118
+ ```jsonc
119
+ {
120
+ "id": "implement",
121
+ "name": "Implement",
122
+ "type": "agent",
123
+ "phase": "build",
124
+ "agentId": "builder", // optional, default: resolved default agent (usually "planner")
125
+ "prompt": "Implement {{criteriaCount}} criteria…",
126
+ "nudgePrompt": "Keep going. {{reason}} …", // optional, injected on re-entry
127
+ "transitions": [/* … */],
128
+ }
129
+ ```
130
+
131
+ - Runs a full agent turn (LLM + tool loop) with the agent's tool registry.
132
+ - `agentId` defaults to the resolved default agent: DB setting → global config →
133
+ `OPENFOX_DEFAULT_AGENT` env → `"planner"`. Common values: `"builder"`, `"planner"`.
134
+ - `prompt` is injected as a user message **on first entry**, with
135
+ `"\n\nOnce you're done, call step_done()"` appended. Supports template variables (§6).
136
+ - **Advance rule:** the step only advances after the agent calls **`step_done()`**
137
+ successfully. If it finishes without `step_done()`, the executor **loops back to the
138
+ same step** and injects a nudge (`nudgePrompt` if present, plus a `step_done()` reminder).
139
+ If no `prompt` is set and it's the first entry, a generic kickoff
140
+ ("Proceed with the current step.") is injected.
141
+ - **Result:** if the agent uses `return_value` (with `result` and/or `content`), those
142
+ become the step's `result` and `stepOutput`; otherwise the result defaults to
143
+ `"completed"`. `stepOutput.stepDoneCalled` is `"true"`/`"false"`.
144
+
145
+ ### 3.2 `sub_agent` — isolated sub-agent with fresh context
146
+
147
+ ```jsonc
148
+ {
149
+ "id": "verify",
150
+ "name": "Verifier",
151
+ "type": "sub_agent",
152
+ "phase": "verification",
153
+ "subAgentType": "verifier", // required — any configured sub-agent type
154
+ "prompt": "## Criteria\n{{criteriaList}} …",
155
+ "nudgePrompt": "…", // declared in the schema
156
+ "transitions": [/* … */],
157
+ }
158
+ ```
159
+
160
+ - Runs one isolated sub-agent turn (fresh context). The `step_done` tool is **removed**
161
+ from sub-agents.
162
+ - `prompt` defaults to `"Perform your task."` if omitted.
163
+ - Unknown `subAgentType` ⇒ the step resolves with `result: "error"`.
164
+ - **Result:** the sub-agent's `return_value` `result`, or `"success"` if none. Content
165
+ lands in `{{stepOutput.content}}`.
166
+
167
+ ### 3.3 `shell` — run a command, branch on exit code
168
+
169
+ ```jsonc
170
+ {
171
+ "id": "lint",
172
+ "name": "Lint",
173
+ "type": "shell",
174
+ "phase": "verification",
175
+ "command": "npm run lint",
176
+ "timeout": 90000, // optional, ms, default 60000
177
+ "successExitCodes": [0], // optional, default [0]
178
+ "transitions": [/* … */],
179
+ }
180
+ ```
181
+
182
+ - `command` runs in the session workdir and supports template variables (§6).
183
+ - **Result:** `"success"` if the exit code is in `successExitCodes`, else `"failure"`.
184
+ - `stepOutput`: `stdout`, `stderr`, `exitCode` (string).
185
+ - The command and its output (truncated to 10k chars) are echoed into the chat as system
186
+ messages.
187
+
188
+ ### 3.4 `user` — pause for a human decision
189
+
190
+ ```jsonc
191
+ {
192
+ "id": "approve",
193
+ "name": "Approve Fix Plan",
194
+ "type": "user",
195
+ "phase": "verification",
196
+ "transitions": [
197
+ { "when": { "type": "step_result", "result": "apply" }, "goto": "apply_fixes" },
198
+ { "when": { "type": "step_result", "result": "skip" }, "goto": "start_dev_server" },
199
+ { "when": { "type": "always" }, "goto": "apply_fixes" },
200
+ ],
201
+ }
202
+ ```
203
+
204
+ - Pauses the workflow and presents **buttons derived from the transitions**:
205
+ - each `step_result` transition ⇒ one choice button (its `result` string is both the id
206
+ and the label);
207
+ - an `always` transition ⇒ a `"Continue"` button (`id: "continue"`).
208
+ - On resume, the picked choice becomes the step's `result`; the chosen button's `goto`
209
+ drives the next transition. Selecting "Continue" (or resuming without an explicit choice)
210
+ yields the reserved result `"continue"`, which matches an `always` transition.
211
+ - Use this for approvals, plan sign-off, manual QA gates, etc. Only `step_result` and
212
+ `always` transitions matter for choices; other conditions are ignored when deriving
213
+ buttons.
214
+
215
+ ---
216
+
217
+ ## 4. Transitions & Conditions
218
+
219
+ Each step carries an ordered `transitions` array. The executor evaluates them **in order
220
+ and takes the first whose `when` matches**. If none matches, the workflow goes `$blocked`
221
+ ("Runner blocked: No matching transition").
222
+
223
+ ```jsonc
224
+ { "when": { /* condition */ }, "goto": "next_step_id" | "$done" | "$blocked", "subGroup": "optional" }
225
+ ```
226
+
227
+ - `goto` is a step `id` or one of the terminal states:
228
+ - `"$done"` — workflow completes (session phase `done`, stats recorded).
229
+ - `"$blocked"` — workflow stops blocked.
230
+ - `subGroup` on a transition is only meaningful when running that sub-group (§7).
231
+
232
+ ### Conditions (all four)
233
+
234
+ | Condition | Matches when |
235
+ | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
236
+ | `{ "type": "always" }` | Always. Use as the final fallback to avoid dead ends. |
237
+ | `{ "type": "step_result", "result": "x" }` | The current step returned result exactly `"x"` (from `return_value`, shell exit classification, or a user choice). |
238
+ | `{ "type": "metadata_all_match", "key": "criteria", "field": "status", "value": "passed" }` | **Every** session-metadata entry under `key` has `entry[field] === value`. |
239
+ | `{ "type": "metadata_all_in", "key": "criteria", "field": "status", "values": ["completed","passed"] }` | Every entry's `field` is **one of** `values`. |
240
+
241
+ Notes on metadata conditions:
242
+
243
+ - Operate on the session's metadata entries (managed via the `session_metadata` tool),
244
+ keyed by name — common keys: `criteria` (fields `status`, `description`, …) and
245
+ `review_findings` (field `status`: `open`/`resolved`/`dismissed`).
246
+ - **Empty entry list ⇒ condition is `true`** (vacuous truth). If there are no criteria at
247
+ all, a `metadata_all_match` on `criteria` passes.
248
+ - Evaluated with the **latest** session state after the step executes.
249
+
250
+ Example — build → test → fix loop:
251
+
252
+ ```
253
+ build ──(metadata_all_in status completed|passed)──▶ test
254
+ ▲ │
255
+ └───────────────(always fallback)─────────────────────┘
256
+ test ──(step_result "passed")────────────────────────▶ $done
257
+ test ──(step_result "failed")────────────────────────▶ fix
258
+ fix ──(always)──────────────────────────────────────▶ test
259
+ ```
260
+
261
+ ---
262
+
263
+ ## 5. Runtime Semantics (authoring-critical)
264
+
265
+ - **`step_done()` is mandatory for agent steps.** Without it, the step loops back on itself
266
+ with nudges. Write prompts that explicitly end with "call `step_done()`".
267
+ - **Results come from `return_value`** (`result`, `content`). Branch on `step_result`
268
+ conditions. Shell steps produce `success`/`failure` from exit codes; sub-agents default
269
+ to `success`; agent steps default to `completed`.
270
+ - **`maxIterations` caps the whole workflow**, not individual steps. Tight loops +
271
+ `always` self-transitions can burn it fast. Escape loops with `step_result` /
272
+ `metadata_*` conditions.
273
+ - **Blocking:** no matching transition ⇒ `$blocked`. `startCondition` unmet ⇒ blocked
274
+ before the first step. Hitting `maxIterations` ⇒ blocked.
275
+ - **Abort/resume:** aborting mid-workflow keeps the execution record alive; sending a new
276
+ message resumes from the current step.
277
+ - **Phases:** `step.phase` drives the session-phase UI (`build`, `verification`, …) and is
278
+ set as each step runs.
279
+ - **Session mode:** agent steps set the session mode to their `agentId`.
280
+
281
+ ---
282
+
283
+ ## 6. Template Variables (prompts, nudges, shell commands)
284
+
285
+ The named variables below are the canonical list (the API exposes them via
286
+ `GET /api/workflows/template-variables`); `{{stepOutput.<key>}}` resolves generically
287
+ against the previous step's output map:
288
+
289
+ | Variable | Meaning |
290
+ | ------------------------------- | -------------------------------------------------------------------------------------------------------------- |
291
+ | `{{workdir}}` | Session working directory |
292
+ | `{{reason}}` | Human-readable reason (e.g. "N criteria remaining") |
293
+ | `{{criteriaCount}}` | Total number of criteria |
294
+ | `{{pendingCount}}` | Number of pending/failed criteria |
295
+ | `{{criteriaList}}` | Formatted list of all criteria with status (`[PASSED]`, `[NEEDS VERIFICATION]`, `[FAILED]`, `[NOT COMPLETED]`) |
296
+ | `{{modifiedFiles}}` | Git-diff list of files modified this session |
297
+ | `{{stepOutput.content}}` | Text output of the previous step (agent/sub-agent `return_value` content) |
298
+ | `{{stepOutput.result}}` | Result string of the previous step |
299
+ | `{{stepOutput.stdout}}` | Previous **shell** step stdout |
300
+ | `{{stepOutput.stderr}}` | Previous **shell** step stderr |
301
+ | `{{stepOutput.exitCode}}` | Previous **shell** step exit code |
302
+ | `{{stepOutput.stepDoneCalled}}` | Whether the previous agent step called `step_done()` |
303
+ | `{{params}}` / `{{someParam}}` | User-supplied launch parameters (see below) |
304
+
305
+ - `{{stepOutput.<anything>}}` is resolved generically from the previous step's output map;
306
+ unknown keys render empty.
307
+ - `{{stepOutput.*}}` refers to the **immediately preceding** executed step in the run (not
308
+ the step that transitioned to the current one via a loop).
309
+ - **Parameters:** any `metadata.parameters` entry is collected at launch and injected as
310
+ `{{paramId}}` in prompts/nudges/commands. Parameters resolve last and **cannot override**
311
+ the built-in variables above. Example: the `review` workflow prompts the user for
312
+ `pr_number` and uses `{{pr_number}}` throughout.
313
+ - Deprecated aliases: `{{verifierFindings}}` → `{{stepOutput.content}}`,
314
+ `{{previousStepOutput}}` → `{{stepOutput.stdout}}`.
315
+
316
+ ---
317
+
318
+ ## 7. Sub-Groups
319
+
320
+ A `subGroup` string on a step groups related steps so the workflow can be **run in
321
+ isolation as a slice** (e.g. the UI runs just the "code review" group of a larger
322
+ workflow):
323
+
324
+ - Running a sub-group executes **only** steps whose `subGroup` matches, starting at the
325
+ group's first step.
326
+ - Within a group, only transitions with no `subGroup` **or** the same `subGroup` are
327
+ considered.
328
+ - A transition pointing to a step **outside** the active group is treated as `$done` (the
329
+ slice completes).
330
+
331
+ On a full run, `subGroup` is purely organizational (used for display/grouping).
332
+
333
+ ---
334
+
335
+ ## 8. Authoring Checklist (do this every time)
336
+
337
+ 1. **Choose scope.** Project workflows go in `.openfox/workflows/` (commit them — they're
338
+ part of the repo contract). User-global workflows go in `{configDir}/workflows/`.
339
+ 2. **Slug the ID** — lowercase `[a-z0-9-]`, used as filename `{id}.workflow.json` and as
340
+ the override key.
341
+ 3. **Fill `metadata`** completely: `id`, `name`, `description`, `version`; add `color` and
342
+ `parameters` when useful.
343
+ 4. **Design the graph:** pick `entryStep`, size `settings.maxIterations` generously but
344
+ sanely, and sketch steps + transitions on paper first.
345
+ 5. **Give every step** a unique `id`, a readable `name`, and a `phase`.
346
+ 6. **Agent steps:** write a concrete `prompt`, end it with "call `step_done()`", and use
347
+ `return_value` (with distinct `result` strings) wherever downstream steps branch on
348
+ outcome. Add `nudgePrompt` for retries when useful.
349
+ 7. **Transitions:** order them so specific conditions come first, and **always end with an
350
+ `always` fallback** to prevent `$blocked` dead ends.
351
+ 8. **Insert `user` steps** for anything needing a human gate (approvals, test sign-off).
352
+ 9. **Validate:** the file must be valid JSON with `metadata.id` + non-empty `steps`, or the
353
+ loader silently skips it. IDs must match `goto`/`entryStep` exactly.
354
+ 10. **Test:** launch the workflow (UI "Workflows ›" dropdown or `/api/workflows`) and watch
355
+ for `$blocked`/`Max iterations` outcomes; iterate.
356
+
357
+ ---
358
+
359
+ ## 9. Worked Examples
360
+
361
+ ### 9.1 Minimal skeleton — linear chain
362
+
363
+ ```json
364
+ {
365
+ "metadata": {
366
+ "id": "hello-check",
367
+ "name": "Hello Check",
368
+ "description": "Greet, run tests, report.",
369
+ "version": "1.0.0",
370
+ "color": "#22c55e"
371
+ },
372
+ "entryStep": "greet",
373
+ "settings": { "maxIterations": 10 },
374
+ "steps": [
375
+ {
376
+ "id": "greet",
377
+ "name": "Greet",
378
+ "type": "agent",
379
+ "phase": "build",
380
+ "agentId": "builder",
381
+ "prompt": "Say hi in one line. Then call step_done().",
382
+ "transitions": [{ "when": { "type": "always" }, "goto": "run_tests" }]
383
+ },
384
+ {
385
+ "id": "run_tests",
386
+ "name": "Run Tests",
387
+ "type": "shell",
388
+ "phase": "verification",
389
+ "command": "npm run test",
390
+ "transitions": [
391
+ { "when": { "type": "step_result", "result": "success" }, "goto": "report" },
392
+ { "when": { "type": "always" }, "goto": "$blocked" }
393
+ ]
394
+ },
395
+ {
396
+ "id": "report",
397
+ "name": "Report",
398
+ "type": "agent",
399
+ "phase": "verification",
400
+ "agentId": "builder",
401
+ "prompt": "Tests passed. Summarize in two lines, then call step_done().",
402
+ "transitions": [{ "when": { "type": "always" }, "goto": "$done" }]
403
+ }
404
+ ],
405
+ "startCondition": { "type": "always" }
406
+ }
407
+ ```
408
+
409
+ ### 9.2 Rich — loop with metadata gating and a human gate
410
+
411
+ Uses `session_metadata` statuses: builder marks criteria `completed`; verifier flips them
412
+ to `passed`/`failed`; the workflow advances only when all are `passed`, with an `always`
413
+ fallback that loops back to the builder.
414
+
415
+ ```json
416
+ {
417
+ "metadata": {
418
+ "id": "review-loop",
419
+ "name": "Review Loop",
420
+ "description": "Implement, verify, human-approve, finalize.",
421
+ "version": "1.0.0",
422
+ "color": "#a371f7"
423
+ },
424
+ "entryStep": "build",
425
+ "settings": { "maxIterations": 50 },
426
+ "steps": [
427
+ {
428
+ "id": "build",
429
+ "name": "Implement",
430
+ "type": "agent",
431
+ "phase": "build",
432
+ "agentId": "builder",
433
+ "prompt": "Fulfil the {{criteriaCount}} criteria. Update each with session_metadata (status completed). Then call step_done().",
434
+ "transitions": [
435
+ {
436
+ "when": {
437
+ "type": "metadata_all_in",
438
+ "key": "criteria",
439
+ "field": "status",
440
+ "values": ["completed", "passed"]
441
+ },
442
+ "goto": "verify"
443
+ },
444
+ { "when": { "type": "always" }, "goto": "build" }
445
+ ]
446
+ },
447
+ {
448
+ "id": "verify",
449
+ "name": "Verify",
450
+ "type": "sub_agent",
451
+ "phase": "verification",
452
+ "subAgentType": "verifier",
453
+ "prompt": "## Criteria\n{{criteriaList}}\n\nMark each as passed or failed via session_metadata.",
454
+ "transitions": [
455
+ {
456
+ "when": { "type": "metadata_all_match", "key": "criteria", "field": "status", "value": "passed" },
457
+ "goto": "approve"
458
+ },
459
+ { "when": { "type": "always" }, "goto": "build" }
460
+ ]
461
+ },
462
+ {
463
+ "id": "approve",
464
+ "name": "Approve",
465
+ "type": "user",
466
+ "phase": "verification",
467
+ "transitions": [
468
+ { "when": { "type": "step_result", "result": "go" }, "goto": "finalize" },
469
+ { "when": { "type": "step_result", "result": "rework" }, "goto": "build" },
470
+ { "when": { "type": "always" }, "goto": "finalize" }
471
+ ]
472
+ },
473
+ {
474
+ "id": "finalize",
475
+ "name": "Finalize",
476
+ "type": "agent",
477
+ "phase": "verification",
478
+ "agentId": "builder",
479
+ "prompt": "Wrap up with a summary of what changed, then call step_done().",
480
+ "transitions": [{ "when": { "type": "always" }, "goto": "$done" }]
481
+ }
482
+ ],
483
+ "startCondition": { "type": "always" }
484
+ }
485
+ ```
486
+
487
+ ---
488
+
489
+ ## 10. Troubleshooting (authoring mistakes)
490
+
491
+ | Symptom | Likely cause |
492
+ | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
493
+ | Workflow doesn't appear at all | File invalid (missing `metadata.id` or empty `steps`) or malformed JSON — loader skips it. Wrong filename (must be `{id}.workflow.json`). |
494
+ | Stuck looping on one agent step | Agent never calls `step_done()`. Add the instruction to the prompt. |
495
+ | "Runner blocked: No matching transition" | No condition matched. Add an `always` fallback, or fix `step_result` strings / metadata field names to match exactly. |
496
+ | Never leaves a step despite `step_result` | Result string mismatch (case/whitespace) or wrong step's output is being inspected (`stepOutput` is the immediately-preceding step). |
497
+ | Blocked immediately at start | `startCondition` (non-`always`) evaluated false against current session metadata. |
498
+ | "Max iterations (N) reached" | Loop lacks a terminating condition. Widen the escape conditions, not just `maxIterations`. |
499
+ | User step shows unexpected/missing buttons | Choices are derived only from `step_result` and `always` transitions of that step. |
@@ -9,49 +9,49 @@ import {
9
9
  setMcpTools,
10
10
  stepDoneTool,
11
11
  validateToolAction
12
- } from "./chunk-RAKPI4OE.js";
13
- import "./chunk-BOSKQI62.js";
12
+ } from "./chunk-5VYSKVZT.js";
13
+ import "./chunk-FF2EKUQO.js";
14
14
  import "./chunk-JR54MDZI.js";
15
- import "./chunk-XWNCKIPO.js";
16
- import "./chunk-33Q7LCVD.js";
17
- import "./chunk-SVJFMZ2Q.js";
15
+ import "./chunk-HKD4IMOE.js";
16
+ import "./chunk-R2MTWC7C.js";
17
+ import "./chunk-EC5T5A6O.js";
18
18
  import "./chunk-I72ACQ6R.js";
19
19
  import "./chunk-JE7LW7Y6.js";
20
20
  import "./chunk-OAN4BXEW.js";
21
- import "./chunk-FR5LUTLQ.js";
21
+ import "./chunk-VGRVPYZ5.js";
22
22
  import "./chunk-J7GAZGZT.js";
23
- import "./chunk-JDT63SSP.js";
23
+ import "./chunk-SNLLOXCM.js";
24
24
  import {
25
25
  PathAccessDeniedError,
26
26
  cancelPathConfirmationsForSession,
27
27
  getConfirmationSessionId,
28
28
  providePathConfirmation,
29
29
  requestPathAccess
30
- } from "./chunk-FFPCVN5M.js";
31
- import "./chunk-NB2D6I64.js";
32
- import "./chunk-SYJMUIRC.js";
33
- import "./chunk-N4RTAGFY.js";
30
+ } from "./chunk-6UPBZ2ZR.js";
31
+ import "./chunk-CP44U7BT.js";
32
+ import "./chunk-4NMGX5F2.js";
33
+ import "./chunk-BNFGDTEU.js";
34
34
  import {
35
35
  AskUserInterrupt,
36
36
  cancelQuestionsForSession,
37
37
  getPendingQuestionsForSession,
38
38
  provideAnswer
39
39
  } from "./chunk-JURM3RPZ.js";
40
- import "./chunk-RISQXU32.js";
41
- import "./chunk-6FPGJR7O.js";
42
- import "./chunk-QGXLZZBS.js";
40
+ import "./chunk-Y64PEWKP.js";
41
+ import "./chunk-XQR76MQ5.js";
42
+ import "./chunk-YB6WYVNY.js";
43
43
  import "./chunk-LCLH6ZUL.js";
44
- import "./chunk-C5NQF2K4.js";
45
- import "./chunk-JNHKKUGJ.js";
44
+ import "./chunk-AZFZE36I.js";
45
+ import "./chunk-BYSUSSSC.js";
46
46
  import "./chunk-J2GP3J3X.js";
47
47
  import "./chunk-V4IE7HJY.js";
48
48
  import "./chunk-YHEQTVFV.js";
49
49
  import "./chunk-L737Y63F.js";
50
- import "./chunk-RT2A2RRL.js";
51
- import "./chunk-LCQ5AN6Y.js";
50
+ import "./chunk-WWTYWLDM.js";
51
+ import "./chunk-PWXBIUEY.js";
52
52
  import "./chunk-K44MW7JJ.js";
53
- import "./chunk-P45RJGOT.js";
54
- import "./chunk-C372UH7N.js";
53
+ import "./chunk-BKCW3JLL.js";
54
+ import "./chunk-KSXCOHHW.js";
55
55
  import "./chunk-CQGTEGKL.js";
56
56
  import "./chunk-5WRI5ZAA.js";
57
57
  export {
@@ -75,4 +75,4 @@ export {
75
75
  stepDoneTool,
76
76
  validateToolAction
77
77
  };
78
- //# sourceMappingURL=tools-HTUTYMKI.js.map
78
+ //# sourceMappingURL=tools-2B363F7L.js.map
@@ -23,6 +23,10 @@ interface WorkflowParameter {
23
23
  position?: number;
24
24
  required?: boolean;
25
25
  }
26
+ /** Where a workflow definition lives: bundled defaults, global config, or the project's .openfox/. */
27
+ type WorkflowScope = 'builtin' | 'user' | 'project';
28
+ /** Launch scope: an explicit bucket to resolve from, or 'auto' for server precedence (project > user > builtin). */
29
+ type WorkflowLaunchScope = WorkflowScope | 'auto';
26
30
  type WorkflowExecutionStatus = 'running' | 'waiting' | 'completed' | 'cancelled' | 'blocked';
27
31
  /** A selectable branch presented to the user at a paused user step. */
28
32
  interface UserStepChoice {
@@ -32,6 +36,8 @@ interface UserStepChoice {
32
36
  label: string;
33
37
  /** Target step the choice routes to. */
34
38
  goto: string;
39
+ /** Display name of the target step, when it resolves to a step in the workflow. */
40
+ nextStepName?: string;
35
41
  }
36
42
  interface WorkflowExecution {
37
43
  id: string;
@@ -104,6 +110,7 @@ interface SessionSummary {
104
110
  mode: SessionMode;
105
111
  phase: SessionPhase;
106
112
  isRunning: boolean;
113
+ isFavorite: boolean;
107
114
  providerId?: string | null;
108
115
  providerModel?: string | null;
109
116
  createdAt: string;
@@ -591,4 +598,4 @@ interface ElementData {
591
598
  attributes: Record<string, string>;
592
599
  }
593
600
 
594
- export type { Attachment as A, SessionSummary as B, CallStatsDataPoint as C, DangerLevel as D, EditContextEdit as E, FileReadEntry as F, StatsDataPoint as G, StatsIdentity as H, InjectedFile as I, Todo as J, ToolMode as K, LLMCallStats as L, ModelConfig as M, ToolName as N, ToolResult as O, PreparingToolCall as P, WorkflowExecutionStatus as Q, RecentUserPrompt as R, SessionStats as S, ToolCall as T, UserStepChoice as U, ValidationResult as V, WorkflowExecution as W, WorkflowParameter as X, Message as a, Config as b, ContextState as c, ContextWindow as d, Criterion as e, CriterionAttempt as f, CriterionStatus as g, CriterionValidation as h, Diagnostic as i, EditContextLine as j, EditContextRegion as k, ElementData as l, ExecutionState as m, LlmBackend as n, MessageRole as o, MessageSegment as p, MessageStats as q, MetadataEntry as r, ModelSessionStats as s, Project as t, Provider as u, ProviderBackend as v, Session as w, SessionMetadata as x, SessionMode as y, SessionPhase as z };
601
+ export type { Attachment as A, SessionSummary as B, CallStatsDataPoint as C, DangerLevel as D, EditContextEdit as E, FileReadEntry as F, StatsDataPoint as G, StatsIdentity as H, InjectedFile as I, Todo as J, ToolMode as K, LLMCallStats as L, ModelConfig as M, ToolName as N, ToolResult as O, PreparingToolCall as P, WorkflowExecutionStatus as Q, RecentUserPrompt as R, SessionStats as S, ToolCall as T, UserStepChoice as U, ValidationResult as V, WorkflowExecution as W, WorkflowLaunchScope as X, WorkflowParameter as Y, WorkflowScope as Z, Message as a, Config as b, ContextState as c, ContextWindow as d, Criterion as e, CriterionAttempt as f, CriterionStatus as g, CriterionValidation as h, Diagnostic as i, EditContextLine as j, EditContextRegion as k, ElementData as l, ExecutionState as m, LlmBackend as n, MessageRole as o, MessageSegment as p, MessageStats as q, MetadataEntry as r, ModelSessionStats as s, Project as t, Provider as u, ProviderBackend as v, Session as w, SessionMetadata as x, SessionMode as y, SessionPhase as z };
@@ -1,4 +1,4 @@
1
- import { T as ToolCall, A as Attachment } from './types-BMEOlLT0.js';
1
+ import { T as ToolCall, A as Attachment } from './types-BAxh20ZL.js';
2
2
 
3
3
  interface LLMMessage {
4
4
  role: 'system' | 'user' | 'assistant' | 'tool';
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-P5AA7OYJ.js";
3
+ } from "./chunk-B2PHPBFU.js";
4
4
  import "./chunk-5WRI5ZAA.js";
5
5
 
6
6
  // src/cli/update.ts
@@ -46,4 +46,4 @@ async function runUpdate(options = {}) {
46
46
  export {
47
47
  runUpdate
48
48
  };
49
- //# sourceMappingURL=update-BRUFGGTW.js.map
49
+ //# sourceMappingURL=update-IQMRYN7J.js.map