@ordewell/core 0.4.23 → 0.5.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.
Files changed (45) hide show
  1. package/dist/{ITerminalRunner-CuNYBnqK.d.mts → ITerminalRunner-DXlJop_f.d.mts} +26 -1
  2. package/dist/{ITerminalRunner-Da1gIAFX.d.ts → ITerminalRunner-nupxhtPb.d.ts} +26 -1
  3. package/dist/{ModeResolver-Ob-ihQbI.d.ts → ModeResolver-BBVktyiP.d.ts} +1 -1
  4. package/dist/{ModeResolver-mtIWrLXn.d.mts → ModeResolver-D1-2xJsE.d.mts} +1 -1
  5. package/dist/Task-CfTjRQeS.d.mts +586 -0
  6. package/dist/Task-CfTjRQeS.d.ts +586 -0
  7. package/dist/{chunk-O2MFTRHA.mjs → chunk-GWPIYDQW.mjs} +29 -5
  8. package/dist/chunk-GWPIYDQW.mjs.map +1 -0
  9. package/dist/chunk-HQICT7HQ.mjs +90 -0
  10. package/dist/chunk-HQICT7HQ.mjs.map +1 -0
  11. package/dist/{chunk-T4KGVP4N.mjs → chunk-JVMDEHRQ.mjs} +1 -1
  12. package/dist/chunk-JVMDEHRQ.mjs.map +1 -0
  13. package/dist/{chunk-MKUTJDT2.mjs → chunk-XWOUIA6A.mjs} +2 -2
  14. package/dist/index.d.mts +589 -104
  15. package/dist/index.d.ts +589 -104
  16. package/dist/index.js +3320 -1155
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +3320 -1257
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/order-labels.d.mts +1 -1
  21. package/dist/order-labels.d.ts +1 -1
  22. package/dist/{parsing-BWfpLch9.d.mts → parsing-BJECTMOD.d.mts} +2 -2
  23. package/dist/{parsing-BJoHbA-t.d.ts → parsing-Bv3g2Tu_.d.ts} +2 -2
  24. package/dist/parsing.d.mts +3 -3
  25. package/dist/parsing.d.ts +3 -3
  26. package/dist/parsing.js.map +1 -1
  27. package/dist/parsing.mjs +2 -2
  28. package/dist/{plan-utils-s6VL8NDF.d.mts → plan-utils-B5oIZkuI.d.mts} +30 -5
  29. package/dist/{plan-utils-DJm2a7Zp.d.ts → plan-utils-Cy-RsBSV.d.ts} +30 -5
  30. package/dist/plan-utils.d.mts +3 -3
  31. package/dist/plan-utils.d.ts +3 -3
  32. package/dist/plan-utils.js.map +1 -1
  33. package/dist/plan-utils.mjs +2 -2
  34. package/dist/testing.d.mts +81 -3
  35. package/dist/testing.d.ts +81 -3
  36. package/dist/testing.js +152 -0
  37. package/dist/testing.js.map +1 -1
  38. package/dist/testing.mjs +136 -0
  39. package/dist/testing.mjs.map +1 -1
  40. package/package.json +1 -1
  41. package/dist/Task-4rDGr8Su.d.mts +0 -271
  42. package/dist/Task-4rDGr8Su.d.ts +0 -271
  43. package/dist/chunk-O2MFTRHA.mjs.map +0 -1
  44. package/dist/chunk-T4KGVP4N.mjs.map +0 -1
  45. /package/dist/{chunk-MKUTJDT2.mjs.map → chunk-XWOUIA6A.mjs.map} +0 -0
package/dist/index.d.ts CHANGED
@@ -1,17 +1,17 @@
1
- import { h as RunnerId, c as PlanStatus, L as LegacyPlanState, a as DiscoveredModel, T as Task, d as ResearchProgress, R as ResearchLogEntry, C as ConversationMessage, n as TaskSnapshot, Q as QueuedMessage, A as ActiveTaskSession, g as ResearchToolType, b as PlanState, v as Verdict } from './Task-4rDGr8Su.js';
2
- export { D as DiscoveredMode, M as Message, P as PlanModificationWarnings, e as ResearchStep, f as ResearchStepOutcome, S as StreamEvent, i as StreamStepEvent, j as StreamThinkingEvent, k as TaskMode, l as TaskModelAssignment, m as TaskOutputSummary, o as TaskStatus, p as TaskType, q as TaskWithParent, r as ThinkingBlock, U as UserPromptEntry, s as UserStep, V as ValidationCheck, t as ValidationContext, u as ValidationResult, w as VerificationCheck, x as addTaskToPlan, y as createEmptyPlan, z as createTask, B as emptyWarnings, E as flattenTasks, F as flattenTasksWithParents, G as migrateLegacyPlan, H as migratePlanState, I as migrateTask, J as removeTaskFromPlan, K as renumberTasks, N as updateTaskInPlan, O as validateModifiedPlan, W as warningsText } from './Task-4rDGr8Su.js';
3
- import { f as IFileSystem, R as ReadFileOpts, T as ToolOutcome, d as GrepOptions, c as GlobOptions, b as FindSymbolOptions, I as IConfig, A as AiProvider, r as ProviderModelLists, i as ITerminalSession, h as ITerminalRunner, w as RunnerRegistry, k as OrchestratorOption, u as RunnerInvocation, g as IPluginStore, v as RunnerPluginManifest, t as ResolveContext } from './ITerminalRunner-Da1gIAFX.js';
4
- export { a as AllProviderModels, C as CatalogModel, D as DiscoveryCommand, F as FetchAllProviderModelsOptions, G as GREP_DEFAULT_HEAD_LIMIT, e as GrepOutputMode, M as ModelCatalog, j as ModelShortcut, O as ORCHESTRATOR_SHORTCUTS, P as PluginCloneFn, l as PluginEntry, m as PluginFeatures, n as PluginMode, o as PluginModelDiscovery, p as PluginRunnerDef, q as ProviderCredentialSource, s as ProviderModelsResult, S as SEARCH_EXCLUSIONS, x as collectProviderCredentials, y as enabledRunners, z as fetchAllProviderModels, B as isReservedRunnerName, E as knownModelId, H as resolveModelShortcut, J as resolveProvider, K as toOrchestratorOptions } from './ITerminalRunner-Da1gIAFX.js';
1
+ import { B as RunnerId, t as PlanStatus, L as LegacyPlanState, j as IsolationMergeResult, I as IWorktreeIsolation, p as IsolationTaskStatus, P as PlanIsolation, R as RepoGroupLayout, a as DiscoveredModel, T as Task, w as ResearchProgress, v as ResearchLogEntry, C as ConversationMessage, O as TaskSnapshot, c as IsolationHandoff, G as TaskIsolation, q as IsolationView, Q as QueuedMessage, A as ActiveTaskSession, z as ResearchToolType, s as PlanState, a1 as Verdict } from './Task-CfTjRQeS.js';
2
+ export { D as DiscoveredMode, b as IsolationAvailability, d as IsolationHandoffRepo, e as IsolationInactiveReason, f as IsolationLandedTask, g as IsolationLanding, h as IsolationMergeBlock, i as IsolationMergeBlockReason, k as IsolationOutcome, l as IsolationRepo, m as IsolationRun, n as IsolationTaskRecord, o as IsolationTaskRepo, M as Message, r as PlanModificationWarnings, u as PreparedTask, x as ResearchStep, y as ResearchStepOutcome, S as StreamEvent, E as StreamStepEvent, F as StreamThinkingEvent, H as TaskIsolationState, J as TaskMode, K as TaskModelAssignment, N as TaskOutputSummary, U as TaskStatus, V as TaskType, W as TaskWithParent, X as ThinkingBlock, Y as UserPromptEntry, Z as UserStep, _ as ValidationCheck, $ as ValidationContext, a0 as ValidationResult, a2 as VerificationCheck, a3 as addTaskToPlan, a4 as createEmptyPlan, a5 as createTask, a6 as emptyWarnings, a7 as flattenTasks, a8 as flattenTasksWithParents, a9 as migrateLegacyPlan, aa as migratePlanState, ab as migrateTask, ac as removeTaskFromPlan, ad as renumberTasks, ae as updateTaskInPlan, af as validateModifiedPlan, ag as warningsText } from './Task-CfTjRQeS.js';
3
+ import { f as IFileSystem, R as ReadFileOpts, T as ToolOutcome, d as GrepOptions, c as GlobOptions, b as FindSymbolOptions, I as IConfig, A as AiProvider, r as ProviderModelLists, i as ITerminalSession, h as ITerminalRunner, w as RunnerRegistry, k as OrchestratorOption, u as RunnerInvocation, g as IPluginStore, v as RunnerPluginManifest, t as ResolveContext } from './ITerminalRunner-nupxhtPb.js';
4
+ export { a as AllProviderModels, C as CatalogModel, D as DiscoveryCommand, F as FetchAllProviderModelsOptions, G as GREP_DEFAULT_HEAD_LIMIT, e as GrepOutputMode, M as ModelCatalog, j as ModelShortcut, O as ORCHESTRATOR_SHORTCUTS, P as PluginCloneFn, l as PluginEntry, m as PluginFeatures, n as PluginMode, o as PluginModelDiscovery, p as PluginRunnerDef, q as ProviderCredentialSource, s as ProviderModelsResult, S as SEARCH_EXCLUSIONS, x as collectProviderCredentials, y as enabledRunners, z as fetchAllProviderModels, B as isReservedRunnerName, E as knownModelId, H as resolveModelShortcut, J as resolveProvider, K as toOrchestratorOptions } from './ITerminalRunner-nupxhtPb.js';
5
5
  import { I as IApproval, d as ApprovalRequest, a as ApprovalMode } from './ApprovalPolicy-BVhGdECT.js';
6
6
  export { A as ApprovalKind, b as ApprovalPolicy, c as ApprovalPolicyOptions, e as ApprovalSource, D as DENY_ALL } from './ApprovalPolicy-BVhGdECT.js';
