@prohost/cli 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Prohost AI, Inc.
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,229 @@
1
+ # `@prohost/cli`
2
+
3
+ The [ProhostAI](https://www.prohost.ai) command-line tool. Two things it does:
4
+
5
+ - **`prohost agent`** — run your own local agent as a ProhostAI teammate. Someone
6
+ @-mentions it in the app, it runs on your machine, and its answer is posted
7
+ back into the conversation.
8
+ - **`prohost listen`** — stream your account's webhooks to a local HTTP endpoint
9
+ while you develop against them (like `stripe listen`).
10
+
11
+ You need a ProhostAI account and **Node.js 18 or newer**. Nothing to install —
12
+ `npx` fetches it on demand:
13
+
14
+ ```bash
15
+ npx @prohost/cli --help # all commands and flags
16
+ npx @prohost/cli --version # the version you're running
17
+ ```
18
+
19
+ ## Quick start
20
+
21
+ Pair once, then leave the agent running:
22
+
23
+ ```bash
24
+ # 1. In ProhostAI: Hire an AI employee → Bring your own agent → copy the code.
25
+ # Codes are single-use and expire 15 minutes after they're generated.
26
+ npx @prohost/cli agent pair --code <pairing-code>
27
+
28
+ # 2. Point it at whatever agent you already use.
29
+ npx @prohost/cli agent run --exec 'claude -p'
30
+ ```
31
+
32
+ That's it — @-mention the agent in ProhostAI and it answers from your machine.
33
+ The rest of this page is detail on each step.
34
+
35
+ ---
36
+
37
+ ## Bring your own agent
38
+
39
+ Any agent that reads a prompt on stdin and writes its answer to stdout works —
40
+ Claude Code, Codex CLI, goose, or a shell script you wrote yourself. The CLI
41
+ doesn't know or care which one it is.
42
+
43
+ ### 1. Get a pairing code
44
+
45
+ In ProhostAI: **Hire an AI employee → Bring your own agent**. You'll get a
46
+ one-time code. It is single-use and expires 15 minutes after it's generated.
47
+
48
+ ### 2. Pair
49
+
50
+ ```bash
51
+ npx @prohost/cli agent pair --code <pairing-code>
52
+ ```
53
+
54
+ This exchanges the code for a scoped API key and stores it in
55
+ `~/.prohost/agent.json` (`chmod 600`). The key is never printed in full — only a
56
+ masked prefix — and the file should never be committed or copied between
57
+ machines; re-pair instead.
58
+
59
+ That gives you a **stream-only pairing**: events arrive over the websocket and
60
+ nothing is ever POSTed to you, which is what you want on a laptop with no public
61
+ HTTPS address. Events are still signed, and the CLI still verifies every one.
62
+
63
+ Pass `--webhook-url https://…` only if something else of yours *also* needs the
64
+ events pushed over HTTPS. It changes where events are delivered; it is not
65
+ required to receive runs.
66
+
67
+ You can also pass the code as `$PROHOST_PAIRING_CODE`.
68
+
69
+ ### 3. Run
70
+
71
+ ```bash
72
+ npx @prohost/cli agent run --exec 'claude -p'
73
+ ```
74
+
75
+ Leave it running. On every mention you'll see:
76
+
77
+ ```
78
+ 14:22:07 ● connected as "Ada" — waiting for mentions (exec: claude -p)
79
+ 14:24:31 ▶ run 01JQ8Z4K2M started (conversation 9f3c…)
80
+ 14:24:58 ✓ run 01JQ8Z4K2M replied in 26.4s (412 chars)
81
+ ```
82
+
83
+ Other agents, same command:
84
+
85
+ ```bash
86
+ npx @prohost/cli agent run --exec 'codex exec -'
87
+ npx @prohost/cli agent run --exec 'goose run -i -'
88
+ npx @prohost/cli agent run --exec './my-agent.sh'
89
+ ```
90
+
91
+ Try it without sending anything first:
92
+
93
+ ```bash
94
+ npx @prohost/cli agent run --exec 'claude -p' --dry-run
95
+ ```
96
+
97
+ `--dry-run` prints the reply your agent produced and makes no writes at all —
98
+ nothing is posted and the run is left open.
99
+
100
+ ### How a run works
101
+
102
+ 1. Someone @-mentions your agent. ProhostAI emits an `agent.run_requested` event
103
+ on the authenticated websocket. Your machine shows up in ProhostAI as
104
+ connected while `agent run` holds the socket open.
105
+ 2. The CLI verifies the event's HMAC signature. An event that doesn't verify is
106
+ **not executed** — the whole point of the signature is that a run event makes
107
+ your machine execute a command.
108
+ 3. It builds a prompt (who your agent is, the conversation, the message that
109
+ mentioned it) and runs your `--exec` command with that prompt on **stdin**.
110
+ 4. Your command's **stdout** is the reply. It's posted into the conversation
111
+ verbatim, so print the message and nothing else — no preamble, no reasoning.
112
+ 5. The run is completed either way. A command that crashes, times out, or prints
113
+ nothing reports `failed`; a run is never left hanging.
114
+
115
+ Runs are handled one at a time, and each run executes **at most once on this
116
+ machine** — even across restarts. Started runs are recorded in
117
+ `~/.prohost/runs.json` before the command launches, so a webhook redelivery
118
+ that arrives after a crash or restart will close the run out rather than run
119
+ your agent a second time. That matters because your agent holds a credential:
120
+ a second execution could file the same task or send the same email again, which
121
+ no amount of message deduplication would undo.
122
+
123
+ **Treat mention text as untrusted input.** The prompt contains a message someone
124
+ else wrote, and your agent runs on your machine with whatever credentials and
125
+ tools you gave it. Give it the access the job needs and no more — the same
126
+ judgement you'd apply to any agent reading input from the internet. (The message
127
+ only ever reaches your command on stdin, never the shell, so it cannot inject a
128
+ command.)
129
+
130
+ ### `agent run` flags
131
+
132
+ | Flag | Default | What it does |
133
+ | --- | --- | --- |
134
+ | `--exec` | *(required)* | Command to run for each mention. Prompt arrives on stdin. |
135
+ | `--timeout` | `300` | Seconds one run may take before the command is killed. |
136
+ | `--dry-run` | off | Print the reply that would be posted; write nothing. |
137
+ | `--cwd` | current dir | Working directory for the command. |
138
+ | `--machine` | OS hostname | Name shown next to the connection in ProhostAI. |
139
+ | `--url` | paired value | Websocket endpoint override. |
140
+ | `--allow-unverified` | off | Run events whose signature can't be verified. See below. |
141
+
142
+ ### `agent pair` flags
143
+
144
+ | Flag | Default | What it does |
145
+ | --- | --- | --- |
146
+ | `--code` | `$PROHOST_PAIRING_CODE` | The one-time pairing code. |
147
+ | `--webhook-url` | none | Also push events to this HTTPS endpoint. Omit for stream-only, which is the normal laptop setup. |
148
+ | `--base-url` | `https://connect.prohost.ai` | Public API base. |
149
+
150
+ ### About `--allow-unverified`
151
+
152
+ Every pairing gets a signing secret, including a stream-only one, so
153
+ verification works out of the box. If a pairing somehow has no secret, the CLI
154
+ refuses to execute run events rather than running an unauthenticated command,
155
+ and says so. `--allow-unverified` opts out of that check — reasonable when
156
+ you're pointing `--url` at a local test server, a bad idea anywhere your agent
157
+ has real credentials.
158
+
159
+ ### Troubleshooting
160
+
161
+ | You see | What it means |
162
+ | --- | --- |
163
+ | `No paired agent found` | Run `prohost agent pair --code <code>` first. |
164
+ | `no event subscription was provisioned` | Pairing half-failed server-side and nothing was saved. The code is spent — generate a fresh one and pair again. |
165
+ | `Pairing code is invalid, expired, or already used` | Codes are single-use with a 15-minute life. Generate a fresh one. |
166
+ | `auth_error: invalid_api_key` | The connection was revoked in ProhostAI. Re-pair. |
167
+ | `auth_error: insufficient_scope` | The credential isn't allowed to stream events — ask your ProhostAI admin to re-issue the pairing. |
168
+ | `auth_error: stream_subscription_unavailable` | The connection has no event subscription to stream. Re-pair. |
169
+ | `signature did not verify` | The event didn't come from ProhostAI, or your stored secret is stale. Re-pair. |
170
+ | 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`). |
171
+
172
+ Credentials live in `~/.prohost/agent.json` and the run ledger in
173
+ `~/.prohost/runs.json`; set `$PROHOST_HOME` to move that directory. Delete the file to un-pair locally, and revoke the connection in
174
+ ProhostAI to invalidate the key itself.
175
+
176
+ ### What the pairing can and can't do
177
+
178
+ The credential is deliberately narrow. It can stream events, post in internal
179
+ team chat, manage its own task queue, file approvals, and close out its own
180
+ runs. It **cannot** create or delete webhook subscriptions, and it cannot send
181
+ to guest channels (OTA, SMS, email, WhatsApp) — a guest-facing send has to go
182
+ through ProhostAI's own approval path, not around it via an API key.
183
+
184
+ ---
185
+
186
+ ## Local webhook forwarding
187
+
188
+ ```bash
189
+ npx @prohost/cli listen --forward-to http://localhost:3000/webhook
190
+ ```
191
+
192
+ Streams your account's webhook deliveries over an authenticated websocket and
193
+ POSTs each payload to your local URL, preserving the `X-ProhostAI-*` headers so
194
+ your handler sees the same shape as a production delivery.
195
+
196
+ | Flag | Default | What it does |
197
+ | --- | --- | --- |
198
+ | `--forward-to` | *(required)* | Local URL each payload is POSTed to. |
199
+ | `--api-key` | `$PROHOST_API_KEY` | ProhostAI API key (`ph_live_…`). |
200
+ | `--url` | `wss://api.prohost.ai/ws/webhooks` | Websocket endpoint. |
201
+ | `--machine` | OS hostname | Name displayed in the UI. |
202
+ | `--subscription-id` | none | Stamp `connected_from` on a specific subscription. |
203
+
204
+ ---
205
+
206
+ ## Support
207
+
208
+ Email [support@prohost.ai](mailto:support@prohost.ai). Include the output of
209
+ `npx @prohost/cli --version`.
210
+
211
+ The source lives in ProhostAI's (private) backend monorepo under
212
+ `packages/prohost-cli`; there is no public issue tracker.
213
+
214
+ ---
215
+
216
+ ## Development
217
+
218
+ For ProhostAI engineers working in the monorepo:
219
+
220
+ ```bash
221
+ bun install # from the repo root
222
+ bun test # package test suite
223
+ npm run lint # tsc --noEmit
224
+ npm run build # emit dist/
225
+ npm pack # build + tarball, exactly as published
226
+ ```
227
+
228
+ `prepack` runs the build, so `npm pack` and `npm publish` can never ship a
229
+ stale or missing `dist/`.
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The two HTTP writes the harness makes back into ProhostAI: post the reply,
3
+ * then close out the run.
4
+ *
5
+ * Both authenticate with the paired API key over ``X-API-Key`` and both return
6
+ * a result object rather than throwing — the runner has to keep going (and
7
+ * still complete the run) whichever one fails.
8
+ *
9
+ * The paths come from the run event itself (`reply_instructions.path` /
10
+ * `completion.path`), which is why these take a path rather than building one.
11
+ */
12
+ export interface ApiOptions {
13
+ baseUrl: string;
14
+ apiKey: string;
15
+ fetchImpl?: typeof fetch;
16
+ /** Per-request timeout. Defaults to 30 seconds. */
17
+ timeoutMs?: number;
18
+ }
19
+ export interface ApiResult {
20
+ ok: boolean;
21
+ status: number;
22
+ error?: string;
23
+ }
24
+ /**
25
+ * Post the agent's reply into the conversation that triggered the run.
26
+ *
27
+ * Keyed on ``run_id`` so the send is idempotent server-side. The harness dedupes
28
+ * runs in memory, but that memory dies with the process: without this header a
29
+ * redelivered run after a restart would post the reply a second time.
30
+ */
31
+ export declare function postReply(options: ApiOptions, path: string, bodyKey: string, message: string, runId: string): Promise<ApiResult>;
32
+ /**
33
+ * Report a run's terminal state. Called for every run — including one whose
34
+ * command crashed — so the server never waits on a run this harness abandoned.
35
+ *
36
+ * A ``409`` is reported as success: it means the run already reached a terminal
37
+ * state (a redelivery we raced, or the 2h stale-run sweeper got there first).
38
+ * Treating that as a failure would only produce noise about work that is done.
39
+ */
40
+ export declare function completeRun(options: ApiOptions, path: string, outcome: {
41
+ ok: true;
42
+ } | {
43
+ ok: false;
44
+ error: string;
45
+ }): Promise<ApiResult>;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The two HTTP writes the harness makes back into ProhostAI: post the reply,
3
+ * then close out the run.
4
+ *
5
+ * Both authenticate with the paired API key over ``X-API-Key`` and both return
6
+ * a result object rather than throwing — the runner has to keep going (and
7
+ * still complete the run) whichever one fails.
8
+ *
9
+ * The paths come from the run event itself (`reply_instructions.path` /
10
+ * `completion.path`), which is why these take a path rather than building one.
11
+ */
12
+ import { USER_AGENT } from '../version.js';
13
+ import { MAX_ERROR_CHARS, RUN_STATUS_FAILED, RUN_STATUS_SUCCEEDED } from './contract.js';
14
+ const DEFAULT_TIMEOUT_MS = 30_000;
15
+ async function post(options, path, body, idempotencyKey) {
16
+ const fetchImpl = options.fetchImpl ?? fetch;
17
+ const controller = new AbortController();
18
+ const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
19
+ try {
20
+ const response = await fetchImpl(`${options.baseUrl.replace(/\/+$/, '')}${path}`, {
21
+ method: 'POST',
22
+ headers: {
23
+ 'Content-Type': 'application/json',
24
+ 'X-API-Key': options.apiKey,
25
+ 'User-Agent': USER_AGENT,
26
+ ...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {}),
27
+ },
28
+ body: JSON.stringify(body),
29
+ signal: controller.signal,
30
+ });
31
+ const ok = response.status >= 200 && response.status < 300;
32
+ let error;
33
+ if (!ok) {
34
+ // Surface the server's reason — a scope or tier denial is the most
35
+ // likely failure here and the host can act on it.
36
+ error = await response
37
+ .text()
38
+ .then((t) => t.slice(0, 500))
39
+ .catch(() => undefined);
40
+ }
41
+ return { ok, status: response.status, error };
42
+ }
43
+ catch (err) {
44
+ return {
45
+ ok: false,
46
+ status: 0,
47
+ error: err instanceof Error ? err.message : String(err),
48
+ };
49
+ }
50
+ finally {
51
+ clearTimeout(timer);
52
+ }
53
+ }
54
+ /**
55
+ * Post the agent's reply into the conversation that triggered the run.
56
+ *
57
+ * Keyed on ``run_id`` so the send is idempotent server-side. The harness dedupes
58
+ * runs in memory, but that memory dies with the process: without this header a
59
+ * redelivered run after a restart would post the reply a second time.
60
+ */
61
+ export async function postReply(options, path, bodyKey, message, runId) {
62
+ return post(options, path, { [bodyKey]: message }, `prohost-cli-run-${runId}`);
63
+ }
64
+ /**
65
+ * Report a run's terminal state. Called for every run — including one whose
66
+ * command crashed — so the server never waits on a run this harness abandoned.
67
+ *
68
+ * A ``409`` is reported as success: it means the run already reached a terminal
69
+ * state (a redelivery we raced, or the 2h stale-run sweeper got there first).
70
+ * Treating that as a failure would only produce noise about work that is done.
71
+ */
72
+ export async function completeRun(options, path, outcome) {
73
+ const body = outcome.ok
74
+ ? { status: RUN_STATUS_SUCCEEDED }
75
+ : { status: RUN_STATUS_FAILED, error: outcome.error.slice(0, MAX_ERROR_CHARS) };
76
+ const result = await post(options, path, body);
77
+ if (result.status === 409)
78
+ return { ...result, ok: true };
79
+ return result;
80
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Flag plumbing for `prohost agent <pair|run>`.
3
+ *
4
+ * Kept separate from the harness itself so `pair.ts` / `run.ts` stay callable
5
+ * (and testable) as plain async functions with no argv or process concerns.
6
+ */
7
+ type Flags = Record<string, string | boolean>;
8
+ /** Dispatch an ``agent`` subcommand. Returns the process exit code. */
9
+ export declare function runAgent(subcommand: string | undefined, flags: Flags, printHelp: () => void): Promise<number>;
10
+ export {};
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Flag plumbing for `prohost agent <pair|run>`.
3
+ *
4
+ * Kept separate from the harness itself so `pair.ts` / `run.ts` stay callable
5
+ * (and testable) as plain async functions with no argv or process concerns.
6
+ */
7
+ import process from 'node:process';
8
+ import { PairError, runPair } from './pair.js';
9
+ import { CredentialsMissingError, loadCredentials } from './credentials.js';
10
+ import { DEFAULT_EXEC_TIMEOUT_MS, runAgentHarness } from './run.js';
11
+ function flagString(flags, name) {
12
+ const value = flags[name];
13
+ return typeof value === 'string' ? value : undefined;
14
+ }
15
+ /** Dispatch an ``agent`` subcommand. Returns the process exit code. */
16
+ export async function runAgent(subcommand, flags, printHelp) {
17
+ if (subcommand === 'pair')
18
+ return pairCommand(flags);
19
+ if (subcommand === 'run')
20
+ return runCommand(flags);
21
+ process.stderr.write(subcommand
22
+ ? `Unknown agent subcommand: ${subcommand}\n\n`
23
+ : 'Missing agent subcommand — expected `pair` or `run`.\n\n');
24
+ printHelp();
25
+ return 1;
26
+ }
27
+ async function pairCommand(flags) {
28
+ const code = flagString(flags, 'code') ?? process.env.PROHOST_PAIRING_CODE;
29
+ if (!code) {
30
+ process.stderr.write('Missing pairing code. Pass --code or set $PROHOST_PAIRING_CODE.\n');
31
+ return 1;
32
+ }
33
+ try {
34
+ await runPair({
35
+ code,
36
+ baseUrl: flagString(flags, 'base-url'),
37
+ wsUrl: flagString(flags, 'url') ?? process.env.PROHOST_WS_URL,
38
+ webhookUrl: flagString(flags, 'webhook-url'),
39
+ });
40
+ return 0;
41
+ }
42
+ catch (err) {
43
+ if (err instanceof PairError) {
44
+ process.stderr.write(`✗ ${err.message}\n`);
45
+ return 1;
46
+ }
47
+ throw err;
48
+ }
49
+ }
50
+ async function runCommand(flags) {
51
+ const exec = flagString(flags, 'exec');
52
+ if (!exec) {
53
+ process.stderr.write("Missing required flag: --exec. Example: prohost agent run --exec 'claude -p'\n");
54
+ return 1;
55
+ }
56
+ let credentials;
57
+ try {
58
+ credentials = loadCredentials();
59
+ }
60
+ catch (err) {
61
+ if (err instanceof CredentialsMissingError) {
62
+ process.stderr.write(`✗ ${err.message}\n`);
63
+ return 1;
64
+ }
65
+ throw err;
66
+ }
67
+ const timeoutSeconds = flagString(flags, 'timeout');
68
+ const parsedTimeout = timeoutSeconds === undefined ? undefined : Number(timeoutSeconds);
69
+ if (parsedTimeout !== undefined && (!Number.isFinite(parsedTimeout) || parsedTimeout <= 0)) {
70
+ process.stderr.write('--timeout must be a positive number of seconds.\n');
71
+ return 1;
72
+ }
73
+ // First signal stops accepting work and lets the in-flight run finish
74
+ // reporting itself (so the server doesn't wait on a run we abandoned); a
75
+ // second one means the operator wants out now.
76
+ const controller = new AbortController();
77
+ const onSig = () => {
78
+ if (controller.signal.aborted) {
79
+ process.stdout.write('\nQuitting — the in-flight run was not reported.\n');
80
+ process.exit(130);
81
+ }
82
+ controller.abort();
83
+ process.stdout.write('\nFinishing the in-flight run… press Ctrl-C again to quit immediately.\n');
84
+ };
85
+ process.on('SIGINT', onSig);
86
+ process.on('SIGTERM', onSig);
87
+ try {
88
+ const metrics = await runAgentHarness({
89
+ exec,
90
+ credentials,
91
+ url: flagString(flags, 'url') ?? process.env.PROHOST_WS_URL,
92
+ machine: flagString(flags, 'machine'),
93
+ cwd: flagString(flags, 'cwd'),
94
+ timeoutMs: parsedTimeout === undefined ? DEFAULT_EXEC_TIMEOUT_MS : parsedTimeout * 1000,
95
+ dryRun: flags['dry-run'] === true,
96
+ allowUnverified: flags['allow-unverified'] === true,
97
+ signal: controller.signal,
98
+ });
99
+ process.stdout.write(`\nRuns: ${metrics.runsSucceeded} succeeded, ${metrics.runsFailed} failed, ` +
100
+ `${metrics.signatureRejections} rejected.\n`);
101
+ // A harness that never connected must not look like a clean shutdown to a
102
+ // service manager or a wrapper script.
103
+ return metrics.fatal === undefined ? 0 : 1;
104
+ }
105
+ finally {
106
+ process.off('SIGINT', onSig);
107
+ process.off('SIGTERM', onSig);
108
+ }
109
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The wire contract between this CLI and ProhostAI's agent-run loop.
3
+ *
4
+ * Everything the server owns — event names, HTTP paths, run-status values —
5
+ * lives here and nowhere else, so a server-side rename is a one-line change
6
+ * in this file rather than a hunt through the runner.
7
+ *
8
+ * The `agent.run_requested` payload spells out its own reply and completion
9
+ * paths precisely so a harness doesn't hand-derive them, and this module
10
+ * prefers those server-supplied paths. The constants below are the fallback
11
+ * for a payload that omits them.
12
+ *
13
+ * Server-side sources of truth:
14
+ * - payload shape — `shared/events/payloads/agent_runs.py`
15
+ * - `POST /v1/agent-runs/{id}/complete` — `api_public/routers/agent_runs.py`
16
+ * - `POST /v1/agent-pairing/redeem` — `api_public/routers/agent_pairing.py`
17
+ * - `POST /v1/conversations/{id}/messages` — `api_public/routers/conversations.py`
18
+ */
19
+ /** Default host for the public API (`api_public`, mounted under `/v1`). */
20
+ export declare const DEFAULT_API_BASE_URL = "https://connect.prohost.ai";
21
+ /** Fired when a paired agent is given work — the trigger to run. */
22
+ export declare const EVENT_AGENT_RUN_REQUESTED = "agent.run_requested";
23
+ /** Informational events the harness logs but does not act on. */
24
+ export declare const EVENT_MENTION_CREATED = "mention.created";
25
+ export declare const EVENT_MESSAGE_TEAM_CHAT = "message.team_chat";
26
+ /** Terminal statuses accepted by the run-completion endpoint. */
27
+ export declare const RUN_STATUS_SUCCEEDED = "succeeded";
28
+ export declare const RUN_STATUS_FAILED = "failed";
29
+ /** Server-side cap on the `error` field of a completion (`MAX_ERROR_CHARS`). */
30
+ export declare const MAX_ERROR_CHARS = 1000;
31
+ /** `POST` — exchange a one-time pairing code for a scoped credential. */
32
+ export declare const REDEEM_PATH = "/v1/agent-pairing/redeem";
33
+ /** `POST` — send a message into a conversation as the paired agent. */
34
+ export declare function conversationMessagesPath(conversationId: string): string;
35
+ /** `POST` — close out a run. Must be called for every run, success or not. */
36
+ export declare function agentRunCompletePath(runId: string): string;
37
+ /** The `agent.run_requested` payload, as far as the harness cares about it. */
38
+ export interface AgentRunRequest {
39
+ run_id: string;
40
+ agent_id?: string;
41
+ agent_user_id?: string;
42
+ agent_name?: string;
43
+ account_id?: string;
44
+ /** Why the agent woke — `team_chat_mention`, `task_comment`, … */
45
+ kind?: string;
46
+ conversation_id?: string;
47
+ task_id?: string;
48
+ cleaning_id?: string;
49
+ trigger_source?: string;
50
+ trigger_message_id?: string;
51
+ /** Capped inline excerpt of the message that woke the agent. */
52
+ trigger_text?: string;
53
+ trigger_user_id?: string;
54
+ /** For a trigger fan-out with no surface: the event type that fired it. */
55
+ trigger_type?: string;
56
+ /** For a trigger fan-out with no surface: the entity that event was about. */
57
+ trigger_entity_id?: string;
58
+ /**
59
+ * What the run is about, exactly as the server declared it. This is NOT the
60
+ * same question as "can the reply be posted": a task run is a task run even
61
+ * though the external API has no task-comment route yet, and the agent needs
62
+ * to be told that to answer sensibly. Postability is `reply_path` +
63
+ * `reply_body_key`.
64
+ */
65
+ reply_surface: ReplySurface;
66
+ /** Server-supplied path to post the reply to. Absent when there is nowhere. */
67
+ reply_path?: string;
68
+ /**
69
+ * JSON field the reply text goes in — taken from the event's
70
+ * ``reply_instructions.body_field`` when present. Conversations take
71
+ * ``message``; comment surfaces take ``content``
72
+ * (`shared/comments/schemas.py`).
73
+ */
74
+ reply_body_key?: string;
75
+ /** Server-supplied path to complete the run at. */
76
+ completion_path: string;
77
+ }
78
+ /** Reply destinations the server can bind a run to, including future surfaces. */
79
+ export type ReplySurface = 'conversation' | 'task' | 'cleaning' | 'none' | (string & {});
80
+ /**
81
+ * Extract the event body from a websocket frame's payload.
82
+ *
83
+ * Deliveries arrive wrapped in the outbound-webhook envelope
84
+ * (`{id, event, created_at, schema_version, data}`); the fields we want live
85
+ * in `data`. Frames without an envelope (the server's `echo` test frame) are
86
+ * passed through unchanged.
87
+ */
88
+ export declare function eventData(payload: Record<string, unknown>): Record<string, unknown>;
89
+ /**
90
+ * Validate an `agent.run_requested` body. Returns ``null`` when the frame is
91
+ * missing a ``run_id`` — without one there is no run to complete, so the only
92
+ * safe move is to ignore it rather than execute an unattributable prompt.
93
+ */
94
+ export declare function parseRunRequest(data: Record<string, unknown>): AgentRunRequest | null;