@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.
- package/dist/IFileSystem-BkPX7mLD.d.mts +76 -0
- package/dist/IFileSystem-C0l-4MGT.d.ts +76 -0
- package/dist/{ModeResolver-Dkig8ghQ.d.ts → ModeResolver--16lh7dS.d.mts} +1 -1
- package/dist/{ModeResolver-DVJ7HV3k.d.mts → ModeResolver-CjpG5Wli.d.ts} +1 -1
- package/dist/Task-Dyxp67s2.d.mts +2086 -0
- package/dist/Task-Dyxp67s2.d.ts +2086 -0
- package/dist/{chunk-T2S5O36I.mjs → chunk-C44UWIAD.mjs} +6 -6
- package/dist/chunk-C44UWIAD.mjs.map +1 -0
- package/dist/{chunk-JVMDEHRQ.mjs → chunk-EDGUFCIR.mjs} +77 -37
- package/dist/chunk-EDGUFCIR.mjs.map +1 -0
- package/dist/{chunk-XWOUIA6A.mjs → chunk-JBEFAJ2W.mjs} +2 -2
- package/dist/chunk-ROVYWEBI.mjs +2115 -0
- package/dist/chunk-ROVYWEBI.mjs.map +1 -0
- package/dist/index.d.mts +1647 -758
- package/dist/index.d.ts +1647 -758
- package/dist/index.js +9301 -4857
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6222 -2968
- package/dist/index.mjs.map +1 -1
- package/dist/order-labels.d.mts +3 -1
- package/dist/order-labels.d.ts +3 -1
- package/dist/{parsing-DRp4dPC0.d.mts → parsing-DPEpAszP.d.mts} +11 -3
- package/dist/{parsing-CF_grC29.d.ts → parsing-EcmCsF1y.d.ts} +11 -3
- package/dist/parsing.d.mts +5 -3
- package/dist/parsing.d.ts +5 -3
- package/dist/parsing.js.map +1 -1
- package/dist/parsing.mjs +2 -2
- package/dist/plan-utils-BAvW3hvl.d.mts +877 -0
- package/dist/plan-utils-BMyEiDKv.d.ts +877 -0
- package/dist/plan-utils.d.mts +5 -4
- package/dist/plan-utils.d.ts +5 -4
- package/dist/plan-utils.js +956 -8
- package/dist/plan-utils.js.map +1 -1
- package/dist/plan-utils.mjs +47 -7
- package/dist/testing.d.mts +57 -5
- package/dist/testing.d.ts +57 -5
- package/dist/testing.js +96 -2
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +94 -2
- package/dist/testing.mjs.map +1 -1
- package/package.json +2 -1
- package/skills/grilling/SKILL.md +6 -16
- package/skills/improve-codebase-architecture/SKILL.md +1 -1
- package/dist/ApprovalPolicy-BVhGdECT.d.mts +0 -79
- package/dist/ApprovalPolicy-BVhGdECT.d.ts +0 -79
- package/dist/ITerminalRunner-Bd-vAJnw.d.ts +0 -561
- package/dist/ITerminalRunner-ByeoLF57.d.mts +0 -561
- package/dist/Task-BxQkPlXO.d.mts +0 -664
- package/dist/Task-BxQkPlXO.d.ts +0 -664
- package/dist/chunk-GWPIYDQW.mjs +0 -945
- package/dist/chunk-GWPIYDQW.mjs.map +0 -1
- package/dist/chunk-JVMDEHRQ.mjs.map +0 -1
- package/dist/chunk-T2S5O36I.mjs.map +0 -1
- package/dist/plan-utils-CkNbqAmS.d.ts +0 -329
- package/dist/plan-utils-CtB3_Ovf.d.mts +0 -329
- /package/dist/{chunk-XWOUIA6A.mjs.map → chunk-JBEFAJ2W.mjs.map} +0 -0
package/dist/plan-utils.js
CHANGED
|
@@ -21,14 +21,35 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var plan_utils_exports = {};
|
|
22
22
|
__export(plan_utils_exports, {
|
|
23
23
|
CHECKPOINT_TRUNCATE_LENGTH: () => CHECKPOINT_TRUNCATE_LENGTH,
|
|
24
|
+
EMPTY_CONVERSATION: () => EMPTY_CONVERSATION,
|
|
25
|
+
EMPTY_HOLD: () => EMPTY_HOLD,
|
|
26
|
+
EMPTY_TASK_LOG: () => EMPTY_TASK_LOG,
|
|
27
|
+
NO_TURN: () => NO_TURN,
|
|
28
|
+
aheadOfDraft: () => aheadOfDraft,
|
|
24
29
|
canMergeTasks: () => canMergeTasks,
|
|
25
30
|
canSetDependencies: () => canSetDependencies,
|
|
26
31
|
canSplitTask: () => canSplitTask,
|
|
27
32
|
capConflictFiles: () => capConflictFiles,
|
|
28
33
|
dependencyCandidates: () => dependencyCandidates,
|
|
29
34
|
dependentsOf: () => dependentsOf,
|
|
35
|
+
drainNext: () => drainNext,
|
|
36
|
+
followTurn: () => followTurn,
|
|
37
|
+
fromTranscript: () => fromTranscript,
|
|
38
|
+
hasHiddenDetail: () => hasHiddenDetail,
|
|
39
|
+
holdPrompt: () => holdPrompt,
|
|
40
|
+
outputLines: () => outputLines,
|
|
41
|
+
outputPreview: () => outputPreview,
|
|
42
|
+
reduceConversation: () => reduceConversation,
|
|
43
|
+
reduceTaskLog: () => reduceTaskLog,
|
|
44
|
+
replayTaskLog: () => replayTaskLog,
|
|
45
|
+
runnerToolSubject: () => runnerToolSubject,
|
|
46
|
+
stopTurn: () => stopTurn,
|
|
30
47
|
summarizeToolCall: () => summarizeToolCall,
|
|
31
|
-
|
|
48
|
+
taskStartedNotice: () => taskStartedNotice,
|
|
49
|
+
toolHeadline: () => toolHeadline,
|
|
50
|
+
truncateCheckpointSummary: () => truncateCheckpointSummary,
|
|
51
|
+
unsendAll: () => unsendAll,
|
|
52
|
+
unsendLatest: () => unsendLatest
|
|
32
53
|
});
|
|
33
54
|
module.exports = __toCommonJS(plan_utils_exports);
|
|
34
55
|
|
|
@@ -105,9 +126,9 @@ function canSplitTask(tasks, taskId) {
|
|
|
105
126
|
}
|
|
106
127
|
|
|
107
128
|
// src/services/researchStepSummary.ts
|
|
108
|
-
function ellipsize(
|
|
109
|
-
const
|
|
110
|
-
return
|
|
129
|
+
function ellipsize(text2, max) {
|
|
130
|
+
const oneLine2 = text2.replace(/\s+/g, " ").trim();
|
|
131
|
+
return oneLine2.length > max ? `${oneLine2.slice(0, max)}\u2026` : oneLine2;
|
|
111
132
|
}
|
|
112
133
|
function summarizeToolCall(tool, argsJson, toolLabel) {
|
|
113
134
|
const display = toolLabel?.trim() || tool;
|
|
@@ -124,8 +145,8 @@ function summarizeToolCall(tool, argsJson, toolLabel) {
|
|
|
124
145
|
try {
|
|
125
146
|
const args = JSON.parse(argsJson);
|
|
126
147
|
const prompt = typeof args.prompt === "string" ? args.prompt : "";
|
|
127
|
-
const
|
|
128
|
-
return `spawn: "${
|
|
148
|
+
const oneLine2 = prompt.replace(/\s+/g, " ").trim();
|
|
149
|
+
return `spawn: "${oneLine2.slice(0, 60)}${oneLine2.length > 60 ? "\u2026" : ""}"`;
|
|
129
150
|
} catch {
|
|
130
151
|
return "spawn_research_agent";
|
|
131
152
|
}
|
|
@@ -173,20 +194,947 @@ function truncateCheckpointSummary(summary) {
|
|
|
173
194
|
return firstLine.slice(0, CHECKPOINT_TRUNCATE_LENGTH - 1) + "\u2026";
|
|
174
195
|
}
|
|
175
196
|
|
|
176
|
-
// src/services/
|
|
197
|
+
// src/services/conflictFiles.ts
|
|
177
198
|
function capConflictFiles(files, max = 5) {
|
|
178
199
|
return files.length <= max ? files.join(", ") : `${files.slice(0, max).join(", ")}, +${files.length - max} more`;
|
|
179
200
|
}
|
|
201
|
+
|
|
202
|
+
// src/models/Usage.ts
|
|
203
|
+
var TOKEN_FIELDS = ["inputTokens", "outputTokens", "cachedInputTokens"];
|
|
204
|
+
function addUsage(totals, record) {
|
|
205
|
+
const next = { ...totals };
|
|
206
|
+
for (const field of TOKEN_FIELDS) {
|
|
207
|
+
const reported = record[field];
|
|
208
|
+
if (reported !== void 0) next[field] = (totals[field] ?? 0) + reported;
|
|
209
|
+
}
|
|
210
|
+
if (record.reportedCost) {
|
|
211
|
+
const { amount, currency } = record.reportedCost;
|
|
212
|
+
next.reportedCost = { ...totals.reportedCost, [currency]: (totals.reportedCost?.[currency] ?? 0) + amount };
|
|
213
|
+
}
|
|
214
|
+
return next;
|
|
215
|
+
}
|
|
216
|
+
function addPlannerUsage(usage, record) {
|
|
217
|
+
const next = { ...usage, totals: addUsage(usage.totals, record) };
|
|
218
|
+
if (record.subagentId) {
|
|
219
|
+
next.bySubagent = { ...usage.bySubagent, [record.subagentId]: addUsage(usage.bySubagent?.[record.subagentId] ?? {}, record) };
|
|
220
|
+
return next;
|
|
221
|
+
}
|
|
222
|
+
if (record.inputTokens !== void 0) next.lastPromptTokens = record.inputTokens;
|
|
223
|
+
if (record.contextWindow !== void 0 && record.contextWindow > 0) next.contextWindow = record.contextWindow;
|
|
224
|
+
return next;
|
|
225
|
+
}
|
|
226
|
+
function isMeasured(totals) {
|
|
227
|
+
return totals.inputTokens !== void 0 || totals.outputTokens !== void 0 || totals.cachedInputTokens !== void 0 || Object.keys(totals.reportedCost ?? {}).length > 0;
|
|
228
|
+
}
|
|
229
|
+
function usageLine(usage) {
|
|
230
|
+
const contextFill = plannerContextFill(usage);
|
|
231
|
+
return {
|
|
232
|
+
totals: usage.totals,
|
|
233
|
+
...usage.bySubagent ? { bySubagent: usage.bySubagent } : {},
|
|
234
|
+
...contextFill ? { contextFill } : {}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function plannerContextFill(usage) {
|
|
238
|
+
if (usage.lastPromptTokens === void 0 || usage.contextWindow === void 0 || usage.contextWindow <= 0) return void 0;
|
|
239
|
+
return { usedTokens: usage.lastPromptTokens, windowTokens: usage.contextWindow };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// src/services/terminalRender.ts
|
|
243
|
+
var ANSI_OR_CTRL_RE = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)?|\x1b[()][AB012]|\x1b[=>]|[\x00-\x08\x0b-\x1f\x7f]/g;
|
|
244
|
+
|
|
245
|
+
// src/conversation/format.ts
|
|
246
|
+
var DISPLAY_NAMES = {
|
|
247
|
+
read_file: "Read",
|
|
248
|
+
read_files: "Read",
|
|
249
|
+
list_dir: "List",
|
|
250
|
+
glob: "Glob",
|
|
251
|
+
grep: "Grep",
|
|
252
|
+
find_symbol: "Symbol",
|
|
253
|
+
bash: "Bash",
|
|
254
|
+
fetch: "Fetch",
|
|
255
|
+
web_search: "WebSearch",
|
|
256
|
+
spawn_research_agent: "Agent"
|
|
257
|
+
};
|
|
258
|
+
var HINT_FIELDS = ["file_path", "filePath", "path", "url", "pattern", "query", "command", "description", "prompt"];
|
|
259
|
+
function parseArgs(args) {
|
|
260
|
+
try {
|
|
261
|
+
const parsed = JSON.parse(args);
|
|
262
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
263
|
+
} catch {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function oneLine(text2) {
|
|
268
|
+
return text2.replace(/\s+/g, " ").trim();
|
|
269
|
+
}
|
|
270
|
+
function firstLineOf(command) {
|
|
271
|
+
const lines = command.trim().split(/\r?\n/);
|
|
272
|
+
const more = lines.length - 1;
|
|
273
|
+
return more > 0 ? `${lines[0]} \u2026 +${more} line${more === 1 ? "" : "s"}` : lines[0];
|
|
274
|
+
}
|
|
275
|
+
function text(value) {
|
|
276
|
+
return typeof value === "string" ? value : "";
|
|
277
|
+
}
|
|
278
|
+
function searchArg(pattern, path, include = "") {
|
|
279
|
+
return `${pattern}${path ? ` in ${path}` : ""}${include ? ` (${include})` : ""}`;
|
|
280
|
+
}
|
|
281
|
+
function hintArg(args) {
|
|
282
|
+
for (const field of HINT_FIELDS) {
|
|
283
|
+
const value = text(args[field]);
|
|
284
|
+
if (value) return field === "command" ? firstLineOf(value) : oneLine(value);
|
|
285
|
+
}
|
|
286
|
+
const firstText = Object.values(args).find((v) => typeof v === "string" && v.trim() !== "");
|
|
287
|
+
return firstText ? oneLine(firstText) : "";
|
|
288
|
+
}
|
|
289
|
+
function keyArgOf(tool, args) {
|
|
290
|
+
switch (tool) {
|
|
291
|
+
case "bash":
|
|
292
|
+
return firstLineOf(text(args.command));
|
|
293
|
+
case "read_file":
|
|
294
|
+
case "list_dir":
|
|
295
|
+
return text(args.path);
|
|
296
|
+
case "read_files":
|
|
297
|
+
return Array.isArray(args.paths) ? args.paths.map(String).join(", ") : "";
|
|
298
|
+
case "glob":
|
|
299
|
+
return searchArg(text(args.pattern), text(args.path));
|
|
300
|
+
case "grep":
|
|
301
|
+
return searchArg(text(args.pattern), text(args.path), text(args.include));
|
|
302
|
+
case "find_symbol":
|
|
303
|
+
return `${text(args.symbol)}${text(args.language) ? ` [${text(args.language)}]` : ""}`;
|
|
304
|
+
case "fetch":
|
|
305
|
+
return text(args.url);
|
|
306
|
+
case "web_search":
|
|
307
|
+
return oneLine(text(args.query));
|
|
308
|
+
case "spawn_research_agent":
|
|
309
|
+
return oneLine(text(args.prompt));
|
|
310
|
+
default:
|
|
311
|
+
return hintArg(args);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function toolHeadline(tool, args, toolLabel) {
|
|
315
|
+
const name = toolLabel?.trim() || DISPLAY_NAMES[tool] || tool;
|
|
316
|
+
const parsed = parseArgs(args);
|
|
317
|
+
return { name, keyArg: parsed ? keyArgOf(tool, parsed) : oneLine(args) };
|
|
318
|
+
}
|
|
319
|
+
function outputLines(output) {
|
|
320
|
+
const lines = output.replace(/\r\n?/g, "\n").replace(ANSI_OR_CTRL_RE, "").split("\n");
|
|
321
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === "") lines.pop();
|
|
322
|
+
return lines;
|
|
323
|
+
}
|
|
324
|
+
function outputPreview(output, maxLines) {
|
|
325
|
+
const lines = outputLines(output);
|
|
326
|
+
const shown = lines.slice(0, Math.max(0, maxLines));
|
|
327
|
+
return { lines: shown, hiddenLineCount: lines.length - shown.length };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// src/conversation/records.ts
|
|
331
|
+
function settledMessage(id, role, text2, turnId) {
|
|
332
|
+
return { type: "message", id, role, text: text2, streaming: false, ...turnId ? { turnId } : {} };
|
|
333
|
+
}
|
|
334
|
+
function subagentBlock(id, { toolCallId, model, usage, turnId, ...fields }) {
|
|
335
|
+
return {
|
|
336
|
+
type: "subagent",
|
|
337
|
+
id,
|
|
338
|
+
...fields,
|
|
339
|
+
...toolCallId ? { toolCallId } : {},
|
|
340
|
+
...model ? { model } : {},
|
|
341
|
+
...usage ? { usage } : {},
|
|
342
|
+
...turnId ? { turnId } : {}
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function usageBlock(id, { totals, bySubagent, contextFill }) {
|
|
346
|
+
return { type: "usage", id, totals, ...bySubagent ? { bySubagent } : {}, ...contextFill ? { contextFill } : {} };
|
|
347
|
+
}
|
|
348
|
+
var STATUS_OF_OUTCOME = {
|
|
349
|
+
success: "ok",
|
|
350
|
+
failure: "error",
|
|
351
|
+
refused: "denied",
|
|
352
|
+
denied: "denied",
|
|
353
|
+
not_executed: "interrupted"
|
|
354
|
+
};
|
|
355
|
+
function finishedTool(call, output, outcome) {
|
|
356
|
+
return { ...call, status: STATUS_OF_OUTCOME[outcome], outcome, output, outputLineCount: outputLines(output).length };
|
|
357
|
+
}
|
|
358
|
+
function settledTool(call, step) {
|
|
359
|
+
return finishedTool(call, step.result, step.outcome);
|
|
360
|
+
}
|
|
361
|
+
function pendingTool(id, { tool, toolLabel, args, toolCallId, turnId }) {
|
|
362
|
+
return {
|
|
363
|
+
type: "tool",
|
|
364
|
+
id,
|
|
365
|
+
tool,
|
|
366
|
+
headline: toolHeadline(tool, args, toolLabel),
|
|
367
|
+
args,
|
|
368
|
+
status: "pending",
|
|
369
|
+
output: "",
|
|
370
|
+
outputLineCount: 0,
|
|
371
|
+
...toolCallId ? { toolCallId } : {},
|
|
372
|
+
...toolLabel ? { toolLabel } : {},
|
|
373
|
+
...turnId ? { turnId } : {}
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function toolFromStep(id, step, turnId) {
|
|
377
|
+
return settledTool(pendingTool(id, { ...step, turnId }), step);
|
|
378
|
+
}
|
|
379
|
+
function planMarker(content) {
|
|
380
|
+
const count = /(\d+) tasks?\b/.exec(content);
|
|
381
|
+
return { status: content.startsWith("Plan updated") ? "updated" : "generated", ...count ? { taskCount: Number(count[1]) } : {} };
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/conversation/lanes.ts
|
|
385
|
+
function findLastIndex(items, match) {
|
|
386
|
+
for (let i = items.length - 1; i >= 0; i--) if (match(items[i])) return i;
|
|
387
|
+
return -1;
|
|
388
|
+
}
|
|
389
|
+
function replaceAt(items, index, item) {
|
|
390
|
+
const next = items.slice();
|
|
391
|
+
next[index] = item;
|
|
392
|
+
return next;
|
|
393
|
+
}
|
|
394
|
+
function insertTop(blocks, block) {
|
|
395
|
+
const last = blocks[blocks.length - 1];
|
|
396
|
+
return last?.type === "usage" ? [...blocks.slice(0, -1), block, last] : [...blocks, block];
|
|
397
|
+
}
|
|
398
|
+
function append(view, make) {
|
|
399
|
+
return { ...view, blocks: insertTop(view.blocks, make(`b${view.nextId}`)), nextId: view.nextId + 1 };
|
|
400
|
+
}
|
|
401
|
+
function subagentAt(view, lane) {
|
|
402
|
+
const block = view.blocks[lane];
|
|
403
|
+
if (block.type !== "subagent") throw new Error(`block ${lane} is not a subagent`);
|
|
404
|
+
return block;
|
|
405
|
+
}
|
|
406
|
+
function laneBlocks(view, lane) {
|
|
407
|
+
return lane === null ? view.blocks : subagentAt(view, lane).children;
|
|
408
|
+
}
|
|
409
|
+
function setChildren(view, lane, children) {
|
|
410
|
+
return { ...view, blocks: replaceAt(view.blocks, lane, { ...subagentAt(view, lane), children }) };
|
|
411
|
+
}
|
|
412
|
+
function laneReplace(view, lane, index, block) {
|
|
413
|
+
if (lane === null) return { ...view, blocks: replaceAt(view.blocks, index, block) };
|
|
414
|
+
return setChildren(view, lane, replaceAt(subagentAt(view, lane).children, index, block));
|
|
415
|
+
}
|
|
416
|
+
function laneAppend(view, lane, make) {
|
|
417
|
+
if (lane === null) return append(view, make);
|
|
418
|
+
const children = [...subagentAt(view, lane).children, make(`b${view.nextId}`)];
|
|
419
|
+
return { ...setChildren(view, lane, children), nextId: view.nextId + 1 };
|
|
420
|
+
}
|
|
421
|
+
function isStreaming(block) {
|
|
422
|
+
return (block.type === "message" || block.type === "thinking") && block.streaming;
|
|
423
|
+
}
|
|
424
|
+
function sealed(block) {
|
|
425
|
+
return isStreaming(block) ? { ...block, streaming: false } : block;
|
|
426
|
+
}
|
|
427
|
+
function sealLane(view, lane, keep) {
|
|
428
|
+
const blocks = laneBlocks(view, lane);
|
|
429
|
+
if (!blocks.some((b, i) => i !== keep && isStreaming(b))) return view;
|
|
430
|
+
if (lane === null) return { ...view, blocks: view.blocks.map((b, i) => i === keep ? b : sealed(b)) };
|
|
431
|
+
return setChildren(view, lane, subagentAt(view, lane).children.map((b, i) => i === keep ? b : sealed(b)));
|
|
432
|
+
}
|
|
433
|
+
function appendOutput(view, lane, make) {
|
|
434
|
+
const next = laneAppend(view, lane, make);
|
|
435
|
+
return sealLane(next, lane, findLastIndex(laneBlocks(next, lane), (b) => b.type !== "usage"));
|
|
436
|
+
}
|
|
437
|
+
function subagentLane(view, subagentId, turnId) {
|
|
438
|
+
const existing = findLastIndex(view.blocks, (b) => b.type === "subagent" && b.subagentId === subagentId);
|
|
439
|
+
if (existing >= 0) return [view, existing];
|
|
440
|
+
const spawn = findLastIndex(view.blocks, (b) => b.type === "tool" && (b.spawns === subagentId || b.toolCallId === subagentId));
|
|
441
|
+
const call = view.blocks[spawn];
|
|
442
|
+
if (call?.type === "tool") {
|
|
443
|
+
const block = subagentBlock(call.id, {
|
|
444
|
+
subagentId,
|
|
445
|
+
brief: call.headline.keyArg,
|
|
446
|
+
status: "running",
|
|
447
|
+
children: [],
|
|
448
|
+
digest: "",
|
|
449
|
+
toolCallId: call.toolCallId,
|
|
450
|
+
turnId: call.turnId
|
|
451
|
+
});
|
|
452
|
+
return [{ ...view, blocks: replaceAt(view.blocks, spawn, block) }, spawn];
|
|
453
|
+
}
|
|
454
|
+
const next = append(view, (id) => subagentBlock(id, { subagentId, brief: "", status: "running", children: [], digest: "", turnId }));
|
|
455
|
+
return [next, findLastIndex(next.blocks, (b) => b.type === "subagent" && b.subagentId === subagentId)];
|
|
456
|
+
}
|
|
457
|
+
function updateSubagent(view, subagentId, turnId, update) {
|
|
458
|
+
const [next, lane] = subagentLane(view, subagentId, turnId);
|
|
459
|
+
return { ...next, blocks: replaceAt(next.blocks, lane, update(subagentAt(next, lane))) };
|
|
460
|
+
}
|
|
461
|
+
function laneOf(view, subagentId, turnId) {
|
|
462
|
+
return subagentId ? subagentLane(view, subagentId, turnId) : [view, null];
|
|
463
|
+
}
|
|
464
|
+
function think(view, text2, turnId, segmentId, subagentId) {
|
|
465
|
+
const [placed, lane] = laneOf(view, subagentId, turnId);
|
|
466
|
+
const blocks = laneBlocks(placed, lane);
|
|
467
|
+
const lastIndex = findLastIndex(blocks, (b) => b.type !== "usage");
|
|
468
|
+
const last = blocks[lastIndex];
|
|
469
|
+
if (last?.type === "thinking" && last.streaming && last.segmentId === segmentId) {
|
|
470
|
+
return laneReplace(placed, lane, lastIndex, { ...last, text: last.text + text2 });
|
|
471
|
+
}
|
|
472
|
+
return appendOutput(placed, lane, (id) => ({
|
|
473
|
+
type: "thinking",
|
|
474
|
+
id,
|
|
475
|
+
text: text2,
|
|
476
|
+
streaming: true,
|
|
477
|
+
...turnId ? { turnId } : {},
|
|
478
|
+
...segmentId ? { segmentId } : {},
|
|
479
|
+
...subagentId ? { subagentId } : {}
|
|
480
|
+
}));
|
|
481
|
+
}
|
|
482
|
+
function setUsageLine(view, line) {
|
|
483
|
+
const last = view.blocks[view.blocks.length - 1];
|
|
484
|
+
const block = usageBlock(last?.type === "usage" ? last.id : `b${view.nextId}`, line);
|
|
485
|
+
if (last?.type === "usage") return { ...view, blocks: replaceAt(view.blocks, view.blocks.length - 1, block) };
|
|
486
|
+
return { ...view, blocks: [...view.blocks, block], nextId: view.nextId + 1 };
|
|
487
|
+
}
|
|
488
|
+
function closeBlock(block, interrupted) {
|
|
489
|
+
return interrupted && block.type === "tool" && block.status === "pending" ? { ...block, status: "interrupted" } : sealed(block);
|
|
490
|
+
}
|
|
491
|
+
function closeSubagent(block, cut) {
|
|
492
|
+
const children = block.children.map((c) => closeBlock(c, cut !== null));
|
|
493
|
+
const status = cut && block.status === "running" ? cut : block.status;
|
|
494
|
+
return status === block.status && children.every((c, i) => c === block.children[i]) ? block : { ...block, status, children };
|
|
495
|
+
}
|
|
496
|
+
function closeOpenBlocks(view, cut) {
|
|
497
|
+
const blocks = view.blocks.map((b) => b.type === "subagent" ? closeSubagent(b, cut) : closeBlock(b, cut !== null));
|
|
498
|
+
return blocks.every((b, i) => b === view.blocks[i]) ? view : { ...view, blocks };
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/conversation/reduce.ts
|
|
502
|
+
var EMPTY_CONVERSATION = { blocks: [], nextId: 1 };
|
|
503
|
+
function isUnsettledSegment(block, turnId) {
|
|
504
|
+
return block.type === "message" && block.turnId === turnId && block.segmentId !== void 0;
|
|
505
|
+
}
|
|
506
|
+
function isBuildingPlan(block, turnId) {
|
|
507
|
+
return block.type === "plan" && block.status === "building" && block.turnId === turnId;
|
|
508
|
+
}
|
|
509
|
+
function finalSegmentIndex(blocks, turnId, pastPlan = false) {
|
|
510
|
+
for (let i = blocks.length - 1; i >= 0; i--) {
|
|
511
|
+
const block = blocks[i];
|
|
512
|
+
if (block.type === "usage" || block.type === "thinking" || block.type === "approval" || pastPlan && block.type === "plan") continue;
|
|
513
|
+
if (block.turnId !== turnId) continue;
|
|
514
|
+
return isUnsettledSegment(block, turnId) ? i : -1;
|
|
515
|
+
}
|
|
516
|
+
return -1;
|
|
517
|
+
}
|
|
518
|
+
function openTurn(view, turnId, prompt) {
|
|
519
|
+
const i = findLastIndex(view.blocks, (b) => b.type === "message" && b.role === "user");
|
|
520
|
+
const sent = view.blocks[i];
|
|
521
|
+
if (sent?.type === "message" && sent.turnId === void 0 && sent.text === prompt) {
|
|
522
|
+
return { ...view, blocks: replaceAt(view.blocks, i, { ...sent, turnId }) };
|
|
523
|
+
}
|
|
524
|
+
return append(view, (id) => settledMessage(id, "user", prompt, turnId));
|
|
525
|
+
}
|
|
526
|
+
function streamText(view, { turnId, segmentId, text: text2 }) {
|
|
527
|
+
const i = findLastIndex(view.blocks, (b) => isUnsettledSegment(b, turnId) && b.segmentId === segmentId);
|
|
528
|
+
const segment = view.blocks[i];
|
|
529
|
+
if (segment?.type === "message") {
|
|
530
|
+
return sealLane(laneReplace(view, null, i, { ...segment, text: segment.text + text2, streaming: true }), null, i);
|
|
531
|
+
}
|
|
532
|
+
return appendOutput(view, null, (id) => ({ type: "message", id, role: "planner", text: text2, streaming: true, turnId, segmentId }));
|
|
533
|
+
}
|
|
534
|
+
function dropBuildingPlan(view, turnId, segmentId) {
|
|
535
|
+
const blocks = view.blocks.filter((b) => !(isBuildingPlan(b, turnId) && (segmentId === void 0 || b.segmentId === segmentId)));
|
|
536
|
+
return blocks.length === view.blocks.length ? view : { ...view, blocks };
|
|
537
|
+
}
|
|
538
|
+
function settleReply(view, { content, turnId }) {
|
|
539
|
+
if (turnId !== void 0) {
|
|
540
|
+
const i = finalSegmentIndex(view.blocks, turnId);
|
|
541
|
+
if (i >= 0) {
|
|
542
|
+
return dropBuildingPlan({ ...view, blocks: replaceAt(view.blocks, i, settledMessage(view.blocks[i].id, "planner", content, turnId)) }, turnId);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return append(dropBuildingPlan(view, turnId), (id) => settledMessage(id, "planner", content, turnId));
|
|
546
|
+
}
|
|
547
|
+
function retractText(view, { turnId, segmentId }) {
|
|
548
|
+
const blocks = view.blocks.filter((b) => !(isUnsettledSegment(b, turnId) && (segmentId === void 0 || b.segmentId === segmentId)));
|
|
549
|
+
return dropBuildingPlan(blocks.length === view.blocks.length ? view : { ...view, blocks }, turnId, segmentId);
|
|
550
|
+
}
|
|
551
|
+
function startSubagent(view, { subagentId, brief, model, turnId }) {
|
|
552
|
+
return updateSubagent(view, subagentId, turnId, (block) => ({ ...block, ...brief ? { brief } : {}, ...model ? { model } : {} }));
|
|
553
|
+
}
|
|
554
|
+
function finishSubagent(view, { subagentId, outcome, digest, usage, turnId }) {
|
|
555
|
+
return updateSubagent(view, subagentId, turnId, (block) => ({
|
|
556
|
+
...block,
|
|
557
|
+
status: outcome,
|
|
558
|
+
digest,
|
|
559
|
+
children: block.children.map(sealed),
|
|
560
|
+
...usage ? { usage } : {}
|
|
561
|
+
}));
|
|
562
|
+
}
|
|
563
|
+
function announceTool(view, call) {
|
|
564
|
+
const spawns = call.tool === "spawn_research_agent" ? call.subagentId : void 0;
|
|
565
|
+
const [placed, lane] = laneOf(view, spawns ? void 0 : call.subagentId, call.turnId);
|
|
566
|
+
return appendOutput(placed, lane, (id) => ({ ...pendingTool(id, call), ...spawns ? { spawns } : {} }));
|
|
567
|
+
}
|
|
568
|
+
function isPendingCallOf(block, step) {
|
|
569
|
+
if (block.type !== "tool" || block.status !== "pending") return false;
|
|
570
|
+
return step.toolCallId ? block.toolCallId === step.toolCallId : block.toolCallId === void 0 && block.tool === step.tool;
|
|
571
|
+
}
|
|
572
|
+
function settleTool(view, { step, turnId }) {
|
|
573
|
+
const [placed, lane] = laneOf(view, step.subagentId, turnId);
|
|
574
|
+
const blocks = laneBlocks(placed, lane);
|
|
575
|
+
const i = findLastIndex(blocks, (b) => isPendingCallOf(b, step));
|
|
576
|
+
const pending = blocks[i];
|
|
577
|
+
if (pending?.type === "tool") return laneReplace(placed, lane, i, settledTool(pending, step));
|
|
578
|
+
if (lane === null && step.toolCallId && blocks.some((b) => b.type === "subagent" && b.toolCallId === step.toolCallId)) return view;
|
|
579
|
+
return laneAppend(placed, lane, (id) => toolFromStep(id, step, turnId));
|
|
580
|
+
}
|
|
581
|
+
function streamPlan(view, { token, turnId, segmentId }) {
|
|
582
|
+
const i = findLastIndex(view.blocks, (b) => isBuildingPlan(b, turnId));
|
|
583
|
+
const building = view.blocks[i];
|
|
584
|
+
if (building?.type === "plan" && building.segmentId === segmentId) {
|
|
585
|
+
return { ...view, blocks: replaceAt(view.blocks, i, { ...building, text: building.text + token }) };
|
|
586
|
+
}
|
|
587
|
+
return append(dropBuildingPlan(view, turnId), (id) => ({
|
|
588
|
+
type: "plan",
|
|
589
|
+
id,
|
|
590
|
+
status: "building",
|
|
591
|
+
text: token,
|
|
592
|
+
...turnId ? { turnId } : {},
|
|
593
|
+
...segmentId ? { segmentId } : {}
|
|
594
|
+
}));
|
|
595
|
+
}
|
|
596
|
+
function markPlan(view, content, turnId) {
|
|
597
|
+
const marker = planMarker(content);
|
|
598
|
+
const i = findLastIndex(view.blocks, (b) => isBuildingPlan(b, turnId));
|
|
599
|
+
const building = view.blocks[i];
|
|
600
|
+
if (building?.type === "plan") {
|
|
601
|
+
const settled = { type: "plan", id: building.id, ...marker, text: "", ...building.turnId ? { turnId: building.turnId } : {} };
|
|
602
|
+
return { ...view, blocks: replaceAt(view.blocks, i, settled) };
|
|
603
|
+
}
|
|
604
|
+
return append(view, (id) => ({ type: "plan", id, text: "", ...marker }));
|
|
605
|
+
}
|
|
606
|
+
function requestApproval(view, { id: approvalId, kind, subject, scope, detail, turnId }) {
|
|
607
|
+
return append(view, (id) => ({
|
|
608
|
+
type: "approval",
|
|
609
|
+
id,
|
|
610
|
+
approvalId,
|
|
611
|
+
kind,
|
|
612
|
+
subject,
|
|
613
|
+
scope,
|
|
614
|
+
status: "pending",
|
|
615
|
+
...detail ? { detail } : {},
|
|
616
|
+
...turnId ? { turnId } : {}
|
|
617
|
+
}));
|
|
618
|
+
}
|
|
619
|
+
function settleApproval(view, { id, granted }) {
|
|
620
|
+
const i = findLastIndex(view.blocks, (b) => b.type === "approval" && b.approvalId === id);
|
|
621
|
+
const request = view.blocks[i];
|
|
622
|
+
if (request?.type !== "approval") return view;
|
|
623
|
+
return { ...view, blocks: replaceAt(view.blocks, i, { ...request, status: granted ? "granted" : "denied", decidedBy: "asked" }) };
|
|
624
|
+
}
|
|
625
|
+
function decideApproval(view, { kind, subject, scope, detail, granted, source }) {
|
|
626
|
+
return append(view, (id) => ({
|
|
627
|
+
type: "approval",
|
|
628
|
+
id,
|
|
629
|
+
kind,
|
|
630
|
+
subject,
|
|
631
|
+
scope,
|
|
632
|
+
status: granted ? "granted" : "denied",
|
|
633
|
+
decidedBy: source,
|
|
634
|
+
...detail ? { detail } : {}
|
|
635
|
+
}));
|
|
636
|
+
}
|
|
637
|
+
function reportUsage(view, message) {
|
|
638
|
+
return isMeasured(message.totals) ? setUsageLine(view, message) : view;
|
|
639
|
+
}
|
|
640
|
+
function endTurn(view, { turnId, outcome }) {
|
|
641
|
+
let next = dropBuildingPlan(view, turnId);
|
|
642
|
+
const planText = outcome === "plan" ? finalSegmentIndex(next.blocks, turnId, true) : -1;
|
|
643
|
+
if (planText >= 0) next = { ...next, blocks: next.blocks.filter((_, i) => i !== planText) };
|
|
644
|
+
return closeOpenBlocks(next, outcome === "stopped" ? "stopped" : outcome === "error" ? "failed" : null);
|
|
645
|
+
}
|
|
646
|
+
function markCompaction(view, summary) {
|
|
647
|
+
const i = findLastIndex(view.blocks, (b) => b.type === "message" && b.role === "planner" && b.turnId === void 0 && b.text === summary);
|
|
648
|
+
const announced = view.blocks[i];
|
|
649
|
+
if (announced?.type === "message") return { ...view, blocks: replaceAt(view.blocks, i, { ...announced, role: "system" }) };
|
|
650
|
+
return append(view, (id) => settledMessage(id, "system", summary));
|
|
651
|
+
}
|
|
652
|
+
function syncTranscript(view, { plan, turnId }) {
|
|
653
|
+
const history = plan.conversationHistory ?? [];
|
|
654
|
+
const committed = findLastIndex(history, (e) => e.kind === "plan_generated");
|
|
655
|
+
let next = view;
|
|
656
|
+
let latest = view.transcriptAt;
|
|
657
|
+
for (const [i, entry] of history.entries()) {
|
|
658
|
+
const isNew = view.transcriptAt === void 0 || entry.timestamp > view.transcriptAt;
|
|
659
|
+
if (isNew && entry.kind === "plan_generated") next = markPlan(next, entry.content, i === committed ? turnId : void 0);
|
|
660
|
+
if (isNew && entry.kind === "system") next = append(next, (id) => settledMessage(id, "system", entry.content));
|
|
661
|
+
if (isNew && entry.kind === "compaction") next = markCompaction(next, entry.content);
|
|
662
|
+
if (latest === void 0 || entry.timestamp > latest) latest = entry.timestamp;
|
|
663
|
+
}
|
|
664
|
+
return next === view && latest === view.transcriptAt ? view : { ...next, transcriptAt: latest };
|
|
665
|
+
}
|
|
666
|
+
function reduceConversation(view, input) {
|
|
667
|
+
switch (input.type) {
|
|
668
|
+
case "local_entry":
|
|
669
|
+
return append(view, (id) => settledMessage(id, input.role, input.text));
|
|
670
|
+
case "planner_turn_started":
|
|
671
|
+
return input.prompt === void 0 ? view : openTurn(view, input.turnId, input.prompt);
|
|
672
|
+
case "planner_text_delta":
|
|
673
|
+
return streamText(view, input);
|
|
674
|
+
case "planner_message":
|
|
675
|
+
return settleReply(view, input);
|
|
676
|
+
case "planner_text_retracted":
|
|
677
|
+
return retractText(view, input);
|
|
678
|
+
case "planner_thinking_delta":
|
|
679
|
+
return think(view, input.text, input.turnId, input.segmentId, input.subagentId);
|
|
680
|
+
case "research_step":
|
|
681
|
+
return announceTool(view, input);
|
|
682
|
+
case "research_step_done":
|
|
683
|
+
return settleTool(view, input);
|
|
684
|
+
case "subagent_started":
|
|
685
|
+
return startSubagent(view, input);
|
|
686
|
+
case "subagent_finished":
|
|
687
|
+
return finishSubagent(view, input);
|
|
688
|
+
case "plan_token":
|
|
689
|
+
return streamPlan(view, input);
|
|
690
|
+
case "plan_generated":
|
|
691
|
+
return syncTranscript(view, input);
|
|
692
|
+
case "planner_turn_ended":
|
|
693
|
+
return endTurn(view, input);
|
|
694
|
+
case "approval_request":
|
|
695
|
+
return requestApproval(view, input);
|
|
696
|
+
case "approval_settled":
|
|
697
|
+
return settleApproval(view, input);
|
|
698
|
+
case "approval_decided":
|
|
699
|
+
return decideApproval(view, input);
|
|
700
|
+
case "planner_usage":
|
|
701
|
+
return reportUsage(view, input);
|
|
702
|
+
default:
|
|
703
|
+
return view;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// src/conversation/transcript.ts
|
|
708
|
+
var USER = 0;
|
|
709
|
+
var RESEARCH = 1;
|
|
710
|
+
var SETTLED = 2;
|
|
711
|
+
function fromEntry(entry) {
|
|
712
|
+
if (entry.kind === "plan_generated") return (id) => ({ type: "plan", id: id(), text: "", ...planMarker(entry.content) });
|
|
713
|
+
const role = entry.kind === "system" || entry.kind === "compaction" ? "system" : entry.role === "user" ? "user" : "planner";
|
|
714
|
+
return (id) => settledMessage(id(), role, entry.content);
|
|
715
|
+
}
|
|
716
|
+
function spawnBrief(step) {
|
|
717
|
+
try {
|
|
718
|
+
const args = JSON.parse(step.args);
|
|
719
|
+
const prompt = args && typeof args === "object" ? args.prompt : void 0;
|
|
720
|
+
return typeof prompt === "string" ? prompt.trim() : void 0;
|
|
721
|
+
} catch {
|
|
722
|
+
return void 0;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
function spawnedBy(step, unclaimed) {
|
|
726
|
+
const byId = unclaimed.find((e) => e.subagentId === step.toolCallId);
|
|
727
|
+
if (byId) return byId;
|
|
728
|
+
if (step.tool !== "spawn_research_agent") return void 0;
|
|
729
|
+
const brief = spawnBrief(step);
|
|
730
|
+
return brief === void 0 ? void 0 : unclaimed.find((e) => e.brief === brief);
|
|
731
|
+
}
|
|
732
|
+
function research(log) {
|
|
733
|
+
const subagents = log.filter((e) => "type" in e && e.type === "subagent");
|
|
734
|
+
const ownSubagent = new Set(subagents.map((e) => e.subagentId));
|
|
735
|
+
const unclaimed = [...subagents];
|
|
736
|
+
const spawnCall = /* @__PURE__ */ new Map();
|
|
737
|
+
const placed = [];
|
|
738
|
+
for (const entry of log) {
|
|
739
|
+
if ("type" in entry) {
|
|
740
|
+
if (entry.type === "system") placed.push({ at: entry.timestamp, rank: RESEARCH, build: (id) => settledMessage(id(), "system", entry.content) });
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
if (entry.subagentId && ownSubagent.has(entry.subagentId)) continue;
|
|
744
|
+
const spawned = spawnedBy(entry, unclaimed);
|
|
745
|
+
if (spawned) {
|
|
746
|
+
unclaimed.splice(unclaimed.indexOf(spawned), 1);
|
|
747
|
+
spawnCall.set(spawned.subagentId, entry.toolCallId);
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
750
|
+
placed.push({ at: entry.timestamp, rank: RESEARCH, build: (id) => toolFromStep(id(), entry) });
|
|
751
|
+
}
|
|
752
|
+
for (const entry of subagents) {
|
|
753
|
+
const toolCallId = spawnCall.get(entry.subagentId);
|
|
754
|
+
const steps = log.filter((e) => !("type" in e) && e.subagentId === entry.subagentId);
|
|
755
|
+
placed.push({
|
|
756
|
+
at: entry.timestamp,
|
|
757
|
+
rank: RESEARCH,
|
|
758
|
+
build: (id) => subagentBlock(id(), {
|
|
759
|
+
subagentId: entry.subagentId,
|
|
760
|
+
brief: entry.brief,
|
|
761
|
+
status: entry.outcome,
|
|
762
|
+
digest: entry.digest,
|
|
763
|
+
children: steps.map((step) => toolFromStep(id(), step)),
|
|
764
|
+
toolCallId,
|
|
765
|
+
model: entry.model,
|
|
766
|
+
usage: entry.usage
|
|
767
|
+
})
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
return placed;
|
|
771
|
+
}
|
|
772
|
+
function fromTranscript(conversationHistory, researchLog, plannerUsage) {
|
|
773
|
+
const history = conversationHistory ?? [];
|
|
774
|
+
const compacted = history[0]?.kind === "compaction" ? history[0] : void 0;
|
|
775
|
+
const kept = compacted ? history.slice(1) : history;
|
|
776
|
+
const cutoff = compacted ? kept[0]?.timestamp ?? compacted.timestamp : "";
|
|
777
|
+
const placed = [
|
|
778
|
+
...kept.map((entry) => ({ at: entry.timestamp, rank: entry.role === "user" && !entry.kind ? USER : SETTLED, build: fromEntry(entry) })),
|
|
779
|
+
...research(researchLog ?? []).filter((item) => item.at >= cutoff)
|
|
780
|
+
];
|
|
781
|
+
placed.sort((a, b) => a.at < b.at ? -1 : a.at > b.at ? 1 : a.rank - b.rank);
|
|
782
|
+
if (compacted) placed.unshift({ at: "", rank: SETTLED, build: fromEntry(compacted) });
|
|
783
|
+
let nextId = 1;
|
|
784
|
+
const id = () => `b${nextId++}`;
|
|
785
|
+
const blocks = placed.map((item) => item.build(id));
|
|
786
|
+
if (plannerUsage && isMeasured(plannerUsage.totals)) blocks.push(usageBlock(id(), usageLine(plannerUsage)));
|
|
787
|
+
const transcriptAt = history.reduce((latest, e) => latest === void 0 || e.timestamp > latest ? e.timestamp : latest, void 0);
|
|
788
|
+
return { blocks, nextId, ...transcriptAt ? { transcriptAt } : {} };
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// src/services/harness/agentTools.ts
|
|
792
|
+
var KNOWN_TOOLS = {
|
|
793
|
+
// Claude Code
|
|
794
|
+
read: "read_file",
|
|
795
|
+
grep: "grep",
|
|
796
|
+
glob: "glob",
|
|
797
|
+
bash: "bash",
|
|
798
|
+
ls: "list_dir",
|
|
799
|
+
webfetch: "fetch",
|
|
800
|
+
websearch: "web_search",
|
|
801
|
+
// Codex — its sandboxed command tool is a shell by another name
|
|
802
|
+
shell: "bash",
|
|
803
|
+
exec_command: "bash",
|
|
804
|
+
local_shell: "bash",
|
|
805
|
+
// OpenCode
|
|
806
|
+
list: "list_dir",
|
|
807
|
+
fetch: "fetch"
|
|
808
|
+
};
|
|
809
|
+
function mapAgentTool(name) {
|
|
810
|
+
const normalized = name.trim().toLowerCase().replace(/[_-]/g, "");
|
|
811
|
+
const direct = KNOWN_TOOLS[normalized] ?? KNOWN_TOOLS[name.trim().toLowerCase()];
|
|
812
|
+
if (direct) {
|
|
813
|
+
return { tool: direct, toolLabel: name };
|
|
814
|
+
}
|
|
815
|
+
return { tool: "agent_tool", toolLabel: name };
|
|
816
|
+
}
|
|
817
|
+
function normalizeAgentArgs(tool, args) {
|
|
818
|
+
const out = { ...args };
|
|
819
|
+
const alias = (from, to) => {
|
|
820
|
+
if (out[to] === void 0 && out[from] !== void 0) out[to] = out[from];
|
|
821
|
+
};
|
|
822
|
+
if (tool === "read_file" || tool === "list_dir") {
|
|
823
|
+
alias("filePath", "path");
|
|
824
|
+
alias("file_path", "path");
|
|
825
|
+
alias("target", "path");
|
|
826
|
+
}
|
|
827
|
+
if (tool === "bash") {
|
|
828
|
+
alias("cmd", "command");
|
|
829
|
+
alias("script", "command");
|
|
830
|
+
if (typeof out.command !== "string" && Array.isArray(out.command)) {
|
|
831
|
+
out.command = out.command.join(" ");
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
if (tool === "fetch") alias("uri", "url");
|
|
835
|
+
if (tool === "web_search") alias("q", "query");
|
|
836
|
+
return out;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// src/conversation/taskLog.ts
|
|
840
|
+
var EMPTY_TASK_LOG = { blocks: [], nextId: 1, queued: [], working: false, streams: {} };
|
|
841
|
+
var TEXT_STREAM = "text";
|
|
842
|
+
var thinkingStream = (subagentId) => `thinking:${subagentId ?? ""}`;
|
|
843
|
+
function withoutStream(view, key) {
|
|
844
|
+
if (!(key in view.streams)) return view;
|
|
845
|
+
return { ...view, streams: Object.fromEntries(Object.entries(view.streams).filter(([k]) => k !== key)) };
|
|
846
|
+
}
|
|
847
|
+
function blockText(text2) {
|
|
848
|
+
return text2.replace(/^\n+/, "");
|
|
849
|
+
}
|
|
850
|
+
function openStream(view, lane, key) {
|
|
851
|
+
const id = view.streams[key];
|
|
852
|
+
if (id === void 0) return -1;
|
|
853
|
+
const blocks = laneBlocks(view, lane);
|
|
854
|
+
const i = findLastIndex(blocks, (b) => b.type !== "usage");
|
|
855
|
+
const block = blocks[i];
|
|
856
|
+
return block?.id === id && (block.type === "message" || block.type === "thinking") && block.streaming ? i : -1;
|
|
857
|
+
}
|
|
858
|
+
function streamDelta(view, lane, key, text2, make) {
|
|
859
|
+
const i = openStream(view, lane, key);
|
|
860
|
+
const open = laneBlocks(view, lane)[i];
|
|
861
|
+
if (open?.type === "message" || open?.type === "thinking") return laneReplace(view, lane, i, { ...open, text: open.text + text2 });
|
|
862
|
+
const next = appendOutput(view, lane, (id) => make(id, blockText(text2)));
|
|
863
|
+
return { ...next, streams: { ...next.streams, [key]: `b${view.nextId}` } };
|
|
864
|
+
}
|
|
865
|
+
function streamWhole(view, lane, key, text2, make) {
|
|
866
|
+
const id = view.streams[key];
|
|
867
|
+
const blocks = laneBlocks(view, lane);
|
|
868
|
+
const i = id === void 0 ? -1 : findLastIndex(blocks, (b) => b.id === id);
|
|
869
|
+
const streamed = blocks[i];
|
|
870
|
+
const next = withoutStream(view, key);
|
|
871
|
+
if (streamed?.type === "message" || streamed?.type === "thinking") {
|
|
872
|
+
return laneReplace(next, lane, i, { ...streamed, text: blockText(text2), streaming: false });
|
|
873
|
+
}
|
|
874
|
+
return appendOutput(next, lane, make);
|
|
875
|
+
}
|
|
876
|
+
function agentText(view, text2) {
|
|
877
|
+
return streamWhole(view, null, TEXT_STREAM, text2, (id) => settledMessage(id, "agent", blockText(text2)));
|
|
878
|
+
}
|
|
879
|
+
function agentTextDelta(view, text2) {
|
|
880
|
+
return streamDelta(view, null, TEXT_STREAM, text2, (id, t) => ({ type: "message", id, role: "agent", text: t, streaming: true }));
|
|
881
|
+
}
|
|
882
|
+
function thinkingBlock(id, text2, streaming, subagentId) {
|
|
883
|
+
return { type: "thinking", id, text: text2, streaming, ...subagentId ? { subagentId } : {} };
|
|
884
|
+
}
|
|
885
|
+
function thinkingDelta(view, { text: text2, subagentId }) {
|
|
886
|
+
const [placed, lane] = laneOf(view, subagentId);
|
|
887
|
+
return streamDelta(placed, lane, thinkingStream(subagentId), text2, (id, t) => thinkingBlock(id, t, true, subagentId));
|
|
888
|
+
}
|
|
889
|
+
function thinkingWhole(view, { text: text2, subagentId }) {
|
|
890
|
+
const [placed, lane] = laneOf(view, subagentId);
|
|
891
|
+
return streamWhole(placed, lane, thinkingStream(subagentId), text2, (id) => thinkingBlock(id, blockText(text2), false, subagentId));
|
|
892
|
+
}
|
|
893
|
+
function announceTool2(view, { id: toolCallId, name, args, subagentId }) {
|
|
894
|
+
const { tool, toolLabel } = mapAgentTool(name);
|
|
895
|
+
const [placed, lane] = laneOf(view, subagentId);
|
|
896
|
+
return appendOutput(placed, lane, (id) => {
|
|
897
|
+
const call = pendingTool(id, { tool, toolLabel, args, toolCallId });
|
|
898
|
+
const parsed = parseArgs2(args);
|
|
899
|
+
return parsed ? { ...call, headline: toolHeadline(tool, JSON.stringify(normalizeAgentArgs(tool, parsed)), toolLabel) } : call;
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
function parseArgs2(args) {
|
|
903
|
+
try {
|
|
904
|
+
const parsed = JSON.parse(args);
|
|
905
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
906
|
+
} catch {
|
|
907
|
+
return null;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
function settleTool2(view, { id: toolCallId, output, success, subagentId }) {
|
|
911
|
+
const [placed, lane] = laneOf(view, subagentId);
|
|
912
|
+
const blocks = laneBlocks(placed, lane);
|
|
913
|
+
const i = findLastIndex(blocks, (b) => b.type === "tool" && b.status === "pending" && b.toolCallId === toolCallId);
|
|
914
|
+
const pending = blocks[i];
|
|
915
|
+
const outcome = success ? "success" : "failure";
|
|
916
|
+
if (pending?.type === "tool") return laneReplace(placed, lane, i, finishedTool(pending, output, outcome));
|
|
917
|
+
if (lane === null && blocks.some((b) => b.type === "subagent" && b.toolCallId === toolCallId)) return view;
|
|
918
|
+
return laneAppend(placed, lane, (id) => finishedTool(pendingTool(id, { tool: "agent_tool", toolLabel: "Tool", args: "", toolCallId }), output, outcome));
|
|
919
|
+
}
|
|
920
|
+
function startSubagent2(view, { subagentId, brief, model }) {
|
|
921
|
+
return updateSubagent(view, subagentId, void 0, (block) => ({ ...block, ...brief ? { brief } : {}, ...model ? { model } : {} }));
|
|
922
|
+
}
|
|
923
|
+
function finishSubagent2(view, { subagentId, outcome, digest }) {
|
|
924
|
+
const usage = view.usage?.bySubagent?.[subagentId];
|
|
925
|
+
const next = updateSubagent(view, subagentId, void 0, (block) => ({
|
|
926
|
+
...block,
|
|
927
|
+
status: outcome,
|
|
928
|
+
digest,
|
|
929
|
+
children: block.children.map(sealed),
|
|
930
|
+
...usage ? { usage } : {}
|
|
931
|
+
}));
|
|
932
|
+
return withoutStream(next, thinkingStream(subagentId));
|
|
933
|
+
}
|
|
934
|
+
function reportUsage2(view, { record }) {
|
|
935
|
+
const usage = addPlannerUsage(view.usage ?? { totals: {} }, record);
|
|
936
|
+
const next = { ...view, usage };
|
|
937
|
+
return isMeasured(usage.totals) ? setUsageLine(next, usageLine(usage)) : next;
|
|
938
|
+
}
|
|
939
|
+
function runnerToolSubject(tool, args) {
|
|
940
|
+
const { tool: mapped, toolLabel } = mapAgentTool(tool);
|
|
941
|
+
const parsed = parseArgs2(args);
|
|
942
|
+
const headline = toolHeadline(mapped, parsed ? JSON.stringify(normalizeAgentArgs(mapped, parsed)) : args, toolLabel);
|
|
943
|
+
return headline.keyArg ? `${headline.name}(${headline.keyArg})` : headline.name;
|
|
944
|
+
}
|
|
945
|
+
function requestApproval2(view, { approvalId, tool, args, allowForTask, toolCallId }) {
|
|
946
|
+
if (view.blocks.some((b) => b.type === "approval" && b.approvalId === approvalId)) return view;
|
|
947
|
+
return append(view, (id) => ({
|
|
948
|
+
type: "approval",
|
|
949
|
+
id,
|
|
950
|
+
approvalId,
|
|
951
|
+
kind: "runner_tool",
|
|
952
|
+
subject: runnerToolSubject(tool, args),
|
|
953
|
+
scope: tool,
|
|
954
|
+
status: "pending",
|
|
955
|
+
...allowForTask ? { allowForTask } : {},
|
|
956
|
+
...toolCallId ? { toolCallId } : {}
|
|
957
|
+
}));
|
|
958
|
+
}
|
|
959
|
+
function settleApproval2(view, approvalId, settle) {
|
|
960
|
+
const i = findLastIndex(view.blocks, (b) => b.type === "approval" && b.approvalId === approvalId);
|
|
961
|
+
const block = view.blocks[i];
|
|
962
|
+
if (block?.type !== "approval" || block.status !== "pending") return view;
|
|
963
|
+
return { ...view, blocks: replaceAt(view.blocks, i, settle(block)) };
|
|
964
|
+
}
|
|
965
|
+
function decideApproval2(view, { approvalId, decision, note }) {
|
|
966
|
+
return settleApproval2(view, approvalId, (block) => ({
|
|
967
|
+
...block,
|
|
968
|
+
status: decision === "deny" ? "denied" : "granted",
|
|
969
|
+
decidedBy: "asked",
|
|
970
|
+
...decision === "allowForTask" ? { forTask: true } : {},
|
|
971
|
+
...note ? { note } : {}
|
|
972
|
+
}));
|
|
973
|
+
}
|
|
974
|
+
function startTurn(view, { message, messageId }) {
|
|
975
|
+
const queued = messageId ? view.queued.filter((m) => m.id !== messageId) : view.queued;
|
|
976
|
+
return append({ ...view, queued, working: true }, (id) => settledMessage(id, "user", message));
|
|
977
|
+
}
|
|
978
|
+
var CUT = { completed: null, interrupted: "stopped", failed: "failed" };
|
|
979
|
+
function endTurn2(view, { reason }) {
|
|
980
|
+
const closed = closeOpenBlocks({ ...view, working: false, lastTurnEnd: reason, streams: {} }, CUT[reason]);
|
|
981
|
+
return reason === "interrupted" ? append(closed, (id) => settledMessage(id, "system", "Interrupted.")) : closed;
|
|
982
|
+
}
|
|
983
|
+
function queueMessage(view, { messageId, text: text2 }) {
|
|
984
|
+
if (view.queued.some((m) => m.id === messageId)) return view;
|
|
985
|
+
return { ...view, queued: [...view.queued, { id: messageId, text: text2 }] };
|
|
986
|
+
}
|
|
987
|
+
function unqueueMessage(view, { messageId }) {
|
|
988
|
+
const queued = view.queued.filter((m) => m.id !== messageId);
|
|
989
|
+
return queued.length === view.queued.length ? view : { ...view, queued };
|
|
990
|
+
}
|
|
991
|
+
function reduceTaskLog(view, event) {
|
|
992
|
+
switch (event.type) {
|
|
993
|
+
case "turn_start":
|
|
994
|
+
return startTurn(view, event);
|
|
995
|
+
case "turn_end":
|
|
996
|
+
return endTurn2(view, event);
|
|
997
|
+
case "text_delta":
|
|
998
|
+
return agentTextDelta(view, event.text);
|
|
999
|
+
case "text":
|
|
1000
|
+
return agentText(view, event.text);
|
|
1001
|
+
case "thinking_delta":
|
|
1002
|
+
return thinkingDelta(view, event);
|
|
1003
|
+
case "thinking":
|
|
1004
|
+
return thinkingWhole(view, event);
|
|
1005
|
+
case "tool_call":
|
|
1006
|
+
return announceTool2(view, event);
|
|
1007
|
+
case "tool_result":
|
|
1008
|
+
return settleTool2(view, event);
|
|
1009
|
+
case "subagent_started":
|
|
1010
|
+
return startSubagent2(view, event);
|
|
1011
|
+
case "subagent_finished":
|
|
1012
|
+
return finishSubagent2(view, event);
|
|
1013
|
+
case "usage":
|
|
1014
|
+
return reportUsage2(view, event);
|
|
1015
|
+
case "message_queued":
|
|
1016
|
+
return queueMessage(view, event);
|
|
1017
|
+
case "message_removed":
|
|
1018
|
+
return unqueueMessage(view, event);
|
|
1019
|
+
case "error":
|
|
1020
|
+
return appendOutput(view, null, (id) => settledMessage(id, "error", event.message));
|
|
1021
|
+
case "approval_requested":
|
|
1022
|
+
return requestApproval2(view, event);
|
|
1023
|
+
case "approval_decided":
|
|
1024
|
+
return decideApproval2(view, event);
|
|
1025
|
+
case "approval_withdrawn":
|
|
1026
|
+
return settleApproval2(view, event.approvalId, (block) => ({ ...block, status: "withdrawn" }));
|
|
1027
|
+
default:
|
|
1028
|
+
return view;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
function replayTaskLog(events, from = EMPTY_TASK_LOG) {
|
|
1032
|
+
return events.reduce(reduceTaskLog, from);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
// src/conversation/promptHold.ts
|
|
1036
|
+
var EMPTY_HOLD = [];
|
|
1037
|
+
function holdPrompt(hold, text2) {
|
|
1038
|
+
return [...hold, text2];
|
|
1039
|
+
}
|
|
1040
|
+
function drainNext(hold) {
|
|
1041
|
+
const [text2, ...rest] = hold;
|
|
1042
|
+
return text2 === void 0 ? void 0 : { text: text2, rest };
|
|
1043
|
+
}
|
|
1044
|
+
function unsendLatest(hold) {
|
|
1045
|
+
if (hold.length === 0) return void 0;
|
|
1046
|
+
return { text: hold[hold.length - 1], rest: hold.slice(0, -1) };
|
|
1047
|
+
}
|
|
1048
|
+
function unsendAll(hold) {
|
|
1049
|
+
if (hold.length === 0) return void 0;
|
|
1050
|
+
return { text: hold.join("\n"), rest: EMPTY_HOLD };
|
|
1051
|
+
}
|
|
1052
|
+
function aheadOfDraft(text2, draft) {
|
|
1053
|
+
return draft ? `${text2}
|
|
1054
|
+
${draft}` : text2;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// src/conversation/turnGate.ts
|
|
1058
|
+
var NO_TURN = { open: null, stopped: null };
|
|
1059
|
+
var TURN_STREAM = /* @__PURE__ */ new Set([
|
|
1060
|
+
"planner_text_delta",
|
|
1061
|
+
"planner_thinking_delta",
|
|
1062
|
+
"planner_text_retracted",
|
|
1063
|
+
"planner_message",
|
|
1064
|
+
"plan_token",
|
|
1065
|
+
"research_step",
|
|
1066
|
+
"research_step_done",
|
|
1067
|
+
"subagent_started",
|
|
1068
|
+
"subagent_finished",
|
|
1069
|
+
"planner_turn_ended"
|
|
1070
|
+
]);
|
|
1071
|
+
function cutOff(gate, input) {
|
|
1072
|
+
if (gate.stopped === null || !TURN_STREAM.has(input.type)) return false;
|
|
1073
|
+
const turnId = "turnId" in input ? input.turnId : void 0;
|
|
1074
|
+
return turnId === void 0 || turnId === gate.stopped;
|
|
1075
|
+
}
|
|
1076
|
+
function withGate(live, gate) {
|
|
1077
|
+
return gate.open === live.gate.open && gate.stopped === live.gate.stopped ? live : { ...live, gate };
|
|
1078
|
+
}
|
|
1079
|
+
function followTurn(view, gate, input) {
|
|
1080
|
+
const live = { view, gate };
|
|
1081
|
+
if (input.type === "planner_turn_started") {
|
|
1082
|
+
return withGate({ view: reduceConversation(view, input), gate }, { open: input.turnId, stopped: null });
|
|
1083
|
+
}
|
|
1084
|
+
if (cutOff(gate, input)) {
|
|
1085
|
+
return input.type === "planner_turn_ended" ? withGate(live, { ...gate, stopped: null }) : live;
|
|
1086
|
+
}
|
|
1087
|
+
const next = { view: reduceConversation(view, input), gate };
|
|
1088
|
+
return input.type === "planner_turn_ended" && input.turnId === gate.open ? withGate(next, { ...gate, open: null }) : next;
|
|
1089
|
+
}
|
|
1090
|
+
function stopTurn(view, gate) {
|
|
1091
|
+
if (gate.open === null) return { view, gate };
|
|
1092
|
+
return {
|
|
1093
|
+
view: reduceConversation(view, { type: "planner_turn_ended", turnId: gate.open, outcome: "stopped" }),
|
|
1094
|
+
gate: { open: null, stopped: gate.open }
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// src/conversation/detail.ts
|
|
1099
|
+
function hasHiddenDetail(blocks) {
|
|
1100
|
+
return blocks.some((b) => b.type === "thinking" || b.type === "tool" || b.type === "subagent");
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// src/conversation/notices.ts
|
|
1104
|
+
function taskStartedNotice(title, runner) {
|
|
1105
|
+
return runner ? `Started "${title}" \xB7 ${runner}` : `Started "${title}"`;
|
|
1106
|
+
}
|
|
180
1107
|
// Annotate the CommonJS export names for ESM import in node:
|
|
181
1108
|
0 && (module.exports = {
|
|
182
1109
|
CHECKPOINT_TRUNCATE_LENGTH,
|
|
1110
|
+
EMPTY_CONVERSATION,
|
|
1111
|
+
EMPTY_HOLD,
|
|
1112
|
+
EMPTY_TASK_LOG,
|
|
1113
|
+
NO_TURN,
|
|
1114
|
+
aheadOfDraft,
|
|
183
1115
|
canMergeTasks,
|
|
184
1116
|
canSetDependencies,
|
|
185
1117
|
canSplitTask,
|
|
186
1118
|
capConflictFiles,
|
|
187
1119
|
dependencyCandidates,
|
|
188
1120
|
dependentsOf,
|
|
1121
|
+
drainNext,
|
|
1122
|
+
followTurn,
|
|
1123
|
+
fromTranscript,
|
|
1124
|
+
hasHiddenDetail,
|
|
1125
|
+
holdPrompt,
|
|
1126
|
+
outputLines,
|
|
1127
|
+
outputPreview,
|
|
1128
|
+
reduceConversation,
|
|
1129
|
+
reduceTaskLog,
|
|
1130
|
+
replayTaskLog,
|
|
1131
|
+
runnerToolSubject,
|
|
1132
|
+
stopTurn,
|
|
189
1133
|
summarizeToolCall,
|
|
190
|
-
|
|
1134
|
+
taskStartedNotice,
|
|
1135
|
+
toolHeadline,
|
|
1136
|
+
truncateCheckpointSummary,
|
|
1137
|
+
unsendAll,
|
|
1138
|
+
unsendLatest
|
|
191
1139
|
});
|
|
192
1140
|
//# sourceMappingURL=plan-utils.js.map
|