@ordewell/core 0.9.0 → 0.10.0
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-DpaaFdit.d.ts → IFileSystem-BCJBHFwB.d.ts} +3 -1
- package/dist/{IFileSystem-_m7HdoxJ.d.mts → IFileSystem-Bj6fy1cG.d.mts} +3 -1
- package/dist/{IRunner-BiQio8gJ.d.ts → IRunner-C7_SSpzG.d.ts} +614 -77
- package/dist/{IRunner-CxiymkP_.d.mts → IRunner-hWAJC4hg.d.mts} +614 -77
- package/dist/ModeResolver-m1d8Cb-j.d.mts +35 -0
- package/dist/ModeResolver-u5ZhRC8x.d.ts +35 -0
- package/dist/{Task-1Q19y7sZ.d.mts → Task-CxwnCJLi.d.mts} +346 -127
- package/dist/{Task-1Q19y7sZ.d.ts → Task-CxwnCJLi.d.ts} +346 -127
- package/dist/{chunk-UUU46PVQ.mjs → chunk-2IIRSBEQ.mjs} +109 -142
- package/dist/chunk-2IIRSBEQ.mjs.map +1 -0
- package/dist/{chunk-QUARDKVT.mjs → chunk-CH5IGWFS.mjs} +546 -967
- package/dist/chunk-CH5IGWFS.mjs.map +1 -0
- package/dist/chunk-DJAPDO6E.mjs +1358 -0
- package/dist/chunk-DJAPDO6E.mjs.map +1 -0
- package/dist/chunk-DNFT4SQG.mjs +97 -0
- package/dist/chunk-DNFT4SQG.mjs.map +1 -0
- package/dist/index.d.mts +3204 -3926
- package/dist/index.d.ts +3204 -3926
- package/dist/index.js +22283 -16472
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +20220 -14690
- package/dist/index.mjs.map +1 -1
- package/dist/parsing.d.mts +55 -3
- package/dist/parsing.d.ts +55 -3
- package/dist/parsing.js +311 -124
- package/dist/parsing.js.map +1 -1
- package/dist/parsing.mjs +86 -6
- package/dist/parsing.mjs.map +1 -1
- package/dist/plan-utils-B6yJdpgi.d.ts +2902 -0
- package/dist/plan-utils-CKXdD1AD.d.mts +2902 -0
- package/dist/plan-utils.d.mts +4 -5
- package/dist/plan-utils.d.ts +4 -5
- package/dist/plan-utils.js +471 -86
- package/dist/plan-utils.js.map +1 -1
- package/dist/plan-utils.mjs +68 -19
- package/dist/testing.d.mts +22 -4
- package/dist/testing.d.ts +22 -4
- package/dist/testing.js +46 -9
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +47 -11
- package/dist/testing.mjs.map +1 -1
- package/package.json +2 -10
- package/skills/tdd/SKILL.md +0 -1
- package/dist/ModeResolver-DE-o2lRF.d.mts +0 -48
- package/dist/ModeResolver-EWlpmVAy.d.ts +0 -48
- package/dist/chunk-2M4X3VB7.mjs +0 -432
- package/dist/chunk-2M4X3VB7.mjs.map +0 -1
- package/dist/chunk-H5O74L77.mjs +0 -185
- package/dist/chunk-H5O74L77.mjs.map +0 -1
- package/dist/chunk-QUARDKVT.mjs.map +0 -1
- package/dist/chunk-UUU46PVQ.mjs.map +0 -1
- package/dist/chunk-V3JHS7AX.mjs +0 -27
- package/dist/chunk-V3JHS7AX.mjs.map +0 -1
- package/dist/order-labels.d.mts +0 -30
- package/dist/order-labels.d.ts +0 -30
- package/dist/order-labels.js +0 -54
- package/dist/order-labels.js.map +0 -1
- package/dist/order-labels.mjs +0 -13
- package/dist/order-labels.mjs.map +0 -1
- package/dist/parsing-CZdp2p_n.d.ts +0 -154
- package/dist/parsing-GgUJoYKs.d.mts +0 -154
- package/dist/plan-utils-6rUr7ZOx.d.ts +0 -1222
- package/dist/plan-utils-P3PIEwNa.d.mts +0 -1222
package/dist/plan-utils.js
CHANGED
|
@@ -24,12 +24,21 @@ __export(plan_utils_exports, {
|
|
|
24
24
|
DEFAULT_RUNNERS: () => DEFAULT_RUNNERS,
|
|
25
25
|
EMPTY_CONVERSATION: () => EMPTY_CONVERSATION,
|
|
26
26
|
EMPTY_HOLD: () => EMPTY_HOLD,
|
|
27
|
+
EMPTY_RUN: () => EMPTY_RUN,
|
|
27
28
|
EMPTY_TASK_LOG: () => EMPTY_TASK_LOG,
|
|
28
29
|
NOT_STARTED: () => NOT_STARTED,
|
|
29
30
|
NO_TURN: () => NO_TURN,
|
|
31
|
+
SUPERVISOR_LEVEL_LABEL: () => SUPERVISOR_LEVEL_LABEL,
|
|
32
|
+
addAttemptUsage: () => addAttemptUsage,
|
|
33
|
+
addModelUsage: () => addModelUsage,
|
|
34
|
+
addPlannerUsage: () => addPlannerUsage,
|
|
35
|
+
addUsage: () => addUsage,
|
|
36
|
+
addUsageTotals: () => addUsageTotals,
|
|
30
37
|
aheadOfDraft: () => aheadOfDraft,
|
|
31
38
|
approvalLabel: () => approvalLabel,
|
|
32
39
|
awaitingLabel: () => awaitingLabel,
|
|
40
|
+
blockedByLabel: () => blockedByLabel,
|
|
41
|
+
blockingFailures: () => blockingFailures,
|
|
33
42
|
canMergeTasks: () => canMergeTasks,
|
|
34
43
|
canSetDependencies: () => canSetDependencies,
|
|
35
44
|
canSplitTask: () => canSplitTask,
|
|
@@ -41,10 +50,12 @@ __export(plan_utils_exports, {
|
|
|
41
50
|
diffStat: () => diffStat,
|
|
42
51
|
diffSummary: () => diffSummary,
|
|
43
52
|
drainNext: () => drainNext,
|
|
53
|
+
endAttemptUsage: () => endAttemptUsage,
|
|
44
54
|
followTurn: () => followTurn,
|
|
45
55
|
fromTranscript: () => fromTranscript,
|
|
46
56
|
hasHiddenDetail: () => hasHiddenDetail,
|
|
47
57
|
holdPrompt: () => holdPrompt,
|
|
58
|
+
isMeasured: () => isMeasured,
|
|
48
59
|
isTaskRunning: () => isTaskRunning,
|
|
49
60
|
loadedSkillTokens: () => loadedSkillTokens,
|
|
50
61
|
markAction: () => markAction,
|
|
@@ -54,32 +65,43 @@ __export(plan_utils_exports, {
|
|
|
54
65
|
opsEditable: () => opsEditable,
|
|
55
66
|
outputLines: () => outputLines,
|
|
56
67
|
outputPreview: () => outputPreview,
|
|
68
|
+
parseCount: () => parseCount,
|
|
69
|
+
partedPromptUsage: () => partedPromptUsage,
|
|
57
70
|
pastGateConfirmation: () => pastGateConfirmation,
|
|
71
|
+
pausedOnLimit: () => pausedOnLimit,
|
|
72
|
+
planUsage: () => planUsage,
|
|
73
|
+
plannerContextFill: () => plannerContextFill,
|
|
58
74
|
reduceConversation: () => reduceConversation,
|
|
59
75
|
reduceTaskLog: () => reduceTaskLog,
|
|
60
76
|
replayTaskLog: () => replayTaskLog,
|
|
77
|
+
resolveTaskRef: () => resolveTaskRef,
|
|
78
|
+
resumeLabel: () => resumeLabel,
|
|
79
|
+
resumeTime: () => resumeTime,
|
|
61
80
|
runnerToolSubject: () => runnerToolSubject,
|
|
62
81
|
skillTokens: () => skillTokens,
|
|
82
|
+
skippedLabel: () => skippedLabel,
|
|
83
|
+
startAttemptUsage: () => startAttemptUsage,
|
|
63
84
|
stopTurn: () => stopTurn,
|
|
64
85
|
summarizeToolCall: () => summarizeToolCall,
|
|
86
|
+
takeAt: () => takeAt,
|
|
65
87
|
taskOrderLabel: () => taskOrderLabel,
|
|
66
88
|
taskRef: () => taskRef,
|
|
89
|
+
taskRefOf: () => taskRefOf,
|
|
67
90
|
taskRowActions: () => taskRowActions,
|
|
68
91
|
taskRowView: () => taskRowView,
|
|
69
92
|
taskStartedNotice: () => taskStartedNotice,
|
|
70
93
|
taskStatusKind: () => taskStatusKind,
|
|
71
94
|
titledRefs: () => titledRefs,
|
|
72
95
|
titledTaskRef: () => titledTaskRef,
|
|
96
|
+
titledTaskRefOf: () => titledTaskRefOf,
|
|
73
97
|
toolHeadline: () => toolHeadline,
|
|
74
98
|
truncateCheckpointSummary: () => truncateCheckpointSummary,
|
|
75
99
|
unsendAll: () => unsendAll,
|
|
76
|
-
unsendLatest: () => unsendLatest
|
|
100
|
+
unsendLatest: () => unsendLatest,
|
|
101
|
+
usageLine: () => usageLine
|
|
77
102
|
});
|
|
78
103
|
module.exports = __toCommonJS(plan_utils_exports);
|
|
79
104
|
|
|
80
|
-
// src/services/TaskOps.ts
|
|
81
|
-
var import_uuid2 = require("uuid");
|
|
82
|
-
|
|
83
105
|
// src/models/Task.ts
|
|
84
106
|
var import_uuid = require("uuid");
|
|
85
107
|
|
|
@@ -102,9 +124,9 @@ function loadedSkillTokens(text2, loaded) {
|
|
|
102
124
|
return skillTokens(text2).filter((t) => loaded.includes(t.name));
|
|
103
125
|
}
|
|
104
126
|
|
|
105
|
-
// src/
|
|
127
|
+
// src/services/TaskRefs.ts
|
|
106
128
|
function taskOrderLabel(task, parent) {
|
|
107
|
-
return parent ? `${parent.order}.${task.order}` : String(task.order);
|
|
129
|
+
return parent ? `${parent.label ?? parent.order}.${task.order}` : String(task.order);
|
|
108
130
|
}
|
|
109
131
|
function taskRef(task, parent) {
|
|
110
132
|
return `#${taskOrderLabel(task, parent)}`;
|
|
@@ -112,10 +134,49 @@ function taskRef(task, parent) {
|
|
|
112
134
|
function titledTaskRef(task, parent) {
|
|
113
135
|
return `${taskRef(task, parent)} ${task.title}`;
|
|
114
136
|
}
|
|
137
|
+
function* depthFirst(tasks, parent, parentLabel) {
|
|
138
|
+
for (const task of tasks) {
|
|
139
|
+
const label = taskOrderLabel(task, parent ? { order: parent.order, label: parentLabel } : void 0);
|
|
140
|
+
yield { task, parent, label };
|
|
141
|
+
yield* depthFirst(task.subtasks ?? [], task, label);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function findTaskRef(tasks, id) {
|
|
145
|
+
for (const match of depthFirst(tasks)) if (match.task.id === id) return match;
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
function taskRefOf(tasks, id) {
|
|
149
|
+
const found = findTaskRef(tasks, id);
|
|
150
|
+
return found && `#${found.label}`;
|
|
151
|
+
}
|
|
152
|
+
function titledTaskRefOf(tasks, id) {
|
|
153
|
+
const found = findTaskRef(tasks, id);
|
|
154
|
+
return found && `#${found.label} ${found.task.title}`;
|
|
155
|
+
}
|
|
156
|
+
function resolveTaskRef(tasks, ref) {
|
|
157
|
+
const s = String(ref).trim();
|
|
158
|
+
if (!s) return void 0;
|
|
159
|
+
for (const { task } of depthFirst(tasks)) if (task.id === s) return task;
|
|
160
|
+
const label = s.startsWith("#") ? s.slice(1) : s;
|
|
161
|
+
if (/^\d+(\.\d+)*$/.test(label)) {
|
|
162
|
+
let level = tasks;
|
|
163
|
+
let found;
|
|
164
|
+
for (const order of label.split(".").map(Number)) {
|
|
165
|
+
found = level?.find((t) => t.order === order);
|
|
166
|
+
level = found?.subtasks;
|
|
167
|
+
}
|
|
168
|
+
return found;
|
|
169
|
+
}
|
|
170
|
+
for (const { task } of depthFirst(tasks)) if (task.title === s) return task;
|
|
171
|
+
return void 0;
|
|
172
|
+
}
|
|
115
173
|
|
|
116
174
|
// src/models/Task.ts
|
|
117
175
|
var SKILL_NAME = new RegExp(`^${SKILL_NAME_PATTERN}$`);
|
|
118
176
|
var DEFAULT_RUNNERS = ["claude-code"];
|
|
177
|
+
function pausedOnLimit(task) {
|
|
178
|
+
return task.status === "awaiting_user" && task.awaitingReason === "usage-limit";
|
|
179
|
+
}
|
|
119
180
|
|
|
120
181
|
// src/taskRow/status.ts
|
|
121
182
|
var isTaskRunning = (task) => task.status === "in_progress" || task.status === "running";
|
|
@@ -126,6 +187,8 @@ function taskStatusKind(task) {
|
|
|
126
187
|
return "done";
|
|
127
188
|
case "failed":
|
|
128
189
|
return "failed";
|
|
190
|
+
case "skipped":
|
|
191
|
+
return "skipped";
|
|
129
192
|
case "blocked":
|
|
130
193
|
return "blocked";
|
|
131
194
|
case "awaiting_user":
|
|
@@ -138,39 +201,63 @@ var AWAITING_LABELS = {
|
|
|
138
201
|
input: "Waiting for your input",
|
|
139
202
|
checkpoint: "Checkpoint",
|
|
140
203
|
conflict: "Merge conflict",
|
|
141
|
-
"files-changed": "Changed tracked files"
|
|
204
|
+
"files-changed": "Changed tracked files",
|
|
205
|
+
"usage-limit": "Paused on a usage limit"
|
|
142
206
|
};
|
|
143
207
|
function awaitingLabel(task) {
|
|
144
208
|
return task.status === "awaiting_user" && task.awaitingReason ? AWAITING_LABELS[task.awaitingReason] : null;
|
|
145
209
|
}
|
|
210
|
+
function blockedByLabel(refs) {
|
|
211
|
+
return `Blocked by ${refs.join(", ")}`;
|
|
212
|
+
}
|
|
213
|
+
function skippedLabel(reason) {
|
|
214
|
+
return reason ? `Skipped: ${reason}` : "Skipped";
|
|
215
|
+
}
|
|
146
216
|
function approvalLabel(count) {
|
|
147
217
|
if (!count) return null;
|
|
148
218
|
return count > 1 ? `Waiting for approval (${count})` : "Waiting for approval";
|
|
149
219
|
}
|
|
220
|
+
function resumeTime(at, now = /* @__PURE__ */ new Date()) {
|
|
221
|
+
const clock = at.toLocaleTimeString("en-US", { hour: "numeric", minute: at.getMinutes() === 0 ? void 0 : "2-digit", hour12: true }).replace(/[ \u202f]([AP]M)$/i, (_m, meridiem) => meridiem.toLowerCase());
|
|
222
|
+
if (at.toDateString() === now.toDateString()) return clock;
|
|
223
|
+
return `${at.toLocaleDateString("en-US", { month: "short", day: "numeric" })}, ${clock}`;
|
|
224
|
+
}
|
|
225
|
+
function resumeLabel(task, now = /* @__PURE__ */ new Date()) {
|
|
226
|
+
if (task.status !== "awaiting_user" || task.awaitingReason !== "usage-limit" || !task.resumeAt) return null;
|
|
227
|
+
const at = new Date(task.resumeAt);
|
|
228
|
+
return Number.isNaN(at.getTime()) ? null : `Resumes at ${resumeTime(at, now)}`;
|
|
229
|
+
}
|
|
150
230
|
|
|
151
|
-
// src/services/
|
|
152
|
-
|
|
231
|
+
// src/services/PlannedTask.ts
|
|
232
|
+
var import_uuid2 = require("uuid");
|
|
233
|
+
function findCycle(tasks) {
|
|
153
234
|
const byId = new Map(tasks.map((t) => [t.id, t]));
|
|
154
|
-
const
|
|
235
|
+
const path = [];
|
|
155
236
|
const done = /* @__PURE__ */ new Set();
|
|
156
237
|
const visit = (id) => {
|
|
157
238
|
if (done.has(id)) return null;
|
|
158
|
-
|
|
159
|
-
|
|
239
|
+
const at = path.indexOf(id);
|
|
240
|
+
if (at >= 0) return path.slice(at);
|
|
241
|
+
path.push(id);
|
|
160
242
|
for (const dep of byId.get(id)?.dependencies ?? []) {
|
|
161
243
|
const hit = visit(dep);
|
|
162
244
|
if (hit) return hit;
|
|
163
245
|
}
|
|
164
|
-
|
|
246
|
+
path.pop();
|
|
165
247
|
done.add(id);
|
|
166
248
|
return null;
|
|
167
249
|
};
|
|
168
250
|
for (const t of tasks) {
|
|
169
251
|
const hit = visit(t.id);
|
|
170
|
-
if (hit) return byId.get(
|
|
252
|
+
if (hit) return hit.map((id) => byId.get(id));
|
|
171
253
|
}
|
|
172
254
|
return null;
|
|
173
255
|
}
|
|
256
|
+
function detectCycle(tasks) {
|
|
257
|
+
return findCycle(tasks)?.[0].title ?? null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// src/services/TaskOps.ts
|
|
174
261
|
function canMergeTasks(tasks, selectedIds) {
|
|
175
262
|
if (selectedIds.length < 2) return { ok: false, error: "Select at least two tasks to merge" };
|
|
176
263
|
const idSet = new Set(selectedIds);
|
|
@@ -467,9 +554,13 @@ function normalizeAgentArgs(tool, args) {
|
|
|
467
554
|
function settledMessage(id, role, text2, turnId) {
|
|
468
555
|
return { type: "message", id, role, text: text2, streaming: false, ...turnId ? { turnId } : {} };
|
|
469
556
|
}
|
|
470
|
-
function userMessage(id, text2, skills, turnId) {
|
|
557
|
+
function userMessage(id, text2, skills, { turnId, forced } = {}) {
|
|
471
558
|
const block = settledMessage(id, "user", text2, turnId);
|
|
472
|
-
return
|
|
559
|
+
return {
|
|
560
|
+
...block,
|
|
561
|
+
...skills.length > 0 ? { skills: skills.map((s) => s.name) } : {},
|
|
562
|
+
...forced ? { forced: true } : {}
|
|
563
|
+
};
|
|
473
564
|
}
|
|
474
565
|
function skillLoadNotice({ invokedBy, name, source, path, attaches }) {
|
|
475
566
|
return { invokedBy, name, source, path, ...attaches ? { attaches } : {} };
|
|
@@ -488,8 +579,8 @@ function subagentBlock(id, { toolCallId, model, usage, turnId, ...fields }) {
|
|
|
488
579
|
...turnId ? { turnId } : {}
|
|
489
580
|
};
|
|
490
581
|
}
|
|
491
|
-
function usageBlock(id, { totals, bySubagent, contextFill }) {
|
|
492
|
-
return { type: "usage", id, totals, ...bySubagent ? { bySubagent } : {}, ...contextFill ? { contextFill } : {} };
|
|
582
|
+
function usageBlock(id, { totals, bySubagent, byModel, model, contextFill }) {
|
|
583
|
+
return { type: "usage", id, totals, ...byModel ? { byModel } : {}, ...model ? { model } : {}, ...bySubagent ? { bySubagent } : {}, ...contextFill ? { contextFill } : {} };
|
|
493
584
|
}
|
|
494
585
|
var STATUS_OF_OUTCOME = {
|
|
495
586
|
success: "ok",
|
|
@@ -528,6 +619,44 @@ function planMarker(content) {
|
|
|
528
619
|
return { status: content.startsWith("Plan updated") ? "updated" : "generated", ...count ? { taskCount: Number(count[1]) } : {} };
|
|
529
620
|
}
|
|
530
621
|
|
|
622
|
+
// src/services/blocking.ts
|
|
623
|
+
var UNSTARTED = /* @__PURE__ */ new Set(["pending", "approved", "blocked"]);
|
|
624
|
+
function blockingFailures(tasks) {
|
|
625
|
+
const byId = /* @__PURE__ */ new Map();
|
|
626
|
+
const index = (list) => {
|
|
627
|
+
for (const t of list) {
|
|
628
|
+
byId.set(t.id, t);
|
|
629
|
+
index(t.subtasks ?? []);
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
index(tasks);
|
|
633
|
+
const memo = /* @__PURE__ */ new Map();
|
|
634
|
+
const failuresOf = (id, visiting) => {
|
|
635
|
+
const known = memo.get(id);
|
|
636
|
+
if (known) return known;
|
|
637
|
+
const task = byId.get(id);
|
|
638
|
+
if (!task || !UNSTARTED.has(task.status) || visiting.has(id)) return [];
|
|
639
|
+
visiting.add(id);
|
|
640
|
+
const failed = /* @__PURE__ */ new Set();
|
|
641
|
+
for (const depId of task.dependencies) {
|
|
642
|
+
const dep = byId.get(depId);
|
|
643
|
+
if (!dep || dep.status === "completed" || dep.status === "skipped") continue;
|
|
644
|
+
if (dep.status === "failed") failed.add(depId);
|
|
645
|
+
else for (const root of failuresOf(depId, visiting)) failed.add(root);
|
|
646
|
+
}
|
|
647
|
+
visiting.delete(id);
|
|
648
|
+
const result = [...failed];
|
|
649
|
+
memo.set(id, result);
|
|
650
|
+
return result;
|
|
651
|
+
};
|
|
652
|
+
const blocked = /* @__PURE__ */ new Map();
|
|
653
|
+
for (const id of byId.keys()) {
|
|
654
|
+
const failures = failuresOf(id, /* @__PURE__ */ new Set());
|
|
655
|
+
if (failures.length > 0) blocked.set(id, failures);
|
|
656
|
+
}
|
|
657
|
+
return blocked;
|
|
658
|
+
}
|
|
659
|
+
|
|
531
660
|
// src/services/SessionMessage.ts
|
|
532
661
|
var CHECKPOINT_TRUNCATE_LENGTH = 120;
|
|
533
662
|
function truncateCheckpointSummary(summary) {
|
|
@@ -541,7 +670,22 @@ function capConflictFiles(files, max = 5) {
|
|
|
541
670
|
return files.length <= max ? files.join(", ") : `${files.slice(0, max).join(", ")}, +${files.length - max} more`;
|
|
542
671
|
}
|
|
543
672
|
|
|
673
|
+
// src/services/runSettingsText.ts
|
|
674
|
+
var SUPERVISOR_LEVEL_LABEL = { off: "Off", watch: "Watch", act: "Act" };
|
|
675
|
+
function parseCount(arg, min) {
|
|
676
|
+
if (arg === void 0 || !/^\d+$/.test(arg)) return null;
|
|
677
|
+
const n = Number(arg);
|
|
678
|
+
return n >= min ? n : null;
|
|
679
|
+
}
|
|
680
|
+
|
|
544
681
|
// src/models/Usage.ts
|
|
682
|
+
function partedPromptUsage(parts) {
|
|
683
|
+
const reported = [parts.uncached, parts.cacheRead, parts.cacheWrite].filter((n) => n !== void 0);
|
|
684
|
+
return {
|
|
685
|
+
...reported.length > 0 ? { inputTokens: reported.reduce((a, b) => a + b, 0) } : {},
|
|
686
|
+
...parts.cacheRead !== void 0 ? { cachedInputTokens: parts.cacheRead } : {}
|
|
687
|
+
};
|
|
688
|
+
}
|
|
545
689
|
var TOKEN_FIELDS = ["inputTokens", "outputTokens", "cachedInputTokens"];
|
|
546
690
|
function addUsage(totals, record) {
|
|
547
691
|
const next = { ...totals };
|
|
@@ -556,13 +700,21 @@ function addUsage(totals, record) {
|
|
|
556
700
|
return next;
|
|
557
701
|
}
|
|
558
702
|
function addPlannerUsage(usage, record) {
|
|
559
|
-
const next = { ...usage, totals: addUsage(usage.totals, record) };
|
|
703
|
+
const next = { ...usage, totals: addUsage(usage.totals, record), byModel: addModelUsage(usage.byModel ?? [], record) };
|
|
560
704
|
if (record.subagentId) {
|
|
561
705
|
next.bySubagent = { ...usage.bySubagent, [record.subagentId]: addUsage(usage.bySubagent?.[record.subagentId] ?? {}, record) };
|
|
562
706
|
return next;
|
|
563
707
|
}
|
|
708
|
+
if (!record.aggregate) {
|
|
709
|
+
if (record.source !== usage.source || record.model !== usage.model) {
|
|
710
|
+
delete next.lastPromptTokens;
|
|
711
|
+
delete next.contextWindow;
|
|
712
|
+
}
|
|
713
|
+
next.source = record.source;
|
|
714
|
+
next.model = record.model;
|
|
715
|
+
}
|
|
564
716
|
if (record.inputTokens !== void 0) next.lastPromptTokens = record.inputTokens;
|
|
565
|
-
if (record.contextWindow !== void 0
|
|
717
|
+
if (record.contextWindow !== void 0) next.contextWindow = record.contextWindow > 0 ? record.contextWindow : void 0;
|
|
566
718
|
return next;
|
|
567
719
|
}
|
|
568
720
|
function isMeasured(totals) {
|
|
@@ -572,6 +724,8 @@ function usageLine(usage) {
|
|
|
572
724
|
const contextFill = plannerContextFill(usage);
|
|
573
725
|
return {
|
|
574
726
|
totals: usage.totals,
|
|
727
|
+
...usage.byModel ? { byModel: usage.byModel } : {},
|
|
728
|
+
...usage.model ? { model: usage.model } : {},
|
|
575
729
|
...usage.bySubagent ? { bySubagent: usage.bySubagent } : {},
|
|
576
730
|
...contextFill ? { contextFill } : {}
|
|
577
731
|
};
|
|
@@ -580,6 +734,48 @@ function plannerContextFill(usage) {
|
|
|
580
734
|
if (usage.lastPromptTokens === void 0 || usage.contextWindow === void 0 || usage.contextWindow <= 0) return void 0;
|
|
581
735
|
return { usedTokens: usage.lastPromptTokens, windowTokens: usage.contextWindow };
|
|
582
736
|
}
|
|
737
|
+
function addModelUsage(models, record) {
|
|
738
|
+
const index = models.findIndex((m) => m.source === record.source && m.model === record.model);
|
|
739
|
+
if (index < 0) return [...models, { source: record.source, ...record.model ? { model: record.model } : {}, totals: addUsage({}, record) }];
|
|
740
|
+
return models.map((m, i) => i === index ? { ...m, totals: addUsage(m.totals, record) } : m);
|
|
741
|
+
}
|
|
742
|
+
function startAttemptUsage(attempt, runner, startedAt) {
|
|
743
|
+
return { attempt, runner, startedAt, wallTimeMs: 0, totals: {}, byModel: [] };
|
|
744
|
+
}
|
|
745
|
+
function addAttemptUsage(usage, record, now) {
|
|
746
|
+
const folded = addPlannerUsage(usage, record);
|
|
747
|
+
return { ...usage, ...folded, lastPromptTokens: folded.lastPromptTokens, contextWindow: folded.contextWindow, wallTimeMs: Math.max(0, Date.parse(now) - Date.parse(usage.startedAt)) };
|
|
748
|
+
}
|
|
749
|
+
function endAttemptUsage(usage, now) {
|
|
750
|
+
return { ...usage, endedAt: now, wallTimeMs: Math.max(0, Date.parse(now) - Date.parse(usage.startedAt)) };
|
|
751
|
+
}
|
|
752
|
+
function addUsageTotals(totals, addition) {
|
|
753
|
+
let next = addUsage(totals, { source: "", inputTokens: addition.inputTokens, outputTokens: addition.outputTokens, cachedInputTokens: addition.cachedInputTokens });
|
|
754
|
+
for (const [currency, amount] of Object.entries(addition.reportedCost ?? {})) next = addUsage(next, { source: "", reportedCost: { currency, amount } });
|
|
755
|
+
return next;
|
|
756
|
+
}
|
|
757
|
+
function planUsage(tasks) {
|
|
758
|
+
const result = { totals: {}, byModel: [], wallTimeMs: 0, attempts: 0 };
|
|
759
|
+
for (const task of tasks) {
|
|
760
|
+
const child = planUsage(task.subtasks ?? []);
|
|
761
|
+
result.totals = addUsageTotals(result.totals, child.totals);
|
|
762
|
+
result.wallTimeMs += child.wallTimeMs;
|
|
763
|
+
result.attempts += child.attempts;
|
|
764
|
+
const models = [...child.byModel];
|
|
765
|
+
for (const usage of task.attemptUsage ?? []) {
|
|
766
|
+
result.totals = addUsageTotals(result.totals, usage.totals);
|
|
767
|
+
result.wallTimeMs += usage.wallTimeMs;
|
|
768
|
+
result.attempts++;
|
|
769
|
+
models.push(...usage.byModel ?? []);
|
|
770
|
+
}
|
|
771
|
+
for (const model of models) {
|
|
772
|
+
const existing = result.byModel.find((m) => m.source === model.source && m.model === model.model);
|
|
773
|
+
if (existing) existing.totals = addUsageTotals(existing.totals, model.totals);
|
|
774
|
+
else result.byModel.push({ ...model, totals: addUsageTotals({}, model.totals) });
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
return result;
|
|
778
|
+
}
|
|
583
779
|
|
|
584
780
|
// src/conversation/lanes.ts
|
|
585
781
|
function findLastIndex(items, match) {
|
|
@@ -710,16 +906,55 @@ function finalSegmentIndex(blocks, turnId, pastPlan = false) {
|
|
|
710
906
|
for (let i = blocks.length - 1; i >= 0; i--) {
|
|
711
907
|
const block = blocks[i];
|
|
712
908
|
if (block.type === "usage" || block.type === "thinking" || block.type === "approval" || pastPlan && block.type === "plan") continue;
|
|
909
|
+
if (block.type === "command" || block.type === "compaction" || block.type === "runner_loaded") continue;
|
|
713
910
|
if (block.turnId !== turnId) continue;
|
|
714
911
|
return isUnsettledSegment(block, turnId) ? i : -1;
|
|
715
912
|
}
|
|
716
913
|
return -1;
|
|
717
914
|
}
|
|
718
|
-
function openTurn(view, { turnId, prompt = "", skills = [] }) {
|
|
915
|
+
function openTurn(view, { turnId, prompt = "", skills = [], wakeUps = [], forced }) {
|
|
916
|
+
const adopted = takePendingInputs(view, prompt, wakeUps, turnId);
|
|
917
|
+
view = adopted.view;
|
|
918
|
+
wakeUps = wakeUps.slice(adopted.wakeUps);
|
|
919
|
+
if (wakeUps.length === 0) return adopted.prompt || prompt === "" && adopted.wakeUps > 0 ? view : promptLine(view, turnId, prompt, skills, forced);
|
|
920
|
+
const said = prompt === "" || adopted.prompt ? view : promptLine(view, turnId, prompt, skills, forced);
|
|
921
|
+
const at = prompt === "" ? said.blocks.length : findLastIndex(said.blocks, (b) => b.type === "message" && b.role === "user" && b.turnId === turnId);
|
|
922
|
+
const updates = wakeUps.map((text2, n) => settledMessage(`b${said.nextId + n}`, "wake_up", text2, turnId));
|
|
923
|
+
return { ...said, blocks: [...said.blocks.slice(0, at), ...updates, ...said.blocks.slice(at)], nextId: said.nextId + updates.length };
|
|
924
|
+
}
|
|
925
|
+
function takePendingInputs(view, prompt, wakeUps, turnId) {
|
|
926
|
+
const pending = view.pendingInputs ?? [];
|
|
927
|
+
const users = pending.filter(({ input }) => input.source === "user");
|
|
928
|
+
const updates = pending.filter(({ input }) => input.source === "supervisor");
|
|
929
|
+
const taken = /* @__PURE__ */ new Set();
|
|
930
|
+
let matchedPrompt = false;
|
|
931
|
+
for (let n = 1; prompt !== void 0 && n <= users.length; n++) {
|
|
932
|
+
if (users.slice(0, n).map(({ input }) => input.text).join("\n\n") !== prompt) continue;
|
|
933
|
+
for (const { input } of users.slice(0, n)) taken.add(input.id);
|
|
934
|
+
matchedPrompt = true;
|
|
935
|
+
break;
|
|
936
|
+
}
|
|
937
|
+
let matchedUpdates = 0;
|
|
938
|
+
while (matchedUpdates < wakeUps.length && updates[matchedUpdates]?.input.text === wakeUps[matchedUpdates]) {
|
|
939
|
+
taken.add(updates[matchedUpdates++].input.id);
|
|
940
|
+
}
|
|
941
|
+
if (taken.size === 0) return { view, prompt: false, wakeUps: 0 };
|
|
942
|
+
const blockIds = new Set(pending.filter(({ input }) => taken.has(input.id)).flatMap(({ blockIds: blockIds2 }) => blockIds2));
|
|
943
|
+
return {
|
|
944
|
+
view: {
|
|
945
|
+
...view,
|
|
946
|
+
blocks: turnId === void 0 ? view.blocks : view.blocks.map((block) => blockIds.has(block.id) ? { ...block, turnId } : block),
|
|
947
|
+
pendingInputs: pending.filter(({ input }) => !taken.has(input.id))
|
|
948
|
+
},
|
|
949
|
+
prompt: matchedPrompt,
|
|
950
|
+
wakeUps: matchedUpdates
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
function promptLine(view, turnId, prompt, skills, forced) {
|
|
719
954
|
const i = findLastIndex(view.blocks, (b) => b.type === "message" && b.role === "user");
|
|
720
955
|
const sent = view.blocks[i];
|
|
721
956
|
const adopted = sent?.type === "message" && sent.turnId === void 0 && sent.text === prompt;
|
|
722
|
-
const said = adopted ? { ...view, blocks: replaceAt(view.blocks, i, userMessage(sent.id, prompt, skills, turnId)) } : append(view, (id) => userMessage(id, prompt, skills, turnId));
|
|
957
|
+
const said = adopted ? { ...view, blocks: replaceAt(view.blocks, i, userMessage(sent.id, prompt, skills, { turnId, forced })) } : append(view, (id) => userMessage(id, prompt, skills, { turnId, forced }));
|
|
723
958
|
if (skills.length === 0) return said;
|
|
724
959
|
const at = adopted ? i + 1 : findLastIndex(said.blocks, (b) => b.type === "message" && b.role === "user") + 1;
|
|
725
960
|
const loads = skills.map((skill, n) => skillLoadBlock(`b${said.nextId + n}`, skill, turnId));
|
|
@@ -737,14 +972,15 @@ function dropBuildingPlan(view, turnId, segmentId) {
|
|
|
737
972
|
const blocks = view.blocks.filter((b) => !(isBuildingPlan(b, turnId) && (segmentId === void 0 || b.segmentId === segmentId)));
|
|
738
973
|
return blocks.length === view.blocks.length ? view : { ...view, blocks };
|
|
739
974
|
}
|
|
740
|
-
function settleReply(view, { content, turnId }) {
|
|
975
|
+
function settleReply(view, { content, turnId, supervisor }) {
|
|
976
|
+
const role = supervisor ? "supervisor" : "planner";
|
|
741
977
|
if (turnId !== void 0) {
|
|
742
978
|
const i = finalSegmentIndex(view.blocks, turnId);
|
|
743
979
|
if (i >= 0) {
|
|
744
|
-
return dropBuildingPlan({ ...view, blocks: replaceAt(view.blocks, i, settledMessage(view.blocks[i].id,
|
|
980
|
+
return dropBuildingPlan({ ...view, blocks: replaceAt(view.blocks, i, settledMessage(view.blocks[i].id, role, content, turnId)) }, turnId);
|
|
745
981
|
}
|
|
746
982
|
}
|
|
747
|
-
return append(dropBuildingPlan(view, turnId), (id) => settledMessage(id,
|
|
983
|
+
return append(dropBuildingPlan(view, turnId), (id) => settledMessage(id, role, content, turnId));
|
|
748
984
|
}
|
|
749
985
|
function retractText(view, { turnId, segmentId }) {
|
|
750
986
|
const blocks = view.blocks.filter((b) => !(isUnsettledSegment(b, turnId) && (segmentId === void 0 || b.segmentId === segmentId)));
|
|
@@ -858,6 +1094,8 @@ function syncTranscript(view, { plan, turnId }) {
|
|
|
858
1094
|
let latest = view.transcriptAt;
|
|
859
1095
|
for (const [i, entry] of history.entries()) {
|
|
860
1096
|
const isNew = view.transcriptAt === void 0 || entry.timestamp > view.transcriptAt;
|
|
1097
|
+
if (isNew && entry.role === "user" && !entry.kind) next = takePendingInputs(next, entry.content, []).view;
|
|
1098
|
+
if (isNew && entry.kind === "wake_up") next = takePendingInputs(next, void 0, [entry.content]).view;
|
|
861
1099
|
if (isNew && entry.kind === "plan_generated") next = markPlan(next, entry.content, i === committed ? turnId : void 0);
|
|
862
1100
|
if (isNew && entry.kind === "system") next = append(next, (id) => settledMessage(id, "system", entry.content));
|
|
863
1101
|
if (isNew && entry.kind === "compaction") next = markCompaction(next, entry.content);
|
|
@@ -901,6 +1139,8 @@ function reduceConversation(view, input) {
|
|
|
901
1139
|
return settleApproval(view, input);
|
|
902
1140
|
case "approval_decided":
|
|
903
1141
|
return decideApproval(view, input);
|
|
1142
|
+
case "execution_action":
|
|
1143
|
+
return append(view, (id) => settledMessage(id, "system", input.text));
|
|
904
1144
|
case "planner_usage":
|
|
905
1145
|
return reportUsage(view, input);
|
|
906
1146
|
default:
|
|
@@ -918,9 +1158,22 @@ function fromEntry(entry, loaded = []) {
|
|
|
918
1158
|
const { skill } = entry;
|
|
919
1159
|
return (id) => skillLoadBlock(id(), skillLoadNotice(skill));
|
|
920
1160
|
}
|
|
921
|
-
if (entry.role === "user" && !entry.kind) return (id) => userMessage(id(), entry.content, loaded.map(skillLoadNotice));
|
|
922
|
-
|
|
923
|
-
|
|
1161
|
+
if (entry.role === "user" && !entry.kind) return (id) => userMessage(id(), entry.content, loaded.map(skillLoadNotice), { forced: entry.forced });
|
|
1162
|
+
return (id) => settledMessage(id(), roleOf(entry), entry.content);
|
|
1163
|
+
}
|
|
1164
|
+
function roleOf(entry) {
|
|
1165
|
+
switch (entry.kind) {
|
|
1166
|
+
case "system":
|
|
1167
|
+
case "compaction":
|
|
1168
|
+
case "execution":
|
|
1169
|
+
return "system";
|
|
1170
|
+
case "wake_up":
|
|
1171
|
+
return "wake_up";
|
|
1172
|
+
case "supervisor":
|
|
1173
|
+
return "supervisor";
|
|
1174
|
+
default:
|
|
1175
|
+
return entry.role === "user" ? "user" : "planner";
|
|
1176
|
+
}
|
|
924
1177
|
}
|
|
925
1178
|
function loadsAfter(entries, index) {
|
|
926
1179
|
const loads = [];
|
|
@@ -931,7 +1184,7 @@ function loadsAfter(entries, index) {
|
|
|
931
1184
|
return loads;
|
|
932
1185
|
}
|
|
933
1186
|
function rankOf(entry) {
|
|
934
|
-
return entry.role === "user" && (!entry.kind || entry.kind === "skill_load") ? USER : SETTLED;
|
|
1187
|
+
return entry.role === "user" && (!entry.kind || entry.kind === "skill_load" || entry.kind === "wake_up") ? USER : SETTLED;
|
|
935
1188
|
}
|
|
936
1189
|
function spawnBrief(step) {
|
|
937
1190
|
try {
|
|
@@ -989,7 +1242,7 @@ function research(log) {
|
|
|
989
1242
|
}
|
|
990
1243
|
return placed;
|
|
991
1244
|
}
|
|
992
|
-
function fromTranscript(conversationHistory, researchLog, plannerUsage) {
|
|
1245
|
+
function fromTranscript(conversationHistory, researchLog, plannerUsage, pendingTurns = []) {
|
|
993
1246
|
const history = conversationHistory ?? [];
|
|
994
1247
|
const compacted = history[0]?.kind === "compaction" ? history[0] : void 0;
|
|
995
1248
|
const kept = compacted ? history.slice(1) : history;
|
|
@@ -1003,9 +1256,27 @@ function fromTranscript(conversationHistory, researchLog, plannerUsage) {
|
|
|
1003
1256
|
let nextId = 1;
|
|
1004
1257
|
const id = () => `b${nextId++}`;
|
|
1005
1258
|
const blocks = placed.map((item) => item.build(id));
|
|
1259
|
+
const pendingInputs = [];
|
|
1260
|
+
for (const input of pendingTurns) {
|
|
1261
|
+
const start = blocks.length;
|
|
1262
|
+
if (input.source === "supervisor") {
|
|
1263
|
+
blocks.push(settledMessage(id(), "wake_up", input.text));
|
|
1264
|
+
} else {
|
|
1265
|
+
const skills = (input.skills ?? []).map(skillLoadNotice);
|
|
1266
|
+
blocks.push(userMessage(id(), input.text, skills, { forced: input.forced }));
|
|
1267
|
+
blocks.push(...skills.map((skill) => skillLoadBlock(id(), skill)));
|
|
1268
|
+
}
|
|
1269
|
+
pendingInputs.push({ input: { ...input, skills: input.skills?.map(skillLoadNotice) }, blockIds: blocks.slice(start).map((block) => block.id) });
|
|
1270
|
+
}
|
|
1006
1271
|
if (plannerUsage && isMeasured(plannerUsage.totals)) blocks.push(usageBlock(id(), usageLine(plannerUsage)));
|
|
1007
1272
|
const transcriptAt = history.reduce((latest, e) => latest === void 0 || e.timestamp > latest ? e.timestamp : latest, void 0);
|
|
1008
|
-
return { blocks, nextId, ...transcriptAt ? { transcriptAt } : {} };
|
|
1273
|
+
return { blocks, nextId, ...transcriptAt ? { transcriptAt } : {}, ...pendingInputs.length > 0 ? { pendingInputs } : {} };
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
// src/utils/sameMessage.ts
|
|
1277
|
+
function sameMessage(a, b) {
|
|
1278
|
+
const bare = (value) => value.trim().replace(/\.+$/, "").trimEnd();
|
|
1279
|
+
return bare(a) !== "" && bare(a) === bare(b);
|
|
1009
1280
|
}
|
|
1010
1281
|
|
|
1011
1282
|
// src/conversation/taskLog.ts
|
|
@@ -1046,10 +1317,17 @@ function streamWhole(view, lane, key, text2, make) {
|
|
|
1046
1317
|
return appendOutput(next, lane, make);
|
|
1047
1318
|
}
|
|
1048
1319
|
function agentText(view, text2) {
|
|
1049
|
-
|
|
1320
|
+
const next = streamWhole(view, null, TEXT_STREAM, text2, (id) => settledMessage(id, "agent", blockText(text2)));
|
|
1321
|
+
return { ...next, latestText: next.nextId > view.nextId ? `b${view.nextId}` : view.streams[TEXT_STREAM] };
|
|
1050
1322
|
}
|
|
1051
1323
|
function agentTextDelta(view, text2) {
|
|
1052
|
-
|
|
1324
|
+
const next = streamDelta(view, null, TEXT_STREAM, text2, (id, t) => ({ type: "message", id, role: "agent", text: t, streaming: true }));
|
|
1325
|
+
return { ...next, latestText: next.streams[TEXT_STREAM] };
|
|
1326
|
+
}
|
|
1327
|
+
function failTurn(view, message) {
|
|
1328
|
+
const repeated = view.blocks.find((b) => b.id === view.latestText);
|
|
1329
|
+
const settled = repeated?.type === "message" && repeated.role === "agent" && sameMessage(repeated.text, message) ? withoutStream({ ...view, blocks: view.blocks.filter((b) => b !== repeated) }, TEXT_STREAM) : view;
|
|
1330
|
+
return appendOutput({ ...settled, latestText: void 0 }, null, (id) => settledMessage(id, "error", message));
|
|
1053
1331
|
}
|
|
1054
1332
|
function thinkingBlock(id, text2, streaming, subagentId) {
|
|
1055
1333
|
return { type: "thinking", id, text: text2, streaming, ...subagentId ? { subagentId } : {} };
|
|
@@ -1114,19 +1392,22 @@ function runnerToolSubject(tool, args) {
|
|
|
1114
1392
|
const headline = toolHeadline(mapped, parsed ? JSON.stringify(normalizeAgentArgs(mapped, parsed)) : args, toolLabel);
|
|
1115
1393
|
return headline.keyArg ? `${headline.name}(${headline.keyArg})` : headline.name;
|
|
1116
1394
|
}
|
|
1117
|
-
function requestApproval2(view, { approvalId, tool, args, allowForTask, toolCallId }) {
|
|
1395
|
+
function requestApproval2(view, { approvalId, tool, args, allowForTask, toolCallId, decided }) {
|
|
1118
1396
|
if (view.blocks.some((b) => b.type === "approval" && b.approvalId === approvalId)) return view;
|
|
1119
|
-
return append(view, (id) =>
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1397
|
+
return append(view, (id) => {
|
|
1398
|
+
const block = {
|
|
1399
|
+
type: "approval",
|
|
1400
|
+
id,
|
|
1401
|
+
approvalId,
|
|
1402
|
+
kind: "runner_tool",
|
|
1403
|
+
subject: runnerToolSubject(tool, args),
|
|
1404
|
+
scope: tool,
|
|
1405
|
+
status: "pending",
|
|
1406
|
+
...allowForTask ? { allowForTask } : {},
|
|
1407
|
+
...toolCallId ? { toolCallId } : {}
|
|
1408
|
+
};
|
|
1409
|
+
return decided ? answered(block, decided) : block;
|
|
1410
|
+
});
|
|
1130
1411
|
}
|
|
1131
1412
|
function settleApproval2(view, approvalId, settle) {
|
|
1132
1413
|
const i = findLastIndex(view.blocks, (b) => b.type === "approval" && b.approvalId === approvalId);
|
|
@@ -1134,36 +1415,73 @@ function settleApproval2(view, approvalId, settle) {
|
|
|
1134
1415
|
if (block?.type !== "approval" || block.status !== "pending") return view;
|
|
1135
1416
|
return { ...view, blocks: replaceAt(view.blocks, i, settle(block)) };
|
|
1136
1417
|
}
|
|
1137
|
-
function
|
|
1138
|
-
return
|
|
1418
|
+
function answered(block, decision, note) {
|
|
1419
|
+
return {
|
|
1139
1420
|
...block,
|
|
1140
1421
|
status: decision === "deny" ? "denied" : "granted",
|
|
1141
1422
|
decidedBy: "asked",
|
|
1142
1423
|
...decision === "allowForTask" ? { forTask: true } : {},
|
|
1143
1424
|
...note ? { note } : {}
|
|
1144
|
-
}
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
function decideApproval2(view, { approvalId, decision, note }) {
|
|
1428
|
+
return settleApproval2(view, approvalId, (block) => answered(block, decision, note));
|
|
1429
|
+
}
|
|
1430
|
+
function startTurn(view, { message, messageId, from, forced }) {
|
|
1431
|
+
const queued = messageId ? view.queued.filter((m) => m.id !== messageId) : view.queued;
|
|
1432
|
+
const working = { ...view, queued, working: true, latestText: void 0 };
|
|
1433
|
+
return message ? append(working, (id) => ({ ...sentMessage(id, message, from), ...forced ? { forced: true } : {} })) : working;
|
|
1434
|
+
}
|
|
1435
|
+
function sentMessage(id, text2, from) {
|
|
1436
|
+
return from === "ordewell" ? settledMessage(id, "system", `Ordewell: ${text2}`) : settledMessage(id, "user", text2);
|
|
1145
1437
|
}
|
|
1146
|
-
function
|
|
1438
|
+
function startCommand(view, { name, args, messageId }) {
|
|
1147
1439
|
const queued = messageId ? view.queued.filter((m) => m.id !== messageId) : view.queued;
|
|
1148
|
-
|
|
1149
|
-
|
|
1440
|
+
return append({ ...view, queued, working: true, latestText: void 0 }, (id) => ({
|
|
1441
|
+
type: "command",
|
|
1442
|
+
id,
|
|
1443
|
+
name,
|
|
1444
|
+
...args ? { args } : {},
|
|
1445
|
+
status: "running",
|
|
1446
|
+
output: ""
|
|
1447
|
+
}));
|
|
1448
|
+
}
|
|
1449
|
+
function runningCommand(view) {
|
|
1450
|
+
return findLastIndex(view.blocks, (b) => b.type === "command" && b.status === "running");
|
|
1451
|
+
}
|
|
1452
|
+
function commandOutput(view, { text: text2 }) {
|
|
1453
|
+
const i = runningCommand(view);
|
|
1454
|
+
const block = view.blocks[i];
|
|
1455
|
+
if (block?.type !== "command") return append(view, (id) => settledMessage(id, "system", text2));
|
|
1456
|
+
return { ...view, blocks: replaceAt(view.blocks, i, { ...block, output: block.output ? `${block.output}
|
|
1457
|
+
${text2}` : text2 }) };
|
|
1458
|
+
}
|
|
1459
|
+
function appendStep(view, make) {
|
|
1460
|
+
const next = append(view, make);
|
|
1461
|
+
return sealLane(next, null, findLastIndex(next.blocks, (b) => b.type !== "usage"));
|
|
1150
1462
|
}
|
|
1151
1463
|
var CUT = { completed: null, interrupted: "stopped", failed: "failed" };
|
|
1464
|
+
var COMMAND_END = { completed: "done", interrupted: "stopped", failed: "failed" };
|
|
1152
1465
|
function endTurn2(view, { reason }) {
|
|
1153
|
-
const
|
|
1466
|
+
const i = runningCommand(view);
|
|
1467
|
+
const command = view.blocks[i];
|
|
1468
|
+
const settled = command?.type === "command" ? { ...view, blocks: replaceAt(view.blocks, i, { ...command, status: COMMAND_END[reason] }) } : view;
|
|
1469
|
+
const closed = closeOpenBlocks({ ...settled, working: false, lastTurnEnd: reason, streams: {}, latestText: void 0 }, CUT[reason]);
|
|
1154
1470
|
return reason === "interrupted" ? append(closed, (id) => settledMessage(id, "system", "Interrupted.")) : closed;
|
|
1155
1471
|
}
|
|
1156
|
-
function queueMessage(view, { messageId, text: text2, forced }) {
|
|
1472
|
+
function queueMessage(view, { messageId, text: text2, forced, from }) {
|
|
1157
1473
|
const listed = view.queued.find((m) => m.id === messageId);
|
|
1474
|
+
const sender = from ? { from } : {};
|
|
1158
1475
|
if (forced) {
|
|
1159
1476
|
const rest = view.queued.filter((m) => m !== listed);
|
|
1160
1477
|
const at = rest.findIndex((m) => !m.forced);
|
|
1161
1478
|
const placed = at < 0 ? rest.length : at;
|
|
1162
|
-
|
|
1479
|
+
const handedOver = listed?.handedOver ? { handedOver: true } : {};
|
|
1480
|
+
return { ...view, queued: [...rest.slice(0, placed), { id: messageId, text: text2, forced: true, ...handedOver, ...sender }, ...rest.slice(placed)] };
|
|
1163
1481
|
}
|
|
1164
|
-
if (!listed) return { ...view, queued: [...view.queued, { id: messageId, text: text2 }] };
|
|
1482
|
+
if (!listed) return { ...view, queued: [...view.queued, { id: messageId, text: text2, ...sender }] };
|
|
1165
1483
|
if (!listed.handedOver) return view;
|
|
1166
|
-
return { ...view, queued: view.queued.map((m) => m === listed ? { id: m.id, text: m.text } : m) };
|
|
1484
|
+
return { ...view, queued: view.queued.map((m) => m === listed ? { id: m.id, text: m.text, ...m.forced ? { forced: true } : {}, ...sender } : m) };
|
|
1167
1485
|
}
|
|
1168
1486
|
function handOver(view, { messageId }) {
|
|
1169
1487
|
const listed = view.queued.find((m) => m.id === messageId);
|
|
@@ -1171,18 +1489,44 @@ function handOver(view, { messageId }) {
|
|
|
1171
1489
|
return { ...view, queued: view.queued.map((m) => m === listed ? { ...m, handedOver: true } : m) };
|
|
1172
1490
|
}
|
|
1173
1491
|
function deliverMidTurn(view, event) {
|
|
1174
|
-
return appendOutput(unqueueMessage(view, event), null, (id) =>
|
|
1492
|
+
return appendOutput(unqueueMessage(view, event), null, (id) => sentMessage(id, event.text, event.from));
|
|
1175
1493
|
}
|
|
1176
1494
|
function unqueueMessage(view, { messageId }) {
|
|
1177
1495
|
const queued = view.queued.filter((m) => m.id !== messageId);
|
|
1178
1496
|
return queued.length === view.queued.length ? view : { ...view, queued };
|
|
1179
1497
|
}
|
|
1498
|
+
function endCall(block) {
|
|
1499
|
+
return block.type === "tool" && block.status === "pending" ? finishedTool(block, "", "not_executed") : block;
|
|
1500
|
+
}
|
|
1501
|
+
function endOpenCalls(view) {
|
|
1502
|
+
let changed = false;
|
|
1503
|
+
const blocks = view.blocks.map((block) => {
|
|
1504
|
+
const ended = block.type === "subagent" ? block.children.some((c) => c.type === "tool" && c.status === "pending") ? { ...block, children: block.children.map(endCall) } : block : block.type === "tool" ? endCall(block) : block;
|
|
1505
|
+
if (ended !== block) changed = true;
|
|
1506
|
+
return ended;
|
|
1507
|
+
});
|
|
1508
|
+
return changed ? { ...view, blocks } : view;
|
|
1509
|
+
}
|
|
1180
1510
|
function reduceTaskLog(view, event) {
|
|
1181
1511
|
switch (event.type) {
|
|
1182
1512
|
case "task_skills":
|
|
1183
1513
|
return append(view, (id) => settledMessage(id, "system", `Skills: ${event.skills.map((s) => `${s.name} (${s.path})`).join(", ")}`));
|
|
1184
1514
|
case "turn_start":
|
|
1185
1515
|
return startTurn(view, event);
|
|
1516
|
+
case "command":
|
|
1517
|
+
return startCommand(view, event);
|
|
1518
|
+
case "command_output":
|
|
1519
|
+
return commandOutput(view, event);
|
|
1520
|
+
case "compaction":
|
|
1521
|
+
return appendStep(view, (id) => ({
|
|
1522
|
+
type: "compaction",
|
|
1523
|
+
id,
|
|
1524
|
+
trigger: event.trigger,
|
|
1525
|
+
...event.preTokens !== void 0 ? { preTokens: event.preTokens } : {},
|
|
1526
|
+
...event.postTokens !== void 0 ? { postTokens: event.postTokens } : {}
|
|
1527
|
+
}));
|
|
1528
|
+
case "runner_loaded":
|
|
1529
|
+
return append(view, (id) => ({ type: "runner_loaded", id, commands: event.commands, mcpServers: event.mcpServers }));
|
|
1186
1530
|
case "turn_end":
|
|
1187
1531
|
return endTurn2(view, event);
|
|
1188
1532
|
case "text_delta":
|
|
@@ -1214,13 +1558,15 @@ function reduceTaskLog(view, event) {
|
|
|
1214
1558
|
case "message_undelivered":
|
|
1215
1559
|
return append(unqueueMessage(view, event), (id) => settledMessage(id, "system", `${event.text} \xB7 not delivered`));
|
|
1216
1560
|
case "error":
|
|
1217
|
-
return
|
|
1561
|
+
return failTurn(view, event.message);
|
|
1218
1562
|
case "approval_requested":
|
|
1219
1563
|
return requestApproval2(view, event);
|
|
1220
1564
|
case "approval_decided":
|
|
1221
1565
|
return decideApproval2(view, event);
|
|
1222
1566
|
case "approval_withdrawn":
|
|
1223
1567
|
return settleApproval2(view, event.approvalId, (block) => ({ ...block, status: "withdrawn" }));
|
|
1568
|
+
case "runner_ended":
|
|
1569
|
+
return endOpenCalls(view);
|
|
1224
1570
|
default:
|
|
1225
1571
|
return view;
|
|
1226
1572
|
}
|
|
@@ -1242,6 +1588,10 @@ function unsendLatest(hold) {
|
|
|
1242
1588
|
if (hold.length === 0) return void 0;
|
|
1243
1589
|
return { text: hold[hold.length - 1], rest: hold.slice(0, -1) };
|
|
1244
1590
|
}
|
|
1591
|
+
function takeAt(hold, index) {
|
|
1592
|
+
if (index < 0 || index >= hold.length) return void 0;
|
|
1593
|
+
return { text: hold[index], rest: [...hold.slice(0, index), ...hold.slice(index + 1)] };
|
|
1594
|
+
}
|
|
1245
1595
|
function unsendAll(hold) {
|
|
1246
1596
|
if (hold.length === 0) return void 0;
|
|
1247
1597
|
return { text: hold.join("\n"), rest: EMPTY_HOLD };
|
|
@@ -1295,7 +1645,7 @@ function stopTurn(view, gate) {
|
|
|
1295
1645
|
|
|
1296
1646
|
// src/conversation/detail.ts
|
|
1297
1647
|
function hasHiddenDetail(blocks) {
|
|
1298
|
-
return blocks.some((b) => b.type === "thinking" || b.type === "tool" || b.type === "subagent");
|
|
1648
|
+
return blocks.some((b) => b.type === "thinking" || b.type === "tool" || b.type === "subagent" || b.type === "runner_loaded");
|
|
1299
1649
|
}
|
|
1300
1650
|
|
|
1301
1651
|
// src/conversation/notices.ts
|
|
@@ -1306,20 +1656,29 @@ function taskStartedNotice(title, runner) {
|
|
|
1306
1656
|
// src/taskRow/actions.ts
|
|
1307
1657
|
var UNSTARTED_AI = (t) => t.type === "ai" && NOT_STARTED.has(t.status);
|
|
1308
1658
|
var QUEUED = (t) => t.status === "pending" || t.status === "approved";
|
|
1659
|
+
var PAUSED = (t) => t.status === "awaiting_user" && t.awaitingReason === "usage-limit";
|
|
1660
|
+
var RESUMES = (t) => PAUSED(t) && !!t.resumeAt;
|
|
1661
|
+
var RESUMABLE = (t) => PAUSED(t) && t.continuable === true;
|
|
1662
|
+
var HOLDS_UP = (t) => t.status === "failed" || t.status === "blocked" || t.status === "awaiting_user";
|
|
1309
1663
|
var TASK_RULES = [
|
|
1310
1664
|
["cancel", (t) => t.status === "in_progress"],
|
|
1311
|
-
["
|
|
1665
|
+
["resume-now", RESUMABLE],
|
|
1666
|
+
["cancel-resume", RESUMES],
|
|
1667
|
+
["skip", HOLDS_UP],
|
|
1312
1668
|
["force-start", UNSTARTED_AI],
|
|
1313
|
-
// A manual task has no runner to report it done, so it can always be marked
|
|
1314
|
-
|
|
1669
|
+
// A manual task has no runner to report it done, so it can always be marked;
|
|
1670
|
+
// a skipped one's kept work can still be vouched for.
|
|
1671
|
+
["complete", (t) => t.status === "awaiting_user" || t.status === "skipped" || t.type === "user"],
|
|
1315
1672
|
["uncomplete", (t) => t.status === "completed"]
|
|
1316
1673
|
];
|
|
1317
1674
|
var SUBTASK_RULES = [
|
|
1318
1675
|
["cancel", (t) => t.status === "in_progress"],
|
|
1676
|
+
["resume-now", RESUMABLE],
|
|
1677
|
+
["cancel-resume", RESUMES],
|
|
1319
1678
|
["complete", (t) => t.status === "in_progress" && t.type === "ai"],
|
|
1320
1679
|
["force-start", (t) => t.type === "ai" && QUEUED(t)],
|
|
1321
|
-
["skip", QUEUED],
|
|
1322
|
-
["complete", (t) => t.status === "awaiting_user"],
|
|
1680
|
+
["skip", (t) => QUEUED(t) || t.status === "failed"],
|
|
1681
|
+
["complete", (t) => t.status === "awaiting_user" || t.status === "skipped"],
|
|
1323
1682
|
["uncomplete", (t) => t.status === "completed"]
|
|
1324
1683
|
];
|
|
1325
1684
|
function taskRowActions(task, placement = "task") {
|
|
@@ -1330,7 +1689,7 @@ function markAction(task) {
|
|
|
1330
1689
|
return task.status === "completed" ? "uncomplete" : "complete";
|
|
1331
1690
|
}
|
|
1332
1691
|
function markRequestFor(action) {
|
|
1333
|
-
if (action === "complete"
|
|
1692
|
+
if (action === "complete") return "complete";
|
|
1334
1693
|
if (action === "uncomplete") return "uncomplete";
|
|
1335
1694
|
return null;
|
|
1336
1695
|
}
|
|
@@ -1339,52 +1698,50 @@ function opsEditable(task) {
|
|
|
1339
1698
|
}
|
|
1340
1699
|
|
|
1341
1700
|
// src/taskRow/view.ts
|
|
1342
|
-
var refsOf = (ids,
|
|
1343
|
-
const order = orderOf?.get(id);
|
|
1344
|
-
return order === void 0 ? id : taskRef({ order });
|
|
1345
|
-
});
|
|
1701
|
+
var refsOf = (ids, tasks) => ids.map((id) => tasks?.length ? taskRefOf(tasks, id) ?? id : id);
|
|
1346
1702
|
var namedRepo = (repo) => repo && repo !== "." ? repo : null;
|
|
1347
1703
|
var namedRepos = (repos) => (repos ?? []).filter((r) => r !== ".");
|
|
1348
1704
|
var filesOf = (files) => files?.length ? capConflictFiles(files) : null;
|
|
1705
|
+
var asBlocking = (tasks) => tasks.map((t) => ({ id: t.id, status: t.status ?? "", dependencies: t.dependencies ?? [], subtasks: asBlocking(t.subtasks ?? []) }));
|
|
1706
|
+
function blockedBy(task, tasks) {
|
|
1707
|
+
if (!tasks?.length || !NOT_STARTED.has(task.status)) return [];
|
|
1708
|
+
return blockingFailures(asBlocking(tasks)).get(task.id) ?? [];
|
|
1709
|
+
}
|
|
1349
1710
|
function taskRowView(task, context = {}) {
|
|
1350
1711
|
const parent = context.parent ?? void 0;
|
|
1351
1712
|
const isolation = task.isolation && task.isolation.state !== "none" ? task.isolation : null;
|
|
1713
|
+
const latestUsage = task.attemptUsage?.at(-1);
|
|
1714
|
+
const blocked = blockedBy(task, context.tasks);
|
|
1352
1715
|
return {
|
|
1716
|
+
usage: latestUsage ? { ...usageLine(latestUsage), attempt: latestUsage.attempt, wallTimeMs: latestUsage.wallTimeMs, byModel: latestUsage.byModel } : task.type === "ai" && ["completed", "failed", "in_progress", "awaiting_user"].includes(task.status) ? { totals: {}, attempt: 0, byModel: [] } : null,
|
|
1353
1717
|
orderLabel: taskOrderLabel(task, parent),
|
|
1354
1718
|
kind: task.type === "user" ? "user" : task.ops ? "ops" : "ai",
|
|
1355
|
-
status: taskStatusKind(task),
|
|
1719
|
+
status: blocked.length > 0 ? "blocked" : taskStatusKind(task),
|
|
1356
1720
|
running: isTaskRunning(task),
|
|
1357
1721
|
awaiting: awaitingLabel(task),
|
|
1722
|
+
resumes: resumeLabel(task, context.now),
|
|
1358
1723
|
approvals: approvalLabel(task.awaitingApproval),
|
|
1359
1724
|
autonomous: task.type === "ai" && context.modes?.find((m) => m.id === task.taskMode)?.autonomous === true,
|
|
1360
1725
|
repos: namedRepos(isolation?.repos),
|
|
1361
1726
|
conflict: isolation?.state === "conflict" ? { repo: namedRepo(isolation.conflictRepo), files: filesOf(isolation.conflictFiles) } : null,
|
|
1362
1727
|
repairing: isolation?.state === "repairing" ? { files: filesOf(isolation.conflictFiles), attempt: isolation.repair ?? null } : null,
|
|
1363
1728
|
repaired: isolation?.repairedFiles?.length ? { files: capConflictFiles(isolation.repairedFiles), landed: isolation.state === "integrated" } : null,
|
|
1364
|
-
mergeGate: task.mergeGate?.length ? refsOf(task.mergeGate, context.
|
|
1729
|
+
mergeGate: task.mergeGate?.length ? refsOf(task.mergeGate, context.tasks) : null,
|
|
1730
|
+
blockedBy: blocked.length > 0 ? refsOf(blocked, context.tasks) : null,
|
|
1731
|
+
skipReason: task.status === "skipped" ? task.skipReason ?? null : null,
|
|
1365
1732
|
opsChangedFiles: task.status === "awaiting_user" && task.awaitingReason === "files-changed",
|
|
1366
1733
|
forcedPastGate: task.forcedPastGate?.length ? task.forcedPastGate : null,
|
|
1367
|
-
dependencies: refsOf(task.dependencies, context.
|
|
1368
|
-
|
|
1734
|
+
dependencies: refsOf(task.dependencies, context.tasks),
|
|
1735
|
+
// A block read off the plan offers what a stored one does: Skip among them.
|
|
1736
|
+
actions: taskRowActions(blocked.length > 0 ? { ...task, status: "blocked" } : task, context.placement ?? (parent ? "subtask" : "task")),
|
|
1369
1737
|
mark: markAction(task),
|
|
1370
1738
|
opsEditable: opsEditable(task)
|
|
1371
1739
|
};
|
|
1372
1740
|
}
|
|
1373
1741
|
|
|
1374
1742
|
// src/taskRow/texts.ts
|
|
1375
|
-
function findNamed(tasks, id) {
|
|
1376
|
-
for (const task of tasks) {
|
|
1377
|
-
if (task.id === id) return task;
|
|
1378
|
-
const nested = findNamed(task.subtasks ?? [], id);
|
|
1379
|
-
if (nested) return nested;
|
|
1380
|
-
}
|
|
1381
|
-
return void 0;
|
|
1382
|
-
}
|
|
1383
1743
|
function titledRefs(ids, tasks) {
|
|
1384
|
-
return ids.map((id) =>
|
|
1385
|
-
const task = findNamed(tasks, id);
|
|
1386
|
-
return task ? titledTaskRef(task) : id;
|
|
1387
|
-
});
|
|
1744
|
+
return ids.map((id) => titledTaskRefOf(tasks, id) ?? id);
|
|
1388
1745
|
}
|
|
1389
1746
|
function dependentsNotice(dependents) {
|
|
1390
1747
|
if (dependents.length === 0) return null;
|
|
@@ -1399,18 +1756,30 @@ function newTaskFields(title, prompt) {
|
|
|
1399
1756
|
if (!name) return null;
|
|
1400
1757
|
return { title: name, description: name, prompt: prompt?.trim() ? prompt : name, type: "ai" };
|
|
1401
1758
|
}
|
|
1759
|
+
|
|
1760
|
+
// src/services/runView.ts
|
|
1761
|
+
var EMPTY_RUN = { tasks: [], runMerge: null, handoff: null, mergeResult: null };
|
|
1402
1762
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1403
1763
|
0 && (module.exports = {
|
|
1404
1764
|
CHECKPOINT_TRUNCATE_LENGTH,
|
|
1405
1765
|
DEFAULT_RUNNERS,
|
|
1406
1766
|
EMPTY_CONVERSATION,
|
|
1407
1767
|
EMPTY_HOLD,
|
|
1768
|
+
EMPTY_RUN,
|
|
1408
1769
|
EMPTY_TASK_LOG,
|
|
1409
1770
|
NOT_STARTED,
|
|
1410
1771
|
NO_TURN,
|
|
1772
|
+
SUPERVISOR_LEVEL_LABEL,
|
|
1773
|
+
addAttemptUsage,
|
|
1774
|
+
addModelUsage,
|
|
1775
|
+
addPlannerUsage,
|
|
1776
|
+
addUsage,
|
|
1777
|
+
addUsageTotals,
|
|
1411
1778
|
aheadOfDraft,
|
|
1412
1779
|
approvalLabel,
|
|
1413
1780
|
awaitingLabel,
|
|
1781
|
+
blockedByLabel,
|
|
1782
|
+
blockingFailures,
|
|
1414
1783
|
canMergeTasks,
|
|
1415
1784
|
canSetDependencies,
|
|
1416
1785
|
canSplitTask,
|
|
@@ -1422,10 +1791,12 @@ function newTaskFields(title, prompt) {
|
|
|
1422
1791
|
diffStat,
|
|
1423
1792
|
diffSummary,
|
|
1424
1793
|
drainNext,
|
|
1794
|
+
endAttemptUsage,
|
|
1425
1795
|
followTurn,
|
|
1426
1796
|
fromTranscript,
|
|
1427
1797
|
hasHiddenDetail,
|
|
1428
1798
|
holdPrompt,
|
|
1799
|
+
isMeasured,
|
|
1429
1800
|
isTaskRunning,
|
|
1430
1801
|
loadedSkillTokens,
|
|
1431
1802
|
markAction,
|
|
@@ -1435,25 +1806,39 @@ function newTaskFields(title, prompt) {
|
|
|
1435
1806
|
opsEditable,
|
|
1436
1807
|
outputLines,
|
|
1437
1808
|
outputPreview,
|
|
1809
|
+
parseCount,
|
|
1810
|
+
partedPromptUsage,
|
|
1438
1811
|
pastGateConfirmation,
|
|
1812
|
+
pausedOnLimit,
|
|
1813
|
+
planUsage,
|
|
1814
|
+
plannerContextFill,
|
|
1439
1815
|
reduceConversation,
|
|
1440
1816
|
reduceTaskLog,
|
|
1441
1817
|
replayTaskLog,
|
|
1818
|
+
resolveTaskRef,
|
|
1819
|
+
resumeLabel,
|
|
1820
|
+
resumeTime,
|
|
1442
1821
|
runnerToolSubject,
|
|
1443
1822
|
skillTokens,
|
|
1823
|
+
skippedLabel,
|
|
1824
|
+
startAttemptUsage,
|
|
1444
1825
|
stopTurn,
|
|
1445
1826
|
summarizeToolCall,
|
|
1827
|
+
takeAt,
|
|
1446
1828
|
taskOrderLabel,
|
|
1447
1829
|
taskRef,
|
|
1830
|
+
taskRefOf,
|
|
1448
1831
|
taskRowActions,
|
|
1449
1832
|
taskRowView,
|
|
1450
1833
|
taskStartedNotice,
|
|
1451
1834
|
taskStatusKind,
|
|
1452
1835
|
titledRefs,
|
|
1453
1836
|
titledTaskRef,
|
|
1837
|
+
titledTaskRefOf,
|
|
1454
1838
|
toolHeadline,
|
|
1455
1839
|
truncateCheckpointSummary,
|
|
1456
1840
|
unsendAll,
|
|
1457
|
-
unsendLatest
|
|
1841
|
+
unsendLatest,
|
|
1842
|
+
usageLine
|
|
1458
1843
|
});
|
|
1459
1844
|
//# sourceMappingURL=plan-utils.js.map
|