bullswarm 0.8.0 → 0.10.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/CHANGELOG.md CHANGED
@@ -1,5 +1,50 @@
1
1
  # bullswarm changelog
2
2
 
3
+ ## 0.10.0 — battle-tested advisory orchestration and agent activity
4
+
5
+ - Made `maxAgents` and `maxWorkflowSeconds` advisory planning targets instead
6
+ of hard stops. Workflows can exceed them to finish required implementation
7
+ and verification; structural graph-growth limits remain enforced.
8
+ - Removed implicit connector and generated-goal wall-clock timeouts. Delegates
9
+ wait for natural completion unless an operator explicitly supplies a timeout,
10
+ requests cancellation, or a definitive auth/quota failure is observed.
11
+ - Added compatibility migration for generated 0.9.0 goals carrying Bullswarm's
12
+ former 900-second planner/action timeout defaults.
13
+ - Fixed adaptive completion policy, current-action metadata, provider routing
14
+ history, usage aggregation, latest-worker verification, and truthful partial
15
+ token/cost accounting found during the Kipwise battle test.
16
+ - Added connector-owned native JSONL event adapters for Codex, Claude, Grok,
17
+ Command Code, and OpenCode. Workflows now retain and display the latest three
18
+ semantic shell/read/edit/write/response actions for every active agent.
19
+ - Added conservative stall evidence: ten minutes without transport, parsed
20
+ event, or semantic action activity is labeled `suspected_stalled` but never
21
+ causes an automatic kill.
22
+ - Added a low-noise `workflow watch <id>` progress stream with semantic-change
23
+ updates, heartbeats, last-three agent actions, and terminal per-attempt timing.
24
+ - Added optional durable `workflow steer <id> --message ...` guidance delivered
25
+ only at the next planning checkpoint, never injected into an active worker.
26
+ - Captured runtime model IDs declared in provider event streams for more complete
27
+ model/cost attribution, preserved Grok tool kinds across name-less updates,
28
+ and enabled Claude's supported forwarded-subagent text stream.
29
+ - Taught planners to reuse clean full-suite evidence and isolate mutation/pre-fix
30
+ experiments instead of redundantly or concurrently testing a changing tree.
31
+ - Expanded the offline suite to 224 tests. Real bounded CLI probes confirmed
32
+ all five provider event formats, and an exact packaged OpenCode watch smoke
33
+ passed argument injection, action normalization, final-output extraction,
34
+ and the content gate together.
35
+
36
+ ## 0.9.0 — resilient dynamic workflow routing
37
+
38
+ - Added cooperative `SIGTERM`/`SIGINT` handling, durable `interrupted` states,
39
+ dead/stale owner reconciliation, and clean resume after interruption.
40
+ - Added capability-context filtering for model recommendations plus an explicit
41
+ `strategy apply --yes` approval gate and TTL-based automatic refresh.
42
+ - Added setup-time worktree policy and strategy-autopilot choices.
43
+ - Added per-attempt routing reasons/candidate surplus to durable events, state,
44
+ decision logs, and the printable workflow tree.
45
+ - Added top-level `runs` and `--version` aliases, complete `workflow goal`
46
+ budget help, and correct phase/terminal display for completed runs.
47
+
3
48
  ## 0.8.0 — autonomous goals, model strategy, and auditable usage
4
49
 
5
50
  - Added `workflow goal` for bounded observe-plan-execute loops without an
package/README.md CHANGED
@@ -31,7 +31,7 @@ npm install -g bullswarm # or: node bin/bullswarm.js directly from a checkout
31
31
  bullswarm # first run: interactive setup wizard
32
32
  bullswarm setup # re-run or repair
33
33
  bullswarm pools # meter state, pace position, quarantine status
34
- bullswarm strategy refresh # discover local models and refresh tier suggestions
34
+ bullswarm strategy refresh --apply --yes # approve capability-aware tier autopilot
35
35
  bullswarm run --lane analyze --add-dir ~/some-repo --task-file /tmp/t.md --json
