@op1/threads 0.1.8 → 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 CHANGED
@@ -1,20 +1,32 @@
1
1
  # @op1/threads
2
2
 
3
- Managed top-level worker sessions for OpenCode 2.0.7. The server entrypoint is `index.ts`; the terminal entrypoint is `tui.ts`.
3
+ Managed top-level worker sessions and dynamic workflows for OpenCode V2. Dynamic workflows target OpenCode 2.0.12. The server entrypoint is `index.ts`; the terminal entrypoint is `tui.ts`.
4
4
 
5
- ## Install
5
+ ## Dynamic workflows
6
6
 
7
- Install the plugin globally:
7
+ Use `/workflow-run <task>` to have OpenCode author a JavaScript workflow with parallel agents, structured handoffs, and a durable run journal. `/workflows` opens the run navigator. Workers use native OpenCode conversations and configured agent profiles, including VERA roles.
8
+
9
+ Dynamic workflows are included in `@op1/threads` 0.2.0. The `v0.1.8` tag preserves the pre-workflow release.
10
+
11
+ Read [Run a dynamic workflow](docs/workflows.md) for progress, pause, stop, resume, worktree, and saved-script usage. The [runtime contract](skills/workflow-authoring/references/runtime.md) documents the authoring API and execution limits.
12
+
13
+ See [verification evidence](docs/workflows-verification.md) for the native checks and recovery guarantees.
14
+
15
+ See [capacity and limits](docs/workflow-capacity-findings.md) for total agent steps, concurrent workers, and measured load.
16
+
17
+ ## Install dynamic workflows
18
+
19
+ Install the versioned plugin globally:
8
20
 
9
21
  ```sh
10
- opencode plugin add @op1/threads
22
+ opencode plugin add @op1/threads@0.2.0
11
23
  ```
12
24
 
13
25
  Or add it to `plugins` in `~/.config/opencode/opencode.jsonc`:
14
26
 
15
27
  ```jsonc
16
28
  {
17
- "plugins": ["@op1/threads"]
29
+ "plugins": ["@op1/threads@0.2.0"]
18
30
  }
19
31
  ```
20
32
 
@@ -22,7 +34,7 @@ Keep the other entries in your plugin list. Open a fresh TUI to load the termina
22
34
 
23
35
  The `skills/managed-sessions` directory contains the VERA delegation guide. Copy or link it into `~/.config/opencode/skills/managed-sessions` to make it available to agents.
24
36
 
