bullswarm 0.19.0 → 0.20.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/AGENTS.md +9 -6
- package/CHANGELOG.md +36 -0
- package/README.md +10 -1
- package/bin/check-output-schema.js +30 -0
- package/connectors/opencode2.json +2 -0
- package/docs/claude-dynamic-workflow-mechanics.md +7 -2
- package/docs/dynamic-workflow-handoff.md +41 -10
- package/docs/dynamic-workflow-qa.md +13 -0
- package/docs/workflow-design.md +24 -5
- package/mcp/server.mjs +17 -2
- package/package.json +1 -1
- package/skill/SKILL.md +112 -577
- package/skill/agents/openai.yaml +7 -0
- package/skill/references/operations.md +88 -0
- package/src/cli.js +3 -0
- package/src/delegate.js +282 -0
- package/src/help.js +53 -11
- package/src/integrate.js +8 -6
- package/src/lib/config.js +2 -0
- package/src/lib/opencode-kaihk.js +119 -0
- package/src/lib/verify.js +13 -3
- package/src/meters/kaihk.js +97 -0
- package/src/meters/registry.js +21 -1
- package/src/setup.js +1 -1
- package/src/workflow/cli.js +2 -0
- package/src/workflow/decision.js +23 -10
- package/src/workflow/goal.js +20 -5
- package/src/workflow/runner.js +5 -4
- package/src/workflow/runtime.js +65 -9
- package/src/workflow/schema.js +24 -23
- package/src/workflow/validate.js +22 -9
package/AGENTS.md
CHANGED
|
@@ -57,18 +57,21 @@ bullswarm workflow runs delete <shortId> --yes
|
|
|
57
57
|
## Using bullswarm from another agent
|
|
58
58
|
|
|
59
59
|
If you are an agent that wants to offload bounded work via bullswarm,
|
|
60
|
-
read `skill/SKILL.md` — that's the agent-facing user guide.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
read `skill/SKILL.md` — that's the agent-facing user guide. Use
|
|
61
|
+
`bullswarm delegate` (or the installed `/bullswarm` skill) by default: it
|
|
62
|
+
previews whether one bounded agent or an autonomous workflow is appropriate,
|
|
63
|
+
shows the conceptual plan, and executes the chosen engine. Reach for `run`,
|
|
64
|
+
`workflow goal`, or a fixed workflow graph directly only when the caller has
|
|
65
|
+
already chosen that execution shape. The skill is published alongside the
|
|
66
|
+
package and is the canonical reference for the CLI surface.
|
|
65
67
|
|
|
66
68
|
- Zero runtime dependencies. Node >= 18. Tests must never require network:
|
|
67
69
|
prime `~/.bullswarm/meters/*.json` caches with fresh timestamps if needed.
|
|
68
70
|
- Every verb must work non-interactively (no TTY). The interactive wizard is
|
|
69
71
|
a human convenience, never a requirement.
|
|
70
72
|
- Version single source: package.json. Release via
|
|
71
|
-
`node bin/bullswarm.js release patch|minor|major
|
|
73
|
+
`node bin/bullswarm.js release patch|minor|major`, then `git push` and
|
|
74
|
+
`git push --tags`
|
|
72
75
|
— CI publishes through npm trusted publishing (OIDC), no tokens.
|
|
73
76
|
|
|
74
77
|
## Adding a connector
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# bullswarm changelog
|
|
2
2
|
|
|
3
|
+
## 0.20.0 — common agent delegation entry point
|
|
4
|
+
|
|
5
|
+
- `/bullswarm` and `bullswarm delegate` now give agents one transparent entry
|
|
6
|
+
point for arbitrary self-contained tasks: classify the request as one bounded
|
|
7
|
+
delegate or an autonomous workflow, show the reason and conceptual plan, then
|
|
8
|
+
execute the selected engine. Explicit mode and lane overrides remain
|
|
9
|
+
available, and `--dry-run --json` exposes the decision without dispatching.
|
|
10
|
+
- Workflow decisions persist the suggested conceptual plan alongside the
|
|
11
|
+
original intent, while the packaged skill keeps the common path concise and
|
|
12
|
+
moves operational detail into a focused reference.
|
|
13
|
+
- Planner context now labels the preflight scout as completion-ineligible and
|
|
14
|
+
requires the first program to contain a real delivery worker plus its
|
|
15
|
+
verifier, preventing an apparently complete scout report from causing a
|
|
16
|
+
rejected completion and redundant recovery round.
|
|
17
|
+
- Ready siblings now honor a connector-owned soft concurrency preference. The
|
|
18
|
+
OpenCode route prefers one in-flight worker, so additional parallel work is
|
|
19
|
+
spread across healthy subscriptions instead of risking correlated headless
|
|
20
|
+
session exits; a lone eligible pool still runs rather than failing capacity.
|
|
21
|
+
- Agent integration upgrades its managed awareness marker to advertise the
|
|
22
|
+
common interface consistently across Codex, Claude, and Grok.
|
|
23
|
+
- Classification now understands negated and instructional mutation language,
|
|
24
|
+
so read-only requests that discuss how to add or write something do not
|
|
25
|
+
accidentally enter the build lane, while a later affirmative implementation
|
|
26
|
+
request still does.
|
|
27
|
+
- A trailing `help` token remains contextual and side-effect-free even after
|
|
28
|
+
options, matching `-h` and `--help`; several README and setup/help examples
|
|
29
|
+
were also brought back into sync with the real CLI.
|
|
30
|
+
- Historical workflow design documents now identify themselves as dated
|
|
31
|
+
implementation records and list the current `verify`, `decide`, and
|
|
32
|
+
`outputSchema` surfaces instead of presenting resolved gaps as current.
|
|
33
|
+
- Extra KaiHK providers in `~/.config/opencode/opencode.json` (`kaihk-2`, …)
|
|
34
|
+
become `opencode2:<id>` pools, spawned with `--model <id>/gpt-5.6-luna`.
|
|
35
|
+
Spend is read from `GET /api/usage/token` plus
|
|
36
|
+
`/v1/dashboard/billing/usage` (USD = `total_usage / 100`). The HTML wallet
|
|
37
|
+
page still needs a browser session and is not the key API.
|
|
38
|
+
|
|
3
39
|
## 0.19.0 — unified workflow dashboard
|
|
4
40
|
|
|
5
41
|
- Running `bullswarm workflow` on an interactive terminal now opens one
|
package/README.md
CHANGED
|
@@ -5,6 +5,11 @@ orchestrator, build and expand the plan, route bounded worker actions by quota,
|
|
|
5
5
|
verify the result, and finish without an initiating agent authoring a graph.
|
|
6
6
|
Every delegate output is judged by content before it counts.
|
|
7
7
|
|
|
8
|
+
For agents, `/bullswarm` (or `$bullswarm` where skills use that syntax) is the
|
|
9
|
+
common entry point. Its durable CLI equivalent is `bullswarm delegate`: it
|
|
10
|
+
first explains whether the request needs one bounded agent or an autonomous
|
|
11
|
+
workflow, shows the conceptual plan, and then executes the selected engine.
|
|
12
|
+
|
|
8
13
|
Every command and nested subcommand supports contextual `-h` / `--help`
|
|
9
14
|
without initializing state or executing the command:
|
|
10
15
|
|
|
@@ -66,6 +71,9 @@ bullswarm # first run: interactive setup wizard
|
|
|
66
71
|
bullswarm setup # re-run or repair
|
|
67
72
|
bullswarm pools # meter state, pace position, quarantine status
|
|
68
73
|
bullswarm strategy refresh --apply --yes # approve capability-aware tier autopilot
|
|
74
|
+
bullswarm delegate --cwd ~/some-repo --prompt "Explain the parser" # one agent
|
|
75
|
+
bullswarm delegate --cwd ~/some-repo --prompt "Audit all commands, fix help, and independently verify" # workflow
|
|
76
|
+
bullswarm delegate --dry-run --json --cwd ~/some-repo --prompt "Your task" # decision + plan only
|
|
69
77
|
bullswarm run --lane analyze --add-dir ~/some-repo --task-file /tmp/t.md --json
|
|
70
78
|
bullswarm run --lane analyze --add-dir ~/some-repo --prompt "Inspect the parser" --json
|
|
71
79
|
bullswarm workflow goal "Fix the failing tests and verify the change" --cwd ~/some-repo
|
|
@@ -78,6 +86,7 @@ bullswarm health # re-judge saved outputs; catch gate failures
|
|
|
78
86
|
|---|---|
|
|
79
87
|
| `setup` | Discover installed agent CLIs, show quota state, toggle pools, suggest a routing table, write config. Approval-gated, idempotent. |
|
|
80
88
|
| `integrate` | Register or remove the canonical Bullswarm skill and global awareness rules for Codex, Claude, and Grok. |
|
|
89
|
+
| `delegate` | Explain and execute the smallest reliable shape: one content-verified agent or an autonomous verified workflow. |
|
|
81
90
|
| `run` | route → dispatch → watch → verify → one JSON verdict |
|
|
82
91
|
| `health` | Re-judge saved outputs against their verdicts; surface verify-gate failures and quarantine clusters |
|
|
83
92
|
| `pools` | Show each pool's meter state, pace position, quarantine status |
|
|
@@ -227,7 +236,7 @@ bullswarm workflow draft phase add audit-code discover
|
|
|
227
236
|
bullswarm workflow draft phase add audit-code review
|
|
228
237
|
bullswarm workflow draft step add audit-code discover list-files \
|
|
229
238
|
--type run --lane chore --prompt "List every .js file in src/" \
|
|
230
|
-
--
|
|
239
|
+
--add-dir '{{inputs.targetDir}}'
|
|
231
240
|
bullswarm workflow draft step add audit-code review per-file \
|
|
232
241
|
--type fanout --items-from 'outputs.list-files.outFile' \
|
|
233
242
|
--lane analyze --concurrency 2 \
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { isValidOutputSchema, validateAgainstSchema } from '../src/workflow/schema.js';
|
|
5
|
+
|
|
6
|
+
function argValue(name) {
|
|
7
|
+
const index = process.argv.indexOf(name);
|
|
8
|
+
return index >= 0 ? process.argv[index + 1] : null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const schemaPath = argValue('--schema');
|
|
12
|
+
const valuePath = argValue('--value');
|
|
13
|
+
if (!schemaPath || !valuePath) {
|
|
14
|
+
console.error('Usage: check-output-schema --schema <schema.json> --value <candidate.json>');
|
|
15
|
+
process.exit(2);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const schema = JSON.parse(readFileSync(schemaPath, 'utf8'));
|
|
20
|
+
const value = JSON.parse(readFileSync(valuePath, 'utf8'));
|
|
21
|
+
const schemaValidity = isValidOutputSchema(schema);
|
|
22
|
+
const result = schemaValidity.ok
|
|
23
|
+
? validateAgainstSchema(value, schema)
|
|
24
|
+
: { ok: false, errors: schemaValidity.issues };
|
|
25
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
26
|
+
process.exit(result.ok ? 0 : 1);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
process.stdout.write(`${JSON.stringify({ ok: false, errors: [error.message] })}\n`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
@@ -25,6 +25,8 @@
|
|
|
25
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.",
|
|
26
26
|
"meter": { "type": "none" },
|
|
27
27
|
"costRank": 1,
|
|
28
|
+
"preferredConcurrency": 1,
|
|
29
|
+
"$comment-preferredConcurrency": "OpenCode's shared local/provider session can terminate sibling headless runs together under parallel load. Prefer one in-flight OpenCode worker and route other ready siblings to healthy pools; if no alternative is eligible, availability wins and OpenCode may still be used.",
|
|
28
30
|
"lanes": ["analyze", "build", "chore"],
|
|
29
31
|
"capabilities": ["strong-analysis", "code-reading", "file-editing", "workflow-planning"],
|
|
30
32
|
"modelDiscovery": { "cmd": ["opencode", "models"], "parse": "lines", "includePattern": "^[^\\s]+/[^\\s]+$", "timeoutMs": 20000, "maxModels": 250 },
|
|
@@ -13,6 +13,8 @@ Every statement is tagged:
|
|
|
13
13
|
(`docs/experiments/2026-08-29-ultracode-vs-bullswarm.md`).
|
|
14
14
|
- **[INFERRED]** — my reading of how the harness must behave to satisfy the
|
|
15
15
|
spec. Not confirmed by source; treat as a hypothesis.
|
|
16
|
+
- **[IMPLEMENTED]** — behavior shipped in Bullswarm and backed by its source
|
|
17
|
+
and regression suite, rather than a claim about Claude's workflow contract.
|
|
16
18
|
|
|
17
19
|
## 0. The one-paragraph shape
|
|
18
20
|
|
|
@@ -349,7 +351,7 @@ author and the `Workflow` runtime.
|
|
|
349
351
|
exist — the script's `while (!ok)` loop *is* the evidence — which is the
|
|
350
352
|
general lesson: every piece of control flow bullswarm moves from planner
|
|
351
353
|
into runtime needs its evidence rule moved with it.
|
|
352
|
-
11. **[
|
|
354
|
+
11. **[IMPLEMENTED] Schema-enforced worker output** — a planner `run` action or fan-out
|
|
353
355
|
`stepTemplate` may declare an object-typed `outputSchema` subset. The
|
|
354
356
|
runtime appends instructions for one trailing matching JSON object, with no
|
|
355
357
|
prose or markdown fences after it, then parses and validates the object.
|
|
@@ -363,7 +365,10 @@ author and the `Workflow` runtime.
|
|
|
363
365
|
fields, and `fanout.itemsFrom` can consume `outputs.<id>.data.items` without
|
|
364
366
|
extraction when it is already an array. Planner decision validation rejects
|
|
365
367
|
`outputSchema` on a proposed `verify` because verify has a fixed verdict
|
|
366
|
-
shape.
|
|
368
|
+
shape. Schema-backed dispatches suppress ordinary same-pool retries so the
|
|
369
|
+
schema contract gets exactly its one bounded correction attempt. On resume,
|
|
370
|
+
a fan-out item is skipped only when both its verdict and declared schema
|
|
371
|
+
are satisfied; the schema must be declared on `stepTemplate.outputSchema`.
|
|
367
372
|
|
|
368
373
|
**Honest limitation.** `itemsFrom` removes the planner *turn*, not the stage
|
|
369
374
|
*barrier*: a verify depending on a data-driven fan-out waits for all items,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Bullswarm Dynamic Workflow Handoff
|
|
1
|
+
# Bullswarm Dynamic Workflow Handoff (Historical)
|
|
2
2
|
|
|
3
3
|
**Purpose:** iteration brief for making bullswarm's workflow system behave like
|
|
4
4
|
Claude Code's dynamic workflows while preserving bullswarm's provider routing,
|
|
@@ -6,8 +6,10 @@ quota pacing, content verification, and agent-friendly CLI contracts.
|
|
|
6
6
|
|
|
7
7
|
**Audience:** the next implementation agent.
|
|
8
8
|
|
|
9
|
-
**Status:**
|
|
10
|
-
|
|
9
|
+
**Status:** historical implementation brief from 2026-08-21. The gaps and task
|
|
10
|
+
list below describe the state at that date; they are not a current capability
|
|
11
|
+
matrix. For current behavior use `README.md`, `skill/SKILL.md`,
|
|
12
|
+
`docs/claude-dynamic-workflow-mechanics.md`, and the contextual CLI help.
|
|
11
13
|
|
|
12
14
|
## Executive Summary
|
|
13
15
|
|
|
@@ -23,10 +25,12 @@ understand request
|
|
|
23
25
|
-> repeat until complete
|
|
24
26
|
```
|
|
25
27
|
|
|
26
|
-
Bullswarm
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
Bullswarm now has the control loop this brief proposed: a durable orchestrator
|
|
29
|
+
observes completed work, proposes a bounded program, deterministic validation
|
|
30
|
+
accepts or rejects it, and the runtime schedules ready actions before the next
|
|
31
|
+
checkpoint. Static JSON workflows remain supported alongside zero-graph
|
|
32
|
+
`workflow goal` execution. The rest of this document preserves the historical
|
|
33
|
+
evidence and build rationale that led to that implementation.
|
|
30
34
|
|
|
31
35
|
The target is not an uncontrolled mutable DAG and not an LLM that owns the
|
|
32
36
|
runtime. The target is a hybrid:
|
|
@@ -235,6 +239,8 @@ Supported step types:
|
|
|
235
239
|
- `run`: one delegate invocation.
|
|
236
240
|
- `fanout`: one delegate invocation per item.
|
|
237
241
|
- `verify`: a skeptical review of a prior output artifact.
|
|
242
|
+
- `decide`: a durable adaptive planning gate whose proposal is validated before
|
|
243
|
+
any new action is appended or executed.
|
|
238
244
|
|
|
239
245
|
The implementation is mainly in:
|
|
240
246
|
|
|
@@ -276,8 +282,19 @@ A later step can reference them with templates:
|
|
|
276
282
|
{{outputs.previous.pool}}
|
|
277
283
|
{{outputs.previous.outputText}}
|
|
278
284
|
{{outputs.previous.outFile}}
|
|
285
|
+
{{outputs.previous.data.field}}
|
|
279
286
|
```
|
|
280
287
|
|
|
288
|
+
For a structured `run`, declare an object `outputSchema` when a later step
|
|
289
|
+
needs typed data. The successful state record contains `data` and
|
|
290
|
+
`schemaOk: true`; after the single schema correction retry fails it retains the
|
|
291
|
+
output text and contains `schemaOk: false` and `schemaErrors`. A schema retry is
|
|
292
|
+
observable as `action.output_schema_retry`, followed by
|
|
293
|
+
`action.output_validated` only when the corrected object passes validation.
|
|
294
|
+
The task also supplies an exact local schema-preflight command. The worker uses
|
|
295
|
+
it on a temporary candidate before replying, while the runtime independently
|
|
296
|
+
revalidates the captured response before exposing `data` downstream.
|
|
297
|
+
|
|
281
298
|
A fan-out can use a prior output file as its item source:
|
|
282
299
|
|
|
283
300
|
```json
|
|
@@ -290,8 +307,13 @@ A fan-out can use a prior output file as its item source:
|
|
|
290
307
|
}
|
|
291
308
|
```
|
|
292
309
|
|
|
293
|
-
The runtime
|
|
294
|
-
|
|
310
|
+
The runtime first consumes an already-recorded array such as
|
|
311
|
+
`outputs.discover.data.items`. For the legacy `outputs.<id>.outFile` form it
|
|
312
|
+
reads the referenced file and parses a JSON array. This is dynamic item
|
|
313
|
+
expansion, not dynamic workflow graph expansion. Fan-out schemas belong on
|
|
314
|
+
`stepTemplate.outputSchema`; each item stores its own `data`, `schemaOk`, and
|
|
315
|
+
possible `schemaErrors`, and resume re-runs only items whose verdict or schema
|
|
316
|
+
is incomplete.
|
|
295
317
|
|
|
296
318
|
### Routing and model selection
|
|
297
319
|
|
|
@@ -335,6 +357,7 @@ Bullswarm currently has:
|
|
|
335
357
|
- Recursion-depth propagation and guard
|
|
336
358
|
- Resume of successful steps
|
|
337
359
|
- Fan-out resume by item fingerprint
|
|
360
|
+
- Structured worker output with one schema retry and durable schema state
|
|
338
361
|
- Cooperative cancellation through `state.json`
|
|
339
362
|
- Heartbeats during long dispatches
|
|
340
363
|
- Basic interactive dashboard
|
|
@@ -346,6 +369,7 @@ Bullswarm currently has:
|
|
|
346
369
|
bullswarm doctor --json
|
|
347
370
|
bullswarm workflow capabilities --json
|
|
348
371
|
bullswarm workflow inspect <file-or-name>
|
|
372
|
+
bullswarm workflow runs result <shortId> --json
|
|
349
373
|
bullswarm workflow tui --json
|
|
350
374
|
bullswarm workflow tui --json <shortId>
|
|
351
375
|
bullswarm workflow tui --json --cancel <shortId>
|
|
@@ -360,7 +384,14 @@ bullswarm workflow inspect <file-or-name>
|
|
|
360
384
|
bullswarm workflow tui --json
|
|
361
385
|
```
|
|
362
386
|
|
|
363
|
-
## 3.
|
|
387
|
+
## 3. Historical Gaps, Ordered by Priority
|
|
388
|
+
|
|
389
|
+
This section is an as-built checklist from the original handoff. The adaptive
|
|
390
|
+
decision loop, bounded graph expansion, structured decisions, attempt ledger,
|
|
391
|
+
ordered event log, cancellation, and timeline/dashboard surfaces described
|
|
392
|
+
below are implemented now. The past-tense gap text is retained so reviewers can
|
|
393
|
+
trace requirements to the resulting runtime and tests; it must not be read as
|
|
394
|
+
current product status.
|
|
364
395
|
|
|
365
396
|
### P0: no observe-plan-execute loop
|
|
366
397
|
|
|
@@ -94,6 +94,19 @@ the internal `review` artifact path. Bullswarm now deterministically infers that
|
|
|
94
94
|
path for a verifier with one dependency and always appends the required JSON
|
|
95
95
|
verdict contract. This is deliberately runtime knowledge, not caller steering.
|
|
96
96
|
|
|
97
|
+
## Structured Output Evidence
|
|
98
|
+
|
|
99
|
+
For an optional object `outputSchema` on a `run`, the runtime appends the
|
|
100
|
+
contract to the worker prompt, parses the trailing object, and allows exactly
|
|
101
|
+
one schema correction retry. The durable `state.json` record exposes
|
|
102
|
+
`outputs.<id>.data` and `schemaOk`; a failed correction retains `schemaErrors`
|
|
103
|
+
and the output text. Fan-out item records use the same fields under
|
|
104
|
+
`outputs.<fanoutId>.items[]`. Offline regressions in
|
|
105
|
+
`tests/workflow-adaptive.test.js` and `tests/workflow-gaps.test.js` verify retry
|
|
106
|
+
events, persisted state, resume behavior, downstream rendering, and data-backed
|
|
107
|
+
fan-out. The stable result envelope returns the durable artifact content; typed
|
|
108
|
+
worker data remains inspectable in `state.outputs.<id>.data`.
|
|
109
|
+
|
|
97
110
|
## Acceptance interpretation
|
|
98
111
|
|
|
99
112
|
- Provider output is accepted by content verification, never by exit status alone.
|
package/docs/workflow-design.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# bullswarm Dynamic Workflows — Design
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
3
|
+
**Status:** implemented; historical design rationale retained · **Created:** 2026-08-21
|
|
4
4
|
|
|
5
5
|
## Problem
|
|
6
6
|
|
|
@@ -65,7 +65,7 @@ added as a second format without touching the runtime contract.
|
|
|
65
65
|
"steps": [
|
|
66
66
|
{
|
|
67
67
|
"id": "fanout-review", // required, unique
|
|
68
|
-
"type": "run", // run | fanout
|
|
68
|
+
"type": "run", // run | fanout | verify | decide
|
|
69
69
|
"taskFile": "/tmp/wf/{{runId}}/task-{{item}}.md",
|
|
70
70
|
"lane": "analyze",
|
|
71
71
|
"addDir": "{{inputs.targetDir}}",
|
|
@@ -105,6 +105,25 @@ Step fields (all pass through to the existing `run` pipeline):
|
|
|
105
105
|
- **`fanout`** — expand `stepTemplate` once per item from `itemsFrom`.
|
|
106
106
|
Items may be strings or objects (`{{item.path}}` paths work). Concurrency
|
|
107
107
|
capped by min(step, settings).
|
|
108
|
+
- **`verify`** — independently review a prior artifact and require structured
|
|
109
|
+
`{ok, concerns, summary}` evidence before dependent work may trust it.
|
|
110
|
+
- **`decide`** — give the durable orchestrator current intent, outputs,
|
|
111
|
+
failures, budgets, and capabilities. Its versioned proposal is validated
|
|
112
|
+
before bounded `run`, `fanout`, or `verify` actions enter the plan.
|
|
113
|
+
|
|
114
|
+
`run` and fan-out templates may declare `outputSchema` when later actions need
|
|
115
|
+
structured data. The schema is an object-typed JSON-Schema subset. A successful
|
|
116
|
+
run records `outputs.<id>.data` and `schemaOk: true`; a fan-out records the same
|
|
117
|
+
fields on each `outputs.<fanoutId>.items[]` entry. A mismatch gets one bounded
|
|
118
|
+
schema retry and records `schemaOk: false` plus `schemaErrors` if the retry also
|
|
119
|
+
fails. Ordinary prose should leave `outputSchema` unset; `verify` has its own
|
|
120
|
+
fixed verdict schema.
|
|
121
|
+
|
|
122
|
+
Before replying, a schema-bound worker receives the exact schema file and a
|
|
123
|
+
deterministic `check-output-schema` command for a temporary candidate object.
|
|
124
|
+
It must correct the candidate until that preflight exits zero and then emit the
|
|
125
|
+
validated object. The runtime validates the captured response again; worker
|
|
126
|
+
preflight reduces avoidable retries but never replaces the authoritative gate.
|
|
108
127
|
|
|
109
128
|
### Templating
|
|
110
129
|
|
|
@@ -134,9 +153,9 @@ artifacts + report + exit code
|
|
|
134
153
|
- **State**: `~/.bullswarm/workflows/<runId>/state.json` after every step —
|
|
135
154
|
crash-safe by construction.
|
|
136
155
|
- **Resume**: `workflow run --resume <runId>` skips steps whose saved verdict
|
|
137
|
-
is `ok:true
|
|
138
|
-
|
|
139
|
-
|
|
156
|
+
is `ok:true` and whose declared output schema, if any, is satisfied;
|
|
157
|
+
everything else re-runs. Fanout items resume by content fingerprint, so
|
|
158
|
+
already verified items remain complete even when discovery order changes.
|
|
140
159
|
- **Artifacts** per run: `state.json`, `report.json`, every task/out file.
|
|
141
160
|
|
|
142
161
|
## Terminal UX (the deliverable's face)
|
package/mcp/server.mjs
CHANGED
|
@@ -46,6 +46,13 @@ function result(id, r) {
|
|
|
46
46
|
write({ jsonrpc: '2.0', id, result: r });
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
let inputClosed = false;
|
|
50
|
+
let pendingCalls = 0;
|
|
51
|
+
|
|
52
|
+
function exitWhenDrained() {
|
|
53
|
+
if (inputClosed && pendingCalls === 0) process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
49
56
|
async function callTool(name, args) {
|
|
50
57
|
// Reuse the CLI verbs but capture stdout instead of leaking to our protocol
|
|
51
58
|
// stream: swap console.log for the duration of the call.
|
|
@@ -108,6 +115,7 @@ function handleMessage(line) {
|
|
|
108
115
|
result(id, { tools: TOOLS });
|
|
109
116
|
break;
|
|
110
117
|
case 'tools/call':
|
|
118
|
+
pendingCalls += 1;
|
|
111
119
|
callTool(params.name, params.arguments ?? {})
|
|
112
120
|
.then((r) => id != null && result(id, r))
|
|
113
121
|
.catch((err) =>
|
|
@@ -117,7 +125,11 @@ function handleMessage(line) {
|
|
|
117
125
|
id,
|
|
118
126
|
error: { code: -32603, message: err?.message ?? 'internal error' },
|
|
119
127
|
}),
|
|
120
|
-
)
|
|
128
|
+
)
|
|
129
|
+
.finally(() => {
|
|
130
|
+
pendingCalls -= 1;
|
|
131
|
+
exitWhenDrained();
|
|
132
|
+
});
|
|
121
133
|
break;
|
|
122
134
|
case 'ping':
|
|
123
135
|
result(id, {});
|
|
@@ -135,4 +147,7 @@ function handleMessage(line) {
|
|
|
135
147
|
|
|
136
148
|
const rl = createInterface({ input: process.stdin });
|
|
137
149
|
rl.on('line', handleMessage);
|
|
138
|
-
rl.on('close', () =>
|
|
150
|
+
rl.on('close', () => {
|
|
151
|
+
inputClosed = true;
|
|
152
|
+
exitWhenDrained();
|
|
153
|
+
});
|
package/package.json
CHANGED