@agentproto/runtime 0.7.0 → 0.8.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/README.md +1 -0
- package/dist/config-BRKy_SAF.d.ts +569 -0
- package/dist/config.d.ts +1 -272
- package/dist/config.mjs.map +1 -1
- package/dist/index.d.ts +154 -3
- package/dist/index.mjs +191 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -9
- package/dist/spawn-defaults-DAbADRd4.d.ts +0 -256
package/dist/config.d.ts
CHANGED
|
@@ -1,273 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
export { i as AcpAgentConfigEntry, j as AgentprotoConfig, k as CONFIG_FILE_PATH, l as CONFIG_VERSION, m as DaemonConfig, F as FeaturesConfig, P as PairingConfig, o as ProfileConfig, T as TerminalPreset, p as TunnelConfig, W as WorktreeIsolationMode, q as WorktreesConfig, s as getConfigKey, t as loadConfig, u as saveConfig, v as setConfigKey } from './config-BRKy_SAF.js';
|
|
2
2
|
import '@agentproto/model-catalog';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* `~/.agentproto/config.json` — single hand-editable JSON for the
|
|
6
|
-
* agentproto control plane's defaults. Sits alongside the existing
|
|
7
|
-
* surface files (workspaces.json, credentials.json, sessions.json):
|
|
8
|
-
*
|
|
9
|
-
* workspaces.json which directories are workspaces + which is active
|
|
10
|
-
* credentials.json tunnel host bearer tokens (mode 0600)
|
|
11
|
-
* sessions.json last-known snapshot of the registry (informational)
|
|
12
|
-
* config.json daemon defaults: port, bind, allowed origins,
|
|
13
|
-
* tunnel host, feature toggles
|
|
14
|
-
*
|
|
15
|
-
* Resolution order for every daemon knob is:
|
|
16
|
-
* 1. CLI flag (e.g. --port)
|
|
17
|
-
* 2. Env var (where one exists, e.g. AGENTPROTO_TOKEN)
|
|
18
|
-
* 3. config.json
|
|
19
|
-
* 4. Hardcoded default
|
|
20
|
-
*
|
|
21
|
-
* This means a user can call `agentproto config set daemon.port 18791`
|
|
22
|
-
* once and never re-pass `--port 18791` to `serve install` etc. CLI
|
|
23
|
-
* flags still win for one-off overrides.
|
|
24
|
-
*
|
|
25
|
-
* Schema is intentionally narrow + extensible — unknown keys are
|
|
26
|
-
* preserved on save (deep-merge), so a newer CLI writing a new
|
|
27
|
-
* field won't drop one an older CLI doesn't know about. No secrets
|
|
28
|
-
* here; credentials stay in credentials.json (mode 0600).
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
declare const CONFIG_VERSION: 1;
|
|
32
|
-
interface DaemonConfig {
|
|
33
|
-
/** Absolute path to the workspace the daemon binds to at boot. */
|
|
34
|
-
workspace?: string;
|
|
35
|
-
/** HTTP port. Default 18790. */
|
|
36
|
-
port?: number;
|
|
37
|
-
/** Bind addr. Default 127.0.0.1. */
|
|
38
|
-
bind?: string;
|
|
39
|
-
/** Trusted browser origins for mutating /sessions/* routes (in
|
|
40
|
-
* addition to the hardcoded localhost defaults). */
|
|
41
|
-
allowedOrigins?: string[];
|
|
42
|
-
/** When true, the daemon does NOT auto-trust localhost-on-any-port.
|
|
43
|
-
* Only origins explicitly listed in `allowedOrigins` are allowed.
|
|
44
|
-
* Pair with a curated list (e.g. `["http://localhost:3000"]`) for
|
|
45
|
-
* hardened setups. Default false. */
|
|
46
|
-
strictOrigins?: boolean;
|
|
47
|
-
/** Server label sent in tunnel hello frames. */
|
|
48
|
-
label?: string;
|
|
49
|
-
/** Bearer token gating the gateway at boot (`AuthOptions` with
|
|
50
|
-
* `mode: "bearer"`). Unlike `remote_enable`'s ephemeral quick-tunnel
|
|
51
|
-
* token, this one lives in config.json and survives daemon restarts.
|
|
52
|
-
* Set via `agentproto config set daemon.authToken <token>` (e.g.
|
|
53
|
-
* `$(openssl rand -hex 32)`). Unset ⇒ the gateway boots with
|
|
54
|
-
* `mode: "none"` — fully open on loopback, same as today. */
|
|
55
|
-
authToken?: string;
|
|
56
|
-
}
|
|
57
|
-
interface TunnelConfig {
|
|
58
|
-
/** Cloud WS URL. When set + autoconnect=true, `agentproto serve`
|
|
59
|
-
* bootstraps with `--connect <host>`. */
|
|
60
|
-
host?: string;
|
|
61
|
-
/** apt_ daemon token to present at the tunnel upgrade. When set,
|
|
62
|
-
* `agentproto serve` uses this BEFORE falling back to
|
|
63
|
-
* credentials.json — handy in profiles where the token-per-host
|
|
64
|
-
* mapping in credentials.json doesn't fit (e.g. host = tunnel URL
|
|
65
|
-
* but credentials were minted against the api URL). */
|
|
66
|
-
token?: string;
|
|
67
|
-
/** Whether `agentproto daemon start` connects the tunnel by
|
|
68
|
-
* default. v0 only — implementer can ignore until daemon needs it. */
|
|
69
|
-
autoconnect?: boolean;
|
|
70
|
-
/**
|
|
71
|
-
* Opt into end-to-end encryption of the outbound `serve --connect` tunnel
|
|
72
|
-
* (design: tunnel-e2e/v1). When true, the daemon negotiates a
|
|
73
|
-
* token-authenticated ephemeral handshake with the host and wraps the tunnel
|
|
74
|
-
* frames in an AEAD box, so even the trusted host loses plaintext visibility.
|
|
75
|
-
* The handshake authenticates both ends against the shared `tunnel.token`, so
|
|
76
|
-
* `token` MUST also be set. Fully backward-compatible: if the host doesn't
|
|
77
|
-
* advertise e2e (an older host), the daemon falls back to today's plaintext
|
|
78
|
-
* tunnel. Unset/false ⇒ plaintext, byte-identical to today. */
|
|
79
|
-
e2e?: boolean;
|
|
80
|
-
}
|
|
81
|
-
interface FeaturesConfig {
|
|
82
|
-
/** Hint that PTY is desired — informational; the daemon still
|
|
83
|
-
* detects node-pty's presence at runtime. */
|
|
84
|
-
pty?: boolean;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Policy for `agentproto worktree new` (PLAN.md §1.4 — config carries
|
|
88
|
-
* policy, never state; git itself is the authority for which worktrees
|
|
89
|
-
* exist). This is the fix for the sprawl the plan measured: 31 linked
|
|
90
|
-
* worktrees across 6 different parent directories, because there was no
|
|
91
|
-
* `worktree new` verb and therefore no convention to converge on.
|
|
92
|
-
*/
|
|
93
|
-
interface WorktreesConfig {
|
|
94
|
-
/**
|
|
95
|
-
* Absolute path new worktrees are created under. Layout:
|
|
96
|
-
* `<root>/<repoName>/<slug>`. Resolution order (mirrors every other
|
|
97
|
-
* knob in this file, see the module docblock): `--root` flag >
|
|
98
|
-
* `AGENTPROTO_WORKTREES_ROOT` env > this field > the hardcoded default
|
|
99
|
-
* `~/.agentproto/worktrees`. The default is a real single root, not
|
|
100
|
-
* "unconfigured" — `worktree new` converges to one place with zero
|
|
101
|
-
* setup, which is the only way the sprawl actually stops (the 6 roots
|
|
102
|
-
* that exist today are 6 people each inventing a default by hand).
|
|
103
|
-
*/
|
|
104
|
-
root?: string;
|
|
105
|
-
}
|
|
106
|
-
interface PairingConfig {
|
|
107
|
-
/** Rendezvous broker WS URL (ws:// or wss://) used by `pair offer` and by
|
|
108
|
-
* autoconnect on boot. When unset, `pair offer` requires an explicit
|
|
109
|
-
* `--rendezvous`. Mirrors `tunnel.host`. */
|
|
110
|
-
rendezvous?: string;
|
|
111
|
-
/** Whether the daemon opens standing rendezvous connections for every
|
|
112
|
-
* persisted pairing on boot (so a paired client can reconnect anytime).
|
|
113
|
-
* Mirrors `tunnel.autoconnect`. Default true when a rendezvous is set. */
|
|
114
|
-
autoconnect?: boolean;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* A user-defined generic ACP agent — the config-file half of
|
|
118
|
-
* `AcpAgentSpec` (the slug is the record key in `acpAgents`, so it's
|
|
119
|
-
* omitted here). Any CLI that already speaks the Agent Client Protocol
|
|
120
|
-
* can be wired with zero code by declaring one of these under
|
|
121
|
-
* `acpAgents.<slug>` in `~/.agentproto/config.json`; the CLI's
|
|
122
|
-
* `acpHandleFromSpec` mints a runnable `AgentCliHandle` from it at
|
|
123
|
-
* resolve time (see `packages/cli/src/registry/acp-generic.ts`). Kept
|
|
124
|
-
* in this package (not the CLI's) so `config.ts` stays the single
|
|
125
|
-
* source of truth for the config surface without a cli→runtime→cli
|
|
126
|
-
* import cycle — the CLI's `AcpAgentSpec` extends this shape.
|
|
127
|
-
*/
|
|
128
|
-
interface AcpAgentConfigEntry {
|
|
129
|
-
/** Display name. Defaults to the slug when omitted. */
|
|
130
|
-
name?: string;
|
|
131
|
-
/** One-line description surfaced in `agentproto acp ls`. */
|
|
132
|
-
description?: string;
|
|
133
|
-
/** Executable to spawn, e.g. "gemini". */
|
|
134
|
-
bin: string;
|
|
135
|
-
/** Extra argv appended after `bin`, e.g. ["--experimental-acp"]. */
|
|
136
|
-
bin_args?: string[];
|
|
137
|
-
/** Extra environment variables for the spawned process. */
|
|
138
|
-
env?: Record<string, string>;
|
|
139
|
-
/** Flag the CLI uses to receive the working directory, if it needs
|
|
140
|
-
* one passed explicitly (most ACP agents take cwd over the wire). */
|
|
141
|
-
cwd_flag?: string;
|
|
142
|
-
/** When true, advertise resumable + native-resume continuation. */
|
|
143
|
-
resumable?: boolean;
|
|
144
|
-
/** Known model ids for the agent (informational + validation hints). */
|
|
145
|
-
models?: {
|
|
146
|
-
default?: string;
|
|
147
|
-
allowed?: string[];
|
|
148
|
-
};
|
|
149
|
-
/** Shown when `bin` is missing from PATH (how to install the CLI). */
|
|
150
|
-
install_hint?: string;
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Per-environment connection bundle. A profile overrides specific
|
|
154
|
-
* fields of the top-level `daemon` / `tunnel` / `features` blocks
|
|
155
|
-
* when selected via `--profile <name>` (or the top-level
|
|
156
|
-
* `activeProfile` setting). Missing fields fall through to the
|
|
157
|
-
* top-level config, so a profile only needs to declare what's
|
|
158
|
-
* different — typically just `tunnel.host` + `tunnel.token`.
|
|
159
|
-
*
|
|
160
|
-
* Example:
|
|
161
|
-
* {
|
|
162
|
-
* "daemon": { "workspace": "/code", "port": 18790 },
|
|
163
|
-
* "activeProfile": "local",
|
|
164
|
-
* "profiles": {
|
|
165
|
-
* "local": { "tunnel": { "host": "ws://localhost:3200/connect",
|
|
166
|
-
* "token": "apt_local", "autoconnect": true } },
|
|
167
|
-
* "prod": { "tunnel": { "host": "wss://tunnel.guilde.work/connect",
|
|
168
|
-
* "token": "apt_prod", "autoconnect": true },
|
|
169
|
-
* "daemon": { "port": 18791 } }
|
|
170
|
-
* }
|
|
171
|
-
* }
|
|
172
|
-
*
|
|
173
|
-
* Sandbox daemons generate per-sandbox profile entries at provision
|
|
174
|
-
* time so the daemon inside the sandbox boots with
|
|
175
|
-
* `agentproto serve --profile sandbox-<id>` and no extra plumbing.
|
|
176
|
-
*/
|
|
177
|
-
interface ProfileConfig {
|
|
178
|
-
daemon?: DaemonConfig;
|
|
179
|
-
tunnel?: TunnelConfig;
|
|
180
|
-
features?: FeaturesConfig;
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* A user-defined named terminal/TUI preset stored in
|
|
184
|
-
* `~/.agentproto/config.json` under `terminalPresets`. Presets keep
|
|
185
|
-
* local launch recipes (argv, env, cwd, name/label) out of shared
|
|
186
|
-
* adapter manifests — e.g. pointing a Claude Code TUI at a local
|
|
187
|
-
* LLM gateway without retyping proxy env vars every spawn.
|
|
188
|
-
*/
|
|
189
|
-
interface TerminalPreset {
|
|
190
|
-
/** Command + args to spawn. When provided, `sessions terminal` can
|
|
191
|
-
* be used without `-- <argv...>`. */
|
|
192
|
-
argv?: string[];
|
|
193
|
-
/** Extra environment variables layered on top of the daemon's
|
|
194
|
-
* inherited process.env. Values MUST be strings. */
|
|
195
|
-
env?: Record<string, string>;
|
|
196
|
-
/** Working directory for the PTY session. Relative paths are
|
|
197
|
-
* resolved against the current working directory at CLI time. */
|
|
198
|
-
cwd?: string;
|
|
199
|
-
/** Workspace slug used for cwd fallback when `cwd` is omitted. */
|
|
200
|
-
workspace?: string;
|
|
201
|
-
/** Stable session name passed to the registry (`name` field). */
|
|
202
|
-
name?: string;
|
|
203
|
-
/** Human-readable label surfaced in session listings. */
|
|
204
|
-
label?: string;
|
|
205
|
-
}
|
|
206
|
-
interface AgentprotoConfig {
|
|
207
|
-
version?: number;
|
|
208
|
-
daemon?: DaemonConfig;
|
|
209
|
-
tunnel?: TunnelConfig;
|
|
210
|
-
features?: FeaturesConfig;
|
|
211
|
-
/** E2E daemon-pairing defaults (rendezvous URL + autoconnect). */
|
|
212
|
-
pairing?: PairingConfig;
|
|
213
|
-
/** Where `agentproto worktree new` creates worktrees. See
|
|
214
|
-
* {@link WorktreesConfig}. */
|
|
215
|
-
worktrees?: WorktreesConfig;
|
|
216
|
-
/** Named connection profiles. See `ProfileConfig` for the merge
|
|
217
|
-
* semantics — a profile's fields shallow-override the top-level
|
|
218
|
-
* defaults for the selected run. */
|
|
219
|
-
profiles?: Record<string, ProfileConfig>;
|
|
220
|
-
/** Profile name to use when `--profile` isn't passed. When unset,
|
|
221
|
-
* the top-level `daemon` / `tunnel` blocks are used directly. */
|
|
222
|
-
activeProfile?: string;
|
|
223
|
-
/** Default `skills` + `options` auto-applied to every `agent_start`
|
|
224
|
-
* spawn — global and per-adapter. See `resolveSpawnDefaults` in
|
|
225
|
-
* `spawn-defaults.ts` for the merge precedence with an explicit call.
|
|
226
|
-
* Absent ⇒ current behaviour exactly (no regression). */
|
|
227
|
-
defaults?: SpawnDefaultsConfig;
|
|
228
|
-
/** User-defined generic ACP agents, keyed by adapter slug. Each entry
|
|
229
|
-
* is minted into a runnable handle by the CLI's `acpHandleFromSpec`
|
|
230
|
-
* when `resolveAdapter(slug)` finds no npm adapter package. User
|
|
231
|
-
* entries shadow the curated `ACP_CATALOG` on slug collision. */
|
|
232
|
-
acpAgents?: Record<string, AcpAgentConfigEntry>;
|
|
233
|
-
/** User-defined named terminal/TUI presets. Local-only; never
|
|
234
|
-
* packaged in shared adapter manifests or defaults. */
|
|
235
|
-
terminalPresets?: Record<string, TerminalPreset>;
|
|
236
|
-
/** Unknown keys preserved across save round-trips. */
|
|
237
|
-
[unknown: string]: unknown;
|
|
238
|
-
}
|
|
239
|
-
declare const CONFIG_FILE_PATH: () => string;
|
|
240
|
-
/**
|
|
241
|
-
* Load config.json. Returns an empty object (NOT null) when the file
|
|
242
|
-
* is missing, malformed, or unreadable — callers can `cfg.daemon?.port`
|
|
243
|
-
* safely without null-guards. Errors during a malformed-read are
|
|
244
|
-
* logged once so the user notices the file is broken without the
|
|
245
|
-
* daemon refusing to boot.
|
|
246
|
-
*/
|
|
247
|
-
declare function loadConfig(path?: string): Promise<AgentprotoConfig>;
|
|
248
|
-
/**
|
|
249
|
-
* Write config.json atomically (tmp + rename) so a concurrent
|
|
250
|
-
* `agentproto config edit` can't half-truncate the file. Writes
|
|
251
|
-
* `next` AS-IS — callers are expected to pass the full desired
|
|
252
|
-
* state (loaded the existing config, mutated, passed it back).
|
|
253
|
-
*
|
|
254
|
-
* Earlier versions deep-merged with the on-disk file, but that made
|
|
255
|
-
* deletions impossible: `setConfigKey(cfg, "x", undefined)` would
|
|
256
|
-
* remove the key from memory, then the deep-merge would silently
|
|
257
|
-
* re-add it from disk. The current design trusts the caller's
|
|
258
|
-
* snapshot and uses atomic rename for crash safety.
|
|
259
|
-
*/
|
|
260
|
-
declare function saveConfig(next: AgentprotoConfig, path?: string): Promise<void>;
|
|
261
|
-
/**
|
|
262
|
-
* Read a dot-notation key (`daemon.port`) out of a config. Returns
|
|
263
|
-
* `undefined` when any segment is missing.
|
|
264
|
-
*/
|
|
265
|
-
declare function getConfigKey(cfg: AgentprotoConfig, dotted: string): unknown;
|
|
266
|
-
/**
|
|
267
|
-
* Set a dot-notation key in a config. Returns a new object — does
|
|
268
|
-
* NOT mutate. Creates intermediate objects as needed. Setting
|
|
269
|
-
* `value: undefined` is treated as a delete.
|
|
270
|
-
*/
|
|
271
|
-
declare function setConfigKey(cfg: AgentprotoConfig, dotted: string, value: unknown): AgentprotoConfig;
|
|
272
|
-
|
|
273
|
-
export { type AcpAgentConfigEntry, type AgentprotoConfig, CONFIG_FILE_PATH, CONFIG_VERSION, type DaemonConfig, type FeaturesConfig, type PairingConfig, type ProfileConfig, type TerminalPreset, type TunnelConfig, type WorktreesConfig, getConfigKey, loadConfig, saveConfig, setConfigKey };
|
package/dist/config.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config.ts"],"names":["fs"],"mappings":"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","file":"config.mjs","sourcesContent":["/**\n * `~/.agentproto/config.json` — single hand-editable JSON for the\n * agentproto control plane's defaults. Sits alongside the existing\n * surface files (workspaces.json, credentials.json, sessions.json):\n *\n * workspaces.json which directories are workspaces + which is active\n * credentials.json tunnel host bearer tokens (mode 0600)\n * sessions.json last-known snapshot of the registry (informational)\n * config.json daemon defaults: port, bind, allowed origins,\n * tunnel host, feature toggles\n *\n * Resolution order for every daemon knob is:\n * 1. CLI flag (e.g. --port)\n * 2. Env var (where one exists, e.g. AGENTPROTO_TOKEN)\n * 3. config.json\n * 4. Hardcoded default\n *\n * This means a user can call `agentproto config set daemon.port 18791`\n * once and never re-pass `--port 18791` to `serve install` etc. CLI\n * flags still win for one-off overrides.\n *\n * Schema is intentionally narrow + extensible — unknown keys are\n * preserved on save (deep-merge), so a newer CLI writing a new\n * field won't drop one an older CLI doesn't know about. No secrets\n * here; credentials stay in credentials.json (mode 0600).\n */\n\nimport { promises as fs } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { dirname, join } from \"node:path\"\nimport type { SpawnDefaultsConfig } from \"./spawn-defaults.js\"\n\nexport const CONFIG_VERSION = 1 as const\n\nexport interface DaemonConfig {\n /** Absolute path to the workspace the daemon binds to at boot. */\n workspace?: string\n /** HTTP port. Default 18790. */\n port?: number\n /** Bind addr. Default 127.0.0.1. */\n bind?: string\n /** Trusted browser origins for mutating /sessions/* routes (in\n * addition to the hardcoded localhost defaults). */\n allowedOrigins?: string[]\n /** When true, the daemon does NOT auto-trust localhost-on-any-port.\n * Only origins explicitly listed in `allowedOrigins` are allowed.\n * Pair with a curated list (e.g. `[\"http://localhost:3000\"]`) for\n * hardened setups. Default false. */\n strictOrigins?: boolean\n /** Server label sent in tunnel hello frames. */\n label?: string\n /** Bearer token gating the gateway at boot (`AuthOptions` with\n * `mode: \"bearer\"`). Unlike `remote_enable`'s ephemeral quick-tunnel\n * token, this one lives in config.json and survives daemon restarts.\n * Set via `agentproto config set daemon.authToken <token>` (e.g.\n * `$(openssl rand -hex 32)`). Unset ⇒ the gateway boots with\n * `mode: \"none\"` — fully open on loopback, same as today. */\n authToken?: string\n}\n\nexport interface TunnelConfig {\n /** Cloud WS URL. When set + autoconnect=true, `agentproto serve`\n * bootstraps with `--connect <host>`. */\n host?: string\n /** apt_ daemon token to present at the tunnel upgrade. When set,\n * `agentproto serve` uses this BEFORE falling back to\n * credentials.json — handy in profiles where the token-per-host\n * mapping in credentials.json doesn't fit (e.g. host = tunnel URL\n * but credentials were minted against the api URL). */\n token?: string\n /** Whether `agentproto daemon start` connects the tunnel by\n * default. v0 only — implementer can ignore until daemon needs it. */\n autoconnect?: boolean\n /**\n * Opt into end-to-end encryption of the outbound `serve --connect` tunnel\n * (design: tunnel-e2e/v1). When true, the daemon negotiates a\n * token-authenticated ephemeral handshake with the host and wraps the tunnel\n * frames in an AEAD box, so even the trusted host loses plaintext visibility.\n * The handshake authenticates both ends against the shared `tunnel.token`, so\n * `token` MUST also be set. Fully backward-compatible: if the host doesn't\n * advertise e2e (an older host), the daemon falls back to today's plaintext\n * tunnel. Unset/false ⇒ plaintext, byte-identical to today. */\n e2e?: boolean\n}\n\nexport interface FeaturesConfig {\n /** Hint that PTY is desired — informational; the daemon still\n * detects node-pty's presence at runtime. */\n pty?: boolean\n}\n\n/**\n * Policy for `agentproto worktree new` (PLAN.md §1.4 — config carries\n * policy, never state; git itself is the authority for which worktrees\n * exist). This is the fix for the sprawl the plan measured: 31 linked\n * worktrees across 6 different parent directories, because there was no\n * `worktree new` verb and therefore no convention to converge on.\n */\nexport interface WorktreesConfig {\n /**\n * Absolute path new worktrees are created under. Layout:\n * `<root>/<repoName>/<slug>`. Resolution order (mirrors every other\n * knob in this file, see the module docblock): `--root` flag >\n * `AGENTPROTO_WORKTREES_ROOT` env > this field > the hardcoded default\n * `~/.agentproto/worktrees`. The default is a real single root, not\n * \"unconfigured\" — `worktree new` converges to one place with zero\n * setup, which is the only way the sprawl actually stops (the 6 roots\n * that exist today are 6 people each inventing a default by hand).\n */\n root?: string\n}\n\nexport interface PairingConfig {\n /** Rendezvous broker WS URL (ws:// or wss://) used by `pair offer` and by\n * autoconnect on boot. When unset, `pair offer` requires an explicit\n * `--rendezvous`. Mirrors `tunnel.host`. */\n rendezvous?: string\n /** Whether the daemon opens standing rendezvous connections for every\n * persisted pairing on boot (so a paired client can reconnect anytime).\n * Mirrors `tunnel.autoconnect`. Default true when a rendezvous is set. */\n autoconnect?: boolean\n}\n\n/**\n * A user-defined generic ACP agent — the config-file half of\n * `AcpAgentSpec` (the slug is the record key in `acpAgents`, so it's\n * omitted here). Any CLI that already speaks the Agent Client Protocol\n * can be wired with zero code by declaring one of these under\n * `acpAgents.<slug>` in `~/.agentproto/config.json`; the CLI's\n * `acpHandleFromSpec` mints a runnable `AgentCliHandle` from it at\n * resolve time (see `packages/cli/src/registry/acp-generic.ts`). Kept\n * in this package (not the CLI's) so `config.ts` stays the single\n * source of truth for the config surface without a cli→runtime→cli\n * import cycle — the CLI's `AcpAgentSpec` extends this shape.\n */\nexport interface AcpAgentConfigEntry {\n /** Display name. Defaults to the slug when omitted. */\n name?: string\n /** One-line description surfaced in `agentproto acp ls`. */\n description?: string\n /** Executable to spawn, e.g. \"gemini\". */\n bin: string\n /** Extra argv appended after `bin`, e.g. [\"--experimental-acp\"]. */\n bin_args?: string[]\n /** Extra environment variables for the spawned process. */\n env?: Record<string, string>\n /** Flag the CLI uses to receive the working directory, if it needs\n * one passed explicitly (most ACP agents take cwd over the wire). */\n cwd_flag?: string\n /** When true, advertise resumable + native-resume continuation. */\n resumable?: boolean\n /** Known model ids for the agent (informational + validation hints). */\n models?: { default?: string; allowed?: string[] }\n /** Shown when `bin` is missing from PATH (how to install the CLI). */\n install_hint?: string\n}\n\n/**\n * Per-environment connection bundle. A profile overrides specific\n * fields of the top-level `daemon` / `tunnel` / `features` blocks\n * when selected via `--profile <name>` (or the top-level\n * `activeProfile` setting). Missing fields fall through to the\n * top-level config, so a profile only needs to declare what's\n * different — typically just `tunnel.host` + `tunnel.token`.\n *\n * Example:\n * {\n * \"daemon\": { \"workspace\": \"/code\", \"port\": 18790 },\n * \"activeProfile\": \"local\",\n * \"profiles\": {\n * \"local\": { \"tunnel\": { \"host\": \"ws://localhost:3200/connect\",\n * \"token\": \"apt_local\", \"autoconnect\": true } },\n * \"prod\": { \"tunnel\": { \"host\": \"wss://tunnel.guilde.work/connect\",\n * \"token\": \"apt_prod\", \"autoconnect\": true },\n * \"daemon\": { \"port\": 18791 } }\n * }\n * }\n *\n * Sandbox daemons generate per-sandbox profile entries at provision\n * time so the daemon inside the sandbox boots with\n * `agentproto serve --profile sandbox-<id>` and no extra plumbing.\n */\nexport interface ProfileConfig {\n daemon?: DaemonConfig\n tunnel?: TunnelConfig\n features?: FeaturesConfig\n}\n\n/**\n * A user-defined named terminal/TUI preset stored in\n * `~/.agentproto/config.json` under `terminalPresets`. Presets keep\n * local launch recipes (argv, env, cwd, name/label) out of shared\n * adapter manifests — e.g. pointing a Claude Code TUI at a local\n * LLM gateway without retyping proxy env vars every spawn.\n */\nexport interface TerminalPreset {\n /** Command + args to spawn. When provided, `sessions terminal` can\n * be used without `-- <argv...>`. */\n argv?: string[]\n /** Extra environment variables layered on top of the daemon's\n * inherited process.env. Values MUST be strings. */\n env?: Record<string, string>\n /** Working directory for the PTY session. Relative paths are\n * resolved against the current working directory at CLI time. */\n cwd?: string\n /** Workspace slug used for cwd fallback when `cwd` is omitted. */\n workspace?: string\n /** Stable session name passed to the registry (`name` field). */\n name?: string\n /** Human-readable label surfaced in session listings. */\n label?: string\n}\n\nexport interface AgentprotoConfig {\n version?: number\n daemon?: DaemonConfig\n tunnel?: TunnelConfig\n features?: FeaturesConfig\n /** E2E daemon-pairing defaults (rendezvous URL + autoconnect). */\n pairing?: PairingConfig\n /** Where `agentproto worktree new` creates worktrees. See\n * {@link WorktreesConfig}. */\n worktrees?: WorktreesConfig\n /** Named connection profiles. See `ProfileConfig` for the merge\n * semantics — a profile's fields shallow-override the top-level\n * defaults for the selected run. */\n profiles?: Record<string, ProfileConfig>\n /** Profile name to use when `--profile` isn't passed. When unset,\n * the top-level `daemon` / `tunnel` blocks are used directly. */\n activeProfile?: string\n /** Default `skills` + `options` auto-applied to every `agent_start`\n * spawn — global and per-adapter. See `resolveSpawnDefaults` in\n * `spawn-defaults.ts` for the merge precedence with an explicit call.\n * Absent ⇒ current behaviour exactly (no regression). */\n defaults?: SpawnDefaultsConfig\n /** User-defined generic ACP agents, keyed by adapter slug. Each entry\n * is minted into a runnable handle by the CLI's `acpHandleFromSpec`\n * when `resolveAdapter(slug)` finds no npm adapter package. User\n * entries shadow the curated `ACP_CATALOG` on slug collision. */\n acpAgents?: Record<string, AcpAgentConfigEntry>\n /** User-defined named terminal/TUI presets. Local-only; never\n * packaged in shared adapter manifests or defaults. */\n terminalPresets?: Record<string, TerminalPreset>\n /** Unknown keys preserved across save round-trips. */\n [unknown: string]: unknown\n}\n\nexport const CONFIG_FILE_PATH = (): string =>\n join(homedir(), \".agentproto\", \"config.json\")\n\n/**\n * Drop any `acpAgents` entries that aren't a shape we can turn into a\n * handle. The one hard requirement is a non-empty string `bin` (the\n * executable to spawn); everything else is optional and defaulted\n * downstream. Invalid entries are removed rather than throwing so the\n * daemon still boots — one warning names the offending slug so the\n * user can fix their config. Returns `undefined` when nothing valid\n * remains, keeping the key absent (== \"no generic agents\").\n */\nfunction sanitizeAcpAgents(\n raw: unknown,\n target: string,\n): Record<string, AcpAgentConfigEntry> | undefined {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) {\n console.warn(\n `[runtime/config] ${target}: 'acpAgents' is not an object — ignoring`,\n )\n return undefined\n }\n const out: Record<string, AcpAgentConfigEntry> = {}\n for (const [slug, value] of Object.entries(raw as Record<string, unknown>)) {\n if (\n value &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n typeof (value as { bin?: unknown }).bin === \"string\" &&\n (value as { bin: string }).bin.length > 0\n ) {\n out[slug] = value as AcpAgentConfigEntry\n } else {\n console.warn(\n `[runtime/config] ${target}: acpAgents.${slug} is missing a string 'bin' — ignoring`,\n )\n }\n }\n return Object.keys(out).length > 0 ? out : undefined\n}\n\n/**\n * Load config.json. Returns an empty object (NOT null) when the file\n * is missing, malformed, or unreadable — callers can `cfg.daemon?.port`\n * safely without null-guards. Errors during a malformed-read are\n * logged once so the user notices the file is broken without the\n * daemon refusing to boot.\n */\nexport async function loadConfig(path?: string): Promise<AgentprotoConfig> {\n const target = path ?? CONFIG_FILE_PATH()\n try {\n const raw = await fs.readFile(target, \"utf8\")\n const parsed = JSON.parse(raw) as unknown\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n const cfg = parsed as AgentprotoConfig\n // Sanitize `acpAgents` in the same tolerant spirit as the rest of\n // this loader: a malformed entry is dropped (with one warning) so a\n // single bad hand-edit can't make every generic ACP agent\n // unresolvable. Full AIP-45 validation happens later, at\n // `acpHandleFromSpec` time, with precise field-level messages.\n if (cfg.acpAgents !== undefined) {\n cfg.acpAgents = sanitizeAcpAgents(cfg.acpAgents, target)\n }\n return cfg\n }\n console.warn(\n `[runtime/config] ${target}: top-level value is not an object — ignoring`,\n )\n return {}\n } catch (err) {\n // ENOENT is the common case; only warn on other shapes.\n const code = (err as NodeJS.ErrnoException).code\n if (code && code !== \"ENOENT\") {\n console.warn(\n `[runtime/config] failed to read ${target}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n return {}\n }\n}\n\n/**\n * Write config.json atomically (tmp + rename) so a concurrent\n * `agentproto config edit` can't half-truncate the file. Writes\n * `next` AS-IS — callers are expected to pass the full desired\n * state (loaded the existing config, mutated, passed it back).\n *\n * Earlier versions deep-merged with the on-disk file, but that made\n * deletions impossible: `setConfigKey(cfg, \"x\", undefined)` would\n * remove the key from memory, then the deep-merge would silently\n * re-add it from disk. The current design trusts the caller's\n * snapshot and uses atomic rename for crash safety.\n */\nexport async function saveConfig(\n next: AgentprotoConfig,\n path?: string,\n): Promise<void> {\n const target = path ?? CONFIG_FILE_PATH()\n const payload = { ...next, version: CONFIG_VERSION }\n const dir = dirname(target)\n await fs.mkdir(dir, { recursive: true })\n const tmp = `${target}.tmp`\n await fs.writeFile(tmp, JSON.stringify(payload, null, 2) + \"\\n\", \"utf8\")\n await fs.rename(tmp, target)\n}\n\n/**\n * Read a dot-notation key (`daemon.port`) out of a config. Returns\n * `undefined` when any segment is missing.\n */\nexport function getConfigKey(\n cfg: AgentprotoConfig,\n dotted: string,\n): unknown {\n let cur: unknown = cfg\n for (const part of dotted.split(\".\")) {\n if (cur == null || typeof cur !== \"object\") return undefined\n cur = (cur as Record<string, unknown>)[part]\n }\n return cur\n}\n\n/**\n * Set a dot-notation key in a config. Returns a new object — does\n * NOT mutate. Creates intermediate objects as needed. Setting\n * `value: undefined` is treated as a delete.\n */\nexport function setConfigKey(\n cfg: AgentprotoConfig,\n dotted: string,\n value: unknown,\n): AgentprotoConfig {\n const parts = dotted.split(\".\")\n const out: AgentprotoConfig = { ...cfg }\n let cur: Record<string, unknown> = out as Record<string, unknown>\n for (let i = 0; i < parts.length - 1; i++) {\n const k = parts[i]!\n const next = cur[k]\n if (next && typeof next === \"object\" && !Array.isArray(next)) {\n cur[k] = { ...(next as Record<string, unknown>) }\n } else {\n cur[k] = {}\n }\n cur = cur[k] as Record<string, unknown>\n }\n const leaf = parts[parts.length - 1]!\n if (value === undefined) {\n delete cur[leaf]\n } else {\n cur[leaf] = value\n }\n return out\n}\n\n/**\n * Deep merge — objects are recursively combined, everything else\n * (arrays, primitives) is replaced wholesale by `b`. Mirrors what\n * `Object.assign({}, a, b)` does for shallow keys.\n */\nfunction deepMerge<A extends Record<string, unknown>, B extends Record<string, unknown>>(\n a: A,\n b: B,\n): A & B {\n const out: Record<string, unknown> = { ...a }\n for (const [k, v] of Object.entries(b)) {\n const cur = out[k]\n if (\n v &&\n typeof v === \"object\" &&\n !Array.isArray(v) &&\n cur &&\n typeof cur === \"object\" &&\n !Array.isArray(cur)\n ) {\n out[k] = deepMerge(\n cur as Record<string, unknown>,\n v as Record<string, unknown>,\n )\n } else {\n out[k] = v\n }\n }\n return out as A & B\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"names":["fs"],"mappings":"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","file":"config.mjs","sourcesContent":["/**\n * `~/.agentproto/config.json` — single hand-editable JSON for the\n * agentproto control plane's defaults. Sits alongside the existing\n * surface files (workspaces.json, credentials.json, sessions.json):\n *\n * workspaces.json which directories are workspaces + which is active\n * credentials.json tunnel host bearer tokens (mode 0600)\n * sessions.json last-known snapshot of the registry (informational)\n * config.json daemon defaults: port, bind, allowed origins,\n * tunnel host, feature toggles\n *\n * Resolution order for every daemon knob is:\n * 1. CLI flag (e.g. --port)\n * 2. Env var (where one exists, e.g. AGENTPROTO_TOKEN)\n * 3. config.json\n * 4. Hardcoded default\n *\n * This means a user can call `agentproto config set daemon.port 18791`\n * once and never re-pass `--port 18791` to `serve install` etc. CLI\n * flags still win for one-off overrides.\n *\n * Schema is intentionally narrow + extensible — unknown keys are\n * preserved on save (deep-merge), so a newer CLI writing a new\n * field won't drop one an older CLI doesn't know about. No secrets\n * here; credentials stay in credentials.json (mode 0600).\n */\n\nimport { promises as fs } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { dirname, join } from \"node:path\"\nimport type { SpawnDefaultsConfig } from \"./spawn-defaults.js\"\n\nexport const CONFIG_VERSION = 1 as const\n\nexport interface DaemonConfig {\n /** Absolute path to the workspace the daemon binds to at boot. */\n workspace?: string\n /** HTTP port. Default 18790. */\n port?: number\n /** Bind addr. Default 127.0.0.1. */\n bind?: string\n /** Trusted browser origins for mutating /sessions/* routes (in\n * addition to the hardcoded localhost defaults). */\n allowedOrigins?: string[]\n /** When true, the daemon does NOT auto-trust localhost-on-any-port.\n * Only origins explicitly listed in `allowedOrigins` are allowed.\n * Pair with a curated list (e.g. `[\"http://localhost:3000\"]`) for\n * hardened setups. Default false. */\n strictOrigins?: boolean\n /** Server label sent in tunnel hello frames. */\n label?: string\n /** Bearer token gating the gateway at boot (`AuthOptions` with\n * `mode: \"bearer\"`). Unlike `remote_enable`'s ephemeral quick-tunnel\n * token, this one lives in config.json and survives daemon restarts.\n * Set via `agentproto config set daemon.authToken <token>` (e.g.\n * `$(openssl rand -hex 32)`). Unset ⇒ the gateway boots with\n * `mode: \"none\"` — fully open on loopback, same as today. */\n authToken?: string\n}\n\nexport interface TunnelConfig {\n /** Cloud WS URL. When set + autoconnect=true, `agentproto serve`\n * bootstraps with `--connect <host>`. */\n host?: string\n /** apt_ daemon token to present at the tunnel upgrade. When set,\n * `agentproto serve` uses this BEFORE falling back to\n * credentials.json — handy in profiles where the token-per-host\n * mapping in credentials.json doesn't fit (e.g. host = tunnel URL\n * but credentials were minted against the api URL). */\n token?: string\n /** Whether `agentproto daemon start` connects the tunnel by\n * default. v0 only — implementer can ignore until daemon needs it. */\n autoconnect?: boolean\n /**\n * Opt into end-to-end encryption of the outbound `serve --connect` tunnel\n * (design: tunnel-e2e/v1). When true, the daemon negotiates a\n * token-authenticated ephemeral handshake with the host and wraps the tunnel\n * frames in an AEAD box, so even the trusted host loses plaintext visibility.\n * The handshake authenticates both ends against the shared `tunnel.token`, so\n * `token` MUST also be set. Fully backward-compatible: if the host doesn't\n * advertise e2e (an older host), the daemon falls back to today's plaintext\n * tunnel. Unset/false ⇒ plaintext, byte-identical to today. */\n e2e?: boolean\n}\n\nexport interface FeaturesConfig {\n /** Hint that PTY is desired — informational; the daemon still\n * detects node-pty's presence at runtime. */\n pty?: boolean\n}\n\n/**\n * Policy for `agentproto worktree new` (PLAN.md §1.4 — config carries\n * policy, never state; git itself is the authority for which worktrees\n * exist). This is the fix for the sprawl the plan measured: 31 linked\n * worktrees across 6 different parent directories, because there was no\n * `worktree new` verb and therefore no convention to converge on.\n */\n/**\n * How the daemon isolates a freshly-spawned agent session into its own git\n * worktree (`agent_start.worktree`):\n * - `\"always\"` — every depth-0 spawn is provisioned into a worktree,\n * whether or not the caller asked. A cwd that is not in\n * a git repo has nothing to isolate, so it spawns plain.\n * - `\"on-request\"` — isolate ONLY when the caller passes `worktree`. This\n * is the default and the back-compatible behaviour:\n * today's callers pass nothing and spawn exactly where\n * they asked.\n * - `\"never\"` — isolation is off; an explicit `worktree` field is\n * REJECTED (loud, not silently ignored) so a caller\n * never believes it got an isolated tree it didn't.\n */\nexport type WorktreeIsolationMode = \"always\" | \"on-request\" | \"never\"\n\nexport interface WorktreesConfig {\n /**\n * Absolute path new worktrees are created under. Layout:\n * `<root>/<repoName>/<slug>`. Resolution order (mirrors every other\n * knob in this file, see the module docblock): `--root` flag >\n * `AGENTPROTO_WORKTREES_ROOT` env > this field > the hardcoded default\n * `~/.agentproto/worktrees`. The default is a real single root, not\n * \"unconfigured\" — `worktree new` converges to one place with zero\n * setup, which is the only way the sprawl actually stops (the 6 roots\n * that exist today are 6 people each inventing a default by hand).\n */\n root?: string\n /**\n * Policy for `agent_start.worktree` isolation. Resolution order mirrors\n * the module docblock (there is no CLI flag — this is a daemon-side\n * policy read at spawn, not a per-invocation flag):\n * `AGENTPROTO_WORKTREES_ISOLATION` env > this field > the hardcoded\n * default `\"on-request\"`. `\"on-request\"` is deliberately the default:\n * any other would break back-compat by isolating callers that never\n * asked (see `worktree-isolation.ts`).\n */\n isolation?: WorktreeIsolationMode\n}\n\nexport interface PairingConfig {\n /** Rendezvous broker WS URL (ws:// or wss://) used by `pair offer` and by\n * autoconnect on boot. When unset, `pair offer` requires an explicit\n * `--rendezvous`. Mirrors `tunnel.host`. */\n rendezvous?: string\n /** Whether the daemon opens standing rendezvous connections for every\n * persisted pairing on boot (so a paired client can reconnect anytime).\n * Mirrors `tunnel.autoconnect`. Default true when a rendezvous is set. */\n autoconnect?: boolean\n}\n\n/**\n * A user-defined generic ACP agent — the config-file half of\n * `AcpAgentSpec` (the slug is the record key in `acpAgents`, so it's\n * omitted here). Any CLI that already speaks the Agent Client Protocol\n * can be wired with zero code by declaring one of these under\n * `acpAgents.<slug>` in `~/.agentproto/config.json`; the CLI's\n * `acpHandleFromSpec` mints a runnable `AgentCliHandle` from it at\n * resolve time (see `packages/cli/src/registry/acp-generic.ts`). Kept\n * in this package (not the CLI's) so `config.ts` stays the single\n * source of truth for the config surface without a cli→runtime→cli\n * import cycle — the CLI's `AcpAgentSpec` extends this shape.\n */\nexport interface AcpAgentConfigEntry {\n /** Display name. Defaults to the slug when omitted. */\n name?: string\n /** One-line description surfaced in `agentproto acp ls`. */\n description?: string\n /** Executable to spawn, e.g. \"gemini\". */\n bin: string\n /** Extra argv appended after `bin`, e.g. [\"--experimental-acp\"]. */\n bin_args?: string[]\n /** Extra environment variables for the spawned process. */\n env?: Record<string, string>\n /** Flag the CLI uses to receive the working directory, if it needs\n * one passed explicitly (most ACP agents take cwd over the wire). */\n cwd_flag?: string\n /** When true, advertise resumable + native-resume continuation. */\n resumable?: boolean\n /** Known model ids for the agent (informational + validation hints). */\n models?: { default?: string; allowed?: string[] }\n /** Shown when `bin` is missing from PATH (how to install the CLI). */\n install_hint?: string\n}\n\n/**\n * Per-environment connection bundle. A profile overrides specific\n * fields of the top-level `daemon` / `tunnel` / `features` blocks\n * when selected via `--profile <name>` (or the top-level\n * `activeProfile` setting). Missing fields fall through to the\n * top-level config, so a profile only needs to declare what's\n * different — typically just `tunnel.host` + `tunnel.token`.\n *\n * Example:\n * {\n * \"daemon\": { \"workspace\": \"/code\", \"port\": 18790 },\n * \"activeProfile\": \"local\",\n * \"profiles\": {\n * \"local\": { \"tunnel\": { \"host\": \"ws://localhost:3200/connect\",\n * \"token\": \"apt_local\", \"autoconnect\": true } },\n * \"prod\": { \"tunnel\": { \"host\": \"wss://tunnel.guilde.work/connect\",\n * \"token\": \"apt_prod\", \"autoconnect\": true },\n * \"daemon\": { \"port\": 18791 } }\n * }\n * }\n *\n * Sandbox daemons generate per-sandbox profile entries at provision\n * time so the daemon inside the sandbox boots with\n * `agentproto serve --profile sandbox-<id>` and no extra plumbing.\n */\nexport interface ProfileConfig {\n daemon?: DaemonConfig\n tunnel?: TunnelConfig\n features?: FeaturesConfig\n}\n\n/**\n * A user-defined named terminal/TUI preset stored in\n * `~/.agentproto/config.json` under `terminalPresets`. Presets keep\n * local launch recipes (argv, env, cwd, name/label) out of shared\n * adapter manifests — e.g. pointing a Claude Code TUI at a local\n * LLM gateway without retyping proxy env vars every spawn.\n */\nexport interface TerminalPreset {\n /** Command + args to spawn. When provided, `sessions terminal` can\n * be used without `-- <argv...>`. */\n argv?: string[]\n /** Extra environment variables layered on top of the daemon's\n * inherited process.env. Values MUST be strings. */\n env?: Record<string, string>\n /** Working directory for the PTY session. Relative paths are\n * resolved against the current working directory at CLI time. */\n cwd?: string\n /** Workspace slug used for cwd fallback when `cwd` is omitted. */\n workspace?: string\n /** Stable session name passed to the registry (`name` field). */\n name?: string\n /** Human-readable label surfaced in session listings. */\n label?: string\n}\n\nexport interface AgentprotoConfig {\n version?: number\n daemon?: DaemonConfig\n tunnel?: TunnelConfig\n features?: FeaturesConfig\n /** E2E daemon-pairing defaults (rendezvous URL + autoconnect). */\n pairing?: PairingConfig\n /** Where `agentproto worktree new` creates worktrees. See\n * {@link WorktreesConfig}. */\n worktrees?: WorktreesConfig\n /** Named connection profiles. See `ProfileConfig` for the merge\n * semantics — a profile's fields shallow-override the top-level\n * defaults for the selected run. */\n profiles?: Record<string, ProfileConfig>\n /** Profile name to use when `--profile` isn't passed. When unset,\n * the top-level `daemon` / `tunnel` blocks are used directly. */\n activeProfile?: string\n /** Default `skills` + `options` auto-applied to every `agent_start`\n * spawn — global and per-adapter. See `resolveSpawnDefaults` in\n * `spawn-defaults.ts` for the merge precedence with an explicit call.\n * Absent ⇒ current behaviour exactly (no regression). */\n defaults?: SpawnDefaultsConfig\n /** User-defined generic ACP agents, keyed by adapter slug. Each entry\n * is minted into a runnable handle by the CLI's `acpHandleFromSpec`\n * when `resolveAdapter(slug)` finds no npm adapter package. User\n * entries shadow the curated `ACP_CATALOG` on slug collision. */\n acpAgents?: Record<string, AcpAgentConfigEntry>\n /** User-defined named terminal/TUI presets. Local-only; never\n * packaged in shared adapter manifests or defaults. */\n terminalPresets?: Record<string, TerminalPreset>\n /** Unknown keys preserved across save round-trips. */\n [unknown: string]: unknown\n}\n\nexport const CONFIG_FILE_PATH = (): string =>\n join(homedir(), \".agentproto\", \"config.json\")\n\n/**\n * Drop any `acpAgents` entries that aren't a shape we can turn into a\n * handle. The one hard requirement is a non-empty string `bin` (the\n * executable to spawn); everything else is optional and defaulted\n * downstream. Invalid entries are removed rather than throwing so the\n * daemon still boots — one warning names the offending slug so the\n * user can fix their config. Returns `undefined` when nothing valid\n * remains, keeping the key absent (== \"no generic agents\").\n */\nfunction sanitizeAcpAgents(\n raw: unknown,\n target: string,\n): Record<string, AcpAgentConfigEntry> | undefined {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) {\n console.warn(\n `[runtime/config] ${target}: 'acpAgents' is not an object — ignoring`,\n )\n return undefined\n }\n const out: Record<string, AcpAgentConfigEntry> = {}\n for (const [slug, value] of Object.entries(raw as Record<string, unknown>)) {\n if (\n value &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n typeof (value as { bin?: unknown }).bin === \"string\" &&\n (value as { bin: string }).bin.length > 0\n ) {\n out[slug] = value as AcpAgentConfigEntry\n } else {\n console.warn(\n `[runtime/config] ${target}: acpAgents.${slug} is missing a string 'bin' — ignoring`,\n )\n }\n }\n return Object.keys(out).length > 0 ? out : undefined\n}\n\n/**\n * Load config.json. Returns an empty object (NOT null) when the file\n * is missing, malformed, or unreadable — callers can `cfg.daemon?.port`\n * safely without null-guards. Errors during a malformed-read are\n * logged once so the user notices the file is broken without the\n * daemon refusing to boot.\n */\nexport async function loadConfig(path?: string): Promise<AgentprotoConfig> {\n const target = path ?? CONFIG_FILE_PATH()\n try {\n const raw = await fs.readFile(target, \"utf8\")\n const parsed = JSON.parse(raw) as unknown\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n const cfg = parsed as AgentprotoConfig\n // Sanitize `acpAgents` in the same tolerant spirit as the rest of\n // this loader: a malformed entry is dropped (with one warning) so a\n // single bad hand-edit can't make every generic ACP agent\n // unresolvable. Full AIP-45 validation happens later, at\n // `acpHandleFromSpec` time, with precise field-level messages.\n if (cfg.acpAgents !== undefined) {\n cfg.acpAgents = sanitizeAcpAgents(cfg.acpAgents, target)\n }\n return cfg\n }\n console.warn(\n `[runtime/config] ${target}: top-level value is not an object — ignoring`,\n )\n return {}\n } catch (err) {\n // ENOENT is the common case; only warn on other shapes.\n const code = (err as NodeJS.ErrnoException).code\n if (code && code !== \"ENOENT\") {\n console.warn(\n `[runtime/config] failed to read ${target}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n return {}\n }\n}\n\n/**\n * Write config.json atomically (tmp + rename) so a concurrent\n * `agentproto config edit` can't half-truncate the file. Writes\n * `next` AS-IS — callers are expected to pass the full desired\n * state (loaded the existing config, mutated, passed it back).\n *\n * Earlier versions deep-merged with the on-disk file, but that made\n * deletions impossible: `setConfigKey(cfg, \"x\", undefined)` would\n * remove the key from memory, then the deep-merge would silently\n * re-add it from disk. The current design trusts the caller's\n * snapshot and uses atomic rename for crash safety.\n */\nexport async function saveConfig(\n next: AgentprotoConfig,\n path?: string,\n): Promise<void> {\n const target = path ?? CONFIG_FILE_PATH()\n const payload = { ...next, version: CONFIG_VERSION }\n const dir = dirname(target)\n await fs.mkdir(dir, { recursive: true })\n const tmp = `${target}.tmp`\n await fs.writeFile(tmp, JSON.stringify(payload, null, 2) + \"\\n\", \"utf8\")\n await fs.rename(tmp, target)\n}\n\n/**\n * Read a dot-notation key (`daemon.port`) out of a config. Returns\n * `undefined` when any segment is missing.\n */\nexport function getConfigKey(\n cfg: AgentprotoConfig,\n dotted: string,\n): unknown {\n let cur: unknown = cfg\n for (const part of dotted.split(\".\")) {\n if (cur == null || typeof cur !== \"object\") return undefined\n cur = (cur as Record<string, unknown>)[part]\n }\n return cur\n}\n\n/**\n * Set a dot-notation key in a config. Returns a new object — does\n * NOT mutate. Creates intermediate objects as needed. Setting\n * `value: undefined` is treated as a delete.\n */\nexport function setConfigKey(\n cfg: AgentprotoConfig,\n dotted: string,\n value: unknown,\n): AgentprotoConfig {\n const parts = dotted.split(\".\")\n const out: AgentprotoConfig = { ...cfg }\n let cur: Record<string, unknown> = out as Record<string, unknown>\n for (let i = 0; i < parts.length - 1; i++) {\n const k = parts[i]!\n const next = cur[k]\n if (next && typeof next === \"object\" && !Array.isArray(next)) {\n cur[k] = { ...(next as Record<string, unknown>) }\n } else {\n cur[k] = {}\n }\n cur = cur[k] as Record<string, unknown>\n }\n const leaf = parts[parts.length - 1]!\n if (value === undefined) {\n delete cur[leaf]\n } else {\n cur[leaf] = value\n }\n return out\n}\n\n/**\n * Deep merge — objects are recursively combined, everything else\n * (arrays, primitives) is replaced wholesale by `b`. Mirrors what\n * `Object.assign({}, a, b)` does for shallow keys.\n */\nfunction deepMerge<A extends Record<string, unknown>, B extends Record<string, unknown>>(\n a: A,\n b: B,\n): A & B {\n const out: Record<string, unknown> = { ...a }\n for (const [k, v] of Object.entries(b)) {\n const cur = out[k]\n if (\n v &&\n typeof v === \"object\" &&\n !Array.isArray(v) &&\n cur &&\n typeof cur === \"object\" &&\n !Array.isArray(cur)\n ) {\n out[k] = deepMerge(\n cur as Record<string, unknown>,\n v as Record<string, unknown>,\n )\n } else {\n out[k] = v\n }\n }\n return out as A & B\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ import { B as BuildHeartbeatAgent } from './heartbeat-COGpMrJS.js';
|
|
|
3
3
|
export { H as HeartbeatAgent, a as HeartbeatRunner, R as RuntimeEvent, b as RuntimeEvents, p as parseDuration } from './heartbeat-COGpMrJS.js';
|
|
4
4
|
import { AcpPermissionResolution, AcpMcpServer } from '@agentproto/acp';
|
|
5
5
|
import { ChildProcess } from 'node:child_process';
|
|
6
|
-
import { R as ResolvedAuthSpec, D as DeclaredAdapterOption, A as AdapterAuthDescriptor } from './
|
|
7
|
-
export { a as AuthEcho, b as AuthResolutionError, C as CredentialSource, c as DefaultsAdapterAuthConfig, d as DefaultsAdapterConfig, e as ResolvedSpawnAuthMaterial, f as ResolvedSpawnDefaults, S as SpawnDefaultsConfig, g as credentialFingerprint, n as normalizeSkillsOption, r as resolveAuthSpec, h as resolveSpawnDefaults } from './
|
|
6
|
+
import { W as WorktreeIsolationMode, R as ResolvedAuthSpec, D as DeclaredAdapterOption, A as AdapterAuthDescriptor } from './config-BRKy_SAF.js';
|
|
7
|
+
export { a as AuthEcho, b as AuthResolutionError, C as CredentialSource, c as DefaultsAdapterAuthConfig, d as DefaultsAdapterConfig, e as ResolvedSpawnAuthMaterial, f as ResolvedSpawnDefaults, S as SpawnDefaultsConfig, g as credentialFingerprint, n as normalizeSkillsOption, r as resolveAuthSpec, h as resolveSpawnDefaults } from './config-BRKy_SAF.js';
|
|
8
8
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
9
|
import { AdapterHandle, SetupField, AdapterResolver, AdapterLister, AdapterEntry } from '@agentproto/provider-kit';
|
|
10
10
|
import { SandboxProvider } from '@agentproto/sandbox';
|
|
@@ -2222,6 +2222,127 @@ type SandboxProviderResolver = AdapterResolver<SandboxProviderHandle>;
|
|
|
2222
2222
|
/** List every sandbox provider with its live status + capabilities. */
|
|
2223
2223
|
type SandboxProviderLister = AdapterLister<SandboxAdapterInfo>;
|
|
2224
2224
|
|
|
2225
|
+
/**
|
|
2226
|
+
* Policy layer for `agent_start.worktree` — the config-driven decision of
|
|
2227
|
+
* WHETHER to isolate a spawn into its own git worktree, kept deliberately
|
|
2228
|
+
* separate from HOW one is provisioned.
|
|
2229
|
+
*
|
|
2230
|
+
* This module holds no dependency on `@agentproto/worktree`. Same reasoning
|
|
2231
|
+
* as `worktree-identity.ts`'s docblock, only stronger: provisioning runs the
|
|
2232
|
+
* `worktree.provision` TOOL (git + the base tree's `agentproto.json` setup
|
|
2233
|
+
* hooks), which would drag the driver/harness/provider graph into
|
|
2234
|
+
* `@agentproto/runtime` for a capability the runtime only ever *triggers*.
|
|
2235
|
+
* So the concrete provisioner is an INJECTED PORT ({@link WorktreeProvisioner})
|
|
2236
|
+
* wired at the composition root by a host that already depends on the
|
|
2237
|
+
* worktree package (the CLI). The runtime owns only the pure decision
|
|
2238
|
+
* ({@link decideWorktreeIsolation}) and the config/env resolution
|
|
2239
|
+
* ({@link loadWorktreeIsolation}) — both trivially unit-testable without git.
|
|
2240
|
+
*
|
|
2241
|
+
* Mirrors `agent_start.sandbox`'s `resolveSandboxProvider` injection shape,
|
|
2242
|
+
* except sandbox can default its resolver inside the runtime (the runtime
|
|
2243
|
+
* depends on `@agentproto/sandbox`); worktree cannot, so an unwired daemon
|
|
2244
|
+
* simply has no provisioner and a spawn that would need one fails loud rather
|
|
2245
|
+
* than silently spawning unisolated.
|
|
2246
|
+
*/
|
|
2247
|
+
|
|
2248
|
+
/** Env override for the isolation policy. Highest-priority source, ahead of
|
|
2249
|
+
* the `worktrees.isolation` config field — see `loadWorktreeIsolation`. */
|
|
2250
|
+
declare const WORKTREE_ISOLATION_ENV = "AGENTPROTO_WORKTREES_ISOLATION";
|
|
2251
|
+
/** The default when nothing is configured. Back-compat-preserving: a caller
|
|
2252
|
+
* that passes no `worktree` field spawns exactly where it asked. */
|
|
2253
|
+
declare const DEFAULT_WORKTREE_ISOLATION: WorktreeIsolationMode;
|
|
2254
|
+
/**
|
|
2255
|
+
* The `agent_start.worktree` field. `true` isolates with an auto-minted slug;
|
|
2256
|
+
* an object additionally pins the slug and/or base ref; `false` (or omitted)
|
|
2257
|
+
* is "no explicit request" — the policy mode decides.
|
|
2258
|
+
*/
|
|
2259
|
+
type WorktreeField = boolean | {
|
|
2260
|
+
slug?: string;
|
|
2261
|
+
base?: string;
|
|
2262
|
+
};
|
|
2263
|
+
/** The caller's explicit request, normalized — `undefined` when the field is
|
|
2264
|
+
* absent or `false`, otherwise the (possibly empty) slug/base overrides. */
|
|
2265
|
+
interface WorktreeRequest {
|
|
2266
|
+
slug?: string;
|
|
2267
|
+
base?: string;
|
|
2268
|
+
}
|
|
2269
|
+
/** What the runtime hands the provisioner. `cwd` is where the session would
|
|
2270
|
+
* otherwise spawn; the provisioner resolves the owning repo from it. */
|
|
2271
|
+
interface WorktreeProvisionRequest {
|
|
2272
|
+
cwd: string;
|
|
2273
|
+
/** Explicit slug from the caller; omitted ⇒ the provisioner mints a
|
|
2274
|
+
* collision-free one (see the host implementation). */
|
|
2275
|
+
slug?: string;
|
|
2276
|
+
/** Base ref the worktree branch is cut from; omitted ⇒ the provisioner's
|
|
2277
|
+
* default (`origin/main`). */
|
|
2278
|
+
base?: string;
|
|
2279
|
+
/** Free-text label (the session's `label`) the provisioner may fold into a
|
|
2280
|
+
* minted slug for human readability. Never load-bearing for correctness. */
|
|
2281
|
+
labelHint?: string;
|
|
2282
|
+
}
|
|
2283
|
+
/** The provisioner's outcome. `isolated: false` is NOT a failure — it means
|
|
2284
|
+
* there was nothing to isolate (`cwd` sits in no git repo), and the caller
|
|
2285
|
+
* should spawn plain at the original cwd. A genuine failure throws. */
|
|
2286
|
+
type WorktreeProvisionOutcome = {
|
|
2287
|
+
isolated: true;
|
|
2288
|
+
cwd: string;
|
|
2289
|
+
branch: string;
|
|
2290
|
+
} | {
|
|
2291
|
+
isolated: false;
|
|
2292
|
+
reason: "not-a-git-repo";
|
|
2293
|
+
};
|
|
2294
|
+
/**
|
|
2295
|
+
* Injected port: provision a git worktree for `req.cwd` and return the
|
|
2296
|
+
* worktree's own cwd for the spawn to land in. Wired at the composition root
|
|
2297
|
+
* by the CLI (over `@agentproto/worktree`); absent on a bare runtime.
|
|
2298
|
+
*/
|
|
2299
|
+
type WorktreeProvisioner = (req: WorktreeProvisionRequest) => Promise<WorktreeProvisionOutcome>;
|
|
2300
|
+
/** The pure decision's three outcomes. */
|
|
2301
|
+
type WorktreeDecision = {
|
|
2302
|
+
action: "spawn-in-place";
|
|
2303
|
+
} | {
|
|
2304
|
+
action: "provision";
|
|
2305
|
+
request: WorktreeRequest;
|
|
2306
|
+
} | {
|
|
2307
|
+
action: "reject";
|
|
2308
|
+
message: string;
|
|
2309
|
+
};
|
|
2310
|
+
/**
|
|
2311
|
+
* Normalize the raw field into an explicit request, or `undefined` when the
|
|
2312
|
+
* caller made no request (`false` / omitted). An object with no overrides
|
|
2313
|
+
* still counts as a request (`{}`) — the caller opted in, just without pins.
|
|
2314
|
+
*/
|
|
2315
|
+
declare function normalizeWorktreeField(field: WorktreeField | undefined): WorktreeRequest | undefined;
|
|
2316
|
+
/**
|
|
2317
|
+
* The resolution matrix — mode × explicit-request × depth — with no side
|
|
2318
|
+
* effects. Every branch is exercised in `worktree-isolation.test.ts`.
|
|
2319
|
+
*
|
|
2320
|
+
* Depth-0 gate: a nested spawn (`depth > 0`, i.e. made through the scoped
|
|
2321
|
+
* orchestrator sub-gateway) NEVER provisions — it inherits its parent's
|
|
2322
|
+
* ground, per AIP-46 §Delegation. This bites before the mode is even
|
|
2323
|
+
* consulted, so `always` too provisions only at the root and an explicit
|
|
2324
|
+
* request from a nested spawn is a silent no-op (spawn-in-place), not a
|
|
2325
|
+
* reject: the child is meant to share the parent's tree.
|
|
2326
|
+
*/
|
|
2327
|
+
declare function decideWorktreeIsolation(input: {
|
|
2328
|
+
mode: WorktreeIsolationMode;
|
|
2329
|
+
field: WorktreeField | undefined;
|
|
2330
|
+
depth: number;
|
|
2331
|
+
}): WorktreeDecision;
|
|
2332
|
+
/** Parse a raw string into a valid mode, or `undefined` when it isn't one. */
|
|
2333
|
+
declare function parseWorktreeIsolationMode(raw: string | undefined): WorktreeIsolationMode | undefined;
|
|
2334
|
+
/**
|
|
2335
|
+
* Resolve the effective isolation mode: env > config field > default. Mirrors
|
|
2336
|
+
* `resolveWorktreesRoot`'s precedence (there's no flag layer — this is read
|
|
2337
|
+
* daemon-side at spawn, not from an invocation). Never throws: an unreadable
|
|
2338
|
+
* config falls through to the default.
|
|
2339
|
+
*/
|
|
2340
|
+
declare function loadWorktreeIsolation(loadCfg?: () => Promise<{
|
|
2341
|
+
worktrees?: {
|
|
2342
|
+
isolation?: WorktreeIsolationMode;
|
|
2343
|
+
};
|
|
2344
|
+
}>): Promise<WorktreeIsolationMode>;
|
|
2345
|
+
|
|
2225
2346
|
/**
|
|
2226
2347
|
* Pluggable adapter resolver — keeps the runtime package free of any
|
|
2227
2348
|
* @agentproto/cli dep. The host (cli `serve`, playground, embedding
|
|
@@ -2911,6 +3032,26 @@ declare function readRuntimeMeta(workspace: string): Promise<{
|
|
|
2911
3032
|
*/
|
|
2912
3033
|
declare function sweepStaleRuntimeMetas(workspaces: readonly string[], currentWorkspace: string): Promise<string[]>;
|
|
2913
3034
|
|
|
3035
|
+
/**
|
|
3036
|
+
* Read-only probe: does a credential actually RESOLVE for an adapter slug's
|
|
3037
|
+
* billing auth? Used by `adapter_list` to report an HONEST status instead of
|
|
3038
|
+
* claiming "ready" on a host with zero credentials for an adapter that hard-
|
|
3039
|
+
* fails every spawn (claude-code, `authEnforce: "always"`).
|
|
3040
|
+
*
|
|
3041
|
+
* THE TRAP (see the money regression test in `__tests__/auth-probe.test.ts`):
|
|
3042
|
+
* a probe that answers "is auth configured?" by checking `providers.json`
|
|
3043
|
+
* alone would report `true` for a spawn that still throws
|
|
3044
|
+
* `missing_auth_credential` — the store lookup at `session-spawn.ts:612-617`
|
|
3045
|
+
* is gated on `explicit` (PR #321: an unconfigured `always`-enforcing adapter
|
|
3046
|
+
* must never pick up a leftover store key and bill org credits instead of the
|
|
3047
|
+
* Max subscription). So this mirrors `session-spawn.ts`'s resolution exactly
|
|
3048
|
+
* — same `resolveSpawnDefaults` + `resolveAuthSpec`, same explicit gate on
|
|
3049
|
+
* the store lookup — rather than reimplementing the precedence: a second
|
|
3050
|
+
* copy of this logic WILL drift, and the drift is denominated in money.
|
|
3051
|
+
*/
|
|
3052
|
+
|
|
3053
|
+
declare function isAgentCliAuthConfigured(slug: string, descriptor: AdapterAuthDescriptor, model?: string): Promise<boolean>;
|
|
3054
|
+
|
|
2914
3055
|
/**
|
|
2915
3056
|
* MCP tools for event-driven orchestration:
|
|
2916
3057
|
* - session_events_poll — cheap cursor-based snapshot of session events
|
|
@@ -3126,6 +3267,16 @@ interface CreateGatewayOptions {
|
|
|
3126
3267
|
/** Optional sandbox provider lister — mirrors `listAgentAdapters`.
|
|
3127
3268
|
* Overrides the default catalog-driven lister behind `list_sandbox_providers`. */
|
|
3128
3269
|
listSandboxProviders?: SandboxProviderLister;
|
|
3270
|
+
/**
|
|
3271
|
+
* Optional git-worktree provisioner powering `agent_start.worktree` and the
|
|
3272
|
+
* `worktrees.isolation` policy. Injected here (rather than defaulted inside
|
|
3273
|
+
* the runtime like `resolveSandboxProvider`) because provisioning runs the
|
|
3274
|
+
* `@agentproto/worktree` TOOL, a dependency the runtime deliberately does
|
|
3275
|
+
* NOT take — see `worktree-identity.ts` / `worktree-isolation.ts`. The CLI
|
|
3276
|
+
* wires it. Omitted → a spawn the policy says to isolate is rejected with
|
|
3277
|
+
* `worktree_provisioner_not_enabled` (never silently spawned unisolated).
|
|
3278
|
+
*/
|
|
3279
|
+
provisionWorktree?: WorktreeProvisioner;
|
|
3129
3280
|
/**
|
|
3130
3281
|
* Optional E2E pairing registry (see `createPairingRegistry`). When wired,
|
|
3131
3282
|
* the gateway mounts the `/pairings/*` REST routes and the `pair_*` MCP
|
|
@@ -3183,4 +3334,4 @@ interface GatewayHandle {
|
|
|
3183
3334
|
*/
|
|
3184
3335
|
declare function createGateway(opts: CreateGatewayOptions): Promise<GatewayHandle>;
|
|
3185
3336
|
|
|
3186
|
-
export { AdapterAuthDescriptor, type AdapterListEntry, type AgentAdapterLister, type AgentAdapterResolver, type AgentSessionLike, type AgentStreamEvent, type AttachPolicyInput, BUCKETS_ROOT, type BrowserAdapterHandle, type BrowserAdapterInfo, type BrowserAdapterLister, type BrowserAdapterResolver, BuildHeartbeatAgent, type CommitSpec, type CompletionPolicySupervisor, type CreateGatewayOptions, type CreateOfferInput, type CreatedOffer, DEFAULT_BUCKET, DEFAULT_ORCHESTRATOR_TOOLS, DeclaredAdapterOption, type DeclaredAdapterPreset, type GateSpec, type GatewayHandle, type InboundWatcher, type JudgeGateSpec, LEGACY_SESSIONS_FILE, type McpCredentialDeps, type MigrationMarker, type OnFailSpec, type OrchestratorGatewayDeps, type OrchestratorInjection, type OrchestratorInjector, type OrchestratorInjectorDeps, type OrchestratorMcpServerFactory, type OrchestratorScope, PAIRINGS_VERSION, type PairingChannelContext, type PairingChannelHandle, type PairingChannelMode, type PairingRecord, type PairingRegistry, type PairingRegistryDeps, type PendingPermission, type PermissionRespondInput, type PermissionRespondResult, type PolicyRunState, type PolicyRunStatus, type PresetInfo, type PricingResolver, type RegisterBrowserInput, type RegisterPairingToolsOptions, type RegisterSessionInput, ResolvedAuthSpec, type RuntimeMeta, type SandboxAdapterInfo, type SandboxProviderCapabilities, type SandboxProviderHandle, type SandboxProviderLister, type SandboxProviderResolver, type ScopeTokenRegistry, type SessionDescriptor, type SessionKind, type SessionObserver, type SessionStatus, type SessionUsage, type SessionWaitEvent, type SessionWaitResult, type SessionsRegistry, type ShellGateSpec, type SpawnAgentInput, type SpawnSessionInput, type TokenPricing, type TunnelDescriptor, type TunnelProvider, type TunnelStatus, type UsageComputeInput, type UsageSource, type WatcherDescriptor, type WatcherStartInput, WorkspaceFs, bucketDir, bucketSessionsFile, bucketTranscriptDir, composeSessionObservers, createFileStepCache, createGateway, createOrchestratorInjector, createOrchestratorMcpServerFactory, createPairingRegistry, createScopeTokenRegistry, daemonRegistryDir, declaredPresetToProviderPreset, deriveSessionUsage, formatToolCall, formatToolResult, getMcpCredentialDeps, isSafeBucketSlug, listBuckets, listPresets, makeBrowserAdapterLister, migrateLegacySessionsFile, migrationMarkerPath, monitorPolicyWait, monitorSessionWait, narrowOrchestratorTools, policyWatchesSession, projectSessionUsage, readDaemonRegistry, readRegisteredSlugs, readRuntimeMeta, registerPairingTools, resolveBucketSlug, setMcpCredentialDeps, sweepStaleDaemonRegistry, sweepStaleRuntimeMetas, unlinkDaemonRegistryEntry, unlinkRuntimeMeta, writeDaemonRegistryEntry };
|
|
3337
|
+
export { AdapterAuthDescriptor, type AdapterListEntry, type AgentAdapterLister, type AgentAdapterResolver, type AgentSessionLike, type AgentStreamEvent, type AttachPolicyInput, BUCKETS_ROOT, type BrowserAdapterHandle, type BrowserAdapterInfo, type BrowserAdapterLister, type BrowserAdapterResolver, BuildHeartbeatAgent, type CommitSpec, type CompletionPolicySupervisor, type CreateGatewayOptions, type CreateOfferInput, type CreatedOffer, DEFAULT_BUCKET, DEFAULT_ORCHESTRATOR_TOOLS, DEFAULT_WORKTREE_ISOLATION, DeclaredAdapterOption, type DeclaredAdapterPreset, type GateSpec, type GatewayHandle, type InboundWatcher, type JudgeGateSpec, LEGACY_SESSIONS_FILE, type McpCredentialDeps, type MigrationMarker, type OnFailSpec, type OrchestratorGatewayDeps, type OrchestratorInjection, type OrchestratorInjector, type OrchestratorInjectorDeps, type OrchestratorMcpServerFactory, type OrchestratorScope, PAIRINGS_VERSION, type PairingChannelContext, type PairingChannelHandle, type PairingChannelMode, type PairingRecord, type PairingRegistry, type PairingRegistryDeps, type PendingPermission, type PermissionRespondInput, type PermissionRespondResult, type PolicyRunState, type PolicyRunStatus, type PresetInfo, type PricingResolver, type RegisterBrowserInput, type RegisterPairingToolsOptions, type RegisterSessionInput, ResolvedAuthSpec, type RuntimeMeta, type SandboxAdapterInfo, type SandboxProviderCapabilities, type SandboxProviderHandle, type SandboxProviderLister, type SandboxProviderResolver, type ScopeTokenRegistry, type SessionDescriptor, type SessionKind, type SessionObserver, type SessionStatus, type SessionUsage, type SessionWaitEvent, type SessionWaitResult, type SessionsRegistry, type ShellGateSpec, type SpawnAgentInput, type SpawnSessionInput, type TokenPricing, type TunnelDescriptor, type TunnelProvider, type TunnelStatus, type UsageComputeInput, type UsageSource, WORKTREE_ISOLATION_ENV, type WatcherDescriptor, type WatcherStartInput, WorkspaceFs, type WorktreeDecision, type WorktreeField, WorktreeIsolationMode, type WorktreeProvisionOutcome, type WorktreeProvisionRequest, type WorktreeProvisioner, type WorktreeRequest, bucketDir, bucketSessionsFile, bucketTranscriptDir, composeSessionObservers, createFileStepCache, createGateway, createOrchestratorInjector, createOrchestratorMcpServerFactory, createPairingRegistry, createScopeTokenRegistry, daemonRegistryDir, decideWorktreeIsolation, declaredPresetToProviderPreset, deriveSessionUsage, formatToolCall, formatToolResult, getMcpCredentialDeps, isAgentCliAuthConfigured, isSafeBucketSlug, listBuckets, listPresets, loadWorktreeIsolation, makeBrowserAdapterLister, migrateLegacySessionsFile, migrationMarkerPath, monitorPolicyWait, monitorSessionWait, narrowOrchestratorTools, normalizeWorktreeField, parseWorktreeIsolationMode, policyWatchesSession, projectSessionUsage, readDaemonRegistry, readRegisteredSlugs, readRuntimeMeta, registerPairingTools, resolveBucketSlug, setMcpCredentialDeps, sweepStaleDaemonRegistry, sweepStaleRuntimeMetas, unlinkDaemonRegistryEntry, unlinkRuntimeMeta, writeDaemonRegistryEntry };
|