36
36
  bullswarm workflow goal "Fix the failing tests and verify the change" --cwd ~/some-repo
37
37
  bullswarm health # re-judge saved outputs; catch gate failures
@@ -57,18 +57,25 @@ connector-declared, dated pricing/benchmark metadata with live quota surplus:
57
57
  ```bash
58
58
  bullswarm strategy refresh
59
59
  bullswarm strategy show --json
60
+ bullswarm strategy apply --yes --refresh-hours 24
61
+ bullswarm strategy auto status
60
62
  bullswarm strategy set-subscription command-code \
61
63
  --plan GOAT --monthly-usd 10 --included-usd 70 --quota-window monthly
62
64
  bullswarm strategy assign high --pool claude-code --model claude-opus-4-6
63
65
  bullswarm run --effort high --lane analyze --task-file /tmp/task.md --json
64
66
  ```
65
67
 
66
- Setup recommends an initial refresh; run `strategy refresh` whenever a CLI's
67
- catalog or subscription changes. Discovery commands, model argument syntax,
68
- pricing, and benchmark declarations remain connector-owned. Unknown license
69
- value, prices, and benchmarks stay `null` rather than being guessed. An
70
- assignment is only a preference: quarantine, exhaustion, burst gates, and
71
- capability checks still win.
68
+ Interactive setup asks whether to enable strategy autopilot; non-interactive
69
+ setup requires the explicit `setup --yes --strategy` flag. Recommendations are
70
+ context-filtered before ranking: high requires analysis plus workflow-planning,
71
+ medium requires build/edit capabilities, and low targets bounded chores. An
72
+ approved policy refreshes stale discovery before later runs and re-applies the
73
+ best eligible models on its configured interval. Disable it with
74
+ `strategy auto off --yes`. Discovery commands, model argument syntax, pricing,
75
+ and benchmark declarations remain connector-owned. Unknown license value,
76
+ prices, and benchmarks stay `null` rather than being guessed. An assignment is
77
+ only a preference: quarantine, exhaustion, burst gates, and capability checks
78
+ still win.
72
79
 
73
80
  Every run and workflow attempt reports its selected agent/model and estimated
74
81
  usage. When a delegate does not expose counters, Bullswarm labels its UTF-8
@@ -94,8 +101,9 @@ bullswarm workflow goal \
94
101
  --cwd ~/some-repo --detach --json
95
102
  ```
96
103
 
97
- Bullswarm selects an eligible `workflow-planning` orchestrator by live quota
98
- surplus. The orchestrator observes durable evidence, proposes bounded actions,
104
+ Bullswarm first honors an approved high-tier provider/model assignment when it
105
+ remains eligible, otherwise it selects an eligible `workflow-planning`
106
+ orchestrator by live quota surplus. The orchestrator observes durable evidence, proposes bounded actions,
99
107
  and decides when another expansion or verification is necessary. Bullswarm
100
108
  validates the proposal, owns agent/process selection, routes workers, and calls
101
109
  the orchestrator again until completion, cancellation, failure, approval, or a
@@ -106,6 +114,7 @@ The detached response includes a short ID and exact observation commands:
106
114
 
107
115
  ```bash
108
116
  bullswarm workflow runs show <shortId>
117
+ bullswarm workflow watch <shortId> # low-noise live progress + terminal timing
109
118
  bullswarm workflow tui <shortId> # printable phase/action/attempt tree
110
119
  bullswarm workflow tui --json <shortId>
111
120
  bullswarm workflow events --json <shortId> --after 0
@@ -121,7 +130,9 @@ bullswarm workflow goal --resume <shortId> --json
121
130
  `--orchestrator <pool>` exists for controlled testing; ordinary use should
122
131
  leave selection on `auto`. Hard limits can be adjusted with `--max-agents`,
123
132
  `--max-expansion-rounds`, `--max-actions`, `--max-items-per-expansion`, and
