taskflow-hosts 0.1.6
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/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/claude-runner.d.ts +104 -0
- package/dist/claude-runner.d.ts.map +1 -0
- package/dist/claude-runner.js +259 -0
- package/dist/claude-runner.js.map +1 -0
- package/dist/codex-runner.d.ts +93 -0
- package/dist/codex-runner.d.ts.map +1 -0
- package/dist/codex-runner.js +205 -0
- package/dist/codex-runner.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/opencode-runner.d.ts +115 -0
- package/dist/opencode-runner.d.ts.map +1 -0
- package/dist/opencode-runner.js +232 -0
- package/dist/opencode-runner.js.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 heggria
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# taskflow-hosts
|
|
2
|
+
|
|
3
|
+
> Shared host-runner collection for [taskflow](https://github.com/heggria/taskflow).
|
|
4
|
+
|
|
5
|
+
This package holds the `SubagentRunner` implementations for taskflow's non-pi
|
|
6
|
+
hosts — **codex**, **claude**, and **opencode** — plus their pure argv builders
|
|
7
|
+
(`buildCodexArgs` / `buildClaudeArgs` / `buildOpencodeArgs`) and event-stream
|
|
8
|
+
parsers. It is the **single place** host runners live; a new host adds a
|
|
9
|
+
`<host>-runner.ts` here.
|
|
10
|
+
|
|
11
|
+
## Why a separate package
|
|
12
|
+
|
|
13
|
+
Each host has two halves:
|
|
14
|
+
|
|
15
|
+
1. **The runner** — spawn that host's CLI, fold its JSON event stream into a
|
|
16
|
+
host-neutral `RunResult`, classify the outcome. + the pure argv builder +
|
|
17
|
+
permission/model helpers.
|
|
18
|
+
2. **The delivery** — the per-host MCP server + bin + plugin scaffold, which is
|
|
19
|
+
that host ecosystem's install target (`codex plugin add`, `claude plugin
|
|
20
|
+
install`, OpenCode config).
|
|
21
|
+
|
|
22
|
+
Half #1 is nearly identical in *shape* across hosts and changes for the same
|
|
23
|
+
reasons (a `taskflow-core` contract change, or a host CLI flag change). Half #2
|
|
24
|
+
is genuinely host-specific (different install mechanisms, different plugin
|
|
25
|
+
manifests). So #1 is collected here; #2 stays in `codex-taskflow` /
|
|
26
|
+
`claude-taskflow` / `opencode-taskflow`, which import their runner from this
|
|
27
|
+
package.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
You usually don't install this directly — install the host delivery package:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install -g codex-taskflow # or claude-taskflow / opencode-taskflow
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For code-level use:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install taskflow-hosts
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
// one host, tree-shaken:
|
|
47
|
+
import { codexSubagentRunner, buildCodexArgs } from "taskflow-hosts/codex";
|
|
48
|
+
|
|
49
|
+
// or the barrel:
|
|
50
|
+
import { claudeSubagentRunner, opencodeSubagentRunner } from "taskflow-hosts";
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Each runner export includes: the `SubagentRunner` (`codexSubagentRunner` / …),
|
|
54
|
+
the `runXxxAgentTask` function, the pure `buildXxxArgs` builder, the
|
|
55
|
+
`foldXxxEventLine` parser + `newXxxAccumulator`, and the permission/model
|
|
56
|
+
helpers (`sandboxForTools`, `permissionArgsForTools`, `isReadOnlyPhase`,
|
|
57
|
+
`resolveXxxModel`, `xxxBin`).
|
|
58
|
+
|
|
59
|
+
## Adding a host
|
|
60
|
+
|
|
61
|
+
1. Add `<host>-runner.ts` in `src/` (model it on an existing one — a pure
|
|
62
|
+
`buildXxxArgs` + the event parser + a `SubagentRunner` export).
|
|
63
|
+
2. Add an export entry in `src/index.ts`.
|
|
64
|
+
3. Add `*-runner.test.ts` (event-stream parser) + `*-args.test.ts` (argv
|
|
65
|
+
contract — CI-locked) in `test/`.
|
|
66
|
+
4. The host's *delivery* (MCP server/bin + plugin scaffold) goes in a
|
|
67
|
+
`taskflow-hosts`-depending package of its own.
|
|
68
|
+
|
|
69
|
+
See [`AGENTS.md`](../../AGENTS.md) and [`DECISIONS.md`](../../DECISIONS.md) in
|
|
70
|
+
the repo root for the full architecture and the packaging decision.
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
MIT
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code subagent runner — the Claude host's `SubagentRunner` implementation.
|
|
3
|
+
*
|
|
4
|
+
* Spawns an isolated `claude -p --output-format stream-json` process per task
|
|
5
|
+
* and folds its JSONL event stream into the same host-neutral `RunResult` the
|
|
6
|
+
* pi and codex runners produce, so the engine (runtime.ts) treats a Claude
|
|
7
|
+
* subagent identically to a pi or codex one.
|
|
8
|
+
*
|
|
9
|
+
* Real claude stream-json schema (claude-code ≥ 2.1), observed empirically:
|
|
10
|
+
* {"type":"system","subtype":"init","model":"claude-…","tools":[…],…}
|
|
11
|
+
* {"type":"assistant","message":{"model":"…","content":[{"type":"text","text":"…"},
|
|
12
|
+
* {"type":"tool_use","name":"Bash","input":{…}}],"usage":{"input_tokens":…,
|
|
13
|
+
* "output_tokens":…,"cache_read_input_tokens":…,"cache_creation_input_tokens":…}},…}
|
|
14
|
+
* {"type":"user","message":{…tool_result…}}
|
|
15
|
+
* {"type":"result","subtype":"success"|"error_max_turns"|…,"is_error":bool,
|
|
16
|
+
* "num_turns":N,"result":"…","total_cost_usd":X,"usage":{…}} ← authoritative
|
|
17
|
+
*
|
|
18
|
+
* Mapping to the host-neutral contract:
|
|
19
|
+
* - output = `result` field of the result event (fallback: last assistant text)
|
|
20
|
+
* - usage = the result event's cumulative usage + num_turns + total_cost_usd
|
|
21
|
+
* (assistant events accumulate per-turn usage for live streaming;
|
|
22
|
+
* the result event overwrites with the authoritative totals)
|
|
23
|
+
* - lastActivity = latest assistant text or a one-line tool summary
|
|
24
|
+
* - failure = result.is_error, a stream-level error, or a non-zero exit
|
|
25
|
+
*
|
|
26
|
+
* Permission mapping (the codex `sandboxForTools` analogue): Claude Code has no
|
|
27
|
+
* OS-level sandbox in -p mode — a tool call is either whitelisted or denied. A
|
|
28
|
+
* phase whose tool whitelist is read-only gets `--allowedTools <read-only set>`
|
|
29
|
+
* (mutating tools are denied outright); anything else gets
|
|
30
|
+
* `--permission-mode bypassPermissions`, which behaves like codex's
|
|
31
|
+
* workspace-write but WITHOUT an OS sandbox backstop — document accordingly.
|
|
32
|
+
*
|
|
33
|
+
* Process handling (idle watchdog, abort, signal-kill detection, stderr cap,
|
|
34
|
+
* error sanitization) mirrors the pi/codex runners so behavior is uniform.
|
|
35
|
+
*/
|
|
36
|
+
import { type AgentConfig, type LiveUpdate, type RunOptions, type RunResult, type SubagentRunner, type UsageStats } from "taskflow-core";
|
|
37
|
+
/** Accumulated state folded from a claude stream-json event stream. */
|
|
38
|
+
export interface ClaudeAccumulator {
|
|
39
|
+
usage: UsageStats;
|
|
40
|
+
model?: string;
|
|
41
|
+
/** Final answer = the result event's `result`, else the last assistant text. */
|
|
42
|
+
finalText: string;
|
|
43
|
+
lastActivity: string;
|
|
44
|
+
/** Set when the stream reported a fatal error (result.is_error / stream error). */
|
|
45
|
+
fatalError?: string;
|
|
46
|
+
/** True once the authoritative `result` event has been folded. */
|
|
47
|
+
sawResult: boolean;
|
|
48
|
+
}
|
|
49
|
+
export declare function newClaudeAccumulator(model?: string): ClaudeAccumulator;
|
|
50
|
+
/**
|
|
51
|
+
* Fold one claude stream-json line into the accumulator. Returns a LiveUpdate
|
|
52
|
+
* when the stream produced new activity (for streaming), else null. Empty or
|
|
53
|
+
* malformed lines are ignored — robust to partial buffers and noise,
|
|
54
|
+
* unit-testable without spawning a process.
|
|
55
|
+
*/
|
|
56
|
+
export declare function foldClaudeEventLine(acc: ClaudeAccumulator, line: string): LiveUpdate | null;
|
|
57
|
+
/** Override the claude binary (tests / unusual installs). */
|
|
58
|
+
export declare function claudeBin(): string;
|
|
59
|
+
/**
|
|
60
|
+
* Map a phase's tool whitelist to claude permission flags — the codex
|
|
61
|
+
* `sandboxForTools` analogue. A phase that only reads (no write/edit/bash in
|
|
62
|
+
* its whitelist) gets a read-only `--allowedTools` set; anything else gets
|
|
63
|
+
* `--permission-mode bypassPermissions` (non-interactive -p runs cannot answer
|
|
64
|
+
* permission prompts, so the codex workspace-write equivalent is bypass —
|
|
65
|
+
* WITHOUT an OS sandbox backstop). No whitelist → bypass (the engine's
|
|
66
|
+
* default-capable agent).
|
|
67
|
+
*/
|
|
68
|
+
export declare function permissionArgsForTools(tools: string[] | undefined): string[];
|
|
69
|
+
/** Resolve a modelRoles/pi model id for `claude --model`, or `undefined` to let
|
|
70
|
+
* claude fall back to its own default. Claude model ids are FLAT (aliases like
|
|
71
|
+
* "sonnet"/"haiku" or full ids like "claude-sonnet-4-6"), so — same rule as
|
|
72
|
+
* codex — a pi-provider path (contains "/") or an unresolved {{placeholder}}
|
|
73
|
+
* is dropped. Exported for unit-testing the contract. */
|
|
74
|
+
export declare function resolveClaudeModel(model: string | undefined): string | undefined;
|
|
75
|
+
/** Context for {@link buildClaudeArgs} — the pure inputs to argv construction. */
|
|
76
|
+
export interface ClaudeArgsCtx {
|
|
77
|
+
systemPrompt: string;
|
|
78
|
+
task: string;
|
|
79
|
+
/** Already-resolved model (opts.model ?? agent.model). */
|
|
80
|
+
model?: string;
|
|
81
|
+
tools?: string[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Build the full `claude -p` argv from a phase's resolved context — PURE
|
|
85
|
+
* (no process.env, no spawn). Extracted from `runClaudeAgentTask` so the host's
|
|
86
|
+
* CLI flag contract is unit-testable in CI without a live claude session.
|
|
87
|
+
*
|
|
88
|
+
* claude -p --output-format stream-json --verbose --strict-mcp-config
|
|
89
|
+
* [--permission-mode bypassPermissions | --allowedTools ...]
|
|
90
|
+
* [--model m] [--append-system-prompt ...] <task>
|
|
91
|
+
*/
|
|
92
|
+
export declare function buildClaudeArgs(ctx: ClaudeArgsCtx): string[];
|
|
93
|
+
/**
|
|
94
|
+
* Run a single subagent task via `claude -p --output-format stream-json`.
|
|
95
|
+
* Resolves the agent from `agents` by name; returns the same structured
|
|
96
|
+
* `RunResult` the pi and codex runners do.
|
|
97
|
+
*/
|
|
98
|
+
export declare function runClaudeAgentTask(defaultCwd: string, agents: AgentConfig[], agentName: string, task: string, opts: RunOptions, globalThinking?: string): Promise<RunResult>;
|
|
99
|
+
/**
|
|
100
|
+
* The Claude host's `SubagentRunner`. Drops into `RuntimeDeps.runTask` exactly
|
|
101
|
+
* like the pi/codex/opencode runners, so the engine runs unchanged on Claude Code.
|
|
102
|
+
*/
|
|
103
|
+
export declare const claudeSubagentRunner: SubagentRunner<AgentConfig>;
|
|
104
|
+
//# sourceMappingURL=claude-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-runner.d.ts","sourceRoot":"","sources":["../src/claude-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAIN,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,MAAM,eAAe,CAAC;AASvB,uEAAuE;AACvE,MAAM,WAAW,iBAAiB;IACjC,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,SAAS,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAEtE;AA+BD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAyE3F;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,EAAE,CAM5E;AAED;;;;0DAI0D;AAC1D,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAKhF;AAED,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,EAAE,CAQ5D;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACvC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,EAAE,EACrB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,cAAc,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,SAAS,CAAC,CA4BpB;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,cAAc,CAAC,WAAW,CAE5D,CAAC"}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code subagent runner — the Claude host's `SubagentRunner` implementation.
|
|
3
|
+
*
|
|
4
|
+
* Spawns an isolated `claude -p --output-format stream-json` process per task
|
|
5
|
+
* and folds its JSONL event stream into the same host-neutral `RunResult` the
|
|
6
|
+
* pi and codex runners produce, so the engine (runtime.ts) treats a Claude
|
|
7
|
+
* subagent identically to a pi or codex one.
|
|
8
|
+
*
|
|
9
|
+
* Real claude stream-json schema (claude-code ≥ 2.1), observed empirically:
|
|
10
|
+
* {"type":"system","subtype":"init","model":"claude-…","tools":[…],…}
|
|
11
|
+
* {"type":"assistant","message":{"model":"…","content":[{"type":"text","text":"…"},
|
|
12
|
+
* {"type":"tool_use","name":"Bash","input":{…}}],"usage":{"input_tokens":…,
|
|
13
|
+
* "output_tokens":…,"cache_read_input_tokens":…,"cache_creation_input_tokens":…}},…}
|
|
14
|
+
* {"type":"user","message":{…tool_result…}}
|
|
15
|
+
* {"type":"result","subtype":"success"|"error_max_turns"|…,"is_error":bool,
|
|
16
|
+
* "num_turns":N,"result":"…","total_cost_usd":X,"usage":{…}} ← authoritative
|
|
17
|
+
*
|
|
18
|
+
* Mapping to the host-neutral contract:
|
|
19
|
+
* - output = `result` field of the result event (fallback: last assistant text)
|
|
20
|
+
* - usage = the result event's cumulative usage + num_turns + total_cost_usd
|
|
21
|
+
* (assistant events accumulate per-turn usage for live streaming;
|
|
22
|
+
* the result event overwrites with the authoritative totals)
|
|
23
|
+
* - lastActivity = latest assistant text or a one-line tool summary
|
|
24
|
+
* - failure = result.is_error, a stream-level error, or a non-zero exit
|
|
25
|
+
*
|
|
26
|
+
* Permission mapping (the codex `sandboxForTools` analogue): Claude Code has no
|
|
27
|
+
* OS-level sandbox in -p mode — a tool call is either whitelisted or denied. A
|
|
28
|
+
* phase whose tool whitelist is read-only gets `--allowedTools <read-only set>`
|
|
29
|
+
* (mutating tools are denied outright); anything else gets
|
|
30
|
+
* `--permission-mode bypassPermissions`, which behaves like codex's
|
|
31
|
+
* workspace-write but WITHOUT an OS sandbox backstop — document accordingly.
|
|
32
|
+
*
|
|
33
|
+
* Process handling (idle watchdog, abort, signal-kill detection, stderr cap,
|
|
34
|
+
* error sanitization) mirrors the pi/codex runners so behavior is uniform.
|
|
35
|
+
*/
|
|
36
|
+
import { runSubagentProcess, num, unknownAgentResult, } from "taskflow-core";
|
|
37
|
+
import { emptyUsage } from "taskflow-core";
|
|
38
|
+
/** The Claude tools a read-only phase may use. `Bash` is excluded — Claude has
|
|
39
|
+
* no read-only shell (unlike codex's read-only OS sandbox, which still allows
|
|
40
|
+
* read-only commands), so a listed-tools phase without write/edit/bash gets
|
|
41
|
+
* file reads + search + web only. */
|
|
42
|
+
const READ_ONLY_TOOLS = ["Read", "Grep", "Glob", "WebFetch", "WebSearch"];
|
|
43
|
+
export function newClaudeAccumulator(model) {
|
|
44
|
+
return { usage: emptyUsage(), model, finalText: "", lastActivity: "", sawResult: false };
|
|
45
|
+
}
|
|
46
|
+
/** Summarize a claude tool_use for the live activity stream — parity with the
|
|
47
|
+
* pi runner's summarizeToolCall and codex's shortCmd (which cover all tools,
|
|
48
|
+
* not just Bash). Without this a Write/Edit/Read shows as just the bare tool
|
|
49
|
+
* name, a UX regression vs the other hosts. */
|
|
50
|
+
function shortTool(name, input) {
|
|
51
|
+
const n = String(name ?? "tool");
|
|
52
|
+
if (!input || typeof input !== "object")
|
|
53
|
+
return n;
|
|
54
|
+
const obj = input;
|
|
55
|
+
const trim = (v) => String(v ?? "").replace(/\s+/g, " ").trim();
|
|
56
|
+
const brief = (v) => {
|
|
57
|
+
const s = trim(v);
|
|
58
|
+
return s.length > 64 ? `${s.slice(0, 64)}…` : s;
|
|
59
|
+
};
|
|
60
|
+
switch (n) {
|
|
61
|
+
case "Bash":
|
|
62
|
+
return `$ ${brief(obj.command)}`;
|
|
63
|
+
case "Read":
|
|
64
|
+
case "Write":
|
|
65
|
+
case "Edit":
|
|
66
|
+
case "NotebookEdit":
|
|
67
|
+
return `${n}: ${brief(obj.file_path ?? obj.path)}`;
|
|
68
|
+
case "Grep":
|
|
69
|
+
case "Glob":
|
|
70
|
+
return `${n}: ${brief(obj.pattern)}`;
|
|
71
|
+
default:
|
|
72
|
+
return n;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Fold one claude stream-json line into the accumulator. Returns a LiveUpdate
|
|
77
|
+
* when the stream produced new activity (for streaming), else null. Empty or
|
|
78
|
+
* malformed lines are ignored — robust to partial buffers and noise,
|
|
79
|
+
* unit-testable without spawning a process.
|
|
80
|
+
*/
|
|
81
|
+
export function foldClaudeEventLine(acc, line) {
|
|
82
|
+
if (!line.trim())
|
|
83
|
+
return null;
|
|
84
|
+
let event;
|
|
85
|
+
try {
|
|
86
|
+
event = JSON.parse(line);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
let activity = "";
|
|
92
|
+
if (event.type === "system" && event.subtype === "init") {
|
|
93
|
+
if (typeof event.model === "string" && event.model)
|
|
94
|
+
acc.model = event.model;
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
else if (event.type === "assistant" && event.message) {
|
|
98
|
+
const msg = event.message;
|
|
99
|
+
// Claude injects synthetic assistant messages (model "<synthetic>") for
|
|
100
|
+
// harness-level errors (auth failures, aborts) — their text is diagnostic,
|
|
101
|
+
// not an answer. Surface it as activity but never as the final answer.
|
|
102
|
+
const synthetic = msg.model === "<synthetic>";
|
|
103
|
+
if (!synthetic && typeof msg.model === "string" && msg.model)
|
|
104
|
+
acc.model = msg.model;
|
|
105
|
+
for (const part of Array.isArray(msg.content) ? msg.content : []) {
|
|
106
|
+
if (part?.type === "text" && typeof part.text === "string" && part.text.trim()) {
|
|
107
|
+
if (!synthetic)
|
|
108
|
+
acc.finalText = part.text;
|
|
109
|
+
activity = part.text.trim();
|
|
110
|
+
}
|
|
111
|
+
else if (part?.type === "tool_use") {
|
|
112
|
+
activity = shortTool(part.name, part.input);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (typeof event.error === "string" && event.error) {
|
|
116
|
+
acc.fatalError = activity || event.error;
|
|
117
|
+
}
|
|
118
|
+
// Per-turn usage for live progress; the result event overwrites with the
|
|
119
|
+
// run's authoritative totals (never double-counted).
|
|
120
|
+
const u = msg.usage;
|
|
121
|
+
if (!acc.sawResult && u && !synthetic) {
|
|
122
|
+
acc.usage.turns++;
|
|
123
|
+
acc.usage.input += num(u.input_tokens);
|
|
124
|
+
acc.usage.output += num(u.output_tokens);
|
|
125
|
+
acc.usage.cacheRead += num(u.cache_read_input_tokens);
|
|
126
|
+
acc.usage.cacheWrite += num(u.cache_creation_input_tokens);
|
|
127
|
+
// contextTokens is a host-specific gauge (NOT additive): claude reports input_tokens
|
|
128
|
+
// EXCLUDING cache read, so input+cache_read+output = full last-turn context.
|
|
129
|
+
acc.usage.contextTokens =
|
|
130
|
+
num(u.input_tokens) + num(u.cache_read_input_tokens) + num(u.output_tokens);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else if (event.type === "result") {
|
|
134
|
+
acc.sawResult = true;
|
|
135
|
+
const u = event.usage;
|
|
136
|
+
if (u) {
|
|
137
|
+
const prev = acc.usage.contextTokens;
|
|
138
|
+
acc.usage = emptyUsage();
|
|
139
|
+
acc.usage.input = num(u.input_tokens);
|
|
140
|
+
acc.usage.output = num(u.output_tokens);
|
|
141
|
+
acc.usage.cacheRead = num(u.cache_read_input_tokens);
|
|
142
|
+
acc.usage.cacheWrite = num(u.cache_creation_input_tokens);
|
|
143
|
+
acc.usage.contextTokens = prev;
|
|
144
|
+
}
|
|
145
|
+
acc.usage.turns = typeof event.num_turns === "number" ? event.num_turns : acc.usage.turns;
|
|
146
|
+
acc.usage.cost = typeof event.total_cost_usd === "number" ? event.total_cost_usd : 0;
|
|
147
|
+
if (event.is_error) {
|
|
148
|
+
acc.fatalError =
|
|
149
|
+
(typeof event.result === "string" && event.result.trim()) ||
|
|
150
|
+
`claude run failed (${event.subtype ?? "unknown"})`;
|
|
151
|
+
activity = `error: ${acc.fatalError}`;
|
|
152
|
+
}
|
|
153
|
+
else if (typeof event.result === "string" && event.result.trim()) {
|
|
154
|
+
acc.finalText = event.result;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
return null; // user (tool results) / stream_event — nothing to fold.
|
|
159
|
+
}
|
|
160
|
+
if (activity)
|
|
161
|
+
acc.lastActivity = activity.replace(/\s+/g, " ").trim();
|
|
162
|
+
return { text: acc.lastActivity, usage: { ...acc.usage }, model: acc.model };
|
|
163
|
+
}
|
|
164
|
+
/** Override the claude binary (tests / unusual installs). */
|
|
165
|
+
export function claudeBin() {
|
|
166
|
+
return process.env.PI_TASKFLOW_CLAUDE_BIN || "claude";
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Map a phase's tool whitelist to claude permission flags — the codex
|
|
170
|
+
* `sandboxForTools` analogue. A phase that only reads (no write/edit/bash in
|
|
171
|
+
* its whitelist) gets a read-only `--allowedTools` set; anything else gets
|
|
172
|
+
* `--permission-mode bypassPermissions` (non-interactive -p runs cannot answer
|
|
173
|
+
* permission prompts, so the codex workspace-write equivalent is bypass —
|
|
174
|
+
* WITHOUT an OS sandbox backstop). No whitelist → bypass (the engine's
|
|
175
|
+
* default-capable agent).
|
|
176
|
+
*/
|
|
177
|
+
export function permissionArgsForTools(tools) {
|
|
178
|
+
if (!tools || tools.length === 0)
|
|
179
|
+
return ["--permission-mode", "bypassPermissions"];
|
|
180
|
+
const mutating = new Set(["write", "edit", "bash", "apply_patch"]);
|
|
181
|
+
const canMutate = tools.some((t) => mutating.has(t));
|
|
182
|
+
if (canMutate)
|
|
183
|
+
return ["--permission-mode", "bypassPermissions"];
|
|
184
|
+
return ["--allowedTools", READ_ONLY_TOOLS.join(",")];
|
|
185
|
+
}
|
|
186
|
+
/** Resolve a modelRoles/pi model id for `claude --model`, or `undefined` to let
|
|
187
|
+
* claude fall back to its own default. Claude model ids are FLAT (aliases like
|
|
188
|
+
* "sonnet"/"haiku" or full ids like "claude-sonnet-4-6"), so — same rule as
|
|
189
|
+
* codex — a pi-provider path (contains "/") or an unresolved {{placeholder}}
|
|
190
|
+
* is dropped. Exported for unit-testing the contract. */
|
|
191
|
+
export function resolveClaudeModel(model) {
|
|
192
|
+
if (!model)
|
|
193
|
+
return undefined;
|
|
194
|
+
if (model.includes("/"))
|
|
195
|
+
return undefined;
|
|
196
|
+
if (/^\{\{.*\}\}$/.test(model))
|
|
197
|
+
return undefined;
|
|
198
|
+
return model;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Build the full `claude -p` argv from a phase's resolved context — PURE
|
|
202
|
+
* (no process.env, no spawn). Extracted from `runClaudeAgentTask` so the host's
|
|
203
|
+
* CLI flag contract is unit-testable in CI without a live claude session.
|
|
204
|
+
*
|
|
205
|
+
* claude -p --output-format stream-json --verbose --strict-mcp-config
|
|
206
|
+
* [--permission-mode bypassPermissions | --allowedTools ...]
|
|
207
|
+
* [--model m] [--append-system-prompt ...] <task>
|
|
208
|
+
*/
|
|
209
|
+
export function buildClaudeArgs(ctx) {
|
|
210
|
+
const claudeModel = resolveClaudeModel(ctx.model);
|
|
211
|
+
const args = ["-p", "--output-format", "stream-json", "--verbose", "--strict-mcp-config"];
|
|
212
|
+
args.push(...permissionArgsForTools(ctx.tools));
|
|
213
|
+
if (claudeModel)
|
|
214
|
+
args.push("--model", claudeModel);
|
|
215
|
+
if (ctx.systemPrompt.trim())
|
|
216
|
+
args.push("--append-system-prompt", ctx.systemPrompt.trim());
|
|
217
|
+
args.push(ctx.task);
|
|
218
|
+
return args;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Run a single subagent task via `claude -p --output-format stream-json`.
|
|
222
|
+
* Resolves the agent from `agents` by name; returns the same structured
|
|
223
|
+
* `RunResult` the pi and codex runners do.
|
|
224
|
+
*/
|
|
225
|
+
export async function runClaudeAgentTask(defaultCwd, agents, agentName, task, opts, globalThinking) {
|
|
226
|
+
const agent = agents.find((a) => a.name === agentName);
|
|
227
|
+
if (!agent)
|
|
228
|
+
return unknownAgentResult(agentName, task, agents);
|
|
229
|
+
const model = opts.model ?? agent.model;
|
|
230
|
+
const tools = opts.tools ?? agent.tools;
|
|
231
|
+
void globalThinking; // claude -p has no thinking-level flag; reserved.
|
|
232
|
+
const args = buildClaudeArgs({
|
|
233
|
+
systemPrompt: agent.systemPrompt,
|
|
234
|
+
task,
|
|
235
|
+
model,
|
|
236
|
+
tools,
|
|
237
|
+
});
|
|
238
|
+
return runSubagentProcess({
|
|
239
|
+
agent: agentName,
|
|
240
|
+
task,
|
|
241
|
+
model,
|
|
242
|
+
bin: claudeBin(),
|
|
243
|
+
args,
|
|
244
|
+
cwd: opts.cwd ?? defaultCwd,
|
|
245
|
+
idleTimeoutMs: opts.idleTimeoutMs,
|
|
246
|
+
signal: opts.signal,
|
|
247
|
+
onLive: opts.onLive,
|
|
248
|
+
acc: newClaudeAccumulator(model),
|
|
249
|
+
foldLine: foldClaudeEventLine,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* The Claude host's `SubagentRunner`. Drops into `RuntimeDeps.runTask` exactly
|
|
254
|
+
* like the pi/codex/opencode runners, so the engine runs unchanged on Claude Code.
|
|
255
|
+
*/
|
|
256
|
+
export const claudeSubagentRunner = {
|
|
257
|
+
runTask: runClaudeAgentTask,
|
|
258
|
+
};
|
|
259
|
+
//# sourceMappingURL=claude-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-runner.js","sourceRoot":"","sources":["../src/claude-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex subagent runner — the Codex host's `SubagentRunner` implementation.
|
|
3
|
+
*
|
|
4
|
+
* Spawns an isolated `codex exec --json` process per task and folds its JSONL
|
|
5
|
+
* event stream into the same host-neutral `RunResult` the pi runner produces,
|
|
6
|
+
* so the engine (runtime.ts) treats a Codex subagent identically to a pi one.
|
|
7
|
+
*
|
|
8
|
+
* Real codex JSONL schema (codex-cli ≥ 0.142), observed empirically:
|
|
9
|
+
* {"type":"thread.started","thread_id":"…"}
|
|
10
|
+
* {"type":"turn.started"}
|
|
11
|
+
* {"type":"item.started", "item":{"id","type":"command_execution","command","status":"in_progress",…}}
|
|
12
|
+
* {"type":"item.completed","item":{"id","type":"command_execution","command","exit_code","status":"completed",…}}
|
|
13
|
+
* {"type":"item.completed","item":{"id","type":"agent_message","text":"…"}} ← final answer
|
|
14
|
+
* {"type":"item.completed","item":{"id","type":"error","message":"…"}} ← warning OR fatal
|
|
15
|
+
* {"type":"turn.completed","usage":{"input_tokens","cached_input_tokens","output_tokens","reasoning_output_tokens"}}
|
|
16
|
+
*
|
|
17
|
+
* Mapping to the pi contract:
|
|
18
|
+
* - output = text of the LAST `agent_message` item
|
|
19
|
+
* - usage.input = input_tokens; usage.output = output_tokens + reasoning_output_tokens;
|
|
20
|
+
* usage.cacheRead = cached_input_tokens; turns = number of `turn.completed`
|
|
21
|
+
* - lastActivity for streaming = latest agent_message text or a one-line
|
|
22
|
+
* command summary
|
|
23
|
+
* - benign warnings (the "Under-development features" / "Skill descriptions"
|
|
24
|
+
* notices) are NOT treated as failures — only a fatal error item or a
|
|
25
|
+
* non-zero exit is.
|
|
26
|
+
*
|
|
27
|
+
* Process handling (idle watchdog, abort, signal-kill detection, stderr cap,
|
|
28
|
+
* error sanitization) is delegated to the shared `runSubagentProcess` in
|
|
29
|
+
* taskflow-core, so behavior is uniform across hosts.
|
|
30
|
+
*/
|
|
31
|
+
import { type AgentConfig, type LiveUpdate, type RunOptions, type RunResult, type SubagentRunner, type UsageStats } from "taskflow-core";
|
|
32
|
+
/** Accumulated state folded from a codex JSONL event stream. */
|
|
33
|
+
export interface CodexAccumulator {
|
|
34
|
+
usage: UsageStats;
|
|
35
|
+
model?: string;
|
|
36
|
+
/** Final answer = text of the last agent_message item seen. */
|
|
37
|
+
finalText: string;
|
|
38
|
+
lastActivity: string;
|
|
39
|
+
/** Set when a fatal (non-benign) error item is seen. */
|
|
40
|
+
fatalError?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function newCodexAccumulator(model?: string): CodexAccumulator;
|
|
43
|
+
/**
|
|
44
|
+
* Fold one codex JSONL line into the accumulator. Returns a LiveUpdate when the
|
|
45
|
+
* stream produced new activity (for streaming), else null. Empty/malformed
|
|
46
|
+
* lines are ignored — robust to partial buffers and noise, unit-testable
|
|
47
|
+
* without spawning a process.
|
|
48
|
+
*/
|
|
49
|
+
export declare function foldCodexEventLine(acc: CodexAccumulator, line: string): LiveUpdate | null;
|
|
50
|
+
/** Override the codex binary (tests / unusual installs). */
|
|
51
|
+
export declare function codexBin(): string;
|
|
52
|
+
/**
|
|
53
|
+
* Map a phase's tool whitelist to a codex sandbox mode. Codex has no per-tool
|
|
54
|
+
* whitelist; it has sandbox policies. The conservative mapping: a phase that
|
|
55
|
+
* only reads (no write/edit/bash in its whitelist) gets `read-only`; anything
|
|
56
|
+
* else gets `workspace-write`. No whitelist → workspace-write (the engine's
|
|
57
|
+
* default-capable agent). `danger-full-access` is never selected automatically.
|
|
58
|
+
*/
|
|
59
|
+
export declare function sandboxForTools(tools: string[] | undefined): "read-only" | "workspace-write";
|
|
60
|
+
/** Resolve a modelRoles/pi model id for `codex exec -m`, or `undefined` to let
|
|
61
|
+
* codex fall back to its own default. Codex model ids are FLAT (e.g.
|
|
62
|
+
* "gpt-5.5"), so a pi-provider path (contains "/") or an unresolved role
|
|
63
|
+
* placeholder ({{...}}) is dropped. Exported so the model-id contract is
|
|
64
|
+
* unit-testable without a live codex session. */
|
|
65
|
+
export declare function resolveCodexModel(model: string | undefined): string | undefined;
|
|
66
|
+
/** Context for {@link buildCodexArgs} — the pure inputs to argv construction. */
|
|
67
|
+
export interface CodexArgsCtx {
|
|
68
|
+
systemPrompt: string;
|
|
69
|
+
task: string;
|
|
70
|
+
/** Already-resolved model (opts.model ?? agent.model). */
|
|
71
|
+
model?: string;
|
|
72
|
+
tools?: string[];
|
|
73
|
+
cwd?: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Build the full `codex exec` argv from a phase's resolved context — PURE
|
|
77
|
+
* (no process.env, no spawn). Extracted from `runCodexAgentTask` so the host's
|
|
78
|
+
* CLI flag contract is unit-testable in CI without a live codex session.
|
|
79
|
+
*
|
|
80
|
+
* codex exec --json --skip-git-repo-check -s <sandbox> [-m model] [-C cwd] <prompt>
|
|
81
|
+
*/
|
|
82
|
+
export declare function buildCodexArgs(ctx: CodexArgsCtx): string[];
|
|
83
|
+
/**
|
|
84
|
+
* Run a single subagent task via `codex exec --json`. Resolves the agent from
|
|
85
|
+
* `agents` by name; returns the same structured `RunResult` the pi runner does.
|
|
86
|
+
*/
|
|
87
|
+
export declare function runCodexAgentTask(defaultCwd: string, agents: AgentConfig[], agentName: string, task: string, opts: RunOptions, globalThinking?: string): Promise<RunResult>;
|
|
88
|
+
/**
|
|
89
|
+
* The Codex host's `SubagentRunner`. Drops into `RuntimeDeps.runTask` exactly
|
|
90
|
+
* like the pi/codex/claude/opencode runners, so the engine runs unchanged on Codex.
|
|
91
|
+
*/
|
|
92
|
+
export declare const codexSubagentRunner: SubagentRunner<AgentConfig>;
|
|
93
|
+
//# sourceMappingURL=codex-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-runner.d.ts","sourceRoot":"","sources":["../src/codex-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAIN,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,MAAM,eAAe,CAAC;AAcvB,gEAAgE;AAChE,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAEpE;AAOD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAkDzF;AAED,4DAA4D;AAC5D,wBAAgB,QAAQ,IAAI,MAAM,CAEjC;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,WAAW,GAAG,iBAAiB,CAK5F;AAED;;;;kDAIkD;AAClD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAK/E;AAED,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,EAAE,CAW1D;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACtC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,EAAE,EACrB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,cAAc,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,SAAS,CAAC,CA8BpB;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,cAAc,CAAC,WAAW,CAE3D,CAAC"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex subagent runner — the Codex host's `SubagentRunner` implementation.
|
|
3
|
+
*
|
|
4
|
+
* Spawns an isolated `codex exec --json` process per task and folds its JSONL
|
|
5
|
+
* event stream into the same host-neutral `RunResult` the pi runner produces,
|
|
6
|
+
* so the engine (runtime.ts) treats a Codex subagent identically to a pi one.
|
|
7
|
+
*
|
|
8
|
+
* Real codex JSONL schema (codex-cli ≥ 0.142), observed empirically:
|
|
9
|
+
* {"type":"thread.started","thread_id":"…"}
|
|
10
|
+
* {"type":"turn.started"}
|
|
11
|
+
* {"type":"item.started", "item":{"id","type":"command_execution","command","status":"in_progress",…}}
|
|
12
|
+
* {"type":"item.completed","item":{"id","type":"command_execution","command","exit_code","status":"completed",…}}
|
|
13
|
+
* {"type":"item.completed","item":{"id","type":"agent_message","text":"…"}} ← final answer
|
|
14
|
+
* {"type":"item.completed","item":{"id","type":"error","message":"…"}} ← warning OR fatal
|
|
15
|
+
* {"type":"turn.completed","usage":{"input_tokens","cached_input_tokens","output_tokens","reasoning_output_tokens"}}
|
|
16
|
+
*
|
|
17
|
+
* Mapping to the pi contract:
|
|
18
|
+
* - output = text of the LAST `agent_message` item
|
|
19
|
+
* - usage.input = input_tokens; usage.output = output_tokens + reasoning_output_tokens;
|
|
20
|
+
* usage.cacheRead = cached_input_tokens; turns = number of `turn.completed`
|
|
21
|
+
* - lastActivity for streaming = latest agent_message text or a one-line
|
|
22
|
+
* command summary
|
|
23
|
+
* - benign warnings (the "Under-development features" / "Skill descriptions"
|
|
24
|
+
* notices) are NOT treated as failures — only a fatal error item or a
|
|
25
|
+
* non-zero exit is.
|
|
26
|
+
*
|
|
27
|
+
* Process handling (idle watchdog, abort, signal-kill detection, stderr cap,
|
|
28
|
+
* error sanitization) is delegated to the shared `runSubagentProcess` in
|
|
29
|
+
* taskflow-core, so behavior is uniform across hosts.
|
|
30
|
+
*/
|
|
31
|
+
import { runSubagentProcess, num, unknownAgentResult, } from "taskflow-core";
|
|
32
|
+
import { emptyUsage } from "taskflow-core";
|
|
33
|
+
/** Benign codex `error` items that are warnings, not failures. Matched as a
|
|
34
|
+
* prefix/substring so version drift in the message tail still classifies. */
|
|
35
|
+
const BENIGN_ERROR_MARKERS = [
|
|
36
|
+
"Under-development features",
|
|
37
|
+
"Skill descriptions were shortened",
|
|
38
|
+
];
|
|
39
|
+
function isBenignCodexError(message) {
|
|
40
|
+
return BENIGN_ERROR_MARKERS.some((m) => message.includes(m));
|
|
41
|
+
}
|
|
42
|
+
export function newCodexAccumulator(model) {
|
|
43
|
+
return { usage: emptyUsage(), model, finalText: "", lastActivity: "" };
|
|
44
|
+
}
|
|
45
|
+
function shortCmd(cmd) {
|
|
46
|
+
const s = String(cmd ?? "").replace(/\s+/g, " ").trim();
|
|
47
|
+
return s.length > 64 ? `${s.slice(0, 64)}…` : s;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Fold one codex JSONL line into the accumulator. Returns a LiveUpdate when the
|
|
51
|
+
* stream produced new activity (for streaming), else null. Empty/malformed
|
|
52
|
+
* lines are ignored — robust to partial buffers and noise, unit-testable
|
|
53
|
+
* without spawning a process.
|
|
54
|
+
*/
|
|
55
|
+
export function foldCodexEventLine(acc, line) {
|
|
56
|
+
if (!line.trim())
|
|
57
|
+
return null;
|
|
58
|
+
let event;
|
|
59
|
+
try {
|
|
60
|
+
event = JSON.parse(line);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
let activity = "";
|
|
66
|
+
if (event.type === "turn.completed" && event.usage) {
|
|
67
|
+
const u = event.usage;
|
|
68
|
+
acc.usage.turns++;
|
|
69
|
+
acc.usage.input += num(u.input_tokens);
|
|
70
|
+
acc.usage.output += num(u.output_tokens) + num(u.reasoning_output_tokens);
|
|
71
|
+
acc.usage.cacheRead += num(u.cached_input_tokens);
|
|
72
|
+
// contextTokens is a host-specific point-in-time gauge (NOT additive — excluded from aggregateUsage):
|
|
73
|
+
// each host's formula differs because each accounts for cache differently. Codex's input_tokens
|
|
74
|
+
// already includes cached tokens, so input+output = full last-turn context.
|
|
75
|
+
acc.usage.contextTokens = num(u.input_tokens) + num(u.output_tokens);
|
|
76
|
+
}
|
|
77
|
+
else if (event.type === "item.completed" || event.type === "item.started") {
|
|
78
|
+
const item = event.item;
|
|
79
|
+
if (!item)
|
|
80
|
+
return null;
|
|
81
|
+
switch (item.type) {
|
|
82
|
+
case "agent_message":
|
|
83
|
+
if (typeof item.text === "string" && item.text.trim()) {
|
|
84
|
+
// Final answer is the LAST agent_message; keep overwriting.
|
|
85
|
+
if (event.type === "item.completed")
|
|
86
|
+
acc.finalText = item.text;
|
|
87
|
+
activity = item.text.trim();
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
case "command_execution":
|
|
91
|
+
activity = `$ ${shortCmd(item.command)}`;
|
|
92
|
+
break;
|
|
93
|
+
case "error":
|
|
94
|
+
if (typeof item.message === "string" && !isBenignCodexError(item.message)) {
|
|
95
|
+
acc.fatalError = item.message;
|
|
96
|
+
activity = `error: ${item.message}`;
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
// file_change / reasoning / mcp_tool_call / etc. — note generically.
|
|
101
|
+
if (typeof item.type === "string")
|
|
102
|
+
activity = item.type;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
return null; // thread.started / turn.started — nothing to fold.
|
|
107
|
+
}
|
|
108
|
+
if (activity)
|
|
109
|
+
acc.lastActivity = activity.replace(/\s+/g, " ").trim();
|
|
110
|
+
return { text: acc.lastActivity, usage: { ...acc.usage }, model: acc.model };
|
|
111
|
+
}
|
|
112
|
+
/** Override the codex binary (tests / unusual installs). */
|
|
113
|
+
export function codexBin() {
|
|
114
|
+
return process.env.PI_TASKFLOW_CODEX_BIN || "codex";
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Map a phase's tool whitelist to a codex sandbox mode. Codex has no per-tool
|
|
118
|
+
* whitelist; it has sandbox policies. The conservative mapping: a phase that
|
|
119
|
+
* only reads (no write/edit/bash in its whitelist) gets `read-only`; anything
|
|
120
|
+
* else gets `workspace-write`. No whitelist → workspace-write (the engine's
|
|
121
|
+
* default-capable agent). `danger-full-access` is never selected automatically.
|
|
122
|
+
*/
|
|
123
|
+
export function sandboxForTools(tools) {
|
|
124
|
+
if (!tools || tools.length === 0)
|
|
125
|
+
return "workspace-write";
|
|
126
|
+
const mutating = new Set(["write", "edit", "bash", "apply_patch"]);
|
|
127
|
+
const canMutate = tools.some((t) => mutating.has(t));
|
|
128
|
+
return canMutate ? "workspace-write" : "read-only";
|
|
129
|
+
}
|
|
130
|
+
/** Resolve a modelRoles/pi model id for `codex exec -m`, or `undefined` to let
|
|
131
|
+
* codex fall back to its own default. Codex model ids are FLAT (e.g.
|
|
132
|
+
* "gpt-5.5"), so a pi-provider path (contains "/") or an unresolved role
|
|
133
|
+
* placeholder ({{...}}) is dropped. Exported so the model-id contract is
|
|
134
|
+
* unit-testable without a live codex session. */
|
|
135
|
+
export function resolveCodexModel(model) {
|
|
136
|
+
if (!model)
|
|
137
|
+
return undefined;
|
|
138
|
+
if (model.includes("/"))
|
|
139
|
+
return undefined;
|
|
140
|
+
if (/^\{\{.*\}\}$/.test(model))
|
|
141
|
+
return undefined;
|
|
142
|
+
return model;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Build the full `codex exec` argv from a phase's resolved context — PURE
|
|
146
|
+
* (no process.env, no spawn). Extracted from `runCodexAgentTask` so the host's
|
|
147
|
+
* CLI flag contract is unit-testable in CI without a live codex session.
|
|
148
|
+
*
|
|
149
|
+
* codex exec --json --skip-git-repo-check -s <sandbox> [-m model] [-C cwd] <prompt>
|
|
150
|
+
*/
|
|
151
|
+
export function buildCodexArgs(ctx) {
|
|
152
|
+
const sandbox = sandboxForTools(ctx.tools);
|
|
153
|
+
const codexModel = resolveCodexModel(ctx.model);
|
|
154
|
+
const fullPrompt = ctx.systemPrompt.trim()
|
|
155
|
+
? `${ctx.systemPrompt.trim()}\n\n---\n\nTask: ${ctx.task}`
|
|
156
|
+
: `Task: ${ctx.task}`;
|
|
157
|
+
const args = ["exec", "--json", "--skip-git-repo-check", "-s", sandbox];
|
|
158
|
+
if (codexModel)
|
|
159
|
+
args.push("-m", codexModel);
|
|
160
|
+
if (ctx.cwd)
|
|
161
|
+
args.push("-C", ctx.cwd);
|
|
162
|
+
args.push(fullPrompt);
|
|
163
|
+
return args;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Run a single subagent task via `codex exec --json`. Resolves the agent from
|
|
167
|
+
* `agents` by name; returns the same structured `RunResult` the pi runner does.
|
|
168
|
+
*/
|
|
169
|
+
export async function runCodexAgentTask(defaultCwd, agents, agentName, task, opts, globalThinking) {
|
|
170
|
+
const agent = agents.find((a) => a.name === agentName);
|
|
171
|
+
if (!agent)
|
|
172
|
+
return unknownAgentResult(agentName, task, agents);
|
|
173
|
+
const model = opts.model ?? agent.model;
|
|
174
|
+
const tools = opts.tools ?? agent.tools;
|
|
175
|
+
void globalThinking; // codex has no thinking-level flag on exec; reserved.
|
|
176
|
+
const cwd = opts.cwd ?? defaultCwd;
|
|
177
|
+
const args = buildCodexArgs({
|
|
178
|
+
systemPrompt: agent.systemPrompt,
|
|
179
|
+
task,
|
|
180
|
+
model,
|
|
181
|
+
tools,
|
|
182
|
+
cwd,
|
|
183
|
+
});
|
|
184
|
+
return runSubagentProcess({
|
|
185
|
+
agent: agentName,
|
|
186
|
+
task,
|
|
187
|
+
model,
|
|
188
|
+
bin: codexBin(),
|
|
189
|
+
args,
|
|
190
|
+
cwd,
|
|
191
|
+
idleTimeoutMs: opts.idleTimeoutMs,
|
|
192
|
+
signal: opts.signal,
|
|
193
|
+
onLive: opts.onLive,
|
|
194
|
+
acc: newCodexAccumulator(model),
|
|
195
|
+
foldLine: foldCodexEventLine,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* The Codex host's `SubagentRunner`. Drops into `RuntimeDeps.runTask` exactly
|
|
200
|
+
* like the pi/codex/claude/opencode runners, so the engine runs unchanged on Codex.
|
|
201
|
+
*/
|
|
202
|
+
export const codexSubagentRunner = {
|
|
203
|
+
runTask: runCodexAgentTask,
|
|
204
|
+
};
|
|
205
|
+
//# sourceMappingURL=codex-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-runner.js","sourceRoot":"","sources":["../src/codex-runner.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* taskflow-hosts — the shared collection of host-runner implementations.
|
|
3
|
+
*
|
|
4
|
+
* Each non-pi host (codex / claude / opencode) has two halves:
|
|
5
|
+
* 1. the **runner** — a `SubagentRunner` that spawns that host's CLI, folds
|
|
6
|
+
* its event stream, and classifies the outcome into a host-neutral
|
|
7
|
+
* `RunResult`. PLUS the pure argv builders (`buildXxxArgs`) and
|
|
8
|
+
* permission/model helpers.
|
|
9
|
+
* 2. the **delivery** — the per-host MCP server + bin + plugin scaffold,
|
|
10
|
+
* which lives in that host's own package (codex-taskflow /
|
|
11
|
+
* claude-taskflow / opencode-taskflow) because it's the host-ecosystem
|
|
12
|
+
* install target (`codex plugin add`, `claude plugin install`, …).
|
|
13
|
+
*
|
|
14
|
+
* This package holds only #1 — the runners — so all host runners live in one
|
|
15
|
+
* place. A new host adds a `<host>-runner.ts` here; its delivery package
|
|
16
|
+
* imports the runner from `taskflow-hosts`. The three legacy host packages
|
|
17
|
+
* re-export from here (or import the runner directly into their `mcp/server`).
|
|
18
|
+
*
|
|
19
|
+
* The barrel re-exports the full public surface of each runner so a consumer
|
|
20
|
+
* can `import { codexSubagentRunner, buildCodexArgs } from "taskflow-hosts"`,
|
|
21
|
+
* or use a sub-path (`taskflow-hosts/codex`) to tree-shake to one host.
|
|
22
|
+
*/
|
|
23
|
+
export * from "./codex-runner.ts";
|
|
24
|
+
export * from "./claude-runner.ts";
|
|
25
|
+
export * from "./opencode-runner.ts";
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* taskflow-hosts — the shared collection of host-runner implementations.
|
|
3
|
+
*
|
|
4
|
+
* Each non-pi host (codex / claude / opencode) has two halves:
|
|
5
|
+
* 1. the **runner** — a `SubagentRunner` that spawns that host's CLI, folds
|
|
6
|
+
* its event stream, and classifies the outcome into a host-neutral
|
|
7
|
+
* `RunResult`. PLUS the pure argv builders (`buildXxxArgs`) and
|
|
8
|
+
* permission/model helpers.
|
|
9
|
+
* 2. the **delivery** — the per-host MCP server + bin + plugin scaffold,
|
|
10
|
+
* which lives in that host's own package (codex-taskflow /
|
|
11
|
+
* claude-taskflow / opencode-taskflow) because it's the host-ecosystem
|
|
12
|
+
* install target (`codex plugin add`, `claude plugin install`, …).
|
|
13
|
+
*
|
|
14
|
+
* This package holds only #1 — the runners — so all host runners live in one
|
|
15
|
+
* place. A new host adds a `<host>-runner.ts` here; its delivery package
|
|
16
|
+
* imports the runner from `taskflow-hosts`. The three legacy host packages
|
|
17
|
+
* re-export from here (or import the runner directly into their `mcp/server`).
|
|
18
|
+
*
|
|
19
|
+
* The barrel re-exports the full public surface of each runner so a consumer
|
|
20
|
+
* can `import { codexSubagentRunner, buildCodexArgs } from "taskflow-hosts"`,
|
|
21
|
+
* or use a sub-path (`taskflow-hosts/codex`) to tree-shake to one host.
|
|
22
|
+
*/
|
|
23
|
+
export * from "./codex-runner.js";
|
|
24
|
+
export * from "./claude-runner.js";
|
|
25
|
+
export * from "./opencode-runner.js";
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode subagent runner — the OpenCode host's `SubagentRunner` implementation.
|
|
3
|
+
*
|
|
4
|
+
* Spawns an isolated `opencode run --format json` process per task and folds its
|
|
5
|
+
* JSON event stream into the same host-neutral `RunResult` the pi, codex, and
|
|
6
|
+
* claude runners produce, so the engine (runtime.ts) treats an OpenCode subagent
|
|
7
|
+
* identically.
|
|
8
|
+
*
|
|
9
|
+
* Real opencode `run --format json` schema (opencode ≥ 1.17), observed
|
|
10
|
+
* empirically — one JSON object per line:
|
|
11
|
+
* {"type":"step_start", "part":{"type":"step-start",…}}
|
|
12
|
+
* {"type":"text", "part":{"type":"text","text":"…"}} ← assistant text
|
|
13
|
+
* {"type":"tool_use", "part":{"type":"tool","tool":"bash",
|
|
14
|
+
* "state":{"status":"completed","input":{"command":"…"},"title":"…"}}}
|
|
15
|
+
* {"type":"step_finish","part":{"type":"step-finish","reason":"stop"|"tool-calls",
|
|
16
|
+
* "tokens":{"total","input","output","reasoning","cache":{"read","write"}},"cost":0}}
|
|
17
|
+
* {"type":"error", "error":{"name":"…","data":{"message":"…"}}} ← fatal
|
|
18
|
+
*
|
|
19
|
+
* Mapping to the host-neutral contract:
|
|
20
|
+
* - output = accumulated `text` parts of the LAST step (text before a
|
|
21
|
+
* tool call is intermediate reasoning → reset on tool_use)
|
|
22
|
+
* - usage.input = Σ step tokens.input; usage.output = Σ (output + reasoning);
|
|
23
|
+
* cacheRead/cacheWrite from tokens.cache; cost = Σ part.cost;
|
|
24
|
+
* turns = number of step_finish events; contextTokens = latest tokens.total
|
|
25
|
+
* - failure = an `error` event, or a non-zero exit
|
|
26
|
+
*
|
|
27
|
+
* Permission mapping (the codex `sandboxForTools` analogue): OpenCode has no
|
|
28
|
+
* per-run tool-whitelist flag, but it honours a per-process config injected via
|
|
29
|
+
* the `OPENCODE_CONFIG_CONTENT` env var. A read-only phase (no write/edit/bash
|
|
30
|
+
* in its whitelist) is launched with a permission policy that DENIES
|
|
31
|
+
* bash/write/edit — genuinely enforced (a denied tool call is rejected, not
|
|
32
|
+
* merely un-approved). A mutating phase (or no whitelist) runs with `--auto`
|
|
33
|
+
* (auto-approve every permission), the workspace-write analogue.
|
|
34
|
+
*
|
|
35
|
+
* Process handling (idle watchdog, abort, signal-kill detection, stderr cap,
|
|
36
|
+
* error sanitization) mirrors the other runners so behavior is uniform.
|
|
37
|
+
*/
|
|
38
|
+
import { type AgentConfig, type LiveUpdate, type RunOptions, type RunResult, type SubagentRunner, type UsageStats } from "taskflow-core";
|
|
39
|
+
/** Accumulated state folded from an opencode JSON event stream. */
|
|
40
|
+
export interface OpencodeAccumulator {
|
|
41
|
+
usage: UsageStats;
|
|
42
|
+
model?: string;
|
|
43
|
+
/** Final answer = text parts of the last step (reset when a tool runs). */
|
|
44
|
+
finalText: string;
|
|
45
|
+
lastActivity: string;
|
|
46
|
+
/** Set when the stream reported a fatal `error` event. */
|
|
47
|
+
fatalError?: string;
|
|
48
|
+
}
|
|
49
|
+
export declare function newOpencodeAccumulator(model?: string): OpencodeAccumulator;
|
|
50
|
+
/**
|
|
51
|
+
* Fold one opencode JSON line into the accumulator. Returns a LiveUpdate when
|
|
52
|
+
* the stream produced new activity (for streaming), else null. Empty/malformed
|
|
53
|
+
* lines are ignored — robust to partial buffers and noise, unit-testable
|
|
54
|
+
* without spawning a process.
|
|
55
|
+
*/
|
|
56
|
+
export declare function foldOpencodeEventLine(acc: OpencodeAccumulator, line: string): LiveUpdate | null;
|
|
57
|
+
/** Override the opencode binary (tests / unusual installs). */
|
|
58
|
+
export declare function opencodeBin(): string;
|
|
59
|
+
/**
|
|
60
|
+
* Decide whether a phase is read-only from its tool whitelist — the codex
|
|
61
|
+
* `sandboxForTools` analogue. No whitelist → not read-only (default-capable).
|
|
62
|
+
*/
|
|
63
|
+
export declare function isReadOnlyPhase(tools: string[] | undefined): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Resolve a taskflow/pi model id to something `opencode run -m` accepts, or
|
|
66
|
+
* `undefined` to let opencode use its configured default.
|
|
67
|
+
*
|
|
68
|
+
* Unlike codex/claude (whose model ids are flat, so "contains `/`" ⇒ a pi
|
|
69
|
+
* provider path to drop), a *valid* opencode model IS `provider/model` (one
|
|
70
|
+
* slash). So we only drop ids that clearly aren't opencode models:
|
|
71
|
+
* - an unresolved role placeholder `{{fast}}`
|
|
72
|
+
* - a pi thinking suffix (`…:xhigh`)
|
|
73
|
+
* - a multi-segment openrouter path (`openrouter/vendor/model`, ≥ 2 slashes)
|
|
74
|
+
* A clean `provider/model` passes straight through.
|
|
75
|
+
*/
|
|
76
|
+
export declare function resolveOpencodeModel(model: string | undefined): string | undefined;
|
|
77
|
+
/** Context for {@link buildOpencodeArgs} — the pure inputs to argv construction. */
|
|
78
|
+
export interface OpencodeArgsCtx {
|
|
79
|
+
systemPrompt: string;
|
|
80
|
+
task: string;
|
|
81
|
+
/** Already-resolved model (opts.model ?? agent.model). */
|
|
82
|
+
model?: string;
|
|
83
|
+
tools?: string[];
|
|
84
|
+
cwd?: string;
|
|
85
|
+
}
|
|
86
|
+
/** Result of {@link buildOpencodeArgs}: the argv plus whether a read-only
|
|
87
|
+
* permission policy should be injected via OPENCODE_CONFIG_CONTENT (the env
|
|
88
|
+
* side-effect is left to the caller, which owns `process.env`). */
|
|
89
|
+
export interface OpencodeArgs {
|
|
90
|
+
args: string[];
|
|
91
|
+
/** True for a read-only phase — the caller injects the deny-mutations config. */
|
|
92
|
+
readOnly: boolean;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Build the full `opencode run` argv from a phase's resolved context — PURE
|
|
96
|
+
* (no process.env, no spawn). Extracted from `runOpencodeAgentTask` so the
|
|
97
|
+
* host's CLI flag contract is unit-testable in CI without a live opencode
|
|
98
|
+
* session. Returns whether the phase is read-only so the caller can inject the
|
|
99
|
+
* matching OPENCODE_CONFIG_CONTENT env without re-deriving it.
|
|
100
|
+
*
|
|
101
|
+
* opencode run <prompt> --format json [--dir cwd] [-m model] [--auto]
|
|
102
|
+
*/
|
|
103
|
+
export declare function buildOpencodeArgs(ctx: OpencodeArgsCtx): OpencodeArgs;
|
|
104
|
+
/**
|
|
105
|
+
* Run a single subagent task via `opencode run --format json`. Resolves the
|
|
106
|
+
* agent from `agents` by name; returns the same structured `RunResult` the pi,
|
|
107
|
+
* codex, and claude runners do.
|
|
108
|
+
*/
|
|
109
|
+
export declare function runOpencodeAgentTask(defaultCwd: string, agents: AgentConfig[], agentName: string, task: string, opts: RunOptions, globalThinking?: string): Promise<RunResult>;
|
|
110
|
+
/**
|
|
111
|
+
* The OpenCode host's `SubagentRunner`. Drops into `RuntimeDeps.runTask` exactly
|
|
112
|
+
* like the pi/codex/claude runners, so the engine runs unchanged on OpenCode.
|
|
113
|
+
*/
|
|
114
|
+
export declare const opencodeSubagentRunner: SubagentRunner<AgentConfig>;
|
|
115
|
+
//# sourceMappingURL=opencode-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode-runner.d.ts","sourceRoot":"","sources":["../src/opencode-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EAIN,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,MAAM,eAAe,CAAC;AAQvB,mEAAmE;AACnE,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,sBAAsB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAE1E;AAcD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAwD/F;AAED,+DAA+D;AAC/D,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,OAAO,CAIpE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CASlF;AAED,oFAAoF;AACpF,MAAM,WAAW,eAAe;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;oEAEoE;AACpE,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,iFAAiF;IACjF,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,eAAe,GAAG,YAAY,CAWpE;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACzC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,WAAW,EAAE,EACrB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,UAAU,EAChB,cAAc,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,SAAS,CAAC,CAiCpB;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,WAAW,CAE9D,CAAC"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode subagent runner — the OpenCode host's `SubagentRunner` implementation.
|
|
3
|
+
*
|
|
4
|
+
* Spawns an isolated `opencode run --format json` process per task and folds its
|
|
5
|
+
* JSON event stream into the same host-neutral `RunResult` the pi, codex, and
|
|
6
|
+
* claude runners produce, so the engine (runtime.ts) treats an OpenCode subagent
|
|
7
|
+
* identically.
|
|
8
|
+
*
|
|
9
|
+
* Real opencode `run --format json` schema (opencode ≥ 1.17), observed
|
|
10
|
+
* empirically — one JSON object per line:
|
|
11
|
+
* {"type":"step_start", "part":{"type":"step-start",…}}
|
|
12
|
+
* {"type":"text", "part":{"type":"text","text":"…"}} ← assistant text
|
|
13
|
+
* {"type":"tool_use", "part":{"type":"tool","tool":"bash",
|
|
14
|
+
* "state":{"status":"completed","input":{"command":"…"},"title":"…"}}}
|
|
15
|
+
* {"type":"step_finish","part":{"type":"step-finish","reason":"stop"|"tool-calls",
|
|
16
|
+
* "tokens":{"total","input","output","reasoning","cache":{"read","write"}},"cost":0}}
|
|
17
|
+
* {"type":"error", "error":{"name":"…","data":{"message":"…"}}} ← fatal
|
|
18
|
+
*
|
|
19
|
+
* Mapping to the host-neutral contract:
|
|
20
|
+
* - output = accumulated `text` parts of the LAST step (text before a
|
|
21
|
+
* tool call is intermediate reasoning → reset on tool_use)
|
|
22
|
+
* - usage.input = Σ step tokens.input; usage.output = Σ (output + reasoning);
|
|
23
|
+
* cacheRead/cacheWrite from tokens.cache; cost = Σ part.cost;
|
|
24
|
+
* turns = number of step_finish events; contextTokens = latest tokens.total
|
|
25
|
+
* - failure = an `error` event, or a non-zero exit
|
|
26
|
+
*
|
|
27
|
+
* Permission mapping (the codex `sandboxForTools` analogue): OpenCode has no
|
|
28
|
+
* per-run tool-whitelist flag, but it honours a per-process config injected via
|
|
29
|
+
* the `OPENCODE_CONFIG_CONTENT` env var. A read-only phase (no write/edit/bash
|
|
30
|
+
* in its whitelist) is launched with a permission policy that DENIES
|
|
31
|
+
* bash/write/edit — genuinely enforced (a denied tool call is rejected, not
|
|
32
|
+
* merely un-approved). A mutating phase (or no whitelist) runs with `--auto`
|
|
33
|
+
* (auto-approve every permission), the workspace-write analogue.
|
|
34
|
+
*
|
|
35
|
+
* Process handling (idle watchdog, abort, signal-kill detection, stderr cap,
|
|
36
|
+
* error sanitization) mirrors the other runners so behavior is uniform.
|
|
37
|
+
*/
|
|
38
|
+
import { runSubagentProcess, num, unknownAgentResult, } from "taskflow-core";
|
|
39
|
+
import { emptyUsage } from "taskflow-core";
|
|
40
|
+
/** The permission policy injected (via OPENCODE_CONFIG_CONTENT) for a read-only
|
|
41
|
+
* phase: deny every mutating capability so a listed-tools phase without
|
|
42
|
+
* write/edit/bash cannot change the workspace. */
|
|
43
|
+
const READ_ONLY_CONFIG = JSON.stringify({ permission: { bash: "deny", write: "deny", edit: "deny" } });
|
|
44
|
+
export function newOpencodeAccumulator(model) {
|
|
45
|
+
return { usage: emptyUsage(), model, finalText: "", lastActivity: "" };
|
|
46
|
+
}
|
|
47
|
+
function shortTool(part) {
|
|
48
|
+
const tool = String(part?.tool ?? "tool");
|
|
49
|
+
const cmd = part?.state?.input?.command;
|
|
50
|
+
if (tool === "bash" && typeof cmd === "string" && cmd.trim()) {
|
|
51
|
+
const s = cmd.replace(/\s+/g, " ").trim();
|
|
52
|
+
return `$ ${s.length > 64 ? `${s.slice(0, 64)}…` : s}`;
|
|
53
|
+
}
|
|
54
|
+
const title = part?.state?.title;
|
|
55
|
+
if (typeof title === "string" && title.trim())
|
|
56
|
+
return `${tool}: ${title.trim()}`;
|
|
57
|
+
return tool;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Fold one opencode JSON line into the accumulator. Returns a LiveUpdate when
|
|
61
|
+
* the stream produced new activity (for streaming), else null. Empty/malformed
|
|
62
|
+
* lines are ignored — robust to partial buffers and noise, unit-testable
|
|
63
|
+
* without spawning a process.
|
|
64
|
+
*/
|
|
65
|
+
export function foldOpencodeEventLine(acc, line) {
|
|
66
|
+
if (!line.trim())
|
|
67
|
+
return null;
|
|
68
|
+
let event;
|
|
69
|
+
try {
|
|
70
|
+
event = JSON.parse(line);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
let activity = "";
|
|
76
|
+
const part = event.part;
|
|
77
|
+
switch (event.type) {
|
|
78
|
+
case "text":
|
|
79
|
+
if (part && typeof part.text === "string" && part.text) {
|
|
80
|
+
// Text parts stream; concatenate within the current step. A tool call
|
|
81
|
+
// (below) resets this, so only the LAST step's text is the answer.
|
|
82
|
+
acc.finalText += part.text;
|
|
83
|
+
activity = acc.finalText.trim();
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
case "tool_use":
|
|
87
|
+
// A tool call means any text so far was intermediate reasoning, not the
|
|
88
|
+
// final answer — drop it and keep only text that follows the last tool.
|
|
89
|
+
acc.finalText = "";
|
|
90
|
+
if (part)
|
|
91
|
+
activity = shortTool(part);
|
|
92
|
+
break;
|
|
93
|
+
case "step_finish": {
|
|
94
|
+
acc.usage.turns++;
|
|
95
|
+
const tk = part?.tokens;
|
|
96
|
+
if (tk) {
|
|
97
|
+
acc.usage.input += num(tk.input);
|
|
98
|
+
acc.usage.output += num(tk.output) + num(tk.reasoning);
|
|
99
|
+
acc.usage.cacheRead += num(tk.cache?.read);
|
|
100
|
+
acc.usage.cacheWrite += num(tk.cache?.write);
|
|
101
|
+
// contextTokens is a host-specific gauge (NOT additive): opencode's tokens.total is
|
|
102
|
+
// the full last-turn context (input+output+reasoning), already cache-accounted.
|
|
103
|
+
acc.usage.contextTokens = num(tk.total) || acc.usage.contextTokens;
|
|
104
|
+
}
|
|
105
|
+
if (typeof part?.cost === "number")
|
|
106
|
+
acc.usage.cost += part.cost;
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
case "error": {
|
|
110
|
+
const msg = (event.error?.data?.message && String(event.error.data.message)) ||
|
|
111
|
+
(event.error?.name && String(event.error.name)) ||
|
|
112
|
+
"opencode run failed";
|
|
113
|
+
acc.fatalError = msg;
|
|
114
|
+
activity = `error: ${msg}`;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
default:
|
|
118
|
+
return null; // step_start / other — nothing to fold.
|
|
119
|
+
}
|
|
120
|
+
if (activity)
|
|
121
|
+
acc.lastActivity = activity.replace(/\s+/g, " ").trim();
|
|
122
|
+
return { text: acc.lastActivity, usage: { ...acc.usage }, model: acc.model };
|
|
123
|
+
}
|
|
124
|
+
/** Override the opencode binary (tests / unusual installs). */
|
|
125
|
+
export function opencodeBin() {
|
|
126
|
+
return process.env.PI_TASKFLOW_OPENCODE_BIN || "opencode";
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Decide whether a phase is read-only from its tool whitelist — the codex
|
|
130
|
+
* `sandboxForTools` analogue. No whitelist → not read-only (default-capable).
|
|
131
|
+
*/
|
|
132
|
+
export function isReadOnlyPhase(tools) {
|
|
133
|
+
if (!tools || tools.length === 0)
|
|
134
|
+
return false;
|
|
135
|
+
const mutating = new Set(["write", "edit", "bash", "apply_patch"]);
|
|
136
|
+
return !tools.some((t) => mutating.has(t));
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Resolve a taskflow/pi model id to something `opencode run -m` accepts, or
|
|
140
|
+
* `undefined` to let opencode use its configured default.
|
|
141
|
+
*
|
|
142
|
+
* Unlike codex/claude (whose model ids are flat, so "contains `/`" ⇒ a pi
|
|
143
|
+
* provider path to drop), a *valid* opencode model IS `provider/model` (one
|
|
144
|
+
* slash). So we only drop ids that clearly aren't opencode models:
|
|
145
|
+
* - an unresolved role placeholder `{{fast}}`
|
|
146
|
+
* - a pi thinking suffix (`…:xhigh`)
|
|
147
|
+
* - a multi-segment openrouter path (`openrouter/vendor/model`, ≥ 2 slashes)
|
|
148
|
+
* A clean `provider/model` passes straight through.
|
|
149
|
+
*/
|
|
150
|
+
export function resolveOpencodeModel(model) {
|
|
151
|
+
if (!model)
|
|
152
|
+
return undefined;
|
|
153
|
+
if (/^\{\{.*\}\}$/.test(model))
|
|
154
|
+
return undefined; // unresolved role placeholder
|
|
155
|
+
// pi thinking suffix: `<model>:<level>` (e.g. "anthropic/glm-5.2:xhigh"). Only the
|
|
156
|
+
// colon-delimited form is dropped — a real `:tag` (no level keyword) passes
|
|
157
|
+
// through so opencode itself can reject it with a clear error if invalid.
|
|
158
|
+
if (/:\s*(?:xhigh|high|medium|low|off)$/.test(model))
|
|
159
|
+
return undefined;
|
|
160
|
+
if ((model.match(/\//g)?.length ?? 0) >= 2)
|
|
161
|
+
return undefined; // openrouter path (openrouter/vendor/model)
|
|
162
|
+
return model;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Build the full `opencode run` argv from a phase's resolved context — PURE
|
|
166
|
+
* (no process.env, no spawn). Extracted from `runOpencodeAgentTask` so the
|
|
167
|
+
* host's CLI flag contract is unit-testable in CI without a live opencode
|
|
168
|
+
* session. Returns whether the phase is read-only so the caller can inject the
|
|
169
|
+
* matching OPENCODE_CONFIG_CONTENT env without re-deriving it.
|
|
170
|
+
*
|
|
171
|
+
* opencode run <prompt> --format json [--dir cwd] [-m model] [--auto]
|
|
172
|
+
*/
|
|
173
|
+
export function buildOpencodeArgs(ctx) {
|
|
174
|
+
const opencodeModel = resolveOpencodeModel(ctx.model);
|
|
175
|
+
const readOnly = isReadOnlyPhase(ctx.tools);
|
|
176
|
+
const fullPrompt = ctx.systemPrompt.trim()
|
|
177
|
+
? `${ctx.systemPrompt.trim()}\n\n---\n\nTask: ${ctx.task}`
|
|
178
|
+
: `Task: ${ctx.task}`;
|
|
179
|
+
const args = ["run", fullPrompt, "--format", "json"];
|
|
180
|
+
if (ctx.cwd)
|
|
181
|
+
args.push("--dir", ctx.cwd);
|
|
182
|
+
if (opencodeModel)
|
|
183
|
+
args.push("-m", opencodeModel);
|
|
184
|
+
if (!readOnly)
|
|
185
|
+
args.push("--auto");
|
|
186
|
+
return { args, readOnly };
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Run a single subagent task via `opencode run --format json`. Resolves the
|
|
190
|
+
* agent from `agents` by name; returns the same structured `RunResult` the pi,
|
|
191
|
+
* codex, and claude runners do.
|
|
192
|
+
*/
|
|
193
|
+
export async function runOpencodeAgentTask(defaultCwd, agents, agentName, task, opts, globalThinking) {
|
|
194
|
+
const agent = agents.find((a) => a.name === agentName);
|
|
195
|
+
if (!agent)
|
|
196
|
+
return unknownAgentResult(agentName, task, agents);
|
|
197
|
+
const model = opts.model ?? agent.model;
|
|
198
|
+
const tools = opts.tools ?? agent.tools;
|
|
199
|
+
void globalThinking; // opencode's --variant is provider-specific; reserved.
|
|
200
|
+
const cwd = opts.cwd ?? defaultCwd;
|
|
201
|
+
const { args, readOnly } = buildOpencodeArgs({
|
|
202
|
+
systemPrompt: agent.systemPrompt,
|
|
203
|
+
task,
|
|
204
|
+
model,
|
|
205
|
+
tools,
|
|
206
|
+
cwd,
|
|
207
|
+
});
|
|
208
|
+
return runSubagentProcess({
|
|
209
|
+
agent: agentName,
|
|
210
|
+
task,
|
|
211
|
+
model,
|
|
212
|
+
bin: opencodeBin(),
|
|
213
|
+
args,
|
|
214
|
+
// A read-only phase injects the deny-mutations permission policy; the
|
|
215
|
+
// shared runner merges this over the parent env.
|
|
216
|
+
env: readOnly ? { ...process.env, OPENCODE_CONFIG_CONTENT: READ_ONLY_CONFIG } : undefined,
|
|
217
|
+
cwd,
|
|
218
|
+
idleTimeoutMs: opts.idleTimeoutMs,
|
|
219
|
+
signal: opts.signal,
|
|
220
|
+
onLive: opts.onLive,
|
|
221
|
+
acc: newOpencodeAccumulator(model),
|
|
222
|
+
foldLine: foldOpencodeEventLine,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* The OpenCode host's `SubagentRunner`. Drops into `RuntimeDeps.runTask` exactly
|
|
227
|
+
* like the pi/codex/claude runners, so the engine runs unchanged on OpenCode.
|
|
228
|
+
*/
|
|
229
|
+
export const opencodeSubagentRunner = {
|
|
230
|
+
runTask: runOpencodeAgentTask,
|
|
231
|
+
};
|
|
232
|
+
//# sourceMappingURL=opencode-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opencode-runner.js","sourceRoot":"","sources":["../src/opencode-runner.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "taskflow-hosts",
|
|
3
|
+
"version": "0.1.6",
|
|
4
|
+
"description": "Shared host-runner collection for taskflow — the codex, claude, and opencode SubagentRunner implementations + their argv builders and event-stream parsers. The per-host MCP servers, plugin scaffolds, and bins live in codex-taskflow / claude-taskflow / opencode-taskflow; this package holds just the runners so a new host can be added in one place.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=22.19.0"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"development": "./src/index.ts",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./codex": {
|
|
16
|
+
"development": "./src/codex-runner.ts",
|
|
17
|
+
"types": "./dist/codex-runner.d.ts",
|
|
18
|
+
"default": "./dist/codex-runner.js"
|
|
19
|
+
},
|
|
20
|
+
"./claude": {
|
|
21
|
+
"development": "./src/claude-runner.ts",
|
|
22
|
+
"types": "./dist/claude-runner.d.ts",
|
|
23
|
+
"default": "./dist/claude-runner.js"
|
|
24
|
+
},
|
|
25
|
+
"./opencode": {
|
|
26
|
+
"development": "./src/opencode-runner.ts",
|
|
27
|
+
"types": "./dist/opencode-runner.d.ts",
|
|
28
|
+
"default": "./dist/opencode-runner.js"
|
|
29
|
+
},
|
|
30
|
+
"./*": {
|
|
31
|
+
"development": "./src/*.ts",
|
|
32
|
+
"types": "./dist/*.d.ts",
|
|
33
|
+
"default": "./dist/*.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"main": "./dist/index.js",
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"README.md"
|
|
41
|
+
],
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"taskflow-core": "0.1.6"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"typescript": "^6.0.3"
|
|
47
|
+
},
|
|
48
|
+
"keywords": [
|
|
49
|
+
"taskflow",
|
|
50
|
+
"subagent",
|
|
51
|
+
"codex",
|
|
52
|
+
"claude",
|
|
53
|
+
"opencode",
|
|
54
|
+
"orchestration"
|
|
55
|
+
],
|
|
56
|
+
"license": "MIT",
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json"
|
|
59
|
+
}
|
|
60
|
+
}
|