@ordewell/core 0.10.4 → 0.10.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/{IRunner-D_UkkMbJ.d.ts → IFileSystem-C4_mpfze.d.ts} +159 -15
- package/dist/{IRunner-NtTHQgLR.d.mts → IFileSystem-DUgeWWQN.d.mts} +159 -15
- package/dist/{ModeResolver-wnTFsDnF.d.mts → ModeResolver-4qNjxcDs.d.mts} +1 -1
- package/dist/{ModeResolver-Dy3Ef6p-.d.ts → ModeResolver-DGK2rWHE.d.ts} +1 -1
- package/dist/{Task-DZs9eKgg.d.mts → Task-C0o4i3Nn.d.mts} +114 -13
- package/dist/{Task-DZs9eKgg.d.ts → Task-C0o4i3Nn.d.ts} +114 -13
- package/dist/{chunk-FH6M5QZG.mjs → chunk-DMG4ISUG.mjs} +1008 -49
- package/dist/chunk-DMG4ISUG.mjs.map +1 -0
- package/dist/{chunk-SM6BV6XS.mjs → chunk-EEE2GBLG.mjs} +116 -27
- package/dist/chunk-EEE2GBLG.mjs.map +1 -0
- package/dist/chunk-H7QX32BH.mjs +647 -0
- package/dist/chunk-H7QX32BH.mjs.map +1 -0
- package/dist/{chunk-645FALZX.mjs → chunk-LTHKFBKM.mjs} +4 -4
- package/dist/chunk-LTHKFBKM.mjs.map +1 -0
- package/dist/{chunk-OYUSADFM.mjs → chunk-NJGID6BG.mjs} +3 -1
- package/dist/chunk-NJGID6BG.mjs.map +1 -0
- package/dist/index.d.mts +498 -1689
- package/dist/index.d.ts +498 -1689
- package/dist/index.js +4108 -2082
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3380 -1639
- package/dist/index.mjs.map +1 -1
- package/dist/parsing.d.mts +2 -2
- package/dist/parsing.d.ts +2 -2
- package/dist/parsing.js +231 -75
- package/dist/parsing.js.map +1 -1
- package/dist/parsing.mjs +3 -3
- package/dist/plan-utils.d.mts +20 -4
- package/dist/plan-utils.d.ts +20 -4
- package/dist/plan-utils.js +335 -88
- package/dist/plan-utils.js.map +1 -1
- package/dist/plan-utils.mjs +43 -19
- package/dist/{plan-utils-CtKceSsQ.d.mts → runSettingsText-BgOmLxdw.d.mts} +2242 -224
- package/dist/{plan-utils-POIz5d8z.d.ts → runSettingsText-BgXvajj2.d.ts} +2242 -224
- package/dist/testing.d.mts +5 -3
- package/dist/testing.d.ts +5 -3
- package/dist/testing.js +7 -0
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +7 -2
- package/dist/testing.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/IFileSystem-_effm8-b.d.ts +0 -78
- package/dist/IFileSystem-vyqMdL-x.d.mts +0 -78
- package/dist/chunk-645FALZX.mjs.map +0 -1
- package/dist/chunk-A57ITQE7.mjs +0 -1378
- package/dist/chunk-A57ITQE7.mjs.map +0 -1
- package/dist/chunk-FH6M5QZG.mjs.map +0 -1
- package/dist/chunk-OYUSADFM.mjs.map +0 -1
- package/dist/chunk-SM6BV6XS.mjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { ai as Task, a4 as RunnerId, aq as TaskSnapshot, b as AwaitingReason, av as UsageRecord, a5 as RunnerSessionCost, d as DiscoveredModel, af as SkillsService, aa as SkillInfo, Z as RepoGroupLayout, I as IWorktreeIsolation, w as IsolationRun, k as IsolationHandoff, J as PlanIsolation, x as IsolationTaskRecord, al as TaskIsolation, H as IsolationView, S as PrepareIntent, V as PreviousWork, s as IsolationOutcome, X as RepairEvidence, at as TreeSnapshot, aj as TaskDiffRead, ak as TaskDiffResult, r as IsolationMergeResult, ax as Verdict, ap as TaskSkillSnapshot, ao as TaskSkillNotice, ag as SubagentOutcome, L as LegacyPlanState, C as ConversationMessage, ac as SkillLoadNotice, P as PendingPlanTurn, a as AttemptUsage, a3 as RunSettings, O as PlanUsage, Y as ReplyRun, M as ModelUsage, aw as UsageTotals, f as ExecutionRecord, a0 as ResearchStep, K as PlanState, $ as ResearchProgress, _ as ResearchLogEntry, ab as SkillLoad, N as PlanStatus, ah as SupervisorLevel, a1 as ResearchStepOutcome, R as PlannerUsage, am as TaskIsolationState, as as TaskUsageSummary } from './Task-C0o4i3Nn.js';
|
|
2
|
+
import { N as IRunnerSession, K as IConfig, aL as TaskMessageFrom, ax as RunnerModelRef, ap as RunnerCommand, au as RunnerMcpServer, aE as StructuredTurnEnd, j as ApprovalDecision, aD as StructuredEvent, ah as QueuedTaskMessage, k as ApprovalKind, q as ApprovalSource, M as IRunner, ay as RunnerRegistry, J as IApproval, aU as ToolOutcome, d as AgentProcessDeps, a as AgentAdapter, a4 as OrdewellMcpServer, L as IFileSystem, aV as UsageLimitSignal, ac as PlannerToolHandler, p as ApprovalRequest, i as ApprovalAnswer, f as AiProvider, a3 as OrchestratorOption, a9 as PlannerBackendId } from './IFileSystem-C4_mpfze.js';
|
|
3
|
+
import { R as RunnerModeInfo } from './ModeResolver-DGK2rWHE.js';
|
|
4
4
|
|
|
5
5
|
type NotificationAction = {
|
|
6
6
|
label: string;
|
|
@@ -142,15 +142,15 @@ declare class PlanStore {
|
|
|
142
142
|
setForcedPastGate(id: string, dependencies: string[]): void;
|
|
143
143
|
/** A reason outlives nothing: any status but `awaiting_user` drops it, and the resume time with it; any status drops a skip's reason. */
|
|
144
144
|
private setStatus;
|
|
145
|
-
blockDependents(id: string): void;
|
|
146
|
-
unblockDependents(id: string): void;
|
|
147
145
|
setTaskVerdict(id: string, verdict: Task['verdict']): void;
|
|
148
146
|
setTaskOutputSummary(id: string, summary: Task['outputSummary']): void;
|
|
149
|
-
|
|
147
|
+
/** The owner is saved with the session it belongs to, and dropped with it. */
|
|
148
|
+
setTaskRunnerSessionId(id: string, runnerSessionId: string | undefined, owner?: RunnerId): void;
|
|
149
|
+
setCarriedFrom(id: string, runner: RunnerId): void;
|
|
150
150
|
clearCarriedFrom(id: string): void;
|
|
151
151
|
startAttemptUsage(id: string, attempt: number, runner: string, startedAt: string): void;
|
|
152
152
|
recordAttemptUsage(id: string, attempt: number, record: UsageRecord, now: string): void;
|
|
153
|
-
endAttemptUsage(id: string, attempt: number, now: string): void;
|
|
153
|
+
endAttemptUsage(id: string, attempt: number, now: string, runnerSessionCost?: RunnerSessionCost, sessionId?: string): void;
|
|
154
154
|
setTaskAttemptSkills(id: string, skills: Task['attemptSkills']): void;
|
|
155
155
|
/**
|
|
156
156
|
* Widen the plan's runner set. Retargeting a task onto a runner the plan has
|
|
@@ -165,19 +165,90 @@ declare class PlanStore {
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
/**
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
168
|
+
* Who writes a task's assignment. It decides what a model the writer names
|
|
169
|
+
* meets where the task's runner does not offer it or the allowlist excludes it
|
|
170
|
+
* (ADR-0003), which runners it may put a task on, and whether a live runner's
|
|
171
|
+
* hold applies (ADR-0041):
|
|
172
|
+
*
|
|
173
|
+
* - `plan`: a whole plan the planner submitted, and every planner task as it
|
|
174
|
+
* lands. A stray model is replaced, and the planner is told what moved.
|
|
175
|
+
* - `edit`: the planner's edit. A stray model is refused, which the planner
|
|
176
|
+
* answers in the same reply.
|
|
177
|
+
* - `user`: a user's direct edit. A stray model is refused, with the reason.
|
|
178
|
+
* - `userAdd`: a task the user adds. A stray model is replaced, and the user
|
|
179
|
+
* is told what moved.
|
|
180
|
+
* - `runner`: a switch made with the runner's own `/model`, written as the
|
|
181
|
+
* user's edit. The runner already runs it, so neither the allowlist nor the
|
|
182
|
+
* live runner's hold applies.
|
|
183
|
+
*/
|
|
184
|
+
type AssignmentWriter = 'plan' | 'edit' | 'user' | 'userAdd' | 'runner';
|
|
185
|
+
/**
|
|
186
|
+
* What an assignment is resolved against: the discovered models, the manifest
|
|
187
|
+
* modes and the allowlist, as the session holds them when the write lands —
|
|
188
|
+
* the same catalog the planner's per-turn block shows, so nothing is refused
|
|
189
|
+
* that the planner was never told about.
|
|
172
190
|
*/
|
|
173
|
-
interface
|
|
191
|
+
interface AssignmentCatalog {
|
|
192
|
+
/** A runner absent, or listed with nothing, has models discovery could not find, not none. */
|
|
174
193
|
modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>;
|
|
175
|
-
|
|
176
|
-
|
|
194
|
+
/** Absent when no runner's modes are known: no mode is then derived. */
|
|
195
|
+
runnerModes?: Partial<Record<RunnerId, readonly RunnerModeInfo[]>>;
|
|
196
|
+
/** Raw (unfiltered) allowlist, keyed by runner; read through {@link effectiveAllowlist}. */
|
|
177
197
|
perRunnerAllowlist?: Partial<Record<RunnerId, string[]>>;
|
|
178
|
-
/** Full (true) or Guarded: the mode a task
|
|
198
|
+
/** Full (true) or Guarded: the mode a derived task lands on unless it is read-only (ADR-0001). Full when absent. */
|
|
179
199
|
autonomousDefault?: boolean;
|
|
200
|
+
/**
|
|
201
|
+
* Whether a task's runner is live: an attempt of it is claimed, running,
|
|
202
|
+
* quiet or waiting on someone. Its model, effort and runner are then that
|
|
203
|
+
* runner's (ADR-0041): the process was started on them, and only the
|
|
204
|
+
* runner's own command moves them. Absent means none is.
|
|
205
|
+
*/
|
|
206
|
+
liveRunner?: (taskId: string) => boolean;
|
|
207
|
+
}
|
|
208
|
+
/** What a runner offers a task, as discovery and its manifest have it. */
|
|
209
|
+
interface RunnerCatalog {
|
|
210
|
+
models: DiscoveredModel[];
|
|
211
|
+
modes: RunnerModeInfo[];
|
|
212
|
+
}
|
|
213
|
+
/** A change the resolver made to what was written, named so it is never silent (ADR-0001). */
|
|
214
|
+
interface AssignmentAdjustment {
|
|
215
|
+
taskId: string;
|
|
216
|
+
title: string;
|
|
217
|
+
field: 'assignedModel' | 'thinkingEffort';
|
|
218
|
+
from: string | null;
|
|
219
|
+
to: string | null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Plan placement and dependency rules, shared with browser-bundled surfaces. */
|
|
223
|
+
interface TreeTask<T> {
|
|
224
|
+
id: string;
|
|
225
|
+
order: number;
|
|
226
|
+
title: string;
|
|
227
|
+
subtasks?: readonly T[];
|
|
228
|
+
}
|
|
229
|
+
interface DependencyTask<T> extends TreeTask<T> {
|
|
230
|
+
dependencies: readonly string[];
|
|
231
|
+
}
|
|
232
|
+
interface TaskPlacement<T> {
|
|
233
|
+
task: T;
|
|
234
|
+
parent?: T;
|
|
235
|
+
ancestors: readonly T[];
|
|
236
|
+
siblings: readonly T[];
|
|
237
|
+
label: string;
|
|
238
|
+
}
|
|
239
|
+
declare function walkPlan<T extends TreeTask<T>>(tasks: readonly T[]): Generator<TaskPlacement<T>>;
|
|
240
|
+
declare function taskPlacement<T extends TreeTask<T>>(tasks: readonly T[], id: string): TaskPlacement<T> | undefined;
|
|
241
|
+
/** Includes the root: deletion has to detach every edge into the removed tree. */
|
|
242
|
+
declare function subtreeIds<T extends TreeTask<T>>(tasks: readonly T[], id: string): Set<string>;
|
|
243
|
+
/** Pickers offer earlier siblings; API validity is independent of display order. */
|
|
244
|
+
declare function dependencyCandidates<T extends TreeTask<T>>(tasks: readonly T[], taskId?: string): T[];
|
|
245
|
+
/** The surviving tasks whose dependency lists a subtree removal will change. */
|
|
246
|
+
declare function dependentsOf<T extends DependencyTask<T>>(tasks: readonly T[], taskId: string): T[];
|
|
247
|
+
interface DependencyCheck {
|
|
248
|
+
ok: boolean;
|
|
249
|
+
error?: string;
|
|
180
250
|
}
|
|
251
|
+
declare function canSetDependencies<T extends DependencyTask<T>>(tasks: readonly T[], taskId: string, dependencies: readonly string[]): DependencyCheck;
|
|
181
252
|
|
|
182
253
|
/**
|
|
183
254
|
* Targeted task edits emitted by the planner conversation (the 'task_ops'
|
|
@@ -231,6 +302,8 @@ interface ApplyTaskOpsResult {
|
|
|
231
302
|
errors: string[];
|
|
232
303
|
/** Human-readable summary of what changed, for the chat transcript. */
|
|
233
304
|
summary: string[];
|
|
305
|
+
/** On ok, what the batch's tasks were adjusted to beyond what the planner wrote, as they stand at its end. */
|
|
306
|
+
adjustments?: AssignmentAdjustment[];
|
|
234
307
|
/** On ok, the id of the task each batch handle names. */
|
|
235
308
|
handles?: ReadonlyMap<string, string>;
|
|
236
309
|
}
|
|
@@ -260,22 +333,7 @@ interface TaskRef {
|
|
|
260
333
|
title: string;
|
|
261
334
|
dependencies: string[];
|
|
262
335
|
}
|
|
263
|
-
|
|
264
|
-
declare function dependentsOf<T extends Pick<TaskRef, 'id' | 'dependencies'>>(tasks: T[], taskId: string): T[];
|
|
265
|
-
/**
|
|
266
|
-
* The tasks that may become dependencies of `taskId`: those displayed before it.
|
|
267
|
-
*
|
|
268
|
-
* Offering only earlier tasks is what keeps a hand-edited graph valid without a
|
|
269
|
-
* cycle check — dependencies then only ever point backwards in display order,
|
|
270
|
-
* the same invariant `applyTaskOps` enforces. Omit `taskId` for a task that does
|
|
271
|
-
* not exist yet: it lands last, so every current task is a candidate.
|
|
272
|
-
*/
|
|
273
|
-
declare function dependencyCandidates<T extends Pick<TaskRef, 'id' | 'order'>>(tasks: readonly T[], taskId?: string): T[];
|
|
274
|
-
/** Pre-flight check for a hand-edited dependency list: every id exists and comes earlier. */
|
|
275
|
-
declare function canSetDependencies<T extends TaskRef>(tasks: readonly T[], taskId: string, dependencies: string[]): {
|
|
276
|
-
ok: boolean;
|
|
277
|
-
error?: string;
|
|
278
|
-
};
|
|
336
|
+
|
|
279
337
|
/** Pre-flight check for a split: the task exists and is not locked by execution. */
|
|
280
338
|
declare function canSplitTask(tasks: readonly Task[], taskId: string): {
|
|
281
339
|
ok: boolean;
|
|
@@ -287,7 +345,7 @@ declare function canSplitTask(tasks: readonly Task[], taskId: string): {
|
|
|
287
345
|
* completed tasks untouched), or nothing is returned and `errors` explains
|
|
288
346
|
* why. The caller commits `tasks` on ok.
|
|
289
347
|
*/
|
|
290
|
-
declare function applyTaskOps(currentTasks: readonly Task[], newOps: TaskOp[], runners: RunnerId[], catalog?:
|
|
348
|
+
declare function applyTaskOps(currentTasks: readonly Task[], newOps: TaskOp[], runners: RunnerId[], catalog?: AssignmentCatalog, held?: readonly TaskOp[]): ApplyTaskOpsResult;
|
|
291
349
|
|
|
292
350
|
/**
|
|
293
351
|
* Task skills end to end (ADR-0024): a skill reaches a runner only by being
|
|
@@ -358,6 +416,8 @@ interface IsolationRunListener {
|
|
|
358
416
|
blocked(repos: string[]): void;
|
|
359
417
|
/** An isolated run closed and handed its integration branches over. */
|
|
360
418
|
handoff(handoff: IsolationHandoff): void;
|
|
419
|
+
/** The run was cleared up and forgotten: nothing was handed over, or it was discarded. */
|
|
420
|
+
closed(): void;
|
|
361
421
|
/** Already said through the notifications; also for a surface that shows no toasts. */
|
|
362
422
|
notice(level: IsolationNoticeLevel, message: string): void;
|
|
363
423
|
}
|
|
@@ -724,6 +784,86 @@ declare class Landing {
|
|
|
724
784
|
private isGroup;
|
|
725
785
|
}
|
|
726
786
|
|
|
787
|
+
/**
|
|
788
|
+
* Why a task cannot start now: the one start rule (ADR-0034), which the
|
|
789
|
+
* scheduler, the explicit starts, the command seam's refusals, the task row's
|
|
790
|
+
* merge gate and the planner's notes all read. {@link startWaits} lists every
|
|
791
|
+
* one that holds, in this order, so the first is the reason a task waits.
|
|
792
|
+
*/
|
|
793
|
+
type StartWait =
|
|
794
|
+
/** Under way, waiting on the user, or ended: only a retry, a continue or a resume starts it again. */
|
|
795
|
+
{
|
|
796
|
+
kind: 'started';
|
|
797
|
+
}
|
|
798
|
+
/** Mark complete is landing its work. */
|
|
799
|
+
| {
|
|
800
|
+
kind: 'completing';
|
|
801
|
+
}
|
|
802
|
+
/** An AI task with no prompt yet: there is nothing to run. */
|
|
803
|
+
| {
|
|
804
|
+
kind: 'no-prompt';
|
|
805
|
+
}
|
|
806
|
+
/** Outside the user's approval record (ADR-0035). */
|
|
807
|
+
| {
|
|
808
|
+
kind: 'unapproved';
|
|
809
|
+
}
|
|
810
|
+
/** Pulled out of the scheduler: cancelled, marked not done, or a spawn that failed. */
|
|
811
|
+
| {
|
|
812
|
+
kind: 'on-hold';
|
|
813
|
+
}
|
|
814
|
+
/** The failed tasks it waits on through its dependencies (ADR-0034). */
|
|
815
|
+
| {
|
|
816
|
+
kind: 'blocked';
|
|
817
|
+
failed: string[];
|
|
818
|
+
}
|
|
819
|
+
/** What its dependencies still hold it back for (see {@link unmetDependencies}). */
|
|
820
|
+
| {
|
|
821
|
+
kind: 'dependencies';
|
|
822
|
+
waiting: string[];
|
|
823
|
+
}
|
|
824
|
+
/** A force start queued behind the dependencies still under way or yet to run, which starts once they finish. */
|
|
825
|
+
| {
|
|
826
|
+
kind: 'queued';
|
|
827
|
+
waiting: string[];
|
|
828
|
+
}
|
|
829
|
+
/** An ops task, and a Merge all is under way (ADR-0020). */
|
|
830
|
+
| {
|
|
831
|
+
kind: 'merging';
|
|
832
|
+
}
|
|
833
|
+
/** Its runner hit its usage limit: nothing more starts on that runner until the reset. */
|
|
834
|
+
| {
|
|
835
|
+
kind: 'usage-limit';
|
|
836
|
+
runner: string;
|
|
837
|
+
}
|
|
838
|
+
/** A planner turn holds new starts until its reply ends (ADR-0029). */
|
|
839
|
+
| {
|
|
840
|
+
kind: 'start-hold';
|
|
841
|
+
}
|
|
842
|
+
/** Its merge gate (ADR-0020): the ancestors whose landed work is not in the user's branch yet. */
|
|
843
|
+
| {
|
|
844
|
+
kind: 'merge-gate';
|
|
845
|
+
unmerged: string[];
|
|
846
|
+
}
|
|
847
|
+
/** A user task: the user does it by hand, and nothing starts it. */
|
|
848
|
+
| {
|
|
849
|
+
kind: 'by-hand';
|
|
850
|
+
};
|
|
851
|
+
/**
|
|
852
|
+
* A start the user forced, which goes past what the scheduler waits for: a
|
|
853
|
+
* hold, a failed dependency (ADR-0034), its runner's usage limit, and the
|
|
854
|
+
* merge gate they confirmed (ADR-0020). Made now, it is the user's own act
|
|
855
|
+
* and goes past the whole gate and a planner turn's start hold. Queued behind
|
|
856
|
+
* its dependencies, it starts later with nobody asking, so it waits for a
|
|
857
|
+
* start hold as the scheduler's starts do, and goes past only the gate it was
|
|
858
|
+
* confirmed past: `pastGate`.
|
|
859
|
+
*/
|
|
860
|
+
type ForcedStart = {
|
|
861
|
+
queued: false;
|
|
862
|
+
} | {
|
|
863
|
+
queued: true;
|
|
864
|
+
pastGate: readonly string[];
|
|
865
|
+
};
|
|
866
|
+
|
|
727
867
|
/**
|
|
728
868
|
* The variables one workspace's planner and agents run with, on top of the
|
|
729
869
|
* daemon's own environment (ADR-0016).
|
|
@@ -799,6 +939,23 @@ type TaskLogEvent =
|
|
|
799
939
|
preTokens?: number;
|
|
800
940
|
postTokens?: number;
|
|
801
941
|
}
|
|
942
|
+
/**
|
|
943
|
+
* The runner's main thread moved to another model, and what the plan says
|
|
944
|
+
* once the write-back settled (#106): written from its outcome, not from the
|
|
945
|
+
* runner's event, so the line never claims an edit that did not land.
|
|
946
|
+
* `kept` says why the plan did not follow, when it did not. A log saved
|
|
947
|
+
* before the plan's side was recorded has no `plan`: it says nothing of it.
|
|
948
|
+
*/
|
|
949
|
+
| {
|
|
950
|
+
type: 'model_switched';
|
|
951
|
+
from: RunnerModelRef;
|
|
952
|
+
to: RunnerModelRef;
|
|
953
|
+
plan?: {
|
|
954
|
+
model?: RunnerModelRef;
|
|
955
|
+
effort?: string;
|
|
956
|
+
};
|
|
957
|
+
kept?: string;
|
|
958
|
+
}
|
|
802
959
|
/** What the runner loaded from the user's and the project's setup, as its process started. */
|
|
803
960
|
| {
|
|
804
961
|
type: 'runner_loaded';
|
|
@@ -807,13 +964,21 @@ type TaskLogEvent =
|
|
|
807
964
|
} | {
|
|
808
965
|
type: 'turn_end';
|
|
809
966
|
reason: StructuredTurnEnd;
|
|
810
|
-
}
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* A piece of the run `runId` of the agent's reply (see `AgentEvent`). A log
|
|
970
|
+
* saved before runs carried an id has none, and opens each later run of a
|
|
971
|
+
* turn with the paragraph break that joined it.
|
|
972
|
+
*/
|
|
973
|
+
| {
|
|
811
974
|
type: 'text_delta';
|
|
975
|
+
runId?: string;
|
|
812
976
|
text: string;
|
|
813
977
|
}
|
|
814
|
-
/** A complete run of the agent's reply, authoritative over the deltas streamed
|
|
978
|
+
/** A complete run of the agent's reply, authoritative over the deltas streamed under its `runId`. */
|
|
815
979
|
| {
|
|
816
980
|
type: 'text';
|
|
981
|
+
runId?: string;
|
|
817
982
|
text: string;
|
|
818
983
|
} | {
|
|
819
984
|
type: 'thinking_delta';
|
|
@@ -940,15 +1105,17 @@ declare function trimToolOutput(output: string): {
|
|
|
940
1105
|
};
|
|
941
1106
|
/**
|
|
942
1107
|
* The log entry for one structured event, or null for what the log does not
|
|
943
|
-
* keep: background-agent launches, which the subagent's own events already
|
|
1108
|
+
* keep: background-agent launches, which the subagent's own events already
|
|
1109
|
+
* show, and a model change, which is logged with what the plan made of it.
|
|
944
1110
|
*/
|
|
945
1111
|
declare function toTaskLogEvent(event: StructuredEvent): TaskLogEvent | null;
|
|
946
1112
|
/** A log as a surface is sent it, live or read back: everything but the skills' bodies. */
|
|
947
1113
|
declare function taskLogForSurface(events: readonly TaskLogEvent[]): TaskLogEvent[];
|
|
948
1114
|
/**
|
|
949
|
-
* Merge
|
|
950
|
-
*
|
|
951
|
-
*
|
|
1115
|
+
* Merge consecutive deltas of one stream into one event each: of one run of
|
|
1116
|
+
* text, or of one voice's thinking. Deltas arrive a few characters at a time;
|
|
1117
|
+
* merged, a batch is one message on the wire and one line on disk, and the
|
|
1118
|
+
* view folds to the same blocks either way.
|
|
952
1119
|
*/
|
|
953
1120
|
declare function coalesceTaskLog(events: readonly TaskLogEvent[]): TaskLogEvent[];
|
|
954
1121
|
/**
|
|
@@ -1078,6 +1245,8 @@ type SurfacePlanState = (Omit<Extract<PlanState, {
|
|
|
1078
1245
|
});
|
|
1079
1246
|
type SerializedPlan = {
|
|
1080
1247
|
runSettings?: LegacyPlanState['runSettings'];
|
|
1248
|
+
/** What waits on the planner's usage limit, and when it goes, so a surface can say so and offer to cancel it. */
|
|
1249
|
+
plannerResend?: LegacyPlanState['plannerResend'];
|
|
1081
1250
|
tasks: SerializedTask[];
|
|
1082
1251
|
runners: RunnerId[];
|
|
1083
1252
|
generatedAt: string;
|
|
@@ -1109,8 +1278,9 @@ type PlannerTurnOutcome = 'message' | 'plan' | 'task_ops' | 'stopped' | 'error';
|
|
|
1109
1278
|
* `planner_turn_ended` with the same `turnId`; every turn-scoped message in
|
|
1110
1279
|
* between carries it. The stream is provisional and the settled messages are
|
|
1111
1280
|
* authoritative:
|
|
1112
|
-
* - `planner_message`
|
|
1113
|
-
* a surface drops what it accumulated
|
|
1281
|
+
* - `planner_message` settles the streamed text of each run it carries in that
|
|
1282
|
+
* run's segment, where it streamed — a surface drops what it accumulated
|
|
1283
|
+
* there and shows the run's text instead.
|
|
1114
1284
|
* - A reply that is a JSON envelope (plan, taskOps, taskQuery) never arrives
|
|
1115
1285
|
* as `planner_text_delta`; it streams as `plan_token`, the "building plan"
|
|
1116
1286
|
* display.
|
|
@@ -1132,9 +1302,12 @@ type SessionMessage =
|
|
|
1132
1302
|
}
|
|
1133
1303
|
/**
|
|
1134
1304
|
* The settled reply of a planner turn, emitted by the session once the turn
|
|
1135
|
-
* is classified.
|
|
1136
|
-
* final segment. `turnId` is absent for replies sent outside a streamed turn.
|
|
1305
|
+
* is classified. `turnId` is absent for replies sent outside a streamed turn.
|
|
1137
1306
|
* `supervisor`: the supervisor said it in a wake-up turn (ADR-0029).
|
|
1307
|
+
* `runs`: what the reply shows, run by run, each authoritative over its
|
|
1308
|
+
* segment's `planner_text_delta`s; `content` stays the whole reply. Without
|
|
1309
|
+
* runs (a note the conversation wrote, an older daemon) the message is
|
|
1310
|
+
* authoritative over the turn's final segment.
|
|
1138
1311
|
*/
|
|
1139
1312
|
| {
|
|
1140
1313
|
type: 'planner_message';
|
|
@@ -1142,6 +1315,7 @@ type SessionMessage =
|
|
|
1142
1315
|
timestamp: string;
|
|
1143
1316
|
turnId?: string;
|
|
1144
1317
|
supervisor?: true;
|
|
1318
|
+
runs?: ReplyRun[];
|
|
1145
1319
|
}
|
|
1146
1320
|
/**
|
|
1147
1321
|
* A planner turn began. Emitted once per turn by whoever runs the turn,
|
|
@@ -1334,6 +1508,8 @@ type SessionMessage =
|
|
|
1334
1508
|
type: 'isolation_merge';
|
|
1335
1509
|
result: IsolationMergeResult;
|
|
1336
1510
|
trigger?: AutoMergeTrigger;
|
|
1511
|
+
} | {
|
|
1512
|
+
type: 'run_closed';
|
|
1337
1513
|
} | {
|
|
1338
1514
|
type: 'planner_liveness';
|
|
1339
1515
|
} | {
|
|
@@ -1511,6 +1687,22 @@ interface OrchestratorObserver {
|
|
|
1511
1687
|
resumeAt: string | null;
|
|
1512
1688
|
cancelled?: true;
|
|
1513
1689
|
}): void;
|
|
1690
|
+
/**
|
|
1691
|
+
* A task's runner now answers on another model (#106). `asked` when a model
|
|
1692
|
+
* command sent through its chat chose it: the user's own choice, which the
|
|
1693
|
+
* plan is to follow. `attemptEnded` when the attempt that runs `sessionId`
|
|
1694
|
+
* is no longer the task's, so the switch says nothing of what runs next.
|
|
1695
|
+
* `effort` only where the runner reported one.
|
|
1696
|
+
*/
|
|
1697
|
+
onModelSwitched?(data: {
|
|
1698
|
+
taskId: string;
|
|
1699
|
+
sessionId: string;
|
|
1700
|
+
from: RunnerModelRef;
|
|
1701
|
+
model: RunnerModelRef;
|
|
1702
|
+
effort?: string;
|
|
1703
|
+
asked: boolean;
|
|
1704
|
+
attemptEnded: boolean;
|
|
1705
|
+
}): void;
|
|
1514
1706
|
/** A live task's runner went silent (ADR-0018's idle timer); paused while it waits on input, a checkpoint or an approval. */
|
|
1515
1707
|
onTaskIdle?(data: {
|
|
1516
1708
|
taskId: string;
|
|
@@ -1541,6 +1733,8 @@ interface OrchestratorObserver {
|
|
|
1541
1733
|
}): void;
|
|
1542
1734
|
/** An isolated run settled; emitted before `onExecutionComplete`, which surfaces treat as terminal. */
|
|
1543
1735
|
onIsolationHandoff?(handoff: IsolationHandoff): void;
|
|
1736
|
+
/** The isolated run was cleared up and forgotten, by a Merge all that left nothing to hand over or by a discard. */
|
|
1737
|
+
onRunClosed?(): void;
|
|
1544
1738
|
/**
|
|
1545
1739
|
* What a run says about how it isolates — the fallback to the workspace root,
|
|
1546
1740
|
* shared paths, copies, a stash. Beside the notification channel, which a
|
|
@@ -1596,6 +1790,12 @@ interface TaskOrchestratorDeps {
|
|
|
1596
1790
|
skillsAt: (roots: readonly string[]) => SkillLookup;
|
|
1597
1791
|
/** What the task's last saved attempt did, or null when it left no log (ADR-0020). */
|
|
1598
1792
|
previousAttemptFromLog: (taskId: string) => string | null;
|
|
1793
|
+
/**
|
|
1794
|
+
* The newest attempt the task has a saved log for, 0 for none. An attempt's
|
|
1795
|
+
* log is saved under its number, so a new one is numbered past a log kept
|
|
1796
|
+
* from before the task had ledgers.
|
|
1797
|
+
*/
|
|
1798
|
+
lastLoggedAttempt: (taskId: string) => number;
|
|
1599
1799
|
/**
|
|
1600
1800
|
* The open run's settings, read as each automatic merge is decided rather
|
|
1601
1801
|
* than copied when the run opens, so a change to the run under way applies
|
|
@@ -1625,12 +1825,13 @@ interface TaskOrchestratorOptions {
|
|
|
1625
1825
|
workspaceEnv?: (cwd: string) => Promise<WorkspaceEnv>;
|
|
1626
1826
|
skillsAt?: (roots: readonly string[]) => SkillLookup;
|
|
1627
1827
|
previousAttemptFromLog?: (taskId: string) => string | null;
|
|
1828
|
+
lastLoggedAttempt?: (taskId: string) => number;
|
|
1628
1829
|
runSettings?: () => SchedulerRunSettings | undefined;
|
|
1629
1830
|
/** Every task is approved unless a Session supplies its record: an orchestrator on its own has no user to ask. */
|
|
1630
1831
|
isApproved?: (taskId: string) => boolean;
|
|
1631
1832
|
}
|
|
1632
1833
|
/**
|
|
1633
|
-
* The pure scheduler. Owns execution state (
|
|
1834
|
+
* The pure scheduler. Owns execution state (the {@link RunState},
|
|
1634
1835
|
* `reviewApproved`, the live task attempts, the start hold) and the verifier.
|
|
1635
1836
|
* All task-shaped state — the plan tree, the flat index, the completed set
|
|
1636
1837
|
* — lives in {@link PlanStore}, injected at construction. The orchestrator
|
|
@@ -1647,8 +1848,8 @@ declare class TaskOrchestrator {
|
|
|
1647
1848
|
private output;
|
|
1648
1849
|
private attempts;
|
|
1649
1850
|
private verifier;
|
|
1650
|
-
|
|
1651
|
-
private
|
|
1851
|
+
/** Changed only by start, stop, a plan load, a usage-limit pause, a move off the limit, and {@link settleRun}. */
|
|
1852
|
+
private runState;
|
|
1652
1853
|
/** Planner turns in flight that new starts wait for (ADR-0029); running attempts carry on. */
|
|
1653
1854
|
private startHolds;
|
|
1654
1855
|
/** Bumped by the user's Stop: a start admitted under an older generation never takes its claim. */
|
|
@@ -1660,14 +1861,6 @@ declare class TaskOrchestrator {
|
|
|
1660
1861
|
private mergesInFlight;
|
|
1661
1862
|
private retryCounts;
|
|
1662
1863
|
private spawnCounts;
|
|
1663
|
-
/** A full-plan run a usage-limit pause holds: no new start until the reset resumes it. */
|
|
1664
|
-
private heldOnLimit;
|
|
1665
|
-
/**
|
|
1666
|
-
* A full-plan run that ended with failed tasks and the tasks they block
|
|
1667
|
-
* (ADR-0034). A retry of a failed task continues it, so what it blocked
|
|
1668
|
-
* starts once it passes — without this the retried task ran alone.
|
|
1669
|
-
*/
|
|
1670
|
-
private endedOnFailure;
|
|
1671
1864
|
/** A Merge all is under way: no ops task starts until it ends (ADR-0020). */
|
|
1672
1865
|
private merging;
|
|
1673
1866
|
/** The last "waiting for Merge all" notice, so a run paused at a gate says it once, not on every tick. */
|
|
@@ -1683,6 +1876,12 @@ declare class TaskOrchestrator {
|
|
|
1683
1876
|
* task whose spawn always throws.
|
|
1684
1877
|
*/
|
|
1685
1878
|
private onHold;
|
|
1879
|
+
/**
|
|
1880
|
+
* Force starts queued behind dependencies the scheduler will not start them
|
|
1881
|
+
* after: outside a run it drives, or past a failed dependency, which it
|
|
1882
|
+
* never forces. Each starts once those still under way have finished.
|
|
1883
|
+
*/
|
|
1884
|
+
private queuedStarts;
|
|
1686
1885
|
/**
|
|
1687
1886
|
* Tasks Mark complete is landing after it ended their attempt: in progress
|
|
1688
1887
|
* with no attempt for as long as that takes, and not orphans for it.
|
|
@@ -1704,6 +1903,7 @@ declare class TaskOrchestrator {
|
|
|
1704
1903
|
private observers;
|
|
1705
1904
|
private skillsAt;
|
|
1706
1905
|
private previousAttemptFromLog;
|
|
1906
|
+
private lastLoggedAttempt;
|
|
1707
1907
|
private runSettings;
|
|
1708
1908
|
private isApproved;
|
|
1709
1909
|
constructor(deps: TaskOrchestratorDeps);
|
|
@@ -1820,11 +2020,14 @@ declare class TaskOrchestrator {
|
|
|
1820
2020
|
admitStart(): StartAdmission;
|
|
1821
2021
|
/**
|
|
1822
2022
|
* Something can still start that nobody has to ask for again: a start
|
|
1823
|
-
* admitted and not yet claimed,
|
|
1824
|
-
*
|
|
2023
|
+
* admitted and not yet claimed, a force start queued behind its
|
|
2024
|
+
* dependencies, or a task that resumes on its own at its usage limit's
|
|
2025
|
+
* reset. A Stop is owed to it, whether or not anything runs.
|
|
1825
2026
|
*/
|
|
1826
2027
|
get startsPending(): boolean;
|
|
1827
|
-
/**
|
|
2028
|
+
/** A start admitted now that nothing has to count as live work: one that claims before it awaits, or follows on from one that did. */
|
|
2029
|
+
private admittedNow;
|
|
2030
|
+
/** Run `work`, which awaits before it claims, under an admission it holds throughout and claims under. */
|
|
1828
2031
|
private whileAdmitted;
|
|
1829
2032
|
/**
|
|
1830
2033
|
* "Merge all"; a settled run that merged everything is cleared up and
|
|
@@ -1849,10 +2052,20 @@ declare class TaskOrchestrator {
|
|
|
1849
2052
|
runMergeView(): RunMergeView | null;
|
|
1850
2053
|
/**
|
|
1851
2054
|
* The ancestors an ops or user task waits on at its merge gate: those whose
|
|
1852
|
-
* landed work is not in the user's branch yet (ADR-0020). Empty
|
|
1853
|
-
* task
|
|
2055
|
+
* landed work is not in the user's branch yet (ADR-0020). Empty for every
|
|
2056
|
+
* other task. Judged whatever the task's status but `completed` — a retry of
|
|
2057
|
+
* a failed ops task acts on the checkout as a first start does — so a start
|
|
2058
|
+
* the user is about to confirm is told what it would act without.
|
|
1854
2059
|
*/
|
|
1855
2060
|
getMergeGate(taskId: string): string[];
|
|
2061
|
+
/**
|
|
2062
|
+
* Every reason a task cannot start now, from the one start rule
|
|
2063
|
+
* (ADR-0034); none when it can. `forced` judges a start the user forced.
|
|
2064
|
+
* Takes the task, not its id: the command seam judges one as a held edit
|
|
2065
|
+
* leaves it.
|
|
2066
|
+
*/
|
|
2067
|
+
startWaits(task: Readonly<Task>, forced?: ForcedStart): StartWait[];
|
|
2068
|
+
private startRule;
|
|
1856
2069
|
/** The failed tasks a task not started yet waits on, through its dependencies (ADR-0034); empty when it is not blocked. */
|
|
1857
2070
|
getBlockedBy(taskId: string): string[];
|
|
1858
2071
|
loadPlan(tasks: readonly Task[], planRunners?: RunnerId[]): void;
|
|
@@ -1871,8 +2084,16 @@ declare class TaskOrchestrator {
|
|
|
1871
2084
|
* not a retry, and the tasks it blocks wait for that retry (ADR-0034).
|
|
1872
2085
|
*/
|
|
1873
2086
|
reconcilePlan(newTasks: Task[], planRunners?: RunnerId[]): void;
|
|
2087
|
+
/** Start the full-plan run, or continue one that is held or ended on a failure. */
|
|
1874
2088
|
start(origin?: RunOrigin): Promise<void>;
|
|
1875
|
-
/**
|
|
2089
|
+
/**
|
|
2090
|
+
* A usage-limit pause with a reset to wait for holds the run (ADR-0018,
|
|
2091
|
+
* U1): spawning more would spend the same exhausted limit, and the run
|
|
2092
|
+
* stays open — neither handed over nor merged at its end — however it was
|
|
2093
|
+
* started, so a task started alone keeps its run for the reset too. A pause
|
|
2094
|
+
* no reset will resume holds nothing: it keeps only its own runner's tasks
|
|
2095
|
+
* from starting (`readiness.usage-limit`).
|
|
2096
|
+
*/
|
|
1876
2097
|
private holdOnLimit;
|
|
1877
2098
|
/**
|
|
1878
2099
|
* Stop the run. The user's Stop also withdraws every start admitted before
|
|
@@ -1932,12 +2153,18 @@ declare class TaskOrchestrator {
|
|
|
1932
2153
|
* integration branch.
|
|
1933
2154
|
*/
|
|
1934
2155
|
private land;
|
|
1935
|
-
/**
|
|
2156
|
+
/**
|
|
2157
|
+
* Settle a task by how its landing went; `onLanded` is what the path that
|
|
2158
|
+
* landed it adds to completing it. `admission` is the start the landed work
|
|
2159
|
+
* came from, which a repair follows on from.
|
|
2160
|
+
*/
|
|
1936
2161
|
private applyLanding;
|
|
1937
2162
|
/**
|
|
1938
2163
|
* Work that did not land waits on the user — it never fails the task or
|
|
1939
2164
|
* blocks its dependents — unless its conflict is owed a repair, which takes
|
|
1940
2165
|
* the slot the ending attempt freed and never one more than the run allows.
|
|
2166
|
+
* The repair follows on from the start whose work it repairs: a Stop since
|
|
2167
|
+
* that was admitted withdraws it.
|
|
1941
2168
|
*/
|
|
1942
2169
|
private settleUnlanded;
|
|
1943
2170
|
private landResolved;
|
|
@@ -1960,10 +2187,10 @@ declare class TaskOrchestrator {
|
|
|
1960
2187
|
*/
|
|
1961
2188
|
private openFor;
|
|
1962
2189
|
/**
|
|
1963
|
-
* {@link openFor} for an explicit start
|
|
1964
|
-
*
|
|
1965
|
-
*
|
|
1966
|
-
*
|
|
2190
|
+
* {@link openFor} for an explicit start under `admission`. A Stop while the
|
|
2191
|
+
* run opened interrupts that opening too, which then opens nothing; the
|
|
2192
|
+
* start says it was stopped rather than going quiet as if a dirty tree were
|
|
2193
|
+
* waiting on the user.
|
|
1967
2194
|
*/
|
|
1968
2195
|
private openForAdmitted;
|
|
1969
2196
|
/** The kind a task's next attempt starts as, short of a repair or a continue: what is weighed before one exists. */
|
|
@@ -2018,14 +2245,35 @@ declare class TaskOrchestrator {
|
|
|
2018
2245
|
/**
|
|
2019
2246
|
* Keep a task paused on its runner's usage limit from resuming on its own:
|
|
2020
2247
|
* it waits for a manual retry, as one whose reset time could not be read
|
|
2021
|
-
* does. What a surface's cancel calls
|
|
2022
|
-
*
|
|
2248
|
+
* does. What a surface's cancel calls. A run the pause held goes on once
|
|
2249
|
+
* nothing else waits on a reset. False when no resume was scheduled for it.
|
|
2250
|
+
*/
|
|
2251
|
+
cancelScheduledResume(taskId: string): Promise<boolean>;
|
|
2252
|
+
/** {@link cancelScheduledResume} without its tick, for a caller that ticks or starts once it is done. */
|
|
2253
|
+
private dropResume;
|
|
2254
|
+
/**
|
|
2255
|
+
* The hold a usage-limit pause put on the run is released once nothing
|
|
2256
|
+
* waits on a reset (ADR-0018, U1) — however the last wait went: the reset,
|
|
2257
|
+
* a cancel, Mark complete or the task leaving the plan. The run is still
|
|
2258
|
+
* open, so the next tick schedules it.
|
|
2259
|
+
*/
|
|
2260
|
+
private releaseHold;
|
|
2261
|
+
/**
|
|
2262
|
+
* The runner whose attempt left the task's work, when the task has been
|
|
2263
|
+
* moved off it since: a session cannot cross runners, so whatever starts
|
|
2264
|
+
* the task next is a carry-over of that work, the rule a paused task's move
|
|
2265
|
+
* follows (ADR-0018, U1).
|
|
2023
2266
|
*/
|
|
2024
|
-
|
|
2267
|
+
private movedFrom;
|
|
2025
2268
|
/**
|
|
2026
2269
|
* Run before writing assignments so cancellation sees the old runner's
|
|
2027
2270
|
* shared wait. A session cannot cross runners, so the next attempt is a
|
|
2028
2271
|
* carry-over of the paused attempt's files (ADR-0018, U1).
|
|
2272
|
+
*
|
|
2273
|
+
* The move routes the task around the limit, so a run the pause held goes
|
|
2274
|
+
* on, as a retry would continue it. That run is still open — a hold never
|
|
2275
|
+
* closes one — and every edit that calls this ticks once it is written,
|
|
2276
|
+
* which starts the task.
|
|
2029
2277
|
*/
|
|
2030
2278
|
releaseMovedResume(taskId: string, changes: Partial<Task>): Partial<Task>;
|
|
2031
2279
|
/**
|
|
@@ -2070,26 +2318,65 @@ declare class TaskOrchestrator {
|
|
|
2070
2318
|
/**
|
|
2071
2319
|
* Retry a task paused on a usage limit, continuing its saved session where
|
|
2072
2320
|
* it left one (ADR-0018, K1), then release the hold the pause put on the
|
|
2073
|
-
* run once nothing else waits on a reset.
|
|
2321
|
+
* run once nothing else waits on a reset. `now` is the Resume now that
|
|
2322
|
+
* asked for it, or for another task on its runner, which sweeps it along.
|
|
2074
2323
|
*/
|
|
2075
2324
|
private resumeTask;
|
|
2076
2325
|
/**
|
|
2077
|
-
* A resume
|
|
2078
|
-
*
|
|
2079
|
-
* time dropped, and said
|
|
2326
|
+
* A resume nobody asked for cannot be refused to anyone. At the limit's
|
|
2327
|
+
* reset, one that may not claim waits for the user instead — its resume
|
|
2328
|
+
* time dropped, and said. One swept along by a Resume now keeps its resume
|
|
2329
|
+
* time, which its caller arms again: the reset has not come. A Stop says
|
|
2330
|
+
* so itself.
|
|
2080
2331
|
*/
|
|
2081
|
-
private
|
|
2332
|
+
private mayResume;
|
|
2082
2333
|
/**
|
|
2083
|
-
* Manually start a single AI task right now
|
|
2084
|
-
*
|
|
2085
|
-
* it still needs the user's approval,
|
|
2086
|
-
*
|
|
2334
|
+
* Manually start a single AI task right now (the "force start" affordance on
|
|
2335
|
+
* a task card), past a failed dependency (ADR-0034) or a merge gate
|
|
2336
|
+
* (ADR-0020), but never past the claim: it still needs the user's approval,
|
|
2337
|
+
* a free slot, and no Stop since it was asked for, and throws
|
|
2338
|
+
* {@link TaskControlError} saying which it lacks. A dependency still under
|
|
2339
|
+
* way or yet to run is not forced past: the task is released to the
|
|
2340
|
+
* scheduler instead, which starts it once they finish, and the line saying
|
|
2341
|
+
* so is the answer.
|
|
2087
2342
|
* Reuses the scheduler's own claim so a force-started task gets the same
|
|
2088
2343
|
* augmented prompt, session tracking, and exit handling — callers must not
|
|
2089
2344
|
* re-spawn the runner themselves. No-op if the task is unknown, not an AI
|
|
2090
2345
|
* task, or already running.
|
|
2091
2346
|
*/
|
|
2092
|
-
forceStartTask(taskId: string, origin?: RunOrigin): Promise<
|
|
2347
|
+
forceStartTask(taskId: string, origin?: RunOrigin): Promise<string | undefined>;
|
|
2348
|
+
/**
|
|
2349
|
+
* A forced start, from its run opening to its claim. It is judged again once
|
|
2350
|
+
* the run has opened, against the task as it stands then, which is what
|
|
2351
|
+
* the claim reads and runs. A queued one that something holds back again
|
|
2352
|
+
* meanwhile goes back in the queue; one made now that an edit gave
|
|
2353
|
+
* dependencies still under way waits for them as if forced after the edit,
|
|
2354
|
+
* and says so.
|
|
2355
|
+
*/
|
|
2356
|
+
private startForced;
|
|
2357
|
+
/**
|
|
2358
|
+
* A force start of a task whose dependencies are still under way or yet to
|
|
2359
|
+
* run: started now, it would build on work that is not there, so it waits
|
|
2360
|
+
* for them in the run, off any hold, and says so. Dependencies are never
|
|
2361
|
+
* bypassed silently. A blocked task is forced past its failure as asked.
|
|
2362
|
+
* What it goes past at the merge gate is the gate the user confirmed now.
|
|
2363
|
+
*/
|
|
2364
|
+
private queueBehindDependencies;
|
|
2365
|
+
/**
|
|
2366
|
+
* Starts the queued force starts nothing holds back any more. One the
|
|
2367
|
+
* scheduler now drives the run for, that nothing blocks and that was not
|
|
2368
|
+
* confirmed past a gate, is left to it. One at a gate the user did not
|
|
2369
|
+
* confirm — work that landed after they forced the start — waits for Merge
|
|
2370
|
+
* all, or for another force start, and says so once.
|
|
2371
|
+
*/
|
|
2372
|
+
private startQueued;
|
|
2373
|
+
/**
|
|
2374
|
+
* A queued start the start rule holds back, at a gate it was not confirmed
|
|
2375
|
+
* past or otherwise. Taken out of the queue by now, so its dependencies
|
|
2376
|
+
* still under way read as `dependencies` rather than `queued`.
|
|
2377
|
+
*/
|
|
2378
|
+
private holdsQueued;
|
|
2379
|
+
private sayQueuedAtGate;
|
|
2093
2380
|
private refuseOpsDuringMerge;
|
|
2094
2381
|
/**
|
|
2095
2382
|
* A force start passes a merge gate (ADR-0020), and the surface confirmed it
|
|
@@ -2097,24 +2384,27 @@ declare class TaskOrchestrator {
|
|
|
2097
2384
|
* on is not in the user's branch.
|
|
2098
2385
|
*/
|
|
2099
2386
|
private notePastGate;
|
|
2100
|
-
/**
|
|
2101
|
-
* Run exactly one task outside full-plan scheduling. The active/starting
|
|
2102
|
-
* session still contributes to {@link hasLiveWork} so every surface exposes
|
|
2103
|
-
* Stop and disables Execute Plan, but onVerdict cannot auto-schedule other
|
|
2104
|
-
* tasks because the plan scheduler's `running` flag remains false.
|
|
2105
|
-
*/
|
|
2106
|
-
runTask(taskId: string): Promise<void>;
|
|
2107
2387
|
getCompletedCount(): number;
|
|
2108
2388
|
getTotalCount(): number;
|
|
2109
2389
|
isAllComplete(): boolean;
|
|
2110
2390
|
isAnyFailed(): boolean;
|
|
2111
2391
|
approveReview(origin?: RunOrigin): Promise<void>;
|
|
2112
2392
|
tick(): Promise<void>;
|
|
2113
|
-
/**
|
|
2393
|
+
/**
|
|
2394
|
+
* The one step that decides, with nothing at work and nothing the scheduler
|
|
2395
|
+
* can start, whether the run goes on, is held, waits on the user or is over
|
|
2396
|
+
* (ADR-0034). Every surface takes `onExecutionComplete` as the run's end, so
|
|
2397
|
+
* only an end emits it. A run the scheduler is not driving — tasks started
|
|
2398
|
+
* on their own — ends on a verdict; a cancel, Mark complete or failed spawn
|
|
2399
|
+
* just closes it, so the next run decides its own mode rather than inherit
|
|
2400
|
+
* this one's.
|
|
2401
|
+
*/
|
|
2402
|
+
private settleRun;
|
|
2114
2403
|
/** Skipped work is not completed work, so a run with any says so rather than that everything completed. */
|
|
2115
2404
|
private sayFinished;
|
|
2405
|
+
/** How a run that ended on failures ends: what it finished, what failed, and what that blocks. */
|
|
2116
2406
|
private sayEndedOnFailure;
|
|
2117
|
-
/** Whether the scheduler may start `id` now:
|
|
2407
|
+
/** Whether the scheduler may start `id` now: nothing in the start rule holds it back, and a slot is free. */
|
|
2118
2408
|
private stillReady;
|
|
2119
2409
|
/**
|
|
2120
2410
|
* A run paused at merge gates says so, as plainly as a task that waits on
|
|
@@ -2123,9 +2413,10 @@ declare class TaskOrchestrator {
|
|
|
2123
2413
|
private sayGate;
|
|
2124
2414
|
/**
|
|
2125
2415
|
* Why a start may not take its claim now, or null when it may: a Stop came
|
|
2126
|
-
* since it was admitted (ADR-0029), the task
|
|
2127
|
-
*
|
|
2128
|
-
*
|
|
2416
|
+
* since it was admitted (ADR-0029), the task left the plan or is being
|
|
2417
|
+
* marked complete, it is outside the user's approval record (ADR-0035), or
|
|
2418
|
+
* every slot is taken. Read in the same synchronous step as the claim it
|
|
2419
|
+
* guards, so nothing can change in between.
|
|
2129
2420
|
*/
|
|
2130
2421
|
private claimRefusal;
|
|
2131
2422
|
/** An explicit start's refusal, before it changes anything: its caller's answer, as a {@link TaskControlError}. */
|
|
@@ -2137,7 +2428,10 @@ declare class TaskOrchestrator {
|
|
|
2137
2428
|
* start, a retry's, a continue, a resume, a repair. Checked and taken in one
|
|
2138
2429
|
* synchronous step ({@link claimRefusal}), then marked claimed before
|
|
2139
2430
|
* anything is awaited, so no other start can take the same task or slot.
|
|
2140
|
-
*
|
|
2431
|
+
* The task is read here, as it stands: a start that awaited first — its run
|
|
2432
|
+
* opening, the user's answer — runs what the plan holds now, not what it
|
|
2433
|
+
* held when it was asked for. A task already claimed is not claimed again:
|
|
2434
|
+
* there is nothing to start.
|
|
2141
2435
|
*/
|
|
2142
2436
|
private claim;
|
|
2143
2437
|
private runClaimed;
|
|
@@ -2165,6 +2459,8 @@ declare class TaskOrchestrator {
|
|
|
2165
2459
|
* A spawn whose attempt ended while it was in flight: kill what it produced
|
|
2166
2460
|
* and take back the claim it made. Only the claim — whatever ended the
|
|
2167
2461
|
* attempt may have decided the task since (mark complete, cancel, retry).
|
|
2462
|
+
* A task Mark complete is landing stays in progress until that lands:
|
|
2463
|
+
* back to not started, the scheduler would start it again under the landing.
|
|
2168
2464
|
*/
|
|
2169
2465
|
private abandonSpawn;
|
|
2170
2466
|
private tell;
|
|
@@ -2194,153 +2490,1828 @@ declare class TaskOrchestrator {
|
|
|
2194
2490
|
}
|
|
2195
2491
|
|
|
2196
2492
|
/**
|
|
2197
|
-
*
|
|
2198
|
-
*
|
|
2199
|
-
*
|
|
2200
|
-
*
|
|
2201
|
-
*
|
|
2493
|
+
* The planner's read channel: a `{"taskQuery":{...}}` reply asking Ordewell to
|
|
2494
|
+
* show it what the per-turn plan block deliberately leaves out.
|
|
2495
|
+
*
|
|
2496
|
+
* The plan block carries only short fields, so a planner asked to "add a line
|
|
2497
|
+
* to task 3's prompt" would be rewriting text it has never read. Inlining every
|
|
2498
|
+
* body on every turn was rejected — a twenty-task plan re-sends thousands of
|
|
2499
|
+
* tokens per message — so the planner asks instead, and pays for the detail
|
|
2500
|
+
* only on the turns it uses it.
|
|
2501
|
+
*
|
|
2502
|
+
* This module owns the wire shape and the rendering of the answer; the loop,
|
|
2503
|
+
* the budgets and the injection live in the Session, and the classification
|
|
2504
|
+
* lives in PlanRepair alongside the plan and task-ops envelopes.
|
|
2202
2505
|
*/
|
|
2203
|
-
/** The
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2506
|
+
/** The long fields a query may ask for. Everything else is already in the plan block. */
|
|
2507
|
+
declare const TASK_QUERY_FIELDS: readonly ["description", "prompt", "userSteps", "verdict", "outputSummary", "userStoriesCovered", "skills", "output"];
|
|
2508
|
+
type TaskQueryField = typeof TASK_QUERY_FIELDS[number];
|
|
2509
|
+
/** Lines a `output` read returns when the query sets no count. */
|
|
2510
|
+
declare const OUTPUT_LINES_DEFAULT = 80;
|
|
2511
|
+
/** Hard cap on `outputLines` — the tail read is bounded however the planner asks. */
|
|
2512
|
+
declare const OUTPUT_LINES_MAX = 400;
|
|
2210
2513
|
/**
|
|
2211
|
-
*
|
|
2212
|
-
*
|
|
2213
|
-
* the
|
|
2514
|
+
* Character budget the rendered answer converges to. A running output tail is
|
|
2515
|
+
* the only part with no natural bound — a runner may print for minutes — so
|
|
2516
|
+
* the tail absorbs the clamp; everything else (task bodies, the catalog cap)
|
|
2517
|
+
* is bounded upstream.
|
|
2214
2518
|
*/
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2519
|
+
declare const TASK_QUERY_ANSWER_MAX_CHARS = 20000;
|
|
2520
|
+
interface TaskQuery {
|
|
2521
|
+
/** Task references to read in full, as {@link resolveTaskRef} reads them. */
|
|
2522
|
+
tasks: string[];
|
|
2523
|
+
/** Narrows what comes back. Omitted means every field in {@link TASK_QUERY_FIELDS}. */
|
|
2524
|
+
fields?: TaskQueryField[];
|
|
2525
|
+
/** Also return the full runner/model catalog with labels, variants and mode descriptions. */
|
|
2526
|
+
catalog: boolean;
|
|
2527
|
+
/** Line count for `output` reads. Clamped at render to [1, {@link OUTPUT_LINES_MAX}]. */
|
|
2528
|
+
outputLines?: number;
|
|
2529
|
+
/** A previous answer's next offset: the output tail then covers only what came after it. */
|
|
2530
|
+
outputSince?: number;
|
|
2531
|
+
}
|
|
2532
|
+
declare function textHasTaskQuery(text: string): boolean;
|
|
2533
|
+
/** Parse a `{"taskQuery":...}` reply. Throws PlanParseError when the JSON is unusable. */
|
|
2534
|
+
declare function parseTaskQueryJson(text: string): TaskQuery;
|
|
2535
|
+
/** Stable identity of a query, so a turn can recognise the planner asking the same thing twice. */
|
|
2536
|
+
declare function taskQuerySignature(query: TaskQuery): string;
|
|
2537
|
+
/** What a full-catalog read is answered from. Models arrive already allowlist-filtered. */
|
|
2538
|
+
interface TaskQueryCatalog {
|
|
2539
|
+
runners: RunnerId[];
|
|
2540
|
+
models: Partial<Record<RunnerId, DiscoveredModel[]>>;
|
|
2541
|
+
modes: Record<RunnerId, RunnerModeInfo[]>;
|
|
2542
|
+
autonomousDefault: boolean;
|
|
2218
2543
|
}
|
|
2219
|
-
/** Dotted path label for a task, e.g. "2.1" for the first subtask of task 2, "2.1.3" one level down. */
|
|
2220
|
-
declare function taskOrderLabel(task: {
|
|
2221
|
-
order: number;
|
|
2222
|
-
}, parent?: RefParent): string;
|
|
2223
|
-
/** How a surface refers to a task in prose: "#2", or "#2.1" for a subtask. */
|
|
2224
|
-
declare function taskRef(task: {
|
|
2225
|
-
order: number;
|
|
2226
|
-
}, parent?: RefParent): string;
|
|
2227
|
-
/** A task named in full, as a confirmation or a picker title does: "#2 Deploy". */
|
|
2228
|
-
declare function titledTaskRef(task: {
|
|
2229
|
-
order: number;
|
|
2230
|
-
title: string;
|
|
2231
|
-
}, parent?: RefParent): string;
|
|
2232
2544
|
/**
|
|
2233
|
-
*
|
|
2234
|
-
*
|
|
2545
|
+
* Appended when the planner has spent its read budget, or asked the identical
|
|
2546
|
+
* question twice. The detail still comes back — refusing it would leave the
|
|
2547
|
+
* planner editing text it has not read, which is the whole failure this
|
|
2548
|
+
* channel exists to prevent — but the turn is told to land.
|
|
2235
2549
|
*/
|
|
2236
|
-
declare
|
|
2550
|
+
declare const TASK_QUERY_ANSWER_OR_OPS: string;
|
|
2237
2551
|
/**
|
|
2238
|
-
* The
|
|
2239
|
-
*
|
|
2240
|
-
*
|
|
2552
|
+
* The protocol as the planner is taught it, owned here beside the parser so the
|
|
2553
|
+
* two cannot drift. Both planner backends get these lines verbatim (ADR-0009):
|
|
2554
|
+
* a harness planner has no tool loop to reach, so the envelope is the channel.
|
|
2241
2555
|
*/
|
|
2242
|
-
declare
|
|
2556
|
+
declare const TASK_QUERY_PROTOCOL: string[];
|
|
2557
|
+
/** The one-line reminder the per-turn plan block carries, so the protocol has a single owner. */
|
|
2558
|
+
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.";
|
|
2243
2559
|
/**
|
|
2244
|
-
*
|
|
2245
|
-
*
|
|
2246
|
-
*
|
|
2247
|
-
* An order label is read against the tree, never a flattened list: subtask
|
|
2248
|
-
* orders restart at 1 under each parent, so "#2" always means top-level task 2
|
|
2249
|
-
* and a subtask is reachable only by its dotted path ("#2.1", "#2.1.3").
|
|
2560
|
+
* Where an `output` read is answered from: the orchestrator's live capture of
|
|
2561
|
+
* the task's latest attempt. Injected so this module stays planner-side and
|
|
2562
|
+
* never imports the orchestrator.
|
|
2250
2563
|
*/
|
|
2251
|
-
|
|
2564
|
+
type LiveOutputLookup = (taskId: string, opts: LiveTailOptions) => LiveTail | null;
|
|
2565
|
+
/**
|
|
2566
|
+
* Render the injected answer to one read. Never persisted — it is context for
|
|
2567
|
+
* the planner's next reply and nothing else — so it is built fresh from live
|
|
2568
|
+
* state on every query rather than cached. `liveOutput` backs the `output`
|
|
2569
|
+
* field; omitting it reads as "nothing captured".
|
|
2570
|
+
*/
|
|
2571
|
+
declare function renderTaskQueryAnswer(query: TaskQuery, tasks: readonly Task[], catalog: TaskQueryCatalog, liveOutput?: LiveOutputLookup): string;
|
|
2252
2572
|
|
|
2253
2573
|
/**
|
|
2254
|
-
*
|
|
2255
|
-
*
|
|
2256
|
-
*
|
|
2574
|
+
* The SSRF guard for anything that leaves the machine on the planner's behalf.
|
|
2575
|
+
* An unguarded `http://169.254.169.254/` would hand a prompt-injected planner
|
|
2576
|
+
* the cloud metadata credentials, so every outbound host is vetted here: IP
|
|
2577
|
+
* literals by range, names by every address DNS answers with.
|
|
2257
2578
|
*/
|
|
2258
|
-
|
|
2579
|
+
type ResolveAll = (host: string) => Promise<string[]>;
|
|
2259
2580
|
|
|
2260
|
-
/**
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
/** Reads a wake-up turn may make, in place of a user message's caps. */
|
|
2269
|
-
wakeUpReadBudget: number;
|
|
2581
|
+
/**
|
|
2582
|
+
* The one decision every planner web read goes through, whichever runner
|
|
2583
|
+
* plans and whichever path carries the bytes (ADR-0027). A fetch names the
|
|
2584
|
+
* URL it reads; a search names its query and the provider that receives it.
|
|
2585
|
+
*/
|
|
2586
|
+
type WebRead = {
|
|
2587
|
+
kind: 'fetch';
|
|
2588
|
+
url: string;
|
|
2270
2589
|
}
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2590
|
+
/** `provider` keys the grant; `providerLabel`, when given, names it to the person asked. */
|
|
2591
|
+
| {
|
|
2592
|
+
kind: 'search';
|
|
2593
|
+
query: string;
|
|
2594
|
+
provider: string;
|
|
2595
|
+
providerLabel?: string;
|
|
2596
|
+
note?: string;
|
|
2597
|
+
};
|
|
2598
|
+
/** A refusal's `reason` is written for the planner: it says what to do next. */
|
|
2599
|
+
type WebReadDecision = {
|
|
2600
|
+
allowed: true;
|
|
2601
|
+
} | {
|
|
2602
|
+
allowed: false;
|
|
2603
|
+
reason: string;
|
|
2604
|
+
};
|
|
2605
|
+
interface DecideOptions {
|
|
2606
|
+
/**
|
|
2607
|
+
* Research subagents run in the background where nothing can prompt
|
|
2608
|
+
* (ADR-0008, T6). When false, a read that would ask is refused with the gap
|
|
2609
|
+
* to report, rather than raising a prompt nobody can answer.
|
|
2610
|
+
*/
|
|
2611
|
+
prompting?: boolean;
|
|
2274
2612
|
}
|
|
2275
|
-
interface
|
|
2276
|
-
|
|
2277
|
-
/**
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2613
|
+
interface WebReadPolicyOptions {
|
|
2614
|
+
approval: IApproval;
|
|
2615
|
+
/** The secrets Ordewell holds — provider keys, MCP tokens — read per request. */
|
|
2616
|
+
secrets?: () => Iterable<string>;
|
|
2617
|
+
/** Defaults to `process.env`; its credential-named values are secrets too. */
|
|
2618
|
+
env?: () => Record<string, string | undefined>;
|
|
2619
|
+
/** How many web reads one planner turn may make; read per request, so a settings change applies at once. */
|
|
2620
|
+
budget?: () => number;
|
|
2621
|
+
/** The planner turn in progress; a new one starts a new budget. */
|
|
2622
|
+
turn?: () => string | undefined;
|
|
2623
|
+
/** Override DNS resolution (every answer for a name) in tests. */
|
|
2624
|
+
resolveHost?: ResolveAll;
|
|
2625
|
+
}
|
|
2626
|
+
declare const DEFAULT_WEB_READ_BUDGET = 40;
|
|
2627
|
+
declare class WebReadPolicy {
|
|
2628
|
+
private readonly approval;
|
|
2629
|
+
private readonly resolveHost;
|
|
2630
|
+
private readonly secrets;
|
|
2631
|
+
private readonly env;
|
|
2632
|
+
private readonly budget;
|
|
2633
|
+
private readonly turn;
|
|
2634
|
+
private spent;
|
|
2635
|
+
constructor(opts: WebReadPolicyOptions);
|
|
2282
2636
|
/**
|
|
2283
|
-
*
|
|
2284
|
-
*
|
|
2637
|
+
* Every check that needs no network runs first, then the budget and the
|
|
2638
|
+
* approval, and only then is a fetch's host resolved: a lookup is itself a
|
|
2639
|
+
* request, so `<data>.attacker.example` would leave through DNS even for a
|
|
2640
|
+
* read that is then excluded, over budget or declined.
|
|
2285
2641
|
*/
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2642
|
+
decide(read: WebRead, opts?: DecideOptions): Promise<WebReadDecision>;
|
|
2643
|
+
private subagentRefusal;
|
|
2644
|
+
/**
|
|
2645
|
+
* Whether a search through `provider` would run without asking anyone, for
|
|
2646
|
+
* a runner whose own search raises no request and is set on or off when
|
|
2647
|
+
* its session starts (ADR-0027). A zero budget disables it; no query is
|
|
2648
|
+
* known yet, so secrets and individual reads cannot be checked or counted.
|
|
2649
|
+
*/
|
|
2650
|
+
searchesUnasked(provider: string): boolean;
|
|
2651
|
+
/** The last check, the one that leaves the machine: every DNS answer for the host must be public. */
|
|
2652
|
+
private resolved;
|
|
2653
|
+
private refuse;
|
|
2654
|
+
private vetFetch;
|
|
2655
|
+
private vetSearch;
|
|
2656
|
+
private spend;
|
|
2657
|
+
private refund;
|
|
2658
|
+
private carriesSecret;
|
|
2294
2659
|
}
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
* across processes by default. `ORDEWELL_SETTINGS_PATH` lets independent runs
|
|
2298
|
-
* use separate files: otherwise a write in one run would change another's
|
|
2299
|
-
* settings when `getAll()` detects the new mtime.
|
|
2300
|
-
*/
|
|
2301
|
-
declare function getSettingsPath(): string;
|
|
2302
|
-
/** The run settings `settings` holds, each one it leaves out at its default. */
|
|
2303
|
-
declare function runSettingsOf(settings: UserSettings): GlobalRunSettings;
|
|
2304
|
-
declare class SettingsService {
|
|
2305
|
-
private filePath;
|
|
2306
|
-
private cache;
|
|
2307
|
-
private cachedMtimeMs;
|
|
2308
|
-
constructor(filePath?: string);
|
|
2309
|
-
getAll(): UserSettings;
|
|
2310
|
-
private fileMtimeMs;
|
|
2311
|
-
getModelAllowlist(runner: string): string[] | undefined;
|
|
2312
|
-
setModelAllowlist(runner: string, ids: string[] | undefined): void;
|
|
2313
|
-
getPlannerModel(provider: string): {
|
|
2314
|
-
model: string;
|
|
2315
|
-
effort?: string;
|
|
2316
|
-
} | undefined;
|
|
2317
|
-
setPlannerModel(provider: string, entry: {
|
|
2318
|
-
model: string;
|
|
2319
|
-
effort?: string;
|
|
2320
|
-
} | undefined): void;
|
|
2321
|
-
getEnabledRunners(): string[] | undefined;
|
|
2322
|
-
setEnabledRunners(ids: string[]): void;
|
|
2660
|
+
|
|
2661
|
+
interface IWebFetcher {
|
|
2323
2662
|
/**
|
|
2324
|
-
*
|
|
2325
|
-
*
|
|
2326
|
-
*
|
|
2663
|
+
* The web-read policy's decision (ADR-0027), for a read this fetcher will
|
|
2664
|
+
* carry or one a runner carries natively, such as Claude's `WebFetch`.
|
|
2665
|
+
* `fetch` and `search` decide for themselves; never call both.
|
|
2327
2666
|
*/
|
|
2328
|
-
|
|
2329
|
-
/** Whether `
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2667
|
+
decide(read: WebRead): Promise<WebReadDecision>;
|
|
2668
|
+
/** Whether a runner's own search through `provider` would run unasked; see `WebReadPolicy.searchesUnasked`. */
|
|
2669
|
+
searchesUnasked?(provider: string): boolean;
|
|
2670
|
+
fetch(url: string): Promise<ToolOutcome>;
|
|
2671
|
+
/**
|
|
2672
|
+
* Optional web search. A fetcher without it makes the `web_search` tool
|
|
2673
|
+
* report itself unavailable rather than failing the turn — the same
|
|
2674
|
+
* degradation `fetch` already uses when no fetcher is wired at all.
|
|
2675
|
+
*/
|
|
2676
|
+
search?(query: string): Promise<ToolOutcome>;
|
|
2677
|
+
/**
|
|
2678
|
+
* A variant of this fetcher for a research subagent: same policy, cache,
|
|
2679
|
+
* transport and budget, but a read that would ask a person is refused
|
|
2680
|
+
* instead (ADR-0027, ADR-0008 T6). Absent means subagents get no web tools
|
|
2681
|
+
* and the `fetch`/`web_search` tools report themselves unavailable.
|
|
2682
|
+
*/
|
|
2683
|
+
forSubagent?(): IWebFetcher;
|
|
2337
2684
|
}
|
|
2338
2685
|
|
|
2339
|
-
|
|
2340
|
-
|
|
2686
|
+
/**
|
|
2687
|
+
* A person's approvals of project MCP servers, kept across sessions
|
|
2688
|
+
* (ADR-0028). Keyed by repository root and scope, which names the server
|
|
2689
|
+
* and hashes its definition: a changed definition is a new question.
|
|
2690
|
+
*/
|
|
2691
|
+
interface ProjectServerApprovals {
|
|
2692
|
+
approved(repoRoot: string, scope: string): boolean;
|
|
2693
|
+
remember(repoRoot: string, scope: string): void;
|
|
2694
|
+
}
|
|
2341
2695
|
|
|
2342
|
-
|
|
2343
|
-
|
|
2696
|
+
interface CliAgentAiServiceDeps extends Partial<AgentProcessDeps> {
|
|
2697
|
+
/** Overrides adapter construction. Tests supply a fake agent; production picks by runner id. */
|
|
2698
|
+
createAdapter?: (runner: string, deps: AgentProcessDeps) => AgentAdapter | null;
|
|
2699
|
+
/** Workspace root the agent explores. Defaults to the host process's cwd. */
|
|
2700
|
+
workspaceRoot?: () => string;
|
|
2701
|
+
/** Where a conversation's planner tools are served (ADR-0022). Absent: no conversation can start. */
|
|
2702
|
+
mcpServer?: OrdewellMcpServer;
|
|
2703
|
+
/** A person's approvals of project MCP servers, kept across sessions (ADR-0028). Defaults to Ordewell's data directory. */
|
|
2704
|
+
projectServerApprovals?: ProjectServerApprovals;
|
|
2705
|
+
}
|
|
2706
|
+
/**
|
|
2707
|
+
* A coding agent driven as Ordewell's planner (ADR-0009).
|
|
2708
|
+
*
|
|
2709
|
+
* The second transport behind `IAiService`, sitting beside `OpenAiService` and
|
|
2710
|
+
* `GeminiService`. It deliberately does **not** extend {@link BaseAiService}:
|
|
2711
|
+
* that class's body is Ordewell executing research tools on a model's behalf,
|
|
2712
|
+
* which is precisely the part a coding agent replaces. What it reuses instead
|
|
2713
|
+
* is everything above the transport — `settleReply` (the empty-reply nudge
|
|
2714
|
+
* and the turn's settling), the plan validator, the `ResearchProgress` events the
|
|
2715
|
+
* four surfaces already render. That is why this backend reaches VS Code, the
|
|
2716
|
+
* web UI, the CLI and the TUI without any of them learning a coding agent is
|
|
2717
|
+
* on the other end.
|
|
2718
|
+
*/
|
|
2719
|
+
declare class CliAgentAiService implements IAiService {
|
|
2720
|
+
private config;
|
|
2721
|
+
private readonly runner;
|
|
2722
|
+
private readonly processDeps;
|
|
2723
|
+
private readonly makeAdapter;
|
|
2724
|
+
private readonly workspaceRoot;
|
|
2725
|
+
private environmentSecrets;
|
|
2726
|
+
/** The conversation's session, from its start until the next session boundary. */
|
|
2727
|
+
private session;
|
|
2728
|
+
private readonly mcpServer;
|
|
2729
|
+
private readonly projectServerApprovals;
|
|
2730
|
+
private activeAbort;
|
|
2731
|
+
constructor(config: IConfig, deps?: CliAgentAiServiceDeps);
|
|
2732
|
+
hasActiveConversation(): boolean;
|
|
2733
|
+
/**
|
|
2734
|
+
* False when the running agent process was spawned under a model/effort the
|
|
2735
|
+
* user has since changed in the picker. Unlike a vendor backend, the model
|
|
2736
|
+
* is a spawn-time argument to the agent CLI (`--model`), not a per-request
|
|
2737
|
+
* field — `continueConversation` sends the next turn to whatever process is
|
|
2738
|
+
* already running, so a plain config read here would silently keep planning
|
|
2739
|
+
* on the old model. No conversation yet is vacuously "current".
|
|
2740
|
+
*/
|
|
2741
|
+
conversationMatchesConfig(): boolean;
|
|
2742
|
+
/** Always: a conversation whose planner cannot reach the Ordewell MCP server fails to start rather than falling back to the envelopes. */
|
|
2743
|
+
plannerToolsAttached(): boolean;
|
|
2744
|
+
/** The agent's own session id, a resumption hint only — Ordewell's transcript is authoritative (T4). */
|
|
2745
|
+
nativeSessionId(): string | null;
|
|
2746
|
+
heldSecrets(): readonly string[];
|
|
2747
|
+
restart(): void;
|
|
2748
|
+
reset(): void;
|
|
2749
|
+
startConversation(req: ConversationRequest): Promise<ConversationTurn>;
|
|
2750
|
+
continueConversation(userMessage: string, onProgress: (progress: ResearchProgress) => void, signal?: AbortSignal): Promise<ConversationTurn>;
|
|
2751
|
+
private openingMessage;
|
|
2752
|
+
/**
|
|
2753
|
+
* Drive one user message to a settled planner turn through
|
|
2754
|
+
* {@link settleReply}, the loop the API backend settles through too. The
|
|
2755
|
+
* tool rounds belong to the agent now, so one call is one agent turn —
|
|
2756
|
+
* continued while it left subagents running in the background.
|
|
2757
|
+
*/
|
|
2758
|
+
private runConversation;
|
|
2759
|
+
/**
|
|
2760
|
+
* Run one agent turn: stream its events into `ResearchProgress`, collect the
|
|
2761
|
+
* reply text, and return the research steps produced. Tool calls are matched
|
|
2762
|
+
* to their results by the agent's own call id — matching by tool name puts
|
|
2763
|
+
* one file's body on another file's row the moment an agent runs two reads
|
|
2764
|
+
* at once, which all three of these do routinely.
|
|
2765
|
+
*/
|
|
2766
|
+
/**
|
|
2767
|
+
* A turn, and then — while the agent ended it with subagents still in the
|
|
2768
|
+
* background — a turn asking it to wait for them: whatever they find would
|
|
2769
|
+
* otherwise be said into a closed turn, or, in a one-shot, into a session
|
|
2770
|
+
* already thrown away. Bounded by `waits`, shared across a reply's sends.
|
|
2771
|
+
*/
|
|
2772
|
+
private runTurnAwaitingAgents;
|
|
2773
|
+
private runTurn;
|
|
2774
|
+
/**
|
|
2775
|
+
* The only way this service starts an agent, and it takes a planner start by
|
|
2776
|
+
* type: the read-only boundary (ADR-0008/0009) cannot be crossed into task
|
|
2777
|
+
* mode from here without changing this signature. A planner that cannot
|
|
2778
|
+
* reach its tools cannot submit a plan, so it is respawned once and never
|
|
2779
|
+
* left to plan without them (ADR-0025).
|
|
2780
|
+
*/
|
|
2781
|
+
private startAdapter;
|
|
2782
|
+
private newAdapter;
|
|
2783
|
+
/** Whether the web-read policy lets the runner's own session-wide search run unasked now (ADR-0027). */
|
|
2784
|
+
private nativeSearch;
|
|
2785
|
+
/**
|
|
2786
|
+
* The live agent process, restarted from its own session id if it died
|
|
2787
|
+
* between turns. Resume is a hint: when it fails, the caller's next
|
|
2788
|
+
* `startConversation` reseeds from Ordewell's transcript, which is the same
|
|
2789
|
+
* degradation `restoreChat` already performs on every surface.
|
|
2790
|
+
*
|
|
2791
|
+
* Also restarted when the policy's decision on the runner's own search has
|
|
2792
|
+
* changed since the process was spawned: the running thread would otherwise
|
|
2793
|
+
* keep searching unasked after Full turned Guarded. Codex 0.162 applies
|
|
2794
|
+
* `thread/resume`'s `config.web_search` either way (checked live).
|
|
2795
|
+
*/
|
|
2796
|
+
private ensureAdapter;
|
|
2797
|
+
/**
|
|
2798
|
+
* What the planner gets of the repository's config, its runner being
|
|
2799
|
+
* launched without it (ADR-0028): the instruction files, which are text, in
|
|
2800
|
+
* its system prompt, and the project MCP servers it may start. Decided once
|
|
2801
|
+
* per conversation, so a respawn starts the same servers without asking.
|
|
2802
|
+
*/
|
|
2803
|
+
private repoConfig;
|
|
2804
|
+
private plannerModel;
|
|
2805
|
+
/**
|
|
2806
|
+
* A plan from one-shot agent sessions: only what a submit_plan call
|
|
2807
|
+
* accepted, never the reply text, so a plan the tool refused cannot come
|
|
2808
|
+
* back in as the JSON the agent echoed. A session that ends without an
|
|
2809
|
+
* accepted call is followed by one corrective session, then fails.
|
|
2810
|
+
*/
|
|
2811
|
+
private submittedPlan;
|
|
2812
|
+
/**
|
|
2813
|
+
* A single agent session that answers one prompt and exits. Used by every
|
|
2814
|
+
* non-conversational entry point. A plan-shaped envelope in its text
|
|
2815
|
+
* streams to the plan display, as a vendor planner's one-shot does, and the
|
|
2816
|
+
* prose around it is not streamed at all, since no turn is open to show it in.
|
|
2817
|
+
*/
|
|
2818
|
+
private oneShot;
|
|
2819
|
+
researchAndPlan(userDescription: string, runners: RunnerId[], modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>, fs: IFileSystem, onProgress: (progress: ResearchProgress) => void, _fetcher?: IWebFetcher, runnerModes?: Record<RunnerId, RunnerModeInfo[]>, modes?: PlannerModes, signal?: AbortSignal): Promise<{
|
|
2820
|
+
tasks: Task[];
|
|
2821
|
+
researchLog: ResearchLogEntry[];
|
|
2822
|
+
researchResults: string;
|
|
2823
|
+
}>;
|
|
2824
|
+
generatePlanDirect(userDescription: string, runners?: RunnerId[], modelsByRunner?: Partial<Record<RunnerId, DiscoveredModel[]>>, onToken?: (token: string) => void, _fs?: IFileSystem, _fetcher?: IWebFetcher, runnerModes?: Record<RunnerId, RunnerModeInfo[]>, modes?: PlannerModes, signal?: AbortSignal): Promise<Task[]>;
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
/**
|
|
2828
|
+
* The planner tools a session offers its conversation (ADR-0022). A backend
|
|
2829
|
+
* that can inject the Ordewell MCP server into its planner issues a planner
|
|
2830
|
+
* token bound to `sessionId` and these handlers; API planners use the same task read handlers directly in their tool loop.
|
|
2831
|
+
*/
|
|
2832
|
+
interface PlannerToolsOffer {
|
|
2833
|
+
sessionId: string;
|
|
2834
|
+
handler: PlannerToolHandler;
|
|
2835
|
+
}
|
|
2836
|
+
/**
|
|
2837
|
+
* Everything the conversation loop needs to start planning (ADR-0002).
|
|
2838
|
+
* The AI service keeps the tool-use message history internally across turns;
|
|
2839
|
+
* the surfaces only exchange user/assistant messages with it.
|
|
2840
|
+
*/
|
|
2841
|
+
interface ConversationRequest {
|
|
2842
|
+
goal: string;
|
|
2843
|
+
runners: RunnerId[];
|
|
2844
|
+
modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>;
|
|
2845
|
+
fs: IFileSystem;
|
|
2846
|
+
onProgress: (progress: ResearchProgress) => void;
|
|
2847
|
+
fetcher?: IWebFetcher;
|
|
2848
|
+
runnerModes?: Record<RunnerId, RunnerModeInfo[]>;
|
|
2849
|
+
autonomousDefault?: boolean;
|
|
2850
|
+
/** Where tasks will run (ADR-0013, ADR-0014): in worktrees, the prompt drops file-overlap ordering and describes a repo group. */
|
|
2851
|
+
isolatedExecution?: IsolatedExecution;
|
|
2852
|
+
/** A plan the planner submits starts at once (ADR-0030), so the opening prompt has no outline gate. */
|
|
2853
|
+
autorun?: boolean;
|
|
2854
|
+
signal?: AbortSignal;
|
|
2855
|
+
/**
|
|
2856
|
+
* Persisted dialogue to seed a resumed conversation (session reload). The
|
|
2857
|
+
* turns are injected into the message history verbatim — no LLM call happens
|
|
2858
|
+
* for them; the first call is the turn opened by `initialMessage`.
|
|
2859
|
+
*/
|
|
2860
|
+
priorHistory?: ConversationMessage[];
|
|
2861
|
+
/**
|
|
2862
|
+
* The user message that opens this turn. Defaults to `goal` — set it when
|
|
2863
|
+
* resuming so the system prompt keeps the original goal while the turn
|
|
2864
|
+
* carries the user's new message.
|
|
2865
|
+
*/
|
|
2866
|
+
initialMessage?: string;
|
|
2867
|
+
plannerTools?: PlannerToolsOffer;
|
|
2868
|
+
/**
|
|
2869
|
+
* The session's approval policy, which a harness planner's own tool
|
|
2870
|
+
* requests are decided against as the API planner's are (ADR-0026). Absent:
|
|
2871
|
+
* nobody can be asked, so they are denied.
|
|
2872
|
+
*/
|
|
2873
|
+
approval?: IApproval;
|
|
2874
|
+
/**
|
|
2875
|
+
* The workspace's skill catalog. Every planner is shown the task skills it
|
|
2876
|
+
* may attach; only one with tools is shown the planner skills, which load
|
|
2877
|
+
* through `load_skill` alone (ADR-0024).
|
|
2878
|
+
*/
|
|
2879
|
+
skills?: readonly SkillInfo[];
|
|
2880
|
+
}
|
|
2881
|
+
/**
|
|
2882
|
+
* A run of a turn's reply text, as a backend reports it. `narration`: it began
|
|
2883
|
+
* before its call's last tool call, so it stays when a corrective retry
|
|
2884
|
+
* discards what the call replied.
|
|
2885
|
+
*/
|
|
2886
|
+
interface TurnRun extends ReplyRun {
|
|
2887
|
+
narration?: true;
|
|
2888
|
+
}
|
|
2889
|
+
/**
|
|
2890
|
+
* One planner turn's outcome. The planner talks to the user (`message`),
|
|
2891
|
+
* commits a full plan (`plan`, a `{tasks:[...]}` JSON object), emits targeted
|
|
2892
|
+
* task edits (`task_ops`, a `{taskOps:[...]}` JSON object), or asks to read
|
|
2893
|
+
* task bodies and the full catalog before editing (`task_query`, a
|
|
2894
|
+
* `{taskQuery:{...}}` JSON object). The Session validates and applies task ops
|
|
2895
|
+
* atomically and answers queries out of its own state; the AI service only
|
|
2896
|
+
* parses them.
|
|
2897
|
+
*/
|
|
2898
|
+
type ConversationTurn =
|
|
2899
|
+
/**
|
|
2900
|
+
* `aborted`: the call was stopped, and `text` is only what it had by then.
|
|
2901
|
+
* `failed`: the call ended without a reply (a dead agent, a usage limit),
|
|
2902
|
+
* and `text` says why; `usageLimit`, where the runner also said in a field
|
|
2903
|
+
* whether that was its usage limit (see {@link TurnFailure}).
|
|
2904
|
+
* `runs`, on every kind: the reply's runs the turn's surviving calls
|
|
2905
|
+
* streamed, in order, and last a note written in the planner's place when
|
|
2906
|
+
* `text` is one. An envelope's own run is not among them. Absent from a
|
|
2907
|
+
* backend that does not report runs.
|
|
2908
|
+
*/
|
|
2909
|
+
{
|
|
2910
|
+
kind: 'message';
|
|
2911
|
+
text: string;
|
|
2912
|
+
researchLog: ResearchLogEntry[];
|
|
2913
|
+
aborted?: boolean;
|
|
2914
|
+
failed?: boolean;
|
|
2915
|
+
usageLimit?: UsageLimitSignal | false;
|
|
2916
|
+
runs?: TurnRun[];
|
|
2917
|
+
} | {
|
|
2918
|
+
kind: 'plan';
|
|
2919
|
+
tasks: Task[];
|
|
2920
|
+
text: string;
|
|
2921
|
+
researchLog: ResearchLogEntry[];
|
|
2922
|
+
runs?: TurnRun[];
|
|
2923
|
+
} | {
|
|
2924
|
+
kind: 'task_ops';
|
|
2925
|
+
ops: TaskOp[];
|
|
2926
|
+
text: string;
|
|
2927
|
+
researchLog: ResearchLogEntry[];
|
|
2928
|
+
runs?: TurnRun[];
|
|
2929
|
+
} | {
|
|
2930
|
+
kind: 'task_query';
|
|
2931
|
+
query: TaskQuery;
|
|
2932
|
+
text: string;
|
|
2933
|
+
researchLog: ResearchLogEntry[];
|
|
2934
|
+
runs?: TurnRun[];
|
|
2935
|
+
};
|
|
2936
|
+
interface IAiService {
|
|
2937
|
+
/** Credential values from the harness planner's effective environment, for the outbound secret check. */
|
|
2938
|
+
heldSecrets?(): readonly string[];
|
|
2939
|
+
/**
|
|
2940
|
+
* Begin the planner conversation: collect workspace context, run the
|
|
2941
|
+
* research/tool loop, and return the first planner turn. The service
|
|
2942
|
+
* retains the full API message history for subsequent
|
|
2943
|
+
* {@link continueConversation} calls.
|
|
2944
|
+
*/
|
|
2945
|
+
startConversation(req: ConversationRequest): Promise<ConversationTurn>;
|
|
2946
|
+
/**
|
|
2947
|
+
* Feed the user's reply into the active conversation and return the next
|
|
2948
|
+
* planner turn. Throws if no conversation is active.
|
|
2949
|
+
*/
|
|
2950
|
+
continueConversation(userMessage: string, onProgress: (progress: ResearchProgress) => void, signal?: AbortSignal): Promise<ConversationTurn>;
|
|
2951
|
+
/**
|
|
2952
|
+
* Whether a planner conversation is currently held in memory. A branching
|
|
2953
|
+
* predicate only — never a test for whether there is anything to release.
|
|
2954
|
+
* The harness backend holds an OS process that outlives its conversation, so
|
|
2955
|
+
* guarding {@link reset} with this leaks an agent process; call `reset`
|
|
2956
|
+
* unconditionally instead.
|
|
2957
|
+
*/
|
|
2958
|
+
hasActiveConversation(): boolean;
|
|
2959
|
+
/**
|
|
2960
|
+
* Optional: end the planner's live process but keep its own session id, so
|
|
2961
|
+
* the next turn starts a fresh one resumed from it (ADR-0009). What a
|
|
2962
|
+
* supervisor does to a planner that crashed or hung (ADR-0029). Absent where
|
|
2963
|
+
* there is no process to end: the next call is a fresh request anyway.
|
|
2964
|
+
*/
|
|
2965
|
+
restart?(): void;
|
|
2966
|
+
/**
|
|
2967
|
+
* Whether the active conversation (if any) still matches the planner
|
|
2968
|
+
* model/effort currently configured. Optional, and true when absent: a
|
|
2969
|
+
* vendor backend re-reads its model from config on every turn, so there is
|
|
2970
|
+
* nothing to drift. A harness planner (ADR-0009) is the exception — its
|
|
2971
|
+
* model is baked into the agent process at spawn — so only
|
|
2972
|
+
* {@link CliAgentAiService} answers this for real. False tells the caller
|
|
2973
|
+
* the same thing an absent conversation would: don't call
|
|
2974
|
+
* `continueConversation`, restart instead so the new model takes effect.
|
|
2975
|
+
*/
|
|
2976
|
+
conversationMatchesConfig?(): boolean;
|
|
2977
|
+
/**
|
|
2978
|
+
* Whether the planner reads the catalog and submits plans through Ordewell's
|
|
2979
|
+
* MCP tools (ADR-0022). Optional, and false when absent: only a harness
|
|
2980
|
+
* planner the server attached to has them, and every other planner keeps
|
|
2981
|
+
* the per-turn catalog and the envelopes.
|
|
2982
|
+
*/
|
|
2983
|
+
plannerToolsAttached?(): boolean;
|
|
2984
|
+
/**
|
|
2985
|
+
* Optional: prune bulky raw tool output out of the live conversation, in
|
|
2986
|
+
* place, keeping subagent digests. Returns the characters removed. Absent
|
|
2987
|
+
* where the backend holds no tool transcript to prune (a harness planner's
|
|
2988
|
+
* agent owns its own context).
|
|
2989
|
+
*/
|
|
2990
|
+
pruneContext?(): number;
|
|
2991
|
+
/**
|
|
2992
|
+
* One-shot research + plan for non-conversational surfaces (CLI `plan --goal`,
|
|
2993
|
+
* web REST). Never asks questions — it plans with what it can find.
|
|
2994
|
+
*/
|
|
2995
|
+
researchAndPlan(userDescription: string, runners: RunnerId[], modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>, fs: IFileSystem, onProgress: (progress: ResearchProgress) => void, fetcher?: IWebFetcher, runnerModes?: Record<RunnerId, RunnerModeInfo[]>, modes?: PlannerModes, signal?: AbortSignal): Promise<{
|
|
2996
|
+
tasks: Task[];
|
|
2997
|
+
researchLog: ResearchLogEntry[];
|
|
2998
|
+
researchResults: string;
|
|
2999
|
+
}>;
|
|
3000
|
+
generatePlanDirect(userDescription: string, runners: RunnerId[], modelsByRunner: Partial<Record<RunnerId, DiscoveredModel[]>>, onToken?: (token: string) => void, fs?: IFileSystem, fetcher?: IWebFetcher, runnerModes?: Record<RunnerId, RunnerModeInfo[]>, modes?: PlannerModes, signal?: AbortSignal): Promise<Task[]>;
|
|
3001
|
+
/**
|
|
3002
|
+
* Release everything this service holds: the conversation, any in-flight
|
|
3003
|
+
* turn, and — on the harness backend — the agent process itself. Idempotent
|
|
3004
|
+
* and cheap in every implementation, so callers never gate it.
|
|
3005
|
+
*/
|
|
3006
|
+
reset(): void;
|
|
3007
|
+
}
|
|
3008
|
+
/**
|
|
3009
|
+
* The single branch point between planner transports. Two of them talk HTTP to
|
|
3010
|
+
* an LLM vendor; the third (ADR-0009) drives a coding agent already installed
|
|
3011
|
+
* on the machine. Everything downstream — the plan contract, the research-step
|
|
3012
|
+
* stream, the four surfaces — is shared.
|
|
3013
|
+
*/
|
|
3014
|
+
declare function createAiService(config: IConfig, deps?: CliAgentAiServiceDeps): IAiService;
|
|
3015
|
+
|
|
3016
|
+
/** The dialogue record a fork carries — what {@link PlannerConversation.clone} returns. */
|
|
3017
|
+
interface ForkedDialogue {
|
|
3018
|
+
conversationHistory: ConversationMessage[];
|
|
3019
|
+
researchLog: ResearchLogEntry[];
|
|
3020
|
+
}
|
|
3021
|
+
|
|
3022
|
+
/** A user's message as sent, and the skills its `/name` tokens load. */
|
|
3023
|
+
interface SkillInvocation {
|
|
3024
|
+
text: string;
|
|
3025
|
+
skills: SkillLoad[];
|
|
3026
|
+
}
|
|
3027
|
+
/**
|
|
3028
|
+
* A path with the home directory written `~`, for a notice or listing. On
|
|
3029
|
+
* Windows the drive letter's case is not significant and the separators are
|
|
3030
|
+
* rendered `/`, so the same skill reads alike wherever the session reloads.
|
|
3031
|
+
*/
|
|
3032
|
+
declare function abbreviateHome(file: string, home?: string, platform?: NodeJS.Platform): string;
|
|
3033
|
+
/**
|
|
3034
|
+
* Resolve the `/skill-name` tokens in a message to snapshots of the skills they
|
|
3035
|
+
* name. Pure and exported so surfaces can resolve without a full Session. The text itself is never rewritten.
|
|
3036
|
+
*
|
|
3037
|
+
* Every distinct skill named loads, once, in the order first named. A token
|
|
3038
|
+
* naming no skill is plain text, wherever it stands — a whole message of
|
|
3039
|
+
* `/unknown` included: embedded in a sentence it is as likely to be incidental
|
|
3040
|
+
* text (a path, an example command) as a typo'd invocation. So is one naming
|
|
3041
|
+
* a skill with `user-invocable: false`, which only the model may invoke.
|
|
3042
|
+
*/
|
|
3043
|
+
declare function resolveSkillInvocation(text: string, skillsService: Pick<SkillsService, 'findSkill'>, home?: string): SkillInvocation;
|
|
3044
|
+
/**
|
|
3045
|
+
* What the planner receives for a user message: the bodies of the skills it
|
|
3046
|
+
* loaded, then the message as the user wrote it. The one composition for a
|
|
3047
|
+
* live send and for a replay of the transcript, so a resumed conversation
|
|
3048
|
+
* hands the planner what the live one did.
|
|
3049
|
+
*/
|
|
3050
|
+
declare function plannerMessage(text: string, loads: readonly SkillLoad[]): string;
|
|
3051
|
+
/**
|
|
3052
|
+
* The transcript as the planner is to see it on a replay: each user message
|
|
3053
|
+
* carries the skills loaded after it, composed by {@link plannerMessage}, and
|
|
3054
|
+
* the load entries themselves are folded away. Sessions saved before skill
|
|
3055
|
+
* loads existed hold the expanded text in the message and replay unchanged.
|
|
3056
|
+
* A failed turn's entry is left out: the planner never said it.
|
|
3057
|
+
*/
|
|
3058
|
+
declare function plannerTranscript(history: readonly ConversationMessage[]): ConversationMessage[];
|
|
3059
|
+
|
|
3060
|
+
/**
|
|
3061
|
+
* What a stopped runner says about why it stopped, and what ending an attempt
|
|
3062
|
+
* does to the runner it leaves behind. TaskOrchestrator acts on the answers.
|
|
3063
|
+
*/
|
|
3064
|
+
|
|
3065
|
+
/**
|
|
3066
|
+
* A turn's failure as its runner reported it: its words, and `usageLimit`
|
|
3067
|
+
* where its protocol also says in a field whether it was the limit (Codex's
|
|
3068
|
+
* `codexErrorInfo`).
|
|
3069
|
+
*/
|
|
3070
|
+
interface TurnFailure {
|
|
3071
|
+
message: string;
|
|
3072
|
+
usageLimit?: UsageLimitSignal | false;
|
|
3073
|
+
}
|
|
3074
|
+
|
|
3075
|
+
/** What a usage limit that ended a planner turn led to. */
|
|
3076
|
+
interface PlannerLimit {
|
|
3077
|
+
/** The line the transcript keeps, drawn as a failure. */
|
|
3078
|
+
text: string;
|
|
3079
|
+
/** When what the turn took goes again on its own; null when it waits for the user's next message. */
|
|
3080
|
+
resendAt: Date | null;
|
|
3081
|
+
/** Why nothing goes again on its own, when nothing does: what the planner did, as in "the planner hit its usage limit and …". */
|
|
3082
|
+
held?: string;
|
|
3083
|
+
}
|
|
3084
|
+
|
|
3085
|
+
/**
|
|
3086
|
+
* What the user allows the plan to be built from right now: the enabled
|
|
3087
|
+
* runners and each runner's model allowlist. Structural, so a surface that
|
|
3088
|
+
* only holds the two lists can ask without a session.
|
|
3089
|
+
*/
|
|
3090
|
+
interface AssignmentScope {
|
|
3091
|
+
enabledRunners: readonly string[];
|
|
3092
|
+
allowlist: Readonly<Record<string, readonly string[] | undefined>>;
|
|
3093
|
+
/**
|
|
3094
|
+
* The models discovery has found per runner, where the holder has them. An
|
|
3095
|
+
* allowlist is read against them exactly as the planner's catalog reads it
|
|
3096
|
+
* (ids only another runner lists limit nothing), so a flag never says a
|
|
3097
|
+
* model is off a list the planner sees as no limit.
|
|
3098
|
+
*/
|
|
3099
|
+
models?: Readonly<Partial<Record<RunnerId, readonly DiscoveredModel[]>>>;
|
|
3100
|
+
}
|
|
3101
|
+
/** The part of a task an assignment is read from. */
|
|
3102
|
+
interface AssignedTask {
|
|
3103
|
+
type: string;
|
|
3104
|
+
status: string;
|
|
3105
|
+
assignedRunner: string;
|
|
3106
|
+
assignedModel?: {
|
|
3107
|
+
modelId: string;
|
|
3108
|
+
modelLabel?: string;
|
|
3109
|
+
};
|
|
3110
|
+
}
|
|
3111
|
+
type AssignmentIssue = {
|
|
3112
|
+
kind: 'runner-off';
|
|
3113
|
+
runner: string;
|
|
3114
|
+
} | {
|
|
3115
|
+
kind: 'model-off-list';
|
|
3116
|
+
runner: string;
|
|
3117
|
+
modelId: string;
|
|
3118
|
+
modelLabel: string;
|
|
3119
|
+
};
|
|
3120
|
+
/**
|
|
3121
|
+
* Whether a task sits outside the scope it is read against. This only names
|
|
3122
|
+
* the mismatch: such a task still runs (ADR-0003, P1) — the allowlist and the
|
|
3123
|
+
* enabled runners shape what is planned, not what is started — and the user or
|
|
3124
|
+
* the planner may move it. A runner that is off settles the matter before its
|
|
3125
|
+
* model is looked at.
|
|
3126
|
+
*/
|
|
3127
|
+
declare function assignmentIssue(task: AssignedTask, scope: AssignmentScope): AssignmentIssue | null;
|
|
3128
|
+
declare function assignmentIssueText(issue: AssignmentIssue): string;
|
|
3129
|
+
|
|
3130
|
+
/**
|
|
3131
|
+
* The planner turn (see CONTEXT.md): the one piece of planner work that holds
|
|
3132
|
+
* the conversation, from its claim to its settled outcome. Its signal is what a
|
|
3133
|
+
* stop aborts.
|
|
3134
|
+
*/
|
|
3135
|
+
declare class PlannerTurn {
|
|
3136
|
+
private readonly controller;
|
|
3137
|
+
private dropped;
|
|
3138
|
+
private forced;
|
|
3139
|
+
get signal(): AbortSignal;
|
|
3140
|
+
/** The plan it was answering was dropped under it, so nothing it settles may land. */
|
|
3141
|
+
get abandoned(): boolean;
|
|
3142
|
+
/** A force send stopped it, not the user: the forced turn follows. A turn already stopped stays the user's stop. */
|
|
3143
|
+
get forceSent(): boolean;
|
|
3144
|
+
stop(by?: 'force_send'): void;
|
|
3145
|
+
abandon(): void;
|
|
3146
|
+
}
|
|
3147
|
+
/** What a planner turn may change: the whole plan, or tasks through edits. */
|
|
3148
|
+
type PlanChange = 'plan' | 'edit';
|
|
3149
|
+
/**
|
|
3150
|
+
* A wake-up turn (ADR-0029): a supervisor's turn on wake-up digests alone, no
|
|
3151
|
+
* user message in it. What it may do is its level's, decided by the host.
|
|
3152
|
+
*/
|
|
3153
|
+
interface WakeUpTurn {
|
|
3154
|
+
/** Reads the turn may make, in place of a user message's caps. */
|
|
3155
|
+
readBudget: number;
|
|
3156
|
+
/**
|
|
3157
|
+
* Why a whole plan, or a task edit, is refused in this turn, worded for the
|
|
3158
|
+
* model; undefined when its level allows it. Asked when the change is made
|
|
3159
|
+
* and again as it lands, so a level lowered during the turn applies at once.
|
|
3160
|
+
*/
|
|
3161
|
+
refused(change: PlanChange): string | undefined;
|
|
3162
|
+
/** Told to the model after the digests and never saved, like the plan block: who it is in this turn, and what it may do. */
|
|
3163
|
+
brief?: string;
|
|
3164
|
+
}
|
|
3165
|
+
/** A read made through a planner tool: refused once the turn's budget is gone, else answered, told to land when the budget asks it to. */
|
|
3166
|
+
type ToolRead<T> = {
|
|
3167
|
+
status: 'refused';
|
|
3168
|
+
} | {
|
|
3169
|
+
status: 'answered';
|
|
3170
|
+
value: T;
|
|
3171
|
+
landNow: boolean;
|
|
3172
|
+
};
|
|
3173
|
+
/** A plan or task edit handed to the open turn over a channel other than its reply (ADR-0022). */
|
|
3174
|
+
type PlannerSubmission = {
|
|
3175
|
+
kind: 'plan';
|
|
3176
|
+
tasks: Task[];
|
|
3177
|
+
} | {
|
|
3178
|
+
kind: 'task_ops';
|
|
3179
|
+
ops: TaskOp[];
|
|
3180
|
+
};
|
|
3181
|
+
/** Everything (re)opening a conversation needs besides the dialogue itself. */
|
|
3182
|
+
type ConversationOpening = Omit<ConversationRequest, 'goal' | 'onProgress' | 'signal' | 'priorHistory' | 'initialMessage'>;
|
|
3183
|
+
/**
|
|
3184
|
+
* What the conversation needs from the session that hosts it. The session keeps
|
|
3185
|
+
* plan state, persistence and scheduling; the conversation reaches them only
|
|
3186
|
+
* through here, so a fake host is enough to drive it.
|
|
3187
|
+
*/
|
|
3188
|
+
interface PlannerConversationHost {
|
|
3189
|
+
/** The plan the transcript lives on (`conversationHistory`, `researchLog`). */
|
|
3190
|
+
plan(): LegacyPlanState | null;
|
|
3191
|
+
goal(): string;
|
|
3192
|
+
aiService(): IAiService;
|
|
3193
|
+
onProgress(progress: ResearchProgress): void;
|
|
3194
|
+
/** Fresh discovery, allowlist-filtered the way the system prompt shows it. */
|
|
3195
|
+
opening(runners: RunnerId[]): Promise<ConversationOpening>;
|
|
3196
|
+
/** What the per-turn catalog block and every read draw from, as of now. */
|
|
3197
|
+
catalog(): TaskQueryCatalog;
|
|
3198
|
+
/** Whether autorun is on now (ADR-0030); the planner is told in the turn after it changes. */
|
|
3199
|
+
autorun(): boolean;
|
|
3200
|
+
/** The enabled runners and allowlist as of now; the planner is told in the turn after either changes. */
|
|
3201
|
+
assignmentScope(): AssignmentScope;
|
|
3202
|
+
tasks(): readonly Task[];
|
|
3203
|
+
/**
|
|
3204
|
+
* Why a task cannot start now, from the scheduler's start rule (ADR-0034);
|
|
3205
|
+
* none once nothing holds it back, or while the plan is not yet approved.
|
|
3206
|
+
*/
|
|
3207
|
+
startWaits(task: Readonly<Task>): readonly StartWait[];
|
|
3208
|
+
/**
|
|
3209
|
+
* The orchestrator's live capture for a task's latest attempt, backing the
|
|
3210
|
+
* `output` field of a read. Injected the same way as the catalog so the
|
|
3211
|
+
* conversation never reaches into execution state directly.
|
|
3212
|
+
*/
|
|
3213
|
+
liveOutput: LiveOutputLookup;
|
|
3214
|
+
isExecuting(): boolean;
|
|
3215
|
+
/** The session's single mutation ritual: op → persist → notify (default: the plan). */
|
|
3216
|
+
mutate(op: () => boolean, notify?: () => void): LegacyPlanState | null;
|
|
3217
|
+
broadcast: SessionBroadcaster;
|
|
3218
|
+
/** `turnId`: the planner turn whose commit this is, when one is. */
|
|
3219
|
+
broadcastPlan(turnId?: string): void;
|
|
3220
|
+
/**
|
|
3221
|
+
* Validate a batch against live state; nothing of it is applied. A
|
|
3222
|
+
* supervisor's batch that trips the oscillation guard is refused, and the
|
|
3223
|
+
* user is told of the trip once, in the supervisor's voice (ADR-0029): a note
|
|
3224
|
+
* saved as it is written, like any of Ordewell's own, which a turn in flight
|
|
3225
|
+
* holds nothing in.
|
|
3226
|
+
*/
|
|
3227
|
+
validateOps(ops: TaskOp[]): ApplyTaskOpsResult;
|
|
3228
|
+
/** The workspace's skill catalog under its repo group's layout, which the skills a plan or edit attaches are checked against. */
|
|
3229
|
+
taskSkills(): SkillLookup;
|
|
3230
|
+
/** Tell the user something the transcript does not carry. */
|
|
3231
|
+
notice(level: SessionNotice['level'], message: string): void;
|
|
3232
|
+
/** Load planner-produced tasks: an edit keeps run state, a commit starts over. Returns how many landed. */
|
|
3233
|
+
adoptTasks(tasks: readonly Task[], how: 'edit' | 'commit'): number;
|
|
3234
|
+
capturePrd(text: string): void;
|
|
3235
|
+
/** The planner turn was stopped or abandoned: whatever it is waiting on (a parked approval) will never be answered. */
|
|
3236
|
+
turnAborted(): void;
|
|
3237
|
+
/** A planner turn is about to take the conversation, which nothing holds now. */
|
|
3238
|
+
turnClaiming?(): void;
|
|
3239
|
+
/** A planner turn let the conversation go, so the next one may take it. */
|
|
3240
|
+
turnReleased?(): void;
|
|
3241
|
+
/** A turn ended on the planner's usage limit, which it reported as `said`: what is sent again, and when. */
|
|
3242
|
+
usageLimit(said: TurnFailure): PlannerLimit;
|
|
3243
|
+
}
|
|
3244
|
+
/**
|
|
3245
|
+
* A conversation edit (rewind, fork) the conversation refused because the
|
|
3246
|
+
* request itself is wrong — no such message, nothing to fork. Transport-agnostic
|
|
3247
|
+
* like `PlanEditError`: a route maps it to a status, core carries none.
|
|
3248
|
+
*/
|
|
3249
|
+
declare class ConversationEditError extends Error {
|
|
3250
|
+
constructor(message: string);
|
|
3251
|
+
}
|
|
3252
|
+
/**
|
|
3253
|
+
* Refused because a planner turn is in flight. Distinct from a bad request:
|
|
3254
|
+
* the same call succeeds once the turn settles, and the turn would otherwise
|
|
3255
|
+
* append its reply to a transcript that no longer holds the message it
|
|
3256
|
+
* answers.
|
|
3257
|
+
*/
|
|
3258
|
+
declare class ConversationBusyError extends ConversationEditError {
|
|
3259
|
+
constructor(operation: string);
|
|
3260
|
+
}
|
|
3261
|
+
/**
|
|
3262
|
+
* A planner turn that settled after the plan it was answering was dropped (a
|
|
3263
|
+
* new plan, another session adopted, the session closed). Nothing it produced
|
|
3264
|
+
* was committed: landing it would write one session's reply into another's
|
|
3265
|
+
* transcript.
|
|
3266
|
+
*/
|
|
3267
|
+
declare class PlannerTurnDiscardedError extends Error {
|
|
3268
|
+
constructor(options?: ErrorOptions);
|
|
3269
|
+
}
|
|
3270
|
+
/**
|
|
3271
|
+
* A wake-up turn's backend ended without a reply — a dead agent, a usage
|
|
3272
|
+
* limit — and said why. A user's turn shows that in the chat instead; a
|
|
3273
|
+
* wake-up has nobody waiting on it, so its supervisor is told (ADR-0029).
|
|
3274
|
+
*/
|
|
3275
|
+
declare class PlannerTurnFailedError extends Error {
|
|
3276
|
+
constructor(message: string);
|
|
3277
|
+
}
|
|
3278
|
+
/**
|
|
3279
|
+
* A planner turn ended on the planner's usage limit (#84). Nothing of it
|
|
3280
|
+
* lands: the transcript keeps the failure instead, out of the planner's own
|
|
3281
|
+
* dialogue, and the turn's inputs wait to be sent again — at `resendAt`, or
|
|
3282
|
+
* with the next input when it is null.
|
|
3283
|
+
*/
|
|
3284
|
+
declare class PlannerUsageLimitError extends PlannerTurnFailedError {
|
|
3285
|
+
readonly resendAt: Date | null;
|
|
3286
|
+
/** Why nothing is sent again on its own, when nothing is. */
|
|
3287
|
+
readonly held?: string;
|
|
3288
|
+
constructor(limit: PlannerLimit);
|
|
3289
|
+
}
|
|
3290
|
+
/**
|
|
3291
|
+
* The user stopped the turn and the backend reported that as a thrown error
|
|
3292
|
+
* rather than a settled reply. Typed so a surface can stay quiet for it without
|
|
3293
|
+
* reading the message: some SDKs' abort errors are plain `Error`s by name, and
|
|
3294
|
+
* a real failure can mention "aborted" too.
|
|
3295
|
+
*/
|
|
3296
|
+
declare class PlannerTurnStoppedError extends Error {
|
|
3297
|
+
constructor(options?: ErrorOptions);
|
|
3298
|
+
}
|
|
3299
|
+
/** A user message a rewind can fork the conversation from just before. */
|
|
3300
|
+
interface RewindTarget {
|
|
3301
|
+
/** Position in the transcript — what {@link PlannerConversation.cloneBefore} takes. */
|
|
3302
|
+
index: number;
|
|
3303
|
+
preview: string;
|
|
3304
|
+
/** The whole message — the preview cuts it to one line, and a surface confirming the rewind quotes it. */
|
|
3305
|
+
content: string;
|
|
3306
|
+
timestamp: string;
|
|
3307
|
+
}
|
|
3308
|
+
/** What a rewind forks from, and the message it lands just before. */
|
|
3309
|
+
interface RewoundDialogue {
|
|
3310
|
+
dialogue: ForkedDialogue;
|
|
3311
|
+
rewoundMessage: string;
|
|
3312
|
+
}
|
|
3313
|
+
/** What a compaction left behind. */
|
|
3314
|
+
interface ConversationCompaction {
|
|
3315
|
+
summary: string;
|
|
3316
|
+
/** Transcript entries kept verbatim after the summary entry. */
|
|
3317
|
+
keptMessages: number;
|
|
3318
|
+
}
|
|
3319
|
+
interface ReplyOptions {
|
|
3320
|
+
signal?: AbortSignal;
|
|
3321
|
+
/**
|
|
3322
|
+
* Run inside the mutation that lands the turn's outcome — the edit, the
|
|
3323
|
+
* plan or the reply — so what the caller drops with it (the inputs the turn
|
|
3324
|
+
* answered) leaves disk in the same save.
|
|
3325
|
+
*/
|
|
3326
|
+
onCommit?: () => void;
|
|
3327
|
+
/** Wake-up digests delivered with this turn (ADR-0029), oldest first, ahead of the user's message if there is one. */
|
|
3328
|
+
wakeUps?: readonly string[];
|
|
3329
|
+
/** Set when the turn is a wake-up's alone. */
|
|
3330
|
+
wakeUp?: WakeUpTurn;
|
|
3331
|
+
/** The user's message was force sent: the turn replaces the one it stopped (ADR-0023). */
|
|
3332
|
+
forced?: boolean;
|
|
3333
|
+
}
|
|
3334
|
+
/**
|
|
3335
|
+
* The planner conversation (ADR-0002), end to end: the persisted dialogue
|
|
3336
|
+
* record, the live model context behind it, and every turn from the user's
|
|
3337
|
+
* message to a settled, persisted outcome.
|
|
3338
|
+
*
|
|
3339
|
+
* It is the only writer of `conversationHistory` and of the planner's
|
|
3340
|
+
* `researchLog`. The writes land on the host's plan object — which is what the
|
|
3341
|
+
* session persists and broadcasts — and only ever inside the host's mutation
|
|
3342
|
+
* ritual, so each reaches disk in the save made for it. A planner turn writes
|
|
3343
|
+
* nothing until the mutation that lands it ({@link land}): any other save made
|
|
3344
|
+
* while it runs finds nothing of it to persist.
|
|
3345
|
+
*
|
|
3346
|
+
* The live model context (a vendor service's message list, a harness planner's
|
|
3347
|
+
* process and native session id) is disposable: {@link reset} drops it and the
|
|
3348
|
+
* next turn is replayed from the transcript. That is what lets the transcript
|
|
3349
|
+
* be replaced by a summary, or copied into a fork, without the model's memory
|
|
3350
|
+
* drifting from it.
|
|
3351
|
+
*/
|
|
3352
|
+
declare class PlannerConversation {
|
|
3353
|
+
private readonly host;
|
|
3354
|
+
private readonly briefing;
|
|
3355
|
+
private live;
|
|
3356
|
+
private openTurn;
|
|
3357
|
+
constructor(host: PlannerConversationHost);
|
|
3358
|
+
get transcript(): readonly ConversationMessage[];
|
|
3359
|
+
/** Whether a planner turn holds the conversation. */
|
|
3360
|
+
get isTurnInFlight(): boolean;
|
|
3361
|
+
/** Stop the planner turn in flight; it settles as stopped. False when none is. */
|
|
3362
|
+
stopTurn(by?: 'force_send'): boolean;
|
|
3363
|
+
/**
|
|
3364
|
+
* Drop the planner turn in flight together with the plan it was answering:
|
|
3365
|
+
* it is stopped, whatever it settles is discarded, and the conversation is
|
|
3366
|
+
* free at once rather than when the abandoned turn finishes unwinding.
|
|
3367
|
+
*/
|
|
3368
|
+
abandonTurn(): void;
|
|
3369
|
+
/**
|
|
3370
|
+
* Free the conversation from a turn that did not end when stopped — a hung
|
|
3371
|
+
* wake-up (ADR-0029). Unlike {@link abandonTurn} the plan stays, so the turn
|
|
3372
|
+
* is forgotten as its failure would have been: nothing it held lands, and
|
|
3373
|
+
* what it would have told the planner is told to the next turn.
|
|
3374
|
+
*/
|
|
3375
|
+
abandonHungTurn(): void;
|
|
3376
|
+
/**
|
|
3377
|
+
* Let the live turn go. A user turn's end is announced now, and it says
|
|
3378
|
+
* nothing after — not its stream, not its held edit, not its end again —
|
|
3379
|
+
* since it may settle at any time later: the API services restart nothing,
|
|
3380
|
+
* and a hung call settles whenever it settles, while the next turn runs.
|
|
3381
|
+
*/
|
|
3382
|
+
private drop;
|
|
3383
|
+
/**
|
|
3384
|
+
* Run planner work that is not a conversation turn — one-shot plan
|
|
3385
|
+
* generation — as the planner turn, so it is stopped, refused and superseded
|
|
3386
|
+
* like one. It starts a fresh plan, so it supersedes the turn in flight.
|
|
3387
|
+
*/
|
|
3388
|
+
hold<T>(signal: AbortSignal | undefined, body: (turn: PlannerTurn) => Promise<T>): Promise<T>;
|
|
3389
|
+
/**
|
|
3390
|
+
* Take the conversation for a new planner turn. A caller's own signal stops
|
|
3391
|
+
* the turn too; the relay is detached on release because a caller may reuse
|
|
3392
|
+
* that signal, and a stale listener would stop the next turn with it.
|
|
3393
|
+
*/
|
|
3394
|
+
private claim;
|
|
3395
|
+
/** The user turn being answered, for what the host raises during it — an approval the turn's research asks for. */
|
|
3396
|
+
get currentTurnId(): string | undefined;
|
|
3397
|
+
/** The open turn's wake-up rules, when the supervisor is answering it rather than the user's message. */
|
|
3398
|
+
get currentWakeUp(): WakeUpTurn | undefined;
|
|
3399
|
+
/** A skill the planner loaded in the open turn, held with the turn; false when none is open to take it. */
|
|
3400
|
+
recordPlannerSkill(skill: SkillLoad): boolean;
|
|
3401
|
+
/**
|
|
3402
|
+
* Hand the open turn a plan (already validated) or a task edit. The turn
|
|
3403
|
+
* settles with it exactly as with the same plan or ops parsed out of its
|
|
3404
|
+
* reply, and it wins over any envelope that reply carries. A later
|
|
3405
|
+
* submission replaces an earlier one. False when no user turn is open.
|
|
3406
|
+
*/
|
|
3407
|
+
submit(submission: PlannerSubmission): boolean;
|
|
3408
|
+
/**
|
|
3409
|
+
* Tell surfaces what the turn's edit would leave, from the same applier that
|
|
3410
|
+
* lands it, so an edit that would not apply shows nothing. It stays shown
|
|
3411
|
+
* from the moment it is handed in until it lands, is refused, or the turn
|
|
3412
|
+
* ends: the backend's reply claims it, but it has not landed by then.
|
|
3413
|
+
*/
|
|
3414
|
+
private showHeld;
|
|
3415
|
+
private clearHeld;
|
|
3416
|
+
/**
|
|
3417
|
+
* The edit the open turn already holds, for a further edit in the same reply
|
|
3418
|
+
* to join rather than replace. Empty when it holds nothing; null when it holds
|
|
3419
|
+
* a whole plan, which an edit cannot be layered on.
|
|
3420
|
+
*/
|
|
3421
|
+
pendingOps(): TaskOp[] | null;
|
|
3422
|
+
/**
|
|
3423
|
+
* A read the planner makes through a tool. It spends the same per-turn budget
|
|
3424
|
+
* as an envelope read, so a planner cannot read more by switching channels:
|
|
3425
|
+
* past the soft limit, or on a repeated question, the answer still comes but
|
|
3426
|
+
* is told to land the turn; at the hard limit it is refused. `signature`
|
|
3427
|
+
* names the question, for the repeat check.
|
|
3428
|
+
*/
|
|
3429
|
+
read<T>(signature: string, answer: () => Promise<T>): Promise<ToolRead<T>>;
|
|
3430
|
+
/** Whether the model still holds this conversation in memory. */
|
|
3431
|
+
get isActive(): boolean;
|
|
3432
|
+
/** Call inside the host's mutation ritual. */
|
|
3433
|
+
append(role: ConversationMessage['role'], content: string, opts?: {
|
|
3434
|
+
timestamp?: string;
|
|
3435
|
+
kind?: ConversationMessage['kind'];
|
|
3436
|
+
runs?: ReplyRun[];
|
|
3437
|
+
actor?: ConversationMessage['actor'];
|
|
3438
|
+
}): void;
|
|
3439
|
+
private appendEntries;
|
|
3440
|
+
/** Swap the whole transcript. The live context no longer matches it, so pair with {@link reset}. */
|
|
3441
|
+
replace(messages: readonly ConversationMessage[]): void;
|
|
3442
|
+
/**
|
|
3443
|
+
* The plan scope this conversation served is gone: drop the live model
|
|
3444
|
+
* context, and what the planner was told outside the transcript in it (see
|
|
3445
|
+
* {@link PlannerBriefing}). Idempotent; the harness backend holds an OS
|
|
3446
|
+
* process here, so callers never gate it on {@link isActive}.
|
|
3447
|
+
*/
|
|
3448
|
+
reset(): void;
|
|
3449
|
+
/** A run update for the planner's next turn, whoever opens it (ADR-0029). */
|
|
3450
|
+
tellRunUpdate(text: string): void;
|
|
3451
|
+
/** Drop the live model context alone: the next turn replays from the transcript. */
|
|
3452
|
+
private dropContext;
|
|
3453
|
+
/**
|
|
3454
|
+
* The user messages a rewind may land before. The opening message is the
|
|
3455
|
+
* goal: cutting it leaves a conversation about nothing, which is a new
|
|
3456
|
+
* session, not a rewind. After a compaction the summary entry, always
|
|
3457
|
+
* first, plays the goal's part: what it replaced is gone, so a rewind stops
|
|
3458
|
+
* at it.
|
|
3459
|
+
*/
|
|
3460
|
+
rewindTargets(): RewindTarget[];
|
|
3461
|
+
/**
|
|
3462
|
+
* Replace the transcript with a summary of it, keeping the last two
|
|
3463
|
+
* exchanges as they were, and drop the live context so the next message
|
|
3464
|
+
* replays from the shorter record.
|
|
3465
|
+
*
|
|
3466
|
+
* The summary is one hidden turn through whichever planner is configured —
|
|
3467
|
+
* on the live context when it still matches, replayed from the transcript
|
|
3468
|
+
* when not — so the compaction is the same for a vendor API and a harness
|
|
3469
|
+
* agent. Whatever the turn emits besides the summary is discarded, ops
|
|
3470
|
+
* included: it condenses the conversation, never the plan. Nothing is
|
|
3471
|
+
* written until the summary is in hand, so a failed or stopped turn leaves
|
|
3472
|
+
* the transcript exactly as it was.
|
|
3473
|
+
*/
|
|
3474
|
+
compact(signal?: AbortSignal): Promise<ConversationCompaction>;
|
|
3475
|
+
private summarize;
|
|
3476
|
+
/**
|
|
3477
|
+
* A copy of the dialogue record for a forked session to carry. Refused
|
|
3478
|
+
* mid-turn: the copy would hold the user's message without the reply to it.
|
|
3479
|
+
* The live context is not part of it — the fork replays from this record on
|
|
3480
|
+
* its first turn, like any adopted session.
|
|
3481
|
+
*/
|
|
3482
|
+
clone(): ForkedDialogue;
|
|
3483
|
+
/**
|
|
3484
|
+
* A copy of the dialogue as it stood just before the user message at
|
|
3485
|
+
* `index` (a position from {@link rewindTargets}) — what a rewind forks
|
|
3486
|
+
* from — plus that message's full text, for a surface to offer back. This
|
|
3487
|
+
* conversation is not touched. The research trace is cut at the same point
|
|
3488
|
+
* by time: its entries carry no link to the message that caused them.
|
|
3489
|
+
*/
|
|
3490
|
+
cloneBefore(index: number): RewoundDialogue;
|
|
3491
|
+
/**
|
|
3492
|
+
* What the skill check of a landed plan or edit warned about, kept in the
|
|
3493
|
+
* transcript so every surface shows it, on reload too, and not only the
|
|
3494
|
+
* planner that may have read it in a tool result. Call inside the host's
|
|
3495
|
+
* mutation ritual, so the broadcast plan carries it.
|
|
3496
|
+
*/
|
|
3497
|
+
recordSkillWarnings(warnings: readonly string[], timestamp?: string): void;
|
|
3498
|
+
/**
|
|
3499
|
+
* A call at the command seam (ADR-0029), kept where it happened in the
|
|
3500
|
+
* dialogue: a reload shows it, and a planner replayed from the transcript
|
|
3501
|
+
* knows what was started or stopped, and by whom. Call inside the host's
|
|
3502
|
+
* mutation ritual.
|
|
3503
|
+
*/
|
|
3504
|
+
recordExecution(record: ExecutionRecord, text: string): void;
|
|
3505
|
+
/**
|
|
3506
|
+
* Open the conversation on the host's fresh plan: the goal is its first
|
|
3507
|
+
* message. `prepare` (discovery) runs inside the turn, so a stop reaches it.
|
|
3508
|
+
*/
|
|
3509
|
+
start(goal: SkillInvocation, prepare: () => Promise<ConversationOpening>, signal?: AbortSignal): Promise<LegacyPlanState>;
|
|
3510
|
+
/**
|
|
3511
|
+
* Every later user message, to a persisted outcome. A turn that throws lands
|
|
3512
|
+
* nothing: what it held is dropped with it, so session memory never drifts
|
|
3513
|
+
* from disk and the UI.
|
|
3514
|
+
*/
|
|
3515
|
+
reply(message: string | SkillInvocation, options?: ReplyOptions): Promise<LegacyPlanState>;
|
|
3516
|
+
private replyTurn;
|
|
3517
|
+
/**
|
|
3518
|
+
* A wake-up turn lands only a real answer. Stopped — by the user, or by its
|
|
3519
|
+
* timeout — it lands nothing, not the fragment a backend hands back; a
|
|
3520
|
+
* failure is thrown rather than shown, for the supervisor to retry
|
|
3521
|
+
* (ADR-0029).
|
|
3522
|
+
*/
|
|
3523
|
+
private assertWakeUpAnswered;
|
|
3524
|
+
/**
|
|
3525
|
+
* A turn that ended on the planner's usage limit lands nothing: what it held
|
|
3526
|
+
* is dropped, its failure is kept in its place, and the limit's wait is armed
|
|
3527
|
+
* in the same save. Only a turn Ordewell started gets here, so a limit is
|
|
3528
|
+
* never acted on for a planner nobody asked anything of.
|
|
3529
|
+
*/
|
|
3530
|
+
private assertNotLimited;
|
|
3531
|
+
/**
|
|
3532
|
+
* A failure kept in the transcript, drawn as one on reload too, and never
|
|
3533
|
+
* replayed to the planner. Stamped after the newest entry: surfaces take
|
|
3534
|
+
* only entries newer than the last they synced.
|
|
3535
|
+
*/
|
|
3536
|
+
private recordFailure;
|
|
3537
|
+
private assertIdle;
|
|
3538
|
+
/** Refuse to land a turn that no longer belongs here: it was abandoned, or its plan is no longer the host's. */
|
|
3539
|
+
private assertCurrent;
|
|
3540
|
+
/**
|
|
3541
|
+
* {@link assertCurrent} on the far side of an await that precedes a plan
|
|
3542
|
+
* mutation: the skill check waits on git, and a stop, rewind or session
|
|
3543
|
+
* switch in that window drops the edit instead of landing it on whatever
|
|
3544
|
+
* plan is current by then. A stop counts here because nothing has landed
|
|
3545
|
+
* yet — the user asked for the turn to end before it changed the plan.
|
|
3546
|
+
*/
|
|
3547
|
+
private assertStillCurrent;
|
|
3548
|
+
/**
|
|
3549
|
+
* A live conversation is only safe to continue in-place when it also
|
|
3550
|
+
* matches the model/effort configured right now — a harness planner's
|
|
3551
|
+
* running process was spawned with the old one baked in and cannot pick up
|
|
3552
|
+
* a switch (ADR-0009). Otherwise the turn resumes from the transcript.
|
|
3553
|
+
*/
|
|
3554
|
+
private canContinueLive;
|
|
3555
|
+
/**
|
|
3556
|
+
* Bracket one user turn with its start and end, under an id minted here:
|
|
3557
|
+
* the turn is where the stream a surface draws begins and ends, and only the
|
|
3558
|
+
* conversation sees all of it — every backend call, read and retry.
|
|
3559
|
+
*/
|
|
3560
|
+
private userTurn;
|
|
3561
|
+
private requirePlan;
|
|
3562
|
+
/** The message as typed, then one entry per skill it loaded, sharing its timestamp so a reload keeps them together. */
|
|
3563
|
+
private userEntries;
|
|
3564
|
+
private userPrompt;
|
|
3565
|
+
/** Hold what the turn writes until it lands, each entry where the transcript stands now; see {@link TurnWrites}. */
|
|
3566
|
+
private holdWrites;
|
|
3567
|
+
/**
|
|
3568
|
+
* The one mutation that lands a turn's outcome (ADR-0029). What the turn held
|
|
3569
|
+
* goes in first — each entry where it belongs, ahead of what was written
|
|
3570
|
+
* after it, then the research and the subagents — so the transcript reads in
|
|
3571
|
+
* the order it happened and the inputs leave the plan's pending turns in the
|
|
3572
|
+
* save that carried their answer. `outcome` makes the turn's change to the
|
|
3573
|
+
* plan and returns what the turn said, which is written, then sent in that
|
|
3574
|
+
* order; `announce` follows it on every surface. The turn's briefing is told
|
|
3575
|
+
* unless the planner gave no answer (`unheard`): a failure, or a stop.
|
|
3576
|
+
*/
|
|
3577
|
+
private land;
|
|
3578
|
+
private writeHeld;
|
|
3579
|
+
private recordResearch;
|
|
3580
|
+
/**
|
|
3581
|
+
* The opening a replay starts under, read fresh, the live context dropped
|
|
3582
|
+
* first; what it states of autorun and the runners is what the planner is
|
|
3583
|
+
* told by it. A turn abandoned while the opening was read stops here: the
|
|
3584
|
+
* backend may be its successor's by now.
|
|
3585
|
+
*/
|
|
3586
|
+
private reopen;
|
|
3587
|
+
/**
|
|
3588
|
+
* Reopen the model context from the transcript — because the in-memory one
|
|
3589
|
+
* is gone (session reload, extension restart), or because it is stale
|
|
3590
|
+
* against the planner config now in effect. No LLM call happens for the
|
|
3591
|
+
* replayed turns; the first call is the one the user's message opens.
|
|
3592
|
+
*/
|
|
3593
|
+
private resume;
|
|
3594
|
+
/**
|
|
3595
|
+
* Answer every read the planner emits until it says something else.
|
|
3596
|
+
*
|
|
3597
|
+
* The channel is a text envelope rather than a registered tool because the
|
|
3598
|
+
* protocol has to be identical on both planner backends (ADR-0009): Ordewell
|
|
3599
|
+
* owns a tool loop only in the API case, and a harness planner running as a
|
|
3600
|
+
* coding-agent subprocess can only be reached this way.
|
|
3601
|
+
*
|
|
3602
|
+
* Draining here — outside {@link repairLoop} — is what keeps reads free of
|
|
3603
|
+
* the repair budget. A planner that looks a task up and *then* fumbles its
|
|
3604
|
+
* ops JSON still gets its two corrective retries; charging it for the read
|
|
3605
|
+
* would cost it the chance to fix the edit.
|
|
3606
|
+
*/
|
|
3607
|
+
private drainTaskQueries;
|
|
3608
|
+
/**
|
|
3609
|
+
* A call cut off by something other than the turn's own stop failed: what it
|
|
3610
|
+
* hands back is a fragment of an answer, and the turn keeps what it was sent,
|
|
3611
|
+
* as any failure does. Nothing of Ordewell's resets a backend under a turn on
|
|
3612
|
+
* the same plan (see `PlannerBackend`); one that changes the plan abandons the turn.
|
|
3613
|
+
*/
|
|
3614
|
+
private failedIfCutOff;
|
|
3615
|
+
/**
|
|
3616
|
+
* The backend's answer, with what was handed in while it ran standing in
|
|
3617
|
+
* for whatever its reply carried. Taken once: a corrective re-send after a
|
|
3618
|
+
* rejected edit must not settle on the same submission again. A stopped
|
|
3619
|
+
* turn commits nothing, the same as a stopped reply with a plan in it.
|
|
3620
|
+
*/
|
|
3621
|
+
private claimSubmission;
|
|
3622
|
+
/**
|
|
3623
|
+
* Render one read out of live state. Never persisted to the transcript: the
|
|
3624
|
+
* detail is context for the planner's next reply, and re-sending it on every
|
|
3625
|
+
* later turn is exactly the cost this channel exists to avoid.
|
|
3626
|
+
*/
|
|
3627
|
+
private taskQueryAnswer;
|
|
3628
|
+
/**
|
|
3629
|
+
* Drive a planner turn, its reads already drained, to a persisted, broadcast
|
|
3630
|
+
* outcome. Task edits apply atomically; validation failures are fed back to
|
|
3631
|
+
* the model for up to 2 silent retries, then surfaced as a message with the
|
|
3632
|
+
* plan untouched. The first turn and every later turn route through here —
|
|
3633
|
+
* one path, not two.
|
|
3634
|
+
*/
|
|
3635
|
+
private settle;
|
|
3636
|
+
/** A name not found at submit only warns: a task the attaching one depends on may create it. */
|
|
3637
|
+
private warnSkills;
|
|
3638
|
+
/** Validate + commit a task_ops turn atomically. Returns the errors on rejection (plan untouched). */
|
|
3639
|
+
private applyTaskOps;
|
|
3640
|
+
/** The edit was refused as it landed: what it showed is not coming. */
|
|
3641
|
+
private refusedHeld;
|
|
3642
|
+
/** Commit a settled (non-task_ops) turn: a whole plan, or what it said. */
|
|
3643
|
+
private commit;
|
|
3644
|
+
/**
|
|
3645
|
+
* The runs a reply is saved and sent with (ADR-0017 A1): the narration the
|
|
3646
|
+
* turn kept from calls it did not settle on, then the reply's own. None from
|
|
3647
|
+
* a backend that reports no runs, whose reply then stands as one message.
|
|
3648
|
+
*/
|
|
3649
|
+
private shown;
|
|
3650
|
+
/** What a plan or edit turn said beside what it landed: its reply, or an envelope's narration. */
|
|
3651
|
+
private said;
|
|
3652
|
+
/**
|
|
3653
|
+
* The catalog the planner may actually draw from — model ids and task-mode
|
|
3654
|
+
* ids, per runner in the plan — emitted on EVERY turn, before any plan exists
|
|
3655
|
+
* or after. The system prompt shows this once at conversation start; a long
|
|
3656
|
+
* clarifying conversation outlives that single showing and the planner
|
|
3657
|
+
* starts misquoting it, so this re-states it per turn instead.
|
|
3658
|
+
*
|
|
3659
|
+
* The host's catalog is allowlist-filtered, so a restricted allowlist stays a
|
|
3660
|
+
* hard bound on every turn, and reads the plan's runners live, so a runner
|
|
3661
|
+
* admitted mid-session by a retarget is shown like every other. A planner
|
|
3662
|
+
* with Ordewell's tools gets a reminder to read the catalog instead.
|
|
3663
|
+
*/
|
|
3664
|
+
private catalogBlock;
|
|
3665
|
+
/**
|
|
3666
|
+
* The "you are here" block for post-plan chat: current tasks with stable
|
|
3667
|
+
* references, plus the read and edit protocols. Injected per turn (never
|
|
3668
|
+
* persisted) so the model always sees live statuses — including which tasks
|
|
3669
|
+
* are locked by a running execution.
|
|
3670
|
+
*/
|
|
3671
|
+
private planContextBlock;
|
|
3672
|
+
/** One line per task, subtasks indented under their parent, with stable references — null until the plan has tasks. */
|
|
3673
|
+
private currentPlanLines;
|
|
3674
|
+
}
|
|
3675
|
+
|
|
3676
|
+
/**
|
|
3677
|
+
* Task references: the one grammar for naming a task in a plan, both ways.
|
|
3678
|
+
* A label is rendered from a task's order under its parent ("2", "2.1"), and
|
|
3679
|
+
* a reference written by anyone — the planner, a tool call, a command — is
|
|
3680
|
+
* resolved back against the plan tree. Pure so a
|
|
3681
|
+
* browser-bundled surface can use it through `@ordewell/core/plan-utils`.
|
|
3682
|
+
*/
|
|
3683
|
+
/** The shape a reference resolves over: the plan's task tree, or a surface's projection of it. */
|
|
3684
|
+
type RefTask<T> = TreeTask<T>;
|
|
3685
|
+
/**
|
|
3686
|
+
* The parent a label extends. A label is the full path ("3.2.1"), so a parent
|
|
3687
|
+
* below the top level carries the label it was itself reached by; without one
|
|
3688
|
+
* the parent's order stands for it, which is right for a top-level parent.
|
|
3689
|
+
*/
|
|
3690
|
+
interface RefParent {
|
|
3691
|
+
order: number;
|
|
3692
|
+
label?: string;
|
|
3693
|
+
}
|
|
3694
|
+
/** Dotted path label for a task, e.g. "2.1" for the first subtask of task 2, "2.1.3" one level down. */
|
|
3695
|
+
declare function taskOrderLabel(task: {
|
|
3696
|
+
order: number;
|
|
3697
|
+
}, parent?: RefParent): string;
|
|
3698
|
+
/** How a surface refers to a task in prose: "#2", or "#2.1" for a subtask. */
|
|
3699
|
+
declare function taskRef(task: {
|
|
3700
|
+
order: number;
|
|
3701
|
+
}, parent?: RefParent): string;
|
|
3702
|
+
/** A task named in full, as a confirmation or a picker title does: "#2 Deploy". */
|
|
3703
|
+
declare function titledTaskRef(task: {
|
|
3704
|
+
order: number;
|
|
3705
|
+
title: string;
|
|
3706
|
+
}, parent?: RefParent): string;
|
|
3707
|
+
/**
|
|
3708
|
+
* The reference {@link taskRef} renders for the task a plan tree holds, by id
|
|
3709
|
+
* — its full path, dotted, for a subtask. Undefined when the tree holds no such task.
|
|
3710
|
+
*/
|
|
3711
|
+
declare function taskRefOf<T extends RefTask<T>>(tasks: readonly T[], id: string): string | undefined;
|
|
3712
|
+
/**
|
|
3713
|
+
* The reference and title {@link titledTaskRef} renders for the task a plan
|
|
3714
|
+
* tree holds, by id — its full path, dotted, for a subtask. Undefined when the
|
|
3715
|
+
* tree holds no such task.
|
|
3716
|
+
*/
|
|
3717
|
+
declare function titledTaskRefOf<T extends RefTask<T>>(tasks: readonly T[], id: string): string | undefined;
|
|
3718
|
+
/**
|
|
3719
|
+
* Resolve a written reference to a task at any depth: an id, then an order
|
|
3720
|
+
* label with or without "#" ("#2", "2", "#2.1"), then an exact title.
|
|
3721
|
+
*
|
|
3722
|
+
* An order label is read against the tree, never a flattened list: subtask
|
|
3723
|
+
* orders restart at 1 under each parent, so "#2" always means top-level task 2
|
|
3724
|
+
* and a subtask is reachable only by its dotted path ("#2.1", "#2.1.3").
|
|
3725
|
+
*/
|
|
3726
|
+
declare function resolveTaskRef<T extends RefTask<T>>(tasks: readonly T[], ref: string | number): T | undefined;
|
|
3727
|
+
/**
|
|
3728
|
+
* A reference as the user types one: {@link resolveTaskRef}, then a unique
|
|
3729
|
+
* prefix of a task's id at any depth. Only the user's: a model's reference
|
|
3730
|
+
* that names no task is refused, never matched to the one task a fragment of
|
|
3731
|
+
* it happens to fit.
|
|
3732
|
+
*/
|
|
3733
|
+
declare function resolveUserTaskRef<T extends RefTask<T>>(tasks: readonly T[], ref: string | number): T | undefined;
|
|
3734
|
+
|
|
3735
|
+
interface SessionMeta {
|
|
3736
|
+
id: string;
|
|
3737
|
+
goal: string;
|
|
3738
|
+
runners: RunnerId[];
|
|
3739
|
+
taskCount: number;
|
|
3740
|
+
status: PlanStatus;
|
|
3741
|
+
createdAt: string;
|
|
3742
|
+
updatedAt: string;
|
|
3743
|
+
}
|
|
3744
|
+
interface SessionData {
|
|
3745
|
+
meta: SessionMeta;
|
|
3746
|
+
/** The full plan state — including conversationHistory, prdMarkdown, and queuedMessages. */
|
|
3747
|
+
plan: LegacyPlanState;
|
|
3748
|
+
}
|
|
3749
|
+
|
|
3750
|
+
/**
|
|
3751
|
+
* The bridge between "core needs an answer" and "a human somewhere is looking
|
|
3752
|
+
* at a UI". Core cannot prompt: the human may be at a browser, a TUI, a CLI
|
|
3753
|
+
* stream, or a VS Code webview, and on the web server they are on the far end
|
|
3754
|
+
* of a socket. So the Session parks a promise here, announces the request
|
|
3755
|
+
* through the normal broadcast seam, and every surface answers through the
|
|
3756
|
+
* same `resolve(id, granted)` call.
|
|
3757
|
+
*
|
|
3758
|
+
* Timeouts are load-bearing rather than defensive: a planner turn that blocks
|
|
3759
|
+
* forever on an unanswered prompt would hang the whole research loop with no
|
|
3760
|
+
* visible cause. On expiry the request resolves to denied and the model gets a
|
|
3761
|
+
* normal, actionable tool result. A task runner's request is the exception
|
|
3762
|
+
* (ADR-0018, A1): the runner is parked on it rather than a research loop, and
|
|
3763
|
+
* it waits for a person — or the supervisor — however long that takes.
|
|
3764
|
+
*/
|
|
3765
|
+
interface PendingApproval {
|
|
3766
|
+
id: string;
|
|
3767
|
+
request: ApprovalRequest;
|
|
3768
|
+
createdAt: string;
|
|
3769
|
+
}
|
|
3770
|
+
interface PendingApprovalsOptions {
|
|
3771
|
+
/** Denies and resolves after this long with no answer. Default 5 minutes. */
|
|
3772
|
+
timeoutMs?: number;
|
|
3773
|
+
/** Announce a new request to the surfaces. */
|
|
3774
|
+
onRequest?: (pending: PendingApproval) => void;
|
|
3775
|
+
/** Announce that a request is no longer actionable (answered or expired). */
|
|
3776
|
+
onSettled?: (id: string, granted: boolean, settled: {
|
|
3777
|
+
request: ApprovalRequest;
|
|
3778
|
+
decision: ApprovalDecision;
|
|
3779
|
+
}) => void;
|
|
3780
|
+
}
|
|
3781
|
+
interface AskOptions {
|
|
3782
|
+
/**
|
|
3783
|
+
* An id the caller already announced the request under. One still in use is
|
|
3784
|
+
* refused — denied at once — rather than overwriting the request that has it.
|
|
3785
|
+
*/
|
|
3786
|
+
id?: string;
|
|
3787
|
+
/** Wait for an answer however long it takes. */
|
|
3788
|
+
noTimeout?: boolean;
|
|
3789
|
+
/**
|
|
3790
|
+
* Given the answer as the request settles, before anything awaiting the
|
|
3791
|
+
* promise runs: a caller about to tear down what asked (a runner being
|
|
3792
|
+
* stopped) must deliver the answer before it goes.
|
|
3793
|
+
*/
|
|
3794
|
+
onDecision?: (decision: ApprovalDecision) => void;
|
|
3795
|
+
}
|
|
3796
|
+
declare class PendingApprovals {
|
|
3797
|
+
private readonly opts;
|
|
3798
|
+
private readonly entries;
|
|
3799
|
+
private readonly listeners;
|
|
3800
|
+
constructor(opts?: PendingApprovalsOptions);
|
|
3801
|
+
/** Hear each new request once it is announced — the supervisor wakes on a runner's (ADR-0029). */
|
|
3802
|
+
listen(listener: (pending: PendingApproval) => void): () => void;
|
|
3803
|
+
/** Park a request and return the promise the approval policy awaits. */
|
|
3804
|
+
ask(request: ApprovalRequest, options?: AskOptions): Promise<boolean>;
|
|
3805
|
+
/** Park a request whose answer is more than yes or no. */
|
|
3806
|
+
decide(request: ApprovalRequest, options?: AskOptions): Promise<ApprovalDecision>;
|
|
3807
|
+
/**
|
|
3808
|
+
* Answer one request. Returns false when the id is unknown or already
|
|
3809
|
+
* settled. "Allow for this task" on a request that did not offer it is a
|
|
3810
|
+
* plain allow: no grant is made that the requester never proposed.
|
|
3811
|
+
*/
|
|
3812
|
+
resolve(id: string, answer: ApprovalAnswer): boolean;
|
|
3813
|
+
/** Everything still awaiting an answer — replayed to a surface that connects late. */
|
|
3814
|
+
outstanding(): PendingApproval[];
|
|
3815
|
+
/**
|
|
3816
|
+
* Deny everything in flight, or only the requests `which` picks. Called on
|
|
3817
|
+
* abort and on session reset.
|
|
3818
|
+
*/
|
|
3819
|
+
clear(which?: (request: ApprovalRequest) => boolean, note?: string): void;
|
|
3820
|
+
}
|
|
3821
|
+
|
|
3822
|
+
/**
|
|
3823
|
+
* Pure, no-imports helper so a browser surface can format a conflict's files
|
|
3824
|
+
* without pulling `isolationRecord` — and its `path` import — into a webview
|
|
3825
|
+
* bundle. Kept out of `isolationRecord.ts` for exactly that reason.
|
|
3826
|
+
*/
|
|
3827
|
+
declare function capConflictFiles(files: string[], max?: number): string;
|
|
3828
|
+
|
|
3829
|
+
/** How a run is started, watched and merged: the user's, not a plan's (ADR-0030). */
|
|
3830
|
+
interface GlobalRunSettings {
|
|
3831
|
+
supervisorLevel: SupervisorLevel;
|
|
3832
|
+
mergeAtGatesAutomatically: boolean;
|
|
3833
|
+
/** Start a plan when the planner submits it, approved as if the user had pressed Execute. */
|
|
3834
|
+
autorun: boolean;
|
|
3835
|
+
/** Retries an Act supervisor may make of one task in a run (a loop guard). */
|
|
3836
|
+
supervisorRetryLimit: number;
|
|
3837
|
+
/** Reads a wake-up turn may make, in place of a user message's caps. */
|
|
3838
|
+
wakeUpReadBudget: number;
|
|
3839
|
+
}
|
|
3840
|
+
declare const RUN_SETTINGS_DEFAULTS: Readonly<GlobalRunSettings>;
|
|
3841
|
+
declare class RunSettingsError extends Error {
|
|
3842
|
+
constructor(message: string);
|
|
3843
|
+
}
|
|
3844
|
+
interface UserSettings {
|
|
3845
|
+
modelAllowlist?: Record<string, string[]>;
|
|
3846
|
+
/** Last model (and its thinking effort) the user chose for each planner backend, keyed by planner backend id. */
|
|
3847
|
+
plannerModels?: Record<string, {
|
|
3848
|
+
model: string;
|
|
3849
|
+
effort?: string;
|
|
3850
|
+
}>;
|
|
3851
|
+
/**
|
|
3852
|
+
* Runners the user picked for planning. Absent — not empty — is what falls
|
|
3853
|
+
* back to the environment's defaults; `[]` is a deliberate "none of them".
|
|
3854
|
+
*/
|
|
3855
|
+
enabledRunners?: string[];
|
|
3856
|
+
/** Full (true) or Guarded (false), ADR-0001; Full until the user chooses. */
|
|
3857
|
+
autonomousMode?: boolean;
|
|
3858
|
+
supervisorLevel?: SupervisorLevel;
|
|
3859
|
+
mergeAtGatesAutomatically?: boolean;
|
|
3860
|
+
autorun?: boolean;
|
|
3861
|
+
supervisorRetryLimit?: number;
|
|
3862
|
+
wakeUpReadBudget?: number;
|
|
3863
|
+
}
|
|
3864
|
+
/**
|
|
3865
|
+
* The model allowlist, remembered planner models and enabled runners are shared
|
|
3866
|
+
* across processes by default. `ORDEWELL_SETTINGS_PATH` lets independent runs
|
|
3867
|
+
* use separate files: otherwise a write in one run would change another's
|
|
3868
|
+
* settings when `getAll()` detects the new mtime.
|
|
3869
|
+
*/
|
|
3870
|
+
declare function getSettingsPath(): string;
|
|
3871
|
+
/** The run settings `settings` holds, each one it leaves out at its default. */
|
|
3872
|
+
declare function runSettingsOf(settings: UserSettings): GlobalRunSettings;
|
|
3873
|
+
declare class SettingsService {
|
|
3874
|
+
private filePath;
|
|
3875
|
+
private cache;
|
|
3876
|
+
private cachedStamp;
|
|
3877
|
+
constructor(filePath?: string);
|
|
3878
|
+
getAll(): UserSettings;
|
|
3879
|
+
/**
|
|
3880
|
+
* Every write replaces the file, so its inode changes with it; the mtime
|
|
3881
|
+
* alone cannot tell two writes apart on a filesystem whose timestamps are
|
|
3882
|
+
* coarse, and a write that shares the last one's tick stayed unseen.
|
|
3883
|
+
*/
|
|
3884
|
+
private fileStamp;
|
|
3885
|
+
/**
|
|
3886
|
+
* What a change is applied to: the file as it is now, never a copy held since
|
|
3887
|
+
* an earlier read. Another process may have saved in between, and writing
|
|
3888
|
+
* the whole file back would silently undo it.
|
|
3889
|
+
*/
|
|
3890
|
+
private current;
|
|
3891
|
+
/**
|
|
3892
|
+
* Calls `listener` when another process (or this one) saves the file. The
|
|
3893
|
+
* directory is watched, not the file: a write that swaps the file in leaves
|
|
3894
|
+
* a watch on the old one pointing at nothing.
|
|
3895
|
+
*/
|
|
3896
|
+
watch(listener: () => void): {
|
|
3897
|
+
dispose(): void;
|
|
3898
|
+
};
|
|
3899
|
+
getModelAllowlist(runner: string): string[] | undefined;
|
|
3900
|
+
setModelAllowlist(runner: string, ids: string[] | undefined): void;
|
|
3901
|
+
getPlannerModel(provider: string): {
|
|
3902
|
+
model: string;
|
|
3903
|
+
effort?: string;
|
|
3904
|
+
} | undefined;
|
|
3905
|
+
setPlannerModel(provider: string, entry: {
|
|
3906
|
+
model: string;
|
|
3907
|
+
effort?: string;
|
|
3908
|
+
} | undefined): void;
|
|
3909
|
+
getEnabledRunners(): string[] | undefined;
|
|
3910
|
+
setEnabledRunners(ids: string[]): void;
|
|
3911
|
+
/**
|
|
3912
|
+
* Change the list from what the file holds at this moment: the change is
|
|
3913
|
+
* handed the saved list (undefined when the user never chose) and its result
|
|
3914
|
+
* is saved in the same call. A list worked out from an earlier read would
|
|
3915
|
+
* save over what another surface set since. It may be handed the list again,
|
|
3916
|
+
* when another process saved before this one could (see {@link save}).
|
|
3917
|
+
*/
|
|
3918
|
+
updateEnabledRunners(change: (saved: string[] | undefined) => string[]): string[];
|
|
3919
|
+
/** Save `ids` as the user's choice unless one is saved already, judged on the file as it is now. */
|
|
3920
|
+
setEnabledRunnersIfUnset(ids: string[]): boolean;
|
|
3921
|
+
/**
|
|
3922
|
+
* Full or Guarded (ADR-0001), as saved here (ADR-0030). A value set in the
|
|
3923
|
+
* environment wins, so a script or CI run can fix its own without touching
|
|
3924
|
+
* the user's file.
|
|
3925
|
+
*/
|
|
3926
|
+
autonomousMode(): boolean;
|
|
3927
|
+
/** Whether `ORDEWELL_AUTONOMOUS_MODE` decides the level, whatever this file says. */
|
|
3928
|
+
autonomousModeOverridden(): boolean;
|
|
3929
|
+
setAutonomousMode(enabled: boolean): void;
|
|
3930
|
+
getRunSettings(): GlobalRunSettings;
|
|
3931
|
+
/** Checks every value before writing any, so a refused change saves nothing. */
|
|
3932
|
+
updateRunSettings(changes: Partial<GlobalRunSettings>): GlobalRunSettings;
|
|
3933
|
+
private load;
|
|
3934
|
+
/**
|
|
3935
|
+
* Makes `change` to the file as it is now, and saves it. Another process can
|
|
3936
|
+
* save between that read and this write; the file is looked at again just
|
|
3937
|
+
* before ours replaces it, and when it moved the change is made afresh from
|
|
3938
|
+
* what that process saved, so neither save undoes the other. A file that
|
|
3939
|
+
* keeps moving is written over after {@link SAVE_TRIES} tries rather than
|
|
3940
|
+
* never. The look and the swap are one step under the file's lock
|
|
3941
|
+
* ({@link withFileLock}). A change that changes nothing writes nothing.
|
|
3942
|
+
*/
|
|
3943
|
+
private save;
|
|
3944
|
+
/** False, saving nothing, when the file is no longer the one stamped `expected`. */
|
|
3945
|
+
private persist;
|
|
3946
|
+
}
|
|
3947
|
+
|
|
3948
|
+
/** What the preflight reads: the planner choice and whatever it needs to be reachable. */
|
|
3949
|
+
type PlannerReadinessConfig = Pick<IConfig, 'aiProvider' | 'planningModel' | 'openaiCompatibleBaseUrl' | 'getProviderApiKey'>;
|
|
3950
|
+
/**
|
|
3951
|
+
* Why the selected planner cannot plan yet, as data. Each surface words it with
|
|
3952
|
+
* its own command spelling (`describePlannerNotReady`), so a daemon, a chat and
|
|
3953
|
+
* a shell script can all say the same thing without sharing a syntax.
|
|
3954
|
+
*/
|
|
3955
|
+
type PlannerNotReady = {
|
|
3956
|
+
code: 'no_key';
|
|
3957
|
+
provider: AiProvider;
|
|
3958
|
+
label: string;
|
|
3959
|
+
keyEnvVar: string;
|
|
3960
|
+
} | {
|
|
3961
|
+
code: 'no_base_url';
|
|
3962
|
+
provider: AiProvider;
|
|
3963
|
+
label: string;
|
|
3964
|
+
baseUrlEnvVar: string;
|
|
3965
|
+
} | {
|
|
3966
|
+
code: 'no_model';
|
|
3967
|
+
provider: AiProvider;
|
|
3968
|
+
label: string;
|
|
3969
|
+
};
|
|
3970
|
+
/**
|
|
3971
|
+
* What the selected planner still needs before it can plan, or null when it is
|
|
3972
|
+
* ready. There are two ways in (ADR-0009), and only one of them involves a
|
|
3973
|
+
* credential: a harness planner drives a coding agent already installed on the
|
|
3974
|
+
* machine, authenticated by the user's own subscription, so demanding an API
|
|
3975
|
+
* key of it turns a working setup into a wall. Its model is optional too: an
|
|
3976
|
+
* unset id means "the agent's own default".
|
|
3977
|
+
*/
|
|
3978
|
+
declare function plannerNotReady(config: PlannerReadinessConfig): PlannerNotReady | null;
|
|
3979
|
+
/**
|
|
3980
|
+
* `plannerNotReady` judged on a host's own config — the one way every host
|
|
3981
|
+
* builds the preflight's input, so none can hand it a key the selected
|
|
3982
|
+
* planner's transport will not read (a stored OpenRouter key passing for an
|
|
3983
|
+
* OpenAI planner, which then failed inside the transport). Every planner turn
|
|
3984
|
+
* judges through this too, so none can let through what another refuses.
|
|
3985
|
+
*/
|
|
3986
|
+
declare function plannerNotReadyFor(config: Pick<IConfig, 'aiProvider' | 'orchestratorModel' | 'planningModel' | 'openaiCompatibleBaseUrl' | 'getProviderApiKey'>): PlannerNotReady | null;
|
|
3987
|
+
/** How one surface spells the commands a not-ready message points at. */
|
|
3988
|
+
interface PlannerCommandSyntax {
|
|
3989
|
+
pickPlanner: string;
|
|
3990
|
+
setKey(provider: AiProvider): string;
|
|
3991
|
+
setModel: string;
|
|
3992
|
+
}
|
|
3993
|
+
declare const TERMINAL_UI_COMMANDS: PlannerCommandSyntax;
|
|
3994
|
+
/** The VS Code chat shares the terminal's slash commands, except that its `/model` needs `set`. */
|
|
3995
|
+
declare const CHAT_COMMANDS: PlannerCommandSyntax;
|
|
3996
|
+
declare const HEADLESS_CLI_COMMANDS: PlannerCommandSyntax;
|
|
3997
|
+
declare function describePlannerNotReady(reason: PlannerNotReady, commands: PlannerCommandSyntax): string;
|
|
3998
|
+
/**
|
|
3999
|
+
* A planner turn refused before it began. Carries the reason as data so a
|
|
4000
|
+
* client that spells commands differently (the headless CLI) can re-word it;
|
|
4001
|
+
* the message is the terminal UI's spelling, which is what most clients show.
|
|
4002
|
+
*/
|
|
4003
|
+
declare class PlannerNotReadyError extends Error {
|
|
4004
|
+
readonly reason: PlannerNotReady;
|
|
4005
|
+
constructor(reason: PlannerNotReady);
|
|
4006
|
+
}
|
|
4007
|
+
|
|
4008
|
+
/** What the user last picked for one planner backend. */
|
|
4009
|
+
interface PlannerModelChoice {
|
|
4010
|
+
model: string;
|
|
4011
|
+
effort?: string;
|
|
4012
|
+
}
|
|
4013
|
+
/**
|
|
4014
|
+
* A candidate model as this planner's catalog lists it. Deliberately plain
|
|
4015
|
+
* data: a harness planner's catalog is its runner's own model list and a
|
|
4016
|
+
* vendor's is a picker list (ADR-0009), and the rule below is the same for
|
|
4017
|
+
* both — so the caller normalizes, and this module never reaches for a
|
|
4018
|
+
* ModelResolver, a config or the network.
|
|
4019
|
+
*/
|
|
4020
|
+
interface PlannerModelCandidate {
|
|
4021
|
+
id: string;
|
|
4022
|
+
variants?: {
|
|
4023
|
+
id: string;
|
|
4024
|
+
}[];
|
|
4025
|
+
}
|
|
4026
|
+
interface PlannerModelRecall {
|
|
4027
|
+
model: string;
|
|
4028
|
+
effort: string;
|
|
4029
|
+
source: 'remembered' | 'catalog-default' | 'none';
|
|
4030
|
+
}
|
|
4031
|
+
/** The slice of `SettingsService` this needs, so tests can pass a plain object. */
|
|
4032
|
+
interface PlannerModelStore {
|
|
4033
|
+
getPlannerModel(provider: string): PlannerModelChoice | undefined;
|
|
4034
|
+
setPlannerModel(provider: string, entry: PlannerModelChoice | undefined): void;
|
|
4035
|
+
}
|
|
4036
|
+
/**
|
|
4037
|
+
* Remembers, per planner backend, the model the user last chose for it.
|
|
4038
|
+
*
|
|
4039
|
+
* Switching the planner has to clear the model — an id is scoped to the agent
|
|
4040
|
+
* that listed it, so an OpenRouter slug handed to Claude Code points it at
|
|
4041
|
+
* something it cannot spawn. That clear is correct and stays; what was missing
|
|
4042
|
+
* is that switching *back* then forced a re-pick of a model the user had
|
|
4043
|
+
* already chosen. The rule itself is `selectPlanner`'s; this is the storage
|
|
4044
|
+
* the surfaces that keep their memory in the settings file hand it.
|
|
4045
|
+
*/
|
|
4046
|
+
declare class PlannerModelMemory {
|
|
4047
|
+
private store;
|
|
4048
|
+
constructor(store: PlannerModelStore);
|
|
4049
|
+
/** Record what the user picked for `provider`; a blank model forgets it instead. */
|
|
4050
|
+
remember(provider: string, model: string, effort?: string): void;
|
|
4051
|
+
getPlannerModel(provider: string): PlannerModelChoice | undefined;
|
|
4052
|
+
/**
|
|
4053
|
+
* What the model and effort should become when `provider` takes over the
|
|
4054
|
+
* planner, given the models it actually serves. The `source` is what the
|
|
4055
|
+
* three surfaces word their notice from — restored, defaulted, or nothing.
|
|
4056
|
+
*/
|
|
4057
|
+
recall(provider: string, catalog: PlannerModelCandidate[]): PlannerModelRecall;
|
|
4058
|
+
}
|
|
4059
|
+
|
|
4060
|
+
/**
|
|
4061
|
+
* The daemon contract: every HTTP body the local daemon sends, in one place.
|
|
4062
|
+
*
|
|
4063
|
+
* The daemon's routes build their bodies against these types and the CLI's
|
|
4064
|
+
* `ApiClient` reads them back, so a field added on one side is a compile error
|
|
4065
|
+
* on the other. Before this there were three hand-written copies — the routes'
|
|
4066
|
+
* inline `c.json`, the client's per-method generics, and the TUI's slice of the
|
|
4067
|
+
* client — and they had drifted (the client declared a `headless` field the
|
|
4068
|
+
* daemon never sent, and typed `GET /sessions/:id`'s plan as the wrong shape).
|
|
4069
|
+
*
|
|
4070
|
+
* Types only. Core is the one package both the daemon and its clients already
|
|
4071
|
+
* depend on; importing from the daemon would pull its runtime into the CLI.
|
|
4072
|
+
*/
|
|
4073
|
+
/**
|
|
4074
|
+
* Why a request was refused, as a stable code a client switches on instead of
|
|
4075
|
+
* reading the message. Absent on a refusal nobody needs to tell apart — a
|
|
4076
|
+
* missing field, say — where the message is all there is.
|
|
4077
|
+
*/
|
|
4078
|
+
type DaemonErrorCode = 'session_not_found' | 'task_not_found' | 'no_plan' | 'already_executing' | 'workspace_not_found' | 'workspace_not_a_project' | 'conversation_busy'
|
|
4079
|
+
/** The user stopped the planner turn: the turn ended as asked, so a client stays quiet. */
|
|
4080
|
+
| 'planner_turn_stopped'
|
|
4081
|
+
/** The planner turn settled after its plan was dropped; nothing it produced was kept. */
|
|
4082
|
+
| 'planner_turn_discarded'
|
|
4083
|
+
/** The selected planner cannot plan yet (no key, no base URL, no model); `plannerNotReady` says which. */
|
|
4084
|
+
| 'planner_not_ready'
|
|
4085
|
+
/** The request asked for something the plan, task or conversation does not allow. */
|
|
4086
|
+
| 'refused' | 'checkpoint_not_waiting' | 'approval_not_outstanding';
|
|
4087
|
+
/** The body of every non-2xx answer a route gives. */
|
|
4088
|
+
interface ErrorBody {
|
|
4089
|
+
error: string;
|
|
4090
|
+
code?: DaemonErrorCode;
|
|
4091
|
+
/** Set with `workspace_not_a_project`: the directory the daemon refused, so a client can offer to initialize it. */
|
|
4092
|
+
workspace?: string;
|
|
4093
|
+
/** Set with `planner_not_ready`: the reason as data, so a client can word it in its own commands. */
|
|
4094
|
+
plannerNotReady?: PlannerNotReady;
|
|
4095
|
+
}
|
|
4096
|
+
/**
|
|
4097
|
+
* What the command seam said of a control the user pressed (ADR-0029), for the
|
|
4098
|
+
* surface to show as it is, so each words a control alike. Absent from a daemon
|
|
4099
|
+
* older than the field, and from a call that never reached the seam.
|
|
4100
|
+
*/
|
|
4101
|
+
interface Worded {
|
|
4102
|
+
message?: string;
|
|
4103
|
+
}
|
|
4104
|
+
interface OkResponse extends Worded {
|
|
4105
|
+
ok: boolean;
|
|
4106
|
+
}
|
|
4107
|
+
/** A task start's answer: `queued` says why it did not start now, when the task waits for its dependencies instead. */
|
|
4108
|
+
interface TaskStartResponse extends OkResponse {
|
|
4109
|
+
queued?: string;
|
|
4110
|
+
}
|
|
4111
|
+
/**
|
|
4112
|
+
* A plan as the planner routes return it: the session's plan state, not the
|
|
4113
|
+
* `SerializedPlan` a websocket `plan_generated` carries. The two differ —
|
|
4114
|
+
* this one holds raw tasks and the dialogue's bookkeeping — and a client that
|
|
4115
|
+
* reads either reads only the fields it needs. Skill bodies stay in the
|
|
4116
|
+
* session: loads and attempt snapshots arrive as notices.
|
|
4117
|
+
*/
|
|
4118
|
+
interface PlanResponse {
|
|
4119
|
+
plan: SurfacePlan;
|
|
4120
|
+
}
|
|
4121
|
+
/** `POST /plans/:id/review/approve`: the plan the user signed off, as it now runs. */
|
|
4122
|
+
interface ApproveReviewResponse extends PlanResponse, Worded {
|
|
4123
|
+
}
|
|
4124
|
+
/** `POST /plans/:id/resolve-conflict`: `null` when the session had no conflict to hand a resolver task. */
|
|
4125
|
+
interface ResolveConflictResponse {
|
|
4126
|
+
plan: SurfacePlan | null;
|
|
4127
|
+
}
|
|
4128
|
+
/** What `GET /api/models` returns, and what the pool's provider discovery produces. */
|
|
4129
|
+
interface ModelsResponse {
|
|
4130
|
+
models: DiscoveredModel[];
|
|
4131
|
+
modelsByRunner: Record<string, DiscoveredModel[]>;
|
|
4132
|
+
/** Each runner's manifest modes, so a surface can offer a per-task mode picker. */
|
|
4133
|
+
modesByRunner: Record<string, RunnerModeInfo[]>;
|
|
4134
|
+
orchestratorModel: string;
|
|
4135
|
+
providers: AiProvider[];
|
|
4136
|
+
/** Full cross-provider catalog for the orchestrator (planner) model picker. */
|
|
4137
|
+
orchestratorModels: OrchestratorOption[];
|
|
4138
|
+
/** Per-provider catalog-fetch failures, keyed by provider id. */
|
|
4139
|
+
providerErrors: Record<string, string>;
|
|
4140
|
+
}
|
|
4141
|
+
/** One-shot planning answers in the phase-tagged shape a saved session is read back in. */
|
|
4142
|
+
interface GeneratePlanResponse extends Pick<ModelsResponse, 'models' | 'modelsByRunner'> {
|
|
4143
|
+
plan: SurfacePlanState;
|
|
4144
|
+
/** The host notes the plan's transcript records, a skill check's warnings among them, which the phase-tagged plan does not carry. */
|
|
4145
|
+
notes?: string[];
|
|
4146
|
+
}
|
|
4147
|
+
interface ExecuteResponse extends Worded {
|
|
4148
|
+
status: 'running';
|
|
4149
|
+
}
|
|
4150
|
+
interface StopResponse extends Worded {
|
|
4151
|
+
status: 'stopped';
|
|
4152
|
+
}
|
|
4153
|
+
interface CancelPlanningResponse {
|
|
4154
|
+
cancelled: boolean;
|
|
4155
|
+
}
|
|
4156
|
+
/** `POST …/resume/cancel` (cancelled is true: a task with no resume scheduled is refused) and `POST …/converse/resend/cancel` (false when nothing waited). */
|
|
4157
|
+
interface CancelResumeResponse extends Worded {
|
|
4158
|
+
cancelled: boolean;
|
|
4159
|
+
}
|
|
4160
|
+
interface TaskMessageResponse {
|
|
4161
|
+
id: string;
|
|
4162
|
+
}
|
|
4163
|
+
interface ForceSendResponse extends Worded {
|
|
4164
|
+
sent: boolean;
|
|
4165
|
+
}
|
|
4166
|
+
interface RemoveMessageResponse extends Worded {
|
|
4167
|
+
removed: boolean;
|
|
4168
|
+
}
|
|
4169
|
+
interface MergeGateEntry {
|
|
4170
|
+
id: string;
|
|
4171
|
+
order: number;
|
|
4172
|
+
title: string;
|
|
4173
|
+
}
|
|
4174
|
+
interface MergeGateResponse {
|
|
4175
|
+
mergeGate: MergeGateEntry[];
|
|
4176
|
+
}
|
|
4177
|
+
interface IsolationDiffResponse {
|
|
4178
|
+
diff: string;
|
|
4179
|
+
}
|
|
4180
|
+
/** A merge that conflicts or is blocked is an outcome, not a refusal, so it answers 200 with the result. */
|
|
4181
|
+
type IsolationMergeResponse = IsolationMergeResult;
|
|
4182
|
+
/** A fork the daemon has already adopted. */
|
|
4183
|
+
interface ConversationForkResponse {
|
|
4184
|
+
sessionId: string;
|
|
4185
|
+
goal: string;
|
|
4186
|
+
plan: SurfacePlan;
|
|
4187
|
+
}
|
|
4188
|
+
/** A fork made by a rewind, with the full text of the message it was made just before. */
|
|
4189
|
+
interface ConversationRewindResponse extends ConversationForkResponse {
|
|
4190
|
+
rewoundMessage: string;
|
|
4191
|
+
}
|
|
4192
|
+
interface RewindTargetsResponse {
|
|
4193
|
+
targets: RewindTarget[];
|
|
4194
|
+
}
|
|
4195
|
+
interface ConversationCompactResponse extends ConversationCompaction, PlanResponse {
|
|
4196
|
+
}
|
|
4197
|
+
interface PrdResponse {
|
|
4198
|
+
prdMarkdown: string;
|
|
4199
|
+
}
|
|
4200
|
+
interface RunnerEntry {
|
|
4201
|
+
id: string;
|
|
4202
|
+
name: string;
|
|
4203
|
+
enabled: boolean;
|
|
4204
|
+
}
|
|
4205
|
+
interface RunnersResponse {
|
|
4206
|
+
runners: RunnerEntry[];
|
|
4207
|
+
orchestratorModel: string;
|
|
4208
|
+
}
|
|
4209
|
+
/** Sessions: a saved one comes off its file, a live one from the pool's store. */
|
|
4210
|
+
interface SessionResponse {
|
|
4211
|
+
meta: SessionMeta;
|
|
4212
|
+
plan: SurfacePlanState;
|
|
4213
|
+
/**
|
|
4214
|
+
* The live session's run as `Session.runSnapshot()` holds it, isolation
|
|
4215
|
+
* marks and handoff included. Absent for a session nobody holds: the saved
|
|
4216
|
+
* file has no run to speak of, and a surface does not rebuild one from it.
|
|
4217
|
+
*/
|
|
4218
|
+
run?: RunView;
|
|
4219
|
+
}
|
|
4220
|
+
interface AdoptSessionResponse extends OkResponse {
|
|
4221
|
+
plan: SurfacePlan;
|
|
4222
|
+
goal: string;
|
|
4223
|
+
/** The adopted session's run, as {@link SessionResponse.run}. */
|
|
4224
|
+
run?: RunView;
|
|
4225
|
+
}
|
|
4226
|
+
interface TaskLogAttemptsResponse {
|
|
4227
|
+
/** Captured with the synchronous listing, including an empty listing. */
|
|
4228
|
+
position?: StreamStamp;
|
|
4229
|
+
attempts: number[];
|
|
4230
|
+
}
|
|
4231
|
+
interface TaskLogResponse {
|
|
4232
|
+
/** Captured with the synchronous log read; live batches after it are not in events. */
|
|
4233
|
+
position?: StreamStamp;
|
|
4234
|
+
attempt: number;
|
|
4235
|
+
events: TaskLogEvent[];
|
|
4236
|
+
}
|
|
4237
|
+
type SessionListResponse = SessionMeta[];
|
|
4238
|
+
interface SettingsResponse {
|
|
4239
|
+
orchestratorModel: string;
|
|
4240
|
+
/** Who plans (ADR-0009) — a vendor id, or a runner serving as a harness planner. */
|
|
4241
|
+
aiProvider: PlannerBackendId;
|
|
4242
|
+
plannerThinkingEffort: string;
|
|
4243
|
+
maxParallel: number;
|
|
4244
|
+
modelAllowlist: UserSettings['modelAllowlist'];
|
|
4245
|
+
plannerModels: UserSettings['plannerModels'];
|
|
4246
|
+
/** The user's run settings (ADR-0030), each at its default until set. */
|
|
4247
|
+
run: GlobalRunSettings;
|
|
4248
|
+
}
|
|
4249
|
+
/** What `PATCH /api/settings` answers: the settings as they now stand, plus what the write itself decided. */
|
|
4250
|
+
interface SettingsUpdateResponse extends SettingsResponse {
|
|
4251
|
+
/**
|
|
4252
|
+
* Which model a planner switch landed on and why — remembered for that
|
|
4253
|
+
* backend, its catalog default, or none. Present only when the write changed
|
|
4254
|
+
* the planner; absent for every other write, where nothing was recalled.
|
|
4255
|
+
*/
|
|
4256
|
+
switchRecall?: PlannerModelRecall;
|
|
4257
|
+
/** Env keys the daemon refused to apply, so a client does not persist what the daemon would not. */
|
|
4258
|
+
rejectedEnvKeys?: string[];
|
|
4259
|
+
}
|
|
4260
|
+
interface CommandDescriptor {
|
|
4261
|
+
name: string;
|
|
4262
|
+
description: string;
|
|
4263
|
+
}
|
|
4264
|
+
interface CommandsResponse {
|
|
4265
|
+
commands: CommandDescriptor[];
|
|
4266
|
+
}
|
|
4267
|
+
interface WorkspacesResponse {
|
|
4268
|
+
workspaces: string[];
|
|
4269
|
+
}
|
|
4270
|
+
interface ApprovalsResponse {
|
|
4271
|
+
pending: PendingApproval[];
|
|
4272
|
+
approvedScopes: string[];
|
|
4273
|
+
}
|
|
4274
|
+
interface ApprovalAnswerResponse extends OkResponse {
|
|
4275
|
+
granted: boolean;
|
|
4276
|
+
decision: ApprovalDecision['decision'];
|
|
4277
|
+
}
|
|
4278
|
+
/**
|
|
4279
|
+
* Where a frame stands in its session's broadcast stream. A client holding
|
|
4280
|
+
* several sockets on one session gets every broadcast on each, and a frame's
|
|
4281
|
+
* content is no identity: two clears of a held edit are the same JSON. `seq`
|
|
4282
|
+
* rises by one per broadcast of the session for as long as the daemon runs,
|
|
4283
|
+
* whichever Session object holds the id; `stream` names the daemon process,
|
|
4284
|
+
* so a restarted daemon's numbering is never compared with the last one's.
|
|
4285
|
+
*/
|
|
4286
|
+
interface StreamStamp {
|
|
4287
|
+
stream: string;
|
|
4288
|
+
seq: number;
|
|
4289
|
+
}
|
|
4290
|
+
/** A broadcast as the session's socket carries it. Unstamped from a daemon older than the stamp. */
|
|
4291
|
+
type SessionFrame = (SessionMessage | SessionNotice) & Partial<StreamStamp>;
|
|
4292
|
+
type ApprovalRequestMessage = Extract<SessionMessage, {
|
|
4293
|
+
type: 'approval_request';
|
|
4294
|
+
}>;
|
|
4295
|
+
/**
|
|
4296
|
+
* What a session's socket sends right after `connected`: its run as it stands,
|
|
4297
|
+
* so a client that joins mid-run has the run view before the next update —
|
|
4298
|
+
* the held edit's preview included — and the planner's approval requests
|
|
4299
|
+
* still waiting, which no later frame would announce again. `seq` is the last
|
|
4300
|
+
* broadcast it accounts for. Every frame after it is a {@link SessionFrame}.
|
|
4301
|
+
*/
|
|
4302
|
+
interface RunSnapshotFrame extends Partial<StreamStamp> {
|
|
4303
|
+
type: 'run_snapshot';
|
|
4304
|
+
sessionId: string;
|
|
4305
|
+
view: RunView;
|
|
4306
|
+
/** Absent from a daemon older than the field: nothing is known, not "none waiting". */
|
|
4307
|
+
approvals?: ApprovalRequestMessage[];
|
|
4308
|
+
}
|
|
4309
|
+
|
|
4310
|
+
declare const EMPTY_RUN: RunView;
|
|
4311
|
+
declare function reduceRun(view: RunView, input: RunInput): RunView;
|
|
4312
|
+
|
|
4313
|
+
/** What the blocked rule reads of a task; structural, so a surface's own task shape answers it too. */
|
|
4314
|
+
interface BlockingSubject {
|
|
2344
4315
|
id: string;
|
|
2345
4316
|
status: string;
|
|
2346
4317
|
dependencies: readonly string[];
|
|
@@ -2610,10 +4581,10 @@ declare function reduceConversation(view: ConversationView, input: ConversationI
|
|
|
2610
4581
|
|
|
2611
4582
|
/**
|
|
2612
4583
|
* The view a saved session reopens with: what its transcript and research log
|
|
2613
|
-
* kept, in the order it happened — messages,
|
|
2614
|
-
* summary, plan markers, tool calls with
|
|
2615
|
-
* it, and the token line. Reasoning and
|
|
2616
|
-
* reload has none.
|
|
4584
|
+
* kept, in the order it happened — messages, each reply run by run where it
|
|
4585
|
+
* began, notices and the compaction summary, plan markers, tool calls with
|
|
4586
|
+
* each subagent's calls nested under it, and the token line. Reasoning and
|
|
4587
|
+
* streamed text that never settled were never saved, so a reload has none.
|
|
2617
4588
|
*
|
|
2618
4589
|
* The research log interleaves with the transcript by time. Entries older than
|
|
2619
4590
|
* a compaction's first kept message went with the turns it condensed.
|
|
@@ -2638,8 +4609,9 @@ interface TaskLogView extends BlockList {
|
|
|
2638
4609
|
/** What the runner reported over the attempt, subagents included. */
|
|
2639
4610
|
readonly usage?: PlannerUsage;
|
|
2640
4611
|
/**
|
|
2641
|
-
* The block each
|
|
2642
|
-
*
|
|
4612
|
+
* The block each stream of deltas writes into — a run of text by its id,
|
|
4613
|
+
* thinking by whose it is — so the stream's authoritative copy replaces it
|
|
4614
|
+
* instead of repeating it below.
|
|
2643
4615
|
*/
|
|
2644
4616
|
readonly streams: Readonly<Record<string, string>>;
|
|
2645
4617
|
/** The turn's latest agent text block, which a failure that repeats it takes the place of. */
|
|
@@ -2806,6 +4778,40 @@ declare function markdownTokens(text: string): readonly MarkdownToken[];
|
|
|
2806
4778
|
/** A model's text as HTML, safe to set as a webview element's innerHTML. */
|
|
2807
4779
|
declare function markdownHtml(text: string): string;
|
|
2808
4780
|
|
|
4781
|
+
interface TaskLogBatch {
|
|
4782
|
+
attempt: number;
|
|
4783
|
+
events: TaskLogEvent[];
|
|
4784
|
+
position?: StreamStamp;
|
|
4785
|
+
}
|
|
4786
|
+
/** A saved attempt and the live batches that follow it (ADR-0018 P1). */
|
|
4787
|
+
interface TaskLogFollow {
|
|
4788
|
+
view: TaskLogView;
|
|
4789
|
+
attempts: number[];
|
|
4790
|
+
attempt: number;
|
|
4791
|
+
pending: TaskLogBatch[];
|
|
4792
|
+
loaded: boolean;
|
|
4793
|
+
followLatest: boolean;
|
|
4794
|
+
/** The outstanding pick; an older attempt read cannot replace it. */
|
|
4795
|
+
loadingAttempt?: number;
|
|
4796
|
+
/** The saved read's cutoff, also rejecting socket copies delivered after the read. */
|
|
4797
|
+
position?: StreamStamp;
|
|
4798
|
+
}
|
|
4799
|
+
type TaskLogFollowInput = {
|
|
4800
|
+
type: 'opened';
|
|
4801
|
+
} | ({
|
|
4802
|
+
type: 'loaded';
|
|
4803
|
+
attempts?: number[];
|
|
4804
|
+
abandoned?: boolean;
|
|
4805
|
+
} & TaskLogBatch) | ({
|
|
4806
|
+
type: 'live';
|
|
4807
|
+
} & TaskLogBatch) | {
|
|
4808
|
+
type: 'picked';
|
|
4809
|
+
attempt: number;
|
|
4810
|
+
};
|
|
4811
|
+
declare const EMPTY_TASK_LOG_FOLLOW: TaskLogFollow;
|
|
4812
|
+
/** Join by stream position, never by event content or socket arrival time. */
|
|
4813
|
+
declare function followTaskLog(state: TaskLogFollow, input: TaskLogFollowInput): TaskLogFollow;
|
|
4814
|
+
|
|
2809
4815
|
/**
|
|
2810
4816
|
* What a row's status means to a reader, before any surface picks a word,
|
|
2811
4817
|
* icon or colour for it. `quiet` is a running task whose runner has printed
|
|
@@ -2837,7 +4843,7 @@ declare function skippedLabel(reason: string | null): string;
|
|
|
2837
4843
|
* for them (ADR-0035). Not a runner approval: nothing has started, so no
|
|
2838
4844
|
* runner is asking anything.
|
|
2839
4845
|
*/
|
|
2840
|
-
declare const WAITS_FOR_APPROVAL_LABEL = "
|
|
4846
|
+
declare const WAITS_FOR_APPROVAL_LABEL = "Waits for your approval";
|
|
2841
4847
|
/** Runner requests a task waits on (ADR-0018, A1); null when none do. */
|
|
2842
4848
|
declare function approvalLabel(count: number | undefined): string | null;
|
|
2843
4849
|
/**
|
|
@@ -2914,6 +4920,11 @@ interface TaskRowSubject {
|
|
|
2914
4920
|
mergeGate?: readonly string[];
|
|
2915
4921
|
forcedPastGate?: readonly string[];
|
|
2916
4922
|
attemptUsage?: readonly AttemptUsage[];
|
|
4923
|
+
assignedRunner?: string;
|
|
4924
|
+
assignedModel?: {
|
|
4925
|
+
modelId: string;
|
|
4926
|
+
modelLabel?: string;
|
|
4927
|
+
};
|
|
2917
4928
|
}
|
|
2918
4929
|
interface TaskRowIsolation {
|
|
2919
4930
|
state: TaskIsolationState;
|
|
@@ -2955,12 +4966,14 @@ interface TaskRowContext {
|
|
|
2955
4966
|
}[];
|
|
2956
4967
|
/** What a resume time is read against; the clock when absent. */
|
|
2957
4968
|
now?: Date;
|
|
4969
|
+
/** The enabled runners and allowlist an assignment is read against; without it no assignment is flagged. */
|
|
4970
|
+
runnerScope?: AssignmentScope;
|
|
2958
4971
|
}
|
|
2959
4972
|
/** Whether the row stands for an AI task, a manual one, or an ops task (ADR-0020). */
|
|
2960
4973
|
type TaskRowKind = 'ai' | 'user' | 'ops';
|
|
2961
|
-
|
|
4974
|
+
/** The task's whole spend (see `taskUsageSummary`); `attempt` is the latest one, 0 before any reported. */
|
|
4975
|
+
interface TaskRowUsage extends Pick<TaskUsageSummary, 'totals' | 'contextFill'> {
|
|
2962
4976
|
attempt: number;
|
|
2963
|
-
wallTimeMs?: number;
|
|
2964
4977
|
}
|
|
2965
4978
|
interface TaskRowView {
|
|
2966
4979
|
usage: TaskRowUsage | null;
|
|
@@ -2978,6 +4991,10 @@ interface TaskRowView {
|
|
|
2978
4991
|
approvals: string | null;
|
|
2979
4992
|
/** That it waits for the user's approval to start, in sentence case; null once it started, or when it needs none. */
|
|
2980
4993
|
waitsForApproval: string | null;
|
|
4994
|
+
/** Why the task's runner or model is one the user has since moved away from, in sentence case; null when it is not. The task still runs. */
|
|
4995
|
+
assignmentFlag: string | null;
|
|
4996
|
+
/** What the flag means for this task as it stands (a failed one runs again only if retried); set exactly when `assignmentFlag` is. */
|
|
4997
|
+
assignmentConsequence: string | null;
|
|
2981
4998
|
/** The task's mode is tagged autonomous: it runs without permission prompts. */
|
|
2982
4999
|
autonomous: boolean;
|
|
2983
5000
|
/** The repos its isolated work changed, by path; empty for a group of one. */
|
|
@@ -3040,9 +5057,10 @@ declare function titledRefs(ids: readonly string[], tasks: readonly Named[]): st
|
|
|
3040
5057
|
* and a bare "Remove task?" would hide that.
|
|
3041
5058
|
*/
|
|
3042
5059
|
declare function dependentsNotice(dependents: readonly {
|
|
5060
|
+
id?: string;
|
|
3043
5061
|
order: number;
|
|
3044
5062
|
title: string;
|
|
3045
|
-
}[]): string | null;
|
|
5063
|
+
}[], tasks?: readonly Named[]): string | null;
|
|
3046
5064
|
/**
|
|
3047
5065
|
* What a start past a merge gate is asked with (ADR-0020): the work the task
|
|
3048
5066
|
* would act without, named. `subject` is how the asking surface refers to the
|
|
@@ -3075,4 +5093,4 @@ declare function parseSwitch(arg: string): boolean | null;
|
|
|
3075
5093
|
/** A whole number at least `min`, as typed; null for anything else. */
|
|
3076
5094
|
declare function parseCount(arg: string | undefined, min: number): number | null;
|
|
3077
5095
|
|
|
3078
|
-
export { type
|
|
5096
|
+
export { type ForceSendResponse as $, ACT_RISK as A, type BlockingSubject as B, CHAT_COMMANDS as C, type ConversationCompactResponse as D, type ConversationCompaction as E, ConversationEditError as F, type ConversationForkResponse as G, type ConversationInput as H, type ConversationRequest as I, type ConversationRewindResponse as J, type ConversationTurn as K, type ConversationView as L, DEFAULT_WEB_READ_BUDGET as M, type DaemonErrorCode as N, type DecideOptions as O, type DiffRow as P, type DiffRowKind as Q, type DiffStat as R, type DisplayBlock as S, EMPTY_CONVERSATION as T, EMPTY_HOLD as U, EMPTY_RUN as V, EMPTY_TASK_LOG as W, EMPTY_TASK_LOG_FOLLOW as X, type ErrorBody as Y, type ExecuteResponse as Z, type ExecutionSummary as _, AUTORUN_RISK as a, type RewindTargetsResponse as a$, type GatedConversation as a0, type GeneratePlanResponse as a1, type GlobalRunSettings as a2, HEADLESS_CLI_COMMANDS as a3, type HeldEdit as a4, type HeldEditMark as a5, type IAiService as a6, type INotification as a7, type IWebFetcher as a8, type IsolationDiffResponse as a9, type PlanResponse as aA, type PlanSnapshot as aB, PlanStore as aC, type PlannerCommandSyntax as aD, PlannerConversation as aE, type PlannerConversationHost as aF, type PlannerModelCandidate as aG, type PlannerModelChoice as aH, PlannerModelMemory as aI, type PlannerModelRecall as aJ, type PlannerModelStore as aK, type PlannerModes as aL, type PlannerNotReady as aM, PlannerNotReadyError as aN, type PlannerReadinessConfig as aO, type PlannerSubmission as aP, PlannerTurnDiscardedError as aQ, PlannerTurnStoppedError as aR, PlannerUsageLimitError as aS, type PrdResponse as aT, type PromptHold as aU, RUN_SETTINGS_DEFAULTS as aV, type RefTask as aW, type RemoveMessageResponse as aX, type ResolveAll as aY, type ResolveConflictResponse as aZ, type RewindTarget as a_, type IsolationMergeResponse as aa, IsolationRunController as ab, type LiveOutputLookup as ac, type LiveTail as ad, type LiveTailOptions as ae, type LocalEntry as af, type MarkdownToken as ag, type MergeGateEntry as ah, type MergeGateResponse as ai, type MessageBlock as aj, type MessageRole as ak, type ModelsResponse as al, NOT_STARTED as am, NO_TURN as an, type NewTaskFields as ao, type NotificationAction as ap, OUTPUT_LINES_DEFAULT as aq, OUTPUT_LINES_MAX as ar, type OkResponse as as, type OrchestratorObserver as at, type OutputPreview as au, type PendingApproval as av, PendingApprovals as aw, type PendingApprovalsOptions as ax, type PlanBlock as ay, type PlanMarkerStatus as az, type AdoptSessionResponse as b, type TaskPlacement as b$, type RunInput as b0, type RunMergeView as b1, RunSettingsError as b2, type RunSnapshotFrame as b3, type RunTask as b4, type RunTaskStatus as b5, type RunView as b6, type RunnerCatalog as b7, type RunnerLoadedBlock as b8, type RunnersResponse as b9, type SubagentChild as bA, type SubagentStatus as bB, type SurfacePlan as bC, type SurfacePlanState as bD, type SurfaceTask as bE, TASK_QUERY_ANSWER_MAX_CHARS as bF, TASK_QUERY_ANSWER_OR_OPS as bG, TASK_QUERY_FIELDS as bH, TASK_QUERY_PROTOCOL as bI, TASK_QUERY_REMINDER as bJ, TERMINAL_UI_COMMANDS as bK, type TakenPrompt as bL, type TaskAttemptSnapshot as bM, TaskControlError as bN, type TaskLogAttemptsResponse as bO, type TaskLogBatch as bP, type TaskLogEvent as bQ, type TaskLogFollow as bR, type TaskLogFollowInput as bS, type TaskLogResponse as bT, type TaskLogView as bU, type TaskMessageResponse as bV, type TaskOp as bW, TaskOrchestrator as bX, type TaskOrchestratorDeps as bY, type TaskOrchestratorOptions as bZ, type TaskOutputSource as b_, STOPPED_BEFORE_START as ba, SUPERVISOR_LEVEL_LABEL as bb, type SerializedConversationMessage as bc, type SerializedPendingTurn as bd, type SerializedPlan as be, type SerializedTask as bf, type SerializedTaskStatus as bg, type SessionBroadcaster as bh, type SessionData as bi, type SessionFrame as bj, type SessionListResponse as bk, type SessionMessage as bl, type SessionMeta as bm, type SessionNotice as bn, type SessionResponse as bo, type SettingsResponse as bp, SettingsService as bq, type SettingsUpdateResponse as br, type SkillInvocation as bs, type SkillLoadBlock as bt, type SkillToken as bu, type StartAdmission as bv, type StartWait as bw, type StopResponse as bx, type StreamStamp as by, type SubagentBlock as bz, type ApplyTaskOpsResult as c, loadedSkillTokens as c$, type TaskQuery as c0, type TaskQueryCatalog as c1, type TaskQueryField as c2, type TaskRef as c3, type TaskRowAction as c4, type TaskRowContext as c5, type TaskRowIsolation as c6, type TaskRowKind as c7, type TaskRowPlacement as c8, type TaskRowSubject as c9, awaitingLabel as cA, blockedByLabel as cB, blockingFailures as cC, canMergeTasks as cD, canSetDependencies as cE, canSplitTask as cF, capConflictFiles as cG, classifyOutcome as cH, coalesceTaskLog as cI, createAiService as cJ, dependencyCandidates as cK, dependentsNotice as cL, dependentsOf as cM, describePlannerNotReady as cN, diffRows as cO, diffStat as cP, diffSummary as cQ, digestTaskLog as cR, drainNext as cS, executionSummary as cT, followTaskLog as cU, followTurn as cV, fromTranscript as cW, getSettingsPath as cX, hasHiddenDetail as cY, holdPrompt as cZ, isTaskRunning as c_, type TaskRowUsage as ca, type TaskRowView as cb, type TaskStartResponse as cc, type TaskStatusKind as cd, type ThinkingDisplayBlock as ce, type ToolBlock as cf, type ToolHeadline as cg, type ToolStatus as ch, type TreeTask as ci, type TurnGate as cj, type UsageBlock as ck, type UserSettings as cl, WAITS_FOR_APPROVAL_LABEL as cm, type WebRead as cn, type WebReadDecision as co, WebReadPolicy as cp, type Worded as cq, type WorkspacesResponse as cr, abbreviateHome as cs, aheadOfDraft as ct, applyTaskOps as cu, approvalLabel as cv, assignmentIssue as cw, assignmentIssueText as cx, automergeSummary as cy, autorunSummary as cz, type ApprovalAnswerResponse as d, unsendAll as d$, markAction as d0, markRequestFor as d1, markdownHtml as d2, markdownTokens as d3, namedRepos as d4, newTaskFields as d5, opsEditable as d6, outputLines as d7, outputPreview as d8, parseCount as d9, skippedLabel as dA, stopTurn as dB, subtreeIds as dC, summarizeToolCall as dD, supervisorSummary as dE, surfacePlan as dF, surfacePlanState as dG, takeAt as dH, taskLogForSurface as dI, taskOrderLabel as dJ, taskPlacement as dK, taskQuerySignature as dL, taskRef as dM, taskRefOf as dN, taskRowActions as dO, taskRowView as dP, taskStartedNotice as dQ, taskStatusKind as dR, textHasTaskOps as dS, textHasTaskQuery as dT, titledRefs as dU, titledTaskRef as dV, titledTaskRefOf as dW, toTaskLogEvent as dX, toolHeadline as dY, trimToolOutput as dZ, truncateCheckpointSummary as d_, parseSwitch as da, parseTaskOpsJson as db, parseTaskQueryJson as dc, pastGateConfirmation as dd, plannerMessage as de, plannerNotReady as df, plannerNotReadyFor as dg, plannerTranscript as dh, reconcileApprovals as di, reduceConversation as dj, reduceRun as dk, reduceTaskLog as dl, renderTaskQueryAnswer as dm, replayTaskLog as dn, resolveSkillInvocation as dp, resolveTaskRef as dq, resolveUserTaskRef as dr, resumeLabel as ds, resumeTime as dt, runSettingsOf as du, runnerToolSubject as dv, serializePlan as dw, serializeTask as dx, serializeTaskStatus as dy, skillTokens as dz, type ApprovalBlock as e, unsendLatest as e0, walkPlan as e1, type ApprovalRequestMessage as f, type ApprovalStatus as g, type ApprovalsResponse as h, type ApproveReviewResponse as i, type AskOptions as j, type AssignmentCatalog as k, type AssignmentIssue as l, type AssignmentScope as m, type AssignmentWriter as n, type AutoMergeTrigger as o, CHECKPOINT_TRUNCATE_LENGTH as p, type CancelPlanningResponse as q, type CancelResumeResponse as r, CliAgentAiService as s, type CliAgentAiServiceDeps as t, type CommandBlock as u, type CommandDescriptor as v, type CommandStatus as w, type CommandsResponse as x, type CompactionBlock as y, ConversationBusyError as z };
|