@agentproto/runtime 5.2.1 → 5.3.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.
@@ -1,10 +1,11 @@
1
1
  import { z } from 'zod';
2
- import { R as RouteSpec, S as SessionConfig, P as Posture, E as EffortLevel, C as ContextProfile, A as AuthMethod } from './session-config-RyRL2AbD.js';
3
- import { S as SpawnBrowserMode, R as ResolvedContextContinuityPolicy } from './context-continuity-BfNVbX5W.js';
2
+ import { R as RouteSpec, S as SessionConfig, P as Posture, E as EffortLevel, C as ContextProfile, A as AuthMethod } from './session-config-DxIIuJFY.js';
3
+ import { a as SpawnBrowserMode, R as ResolvedContextContinuityPolicy } from './spawn-defaults-Cp9SGZex.js';
4
4
  import { AcpPermissionResolution, AcpMcpServer } from '@agentproto/acp';
5
5
  import { SessionMode } from '@agentproto/acp/client';
6
6
  import { ChildProcess } from 'node:child_process';
7
7
  import { CostBudget } from '@agentproto/auth';
8
+ import { h as WorktreeIsolationMode } from './config-DcESQDmZ.js';
8
9
 
9
10
  /**
10
11
  * Typed, best-effort persistence for the last-seen provider "remaining quota"
@@ -524,6 +525,210 @@ interface UsageDescriptorFields extends Omit<UsageDetail, "turns"> {
524
525
  */
525
526
  declare function projectSessionUsage(desc: UsageDescriptorFields): SessionUsage;
526
527
 
528
+ /**
529
+ * Per-session process-tree resource sampler - answers "which session is
530
+ * eating this host" without a hand-written `ps` walk.
531
+ *
532
+ * One process-table read (`ps` on macOS/Linux, `/proc` as the Linux fallback
533
+ * when `ps` is missing) is attributed to sessions by climbing DOWN the tree
534
+ * from each live session's adapter pid, which the registry already knows -
535
+ * no env scraping for a session whose pid is known. Whatever is left over is
536
+ * bucketed:
537
+ *
538
+ * - `daemon` the daemon process itself + its own non-session children
539
+ * - `provisioning` daemon children spawned while a worktree provision is in
540
+ * flight (setup hooks: `pnpm install`, builds) - work that
541
+ * is not attached to a session yet
542
+ * - `orphans` processes that look agentproto-owned (a dead session's
543
+ * adapter-config dir / worktree in their args, or an
544
+ * `AGENTPROTO_SESSION_ID` / adapter-config env marker on a
545
+ * reparented process) but belong to no live session.
546
+ * Reported only - never killed.
547
+ *
548
+ * The expensive part (table + host reads) is cached for a short TTL and
549
+ * de-duplicated while in flight; attribution over the cached table is pure
550
+ * and cheap, so a burst of `session_list`/CLI/HTTP callers costs one `ps`.
551
+ *
552
+ * `%CPU` is whatever `ps` reports: the kernel's decayed (macOS) or
553
+ * lifetime-average (Linux) figure, not an instantaneous delta. That is the
554
+ * right lens for "what has this session been costing", and the same number an
555
+ * operator reads off `ps`/`top` - but it lags a sudden spike.
556
+ */
557
+
558
+ /** One row of the host process table. */
559
+ interface ProcRow {
560
+ pid: number;
561
+ ppid: number;
562
+ uid: number;
563
+ /** Resident set size in KiB (`ps` reports KiB on both macOS and Linux). */
564
+ rssKib: number;
565
+ /** `%CPU` as reported by the OS (may exceed 100 for multi-threaded work). */
566
+ cpuPercent: number;
567
+ /** Seconds since the process started. */
568
+ elapsedSec: number;
569
+ /** Full command line as one string. */
570
+ args: string;
571
+ }
572
+ /** Where the process table comes from. Injectable for tests. */
573
+ type ProcessTableSource = () => Promise<ProcRow[]>;
574
+ /**
575
+ * Collapse a raw command line to the short name an operator reads:
576
+ * `node …/pnpm.cjs install` → `pnpm install`, a vitest fork worker →
577
+ * `vitest`, `…/@esbuild/darwin-arm64/bin/esbuild --service` → `esbuild`,
578
+ * `…/claude-code/cli.js` → `claude`. Unrecognised node scripts stay `node`;
579
+ * anything else is its executable's basename (a macOS `.app` bundle is the
580
+ * app's name). Pure - no filesystem access.
581
+ */
582
+ declare function normalizeCommand(args: string): string;
583
+ interface CommandGroupStats {
584
+ /** Normalized command name (`pnpm install`, `vitest`, `git`, …). */
585
+ name: string;
586
+ count: number;
587
+ rssBytes: number;
588
+ cpuPercent: number;
589
+ }
590
+ interface ProcessDetail {
591
+ pid: number;
592
+ ppid: number;
593
+ /** Normalized name. */
594
+ command: string;
595
+ /** Raw command line, truncated. */
596
+ args: string;
597
+ rssBytes: number;
598
+ cpuPercent: number;
599
+ elapsedSec: number;
600
+ /** True when the process was attributed by env/args marker rather than by
601
+ * descent from the session's adapter pid (a reparented stray). */
602
+ detached?: boolean;
603
+ }
604
+ interface ResourceStats {
605
+ rssBytes: number;
606
+ cpuPercent: number;
607
+ procCount: number;
608
+ /** Top command groups by RSS. */
609
+ topCommands: CommandGroupStats[];
610
+ /** Every process, largest RSS first - populated by `detail: "full"` only. */
611
+ processes?: ProcessDetail[];
612
+ }
613
+ interface SessionResourceStats extends ResourceStats {
614
+ sessionId: string;
615
+ /** The adapter pid the tree was climbed from. */
616
+ pid: number;
617
+ }
618
+ interface OrphanGroup extends ResourceStats {
619
+ /** Top of the orphaned subtree. */
620
+ pid: number;
621
+ command: string;
622
+ elapsedSec: number;
623
+ /** Session id the markers point at, when one was recoverable. */
624
+ sessionHint?: string;
625
+ /** Why this looks agentproto-owned. */
626
+ reason: string;
627
+ }
628
+ interface ProvisionInFlight {
629
+ sessionId?: string;
630
+ label?: string;
631
+ cwd: string;
632
+ startedAt: string;
633
+ }
634
+ interface ProvisioningStats extends ResourceStats {
635
+ inFlight: ProvisionInFlight[];
636
+ }
637
+ interface HostInfo {
638
+ platform: NodeJS.Platform;
639
+ cpuCount: number;
640
+ /** 1/5/15-minute load averages. */
641
+ loadAvg: [number, number, number];
642
+ totalMemBytes: number;
643
+ /** Memory available for new work - `MemAvailable` on Linux, free +
644
+ * reclaimable pages on macOS, `os.freemem()` elsewhere. */
645
+ freeMemBytes: number;
646
+ }
647
+ type StatsDetail = "summary" | "full";
648
+ interface ProcessStatsReport {
649
+ sampledAt: string;
650
+ detail: StatsDetail;
651
+ host: HostInfo;
652
+ sessions: SessionResourceStats[];
653
+ daemon: ResourceStats & {
654
+ pid: number;
655
+ };
656
+ provisioning: ProvisioningStats;
657
+ orphans: OrphanGroup[];
658
+ /** Sessions + daemon + provisioning. Orphans are reported separately and
659
+ * are NOT in this sum (they belong to nobody). */
660
+ totals: {
661
+ rssBytes: number;
662
+ cpuPercent: number;
663
+ procCount: number;
664
+ };
665
+ }
666
+ interface ProvisionTracker {
667
+ begin(info: {
668
+ sessionId?: string;
669
+ label?: string;
670
+ cwd: string;
671
+ }): () => void;
672
+ list(): ProvisionInFlight[];
673
+ }
674
+ /** Run `fn` (a worktree provision) with the tracker marking it in flight. */
675
+ declare function trackWorktreeProvision<T>(info: {
676
+ sessionId?: string;
677
+ label?: string;
678
+ cwd: string;
679
+ }, fn: () => Promise<T>, tracker?: ProvisionTracker): Promise<T>;
680
+ type HostInfoSource = () => Promise<HostInfo>;
681
+ /** Read `pid → session id` markers from the environment of `pids`. Best
682
+ * effort: Linux reads `/proc/<pid>/environ` (own uid only); macOS asks
683
+ * `ps -E`, which some hardened setups blank out - a miss just means a stray
684
+ * is judged by its command line alone. Never throws. */
685
+ type EnvHintReader = (pids: readonly number[]) => Promise<Map<number, string>>;
686
+ /** The slice of a registry descriptor the service reads. */
687
+ interface StatsSessionDescriptor {
688
+ id: string;
689
+ pid?: number | null;
690
+ status: string;
691
+ adapterConfigDir?: string;
692
+ worktreePath?: string;
693
+ label?: string;
694
+ name?: string;
695
+ kind?: string;
696
+ adapterSlug?: string;
697
+ }
698
+ interface ProcessStatsServiceOptions {
699
+ table?: ProcessTableSource;
700
+ host?: HostInfoSource;
701
+ envHints?: EnvHintReader;
702
+ tracker?: ProvisionTracker;
703
+ daemonPid?: number;
704
+ uid?: number;
705
+ /** How long a sampled table is reused. Default 3000 ms. */
706
+ ttlMs?: number;
707
+ now?: () => number;
708
+ }
709
+ interface ProcessStatsService {
710
+ report(sessions: readonly StatsSessionDescriptor[], opts?: {
711
+ detail?: StatsDetail;
712
+ fresh?: boolean;
713
+ }): Promise<ProcessStatsReport>;
714
+ }
715
+ declare function createProcessStatsService(opts?: ProcessStatsServiceOptions): ProcessStatsService;
716
+ /** Process-wide sampler shared by every surface (MCP, HTTP, CLI-over-HTTP),
717
+ * so one `ps` per TTL serves them all. */
718
+ declare function getProcessStatsService(): ProcessStatsService;
719
+ interface LabeledSessionStats extends SessionResourceStats {
720
+ label?: string;
721
+ name?: string;
722
+ kind?: string;
723
+ status: string;
724
+ adapterSlug?: string;
725
+ }
726
+ interface LabeledProcessStatsReport extends Omit<ProcessStatsReport, "sessions"> {
727
+ sessions: LabeledSessionStats[];
728
+ /** Set for a subtree-scoped caller: host-wide buckets are withheld. */
729
+ scoped?: true;
730
+ }
731
+
527
732
  /**
528
733
  * McpAppsHostService — the daemon as the MCP client that fetches MCP App
529
734
  * UIs (spec 2026-01-26) on behalf of a session's chat UI.
@@ -860,7 +1065,7 @@ interface ActivityRunStepSlice {
860
1065
  interface ActivityWorkflowStageSlice {
861
1066
  index: number;
862
1067
  label?: string;
863
- status: "pending" | "running" | "done" | "failed" | "skipped";
1068
+ status: "pending" | "running" | "done" | "failed" | "skipped" | "cancelled";
864
1069
  steps: readonly ActivityRunStepSlice[];
865
1070
  }
866
1071
  /** Structural slice of a `WorkflowRun` (`workflow_list` rows). */
