@tagma/sdk 0.6.10 → 0.6.12

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.
Files changed (56) hide show
  1. package/README.md +93 -13
  2. package/dist/config-ops.d.ts +4 -2
  3. package/dist/config-ops.d.ts.map +1 -1
  4. package/dist/config-ops.js +30 -2
  5. package/dist/config-ops.js.map +1 -1
  6. package/dist/engine.d.ts.map +1 -1
  7. package/dist/engine.js +75 -27
  8. package/dist/engine.js.map +1 -1
  9. package/dist/pipeline-runner.d.ts +1 -1
  10. package/dist/pipeline-runner.d.ts.map +1 -1
  11. package/dist/pipeline-runner.js +20 -0
  12. package/dist/pipeline-runner.js.map +1 -1
  13. package/dist/ports.d.ts +23 -1
  14. package/dist/ports.d.ts.map +1 -1
  15. package/dist/ports.js +160 -0
  16. package/dist/ports.js.map +1 -1
  17. package/dist/schema.d.ts.map +1 -1
  18. package/dist/schema.js +47 -11
  19. package/dist/schema.js.map +1 -1
  20. package/dist/sdk.d.ts +2 -2
  21. package/dist/sdk.d.ts.map +1 -1
  22. package/dist/sdk.js +1 -1
  23. package/dist/sdk.js.map +1 -1
  24. package/dist/task-ref.d.ts.map +1 -1
  25. package/dist/task-ref.js +2 -0
  26. package/dist/task-ref.js.map +1 -1
  27. package/dist/utils.js +3 -3
  28. package/dist/utils.js.map +1 -1
  29. package/dist/validate-raw.d.ts.map +1 -1
  30. package/dist/validate-raw.js +167 -3
  31. package/dist/validate-raw.js.map +1 -1
  32. package/dist/yaml-compiler.d.ts.map +1 -1
  33. package/dist/yaml-compiler.js +23 -5
  34. package/dist/yaml-compiler.js.map +1 -1
  35. package/package.json +2 -2
  36. package/src/completions/output-check.test.ts +50 -0
  37. package/src/config-ops.test.ts +70 -0
  38. package/src/config-ops.ts +23 -2
  39. package/src/engine-ports.test.ts +66 -0
  40. package/src/engine-task-type.test.ts +56 -0
  41. package/src/engine.ts +100 -26
  42. package/src/pipeline-runner.test.ts +95 -0
  43. package/src/pipeline-runner.ts +18 -1
  44. package/src/ports.test.ts +127 -0
  45. package/src/ports.ts +224 -1
  46. package/src/schema-ports.test.ts +86 -0
  47. package/src/schema.test.ts +113 -1
  48. package/src/schema.ts +52 -13
  49. package/src/sdk.ts +4 -0
  50. package/src/task-ref.ts +1 -0
  51. package/src/utils.test.ts +28 -0
  52. package/src/utils.ts +3 -3
  53. package/src/validate-raw-ports.test.ts +66 -0
  54. package/src/validate-raw.ts +189 -4
  55. package/src/yaml-compiler.test.ts +108 -0
  56. package/src/yaml-compiler.ts +32 -5
package/README.md CHANGED
@@ -72,7 +72,8 @@ console.log(result.success ? 'Done' : 'Failed');
72
72
  - **Middleware** -- enrich prompts before execution (e.g. inject static context)
73
73
  - **Completion checks** -- validate task output with `exit_code`, `file_exists`, or `output_check` plugins
74
74
  - **Plugin schemas** -- triggers/completions/middlewares can declare a `PluginSchema` so visual editors render typed forms for their config
75
- - **Typed task ports** -- declare named, typed `inputs` / `outputs` on a task. Inputs from upstream tasks are substituted into `command` / `prompt` via `{{inputs.<name>}}` and rendered as an `[Inputs]` context block for AI tasks; outputs are extracted from the final-line JSON object on stdout (or `normalizedOutput`) and surfaced to downstream tasks
75
+ - **Lightweight task bindings** -- pass dynamic values with task-level `inputs` / `outputs` without declaring a typed contract
76
+ - **Typed task ports** -- declare named, typed `ports.inputs` / `ports.outputs` when a task needs a strict, validated I/O contract
76
77
 
77
78
  ## Pipeline YAML Reference
78
79
 
@@ -147,6 +148,7 @@ pipeline:
147
148
  | `name` | `string` | Yes | Pipeline name, used in logs and run IDs |
148
149
  | `driver` | `string` | No | Default driver for all tracks/tasks (inherited). Built-in: `opencode` |
149
150
  | `model` | `string` | No | Default model for all tracks/tasks (inherited). Exact model name, e.g. `claude-sonnet-4-6` |
151
+ | `permissions` | `Permissions` | No | Default permissions inherited by all tracks/tasks (see Permissions) |
150
152
  | `timeout` | `string` | No | Pipeline-level timeout. Format: `"30s"`, `"5m"`, `"2h"` |
151
153
  | `plugins` | `string[]` | No | External plugin packages to load, e.g. `["@tagma/driver-codex"]` |
152
154
  | `hooks` | `HooksConfig` | No | Shell commands to run at lifecycle events (see Hooks below) |
@@ -200,6 +202,8 @@ Each hook value can be a single command string or an array of commands.
200
202
  | `middlewares` | `MiddlewareConfig[]` | No | Inherited from track | Middleware override. Set `[]` to disable inherited middlewares |
