@ordewell/core 0.5.4 → 0.5.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.
Files changed (56) hide show
  1. package/dist/IFileSystem-BkPX7mLD.d.mts +76 -0
  2. package/dist/IFileSystem-C0l-4MGT.d.ts +76 -0
  3. package/dist/{ModeResolver-Dkig8ghQ.d.ts → ModeResolver--16lh7dS.d.mts} +1 -1
  4. package/dist/{ModeResolver-DVJ7HV3k.d.mts → ModeResolver-CjpG5Wli.d.ts} +1 -1
  5. package/dist/Task-Dyxp67s2.d.mts +2086 -0
  6. package/dist/Task-Dyxp67s2.d.ts +2086 -0
  7. package/dist/{chunk-T2S5O36I.mjs → chunk-C44UWIAD.mjs} +6 -6
  8. package/dist/chunk-C44UWIAD.mjs.map +1 -0
  9. package/dist/{chunk-JVMDEHRQ.mjs → chunk-EDGUFCIR.mjs} +77 -37
  10. package/dist/chunk-EDGUFCIR.mjs.map +1 -0
  11. package/dist/{chunk-XWOUIA6A.mjs → chunk-JBEFAJ2W.mjs} +2 -2
  12. package/dist/chunk-ROVYWEBI.mjs +2115 -0
  13. package/dist/chunk-ROVYWEBI.mjs.map +1 -0
  14. package/dist/index.d.mts +1647 -758
  15. package/dist/index.d.ts +1647 -758
  16. package/dist/index.js +9301 -4857
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +6222 -2968
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/order-labels.d.mts +3 -1
  21. package/dist/order-labels.d.ts +3 -1
  22. package/dist/{parsing-DRp4dPC0.d.mts → parsing-DPEpAszP.d.mts} +11 -3
  23. package/dist/{parsing-CF_grC29.d.ts → parsing-EcmCsF1y.d.ts} +11 -3
  24. package/dist/parsing.d.mts +5 -3
  25. package/dist/parsing.d.ts +5 -3
  26. package/dist/parsing.js.map +1 -1
  27. package/dist/parsing.mjs +2 -2
  28. package/dist/plan-utils-BAvW3hvl.d.mts +877 -0
  29. package/dist/plan-utils-BMyEiDKv.d.ts +877 -0
  30. package/dist/plan-utils.d.mts +5 -4
  31. package/dist/plan-utils.d.ts +5 -4
  32. package/dist/plan-utils.js +956 -8
  33. package/dist/plan-utils.js.map +1 -1
  34. package/dist/plan-utils.mjs +47 -7
  35. package/dist/testing.d.mts +57 -5
  36. package/dist/testing.d.ts +57 -5
  37. package/dist/testing.js +96 -2
  38. package/dist/testing.js.map +1 -1
  39. package/dist/testing.mjs +94 -2
  40. package/dist/testing.mjs.map +1 -1
  41. package/package.json +2 -1
  42. package/skills/grilling/SKILL.md +6 -16
  43. package/skills/improve-codebase-architecture/SKILL.md +1 -1
  44. package/dist/ApprovalPolicy-BVhGdECT.d.mts +0 -79
  45. package/dist/ApprovalPolicy-BVhGdECT.d.ts +0 -79
  46. package/dist/ITerminalRunner-Bd-vAJnw.d.ts +0 -561
  47. package/dist/ITerminalRunner-ByeoLF57.d.mts +0 -561
  48. package/dist/Task-BxQkPlXO.d.mts +0 -664
  49. package/dist/Task-BxQkPlXO.d.ts +0 -664
  50. package/dist/chunk-GWPIYDQW.mjs +0 -945
  51. package/dist/chunk-GWPIYDQW.mjs.map +0 -1
  52. package/dist/chunk-JVMDEHRQ.mjs.map +0 -1
  53. package/dist/chunk-T2S5O36I.mjs.map +0 -1
  54. package/dist/plan-utils-CkNbqAmS.d.ts +0 -329
  55. package/dist/plan-utils-CtB3_Ovf.d.mts +0 -329
  56. /package/dist/{chunk-XWOUIA6A.mjs.map → chunk-JBEFAJ2W.mjs.map} +0 -0
