@ordewell/core 0.4.4 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { h as RunnerId, c as PlanStatus, L as LegacyPlanState, d as ResearchProgress, a as DiscoveredModel, T as Task, R as ResearchLogEntry, C as ConversationMessage, n as TaskSnapshot, Q as QueuedMessage, A as ActiveTaskSession, g as ResearchToolType, u as Verdict, e as ResearchStep, b as PlanState } from './Task-1NwmUImI.js';
2
2
  export { D as DiscoveredMode, M as Message, P as PlanModificationWarnings, 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 ThinkingBlock, U as UserPromptEntry, r as UserStep, V as ValidationCheck, s as ValidationContext, t as ValidationResult, v as VerificationCheck, w as addTaskToPlan, x as createEmptyPlan, y as createTask, z as emptyWarnings, B as flattenTasks, E as migrateLegacyPlan, F as migratePlanState, G as migrateTask, H as removeTaskFromPlan, I as renumberTasks, J as updateTaskInPlan, K as validateModifiedPlan, N as warningsText } from './Task-1NwmUImI.js';
3
- import { n as IFileSystem, I as IApproval, R as ReadFileOpts, T as ToolOutcome, k as GrepOptions, j as GlobOptions, i as FindSymbolOptions, f as ApprovalRequest, m as IConfig, A as AiProvider, y as ProviderModelLists, c as ApprovalMode, q as ITerminalSession, p as ITerminalRunner, J as RunnerRegistry, s as OrchestratorOption, b as ApprovalKind, g as ApprovalSource, E as RunnerInvocation, o as IPluginStore, H as RunnerPluginManifest, B as ResolveContext } from './ITerminalRunner-D7DwHYSg.js';
4
- export { a as AllProviderModels, d as ApprovalPolicy, e as ApprovalPolicyOptions, C as CatalogModel, D as DENY_ALL, h as DiscoveryCommand, F as FetchAllProviderModelsOptions, G as GREP_DEFAULT_HEAD_LIMIT, l as GrepOutputMode, M as ModelCatalog, r as ModelShortcut, O as ORCHESTRATOR_SHORTCUTS, P as PluginEntry, t as PluginFeatures, u as PluginMode, v as PluginModelDiscovery, w as PluginRunnerDef, x as ProviderCredentialSource, z as ProviderModelsResult, S as SEARCH_EXCLUSIONS, K as collectProviderCredentials, L as enabledRunners, N as fetchAllProviderModels, Q as knownModelId, U as resolveModelShortcut, V as resolveProvider, W as toOrchestratorOptions } from './ITerminalRunner-D7DwHYSg.js';
3
+ import { n as IFileSystem, I as IApproval, R as ReadFileOpts, T as ToolOutcome, k as GrepOptions, j as GlobOptions, i as FindSymbolOptions, f as ApprovalRequest, m as IConfig, A as AiProvider, y as ProviderModelLists, c as ApprovalMode, q as ITerminalSession, p as ITerminalRunner, J as RunnerRegistry, s as OrchestratorOption, b as ApprovalKind, g as ApprovalSource, E as RunnerInvocation, o as IPluginStore, H as RunnerPluginManifest, B as ResolveContext } from './ITerminalRunner-Cpyzyrnl.js';
4
+ export { a as AllProviderModels, d as ApprovalPolicy, e as ApprovalPolicyOptions, C as CatalogModel, D as DENY_ALL, h as DiscoveryCommand, F as FetchAllProviderModelsOptions, G as GREP_DEFAULT_HEAD_LIMIT, l as GrepOutputMode, M as ModelCatalog, r as ModelShortcut, O as ORCHESTRATOR_SHORTCUTS, P as PluginEntry, t as PluginFeatures, u as PluginMode, v as PluginModelDiscovery, w as PluginRunnerDef, x as ProviderCredentialSource, z as ProviderModelsResult, S as SEARCH_EXCLUSIONS, K as collectProviderCredentials, L as enabledRunners, N as fetchAllProviderModels, Q as knownModelId, U as resolveModelShortcut, V as resolveProvider, W as toOrchestratorOptions } from './ITerminalRunner-Cpyzyrnl.js';
5
5
  import { R as RunnerModeInfo, b as PlanParseError } from './parsing-RjN9JbyG.js';
6
6
  export { M as ManifestLookup, P as PLAN_ENVELOPE_KEY, a as PartialPlanTask, T as TASK_OPS_ENVELOPE_KEY, c as buildModeGuide, 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 filteredBuildModes, n as looksLikePlanAttempt, p as parsePartialPlan, o as parsePlanJson, r as resolveDefaultMode, q as resolveTaskMode, s as runnerModesFrom, t as stripModelNoise, u as stripTrailingCommas, v as validatePlanModification } from './parsing-RjN9JbyG.js';
7
7
  import { T as TaskOp } from './plan-utils-Cgc0cCH1.js';
@@ -162,8 +162,9 @@ interface CommandClassification {
162
162
  reason?: string;
163
163
  }
