hermes-action-bridge 0.4.0 → 0.6.1

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/CHANGELOG.md +61 -0
  2. package/README.md +140 -15
  3. package/dist/adapters/hermes-cli.d.ts +14 -0
  4. package/dist/adapters/hermes-cli.js +80 -19
  5. package/dist/adapters/hermes-cli.js.map +1 -1
  6. package/dist/approvals.d.ts +35 -0
  7. package/dist/approvals.js +96 -0
  8. package/dist/approvals.js.map +1 -0
  9. package/dist/audit-log.d.ts +21 -0
  10. package/dist/audit-log.js +31 -0
  11. package/dist/audit-log.js.map +1 -0
  12. package/dist/capabilities.d.ts +29 -0
  13. package/dist/capabilities.js +24 -0
  14. package/dist/capabilities.js.map +1 -0
  15. package/dist/cli.js +205 -18
  16. package/dist/cli.js.map +1 -1
  17. package/dist/doctor.d.ts +8 -1
  18. package/dist/doctor.js +76 -13
  19. package/dist/doctor.js.map +1 -1
  20. package/dist/execution-gate.d.ts +5 -0
  21. package/dist/execution-gate.js +20 -0
  22. package/dist/execution-gate.js.map +1 -0
  23. package/dist/http-server.d.ts +18 -0
  24. package/dist/http-server.js +310 -0
  25. package/dist/http-server.js.map +1 -0
  26. package/dist/install/launcher.d.ts +3 -0
  27. package/dist/install/launcher.js +7 -0
  28. package/dist/install/launcher.js.map +1 -0
  29. package/dist/install/mcp-service.d.ts +44 -0
  30. package/dist/install/mcp-service.js +172 -0
  31. package/dist/install/mcp-service.js.map +1 -0
  32. package/dist/install/templates.js +38 -32
  33. package/dist/install/templates.js.map +1 -1
  34. package/dist/install/types.d.ts +1 -0
  35. package/dist/jobs.d.ts +51 -0
  36. package/dist/jobs.js +244 -0
  37. package/dist/jobs.js.map +1 -0
  38. package/dist/mcp-server.d.ts +32 -0
  39. package/dist/mcp-server.js +382 -25
  40. package/dist/mcp-server.js.map +1 -1
  41. package/dist/types.d.ts +2 -0
  42. package/dist/validation.d.ts +1 -0
  43. package/dist/validation.js +8 -0
  44. package/dist/validation.js.map +1 -0
  45. package/docs/ARCHITECTURE.md +88 -7
  46. package/docs/FUNCTIONAL-TESTS.md +45 -12
  47. package/docs/SMITHERY.md +23 -0
  48. package/docs/distribution-runbook.md +515 -0
  49. package/examples/claude-code/SKILL.md +35 -29
  50. package/examples/codex/SKILL.md +35 -29
  51. package/package.json +12 -4
  52. package/plugins/hermes-action/.claude-plugin/plugin.json +12 -0
  53. package/plugins/hermes-action/.codex-plugin/plugin.json +30 -0
  54. package/plugins/hermes-action/.mcp.json +8 -0
  55. package/plugins/hermes-action/assets/app-icon.png +0 -0
  56. package/plugins/hermes-action/skills/hermes-action-bridge/SKILL.md +61 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,67 @@ All notable changes to this project are documented here. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
