lilflow 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +178 -51
- package/package.json +4 -3
- package/plugin/plugin.json +7 -0
- package/plugin/skills/lilflow-workflow-driver/SKILL.md +110 -0
- package/src/agents/index.js +41 -1
- package/src/agents/output-file.js +204 -0
- package/src/cli.js +17 -9
- package/src/run-workflow.js +202 -1
- package/src/session-bridge.js +644 -0
- package/src/session-prompt.js +59 -0
- package/src/session-runner.js +150 -0
package/README.md
CHANGED
|
@@ -1,112 +1,239 @@
|
|
|
1
|
+
<!-- codeharness:readme -->
|
|
1
2
|
# lilflow
|
|
2
3
|
|
|
3
|
-
Repo-native workflow engine CLI.
|
|
4
|
+
Repo-native workflow engine CLI. Define multi-step workflows in YAML, execute them with step-level persistence, and resume from failure -- all stored under `.flow/` in your repo. No external services, no databases, no daemon.
|
|
4
5
|
|
|
5
|
-
##
|
|
6
|
+
## What it does
|
|
7
|
+
|
|
8
|
+
lilflow turns YAML workflow definitions into executable, resumable pipelines that live inside your repository. It persists every step's start, completion, and failure as append-only JSONL events, so you can resume a failed run from exactly where it broke. It has first-class support for LLM coding agents (Claude Code and OpenCode) as step types, with session management across steps and two execution modes: classic (one agent process per step) and session (the entire workflow runs inside a single long-lived agent session for prompt-cache efficiency). It's for developers who want CI-like orchestration without leaving the repo.
|
|
9
|
+
|
|
10
|
+
## Key features
|
|
11
|
+
|
|
12
|
+
- **Step-level resumability** -- append-only JSONL event logs let you resume from the exact point of failure
|
|
13
|
+
- **LLM agent steps** -- run Claude Code or OpenCode as workflow steps with session continuation and cost tracking
|
|
14
|
+
- **Session mode** -- run the whole workflow inside one agent session; agent calls `flow session-bridge` CLI to advance steps. Keeps the prompt cache warm for the entire run
|
|
15
|
+
- **Conversational agent mode** -- `agent.mode: conversational` lets the agent iterate with the user until the step is complete, then advance
|
|
16
|
+
- **Structured JSON output** -- `output_file` + `output_format: json` on agent steps extracts and validates JSON from the agent's stdout, no parsing scripts required
|
|
17
|
+
- **Parallel execution** -- mark steps with `parallel: true` or group them with `parallel_group` for concurrent batches
|
|
18
|
+
- **For-each loops** -- expand a step across an array of items with `{{item}}` templating
|
|
19
|
+
- **Quality gates** -- conditional checkpoints that halt or warn based on expressions
|
|
20
|
+
- **Subflow composition** -- call child workflows with parameter passing
|
|
21
|
+
- **Wait triggers** -- block on file existence or external signals with `flow signal`
|
|
22
|
+
- **Hierarchical config** -- global, project, workflow-local, env vars, and CLI flags merge in order
|
|
23
|
+
|
|
24
|
+
## Tech stack
|
|
25
|
+
|
|
26
|
+
- **Runtime:** Node.js >= 22
|
|
27
|
+
- **Language:** JavaScript (ES modules)
|
|
28
|
+
- **Dependencies:** js-yaml (single runtime dependency)
|
|
29
|
+
- **Test runner:** node:test (built-in)
|
|
30
|
+
- **Coverage:** c8
|
|
31
|
+
|
|
32
|
+
## Getting started
|
|
33
|
+
|
|
34
|
+
### Install
|
|
6
35
|
|
|
7
36
|
```bash
|
|
8
37
|
npm install -g lilflow
|
|
9
38
|
```
|
|
10
39
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
## Quick Start
|
|
40
|
+
### Run the simplest possible example
|
|
14
41
|
|
|
15
42
|
```bash
|
|
16
|
-
flow init
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
43
|
+
flow init && flow run
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Expected output:
|
|
47
|
+
```
|
|
48
|
+
Initialized .flow/ and workflow.yaml
|
|
49
|
+
Running workflow 'hello-world'...
|
|
50
|
+
[setup] echo "Hello from flow"
|
|
51
|
+
Hello from flow
|
|
52
|
+
Workflow completed.
|
|
20
53
|
```
|
|
21
54
|
|
|
22
|
-
|
|
55
|
+
### Next step
|
|
23
56
|
|
|
24
|
-
|
|
57
|
+
See [Usage](#usage) for common workflows or [docs/index.md](./docs/index.md) for full docs.
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
### Define and run a multi-step workflow
|
|
25
62
|
|
|
26
63
|
```yaml
|
|
27
|
-
name:
|
|
64
|
+
name: build-and-test
|
|
28
65
|
steps:
|
|
66
|
+
- name: install
|
|
67
|
+
run: npm ci
|
|
29
68
|
- name: lint
|
|
30
69
|
run: npm run lint
|
|
31
|
-
|
|
70
|
+
- name: test
|
|
71
|
+
run: npm test
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
flow run
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Run steps in parallel
|
|
32
79
|
|
|
80
|
+
```yaml
|
|
81
|
+
steps:
|
|
82
|
+
- name: lint
|
|
83
|
+
run: npm run lint
|
|
84
|
+
parallel: true
|
|
33
85
|
- name: test
|
|
34
86
|
run: npm test
|
|
35
87
|
parallel: true
|
|
36
|
-
|
|
37
88
|
- name: build
|
|
38
89
|
run: npm run build
|
|
39
90
|
```
|
|
40
91
|
|
|
41
|
-
Parallel step output
|
|
92
|
+
Parallel step output streams in real time with `[lint] ...` / `[test] ...` prefixes.
|
|
42
93
|
|
|
43
|
-
|
|
94
|
+
### Resume a failed run
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
flow status <run-id> # see what failed
|
|
98
|
+
flow resume <run-id> # pick up from the failed step
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Use LLM agents as steps
|
|
44
102
|
|
|
45
103
|
```yaml
|
|
46
|
-
|
|
104
|
+
steps:
|
|
105
|
+
- name: implement
|
|
106
|
+
agent:
|
|
107
|
+
provider: claude-code
|
|
108
|
+
prompt: "Implement the dashboard component"
|
|
109
|
+
- name: review
|
|
110
|
+
agent:
|
|
111
|
+
provider: claude-code
|
|
112
|
+
prompt: "Review the implementation"
|
|
113
|
+
session: continue
|
|
47
114
|
```
|
|
48
115
|
|
|
49
|
-
|
|
116
|
+
### Agent interaction modes
|
|
117
|
+
|
|
118
|
+
Agent steps take an optional `mode` field:
|
|
50
119
|
|
|
51
|
-
|
|
120
|
+
- `autonomous` (default) -- agent works alone, runs headless, reports when done
|
|
121
|
+
- `conversational` -- agent works with the user iteratively in a TTY, advances only after user approval
|
|
52
122
|
|
|
53
123
|
```yaml
|
|
54
|
-
name: deploy
|
|
55
124
|
steps:
|
|
56
|
-
- name:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
125
|
+
- name: build
|
|
126
|
+
agent:
|
|
127
|
+
provider: claude-code
|
|
128
|
+
prompt: "Build the feature described in spec.md"
|
|
129
|
+
mode: conversational
|
|
60
130
|
```
|
|
61
131
|
|
|
62
|
-
|
|
132
|
+
### Extract JSON output from an agent step
|
|
63
133
|
|
|
64
|
-
|
|
134
|
+
```yaml
|
|
135
|
+
steps:
|
|
136
|
+
- name: analyze
|
|
137
|
+
agent:
|
|
138
|
+
provider: claude-code
|
|
139
|
+
prompt: ./analyze.md
|
|
140
|
+
output_file: out/analysis.json
|
|
141
|
+
output_format: json
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
lilflow strips markdown fences, finds the first balanced JSON object/array in stdout, validates it parses, and writes the cleaned JSON to the file. The step fails with a structured error code if the output isn't valid JSON.
|
|
65
145
|
|
|
66
|
-
|
|
146
|
+
### Run the entire workflow in one agent session
|
|
67
147
|
|
|
68
148
|
```yaml
|
|
69
|
-
name:
|
|
149
|
+
name: iterative-dev
|
|
150
|
+
mode: session
|
|
151
|
+
session:
|
|
152
|
+
provider: claude-code # or: opencode (via oh-my-opencode)
|
|
153
|
+
model: claude-opus-4-6
|
|
154
|
+
allow_tools: [Bash, Read, Write, Edit, Grep, Glob]
|
|
155
|
+
plugins: [lilflow]
|
|
70
156
|
steps:
|
|
71
|
-
- name:
|
|
72
|
-
run:
|
|
73
|
-
|
|
157
|
+
- name: scaffold
|
|
158
|
+
run: mkdir -p src/components
|
|
159
|
+
- name: implement
|
|
160
|
+
agent:
|
|
161
|
+
provider: claude-code
|
|
162
|
+
prompt: "Build dashboard components"
|
|
163
|
+
mode: conversational
|
|
164
|
+
- name: test
|
|
165
|
+
run: npm test
|
|
74
166
|
```
|
|
75
167
|
|
|
76
|
-
|
|
168
|
+
`flow run` spawns one `claude` (or `opencode`) process, injects a workflow-aware system prompt, and exposes the `flow session-bridge` CLI through the bundled `lilflow` skill. The agent drives the workflow (`flow session-bridge next` -> execute -> `flow session-bridge update`) without per-step process spawns, keeping the prompt cache warm for the entire run.
|
|
77
169
|
|
|
78
|
-
|
|
170
|
+
OpenCode sessions work through [oh-my-opencode](https://github.com/opensoft/oh-my-opencode)'s Claude Code compatibility layer -- the same bundled plugin loads unmodified.
|
|
79
171
|
|
|
80
|
-
|
|
172
|
+
### Iterate with for-each
|
|
81
173
|
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
|
|
174
|
+
```yaml
|
|
175
|
+
steps:
|
|
176
|
+
- name: deploy
|
|
177
|
+
run: ./deploy.sh {{item}}
|
|
178
|
+
for_each: [dev, staging, prod]
|
|
85
179
|
```
|
|
86
180
|
|
|
87
|
-
|
|
181
|
+
### Wait for external input
|
|
88
182
|
|
|
89
183
|
```yaml
|
|
90
|
-
name: {{template.workflow_name|ci-pipeline}}
|
|
91
|
-
parallelism: {{config.parallelism}}
|
|
92
184
|
steps:
|
|
93
|
-
- name:
|
|
94
|
-
|
|
185
|
+
- name: await-approval
|
|
186
|
+
wait:
|
|
187
|
+
trigger: signal
|
|
188
|
+
timeout: 1h
|
|
95
189
|
```
|
|
96
190
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
191
|
+
Then from another terminal: `flow signal <run-id> await-approval --data '{"approved": true}'`
|
|
192
|
+
|
|
193
|
+
## CLI
|
|
100
194
|
|
|
101
|
-
|
|
195
|
+
```
|
|
196
|
+
flow init [--template <name>]
|
|
197
|
+
flow config
|
|
198
|
+
flow run [<workflow>]
|
|
199
|
+
flow resume <run-id>
|
|
200
|
+
flow set-step <run-id> <step-index>
|
|
201
|
+
flow signal <run-id> <step-name> [--data '{}']
|
|
202
|
+
flow status <run-id>
|
|
203
|
+
flow list
|
|
204
|
+
flow logs <run-id> [--step <step>]
|
|
205
|
+
flow session-bridge <subcommand> # agent-facing bridge for session-mode workflows
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Project structure
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
lilflow/
|
|
212
|
+
├── src/ # Application source (ES modules)
|
|
213
|
+
│ ├── cli.js # Entry point, command router
|
|
214
|
+
│ ├── run-workflow.js # Core workflow engine
|
|
215
|
+
│ ├── config.js # Hierarchical config system
|
|
216
|
+
│ ├── init-project.js # Project scaffolding
|
|
217
|
+
│ ├── session-bridge.js # Agent-facing CLI for session mode
|
|
218
|
+
│ ├── session-runner.js # Single-process execution path
|
|
219
|
+
│ ├── session-prompt.js # System prompt generator
|
|
220
|
+
│ └── agents/ # LLM agent provider adapters + output extraction
|
|
221
|
+
├── plugin/ # Bundled Claude Code plugin + lilflow skill
|
|
222
|
+
├── tests/ # Test suite (node:test)
|
|
223
|
+
├── docs/ # Generated documentation + requirements specs
|
|
224
|
+
└── .github/workflows/ # CI/CD (lint + test + npm publish)
|
|
225
|
+
```
|
|
102
226
|
|
|
103
|
-
##
|
|
227
|
+
## Documentation
|
|
104
228
|
|
|
105
|
-
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
-
|
|
229
|
+
- [Documentation Index](./docs/index.md)
|
|
230
|
+
- [Architecture](./docs/architecture.md)
|
|
231
|
+
- [Component Inventory](./docs/component-inventory.md)
|
|
232
|
+
- [Source Tree Analysis](./docs/source-tree-analysis.md)
|
|
233
|
+
- [Development Guide](./docs/development-guide.md)
|
|
234
|
+
- [Session Mode Spec](./docs/requirements/session-mode.md)
|
|
109
235
|
|
|
110
236
|
## License
|
|
111
237
|
|
|
112
238
|
MIT
|
|
239
|
+
<!-- /codeharness:readme -->
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lilflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Repo-native workflow engine CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"lilflow": "
|
|
8
|
-
"flow": "
|
|
7
|
+
"lilflow": "src/cli.js",
|
|
8
|
+
"flow": "src/cli.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"src",
|
|
12
|
+
"plugin",
|
|
12
13
|
"README.md",
|
|
13
14
|
"AGENTS.md"
|
|
14
15
|
],
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lilflow",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Drive lilflow workflows from inside a Claude Code session. Exposes the flow session-bridge CLI as a skill so the agent can request the next step, update workflow state, evaluate gates, and converse with the user.",
|
|
5
|
+
"homepage": "https://github.com/iVintik/lilflow",
|
|
6
|
+
"license": "MIT"
|
|
7
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lilflow-workflow-driver
|
|
3
|
+
description: Drive a lilflow session-mode workflow from inside this agent session (Claude Code natively, or OpenCode via the oh-my-opencode Claude Code compatibility layer). Use this skill whenever the FLOW_RUN_ID environment variable is set — it means lilflow has started a workflow and expects you to advance it via the `flow session-bridge` CLI. Call `flow session-bridge next` to get the next step, execute it, then call `flow session-bridge update <step> completed|failed` to advance. For conversational agent steps, work with the user iteratively before marking complete.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# lilflow Workflow Driver
|
|
7
|
+
|
|
8
|
+
You are driving a **lilflow** workflow. Each step in the workflow must be fetched, executed, and reported back through the `flow session-bridge` CLI. Do not try to guess the workflow — always ask the bridge what's next.
|
|
9
|
+
|
|
10
|
+
This skill works identically in Claude Code and in OpenCode (via [oh-my-opencode](https://github.com/opensoft/oh-my-opencode)'s Claude Code compatibility layer). The bridge CLI is the same in both environments.
|
|
11
|
+
|
|
12
|
+
## When this skill activates
|
|
13
|
+
|
|
14
|
+
You can tell this skill applies when:
|
|
15
|
+
|
|
16
|
+
- The environment variable `FLOW_RUN_ID` is set (always check `echo $FLOW_RUN_ID` first).
|
|
17
|
+
- The user's initial prompt references a workflow, a "run", or explicitly mentions lilflow.
|
|
18
|
+
|
|
19
|
+
If `FLOW_RUN_ID` is not set, stop and tell the user — the workflow runner did not start this session.
|
|
20
|
+
|
|
21
|
+
## Core loop
|
|
22
|
+
|
|
23
|
+
Follow this loop until `flow session-bridge next` reports `workflow_status: "completed"`:
|
|
24
|
+
|
|
25
|
+
1. **Get the next step:**
|
|
26
|
+
```bash
|
|
27
|
+
flow session-bridge next
|
|
28
|
+
```
|
|
29
|
+
Returns JSON: `{ step, steps, remaining, workflow_status }`. `step` is non-null for a single eligible step; `steps` is a list for a parallel batch.
|
|
30
|
+
|
|
31
|
+
2. **Execute the step** based on its `type`:
|
|
32
|
+
|
|
33
|
+
- **`run`** — execute `step.command` via the `Bash` tool. Capture stdout/exit code.
|
|
34
|
+
- **`agent`** with `mode: autonomous` — execute `step.agent.prompt` yourself. You are the agent.
|
|
35
|
+
- **`agent`** with `mode: conversational` — see [Conversational steps](#conversational-steps) below.
|
|
36
|
+
- **`agent`** with a different provider (e.g. `opencode`) — mark the step `deferred`; the runner handles foreign providers.
|
|
37
|
+
- **`gate`** — call `flow session-bridge gate <name>`. It evaluates the condition and returns `{ passed, workflow_should_stop }`.
|
|
38
|
+
- **`interactive`** — call `flow session-bridge ask <prompt>` to get user input, then run the command with the input.
|
|
39
|
+
- **`wait`** — call `flow session-bridge wait <name>`. Blocks until the trigger fires.
|
|
40
|
+
- **`subflow`** — not yet supported in session mode. Mark the step `deferred`.
|
|
41
|
+
|
|
42
|
+
3. **Report the result:**
|
|
43
|
+
```bash
|
|
44
|
+
flow session-bridge update <step-name> completed --output "..." --exit-code 0
|
|
45
|
+
flow session-bridge update <step-name> failed --reason "..."
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
4. **On failure:** call `flow session-bridge update <name> failed` and stop the loop unless the user instructs otherwise.
|
|
49
|
+
|
|
50
|
+
5. **Repeat** until `workflow_status === "completed"`.
|
|
51
|
+
|
|
52
|
+
## Conversational steps
|
|
53
|
+
|
|
54
|
+
When `step.agent.mode === "conversational"`:
|
|
55
|
+
|
|
56
|
+
1. Present the step's prompt and your proposed approach to the user.
|
|
57
|
+
2. Work iteratively — implement, show results, gather feedback, adjust.
|
|
58
|
+
3. Do NOT call `flow session-bridge update` until the user has confirmed satisfaction OR you have completed the task to its written specification.
|
|
59
|
+
4. Explicitly tell the user when you consider the step complete before advancing.
|
|
60
|
+
5. If the user says "skip", "abort", or similar, call `flow session-bridge update <name> failed --reason "user aborted"`.
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
```
|
|
64
|
+
User: (workflow starts)
|
|
65
|
+
Agent: The workflow says: "Create React components for the dashboard.
|
|
66
|
+
Iterate with the user until they approve." Here's my plan: ...
|
|
67
|
+
User: Add a sidebar too.
|
|
68
|
+
Agent: [revises, implements, shows output]. Anything else?
|
|
69
|
+
User: Looks good.
|
|
70
|
+
Agent: Marking "implement" complete, advancing to the next step.
|
|
71
|
+
[runs: flow session-bridge update implement completed]
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Parallel batches
|
|
75
|
+
|
|
76
|
+
If `flow session-bridge next` returns multiple `steps`, they are eligible in parallel. You may execute them sequentially, or use sub-agents (the `Agent` tool) to run them concurrently. Call `flow session-bridge update` once per step.
|
|
77
|
+
|
|
78
|
+
## User-initiated questions
|
|
79
|
+
|
|
80
|
+
If you need user input in the middle of an autonomous step (an ambiguity the workflow didn't cover), call `flow session-bridge ask "<question>"` — the bridge prompts the user and returns their response on stdout.
|
|
81
|
+
|
|
82
|
+
## Context management
|
|
83
|
+
|
|
84
|
+
After long workflows, your context may accumulate noise from completed steps. Call:
|
|
85
|
+
```bash
|
|
86
|
+
flow session-bridge compact
|
|
87
|
+
```
|
|
88
|
+
This returns a condensed summary of completed steps. Use the summary to anchor your memory and treat earlier tool results as droppable.
|
|
89
|
+
|
|
90
|
+
## Reporting notes and decisions
|
|
91
|
+
|
|
92
|
+
To record observations that don't change workflow state:
|
|
93
|
+
```bash
|
|
94
|
+
flow session-bridge log "observation or decision"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Inspecting state
|
|
98
|
+
|
|
99
|
+
To see the full workflow state (what's done, what's pending):
|
|
100
|
+
```bash
|
|
101
|
+
flow session-bridge status
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Rules
|
|
105
|
+
|
|
106
|
+
1. **Always** call `flow session-bridge next` before executing anything. Do not infer the workflow.
|
|
107
|
+
2. **Always** call `flow session-bridge update` after finishing a step, even on failure. The bridge is the source of truth.
|
|
108
|
+
3. **Never** invent step names or pretend a step succeeded. The event log is authoritative.
|
|
109
|
+
4. When a gate fails with `workflow_should_stop: true`, stop the loop and report the failure to the user.
|
|
110
|
+
5. Respect `mode: conversational` — do not advance these steps without user interaction.
|
package/src/agents/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { runOpencode } from "./opencode.js";
|
|
|
5
5
|
import { runClaudeCode } from "./claude-code.js";
|
|
6
6
|
import { resolvePromptInput } from "./prompt.js";
|
|
7
7
|
import { getSessionId, loadSessionStore, saveSessionId } from "./session-store.js";
|
|
8
|
+
import { AgentOutputError, writeAgentOutput } from "./output-file.js";
|
|
8
9
|
|
|
9
10
|
const PROVIDERS = {
|
|
10
11
|
opencode: {
|
|
@@ -101,6 +102,18 @@ export async function executeAgentStep(options) {
|
|
|
101
102
|
const templatedAppendSystemPrompt = agentSpec.append_system_prompt === undefined
|
|
102
103
|
? undefined
|
|
103
104
|
: templateFn(agentSpec.append_system_prompt);
|
|
105
|
+
|
|
106
|
+
if (agentSpec.interactive !== undefined && typeof warn === "function") {
|
|
107
|
+
warn(
|
|
108
|
+
`Agent step '${step.name}' uses deprecated agent.interactive; prefer agent.mode: ${agentSpec.interactive === true ? "conversational" : "autonomous"}.`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// mode: conversational maps to TTY passthrough. Legacy agent.interactive
|
|
113
|
+
// is respected for back-compat until removal.
|
|
114
|
+
const resolvedInteractive = agentSpec.mode === "conversational"
|
|
115
|
+
|| agentSpec.interactive === true;
|
|
116
|
+
|
|
104
117
|
const result = await provider.run({
|
|
105
118
|
bin: binary,
|
|
106
119
|
prompt: templatedPrompt,
|
|
@@ -109,7 +122,7 @@ export async function executeAgentStep(options) {
|
|
|
109
122
|
timeoutMs,
|
|
110
123
|
cwd,
|
|
111
124
|
env,
|
|
112
|
-
interactive:
|
|
125
|
+
interactive: resolvedInteractive,
|
|
113
126
|
plugins: agentSpec.plugins ?? [],
|
|
114
127
|
allowTools: agentSpec.allow_tools ?? [],
|
|
115
128
|
appendSystemPrompt: templatedAppendSystemPrompt,
|
|
@@ -128,6 +141,33 @@ export async function executeAgentStep(options) {
|
|
|
128
141
|
}
|
|
129
142
|
}
|
|
130
143
|
|
|
144
|
+
if (agentSpec.output_file !== undefined && result.exitCode === 0) {
|
|
145
|
+
try {
|
|
146
|
+
const writeResult = await writeAgentOutput({
|
|
147
|
+
outputFile: agentSpec.output_file,
|
|
148
|
+
format: agentSpec.output_format ?? "text",
|
|
149
|
+
stdout: result.stdout,
|
|
150
|
+
cwd,
|
|
151
|
+
stepName: step.name
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
if (typeof warn === "function") {
|
|
155
|
+
warn(`Agent step '${step.name}' wrote ${writeResult.bytesWritten} bytes to ${agentSpec.output_file}.`);
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
158
|
+
if (error instanceof AgentOutputError) {
|
|
159
|
+
// Surface as step failure without corrupting the agent result payload.
|
|
160
|
+
return {
|
|
161
|
+
...result,
|
|
162
|
+
exitCode: result.exitCode === 0 ? 1 : result.exitCode,
|
|
163
|
+
stderr: `${result.stderr ?? ""}\n${error.message}`.trim(),
|
|
164
|
+
outputError: { code: error.code, message: error.message }
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
131
171
|
return result;
|
|
132
172
|
}
|
|
133
173
|
|