164
164
  /**
165
- * Classify one command line. Output redirection is refused outright: a planner
166
- * that writes files has stopped being a planner.
165
+ * Classify one command line. Output redirection is refused outright unless the
166
+ * target provably writes nothing — `/dev/null`, or an fd duplication like
167
+ * `2>&1` — since a planner that writes files has stopped being a planner.
167
168
  */
168
169
  declare function classifyCommand(command: string, opts?: CommandPolicyOptions): CommandClassification;
169
170
 
@@ -195,7 +196,7 @@ declare abstract class BaseFileSystem implements IFileSystem {
195
196
  protected abstract globImpl(pattern: string, absRoot: string, headLimit: number): Promise<ToolOutcome>;
196
197
  protected abstract grepImpl(pattern: string, absRoot: string, opts: GrepOptions): Promise<ToolOutcome>;
197
198
  protected abstract listDirImpl(absPath: string, depth: number): Promise<ToolOutcome>;
198
- protected abstract execBashImpl(command: string): Promise<ToolOutcome>;
199
+ protected abstract execBashImpl(command: string, signal?: AbortSignal): Promise<ToolOutcome>;
199
200
  /**
200
201
  * Resolve `p` and confirm the planner may touch it. In-workspace paths pass
201
202
  * silently; anything else needs one approval, remembered per containing
@@ -234,9 +235,27 @@ declare abstract class BaseFileSystem implements IFileSystem {
234
235
  * anything else asks once and is remembered, and writes are refused outright
235
236
  * because a planner that mutates the workspace has stopped being a planner.
236
237
  */
237
- bash(command: string): Promise<ToolOutcome>;
238
+ bash(command: string, signal?: AbortSignal): Promise<ToolOutcome>;
239
+ }
240
+
241
+ interface IWebFetcher {
242
+ confirm(url: string): Promise<boolean>;
243
+ fetch(url: string): Promise<ToolOutcome>;
244
+ /**
245
+ * Optional web search. A fetcher without it makes the `web_search` tool
246
+ * report itself unavailable rather than failing the turn — the same
247
+ * degradation `fetch` already uses when no fetcher is wired at all.
248
+ */
249
+ search?(query: string): Promise<ToolOutcome>;
238
250
  }
239
251
 
