@agentproto/runtime 2.6.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -6,15 +6,15 @@ import { AcpMcpServer, AcpPermissionResolution } from '@agentproto/acp';
6
6
  import { SessionMode } from '@agentproto/acp/client';
7
7
  export { SessionMode } from '@agentproto/acp/client';
8
8
  import { ChildProcess } from 'node:child_process';
9
- import { R as RouteSpec, S as SessionConfig, P as Posture, C as ContextProfile, E as EffortLevel, A as AuthMethod, a as CanonicalPosture } from './session-config-DIf6wYYP.js';
10
- export { D as DeclaredAdapterMode, c as composeMode, d as decomposeMode } from './session-config-DIf6wYYP.js';
9
+ import { R as RouteSpec, S as SessionConfig, P as Posture, C as ContextProfile, E as EffortLevel, A as AuthMethod, a as CanonicalPosture } from './session-config-DbWP9RRj.js';
10
+ export { D as DeclaredAdapterMode, c as composeMode, d as decomposeMode } from './session-config-DbWP9RRj.js';
11
11
  import { CostBudget, AuthProfile, CostBudgetScope } from '@agentproto/auth';
12
12
  export { CostBudget, CostBudgetScope } from '@agentproto/auth';
13
- import { R as ResolvedContextContinuityPolicy } from './context-continuity-B9n0t0v-.js';
13
+ import { R as ResolvedContextContinuityPolicy } from './context-continuity-ib9_bVYM.js';
14
14
  import { SandboxMode } from '@agentproto/command-sandbox';
15
15
  import { AdapterHandle, SetupField, AdapterResolver, AdapterLister, HarnessCapabilities, AdapterEntry } from '@agentproto/provider-kit';
16
- import { R as ResolvedAuthSpec, D as DeclaredAdapterOption, A as AdapterAuthDescriptor } from './spawn-defaults-CYJoeHeO.js';
17
- export { a as AuthEcho, b as AuthResolutionError, C as CLAUDE_CODE_OAUTH_SOURCE, c as CredentialSource, d as DefaultsAdapterAuthConfig, e as DefaultsAdapterConfig, f as ResolveSubscriptionCredentialInput, g as ResolvedSpawnAuthMaterial, h as ResolvedSpawnDefaults, S as SpawnDefaultsConfig, i as SubscriptionCredentialResolution, j as SubscriptionSourceError, k as credentialFingerprint, n as normalizeSkillsOption, r as resolveAuthSpec, l as resolveSpawnDefaults, m as resolveSubscriptionCredential } from './spawn-defaults-CYJoeHeO.js';
16
+ import { R as ResolvedAuthSpec, D as DeclaredAdapterOption, A as AdapterAuthDescriptor } from './spawn-defaults-DVgmfxWo.js';
17
+ export { a as AuthEcho, b as AuthResolutionError, C as CLAUDE_CODE_OAUTH_SOURCE, c as CredentialSource, d as DefaultsAdapterAuthConfig, e as DefaultsAdapterConfig, f as ResolveSubscriptionCredentialInput, g as ResolvedSpawnAuthMaterial, h as ResolvedSpawnDefaults, S as SpawnDefaultsConfig, i as SubscriptionCredentialResolution, j as SubscriptionSourceError, k as credentialFingerprint, n as normalizeSkillsOption, r as resolveAuthSpec, l as resolveSpawnDefaults, m as resolveSubscriptionCredential } from './spawn-defaults-DVgmfxWo.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';
@@ -536,6 +536,16 @@ interface ToolCallRecord {
536
536
  exitCode?: number;
537
537
  isError?: boolean;
538
538
  durationMs?: number;
539
+ /** The PR this call CREATED, when the call was a successful shell-shaped
540
+ * `gh pr create` (detected at write time via `detectShellPrCreate` in
541
+ * pr-provenance.ts, from the command string + the call's result text).
542
+ * This is the exact-attribution source the PR-provenance reconciler
543
+ * prefers over branch→PR guessing: the session whose events.jsonl holds
544
+ * this record IS the session that opened the PR, no matter what branch
545
+ * its cwd sits on by the time the turn ends. Absent for every other
546
+ * call. */
547
+ createdPrUrl?: string;
548
+ createdPrNumber?: number;
539
549
  ts: string;
540
550
  }
541
551
 
