@agentproto/runtime 2.6.0 → 2.7.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';
@@ -1661,7 +1661,7 @@ declare function createTaskLedger(opts: {
1661
1661
  * and session_monitor MCP tool (long-poll multiplexed).
1662
1662
  */
1663
1663
 
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";
1664
+ 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
1665
  /**
1666
1666
  * Fixed severity vocabulary for a judge-gate finding (WP-D). Deliberately
1667
1667
  * small and fixed so `policy_status` output is comparable across different
@@ -1743,6 +1743,39 @@ interface SessionAwaitingInputEvent {
1743
1743
  ts: string;
1744
1744
  question?: SessionAwaitingQuestion;
1745
1745
  }
1746
+ /**
1747
+ * Emitted by `SessionsRegistry.flagAwaitingInput` (the `session_flag_status`
1748
+ * MCP verb) when something EXTERNAL — a human, another agent, the future
1749
+ * session watchdog — manually corrects a session's `awaitingInput`/
1750
+ * `awaitingQuestion` classification, overriding (or confirming) what the
1751
+ * internal heuristic (`deriveHeuristicQuestion`) or a driver-reported
1752
+ * `agent-prompt` last set. Distinct from `session:awaiting-input` (which only
1753
+ * ever fires when the flag flips TRUE, from the daemon's own turn-end
1754
+ * detection): this fires for BOTH directions of a manual override, always
1755
+ * carries the mandatory `reason` the caller gave (audit trail — this is the
1756
+ * one write path for this field with no automatic sensor behind it), and
1757
+ * `awaitingInput` states the value the override just set rather than being
1758
+ * implied by the event firing at all. Same bus distribution as every other
1759
+ * lifecycle event (`session_events_poll`, the webhook notifier, the routine
1760
+ * engine, `session_monitor`).
1761
+ */
1762
+ interface SessionAwaitingInputFlaggedEvent {
1763
+ type: "session:awaiting-input-flagged";
1764
+ sessionId: string;
1765
+ awaitingInput: boolean;
1766
+ /** Required justification the caller passed to `session_flag_status` —
1767
+ * why this override was made. */
1768
+ reason: string;
1769
+ label?: string;
1770
+ ts: string;
1771
+ /** Present only when `awaitingInput:true` and a `question` was attached —
1772
+ * mirrors the descriptor's `awaitingQuestion` after the override, same
1773
+ * shape as every other site that sets it (`source: "structured"`, since a
1774
+ * manual override is at least as authoritative as a driver-reported
1775
+ * prompt). Absent when `awaitingInput:false` (the override always clears
1776
+ * any prior question in that case) or when no `question` was given. */
1777
+ question?: SessionAwaitingQuestion;
1778
+ }
1746
1779
  /**
1747
1780
  * Emitted when a permission-hold session parks a `session/request_permission`
1748
1781
  * (see the pending-permissions inbox in sessions.ts). `permissionId` is the
@@ -1862,11 +1895,12 @@ interface SessionStallClearedEvent {
1862
1895
  * is the counter AFTER the attach (read back from the registry, so a
1863
1896
  * concurrent waiter is already reflected). `watcherSessionId` names the
1864
1897
  * 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.
1898
+ * orchestrator's `callerScope.ownerSessionId`); `label` is that session's
1899
+ * label/title, resolved once at attach time (a best-effort snapshot, not
1900
+ * live). Both absent for an anonymous CLI/HTTP waiter. Same bus distribution
1901
+ * as every other lifecycle event (`session_events_poll`, the webhook
1902
+ * notifier, the routine engine, `session_monitor`). Paired with
1903
+ * `session:watcher-detached` when the wait resolves or times out.
1870
1904
  */
1871
1905
  interface SessionWatcherAttachedEvent {
1872
1906
  type: "session:watcher-attached";
@@ -2064,6 +2098,24 @@ interface SessionRenamedEvent {
2064
2098
  renamedByUser?: boolean;
2065
2099
  ts: string;
2066
2100
  }
2101
+ /**
2102
+ * Emitted when an operator pins or unpins a session for list visibility
2103
+ * (`POST /sessions/:id/pin`, the `session_set_pinned` MCP verb). Like
2104
+ * `session:renamed`, this is NOT a `SessionConfig` axis — pinning never
2105
+ * touches the live agent, the idle-reaper, or any notification path, it's
2106
+ * purely a `SessionDescriptor.pinned` display/sort flag — so it rides its
2107
+ * own event rather than `session:config-changed`. `pinned` carries the value
2108
+ * now on the descriptor. Same bus distribution as every other lifecycle event
2109
+ * (`session_events_poll`, the webhook notifier, the routine engine), which is
2110
+ * how a live UI (the VS Code sessions webview) learns to resort its list
2111
+ * without waiting for its next snapshot poll.
2112
+ */
2113
+ interface SessionPinnedEvent {
2114
+ type: "session:pinned-changed";
2115
+ sessionId: string;
2116
+ pinned: boolean;
2117
+ ts: string;
2118
+ }
2067
2119
  /**
2068
2120
  * Emitted when a session is first registered in the registry (WP-R3) — both
2069
2121
  * the agent-cli spawn path (`spawnAgent`) and the terminal path (`spawnPty`).
@@ -2186,7 +2238,7 @@ interface TaskChangedEvent {
2186
2238
  sessionId?: string;
2187
2239
  ts: string;
2188
2240
  }
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;
2241
+ 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
2242
  interface SessionEventBus {
2191
2243
  emit(ev: SessionEvent): void;
2192
2244
  /** Subscribe to a specific event type. Returns an unsubscribe fn. */
@@ -3613,6 +3665,14 @@ type AgentAdapterResolver = (slug: string) => Promise<{
3613
3665
  startSession(opts: {
3614
3666
  cwd: string;
3615
3667
  resumeSessionId?: string;
3668
+ /** Persistent isolated-config location for the adapter (claude-code's
3669
+ * `CLAUDE_CONFIG_DIR`) — forwarded to the driver's
3670
+ * `start({ configDir })`. The daemon keys it per session lineage
3671
+ * (`SessionDescriptor.adapterConfigDir`) so the provider's own
3672
+ * conversation store survives adapter respawns and `resumeSessionId`
3673
+ * can restore full context instead of falling back to a digest.
3674
+ * Adapters that don't isolate a config dir ignore it. */
3675
+ configDir?: string;
3616
3676
  /**
3617
3677
  * Manifest-declared mode id forwarded from `agent_start` (AIP-45
3618
3678
  * `AgentCliHandle.modes` — e.g. claude-code's `plan` /
@@ -3849,6 +3909,12 @@ interface AdapterInstallResult {
3849
3909
  * post-install re-list failed (the install itself may still have
3850
3910
  * succeeded — read `ok`). */
3851
3911
  status?: "supported" | "available" | "ready" | "unresolvable";
3912
+ /** True when the install failed ONLY because a manifest setup step
3913
+ * declares `interactive: true` and this process has no TTY to run it
3914
+ * in (e.g. openclaw's `onboard --install-daemon` TUI). The remedy is a
3915
+ * real terminal — `agentproto setup <slug>` — which a UI can offer
3916
+ * directly as a PTY terminal session. */
3917
+ needsInteractiveSetup?: boolean;
3852
3918
  }
3853
3919
  /**
3854
3920
  * Installs an agent CLI adapter by slug and reports the outcome. Wired by
@@ -4214,6 +4280,48 @@ interface SessionAccessProfileEcho {
4214
4280
  endpoint: string;
4215
4281
  method: AuthMethod;
4216
4282
  }
4283
+ /**
4284
+ * One live waiter behind a session's {@link SessionDescriptor.watchers} count
4285
+ * (#session-visibility). Captured at `monitorSessionWait` attach time and held
4286
+ * only for the life of that wait — never persisted, never updated in place
4287
+ * (a snapshot of the waiter's label at attach time, not a live mirror of it).
4288
+ */
4289
+ interface SessionWatcherInfo {
4290
+ /** The waiting session's id, when this wait was initiated by an agent
4291
+ * session via the orchestrator scope (`callerScope.ownerSessionId`).
4292
+ * Absent for an anonymous CLI/HTTP waiter — it still counts toward
4293
+ * `watchers`, it just can't be named. */
4294
+ watcherSessionId?: string;
4295
+ /** Best-effort label/title for `watcherSessionId`, resolved once at attach
4296
+ * time. A later rename of the watching session is not reflected. */
4297
+ watcherLabel?: string;
4298
+ /** What this wait is blocking on — mirrors `session_monitor`'s `event`
4299
+ * parameter (`SessionWaitEvent` in orchestration-tools.ts; kept as a plain
4300
+ * string here so the registry doesn't depend on the tool-layer type). */
4301
+ event: string;
4302
+ /** The wait's configured timeout, ms — absent for the MCP tool's default. */
4303
+ timeoutMs?: number;
4304
+ /** ISO 8601 timestamp this waiter attached. */
4305
+ since: string;
4306
+ }
4307
+ /** One prompt waiting in `SessionDescriptor.promptQueue` — see that
4308
+ * field's doc for the FIFO/force ordering contract. */
4309
+ interface QueuedPrompt {
4310
+ /** Stable id, assigned at queue time (by the HTTP layer for
4311
+ * `POST /sessions/:id/prompt`'s `queue` arm so the caller can echo it
4312
+ * straight back in the response, or minted here for any other
4313
+ * caller). Used by `removeQueuedPrompt` to cancel this one item
4314
+ * before it dispatches. */
4315
+ id: string;
4316
+ /** Same shape `runAgentTurn`'s `message` parameter accepts — a raw
4317
+ * string or an ACP content block/array. */
4318
+ message: unknown;
4319
+ /** ISO 8601 timestamp this item was queued. */
4320
+ queuedAt: string;
4321
+ /** Same as `enqueuePrompt`'s `opts.source` — carried through to the
4322
+ * turn this item eventually becomes. */
4323
+ source?: string;
4324
+ }
4217
4325
  interface SessionDescriptor {
4218
4326
  id: string;
4219
4327
  kind: SessionKind;
@@ -4303,6 +4411,15 @@ interface SessionDescriptor {
4303
4411
  * daemon restart). 0/absent ⇒ nothing is watching. Lets a UI mark a
4304
4412
  * session "supervised — notify on turn-end". */
4305
4413
  watchers?: number;
4414
+ /** Per-waiter detail behind the {@link watchers} count — who is blocked on
4415
+ * this session and what they're blocked on. Same lifetime/source as
4416
+ * `watchers` (stamped at read time from the registry's live waiter list,
4417
+ * never persisted); empty/absent ⇒ nothing is watching. An anonymous
4418
+ * CLI/HTTP waiter (no `callerScope`) carries no `watcherSessionId` — it
4419
+ * still counts, it just can't be named. Lets a UI answer "who is watching
4420
+ * this, and what are they waiting for" without guessing from the bare
4421
+ * count. */
4422
+ watcherDetails?: readonly SessionWatcherInfo[];
4306
4423
  /** Count of DESCENDANT sessions (children, grandchildren, …) currently
4307
4424
  * mid-turn, derived at read time from the `parentSessionId` lineage
4308
4425
  * (#session-visibility, subtree rollup). Lets a UI show an idle parent that
@@ -4352,6 +4469,24 @@ interface SessionDescriptor {
4352
4469
  * session exit. Emits `session:bg-tasks-parked` /
4353
4470
  * `session:bg-tasks-cleared`. */
4354
4471
  pendingBgTasks?: number;
4472
+ /** ISO 8601 timestamp of the last turn that ended because the ADAPTER
4473
+ * ITSELF reported a failure — `runAgentTurn` observed a `turn-end` event
4474
+ * with `reason:"error"` (session-event-bus.ts's `SessionTurnEndEvent`,
4475
+ * e.g. a refused/errored ACP `stopReason` — see adapters/mastra-agent's
4476
+ * host) — as opposed to a thrown/rejected adapter stream, which flips
4477
+ * `status` to `"error"` directly (a genuinely terminal row the existing
4478
+ * crash/error handling already catches). This is the twin gap for a
4479
+ * turn that fails IN-BAND while the process stays alive: `status` stays
4480
+ * `"running"`, `lastError` stays unset (reserved for `markCrashed`), and
4481
+ * nothing else distinguishes the session from one that simply finished
4482
+ * a clean turn and is idle. Folded into the same "stalled" activity/UI
4483
+ * treatment a mid-turn stall gets (see sessionsTree.logic.ts's
4484
+ * `activityFor`) rather than a new state, on the theory that both mean
4485
+ * "the last thing this session did needs a look, not a re-prompt on
4486
+ * faith". Stamped at turn-end when `turnEndReason === "error"`; cleared
4487
+ * the moment a LATER turn completes without one (same reset shape as
4488
+ * `resumeAttempts`/`restartAttempts`). Detection + signal only. */
4489
+ lastTurnErroredAt?: string;
4355
4490
  /** DERIVED, read-time only (never persisted — stripped by `snapshotRows`,
4356
4491
  * stamped by `stampInterrupted` in list()/get()/findByIdOrName). True when
4357
4492
  * this session died with a turn in flight under a daemon restart —
@@ -4478,6 +4613,17 @@ interface SessionDescriptor {
4478
4613
  * `session_set_keepalive` (`registry.setKeepAlive`). Absent (not `false`)
4479
4614
  * for every session that hasn't opted in. */
4480
4615
  keepAlive?: boolean;
4616
+ /** Server-persisted, list-visibility-only favorite flag — set/cleared via
4617
+ * `session_set_pinned` (`registry.setPinned`) or `POST /sessions/:id/pin`.
4618
+ * Purely a sort/display concern: pinned sessions sort to the top of the
4619
+ * session list (CLI table, VS Code webview's dedicated "Pinned" group).
4620
+ * Deliberately orthogonal to — and must NEVER be confused with —
4621
+ * `keepAlive` (idle-reaper exemption, operational), the VS Code
4622
+ * extension's client-side-only "watch" eye (toast notifications, never
4623
+ * persisted here), or `watchers` (live supervisor wait count). No side
4624
+ * effects on reaping, notifications, or anything else. Absent (not
4625
+ * `false`) for every session that hasn't been pinned. */
4626
+ pinned?: boolean;
4481
4627
  /** True when the session was spawned under a real PTY (node-pty)
4482
4628
  * instead of `child_process.spawn`. PTY sessions carry raw ANSI
4483
4629
  * bytes (alt-screen, key bindings, colors); attach goes through
@@ -4640,6 +4786,17 @@ interface SessionDescriptor {
4640
4786
  * via sessions.json so `restart` can pass it as `resumeSessionId`
4641
4787
  * and reattach to the prior conversation history. */
4642
4788
  adapterSessionId?: string;
4789
+ /** Persistent isolated-config dir handed to the adapter at spawn time
4790
+ * (`startSession({ configDir })` → claude-code's `CLAUDE_CONFIG_DIR`).
4791
+ * Keyed by the FIRST session id in a restart lineage
4792
+ * (`adapterConfigDirFor`) and carried forward verbatim across
4793
+ * restarts/lazy resumes — the provider's own conversation store lives
4794
+ * inside it, so reusing the SAME dir is what lets `resumeSessionId`
4795
+ * restore full context after the adapter process died (before this,
4796
+ * each respawn minted a fresh temp dir and native resume always
4797
+ * degraded to the daemon-transcript digest). Absent on legacy rows and
4798
+ * adapters that don't isolate a config dir. */
4799
+ adapterConfigDir?: string;
4643
4800
  /** MCP servers mounted into the agent's session at spawn time
4644
4801
  * (orchestrator WP1). Persisted so the resume/re-spawn path can
4645
4802
  * re-mount the same host-chosen toolset instead of resuming a
@@ -4739,6 +4896,23 @@ interface SessionDescriptor {
4739
4896
  * message (`runAgentTurn`) — so delivery survives a busy parent without
4740
4897
  * ever cancelling its in-flight work. Absent when nothing is queued. */
4741
4898
  pendingChildCrashNotices?: string[];
4899
+ /** FIFO of prompts that arrived while this session was mid-turn and
4900
+ * asked to be QUEUED rather than rejected (`enqueuePrompt`'s
4901
+ * `opts.queue` arm — see its doc comment). Index 0 is next to
4902
+ * dispatch. A plain queued item is appended to the back; a
4903
+ * `force`-queued one is inserted at the front, jumping everything
4904
+ * already waiting WITHOUT cancelling the live turn (that's what
4905
+ * `interrupt` is for). Drained one item at a time, in this order, by
4906
+ * `dispatchQueuedPrompt` at the end of every `runAgentTurn` — never
4907
+ * by the caller that queued it, which already returned. Always
4908
+ * replaced with a NEW array reference on every mutation (push, drain,
4909
+ * removal), never mutated in place — the VS Code webview's
4910
+ * `sessionDescriptorsEqual` diff is a shallow `!==` per field, so an
4911
+ * in-place `.shift()` here would silently stop propagating queue
4912
+ * changes to the transcript panel. `[]` (not absent) once anything
4913
+ * has ever been queued, matching `pendingChildCrashNotices`'s
4914
+ * convention above. */
4915
+ promptQueue?: QueuedPrompt[];
4742
4916
  /** Best-effort context-handoff digest (Fix D), stashed on a descriptor
4743
4917
  * whose resume degraded to a BLANK spawn — the adapter's own
4744
4918
  * conversation store was missing, or the adapter declared `resumable:
@@ -4893,6 +5067,7 @@ interface SessionSummary {
4893
5067
  activitySummary?: SessionActivitySummary;
4894
5068
  archived?: boolean;
4895
5069
  keepAlive?: boolean;
5070
+ pinned?: boolean;
4896
5071
  pty?: boolean;
4897
5072
  name?: string;
4898
5073
  argv?: readonly string[];
@@ -4919,6 +5094,10 @@ interface SessionSummary {
4919
5094
  * `SessionDescriptor.pendingBgTasks`. Stamped at turn-end, cleared on the
4920
5095
  * next turn start / exit. */
4921
5096
  pendingBgTasks?: number;
5097
+ /** Adapter-reported turn-error marker — see
5098
+ * `SessionDescriptor.lastTurnErroredAt`. Stamped at turn-end, cleared on
5099
+ * the next turn that completes without one. */
5100
+ lastTurnErroredAt?: string;
4922
5101
  origin?: string;
4923
5102
  parentSessionId?: string;
4924
5103
  depth?: number;
@@ -5214,11 +5393,41 @@ interface SessionsRegistry {
5214
5393
  * `false` reproduces today's mid-turn rejection byte-for-byte. */
5215
5394
  /** `opts.source` on either prompt verb is the turn's provenance
5216
5395
  * (`agent:<sessionId>` when another session injected it — see
5217
- * `SessionObserver.recordPrompt`); recording-only, never behavioral. */
5396
+ * `SessionObserver.recordPrompt`); recording-only, never behavioral.
5397
+ *
5398
+ * `opts.queue` is the FIFO arm (additive, opt-in — omitted/false
5399
+ * reproduces the byte-for-byte busy rejection above, unchanged):
5400
+ * when the session is mid-turn AND `queue` is true, the prompt is
5401
+ * appended to `SessionDescriptor.promptQueue` instead of being
5402
+ * rejected, and this resolves immediately WITHOUT dispatching
5403
+ * anything — `dispatchQueuedPrompt` fires it once the current (and
5404
+ * any earlier-queued) turns finish. `opts.force` only matters
5405
+ * alongside `queue`: it inserts at the FRONT of the queue instead of
5406
+ * the back, jumping everything already waiting, but — unlike
5407
+ * `interrupt` — never touches the live turn. `opts.queueId` lets the
5408
+ * caller pin the queued item's id up front (the HTTP layer does this
5409
+ * so it can echo the id straight back in its response); omitted, one
5410
+ * is minted here. On an IDLE session `queue`/`force`/`queueId` are
5411
+ * no-ops — admission falls straight through to the normal dispatch
5412
+ * path below, identical to `queue` omitted. `interrupt` is checked
5413
+ * first: an `{interrupt: true, queue: true}` pair interrupts (never
5414
+ * queues) exactly like `interrupt` alone. */
5218
5415
  enqueuePrompt(id: string, message: unknown, opts?: {
5219
5416
  interrupt?: boolean;
5220
5417
  source?: string;
5418
+ queue?: boolean;
5419
+ force?: boolean;
5420
+ queueId?: string;
5221
5421
  }): Promise<void>;
5422
+ /** Cancel one not-yet-dispatched item in `SessionDescriptor.promptQueue`
5423
+ * by id — the composer's per-item "remove" action. Idempotent: an
5424
+ * unknown session or an id that's already gone (dispatched, already
5425
+ * removed, or never existed) resolves `{removed: false}` rather than
5426
+ * throwing, same shape as `interruptSession`'s no-op-is-not-an-error
5427
+ * contract. */
5428
+ removeQueuedPrompt(id: string, queueId: string): {
5429
+ removed: boolean;
5430
+ };
5222
5431
  /** Eagerly resume ONE dead-but-resumable agent-cli session IN PLACE,
5223
5432
  * WITHOUT a prompt — the boot-time counterpart to the lazy resume that
5224
5433
  * `sendPrompt`/`enqueuePrompt` trigger on the first prompt after a restart
@@ -5362,11 +5571,17 @@ interface SessionsRegistry {
5362
5571
  * ephemeral `watchers` counter surfaced on the descriptor at read time.
5363
5572
  * Called by `monitorSessionWait` when a `/sessions/:id/wait` long-poll or
5364
5573
  * `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;
5574
+ * is fine (the wait may outlive the row). `detail`, when passed, is held
5575
+ * (by reference) alongside the count and surfaced as
5576
+ * `SessionDescriptor.watcherDetails` — pass the SAME object reference to
5577
+ * the matching {@link decWatchers} call so removal is exact. */
5578
+ incWatchers(id: string, detail?: SessionWatcherInfo): void;
5367
5579
  /** 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;
5580
+ * Clamps at 0 and drops the map entry at zero so the counter can't leak.
5581
+ * Pass the same `detail` reference given to `incWatchers` to remove it from
5582
+ * `watcherDetails`; omitted/mismatched detail leaves the count balanced but
5583
+ * the detail list untouched (defensive — should not happen in practice). */
5584
+ decWatchers(id: string, detail?: SessionWatcherInfo): void;
5370
5585
  /** Archive a TERMINAL-status session (exited/killed/error) — sets
5371
5586
  * `archived: true` and persists. Pure housekeeping: hides the row from
5372
5587
  * `list()`'s default view, nothing else. Refuses (throws) a still-alive
@@ -5416,6 +5631,45 @@ interface SessionsRegistry {
5416
5631
  * policy state — never touches the live agent. Throws when the id is
5417
5632
  * unknown. */
5418
5633
  setKeepAlive(id: string, keepAlive: boolean): SessionDescriptor;
5634
+ /** Set or clear a session's list-visibility pin (the `session_set_pinned`
5635
+ * MCP verb / `POST /sessions/:id/pin`). `true` flips
5636
+ * `SessionDescriptor.pinned` on — pure sort/display state for the CLI
5637
+ * table and the VS Code webview's list, which sort a pinned session to
5638
+ * the top. `false` clears it. Persists via the same `schedulePersist`
5639
+ * every descriptor mutation uses and emits `session:pinned-changed` so a
5640
+ * live UI resorts without waiting for its next snapshot poll. NEVER
5641
+ * touches `keepAlive`, reaper eligibility, or any notification path —
5642
+ * pin is quiet, structural state only. Throws when the id is unknown. */
5643
+ setPinned(id: string, pinned: boolean): SessionDescriptor;
5644
+ /**
5645
+ * Manually override `awaitingInput`/`awaitingQuestion` (the
5646
+ * `session_flag_status` MCP verb) — the ONE write path for this pair
5647
+ * besides the internal heuristic (`deriveHeuristicQuestion`) and a
5648
+ * driver-reported `agent-prompt`, both of which only ever run inside a
5649
+ * live turn. Lets a human, another agent, or the session watchdog correct
5650
+ * a missed real question (`awaitingInput:true`, optionally attaching
5651
+ * `question`) or clear a false positive (`awaitingInput:false`).
5652
+ * `awaitingInput:false` ALWAYS clears `awaitingQuestion` too, regardless
5653
+ * of what `question` was passed — a question cannot outlive its
5654
+ * awaiting-input flag. When `question` is given alongside `true`, it's
5655
+ * stored as `{ text: question, source: "structured" }` (an explicit
5656
+ * override is at least as authoritative as a driver-reported prompt).
5657
+ * Guard mirrors the INVERSE of `archiveSession`'s: only a LIVE session
5658
+ * (running/starting) may be flagged — a terminal session has no turn left
5659
+ * to be "awaiting" anything, so correcting it there would be fiction with
5660
+ * nothing downstream (`session_monitor`, the webhook notifier) to observe
5661
+ * it. Persists via `schedulePersist` and emits
5662
+ * `session:awaiting-input-flagged` (carrying the required `reason`) so
5663
+ * the correction is visible via `session_events_poll` same as every other
5664
+ * lifecycle event. Cleared automatically like any other awaiting-input
5665
+ * signal on the session's next prompt/turn start. Throws when the id is
5666
+ * unknown or the session is terminal.
5667
+ */
5668
+ flagAwaitingInput(id: string, patch: {
5669
+ awaitingInput: boolean;
5670
+ question?: string;
5671
+ reason: string;
5672
+ }): SessionDescriptor;
5419
5673
  /** Subscribe to a session's output. Returns an unsubscribe fn.
5420
5674
  * Initial backfill: synchronously invokes `onLine` once for each
5421
5675
  * line currently in the ring buffer so attaches show context. */
@@ -5691,6 +5945,12 @@ interface SpawnAgentInput {
5691
5945
  * the resume/re-spawn path can re-mount the same toolset (orchestrator
5692
5946
  * WP1). */
5693
5947
  mcpServers?: AcpMcpServer[];
5948
+ /** Persistent isolated-config dir this spawn passed to
5949
+ * `startSession({ configDir })` — recorded verbatim onto
5950
+ * {@link SessionDescriptor.adapterConfigDir} so restart/lazy-resume can
5951
+ * point the respawned adapter back at the SAME dir (where the provider's
5952
+ * conversation store lives). See that field's doc. */
5953
+ adapterConfigDir?: string;
5694
5954
  /** Spawning orchestrator's session id — set when the spawn arrived
5695
5955
  * through the scoped sub-gateway (orchestrator WP4). Recorded on the
5696
5956
  * descriptor for subtree scoping + quota accounting. */