@prohost/cli 0.1.2 → 0.2.0

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/CHANGELOG.md CHANGED
@@ -4,6 +4,65 @@ Versions follow [semver](https://semver.org/). Publishing is automated: merging
4
4
  a version bump to `main` triggers `.github/workflows/npm-publish-cli.yml`, which
5
5
  builds via `prepack`, runs the suite, publishes, and tags `cli-v<version>`.
6
6
 
7
+ ## 0.2.0
8
+
9
+ The harness stopped being a one-shot prompt pipe. 0.1.x answered every mention
10
+ from a single 500-character excerpt, in whatever directory you happened to be
11
+ standing in, with no memory of the previous message and no way to look anything
12
+ up — and told the agent about an MCP endpoint it had no client for, which
13
+ produced replies confidently describing lookups that never happened.
14
+
15
+ - **The agent gets the thread.** `agent.run_requested` now carries
16
+ `recent_messages` and `memories` when the server sends them, rendered as
17
+ delimited "Conversation so far" and "Things you remember" blocks ahead of the
18
+ triggering message. Both fields are optional in both directions: an older
19
+ server behaves exactly as before, and a newer one can add or move fields
20
+ without breaking a CLI that is already on someone's laptop.
21
+ - **Claude Code runs keep their session.** When `--exec` is `claude` in print
22
+ mode, the harness runs it with `--output-format json`, reads the reply and
23
+ `session_id` out of the result, and `--resume`s that session for the next run
24
+ on the same conversation (ledger: `~/.prohost/sessions.json`). A session that
25
+ has expired is retried once without `--resume` rather than failing the surface
26
+ forever. Any other `--exec` command keeps the exact stdin/stdout behaviour it
27
+ had before.
28
+ - **Claude Code runs can actually act.** Runs are invoked with
29
+ `--dangerously-skip-permissions`: in print mode nobody is at a terminal to
30
+ approve a tool call, so a gated brain silently declines the work it was hired
31
+ for. `--safe-tools` (alias `--no-skip-permissions`) opts back into gating. The
32
+ README now states the trust model plainly — the agent runs as you, unsandboxed,
33
+ on instructions written by workspace teammates — and the harness says so on
34
+ every start-up. Codex users should pass `codex --yolo` in their `--exec`.
35
+ - **Reply bodies carry the server's extra fields.** `reply_instructions` may now
36
+ include `body_extra`, merged into the reply POST alongside the body field —
37
+ first use is `reply_to_message_id`, so a team-chat reply lands in the right
38
+ sub-thread. Merged without being interpreted, so the server can extend the
39
+ reply body without a CLI release; the body field always wins a key collision,
40
+ so nothing can displace the reply text.
41
+ - **Claude Code runs get ProhostAI tools.** The paired MCP endpoint is written
42
+ to a `0600` `~/.prohost/mcp.json` and passed as `--mcp-config` with
43
+ `--strict-mcp-config` (so the run never picks up the operator's own MCP
44
+ servers) and `--allowedTools`, since nothing can be approved interactively in
45
+ print mode. Tools the run asked for and was refused are now printed.
46
+ - **The prompt is honest about the run.** It states that this is a single
47
+ non-interactive turn, that tools needing approval are unavailable, and that
48
+ the agent should name what it could not do instead of narrating a lookup. The
49
+ ProhostAI tools are described only when they are actually wired in, and no
50
+ workspace domain is named — a paired credential's scopes don't reach every
51
+ tool (reservations and listings are excluded today), so the prompt says the
52
+ credential is narrow and that a permission error is a real limit.
53
+ - **A dedicated workspace.** `agent run` gains `--workdir`, defaulting to
54
+ `$PROHOST_HOME/workspace` instead of the current directory. Agents read
55
+ `CLAUDE.md`, `.claude/settings.json` and `.mcp.json` from their working
56
+ directory; started from a code checkout they inherit that repo's instructions
57
+ and permission denials. `--cwd` still works as an alias.
58
+ - **`prohost agent install-daemon` / `uninstall-daemon`.** Generates and loads a
59
+ `KeepAlive` launchd agent on macOS so the harness survives logout and reboot,
60
+ logging to `$PROHOST_HOME/agent.log`. The plist carries your `PATH` forward,
61
+ since launchd's default one contains no `claude`, and the daemon is scoped to
62
+ `$PROHOST_HOME` — installing twice from one home replaces the daemon instead
63
+ of adding a second one running the same credential. On Linux it prints a
64
+ systemd-user unit to install by hand.
65
+
7
66
  ## 0.1.2
8
67
 
9
68
  The release that carries 0.1.1's fix to users. 0.1.1 was tagged in the monorepo
package/README.md CHANGED
@@ -27,6 +27,9 @@ npx @prohost/cli agent pair --code <pairing-code>
27
27
 
28
28
  # 2. Point it at whatever agent you already use.
29
29
  npx @prohost/cli agent run --exec 'claude -p'
30
+
31
+ # 3. Optional, but you probably want it: keep it running across reboots.
32
+ npx @prohost/cli agent install-daemon --exec 'claude -p'
30
33
  ```
31
34
 
32
35
  That's it — @-mention the agent in ProhostAI and it answers from your machine.
@@ -84,7 +87,7 @@ in a team thread, a 1:1 DM, a task comment, a trigger — you'll see:
84
87
  Other agents, same command:
85
88
 
86
89
  ```bash
87
- npx @prohost/cli agent run --exec 'codex exec -'
90
+ npx @prohost/cli agent run --exec 'codex exec - --yolo'
88
91
  npx @prohost/cli agent run --exec 'goose run -i -'
89
92
  npx @prohost/cli agent run --exec './my-agent.sh'
90
93
  ```
@@ -106,13 +109,74 @@ nothing is posted and the run is left open.
106
109
  2. The CLI verifies the event's HMAC signature. An event that doesn't verify is
107
110
  **not executed** — the whole point of the signature is that a run event makes
108
111
  your machine execute a command.
109
- 3. It builds a prompt (who your agent is, the conversation, the message that
110
- mentioned it) and runs your `--exec` command with that prompt on **stdin**.
112
+ 3. It builds a prompt — who your agent is, what it's replying to, the recent
113
+ messages in the thread, anything the account remembers that bears on it, and
114
+ the message that mentioned it — and runs your `--exec` command with that
115
+ prompt on **stdin**.
111
116
  4. Your command's **stdout** is the reply. It's posted into the conversation
112
117
  verbatim, so print the message and nothing else — no preamble, no reasoning.
118
+ (Claude Code is the exception: see below.)
113
119
  5. The run is completed either way. A command that crashes, times out, or prints
114
120
  nothing reports `failed`; a run is never left hanging.
115
121
 
122
+ ### Where your agent runs
123
+
124
+ By default the command runs in `~/.prohost/workspace`, which the CLI creates —
125
+ **not** the directory you started `agent run` from. That matters more than it
126
+ sounds like it does: coding agents read their working directory as
127
+ configuration. Start one inside a code checkout and it picks up that repo's
128
+ `CLAUDE.md`, its `.claude/settings.json` permission list, and its `.mcp.json`
129
+ servers — so your agent answers a customer's question carrying instructions
130
+ about a codebase, and quietly refuses tools that repo happens to deny.
131
+
132
+ Point `--workdir` somewhere else if you want the agent to work in a particular
133
+ directory. Just choose it deliberately.
134
+
135
+ ### If your agent is Claude Code
136
+
137
+ When `--exec` starts with `claude` and asks for print mode (`-p` / `--print`),
138
+ the CLI does three extra things. Every other command — Codex, goose, your own
139
+ script — is left exactly alone.
140
+
141
+ **It keeps the conversation.** Each run is a fresh process, so without this your
142
+ agent meets every message as its first: someone answers its clarifying question
143
+ and gets a reply that has forgotten asking. The CLI reads the `session_id` out
144
+ of Claude Code's JSON output, records it per conversation in
145
+ `~/.prohost/sessions.json`, and `--resume`s it next time. Sessions don't live
146
+ forever; when one is gone the run is retried once from scratch rather than
147
+ failing.
148
+
149
+ **It lets the agent actually do things.** Runs are invoked with
150
+ `--dangerously-skip-permissions`, so tool calls execute without stopping to ask.
151
+ This is deliberate: in print mode there is nobody at a terminal to approve
152
+ anything, so a permission-gated brain silently declines to do the work you hired
153
+ it for. **Read the trust model below before you leave this running.** Pass
154
+ `--safe-tools` (or `--no-skip-permissions`) to gate tools instead — expect the
155
+ agent to answer rather than act.
156
+
157
+ **It gives the agent your ProhostAI tools.** The MCP endpoint from your pairing
158
+ is written to `~/.prohost/mcp.json` (`chmod 600` — it holds your API key) and
159
+ passed with `--mcp-config`, so the agent can look things up instead of guessing.
160
+ `--strict-mcp-config` is passed too: the run gets *only* this server, never the
161
+ MCP servers you use for your own work.
162
+
163
+ A pairing is deliberately narrow, so not every tool will work: today's default
164
+ scopes cover conversations, tasks, cleanings and approvals, but **not**
165
+ reservations or listings — those tools return a permission error. The prompt
166
+ tells the agent to say what it couldn't reach rather than answer as if it had,
167
+ and refused tools are printed in the run log.
168
+
169
+ **It reads the reply out of JSON.** The run is invoked with
170
+ `--output-format json`, and the `result` field is what gets posted. If that
171
+ output isn't JSON — a login prompt, a crash banner — the run is reported failed
172
+ rather than pasting it into a customer's conversation.
173
+
174
+ Detection is deliberately narrow: it matches only when `claude` is the first
175
+ word of the command *and* print mode is asked for. Anything else — `npx claude
176
+ -p`, `env FOO=1 claude -p`, a wrapper script, or any command containing a pipe,
177
+ redirect, or `&&` — keeps the plain stdin/stdout contract untouched. So
178
+ `--exec 'sh -c "claude -p"'` is the way to opt out.
179
+
116
180
  Runs are handled one at a time, and each run executes **at most once on this
117
181
  machine** — even across restarts. Started runs are recorded in
118
182
  `~/.prohost/runs.json` before the command launches, so a webhook redelivery
@@ -121,12 +185,35 @@ your agent a second time. That matters because your agent holds a credential:
121
185
  a second execution could file the same task or send the same email again, which
122
186
  no amount of message deduplication would undo.
123
187
 
124
- **Treat mention text as untrusted input.** The prompt contains a message someone
125
- else wrote, and your agent runs on your machine with whatever credentials and
126
- tools you gave it. Give it the access the job needs and no more — the same
127
- judgement you'd apply to any agent reading input from the internet. (The message
128
- only ever reaches your command on stdin, never the shell, so it cannot inject a
129
- command.)
188
+ ### The trust model — read this once
189
+
190
+ `agent run` is a standing instruction to execute a command on your machine when
191
+ someone in your ProhostAI workspace asks. Be clear-eyed about what that means:
192
+
193
+ - **The agent runs as you.** Same user, same filesystem, same SSH keys, same
194
+ logged-in CLIs. It is not sandboxed.
195
+ - **By default its tools are not gated.** With `--dangerously-skip-permissions`
196
+ (the default for Claude Code) nothing pauses for confirmation. That is what
197
+ makes it useful and it is also the whole risk.
198
+ - **The instructions come from other people.** The prompt carries a message a
199
+ teammate wrote, plus thread history. Treat all of it as untrusted input — the
200
+ same judgement you'd apply to any agent reading text from the internet. A
201
+ colleague's compromised account, or a guest message quoted into a thread, is
202
+ an instruction channel into your laptop.
203
+
204
+ What the harness does to bound this: the message reaches your command on
205
+ **stdin only**, never the shell, so it cannot inject a command; runs execute at
206
+ most once each; every event's signature is verified before anything runs; the
207
+ agent works in `~/.prohost/workspace` rather than your code; and
208
+ `--strict-mcp-config` keeps your own MCP servers out of the run.
209
+
210
+ What it does **not** do: sandbox the process. If that matters for your machine,
211
+ run `agent run` inside a container or a dedicated user account, or use
212
+ `--safe-tools` and accept that the agent will answer rather than act.
213
+
214
+ Other agents: the CLI shapes no command but Claude Code's, so give yours its own
215
+ equivalent flag — `codex --yolo` for Codex CLI, whatever your runner calls it —
216
+ and apply the same judgement.
130
217
 
131
218
  ### `agent run` flags
132
219
 
@@ -135,10 +222,51 @@ command.)
135
222
  | `--exec` | *(required)* | Command to run for each requested run. Prompt arrives on stdin. |
136
223
  | `--timeout` | `300` | Seconds one run may take before the command is killed. |
137
224
  | `--dry-run` | off | Print the reply that would be posted; write nothing. |
138
- | `--cwd` | current dir | Working directory for the command. |
225
+ | `--workdir` | `~/.prohost/workspace` | Working directory for the command. See "Where your agent runs". |
226
+ | `--cwd` | — | Deprecated alias for `--workdir`. |
139
227
  | `--machine` | OS hostname | Name shown next to the connection in ProhostAI. |
140
228
  | `--url` | paired value | Websocket endpoint override. Pairing stores the URL the server reports, so you should not normally need this. |
141
229
  | `--allow-unverified` | off | Run events whose signature can't be verified. See below. |
230
+ | `--safe-tools` | off | Gate the agent's tools behind permission prompts instead of bypassing them. Claude Code only. Alias: `--no-skip-permissions`. |
231
+
232
+ ### Keeping it running
233
+
234
+ `agent run` in a terminal window stops being an agent the moment you close the
235
+ window or the laptop reboots — and nothing tells you it stopped. Someone
236
+ @-mentions it and the run times out server-side.
237
+
238
+ ```bash
239
+ npx @prohost/cli agent install-daemon --exec 'claude -p'
240
+ npx @prohost/cli agent uninstall-daemon
241
+ ```
242
+
243
+ On macOS this writes a launchd agent to
244
+ `~/Library/LaunchAgents/ai.prohost.agent.<id>.plist` and loads it, with
245
+ `KeepAlive` on so it starts at login and restarts if it exits. Output goes to
246
+ `$PROHOST_HOME/agent.log` — the first place to look when the agent is quiet.
247
+ Re-running it with a different `--exec` replaces the old daemon.
248
+
249
+ The daemon is scoped to `$PROHOST_HOME`, because that's where the one credential
250
+ lives: installing twice from the same home replaces the daemon rather than
251
+ adding a second one. To run two agents on one machine, give each its own
252
+ `$PROHOST_HOME` — separate credentials, separate ledgers, separate daemons.
253
+
254
+ **The daemon does not inherit your shell.** It gets `PATH` and `PROHOST_HOME`
255
+ and nothing else, because a supervised process starts from a bare environment.
256
+ If your `--exec` command needs anything more — an API key, a proxy setting —
257
+ point it at a wrapper script that sets those itself. Copying them into the plist
258
+ would put your secrets in a file on disk that nothing else protects.
259
+
260
+ It also records the `PATH` you had when you ran `install-daemon`. launchd starts
261
+ jobs with a bare `PATH` that contains no `claude`, so without this the daemon
262
+ installs, reports success, and then quietly fails every run with
263
+ `command not found`. If you later move your agent binary, re-run
264
+ `install-daemon`.
265
+
266
+ `install-daemon` takes the same flags as `agent run` and passes them through. On
267
+ Linux it prints a systemd-user unit for you to install by hand, because making
268
+ it survive logout needs `loginctl enable-linger`, which isn't this CLI's
269
+ decision to make.
142
270
 
143
271
  ### `agent pair` flags
144
272
 
@@ -169,10 +297,20 @@ has real credentials.
169
297
  | `auth_error: stream_subscription_unavailable` | The connection has no event subscription to stream. Re-pair. |
170
298
  | `signature did not verify` | The event didn't come from ProhostAI, or your stored secret is stale. Re-pair. |
171
299
  | Replies contain your agent's reasoning | Your command is printing more than the message. Use a "print only the answer" flag (e.g. `claude -p`). |
300
+ | `claude did not return JSON` | Claude Code printed something else — usually a login prompt. Run `claude -p hello` yourself and fix what it says. |
301
+ | `tools the agent asked for and was refused` | The run wanted a tool nothing could approve (nobody is at a terminal). Expected for most built-ins; if it names a `mcp__prohost__…` tool, re-pair. |
302
+ | The agent answers as if it were working on some repo | It's running in a code checkout and inherited that repo's `CLAUDE.md`. Drop `--workdir`, or point it somewhere neutral. |
303
+
304
+ Everything the CLI stores lives under `~/.prohost` (`$PROHOST_HOME` moves it):
172
305
 
173
- Credentials live in `~/.prohost/agent.json` and the run ledger in
174
- `~/.prohost/runs.json`; set `$PROHOST_HOME` to move that directory. Delete the file to un-pair locally, and revoke the connection in
175
- ProhostAI to invalidate the key itself.
306
+ | File | What it is |
307
+ | --- | --- |
308
+ | `agent.json` | Credentials. Delete to un-pair locally; revoke in ProhostAI to invalidate the key itself. |
309
+ | `runs.json` | Runs already executed on this machine, so a redelivery can't run your agent twice. |
310
+ | `sessions.json` | Conversation → Claude Code session, for continuity. Safe to delete; costs the agent its memory of open threads. |
311
+ | `mcp.json` | Generated MCP config. Holds your API key — `chmod 600`. |
312
+ | `workspace/` | Where your agent command runs by default. |
313
+ | `agent.log` | Daemon output. |
176
314
 
177
315
  ### What the pairing can and can't do
178
316
 
@@ -27,8 +27,13 @@ export interface ApiResult {
27
27
  * Keyed on ``run_id`` so the send is idempotent server-side. The harness dedupes
28
28
  * runs in memory, but that memory dies with the process: without this header a
29
29
  * redelivered run after a restart would post the reply a second time.
30
+ *
31
+ * ``extra`` carries the event's ``reply_instructions.body_extra`` verbatim —
32
+ * threading fields and whatever the server adds later. It is merged, not
33
+ * interpreted: the point is that the server can extend the reply body without
34
+ * a CLI release.
30
35
  */
31
- export declare function postReply(options: ApiOptions, path: string, bodyKey: string, message: string, runId: string): Promise<ApiResult>;
36
+ export declare function postReply(options: ApiOptions, path: string, bodyKey: string, message: string, runId: string, extra?: Record<string, unknown>): Promise<ApiResult>;
32
37
  /**
33
38
  * Report a run's terminal state. Called for every run — including one whose
34
39
  * command crashed — so the server never waits on a run this harness abandoned.
package/dist/agent/api.js CHANGED
@@ -57,9 +57,17 @@ async function post(options, path, body, idempotencyKey) {
57
57
  * Keyed on ``run_id`` so the send is idempotent server-side. The harness dedupes
58
58
  * runs in memory, but that memory dies with the process: without this header a
59
59
  * redelivered run after a restart would post the reply a second time.
60
+ *
61
+ * ``extra`` carries the event's ``reply_instructions.body_extra`` verbatim —
62
+ * threading fields and whatever the server adds later. It is merged, not
63
+ * interpreted: the point is that the server can extend the reply body without
64
+ * a CLI release.
60
65
  */
61
- export async function postReply(options, path, bodyKey, message, runId) {
62
- return post(options, path, { [bodyKey]: message }, `prohost-cli-run-${runId}`);
66
+ export async function postReply(options, path, bodyKey, message, runId, extra) {
67
+ // `extra` first, so the message can never be clobbered by a `body_extra` key
68
+ // that happens to collide with the body field. The reply text is the one
69
+ // thing in this request that must survive.
70
+ return post(options, path, { ...extra, [bodyKey]: message }, `prohost-cli-run-${runId}`);
63
71
  }
64
72
  /**
65
73
  * Report a run's terminal state. Called for every run — including one whose
@@ -0,0 +1,161 @@
1
+ /**
2
+ * The one place that knows anything about Claude Code specifically.
3
+ *
4
+ * `exec.ts` is deliberately agent-agnostic — prompt on stdin, reply on stdout,
5
+ * the lowest common denominator every CLI agent already speaks — and it stays
6
+ * that way. But that contract costs two things Claude Code can actually
7
+ * provide: a run has no memory of the previous one, and it has no tools for
8
+ * looking anything up in the workspace it is answering about. Both are fixed
9
+ * with flags, and flags are not portable. So the knowledge lives here, behind a
10
+ * detection function, and every other exec command keeps the exact behaviour it
11
+ * had before this module existed.
12
+ *
13
+ * Nothing in here is load-bearing for correctness: if detection misses, if the
14
+ * session is unresumable, if the JSON is not what we expect — the run still
15
+ * happens and still completes. These are enhancements that must fail soft.
16
+ */
17
+ /**
18
+ * Name the ProhostAI MCP server is registered under, which is also what makes
19
+ * its tools appear as `mcp__prohost__<tool>`. Referenced in the prompt, in the
20
+ * `--allowedTools` pattern, and in the config file — keep them in sync from
21
+ * here rather than by hand.
22
+ */
23
+ export declare const MCP_SERVER_NAME = "prohost";
24
+ /**
25
+ * What goes to `--allowedTools` so the ProhostAI tools are callable.
26
+ *
27
+ * Two spellings, comma-joined. `mcp__<server>` is the documented way to approve
28
+ * a whole MCP server and is the one expected to do the work; `mcp__<server>__*`
29
+ * is a pattern people write by analogy with `Bash(git *)`. Sending both costs
30
+ * a few bytes and removes an entire class of silent failure: an unapproved tool
31
+ * in print mode is not an error the agent reports, it is a tool the agent
32
+ * quietly never calls — and the visible symptom is an answer that sounds like
33
+ * the model just didn't feel like looking anything up.
34
+ *
35
+ * Comma-joined into ONE argument rather than passed as two, because
36
+ * `--allowedTools` is variadic (`<tools...>`) and would otherwise keep eating
37
+ * whatever followed it on the command line.
38
+ */
39
+ export declare const MCP_ALLOWED_TOOLS = "mcp__prohost,mcp__prohost__*";
40
+ /** Filename of the generated MCP config, inside `$PROHOST_HOME`. */
41
+ export declare const MCP_CONFIG_FILENAME = "mcp.json";
42
+ /**
43
+ * Is this exec command Claude Code in print mode?
44
+ *
45
+ * A **heuristic**, and intentionally a narrow one: it matches when the first
46
+ * word of the command is `claude` (or a path ending in `/claude`) *and* the
47
+ * command asks for print mode. Both halves matter. The first because the whole
48
+ * point of `--exec` is that it takes an arbitrary command, and the second
49
+ * because `--output-format` is a print-mode flag — appending it to an
50
+ * interactive invocation would break a command that works today.
51
+ *
52
+ * Things this deliberately does **not** match: `env X=1 claude -p`,
53
+ * `npx claude -p`, a wrapper script that calls Claude Code internally,
54
+ * `sh -c 'claude -p'`. Each of those keeps the plain stdin/stdout behaviour —
55
+ * which is correct output, just without session continuity or MCP tools. The
56
+ * failure direction is what was chosen here: a missed detection costs features,
57
+ * while a false positive would append flags to somebody's shell script and
58
+ * break their agent outright.
59
+ *
60
+ * Nor does it match a command containing shell control operators, even one that
61
+ * starts with `claude`. Flags are appended to the *end* of the string, so for
62
+ * `claude -p | jq -r .result` they would land on `jq` — breaking a command that
63
+ * works today, which is precisely the outcome the narrowness is for.
64
+ */
65
+ export declare function isClaudeExec(command: string): boolean;
66
+ export interface ClaudeFlags {
67
+ /** Session to resume. Omitted for the first turn on a surface. */
68
+ sessionId?: string;
69
+ /** Path to the generated MCP config, when MCP wiring is active. */
70
+ mcpConfigPath?: string;
71
+ /**
72
+ * Run the agent with permission checks bypassed. Default ON — see
73
+ * {@link claudeCommand}. Set false by `agent run --safe-tools`.
74
+ */
75
+ skipPermissions?: boolean;
76
+ /** Quoting target. Defaults to the running platform; tests set it. */
77
+ platform?: NodeJS.Platform;
78
+ }
79
+ /**
80
+ * Append the flags this harness needs to a Claude Code command.
81
+ *
82
+ * `--dangerously-skip-permissions` is on by default, by explicit product
83
+ * decision: a paired brain is hired to *do* things, and in print mode there is
84
+ * nobody to approve a tool call — so without it the agent silently declines to
85
+ * act and the feature is a chat box. The trust model this assumes is stated
86
+ * plainly in the README: the brain runs as the host user with that user's full
87
+ * local permissions, on prompts written by workspace teammates. `--safe-tools`
88
+ * opts out for hosts who want prompt-gated behaviour instead.
89
+ *
90
+ * `--strict-mcp-config` is not optional decoration, and matters *more* under
91
+ * skipped permissions rather than less. Without it Claude Code merges the
92
+ * servers in the operator's own configuration with ours, so a run answering a
93
+ * customer's question would hold whatever MCP servers they use for their day
94
+ * job — with permission checks off. Ours is the only one this run is entitled
95
+ * to.
96
+ */
97
+ export declare function claudeCommand(base: string, flags?: ClaudeFlags): string;
98
+ /** What the harness got back from one Claude Code invocation. */
99
+ export type ClaudeOutput = {
100
+ ok: true;
101
+ text: string;
102
+ sessionId?: string;
103
+ denials: string[];
104
+ } | {
105
+ ok: false;
106
+ error: string;
107
+ sessionId?: string;
108
+ denials: string[];
109
+ };
110
+ /**
111
+ * Read the reply and the session id out of `--output-format json` stdout.
112
+ *
113
+ * Unparseable output is a **failure**, not a fallback to posting the raw text.
114
+ * We asked for JSON; stdout that isn't JSON means something went wrong that we
115
+ * do not understand — a crash banner, a login prompt, an npm warning — and the
116
+ * one thing that must not happen is that being posted verbatim into a customer's
117
+ * conversation. Reporting the run failed is recoverable; sending a stack trace
118
+ * to a guest is not.
119
+ */
120
+ export declare function parseClaudeOutput(stdout: string): ClaudeOutput;
121
+ /**
122
+ * Does this look like the resume having been rejected, rather than the agent
123
+ * having failed at its actual work?
124
+ *
125
+ * Sessions do not live forever: the transcript is on disk and gets cleaned up,
126
+ * and the ledger has no way to know. A stale entry must not turn into a
127
+ * permanently broken surface, so the caller retries once without `--resume`.
128
+ *
129
+ * The observed failure is exit 1, empty stdout, and
130
+ * `No conversation found with session ID: <uuid>` on stderr. Matching is
131
+ * broader than that string because the wording is not ours to depend on, and it
132
+ * is gated by the caller on the run having actually failed — so a *successful*
133
+ * run whose reply text happens to quote a session id is never mistaken for one.
134
+ */
135
+ export declare function looksLikeResumeFailure(output: string, sessionId: string): boolean;
136
+ /** Absolute path of the generated MCP config. */
137
+ export declare function mcpConfigPath(env?: NodeJS.ProcessEnv): string;
138
+ export interface McpConfigOptions {
139
+ /** The MCP endpoint the pairing reported, e.g. `https://connect.prohost.ai/mcp`. */
140
+ mcpUrl: string;
141
+ /** The paired API key. Written to a `0600` file and nowhere else. */
142
+ apiKey: string;
143
+ env?: NodeJS.ProcessEnv;
144
+ }
145
+ /**
146
+ * The config object handed to `--mcp-config`.
147
+ *
148
+ * Split out from the write so a test can assert on the shape without going near
149
+ * the filesystem, and so the header scheme is stated in exactly one place.
150
+ */
151
+ export declare function buildMcpConfig(options: McpConfigOptions): Record<string, unknown>;
152
+ /**
153
+ * Write the MCP config and return its path.
154
+ *
155
+ * The file holds a live API key, so it gets the same treatment as
156
+ * `agent.json`: `0600` inside a `0700` directory, written with the mode set at
157
+ * create time rather than chmod'ed afterwards — and chmod'ed anyway, because
158
+ * `writeFileSync`'s mode is masked by the umask and is ignored outright when
159
+ * the file already exists.
160
+ */
161
+ export declare function writeMcpConfig(options: McpConfigOptions): string;