201
203
  | `trigger` | `TriggerConfig` | No | — | Gate that must resolve before the task runs (see Triggers) |
202
204
  | `completion` | `CompletionConfig` | No | — | Post-execution check to validate task output (see Completions) |
205
+ | `inputs` | `TaskInputBindings` | No | — | Lightweight parameter bindings for `{{inputs.<name>}}` |
206
+ | `outputs` | `TaskOutputBindings` | No | — | Lightweight named outputs published after success |
203
207
  | `ports` | `TaskPorts` | No | — | Typed input/output ports — see Typed Ports below |
204
208
 
205
209
  ### Permissions
@@ -220,6 +224,33 @@ Track-level `middlewares` apply to all tasks in the track. Setting task-level `m
220
224
 
221
225
  ---
222
226
 
227
+ ### Lightweight Bindings
228
+
229
+ Use task-level `inputs` / `outputs` for ordinary parameter passing. They are task-level only, do not inherit, and do not add prompt schema blocks or type coercion.
230
+
231
+ ```yaml
232
+ - id: build
233
+ command: bun run build
234
+ outputs:
235
+ bundlePath: { from: json.bundlePath }
236
+
237
+ - id: test
238
+ depends_on: [build]
239
+ command: 'bun test "{{inputs.bundlePath}}"'
240
+ inputs:
241
+ bundlePath:
242
+ from: t.build.outputs.bundlePath
243
+ required: true
244
+ ```
245
+
246
+ Input bindings support `value`, `from`, `default`, and `required`. `from` accepts `taskId.outputs.name`, `taskId.stdout`, `taskId.stderr`, `taskId.normalizedOutput`, `taskId.exitCode`, or `outputs.name` to name-match direct upstream outputs.
247
+
248
+ Output bindings support `value`, `from`, and `default`. `from` defaults to `json.<outputName>` and also accepts `stdout`, `stderr`, or `normalizedOutput`.
249
+
250
+ Use `ports` instead when downstream tasks need required typed values, the editor should present a stable I/O contract, or prompt tasks should receive `[Inputs]` / `[Output Format]` blocks.
251
+
252
+ ---
253
+
223
254
  ### Typed Ports
224
255
 
225
256
  Tasks can declare named, typed `inputs` / `outputs`. Inputs flow in from upstream task outputs; outputs are extracted from a task's stdout (or the AI driver's `normalizedOutput`) on success.
@@ -344,7 +375,7 @@ Options:
344
375
  - `signal` -- `AbortSignal` to cancel the run externally
345
376
  - `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:
346
377
  - `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.
347
- - `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.
378
+ - `task_update` — a task's status or result changed; flat fields (`status`, `startedAt?`, `finishedAt?`, `durationMs?`, `exitCode?`, `stdout?`, `stderr?`, `stdoutPath?`, `stderrPath?`, `stdoutBytes?`, `stderrBytes?`, `sessionId?`, `normalizedOutput?`, `outputs?`, `inputs?`, `resolvedDriver?`, `resolvedModel?`, `resolvedPermissions?`) so clients can fold partial updates with `??` semantics. `inputs` carries the resolved port input map (set once just before the task transitions to `running`); `outputs` carries the extracted port output map after a successful terminal transition. `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.
348
379
  - `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.
349
380
  - `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).
350
381
  - `run_error` — reserved for fatal engine errors surfaced over the wire.
@@ -372,8 +403,11 @@ runner.start(); // returns Promise<EngineResult>, idempotent
372
403
  // Cancel from IPC
373
404
  runner.abort();
374
405
 
375
- // Live wire-shape task mirror, maintained from run_start + task_update events.
406
+ // Live wire-shape task mirror, maintained from run_start + task_update + task_log events.
376
407
  // Empty map before the first run_start; safe to read at any time.
408
+ // task_log entries are folded into RunTaskState.logs (capped at TASK_LOG_CAP)
409
+ // and totalLogCount, so hosts get a self-contained per-task view without
410
+ // having to buffer the event stream themselves.
377
411
  const tasks = runner.getTasks(); // ReadonlyMap<taskId, RunTaskState>