5
5
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [Unreleased]
8
+
9
+ ## [0.6.1] - 2026-07-25
10
+
11
+ ### Fixed
12
+
13
+ - Allowed the deterministic MCPB reproducibility test enough time to rebuild
14
+ its production bundle on GitHub-hosted runners without hitting Vitest's
15
+ default test or setup-hook timeouts.
16
+
17
+ ## [0.6.0] - 2026-07-25
18
+
19
+ ### Added
20
+
21
+ - Claude Code plugin marketplace metadata for installing the version-pinned
22
+ Hermes Action plugin directly from GitHub.
23
+ - A versioned, self-contained MCPB release artifact for Claude Desktop and
24
+ Smithery local-stdio distribution.
25
+ - A canonical `skills/hermes-action-bridge/SKILL.md` entry for skills.sh and
26
+ other Agent Skills-compatible clients.
27
+ - Distribution drift tests and release-asset automation for the marketplace,
28
+ Skill, and MCPB metadata.
29
+
30
+ ### Changed
31
+
32
+ - Pinned the Codex marketplace plugin runtime to the matching published bridge
33
+ version instead of resolving an unbounded npm latest release.
34
+
35
+ ## [0.5.0] - 2026-07-25
36
+
37
+ ### Added
38
+
39
+ - Complete Codex and Claude Code installation: `install all` now installs the
40
+ managed skills and registers a verified global MCP server through each
41
+ agent's native CLI using the absolute bridge launcher.
42
+ - `doctor` now detects missing/conflicting MCP registrations and performs a
43
+ token-free MCP initialize/list-tools handshake.
44
+ - Configuration-backed `hermes_capabilities`, concurrency-limited
45
+ asynchronous jobs (`submit`, `status`, `result`, `cancel`), and one-shot
46
+ two-phase actions (`prepare`, `approve`, `reject`).
47
+ - Privacy-preserving owner-only JSONL audit metadata for job and approval
48
+ transitions.
49
+ - Opt-in Streamable HTTP transport with loopback defaults and an authenticated,
50
+ Tailscale-only direct-listen mode.
51
+
52
+ ### Changed
53
+
54
+ - Native skills now prefer discoverable `hermes_*` MCP tools and clearly
55
+ separate Hermes-owned integrations from ordinary local coding work.
56
+ - The Codex plugin manifest now uses the supported `mcpServers` shape, ships in
57
+ the npm tarball, and shares the package version.
58
+ - Refreshed patched `fast-uri` and `postcss` releases in the lockfile and
59
+ documented why the remaining transitive Hono advisory is not reachable in
60
+ the bridge HTTP architecture.
61
+
62
+ ### Documentation
63
+
64
+ - Added a "Trusted presets (direct execution)" README section: how to define a `require_approval_for: []`
65
+ preset in user config to run `execute` actions without the approval gate, plus the safety trade-offs
66
+ (keep it in user scope, never allowlist `hermes-action run`).
67
+
7
68
  ## [0.4.0] - 2026-07-03
8
69
 
9
70
  Deterministic security model, per-call MCP overrides, and installer hardening.
package/README.md CHANGED
@@ -30,7 +30,9 @@ If an agent needs to do something outside its local coding session — research,
30
30
  - Automatic large-context delivery: an oversized prompt is handed to Hermes through a secure temp file instead of overflowing the command line.
31
31
  - Per-run timeouts (configurable, with per-mode defaults) that always reap the Hermes child process.
32
32
  - Dry-run mode for debugging the exact Hermes command, prompt, and computed prompt size.
33
- - Minimal MCP server exposing delegation tools: `hermes_run`, `hermes_plan`, `hermes_presets`, and `hermes_status`.
33
+ - Complete Codex and Claude Code installer: managed skill plus verified global MCP registration.
34
+ - MCP capability discovery, cancellable asynchronous jobs, and one-shot two-phase approvals with a privacy-preserving local audit log.
35
+ - Local stdio transport plus an opt-in, authenticated Streamable HTTP transport for Tailscale-restricted hosts.
34
36
  - No project-specific assumptions. All behavior is configured through YAML and CLI flags.
35
37
 
36
38
  ## Requirements
@@ -52,13 +54,46 @@ Install from npm:
52
54
  npm install -g hermes-action-bridge
53
55
  ```
54
56
 
55
- Then check it works:
57
+ Install the skill and MCP registration for Codex and Claude Code, then verify
58
+ the complete setup:
56
59
 
57
60
  ```bash
58
61
  hermes-action --version
62
+ hermes-action install all
59
63
  hermes-action doctor
