taskflow-hosts 0.2.8 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,9 +3,9 @@
3
3
  > Shared host-runner collection for [taskflow](https://github.com/heggria/taskflow).
4
4
 
5
5
  This package holds the `SubagentRunner` implementations for taskflow's non-pi
6
- hosts — **codex**, **claude**, **opencode**, and **grok** — plus their pure argv
6
+ hosts — **codex**, **claude**, **opencode**, **grok**, and **hermes** — plus their pure argv
7
7
  builders (`buildCodexArgs` / `buildClaudeArgs` / `buildOpencodeArgs` /
8
- `buildGrokArgs`) and event-stream parsers. It is the **single place** host
8
+ `buildGrokArgs` / `buildHermesArgs`) and event-stream parsers. It is the **single place** host
9
9
  runners live; a new host adds a `<host>-runner.ts` here.
10
10
 
11
11
  ## Why a separate package
@@ -17,13 +17,13 @@ Each host has two halves:
17
17
  permission/model helpers.
18
18
  2. **The delivery** — the per-host MCP server + bin + plugin scaffold, which is
19
19
  that host ecosystem's install target (`codex plugin add`, `claude plugin
20
- install`, OpenCode config, `grok plugin install`).
20
+ install`, OpenCode config, `grok plugin install`, Hermes `mcp_servers`).
21
21
 
22
22
  Half #1 is nearly identical in *shape* across hosts and changes for the same
23
23
  reasons (a `taskflow-core` contract change, or a host CLI flag change). Half #2
24
24
  is genuinely host-specific (different install mechanisms, different plugin
25
25
  manifests). So #1 is collected here; #2 stays in `codex-taskflow` /
26
- `claude-taskflow` / `opencode-taskflow` / `grok-taskflow`, which import their
26
+ `claude-taskflow` / `opencode-taskflow` / `grok-taskflow` / `hermes-taskflow`, which import their
27
27
  runner from this package.
28
28
 
29
29
  ## Install
@@ -31,7 +31,7 @@ runner from this package.
31
31
  You usually don't install this directly — install the host delivery package:
32
32
 
33
33
  ```bash
34
- npm install -g codex-taskflow # or claude-taskflow / opencode-taskflow / grok-taskflow
34
+ npm install -g codex-taskflow # or claude-taskflow / opencode-taskflow / grok-taskflow / hermes-taskflow
35
35
  ```
36
36
 
37
37
  For code-level use:
@@ -46,12 +46,14 @@ npm install taskflow-hosts
46
46
  // one host, tree-shaken:
47
47
  import { codexSubagentRunner, buildCodexArgs } from "taskflow-hosts/codex";
48
48
  import { grokSubagentRunner, buildGrokArgs } from "taskflow-hosts/grok";
49
+ import { hermesSubagentRunner, buildHermesArgs } from "taskflow-hosts/hermes";
49
50
 
50
51
  // or the barrel:
51
52
  import {
52
53
  claudeSubagentRunner,
53
54
  opencodeSubagentRunner,
54
55
  grokSubagentRunner,
56
+ hermesSubagentRunner,
55
57
  } from "taskflow-hosts";
56
58
  ```
57
59
 
@@ -69,6 +71,7 @@ helpers (`sandboxForTools`, `permissionArgsForTools` / `permissionArgsForGrokToo
69
71
  | Claude Code | `claude -p --output-format stream-json` | `claude-taskflow` |
70
72
  | OpenCode | `opencode run --format json` | `opencode-taskflow` |
71
73
  | Grok Build | `grok -p --output-format streaming-json` | `grok-taskflow` |
74
+ | Hermes Agent | `hermes chat -q -Q --source tool` | `hermes-taskflow` |
72
75
 
73
76
  ## Adding a host