25
- For local development, clone [opzero1/threads](https://github.com/opzero1/threads), run `bun install`, and use the clone's absolute path as the plugin entry instead. The internal plugin ID remains `op-threads`, so switching between local and published installs preserves worker records.
37
+ The internal plugin ID remains `op-threads`, so switching between local and published installs preserves worker records. For local development, clone the `dynamic-workflows` branch, run `bun install`, and use the checkout's absolute path as the plugin entry.
26
38
 
27
39
  ## Delegate work
28
40
 
@@ -88,6 +100,8 @@ Send keys are scoped to the worker and determine a stable message ID. A retry wi
88
100
 
89
101
  Plugin option `maxWorkers` defaults to 4 and accepts integers from 1 through 32. Admission is serialized by coordinator within the loaded server process. A worker without a report continues to occupy a slot unless its native outcome is `failed` or `interrupted`. A successful run without a report does not silently free its slot.
90
102
 
103
+ Plugin option `workflowConcurrency` sets the default concurrency for new workflows. It accepts integers from 1 through 8 and defaults to 3. Plugin option `workflowMaxAgents` sets their total agent-step limit, accepts integers from 1 through 1,000, and defaults to 4. Explicit `workflows_start` values override these defaults. Existing runs retain their recorded limits. For eight concurrent agents with eight total steps, configure `"options": { "maxWorkers": 32, "workflowConcurrency": 8, "workflowMaxAgents": 8 }` on the plugin entry.
104
+
91
105
  ## Terminal and RPC
92
106
 
93
107
  The terminal synchronizes workers before opening native tabs without changing focus. A TUI memory index survives plugin reloads and respects manually closed native tabs. Conversations closed through Activity stay dismissed across restarts. `/threads` explicitly reopens workers for open coordinator tabs, including workers dismissed through Activity. A new TUI recovers the other workers from durable storage. Closing the TUI does not interrupt workers.
@@ -183,6 +197,8 @@ Run `bun run verify:activity` to exercise Activity enabled against an isolated i
183
197
 
184
198
  Run `bun run verify:roles` to verify named profiles against the native server and deterministic model endpoint. It checks actual system prompts, model variants, native delegation, read-only execution, reporting, and role-aware retries.
185
199
 
200
+ Run `bun run verify:workflows` to exercise dynamic scripts through real OpenCode tools and sessions. The isolated fixture checks structured pipelines, exact retries, role restrictions, ownership, checkpoints, saved scripts, retained worktrees, service restart, and the terminal navigator.
201
+
186
202
  Run `bun run verify:idle-tabs` to open two native terminals on different idle sessions in the same directory and verify that their shared tab order stays stable.
187
203
 
188
204
  Pass an extracted package directory to test the release artifact: `bun run verify:live /absolute/path/to/package`.
@@ -0,0 +1,46 @@
1
+ # Dynamic workflows implementation
2
+
3
+ ## Completion criteria
4
+
5
+ An OpenCode V2 session can submit a JavaScript workflow, continue its conversation while agents run, inspect phases and worker sessions, receive validated results, pause or stop, and resume recorded work after a service restart. Writing steps can use retained worktrees. Saved workflows can run again with different arguments. VERA profiles retain their configured models and permissions. A failed or inconclusive task cannot silently become a passing workflow.
6
+
7
+ Verification covers the confined interpreter, the durable runner, actual OpenCode session and tool calls, restart recovery, and the terminal controls. A final real-model run exercises workflow authoring and structured handoffs. An independent reviewer inspects the implementation and evidence before completion.
8
+
9
+ ## Baseline
10
+
11
+ - Threads: `df7686af1c6601ae7d05ae607bc584f185109231`, annotated tag `v0.1.8`, already present at `opzero1/threads`.
12
+ - Dotfiles: snapshot the current tracked setup and intended additions in `afif-reap/dotfiles` before changing VERA guidance.
13
+ - Installed OpenCode: `2.0.12`.
14
+
15
+ ## Ownership
16
+
17
+ Implement workflow modules in the existing Threads package. This shares the worker service and its ownership checks without requiring an unauthenticated cross-plugin dispatch API. The `workflows` tool namespace and RPC remain distinct from the existing `threads` interface.
18
+
19
+ VERA selects the process and owns the final engineering verdict. The workflow runner schedules steps, validates handoffs, journals progress, and reconciles interrupted work. OpenCode owns model execution, permissions, tools, and worktrees. The TUI renders server state and links to native sessions.
20
+
21
+ Use the published `@opencode/codemode` package pinned to `2.0.12` for confined execution. The initial reference checkout marked its older package private; the published V2 package has a supported export. Generated scripts never run through host `eval`, `Function`, or Node `vm`.
22
+
23
+ ## Protocol
24
+
25
+ 1. Checkpoint the repositories and capture the existing test baseline.
26
+ 2. Prove the confined script adapter with real interpreter tests: structured fan-out, per-item pipelines, deterministic inputs, cancellation, invalid scripts, and failures.
27
+ 3. Implement the durable runner and worker adapter. Persist dispatch identities before starting work. Record validated results before returning them to scripts. Reconcile existing worker sessions on resume. Reject concurrent execution of the same run.
28
+ 4. Integrate role authorization, structured result reporting, retained worktrees, concurrency and call limits, measured usage, saved workflows, and bounded retry helpers.
29
+ 5. Add workflow tools, server commands, RPC, terminal navigation, phase and step progress, and pause, stop, resume, and checkpoint controls.
30
+ 6. Add the workflow-authoring and VERA recipes. Verify actual OpenCode sessions and terminal interactions in isolated fixtures, then a scoped real-model run.
31
+ 7. Inspect the complete diff, independently audit behavior and the decision trail, and resolve accepted findings.
32
+
33
+ ## Recovery contract
34
+
35
+ The journal records a run, its script and arguments, each named step's request fingerprint, worker identity, execution outcome, validated result, and evidence. A restart does not blindly replay user-visible effects. Resume first reconciles an existing worker and any recorded result. An interrupted write with uncertain state requires inspection rather than an automatic fresh worker.
36
+
37
+ Script control flow uses only arguments and recorded step results. Reject clock and randomness access. A run's script and arguments are immutable. Resume reuses matching named steps; a changed request under the same key fails closed. An edited saved script starts a new run. This avoids reusing results whose dependencies changed through untracked script control flow. Worktree paths and results survive completion. Integration is a distinct verified action.
38
+
39
+ ## Reference guidance
40
+
41
+ - [Pi Dynamic Workflows](https://github.com/QuintinShaw/pi-dynamic-workflows): code orchestration, role routing, journaling, worktrees, and interactive progress.
42
+ - [Devin Dynamic Workflows](https://docs.devin.ai/work-with-devin/dynamic-workflows): structured per-item pipelines and when workflows are useful.
43
+ - [Claude Code workflows](https://code.claude.com/docs/en/workflows): deterministic scripts, replay semantics, saved commands, background controls, and validation before spawning.
44
+ - [OpenCode V2 plugins](https://opencode.ai/v2/docs/build/plugins): supported host integration.
45
+
46
+ The references inform the behavior. Existing OpenCode and VERA ownership, permissions, and evidence requirements determine the implementation.
@@ -0,0 +1,47 @@
1
+ # Workflow capacity findings
2
+
3
+ The native load harness uses an isolated OpenCode service and a deterministic local provider. It makes no paid model calls. The package uses `@opencode/plugin` and `@opencode/schema` 2.0.7, with `@opencode/codemode` 2.0.12.
4
+
5
+ ## Exact bounds
6
+
7
+ - Workflow concurrency defaults to 3 and accepts 1–8. The plugin option `workflowConcurrency` overrides the default for new runs; explicit run values take precedence.
8
+ - A workflow defaults to 4 agents and accepts 1–1,000 total agent steps. The plugin option `workflowMaxAgents` overrides the default for new runs; explicit run values take precedence.
9
+ - Worker and run timeouts accept 1 second through 7 days. Defaults are 30 minutes per worker and 24 hours per run.
10
+ - Threads `maxWorkers` defaults to 4 and accepts 1–32. It is both the unfinished managed-worker admission limit and the owner-wide workflow execution limit. A run's effective concurrency is therefore `min(concurrency, maxWorkers)`, with `maxWorkers` shared by simultaneous workflows owned by one session.
11
+ - Workflow workers are top-level native OpenCode sessions. Workflow workers cannot delegate, start workflows, or spawn managed workers. Managed Threads workers may use native subagents if their permissions allow it, but cannot spawn another managed worker.
12
+ - Saved workflow nesting permits four `workflow()` boundaries; the fifth fails. Cumulative host calls are bounded at `maxAgents * 8 + 100`, shared with nested calls. Arguments, host-call payloads, results, checkpoint responses, and each durable worker report are limited to 1 MiB.
13
+ - The runtime module admits 64 simultaneous interpreter calls, including nested calls. These Bun workers execute scripts; they are separate from native agent sessions. Excess calls fail immediately rather than waiting for nested work to free a slot.
14
+ - The durable run record is limited to 16 MiB, including embedded settlement order. Payload admission reserves at least 64 KiB for control metadata, with additional space for outstanding step and settlement diagnostics. The reservation reduces usable payload capacity.
15
+ - Progress logs retain the newest 200 entries, each truncated to 2,000 characters. Checkpoint prompts are truncated to 10,000 characters. Reports allow at most 100 evidence strings; summary and evidence strings are each limited to 20,000 characters.
16
+
17
+ ## Measured capacity
18
+
19
+ `bun run verify:workflow-capacity --steps 1000 --timeout 900` passes all eight scenarios on OpenCode 2.0.14 and Bun 1.4.0:
20
+
21
+ | Measurement | Result |
22
+ | --- | --- |
23
+ | Sustained run | 1,000 steps, 1,000 unique native worker sessions, ordered results |
24
+ | Sustained elapsed time | 229.823 seconds |
25
+ | Owner pool | Two runs share eight concurrent workers; 16 unique sessions across both runs |
26
+ | Service RSS at eight blocked workers | 565,493,760 bytes, about 539 MiB |
27
+ | Pause | 0.242 seconds; eight active workers drain, eight queued workers do not start |
28
+ | Stop | 0.120 seconds; eight active native requests interrupted |
29
+ | One-second worker deadline | Failure observed after 1.321 seconds, including dispatch and teardown |
30
+ | One-second run deadline | Failure observed after 1.149 seconds |
31
+ | Host-call boundary | 108 accepted; 109 rejected when `maxAgents` is 1 |
32
+ | Log retention | Newest 200 of 205 entries retained |
33
+ | Cleanup | Zero active sessions; all 1,016 worker identities from the sustained and shared-pool scenarios retained |
34
+
35
+ The measured source hash is `1414fd9d27ead4a1f955f11168917842aaef043a8eec172b7bd73699aaf3e414`. These timings describe a deterministic provider on one machine, not real-model throughput. RSS is one service sample, not peak memory. The result does not establish capacity at 32 workers, across multiple owners, or over a seven-day run.
36
+
37
+ Separate native regressions exercise the nesting boundary. Focused tests exercise byte-size limits and simultaneous failure diagnostics. The 35-cycle runtime soak checks CPU termination and native thread cleanup; it does not load-test 64 simultaneous interpreters.
38
+
39
+ ## Native OpenCode limits
40
+
41
+ The inspected V2 schema defines agent `steps` as a positive integer and exposes no global native-session concurrency ceiling. This is not proof of unlimited capacity. `experimental.subagent_depth` defaults to 1. These native-generation controls are separate from workflow concurrency and total steps. The user's current configuration sets depth to 2 and the `general` agent to 20 steps.
42
+
43
+ ## Practical recommendation
44
+
45
+ Use at most eight workflow workers per owner, keep the default four for ordinary interactive use, and increase to eight only for an isolated load or known I/O-bound work. Prefer batches of 100–250 concise steps even though 1,000 are admitted. Store large evidence in artifact files and return paths. Keep reports far below 1 MiB so the 16 MiB aggregate journal retains headroom. Do not treat the configured maximum of 32 managed workers as a verified operating target.
46
+
47
+ The harness writes measured evidence to `.audit/workflow-capacity/evidence.json`. The capacity fixture explicitly configures eight workers. The plugin's four-worker default can be overridden with `maxWorkers`.
@@ -0,0 +1,105 @@
1
+ # Dynamic workflow verification
2
+
3
+ The hardening pass targets the seven defects reproduced against `5858de1`, plus storage and timeout races found during integration. The original blanket readiness claim is superseded by these checks and the limits below.
4
+
5
+ Current verification uses OpenCode `2.0.14`, Bun `1.4.0`, and published `@opencode/codemode` `2.0.12`. Earlier baseline and failing-before evidence used OpenCode `2.0.12`.
6
+
7
+ ## Version 0.2.0 release verification
8
+
9
+ The release adds configurable `workflowConcurrency` and `workflowMaxAgents` defaults for new runs. Explicit run values take precedence, and resumed records retain their original limits. The persisted schema defaults remain 3 concurrent agents and 4 total steps.
10
+
11
+ Release verification passed on OpenCode 2.0.14:
12
+
13
+ - `bun run typecheck` and all 109 unit tests / 407 assertions.
14
+ - 13 native regression cases against the extracted npm package, including configured 8/8 defaults, explicit 2/2 overrides, eight completed steps, and resumed 3/4 limits.
15
+ - 27 native workflow/TUI checks and 27 managed-session checks against the extracted package with fresh production dependencies.
16
+ - Independent source review: **PASS WITH NOTES**, with no release-blocking findings.
17
+
18
+ The packed source hash is `3b5b77f200d79164227c3caeb86eeab95f4c1188b8f7d5c1f689296e1022df72`. The package includes both entrypoints, all runtime worker modules, and the authoring skill with its runtime reference. Local release artifacts are in `.audit/release-0.2.0/`.
19
+
20
+ ## Hardening checkpoint checks
21
+
22
+ | Command | Result | Coverage |
23
+ | --- | --- | --- |
24
+ | `bun run typecheck` | Pass | Server, runtime, and terminal types |
25
+ | `bun test` | 109 tests, 407 assertions pass | Confinement, replay, checkpoint atomicity, journal limits, deadline races, failed-attempt accounting, and existing Threads behavior |
26
+ | `bun run verify:workflow-regressions` | 12 cases pass | Original engine defects, CPU-bound deadline, saved and nested execution, composition helpers, and retained-worktree handoff |
27
+ | `bun run verify:roles` | 17 checks pass | Configured profiles, inherited restrictions, role admission, and native delegation |
28
+ | `bun run verify:live` | 27 checks pass | Managed-worker lifecycle, reports, limits, native tabs, and restart recovery |
29
+ | `bun run verify:workflow-runtime` | Pass | 35 CPU-bound interpreter cancellations, stable native thread count, and idle CPU |
30
+ | `python3 scripts/verify-workflows-model.py` | Pass | Model-authored workflow, real VERA readers, validated handoffs, and automatic coordinator notification |
31
+ | `bun run verify:workflows` | 27 checks pass | Native workflow lifecycle, permissions, hard restart, uncertain writes, fresh navigator snapshots, keyboard step navigation, and terminal controls |
32
+ | `bun run verify:workflow-capacity --steps 1000 --timeout 900` | 8 scenarios pass | Eight concurrent workers, 1,000 unique native sessions, owner pool sharing, controls, deadlines, and cleanup |
33
+
34
+ Native harnesses use real isolated OpenCode services with deterministic local providers. These fixtures prove execution behavior rather than model quality. The separate real-model check uses the installed plugin and configured VERA profiles.
35
+
36
+ Each workflow, regression, and capacity harness records a hash of `index.ts`, `tui.ts`, `package.json`, and direct TypeScript sources in `src/`. It rejects source changes during its run. Local artifacts are retained under `.audit/` and are gitignored:
37
+
38
+ - `workflows/evidence.json` and `workflows/tui.screen.txt`
39
+ - `workflow-regressions/evidence.json` and `workflow-regressions/evidence.baseline.json`
40
+ - `workflow-capacity/evidence.json`
41
+ - `runtime-soak/evidence.json`
42
+ - `workflow-model/evidence.json`
43
+
44
+ The commands regenerate the evidence. Raw transcripts and temporary session directories are not published.
45
+
46
+ The final workflow, regression, and 1,000-step capacity runs all verified source hash `1414fd9d27ead4a1f955f11168917842aaef043a8eec172b7bd73699aaf3e414`.
47
+
48
+ ## Regression and recovery proof
49
+
50
+ The native regression harness first reproduced six engine failures on the old source. The runtime tests separately reproduced the synchronous deadline failure. All twelve native cases now pass:
51
+
52
+ 1. Queued agents cannot dispatch after the measured token budget is exhausted.
53
+ 2. Failed attempts count toward that budget.
54
+ 3. Answering one checkpoint preserves `waiting` when another remains unanswered.
55
+ 4. Concurrent checkpoint responses replay in their recorded order after restart.
56
+ 5. An exposed failure remains a failure on replay, preserving the script's fallback branch.
57
+ 6. A crash after an accepted report requires explicit same-worker resolution.
58
+ 7. Saved scripts accept new arguments and retain pinned nested source after restart.
59
+ 8. Composition helpers execute through the native service.
60
+ 9. Saved commands register and refresh.
61
+ 10. A fifth nested workflow boundary fails.
62
+ 11. A writer and verifier use the same retained worktree.
63
+ 12. A CPU-bound script reaches its deadline while service RPC remains responsive.
64
+
65
+ The hard-crash fixture appends one line, blocks before its report, and kills the service. After restart, no provider request is allowed until explicit recovery. Resume preserves the uncertain write. A follow-up asks the same worker to inspect and report its existing effect; the final file still has exactly one line.
66
+
67
+ Checkpoint responses and settlement order are committed atomically. Focused tests reject oversized responses before persistence, allow a smaller retry, and retain a concurrently committed agent settlement. Legacy external journals remain preserved during migration. Payload admission reserves diagnostic space while retaining the 16 MiB hard limit.
68
+
69
+ The macOS runtime soak starts and cancels 35 CPU-bound interpreters. Native thread count returns from 25 to 25, and the following idle second consumes 2.157 ms of process CPU. The check uses the real CodeMode interpreter inside terminable Bun workers.
70
+
71
+ ## Independent review
72
+
73
+ Independent review is a release gate. The initial audit rejected unqualified readiness and supplied executable counterexamples. Later review found a constructor-failure capacity leak, checkpoint journal poisoning, insufficient control headroom, and recovery failures that affected healthy sibling runs. Those findings received focused regression tests and fixes.
74
+
75
+ The final integrated engine/store review returned **PASS WITH NOTES**, with no blocking findings. Its remaining timer-cleanup finding was reproduced and fixed: a full legacy journal now clears the deadline and notifies the owner even when failure persistence also fails. The focused test preserves both original records.
76
+
77
+ A separate UI reviewer identified the stale navigator snapshot, then returned **PASS WITH NOTES** after the fix. Explicit refreshes are serialized, background requests coalesce, and responses are guarded across owner navigation. The native terminal suite passes all 27 checks. The A→B→A generation guard has source review but no dedicated navigation regression.
78
+
79
+ Accepted review limits include unsupported selective deletion of journal records, replay-order mismatches waiting until the run deadline, and reliance on a single loaded scheduler implementation. Checkpoint responses appear both in the checkpoint and its settlement entry, so they count twice toward the journal limit. A nearly full run can reject even a small response while preserving the unanswered checkpoint. Passing implementation-worker reports alone are not treated as independent approval.
80
+
81
+ ## Real-model verification
82
+
83
+ Run `wfr_8eafe136e930d91c0127b44532be49a4` completed through coordinator `ses_f35910071ffe0I7E0NR3Bzfvfk`, using `vera-core`:
84
+
85
+ - `vera-operator-readonly`, `openai/gpt-5.6-sol#low`, read `src/workflow-types.ts`.
86
+ - `vera-engineer-readonly`, `openai/gpt-5.6-sol#medium`, read `src/workflow-rpc.ts`.
87
+ - Validated result: `{"limits":{"concurrency":3,"maxAgents":4},"controls":["pause","resume","stop"]}`.
88
+
89
+ Both worker models matched their profiles and journal records. Their transcripts contain successful reads and accepted results. After the automatic notification, the coordinator inspected the run and delivered a PASS receipt.
90
+
91
+ ## Execution boundaries
92
+
93
+ - One OpenCode service with one loaded scheduler implementation owns scheduling. Multiple services or duplicate module instances sharing storage are unsupported.
94
+ - Interrupted writes require inspection and same-worker resolution. A missing worker does not authorize repeating its effects.
95
+ - Scripts, arguments, and nested scripts are immutable within a run. Changes require a new run key.
96
+ - Native failures and explicit `FAIL` or `INCONCLUSIVE` reports prevent completion, even when the script catches them.
97
+ - Token budgets govern dispatch using reported usage. In-flight work can exceed the threshold; unmeasured usage blocks further budgeted dispatch.
98
+ - Old journals without checkpoint settlement order cannot replay answered checkpoints deterministically. They fail with a diagnostic rather than inventing an order.
99
+ - Corrupt and exact-limit legacy records retain their evidence and produce owner-visible diagnostics. A full legacy record may require explicit repair or a new run key.
100
+ - Selectively deleting a run record while retaining its legacy completion journal is unsupported. Both records belong to the same run identity.
101
+ - An inconsistent settlement order can wait until the run deadline. Recovery does not invent missing completions to make the script advance.
102
+ - Interpreter termination stops script CPU work. Parent-side host effects remain subject to native interruption and uncertain-write recovery.
103
+ - Worktrees remain available for inspection and integration. The coordinator owns the final engineering verdict.
104
+
105
+ The [capacity findings](workflow-capacity-findings.md) distinguish cumulative sessions, concurrent agents, interpreter calls, and measured operating limits.
@@ -0,0 +1,60 @@
1
+ # Run a dynamic workflow
2
+
3
+ Describe the work with `/workflow-run`. OpenCode loads the authoring contract, writes a JavaScript script, and starts the run in the background.
4
+
5
+ ```text
6
+ /workflow-run Audit src/auth and src/billing for missing authorization checks. Use VERA reviewers, confirm each finding independently, and return source references.
7
+ ```
8
+
9
+ The run has a stable ID. Your conversation stays available while its agents work. Each worker uses a native OpenCode session with its selected profile, model, and permissions.
10
+
11
+ ## Inspect progress
12
+
13
+ 1. Run `/workflows`.
14
+ 2. Select a run to open its panel.
15
+ 3. Select a step with the arrow keys and press **Enter**, or click it, to open its native worker conversation.
16
+
17
+ The panel shows phases, step outcomes, verdicts, evidence, retained directories, recorded usage, and the final result. Step counts include work recorded so far; a dynamic script can add more steps. Press `f` for the full-screen view or `Esc` to close the panel.
18
+
19
+ The navigator lists every run owned by the current coordinator session, including completed and failed runs. The detail panel shows one selected run. Other coordinator sessions have their own run lists.
20
+
21
+ Ask the agent to inspect the run when you need its complete saved script or structured handoffs:
22
+
23
+ ```text
24
+ Inspect that workflow and summarize the confirmed findings and missing evidence.
25
+ ```
26
+
27
+ ## Pause, stop, and resume
28
+
29
+ Press `p` in the run panel to pause new scheduling. Active steps finish before the run becomes paused.
30
+
31
+ Press `x` to stop the run and interrupt its active workers. Completed results and worktree directories remain available.
32
+
33
+ Press `r` to resume. For a waiting checkpoint, enter a JSON response. For example, enter `true`, `42`, or a quoted string.
34
+
35
+ After a service restart, reopen the original conversation and select the run with `/workflows`. Resume reconciles its existing sessions before scheduling more work. It does not assume that an interrupted write left the directory unchanged.
36
+
37
+ For an uncertain write, ask the agent to inspect the retained worker and directory. Send the resolution request to that same worker. After the worker reports and finishes, resume the run. Use the [recovery contract](../skills/workflow-authoring/references/runtime.md#start-and-control) for crash and legacy-journal limits.
38
+
39
+ ## Save a useful workflow
40
+
41
+ 1. Select its run in `/workflows`.
42
+ 2. Press `s`.
43
+ 3. Enter a new name.
44
+ 4. Select **Project** or **User**.
45
+
46
+ The saved file contains the script. Run arguments and worker conversations stay in the original run. Existing files are not overwritten.
47
+
48
+ Invoke `/workflow-<name>` to reuse the script with new input. Project scripts live in `.opencode/workflows/`. Personal scripts live in the `workflows/` directory under your OpenCode configuration.
49
+
50
+ An edited script starts a new run. A run's script and arguments remain fixed so resuming it cannot silently reuse results from different instructions.
51
+
52
+ After editing a saved file directly, run `/workflow-refresh` to reload its command. The names `run` and `refresh` are reserved.
53
+
54
+ ## Use VERA roles
55
+
56
+ Ask for VERA when the task needs its engineering and evidence rules. The workflow chooses configured role IDs such as `vera-engineer` and `vera-auditor-readonly`; `/setup-vera` remains the place to change their models.
57
+
58
+ Keep an implementation and its runtime verification on the same retained worktree. Give the auditor the changed paths and verification evidence. The root conversation inspects the artifacts and owns integration and the final verdict.
59
+
60
+ For script syntax, limits, tools, and recovery semantics, read the [runtime contract](../skills/workflow-authoring/references/runtime.md). The [implementation plan](dynamic-workflows-plan.md) describes module ownership and verification requirements.
package/index.ts CHANGED
@@ -3,6 +3,7 @@ import type { SessionContext } from "@opencode/plugin/promise/session";
3
3
  import { z } from "zod";
4
4
  import { Report, ThreadsRpc, WorkerView } from "./src/rpc";
5
5
  import { Send, Spawn, WorkerTarget, threads } from "./src/threads";
6
+ import { workflows } from "./src/workflows";
6
7
 
7
8
  export default Plugin.define({
8
9
  id: "op-threads",
@@ -121,5 +122,6 @@ export default Plugin.define({
121
122
  },
122
123
  });
123
124
  });
125
+ return workflows(ctx, workers, models, limit);
124
126
  },
125
127
  });
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@op1/threads",
3
- "version": "0.1.8",
4
- "description": "Visible top-level worker sessions for OpenCode V2, with native tabs and durable reports.",
3
+ "version": "0.2.0",
4
+ "description": "Visible worker sessions and durable dynamic workflows for OpenCode V2.",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
7
7
  "exports": {
8
8
  ".": "./index.ts",
9
9
  "./tui": "./tui.ts"
10
10
  },
11
- "files": ["index.ts", "tui.ts", "src/", "skills/"],
11
+ "files": ["index.ts", "tui.ts", "src/", "skills/", "docs/workflows.md", "docs/workflows-verification.md", "docs/workflow-capacity-findings.md", "docs/dynamic-workflows-plan.md"],
12
12
  "repository": {
13
13
  "type": "git",
14
14
  "url": "git+https://github.com/opzero1/threads.git"
@@ -28,11 +28,19 @@
28
28
  "verify:roles": "python3 scripts/verify-roles.py",
29
29
  "verify:idle-tabs": "uv run --with pyte python scripts/verify-idle-tabs.py",
30
30
  "verify:tabs": "uv run --with pyte python scripts/verify-tab-groups.py",
31
- "verify:activity": "uv run --with pyte python scripts/verify-activity.py"
31
+ "verify:activity": "uv run --with pyte python scripts/verify-activity.py",
32
+ "verify:workflows": "uv run --with pyte python scripts/verify-workflows.py",
33
+ "verify:workflow-regressions": "python3 scripts/verify-workflow-regressions.py",
34
+ "verify:workflow-capacity": "python3 scripts/verify-workflow-capacity.py",
35
+ "verify:workflow-runtime": "bun scripts/verify-workflow-runtime.ts"
32
36
  },
33
37
  "dependencies": {
38
+ "@opencode/codemode": "2.0.12",
34
39
  "@opencode/plugin": "2.0.7",
35
40
  "@opencode/schema": "2.0.7",
41
+ "acorn": "8.15.0",
42
+ "ajv": "8.17.1",
43
+ "effect": "4.0.0-rc.112",
36
44
  "fuzzysort": "3.1.0",
37
45
  "zod": "4.1.8"
38
46
  },
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: Workflow authoring
3
+ description: Author and run durable dynamic JavaScript workflows in OpenCode with parallel agents, structured handoffs, and resumable progress.
4
+ ---
5
+
6
+ # Workflow authoring
7
+
8
+ Use a workflow for broad independent work or a pipeline whose later steps consume earlier results. Keep small, tightly coupled tasks with one owner.
9
+
10
+ 1. Define the result, named slices, and verification predicate. For VERA tasks, retain the selected protocol and role profiles.
11
+ 2. Read [the runtime contract](references/runtime.md). Write a script with a literal `export const meta` first, named agent steps, and a final JSON result.
12
+ 3. Call `workflows_start` with a unique task key, `script` or saved `name`, JSON `args`, and the smallest useful concurrency and agent limit. It returns immediately.
13
+ 4. Continue independent work. The runner delivers a final notification. Use `workflows_inspect` for evidence and `workflows_control` for pause, stop, resume, and checkpoint responses. Never infer success from an idle worker.
14
+ 5. Inspect the result and the affected artifacts before giving the final verdict. Save a useful script with `workflows_save`; its arguments and transcripts are not saved with it.
15
+
16
+ Every step selects an actual configured `agent`. Use `vera-operator-readonly` for discovery, `vera-engineer` for implementation, and `vera-auditor-readonly` for independent review. Model choices remain in the profiles. Workflow workers are leaves. The runner bounds the complete run, including nested workflows.
17
+
18
+ Use `access: "read"` for investigations. It denies edits and shell commands. Real command-based verification needs a suitably permitted worker with `access: "write"`, even when its intended task is only running tests. Use `isolation: "worktree"` for independent implementations and return the retained directory and changed paths. Give subsequent verification the same directory. The root owns integration.
19
+
20
+ Verification evidence is part of the handoff. `FAIL` and `INCONCLUSIVE` cannot establish a passing step. Do not replace execution evidence with reviewer votes. A workflow does not expand the user's authorization for external actions.
21
+
22
+ ## Example
23
+
24
+ ```javascript
25
+ export const meta = {
26
+ name: "module-audit",
27
+ description: "Audit named modules and return source-backed findings",
28
+ };
29
+
30
+ await phase("Audit");
31
+ const findings = await pipeline(args.modules, module => agent(
32
+ `Audit ${module} for ${args.check}. Read the actual source. Return concrete findings with paths and line numbers.`,
33
+ {
34
+ key: `audit:${module}`,
35
+ agent: "vera-engineer-readonly",
36
+ access: "read",
37
+ schema: {
38
+ type: "object",
39
+ properties: { findings: { type: "array", items: { type: "string" } } },
40
+ required: ["findings"],
41
+ additionalProperties: false,
42
+ },
43
+ },
44
+ ));
45
+ return findings;
46
+ ```
47
+
48
+ Invoke with structured arguments, for example `args: { modules: ["src/auth", "src/billing"], check: "missing authorization checks" }`. Set `maxAgents` to cover the named slices and any verification steps. Built-in defaults are four total agents and three concurrent agents; plugin options can override them. The `workflows_start` schema shows the configured defaults.
49
+
50
+ The example returns candidate findings. Add an independent confirmation stage when the task requires a verified report.
@@ -0,0 +1,87 @@
1
+ # Workflow runtime contract
2
+
3
+ Scripts run in OpenCode's confined Code Mode interpreter inside a terminable Bun worker. The parent service enforces the deadline, including during synchronous script work. Scripts cannot import modules or use host filesystem, process, network, clock, or randomness APIs. Agents inspect the outside world and return recorded data. Use arguments for timestamps and other variable inputs.
4
+
5
+ Keep each result below 1 MiB and the combined run journal below 16 MiB. Payload admission reserves space for control metadata and bounded failure diagnostics, so usable payload capacity is lower. Return concise structured findings and artifact paths instead of complete file contents. Run lists and completion notifications omit the full result; inspect a run to read it.
6
+
7
+ ## Script shape
8
+
9
+ The first statement is `export const meta = { name: "name", description: "description" }`. Metadata contains literal values. An optional `phases` array contains objects with a `title`.
10
+
11
+ The body supports top-level `await`, ordinary data transformations, branching, and bounded loops. Return a JSON value. Intermediate agent results stay in script variables and the durable journal.
12
+
13
+ ## Agent steps
14
+
15
+ `await agent(prompt, options)` returns the validated `result` submitted by its worker. Options are:
16
+
17
+ | Field | Meaning |
18
+ | --- | --- |
19
+ | `key` | Required unique, stable step name. Include the item and round when using loops. |
20
+ | `agent` | Required configured OpenCode agent ID. |
21
+ | `label` | Optional display title. |
22
+ | `phase` | Optional phase override. |
23
+ | `schema` | Optional JSON Schema for the result. Invalid schemas fail before dispatch. |
24
+ | `access` | `read` by default: only the profile's permitted read, glob, grep, webfetch, websearch, and skill tools. `write` permits the selected profile's other tools. |
25
+ | `isolation` | `shared` by default, or `worktree` for a retained isolated checkout. Creating a worktree requires `access: "write"`; later readers can use its returned `directory`. |
26
+ | `directory` | Optional existing directory inside the owner project or one of its registered worktrees. Symlinks are resolved before containment checks. |
27
+ | `timeoutMs` | Optional step timeout, bounded by the run's execution policy. |
28
+
29
+ Workers submit `workflows_result({ verdict, summary, evidence, result })`. Invalid results return a validation error so the worker can repair its output. A native execution that ends without a result has no task verdict.
30
+
31
+ Profile restrictions and the coordinator's restrictions still apply. Read access blocks other plugin and MCP tools, including those with side effects, even if the selected profile permits them. Workflow workers cannot delegate. Selecting `access: "write"` does not grant permissions that the selected profile lacks.
32
+
33
+ ## Composition
34
+
35
+ - `parallel(thunks)` runs async functions concurrently and returns results in input order.
36
+ - `pipeline(items, ...stages)` runs each item through its stages independently. A fast item does not wait for slower items between stages.
37
+ - `await phase(title)` records a display phase.
38
+ - `await log(message)` records a bounded progress message.
39
+ - `workflow(name, args)` invokes a saved workflow within the parent's limits.
40
+ - `retry(thunk, { attempts })` bounds logical or validation retries, with three attempts by default. Give each agent attempt a distinct step key. Return expected negative findings as validated data, then let the validator decide whether another attempt is useful. A native execution failure or an explicit `FAIL`/`INCONCLUSIVE` report remains an unresolved failure and prevents the run from passing, even when caught by the script. Inspect uncertain writes before starting a replacement run.
41
+ - `gate(thunk, validator, { attempts })` repeats until the validator accepts or attempts run out, with three attempts by default. The validator returns a boolean or `{ ok: boolean, feedback?: string }`. A truthy object without `ok: true` does not pass.
42
+ - `loopUntilDry({ round, key, consecutiveEmpty, maxRounds })` accumulates unique findings until discovery stops producing new items. `round` and `key` are required; `key` is a property name or identity function. Defaults are two consecutive empty rounds and ten maximum rounds. Reaching the maximum returns the accumulated findings.
43
+ - `checkpoint(prompt, { key })` records a question and waits for an explicit response through the run controls. The response becomes recorded input on resume. Responses are limited to 1 MiB and must fit in the aggregate journal. Rejected responses leave the checkpoint unanswered so a smaller response can be submitted.
44
+
45
+ Errors remain errors unless the script explicitly handles them. Do not discard failed items or substitute a passing result for missing evidence.
46
+
47
+ ## Start and control
48
+
49
+ `workflows_start` accepts exactly one of `script` or saved `name`, plus a unique task `key`, optional JSON `args`, and limits:
50
+
51
+ | Limit | Default | Range |
52
+ | --- | --- | --- |
53
+ | `concurrency` | 3 | 1–8 |
54
+ | `maxAgents` | 4 | 1–1000 |
55
+ | `agentTimeoutMs` | 30 minutes | 1 second–7 days |
56
+ | `timeoutMs` | 24 hours | 1 second–7 days |
57
+ | `tokenBudget` | Unset | Positive integer |
58
+
59
+ The plugin options `workflowConcurrency` and `workflowMaxAgents` override the default concurrency and total agent-step limit for new runs. The registered `workflows_start` schema advertises the configured defaults. Explicit run values override them, and existing runs retain their recorded limits. To use eight concurrent agents, the total agent-step limit must also be at least eight.
60
+
61
+ `tokenBudget` measures cumulative native input and output tokens across worker turns. It is an admission threshold, not a hard generation cap: workers already running can exceed it before the next dispatch checks their usage. Multi-turn code investigation can consume much more than the final answer's token count. Choose the threshold for the whole run, including review steps, or omit it and use the agent and time limits.
62
+
63
+ The cumulative host-call cap is `maxAgents * 8 + 100`, including agent, phase, log, checkpoint, and nested-workflow calls. Nested saved workflows share that cap and the parent agent budget. Nesting is limited to four child levels.
64
+
65
+ Helper bounds (`attempts`, `maxRounds`, and `consecutiveEmpty`) must be safe positive integers no greater than 1,000. Arguments, host-call payloads, and results are each limited to 1 MiB; error diagnostics are capped at 8 KiB. The runtime module allows 64 simultaneous interpreter calls, including nested calls, and rejects excess calls immediately. This is separate from the agent concurrency limit.
66
+
67
+ The configured Threads worker limit also applies across simultaneous runs owned by one coordinator. Its default is four workers. A token budget checks recorded usage at dispatch, including failed and interrupted attempts. Already-running agents can exceed the remaining budget. Unreported usage is marked unmeasured and blocks further budgeted dispatch.
68
+
69
+ The same start key and identical input identify the same run. Start retries do not restart completed or stopped runs. Pause stops new scheduling and drains active steps. Stop interrupts active work. Resume uses the same recorded script and arguments, reconciles existing sessions, and reuses completed results. A changed step request under an existing key fails rather than returning a stale result. Save edits as a new workflow run.
70
+
71
+ A service restart leaves interrupted work available for explicit resume. Worktrees and session evidence are retained. A missing result after an interrupted write requires inspection; it does not prove that no write occurred.
72
+
73
+ For an uncertain write, inspect its retained worker and directory. Use `threads_send` to ask that same worker to verify the existing effects and submit `workflows_result` without repeating completed actions. After its native execution finishes, resume the workflow. A missing worker session is not permission to replay its writes in a new session.
74
+
75
+ Nested saved scripts are pinned to their run. Agent successes, exposed failures, and checkpoint responses replay in their original settlement order. An exposed failure cannot become a success during replay. If a selected profile's model, instructions, or permissions change, start a new run rather than treating its old result as evidence from the new profile.
76
+
77
+ Legacy journals without checkpoint settlement order cannot deterministically resume an answered checkpoint. Such runs fail with a diagnostic and require a new run key. A corrupt or already-full legacy record remains preserved and produces an owner-visible diagnostic without blocking healthy sibling runs. An exact-limit legacy record may need explicit repair before any further control metadata fits.
78
+
79
+ Checkpoint responses are stored in both checkpoint state and settlement order, so their bytes count twice toward the journal limit. A run too full to accept a response remains unanswered; use a smaller response or a new run key. An inconsistent settlement order may wait until the configured run deadline.
80
+
81
+ ## Saved workflows
82
+
83
+ `workflows_save` writes a run's script to `.opencode/workflows/<name>.js` in the current directory or to `workflows/<name>.js` under the user's OpenCode configuration. Existing files are not overwritten. Project workflows take precedence over user workflows of the same name.
84
+
85
+ Use `workflows_saved` to list scripts and `/workflow-<name>` to ask the agent to invoke one with arguments. `/workflow-run <task>` asks the current agent to author a workflow. `/workflows` opens the terminal run navigator.
86
+
87
+ After editing saved files directly, use `/workflow-refresh` to reload their commands. The saved names `run` and `refresh` are reserved for built-in workflow commands.
@@ -1,6 +1,6 @@
1
1
  import type { Permission } from "@opencode/schema/permission";
2
2
 
3
- function matches(pattern: string, value: string) {
3
+ export function permissionMatches(pattern: string, value: string) {
4
4
  const expression = pattern
5
5
  .replaceAll("\\", "/")
6
6
  .replace(/[.+^${}()|[\]\\]/g, "\\$&")
@@ -19,7 +19,7 @@ export function delegationEffect(
19
19
  agentID: string,
20
20
  ): Permission.Effect {
21
21
  return rules.findLast((rule) =>
22
- matches(rule.action, "subagent") && matches(rule.resource, agentID)
22
+ permissionMatches(rule.action, "subagent") && permissionMatches(rule.resource, agentID)
23
23
  )?.effect ?? "ask";
24
24
  }
25
25