7
- import { R as RunnerModeInfo } from './ModeResolver-Ob-ihQbI.js';
8
- export { M as ManifestLookup, b as buildModeGuide, f as filteredBuildModes, r as resolveDefaultMode, a as resolveTaskMode, c as runnerModesFrom } from './ModeResolver-Ob-ihQbI.js';
9
- import { T as TaskOp, c as SessionBroadcaster } from './plan-utils-DJm2a7Zp.js';
10
- export { A as ApplyTaskOpsResult, C as CHECKPOINT_TRUNCATE_LENGTH, S as SerializedPlan, a as SerializedTask, b as SerializedTaskStatus, d as SessionMessage, e as TaskRef, f as applyTaskOps, g as canMergeTasks, h as canSetDependencies, i as canSplitTask, j as classifyOutcome, k as dependencyCandidates, l as dependentsOf, m as executionSummary, p as parseTaskOpsJson, s as serializePlan, n as serializeTask, o as serializeTaskStatus, q as summarizeToolCall, t as textHasTaskOps, r as truncateCheckpointSummary } from './plan-utils-DJm2a7Zp.js';
7
+ import { R as RunnerModeInfo } from './ModeResolver-BBVktyiP.js';
8
+ export { M as ManifestLookup, b as buildModeGuide, f as filteredBuildModes, r as resolveDefaultMode, a as resolveTaskMode, c as runnerModesFrom } from './ModeResolver-BBVktyiP.js';
9
+ import { T as TaskOp, c as SessionBroadcaster, e as SessionNotice } from './plan-utils-Cy-RsBSV.js';
10
+ export { A as ApplyTaskOpsResult, C as CHECKPOINT_TRUNCATE_LENGTH, S as SerializedPlan, a as SerializedTask, b as SerializedTaskStatus, d as SessionMessage, f as TaskRef, g as applyTaskOps, h as canMergeTasks, i as canSetDependencies, j as canSplitTask, k as classifyOutcome, l as dependencyCandidates, m as dependentsOf, n as executionSummary, p as parseTaskOpsJson, s as serializePlan, o as serializeTask, q as serializeTaskStatus, r as summarizeToolCall, t as textHasTaskOps, u as truncateCheckpointSummary } from './plan-utils-Cy-RsBSV.js';
11
11
  import { ChildProcess } from 'child_process';
12
12
  import { EventEmitter } from 'events';
13
- import { b as PlanParseError } from './parsing-BJoHbA-t.js';
14
- export { P as PLAN_ENVELOPE_KEY, a as PartialPlanTask, T as TASK_OPS_ENVELOPE_KEY, c as TASK_QUERY_ENVELOPE_KEY, d as checkDepsResolve, e as checkImmutableLog, f as checkInProgress, g as checkNoCycles, h as checkUniqueIds, i as escapeControlCharsInStrings, j as extractJsonObject, k as extractObjectWithBalance, l as extractObjectsWithKey, m as looksLikePlanAttempt, p as parsePartialPlan, n as parsePlanJson, s as stripModelNoise, o as stripTrailingCommas, v as validatePlanModification } from './parsing-BJoHbA-t.js';
13
+ import { b as PlanParseError } from './parsing-Bv3g2Tu_.js';
14
+ export { P as PLAN_ENVELOPE_KEY, a as PartialPlanTask, T as TASK_OPS_ENVELOPE_KEY, c as TASK_QUERY_ENVELOPE_KEY, d as checkDepsResolve, e as checkImmutableLog, f as checkInProgress, g as checkNoCycles, h as checkUniqueIds, i as escapeControlCharsInStrings, j as extractJsonObject, k as extractObjectWithBalance, l as extractObjectsWithKey, m as looksLikePlanAttempt, p as parsePartialPlan, n as parsePlanJson, s as stripModelNoise, o as stripTrailingCommas, v as validatePlanModification } from './parsing-Bv3g2Tu_.js';
15
15
  export { resolveOrderLabel, taskOrderLabel } from './order-labels.js';
16
16
 
