@agentproto/runtime 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -451,13 +451,64 @@ type UsageSource = "adapter" | "computed" | "no-pricing" | "none";
451
451
  interface TokenPricing {
452
452
  inputPer1M: number;
453
453
  outputPer1M: number;
454
+ /** Multiplier on `inputPer1M` for cache-read tokens (Anthropic: ~0.1).
455
+ * Absent = 1 (no discount), same default as the catalog. */
456
+ cacheReadMultiplier?: number;
457
+ /** Multiplier on `inputPer1M` for cache-creation tokens (Anthropic:
458
+ * ~1.25). Absent = 1. */
459
+ cacheWriteMultiplier?: number;
460
+ }
461
+ /**
462
+ * Token-detail and activity fields shared by every usage shape below. All
463
+ * optional and never zero-filled: absent means "this source doesn't report
464
+ * it", a present 0 is a measured zero.
465
+ *
466
+ * - `cacheReadTokens` / `cacheWriteTokens`: prompt-cache reads / cache
467
+ * creation, cumulative. Kept OUT of `tokensIn` (which is uncached input
468
+ * only when a cache split is known).
469
+ * - `reasoningTokens`: thinking/reasoning tokens, cumulative. A subset of
470
+ * `tokensOut` for providers that bill them as output (Anthropic).
471
+ * - `turns`: completed turns (the descriptor's `turnsCompleted`).
472
+ * - `toolCalls`: tool calls the daemon observed, cumulative.
473
+ * - `durationMs`: wall time spent inside turns, cumulative.
474
+ */
475
+ interface UsageDetail {
476
+ cacheReadTokens?: number;
477
+ cacheWriteTokens?: number;
478
+ reasoningTokens?: number;
479
+ turns?: number;
480
+ toolCalls?: number;
481
+ durationMs?: number;
454
482
  }
483
+ /** Copy only the present (`number`) `UsageDetail` fields of `src`. */
484
+ declare function pickUsageDetail(src: UsageDetail): UsageDetail;
455
485
  /** Pluggable pricing accessor — defaults to the in-repo catalog's
456
486
  * `resolvePricing`, overridable in tests. Returns undefined for an
457
487
  * unknown model (the caller then tags `no-pricing`). */
458
488
  type PricingResolver = (model: string) => TokenPricing | undefined;
489
+ /**
490
+ * What an adapter's usage reader (`readUsage`: hermes state.db, opencode.db,
491
+ * the claude-code transcript JSONL) returns. Every field is cumulative for
492
+ * the session and optional; `null` from the reader means "nothing found".
493
+ */
494
+ interface UsageReadResult {
495
+ model?: string;
496
+ costUsd?: number;
497
+ tokensIn?: number;
498
+ tokensOut?: number;
499
+ cacheReadTokens?: number;
500
+ cacheWriteTokens?: number;
501
+ reasoningTokens?: number;
502
+ }
503
+ /** Where a reader should look for the session's native store — the spawn's
504
+ * cwd and isolated provider config dir (`CLAUDE_CONFIG_DIR` for
505
+ * claude-code). Readers that key only on the session id ignore it. */
506
+ interface UsageReadContext {
507
+ cwd?: string;
508
+ configDir?: string;
509
+ }
459
510
  /** Raw signals gathered over a turn, handed to `deriveSessionUsage`. */
