@agentproto/runtime 5.2.1 → 5.4.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,614 +1,5 @@
1
- import { S as SpawnDefaultsConfig } from './spawn-defaults-CHvz85DD.js';
2
- import './context-continuity-BfNVbX5W.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, J as JevConfig, 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-Gz7q9yfJ.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
- /** Opt this daemon in to being usable as an `agent_start({ sandbox:
197
- * "device:<name>" })` target (DEVICES-PLAN PR-D) — exposes
198
- * `/device-spawn/*` in http-server.ts, a self-proxy onto this daemon's
199
- * own `/mcp` + `/sessions/:id/events/stream`. Default false: even with
200
- * this on, only a HOST-scoped pairing (`pair offer --host` + `devices
201
- * add`) can reach the routes — a plain remote-control pairing never can,
202
- * whatever this is set to. Toggle via `agentproto devices allow-spawn
203
- * on|off`. */
204
- deviceSpawnAllow?: boolean;
205
- }
206
- /**
207
- * Policy for `agentproto worktree new` (PLAN.md §1.4 — config carries
208
- * policy, never state; git itself is the authority for which worktrees
209
- * exist). This is the fix for the sprawl the plan measured: 31 linked
210
- * worktrees across 6 different parent directories, because there was no
211
- * `worktree new` verb and therefore no convention to converge on.
212
- */
213
- /**
214
- * How the daemon isolates a freshly-spawned agent session into its own git
215
- * worktree (`agent_start.worktree`):
216
- * - `"always"` — every depth-0 spawn is provisioned into a worktree,
217
- * whether or not the caller asked. A cwd that is not in
218
- * a git repo has nothing to isolate, so it spawns plain.
219
- * - `"on-request"` — isolate ONLY when the caller passes `worktree`. This
220
- * is the default and the back-compatible behaviour:
221
- * today's callers pass nothing and spawn exactly where
222
- * they asked.
223
- * - `"never"` — isolation is off; an explicit `worktree` field is
224
- * REJECTED (loud, not silently ignored) so a caller
225
- * never believes it got an isolated tree it didn't.
226
- */
227
- type WorktreeIsolationMode = "always" | "on-request" | "never";
228
- interface WorktreesConfig {
229
- /**
230
- * Absolute path new worktrees are created under. Layout:
231
- * `<root>/<repoName>/<slug>`. Resolution order (mirrors every other
232
- * knob in this file, see the module docblock): `--root` flag >
233
- * `AGENTPROTO_WORKTREES_ROOT` env > this field > the hardcoded default
234
- * `~/.agentproto/worktrees`. The default is a real single root, not
235
- * "unconfigured" — `worktree new` converges to one place with zero
236
- * setup, which is the only way the sprawl actually stops (the 6 roots
237
- * that exist today are 6 people each inventing a default by hand).
238
- */
239
- root?: string;
240
- /**
241
- * Policy for `agent_start.worktree` isolation. Resolution order mirrors
242
- * the module docblock (there is no CLI flag — this is a daemon-side
243
- * policy read at spawn, not a per-invocation flag):
244
- * `AGENTPROTO_WORKTREES_ISOLATION` env > this field > the hardcoded
245
- * default `"on-request"`. `"on-request"` is deliberately the default:
246
- * any other would break back-compat by isolating callers that never
247
- * asked (see `worktree-isolation.ts`).
248
- */
249
- isolation?: WorktreeIsolationMode;
250
- }
251
- /**
252
- * Policy for `agent_start.attach` — whether a spawn AUTO-attaches to its
253
- * calling session as parent lineage. `"always"` (the default) nests a
254
- * spawned child under the session that spawned it whenever that identity is
255
- * derivable (the trusted `?callerSessionId=` on the daemon self-ref URL, or
256
- * an explicit `parentSessionId` hint), so a supervisor's executors stop
257
- * landing as depth-0 orphans. `"on-request"` reverts to the pre-attach
258
- * behaviour: auto-attribution is off, and a child nests only when the caller
259
- * explicitly opts in (`attach: true` / `attach: { parent }` / an explicit
260
- * `parentSessionId`). Either way a per-call `attach: false` forces an
261
- * independent root, exactly as `worktree: false` opts out of isolation. See
262
- * `spawn-attach.ts`.
263
- */
264
- type SpawnAttachMode = "always" | "on-request";
265
- /**
266
- * Policy for `agent_start`'s IMPLICIT dedupe — what happens when a caller
267
- * spawns with NO `idempotencyKey` at all (see `spawn-dedupe.ts`):
268
- * - `"always"` — the daemon DERIVES an implicit key from the spawn's
269
- * `label` (required — see below) plus a hash of the
270
- * initial `prompt`, and dedupes a same-adapter/cwd
271
- * repeat against it exactly as an explicit key would.
272
- * This is the default, mirroring `attach`'s own
273
- * "opt-in-only guard is not a guard" precedent above.
274
- * Deriving needs a `label` to produce anything at all
275
- * — an unlabelled spawn is untouched, which is what
276
- * keeps this safe for the fan-out pattern this repo
277
- * exercises (several agents into one cwd with no
278
- * shared label): see `spawn-dedupe.ts`'s docblock for
279
- * the full false-dedup analysis, and PR #803's own
280
- * no-opt-in label+cwd warning backstop in
281
- * `session-spawn.ts`, which independently landed on
282
- * the same label-is-the-signal boundary.
283
- * - `"on-request"` — no implicit derivation; only an explicit
284
- * `idempotencyKey` dedupes (today's behaviour,
285
- * unchanged). A per-call `dedupe: true` still opts in
286
- * under this policy, mirroring `attach: true`.
287
- * Either way a per-call `dedupe: false` disables implicit derivation for
288
- * that one spawn regardless of policy — the escape hatch, mirroring
289
- * `attach: false` / `worktree: false`. An explicit `idempotencyKey` always
290
- * wins over a derived one (derivation is only attempted when the caller
291
- * supplied none).
292
- */
293
- type SpawnDedupeMode = "always" | "on-request";
294
- interface SpawnConfig {
295
- /**
296
- * Attach policy for `agent_start`. Resolution order mirrors the module
297
- * docblock (no CLI flag — a daemon-side policy read at spawn):
298
- * `AGENTPROTO_SPAWN_ATTACH` env > this field > the hardcoded default
299
- * `"always"`. Unlike `worktrees.isolation` (whose default preserves
300
- * back-compat by NOT isolating), attach defaults ON: an orphaned executor
301
- * is a bug, not a feature, and the auto-parent is descriptor-only lineage
302
- * that never relaxes a privilege gate.
303
- */
304
- attach?: SpawnAttachMode;
305
- /**
306
- * Implicit-dedupe policy for `agent_start`. Resolution order mirrors
307
- * `attach` above (no CLI flag — a daemon-side policy read at spawn):
308
- * `AGENTPROTO_SPAWN_DEDUPE` env > this field > the hardcoded default
309
- * `"always"`. See {@link SpawnDedupeMode} and `spawn-dedupe.ts` for the
310
- * full reasoning: a retry-safety guard that only works when a caller
311
- * remembers to ask for it (`idempotencyKey`) is not a guard — the same
312
- * argument `attach` already settled for parent lineage.
313
- */
314
- dedupe?: SpawnDedupeMode;
315
- }
316
- /**
317
- * Provenance policy — the opt-in `gh` PATH shim (`gh-provenance-shim.ts`).
318
- * Off by default. When `wrapGh` is on, every agent session the daemon spawns
319
- * gets a shim directory prepended to its PATH so that any `gh pr create` the
320
- * session (or an adapter subprocess shelling out — claude-code, codex, …) runs
321
- * has a deterministic `@agentproto-bot` provenance footer appended to the
322
- * created PR's BODY, matching the footer the cloud runner stamps. The TOOL
323
- * stamps, never the model; commit messages are never touched (the repo's
324
- * hygiene-check forbids attribution there). Resolution order mirrors
325
- * `spawn.attach`: `AGENTPROTO_PROVENANCE_WRAP_GH` env > this field > default
326
- * `false`.
327
- */
328
- interface ProvenanceConfig {
329
- /** Enable the opt-in `gh` provenance PATH shim for spawned sessions. */
330
- wrapGh?: boolean;
331
- }
332
- /**
333
- * Sentinel auto-link policy (AIP-60 §6, step 4) — whether a PR an executor
334
- * session opens gets a sentinel created for it automatically.
335
- */
336
- interface SentinelConfig {
337
- /** Auto-watch a PR when a session opens one (`recordOpenedPr`, fed by
338
- * `command_execute`'s stamper and both `pr-provenance-reconciler.ts`
339
- * lanes). When unset, the daemon defaults to `true` only when the
340
- * `local-gh` provider is available (i.e. `gh auth status` succeeds) —
341
- * otherwise `false`, logged once. Explicitly setting this always wins
342
- * over that probe. Per-spawn opt-out: `agent_start.sentinel: false`. */
343
- autoWatchPrs?: boolean;
344
- }
345
- /**
346
- * Review-primitive host policy (`@agentproto/review` + `review-runner.ts`).
347
- */
348
- interface ReviewConfig {
349
- /** The owner identity attestation signatures claim (`attestor.signature.
350
- * principal`) — see `review-signing.ts`'s `resolvePrincipal`. When unset,
351
- * the daemon falls back to `git config user.email` of the reviewed repo,
352
- * then a host-derived value; this never blocks signing, it only lets an
353
- * operator pin one identity across every repo the daemon reviews. */
354
- principal?: string;
355
- }
356
- /**
357
- * Session-presence policy — how the dashboard triages a session into its
358
- * `running` / `tending` / `attention` / `quiet` presence state (see
359
- * `session-presence.ts`). Optional block on `AgentprotoConfig.sessions`.
360
- */
361
- interface SessionsConfig {
362
- /**
363
- * How long (seconds) a session stays shown as `running` after its last
364
- * turn ends, before it settles into `attention`/`quiet`. The grace window
365
- * is measured from the session's most recent `lastActivityAt` — ANY new
366
- * event resets it — so a session that just answered does not read as
367
- * "quiet" the instant its turn finishes, but something that went idle long
368
- * ago, with nothing waiting on it, settles grey. Resolution order mirrors
369
- * every other knob in this file (module docblock): env var
370
- * `AGENTPROTO_SESSIONS_ATTENTION_DELAY_SEC` > this field > the hardcoded
371
- * default `60`. Absent ⇒ 60s.
372
- */
373
- attentionDelaySec?: number;
374
- /**
375
- * Root directory the daemon stores per-session transcripts under — each
376
- * session's `events.jsonl` lands at `<eventsDir>/<sessionId>/events.jsonl`
377
- * (and a terminal session's `terminal.jsonl` alongside it). Every
378
- * reader/writer (transcript writer, `/sessions/:id/events` HTTP routes,
379
- * exports, tool-call/usage logs) resolves through this single knob.
380
- * Resolution order mirrors every other knob in this file: relative paths
381
- * resolve against the home directory. Absent ⇒ the hardcoded default
382
- * `~/.agentproto/sessions` — zero behaviour change out of the box. A
383
- * change requires a daemon restart to take effect (and existing sessions'
384
- * transcripts do NOT move).
385
- */
386
- eventsDir?: string;
387
- }
388
- /**
389
- * Policy for the daemon's own AGENTS.md resolution + injection (WP-R2) —
390
- * how a spawned session's initial prompt carries the repo's `AGENTS.md`
391
- * (see `agents-md.ts`). The daemon resolves the nearest `AGENTS.md` walking
392
- * up from the session's `cwd`, bounded by that repo's git toplevel, and
393
- * injects one AGENTS.md block after the role disposition whenever the spawn
394
- * carries a prompt.
395
- */
396
- interface AgentsMdConfig {
397
- /**
398
- * Inline-vs-pointer threshold in KiB. A resolved AGENTS.md whose byte size
399
- * is STRICTLY LESS than `inlineMaxKb` KiB is inlined in full into the
400
- * spawn's initial prompt, clearly delimited; `>=` the threshold is injected
401
- * as a short pointer telling the agent to read the file before its first
402
- * tool call instead. An absent AGENTS.md (nothing found up the walk) injects
403
- * nothing but is still reported on the descriptor via
404
- * `SessionDescriptor.agentsMdMode === "absent"`. Resolution order mirrors
405
- * the module docblock (no CLI flag, and no env override — not part of the
406
- * approved design): this field > the hardcoded default `8` (KiB).
407
- */
408
- inlineMaxKb?: number;
409
- }
410
- interface PairingConfig {
411
- /** Rendezvous broker WS URL (ws:// or wss://) used by `pair offer` and by
412
- * autoconnect on boot. When unset, `pair offer` requires an explicit
413
- * `--rendezvous`. Mirrors `tunnel.host`. */
414
- rendezvous?: string;
415
- /** Whether the daemon opens standing rendezvous connections for every
416
- * persisted pairing on boot (so a paired client can reconnect anytime).
417
- * Mirrors `tunnel.autoconnect`. Default true when a rendezvous is set. */
418
- autoconnect?: boolean;
419
- /** Web pair page for `pair offer --qr` (the offer rides in its fragment).
420
- * A template with `{fp}` in the HOSTNAME, filled with the daemon
421
- * fingerprint (one origin per daemon), or a plain http(s) URL (one shared
422
- * origin). Unset → `https://{fp}.agentproto.cloud/pair`. `--pair-page`
423
- * overrides it. */
424
- pairPage?: string;
425
- }
426
- /**
427
- * A user-defined generic ACP agent — the config-file half of
428
- * `AcpAgentSpec` (the slug is the record key in `acpAgents`, so it's
429
- * omitted here). Any CLI that already speaks the Agent Client Protocol
430
- * can be wired with zero code by declaring one of these under
431
- * `acpAgents.<slug>` in `~/.agentproto/config.json`; the CLI's
432
- * `acpHandleFromSpec` mints a runnable `AgentCliHandle` from it at
433
- * resolve time (see `packages/cli/src/registry/acp-generic.ts`). Kept
434
- * in this package (not the CLI's) so `config.ts` stays the single
435
- * source of truth for the config surface without a cli→runtime→cli
436
- * import cycle — the CLI's `AcpAgentSpec` extends this shape.
437
- */
438
- interface AcpAgentConfigEntry {
439
- /** Display name. Defaults to the slug when omitted. */
440
- name?: string;
441
- /** One-line description surfaced in `agentproto acp ls`. */
442
- description?: string;
443
- /** Executable to spawn, e.g. "gemini". */
444
- bin: string;
445
- /** Extra argv appended after `bin`, e.g. ["--experimental-acp"]. */
446
- bin_args?: string[];
447
- /** Extra environment variables for the spawned process. */
448
- env?: Record<string, string>;
449
- /** Flag the CLI uses to receive the working directory, if it needs
450
- * one passed explicitly (most ACP agents take cwd over the wire). */
451
- cwd_flag?: string;
452
- /** When true, advertise resumable + native-resume continuation. */
453
- resumable?: boolean;
454
- /** Known model ids for the agent (informational + validation hints). */
455
- models?: {
456
- default?: string;
457
- allowed?: string[];
458
- };
459
- /** Billing endpoint this CLI's own auth bills (e.g. "mistral",
460
- * "moonshot", "google") — the provider whose wallet/auth-profile the
461
- * agent consumes. Lets clients link the harness to that provider's
462
- * wallets even when no model list is declared. Unset when the CLI's
463
- * billing target isn't a single known endpoint. */
464
- provider?: string;
465
- /** Shown when `bin` is missing from PATH (how to install the CLI). */
466
- install_hint?: string;
467
- }
468
- /**
469
- * Per-environment connection bundle. A profile overrides specific
470
- * fields of the top-level `daemon` / `tunnel` / `features` blocks
471
- * when selected via `--profile <name>` (or the top-level
472
- * `activeProfile` setting). Missing fields fall through to the
473
- * top-level config, so a profile only needs to declare what's
474
- * different — typically just `tunnel.host` + `tunnel.token`.
475
- *
476
- * Example:
477
- * {
478
- * "daemon": { "workspace": "/code", "port": 18790 },
479
- * "activeProfile": "local",
480
- * "profiles": {
481
- * "local": { "tunnel": { "host": "ws://localhost:3200/connect",
482
- * "token": "apt_local", "autoconnect": true } },
483
- * "prod": { "tunnel": { "host": "wss://tunnel.guilde.work/connect",
484
- * "token": "apt_prod", "autoconnect": true },
485
- * "daemon": { "port": 18791 } }
486
- * }
487
- * }
488
- *
489
- * Sandbox daemons generate per-sandbox profile entries at provision
490
- * time so the daemon inside the sandbox boots with
491
- * `agentproto serve --profile sandbox-<id>` and no extra plumbing.
492
- */
493
- interface ProfileConfig {
494
- daemon?: DaemonConfig;
495
- tunnel?: TunnelConfig;
496
- features?: FeaturesConfig;
497
- }
498
- /**
499
- * A user-defined named terminal/TUI preset stored in
500
- * `~/.agentproto/config.json` under `terminalPresets`. Presets keep
501
- * local launch recipes (argv, env, cwd, name/label) out of shared
502
- * adapter manifests — e.g. pointing a Claude Code TUI at a local
503
- * LLM gateway without retyping proxy env vars every spawn.
504
- */
505
- interface TerminalPreset {
506
- /** Command + args to spawn. When provided, `sessions terminal` can
507
- * be used without `-- <argv...>`. */
508
- argv?: string[];
509
- /** Extra environment variables layered on top of the daemon's
510
- * inherited process.env. Values MUST be strings. */
511
- env?: Record<string, string>;
512
- /** Working directory for the PTY session. Relative paths are
513
- * resolved against the current working directory at CLI time. */
514
- cwd?: string;
515
- /** Workspace slug used for cwd fallback when `cwd` is omitted. */
516
- workspace?: string;
517
- /** Stable session name passed to the registry (`name` field). */
518
- name?: string;
519
- /** Human-readable label surfaced in session listings. */
520
- label?: string;
521
- }
522
- interface AgentprotoConfig {
523
- version?: number;
524
- daemon?: DaemonConfig;
525
- tunnel?: TunnelConfig;
526
- features?: FeaturesConfig;
527
- /** E2E daemon-pairing defaults (rendezvous URL + autoconnect). */
528
- pairing?: PairingConfig;
529
- /** Where `agentproto worktree new` creates worktrees. See
530
- * {@link WorktreesConfig}. */
531
- worktrees?: WorktreesConfig;
532
- /** Spawn-time policy (`agent_start`). See {@link SpawnConfig}. */
533
- spawn?: SpawnConfig;
534
- /** Session-presence policy for the dashboard. See {@link SessionsConfig}. */
535
- sessions?: SessionsConfig;
536
- /** Provenance policy — the opt-in `gh` PATH shim. See {@link ProvenanceConfig}. */
537
- provenance?: ProvenanceConfig;
538
- /** Sentinel auto-link policy. See {@link SentinelConfig}. */
539
- sentinel?: SentinelConfig;
540
- /** Review-primitive host policy. See {@link ReviewConfig}. */
541
- review?: ReviewConfig;
542
- /** Daemon-side AGENTS.md resolution/injection policy. See {@link AgentsMdConfig}. */
543
- agentsMd?: AgentsMdConfig;
544
- /** Daemon-side session titler (`session-titler.ts`). DEFAULT OFF — when
545
- * `enabled` is not explicitly true the titler is a complete no-op and
546
- * nothing renames anything. When on, at the end of the FIRST completed
547
- * turn of a `kind:"agent-cli"` session whose label is still a spawn
548
- * default (`chat-starter`, `chat-starter-autoprompt`, `agentproto`, empty,
549
- * or equal to the derived title), the daemon generates a short prosaic
550
- * title (4-8 words) from the first turn's transcript and renames the
551
- * session via the same write path as `session_rename`. Generation tries
552
- * OpenRouter (`model`, default `z-ai/glm-5.2@openrouter`, needs
553
- * `OPENROUTER_API_KEY`) and falls back locally (first line of the first
554
- * user prompt, 6 whole words) on any failure. A user-created label is
555
- * NEVER overwritten, and a session is titled at most once. */
556
- titler?: TitlerConfig;
557
- /** Named connection profiles. See `ProfileConfig` for the merge
558
- * semantics — a profile's fields shallow-override the top-level
559
- * defaults for the selected run. */
560
- profiles?: Record<string, ProfileConfig>;
561
- /** Profile name to use when `--profile` isn't passed. When unset,
562
- * the top-level `daemon` / `tunnel` blocks are used directly. */
563
- activeProfile?: string;
564
- /** Default `skills` + `options` auto-applied to every `agent_start`
565
- * spawn — global and per-adapter. See `resolveSpawnDefaults` in
566
- * `spawn-defaults.ts` for the merge precedence with an explicit call.
567
- * Absent ⇒ current behaviour exactly (no regression). */
568
- defaults?: SpawnDefaultsConfig;
569
- /** User-defined generic ACP agents, keyed by adapter slug. Each entry
570
- * is minted into a runnable handle by the CLI's `acpHandleFromSpec`
571
- * when `resolveAdapter(slug)` finds no npm adapter package. User
572
- * entries shadow the curated `ACP_CATALOG` on slug collision. */
573
- acpAgents?: Record<string, AcpAgentConfigEntry>;
574
- /** User-defined named terminal/TUI presets. Local-only; never
575
- * packaged in shared adapter manifests or defaults. */
576
- terminalPresets?: Record<string, TerminalPreset>;
577
- /** Unknown keys preserved across save round-trips. */
578
- [unknown: string]: unknown;
579
- }
580
- declare const CONFIG_FILE_PATH: () => string;
581
- /**
582
- * Load config.json. Returns an empty object (NOT null) when the file
583
- * is missing, malformed, or unreadable — callers can `cfg.daemon?.port`
584
- * safely without null-guards. Errors during a malformed-read are
585
- * logged once so the user notices the file is broken without the
586
- * daemon refusing to boot.
587
- */
588
- declare function loadConfig(path?: string): Promise<AgentprotoConfig>;
589
- /**
590
- * Write config.json atomically (tmp + rename) so a concurrent
591
- * `agentproto config edit` can't half-truncate the file. Writes
592
- * `next` AS-IS — callers are expected to pass the full desired
593
- * state (loaded the existing config, mutated, passed it back).
594
- *
595
- * Earlier versions deep-merged with the on-disk file, but that made
596
- * deletions impossible: `setConfigKey(cfg, "x", undefined)` would
597
- * remove the key from memory, then the deep-merge would silently
598
- * re-add it from disk. The current design trusts the caller's
599
- * snapshot and uses atomic rename for crash safety.
600
- */
601
- declare function saveConfig(next: AgentprotoConfig, path?: string): Promise<void>;
602
- /**
603
- * Read a dot-notation key (`daemon.port`) out of a config. Returns
604
- * `undefined` when any segment is missing.
605
- */
606
- declare function getConfigKey(cfg: AgentprotoConfig, dotted: string): unknown;
607
- /**
608
- * Set a dot-notation key in a config. Returns a new object — does
609
- * NOT mutate. Creates intermediate objects as needed. Setting
610
- * `value: undefined` is treated as a delete.
611
- */
612
- declare function setConfigKey(cfg: AgentprotoConfig, dotted: string, value: unknown): AgentprotoConfig;
613
-
614
- export { type AcpAgentConfigEntry, type AgentprotoConfig, type AgentsMdConfig, CONFIG_FILE_PATH, CONFIG_VERSION, type DaemonConfig, type FeaturesConfig, type PairingConfig, type ProfileConfig, type ProvenanceConfig, type ReviewConfig, type SentinelConfig, type SessionsConfig, type SpawnAttachMode, type SpawnConfig, type SpawnDedupeMode, type TerminalPreset, type TitlerConfig, type TunnelConfig, type WorktreeIsolationMode, type WorktreesConfig, getConfigKey, loadConfig, saveConfig, setConfigKey };