378
412
  ```
379
413
 
@@ -493,7 +527,21 @@ const yaml = serializePipeline(config);
493
527
  | `upsertTask(config, trackId, task)` | Insert or replace a task |
494
528
  | `removeTask(config, trackId, taskId, cleanRefs?)` | Remove a task; pass `cleanRefs: true` to also strip dangling `depends_on` / `continue_from` references. Only refs that resolve to the deleted task are removed — same-named tasks in other tracks are unaffected |
495
529
  | `moveTask(config, trackId, taskId, toIndex)` | Reorder a task within its track |
496
- | `transferTask(config, fromTrackId, taskId, toTrackId, qualifyRefs?)` | Move a task across tracks. When `qualifyRefs` is `true` (default), bare `depends_on` / `continue_from` references to the moved task are converted to fully-qualified form (`toTrackId.taskId`) so same-track resolution stays correct |
530
+ | `transferTask(config, fromTrackId, taskId, toTrackId, qualifyRefs?)` | Move a task across tracks (see invariants below) |
531
+
532
+ `transferTask` invariants — the call is a **no-op** (returns the input config unchanged) when:
533
+
534
+ - the target track doesn't exist, **or**
535
+ - the target track already contains a task with the same id
536
+
537
+ It never silently drops the source task or overwrites a task in the target track.
538
+
539
+ When `qualifyRefs` is `true` (default), reference rewriting runs in two passes so same-track shorthand stays correct after the move:
540
+
541
+ - the **moved task's own** bare `depends_on` / `continue_from` entries that pointed at siblings in the source track are rewritten to `fromTrackId.<dep>` (so they keep pointing at the original peers, not at coincidental same-named tasks in the destination track)
542
+ - bare references **from other tasks** that resolved to the moved task are rewritten to `toTrackId.taskId` when same-track resolution would otherwise break (i.e. no shadow task exists in the referencing track and no other track still holds the bare id globally)
543
+
544
+ Pass `qualifyRefs: false` to skip both passes — useful when the caller is doing a batch move and will requalify everything itself.
497
545
 
498
546
  ### `parseYaml(content: string): RawPipelineConfig`
499
547
 
@@ -523,15 +571,17 @@ Validates a resolved pipeline config without executing it. Checks DAG structure
523
571
 
524
572
  Use `validateRaw` for editing raw configs in a UI; use `validateConfig` after `resolveConfig` for a final pre-run check.
525
573
 
526
- ### Typed Ports API
574
+ ### Bindings And Ports API
527
575
 
528
- Pure helpers backing the `task.ports` feature (see Typed Ports above). Safe to use in editors, simulators, and custom drivers — no I/O, no side effects.
576
+ Pure helpers backing lightweight bindings and `task.ports`. Safe to use in editors, simulators, and custom drivers — no I/O, no side effects.
529
577
 
530
578
  | Function | Description |
531
579
  | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
532
580
  | `substituteInputs(text, inputs)` | Expand `{{inputs.<name>}}` placeholders in `text`. Returns `{ text, unresolved }`. Strings pass through, numbers/booleans coerce via `String(...)`, objects/arrays via `JSON.stringify`. Caller is responsible for shell quoting |
533
581
  | `extractInputReferences(text)` | Return the set of input port names referenced by `{{inputs.<name>}}` placeholders in `text`. Use at edit time to flag undeclared references |
582
+ | `resolveTaskBindingInputs(task, upstreamData, dependsOn)` | Resolve lightweight task-level `inputs` from literal values, upstream outputs, stdout/stderr, normalized output, defaults, and required flags |
534
583
  | `resolveTaskInputs(task, upstreamOutputs, dependsOn)` | Gather the input values a task will consume from its direct upstreams. Applies `from` bindings, defaults, and type coercion. Returns `{ kind: 'ready', inputs, missingOptional }` or `{ kind: 'blocked', missingRequired, ambiguous, typeErrors, reason }` |
584
+ | `extractTaskBindingOutputs(outputs, stdout, stderr, normalizedOutput)` | Publish lightweight task-level `outputs` from final-line JSON, stdout/stderr, normalized output, literal values, or defaults |
535
585
  | `extractTaskOutputs(ports, stdout, normalizedOutput)` | Pull declared output values from a terminated task's output. Strategy: prefer `normalizedOutput`; find the last non-empty line that parses as a JSON object; coerce each declared key. Returns `{ outputs, diagnostic }` |
536
586
  | `prependContext(doc, block)` | Same shape as `appendContext` but prepends; the engine uses this to place `[Output Format]` and `[Inputs]` blocks before middleware-added context |
537
587
  | `renderInputsBlock(inputsDecl, values)` | Build the `[Inputs]` `PromptContextBlock` rendered into AI prompts (`name: value # description` lines). Returns `null` when no inputs to render |
@@ -539,21 +589,51 @@ Pure helpers backing the `task.ports` feature (see Typed Ports above). Safe to u
539
589
 
540
590
  Custom drivers that wrap the prompt in their own envelope can read `DriverContext.inputs` (resolved + coerced map keyed by port name) and call `substituteInputs` themselves — the engine has already substituted into `task.prompt` upstream, so most drivers can ignore this.
541
591
 
542
- ### `validateRaw(config: RawPipelineConfig): ValidationError[]`
592
+ ### `validateRaw(config: RawPipelineConfig, knownTypes?: KnownPluginTypes): ValidationError[]`
543
593
 
544
- Validates a raw pipeline config without resolving inheritance or executing anything. Returns a flat list of `{ path, message }` objects — empty array means valid.
594
+ Validates a raw pipeline config without resolving inheritance or executing anything. Returns a flat list of `{ path, message, severity? }` objects — empty array means valid. `severity` is `'error'` (default, fatal) or `'warning'` (soft hint; non-blocking).
545
595
 
546
- Checks: required fields, `prompt`/`command` exclusivity, duplicate task IDs within a track, `depends_on`/`continue_from` reference integrity (including ambiguous bare refs that exist in multiple tracks — use `trackId.taskId` to disambiguate), circular dependency detection, port shape (name format, valid `type`, duplicate names, `enum` requires non-empty `enum` array, `required`/`from` ignored on outputs), and `{{inputs.<name>}}` references resolving to a declared input port.
596
+ Checks: required fields, `prompt`/`command` exclusivity, duplicate task IDs within a track, `depends_on`/`continue_from` reference integrity (including ambiguous bare refs that exist in multiple tracks — use `trackId.taskId` to disambiguate), circular dependency detection, port shape (name format, valid `type`, duplicate names, `enum` requires non-empty `enum` array, `required`/`from` ignored on outputs), `{{inputs.<name>}}` references resolving to a declared input port, and `permissions` shape (must be an object with boolean `read`/`write`/`execute`). Tolerant of half-built configs — non-array `tracks` or `tasks` produce a structured error instead of throwing.
547
597
 