74
77
 
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Hermes Agent subagent runner — the Hermes host's `SubagentRunner`.
3
+ *
4
+ * Spawns an isolated one-shot:
5
+ * hermes chat -q <prompt> -Q --source tool [--in cwd] [-m model] [-t toolsets]
6
+ * [--reasoning level] [--max-turns N] [--yolo]
7
+ *
8
+ * Quiet mode (`-Q`) emits plain text on stdout (final answer).
9
+ * Session id is printed on stderr by Hermes so piped stdout stays clean.
10
+ * Mapping to the host-neutral contract:
11
+ * - output = stdout answer text (session id is stderr-only metadata)
12
+ * - lastActivity = last non-empty stdout line
13
+ * - usage = unavailable from quiet mode (emptyUsage); budgeted runs
14
+ * still fail-closed at the engine when costs are required
15
+ * - failure = non-zero exit, or empty output with non-zero semantics
16
+ *
17
+ * Permission mapping:
18
+ * - read-only + local-read tools → `-t taskflow_readonly_files`
19
+ * (ephemeral plugin: read_file + search_files only; write_file/patch blocked)
20
+ * - read-only without local tools → explicit empty model-only `-t`; network opt-in via
21
+ * PI_TASKFLOW_HERMES_READONLY_WEB=1 → web,search
22
+ * - mutating / default-capable → requires PI_TASKFLOW_HERMES_UNSAFE_YOLO=1 + `--yolo`
23
+ * - isolation: ephemeral HERMES_HOME (creds + minimal config + RO plugin) and
24
+ * `--ignore-rules` (not `--safe-mode`, so our config can disable reasoning UI)
25
+ *
26
+ * Quiet mode (`-Q`): answer on stdout; `session_id:` on stderr. Reasoning boxes
27
+ * are suppressed via config and stripped from output as defense-in-depth.
28
+ * Process handling (idle watchdog, abort, signal-kill, stderr cap, sanitize)
29
+ * is delegated to shared `runSubagentProcess` in taskflow-core.
30
+ *
31
+ * @see https://hermes-agent.nousresearch.com/docs/
32
+ */
33
+ import { type AgentConfig, type LiveUpdate, type RunOptions, type RunResult, type SubagentRunner, type UsageStats } from "taskflow-core";
34
+ /** Explicit operator acknowledgement required before Hermes may use `--yolo`
35
+ * (bypass dangerous-command approvals) for mutating/default-capable phases. */
36
+ export declare const HERMES_UNSAFE_YOLO_ENV = "PI_TASKFLOW_HERMES_UNSAFE_YOLO";
37
+ /** Optional max-turns override for child Hermes runs (default 64). */
38
+ export declare const HERMES_MAX_TURNS_ENV = "PI_TASKFLOW_HERMES_MAX_TURNS";
39
+ export declare function hermesUnsafeYoloEnabled(env?: NodeJS.ProcessEnv): boolean;
40
+ /**
41
+ * Build a least-privilege env for a Hermes child.
42
+ * Keeps provider credentials + HERMES_HOME (for .env auth), but strips YOLO and
43
+ * other process-scoped bypass flags so parent gateway yolo cannot silently arm
44
+ * a read-only phase. Also strips prompt-injection HERMES_* control vars.
45
+ */
46
+ export declare function hermesChildEnv(source?: NodeJS.ProcessEnv, opts?: {
47
+ allowUnsafeYolo?: boolean;
48
+ }): NodeJS.ProcessEnv;
49
+ /** Accumulated state folded from Hermes quiet-mode plain-text stdout. */
50
+ export interface HermesAccumulator {
51
+ usage: UsageStats;
52
+ model?: string;
53
+ finalText: string;
54
+ lastActivity: string;
55
+ fatalError?: string;
56
+ /** Quiet mode has no structured terminal event; set true once we have seen
57
+ * any non-meta stdout (or on process end via the runner when empty is OK). */
58
+ terminalSeen?: boolean;
59
+ sessionId?: string;
60
+ }
61
+ export declare function newHermesAccumulator(model?: string): HermesAccumulator;
62
+ /**
63
+ * Fold one stdout line from `hermes chat -Q`. Stdout is answer text; Hermes
64
+ * session metadata is parsed from stderr after process exit. Empty lines are
65
+ * ignored for activity but preserved inside the body once content has started.
66
+ */
67
+ export declare function foldHermesQuietLine(acc: HermesAccumulator, line: string): LiveUpdate | null;
68
+ /** Override the hermes binary (tests / unusual installs). */
69
+ export declare function hermesBin(): string;
70
+ /**
71
+ * Decide whether a phase is read-only from its tool whitelist. No whitelist →
72
+ * not read-only (default-capable, needs --yolo opt-in).
73
+ *
74
+ * Hermes-mutating tool aliases (taskflow DSL style + hermes native names).
75
+ */
76
+ export declare function isHermesReadOnlyPhase(tools: string[] | undefined): boolean;
77
+ /** Hermes toolset name registered by the ephemeral taskflow_readonly plugin. */
78
+ export declare const HERMES_READONLY_FILES_TOOLSET = "taskflow_readonly_files";
79
+ /** Empty toolset — always pass `-t` so Hermes does not fall back to hermes-cli defaults. */
80
+ export declare const HERMES_MODEL_ONLY_TOOLSET = "taskflow_model_only";
81
+ /**
82
+ * Map a phase tool whitelist to Hermes `-t` toolsets. Best-effort:
83
+ * - read-only + local-read aliases → `taskflow_readonly_files` (plugin)
84
+ * - read-only + READONLY_WEB → adds web,search
85
+ * - read-only otherwise → `taskflow_model_only` (empty toolset; NEVER omit -t —
86
+ * Hermes defaults to full hermes-cli tools when -t is absent)
87
+ * - mutating with explicit tools → union of matching toolsets (narrow)
88
+ * - default / empty tools → file,terminal (network/control-plane denied)
89
+ * - unmapped non-empty tools list → throw (never fail-open to wide default)
90
+ */
91
+ export declare function resolveHermesToolsets(tools: string[] | undefined, readOnly: boolean, opts?: {
92
+ readonlyWeb?: boolean;
93
+ }): string;
94
+ /**
95
+ * Strip Hermes quiet-mode reasoning chrome and model think-tags from text.
96
+ * Primary suppression is `display.show_reasoning: false` in the ephemeral
97
+ * config; this is defense-in-depth when a model still leaks boxes/tags.
98
+ */
99
+ export declare function stripHermesReasoningNoise(text: string): string;
100
+ /** Opt-in network for read-only Hermes phases (default off). */
101
+ export declare const HERMES_READONLY_WEB_ENV = "PI_TASKFLOW_HERMES_READONLY_WEB";
102
+ export declare function hermesReadonlyWebEnabled(env?: NodeJS.ProcessEnv): boolean;
103
+ /** Resolve a taskflow model id to something `hermes chat -m` accepts. */
104
+ export declare function resolveHermesModel(model: string | undefined): string | undefined;
105
+ /** Normalize Taskflow thinking aliases to Hermes `--reasoning` levels. */
106
+ export declare function resolveHermesReasoning(thinking: string | undefined): string | undefined;
107
+ export interface HermesArgsCtx {
108
+ systemPrompt: string;
109
+ task: string;
110
+ model?: string;
111
+ thinking?: string;
112
+ tools?: string[];
113
+ cwd?: string;
114
+ /** Explicit acknowledgement for Hermes `--yolo`. */
115
+ allowUnsafeYolo?: boolean;
116
+ /** Max tool-calling iterations (default 64). */
117
+ maxTurns?: number;
118
+ /** Opt-in network for read-only phases (PI_TASKFLOW_HERMES_READONLY_WEB=1). */
119
+ readonlyWeb?: boolean;
120
+ }
121
+ export interface HermesArgs {
122
+ args: string[];
123
+ readOnly: boolean;
124
+ toolsets: string;
125
+ }
126
+ /**
127
+ * Build the full `hermes chat` argv — PURE (no process.env, no spawn).
128
+ *
129
+ * hermes chat -q <prompt> -Q --source tool
130
+ * --ignore-rules
131
+ * [--in cwd] [-m model] [-t toolsets] [--reasoning level]
132
+ * [--max-turns N] [--yolo]
133
+ *
134
+ * Isolation: ephemeral HERMES_HOME (credentials + show_reasoning:false + RO
135
+ * plugin). `--ignore-rules` skips AGENTS.md injection. Do not use --safe-mode
136
+ * (it would ignore our ephemeral config/plugin).
137
+ * Credentials still load from the ephemeral home's .env/auth.json. */
138
+ export declare function buildHermesArgs(ctx: HermesArgsCtx): HermesArgs;
139
+ /** Keep only explicitly supported inference-provider assignments from dotenv. */
140
+ export declare function filterHermesProviderDotenv(source: string): string;
141
+ /** Filter Hermes auth.json to known inference-provider credential entries. */
142
+ export declare function filterHermesAuthJson(source: string, routedProviders?: ReadonlySet<string>): string;
143
+ /**
144
+ * Pull a top-level YAML mapping block (e.g. `model:`) from parent config text.
145
+ * Indentation-based; no full YAML parser dependency.
146
+ */
147
+ export declare function extractYamlTopLevelBlock(source: string, key: string): string | undefined;
148
+ /**
149
+ * Carry only non-secret scalar routing fields from the parent config.
150
+ * This deliberately omits `providers:` and all nested mappings (MCP/plugins/
151
+ * api_key/token) rather than treating indentation as a security boundary.
152
+ */
153
+ export declare function sanitizeHermesRoutingConfig(source: string): string;
154
+ /** Build ephemeral config.yaml text: isolation defaults + parent model routing. */
155
+ export declare function buildEphemeralHermesConfigYaml(parentHome: string, opts?: {
156
+ readOnly?: boolean;
157
+ }): string;
158
+ export interface EphemeralHermesHome {
159
+ /** Temp directory used as HERMES_HOME for one child. */
160
+ home: string;
161
+ /** Remove the temp home (best-effort). */
162
+ cleanup: () => void;
163
+ }
164
+ /**
165
+ * Build a throwaway HERMES_HOME with credentials + minimal config + RO plugin.
166
+ * Children authenticate via .env/auth.json, cannot see parent skills/MCP, and
167
+ * get display.show_reasoning=false so quiet stdout stays clean.
168
+ */
169
+ export declare function prepareEphemeralHermesHome(parentHome: string, opts?: {
170
+ tmpRoot?: string;
171
+ readOnly?: boolean;
172
+ }): EphemeralHermesHome;
173
+ /**
174
+ * Resolve the operator Hermes profile to clone credentials/model routing from.
175
+ * Prefer `PI_TASKFLOW_HERMES_PARENT_HOME`, then a usable `HERMES_HOME`, then `~/.hermes`.
176
+ * Skips ephemeral taskflow temps and empty tmp HERMES_HOME leftovers.
177
+ */
178
+ export declare function resolveParentHermesHome(env?: NodeJS.ProcessEnv): string;
179
+ /**
180
+ * Run a single subagent task via `hermes chat -q -Q`. Resolves the agent from
181
+ * `agents` by name; returns the same structured `RunResult` the other host
182
+ * runners produce.
183
+ */
184
+ export declare function runHermesAgentTask(defaultCwd: string, agents: AgentConfig[], agentName: string, task: string, opts: RunOptions, globalThinking?: string): Promise<RunResult>;
185
+ /** The Hermes host's `SubagentRunner`. Drops into `RuntimeDeps.runTask`. */
186
+ export declare const hermesSubagentRunner: SubagentRunner<AgentConfig>;
187
+ //# sourceMappingURL=hermes-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hermes-runner.d.ts","sourceRoot":"","sources":["../src/hermes-runner.ts"],"names":[],"mappings":"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"}