60
64
  ```
61
65
 
66
+ Global installation is all-or-nothing for the requested agents. If Codex or
67
+ Claude Code is unavailable, conflicting, or cannot verify its user-scoped
68
+ stdio MCP entry, the command exits non-zero without installing the requested
69
+ skills. Use `--project` for an explicit skill-only setup.
70
+
71
+ ### Other distribution channels
72
+
73
+ Choose the channel that matches the agent surface. Hermes Agent must always be
74
+ installed and configured on the same computer as the local bridge.
75
+
76
+ | Channel | What it configures | Install |
77
+ | --- | --- | --- |
78
+ | npm installer | Skill and verified user-scoped MCP for Codex CLI/app and Claude Code | `hermes-action install all` |
79
+ | Claude Code marketplace | Claude plugin with the Skill and a version-pinned MCP server | `claude plugin marketplace add TheBlueHouse75/hermes-action-bridge`, then `claude plugin install hermes-action@hermes-action-bridge` |
80
+ | Claude Desktop | Self-contained MCPB extension for the bridge runtime | From the [latest GitHub Release](https://github.com/TheBlueHouse75/hermes-action-bridge/releases/latest), download `hermes-action-bridge-<version>.mcpb` and open it with Claude Desktop |
81
+ | Agent Skills | Portable instructions for supported coding agents | `npx skills add TheBlueHouse75/hermes-action-bridge --skill hermes-action-bridge` |
82
+ | Smithery | The same local MCPB bundle through the Smithery catalog | Check the [latest release notes](https://github.com/TheBlueHouse75/hermes-action-bridge/releases/latest) for the verified Smithery listing or its pending status |
83
+
84
+ The standalone Agent Skill only teaches an agent when and how to delegate; it
85
+ does not register an MCP server. Use the npm installer, Claude marketplace, or
86
+ MCPB channel when MCP tools are required.
87
+
88
+ Maintainers releasing a new version should follow the
89
+ [distribution runbook](docs/distribution-runbook.md), which covers the
90
+ automated release workflow and the manual directory publication checks.
91
+
92
+ Codex CLI and the Codex surface in the ChatGPT desktop app share the local
93
+ Codex MCP configuration on the same host. A general ChatGPT connector is a
94
+ different distribution target: it requires a remotely reachable MCP server
95
+ and is not installed by this local stdio bundle.
96
+
62
97
  <details>
63
98
  <summary>From source (for development)</summary>
64
99
 
@@ -103,10 +138,12 @@ hermes-action run --dry-run --json "Summarize this project."
103
138
 
104
139
  ## Execution modes
105
140
 
106
- - `plan`: Hermes returns a plan only. No side effects.
141
+ - `plan`: Hermes is instructed to return a plan only. Treat it as open-world because configured tools remain available.
107
142
  - `draft`: Hermes produces an artifact only. No external side effects.
108
143
  - `execute`: Hermes may execute allowed actions, while still following Hermes' own safety rules.
109
- - `request-approval`: Hermes prepares the action and asks the human for approval before irreversible external effects.
144
+ - `request-approval`: Hermes prepares an action without executing it. MCP
145
+ callers should use `hermes_prepare` and then call `hermes_approve` only after
146
+ explicit human approval.
110
147
 
111
148
  The guard is deterministic and language-agnostic: in `execute` mode the bridge switches to `request-approval` by default — regardless of the prompt's wording or language — unless the preset is explicitly trusted (empty `require_approval_for`) or `--yolo` is set. It does not try to infer risk from keywords. See [Security model](#security-model) for why the real barrier is your coding agent's own approval prompt.
112
149
 
@@ -132,6 +169,30 @@ YOLO only bypasses the bridge policy. It does not remove Hermes Agent's own safe
132
169
 
133
170
  Use it only when the caller and environment are trusted.
134
171
 
172
+ ## Trusted presets (direct execution)
173
+
174
+ By default every `execute` is downgraded to `request-approval` — the right default when an agent might act unattended. When *you* are the one asking for an action and want it to run without that extra gate, define a **trusted preset**: one whose `require_approval_for` is an empty list. Put it in your **user** config so it is yours alone and available in every project:
175
+
176
+ ```yaml
177
+ # ~/.config/hermes-action/config.yaml (user scope, merged before any project config)
178
+ presets:
179
+ act:
180
+ description: Direct execution of an action I explicitly asked for (trusted).
181
+ require_approval_for: []
182
+ ```
183
+
184
+ Run actions through it — `execute` stays `execute`, with no downgrade and no `--yolo` needed:
185
+
186
+ ```bash
187
+ hermes-action run --preset act --mode execute "post the release note to #general"
188
+ ```
189
+
190
+ **Keep it safe:**
191
+
192
+ - A trusted preset relaxes only the bridge's own gate. The real barrier stays your coding agent's approval prompt, so **do not allowlist `hermes-action run` (`execute`/`yolo`)** (see [Security model](#security-model)).
193
+ - Keep it in your **user** config; it never ships in the public package, and the distributed `SKILL.md` stays cautious for everyone else.
194
+ - Reserve trusted presets for actions a human explicitly asks for. Leave the conservative default for anything an agent could trigger on its own.
195
+
135
196
  ## Configuration
136
197
 
137
198
  `hermes-action` loads config in this order:
@@ -222,6 +283,7 @@ hermes-action run [options] "request"
222
283
  hermes-action presets [--json]
223
284
  hermes-action status [--json]
224
285
  hermes-action mcp
286
+ hermes-action serve [--listen 127.0.0.1] [--port 8765]
225
287
  hermes-action install <claude-code|codex|all|mcp> [options]
226
288
  hermes-action uninstall <claude-code|codex|all|mcp> [options]
227
289
  hermes-action doctor [--json] [--probe]
@@ -246,17 +308,24 @@ Common `run` options:
246
308
 
247
309
  ## Native agent skills
248
310
 
249
- Instead of pasting instructions by hand, install a native skill so Claude Code and Codex know when to delegate to Hermes. The skill is the same open-standard `SKILL.md` for both agents; the CLI stays the deterministic execution layer.
311
+ Instead of pasting instructions and MCP snippets by hand, run the complete
312
+ installer. It installs the open-standard skill and registers the local stdio
313
+ MCP server through each agent's own CLI:
250
314
 
251
315
  ```bash
