@ordewell/core 0.10.12 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AUTHORS.md +37 -0
- package/NOTICE +71 -0
- package/README.md +1 -1
- package/dist/{IFileSystem-1PqRCjhu.d.mts → IRunner-B5l9PCTv.d.ts} +926 -102
- package/dist/{IFileSystem-lTRL_G9Z.d.ts → IRunner-DBgDx_Mr.d.mts} +926 -102
- package/dist/{ModeResolver-CoJAJD-f.d.ts → ModeResolver-BDAgouuE.d.mts} +9 -2
- package/dist/{ModeResolver-6TQXVsde.d.mts → ModeResolver-DyXeZ9ib.d.ts} +9 -2
- package/dist/{Task-CfBM0wkV.d.mts → Task-BL4Idm9r.d.mts} +656 -26
- package/dist/{Task-CfBM0wkV.d.ts → Task-BL4Idm9r.d.ts} +656 -26
- package/dist/{chunk-ESKK5I4F.mjs → chunk-4QXOWEGC.mjs} +1888 -334
- package/dist/chunk-4QXOWEGC.mjs.map +1 -0
- package/dist/{chunk-KWS3VEER.mjs → chunk-MYXMIVWA.mjs} +6 -6
- package/dist/chunk-MYXMIVWA.mjs.map +1 -0
- package/dist/{chunk-PFH7WFIB.mjs → chunk-QTZ5XTBY.mjs} +228 -15
- package/dist/chunk-QTZ5XTBY.mjs.map +1 -0
- package/dist/{chunk-24EL4Z2W.mjs → chunk-YAFNYNFX.mjs} +342 -36
- package/dist/chunk-YAFNYNFX.mjs.map +1 -0
- package/dist/index.d.mts +1488 -769
- package/dist/index.d.ts +1488 -769
- package/dist/index.js +32003 -20350
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +29380 -19553
- package/dist/index.mjs.map +1 -1
- package/dist/parsing.d.mts +2 -2
- package/dist/parsing.d.ts +2 -2
- package/dist/parsing.js +232 -25
- package/dist/parsing.js.map +1 -1
- package/dist/parsing.mjs +3 -3
- package/dist/plan-utils.d.mts +4 -4
- package/dist/plan-utils.d.ts +4 -4
- package/dist/plan-utils.js +1156 -96
- package/dist/plan-utils.js.map +1 -1
- package/dist/plan-utils.mjs +144 -8
- package/dist/{runSettingsText-Jb_efnI4.d.mts → runSettingsText-BKwWs052.d.mts} +2882 -621
- package/dist/{runSettingsText-C5uv2axI.d.ts → runSettingsText-CAXiTpsj.d.ts} +2882 -621
- package/dist/testing.d.mts +10 -4
- package/dist/testing.d.ts +10 -4
- package/dist/testing.js +30 -7
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +26 -7
- package/dist/testing.mjs.map +1 -1
- package/package.json +7 -3
- package/skills/diagnose/SKILL.md +52 -0
- package/skills/explore/SKILL.md +65 -0
- package/dist/chunk-24EL4Z2W.mjs.map +0 -1
- package/dist/chunk-ESKK5I4F.mjs.map +0 -1
- package/dist/chunk-KWS3VEER.mjs.map +0 -1
- package/dist/chunk-NJGID6BG.mjs +0 -115
- package/dist/chunk-NJGID6BG.mjs.map +0 -1
- package/dist/chunk-PFH7WFIB.mjs.map +0 -1
package/dist/plan-utils.js
CHANGED
|
@@ -37,27 +37,47 @@ __export(plan_utils_exports, {
|
|
|
37
37
|
EMPTY_RUN: () => EMPTY_RUN,
|
|
38
38
|
EMPTY_TASK_LOG: () => EMPTY_TASK_LOG,
|
|
39
39
|
EMPTY_TASK_LOG_FOLLOW: () => EMPTY_TASK_LOG_FOLLOW,
|
|
40
|
+
IDLE_ACTIVITY: () => IDLE_ACTIVITY,
|
|
40
41
|
NOT_STARTED: () => NOT_STARTED,
|
|
41
42
|
NO_TURN: () => NO_TURN,
|
|
43
|
+
PLANNED_WITH_LABEL: () => PLANNED_WITH_LABEL,
|
|
44
|
+
REPORT_PREVIEW_LINES: () => REPORT_PREVIEW_LINES,
|
|
45
|
+
SEVERITY_LABEL: () => SEVERITY_LABEL,
|
|
42
46
|
SUPERVISOR_LEVEL_LABEL: () => SUPERVISOR_LEVEL_LABEL,
|
|
43
47
|
WAITS_FOR_APPROVAL_LABEL: () => WAITS_FOR_APPROVAL_LABEL,
|
|
48
|
+
accountLimitPercentText: () => accountLimitPercentText,
|
|
49
|
+
accountLimitWindowLabel: () => accountLimitWindowLabel,
|
|
50
|
+
accountLimitsOf: () => accountLimitsOf,
|
|
51
|
+
accountLimitsText: () => accountLimitsText,
|
|
52
|
+
accountLimitsTitle: () => accountLimitsTitle,
|
|
44
53
|
addAttemptUsage: () => addAttemptUsage,
|
|
45
54
|
addModelUsage: () => addModelUsage,
|
|
46
55
|
addPlannerUsage: () => addPlannerUsage,
|
|
47
56
|
addUsage: () => addUsage,
|
|
48
57
|
addUsageTotals: () => addUsageTotals,
|
|
49
58
|
aheadOfDraft: () => aheadOfDraft,
|
|
59
|
+
approvalKindText: () => approvalKindText,
|
|
50
60
|
approvalLabel: () => approvalLabel,
|
|
61
|
+
approvalStatusText: () => approvalStatusText,
|
|
62
|
+
assignedRunnerOf: () => assignedRunnerOf,
|
|
51
63
|
assignmentIssue: () => assignmentIssue,
|
|
52
64
|
assignmentIssueText: () => assignmentIssueText,
|
|
53
65
|
awaitingLabel: () => awaitingLabel,
|
|
66
|
+
betweenRounds: () => betweenRounds,
|
|
67
|
+
blockNotice: () => blockNotice,
|
|
68
|
+
blockNotices: () => blockNotices,
|
|
54
69
|
blockedByLabel: () => blockedByLabel,
|
|
55
70
|
blockingFailures: () => blockingFailures,
|
|
56
71
|
canMergeTasks: () => canMergeTasks,
|
|
57
72
|
canSetDependencies: () => canSetDependencies,
|
|
58
73
|
canSplitTask: () => canSplitTask,
|
|
59
74
|
capConflictFiles: () => capConflictFiles,
|
|
75
|
+
cardEditNeedsUnstarted: () => cardEditNeedsUnstarted,
|
|
76
|
+
cardEditable: () => cardEditable,
|
|
77
|
+
checkOf: () => checkOf,
|
|
60
78
|
clampThinkingEffort: () => clampThinkingEffort,
|
|
79
|
+
compactionText: () => compactionText,
|
|
80
|
+
confirmedOverride: () => confirmedOverride,
|
|
61
81
|
contextMeterText: () => contextMeterText,
|
|
62
82
|
contextMeterTitle: () => contextMeterTitle,
|
|
63
83
|
costText: () => costText,
|
|
@@ -65,47 +85,89 @@ __export(plan_utils_exports, {
|
|
|
65
85
|
dependencyCandidates: () => dependencyCandidates,
|
|
66
86
|
dependentsNotice: () => dependentsNotice,
|
|
67
87
|
dependentsOf: () => dependentsOf,
|
|
88
|
+
describeIsolationPreview: () => describeIsolationPreview,
|
|
89
|
+
describeMergeResult: () => describeMergeResult,
|
|
68
90
|
diffRows: () => diffRows,
|
|
69
91
|
diffStat: () => diffStat,
|
|
70
92
|
diffSummary: () => diffSummary,
|
|
71
93
|
drainNext: () => drainNext,
|
|
94
|
+
editableWhileRunning: () => editableWhileRunning,
|
|
72
95
|
endAttemptUsage: () => endAttemptUsage,
|
|
73
96
|
followTaskLog: () => followTaskLog,
|
|
74
97
|
followTurn: () => followTurn,
|
|
75
98
|
formatCost: () => formatCost,
|
|
76
99
|
formatTokenCount: () => formatTokenCount,
|
|
77
100
|
fromTranscript: () => fromTranscript,
|
|
101
|
+
handoffBaseLine: () => handoffBaseLine,
|
|
102
|
+
handoffBranchName: () => handoffBranchName,
|
|
103
|
+
handoffFindingsText: () => handoffFindingsText,
|
|
104
|
+
handoffPeekParts: () => handoffPeekParts,
|
|
105
|
+
handoffReferenceText: () => handoffReferenceText,
|
|
106
|
+
hasAccountLimits: () => hasAccountLimits,
|
|
78
107
|
hasHiddenDetail: () => hasHiddenDetail,
|
|
108
|
+
hasLongReport: () => hasLongReport,
|
|
109
|
+
holdLabel: () => holdLabel,
|
|
79
110
|
holdPrompt: () => holdPrompt,
|
|
111
|
+
isFileEditTool: () => isFileEditTool,
|
|
80
112
|
isMeasured: () => isMeasured,
|
|
113
|
+
isRepoGroup: () => isRepoGroup,
|
|
81
114
|
isTaskRunning: () => isTaskRunning,
|
|
82
115
|
loadedSkillTokens: () => loadedSkillTokens,
|
|
116
|
+
logContinues: () => logContinues,
|
|
117
|
+
logResumes: () => logResumes,
|
|
118
|
+
loopBodyLabel: () => loopBodyLabel,
|
|
119
|
+
loopBodyRound: () => loopBodyRound,
|
|
120
|
+
loopCheckLabel: () => loopCheckLabel,
|
|
121
|
+
loopCheckState: () => loopCheckState,
|
|
122
|
+
loopLeaveNotice: () => loopLeaveNotice,
|
|
123
|
+
loopRemovalRefusal: () => loopRemovalRefusal,
|
|
124
|
+
loopRoundOf: () => loopRoundOf,
|
|
125
|
+
loopRowParts: () => loopRowParts,
|
|
126
|
+
manualOverrideConfirmation: () => manualOverrideConfirmation,
|
|
127
|
+
manualOverrideStopFirst: () => manualOverrideStopFirst,
|
|
83
128
|
markAction: () => markAction,
|
|
84
|
-
markRequestFor: () => markRequestFor,
|
|
85
129
|
markdownHtml: () => markdownHtml,
|
|
86
130
|
markdownTokens: () => markdownTokens,
|
|
131
|
+
mergeOutcome: () => mergeOutcome,
|
|
87
132
|
modelCostTitle: () => modelCostTitle,
|
|
88
133
|
namedRepos: () => namedRepos,
|
|
89
134
|
newTaskFields: () => newTaskFields,
|
|
135
|
+
noExecutorRefusal: () => noExecutorRefusal,
|
|
90
136
|
opsEditable: () => opsEditable,
|
|
137
|
+
outOfRounds: () => outOfRounds,
|
|
91
138
|
outputLines: () => outputLines,
|
|
92
139
|
outputPreview: () => outputPreview,
|
|
140
|
+
overrideNeedsConfirmation: () => overrideNeedsConfirmation,
|
|
93
141
|
parseCount: () => parseCount,
|
|
94
142
|
partedPromptUsage: () => partedPromptUsage,
|
|
95
143
|
pastGateConfirmation: () => pastGateConfirmation,
|
|
144
|
+
patchView: () => patchView,
|
|
96
145
|
pausedOnLimit: () => pausedOnLimit,
|
|
146
|
+
planBlocks: () => planBlocks,
|
|
147
|
+
planMarkerParts: () => planMarkerParts,
|
|
97
148
|
planUsage: () => planUsage,
|
|
98
149
|
plannerContextFill: () => plannerContextFill,
|
|
150
|
+
receiptLabel: () => receiptLabel,
|
|
99
151
|
reconcileApprovals: () => reconcileApprovals,
|
|
100
152
|
reduceConversation: () => reduceConversation,
|
|
101
153
|
reduceTaskLog: () => reduceTaskLog,
|
|
154
|
+
repairedLanded: () => repairedLanded,
|
|
155
|
+
repairedNotice: () => repairedNotice,
|
|
156
|
+
repeatingChecks: () => repeatingChecks,
|
|
102
157
|
replayTaskLog: () => replayTaskLog,
|
|
158
|
+
repoBaseLine: () => repoBaseLine,
|
|
159
|
+
repoResultLines: () => repoResultLines,
|
|
103
160
|
resolveTaskRef: () => resolveTaskRef,
|
|
104
161
|
resolveUserTaskRef: () => resolveUserTaskRef,
|
|
105
162
|
resumeLabel: () => resumeLabel,
|
|
106
163
|
resumeTime: () => resumeTime,
|
|
107
164
|
resumedSessionWindow: () => resumedSessionWindow,
|
|
165
|
+
runHoldsEditors: () => runHoldsEditors,
|
|
108
166
|
runnerToolSubject: () => runnerToolSubject,
|
|
167
|
+
savedPatchView: () => savedPatchView,
|
|
168
|
+
savedPatchViews: () => savedPatchViews,
|
|
169
|
+
scriptTaskText: () => scriptTaskText,
|
|
170
|
+
severityLabel: () => severityLabel,
|
|
109
171
|
skillTokens: () => skillTokens,
|
|
110
172
|
skippedLabel: () => skippedLabel,
|
|
111
173
|
startAttemptUsage: () => startAttemptUsage,
|
|
@@ -114,6 +176,10 @@ __export(plan_utils_exports, {
|
|
|
114
176
|
subtreeIds: () => subtreeIds,
|
|
115
177
|
summarizeToolCall: () => summarizeToolCall,
|
|
116
178
|
takeAt: () => takeAt,
|
|
179
|
+
taskActionCommand: () => taskActionCommand,
|
|
180
|
+
taskChatAvailable: () => taskChatAvailable,
|
|
181
|
+
taskLogCommands: () => taskLogCommands,
|
|
182
|
+
taskLogHeaderView: () => taskLogHeaderView,
|
|
117
183
|
taskOrderLabel: () => taskOrderLabel,
|
|
118
184
|
taskPlacement: () => taskPlacement,
|
|
119
185
|
taskRef: () => taskRef,
|
|
@@ -133,7 +199,9 @@ __export(plan_utils_exports, {
|
|
|
133
199
|
unsendLatest: () => unsendLatest,
|
|
134
200
|
usageLine: () => usageLine,
|
|
135
201
|
usageText: () => usageText,
|
|
136
|
-
|
|
202
|
+
wakeUpItems: () => wakeUpItems,
|
|
203
|
+
walkPlan: () => walkPlan,
|
|
204
|
+
worstSeverity: () => worstSeverity
|
|
137
205
|
});
|
|
138
206
|
module.exports = __toCommonJS(plan_utils_exports);
|
|
139
207
|
|
|
@@ -243,6 +311,9 @@ function canSetDependencies(tasks, taskId, dependencies) {
|
|
|
243
311
|
return cycle ? { ok: false, error: `Dependency cycle detected: ${[...cycle, cycle[0]].map((t) => `"${t.title}"`).join(" \u2192 ")}` } : { ok: true };
|
|
244
312
|
}
|
|
245
313
|
|
|
314
|
+
// src/services/repoPath.ts
|
|
315
|
+
var SELF_REPO = ".";
|
|
316
|
+
|
|
246
317
|
// src/services/TaskRefs.ts
|
|
247
318
|
function taskOrderLabel(task, parent) {
|
|
248
319
|
return parent ? `${parent.label ?? parent.order}.${task.order}` : String(task.order);
|
|
@@ -331,10 +402,21 @@ function awaitingLabel(task) {
|
|
|
331
402
|
function blockedByLabel(refs) {
|
|
332
403
|
return `Blocked by ${refs.join(", ")}`;
|
|
333
404
|
}
|
|
405
|
+
function holdLabel(reason) {
|
|
406
|
+
return `On hold: ${reason}`;
|
|
407
|
+
}
|
|
334
408
|
function skippedLabel(reason) {
|
|
335
409
|
return reason ? `Skipped: ${reason}` : "Skipped";
|
|
336
410
|
}
|
|
337
411
|
var WAITS_FOR_APPROVAL_LABEL = "Waits for your approval";
|
|
412
|
+
var RECEIPT_LABELS = {
|
|
413
|
+
done: "Completion received",
|
|
414
|
+
failed: "Failure report received",
|
|
415
|
+
blocked: "Blocked report received"
|
|
416
|
+
};
|
|
417
|
+
function receiptLabel(task) {
|
|
418
|
+
return isTaskRunning(task) && task.completionReceived ? RECEIPT_LABELS[task.completionReceived] : null;
|
|
419
|
+
}
|
|
338
420
|
function approvalLabel(count) {
|
|
339
421
|
if (!count) return null;
|
|
340
422
|
return count > 1 ? `Waiting for approval (${count})` : "Waiting for approval";
|
|
@@ -386,6 +468,106 @@ function clampThinkingEffort(effort, variants) {
|
|
|
386
468
|
return void 0;
|
|
387
469
|
}
|
|
388
470
|
|
|
471
|
+
// src/models/ScriptTask.ts
|
|
472
|
+
var DEFAULT_SCRIPT_TIMEOUT_MS = 10 * 6e4;
|
|
473
|
+
var MAX_SCRIPT_TIMEOUT_MS = 24 * 60 * 6e4;
|
|
474
|
+
var SCRIPT_BODY_FIELDS = ["commands", "script", "timeoutMs"];
|
|
475
|
+
function scriptBodyOf(task) {
|
|
476
|
+
if (task.type !== "script" || scriptBodyErrors(task).length > 0) return null;
|
|
477
|
+
const timeoutMs = task.timeoutMs ?? DEFAULT_SCRIPT_TIMEOUT_MS;
|
|
478
|
+
return task.commands ? { commands: task.commands.map((argv) => [...argv]), timeoutMs } : { script: task.script ?? "", timeoutMs };
|
|
479
|
+
}
|
|
480
|
+
function scriptBodyErrors(task) {
|
|
481
|
+
if (task.type !== "script") {
|
|
482
|
+
return SCRIPT_BODY_FIELDS.filter((f) => task[f] !== void 0).map((field) => ({ field, message: `has "${field}", which only a script task has: set "type": "script", or leave it out` }));
|
|
483
|
+
}
|
|
484
|
+
const errors = [];
|
|
485
|
+
const { commands, script, timeoutMs } = task;
|
|
486
|
+
if (commands !== void 0 && script !== void 0) {
|
|
487
|
+
errors.push({ field: "commands", message: 'has both "commands" and "script": a script task runs exactly one of them' });
|
|
488
|
+
} else if (commands === void 0 && script === void 0) {
|
|
489
|
+
errors.push({ field: "commands", message: 'is a script task with neither "commands" nor "script": give it the argument vectors to run, or the script' });
|
|
490
|
+
}
|
|
491
|
+
if (commands !== void 0) errors.push(...commandsErrors(commands));
|
|
492
|
+
if (script !== void 0 && (typeof script !== "string" || !script.trim())) {
|
|
493
|
+
errors.push({ field: "script", message: 'has an empty "script": write the script it runs' });
|
|
494
|
+
}
|
|
495
|
+
if (timeoutMs !== void 0 && !(typeof timeoutMs === "number" && Number.isInteger(timeoutMs) && timeoutMs > 0 && timeoutMs <= MAX_SCRIPT_TIMEOUT_MS)) {
|
|
496
|
+
errors.push({ field: "timeoutMs", message: `has "timeoutMs" ${JSON.stringify(timeoutMs)}: the time limit is a whole number of milliseconds from 1 to ${MAX_SCRIPT_TIMEOUT_MS}, or left out for ten minutes` });
|
|
497
|
+
}
|
|
498
|
+
return errors;
|
|
499
|
+
}
|
|
500
|
+
function commandsErrors(commands) {
|
|
501
|
+
const shape = 'a list of argument vectors, each a list of strings, like [["git", "push", "origin", "main"]]';
|
|
502
|
+
if (!Array.isArray(commands) || commands.length === 0) return [{ field: "commands", message: `has "commands" that is not ${shape}` }];
|
|
503
|
+
for (const [i, argv] of commands.entries()) {
|
|
504
|
+
const n = i + 1;
|
|
505
|
+
if (!Array.isArray(argv) || !argv.every((word) => typeof word === "string")) {
|
|
506
|
+
return [{ field: "commands", message: `has command ${n} that is not an argument vector: "commands" is ${shape}` }];
|
|
507
|
+
}
|
|
508
|
+
if (argv.length === 0) return [{ field: "commands", message: `has command ${n} as an empty argument vector: name the program it runs` }];
|
|
509
|
+
if (!argv[0].trim()) return [{ field: "commands", message: `has command ${n} naming no program: its first word is the program it runs` }];
|
|
510
|
+
}
|
|
511
|
+
return [];
|
|
512
|
+
}
|
|
513
|
+
var PLAIN_WORD = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
514
|
+
var UNSEEN = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;
|
|
515
|
+
function commandLine(argv) {
|
|
516
|
+
return argv.map(quotedWord).join(" ");
|
|
517
|
+
}
|
|
518
|
+
function quotedWord(word) {
|
|
519
|
+
if (PLAIN_WORD.test(word)) return word;
|
|
520
|
+
if (!UNSEEN.test(word)) return `'${word.replace(/'/g, `'\\''`)}'`;
|
|
521
|
+
return `$'${[...word].map(escaped).join("")}'`;
|
|
522
|
+
}
|
|
523
|
+
var ESCAPES = { "\\": "\\\\", "'": "\\'", "\n": "\\n", "\r": "\\r", " ": "\\t" };
|
|
524
|
+
function escaped(char) {
|
|
525
|
+
return ESCAPES[char] ?? (UNSEEN.test(char) ? codeOf(char) : char);
|
|
526
|
+
}
|
|
527
|
+
function codeOf(char) {
|
|
528
|
+
const code = char.codePointAt(0) ?? 0;
|
|
529
|
+
const hex = code.toString(16).toUpperCase();
|
|
530
|
+
return code <= 255 ? `\\x${hex.padStart(2, "0")}` : code <= 65535 ? `\\u${hex.padStart(4, "0")}` : `\\U${hex.padStart(8, "0")}`;
|
|
531
|
+
}
|
|
532
|
+
function scriptBodyText(body) {
|
|
533
|
+
const shown = "commands" in body ? body.commands.map(commandLine) : [body.script.replace(/\r\n|[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/gu, (char) => char === "\r\n" || char === "\n" || char === " " ? char : `<U+${(char.codePointAt(0) ?? 0).toString(16).toUpperCase().padStart(4, "0")}>`)];
|
|
534
|
+
return [...shown, "", `Time limit: ${timeLimitText(body.timeoutMs)}.`].join("\n");
|
|
535
|
+
}
|
|
536
|
+
function scriptTaskText(task) {
|
|
537
|
+
const body = scriptBodyOf(task);
|
|
538
|
+
return body ? scriptBodyText(body) : "This script task has no valid body, so it cannot run.";
|
|
539
|
+
}
|
|
540
|
+
function timeLimitText(ms) {
|
|
541
|
+
const units = [[36e5, "hour"], [6e4, "minute"], [1e3, "second"], [1, "millisecond"]];
|
|
542
|
+
const [size, name] = units.find(([unit]) => ms % unit === 0) ?? units[units.length - 1];
|
|
543
|
+
const n = ms / size;
|
|
544
|
+
return `${n} ${name}${n === 1 ? "" : "s"}`;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// src/models/Loop.ts
|
|
548
|
+
function loopRoundOf(check) {
|
|
549
|
+
return check.loopRound ?? 1;
|
|
550
|
+
}
|
|
551
|
+
function foundWanting(check) {
|
|
552
|
+
return check.verdict?.outcome === "fail" && check.verdict.reported === "failed";
|
|
553
|
+
}
|
|
554
|
+
function betweenRounds(check) {
|
|
555
|
+
return !!check.loop && check.status === "failed" && foundWanting(check) && loopRoundOf(check) < check.loop.maxRounds;
|
|
556
|
+
}
|
|
557
|
+
function outOfRounds(check) {
|
|
558
|
+
return !!check.loop && check.status === "failed" && foundWanting(check) && loopRoundOf(check) >= check.loop.maxRounds;
|
|
559
|
+
}
|
|
560
|
+
function checkOf(tasks, taskId) {
|
|
561
|
+
return tasks.find((t) => t.loop?.body.includes(taskId));
|
|
562
|
+
}
|
|
563
|
+
function loopRemovalRefusal(task) {
|
|
564
|
+
return task.loop ? `"${task.title}" is a loop's check: remove its body tasks first, or drop its loop ("loop": null), so they do not run on their own` : null;
|
|
565
|
+
}
|
|
566
|
+
function loopReshapeRefusal(tasks, target, action) {
|
|
567
|
+
if (target.loop) return `"${target.title}" is a loop's check and cannot be ${action}: drop its loop first ("loop": null)`;
|
|
568
|
+
return checkOf(tasks, target.id) ? `"${target.title}" is in a loop's body and cannot be ${action}: take it out of the loop first` : null;
|
|
569
|
+
}
|
|
570
|
+
|
|
389
571
|
// src/services/PlannedTask.ts
|
|
390
572
|
function findCycle(tasks) {
|
|
391
573
|
return findDependencyCycle(tasks);
|
|
@@ -403,6 +585,8 @@ function canMergeTasks(tasks, selectedIds) {
|
|
|
403
585
|
for (const t of toMerge) {
|
|
404
586
|
if (t.status === "in_progress") return { ok: false, error: `"${t.title}" is running and cannot be merged` };
|
|
405
587
|
if (t.status === "completed") return { ok: false, error: `"${t.title}" is completed and cannot be merged` };
|
|
588
|
+
const looped = loopReshapeRefusal(tasks, t, "merged");
|
|
589
|
+
if (looped) return { ok: false, error: looped };
|
|
406
590
|
}
|
|
407
591
|
const ordered = [...toMerge].sort((a, b) => a.order - b.order);
|
|
408
592
|
const survivorId = "__merge_sim__";
|
|
@@ -418,7 +602,8 @@ function canSplitTask(tasks, taskId) {
|
|
|
418
602
|
if (!target) return { ok: false, error: "Task not found" };
|
|
419
603
|
if (target.status === "in_progress") return { ok: false, error: `"${target.title}" is running and cannot be split` };
|
|
420
604
|
if (target.status === "completed") return { ok: false, error: `"${target.title}" is completed and cannot be split` };
|
|
421
|
-
|
|
605
|
+
const looped = loopReshapeRefusal(tasks, target, "split");
|
|
606
|
+
return looped ? { ok: false, error: looped } : { ok: true };
|
|
422
607
|
}
|
|
423
608
|
|
|
424
609
|
// src/services/researchStepSummary.ts
|
|
@@ -481,6 +666,72 @@ function summarizeToolCall(tool, argsJson, toolLabel) {
|
|
|
481
666
|
}
|
|
482
667
|
return display;
|
|
483
668
|
}
|
|
669
|
+
var SEALED_RUN_MARK = "[Ran sealed";
|
|
670
|
+
function ranSealed(output) {
|
|
671
|
+
return output.startsWith(SEALED_RUN_MARK);
|
|
672
|
+
}
|
|
673
|
+
function withoutSealedMark(output) {
|
|
674
|
+
if (!ranSealed(output)) return output;
|
|
675
|
+
const end = output.indexOf("\n");
|
|
676
|
+
return end < 0 ? "" : output.slice(end + 1);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// src/services/harness/agentTools.ts
|
|
680
|
+
var KNOWN_TOOLS = {
|
|
681
|
+
// Claude Code
|
|
682
|
+
read: "read_file",
|
|
683
|
+
grep: "grep",
|
|
684
|
+
glob: "glob",
|
|
685
|
+
bash: "bash",
|
|
686
|
+
// Its second shell tool, under the same opt-in and launcher (ADR-0036 N6).
|
|
687
|
+
monitor: "bash",
|
|
688
|
+
ls: "list_dir",
|
|
689
|
+
webfetch: "fetch",
|
|
690
|
+
websearch: "web_search",
|
|
691
|
+
// Codex — its sandboxed command tool is a shell by another name
|
|
692
|
+
shell: "bash",
|
|
693
|
+
exec_command: "bash",
|
|
694
|
+
local_shell: "bash",
|
|
695
|
+
// OpenCode
|
|
696
|
+
list: "list_dir",
|
|
697
|
+
fetch: "fetch",
|
|
698
|
+
// Ordewell's own research shell (ADR-0026), under every runner's name for it
|
|
699
|
+
runcommand: "bash"
|
|
700
|
+
};
|
|
701
|
+
var ORDEWELL_TOOL_PREFIX = /^(mcp__ordewell__|ordewell_)/i;
|
|
702
|
+
var FILE_EDIT_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "multiedit", "notebookedit", "applypatch", "patch", "add", "update", "delete", "filechange"]);
|
|
703
|
+
function isFileEditTool(name) {
|
|
704
|
+
return FILE_EDIT_TOOLS.has(name.trim().toLowerCase().replace(/[_-]/g, ""));
|
|
705
|
+
}
|
|
706
|
+
function mapAgentTool(name) {
|
|
707
|
+
const normalized = name.trim().replace(ORDEWELL_TOOL_PREFIX, "").toLowerCase().replace(/[_-]/g, "");
|
|
708
|
+
const direct = KNOWN_TOOLS[normalized] ?? KNOWN_TOOLS[name.trim().toLowerCase()];
|
|
709
|
+
if (direct) {
|
|
710
|
+
return { tool: direct, toolLabel: name };
|
|
711
|
+
}
|
|
712
|
+
return { tool: "agent_tool", toolLabel: name };
|
|
713
|
+
}
|
|
714
|
+
function normalizeAgentArgs(tool, args) {
|
|
715
|
+
const out = { ...args };
|
|
716
|
+
const alias = (from, to) => {
|
|
717
|
+
if (out[to] === void 0 && out[from] !== void 0) out[to] = out[from];
|
|
718
|
+
};
|
|
719
|
+
if (tool === "read_file" || tool === "list_dir") {
|
|
720
|
+
alias("filePath", "path");
|
|
721
|
+
alias("file_path", "path");
|
|
722
|
+
alias("target", "path");
|
|
723
|
+
}
|
|
724
|
+
if (tool === "bash") {
|
|
725
|
+
alias("cmd", "command");
|
|
726
|
+
alias("script", "command");
|
|
727
|
+
if (typeof out.command !== "string" && Array.isArray(out.command)) {
|
|
728
|
+
out.command = out.command.join(" ");
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
if (tool === "fetch") alias("uri", "url");
|
|
732
|
+
if (tool === "web_search") alias("q", "query");
|
|
733
|
+
return out;
|
|
734
|
+
}
|
|
484
735
|
|
|
485
736
|
// src/conversation/format.ts
|
|
486
737
|
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;
|
|
@@ -610,61 +861,6 @@ function diffSummary({ added, removed }) {
|
|
|
610
861
|
return `Added ${lines(added)}, removed ${lines(removed)}`;
|
|
611
862
|
}
|
|
612
863
|
|
|
613
|
-
// src/services/harness/agentTools.ts
|
|
614
|
-
var KNOWN_TOOLS = {
|
|
615
|
-
// Claude Code
|
|
616
|
-
read: "read_file",
|
|
617
|
-
grep: "grep",
|
|
618
|
-
glob: "glob",
|
|
619
|
-
bash: "bash",
|
|
620
|
-
ls: "list_dir",
|
|
621
|
-
webfetch: "fetch",
|
|
622
|
-
websearch: "web_search",
|
|
623
|
-
// Codex — its sandboxed command tool is a shell by another name
|
|
624
|
-
shell: "bash",
|
|
625
|
-
exec_command: "bash",
|
|
626
|
-
local_shell: "bash",
|
|
627
|
-
// OpenCode
|
|
628
|
-
list: "list_dir",
|
|
629
|
-
fetch: "fetch",
|
|
630
|
-
// Ordewell's own research shell (ADR-0026), under every runner's name for it
|
|
631
|
-
runcommand: "bash"
|
|
632
|
-
};
|
|
633
|
-
var ORDEWELL_TOOL_PREFIX = /^(mcp__ordewell__|ordewell_)/i;
|
|
634
|
-
var FILE_EDIT_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "multiedit", "notebookedit", "applypatch", "patch", "add", "update", "delete", "filechange"]);
|
|
635
|
-
function isFileEditTool(name) {
|
|
636
|
-
return FILE_EDIT_TOOLS.has(name.trim().toLowerCase().replace(/[_-]/g, ""));
|
|
637
|
-
}
|
|
638
|
-
function mapAgentTool(name) {
|
|
639
|
-
const normalized = name.trim().replace(ORDEWELL_TOOL_PREFIX, "").toLowerCase().replace(/[_-]/g, "");
|
|
640
|
-
const direct = KNOWN_TOOLS[normalized] ?? KNOWN_TOOLS[name.trim().toLowerCase()];
|
|
641
|
-
if (direct) {
|
|
642
|
-
return { tool: direct, toolLabel: name };
|
|
643
|
-
}
|
|
644
|
-
return { tool: "agent_tool", toolLabel: name };
|
|
645
|
-
}
|
|
646
|
-
function normalizeAgentArgs(tool, args) {
|
|
647
|
-
const out = { ...args };
|
|
648
|
-
const alias = (from, to) => {
|
|
649
|
-
if (out[to] === void 0 && out[from] !== void 0) out[to] = out[from];
|
|
650
|
-
};
|
|
651
|
-
if (tool === "read_file" || tool === "list_dir") {
|
|
652
|
-
alias("filePath", "path");
|
|
653
|
-
alias("file_path", "path");
|
|
654
|
-
alias("target", "path");
|
|
655
|
-
}
|
|
656
|
-
if (tool === "bash") {
|
|
657
|
-
alias("cmd", "command");
|
|
658
|
-
alias("script", "command");
|
|
659
|
-
if (typeof out.command !== "string" && Array.isArray(out.command)) {
|
|
660
|
-
out.command = out.command.join(" ");
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
if (tool === "fetch") alias("uri", "url");
|
|
664
|
-
if (tool === "web_search") alias("q", "query");
|
|
665
|
-
return out;
|
|
666
|
-
}
|
|
667
|
-
|
|
668
864
|
// src/conversation/records.ts
|
|
669
865
|
function settledMessage(id, role, text2, turnId) {
|
|
670
866
|
return { type: "message", id, role, text: text2, streaming: false, ...turnId ? { turnId } : {} };
|
|
@@ -694,6 +890,20 @@ function subagentBlock(id, { toolCallId, model, usage, turnId, ...fields }) {
|
|
|
694
890
|
...turnId ? { turnId } : {}
|
|
695
891
|
};
|
|
696
892
|
}
|
|
893
|
+
function compactionBlock(id, { trigger, compactionId, origin, status, summary, preTokens, postTokens, turnId }) {
|
|
894
|
+
return {
|
|
895
|
+
type: "compaction",
|
|
896
|
+
id,
|
|
897
|
+
trigger,
|
|
898
|
+
...compactionId ? { compactionId } : {},
|
|
899
|
+
...origin ? { origin } : {},
|
|
900
|
+
...status ? { status } : {},
|
|
901
|
+
...summary ? { summary } : {},
|
|
902
|
+
...preTokens !== void 0 ? { preTokens } : {},
|
|
903
|
+
...postTokens !== void 0 ? { postTokens } : {},
|
|
904
|
+
...turnId ? { turnId } : {}
|
|
905
|
+
};
|
|
906
|
+
}
|
|
697
907
|
function usageBlock(id, { totals, bySubagent, byModel, model, contextFill }) {
|
|
698
908
|
return { type: "usage", id, totals, ...byModel ? { byModel } : {}, ...model ? { model } : {}, ...bySubagent ? { bySubagent } : {}, ...contextFill ? { contextFill } : {} };
|
|
699
909
|
}
|
|
@@ -706,7 +916,9 @@ var STATUS_OF_OUTCOME = {
|
|
|
706
916
|
};
|
|
707
917
|
function finishedTool(call, output, outcome) {
|
|
708
918
|
const diff = outcome === "success" && isFileEditTool(call.toolLabel ?? call.tool) ? diffStat(output) : null;
|
|
709
|
-
|
|
919
|
+
const sealed2 = call.tool === "bash" && ranSealed(output);
|
|
920
|
+
const shown = sealed2 ? withoutSealedMark(output) : output;
|
|
921
|
+
return { ...call, status: STATUS_OF_OUTCOME[outcome], outcome, output: shown, outputLineCount: outputLines(shown).length, ...diff ? { diff } : {}, ...sealed2 ? { sealed: sealed2 } : {} };
|
|
710
922
|
}
|
|
711
923
|
function settledTool(call, step) {
|
|
712
924
|
return finishedTool(call, step.result, step.outcome);
|
|
@@ -737,6 +949,12 @@ function planMarker(content) {
|
|
|
737
949
|
// src/services/blocking.ts
|
|
738
950
|
var UNSTARTED = /* @__PURE__ */ new Set(["pending", "approved"]);
|
|
739
951
|
function blockingFailures(tasks) {
|
|
952
|
+
return planBlocks(tasks).blocked;
|
|
953
|
+
}
|
|
954
|
+
function repeatingChecks(tasks) {
|
|
955
|
+
return planBlocks(tasks).repeating;
|
|
956
|
+
}
|
|
957
|
+
function planBlocks(tasks) {
|
|
740
958
|
const byId = /* @__PURE__ */ new Map();
|
|
741
959
|
const index = (list) => {
|
|
742
960
|
for (const t of list) {
|
|
@@ -746,6 +964,19 @@ function blockingFailures(tasks) {
|
|
|
746
964
|
};
|
|
747
965
|
index(tasks);
|
|
748
966
|
const memo = /* @__PURE__ */ new Map();
|
|
967
|
+
const stopped = /* @__PURE__ */ new Map();
|
|
968
|
+
const repeats = (check) => {
|
|
969
|
+
if (!betweenRounds(check)) return false;
|
|
970
|
+
const known = stopped.get(check.id);
|
|
971
|
+
if (known !== void 0) return !known;
|
|
972
|
+
stopped.set(check.id, false);
|
|
973
|
+
const halted = (check.loop?.body ?? []).some((id) => {
|
|
974
|
+
const body = byId.get(id);
|
|
975
|
+
return !!body && (body.status === "failed" || body.status === "skipped" || failuresOf(id, /* @__PURE__ */ new Set()).length > 0);
|
|
976
|
+
});
|
|
977
|
+
stopped.set(check.id, halted);
|
|
978
|
+
return !halted;
|
|
979
|
+
};
|
|
749
980
|
const failuresOf = (id, visiting) => {
|
|
750
981
|
const known = memo.get(id);
|
|
751
982
|
if (known) return known;
|
|
@@ -756,8 +987,10 @@ function blockingFailures(tasks) {
|
|
|
756
987
|
for (const depId of task.dependencies) {
|
|
757
988
|
const dep = byId.get(depId);
|
|
758
989
|
if (!dep || dep.status === "completed" || dep.status === "skipped") continue;
|
|
759
|
-
if (dep.status === "failed")
|
|
760
|
-
|
|
990
|
+
if (dep.status === "failed") {
|
|
991
|
+
if (betweenRounds(dep) && (dep.loop?.body.includes(id) || repeats(dep))) continue;
|
|
992
|
+
failed.add(depId);
|
|
993
|
+
} else for (const root of failuresOf(depId, visiting)) failed.add(root);
|
|
761
994
|
}
|
|
762
995
|
visiting.delete(id);
|
|
763
996
|
const result = [...failed];
|
|
@@ -765,14 +998,17 @@ function blockingFailures(tasks) {
|
|
|
765
998
|
return result;
|
|
766
999
|
};
|
|
767
1000
|
const blocked = /* @__PURE__ */ new Map();
|
|
768
|
-
|
|
1001
|
+
const repeating = /* @__PURE__ */ new Set();
|
|
1002
|
+
for (const [id, task] of byId) {
|
|
769
1003
|
const failures = failuresOf(id, /* @__PURE__ */ new Set());
|
|
770
1004
|
if (failures.length > 0) blocked.set(id, failures);
|
|
1005
|
+
if (repeats(task)) repeating.add(id);
|
|
771
1006
|
}
|
|
772
|
-
return blocked;
|
|
1007
|
+
return { blocked, repeating };
|
|
773
1008
|
}
|
|
774
1009
|
|
|
775
1010
|
// src/services/SessionMessage.ts
|
|
1011
|
+
var IDLE_ACTIVITY = { starting: false, running: false, scheduled: false, resumable: false, advancesOnItsOwn: false };
|
|
776
1012
|
var CHECKPOINT_TRUNCATE_LENGTH = 120;
|
|
777
1013
|
function truncateCheckpointSummary(summary) {
|
|
778
1014
|
const firstLine = summary.split("\n")[0].trim();
|
|
@@ -785,8 +1021,155 @@ function capConflictFiles(files, max = 5) {
|
|
|
785
1021
|
return files.length <= max ? files.join(", ") : `${files.slice(0, max).join(", ")}, +${files.length - max} more`;
|
|
786
1022
|
}
|
|
787
1023
|
|
|
1024
|
+
// src/services/mergeResultNotice.ts
|
|
1025
|
+
function blockNotice({ repo, reason, files, expected, actual, rebasing }) {
|
|
1026
|
+
switch (reason) {
|
|
1027
|
+
case "merge-in-progress":
|
|
1028
|
+
return `${repo} has a merge in progress`;
|
|
1029
|
+
case "conflict":
|
|
1030
|
+
return `${repo} would conflict in ${files.join(", ")}`;
|
|
1031
|
+
case "uncommitted-changes":
|
|
1032
|
+
return `${repo} has uncommitted changes to ${files.join(", ")}`;
|
|
1033
|
+
case "partial-landing":
|
|
1034
|
+
return `${repo} holds part of a task whose landing could not be rolled back`;
|
|
1035
|
+
case "wrong-branch":
|
|
1036
|
+
return `${repo === SELF_REPO ? "your checkout" : repo} is ${actual ? `on ${actual}` : rebasing ? "in the middle of a rebase" : "on a detached HEAD"}; this run started on ${expected}`;
|
|
1037
|
+
case "git-error":
|
|
1038
|
+
return `git could not check ${repo}`;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
function blockNotices(blocked) {
|
|
1042
|
+
return blocked.map(blockNotice);
|
|
1043
|
+
}
|
|
1044
|
+
function branchFix(blocked) {
|
|
1045
|
+
if (blocked.length === 0 || blocked.some((b) => b.reason !== "wrong-branch")) return "";
|
|
1046
|
+
const rebasing = blocked.filter((b) => b.rebasing);
|
|
1047
|
+
if (rebasing.length > 0) {
|
|
1048
|
+
const where = rebasing.map((b) => b.repo === SELF_REPO ? "your checkout" : b.repo).join(", ");
|
|
1049
|
+
return ` Finish or abort the rebase in ${where}${rebasing.length < blocked.length ? " and check out the branch each other repository started on" : ""}, then press Merge all again.`;
|
|
1050
|
+
}
|
|
1051
|
+
const started = [...new Set(blocked.map((b) => b.expected))];
|
|
1052
|
+
return started.length === 1 && blocked.length === 1 ? ` Check out ${started[0]} and press Merge all again.` : " Check out the branch each repository started on and press Merge all again.";
|
|
1053
|
+
}
|
|
1054
|
+
function landedNotice(landed) {
|
|
1055
|
+
if (landed.length === 0) return "Nothing was merged.";
|
|
1056
|
+
return landed.length === 1 ? `${landed[0]} was merged already and stays merged.` : `${landed.join(", ")} were merged already and stay merged.`;
|
|
1057
|
+
}
|
|
1058
|
+
function repairedLanded(handoff) {
|
|
1059
|
+
return handoff.landed.filter((t) => t.repairedFiles?.length);
|
|
1060
|
+
}
|
|
1061
|
+
function repairedNotice(handoff) {
|
|
1062
|
+
const repaired = repairedLanded(handoff);
|
|
1063
|
+
if (repaired.length === 0) return "";
|
|
1064
|
+
const named = repaired.map((t) => `${t.title}${t.repairedFiles?.length ? ` (${capConflictFiles(t.repairedFiles)})` : ""}`);
|
|
1065
|
+
const list = named.length === 1 ? named[0] : `${named.slice(0, -1).join(", ")} and ${named[named.length - 1]}`;
|
|
1066
|
+
return ` ${list} landed through ${repaired.length === 1 ? "a conflict repair" : "conflict repairs"}.`;
|
|
1067
|
+
}
|
|
1068
|
+
function describeMergeResult(result, branch, group, repaired = []) {
|
|
1069
|
+
switch (result.outcome) {
|
|
1070
|
+
case "merged": {
|
|
1071
|
+
const base = group ? `Merged ${branch} into the checked-out branch of every repository.` : `Merged ${branch} into your checked-out branch.`;
|
|
1072
|
+
const repairedNoticeText = repairedNotice({ landed: repaired });
|
|
1073
|
+
return { level: "info", message: `${base}${repairedNoticeText}` };
|
|
1074
|
+
}
|
|
1075
|
+
case "blocked":
|
|
1076
|
+
return { level: "warn", message: `Merged nothing, so every tree is as it was: ${blockNotices(result.blocked).join("; ")}.${branchFix(result.blocked)}` };
|
|
1077
|
+
case "conflict":
|
|
1078
|
+
return {
|
|
1079
|
+
level: "warn",
|
|
1080
|
+
message: result.repo === SELF_REPO ? `Merging ${branch} conflicted, so it was aborted: your tree is as it was.` : `Merging ${branch} conflicted in ${result.repo}${result.files?.length ? ` (${result.files.join(", ")})` : ""}, so it was aborted there. ${landedNotice(result.landed ?? [])}`
|
|
1081
|
+
};
|
|
1082
|
+
case "failed": {
|
|
1083
|
+
const lone = result.repo === SELF_REPO;
|
|
1084
|
+
const where = lone ? "" : ` in ${result.repo}`;
|
|
1085
|
+
if (result.error) {
|
|
1086
|
+
return { level: "error", message: `Could not merge ${branch}${where}: git said "${result.error}". ${lone ? "Your tree is as it was." : landedNotice(result.landed ?? [])}` };
|
|
1087
|
+
}
|
|
1088
|
+
return {
|
|
1089
|
+
level: "error",
|
|
1090
|
+
message: lone ? `Could not merge ${branch}: finish or abort the merge already in progress, then try again.` : `Could not merge ${branch}${where}: finish or abort any merge in progress there, then try again. ${landedNotice(result.landed ?? [])}`
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
function mergeOutcome(result, branch, group, repaired = []) {
|
|
1096
|
+
const ok = result.outcome === "merged";
|
|
1097
|
+
if (!group && result.outcome === "conflict") {
|
|
1098
|
+
return { ok, message: `Merging ${branch} conflicted, so it was aborted: your tree is as it was. Merge it with git and resolve the conflict there.` };
|
|
1099
|
+
}
|
|
1100
|
+
const { message } = describeMergeResult(result, branch, group, repaired);
|
|
1101
|
+
return { ok, message: ok || !group ? message : `${message} Each repository's ${branch} is a plain branch you can merge by hand.` };
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// src/services/isolationPreviewText.ts
|
|
1105
|
+
var SHARED_NAMED = 5;
|
|
1106
|
+
var OPS_TASKS = "Ops and script tasks use your checkout.";
|
|
1107
|
+
function repoName(repo) {
|
|
1108
|
+
if (repo.path !== ".") return repo.path;
|
|
1109
|
+
return repo.top ? `Repository above this workspace (${repo.top})` : "This workspace";
|
|
1110
|
+
}
|
|
1111
|
+
function repoDetail(repo, status) {
|
|
1112
|
+
const head = repo.head;
|
|
1113
|
+
const base = head.kind === "branch" ? `on ${head.branch} (${head.commit})` : head.kind === "detached" ? `detached HEAD at ${head.commit}` : head.kind === "unknown" ? "branch not read" : head.branch ? `no commits yet on ${head.branch}` : "no commits yet";
|
|
1114
|
+
const shared = head.kind === "unborn" && (status === "ready" || status === "dirty") ? "; tasks share it instead of isolating it" : "";
|
|
1115
|
+
return `${base}${shared}${repo.dirty ? " \xB7 uncommitted changes to tracked files" : ""}`;
|
|
1116
|
+
}
|
|
1117
|
+
function sharedLine(shared) {
|
|
1118
|
+
if (shared.length === 0) return void 0;
|
|
1119
|
+
const more = shared.length - SHARED_NAMED;
|
|
1120
|
+
const named = shared.slice(0, SHARED_NAMED).join(", ");
|
|
1121
|
+
return `Shared live between tasks: ${more > 0 ? `${named} and ${more} more` : named}`;
|
|
1122
|
+
}
|
|
1123
|
+
function describeIsolationPreview(preview) {
|
|
1124
|
+
const repos = preview.repos.map((repo) => ({ name: repoName(repo), detail: repoDetail(repo, preview.status) }));
|
|
1125
|
+
const shared = sharedLine(preview.shared);
|
|
1126
|
+
const notice = (summary, note) => ({ summary, repos, ...shared ? { shared } : {}, ...note ? { note } : {} });
|
|
1127
|
+
switch (preview.status) {
|
|
1128
|
+
case "ready":
|
|
1129
|
+
return notice("Code tasks run in isolated git worktrees based on the repositories and branches below.", `${OPS_TASKS} The base branch is read again when a run starts.`);
|
|
1130
|
+
case "dirty":
|
|
1131
|
+
return notice(
|
|
1132
|
+
"Code tasks get isolated worktrees from the repositories and branches below once you decide what to do with uncommitted changes.",
|
|
1133
|
+
`Starting a run asks whether to stash them or run without isolation. ${OPS_TASKS}`
|
|
1134
|
+
);
|
|
1135
|
+
case "disabled":
|
|
1136
|
+
return notice("Worktree isolation is turned off, so tasks run in the workspace without isolated worktrees.");
|
|
1137
|
+
case "git-missing":
|
|
1138
|
+
return notice("Git was not found, so tasks run in the workspace without isolated worktrees.");
|
|
1139
|
+
case "no-commits":
|
|
1140
|
+
return notice("No repository has a commit yet, so tasks run in the workspace without isolated worktrees.");
|
|
1141
|
+
case "no-repo":
|
|
1142
|
+
return notice("No Git repository detected. Tasks run in the workspace without isolated worktrees.");
|
|
1143
|
+
case "failed":
|
|
1144
|
+
return notice(
|
|
1145
|
+
"Could not read the Git repositories just now, so Ordewell can't say yet whether code tasks get isolated worktrees.",
|
|
1146
|
+
`${preview.error ?? "Git did not answer"}. It is checked again when a run starts.`
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// src/services/mergeWording.ts
|
|
1152
|
+
function isRepoGroup(handoff) {
|
|
1153
|
+
return handoff.repos.some((r) => r.path !== ".");
|
|
1154
|
+
}
|
|
1155
|
+
function handoffBranchName(handoff) {
|
|
1156
|
+
return [...new Set(handoff.repos.map((r) => r.integrationBranch))].join(", ");
|
|
1157
|
+
}
|
|
1158
|
+
function repoBaseLine(repo, length) {
|
|
1159
|
+
const commit = repo.baseRef.slice(0, length);
|
|
1160
|
+
return repo.baseBranch ? `${repo.baseBranch} @ ${commit}` : `${repo.startedDetached ? "detached at" : "base"} ${commit}`;
|
|
1161
|
+
}
|
|
1162
|
+
function handoffBaseLine(handoff, length) {
|
|
1163
|
+
const group = isRepoGroup(handoff);
|
|
1164
|
+
return handoff.repos.map((repo) => `${group ? `${repo.path}: ` : ""}${repoBaseLine(repo, length)}`).join(", ");
|
|
1165
|
+
}
|
|
1166
|
+
function repoResultLines(handoff) {
|
|
1167
|
+
return handoff.repos.map(({ path, landed }) => `${path}: ${landed.length === 0 ? "nothing to merge" : `${landed.length} task${landed.length === 1 ? "" : "s"} landed`}`);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
788
1170
|
// src/services/runSettingsText.ts
|
|
789
1171
|
var SUPERVISOR_LEVEL_LABEL = { off: "Off", watch: "Watch", act: "Act" };
|
|
1172
|
+
var PLANNED_WITH_LABEL = "Planned-with line in commits Ordewell writes";
|
|
790
1173
|
function parseCount(arg, min) {
|
|
791
1174
|
if (arg === void 0 || !/^\d+$/.test(arg)) return null;
|
|
792
1175
|
const n = Number(arg);
|
|
@@ -970,13 +1353,17 @@ function formatCost(currency, amount) {
|
|
|
970
1353
|
function contextMeterText(fill) {
|
|
971
1354
|
if (!fill) return null;
|
|
972
1355
|
const used = `ctx ${formatTokenCount(fill.usedTokens)}`;
|
|
973
|
-
|
|
974
|
-
const
|
|
975
|
-
return `${
|
|
1356
|
+
const share = fill.windowTokens ? fill.usedTokens / fill.windowTokens * 100 : void 0;
|
|
1357
|
+
const meter = share === void 0 ? used : `${used} (${share < 1 ? "<1" : Math.round(share)}%)`;
|
|
1358
|
+
return fill.autoCompaction ? `${meter} \xB7 no auto-compact` : meter;
|
|
976
1359
|
}
|
|
977
1360
|
function contextMeterTitle(fill) {
|
|
978
1361
|
const used = fill.usedTokens.toLocaleString("en-US");
|
|
979
|
-
|
|
1362
|
+
const counts = fill.windowTokens ? `${used} of ${fill.windowTokens.toLocaleString("en-US")} tokens` : `${used} tokens`;
|
|
1363
|
+
const off = fill.autoCompaction;
|
|
1364
|
+
if (!off) return counts;
|
|
1365
|
+
const why = off.off === "settings" ? "Automatic compaction is off in the settings file" : off.model ? `Automatic compaction is off: no catalog Ordewell holds states ${off.model}'s window` : "Automatic compaction is off: no planner model is selected";
|
|
1366
|
+
return `${counts}. ${why}; /compact still condenses the conversation.`;
|
|
980
1367
|
}
|
|
981
1368
|
function costParts(totals) {
|
|
982
1369
|
const rank = (currency) => currency.toLowerCase() === "usd" ? "" : currency.toUpperCase();
|
|
@@ -1023,6 +1410,13 @@ function tasksTotalText(tasks) {
|
|
|
1023
1410
|
return parts.length > 0 ? `Tasks total: ${parts.join(" \xB7 ")}` : null;
|
|
1024
1411
|
}
|
|
1025
1412
|
|
|
1413
|
+
// src/services/conversationSummary.ts
|
|
1414
|
+
function isCheckpoint(value) {
|
|
1415
|
+
if (!value || typeof value !== "object") return false;
|
|
1416
|
+
const { id, keptFrom, trigger } = value;
|
|
1417
|
+
return typeof id === "string" && id !== "" && Number.isInteger(keptFrom) && (trigger === "manual" || trigger === "auto");
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1026
1420
|
// src/conversation/lanes.ts
|
|
1027
1421
|
function findLastIndex(items, match) {
|
|
1028
1422
|
for (let i = items.length - 1; i >= 0; i--) if (match(items[i])) return i;
|
|
@@ -1136,7 +1530,11 @@ function closeSubagent(block, cut) {
|
|
|
1136
1530
|
return status === block.status && children.every((c, i) => c === block.children[i]) ? block : { ...block, status, children };
|
|
1137
1531
|
}
|
|
1138
1532
|
function closeOpenBlocks(view, cut) {
|
|
1139
|
-
const blocks = view.blocks.map((b) =>
|
|
1533
|
+
const blocks = view.blocks.map((b) => {
|
|
1534
|
+
if (b.type === "subagent") return closeSubagent(b, cut);
|
|
1535
|
+
if (b.type === "compaction") return b.status === "running" && (cut === "stopped" || cut === "failed") ? { ...b, status: cut } : b;
|
|
1536
|
+
return closeBlock(b, cut !== null);
|
|
1537
|
+
});
|
|
1140
1538
|
return blocks.every((b, i) => b === view.blocks[i]) ? view : { ...view, blocks };
|
|
1141
1539
|
}
|
|
1142
1540
|
|
|
@@ -1152,7 +1550,7 @@ function finalSegmentIndex(blocks, turnId, pastPlan = false) {
|
|
|
1152
1550
|
for (let i = blocks.length - 1; i >= 0; i--) {
|
|
1153
1551
|
const block = blocks[i];
|
|
1154
1552
|
if (block.type === "usage" || block.type === "thinking" || block.type === "approval" || pastPlan && block.type === "plan") continue;
|
|
1155
|
-
if (block.type === "command" || block.type === "compaction" || block.type === "runner_loaded") continue;
|
|
1553
|
+
if (block.type === "command" || block.type === "compaction" || block.type === "runner_loaded" || block.type === "handoff") continue;
|
|
1156
1554
|
if (block.turnId !== turnId) continue;
|
|
1157
1555
|
return isUnsettledSegment(block, turnId) ? i : -1;
|
|
1158
1556
|
}
|
|
@@ -1168,10 +1566,11 @@ function openTurn(view, { turnId, prompt = "", skills = [], wakeUps = [], forced
|
|
|
1168
1566
|
const updates = wakeUps.map((text2, n) => settledMessage(`b${said.nextId + n}`, "wake_up", text2, turnId));
|
|
1169
1567
|
return { ...said, blocks: [...said.blocks.slice(0, at), ...updates, ...said.blocks.slice(at)], nextId: said.nextId + updates.length };
|
|
1170
1568
|
}
|
|
1171
|
-
function takePendingInputs(view, prompt, wakeUps, turnId) {
|
|
1569
|
+
function takePendingInputs(view, prompt, wakeUps, turnId, heldAt) {
|
|
1172
1570
|
const pending = view.pendingInputs ?? [];
|
|
1173
|
-
const
|
|
1174
|
-
const
|
|
1571
|
+
const arrived = ({ input }) => heldAt === void 0 || input.timestamp <= heldAt;
|
|
1572
|
+
const users = pending.filter((p) => p.input.source === "user" && arrived(p));
|
|
1573
|
+
const updates = pending.filter((p) => p.input.source === "supervisor" && arrived(p));
|
|
1175
1574
|
const taken = /* @__PURE__ */ new Set();
|
|
1176
1575
|
let matchedPrompt = false;
|
|
1177
1576
|
for (let n = 1; prompt !== void 0 && n <= users.length; n++) {
|
|
@@ -1307,7 +1706,7 @@ function markPlan(view, content, turnId) {
|
|
|
1307
1706
|
}
|
|
1308
1707
|
return append(view, (id) => ({ type: "plan", id, text: "", ...marker }));
|
|
1309
1708
|
}
|
|
1310
|
-
function requestApproval(view, { id: approvalId, kind, subject, scope, detail, turnId }) {
|
|
1709
|
+
function requestApproval(view, { id: approvalId, kind, subject, scope, detail, body, turnId }) {
|
|
1311
1710
|
return append(view, (id) => ({
|
|
1312
1711
|
type: "approval",
|
|
1313
1712
|
id,
|
|
@@ -1317,6 +1716,7 @@ function requestApproval(view, { id: approvalId, kind, subject, scope, detail, t
|
|
|
1317
1716
|
scope,
|
|
1318
1717
|
status: "pending",
|
|
1319
1718
|
...detail ? { detail } : {},
|
|
1719
|
+
...body ? { body } : {},
|
|
1320
1720
|
...turnId ? { turnId } : {}
|
|
1321
1721
|
}));
|
|
1322
1722
|
}
|
|
@@ -1347,11 +1747,48 @@ function endTurn(view, { turnId, outcome }) {
|
|
|
1347
1747
|
if (planText >= 0) next = { ...next, blocks: next.blocks.filter((_, i) => i !== planText) };
|
|
1348
1748
|
return closeOpenBlocks(next, outcome === "stopped" ? "stopped" : outcome === "error" ? "failed" : null);
|
|
1349
1749
|
}
|
|
1350
|
-
function
|
|
1750
|
+
function compactionAt(view, compactionId) {
|
|
1751
|
+
return findLastIndex(view.blocks, (b) => b.type === "compaction" && b.compactionId === compactionId);
|
|
1752
|
+
}
|
|
1753
|
+
function followCompaction(view, { compaction, turnId }) {
|
|
1754
|
+
const i = compactionAt(view, compaction.id);
|
|
1755
|
+
const at = view.blocks[i];
|
|
1756
|
+
const known = at?.type === "compaction" ? at : void 0;
|
|
1757
|
+
if (compaction.phase === "started") {
|
|
1758
|
+
if (known) return view;
|
|
1759
|
+
return append(view, (id) => compactionBlock(id, { compactionId: compaction.id, origin: compaction.origin, trigger: compaction.trigger, status: "running", turnId }));
|
|
1760
|
+
}
|
|
1761
|
+
const { outcome, preTokens, postTokens } = compaction;
|
|
1762
|
+
if (known && known.status !== "running") return view;
|
|
1763
|
+
if (outcome === "unchanged") return known ? { ...view, blocks: view.blocks.filter((_, n) => n !== i) } : view;
|
|
1764
|
+
if (!known && (outcome === "stopped" || outcome === "failed")) return view;
|
|
1765
|
+
const ended = (id) => compactionBlock(id, {
|
|
1766
|
+
compactionId: compaction.id,
|
|
1767
|
+
origin: compaction.origin,
|
|
1768
|
+
trigger: compaction.trigger,
|
|
1769
|
+
status: outcome,
|
|
1770
|
+
preTokens,
|
|
1771
|
+
postTokens,
|
|
1772
|
+
turnId: known?.turnId ?? turnId
|
|
1773
|
+
});
|
|
1774
|
+
return known ? { ...view, blocks: replaceAt(view.blocks, i, ended(known.id)) } : append(view, ended);
|
|
1775
|
+
}
|
|
1776
|
+
function markCompaction(view, entry) {
|
|
1777
|
+
const { content: summary } = entry;
|
|
1778
|
+
const checkpoint = isCheckpoint(entry.checkpoint) ? entry.checkpoint : void 0;
|
|
1779
|
+
if (checkpoint) {
|
|
1780
|
+
const i2 = compactionAt(view, checkpoint.id);
|
|
1781
|
+
const known = view.blocks[i2];
|
|
1782
|
+
if (known?.type !== "compaction") {
|
|
1783
|
+
return append(view, (id) => compactionBlock(id, { compactionId: checkpoint.id, origin: "ordewell", trigger: checkpoint.trigger, status: "condensed", summary }));
|
|
1784
|
+
}
|
|
1785
|
+
if (known.status === "condensed" && known.summary === summary) return view;
|
|
1786
|
+
return { ...view, blocks: replaceAt(view.blocks, i2, { ...known, status: "condensed", summary }) };
|
|
1787
|
+
}
|
|
1788
|
+
const settled = (id) => compactionBlock(id, { origin: "ordewell", trigger: "manual", status: "condensed", summary });
|
|
1351
1789
|
const i = findLastIndex(view.blocks, (b) => b.type === "message" && b.role === "planner" && b.turnId === void 0 && b.text === summary);
|
|
1352
1790
|
const announced = view.blocks[i];
|
|
1353
|
-
|
|
1354
|
-
return append(view, (id) => settledMessage(id, "system", summary));
|
|
1791
|
+
return announced ? { ...view, blocks: replaceAt(view.blocks, i, settled(announced.id)) } : append(view, settled);
|
|
1355
1792
|
}
|
|
1356
1793
|
function syncTranscript(view, { plan, turnId }) {
|
|
1357
1794
|
const history = plan.conversationHistory ?? [];
|
|
@@ -1360,12 +1797,13 @@ function syncTranscript(view, { plan, turnId }) {
|
|
|
1360
1797
|
let latest = view.transcriptAt;
|
|
1361
1798
|
for (const [i, entry] of history.entries()) {
|
|
1362
1799
|
const isNew = view.transcriptAt === void 0 || entry.timestamp > view.transcriptAt;
|
|
1363
|
-
|
|
1364
|
-
if (
|
|
1800
|
+
const heldAt = isNew ? void 0 : entry.timestamp;
|
|
1801
|
+
if (entry.role === "user" && !entry.kind) next = takePendingInputs(next, entry.content, [], void 0, heldAt).view;
|
|
1802
|
+
if (entry.kind === "wake_up") next = takePendingInputs(next, void 0, [entry.content], void 0, heldAt).view;
|
|
1365
1803
|
if (isNew && entry.kind === "plan_generated") next = markPlan(next, entry.content, i === committed ? turnId : void 0);
|
|
1366
1804
|
if (isNew && entry.kind === "system") next = append(next, (id) => settledMessage(id, "system", entry.content));
|
|
1367
1805
|
if (isNew && entry.kind === "error") next = append(next, (id) => settledMessage(id, "error", entry.content));
|
|
1368
|
-
if (isNew && entry.kind === "compaction") next = markCompaction(next, entry
|
|
1806
|
+
if (isNew && entry.kind === "compaction") next = markCompaction(next, entry);
|
|
1369
1807
|
if (latest === void 0 || entry.timestamp > latest) latest = entry.timestamp;
|
|
1370
1808
|
}
|
|
1371
1809
|
return next === view && latest === view.transcriptAt ? view : { ...next, transcriptAt: latest };
|
|
@@ -1417,6 +1855,8 @@ function reduceConversation(view, input) {
|
|
|
1417
1855
|
return append(view, (id) => settledMessage(id, "system", input.text));
|
|
1418
1856
|
case "planner_usage":
|
|
1419
1857
|
return reportUsage(view, input);
|
|
1858
|
+
case "planner_compaction":
|
|
1859
|
+
return followCompaction(view, input);
|
|
1420
1860
|
default:
|
|
1421
1861
|
return view;
|
|
1422
1862
|
}
|
|
@@ -1432,13 +1872,22 @@ function fromEntry(entry, loaded = []) {
|
|
|
1432
1872
|
const { skill } = entry;
|
|
1433
1873
|
return (id) => skillLoadBlock(id(), skillLoadNotice(skill));
|
|
1434
1874
|
}
|
|
1875
|
+
if (entry.kind === "compaction") {
|
|
1876
|
+
const checkpoint = isCheckpoint(entry.checkpoint) ? entry.checkpoint : void 0;
|
|
1877
|
+
return (id) => compactionBlock(id(), {
|
|
1878
|
+
compactionId: checkpoint?.id,
|
|
1879
|
+
origin: "ordewell",
|
|
1880
|
+
trigger: checkpoint?.trigger ?? "manual",
|
|
1881
|
+
status: "condensed",
|
|
1882
|
+
summary: entry.content
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1435
1885
|
if (entry.role === "user" && !entry.kind) return (id) => userMessage(id(), entry.content, loaded.map(skillLoadNotice), { forced: entry.forced });
|
|
1436
1886
|
return (id) => settledMessage(id(), roleOf(entry), entry.content);
|
|
1437
1887
|
}
|
|
1438
1888
|
function roleOf(entry) {
|
|
1439
1889
|
switch (entry.kind) {
|
|
1440
1890
|
case "system":
|
|
1441
|
-
case "compaction":
|
|
1442
1891
|
case "execution":
|
|
1443
1892
|
return "system";
|
|
1444
1893
|
case "wake_up":
|
|
@@ -1549,6 +1998,17 @@ function fromTranscript(conversationHistory, researchLog, plannerUsage, pendingT
|
|
|
1549
1998
|
return { blocks, nextId, ...transcriptAt ? { transcriptAt } : {}, ...pendingInputs.length > 0 ? { pendingInputs } : {} };
|
|
1550
1999
|
}
|
|
1551
2000
|
|
|
2001
|
+
// src/models/TaskLog.ts
|
|
2002
|
+
function secondsText(ms) {
|
|
2003
|
+
return `${Math.round(ms / 100) / 10} s`;
|
|
2004
|
+
}
|
|
2005
|
+
function scriptExitText(exit) {
|
|
2006
|
+
const seconds = secondsText(exit.durationMs);
|
|
2007
|
+
if (exit.error !== void 0) return `could not start: ${exit.error}`;
|
|
2008
|
+
const how = exit.signal ? `ended by ${exit.signal}` : `exited ${exit.code ?? "unknown"}`;
|
|
2009
|
+
return `${how} after ${seconds}${exit.timedOut ? ": the time limit ran out" : ""}`;
|
|
2010
|
+
}
|
|
2011
|
+
|
|
1552
2012
|
// src/utils/sameMessage.ts
|
|
1553
2013
|
function sameMessage(a, b) {
|
|
1554
2014
|
const bare = (value) => value.trim().replace(/\.+$/, "").trimEnd();
|
|
@@ -1785,6 +2245,43 @@ function unqueueMessage(view, { messageId }) {
|
|
|
1785
2245
|
const queued = view.queued.filter((m) => m.id !== messageId);
|
|
1786
2246
|
return queued.length === view.queued.length ? view : { ...view, queued };
|
|
1787
2247
|
}
|
|
2248
|
+
function runScriptStep(view, event) {
|
|
2249
|
+
const [command, toolLabel] = "argv" in event ? [commandLine(event.argv), "Run"] : [event.script, "Script"];
|
|
2250
|
+
return appendOutput({ ...view, working: true }, null, (id) => pendingTool(id, { tool: "bash", toolLabel, args: JSON.stringify({ command }) }));
|
|
2251
|
+
}
|
|
2252
|
+
function runningScriptStep(view) {
|
|
2253
|
+
return findLastIndex(view.blocks, (b) => b.type === "tool" && b.status === "pending" && b.tool === "bash");
|
|
2254
|
+
}
|
|
2255
|
+
function scriptStepOutput(view, { text: text2, omittedChars }) {
|
|
2256
|
+
const said = omittedChars ? `\u2026 ${omittedChars} characters omitted \u2026
|
|
2257
|
+
${text2}` : text2;
|
|
2258
|
+
const i = runningScriptStep(view);
|
|
2259
|
+
const block = view.blocks[i];
|
|
2260
|
+
if (block?.type !== "tool") return append(view, (id) => settledMessage(id, "system", said));
|
|
2261
|
+
const output = block.output + said;
|
|
2262
|
+
return { ...view, blocks: replaceAt(view.blocks, i, { ...block, output, outputLineCount: outputLines(output).length }) };
|
|
2263
|
+
}
|
|
2264
|
+
function endScriptStep(view, event) {
|
|
2265
|
+
const status = scriptExitText(event);
|
|
2266
|
+
const passed = event.code === 0 && !event.signal && event.error === void 0;
|
|
2267
|
+
const i = runningScriptStep(view);
|
|
2268
|
+
const block = view.blocks[i];
|
|
2269
|
+
const ended = { ...view, working: false };
|
|
2270
|
+
if (block?.type !== "tool") return append(ended, (id) => settledMessage(id, "system", status));
|
|
2271
|
+
const output = block.output.trim() ? `${block.output.replace(/\n+$/, "")}
|
|
2272
|
+
${status}` : status;
|
|
2273
|
+
return { ...ended, blocks: replaceAt(view.blocks, i, finishedTool(block, output, passed ? "success" : "failure")) };
|
|
2274
|
+
}
|
|
2275
|
+
function sameHandoff(a, b) {
|
|
2276
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
2277
|
+
}
|
|
2278
|
+
function showHandoff(view, { handoff }) {
|
|
2279
|
+
const i = findLastIndex(view.blocks, (b) => b.type === "handoff");
|
|
2280
|
+
const block = view.blocks[i];
|
|
2281
|
+
if (block?.type !== "handoff") return appendStep(view, (id) => ({ type: "handoff", id, handoff }));
|
|
2282
|
+
if (sameHandoff(block.handoff, handoff)) return view;
|
|
2283
|
+
return { ...view, blocks: replaceAt(view.blocks, i, { ...block, handoff }) };
|
|
2284
|
+
}
|
|
1788
2285
|
function endCall(block) {
|
|
1789
2286
|
return block.type === "tool" && block.status === "pending" ? finishedTool(block, "", "not_executed") : block;
|
|
1790
2287
|
}
|
|
@@ -1857,6 +2354,14 @@ function reduceTaskLog(view, event) {
|
|
|
1857
2354
|
return decideApproval2(view, event);
|
|
1858
2355
|
case "approval_withdrawn":
|
|
1859
2356
|
return settleApproval2(view, event.approvalId, (block) => ({ ...block, status: "withdrawn" }));
|
|
2357
|
+
case "script_run":
|
|
2358
|
+
return runScriptStep(view, event);
|
|
2359
|
+
case "script_output":
|
|
2360
|
+
return scriptStepOutput(view, event);
|
|
2361
|
+
case "script_exit":
|
|
2362
|
+
return endScriptStep(view, event);
|
|
2363
|
+
case "handoff":
|
|
2364
|
+
return showHandoff(view, event);
|
|
1860
2365
|
case "runner_ended":
|
|
1861
2366
|
return closeOpenBlocks({ ...endOpenCalls(view), working: false, streams: {}, latestText: void 0 }, "stopped");
|
|
1862
2367
|
default:
|
|
@@ -1867,6 +2372,161 @@ function replayTaskLog(events, from = EMPTY_TASK_LOG) {
|
|
|
1867
2372
|
return events.reduce(reduceTaskLog, from);
|
|
1868
2373
|
}
|
|
1869
2374
|
|
|
2375
|
+
// src/models/Handoff.ts
|
|
2376
|
+
var SEVERITY_LABEL = {
|
|
2377
|
+
blocker: "Blocker",
|
|
2378
|
+
major: "Major",
|
|
2379
|
+
minor: "Minor",
|
|
2380
|
+
info: "Info"
|
|
2381
|
+
};
|
|
2382
|
+
function severityLabel(severity) {
|
|
2383
|
+
return SEVERITY_LABEL[severity];
|
|
2384
|
+
}
|
|
2385
|
+
var SEVERITY_RANK = { blocker: 0, major: 1, minor: 2, info: 3 };
|
|
2386
|
+
function worstSeverity(findings) {
|
|
2387
|
+
let worst = null;
|
|
2388
|
+
for (const { severity } of findings) {
|
|
2389
|
+
if (worst === null || SEVERITY_RANK[severity] < SEVERITY_RANK[worst]) worst = severity;
|
|
2390
|
+
}
|
|
2391
|
+
return worst;
|
|
2392
|
+
}
|
|
2393
|
+
function handoffReferenceText(ref, taskName = () => void 0) {
|
|
2394
|
+
switch (ref.kind) {
|
|
2395
|
+
case "file":
|
|
2396
|
+
return `${ref.line !== void 0 ? `${ref.path}:${ref.line}` : ref.path}${ref.resolved === true ? "" : " (unresolved)"}`;
|
|
2397
|
+
case "commit":
|
|
2398
|
+
return `commit ${ref.sha}${ref.repo ? ` in ${ref.repo}` : ""}${ref.resolved === true ? "" : " (unresolved)"}`;
|
|
2399
|
+
case "task":
|
|
2400
|
+
return `task ${taskName(ref.task) ?? ref.task}${ref.finding ? ` ${ref.finding}` : ""}`;
|
|
2401
|
+
case "url":
|
|
2402
|
+
return ref.url;
|
|
2403
|
+
case "patch":
|
|
2404
|
+
return patchText(ref);
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
var FLAG_WORDS = {
|
|
2408
|
+
excluded: "not carried (links and submodules)",
|
|
2409
|
+
dropped: "dropped over the size cap",
|
|
2410
|
+
normalized: "normalized by git attributes"
|
|
2411
|
+
};
|
|
2412
|
+
function counted(count, noun) {
|
|
2413
|
+
return `${count} ${noun}${count === 1 ? "" : "s"}`;
|
|
2414
|
+
}
|
|
2415
|
+
function byteSize(bytes) {
|
|
2416
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
2417
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
2418
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
2419
|
+
}
|
|
2420
|
+
function patchFlagText(flag) {
|
|
2421
|
+
const more = flag.count - flag.paths.length;
|
|
2422
|
+
return `${FLAG_WORDS[flag.kind]}: ${counted(flag.count, "file")}${flag.paths.length ? ` (${flag.paths.join(", ")}${more > 0 ? `, +${more} more` : ""})` : ""}`;
|
|
2423
|
+
}
|
|
2424
|
+
function patchChangesText(patch) {
|
|
2425
|
+
const binary = patch.binary > 0 ? `, ${patch.binary} binary` : "";
|
|
2426
|
+
return `${counted(patch.files, "file")}${binary}, +${patch.added} -${patch.removed}, ${byteSize(patch.bytes)}`;
|
|
2427
|
+
}
|
|
2428
|
+
function patchLinesText(patch) {
|
|
2429
|
+
const binary = patch.binary > 0 ? `, ${patch.binary} binary` : "";
|
|
2430
|
+
return `${counted(patch.files, "file")}${binary}, +${patch.added} -${patch.removed}`;
|
|
2431
|
+
}
|
|
2432
|
+
function patchText(patch) {
|
|
2433
|
+
const flags = patch.flags?.length ? `; ${patch.flags.map(patchFlagText).join("; ")}` : "";
|
|
2434
|
+
return `patch ${patch.path} (attempt ${patch.attempt}): ${patchChangesText(patch)}, from commit ${patch.base.slice(0, 12)}${flags}`;
|
|
2435
|
+
}
|
|
2436
|
+
function patchView(patch) {
|
|
2437
|
+
return {
|
|
2438
|
+
path: patch.path,
|
|
2439
|
+
attempt: `attempt ${patch.attempt}`,
|
|
2440
|
+
changes: patchChangesText(patch),
|
|
2441
|
+
base: `from commit ${patch.base.slice(0, 12)}`,
|
|
2442
|
+
saved: `saved as ${patch.path} (attempt ${patch.attempt})`,
|
|
2443
|
+
flags: (patch.flags ?? []).map(patchFlagText),
|
|
2444
|
+
peek: `patch: ${patchLinesText(patch)}`
|
|
2445
|
+
};
|
|
2446
|
+
}
|
|
2447
|
+
function savedPatchViews(handoff) {
|
|
2448
|
+
return (handoff?.artifacts ?? []).filter((a) => a.kind === "patch").map(patchView);
|
|
2449
|
+
}
|
|
2450
|
+
function savedPatchView(handoff) {
|
|
2451
|
+
return savedPatchViews(handoff)[0] ?? null;
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
// src/conversation/labels.ts
|
|
2455
|
+
var APPROVAL_KIND_TEXT = {
|
|
2456
|
+
shell_command: "Run a command",
|
|
2457
|
+
url_fetch: "Fetch a URL",
|
|
2458
|
+
web_search: "Search the web",
|
|
2459
|
+
external_path: "Read outside the workspace",
|
|
2460
|
+
mcp_tool: "Use an MCP tool",
|
|
2461
|
+
mcp_server: "Start a project MCP server",
|
|
2462
|
+
runner_tool: "Use a tool",
|
|
2463
|
+
task_start: "Start work"
|
|
2464
|
+
};
|
|
2465
|
+
function approvalKindText(kind) {
|
|
2466
|
+
return APPROVAL_KIND_TEXT[kind];
|
|
2467
|
+
}
|
|
2468
|
+
var APPROVAL_SOURCE_TEXT = {
|
|
2469
|
+
"pre-approved": "pre-approved",
|
|
2470
|
+
remembered: "remembered",
|
|
2471
|
+
mode: "policy",
|
|
2472
|
+
"no-channel": "no approval channel",
|
|
2473
|
+
excluded: "excluded",
|
|
2474
|
+
guard: "blocked address",
|
|
2475
|
+
secret: "secret check",
|
|
2476
|
+
budget: "web-read budget",
|
|
2477
|
+
unsupported: "not usable on this runner version"
|
|
2478
|
+
};
|
|
2479
|
+
function approvalStatusText(block) {
|
|
2480
|
+
const policy = block.decidedBy && block.decidedBy !== "asked" ? APPROVAL_SOURCE_TEXT[block.decidedBy] : void 0;
|
|
2481
|
+
switch (block.status) {
|
|
2482
|
+
case "pending":
|
|
2483
|
+
return "Waiting for you";
|
|
2484
|
+
case "granted":
|
|
2485
|
+
return policy ? `Auto-approved (${policy})` : block.forTask ? "Approved for this task" : "Approved";
|
|
2486
|
+
case "denied":
|
|
2487
|
+
return policy ? `Auto-denied (${policy})` : "Denied";
|
|
2488
|
+
case "withdrawn":
|
|
2489
|
+
return "Withdrawn";
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
function planMarkerParts(block) {
|
|
2493
|
+
if (block.status === "building") return ["Building plan\u2026"];
|
|
2494
|
+
const count = block.taskCount === void 0 ? [] : [`${block.taskCount} task${block.taskCount === 1 ? "" : "s"}`];
|
|
2495
|
+
return [`Plan ${block.status}`, ...count];
|
|
2496
|
+
}
|
|
2497
|
+
function handoffFindingsText(count) {
|
|
2498
|
+
return count === 0 ? "no findings" : `${count} finding${count === 1 ? "" : "s"}`;
|
|
2499
|
+
}
|
|
2500
|
+
function firstNonBlankLine(text2) {
|
|
2501
|
+
return text2.split(/\r?\n/).map((line) => line.trim()).find((line) => line !== "") ?? "";
|
|
2502
|
+
}
|
|
2503
|
+
function handoffPeekParts(handoff) {
|
|
2504
|
+
const severity = handoff.severity ? [severityLabel(handoff.severity)] : [];
|
|
2505
|
+
const commit = handoff.commit ? [`Commit: ${handoff.commit.subject.trim()}`] : [];
|
|
2506
|
+
const summary = firstNonBlankLine(handoff.summary);
|
|
2507
|
+
return [handoffFindingsText(handoff.findingCount), ...severity, ...handoff.patch ? [handoff.patch.peek] : [], ...commit, ...summary ? [summary] : []];
|
|
2508
|
+
}
|
|
2509
|
+
var COMPACTION_STATUS_TEXT = {
|
|
2510
|
+
running: "Compacting context\u2026",
|
|
2511
|
+
stopped: "Compaction stopped \xB7 nothing changed",
|
|
2512
|
+
failed: "Compaction failed \xB7 nothing changed",
|
|
2513
|
+
paused: "Context does not fit \xB7 the planner is paused",
|
|
2514
|
+
pruned: "Older tool output trimmed from context",
|
|
2515
|
+
unchanged: "Context unchanged"
|
|
2516
|
+
};
|
|
2517
|
+
function compactedBy({ origin, trigger }) {
|
|
2518
|
+
if (origin === "native") return trigger === "auto" ? "The planner compacted its context on its own" : "The planner compacted its context";
|
|
2519
|
+
if (origin === "ordewell") return trigger === "auto" ? "Context compacted automatically" : "Context compacted";
|
|
2520
|
+
return trigger === "auto" ? "Context compacted by the runner on its own" : "Context compacted";
|
|
2521
|
+
}
|
|
2522
|
+
function compactionText(block) {
|
|
2523
|
+
const status = block.status ? COMPACTION_STATUS_TEXT[block.status] : void 0;
|
|
2524
|
+
if (status) return status;
|
|
2525
|
+
const { preTokens, postTokens } = block;
|
|
2526
|
+
const sizes = preTokens !== void 0 && postTokens !== void 0 ? ` \xB7 ${formatTokenCount(preTokens)} \u2192 ${formatTokenCount(postTokens)} tokens` : "";
|
|
2527
|
+
return `${compactedBy(block)}${sizes}`;
|
|
2528
|
+
}
|
|
2529
|
+
|
|
1870
2530
|
// src/conversation/promptHold.ts
|
|
1871
2531
|
var EMPTY_HOLD = [];
|
|
1872
2532
|
function holdPrompt(hold, text2) {
|
|
@@ -1935,9 +2595,45 @@ function stopTurn(view, gate) {
|
|
|
1935
2595
|
};
|
|
1936
2596
|
}
|
|
1937
2597
|
|
|
2598
|
+
// src/services/WakePolicy.ts
|
|
2599
|
+
var REPORT_LEAD = ", whose result you asked for, has ended.";
|
|
2600
|
+
|
|
2601
|
+
// src/conversation/wakeUp.ts
|
|
2602
|
+
var DIGEST_HEAD = "Run update:";
|
|
2603
|
+
var REPORT_PREVIEW_LINES = 8;
|
|
2604
|
+
function hasLongReport(text2) {
|
|
2605
|
+
return wakeUpItems(text2).some((item) => item.kind === "report" && item.lines.length > REPORT_PREVIEW_LINES);
|
|
2606
|
+
}
|
|
2607
|
+
var REPORTED = /^task (#[\d.]+ ".*")$/;
|
|
2608
|
+
function wakeUpItems(text2) {
|
|
2609
|
+
const items = [];
|
|
2610
|
+
let report = null;
|
|
2611
|
+
for (const line of text2.replace(/\r\n?/g, "\n").split("\n")) {
|
|
2612
|
+
if (report && (line.startsWith(" ") || line === "")) {
|
|
2613
|
+
report.lines.push(line.slice(2));
|
|
2614
|
+
continue;
|
|
2615
|
+
}
|
|
2616
|
+
report = null;
|
|
2617
|
+
if (line === DIGEST_HEAD && items.length === 0) continue;
|
|
2618
|
+
const bullet = line.startsWith("- ") ? line.slice(2) : null;
|
|
2619
|
+
const at = bullet?.indexOf(REPORT_LEAD) ?? -1;
|
|
2620
|
+
const task = bullet === null || at < 0 ? void 0 : REPORTED.exec(bullet.slice(0, at))?.[1];
|
|
2621
|
+
if (bullet !== null && task !== void 0) {
|
|
2622
|
+
report = { kind: "report", task, lines: [bullet.slice(at + REPORT_LEAD.length).trimStart()] };
|
|
2623
|
+
items.push(report);
|
|
2624
|
+
} else items.push({ kind: "line", text: line });
|
|
2625
|
+
}
|
|
2626
|
+
return items.map((item) => item.kind === "report" ? { ...item, lines: trimmed(item.lines) } : item);
|
|
2627
|
+
}
|
|
2628
|
+
function trimmed(lines2) {
|
|
2629
|
+
const kept = [...lines2];
|
|
2630
|
+
while (kept.length > 1 && kept[kept.length - 1].trim() === "") kept.pop();
|
|
2631
|
+
return kept;
|
|
2632
|
+
}
|
|
2633
|
+
|
|
1938
2634
|
// src/conversation/detail.ts
|
|
1939
2635
|
function hasHiddenDetail(blocks) {
|
|
1940
|
-
return blocks.some((b) => b.type === "thinking" || b.type === "tool" || b.type === "subagent" || b.type === "runner_loaded");
|
|
2636
|
+
return blocks.some((b) => b.type === "thinking" || b.type === "tool" || b.type === "subagent" || b.type === "runner_loaded" || b.type === "message" && b.role === "wake_up" && hasLongReport(b.text) || b.type === "handoff" && (b.handoff.report?.replace(/\n+$/, "").split("\n").length ?? 0) > REPORT_PREVIEW_LINES);
|
|
1941
2637
|
}
|
|
1942
2638
|
|
|
1943
2639
|
// src/conversation/notices.ts
|
|
@@ -2029,8 +2725,26 @@ function followTaskLog(state, input) {
|
|
|
2029
2725
|
}
|
|
2030
2726
|
}
|
|
2031
2727
|
|
|
2728
|
+
// src/taskRow/editability.ts
|
|
2729
|
+
function editableWhileRunning(task, run) {
|
|
2730
|
+
if (!NOT_STARTED.has(task.status)) return false;
|
|
2731
|
+
if (!run) return true;
|
|
2732
|
+
if ((run.awaitingApproval ?? 0) > 0) return false;
|
|
2733
|
+
return run.isolation?.state !== "active" && run.isolation?.state !== "repairing";
|
|
2734
|
+
}
|
|
2735
|
+
function runHoldsEditors(isExecuting, activity) {
|
|
2736
|
+
return isExecuting || activity.starting || activity.advancesOnItsOwn;
|
|
2737
|
+
}
|
|
2738
|
+
function cardEditable(runHolds, task, run) {
|
|
2739
|
+
return !runHolds || editableWhileRunning(task, run);
|
|
2740
|
+
}
|
|
2741
|
+
function cardEditNeedsUnstarted(runHolds, task) {
|
|
2742
|
+
return runHolds || NOT_STARTED.has(task.status);
|
|
2743
|
+
}
|
|
2744
|
+
|
|
2032
2745
|
// src/taskRow/actions.ts
|
|
2033
2746
|
var UNSTARTED_AI = (t) => t.type === "ai" && NOT_STARTED.has(t.status);
|
|
2747
|
+
var UNSTARTED_RUN = (t) => t.type !== "user" && NOT_STARTED.has(t.status);
|
|
2034
2748
|
var QUEUED = (t) => t.status === "pending" || t.status === "approved";
|
|
2035
2749
|
var PAUSED = (t) => t.status === "awaiting_user" && t.awaitingReason === "usage-limit";
|
|
2036
2750
|
var RESUMES = (t) => PAUSED(t) && !!t.resumeAt;
|
|
@@ -2041,7 +2755,7 @@ var TASK_RULES = [
|
|
|
2041
2755
|
["resume-now", RESUMABLE],
|
|
2042
2756
|
["cancel-resume", RESUMES],
|
|
2043
2757
|
["skip", HOLDS_UP],
|
|
2044
|
-
["force-start",
|
|
2758
|
+
["force-start", UNSTARTED_RUN],
|
|
2045
2759
|
// A manual task has no runner to report it done, so it can always be marked;
|
|
2046
2760
|
// a skipped one's kept work can still be vouched for.
|
|
2047
2761
|
["complete", (t) => t.status === "awaiting_user" || t.status === "skipped" || t.type === "user"],
|
|
@@ -2064,16 +2778,43 @@ function taskRowActions(task, placement = "task") {
|
|
|
2064
2778
|
function markAction(task) {
|
|
2065
2779
|
return task.status === "completed" ? "uncomplete" : "complete";
|
|
2066
2780
|
}
|
|
2067
|
-
function
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2781
|
+
function taskActionCommand(action, task, reason) {
|
|
2782
|
+
switch (action) {
|
|
2783
|
+
case "run":
|
|
2784
|
+
return { command: "run_task", task };
|
|
2785
|
+
case "retry":
|
|
2786
|
+
return { command: "retry_task", task, from: "fresh" };
|
|
2787
|
+
case "cancel":
|
|
2788
|
+
return { command: "stop_task", task };
|
|
2789
|
+
// The user says the limit has reset early: the task resumes in its saved session now.
|
|
2790
|
+
case "resume-now":
|
|
2791
|
+
return { command: "retry_task", task, from: "session" };
|
|
2792
|
+
case "cancel-resume":
|
|
2793
|
+
return { command: "cancel_resume", task };
|
|
2794
|
+
case "skip":
|
|
2795
|
+
return { command: "skip_task", task, ...reason ? { reason } : {} };
|
|
2796
|
+
case "force-start":
|
|
2797
|
+
return { command: "force_start", task };
|
|
2798
|
+
case "complete":
|
|
2799
|
+
return { command: "mark_complete", task };
|
|
2800
|
+
case "uncomplete":
|
|
2801
|
+
return { command: "mark_not_done", task };
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
function overrideNeedsConfirmation(task) {
|
|
2805
|
+
return task.type !== "user";
|
|
2806
|
+
}
|
|
2807
|
+
function confirmedOverride(command, seen) {
|
|
2808
|
+
return command.command === "mark_complete" || command.command === "mark_not_done" ? { ...command, confirmed: true, ...seen ? { seen } : {} } : command;
|
|
2071
2809
|
}
|
|
2072
2810
|
function opsEditable(task) {
|
|
2073
2811
|
return UNSTARTED_AI(task);
|
|
2074
2812
|
}
|
|
2075
2813
|
|
|
2076
2814
|
// src/services/AssignmentScope.ts
|
|
2815
|
+
function assignedRunnerOf(task) {
|
|
2816
|
+
return task.type === "ai" ? task.assignedRunner || void 0 : void 0;
|
|
2817
|
+
}
|
|
2077
2818
|
var OVER = /* @__PURE__ */ new Set(["completed", "skipped"]);
|
|
2078
2819
|
function assignmentIssue(task, scope) {
|
|
2079
2820
|
if (task.type !== "ai" || OVER.has(task.status)) return null;
|
|
@@ -2098,15 +2839,105 @@ function assignmentConsequence({ status, awaitingReason, resumeAt }) {
|
|
|
2098
2839
|
return "runs as assigned unless moved";
|
|
2099
2840
|
}
|
|
2100
2841
|
|
|
2842
|
+
// src/taskRow/loop.ts
|
|
2843
|
+
function loopCheckState(check, goesOn) {
|
|
2844
|
+
switch (check.status) {
|
|
2845
|
+
case "completed":
|
|
2846
|
+
return "passed";
|
|
2847
|
+
case "in_progress":
|
|
2848
|
+
case "running":
|
|
2849
|
+
case "awaiting_user":
|
|
2850
|
+
return "running";
|
|
2851
|
+
case "failed":
|
|
2852
|
+
if (betweenRounds(check)) return goesOn ? "repeating" : "stopped";
|
|
2853
|
+
return outOfRounds(check) ? "out-of-rounds" : "failed";
|
|
2854
|
+
case "pending":
|
|
2855
|
+
case "approved":
|
|
2856
|
+
case "blocked":
|
|
2857
|
+
return loopRoundOf(check) > 1 ? "running" : "waiting";
|
|
2858
|
+
default:
|
|
2859
|
+
return "ended";
|
|
2860
|
+
}
|
|
2861
|
+
}
|
|
2862
|
+
var roundsOf = (count) => `${count} round${count === 1 ? "" : "s"}`;
|
|
2863
|
+
function loopCheckLabel(check, state) {
|
|
2864
|
+
const round = loopRoundOf(check);
|
|
2865
|
+
const of = `round ${round} of ${check.loop.maxRounds}`;
|
|
2866
|
+
switch (state) {
|
|
2867
|
+
case "waiting":
|
|
2868
|
+
case "ended":
|
|
2869
|
+
return `Loop of up to ${roundsOf(check.loop.maxRounds)}`;
|
|
2870
|
+
case "running":
|
|
2871
|
+
return `Round ${round} of ${check.loop.maxRounds}`;
|
|
2872
|
+
case "repeating":
|
|
2873
|
+
return `Round ${round} of ${check.loop.maxRounds}: fixing`;
|
|
2874
|
+
case "stopped":
|
|
2875
|
+
return `Stopped in ${of}`;
|
|
2876
|
+
case "failed":
|
|
2877
|
+
return `Failed in ${of}`;
|
|
2878
|
+
case "passed":
|
|
2879
|
+
return `${check.verdict?.outcome === "pass" ? "Passed" : "Completed"} in ${of}`;
|
|
2880
|
+
case "out-of-rounds":
|
|
2881
|
+
return `Failed after ${roundsOf(round)}`;
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
function loopBodyRound(check, bodyStatus) {
|
|
2885
|
+
if (!["in_progress", "running", "awaiting_user", "completed", "failed"].includes(bodyStatus)) return null;
|
|
2886
|
+
return check.status === "failed" ? loopRoundOf(check) : Math.max(1, loopRoundOf(check) - 1);
|
|
2887
|
+
}
|
|
2888
|
+
function loopBodyLabel(checkRef, round, maxRounds) {
|
|
2889
|
+
return `Runs if ${checkRef} fails${round === null ? "" : ` (round ${round} of ${maxRounds})`}`;
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2101
2892
|
// src/taskRow/view.ts
|
|
2893
|
+
function taskChatAvailable(task) {
|
|
2894
|
+
if (task.type === "user") return false;
|
|
2895
|
+
return task.attempted === true || (task.attemptUsage?.length ?? 0) > 0;
|
|
2896
|
+
}
|
|
2102
2897
|
var refsOf = (ids, tasks) => ids.map((id) => tasks?.length ? taskRefOf(tasks, id) ?? id : id);
|
|
2103
2898
|
var namedRepo = (repo) => repo && repo !== "." ? repo : null;
|
|
2104
2899
|
var namedRepos = (repos) => (repos ?? []).filter((r) => r !== ".");
|
|
2105
2900
|
var filesOf = (files) => files?.length ? capConflictFiles(files) : null;
|
|
2106
|
-
var asBlocking = (tasks) => tasks.map((t) => ({
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2901
|
+
var asBlocking = (tasks) => tasks.map((t) => ({
|
|
2902
|
+
id: t.id,
|
|
2903
|
+
status: t.status ?? "",
|
|
2904
|
+
dependencies: t.dependencies ?? [],
|
|
2905
|
+
subtasks: asBlocking(t.subtasks ?? []),
|
|
2906
|
+
...t.loop ? { loop: t.loop, loopRound: t.loopRound, ...t.verdict ? { verdict: t.verdict } : {} } : {}
|
|
2907
|
+
}));
|
|
2908
|
+
function blockedBy(task, reading) {
|
|
2909
|
+
if (!reading || !NOT_STARTED.has(task.status)) return [];
|
|
2910
|
+
return reading.blocks.blocked.get(task.id) ?? [];
|
|
2911
|
+
}
|
|
2912
|
+
function loopOf(task, reading) {
|
|
2913
|
+
const tasks = reading?.tasks;
|
|
2914
|
+
if (task.loop) {
|
|
2915
|
+
const check2 = { id: task.id, status: task.status, loop: task.loop, loopRound: task.loopRound, ...task.verdict ? { verdict: task.verdict } : {} };
|
|
2916
|
+
const state = loopCheckState(check2, reading ? reading.blocks.repeating.has(task.id) : true);
|
|
2917
|
+
const body = refsOf(task.loop.body, tasks);
|
|
2918
|
+
const halted = reading ? task.loop.body.filter((id) => {
|
|
2919
|
+
const status = reading.tasks.find((t) => t.id === id)?.status;
|
|
2920
|
+
return status === "failed" || status === "skipped" || reading.blocks.blocked.has(id);
|
|
2921
|
+
}) : [];
|
|
2922
|
+
const detail = state === "repeating" ? `Found problems: runs again once ${body.join(", ")} ${body.length === 1 ? "has" : "have"} landed` : state === "stopped" ? `${refsOf(halted, tasks).join(", ")} did not pass: retry ${halted.length === 1 ? "it" : "them"} and the loop goes on` : null;
|
|
2923
|
+
return { role: "check", state, label: loopCheckLabel(check2, state), detail, body };
|
|
2924
|
+
}
|
|
2925
|
+
const owner = reading && checkOf(reading.tasks, task.id);
|
|
2926
|
+
if (!owner?.loop) return null;
|
|
2927
|
+
const [check] = refsOf([owner.id], tasks);
|
|
2928
|
+
const round = loopBodyRound({ status: owner.status ?? "", loopRound: owner.loopRound }, task.status);
|
|
2929
|
+
return { role: "body", label: loopBodyLabel(check, round, owner.loop.maxRounds), check, round };
|
|
2930
|
+
}
|
|
2931
|
+
function handoffOf(task) {
|
|
2932
|
+
if (!task.handoff) return null;
|
|
2933
|
+
const patch = savedPatchView(task.handoff);
|
|
2934
|
+
return {
|
|
2935
|
+
...task.handoff.commit ? { commit: task.handoff.commit } : {},
|
|
2936
|
+
summary: task.handoff.summary,
|
|
2937
|
+
findingCount: task.handoff.findingCount,
|
|
2938
|
+
severity: worstSeverity(task.handoff.findings),
|
|
2939
|
+
...patch ? { patch } : {}
|
|
2940
|
+
};
|
|
2110
2941
|
}
|
|
2111
2942
|
function flagOf(task, scope) {
|
|
2112
2943
|
const issue = scope && task.assignedRunner !== void 0 ? assignmentIssue({ ...task, assignedRunner: task.assignedRunner }, scope) : null;
|
|
@@ -2117,17 +2948,26 @@ function taskRowView(task, context = {}) {
|
|
|
2117
2948
|
const isolation = task.isolation && task.isolation.state !== "none" ? task.isolation : null;
|
|
2118
2949
|
const spend = taskUsageSummary(task);
|
|
2119
2950
|
const latestAttempt = task.attemptUsage?.at(-1);
|
|
2120
|
-
const
|
|
2951
|
+
const tasks = task.loop ? context.tasks?.map((t) => t.id === task.id ? { ...t, status: task.status, loopRound: task.loopRound, verdict: task.verdict } : t) : context.tasks;
|
|
2952
|
+
const reading = tasks?.length ? { tasks, blocks: planBlocks(asBlocking(tasks)) } : null;
|
|
2953
|
+
const blocked = blockedBy(task, reading);
|
|
2954
|
+
const loop = loopOf(task, reading);
|
|
2121
2955
|
return {
|
|
2122
2956
|
usage: latestAttempt ? { totals: spend.totals, ...spend.contextFill ? { contextFill: spend.contextFill } : {}, attempt: latestAttempt.attempt } : task.type === "ai" && ["completed", "failed", "in_progress", "awaiting_user"].includes(task.status) ? { totals: {}, attempt: 0 } : null,
|
|
2123
2957
|
orderLabel: taskOrderLabel(task, parent),
|
|
2124
|
-
kind: task.type === "user" ? "user" : task.ops ? "ops" : "ai",
|
|
2125
|
-
|
|
2958
|
+
kind: task.type === "user" ? "user" : task.type === "script" ? "script" : task.ops ? "ops" : "ai",
|
|
2959
|
+
// Between rounds a check waits for its fix as a task waits for its dependencies, and is no failure (ADR-0045 L3).
|
|
2960
|
+
status: blocked.length > 0 ? "blocked" : loop?.role === "check" && loop.state === "repeating" ? "todo" : taskStatusKind(task),
|
|
2126
2961
|
running: isTaskRunning(task),
|
|
2962
|
+
chatAvailable: taskChatAvailable(task),
|
|
2127
2963
|
awaiting: awaitingLabel(task),
|
|
2128
2964
|
resumes: resumeLabel(task, context.now),
|
|
2129
2965
|
approvals: approvalLabel(task.awaitingApproval),
|
|
2966
|
+
receipt: receiptLabel(task),
|
|
2130
2967
|
waitsForApproval: task.waitsForApproval && NOT_STARTED.has(task.status) ? WAITS_FOR_APPROVAL_LABEL : null,
|
|
2968
|
+
holdReason: task.holdReason?.trim() || null,
|
|
2969
|
+
research: task.type === "ai" && task.research === true,
|
|
2970
|
+
runner: assignedRunnerOf(task) ?? null,
|
|
2131
2971
|
...flagOf(task, context.runnerScope),
|
|
2132
2972
|
autonomous: task.type === "ai" && context.modes?.find((m) => m.id === task.taskMode)?.autonomous === true,
|
|
2133
2973
|
repos: namedRepos(isolation?.repos),
|
|
@@ -2139,6 +2979,8 @@ function taskRowView(task, context = {}) {
|
|
|
2139
2979
|
skipReason: task.status === "skipped" ? task.skipReason ?? null : null,
|
|
2140
2980
|
opsChangedFiles: task.status === "awaiting_user" && task.awaitingReason === "files-changed",
|
|
2141
2981
|
forcedPastGate: task.forcedPastGate?.length ? task.forcedPastGate : null,
|
|
2982
|
+
loop,
|
|
2983
|
+
handoff: handoffOf(task),
|
|
2142
2984
|
dependencies: refsOf(task.dependencies, context.tasks),
|
|
2143
2985
|
// A block read off the plan offers what a stored one does: Skip among them.
|
|
2144
2986
|
actions: taskRowActions(blocked.length > 0 ? { ...task, status: "blocked" } : task, context.placement ?? (parent ? "subtask" : "task")),
|
|
@@ -2146,6 +2988,91 @@ function taskRowView(task, context = {}) {
|
|
|
2146
2988
|
opsEditable: opsEditable(task)
|
|
2147
2989
|
};
|
|
2148
2990
|
}
|
|
2991
|
+
function loopRowParts(loop) {
|
|
2992
|
+
if (loop.role === "body") return [loop.label];
|
|
2993
|
+
const runs = loop.state === "waiting" || loop.state === "running" ? `Runs ${loop.body.join(", ")} if it fails` : null;
|
|
2994
|
+
return [loop.label, loop.detail ?? runs].filter((part) => part !== null);
|
|
2995
|
+
}
|
|
2996
|
+
|
|
2997
|
+
// src/taskRow/logHeader.ts
|
|
2998
|
+
var STATE_LABEL = {
|
|
2999
|
+
done: "Done",
|
|
3000
|
+
failed: "Failed",
|
|
3001
|
+
running: "Running",
|
|
3002
|
+
quiet: "Stalled",
|
|
3003
|
+
awaiting: "Waiting for your input",
|
|
3004
|
+
blocked: "Blocked",
|
|
3005
|
+
skipped: "Skipped",
|
|
3006
|
+
todo: "To do"
|
|
3007
|
+
};
|
|
3008
|
+
function stateOf(task, follow, approvals) {
|
|
3009
|
+
if (task.status === "completed") return { kind: "done", label: STATE_LABEL.done };
|
|
3010
|
+
if (task.status === "failed") return { kind: "failed", label: STATE_LABEL.failed };
|
|
3011
|
+
if (task.status === "skipped") return { kind: "skipped", label: skippedLabel(task.skipReason ?? null) };
|
|
3012
|
+
const approval = approvalLabel(approvals);
|
|
3013
|
+
if (approval) return { kind: "approval", label: approval };
|
|
3014
|
+
if (follow.view.working) return { kind: "running", label: "Working" };
|
|
3015
|
+
if (task.status === "awaiting_user") {
|
|
3016
|
+
return { kind: task.awaitingReason === "checkpoint" ? "checkpoint" : "awaiting", label: awaitingLabel(task) ?? STATE_LABEL.awaiting };
|
|
3017
|
+
}
|
|
3018
|
+
if (task.status === "blocked") return { kind: "blocked", label: STATE_LABEL.blocked };
|
|
3019
|
+
if (follow.view.lastTurnEnd === "failed") return { kind: "failed", label: "Turn failed" };
|
|
3020
|
+
if (follow.view.lastTurnEnd === "interrupted") return { kind: "todo", label: "Interrupted" };
|
|
3021
|
+
const kind = taskStatusKind(task);
|
|
3022
|
+
return { kind, label: STATE_LABEL[kind] };
|
|
3023
|
+
}
|
|
3024
|
+
function loggedUsage(view) {
|
|
3025
|
+
const last = view.blocks.at(-1);
|
|
3026
|
+
if (last?.type !== "usage") return null;
|
|
3027
|
+
const text2 = usageText(last);
|
|
3028
|
+
return text2 ? { text: text2, ...last.contextFill ? { contextFill: last.contextFill } : {} } : null;
|
|
3029
|
+
}
|
|
3030
|
+
function usageRow(text2, contextFill) {
|
|
3031
|
+
return text2 ? { text: text2, ...contextFill ? { contextFill } : {} } : null;
|
|
3032
|
+
}
|
|
3033
|
+
function usageOf(task, follow, attempt) {
|
|
3034
|
+
const attempts = task.attemptUsage ?? [];
|
|
3035
|
+
const latest = attempts.at(-1);
|
|
3036
|
+
const past = attempt > 0 && attempt !== latest?.attempt;
|
|
3037
|
+
const total = taskUsageSummary(task);
|
|
3038
|
+
const totalText = usageText(total);
|
|
3039
|
+
if (!past) {
|
|
3040
|
+
if (attempts.length > 0) return totalText ? [{ text: totalText, ...total.contextFill ? { contextFill: total.contextFill } : {} }] : [];
|
|
3041
|
+
const logged = loggedUsage(follow.view);
|
|
3042
|
+
return logged ? [logged] : [];
|
|
3043
|
+
}
|
|
3044
|
+
const onScreen = attempts.find((a) => a.attempt === attempt);
|
|
3045
|
+
const own = onScreen ? usageRow(usageText({ totals: onScreen.totals, contextFill: plannerContextFill(onScreen) }), plannerContextFill(onScreen)) : loggedUsage(follow.view);
|
|
3046
|
+
const rows = [];
|
|
3047
|
+
if (own) rows.push(onScreen ? { ...own, text: `attempt ${onScreen.attempt}: ${own.text}` } : own);
|
|
3048
|
+
if (totalText) rows.push({ text: `task total: ${totalText}`, ...total.contextFill ? { contextFill: total.contextFill } : {} });
|
|
3049
|
+
return rows;
|
|
3050
|
+
}
|
|
3051
|
+
function taskLogCommands(view) {
|
|
3052
|
+
for (let i = view.blocks.length - 1; i >= 0; i--) {
|
|
3053
|
+
const block = view.blocks[i];
|
|
3054
|
+
if (block.type === "runner_loaded") return block.commands;
|
|
3055
|
+
}
|
|
3056
|
+
return [];
|
|
3057
|
+
}
|
|
3058
|
+
function logContinues(task) {
|
|
3059
|
+
const finished = task.status === "completed" || task.status === "failed" || task.status === "skipped";
|
|
3060
|
+
return task.continuable === true && (finished || task.status === "awaiting_user" && task.awaitingReason === "usage-limit");
|
|
3061
|
+
}
|
|
3062
|
+
function logResumes(task) {
|
|
3063
|
+
return task.continuable === true && task.status === "awaiting_user" && task.awaitingReason === "usage-limit";
|
|
3064
|
+
}
|
|
3065
|
+
function taskLogHeaderView(task, follow, approvals, attempt) {
|
|
3066
|
+
return {
|
|
3067
|
+
state: stateOf(task, follow, approvals),
|
|
3068
|
+
resumes: resumeLabel(task),
|
|
3069
|
+
continuable: logContinues(task),
|
|
3070
|
+
resumable: logResumes(task),
|
|
3071
|
+
working: follow.view.working,
|
|
3072
|
+
usage: usageOf(task, follow, attempt),
|
|
3073
|
+
commands: taskLogCommands(follow.view)
|
|
3074
|
+
};
|
|
3075
|
+
}
|
|
2149
3076
|
|
|
2150
3077
|
// src/taskRow/texts.ts
|
|
2151
3078
|
function titledRefs(ids, tasks) {
|
|
@@ -2156,17 +3083,82 @@ function dependentsNotice(dependents, tasks = []) {
|
|
|
2156
3083
|
const subject = dependents.length === 1 ? "1 task depends" : `${dependents.length} tasks depend`;
|
|
2157
3084
|
return `${subject} on it and will lose that dependency: ${dependents.map((t) => (t.id ? titledTaskRefOf(tasks, t.id) : void 0) ?? titledTaskRef(t)).join(", ")}.`;
|
|
2158
3085
|
}
|
|
3086
|
+
function loopLeaveNotice(tasks, taskId) {
|
|
3087
|
+
const check = checkOf(tasks, taskId);
|
|
3088
|
+
if (!check?.loop) return null;
|
|
3089
|
+
const [named] = titledRefs([check.id], tasks);
|
|
3090
|
+
return check.loop.body.length > 1 ? `It is in the body of ${named}'s loop, and leaves it.` : `It is the whole body of ${named}'s loop, so the loop is dropped with it.`;
|
|
3091
|
+
}
|
|
2159
3092
|
function pastGateConfirmation(unmerged, subject = "This task") {
|
|
2160
3093
|
return `${subject} waits for Merge all: the work of ${unmerged.join(", ")} is not merged into your branch yet, so it would act without it. Starting it now is kept on the task.`;
|
|
2161
3094
|
}
|
|
3095
|
+
function manualOverrideConfirmation(direction, label) {
|
|
3096
|
+
return direction === "complete" ? `Mark ${label} complete? You vouch for its work although no passing verdict said it was done. Any changes it kept may land in your run, and the tasks that depend on it are released to start on a future run.` : `Mark ${label} not done? This clears its completion and holds it until you retry or start it. The work it did stays where it is, and tasks that already ran on its result are not undone.`;
|
|
3097
|
+
}
|
|
3098
|
+
function manualOverrideStopFirst(direction, label) {
|
|
3099
|
+
return `${label.charAt(0).toUpperCase()}${label.slice(1)} cannot be marked ${direction === "complete" ? "complete" : "not done"} while the plan is starting, running or waiting to resume, because the run could still move it on. Stop the run first, and mark it once everything has stopped.`;
|
|
3100
|
+
}
|
|
2162
3101
|
function newTaskFields(title, prompt) {
|
|
2163
3102
|
const name = title.trim();
|
|
2164
3103
|
if (!name) return null;
|
|
2165
3104
|
return { title: name, description: name, prompt: prompt?.trim() ? prompt : name, type: "ai" };
|
|
2166
3105
|
}
|
|
3106
|
+
function noExecutorRefusal(type, manual, script) {
|
|
3107
|
+
return type === "script" ? `A script task runs no model, so ${script}` : `Manual tasks do not run an executor, so ${manual}`;
|
|
3108
|
+
}
|
|
2167
3109
|
|
|
2168
3110
|
// src/services/runView.ts
|
|
2169
|
-
var EMPTY_RUN = { tasks: [], runMerge: null, handoff: null, mergeResult: null };
|
|
3111
|
+
var EMPTY_RUN = { tasks: [], runMerge: null, handoff: null, mergeResult: null, activity: IDLE_ACTIVITY };
|
|
3112
|
+
|
|
3113
|
+
// src/models/AccountLimits.ts
|
|
3114
|
+
function hasAccountLimits(snapshot) {
|
|
3115
|
+
return (snapshot.status === "fresh" || snapshot.status === "stale") && snapshot.buckets.some((bucket) => bucket.windows.some((window) => window.usedPercent !== null));
|
|
3116
|
+
}
|
|
3117
|
+
function accountLimitsOf(snapshots, runner, model) {
|
|
3118
|
+
if (!runner) return [];
|
|
3119
|
+
return snapshots.filter((snapshot) => snapshot.harness === runner && (snapshot.provider === void 0 || (model?.startsWith(`${snapshot.provider}/`) ?? false)));
|
|
3120
|
+
}
|
|
3121
|
+
var WEEK = 7 * 24 * 60;
|
|
3122
|
+
var DAY = 24 * 60;
|
|
3123
|
+
var SLOT_LABELS = { primary: "Primary", secondary: "Secondary", tertiary: "Tertiary" };
|
|
3124
|
+
function accountLimitWindowLabel(window) {
|
|
3125
|
+
if (window.period === "month") return "Monthly";
|
|
3126
|
+
const minutes = window.durationMinutes;
|
|
3127
|
+
if (minutes === null) return SLOT_LABELS[window.slot];
|
|
3128
|
+
if (minutes === WEEK) return "Weekly";
|
|
3129
|
+
if (minutes === DAY) return "Daily";
|
|
3130
|
+
if (minutes % WEEK === 0) return `${minutes / WEEK}w`;
|
|
3131
|
+
if (minutes % DAY === 0) return `${minutes / DAY}d`;
|
|
3132
|
+
if (minutes % 60 === 0) return `${minutes / 60}h`;
|
|
3133
|
+
return `${minutes}m`;
|
|
3134
|
+
}
|
|
3135
|
+
function accountLimitPercentText(percent) {
|
|
3136
|
+
const shown = percent > 0 && percent < 1 ? "<1" : String(Math.round(percent));
|
|
3137
|
+
return `${shown}% used`;
|
|
3138
|
+
}
|
|
3139
|
+
function accountLimitsText(snapshot) {
|
|
3140
|
+
if (!hasAccountLimits(snapshot)) return null;
|
|
3141
|
+
const parts = snapshot.buckets.flatMap((bucket, index) => {
|
|
3142
|
+
const windows = bucket.windows.flatMap((window) => window.usedPercent === null ? [] : [`${accountLimitWindowLabel(window)} ${accountLimitPercentText(window.usedPercent)}`]);
|
|
3143
|
+
if (windows.length === 0) return [];
|
|
3144
|
+
return index === 0 ? windows : [`${bucket.name ?? bucket.id} ${windows.join(" \xB7 ")}`];
|
|
3145
|
+
});
|
|
3146
|
+
return `${snapshot.label} ${parts.join(" \xB7 ")}${snapshot.status === "stale" ? " (stale)" : ""}`;
|
|
3147
|
+
}
|
|
3148
|
+
function accountLimitsTitle(snapshot, time) {
|
|
3149
|
+
const lines2 = snapshot.buckets.flatMap((bucket, index) => bucket.windows.map((window) => {
|
|
3150
|
+
const owner = index === 0 ? snapshot.label : `${snapshot.label} ${bucket.name ?? bucket.id}`;
|
|
3151
|
+
const share = window.usedPercent === null ? "not reported" : `${window.usedPercent}% used`;
|
|
3152
|
+
const reset = window.resetsAt ? `, resets ${time(new Date(window.resetsAt))}` : "";
|
|
3153
|
+
return `${owner} ${accountLimitWindowLabel(window)}: ${share}${reset}`;
|
|
3154
|
+
}));
|
|
3155
|
+
if (snapshot.plan) lines2.push(`Plan: ${snapshot.plan}`);
|
|
3156
|
+
if (snapshot.readAt) {
|
|
3157
|
+
const read = time(new Date(snapshot.readAt));
|
|
3158
|
+
lines2.push(snapshot.status === "stale" ? `Last read ${read}; not current, a newer read is pending or failed` : `Read ${read}`);
|
|
3159
|
+
}
|
|
3160
|
+
return lines2.join("\n");
|
|
3161
|
+
}
|
|
2170
3162
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2171
3163
|
0 && (module.exports = {
|
|
2172
3164
|
CHECKPOINT_TRUNCATE_LENGTH,
|
|
@@ -2176,27 +3168,47 @@ var EMPTY_RUN = { tasks: [], runMerge: null, handoff: null, mergeResult: null };
|
|
|
2176
3168
|
EMPTY_RUN,
|
|
2177
3169
|
EMPTY_TASK_LOG,
|
|
2178
3170
|
EMPTY_TASK_LOG_FOLLOW,
|
|
3171
|
+
IDLE_ACTIVITY,
|
|
2179
3172
|
NOT_STARTED,
|
|
2180
3173
|
NO_TURN,
|
|
3174
|
+
PLANNED_WITH_LABEL,
|
|
3175
|
+
REPORT_PREVIEW_LINES,
|
|
3176
|
+
SEVERITY_LABEL,
|
|
2181
3177
|
SUPERVISOR_LEVEL_LABEL,
|
|
2182
3178
|
WAITS_FOR_APPROVAL_LABEL,
|
|
3179
|
+
accountLimitPercentText,
|
|
3180
|
+
accountLimitWindowLabel,
|
|
3181
|
+
accountLimitsOf,
|
|
3182
|
+
accountLimitsText,
|
|
3183
|
+
accountLimitsTitle,
|
|
2183
3184
|
addAttemptUsage,
|
|
2184
3185
|
addModelUsage,
|
|
2185
3186
|
addPlannerUsage,
|
|
2186
3187
|
addUsage,
|
|
2187
3188
|
addUsageTotals,
|
|
2188
3189
|
aheadOfDraft,
|
|
3190
|
+
approvalKindText,
|
|
2189
3191
|
approvalLabel,
|
|
3192
|
+
approvalStatusText,
|
|
3193
|
+
assignedRunnerOf,
|
|
2190
3194
|
assignmentIssue,
|
|
2191
3195
|
assignmentIssueText,
|
|
2192
3196
|
awaitingLabel,
|
|
3197
|
+
betweenRounds,
|
|
3198
|
+
blockNotice,
|
|
3199
|
+
blockNotices,
|
|
2193
3200
|
blockedByLabel,
|
|
2194
3201
|
blockingFailures,
|
|
2195
3202
|
canMergeTasks,
|
|
2196
3203
|
canSetDependencies,
|
|
2197
3204
|
canSplitTask,
|
|
2198
3205
|
capConflictFiles,
|
|
3206
|
+
cardEditNeedsUnstarted,
|
|
3207
|
+
cardEditable,
|
|
3208
|
+
checkOf,
|
|
2199
3209
|
clampThinkingEffort,
|
|
3210
|
+
compactionText,
|
|
3211
|
+
confirmedOverride,
|
|
2200
3212
|
contextMeterText,
|
|
2201
3213
|
contextMeterTitle,
|
|
2202
3214
|
costText,
|
|
@@ -2204,47 +3216,89 @@ var EMPTY_RUN = { tasks: [], runMerge: null, handoff: null, mergeResult: null };
|
|
|
2204
3216
|
dependencyCandidates,
|
|
2205
3217
|
dependentsNotice,
|
|
2206
3218
|
dependentsOf,
|
|
3219
|
+
describeIsolationPreview,
|
|
3220
|
+
describeMergeResult,
|
|
2207
3221
|
diffRows,
|
|
2208
3222
|
diffStat,
|
|
2209
3223
|
diffSummary,
|
|
2210
3224
|
drainNext,
|
|
3225
|
+
editableWhileRunning,
|
|
2211
3226
|
endAttemptUsage,
|
|
2212
3227
|
followTaskLog,
|
|
2213
3228
|
followTurn,
|
|
2214
3229
|
formatCost,
|
|
2215
3230
|
formatTokenCount,
|
|
2216
3231
|
fromTranscript,
|
|
3232
|
+
handoffBaseLine,
|
|
3233
|
+
handoffBranchName,
|
|
3234
|
+
handoffFindingsText,
|
|
3235
|
+
handoffPeekParts,
|
|
3236
|
+
handoffReferenceText,
|
|
3237
|
+
hasAccountLimits,
|
|
2217
3238
|
hasHiddenDetail,
|
|
3239
|
+
hasLongReport,
|
|
3240
|
+
holdLabel,
|
|
2218
3241
|
holdPrompt,
|
|
3242
|
+
isFileEditTool,
|
|
2219
3243
|
isMeasured,
|
|
3244
|
+
isRepoGroup,
|
|
2220
3245
|
isTaskRunning,
|
|
2221
3246
|
loadedSkillTokens,
|
|
3247
|
+
logContinues,
|
|
3248
|
+
logResumes,
|
|
3249
|
+
loopBodyLabel,
|
|
3250
|
+
loopBodyRound,
|
|
3251
|
+
loopCheckLabel,
|
|
3252
|
+
loopCheckState,
|
|
3253
|
+
loopLeaveNotice,
|
|
3254
|
+
loopRemovalRefusal,
|
|
3255
|
+
loopRoundOf,
|
|
3256
|
+
loopRowParts,
|
|
3257
|
+
manualOverrideConfirmation,
|
|
3258
|
+
manualOverrideStopFirst,
|
|
2222
3259
|
markAction,
|
|
2223
|
-
markRequestFor,
|
|
2224
3260
|
markdownHtml,
|
|
2225
3261
|
markdownTokens,
|
|
3262
|
+
mergeOutcome,
|
|
2226
3263
|
modelCostTitle,
|
|
2227
3264
|
namedRepos,
|
|
2228
3265
|
newTaskFields,
|
|
3266
|
+
noExecutorRefusal,
|
|
2229
3267
|
opsEditable,
|
|
3268
|
+
outOfRounds,
|
|
2230
3269
|
outputLines,
|
|
2231
3270
|
outputPreview,
|
|
3271
|
+
overrideNeedsConfirmation,
|
|
2232
3272
|
parseCount,
|
|
2233
3273
|
partedPromptUsage,
|
|
2234
3274
|
pastGateConfirmation,
|
|
3275
|
+
patchView,
|
|
2235
3276
|
pausedOnLimit,
|
|
3277
|
+
planBlocks,
|
|
3278
|
+
planMarkerParts,
|
|
2236
3279
|
planUsage,
|
|
2237
3280
|
plannerContextFill,
|
|
3281
|
+
receiptLabel,
|
|
2238
3282
|
reconcileApprovals,
|
|
2239
3283
|
reduceConversation,
|
|
2240
3284
|
reduceTaskLog,
|
|
3285
|
+
repairedLanded,
|
|
3286
|
+
repairedNotice,
|
|
3287
|
+
repeatingChecks,
|
|
2241
3288
|
replayTaskLog,
|
|
3289
|
+
repoBaseLine,
|
|
3290
|
+
repoResultLines,
|
|
2242
3291
|
resolveTaskRef,
|
|
2243
3292
|
resolveUserTaskRef,
|
|
2244
3293
|
resumeLabel,
|
|
2245
3294
|
resumeTime,
|
|
2246
3295
|
resumedSessionWindow,
|
|
3296
|
+
runHoldsEditors,
|
|
2247
3297
|
runnerToolSubject,
|
|
3298
|
+
savedPatchView,
|
|
3299
|
+
savedPatchViews,
|
|
3300
|
+
scriptTaskText,
|
|
3301
|
+
severityLabel,
|
|
2248
3302
|
skillTokens,
|
|
2249
3303
|
skippedLabel,
|
|
2250
3304
|
startAttemptUsage,
|
|
@@ -2253,6 +3307,10 @@ var EMPTY_RUN = { tasks: [], runMerge: null, handoff: null, mergeResult: null };
|
|
|
2253
3307
|
subtreeIds,
|
|
2254
3308
|
summarizeToolCall,
|
|
2255
3309
|
takeAt,
|
|
3310
|
+
taskActionCommand,
|
|
3311
|
+
taskChatAvailable,
|
|
3312
|
+
taskLogCommands,
|
|
3313
|
+
taskLogHeaderView,
|
|
2256
3314
|
taskOrderLabel,
|
|
2257
3315
|
taskPlacement,
|
|
2258
3316
|
taskRef,
|
|
@@ -2272,6 +3330,8 @@ var EMPTY_RUN = { tasks: [], runMerge: null, handoff: null, mergeResult: null };
|
|
|
2272
3330
|
unsendLatest,
|
|
2273
3331
|
usageLine,
|
|
2274
3332
|
usageText,
|
|
2275
|
-
|
|
3333
|
+
wakeUpItems,
|
|
3334
|
+
walkPlan,
|
|
3335
|
+
worstSeverity
|
|
2276
3336
|
});
|
|
2277
3337
|
//# sourceMappingURL=plan-utils.js.map
|