17
17
  interface SessionMeta {
@@ -523,6 +523,10 @@ declare abstract class BaseConfig implements IConfig {
523
523
  get researchMaxFileSize(): number;
524
524
  get planMapEnabled(): boolean;
525
525
  get autonomousMode(): boolean;
526
+ get worktreeIsolation(): boolean;
527
+ get worktreeSetupCommand(): string | undefined;
528
+ get workspaceRepos(): string[];
529
+ get worktreeLinks(): string[];
526
530
  get approvalMode(): ApprovalMode;
527
531
  get approvalPreApproved(): string[];
528
532
  protected static detectProvider(fallback: AiProvider): AiProvider;
@@ -553,6 +557,64 @@ interface INotification {
553
557
  confirm(msg: string, options: string[]): Promise<string | undefined>;
554
558
  }
555
559
 
560
+ /**
561
+ * How "Merge all" went, in words every surface shares. `group` is whether the
562
+ * run spans more than a lone repo at `.`; only a merge into every repository
563
+ * has something to say about that.
564
+ */
565
+ declare function describeMergeResult(result: IsolationMergeResult, branch: string, group: boolean): {
566
+ level: 'info' | 'warn' | 'error';
567
+ message: string;
568
+ };
569
+
570
+ type GitExecFn = (file: string, args: string[], opts: {
571
+ cwd?: string;
572
+ env: NodeJS.ProcessEnv;
573
+ }) => Promise<{
574
+ stdout: string;
575
+ stderr: string;
576
+ }>;
577
+ interface WorktreeIsolationDeps {
578
+ config: Pick<IConfig, 'worktreeIsolation' | 'worktreeSetupCommand' | 'workspaceRepos' | 'worktreeLinks'>;
579
+ /** Test seam: every git invocation goes through this. */
580
+ execFileImpl?: GitExecFn;
581
+ resolvePath?: () => Promise<string>;
582
+ platform?: NodeJS.Platform;
583
+ mintRunId?: () => string;
584
+ }
585
+ declare function createWorktreeIsolation(deps: WorktreeIsolationDeps): IWorktreeIsolation;
586
+
587
+ /** A task record as ADR-0013 persisted it, for one repository. */
588
+ interface Adr0013TaskRecord {
589
+ taskId: string;
590
+ order: number;
591
+ title: string;
592
+ branch: string;
593
+ worktree: string;
594
+ status: IsolationTaskStatus;
595
+ linked: string[];
596
+ }
597
+ /** A run as ADR-0013 persisted it (0.4.23): one repository, its refs on the run itself. */
598
+ interface Adr0013IsolationRun {
599
+ id: string;
600
+ workspaceRoot: string;
601
+ baseRef: string;
602
+ baseBranch?: string;
603
+ integrationBranch: string;
604
+ tasks: Record<string, Adr0013TaskRecord>;
605
+ }
606
+ interface Adr0013PlanIsolation {
607
+ run: Adr0013IsolationRun;
608
+ resolvers: Record<string, string>;
609
+ }
610
+ /**
611
+ * A persisted plan isolation in today's shape. A run saved in the ADR-0013
612
+ * format — recognised by its own `integrationBranch` — becomes a group of one
613
+ * at `.`, keeping its branches, so a session saved by 0.4.23 resumes and hands
614
+ * off exactly as it would have.
615
+ */
616
+ declare function migratePlanIsolation(state: PlanIsolation | Adr0013PlanIsolation): PlanIsolation;
617
+
556
618
  interface ILogger {
557
619
  warn(scope: string, message: string, err?: unknown): void;
558
620
  }
@@ -628,9 +690,75 @@ interface PlannerRuntimeToggles {
628
690
  * every planner signature used to carry positionally — where a thirteenth
629
691
  * parameter was the only place left to put a new toggle.
630
692
  */
693
+ /**
694
+ * Where the run's tasks will work: `false` in the shared workspace root;
695
+ * otherwise each in its own worktree of every repo of the group (ADR-0013,
696
+ * ADR-0014), which is what decides if tasks on the same file must be ordered.
697
+ */
698
+ type IsolatedExecution = false | RepoGroupLayout;
631
699
  interface PlannerModes {
632
700
  autonomousDefault: boolean;
633
701
  verification: boolean;
702
+ /** Not a toggle: a fact about the run. */
703
+ isolatedExecution: IsolatedExecution;
704
+ }
705
+
706
+ /** Context of one task attempt, enough to locate its transcript. */
707
+ interface TranscriptQuery {
708
+ runner: string;
709
+ /** The task's working directory (the agent's cwd at spawn). */
710
+ cwd: string;
711
+ /** When the agent process started, ISO — used to reject older sessions. */
712
+ startedAt?: string;
713
+ /**
714
+ * The task's completion marker UUID. The prompt carries it, so a transcript
715
+ * that does not contain it belongs to some other task — parallel attempts in
716
+ * one cwd are otherwise indistinguishable by directory and recency.
717
+ */
718
+ marker: string;
719
+ }
720
+ /** Reads the agent's own session transcript for a task's final answer. */
721
+ interface TranscriptReader {
722
+ /** Last assistant-authored prose, clamped to `maxChars` from the end, or null. */
723
+ finalAssistantText(query: TranscriptQuery, maxChars?: number): Promise<string | null>;
724
+ }
725
+ /** The part of a task attempt its output is looked up by. */
726
+ interface TaskOutputAttempt {
727
+ readonly taskId: string;
728
+ readonly runner: string;
729
+ /** Null when the attempt ended before its working directory was settled. */
730
+ readonly cwd: string | null;
731
+ readonly startedAt: string;
732
+ /** The completion marker UUID the attempt's prompt carried. */
733
+ readonly completionMarker: string;
734
+ }
735
+ interface LiveTailOptions {
736
+ maxLines: number;
737
+ /** A previous {@link LiveTail.nextOffset}: only output after it is rendered. */
738
+ sinceOffset?: number;
739
+ }
740
+ interface LiveTail {
741
+ /** Clean terminal render, not raw ANSI. */
742
+ text: string;
743
+ /** Total output received so far; pass it back as `sinceOffset` to read only what follows. */
744
+ nextOffset: number;
745
+ running: boolean;
746
+ }
747
+ /**
748
+ * The one owner of a task attempt's output: what it printed while running
749
+ * and what it answered once it ended.
750
+ */
751
+ interface TaskOutputSource {
752
+ /** Start capturing a session; replaces any earlier capture of the task. */
753
+ attach(taskId: string, session: ITerminalSession): void;
754
+ /** Stop capturing the task's session; what was captured stays readable. */
755
+ detach(taskId: string): void;
756
+ /** Drop every capture (a new plan reuses task ids). */
757
+ reset(): void;
758
+ /** The attempt's final answer: its transcript first, the clean terminal render otherwise. */
759
+ finalText(attempt: TaskOutputAttempt, doneToken: string): Promise<string>;
760
+ /** Recent output of the task's latest capture, or null if it never had one. */
761
+ liveTail(taskId: string, opts: LiveTailOptions): LiveTail | null;
634
762
  }
635
763
 
636
764
  /**
@@ -648,8 +776,19 @@ interface PlannerModes {
648
776
  * lives in PlanRepair alongside the plan and task-ops envelopes.
649
777
  */
650
778
  /** The long fields a query may ask for. Everything else is already in the plan block. */
651
- declare const TASK_QUERY_FIELDS: readonly ["description", "prompt", "userSteps", "verdict", "outputSummary", "userStoriesCovered"];
779
+ declare const TASK_QUERY_FIELDS: readonly ["description", "prompt", "userSteps", "verdict", "outputSummary", "userStoriesCovered", "output"];
652
780
  type TaskQueryField = typeof TASK_QUERY_FIELDS[number];
781
+ /** Lines a `output` read returns when the query sets no count. */
782
+ declare const OUTPUT_LINES_DEFAULT = 80;
783
+ /** Hard cap on `outputLines` — the tail read is bounded however the planner asks. */
784
+ declare const OUTPUT_LINES_MAX = 400;
785
+ /**
786
+ * Character budget the rendered answer converges to. A running output tail is
787
+ * the only part with no natural bound — a runner may print for minutes — so
788
+ * the tail absorbs the clamp; everything else (task bodies, the catalog cap)
789
+ * is bounded upstream.
790
+ */
791
+ declare const TASK_QUERY_ANSWER_MAX_CHARS = 20000;
653
792
  interface TaskQuery {
654
793
  /** Task references to read in full — an id, "#order", a bare order, or a title. */
655
794
  tasks: string[];
@@ -657,6 +796,10 @@ interface TaskQuery {
657
796
  fields?: TaskQueryField[];
658
797
  /** Also return the full runner/model catalog with labels, variants and mode descriptions. */
659
798
  catalog: boolean;
799
+ /** Line count for `output` reads. Clamped at render to [1, {@link OUTPUT_LINES_MAX}]. */
800
+ outputLines?: number;
801
+ /** A previous answer's next offset: the output tail then covers only what came after it. */
802
+ outputSince?: number;
660
803
  }
661
804
  declare function textHasTaskQuery(text: string): boolean;
662
805
  /** Parse a `{"taskQuery":...}` reply. Throws PlanParseError when the JSON is unusable. */
@@ -685,12 +828,19 @@ declare const TASK_QUERY_ANSWER_OR_OPS: string;
685
828
  declare const TASK_QUERY_PROTOCOL: string[];
686
829
  /** The one-line reminder the per-turn plan block carries, so the protocol has a single owner. */
687
830
  declare const TASK_QUERY_REMINDER = "- To READ a task in full (prompt, user steps, verdict, output, user stories) or the whole model/mode catalog before editing, reply with ONLY {\"taskQuery\":{\"tasks\":[\"<id or #order>\"],\"catalog\":true}} \u2014 it changes nothing, and you then reply again with your ops.";
831
+ /**
832
+ * Where an `output` read is answered from: the orchestrator's live capture of
833
+ * the task's latest attempt. Injected so this module stays planner-side and
834
+ * never imports the orchestrator.
835
+ */
836
+ type LiveOutputLookup = (taskId: string, opts: LiveTailOptions) => LiveTail | null;
688
837
  /**
689
838
  * Render the injected answer to one read. Never persisted — it is context for
690
839
  * the planner's next reply and nothing else — so it is built fresh from live
691
- * state on every query rather than cached.
840
+ * state on every query rather than cached. `liveOutput` backs the `output`
841
+ * field; omitting it reads as "nothing captured".
692
842
  */
693
- declare function renderTaskQueryAnswer(query: TaskQuery, tasks: Task[], catalog: TaskQueryCatalog): string;
843
+ declare function renderTaskQueryAnswer(query: TaskQuery, tasks: Task[], catalog: TaskQueryCatalog, liveOutput?: LiveOutputLookup): string;
694
844
 
695
845
  declare abstract class AbstractTerminalSession implements ITerminalSession {
696
846
  id: string;
@@ -967,6 +1117,7 @@ declare class HeadlessRunner extends AbstractRunner<HeadlessSession> {
967
1117
  private hasScriptCmd;
968
1118
  private resolvePath;
969
1119
  private launchDeps;
1120
+ private spawnCount;
970
1121
  /**
971
1122
  * Session shape: a piped subprocess is not a terminal, so runners get their
972
1123
  * non-interactive subcommand. The VS Code runner owns a pseudoterminal and
@@ -976,6 +1127,13 @@ declare class HeadlessRunner extends AbstractRunner<HeadlessSession> {
976
1127
  */
977
1128
  protected readonly defaultInteractive: boolean;
978
1129
  constructor(deps?: HeadlessRunnerDeps);
1130
+ /**
1131
+ * Unique per spawn, not per task: a retry reuses its task id and ids often
1132
+ * share a prefix, and a shared registry key let the old attempt's exit
1133
+ * unregister the new one. Unlike TmuxRunner, nothing outside this process
1134
+ * keys on the id, so a counter is enough to scope it to the attempt.
1135
+ */
1136
+ protected nextSessionId(taskId: string): string;
979
1137
  protected createSession(id: string, taskId: string): HeadlessSession;
980
1138
  /** Everything up to, but not including, spawning — so a surface that owns its own child reaches the same decisions. */
981
1139
  protected prepareLaunch(opts: RunnerSpawnOptions, ptyOptions?: PtyWrapOptions): Promise<PreparedLaunch>;
@@ -1233,6 +1391,8 @@ interface ConversationRequest {
1233
1391
  runnerModes?: Record<RunnerId, RunnerModeInfo[]>;
1234
1392
  autonomousDefault?: boolean;
1235
1393
  verificationEnabled?: boolean;
1394
+ /** Where tasks will run (ADR-0013, ADR-0014): in worktrees, the prompt drops file-overlap ordering and describes a repo group. */
1395
+ isolatedExecution?: IsolatedExecution;
1236
1396
  signal?: AbortSignal;
1237
1397
  /**
1238
1398
  * Persisted dialogue to seed a resumed conversation (session reload). The
@@ -1308,6 +1468,13 @@ interface IAiService {
1308
1468
  * `continueConversation`, restart instead so the new model takes effect.
1309
1469
  */
1310
1470
  conversationMatchesConfig?(): boolean;
1471
+ /**
1472
+ * Optional: prune bulky raw tool output out of the live conversation, in
1473
+ * place, keeping subagent digests. Returns the characters removed. Absent
1474
+ * where the backend holds no tool transcript to prune (a harness planner's
1475
+ * agent owns its own context).
1476
+ */
1477
+ pruneContext?(): number;
1311
1478
  /**
1312
1479
  * One-shot research + plan for non-conversational surfaces (CLI `plan --goal`,
1313
1480
  * web REST). Never asks questions — it plans with what it can find.
@@ -1391,6 +1558,7 @@ declare abstract class BaseAiService {
1391
1558
  abstract reset(): void;
1392
1559
  abstract ensureInit(): void;
1393
1560
  hasActiveConversation(): boolean;
1561
+ pruneContext(): number;
1394
1562
  protected startAbortScope(callerSignal?: AbortSignal): AbortSignal | undefined;
1395
1563
  protected stopAbortScope(): void;
1396
1564
  sendPlanningPrompt(prompt: string, runners: RunnerId[], runnerModes?: Record<RunnerId, RunnerModeInfo[]>, autonomousDefault?: boolean): Promise<Task[]>;
@@ -1626,10 +1794,39 @@ interface OrchestratorObserver {
1626
1794
  taskTitle: string;
1627
1795
  summary: string;
1628
1796
  }): void;
1797
+ /** The isolation run record changed and should be persisted with the plan. */
1798
+ onIsolationChanged?(): void;
1799
+ /** A run did not start: the tree is dirty, and the user picks stash or no isolation. `repos` names the dirty repos of a group. */
1800
+ onIsolationBlocked?(data: {
1801
+ reason: 'dirty';
1802
+ repos: string[];
1803
+ }): void;
1804
+ /** An isolated run settled; emitted before `onExecutionComplete`, which surfaces treat as terminal. */
1805
+ onIsolationHandoff?(handoff: IsolationHandoff): void;
1806
+ /**
1807
+ * What a run says about how it isolates — the fallback to the workspace root,
1808
+ * shared paths, copies, a stash. Beside the notification channel, which a
1809
+ * daemon may leave unwired, so a surface without toasts can still show it.
1810
+ */
1811
+ onIsolationNotice?(data: {
1812
+ level: 'info' | 'warn';
1813
+ message: string;
1814
+ }): void;
1815
+ }
1816
+ type AttemptPhase = 'starting' | 'running' | 'integrating';
1817
+ /** Read-only view of a task's live attempt. */
1818
+ interface TaskAttemptSnapshot {
1819
+ taskId: string;
1820
+ attempt: number;
1821
+ phase: AttemptPhase;
1822
+ sessionId: string | null;
1823
+ runner: string;
1824
+ cwd: string | null;
1825
+ startedAt: string;
1629
1826
  }
1630
1827
  /**
1631
1828
  * The pure scheduler. Owns execution state (`running`, `planStatus`,
1632
- * `reviewApproved`, `activeTaskSessions`, `messageQueue`) and the verifier.
1829
+ * `reviewApproved`, the live task attempts, `messageQueue`) and the verifier.
1633
1830
  * All task-shaped state — the plan tree, the flat index, the completed set
1634
1831
  * — lives in {@link PlanStore}, injected at construction. The orchestrator
1635
1832
  * calls `store.markCompleted(id)` / `store.markFailed(id)` instead of mutating
@@ -1641,15 +1838,16 @@ declare class TaskOrchestrator {
1641
1838
  private config;
1642
1839
  private notifications;
1643
1840
  private terminalRunner;
1841
+ private output;
1644
1842
  private store;
1645
- private activeTaskSessions;
1646
- private startingTaskIds;
1843
+ private attempts;
1647
1844
  private verifier;
1648
1845
  private running;
1649
1846
  private planStatus;
1650
1847
  private messageQueue;
1651
1848
  private reviewApproved;
1652
1849
  private retryCounts;
1850
+ private spawnCounts;
1653
1851
  /**
1654
1852
  * Tasks pulled out of auto-scheduling (user-cancelled or failed to spawn).
1655
1853
  * They stay 'pending' — "not executed" — but the scheduler skips them until
@@ -1657,18 +1855,33 @@ declare class TaskOrchestrator {
1657
1855
  * task whose spawn always throws.
1658
1856
  */
1659
1857
  private onHold;
1858
+ private isolation;
1859
+ /** The plan's isolation run (ADR-0013). Outlives one run: a resumed plan continues it. */
1860
+ private isolationRun;
1861
+ /** Copied paths already reported for the current run: every task gets the same copies. */
1862
+ private reportedCopies;
1863
+ /**
1864
+ * How the open run executes; null while no run is open. A run is one
1865
+ * Execute-Plan or one manual task run, from its start until it settles or
1866
+ * is stopped.
1867
+ */
1868
+ private runMode;
1869
+ /** Resolver task id → the conflicted task it resolves; see {@link linkConflictResolver}. */
1870
+ private resolvers;
1871
+ private opening;
1872
+ /** The start a dirty tree turned away, replayed once the user chooses how to go on. */
1873
+ private blockedStart;
1874
+ /** The dirty repos behind {@link blockedStart}, for the stash notice. */
1875
+ private blockedRepos;
1660
1876
  private registry;
1661
1877
  private workspaceRootFn;
1662
1878
  private observers;
1663
1879
  private tddEnabled;
1664
- /**
1665
- * Spawn context per active task, for locating the agent's own session
1666
- * transcript at verdict time (issue #16). Cleared with the session entry.
1667
- */
1668
- private taskSpawnContext;
1669
- constructor(config: IConfig, notifications: INotification, terminalRunner: ITerminalRunner, store?: PlanStore);
1880
+ constructor(config: IConfig, notifications: INotification, terminalRunner: ITerminalRunner, store?: PlanStore, output?: TaskOutputSource, isolation?: IWorktreeIsolation);
1670
1881
  /** Advisory silence timestamp for a task's live runner, or null if not idle. */
1671
1882
  getIdleSince(taskId: string): string | null;
1883
+ /** Recent clean output of a task's latest attempt, running or ended; null if it never ran. */
1884
+ getLiveOutput(taskId: string, opts: LiveTailOptions): LiveTail | null;
1672
1885
  get storeInstance(): PlanStore;
1673
1886
  setWorkspaceRoot(fn: () => string): void;
1674
1887
  setRegistry(registry: RunnerRegistry): void;
@@ -1694,8 +1907,41 @@ declare class TaskOrchestrator {
1694
1907
  get isReviewApproved(): boolean;
1695
1908
  get status(): 'approved' | 'running' | 'completed';
1696
1909
  get activeTaskIds(): string[];
1910
+ /** Task id → session id of every attempt whose runner is up; a spawn in flight has no session yet. */
1697
1911
  get activeSessionMap(): Map<string, string>;
1912
+ getAttempt(taskId: string): TaskAttemptSnapshot | undefined;
1913
+ /** Where a task's isolated work stands; null when the plan has no isolation run to speak of. */
1914
+ getTaskIsolation(taskId: string): TaskIsolation | null;
1915
+ /**
1916
+ * The plan's isolation as a surface shows it, for one with no stream to have
1917
+ * told it — a reconnected webview, a session just loaded. Null without a run.
1918
+ */
1919
+ isolationView(): IsolationView | null;
1920
+ getAttemptSession(taskId: string): ITerminalSession | undefined;
1698
1921
  get queuedCount(): number;
1922
+ /** A run is waiting on the user to stash or to go on without isolation. */
1923
+ get awaitingIsolationChoice(): boolean;
1924
+ /**
1925
+ * Take over a plan's persisted isolation, or none for a plan that has not
1926
+ * isolated yet. Whatever a crashed process left behind for the run — a
1927
+ * worktree still marked active, a directory no record owns — is pruned,
1928
+ * while kept, failed and conflicted worktrees stay for the user.
1929
+ *
1930
+ * Deliberately silent on the observer: adopting is not a change to persist,
1931
+ * and a host that adopts without persisting (VS Code's restore) would
1932
+ * otherwise write a new session file on every reload.
1933
+ */
1934
+ adoptIsolation(state: PlanIsolation | null): Promise<void>;
1935
+ reviewRunDiff(): Promise<string>;
1936
+ /** "Merge all": the run's integration branches into whatever the user has checked out, in every repo or none. */
1937
+ mergeRun(): Promise<IsolationMergeResult>;
1938
+ /** Worktrees and task branches go; the integration branch and the record stay for review and merge. */
1939
+ cleanupRun(): Promise<void>;
1940
+ /** The run and everything it made go, and the plan forgets it; the next run starts afresh. */
1941
+ discardRun(): Promise<void>;
1942
+ private requireRun;
1943
+ /** What the plan persists of isolated execution; null when no run ever isolated. */
1944
+ get isolationRecord(): PlanIsolation | null;
1699
1945
  queueMessage(text: string): void;
1700
1946
  getQueuedMessages(): QueuedMessage[];
1701
1947
  setQueuedMessages(messages: QueuedMessage[]): void;
@@ -1718,7 +1964,42 @@ declare class TaskOrchestrator {
1718
1964
  stop(): void;
1719
1965
  onUserTaskComplete(taskId: string): Promise<void>;
1720
1966
  private onVerdict;
1967
+ /**
1968
+ * Merge a passed attempt's worktree. The attempt stays live while it waits on
1969
+ * the module's merge queue, so a cancel, retry or stop in that window still
1970
+ * wins, and nothing counts the task as done before its work is on the
1971
+ * integration branch.
1972
+ */
1973
+ private integrate;
1974
+ /** A worktree whose work is not on the integration branch yet. */
1975
+ private hasUnlandedWork;
1976
+ private integrateWork;
1977
+ /** Settle a task whose work passed, by what its integration reported. */
1978
+ private landPassed;
1979
+ private landUnmerged;
1980
+ /**
1981
+ * Mark which added task resolves which conflict. The resolver merges the
1982
+ * conflicted task's branch by hand in its own worktree; once that lands, the
1983
+ * conflicted task's branch is already on the integration branch and it can
1984
+ * land in turn — through the same merge, so a resolver that did not really
1985
+ * bring it along conflicts again instead of being taken at its word.
1986
+ */
1987
+ linkConflictResolver(resolverId: string, conflictedId: string): void;
1988
+ private landResolved;
1989
+ /**
1990
+ * Let go of a task's worktree. `keep` leaves it and its branch for
1991
+ * inspection; otherwise both go. A merge still in flight finishes first, so
1992
+ * the worktree is never torn down underneath it.
1993
+ */
1994
+ private releaseWorktree;
1721
1995
  getReadyTasks(): Task[];
1996
+ /**
1997
+ * In an isolated run a dependency is met once its work is on the integration
1998
+ * branch, not merely once it passed: the dependent's worktree is cut from
1999
+ * that branch, so starting earlier would hand it a tree without the work it
2000
+ * depends on.
2001
+ */
2002
+ private dependencyMet;
1722
2003
  isBlocked(task: Task): boolean;
1723
2004
  /**
1724
2005
  * Cancel a running (or scheduled) task: kill its session and return it to
@@ -1728,12 +2009,10 @@ declare class TaskOrchestrator {
1728
2009
  cancelTask(taskId: string): Promise<void>;
1729
2010
  /**
1730
2011
  * Let go of a task that is leaving the plan. A live runner is cancelled
1731
- * through {@link cancelTask} so the verifier generation is bumped before the
1732
- * process dies; a spawn still in flight is dropped from `startingTaskIds`,
2012
+ * through {@link cancelTask}; a spawn still in flight just loses its attempt,
1733
2013
  * which is what makes {@link startTask} kill the session it is about to
1734
- * receive. The id's remaining bookkeeping goes too — a hold or retry count
1735
- * kept for a task that no longer exists would be inherited by nothing and
1736
- * leaves `isRunning` reading true forever.
2014
+ * receive. The id's cross-attempt bookkeeping goes too — a hold or retry
2015
+ * count kept for a task that no longer exists would be inherited by nothing.
1737
2016
  */
1738
2017
  releaseTask(taskId: string): Promise<void>;
1739
2018
  markTaskComplete(taskId: string): Promise<void>;
@@ -1779,6 +2058,72 @@ declare class TaskOrchestrator {
1779
2058
  };
1780
2059
  tick(): Promise<void>;
1781
2060
  private startTask;
2061
+ /**
2062
+ * A spawn whose attempt ended while it was in flight: kill what it produced
2063
+ * and take back the claim it made. Only the claim — whatever ended the
2064
+ * attempt may have decided the task since (mark complete, cancel, retry).
2065
+ */
2066
+ private abandonSpawn;
2067
+ /**
2068
+ * The one place an attempt's working directory is decided. Async so a
2069
+ * per-attempt workspace (worktree isolation, #12) can be prepared here.
2070
+ */
2071
+ private resolveAttemptCwd;
2072
+ private tell;
2073
+ private reportCopies;
2074
+ /**
2075
+ * Open a run if none is: decide once, at its start, whether it executes in
2076
+ * worktrees. `resume` is what a dirty tree parks until the user chooses how
2077
+ * to go on. Resolves false when the run did not start.
2078
+ */
2079
+ private openRun;
2080
+ /**
2081
+ * Whether the run in force, or else the next one, gives each task its own
2082
+ * worktrees, and of which repo group — what the planner is told, since it
2083
+ * decides whether tasks on the same file have to be ordered and which shared
2084
+ * paths two tasks must not edit at once. A tree that would block counts as
2085
+ * not isolating: the user may yet run without isolation, and ordering is
2086
+ * the safe rule then.
2087
+ */
2088
+ plannerIsolation(): Promise<IsolatedExecution>;
2089
+ private decideRun;
2090
+ private activate;
2091
+ /**
2092
+ * The plan's run carries on while anything has landed on its integration
2093
+ * branch: a resumed plan's dependents must start from a tip that holds their
2094
+ * predecessors' work, and a fresh branch from the checked-out commit does not.
2095
+ */
2096
+ private continuableRun;
2097
+ /**
2098
+ * A run with nothing landed holds only superseded attempts, so it goes whole.
2099
+ * One that cannot be continued for another reason — it ran from a different
2100
+ * workspace path — keeps its integration branch: only the user gives landed
2101
+ * work up.
2102
+ */
2103
+ private mintRun;
2104
+ /** Close the open run. An isolated one hands its integration branch over for review. */
2105
+ private closeRun;
2106
+ /**
2107
+ * Replay the start a dirty tree turned away. `stash` puts the user's tracked
2108
+ * changes on the git stash first, so the run isolates; `shared` runs this one
2109
+ * run in the workspace root, knowingly.
2110
+ */
2111
+ continueBlockedRun(how: 'stash' | 'shared'): Promise<void>;
2112
+ /**
2113
+ * The one way an attempt ends. Dropping the record is itself what
2114
+ * invalidates a spawn still in flight ({@link startTask} kills the session
2115
+ * it receives for an attempt that is no longer current).
2116
+ *
2117
+ * A user interruption also bumps the verifier's generation *before* stopping
2118
+ * the runner: some runners (e.g. tmux) fire onExit synchronously from
2119
+ * stop(), and if that exit reaches VerdictEngine under the still-valid
2120
+ * generation it delivers a verdict that marks the task 'completed' for one
2121
+ * tick — long enough for the scheduler to start a dependent task. A verdict
2122
+ * leaves the runner up so its terminal stays readable, and stop/load reset
2123
+ * the whole verifier themselves.
2124
+ */
2125
+ private endAttempt;
2126
+ private endAllAttempts;
1782
2127
  }
1783
2128
 
1784
2129
  /**
@@ -1821,6 +2166,8 @@ interface ModifyDuringExecutionRequest {
1821
2166
  runnerModes?: Record<RunnerId, RunnerModeInfo[]>;
1822
2167
  autonomousDefault?: boolean;
1823
2168
  perRunnerAllowlist?: Partial<Record<RunnerId, string[]>>;
2169
+ /** Where the run in force puts each task (ADR-0013, ADR-0014). */
2170
+ isolatedExecution?: IsolatedExecution;
1824
2171
  }
1825
2172
  interface ModifyDuringExecutionResult {
1826
2173
  pendingTasks: Task[];
@@ -2333,6 +2680,37 @@ declare function mapAgentTool(name: string): MappedTool;
2333
2680
  */
2334
2681
  declare function normalizeAgentArgs(tool: ResearchToolType, args: Record<string, unknown>): Record<string, unknown>;
2335
2682
 
2683
+ /**
2684
+ * A conversation edit (rewind, fork) the conversation refused because the
2685
+ * request itself is wrong — no such message, nothing to fork. Transport-agnostic
2686
+ * like `PlanEditError`: a route maps it to a status, core carries none.
2687
+ */
2688
+ declare class ConversationEditError extends Error {
2689
+ constructor(message: string);
2690
+ }
2691
+ /**
2692
+ * Refused because a planner turn is in flight. Distinct from a bad request:
2693
+ * the same call succeeds once the turn settles, and the turn would otherwise
2694
+ * append its reply to a transcript that no longer holds the message it
2695
+ * answers.
2696
+ */
2697
+ declare class ConversationBusyError extends ConversationEditError {
2698
+ constructor(operation: string);
2699
+ }
2700
+ /** A user message the conversation can be rewound to just before. */
2701
+ interface RewindTarget {
2702
+ /** Position in the transcript — what {@link PlannerConversation.rewind} takes. */
2703
+ index: number;
2704
+ preview: string;
2705
+ timestamp: string;
2706
+ }
2707
+ /** What a compaction left behind. */
2708
+ interface ConversationCompaction {
2709
+ summary: string;
2710
+ /** Transcript entries kept verbatim after the summary entry. */
2711
+ keptMessages: number;
2712
+ }
2713
+
2336
2714
  declare const BUILTIN_SKILL_NAMES: readonly ["grilling", "to-spec", "improve-codebase-architecture"];
2337
2715
  interface SkillMetadata {
2338
2716
  name: string;
@@ -2423,6 +2801,12 @@ declare function resolveSkillInvocation(text: string, skillsService: Pick<Skills
2423
2801
  * settings (tdd, verification) are read live via the `settings` callback so a toggle
2424
2802
  * between generate and execute takes effect.
2425
2803
  */
2804
+ /** Where a fork landed: the new session's id, and what adopting it needs. */
2805
+ interface ConversationFork {
2806
+ sessionId: string;
2807
+ goal: string;
2808
+ workspace: string;
2809
+ }
2426
2810
  interface SessionDeps {
2427
2811
  config: IConfig;
2428
2812
  notifications: INotification;
@@ -2434,6 +2818,8 @@ interface SessionDeps {
2434
2818
  fsAdapter: IFileSystem;
2435
2819
  /** Emits plan-lifecycle events to the surface. Transport-agnostic. */
2436
2820
  broadcast: SessionBroadcaster;
2821
+ /** Where {@link SessionNotice}s go, for a host whose `notifications` are not seen by the user. */
2822
+ onNotice?: (notice: SessionNotice) => void;
2437
2823
  /** Shared across sessions — sole producer of model catalogs and routing lists. */
2438
2824
  modelResolver: ModelResolver;
2439
2825
  /** Live runtime settings (tdd, verification). Read at each operation that needs them. */
@@ -2457,6 +2843,17 @@ interface SessionDeps {
2457
2843
  * over the session's workspace root.
2458
2844
  */
2459
2845
  skillsService?: SkillsService;
2846
+ /**
2847
+ * Where a task's output and final answer are read. Defaults to the agents'
2848
+ * own transcripts under the user's home; tests inject one that never
2849
+ * touches the disk.
2850
+ */
2851
+ taskOutput?: TaskOutputSource;
2852
+ /**
2853
+ * Git worktree isolation (ADR-0013). Defaults to git itself, gated by
2854
+ * `config.worktreeIsolation`; tests inject `FakeWorktreeIsolation`.
2855
+ */
2856
+ isolation?: IWorktreeIsolation;
2460
2857
  }
2461
2858
  /**
2462
2859
  * The per-session execution stack — deepened from a wiring bag into the
@@ -2486,6 +2883,7 @@ declare class Session {
2486
2883
  private goal;
2487
2884
  private workspace;
2488
2885
  private broadcast;
2886
+ private onNotice?;
2489
2887
  private modelResolver;
2490
2888
  private fsAdapter;
2491
2889
  private approvals;
@@ -2498,6 +2896,7 @@ declare class Session {
2498
2896
  private readonly hostSessionId?;
2499
2897
  private currentSessionId;
2500
2898
  private readonly skillsService;
2899
+ private readonly conversation;
2501
2900
  constructor(deps: SessionDeps);
2502
2901
  /**
2503
2902
  * The planner transport for the provider configured *right now* (ADR-0009).
@@ -2531,6 +2930,7 @@ declare class Session {
2531
2930
  get planTasks(): Task[];
2532
2931
  private attachObserver;
2533
2932
  private buildObserver;
2933
+ private broadcastStatus;
2534
2934
  private translateProgress;
2535
2935
  /** Persists PlanStore state to disk. PlanStore is the single authority;
2536
2936
  * LegacyPlanState.tasks is populated only here, at persist time. */
@@ -2541,8 +2941,8 @@ declare class Session {
2541
2941
  * A new plan starts from zero: drop the live planner conversation and every
2542
2942
  * task, log, and queued message left over from a previous plan on this
2543
2943
  * Session. Without this, a long-lived Session (VS Code hosts exactly one)
2544
- * leaks the previous session's tasks into `planContextBlock()` — the model
2545
- * is told they are the CURRENT plan and re-presents them as its draft.
2944
+ * leaks the previous session's tasks into the per-turn plan block — the
2945
+ * model is told they are the CURRENT plan and re-presents them as its draft.
2546
2946
  */
2547
2947
  private beginFreshPlan;
2548
2948
  /**
@@ -2611,8 +3011,8 @@ declare class Session {
2611
3011
  generatePlan(goal: string, runners: RunnerId[], options?: GeneratePlanOptions): Promise<LegacyPlanState>;
2612
3012
  /**
2613
3013
  * Kick off the planner conversation (ADR-0002): research + the first planner
2614
- * message. The AI service retains the tool-use history; the Session persists
2615
- * the user/assistant dialogue on the plan state.
3014
+ * message. The conversation itself — transcript, live model context, turn
3015
+ * settlement — is {@link PlannerConversation}'s; the Session hosts it.
2616
3016
  */
2617
3017
  startPlanning(goal: string, runners: RunnerId[], options?: GeneratePlanOptions): Promise<LegacyPlanState>;
2618
3018
  /**
@@ -2621,69 +3021,29 @@ declare class Session {
2621
3021
  */
2622
3022
  continueConversation(userMessage: string, options?: GeneratePlanOptions): Promise<LegacyPlanState>;
2623
3023
  /**
2624
- * Answer every read the planner emits until it says something else.
2625
- *
2626
- * The channel is a text envelope rather than a registered tool because the
2627
- * protocol has to be identical on both planner backends (ADR-0009): Ordewell
2628
- * owns a tool loop only in the API case, and a harness planner running as a
2629
- * coding-agent subprocess can only be reached this way.
2630
- *
2631
- * Draining here — outside {@link repairLoop} — is what keeps reads free of
2632
- * the repair budget. A planner that looks a task up and *then* fumbles its
2633
- * ops JSON still gets its two corrective retries; charging it for the read
2634
- * would cost it the chance to fix the edit.
2635
- */
2636
- private drainTaskQueries;
2637
- /**
2638
- * Render one read out of live state. Never persisted to the transcript: the
2639
- * detail is context for the planner's next reply, and re-sending it on every
2640
- * later turn is exactly the cost this channel exists to avoid.
3024
+ * Copy this conversation and its task list into a new persisted session and
3025
+ * answer its id. This session is untouched — not even its live planner
3026
+ * context, so the original carries on exactly where it was. The fork holds
3027
+ * no run (see {@link forkPlanState}); a host adopts it like any saved
3028
+ * session, and its first message replays the copied transcript.
2641
3029
  */
2642
- private taskQueryAnswer;
3030
+ forkConversation(): ConversationFork;
2643
3031
  /**
2644
- * Drive a planner turn to a persisted, broadcast outcome. Task edits apply
2645
- * atomically; validation failures are fed back to the model for up to 2
2646
- * silent retries, then surfaced as a message with the plan untouched. The
2647
- * first turn (startPlanning) and every later turn route through here — one
2648
- * path, not two.
3032
+ * Cut the conversation back to just before a user message (a transcript
3033
+ * position from {@link rewindTargets}). Conversation only: the task list,
3034
+ * and any run executing it, carry on as they are. The planner's live context
3035
+ * is dropped, so the next message replays from the shortened transcript.
2649
3036
  */
2650
- private settleTurn;
3037
+ rewindConversation(userMessageIndex: number): LegacyPlanState;
2651
3038
  /**
2652
- * The catalog the planner may actually draw from — model ids and task-mode
2653
- * ids, per selected runner — emitted on EVERY turn, before any plan exists
2654
- * or after. The system prompt shows this once at conversation start; a long
2655
- * clarifying conversation outlives that single showing and the planner
2656
- * starts misquoting it, so this re-states it per turn instead.
2657
- *
2658
- * Built from the allowlist-filtered view (`filterModelsForPrompt`), the same
2659
- * one the system prompt used — a restricted allowlist stays a hard bound on
2660
- * every turn, not just the first. {@link models} itself is never
2661
- * filtered: `coerceAssignments` needs the full discovered catalog alongside
2662
- * the allowlist to resolve labels and clamp effort to real variants.
2663
- *
2664
- * Reads `this.plan.runners` live, so a runner admitted mid-session by a
2665
- * retarget (`admitRunner`) is filtered and shown like every other — there is
2666
- * no separate "originally selected" list to fall stale.
2667
- */
2668
- private catalogBlock;
2669
- /**
2670
- * The "you are here" block for post-plan chat: current tasks with stable
2671
- * references, plus the task-ops protocol. Injected per turn (never
2672
- * persisted) so the model always sees live statuses — including which tasks
2673
- * are locked by a running execution.
2674
- */
2675
- private planContextBlock;
2676
- /** Validate + commit a task_ops turn atomically. Returns the errors on rejection (plan untouched). */
2677
- private applyTaskOpsTurn;
2678
- /**
2679
- * Resume a persisted dialogue onto a fresh AI-service conversation — either
2680
- * because the in-memory one is gone (session reload, extension restart), or
2681
- * because {@link continueConversation} found it stale against the planner
2682
- * config now in effect (a harness planner's model/effort switched mid-chat).
2683
- * The saved transcript seeds the new conversation; no LLM call happens
2684
- * before the user's message is sent.
3039
+ * Condense the conversation on the user's say-so: a hidden planner turn
3040
+ * summarises it, and the summary replaces everything but the last two
3041
+ * exchanges. Conversation only, like a rewind — the tasks, and any run
3042
+ * executing them, are untouched. Atomic: a failed or stopped turn changes
3043
+ * nothing.
2685
3044
  */
2686
- private resumeConversation;
3045
+ compactConversation(signal?: AbortSignal): Promise<ConversationCompaction>;
3046
+ rewindTargets(): RewindTarget[];
2687
3047
  /** Whether the planner conversation is live (started and not yet committed to a plan). */
2688
3048
  get isConversationActive(): boolean;
2689
3049
  /**
@@ -2695,10 +3055,22 @@ declare class Session {
2695
3055
  * bare /unknown through to be mis-resolved.
2696
3056
  */
2697
3057
  private resolveSkillInvocation;
2698
- /** Append one dialogue entry to the persisted transcript. Callers persist via the mutatePlan ritual. */
2699
- private appendTranscript;
2700
- /** Commit a settled (non-task_ops) turn. Both branches run the mutatePlan ritual. */
2701
- private applyConversationTurn;
3058
+ /**
3059
+ * The one place planning discovers what it may draw from: the runners'
3060
+ * models (cached for later effort clamping), their modes, and the settings
3061
+ * in force. `filteredModels` is the allowlisted view a prompt may show;
3062
+ * `modelsByRunner` stays whole because coercion needs real labels and variants.
3063
+ */
3064
+ private plannerCatalog;
3065
+ private conversationOpening;
3066
+ /**
3067
+ * Load planner-produced tasks, coerced to the allowlist read live — it may
3068
+ * have changed since planning started. An edit on an armed scheduler is
3069
+ * reconciled rather than reloaded: `loadPlan` clears the on-hold set and the
3070
+ * review approval, so a task the user cancelled would be re-armed and
3071
+ * re-spawned by the re-tick that follows.
3072
+ */
3073
+ private adoptPlannerTasks;
2702
3074
  /**
2703
3075
  * PRD mode: when the planner writes the full markdown PRD, save it to
2704
3076
  * .scratch/<slug>/PRD.md (Matt Pocock to-prd convention) and keep it on the plan.
@@ -2724,6 +3096,42 @@ declare class Session {
2724
3096
  get queuedCount(): number;
2725
3097
  getTask(taskId: string): Task | undefined;
2726
3098
  get isReviewApproved(): boolean;
3099
+ /** Replay a run a dirty tree blocked, after stashing the tracked changes. */
3100
+ continueWithStash(): Promise<void>;
3101
+ /** Replay a run a dirty tree blocked, in the workspace root, for this run only. */
3102
+ continueWithoutIsolation(): Promise<void>;
3103
+ /**
3104
+ * Each task's isolation mark and the run's handoff, read from the run record.
3105
+ * The stream reports changes only; a surface that (re)connects or loads a
3106
+ * session asks here instead.
3107
+ */
3108
+ isolationView(): IsolationView | null;
3109
+ /** The run's integration branch against its base ref, as a unified diff. */
3110
+ reviewRunDiff(): Promise<string>;
3111
+ /**
3112
+ * "Merge all": each repo's integration branch into whatever the user has
3113
+ * checked out there — every repo, or none if any cannot take it. The one
3114
+ * irreversible step of isolated execution, so this explicit call is the
3115
+ * only way it ever happens.
3116
+ */
3117
+ mergeRun(): Promise<IsolationMergeResult>;
3118
+ /** Remove the run's worktrees and task branches; keep its integration branch to review or merge. */
3119
+ cleanupRun(): Promise<void>;
3120
+ /**
3121
+ * Give the whole run up, integration branch included. The plan is not
3122
+ * rewritten: a task that landed there stays completed until the user says
3123
+ * otherwise (Mark not done), because only they know whether they kept the work.
3124
+ */
3125
+ discardRun(): Promise<void>;
3126
+ private requireSettledRun;
3127
+ /**
3128
+ * The opt-in way through a merge conflict: add an AI task, on the conflicted
3129
+ * task's own runner and model, that merges its branch by hand in a worktree
3130
+ * of its own. When that task lands, the conflicted one lands through it (see
3131
+ * {@link TaskOrchestrator.linkConflictResolver}). Never automatic — nothing
3132
+ * but this call adds it.
3133
+ */
3134
+ resolveConflictAsTask(taskId: string): Promise<LegacyPlanState | null>;
2727
3135
  stopExecution(): void;
2728
3136
  /**
2729
3137
  * The one mutation seam: every structural plan mutation runs the same
@@ -2740,7 +3148,7 @@ declare class Session {
2740
3148
  * queues — so a task the edit just unblocked would sit ready and never
2741
3149
  * start. `tick()` no-ops while the scheduler is idle, so this costs nothing
2742
3150
  * during plain planning. The planner-driven path re-ticks in
2743
- * {@link settleTurn} instead; it must not tick twice.
3151
+ * {@link PlannerConversation} instead (`afterEdit`); it must not tick twice.
2744
3152
  */
2745
3153
  private editPlan;
2746
3154
  /**
@@ -2860,25 +3268,31 @@ declare function globalDataDir(): string;
2860
3268
  */
2861
3269
  declare function migrateOldConfigDir(): void;
2862
3270
 
2863
- type VerdictListener = (taskId: string, verdict: Verdict, output: string) => void;
3271
+ type VerdictListener = (taskId: string, verdict: Verdict) => void;
2864
3272
  type CheckpointListener = (taskId: string, summary: string) => void;
2865
3273
  /** Fires on every idleSince transition (null→timestamp on silence, timestamp→null on resume/teardown). */
2866
3274
  type IdleListener = (taskId: string, idleSince: string | null) => void;
2867
3275
  declare class VerdictEngine {
2868
3276
  private markerSeen;
2869
- private buffers;
2870
- private checkpointCounts;
3277
+ private markerTails;
3278
+ private checkpointCarry;
2871
3279
  private pausedSessions;
2872
3280
  private listeners;
2873
3281
  private checkpointListeners;
2874
3282
  private idleListeners;
2875
3283
  /**
2876
- * Per-task generation counter. Incremented on every watch() and clear().
3284
+ * Per-task generation. Replaced on every watch(), clear() and verdict.
2877
3285
  * Stale callbacks (from a prior session whose generation doesn't match
2878
3286
  * the current one) bail out instead of delivering a verdict for the
2879
3287
  * wrong session.
2880
3288
  */
2881
3289
  private generations;
3290
+ /**
3291
+ * Generations come from one counter that {@link reset} never rewinds: a
3292
+ * per-task count restarted at 1 after a reset, and the next watch handed a
3293
+ * session that outlived the reset the same generation as its successor.
3294
+ */
3295
+ private lastGeneration;
2882
3296
  private idleTimers;
2883
3297
  private idleSince;
2884
3298
  onVerdict(listener: VerdictListener): void;
@@ -2901,12 +3315,16 @@ declare class VerdictEngine {
2901
3315
  approveCheckpoint(taskId: string): void;
2902
3316
  rejectCheckpoint(taskId: string, reason: string): void;
2903
3317
  /**
2904
- * Attach to a spawned session: buffer output, scan for the task's completion
3318
+ * Attach to a spawned session: scan the output tail for the task's completion
2905
3319
  * marker (delivering a verdict immediately while leaving interactive sessions
2906
3320
  * open), scan for checkpoint markers, and on exit produce a failed verdict
2907
3321
  * when the marker was never observed.
2908
3322
  */
2909
3323
  watch(task: Task, session: ITerminalSession): void;
3324
+ /** Scan only the new text plus the unmatched carry, so a long run stays linear. */
3325
+ private scanCheckpoints;
3326
+ private bumpGeneration;
3327
+ private forget;
2910
3328
  /** Manual "Mark complete" override: a pass verdict that bypasses evidence. */
2911
3329
  markComplete(task: Task): Verdict;
2912
3330
  /** Clear verification state for a task (used on retry). */
@@ -2916,6 +3334,49 @@ declare class VerdictEngine {
2916
3334
  private decide;
2917
3335
  }
2918
3336
 
3337
+ /**
3338
+ * Owns one bounded raw buffer per task attempt, fed from the attempt's
3339
+ * session, and reads final answers from the agent's transcript through an
3340
+ * injected {@link TranscriptReader}.
3341
+ */
3342
+ declare class BufferedTaskOutputSource implements TaskOutputSource {
3343
+ private captures;
3344
+ private transcripts;
3345
+ private maxBufferChars;
3346
+ constructor(opts?: {
3347
+ transcripts?: TranscriptReader;
3348
+ maxBufferChars?: number;
3349
+ });
3350
+ attach(taskId: string, session: ITerminalSession): void;
3351
+ detach(taskId: string): void;
3352
+ reset(): void;
3353
+ finalText(attempt: TaskOutputAttempt, doneToken: string): Promise<string>;
3354
+ liveTail(taskId: string, opts: LiveTailOptions): LiveTail | null;
3355
+ private append;
3356
+ }
3357
+
3358
+ /**
3359
+ * Read a task's final answer from the agent's own session transcript — the
3360
+ * clean, structured record the agent writes about itself — instead of
3361
+ * reconstructing what a TUI painted (issue #16, built on #14's render).
3362
+ *
3363
+ * These stores are private and undocumented and DO rotate (OpenCode migrated
3364
+ * from files to SQLite mid-2026). Every reader is therefore defensive: any
3365
+ * missing file, unparseable line, or shape drift returns null and the caller
3366
+ * falls back to the terminal capture. Nothing here is a hard dependency.
3367
+ *
3368
+ * Directory and recency only narrow the candidates: with several attempts in
3369
+ * one cwd the newest session is whichever task finished last. A transcript is
3370
+ * accepted only when its content carries the task's completion marker.
3371
+ */
3372
+ declare class HomeTranscriptReader implements TranscriptReader {
3373
+ private readonly home;
3374
+ constructor(opts?: {
3375
+ homeDir?: string;
3376
+ });
3377
+ finalAssistantText(query: TranscriptQuery, maxChars?: number): Promise<string | null>;
3378
+ }
3379
+
2919
3380
  /**
2920
3381
  * The ids a runner may actually run: the user's allowlist with the ids that
2921
3382
  * provably belong to a *different* runner dropped. `undefined` means no
@@ -2999,9 +3460,18 @@ declare function buildResearchToolsPrompt(): string;
2999
3460
  * never a task plan.
3000
3461
  */
3001
3462
  declare function buildSubagentSystemPrompt(): string;
3002
- declare function buildConversationSystemPrompt(goal: string, context: string, modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>, runners: RunnerId[], runnerModes?: Record<RunnerId, RunnerModeInfo[]>, autonomousDefault?: boolean, verificationEnabled?: boolean,
3003
- /** Harness planner (ADR-0009): the agent owns its own tools and research budget. */
3004
- harnessMode?: boolean): string;
3463
+ /**
3464
+ * Facts about who plans and where tasks will run, as opposed to the user's
3465
+ * mode toggles: they change what the prompt can promise, not what it asks for.
3466
+ */
3467
+ interface ConversationVariant {
3468
+ /** Harness planner (ADR-0009): the agent owns its own tools and research budget. */
3469
+ harness?: boolean;
3470
+ /** Every AI task gets its own worktree (ADR-0013), so file overlap no longer forces an order; of every repo of a group (ADR-0014). */
3471
+ isolatedExecution?: IsolatedExecution;
3472
+ }
3473
+ declare function buildConversationSystemPrompt(goal: string, context: string, modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>, runners: RunnerId[], runnerModes?: Record<RunnerId, RunnerModeInfo[]>, autonomousDefault?: boolean, verificationEnabled?: boolean, variant?: ConversationVariant): string;
3474
+ /** The one-shot planner's core rules for tasks that share the workspace root. */
3005
3475
  declare const CORE_PLANNER_PROMPT: string;
3006
3476
  declare function buildResearchPrompt(userGoal: string, context: string, modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>, runners: RunnerId[], runnerModes?: Record<RunnerId, RunnerModeInfo[]>, modes?: PlannerModes): string;
3007
3477
  declare function buildPlanWithResults(userGoal: string, context: string, researchResults: string, modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>, runners: RunnerId[], runnerModes?: Record<RunnerId, RunnerModeInfo[]>, modes?: PlannerModes): string;
@@ -3009,7 +3479,7 @@ declare function buildModifyPlanPrompt(existingPlan: LegacyPlanState, userReques
3009
3479
  declare function modelContextBlock(modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>, runners: RunnerId[], runnerModes?: Record<RunnerId, RunnerModeInfo[]>, autonomousDefault?: boolean): string;
3010
3480
  declare function executionLogBlock(executionLog: TaskSnapshot[]): string;
3011
3481
  declare function pendingEditRulesBlock(): string;
3012
- declare function buildModifyDuringExecutionPrompt(executionLog: TaskSnapshot[], pendingTasks: string, userMessage: string, modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>, runners: RunnerId[], runnerModes?: Record<RunnerId, RunnerModeInfo[]>, autonomousDefault?: boolean): string;
3482
+ declare function buildModifyDuringExecutionPrompt(executionLog: TaskSnapshot[], pendingTasks: string, userMessage: string, modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>, runners: RunnerId[], runnerModes?: Record<RunnerId, RunnerModeInfo[]>, modes?: Pick<PlannerModes, 'autonomousDefault' | 'isolatedExecution'>): string;
3013
3483
  /**
3014
3484
  * User-message prompt for a planner-driven merge: asks the model to combine the
3015
3485
  * selected tasks into one. Routed through the conversation loop, so the
@@ -3023,6 +3493,21 @@ declare function buildMergePrompt(taskIds: string[], tasks: Task[]): string;
3023
3493
  * the user does not hand-type the parts.
3024
3494
  */
3025
3495
  declare function buildSplitPrompt(taskId: string, tasks: Task[]): string;
3496
+ /**
3497
+ * Runner prompt for the opt-in task that resolves an integration conflict
3498
+ * (ADR-0013). The resolver's own worktree starts at the integration tip, so the
3499
+ * merge it performs is exactly the one that conflicted; the conflicted task
3500
+ * then lands through that merge. Ordewell never resolves a conflict itself.
3501
+ *
3502
+ * In a repo group (ADR-0014) the conflict in one repo rolled the task back in
3503
+ * all of them, so the resolver merges the branch in every repo the task
3504
+ * changed, not only the one that conflicted.
3505
+ */
3506
+ declare function buildConflictResolutionPrompt(task: Task, conflict: {
3507
+ branch: string;
3508
+ repos: string[];
3509
+ conflictRepo?: string;
3510
+ }, integrationBranch: string): string;
3026
3511
 
3027
3512
  /**
3028
3513
  * The one owner of "the model emitted something unusable — correct it and
@@ -3677,4 +4162,4 @@ declare class PlannerModelMemory {
3677
4162
  recall(provider: string, catalog: PlannerModelCandidate[]): PlannerModelRecall;
3678
4163
  }
3679
4164
 
3680
- export { ALLOWED_PLUGIN_HOSTS, ALL_PROVIDERS, AUTO_COMMANDS, AbstractRunner, AbstractTerminalSession, ActiveTaskSession, type AgentAdapter, type AgentAdapterFactory, type AgentEvent, type AgentProcessDeps, type AgentStartOptions, AiProvider, ApprovalMode, ApprovalRequest, BUILTIN_SKILL_NAMES, BaseAiService, BaseConfig, BaseFileSystem, CLAUDE_CODE_MANIFEST, CLI_PROVIDERS, CMD_EXE_MAX_COMMAND_LINE, CORE_PLANNER_PROMPT, type CappedRows, type CheckpointListener, ClaudeCodeAdapter, CliAgentAiService, type CliAgentAiServiceDeps, CodexAdapter, type CommandClassification, CommandLineTooLongError, type CommandTier, type ComposeOptions, ConsoleLogger, ContextCollector, ConversationMessage, type ConversationRequest, type ConversationTurn, DAEMON_SUBPROTOCOL, DAEMON_TOKEN_SUBPROTOCOL_PREFIX, DiscoveredModel, EmbeddedNewlineError, type EnvAdmission, EnvConfig, type ExecFileFn, type ExecImpl, ExecutableNotFoundError, FindSymbolOptions, FsPluginStore, GIT_READONLY_SUBCOMMANDS, GeminiService, GlobOptions, type GrepInvocation, GrepOptions, type HasBinFn, HeadlessRunner, type HeadlessRunnerDeps, HeadlessSession, type IAiService, IApproval, IConfig, IFileSystem, type ILogger, type INotification, IPluginStore, ITerminalRunner, ITerminalSession, JSON_REPAIR_INSTRUCTION, type KillTreeDeps, type LaunchDeps, type LaunchPlan, LegacyPlanState, LineBuffer, type MappedTool, ModelResolver, type ModelResolverDeps, type ModifyPlanRequest, type NotificationAction, OPENCODE_MANIFEST, ORDEWELL_SETTABLE_ENV, OpenAiService, OpenCodeAdapter, type OrchestratorObserver, OrchestratorOption, PLUGIN_NAME_PATTERN, PROVIDER_CREDENTIAL_ENV, PROVIDER_DETECT_PRIORITY, PROVIDER_LABEL, PROVIDER_PRIORITY, PROVIDER_SHORT_LABEL, type PendingApproval, PendingApprovals, type PendingApprovalsOptions, PlanEditError, PlanParseError, type PlanRequest, PlanState, PlanStatus, PlanStore, Planner, type PlannerModelCandidate, type PlannerModelChoice, PlannerModelMemory, type PlannerModelRecall, type PlannerModelStore, type PlannerReplyClassification, type PlannerRuntimeToggles, type PluginSource, type PrdBlock, type PreparedLaunch, type PriorOutput, type ProbeFn, ProviderModelLists, type ProviderRegistration, type PtySize, type PtyWrapOptions, QueuedMessage, REFUSED_COMMANDS, ReadFileOpts, type RepairLoopOpts, type RepairVerdict, type ResearchChat, ResearchLogEntry, ResearchProgress, type ResearchShell, type ResearchShellDeps, ResearchToolType, type ResearchTurn, ResolveContext, type RunnerCatalog, RunnerId, RunnerInstallation, RunnerInvocation, type RunnerMode, RunnerModeInfo, RunnerPluginManifest, RunnerRegistry, type RunnerSpawnOptions, SETTINGS_ENV_ALLOWLIST, SETTINGS_ENV_REFUSED, STATE_DIR, STOPPED_TOOL_RESULT, SYMBOL_LANGUAGES, Session, SessionBroadcaster, type SessionData, type SessionDeps, type SessionMeta, type SessionPlanner, type SessionRuntimeSettings, SettingsService, type ShellDialect, type SkillInfo, type SkillMetadata, SkillsService, type SpawnSpec, StdioAgentAdapter, TASK_QUERY_ANSWER_OR_OPS, TASK_QUERY_FIELDS, TASK_QUERY_PROTOCOL, TASK_QUERY_REMINDER, TRUNCATED_PLAN_REPAIR_INSTRUCTION, Task, TaskOp, TaskOrchestrator, type TaskQuery, type TaskQueryCatalog, type TaskQueryField, TaskSnapshot, TmuxRunner, type TmuxRunnerDeps, type TokenCarriers, type ToolCall, ToolOutcome, type ToolResult, type UserSettings, Verdict, VerdictEngine, type VerdictListener, WINDOWS_MAX_COMMAND_LINE, type WorkspaceCheckDeps, WorkspaceNotAProjectError, WorkspaceNotFoundError, type WorkspaceProjectCheckDeps, admitSettingsEnv, applyHeadLimit, assertInstallablePluginUrl, assertPlainPluginName, assertWorkspaceExists, assertWorkspaceIsProject, augmentPromptWithPriorOutputs, augmentedPath, bearerHeaderValue, buildConversationSystemPrompt, buildFallbackGrepArgs, buildGlobArgs, buildGrepArgs, buildMergePrompt, buildModifyDuringExecutionPrompt, buildModifyPlanPrompt, buildPlanWithResults, buildResearchPrompt, buildResearchToolsPrompt, buildRunnerInvocation, buildShellInvocation, buildSplitPrompt, buildSubagentSystemPrompt, clampThinkingEffort, classifyCommand, classifyPlannerReply, classifyPluginSource, clearAugmentedPathCache, clearDaemonToken, clearResearchShellCache, clearState, clipboardCopyCommand, coerceAssignments, collectDirectDependencyOutputs, composeAugmentedPrompt, configuredProviders, createAiService, createSkillsService, daemonTokenPath, defaultLogger, definitionPattern, deleteSession, discoverGeminiModels, effectiveAllowlist, ensureDir, ensureStateDirIgnored, executionLogBlock, extractPrdBlock, extractPresentedToken, filterFallbackByAnchoredInclude, filterModelsForPrompt, formatSearchOutput, generatePlanWithRepair, getLatestSession, getProviderMeta, getSettingsPath, getStateDir, globalDataDir, grantScopeFor, hasTmux, includeGlobFor, isCliProvider, isExecutableResolved, isOpenAiProvider, isPlainPluginName, isValidManifest, killTree, languageForId, listSessions, loadSession, loadSessionPlanState, loadState, mapAgentTool, migrateOldConfigDir, mintDaemonToken, mintSessionId, modelContextBlock, modifyValidationFeedback, normalizeAgentArgs, normalizeGeminiModel, parseTaskQueryJson, pendingEditRulesBlock, planDirectLaunch, planShellLaunch, posixShellQuote, prefixModelId, providerForRunner, reEmitPlanPrompt, reEmitTaskOpsPrompt, reEmitTaskQueryPrompt, readDaemonToken, referencePattern, renderPlanMap, renderPriorOutputs, renderTaskQueryAnswer, repairLoop, researchShellWarning, researchToolsPath, resolveArgs, resolvePluginInstallDir, resolveProviderFromPrefix, resolveResearchShell, resolveSkillInvocation, resolveWithin, retargetTaskRunner, runnerAssignment, runnerForProvider, sanitizeSlug, savePrdMarkdown, saveSession, saveState, sessionRuntimeSettings, stateExists, stripAnsi, stripModelPrefix, summarizeOutput, taskOpsRejectedPrompt, taskQuerySignature, textHasTaskQuery, tmuxSessionName, tmuxSocketName, tmuxWindowName, tokenSubprotocols, tokensMatch, truncatedPlanReEmitPrompt, wellKnownBinDirs, windowsCommandLine, withPath, wrapWithPty };
4165
+ export { ALLOWED_PLUGIN_HOSTS, ALL_PROVIDERS, AUTO_COMMANDS, AbstractRunner, AbstractTerminalSession, ActiveTaskSession, type Adr0013IsolationRun, type Adr0013PlanIsolation, type Adr0013TaskRecord, type AgentAdapter, type AgentAdapterFactory, type AgentEvent, type AgentProcessDeps, type AgentStartOptions, AiProvider, ApprovalMode, ApprovalRequest, BUILTIN_SKILL_NAMES, BaseAiService, BaseConfig, BaseFileSystem, BufferedTaskOutputSource, CLAUDE_CODE_MANIFEST, CLI_PROVIDERS, CMD_EXE_MAX_COMMAND_LINE, CORE_PLANNER_PROMPT, type CappedRows, type CheckpointListener, ClaudeCodeAdapter, CliAgentAiService, type CliAgentAiServiceDeps, CodexAdapter, type CommandClassification, CommandLineTooLongError, type CommandTier, type ComposeOptions, ConsoleLogger, ContextCollector, ConversationBusyError, type ConversationCompaction, ConversationEditError, type ConversationFork, ConversationMessage, type ConversationRequest, type ConversationTurn, type ConversationVariant, DAEMON_SUBPROTOCOL, DAEMON_TOKEN_SUBPROTOCOL_PREFIX, DiscoveredModel, EmbeddedNewlineError, type EnvAdmission, EnvConfig, type ExecFileFn, type ExecImpl, ExecutableNotFoundError, FindSymbolOptions, FsPluginStore, GIT_READONLY_SUBCOMMANDS, GeminiService, type GitExecFn, GlobOptions, type GrepInvocation, GrepOptions, type HasBinFn, HeadlessRunner, type HeadlessRunnerDeps, HeadlessSession, HomeTranscriptReader, type IAiService, IApproval, IConfig, IFileSystem, type ILogger, type INotification, IPluginStore, ITerminalRunner, ITerminalSession, IWorktreeIsolation, IsolationHandoff, IsolationMergeResult, IsolationTaskStatus, IsolationView, JSON_REPAIR_INSTRUCTION, type KillTreeDeps, type LaunchDeps, type LaunchPlan, LegacyPlanState, LineBuffer, type LiveOutputLookup, type LiveTail, type LiveTailOptions, type MappedTool, ModelResolver, type ModelResolverDeps, type ModifyPlanRequest, type NotificationAction, OPENCODE_MANIFEST, ORDEWELL_SETTABLE_ENV, OUTPUT_LINES_DEFAULT, OUTPUT_LINES_MAX, OpenAiService, OpenCodeAdapter, type OrchestratorObserver, OrchestratorOption, PLUGIN_NAME_PATTERN, PROVIDER_CREDENTIAL_ENV, PROVIDER_DETECT_PRIORITY, PROVIDER_LABEL, PROVIDER_PRIORITY, PROVIDER_SHORT_LABEL, type PendingApproval, PendingApprovals, type PendingApprovalsOptions, PlanEditError, PlanIsolation, PlanParseError, type PlanRequest, PlanState, PlanStatus, PlanStore, Planner, type PlannerModelCandidate, type PlannerModelChoice, PlannerModelMemory, type PlannerModelRecall, type PlannerModelStore, type PlannerReplyClassification, type PlannerRuntimeToggles, type PluginSource, type PrdBlock, type PreparedLaunch, type PriorOutput, type ProbeFn, ProviderModelLists, type ProviderRegistration, type PtySize, type PtyWrapOptions, QueuedMessage, REFUSED_COMMANDS, ReadFileOpts, type RepairLoopOpts, type RepairVerdict, RepoGroupLayout, type ResearchChat, ResearchLogEntry, ResearchProgress, type ResearchShell, type ResearchShellDeps, ResearchToolType, type ResearchTurn, ResolveContext, type RewindTarget, type RunnerCatalog, RunnerId, RunnerInstallation, RunnerInvocation, type RunnerMode, RunnerModeInfo, RunnerPluginManifest, RunnerRegistry, type RunnerSpawnOptions, SETTINGS_ENV_ALLOWLIST, SETTINGS_ENV_REFUSED, STATE_DIR, STOPPED_TOOL_RESULT, SYMBOL_LANGUAGES, Session, SessionBroadcaster, type SessionData, type SessionDeps, type SessionMeta, SessionNotice, type SessionPlanner, type SessionRuntimeSettings, SettingsService, type ShellDialect, type SkillInfo, type SkillMetadata, SkillsService, type SpawnSpec, StdioAgentAdapter, TASK_QUERY_ANSWER_MAX_CHARS, TASK_QUERY_ANSWER_OR_OPS, TASK_QUERY_FIELDS, TASK_QUERY_PROTOCOL, TASK_QUERY_REMINDER, TRUNCATED_PLAN_REPAIR_INSTRUCTION, Task, type TaskAttemptSnapshot, TaskIsolation, TaskOp, TaskOrchestrator, type TaskOutputAttempt, type TaskOutputSource, type TaskQuery, type TaskQueryCatalog, type TaskQueryField, TaskSnapshot, TmuxRunner, type TmuxRunnerDeps, type TokenCarriers, type ToolCall, ToolOutcome, type ToolResult, type TranscriptQuery, type TranscriptReader, type UserSettings, Verdict, VerdictEngine, type VerdictListener, WINDOWS_MAX_COMMAND_LINE, type WorkspaceCheckDeps, WorkspaceNotAProjectError, WorkspaceNotFoundError, type WorkspaceProjectCheckDeps, type WorktreeIsolationDeps, admitSettingsEnv, applyHeadLimit, assertInstallablePluginUrl, assertPlainPluginName, assertWorkspaceExists, assertWorkspaceIsProject, augmentPromptWithPriorOutputs, augmentedPath, bearerHeaderValue, buildConflictResolutionPrompt, buildConversationSystemPrompt, buildFallbackGrepArgs, buildGlobArgs, buildGrepArgs, buildMergePrompt, buildModifyDuringExecutionPrompt, buildModifyPlanPrompt, buildPlanWithResults, buildResearchPrompt, buildResearchToolsPrompt, buildRunnerInvocation, buildShellInvocation, buildSplitPrompt, buildSubagentSystemPrompt, clampThinkingEffort, classifyCommand, classifyPlannerReply, classifyPluginSource, clearAugmentedPathCache, clearDaemonToken, clearResearchShellCache, clearState, clipboardCopyCommand, coerceAssignments, collectDirectDependencyOutputs, composeAugmentedPrompt, configuredProviders, createAiService, createSkillsService, createWorktreeIsolation, daemonTokenPath, defaultLogger, definitionPattern, deleteSession, describeMergeResult, discoverGeminiModels, effectiveAllowlist, ensureDir, ensureStateDirIgnored, executionLogBlock, extractPrdBlock, extractPresentedToken, filterFallbackByAnchoredInclude, filterModelsForPrompt, formatSearchOutput, generatePlanWithRepair, getLatestSession, getProviderMeta, getSettingsPath, getStateDir, globalDataDir, grantScopeFor, hasTmux, includeGlobFor, isCliProvider, isExecutableResolved, isOpenAiProvider, isPlainPluginName, isValidManifest, killTree, languageForId, listSessions, loadSession, loadSessionPlanState, loadState, mapAgentTool, migrateOldConfigDir, migratePlanIsolation, mintDaemonToken, mintSessionId, modelContextBlock, modifyValidationFeedback, normalizeAgentArgs, normalizeGeminiModel, parseTaskQueryJson, pendingEditRulesBlock, planDirectLaunch, planShellLaunch, posixShellQuote, prefixModelId, providerForRunner, reEmitPlanPrompt, reEmitTaskOpsPrompt, reEmitTaskQueryPrompt, readDaemonToken, referencePattern, renderPlanMap, renderPriorOutputs, renderTaskQueryAnswer, repairLoop, researchShellWarning, researchToolsPath, resolveArgs, resolvePluginInstallDir, resolveProviderFromPrefix, resolveResearchShell, resolveSkillInvocation, resolveWithin, retargetTaskRunner, runnerAssignment, runnerForProvider, sanitizeSlug, savePrdMarkdown, saveSession, saveState, sessionRuntimeSettings, stateExists, stripAnsi, stripModelPrefix, summarizeOutput, taskOpsRejectedPrompt, taskQuerySignature, textHasTaskQuery, tmuxSessionName, tmuxSocketName, tmuxWindowName, tokenSubprotocols, tokensMatch, truncatedPlanReEmitPrompt, wellKnownBinDirs, windowsCommandLine, withPath, wrapWithPty };