@agentproto/runtime 5.1.0 → 5.3.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/dist/config.d.ts CHANGED
@@ -1,592 +1,5 @@
1
- import { S as SpawnDefaultsConfig } from './spawn-defaults-wQV9AeiG.js';
2
- import './context-continuity-Bn99UtVC.js';
1
+ import './spawn-defaults-Cp9SGZex.js';
2
+ export { f as AcpAgentConfigEntry, t as AgentprotoConfig, c as AgentsMdConfig, A as ApprovalsConfig, u as CONFIG_FILE_PATH, v as CONFIG_VERSION, C as CatalogConfig, w as CatalogSourceConfig, D as DaemonConfig, F as FeaturesConfig, P as PairingConfig, e as ProfileConfig, b as ProvenanceConfig, x as ReviewConfig, y as SentinelConfig, a as SessionsConfig, i as SpawnAttachMode, S as SpawnConfig, j as SpawnDedupeMode, g as TerminalPreset, d as TitlerConfig, T as TunnelConfig, h as WorktreeIsolationMode, W as WorktreesConfig, z as getConfigKey, B as loadConfig, E as saveConfig, G as setConfigKey, H as splitConfigPath } from './config-DcESQDmZ.js';
3
3
  import '@agentproto/acp';
4
4
  import '@agentproto/command-sandbox';
5
5
  import '@agentproto/model-catalog';
