@agentproto/runtime 4.1.0 → 5.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
@@ -13,8 +13,8 @@ export { CostBudget, CostBudgetScope } from '@agentproto/auth';
13
13
  import { S as SpawnBrowserMode, R as ResolveHeadlessBrowser, C as ContextContinuityPolicy, a as ResolvedContextContinuityPolicy } from './context-continuity-Bn99UtVC.js';
14
14
  import { SandboxMode } from '@agentproto/command-sandbox';
15
15
  import { AdapterHandle, SetupField, AdapterResolver, AdapterLister, CredsStore, HarnessCapabilities, AdapterEntry } from '@agentproto/provider-kit';
16
- import { S as SpawnDefaultsConfig, D as DefaultsAdapterAuthConfig, R as ResolvedAuthSpec, a as DeclaredAdapterOption, A as AdapterAuthDescriptor } from './spawn-defaults-CZj39Znq.js';
17
- export { b as AuthEcho, c as AuthResolutionError, C as CLAUDE_CODE_OAUTH_SOURCE, d as CredentialSource, e as DefaultsAdapterConfig, f as DeferredToolsConfig, g as ResolveSubscriptionCredentialInput, h as ResolvedSpawnAuthMaterial, i as ResolvedSpawnDefaults, j as SubscriptionCredentialResolution, k as SubscriptionSourceError, l as credentialFingerprint, n as normalizeSkillsOption, r as resolveAuthSpec, m as resolveDeferredToolsGatewayOption, o as resolveSpawnDefaults, p as resolveSubscriptionCredential } from './spawn-defaults-CZj39Znq.js';
16
+ import { S as SpawnDefaultsConfig, D as DefaultsAdapterAuthConfig, R as ResolvedAuthSpec, a as DeclaredAdapterOption, A as AdapterAuthDescriptor } from './spawn-defaults-wQV9AeiG.js';
17
+ export { b as AuthEcho, c as AuthResolutionError, C as CLAUDE_CODE_OAUTH_SOURCE, d as CredentialSource, e as DefaultsAdapterConfig, f as DeferredToolsConfig, g as ResolveSubscriptionCredentialInput, h as ResolvedSpawnAuthMaterial, i as ResolvedSpawnDefaults, j as SubscriptionCredentialResolution, k as SubscriptionSourceError, l as credentialFingerprint, n as normalizeSkillsOption, r as resolveAuthSpec, m as resolveDeferredToolsGatewayOption, o as resolveSpawnDefaults, p as resolveSubscriptionCredential } from './spawn-defaults-wQV9AeiG.js';
18
18
  import { CatalogModelsQuery, CatalogModelsResponse } from './catalog-models.js';
19
19
  export { CatalogAdapterInput, CatalogAdapterModelInput, CatalogPricing, CatalogProduct, CatalogRoute, CatalogRouteSummary, CatalogVendor, buildCatalogModels } from './catalog-models.js';
20
20
  import { FooterSession } from './pr-provenance.js';
@@ -26,6 +26,7 @@ import * as _agentproto_tool from '@agentproto/tool';
26
26
  import { ToolContext } from '@agentproto/tool';
27
27
  import { DriverHandle } from '@agentproto/driver';
28
28
  import { Attestation, LedgerKey, RubricDigest, ReviewPrRef, LaneResult, ReviewLaneExecutor, LaneOutcome } from '@agentproto/review';
29
+ import { ImportedMcpsConfig } from './mcp-imports.js';
29
30
  import { UserPreset } from './user-presets.js';
30
31
  export { UserPresetsFile, deleteUserPreset, getUserPreset, listUserPresets, loadUserPresets, saveUserPreset, userPresetsPath } from './user-presets.js';
31
32
  import { WorktreeIsolationMode, SpawnAttachMode, SpawnDedupeMode } from './config.js';