252
- hermes-action doctor # check Node, Hermes, agents, and installed skills
253
- hermes-action install all # ~/.claude/skills and ~/.codex/skills
316
+ hermes-action install all # skills + global MCP for both agents
317
+ hermes-action doctor # verifies skills, registrations, and MCP handshake
254
318
  hermes-action install claude-code --print # preview, write nothing
255
319
  hermes-action install codex --project-hint # also add a marker block to AGENTS.md
256
320
  hermes-action uninstall all
257
321
  ```
258
322
 
259
- Install behavior is safe by default: it never modifies `CLAUDE.md` / `AGENTS.md` unless you pass `--project-hint`, refuses to overwrite a file it did not generate, and is idempotent. Use `--project` for a project-local skill and `--dry-run` to preview operations.
323
+ Install behavior is safe by default: it uses a stable absolute bridge path,
324
+ does not depend on an interactive `PATH`, never modifies `CLAUDE.md` /
325
+ `AGENTS.md` unless you pass `--project-hint`, and refuses to overwrite either a
326
+ foreign skill or a customized MCP entry. Re-running it is idempotent. Use
327
+ `--project` for a project-local skill only and `--dry-run` to preview
328
+ operations.
260
329
 
261
330
  The generated skill is shown in [`examples/claude-code/SKILL.md`](examples/claude-code/SKILL.md). Project-hint usage is documented in [`examples/claude-code/CLAUDE.md`](examples/claude-code/CLAUDE.md) and [`examples/codex/AGENTS.md`](examples/codex/AGENTS.md).
262
331
 
@@ -269,19 +338,24 @@ codex plugin marketplace add TheBlueHouse75/hermes-action-bridge
269
338
  codex plugin add hermes-action@hermes-action-bridge
270
339
  ```
271
340
 
272
- This registers the `hermes-action` MCP server and the Hermes delegation skill in Codex. Hermes Agent must be installed locally — the plugin runs `npx -y hermes-action-bridge mcp`.
341
+ This registers the `hermes-action` MCP server and the Hermes delegation skill
342
+ in Codex. Hermes Agent must be installed locally — the plugin runs the
343
+ version-pinned npm package with `npx`.
273
344
 
274
- > **Faster MCP startup:** `npx` re-resolves the package on every server start (~200 ms overhead, more on a cold cache). If you install the package globally (`npm install -g hermes-action-bridge`), point the MCP config at the binary — `command: "hermes-action"`, `args: ["mcp"]` — which is what `hermes-action install mcp` generates.
345
+ > **Faster MCP startup:** `npx` re-resolves the package on every server start.
346
+ > The native installer registers the absolute global launcher instead.
275
347
 
276
348
  ## MCP configuration
277
349
 
278
- Print the config snippet for your client (Claude Code / Cursor / VS Code use JSON; Codex uses TOML):
350
+ Register the global MCP server for Codex and Claude Code without reinstalling
351
+ their skills:
279
352
 
280
353
  ```bash
281
354
  hermes-action install mcp
282
355
  ```
283
356
 
284
- For Claude Code, write or merge the project `.mcp.json` directly (preserving other servers):
357
+ For other JSON MCP clients, or a Claude Code project-scoped configuration,
358
+ write or merge `.mcp.json` directly while preserving other servers:
285
359
 