548
- Does **not** check plugin registration (plugins may not be loaded at edit time).
598
+ Plugin-type checks are opt-in via `knownTypes`: when provided, references to trigger/completion/middleware/driver types that are neither built-in nor in the supplied set produce a **warning** (`severity: 'warning'`) so editors can light up uninstalled plugins without blocking save / run. Omit `knownTypes` for offline / pre-load validation — no plugin warnings are emitted in that case.
549
599
 
550
600
  ```ts
551
- const errors = validateRaw(draftConfig);
552
- if (errors.length > 0) {
553
- errors.forEach((e) => highlightNode(e.path, e.message));
601
+ const errors = validateRaw(draftConfig, {
602
+ triggers: registry.list('triggers'),
603
+ completions: registry.list('completions'),
604
+ middlewares: registry.list('middlewares'),
605
+ drivers: registry.list('drivers'),
606
+ });
607
+ errors.forEach((e) => highlightNode(e.path, e.message, e.severity ?? 'error'));
608
+ ```
609
+
610
+ ### `compileYamlContent(content: string, opts?: CompileYamlOptions): YamlCompileResult`
611
+
612
+ One-shot YAML → diagnostics for editor compile flows. Distinguishes **YAML syntax errors** (`parseOk: false`, `validation.errors` empty, `summary` starts with `"YAML parse error:"`) from **schema / structure errors** (`parseOk: true`, errors land in `validation.errors`). Half-built configs — missing top-level `pipeline`, non-object `pipeline`, non-array `tracks` / `tasks`, malformed `permissions` — surface as ordinary validation errors rather than parse failures or `"Validation crashed"` messages, so the editor never crashes on in-progress YAML.
613
+
614
+ ```ts
615
+ const result = compileYamlContent(yaml, {
616
+ sourceName: 'pipeline.yaml',
617
+ knownTypes: {
618
+ triggers: registry.list('triggers'),
619
+ completions: registry.list('completions'),
620
+ middlewares: registry.list('middlewares'),
621
+ drivers: registry.list('drivers'),
622
+ },
623
+ });
624
+
625
+ if (!result.parseOk) {
626
+ showParseError(result.summary); // YAML syntax broken
627
+ } else if (!result.success) {
628
+ result.validation.errors.forEach((e) => highlightNode(e.path, e.message));
629
+ result.validation.warnings.forEach((w) => softHint(w.path, w.message));
554
630
  }
555
631
  ```
556
632
 