@@ -1,945 +0,0 @@
1
- import {
2
- PlanParseError,
3
- TASK_OPS_ENVELOPE_KEY,
4
- addTaskToPlan,
5
- createTask,
6
- escapeControlCharsInStrings,
7
- extractObjectsWithKey,
8
- flattenTasks,
9
- removeTaskFromPlan,
10
- renumberTasks,
11
- stripTrailingCommas,
12
- updateTaskInPlan
13
- } from "./chunk-JVMDEHRQ.mjs";
14
-
15
- // src/services/ModelAllowlistResolver.ts
16
- function effectiveAllowlist(allowlist, runner, modelsByRunner) {
17
- if (!allowlist || allowlist.length === 0) return void 0;
18
- if (!modelsByRunner) return allowlist;
19
- const own = new Set((modelsByRunner[runner] ?? []).map((m) => m.modelId));
20
- const elsewhere = /* @__PURE__ */ new Set();
21
- for (const [other, models] of Object.entries(modelsByRunner)) {
22
- if (other === runner) continue;
23
- for (const m of models ?? []) elsewhere.add(m.modelId);
24
- }
25
- const kept = allowlist.filter((id) => own.has(id) || !elsewhere.has(id));
26
- return kept.length > 0 ? kept : void 0;
27
- }
28
- function filterModelsForPrompt(modelsByRunner, perRunnerAllowlist) {
29
- const result = {};
30
- for (const [runner, models] of Object.entries(modelsByRunner)) {
31
- if (!models) continue;
32
- const allowed = effectiveAllowlist(perRunnerAllowlist[runner], runner, modelsByRunner);
33
- if (!allowed) {
34
- result[runner] = models;
35
- continue;
36
- }
37
- const allowedSet = new Set(allowed);
38
- const intersection = models.filter((m) => allowedSet.has(m.modelId));
39
- const covered = new Set(intersection.map((m) => m.modelId));
40
- const missing = allowed.filter((id) => !covered.has(id)).map((id) => ({ modelId: id, modelLabel: id.split("/").pop() || id, variants: [] }));
41
- result[runner] = [...intersection, ...missing];
42
- }
43
- return result;
44
- }
45
- var EFFORT_LADDER = ["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"];
46
- var EFFORT_ALIASES = { disabled: "none", adaptive: "medium" };
47
- function clampThinkingEffort(effort, variants) {
48
- if (!effort) return void 0;
49
- if (variants.length === 0) return void 0;
50
- const ids = new Set(variants.map((v) => v.id));
51
- if (ids.has(effort)) return effort;
52
- const canon = EFFORT_ALIASES[effort] ?? effort;
53
- if (ids.has(canon)) return canon;
54
- const idx = EFFORT_LADDER.indexOf(canon);
55
- if (idx < 0) return void 0;
56
- for (let d = 1; d < EFFORT_LADDER.length; d++) {
57
- const lower = EFFORT_LADDER[idx - d];
58
- if (lower && ids.has(lower)) return lower;
59
- const higher = EFFORT_LADDER[idx + d];
60
- if (higher && ids.has(higher)) return higher;
61
- }
62
- return void 0;
63
- }
64
- function coerceAssignments(tasks, perRunnerAllowlist, allowedRunners, modelsByRunner) {
65
- const clampVariant = (task) => {
66
- if (!task.assignedModel || !modelsByRunner) return task;
67
- const model = modelsByRunner[task.assignedRunner]?.find(
68
- (m) => m.modelId === task.assignedModel.modelId
69
- );
70
- if (!model) return task;
71
- const clamped = clampThinkingEffort(task.assignedModel.thinkingEffort, model.variants);
72
- return {
73
- ...task,
74
- assignedModel: {
75
- ...task.assignedModel,
76
- thinkingEffort: clamped,
77
- availableVariants: model.variants.map((v) => v.id)
78
- }
79
- };
80
- };
81
- const allowedFor = (runner) => effectiveAllowlist(perRunnerAllowlist[runner], runner, modelsByRunner);
82
- const assignmentFor = (runner, modelId) => {
83
- const known = modelsByRunner?.[runner]?.find((m) => m.modelId === modelId);
84
- return {
85
- modelId,
86
- modelLabel: known?.modelLabel ?? modelId,
87
- thinkingEffort: void 0,
88
- ...known ? { availableVariants: known.variants.map((v) => v.id) } : {}
89
- };
90
- };
91
- return tasks.map((task) => {
92
- if (task.type === "user") return task;
93
- if (allowedRunners && !allowedRunners.includes(task.assignedRunner)) {
94
- const fallbackRunner = allowedRunners[0];
95
- const fallbackModels = allowedFor(fallbackRunner);
96
- const fallbackModel = fallbackModels?.length ? assignmentFor(fallbackRunner, fallbackModels[0]) : modelsByRunner?.[fallbackRunner]?.[0] ? assignmentFor(fallbackRunner, modelsByRunner[fallbackRunner][0].modelId) : task.assignedModel;
97
- return clampVariant({
98
- ...task,
99
- assignedRunner: fallbackRunner,
100
- assignedModel: fallbackModel
101
- });
102
- }
103
- const allowlist = allowedFor(task.assignedRunner);
104
- if (!task.assignedModel) {
105
- if (allowlist && allowlist.length > 0) {
106
- return clampVariant({ ...task, assignedModel: assignmentFor(task.assignedRunner, allowlist[0]) });
107
- }
108
- const discovered = modelsByRunner?.[task.assignedRunner]?.[0];
109
- if (discovered) {
110
- return clampVariant({ ...task, assignedModel: assignmentFor(task.assignedRunner, discovered.modelId) });
111
- }
112
- return task;
113
- }
114
- if (!allowlist || allowlist.length === 0) return clampVariant(task);
115
- if (allowlist.includes(task.assignedModel.modelId)) return clampVariant(task);
116
- return { ...task, assignedModel: assignmentFor(task.assignedRunner, allowlist[0]) };
117
- });
118
- }
119
-
120
- // src/services/TaskOps.ts
121
- import { v4 as uuidv4 } from "uuid";
122
-
123
- // src/services/TaskEditValidator.ts
124
- var AI_ONLY_FIELDS = ["assignedModel", "thinkingEffort", "taskMode", "autonomy"];
125
- function checkModelAndModeValidity(runner, assignedModel, taskMode, catalog) {
126
- if (!catalog) return { ok: true };
127
- if (assignedModel) {
128
- const models = catalog.modelsByRunner[runner];
129
- if (models) {
130
- const modelId = assignedModel.modelId;
131
- const allowed = effectiveAllowlist(catalog.perRunnerAllowlist?.[runner], runner, catalog.modelsByRunner);
132
- if (!models.some((m) => m.modelId === modelId) && !allowed?.includes(modelId)) {
133
- return { ok: false, error: `Runner "${runner}" does not offer model "${modelId}"` };
134
- }
135
- if (allowed && !allowed.includes(modelId)) {
136
- return { ok: false, error: `Model "${modelId}" is excluded by the allowlist for runner "${runner}"` };
137
- }
138
- }
139
- }
140
- if (taskMode !== void 0) {
141
- const modes = catalog.runnerModes[runner];
142
- if (modes && modes.length > 0 && !modes.some((m) => m.id === taskMode)) {
143
- return { ok: false, error: `Runner "${runner}" has no task mode "${taskMode}" \u2014 valid modes: ${modes.map((m) => m.id).join(", ")}` };
144
- }
145
- }
146
- return { ok: true };
147
- }
148
- function typeCoherenceCheck(target, changes) {
149
- if (!changes.type || changes.type === target.type) return null;
150
- if (changes.type === "user") {
151
- if (!Array.isArray(changes.userSteps) || changes.userSteps.length === 0) {
152
- return { ok: false, error: `"${target.title}" cannot become a manual task without user steps \u2014 include "userSteps" in this edit` };
153
- }
154
- return { ok: true, clear: AI_ONLY_FIELDS.filter((f) => target[f] !== void 0) };
155
- }
156
- if (!changes.prompt || !changes.prompt.trim()) {
157
- return { ok: false, error: `"${target.title}" cannot become an AI task without a prompt \u2014 include "prompt" in this edit` };
158
- }
159
- return { ok: true, clear: target.userSteps && target.userSteps.length > 0 ? ["userSteps"] : [] };
160
- }
161
- function validateTaskEdit(actor, tasks, taskId, changes, catalog) {
162
- const target = tasks.find((t) => t.id === taskId);
163
- if (!target) return { ok: false, error: "Task not found" };
164
- if (actor === "planner") {
165
- if (target.status === "in_progress") return { ok: false, error: `"${target.title}" is running and cannot be modified` };
166
- if (target.status === "completed") return { ok: false, error: `"${target.title}" is completed and cannot be modified` };
167
- }
168
- if (changes.dependencies) {
169
- const depCheck = canSetDependencies(tasks, taskId, changes.dependencies);
170
- if (!depCheck.ok) return depCheck;
171
- }
172
- const coherence = typeCoherenceCheck(target, changes);
173
- if (coherence && !coherence.ok) return coherence;
174
- const runner = changes.assignedRunner ?? target.assignedRunner;
175
- const validity = checkModelAndModeValidity(runner, changes.assignedModel, changes.taskMode, catalog);
176
- if (!validity.ok) return validity;
177
- return coherence ?? { ok: true };
178
- }
179
-
180
- // src/services/TaskOps.ts
181
- var UPDATABLE_FIELDS = [
182
- "title",
183
- "description",
184
- "prompt",
185
- "dependencies",
186
- "assignedRunner",
187
- "assignedModel",
188
- "taskMode",
189
- "thinkingEffort",
190
- "type",
191
- "userSteps",
192
- "autonomy",
193
- "sliceType"
194
- ];
195
- function taskOpsProtocol(executionNote = "") {
196
- const updateChanges = UPDATABLE_FIELDS.map((f) => f === "dependencies" ? '"dependencies"?:["<id or #order>"]' : `"${f}"?`).join(",");
197
- return [
198
- "- To modify specific tasks, reply with ONLY this JSON object:",
199
- ' {"taskOps": [',
200
- ` {"op":"update","taskId":"<id or #order>","changes":{${updateChanges}}},`,
201
- ' {"op":"add","task":{"title","description","prompt","dependencies":["<id or #order>"],"assignedRunner"?,"assignedModel"?},"handle"?:"<name>"},',
202
- ' {"op":"remove","taskId":"<id or #order>"},',
203
- ' {"op":"reorder","taskIds":["<id or #order>", "... every task exactly once"]}, // only to re-prioritise INDEPENDENT tasks',
204
- ' {"op":"merge","taskIds":["<id or #order>", "..."],"merged":{"title","description","prompt"?,"assignedRunner"?,"assignedModel"?,...},"handle"?:"<name>"},',
205
- ' {"op":"split","taskId":"<id or #order>","parts":[{"title","description","prompt"?,"assignedRunner"?,"assignedModel"?,...}, ...],"handle"?:"<name>"},',
206
- ` {"op":"rearm","taskId":"<id or #order>","changes"?:{${updateChanges}}}`,
207
- " ]}",
208
- '- For sweeping changes, you may instead emit a full {"tasks":[...]} plan JSON.',
209
- 'Every "<id or #order>" ref in a batch resolves against the plan shown above, before any op in the batch runs \u2014 an earlier remove/merge/split never shifts what a later "#N" means.',
210
- 'Give "add", "merge", or "split" a "handle" (any name you choose, unused elsewhere in this batch) to let a LATER op in the same batch reference the task it produces \u2014 for "split", the handle names its last part. A handle used before its defining op is rejected.',
211
- 'When creating or changing tasks, set "assignedRunner" and "assignedModel" ({"modelId","modelLabel","thinkingEffort"?}) using only the runners and models listed in <available_models>.',
212
- 'Keep dependencies consistent: no cycles, no references to removed tasks, and never touch running or completed tasks \u2014 "rearm" is the one exception, below.',
213
- 'Just declare the dependencies you want \u2014 display order is repaired for you afterwards, so a rewire or a newly added prerequisite never needs a "reorder" op. Only a task that is running or completed cannot be shifted, so an edit that would need one to move is rejected.' + executionNote,
214
- 'Flipping "type" between "ai" and "user" is a content change, not just a label: an update to "user" needs "userSteps" in the SAME op, and an update to "ai" needs "prompt" in the SAME op \u2014 the model/mode/effort/autonomy fields (flipping to "user") or the userSteps (flipping to "ai") are cleared automatically.',
215
- '"rearm" puts a failed OR completed task back to pending \u2014 verdict and output summary are cleared, any dependents it had blocked are released, and it may carry field changes (e.g. a corrected "prompt") applied in the same op. A running task cannot be re-armed. Never rearm a task just to relabel it \u2014 only when it should actually run again.'
216
- ];
217
- }
218
- function textHasTaskOps(text) {
219
- return text.includes(`"${TASK_OPS_ENVELOPE_KEY}"`);
220
- }
221
- function parseTaskOpsJson(text) {
222
- const { matches, fallback } = extractObjectsWithKey(text, TASK_OPS_ENVELOPE_KEY);
223
- const candidates = matches.length > 0 ? [...matches].reverse() : [fallback.json];
224
- let lastError = null;
225
- for (const json of candidates) {
226
- try {
227
- return parseTaskOpsObject(json, text);
228
- } catch (err) {
229
- if (!(err instanceof PlanParseError)) throw err;
230
- if (!lastError) lastError = err;
231
- }
232
- }
233
- throw lastError ?? new PlanParseError("No JSON object found in task-ops reply", text);
234
- }
235
- function parseTaskOpsObject(json, text) {
236
- let parsed;
237
- try {
238
- parsed = JSON.parse(escapeControlCharsInStrings(stripTrailingCommas(json)));
239
- } catch (err) {
240
- throw new PlanParseError(`Task-ops JSON is invalid: ${err instanceof Error ? err.message : String(err)}`, text);
241
- }
242
- const ops = parsed.taskOps;
243
- if (!Array.isArray(ops) || ops.length === 0) {
244
- throw new PlanParseError('"taskOps" must be a non-empty array', text);
245
- }
246
- for (const op of ops) {
247
- if (typeof op !== "object" || op === null || typeof op.op !== "string") {
248
- throw new PlanParseError('Every task op needs an "op" field (update | add | remove | reorder | merge | split)', text);
249
- }
250
- }
251
- return ops;
252
- }
253
- function resolveRef(ref, originalScope, liveScope, handleOwner, handleId, opIndex) {
254
- if (typeof ref !== "string" && typeof ref !== "number") return { error: `invalid reference "${String(ref)}"` };
255
- const s = String(ref).trim();
256
- const ownerIdx = handleOwner.get(s);
257
- if (ownerIdx !== void 0) {
258
- if (ownerIdx >= opIndex) {
259
- return { error: `handle "${s}" is defined later in this batch (op ${ownerIdx + 1}) and cannot be referenced yet` };
260
- }
261
- const id = handleId.get(s);
262
- if (!id) return { error: `handle "${s}" was never resolved` };
263
- return { id };
264
- }
265
- const byLiveId = liveScope.find((t) => t.id === s);
266
- if (byLiveId) return { id: byLiveId.id };
267
- const orderStr = s.startsWith("#") ? s.slice(1) : s;
268
- if (/^\d+$/.test(orderStr)) {
269
- const order = Number(orderStr);
270
- const byOrder = originalScope.filter((t) => t.order === order);
271
- if (byOrder.length === 1) return { id: byOrder[0].id };
272
- }
273
- const byTitle = originalScope.find((t) => t.title === s);
274
- if (byTitle) return { id: byTitle.id };
275
- return { error: `task "${s}" not found` };
276
- }
277
- function refCollidesWithExisting(name, originalFlatAll) {
278
- if (originalFlatAll.some((t) => t.id === name)) return true;
279
- const orderStr = name.startsWith("#") ? name.slice(1) : name;
280
- if (/^\d+$/.test(orderStr) && originalFlatAll.some((t) => t.order === Number(orderStr))) return true;
281
- return originalFlatAll.some((t) => t.title === name);
282
- }
283
- function findInTasks(tasks, id) {
284
- return flattenTasks(tasks).find((t) => t.id === id);
285
- }
286
- function findTopLevel(tasks, id) {
287
- return tasks.find((t) => t.id === id);
288
- }
289
- function detectCycle(tasks) {
290
- const byId = new Map(tasks.map((t) => [t.id, t]));
291
- const visiting = /* @__PURE__ */ new Set();
292
- const done = /* @__PURE__ */ new Set();
293
- const visit = (id) => {
294
- if (done.has(id)) return null;
295
- if (visiting.has(id)) return id;
296
- visiting.add(id);
297
- for (const dep of byId.get(id)?.dependencies ?? []) {
298
- const hit = visit(dep);
299
- if (hit) return hit;
300
- }
301
- visiting.delete(id);
302
- done.add(id);
303
- return null;
304
- };
305
- for (const t of tasks) {
306
- const hit = visit(t.id);
307
- if (hit) return byId.get(hit)?.title ?? hit;
308
- }
309
- return null;
310
- }
311
- function isPinned(t) {
312
- return t.status === "in_progress" || t.status === "completed";
313
- }
314
- function repairOrder(tasks) {
315
- const byId = new Map(tasks.map((t) => [t.id, t]));
316
- const placed = /* @__PURE__ */ new Set();
317
- const movable = tasks.filter((t) => !isPinned(t));
318
- const result = [];
319
- const blockerOf = (t) => {
320
- const id = t.dependencies.find((d) => byId.has(d) && !placed.has(d));
321
- return id ? byId.get(id) : void 0;
322
- };
323
- const place = (t) => {
324
- result.push(t);
325
- placed.add(t.id);
326
- };
327
- for (const occupant of tasks) {
328
- if (isPinned(occupant)) {
329
- const blocker = blockerOf(occupant);
330
- if (blocker) return { error: pinnedRefusal(blocker, occupant, "before") };
331
- place(occupant);
332
- continue;
333
- }
334
- const next = movable.findIndex((t) => !blockerOf(t));
335
- if (next < 0) {
336
- const [stuck, pin] = trailToPin(movable[0], blockerOf);
337
- return { error: pinnedRefusal(stuck, pin, "after") };
338
- }
339
- place(movable.splice(next, 1)[0]);
340
- }
341
- return { tasks: result };
342
- }
343
- function trailToPin(task, blockerOf) {
344
- let at = task;
345
- for (; ; ) {
346
- const blocker = blockerOf(at);
347
- if (!blocker || isPinned(blocker)) return [at, blocker ?? at];
348
- at = blocker;
349
- }
350
- }
351
- function describeMoves(before, after) {
352
- const wasAt = new Map(before.map((t, i) => [t.id, i + 1]));
353
- const moved = after.map((t, i) => ({ t, from: wasAt.get(t.id), to: i + 1 })).filter((m) => m.from !== m.to);
354
- return moved.length ? moved.map((m) => `"${m.t.title}" #${m.from}\u2192#${m.to}`).join(", ") : null;
355
- }
356
- function pinnedRefusal(other, pin, side) {
357
- const word = pin.status === "in_progress" ? "running" : "completed";
358
- return `"${other.title}" would have to run ${side} "${pin.title}", which is ${word} and cannot be moved`;
359
- }
360
- function canMergeTasks(tasks, selectedIds) {
361
- if (selectedIds.length < 2) return { ok: false, error: "Select at least two tasks to merge" };
362
- const idSet = new Set(selectedIds);
363
- const toMerge = tasks.filter((t) => idSet.has(t.id));
364
- if (toMerge.length < 2) return { ok: false, error: "At least two valid tasks are required to merge" };
365
- for (const t of toMerge) {
366
- if (t.status === "in_progress") return { ok: false, error: `"${t.title}" is running and cannot be merged` };
367
- if (t.status === "completed") return { ok: false, error: `"${t.title}" is completed and cannot be merged` };
368
- }
369
- const ordered = [...toMerge].sort((a, b) => a.order - b.order);
370
- const survivorId = "__merge_sim__";
371
- const sim = tasks.filter((t) => !idSet.has(t.id)).map((t) => ({ ...t, dependencies: t.dependencies.map((d) => idSet.has(d) ? survivorId : d).filter((d) => d !== t.id) }));
372
- const unionDeps = [...new Set(toMerge.flatMap((t) => t.dependencies))].filter((d) => !idSet.has(d));
373
- sim.push({ ...ordered[0], id: survivorId, title: "__merge_sim__", order: ordered[0].order, dependencies: unionDeps });
374
- const cycle = detectCycle(sim);
375
- if (cycle) return { ok: false, error: `Merging would create a dependency cycle involving "${cycle}"` };
376
- return { ok: true };
377
- }
378
- function dependentsOf(tasks, taskId) {
379
- return tasks.filter((t) => t.id !== taskId && t.dependencies.includes(taskId));
380
- }
381
- function dependencyCandidates(tasks, taskId) {
382
- if (!taskId) return [...tasks];
383
- const target = tasks.find((t) => t.id === taskId);
384
- return target ? tasks.filter((t) => t.order < target.order) : [];
385
- }
386
- function canSetDependencies(tasks, taskId, dependencies) {
387
- const target = tasks.find((t) => t.id === taskId);
388
- if (!target) return { ok: false, error: "Task not found" };
389
- const allowed = new Set(dependencyCandidates(tasks, taskId).map((t) => t.id));
390
- for (const dep of dependencies) {
391
- if (dep === taskId) return { ok: false, error: `"${target.title}" cannot depend on itself` };
392
- const found = tasks.find((t) => t.id === dep);
393
- if (!found) return { ok: false, error: `Unknown dependency "${dep}"` };
394
- if (!allowed.has(dep)) {
395
- return { ok: false, error: `"${target.title}" cannot depend on "${found.title}", which comes after it in the plan` };
396
- }
397
- }
398
- return { ok: true };
399
- }
400
- function canSplitTask(tasks, taskId) {
401
- const target = tasks.find((t) => t.id === taskId);
402
- if (!target) return { ok: false, error: "Task not found" };
403
- if (target.status === "in_progress") return { ok: false, error: `"${target.title}" is running and cannot be split` };
404
- if (target.status === "completed") return { ok: false, error: `"${target.title}" is completed and cannot be split` };
405
- return { ok: true };
406
- }
407
- function applyTaskOps(currentTasks, ops, runners, catalog) {
408
- let tasks = JSON.parse(JSON.stringify(currentTasks));
409
- const errors = [];
410
- const summary = [];
411
- const originalFlatAll = flattenTasks(currentTasks);
412
- const handleOwner = /* @__PURE__ */ new Map();
413
- const handleId = /* @__PURE__ */ new Map();
414
- for (const [i, op] of ops.entries()) {
415
- const handle = op.handle;
416
- if (handle === void 0) continue;
417
- if (typeof handle !== "string" || !handle.trim()) {
418
- errors.push(`op ${i + 1} (${op.op}): handle must be a non-empty string`);
419
- continue;
420
- }
421
- if (handleOwner.has(handle)) {
422
- errors.push(`op ${i + 1} (${op.op}): handle "${handle}" is already used earlier in this batch`);
423
- continue;
424
- }
425
- if (refCollidesWithExisting(handle, originalFlatAll)) {
426
- errors.push(`op ${i + 1} (${op.op}): handle "${handle}" collides with an existing task reference`);
427
- continue;
428
- }
429
- handleOwner.set(handle, i);
430
- }
431
- if (errors.length) return { ok: false, tasks: currentTasks, errors, summary: [] };
432
- const resolveDeps = (deps, opIndex) => {
433
- const ids = [];
434
- const bad = [];
435
- const liveFlat = flattenTasks(tasks);
436
- for (const d of Array.isArray(deps) ? deps : []) {
437
- const r = resolveRef(d, originalFlatAll, liveFlat, handleOwner, handleId, opIndex);
438
- if ("error" in r) {
439
- bad.push(`${String(d)} (${r.error})`);
440
- continue;
441
- }
442
- if (!liveFlat.some((t) => t.id === r.id)) {
443
- bad.push(`${String(d)} (no longer exists in this batch)`);
444
- continue;
445
- }
446
- ids.push(r.id);
447
- }
448
- return { ids, bad };
449
- };
450
- for (const [i, op] of ops.entries()) {
451
- const label = `op ${i + 1} (${op.op})`;
452
- switch (op.op) {
453
- case "update": {
454
- const ref = resolveRef(op.taskId, originalFlatAll, flattenTasks(tasks), handleOwner, handleId, i);
455
- if ("error" in ref) {
456
- errors.push(`${label}: ${ref.error}`);
457
- break;
458
- }
459
- const target = findInTasks(tasks, ref.id);
460
- if (!target) {
461
- errors.push(`${label}: task "${op.taskId}" no longer exists in this batch (removed, merged, or split by an earlier op)`);
462
- break;
463
- }
464
- const changes = {};
465
- for (const key of UPDATABLE_FIELDS) {
466
- if (op.changes && key in op.changes) changes[key] = op.changes[key];
467
- }
468
- if (changes.assignedRunner && !runners.includes(changes.assignedRunner)) {
469
- errors.push(`${label}: runner "${changes.assignedRunner}" is not in this plan's runner set [${runners.join(", ")}]`);
470
- break;
471
- }
472
- const changesForCheck = { ...changes };
473
- delete changesForCheck.dependencies;
474
- const check = validateTaskEdit("planner", flattenTasks(tasks), target.id, changesForCheck, catalog);
475
- if (!check.ok) {
476
- errors.push(`${label}: ${check.error}`);
477
- break;
478
- }
479
- for (const field of check.clear ?? []) changes[field] = void 0;
480
- if ("dependencies" in changes) {
481
- const { ids, bad } = resolveDeps(changes.dependencies, i);
482
- if (bad.length) {
483
- errors.push(`${label}: unknown dependencies: ${bad.join(", ")}`);
484
- break;
485
- }
486
- changes.dependencies = ids.filter((id) => id !== target.id);
487
- }
488
- if (Object.keys(changes).length === 0) {
489
- errors.push(`${label}: no valid changes provided`);
490
- break;
491
- }
492
- tasks = updateTaskInPlan(tasks, target.id, changes);
493
- const clearedNote = check.clear?.length ? `; cleared ${check.clear.join(", ")}` : "";
494
- summary.push(`Updated "${target.title}" (${Object.keys(changes).join(", ")})${clearedNote}`);
495
- break;
496
- }
497
- case "add": {
498
- const spec = op.task ?? {};
499
- if (!spec.title) {
500
- errors.push(`${label}: new task needs a title`);
501
- break;
502
- }
503
- const { ids, bad } = resolveDeps(spec.dependencies, i);
504
- if (bad.length) {
505
- errors.push(`${label}: unknown dependencies: ${bad.join(", ")}`);
506
- break;
507
- }
508
- const runner = spec.assignedRunner && runners.includes(spec.assignedRunner) ? spec.assignedRunner : runners[0];
509
- const validity = checkModelAndModeValidity(runner, spec.assignedModel, spec.taskMode, catalog);
510
- if (!validity.ok) {
511
- errors.push(`${label}: ${validity.error}`);
512
- break;
513
- }
514
- const newId = uuidv4();
515
- tasks = addTaskToPlan(tasks, {
516
- ...spec,
517
- id: newId,
518
- dependencies: ids,
519
- assignedRunner: runner,
520
- description: spec.description ?? spec.title,
521
- prompt: spec.prompt ?? spec.description ?? spec.title
522
- });
523
- if (op.handle) handleId.set(op.handle, newId);
524
- summary.push(`Added "${spec.title}"`);
525
- break;
526
- }
527
- case "remove": {
528
- const ref = resolveRef(op.taskId, originalFlatAll, flattenTasks(tasks), handleOwner, handleId, i);
529
- if ("error" in ref) {
530
- errors.push(`${label}: ${ref.error}`);
531
- break;
532
- }
533
- const target = findInTasks(tasks, ref.id);
534
- if (!target) {
535
- errors.push(`${label}: task "${op.taskId}" no longer exists in this batch (removed, merged, or split by an earlier op)`);
536
- break;
537
- }
538
- if (target.status === "in_progress") {
539
- errors.push(`${label}: "${target.title}" is running and cannot be removed`);
540
- break;
541
- }
542
- if (target.status === "completed") {
543
- errors.push(`${label}: "${target.title}" is completed and cannot be removed`);
544
- break;
545
- }
546
- tasks = removeTaskFromPlan(tasks, target.id);
547
- summary.push(`Removed "${target.title}"`);
548
- break;
549
- }
550
- case "reorder": {
551
- const refs = (op.taskIds ?? []).map((r) => resolveRef(r, currentTasks, tasks, handleOwner, handleId, i));
552
- const badRef = refs.find((r) => "error" in r);
553
- if (badRef) {
554
- errors.push(`${label}: ${badRef.error}`);
555
- break;
556
- }
557
- const resolved = refs.map((r) => findTopLevel(tasks, r.id));
558
- if (resolved.some((t) => !t)) {
559
- errors.push(`${label}: unknown task in reorder list`);
560
- break;
561
- }
562
- const orderIds = resolved.map((t) => t.id);
563
- if (new Set(orderIds).size !== tasks.length) {
564
- errors.push(`${label}: reorder must list every top-level task exactly once (${tasks.length} tasks)`);
565
- break;
566
- }
567
- const byId = new Map(tasks.map((t) => [t.id, t]));
568
- tasks = renumberTasks(orderIds.map((id) => byId.get(id)));
569
- summary.push("Reordered tasks");
570
- break;
571
- }
572
- case "merge": {
573
- const refs = (op.taskIds ?? []).map((r) => resolveRef(r, currentTasks, tasks, handleOwner, handleId, i));
574
- const badRef = refs.find((r) => "error" in r);
575
- if (badRef) {
576
- errors.push(`${label}: ${badRef.error}`);
577
- break;
578
- }
579
- const resolved = refs.map((r) => findTopLevel(tasks, r.id));
580
- if (resolved.some((t) => !t)) {
581
- errors.push(`${label}: unknown task in merge list (already removed, merged, or split by an earlier op)`);
582
- break;
583
- }
584
- const toMerge = resolved.map((t) => t).sort((a, b) => a.order - b.order);
585
- if (toMerge.length < 2) {
586
- errors.push(`${label}: merge needs at least two tasks`);
587
- break;
588
- }
589
- const locked = toMerge.find((t) => t.status === "in_progress" || t.status === "completed");
590
- if (locked) {
591
- errors.push(`${label}: "${locked.title}" is ${locked.status === "in_progress" ? "running" : "completed"} and cannot be merged`);
592
- break;
593
- }
594
- const idSet = new Set(toMerge.map((t) => t.id));
595
- const unionDepsRaw = [...new Set(toMerge.flatMap((t) => t.dependencies))].filter((d) => !idSet.has(d));
596
- const { ids, bad } = resolveDeps(unionDepsRaw, i);
597
- if (bad.length) {
598
- errors.push(`${label}: unknown dependencies: ${bad.join(", ")}`);
599
- break;
600
- }
601
- const spec = op.merged ?? {};
602
- if (!spec.title) {
603
- errors.push(`${label}: merged task needs a title`);
604
- break;
605
- }
606
- const runner = spec.assignedRunner && runners.includes(spec.assignedRunner) ? spec.assignedRunner : toMerge[0].assignedRunner;
607
- const mergedTask = createTask({
608
- title: spec.title,
609
- description: spec.description ?? toMerge.map((t) => t.description).filter(Boolean).join("\n\n"),
610
- prompt: spec.prompt ?? (toMerge.map((t) => t.prompt).filter(Boolean).join("\n\n---\n\n") || void 0),
611
- type: spec.type ?? (toMerge.some((t) => t.type === "user") ? "user" : "ai"),
612
- dependencies: ids,
613
- assignedRunner: runner,
614
- assignedModel: spec.assignedModel ?? toMerge.find((t) => t.assignedModel)?.assignedModel,
615
- thinkingEffort: spec.thinkingEffort ?? toMerge.find((t) => t.thinkingEffort)?.thinkingEffort,
616
- taskMode: spec.taskMode ?? toMerge[0].taskMode,
617
- autonomy: spec.autonomy ?? toMerge.find((t) => t.autonomy)?.autonomy,
618
- sliceType: spec.sliceType ?? toMerge.find((t) => t.sliceType)?.sliceType,
619
- userStoriesCovered: spec.userStoriesCovered ?? (toMerge.flatMap((t) => t.userStoriesCovered ?? []).length ? [...new Set(toMerge.flatMap((t) => t.userStoriesCovered ?? []))] : void 0)
620
- });
621
- const mergedId = mergedTask.id;
622
- const firstId = toMerge[0].id;
623
- const result = [];
624
- let inserted = false;
625
- for (const t of tasks) {
626
- if (idSet.has(t.id)) {
627
- if (!inserted && t.id === firstId) {
628
- result.push(mergedTask);
629
- inserted = true;
630
- }
631
- continue;
632
- }
633
- result.push({ ...t, dependencies: t.dependencies.map((d) => idSet.has(d) ? mergedId : d) });
634
- }
635
- if (!inserted) result.push(mergedTask);
636
- tasks = renumberTasks(result);
637
- if (op.handle) handleId.set(op.handle, mergedId);
638
- summary.push(`Merged ${toMerge.length} tasks into "${spec.title}"`);
639
- break;
640
- }
641
- case "split": {
642
- const ref = resolveRef(op.taskId, currentTasks, tasks, handleOwner, handleId, i);
643
- if ("error" in ref) {
644
- errors.push(`${label}: ${ref.error}`);
645
- break;
646
- }
647
- const target = findTopLevel(tasks, ref.id);
648
- if (!target) {
649
- errors.push(`${label}: task "${op.taskId}" not found`);
650
- break;
651
- }
652
- if (target.status === "in_progress") {
653
- errors.push(`${label}: "${target.title}" is running and cannot be split`);
654
- break;
655
- }
656
- if (target.status === "completed") {
657
- errors.push(`${label}: "${target.title}" is completed and cannot be split`);
658
- break;
659
- }
660
- const parts = op.parts ?? [];
661
- if (parts.length < 2) {
662
- errors.push(`${label}: split needs at least two parts`);
663
- break;
664
- }
665
- const newTasks = [];
666
- let prevId;
667
- let buildError = false;
668
- for (let p = 0; p < parts.length; p++) {
669
- const spec = parts[p];
670
- if (!spec || !spec.title) {
671
- errors.push(`${label}: split part ${p + 1} needs a title`);
672
- buildError = true;
673
- break;
674
- }
675
- let deps;
676
- if (p === 0) {
677
- const { ids, bad } = resolveDeps(target.dependencies, i);
678
- if (bad.length) {
679
- errors.push(`${label}: split part 1 unknown dependencies: ${bad.join(", ")}`);
680
- buildError = true;
681
- break;
682
- }
683
- deps = ids;
684
- } else {
685
- deps = prevId ? [prevId] : [];
686
- }
687
- const runner = spec.assignedRunner && runners.includes(spec.assignedRunner) ? spec.assignedRunner : target.assignedRunner;
688
- const nt = createTask({
689
- title: spec.title,
690
- description: spec.description ?? spec.title,
691
- prompt: spec.prompt ?? spec.description ?? spec.title,
692
- type: spec.type ?? target.type,
693
- dependencies: deps,
694
- assignedRunner: runner,
695
- assignedModel: spec.assignedModel ?? target.assignedModel,
696
- taskMode: spec.taskMode ?? target.taskMode,
697
- autonomy: spec.autonomy ?? target.autonomy,
698
- sliceType: spec.sliceType ?? target.sliceType,
699
- userStoriesCovered: spec.userStoriesCovered ?? target.userStoriesCovered
700
- });
701
- newTasks.push(nt);
702
- prevId = nt.id;
703
- }
704
- if (buildError) break;
705
- const tailId = newTasks[newTasks.length - 1].id;
706
- const result = [];
707
- for (const t of tasks) {
708
- if (t.id === target.id) {
709
- result.push(...newTasks);
710
- continue;
711
- }
712
- result.push({ ...t, dependencies: t.dependencies.map((d) => d === target.id ? tailId : d) });
713
- }
714
- tasks = renumberTasks(result);
715
- if (op.handle) handleId.set(op.handle, tailId);
716
- summary.push(`Split "${target.title}" into ${newTasks.length} tasks`);
717
- break;
718
- }
719
- case "rearm": {
720
- const ref = resolveRef(op.taskId, originalFlatAll, flattenTasks(tasks), handleOwner, handleId, i);
721
- if ("error" in ref) {
722
- errors.push(`${label}: ${ref.error}`);
723
- break;
724
- }
725
- const target = findInTasks(tasks, ref.id);
726
- if (!target) {
727
- errors.push(`${label}: task "${op.taskId}" no longer exists in this batch (removed, merged, or split by an earlier op)`);
728
- break;
729
- }
730
- if (target.status === "in_progress") {
731
- errors.push(`${label}: "${target.title}" is running and cannot be re-armed`);
732
- break;
733
- }
734
- const changes = {};
735
- for (const key of UPDATABLE_FIELDS) {
736
- if (op.changes && key in op.changes) changes[key] = op.changes[key];
737
- }
738
- if (changes.assignedRunner && !runners.includes(changes.assignedRunner)) {
739
- errors.push(`${label}: runner "${changes.assignedRunner}" is not in this plan's runner set [${runners.join(", ")}]`);
740
- break;
741
- }
742
- const changesForCheck = { ...changes };
743
- delete changesForCheck.dependencies;
744
- const check = validateTaskEdit("direct", flattenTasks(tasks), target.id, changesForCheck, catalog);
745
- if (!check.ok) {
746
- errors.push(`${label}: ${check.error}`);
747
- break;
748
- }
749
- for (const field of check.clear ?? []) changes[field] = void 0;
750
- if ("dependencies" in changes) {
751
- const { ids, bad } = resolveDeps(changes.dependencies, i);
752
- if (bad.length) {
753
- errors.push(`${label}: unknown dependencies: ${bad.join(", ")}`);
754
- break;
755
- }
756
- changes.dependencies = ids.filter((id) => id !== target.id);
757
- }
758
- tasks = updateTaskInPlan(tasks, target.id, { ...changes, status: "pending", verdict: void 0, outputSummary: void 0 });
759
- for (const dep of flattenTasks(tasks)) {
760
- if (dep.status === "blocked" && dep.dependencies.includes(target.id)) {
761
- tasks = updateTaskInPlan(tasks, dep.id, { status: "pending" });
762
- }
763
- }
764
- const clearedNote = check.clear?.length ? `; cleared ${check.clear.join(", ")}` : "";
765
- const changedNote = Object.keys(changes).length ? ` (${Object.keys(changes).join(", ")})` : "";
766
- summary.push(`Re-armed "${target.title}"${changedNote}${clearedNote}`);
767
- break;
768
- }
769
- default:
770
- errors.push(`${label}: unknown op "${op.op}"`);
771
- }
772
- if (errors.length) break;
773
- }
774
- if (!errors.length) {
775
- const flat = flattenTasks(tasks);
776
- const idSet = new Set(flat.map((t) => t.id));
777
- for (const t of flat) {
778
- for (const dep of t.dependencies) {
779
- if (!idSet.has(dep)) errors.push(`"${t.title}" depends on a task that no longer exists (${dep})`);
780
- }
781
- }
782
- const cycleAt = detectCycle(flat);
783
- if (cycleAt) errors.push(`Dependency cycle detected involving "${cycleAt}"`);
784
- if (!errors.length) {
785
- const repaired = repairOrder(tasks);
786
- if ("error" in repaired) {
787
- errors.push(repaired.error);
788
- } else {
789
- const moves = describeMoves(tasks, repaired.tasks);
790
- if (moves) summary.push(`Reordered to keep dependencies first: ${moves}`);
791
- tasks = renumberTasks(repaired.tasks);
792
- }
793
- }
794
- }
795
- return errors.length ? { ok: false, tasks: currentTasks, errors, summary: [] } : { ok: true, tasks, errors: [], summary };
796
- }
797
-
798
- // src/services/researchStepSummary.ts
799
- function ellipsize(text, max) {
800
- const oneLine = text.replace(/\s+/g, " ").trim();
801
- return oneLine.length > max ? `${oneLine.slice(0, max)}\u2026` : oneLine;
802
- }
803
- function summarizeToolCall(tool, argsJson, toolLabel) {
804
- const display = toolLabel?.trim() || tool;
805
- if (tool === "agent_tool") {
806
- try {
807
- const args = JSON.parse(argsJson);
808
- const hint = args.path ?? args.file_path ?? args.filePath ?? args.pattern ?? args.query ?? args.url ?? args.command;
809
- if (hint) return `${display} ${ellipsize(String(hint), 50)}`;
810
- } catch {
811
- }
812
- return display;
813
- }
814
- if (tool === "spawn_research_agent") {
815
- try {
816
- const args = JSON.parse(argsJson);
817
- const prompt = typeof args.prompt === "string" ? args.prompt : "";
818
- const oneLine = prompt.replace(/\s+/g, " ").trim();
819
- return `spawn: "${oneLine.slice(0, 60)}${oneLine.length > 60 ? "\u2026" : ""}"`;
820
- } catch {
821
- return "spawn_research_agent";
822
- }
823
- }
824
- try {
825
- const args = JSON.parse(argsJson);
826
- if (tool === "find_symbol" && args.symbol) {
827
- return `find_symbol ${args.symbol}${args.language ? ` [${args.language}]` : ""}`;
828
- }
829
- if (tool === "web_search" && args.query) {
830
- return `web_search "${ellipsize(String(args.query), 50)}"`;
831
- }
832
- if (tool === "fetch" && args.url) {
833
- return `fetch ${ellipsize(String(args.url), 60)}`;
834
- }
835
- if (tool === "glob" && args.pattern) {
836
- return `glob ${ellipsize(String(args.pattern), 50)}`;
837
- }
838
- if (tool === "grep" && args.pattern) {
839
- const mode = args.output_mode && args.output_mode !== "content" ? ` (${args.output_mode})` : "";
840
- const scope = args.include ? ` in ${args.include}` : "";
841
- return `grep ${ellipsize(String(args.pattern), 40)}${scope}${mode}`;
842
- }
843
- const path = args.filePath || args.file_path || args.path;
844
- if (path) {
845
- const basename = String(path).split("/").pop() || String(path);
846
- return `${display} ${basename}`;
847
- }
848
- if (args.pattern) {
849
- return `${display} ${args.pattern}`;
850
- }
851
- if (args.command) {
852
- return `bash: ${String(args.command).slice(0, 40)}`;
853
- }
854
- } catch {
855
- }
856
- return display;
857
- }
858
- function classifyOutcome(success, output) {
859
- if (!success) {
860
- if (/^Command refused:/.test(output) || /is not available\. You are a read-only research subagent/.test(output)) return "refused";
861
- if (/not approved/i.test(output) || /^Access denied:/.test(output) || /needs user approval/i.test(output)) return "denied";
862
- return "failure";
863
- }
864
- return "success";
865
- }
866
-
867
- // src/services/SessionMessage.ts
868
- function serializeTask(t) {
869
- return {
870
- id: t.id,
871
- order: t.order,
872
- title: t.title,
873
- type: t.type,
874
- description: t.description,
875
- dependencies: t.dependencies || [],
876
- assignedRunner: t.assignedRunner,
877
- assignedModel: t.assignedModel || null,
878
- taskMode: t.taskMode || "build",
879
- prompt: t.prompt || null,
880
- subtasks: (t.subtasks || []).map(serializeTask),
881
- userSteps: t.userSteps || void 0,
882
- thinkingEffort: t.thinkingEffort || void 0,
883
- autonomy: t.autonomy || void 0,
884
- sliceType: t.sliceType || void 0,
885
- userStoriesCovered: t.userStoriesCovered || void 0
886
- };
887
- }
888
- function serializeTaskStatus(t, idleSince = null, isolation = null) {
889
- return {
890
- id: t.id,
891
- status: t.status,
892
- verdict: t.verdict ? { outcome: t.verdict.outcome, reason: t.verdict.reason, checks: t.verdict.checks || [] } : null,
893
- idleSince,
894
- ...isolation ? { isolation } : {}
895
- };
896
- }
897
- function serializePlan(plan) {
898
- return {
899
- tasks: plan.tasks.map(serializeTask),
900
- runners: plan.runners,
901
- generatedAt: plan.generatedAt,
902
- conversationHistory: plan.conversationHistory,
903
- prdMarkdown: plan.prdMarkdown,
904
- queuedMessages: plan.queuedMessages
905
- };
906
- }
907
- function executionSummary(tasks) {
908
- return {
909
- total: tasks.length,
910
- completed: tasks.filter((t) => t.status === "completed").length,
911
- failed: tasks.filter((t) => t.status === "failed").length
912
- };
913
- }
914
- var CHECKPOINT_TRUNCATE_LENGTH = 120;
915
- function truncateCheckpointSummary(summary) {
916
- const firstLine = summary.split("\n")[0].trim();
917
- if (firstLine.length <= CHECKPOINT_TRUNCATE_LENGTH) return firstLine;
918
- return firstLine.slice(0, CHECKPOINT_TRUNCATE_LENGTH - 1) + "\u2026";
919
- }
920
-
921
- export {
922
- effectiveAllowlist,
923
- filterModelsForPrompt,
924
- clampThinkingEffort,
925
- coerceAssignments,
926
- validateTaskEdit,
927
- taskOpsProtocol,
928
- textHasTaskOps,
929
- parseTaskOpsJson,
930
- canMergeTasks,
931
- dependentsOf,
932
- dependencyCandidates,
933
- canSetDependencies,
934
- canSplitTask,
935
- applyTaskOps,
936
- summarizeToolCall,
937
- classifyOutcome,
938
- serializeTask,
939
- serializeTaskStatus,
940
- serializePlan,
941
- executionSummary,
942
- CHECKPOINT_TRUNCATE_LENGTH,
943
- truncateCheckpointSummary
944
- };
945
- //# sourceMappingURL=chunk-GWPIYDQW.mjs.map