286
360
  ```bash
287
361
  hermes-action install mcp --write
@@ -325,10 +399,54 @@ Exposed tools:
325
399
 
326
400
  - `hermes_run`: delegate a request to Hermes. Optional overrides: `mode`, `preset`, `contextFiles`, `yolo`, `dryRun`, and — to trade cost for speed on simple tasks — `model`, `provider`, `maxTurns`, `timeoutSeconds`.
327
401
  - `hermes_plan`: shortcut for `hermes_run` with `mode=plan`.
402
+ - `hermes_capabilities`: report configured presets, skills, toolsets, runtime, and policy without inventing live integrations.
328
403
  - `hermes_presets`: list configured presets.
329
404
  - `hermes_status`: check the configured Hermes runtime command.
405
+ - `hermes_submit`, `hermes_job_status`, `hermes_result`, `hermes_cancel`:
406
+ manage bounded, process-local asynchronous jobs.
407
+ - `hermes_prepare`, `hermes_approve`, `hermes_reject`: create and consume a
408
+ short-lived one-shot approval before execution.
409
+
410
+ The MCP surface delegates to Hermes instead of mirroring every Hermes tool.
411
+ Jobs and approvals are intentionally process-local: restarting the MCP server
412
+ clears them. Audit events are written as owner-only JSONL metadata under
413
+ `$XDG_STATE_HOME/hermes-action/audit.jsonl` (or
414
+ `~/.local/state/hermes-action/audit.jsonl`) without prompt, context, token, or
415
+ full result content.
416
+
417
+ ## Remote MCP over Tailscale
418
+
419
+ The HTTP transport is opt-in and loopback-only by default:
420
+
421
+ ```bash
422
+ hermes-action serve --listen 127.0.0.1 --port 8765
423
+ ```
424
+
425
+ Prefer placing Tailscale Serve in front of that loopback endpoint. Forwarded
426
+ `*.ts.net` requests still require a bearer token, so start the loopback server
427
+ with `--token-env`:
428
+
429
+ ```bash
430
+ export HERMES_ACTION_HTTP_TOKEN="<at-least-32-random-bytes>"
431
+ hermes-action serve \
432
+ --listen 127.0.0.1 \
433
+ --port 8765 \
434
+ --token-env HERMES_ACTION_HTTP_TOKEN
435
+ ```
436
+
437
+ For direct tailnet listening, the bridge accepts only an explicit Tailscale
438
+ IPv4 address and requires the same bearer-token setup:
439
+
440
+ ```bash
441
+ hermes-action serve \
442
+ --listen 100.64.0.10 \
443
+ --port 8765 \
444
+ --allow-tailnet \
445
+ --token-env HERMES_ACTION_HTTP_TOKEN
446
+ ```
330
447
 
331
- The MCP surface is intentionally small. The bridge delegates to Hermes instead of exposing every Hermes tool one by one.
448
+ The token is never accepted as a CLI value or stored in bridge configuration.
449
+ Public/wildcard and non-Tailscale addresses are refused.
332
450
 
333
451
  ## Development
334
452
 
@@ -351,11 +469,18 @@ Functional tests use a fake Hermes binary to verify command construction, prompt
351
469
 
352
470
  ## Security model
353
471
 
354
- The bridge assumes a **supervised host agent**: Claude Code / Codex ask you to approve each `hermes-action` command (or `hermes_run` tool call) before it runs. That host approval — not the keyword detection — is the real, deterministic, language-agnostic barrier, and it is what makes a French, German, or paraphrased request as safe as an English one.
472
+ Direct CLI and `hermes_run` usage assumes a **supervised host agent**: Claude
473
+ Code / Codex ask you to approve the command or tool call. For MCP side effects,
474
+ prefer the bridge's additional, testable two-phase flow:
475
+ `hermes_prepare` returns a local no-tool summary and opaque approval ID, and
476
+ `hermes_approve` consumes the unchanged request exactly once after the human
477
+ approves it.
355
478
 
356
479
  The bridge's own guard is a secondary net: `execute` is downgraded to `request-approval` by default unless a preset is trusted (empty `require_approval_for`) or `--yolo` is set. The bridge never injects `--yolo` or a bypass flag on its own.
357
480
 
358
- - **Do not allowlist / auto-approve `hermes-action run` (especially `--mode execute` or `--yolo`)** in your coding agent. A command-pattern allowlist cannot tell `run --mode plan` (safe) from `run --mode execute` (side effects); at most, `status`, `presets`, and `--dry-run` are safe to auto-approve.
481
+ - **Do not allowlist / auto-approve `hermes-action run`, `hermes_run`, or
482
+ `hermes_approve`**. A command-pattern allowlist cannot distinguish a safe
483
+ plan from an execution with side effects.
359
484
  - Do not put secrets in `.hermes-action.yaml`. Keep provider credentials in Hermes Agent, your OS keychain, or the platform's secure store.
360
485
  - Treat `--yolo` as a trusted-local escape hatch, not a default.
361
486
 
@@ -1,3 +1,17 @@
1
1
  import type { AdapterResult, BridgeConfig, EffectiveRun } from "../types.js";
2
2
  export declare function buildHermesCliArgs(run: EffectiveRun, prompt?: string, extraToolsets?: string[]): string[];
3
+ /** A started Hermes process. `cancel` is idempotent and asks the child to stop gracefully before forcing it. */
4
+ export interface HermesCliExecution {
5
+ result: Promise<AdapterResult>;
6
+ cancel: () => boolean;
7
+ }
8
+ export interface HermesCliStartOptions {
9
+ maxOutputBytes?: number | undefined;
10
+ }
11
+ /**
12
+ * Start a Hermes CLI invocation without awaiting it. Consumers that need asynchronous work can retain
13
+ * the cancellation handle; `runHermesCli` below remains the backward-compatible awaitable wrapper.
14
+ */
15
+ export declare function startHermesCli(config: BridgeConfig, run: EffectiveRun, dryRun: boolean, options?: HermesCliStartOptions): HermesCliExecution;
16
+ /** Backward-compatible synchronous facade for CLI callers. */
3
17
  export declare function runHermesCli(config: BridgeConfig, run: EffectiveRun, dryRun: boolean): Promise<AdapterResult>;
@@ -38,7 +38,15 @@ function filePointerPrompt(path) {
38
38
  "Never reveal secrets.",
39
39
  ].join("\n");
40
40
  }
41
- export async function runHermesCli(config, run, dryRun) {
41
+ /**
42
+ * Start a Hermes CLI invocation without awaiting it. Consumers that need asynchronous work can retain
43
+ * the cancellation handle; `runHermesCli` below remains the backward-compatible awaitable wrapper.
44
+ */
45
+ export function startHermesCli(config, run, dryRun, options = {}) {
46
+ const maxOutputBytes = options.maxOutputBytes;
47
+ if (maxOutputBytes !== undefined && (!Number.isSafeInteger(maxOutputBytes) || maxOutputBytes <= 0)) {
48
+ throw new Error("maxOutputBytes must be a positive safe integer");
49
+ }
42
50
  const prompt = buildHermesPrompt(run);
43
51
  const promptBytes = Buffer.byteLength(prompt, "utf8");
44
52
  const isLarge = promptBytes > largePromptThresholdBytes;
@@ -46,7 +54,20 @@ export async function runHermesCli(config, run, dryRun) {
46
54
  const args = isLarge ? buildHermesCliArgs(run, filePointerPrompt("<temp-file>"), ["file"]) : buildHermesCliArgs(run, prompt);
47
55
  const command = [config.runtime.command, ...args];
48
56
  const stdout = JSON.stringify({ command, prompt, promptBytes, promptChars: prompt.length, delivery: isLarge ? "temp-file" : "argv" }, null, 2);
49
- return { ok: true, exitCode: 0, stdout, stderr: "", command, prompt, dryRun: true };
57
+ const captured = captureChunk(stdout, 0, maxOutputBytes);
58
+ return {
59
+ result: Promise.resolve({
60
+ ok: true,
61
+ exitCode: 0,
62
+ stdout: captured.value,
63
+ stderr: "",
64
+ command,
65
+ prompt,
66
+ dryRun: true,
67
+ outputTruncated: captured.truncated,
68
+ }),
69
+ cancel: () => false,
70
+ };
50
71
  }
51
72
  // Large envelopes can't ride argv (E2BIG); write them to a secure temp file the child reads itself.
52
73
  // tmpDir is set only after a successful write so a failed write cleans up and never leaks an empty dir.
@@ -69,22 +90,27 @@ export async function runHermesCli(config, run, dryRun) {
69
90
  }
70
91
  })();
71
92
  const command = [config.runtime.command, ...args];
72
- return new Promise((resolve) => {
73
- const child = spawn(config.runtime.command, args, { stdio: ["ignore", "pipe", "pipe"] });
93
+ let cancelRequested = false;
94
+ let settled = false;
95
+ let killTimer;
96
+ let child;
97
+ const result = new Promise((resolve) => {
98
+ const spawned = spawn(config.runtime.command, args, { stdio: ["ignore", "pipe", "pipe"] });
99
+ child = spawned;
74
100
  let stdout = "";
75
101
  let stderr = "";
102
+ let capturedBytes = 0;
103
+ let outputTruncated = false;
76
104
  let timedOut = false;
77
- let killTimer;
78
105
  const timeoutMs = Math.min(run.timeoutSeconds * 1000, maxTimeoutMs);
79
106
  const timer = setTimeout(() => {
80
107
  timedOut = true;
81
- child.kill("SIGTERM");
82
- killTimer = setTimeout(() => child.kill("SIGKILL"), killGraceMs);
108
+ child?.kill("SIGTERM");
109
+ killTimer = setTimeout(() => child?.kill("SIGKILL"), killGraceMs);
83
110
  }, timeoutMs);
84
111
  // 'error' and 'close' can both fire (e.g. spawn ENOENT); guard so cleanup and resolve run exactly once.
85
112
  // The temp file is removed here on every settle path. Only an uncatchable SIGKILL of the bridge itself
86
113
  // would leave it behind; mkdtemp + 0600 + the OS temp reaper bound that residual risk.
87
- let settled = false;
88
114
  const settle = (result) => {
89
115
  if (settled)
90
116
  return;
@@ -102,26 +128,61 @@ export async function runHermesCli(config, run, dryRun) {
102
128
  }
103
129
  resolve(result);
104
130
  };
105
- child.stdout.setEncoding("utf8");
106
- child.stderr.setEncoding("utf8");
107
- child.stdout.on("data", (chunk) => {
108
- stdout += chunk;
131
+ spawned.stdout.setEncoding("utf8");
132
+ spawned.stderr.setEncoding("utf8");
133
+ spawned.stdout.on("data", (chunk) => {
134
+ const captured = captureChunk(chunk, capturedBytes, maxOutputBytes);
135
+ stdout += captured.value;
136
+ capturedBytes += captured.bytes;
137
+ outputTruncated ||= captured.truncated;
109
138
  });
110
- child.stderr.on("data", (chunk) => {
111
- stderr += chunk;
139
+ spawned.stderr.on("data", (chunk) => {
140
+ const captured = captureChunk(chunk, capturedBytes, maxOutputBytes);
141
+ stderr += captured.value;
142
+ capturedBytes += captured.bytes;
143
+ outputTruncated ||= captured.truncated;
112
144
  });
113
- child.on("error", (error) => {
114
- settle({ ok: false, exitCode: 127, stdout, stderr: `${stderr}${error.message}`, command, prompt, dryRun: false });
145
+ spawned.on("error", (error) => {
146
+ settle({ ok: false, exitCode: 127, stdout, stderr: `${stderr}${error.message}`, command, prompt, dryRun: false, outputTruncated });
115
147
  });
116
- child.on("close", (code) => {
148
+ spawned.on("close", (code) => {
117
149
  if (timedOut) {
118
150
  const message = `Hermes timed out after ${run.timeoutSeconds}s and was terminated.`;
119
- settle({ ok: false, exitCode: code ?? 124, stdout, stderr: stderr ? `${stderr}\n${message}` : message, command, prompt, dryRun: false, timedOut: true });
151
+ settle({ ok: false, exitCode: code ?? 124, stdout, stderr: stderr ? `${stderr}\n${message}` : message, command, prompt, dryRun: false, outputTruncated, timedOut: true });
120
152
  return;
121
153
  }
122
154
  const exitCode = code ?? 1;
123
- settle({ ok: exitCode === 0, exitCode, stdout, stderr, command, prompt, dryRun: false });
155
+ settle({ ok: exitCode === 0, exitCode, stdout, stderr, command, prompt, dryRun: false, outputTruncated });
124
156
  });
125
157
  });
158
+ const cancel = () => {
159
+ if (settled || cancelRequested || !child)
160
+ return false;
161
+ if (!child.kill("SIGTERM"))
162
+ return false;
163
+ cancelRequested = true;
164
+ killTimer = setTimeout(() => child?.kill("SIGKILL"), killGraceMs);
165
+ return true;
166
+ };
167
+ return { result, cancel };
168
+ }
169
+ /** Backward-compatible synchronous facade for CLI callers. */
170
+ export async function runHermesCli(config, run, dryRun) {
171
+ return startHermesCli(config, run, dryRun).result;
172
+ }
173
+ function captureChunk(chunk, capturedBytes, maxOutputBytes) {
174
+ if (maxOutputBytes === undefined)
175
+ return { value: chunk, bytes: Buffer.byteLength(chunk, "utf8"), truncated: false };
176
+ const remaining = Math.max(0, maxOutputBytes - capturedBytes);
177
+ if (remaining === 0)
178
+ return { value: "", bytes: 0, truncated: chunk.length > 0 };
179
+ const buffer = Buffer.from(chunk, "utf8");
180
+ if (buffer.length <= remaining)
181
+ return { value: chunk, bytes: buffer.length, truncated: false };
182
+ let end = remaining;
183
+ while (end > 0 && (buffer[end] ?? 0) >= 0x80 && (buffer[end] ?? 0) < 0xc0)
184
+ end -= 1;
185
+ const value = buffer.subarray(0, end).toString("utf8");
186
+ return { value, bytes: Buffer.byteLength(value, "utf8"), truncated: true };
126
187
  }
127
188
  //# sourceMappingURL=hermes-cli.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hermes-cli.js","sourceRoot":"","sources":["../../src/adapters/hermes-cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hermes-cli.js","sourceRoot":"","sources":["../../src/adapters/hermes-cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { EffectiveRun } from "./types.js";
2
+ export type ApprovalStatus = "awaiting_approval" | "approved" | "rejected" | "expired";
3
+ export interface ApprovalRequest {
4
+ id: string;
5
+ status: ApprovalStatus;
6
+ createdAt: string;
7
+ expiresAt: string;
8
+ requestedMode: EffectiveRun["requestedMode"];
9
+ effectiveMode: EffectiveRun["mode"];
10
+ presetName: string;
11
+ detectedRisks: EffectiveRun["detectedRisks"];
12
+ }
13
+ export interface ConsumedApproval {
14
+ approval: ApprovalRequest;
15
+ run: EffectiveRun;
16
+ }
17
+ export interface ApprovalStoreOptions {
18
+ ttlMs?: number | undefined;
19
+ maxPending?: number | undefined;
20
+ now?: () => Date;
21
+ createId?: () => string;
22
+ onExpired?: ((approval: ApprovalRequest) => void) | undefined;
23
+ }
24
+ export interface ApprovalStore {
25
+ prepare(run: EffectiveRun): ApprovalRequest;
26
+ get(approvalId: string): ApprovalRequest | undefined;
27
+ approve(approvalId: string): ConsumedApproval | undefined;
28
+ reject(approvalId: string): ApprovalRequest | undefined;
29
+ cleanup(): number;
30
+ }
31
+ /**
32
+ * One-shot, in-process approvals. A stored run is returned exactly once by `approve`; callers must
33
+ * execute that unchanged run rather than accepting new execution options at approval time.
34
+ */
35
+ export declare function createApprovalStore(options?: ApprovalStoreOptions): ApprovalStore;
@@ -0,0 +1,96 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { positiveInteger } from "./validation.js";
3
+ const defaultTtlMs = 15 * 60 * 1000;
4
+ const defaultMaxPending = 32;
5
+ /**
6
+ * One-shot, in-process approvals. A stored run is returned exactly once by `approve`; callers must
7
+ * execute that unchanged run rather than accepting new execution options at approval time.
8
+ */
9
+ export function createApprovalStore(options = {}) {
10
+ const ttlMs = positiveInteger(options.ttlMs, defaultTtlMs, "ttlMs");
11
+ const maxPending = positiveInteger(options.maxPending, defaultMaxPending, "maxPending");
12
+ const now = options.now ?? (() => new Date());
13
+ const createId = options.createId ?? randomUUID;
14
+ const onExpired = options.onExpired;
15
+ const entries = new Map();
16
+ const expire = (entry, current) => {
17
+ if (entry.approval.status === "awaiting_approval" && Date.parse(entry.approval.expiresAt) <= current.getTime()) {
18
+ entry.approval.status = "expired";
19
+ }
20
+ };
21
+ const cleanupExpired = () => {
22
+ const current = now();
23
+ let removed = 0;
24
+ for (const [id, entry] of entries) {
25
+ expire(entry, current);
26
+ if (Date.parse(entry.approval.expiresAt) > current.getTime())
27
+ continue;
28
+ if (entry.approval.status === "expired") {
29
+ try {
30
+ onExpired?.(structuredClone(entry.approval));
31
+ }
32
+ catch {
33
+ // Expiration cleanup must not crash a long-lived bridge when best-effort telemetry fails.
34
+ }
35
+ }
36
+ entries.delete(id);
37
+ removed += 1;
38
+ }
39
+ return removed;
40
+ };
41
+ return {
42
+ prepare(run) {
43
+ cleanupExpired();
44
+ if (entries.size >= maxPending)
45
+ throw new Error(`Hermes approval store is full (${maxPending} pending)`);
46
+ const created = now();
47
+ const id = createId();
48
+ if (entries.has(id))
49
+ throw new Error("Approval ID collision");
50
+ const approval = {
51
+ id,
52
+ status: "awaiting_approval",
53
+ createdAt: created.toISOString(),
54
+ expiresAt: new Date(created.getTime() + ttlMs).toISOString(),
55
+ requestedMode: run.requestedMode,
56
+ effectiveMode: run.mode,
57
+ presetName: run.presetName,
58
+ detectedRisks: [...run.detectedRisks],
59
+ };
60
+ entries.set(id, { approval, run: structuredClone(run) });
61
+ return structuredClone(approval);
62
+ },
63
+ get(approvalId) {
64
+ const entry = entries.get(approvalId);
65
+ if (!entry)
66
+ return undefined;
67
+ expire(entry, now());
68
+ return structuredClone(entry.approval);
69
+ },
70
+ approve(approvalId) {
71
+ const entry = entries.get(approvalId);
72
+ if (!entry)
73
+ return undefined;
74
+ expire(entry, now());
75
+ if (entry.approval.status !== "awaiting_approval")
76
+ return undefined;
77
+ entry.approval.status = "approved";
78
+ const consumed = { approval: structuredClone(entry.approval), run: structuredClone(entry.run) };
79
+ entries.delete(approvalId);
80
+ return consumed;
81
+ },
82
+ reject(approvalId) {
83
+ const entry = entries.get(approvalId);
84
+ if (!entry)
85
+ return undefined;
86
+ expire(entry, now());
87
+ if (entry.approval.status === "awaiting_approval")
88
+ entry.approval.status = "rejected";
89
+ const rejected = structuredClone(entry.approval);
90
+ entries.delete(approvalId);
91
+ return rejected;
92
+ },
93
+ cleanup: cleanupExpired,
94
+ };
95
+ }
96
+ //# sourceMappingURL=approvals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approvals.js","sourceRoot":"","sources":["../src/approvals.ts"],"names":[],"mappings":"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"}