124
- `--max-workflow-seconds`.
133
+ `--max-workflow-seconds`. Interactive setup also records a worktree-isolation
134
+ preference (`agent-decides`, `off`, or `required`); Bullswarm communicates that
135
+ policy to the orchestrator without imposing repository topology itself.
125
136
 
126
137
  ## Building a workflow from the shell
127
138
 
@@ -164,6 +175,7 @@ bullswarm workflow runs --historical # only historical
164
175
  bullswarm workflow runs --name audit-code # filter by workflow
165
176
  bullswarm workflow runs --limit 20 # cap the result count
166
177
  bullswarm workflow runs show <shortId> # state + report + summary
178
+ bullswarm runs show <shortId> # top-level shorthand
167
179
  bullswarm workflow runs delete <shortId> --yes # remove the run dir
168
180
 
169
181
  # Resume by shortId — runs the same logic as the full runId
@@ -172,6 +184,18 @@ bullswarm workflow run audit-code --resume <shortId>
172
184
 
173
185
  ### Live workflow dashboard
174
186
 
187
+ For ordinary observation, use the non-interactive watcher. It prints only when
188
+ the phase, step, agent action, routing, or status changes, plus a 60-second
189
+ heartbeat while otherwise quiet. Terminal output includes every attempt's
190
+ agent/model, elapsed time, outcome, and tokens, so a slow test is distinguishable
191
+ from a stalled process without writing a polling script.
192
+
193
+ ```bash
194
+ bullswarm workflow watch <shortId>
195
+ bullswarm workflow watch <shortId> --jsonl # automation-friendly stream
196
+ bullswarm workflow watch <shortId> --once # one current/terminal snapshot
197
+ ```
198
+
175
199
  `workflow tui` is the interactive, Claude-style `/workflows` view. It watches
176
200
  ongoing runs from disk and supports `j`/`k` or arrow-key selection, Enter for
177
201
  details, `c` to request a cooperative stop, `r` to refresh, and `q` to quit.
@@ -191,17 +215,62 @@ bullswarm workflow tui --json --cancel <id> # request cooperative stop
191
215
  bullswarm workflow capabilities --json # pools, lanes, models, meters, limits
192
216
  bullswarm workflow inspect <file-or-name> # workflow shape and semantics
193
217
  bullswarm workflow events --json <id> --after 20
218
+ bullswarm workflow steer <id> --message "Prefer focused tests before another full suite"
194
219
  bullswarm workflow action show --json <id> <actionId>
195
220
  bullswarm workflow approval approve --json <id> # then resume the run
