@tagma/sdk 0.5.0 → 0.5.2
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 +28 -24
- package/dist/bootstrap.js +5 -5
- package/dist/bootstrap.js.map +1 -1
- package/dist/drivers/opencode.d.ts +3 -0
- package/dist/drivers/opencode.d.ts.map +1 -0
- package/dist/drivers/opencode.js +316 -0
- package/dist/drivers/opencode.js.map +1 -0
- package/dist/engine.js +5 -5
- package/dist/engine.js.map +1 -1
- package/dist/schema.js +4 -4
- package/dist/schema.js.map +1 -1
- package/dist/schema.test.js +3 -3
- package/dist/schema.test.js.map +1 -1
- package/package.json +2 -2
- package/src/bootstrap.ts +5 -5
- package/src/drivers/opencode.ts +371 -0
- package/src/engine.ts +6 -6
- package/src/schema.test.ts +3 -3
- package/src/schema.ts +4 -4
- package/src/drivers/claude-code.ts +0 -250
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
> the npm registry website and cannot be removed — please ignore it and use
|
|
16
16
|
> the command above.)_
|
|
17
17
|
|
|
18
|
-
A local AI task orchestration SDK for [Bun](https://bun.sh). Define multi-track pipelines in YAML, run AI coding agents (
|
|
18
|
+
A local AI task orchestration SDK for [Bun](https://bun.sh). Define multi-track pipelines in YAML, run AI coding agents (OpenCode, Codex, Claude Code) and shell commands in parallel with dependency resolution, approval gates, and lifecycle hooks.
|
|
19
19
|
|
|
20
20
|
## Install
|
|
21
21
|
|
|
@@ -35,7 +35,7 @@ pipeline:
|
|
|
35
35
|
tracks:
|
|
36
36
|
- id: backend
|
|
37
37
|
name: Backend
|
|
38
|
-
driver:
|
|
38
|
+
driver: opencode
|
|
39
39
|
permissions: { read: true, write: true, execute: false }
|
|
40
40
|
tasks:
|
|
41
41
|
- id: implement
|
|
@@ -65,7 +65,7 @@ console.log(result.success ? 'Done' : 'Failed');
|
|
|
65
65
|
## Features
|
|
66
66
|
|
|
67
67
|
- **Multi-track DAG execution** -- tasks run in parallel across tracks, respecting `depends_on` ordering
|
|
68
|
-
- **Driver plugins** -- built-in `
|
|
68
|
+
- **Driver plugins** -- built-in `opencode` driver; install `@tagma/driver-codex` or `@tagma/driver-claude-code` for other agents
|
|
69
69
|
- **Session handoff** -- `continue_from` passes context between tasks (session resume or text injection)
|
|
70
70
|
- **Approval gates** -- trigger-based approval with stdin and WebSocket adapters
|
|
71
71
|
- **Lifecycle hooks** -- `pipeline_start`, `task_start`, `task_success`, `task_failure`, `pipeline_complete`, `pipeline_error`
|
|
@@ -80,10 +80,11 @@ console.log(result.success ? 'Done' : 'Failed');
|
|
|
80
80
|
```yaml
|
|
81
81
|
pipeline:
|
|
82
82
|
name: my-pipeline
|
|
83
|
-
driver:
|
|
83
|
+
driver: opencode
|
|
84
84
|
timeout: 30m
|
|
85
85
|
plugins:
|
|
86
86
|
- '@tagma/driver-codex'
|
|
87
|
+
- '@tagma/driver-claude-code'
|
|
87
88
|
hooks:
|
|
88
89
|
pipeline_start: 'echo starting'
|
|
89
90
|
task_start: 'echo task begin'
|
|
@@ -95,8 +96,8 @@ pipeline:
|
|
|
95
96
|
- id: track-1
|
|
96
97
|
name: Track One
|
|
97
98
|
color: '#3b82f6'
|
|
98
|
-
driver:
|
|
99
|
-
model:
|
|
99
|
+
driver: opencode
|
|
100
|
+
model: opencode/big-pickle
|
|
100
101
|
agent_profile: senior
|
|
101
102
|
cwd: ./services/backend
|
|
102
103
|
permissions:
|
|
@@ -113,8 +114,8 @@ pipeline:
|
|
|
113
114
|
name: Do something
|
|
114
115
|
prompt: 'Your prompt here'
|
|
115
116
|
timeout: 10m
|
|
116
|
-
driver:
|
|
117
|
-
model:
|
|
117
|
+
driver: opencode
|
|
118
|
+
model: opencode/big-pickle
|
|
118
119
|
agent_profile: senior
|
|
119
120
|
cwd: ./src
|
|
120
121
|
permissions:
|
|
@@ -143,7 +144,7 @@ pipeline:
|
|
|
143
144
|
| Field | Type | Required | Description |
|
|
144
145
|
| --------- | --------------- | -------- | ------------------------------------------------------------------------------------------ |
|
|
145
146
|
| `name` | `string` | Yes | Pipeline name, used in logs and run IDs |
|
|
146
|
-
| `driver` | `string` | No | Default driver for all tracks/tasks (inherited). Built-in: `
|
|
147
|
+
| `driver` | `string` | No | Default driver for all tracks/tasks (inherited). Built-in: `opencode` |
|
|
147
148
|
| `model` | `string` | No | Default model for all tracks/tasks (inherited). Exact model name, e.g. `claude-sonnet-4-6` |
|
|
148
149
|
| `timeout` | `string` | No | Pipeline-level timeout. Format: `"30s"`, `"5m"`, `"2h"` |
|
|
149
150
|
| `plugins` | `string[]` | No | External plugin packages to load, e.g. `["@tagma/driver-codex"]` |
|
|
@@ -280,7 +281,7 @@ Track-level `middlewares` apply to all tasks in the track. Setting task-level `m
|
|
|
280
281
|
|
|
281
282
|
### `bootstrapBuiltins()`
|
|
282
283
|
|
|
283
|
-
Registers all built-in plugins (
|
|
284
|
+
Registers all built-in plugins (opencode driver, file/manual triggers, completion checks, static-context middleware).
|
|
284
285
|
|
|
285
286
|
### `loadPipeline(yaml: string, workDir: string): Promise<PipelineConfig>`
|
|
286
287
|
|
|
@@ -294,24 +295,27 @@ Options:
|
|
|
294
295
|
|
|
295
296
|
- `approvalGateway` -- custom `ApprovalGateway` instance (defaults to `InMemoryApprovalGateway`)
|
|
296
297
|
- `signal` -- `AbortSignal` to cancel the run externally
|
|
297
|
-
- `onEvent` -- callback for real-time `
|
|
298
|
-
- `
|
|
299
|
-
- `
|
|
298
|
+
- `onEvent` -- callback for real-time `RunEventPayload` updates. Every payload carries `runId`. The editor server stamps a per-run `seq` on top of this payload before broadcasting over SSE (producing a `WireRunEvent`); the SDK itself does not stamp `seq`. Event variants:
|
|
299
|
+
- `run_start` — pipeline approved and all tasks transitioned to `waiting`; includes `tasks: RunTaskState[]` (wire-shape snapshot of every task). Fires only when the `pipeline_start` hook allows the run — blocked pipelines emit no wire events at all.
|
|
300
|
+
- `task_update` — a task's status or result changed; flat fields (`status`, `startedAt?`, `finishedAt?`, `durationMs?`, `exitCode?`, `stdout?`, `stderr?`, `stderrPath?`, `sessionId?`, `normalizedOutput?`, `resolvedDriver?`, `resolvedModel?`, `resolvedPermissions?`) so clients can fold partial updates with `??` semantics. `startedAt` is populated before the `running` transition; `finishedAt` and result fields are populated before any terminal-status transition. Terminal-state locking in the engine guarantees at most one terminal event per task.
|
|
300
301
|
- `task_log` — a structured log line was written to `pipeline.log`. Mirrors every `Logger` call (info/warn/error/debug/section/quiet) and carries `{ taskId: string | null, level, timestamp, text }`. `taskId` is non-null for lines tagged with a `[task:<id>]` prefix (or passed explicitly to `section`/`quiet`) and `null` for pipeline-wide messages such as the configuration dump and DAG topology. Use this to stream the full run process into UIs without tailing the log file.
|
|
301
|
-
- `
|
|
302
|
+
- `run_end` — pipeline finished; includes `success: boolean` and `abortReason: 'timeout' | 'stop_all' | 'external' | null`. `null` means the run completed on its own steam (success may still be `false` if tasks failed).
|
|
303
|
+
- `run_error` — reserved for fatal engine errors surfaced over the wire.
|
|
304
|
+
- `approval_request` / `approval_resolved` — bridged from the approval gateway so hosts see approvals on the same channel as task updates, without separately subscribing to the gateway.
|
|
302
305
|
- `runId` -- caller-supplied run ID. When provided the engine uses this instead of generating its own, keeping the caller and the SDK log directories aligned on the same ID
|
|
303
306
|
- `maxLogRuns` -- number of per-run log directories to keep under `<workDir>/.tagma/logs/` (default: 20)
|
|
307
|
+
- `skipPluginLoading` -- skip the engine's built-in `loadPlugins(config.plugins)` call. Set this when the host has already pre-loaded plugins from a custom resolution path (e.g. the editor loading from the user's workspace `node_modules`) so the engine doesn't re-resolve them via Node's default cwd-based import.
|
|
304
308
|
|
|
305
309
|
### `PipelineRunner`
|
|
306
310
|
|
|
307
311
|
Higher-level wrapper for managing multiple concurrent pipeline runs — designed for sidecar / Tauri IPC scenarios where the frontend controls pipeline lifecycle by ID.
|
|
308
312
|
|
|
309
313
|
```ts
|
|
310
|
-
const runner = new PipelineRunner(config, workDir);
|
|
314
|
+
const runner = new PipelineRunner(config, workDir, options?); // options: Omit<RunPipelineOptions, 'signal' | 'onEvent'>
|
|
311
315
|
|
|
312
|
-
// Subscribe before start — handler is called for every
|
|
316
|
+
// Subscribe before start — handler is called for every RunEventPayload
|
|
313
317
|
const unsubscribe = runner.subscribe((event) => {
|
|
314
|
-
tauriEmit('
|
|
318
|
+
tauriEmit('run_event', { id: runner.instanceId, event });
|
|
315
319
|
});
|
|
316
320
|
|
|
317
321
|
runner.start(); // returns Promise<EngineResult>, idempotent
|
|
@@ -319,16 +323,16 @@ runner.start(); // returns Promise<EngineResult>, idempotent
|
|
|
319
323
|
// Cancel from IPC
|
|
320
324
|
runner.abort();
|
|
321
325
|
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
const
|
|
326
|
+
// Live wire-shape task mirror, maintained from run_start + task_update events.
|
|
327
|
+
// Empty map before the first run_start; safe to read at any time.
|
|
328
|
+
const tasks = runner.getTasks(); // ReadonlyMap<taskId, RunTaskState>
|
|
325
329
|
```
|
|
326
330
|
|
|
327
331
|
Properties:
|
|
328
332
|
|
|
329
333
|
- `instanceId` — stable ID assigned at construction, safe to use as a Map key before `start()`
|
|
330
|
-
- `runId` — engine-assigned run ID, available after the first `
|
|
331
|
-
- `status` — `'idle' | 'running' | 'done' | 'aborted'`
|
|
334
|
+
- `runId` — engine-assigned run ID, available after the first `run_start` event (`null` until then)
|
|
335
|
+
- `status` — `'idle' | 'running' | 'done' | 'aborted'` (see `PipelineRunnerStatus`)
|
|
332
336
|
|
|
333
337
|
### `TriggerBlockedError` / `TriggerTimeoutError`
|
|
334
338
|
|
|
@@ -561,8 +565,8 @@ Truncates `text` to at most `maxBytes` UTF-8 bytes (default 16 KB), appending a
|
|
|
561
565
|
| Package | Description |
|
|
562
566
|
| ------------------------------------------------------------------------------ | -------------------------- |
|
|
563
567
|
| [@tagma/types](https://www.npmjs.com/package/@tagma/types) | Shared TypeScript types |
|
|
564
|
-
| [@tagma/driver-codex](https://www.npmjs.com/package/@tagma/driver-codex)
|
|
565
|
-
| [@tagma/driver-
|
|
568
|
+
| [@tagma/driver-codex](https://www.npmjs.com/package/@tagma/driver-codex) | Codex CLI driver plugin |
|
|
569
|
+
| [@tagma/driver-claude-code](https://www.npmjs.com/package/@tagma/driver-claude-code) | Claude Code CLI driver plugin |
|
|
566
570
|
|
|
567
571
|
## License
|
|
568
572
|
|
package/dist/bootstrap.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { registerPlugin } from './registry';
|
|
2
2
|
// Built-in Drivers
|
|
3
|
-
// Only
|
|
4
|
-
// workspace plugins under
|
|
3
|
+
// Only opencode is built in. Other drivers (codex, claude-code) ship as
|
|
4
|
+
// workspace plugins under packages/ and must be declared in pipeline.yaml
|
|
5
5
|
// via the `plugins` field, e.g.:
|
|
6
|
-
// plugins: ["@tagma/driver-codex", "@tagma/driver-
|
|
7
|
-
import {
|
|
6
|
+
// plugins: ["@tagma/driver-codex", "@tagma/driver-claude-code"]
|
|
7
|
+
import { OpenCodeDriver } from './drivers/opencode';
|
|
8
8
|
// Built-in Triggers
|
|
9
9
|
import { FileTrigger } from './triggers/file';
|
|
10
10
|
import { ManualTrigger } from './triggers/manual';
|
|
@@ -16,7 +16,7 @@ import { OutputCheckCompletion } from './completions/output-check';
|
|
|
16
16
|
import { StaticContextMiddleware } from './middlewares/static-context';
|
|
17
17
|
export function bootstrapBuiltins() {
|
|
18
18
|
// Drivers
|
|
19
|
-
registerPlugin('drivers', '
|
|
19
|
+
registerPlugin('drivers', 'opencode', OpenCodeDriver);
|
|
20
20
|
// Triggers
|
|
21
21
|
registerPlugin('triggers', 'file', FileTrigger);
|
|
22
22
|
registerPlugin('triggers', 'manual', ManualTrigger);
|
package/dist/bootstrap.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,mBAAmB;AACnB,wEAAwE;AACxE,
|
|
1
|
+
{"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,mBAAmB;AACnB,wEAAwE;AACxE,0EAA0E;AAC1E,iCAAiC;AACjC,kEAAkE;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,oBAAoB;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,uBAAuB;AACvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,sBAAsB;AACtB,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAEvE,MAAM,UAAU,iBAAiB;IAC/B,UAAU;IACV,cAAc,CAAC,SAAS,EAAE,UAAU,EAAE,cAAc,CAAC,CAAC;IAEtD,WAAW;IACX,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAChD,cAAc,CAAC,UAAU,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;IAEpD,cAAc;IACd,cAAc,CAAC,aAAa,EAAE,WAAW,EAAE,kBAAkB,CAAC,CAAC;IAC/D,cAAc,CAAC,aAAa,EAAE,aAAa,EAAE,oBAAoB,CAAC,CAAC;IACnE,cAAc,CAAC,aAAa,EAAE,cAAc,EAAE,qBAAqB,CAAC,CAAC;IAErE,cAAc;IACd,cAAc,CAAC,aAAa,EAAE,gBAAgB,EAAE,uBAAuB,CAAC,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.d.ts","sourceRoot":"","sources":["../../src/drivers/opencode.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAOb,MAAM,UAAU,CAAC;AAyLlB,eAAO,MAAM,cAAc,EAAE,YAiL5B,CAAC"}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
const DEFAULT_MODEL = 'opencode/big-pickle';
|
|
2
|
+
// NOTE on Windows multi-line prompts: `opencode` resolves to `opencode.cmd`,
|
|
3
|
+
// an npm-generated batch wrapper. cmd.exe silently truncates argv elements
|
|
4
|
+
// at the first newline, so a multi-line prompt reaches the model as only
|
|
5
|
+
// its first line. The SDK's runner auto-unwraps npm .cmd shims into direct
|
|
6
|
+
// `node <js-entry>` invocations so newlines survive, and this driver can
|
|
7
|
+
// keep using the bare `opencode` name on every platform.
|
|
8
|
+
// tagma uses a provider-neutral reasoning_effort vocabulary (low|medium|high)
|
|
9
|
+
// but opencode's `--variant` is provider-specific (e.g. high|max|minimal).
|
|
10
|
+
// Map the tagma values to the closest opencode variant:
|
|
11
|
+
// low → minimal (least thinking)
|
|
12
|
+
// medium → <no flag, provider default>
|
|
13
|
+
// high → high (most thinking)
|
|
14
|
+
// Unknown values pass through unchanged so users who target a specific
|
|
15
|
+
// opencode variant (e.g. "max") still work.
|
|
16
|
+
const EFFORT_TO_VARIANT = {
|
|
17
|
+
low: 'minimal',
|
|
18
|
+
medium: null,
|
|
19
|
+
high: 'high',
|
|
20
|
+
};
|
|
21
|
+
let opencodeReady;
|
|
22
|
+
let cachedDefaultModel;
|
|
23
|
+
async function runCapture(args) {
|
|
24
|
+
try {
|
|
25
|
+
const proc = Bun.spawn(args, { stdout: 'pipe', stderr: 'pipe' });
|
|
26
|
+
const [stdout, stderr, code] = await Promise.all([
|
|
27
|
+
new Response(proc.stdout).text(),
|
|
28
|
+
new Response(proc.stderr).text(),
|
|
29
|
+
proc.exited,
|
|
30
|
+
]);
|
|
31
|
+
return { code, stdout, stderr };
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return { code: -1, stdout: '', stderr: '' };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function ensureOpencodeInstalled() {
|
|
38
|
+
if (opencodeReady !== undefined)
|
|
39
|
+
return opencodeReady;
|
|
40
|
+
// Probe existing install first — users who already have it get no delay.
|
|
41
|
+
const probe = await runCapture(['opencode', '--version']);
|
|
42
|
+
if (probe.code === 0) {
|
|
43
|
+
opencodeReady = true;
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
console.error('[driver:opencode] opencode CLI not found — installing via `bun install -g opencode-ai`... (this may take up to a minute)');
|
|
47
|
+
// Use inherit here so the user sees bun's own progress during the one-time
|
|
48
|
+
// install; runCapture would swallow it.
|
|
49
|
+
const install = Bun.spawn(['bun', 'install', '-g', 'opencode-ai'], {
|
|
50
|
+
stdout: 'inherit',
|
|
51
|
+
stderr: 'inherit',
|
|
52
|
+
});
|
|
53
|
+
const installCode = await install.exited;
|
|
54
|
+
if (installCode !== 0) {
|
|
55
|
+
console.error('[driver:opencode] install failed — opencode run will likely fail below.');
|
|
56
|
+
opencodeReady = false;
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
// Bun installs globals under `~/.bun/bin` (or `%USERPROFILE%\.bun\bin`),
|
|
60
|
+
// which isn't on this process's cached PATH unless the user already has
|
|
61
|
+
// bun set up. Ask bun for the directory and prepend it so bare `opencode`
|
|
62
|
+
// resolves in this process without requiring a shell reload.
|
|
63
|
+
const bin = await runCapture(['bun', 'pm', 'bin', '-g']);
|
|
64
|
+
if (bin.code === 0) {
|
|
65
|
+
const dir = bin.stdout.trim();
|
|
66
|
+
const sep = process.platform === 'win32' ? ';' : ':';
|
|
67
|
+
const current = process.env.PATH ?? '';
|
|
68
|
+
if (dir && !current.split(sep).includes(dir)) {
|
|
69
|
+
process.env.PATH = `${dir}${sep}${current}`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const verify = await runCapture(['opencode', '--version']);
|
|
73
|
+
opencodeReady = verify.code === 0;
|
|
74
|
+
if (!opencodeReady) {
|
|
75
|
+
console.error('[driver:opencode] `opencode` still not resolvable after install — check that bun global bin is on PATH.');
|
|
76
|
+
}
|
|
77
|
+
return opencodeReady;
|
|
78
|
+
}
|
|
79
|
+
// `opencode models --verbose` emits "<provider>/<id>\n{...json...}\n" pairs.
|
|
80
|
+
// Walk balanced braces rather than split on newlines so we survive any
|
|
81
|
+
// whitespace oddities in the JSON payload.
|
|
82
|
+
function parseVerboseModels(stdout) {
|
|
83
|
+
const out = [];
|
|
84
|
+
let depth = 0;
|
|
85
|
+
let start = -1;
|
|
86
|
+
for (let i = 0; i < stdout.length; i++) {
|
|
87
|
+
const c = stdout[i];
|
|
88
|
+
if (c === '{') {
|
|
89
|
+
if (depth === 0)
|
|
90
|
+
start = i;
|
|
91
|
+
depth++;
|
|
92
|
+
}
|
|
93
|
+
else if (c === '}') {
|
|
94
|
+
depth--;
|
|
95
|
+
if (depth === 0 && start !== -1) {
|
|
96
|
+
try {
|
|
97
|
+
out.push(JSON.parse(stdout.slice(start, i + 1)));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
/* skip malformed block */
|
|
101
|
+
}
|
|
102
|
+
start = -1;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
function pickFreeModel(models) {
|
|
109
|
+
const fullId = (m) => `${m.providerID ?? 'opencode'}/${m.id ?? ''}`;
|
|
110
|
+
const eligible = models.filter((m) => {
|
|
111
|
+
if (!m.id || m.id === 'big-pickle')
|
|
112
|
+
return false;
|
|
113
|
+
if (m.status && m.status !== 'active')
|
|
114
|
+
return false;
|
|
115
|
+
const cost = m.cost;
|
|
116
|
+
if (!cost || cost.input !== 0 || cost.output !== 0)
|
|
117
|
+
return false;
|
|
118
|
+
const ctx = m.limit?.context;
|
|
119
|
+
if (typeof ctx !== 'number' || ctx <= 128000)
|
|
120
|
+
return false;
|
|
121
|
+
return true;
|
|
122
|
+
});
|
|
123
|
+
// Prefer models explicitly labelled "-free" by the provider — those are
|
|
124
|
+
// a stronger stability signal than "cost happens to be 0 right now".
|
|
125
|
+
const preferred = eligible.filter((m) => m.id?.endsWith('-free'));
|
|
126
|
+
const pool = preferred.length > 0 ? preferred : eligible;
|
|
127
|
+
if (pool.length === 0)
|
|
128
|
+
return null;
|
|
129
|
+
// Deterministic pick: sort by full id so upstream model-list reordering
|
|
130
|
+
// doesn't flip our choice between runs.
|
|
131
|
+
pool.sort((a, b) => fullId(a).localeCompare(fullId(b)));
|
|
132
|
+
return fullId(pool[0]);
|
|
133
|
+
}
|
|
134
|
+
async function resolveDefaultModel() {
|
|
135
|
+
if (cachedDefaultModel !== undefined)
|
|
136
|
+
return cachedDefaultModel;
|
|
137
|
+
const ready = await ensureOpencodeInstalled();
|
|
138
|
+
if (!ready) {
|
|
139
|
+
cachedDefaultModel = DEFAULT_MODEL;
|
|
140
|
+
return cachedDefaultModel;
|
|
141
|
+
}
|
|
142
|
+
console.error('[driver:opencode] resolving free opencode model...');
|
|
143
|
+
const { code, stdout } = await runCapture(['opencode', 'models', '--verbose']);
|
|
144
|
+
if (code !== 0) {
|
|
145
|
+
cachedDefaultModel = DEFAULT_MODEL;
|
|
146
|
+
return cachedDefaultModel;
|
|
147
|
+
}
|
|
148
|
+
const picked = pickFreeModel(parseVerboseModels(stdout));
|
|
149
|
+
cachedDefaultModel = picked ?? DEFAULT_MODEL;
|
|
150
|
+
console.error(`[driver:opencode] default model: ${cachedDefaultModel}`);
|
|
151
|
+
return cachedDefaultModel;
|
|
152
|
+
}
|
|
153
|
+
export const OpenCodeDriver = {
|
|
154
|
+
name: 'opencode',
|
|
155
|
+
capabilities: {
|
|
156
|
+
sessionResume: true, // supports --session
|
|
157
|
+
systemPrompt: false, // no --system-prompt flag; prepend to prompt instead
|
|
158
|
+
outputFormat: true, // supports --format json
|
|
159
|
+
},
|
|
160
|
+
resolveModel() {
|
|
161
|
+
return DEFAULT_MODEL;
|
|
162
|
+
},
|
|
163
|
+
async buildCommand(task, track, ctx) {
|
|
164
|
+
const explicitModel = task.model ?? track.model;
|
|
165
|
+
// Always make sure the opencode CLI is usable before we spawn it — even
|
|
166
|
+
// when the user pinned a model. If missing, ensureOpencodeInstalled
|
|
167
|
+
// auto-installs it via `bun install -g opencode-ai`.
|
|
168
|
+
if (explicitModel)
|
|
169
|
+
await ensureOpencodeInstalled();
|
|
170
|
+
// Otherwise resolveDefaultModel both ensures the CLI and picks a free
|
|
171
|
+
// model from `opencode models --verbose` (cached per-process).
|
|
172
|
+
const model = explicitModel ?? (await resolveDefaultModel());
|
|
173
|
+
// Resolve reasoning_effort → opencode --variant. SDK schema layer already
|
|
174
|
+
// resolved task → track → pipeline inheritance, so we only need to read
|
|
175
|
+
// task.reasoning_effort here.
|
|
176
|
+
const rawEffort = task.reasoning_effort ?? track.reasoning_effort;
|
|
177
|
+
const variant = rawEffort
|
|
178
|
+
? rawEffort in EFFORT_TO_VARIANT
|
|
179
|
+
? EFFORT_TO_VARIANT[rawEffort]
|
|
180
|
+
: rawEffort
|
|
181
|
+
: null;
|
|
182
|
+
let prompt = task.prompt;
|
|
183
|
+
// agent_profile has no dedicated flag; prepend to prompt
|
|
184
|
+
const profile = task.agent_profile ?? track.agent_profile;
|
|
185
|
+
if (profile) {
|
|
186
|
+
prompt = `[Role]\n${profile}\n\n[Task]\n${prompt}`;
|
|
187
|
+
}
|
|
188
|
+
// continue_from: prefer session resume, fall back to text injection
|
|
189
|
+
let sessionId = null;
|
|
190
|
+
if (task.continue_from) {
|
|
191
|
+
sessionId = ctx.sessionMap.get(task.continue_from) ?? null;
|
|
192
|
+
if (!sessionId) {
|
|
193
|
+
// no session — degrade to text context passthrough
|
|
194
|
+
let prev = null;
|
|
195
|
+
if (ctx.normalizedMap.has(task.continue_from)) {
|
|
196
|
+
prev = ctx.normalizedMap.get(task.continue_from);
|
|
197
|
+
}
|
|
198
|
+
if (prev !== null) {
|
|
199
|
+
prompt = `[Previous Output]\n${prev}\n\n[Current Task]\n${prompt}`;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// opencode run does not support stdin (no `-` placeholder like codex exec).
|
|
204
|
+
// Prompt is always a positional argument. Flags must be declared before `--`;
|
|
205
|
+
// the prompt follows after so that leading `--flag` content cannot be
|
|
206
|
+
// misread by opencode's argument parser (flag-injection mitigation).
|
|
207
|
+
// Shell-level injection is already prevented by Bun.spawn's direct argv array.
|
|
208
|
+
// Windows cmd.exe argv truncation on the `.cmd` wrapper is handled by the
|
|
209
|
+
// SDK runner's shim unwrapping — see note at the top of this file.
|
|
210
|
+
const args = [
|
|
211
|
+
'opencode',
|
|
212
|
+
'run',
|
|
213
|
+
'--model',
|
|
214
|
+
model,
|
|
215
|
+
'--format',
|
|
216
|
+
'json', // JSON output for parseResult
|
|
217
|
+
];
|
|
218
|
+
// `--variant` must precede `--` like every other flag. opencode rejects
|
|
219
|
+
// unknown variant names with a clear error, so we don't pre-validate.
|
|
220
|
+
if (variant) {
|
|
221
|
+
args.push('--variant', variant);
|
|
222
|
+
}
|
|
223
|
+
// session resume (must appear before --)
|
|
224
|
+
if (sessionId) {
|
|
225
|
+
args.push('--session', sessionId);
|
|
226
|
+
}
|
|
227
|
+
// `--` (POSIX end-of-options) isolates prompt from flag parsing
|
|
228
|
+
args.push('--', prompt);
|
|
229
|
+
return { args, cwd: task.cwd ?? ctx.workDir };
|
|
230
|
+
},
|
|
231
|
+
parseResult(stdout) {
|
|
232
|
+
// opencode --format json emits NDJSON — one JSON object per line
|
|
233
|
+
// (step_start / text / step_finish / …). The previous single
|
|
234
|
+
// `JSON.parse(stdout)` always threw on this shape and fell through to
|
|
235
|
+
// the catch, returning sessionId:null and losing session resume.
|
|
236
|
+
// Walk line-by-line, pick up the first sessionID we see, concatenate
|
|
237
|
+
// any text-type parts into normalizedOutput, and bail early on error
|
|
238
|
+
// payloads.
|
|
239
|
+
const lines = stdout.split(/\r?\n/);
|
|
240
|
+
let sessionId;
|
|
241
|
+
const textParts = [];
|
|
242
|
+
let sawAnyJson = false;
|
|
243
|
+
let errorReason = null;
|
|
244
|
+
for (const raw of lines) {
|
|
245
|
+
const line = raw.trim();
|
|
246
|
+
if (!line)
|
|
247
|
+
continue;
|
|
248
|
+
let json;
|
|
249
|
+
try {
|
|
250
|
+
json = JSON.parse(line);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
continue; // tolerate interleaved non-JSON noise
|
|
254
|
+
}
|
|
255
|
+
sawAnyJson = true;
|
|
256
|
+
// M12: opencode sometimes emits {type:"error", error:{...}} with
|
|
257
|
+
// exit 0 for transient API failures. Force-fail so downstream
|
|
258
|
+
// skip_downstream / stop_all kicks in.
|
|
259
|
+
if (json.type === 'error') {
|
|
260
|
+
const err = json.error;
|
|
261
|
+
const msg = typeof err === 'object' && err !== null && typeof err.message === 'string'
|
|
262
|
+
? err.message
|
|
263
|
+
: typeof err === 'string'
|
|
264
|
+
? err
|
|
265
|
+
: null;
|
|
266
|
+
errorReason = msg
|
|
267
|
+
? `opencode reported error: ${msg}`
|
|
268
|
+
: 'opencode emitted an error JSON payload';
|
|
269
|
+
// D21: stop at the first error. Continuing meant subsequent text
|
|
270
|
+
// lines got accumulated into `textParts` only to be discarded by
|
|
271
|
+
// the error-return below, and a later `{type:"error"}` would
|
|
272
|
+
// silently overwrite the original cause — operators then debugged
|
|
273
|
+
// a downstream symptom while the root-cause line scrolled past.
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
// Session id — opencode uses `sessionID` (camelCase with capital D).
|
|
277
|
+
// Keep `session_id` / `sessionId` as fallbacks for forward/backward
|
|
278
|
+
// compatibility with other shapes.
|
|
279
|
+
if (!sessionId) {
|
|
280
|
+
const sid = json.sessionID ??
|
|
281
|
+
json.session_id ??
|
|
282
|
+
json.sessionId ??
|
|
283
|
+
null;
|
|
284
|
+
if (typeof sid === 'string' && sid.length > 0)
|
|
285
|
+
sessionId = sid;
|
|
286
|
+
}
|
|
287
|
+
// Extract human-readable text from text-type parts.
|
|
288
|
+
if (json.type === 'text') {
|
|
289
|
+
const part = json.part;
|
|
290
|
+
if (part && typeof part.text === 'string') {
|
|
291
|
+
textParts.push(part.text);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
else if (typeof json.result === 'string') {
|
|
295
|
+
textParts.push(json.result);
|
|
296
|
+
}
|
|
297
|
+
else if (typeof json.content === 'string') {
|
|
298
|
+
textParts.push(json.content);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (errorReason) {
|
|
302
|
+
return { forceFailure: true, forceFailureReason: errorReason };
|
|
303
|
+
}
|
|
304
|
+
// If nothing parsed as JSON, treat stdout as plain text.
|
|
305
|
+
const normalizedOutput = !sawAnyJson
|
|
306
|
+
? stdout
|
|
307
|
+
: textParts.length > 0
|
|
308
|
+
? textParts.join('\n')
|
|
309
|
+
: stdout;
|
|
310
|
+
return {
|
|
311
|
+
sessionId,
|
|
312
|
+
normalizedOutput,
|
|
313
|
+
};
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
//# sourceMappingURL=opencode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../src/drivers/opencode.ts"],"names":[],"mappings":"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"}
|
package/dist/engine.js
CHANGED
|
@@ -31,7 +31,7 @@ function preflight(config, dag) {
|
|
|
31
31
|
for (const [, node] of dag.nodes) {
|
|
32
32
|
const task = node.task;
|
|
33
33
|
const track = node.track;
|
|
34
|
-
const driverName = task.driver ?? track.driver ?? config.driver ?? '
|
|
34
|
+
const driverName = task.driver ?? track.driver ?? config.driver ?? 'opencode';
|
|
35
35
|
// Pure command tasks don't use a driver — skip driver registration check.
|
|
36
36
|
const isCommandOnly = task.command && !task.prompt;
|
|
37
37
|
if (!isCommandOnly && !hasHandler('drivers', driverName)) {
|
|
@@ -62,7 +62,7 @@ function preflight(config, dag) {
|
|
|
62
62
|
// A handoff is possible via session resume (already ruled out above),
|
|
63
63
|
// OR in-memory text injection through normalizedMap
|
|
64
64
|
// (when the upstream driver implements parseResult and returns normalizedOutput).
|
|
65
|
-
const upstreamDriverName = upstream.task.driver ?? upstream.track.driver ?? config.driver ?? '
|
|
65
|
+
const upstreamDriverName = upstream.task.driver ?? upstream.track.driver ?? config.driver ?? 'opencode';
|
|
66
66
|
const upstreamDriver = hasHandler('drivers', upstreamDriverName)
|
|
67
67
|
? getHandler('drivers', upstreamDriverName)
|
|
68
68
|
: null;
|
|
@@ -153,7 +153,7 @@ export async function runPipeline(config, workDir, options = {}) {
|
|
|
153
153
|
// File-only: dump the resolved pipeline shape + DAG topology for post-mortem.
|
|
154
154
|
log.section('Pipeline configuration');
|
|
155
155
|
log.quiet(`name: ${config.name}`);
|
|
156
|
-
log.quiet(`driver: ${config.driver ?? '(default:
|
|
156
|
+
log.quiet(`driver: ${config.driver ?? '(default: opencode)'}`);
|
|
157
157
|
log.quiet(`timeout: ${config.timeout ?? '(none)'}`);
|
|
158
158
|
log.quiet(`tracks: ${config.tracks.length}`);
|
|
159
159
|
log.quiet(`tasks (total): ${dag.nodes.size}`);
|
|
@@ -515,7 +515,7 @@ export async function runPipeline(config, workDir, options = {}) {
|
|
|
515
515
|
setTaskStatus(taskId, 'running');
|
|
516
516
|
log.info(`[task:${taskId}]`, task.command ? `running: ${task.command}` : `running (driver task)`);
|
|
517
517
|
// File-only: resolved config for this task
|
|
518
|
-
const resolvedDriver = task.driver ?? track.driver ?? config.driver ?? '
|
|
518
|
+
const resolvedDriver = task.driver ?? track.driver ?? config.driver ?? 'opencode';
|
|
519
519
|
const resolvedModel = task.model ?? track.model ?? config.model ?? '(default)';
|
|
520
520
|
const resolvedPerms = task.permissions ?? track.permissions ?? '(default)';
|
|
521
521
|
const resolvedCwd = task.cwd ?? track.cwd ?? workDir;
|
|
@@ -537,7 +537,7 @@ export async function runPipeline(config, workDir, options = {}) {
|
|
|
537
537
|
}
|
|
538
538
|
else {
|
|
539
539
|
// AI task: apply middleware chain against a structured PromptDocument.
|
|
540
|
-
const driverName = task.driver ?? track.driver ?? config.driver ?? '
|
|
540
|
+
const driverName = task.driver ?? track.driver ?? config.driver ?? 'opencode';
|
|
541
541
|
const driver = getHandler('drivers', driverName);
|
|
542
542
|
const originalLen = task.prompt.length;
|
|
543
543
|
let doc = promptDocumentFromString(task.prompt);
|