@@ -1421,10 +1626,22 @@ interface CompletionPolicySupervisor {
1421
1626
  *
1422
1627
  * State machine (deliberately small):
1423
1628
  * pending ⇄ in_progress → done | failed
1424
- * pending | in_progress → cancelled
1629
+ * in_progress ⇄ awaiting_approval → cancelled (denied)
1630
+ * pending | in_progress | awaiting_approval → cancelled
1425
1631
  * done → pending only via an explicit reopen (creator/operator; bumps rev,
1426
1632
  * records who in `meta.reopenedBy` — no silent resurrection)
1427
1633
  *
1634
+ * Approval gate — `awaiting_approval` (E1b, on top of E1a's approvals
1635
+ * engine): a task parked on one or more `approvalIds` (with an optional
1636
+ * `artifacts` trail pointing at session artifacts or approval payloads).
1637
+ * `linkApproval` is the synchronous wiring point a caller uses at
1638
+ * `approval_request` time (see `session-event-bus.ts`'s
1639
+ * `ApprovalRequestedEvent` doc); `task_update` can set the same fields
1640
+ * directly. The ledger itself subscribes to `approval:decided`: approved →
1641
+ * `in_progress`; denied → `cancelled` with `meta.reason = "denied"` — a
1642
+ * SYSTEM transition, not subject to the update() ACL (mirrors `settleVerify`
1643
+ * and `releaseTask` below).
1644
+ *
1428
1645
  * Ownership (two roles, no per-field matrix):
1429
1646
  * - `owner` absent = claimable. Claim is CAS — succeeds only if `owner` is
1430
1647
  * absent AND `rev` matches; sets in_progress, appends to `sessions[]`.
@@ -1483,7 +1700,7 @@ interface CompletionPolicySupervisor {
1483
1700
  * turn-end-based auto-completion: a turn ending is not a task being done.
1484
1701
  */
1485
1702
 
1486
- declare const TASK_STATUSES: readonly ["pending", "in_progress", "done", "failed", "cancelled"];
1703
+ declare const TASK_STATUSES: readonly ["pending", "in_progress", "awaiting_approval", "done", "failed", "cancelled"];
1487
1704
  type TaskStatus = (typeof TASK_STATUSES)[number];
1488
1705
  /** Parse a raw query/body value into a TaskStatus, or undefined when it
1489
1706
  * isn't one — the `parseActivityState` idiom for the `/tasks` routes. */
@@ -1508,6 +1725,20 @@ type TaskVerification = {
1508
1725
  kind: "human";
1509
1726
  ts: string;
1510
1727
  };
1728
+ /**
1729
+ * A link on a task, pointing either at a durable session artifact
1730
+ * (`session-artifacts.ts` — content-addressed, `sessionId` + `key` +
1731
+ * `sha256`) or at one of E1a's approval payloads (`approvalId`, resolved
1732
+ * through the approvals engine). Opaque to the ledger — it never dereferences
1733
+ * either shape, only stores and returns it.
1734
+ */
1735
+ type TaskArtifactLink = {
1736
+ sessionId: string;
1737
+ key: string;
1738
+ sha256: string;
1739
+ } | {
1740
+ approvalId: string;
1741
+ };
1511
1742
  interface TaskRecord {
1512
1743
  taskId: string;
1513
1744
  /** Scope — see the board rules in the module docblock. */
@@ -1521,6 +1752,13 @@ interface TaskRecord {
1521
1752
  createdBy: string;
1522
1753
  /** INFORMATIONAL in v1 — no scheduler reads it. */
1523
1754
  blockedBy?: string[];
1755
+ /** Approvals (E1a's `ApprovalRecord.id`s) gating this task. Set via
1756
+ * `linkApproval` or `task_update`. Driving one into `awaiting_approval`
1757
+ * requires this to end up non-empty. */
1758
+ approvalIds?: string[];
1759
+ /** Evidence trail — session artifacts and/or approval payload pointers.
1760
+ * See {@link TaskArtifactLink}. */
1761
+ artifacts?: TaskArtifactLink[];
1524
1762
  /** Every session that ever claimed this task (append-only) — one of the
1525
1763
  * two edges the Activity projector will join on later. */
1526
1764
  sessions?: string[];
@@ -1672,6 +1910,12 @@ interface TaskUpdateInput {
1672
1910
  };
1673
1911
  /** Free-text note, stamped into `meta.note` (last-write-wins). */
1674
1912
  note?: string;
1913
+ /** Replace the linked approval ids. Owner-settable (like `note`) — not
1914
+ * gated behind the manager-only field-edit check. */
1915
+ approvalIds?: string[];
1916
+ /** Replace the linked artifact/approval evidence trail. Owner-settable,
1917
+ * same as `approvalIds`. */
1918
+ artifacts?: TaskArtifactLink[];
1675
1919
  }
1676
1920
  /**
1677
1921
  * Every write verb answers with this three-way union: applied / rev-CAS
@@ -1699,6 +1943,17 @@ interface TaskLedger {
1699
1943
  get(taskId: string, caller: TaskCaller): TaskRecord | undefined;
1700
1944
  claim(input: TaskClaimInput, caller: TaskCaller): TaskWriteResult;
1701
1945
  update(input: TaskUpdateInput, caller: TaskCaller): TaskWriteResult;
1946
+ /**
1947
+ * Link an approval to a task — the synchronous wiring point a caller uses
1948
+ * at `approval_request` time (see `ApprovalRequestedEvent`'s doc in
1949
+ * `session-event-bus.ts`): appends `approvalId` to `approvalIds`
1950
+ * (idempotent) and, when the task is `in_progress`, moves it to
1951
+ * `awaiting_approval`. Requires owner or manager; the task must already be
1952
+ * `in_progress` or `awaiting_approval` (a pending/closed task has nothing
1953
+ * running to wait on). No `rev` — this is an internal wiring verb, not a
1954
+ * caller-facing CAS write.
1955
+ */
1956
+ linkApproval(taskId: string, approvalId: string, caller: TaskCaller): TaskWriteResult;
1702
1957
  /** The caller's default board — the spawn-stamped `meta.boardId` (when
1703
1958
  * present) else `tree:<root>` for a session caller, `ws:<workspaceSlug>`
1704
1959
  * for the operator. Exposed so the tool/HTTP layers can echo it without
@@ -1783,7 +2038,7 @@ declare function isProviderLimitError(message: string | undefined): boolean;
1783
2038
  * and session_monitor MCP tool (long-poll multiplexed).
1784
2039
  */
1785
2040
 
1786
- 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";
2041
+ 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:provisioning" | "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" | "approval:requested" | "approval:decided" | "approval:consumed" | "approval:expired";
1787
2042
  /**
1788
2043
  * Fixed severity vocabulary for a judge-gate finding (WP-D). Deliberately
1789
2044
  * small and fixed so `policy_status` output is comparable across different
@@ -2375,6 +2630,25 @@ interface SessionSpawnedEvent {
2375
2630
  depth: number;
2376
2631
  ts: string;
2377
2632
  }
2633
+ /**
2634
+ * Emitted as a session's worktree provisioning moves through the daemon-wide
2635
+ * provisioning queue: `queued` (waiting for a heavy-phase slot; `position` is
2636
+ * the 1-based projected dispatch order), `started` (slot granted, heavy
2637
+ * segment running), `phase` (entered another heavy phase), `done` (finished;
2638
+ * `outcome` says how). `phase` names the heavy phase concerned. For a
2639
+ * synchronous `agent_start` the `sessionId` is the id the session will have
2640
+ * once provisioning finishes (no registry row exists before then).
2641
+ */
2642
+ interface SessionProvisioningEvent {
2643
+ type: "session:provisioning";
2644
+ sessionId: string;
2645
+ kind: "queued" | "started" | "phase" | "done";
2646
+ phase?: "worktree" | "clone" | "deps" | "copy" | "setup";
2647
+ position?: number;
2648
+ outcome?: "ok" | "failed" | "cancelled";
2649
+ label?: string;
2650
+ ts: string;
2651
+ }
2378
2652
  /** Emitted by the supervisor when a completion policy's gate passes. */
2379
2653
  interface PolicyPassedEvent {
2380
2654
  type: "policy:passed";
@@ -2575,7 +2849,55 @@ interface SessionHarnessWarningEvent {
2575
2849
  label?: string;
2576
2850
  ts: string;
2577
2851
  }
2578
- 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;
2852
+ /**
2853
+ * Emitted by the approvals engine (`approvals/engine.ts`) when an
2854
+ * `approval_request` (MCP or `POST /approvals`) raises a new pending
2855
+ * approval. `taskId` is present when the request is linked to a task —
2856
+ * the task ledger uses it for nothing (linking already happened
2857
+ * synchronously at request time via `TaskLedger.linkApproval`); it rides
2858
+ * here purely for a listener's convenience.
2859
+ */
2860
+ interface ApprovalRequestedEvent {
2861
+ type: "approval:requested";
2862
+ approvalId: string;
2863
+ kind: string;
2864
+ taskId?: string;
2865
+ ts: string;
2866
+ }
2867
+ /**
2868
+ * Emitted once a human decides a pending approval through a declared
2869
+ * channel (`web_click`, `ui_card`) — never for anything else. The task
2870
+ * ledger subscribes to this: a task in `awaiting_approval` whose
2871
+ * `approvalIds` contains `approvalId` moves to `in_progress` on
2872
+ * `decision:"approved"`, or `cancelled` (`meta.reason:"denied"`) on
2873
+ * `decision:"denied"`.
2874
+ */
2875
+ interface ApprovalDecidedEvent {
2876
+ type: "approval:decided";
2877
+ approvalId: string;
2878
+ decision: "approved" | "denied";
2879
+ channel: "web_click" | "ui_card";
2880
+ taskId?: string;
2881
+ ts: string;
2882
+ }
2883
+ /** Emitted when `approval_consume` (or its HTTP twin) atomically consumes
2884
+ * an approved approval — the gated action is now cleared to run exactly
2885
+ * once. */
2886
+ interface ApprovalConsumedEvent {
2887
+ type: "approval:consumed";
2888
+ approvalId: string;
2889
+ taskId?: string;
2890
+ ts: string;
2891
+ }
2892
+ /** Emitted when a pending approval's `expiresAt` (or its owning task's end)
2893
+ * lapses before a human decided. */
2894
+ interface ApprovalExpiredEvent {
2895
+ type: "approval:expired";
2896
+ approvalId: string;
2897
+ taskId?: string;
2898
+ ts: string;
2899
+ }
2900
+ type SessionEvent = SessionTurnEndEvent | SessionAwaitingInputEvent | SessionAwaitingInputFlaggedEvent | SessionAwaitingQuestionAnsweredEvent | SessionPermissionRequestEvent | SessionPermissionResolvedEvent | SessionExitedEvent | SessionReapedEvent | SessionStalledEvent | SessionStallClearedEvent | SessionWatcherAttachedEvent | SessionWatcherDetachedEvent | SessionBgTasksParkedEvent | SessionBgTasksClearedEvent | SessionBgTaskEvent | SessionResumedEvent | SessionSpawnedEvent | SessionProvisioningEvent | SessionCommandDoneEvent | SessionModelChangedEvent | SessionConfigChangedEvent | SessionRenamedEvent | SessionPinnedEvent | SessionArtifactAddedEvent | SessionArtifactPinnedEvent | SessionMessageEvent | PolicyPassedEvent | PolicyFailedEvent | PolicyCommitReadyEvent | PolicyCommittedEvent | CronFiredEvent | CronSucceededEvent | CronFailedEvent | ActivityChangedEvent | TaskChangedEvent | WorkflowGateReportEvent | WorkflowApprovalRequestedEvent | WorkflowApprovalResolvedEvent | WorkflowSuspendedEvent | WorkflowSuspendResumedEvent | SessionHarnessWarningEvent | ApprovalRequestedEvent | ApprovalDecidedEvent | ApprovalConsumedEvent | ApprovalExpiredEvent;
2579
2901
  interface SessionEventBus {
2580
2902
  emit(ev: SessionEvent): void;
2581
2903
  /** Subscribe to a specific event type. Returns an unsubscribe fn. */
@@ -2951,6 +3273,264 @@ interface SessionAppServeInfo {
2951
3273
  message?: string;
2952
3274
  }
2953
3275
 
3276
+ /**
3277
+ * Policy layer for `agent_start.worktree` — the config-driven decision of
3278
+ * WHETHER to isolate a spawn into its own git worktree, kept deliberately
3279
+ * separate from HOW one is provisioned.
3280
+ *
3281
+ * This module holds no dependency on `@agentproto/worktree`. Same reasoning
3282
+ * as `worktree-identity.ts`'s docblock, only stronger: provisioning runs the
3283
+ * `worktree.provision` TOOL (git + the base tree's `agentproto.json` setup
3284
+ * hooks), which would drag the driver/harness/provider graph into
3285
+ * `@agentproto/runtime` for a capability the runtime only ever *triggers*.
3286
+ * So the concrete provisioner is an INJECTED PORT ({@link WorktreeProvisioner})
3287
+ * wired at the composition root by a host that already depends on the
3288
+ * worktree package (the CLI). The runtime owns only the pure decision
3289
+ * ({@link decideWorktreeIsolation}) and the config/env resolution
3290
+ * ({@link loadWorktreeIsolation}) — both trivially unit-testable without git.
3291
+ *
3292
+ * Mirrors `agent_start.sandbox`'s `resolveSandboxProvider` injection shape,
3293
+ * except sandbox can default its resolver inside the runtime (the runtime
3294
+ * depends on `@agentproto/sandbox`); worktree cannot, so an unwired daemon
3295
+ * simply has no provisioner and a spawn that would need one fails loud rather
3296
+ * than silently spawning unisolated.
3297
+ */
3298
+
3299
+ /** Env override for the isolation policy. Highest-priority source, ahead of
3300
+ * the `worktrees.isolation` config field — see `loadWorktreeIsolation`. */
3301
+ declare const WORKTREE_ISOLATION_ENV = "AGENTPROTO_WORKTREES_ISOLATION";
3302
+ /** Env override for the daemon-wide cap on concurrently running HEAVY
3303
+ * worktree-provisioning segments (`depsCmd` etc.). Highest-priority source,
3304
+ * ahead of `worktrees.provisionConcurrency`. Enforced by
3305
+ * `@agentproto/worktree`'s scheduler (which reads the same env name); the
3306
+ * runtime only carries the name and the default for the config registry. */
3307
+ declare const WORKTREE_PROVISION_CONCURRENCY_ENV = "AGENTPROTO_WORKTREES_PROVISION_CONCURRENCY";
3308
+ /** Default cap on concurrently running heavy provisioning segments. Mirrors
3309
+ * `@agentproto/worktree`'s `DEFAULT_PROVISION_CONCURRENCY` (a CLI test pins
3310
+ * the two together — the runtime takes no dependency on that package). */
3311
+ declare const DEFAULT_WORKTREE_PROVISION_CONCURRENCY = 2;
3312
+ /** The default when nothing is configured. Back-compat-preserving: a caller
3313
+ * that passes no `worktree` field spawns exactly where it asked. */
3314
+ declare const DEFAULT_WORKTREE_ISOLATION: WorktreeIsolationMode;
3315
+ /**
3316
+ * The `agent_start.worktree` field. `true` isolates with an auto-minted slug;
3317
+ * an object additionally pins the slug and/or base ref, and can opt into
3318
+ * async provisioning (`async: true` — see `WorktreeRequest.async`); `false`
3319
+ * (or omitted) is "no explicit request" — the policy mode decides.
3320
+ */
3321
+ type WorktreeField = boolean | {
3322
+ slug?: string;
3323
+ base?: string;
3324
+ async?: boolean;
3325
+ };
3326
+ /** The caller's explicit request, normalized — `undefined` when the field is
3327
+ * absent or `false`, otherwise the (possibly empty) slug/base overrides. */
3328
+ interface WorktreeRequest {
3329
+ slug?: string;
3330
+ base?: string;
3331
+ /** Return a real, registered session as soon as it's minted, provisioning
3332
+ * the worktree in the BACKGROUND instead of blocking `agent_start`'s
3333
+ * response on `git worktree add` + the repo's setup hooks (which can run
3334
+ * minutes — see `session-spawn.ts`'s async-provision branch).
3335
+ *
3336
+ * DEFAULTS TO TRUE for any spawn that provisions a worktree, UNLESS this
3337
+ * spawn also carries `wait` (which needs a first turn to block on — see
3338
+ * `worktree_async_wait_conflict` — so the presence of `wait` alone falls
3339
+ * back to the synchronous path rather than conflicting by default). That
3340
+ * default is applied by `session-spawn.ts` itself (WP-H), not by this
3341
+ * field or `normalizeWorktreeField` — the normalizer leaves `async`
3342
+ * exactly as the caller sent it (`undefined` when omitted; see its own
3343
+ * test), so a caller inspecting the normalized request never sees a
3344
+ * value it didn't write. WP-H incident: the synchronous contract this
3345
+ * field opts OUT of — hold the RPC open for however long setup hooks
3346
+ * take, routinely minutes — is exactly what let a client's own request
3347
+ * timeout retry into a second live agent sharing the first one's
3348
+ * worktree (closed on the other side too — see `session-spawn.ts`'s
3349
+ * `findWorktreeLabelCwdCollision`, which now refuses that retry outright
3350
+ * rather than merely warning). Pass `false` explicitly to keep the old
3351
+ * blocking ok/fail contract (this package's own `worktree_provision_failed`
3352
+ * test coverage exercises it deliberately synchronous). */
3353
+ async?: boolean;
3354
+ }
3355
+ /** What the runtime hands the provisioner. `cwd` is where the session would
3356
+ * otherwise spawn; the provisioner resolves the owning repo from it. */
3357
+ interface WorktreeProvisionRequest {
3358
+ cwd: string;
3359
+ /** Explicit slug from the caller; omitted ⇒ the provisioner mints a
3360
+ * collision-free one (see the host implementation). */
3361
+ slug?: string;
3362
+ /** Base ref the worktree branch is cut from; omitted ⇒ the provisioner's
3363
+ * default (`origin/main`). */
3364
+ base?: string;
3365
+ /** Free-text label (the session's `label`) the provisioner may fold into a
3366
+ * minted slug for human readability. Never load-bearing for correctness. */
3367
+ labelHint?: string;
3368
+ /** Absolute path the provisioner should append the setup hooks' FULL
3369
+ * stdout+stderr to (see `worktree.provision`'s `setupLogPath` input and
3370
+ * `@agentproto/worktree`'s `runSetup`). `session-spawn.ts` passes the
3371
+ * spawning session's own transcript dir here, so a failing setup hook's
3372
+ * full output survives even after a subsequently-reclaimed worktree is
3373
+ * gone — the short `HookError` message alone only carries a filtered
3374
+ * tail. Omitted callers (e.g. `agentproto worktree new`, no session to
3375
+ * log into) simply get no persisted log. */
3376
+ setupLogPath?: string;
3377
+ /** Re-run a failing `worktree.setup` command exactly once before giving
3378
+ * up — see `worktree.provision`'s `retrySetupOnFailure` input and
3379
+ * `runSetup`'s doc for why this is a bounded complement, not a
3380
+ * substitute, and why the daemon opts in while `agentproto worktree new`
3381
+ * doesn't. */
3382
+ retrySetupOnFailure?: boolean;
3383
+ /** Cancels this provisioning: a queued one is dropped from the daemon-wide
3384
+ * provisioning queue, a running one has its child process tree killed (no
3385
+ * orphaned `pnpm install`). The provisioner then rejects. */
3386
+ signal?: AbortSignal;
3387
+ /** Whose spawn this is, for the scheduler's fair-share dispatch (the parent
3388
+ * session id, or an anonymous key for a top-level spawn). */
3389
+ callerId?: string;
3390
+ /** Progress of the heavy provisioning segment (queued / started / phase /
3391
+ * done). Best-effort; never throws into the provisioner. */
3392
+ onProgress?: (progress: WorktreeProvisionProgress) => void;
3393
+ }
3394
+ /** Phases of a worktree provisioning. `worktree` is the unthrottled
3395
+ * `git worktree add` step; the rest are the throttled heavy ones. */
3396
+ type WorktreeProvisionPhase = "worktree" | "clone" | "deps" | "copy" | "setup";
3397
+ /** Structural mirror of `@agentproto/worktree`'s `ProvisionProgress` (the
3398
+ * runtime deliberately takes no dependency on that package). */
3399
+ type WorktreeProvisionProgress = {
3400
+ kind: "queued";
3401
+ position: number;
3402
+ phase: WorktreeProvisionPhase;
3403
+ } | {
3404
+ kind: "started";
3405
+ phase: WorktreeProvisionPhase;
3406
+ } | {
3407
+ kind: "phase";
3408
+ phase: WorktreeProvisionPhase;
3409
+ } | {
3410
+ kind: "done";
3411
+ outcome: "ok" | "failed" | "cancelled";
3412
+ };
3413
+ /** The provisioner's outcome. `isolated: false` is NOT a failure — it means
3414
+ * there was nothing to isolate (`cwd` sits in no git repo), and the caller
3415
+ * should spawn plain at the original cwd. A genuine failure throws. */
3416
+ type WorktreeProvisionOutcome = {
3417
+ isolated: true;
3418
+ cwd: string;
3419
+ branch: string;
3420
+ } | {
3421
+ isolated: false;
3422
+ reason: "not-a-git-repo";
3423
+ };
3424
+ /**
3425
+ * Injected port: provision a git worktree for `req.cwd` and return the
3426
+ * worktree's own cwd for the spawn to land in. Wired at the composition root
3427
+ * by the CLI (over `@agentproto/worktree`); absent on a bare runtime.
3428
+ */
3429
+ type WorktreeProvisioner = (req: WorktreeProvisionRequest) => Promise<WorktreeProvisionOutcome>;
3430
+ /**
3431
+ * Injected port: best-effort attempt to reclaim ONE worktree by path, called
3432
+ * when its session reaches a terminal state (see
3433
+ * `SessionDescriptor.worktreeAutoProvisioned` and `createSessionsRegistry`'s
3434
+ * `runWorktreeAutoReclaim` option in `sessions.ts`). Deliberately scoped to
3435
+ * exactly the one path a session's own exit is allowed to touch — never a
3436
+ * repo-wide sweep. The implementation (wired by the CLI over
3437
+ * `@agentproto/worktree`'s `reclaimOneWorktree`) re-classifies fresh and only
3438
+ * ever removes the worktree when that comes back `reclaim` (merged-or-fresh,
3439
+ * clean, idle) — a dirty or held worktree is simply left alone. Must never
3440
+ * reject in a way the caller can't safely ignore; `sessions.ts` treats any
3441
+ * rejection as "couldn't reclaim this time, leave it for a manual or
3442
+ * scheduled `gc` sweep" and logs rather than lets it interrupt session
3443
+ * teardown. Absent on a bare runtime — auto-reclaim is simply skipped, and a
3444
+ * caller-explicit worktree request is never routed through this port at all
3445
+ * (see `WorktreeDecision.provision.implicit`).
3446
+ */
3447
+ type WorktreeAutoReclaimer = (worktreePath: string) => Promise<void>;
3448
+ /** The pure decision's three outcomes. `spawn-in-place` may carry a `warn`:
3449
+ * a non-fatal notice the caller should surface (the child is about to run in
3450
+ * a shared, dirty checkout it doesn't own). A `warn` never blocks the spawn —
3451
+ * it's the loud-but-legitimate middle ground between silently spawning into a
3452
+ * shared tree and hard-rejecting an in-place spawn that's normal at depth.
3453
+ * `provision.implicit` is `true` exactly when the caller made no explicit
3454
+ * `worktree` request and the `"always"` policy provisioned one anyway (the
3455
+ * worktree the caller never asked to keep) — `false` whenever the request
3456
+ * came from the caller itself (`on-request`'s only path here, or `"always"`
3457
+ * with an explicit field). This is the signal `session-spawn.ts` threads
3458
+ * onto the session descriptor so exit-time auto-reclaim (`sessions.ts`)
3459
+ * only ever touches a worktree the daemon minted on its own — a worktree a
3460
+ * caller explicitly asked to keep is never auto-removed. */
3461
+ type WorktreeDecision = {
3462
+ action: "spawn-in-place";
3463
+ warn?: string;
3464
+ } | {
3465
+ action: "provision";
3466
+ request: WorktreeRequest;
3467
+ implicit: boolean;
3468
+ } | {
3469
+ action: "reject";
3470
+ message: string;
3471
+ };
3472
+ /**
3473
+ * Normalize the raw field into an explicit request, or `undefined` when the
3474
+ * caller made no request (`false` / omitted). An object with no overrides
3475
+ * still counts as a request (`{}`) — the caller opted in, just without pins.
3476
+ */
3477
+ declare function normalizeWorktreeField(field: WorktreeField | undefined): WorktreeRequest | undefined;
3478
+ /**
3479
+ * The resolution matrix — mode × explicit-request × depth — with no side
3480
+ * effects. Every branch is exercised in `worktree-isolation.test.ts`.
3481
+ *
3482
+ * Depth-0 gate: a nested spawn (`depth > 0`, i.e. made through the scoped
3483
+ * orchestrator sub-gateway) NEVER provisions a worktree of its own — it
3484
+ * inherits its parent's ground, per AIP-46 §Delegation. This bites before
3485
+ * the mode is even consulted, so `always` too provisions only at the root.
3486
+ * But "never provisions" is NOT "silently ignore an explicit request": a
3487
+ * nested caller that passes an EXPLICIT `worktree` (`true` / `{slug}`) asked
3488
+ * for isolation and would silently get none — the spawn quietly runs in the
3489
+ * shared checkout, exactly the footgun that nearly clobbered a shared tree.
3490
+ * So an explicit request at depth > 0 is REJECTED loudly (same shape as the
3491
+ * `never`-policy reject below), pointing the caller at sandbox isolation —
3492
+ * child isolation is the sandbox, not a second worktree. Only the IMPLICIT/
3493
+ * absent case (`undefined` / `false`) stays an in-place spawn: the child is
3494
+ * meant to share the parent's tree and made no request to the contrary.
3495
+ *
3496
+ * That implicit case still has a footgun, though: the inherited cwd may be a
3497
+ * LIVE, DIRTY, shared checkout (the real repo, not a daemon-provisioned
3498
+ * worktree) — spawning in place there lets a nested child edit a tree someone
3499
+ * else is actively working in. We don't hard-reject it (implicit in-place is
3500
+ * legitimate and common), but we DON'T stay silent either: when the caller
3501
+ * hands us `sharedDirtyCwd` (an impure signal it computed — see
3502
+ * `session-spawn.ts`), the in-place spawn carries a `warn`. A caller that
3503
+ * genuinely intends it passes `allowSharedCwd` to acknowledge and silence the
3504
+ * warning. The signal is only consulted for the implicit nested case; every
3505
+ * other branch ignores it, keeping the root matrix unchanged.
3506
+ */
3507
+ declare function decideWorktreeIsolation(input: {
3508
+ mode: WorktreeIsolationMode;
3509
+ field: WorktreeField | undefined;
3510
+ depth: number;
3511
+ /** Impure signal, computed by the caller (never inside this pure fn): the
3512
+ * inherited cwd is a git work tree that is DIRTY and NOT a daemon-
3513
+ * provisioned worktree — i.e. a shared checkout the child would edit in
3514
+ * place. Only consulted for an implicit nested spawn. */
3515
+ sharedDirtyCwd?: boolean;
3516
+ /** Caller's explicit acknowledgement that spawning into a shared dirty cwd
3517
+ * is intended — silences the `sharedDirtyCwd` warning. */
3518
+ allowSharedCwd?: boolean;
3519
+ }): WorktreeDecision;
3520
+ /** Parse a raw string into a valid mode, or `undefined` when it isn't one. */
3521
+ declare function parseWorktreeIsolationMode(raw: string | undefined): WorktreeIsolationMode | undefined;
3522
+ /**
3523
+ * Resolve the effective isolation mode: env > config field > default. Mirrors
3524
+ * `resolveWorktreesRoot`'s precedence (there's no flag layer — this is read
3525
+ * daemon-side at spawn, not from an invocation). Never throws: an unreadable
3526
+ * config falls through to the default.
3527
+ */
3528
+ declare function loadWorktreeIsolation(loadCfg?: () => Promise<{
3529
+ worktrees?: {
3530
+ isolation?: WorktreeIsolationMode;
3531
+ };
3532
+ }>): Promise<WorktreeIsolationMode>;
3533
+
2954
3534
  /** One background task as mirrored onto `SessionDescriptor.backgroundTasks`. */
2955
3535
  interface SessionBackgroundTask {
2956
3536
  taskId: string;
@@ -3649,6 +4229,24 @@ interface SessionHandoff {
3649
4229
  /** ISO 8601 timestamp the handoff spawn completed. */
3650
4230
  at: string;
3651
4231
  }
4232
+ /**
4233
+ * Where a still-`starting` session's worktree provisioning stands in the
4234
+ * daemon-wide provisioning queue (heavy phases, `depsCmd` above all, are
4235
+ * throttled so a burst of spawns cannot melt the host). Present only while
4236
+ * provisioning is in flight; cleared when it finishes, fails, or is cancelled.
4237
+ */
4238
+ interface SessionProvisioning {
4239
+ /** `queued`: waiting for a heavy-phase slot. `running`: provisioning is
4240
+ * actually executing (the cheap prep, or a granted heavy segment). */
4241
+ state: "queued" | "running";
4242
+ /** 1-based projected dispatch position; only while `queued`. */
4243
+ position?: number;
4244
+ /** The phase queued for (`queued`) or currently executing (`running`). */
4245
+ phase: WorktreeProvisionPhase;
4246
+ /** ISO-8601 instant provisioning began (the spawn), constant across the
4247
+ * queued and running states so `now - startedAt` is the total wait. */
4248
+ startedAt: string;
4249
+ }
3652
4250
  interface SessionDescriptor {
3653
4251
  id: string;
3654
4252
  kind: SessionKind;
@@ -3658,6 +4256,9 @@ interface SessionDescriptor {
3658
4256
  command: string;
3659
4257
  pid: number | null;
3660
4258
  status: SessionStatus;
4259
+ /** Worktree-provisioning progress of a `starting` row — see
4260
+ * {@link SessionProvisioning}. Absent once the session left `starting`. */
4261
+ provisioning?: SessionProvisioning;
3661
4262
  /** Unambiguous liveness signal, stamped at read time (list()/get()/
3662
4263
  * findByIdOrName) — true iff `status` is "running" or "starting", the
3663
4264
  * same test the daemon uses internally (validateAgentTurn). Ephemeral:
@@ -3788,6 +4389,11 @@ interface SessionDescriptor {
3788
4389
  * `withMemory: true`) since it costs a `ps` spawn; absent otherwise, and
3789
4390
  * absent for a row with no `pid` or that `ps` didn't report. */
3790
4391
  rssBytes?: number;
4392
+ /** Process-tree resource sample for this session (`process-stats.ts`):
4393
+ * RSS, %CPU, process count, top commands by RSS - plus, at `stats:"full"`,
4394
+ * every process. Ephemeral and opt-in (`session_list`/`agent_sessions_list`
4395
+ * `stats`), never persisted; absent for a row with no live process. */
4396
+ stats?: Omit<SessionResourceStats, "sessionId">;
3791
4397
  /** Count of live supervisors currently blocked waiting on this session —
3792
4398
  * HTTP `GET /sessions/:id/wait` long-polls and `session_monitor`
3793
4399
  * subscriptions, both via `monitorSessionWait` (#session-visibility).
@@ -4611,10 +5217,13 @@ interface SessionDescriptor {
4611
5217
  contextContinuityAckedAtPct?: number;
4612
5218
  /** Id of the most recent checkpoint created for this session. */
4613
5219
  checkpointId?: string;
4614
- /** When this session was continued fresh, the source session id. */
5220
+ /** The source session id when this row continues another one: a
5221
+ * continue-fresh handoff OR a revival (restart / cron / sentinel / inbound
5222
+ * wake / direct resume of the same adapter conversation — also recorded as
5223
+ * `resumedFrom`). Absent on a row that started a conversation. */
4615
5224
  continuedFrom?: string;
4616
- /** When this session was continued fresh into a new session, the target
4617
- * session id. */
5225
+ /** The row that continued this one (the inverse of `continuedFrom`); the
5226
+ * latest continuation wins when a row is revived more than once. */
4618
5227
  continuedTo?: string;
4619
5228
  /** Set alongside `continuedFrom` on the NEW session — the harness the
4620
5229
  * checkpoint moved from/to and when. Present on every `continue_fresh`
@@ -4749,6 +5358,9 @@ interface SessionSummary {
4749
5358
  command: string;
4750
5359
  pid: number | null;
4751
5360
  status: SessionStatus;
5361
+ /** Worktree-provisioning progress while `starting` — see
5362
+ * `SessionDescriptor.provisioning`. */
5363
+ provisioning?: SessionProvisioning;
4752
5364
  startedAt: string;
4753
5365
  endedAt?: string;
4754
5366
  exitCode?: number;
@@ -4820,6 +5432,9 @@ interface SessionSummary {
4820
5432
  * `SessionDescriptor.lastTurnErroredAt`. Stamped at turn-end, cleared on
4821
5433
  * the next turn that completes without one. */
4822
5434
  lastTurnErroredAt?: string;
5435
+ /** `SessionDescriptor.lastError` (spawn/crash cause), capped at 2000 chars
5436
+ * for the list projection. Lets a list view say WHY an errored row died. */
5437
+ lastError?: string;
4823
5438
  origin?: string;
4824
5439
  parentSessionId?: string;
4825
5440
  depth?: number;
@@ -5088,6 +5703,13 @@ interface SessionsRegistry {
5088
5703
  * killed it mid-provision. A terminal descriptor is never resurrected.
5089
5704
  * Also a no-op for an unknown id (the row was removed entirely). */
5090
5705
  settlePendingAgent(id: string, outcome: PendingAgentOutcome): void;
5706
+ /** Record a provisioning progress report for `id`: updates the row's
5707
+ * {@link SessionProvisioning} (`queued` / `started` / `phase`; `done`
5708
+ * clears it) and emits `session:provisioning` on the bus. Also emits for an
5709
+ * id with no registry row (a synchronous provision, whose session doesn't
5710
+ * exist until it finishes) — only the descriptor update is skipped. Never
5711
+ * touches a row that already left `starting`. */
5712
+ reportProvisioning(id: string, progress: WorktreeProvisionProgress): void;
5091
5713
  /** Spawn a process under a real PTY (node-pty). Bytes flow through
5092
5714
  * the registry's byte ring buffer + emitter; attach with
5093
5715
  * `attachPty(id, ...)`. Throws when the registry was constructed
@@ -5401,6 +6023,13 @@ interface SessionsRegistry {
5401
6023
  * builds the fully-typed event (axis + value already reflected on the new
5402
6024
  * descriptor); the registry only forwards it. */
5403
6025
  emitConfigChanged(ev: SessionConfigChangedEvent): void;
6026
+ /**
6027
+ * Announce a `session:harness-warning` for a session — an explicit,
6028
+ * non-fatal "the host could not honor part of what was asked" notice (the
6029
+ * app-boundary fs zones not enforceable on this harness, …). Best-effort
6030
+ * observability; no-op when the registry has no event bus.
6031
+ */
6032
+ emitHarnessWarning(sessionId: string, warnings: readonly string[], label?: string): void;
5404
6033
  /**
5405
6034
  * Switch the reasoning/compute budget (effort) on a LIVE agent-cli session
5406
6035
  * without restarting it — the live-effort verb (SPEC §4.2, build step 5),
@@ -6074,7 +6703,13 @@ interface SpawnAgentInput {
6074
6703
  /** `SpawnAgentInput` minus the fields that only exist once the driver's
6075
6704
  * `startSession` has actually run — see `spawnAgentPending`'s doc for why
6076
6705
  * everything else is safe to record up front. */
6077
- type SpawnAgentPendingInput = Omit<SpawnAgentInput, "agentSession" | "commandPreview" | "resumable" | "nativeTerminalResume" | "initialPrompt">;
6706
+ type SpawnAgentPendingInput = Omit<SpawnAgentInput, "agentSession" | "commandPreview" | "resumable" | "nativeTerminalResume" | "initialPrompt"> & {
6707
+ /** Invoked once when the placeholder reaches a terminal status (killed, or
6708
+ * settled failed) while provisioning may still be in flight — the hook
6709
+ * that aborts the provisioner so a killed spawn leaves no queued entry
6710
+ * and no orphaned install. Never invoked after a successful settle. */
6711
+ onCancel?: () => void;
6712
+ };
6078
6713
  /** The deferred outcome `settlePendingAgent` resolves a placeholder with —
6079
6714
  * see that method's doc. */
6080
6715
  type PendingAgentOutcome = {
@@ -6228,4 +6863,4 @@ interface RecordOpenedPrInput {
6228
6863
  url: string;
6229
6864
  }
6230
6865
 
6231
- export { MESSAGE_URGENCIES as $, type ActivityPolicySlice as A, type PtyFactory as B, type CompletionPolicySupervisor as C, type ActivityKind as D, type ActivitySource as E, type ActivityState as F, type ActivityWaitingOn as G, type AnthropicReaderConfig as H, AnthropicRemainingQuotaReader as I, type AttachPolicyInput as J, type CommitSpec as K, type EagerResumeFailReason as L, type MessageUrgency as M, type EagerResumeOutcome as N, type EagerResumeSkipReason as O, type PrResolvedState as P, type QuotaReadableProfile as Q, type RestartPolicy as R, type SessionEvent as S, type TaskLedger as T, type UserPreset as U, type GateSpec as V, type JudgeGateSpec as W, MAX_MESSAGE_DATA_BYTES as X, MESSAGE_KINDS as Y, MESSAGE_PREAMBLE as Z, MESSAGE_TAG as _, type SessionEventType as a, escapeMessageBody as a$, type MessageDeliveryVia as a0, type MessageFilter as a1, type MessageRelation as a2, type MessageTreeNode as a3, type OnFailSpec as a4, type PendingPermission as a5, type PermissionRespondInput as a6, type PermissionRespondResult as a7, type PolicyRunStatus as a8, type PricingResolver as a9, type TaskGateOutcome as aA, type TaskGateRunner as aB, type TaskLedgerRegistry as aC, type TaskLedgerSessionSlice as aD, type TaskListFilter as aE, type TaskRecord as aF, type TaskStatus as aG, type TaskUpdateInput as aH, type TaskVerification as aI, type TaskVerifySupervisor as aJ, type TaskWriteResult as aK, type TokenPricing as aL, type UsageBucket as aM, type UsageComputeInput as aN, type UsageDetail as aO, type UsageSource as aP, type UserPresetsFile as aQ, WORKSPACE_SLUG_ENV as aR, activityCounts as aS, compactOutcome as aT, createSessionMessage as aU, createSupervisorTaskGateRunner as aV, createTaskLedger as aW, deleteUserPreset as aX, deriveSessionOutcome as aY, deriveSessionUsage as aZ, escapeHumanPrompt as a_, type QuotaStoreFile as aa, type QuotaStoreOptions as ab, type RegisterBrowserInput as ac, type RegisterSessionInput as ad, type RemainingQuota as ae, ResumeDisabledError as af, SESSION_END_REASONS as ag, SESSION_ID_ENV as ah, type SessionCurrentPhase as ai, type SessionKind as aj, type SessionMessageFrom as ak, type SessionOutcome as al, type SessionOutcomeArtifact as am, type SessionOutcomeCompact as an, type SessionOutcomeLink as ao, type SessionSnapshots as ap, type SessionStatus as aq, type ShellGateSpec as ar, type SpawnAgentInput as as, type SpawnSessionInput as at, type StoredProfileQuota as au, TASK_STATUSES as av, type TaskCaller as aw, type TaskChangedEvent as ax, type TaskClaimInput as ay, type TaskCreateInput as az, type SessionEventBus as b, filterActivities as b0, getUserPreset as b1, isClosedTaskStatus as b2, isKnownSessionEndReason as b3, isMessageAllowed as b4, isProviderLimitError as b5, isTerminalActivityState as b6, listUserPresets as b7, loadQuotaStore as b8, loadUserPresets as b9, userPresetSchema as bA, matchesMessageFilter as ba, messageFrom as bb, mintMessageId as bc, parseAnthropicRateLimitHeaders as bd, parseTaskStatus as be, parseWindow as bf, pickUsageDetail as bg, policyToActivities as bh, policyWatchesSession as bi, prToActivities as bj, projectSessionUsage as bk, readProfileQuota as bl, recordProfileQuota as bm, renderInboxDigest as bn, renderSessionMessage as bo, renderSessionMessages as bp, resolveRelation as bq, rollupUsage as br, sanitizeLabel as bs, saveUserPreset as bt, turnToActivities as bu, userPresetsPath as bv, workflowToActivities as bw, type RecentSpawnConfig as bx, deriveRecentSpawnConfigs as by, touchUserPreset as bz, type SessionsRegistry as c, type SessionDescriptor as d, type SessionEndReason as e, type ActivityListFilter as f, type ActivityRecord as g, type ActivityTurnSession as h, type ActivityPrSession as i, type ActivityWorkflowRunSlice as j, type ActivityTaskSlice as k, type AgentsMdResolution as l, type SandboxAppServeSpec as m, type AgentSessionLike as n, type UsageReadContext as o, type UsageReadResult as p, type MessageKind as q, type SessionMessage as r, type AgentStreamEvent as s, type SessionUsage as t, type ToolCallRecord as u, type UsageRollup as v, type RemainingQuotaReader as w, type UsageSnapshotRecord as x, type SessionAwaitingQuestion as y, type PolicyRunState as z };
6866
+ export { DEFAULT_WORKTREE_PROVISION_CONCURRENCY as $, type ActivityPolicySlice as A, type UsageRollup as B, type CompletionPolicySupervisor as C, type RemainingQuotaReader as D, type UsageSnapshotRecord as E, type SessionAwaitingQuestion as F, type PolicyRunState as G, type PtyFactory as H, type WorktreeAutoReclaimer as I, type ActivityKind as J, type ActivitySource as K, type ActivityState as L, type MessageUrgency as M, type ActivityWaitingOn as N, type AnthropicReaderConfig as O, type PrResolvedState as P, type QuotaReadableProfile as Q, type RestartPolicy as R, type SessionEvent as S, type TaskLedger as T, type UserPreset as U, AnthropicRemainingQuotaReader as V, type WorktreeProvisioner as W, type AttachPolicyInput as X, type CommandGroupStats as Y, type CommitSpec as Z, DEFAULT_WORKTREE_ISOLATION as _, type SessionEventType as a, type TaskUpdateInput as a$, type EagerResumeFailReason as a0, type EagerResumeOutcome as a1, type EagerResumeSkipReason as a2, type GateSpec as a3, type HostInfo as a4, type JudgeGateSpec as a5, type LabeledProcessStatsReport as a6, type LabeledSessionStats as a7, MAX_MESSAGE_DATA_BYTES as a8, MESSAGE_KINDS as a9, type SessionCurrentPhase as aA, type SessionKind as aB, type SessionMessageFrom as aC, type SessionOutcome as aD, type SessionOutcomeArtifact as aE, type SessionOutcomeCompact as aF, type SessionOutcomeLink as aG, type SessionResourceStats as aH, type SessionSnapshots as aI, type SessionStatus as aJ, type ShellGateSpec as aK, type SpawnAgentInput as aL, type SpawnSessionInput as aM, type StatsDetail as aN, type StoredProfileQuota as aO, TASK_STATUSES as aP, type TaskCaller as aQ, type TaskChangedEvent as aR, type TaskClaimInput as aS, type TaskCreateInput as aT, type TaskGateOutcome as aU, type TaskGateRunner as aV, type TaskLedgerRegistry as aW, type TaskLedgerSessionSlice as aX, type TaskListFilter as aY, type TaskRecord as aZ, type TaskStatus as a_, MESSAGE_PREAMBLE as aa, MESSAGE_TAG as ab, MESSAGE_URGENCIES as ac, type MessageDeliveryVia as ad, type MessageFilter as ae, type MessageRelation as af, type MessageTreeNode as ag, type OnFailSpec as ah, type OrphanGroup as ai, type PendingPermission as aj, type PermissionRespondInput as ak, type PermissionRespondResult as al, type PolicyRunStatus as am, type PricingResolver as an, type ProcessDetail as ao, type ProcessStatsReport as ap, type ProcessStatsService as aq, type QuotaStoreFile as ar, type QuotaStoreOptions as as, type RegisterBrowserInput as at, type RegisterSessionInput as au, type RemainingQuota as av, type ResourceStats as aw, ResumeDisabledError as ax, SESSION_END_REASONS as ay, SESSION_ID_ENV as az, type SessionEventBus as b, sanitizeLabel as b$, type TaskVerification as b0, type TaskVerifySupervisor as b1, type TaskWriteResult as b2, type TokenPricing as b3, type UsageBucket as b4, type UsageComputeInput as b5, type UsageDetail as b6, type UsageSource as b7, type UserPresetsFile as b8, WORKSPACE_SLUG_ENV as b9, isProviderLimitError as bA, isTerminalActivityState as bB, listUserPresets as bC, loadQuotaStore as bD, loadUserPresets as bE, loadWorktreeIsolation as bF, matchesMessageFilter as bG, messageFrom as bH, mintMessageId as bI, normalizeCommand as bJ, normalizeWorktreeField as bK, parseAnthropicRateLimitHeaders as bL, parseTaskStatus as bM, parseWindow as bN, parseWorktreeIsolationMode as bO, pickUsageDetail as bP, policyToActivities as bQ, policyWatchesSession as bR, prToActivities as bS, projectSessionUsage as bT, readProfileQuota as bU, recordProfileQuota as bV, renderInboxDigest as bW, renderSessionMessage as bX, renderSessionMessages as bY, resolveRelation as bZ, rollupUsage as b_, WORKTREE_ISOLATION_ENV as ba, WORKTREE_PROVISION_CONCURRENCY_ENV as bb, type WorktreeDecision as bc, type WorktreeProvisionOutcome as bd, type WorktreeProvisionPhase as be, type WorktreeProvisionProgress as bf, type WorktreeProvisionRequest as bg, type WorktreeRequest as bh, activityCounts as bi, compactOutcome as bj, createProcessStatsService as bk, createSessionMessage as bl, createSupervisorTaskGateRunner as bm, createTaskLedger as bn, decideWorktreeIsolation as bo, deleteUserPreset as bp, deriveSessionOutcome as bq, deriveSessionUsage as br, escapeHumanPrompt as bs, escapeMessageBody as bt, filterActivities as bu, getProcessStatsService as bv, getUserPreset as bw, isClosedTaskStatus as bx, isKnownSessionEndReason as by, isMessageAllowed as bz, type SessionsRegistry as c, saveUserPreset as c0, trackWorktreeProvision as c1, turnToActivities as c2, userPresetsPath as c3, workflowToActivities as c4, type RecentSpawnConfig as c5, deriveRecentSpawnConfigs as c6, touchUserPreset as c7, userPresetSchema as c8, type SessionDescriptor as d, type SessionEndReason as e, type ActivityListFilter as f, type ActivityRecord as g, type ActivityTurnSession as h, type ActivityPrSession as i, type ActivityWorkflowRunSlice as j, type ActivityTaskSlice as k, type AgentsMdResolution as l, type SandboxAppServeSpec as m, type WorktreeField as n, type AgentSessionLike as o, type UsageReadContext as p, type UsageReadResult as q, type MessageKind as r, type SessionMessage as s, type AgentStreamEvent as t, type SessionUsage as u, type ToolCallRecord as v, type StatsSessionDescriptor as w, type ProcRow as x, type ProvisionInFlight as y, type ProvisionTracker as z };