6
-
7
- /**
8
- * `~/.agentproto/config.json` — single hand-editable JSON for the
9
- * agentproto control plane's defaults. Sits alongside the existing
10
- * surface files (workspaces.json, credentials.json, sessions.json):
11
- *
12
- * workspaces.json which directories are workspaces + which is active
13
- * credentials.json tunnel host bearer tokens (mode 0600)
14
- * sessions.json last-known snapshot of the registry (informational)
15
- * config.json daemon defaults: port, bind, allowed origins,
16
- * tunnel host, feature toggles
17
- *
18
- * Resolution order for every daemon knob is:
19
- * 1. CLI flag (e.g. --port)
20
- * 2. Env var (where one exists, e.g. AGENTPROTO_TOKEN)
21
- * 3. config.json
22
- * 4. Hardcoded default
23
- *
24
- * This means a user can call `agentproto config set daemon.port 18791`
25
- * once and never re-pass `--port 18791` to `serve install` etc. CLI
26
- * flags still win for one-off overrides.
27
- *
28
- * Schema is intentionally narrow + extensible — unknown keys are
29
- * preserved on save (deep-merge), so a newer CLI writing a new
30
- * field won't drop one an older CLI doesn't know about. No secrets
31
- * here; credentials stay in credentials.json (mode 0600).
32
- */
33
-
34
- declare const CONFIG_VERSION: 1;
35
- interface DaemonConfig {
36
- /** Absolute path to the workspace the daemon binds to at boot. */
37
- workspace?: string;
38
- /** HTTP port. Default 18790. */
39
- port?: number;
40
- /** Bind addr. Default 127.0.0.1. */
41
- bind?: string;
42
- /** Trusted browser origins for mutating /sessions/* routes (in
43
- * addition to the hardcoded localhost defaults). */
44
- allowedOrigins?: string[];
45
- /** When true, the daemon does NOT auto-trust localhost-on-any-port.
46
- * Only origins explicitly listed in `allowedOrigins` are allowed.
47
- * Pair with a curated list (e.g. `["http://localhost:3000"]`) for
48
- * hardened setups. Default false. */
49
- strictOrigins?: boolean;
50
- /** Server label sent in tunnel hello frames. */
51
- label?: string;
52
- /** Bearer token gating the gateway at boot (`AuthOptions` with
53
- * `mode: "bearer"`). Unlike `remote_enable`'s ephemeral quick-tunnel
54
- * token, this one lives in config.json and survives daemon restarts.
55
- * Set via `agentproto config set daemon.authToken <token>` (e.g.
56
- * `$(openssl rand -hex 32)`). Unset ⇒ the gateway boots with
57
- * `mode: "none"` — fully open on loopback, same as today. */
58
- authToken?: string;
59
- /** Opt-in eager resume-on-boot (session-survivability plan §5
60
- * "Opt-in vs automatic", PR-4). When true, a daemon restart doesn't just
61
- * leave agent-cli sessions dead-but-lazy-resumable — the boot pass eagerly
62
- * re-spawns the eligible ones (those that died with
63
- * `endedReason: "daemon-restart"` and still pass `canResume`) IN PLACE,
64
- * restoring liveness without waiting for a prompt. Lazy resume-on-prompt is
65
- * always on regardless of this flag (it costs nothing until someone acts);
66
- * this flag only controls the *automatic* boot-time pass. Default false: a
67
- * box-wide restart should not silently relaunch every adapter (cost, spawn
68
- * storm, credential re-resolution), and default-off also keeps two daemons
69
- * sharing the workspace buckets from racing to resume the same rows. Set via
70
- * `agentproto config set daemon.resumeSessionsOnBoot true`. Surfaced in
71
- * `daemon_health` / `GET /health`. */
72
- resumeSessionsOnBoot?: boolean;
73
- /** Opt-in continue-on-boot (continue-interrupted). When true, right after
74
- * the eager resume pass, the boot sends a "the daemon restarted mid-turn,
75
- * check the state on disk and continue" prompt to every session the LAST
76
- * restart interrupted mid-turn (`interrupted: true`) that is resumable,
77
- * under the resume cap, idle, and whose resume didn't fail. Never loops: at
78
- * most one auto-continue per restart and `MAX_AUTO_CONTINUE_ATTEMPTS` in a
79
- * row without a successful turn-end. Default false — the interrupted prompt
80
- * itself is never auto-retried, and a continue prompt is still a prompt
81
- * (tokens, side effects), so it's opt-in. The manual
82
- * `session_continue_interrupted` verb works regardless. Set via
83
- * `agentproto config set daemon.continueInterruptedOnBoot true`. */
84
- continueInterruptedOnBoot?: boolean;
85
- /** Idle agent-session reaper (PR-6). When set to a positive number of
86
- * milliseconds, the daemon periodically retires agent-cli sessions that have
87
- * been idle (not busy, not awaiting input, `status:"running"`) longer than
88
- * this: it SIGTERMs the adapter child to free the process and flips the row
89
- * to `killed`/`endedReason:"idle-reaped"` — dead-but-lazy-resumable (a later
90
- * prompt revives it) and, critically, excluded from eager resume-on-boot
91
- * (#638 gates on `endedReason === "daemon-restart"`), so enabling this makes
92
- * eager resume safe: only genuinely-recent work survives a restart. Default
93
- * OFF (unset / 0 / negative ⇒ the reaper never runs). Resolution order
94
- * mirrors the module docblock: `AGENTPROTO_IDLE_REAP_AFTER_MS` env > this
95
- * field > off. Set via `agentproto config set daemon.idleReapAfterMs
96
- * <ms>`. Surfaced in `daemon_health` / `GET /health`. */
97
- idleReapAfterMs?: number;
98
- /** Crash-detect sweep interval in ms (crash-detect PR-1). Unlike
99
- * `idleReapAfterMs`, this is DEFAULT ON (non-destructive observability):
100
- * the daemon periodically probes every live agent-cli session's OS
101
- * process (`process.kill(pid, 0)`) and, when it's provably gone with no
102
- * exit event ever emitted for it, flips the row to
103
- * `error`/`endedReason:"crashed"` with a `lastError` string — surfacing a
104
- * death that would otherwise sit silently as `status:"running"` until the
105
- * next prompt's RPC throws (or forever, for a parked session with no next
106
- * prompt). Detects only; never restarts or notifies (later PRs). Set to a
107
- * non-positive value to disable the sweep entirely. Resolution order
108
- * mirrors `idleReapAfterMs`: `AGENTPROTO_CRASH_DETECT_INTERVAL_MS` env >
109
- * this field > the hardcoded default (30s). Set via `agentproto config
110
- * set daemon.crashDetectIntervalMs <ms>`. Surfaced in `daemon_health` /
111
- * `GET /health`. */
112
- crashDetectIntervalMs?: number;
113
- /** Restart-scheduler sweep interval in ms (restart-scheduler PR-2). OFF by
114
- * default (unset / 0 / negative ⇒ the sweep never runs), unlike
115
- * `crashDetectIntervalMs` — a positive value here only arms the periodic
116
- * sweep that EXECUTES an already-scheduled restart; it never opts a
117
- * session in by itself (that's the per-session `agent_start.restartPolicy`
118
- * field). Resolution order mirrors `idleReapAfterMs`:
119
- * `AGENTPROTO_RESTART_SWEEP_INTERVAL_MS` env > this field > off. Set via
120
- * `agentproto config set daemon.restartSweepIntervalMs <ms>`. Surfaced in
121
- * `daemon_health` / `GET /health`. */
122
- restartSweepIntervalMs?: number;
123
- /** Turn-liveness watchdog threshold in ms (turn-liveness-watchdog
124
- * chantier). Same shape as `crashDetectIntervalMs`: DEFAULT ON
125
- * (non-destructive observability). The daemon periodically sweeps every
126
- * BUSY agent-cli session and, for one that is mid-turn, NOT legitimately
127
- * `blockedOn` a subagent/command, and has had no adapter activity
128
- * (`lastActivityAt`) for longer than this threshold, stamps
129
- * `stalledSinceMs` on the descriptor and emits `session:stalled` —
130
- * surfacing a dead adapter stream (network drop, hung child — zero
131
- * frames mid-turn) that would otherwise sit indistinguishable from
132
- * healthy long work: `status:"running"`, `lastError:null`, no other
133
- * signal. Conservative by design: a session legitimately blocked on a
134
- * long tool call is NEVER a candidate, no matter how long it's silent —
135
- * see `stall-watchdog.ts`'s docblock for why. Detects only; never kills
136
- * or restarts (this is a later concern, if ever). Set to a non-positive
137
- * value to disable the sweep entirely. Resolution order mirrors
138
- * `idleReapAfterMs`: `AGENTPROTO_TURN_STALL_AFTER_MS` env > this field >
139
- * the hardcoded default (5 min). Set via `agentproto config set
140
- * daemon.turnStallAfterMs <ms>`. Surfaced in `daemon_health` /
141
- * `GET /health`. */
142
- turnStallAfterMs?: number;
143
- }
144
- interface TitlerConfig {
145
- /** Enable the daemon-side session titler. Default false. */
146
- enabled?: boolean;
147
- /** OpenRouter model id used to generate titles. Default
148
- * `z-ai/glm-5.2@openrouter`. Requires `OPENROUTER_API_KEY` in the
149
- * daemon's environment; without a key the titler falls back to the
150
- * local (first-prompt-line) title. */
151
- model?: string;
152
- }
153
- interface TunnelConfig {
154
- /** Cloud WS URL. When set + autoconnect=true, `agentproto serve`
155
- * bootstraps with `--connect <host>`. */
156
- host?: string;
157
- /** apt_ daemon token to present at the tunnel upgrade. When set,
158
- * `agentproto serve` uses this BEFORE falling back to
159
- * credentials.json — handy in profiles where the token-per-host
160
- * mapping in credentials.json doesn't fit (e.g. host = tunnel URL
161
- * but credentials were minted against the api URL). */
162
- token?: string;
163
- /** Whether `agentproto daemon start` connects the tunnel by
164
- * default. v0 only — implementer can ignore until daemon needs it. */
165
- autoconnect?: boolean;
166
- /**
167
- * Opt into end-to-end encryption of the outbound `serve --connect` tunnel
168
- * (design: tunnel-e2e/v1). When true, the daemon negotiates a
169
- * token-authenticated ephemeral handshake with the host and wraps the tunnel
170
- * frames in an AEAD box, so even the trusted host loses plaintext visibility.
171
- * The handshake authenticates both ends against the shared `tunnel.token`, so
172
- * `token` MUST also be set. Fully backward-compatible: if the host doesn't
173
- * advertise e2e (an older host), the daemon falls back to today's plaintext
174
- * tunnel. Unset/false ⇒ plaintext, byte-identical to today. */
175
- e2e?: boolean;
176
- }
177
- interface FeaturesConfig {
178
- /** Hint that PTY is desired — informational; the daemon still
179
- * detects node-pty's presence at runtime. */
180
- pty?: boolean;
181
- /** Enable the local LLM Endpoint proxy sidecar (route registration,
182
- * MCP tools, child-process lifecycle). Default false — the endpoint is
183
- * an opt-in feature; when off, the `llm-endpoint` custom route is not
184
- * registered and the `llm_endpoint_*` MCP tools are not exposed. */
185
- llmEndpoint?: boolean;
186
- /** Expose this daemon's local inference endpoint(s) (the `llmEndpoint`
187
- * sidecar above) to paired controllers over a HOST-scoped pairing —
188
- * `GET /device-inference/v1/models` + `POST
189
- * /device-inference/v1/chat/completions` in http-server.ts. Default
190
- * false: even with `llmEndpoint` on, a plain remote-control pairing (or
191
- * `serve --connect`) never gets these routes regardless of this flag —
192
- * it also requires the pairing itself to be host-scoped (`pair offer
193
- * --host` + `devices add`). Toggle via `agentproto devices
194
- * share-inference on|off`. */
195
- deviceInferenceShare?: boolean;
196
- }
197
- /**
198
- * Policy for `agentproto worktree new` (PLAN.md §1.4 — config carries
199
- * policy, never state; git itself is the authority for which worktrees
200
- * exist). This is the fix for the sprawl the plan measured: 31 linked
201
- * worktrees across 6 different parent directories, because there was no
202
- * `worktree new` verb and therefore no convention to converge on.
203
- */
204
- /**
205
- * How the daemon isolates a freshly-spawned agent session into its own git
206
- * worktree (`agent_start.worktree`):
207
- * - `"always"` — every depth-0 spawn is provisioned into a worktree,
208
- * whether or not the caller asked. A cwd that is not in
209
- * a git repo has nothing to isolate, so it spawns plain.
210
- * - `"on-request"` — isolate ONLY when the caller passes `worktree`. This
211
- * is the default and the back-compatible behaviour:
212
- * today's callers pass nothing and spawn exactly where
213
- * they asked.
214
- * - `"never"` — isolation is off; an explicit `worktree` field is
215
- * REJECTED (loud, not silently ignored) so a caller
216
- * never believes it got an isolated tree it didn't.
217
- */
218
- type WorktreeIsolationMode = "always" | "on-request" | "never";
219
- interface WorktreesConfig {
220
- /**
221
- * Absolute path new worktrees are created under. Layout:
222
- * `<root>/<repoName>/<slug>`. Resolution order (mirrors every other
223
- * knob in this file, see the module docblock): `--root` flag >
224
- * `AGENTPROTO_WORKTREES_ROOT` env > this field > the hardcoded default
225
- * `~/.agentproto/worktrees`. The default is a real single root, not
226
- * "unconfigured" — `worktree new` converges to one place with zero
227
- * setup, which is the only way the sprawl actually stops (the 6 roots
228
- * that exist today are 6 people each inventing a default by hand).
229
- */
230
- root?: string;
231
- /**
232
- * Policy for `agent_start.worktree` isolation. Resolution order mirrors
233
- * the module docblock (there is no CLI flag — this is a daemon-side
234
- * policy read at spawn, not a per-invocation flag):
235
- * `AGENTPROTO_WORKTREES_ISOLATION` env > this field > the hardcoded
236
- * default `"on-request"`. `"on-request"` is deliberately the default:
237
- * any other would break back-compat by isolating callers that never
238
- * asked (see `worktree-isolation.ts`).
239
- */
240
- isolation?: WorktreeIsolationMode;
241
- }
242
- /**
243
- * Policy for `agent_start.attach` — whether a spawn AUTO-attaches to its
244
- * calling session as parent lineage. `"always"` (the default) nests a
245
- * spawned child under the session that spawned it whenever that identity is
246
- * derivable (the trusted `?callerSessionId=` on the daemon self-ref URL, or
247
- * an explicit `parentSessionId` hint), so a supervisor's executors stop
248
- * landing as depth-0 orphans. `"on-request"` reverts to the pre-attach
249
- * behaviour: auto-attribution is off, and a child nests only when the caller
250
- * explicitly opts in (`attach: true` / `attach: { parent }` / an explicit
251
- * `parentSessionId`). Either way a per-call `attach: false` forces an
252
- * independent root, exactly as `worktree: false` opts out of isolation. See
253
- * `spawn-attach.ts`.
254
- */
255
- type SpawnAttachMode = "always" | "on-request";
256
- /**
257
- * Policy for `agent_start`'s IMPLICIT dedupe — what happens when a caller
258
- * spawns with NO `idempotencyKey` at all (see `spawn-dedupe.ts`):
259
- * - `"always"` — the daemon DERIVES an implicit key from the spawn's
260
- * `label` (required — see below) plus a hash of the
261
- * initial `prompt`, and dedupes a same-adapter/cwd
262
- * repeat against it exactly as an explicit key would.
263
- * This is the default, mirroring `attach`'s own
264
- * "opt-in-only guard is not a guard" precedent above.
265
- * Deriving needs a `label` to produce anything at all
266
- * — an unlabelled spawn is untouched, which is what
267
- * keeps this safe for the fan-out pattern this repo
268
- * exercises (several agents into one cwd with no
269
- * shared label): see `spawn-dedupe.ts`'s docblock for
270
- * the full false-dedup analysis, and PR #803's own
271
- * no-opt-in label+cwd warning backstop in
272
- * `session-spawn.ts`, which independently landed on
273
- * the same label-is-the-signal boundary.
274
- * - `"on-request"` — no implicit derivation; only an explicit
275
- * `idempotencyKey` dedupes (today's behaviour,
276
- * unchanged). A per-call `dedupe: true` still opts in
277
- * under this policy, mirroring `attach: true`.
278
- * Either way a per-call `dedupe: false` disables implicit derivation for
279
- * that one spawn regardless of policy — the escape hatch, mirroring
280
- * `attach: false` / `worktree: false`. An explicit `idempotencyKey` always
281
- * wins over a derived one (derivation is only attempted when the caller
282
- * supplied none).
283
- */
284
- type SpawnDedupeMode = "always" | "on-request";
285
- interface SpawnConfig {
286
- /**
287
- * Attach policy for `agent_start`. Resolution order mirrors the module
288
- * docblock (no CLI flag — a daemon-side policy read at spawn):
289
- * `AGENTPROTO_SPAWN_ATTACH` env > this field > the hardcoded default
290
- * `"always"`. Unlike `worktrees.isolation` (whose default preserves
291
- * back-compat by NOT isolating), attach defaults ON: an orphaned executor
292
- * is a bug, not a feature, and the auto-parent is descriptor-only lineage
293
- * that never relaxes a privilege gate.
294
- */
295
- attach?: SpawnAttachMode;
296
- /**
297
- * Implicit-dedupe policy for `agent_start`. Resolution order mirrors
298
- * `attach` above (no CLI flag — a daemon-side policy read at spawn):
299
- * `AGENTPROTO_SPAWN_DEDUPE` env > this field > the hardcoded default
300
- * `"always"`. See {@link SpawnDedupeMode} and `spawn-dedupe.ts` for the
301
- * full reasoning: a retry-safety guard that only works when a caller
302
- * remembers to ask for it (`idempotencyKey`) is not a guard — the same
303
- * argument `attach` already settled for parent lineage.
304
- */
305
- dedupe?: SpawnDedupeMode;
306
- }
307
- /**
308
- * Provenance policy — the opt-in `gh` PATH shim (`gh-provenance-shim.ts`).
309
- * Off by default. When `wrapGh` is on, every agent session the daemon spawns
310
- * gets a shim directory prepended to its PATH so that any `gh pr create` the
311
- * session (or an adapter subprocess shelling out — claude-code, codex, …) runs
312
- * has a deterministic `@agentproto-bot` provenance footer appended to the
313
- * created PR's BODY, matching the footer the cloud runner stamps. The TOOL
314
- * stamps, never the model; commit messages are never touched (the repo's
315
- * hygiene-check forbids attribution there). Resolution order mirrors
316
- * `spawn.attach`: `AGENTPROTO_PROVENANCE_WRAP_GH` env > this field > default
317
- * `false`.
318
- */
319
- interface ProvenanceConfig {
320
- /** Enable the opt-in `gh` provenance PATH shim for spawned sessions. */
321
- wrapGh?: boolean;
322
- }
323
- /**
324
- * Sentinel auto-link policy (AIP-60 §6, step 4) — whether a PR an executor
325
- * session opens gets a sentinel created for it automatically.
326
- */
327
- interface SentinelConfig {
328
- /** Auto-watch a PR when a session opens one (`recordOpenedPr`, fed by
329
- * `command_execute`'s stamper and both `pr-provenance-reconciler.ts`
330
- * lanes). When unset, the daemon defaults to `true` only when the
331
- * `local-gh` provider is available (i.e. `gh auth status` succeeds) —
332
- * otherwise `false`, logged once. Explicitly setting this always wins
333
- * over that probe. Per-spawn opt-out: `agent_start.sentinel: false`. */
334
- autoWatchPrs?: boolean;
335
- }
336
- /**
337
- * Session-presence policy — how the dashboard triages a session into its
338
- * `running` / `tending` / `attention` / `quiet` presence state (see
339
- * `session-presence.ts`). Optional block on `AgentprotoConfig.sessions`.
340
- */
341
- interface SessionsConfig {
342
- /**
343
- * How long (seconds) a session stays shown as `running` after its last
344
- * turn ends, before it settles into `attention`/`quiet`. The grace window
345
- * is measured from the session's most recent `lastActivityAt` — ANY new
346
- * event resets it — so a session that just answered does not read as
347
- * "quiet" the instant its turn finishes, but something that went idle long
348
- * ago, with nothing waiting on it, settles grey. Resolution order mirrors
349
- * every other knob in this file (module docblock): env var
350
- * `AGENTPROTO_SESSIONS_ATTENTION_DELAY_SEC` > this field > the hardcoded
351
- * default `60`. Absent ⇒ 60s.
352
- */
353
- attentionDelaySec?: number;
354
- /**
355
- * Root directory the daemon stores per-session transcripts under — each
356
- * session's `events.jsonl` lands at `<eventsDir>/<sessionId>/events.jsonl`
357
- * (and a terminal session's `terminal.jsonl` alongside it). Every
358
- * reader/writer (transcript writer, `/sessions/:id/events` HTTP routes,
359
- * exports, tool-call/usage logs) resolves through this single knob.
360
- * Resolution order mirrors every other knob in this file: relative paths
361
- * resolve against the home directory. Absent ⇒ the hardcoded default
362
- * `~/.agentproto/sessions` — zero behaviour change out of the box. A
363
- * change requires a daemon restart to take effect (and existing sessions'
364
- * transcripts do NOT move).
365
- */
366
- eventsDir?: string;
367
- }
368
- /**
369
- * Policy for the daemon's own AGENTS.md resolution + injection (WP-R2) —
370
- * how a spawned session's initial prompt carries the repo's `AGENTS.md`
371
- * (see `agents-md.ts`). The daemon resolves the nearest `AGENTS.md` walking
372
- * up from the session's `cwd`, bounded by that repo's git toplevel, and
373
- * injects one AGENTS.md block after the role disposition whenever the spawn
374
- * carries a prompt.
375
- */
376
- interface AgentsMdConfig {
377
- /**
378
- * Inline-vs-pointer threshold in KiB. A resolved AGENTS.md whose byte size
379
- * is STRICTLY LESS than `inlineMaxKb` KiB is inlined in full into the
380
- * spawn's initial prompt, clearly delimited; `>=` the threshold is injected
381
- * as a short pointer telling the agent to read the file before its first
382
- * tool call instead. An absent AGENTS.md (nothing found up the walk) injects
383
- * nothing but is still reported on the descriptor via
384
- * `SessionDescriptor.agentsMdMode === "absent"`. Resolution order mirrors
385
- * the module docblock (no CLI flag, and no env override — not part of the
386
- * approved design): this field > the hardcoded default `8` (KiB).
387
- */
388
- inlineMaxKb?: number;
389
- }
390
- interface PairingConfig {
391
- /** Rendezvous broker WS URL (ws:// or wss://) used by `pair offer` and by
392
- * autoconnect on boot. When unset, `pair offer` requires an explicit
393
- * `--rendezvous`. Mirrors `tunnel.host`. */
394
- rendezvous?: string;
395
- /** Whether the daemon opens standing rendezvous connections for every
396
- * persisted pairing on boot (so a paired client can reconnect anytime).
397
- * Mirrors `tunnel.autoconnect`. Default true when a rendezvous is set. */
398
- autoconnect?: boolean;
399
- /** Web pair page for `pair offer --qr` (the offer rides in its fragment).
400
- * A template with `{fp}` in the HOSTNAME, filled with the daemon
401
- * fingerprint (one origin per daemon), or a plain http(s) URL (one shared
402
- * origin). Unset → `https://{fp}.agentproto.cloud/pair`. `--pair-page`
403
- * overrides it. */
404
- pairPage?: string;
405
- }
406
- /**
407
- * A user-defined generic ACP agent — the config-file half of
408
- * `AcpAgentSpec` (the slug is the record key in `acpAgents`, so it's
409
- * omitted here). Any CLI that already speaks the Agent Client Protocol
410
- * can be wired with zero code by declaring one of these under
411
- * `acpAgents.<slug>` in `~/.agentproto/config.json`; the CLI's
412
- * `acpHandleFromSpec` mints a runnable `AgentCliHandle` from it at
413
- * resolve time (see `packages/cli/src/registry/acp-generic.ts`). Kept
414
- * in this package (not the CLI's) so `config.ts` stays the single
415
- * source of truth for the config surface without a cli→runtime→cli
416
- * import cycle — the CLI's `AcpAgentSpec` extends this shape.
417
- */
418
- interface AcpAgentConfigEntry {
419
- /** Display name. Defaults to the slug when omitted. */
420
- name?: string;
421
- /** One-line description surfaced in `agentproto acp ls`. */
422
- description?: string;
423
- /** Executable to spawn, e.g. "gemini". */
424
- bin: string;
425
- /** Extra argv appended after `bin`, e.g. ["--experimental-acp"]. */
426
- bin_args?: string[];
427
- /** Extra environment variables for the spawned process. */
428
- env?: Record<string, string>;
429
- /** Flag the CLI uses to receive the working directory, if it needs
430
- * one passed explicitly (most ACP agents take cwd over the wire). */
431
- cwd_flag?: string;
432
- /** When true, advertise resumable + native-resume continuation. */
433
- resumable?: boolean;
434
- /** Known model ids for the agent (informational + validation hints). */
435
- models?: {
436
- default?: string;
437
- allowed?: string[];
438
- };
439
- /** Billing endpoint this CLI's own auth bills (e.g. "mistral",
440
- * "moonshot", "google") — the provider whose wallet/auth-profile the
441
- * agent consumes. Lets clients link the harness to that provider's
442
- * wallets even when no model list is declared. Unset when the CLI's
443
- * billing target isn't a single known endpoint. */
444
- provider?: string;
445
- /** Shown when `bin` is missing from PATH (how to install the CLI). */
446
- install_hint?: string;
447
- }
448
- /**
449
- * Per-environment connection bundle. A profile overrides specific
450
- * fields of the top-level `daemon` / `tunnel` / `features` blocks
451
- * when selected via `--profile <name>` (or the top-level
452
- * `activeProfile` setting). Missing fields fall through to the
453
- * top-level config, so a profile only needs to declare what's
454
- * different — typically just `tunnel.host` + `tunnel.token`.
455
- *
456
- * Example:
457
- * {
458
- * "daemon": { "workspace": "/code", "port": 18790 },
459
- * "activeProfile": "local",
460
- * "profiles": {
461
- * "local": { "tunnel": { "host": "ws://localhost:3200/connect",
462
- * "token": "apt_local", "autoconnect": true } },
463
- * "prod": { "tunnel": { "host": "wss://tunnel.guilde.work/connect",
464
- * "token": "apt_prod", "autoconnect": true },
465
- * "daemon": { "port": 18791 } }
466
- * }
467
- * }
468
- *
469
- * Sandbox daemons generate per-sandbox profile entries at provision
470
- * time so the daemon inside the sandbox boots with
471
- * `agentproto serve --profile sandbox-<id>` and no extra plumbing.
472
- */
473
- interface ProfileConfig {
474
- daemon?: DaemonConfig;
475
- tunnel?: TunnelConfig;
476
- features?: FeaturesConfig;
477
- }
478
- /**
479
- * A user-defined named terminal/TUI preset stored in
480
- * `~/.agentproto/config.json` under `terminalPresets`. Presets keep
481
- * local launch recipes (argv, env, cwd, name/label) out of shared
482
- * adapter manifests — e.g. pointing a Claude Code TUI at a local
483
- * LLM gateway without retyping proxy env vars every spawn.
484
- */
485
- interface TerminalPreset {
486
- /** Command + args to spawn. When provided, `sessions terminal` can
487
- * be used without `-- <argv...>`. */
488
- argv?: string[];
489
- /** Extra environment variables layered on top of the daemon's
490
- * inherited process.env. Values MUST be strings. */
491
- env?: Record<string, string>;
492
- /** Working directory for the PTY session. Relative paths are
493
- * resolved against the current working directory at CLI time. */
494
- cwd?: string;
495
- /** Workspace slug used for cwd fallback when `cwd` is omitted. */
496
- workspace?: string;
497
- /** Stable session name passed to the registry (`name` field). */
498
- name?: string;
499
- /** Human-readable label surfaced in session listings. */
500
- label?: string;
501
- }
502
- interface AgentprotoConfig {
503
- version?: number;
504
- daemon?: DaemonConfig;
505
- tunnel?: TunnelConfig;
506
- features?: FeaturesConfig;
507
- /** E2E daemon-pairing defaults (rendezvous URL + autoconnect). */
508
- pairing?: PairingConfig;
509
- /** Where `agentproto worktree new` creates worktrees. See
510
- * {@link WorktreesConfig}. */
511
- worktrees?: WorktreesConfig;
512
- /** Spawn-time policy (`agent_start`). See {@link SpawnConfig}. */
513
- spawn?: SpawnConfig;
514
- /** Session-presence policy for the dashboard. See {@link SessionsConfig}. */
515
- sessions?: SessionsConfig;
516
- /** Provenance policy — the opt-in `gh` PATH shim. See {@link ProvenanceConfig}. */
517
- provenance?: ProvenanceConfig;
518
- /** Sentinel auto-link policy. See {@link SentinelConfig}. */
519
- sentinel?: SentinelConfig;
520
- /** Daemon-side AGENTS.md resolution/injection policy. See {@link AgentsMdConfig}. */
521
- agentsMd?: AgentsMdConfig;
522
- /** Daemon-side session titler (`session-titler.ts`). DEFAULT OFF — when
523
- * `enabled` is not explicitly true the titler is a complete no-op and
524
- * nothing renames anything. When on, at the end of the FIRST completed
525
- * turn of a `kind:"agent-cli"` session whose label is still a spawn
526
- * default (`chat-starter`, `chat-starter-autoprompt`, `agentproto`, empty,
527
- * or equal to the derived title), the daemon generates a short prosaic
528
- * title (4-8 words) from the first turn's transcript and renames the
529
- * session via the same write path as `session_rename`. Generation tries
530
- * OpenRouter (`model`, default `z-ai/glm-5.2@openrouter`, needs
531
- * `OPENROUTER_API_KEY`) and falls back locally (first line of the first
532
- * user prompt, 6 whole words) on any failure. A user-created label is
533
- * NEVER overwritten, and a session is titled at most once. */
534
- titler?: TitlerConfig;
535
- /** Named connection profiles. See `ProfileConfig` for the merge
536
- * semantics — a profile's fields shallow-override the top-level
537
- * defaults for the selected run. */
538
- profiles?: Record<string, ProfileConfig>;
539
- /** Profile name to use when `--profile` isn't passed. When unset,
540
- * the top-level `daemon` / `tunnel` blocks are used directly. */
541
- activeProfile?: string;
542
- /** Default `skills` + `options` auto-applied to every `agent_start`
543
- * spawn — global and per-adapter. See `resolveSpawnDefaults` in
544
- * `spawn-defaults.ts` for the merge precedence with an explicit call.
545
- * Absent ⇒ current behaviour exactly (no regression). */
546
- defaults?: SpawnDefaultsConfig;
547
- /** User-defined generic ACP agents, keyed by adapter slug. Each entry
548
- * is minted into a runnable handle by the CLI's `acpHandleFromSpec`
549
- * when `resolveAdapter(slug)` finds no npm adapter package. User
550
- * entries shadow the curated `ACP_CATALOG` on slug collision. */
551
- acpAgents?: Record<string, AcpAgentConfigEntry>;
552
- /** User-defined named terminal/TUI presets. Local-only; never
553
- * packaged in shared adapter manifests or defaults. */
554
- terminalPresets?: Record<string, TerminalPreset>;
555
- /** Unknown keys preserved across save round-trips. */
556
- [unknown: string]: unknown;
557
- }
558
- declare const CONFIG_FILE_PATH: () => string;
559
- /**
560
- * Load config.json. Returns an empty object (NOT null) when the file
561
- * is missing, malformed, or unreadable — callers can `cfg.daemon?.port`
562
- * safely without null-guards. Errors during a malformed-read are
563
- * logged once so the user notices the file is broken without the
564
- * daemon refusing to boot.
565
- */
566
- declare function loadConfig(path?: string): Promise<AgentprotoConfig>;
567
- /**
568
- * Write config.json atomically (tmp + rename) so a concurrent
569
- * `agentproto config edit` can't half-truncate the file. Writes
570
- * `next` AS-IS — callers are expected to pass the full desired
571
- * state (loaded the existing config, mutated, passed it back).
572
- *
573
- * Earlier versions deep-merged with the on-disk file, but that made
574
- * deletions impossible: `setConfigKey(cfg, "x", undefined)` would
575
- * remove the key from memory, then the deep-merge would silently
576
- * re-add it from disk. The current design trusts the caller's
577
- * snapshot and uses atomic rename for crash safety.
578
- */
579
- declare function saveConfig(next: AgentprotoConfig, path?: string): Promise<void>;
580
- /**
581
- * Read a dot-notation key (`daemon.port`) out of a config. Returns
582
- * `undefined` when any segment is missing.
583
- */
584
- declare function getConfigKey(cfg: AgentprotoConfig, dotted: string): unknown;
585
- /**
586
- * Set a dot-notation key in a config. Returns a new object — does
587
- * NOT mutate. Creates intermediate objects as needed. Setting
588
- * `value: undefined` is treated as a delete.
589
- */
590
- declare function setConfigKey(cfg: AgentprotoConfig, dotted: string, value: unknown): AgentprotoConfig;
591
-
592
- export { type AcpAgentConfigEntry, type AgentprotoConfig, type AgentsMdConfig, CONFIG_FILE_PATH, CONFIG_VERSION, type DaemonConfig, type FeaturesConfig, type PairingConfig, type ProfileConfig, type ProvenanceConfig, type SentinelConfig, type SessionsConfig, type SpawnAttachMode, type SpawnConfig, type SpawnDedupeMode, type TerminalPreset, type TitlerConfig, type TunnelConfig, type WorktreeIsolationMode, type WorktreesConfig, getConfigKey, loadConfig, saveConfig, setConfigKey };