196
221
  ```
197
222
 
198
223
  Cancellation is persisted as `cancelling`, terminates an active child process,
199
224
  records its termination signal and latency evidence, then commits `cancelled`.
225
+ `SIGTERM` and `SIGINT` use the same cooperative child termination path but
226
+ commit a distinct resumable `interrupted` state. On every workflow command,
227
+ active states with a dead/stale owner are automatically reconciled to
228
+ `interrupted` instead of remaining falsely `running`.
229
+
230
+ `workflow steer` is optional operator guidance, not hot-patching. It appends a
231
+ durable instruction that is delivered only to the next not-yet-started
232
+ `decide` checkpoint; the active worker continues unchanged. Steering remains
233
+ inside the original goal and authorization boundary and cannot bypass runtime
234
+ validation or required verification. Static workflows and terminal runs reject
235
+ steering because they have no future orchestration checkpoint.
236
+ Live attempts record the last stdout/stderr activity time and observed byte
237
+ count separately from the runner heartbeat. This makes a silent process
238
+ visible without treating elapsed wall time alone as proof that it is hung.
239
+ Supported coding-agent connectors also enable their native JSONL event mode and
240
+ declaratively map provider events into a common semantic action record:
241
+
242
+ ```json
243
+ {"id":"provider-action-id","at":"...","kind":"shell_command|read_file|edit|response","status":"running|completed|failed","summary":"safe scalar preview"}
244
+ ```
245
+
246
+ The live workflow pane retains the latest three logical actions per agent.
247
+ Repeated updates for the same tool call replace its status, and streaming text
248
+ chunks coalesce into one response action. Heartbeats, token/thought deltas,
249
+ usage messages, hooks, and unparsed output remain liveness evidence but do not
250
+ occupy the action pane. Connector-specific flags, paths, and mappings live in
251
+ `connectors/*.json` under `eventStream`; core contains no provider event names.
252
+
253
+ After ten minutes without transport, parsed-event, or semantic-action evidence,
254
+ an active child is labeled `suspected_stalled`. This is an inspection signal,
255
+ not a death verdict and never an automatic kill: buffered CLIs can be silent
256
+ while working. Process exit, a fatal auth/quota signature, explicit operator
257
+ cancellation, or an opt-in timeout remain the terminal signals.
258
+
259
+ Each attempt records the phase/action, selected pool and model, effort tier,
260
+ routing reason, all eligible candidates with quota surplus, timestamps,
261
+ artifact paths, outcome, and reported-or-estimated token/cost/quota usage.
262
+ `workflow tui <id>` renders this breakdown for completed runs as well as live
263
+ ones; `workflow tui --json <id>` exposes the durable audit document.
264
+ When a provider event stream reports the actual model, Bullswarm records that
265
+ runtime value and uses its matching connector rate metadata for the attempt's
266
+ cost estimate. Unknown or provider-hidden model identity remains explicitly
267
+ unknown.
200
268
 
201
269
  ### Adaptive workflows
202
270
 
203
271
  Static workflows remain zero-extra-LLM orchestration. An adaptive workflow adds
204
- an explicit `decide` step and hard limits:
272
+ an explicit `decide` step, advisory resource targets, and structural expansion
273
+ limits:
205
274
 
206
275
  ```json
207
276
  {
@@ -228,6 +297,13 @@ an explicit `decide` step and hard limits:
228
297
  }
229
298
  ```
230
299
 
300
+ `maxAgents` and `maxWorkflowSeconds` are advisory inputs to the orchestrator.
301
+ Crossing either target is recorded in durable state but never stops a worker,
302
+ skips verification, or fails a run. `maxExpansionRounds`, `maxActions`, and
303
+ `maxItemsPerExpansion` remain hard graph-growth safeguards. Delegates have no
304
+ implicit wall-clock timeout; set a step's `timeoutSec` (or direct-run
305
+ `--timeout`) only when an operator explicitly wants a hard termination timer.
306
+
231
307
  The planner returns versioned JSON. It may propose `needs_more_work` with
232
308
  bounded `run`, inline-`fanout`, or `verify` actions. The deterministic runtime
233
309
  validates IDs, dependencies, operation types, capabilities, and budgets before
@@ -12,9 +12,35 @@
12
12
  "authSignatures": ["strings in output that mean auth/throttle failure"],
13
13
  "outputExtraction": {
14
14
  "$comment": "how to get the real answer out of stdout+stderr+files",
15
- "strategy": "stdout|stdout-tail|json-field|file",
15
+ "strategy": "stdout|stdout-tail|json-field|file|event-stream",
16
16
  "field": "optional json field path or file glob"
17
17
  },
18
+ "eventStream": {
19
+ "$comment": "Optional declarative JSONL adapter. Provider quirks stay here; core emits normalized semantic actions.",
20
+ "format": "jsonl",
21
+ "args": ["CLI flags that enable machine-readable streaming"],
22
+ "modelPaths": ["provider model-id paths in priority order"],
23
+ "silenceThresholdSec": 600,
24
+ "rules": [{
25
+ "rootMatch": {"path": "provider.event.type", "equals": "tool_started"},
26
+ "forEach": "optional.array.path",
27
+ "match": {"path": "nested.type", "equals": "tool_use"},
28
+ "idPaths": ["provider action id paths in priority order"],
29
+ "kindPaths": ["tool or response kind paths"],
30
+ "summaryPaths": ["safe command/path/text scalar paths"],
31
+ "status": "queued|running|streaming|completed|failed",
32
+ "statusPath": "optional provider status path",
33
+ "statusMap": {"provider-status": "normalized-status"},
34
+ "aggregate": "consecutive",
35
+ "summaryMode": "replace|concat"
36
+ }],
37
+ "output": [{
38
+ "match": {"path": "provider.event.type", "equals": "final"},
39
+ "path": "provider final response scalar path",
40
+ "mode": "last|concat",
41
+ "separator": "optional separator"
42
+ }]
43
+ },
18
44
  "meter": {
19
45
  "$comment": "none | declared (manual) | reader (programmatic)",
20
46
  "type": "none|declared|reader",
@@ -50,5 +76,5 @@
50
76
  "stealth": false,
51
77
  "$comment-stealth": "stealth=true: prompts/completions retained by an anonymous provider — opt-in only, never a default"
52
78
  },
53
- "timeoutSec": 900
79
+ "$comment-timeout": "Delegates have no implicit wall-clock timeout; use direct --timeout or workflow step.timeoutSec as an explicit opt-in"
54
80
  }
@@ -23,7 +23,21 @@
23
23
  "please run /login"
24
24
  ],
25
25
  "outputExtraction": {
26
- "strategy": "stdout"
26
+ "strategy": "event-stream"
27
+ },
28
+ "eventStream": {
29
+ "format": "jsonl",
30
+ "args": ["--output-format", "stream-json", "--verbose", "--forward-subagent-text"],
31
+ "modelPaths": ["model", "message.model"],
32
+ "silenceThresholdSec": 600,
33
+ "rules": [
34
+ { "rootMatch": { "path": "type", "equals": "assistant" }, "forEach": "message.content", "match": { "path": "type", "equals": "tool_use" }, "idPaths": ["id"], "kindPaths": ["name"], "summaryPaths": ["input.command", "input.file_path", "input.path", "input.pattern"], "status": "running" },
35
+ { "rootMatch": { "path": "type", "equals": "user" }, "forEach": "message.content", "match": { "path": "type", "equals": "tool_result" }, "idPaths": ["tool_use_id"], "kind": "tool", "statusPath": "is_error", "statusMap": { "true": "failed", "false": "completed" }, "defaultStatus": "completed" },
36
+ { "rootMatch": { "path": "type", "equals": "assistant" }, "forEach": "message.content", "match": { "path": "type", "equals": "text" }, "kind": "response", "summaryPaths": ["text"], "status": "completed" }
37
+ ],
38
+ "output": [
39
+ { "match": { "path": "type", "equals": "result" }, "path": "result", "mode": "last" }
40
+ ]
27
41
  },
28
42
  "$comment-meter": "the CALLER pool \u2014 competes in analyze/build, wins only when no delegate can take the lane",
29
43
  "meter": {
@@ -51,5 +65,5 @@
51
65
  "stealth": false,
52
66
  "isCaller": true
53
67
  },
54
- "timeoutSec": 900
68
+ "$comment-timeout": "No implicit wall-clock timeout; callers may opt in with --timeout or workflow step.timeoutSec"
55
69
  }
@@ -22,7 +22,19 @@
22
22
  "invalid api key"
23
23
  ],
24
24
  "outputExtraction": {
25
- "strategy": "stdout"
25
+ "strategy": "event-stream"
26
+ },
27
+ "eventStream": {
28
+ "format": "jsonl",
29
+ "args": ["--json"],
30
+ "silenceThresholdSec": 600,
31
+ "rules": [
32
+ { "rootMatch": { "path": "type", "equals": "item.started" }, "idPaths": ["item.id"], "kindPaths": ["item.type"], "kindMap": { "agent_message": "response" }, "summaryPaths": ["item.command", "item.text"], "status": "running" },
33
+ { "rootMatch": { "path": "type", "equals": "item.completed" }, "idPaths": ["item.id"], "kindPaths": ["item.type"], "kindMap": { "agent_message": "response" }, "summaryPaths": ["item.command", "item.text"], "status": "completed" }
34
+ ],
35
+ "output": [
36
+ { "match": { "path": "type", "equals": "item.completed" }, "path": "item.text", "mode": "last" }
37
+ ]
26
38
  },
27
39
  "meter": {
28
40
  "type": "reader",
@@ -49,5 +61,5 @@
49
61
  "flags": {
50
62
  "stealth": false
51
63
  },
52
- "timeoutSec": 900
64
+ "$comment-timeout": "No implicit wall-clock timeout; callers may opt in with --timeout or workflow step.timeoutSec"
53
65
  }
@@ -22,7 +22,23 @@
22
22
  "cmd login"
23
23
  ],
24
24
  "outputExtraction": {
25
- "strategy": "stdout"
25
+ "strategy": "event-stream"
26
+ },
27
+ "eventStream": {
28
+ "format": "jsonl",
29
+ "args": ["--output-format", "json"],
30
+ "modelPaths": ["event.model", "model"],
31
+ "silenceThresholdSec": 600,
32
+ "rules": [
33
+ { "rootMatch": { "path": "event.type", "equals": "tool_queued" }, "idPaths": ["event.toolCallId"], "kindPaths": ["event.toolName"], "summaryPaths": ["event.input.command", "event.input.file_path", "event.input.path"], "status": "queued" },
34
+ { "rootMatch": { "path": "event.type", "equals": "tool_running" }, "idPaths": ["event.toolCallId"], "kindPaths": ["event.toolName"], "summaryPaths": ["event.description"], "status": "running" },
35
+ { "rootMatch": { "path": "event.type", "equals": "tool_completed" }, "idPaths": ["event.toolCallId"], "kindPaths": ["event.toolName"], "status": "completed" },
36
+ { "rootMatch": { "path": "event.type", "equals": "message_end" }, "forEach": "event.content", "match": { "path": "type", "equals": "text" }, "kind": "response", "summaryPaths": ["text"], "status": "completed" }
37
+ ],
38
+ "output": [
39
+ { "match": { "path": "type", "equals": "result" }, "path": "finalText", "mode": "last" },
40
+ { "match": { "path": "event.type", "equals": "run_end" }, "path": "event.result.finalText", "mode": "last" }
41
+ ]
26
42
  },
27
43
  "meter": {
28
44
  "type": "reader",
@@ -53,5 +69,5 @@
53
69
  "flags": {
54
70
  "stealth": false
55
71
  },
56
- "timeoutSec": 900
72
+ "$comment-timeout": "No implicit wall-clock timeout; callers may opt in with --timeout or workflow step.timeoutSec"
57
73
  }
@@ -8,7 +8,14 @@
8
8
  import { readFileSync } from 'node:fs';
9
9
 
10
10
  const task = readFileSync(process.argv[2], 'utf8');
11
+ const sleepMatch = task.match(/SLEEP_MS:(\d+)/);
12
+ if (sleepMatch) await new Promise((resolve) => setTimeout(resolve, Number(sleepMatch[1])));
11
13
 
14
+ if (task.includes('FAIL:auth-hang')) {
15
+ console.log('Authentication failed: quota exhausted; waiting process should be terminated.');
16
+ await new Promise((resolve) => setTimeout(resolve, 5000));
17
+ process.exit(0);
18
+ }
12
19
  if (task.includes('FAIL:auth')) {
13
20
  console.log('Authentication failed: no credentials found in keychain.');
14
21
  process.exit(0);
@@ -24,5 +24,5 @@
24
24
  "pricingUpdatedAt": "2026-08-27"
25
25
  }],
26
26
  "flags": { "stealth": false },
27
- "timeoutSec": 120
27
+ "$comment-timeout": "No implicit wall-clock timeout; callers may opt in with --timeout or workflow step.timeoutSec"
28
28
  }
@@ -18,7 +18,20 @@
18
18
  "invalid api key"
19
19
  ],
20
20
  "outputExtraction": {
21
- "strategy": "stdout"
21
+ "strategy": "event-stream"
22
+ },
23
+ "eventStream": {
24
+ "format": "jsonl",
25
+ "args": ["--output-format", "streaming-json"],
26
+ "silenceThresholdSec": 600,
27
+ "rules": [
28
+ { "rootMatch": { "path": "type", "equals": "tool_call" }, "idPaths": ["toolCallId"], "kindPaths": ["toolName", "title"], "summaryPaths": ["rawInput.command", "rawInput.file_path", "rawInput.path", "title"], "statusPath": "status" },
29
+ { "rootMatch": { "path": "type", "equals": "tool_call_update" }, "idPaths": ["toolCallId"], "kindPaths": ["toolName"], "summaryPaths": ["rawOutput.command"], "statusPath": "status" },
30
+ { "rootMatch": { "path": "type", "equals": "text" }, "kind": "response", "summaryPaths": ["data"], "status": "streaming", "aggregate": "consecutive", "summaryMode": "concat" }
31
+ ],
32
+ "output": [
33
+ { "match": { "path": "type", "equals": "text" }, "path": "data", "mode": "concat" }
34
+ ]
22
35
  },
23
36
  "meter": {
24
37
  "type": "reader",
@@ -43,6 +56,6 @@
43
56
  "flags": {
44
57
  "stealth": false
45
58
  },
46
- "timeoutSec": 900,
59
+ "$comment-timeout": "No implicit wall-clock timeout; callers may opt in with --timeout or workflow step.timeoutSec",
47
60
  "$comment-meter": "weekly shared credit pool via billing endpoint; no 5h window exists on unified-billing accounts"
48
61
  }
@@ -8,7 +8,19 @@
8
8
  "$comment-cwdMode": "QUIRK: resolves its project from $PWD, not the spawn cwd. The watcher MUST set env.PWD and spawn with cwd inside the target repo, or it will silently analyse the wrong repository and answer confidently about it."
9
9
  },
10
10
  "authSignatures": ["No cookie auth credentials found", "unauthorized"],
11
- "outputExtraction": { "strategy": "stdout" },
11
+ "outputExtraction": { "strategy": "event-stream" },
12
+ "eventStream": {
13
+ "format": "jsonl",
14
+ "args": ["--format", "json"],
15
+ "silenceThresholdSec": 600,
16
+ "rules": [
17
+ { "rootMatch": { "path": "type", "equals": "tool_use" }, "idPaths": ["part.callID", "part.id"], "kindPaths": ["part.tool"], "summaryPaths": ["part.state.input.command", "part.state.input.file_path", "part.state.input.path", "part.state.title"], "statusPath": "part.state.status" },
18
+ { "rootMatch": { "path": "type", "equals": "text" }, "idPaths": ["part.id"], "kind": "response", "summaryPaths": ["part.text"], "status": "completed" }
19
+ ],
20
+ "output": [
21
+ { "match": { "path": "type", "equals": "text" }, "path": "part.text", "mode": "concat", "separator": "\n" }
22
+ ]
23
+ },
12
24
  "$comment-auto": "--auto is required for headless workflow dispatch: task files live under ~/.bullswarm, outside the target repo, and OpenCode otherwise pauses for an interactive permission approval. --model pins the QA/runtime pool to Luna instead of the CLI default.",
13
25
  "$comment-exit1": "known failure mode: writes a complete correct answer, then dies with a Console-sync auth error and exit 1. The verdict sets contentUsableDespiteExit instead of discarding the work.",
14
26
  "meter": { "type": "none" },
@@ -24,5 +36,5 @@
24
36
  ],
25
37
  "subscription": { "plan": null, "monthlyPriceUsd": null, "includedValueUsd": null, "quotaWindow": null },
26
38
  "flags": { "stealth": false },
27
- "timeoutSec": 900
39
+ "$comment-timeout": "No implicit wall-clock timeout; callers may opt in with --timeout or workflow step.timeoutSec"
28
40
  }