@@ -883,7 +884,7 @@ declare function turnToActivities(session: ActivityTurnSession, opts?: {
883
884
  interface ActivityRunStepSlice {
884
885
  index: number;
885
886
  label: string;
886
- status: "pending" | "running" | "done" | "failed" | "skipped";
887
+ status: "pending" | "running" | "done" | "failed" | "skipped" | "cancelled";
887
888
  sessionId?: string;
888
889
  startedAt?: string;
889
890
  endedAt?: string;
@@ -908,8 +909,8 @@ interface ActivityWorkflowRunSlice {
908
909
  * Project one workflow run's steps (stage-barrier semantics): running →
909
910
  * active; a `pending` step waits on the stage barrier — the unfinished
910
911
  * steps of EARLIER stages are the sessions whose turn-ends open it;
911
- * done/failed → terminal; skipped → cancelled; a step still pending inside
912
- * a terminal run settles as cancelled.
912
+ * done/failed → terminal; skipped/cancelled → cancelled; a step still
913
+ * pending inside a terminal run settles as cancelled.
913
914
  */
914
915
  declare function workflowToActivities(run: ActivityWorkflowRunSlice): ActivityRecord[];
915
916
  /**
@@ -1789,7 +1790,7 @@ declare function createTaskLedger(opts: {
1789
1790
  * list — {@link isKnownSessionEndReason} says so, and callers should fall
1790
1791
  * back to a generic label rather than throw.
1791
1792
  */
1792
- declare const SESSION_END_REASONS: readonly ["daemon-restart", "idle-reaped", "crashed", "operator-completed", "operator-stopped", "cost-cap-exceeded", "policy-cleanup", "parent-exited", "provider-limit", "forgotten"];
1793
+ declare const SESSION_END_REASONS: readonly ["daemon-restart", "idle-reaped", "crashed", "operator-completed", "operator-stopped", "cost-cap-exceeded", "policy-cleanup", "parent-exited", "provider-limit", "forgotten", "steward-completed", "steward-abandoned"];
1793
1794
  type SessionEndReason = (typeof SESSION_END_REASONS)[number];
1794
1795
  /** True when `reason` is one of {@link SESSION_END_REASONS}. A label
1795
1796
  * lookup, not a wire-format gate — an unrecognized string is still a
@@ -1816,7 +1817,7 @@ declare function isProviderLimitError(message: string | undefined): boolean;
1816
1817
  * and session_monitor MCP tool (long-poll multiplexed).
1817
1818
  */
1818
1819
 
1819
- type SessionEventType = "session:turn-end" | "session:awaiting-input" | "session:awaiting-input-flagged" | "session:awaiting-question-answered" | "session:permission-request" | "session:permission-resolved" | "session:exited" | "session:reaped" | "session:stalled" | "session:stall-cleared" | "session:watcher-attached" | "session:watcher-detached" | "session:bg-tasks-parked" | "session:bg-tasks-cleared" | "session:bg-task" | "session:resumed" | "session:spawned" | "session:command-done" | "session:model-changed" | "session:config-changed" | "session:renamed" | "session:pinned-changed" | "session:message" | "policy:passed" | "policy:failed" | "policy:commit-ready" | "policy:committed" | "cron:fired" | "cron:succeeded" | "cron:failed" | "activity:changed" | "task:changed" | "workflow:gate-report" | "workflow:suspended" | "workflow:suspend-resumed" | "session:harness-warning";
1820
+ type SessionEventType = "session:turn-end" | "session:awaiting-input" | "session:awaiting-input-flagged" | "session:awaiting-question-answered" | "session:permission-request" | "session:permission-resolved" | "session:exited" | "session:reaped" | "session:stalled" | "session:stall-cleared" | "session:watcher-attached" | "session:watcher-detached" | "session:bg-tasks-parked" | "session:bg-tasks-cleared" | "session:bg-task" | "session:resumed" | "session:spawned" | "session:command-done" | "session:model-changed" | "session:config-changed" | "session:renamed" | "session:pinned-changed" | "session:artifact-added" | "session:artifact-pinned-changed" | "session:message" | "policy:passed" | "policy:failed" | "policy:commit-ready" | "policy:committed" | "cron:fired" | "cron:succeeded" | "cron:failed" | "activity:changed" | "task:changed" | "workflow:gate-report" | "workflow:suspended" | "workflow:suspend-resumed" | "session:harness-warning";
1820
1821
  /**
1821
1822
  * Fixed severity vocabulary for a judge-gate finding (WP-D). Deliberately
1822
1823
  * small and fixed so `policy_status` output is comparable across different
@@ -2364,6 +2365,32 @@ interface SessionPinnedEvent {
2364
2365
  pinned: boolean;
2365
2366
  ts: string;
2366
2367
  }
2368
+ /**
2369
+ * Emitted when a new artifact (or a new version of an existing one) is
2370
+ * materialized into the session's artifact store (`session_artifact_add`,
2371
+ * `POST /sessions/:id/artifacts` — see `session-artifacts.ts`). Carries just
2372
+ * enough for a live UI to append/update its inline card and "Artifacts"
2373
+ * section without a round trip — the full record (all versions) is a
2374
+ * `session_artifact_list` / `GET /sessions/:id/artifacts` call away.
2375
+ */
2376
+ interface SessionArtifactAddedEvent {
2377
+ type: "session:artifact-added";
2378
+ sessionId: string;
2379
+ key: string;
2380
+ kind: string;
2381
+ version: number;
2382
+ label?: string;
2383
+ ts: string;
2384
+ }
2385
+ /** Emitted when an artifact's `pinned` flag changes
2386
+ * (`session_artifact_pin`, `POST /sessions/:id/artifacts/:key/pin`). */
2387
+ interface SessionArtifactPinnedEvent {
2388
+ type: "session:artifact-pinned-changed";
2389
+ sessionId: string;
2390
+ key: string;
2391
+ pinned: boolean;
2392
+ ts: string;
2393
+ }
2367
2394
  /**
2368
2395
  * Emitted when a session is first registered in the registry (WP-R3) — both
2369
2396
  * the agent-cli spawn path (`spawnAgent`) and the terminal path (`spawnPty`).
@@ -2582,7 +2609,7 @@ interface SessionHarnessWarningEvent {
2582
2609
  label?: string;
2583
2610
  ts: string;
2584
2611
  }
2585
- type SessionEvent = SessionTurnEndEvent | SessionAwaitingInputEvent | SessionAwaitingInputFlaggedEvent | SessionAwaitingQuestionAnsweredEvent | SessionPermissionRequestEvent | SessionPermissionResolvedEvent | SessionExitedEvent | SessionReapedEvent | SessionStalledEvent | SessionStallClearedEvent | SessionWatcherAttachedEvent | SessionWatcherDetachedEvent | SessionBgTasksParkedEvent | SessionBgTasksClearedEvent | SessionBgTaskEvent | SessionResumedEvent | SessionSpawnedEvent | SessionCommandDoneEvent | SessionModelChangedEvent | SessionConfigChangedEvent | SessionRenamedEvent | SessionPinnedEvent | SessionMessageEvent | PolicyPassedEvent | PolicyFailedEvent | PolicyCommitReadyEvent | PolicyCommittedEvent | CronFiredEvent | CronSucceededEvent | CronFailedEvent | ActivityChangedEvent | TaskChangedEvent | WorkflowGateReportEvent | WorkflowApprovalRequestedEvent | WorkflowApprovalResolvedEvent | WorkflowSuspendedEvent | WorkflowSuspendResumedEvent | SessionHarnessWarningEvent;
2612
+ type SessionEvent = SessionTurnEndEvent | SessionAwaitingInputEvent | SessionAwaitingInputFlaggedEvent | SessionAwaitingQuestionAnsweredEvent | SessionPermissionRequestEvent | SessionPermissionResolvedEvent | SessionExitedEvent | SessionReapedEvent | SessionStalledEvent | SessionStallClearedEvent | SessionWatcherAttachedEvent | SessionWatcherDetachedEvent | SessionBgTasksParkedEvent | SessionBgTasksClearedEvent | SessionBgTaskEvent | SessionResumedEvent | SessionSpawnedEvent | SessionCommandDoneEvent | SessionModelChangedEvent | SessionConfigChangedEvent | SessionRenamedEvent | SessionPinnedEvent | SessionArtifactAddedEvent | SessionArtifactPinnedEvent | SessionMessageEvent | PolicyPassedEvent | PolicyFailedEvent | PolicyCommitReadyEvent | PolicyCommittedEvent | CronFiredEvent | CronSucceededEvent | CronFailedEvent | ActivityChangedEvent | TaskChangedEvent | WorkflowGateReportEvent | WorkflowApprovalRequestedEvent | WorkflowApprovalResolvedEvent | WorkflowSuspendedEvent | WorkflowSuspendResumedEvent | SessionHarnessWarningEvent;
2586
2613
  interface SessionEventBus {
2587
2614
  emit(ev: SessionEvent): void;
2588
2615
  /** Subscribe to a specific event type. Returns an unsubscribe fn. */
@@ -2828,6 +2855,234 @@ declare class TunnelRegistry {
2828
2855
  private loadFromDisk;
2829
2856
  }
2830
2857
 
2858
+ /**
2859
+ * Typed inter-session message envelope (AIP-46 §Session messages).
2860
+ *
2861
+ * A PROMPT is an instruction from whoever drives a session; a MESSAGE is a
2862
+ * report between sessions in the same tree whose sender the DAEMON attests.
2863
+ * Everything in `from` is computed here from the caller's verified identity
2864
+ * and the session forest — a send request can never set it.
2865
+ *
2866
+ * Pure module: no registry, no I/O. The registry (sessions.ts) carries a
2867
+ * `SessionMessage` on a queued prompt (`QueuedPrompt.envelope`), renders it
2868
+ * into the model's context with `renderSessionMessages`, and records it as
2869
+ * its own `session-message` transcript record — never a `user-prompt`.
2870
+ */
2871
+ /** Sender's position in the tree, relative to the recipient. */
2872
+ type MessageRelation = "child" | "parent" | "sibling" | "human" | "system";
2873
+ type MessageKind = "report" | "question" | "blocker" | "done" | "notice";
2874
+ type MessageUrgency = "fyi" | "next-turn" | "steer" | "interrupt";
2875
+ type MessageDeliveryVia = "wait" | "steer" | "turn" | "interrupt" | "inbox";
2876
+ declare const MESSAGE_KINDS: readonly MessageKind[];
2877
+ declare const MESSAGE_URGENCIES: readonly MessageUrgency[];
2878
+ interface SessionMessageFrom {
2879
+ /** Absent only for relation `human` | `system`. */
2880
+ sessionId?: string;
2881
+ /** Registry label at send time — display only, never identity. */
2882
+ label?: string;
2883
+ role?: string;
2884
+ adapter?: string;
2885
+ relation: MessageRelation;
2886
+ }
2887
+ interface SessionMessage {
2888
+ /** `msg_<uuid8>`, daemon-minted. */
2889
+ id: string;
2890
+ ts: string;
2891
+ /** Recipient sessionId. */
2892
+ to: string;
2893
+ from: SessionMessageFrom;
2894
+ kind: MessageKind;
2895
+ /** Requested urgency. */
2896
+ urgency: MessageUrgency;
2897
+ /** Stamped on delivery. */
2898
+ delivered?: {
2899
+ via: MessageDeliveryVia;
2900
+ at: string;
2901
+ turnSeq?: number;
2902
+ };
2903
+ /** Thread id; defaults to the first message's id. */
2904
+ correlationId?: string;
2905
+ /** A `SessionMessage.id` this answers. */
2906
+ replyTo?: string;
2907
+ text: string;
2908
+ /** Optional structured payload (≤ `MAX_MESSAGE_DATA_BYTES`). */
2909
+ data?: Record<string, unknown>;
2910
+ ackedAt?: string;
2911
+ }
2912
+ declare const MAX_MESSAGE_DATA_BYTES: number;
2913
+ /** The slice of a session descriptor relation/ACL resolution needs. */
2914
+ interface MessageTreeNode {
2915
+ id: string;
2916
+ parentSessionId?: string;
2917
+ label?: string;
2918
+ role?: string;
2919
+ adapterSlug?: string;
2920
+ }
2921
+ /**
2922
+ * Relation of `sender` to `recipient` in the session forest, or `undefined`
2923
+ * when they're not tree neighbours (cousins, strangers, self). `sender`
2924
+ * absent ⇒ `human` (an HTTP/CLI operator); daemon-originated notices pass
2925
+ * `"system"` explicitly instead of calling this.
2926
+ */
2927
+ declare function resolveRelation(sender: MessageTreeNode | undefined, recipient: MessageTreeNode): Exclude<MessageRelation, "system"> | undefined;
2928
+ /**
2929
+ * ACL: child→parent, parent→direct child, human/system→any. Sibling↔sibling
2930
+ * only when `allowSiblings` (config-gated, default off). Anything else — a
2931
+ * cousin, a stranger, yourself — is refused.
2932
+ */
2933
+ declare function isMessageAllowed(relation: MessageRelation | undefined, opts?: {
2934
+ allowSiblings?: boolean;
2935
+ }): relation is MessageRelation;
2936
+ /** Build a `from` block from the verified sender + the resolved relation. */
2937
+ declare function messageFrom(sender: MessageTreeNode | undefined, relation: MessageRelation): SessionMessageFrom;
2938
+ declare function mintMessageId(): string;
2939
+ declare function createSessionMessage(input: {
2940
+ to: string;
2941
+ from: SessionMessageFrom;
2942
+ text: string;
2943
+ kind?: MessageKind;
2944
+ urgency?: MessageUrgency;
2945
+ replyTo?: string;
2946
+ correlationId?: string;
2947
+ data?: Record<string, unknown>;
2948
+ }): SessionMessage;
2949
+ interface MessageFilter {
2950
+ /** Sender session ids, or `"children"` for any message whose relation is
2951
+ * `child` (i.e. from one of the recipient's own children). */
2952
+ from?: readonly string[] | "children";
2953
+ kind?: readonly MessageKind[];
2954
+ correlationId?: string;
2955
+ }
2956
+ declare function matchesMessageFilter(msg: SessionMessage, filter: MessageFilter): boolean;
2957
+ declare const MESSAGE_TAG = "agentproto-message";
2958
+ /** Child-settable label → attribute-safe: `[A-Za-z0-9._-]`, ≤ 48 chars. */
2959
+ declare function sanitizeLabel(label: string | undefined): string | undefined;
2960
+ /** Escape every envelope/body sentinel inside a message body, so a sender
2961
+ * can't close the tag early and forge a second header. */
2962
+ declare function escapeMessageBody(text: string): string;
2963
+ /** A human prompt is NOT wrapped (humans keep today's UX), but a line that
2964
+ * starts with the envelope sentinel is escaped, so the invariant "text
2965
+ * outside an `<agentproto-message>` tag is the human" holds. Nothing else
2966
+ * in human text changes. */
2967
+ declare function escapeHumanPrompt(text: string): string;
2968
+ declare function renderSessionMessage(msg: SessionMessage): string;
2969
+ /** A coalesced batch → sibling tags, one per message, in order. */
2970
+ declare function renderSessionMessages(msgs: readonly SessionMessage[]): string;
2971
+ /** One-line typed digest of `fyi` messages that never woke the session —
2972
+ * prepended (as a `system-prompt` slice) to the next turn it runs anyway. */
2973
+ declare function renderInboxDigest(msgs: readonly SessionMessage[]): string;
2974
+ /** Taught once, the first time a session RECEIVES a message (recorded as a
2975
+ * `system-prompt`). Children also get the same invariant in their lineage
2976
+ * line at spawn. */
2977
+ declare const MESSAGE_PREAMBLE: string;
2978
+
2979
+ /**
2980
+ * Shared types for pluggable sentinel providers (AIP-60 §3, §4).
2981
+ *
2982
+ * The sentinel family is a `@agentproto/provider-kit` consumer, same shape as
2983
+ * the tunnel family (`remote-providers/types.ts`) and the sandbox family
2984
+ * (`sandbox-providers/types.ts`): {@link SentinelProviderHandle} extends the
2985
+ * kit's generic `AdapterHandle` (slug/name/version/description/requiresSetup/
2986
+ * check) with the sentinel-specific `capabilities` plus the create/attach/
2987
+ * cancel/status/poll/ack/parseInbound lifecycle a provider implements.
2988
+ *
2989
+ * This file is deliberately self-contained (no import from `sentinel-store.js`)
2990
+ * so the contract never depends on the store's internals — `SentinelSpec` is
2991
+ * the vocabulary a provider is handed, `sentinel-store.ts` imports it from
2992
+ * here rather than the other way around.
2993
+ */
2994
+
2995
+ /** When a sentinel stops watching. */
2996
+ type SentinelUntil = {
2997
+ kind: "subject_terminal";
2998
+ } | {
2999
+ kind: "at";
3000
+ ms: number;
3001
+ } | {
3002
+ kind: "count";
3003
+ n: number;
3004
+ } | {
3005
+ kind: "never";
3006
+ };
3007
+ /** Where a matching event is delivered.
3008
+ *
3009
+ * `routine` is reserved for AIP-41 `schedule.kind: event` binding; `webhook`
3010
+ * is reserved for forwarding a matched event to an external HTTP callback.
3011
+ * Both are FROZEN SHAPES ONLY — `SentinelStore.create` rejects them with
3012
+ * {@link SentinelTargetNotImplementedError} until a later step wires actual
3013
+ * delivery (design §6/§12 step 4 for `routine`; `webhook` has no design
3014
+ * section yet). Declaring the shape now lets callers/tools reference the
3015
+ * full union before delivery exists. */
3016
+ type SentinelTarget = {
3017
+ kind: "session";
3018
+ sessionId: string;
3019
+ urgency: MessageUrgency;
3020
+ } | {
3021
+ kind: "routine";
3022
+ routineId: string;
3023
+ } | {
3024
+ kind: "webhook";
3025
+ url: string;
3026
+ secret: string;
3027
+ };
3028
+ /** One clause of a sentinel's watch: a subject (exact, or a `*`-suffixed
3029
+ * prefix match against an event's `subjects`) plus an optional per-clause
3030
+ * type glob list. */
3031
+ interface SentinelMatchClause {
3032
+ /** Hierarchical routing key, e.g. "github:agentproto/ts#1428". A trailing
3033
+ * `*` is a prefix match against an event's `subjects`. */
3034
+ subject: string;
3035
+ /** Type globs; undefined = the provider's `defaultTypes(subject)`. */
3036
+ types?: string[];
3037
+ }
3038
+ /**
3039
+ * "Watch any of `match` until `condition`, deliver to `target`."
3040
+ *
3041
+ * `match` is OR semantics: an event matches the sentinel if it matches ANY
3042
+ * clause (subject + that clause's own type glob / provider default). At
3043
+ * least one clause is required — use {@link singleMatch} for a one-clause
3044
+ * spec. The event's own `subject` (not the matching clause's, which may be a
3045
+ * `*` prefix template) is what lands as `correlationId: sentinel:<subject>`
3046
+ * and the `[<scheme>]` inbox-text prefix — see `sentinel-runtime.ts`.
3047
+ *
3048
+ * Dedup key is `(sentinelId, event.id)`, same for every clause — see
3049
+ * {@link SentinelEvent.id}.
3050
+ */
3051
+ interface SentinelSpec {
3052
+ match: SentinelMatchClause[];
3053
+ until: SentinelUntil;
3054
+ target: SentinelTarget;
3055
+ /** Provider slug; undefined = auto-select (not implemented by the runtime
3056
+ * yet — step 2 requires an explicit slug). */
3057
+ provider?: string;
3058
+ /** Fan-out grouping — sentinels created together (e.g. several
3059
+ * PR-related watches for one review) share a `group` id so they can be
3060
+ * listed/removed together later. Purely descriptive: the runtime never
3061
+ * reads it, no MCP tool consumes it yet. */
3062
+ group?: string;
3063
+ /** Human-readable label surfaced by `sentinel_list` / the CLI. Purely
3064
+ * descriptive, same as `group`. */
3065
+ label?: string;
3066
+ }
3067
+
3068
+ /**
3069
+ * SentinelStore — persists `~/.agentproto/sentinels.json` (AIP-60 §2).
3070
+ *
3071
+ * State is in-memory; sentinels persist to disk (debounced async write +
3072
+ * sync flush at shutdown), mirroring `inbound-endpoints.ts`'s
3073
+ * `InboundEndpointStore`. Unlike that store's `markSeen` (deliberately
3074
+ * in-memory only), dedup here is PERSISTED per sentinel, bounded to the last
3075
+ * 1000 event ids (design §2/§9) — a sentinel's dedup window must survive a
3076
+ * daemon restart, since the whole point of a sentinel is to keep watching
3077
+ * across restarts.
3078
+ *
3079
+ * Writes are atomic (write to a per-process tmp file, then rename), same
3080
+ * idiom as `sandbox-ledger.ts` — a crash mid-write must never leave a
3081
+ * corrupt `sentinels.json` behind.
3082
+ */
3083
+
3084
+ type SentinelStatus = "active" | "paused" | "expired" | "orphaned" | "error";
3085
+
2831
3086
  /**
2832
3087
  * LlmEndpointRegistry — supervised-sidecar manager for the
2833
3088
  * `@agentproto/llm-endpoint` proxy gateway.
@@ -3138,6 +3393,13 @@ interface PairingChannelContext {
3138
3393
  fingerprint: string;
3139
3394
  /** Client label. */
3140
3395
  name: string;
3396
+ /** This pairing's OWN recorded scope (mirrors `PairingRecord.scope`) —
3397
+ * `"host"` when the offer that produced it was minted with
3398
+ * `agentproto pair offer --host`, absent for an ordinary remote-control
3399
+ * pairing. Present on BOTH the offer and every reconnect context, so
3400
+ * `serve` can gate scope-restricted routes (e.g. device-inference
3401
+ * proxying) the same way on first contact and on every later reconnect. */
3402
+ scope?: "host";
3141
3403
  }
3142
3404
  /** Handle to a live served channel — the registry closes it on teardown. */
3143
3405
  interface PairingChannelHandle {
@@ -3322,6 +3584,20 @@ interface HostRecord {
3322
3584
  * by `parseOfferUrl` before `add()` ever sees them, so this is currently
3323
3585
  * unreachable — kept so a future downgrade path has somewhere to flag it. */
3324
3586
  legacy?: true;
3587
+ /** Self-reported by the host at add-time (SANDBOX-VISIBILITY-JOIN) — e.g. a
3588
+ * sandbox provider slug, a provider-side sandbox id, and free-form labels
3589
+ * (PR number, run URL, …). Never verified beyond "the host said so"; purely
3590
+ * descriptive for `device_list`/`devices sessions`. */
3591
+ provider?: string;
3592
+ sandboxId?: string;
3593
+ labels?: Record<string, string>;
3594
+ }
3595
+ /** Optional self-reported metadata `add()` attaches to the resulting
3596
+ * `HostRecord` (see `HostRecord.provider`/`sandboxId`/`labels`). */
3597
+ interface HostJoinMeta {
3598
+ provider?: string;
3599
+ sandboxId?: string;
3600
+ labels?: Record<string, string>;
3325
3601
  }
3326
3602
  interface HostRegistryDeps {
3327
3603
  /** Dial a rendezvous WS and adapt it to a `FrameSink`. Same shape as
@@ -3350,6 +3626,15 @@ interface ForwardHttpResponse {
3350
3626
  headers: Record<string, string>;
3351
3627
  body: Uint8Array;
3352
3628
  }
3629
+ /** Streaming counterpart of {@link ForwardHttpResponse} — `body` is a Web
3630
+ * `ReadableStream` the caller drains directly (SSE chat/completions, most
3631
+ * notably) instead of a fully-buffered `Uint8Array`. See
3632
+ * {@link HostRegistry.forwardHttpStream}. */
3633
+ interface ForwardHttpStreamResponse {
3634
+ status: number;
3635
+ headers: Record<string, string>;
3636
+ body: ReadableStream<Uint8Array>;
3637
+ }
3353
3638
  interface HostRegistry {
3354
3639
  /**
3355
3640
  * Accept an offer URL and register the target daemon as a driveable host.
@@ -3360,7 +3645,7 @@ interface HostRegistry {
3360
3645
  * mirrors `pair-transport.ts`'s `acceptOffer`), derives the pair root, and
3361
3646
  * persists a `HostRecord` (upserted by fingerprint — re-adding replaces).
3362
3647
  */
3363
- add(offerUrl: string, name?: string): Promise<{
3648
+ add(offerUrl: string, name?: string, meta?: HostJoinMeta): Promise<{
3364
3649
  fingerprint: string;
3365
3650
  name: string;
3366
3651
  rendezvousUrl: string;
@@ -3383,9 +3668,174 @@ interface HostRegistry {
3383
3668
  * on the hello (likely a pair/v1 peer).
3384
3669
  */
3385
3670
  forwardHttp(idOrName: string, req: ForwardHttpRequest): Promise<ForwardHttpResponse>;
3671
+ /**
3672
+ * Same dial/handshake/retry shape as {@link forwardHttp}, but for a
3673
+ * streamed response (SSE, NDJSON, long-poll) — the daemon-inference proxy's
3674
+ * `/v1/chat/completions` most notably, which must relay tokens as they
3675
+ * arrive rather than buffering the whole completion first. Unlike
3676
+ * `forwardHttp`, the tunnel client is kept open (and `isOnline` stays true)
3677
+ * until the returned `body` stream is fully drained, errors, or is
3678
+ * cancelled — only then does it close and the online count drop.
3679
+ */
3680
+ forwardHttpStream(idOrName: string, req: ForwardHttpRequest): Promise<ForwardHttpStreamResponse>;
3386
3681
  }
3387
3682
  declare function createHostRegistry(deps: HostRegistryDeps): HostRegistry;
3388
3683
 
3684
+ /**
3685
+ * Daemon-side JOIN TOKEN registry (SANDBOX-VISIBILITY-JOIN, stacked on
3686
+ * DEVICES-PLAN PR-C's `host-registry.ts`).
3687
+ *
3688
+ * `host-registry.ts`'s `add()` requires a HUMAN to relay a host-scoped offer
3689
+ * URL from the box to this daemon (`agentproto pair offer --host` on the box,
3690
+ * `agentproto devices add <url>` here) — fine for a laptop, impossible for an
3691
+ * unattended CI sandbox with no one watching its logs to copy a one-time,
3692
+ * 10-minute-TTL secret. A join token flips who initiates: this daemon mints a
3693
+ * long-lived, revocable, reusable credential ahead of time (stored as a
3694
+ * GitHub secret, say); a box started with `AGENTPROTO_JOIN=<token>` dials
3695
+ * in — over the SAME rendezvous/pair-v2 primitives, no protocol change — and
3696
+ * this daemon adds it as a host automatically.
3697
+ *
3698
+ * ## Why this isn't just a long-lived `host-registry.ts` offer
3699
+ *
3700
+ * pair/v2 deliberately gives a CLIENT no persistent identity key (see
3701
+ * `pairing-registry.ts`'s "Reconnect authentication" doc) — only the
3702
+ * DAEMON/offerer side has stable keys a peer can pin and re-dial later.
3703
+ * `HostRegistry.forwardHttp` needs exactly those stable keys to reach a host
3704
+ * on demand, so the box MUST end up as the offerer of its own host-scoped
3705
+ * offer (a normal, single-use `pair offer --host`) with THIS daemon
3706
+ * `add()`-ing it — `host-registry.ts` is not touched by this module at all.
3707
+ *
3708
+ * What the join token replaces is only the relay step: this daemon parks a
3709
+ * STANDING accept loop (`side=daemon`, exactly like a `pair offer`'s park,
3710
+ * except never single-use) on a route deterministically derived from the
3711
+ * token's own secret. A box holding the token dials that route as the
3712
+ * CLIENT — the role a browser/CLI plays consuming a normal offer — and its
3713
+ * sealed hello's `clientName` carries a small JSON envelope: the box's own
3714
+ * self-minted host-scoped offer URL (freshly created, in-process, via its
3715
+ * own `PairingRegistry.createOffer({scope:"host"})` — see
3716
+ * `packages/cli/src/commands/serve.ts`'s `AGENTPROTO_JOIN` boot wiring) plus
3717
+ * a self-reported name/provider/sandboxId/labels. On receipt, this daemon
3718
+ * just calls `HostRegistry.add(offerUrl, name, meta)` — the exact call a
3719
+ * human would type — closes the bootstrap channel, and re-parks for the next
3720
+ * box. No new wire messages, no new crypto: two ordinary pair/v2 handshakes
3721
+ * back to back, the first only ever used to carry one string.
3722
+ *
3723
+ * ## Reuse and revocation
3724
+ *
3725
+ * Unlike an `OfferEntry` (`pairing-registry.ts`), a join token's
3726
+ * `{route, auth}` never rotates and is never "spent" — `useCount`/`maxUses`/
3727
+ * `expiresAt`/`revokedAt` (all checked inside `verifyAuthToken`, which may be
3728
+ * async) gate reuse instead. This is the same trust shape as an existing
3729
+ * epoch reconnect token (valid, unrotated, for its whole day) stretched over
3730
+ * a longer, explicit, revocable window — not a new security primitive.
3731
+ *
3732
+ * ## A known limitation
3733
+ *
3734
+ * One token parks on exactly one fixed route, so exactly one box can be
3735
+ * mid-handshake on it at a time; a second box dialing in the same instant
3736
+ * either waits for the loop to re-park (typical: the ceremony is a single
3737
+ * short-TTL round trip) or, in the worst case, times out and retries with
3738
+ * backoff. Fine for the sequential-CI-review case this exists for; a fleet
3739
+ * of concurrently-joining boxes would want multiple parked slots per token,
3740
+ * deferred (see PR body).
3741
+ *
3742
+ * Persisted to `~/.agentproto/join-tokens.json`, same atomic-write + lazy-
3743
+ * load discipline as `hosts.json`/`pairings.json`. The raw `secret` — not a
3744
+ * hash — is stored, exactly like `pairings.json` stores `pairRoot` in the
3745
+ * clear: it is crypto material this daemon must re-derive `{route, auth}`
3746
+ * from on every boot, not a password to be hashed.
3747
+ */
3748
+
3749
+ /** `join-tokens.json` format. */
3750
+ declare const JOIN_TOKENS_VERSION: 1;
3751
+ /** One persisted join token. `secret` is the crypto material a box's
3752
+ * `AGENTPROTO_JOIN` value carries too — never log or echo it once minted. */
3753
+ interface JoinTokenRecord {
3754
+ /** Short random id (CLI/MCP target), distinct from `secret`. */
3755
+ id: string;
3756
+ /** Human-facing label (e.g. "ci-reviewer"). */
3757
+ name: string;
3758
+ /** One-time-offer-shaped secret (base64url) — re-derives `{route, auth}`
3759
+ * via `deriveOfferTokens`. Never rotates for this token's lifetime. */
3760
+ secret: string;
3761
+ /** Rendezvous endpoint boxes dial through. */
3762
+ rendezvousUrl: string;
3763
+ /** ISO-8601 mint time. */
3764
+ createdAt: string;
3765
+ /** ISO-8601 expiry — always set (`create()` defaults `ttlMs`). */
3766
+ expiresAt: string;
3767
+ /** Reuse ceiling. Absent = unlimited (until `expiresAt`/revoke). */
3768
+ maxUses?: number;
3769
+ /** Successful joins so far. */
3770
+ useCount: number;
3771
+ /** ISO-8601 of the most recent successful join. */
3772
+ lastUsedAt?: string;
3773
+ /** ISO-8601 revoke time. Present ⇒ inactive; kept for `list()` history. */
3774
+ revokedAt?: string;
3775
+ }
3776
+ interface CreateJoinTokenInput {
3777
+ name: string;
3778
+ /** Time-to-live in ms. Default 90 days. */
3779
+ ttlMs?: number;
3780
+ /** Reuse ceiling. Absent = unlimited. */
3781
+ maxUses?: number;
3782
+ /** Rendezvous URL override; falls back to the configured default. */
3783
+ rendezvousUrl?: string;
3784
+ }
3785
+ interface CreatedJoinToken {
3786
+ id: string;
3787
+ name: string;
3788
+ /** The `AGENTPROTO_JOIN` value — an `agentproto://pair?…&scope=host` URL.
3789
+ * Shown ONCE; `list()` never returns it. */
3790
+ token: string;
3791
+ rendezvousUrl: string;
3792
+ expiresAt: string;
3793
+ }
3794
+ interface JoinTokenRegistryDeps {
3795
+ /** Lazily load (create on first use) the daemon identity — same contract
3796
+ * as `PairingRegistryDeps.loadIdentity`. */
3797
+ loadIdentity: () => Promise<DaemonIdentity>;
3798
+ /** Path to `join-tokens.json`. Defaults to `~/.agentproto/join-tokens.json`. */
3799
+ joinTokensPath?: string;
3800
+ /** Configured rendezvous URL default — same three-state contract as
3801
+ * `PairingRegistryDeps.defaultRendezvousUrl` (absent → hosted default;
3802
+ * `""` → explicit opt-out, fails closed without `--rendezvous`). */
3803
+ defaultRendezvousUrl?: string;
3804
+ /** Dial a rendezvous WS and adapt it to a `FrameSink`. Same shape as
3805
+ * `PairingRegistryDeps.dial`/`HostRegistryDeps.dial`. */
3806
+ dial: (wsUrl: string, signal: AbortSignal) => Promise<FrameSink>;
3807
+ /** Called once per successful join, with the box's self-minted host offer
3808
+ * URL and self-reported name/meta — normally `hostRegistry.add`. Errors
3809
+ * are logged and swallowed: a bad/unreachable offer from one join must not
3810
+ * crash the standing accept loop. */
3811
+ addHost: (offerUrl: string, name?: string, meta?: HostJoinMeta) => Promise<unknown>;
3812
+ /** Injectable clock (ms). Defaults to Date.now. */
3813
+ now?: () => number;
3814
+ /** Diagnostic log sink. */
3815
+ log?: (line: string) => void;
3816
+ /** Handshake ceiling per attempt. Default 110s (mirrors PairingRegistry's
3817
+ * park timeout, kept just under the broker's own). */
3818
+ handshakeTimeoutMs?: number;
3819
+ /** Reconnect backoff floor / ceiling (ms) after a failed dial. Defaults 1s/30s. */
3820
+ reconnectMinMs?: number;
3821
+ reconnectMaxMs?: number;
3822
+ }
3823
+ interface JoinTokenRegistry {
3824
+ /** Mint a token and start its standing accept loop. */
3825
+ create(input: CreateJoinTokenInput): Promise<CreatedJoinToken>;
3826
+ /** All persisted tokens (copies), `secret` stripped. Loads on first call. */
3827
+ list(): Promise<Omit<JoinTokenRecord, "secret">[]>;
3828
+ /** Revoke by id or name — stops its accept loop. Returns false if nothing
3829
+ * matched or it was already revoked. */
3830
+ revoke(idOrName: string): Promise<boolean>;
3831
+ /** Start standing accept loops for every active persisted token. Call once
3832
+ * at boot, after the gateway/hostRegistry are wired. */
3833
+ startAutoconnect(): Promise<void>;
3834
+ /** Tear down every accept loop. */
3835
+ shutdown(): Promise<void>;
3836
+ }
3837
+ declare function createJoinTokenRegistry(deps: JoinTokenRegistryDeps): JoinTokenRegistry;
3838
+
3389
3839
  /**
3390
3840
  * Shared types for pluggable sandbox providers.
3391
3841
  *
@@ -4196,6 +4646,29 @@ declare function createActivityProjector(opts: {
4196
4646
  resolvePrState?: PrStateResolver;
4197
4647
  }): ActivityProjector;
4198
4648
 
4649
+ /**
4650
+ * `~/.agentproto/bundles.json` — named sets of imported MCPs + skills (+
4651
+ * optionally the daemon's own `/mcp`), attachable to any harness in one
4652
+ * `agent_start({bundles:["research"]})` call.
4653
+ *
4654
+ * Same load/save/atomic-write pattern as `user-presets.ts`; validation
4655
+ * against the live imported-MCP set is the single boundary every writer
4656
+ * (MCP tools, HTTP routes) goes through (`saveBundle`).
4657
+ */
4658
+
4659
+ declare const bundlesFileSchema: z.ZodObject<{
4660
+ version: z.ZodLiteral<1>;
4661
+ bundles: z.ZodArray<z.ZodObject<{
4662
+ id: z.ZodString;
4663
+ label: z.ZodString;
4664
+ description: z.ZodOptional<z.ZodString>;
4665
+ mcpImports: z.ZodArray<z.ZodString>;
4666
+ includeDaemon: z.ZodOptional<z.ZodBoolean>;
4667
+ skills: z.ZodArray<z.ZodString>;
4668
+ }, z.core.$strip>>;
4669
+ }, z.core.$strip>;
4670
+ type BundlesFile = z.infer<typeof bundlesFileSchema>;
4671
+
4199
4672
  /**
4200
4673
  * Spawn-time role profiles — the primitive that decides whether a
4201
4674
  * spawned agent may itself delegate (spawn further children).
@@ -4436,6 +4909,21 @@ interface WorktreeProvisionRequest {
4436
4909
  /** Free-text label (the session's `label`) the provisioner may fold into a
4437
4910
  * minted slug for human readability. Never load-bearing for correctness. */
4438
4911
  labelHint?: string;
4912
+ /** Absolute path the provisioner should append the setup hooks' FULL
4913
+ * stdout+stderr to (see `worktree.provision`'s `setupLogPath` input and
4914
+ * `@agentproto/worktree`'s `runSetup`). `session-spawn.ts` passes the
4915
+ * spawning session's own transcript dir here, so a failing setup hook's
4916
+ * full output survives even after a subsequently-reclaimed worktree is
4917
+ * gone — the short `HookError` message alone only carries a filtered
4918
+ * tail. Omitted callers (e.g. `agentproto worktree new`, no session to
4919
+ * log into) simply get no persisted log. */
4920
+ setupLogPath?: string;
4921
+ /** Re-run a failing `worktree.setup` command exactly once before giving
4922
+ * up — see `worktree.provision`'s `retrySetupOnFailure` input and
4923
+ * `runSetup`'s doc for why this is a bounded complement, not a
4924
+ * substitute, and why the daemon opts in while `agentproto worktree new`
4925
+ * doesn't. */
4926
+ retrySetupOnFailure?: boolean;
4439
4927
  }
4440
4928
  /** The provisioner's outcome. `isolated: false` is NOT a failure — it means
4441
4929
  * there was nothing to isolate (`cwd` sits in no git repo), and the caller
@@ -4652,6 +5140,15 @@ interface SpawnAgentSessionDeps {
4652
5140
  * `loadConfig` when omitted; tests inject a stub to avoid touching
4653
5141
  * the real file. */
4654
5142
  loadDefaultsConfig?: () => Promise<SpawnDefaultsConfig | undefined>;
5143
+ /** Loads `~/.agentproto/bundles.json` for capability-bundle expansion
5144
+ * (`agent_start.bundles` / config `defaults.bundles`, PLAN D phase 1).
5145
+ * Defaults to the real loader when omitted; tests inject a stub to avoid
5146
+ * touching the real file. */
5147
+ loadBundlesConfig?: () => Promise<BundlesFile>;
5148
+ /** Loads `~/.agentproto/imported-mcps.json` — resolves a bundle's
5149
+ * `mcpImports` ids to their alias/snapshot and detects dangling ones.
5150
+ * Defaults to the real loader when omitted; tests inject a stub. */
5151
+ loadImportedMcpsConfig?: () => Promise<ImportedMcpsConfig>;
4655
5152
  /** Builds the per-session headless-browser mount for `browser:
4656
5153
  * "headless"` (see `browser-mount.ts`). Defaults to the real resolver
4657
5154
  * (installs chrome-devtools-mcp on first use); tests inject a stub. */
@@ -4824,6 +5321,22 @@ interface SpawnAgentSessionInput {
4824
5321
  * such option (e.g. claude-code, which auto-discovers skills) ignore
4825
5322
  * it. See `resolveSpawnDefaults` / `normalizeSkillsOption`. */
4826
5323
  skills?: string[];
5324
+ /** Capability bundle ids (`bundle_list`) to attach (PLAN D phase 1). Merged
5325
+ * with config.json's `defaults.bundles` / `defaults.adapters.<slug>.bundles`
5326
+ * with the same precedence/replace semantics as `skills` — see
5327
+ * `resolveBundleDefaults`. Each bundle expands to its `mcpImports` (mounted
5328
+ * as native MCP servers via the daemon's `/mcp/imported/<id>` passthrough,
5329
+ * never the two-step `mcp_imported_*` indirection), optionally the
5330
+ * daemon's own `/mcp` (`includeDaemon`), and its `skills` (unioned into
5331
+ * the resolved skill list). Ignored (with a warning) for a sandbox spawn —
5332
+ * the box cannot reach this daemon's loopback `/mcp`. */
5333
+ bundles?: string[];
5334
+ /** Explicitly mount the daemon's own scoped `/mcp` for this spawn — the
5335
+ * only way an adapter outside `shouldInjectDaemonSelfMount`'s allowlist
5336
+ * (opencode, codex, gemini, …) gets it. Merged with config's
5337
+ * `defaults.adapters.<slug>.daemonMount` (per-spawn wins). Ignored (with a
5338
+ * warning) for a sandbox spawn. */
5339
+ daemonMount?: boolean;
4827
5340
  model?: string;
4828
5341
  effort?: string;
4829
5342
  /** Decomposed route identity. This is canonical transport; `mode` remains
@@ -4962,6 +5475,12 @@ interface SpawnAgentSessionInput {
4962
5475
  * external webhook path (`notifyUrl`) already fires regardless of this
4963
5476
  * flag. */
4964
5477
  notifyParentOnCrash?: boolean;
5478
+ /** Per-spawn opt-out of sentinel auto-link (AIP-60 §6/step 4): `false`
5479
+ * means a PR this session opens is never auto-watched, regardless of
5480
+ * `config.sentinel.autoWatchPrs`. Recorded verbatim onto
5481
+ * {@link SessionDescriptor.sentinelAutoWatch}; `undefined`/`true` = allowed
5482
+ * (still gated by the config default). */
5483
+ sentinel?: boolean;
4965
5484
  /** Exempt this session from the idle-reaper (`isReapable` in
4966
5485
  * idle-reaper.ts) regardless of how long it sits idle. Stamped straight
4967
5486
  * onto the descriptor — see `SessionDescriptor.keepAlive`. Default false
@@ -5382,6 +5901,22 @@ interface BranchGcVerdictLookupInput {
5382
5901
  * lookup, so a caller can check one tip without re-running a whole `branch_gc` plan. */
5383
5902
  type BranchGcVerdictReader = (input: BranchGcVerdictLookupInput) => Promise<BranchGcVerdictRecordView | null>;
5384
5903
 
5904
+ /**
5905
+ * Set by the tunnel server (never a caller) on every `http_request` it
5906
+ * forwards from a HOST-scoped pairing (DEVICES-PLAN item 1) —
5907
+ * `buildDaemonTunnelServerOptions`'s `pairingScope` in
5908
+ * `packages/cli/src/util/tunnel-serve.ts`. `httpInjectHeaders` overrides any
5909
+ * same-named header the ORIGINAL caller's frame carried, so this can never be
5910
+ * spoofed by whoever dials in over a pairing — only the daemon's own
5911
+ * server-side scope decision at serve time ever sets it.
5912
+ *
5913
+ * Deliberately NOT paired with an `isLoopback` bypass the way most other
5914
+ * routes are: every `http_request` a tunnel forwards to THIS server lands as
5915
+ * a loopback socket (the daemon dialing its own gateway via `fetch`), so
5916
+ * `isLoopback` is true for ALL forwarded traffic — host-scoped or not — and
5917
+ * cannot distinguish them. `handleDeviceInference` checks this header alone.
5918
+ */
5919
+ declare const HOST_SCOPE_HEADER = "x-agentproto-host-scope";
5385
5920
  /**
5386
5921
  * Pluggable adapter resolver — keeps the runtime package free of any
5387
5922
  * @agentproto/cli dep. The host (cli `serve`, playground, embedding
@@ -5703,127 +6238,6 @@ interface SessionBackgroundTask {
5703
6238
  startedAt: string;
5704
6239
  }
5705
6240
 
5706
- /**
5707
- * Typed inter-session message envelope (AIP-46 §Session messages).
5708
- *
5709
- * A PROMPT is an instruction from whoever drives a session; a MESSAGE is a
5710
- * report between sessions in the same tree whose sender the DAEMON attests.
5711
- * Everything in `from` is computed here from the caller's verified identity
5712
- * and the session forest — a send request can never set it.
5713
- *
5714
- * Pure module: no registry, no I/O. The registry (sessions.ts) carries a
5715
- * `SessionMessage` on a queued prompt (`QueuedPrompt.envelope`), renders it
5716
- * into the model's context with `renderSessionMessages`, and records it as
5717
- * its own `session-message` transcript record — never a `user-prompt`.
5718
- */
5719
- /** Sender's position in the tree, relative to the recipient. */
5720
- type MessageRelation = "child" | "parent" | "sibling" | "human" | "system";
5721
- type MessageKind = "report" | "question" | "blocker" | "done" | "notice";
5722
- type MessageUrgency = "fyi" | "next-turn" | "steer" | "interrupt";
5723
- type MessageDeliveryVia = "wait" | "steer" | "turn" | "interrupt" | "inbox";
5724
- declare const MESSAGE_KINDS: readonly MessageKind[];
5725
- declare const MESSAGE_URGENCIES: readonly MessageUrgency[];
5726
- interface SessionMessageFrom {
5727
- /** Absent only for relation `human` | `system`. */
5728
- sessionId?: string;
5729
- /** Registry label at send time — display only, never identity. */
5730
- label?: string;
5731
- role?: string;
5732
- adapter?: string;
5733
- relation: MessageRelation;
5734
- }
5735
- interface SessionMessage {
5736
- /** `msg_<uuid8>`, daemon-minted. */
5737
- id: string;
5738
- ts: string;
5739
- /** Recipient sessionId. */
5740
- to: string;
5741
- from: SessionMessageFrom;
5742
- kind: MessageKind;
5743
- /** Requested urgency. */
5744
- urgency: MessageUrgency;
5745
- /** Stamped on delivery. */
5746
- delivered?: {
5747
- via: MessageDeliveryVia;
5748
- at: string;
5749
- turnSeq?: number;
5750
- };
5751
- /** Thread id; defaults to the first message's id. */
5752
- correlationId?: string;
5753
- /** A `SessionMessage.id` this answers. */
5754
- replyTo?: string;
5755
- text: string;
5756
- /** Optional structured payload (≤ `MAX_MESSAGE_DATA_BYTES`). */
5757
- data?: Record<string, unknown>;
5758
- ackedAt?: string;
5759
- }
5760
- declare const MAX_MESSAGE_DATA_BYTES: number;
5761
- /** The slice of a session descriptor relation/ACL resolution needs. */
5762
- interface MessageTreeNode {
5763
- id: string;
5764
- parentSessionId?: string;
5765
- label?: string;
5766
- role?: string;
5767
- adapterSlug?: string;
5768
- }
5769
- /**
5770
- * Relation of `sender` to `recipient` in the session forest, or `undefined`
5771
- * when they're not tree neighbours (cousins, strangers, self). `sender`
5772
- * absent ⇒ `human` (an HTTP/CLI operator); daemon-originated notices pass
5773
- * `"system"` explicitly instead of calling this.
5774
- */
5775
- declare function resolveRelation(sender: MessageTreeNode | undefined, recipient: MessageTreeNode): Exclude<MessageRelation, "system"> | undefined;
5776
- /**
5777
- * ACL: child→parent, parent→direct child, human/system→any. Sibling↔sibling
5778
- * only when `allowSiblings` (config-gated, default off). Anything else — a
5779
- * cousin, a stranger, yourself — is refused.
5780
- */
5781
- declare function isMessageAllowed(relation: MessageRelation | undefined, opts?: {
5782
- allowSiblings?: boolean;
5783
- }): relation is MessageRelation;
5784
- /** Build a `from` block from the verified sender + the resolved relation. */
5785
- declare function messageFrom(sender: MessageTreeNode | undefined, relation: MessageRelation): SessionMessageFrom;
5786
- declare function mintMessageId(): string;
5787
- declare function createSessionMessage(input: {
5788
- to: string;
5789
- from: SessionMessageFrom;
5790
- text: string;
5791
- kind?: MessageKind;
5792
- urgency?: MessageUrgency;
5793
- replyTo?: string;
5794
- correlationId?: string;
5795
- data?: Record<string, unknown>;
5796
- }): SessionMessage;
5797
- interface MessageFilter {
5798
- /** Sender session ids, or `"children"` for any message whose relation is
5799
- * `child` (i.e. from one of the recipient's own children). */
5800
- from?: readonly string[] | "children";
5801
- kind?: readonly MessageKind[];
5802
- correlationId?: string;
5803
- }
5804
- declare function matchesMessageFilter(msg: SessionMessage, filter: MessageFilter): boolean;
5805
- declare const MESSAGE_TAG = "agentproto-message";
5806
- /** Child-settable label → attribute-safe: `[A-Za-z0-9._-]`, ≤ 48 chars. */
5807
- declare function sanitizeLabel(label: string | undefined): string | undefined;
5808
- /** Escape every envelope/body sentinel inside a message body, so a sender
5809
- * can't close the tag early and forge a second header. */
5810
- declare function escapeMessageBody(text: string): string;
5811
- /** A human prompt is NOT wrapped (humans keep today's UX), but a line that
5812
- * starts with the envelope sentinel is escaped, so the invariant "text
5813
- * outside an `<agentproto-message>` tag is the human" holds. Nothing else
5814
- * in human text changes. */
5815
- declare function escapeHumanPrompt(text: string): string;
5816
- declare function renderSessionMessage(msg: SessionMessage): string;
5817
- /** A coalesced batch → sibling tags, one per message, in order. */
5818
- declare function renderSessionMessages(msgs: readonly SessionMessage[]): string;
5819
- /** One-line typed digest of `fyi` messages that never woke the session —
5820
- * prepended (as a `system-prompt` slice) to the next turn it runs anyway. */
5821
- declare function renderInboxDigest(msgs: readonly SessionMessage[]): string;
5822
- /** Taught once, the first time a session RECEIVES a message (recorded as a
5823
- * `system-prompt`). Children also get the same invariant in their lineage
5824
- * line at spawn. */
5825
- declare const MESSAGE_PREAMBLE: string;
5826
-
5827
6241
  /**
5828
6242
  * A per-session observer — the pluggable seam behind the runtime's transcript
5829
6243
  * tap. `sessions.ts` drives one observer per turn (prompt in, each stream event,
@@ -5884,6 +6298,94 @@ interface SessionObserver {
5884
6298
  */
5885
6299
  declare function composeSessionObservers(observers: readonly SessionObserver[]): SessionObserver;
5886
6300
 
6301
+ /**
6302
+ * Session-scoped artifact store — durable, content-addressed documents
6303
+ * (image, pdf, html, presentation, site, generic file) attached to an
6304
+ * agent-cli session, kept across restarts and browsable in the session's
6305
+ * "Artifacts" section, distinct from the ephemeral inline card a chat
6306
+ * message renders.
6307
+ *
6308
+ * Deliberately mirrors `transcript-writer.ts`'s `AttachmentEntry`, which
6309
+ * itself mirrors AIP-58's `ArtifactEntry` (`packages/workflow-runtime/src/
6310
+ * types.ts`) field-for-field — `key`/`path`/`sha256`/`size`/`contentType` —
6311
+ * so promoting an attachment to an artifact is a metadata copy, never a
6312
+ * data migration. What this module adds on top of a bare `ArtifactEntry`
6313
+ * is the session-artifact-specific envelope AIP-58 has no notion of:
6314
+ * `kind`, `label`, `pinned`, `createdBy`, `sourceRef`, and version history
6315
+ * under one stable `key`.
6316
+ *
6317
+ * Storage layout, under `sessionTranscriptDir(sessionId)`:
6318
+ *
6319
+ * artifacts/<sha256>.<ext> — one file per single-file version
6320
+ * artifacts/<sha256>/ — one directory per "site" version
6321
+ * artifacts.jsonl — append-only ledger, one JSON line per
6322
+ * mutation (`artifact.added` |
6323
+ * `artifact.pinned`)
6324
+ *
6325
+ * The ledger is the single source of truth, same posture AIP-58 §5 takes
6326
+ * for its run event log: `listSessionArtifacts`/`getSessionArtifact` are
6327
+ * folds over it, never a second store that could disagree with it. A
6328
+ * `key`'s version history is append-only; nothing here ever rewrites a
6329
+ * prior version's file or ledger line.
6330
+ */
6331
+ type ArtifactKind = "image" | "document" | "pdf" | "html" | "presentation" | "site" | "file";
6332
+ type ArtifactCreator = "agent" | "user";
6333
+ interface ArtifactVersionEntry {
6334
+ version: number;
6335
+ /** Content hash — sha256 of the file's bytes for a single-file version,
6336
+ * or a manifest hash (sorted relative paths + sizes) for a "site"
6337
+ * directory version. Not a cryptographic content-integrity guarantee
6338
+ * for the directory case, only a stable change-detection key. */
6339
+ sha256: string;
6340
+ /** Relative to `sessionTranscriptDir(sessionId)` — `"artifacts/<sha>.<ext>"`
6341
+ * for a single file, `"artifacts/<sha>"` (a directory) for a site. */
6342
+ path: string;
6343
+ /** True when `path` names a directory (a "site"). */
6344
+ isDirectory: boolean;
6345
+ size: number;
6346
+ contentType?: string;
6347
+ createdAt: string;
6348
+ createdBy: ArtifactCreator;
6349
+ sourceRef?: string;
6350
+ }
6351
+ interface ArtifactRecord {
6352
+ key: string;
6353
+ kind: ArtifactKind;
6354
+ label?: string;
6355
+ pinned: boolean;
6356
+ updatedAt: string;
6357
+ versions: ArtifactVersionEntry[];
6358
+ }
6359
+ interface AddSessionArtifactInput {
6360
+ /** Stable id this version is filed under. Versions accrue when the same
6361
+ * key is added again with different content. Defaults to a slug of
6362
+ * `name`, else the content hash. */
6363
+ key?: string;
6364
+ kind?: ArtifactKind;
6365
+ label?: string;
6366
+ createdBy: ArtifactCreator;
6367
+ /** e.g. `"attachment:attachments/<sha>.<ext>"`, `"workflow-run:<runId>/
6368
+ * <artifactKey>"`, `"canvakit:<exportPath>"` — free-form provenance. */
6369
+ sourceRef?: string;
6370
+ /** Inline content, base64-encoded. Mutually exclusive with `sourcePath`. */
6371
+ bytes?: string;
6372
+ /** Display name — also the default `key`/extension source. */
6373
+ name?: string;
6374
+ mimeType?: string;
6375
+ /** Absolute path to an existing file or directory (e.g. a canvakit
6376
+ * export) to copy into the store. A directory implies `kind: "site"`
6377
+ * unless overridden. Mutually exclusive with `bytes`. */
6378
+ sourcePath?: string;
6379
+ }
6380
+ interface GetSessionArtifactResult {
6381
+ record: ArtifactRecord;
6382
+ version: ArtifactVersionEntry;
6383
+ /** Absent for a "site" (directory) version — browse it via the raw-serve
6384
+ * route instead; nothing here bounds a whole directory's bytes. */
6385
+ content?: Buffer;
6386
+ truncated: boolean;
6387
+ }
6388
+
5887
6389
  /** Where a session's `contextSize` came from — see the module doc. */
5888
6390
  type ContextSizeSource = "adapter" | "catalog" | "reported";
5889
6391
 
@@ -5907,8 +6409,11 @@ type ContextSizeSource = "adapter" | "catalog" | "reported";
5907
6409
  */
5908
6410
 
5909
6411
  interface SessionOutcomeArtifact {
5910
- type: "pr" | "commit" | "url";
5911
- /** The artifact's canonical reference — a PR url, a commit sha, a url. */
6412
+ type: "pr" | "commit" | "url" | "file";
6413
+ /** The artifact's canonical reference — a PR url, a commit sha, a url,
6414
+ * or (for `type: "file"`) the session artifact's `key`
6415
+ * (`session-artifacts.ts`) — resolve it via `session_artifact_get` /
6416
+ * `GET /sessions/:id/artifacts/:key`. */
5912
6417
  ref: string;
5913
6418
  title?: string;
5914
6419
  }
@@ -5921,12 +6426,26 @@ interface SessionOutcomeLink {
5921
6426
  title?: string;
5922
6427
  }
5923
6428
  interface SessionOutcome {
5924
- /** Level 1 only ever writes `"derived"`; a declared outcome (Level 2) is a
5925
- * later addition. */
5926
- source: "derived";
6429
+ /** Level 1 (`deriveSessionOutcome`) only ever writes `"derived"` — what an
6430
+ * ended session produced, with zero agent/human cooperation. Level 2 adds
6431
+ * `"judged"` (a judge agent decided the verdict, `judgedBy` names the
6432
+ * judge session) and `"declared"` (a deterministic rule or an operator
6433
+ * declared it, `judgedBy` is `"steward-rules"` or absent). See
6434
+ * `registry.closeWithOutcome`. */
6435
+ source: "derived" | "judged" | "declared";
5927
6436
  /** `produced` — the session said something or left an artifact;
5928
6437
  * `empty` — no assistant text and no artifacts. */
5929
6438
  status: "produced" | "empty";
6439
+ /** Level 2 only: what a judge/declaration decided the session's work
6440
+ * amounted to. Absent on a plain Level 1 `"derived"` outcome. */
6441
+ verdict?: "done" | "abandoned" | "blocked" | "needs-input";
6442
+ /** Level 2 only: who reached `verdict` — a session id (a judge agent) or
6443
+ * the literal `"steward-rules"` for a deterministic close with no judge
6444
+ * in the loop. Absent on a plain Level 1 `"derived"` outcome. */
6445
+ judgedBy?: string;
6446
+ /** Level 2 only: free-text note from the judge/declaration explaining
6447
+ * `verdict` — never populated by `deriveSessionOutcome` itself. */
6448
+ note?: string;
5930
6449
  /** Last assistant message of the session, trimmed to
5931
6450
  * {@link OUTCOME_SUMMARY_MAX} chars (the TAIL is kept — the conclusion,
5932
6451
  * not the preamble). Absent when the session never said anything. */
@@ -5972,6 +6491,12 @@ interface SessionOutcomeCompact {
5972
6491
  interface DeriveSessionOutcomeInput {
5973
6492
  /** The last assistant message the registry observed (raw, untrimmed). */
5974
6493
  lastAssistantText?: string;
6494
+ /** Extra `type: "file"` artifacts to merge alongside the PR-derived ones
6495
+ * — the registry passes the session's PINNED `session-artifacts.ts`
6496
+ * records here so the ended block can show them (an unpinned artifact
6497
+ * still lists in the session's "Artifacts" section, just not surfaced
6498
+ * in the terse outcome). */
6499
+ artifacts?: SessionOutcomeArtifact[];
5975
6500
  now?: Date;
5976
6501
  }
5977
6502
  /**
@@ -6502,7 +7027,10 @@ interface SessionDescriptor {
6502
7027
  * `"parent-exited"` (an orchestrator's dying subtree reap),
6503
7028
  * `"provider-limit"` (a driver-reported subscription/usage-cap error,
6504
7029
  * e.g. Claude Code's "hit your session limit"), `"forgotten"` (a live
6505
- * session killed as part of an operator `DELETE`).
7030
+ * session killed as part of an operator `DELETE`), `"steward-completed"` /
7031
+ * `"steward-abandoned"` (the session steward's `closeWithOutcome` —
7032
+ * verdict `"done"` vs. `"abandoned"`; a `"blocked"`/`"needs-input"`
7033
+ * verdict doesn't close at all, see `SessionDescriptor.wrapupFlag`).
6506
7034
  * Absent for every terminal path this file doesn't tag (a plain natural
6507
7035
  * exit, an ordinary turn error) — the session's own fault, or at least
6508
7036
  * not something worth a special label. A string outside this list is
@@ -6535,6 +7063,20 @@ interface SessionDescriptor {
6535
7063
  * revives the row. Persisted, so it survives restarts and archiving. See
6536
7064
  * `session-outcome.ts`. */
6537
7065
  outcome?: SessionOutcome;
7066
+ /** A steward verdict of `"blocked"` or `"needs-input"` from
7067
+ * `registry.closeWithOutcome` (`session-wrapup.ts` / FIX-9A) — recorded
7068
+ * INSTEAD of closing, since neither verdict means the session is done.
7069
+ * The session stays exactly as alive as it was; this is a signal for a
7070
+ * human/orchestrator to look, not a termination. Persisted; overwritten
7071
+ * (not accumulated) by the next flag, and left stale on the descriptor
7072
+ * until something clears it (nothing here does — a later successful
7073
+ * turn/close is a separate, more informative signal than deleting this). */
7074
+ wrapupFlag?: {
7075
+ verdict: "blocked" | "needs-input";
7076
+ note?: string;
7077
+ judgedBy?: string;
7078
+ at: string;
7079
+ };
6538
7080
  /** Last time anything was written to stdout/stderr. Lets the UI
6539
7081
  * spot stuck sessions ("running for 2h, last output 12min ago"). */
6540
7082
  lastOutputAt?: string;
@@ -6572,6 +7114,12 @@ interface SessionDescriptor {
6572
7114
  * every read since it's a live OS query, stale the instant it's
6573
7115
  * written to disk. */
6574
7116
  processAlive?: boolean;
7117
+ /** Summed RSS in bytes of this session's process and all its OS-level
7118
+ * descendants (`processTreeRss`, `process-memory.ts`). Ephemeral,
7119
+ * never persisted — stamped only when a caller opts in (`session_list`'s
7120
+ * `withMemory: true`) since it costs a `ps` spawn; absent otherwise, and
7121
+ * absent for a row with no `pid` or that `ps` didn't report. */
7122
+ rssBytes?: number;
6575
7123
  /** Count of live supervisors currently blocked waiting on this session —
6576
7124
  * HTTP `GET /sessions/:id/wait` long-polls and `session_monitor`
6577
7125
  * subscriptions, both via `monitorSessionWait` (#session-visibility).
@@ -6722,11 +7270,14 @@ interface SessionDescriptor {
6722
7270
  * the underlying `killedMidTurn`/`endedReason`). The in-place resume path
6723
7271
  * never auto-retries the interrupted prompt. */
6724
7272
  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
7273
+ /** The `SessionsRegistry.bootId` of the daemon boot that followed the
7274
+ * restart which cut this row's turn off. The graceful shutdown that
7275
+ * interrupts a turn parks `INTERRUPTED_PENDING_BOOT` here; the next boot
7276
+ * reload (`loadHistorySnapshot`) swaps that — or, after an unclean exit, a
7277
+ * row still alive on disk — for its own `bootId`. Nothing else is ever
7278
+ * stamped: an interrupted row carrying no marker was persisted by a daemon
7279
+ * predating it, i.e. by an older restart, and stays stale. Cleared on the
7280
+ * next successful turn-end (with the markers it
6730
7281
  * qualifies). `interruptedAtBoot === registry.bootId` is how the
6731
7282
  * continue-interrupted verb and boot pass pick "interrupted by the LAST
6732
7283
  * restart" apart from a stale interruption nobody picked back up. PERSISTED
@@ -7175,6 +7726,20 @@ interface SessionDescriptor {
7175
7726
  * — so this carries no secrets today. Should the shape ever grow
7176
7727
  * headers/tokens, NEVER log this field's contents. */
7177
7728
  mcpServers?: AcpMcpServer[];
7729
+ /** Resolved skills list at spawn time — explicit ∪ preset ∪ config
7730
+ * defaults (`resolveSpawnDefaults`'s own merge), recorded regardless of
7731
+ * whether the adapter actually CONSUMES a `skills` spawn option (today
7732
+ * only hermes does — see `normalizeSkillsOption`). Persisted so it
7733
+ * survives restart/resume like `mcpServers`, and so a read surface
7734
+ * (`session_capabilities`) can show what was resolved even for an
7735
+ * adapter (claude-code) that auto-discovers skills on its own and never
7736
+ * reads this list back. */
7737
+ skills?: string[];
7738
+ /** Capability bundle ids (`bundle_list`) resolved for this spawn (PLAN D
7739
+ * phase 1) — ids only, never the expanded imported-MCP snapshots (those
7740
+ * live only in `mcpServers` above, as reference-only proxy URLs). Absent
7741
+ * ⇒ no bundle was attached. */
7742
+ bundles?: string[];
7178
7743
  /** Provider-specific resume hints sniffed from the session's
7179
7744
  * output. claude-code prints `claude --resume <uuid>` on exit;
7180
7745
  * we capture that uuid as `claudeResumeId`. On `restart`, when a
@@ -7277,6 +7842,12 @@ interface SessionDescriptor {
7277
7842
  * want. Threaded like `permissionHold` — set at spawn time, immutable
7278
7843
  * thereafter. */
7279
7844
  notifyParentOnCrash?: boolean;
7845
+ /** Per-spawn opt-out of sentinel auto-link (AIP-60 §6/step 4,
7846
+ * `agent_start.sentinel: false`). `undefined`/`true` = allowed (subject to
7847
+ * `config.sentinel.autoWatchPrs`); `false` = this session's opened PRs are
7848
+ * never auto-watched, no matter the config. Set once at spawn, immutable
7849
+ * thereafter — same threading as `notifyParentOnCrash`. */
7850
+ sentinelAutoWatch?: boolean;
7280
7851
  /** True when the session was spawned in permission-hold mode. */
7281
7852
  permissionHold?: boolean;
7282
7853
  /** Effective OS-level confinement of the adapter's own process
@@ -7311,9 +7882,17 @@ interface SessionDescriptor {
7311
7882
  * time (spawn / resume) — shown in `session_list` so a sender can predict
7312
7883
  * a message's delivery tier. `steering`: the agent accepts ACP steering
7313
7884
  * (`_session/steering`) — a `steer` message can be injected into its
7314
- * running turn instead of waiting for it to end. */
7885
+ * running turn instead of waiting for it to end. `commandsSupported`:
7886
+ * the live agent session is the ACP protocol arm (has a working `steer`
7887
+ * method — only the ACP arm implements it, per `AgentSessionLike`'s own
7888
+ * doc), which is what actually emits `available_commands_update`
7889
+ * notifications; false for the print/proprietary arms regardless of
7890
+ * `steeringSupported`, since a harness can be ACP without advertising
7891
+ * the steering extension. Read by `session_capabilities` to derive its
7892
+ * `arm`/`commandsSupported` fields without re-deriving the signal. */
7315
7893
  capabilities?: {
7316
7894
  steering: boolean;
7895
+ commandsSupported: boolean;
7317
7896
  };
7318
7897
  /** FIFO of prompts that arrived while this session was mid-turn and
7319
7898
  * asked to be QUEUED rather than rejected (`enqueuePrompt`'s
@@ -7767,6 +8346,28 @@ type PermissionRespondResult = {
7767
8346
  error: "not_found" | "session_gone" | "no_matching_option" | "unsupported";
7768
8347
  message: string;
7769
8348
  };
8349
+ /** Input to `SessionsRegistry.closeWithOutcome`. `verdict: "done" |
8350
+ * "abandoned"` records the Level 2 fields onto the session's
8351
+ * `SessionOutcome` and actually closes it; `"blocked" | "needs-input"`
8352
+ * records the SAME `note`/`judgedBy` onto `SessionDescriptor.wrapupFlag`
8353
+ * instead and never touches the session's liveness. See {@link
8354
+ * SessionOutcome} / {@link SessionDescriptor.wrapupFlag} for the field
8355
+ * meanings. */
8356
+ interface CloseWithOutcomeInput {
8357
+ verdict: "done" | "abandoned" | "blocked" | "needs-input";
8358
+ /** Overrides the outcome's derived summary when given (e.g. a judge's own
8359
+ * written summary) — trimmed the same way `deriveSessionOutcome` trims
8360
+ * `lastAssistantText`. Omitted keeps whatever `deriveSessionOutcome`
8361
+ * computed from the session's own last assistant message. Ignored for a
8362
+ * `"blocked"`/`"needs-input"` verdict (there's no outcome to summarize —
8363
+ * nothing closed). */
8364
+ summary?: string;
8365
+ note?: string;
8366
+ /** A judge session id, or `"steward-rules"` for a deterministic close with
8367
+ * no judge in the loop. */
8368
+ judgedBy?: string;
8369
+ source: "judged" | "declared";
8370
+ }
7770
8371
  interface SessionsRegistry {
7771
8372
  spawn(input: SpawnSessionInput): SessionDescriptor;
7772
8373
  /** Adopt a ChildProcess that was spawned outside the registry —
@@ -8275,6 +8876,29 @@ interface SessionsRegistry {
8275
8876
  * touches `keepAlive`, reaper eligibility, or any notification path —
8276
8877
  * pin is quiet, structural state only. Throws when the id is unknown. */
8277
8878
  setPinned(id: string, pinned: boolean): SessionDescriptor;
8879
+ /** Materialize a new artifact (or version of one) into the session's
8880
+ * durable artifact store (`session_artifact_add` MCP verb / `POST
8881
+ * /sessions/:id/artifacts`) — see `session-artifacts.ts`. Emits
8882
+ * `session:artifact-added` on a real (non-deduped) write. Throws when
8883
+ * the id is unknown. */
8884
+ addSessionArtifact(id: string, input: AddSessionArtifactInput): ArtifactRecord;
8885
+ /** Current-state fold of the session's artifact store — every key's
8886
+ * version history, most-recently-updated first. Empty array for a
8887
+ * known session with no artifacts yet. Throws when the id is unknown. */
8888
+ listSessionArtifacts(id: string): ArtifactRecord[];
8889
+ /** One artifact version's metadata plus its bounded file content (single-
8890
+ * file kinds only — a "site" version's content is browsed via the raw-
8891
+ * serve HTTP route instead). Undefined when the key/version doesn't
8892
+ * exist. Throws when the session id is unknown. */
8893
+ getSessionArtifact(id: string, key: string, opts?: {
8894
+ version?: number;
8895
+ maxBytes?: number;
8896
+ }): GetSessionArtifactResult | undefined;
8897
+ /** Set or clear an artifact's pin (`session_artifact_pin` MCP verb /
8898
+ * `POST /sessions/:id/artifacts/:key/pin`). Emits
8899
+ * `session:artifact-pinned-changed`. Undefined when the key doesn't
8900
+ * exist. Throws when the session id is unknown. */
8901
+ setArtifactPinned(id: string, key: string, pinned: boolean): ArtifactRecord | undefined;
8278
8902
  /**
8279
8903
  * Manually override `awaitingInput`/`awaitingQuestion` (the
8280
8904
  * `session_flag_status` MCP verb) — the ONE write path for this pair
@@ -8380,6 +9004,34 @@ interface SessionsRegistry {
8380
9004
  * reason (including `"operator-stopped"` or none) on a terminal row
8381
9005
  * stays the plain no-op. */
8382
9006
  kill(id: string, signal?: NodeJS.Signals, reason?: SessionEndReason): boolean;
9007
+ /** Close a LIVE agent-cli session with a Level 2 (judged/declared) outcome
9008
+ * — the primitive the session steward (FIX-9A/9B) drives once a wrap-up
9009
+ * plan (`planSessionWrapup`, `session-wrapup.ts`) puts a session in the
9010
+ * `close`/`stuck` class, or a judge agent reaches a verdict on a `judge`
9011
+ * one. Verdict `"done"` or `"abandoned"` actually closes: terminates the
9012
+ * same graceful way `kill()` does, tags `endedReason: "steward-completed"`
9013
+ * (`"done"`) or `"steward-abandoned"` (`"abandoned"`), and — like
9014
+ * `reapIdle`, unlike a plain `kill()` — CLEARS the in-memory
9015
+ * `agentSession` binding so the row stays lazy-resumable in place. The
9016
+ * recorded outcome's `source`/`verdict`/`judgedBy`/`note` are stamped onto
9017
+ * the row's `SessionOutcome` (see `session-outcome.ts`) on top of what
9018
+ * `deriveSessionOutcome` would otherwise compute (last assistant message,
9019
+ * PRs, cost) — a declared close still shows what the session actually
9020
+ * produced. Verdict `"blocked"` or `"needs-input"` is NOT a completion —
9021
+ * nothing is terminated; the verdict is recorded as
9022
+ * `SessionDescriptor.wrapupFlag` instead, and the session is left exactly
9023
+ * as alive as it was.
9024
+ *
9025
+ * Refuses (returns false, no-op) a session that is not a live
9026
+ * (`running`/`starting`) agent-cli row, or that AT THE MOMENT OF THE CALL
9027
+ * is `busy`/`awaitingInput`/`awaitingPermission` or has a background task
9028
+ * outstanding (`pendingBgTasks`/`backgroundTasks`) — a plan computed
9029
+ * moments earlier can be stale; this is the re-check that keeps an
9030
+ * autonomous close from ever landing on a session that just picked up a
9031
+ * turn, asked a question, or still has something in flight. Applies to
9032
+ * BOTH branches (close and flag-only) — a stale plan is refused either
9033
+ * way, never silently acted on. */
9034
+ closeWithOutcome(id: string, input: CloseWithOutcomeInput): boolean;
8383
9035
  /** Retire a long-idle agent-cli session to free its adapter process — the
8384
9036
  * primitive the idle-session reaper (`runIdleReapPass`, PR-6) drives on a
8385
9037
  * periodic sweep. Terminates the underlying adapter/child the SAME graceful
@@ -8487,6 +9139,18 @@ interface SessionsRegistry {
8487
9139
  * window keeps aging out naturally rather than being wiped by the
8488
9140
  * give-up itself. Returns false (no-op) for an unknown id. */
8489
9141
  giveUpRestart(id: string, message: string): boolean;
9142
+ /** Record a daemon-authored DISPLAY-ONLY notice into an agent-cli
9143
+ * session's transcript — the same `kind: "notice"` event `markCrashed`/
9144
+ * `giveUpRestart`/`interruptInFlightTurn` already stamp on their own
9145
+ * teardown/interrupt paths (see transcript-writer.ts's "notice" case),
9146
+ * exposed generically for a caller with just a session id, no `rt`
9147
+ * in hand. Appends to `events.jsonl` (so `session_story`/`live_session`
9148
+ * render it as a system message) and the ring buffer — nothing else:
9149
+ * it does NOT touch `promptQueue`, the inbox, or `busy`, so it can never
9150
+ * enqueue a prompt, wake an idle session into a turn, or interrupt a busy
9151
+ * one. Returns false (no-op) for an unknown id or a non-`agent-cli`
9152
+ * session. */
9153
+ recordNotice(id: string, text: string): boolean;
8490
9154
  /** List permission requests currently parked in the pending-permissions
8491
9155
  * inbox across all permission-hold sessions, newest last. Optionally
8492
9156
  * filtered to one session. */
@@ -8617,6 +9281,13 @@ interface SpawnAgentInput {
8617
9281
  * the resume/re-spawn path can re-mount the same toolset (orchestrator
8618
9282
  * WP1). */
8619
9283
  mcpServers?: AcpMcpServer[];
9284
+ /** Resolved skills list at spawn time (`resolveSpawnDefaults`'s merged
9285
+ * explicit ∪ preset ∪ config-defaults result) — recorded verbatim onto
9286
+ * {@link SessionDescriptor.skills}. See that field's doc. */
9287
+ skills?: string[];
9288
+ /** Capability bundle ids resolved for this spawn — recorded verbatim onto
9289
+ * {@link SessionDescriptor.bundles}; see that field's doc. */
9290
+ bundles?: string[];
8620
9291
  /** Persistent isolated-config dir this spawn passed to
8621
9292
  * `startSession({ configDir })` — recorded verbatim onto
8622
9293
  * {@link SessionDescriptor.adapterConfigDir} so restart/lazy-resume can
@@ -8720,6 +9391,9 @@ interface SpawnAgentInput {
8720
9391
  /** Recorded verbatim onto {@link SessionDescriptor.notifyParentOnCrash} —
8721
9392
  * see that field's doc. Default false. */
8722
9393
  notifyParentOnCrash?: boolean;
9394
+ /** Recorded verbatim onto {@link SessionDescriptor.sentinelAutoWatch} — see
9395
+ * that field's doc. `undefined` = allowed. */
9396
+ sentinelAutoWatch?: boolean;
8723
9397
  /** Exempt this session from the idle-reaper (`isReapable` in
8724
9398
  * idle-reaper.ts) regardless of how long it sits idle — stamped straight
8725
9399
  * onto `SessionDescriptor.keepAlive`. Default false. */
@@ -9048,10 +9722,13 @@ declare function runEagerResumePass(opts: {
9048
9722
  * Eligibility (both modes): an agent-cli row that is `interrupted` BY THE LAST
9049
9723
  * RESTART (`interruptedAtBoot === registry.bootId` — a stale interruption from
9050
9724
  * 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.
9725
+ * attempt cap (`canResume`), not already busy, and whose `cwd` still exists
9726
+ * (a removed worktree fails the adapter respawn every time — knowable up front,
9727
+ * so the dry run must not call such a row eligible). The prompt goes through
9728
+ * `enqueuePrompt`, the same entry MCP `agent_prompt` uses, so a
9729
+ * dead-but-resumable row lazy-resumes with its billing auth re-resolved exactly
9730
+ * as a human prompt would. When that resume fails, the outcome says so instead
9731
+ * of passing on the bare post-resume "not alive" admission error.
9055
9732
  *
9056
9733
  * No-loop rules (boot mode only — a human pressing the button is intent):
9057
9734
  * - never auto-continue a row whose resume has failed (`resumeAttempts > 0`:
@@ -9091,6 +9768,8 @@ type ContinueInterruptedSkipReason =
9091
9768
  | "resume-cap-exhausted"
9092
9769
  /** Mid-turn already — someone got there first. */
9093
9770
  | "busy"
9771
+ /** The row's `cwd` is gone (removed worktree) — the resume would fail. */
9772
+ | "cwd-missing"
9094
9773
  /** Boot mode: a resume attempt has failed on this row. */
9095
9774
  | "resume-failed"
9096
9775
  /** Boot mode: already auto-continued by this boot. */
@@ -9160,6 +9839,8 @@ declare function continueInterruptedSessions(opts: {
9160
9839
  /** Extra visibility filter (a scoped orchestrator's subtree). Rows it
9161
9840
  * rejects are dropped silently — never reported, never sent. */
9162
9841
  visible?: (desc: SessionDescriptor) => boolean;
9842
+ /** Filesystem probe for the `cwd-missing` check. Default `existsSync`. */
9843
+ cwdExists?: (cwd: string) => boolean;
9163
9844
  }): Promise<ContinueInterruptedResult>;
9164
9845
  /** Tally of the boot-time continue pass, for the boot-banner line. */
9165
9846
  interface ContinueOnBootSummary {
@@ -9387,6 +10068,89 @@ interface InboundWatcher {
9387
10068
  shutdown(): void;
9388
10069
  }
9389
10070
 
10071
+ /**
10072
+ * `capabilities_inventory` — one read that answers "what MCP servers and
10073
+ * skills does this daemon actually know about, and which harnesses can
10074
+ * reach them?" Backs both the `capabilities_inventory` MCP tool
10075
+ * (session-tools.ts) and its HTTP twin (`GET /capabilities/inventory`,
10076
+ * http-server.ts) so the two surfaces can never drift.
10077
+ *
10078
+ * Read-only, never throws: a failure in one source (MCP discovery, the
10079
+ * proxy registry, an adapter package that fails to import) becomes an
10080
+ * `error` string on that block alone — the rest of the inventory still
10081
+ * returns. Redaction: nothing here ever carries command/args/env/headers/
10082
+ * url — only the identity/status fields a UI needs (same discipline as
10083
+ * `compactDiscoveredMcp` / `compactImportedMcpEntry`).
10084
+ *
10085
+ * The `skills` block's per-adapter `metadata.skills` / `options` read uses
10086
+ * the same dynamic-`import()` + duck-typed-export technique as
10087
+ * `role-registry.ts`'s adapter-carried role discovery — reused rather than
10088
+ * reinvented, and importantly NOT a `packages/cli` dependency: it walks the
10089
+ * exact adapter list `listAgentAdapters` already returned (so `byHarness`
10090
+ * is guaranteed the same installed set as `adapter_list`), importing each
10091
+ * adapter's own already-installed package by its `packageName`.
10092
+ */
10093
+
10094
+ interface CapabilitiesInventoryImportedMcp {
10095
+ id: string;
10096
+ alias?: string;
10097
+ name: string;
10098
+ type: string;
10099
+ source: string;
10100
+ status: "connected" | "idle" | "error" | "unknown";
10101
+ error?: string;
10102
+ /** Only present when `status === "connected"` — never connects just to count. */
10103
+ toolCount?: number;
10104
+ usedBySessions: string[];
10105
+ }
10106
+ interface CapabilitiesInventoryDiscoveredMcp {
10107
+ id: string;
10108
+ source: string;
10109
+ scope: string;
10110
+ name: string;
10111
+ type: string;
10112
+ imported: boolean;
10113
+ }
10114
+ interface CapabilitiesInventoryMcp {
10115
+ imported: CapabilitiesInventoryImportedMcp[];
10116
+ discovered: CapabilitiesInventoryDiscoveredMcp[];
10117
+ /** Which harnesses get the daemon's own `/mcp` (and so the imported pool)
10118
+ * mounted by default — derived from `shouldInjectDaemonSelfMount` +
10119
+ * each installed adapter's declared protocol, never a hardcoded list. */
10120
+ daemonMountByHarness: Record<string, "default" | "on-request" | "none">;
10121
+ error?: string;
10122
+ }
10123
+ interface SkillPackInventoryEntry {
10124
+ name: string;
10125
+ version?: string;
10126
+ path: string;
10127
+ skills: string[];
10128
+ }
10129
+ interface SkillsByHarnessEntry {
10130
+ adapter: string;
10131
+ target?: {
10132
+ format: string;
10133
+ dir: string;
10134
+ unit?: string;
10135
+ };
10136
+ installed: string[];
10137
+ native: string[];
10138
+ spawnOption: boolean;
10139
+ }
10140
+ interface CapabilitiesInventorySkills {
10141
+ packs: SkillPackInventoryEntry[];
10142
+ byHarness: SkillsByHarnessEntry[];
10143
+ defaults: {
10144
+ global: string[];
10145
+ byAdapter: Record<string, string[]>;
10146
+ };
10147
+ error?: string;
10148
+ }
10149
+ interface CapabilitiesInventory {
10150
+ mcp: CapabilitiesInventoryMcp;
10151
+ skills: CapabilitiesInventorySkills;
10152
+ }
10153
+
9390
10154
  /**
9391
10155
  * Dynamic MCP-app panels for installed `@agentproto/app-kit` apps that ship
9392
10156
  * a `ui` block (`defineApp({ ui: { html, title?, tools?, csp? } })`,
@@ -9648,6 +10412,9 @@ interface PrStatusSnapshot {
9648
10412
  name: string;
9649
10413
  conclusion: string | null;
9650
10414
  }>;
10415
+ /** The PR's head commit at fetch time — absent from snapshots taken
10416
+ * before this field existed. */
10417
+ headSha?: string;
9651
10418
  }
9652
10419
  /** The mutable annotations sidecar of a ledger entry. */
9653
10420
  interface LedgerAnnotations {
@@ -9660,6 +10427,13 @@ interface ReviewLedgerFilter {
9660
10427
  rangeSha?: string;
9661
10428
  binding?: string;
9662
10429
  manifestSha?: string;
10430
+ /** Keep only entries whose `attestation.requester.sessionId` is one of
10431
+ * these — backs `review_ledger({requesterSessionId, subtree})`: a single
10432
+ * id, or (with `subtree: true`) that session's whole subtree, resolved by
10433
+ * the caller before reaching the ledger. Answered from an in-memory
10434
+ * `requesterSessionId -> ledger keys` index (built lazily on first use,
10435
+ * kept current on every `put()`), never a full re-scan. */
10436
+ requesterSessionIds?: readonly string[];
9663
10437
  }
9664
10438
  interface ReviewLedger {
9665
10439
  /** Absolute root directory of the ledger. */
@@ -9842,12 +10616,27 @@ interface ReviewRun {
9842
10616
  /** Where the attestation was written in the ledger. */
9843
10617
  ledgerPath?: string;
9844
10618
  error?: string;
10619
+ /** The session that requested this run (`input.requesterSessionId ??
10620
+ * input.parentSessionId`), when known — mirrors
10621
+ * `Attestation.requester.sessionId` for a run that hasn't (or never will)
10622
+ * reach the ledger, so `review_ledger({includeRunning: true})` and the
10623
+ * `session_tree` badge (WP-review-panel) can attribute an in-flight/
10624
+ * cancelled/failed run to its requester without waiting for an
10625
+ * attestation. */
10626
+ requesterSessionId?: string;
9845
10627
  }
9846
10628
  interface ReviewRunner {
9847
10629
  start(input: ReviewRunInput): ReviewRun;
9848
10630
  status(runId: string): Promise<ReviewRun | undefined>;
9849
10631
  wait(runId: string): Promise<ReviewRun | undefined>;
9850
10632
  cancel(runId: string): boolean;
10633
+ /** Every run this daemon process has started, newest first — running runs
10634
+ * plus ones that settled (done/failed/cancelled) without a daemon
10635
+ * restart in between. Runs live in memory only for the process lifetime
10636
+ * (a restart loses this list, same as the rest of `runs` — the ATTESTATION
10637
+ * is what persists, in the ledger). Backs `review_ledger({includeRunning:
10638
+ * true})` and the `session_tree` review badge. */
10639
+ list(): ReviewRun[];
9851
10640
  readonly ledger: ReviewLedger;
9852
10641
  }
9853
10642
  interface CreateReviewRunnerOptions {
@@ -9857,6 +10646,14 @@ interface CreateReviewRunnerOptions {
9857
10646
  reviewers?: ReviewerSessionHost;
9858
10647
  /** Attestor identity. Default: `agentproto-runtime@<hostname>`. */
9859
10648
  daemonId?: string;
10649
+ /** Called once a run with a `requesterSessionId` reaches `done` — never
10650
+ * for `failed`/`cancelled` (no verdict to report). Display-only: the
10651
+ * daemon writes `text` into the requester's transcript as a `notice`
10652
+ * (see `sessions.ts`'s `recordNotice`) so `session_story`/`live_session`
10653
+ * show it — this never enqueues a prompt, touches the inbox, or starts a
10654
+ * turn. Fire-and-forget from the runner's perspective; a throw here is
10655
+ * swallowed. */
10656
+ notifyRequester?: (sessionId: string, text: string) => void;
9860
10657
  }
9861
10658
  /** Placeholder values the daemon binds for every review. `{changed}` is a
9862
10659
  * shell-quoted turbo filter for the packages changed since the range base
@@ -10086,6 +10883,12 @@ interface RegisterReviewToolsOptions {
10086
10883
  /** `gh` runner for `review_pr` — injectable for tests. Default: the real
10087
10884
  * `gh` on the daemon's PATH. */
10088
10885
  gh?: GhRunner;
10886
+ /** Resolve a session id to its subtree (itself + every session it
10887
+ * transitively spawned) — backs `review_ledger({requesterSessionId,
10888
+ * subtree: true})`. Omitted ⇒ `subtree` is ignored (scoped to the exact
10889
+ * id only), so a caller without sessions-registry access still works,
10890
+ * just without the subtree expansion. */
10891
+ resolveSubtree?: (sessionId: string) => readonly string[];
10089
10892
  }
10090
10893
  declare function registerReviewTools(server: McpServer, opts: RegisterReviewToolsOptions): void;
10091
10894
 
@@ -11085,6 +11888,43 @@ declare function enrichRollupWithProviderQuota(rollup: UsageRollup, window: stri
11085
11888
  * such lines; skips any malformed or non-`usage_snapshot` line. Never throws. */
11086
11889
  declare function readUsageSnapshots(sessionId: string, baseDir?: string): Promise<UsageSnapshotRecord[]>;
11087
11890
 
11891
+ /**
11892
+ * MCP surface for the sentinel primitive (AIP-60 §6): `sentinel_watch`,
11893
+ * `sentinel_list`, `sentinel_unwatch`, `sentinel_poll_now`.
11894
+ *
11895
+ * `sentinel_watch`'s `sessionId` defaults to the calling session — same
11896
+ * "trusted `?callerSessionId=`" pattern `message-tools.ts` uses — so a
11897
+ * session watching its own PR doesn't need to know its own id.
11898
+ *
11899
+ * Provider auto-select isn't implemented yet (design §6): `provider`
11900
+ * defaults to `local-gh`, the only built-in that ships as of this step.
11901
+ *
11902
+ * `createSentinelWatch`/`cancelSentinelWatch`/`sentinelView` are exported so
11903
+ * `http-server.ts`'s `POST/GET/DELETE /sentinels` routes (the daemon-HTTP
11904
+ * surface `agentproto sentinel` talks to) share the exact same
11905
+ * validation/sugar-resolution logic as this MCP surface — same pattern as
11906
+ * `TunnelRegistry` backing both `tunnel_create` and `POST /tunnels`, except
11907
+ * here the shared logic is a plain function rather than a stateful registry
11908
+ * method, since sentinel creation also needs the provider handle.
11909
+ */
11910
+
11911
+ /** Compact wire view of a sentinel for tool/HTTP results — the internal-only
11912
+ * fields (`handle`, `seen`, `terminalSubjects`) never leave the daemon. */
11913
+ interface SentinelView {
11914
+ id: string;
11915
+ provider: string;
11916
+ status: SentinelStatus;
11917
+ match: SentinelMatchClause[];
11918
+ until: SentinelUntil;
11919
+ target: SentinelTarget;
11920
+ group?: string;
11921
+ label?: string;
11922
+ createdTs: number;
11923
+ eventCount: number;
11924
+ lastEventTs?: number;
11925
+ lastError?: string;
11926
+ }
11927
+
11088
11928
  /**
11089
11929
  * Smart default for `features.llmEndpoint` (daemon-managed-gateway): an
11090
11930
  * operator who already configured a named endpoint, an upstream credential
@@ -11164,6 +12004,11 @@ interface Device {
11164
12004
  * a user can see, from EITHER daemon, which of their pairings/hosts grant
11165
12005
  * host control. */
11166
12006
  scope?: "host";
12007
+ /** Self-reported by a host at join time (SANDBOX-VISIBILITY-JOIN) — see
12008
+ * `HostRecord.provider`/`sandboxId`/`labels`. Absent for a client device. */
12009
+ provider?: string;
12010
+ sandboxId?: string;
12011
+ labels?: Record<string, string>;
11167
12012
  }
11168
12013
  interface DeviceRegistry {
11169
12014
  /** Every known device. Read-only. */
@@ -11184,6 +12029,10 @@ interface DeviceRegistry {
11184
12029
  name: string;
11185
12030
  rendezvousUrl: string;
11186
12031
  }>;
12032
+ /** Forward one HTTP request to a HOST device on demand (delegates to
12033
+ * `HostRegistry.forwardHttp`) — the basis for `device_sessions`. Rejects
12034
+ * if no `HostRegistry` was wired, or if `idOrName` doesn't match a host. */
12035
+ forwardHttp(idOrName: string, req: ForwardHttpRequest): Promise<ForwardHttpResponse>;
11187
12036
  }
11188
12037
  declare function createDeviceRegistry(pairing: PairingRegistry, hosts?: HostRegistry): DeviceRegistry;
11189
12038
 
@@ -11207,6 +12056,9 @@ declare function createDeviceRegistry(pairing: PairingRegistry, hosts?: HostRegi
11207
12056
 
11208
12057
  interface RegisterDeviceToolsOptions {
11209
12058
  registry: DeviceRegistry;
12059
+ /** Optional (SANDBOX-VISIBILITY-JOIN) — when wired, registers
12060
+ * `join_token_create`/`join_token_list`/`join_token_revoke`. */
12061
+ joinTokens?: JoinTokenRegistry;
11210
12062
  }
11211
12063
  declare function registerDeviceTools(server: McpServer, opts: RegisterDeviceToolsOptions): void;
11212
12064
 
@@ -12475,11 +13327,27 @@ interface CreateGatewayOptions {
12475
13327
  * autoconnect or tear down on shutdown.
12476
13328
  */
12477
13329
  hostRegistry?: HostRegistry;
13330
+ /**
13331
+ * Optional JOIN TOKEN registry (see `createJoinTokenRegistry`,
13332
+ * SANDBOX-VISIBILITY-JOIN). When wired alongside `pairingRegistry` +
13333
+ * `hostRegistry`, the gateway mounts `POST/GET /devices/join-tokens` +
13334
+ * `DELETE /devices/join-tokens/:id`, the `join_token_create/list/revoke`
13335
+ * MCP tools, and `device_sessions`/`GET /devices/:id/sessions[/…/output]`.
13336
+ * Autoconnect is the caller's to start (after this returns), same as
13337
+ * `pairingRegistry`.
13338
+ */
13339
+ joinTokens?: JoinTokenRegistry;
12478
13340
  /** Enable the local LLM Endpoint proxy sidecar (route registration,
12479
13341
  * MCP tools, child-process lifecycle). Default false — the endpoint is
12480
13342
  * an opt-in feature; when off, the `llm-endpoint` custom route is not
12481
13343
  * registered and the `llm_endpoint_*` MCP tools are not exposed. */
12482
13344
  llmEndpoint?: boolean;
13345
+ /** Mirrors `config.features.deviceInferenceShare` (DEVICES-PLAN item 1).
13346
+ * Default false. Only meaningful alongside `llmEndpoint: true` — passed
13347
+ * straight through to `startHttpServer`'s `deviceInferenceShare`, which
13348
+ * gates `/device-inference/*` (see http-server.ts's `HOST_SCOPE_HEADER`
13349
+ * doc comment for the full scope-gate story). */
13350
+ deviceInferenceShare?: boolean;
12483
13351
  }
12484
13352
  /**
12485
13353
  * Default always-on set when `deferredTools` is enabled without an
@@ -12490,10 +13358,10 @@ interface CreateGatewayOptions {
12490
13358
  * `toolPolicy.delegation: "deny"`, so those two names are simply absent
12491
13359
  * from a deny-role's registry and cost nothing) but still needs to report
12492
13360
  * back to its parent (`message_parent`), coordinate over the task ledger
12493
- * (`task_*`), and observe its own session/turn state
12494
- * (`session_context_status` alongside the pre-existing `session_list` /
12495
- * `session_monitor` / `session_events_poll`). Everything else (fs_*,
12496
- * directory_*, command_*, remote_*, browser_*, terminal_*, mcp_*,
13361
+ * (`task_*`), and observe its own session/turn state and capabilities
13362
+ * (`session_context_status` / `session_capabilities` alongside the
13363
+ * pre-existing `session_list` / `session_monitor` / `session_events_poll`).
13364
+ * Everything else (fs_*, directory_*, command_*, remote_*, browser_*, terminal_*, mcp_*,
12497
13365
  * routine_*, workflow_*, policy_*, inbound_watcher_*, session_tree,
12498
13366
  * agent_export, adapter_list, every `app_ui_*` panel tool, ...) starts
12499
13367
  * deferred — reachable via `tool_search`, never removed from `tools/call`.
@@ -12520,6 +13388,11 @@ interface GatewayHandle {
12520
13388
  * (DEVICES-PLAN PR-C). Undefined otherwise. No autoconnect/shutdown to
12521
13389
  * call on it — see that option's doc comment. */
12522
13390
  hosts?: HostRegistry;
13391
+ /** JOIN TOKEN registry, when one was wired via
13392
+ * `CreateGatewayOptions.joinTokens` (SANDBOX-VISIBILITY-JOIN). Undefined
13393
+ * otherwise. Exposed so the CLI can `startAutoconnect()` after boot and
13394
+ * `shutdown()` it, same as `pairing`. */
13395
+ joinTokens?: JoinTokenRegistry;
12523
13396
  /** Per-boot bearer token required on mutating /sessions/* routes
12524
13397
  * + WS PTY upgrades. Exposed so an embedding host (e.g. the CLI
12525
13398
  * shell that hosts the gateway in-process) can pass it to child
@@ -12570,4 +13443,4 @@ interface GatewayHandle {
12570
13443
  */
12571
13444
  declare function createGateway(opts: CreateGatewayOptions): Promise<GatewayHandle>;
12572
13445
 
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 };
13446
+ 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 CapabilitiesInventory, type CapabilitiesInventoryMcp, type CapabilitiesInventorySkills, 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 CreateJoinTokenInput, type CreateOfferInput, type CreateReviewRunnerOptions, type CreatedJoinToken, 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, HOST_SCOPE_HEADER, type HarnessPreset, type HarnessPresetValidationDeps, HarnessPresetValidationError, type HarnessPresetsFile, type HermesNativeRef, type HostJoinMeta, 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, JOIN_TOKENS_VERSION, type JoinTokenRecord, type JoinTokenRegistry, type JoinTokenRegistryDeps, 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, type SentinelMatchClause, type SentinelSpec, type SentinelStatus, type SentinelTarget, type SentinelUntil, type SentinelView, 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 SkillPackInventoryEntry, type SkillsByHarnessEntry, 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, createJoinTokenRegistry, 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 };