252
+ /**
253
+ * Short, non-alarming answer for a call the turn was stopped before running.
254
+ * The call still needs *an* answer — a dangling tool_call leaves the provider's
255
+ * message history invalid for whatever the user does next.
256
+ */
257
+ declare const STOPPED_TOOL_RESULT = "Stopped by the user \u2014 this call was not executed, and no further tool calls will run this turn.";
258
+
240
259
  /**
241
260
  * Workspace containment, in one place. Both the filesystem policy layer
242
261
  * (which may ask the user to approve an escape) and the research-subagent
@@ -506,17 +525,6 @@ declare class ConsoleLogger implements ILogger {
506
525
  }
507
526
  declare const defaultLogger: ILogger;
508
527
 
509
- interface IWebFetcher {
510
- confirm(url: string): Promise<boolean>;
511
- fetch(url: string): Promise<ToolOutcome>;
512
- /**
513
- * Optional web search. A fetcher without it makes the `web_search` tool
514
- * report itself unavailable rather than failing the turn — the same
515
- * degradation `fetch` already uses when no fetcher is wired at all.
516
- */
517
- search?(query: string): Promise<ToolOutcome>;
518
- }
519
-
520
528
  interface UserSettings {
521
529
  grillMe: {
522
530
  enabled: boolean;
@@ -537,6 +545,11 @@ interface UserSettings {
537
545
  enabled: boolean;
538
546
  };
539
547
  modelAllowlist?: Record<string, string[]>;
548
+ /** Last model (and its thinking effort) the user chose for each planner backend, keyed by AiProvider id. */
549
+ plannerModels?: Record<string, {
550
+ model: string;
551
+ effort?: string;
552
+ }>;
540
553
  /**
541
554
  * Runners the user picked for planning. Absent — not empty — is what falls
542
555
  * back to the environment's defaults; `[]` is a deliberate "none of them".
@@ -575,6 +588,14 @@ declare class SettingsService {
575
588
  setResearchSubagents(enabled: boolean): void;
576
589
  getModelAllowlist(runner: string): string[] | undefined;
577
590
  setModelAllowlist(runner: string, ids: string[] | undefined): void;
591
+ getPlannerModel(provider: string): {
592
+ model: string;
593
+ effort?: string;
594
+ } | undefined;
595
+ setPlannerModel(provider: string, entry: {
596
+ model: string;
597
+ effort?: string;
598
+ } | undefined): void;
578
599
  getEnabledRunners(): string[] | undefined;
579
600
  setEnabledRunners(ids: string[]): void;
580
601
  private load;
@@ -701,6 +722,29 @@ declare class EmbeddedNewlineError extends Error {
701
722
  readonly command: string;
702
723
  constructor(command: string);
703
724
  }
725
+ /**
726
+ * Thrown when `command` cannot be resolved to a real file. Kept distinguishable
727
+ * from `WorkspaceNotFoundError` (utils/workspace) even though both a missing
728
+ * cwd and a missing binary surface as the same `spawn` ENOENT to Node — the two
729
+ * are checked, and named, separately so the failure names the actual cause.
730
+ */
731
+ declare class ExecutableNotFoundError extends Error {
732
+ readonly command: string;
733
+ readonly searchedPath: string;
734
+ constructor(command: string, searchedPath: string);
735
+ }
736
+ /**
737
+ * Whether `command` actually resolves to a file, given the plan
738
+ * {@link planDirectLaunch} produced for it and the PATH it was resolved
739
+ * against.
740
+ *
741
+ * POSIX is deliberately identity in `planDirectLaunch` (execvp does its own
742
+ * PATH search), so the search is repeated here instead. Windows already did
743
+ * the search inside `planDirectLaunch` — signalled by the returned file
744
+ * differing from the bare command name it was given; an unresolved command
745
+ * comes back unchanged.
746
+ */
747
+ declare function isExecutableResolved(command: string, plan: LaunchPlan, PATH: string, deps?: LaunchDeps): boolean;
704
748
  /** The verbatim command line cmd.exe receives after `/d /s /c`, before wrapping. */
705
749
  declare function windowsCommandLine(file: string, args: string[]): string;
706
750
  /**
@@ -908,6 +952,10 @@ interface AgentProcessDeps {
908
952
  resolvePath?: () => Promise<string>;
909
953
  /** Host platform. Defaults to the real one; injected so OS-specific behavior is testable anywhere. */
910
954
  platform?: NodeJS.Platform;
955
+ /** True when `workspace` names an existing directory. Defaults to a real filesystem check. */
956
+ isDirectory?: (workspace: string) => boolean;
957
+ /** True when `candidate` names an existing, spawnable file. Defaults to a real filesystem check. */
958
+ exists?: (candidate: string) => boolean;
911
959
  }
912
960
  /** Builds the adapter for one runner id, or null when that runner cannot plan. */
913
961
  type AgentAdapterFactory = (runner: string, deps: AgentProcessDeps) => AgentAdapter | null;
@@ -1482,6 +1530,13 @@ declare class TaskOrchestrator {
1482
1530
  subscribe(observer: OrchestratorObserver): () => void;
1483
1531
  private emit;
1484
1532
  get isRunning(): boolean;
1533
+ /**
1534
+ * A runner is executing *right now*. Narrower than {@link isRunning}, which
1535
+ * also covers the armed-but-idle scheduler `tick()` deliberately leaves
1536
+ * behind when a plan is paused on a user task, a checkpoint or a hold —
1537
+ * nothing is executing then, so nothing is reading the plan mid-mutation.
1538
+ */
1539
+ get hasLiveWork(): boolean;
1485
1540
  get isReviewApproved(): boolean;
1486
1541
  get status(): 'approved' | 'running' | 'completed';
1487
1542
  get activeTaskIds(): string[];
@@ -1493,6 +1548,17 @@ declare class TaskOrchestrator {
1493
1548
  clearQueuedMessages(): void;
1494
1549
  processNextQueuedMessage(): QueuedMessage | null;
1495
1550
  loadPlan(tasks: Task[], planRunners?: RunnerId[]): void;
1551
+ /**
1552
+ * Adopt an edited plan without letting go of the run in progress. Everything
1553
+ * the scheduler owns — live sessions, holds, retry counts, the verifier and
1554
+ * the review approval — survives, because `loadPlan` clears all of it and a
1555
+ * mid-run edit is not a new run. Only the tasks change.
1556
+ *
1557
+ * The ids with live sessions are defended: one dropped from the edited plan is
1558
+ * carried over (its verdict still has to land somewhere), and one whose status
1559
+ * the snapshot predates is put back to `in_progress` — adopting the snapshot's
1560
+ * "pending" would offer the scheduler work a runner is already doing.
1561
+ */
1496
1562
  reconcilePlan(newTasks: Task[], planRunners?: RunnerId[]): void;
1497
1563
  start(): Promise<void>;
1498
1564
  stop(): void;
@@ -1506,6 +1572,16 @@ declare class TaskOrchestrator {
1506
1572
  * doesn't immediately restart it; Retry / Force Start release the hold.
1507
1573
  */
1508
1574
  cancelTask(taskId: string): Promise<void>;
1575
+ /**
1576
+ * Let go of a task that is leaving the plan. A live runner is cancelled
1577
+ * through {@link cancelTask} so the verifier generation is bumped before the
1578
+ * process dies; a spawn still in flight is dropped from `startingTaskIds`,
1579
+ * which is what makes {@link startTask} kill the session it is about to
1580
+ * receive. The id's remaining bookkeeping goes too — a hold or retry count
1581
+ * kept for a task that no longer exists would be inherited by nothing and
1582
+ * leaves `isRunning` reading true forever.
1583
+ */
1584
+ releaseTask(taskId: string): Promise<void>;
1509
1585
  markTaskComplete(taskId: string): Promise<void>;
1510
1586
  markAiTaskComplete(taskId: string): Promise<void>;
1511
1587
  /**
@@ -1671,6 +1747,13 @@ declare class ModelResolver {
1671
1747
  getDiscoveryErrors(): Record<string, string>;
1672
1748
  refresh(): Promise<ProviderModelLists>;
1673
1749
  refreshRunnerModels(): void;
1750
+ /**
1751
+ * Synchronous reads of whatever the last `modelsForRunners`/`pickerOptions`
1752
+ * call already cached — never trigger discovery or a fetch. For callers
1753
+ * (e.g. a settings write) that need a catalog without going async.
1754
+ */
1755
+ getCachedRunnerModels(runner: string): DiscoveredModel[];
1756
+ getCachedPickerOptions(): OrchestratorOption[];
1674
1757
  invalidate(): void;
1675
1758
  }
1676
1759
 
@@ -2124,6 +2207,16 @@ declare function executionSummary(tasks: Task[]): {
2124
2207
  failed: number;
2125
2208
  };
2126
2209
 
2210
+ /**
2211
+ * A direct (non-planner) plan edit the session refused. Distinct from a plain
2212
+ * Error so a surface can tell "you asked for something invalid" from "something
2213
+ * broke" and say which — the HTTP routes used to collapse both into 404/500,
2214
+ * which read to the TUI and VS Code as the edit silently doing nothing. Carries
2215
+ * no status code: core is transport-agnostic, the route maps it.
2216
+ */
2217
+ declare class PlanEditError extends Error {
2218
+ constructor(message: string);
2219
+ }
2127
2220
  /**
2128
2221
  * Options for plan generation. Progress is not overridable: every planner
2129
2222
  * progress event is translated to a SessionMessage inside the Session and
@@ -2296,8 +2389,24 @@ declare class Session {
2296
2389
  get currentGoal(): string;
2297
2390
  get isPlanning(): boolean;
2298
2391
  get isExecuting(): boolean;
2392
+ /** See {@link TaskOrchestrator.hasLiveWork} — a spawned runner, not merely an armed scheduler. */
2393
+ get hasLiveWork(): boolean;
2299
2394
  get status(): 'approved' | 'running' | 'completed';
2300
2395
  get sessionConfig(): IConfig;
2396
+ /**
2397
+ * Deny every parked approval as soon as a planning turn is aborted, for the
2398
+ * same reason `beginFreshPlan` does it: a prompt raised by a turn nobody is
2399
+ * waiting on has no one left to serve, and denying it unblocks the research
2400
+ * loop instead of stranding it. Without this the request sat out its
2401
+ * five-minute timeout and the loop then carried on as if nothing had
2402
+ * happened — the abort was real, but invisible until long after the user
2403
+ * pressed stop.
2404
+ *
2405
+ * The listener is returned as a disposer rather than left attached: callers
2406
+ * own the signal and may reuse it, and a leaked listener would deny the
2407
+ * *next* turn's prompts the moment that stale signal aborted.
2408
+ */
2409
+ private denyApprovalsOnAbort;
2301
2410
  startExecution(): Promise<void>;
2302
2411
  generatePlan(goal: string, runners: RunnerId[], options?: GeneratePlanOptions): Promise<LegacyPlanState>;
2303
2412
  /**
@@ -2377,14 +2486,29 @@ declare class Session {
2377
2486
  * task state; LegacyPlanState.tasks is populated only at persist time.
2378
2487
  */
2379
2488
  private mutatePlan;
2380
- updateTask(taskId: string, changes: Partial<Task>): LegacyPlanState | null;
2489
+ /**
2490
+ * The direct-edit seam: {@link mutatePlan} plus the reschedule every
2491
+ * structural edit owes an armed scheduler. Nothing else wakes one after a
2492
+ * hand edit — the queue-drain path never runs, because a direct edit never
2493
+ * queues — so a task the edit just unblocked would sit ready and never
2494
+ * start. `tick()` no-ops while the scheduler is idle, so this costs nothing
2495
+ * during plain planning. The planner-driven path re-ticks in
2496
+ * {@link settleTurn} instead; it must not tick twice.
2497
+ */
2498
+ private editPlan;
2499
+ /**
2500
+ * Patch one task's fields. A hand-set dependency list is the one patch that
2501
+ * can leave the plan unschedulable, so it goes through the same
2502
+ * {@link canSetDependencies} guard the planner's pickers use rather than a
2503
+ * second copy of the rule — and throws, so the surface can say why.
2504
+ */
2505
+ updateTask(taskId: string, changes: Partial<Task>): Promise<LegacyPlanState | null>;
2381
2506
  /**
2382
2507
  * Move one task onto a different runner. Distinct from {@link updateTask}
2383
2508
  * because a runner change is never a single-field edit: the task's model,
2384
2509
  * thinking effort and mode are all scoped to its runner, so they are
2385
2510
  * re-derived from the new runner's catalog (see {@link retargetTaskRunner}).
2386
- * That needs discovery, which is async — hence a method of its own rather
2387
- * than a branch inside the sync `updateTask`.
2511
+ * That needs discovery, which is why it is not a branch inside `updateTask`.
2388
2512
  *
2389
2513
  * The runner is also admitted into `plan.runners` — see {@link admitRunner}.
2390
2514
  */
@@ -2411,16 +2535,21 @@ declare class Session {
2411
2535
  */
2412
2536
  private admitRunner;
2413
2537
  /**
2414
- * Replace one task's dependency list. Separate from {@link updateTask}
2415
- * because a hand-edited graph is the one task edit that can leave a plan
2416
- * unschedulable: `canSetDependencies` is the guard, and it lives behind this
2417
- * one method so the API and both surfaces' pickers reject the same edits
2418
- * rather than each carrying a copy of the rule. Throws so a surface can say
2419
- * why the edit was refused.
2538
+ * Replace one task's dependency list — the named entry point the surfaces'
2539
+ * dependency pickers call. The guard itself lives in {@link updateTask}, so
2540
+ * a dependency list arriving as a plain field patch is rejected by the same
2541
+ * rule instead of slipping past it.
2420
2542
  */
2421
- setTaskDependencies(taskId: string, dependencies: string[]): LegacyPlanState | null;
2543
+ setTaskDependencies(taskId: string, dependencies: string[]): Promise<LegacyPlanState | null>;
2422
2544
  completeTask(taskId: string): Promise<void>;
2423
- removeTask(taskId: string): LegacyPlanState | null;
2545
+ /**
2546
+ * Delete one task. A running task is cancelled first: the plan can drop it
2547
+ * either way, but nothing can reach its runner afterwards — the tmux session
2548
+ * outlives the plan and the orchestrator keeps counting it as active. The
2549
+ * planner-driven path refuses instead (see {@link applyTaskOps}); a user
2550
+ * deleting their own task means it.
2551
+ */
2552
+ removeTask(taskId: string): Promise<LegacyPlanState | null>;
2424
2553
  /**
2425
2554
  * Add one task, filling in whatever the caller left unset. A task with no
2426
2555
  * runnable assignment is not a lighter task but an unspawnable one, so the
@@ -2435,9 +2564,9 @@ declare class Session {
2435
2564
  * moved on, not that the whole task should be refused.
2436
2565
  */
2437
2566
  addTask(draft: Partial<Task>): Promise<LegacyPlanState | null>;
2438
- mergeTasks(taskIdA: string, taskIdB: string): LegacyPlanState | null;
2439
- mergeMultipleTasks(taskIds: string[]): LegacyPlanState | null;
2440
- splitTask(taskId: string, newTasks: Partial<Task>[]): LegacyPlanState | null;
2567
+ mergeTasks(taskIdA: string, taskIdB: string): Promise<LegacyPlanState | null>;
2568
+ mergeMultipleTasks(taskIds: string[]): Promise<LegacyPlanState | null>;
2569
+ splitTask(taskId: string, newTasks: Partial<Task>[]): Promise<LegacyPlanState | null>;
2441
2570
  /**
2442
2571
  * Planner-driven merge: validate compatibility up front, then ask the planner
2443
2572
  * LLM to produce a single "merge" taskOps op combining the selected tasks.
@@ -2752,6 +2881,7 @@ interface TmuxRunnerDeps {
2752
2881
  resolvePath?: () => Promise<string>;
2753
2882
  pollIntervalMs?: number;
2754
2883
  logDir?: string;
2884
+ clipboardCommand?: () => string | null;
2755
2885
  }
2756
2886
  declare class TmuxSession extends AbstractTerminalSession {
2757
2887
  private tmuxSession;
@@ -2803,6 +2933,7 @@ declare class TmuxRunner extends AbstractRunner<TmuxSession> {
2803
2933
  private resolvePath;
2804
2934
  private pollIntervalMs;
2805
2935
  private logDir;
2936
+ private clipboardCommand;
2806
2937
  /** Retries get a freshly named window so a failed attempt's output stays inspectable. */
2807
2938
  private attempts;
2808
2939
  private ready;
@@ -2818,14 +2949,17 @@ declare class TmuxRunner extends AbstractRunner<TmuxSession> {
2818
2949
  private tmux;
2819
2950
  private createFreshSession;
2820
2951
  /**
2821
- * Makes an attached runner terminal scrollable with the mouse wheel and
2822
- * Page Up/Down. A fresh tmux session ships with `mouse off`, a 2000-line
2823
- * scrollback, and no PageUp binding — none of which lets a user page back
2824
- * through a finished task's output, which is the whole point of opening its
2825
- * window. Scoping is harmless: the daemon owns a private socket
2826
- * (`tmuxSocketName`), so these server-wide options touch nothing but its
2827
- * own session. Best-effort so an ancient or stripped tmux cannot stop tasks
2828
- * from spawning — the session still works without the scroll comforts.
2952
+ * Gives an attached runner terminal wheel/PageUp scrolling over a deep
2953
+ * scrollback, and a mouse selection that reaches the system clipboard — none
2954
+ * of which a default tmux session does. Server-wide scoping is harmless: the
2955
+ * daemon owns a private socket (`tmuxSocketName`).
2956
+ *
2957
+ * `mouse on` is what costs the emulator's own drag-select, so tmux must hand
2958
+ * the selection back out itself: via a clipboard binary when there is one,
2959
+ * and via OSC 52 (`set-clipboard`) for what that cannot cover, such as
2960
+ * viewing over SSH. Naming the binary on the binding as well as in
2961
+ * `copy-command` keeps drag-to-copy working on tmux older than 3.2, which has
2962
+ * no `copy-command`.
2829
2963
  */
2830
2964
  private configureScrolling;
2831
2965
  /**
@@ -2884,6 +3018,26 @@ declare function wrapWithPty(command: string, args: string[]): {
2884
3018
  args: string[];
2885
3019
  };
2886
3020
 
3021
+ /**
3022
+ * A workspace path that does not exist, or exists but is not a directory.
3023
+ * Checked at every surface that starts a planner or spawns an agent inside
3024
+ * one: a shell whose own cwd was deleted still reports the stale path from
3025
+ * `process.cwd()`, so a stat is the only reliable signal, never the string
3026
+ * itself. Left unchecked, the underlying failure is `spawn`'s ENOENT — which
3027
+ * reads as a missing binary, not a missing directory. No status code: this is
3028
+ * transport-agnostic, and each surface maps it to its own response shape.
3029
+ */
3030
+ declare class WorkspaceNotFoundError extends Error {
3031
+ readonly workspace: string;
3032
+ constructor(workspace: string);
3033
+ }
3034
+ interface WorkspaceCheckDeps {
3035
+ /** True when `candidate` names an existing directory. Defaults to a real `fs.statSync`. */
3036
+ isDirectory?: (candidate: string) => boolean;
3037
+ }
3038
+ /** Throws {@link WorkspaceNotFoundError} unless `workspace` names an existing directory. */
3039
+ declare function assertWorkspaceExists(workspace: string, deps?: WorkspaceCheckDeps): void;
3040
+
2887
3041
  interface KillTreeDeps {
2888
3042
  platform?: NodeJS.Platform;
2889
3043
  /** Runs `taskkill`. Injected so the Windows path is testable off Windows. */
@@ -2970,6 +3124,15 @@ declare function tmuxWindowName(taskId: string, planSessionId?: string): string;
2970
3124
  type ProbeFn = () => void;
2971
3125
  /** Feature-detects tmux the same way `HeadlessRunner` detects `script`. */
2972
3126
  declare function hasTmux(probe?: ProbeFn): boolean;
3127
+ type HasBinFn = (bin: string) => boolean;
3128
+ /**
3129
+ * Shell command tmux should pipe a copy-mode selection into, so selecting text
3130
+ * in a task's terminal puts it on the *system* clipboard rather than in a tmux
3131
+ * paste buffer no other application can read. Preferred over tmux's OSC 52
3132
+ * bridge, which plenty of emulators (VTE before 0.76, xterm without
3133
+ * `allowWindowOps`) drop on the floor.
3134
+ */
3135
+ declare function clipboardCopyCommand(hasBin?: HasBinFn, platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): string | null;
2973
3136
 
2974
3137
  declare class FsPluginStore implements IPluginStore {
2975
3138
  getUserPluginsDir(): string;
@@ -3030,4 +3193,55 @@ declare function sanitizeSlug(raw: string): string;
3030
3193
  /** Save the PRD to `<workspace>/.scratch/<slug>/PRD.md` (to-prd native convention). */
3031
3194
  declare function savePrdMarkdown(workspace: string, slug: string, markdown: string): string;
3032
3195
 
3033
- export { ALL_PROVIDERS, AUTO_COMMANDS, AbstractRunner, AbstractTerminalSession, ActiveTaskSession, type AgentAdapter, type AgentAdapterFactory, type AgentEvent, type AgentProcessDeps, type AgentStartOptions, AiProvider, ApprovalKind, ApprovalMode, ApprovalRequest, ApprovalSource, 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, DiscoveredModel, EmbeddedNewlineError, EnvConfig, type ExecFileFn, type ExecImpl, FindSymbolOptions, FsPluginStore, GIT_READONLY_SUBCOMMANDS, GeminiService, GlobOptions, type GrepInvocation, GrepOptions, 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, OpenAiService, OpenCodeAdapter, type OrchestratorObserver, OrchestratorOption, PROVIDER_DETECT_PRIORITY, PROVIDER_LABEL, PROVIDER_PRIORITY, PROVIDER_SHORT_LABEL, type PendingApproval, PendingApprovals, type PendingApprovalsOptions, PlanParseError, type PlanRequest, PlanState, PlanStatus, PlanStore, Planner, type PlannerReplyClassification, type PlannerRuntimeToggles, type PrdBlock, type PreparedLaunch, type PriorOutput, type ProbeFn, ProviderModelLists, type ProviderRegistration, QueuedMessage, REFUSED_COMMANDS, ReadFileOpts, type RepairLoopOpts, type RepairVerdict, type ResearchChat, ResearchLogEntry, ResearchProgress, type ResearchShell, type ResearchShellDeps, ResearchStep, ResearchToolType, type ResearchTurn, ResolveContext, type RunnerCatalog, RunnerId, RunnerInstallation, RunnerInvocation, type RunnerMode, RunnerModeInfo, RunnerPluginManifest, RunnerRegistry, type RunnerSpawnOptions, STATE_DIR, SYMBOL_LANGUAGES, type SerializedPlan, type SerializedTask, type SerializedTaskStatus, Session, type SessionBroadcaster, type SessionData, type SessionDeps, type SessionMessage, type SessionMeta, type SessionPlanner, type SessionRuntimeSettings, SettingsService, type ShellDialect, type SpawnSpec, StdioAgentAdapter, TRUNCATED_PLAN_REPAIR_INSTRUCTION, Task, TaskOp, TaskOrchestrator, TaskSnapshot, TmuxRunner, type TmuxRunnerDeps, type ToolCall, ToolOutcome, type ToolResult, type UserSettings, Verdict, VerdictEngine, type VerdictListener, WINDOWS_MAX_COMMAND_LINE, applyHeadLimit, augmentPromptWithPriorOutputs, augmentedPath, buildConversationSystemPrompt, buildFallbackGrepArgs, buildGlobArgs, buildGrepArgs, buildMergePrompt, buildModifyDuringExecutionPrompt, buildModifyPlanPrompt, buildPlanWithResults, buildResearchPrompt, buildResearchToolsPrompt, buildRunnerInvocation, buildShellInvocation, buildSplitPrompt, buildSubagentSystemPrompt, clampThinkingEffort, classifyCommand, classifyPlannerReply, clearAugmentedPathCache, clearResearchShellCache, clearState, coerceAssignments, collectDirectDependencyOutputs, composeAugmentedPrompt, configuredProviders, createAiService, defaultLogger, definitionPattern, deleteSession, discoverGeminiModels, effectiveAllowlist, ensureDir, executionLogBlock, executionSummary, extractPrdBlock, filterFallbackByAnchoredInclude, filterModelsForPrompt, formatSearchOutput, generatePlanWithRepair, getLatestSession, getProviderMeta, getSettingsPath, getStateDir, grantScopeFor, hasTmux, includeGlobFor, isCliProvider, isOpenAiProvider, killTree, languageForId, listSessions, loadSession, loadSessionPlanState, loadState, mapAgentTool, modelContextBlock, modifyValidationFeedback, normalizeAgentArgs, normalizeGeminiModel, pendingEditRulesBlock, planDirectLaunch, planShellLaunch, posixShellQuote, prefixModelId, providerForRunner, reEmitPlanPrompt, reEmitTaskOpsPrompt, referencePattern, renderPlanMap, renderPriorOutputs, repairLoop, researchShellWarning, researchToolsPath, resolveArgs, resolveProviderFromPrefix, resolveResearchShell, resolveWithin, retargetTaskRunner, runnerAssignment, runnerForProvider, sanitizeSlug, savePrdMarkdown, saveSession, saveState, serializePlan, serializeTask, serializeTaskStatus, sessionRuntimeSettings, stateExists, stripAnsi, stripModelPrefix, summarizeOutput, taskOpsRejectedPrompt, tmuxSessionName, tmuxSocketName, tmuxWindowName, truncatedPlanReEmitPrompt, wellKnownBinDirs, windowsCommandLine, withPath, wrapWithPty };
3196
+ /** What the user last picked for one planner backend. */
3197
+ interface PlannerModelChoice {
3198
+ model: string;
3199
+ effort?: string;
3200
+ }
3201
+ /**
3202
+ * A candidate model as this planner's catalog lists it. Deliberately plain
3203
+ * data: a harness planner's catalog is its runner's own model list and a
3204
+ * vendor's is a picker list (ADR-0009), and the rule below is the same for
3205
+ * both — so the caller normalizes, and this module never reaches for a
3206
+ * ModelResolver, a config or the network.
3207
+ */
3208
+ interface PlannerModelCandidate {
3209
+ id: string;
3210
+ variants?: {
3211
+ id: string;
3212
+ }[];
3213
+ }
3214
+ interface PlannerModelRecall {
3215
+ model: string;
3216
+ effort: string;
3217
+ source: 'remembered' | 'catalog-default' | 'none';
3218
+ }
3219
+ /** The slice of `SettingsService` this needs, so tests can pass a plain object. */
3220
+ interface PlannerModelStore {
3221
+ getPlannerModel(provider: string): PlannerModelChoice | undefined;
3222
+ setPlannerModel(provider: string, entry: PlannerModelChoice | undefined): void;
3223
+ }
3224
+ /**
3225
+ * Remembers, per planner backend, the model the user last chose for it.
3226
+ *
3227
+ * Switching the planner has to clear the model — an id is scoped to the agent
3228
+ * that listed it, so an OpenRouter slug handed to Claude Code points it at
3229
+ * something it cannot spawn. That clear is correct and stays; what was missing
3230
+ * is that switching *back* then forced a re-pick of a model the user had
3231
+ * already chosen. Both surfaces' clears and the restore now read one rule from
3232
+ * here rather than three copies drifting apart.
3233
+ */
3234
+ declare class PlannerModelMemory {
3235
+ private store;
3236
+ constructor(store: PlannerModelStore);
3237
+ /** Record what the user picked for `provider`; a blank model forgets it instead. */
3238
+ remember(provider: string, model: string, effort?: string): void;
3239
+ /**
3240
+ * What the model and effort should become when `provider` takes over the
3241
+ * planner, given the models it actually serves. The `source` is what the
3242
+ * three surfaces word their notice from — restored, defaulted, or nothing.
3243
+ */
3244
+ recall(provider: string, catalog: PlannerModelCandidate[]): PlannerModelRecall;
3245
+ }
3246
+
3247
+ export { ALL_PROVIDERS, AUTO_COMMANDS, AbstractRunner, AbstractTerminalSession, ActiveTaskSession, type AgentAdapter, type AgentAdapterFactory, type AgentEvent, type AgentProcessDeps, type AgentStartOptions, AiProvider, ApprovalKind, ApprovalMode, ApprovalRequest, ApprovalSource, 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, DiscoveredModel, EmbeddedNewlineError, 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, OpenAiService, OpenCodeAdapter, type OrchestratorObserver, OrchestratorOption, 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 PrdBlock, type PreparedLaunch, type PriorOutput, type ProbeFn, ProviderModelLists, type ProviderRegistration, QueuedMessage, REFUSED_COMMANDS, ReadFileOpts, type RepairLoopOpts, type RepairVerdict, type ResearchChat, ResearchLogEntry, ResearchProgress, type ResearchShell, type ResearchShellDeps, ResearchStep, ResearchToolType, type ResearchTurn, ResolveContext, type RunnerCatalog, RunnerId, RunnerInstallation, RunnerInvocation, type RunnerMode, RunnerModeInfo, RunnerPluginManifest, RunnerRegistry, type RunnerSpawnOptions, STATE_DIR, STOPPED_TOOL_RESULT, SYMBOL_LANGUAGES, type SerializedPlan, type SerializedTask, type SerializedTaskStatus, Session, type SessionBroadcaster, type SessionData, type SessionDeps, type SessionMessage, type SessionMeta, type SessionPlanner, type SessionRuntimeSettings, SettingsService, type ShellDialect, type SpawnSpec, StdioAgentAdapter, TRUNCATED_PLAN_REPAIR_INSTRUCTION, Task, TaskOp, TaskOrchestrator, TaskSnapshot, TmuxRunner, type TmuxRunnerDeps, type ToolCall, ToolOutcome, type ToolResult, type UserSettings, Verdict, VerdictEngine, type VerdictListener, WINDOWS_MAX_COMMAND_LINE, type WorkspaceCheckDeps, WorkspaceNotFoundError, applyHeadLimit, assertWorkspaceExists, augmentPromptWithPriorOutputs, augmentedPath, buildConversationSystemPrompt, buildFallbackGrepArgs, buildGlobArgs, buildGrepArgs, buildMergePrompt, buildModifyDuringExecutionPrompt, buildModifyPlanPrompt, buildPlanWithResults, buildResearchPrompt, buildResearchToolsPrompt, buildRunnerInvocation, buildShellInvocation, buildSplitPrompt, buildSubagentSystemPrompt, clampThinkingEffort, classifyCommand, classifyPlannerReply, clearAugmentedPathCache, clearResearchShellCache, clearState, clipboardCopyCommand, coerceAssignments, collectDirectDependencyOutputs, composeAugmentedPrompt, configuredProviders, createAiService, defaultLogger, definitionPattern, deleteSession, discoverGeminiModels, effectiveAllowlist, ensureDir, executionLogBlock, executionSummary, extractPrdBlock, filterFallbackByAnchoredInclude, filterModelsForPrompt, formatSearchOutput, generatePlanWithRepair, getLatestSession, getProviderMeta, getSettingsPath, getStateDir, grantScopeFor, hasTmux, includeGlobFor, isCliProvider, isExecutableResolved, isOpenAiProvider, killTree, languageForId, listSessions, loadSession, loadSessionPlanState, loadState, mapAgentTool, modelContextBlock, modifyValidationFeedback, normalizeAgentArgs, normalizeGeminiModel, pendingEditRulesBlock, planDirectLaunch, planShellLaunch, posixShellQuote, prefixModelId, providerForRunner, reEmitPlanPrompt, reEmitTaskOpsPrompt, referencePattern, renderPlanMap, renderPriorOutputs, repairLoop, researchShellWarning, researchToolsPath, resolveArgs, resolveProviderFromPrefix, resolveResearchShell, resolveWithin, retargetTaskRunner, runnerAssignment, runnerForProvider, sanitizeSlug, savePrdMarkdown, saveSession, saveState, serializePlan, serializeTask, serializeTaskStatus, sessionRuntimeSettings, stateExists, stripAnsi, stripModelPrefix, summarizeOutput, taskOpsRejectedPrompt, tmuxSessionName, tmuxSocketName, tmuxWindowName, truncatedPlanReEmitPrompt, wellKnownBinDirs, windowsCommandLine, withPath, wrapWithPty };