@@ -1661,7 +1671,7 @@ declare function createTaskLedger(opts: {
1661
1671
  * and session_monitor MCP tool (long-poll multiplexed).
1662
1672
  */
1663
1673
 
1664
- type SessionEventType = "session:turn-end" | "session:awaiting-input" | "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:resumed" | "session:spawned" | "session:command-done" | "session:model-changed" | "session:config-changed" | "session:renamed" | "policy:passed" | "policy:failed" | "policy:commit-ready" | "policy:committed" | "cron:fired" | "cron:succeeded" | "cron:failed" | "activity:changed" | "task:changed";
1674
+ type SessionEventType = "session:turn-end" | "session:awaiting-input" | "session:awaiting-input-flagged" | "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:resumed" | "session:spawned" | "session:command-done" | "session:model-changed" | "session:config-changed" | "session:renamed" | "session:pinned-changed" | "policy:passed" | "policy:failed" | "policy:commit-ready" | "policy:committed" | "cron:fired" | "cron:succeeded" | "cron:failed" | "activity:changed" | "task:changed";
1665
1675
  /**
1666
1676
  * Fixed severity vocabulary for a judge-gate finding (WP-D). Deliberately
1667
1677
  * small and fixed so `policy_status` output is comparable across different
@@ -1743,6 +1753,39 @@ interface SessionAwaitingInputEvent {
1743
1753
  ts: string;
1744
1754
  question?: SessionAwaitingQuestion;
1745
1755
  }
1756
+ /**
1757
+ * Emitted by `SessionsRegistry.flagAwaitingInput` (the `session_flag_status`
1758
+ * MCP verb) when something EXTERNAL — a human, another agent, the future
1759
+ * session watchdog — manually corrects a session's `awaitingInput`/
1760
+ * `awaitingQuestion` classification, overriding (or confirming) what the
1761
+ * internal heuristic (`deriveHeuristicQuestion`) or a driver-reported
1762
+ * `agent-prompt` last set. Distinct from `session:awaiting-input` (which only
1763
+ * ever fires when the flag flips TRUE, from the daemon's own turn-end
1764
+ * detection): this fires for BOTH directions of a manual override, always
1765
+ * carries the mandatory `reason` the caller gave (audit trail — this is the
1766
+ * one write path for this field with no automatic sensor behind it), and
1767
+ * `awaitingInput` states the value the override just set rather than being
1768
+ * implied by the event firing at all. Same bus distribution as every other
1769
+ * lifecycle event (`session_events_poll`, the webhook notifier, the routine
1770
+ * engine, `session_monitor`).
1771
+ */
1772
+ interface SessionAwaitingInputFlaggedEvent {
1773
+ type: "session:awaiting-input-flagged";
1774
+ sessionId: string;
1775
+ awaitingInput: boolean;
1776
+ /** Required justification the caller passed to `session_flag_status` —
1777
+ * why this override was made. */
1778
+ reason: string;
1779
+ label?: string;
1780
+ ts: string;
1781
+ /** Present only when `awaitingInput:true` and a `question` was attached —
1782
+ * mirrors the descriptor's `awaitingQuestion` after the override, same
1783
+ * shape as every other site that sets it (`source: "structured"`, since a
1784
+ * manual override is at least as authoritative as a driver-reported
1785
+ * prompt). Absent when `awaitingInput:false` (the override always clears
1786
+ * any prior question in that case) or when no `question` was given. */
1787
+ question?: SessionAwaitingQuestion;
1788
+ }
1746
1789
  /**
1747
1790
  * Emitted when a permission-hold session parks a `session/request_permission`
1748
1791
  * (see the pending-permissions inbox in sessions.ts). `permissionId` is the
@@ -1862,11 +1905,12 @@ interface SessionStallClearedEvent {
1862
1905
  * is the counter AFTER the attach (read back from the registry, so a
1863
1906
  * concurrent waiter is already reflected). `watcherSessionId` names the
1864
1907
  * supervising session when the wait was initiated by one (the scoped
1865
- * orchestrator's `callerScope.ownerSessionId`); absent for an anonymous
1866
- * CLI/HTTP waiter. Same bus distribution as every other lifecycle event
1867
- * (`session_events_poll`, the webhook notifier, the routine engine,
1868
- * `session_monitor`). Paired with `session:watcher-detached` when the wait
1869
- * resolves or times out.
1908
+ * orchestrator's `callerScope.ownerSessionId`); `label` is that session's
1909
+ * label/title, resolved once at attach time (a best-effort snapshot, not
1910
+ * live). Both absent for an anonymous CLI/HTTP waiter. Same bus distribution
1911
+ * as every other lifecycle event (`session_events_poll`, the webhook
1912
+ * notifier, the routine engine, `session_monitor`). Paired with
1913
+ * `session:watcher-detached` when the wait resolves or times out.
1870
1914
  */
1871
1915
  interface SessionWatcherAttachedEvent {
1872
1916
  type: "session:watcher-attached";
@@ -2064,6 +2108,24 @@ interface SessionRenamedEvent {
2064
2108
  renamedByUser?: boolean;
2065
2109
  ts: string;
2066
2110
  }
2111
+ /**
2112
+ * Emitted when an operator pins or unpins a session for list visibility
2113
+ * (`POST /sessions/:id/pin`, the `session_set_pinned` MCP verb). Like
2114
+ * `session:renamed`, this is NOT a `SessionConfig` axis — pinning never
2115
+ * touches the live agent, the idle-reaper, or any notification path, it's
2116
+ * purely a `SessionDescriptor.pinned` display/sort flag — so it rides its
2117
+ * own event rather than `session:config-changed`. `pinned` carries the value
2118
+ * now on the descriptor. Same bus distribution as every other lifecycle event
2119
+ * (`session_events_poll`, the webhook notifier, the routine engine), which is
2120
+ * how a live UI (the VS Code sessions webview) learns to resort its list
2121
+ * without waiting for its next snapshot poll.
2122
+ */
2123
+ interface SessionPinnedEvent {
2124
+ type: "session:pinned-changed";
2125
+ sessionId: string;
2126
+ pinned: boolean;
2127
+ ts: string;
2128
+ }
2067
2129
  /**
2068
2130
  * Emitted when a session is first registered in the registry (WP-R3) — both
2069
2131
  * the agent-cli spawn path (`spawnAgent`) and the terminal path (`spawnPty`).
@@ -2186,7 +2248,7 @@ interface TaskChangedEvent {
2186
2248
  sessionId?: string;
2187
2249
  ts: string;
2188
2250
  }
2189
- type SessionEvent = SessionTurnEndEvent | SessionAwaitingInputEvent | SessionPermissionRequestEvent | SessionPermissionResolvedEvent | SessionExitedEvent | SessionReapedEvent | SessionStalledEvent | SessionStallClearedEvent | SessionWatcherAttachedEvent | SessionWatcherDetachedEvent | SessionBgTasksParkedEvent | SessionBgTasksClearedEvent | SessionResumedEvent | SessionSpawnedEvent | SessionCommandDoneEvent | SessionModelChangedEvent | SessionConfigChangedEvent | SessionRenamedEvent | PolicyPassedEvent | PolicyFailedEvent | PolicyCommitReadyEvent | PolicyCommittedEvent | CronFiredEvent | CronSucceededEvent | CronFailedEvent | ActivityChangedEvent | TaskChangedEvent;
2251
+ type SessionEvent = SessionTurnEndEvent | SessionAwaitingInputEvent | SessionAwaitingInputFlaggedEvent | SessionPermissionRequestEvent | SessionPermissionResolvedEvent | SessionExitedEvent | SessionReapedEvent | SessionStalledEvent | SessionStallClearedEvent | SessionWatcherAttachedEvent | SessionWatcherDetachedEvent | SessionBgTasksParkedEvent | SessionBgTasksClearedEvent | SessionResumedEvent | SessionSpawnedEvent | SessionCommandDoneEvent | SessionModelChangedEvent | SessionConfigChangedEvent | SessionRenamedEvent | SessionPinnedEvent | PolicyPassedEvent | PolicyFailedEvent | PolicyCommitReadyEvent | PolicyCommittedEvent | CronFiredEvent | CronSucceededEvent | CronFailedEvent | ActivityChangedEvent | TaskChangedEvent;
2190
2252
  interface SessionEventBus {
2191
2253
  emit(ev: SessionEvent): void;
2192
2254
  /** Subscribe to a specific event type. Returns an unsubscribe fn. */
@@ -3613,6 +3675,14 @@ type AgentAdapterResolver = (slug: string) => Promise<{
3613
3675
  startSession(opts: {
3614
3676
  cwd: string;
3615
3677
  resumeSessionId?: string;
3678
+ /** Persistent isolated-config location for the adapter (claude-code's
3679
+ * `CLAUDE_CONFIG_DIR`) — forwarded to the driver's
3680
+ * `start({ configDir })`. The daemon keys it per session lineage
3681
+ * (`SessionDescriptor.adapterConfigDir`) so the provider's own
3682
+ * conversation store survives adapter respawns and `resumeSessionId`
3683
+ * can restore full context instead of falling back to a digest.
3684
+ * Adapters that don't isolate a config dir ignore it. */
3685
+ configDir?: string;
3616
3686
  /**
3617
3687
  * Manifest-declared mode id forwarded from `agent_start` (AIP-45
3618
3688
  * `AgentCliHandle.modes` — e.g. claude-code's `plan` /
@@ -3849,6 +3919,12 @@ interface AdapterInstallResult {
3849
3919
  * post-install re-list failed (the install itself may still have
3850
3920
  * succeeded — read `ok`). */
3851
3921
  status?: "supported" | "available" | "ready" | "unresolvable";
3922
+ /** True when the install failed ONLY because a manifest setup step
3923
+ * declares `interactive: true` and this process has no TTY to run it
3924
+ * in (e.g. openclaw's `onboard --install-daemon` TUI). The remedy is a
3925
+ * real terminal — `agentproto setup <slug>` — which a UI can offer
3926
+ * directly as a PTY terminal session. */
3927
+ needsInteractiveSetup?: boolean;
3852
3928
  }
3853
3929
  /**
3854
3930
  * Installs an agent CLI adapter by slug and reports the outcome. Wired by
@@ -4214,6 +4290,48 @@ interface SessionAccessProfileEcho {
4214
4290
  endpoint: string;
4215
4291
  method: AuthMethod;
4216
4292
  }
4293
+ /**
4294
+ * One live waiter behind a session's {@link SessionDescriptor.watchers} count
4295
+ * (#session-visibility). Captured at `monitorSessionWait` attach time and held
4296
+ * only for the life of that wait — never persisted, never updated in place
4297
+ * (a snapshot of the waiter's label at attach time, not a live mirror of it).
4298
+ */
4299
+ interface SessionWatcherInfo {
4300
+ /** The waiting session's id, when this wait was initiated by an agent
4301
+ * session via the orchestrator scope (`callerScope.ownerSessionId`).
4302
+ * Absent for an anonymous CLI/HTTP waiter — it still counts toward
4303
+ * `watchers`, it just can't be named. */
4304
+ watcherSessionId?: string;
4305
+ /** Best-effort label/title for `watcherSessionId`, resolved once at attach
4306
+ * time. A later rename of the watching session is not reflected. */
4307
+ watcherLabel?: string;
4308
+ /** What this wait is blocking on — mirrors `session_monitor`'s `event`
4309
+ * parameter (`SessionWaitEvent` in orchestration-tools.ts; kept as a plain
4310
+ * string here so the registry doesn't depend on the tool-layer type). */
4311
+ event: string;
4312
+ /** The wait's configured timeout, ms — absent for the MCP tool's default. */
4313
+ timeoutMs?: number;
4314
+ /** ISO 8601 timestamp this waiter attached. */
4315
+ since: string;
4316
+ }
4317
+ /** One prompt waiting in `SessionDescriptor.promptQueue` — see that
4318
+ * field's doc for the FIFO/force ordering contract. */
4319
+ interface QueuedPrompt {
4320
+ /** Stable id, assigned at queue time (by the HTTP layer for
4321
+ * `POST /sessions/:id/prompt`'s `queue` arm so the caller can echo it
4322
+ * straight back in the response, or minted here for any other
4323
+ * caller). Used by `removeQueuedPrompt` to cancel this one item
4324
+ * before it dispatches. */
4325
+ id: string;
4326
+ /** Same shape `runAgentTurn`'s `message` parameter accepts — a raw
4327
+ * string or an ACP content block/array. */
4328
+ message: unknown;
4329
+ /** ISO 8601 timestamp this item was queued. */
4330
+ queuedAt: string;
4331
+ /** Same as `enqueuePrompt`'s `opts.source` — carried through to the
4332
+ * turn this item eventually becomes. */
4333
+ source?: string;
4334
+ }
4217
4335
  interface SessionDescriptor {
4218
4336
  id: string;
4219
4337
  kind: SessionKind;
@@ -4303,6 +4421,15 @@ interface SessionDescriptor {
4303
4421
  * daemon restart). 0/absent ⇒ nothing is watching. Lets a UI mark a
4304
4422
  * session "supervised — notify on turn-end". */
4305
4423
  watchers?: number;
4424
+ /** Per-waiter detail behind the {@link watchers} count — who is blocked on
4425
+ * this session and what they're blocked on. Same lifetime/source as
4426
+ * `watchers` (stamped at read time from the registry's live waiter list,
4427
+ * never persisted); empty/absent ⇒ nothing is watching. An anonymous
4428
+ * CLI/HTTP waiter (no `callerScope`) carries no `watcherSessionId` — it
4429
+ * still counts, it just can't be named. Lets a UI answer "who is watching
4430
+ * this, and what are they waiting for" without guessing from the bare
4431
+ * count. */
4432
+ watcherDetails?: readonly SessionWatcherInfo[];
4306
4433
  /** Count of DESCENDANT sessions (children, grandchildren, …) currently
4307
4434
  * mid-turn, derived at read time from the `parentSessionId` lineage
4308
4435
  * (#session-visibility, subtree rollup). Lets a UI show an idle parent that
@@ -4352,6 +4479,24 @@ interface SessionDescriptor {
4352
4479
  * session exit. Emits `session:bg-tasks-parked` /
4353
4480
  * `session:bg-tasks-cleared`. */
4354
4481
  pendingBgTasks?: number;
4482
+ /** ISO 8601 timestamp of the last turn that ended because the ADAPTER
4483
+ * ITSELF reported a failure — `runAgentTurn` observed a `turn-end` event
4484
+ * with `reason:"error"` (session-event-bus.ts's `SessionTurnEndEvent`,
4485
+ * e.g. a refused/errored ACP `stopReason` — see adapters/mastra-agent's
4486
+ * host) — as opposed to a thrown/rejected adapter stream, which flips
4487
+ * `status` to `"error"` directly (a genuinely terminal row the existing
4488
+ * crash/error handling already catches). This is the twin gap for a
4489
+ * turn that fails IN-BAND while the process stays alive: `status` stays
4490
+ * `"running"`, `lastError` stays unset (reserved for `markCrashed`), and
4491
+ * nothing else distinguishes the session from one that simply finished
4492
+ * a clean turn and is idle. Folded into the same "stalled" activity/UI
4493
+ * treatment a mid-turn stall gets (see sessionsTree.logic.ts's
4494
+ * `activityFor`) rather than a new state, on the theory that both mean
4495
+ * "the last thing this session did needs a look, not a re-prompt on
4496
+ * faith". Stamped at turn-end when `turnEndReason === "error"`; cleared
4497
+ * the moment a LATER turn completes without one (same reset shape as
4498
+ * `resumeAttempts`/`restartAttempts`). Detection + signal only. */
4499
+ lastTurnErroredAt?: string;
4355
4500
  /** DERIVED, read-time only (never persisted — stripped by `snapshotRows`,
4356
4501
  * stamped by `stampInterrupted` in list()/get()/findByIdOrName). True when
4357
4502
  * this session died with a turn in flight under a daemon restart —
@@ -4478,6 +4623,17 @@ interface SessionDescriptor {
4478
4623
  * `session_set_keepalive` (`registry.setKeepAlive`). Absent (not `false`)
4479
4624
  * for every session that hasn't opted in. */
4480
4625
  keepAlive?: boolean;
4626
+ /** Server-persisted, list-visibility-only favorite flag — set/cleared via
4627
+ * `session_set_pinned` (`registry.setPinned`) or `POST /sessions/:id/pin`.
4628
+ * Purely a sort/display concern: pinned sessions sort to the top of the
4629
+ * session list (CLI table, VS Code webview's dedicated "Pinned" group).
4630
+ * Deliberately orthogonal to — and must NEVER be confused with —
4631
+ * `keepAlive` (idle-reaper exemption, operational), the VS Code
4632
+ * extension's client-side-only "watch" eye (toast notifications, never
4633
+ * persisted here), or `watchers` (live supervisor wait count). No side
4634
+ * effects on reaping, notifications, or anything else. Absent (not
4635
+ * `false`) for every session that hasn't been pinned. */
4636
+ pinned?: boolean;
4481
4637
  /** True when the session was spawned under a real PTY (node-pty)
4482
4638
  * instead of `child_process.spawn`. PTY sessions carry raw ANSI
4483
4639
  * bytes (alt-screen, key bindings, colors); attach goes through
@@ -4640,6 +4796,17 @@ interface SessionDescriptor {
4640
4796
  * via sessions.json so `restart` can pass it as `resumeSessionId`
4641
4797
  * and reattach to the prior conversation history. */
4642
4798
  adapterSessionId?: string;
4799
+ /** Persistent isolated-config dir handed to the adapter at spawn time
4800
+ * (`startSession({ configDir })` → claude-code's `CLAUDE_CONFIG_DIR`).
4801
+ * Keyed by the FIRST session id in a restart lineage
4802
+ * (`adapterConfigDirFor`) and carried forward verbatim across
4803
+ * restarts/lazy resumes — the provider's own conversation store lives
4804
+ * inside it, so reusing the SAME dir is what lets `resumeSessionId`
4805
+ * restore full context after the adapter process died (before this,
4806
+ * each respawn minted a fresh temp dir and native resume always
4807
+ * degraded to the daemon-transcript digest). Absent on legacy rows and
4808
+ * adapters that don't isolate a config dir. */
4809
+ adapterConfigDir?: string;
4643
4810
  /** MCP servers mounted into the agent's session at spawn time
4644
4811
  * (orchestrator WP1). Persisted so the resume/re-spawn path can
4645
4812
  * re-mount the same host-chosen toolset instead of resuming a
@@ -4739,6 +4906,23 @@ interface SessionDescriptor {
4739
4906
  * message (`runAgentTurn`) — so delivery survives a busy parent without
4740
4907
  * ever cancelling its in-flight work. Absent when nothing is queued. */
4741
4908
  pendingChildCrashNotices?: string[];
4909
+ /** FIFO of prompts that arrived while this session was mid-turn and
4910
+ * asked to be QUEUED rather than rejected (`enqueuePrompt`'s
4911
+ * `opts.queue` arm — see its doc comment). Index 0 is next to
4912
+ * dispatch. A plain queued item is appended to the back; a
4913
+ * `force`-queued one is inserted at the front, jumping everything
4914
+ * already waiting WITHOUT cancelling the live turn (that's what
4915
+ * `interrupt` is for). Drained one item at a time, in this order, by
4916
+ * `dispatchQueuedPrompt` at the end of every `runAgentTurn` — never
4917
+ * by the caller that queued it, which already returned. Always
4918
+ * replaced with a NEW array reference on every mutation (push, drain,
4919
+ * removal), never mutated in place — the VS Code webview's
4920
+ * `sessionDescriptorsEqual` diff is a shallow `!==` per field, so an
4921
+ * in-place `.shift()` here would silently stop propagating queue
4922
+ * changes to the transcript panel. `[]` (not absent) once anything
4923
+ * has ever been queued, matching `pendingChildCrashNotices`'s
4924
+ * convention above. */
4925
+ promptQueue?: QueuedPrompt[];
4742
4926
  /** Best-effort context-handoff digest (Fix D), stashed on a descriptor
4743
4927
  * whose resume degraded to a BLANK spawn — the adapter's own
4744
4928
  * conversation store was missing, or the adapter declared `resumable:
@@ -4893,6 +5077,7 @@ interface SessionSummary {
4893
5077
  activitySummary?: SessionActivitySummary;
4894
5078
  archived?: boolean;
4895
5079
  keepAlive?: boolean;
5080
+ pinned?: boolean;
4896
5081
  pty?: boolean;
4897
5082
  name?: string;
4898
5083
  argv?: readonly string[];
@@ -4919,6 +5104,10 @@ interface SessionSummary {
4919
5104
  * `SessionDescriptor.pendingBgTasks`. Stamped at turn-end, cleared on the
4920
5105
  * next turn start / exit. */
4921
5106
  pendingBgTasks?: number;
5107
+ /** Adapter-reported turn-error marker — see
5108
+ * `SessionDescriptor.lastTurnErroredAt`. Stamped at turn-end, cleared on
5109
+ * the next turn that completes without one. */
5110
+ lastTurnErroredAt?: string;
4922
5111
  origin?: string;
4923
5112
  parentSessionId?: string;
4924
5113
  depth?: number;
@@ -5214,11 +5403,41 @@ interface SessionsRegistry {
5214
5403
  * `false` reproduces today's mid-turn rejection byte-for-byte. */
5215
5404
  /** `opts.source` on either prompt verb is the turn's provenance
5216
5405
  * (`agent:<sessionId>` when another session injected it — see
5217
- * `SessionObserver.recordPrompt`); recording-only, never behavioral. */
5406
+ * `SessionObserver.recordPrompt`); recording-only, never behavioral.
5407
+ *
5408
+ * `opts.queue` is the FIFO arm (additive, opt-in — omitted/false
5409
+ * reproduces the byte-for-byte busy rejection above, unchanged):
5410
+ * when the session is mid-turn AND `queue` is true, the prompt is
5411
+ * appended to `SessionDescriptor.promptQueue` instead of being
5412
+ * rejected, and this resolves immediately WITHOUT dispatching
5413
+ * anything — `dispatchQueuedPrompt` fires it once the current (and
5414
+ * any earlier-queued) turns finish. `opts.force` only matters
5415
+ * alongside `queue`: it inserts at the FRONT of the queue instead of
5416
+ * the back, jumping everything already waiting, but — unlike
5417
+ * `interrupt` — never touches the live turn. `opts.queueId` lets the
5418
+ * caller pin the queued item's id up front (the HTTP layer does this
5419
+ * so it can echo the id straight back in its response); omitted, one
5420
+ * is minted here. On an IDLE session `queue`/`force`/`queueId` are
5421
+ * no-ops — admission falls straight through to the normal dispatch
5422
+ * path below, identical to `queue` omitted. `interrupt` is checked
5423
+ * first: an `{interrupt: true, queue: true}` pair interrupts (never
5424
+ * queues) exactly like `interrupt` alone. */
5218
5425
  enqueuePrompt(id: string, message: unknown, opts?: {
5219
5426
  interrupt?: boolean;
5220
5427
  source?: string;
5428
+ queue?: boolean;
5429
+ force?: boolean;
5430
+ queueId?: string;
5221
5431
  }): Promise<void>;
5432
+ /** Cancel one not-yet-dispatched item in `SessionDescriptor.promptQueue`
5433
+ * by id — the composer's per-item "remove" action. Idempotent: an
5434
+ * unknown session or an id that's already gone (dispatched, already
5435
+ * removed, or never existed) resolves `{removed: false}` rather than
5436
+ * throwing, same shape as `interruptSession`'s no-op-is-not-an-error
5437
+ * contract. */
5438
+ removeQueuedPrompt(id: string, queueId: string): {
5439
+ removed: boolean;
5440
+ };
5222
5441
  /** Eagerly resume ONE dead-but-resumable agent-cli session IN PLACE,
5223
5442
  * WITHOUT a prompt — the boot-time counterpart to the lazy resume that
5224
5443
  * `sendPrompt`/`enqueuePrompt` trigger on the first prompt after a restart
@@ -5362,11 +5581,17 @@ interface SessionsRegistry {
5362
5581
  * ephemeral `watchers` counter surfaced on the descriptor at read time.
5363
5582
  * Called by `monitorSessionWait` when a `/sessions/:id/wait` long-poll or
5364
5583
  * `session_monitor` subscription actually blocks. A no-op for an unknown id
5365
- * is fine (the wait may outlive the row). */
5366
- incWatchers(id: string): void;
5584
+ * is fine (the wait may outlive the row). `detail`, when passed, is held
5585
+ * (by reference) alongside the count and surfaced as
5586
+ * `SessionDescriptor.watcherDetails` — pass the SAME object reference to
5587
+ * the matching {@link decWatchers} call so removal is exact. */
5588
+ incWatchers(id: string, detail?: SessionWatcherInfo): void;
5367
5589
  /** Balance an {@link incWatchers} when the waiter resolves or times out.
5368
- * Clamps at 0 and drops the map entry at zero so the counter can't leak. */
5369
- decWatchers(id: string): void;
5590
+ * Clamps at 0 and drops the map entry at zero so the counter can't leak.
5591
+ * Pass the same `detail` reference given to `incWatchers` to remove it from
5592
+ * `watcherDetails`; omitted/mismatched detail leaves the count balanced but
5593
+ * the detail list untouched (defensive — should not happen in practice). */
5594
+ decWatchers(id: string, detail?: SessionWatcherInfo): void;
5370
5595
  /** Archive a TERMINAL-status session (exited/killed/error) — sets
5371
5596
  * `archived: true` and persists. Pure housekeeping: hides the row from
5372
5597
  * `list()`'s default view, nothing else. Refuses (throws) a still-alive
@@ -5416,6 +5641,45 @@ interface SessionsRegistry {
5416
5641
  * policy state — never touches the live agent. Throws when the id is
5417
5642
  * unknown. */
5418
5643
  setKeepAlive(id: string, keepAlive: boolean): SessionDescriptor;
5644
+ /** Set or clear a session's list-visibility pin (the `session_set_pinned`
5645
+ * MCP verb / `POST /sessions/:id/pin`). `true` flips
5646
+ * `SessionDescriptor.pinned` on — pure sort/display state for the CLI
5647
+ * table and the VS Code webview's list, which sort a pinned session to
5648
+ * the top. `false` clears it. Persists via the same `schedulePersist`
5649
+ * every descriptor mutation uses and emits `session:pinned-changed` so a
5650
+ * live UI resorts without waiting for its next snapshot poll. NEVER
5651
+ * touches `keepAlive`, reaper eligibility, or any notification path —
5652
+ * pin is quiet, structural state only. Throws when the id is unknown. */
5653
+ setPinned(id: string, pinned: boolean): SessionDescriptor;
5654
+ /**
5655
+ * Manually override `awaitingInput`/`awaitingQuestion` (the
5656
+ * `session_flag_status` MCP verb) — the ONE write path for this pair
5657
+ * besides the internal heuristic (`deriveHeuristicQuestion`) and a
5658
+ * driver-reported `agent-prompt`, both of which only ever run inside a
5659
+ * live turn. Lets a human, another agent, or the session watchdog correct
5660
+ * a missed real question (`awaitingInput:true`, optionally attaching
5661
+ * `question`) or clear a false positive (`awaitingInput:false`).
5662
+ * `awaitingInput:false` ALWAYS clears `awaitingQuestion` too, regardless
5663
+ * of what `question` was passed — a question cannot outlive its
5664
+ * awaiting-input flag. When `question` is given alongside `true`, it's
5665
+ * stored as `{ text: question, source: "structured" }` (an explicit
5666
+ * override is at least as authoritative as a driver-reported prompt).
5667
+ * Guard mirrors the INVERSE of `archiveSession`'s: only a LIVE session
5668
+ * (running/starting) may be flagged — a terminal session has no turn left
5669
+ * to be "awaiting" anything, so correcting it there would be fiction with
5670
+ * nothing downstream (`session_monitor`, the webhook notifier) to observe
5671
+ * it. Persists via `schedulePersist` and emits
5672
+ * `session:awaiting-input-flagged` (carrying the required `reason`) so
5673
+ * the correction is visible via `session_events_poll` same as every other
5674
+ * lifecycle event. Cleared automatically like any other awaiting-input
5675
+ * signal on the session's next prompt/turn start. Throws when the id is
5676
+ * unknown or the session is terminal.
5677
+ */
5678
+ flagAwaitingInput(id: string, patch: {
5679
+ awaitingInput: boolean;
5680
+ question?: string;
5681
+ reason: string;
5682
+ }): SessionDescriptor;
5419
5683
  /** Subscribe to a session's output. Returns an unsubscribe fn.
5420
5684
  * Initial backfill: synchronously invokes `onLine` once for each
5421
5685
  * line currently in the ring buffer so attaches show context. */
@@ -5691,6 +5955,12 @@ interface SpawnAgentInput {
5691
5955
  * the resume/re-spawn path can re-mount the same toolset (orchestrator
5692
5956
  * WP1). */
5693
5957
  mcpServers?: AcpMcpServer[];
5958
+ /** Persistent isolated-config dir this spawn passed to
5959
+ * `startSession({ configDir })` — recorded verbatim onto
5960
+ * {@link SessionDescriptor.adapterConfigDir} so restart/lazy-resume can
5961
+ * point the respawned adapter back at the SAME dir (where the provider's
5962
+ * conversation store lives). See that field's doc. */
5963
+ adapterConfigDir?: string;
5694
5964
  /** Spawning orchestrator's session id — set when the spawn arrived
5695
5965
  * through the scoped sub-gateway (orchestrator WP4). Recorded on the
5696
5966
  * descriptor for subtree scoping + quota accounting. */
@@ -6122,12 +6392,22 @@ type GhRunner = (args: readonly string[], cwd: string) => Promise<{
6122
6392
  * footer is never applied.
6123
6393
  *
6124
6394
  * This closes that gap tool-agnostically: it subscribes to the session event
6125
- * bus and, at each executor session's turn-end / exit, resolves the OPEN PR for
6126
- * that session's branch (via the injected {@link OpenPrResolver} port) and
6127
- * stamps the same `@agentproto-bot` footer if it's missing — reusing
6128
- * {@link stampFooterOnPr}. Turn-end/exit are only poll checkpoints, never an
6129
- * assertion the PR is ready: the real predicate is "does an open PR for this
6130
- * branch now exist?", answered by the resolver, not by timing.
6395
+ * bus and, at each executor session's turn-end / exit, checks two lanes in
6396
+ * order and stamps the same `@agentproto-bot` footer if it's missing —
6397
+ * reusing {@link stampFooterOnPr}:
6398
+ *
6399
+ * A. EXACT the session's own recorded tool calls: the transcript writer
6400
+ * marks a successful `gh pr create` with the created PR's url/number
6401
+ * (`ToolCallRecord.createdPrUrl`), naming the author session directly.
6402
+ * Immune to branch switches and shared checkouts.
6403
+ * B. BRANCH — resolve the OPEN PR for the session cwd's current branch
6404
+ * (via the injected {@link OpenPrResolver} port), the fallback for
6405
+ * adapters whose tool calls aren't recorded.
6406
+ *
6407
+ * Turn-end/exit are only poll checkpoints, never an assertion the PR is
6408
+ * ready: the real predicate is "did this session create a PR / does an open
6409
+ * PR for this branch now exist?", answered by the records and resolver, not
6410
+ * by timing.
6131
6411
  *
6132
6412
  * Strictly best-effort: every handler is wrapped so a missing session, an
6133
6413
  * unreachable forge, or a failed `gh` can never throw out of a bus callback.
@@ -6179,6 +6459,10 @@ declare function createPrProvenanceReconciler(opts: {
6179
6459
  registry: ReconcilerRegistry;
6180
6460
  sessionEvents: SessionEventBus;
6181
6461
  resolveOpenPr: OpenPrResolver;
6462
+ /** Session's recorded tool calls, the exact-attribution source (lane A).
6463
+ * Defaults to reading the session's own events.jsonl via
6464
+ * {@link readToolCallRecords}; injectable for tests. */
6465
+ listToolCalls?: (sessionId: string) => Promise<ToolCallRecord[]>;
6182
6466
  run?: GhRunner;
6183
6467
  host?: string;
6184
6468
  }): PrProvenanceReconciler;
@@ -7577,6 +7861,12 @@ interface ConversationIndexRecord {
7577
7861
  cwd: string;
7578
7862
  adapterSlug: string;
7579
7863
  adapterSessionId: string;
7864
+ /** The session's isolated provider config dir at write time (see
7865
+ * `ResolveNativeLinkInput.adapterConfigDir`) — recorded so readers
7866
+ * that go through the store abstraction (`store.read`, not
7867
+ * `native.path`) can resolve the same isolated dir. Absent on
7868
+ * pre-#824 rows and native PTY sessions. */
7869
+ adapterConfigDir?: string;
7580
7870
  /** Absent when `adapterSlug` has no known native store (an adapter
7581
7871
  * outside claude-code/hermes) — the record still links session ↔
7582
7872
  * cwd ↔ adapterSessionId, it just can't point at a native file. */
@@ -7593,6 +7883,12 @@ interface ResolveNativeLinkInput {
7593
7883
  cwd: string;
7594
7884
  adapterSlug: string;
7595
7885
  adapterSessionId: string;
7886
+ /** The session's isolated provider config dir (`SessionDescriptor.
7887
+ * adapterConfigDir`): since #824 a daemon-spawned claude-code session
7888
+ * writes its transcripts under `<adapterConfigDir>/projects/<slug>`,
7889
+ * not `~/.claude/projects/<slug>`. Absent for a native PTY or a
7890
+ * pre-#824 row — the global dir applies. */
7891
+ adapterConfigDir?: string;
7596
7892
  }
7597
7893
  /**
7598
7894
  * Resolve where THIS provider keeps this conversation, computed once (at
@@ -8129,6 +8425,21 @@ interface CreateGatewayOptions {
8129
8425
  name?: string;
8130
8426
  /** Server version advertised over MCP. */
8131
8427
  version?: string;
8428
+ /** Build identity of the binary actually serving — surfaced verbatim via
8429
+ * `/health` and `daemon_health` so an operator can tell a workspace dist
8430
+ * from the published tarball of the same version. `sha`/`builtAt` are
8431
+ * stamped into the CLI at build time (tsup `define`); `source` is the
8432
+ * serve command's runtime judgement of where its own entry lives. */
8433
+ build?: {
8434
+ /** Short git sha of the checkout the dist was built from ("" when the
8435
+ * build ran outside git). */
8436
+ sha?: string;
8437
+ /** ISO timestamp of the build. */
8438
+ builtAt?: string;
8439
+ /** "workspace" (a checkout's dist), "published" (npm/npx install), or
8440
+ * "unknown". */
8441
+ source?: string;
8442
+ };
8132
8443
  /**
8133
8444
  * Run BOOT.md once at startup. Pass `false` to disable. Defaults to
8134
8445
  * `true`. The boot file is plain markdown — frontmatter-free; the