633
+ `opts.knownTypes` is forwarded to `validateRaw` (see above) — pass it when the host has loaded its plugin registry so unregistered plugin types light up as warnings; omit it for offline validation.
634
+
635
+ `YamlCompileResult` shape: `{ timestamp, sourceName, success, parseOk, validation: { errors, warnings }, summary }`. `success` is `true` only when there are zero errors (warnings don't count). `validation.errors` and `validation.warnings` carry `{ path, message }` projections of the underlying `ValidationError` list.
636
+
557
637
  ### `buildRawDag(config: RawPipelineConfig): RawDag`
558
638
 
559
639
  Extracts the topology of a raw (unresolved) pipeline config as a graph — no `workDir` or plugin registration required. Intended for the visual editor to render the flow graph during editing.
@@ -12,7 +12,7 @@ export declare function setPipelineField(config: RawPipelineConfig, fields: Part
12
12
  */
13
13
  export declare function upsertTrack(config: RawPipelineConfig, track: RawTrackConfig): RawPipelineConfig;
14
14
  /**
15
- * Remove a track by id. No-op if the id is not found.
15
+ * Remove a track by id. No-op (same reference) if the id is not found.
16
16
  */
17
17
  export declare function removeTrack(config: RawPipelineConfig, trackId: string): RawPipelineConfig;
18
18
  /**
@@ -22,11 +22,12 @@ export declare function removeTrack(config: RawPipelineConfig, trackId: string):
22
22
  export declare function moveTrack(config: RawPipelineConfig, trackId: string, toIndex: number): RawPipelineConfig;
23
23
  /**
24
24
  * Update fields on a single track (excluding tasks list, use upsertTask / removeTask for that).
25
+ * No-op (same reference) if the trackId is not found.
25
26
  */
26
27
  export declare function updateTrack(config: RawPipelineConfig, trackId: string, fields: Partial<Omit<RawTrackConfig, 'id' | 'tasks'>>): RawPipelineConfig;
27
28
  /**
28
29
  * Insert or replace a task within a track, matched by task.id. Appends if new.
29
- * No-op if the trackId is not found.
30
+ * No-op (same reference) if the trackId is not found.
30
31
  */
31
32
  export declare function upsertTask(config: RawPipelineConfig, trackId: string, task: RawTaskConfig): RawPipelineConfig;
32
33
  /**
@@ -40,6 +41,7 @@ export declare function removeTask(config: RawPipelineConfig, trackId: string, t
40
41
  /**
41
42
  * Reorder a task within its track.
42
43
  * Clamps toIndex to valid bounds.
44
+ * No-op (same reference) if the trackId or taskId is not found.
43
45
  */
44
46
  export declare function moveTask(config: RawPipelineConfig, trackId: string, taskId: string, toIndex: number): RawPipelineConfig;
45
47
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"config-ops.d.ts","sourceRoot":"","sources":["../src/config-ops.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAIhF;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAEnE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC,GACjD,iBAAiB,CAEnB;AAID;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,cAAc,GAAG,iBAAiB,CAQ/F;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAEzF;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,iBAAiB,CAQnB;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC,GACpD,iBAAiB,CAKnB;AAID;;;GAGG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,aAAa,GAClB,iBAAiB,CAYnB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,SAAS,UAAQ,GAChB,iBAAiB,CAmDnB;AAkBD;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,iBAAiB,CAcnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,iBAAiB,EACzB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,WAAW,UAAO,GACjB,iBAAiB,CA6DnB"}
1
+ {"version":3,"file":"config-ops.d.ts","sourceRoot":"","sources":["../src/config-ops.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAIhF;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAEnE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC,GACjD,iBAAiB,CAEnB;AAID;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,cAAc,GAAG,iBAAiB,CAQ/F;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAGzF;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,iBAAiB,CAQnB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC,GACpD,iBAAiB,CAMnB;AAID;;;GAGG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,aAAa,GAClB,iBAAiB,CAanB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,SAAS,UAAQ,GAChB,iBAAiB,CAsDnB;AAkBD;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,iBAAiB,CAgBnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,iBAAiB,EACzB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,WAAW,UAAO,GACjB,iBAAiB,CAwEnB"}
@@ -32,9 +32,11 @@ export function upsertTrack(config, track) {
32
32
  };
33
33
  }
34
34
  /**
35
- * Remove a track by id. No-op if the id is not found.
35
+ * Remove a track by id. No-op (same reference) if the id is not found.
36
36
  */
37
37
  export function removeTrack(config, trackId) {
38
+ if (!config.tracks.some((t) => t.id === trackId))
39
+ return config;
38
40
  return { ...config, tracks: config.tracks.filter((t) => t.id !== trackId) };
39
41
  }
40
42
  /**
@@ -53,8 +55,11 @@ export function moveTrack(config, trackId, toIndex) {
53
55
  }
54
56
  /**
55
57
  * Update fields on a single track (excluding tasks list, use upsertTask / removeTask for that).
58
+ * No-op (same reference) if the trackId is not found.
56
59
  */
57
60
  export function updateTrack(config, trackId, fields) {
61
+ if (!config.tracks.some((t) => t.id === trackId))
62
+ return config;
58
63
  return {
59
64
  ...config,
60
65
  tracks: config.tracks.map((t) => (t.id === trackId ? { ...t, ...fields } : t)),
@@ -63,9 +68,11 @@ export function updateTrack(config, trackId, fields) {
63
68
  // ── Tasks ──
64
69
  /**
65
70
  * Insert or replace a task within a track, matched by task.id. Appends if new.
66
- * No-op if the trackId is not found.
71
+ * No-op (same reference) if the trackId is not found.
67
72
  */
68
73
  export function upsertTask(config, trackId, task) {
74
+ if (!config.tracks.some((t) => t.id === trackId))
75
+ return config;
69
76
  return {
70
77
  ...config,
71
78
  tracks: config.tracks.map((t) => {
@@ -87,6 +94,9 @@ export function upsertTask(config, trackId, task) {
87
94
  * validateRaw from reporting dangling-ref errors after the deletion.
88
95
  */
89
96
  export function removeTask(config, trackId, taskId, cleanRefs = false) {
97
+ const track = config.tracks.find((t) => t.id === trackId);
98
+ if (!track || !track.tasks.some((tk) => tk.id === taskId))
99
+ return config;
90
100
  const withoutTask = {
91
101
  ...config,
92
102
  tracks: config.tracks.map((t) => {
@@ -151,8 +161,12 @@ function cleanTaskRefs(task, isRemoved) {
151
161
  /**
152
162
  * Reorder a task within its track.
153
163
  * Clamps toIndex to valid bounds.
164
+ * No-op (same reference) if the trackId or taskId is not found.
154
165
  */
155
166
  export function moveTask(config, trackId, taskId, toIndex) {
167
+ const track = config.tracks.find((t) => t.id === trackId);
168
+ if (!track || !track.tasks.some((tk) => tk.id === taskId))
169
+ return config;
156
170
  return {
157
171
  ...config,
158
172
  tracks: config.tracks.map((t) => {
@@ -181,7 +195,13 @@ export function moveTask(config, trackId, taskId, toIndex) {
181
195
  export function transferTask(config, fromTrackId, taskId, toTrackId, qualifyRefs = true) {
182
196
  if (fromTrackId === toTrackId)
183
197
  return config;
198
+ const targetTrack = config.tracks.find((t) => t.id === toTrackId);
199
+ if (!targetTrack)
200
+ return config;
201
+ if (targetTrack.tasks.some((tk) => tk.id === taskId))
202
+ return config;
184
203
  let task;
204
+ let sourceLocalIds = new Set();
185
205
  const afterRemove = {
186
206
  ...config,
187
207
  tracks: config.tracks.map((t) => {
@@ -190,12 +210,20 @@ export function transferTask(config, fromTrackId, taskId, toTrackId, qualifyRefs
190
210
  const found = t.tasks.find((tk) => tk.id === taskId);
191
211
  if (!found)
192
212
  return t;
213
+ sourceLocalIds = new Set(t.tasks.map((tk) => tk.id));
193
214
  task = found;
194
215
  return { ...t, tasks: t.tasks.filter((tk) => tk.id !== taskId) };
195
216
  }),
196
217
  };
197
218
  if (!task)
198
219
  return config;
220
+ if (qualifyRefs) {
221
+ task = qualifyTaskRefs(task, (ref) => {
222
+ if (ref.includes('.') || !sourceLocalIds.has(ref))
223
+ return ref;
224
+ return `${fromTrackId}.${ref}`;
225
+ });
226
+ }
199
227
  const afterInsert = upsertTask(afterRemove, toTrackId, task);
200
228
  if (!qualifyRefs)
201
229
  return afterInsert;
@@ -1 +1 @@
1
- {"version":3,"file":"config-ops.js","sourceRoot":"","sources":["../src/config-ops.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config-ops.js","sourceRoot":"","sources":["../src/config-ops.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EAEd,SAAS,EAaT,eAAe,EAEhB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AA4BlE,OAAO,EAA2B,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAM3E,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AA6ED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CACjD;AAWD,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA8C/C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC;IAC3C;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;CACpC;AAWD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,YAAY,CAAC,CA4rCvB"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,cAAc,EAEd,SAAS,EAaT,eAAe,EAEhB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AA+BlE,OAAO,EAA2B,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAM3E,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EAAG,iBAAiB,CAAU;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAyFD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CACjD;AAWD,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA8C/C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC;IAC3C;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IACpD;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;CACpC;AAWD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,YAAY,CAAC,CAuvCvB"}
package/dist/engine.js CHANGED
@@ -5,7 +5,7 @@ import { defaultRegistry } from './registry';
5
5
  import { runSpawn, runCommand } from './runner';
6
6
  import { parseDuration, nowISO, generateRunId } from './utils';
7
7
  import { promptDocumentFromString, serializePromptDocument, prependContext, renderInputsBlock, renderOutputSchemaBlock, } from './prompt-doc';
8
- import { extractTaskOutputs, inferPromptPorts, resolveTaskInputs, substituteInputs, } from './ports';
8
+ import { extractTaskBindingOutputs, extractTaskOutputs, inferPromptPorts, resolveTaskBindingInputs, resolveTaskInputs, substituteInputs, } from './ports';
9
9
  import { executeHook, buildPipelineStartContext, buildTaskContext, buildPipelineCompleteContext, buildPipelineErrorContext, } from './hooks';
10
10
  import { Logger, tailLines, clip } from './logger';
11
11
  import { InMemoryApprovalGateway } from './approval';
@@ -26,6 +26,12 @@ export class TriggerTimeoutError extends Error {
26
26
  this.name = 'TriggerTimeoutError';
27
27
  }
28
28
  }
29
+ function isPromptTaskConfig(task) {
30
+ return task.prompt !== undefined && task.command === undefined;
31
+ }
32
+ function isCommandTaskConfig(task) {
33
+ return task.command !== undefined && task.prompt === undefined;
34
+ }
29
35
  // ═══ Preflight Validation ═══
30
36
  function preflight(config, dag, registry) {
31
37
  const errors = [];
@@ -34,7 +40,7 @@ function preflight(config, dag, registry) {
34
40
  const track = node.track;
35
41
  const driverName = task.driver ?? track.driver ?? config.driver ?? 'opencode';
36
42
  // Pure command tasks don't use a driver — skip driver registration check.
37
- const isCommandOnly = task.command && !task.prompt;
43
+ const isCommandOnly = isCommandTaskConfig(task);
38
44
  if (!isCommandOnly && !registry.hasHandler('drivers', driverName)) {
39
45
  errors.push(`Task "${node.taskId}": driver "${driverName}" not registered`);
40
46
  }
@@ -171,7 +177,7 @@ export async function runPipeline(config, workDir, options = {}) {
171
177
  log.section('DAG topology');
172
178
  for (const [id, node] of dag.nodes) {
173
179
  const deps = node.dependsOn.length ? node.dependsOn.join(', ') : '(root)';
174
- const kind = node.task.prompt ? 'ai' : 'cmd';
180
+ const kind = isPromptTaskConfig(node.task) ? 'ai' : 'cmd';
175
181
  log.quiet(` • ${id} [${kind}] track=${node.track.id} deps=[${deps}]`);
176
182
  }
177
183
  log.quiet('');
@@ -226,13 +232,12 @@ export async function runPipeline(config, workDir, options = {}) {
226
232
  emit({ type: 'run_start', runId, tasks: runStartTasks });
227
233
  const sessionMap = new Map();
228
234
  const normalizedMap = new Map();
229
- // Extracted port outputs keyed by fully-qualified task id. Populated
230
- // after a task succeeds when its `ports.outputs` is declared; read by
231
- // downstream tasks via `resolveTaskInputs` to assemble their inputs.
232
- // Kept separate from normalizedMap so the continue_from text handoff
233
- // and the typed-port data handoff don't pollute each other — they
234
- // solve different problems and have different lifetimes.
235
+ // Published structured outputs keyed by fully-qualified task id.
236
+ // Includes lightweight task.outputs and strict ports.outputs.
235
237
  const outputValuesMap = new Map();
238
+ // Full upstream result data for lightweight input bindings such as
239
+ // `taskId.stdout` and `taskId.outputs.name`.
240
+ const bindingDataMap = new Map();
236
241
  // Resolved port inputs keyed by fully-qualified task id. Written once,
237
242
  // just before a task runs, so every subsequent task_update event can
238
243
  // echo them to the UI without re-resolving.
@@ -410,7 +415,7 @@ export async function runPipeline(config, workDir, options = {}) {
410
415
  return {
411
416
  id: taskId,
412
417
  name: state.config.name,
413
- type: state.config.prompt ? 'ai' : 'command',
418
+ type: isPromptTaskConfig(state.config) ? 'ai' : 'command',
414
419
  status: state.status,
415
420
  exit_code: state.result?.exitCode ?? null,
416
421
  duration_ms: state.result?.durationMs ?? null,
@@ -434,7 +439,7 @@ export async function runPipeline(config, workDir, options = {}) {
434
439
  const task = node.task;
435
440
  const track = node.track;
436
441
  log.section(`Task ${taskId}`, taskId);
437
- log.debug(`[task:${taskId}]`, `type=${task.prompt ? 'ai' : 'cmd'} track=${track.id} deps=[${node.dependsOn.join(', ') || '(root)'}]`);
442
+ log.debug(`[task:${taskId}]`, `type=${isPromptTaskConfig(task) ? 'ai' : 'cmd'} track=${track.id} deps=[${node.dependsOn.join(', ') || '(root)'}]`);
438
443
  // 1. Check dependencies
439
444
  for (const depId of node.dependsOn) {
440
445
  const result = isDependencySatisfied(depId);
@@ -587,14 +592,14 @@ export async function runPipeline(config, workDir, options = {}) {
587
592
  // pipeline the Command path uses. Collisions that a Prompt can't
588
593
  // disambiguate (same input name on two upstreams, incompatible
589
594
  // downstream output types) block the task with a clear message.
590
- const isPromptTask = task.prompt !== undefined && task.command === undefined;
595
+ const isPromptTask = isPromptTaskConfig(task);
591
596
  let effectivePorts = task.ports;
592
597
  let promptInferenceBlockReason = null;
593
598
  if (isPromptTask) {
594
599
  const inference = inferPromptPorts({
595
600
  upstreams: node.dependsOn.map((upstreamId) => {
596
601
  const upstream = dag.nodes.get(upstreamId);
597
- const isUpstreamCommand = !!upstream?.task.command;
602
+ const isUpstreamCommand = upstream ? isCommandTaskConfig(upstream.task) : false;
598
603
  return {
599
604
  taskId: upstreamId,
600
605
  outputs: isUpstreamCommand ? upstream?.task.ports?.outputs : undefined,
@@ -602,7 +607,7 @@ export async function runPipeline(config, workDir, options = {}) {
602
607
  }),
603
608
  downstreams: (directDownstreams.get(taskId) ?? []).map((downstreamId) => {
604
609
  const downstream = dag.nodes.get(downstreamId);
605
- const isDownstreamCommand = !!downstream?.task.command;
610
+ const isDownstreamCommand = downstream ? isCommandTaskConfig(downstream.task) : false;
606
611
  return {
607
612
  taskId: downstreamId,
608
613
  inputs: isDownstreamCommand ? downstream?.task.ports?.inputs : undefined,
@@ -645,6 +650,36 @@ export async function runPipeline(config, workDir, options = {}) {
645
650
  applyStopAll(node.track.id);
646
651
  return;
647
652
  }
653
+ const bindingResolution = resolveTaskBindingInputs(task, bindingDataMap, node.dependsOn);
654
+ if (bindingResolution.kind === 'blocked') {
655
+ log.error(`[task:${taskId}]`, `blocked — cannot resolve task input bindings:\n${bindingResolution.reason}`);
656
+ state.result = {
657
+ exitCode: -1,
658
+ stdout: '',
659
+ stderr: `[engine] task input binding resolution failed:\n${bindingResolution.reason}`,
660
+ stdoutPath: null,
661
+ stderrPath: null,
662
+ durationMs: 0,
663
+ sessionId: null,
664
+ normalizedOutput: null,
665
+ failureKind: 'spawn_error',
666
+ outputs: null,
667
+ };
668
+ state.finishedAt = nowISO();
669
+ setTaskStatus(taskId, 'blocked');
670
+ try {
671
+ await fireHook(taskId, 'task_failure');
672
+ }
673
+ catch (hookErr) {
674
+ log.error(`[task:${taskId}]`, `hook execution failed: ${hookErr instanceof Error ? hookErr.message : String(hookErr)}`);
675
+ }
676
+ if (getOnFailure(taskId) === 'stop_all')
677
+ applyStopAll(node.track.id);
678
+ return;
679
+ }
680
+ if (bindingResolution.missingOptional.length > 0) {
681
+ log.debug(`[task:${taskId}]`, `optional input bindings unresolved (empty in placeholders): ${bindingResolution.missingOptional.join(', ')}`);
682
+ }
648
683
  // Feed effective ports into `resolveTaskInputs` by shallow-cloning
649
684
  // the task. Prompt tasks get the inferred ports; Command tasks are
650
685
  // unchanged (effectivePorts === task.ports).
@@ -676,7 +711,7 @@ export async function runPipeline(config, workDir, options = {}) {
676
711
  applyStopAll(node.track.id);
677
712
  return;
678
713
  }
679
- const resolvedInputs = inputResolution.inputs;
714
+ const resolvedInputs = { ...bindingResolution.inputs, ...inputResolution.inputs };
680
715
  resolvedInputsMap.set(taskId, resolvedInputs);
681
716
  if (inputResolution.missingOptional.length > 0) {
682
717
  log.debug(`[task:${taskId}]`, `optional inputs unresolved (empty in placeholders): ${inputResolution.missingOptional.join(', ')}`);
@@ -689,7 +724,7 @@ export async function runPipeline(config, workDir, options = {}) {
689
724
  // complete task_update (startedAt non-null) on the status transition.
690
725
  state.startedAt = nowISO();
691
726
  setTaskStatus(taskId, 'running');
692
- log.info(`[task:${taskId}]`, task.command ? `running: ${task.command}` : `running (driver task)`);
727
+ log.info(`[task:${taskId}]`, isCommandTaskConfig(task) ? `running: ${task.command}` : `running (driver task)`);
693
728
  // File-only: resolved config for this task
694
729
  const resolvedDriver = task.driver ?? track.driver ?? config.driver ?? 'opencode';
695
730
  const resolvedModel = task.model ?? track.model ?? config.model ?? '(default)';
@@ -719,7 +754,7 @@ export async function runPipeline(config, workDir, options = {}) {
719
754
  stdoutPath,
720
755
  stderrPath,
721
756
  };
722
- if (task.command) {
757
+ if (isCommandTaskConfig(task)) {
723
758
  // Substitute `{{inputs.X}}` placeholders into the command
724
759
  // string. Tasks with no declared inputs always produce the same
725
760
  // string back (no placeholders to match). Unresolved references
@@ -904,24 +939,27 @@ export async function runPipeline(config, workDir, options = {}) {
904
939
  else {
905
940
  terminalStatus = 'success';
906
941
  }
907
- // Extract declared port outputs from the task's output stream.
908
- // Only meaningful on success — a failed task's output is whatever
909
- // the child happened to emit before exiting, and downstream tasks
910
- // shouldn't receive partial data. `extractTaskOutputs` is a no-op
911
- // when the task has no declared outputs, so this is free for
912
- // pre-ports tasks. Diagnostics are appended to stderr so users
913
- // see *why* a downstream input is missing without having to dig
914
- // through driver-specific logs.
942
+ // Extract declared outputs from the task's output stream. Only
943
+ // meaningful on success — a failed task's output is whatever the
944
+ // child happened to emit before exiting, and downstream tasks
945
+ // shouldn't receive partial data.
915
946
  let extractedOutputs = null;
916
947
  if (terminalStatus === 'success') {
948
+ const looseExtraction = extractTaskBindingOutputs(task.outputs, result.stdout, result.stderr, result.normalizedOutput);
949
+ if (task.outputs && Object.keys(task.outputs).length > 0) {
950
+ extractedOutputs = looseExtraction.outputs;
951
+ log.debug(`[task:${taskId}]`, `extracted binding outputs: ${JSON.stringify(looseExtraction.outputs)}`);
952
+ if (looseExtraction.diagnostic) {
953
+ log.debug(`[task:${taskId}]`, looseExtraction.diagnostic);
954
+ }
955
+ }
917
956
  // Prompt tasks use inferred ports (from direct-downstream Command
918
957
  // inputs); Command tasks use their declared ports. Either way,
919
958
  // `extractTaskOutputs` is a no-op when there are no declared
920
959
  // outputs to pull, so pre-ports tasks pay nothing for this call.
921
960
  const extraction = extractTaskOutputs(effectivePorts, result.stdout, result.normalizedOutput);
922
961
  if (effectivePorts?.outputs && effectivePorts.outputs.length > 0) {
923
- extractedOutputs = extraction.outputs;
924
- outputValuesMap.set(taskId, extraction.outputs);
962
+ extractedOutputs = { ...(extractedOutputs ?? {}), ...extraction.outputs };
925
963
  log.debug(`[task:${taskId}]`, `extracted outputs: ${JSON.stringify(extraction.outputs)}` +
926
964
  (isPromptTask ? ' (inferred from downstream Commands)' : ''));
927
965
  if (extraction.diagnostic) {
@@ -936,6 +974,16 @@ export async function runPipeline(config, workDir, options = {}) {
936
974
  // — hooks, wire events, test assertions — all go through this
937
975
  // one field rather than re-running extraction.
938
976
  result = { ...result, outputs: extractedOutputs };
977
+ if (extractedOutputs !== null) {
978
+ outputValuesMap.set(taskId, extractedOutputs);
979
+ }
980
+ bindingDataMap.set(taskId, {
981
+ outputs: extractedOutputs,
982
+ stdout: result.stdout,
983
+ stderr: result.stderr,
984
+ normalizedOutput: result.normalizedOutput,
985
+ exitCode: result.exitCode,
986
+ });
939
987
  // Store normalized text separately (in-memory) for continue_from handoff.
940
988
  // R15: clip oversized values so a runaway parseResult can't accumulate
941
989
  // hundreds of MB across tasks.