@ordewell/core 0.10.12 → 0.11.1
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/AUTHORS.md +37 -0
- package/NOTICE +71 -0
- package/README.md +1 -1
- package/dist/{IFileSystem-1PqRCjhu.d.mts → IRunner-B5l9PCTv.d.ts} +926 -102
- package/dist/{IFileSystem-lTRL_G9Z.d.ts → IRunner-DBgDx_Mr.d.mts} +926 -102
- package/dist/{ModeResolver-CoJAJD-f.d.ts → ModeResolver-BDAgouuE.d.mts} +9 -2
- package/dist/{ModeResolver-6TQXVsde.d.mts → ModeResolver-DyXeZ9ib.d.ts} +9 -2
- package/dist/{Task-CfBM0wkV.d.mts → Task-BL4Idm9r.d.mts} +656 -26
- package/dist/{Task-CfBM0wkV.d.ts → Task-BL4Idm9r.d.ts} +656 -26
- package/dist/{chunk-ESKK5I4F.mjs → chunk-4QXOWEGC.mjs} +1888 -334
- package/dist/chunk-4QXOWEGC.mjs.map +1 -0
- package/dist/{chunk-KWS3VEER.mjs → chunk-MYXMIVWA.mjs} +6 -6
- package/dist/chunk-MYXMIVWA.mjs.map +1 -0
- package/dist/{chunk-PFH7WFIB.mjs → chunk-QTZ5XTBY.mjs} +228 -15
- package/dist/chunk-QTZ5XTBY.mjs.map +1 -0
- package/dist/{chunk-24EL4Z2W.mjs → chunk-YAFNYNFX.mjs} +342 -36
- package/dist/chunk-YAFNYNFX.mjs.map +1 -0
- package/dist/index.d.mts +1488 -769
- package/dist/index.d.ts +1488 -769
- package/dist/index.js +32003 -20350
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +29380 -19553
- 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 +232 -25
- package/dist/parsing.js.map +1 -1
- package/dist/parsing.mjs +3 -3
- package/dist/plan-utils.d.mts +4 -4
- package/dist/plan-utils.d.ts +4 -4
- package/dist/plan-utils.js +1156 -96
- package/dist/plan-utils.js.map +1 -1
- package/dist/plan-utils.mjs +144 -8
- package/dist/{runSettingsText-Jb_efnI4.d.mts → runSettingsText-BKwWs052.d.mts} +2882 -621
- package/dist/{runSettingsText-C5uv2axI.d.ts → runSettingsText-CAXiTpsj.d.ts} +2882 -621
- package/dist/testing.d.mts +10 -4
- package/dist/testing.d.ts +10 -4
- package/dist/testing.js +30 -7
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +26 -7
- package/dist/testing.mjs.map +1 -1
- package/package.json +7 -3
- package/skills/diagnose/SKILL.md +52 -0
- package/skills/explore/SKILL.md +65 -0
- package/dist/chunk-24EL4Z2W.mjs.map +0 -1
- package/dist/chunk-ESKK5I4F.mjs.map +0 -1
- package/dist/chunk-KWS3VEER.mjs.map +0 -1
- package/dist/chunk-NJGID6BG.mjs +0 -115
- package/dist/chunk-NJGID6BG.mjs.map +0 -1
- package/dist/chunk-PFH7WFIB.mjs.map +0 -1
|
@@ -7,13 +7,13 @@ import {
|
|
|
7
7
|
plannedTask,
|
|
8
8
|
stripTrailingCommas,
|
|
9
9
|
taskWhere
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-YAFNYNFX.mjs";
|
|
11
11
|
import {
|
|
12
12
|
flattenTasksWithParents
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-QTZ5XTBY.mjs";
|
|
14
14
|
|
|
15
15
|
// src/services/PlanValidator.ts
|
|
16
|
-
function parsePlanJson(raw, runners, runnerModes, autonomousDefault =
|
|
16
|
+
function parsePlanJson(raw, runners, runnerModes, autonomousDefault = false) {
|
|
17
17
|
const { matches, fallback, sawUnbalanced } = extractObjectsWithKey(raw, PLAN_ENVELOPE_KEY);
|
|
18
18
|
const candidates = matches.length > 0 ? [...matches].reverse() : [fallback.json];
|
|
19
19
|
let lastError = null;
|
|
@@ -34,7 +34,7 @@ function parsePlanJson(raw, runners, runnerModes, autonomousDefault = true) {
|
|
|
34
34
|
}
|
|
35
35
|
throw lastError ?? new PlanParseError("Invalid plan: missing tasks array", raw);
|
|
36
36
|
}
|
|
37
|
-
function parsePlanObject(json, raw, runners, runnerModes, autonomousDefault =
|
|
37
|
+
function parsePlanObject(json, raw, runners, runnerModes, autonomousDefault = false) {
|
|
38
38
|
const cleaned = stripTrailingCommas(escapeControlCharsInStrings(json));
|
|
39
39
|
let parsed;
|
|
40
40
|
try {
|
|
@@ -48,7 +48,7 @@ function parsePlanObject(json, raw, runners, runnerModes, autonomousDefault = tr
|
|
|
48
48
|
const [first] = result.errors;
|
|
49
49
|
throw new PlanParseError(first.message, raw, { semantic: first.taskId !== void 0 });
|
|
50
50
|
}
|
|
51
|
-
function validatePlanTasks(obj, runners, runnerModes, autonomousDefault =
|
|
51
|
+
function validatePlanTasks(obj, runners, runnerModes, autonomousDefault = false) {
|
|
52
52
|
const rawTasks = isRecord(obj) ? obj.tasks : void 0;
|
|
53
53
|
if (!Array.isArray(rawTasks)) {
|
|
54
54
|
return { ok: false, errors: [{ field: "tasks", message: "Invalid plan: missing tasks array" }] };
|
|
@@ -94,4 +94,4 @@ export {
|
|
|
94
94
|
validatePlanTasks,
|
|
95
95
|
looksLikePlanAttempt
|
|
96
96
|
};
|
|
97
|
-
//# sourceMappingURL=chunk-
|
|
97
|
+
//# sourceMappingURL=chunk-MYXMIVWA.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/PlanValidator.ts"],"sourcesContent":["import { Task, RunnerId, flattenTasksWithParents } from '../models/Task';\nimport { extractObjectsWithKey, stripTrailingCommas, escapeControlCharsInStrings, PlanParseError, PLAN_ENVELOPE_KEY } from './JsonExtractor';\nimport type { RunnerModeInfo } from './ModeResolver';\nimport { plannedTask, checkPlanInvariants, taskWhere, type PlanTaskError } from './PlannedTask';\n\nexport function parsePlanJson(\n raw: string,\n runners: RunnerId[],\n runnerModes?: Record<RunnerId, RunnerModeInfo[]>,\n autonomousDefault = false,\n): Task[] {\n const { matches, fallback, sawUnbalanced } = extractObjectsWithKey(raw, PLAN_ENVELOPE_KEY);\n\n // A reply can carry several tasks-keyed objects — a schema echo from the\n // prompt, an inline draft, then the real plan. The model's LAST emission is\n // its final answer, so candidates are tried back to front and the first one\n // that fully parses and validates wins.\n const candidates = matches.length > 0 ? [...matches].reverse() : [fallback.json];\n\n let lastError: PlanParseError | null = null;\n for (const json of candidates) {\n try {\n return parsePlanObject(json, raw, runners, runnerModes, autonomousDefault);\n } catch (err) {\n if (!(err instanceof PlanParseError)) throw err;\n // Prefer surfacing the error from the most plan-like candidate: the\n // last one carrying a tasks key (tried first).\n if (!lastError) lastError = err;\n }\n }\n\n // No usable tasks object anywhere, but an object ran off the end of the\n // text: the plan was almost certainly cut off mid-stream.\n if (matches.length === 0 && sawUnbalanced) {\n throw new PlanParseError(\n 'Plan response appears truncated: the model likely hit its output length limit before finishing the JSON.',\n raw,\n { truncated: true },\n );\n }\n throw lastError ?? new PlanParseError('Invalid plan: missing tasks array', raw);\n}\n\nfunction parsePlanObject(\n json: string,\n raw: string,\n runners: RunnerId[],\n runnerModes?: Record<RunnerId, RunnerModeInfo[]>,\n autonomousDefault = false,\n): Task[] {\n const cleaned = stripTrailingCommas(escapeControlCharsInStrings(json));\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(cleaned);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new PlanParseError(`Plan response was not valid JSON: ${detail}`, raw);\n }\n\n const result = validatePlanTasks(parsed, runners, runnerModes, autonomousDefault);\n if (result.ok) return result.tasks;\n const [first] = result.errors;\n // An envelope problem (no tasks array, an empty one) keeps the generic\n // re-emit; only a rule broken by a task is worth naming in the corrective.\n throw new PlanParseError(first.message, raw, { semantic: first.taskId !== undefined });\n}\n\nexport type { PlanTaskError } from './PlannedTask';\nexport { requestedSkills } from './PlannedTask';\n\n/** `warnings` name what was accepted but not used, for a channel that can tell the planner. */\nexport type PlanTasksResult = { ok: true; tasks: Task[]; warnings?: string[] } | { ok: false; errors: PlanTaskError[] };\n\n/**\n * Every check a plan's tasks must pass, on an already-parsed `{ tasks }`\n * object — what a plan is held to whichever channel it arrived on. All\n * problems are reported, the slice classification first, so the first is the\n * one the text path's corrective names.\n */\nexport function validatePlanTasks(\n obj: unknown,\n runners: RunnerId[],\n runnerModes?: Record<RunnerId, RunnerModeInfo[]>,\n autonomousDefault = false,\n): PlanTasksResult {\n const rawTasks = isRecord(obj) ? obj.tasks : undefined;\n if (!Array.isArray(rawTasks)) {\n return { ok: false, errors: [{ field: 'tasks', message: 'Invalid plan: missing tasks array' }] };\n }\n if (rawTasks.length === 0) {\n return { ok: false, errors: [{ field: 'tasks', message: 'Plan contained no tasks' }] };\n }\n\n const planned = rawTasks.map((t) => plannedTask(t, { runners, runnerModes, autonomousDefault }));\n const tasks = planned.map((p) => p.task);\n const errors = [\n ...sliceRequired(tasks),\n ...planned.flatMap((p) => p.errors),\n ...checkPlanInvariants(tasks, runners, runnerModes),\n ];\n\n if (errors.length > 0) return { ok: false, errors };\n const ignored = planned.flatMap((p) => p.ignored);\n const warnings = [\n ...(ignored.some((i) => i.field === 'taskMode')\n ? ['\"taskMode\" is not yours to set and was ignored: each task runs in its runner\\'s mode for the user\\'s autonomy level, or its read-only mode when you set \"readOnly\": true.']\n : []),\n ...ignored.filter((i) => i.field === 'readOnly').map((i) => `${i.message}: it was ignored.`),\n ];\n return warnings.length > 0 ? { ok: true, tasks, warnings } : { ok: true, tasks };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Whether a reply that failed {@link parsePlanJson} actually LOOKED like a\n * plan attempt — a balanced tasks-keyed object, or JSON that got cut off.\n * Distinguishes \"the model tried to emit a plan and botched it\" (worth a\n * corrective retry) from prose that merely mentions `\"tasks\"` (leave alone).\n */\nexport function looksLikePlanAttempt(raw: string): boolean {\n const { matches, sawUnbalanced } = extractObjectsWithKey(raw, PLAN_ENVELOPE_KEY);\n if (matches.length > 0) return true;\n return sawUnbalanced && raw.includes(`\"${PLAN_ENVELOPE_KEY}\"`);\n}\n\n/**\n * A whole plan says how each task is sliced: an edit may leave it to the\n * task's parent or the tasks it was made from, a submitted plan may not.\n */\nfunction sliceRequired(tasks: readonly Task[]): PlanTaskError[] {\n return flattenTasksWithParents(tasks).flatMap(({ task: t, parent }) => {\n const where = taskWhere(t.title, parent?.title);\n const errors: PlanTaskError[] = [];\n if (!t.sliceType) errors.push({ taskId: t.id, field: 'sliceType', message: `${where} is missing sliceType. Every task must specify sliceType (\"AFK\" or \"HITL\").` });\n if (t.type === 'ai' && !t.autonomy) errors.push({ taskId: t.id, field: 'autonomy', message: `AI ${where} is missing autonomy. Every AI task must specify autonomy (\"AFK\" or \"HITL\").` });\n return errors;\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -68,18 +68,18 @@ function dependencyTargetErrors(tasks, taskId, dependencies) {
|
|
|
68
68
|
}
|
|
69
69
|
function findDependencyCycle(tasks) {
|
|
70
70
|
const byId = new Map([...walkPlan(tasks)].map(({ task }) => [task.id, task]));
|
|
71
|
-
const
|
|
71
|
+
const path2 = [];
|
|
72
72
|
const done = /* @__PURE__ */ new Set();
|
|
73
73
|
const visit = (id) => {
|
|
74
74
|
if (done.has(id)) return null;
|
|
75
|
-
const at =
|
|
76
|
-
if (at >= 0) return
|
|
77
|
-
|
|
75
|
+
const at = path2.indexOf(id);
|
|
76
|
+
if (at >= 0) return path2.slice(at);
|
|
77
|
+
path2.push(id);
|
|
78
78
|
for (const dep of byId.get(id)?.dependencies ?? []) {
|
|
79
79
|
const hit = visit(dep);
|
|
80
80
|
if (hit) return hit;
|
|
81
81
|
}
|
|
82
|
-
|
|
82
|
+
path2.pop();
|
|
83
83
|
done.add(id);
|
|
84
84
|
return null;
|
|
85
85
|
};
|
|
@@ -101,6 +101,138 @@ function canSetDependencies(tasks, taskId, dependencies) {
|
|
|
101
101
|
return cycle ? { ok: false, error: `Dependency cycle detected: ${[...cycle, cycle[0]].map((t) => `"${t.title}"`).join(" \u2192 ")}` } : { ok: true };
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
// src/services/repoPath.ts
|
|
105
|
+
var SELF_REPO = ".";
|
|
106
|
+
|
|
107
|
+
// src/services/isolationRecord.ts
|
|
108
|
+
import * as path from "path";
|
|
109
|
+
function integrationBranchFor(runId) {
|
|
110
|
+
return `ordewell/${runId}/integration`;
|
|
111
|
+
}
|
|
112
|
+
function repoRootOf(workspaceRoot, repoPath) {
|
|
113
|
+
return path.join(workspaceRoot, repoPath);
|
|
114
|
+
}
|
|
115
|
+
function noRemoval() {
|
|
116
|
+
return { preserved: [], refused: [] };
|
|
117
|
+
}
|
|
118
|
+
var PrepareFailedError = class extends Error {
|
|
119
|
+
constructor(failure, preserved) {
|
|
120
|
+
super(failure instanceof Error ? failure.message : String(failure));
|
|
121
|
+
this.failure = failure;
|
|
122
|
+
this.preserved = preserved;
|
|
123
|
+
this.name = failure instanceof Error ? failure.name : "Error";
|
|
124
|
+
this.cause = failure;
|
|
125
|
+
}
|
|
126
|
+
failure;
|
|
127
|
+
preserved;
|
|
128
|
+
};
|
|
129
|
+
function absorbRemoval(into, from) {
|
|
130
|
+
into.preserved.push(...from.preserved);
|
|
131
|
+
into.refused.push(...from.refused);
|
|
132
|
+
return into;
|
|
133
|
+
}
|
|
134
|
+
function layoutOf(run) {
|
|
135
|
+
return { repos: run.repos.map((r) => r.path), shared: [...run.shared] };
|
|
136
|
+
}
|
|
137
|
+
var ISOLATION_STATE = {
|
|
138
|
+
active: "active",
|
|
139
|
+
merged: "integrated",
|
|
140
|
+
conflict: "conflict",
|
|
141
|
+
repairing: "repairing",
|
|
142
|
+
kept: "kept",
|
|
143
|
+
failed: "kept"
|
|
144
|
+
};
|
|
145
|
+
var isKnownStatus = (status) => Object.prototype.hasOwnProperty.call(ISOLATION_STATE, status);
|
|
146
|
+
function changedReposOf(record) {
|
|
147
|
+
return Object.entries(record.repos).filter(([, r]) => r.changed).map(([repoPath]) => repoPath);
|
|
148
|
+
}
|
|
149
|
+
function taskIsolationOf(record, repairLimit) {
|
|
150
|
+
return {
|
|
151
|
+
state: ISOLATION_STATE[record.status],
|
|
152
|
+
branch: record.branch,
|
|
153
|
+
worktree: record.workspace,
|
|
154
|
+
repos: changedReposOf(record),
|
|
155
|
+
...record.conflictRepo ? { conflictRepo: record.conflictRepo } : {},
|
|
156
|
+
...record.conflictFiles?.length ? { conflictFiles: record.conflictFiles } : {},
|
|
157
|
+
...record.repairs && repairLimit !== void 0 ? { repair: { attempt: record.repairs, limit: repairLimit } } : {},
|
|
158
|
+
...record.repairedFiles?.length ? { repairedFiles: record.repairedFiles } : {}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function handoffOf(run) {
|
|
162
|
+
const merged = Object.values(run.tasks).filter((r) => r.status === "merged" && !r.inHead).sort((a, b) => a.order - b.order);
|
|
163
|
+
const entry = (r) => ({
|
|
164
|
+
taskId: r.taskId,
|
|
165
|
+
order: r.order,
|
|
166
|
+
title: r.title,
|
|
167
|
+
...r.repairedFiles?.length ? { repairedFiles: r.repairedFiles } : {}
|
|
168
|
+
});
|
|
169
|
+
return {
|
|
170
|
+
repos: run.repos.map((repo) => ({
|
|
171
|
+
path: repo.path,
|
|
172
|
+
integrationBranch: repo.integrationBranch,
|
|
173
|
+
baseRef: repo.baseRef,
|
|
174
|
+
...repo.baseBranch ? { baseBranch: repo.baseBranch } : {},
|
|
175
|
+
...repo.startedDetached ? { startedDetached: true } : {},
|
|
176
|
+
landed: merged.filter((r) => r.repos[repo.path]?.changed).map(entry)
|
|
177
|
+
})),
|
|
178
|
+
landed: merged.map(entry)
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function isAdr0013(run) {
|
|
182
|
+
return "integrationBranch" in run && typeof run.integrationBranch === "string";
|
|
183
|
+
}
|
|
184
|
+
function fromAdr0013Task(r) {
|
|
185
|
+
return {
|
|
186
|
+
taskId: r.taskId,
|
|
187
|
+
order: r.order,
|
|
188
|
+
title: r.title,
|
|
189
|
+
branch: r.branch,
|
|
190
|
+
workspace: r.worktree,
|
|
191
|
+
status: r.status,
|
|
192
|
+
// A merged task's branch was merged into the one repo there was; what it
|
|
193
|
+
// changed was never recorded, and landing is what `changed` stands for.
|
|
194
|
+
repos: { [SELF_REPO]: { worktree: r.worktree, linked: r.linked ?? [], ...r.status === "merged" ? { changed: true } : {} } },
|
|
195
|
+
...r.status === "conflict" ? { conflictRepo: SELF_REPO } : {}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function migratePlanIsolation(state) {
|
|
199
|
+
const { run, resolvers } = state;
|
|
200
|
+
if (!isAdr0013(run)) return { run, resolvers: resolvers ?? {} };
|
|
201
|
+
const repo = {
|
|
202
|
+
path: SELF_REPO,
|
|
203
|
+
root: repoRootOf(run.workspaceRoot, SELF_REPO),
|
|
204
|
+
baseRef: run.baseRef,
|
|
205
|
+
...run.baseBranch ? { baseBranch: run.baseBranch } : {},
|
|
206
|
+
integrationBranch: run.integrationBranch
|
|
207
|
+
};
|
|
208
|
+
const tasks = Object.fromEntries(Object.entries(run.tasks ?? {}).map(([id, r]) => [id, fromAdr0013Task(r)]));
|
|
209
|
+
return { run: { id: run.id, workspaceRoot: run.workspaceRoot, repos: [repo], shared: [], sharedRepos: [], tasks }, resolvers: resolvers ?? {} };
|
|
210
|
+
}
|
|
211
|
+
function migratePlanStateIsolation(plan) {
|
|
212
|
+
if (plan.isolation?.run) plan.isolation = migratePlanIsolation(plan.isolation);
|
|
213
|
+
}
|
|
214
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
215
|
+
var isArray = (value) => Array.isArray(value);
|
|
216
|
+
function carriesIsolation(plan) {
|
|
217
|
+
if (!isRecord(plan) || !isRecord(plan.isolation)) return false;
|
|
218
|
+
const { run } = plan.isolation;
|
|
219
|
+
if (!isRecord(run) || !isRecord(run.tasks)) return false;
|
|
220
|
+
if (isArray(run.repos)) {
|
|
221
|
+
return run.repos.some((repo) => isRecord(repo) && typeof repo.path === "string" && typeof repo.integrationBranch === "string" && typeof repo.baseRef === "string");
|
|
222
|
+
}
|
|
223
|
+
return typeof run.integrationBranch === "string" && typeof run.baseRef === "string";
|
|
224
|
+
}
|
|
225
|
+
function isolationViewOfPlan(plan, repairLimit) {
|
|
226
|
+
if (!carriesIsolation(plan)) return null;
|
|
227
|
+
const { run } = migratePlanIsolation(plan.isolation);
|
|
228
|
+
const tasks = {};
|
|
229
|
+
for (const [taskId, record] of Object.entries(run.tasks)) {
|
|
230
|
+
if (!isKnownStatus(record.status)) continue;
|
|
231
|
+
tasks[taskId] = taskIsolationOf(record, repairLimit);
|
|
232
|
+
}
|
|
233
|
+
return { tasks, handoff: handoffOf(run) };
|
|
234
|
+
}
|
|
235
|
+
|
|
104
236
|
// src/services/TaskRefs.ts
|
|
105
237
|
function taskOrderLabel(task, parent) {
|
|
106
238
|
return parent ? `${parent.label ?? parent.order}.${task.order}` : String(task.order);
|
|
@@ -169,6 +301,40 @@ function migrateLoadedTasks(tasks) {
|
|
|
169
301
|
migrateLoadedTasks(task.subtasks ?? []);
|
|
170
302
|
}
|
|
171
303
|
}
|
|
304
|
+
function dropRetiredPlanFields(plan) {
|
|
305
|
+
const raw = plan;
|
|
306
|
+
delete raw.researchBudget;
|
|
307
|
+
const settings = plan.runSettings;
|
|
308
|
+
if (settings) {
|
|
309
|
+
delete settings.researchMaxParallel;
|
|
310
|
+
delete settings.researchMaxPerMessage;
|
|
311
|
+
}
|
|
312
|
+
for (const turn of plan.pendingTurns ?? []) delete turn.budgetStarted;
|
|
313
|
+
for (const task of flattenTasks(plan.tasks)) delete task.researchWait;
|
|
314
|
+
}
|
|
315
|
+
function carryOverQueuedEdits(plan) {
|
|
316
|
+
const queued = plan.queuedMessages ?? [];
|
|
317
|
+
delete plan.queuedMessages;
|
|
318
|
+
if (queued.length === 0) return;
|
|
319
|
+
const carried = queued.map(({ id, text, timestamp, skills }) => ({ id, source: "user", text, timestamp, ...skills ? { skills } : {} }));
|
|
320
|
+
plan.pendingTurns = [...plan.pendingTurns ?? [], ...carried];
|
|
321
|
+
}
|
|
322
|
+
function migrateLoadedPlan(plan, { adopting = true } = {}) {
|
|
323
|
+
plan.approved ??= plan.status !== "draft" && plan.status !== "rejected";
|
|
324
|
+
if (!plan.approvedTasks && plan.approved) {
|
|
325
|
+
plan.approvedTasks = flattenTasks(plan.tasks).map((t) => t.id);
|
|
326
|
+
}
|
|
327
|
+
if (adopting) {
|
|
328
|
+
for (const task of flattenTasks(plan.tasks)) delete task.resumeAt;
|
|
329
|
+
}
|
|
330
|
+
migratePlanStateIsolation(plan);
|
|
331
|
+
delete plan.runnerTransport;
|
|
332
|
+
for (const key of ["supervisorLevel", "mergeAtGatesAutomatically", "supervisorRetryLimit", "wakeUpReadBudget"]) {
|
|
333
|
+
delete plan[key];
|
|
334
|
+
}
|
|
335
|
+
dropRetiredPlanFields(plan);
|
|
336
|
+
carryOverQueuedEdits(plan);
|
|
337
|
+
}
|
|
172
338
|
function isUserMessage(entry) {
|
|
173
339
|
return entry.role === "user" && entry.kind !== "skill_load" && entry.kind !== "wake_up";
|
|
174
340
|
}
|
|
@@ -254,12 +420,13 @@ function migratePlanState(raw) {
|
|
|
254
420
|
}
|
|
255
421
|
function createTask(overrides = {}) {
|
|
256
422
|
const skills = skillNames(overrides.skills);
|
|
423
|
+
const type = overrides.type ?? "ai";
|
|
257
424
|
return {
|
|
258
425
|
id: overrides.id ?? uuidv4(),
|
|
259
426
|
order: overrides.order ?? 0,
|
|
260
427
|
title: overrides.title ?? "",
|
|
261
428
|
description: overrides.description ?? "",
|
|
262
|
-
type
|
|
429
|
+
type,
|
|
263
430
|
status: overrides.status ?? "pending",
|
|
264
431
|
dependencies: overrides.dependencies ?? [],
|
|
265
432
|
prompt: overrides.prompt,
|
|
@@ -267,16 +434,25 @@ function createTask(overrides = {}) {
|
|
|
267
434
|
subtasks: overrides.subtasks ?? [],
|
|
268
435
|
verdict: overrides.verdict,
|
|
269
436
|
outputSummary: overrides.outputSummary,
|
|
437
|
+
...overrides.handoff ? { handoff: structuredClone(overrides.handoff) } : {},
|
|
438
|
+
...overrides.attempted ? { attempted: true } : {},
|
|
270
439
|
...overrides.attemptUsage ? { attemptUsage: structuredClone(overrides.attemptUsage) } : {},
|
|
271
440
|
assignedModel: overrides.assignedModel,
|
|
272
441
|
assignedRunner: overrides.assignedRunner ?? DEFAULT_RUNNERS[0],
|
|
273
|
-
|
|
442
|
+
// A script task runs in no mode (ADR-0035): one given it would read as a mode it runs in.
|
|
443
|
+
taskMode: overrides.taskMode ?? (type === "script" ? void 0 : "build"),
|
|
274
444
|
autonomy: overrides.autonomy,
|
|
275
445
|
sliceType: overrides.sliceType,
|
|
276
446
|
userStoriesCovered: overrides.userStoriesCovered,
|
|
277
447
|
...opsFlag(overrides.ops, overrides.type) ? { ops: true } : {},
|
|
278
448
|
...skills ? { skills } : {},
|
|
279
|
-
...overrides.addedBy === "supervisor" ? { addedBy: "supervisor" } : {}
|
|
449
|
+
...overrides.addedBy === "supervisor" ? { addedBy: "supervisor" } : {},
|
|
450
|
+
...overrides.commands !== void 0 ? { commands: overrides.commands.map((argv) => [...argv]) } : {},
|
|
451
|
+
...overrides.script !== void 0 ? { script: overrides.script } : {},
|
|
452
|
+
...overrides.timeoutMs !== void 0 ? { timeoutMs: overrides.timeoutMs } : {},
|
|
453
|
+
...overrides.loop ? { loop: { body: [...overrides.loop.body], maxRounds: overrides.loop.maxRounds } } : {},
|
|
454
|
+
...overrides.loopRound !== void 0 ? { loopRound: overrides.loopRound } : {},
|
|
455
|
+
...overrides.research !== void 0 ? { research: overrides.research } : {}
|
|
280
456
|
};
|
|
281
457
|
}
|
|
282
458
|
var SKILL_NAME = new RegExp(`^${SKILL_NAME_PATTERN}$`);
|
|
@@ -295,6 +471,9 @@ function inheritedSkills(chosen, inherited) {
|
|
|
295
471
|
function opsFlag(value, type = "ai") {
|
|
296
472
|
return value === true && type === "ai" ? true : void 0;
|
|
297
473
|
}
|
|
474
|
+
function runsAsOps(task) {
|
|
475
|
+
return task.type === "script" || opsFlag(task.ops, task.type) === true;
|
|
476
|
+
}
|
|
298
477
|
function inheritedOps(from, chosen) {
|
|
299
478
|
return chosen ?? from.every((t) => opsFlag(t.ops) === true);
|
|
300
479
|
}
|
|
@@ -342,11 +521,19 @@ function addTaskToPlan(tasks, partial) {
|
|
|
342
521
|
}
|
|
343
522
|
function removeTaskFromPlan(tasks, taskId) {
|
|
344
523
|
const removed = subtreeIds(tasks, taskId);
|
|
345
|
-
const prune = (level) => level.filter((t) => !removed.has(t.id)).map((t) =>
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
524
|
+
const prune = (level) => level.filter((t) => !removed.has(t.id)).map((t) => {
|
|
525
|
+
const pruned = {
|
|
526
|
+
...t,
|
|
527
|
+
dependencies: t.dependencies.filter((depId) => !removed.has(depId)),
|
|
528
|
+
subtasks: prune(t.subtasks)
|
|
529
|
+
};
|
|
530
|
+
if (!t.loop?.body.some((id) => removed.has(id))) return pruned;
|
|
531
|
+
const body = t.loop.body.filter((id) => !removed.has(id));
|
|
532
|
+
if (body.length > 0) return { ...pruned, loop: { ...t.loop, body } };
|
|
533
|
+
delete pruned.loop;
|
|
534
|
+
delete pruned.loopRound;
|
|
535
|
+
return pruned;
|
|
536
|
+
});
|
|
350
537
|
return renumberTasks(prune(tasks));
|
|
351
538
|
}
|
|
352
539
|
function updateTaskInPlan(tasks, taskId, changes) {
|
|
@@ -364,6 +551,9 @@ function renumberTasks(tasks) {
|
|
|
364
551
|
subtasks: renumberTasks(t.subtasks)
|
|
365
552
|
}));
|
|
366
553
|
}
|
|
554
|
+
function waitsMidAttempt(task) {
|
|
555
|
+
return task.status === "awaiting_user" && (task.awaitingReason === "checkpoint" || task.awaitingReason === "input");
|
|
556
|
+
}
|
|
367
557
|
function pausedOnLimit(task) {
|
|
368
558
|
return task.status === "awaiting_user" && task.awaitingReason === "usage-limit";
|
|
369
559
|
}
|
|
@@ -377,7 +567,7 @@ function approvalRecordOf(plan) {
|
|
|
377
567
|
};
|
|
378
568
|
}
|
|
379
569
|
function waitsForApproval(task, record) {
|
|
380
|
-
return record.approved === true && task.type
|
|
570
|
+
return record.approved === true && task.type !== "user" && (task.status === "pending" || task.status === "approved") && !(record.approvedTasks ?? []).includes(task.id);
|
|
381
571
|
}
|
|
382
572
|
function stampOrigin(before, after, bySupervisor) {
|
|
383
573
|
const prior = new Map(flattenTasks([...before]).map((t) => [t.id, t]));
|
|
@@ -404,20 +594,26 @@ function keepExecutionState(current, rewrite) {
|
|
|
404
594
|
const result = tasks.map((t) => {
|
|
405
595
|
const prior = existing.get(t.id);
|
|
406
596
|
if (prior && SETTLED_STATUSES.has(prior.status)) return { ...prior };
|
|
597
|
+
if (prior?.status === "failed" && prior.type === "script" !== (t.type === "script")) return { ...prior };
|
|
407
598
|
return {
|
|
408
599
|
...t,
|
|
409
600
|
status: prior?.status ?? "pending",
|
|
410
601
|
skipReason: prior?.skipReason,
|
|
411
602
|
verdict: prior?.verdict,
|
|
412
603
|
outputSummary: prior?.outputSummary,
|
|
604
|
+
handoff: prior?.handoff,
|
|
413
605
|
runnerSessionId: prior?.runnerSessionId,
|
|
414
606
|
runnerSessionOwner: prior?.runnerSessionOwner,
|
|
415
607
|
carriedFrom: prior?.carriedFrom,
|
|
416
608
|
attemptSkills: prior?.attemptSkills,
|
|
609
|
+
attempted: prior?.attempted,
|
|
417
610
|
attemptUsage: prior?.attemptUsage,
|
|
611
|
+
loopRound: prior?.loopRound,
|
|
418
612
|
// Where a task that has run ran is fixed, like its runner session (ADR-0020).
|
|
419
613
|
...prior?.status === "failed" ? { ops: prior.ops } : {},
|
|
420
614
|
forcedPastGate: prior?.forcedPastGate,
|
|
615
|
+
reportTo: prior?.reportTo,
|
|
616
|
+
researchOrigin: prior?.researchOrigin,
|
|
421
617
|
subtasks: overlay(t.subtasks ?? [], prior?.subtasks ?? [])
|
|
422
618
|
};
|
|
423
619
|
});
|
|
@@ -443,6 +639,19 @@ export {
|
|
|
443
639
|
dependencyTargetErrors,
|
|
444
640
|
findDependencyCycle,
|
|
445
641
|
canSetDependencies,
|
|
642
|
+
SELF_REPO,
|
|
643
|
+
integrationBranchFor,
|
|
644
|
+
repoRootOf,
|
|
645
|
+
noRemoval,
|
|
646
|
+
PrepareFailedError,
|
|
647
|
+
absorbRemoval,
|
|
648
|
+
layoutOf,
|
|
649
|
+
changedReposOf,
|
|
650
|
+
taskIsolationOf,
|
|
651
|
+
handoffOf,
|
|
652
|
+
migratePlanIsolation,
|
|
653
|
+
migratePlanStateIsolation,
|
|
654
|
+
isolationViewOfPlan,
|
|
446
655
|
taskOrderLabel,
|
|
447
656
|
taskRef,
|
|
448
657
|
titledTaskRef,
|
|
@@ -452,6 +661,8 @@ export {
|
|
|
452
661
|
resolveUserTaskRef,
|
|
453
662
|
isAwaitingReason,
|
|
454
663
|
migrateLoadedTasks,
|
|
664
|
+
dropRetiredPlanFields,
|
|
665
|
+
migrateLoadedPlan,
|
|
455
666
|
isUserMessage,
|
|
456
667
|
migratePlanState,
|
|
457
668
|
createTask,
|
|
@@ -459,6 +670,7 @@ export {
|
|
|
459
670
|
skillNames,
|
|
460
671
|
inheritedSkills,
|
|
461
672
|
opsFlag,
|
|
673
|
+
runsAsOps,
|
|
462
674
|
inheritedOps,
|
|
463
675
|
DEFAULT_RUNNERS,
|
|
464
676
|
createEmptyPlan,
|
|
@@ -469,6 +681,7 @@ export {
|
|
|
469
681
|
removeTaskFromPlan,
|
|
470
682
|
updateTaskInPlan,
|
|
471
683
|
renumberTasks,
|
|
684
|
+
waitsMidAttempt,
|
|
472
685
|
pausedOnLimit,
|
|
473
686
|
waitsForReset,
|
|
474
687
|
approvalRecordOf,
|
|
@@ -476,4 +689,4 @@ export {
|
|
|
476
689
|
stampOrigin,
|
|
477
690
|
keepExecutionState
|
|
478
691
|
};
|
|
479
|
-
//# sourceMappingURL=chunk-
|
|
692
|
+
//# sourceMappingURL=chunk-QTZ5XTBY.mjs.map
|