460
- interface UsageComputeInput {
511
+ interface UsageComputeInput extends UsageDetail {
461
512
  /** Requested model id — needed to look up per-token prices. */
462
513
  model?: string;
463
514
  /** Cost the adapter reported directly (readUsage or usage_update.cost).
@@ -471,7 +522,7 @@ interface UsageComputeInput {
471
522
  /** The resolved usage snapshot — the shape `session_usage` returns and the
472
523
  * durable `usage_snapshot` transcript record carries. Cost/token fields are
473
524
  * omitted when absent so a missing value never reads as a measured zero. */
474
- interface SessionUsage {
525
+ interface SessionUsage extends UsageDetail {
475
526
  model?: string;
476
527
  costUsd?: number;
477
528
  tokensIn?: number;
@@ -488,7 +539,7 @@ interface SessionUsage {
488
539
  */
489
540
  declare function deriveSessionUsage(input: UsageComputeInput, resolve?: PricingResolver): SessionUsage;
490
541
  /** Descriptor-shaped fields `projectSessionUsage` reads. */
491
- interface UsageDescriptorFields {
542
+ interface UsageDescriptorFields extends Omit<UsageDetail, "turns"> {
492
543
  model?: string;
493
544
  costUsd?: number;
494
545
  tokensIn?: number;
@@ -496,6 +547,8 @@ interface UsageDescriptorFields {
496
547
  contextSize?: number;
497
548
  contextUsed?: number;
498
549
  usageSource?: UsageSource;
550
+ /** Surfaced as `turns`. */
551
+ turnsCompleted?: number;
499
552
  }
500
553
  /**
501
554
  * Project a session descriptor's persisted usage fields into the
@@ -1837,6 +1890,18 @@ interface SessionTurnEndEvent {
1837
1890
  * a normal, productive turn.
1838
1891
  */
1839
1892
  empty?: boolean;
1893
+ /**
1894
+ * The in-band `error` stream event's message text observed during this
1895
+ * turn, when one occurred (see `SessionDescriptor.lastTurnErrorMessage`'s
1896
+ * doc). Present whenever `reason` is `"error"` AND the adapter emitted an
1897
+ * `error`-kind event before ending the stream — including the case where
1898
+ * the generator simply returned with no explicit turn-end and no thrown
1899
+ * exception, which is exactly what left an errored turn indistinguishable
1900
+ * from a clean one before this field existed. Absent when the adapter
1901
+ * reported `reason:"error"` on its own turn-end without a preceding
1902
+ * `error` event, and absent (not empty string) on every non-error turn.
1903
+ */
1904
+ error?: string;
1840
1905
  /**
1841
1906
  * True when the turn was not started by a prompt: the agent woke on its
1842
1907
  * own (Claude Code's task-notification cycle after a background task
@@ -2763,6 +2828,90 @@ declare class TunnelRegistry {
2763
2828
  private loadFromDisk;
2764
2829
  }
2765
2830
 
2831
+ /**
2832
+ * LlmEndpointRegistry — supervised-sidecar manager for the
2833
+ * `@agentproto/llm-endpoint` proxy gateway.
2834
+ *
2835
+ * The daemon spawns the llm-endpoint bin as a CHILD PROCESS (never an
2836
+ * in-process import — the runtime package has zero dependency on
2837
+ * @agentproto/llm-endpoint, exactly like it never imports cloudflared).
2838
+ * This is the single-sidecar analog of {@link TunnelRegistry}: one endpoint,
2839
+ * one port, an in-memory descriptor that tracks pid / port / status /
2840
+ * startedAt, and a start / stop / status lifecycle exposed over MCP.
2841
+ *
2842
+ * Lifecycle (mirrors RemoteController's singleton style — created fresh each
2843
+ * boot, state in memory; the child dies with the daemon so there is nothing
2844
+ * to reconcile on restart):
2845
+ * start(input) → assemble env, spawn `node <bin> serve`, health-probe to
2846
+ * readiness, store descriptor. Idempotent: a call while the
2847
+ * endpoint is already running+healthy returns the existing
2848
+ * descriptor without spawning (like `start_browser`).
2849
+ * status() → descriptor + live `GET /v1/models` health probe.
2850
+ * stop() → SIGTERM the child, mark stopped (mirrors tunnel stop).
2851
+ * shutdown() → stop the child on daemon exit.
2852
+ *
2853
+ * Env on spawn (see {@link assembleLlmEndpointEnv}): the provider API keys
2854
+ * via `injectProviderKeysIntoEnv` (the same store llm-endpoint's own cli.ts
2855
+ * reads), `LLM_ENDPOINT_PORT` (default 18090), and `LLM_ENDPOINT_ACCESS_TOKENS`
2856
+ * when configured. Explicit env passed to `start` always wins.
2857
+ */
2858
+ type LlmEndpointStatus = "starting" | "running" | "stopped" | "error";
2859
+ /** The `llm_endpoint_status` MCP contract — see {@link LlmEndpointRegistry.status}. */
2860
+ interface LlmEndpointStatusReport {
2861
+ running: boolean;
2862
+ pid: number | null;
2863
+ port: number | null;
2864
+ baseUrl: string | null;
2865
+ healthy: boolean;
2866
+ startedAt: string | null;
2867
+ status: LlmEndpointStatus | "never-started";
2868
+ /** Who is actually serving this endpoint. `"daemon"` — this registry's own
2869
+ * spawn (or nothing at all). `"external"` — a healthy process on the same
2870
+ * port that this registry did NOT spawn (e.g. a launchd KeepAlive
2871
+ * sidecar); `pid` is unknown (always `null`) and `injectedProviders`, if
2872
+ * present, was DERIVED by probing `/v1/models` rather than read off our
2873
+ * own key-injection bookkeeping. */
2874
+ owner: "daemon" | "external";
2875
+ /** Whether upstream links persisted via `llm_endpoint_set_upstream_link`
2876
+ * are actually applied to the process answering this port. Always
2877
+ * `false` for `owner:"external"` — this registry never spawned it, so it
2878
+ * never injected `LLM_ENDPOINT_PROFILE_<UPSTREAM>`; the operator must
2879
+ * configure the external service's own env directly. */
2880
+ linksApplied: boolean;
2881
+ lastError?: string;
2882
+ injectedProviders?: string[];
2883
+ linkedProviders?: string[];
2884
+ /** `@agentproto/llm-endpoint`'s own package version, read from the
2885
+ * resolved bin's `package.json` — best-effort, present only for
2886
+ * `owner:"daemon"` (the version of the code this registry would spawn /
2887
+ * did spawn). Undefined when unresolvable, or for `owner:"external"`
2888
+ * (an adopted process exposes no version of its own to probe). */
2889
+ version?: string;
2890
+ }
2891
+ interface LlmEndpointDescriptor {
2892
+ pid: number | null;
2893
+ port: number;
2894
+ baseUrl: string;
2895
+ status: LlmEndpointStatus;
2896
+ startedAt: string;
2897
+ stoppedAt?: string;
2898
+ lastError?: string;
2899
+ /** Provider names whose keys were injected into the child's env (never values). */
2900
+ injectedProviders?: string[];
2901
+ /** Upstream names mapped to an auth-profile link (`LLM_ENDPOINT_PROFILE_<P>`)
2902
+ * in the child's env. Profile IDS, not secrets — but only the provider names
2903
+ * are surfaced here, mirroring `injectedProviders`. */
2904
+ linkedProviders?: string[];
2905
+ /**
2906
+ * Only populated on the value returned by {@link LlmEndpointRegistry.start}
2907
+ * (never stored on the in-memory descriptor): `true` when the call reused an
2908
+ * already-running/starting child instead of spawning — so the caller can
2909
+ * tell a fresh `start` (new port/tokens applied) from a no-op idempotent hit
2910
+ * (old descriptor returned, nothing new applied). Mirrors `start_browser`.
2911
+ */
2912
+ wasAlreadyRunning?: boolean;
2913
+ }
2914
+
2766
2915
  /**
2767
2916
  * RemoteController — owns the gateway's "publish to the internet" state.
2768
2917
  *
@@ -2970,6 +3119,14 @@ interface PairingRecord {
2970
3119
  /** Set on a pairing made under the retired pair/v1 protocol: listed and
2971
3120
  * revocable, never served — its client is told to re-pair. */
2972
3121
  legacy?: true;
3122
+ /** Set when this pairing was made under an offer minted with
3123
+ * `agentproto pair offer --host` (PR-C) — i.e. the offering daemon's own
3124
+ * intent, recorded server-side at handshake time from the OfferEntry it
3125
+ * itself created, never from the (unauthenticated) `scope` param a client
3126
+ * presented in the offer URL. Absent for an ordinary remote-control
3127
+ * pairing. Purely informational here — it does not change how this
3128
+ * pairing is served. */
3129
+ scope?: "host";
2973
3130
  }
2974
3131
  /** Mode of a served channel — first-contact offer vs. an established reconnect. */
2975
3132
  type PairingChannelMode = "offer" | "reconnect";
@@ -3003,12 +3160,21 @@ interface CreatedOffer {
3003
3160
  * falls back to `HOSTED_RENDEZVOUS_URL`. Surfaced so `pair offer` can flag,
3004
3161
  * never silently, that the daemon is relaying through our infrastructure. */
3005
3162
  rendezvousIsHostedDefault: boolean;
3163
+ /** Echoes `CreateOfferInput.scope`. `"host"` when minted with `--host`;
3164
+ * absent for a plain offer. */
3165
+ scope?: "host";
3006
3166
  }
3007
3167
  interface CreateOfferInput {
3008
3168
  /** Time-to-live in ms. Default 10 minutes. */
3009
3169
  ttlMs?: number;
3010
3170
  /** Rendezvous URL override; falls back to the configured default. */
3011
3171
  rendezvousUrl?: string;
3172
+ /** Mint a HOST-scoped offer (PR-C): the accepting side may register this
3173
+ * daemon as a driveable host (`devices add`), not just remote-control it.
3174
+ * Recorded on the resulting `OfferEntry`/`PairingRecord` server-side — this
3175
+ * is the authoritative record of what was granted, independent of the
3176
+ * `scope` param an accepting client's offer URL happens to carry. */
3177
+ scope?: "host";
3012
3178
  }
3013
3179
  interface PairingRegistryDeps {
3014
3180
  /** Lazily load (create on first use) the daemon identity. Kept lazy so a
@@ -3052,17 +3218,174 @@ interface PairingRegistry {
3052
3218
  createOffer(input?: CreateOfferInput): Promise<CreatedOffer>;
3053
3219
  /** All persisted pairings (copies). Loads `pairings.json` on first call. */
3054
3220
  list(): Promise<PairingRecord[]>;
3221
+ /** Rename a pairing by fingerprint or name (cosmetic only — no effect on
3222
+ * auth or routing). Returns false when nothing matched; throws on an
3223
+ * empty `newName`. */
3224
+ rename(idOrName: string, newName: string): Promise<boolean>;
3055
3225
  /** Drop a pairing by fingerprint or name; stops its rendezvous connections.
3056
3226
  * Returns false when nothing matched. */
3057
3227
  revoke(idOrName: string): Promise<boolean>;
3228
+ /** Is a channel (offer or reconnect) currently served for this fingerprint? */
3229
+ isOnline(fingerprint: string): boolean;
3058
3230
  /** Start standing reconnect connections for every persisted pairing. Call
3059
3231
  * after the gateway is up (so the injected `serve` can reach it). */
3060
3232
  startAutoconnect(): Promise<void>;
3061
3233
  /** Tear down every offer + reconnect connection and served channel. */
3062
3234
  shutdown(): Promise<void>;
3063
3235
  }
3236
+ /**
3237
+ * Read-only snapshot of `pairings.json` for callers that just want the
3238
+ * persisted records — no `dial`/`serve`/`loadIdentity` deps, no rendezvous
3239
+ * connections started (e.g. `agentproto doctor`'s devices check). Mirrors
3240
+ * `ensureLoaded`'s parse + v1-legacy flagging; never throws — a missing or
3241
+ * malformed file just yields `[]`.
3242
+ */
3243
+ declare function readPairingsSnapshot(path?: string): Promise<PairingRecord[]>;
3064
3244
  declare function createPairingRegistry(deps: PairingRegistryDeps): PairingRegistry;
3065
3245
 
3246
+ /**
3247
+ * Daemon-side HOST registry (DEVICES-PLAN PR-C) — the "client" half of
3248
+ * pair/v2, living on the daemon so it's reachable over both an HTTP route
3249
+ * and an MCP tool, not just CLI-local bookkeeping (`client-pairings.ts` /
3250
+ * `pair-transport.ts` are that CLI-local equivalent today).
3251
+ *
3252
+ * Where `pairing-registry.ts` is what a daemon uses to let OTHER machines
3253
+ * pair with and remote-control IT, `host-registry.ts` is what a daemon uses
3254
+ * to register and drive OTHER daemons — reverse pairing. `add()` accepts a
3255
+ * HOST-scoped offer (`agentproto pair offer --host`, offer-url.ts's
3256
+ * `scope`), runs the pair/v2 client handshake, and persists the resulting
3257
+ * pair root; `forwardHttp()` dials on demand, reconnecting over the current
3258
+ * (then previous) epoch token exactly like `pair-transport.ts`'s
3259
+ * `openPairChannel`, forwards one HTTP request over the resulting tunnel
3260
+ * client, and closes. There is no standing connection here — unlike
3261
+ * `PairingRegistry`, a `HostRegistry` never autoconnects or keeps a
3262
+ * reconnect loop parked at the broker; every reach-out is a fresh dial.
3263
+ * That also means shutdown needs no teardown: there is nothing standing to
3264
+ * tear down.
3265
+ *
3266
+ * ## The scope gate
3267
+ *
3268
+ * `add()` REFUSES (throws) any offer whose parsed `scope !== "host"` before
3269
+ * ever dialing anything. This is the default-deny gate PR-C exists to add:
3270
+ * an ordinary "remote-control me" offer (today's `pair offer`, no `--host`)
3271
+ * must not silently become a host registration, even though the underlying
3272
+ * wire capability is identical either way (see offer-url.ts's "Offer scope"
3273
+ * and pairing-registry.ts's `OfferEntry.scope` doc comment for why the
3274
+ * offering daemon's OWN server-side record — not the URL's plaintext
3275
+ * `scope` param — is what's authoritative about what it granted). There is
3276
+ * deliberately no override flag.
3277
+ *
3278
+ * Persisted to `hosts.json` (`~/.agentproto/hosts.json` by default) with the
3279
+ * same atomic-write discipline as `pairings.json` (mkdir, write to a
3280
+ * pid-keyed tmp file at 0600, chmod, rename) and the same lazy-load-on-
3281
+ * first-use pattern.
3282
+ *
3283
+ * ## Online tracking
3284
+ *
3285
+ * `isOnline(fingerprint)` mirrors `PairingRegistry.isOnline`: it's true only
3286
+ * while a `forwardHttp()` call for that fingerprint is in flight (a
3287
+ * reference count, incremented on dial and decremented in a `finally`).
3288
+ * There is no live probe backing `list()` — that would make `devices list`
3289
+ * slow/flaky over N hosts — so `list()`'s `online` is only ever true
3290
+ * immediately after (or during) an actual `forwardHttp`/`devices status`
3291
+ * call, never a background heartbeat. That's enough to make `devices list`
3292
+ * show the right thing right after `devices status`/`exec`, which is what
3293
+ * matters for this PR; a standing/autoconnect host connection with a real
3294
+ * liveness signal is PR-D's to build.
3295
+ */
3296
+
3297
+ /** `hosts.json` format. */
3298
+ declare const HOSTS_VERSION: 1;
3299
+ /** One persisted host — a daemon this daemon can drive. */
3300
+ interface HostRecord {
3301
+ /** The target daemon's identity fingerprint (32 hex). */
3302
+ fingerprint: string;
3303
+ /** Human-facing label; defaults to the fingerprint. */
3304
+ name: string;
3305
+ /** Target daemon's static X25519 public key, standard base64 SPKI DER —
3306
+ * pinned at `add()` time. */
3307
+ daemonX25519Pub: string;
3308
+ /** Target daemon's static Ed25519 public key, standard base64 SPKI DER —
3309
+ * pinned at `add()` time. */
3310
+ daemonEd25519Pub: string;
3311
+ /** Rendezvous endpoint to reach the host through. */
3312
+ rendezvousUrl: string;
3313
+ /** Long-term shared secret (base64), from which epoch routing tokens
3314
+ * derive — same derivation as `PairingRecord.pairRoot`. */
3315
+ pairRoot: string;
3316
+ /** ISO-8601 first-add timestamp. */
3317
+ createdAt: string;
3318
+ /** ISO-8601 of the most recent successful `forwardHttp`. */
3319
+ lastSeen: string;
3320
+ /** Set on a host added under the retired pair/v1 protocol. Present for
3321
+ * parity with `PairingRecord`/`ClientPairing`; pair/v1 offers are refused
3322
+ * by `parseOfferUrl` before `add()` ever sees them, so this is currently
3323
+ * unreachable — kept so a future downgrade path has somewhere to flag it. */
3324
+ legacy?: true;
3325
+ }
3326
+ interface HostRegistryDeps {
3327
+ /** Dial a rendezvous WS and adapt it to a `FrameSink`. Same shape as
3328
+ * `PairingRegistryDeps.dial` — the CLI's `daemonDialRendezvous` satisfies
3329
+ * it directly. Rejects if the dial fails or `signal` aborts. */
3330
+ dial: (wsUrl: string, signal: AbortSignal) => Promise<FrameSink>;
3331
+ /** Path to `hosts.json`. Defaults to `~/.agentproto/hosts.json`. */
3332
+ hostsPath?: string;
3333
+ /** Injectable clock (ms). Defaults to Date.now. */
3334
+ now?: () => number;
3335
+ /** Diagnostic log sink. */
3336
+ log?: (line: string) => void;
3337
+ /** Rendezvous dial ceiling per attempt. Default 15s. */
3338
+ dialTimeoutMs?: number;
3339
+ /** Handshake ceiling per attempt. Default 15s. */
3340
+ handshakeTimeoutMs?: number;
3341
+ }
3342
+ interface ForwardHttpRequest {
3343
+ method: string;
3344
+ path: string;
3345
+ headers?: Record<string, string>;
3346
+ body?: Uint8Array;
3347
+ }
3348
+ interface ForwardHttpResponse {
3349
+ status: number;
3350
+ headers: Record<string, string>;
3351
+ body: Uint8Array;
3352
+ }
3353
+ interface HostRegistry {
3354
+ /**
3355
+ * Accept an offer URL and register the target daemon as a driveable host.
3356
+ * REFUSES (throws a plain `Error`) unless the parsed offer has
3357
+ * `scope === "host"` — no dial is attempted in that case. Otherwise: dials
3358
+ * the offer's rendezvous, runs the pair/v2 client handshake, verifies the
3359
+ * derived peer fingerprint matches the offer's `id` (defence in depth,
3360
+ * mirrors `pair-transport.ts`'s `acceptOffer`), derives the pair root, and
3361
+ * persists a `HostRecord` (upserted by fingerprint — re-adding replaces).
3362
+ */
3363
+ add(offerUrl: string, name?: string): Promise<{
3364
+ fingerprint: string;
3365
+ name: string;
3366
+ rendezvousUrl: string;
3367
+ }>;
3368
+ /** All persisted hosts (copies). Loads `hosts.json` on first call. */
3369
+ list(): Promise<HostRecord[]>;
3370
+ /** Rename a host (fingerprint or current name). Returns false when nothing
3371
+ * matched; throws on an empty `newName`. */
3372
+ rename(idOrName: string, newName: string): Promise<boolean>;
3373
+ /** Drop a host by fingerprint or name. Returns false when nothing matched. */
3374
+ revoke(idOrName: string): Promise<boolean>;
3375
+ /** Is a `forwardHttp` call for this fingerprint in flight right now? */
3376
+ isOnline(fingerprint: string): boolean;
3377
+ /**
3378
+ * On-demand: dial the host, run the client handshake against the current
3379
+ * epoch's tokens (falling back to the previous epoch, bridging clock
3380
+ * skew — same two-attempt shape as `pair-transport.ts`'s
3381
+ * `openPairChannel`), forward one HTTP request over the resulting tunnel
3382
+ * client, then close. Throws with a re-pair hint if every attempt hangs up
3383
+ * on the hello (likely a pair/v1 peer).
3384
+ */
3385
+ forwardHttp(idOrName: string, req: ForwardHttpRequest): Promise<ForwardHttpResponse>;
3386
+ }
3387
+ declare function createHostRegistry(deps: HostRegistryDeps): HostRegistry;
3388
+
3066
3389
  /**
3067
3390
  * Shared types for pluggable sandbox providers.
3068
3391
  *
@@ -4394,6 +4717,18 @@ interface SpawnAgentSessionDeps {
4394
4717
  * manifest not covering the requested model still reaches the driver,
4395
4718
  * same as before this guard existed. */
4396
4719
  listCatalogModels?: CatalogModelsLister;
4720
+ /** Ensure the local `llm-endpoint` proxy sidecar (`LlmEndpointRegistry`)
4721
+ * is up before a spawn that bills through it. Without this hook, a spawn
4722
+ * whose resolved gateway is `"llm-endpoint"` assumed the sidecar was
4723
+ * already running (started externally, or via the `llm_endpoint_start`
4724
+ * MCP tool) — a fresh install with `features.llmEndpoint` on but nothing
4725
+ * having called that tool yet would spawn fine and only fail once the
4726
+ * adapter made its first request. Wired to the registry's own
4727
+ * (idempotent) `start()` at the composition root; omitted ⇒ unchanged
4728
+ * pre-existing behavior. Best-effort: a rejection here is swallowed by
4729
+ * the caller, not surfaced as a spawn failure — the adapter's own first
4730
+ * request is left to report the real connection error. */
4731
+ ensureLlmEndpointRunning?: () => Promise<void>;
4397
4732
  }
4398
4733
  interface SpawnAgentSessionInput {
4399
4734
  adapter: string;
@@ -4455,7 +4790,19 @@ interface SpawnAgentSessionInput {
4455
4790
  * blank. Not exposed on the MCP `agent_start` tool today — only the
4456
4791
  * HTTP route (`sessions restart`) passes this. */
4457
4792
  resumeSessionId?: string;
4458
- prompt?: string;
4793
+ /** The caller's initial ask — a plain string (the common case, composed
4794
+ * with the role/AGENTS.md/RULES.md preamble below into one string turn),
4795
+ * or a content block / block array (an attachment-bearing first message,
4796
+ * same loose shape `POST /sessions/:id/prompt` accepts). The block form
4797
+ * is a narrow path: composing daemon-synthesized preamble TEXT onto a
4798
+ * turn that opens with an image isn't meaningful string concatenation,
4799
+ * so it SKIPS the whole preamble composition below (role disposition,
4800
+ * AGENTS.md, RULES.md, parent-lineage line, posture-as-prompt fallback)
4801
+ * and is sent to the adapter verbatim — see `effectivePrompt`'s doc.
4802
+ * Acceptable because this path is human/UI-initiated (an attachment
4803
+ * typed into a chat composer), not the orchestrator-driven role spawns
4804
+ * that actually depend on the composed disposition text. */
4805
+ prompt?: string | Record<string, unknown> | unknown[];
4459
4806
  label?: string;
4460
4807
  /** Explicit session title (SPEC-3 FIX C, `agentproto sessions start
4461
4808
  * --title`). When set, it wins over the first-sentence derivation from
@@ -4955,6 +5302,8 @@ interface BranchGcPlanView {
4955
5302
  includeReviewed: boolean;
4956
5303
  generatedAt: string;
4957
5304
  otherRemoteRefs: number;
5305
+ /** true = base-remote `fetch --prune` succeeded; false = failed; null = skipped / no remote. */
5306
+ fetched: boolean | null;
4958
5307
  entries: BranchGcPlanEntryView[];
4959
5308
  }
4960
5309
  /** kind → class → count, and kind → (status | "protected") → count. */
@@ -5158,12 +5507,10 @@ type AgentAdapterResolver = (slug: string) => Promise<{
5158
5507
  }): Promise<AgentSessionLike>;
5159
5508
  /** Display label for the descriptor's `command` field. */
5160
5509
  commandPreview?: string;
5161
- /** Best-effort per-session usage reader (adapter-specific, e.g. hermes state.db). */
5162
- readUsage?: (adapterSessionId: string) => Promise<{
5163
- costUsd?: number;
5164
- tokensIn?: number;
5165
- tokensOut?: number;
5166
- } | null>;
5510
+ /** Best-effort per-session usage reader (adapter-specific, e.g. hermes
5511
+ * state.db, the claude-code transcript JSONL). `ctx` carries the spawn's
5512
+ * cwd + isolated config dir for readers that locate their store by them. */
5513
+ readUsage?: (adapterSessionId: string, ctx?: UsageReadContext) => Promise<UsageReadResult | null>;
5167
5514
  /** AIP-45 `options[]` this adapter's manifest declares (id + type only —
5168
5515
  * no spawn internals). Lets `session-spawn.ts` fold a config-level
5169
5516
  * `defaults.skills` list into `options.skills` using the shape the
@@ -6320,16 +6667,32 @@ interface SessionDescriptor {
6320
6667
  * `status` to `"error"` directly (a genuinely terminal row the existing
6321
6668
  * crash/error handling already catches). This is the twin gap for a
6322
6669
  * turn that fails IN-BAND while the process stays alive: `status` stays
6323
- * `"running"`, `lastError` stays unset (reserved for `markCrashed`), and
6324
- * nothing else distinguishes the session from one that simply finished
6325
- * a clean turn and is idle. Folded into the same "stalled" activity/UI
6326
- * treatment a mid-turn stall gets (see sessionsTree.logic.ts's
6327
- * `activityFor`) rather than a new state, on the theory that both mean
6328
- * "the last thing this session did needs a look, not a re-prompt on
6329
- * faith". Stamped at turn-end when `turnEndReason === "error"`; cleared
6330
- * the moment a LATER turn completes without one (same reset shape as
6670
+ * `"running"`, `lastError` stays unset (still reserved for `markCrashed`
6671
+ * — see `lastTurnErrorMessage` below for the message text this field's
6672
+ * own turn carries), and nothing else distinguishes the session from
6673
+ * one that simply finished a clean turn and is idle. Folded into the
6674
+ * same "stalled" activity/UI treatment a mid-turn stall gets (see
6675
+ * sessionsTree.logic.ts's `activityFor`) rather than a new state, on
6676
+ * the theory that both mean "the last thing this session did needs a
6677
+ * look, not a re-prompt on faith". Stamped at turn-end when
6678
+ * `turnEndReason === "error"` — including the case where the adapter's
6679
+ * stream generator simply returned after emitting an in-band `error`
6680
+ * event with no explicit turn-end and no thrown exception, which
6681
+ * `runAgentTurn`'s P5 synthetic-reason fallback now classifies as
6682
+ * `"error"` rather than defaulting to `"exited"`. Cleared the moment a
6683
+ * LATER turn completes without one (same reset shape as
6331
6684
  * `resumeAttempts`/`restartAttempts`). Detection + signal only. */
6332
6685
  lastTurnErroredAt?: string;
6686
+ /** The captured error text for the turn `lastTurnErroredAt` timestamps —
6687
+ * `evt.error.message` off the in-band stream `error` event observed
6688
+ * during that turn (see `runAgentTurn`'s `turnErrorMessage`). Distinct
6689
+ * from `lastError` (reserved for `markCrashed`) and from
6690
+ * `rt.lastErrorMessage` (an internal, cross-turn, never-cleared runtime
6691
+ * field — this one is the public, turn-scoped descriptor projection of
6692
+ * it). Absent when the adapter reported `reason:"error"` on its own
6693
+ * turn-end without a preceding `error`-kind stream event. Stamped and
6694
+ * cleared together with `lastTurnErroredAt`. */
6695
+ lastTurnErrorMessage?: string;
6333
6696
  /** True when the LAST completed turn produced zero assistant output and
6334
6697
  * zero tool calls (mirrors `SessionTurnEndEvent.empty` — see that
6335
6698
  * field's doc). Persisted so `monitorSessionWait`'s synchronous
@@ -6359,6 +6722,29 @@ interface SessionDescriptor {
6359
6722
  * the underlying `killedMidTurn`/`endedReason`). The in-place resume path
6360
6723
  * never auto-retries the interrupted prompt. */
6361
6724
  interrupted?: boolean;
6725
+ /** The `SessionsRegistry.bootId` of the FIRST daemon boot that found this
6726
+ * row interrupted — i.e. which restart cut its turn off. Stamped by the
6727
+ * boot reload (`loadHistorySnapshot`) on an interrupted row that doesn't
6728
+ * carry one yet; dropped whenever the row is interrupted AGAIN (so the next
6729
+ * boot restamps it) and on the next successful turn-end (with the markers it
6730
+ * qualifies). `interruptedAtBoot === registry.bootId` is how the
6731
+ * continue-interrupted verb and boot pass pick "interrupted by the LAST
6732
+ * restart" apart from a stale interruption nobody picked back up. PERSISTED
6733
+ * (unlike the derived `interrupted`) — the boot that stamped it may not be
6734
+ * the one reading it. */
6735
+ interruptedAtBoot?: string;
6736
+ /** How many automatic continue prompts the daemon has sent this row
6737
+ * (`daemon.continueInterruptedOnBoot`) since its last successful turn-end.
6738
+ * Incremented BEFORE the prompt is sent, so a continue turn that itself
6739
+ * gets cut off by another restart still counts; reset on the next
6740
+ * successful turn-end. The boot pass stops at
6741
+ * `MAX_AUTO_CONTINUE_ATTEMPTS` — the no-loop bound for a turn that keeps
6742
+ * taking the daemon down with it. PERSISTED; absent until the first one. */
6743
+ autoContinueAttempts?: number;
6744
+ /** `bootId` of the boot whose pass last auto-continued this row — the
6745
+ * at-most-one-auto-continue-per-restart guard. Reset with
6746
+ * `autoContinueAttempts`. */
6747
+ lastAutoContinueBoot?: string;
6362
6748
  /** How many in-place resume attempts have FAILED in a row for this session
6363
6749
  * (§5 cap/backoff). Incremented on every failed `maybeResumeAgent` attempt —
6364
6750
  * the resume throws, the adapter rejects the id, or the spawn returns null —
@@ -6717,6 +7103,19 @@ interface SessionDescriptor {
6717
7103
  /** Cumulative input / output token counts (same source + cadence as costUsd). */
6718
7104
  tokensIn?: number;
6719
7105
  tokensOut?: number;
7106
+ /** Cumulative prompt-cache read / cache-creation / reasoning tokens, from
7107
+ * the adapter's usage reader (claude-code transcript, hermes state.db,
7108
+ * opencode.db). Absent when the adapter doesn't expose them — never
7109
+ * zero-filled. See `UsageDetail` in usage.ts. */
7110
+ cacheReadTokens?: number;
7111
+ cacheWriteTokens?: number;
7112
+ reasoningTokens?: number;
7113
+ /** Tool calls the daemon observed across completed turns (cumulative).
7114
+ * Stamped at the first turn end, so absent = no turn has finished yet. */
7115
+ toolCalls?: number;
7116
+ /** Wall time spent inside turns, cumulative ms. Same cadence as
7117
+ * `toolCalls`. */
7118
+ durationMs?: number;
6720
7119
  /** Context-window size + tokens-in-context from the latest `usage_update`
6721
7120
  * event (ACP adapters that report a context window). Refreshed live as
6722
7121
  * usage_update events arrive, not just at turn-end. */
@@ -7636,6 +8035,21 @@ interface SessionsRegistry {
7636
8035
  reason?: string;
7637
8036
  interrupted?: boolean;
7638
8037
  }>;
8038
+ /** A currently-RUNNING background task's info plus a tail of its output
8039
+ * file, for the UI's "click a live background-task row to peek at its
8040
+ * output" affordance. Scoped to `SessionDescriptor.backgroundTasks` on
8041
+ * purpose — a settled task is dropped from that list moments after it
8042
+ * reports terminal status (see `noteBackgroundTask`), so this never
8043
+ * becomes an arbitrary-file-read: the caller supplies a `taskId`, never a
8044
+ * path, and only a path the agent itself already reported for a task
8045
+ * still tracked as running is ever read. Returns `null` when the session
8046
+ * or the task is unknown (the caller surfaces 404); `tail` is `null` when
8047
+ * the task has no `outputFile` or the file can't be read (not yet
8048
+ * created, already rotated away, ...). */
8049
+ readBackgroundTaskTail(id: string, taskId: string): {
8050
+ task: SessionBackgroundTask;
8051
+ tail: string | null;
8052
+ } | null;
7639
8053
  /** Eagerly resume ONE dead-but-resumable agent-cli session IN PLACE,
7640
8054
  * WITHOUT a prompt — the boot-time counterpart to the lazy resume that
7641
8055
  * `sendPrompt`/`enqueuePrompt` trigger on the first prompt after a restart
@@ -7658,6 +8072,17 @@ interface SessionsRegistry {
7658
8072
  * touching the adapter, so the bounded boot pass can call it per row and
7659
8073
  * tally the results. */
7660
8074
  resumeOnBoot(id: string): Promise<EagerResumeOutcome>;
8075
+ /** Opaque id unique to this registry instance — one per daemon boot.
8076
+ * Stamped as `interruptedAtBoot` on rows the boot reload finds interrupted,
8077
+ * so `interruptedAtBoot === bootId` means "interrupted by the restart that
8078
+ * preceded THIS boot" (continue-interrupted). */
8079
+ readonly bootId: string;
8080
+ /** Book one automatic continue prompt against `id` (increment
8081
+ * `autoContinueAttempts`, stamp `lastAutoContinueBoot = bootId`) and
8082
+ * persist. Called by the continue-on-boot pass BEFORE it sends, so the
8083
+ * no-loop bound holds even when the continue turn is itself cut off by the
8084
+ * next restart. No-op for an unknown id. */
8085
+ recordAutoContinue(id: string): void;
7661
8086
  /** Cancel the in-flight turn on a live agent-cli session and leave the
7662
8087
  * session itself alive and idle — the bare "interrupt, no next prompt"
7663
8088
  * primitive `sendPrompt`/`enqueuePrompt`'s `opts.interrupt` arm lacks
@@ -8167,8 +8592,10 @@ interface SpawnAgentInput {
8167
8592
  /** Optional initial prompt to dispatch immediately. The promise
8168
8593
  * the registry returns resolves AFTER the spawn — the prompt
8169
8594
  * runs in the background, projecting events into the ring
8170
- * buffer. Skip to spawn idle. */
8171
- initialPrompt?: string;
8595
+ * buffer. Skip to spawn idle. A plain string, or a content-block
8596
+ * prompt (attachment-bearing first message) — see
8597
+ * `SpawnAgentSessionInput.prompt`'s doc. */
8598
+ initialPrompt?: string | Record<string, unknown> | unknown[];
8172
8599
  /** The daemon-composed SYSTEM slice of {@link initialPrompt} — the part
8173
8600
  * it synthesized ahead of the CALLER's ask (role disposition, lineage
8174
8601
  * line, AGENTS.md, posture preamble). The adapter still receives the
@@ -8260,12 +8687,7 @@ interface SpawnAgentInput {
8260
8687
  * cost/token fields on the descriptor. Adapter-specific (e.g. hermes
8261
8688
  * reads its state.db keyed by the adapter session id). Omit for adapters
8262
8689
  * with no usage source. */
8263
- readUsage?: () => Promise<{
8264
- model?: string;
8265
- costUsd?: number;
8266
- tokensIn?: number;
8267
- tokensOut?: number;
8268
- } | null>;
8690
+ readUsage?: () => Promise<UsageReadResult | null>;
8269
8691
  /** Opt this session into Langfuse tracing (prompt/completion + tool spans +
8270
8692
  * tokens/cost). Effective opt-in is `trace ?? opts.langfuseTracingDefault ?? false`. */
8271
8693
  trace?: boolean;
@@ -8322,15 +8744,12 @@ type PendingAgentOutcome = {
8322
8744
  commandPreview?: string;
8323
8745
  resumable?: boolean;
8324
8746
  nativeTerminalResume?: boolean;
8325
- readUsage?: () => Promise<{
8326
- model?: string;
8327
- costUsd?: number;
8328
- tokensIn?: number;
8329
- tokensOut?: number;
8330
- } | null>;
8747
+ readUsage?: () => Promise<UsageReadResult | null>;
8331
8748
  /** Dispatched now that the tree + driver session both exist — never
8332
- * passed to `spawnAgentPending`, which would race the tree. */
8333
- initialPrompt?: string;
8749
+ * passed to `spawnAgentPending`, which would race the tree. A plain
8750
+ * string, or a content-block prompt (attachment-bearing first
8751
+ * message) — see `SpawnAgentSessionInput.prompt`'s doc. */
8752
+ initialPrompt?: string | Record<string, unknown> | unknown[];
8334
8753
  /** The daemon-composed SYSTEM slice of `initialPrompt` (see
8335
8754
  * `SpawnAgentInput.initialPromptSystem`) — threaded through the
8336
8755
  * placeholder so the deferred dispatch records it as a system turn. */
@@ -8606,6 +9025,163 @@ declare function runEagerResumePass(opts: {
8606
9025
  isServed?: (desc: SessionDescriptor) => boolean;
8607
9026
  }): Promise<EagerResumeSummary>;
8608
9027
 
9028
+ /**
9029
+ * Continue-interrupted: pick back up the sessions a daemon restart cut off
9030
+ * mid-turn, by sending each an explicit "continue" prompt.
9031
+ *
9032
+ * Everything below the prompt already exists — the persisted registry is the
9033
+ * snapshot, `maybeResumeAgent` resumes a dead row in place (lazily on its first
9034
+ * prompt, or eagerly at boot via `eager-resume.ts`), and the row keeps the
9035
+ * derived `interrupted` marker until its next successful turn-end. What a
9036
+ * restart does NOT do, by design, is re-run the interrupted prompt (prompts
9037
+ * aren't idempotent). This module is the explicit, opt-in replacement for that:
9038
+ * a NEW prompt that tells the agent its last turn was cut off and to check the
9039
+ * state on disk before carrying on.
9040
+ *
9041
+ * Two callers share one eligibility rule + one sender:
9042
+ * - the manual verb (`session_continue_interrupted` MCP tool,
9043
+ * `POST /sessions/continue-interrupted`, `agentproto sessions
9044
+ * continue-interrupted`) — `mode: "manual"`, dry-run by default;
9045
+ * - the opt-in boot pass (`daemon.continueInterruptedOnBoot`) —
9046
+ * `mode: "boot"`, run by serve.ts right after the eager resume pass.
9047
+ *
9048
+ * Eligibility (both modes): an agent-cli row that is `interrupted` BY THE LAST
9049
+ * RESTART (`interruptedAtBoot === registry.bootId` — a stale interruption from
9050
+ * an older restart that nobody picked up is left alone), resumable under the
9051
+ * attempt cap (`canResume`), and not already busy. The prompt goes through
9052
+ * `enqueuePrompt`, the normal prompt path, so a dead-but-resumable row
9053
+ * lazy-resumes with its billing auth re-resolved exactly as a human prompt
9054
+ * would.
9055
+ *
9056
+ * No-loop rules (boot mode only — a human pressing the button is intent):
9057
+ * - never auto-continue a row whose resume has failed (`resumeAttempts > 0`:
9058
+ * the eager pass, or an earlier attempt, could not bring it back);
9059
+ * - at most one auto-continue per restart (`lastAutoContinueBoot`);
9060
+ * - at most `MAX_AUTO_CONTINUE_ATTEMPTS` auto-continues without a successful
9061
+ * turn-end in between (`autoContinueAttempts`, booked BEFORE sending), so a
9062
+ * continue turn that keeps getting cut off — or keeps taking the daemon
9063
+ * down with it — stops being retried.
9064
+ *
9065
+ * Ordering (boot mode): MUST run after the supervisor is re-armed, same
9066
+ * constraint as `eager-resume.ts` — the continue turn's `turn-end` is exactly
9067
+ * what a re-armed completion policy is waiting for — and after the eager pass,
9068
+ * so a row whose eager resume failed is recognisable (`resumeAttempts`) and
9069
+ * skipped rather than lazily retried.
9070
+ */
9071
+
9072
+ /** The prompt sent when the caller doesn't supply one. */
9073
+ declare const DEFAULT_CONTINUE_PROMPT: string;
9074
+ /** Transcript provenance for the continue prompt (`opts.source`). */
9075
+ declare const CONTINUE_PROMPT_SOURCE = "daemon:continue-interrupted";
9076
+ /** Cap on automatic continue prompts sent to one row without a successful
9077
+ * turn-end in between (boot mode). 2 = the original interruption gets one
9078
+ * auto-continue, and if a second restart cuts THAT turn off it gets one more;
9079
+ * after that only a human (manual verb) re-sends. */
9080
+ declare const MAX_AUTO_CONTINUE_ATTEMPTS = 2;
9081
+ type ContinueInterruptedSkipReason =
9082
+ /** Unknown id (explicit `ids` filter only). */
9083
+ "unknown"
9084
+ /** Not interrupted at all — no turn was cut off. */
9085
+ | "not-interrupted"
9086
+ /** Interrupted by an EARLIER restart, not the last one. */
9087
+ | "stale-interrupt"
9088
+ /** Not in-place resumable (not agent-cli / missing adapter session / archived). */
9089
+ | "not-resumable"
9090
+ /** Already burned through `MAX_RESUME_ATTEMPTS` failed resumes. */
9091
+ | "resume-cap-exhausted"
9092
+ /** Mid-turn already — someone got there first. */
9093
+ | "busy"
9094
+ /** Boot mode: a resume attempt has failed on this row. */
9095
+ | "resume-failed"
9096
+ /** Boot mode: already auto-continued by this boot. */
9097
+ | "already-auto-continued"
9098
+ /** Boot mode: `MAX_AUTO_CONTINUE_ATTEMPTS` reached without recovering. */
9099
+ | "auto-continue-cap"
9100
+ /** Excluded by the cross-process `isServed` gate (another daemon's row). */
9101
+ | "not-served";
9102
+ type ContinueInterruptedOutcome = {
9103
+ id: string;
9104
+ name?: string;
9105
+ status: "eligible";
9106
+ } | {
9107
+ id: string;
9108
+ name?: string;
9109
+ status: "sent";
9110
+ } | {
9111
+ id: string;
9112
+ name?: string;
9113
+ status: "skipped";
9114
+ reason: ContinueInterruptedSkipReason;
9115
+ } | {
9116
+ id: string;
9117
+ name?: string;
9118
+ status: "failed";
9119
+ error: string;
9120
+ };
9121
+ interface ContinueInterruptedResult {
9122
+ dryRun: boolean;
9123
+ /** The prompt that was (or, on a dry run, would be) sent. */
9124
+ prompt: string;
9125
+ /** The boot the candidates were interrupted before (`registry.bootId`). */
9126
+ bootId: string;
9127
+ /** One entry per considered row — every interrupted row when `ids` is
9128
+ * omitted, every requested id otherwise. Eligible-first ordering matches
9129
+ * the send order (newest activity first). */
9130
+ sessions: ContinueInterruptedOutcome[];
9131
+ /** Counts by outcome status. */
9132
+ eligible: number;
9133
+ sent: number;
9134
+ skipped: number;
9135
+ failed: number;
9136
+ }
9137
+ type ContinueInterruptedMode = "manual" | "boot";
9138
+ /** Decide one row. `undefined` ⇒ eligible. Pure — reads the descriptor only. */
9139
+ declare function continueSkipReason(desc: SessionDescriptor, bootId: string, mode: ContinueInterruptedMode): ContinueInterruptedSkipReason | undefined;
9140
+ /**
9141
+ * List — and unless `dryRun` — continue the sessions interrupted by the last
9142
+ * restart. See the module docblock for eligibility, the no-loop rules, and the
9143
+ * ordering contract. No-throw per row: a send that fails admission (row went
9144
+ * busy, resume refused) is reported as `failed` and the pass carries on.
9145
+ */
9146
+ declare function continueInterruptedSessions(opts: {
9147
+ registry: Pick<SessionsRegistry, "list" | "get" | "enqueuePrompt" | "bootId" | "recordAutoContinue">;
9148
+ mode: ContinueInterruptedMode;
9149
+ /** Default true: report what would be sent, send nothing. */
9150
+ dryRun?: boolean;
9151
+ /** Restrict to these session ids. Omitted ⇒ every interrupted row. */
9152
+ ids?: readonly string[];
9153
+ /** Custom prompt. Omitted/blank ⇒ `DEFAULT_CONTINUE_PROMPT`. */
9154
+ prompt?: string;
9155
+ /** Max sends in flight at once (each may lazily resume an adapter). Clamped
9156
+ * to ≥1. Default 4, same as the eager pass. */
9157
+ concurrency?: number;
9158
+ /** Cross-process gate — see `runEagerResumePass`. Omitted ⇒ all served. */
9159
+ isServed?: (desc: SessionDescriptor) => boolean;
9160
+ /** Extra visibility filter (a scoped orchestrator's subtree). Rows it
9161
+ * rejects are dropped silently — never reported, never sent. */
9162
+ visible?: (desc: SessionDescriptor) => boolean;
9163
+ }): Promise<ContinueInterruptedResult>;
9164
+ /** Tally of the boot-time continue pass, for the boot-banner line. */
9165
+ interface ContinueOnBootSummary {
9166
+ /** False when `daemon.continueInterruptedOnBoot` is off (nothing ran). */
9167
+ enabled: boolean;
9168
+ /** Rows passing eligibility at selection time — the banner's denominator. */
9169
+ eligible: number;
9170
+ sent: number;
9171
+ skipped: number;
9172
+ failed: number;
9173
+ }
9174
+ /**
9175
+ * The boot pass: `continueInterruptedSessions` in `mode: "boot"`, not a dry
9176
+ * run, reduced to the banner tally. Caller (the gateway handle, invoked from
9177
+ * serve.ts) is responsible for the ordering contract in the module docblock.
9178
+ */
9179
+ declare function runContinueOnBootPass(opts: {
9180
+ registry: Parameters<typeof continueInterruptedSessions>[0]["registry"];
9181
+ concurrency: number;
9182
+ isServed?: (desc: SessionDescriptor) => boolean;
9183
+ }): Promise<ContinueOnBootSummary>;
9184
+
8609
9185
  /**
8610
9186
  * Daemon-lane PR provenance STAMPER.
8611
9187
  *
@@ -8820,8 +9396,16 @@ interface InboundWatcher {
8820
9396
  *
8821
9397
  * `mcpServerFactory` (index.ts) rebuilds a fresh McpServer per `/mcp`
8822
9398
  * request, so this module never throws — a bad app record must not take
8823
- * down every other request. Failures (a collided tool id, an unreadable
8824
- * `ui.path`) are skipped with a `console.warn`, not surfaced to the caller.
9399
+ * down every other request. A collided tool id is still skipped with a
9400
+ * `console.warn` (there's nowhere else for a second panel to live). Every
9401
+ * OTHER failure — a removed app dir, a missing/unreadable `ui.path`, a
9402
+ * failed or in-flight `ui.build` — still mounts the panel, serving a
9403
+ * readable HTML placeholder/error page in place of the real bundle
9404
+ * (app-ui-placeholder.ts) instead of skipping it outright: a `ui://`
9405
+ * resource that silently doesn't exist just shows as a JSON-RPC "resource
9406
+ * not found" in a host, which is what motivated this file's placeholder
9407
+ * page in the first place — a HOST-VISIBLE page, even a stand-in one, beats
9408
+ * a resource the host has to explain away as missing.
8825
9409
  */
8826
9410
 
8827
9411
  /**
@@ -10295,6 +10879,108 @@ declare function formatToolCall(toolName: string, args: unknown): string;
10295
10879
  */
10296
10880
  declare function formatToolResult(toolName: string | undefined, result: unknown, isError: boolean): string | null;
10297
10881
 
10882
+ /**
10883
+ * Pure sizing helper for a `tools/list` response — shared by
10884
+ * `scripts/measure-mcp-tools.mjs` (human-readable before/after report) and
10885
+ * the budget-guard test (`__tests__/tool-schema-budget.test.ts`) so both
10886
+ * measure the exact same thing: the JSON bytes an MCP client actually pays
10887
+ * for per tool (`description` + `inputSchema`), sorted biggest first.
10888
+ */
10889
+ interface MeasuredToolEntry {
10890
+ name: string;
10891
+ bytes: number;
10892
+ }
10893
+ interface ToolListMeasurement {
10894
+ entries: MeasuredToolEntry[];
10895
+ totalBytes: number;
10896
+ }
10897
+ interface MeasurableTool {
10898
+ name: string;
10899
+ description?: string;
10900
+ inputSchema?: unknown;
10901
+ }
10902
+ /** UTF-8 byte size of the `{description, inputSchema}` JSON a client
10903
+ * receives for one tool — the same two fields `tools/list` sends over the
10904
+ * wire, so this tracks prompt-token cost rather than just character count. */
10905
+ declare function measureTool(tool: MeasurableTool): number;
10906
+ declare function measureToolList(tools: readonly MeasurableTool[]): ToolListMeasurement;
10907
+
10908
+ /**
10909
+ * Long-form MCP tool documentation, read from `docs/mcp-tools/<name>.md`.
10910
+ *
10911
+ * The schema `description` on a tool/field is a contract: what it does, and
10912
+ * the one rule that prevents a wrong call — kept short so `tools/list` stays
10913
+ * cheap. Everything else (edge cases, interactions with other fields,
10914
+ * examples, history) lives here instead, fetched on demand by the `tool_help`
10915
+ * MCP tool (`tool-help-mcp.ts`) and the `agentproto help <tool>` CLI verb
10916
+ * (`packages/cli/src/commands/help.ts`) — both call the two functions below,
10917
+ * so the markdown file is the single source both surfaces read.
10918
+ *
10919
+ * `docs/mcp-tools/` sits at the PACKAGE root (a sibling of `src`/`dist`, not
10920
+ * the repo-level `docs/`) so it ships inside the published npm tarball —
10921
+ * see the `files` array in `package.json`. Resolved relative to this
10922
+ * module's own directory rather than by package name: whether this code is
10923
+ * running from `src` (vitest, no build) or `dist` (built/published), the
10924
+ * module's directory is always a direct child of the package root, so one
10925
+ * `..` reaches it either way — no `require.resolve` needed.
10926
+ */
10927
+ /** Full markdown help text for `name`, or `undefined` when no doc exists. */
10928
+ declare function getToolHelp(name: string): string | undefined;
10929
+ /** Every tool name with a help doc available — for a "did you mean" list. */
10930
+ declare function listToolHelpTopics(): string[];
10931
+ /** Extract a `## <topic>` section (case-insensitive) up to the next `##`
10932
+ * heading — shared by the `tool_help` MCP tool and the `agentproto help`
10933
+ * CLI verb so both slice a doc identically. `undefined` when no section
10934
+ * with that heading exists. */
10935
+ declare function extractHelpSection(doc: string, topic: string): string | undefined;
10936
+
10937
+ /**
10938
+ * Subtree usage rollup: one session's own usage plus the summed usage of
10939
+ * every session it (transitively) spawned — the shape `session_usage
10940
+ * { includeSubtree: true }` and `GET /sessions/:id/usage?includeSubtree=1`
10941
+ * return.
10942
+ *
10943
+ * Pure over a descriptor list (walked by `parentSessionId`, the same graph
10944
+ * `collectSubtree` uses for orchestrator scoping), so it's unit-testable
10945
+ * without a registry.
10946
+ *
10947
+ * Absent stays absent: a total is the sum of the sessions that REPORT the
10948
+ * field, and is omitted entirely when none of them do — a subtree of
10949
+ * cost-less sessions never reads as a measured $0.
10950
+ */
10951
+
10952
+ /** Summed usage across the root and all its descendants. */
10953
+ interface SubtreeUsageTotals {
10954
+ /** Sessions counted, root included. */
10955
+ sessions: number;
10956
+ costUsd?: number;
10957
+ tokensIn?: number;
10958
+ tokensOut?: number;
10959
+ cacheReadTokens?: number;
10960
+ cacheWriteTokens?: number;
10961
+ }
10962
+ /** One direct child of the root, with that child's OWN usage (its own
10963
+ * descendants roll up into `subtree`, not here). */
10964
+ interface SubtreeChildUsage {
10965
+ id: string;
10966
+ label?: string;
10967
+ status: SessionDescriptor["status"];
10968
+ costUsd?: number;
10969
+ tokensIn?: number;
10970
+ tokensOut?: number;
10971
+ }
10972
+ interface SessionUsageWithSubtree {
10973
+ self: SessionUsage;
10974
+ subtree: SubtreeUsageTotals;
10975
+ children: SubtreeChildUsage[];
10976
+ }
10977
+ /**
10978
+ * Roll `root`'s usage up over its subtree. `all` should be the FULL
10979
+ * descriptor list (archived included) so an archived intermediate session
10980
+ * doesn't sever the parent→child chain.
10981
+ */
10982
+ declare function rollupSessionSubtree(root: SessionDescriptor, all: readonly SessionDescriptor[]): SessionUsageWithSubtree;
10983
+
10298
10984
  /**
10299
10985
  * Dependency-injection surface for MCP credential resolution.
10300
10986
  *
@@ -10399,6 +11085,26 @@ declare function enrichRollupWithProviderQuota(rollup: UsageRollup, window: stri
10399
11085
  * such lines; skips any malformed or non-`usage_snapshot` line. Never throws. */
10400
11086
  declare function readUsageSnapshots(sessionId: string, baseDir?: string): Promise<UsageSnapshotRecord[]>;
10401
11087
 
11088
+ /**
11089
+ * Smart default for `features.llmEndpoint` (daemon-managed-gateway): an
11090
+ * operator who already configured a named endpoint, an upstream credential
11091
+ * link, or an explicit `llm-endpoint` custom route is clearly USING the
11092
+ * proxy already — booting with the sidecar off (today's default) would
11093
+ * silently break their next `route:{gateway:"llm-endpoint"}` spawn on a
11094
+ * fresh install. An operator who never touched any of that gets the
11095
+ * unchanged, conservative default (off).
11096
+ *
11097
+ * An explicit `configured` value (`config.json`'s `features.llmEndpoint`,
11098
+ * profile-overlaid) always wins, in either direction — this resolver only
11099
+ * fills in the gap when the operator left the flag unset.
11100
+ */
11101
+ /**
11102
+ * Resolve the EFFECTIVE `features.llmEndpoint` flag. `configured` is the
11103
+ * operator's merged config value (`undefined` when never set in either
11104
+ * `config.json` or the active profile).
11105
+ */
11106
+ declare function resolveEffectiveLlmEndpointFlag(configured: boolean | undefined): Promise<boolean>;
11107
+
10402
11108
  /**
10403
11109
  * MCP tools that drive the pairing registry — the daemon's "let a client pair
10404
11110
  * with me over an untrusted rendezvous" surface (design: DESIGN §6).
@@ -10420,6 +11126,90 @@ interface RegisterPairingToolsOptions {
10420
11126
  }
10421
11127
  declare function registerPairingTools(server: McpServer, opts: RegisterPairingToolsOptions): void;
10422
11128
 
11129
+ /**
11130
+ * Device registry — a read view over `PairingRegistry` (+, when wired,
11131
+ * `HostRegistry`) that presents each pairing/host as a "device" (DEVICES-PLAN
11132
+ * PR-A, hosts added PR-C). A paired CLIENT (today's `pair_offer`/`pair
11133
+ * accept`) gets `role: "client"`; a registered HOST (`pair offer --host` +
11134
+ * `devices add`, host-registry.ts) gets `role: "host"`.
11135
+ *
11136
+ * This is deliberately a thin computed view, not a second store: renaming
11137
+ * and revoking a client device mutate the same `pairings.json`
11138
+ * `PairingRegistry` already owns; a host device mutates the same
11139
+ * `hosts.json` `HostRegistry` — so `pair_*`/`/pairings` and
11140
+ * `device_*`/`/devices` are two surfaces over the same two sources of truth.
11141
+ */
11142
+
11143
+ type DeviceRole = "client" | "host";
11144
+ type DeviceKind = "browser" | "cli" | "daemon";
11145
+ interface Device {
11146
+ fingerprint: string;
11147
+ name: string;
11148
+ role: DeviceRole;
11149
+ kind: DeviceKind;
11150
+ rendezvous: string;
11151
+ createdAt: string;
11152
+ lastSeen: string;
11153
+ /** A channel (offer or reconnect) is served for this device right now
11154
+ * (client) or a `forwardHttp` call is in flight for it right now (host).
11155
+ * For a host this is NOT a live heartbeat — see host-registry.ts's
11156
+ * "Online tracking" — it only reflects actual recent/current traffic. */
11157
+ online: boolean;
11158
+ /** A pair/v1 pairing: listed and revocable, but can't connect until
11159
+ * re-paired — see `PairingRecord.legacy`. */
11160
+ legacy?: true;
11161
+ /** Set when this device's pairing/host record was granted under a
11162
+ * HOST-scoped offer (`agentproto pair offer --host`) — see
11163
+ * offer-url.ts's "Offer scope". Surfaced from either side of a pairing so
11164
+ * a user can see, from EITHER daemon, which of their pairings/hosts grant
11165
+ * host control. */
11166
+ scope?: "host";
11167
+ }
11168
+ interface DeviceRegistry {
11169
+ /** Every known device. Read-only. */
11170
+ list(): Promise<Device[]>;
11171
+ /** Rename a device (fingerprint or current name) to a new label. Tries the
11172
+ * pairing registry first, then the host registry. Returns false when
11173
+ * nothing matched. */
11174
+ rename(idOrName: string, newName: string): Promise<boolean>;
11175
+ /** Drop a device by fingerprint or name — same effect as `pair revoke` for
11176
+ * a client device, `devices revoke`-of-a-host for a host device. Tries
11177
+ * the pairing registry first, then the host registry. Returns false when
11178
+ * nothing matched. */
11179
+ revoke(idOrName: string): Promise<boolean>;
11180
+ /** Register a host from an offer URL (delegates to `HostRegistry.add`).
11181
+ * Rejects if no `HostRegistry` was wired into this device registry. */
11182
+ add(offerUrl: string, name?: string): Promise<{
11183
+ fingerprint: string;
11184
+ name: string;
11185
+ rendezvousUrl: string;
11186
+ }>;
11187
+ }
11188
+ declare function createDeviceRegistry(pairing: PairingRegistry, hosts?: HostRegistry): DeviceRegistry;
11189
+
11190
+ /**
11191
+ * MCP tools over the device registry (DEVICES-PLAN PR-A/PR-C) — `device_list`
11192
+ * / `device_rename` / `device_revoke` / `device_add`. A view on top of the
11193
+ * same registries `pair_offer`/`pair_list`/`pair_revoke` (pairing-tools.ts)
11194
+ * and host-registry.ts drive; those keep working unchanged. `device_revoke`
11195
+ * has the exact effect of `pair_revoke` for a client device.
11196
+ *
11197
+ * `device_add` (PR-C) registers a HOST from a `--host`-scoped offer URL —
11198
+ * see `HostRegistry.add`'s scope gate (host-registry.ts). It requires the
11199
+ * gateway to have wired a `HostRegistry` into the `DeviceRegistry`; without
11200
+ * one the call rejects with a clear message rather than a missing-tool error
11201
+ * (this repo always wires one when `pairingRegistry` is wired, so that's
11202
+ * only reachable via a deliberate host misconfiguration).
11203
+ *
11204
+ * Registered beside `pair_*` — same closure-rebind pattern; the registry
11205
+ * singleton lives on the gateway.
11206
+ */
11207
+
11208
+ interface RegisterDeviceToolsOptions {
11209
+ registry: DeviceRegistry;
11210
+ }
11211
+ declare function registerDeviceTools(server: McpServer, opts: RegisterDeviceToolsOptions): void;
11212
+
10423
11213
  /**
10424
11214
  * Rate-limited failure logging for reconnect loops.
10425
11215
  *
@@ -11257,6 +12047,15 @@ interface SessionWaitResult {
11257
12047
  * reported one. `"error"` means the adapter reported a failed turn.
11258
12048
  * Same three-branch coverage as `empty`. */
11259
12049
  reason?: string;
12050
+ /** The matched turn-end's captured error text — `SessionTurnEndEvent.error`
12051
+ * on the ring-replay/bus branches, `SessionDescriptor.lastTurnErrorMessage`
12052
+ * on the sync fast-path (no event object to read there). Present only
12053
+ * when `reason` is `"error"` AND the adapter emitted an in-band `error`
12054
+ * stream event during the turn. Same three-branch coverage as `empty`/
12055
+ * `reason` — this is the field a waiter (a supervisor session, `sessions
12056
+ * wait`, a webhook) needed to actually learn WHY a turn errored instead
12057
+ * of just that it did. */
12058
+ error?: string;
11260
12059
  }
11261
12060
  /**
11262
12061
  * Block until one of the listed sessions fires a matching lifecycle event
@@ -11406,6 +12205,12 @@ interface CreateGatewayOptions {
11406
12205
  * to an `enabled: false` summary. Also surfaced in `daemon_health` /
11407
12206
  * `GET /health` so an operator can see the effective value. */
11408
12207
  resumeSessionsOnBoot?: boolean;
12208
+ /** Opt-in continue-on-boot. Mirrors the `daemon.continueInterruptedOnBoot`
12209
+ * config knob; when true, the `continueInterruptedOnBoot()` handle method
12210
+ * runs the boot continue pass (see `continue-interrupted.ts`); when
12211
+ * false/omitted it short-circuits to an `enabled: false` summary. Surfaced
12212
+ * in `daemon_health` / `GET /health`. */
12213
+ continueInterruptedOnBoot?: boolean;
11409
12214
  /** Idle agent-session reaper threshold in ms (PR-6). Mirrors the
11410
12215
  * `daemon.idleReapAfterMs` config knob; the CLI resolves it (env >
11411
12216
  * config > off) and passes it here. When positive, the gateway starts a
@@ -11659,6 +12464,17 @@ interface CreateGatewayOptions {
11659
12464
  * returns) so the injected `serve` can capture the finished gateway.
11660
12465
  */
11661
12466
  pairingRegistry?: PairingRegistry;
12467
+ /**
12468
+ * Optional HOST registry (see `createHostRegistry`, DEVICES-PLAN PR-C) —
12469
+ * the reverse-pairing "client" half of pair/v2, living daemon-side. When
12470
+ * wired alongside `pairingRegistry`, the gateway layers registered hosts
12471
+ * into `device_list`/`GET /devices`, and enables the `device_add` MCP tool
12472
+ * plus `POST /devices/add` + `POST /devices/:id/exec`. Unlike
12473
+ * `pairingRegistry`, this registry keeps no standing connections — every
12474
+ * `add`/`forwardHttp` dials fresh — so there is nothing for the gateway to
12475
+ * autoconnect or tear down on shutdown.
12476
+ */
12477
+ hostRegistry?: HostRegistry;
11662
12478
  /** Enable the local LLM Endpoint proxy sidecar (route registration,
11663
12479
  * MCP tools, child-process lifecycle). Default false — the endpoint is
11664
12480
  * an opt-in feature; when off, the `llm-endpoint` custom route is not
@@ -11700,6 +12516,10 @@ interface GatewayHandle {
11700
12516
  * `CreateGatewayOptions.pairingRegistry`. Undefined otherwise. Exposed so
11701
12517
  * the CLI can `startAutoconnect()` after boot and `shutdown()` it. */
11702
12518
  pairing?: PairingRegistry;
12519
+ /** HOST registry, when one was wired via `CreateGatewayOptions.hostRegistry`
12520
+ * (DEVICES-PLAN PR-C). Undefined otherwise. No autoconnect/shutdown to
12521
+ * call on it — see that option's doc comment. */
12522
+ hosts?: HostRegistry;
11703
12523
  /** Per-boot bearer token required on mutating /sessions/* routes
11704
12524
  * + WS PTY upgrades. Exposed so an embedding host (e.g. the CLI
11705
12525
  * shell that hosts the gateway in-process) can pass it to child
@@ -11724,6 +12544,16 @@ interface GatewayHandle {
11724
12544
  resumeSessionsOnBoot(opts?: {
11725
12545
  isServed?: (desc: SessionDescriptor) => boolean;
11726
12546
  }): Promise<EagerResumeSummary>;
12547
+ /** Run the opt-in continue-on-boot pass and return its tally — a no-op
12548
+ * returning `{ enabled: false, ... }` when the `continueInterruptedOnBoot`
12549
+ * knob is off. serve.ts invokes this right AFTER `resumeSessionsOnBoot()`
12550
+ * (so a row whose eager resume failed is recognisable and skipped) — and
12551
+ * therefore, like it, after the supervisor was re-armed, so a re-armed
12552
+ * completion policy sees the continue turn's turn-end. Same `isServed`
12553
+ * cross-process gate. */
12554
+ continueInterruptedOnBoot(opts?: {
12555
+ isServed?: (desc: SessionDescriptor) => boolean;
12556
+ }): Promise<ContinueOnBootSummary>;
11727
12557
  stop(): Promise<void>;
11728
12558
  }
11729
12559
  /**
@@ -11740,4 +12570,4 @@ interface GatewayHandle {
11740
12570
  */
11741
12571
  declare function createGateway(opts: CreateGatewayOptions): Promise<GatewayHandle>;
11742
12572
 
11743
- export { type ActivityKind, type ActivityListFilter, type ActivityPolicyLister, type ActivityProjector, type ActivityProjectorRegistry, type ActivityProjectorSession, type ActivityRecord, type ActivitySource, type ActivityState, type ActivityWaitingOn, type ActivityWorkflowLister, AdapterAuthDescriptor, type AdapterCapabilitiesLister, type AdapterInstallResult, type AdapterListEntry, type AgentAdapterInstaller, type AgentAdapterLister, type AgentAdapterResolver, type AgentSessionLike, type AgentStreamEvent, type AnthropicReaderConfig, AnthropicRemainingQuotaReader, type AttachPolicyInput, type AttachSandboxOpts, type AttachSandboxResult, AuthMethod, BUCKETS_ROOT, type BranchGcClass, type BranchGcCoverageView, type BranchGcHoldReason, type BranchGcKind, type BranchGcOutcomeView, type BranchGcPlanEntryView, type BranchGcPlanView, type BranchGcReclaimReason, type BranchGcResult, type BranchGcRunInput, type BranchGcRunner, type BranchGcStatus, type BranchGcSummaryView, type BranchGcTriageVerdict, type BranchGcVerdictInput, type BranchGcVerdictLookupInput, type BranchGcVerdictReader, type BranchGcVerdictRecordView, type BranchGcVerdictRecorder, type BrowserAdapterHandle, type BrowserAdapterInfo, type BrowserAdapterLister, type BrowserAdapterResolver, BuildHeartbeatAgent, CanonicalPosture, type CatalogModelsLister, CatalogModelsQuery, CatalogModelsResponse, type CatalogProviderModel, type CatalogProviderModelsQuery, type CatalogProviderModelsResponse, type CatalogProviderPricing, type ClaudeNativeRef, type CommitSpec, type CompletionPolicySupervisor, ContextProfile, type ConversationIndexRecord, type ConversationNativeRef, type CostBudgetDecision, type CrashDetectSummary, type CrashReaperRegistry, type CreateGatewayOptions, type CreateOfferInput, type CreateReviewRunnerOptions, type CreatedOffer, DEAD_SESSION_STATUSES, DEFAULT_ALWAYS_ON_TOOLS, DEFAULT_BUCKET, DEFAULT_ORCHESTRATOR_TOOLS, DEFAULT_WORKTREE_ISOLATION, type DaemonReviewerHostDeps, DeclaredAdapterOption, type DeclaredAdapterPreset, DefaultsAdapterAuthConfig, type EagerResumeFailReason, type EagerResumeOutcome, type EagerResumeSkipReason, type EagerResumeSummary, EffortLevel, type EnableInput, type EnableResult, GC_REAPABLE_STATES, type GateSpec, type GatewayHandle, type GhRunner, type HarnessPreset, type HarnessPresetValidationDeps, HarnessPresetValidationError, type HarnessPresetsFile, type HermesNativeRef, INBOUND_PROVIDERS, type IdleReapSummary, type IdleReaperRegistry, type InboundEndpoint, type InboundEndpointStore, type InboundEndpointStoreOptions, type InboundEnqueuePrompt, type InboundIsSessionAlive, type InboundMessage, type InboundProvider, type InboundRestartSession, type InboundRouteMode, type InboundRouterDeps, type InboundRouterLog, type InboundSpawnForContact, type InboundWatcher, type JudgeGateSpec, LEGACY_SESSIONS_FILE, type LedgerAnnotations, type LedgerEntry, type LedgerHostMeta, type LocatedConversation, type LocatedConversationByPath, MAX_MESSAGE_DATA_BYTES, MCP_APP_BRIDGE_SCRIPT, MESSAGE_KINDS, MESSAGE_PREAMBLE, MESSAGE_TAG, MESSAGE_URGENCIES, type McpCredentialDeps, type MessageDeliveryVia, type MessageFilter, type MessageKind, type MessageRelation, type MessageTreeNode, type MessageUrgency, type MigrationMarker, type NormalizeInboundResult, type OnFailSpec, type OpenPrResolver, type OrchestratorGatewayDeps, type OrchestratorInjection, type OrchestratorInjector, type OrchestratorInjectorDeps, type OrchestratorMcpServerFactory, type OrchestratorScope, type OrphanReaperRegistry, PAIRINGS_VERSION, POSTURE_NATIVE_ALIASES, POSTURE_PREAMBLES, type PairingChannelContext, type PairingChannelHandle, type PairingChannelMode, type PairingRecord, type PairingRegistry, type PairingRegistryDeps, type PendingPermission, type PermissionRespondInput, type PermissionRespondResult, type PolicyRunState, type PolicyRunStatus, Posture, type PostureResolution, PrLookupError, type PrLookupErrorCode, type PrResolvedState, type PrStateResolver, type PrStatusSnapshot, type PresetInfo, type PricingResolver, type QuotaReadableProfile, type QuotaStoreFile, type QuotaStoreOptions, type ReconnectLogGate, type RegisterBrainToolsOptions, type RegisterBrowserInput, type RegisterPairingToolsOptions, type RegisterReviewToolsOptions, type RegisterSessionInput, type RegisterWebSearchToolsOptions, type RemainingQuota, type RemainingQuotaReader, type RemoteStatus, type ResolveNativeLinkInput, ResolvedAuthSpec, ResumeDisabledError, type ReuseResolution, type ReviewLedger, type ReviewLedgerFilter, type ReviewRun, type ReviewRunInput, type ReviewRunStatus, type ReviewRunner, type ReviewerRunResult, type ReviewerSessionHost, type RouteAwareLaunchConfig, type RouteAwareLaunchConfigInput, RouteSpec, type RuntimeMeta, SESSION_END_REASONS, SESSION_ID_ENV, type SandboxAdapterInfo, type SandboxConnectionDescriptor, type SandboxGcCandidate, type SandboxGcProviderHandle, type SandboxGcReapDeps, type SandboxGcReapResult, type SandboxLedgerEntry, type SandboxLedgerState, type SandboxProviderCapabilities, type SandboxProviderHandle, type SandboxProviderLister, type SandboxProviderResolver, type SandboxReconcileDeps, type SandboxReconcileProviderHandle, type SandboxReconcileResult, type SandboxReconcileRow, type SandboxReconcileVerdict, type ScopeTokenRegistry, SessionConfig, type SessionCurrentPhase, type SessionDescriptor, type SessionEndReason, type SessionKind, type SessionMessage, type SessionMessageFrom, type SessionMessageSentRecord, type SessionObserver, type SessionOutcome, type SessionOutcomeArtifact, type SessionOutcomeCompact, type SessionOutcomeLink, type SessionSnapshots, type SessionStatus, type SessionUsage, type SessionWaitEvent, type SessionWaitResult, type SessionsRegistry, type ShellGateSpec, type SpawnAgentInput, SpawnDefaultsConfig, type SpawnSessionInput, type StallWatchdogRegistry, type StallWatchdogSummary, type StoredProfileQuota, TASK_STATUSES, type TaskCaller, type TaskChangedEvent, type TaskClaimInput, type TaskCreateInput, type TaskGateOutcome, type TaskGateRunner, type TaskLedger, type TaskLedgerRegistry, type TaskLedgerSessionSlice, type TaskListFilter, type TaskRecord, type TaskStatus, type TaskUpdateInput, type TaskVerification, type TaskVerifySupervisor, type TaskWriteResult, type TokenPricing, type TransmitterBinding, type TransmitterBindingStore, type TunnelDescriptor, type TunnelProvider, type TunnelStatus, type UsageBucket, type UsageComputeInput, type UsageRollup, type UsageSnapshotRecord, type UsageSource, UserPreset, WORKSPACE_SLUG_ENV, WORKTREE_ISOLATION_ENV, type WatcherDescriptor, type WatcherStartInput, type WebSearchInput, type WebSearchOutput, type WebSearchResult, type WorkspaceBrainSubscriber, type WorkspaceBrainSubscriberOptions, type WorkspaceBrains, WorkspaceFs, type WorktreeAutoReclaimer, type WorktreeDecision, type WorktreeField, type WorktreeGcClass, type WorktreeGcOutcomeView, type WorktreeGcPlanEntryView, type WorktreeGcReclaimReason, type WorktreeGcResult, type WorktreeGcRunInput, type WorktreeGcRunner, WorktreeIsolationMode, type WorktreeProvisionOutcome, type WorktreeProvisionRequest, type WorktreeProvisioner, type WorktreeRequest, type WorktreeStatusLister, type WorktreeStatusView, activityCounts, addHarnessPreset, appendConversationRecord, attachSandbox, bucketDir, bucketSessionsFile, bucketTranscriptDir, buildCatalogProviderModels, buildMcpConfigSnippet, buildRouteAwareLaunchConfig, canonicalForModeId, claudeProjectSlug, collectGcCandidates, compactOutcome, composeSessionObservers, conversationIndexPath, createActivityProjector, createDaemonReviewerHost, createFileStepCache, createGateway, createInboundEndpointStore, createOrchestratorInjector, createOrchestratorMcpServerFactory, createPairingRegistry, createPrProvenanceReconciler, createReconnectLogGate, createReviewLaneExecutor, createReviewLedger, createReviewRunner, createScopeTokenRegistry, createSessionMessage, createSupervisorTaskGateRunner, createTaskLedger, createWorkspaceBrainSubscriber, createWorkspaceBrains, daemonRegistryDir, decideWorktreeIsolation, declaredPresetToProviderPreset, defaultReviewLedgerRoot, defineBraveSearchHttpDriver, defineSerperHttpDriver, deriveSessionOutcome, deriveSessionUsage, enrichRollupWithProviderQuota, enrichWithRemainingQuota, escapeHumanPrompt, escapeMessageBody, evaluateCostBudget, execGh, fetchPrStatus, filterActivities, findConversationRecord, findNativeMode, findPrForCommit, formatToolCall, formatToolResult, getDefaultHarnessPreset, getHarnessPreset, getMcpCredentialDeps, githubRepoOf, harnessPresetsPath, hostPlaceholders, injectMcpAppBridge, isAgentCliAuthConfigured, isClosedTaskStatus, isKnownSessionEndReason, isMessageAllowed, isProviderLimitError, isSafeBucketSlug, isTerminalActivityState, listBuckets, listHarnessPresets, listPresets, loadHarnessPresets, loadQuotaStore, loadWorktreeIsolation, locateConversationByNativePath, locateConversationBySessionId, makeBrowserAdapterLister, makeSandboxCredsStore, makeSandboxResolver, matchesMessageFilter, messageFrom, migrateLegacySessionsFile, migrationMarkerPath, mintMessageId, monitorPolicyWait, monitorSessionWait, narrowOrchestratorTools, normalizeInbound, normalizeModeId, normalizeRemote, normalizeWorktreeField, parseAnthropicRateLimitHeaders, parsePrUrl, parseTaskStatus, parseWindow, parseWorktreeIsolationMode, policyToActivities, policyWatchesSession, prHeadSha, prToActivities, projectSessionUsage, readConversationIndex, readDaemonRegistry, readProfileQuota, readRegisteredSlugs, readRuntimeMeta, readSandboxLedger, readSessionForBrain, readUsageSnapshots, reapGcEntry, reapOrphanedDescendants, reconcileSandboxLedger, recordProfileQuota, recordSandboxBoot, recordSandboxLiveness, recordSandboxOrigin, recordSandboxState, registerBrainTools, registerBuiltinRoutes, registerPairingTools, registerReviewTools, registerSandboxAttachTool, registerWebSearchTools, removeHarnessPreset, removeSandboxLedgerEntry, renderInboxDigest, renderSessionMessage, renderSessionMessages, repoSlug, resolveBucketSlug, resolveNativeLink, resolvePosture, resolveRelation, resolveReuseFromLedger, resolveReviewerPreset, rollupUsage, runCrashDetectPass, runEagerResumePass, runIdleReapPass, runShellLane, runStallWatchdogPass, sandboxLedgerPath, sanitizeLabel, setDefaultPreset, setMcpCredentialDeps, sweepStaleDaemonRegistry, sweepStaleRuntimeMetas, toWorktreeStatusView, turnToActivities, unlinkDaemonRegistryEntry, unlinkRuntimeMeta, updateHarnessPreset, upsertSandboxLedger, verifyInboundSignature, webSearchTool, withPr, withPrStatus, workflowToActivities, writeDaemonRegistryEntry };
12573
+ export { type ActivityKind, type ActivityListFilter, type ActivityPolicyLister, type ActivityProjector, type ActivityProjectorRegistry, type ActivityProjectorSession, type ActivityRecord, type ActivitySource, type ActivityState, type ActivityWaitingOn, type ActivityWorkflowLister, AdapterAuthDescriptor, type AdapterCapabilitiesLister, type AdapterInstallResult, type AdapterListEntry, type AgentAdapterInstaller, type AgentAdapterLister, type AgentAdapterResolver, type AgentSessionLike, type AgentStreamEvent, type AnthropicReaderConfig, AnthropicRemainingQuotaReader, type AttachPolicyInput, type AttachSandboxOpts, type AttachSandboxResult, AuthMethod, BUCKETS_ROOT, type BranchGcClass, type BranchGcCoverageView, type BranchGcHoldReason, type BranchGcKind, type BranchGcOutcomeView, type BranchGcPlanEntryView, type BranchGcPlanView, type BranchGcReclaimReason, type BranchGcResult, type BranchGcRunInput, type BranchGcRunner, type BranchGcStatus, type BranchGcSummaryView, type BranchGcTriageVerdict, type BranchGcVerdictInput, type BranchGcVerdictLookupInput, type BranchGcVerdictReader, type BranchGcVerdictRecordView, type BranchGcVerdictRecorder, type BrowserAdapterHandle, type BrowserAdapterInfo, type BrowserAdapterLister, type BrowserAdapterResolver, BuildHeartbeatAgent, CONTINUE_PROMPT_SOURCE, CanonicalPosture, type CatalogModelsLister, CatalogModelsQuery, CatalogModelsResponse, type CatalogProviderModel, type CatalogProviderModelsQuery, type CatalogProviderModelsResponse, type CatalogProviderPricing, type ClaudeNativeRef, type CommitSpec, type CompletionPolicySupervisor, ContextProfile, type ContinueInterruptedMode, type ContinueInterruptedOutcome, type ContinueInterruptedResult, type ContinueInterruptedSkipReason, type ContinueOnBootSummary, type ConversationIndexRecord, type ConversationNativeRef, type CostBudgetDecision, type CrashDetectSummary, type CrashReaperRegistry, type CreateGatewayOptions, type CreateOfferInput, type CreateReviewRunnerOptions, type CreatedOffer, DEAD_SESSION_STATUSES, DEFAULT_ALWAYS_ON_TOOLS, DEFAULT_BUCKET, DEFAULT_CONTINUE_PROMPT, DEFAULT_ORCHESTRATOR_TOOLS, DEFAULT_WORKTREE_ISOLATION, type DaemonReviewerHostDeps, DeclaredAdapterOption, type DeclaredAdapterPreset, DefaultsAdapterAuthConfig, type Device, type DeviceKind, type DeviceRegistry, type DeviceRole, type EagerResumeFailReason, type EagerResumeOutcome, type EagerResumeSkipReason, type EagerResumeSummary, EffortLevel, type EnableInput, type EnableResult, type ForwardHttpRequest, type ForwardHttpResponse, GC_REAPABLE_STATES, type GateSpec, type GatewayHandle, type GhRunner, HOSTS_VERSION, type HarnessPreset, type HarnessPresetValidationDeps, HarnessPresetValidationError, type HarnessPresetsFile, type HermesNativeRef, type HostRecord, type HostRegistry, type HostRegistryDeps, INBOUND_PROVIDERS, type IdleReapSummary, type IdleReaperRegistry, type InboundEndpoint, type InboundEndpointStore, type InboundEndpointStoreOptions, type InboundEnqueuePrompt, type InboundIsSessionAlive, type InboundMessage, type InboundProvider, type InboundRestartSession, type InboundRouteMode, type InboundRouterDeps, type InboundRouterLog, type InboundSpawnForContact, type InboundWatcher, type JudgeGateSpec, LEGACY_SESSIONS_FILE, type LedgerAnnotations, type LedgerEntry, type LedgerHostMeta, type LlmEndpointDescriptor, type LlmEndpointStatus, type LlmEndpointStatusReport, type LocatedConversation, type LocatedConversationByPath, MAX_AUTO_CONTINUE_ATTEMPTS, MAX_MESSAGE_DATA_BYTES, MCP_APP_BRIDGE_SCRIPT, MESSAGE_KINDS, MESSAGE_PREAMBLE, MESSAGE_TAG, MESSAGE_URGENCIES, type McpCredentialDeps, type MeasurableTool, type MeasuredToolEntry, type MessageDeliveryVia, type MessageFilter, type MessageKind, type MessageRelation, type MessageTreeNode, type MessageUrgency, type MigrationMarker, type NormalizeInboundResult, type OnFailSpec, type OpenPrResolver, type OrchestratorGatewayDeps, type OrchestratorInjection, type OrchestratorInjector, type OrchestratorInjectorDeps, type OrchestratorMcpServerFactory, type OrchestratorScope, type OrphanReaperRegistry, PAIRINGS_VERSION, POSTURE_NATIVE_ALIASES, POSTURE_PREAMBLES, type PairingChannelContext, type PairingChannelHandle, type PairingChannelMode, type PairingRecord, type PairingRegistry, type PairingRegistryDeps, type PendingPermission, type PermissionRespondInput, type PermissionRespondResult, type PolicyRunState, type PolicyRunStatus, Posture, type PostureResolution, PrLookupError, type PrLookupErrorCode, type PrResolvedState, type PrStateResolver, type PrStatusSnapshot, type PresetInfo, type PricingResolver, type QuotaReadableProfile, type QuotaStoreFile, type QuotaStoreOptions, type ReconnectLogGate, type RegisterBrainToolsOptions, type RegisterBrowserInput, type RegisterDeviceToolsOptions, type RegisterPairingToolsOptions, type RegisterReviewToolsOptions, type RegisterSessionInput, type RegisterWebSearchToolsOptions, type RemainingQuota, type RemainingQuotaReader, type RemoteStatus, type ResolveNativeLinkInput, ResolvedAuthSpec, ResumeDisabledError, type ReuseResolution, type ReviewLedger, type ReviewLedgerFilter, type ReviewRun, type ReviewRunInput, type ReviewRunStatus, type ReviewRunner, type ReviewerRunResult, type ReviewerSessionHost, type RouteAwareLaunchConfig, type RouteAwareLaunchConfigInput, RouteSpec, type RuntimeMeta, SESSION_END_REASONS, SESSION_ID_ENV, type SandboxAdapterInfo, type SandboxConnectionDescriptor, type SandboxGcCandidate, type SandboxGcProviderHandle, type SandboxGcReapDeps, type SandboxGcReapResult, type SandboxLedgerEntry, type SandboxLedgerState, type SandboxProviderCapabilities, type SandboxProviderHandle, type SandboxProviderLister, type SandboxProviderResolver, type SandboxReconcileDeps, type SandboxReconcileProviderHandle, type SandboxReconcileResult, type SandboxReconcileRow, type SandboxReconcileVerdict, type ScopeTokenRegistry, SessionConfig, type SessionCurrentPhase, type SessionDescriptor, type SessionEndReason, type SessionKind, type SessionMessage, type SessionMessageFrom, type SessionMessageSentRecord, type SessionObserver, type SessionOutcome, type SessionOutcomeArtifact, type SessionOutcomeCompact, type SessionOutcomeLink, type SessionSnapshots, type SessionStatus, type SessionUsage, type SessionUsageWithSubtree, type SessionWaitEvent, type SessionWaitResult, type SessionsRegistry, type ShellGateSpec, type SpawnAgentInput, SpawnDefaultsConfig, type SpawnSessionInput, type StallWatchdogRegistry, type StallWatchdogSummary, type StoredProfileQuota, type SubtreeChildUsage, type SubtreeUsageTotals, TASK_STATUSES, type TaskCaller, type TaskChangedEvent, type TaskClaimInput, type TaskCreateInput, type TaskGateOutcome, type TaskGateRunner, type TaskLedger, type TaskLedgerRegistry, type TaskLedgerSessionSlice, type TaskListFilter, type TaskRecord, type TaskStatus, type TaskUpdateInput, type TaskVerification, type TaskVerifySupervisor, type TaskWriteResult, type TokenPricing, type ToolListMeasurement, type TransmitterBinding, type TransmitterBindingStore, type TunnelDescriptor, type TunnelProvider, type TunnelStatus, type UsageBucket, type UsageComputeInput, type UsageDetail, type UsageReadContext, type UsageReadResult, type UsageRollup, type UsageSnapshotRecord, type UsageSource, UserPreset, WORKSPACE_SLUG_ENV, WORKTREE_ISOLATION_ENV, type WatcherDescriptor, type WatcherStartInput, type WebSearchInput, type WebSearchOutput, type WebSearchResult, type WorkspaceBrainSubscriber, type WorkspaceBrainSubscriberOptions, type WorkspaceBrains, WorkspaceFs, type WorktreeAutoReclaimer, type WorktreeDecision, type WorktreeField, type WorktreeGcClass, type WorktreeGcOutcomeView, type WorktreeGcPlanEntryView, type WorktreeGcReclaimReason, type WorktreeGcResult, type WorktreeGcRunInput, type WorktreeGcRunner, WorktreeIsolationMode, type WorktreeProvisionOutcome, type WorktreeProvisionRequest, type WorktreeProvisioner, type WorktreeRequest, type WorktreeStatusLister, type WorktreeStatusView, activityCounts, addHarnessPreset, appendConversationRecord, attachSandbox, bucketDir, bucketSessionsFile, bucketTranscriptDir, buildCatalogProviderModels, buildMcpConfigSnippet, buildRouteAwareLaunchConfig, canonicalForModeId, claudeProjectSlug, collectGcCandidates, compactOutcome, composeSessionObservers, continueInterruptedSessions, continueSkipReason, conversationIndexPath, createActivityProjector, createDaemonReviewerHost, createDeviceRegistry, createFileStepCache, createGateway, createHostRegistry, createInboundEndpointStore, createOrchestratorInjector, createOrchestratorMcpServerFactory, createPairingRegistry, createPrProvenanceReconciler, createReconnectLogGate, createReviewLaneExecutor, createReviewLedger, createReviewRunner, createScopeTokenRegistry, createSessionMessage, createSupervisorTaskGateRunner, createTaskLedger, createWorkspaceBrainSubscriber, createWorkspaceBrains, daemonRegistryDir, decideWorktreeIsolation, declaredPresetToProviderPreset, defaultReviewLedgerRoot, defineBraveSearchHttpDriver, defineSerperHttpDriver, deriveSessionOutcome, deriveSessionUsage, enrichRollupWithProviderQuota, enrichWithRemainingQuota, escapeHumanPrompt, escapeMessageBody, evaluateCostBudget, execGh, extractHelpSection, fetchPrStatus, filterActivities, findConversationRecord, findNativeMode, findPrForCommit, formatToolCall, formatToolResult, getDefaultHarnessPreset, getHarnessPreset, getMcpCredentialDeps, getToolHelp, githubRepoOf, harnessPresetsPath, hostPlaceholders, injectMcpAppBridge, isAgentCliAuthConfigured, isClosedTaskStatus, isKnownSessionEndReason, isMessageAllowed, isProviderLimitError, isSafeBucketSlug, isTerminalActivityState, listBuckets, listHarnessPresets, listPresets, listToolHelpTopics, loadHarnessPresets, loadQuotaStore, loadWorktreeIsolation, locateConversationByNativePath, locateConversationBySessionId, makeBrowserAdapterLister, makeSandboxCredsStore, makeSandboxResolver, matchesMessageFilter, measureTool, measureToolList, messageFrom, migrateLegacySessionsFile, migrationMarkerPath, mintMessageId, monitorPolicyWait, monitorSessionWait, narrowOrchestratorTools, normalizeInbound, normalizeModeId, normalizeRemote, normalizeWorktreeField, parseAnthropicRateLimitHeaders, parsePrUrl, parseTaskStatus, parseWindow, parseWorktreeIsolationMode, pickUsageDetail, policyToActivities, policyWatchesSession, prHeadSha, prToActivities, projectSessionUsage, readConversationIndex, readDaemonRegistry, readPairingsSnapshot, readProfileQuota, readRegisteredSlugs, readRuntimeMeta, readSandboxLedger, readSessionForBrain, readUsageSnapshots, reapGcEntry, reapOrphanedDescendants, reconcileSandboxLedger, recordProfileQuota, recordSandboxBoot, recordSandboxLiveness, recordSandboxOrigin, recordSandboxState, registerBrainTools, registerBuiltinRoutes, registerDeviceTools, registerPairingTools, registerReviewTools, registerSandboxAttachTool, registerWebSearchTools, removeHarnessPreset, removeSandboxLedgerEntry, renderInboxDigest, renderSessionMessage, renderSessionMessages, repoSlug, resolveBucketSlug, resolveEffectiveLlmEndpointFlag, resolveNativeLink, resolvePosture, resolveRelation, resolveReuseFromLedger, resolveReviewerPreset, rollupSessionSubtree, rollupUsage, runContinueOnBootPass, runCrashDetectPass, runEagerResumePass, runIdleReapPass, runShellLane, runStallWatchdogPass, sandboxLedgerPath, sanitizeLabel, setDefaultPreset, setMcpCredentialDeps, sweepStaleDaemonRegistry, sweepStaleRuntimeMetas, toWorktreeStatusView, turnToActivities, unlinkDaemonRegistryEntry, unlinkRuntimeMeta, updateHarnessPreset, upsertSandboxLedger, verifyInboundSignature, webSearchTool, withPr, withPrStatus, workflowToActivities, writeDaemonRegistryEntry };