@ordewell/cli 0.11.3 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AUTHORS.md +3 -1
- package/README.md +3 -2
- package/dist/main.js +881 -559
- package/package.json +3 -3
package/dist/main.js
CHANGED
|
@@ -25,7 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
|
|
26
26
|
// src/main.ts
|
|
27
27
|
var import_path8 = require("path");
|
|
28
|
-
var
|
|
28
|
+
var import_core60 = require("@ordewell/core");
|
|
29
29
|
|
|
30
30
|
// src/utils/env.ts
|
|
31
31
|
var import_fs = require("fs");
|
|
@@ -108,6 +108,9 @@ function moveAutonomyToSettings() {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
// src/help.ts
|
|
112
|
+
var import_core3 = require("@ordewell/core");
|
|
113
|
+
|
|
111
114
|
// src/runnerNames.ts
|
|
112
115
|
var import_core2 = require("@ordewell/core");
|
|
113
116
|
function manifests() {
|
|
@@ -156,6 +159,7 @@ Tasks (<id> is an order number or a task ID):
|
|
|
156
159
|
ordewell remove-task <id> Remove a task from the current plan
|
|
157
160
|
ordewell complete <id> Mark a task complete (alias: mark-complete; asks first, --yes skips the question)
|
|
158
161
|
ordewell uncomplete <id> Mark a completed task not done (asks first, --yes skips the question)
|
|
162
|
+
ordewell revert <id> Take a landed task's work back off the integration branch (lists what goes with it, asks first, --yes skips the question)
|
|
159
163
|
ordewell skip <id> Skip a task: nothing it did lands, dependents go ahead
|
|
160
164
|
ordewell force-start <id> Approve a task and start it now, or once its dependencies finish (asks past a merge gate)
|
|
161
165
|
ordewell run-task <id> Run only one task
|
|
@@ -187,7 +191,7 @@ Planner, models and runners:
|
|
|
187
191
|
ordewell automerge [on|off] (headless) Merge at merge gates and at the end of a run; /settings covers it
|
|
188
192
|
ordewell trailer [on|off] (headless) End Ordewell's commits with "Planned-with: Ordewell"; set only here or in settings.json
|
|
189
193
|
ordewell autorun [on|off] (headless) Start each plan the planner submits, without review; /settings covers it
|
|
190
|
-
ordewell parallel [<n>] (headless) How many AI tasks run at once, a live run included; /settings covers it
|
|
194
|
+
${import_core3.DELEGATION_OFFERED ? " ordewell delegation [off|<tokens>] (headless) Whether a plan may delegate, and a tree's token budget; /settings covers it\n" : ""} ordewell parallel [<n>] (headless) How many AI tasks run at once, a live run included; /settings covers it
|
|
191
195
|
ordewell refresh Re-check runners; reread settings and cached models
|
|
192
196
|
ordewell models List every provider's catalog (works without a server)
|
|
193
197
|
|
|
@@ -296,6 +300,9 @@ Environment:
|
|
|
296
300
|
ORDEWELL_EFFECT_ALLOW Script tasks the planner may run unasked, comma-separated rules matched
|
|
297
301
|
against each whole command, flags included: "git push origin feature/*".
|
|
298
302
|
Every command must be covered; none is by default
|
|
303
|
+
ORDEWELL_NOTIFY_COMMAND A command to run when something new waits on you, as a JSON array:
|
|
304
|
+
'["program", "arg"]'. No shell; the escalation is in
|
|
305
|
+
ORDEWELL_ESCALATION_ID, _KIND, _TASK and _REASON. A workspace cannot set it
|
|
299
306
|
ORDEWELL_PORT Daemon port CLI commands target (default: 3742)
|
|
300
307
|
ORDEWELL_DIRENV false: agents skip the project's direnv .envrc
|
|
301
308
|
ORDEWELL_RESEARCH_ENABLED true (default) or false
|
|
@@ -310,7 +317,7 @@ var import_os = require("os");
|
|
|
310
317
|
var import_path2 = require("path");
|
|
311
318
|
var import_http = __toESM(require("http"));
|
|
312
319
|
var import_net = require("net");
|
|
313
|
-
var
|
|
320
|
+
var import_core4 = require("@ordewell/core");
|
|
314
321
|
|
|
315
322
|
// src/utils/args.ts
|
|
316
323
|
function inlineValue(arg, name) {
|
|
@@ -374,7 +381,7 @@ function positionals(args) {
|
|
|
374
381
|
// src/daemon.ts
|
|
375
382
|
var DEFAULT_PORT = 3742;
|
|
376
383
|
function configDir() {
|
|
377
|
-
return (0,
|
|
384
|
+
return (0, import_core4.globalDataDir)();
|
|
378
385
|
}
|
|
379
386
|
function oldConfigDir() {
|
|
380
387
|
return (0, import_path2.join)((0, import_os.homedir)(), ".config", "ordewell");
|
|
@@ -396,12 +403,12 @@ function readPidFile(file) {
|
|
|
396
403
|
}
|
|
397
404
|
}
|
|
398
405
|
async function daemonInfo(port) {
|
|
399
|
-
const token = (0,
|
|
406
|
+
const token = (0, import_core4.readDaemonToken)(port);
|
|
400
407
|
try {
|
|
401
408
|
const res = await httpGet(
|
|
402
409
|
`http://127.0.0.1:${port}/api/server`,
|
|
403
410
|
2e3,
|
|
404
|
-
token ? { Authorization: (0,
|
|
411
|
+
token ? { Authorization: (0, import_core4.bearerHeaderValue)(token) } : {}
|
|
405
412
|
);
|
|
406
413
|
if (res.status !== 200) return { kind: "unverifiable" };
|
|
407
414
|
const parsed = JSON.parse(res.data);
|
|
@@ -443,12 +450,12 @@ function getWebServerPath() {
|
|
|
443
450
|
);
|
|
444
451
|
}
|
|
445
452
|
async function isDaemonRunning(port = DEFAULT_PORT) {
|
|
446
|
-
const token = (0,
|
|
453
|
+
const token = (0, import_core4.readDaemonToken)(port);
|
|
447
454
|
try {
|
|
448
455
|
const res = await httpGet(
|
|
449
456
|
`http://127.0.0.1:${port}/api/workspaces`,
|
|
450
457
|
2e3,
|
|
451
|
-
token ? { Authorization: (0,
|
|
458
|
+
token ? { Authorization: (0, import_core4.bearerHeaderValue)(token) } : {}
|
|
452
459
|
);
|
|
453
460
|
return res.status === 200 || res.status === 401;
|
|
454
461
|
} catch {
|
|
@@ -514,7 +521,7 @@ async function stopDaemon(port = DEFAULT_PORT) {
|
|
|
514
521
|
(0, import_fs2.unlinkSync)(pidFile);
|
|
515
522
|
} catch {
|
|
516
523
|
}
|
|
517
|
-
(0,
|
|
524
|
+
(0, import_core4.clearDaemonToken)(port);
|
|
518
525
|
console.error(`Removed an unreadable PID file for port ${port}. Is the server running?`);
|
|
519
526
|
return false;
|
|
520
527
|
}
|
|
@@ -530,7 +537,7 @@ async function stopDaemon(port = DEFAULT_PORT) {
|
|
|
530
537
|
(0, import_fs2.unlinkSync)(pidFile);
|
|
531
538
|
} catch {
|
|
532
539
|
}
|
|
533
|
-
(0,
|
|
540
|
+
(0, import_core4.clearDaemonToken)(port);
|
|
534
541
|
console.log("Server was not running (stale PID removed).");
|
|
535
542
|
return true;
|
|
536
543
|
}
|
|
@@ -597,7 +604,7 @@ function resolvePort(args = []) {
|
|
|
597
604
|
// src/apiClient.ts
|
|
598
605
|
var import_http2 = __toESM(require("http"));
|
|
599
606
|
var import_ws = __toESM(require("ws"));
|
|
600
|
-
var
|
|
607
|
+
var import_core5 = require("@ordewell/core");
|
|
601
608
|
var DEFAULT_HTTP_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
602
609
|
var daemonAgent = new import_http2.default.Agent({ keepAlive: true, timeout: 0 });
|
|
603
610
|
var DaemonError = class extends Error {
|
|
@@ -675,7 +682,7 @@ var ApiClient = class _ApiClient {
|
|
|
675
682
|
* A missing file is not an error here — the daemon's own 401 names the path.
|
|
676
683
|
*/
|
|
677
684
|
token() {
|
|
678
|
-
return (0,
|
|
685
|
+
return (0, import_core5.readDaemonToken)(this.port);
|
|
679
686
|
}
|
|
680
687
|
/**
|
|
681
688
|
* A daemon holds only the sessions adopted since it started, so after a
|
|
@@ -727,7 +734,7 @@ var ApiClient = class _ApiClient {
|
|
|
727
734
|
agent: daemonAgent,
|
|
728
735
|
headers: {
|
|
729
736
|
...body ? { "Content-Type": "application/json" } : {},
|
|
730
|
-
...token ? { Authorization: (0,
|
|
737
|
+
...token ? { Authorization: (0, import_core5.bearerHeaderValue)(token) } : {}
|
|
731
738
|
},
|
|
732
739
|
...idleBoundMs !== void 0 ? { timeout: idleBoundMs } : {}
|
|
733
740
|
};
|
|
@@ -754,7 +761,7 @@ var ApiClient = class _ApiClient {
|
|
|
754
761
|
req.end();
|
|
755
762
|
});
|
|
756
763
|
}
|
|
757
|
-
async generatePlan(goal, runners2, workspace, sessionId = (0,
|
|
764
|
+
async generatePlan(goal, runners2, workspace, sessionId = (0, import_core5.mintSessionId)()) {
|
|
758
765
|
const route = `/api/plans/${sessionId}/generate`;
|
|
759
766
|
const { plan, models, modelsByRunner, notes } = planOf(await this.turn("POST", route, "Plan generation failed", {
|
|
760
767
|
goal,
|
|
@@ -820,6 +827,15 @@ var ApiClient = class _ApiClient {
|
|
|
820
827
|
execute(sessionId, command) {
|
|
821
828
|
return this.call("POST", `/api/plans/${sessionId}/execute-command`, "Command failed", command);
|
|
822
829
|
}
|
|
830
|
+
/** What waits on a person now (ADR-0053). */
|
|
831
|
+
async escalations(sessionId) {
|
|
832
|
+
const res = await this.call("GET", `/api/plans/${sessionId}/escalations`, "Could not read what waits on you");
|
|
833
|
+
return res.escalations;
|
|
834
|
+
}
|
|
835
|
+
/** Answer an escalation by its id; a stale one is a {@link DaemonError} saying so. `confirmed` is the user's yes to the option's `confirm`. */
|
|
836
|
+
answerEscalation(sessionId, answer) {
|
|
837
|
+
return this.call("POST", `/api/plans/${sessionId}/escalations/answer`, "Could not answer", answer);
|
|
838
|
+
}
|
|
823
839
|
/** Keep what waits on the planner's usage limit from being sent at the reset; `cancelled` is false when nothing waited. */
|
|
824
840
|
cancelPlannerResend(sessionId) {
|
|
825
841
|
return this.call("POST", `/api/plans/${sessionId}/converse/resend/cancel`, "Cancel resend failed");
|
|
@@ -902,6 +918,10 @@ var ApiClient = class _ApiClient {
|
|
|
902
918
|
const res = await this.call("GET", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/merge-gate`, "Could not read the merge gate");
|
|
903
919
|
return res.mergeGate ?? [];
|
|
904
920
|
}
|
|
921
|
+
/** What reverting a task would do now (ADR-0054), which the user confirms before `execute` sends `revert_task`. */
|
|
922
|
+
revertPreview(sessionId, taskId) {
|
|
923
|
+
return this.call("GET", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/revert-preview`, "Could not read what the revert would take");
|
|
924
|
+
}
|
|
905
925
|
async reviewRunDiff(sessionId) {
|
|
906
926
|
const res = await this.call("GET", `/api/plans/${sessionId}/isolation/diff`, "Could not read the diff");
|
|
907
927
|
return res.diff;
|
|
@@ -980,7 +1000,7 @@ var ApiClient = class _ApiClient {
|
|
|
980
1000
|
const token = this.token();
|
|
981
1001
|
const socket = new import_ws.default(
|
|
982
1002
|
`ws://127.0.0.1:${this.port}/ws/session/${sessionId}`,
|
|
983
|
-
token ? (0,
|
|
1003
|
+
token ? (0, import_core5.tokenSubprotocols)(token) : void 0
|
|
984
1004
|
);
|
|
985
1005
|
socket.on("unexpected-response", (_req, res) => {
|
|
986
1006
|
const chunks = [];
|
|
@@ -1160,26 +1180,26 @@ var ApiClient = class _ApiClient {
|
|
|
1160
1180
|
var import_readline2 = require("readline");
|
|
1161
1181
|
|
|
1162
1182
|
// src/utils/tasks.ts
|
|
1163
|
-
var
|
|
1183
|
+
var import_core6 = require("@ordewell/core");
|
|
1164
1184
|
function allTasksOf(plan) {
|
|
1165
1185
|
const tasks = plan?.pendingTasks || plan?.tasks || [];
|
|
1166
1186
|
const executionLog = plan?.executionLog || [];
|
|
1167
|
-
return (0,
|
|
1187
|
+
return (0, import_core6.rejoinPlanTree)(tasks, executionLog);
|
|
1168
1188
|
}
|
|
1169
1189
|
function resolveTaskId(plan, identifier) {
|
|
1170
|
-
return (0,
|
|
1190
|
+
return (0, import_core6.resolveUserTaskRef)(allTasksOf(plan), identifier)?.id;
|
|
1171
1191
|
}
|
|
1172
1192
|
|
|
1173
1193
|
// src/utils/session.ts
|
|
1174
1194
|
var import_fs3 = require("fs");
|
|
1175
1195
|
var import_path3 = require("path");
|
|
1176
|
-
var
|
|
1196
|
+
var import_core7 = require("@ordewell/core");
|
|
1177
1197
|
var LAST_SESSION_FILENAME = "last-session.json";
|
|
1178
1198
|
function lastSessionFile(workspace) {
|
|
1179
|
-
return (0, import_path3.join)((0,
|
|
1199
|
+
return (0, import_path3.join)((0, import_core7.getStateDir)(workspace), LAST_SESSION_FILENAME);
|
|
1180
1200
|
}
|
|
1181
1201
|
function saveLastSession(sessionId, goal, runners2, workspace) {
|
|
1182
|
-
const dir = (0,
|
|
1202
|
+
const dir = (0, import_core7.getStateDir)(workspace);
|
|
1183
1203
|
if (!(0, import_fs3.existsSync)(dir)) (0, import_fs3.mkdirSync)(dir, { recursive: true });
|
|
1184
1204
|
(0, import_fs3.writeFileSync)(
|
|
1185
1205
|
lastSessionFile(workspace),
|
|
@@ -1201,14 +1221,14 @@ function expandSessionId(id, workspace) {
|
|
|
1201
1221
|
if (id.startsWith(SESSION_PREFIX)) return id;
|
|
1202
1222
|
let files;
|
|
1203
1223
|
try {
|
|
1204
|
-
files = (0, import_fs3.readdirSync)((0, import_path3.join)((0,
|
|
1224
|
+
files = (0, import_fs3.readdirSync)((0, import_path3.join)((0, import_core7.getStateDir)(workspace), "sessions")).filter((f) => f.endsWith(".json"));
|
|
1205
1225
|
} catch {
|
|
1206
1226
|
return id;
|
|
1207
1227
|
}
|
|
1208
1228
|
const matches = /* @__PURE__ */ new Set();
|
|
1209
1229
|
for (const file of files) {
|
|
1210
1230
|
try {
|
|
1211
|
-
const full = JSON.parse((0, import_fs3.readFileSync)((0, import_path3.join)((0,
|
|
1231
|
+
const full = JSON.parse((0, import_fs3.readFileSync)((0, import_path3.join)((0, import_core7.getStateDir)(workspace), "sessions", file), "utf8"))?.meta?.id;
|
|
1212
1232
|
if (typeof full === "string" && (full.endsWith(id) || full.slice(SESSION_PREFIX.length).startsWith(id))) matches.add(full);
|
|
1213
1233
|
} catch {
|
|
1214
1234
|
}
|
|
@@ -1283,7 +1303,7 @@ ${describeApproval(event)}
|
|
|
1283
1303
|
}
|
|
1284
1304
|
|
|
1285
1305
|
// src/commands/researchLog.ts
|
|
1286
|
-
var
|
|
1306
|
+
var import_core8 = require("@ordewell/core");
|
|
1287
1307
|
var OUTCOME_MARK = {
|
|
1288
1308
|
success: "\u2713",
|
|
1289
1309
|
failure: "\u2717",
|
|
@@ -1295,7 +1315,7 @@ var RESULT_CHARS = 160;
|
|
|
1295
1315
|
var VERBOSE_RESULT_CHARS = 600;
|
|
1296
1316
|
var THINKING_CHARS = 300;
|
|
1297
1317
|
function summarize(tool, args, toolLabel) {
|
|
1298
|
-
const { name, keyArg } = (0,
|
|
1318
|
+
const { name, keyArg } = (0, import_core8.toolHeadline)(tool, args, toolLabel);
|
|
1299
1319
|
return `${name}(${keyArg})`;
|
|
1300
1320
|
}
|
|
1301
1321
|
function oneLine(text, max) {
|
|
@@ -1311,9 +1331,9 @@ function formatStepLine(event, options = {}) {
|
|
|
1311
1331
|
const step2 = event.step;
|
|
1312
1332
|
if (!step2) return null;
|
|
1313
1333
|
const mark = OUTCOME_MARK[step2.outcome] ?? "\u2713";
|
|
1314
|
-
const sealed = step2.tool === "bash" && (0,
|
|
1334
|
+
const sealed = step2.tool === "bash" && (0, import_core8.ranSealed)(step2.result ?? "");
|
|
1315
1335
|
const summary = `${summarize(step2.tool, step2.args, step2.toolLabel)}${sealed ? " [sealed]" : ""}`;
|
|
1316
|
-
const result = oneLine((0,
|
|
1336
|
+
const result = oneLine((0, import_core8.withoutSealedMark)(step2.result ?? ""), options.verbose ? VERBOSE_RESULT_CHARS : RESULT_CHARS);
|
|
1317
1337
|
const indent = event.subagentId ? " \u21B3 " : "";
|
|
1318
1338
|
return result ? `${indent}${mark} ${summary} \u2192 ${result}` : `${indent}${mark} ${summary}`;
|
|
1319
1339
|
}
|
|
@@ -1347,7 +1367,7 @@ function createStepLog(options = {}) {
|
|
|
1347
1367
|
}
|
|
1348
1368
|
|
|
1349
1369
|
// src/commands/plan.ts
|
|
1350
|
-
var
|
|
1370
|
+
var import_core10 = require("@ordewell/core");
|
|
1351
1371
|
|
|
1352
1372
|
// src/commands/shared.ts
|
|
1353
1373
|
var import_readline = require("readline");
|
|
@@ -1398,7 +1418,7 @@ function normalizeCatalog(result) {
|
|
|
1398
1418
|
}
|
|
1399
1419
|
|
|
1400
1420
|
// src/commands/shared.ts
|
|
1401
|
-
var
|
|
1421
|
+
var import_core9 = require("@ordewell/core");
|
|
1402
1422
|
async function connect(subArgs, injected) {
|
|
1403
1423
|
return injected ?? new ApiClient(await ensureDaemon(resolvePort(subArgs)));
|
|
1404
1424
|
}
|
|
@@ -1416,7 +1436,7 @@ function headlessSignedOut(message) {
|
|
|
1416
1436
|
);
|
|
1417
1437
|
}
|
|
1418
1438
|
function headlessPlannerRefusal(err) {
|
|
1419
|
-
return err instanceof DaemonError && err.plannerNotReady ? (0,
|
|
1439
|
+
return err instanceof DaemonError && err.plannerNotReady ? (0, import_core9.describePlannerNotReady)(err.plannerNotReady, import_core9.HEADLESS_CLI_COMMANDS) : null;
|
|
1420
1440
|
}
|
|
1421
1441
|
function lostReplyHint(err, sessionId) {
|
|
1422
1442
|
if (deliveryOf(err) !== "unknown") return null;
|
|
@@ -1564,16 +1584,16 @@ Plan: ${tasks.length} task${tasks.length === 1 ? "" : "s"} (${aiCount} AI${scrip
|
|
|
1564
1584
|
for (const t of tasks) {
|
|
1565
1585
|
const typeIcon = t.type === "user" ? "[MAN]" : t.type === "script" ? "[SCR]" : t.ops ? "[OPS]" : t.research ? "[RES]" : "[ AI]";
|
|
1566
1586
|
const deps = t.dependencies?.length ? ` \u2190 ${t.dependencies.join(", ")}` : "";
|
|
1567
|
-
console.log(` ${(0,
|
|
1587
|
+
console.log(` ${(0, import_core10.taskOrderLabel)(t).padStart(2)}. ${typeIcon} ${t.title}${modelSuffix(t)}${deps}`);
|
|
1568
1588
|
if (t.type === "script") {
|
|
1569
|
-
for (const line of (0,
|
|
1589
|
+
for (const line of (0, import_core10.scriptTaskText)({ ...t, type: "script" }).split("\n")) console.log(line ? ` ${line.replace(/\t/g, " ")}` : "");
|
|
1570
1590
|
}
|
|
1571
|
-
const loop = (0,
|
|
1572
|
-
if (loop) console.log(` \u21BB ${(0,
|
|
1591
|
+
const loop = (0, import_core10.taskRowView)({ id: t.id, order: t.order, status: t.status ?? "pending", type: t.type === "user" || t.type === "script" ? t.type : "ai", dependencies: t.dependencies ?? [], loop: t.loop, loopRound: t.loopRound }, { tasks }).loop;
|
|
1592
|
+
if (loop) console.log(` \u21BB ${(0, import_core10.loopRowParts)(loop).join(" \xB7 ")}`);
|
|
1573
1593
|
for (const sub of t.subtasks || []) {
|
|
1574
1594
|
const subTypeIcon = sub.type === "user" ? "[MAN]" : "[ AI]";
|
|
1575
1595
|
const subDeps = sub.dependencies?.length ? ` \u2190 ${sub.dependencies.join(", ")}` : "";
|
|
1576
|
-
console.log(` ${(0,
|
|
1596
|
+
console.log(` ${(0, import_core10.taskOrderLabel)(sub, t)}. ${subTypeIcon} ${sub.title}${modelSuffix(sub)}${subDeps}`);
|
|
1577
1597
|
}
|
|
1578
1598
|
}
|
|
1579
1599
|
if (manCount > 0) {
|
|
@@ -1637,7 +1657,7 @@ async function handlePlan(subArgs, deps = {}) {
|
|
|
1637
1657
|
runners2 = runnerFlags;
|
|
1638
1658
|
}
|
|
1639
1659
|
console.error(`Generating plan for: "${goal}"...`);
|
|
1640
|
-
const sessionId = (0,
|
|
1660
|
+
const sessionId = (0, import_core10.mintSessionId)();
|
|
1641
1661
|
const isTty = !!process.stderr.isTTY;
|
|
1642
1662
|
let lastLineLength = 0;
|
|
1643
1663
|
const stepLog = createStepLog({ verbose });
|
|
@@ -1750,9 +1770,9 @@ ${note}`);
|
|
|
1750
1770
|
}
|
|
1751
1771
|
|
|
1752
1772
|
// src/utils/output.ts
|
|
1753
|
-
var
|
|
1773
|
+
var import_core11 = require("@ordewell/core");
|
|
1754
1774
|
function iconFor(status) {
|
|
1755
|
-
switch ((0,
|
|
1775
|
+
switch ((0, import_core11.taskStatusKind)({ status })) {
|
|
1756
1776
|
case "done":
|
|
1757
1777
|
return "\x1B[32m\u2713\x1B[0m";
|
|
1758
1778
|
case "running":
|
|
@@ -1772,12 +1792,12 @@ function iconFor(status) {
|
|
|
1772
1792
|
}
|
|
1773
1793
|
|
|
1774
1794
|
// src/commands/run.ts
|
|
1775
|
-
var
|
|
1795
|
+
var import_core13 = require("@ordewell/core");
|
|
1776
1796
|
|
|
1777
1797
|
// src/commands/taskHandoff.ts
|
|
1778
|
-
var
|
|
1798
|
+
var import_core12 = require("@ordewell/core");
|
|
1779
1799
|
function taskHandoffLines(handoff, indent) {
|
|
1780
|
-
const preview = (0,
|
|
1800
|
+
const preview = (0, import_core12.handoffPreviewLines)(handoff);
|
|
1781
1801
|
const [first = "", ...body] = preview;
|
|
1782
1802
|
if (!handoff.summary.trim() || handoff.commit) return [`${indent}\u21B3 Handoff`, ...preview.map((line) => `${indent}${line}`)];
|
|
1783
1803
|
return [`${indent}\u21B3 Handoff: ${first}`, ...body.map((line) => `${indent}${line}`)];
|
|
@@ -1790,12 +1810,12 @@ function loopNote(task, all) {
|
|
|
1790
1810
|
if (task.loop) {
|
|
1791
1811
|
const check2 = { id: task.id, status: task.status, loop: task.loop, loopRound: task.loopRound, ...task.verdict ? { verdict: task.verdict } : {} };
|
|
1792
1812
|
const halted = task.loop.body.some((id) => ["failed", "skipped"].includes(all.find((t) => t.id === id)?.status ?? ""));
|
|
1793
|
-
const state = (0,
|
|
1794
|
-
return { text: (0,
|
|
1813
|
+
const state = (0, import_core13.loopCheckState)(check2, !halted && !task.loopStranded);
|
|
1814
|
+
return { text: (0, import_core13.loopCheckLabel)(check2, state), repeating: state === "repeating" };
|
|
1795
1815
|
}
|
|
1796
1816
|
const check = all.find((t) => t.loop?.body.includes(task.id));
|
|
1797
1817
|
if (!check?.loop) return null;
|
|
1798
|
-
return { text: (0,
|
|
1818
|
+
return { text: (0, import_core13.loopBodyLabel)(refOf(check.id), (0, import_core13.loopBodyRound)({ status: check.status, loopRound: check.loopRound }, task.status), check.loop.maxRounds), repeating: false };
|
|
1799
1819
|
}
|
|
1800
1820
|
function resolveSessionId(subArgs) {
|
|
1801
1821
|
const explicit = flag(subArgs, "--session-id");
|
|
@@ -1824,10 +1844,11 @@ async function followExecution(api, sessionId, start, onBlocked) {
|
|
|
1824
1844
|
const loop = loopNote(t, entries);
|
|
1825
1845
|
const icon = iconFor(loop?.repeating ? "pending" : t.status);
|
|
1826
1846
|
const verdict = t.verdict ? `: ${t.verdict.outcome.toUpperCase()}: ${t.verdict.reason.slice(0, 60)}` : "";
|
|
1827
|
-
const waits = t.waitsForApproval ? `: ${inLine(
|
|
1847
|
+
const waits = t.waitsForApproval ? `: ${inLine(import_core13.WAITS_FOR_APPROVAL_LABEL)}: \`ordewell force-start <id>\` approves it, \`ordewell run\` approves all` : "";
|
|
1828
1848
|
lines.push(` ${icon} ${refOf(t.id)}${t.type === "script" ? " [SCR]" : t.research ? " [RES]" : ""} ${t.status}${verdict}${waits}${loop ? ` \xB7 \u21BB ${inLine(loop.text)}` : ""}`);
|
|
1829
|
-
if (t.holdReason) lines.push(` ${(0,
|
|
1830
|
-
if (t.
|
|
1849
|
+
if (t.holdReason) lines.push(` ${(0, import_core13.holdLabel)(t.holdReason)}`);
|
|
1850
|
+
if (t.isolation?.state === "conflict" && t.isolation.repairWaits) lines.push(` ${(0, import_core13.repairWaitLabel)(t.isolation.repairWaits)}`);
|
|
1851
|
+
if (t.status === "skipped") lines.push(` ${(0, import_core13.skippedLabel)(t.skipReason ?? null)}`);
|
|
1831
1852
|
if (t.handoff && (t.status === "completed" || t.status === "failed")) lines.push(...taskHandoffLines(t.handoff, " "));
|
|
1832
1853
|
}
|
|
1833
1854
|
const output = lines.join("\n");
|
|
@@ -1853,11 +1874,11 @@ async function followExecution(api, sessionId, start, onBlocked) {
|
|
|
1853
1874
|
});
|
|
1854
1875
|
const stream = api.streamExecution(sessionId, (event) => {
|
|
1855
1876
|
if (event.type === "task_started") {
|
|
1856
|
-
process.stderr.write(`\xB7 ${(0,
|
|
1877
|
+
process.stderr.write(`\xB7 ${(0, import_core13.taskStartedNotice)(event.title, event.runner)}
|
|
1857
1878
|
`);
|
|
1858
1879
|
}
|
|
1859
1880
|
if (event.type === "checkpoint") {
|
|
1860
|
-
process.stderr.write(`\xB7 Checkpoint: ${event.taskTitle}: ${(0,
|
|
1881
|
+
process.stderr.write(`\xB7 Checkpoint: ${event.taskTitle}: ${(0, import_core13.truncateCheckpointSummary)(event.summary)}: \`ordewell checkpoint <id> approve|reject [reason]\` answers it
|
|
1861
1882
|
`);
|
|
1862
1883
|
}
|
|
1863
1884
|
if (event.type === "status_update" && event.tasks) {
|
|
@@ -1888,10 +1909,10 @@ Done. ${summary.completed || 0} completed, ${summary.failed || 0} failed, ${summ
|
|
|
1888
1909
|
}
|
|
1889
1910
|
if (event.type === "isolation_handoff") {
|
|
1890
1911
|
const n = event.landed.length;
|
|
1891
|
-
const where = (0,
|
|
1912
|
+
const where = (0, import_core13.isRepoGroup)(event) ? ` in ${event.repos.map((r) => r.path).join(", ")}` : "";
|
|
1892
1913
|
console.log(`
|
|
1893
|
-
Run finished on ${(0,
|
|
1894
|
-
if ((0,
|
|
1914
|
+
Run finished on ${(0, import_core13.handoffBranchName)(event)}${where}: ${n} task${n === 1 ? "" : "s"} landed.`);
|
|
1915
|
+
if ((0, import_core13.isRepoGroup)(event)) for (const line of (0, import_core13.repoResultLines)(event)) console.log(` ${line}`);
|
|
1895
1916
|
if (event.deferredTo === "supervisor") console.log(" The supervisor may merge it.");
|
|
1896
1917
|
console.log(" `ordewell handoff review|merge|discard|cleanup` to land it.");
|
|
1897
1918
|
}
|
|
@@ -1925,7 +1946,7 @@ function printScriptBodies(plan) {
|
|
|
1925
1946
|
for (const t of allTasksOf(plan)) {
|
|
1926
1947
|
if (t.type !== "script" || t.status === "completed" || t.status === "skipped") continue;
|
|
1927
1948
|
console.log(` Script ${refOf(t.id)} "${t.title}":`);
|
|
1928
|
-
for (const line of (0,
|
|
1949
|
+
for (const line of (0, import_core13.scriptTaskText)(t).split("\n")) console.log(line ? ` ${line.replace(/\t/g, " ")}` : "");
|
|
1929
1950
|
}
|
|
1930
1951
|
}
|
|
1931
1952
|
async function handleRun(subArgs, injectedApi) {
|
|
@@ -1954,7 +1975,7 @@ async function handleRun(subArgs, injectedApi) {
|
|
|
1954
1975
|
}
|
|
1955
1976
|
|
|
1956
1977
|
// src/tui/blocks.ts
|
|
1957
|
-
var
|
|
1978
|
+
var import_core15 = require("@ordewell/core");
|
|
1958
1979
|
|
|
1959
1980
|
// ../../node_modules/ansi-regex/index.js
|
|
1960
1981
|
function ansiRegex({ onlyFirst = false } = {}) {
|
|
@@ -2367,9 +2388,9 @@ var style = {
|
|
|
2367
2388
|
};
|
|
2368
2389
|
|
|
2369
2390
|
// src/tui/markdown.ts
|
|
2370
|
-
var
|
|
2391
|
+
var import_core14 = require("@ordewell/core");
|
|
2371
2392
|
function renderMarkdown(source, cols) {
|
|
2372
|
-
return blocks((0,
|
|
2393
|
+
return blocks((0, import_core14.markdownTokens)(source), { room: Math.max(1, cols), base: [] });
|
|
2373
2394
|
}
|
|
2374
2395
|
function closeOf(tokens, open) {
|
|
2375
2396
|
const { level } = tokens[open];
|
|
@@ -2629,7 +2650,7 @@ function messageLines(block2, cols) {
|
|
|
2629
2650
|
}
|
|
2630
2651
|
function reportUpdateLines(block2, cols, detailAll) {
|
|
2631
2652
|
const shown = shownText(block2.text);
|
|
2632
|
-
const items = (0,
|
|
2653
|
+
const items = (0, import_core15.wakeUpItems)(sanitize(shown.text));
|
|
2633
2654
|
if (!items.some((item) => item.kind === "report")) return null;
|
|
2634
2655
|
const rows = [style.yellow(`\u21AF Run update:`)];
|
|
2635
2656
|
for (const item of items) {
|
|
@@ -2639,11 +2660,11 @@ function reportUpdateLines(block2, cols, detailAll) {
|
|
|
2639
2660
|
}
|
|
2640
2661
|
rows.push(...wrap(`- task ${item.task} ended, with this report:`, Math.max(1, cols - 2)).map((line) => style.yellow(` ${line}`)));
|
|
2641
2662
|
const room = Math.max(1, cols - 4);
|
|
2642
|
-
const shownLines = detailAll ? item.lines : item.lines.slice(0,
|
|
2663
|
+
const shownLines = detailAll ? item.lines : item.lines.slice(0, import_core15.REPORT_PREVIEW_LINES);
|
|
2643
2664
|
rows.push(...shownLines.flatMap((line) => wrap(line, room)).map((line) => ` ${line}`));
|
|
2644
2665
|
const hidden = item.lines.length - shownLines.length;
|
|
2645
2666
|
if (hidden > 0) rows.push(style.grey(` \u2026 +${countOf(hidden, "line")} of the report (ctrl+o to expand)`));
|
|
2646
|
-
else if (detailAll && item.lines.length >
|
|
2667
|
+
else if (detailAll && item.lines.length > import_core15.REPORT_PREVIEW_LINES) rows.push(style.grey(" \u2026 (ctrl+o to collapse)"));
|
|
2647
2668
|
}
|
|
2648
2669
|
if (shown.hiddenChars > 0) rows.push(textCutNote(shown.hiddenChars));
|
|
2649
2670
|
return rows;
|
|
@@ -2653,7 +2674,7 @@ function onBand(rows, cols) {
|
|
|
2653
2674
|
return style.enabled ? band.map((row) => style.band(pad(row, cols))) : band;
|
|
2654
2675
|
}
|
|
2655
2676
|
function skillPaintedLines(text, skills, room) {
|
|
2656
|
-
const tokens = (0,
|
|
2677
|
+
const tokens = (0, import_core15.loadedSkillTokens)(text, skills);
|
|
2657
2678
|
return wrapLines(text, room).map(({ line, start }) => {
|
|
2658
2679
|
let painted = "";
|
|
2659
2680
|
let at = 0;
|
|
@@ -2747,7 +2768,7 @@ function headLines(output, maxLines, maxChars) {
|
|
|
2747
2768
|
return lines;
|
|
2748
2769
|
}
|
|
2749
2770
|
function shownOutput(output, maxLines, maxChars) {
|
|
2750
|
-
if (output.length <= FULL_OUTPUT_MAX) return (0,
|
|
2771
|
+
if (output.length <= FULL_OUTPUT_MAX) return (0, import_core15.outputPreview)(output, maxLines);
|
|
2751
2772
|
const lines = headLines(output, maxLines, maxChars);
|
|
2752
2773
|
const total = output.length === 0 ? 0 : lineCountOf(output) + 1;
|
|
2753
2774
|
return { lines, hiddenLineCount: Math.max(0, total - lines.length) };
|
|
@@ -2849,7 +2870,7 @@ var DIFF_PAINT = {
|
|
|
2849
2870
|
var DIFF_SIGN = { added: "+", removed: "-", context: " ", gap: "" };
|
|
2850
2871
|
function diffLines(block2, diff, room, detailAll) {
|
|
2851
2872
|
const bounded = diffOutput(block2.output);
|
|
2852
|
-
const rows = (0,
|
|
2873
|
+
const rows = (0, import_core15.diffRows)(bounded.text);
|
|
2853
2874
|
const gutter = Math.max(0, ...rows.map((row) => String(row.line ?? "").length));
|
|
2854
2875
|
const draw = (row) => {
|
|
2855
2876
|
const text = row.kind === "gap" ? `${" ".repeat(gutter)} \u22EE` : `${String(row.line ?? "").padStart(gutter)} ${DIFF_SIGN[row.kind]} ${sanitize(row.text)}`;
|
|
@@ -2861,7 +2882,7 @@ function diffLines(block2, diff, room, detailAll) {
|
|
|
2861
2882
|
...hidden > 0 ? [style.grey(`\u2026 +${countOf(hidden, "line")} (ctrl+o to expand)`)] : [],
|
|
2862
2883
|
...bounded.hiddenLineCount > 0 ? [style.grey(`\u2026 +${countOf(bounded.hiddenLineCount, "line")} not shown`)] : []
|
|
2863
2884
|
];
|
|
2864
|
-
return [(0,
|
|
2885
|
+
return [(0, import_core15.diffSummary)(diff), ...shown.flatMap(draw), ...more];
|
|
2865
2886
|
}
|
|
2866
2887
|
function toolLines(block2, cols, detailAll) {
|
|
2867
2888
|
const room = Math.max(1, cols - OUTPUT_INDENT.length);
|
|
@@ -2901,7 +2922,7 @@ function subagentLines(block2, cols, detailAll) {
|
|
|
2901
2922
|
function approvalLine(block2, cols) {
|
|
2902
2923
|
const [mark, paint] = block2.status === "pending" ? ["?", style.yellow] : block2.status === "granted" ? ["\u2713", style.green] : block2.status === "withdrawn" ? ["\xB7", style.grey] : ["\u2298", style.yellow];
|
|
2903
2924
|
const note = block2.note ? `: \u201C${firstLine(block2.note)}\u201D` : "";
|
|
2904
|
-
const line = `${mark} ${(0,
|
|
2925
|
+
const line = `${mark} ${(0, import_core15.approvalStatusText)(block2)} \xB7 ${(0, import_core15.approvalKindText)(block2.kind)}: ${firstLine(block2.subject)}${note}`;
|
|
2905
2926
|
return `${paint(mark)}${truncate(line, cols).slice(mark.length)}`;
|
|
2906
2927
|
}
|
|
2907
2928
|
var SETTLED_BODY_LINES = 20;
|
|
@@ -2958,7 +2979,7 @@ function runnerCommandLines(block2, cols, detailAll) {
|
|
|
2958
2979
|
}
|
|
2959
2980
|
function compactionLines(block2, cols, detailAll) {
|
|
2960
2981
|
const hint = block2.summary && !detailAll ? " (ctrl+o for the summary)" : "";
|
|
2961
|
-
const head = style.grey(truncate(`\u2261 ${(0,
|
|
2982
|
+
const head = style.grey(truncate(`\u2261 ${(0, import_core15.compactionText)(block2)}${hint}`, cols));
|
|
2962
2983
|
if (!block2.summary || !detailAll) return [head];
|
|
2963
2984
|
const room = Math.max(1, cols - OUTPUT_INDENT.length);
|
|
2964
2985
|
return [head, ...underHeader(block2.summary.trim().split("\n").flatMap((line) => wrap(sanitize(line), room)).map(style.grey))];
|
|
@@ -2995,7 +3016,7 @@ var HANDOFF_INDENT = " ";
|
|
|
2995
3016
|
var FINDING_INDENT = " ";
|
|
2996
3017
|
function findingHeadLines(f, cols) {
|
|
2997
3018
|
const id = sanitize(f.id);
|
|
2998
|
-
const severity = (0,
|
|
3019
|
+
const severity = (0, import_core15.severityLabel)(f.severity);
|
|
2999
3020
|
const label = `${id} \xB7 ${severity} \xB7 `;
|
|
3000
3021
|
const room = Math.max(1, cols - width(HANDOFF_INDENT) - width(label));
|
|
3001
3022
|
return wrap(sanitize(f.title), room).map((line, i) => i === 0 ? `${HANDOFF_INDENT}${style.grey(id)} \xB7 ${SEVERITY_PAINT[f.severity](severity)} \xB7 ${line}` : `${HANDOFF_INDENT}${" ".repeat(width(label))}${line}`);
|
|
@@ -3003,7 +3024,7 @@ function findingHeadLines(f, cols) {
|
|
|
3003
3024
|
function findingLines(f, cols) {
|
|
3004
3025
|
const room = Math.max(1, cols - FINDING_INDENT.length);
|
|
3005
3026
|
const detail = f.detail ? sanitize(f.detail).replace(/\n+$/, "").split("\n").flatMap((line) => wrap(line, room)).map((line) => `${FINDING_INDENT}${line}`) : [];
|
|
3006
|
-
const evidence = f.evidence?.length ? wrap(`Evidence: ${f.evidence.map((e) => sanitize((0,
|
|
3027
|
+
const evidence = f.evidence?.length ? wrap(`Evidence: ${f.evidence.map((e) => sanitize((0, import_core15.handoffReferenceText)(e))).join("; ")}`, room).map((line) => `${FINDING_INDENT}${style.grey(line)}`) : [];
|
|
3007
3028
|
return [...findingHeadLines(f, cols), ...detail, ...evidence];
|
|
3008
3029
|
}
|
|
3009
3030
|
function handoffArtifactLines(block2, cols) {
|
|
@@ -3012,14 +3033,14 @@ function handoffArtifactLines(block2, cols) {
|
|
|
3012
3033
|
const room = Math.max(1, cols - FINDING_INDENT.length - 2);
|
|
3013
3034
|
const rows = [`${HANDOFF_INDENT}${style.grey("Artifacts:")}`];
|
|
3014
3035
|
for (const artifact of artifacts) {
|
|
3015
|
-
const [first, ...rest] = wrap(sanitize((0,
|
|
3036
|
+
const [first, ...rest] = wrap(sanitize((0, import_core15.handoffReferenceText)(artifact)), room);
|
|
3016
3037
|
rows.push(`${FINDING_INDENT}- ${first}`, ...rest.map((line) => `${FINDING_INDENT} ${line}`));
|
|
3017
3038
|
}
|
|
3018
3039
|
return rows;
|
|
3019
3040
|
}
|
|
3020
3041
|
function handoffPatchLines(block2, cols) {
|
|
3021
3042
|
const room = Math.max(1, cols - FINDING_INDENT.length);
|
|
3022
|
-
return (0,
|
|
3043
|
+
return (0, import_core15.savedPatchViews)(block2.handoff).flatMap((patch) => [
|
|
3023
3044
|
...wrap(`Patch: ${patch.changes}, ${patch.base}`, Math.max(1, cols - HANDOFF_INDENT.length)).map((line, i) => `${HANDOFF_INDENT}${i === 0 ? style.grey(line) : line}`),
|
|
3024
3045
|
...wrap(patch.saved, room).map((line) => `${FINDING_INDENT}${style.grey(line)}`),
|
|
3025
3046
|
...patch.flags.flatMap((flag2) => wrap(flag2, room).map((line) => `${FINDING_INDENT}${style.grey(line)}`))
|
|
@@ -3027,7 +3048,7 @@ function handoffPatchLines(block2, cols) {
|
|
|
3027
3048
|
}
|
|
3028
3049
|
function handoffReportLines(report, cols, detailAll) {
|
|
3029
3050
|
const all = sanitize(report).replace(/\n+$/, "").split("\n");
|
|
3030
|
-
const shown = detailAll ? all : all.slice(0,
|
|
3051
|
+
const shown = detailAll ? all : all.slice(0, import_core15.REPORT_PREVIEW_LINES);
|
|
3031
3052
|
const room = Math.max(1, cols - FINDING_INDENT.length);
|
|
3032
3053
|
const rows = [
|
|
3033
3054
|
`${HANDOFF_INDENT}${style.grey("Report:")}`,
|
|
@@ -3035,13 +3056,13 @@ function handoffReportLines(report, cols, detailAll) {
|
|
|
3035
3056
|
];
|
|
3036
3057
|
const hidden = all.length - shown.length;
|
|
3037
3058
|
if (hidden > 0) rows.push(style.grey(`${FINDING_INDENT}\u2026 +${countOf(hidden, "line")} of the report (ctrl+o to expand)`));
|
|
3038
|
-
else if (detailAll && all.length >
|
|
3059
|
+
else if (detailAll && all.length > import_core15.REPORT_PREVIEW_LINES) rows.push(style.grey(`${FINDING_INDENT}\u2026 (ctrl+o to collapse)`));
|
|
3039
3060
|
return rows;
|
|
3040
3061
|
}
|
|
3041
3062
|
function handoffLines(block2, cols, detailAll) {
|
|
3042
3063
|
const { handoff } = block2;
|
|
3043
|
-
const severity = (0,
|
|
3044
|
-
const summary = handoff.findingCount > 0 ? `${(0,
|
|
3064
|
+
const severity = (0, import_core15.worstSeverity)(handoff.findings);
|
|
3065
|
+
const summary = handoff.findingCount > 0 ? `${(0, import_core15.handoffFindingsText)(handoff.findingCount)}${severity ? ` \xB7 ${(0, import_core15.severityLabel)(severity).toLowerCase()}` : ""}` : null;
|
|
3045
3066
|
const rows = [`${style.brand("\u25C6")} ${style.bold("Handoff")}${summary ? style.grey(` \xB7 ${summary}`) : ""}`];
|
|
3046
3067
|
if (handoff.commit) {
|
|
3047
3068
|
const room = Math.max(1, cols - HANDOFF_INDENT.length);
|
|
@@ -3062,7 +3083,7 @@ function handoffLines(block2, cols, detailAll) {
|
|
|
3062
3083
|
return rows;
|
|
3063
3084
|
}
|
|
3064
3085
|
function planLine(block2, cols) {
|
|
3065
|
-
const text = (0,
|
|
3086
|
+
const text = (0, import_core15.planMarkerParts)(block2).join(" \xB7 ");
|
|
3066
3087
|
return `${style.brand("\u25C7")}${truncate(`\u25C7 ${text}`, cols).slice(1)}`;
|
|
3067
3088
|
}
|
|
3068
3089
|
function blockLines(block2, cols, detailAll) {
|
|
@@ -3114,9 +3135,9 @@ function conversationLines(blocks2, cols, detailAll, answering) {
|
|
|
3114
3135
|
}
|
|
3115
3136
|
function tokenLine(blocks2, cols, limits = []) {
|
|
3116
3137
|
const usage = blocks2[blocks2.length - 1];
|
|
3117
|
-
const context = usage?.type === "usage" ? (0,
|
|
3118
|
-
const cost = usage?.type === "usage" ? (0,
|
|
3119
|
-
const text = [context ? `Planner ${context}` : null, ...limits.map(
|
|
3138
|
+
const context = usage?.type === "usage" ? (0, import_core15.contextMeterText)(usage.contextFill) : null;
|
|
3139
|
+
const cost = usage?.type === "usage" ? (0, import_core15.usageText)({ totals: usage.totals }) : null;
|
|
3140
|
+
const text = [context ? `Planner ${context}` : null, ...limits.map(import_core15.accountLimitsText), cost].filter(Boolean).join(" \xB7 ");
|
|
3120
3141
|
return text ? style.grey(truncate(sanitize(text).replace(/\n/g, " "), cols)) : null;
|
|
3121
3142
|
}
|
|
3122
3143
|
function assignmentFlagText(row) {
|
|
@@ -3125,7 +3146,7 @@ function assignmentFlagText(row) {
|
|
|
3125
3146
|
|
|
3126
3147
|
// src/commands/status.ts
|
|
3127
3148
|
var import_fs4 = require("fs");
|
|
3128
|
-
var
|
|
3149
|
+
var import_core16 = require("@ordewell/core");
|
|
3129
3150
|
async function handleStatus(subArgs, injectedApi) {
|
|
3130
3151
|
const sessionId = flag(subArgs, "--session-id");
|
|
3131
3152
|
const workspace = flag(subArgs, "--workspace") || process.cwd();
|
|
@@ -3193,42 +3214,49 @@ async function assignmentScope(api) {
|
|
|
3193
3214
|
}
|
|
3194
3215
|
function printOneSession(sessionId, meta, plan, runnerScope, run) {
|
|
3195
3216
|
const allTasks = allTasksOf(plan);
|
|
3196
|
-
const everyTask = (0,
|
|
3197
|
-
const { blocked, repeating } = (0,
|
|
3217
|
+
const everyTask = (0, import_core16.flattenTasks)(allTasks);
|
|
3218
|
+
const { blocked, repeating } = (0, import_core16.planBlocks)(everyTask.map((t) => ({ ...t, subtasks: [], dependencies: t.dependencies ?? [] })));
|
|
3198
3219
|
const holds = /* @__PURE__ */ new Map();
|
|
3199
|
-
|
|
3220
|
+
const repairs = /* @__PURE__ */ new Map();
|
|
3221
|
+
const stranded = /* @__PURE__ */ new Set();
|
|
3222
|
+
for (const { task } of (0, import_core16.walkPlan)(run?.tasks ?? [])) {
|
|
3200
3223
|
if (task.holdReason) holds.set(task.id, task.holdReason);
|
|
3224
|
+
if (task.loopStranded) stranded.add(task.id);
|
|
3225
|
+
if (task.isolation?.state === "conflict" && task.isolation.repairWaits) repairs.set(task.id, (0, import_core16.repairWaitLabel)(task.isolation.repairWaits));
|
|
3201
3226
|
}
|
|
3202
|
-
|
|
3227
|
+
if (!run) for (const id of repeating) stranded.add(id);
|
|
3228
|
+
const goesOn = (id) => repeating.has(id) && !stranded.has(id);
|
|
3229
|
+
const count = (status) => everyTask.filter((t) => t.status === status && !(status === "failed" && goesOn(t.id)) || status === "blocked" && blocked.has(t.id)).length;
|
|
3203
3230
|
console.log(`Session: ${sessionId}`);
|
|
3204
3231
|
console.log(`Goal: ${meta.goal}`);
|
|
3205
3232
|
console.log(`Runner: ${(meta.runners || []).join(", ")} | Status: ${meta.status}`);
|
|
3206
3233
|
console.log(`Phase: ${plan?.phase || "unknown"} | Created: ${meta.createdAt}`);
|
|
3207
|
-
const isolation = (0,
|
|
3208
|
-
if (isolation) console.log(`Base: ${(0,
|
|
3234
|
+
const isolation = (0, import_core16.isolationViewOfPlan)(plan);
|
|
3235
|
+
if (isolation) console.log(`Base: ${(0, import_core16.handoffBaseLine)(isolation.handoff, 8)}`);
|
|
3209
3236
|
console.log(`Tasks: ${everyTask.length} total | ${count("completed")} done | ${count("failed")} failed | ${count("in_progress")} running | ${count("blocked")} blocked | ${count("skipped")} skipped
|
|
3210
3237
|
`);
|
|
3211
|
-
const total = (0,
|
|
3238
|
+
const total = (0, import_core16.tasksTotalText)(allTasks);
|
|
3212
3239
|
if (total) console.log(total);
|
|
3213
3240
|
const record = servedApprovalRecord(plan);
|
|
3214
3241
|
const approval = (row, ref) => row.waitsForApproval ? `${row.waitsForApproval}: \`ordewell force-start ${ref}\` approves it, \`ordewell run\` approves all` : null;
|
|
3215
3242
|
for (const t of allTasks) {
|
|
3216
3243
|
const model = t.assignedModel ? ` (${t.assignedModel.modelLabel})` : "";
|
|
3217
3244
|
const shortId = (t.id ?? "").slice(-8);
|
|
3218
|
-
const row = (0,
|
|
3219
|
-
const icon = iconFor(blocked.has(t.id) ? "blocked" :
|
|
3220
|
-
console.log(` ${(0,
|
|
3245
|
+
const row = (0, import_core16.taskRowView)({ ...t, dependencies: t.dependencies ?? [], waitsForApproval: (0, import_core16.waitsForApproval)(t, record), holdReason: holds.get(t.id), loopStranded: stranded.has(t.id) }, { tasks: allTasks, runnerScope });
|
|
3246
|
+
const icon = iconFor(blocked.has(t.id) ? "blocked" : goesOn(t.id) ? "pending" : t.status);
|
|
3247
|
+
console.log(` ${(0, import_core16.taskOrderLabel)(t).padStart(2)}. ${icon} [${shortId}]${row.kind === "script" ? " [SCR]" : row.research ? " [RES]" : ""} ${t.title}${model}`);
|
|
3221
3248
|
if (t.type === "script") {
|
|
3222
|
-
for (const line of (0,
|
|
3249
|
+
for (const line of (0, import_core16.scriptTaskText)(t).split("\n")) console.log(line ? ` ${line.replace(/\t/g, " ")}` : "");
|
|
3223
3250
|
}
|
|
3224
|
-
const waiting = approval(row, (0,
|
|
3251
|
+
const waiting = approval(row, (0, import_core16.taskOrderLabel)(t));
|
|
3225
3252
|
if (waiting) console.log(` ${waiting}`);
|
|
3226
|
-
if (row.holdReason) console.log(` ${(0,
|
|
3253
|
+
if (row.holdReason) console.log(` ${(0, import_core16.holdLabel)(row.holdReason)}`);
|
|
3254
|
+
if (repairs.has(t.id)) console.log(` ${repairs.get(t.id)}`);
|
|
3227
3255
|
if (row.assignmentFlag) console.log(` \u26A0 ${assignmentFlagText(row)}`);
|
|
3228
|
-
if (row.blockedBy) console.log(` ${(0,
|
|
3229
|
-
if (row.loop) console.log(` \u21BB ${(0,
|
|
3230
|
-
if (row.status === "skipped") console.log(` ${(0,
|
|
3231
|
-
const usage = row.usage && (0,
|
|
3256
|
+
if (row.blockedBy) console.log(` ${(0, import_core16.blockedByLabel)(row.blockedBy)}`);
|
|
3257
|
+
if (row.loop) console.log(` \u21BB ${(0, import_core16.loopRowParts)(row.loop).join(" \xB7 ")}`);
|
|
3258
|
+
if (row.status === "skipped") console.log(` ${(0, import_core16.skippedLabel)(row.skipReason)}`);
|
|
3259
|
+
const usage = row.usage && (0, import_core16.usageText)(row.usage);
|
|
3232
3260
|
if (usage) console.log(` ${usage}`);
|
|
3233
3261
|
if (t.handoff && (t.status === "completed" || t.status === "failed")) {
|
|
3234
3262
|
for (const line of taskHandoffLines(t.handoff, " ")) console.log(line);
|
|
@@ -3237,15 +3265,16 @@ function printOneSession(sessionId, meta, plan, runnerScope, run) {
|
|
|
3237
3265
|
const subIcon = iconFor(blocked.has(sub.id) ? "blocked" : sub.status);
|
|
3238
3266
|
const subModel = sub.assignedModel ? ` (${sub.assignedModel.modelLabel})` : "";
|
|
3239
3267
|
const subShortId = (sub.id ?? "").slice(-8);
|
|
3240
|
-
console.log(` ${(0,
|
|
3241
|
-
const subRow = (0,
|
|
3242
|
-
const subWaiting = approval(subRow, (0,
|
|
3268
|
+
console.log(` ${(0, import_core16.taskOrderLabel)(sub, t)}. ${subIcon} [${subShortId}] ${sub.title}${subModel}`);
|
|
3269
|
+
const subRow = (0, import_core16.taskRowView)({ ...sub, dependencies: sub.dependencies ?? [], waitsForApproval: (0, import_core16.waitsForApproval)(sub, record), holdReason: holds.get(sub.id) }, { parent: t, tasks: allTasks, runnerScope });
|
|
3270
|
+
const subWaiting = approval(subRow, (0, import_core16.taskOrderLabel)(sub, t));
|
|
3243
3271
|
if (subWaiting) console.log(` ${subWaiting}`);
|
|
3244
|
-
if (subRow.holdReason) console.log(` ${(0,
|
|
3272
|
+
if (subRow.holdReason) console.log(` ${(0, import_core16.holdLabel)(subRow.holdReason)}`);
|
|
3273
|
+
if (repairs.has(sub.id)) console.log(` ${repairs.get(sub.id)}`);
|
|
3245
3274
|
if (subRow.assignmentFlag) console.log(` \u26A0 ${assignmentFlagText(subRow)}`);
|
|
3246
|
-
if (subRow.blockedBy) console.log(` ${(0,
|
|
3247
|
-
if (subRow.status === "skipped") console.log(` ${(0,
|
|
3248
|
-
const subUsage = subRow.usage && (0,
|
|
3275
|
+
if (subRow.blockedBy) console.log(` ${(0, import_core16.blockedByLabel)(subRow.blockedBy)}`);
|
|
3276
|
+
if (subRow.status === "skipped") console.log(` ${(0, import_core16.skippedLabel)(subRow.skipReason)}`);
|
|
3277
|
+
const subUsage = subRow.usage && (0, import_core16.usageText)(subRow.usage);
|
|
3249
3278
|
if (subUsage) console.log(` ${subUsage}`);
|
|
3250
3279
|
if (sub.handoff && (sub.status === "completed" || sub.status === "failed")) {
|
|
3251
3280
|
for (const line of taskHandoffLines(sub.handoff, " ")) console.log(line);
|
|
@@ -3304,28 +3333,28 @@ async function handleWeb(subArgs) {
|
|
|
3304
3333
|
}
|
|
3305
3334
|
|
|
3306
3335
|
// src/commands/models.ts
|
|
3307
|
-
var
|
|
3336
|
+
var import_core19 = require("@ordewell/core");
|
|
3308
3337
|
|
|
3309
3338
|
// src/commands/model.ts
|
|
3310
|
-
var
|
|
3339
|
+
var import_core18 = require("@ordewell/core");
|
|
3311
3340
|
|
|
3312
3341
|
// src/plannerModelSwitch.ts
|
|
3313
|
-
var
|
|
3342
|
+
var import_core17 = require("@ordewell/core");
|
|
3314
3343
|
function plannerSwitchRecall(settings) {
|
|
3315
3344
|
if (settings.switchRecall) return settings.switchRecall;
|
|
3316
3345
|
const model = settings.orchestratorModel;
|
|
3317
3346
|
return model ? { model, effort: settings.plannerThinkingEffort, source: "remembered" } : { model: "", effort: "", source: "none" };
|
|
3318
3347
|
}
|
|
3319
3348
|
function vendorModelRefusal(provider, model, pickPlanner) {
|
|
3320
|
-
const meta = (0,
|
|
3321
|
-
if (!(0,
|
|
3349
|
+
const meta = (0, import_core17.getProviderMeta)(provider);
|
|
3350
|
+
if (!(0, import_core17.runnerForProvider)(provider) || !meta || !(0, import_core17.isVendorModelFor)(meta.id, model)) return null;
|
|
3322
3351
|
return `${model} is a vendor's model, which ${meta.label} cannot run. To plan with that vendor, switch planner with ${pickPlanner}.`;
|
|
3323
3352
|
}
|
|
3324
3353
|
function uninstalledPlannerRefusal(provider, installed, pickPlanner) {
|
|
3325
|
-
const runner = (0,
|
|
3354
|
+
const runner = (0, import_core17.runnerForProvider)(provider);
|
|
3326
3355
|
if (!runner || installed.includes(runner)) return null;
|
|
3327
|
-
const agents =
|
|
3328
|
-
const label = (0,
|
|
3356
|
+
const agents = import_core17.CLI_PROVIDERS.filter((id) => installed.includes((0, import_core17.runnerForProvider)(id) ?? ""));
|
|
3357
|
+
const label = (0, import_core17.getProviderMeta)(provider)?.label ?? provider;
|
|
3329
3358
|
return agents.length > 0 ? `${label} is not installed or not on PATH. Installed coding agents: ${agents.join(", ")}. Switch with ${pickPlanner}.` : `${label} is not installed or not on PATH, and no other coding agent is. Install one, or plan with an API provider through ${pickPlanner}.`;
|
|
3330
3359
|
}
|
|
3331
3360
|
|
|
@@ -3344,7 +3373,7 @@ async function handleModel(subArgs, injectedApi) {
|
|
|
3344
3373
|
if (!modelId) fail(USAGE);
|
|
3345
3374
|
const settings = await api.getSettings();
|
|
3346
3375
|
const provider = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
|
|
3347
|
-
const runner = (0,
|
|
3376
|
+
const runner = (0, import_core18.runnerForProvider)(provider);
|
|
3348
3377
|
if (runner) {
|
|
3349
3378
|
const known = (await fetchCatalog(api)).models.filter((m) => m.runners?.includes(runner));
|
|
3350
3379
|
if (known.length > 0 && !known.some((m) => m.id === modelId)) {
|
|
@@ -3353,7 +3382,7 @@ async function handleModel(subArgs, injectedApi) {
|
|
|
3353
3382
|
`Run \`ordewell model\` to see what ${runner} offers.`
|
|
3354
3383
|
);
|
|
3355
3384
|
}
|
|
3356
|
-
const refusal = known.length === 0 ? vendorModelRefusal(provider, modelId,
|
|
3385
|
+
const refusal = known.length === 0 ? vendorModelRefusal(provider, modelId, import_core18.HEADLESS_CLI_COMMANDS.pickPlanner) : null;
|
|
3357
3386
|
if (refusal) fail(refusal);
|
|
3358
3387
|
}
|
|
3359
3388
|
const settled = await api.updateSettings({ orchestratorModel: modelId });
|
|
@@ -3369,7 +3398,7 @@ async function showModels(api) {
|
|
|
3369
3398
|
const [settings, catalog] = await Promise.all([api.getSettings(), fetchCatalog(api)]);
|
|
3370
3399
|
const provider = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
|
|
3371
3400
|
const current = typeof settings.orchestratorModel === "string" ? settings.orchestratorModel : "";
|
|
3372
|
-
const runner = (0,
|
|
3401
|
+
const runner = (0, import_core18.runnerForProvider)(provider);
|
|
3373
3402
|
const options = runner ? catalog.models.filter((m) => m.runners?.includes(runner)) : catalog.orchestratorModels;
|
|
3374
3403
|
console.log(`
|
|
3375
3404
|
Planner model: ${current || "(backend default)"}`);
|
|
@@ -3403,8 +3432,8 @@ async function handleModels(subArgs) {
|
|
|
3403
3432
|
const rest = subArgs.filter((_, i) => i !== setIdx && i !== setIdx + 1);
|
|
3404
3433
|
return handleModel(["set", subArgs[setIdx + 1], ...rest]);
|
|
3405
3434
|
}
|
|
3406
|
-
const config = new
|
|
3407
|
-
const providers = (0,
|
|
3435
|
+
const config = new import_core19.EnvConfig();
|
|
3436
|
+
const providers = (0, import_core19.configuredProviders)(config);
|
|
3408
3437
|
if (providers.length === 0) {
|
|
3409
3438
|
console.error("No providers configured. Set an API key for a provider, e.g.:");
|
|
3410
3439
|
console.error(" OPENROUTER_API_KEY=... (OpenRouter)");
|
|
@@ -3413,19 +3442,19 @@ async function handleModels(subArgs) {
|
|
|
3413
3442
|
console.error(" OPENAI_COMPATIBLE_BASE_URL=... (self-hosted / OpenAI-compatible)");
|
|
3414
3443
|
process.exit(1);
|
|
3415
3444
|
}
|
|
3416
|
-
const { apiKeys, baseUrls } = (0,
|
|
3445
|
+
const { apiKeys, baseUrls } = (0, import_core19.collectProviderCredentials)(config);
|
|
3417
3446
|
let models;
|
|
3418
3447
|
let errors;
|
|
3419
3448
|
try {
|
|
3420
|
-
const result = await (0,
|
|
3421
|
-
models = (0,
|
|
3449
|
+
const result = await (0, import_core19.fetchAllProviderModels)({ apiKeys, baseUrls });
|
|
3450
|
+
models = (0, import_core19.toOrchestratorOptions)(result.models, import_core19.ORCHESTRATOR_SHORTCUTS);
|
|
3422
3451
|
errors = result.errors;
|
|
3423
3452
|
} catch (err) {
|
|
3424
3453
|
console.error(`Failed to fetch models: ${err instanceof Error ? err.message : String(err)}`);
|
|
3425
3454
|
process.exit(1);
|
|
3426
3455
|
return;
|
|
3427
3456
|
}
|
|
3428
|
-
const providerLabel = (p) => (0,
|
|
3457
|
+
const providerLabel = (p) => (0, import_core19.getProviderMeta)(p)?.label ?? p;
|
|
3429
3458
|
console.log(
|
|
3430
3459
|
`
|
|
3431
3460
|
Available models (${models.length}) across ${providers.length} configured provider(s):`
|
|
@@ -3466,7 +3495,7 @@ ${providerLabel(provider)}`);
|
|
|
3466
3495
|
|
|
3467
3496
|
// src/commands/setup.ts
|
|
3468
3497
|
var import_fs6 = require("fs");
|
|
3469
|
-
var
|
|
3498
|
+
var import_core20 = require("@ordewell/core");
|
|
3470
3499
|
function handleSetup(subArgs) {
|
|
3471
3500
|
const modelIdx = subArgs.indexOf("--model");
|
|
3472
3501
|
const chosenModel = modelIdx !== -1 ? subArgs[modelIdx + 1] : null;
|
|
@@ -3538,13 +3567,13 @@ function handleSetup(subArgs) {
|
|
|
3538
3567
|
# STEPFUN_API_KEY=...
|
|
3539
3568
|
# SILICONFLOW_API_KEY=...
|
|
3540
3569
|
`;
|
|
3541
|
-
(0,
|
|
3570
|
+
(0, import_core20.writePrivateFile)(envFile, content);
|
|
3542
3571
|
console.log(`Wrote: ${envFile}`);
|
|
3543
3572
|
console.log("Review and edit the file, then run: ordewell tui");
|
|
3544
3573
|
}
|
|
3545
3574
|
|
|
3546
3575
|
// src/commands/skills.ts
|
|
3547
|
-
var
|
|
3576
|
+
var import_core21 = require("@ordewell/core");
|
|
3548
3577
|
var import_path5 = require("path");
|
|
3549
3578
|
function invocation(skill) {
|
|
3550
3579
|
if (skill.userInvocable && skill.modelInvocable) return "both";
|
|
@@ -3557,22 +3586,22 @@ function handleSkills(subArgs) {
|
|
|
3557
3586
|
fail("Usage: ordewell skills [--workspace /path] [--json]");
|
|
3558
3587
|
}
|
|
3559
3588
|
const workspace = (0, import_path5.resolve)(flag(subArgs, "--workspace") || process.cwd());
|
|
3560
|
-
const catalog = (0,
|
|
3589
|
+
const catalog = (0, import_core21.createSkillsService)(workspace, new import_core21.EnvConfig().workspaceRepos).readCatalog();
|
|
3561
3590
|
const skills = catalog.skills.map((skill) => ({
|
|
3562
3591
|
name: skill.name,
|
|
3563
3592
|
scope: skill.source,
|
|
3564
3593
|
appliesTo: skill.appliesTo,
|
|
3565
3594
|
invocation: invocation(skill),
|
|
3566
|
-
path: (0,
|
|
3595
|
+
path: (0, import_core21.abbreviateHome)(skill.path)
|
|
3567
3596
|
}));
|
|
3568
3597
|
const shadowed = catalog.shadowed.map(({ skill, shadowedBy }) => ({
|
|
3569
3598
|
name: skill.name,
|
|
3570
|
-
path: (0,
|
|
3571
|
-
shadowedBy: { scope: shadowedBy.source, path: (0,
|
|
3599
|
+
path: (0, import_core21.abbreviateHome)(skill.path),
|
|
3600
|
+
shadowedBy: { scope: shadowedBy.source, path: (0, import_core21.abbreviateHome)(shadowedBy.path) }
|
|
3572
3601
|
}));
|
|
3573
3602
|
const invalid = catalog.invalid.map((skill) => ({
|
|
3574
3603
|
name: skill.name,
|
|
3575
|
-
path: (0,
|
|
3604
|
+
path: (0, import_core21.abbreviateHome)(skill.path),
|
|
3576
3605
|
reason: skill.reason
|
|
3577
3606
|
}));
|
|
3578
3607
|
if (hasFlag(subArgs, "--json")) {
|
|
@@ -3687,10 +3716,10 @@ async function handleShow(args, api) {
|
|
|
3687
3716
|
}
|
|
3688
3717
|
|
|
3689
3718
|
// src/commands/mark-complete.ts
|
|
3690
|
-
var
|
|
3719
|
+
var import_core23 = require("@ordewell/core");
|
|
3691
3720
|
|
|
3692
3721
|
// src/commands/task-control.ts
|
|
3693
|
-
var
|
|
3722
|
+
var import_core22 = require("@ordewell/core");
|
|
3694
3723
|
async function sessionAndRef(subArgs, usage, injectedApi) {
|
|
3695
3724
|
const workspace = flag(subArgs, "--workspace") || process.cwd();
|
|
3696
3725
|
let sessionId = flag(subArgs, "--session-id");
|
|
@@ -3735,7 +3764,7 @@ async function confirmPastGate(subArgs, api, sessionId, ref, confirm2) {
|
|
|
3735
3764
|
const unmerged = await api.getMergeGate(sessionId, ref).catch(() => []);
|
|
3736
3765
|
if (unmerged.length === 0) return false;
|
|
3737
3766
|
if (hasFlag(subArgs, "--yes")) return true;
|
|
3738
|
-
const question = `${(0,
|
|
3767
|
+
const question = `${(0, import_core22.pastGateConfirmation)(unmerged.map((d) => (0, import_core22.titledTaskRef)(d)))} Start it anyway?`;
|
|
3739
3768
|
if (!await confirm2(question)) fail("Not started: nothing was changed. Pass --yes to start it without a prompt.");
|
|
3740
3769
|
return true;
|
|
3741
3770
|
}
|
|
@@ -3750,7 +3779,7 @@ function makeHandler(action, command = action) {
|
|
|
3750
3779
|
const pastGate = PASSES_GATE.has(action) ? await confirmPastGate(subArgs, api, sessionId, ref, confirm2) : false;
|
|
3751
3780
|
const control = pastGate && action === "run" ? "force-start" : action;
|
|
3752
3781
|
try {
|
|
3753
|
-
const { message } = await api.execute(sessionId, (0,
|
|
3782
|
+
const { message } = await api.execute(sessionId, (0, import_core22.taskActionCommand)(control, ref));
|
|
3754
3783
|
console.log(message);
|
|
3755
3784
|
} catch (err) {
|
|
3756
3785
|
console.error(`Failed to ${control} task: ${err.message}`);
|
|
@@ -3807,17 +3836,17 @@ var handleCancel = makeHandler("cancel");
|
|
|
3807
3836
|
|
|
3808
3837
|
// src/commands/mark-complete.ts
|
|
3809
3838
|
async function overrideCommand(direction, subArgs, api, sessionId, ref, confirm2) {
|
|
3810
|
-
const command = (0,
|
|
3811
|
-
if (hasFlag(subArgs, "--yes")) return (0,
|
|
3839
|
+
const command = (0, import_core23.taskActionCommand)(direction, ref);
|
|
3840
|
+
if (hasFlag(subArgs, "--yes")) return (0, import_core23.confirmedOverride)(command);
|
|
3812
3841
|
const live = await api.getSession(sessionId, flag(subArgs, "--workspace") || process.cwd()).catch(() => null);
|
|
3813
3842
|
const tasks = live ? allTasksOf(live.plan) : [];
|
|
3814
|
-
const task = (0,
|
|
3815
|
-
if (!task || !(0,
|
|
3816
|
-
const label = `task ${(0,
|
|
3817
|
-
if (live?.run?.activity?.advancesOnItsOwn) fail((0,
|
|
3818
|
-
const question = `${(0,
|
|
3843
|
+
const task = (0, import_core23.resolveUserTaskRef)(tasks, ref);
|
|
3844
|
+
if (!task || !(0, import_core23.overrideNeedsConfirmation)(task)) return command;
|
|
3845
|
+
const label = `task ${(0, import_core23.titledTaskRefOf)(tasks, task.id) ?? ref}`;
|
|
3846
|
+
if (live?.run?.activity?.advancesOnItsOwn) fail((0, import_core23.manualOverrideStopFirst)(direction, label));
|
|
3847
|
+
const question = `${(0, import_core23.manualOverrideConfirmation)(direction, label)} Go ahead?`;
|
|
3819
3848
|
if (!await confirm2(question)) fail("Nothing was changed. Pass --yes to confirm without a prompt.");
|
|
3820
|
-
return (0,
|
|
3849
|
+
return (0, import_core23.confirmedOverride)(command, task.status);
|
|
3821
3850
|
}
|
|
3822
3851
|
async function handleMarkComplete(subArgs, api, confirm2 = askYesNo) {
|
|
3823
3852
|
await withTaskRef(
|
|
@@ -3860,7 +3889,7 @@ async function handleSkip(subArgs, api) {
|
|
|
3860
3889
|
api,
|
|
3861
3890
|
async (client, sessionId, ref) => {
|
|
3862
3891
|
try {
|
|
3863
|
-
const done = await client.execute(sessionId, (0,
|
|
3892
|
+
const done = await client.execute(sessionId, (0, import_core23.taskActionCommand)("skip", ref, flag(subArgs, "--reason")));
|
|
3864
3893
|
console.log(done.message);
|
|
3865
3894
|
} catch (err) {
|
|
3866
3895
|
console.error(`Failed to skip task: ${err.message}`);
|
|
@@ -3870,6 +3899,32 @@ async function handleSkip(subArgs, api) {
|
|
|
3870
3899
|
);
|
|
3871
3900
|
}
|
|
3872
3901
|
|
|
3902
|
+
// src/commands/revert.ts
|
|
3903
|
+
var import_core24 = require("@ordewell/core");
|
|
3904
|
+
async function handleRevert(subArgs, api, confirm2 = askYesNo) {
|
|
3905
|
+
await withTaskRef(
|
|
3906
|
+
subArgs,
|
|
3907
|
+
"Usage: ordewell revert <task-id-or-order> [--session-id <id>] [--workspace /path] [--yes]",
|
|
3908
|
+
api,
|
|
3909
|
+
async (client, sessionId, ref) => {
|
|
3910
|
+
try {
|
|
3911
|
+
const preview = await client.revertPreview(sessionId, ref);
|
|
3912
|
+
if (!preview.revertible) fail(preview.message);
|
|
3913
|
+
console.log(`${import_core24.revertEffect}
|
|
3914
|
+
${preview.tasks.map((t) => ` ${(0, import_core24.revertListLine)(t)}`).join("\n")}`);
|
|
3915
|
+
if (!hasFlag(subArgs, "--yes") && !await confirm2("Revert these?")) {
|
|
3916
|
+
fail("Nothing was changed. Pass --yes to confirm without a prompt.");
|
|
3917
|
+
}
|
|
3918
|
+
const done = await client.execute(sessionId, { command: "revert_task", task: ref, confirmed: true, seen: preview.tasks.map((t) => t.taskId) });
|
|
3919
|
+
console.log(done.message);
|
|
3920
|
+
} catch (err) {
|
|
3921
|
+
console.error(`Failed to revert task: ${err.message}`);
|
|
3922
|
+
process.exit(1);
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
);
|
|
3926
|
+
}
|
|
3927
|
+
|
|
3873
3928
|
// src/commands/add-task.ts
|
|
3874
3929
|
var import_plan_utils = require("@ordewell/core/plan-utils");
|
|
3875
3930
|
async function handleAddTask(subArgs, injectedApi) {
|
|
@@ -4119,17 +4174,17 @@ async function handleResend(subArgs, injectedApi) {
|
|
|
4119
4174
|
}
|
|
4120
4175
|
|
|
4121
4176
|
// src/commands/handoff.ts
|
|
4122
|
-
var
|
|
4177
|
+
var import_core27 = require("@ordewell/core");
|
|
4123
4178
|
|
|
4124
4179
|
// src/tui/transcript.ts
|
|
4125
|
-
var
|
|
4180
|
+
var import_core25 = require("@ordewell/core");
|
|
4126
4181
|
function show(state, { view: conversation, gate: turnGate }) {
|
|
4127
4182
|
const gated = turnGate === state.turnGate ? state : { ...state, turnGate };
|
|
4128
4183
|
if (conversation === state.conversation) return gated;
|
|
4129
4184
|
return { ...gated, conversation };
|
|
4130
4185
|
}
|
|
4131
4186
|
function fold(state, input) {
|
|
4132
|
-
return show(state, { view: (0,
|
|
4187
|
+
return show(state, { view: (0, import_core25.reduceConversation)(state.conversation, input), gate: state.turnGate });
|
|
4133
4188
|
}
|
|
4134
4189
|
function say(state, role, text, followTail = true) {
|
|
4135
4190
|
const said = fold(state, { type: "local_entry", role, text });
|
|
@@ -4137,10 +4192,10 @@ function say(state, role, text, followTail = true) {
|
|
|
4137
4192
|
return said.conversation.nextId > state.conversation.nextId ? { ...said, scroll: 0 } : said;
|
|
4138
4193
|
}
|
|
4139
4194
|
function hear(state, message) {
|
|
4140
|
-
return show(state, (0,
|
|
4195
|
+
return show(state, (0, import_core25.followTurn)(state.conversation, state.turnGate, message));
|
|
4141
4196
|
}
|
|
4142
4197
|
function cutTurn(state) {
|
|
4143
|
-
return show(state, (0,
|
|
4198
|
+
return show(state, (0, import_core25.stopTurn)(state.conversation, state.turnGate));
|
|
4144
4199
|
}
|
|
4145
4200
|
function endRunningCompactions(state, outcome) {
|
|
4146
4201
|
let next = state;
|
|
@@ -4158,7 +4213,7 @@ function wiped(view) {
|
|
|
4158
4213
|
}
|
|
4159
4214
|
|
|
4160
4215
|
// src/tui/handoff.ts
|
|
4161
|
-
var
|
|
4216
|
+
var import_core26 = require("@ordewell/core");
|
|
4162
4217
|
var HANDOFF_ACTIONS = [
|
|
4163
4218
|
{ id: "review", label: "Review diff", hint: "the branch against the commit it forked from" },
|
|
4164
4219
|
{ id: "merge", label: "Merge", hint: "into the branch you have checked out (asks first)" },
|
|
@@ -4166,7 +4221,7 @@ var HANDOFF_ACTIONS = [
|
|
|
4166
4221
|
{ id: "cleanup", label: "Clean up", hint: "remove worktrees and task branches, keep this branch" }
|
|
4167
4222
|
];
|
|
4168
4223
|
function handoffActions(handoff) {
|
|
4169
|
-
if (!(0,
|
|
4224
|
+
if (!(0, import_core26.isRepoGroup)(handoff)) return HANDOFF_ACTIONS;
|
|
4170
4225
|
return [
|
|
4171
4226
|
{ id: "review", label: "Review diff", hint: "each repository's branch against the commit it forked from" },
|
|
4172
4227
|
{ id: "merge", label: "Merge all", hint: "every repository's branch into what you have checked out there (asks first)" },
|
|
@@ -4191,14 +4246,14 @@ function handoffCommand(state, arg) {
|
|
|
4191
4246
|
}
|
|
4192
4247
|
function handoffArrived(state, handoff) {
|
|
4193
4248
|
const landed = handoff.landed.length;
|
|
4194
|
-
const where = (0,
|
|
4195
|
-
const branch = (0,
|
|
4249
|
+
const where = (0, import_core26.isRepoGroup)(handoff) ? ` in ${reposOf(handoff)}` : "";
|
|
4250
|
+
const branch = (0, import_core26.handoffBranchName)(handoff);
|
|
4196
4251
|
const tasks = `${landed} task${landed === 1 ? "" : "s"} landed`;
|
|
4197
4252
|
const received = { ...state, handoff };
|
|
4198
4253
|
if (handoff.deferredTo === "supervisor") {
|
|
4199
4254
|
return say(received, "system", `Run finished on ${branch}${where}: ${tasks}. The supervisor may merge it; /handoff to review it yourself.`);
|
|
4200
4255
|
}
|
|
4201
|
-
const returned = state.handoff?.deferredTo === "supervisor" && (0,
|
|
4256
|
+
const returned = state.handoff?.deferredTo === "supervisor" && (0, import_core26.handoffBranchName)(state.handoff) === branch;
|
|
4202
4257
|
const told = say(
|
|
4203
4258
|
received,
|
|
4204
4259
|
"system",
|
|
@@ -4215,7 +4270,7 @@ function runHandoffAction(state, id) {
|
|
|
4215
4270
|
case "review":
|
|
4216
4271
|
return { state, effects: [{ type: "isolationReviewDiff", sessionId }] };
|
|
4217
4272
|
case "cleanup":
|
|
4218
|
-
return { state: closed, effects: [{ type: "isolationCleanup", sessionId, branch: (0,
|
|
4273
|
+
return { state: closed, effects: [{ type: "isolationCleanup", sessionId, branch: (0, import_core26.handoffBranchName)(handoff) }] };
|
|
4219
4274
|
case "merge":
|
|
4220
4275
|
if (midRun(state)) {
|
|
4221
4276
|
return {
|
|
@@ -4223,8 +4278,8 @@ function runHandoffAction(state, id) {
|
|
|
4223
4278
|
...state,
|
|
4224
4279
|
overlay: {
|
|
4225
4280
|
kind: "confirm",
|
|
4226
|
-
title: (0,
|
|
4227
|
-
message: `Merge ${(0,
|
|
4281
|
+
title: (0, import_core26.isRepoGroup)(handoff) ? "Merge what has landed into your branches?" : "Merge what has landed into your branch?",
|
|
4282
|
+
message: `Merge ${(0, import_core26.handoffBranchName)(handoff)} as it stands now into ${(0, import_core26.mergeTarget)(handoff)}.${(0, import_core26.checkoutRule)(handoff)} The run goes on: tasks waiting for Merge all start once it has merged, and later tasks land on the same branch for the next Merge all. A merge that conflicts is aborted, and your ${(0, import_core26.isRepoGroup)(handoff) ? "trees stay" : "tree stays"} as ${(0, import_core26.isRepoGroup)(handoff) ? "they were" : "it was"}.${(0, import_core26.repairedNotice)(handoff)}`,
|
|
4228
4283
|
action: { kind: "merge-run" }
|
|
4229
4284
|
}
|
|
4230
4285
|
},
|
|
@@ -4234,15 +4289,15 @@ function runHandoffAction(state, id) {
|
|
|
4234
4289
|
return {
|
|
4235
4290
|
state: {
|
|
4236
4291
|
...state,
|
|
4237
|
-
overlay: (0,
|
|
4292
|
+
overlay: (0, import_core26.isRepoGroup)(handoff) ? {
|
|
4238
4293
|
kind: "confirm",
|
|
4239
4294
|
title: "Merge all into your branches?",
|
|
4240
|
-
message: `Merge ${(0,
|
|
4295
|
+
message: `Merge ${(0, import_core26.handoffBranchName)(handoff)} into ${(0, import_core26.mergeTarget)(handoff)}.${(0, import_core26.checkoutRule)(handoff)} It checks every repository first and merges none unless every repository can take it; on git older than 2.38 it goes repository by repository and stops at the first that fails. A merge that conflicts is aborted, and your trees stay as they were. Once every repository has merged, the run's worktrees and branches are removed unless a task still has work in its worktree that has not landed: then the run stays, and so does that work.${(0, import_core26.repairedNotice)(handoff)}`,
|
|
4241
4296
|
action: { kind: "merge-run" }
|
|
4242
4297
|
} : {
|
|
4243
4298
|
kind: "confirm",
|
|
4244
4299
|
title: "Merge into your branch?",
|
|
4245
|
-
message: `Merge ${(0,
|
|
4300
|
+
message: `Merge ${(0, import_core26.handoffBranchName)(handoff)} into ${(0, import_core26.mergeTarget)(handoff)}.${(0, import_core26.checkoutRule)(handoff)} If it conflicts the merge is aborted and your tree stays as it was. Once it has merged, the run's worktrees and branches are removed unless a task still has work in its worktree that has not landed: then the run stays, and so does that work.${(0, import_core26.repairedNotice)(handoff)}`,
|
|
4246
4301
|
action: { kind: "merge-run" }
|
|
4247
4302
|
}
|
|
4248
4303
|
},
|
|
@@ -4255,7 +4310,7 @@ function runHandoffAction(state, id) {
|
|
|
4255
4310
|
overlay: {
|
|
4256
4311
|
kind: "confirm",
|
|
4257
4312
|
title: "Discard this run?",
|
|
4258
|
-
message: `Removes the run's worktrees and task branches and deletes ${(0,
|
|
4313
|
+
message: `Removes the run's worktrees and task branches and deletes ${(0, import_core26.handoffBranchName)(handoff)}${(0, import_core26.isRepoGroup)(handoff) ? ` in ${reposOf(handoff)}` : ""}. Tasks keep their status: mark one not done if you did not keep its work.`,
|
|
4259
4314
|
action: { kind: "discard-run" }
|
|
4260
4315
|
}
|
|
4261
4316
|
},
|
|
@@ -4268,19 +4323,19 @@ function confirmedHandoff(state, kind) {
|
|
|
4268
4323
|
const handoff = currentHandoff(state);
|
|
4269
4324
|
if (!state.sessionId || !handoff) return { state: closed, effects: [] };
|
|
4270
4325
|
const { sessionId } = state;
|
|
4271
|
-
const repaired = (0,
|
|
4326
|
+
const repaired = (0, import_core26.repairedLanded)(handoff);
|
|
4272
4327
|
const effect = kind === "merge-run" ? {
|
|
4273
4328
|
type: "isolationMerge",
|
|
4274
4329
|
sessionId,
|
|
4275
|
-
branch: (0,
|
|
4276
|
-
...(0,
|
|
4330
|
+
branch: (0, import_core26.handoffBranchName)(handoff),
|
|
4331
|
+
...(0, import_core26.isRepoGroup)(handoff) ? { group: true } : {},
|
|
4277
4332
|
...repaired.length > 0 ? { repaired } : {}
|
|
4278
|
-
} : { type: "isolationDiscard", sessionId, branch: (0,
|
|
4333
|
+
} : { type: "isolationDiscard", sessionId, branch: (0, import_core26.handoffBranchName)(handoff) };
|
|
4279
4334
|
return { state: closed, effects: [effect] };
|
|
4280
4335
|
}
|
|
4281
4336
|
function showDiff(state, diff) {
|
|
4282
4337
|
const handoff = currentHandoff(state);
|
|
4283
|
-
if (diff.trim() === "") return say(state, "system", `Nothing differs from ${handoff ? (0,
|
|
4338
|
+
if (diff.trim() === "") return say(state, "system", `Nothing differs from ${handoff ? (0, import_core26.handoffBaseLine)(handoff, 8) : "the base commit"}.`);
|
|
4284
4339
|
const index = state.overlay?.kind === "handoff" ? state.overlay.index : 0;
|
|
4285
4340
|
const lines = sanitize(diff.replace(/\t/g, " ")).split("\n");
|
|
4286
4341
|
return { ...state, overlay: { kind: "handoff", index, diff: { lines, scroll: 0 } } };
|
|
@@ -4354,11 +4409,11 @@ async function handleHandoff(subArgs, injectedApi, confirm2 = askYesNo) {
|
|
|
4354
4409
|
}
|
|
4355
4410
|
if (!HANDOFF_ACTIONS.some((a) => a.id === action)) fail(`Unknown handoff action "${action}".`, USAGE4);
|
|
4356
4411
|
const { api, sessionId, plan } = await adopted(subArgs, injectedApi);
|
|
4357
|
-
const handoff = (0,
|
|
4412
|
+
const handoff = (0, import_core27.isolationViewOfPlan)(plan)?.handoff;
|
|
4358
4413
|
if (!handoff) fail("This session has no isolated run to hand off.");
|
|
4359
|
-
const branch = (0,
|
|
4360
|
-
const group = (0,
|
|
4361
|
-
if (group) for (const line of (0,
|
|
4414
|
+
const branch = (0, import_core27.handoffBranchName)(handoff);
|
|
4415
|
+
const group = (0, import_core27.isRepoGroup)(handoff);
|
|
4416
|
+
if (group) for (const line of (0, import_core27.repoResultLines)(handoff)) console.error(line);
|
|
4362
4417
|
const asked = hasFlag(subArgs, "--yes");
|
|
4363
4418
|
const confirmed = async (question) => {
|
|
4364
4419
|
if (asked || await confirm2(question)) return;
|
|
@@ -4374,12 +4429,12 @@ async function handleHandoff(subArgs, injectedApi, confirm2 = askYesNo) {
|
|
|
4374
4429
|
switch (action) {
|
|
4375
4430
|
case "review": {
|
|
4376
4431
|
const diff = await attempt(() => api.reviewRunDiff(sessionId));
|
|
4377
|
-
console.log(diff.trim() === "" ? `Nothing differs from ${(0,
|
|
4432
|
+
console.log(diff.trim() === "" ? `Nothing differs from ${(0, import_core27.handoffBaseLine)(handoff, 8)}.` : diff);
|
|
4378
4433
|
return;
|
|
4379
4434
|
}
|
|
4380
4435
|
case "merge": {
|
|
4381
|
-
await confirmed((group ? `Merge ${branch} into ${(0,
|
|
4382
|
-
const { ok, message } = (0,
|
|
4436
|
+
await confirmed((group ? `Merge ${branch} into ${(0, import_core27.mergeTarget)(handoff)}? Nothing is merged unless every repository can take it${(0, import_core27.checkoutRule)(handoff) ? " and is on that branch" : ""}; once a finished run has merged everything, its worktrees and branches are removed.` : `Merge ${branch} into ${(0, import_core27.mergeTarget)(handoff)}?${(0, import_core27.checkoutRule)(handoff)} Once a finished run has merged everything, its worktrees and branches are removed.`) + (0, import_core27.repairedNotice)(handoff));
|
|
4437
|
+
const { ok, message } = (0, import_core27.mergeOutcome)(await attempt(() => api.mergeRun(sessionId)), branch, group, (0, import_core27.repairedLanded)(handoff));
|
|
4383
4438
|
if (ok) console.log(message);
|
|
4384
4439
|
else fail(message);
|
|
4385
4440
|
return;
|
|
@@ -4397,7 +4452,7 @@ async function handleHandoff(subArgs, injectedApi, confirm2 = askYesNo) {
|
|
|
4397
4452
|
}
|
|
4398
4453
|
|
|
4399
4454
|
// src/commands/planner.ts
|
|
4400
|
-
var
|
|
4455
|
+
var import_core28 = require("@ordewell/core");
|
|
4401
4456
|
async function handlePlanner(subArgs, injectedApi) {
|
|
4402
4457
|
const api = await connect(subArgs, injectedApi);
|
|
4403
4458
|
const [wanted] = positionals(subArgs);
|
|
@@ -4406,13 +4461,13 @@ async function handlePlanner(subArgs, injectedApi) {
|
|
|
4406
4461
|
return;
|
|
4407
4462
|
}
|
|
4408
4463
|
const provider = wanted.toLowerCase();
|
|
4409
|
-
const meta = (0,
|
|
4464
|
+
const meta = (0, import_core28.getProviderMeta)(provider);
|
|
4410
4465
|
if (!meta) {
|
|
4411
4466
|
fail(`Unknown planner: ${provider}`, "Run `ordewell planner` with no arguments to see the list.");
|
|
4412
4467
|
}
|
|
4413
|
-
if ((0,
|
|
4468
|
+
if ((0, import_core28.isCliProvider)(provider)) {
|
|
4414
4469
|
const installed = (await api.getRunners()).runners.map((r) => r.id);
|
|
4415
|
-
const refusal = uninstalledPlannerRefusal(provider, installed,
|
|
4470
|
+
const refusal = uninstalledPlannerRefusal(provider, installed, import_core28.HEADLESS_CLI_COMMANDS.pickPlanner);
|
|
4416
4471
|
if (refusal) fail(refusal);
|
|
4417
4472
|
}
|
|
4418
4473
|
const settings = await persistEnv(api, { AI_PROVIDER: provider });
|
|
@@ -4420,10 +4475,10 @@ async function handlePlanner(subArgs, injectedApi) {
|
|
|
4420
4475
|
writeEnv({ ORCHESTRATOR_MODEL: recall2.model, ORDEWELL_PLANNER_EFFORT: recall2.effort });
|
|
4421
4476
|
const catalog = await fetchCatalog(api);
|
|
4422
4477
|
console.log(
|
|
4423
|
-
(0,
|
|
4478
|
+
(0, import_core28.isCliProvider)(provider) ? `Planning with ${meta.label}: no API key needed, it uses that agent's own subscription.` : `Planner set to ${meta.label}.`
|
|
4424
4479
|
);
|
|
4425
4480
|
console.log(plannerModelLine(recall2));
|
|
4426
|
-
if ((0,
|
|
4481
|
+
if ((0, import_core28.isApiProvider)(provider) && !catalog.providers.includes(provider)) {
|
|
4427
4482
|
console.log(` No ${meta.apiKeyEnvVar} configured yet: set one with \`ordewell key set ${provider} <key>\`.`);
|
|
4428
4483
|
}
|
|
4429
4484
|
}
|
|
@@ -4441,15 +4496,15 @@ async function showPlanners(api) {
|
|
|
4441
4496
|
const [settings, catalog] = await Promise.all([api.getSettings(), fetchCatalog(api)]);
|
|
4442
4497
|
const current = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
|
|
4443
4498
|
console.log("\nCoding agents (no API key; they use their own subscription)");
|
|
4444
|
-
for (const id of
|
|
4445
|
-
const runner = (0,
|
|
4499
|
+
for (const id of import_core28.CLI_PROVIDERS) {
|
|
4500
|
+
const runner = (0, import_core28.runnerForProvider)(id);
|
|
4446
4501
|
const discovered = runner ? catalog.models.some((m) => m.runners?.includes(runner)) : false;
|
|
4447
|
-
console.log(` ${marker(id === current)} ${id.padEnd(14)} ${(0,
|
|
4502
|
+
console.log(` ${marker(id === current)} ${id.padEnd(14)} ${(0, import_core28.getProviderMeta)(id)?.label ?? id}${discovered ? "" : " (not detected)"}`);
|
|
4448
4503
|
}
|
|
4449
4504
|
console.log("\nAPI providers");
|
|
4450
|
-
for (const id of
|
|
4505
|
+
for (const id of import_core28.PROVIDER_PRIORITY) {
|
|
4451
4506
|
const configured = catalog.providers.includes(id);
|
|
4452
|
-
const meta = (0,
|
|
4507
|
+
const meta = (0, import_core28.getProviderMeta)(id);
|
|
4453
4508
|
console.log(` ${marker(id === current)} ${id.padEnd(14)} ${meta.label}${configured ? "" : ` (no ${meta.apiKeyEnvVar})`}`);
|
|
4454
4509
|
}
|
|
4455
4510
|
console.log(`
|
|
@@ -4466,7 +4521,7 @@ async function handlePlannerEffort(subArgs, injectedApi) {
|
|
|
4466
4521
|
const provider = typeof settings.aiProvider === "string" ? settings.aiProvider : "";
|
|
4467
4522
|
const modelId = typeof settings.orchestratorModel === "string" ? settings.orchestratorModel : "";
|
|
4468
4523
|
const currentEffort = typeof settings.plannerThinkingEffort === "string" ? settings.plannerThinkingEffort : "";
|
|
4469
|
-
const runner = (0,
|
|
4524
|
+
const runner = (0, import_core28.runnerForProvider)(provider);
|
|
4470
4525
|
if (!runner) {
|
|
4471
4526
|
fail(
|
|
4472
4527
|
"Thinking effort applies to a coding-agent planner.",
|
|
@@ -4506,7 +4561,7 @@ async function handlePlannerEffort(subArgs, injectedApi) {
|
|
|
4506
4561
|
}
|
|
4507
4562
|
|
|
4508
4563
|
// src/commands/parallel.ts
|
|
4509
|
-
var
|
|
4564
|
+
var import_core29 = require("@ordewell/core");
|
|
4510
4565
|
var USAGE5 = "Usage: ordewell parallel [<number of tasks, 1 or more>]";
|
|
4511
4566
|
async function handleParallel(subArgs, injectedApi) {
|
|
4512
4567
|
const [wanted] = positionals(subArgs);
|
|
@@ -4516,18 +4571,19 @@ async function handleParallel(subArgs, injectedApi) {
|
|
|
4516
4571
|
console.log(`Up to ${String(settings.maxParallel ?? "?")} AI tasks run at once. Change with: ordewell parallel <n>`);
|
|
4517
4572
|
return;
|
|
4518
4573
|
}
|
|
4519
|
-
const limit = (0,
|
|
4574
|
+
const limit = (0, import_core29.parseMaxParallel)(wanted);
|
|
4520
4575
|
if (limit === null) fail(`"${wanted}" is not a number of tasks (1 or more).`, USAGE5);
|
|
4521
4576
|
await persistEnv(api, { ORDEWELL_MAX_PARALLEL: String(limit) });
|
|
4522
4577
|
console.log(`Up to ${limit} AI task${limit === 1 ? "" : "s"} now run at once.`);
|
|
4523
4578
|
}
|
|
4524
4579
|
|
|
4525
4580
|
// src/commands/runSettings.ts
|
|
4526
|
-
var
|
|
4581
|
+
var import_core30 = require("@ordewell/core");
|
|
4527
4582
|
var SUPERVISOR_USAGE = "Usage: ordewell supervisor [off|watch|act] \xB7 ordewell supervisor retries <n> \xB7 ordewell supervisor reads <n>";
|
|
4528
4583
|
var AUTOMERGE_USAGE = "Usage: ordewell automerge [on|off]";
|
|
4529
4584
|
var TRAILER_USAGE = "Usage: ordewell trailer [on|off]";
|
|
4530
4585
|
var AUTORUN_USAGE = "Usage: ordewell autorun [on|off]";
|
|
4586
|
+
var DELEGATION_USAGE = "Usage: ordewell delegation [off|<tokens>]";
|
|
4531
4587
|
async function save(api, changes) {
|
|
4532
4588
|
await api.updateRunSettings(changes);
|
|
4533
4589
|
}
|
|
@@ -4535,28 +4591,28 @@ async function handleSupervisor(subArgs, injectedApi) {
|
|
|
4535
4591
|
const [arg, value] = positionals(subArgs);
|
|
4536
4592
|
let changes = null;
|
|
4537
4593
|
if (arg === "off" || arg === "watch" || arg === "act") changes = { supervisorLevel: arg };
|
|
4538
|
-
else if (arg === "retries") changes = maybe((0,
|
|
4539
|
-
else if (arg === "reads") changes = maybe((0,
|
|
4594
|
+
else if (arg === "retries") changes = maybe((0, import_core30.parseCount)(value, 0), (n) => ({ supervisorRetryLimit: n }));
|
|
4595
|
+
else if (arg === "reads") changes = maybe((0, import_core30.parseCount)(value, 1), (n) => ({ wakeUpReadBudget: n }));
|
|
4540
4596
|
if (arg !== void 0 && changes === null) fail(SUPERVISOR_USAGE);
|
|
4541
4597
|
const api = await connect(subArgs, injectedApi);
|
|
4542
4598
|
if (!changes) {
|
|
4543
|
-
console.log((0,
|
|
4599
|
+
console.log((0, import_core30.supervisorSummary)((await api.getSettings()).run));
|
|
4544
4600
|
console.log(` ${SUPERVISOR_USAGE}`);
|
|
4545
4601
|
return;
|
|
4546
4602
|
}
|
|
4547
4603
|
await save(api, changes);
|
|
4548
|
-
if (changes.supervisorLevel) console.log(`Supervisor: ${
|
|
4549
|
-
if (changes.supervisorLevel === "act") console.log(
|
|
4604
|
+
if (changes.supervisorLevel) console.log(`Supervisor: ${import_core30.SUPERVISOR_LEVEL_LABEL[changes.supervisorLevel]}, for new sessions.`);
|
|
4605
|
+
if (changes.supervisorLevel === "act") console.log(import_core30.ACT_RISK);
|
|
4550
4606
|
if (changes.supervisorRetryLimit !== void 0) console.log(`Supervisor retry limit: ${changes.supervisorRetryLimit} per task.`);
|
|
4551
4607
|
if (changes.wakeUpReadBudget !== void 0) console.log(`Wake-up read budget: ${changes.wakeUpReadBudget} reads.`);
|
|
4552
4608
|
}
|
|
4553
4609
|
async function handleAutomerge(subArgs, injectedApi) {
|
|
4554
4610
|
const [arg] = positionals(subArgs);
|
|
4555
|
-
const enabled = arg === void 0 ? null : (0,
|
|
4611
|
+
const enabled = arg === void 0 ? null : (0, import_core30.parseSwitch)(arg);
|
|
4556
4612
|
if (arg !== void 0 && enabled === null) fail(AUTOMERGE_USAGE);
|
|
4557
4613
|
const api = await connect(subArgs, injectedApi);
|
|
4558
4614
|
if (enabled === null) {
|
|
4559
|
-
console.log((0,
|
|
4615
|
+
console.log((0, import_core30.automergeSummary)((await api.getSettings()).run));
|
|
4560
4616
|
console.log(` ${AUTOMERGE_USAGE}`);
|
|
4561
4617
|
return;
|
|
4562
4618
|
}
|
|
@@ -4565,37 +4621,54 @@ async function handleAutomerge(subArgs, injectedApi) {
|
|
|
4565
4621
|
}
|
|
4566
4622
|
async function handleTrailer(subArgs, injectedApi) {
|
|
4567
4623
|
const [arg] = positionals(subArgs);
|
|
4568
|
-
const enabled = arg === void 0 ? null : (0,
|
|
4624
|
+
const enabled = arg === void 0 ? null : (0, import_core30.parseSwitch)(arg);
|
|
4569
4625
|
if (arg !== void 0 && enabled === null) fail(TRAILER_USAGE);
|
|
4570
4626
|
const api = await connect(subArgs, injectedApi);
|
|
4571
4627
|
if (enabled === null) {
|
|
4572
|
-
console.log((0,
|
|
4628
|
+
console.log((0, import_core30.trailerSummary)((await api.getSettings()).run));
|
|
4573
4629
|
console.log(` ${TRAILER_USAGE}`);
|
|
4574
4630
|
return;
|
|
4575
4631
|
}
|
|
4576
4632
|
await save(api, { plannedWithTrailer: enabled });
|
|
4577
|
-
console.log(`${
|
|
4633
|
+
console.log(`${import_core30.PLANNED_WITH_LABEL}: ${enabled ? "on" : "off"}, from each session's next commit.`);
|
|
4578
4634
|
}
|
|
4579
4635
|
async function handleAutorun(subArgs, injectedApi) {
|
|
4580
4636
|
const [arg] = positionals(subArgs);
|
|
4581
|
-
const enabled = arg === void 0 ? null : (0,
|
|
4637
|
+
const enabled = arg === void 0 ? null : (0, import_core30.parseSwitch)(arg);
|
|
4582
4638
|
if (arg !== void 0 && enabled === null) fail(AUTORUN_USAGE);
|
|
4583
4639
|
const api = await connect(subArgs, injectedApi);
|
|
4584
4640
|
if (enabled === null) {
|
|
4585
|
-
console.log((0,
|
|
4641
|
+
console.log((0, import_core30.autorunSummary)((await api.getSettings()).run));
|
|
4586
4642
|
console.log(` ${AUTORUN_USAGE}`);
|
|
4587
4643
|
return;
|
|
4588
4644
|
}
|
|
4589
4645
|
await save(api, { autorun: enabled });
|
|
4590
4646
|
console.log(`Autorun: ${enabled ? "on: each plan the planner submits starts at once" : "off: plans wait for your review"}.`);
|
|
4591
|
-
if (enabled) console.log(
|
|
4647
|
+
if (enabled) console.log(import_core30.AUTORUN_RISK);
|
|
4648
|
+
}
|
|
4649
|
+
async function handleDelegation(subArgs, injectedApi) {
|
|
4650
|
+
const [arg] = positionals(subArgs);
|
|
4651
|
+
const tokens = arg === void 0 ? null : (0, import_core30.parseDelegation)(arg);
|
|
4652
|
+
if (arg !== void 0 && tokens === null) fail(DELEGATION_USAGE);
|
|
4653
|
+
const api = await connect(subArgs, injectedApi);
|
|
4654
|
+
if (tokens === null) {
|
|
4655
|
+
console.log((0, import_core30.delegationSummary)((await api.getSettings()).run));
|
|
4656
|
+
console.log(` ${DELEGATION_USAGE}`);
|
|
4657
|
+
return;
|
|
4658
|
+
}
|
|
4659
|
+
await save(api, { delegationTokens: tokens });
|
|
4660
|
+
console.log(`${(0, import_core30.delegationSummary)({ ...(await api.getSettings()).run, delegationTokens: tokens })}, for new sessions.`);
|
|
4661
|
+
if (tokens > 0) console.log(import_core30.DELEGATION_RISK);
|
|
4592
4662
|
}
|
|
4593
4663
|
function maybe(n, wrap2) {
|
|
4594
4664
|
return n === null ? null : wrap2(n);
|
|
4595
4665
|
}
|
|
4596
4666
|
|
|
4667
|
+
// src/commands/registry.ts
|
|
4668
|
+
var import_core59 = require("@ordewell/core");
|
|
4669
|
+
|
|
4597
4670
|
// src/commands/key.ts
|
|
4598
|
-
var
|
|
4671
|
+
var import_core31 = require("@ordewell/core");
|
|
4599
4672
|
var USAGE6 = "Usage: ordewell key [list | set <provider> <key>]";
|
|
4600
4673
|
async function handleKey(subArgs, injectedApi) {
|
|
4601
4674
|
const [action, provider, ...keyParts] = positionals(subArgs);
|
|
@@ -4606,8 +4679,8 @@ async function handleKey(subArgs, injectedApi) {
|
|
|
4606
4679
|
if (action !== "set") {
|
|
4607
4680
|
const catalog = await fetchCatalog(api);
|
|
4608
4681
|
console.log("\nAPI provider keys\n");
|
|
4609
|
-
for (const id2 of
|
|
4610
|
-
const meta2 = (0,
|
|
4682
|
+
for (const id2 of import_core31.PROVIDER_PRIORITY) {
|
|
4683
|
+
const meta2 = (0, import_core31.getProviderMeta)(id2);
|
|
4611
4684
|
const configured = catalog.providers.includes(id2);
|
|
4612
4685
|
console.log(` ${configured ? "\u2713" : " "} ${id2.padEnd(14)} ${meta2.apiKeyEnvVar.padEnd(28)} ${meta2.label}`);
|
|
4613
4686
|
}
|
|
@@ -4617,10 +4690,10 @@ async function handleKey(subArgs, injectedApi) {
|
|
|
4617
4690
|
}
|
|
4618
4691
|
if (!provider) fail(USAGE6);
|
|
4619
4692
|
const id = provider.toLowerCase();
|
|
4620
|
-
if (!(0,
|
|
4693
|
+
if (!(0, import_core31.isApiProvider)(id)) {
|
|
4621
4694
|
fail(`Unknown provider: ${provider}`, "Run `ordewell key` to see the list.");
|
|
4622
4695
|
}
|
|
4623
|
-
const meta = (0,
|
|
4696
|
+
const meta = (0, import_core31.getProviderMeta)(id);
|
|
4624
4697
|
const key = keyParts.join(" ").trim();
|
|
4625
4698
|
if (!key) fail(`Usage: ordewell key set ${id} <key>`);
|
|
4626
4699
|
await persistEnv(api, { [meta.apiKeyEnvVar]: key });
|
|
@@ -4628,7 +4701,7 @@ async function handleKey(subArgs, injectedApi) {
|
|
|
4628
4701
|
}
|
|
4629
4702
|
|
|
4630
4703
|
// src/commands/runners.ts
|
|
4631
|
-
var
|
|
4704
|
+
var import_core32 = require("@ordewell/core");
|
|
4632
4705
|
var USAGE7 = "Usage: ordewell runners [<runner-id> on|off]";
|
|
4633
4706
|
async function handleRunners(subArgs, injectedApi) {
|
|
4634
4707
|
const [runner, toggle] = positionals(subArgs);
|
|
@@ -4639,7 +4712,7 @@ async function handleRunners(subArgs, injectedApi) {
|
|
|
4639
4712
|
for (const r of state2.runners) {
|
|
4640
4713
|
console.log(` ${r.enabled ? "\u2713" : " "} ${r.id.padEnd(14)} ${r.name}${r.enabled ? "" : " (disabled)"}`);
|
|
4641
4714
|
}
|
|
4642
|
-
const notice = (0,
|
|
4715
|
+
const notice = (0, import_core32.codexSandboxNotice)((await fetchCatalog(api).catch(() => null))?.modesByRunner);
|
|
4643
4716
|
if (notice) console.log(`
|
|
4644
4717
|
${notice.line}
|
|
4645
4718
|
${notice.docs}`);
|
|
@@ -4660,17 +4733,17 @@ async function handleRunners(subArgs, injectedApi) {
|
|
|
4660
4733
|
var AUTO_USAGE = "Usage: ordewell auto [full|guarded]";
|
|
4661
4734
|
function handleAuto(subArgs) {
|
|
4662
4735
|
const [arg] = positionals(subArgs);
|
|
4663
|
-
const settings = new
|
|
4736
|
+
const settings = new import_core32.SettingsService();
|
|
4664
4737
|
const current = settings.autonomousMode();
|
|
4665
4738
|
if (!arg) {
|
|
4666
|
-
console.log(`Autonomy level: ${(0,
|
|
4739
|
+
console.log(`Autonomy level: ${(0, import_core32.autonomyLevelLabel)(current)}`);
|
|
4667
4740
|
console.log(` ${AUTO_USAGE}`);
|
|
4668
4741
|
return;
|
|
4669
4742
|
}
|
|
4670
|
-
const enabled = (0,
|
|
4743
|
+
const enabled = (0, import_core32.parseAutonomyLevel)(arg);
|
|
4671
4744
|
if (enabled === null) fail(AUTO_USAGE);
|
|
4672
4745
|
settings.setAutonomousMode(enabled);
|
|
4673
|
-
console.log(settings.autonomousModeOverridden() ? `${(0,
|
|
4746
|
+
console.log(settings.autonomousModeOverridden() ? `${(0, import_core32.autonomyLevelLabel)(enabled)} is saved, but ORDEWELL_AUTONOMOUS_MODE in the environment decides the level until it is unset.` : `Autonomy level: ${(0, import_core32.autonomyLevelLabel)(enabled)} for new plans.`);
|
|
4674
4747
|
}
|
|
4675
4748
|
async function handleRefresh(subArgs, injectedApi) {
|
|
4676
4749
|
const api = await connect(subArgs, injectedApi);
|
|
@@ -4701,7 +4774,7 @@ async function handleApprove(subArgs, injectedApi) {
|
|
|
4701
4774
|
}
|
|
4702
4775
|
|
|
4703
4776
|
// src/commands/task-assign.ts
|
|
4704
|
-
var
|
|
4777
|
+
var import_core34 = require("@ordewell/core");
|
|
4705
4778
|
var import_plan_utils4 = require("@ordewell/core/plan-utils");
|
|
4706
4779
|
|
|
4707
4780
|
// src/tui/taskAssignment.ts
|
|
@@ -4743,7 +4816,7 @@ function modesForTask(modesByRunner, task) {
|
|
|
4743
4816
|
|
|
4744
4817
|
// src/tui/state.ts
|
|
4745
4818
|
var import_plan_utils3 = require("@ordewell/core/plan-utils");
|
|
4746
|
-
var
|
|
4819
|
+
var import_core33 = require("@ordewell/core");
|
|
4747
4820
|
|
|
4748
4821
|
// src/tui/editor.ts
|
|
4749
4822
|
function emptyEditor() {
|
|
@@ -4904,7 +4977,6 @@ function queuedTarget(tv) {
|
|
|
4904
4977
|
for (let i = queued.length - 1; i >= 0; i--) if (!queued[i].forced) return i;
|
|
4905
4978
|
return -1;
|
|
4906
4979
|
}
|
|
4907
|
-
var anyTaskRunning = (state) => state.tasks.some(import_core31.isTaskRunning);
|
|
4908
4980
|
var plannerInFlight = (state) => state.planner !== "idle";
|
|
4909
4981
|
var planPaneOpen = (state) => state.focus === "plan" && !state.overlay && (!!state.taskEditor || !!state.expandedTaskId);
|
|
4910
4982
|
function planRows(state) {
|
|
@@ -4929,10 +5001,10 @@ function waitingCheckpoint(task) {
|
|
|
4929
5001
|
return task?.status === "awaiting_user" && task.awaitingReason === "checkpoint" ? task.checkpoint : void 0;
|
|
4930
5002
|
}
|
|
4931
5003
|
function continuesTask(task) {
|
|
4932
|
-
return task ? (0,
|
|
5004
|
+
return task ? (0, import_core33.logContinues)(task) : false;
|
|
4933
5005
|
}
|
|
4934
5006
|
function resumesTask(task) {
|
|
4935
|
-
return task ? (0,
|
|
5007
|
+
return task ? (0, import_core33.logResumes)(task) : false;
|
|
4936
5008
|
}
|
|
4937
5009
|
function findTask(tasks, id) {
|
|
4938
5010
|
return (0, import_plan_utils3.taskPlacement)(tasks, id)?.task;
|
|
@@ -4941,10 +5013,10 @@ function scriptTaskView(state) {
|
|
|
4941
5013
|
return state.taskView ? findTask(state.tasks, state.taskView.taskId)?.type === "script" : false;
|
|
4942
5014
|
}
|
|
4943
5015
|
function refFor(state, task) {
|
|
4944
|
-
return (0,
|
|
5016
|
+
return (0, import_core33.taskRefOf)(state.tasks, task.id) ?? (0, import_core33.taskRef)(task);
|
|
4945
5017
|
}
|
|
4946
5018
|
function titledRefFor(state, task) {
|
|
4947
|
-
return (0,
|
|
5019
|
+
return (0, import_core33.titledTaskRefOf)(state.tasks, task.id) ?? (0, import_core33.titledTaskRef)(task);
|
|
4948
5020
|
}
|
|
4949
5021
|
function asRunTasks(tasks) {
|
|
4950
5022
|
return tasks.map((task) => ({ ...task, subtasks: asRunTasks(task.subtasks ?? []) }));
|
|
@@ -4952,7 +5024,7 @@ function asRunTasks(tasks) {
|
|
|
4952
5024
|
function seedRun(overrides) {
|
|
4953
5025
|
if (overrides.run) return overrides.run;
|
|
4954
5026
|
return {
|
|
4955
|
-
...
|
|
5027
|
+
...import_core33.EMPTY_RUN,
|
|
4956
5028
|
tasks: asRunTasks(overrides.tasks ?? []),
|
|
4957
5029
|
handoff: overrides.handoff ?? null,
|
|
4958
5030
|
runMerge: overrides.runMerge ? { waitingAtGate: overrides.runMerge.waitingAtGate, paused: overrides.runMerge.paused, repos: overrides.runMerge.handoff.repos, landed: overrides.runMerge.handoff.landed } : null
|
|
@@ -4961,11 +5033,11 @@ function seedRun(overrides) {
|
|
|
4961
5033
|
function initialState(overrides = {}) {
|
|
4962
5034
|
return {
|
|
4963
5035
|
editor: emptyEditor(),
|
|
4964
|
-
conversation:
|
|
5036
|
+
conversation: import_core33.EMPTY_CONVERSATION,
|
|
4965
5037
|
accountLimits: [],
|
|
4966
5038
|
servedModels: {},
|
|
4967
5039
|
taskView: null,
|
|
4968
|
-
turnGate:
|
|
5040
|
+
turnGate: import_core33.NO_TURN,
|
|
4969
5041
|
detailAll: false,
|
|
4970
5042
|
status: "idle",
|
|
4971
5043
|
planner: "idle",
|
|
@@ -4991,16 +5063,17 @@ function initialState(overrides = {}) {
|
|
|
4991
5063
|
plannerProvider: "",
|
|
4992
5064
|
plannerEffort: "",
|
|
4993
5065
|
plannerModels: {},
|
|
4994
|
-
maxParallel:
|
|
5066
|
+
maxParallel: import_core33.DEFAULT_MAX_PARALLEL,
|
|
4995
5067
|
configuredProviders: [],
|
|
4996
5068
|
allowlist: {},
|
|
4997
5069
|
autonomous: false,
|
|
4998
|
-
runSettings: { ...
|
|
5070
|
+
runSettings: { ...import_core33.RUN_SETTINGS_DEFAULTS },
|
|
4999
5071
|
runSettingsKnown: false,
|
|
5000
5072
|
settingsRead: 0,
|
|
5001
5073
|
runSettingsRead: 0,
|
|
5002
5074
|
plannerKnown: false,
|
|
5003
5075
|
runnersKnown: false,
|
|
5076
|
+
plannerDetecting: false,
|
|
5004
5077
|
providersKnown: false,
|
|
5005
5078
|
isolationPreview: void 0,
|
|
5006
5079
|
isolationPreviewRead: 0,
|
|
@@ -5016,7 +5089,7 @@ function initialState(overrides = {}) {
|
|
|
5016
5089
|
plannerPause: null,
|
|
5017
5090
|
overlay: null,
|
|
5018
5091
|
pendingApprovals: [],
|
|
5019
|
-
queuedPrompts:
|
|
5092
|
+
queuedPrompts: import_core33.EMPTY_HOLD,
|
|
5020
5093
|
stopArmed: false,
|
|
5021
5094
|
stopArmToken: 0,
|
|
5022
5095
|
stopRequested: false,
|
|
@@ -5026,6 +5099,7 @@ function initialState(overrides = {}) {
|
|
|
5026
5099
|
cols: 80,
|
|
5027
5100
|
exiting: false,
|
|
5028
5101
|
spinnerFrame: 0,
|
|
5102
|
+
now: 0,
|
|
5029
5103
|
...overrides,
|
|
5030
5104
|
run: seedRun(overrides)
|
|
5031
5105
|
};
|
|
@@ -5152,7 +5226,7 @@ Mode \xB7 ${namedOf(tasks, task)}${task.assignedRunner ? ` (${task.assignedRunne
|
|
|
5152
5226
|
for (const m of modes) {
|
|
5153
5227
|
console.log(` ${m.id === task.taskMode ? "*" : " "} ${m.id.padEnd(14)} ${m.label}${m.unavailable ? ": unavailable on this host" : m.description ? `: ${m.description}` : ""}`);
|
|
5154
5228
|
}
|
|
5155
|
-
const notice = (0,
|
|
5229
|
+
const notice = (0, import_core34.codexSandboxNotice)(catalog.modesByRunner);
|
|
5156
5230
|
if (notice) console.log(`
|
|
5157
5231
|
${notice.line}
|
|
5158
5232
|
${notice.docs}`);
|
|
@@ -5202,7 +5276,7 @@ var SKILLS_USAGE = "Usage: ordewell task-skills <task-id-or-order> [<name> [<nam
|
|
|
5202
5276
|
async function handleTaskSkills(subArgs, injectedApi) {
|
|
5203
5277
|
await withTask(subArgs, SKILLS_USAGE, injectedApi, async (api, sessionId, task, tasks, value) => {
|
|
5204
5278
|
if (task.type !== "ai") fail("Manual tasks do not run an executor, so they take no skills.");
|
|
5205
|
-
const catalog = (0,
|
|
5279
|
+
const catalog = (0, import_core34.createSkillsService)(flag(subArgs, "--workspace") || process.cwd(), new import_core34.EnvConfig().workspaceRepos).listSkills().filter((s) => s.appliesTo === "task");
|
|
5206
5280
|
if (!value) {
|
|
5207
5281
|
console.log(`
|
|
5208
5282
|
Skills \xB7 ${namedOf(tasks, task)}
|
|
@@ -5249,10 +5323,10 @@ ${namedOf(tasks, task)} is ${task.ops ? "an ops task: it runs in your checkout o
|
|
|
5249
5323
|
// src/tui/index.ts
|
|
5250
5324
|
var import_child_process4 = require("child_process");
|
|
5251
5325
|
var import_path7 = require("path");
|
|
5252
|
-
var
|
|
5326
|
+
var import_core58 = require("@ordewell/core");
|
|
5253
5327
|
|
|
5254
5328
|
// src/tui/slash.ts
|
|
5255
|
-
var
|
|
5329
|
+
var import_core35 = require("@ordewell/core");
|
|
5256
5330
|
var SLASH_COMMANDS = [
|
|
5257
5331
|
// Planning. There is deliberately no `/plan`: typing the goal *is* the way to
|
|
5258
5332
|
// start planning, and an alias for it only shadowed `/planner` on completion.
|
|
@@ -5265,6 +5339,7 @@ var SLASH_COMMANDS = [
|
|
|
5265
5339
|
{ name: "add-task", usage: "/add-task <title>", description: "Add a task to the current plan", category: "tasks" },
|
|
5266
5340
|
{ name: "remove-task", usage: "/remove-task <id>", description: "Remove a task from the plan", category: "tasks" },
|
|
5267
5341
|
{ name: "complete", usage: "/complete <id>", description: "Mark a task complete (asks first)", category: "tasks" },
|
|
5342
|
+
{ name: "revert", usage: "/revert <id>", description: "Take a task's landed work back off the integration branch (asks first)", category: "tasks" },
|
|
5268
5343
|
{ name: "uncomplete", usage: "/uncomplete <id>", description: "Mark a completed task not done (asks first)", category: "tasks" },
|
|
5269
5344
|
{ name: "skip", usage: "/skip <id>", description: "Skip a task", category: "tasks" },
|
|
5270
5345
|
{ name: "retry", usage: "/retry <id>", description: "Re-run a failed task", category: "tasks" },
|
|
@@ -5273,6 +5348,7 @@ var SLASH_COMMANDS = [
|
|
|
5273
5348
|
{ name: "continue", usage: "/continue <id> <message>", description: "Continue a finished task in its saved runner session, with your message", category: "tasks" },
|
|
5274
5349
|
{ name: "open", usage: "/open <id>", description: "Open a started task's chat: its view, and the keys that answer its checkpoint or tool request", category: "tasks" },
|
|
5275
5350
|
{ name: "checkpoint", usage: "/checkpoint <id> approve|reject [reason]", description: "Answer the checkpoint a task waits at: approve lets it go on, reject tells it why not", category: "tasks" },
|
|
5351
|
+
{ name: "inbox", usage: "/inbox", description: "List everything waiting on you, and answer it or open its task", category: "tasks" },
|
|
5276
5352
|
{ name: "task-runner", usage: "/task-runner <id> [runner]", description: "Choose or set a task's executor (re-picks its model and mode)", category: "tasks" },
|
|
5277
5353
|
{ name: "task-model", usage: "/task-model <id> [model]", description: "Choose or set a task's executor model", category: "tasks" },
|
|
5278
5354
|
{ name: "task-effort", usage: "/task-effort <id> [level]", description: "Choose or set a task's thinking effort", category: "tasks" },
|
|
@@ -5286,7 +5362,7 @@ var SLASH_COMMANDS = [
|
|
|
5286
5362
|
{ name: "allowlist", usage: "/allowlist [set <runner> <ids> | clear <runner>]", description: "Limit which models a runner may use", category: "models" },
|
|
5287
5363
|
{ name: "runners", usage: "/runners [<id> on|off]", description: `Enable or disable runners (${runnerIdList()})`, category: "models" },
|
|
5288
5364
|
{ name: "login", usage: "/login [<runner>]", description: "Sign the active coding agent in, or the one named, in this terminal", category: "models" },
|
|
5289
|
-
{ name: "settings", usage: "/settings", description:
|
|
5365
|
+
{ name: "settings", usage: "/settings", description: `Run settings: autonomy, supervisor, merging, autorun, parallel tasks${import_core35.DELEGATION_OFFERED ? " and delegation" : ""}`, category: "models" },
|
|
5290
5366
|
{ name: "refresh", usage: "/refresh", description: "Re-check runners; reread settings and cached models", category: "models" },
|
|
5291
5367
|
// Sessions
|
|
5292
5368
|
{ name: "sessions", usage: "/sessions", description: "List saved sessions", category: "session" },
|
|
@@ -5386,7 +5462,7 @@ function parseRunnerCommand(text) {
|
|
|
5386
5462
|
return match ? { name: match[1], args: (match[2] ?? "").trim() } : null;
|
|
5387
5463
|
}
|
|
5388
5464
|
function runnerCommandFor(commands, name) {
|
|
5389
|
-
return (0,
|
|
5465
|
+
return (0, import_core35.findRunnerCommand)(commands, name) ?? (0, import_core35.findRunnerCommand)(commands, name.toLowerCase());
|
|
5390
5466
|
}
|
|
5391
5467
|
function composerCompletions(token, runner) {
|
|
5392
5468
|
if (runner) return token.start === 0 ? runner.filter((c) => c.name.toLowerCase().startsWith(token.name)).map((c) => c.name) : [];
|
|
@@ -5398,7 +5474,7 @@ function tokenCompletions(token) {
|
|
|
5398
5474
|
}
|
|
5399
5475
|
|
|
5400
5476
|
// src/tui/accountLimits.ts
|
|
5401
|
-
var
|
|
5477
|
+
var import_core36 = require("@ordewell/core");
|
|
5402
5478
|
function assignmentKey(task) {
|
|
5403
5479
|
return `${task.assignedRunner}\0${task.assignedModel?.modelId ?? ""}`;
|
|
5404
5480
|
}
|
|
@@ -5412,7 +5488,7 @@ function observeTaskServedModel(state, action) {
|
|
|
5412
5488
|
state.taskView?.taskId === task.id ? state.taskView.attempts.at(-1) ?? 0 : 0
|
|
5413
5489
|
);
|
|
5414
5490
|
if (action.attempt < latest) return state;
|
|
5415
|
-
const model = (0,
|
|
5491
|
+
const model = (0, import_core36.lastServedModel)(action.events);
|
|
5416
5492
|
if (!model && action.attempt === before?.attempt) return state;
|
|
5417
5493
|
const served = { attempt: action.attempt, ...model ? { model, assignment: assignmentKey(task) } : {} };
|
|
5418
5494
|
return { ...state, servedModels: { ...state.servedModels, [task.id]: served } };
|
|
@@ -5474,10 +5550,10 @@ function chatEditorRoomFor(state, active) {
|
|
|
5474
5550
|
}
|
|
5475
5551
|
|
|
5476
5552
|
// src/tui/layout.ts
|
|
5477
|
-
var
|
|
5553
|
+
var import_core39 = require("@ordewell/core");
|
|
5478
5554
|
|
|
5479
5555
|
// src/tui/reducers/pickers.ts
|
|
5480
|
-
var
|
|
5556
|
+
var import_core37 = require("@ordewell/core");
|
|
5481
5557
|
var import_plan_utils5 = require("@ordewell/core/plan-utils");
|
|
5482
5558
|
var DEFAULT_EFFORT = "__runner_default__";
|
|
5483
5559
|
function picker(title, items, action, extra = {}) {
|
|
@@ -5502,13 +5578,13 @@ function modelDetail(m) {
|
|
|
5502
5578
|
return [m.provider, m.pricing].filter(Boolean).join(" \xB7 ") || void 0;
|
|
5503
5579
|
}
|
|
5504
5580
|
function plannerModelItems(state, provider, selectedId) {
|
|
5505
|
-
const runner = (0,
|
|
5581
|
+
const runner = (0, import_core37.runnerForProvider)(provider);
|
|
5506
5582
|
if (!runner) {
|
|
5507
5583
|
const failed = Object.entries(state.providerErrors ?? {});
|
|
5508
5584
|
const unavailable = failed.length === 0 ? [] : [{
|
|
5509
5585
|
id: "",
|
|
5510
5586
|
label: "Some providers are unavailable",
|
|
5511
|
-
detail: `\u26A0 ${failed.map(([id]) => (0,
|
|
5587
|
+
detail: `\u26A0 ${failed.map(([id]) => (0, import_core37.getProviderMeta)(id)?.label ?? id).join(", ")}: key or fetch failed`,
|
|
5512
5588
|
disabled: true
|
|
5513
5589
|
}];
|
|
5514
5590
|
return [
|
|
@@ -5528,11 +5604,11 @@ function plannerModelItems(state, provider, selectedId) {
|
|
|
5528
5604
|
return items.length > 0 ? items : [{ id: "", label: `No ${runner} models discovered yet`, detail: "run /refresh", disabled: true }];
|
|
5529
5605
|
}
|
|
5530
5606
|
function plannerHarnessModel(state, provider, modelId) {
|
|
5531
|
-
const runner = (0,
|
|
5607
|
+
const runner = (0, import_core37.runnerForProvider)(provider);
|
|
5532
5608
|
return runner ? state.models.find((m) => m.id === modelId && m.runners?.includes(runner)) : void 0;
|
|
5533
5609
|
}
|
|
5534
5610
|
function plannerEffortItems(state, provider, modelId, selectedEffort) {
|
|
5535
|
-
if (!(0,
|
|
5611
|
+
if (!(0, import_core37.runnerForProvider)(provider)) {
|
|
5536
5612
|
return [{ id: "", label: "Thinking effort applies to a coding-agent planner", detail: "switch with /planner", disabled: true }];
|
|
5537
5613
|
}
|
|
5538
5614
|
const model = plannerHarnessModel(state, provider, modelId);
|
|
@@ -5644,12 +5720,12 @@ function pickerItemsFor(state, action) {
|
|
|
5644
5720
|
function plannerItems(state) {
|
|
5645
5721
|
const installed = new Map(state.runners.map((runner) => [runner.id, runner]));
|
|
5646
5722
|
const usability = {};
|
|
5647
|
-
for (const id of
|
|
5648
|
-
const runner = (0,
|
|
5723
|
+
for (const id of import_core37.CLI_PROVIDERS) {
|
|
5724
|
+
const runner = (0, import_core37.runnerForProvider)(id);
|
|
5649
5725
|
const known = installed.get(runner);
|
|
5650
5726
|
usability[runner] = known ? { usable: true, ...known.auth ? { auth: known.auth } : {} } : { usable: false, reason: "CLI not installed or not on PATH" };
|
|
5651
5727
|
}
|
|
5652
|
-
const offered2 = (0,
|
|
5728
|
+
const offered2 = (0, import_core37.plannerBackendEntries)(usability, state.configuredProviders).filter((entry) => entry.usable);
|
|
5653
5729
|
if (offered2.length === 0) {
|
|
5654
5730
|
return [{ id: "", label: "No planner available", detail: `install a coding agent (${runnerNamesOr()}) or add a key with /key`, disabled: true }];
|
|
5655
5731
|
}
|
|
@@ -5680,7 +5756,7 @@ function cliVersion() {
|
|
|
5680
5756
|
}
|
|
5681
5757
|
|
|
5682
5758
|
// src/tui/planKeys.ts
|
|
5683
|
-
var
|
|
5759
|
+
var import_core38 = require("@ordewell/core");
|
|
5684
5760
|
var PLAN_KEYS = {
|
|
5685
5761
|
move: { keys: "up / down", meaning: "move between tasks", group: "Navigation" },
|
|
5686
5762
|
scroll: { keys: "pgup / pgdn", meaning: "scroll the plan", group: "Navigation" },
|
|
@@ -5704,16 +5780,16 @@ var PLAN_KEYS = {
|
|
|
5704
5780
|
meaning: "start the task, approving it if it waits for you; it starts once its dependencies finish",
|
|
5705
5781
|
group: "Run",
|
|
5706
5782
|
// Added after the plan was approved, it waits for this key; the row says the same.
|
|
5707
|
-
hint: (task) => task && (0,
|
|
5783
|
+
hint: (task) => task && (0, import_core38.taskRowView)(task).waitsForApproval ? "f approve" : "f start"
|
|
5708
5784
|
},
|
|
5709
5785
|
runPlan: { keys: "E", meaning: "run the whole plan", group: "Run", hint: "E run plan" },
|
|
5710
5786
|
stop: { keys: "S", meaning: "stop the run", group: "Run", hint: "S stop" },
|
|
5711
5787
|
cancel: {
|
|
5712
5788
|
keys: "c",
|
|
5713
|
-
meaning: "cancel the task's runner, or keep a task paused on a usage limit from resuming on its own",
|
|
5789
|
+
meaning: "cancel the task's runner, withdraw a conflict repair waiting to start, or keep a task paused on a usage limit from resuming on its own",
|
|
5714
5790
|
group: "Run",
|
|
5715
5791
|
// `c` cancels the resume while the task has one; it has no runner to cancel then.
|
|
5716
|
-
hint: (task) => task && (0,
|
|
5792
|
+
hint: (task) => task && (0, import_core38.resumeLabel)(task) ? "c don't resume" : task && (0, import_core38.taskRowView)(task).repairWaiting ? "c stop repair" : "c cancel"
|
|
5717
5793
|
},
|
|
5718
5794
|
resume: { keys: "r", meaning: "resume a task paused on a usage limit now, once the limit has reset", group: "Run", hint: "r resume now" },
|
|
5719
5795
|
resolve: { keys: "x", meaning: "resolve a merge conflict", group: "Run", hint: "x resolve conflict" },
|
|
@@ -5723,7 +5799,7 @@ var PLAN_KEYS = {
|
|
|
5723
5799
|
meaning: "mark the task done, or take it back",
|
|
5724
5800
|
group: "Status",
|
|
5725
5801
|
// `m` toggles, so the hint names the direction it will go for this task.
|
|
5726
|
-
hint: (task) => task && (0,
|
|
5802
|
+
hint: (task) => task && (0, import_core38.markAction)(task) === "uncomplete" ? "m undone" : "m done"
|
|
5727
5803
|
},
|
|
5728
5804
|
skip: { keys: "s", meaning: "skip the task: none of its work lands, and what depends on it goes ahead", group: "Status", hint: "s skip" },
|
|
5729
5805
|
remove: { keys: "d", meaning: "remove the task", group: "Status" },
|
|
@@ -5750,9 +5826,10 @@ function hintOf(id, task) {
|
|
|
5750
5826
|
}
|
|
5751
5827
|
function taskKeyIds(task, runActive) {
|
|
5752
5828
|
if (!task) return ["add"];
|
|
5753
|
-
const row = (0,
|
|
5829
|
+
const row = (0, import_core38.taskRowView)(task);
|
|
5754
5830
|
const chat = (...ids) => row.chatAvailable ? ids : ids.filter((id) => id !== "log");
|
|
5755
|
-
if (row.conflict) return chat("resolve", "log");
|
|
5831
|
+
if (row.conflict) return row.conflict.resolver ? chat("log") : chat("resolve", "log");
|
|
5832
|
+
if (row.repairWaiting) return chat("cancel", "log");
|
|
5756
5833
|
if (row.actions.includes("resume-now") || row.resumes) {
|
|
5757
5834
|
return chat(...row.actions.includes("resume-now") ? ["resume"] : [], ...row.resumes ? ["cancel"] : [], "skip", "log");
|
|
5758
5835
|
}
|
|
@@ -5780,7 +5857,7 @@ function planFooterHints(task, runActive) {
|
|
|
5780
5857
|
}
|
|
5781
5858
|
|
|
5782
5859
|
// src/tui/layout.ts
|
|
5783
|
-
var
|
|
5860
|
+
var import_core40 = require("@ordewell/core");
|
|
5784
5861
|
var CHROME_ROWS = 1;
|
|
5785
5862
|
var COMPOSER_WINDOW_CHARS = 8 * 1024;
|
|
5786
5863
|
var COMPOSER_BAND_ROWS = 2;
|
|
@@ -5827,7 +5904,7 @@ function footerHints(state) {
|
|
|
5827
5904
|
}
|
|
5828
5905
|
return [...escHint ? [escHint] : [], ...planFooterHints(selected, state.status === "executing")];
|
|
5829
5906
|
}
|
|
5830
|
-
const detailHint = (0,
|
|
5907
|
+
const detailHint = (0, import_core40.hasHiddenDetail)(state.taskView ? state.taskView.view.blocks : state.conversation.blocks) ? [state.detailAll ? "ctrl-o collapse all" : "ctrl-o expand all"] : [];
|
|
5831
5908
|
return [
|
|
5832
5909
|
"/help",
|
|
5833
5910
|
...checkpointHints,
|
|
@@ -5902,12 +5979,13 @@ function logHeaderTask(task) {
|
|
|
5902
5979
|
resumeAt: task?.resumeAt ?? null,
|
|
5903
5980
|
idleSince: task?.idleSince ?? null,
|
|
5904
5981
|
continuable: task?.continuable === true,
|
|
5982
|
+
...task?.isolation?.state === "conflict" && task.isolation.repairWaits ? { repairWaits: task.isolation.repairWaits } : {},
|
|
5905
5983
|
attemptUsage: task?.attemptUsage ?? []
|
|
5906
5984
|
};
|
|
5907
5985
|
}
|
|
5908
5986
|
function logHeader(state, tv) {
|
|
5909
5987
|
const task = findTask(state.tasks, tv.taskId);
|
|
5910
|
-
return (0,
|
|
5988
|
+
return (0, import_core40.taskLogHeaderView)(logHeaderTask(task), tv, task?.awaitingApproval ?? 0, tv.attempt);
|
|
5911
5989
|
}
|
|
5912
5990
|
function taskHeaderLines(state, tv, cols) {
|
|
5913
5991
|
const task = findTask(state.tasks, tv.taskId);
|
|
@@ -5915,7 +5993,7 @@ function taskHeaderLines(state, tv, cols) {
|
|
|
5915
5993
|
const parts = [
|
|
5916
5994
|
`\u2192 Task ${task ? refFor(state, task).slice(1) : "?"}`,
|
|
5917
5995
|
task?.title ?? "(task removed)",
|
|
5918
|
-
(task && (0,
|
|
5996
|
+
(task && (0, import_core40.assignedRunnerOf)(task)) ?? "",
|
|
5919
5997
|
task ? inRow(header.state.label) : "",
|
|
5920
5998
|
header.resumes ? inRow(header.resumes) : ""
|
|
5921
5999
|
].filter(Boolean);
|
|
@@ -5933,31 +6011,44 @@ function taskHeaderLines(state, tv, cols) {
|
|
|
5933
6011
|
function stopHint(state) {
|
|
5934
6012
|
return plannerInFlight(state) && state.stopArmed ? "Press Esc again to stop" : "";
|
|
5935
6013
|
}
|
|
5936
|
-
var DOT_TICKS = 4;
|
|
5937
6014
|
var SPINNER_CYCLE = 40;
|
|
5938
6015
|
function runnerStoppable(state) {
|
|
5939
6016
|
const tv = state.taskView;
|
|
5940
|
-
|
|
6017
|
+
const activity = chatActivity(state);
|
|
6018
|
+
if (!tv || !(activity?.busy || activity?.quiet)) return false;
|
|
5941
6019
|
if (scriptTaskView(state)) return false;
|
|
5942
6020
|
const task = findTask(state.tasks, tv.taskId);
|
|
5943
6021
|
return !task?.awaitingApproval && !waitingApproval(tv);
|
|
5944
6022
|
}
|
|
6023
|
+
function animating(state) {
|
|
6024
|
+
return state.tasks.some((t) => (0, import_core40.taskStatusKind)(t) === "running") || !!chatActivity(state)?.busy;
|
|
6025
|
+
}
|
|
6026
|
+
function activityTime(state, now) {
|
|
6027
|
+
return (0, import_core40.elapsedSince)(chatActivity(state)?.since, now);
|
|
6028
|
+
}
|
|
5945
6029
|
function chatActivity(state) {
|
|
5946
6030
|
const tv = state.taskView;
|
|
5947
6031
|
if (!tv) {
|
|
5948
6032
|
if (state.status === "starting") return { label: "Starting the plan\u2026", busy: true };
|
|
5949
6033
|
if (!plannerInFlight(state)) return null;
|
|
5950
|
-
const label = state.planner === "condensing" ? "Condensing\u2026" : state.planner === "compacting" ? "Compacting\u2026" : state.planner === "awaiting" ? "Waiting for the planner\u2026" : "Planning\u2026";
|
|
5951
|
-
|
|
6034
|
+
const label = state.plannerDetecting ? DETECTING : state.planner === "condensing" ? "Condensing\u2026" : state.planner === "compacting" ? "Compacting\u2026" : state.planner === "awaiting" ? "Waiting for the planner\u2026" : "Planning\u2026";
|
|
6035
|
+
const since = state.turnGate.open !== null ? state.turnGate.openedAt : void 0;
|
|
6036
|
+
return since ? { label, busy: true, since } : { label, busy: true };
|
|
5952
6037
|
}
|
|
5953
6038
|
const own = findTask(state.tasks, tv.taskId);
|
|
5954
6039
|
const task = state.tasks.find((t) => t.subtasks?.some((s) => s.id === tv.taskId)) ?? own;
|
|
5955
6040
|
if (!task) return null;
|
|
5956
|
-
const attaching = (0,
|
|
6041
|
+
const attaching = (0, import_core40.attachingLabel)(task);
|
|
5957
6042
|
if (attaching) return { label: `${attaching}\u2026`, busy: true };
|
|
5958
|
-
if ((0,
|
|
6043
|
+
if ((0, import_core33.isTaskRunning)(task) || own && (0, import_core33.isTaskRunning)(own)) {
|
|
6044
|
+
const running2 = (0, import_core33.isTaskRunning)(task) ? task : own;
|
|
6045
|
+
const archived = tv.attempts.length > 0 && tv.attempt < Math.max(...tv.attempts);
|
|
6046
|
+
const activity = running2 && (0, import_core40.taskActivity)({ ...tv.view, archived }, running2);
|
|
6047
|
+
if (!activity) return null;
|
|
6048
|
+
return activity.busy ? activity : { ...activity, quiet: true };
|
|
6049
|
+
}
|
|
5959
6050
|
const at = task.status === "awaiting_user" && task.awaitingReason === "usage-limit" && task.resumeAt ? new Date(task.resumeAt) : null;
|
|
5960
|
-
if (at && !Number.isNaN(at.getTime())) return { label: `paused \xB7 resumes ${(0,
|
|
6051
|
+
if (at && !Number.isNaN(at.getTime())) return { label: `paused \xB7 resumes ${(0, import_core40.resumeTime)(at)}`, busy: false };
|
|
5961
6052
|
return null;
|
|
5962
6053
|
}
|
|
5963
6054
|
function runHints(state) {
|
|
@@ -5967,6 +6058,10 @@ function runHints(state) {
|
|
|
5967
6058
|
return state.runMerge?.paused ? ["paused for Merge all: /handoff merge", "paused \xB7 /handoff merge", "merge paused"] : [];
|
|
5968
6059
|
}
|
|
5969
6060
|
var runHint = (state) => runHints(state)[0] ?? "";
|
|
6061
|
+
function inboxHint(state) {
|
|
6062
|
+
const waiting = state.run.escalations?.length ?? 0;
|
|
6063
|
+
return waiting > 0 ? `${waiting} waiting: /inbox` : "";
|
|
6064
|
+
}
|
|
5970
6065
|
function packHints(hints, cols) {
|
|
5971
6066
|
const lines = [];
|
|
5972
6067
|
let current = "";
|
|
@@ -6016,7 +6111,7 @@ function plannerResendRow(state, cols) {
|
|
|
6016
6111
|
const at = plannerResend ? new Date(plannerResend.at) : null;
|
|
6017
6112
|
if (!plannerResend || !at || Number.isNaN(at.getTime())) return [];
|
|
6018
6113
|
const whose = plannerResend.runner ? `${plannerResend.runner}'s` : "the planner's";
|
|
6019
|
-
return [style.yellow(truncate(`\u23F8 Resending at ${(0,
|
|
6114
|
+
return [style.yellow(truncate(`\u23F8 Resending at ${(0, import_core40.resumeTime)(at)}, once ${whose} usage limit resets \xB7 /resend cancel keeps it for your next message`, cols))];
|
|
6020
6115
|
}
|
|
6021
6116
|
function plannerPauseRow(state, cols) {
|
|
6022
6117
|
const { plannerPause } = state;
|
|
@@ -6024,18 +6119,18 @@ function plannerPauseRow(state, cols) {
|
|
|
6024
6119
|
return [style.yellow(truncate(`\u23F8 Planner paused: its context does not fit ${plannerPause.model}'s window \xB7 /compact retry attempts recovery \xB7 /stop drops the turn`, cols))];
|
|
6025
6120
|
}
|
|
6026
6121
|
var tokenRow = (state, blocks2, cols) => {
|
|
6027
|
-
const runner = (0,
|
|
6028
|
-
const limits = (0,
|
|
6122
|
+
const runner = (0, import_core40.runnerForProvider)(state.plannerProvider);
|
|
6123
|
+
const limits = (0, import_core40.accountLimitsOf)(state.accountLimits, runner ?? void 0, state.orchestratorModel);
|
|
6029
6124
|
const line = tokenLine(blocks2, cols, limits);
|
|
6030
6125
|
return line ? [line] : [];
|
|
6031
6126
|
};
|
|
6032
6127
|
function taskUsageFooter(state, tv, cols) {
|
|
6033
6128
|
const task = findTask(state.tasks, tv.taskId);
|
|
6034
|
-
const limits = (0,
|
|
6129
|
+
const limits = (0, import_core40.accountLimitsOf)(state.accountLimits, task && (0, import_core40.assignedRunnerOf)(task), task && taskServedModel(state, task)).map(import_core40.accountLimitsText).filter(Boolean).join(" \xB7 ");
|
|
6035
6130
|
const rows = logHeader(state, tv).usage;
|
|
6036
6131
|
const text = rows.map((row, index) => {
|
|
6037
6132
|
if (!limits || index !== rows.length - 1) return row.text;
|
|
6038
|
-
const context = (0,
|
|
6133
|
+
const context = (0, import_core40.contextMeterText)(row.contextFill);
|
|
6039
6134
|
return context ? row.text.replace(context, `${context} \xB7 ${limits}`) : `${limits} \xB7 ${row.text}`;
|
|
6040
6135
|
});
|
|
6041
6136
|
if (text.length === 0 && limits) text.push(limits);
|
|
@@ -6122,22 +6217,23 @@ function chatScrollMax(state) {
|
|
|
6122
6217
|
}
|
|
6123
6218
|
function plannerSetup(state) {
|
|
6124
6219
|
const provider = state.plannerProvider;
|
|
6125
|
-
const registration = (0,
|
|
6220
|
+
const registration = (0, import_core40.getProviderMeta)(provider);
|
|
6126
6221
|
if (!registration) return { label: "not set", ready: false };
|
|
6127
|
-
const runner = (0,
|
|
6128
|
-
const ready = runner ? state.runners.some((r) => r.id === runner) : (0,
|
|
6222
|
+
const runner = (0, import_core40.runnerForProvider)(provider);
|
|
6223
|
+
const ready = runner ? state.runners.some((r) => r.id === runner) : (0, import_core40.isApiProvider)(provider) && state.configuredProviders.includes(provider);
|
|
6129
6224
|
return { label: registration.label, ready };
|
|
6130
6225
|
}
|
|
6131
6226
|
var SETUP_KEY_COLS = 10;
|
|
6132
6227
|
var SETUP_VALUE_COLS = 32;
|
|
6133
6228
|
function runSettingsValue(state) {
|
|
6134
6229
|
if (!state.runSettingsKnown) return style.grey("\u2026");
|
|
6135
|
-
const { supervisorLevel, mergeAtGatesAutomatically, autorun } = state.runSettings;
|
|
6136
|
-
const level = `supervisor ${
|
|
6230
|
+
const { supervisorLevel, mergeAtGatesAutomatically, autorun, delegationTokens } = state.runSettings;
|
|
6231
|
+
const level = `supervisor ${import_core39.SUPERVISOR_LEVEL_LABEL[supervisorLevel]}`;
|
|
6137
6232
|
return [
|
|
6138
6233
|
supervisorLevel === "act" ? style.yellow(level) : level,
|
|
6139
6234
|
`auto-merge ${mergeAtGatesAutomatically ? "on" : "off"}`,
|
|
6140
|
-
autorun ? style.yellow("autorun on") : "autorun off"
|
|
6235
|
+
autorun ? style.yellow("autorun on") : "autorun off",
|
|
6236
|
+
...import_core39.DELEGATION_OFFERED && delegationTokens > 0 ? [style.yellow("delegation on")] : []
|
|
6141
6237
|
].join(" \xB7 ");
|
|
6142
6238
|
}
|
|
6143
6239
|
function plannerEffortLabel(state) {
|
|
@@ -6222,7 +6318,7 @@ function isolationPreviewLines(state, cols) {
|
|
|
6222
6318
|
if (!preview) {
|
|
6223
6319
|
return [head, ...wrap(style.grey(CHECKING_REPOSITORIES), room), ""];
|
|
6224
6320
|
}
|
|
6225
|
-
const notice = (0,
|
|
6321
|
+
const notice = (0, import_core40.describeIsolationPreview)(preview);
|
|
6226
6322
|
const important = preview.status === "dirty" || preview.status === "failed";
|
|
6227
6323
|
const summary = important ? style.yellow(notice.summary) : notice.summary;
|
|
6228
6324
|
return [
|
|
@@ -6236,7 +6332,7 @@ function isolationPreviewLines(state, cols) {
|
|
|
6236
6332
|
}
|
|
6237
6333
|
function signedOutPlannerLines(state, cols) {
|
|
6238
6334
|
if (!state.plannerKnown || !state.runnersKnown) return [];
|
|
6239
|
-
const planned = (0,
|
|
6335
|
+
const planned = (0, import_core40.runnerForProvider)(state.plannerProvider);
|
|
6240
6336
|
const flagged = state.runners.filter(signedOutWithLogin);
|
|
6241
6337
|
const active = flagged.find((row) => row.id === planned);
|
|
6242
6338
|
const room = Math.max(1, cols - 1);
|
|
@@ -6246,7 +6342,7 @@ function signedOutPlannerLines(state, cols) {
|
|
|
6246
6342
|
));
|
|
6247
6343
|
}
|
|
6248
6344
|
function plannerRowValue(state, label) {
|
|
6249
|
-
const alarm = state.runners.find((row) => row.id === (0,
|
|
6345
|
+
const alarm = state.runners.find((row) => row.id === (0, import_core40.runnerForProvider)(state.plannerProvider));
|
|
6250
6346
|
const paint = alarm && signedOutWithLogin(alarm) ? style.red : (text) => text;
|
|
6251
6347
|
const effort = plannerEffortLabel(state);
|
|
6252
6348
|
const model = state.orchestratorModel ? paint(state.orchestratorModel) : style.yellow("no model");
|
|
@@ -6314,10 +6410,11 @@ var STATUS_ICON = {
|
|
|
6314
6410
|
quiet: "~",
|
|
6315
6411
|
todo: "\xB7"
|
|
6316
6412
|
};
|
|
6413
|
+
var QUIET_MARK = STATUS_ICON.quiet;
|
|
6317
6414
|
var inRow = (label) => label.charAt(0).toLowerCase() + label.slice(1);
|
|
6318
6415
|
function planLayout(state, rows, cols) {
|
|
6319
6416
|
const done = state.tasks.filter((t) => t.status === "completed").length;
|
|
6320
|
-
const totalUsage = (0,
|
|
6417
|
+
const totalUsage = (0, import_core40.tasksTotalText)(state.tasks);
|
|
6321
6418
|
const unmerged = state.runMerge?.handoff.landed.length ?? 0;
|
|
6322
6419
|
const count = `${done}/${state.tasks.length}`;
|
|
6323
6420
|
const hints = runHints(state);
|
|
@@ -6403,7 +6500,7 @@ function modelsByRunnerOf(models) {
|
|
|
6403
6500
|
function taskLines(state, planRow, index, cols) {
|
|
6404
6501
|
const { task, parent } = planRow;
|
|
6405
6502
|
const runnerScope = state.runners.length > 0 ? { enabledRunners: state.runners.filter((r) => r.enabled).map((r) => r.id), allowlist: state.allowlist, models: modelsByRunnerOf(state.models) } : void 0;
|
|
6406
|
-
const row = (0,
|
|
6503
|
+
const row = (0, import_core40.taskRowView)(task, { parent, tasks: state.tasks, modes: modesForTask(state.modesByRunner, task), runnerScope });
|
|
6407
6504
|
const selected = state.focus === "plan" && index === state.selectedTask;
|
|
6408
6505
|
const expanded = state.expandedTaskId === task.id;
|
|
6409
6506
|
const rawIcon = row.status === "running" ? SPINNER[state.spinnerFrame % SPINNER.length] : STATUS_ICON[row.status];
|
|
@@ -6442,7 +6539,12 @@ function taskLines(state, planRow, index, cols) {
|
|
|
6442
6539
|
if (row.conflict) {
|
|
6443
6540
|
const where = row.conflict.repo ? ` in ${row.conflict.repo}` : "";
|
|
6444
6541
|
const files = row.conflict.files ? ` (${row.conflict.files})` : "";
|
|
6445
|
-
|
|
6542
|
+
const held = row.conflict.resolver ? `task ${row.conflict.resolver} is resolving it` : "its work is kept on its own branch";
|
|
6543
|
+
lines.push(...statusLines(style.red, "\u26A0", `merge conflict${where}${files}: ${held}`, cols, bodyPad));
|
|
6544
|
+
}
|
|
6545
|
+
if (row.repairWaiting) {
|
|
6546
|
+
const files = row.repairWaiting.files ? ` (${row.repairWaiting.files})` : "";
|
|
6547
|
+
lines.push(...statusLines(style.yellow, "\u23F8", `${inRow(row.repairWaiting.label)}${files}`, cols, bodyPad));
|
|
6446
6548
|
}
|
|
6447
6549
|
if (row.repairing) {
|
|
6448
6550
|
const files = row.repairing.files ? ` in ${row.repairing.files}` : "";
|
|
@@ -6459,13 +6561,13 @@ function taskLines(state, planRow, index, cols) {
|
|
|
6459
6561
|
lines.push(...statusLines(style.yellow, "\u23F8", `${inRow(row.waitsForApproval)} \xB7 f approves it \xB7 /run approves all`, cols, bodyPad));
|
|
6460
6562
|
}
|
|
6461
6563
|
if (row.holdReason) {
|
|
6462
|
-
lines.push(...statusLines(style.yellow, "\u23F8", `${inRow((0,
|
|
6564
|
+
lines.push(...statusLines(style.yellow, "\u23F8", `${inRow((0, import_core40.holdLabel)(row.holdReason))} \xB7 retry or f start when ready`, cols, bodyPad));
|
|
6463
6565
|
}
|
|
6464
6566
|
if (row.blockedBy) {
|
|
6465
|
-
lines.push(...statusLines(style.red, "\u2298", `${inRow((0,
|
|
6567
|
+
lines.push(...statusLines(style.red, "\u2298", `${inRow((0, import_core40.blockedByLabel)(row.blockedBy))}: runs once ${row.blockedBy.length === 1 ? "it is retried and passes" : "they are retried and pass"}`, cols, bodyPad));
|
|
6466
6568
|
}
|
|
6467
6569
|
if (row.status === "skipped") {
|
|
6468
|
-
lines.push(...statusLines(style.grey, "\xBB", `${inRow((0,
|
|
6570
|
+
lines.push(...statusLines(style.grey, "\xBB", `${inRow((0, import_core40.skippedLabel)(row.skipReason))}: nothing of it landed; m done lands its kept work`, cols, bodyPad));
|
|
6469
6571
|
}
|
|
6470
6572
|
const resumable = row.actions.includes("resume-now");
|
|
6471
6573
|
if (row.resumes) {
|
|
@@ -6482,11 +6584,11 @@ function taskLines(state, planRow, index, cols) {
|
|
|
6482
6584
|
let editorLine;
|
|
6483
6585
|
if (expanded) {
|
|
6484
6586
|
if (row.handoff) {
|
|
6485
|
-
lines.push(...taskText("Handoff", (0,
|
|
6587
|
+
lines.push(...taskText("Handoff", (0, import_core40.handoffPeekParts)(row.handoff).map(sanitize).join(" \xB7 "), cols, bodyPad));
|
|
6486
6588
|
}
|
|
6487
6589
|
if (skills) lines.push(style.grey(truncate(`${bodyPad}skills: ${skills}`, cols)));
|
|
6488
6590
|
if (effort || mode) lines.push(style.grey(truncate(`${bodyPad}${[effort, mode].filter(Boolean).join(" \xB7 ")}`, cols)));
|
|
6489
|
-
lines.push(style.grey(`${bodyPad}${STATUS_WORD[(0,
|
|
6591
|
+
lines.push(style.grey(`${bodyPad}${STATUS_WORD[(0, import_core40.taskStatusKind)(task)]}`));
|
|
6490
6592
|
if (task.isolation && task.isolation.state !== "none" && task.isolation.branch) {
|
|
6491
6593
|
lines.push(...taskText("Branch", task.isolation.branch, cols, bodyPad));
|
|
6492
6594
|
if (row.repos.length > 0) lines.push(...taskText("Repos", row.repos.join(", "), cols, bodyPad));
|
|
@@ -6501,7 +6603,7 @@ function taskLines(state, planRow, index, cols) {
|
|
|
6501
6603
|
lines.push(...taskText("Ops", "Runs in your checkout, not a worktree, once the work it depends on is merged. O makes it a change task.", cols, bodyPad));
|
|
6502
6604
|
}
|
|
6503
6605
|
if (row.kind === "script") {
|
|
6504
|
-
lines.push(...taskText("Script", (0,
|
|
6606
|
+
lines.push(...taskText("Script", (0, import_core40.scriptTaskText)(task).replace(/\t/g, " "), cols, bodyPad));
|
|
6505
6607
|
lines.push(...taskText("Ops", "Runs in your checkout with no model, once the work it depends on is merged.", cols, bodyPad));
|
|
6506
6608
|
}
|
|
6507
6609
|
if (row.forcedPastGate) {
|
|
@@ -6545,7 +6647,7 @@ var LOOP_PAINT = {
|
|
|
6545
6647
|
};
|
|
6546
6648
|
function loopLines(loop, cols, pad2) {
|
|
6547
6649
|
const paint = loop.role === "body" ? style.cyan : LOOP_PAINT[loop.state];
|
|
6548
|
-
return statusLines(paint, "\u21BB", (0,
|
|
6650
|
+
return statusLines(paint, "\u21BB", (0, import_core40.loopRowParts)(loop).map(inRow).join(" \xB7 "), cols, pad2);
|
|
6549
6651
|
}
|
|
6550
6652
|
function statusLines(paint, glyph, text, cols, pad2) {
|
|
6551
6653
|
const lead = `${pad2}${glyph} `;
|
|
@@ -6688,8 +6790,8 @@ function helpScrollMax(state) {
|
|
|
6688
6790
|
}
|
|
6689
6791
|
|
|
6690
6792
|
// src/tui/render.ts
|
|
6691
|
-
var
|
|
6692
|
-
var
|
|
6793
|
+
var import_core41 = require("@ordewell/core");
|
|
6794
|
+
var import_core42 = require("@ordewell/core");
|
|
6693
6795
|
function render(state) {
|
|
6694
6796
|
const { rows, cols } = state;
|
|
6695
6797
|
const rowsForBody = bodyRows(state);
|
|
@@ -6791,15 +6893,16 @@ function settingsBadge(state) {
|
|
|
6791
6893
|
}
|
|
6792
6894
|
function runSettingsBadge(state) {
|
|
6793
6895
|
if (!state.runSettingsKnown) return style.grey("run settings \u2026");
|
|
6794
|
-
const { supervisorLevel, mergeAtGatesAutomatically, autorun } = state.runSettings;
|
|
6896
|
+
const { supervisorLevel, mergeAtGatesAutomatically, autorun, delegationTokens } = state.runSettings;
|
|
6795
6897
|
const paint = (text, warn) => warn ? style.yellow(text) : style.grey(text);
|
|
6796
6898
|
const run = state.status === "executing" ? state.runSupervisor : null;
|
|
6797
|
-
const actual = run?.unsupervised ? "unsupervised" : run && run.level !== supervisorLevel ?
|
|
6798
|
-
const level = `supervisor ${
|
|
6899
|
+
const actual = run?.unsupervised ? "unsupervised" : run && run.level !== supervisorLevel ? import_core42.SUPERVISOR_LEVEL_LABEL[run.level] : null;
|
|
6900
|
+
const level = `supervisor ${import_core42.SUPERVISOR_LEVEL_LABEL[supervisorLevel]}${actual ? ` \u2192 ${actual}` : ""}`;
|
|
6799
6901
|
return [
|
|
6800
6902
|
paint(level, supervisorLevel === "act"),
|
|
6801
6903
|
...mergeAtGatesAutomatically ? [style.grey("auto-merge")] : [],
|
|
6802
|
-
...autorun ? [style.yellow("autorun")] : []
|
|
6904
|
+
...autorun ? [style.yellow("autorun")] : [],
|
|
6905
|
+
...import_core42.DELEGATION_OFFERED && delegationTokens > 0 ? [style.yellow("delegation")] : []
|
|
6803
6906
|
].join(style.grey(" \xB7 "));
|
|
6804
6907
|
}
|
|
6805
6908
|
function scrolledBackMark(state) {
|
|
@@ -6818,13 +6921,18 @@ function statusText(state, cols, panes) {
|
|
|
6818
6921
|
if (armed) return truncate(style.red(armed), cols);
|
|
6819
6922
|
const activity = chatActivity(state);
|
|
6820
6923
|
const hint = !panes.plan || panes.plan.rows === 0 ? runHint(state) : "";
|
|
6821
|
-
const
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
const
|
|
6924
|
+
const inbox = inboxHint(state);
|
|
6925
|
+
const notes = [mark, activity && !activity.busy ? activity.label : "", hint, inbox, state.toast].filter(Boolean).join(" \xB7 ");
|
|
6926
|
+
if (activity?.busy || activity?.quiet) {
|
|
6927
|
+
const glyph = activity.busy ? SPINNER[state.spinnerFrame % SPINNER.length] : QUIET_MARK;
|
|
6825
6928
|
const scrolled = mark ? style.grey(`${mark} \xB7 `) : "";
|
|
6826
6929
|
const stop = runnerStoppable(state) ? " \xB7 ctrl-x to stop" : "";
|
|
6827
|
-
|
|
6930
|
+
const elapsed = (0, import_core41.elapsedSince)(activity.since, state.now);
|
|
6931
|
+
const time = elapsed ? ` ${elapsed}` : "";
|
|
6932
|
+
const room = Math.max(1, cols - width(scrolled) - width(glyph) - 1 - width(time) - width(stop));
|
|
6933
|
+
const label = truncate(sanitize(activity.label).replace(/\s+/g, " ").trim(), room);
|
|
6934
|
+
const count = inbox && width(label) + width(inbox) + 3 <= room ? style.grey(` \xB7 ${inbox}`) : "";
|
|
6935
|
+
return truncate(`${scrolled}${style.brand(glyph)} ${label}${time}${count}${stop}`, cols);
|
|
6828
6936
|
}
|
|
6829
6937
|
return notes ? truncate(style.grey(notes), cols) : "";
|
|
6830
6938
|
}
|
|
@@ -6883,10 +6991,10 @@ function composerLines(state, view) {
|
|
|
6883
6991
|
const continuation = " ".repeat(width(prompt));
|
|
6884
6992
|
if (!lines || !view) {
|
|
6885
6993
|
const visible = text.replace(/\n/g, "\xB6").replace(/\t/g, " ");
|
|
6886
|
-
const painted = paintSpans(visible, 0, highlightSpans(text, state.taskView ? (0,
|
|
6994
|
+
const painted = paintSpans(visible, 0, highlightSpans(text, state.taskView ? (0, import_core41.taskLogCommands)(state.taskView.view) : null));
|
|
6887
6995
|
return [prompt + painted + suggest(state, room - width(visible))];
|
|
6888
6996
|
}
|
|
6889
|
-
const spans = highlightSpans(view.text, state.taskView ? (0,
|
|
6997
|
+
const spans = highlightSpans(view.text, state.taskView ? (0, import_core41.taskLogCommands)(state.taskView.view) : null);
|
|
6890
6998
|
const { line: lineIndex, col } = cursorInLines(lines, cursor - view.base, view.text.length);
|
|
6891
6999
|
return lines.map(({ line: raw, start: base }, i) => {
|
|
6892
7000
|
const prefix = i === 0 ? prompt : continuation;
|
|
@@ -6905,7 +7013,7 @@ function suggest(state, room) {
|
|
|
6905
7013
|
if (state.focus === "plan" || room <= 2) return "";
|
|
6906
7014
|
const token = activeToken(state.editor.text, state.editor.cursor);
|
|
6907
7015
|
if (!token) return "";
|
|
6908
|
-
const matches = composerCompletions(token, state.taskView ? (0,
|
|
7016
|
+
const matches = composerCompletions(token, state.taskView ? (0, import_core41.taskLogCommands)(state.taskView.view) : null);
|
|
6909
7017
|
if (matches.length === 0) return "";
|
|
6910
7018
|
const names = matches.join(" ");
|
|
6911
7019
|
return style.bandGrey(` ${truncate(names, Math.max(0, room - 2))}`);
|
|
@@ -6971,23 +7079,23 @@ function renderHandoff(state, overlay, rows, cols) {
|
|
|
6971
7079
|
const { lines, scroll } = overlay.diff;
|
|
6972
7080
|
const shown = lines.slice(scroll, scroll + room).map((line) => paintDiffLine(truncate(line, Math.max(1, cols - 2))));
|
|
6973
7081
|
const more = scroll + room < lines.length ? "\u2191\u2193 pgup/pgdn scroll \xB7 " : "";
|
|
6974
|
-
return frame(`Diff: ${(0,
|
|
7082
|
+
return frame(`Diff: ${(0, import_core41.handoffBranchName)(handoff)}`, [...shown, "", style.grey(`${more}enter or esc goes back`)], rows, cols);
|
|
6975
7083
|
}
|
|
6976
7084
|
const landed = handoff.landed.length === 0 ? [style.grey("Nothing landed on it.")] : handoff.landed.map((task) => {
|
|
6977
|
-
const repaired = task.repairedFiles?.length ? style.yellow(`: repaired (${(0,
|
|
6978
|
-
return ` ${style.green("\u2713")} ${(0,
|
|
7085
|
+
const repaired = task.repairedFiles?.length ? style.yellow(`: repaired (${(0, import_core41.capConflictFiles)(task.repairedFiles)})`) : "";
|
|
7086
|
+
return ` ${style.green("\u2713")} ${(0, import_core41.titledTaskRefOf)(state.tasks, task.taskId) ?? (0, import_core41.titledTaskRef)(task)}${repaired}`;
|
|
6979
7087
|
});
|
|
6980
7088
|
const actions = handoffActions(handoff).map((action, index) => {
|
|
6981
7089
|
const active = index === overlay.index;
|
|
6982
7090
|
const caret = active ? style.brand("\u276F") : " ";
|
|
6983
7091
|
return `${caret} ${active ? style.bold(action.label) : action.label}${style.grey(`: ${action.hint}`)}`;
|
|
6984
7092
|
});
|
|
6985
|
-
const perRepo = (0,
|
|
7093
|
+
const perRepo = (0, import_core41.isRepoGroup)(handoff) ? ["Per repository:", ...handoff.repos.map((repo, i) => ` ${repo.landed.length > 0 ? style.green("\u2713") : style.grey("\xB7")} ${(0, import_core41.repoResultLines)(handoff)[i]}`), ""] : [];
|
|
6986
7094
|
return frame(
|
|
6987
7095
|
"Run handoff",
|
|
6988
7096
|
[
|
|
6989
|
-
`Branch ${style.brand((0,
|
|
6990
|
-
style.grey(`Forked from ${(0,
|
|
7097
|
+
`Branch ${style.brand((0, import_core41.handoffBranchName)(handoff))}`,
|
|
7098
|
+
style.grey(`Forked from ${(0, import_core41.handoffBaseLine)(handoff, 12)}`),
|
|
6991
7099
|
"",
|
|
6992
7100
|
...perRepo,
|
|
6993
7101
|
"Landed on it, in plan order:",
|
|
@@ -7149,13 +7257,13 @@ function frame(title, body, rows, cols) {
|
|
|
7149
7257
|
}
|
|
7150
7258
|
|
|
7151
7259
|
// src/tui/reducer.ts
|
|
7152
|
-
var
|
|
7260
|
+
var import_core55 = require("@ordewell/core");
|
|
7153
7261
|
|
|
7154
7262
|
// src/tui/reducers/overlays.ts
|
|
7155
|
-
var
|
|
7263
|
+
var import_core53 = require("@ordewell/core");
|
|
7156
7264
|
|
|
7157
7265
|
// src/tui/reducers/approvals.ts
|
|
7158
|
-
var
|
|
7266
|
+
var import_core43 = require("@ordewell/core");
|
|
7159
7267
|
|
|
7160
7268
|
// src/tui/reducers/shared.ts
|
|
7161
7269
|
function step(state, effects = []) {
|
|
@@ -7294,7 +7402,7 @@ function syncApprovals(state, waiting) {
|
|
|
7294
7402
|
const ids = new Set(waiting.map((request) => request.id));
|
|
7295
7403
|
const open = state.overlay?.kind === "approval" ? [state.overlay.request] : [];
|
|
7296
7404
|
const answered = [...open, ...state.pendingApprovals].filter((request) => !ids.has(request.id));
|
|
7297
|
-
const dropped = answered.reduce((current, request) => dropApproval(current, request.id), { ...state, conversation: (0,
|
|
7405
|
+
const dropped = answered.reduce((current, request) => dropApproval(current, request.id), { ...state, conversation: (0, import_core43.reconcileApprovals)(state.conversation, waiting) });
|
|
7298
7406
|
return waiting.reduce((current, { id, kind, subject, scope, detail, body }) => kind === "runner_tool" ? current : enqueueApproval(current, { id, kind, subject, scope, ...detail ? { detail } : {}, ...body ? { body } : {} }), dropped);
|
|
7299
7407
|
}
|
|
7300
7408
|
function showNextApproval(state) {
|
|
@@ -7319,10 +7427,10 @@ function scrollApproval(state, overlay, key) {
|
|
|
7319
7427
|
}
|
|
7320
7428
|
|
|
7321
7429
|
// src/tui/reducers/commands.ts
|
|
7322
|
-
var
|
|
7430
|
+
var import_core52 = require("@ordewell/core");
|
|
7323
7431
|
|
|
7324
7432
|
// src/tui/reducers/plannerStepper.ts
|
|
7325
|
-
var
|
|
7433
|
+
var import_core44 = require("@ordewell/core");
|
|
7326
7434
|
var offered = (items) => new Set(items.filter((i) => !i.disabled).map((i) => i.id));
|
|
7327
7435
|
function preferredModel(state, provider) {
|
|
7328
7436
|
const listed = offered(plannerModelItems(state, provider, ""));
|
|
@@ -7370,11 +7478,11 @@ var plannerStepper = {
|
|
|
7370
7478
|
const model = plannerModelItems(state, provider, "").find((m) => m.id === modelId);
|
|
7371
7479
|
const level = plannerEffortItems(state, provider, modelId, "").find((e) => e.id === effort);
|
|
7372
7480
|
const parts = [
|
|
7373
|
-
(0,
|
|
7481
|
+
(0, import_core44.getProviderMeta)(provider)?.label ?? provider,
|
|
7374
7482
|
model?.label ?? modelId,
|
|
7375
7483
|
...effort ? [effortLabel(level?.label ?? effort)] : []
|
|
7376
7484
|
];
|
|
7377
|
-
const active = state.runners.find((row) => row.id === (0,
|
|
7485
|
+
const active = state.runners.find((row) => row.id === (0, import_core44.runnerForProvider)(provider));
|
|
7378
7486
|
const noticeLevel = active && signedOutWithLogin(active) ? "error" : void 0;
|
|
7379
7487
|
return step({
|
|
7380
7488
|
...state,
|
|
@@ -7386,8 +7494,284 @@ var plannerStepper = {
|
|
|
7386
7494
|
}
|
|
7387
7495
|
};
|
|
7388
7496
|
|
|
7497
|
+
// src/tui/reducers/taskView.ts
|
|
7498
|
+
var import_core45 = require("@ordewell/core");
|
|
7499
|
+
function openTaskView(state, sessionId, taskId) {
|
|
7500
|
+
const task = findTask(state.tasks, taskId);
|
|
7501
|
+
if (!task) return fail3(state, `No task ${taskId} in this plan.`);
|
|
7502
|
+
if (!(0, import_core45.taskRowView)(task).chatAvailable) {
|
|
7503
|
+
return fail3(state, "This task hasn\u2019t started yet. Its chat becomes available when it starts.");
|
|
7504
|
+
}
|
|
7505
|
+
const taskView = {
|
|
7506
|
+
taskId,
|
|
7507
|
+
...import_core45.EMPTY_TASK_LOG_FOLLOW,
|
|
7508
|
+
queuedIndex: null
|
|
7509
|
+
};
|
|
7510
|
+
return step({ ...state, taskView, focus: "chat", scroll: 0 }, [{ type: "openTaskLog", sessionId, taskId }]);
|
|
7511
|
+
}
|
|
7512
|
+
function continueTaskStep(state, sessionId, taskId, text) {
|
|
7513
|
+
const opened = state.taskView?.taskId === taskId ? step({ ...state, taskView: { ...state.taskView, followLatest: true } }) : openTaskView(state, sessionId, taskId);
|
|
7514
|
+
const watch = state.status !== "executing";
|
|
7515
|
+
return step(opened.state, [...opened.effects, { type: "continueTask", sessionId, taskId, text, ...watch ? { watch: true } : {} }]);
|
|
7516
|
+
}
|
|
7517
|
+
function clampQueueIndex(tv) {
|
|
7518
|
+
if (tv.queuedIndex === null) return tv;
|
|
7519
|
+
if (tv.view.queued.length === 0) return { ...tv, queuedIndex: null };
|
|
7520
|
+
const max = tv.view.queued.length - 1;
|
|
7521
|
+
return tv.queuedIndex <= max ? tv : { ...tv, queuedIndex: max };
|
|
7522
|
+
}
|
|
7523
|
+
function taskLogAbandoned(state) {
|
|
7524
|
+
const tv = state.taskView;
|
|
7525
|
+
if (!tv || tv.loaded) return state;
|
|
7526
|
+
return { ...state, taskView: clampQueueIndex({ ...tv, ...(0, import_core45.followTaskLog)(tv, { type: "loaded", attempt: tv.attempt, events: [], abandoned: true }) }) };
|
|
7527
|
+
}
|
|
7528
|
+
function taskLogArrived(state, action) {
|
|
7529
|
+
state = observeTaskServedModel(state, action);
|
|
7530
|
+
const tv = state.taskView;
|
|
7531
|
+
if (!tv || tv.taskId !== action.taskId) return state;
|
|
7532
|
+
const next = (0, import_core45.followTaskLog)(tv, { ...action, type: "live" });
|
|
7533
|
+
return { ...state, taskView: next === tv ? tv : clampQueueIndex({ ...tv, ...next }) };
|
|
7534
|
+
}
|
|
7535
|
+
function announceApprovals(state, action) {
|
|
7536
|
+
if (state.taskView?.taskId === action.taskId) return state;
|
|
7537
|
+
const task = findTask(state.tasks, action.taskId);
|
|
7538
|
+
const way = task ? `tab to the plan, select task ${refFor(state, task)} and press enter (or /open ${refFor(state, task)})` : "tab to the plan, select it and press enter";
|
|
7539
|
+
const spoken = action.events.reduce((s, event) => event.type === "approval_requested" && !event.decided ? say(s, "system", `Task ${task ? refFor(state, task) : "?"} waits for approval: ${(0, import_core45.runnerToolSubject)(event.tool, event.args)}: ${way}, then ctrl-y allows or ctrl-g denies`) : s, state);
|
|
7540
|
+
return spoken !== state ? { ...spoken, scroll: state.scroll } : spoken;
|
|
7541
|
+
}
|
|
7542
|
+
function announceCheckpoint(state, action) {
|
|
7543
|
+
const task = findTask(state.tasks, action.taskId) ?? state.tasks.find((t) => t.title === action.title);
|
|
7544
|
+
const label = task ? `Task ${refFor(state, task)}` : action.title;
|
|
7545
|
+
const where = task ? `tab to the plan, select task ${refFor(state, task)} and press enter (or /open ${refFor(state, task)}) to answer` : "tab to the plan, select it and press enter to answer";
|
|
7546
|
+
const spoken = say(state, "system", `${label} asks: ${(0, import_core45.truncateCheckpointSummary)(action.summary)}: ${where}`);
|
|
7547
|
+
return { ...spoken, scroll: state.scroll };
|
|
7548
|
+
}
|
|
7549
|
+
function taskLogLoaded(state, action) {
|
|
7550
|
+
state = observeTaskServedModel(state, action);
|
|
7551
|
+
const tv = state.taskView;
|
|
7552
|
+
if (!tv || tv.taskId !== action.taskId) return state;
|
|
7553
|
+
return { ...state, taskView: clampQueueIndex({ ...tv, ...(0, import_core45.followTaskLog)(tv, { ...action, type: "loaded" }) }) };
|
|
7554
|
+
}
|
|
7555
|
+
function answerApproval(state, tv, sessionId, decision) {
|
|
7556
|
+
const approval = waitingApproval(tv);
|
|
7557
|
+
if (!approval?.approvalId) return step(say(state, "system", "Nothing is waiting for approval."));
|
|
7558
|
+
if (decision === "allowForTask" && !approval.allowForTask) {
|
|
7559
|
+
return step(say(state, "system", "The runner offered no grant for the rest of this task: ctrl-y allows this call."));
|
|
7560
|
+
}
|
|
7561
|
+
const note = decision === "deny" ? state.editor.text.trim() : "";
|
|
7562
|
+
const answer = decision === "deny" ? { decision, ...note ? { note } : {} } : { decision };
|
|
7563
|
+
const next = note ? { ...state, editor: { ...state.editor, text: "", cursor: 0 } } : state;
|
|
7564
|
+
return step(next, [{ type: "answerTaskApproval", sessionId, approvalId: approval.approvalId, answer }]);
|
|
7565
|
+
}
|
|
7566
|
+
function answerCheckpoint(state, sessionId, taskId, answer, reason = "") {
|
|
7567
|
+
const task = findTask(state.tasks, taskId);
|
|
7568
|
+
if (!waitingCheckpoint(task)) return step(say(state, "system", `Task ${task?.order ?? taskId} is not waiting at a checkpoint.`));
|
|
7569
|
+
const note = answer === "reject" ? reason.trim() : "";
|
|
7570
|
+
return step(state, [{ type: "answerTaskCheckpoint", sessionId, taskId, answer, ...note ? { reason: note } : {} }]);
|
|
7571
|
+
}
|
|
7572
|
+
function answerWaiting(state, tv, sessionId, decision) {
|
|
7573
|
+
if (waitingApproval(tv) || !waitingCheckpoint(findTask(state.tasks, tv.taskId))) return answerApproval(state, tv, sessionId, decision);
|
|
7574
|
+
if (decision === "allow") return answerCheckpoint(state, sessionId, tv.taskId, "approve");
|
|
7575
|
+
const answered = answerCheckpoint(state, sessionId, tv.taskId, "reject", state.editor.text);
|
|
7576
|
+
return state.editor.text.trim() ? { ...answered, state: { ...answered.state, editor: { ...answered.state.editor, text: "", cursor: 0 } } } : answered;
|
|
7577
|
+
}
|
|
7578
|
+
function sendNow(state, tv, sessionId) {
|
|
7579
|
+
const text = state.editor.text.trim();
|
|
7580
|
+
if (text) {
|
|
7581
|
+
const task = findTask(state.tasks, tv.taskId);
|
|
7582
|
+
if (continuesTask(task)) {
|
|
7583
|
+
return step(say(state, "system", resumesTask(task) ? "The task is paused on a usage limit, so there is no turn to interrupt: enter resumes it." : "The task has finished, so there is no turn to interrupt: enter continues it."));
|
|
7584
|
+
}
|
|
7585
|
+
return step({ ...state, editor: commit(state.editor) }, [{ type: "forceSendTaskMessage", sessionId, taskId: tv.taskId, text }]);
|
|
7586
|
+
}
|
|
7587
|
+
const queued = tv.view.queued[queuedTarget(tv)];
|
|
7588
|
+
if (!queued) return step(say(state, "system", "Nothing to send now: type a message, or pick a queued one with ctrl-n/ctrl-p."));
|
|
7589
|
+
return step({ ...state, taskView: { ...tv, queuedIndex: null } }, [
|
|
7590
|
+
{ type: "forceSendQueuedTaskMessage", sessionId, taskId: tv.taskId, messageId: queued.id }
|
|
7591
|
+
]);
|
|
7592
|
+
}
|
|
7593
|
+
function handleTaskViewKey(state, key) {
|
|
7594
|
+
const tv = state.taskView;
|
|
7595
|
+
if (!tv || !state.sessionId) return null;
|
|
7596
|
+
if (key.name === "alt-left" || key.name === "alt-right") {
|
|
7597
|
+
const index = tv.attempts.indexOf(tv.loadingAttempt ?? tv.attempt);
|
|
7598
|
+
if (index < 0) return null;
|
|
7599
|
+
const target = key.name === "alt-left" ? index - 1 : index + 1;
|
|
7600
|
+
const attempt = tv.attempts[target];
|
|
7601
|
+
if (attempt === void 0) {
|
|
7602
|
+
return step(say(state, "system", target < 0 ? "This is the first attempt." : "This is the latest attempt."));
|
|
7603
|
+
}
|
|
7604
|
+
return step(
|
|
7605
|
+
{ ...state, taskView: { ...tv, ...(0, import_core45.followTaskLog)(tv, { type: "picked", attempt }) } },
|
|
7606
|
+
[{ type: "loadTaskAttempt", sessionId: state.sessionId, taskId: tv.taskId, attempt }]
|
|
7607
|
+
);
|
|
7608
|
+
}
|
|
7609
|
+
if (scriptTaskView(state)) return null;
|
|
7610
|
+
if (key.name === "ctrl-r") {
|
|
7611
|
+
const queued = tv.view.queued[queuedTarget(tv)];
|
|
7612
|
+
if (!queued) return step(say(state, "system", "No queued message to remove."));
|
|
7613
|
+
if (queued.handedOver) return step(say(state, "system", "The runner already has that message; it can no longer be taken back."));
|
|
7614
|
+
return step(state, [{ type: "removeTaskMessage", sessionId: state.sessionId, taskId: tv.taskId, messageId: queued.id }]);
|
|
7615
|
+
}
|
|
7616
|
+
if (key.name === "ctrl-s") return sendNow(state, tv, state.sessionId);
|
|
7617
|
+
if (key.name === "ctrl-x") {
|
|
7618
|
+
const task = findTask(state.tasks, tv.taskId);
|
|
7619
|
+
if (task && (0, import_core45.resumeLabel)(task)) return step(state, [{ type: "taskAction", sessionId: state.sessionId, taskId: tv.taskId, action: "cancel-resume" }]);
|
|
7620
|
+
return step(state, [{ type: "interruptTask", sessionId: state.sessionId, taskId: tv.taskId }]);
|
|
7621
|
+
}
|
|
7622
|
+
if (key.name === "ctrl-y") return answerWaiting(state, tv, state.sessionId, "allow");
|
|
7623
|
+
if (key.name === "ctrl-t") return answerApproval(state, tv, state.sessionId, "allowForTask");
|
|
7624
|
+
if (key.name === "ctrl-g") return answerWaiting(state, tv, state.sessionId, "deny");
|
|
7625
|
+
if (key.name === "ctrl-n" || key.name === "ctrl-p") {
|
|
7626
|
+
const count = tv.view.queued.length;
|
|
7627
|
+
if (count === 0) return null;
|
|
7628
|
+
const delta = key.name === "ctrl-n" ? 1 : -1;
|
|
7629
|
+
const from = queuedTarget(tv);
|
|
7630
|
+
const queuedIndex = from < 0 ? count - 1 : Math.max(0, Math.min(count - 1, from + delta));
|
|
7631
|
+
return step({ ...state, taskView: { ...tv, queuedIndex } });
|
|
7632
|
+
}
|
|
7633
|
+
return null;
|
|
7634
|
+
}
|
|
7635
|
+
|
|
7636
|
+
// src/tui/reducers/inbox.ts
|
|
7637
|
+
var SPAWNS_RUNNER = /* @__PURE__ */ new Set(["retry", "resume", "continue", "force-start", "run"]);
|
|
7638
|
+
var OPEN_TASK = "__open-task";
|
|
7639
|
+
var NOTHING = "Nothing is waiting on you.";
|
|
7640
|
+
var GONE = "That is no longer waiting on you: the list changed. Run /inbox to see what is.";
|
|
7641
|
+
var listOf = (state) => state.run.escalations ?? [];
|
|
7642
|
+
var find = (state, id) => listOf(state).find((e) => e.id === id);
|
|
7643
|
+
var oneLine3 = (state, text) => truncate(sanitize(text).replace(/\s+/g, " ").trim(), Math.max(1, state.cols));
|
|
7644
|
+
var labelOf = (state, e) => e.task ? oneLine3(state, `${e.task.ref} ${e.task.title}`) : "The run";
|
|
7645
|
+
function inboxItems(state) {
|
|
7646
|
+
return listOf(state).map((e) => ({ id: e.id, label: labelOf(state, e), detail: oneLine3(state, e.reason) }));
|
|
7647
|
+
}
|
|
7648
|
+
function optionDetail(option) {
|
|
7649
|
+
if (option.confirm) return "asks first";
|
|
7650
|
+
if (option.note === "required") return "needs a note";
|
|
7651
|
+
if (option.note === "optional") return "takes a note";
|
|
7652
|
+
return void 0;
|
|
7653
|
+
}
|
|
7654
|
+
function answerItems(state, e) {
|
|
7655
|
+
const options = e.options.map((o) => ({ id: o.id, label: o.label, detail: optionDetail(o) }));
|
|
7656
|
+
return e.task ? [...options, { id: OPEN_TASK, label: "Open the task", detail: oneLine3(state, `${e.task.ref} ${e.task.title}`) }] : options;
|
|
7657
|
+
}
|
|
7658
|
+
function inboxPicker(state, index = 0) {
|
|
7659
|
+
return {
|
|
7660
|
+
title: "Waiting on you",
|
|
7661
|
+
items: inboxItems(state),
|
|
7662
|
+
filter: "",
|
|
7663
|
+
index,
|
|
7664
|
+
multi: false,
|
|
7665
|
+
chosen: [],
|
|
7666
|
+
filterable: false,
|
|
7667
|
+
action: { kind: "inbox" }
|
|
7668
|
+
};
|
|
7669
|
+
}
|
|
7670
|
+
function answerPicker(state, e) {
|
|
7671
|
+
return {
|
|
7672
|
+
title: labelOf(state, e),
|
|
7673
|
+
hint: oneLine3(state, e.reason),
|
|
7674
|
+
items: answerItems(state, e),
|
|
7675
|
+
filter: "",
|
|
7676
|
+
index: 0,
|
|
7677
|
+
multi: false,
|
|
7678
|
+
chosen: [],
|
|
7679
|
+
filterable: false,
|
|
7680
|
+
action: { kind: "inbox-answer", escalationId: e.id }
|
|
7681
|
+
};
|
|
7682
|
+
}
|
|
7683
|
+
function openInbox(state) {
|
|
7684
|
+
if (listOf(state).length === 0) return step(say(state, "system", NOTHING));
|
|
7685
|
+
return step({ ...state, overlay: { kind: "picker", picker: inboxPicker(state) } });
|
|
7686
|
+
}
|
|
7687
|
+
function chooseEscalation(state, id) {
|
|
7688
|
+
const e = find(state, id);
|
|
7689
|
+
if (!e) return gone(state);
|
|
7690
|
+
return step({ ...state, overlay: { kind: "picker", picker: answerPicker(state, e) } });
|
|
7691
|
+
}
|
|
7692
|
+
function chooseAnswer(state, escalationId, itemId) {
|
|
7693
|
+
const e = find(state, escalationId);
|
|
7694
|
+
if (!e) return gone(state);
|
|
7695
|
+
const closed = { ...state, overlay: null };
|
|
7696
|
+
if (itemId === OPEN_TASK) {
|
|
7697
|
+
return e.task && state.sessionId ? openTaskView(closed, state.sessionId, e.task.id) : step(closed);
|
|
7698
|
+
}
|
|
7699
|
+
const option = e.options.find((o) => o.id === itemId);
|
|
7700
|
+
if (!option) return gone(state);
|
|
7701
|
+
if (option.note) {
|
|
7702
|
+
return step({
|
|
7703
|
+
...state,
|
|
7704
|
+
overlay: {
|
|
7705
|
+
kind: "prompt",
|
|
7706
|
+
title: `${option.label}: ${oneLine3(state, e.reason)}`,
|
|
7707
|
+
hint: option.note === "required" ? "A note is required. Enter sends it, esc cancels." : "A note is optional: enter alone sends none, esc cancels.",
|
|
7708
|
+
value: "",
|
|
7709
|
+
action: { kind: "escalation-note", escalationId, option: option.id, required: option.note === "required", ...option.confirm ? { confirm: option.confirm } : {} }
|
|
7710
|
+
}
|
|
7711
|
+
});
|
|
7712
|
+
}
|
|
7713
|
+
return option.confirm ? askFirst(state, escalationId, option) : submit(closed, escalationId, option.id);
|
|
7714
|
+
}
|
|
7715
|
+
function noteEntered(state, action, note) {
|
|
7716
|
+
if (!note && action.required) return step(state);
|
|
7717
|
+
const closed = { ...state, overlay: null };
|
|
7718
|
+
const option = find(state, action.escalationId)?.options.find((o) => o.id === action.option);
|
|
7719
|
+
if (!option) return gone(closed);
|
|
7720
|
+
return option.confirm ? askFirst(state, action.escalationId, option, note) : submit(closed, action.escalationId, option.id, note);
|
|
7721
|
+
}
|
|
7722
|
+
function askFirst(state, escalationId, option, note) {
|
|
7723
|
+
return step({
|
|
7724
|
+
...state,
|
|
7725
|
+
overlay: {
|
|
7726
|
+
kind: "confirm",
|
|
7727
|
+
title: `${option.label}?`,
|
|
7728
|
+
message: sanitize(option.confirm ?? ""),
|
|
7729
|
+
action: { kind: "answer-escalation", escalationId, option: option.id, ...note ? { note } : {} }
|
|
7730
|
+
}
|
|
7731
|
+
});
|
|
7732
|
+
}
|
|
7733
|
+
function confirmedAnswer(state, escalationId, option, note) {
|
|
7734
|
+
return submit({ ...state, overlay: null }, escalationId, option, note, true);
|
|
7735
|
+
}
|
|
7736
|
+
function submit(state, escalationId, option, note, confirmed) {
|
|
7737
|
+
if (!state.sessionId) return fail3(state, "No active plan.");
|
|
7738
|
+
if (!find(state, escalationId)) return fail3(state, GONE);
|
|
7739
|
+
const effect = {
|
|
7740
|
+
type: "answerEscalation",
|
|
7741
|
+
sessionId: state.sessionId,
|
|
7742
|
+
id: escalationId,
|
|
7743
|
+
option,
|
|
7744
|
+
...note ? { note } : {},
|
|
7745
|
+
...confirmed ? { confirmed } : {},
|
|
7746
|
+
...SPAWNS_RUNNER.has(option) && state.status !== "executing" ? { watch: true } : {}
|
|
7747
|
+
};
|
|
7748
|
+
return step(state, [effect]);
|
|
7749
|
+
}
|
|
7750
|
+
function gone(state) {
|
|
7751
|
+
const refused = fail3(state, GONE).state;
|
|
7752
|
+
return step(listOf(state).length === 0 ? { ...refused, overlay: null } : { ...refused, overlay: { kind: "picker", picker: inboxPicker(state) } });
|
|
7753
|
+
}
|
|
7754
|
+
function refillInbox(state) {
|
|
7755
|
+
const overlay = state.overlay;
|
|
7756
|
+
if (overlay?.kind !== "picker") return state;
|
|
7757
|
+
const { action } = overlay.picker;
|
|
7758
|
+
if (action.kind !== "inbox" && action.kind !== "inbox-answer") return state;
|
|
7759
|
+
if (action.kind === "inbox") {
|
|
7760
|
+
if (listOf(state).length === 0) return say({ ...state, overlay: null }, "system", NOTHING);
|
|
7761
|
+
const kept2 = overlay.picker.items[overlay.picker.index]?.id;
|
|
7762
|
+
const index2 = Math.max(0, listOf(state).findIndex((e2) => e2.id === kept2));
|
|
7763
|
+
return { ...state, overlay: { kind: "picker", picker: { ...inboxPicker(state, index2) } } };
|
|
7764
|
+
}
|
|
7765
|
+
const e = find(state, action.escalationId);
|
|
7766
|
+
if (!e) return gone(state).state;
|
|
7767
|
+
const kept = overlay.picker.items[overlay.picker.index]?.id;
|
|
7768
|
+
const items = answerItems(state, e);
|
|
7769
|
+
const index = Math.max(0, items.findIndex((i) => i.id === kept));
|
|
7770
|
+
return { ...state, overlay: { kind: "picker", picker: { ...answerPicker(state, e), index } } };
|
|
7771
|
+
}
|
|
7772
|
+
|
|
7389
7773
|
// src/tui/reducers/stepper.ts
|
|
7390
|
-
var
|
|
7774
|
+
var import_core46 = require("@ordewell/core");
|
|
7391
7775
|
function openStepper(state, definition) {
|
|
7392
7776
|
const { choices, lists } = settle(state, definition, {});
|
|
7393
7777
|
const first = lists.findIndex((list2) => list2 !== null);
|
|
@@ -7410,7 +7794,7 @@ function handleStepperKey(state, stepper, key) {
|
|
|
7410
7794
|
const reopen = (next) => step({ ...state, overlay: { kind: "stepper", stepper: next } });
|
|
7411
7795
|
const { definition, current, entry, choices } = stepper;
|
|
7412
7796
|
const def = definition.steps[current];
|
|
7413
|
-
const typed = def.number && entry ? (0,
|
|
7797
|
+
const typed = def.number && entry ? (0, import_core46.parseCount)(entry.text, def.number.min) : null;
|
|
7414
7798
|
const { lists } = settle(state, definition, choices);
|
|
7415
7799
|
const items = matchingItems(stepper.items, stepper.filter);
|
|
7416
7800
|
const before = lastIndexBefore(lists, current);
|
|
@@ -7483,7 +7867,7 @@ function settle(state, definition, wanted) {
|
|
|
7483
7867
|
const lists = definition.steps.map((def) => {
|
|
7484
7868
|
if (def.skip?.(state, { ...choices })) return null;
|
|
7485
7869
|
if (def.number) {
|
|
7486
|
-
choices[def.id] = String((0,
|
|
7870
|
+
choices[def.id] = String((0, import_core46.parseCount)(wanted[def.id], def.number.min) ?? def.number.current(state, { ...choices }));
|
|
7487
7871
|
return [];
|
|
7488
7872
|
}
|
|
7489
7873
|
const items = def.items(state, { ...choices });
|
|
@@ -7522,149 +7906,10 @@ function lastIndexBefore(lists, index) {
|
|
|
7522
7906
|
}
|
|
7523
7907
|
|
|
7524
7908
|
// src/tui/reducers/planPane.ts
|
|
7525
|
-
var
|
|
7526
|
-
|
|
7527
|
-
// src/tui/reducers/taskView.ts
|
|
7528
|
-
var import_core44 = require("@ordewell/core");
|
|
7529
|
-
function openTaskView(state, sessionId, taskId) {
|
|
7530
|
-
const task = findTask(state.tasks, taskId);
|
|
7531
|
-
if (!task) return fail3(state, `No task ${taskId} in this plan.`);
|
|
7532
|
-
if (!(0, import_core44.taskRowView)(task).chatAvailable) {
|
|
7533
|
-
return fail3(state, "This task hasn\u2019t started yet. Its chat becomes available when it starts.");
|
|
7534
|
-
}
|
|
7535
|
-
const taskView = {
|
|
7536
|
-
taskId,
|
|
7537
|
-
...import_core44.EMPTY_TASK_LOG_FOLLOW,
|
|
7538
|
-
queuedIndex: null
|
|
7539
|
-
};
|
|
7540
|
-
return step({ ...state, taskView, focus: "chat", scroll: 0 }, [{ type: "openTaskLog", sessionId, taskId }]);
|
|
7541
|
-
}
|
|
7542
|
-
function continueTaskStep(state, sessionId, taskId, text) {
|
|
7543
|
-
const opened = state.taskView?.taskId === taskId ? step({ ...state, taskView: { ...state.taskView, followLatest: true } }) : openTaskView(state, sessionId, taskId);
|
|
7544
|
-
const watch = state.status !== "executing";
|
|
7545
|
-
return step(opened.state, [...opened.effects, { type: "continueTask", sessionId, taskId, text, ...watch ? { watch: true } : {} }]);
|
|
7546
|
-
}
|
|
7547
|
-
function clampQueueIndex(tv) {
|
|
7548
|
-
if (tv.queuedIndex === null) return tv;
|
|
7549
|
-
if (tv.view.queued.length === 0) return { ...tv, queuedIndex: null };
|
|
7550
|
-
const max = tv.view.queued.length - 1;
|
|
7551
|
-
return tv.queuedIndex <= max ? tv : { ...tv, queuedIndex: max };
|
|
7552
|
-
}
|
|
7553
|
-
function taskLogAbandoned(state) {
|
|
7554
|
-
const tv = state.taskView;
|
|
7555
|
-
if (!tv || tv.loaded) return state;
|
|
7556
|
-
return { ...state, taskView: clampQueueIndex({ ...tv, ...(0, import_core44.followTaskLog)(tv, { type: "loaded", attempt: tv.attempt, events: [], abandoned: true }) }) };
|
|
7557
|
-
}
|
|
7558
|
-
function taskLogArrived(state, action) {
|
|
7559
|
-
state = observeTaskServedModel(state, action);
|
|
7560
|
-
const tv = state.taskView;
|
|
7561
|
-
if (!tv || tv.taskId !== action.taskId) return state;
|
|
7562
|
-
const next = (0, import_core44.followTaskLog)(tv, { ...action, type: "live" });
|
|
7563
|
-
return { ...state, taskView: next === tv ? tv : clampQueueIndex({ ...tv, ...next }) };
|
|
7564
|
-
}
|
|
7565
|
-
function announceApprovals(state, action) {
|
|
7566
|
-
if (state.taskView?.taskId === action.taskId) return state;
|
|
7567
|
-
const task = findTask(state.tasks, action.taskId);
|
|
7568
|
-
const way = task ? `tab to the plan, select task ${refFor(state, task)} and press enter (or /open ${refFor(state, task)})` : "tab to the plan, select it and press enter";
|
|
7569
|
-
const spoken = action.events.reduce((s, event) => event.type === "approval_requested" && !event.decided ? say(s, "system", `Task ${task ? refFor(state, task) : "?"} waits for approval: ${(0, import_core44.runnerToolSubject)(event.tool, event.args)}: ${way}, then ctrl-y allows or ctrl-g denies`) : s, state);
|
|
7570
|
-
return spoken !== state ? { ...spoken, scroll: state.scroll } : spoken;
|
|
7571
|
-
}
|
|
7572
|
-
function announceCheckpoint(state, action) {
|
|
7573
|
-
const task = findTask(state.tasks, action.taskId) ?? state.tasks.find((t) => t.title === action.title);
|
|
7574
|
-
const label = task ? `Task ${refFor(state, task)}` : action.title;
|
|
7575
|
-
const where = task ? `tab to the plan, select task ${refFor(state, task)} and press enter (or /open ${refFor(state, task)}) to answer` : "tab to the plan, select it and press enter to answer";
|
|
7576
|
-
const spoken = say(state, "system", `${label} asks: ${(0, import_core44.truncateCheckpointSummary)(action.summary)}: ${where}`);
|
|
7577
|
-
return { ...spoken, scroll: state.scroll };
|
|
7578
|
-
}
|
|
7579
|
-
function taskLogLoaded(state, action) {
|
|
7580
|
-
state = observeTaskServedModel(state, action);
|
|
7581
|
-
const tv = state.taskView;
|
|
7582
|
-
if (!tv || tv.taskId !== action.taskId) return state;
|
|
7583
|
-
return { ...state, taskView: clampQueueIndex({ ...tv, ...(0, import_core44.followTaskLog)(tv, { ...action, type: "loaded" }) }) };
|
|
7584
|
-
}
|
|
7585
|
-
function answerApproval(state, tv, sessionId, decision) {
|
|
7586
|
-
const approval = waitingApproval(tv);
|
|
7587
|
-
if (!approval?.approvalId) return step(say(state, "system", "Nothing is waiting for approval."));
|
|
7588
|
-
if (decision === "allowForTask" && !approval.allowForTask) {
|
|
7589
|
-
return step(say(state, "system", "The runner offered no grant for the rest of this task: ctrl-y allows this call."));
|
|
7590
|
-
}
|
|
7591
|
-
const note = decision === "deny" ? state.editor.text.trim() : "";
|
|
7592
|
-
const answer = decision === "deny" ? { decision, ...note ? { note } : {} } : { decision };
|
|
7593
|
-
const next = note ? { ...state, editor: { ...state.editor, text: "", cursor: 0 } } : state;
|
|
7594
|
-
return step(next, [{ type: "answerTaskApproval", sessionId, approvalId: approval.approvalId, answer }]);
|
|
7595
|
-
}
|
|
7596
|
-
function answerCheckpoint(state, sessionId, taskId, answer, reason = "") {
|
|
7597
|
-
const task = findTask(state.tasks, taskId);
|
|
7598
|
-
if (!waitingCheckpoint(task)) return step(say(state, "system", `Task ${task?.order ?? taskId} is not waiting at a checkpoint.`));
|
|
7599
|
-
const note = answer === "reject" ? reason.trim() : "";
|
|
7600
|
-
return step(state, [{ type: "answerTaskCheckpoint", sessionId, taskId, answer, ...note ? { reason: note } : {} }]);
|
|
7601
|
-
}
|
|
7602
|
-
function answerWaiting(state, tv, sessionId, decision) {
|
|
7603
|
-
if (waitingApproval(tv) || !waitingCheckpoint(findTask(state.tasks, tv.taskId))) return answerApproval(state, tv, sessionId, decision);
|
|
7604
|
-
if (decision === "allow") return answerCheckpoint(state, sessionId, tv.taskId, "approve");
|
|
7605
|
-
const answered = answerCheckpoint(state, sessionId, tv.taskId, "reject", state.editor.text);
|
|
7606
|
-
return state.editor.text.trim() ? { ...answered, state: { ...answered.state, editor: { ...answered.state.editor, text: "", cursor: 0 } } } : answered;
|
|
7607
|
-
}
|
|
7608
|
-
function sendNow(state, tv, sessionId) {
|
|
7609
|
-
const text = state.editor.text.trim();
|
|
7610
|
-
if (text) {
|
|
7611
|
-
const task = findTask(state.tasks, tv.taskId);
|
|
7612
|
-
if (continuesTask(task)) {
|
|
7613
|
-
return step(say(state, "system", resumesTask(task) ? "The task is paused on a usage limit, so there is no turn to interrupt: enter resumes it." : "The task has finished, so there is no turn to interrupt: enter continues it."));
|
|
7614
|
-
}
|
|
7615
|
-
return step({ ...state, editor: commit(state.editor) }, [{ type: "forceSendTaskMessage", sessionId, taskId: tv.taskId, text }]);
|
|
7616
|
-
}
|
|
7617
|
-
const queued = tv.view.queued[queuedTarget(tv)];
|
|
7618
|
-
if (!queued) return step(say(state, "system", "Nothing to send now: type a message, or pick a queued one with ctrl-n/ctrl-p."));
|
|
7619
|
-
return step({ ...state, taskView: { ...tv, queuedIndex: null } }, [
|
|
7620
|
-
{ type: "forceSendQueuedTaskMessage", sessionId, taskId: tv.taskId, messageId: queued.id }
|
|
7621
|
-
]);
|
|
7622
|
-
}
|
|
7623
|
-
function handleTaskViewKey(state, key) {
|
|
7624
|
-
const tv = state.taskView;
|
|
7625
|
-
if (!tv || !state.sessionId) return null;
|
|
7626
|
-
if (key.name === "alt-left" || key.name === "alt-right") {
|
|
7627
|
-
const index = tv.attempts.indexOf(tv.loadingAttempt ?? tv.attempt);
|
|
7628
|
-
if (index < 0) return null;
|
|
7629
|
-
const target = key.name === "alt-left" ? index - 1 : index + 1;
|
|
7630
|
-
const attempt = tv.attempts[target];
|
|
7631
|
-
if (attempt === void 0) {
|
|
7632
|
-
return step(say(state, "system", target < 0 ? "This is the first attempt." : "This is the latest attempt."));
|
|
7633
|
-
}
|
|
7634
|
-
return step(
|
|
7635
|
-
{ ...state, taskView: { ...tv, ...(0, import_core44.followTaskLog)(tv, { type: "picked", attempt }) } },
|
|
7636
|
-
[{ type: "loadTaskAttempt", sessionId: state.sessionId, taskId: tv.taskId, attempt }]
|
|
7637
|
-
);
|
|
7638
|
-
}
|
|
7639
|
-
if (scriptTaskView(state)) return null;
|
|
7640
|
-
if (key.name === "ctrl-r") {
|
|
7641
|
-
const queued = tv.view.queued[queuedTarget(tv)];
|
|
7642
|
-
if (!queued) return step(say(state, "system", "No queued message to remove."));
|
|
7643
|
-
if (queued.handedOver) return step(say(state, "system", "The runner already has that message; it can no longer be taken back."));
|
|
7644
|
-
return step(state, [{ type: "removeTaskMessage", sessionId: state.sessionId, taskId: tv.taskId, messageId: queued.id }]);
|
|
7645
|
-
}
|
|
7646
|
-
if (key.name === "ctrl-s") return sendNow(state, tv, state.sessionId);
|
|
7647
|
-
if (key.name === "ctrl-x") {
|
|
7648
|
-
const task = findTask(state.tasks, tv.taskId);
|
|
7649
|
-
if (task && (0, import_core44.resumeLabel)(task)) return step(state, [{ type: "taskAction", sessionId: state.sessionId, taskId: tv.taskId, action: "cancel-resume" }]);
|
|
7650
|
-
return step(state, [{ type: "interruptTask", sessionId: state.sessionId, taskId: tv.taskId }]);
|
|
7651
|
-
}
|
|
7652
|
-
if (key.name === "ctrl-y") return answerWaiting(state, tv, state.sessionId, "allow");
|
|
7653
|
-
if (key.name === "ctrl-t") return answerApproval(state, tv, state.sessionId, "allowForTask");
|
|
7654
|
-
if (key.name === "ctrl-g") return answerWaiting(state, tv, state.sessionId, "deny");
|
|
7655
|
-
if (key.name === "ctrl-n" || key.name === "ctrl-p") {
|
|
7656
|
-
const count = tv.view.queued.length;
|
|
7657
|
-
if (count === 0) return null;
|
|
7658
|
-
const delta = key.name === "ctrl-n" ? 1 : -1;
|
|
7659
|
-
const from = queuedTarget(tv);
|
|
7660
|
-
const queuedIndex = from < 0 ? count - 1 : Math.max(0, Math.min(count - 1, from + delta));
|
|
7661
|
-
return step({ ...state, taskView: { ...tv, queuedIndex } });
|
|
7662
|
-
}
|
|
7663
|
-
return null;
|
|
7664
|
-
}
|
|
7909
|
+
var import_core48 = require("@ordewell/core");
|
|
7665
7910
|
|
|
7666
7911
|
// src/tui/reducers/taskEdits.ts
|
|
7667
|
-
var
|
|
7912
|
+
var import_core47 = require("@ordewell/core");
|
|
7668
7913
|
var import_plan_utils6 = require("@ordewell/core/plan-utils");
|
|
7669
7914
|
var noExecutor = (task, manual, script) => (0, import_plan_utils6.noExecutorRefusal)(task.type, manual, script);
|
|
7670
7915
|
function openTaskRunnerPicker(state, task) {
|
|
@@ -7779,7 +8024,7 @@ function openTaskModePicker(state, task) {
|
|
|
7779
8024
|
overlay: {
|
|
7780
8025
|
kind: "picker",
|
|
7781
8026
|
picker: picker(`Mode \xB7 ${refFor(state, task)}`, pickerItemsFor(state, action), action, {
|
|
7782
|
-
hint: [`Modes declared by ${task.assignedRunner}.`, (0,
|
|
8027
|
+
hint: [`Modes declared by ${task.assignedRunner}.`, (0, import_core47.codexSandboxNotice)(state.modesByRunner)?.line].filter(Boolean).join(" ")
|
|
7783
8028
|
})
|
|
7784
8029
|
}
|
|
7785
8030
|
}
|
|
@@ -7959,9 +8204,9 @@ var PLAN_SHORTCUTS = {
|
|
|
7959
8204
|
f: "force-start",
|
|
7960
8205
|
s: "skip"
|
|
7961
8206
|
};
|
|
7962
|
-
var
|
|
8207
|
+
var SPAWNS_RUNNER2 = ["force-start", "retry", "resume-now"];
|
|
7963
8208
|
function taskActionEffect(state, sessionId, taskId, action, confirmed) {
|
|
7964
|
-
const watch =
|
|
8209
|
+
const watch = SPAWNS_RUNNER2.includes(action) && state.status !== "executing";
|
|
7965
8210
|
return {
|
|
7966
8211
|
type: "taskAction",
|
|
7967
8212
|
sessionId,
|
|
@@ -7972,15 +8217,15 @@ function taskActionEffect(state, sessionId, taskId, action, confirmed) {
|
|
|
7972
8217
|
};
|
|
7973
8218
|
}
|
|
7974
8219
|
function confirmMark(state, task, direction, shortcut) {
|
|
7975
|
-
const needsYes = (0,
|
|
8220
|
+
const needsYes = (0, import_core48.overrideNeedsConfirmation)(task);
|
|
7976
8221
|
if (!state.sessionId) return fail3(state, "No active plan.");
|
|
7977
8222
|
if (!needsYes && !shortcut) return step(state, [taskActionEffect(state, state.sessionId, task.id, direction)]);
|
|
7978
8223
|
const activity = state.run.activity;
|
|
7979
8224
|
if (needsYes && activity?.advancesOnItsOwn) {
|
|
7980
|
-
return fail3(state, (0,
|
|
8225
|
+
return fail3(state, (0, import_core48.manualOverrideStopFirst)(direction, `task ${titledRefFor(state, task)}`));
|
|
7981
8226
|
}
|
|
7982
8227
|
const label = `task ${titledRefFor(state, task)}`;
|
|
7983
|
-
const message = needsYes ? (0,
|
|
8228
|
+
const message = needsYes ? (0, import_core48.manualOverrideConfirmation)(direction, label) : direction === "complete" ? `Mark ${label} done? It is your own sign-off: what depends on it can start.` : `Mark ${label} not done? Your sign-off is taken back and it waits until you retry or start it.`;
|
|
7984
8229
|
return step({
|
|
7985
8230
|
...state,
|
|
7986
8231
|
overlay: {
|
|
@@ -8033,11 +8278,11 @@ function handlePlanKey(state, key) {
|
|
|
8033
8278
|
if (!state.sessionId || key.name !== "char") return step(state);
|
|
8034
8279
|
if (key.char === "E") return startPlan(state, state.sessionId);
|
|
8035
8280
|
if (key.char === "S") return step(state, [{ type: "stopExecution", sessionId: state.sessionId }]);
|
|
8036
|
-
const shortcut = key.char === "c" && (0,
|
|
8281
|
+
const shortcut = key.char === "c" && (0, import_core48.resumeLabel)(task) ? "cancel-resume" : PLAN_SHORTCUTS[key.char ?? ""];
|
|
8037
8282
|
if (key.char === "r") {
|
|
8038
|
-
return (0,
|
|
8283
|
+
return (0, import_core48.taskRowActions)(task).includes("resume-now") ? step(state, [taskActionEffect(state, state.sessionId, task.id, "resume-now")]) : step(state);
|
|
8039
8284
|
}
|
|
8040
|
-
if (key.char === "m") return confirmMark(state, task, (0,
|
|
8285
|
+
if (key.char === "m") return confirmMark(state, task, (0, import_core48.markAction)(task), true);
|
|
8041
8286
|
const action = shortcut;
|
|
8042
8287
|
if (action === "force-start" && task.mergeGate?.length) return confirmForceStartPastGate(state, task);
|
|
8043
8288
|
if (action) {
|
|
@@ -8046,7 +8291,7 @@ function handlePlanKey(state, key) {
|
|
|
8046
8291
|
if (key.char === "O") return toggleTaskOps(state, task, row.parent !== null);
|
|
8047
8292
|
if (key.char === "d") return confirmRemoveTask(state, task);
|
|
8048
8293
|
if (key.char === "x" && task.isolation?.state === "conflict") {
|
|
8049
|
-
return step(state, [{ type: "resolveConflict", sessionId: state.sessionId, taskId: task.id }]);
|
|
8294
|
+
return step(state, [{ type: "resolveConflict", sessionId: state.sessionId, taskId: task.id, ...state.status === "executing" ? {} : { watch: true } }]);
|
|
8050
8295
|
}
|
|
8051
8296
|
if (key.char === "R") return openTaskRunnerPicker(state, task);
|
|
8052
8297
|
if (key.char === "o") return openTaskModelPicker(state, task);
|
|
@@ -8078,9 +8323,9 @@ function commitTaskEdit(state, task, editor) {
|
|
|
8078
8323
|
}
|
|
8079
8324
|
|
|
8080
8325
|
// src/tui/reducers/turnQueue.ts
|
|
8081
|
-
var
|
|
8326
|
+
var import_core49 = require("@ordewell/core");
|
|
8082
8327
|
function drainQueue(state, settled) {
|
|
8083
|
-
const next = (0,
|
|
8328
|
+
const next = (0, import_core49.drainNext)(state.queuedPrompts);
|
|
8084
8329
|
const ended = disarmStop({ ...settled, stopRequested: false, forceSendPending: null });
|
|
8085
8330
|
if (!next) return step(ended);
|
|
8086
8331
|
const spoken = say(ended, "user", next.text);
|
|
@@ -8095,7 +8340,7 @@ function restoreForced(state) {
|
|
|
8095
8340
|
if (!forced) return state;
|
|
8096
8341
|
const cleared = { ...state, forceSendPending: null };
|
|
8097
8342
|
if (forced.queueIndex === void 0) {
|
|
8098
|
-
const text = (0,
|
|
8343
|
+
const text = (0, import_core49.aheadOfDraft)(forced.text, state.editor.text);
|
|
8099
8344
|
return { ...cleared, editor: { ...state.editor, text, cursor: text.length } };
|
|
8100
8345
|
}
|
|
8101
8346
|
const queued = [...state.queuedPrompts];
|
|
@@ -8111,15 +8356,15 @@ function stopPlanning(state, sessionId) {
|
|
|
8111
8356
|
return step(queueToEditor(cutTurn({ ...dismissed, stopArmed: false, stopRequested: true, forceSendPending: null })), [{ type: "cancelPlanning", sessionId }]);
|
|
8112
8357
|
}
|
|
8113
8358
|
function queueToEditor(state) {
|
|
8114
|
-
const all = (0,
|
|
8359
|
+
const all = (0, import_core49.unsendAll)(state.queuedPrompts);
|
|
8115
8360
|
if (!all) return state;
|
|
8116
|
-
const text = (0,
|
|
8361
|
+
const text = (0, import_core49.aheadOfDraft)(all.text, state.editor.text);
|
|
8117
8362
|
return { ...state, queuedPrompts: all.rest, editor: { ...state.editor, text, cursor: all.text.length } };
|
|
8118
8363
|
}
|
|
8119
8364
|
function unsendLatest(state) {
|
|
8120
|
-
const latest = (0,
|
|
8365
|
+
const latest = (0, import_core49.unsendLatest)(state.queuedPrompts);
|
|
8121
8366
|
if (!latest) return step(state);
|
|
8122
|
-
const text = (0,
|
|
8367
|
+
const text = (0, import_core49.aheadOfDraft)(latest.text, state.editor.text);
|
|
8123
8368
|
return step({
|
|
8124
8369
|
...state,
|
|
8125
8370
|
queuedPrompts: latest.rest,
|
|
@@ -8135,7 +8380,7 @@ function forceSendPlanner(state) {
|
|
|
8135
8380
|
{ type: "forceSendPlannerMessage", sessionId: state.sessionId, message: text }
|
|
8136
8381
|
]);
|
|
8137
8382
|
}
|
|
8138
|
-
const latest = (0,
|
|
8383
|
+
const latest = (0, import_core49.unsendLatest)(state.queuedPrompts);
|
|
8139
8384
|
if (!latest) return step(say(state, "system", "Nothing to send now: type a message, or queue one with enter."));
|
|
8140
8385
|
const forced = { text: latest.text, queueIndex: latest.rest.length };
|
|
8141
8386
|
return step({ ...state, queuedPrompts: latest.rest, stopRequested: false, forceSendPending: forced }, [
|
|
@@ -8151,7 +8396,7 @@ function plannerEscape(state) {
|
|
|
8151
8396
|
}
|
|
8152
8397
|
|
|
8153
8398
|
// src/tui/reducers/incoming.ts
|
|
8154
|
-
var
|
|
8399
|
+
var import_core50 = require("@ordewell/core");
|
|
8155
8400
|
var planText = (value, fallback = "") => sanitize(String(value ?? fallback));
|
|
8156
8401
|
var planLabel = (value, fallback = "") => planText(value, fallback).replace(/\n+/g, " ");
|
|
8157
8402
|
var asRecord = (value) => typeof value === "object" && value !== null ? value : {};
|
|
@@ -8206,7 +8451,7 @@ function runTaskToView(t) {
|
|
|
8206
8451
|
...Array.isArray(model.availableVariants) ? { availableVariants: model.availableVariants.map((variant) => planLabel(variant)) } : {}
|
|
8207
8452
|
} : void 0,
|
|
8208
8453
|
isolation: t.isolation,
|
|
8209
|
-
awaitingReason: t.status === "awaiting_user" && (0,
|
|
8454
|
+
awaitingReason: t.status === "awaiting_user" && (0, import_core50.isAwaitingReason)(t.awaitingReason) ? t.awaitingReason : void 0,
|
|
8210
8455
|
...t.status === "awaiting_user" && t.awaitingReason === "usage-limit" && typeof t.resumeAt === "string" ? { resumeAt: t.resumeAt } : {},
|
|
8211
8456
|
...t.status === "skipped" && typeof t.skipReason === "string" ? { skipReason: sanitize(t.skipReason) } : {},
|
|
8212
8457
|
checkpoint: typeof t.checkpoint === "string" ? sanitize(t.checkpoint) : void 0,
|
|
@@ -8214,6 +8459,7 @@ function runTaskToView(t) {
|
|
|
8214
8459
|
awaitingApproval: t.awaitingApproval,
|
|
8215
8460
|
...t.waitsForApproval === true ? { waitsForApproval: true } : {},
|
|
8216
8461
|
...typeof t.holdReason === "string" && t.holdReason.trim() ? { holdReason: sanitize(t.holdReason) } : {},
|
|
8462
|
+
...t.loopStranded === true ? { loopStranded: true } : {},
|
|
8217
8463
|
...t.ops === true ? { ops: true } : {},
|
|
8218
8464
|
...t.research === true ? { research: true } : {},
|
|
8219
8465
|
// Drawn through the same sanitize/wrap as any other plan text; keeping the
|
|
@@ -8233,13 +8479,13 @@ function supervisorOfRun(runSettings) {
|
|
|
8233
8479
|
const unsupervised = typeof runSettings.unsupervised === "string" ? runSettings.unsupervised : void 0;
|
|
8234
8480
|
return {
|
|
8235
8481
|
runSupervisor: {
|
|
8236
|
-
level: (0,
|
|
8482
|
+
level: (0, import_core50.isSupervisorLevel)(runSettings.supervisorLevel) ? runSettings.supervisorLevel : "off",
|
|
8237
8483
|
...unsupervised ? { unsupervised } : {}
|
|
8238
8484
|
}
|
|
8239
8485
|
};
|
|
8240
8486
|
}
|
|
8241
8487
|
function withRun(state, run) {
|
|
8242
|
-
return {
|
|
8488
|
+
return refillInbox({
|
|
8243
8489
|
...state,
|
|
8244
8490
|
run,
|
|
8245
8491
|
tasks: run.tasks.map(runTaskToView),
|
|
@@ -8247,10 +8493,10 @@ function withRun(state, run) {
|
|
|
8247
8493
|
runMerge: run.runMerge ? { waitingAtGate: run.runMerge.waitingAtGate, paused: run.runMerge.paused, handoff: { repos: run.runMerge.repos, landed: run.runMerge.landed } } : null,
|
|
8248
8494
|
runAutoMerge: run.runSettings?.mergeAtGatesAutomatically ?? null,
|
|
8249
8495
|
...supervisorOfRun(run.runSettings)
|
|
8250
|
-
};
|
|
8496
|
+
});
|
|
8251
8497
|
}
|
|
8252
8498
|
function foldRun(state, input) {
|
|
8253
|
-
const run = (0,
|
|
8499
|
+
const run = (0, import_core50.reduceRun)(state.run, input);
|
|
8254
8500
|
return run === state.run ? state : withRun(state, run);
|
|
8255
8501
|
}
|
|
8256
8502
|
function applySettings(state, settings, applyRun = true) {
|
|
@@ -8269,6 +8515,7 @@ function applySettings(state, settings, applyRun = true) {
|
|
|
8269
8515
|
}
|
|
8270
8516
|
function followSession(state, message) {
|
|
8271
8517
|
const heard = hear(state, message);
|
|
8518
|
+
if (message.type === "planner_turn_started") return { ...heard, plannerDetecting: false };
|
|
8272
8519
|
if (message.type === "approval_request") {
|
|
8273
8520
|
const { id, kind, subject, scope, detail, body } = message;
|
|
8274
8521
|
if (kind === "runner_tool") return heard;
|
|
@@ -8278,7 +8525,7 @@ function followSession(state, message) {
|
|
|
8278
8525
|
}
|
|
8279
8526
|
|
|
8280
8527
|
// src/tui/reducers/settings.ts
|
|
8281
|
-
var
|
|
8528
|
+
var import_core51 = require("@ordewell/core");
|
|
8282
8529
|
var SUPERVISOR_DETAIL = {
|
|
8283
8530
|
off: "The planner speaks only when you write",
|
|
8284
8531
|
watch: "The planner follows your runs and suggests what to do",
|
|
@@ -8293,7 +8540,7 @@ var supervisorOff = (_state, choices) => choices.supervisor === "off";
|
|
|
8293
8540
|
var SETTINGS = {
|
|
8294
8541
|
title: "Settings",
|
|
8295
8542
|
afterSave: "stay",
|
|
8296
|
-
hint: "Supervisor, automerge and autorun apply to this session; your last change is the default for new ones. Autonomy and Parallel tasks apply to every session
|
|
8543
|
+
hint: `${import_core51.DELEGATION_OFFERED ? "Supervisor, automerge, autorun and delegation" : "Supervisor, automerge and autorun"} apply to this session; your last change is the default for new ones. Autonomy and Parallel tasks apply to every session.`,
|
|
8297
8544
|
steps: [
|
|
8298
8545
|
{
|
|
8299
8546
|
id: "autonomy",
|
|
@@ -8303,7 +8550,7 @@ var SETTINGS = {
|
|
|
8303
8550
|
{
|
|
8304
8551
|
id: "guarded",
|
|
8305
8552
|
label: "Guarded",
|
|
8306
|
-
detail: [`New plans run tasks in each runner's safer mode`, (0,
|
|
8553
|
+
detail: [`New plans run tasks in each runner's safer mode`, (0, import_core51.codexSandboxNotice)(state.modesByRunner)?.line].filter(Boolean).join(" \xB7 "),
|
|
8307
8554
|
selected: !state.autonomous
|
|
8308
8555
|
}
|
|
8309
8556
|
]
|
|
@@ -8313,7 +8560,7 @@ var SETTINGS = {
|
|
|
8313
8560
|
label: "Supervisor",
|
|
8314
8561
|
items: (state) => ["off", "watch", "act"].map((level) => ({
|
|
8315
8562
|
id: level,
|
|
8316
|
-
label:
|
|
8563
|
+
label: import_core51.SUPERVISOR_LEVEL_LABEL[level],
|
|
8317
8564
|
detail: SUPERVISOR_DETAIL[level],
|
|
8318
8565
|
selected: state.runSettings.supervisorLevel === level
|
|
8319
8566
|
}))
|
|
@@ -8348,7 +8595,14 @@ var SETTINGS = {
|
|
|
8348
8595
|
label: "Parallel",
|
|
8349
8596
|
hint: "How many AI tasks run at once, a live run included",
|
|
8350
8597
|
number: { min: 1, current: (state) => state.maxParallel }
|
|
8351
|
-
}
|
|
8598
|
+
},
|
|
8599
|
+
// Not offered until a delegated task exists (ADR-0055 D21).
|
|
8600
|
+
...import_core51.DELEGATION_OFFERED ? [{
|
|
8601
|
+
id: "delegation",
|
|
8602
|
+
label: "Delegation",
|
|
8603
|
+
hint: "The token budget of a tree of plans; 0 keeps delegation off",
|
|
8604
|
+
number: { min: 0, current: (state) => state.runSettings.delegationTokens }
|
|
8605
|
+
}] : []
|
|
8352
8606
|
],
|
|
8353
8607
|
save: saveChoices
|
|
8354
8608
|
};
|
|
@@ -8360,12 +8614,12 @@ function saveChoices(state, choices) {
|
|
|
8360
8614
|
const autonomous = choices.autonomy === "full";
|
|
8361
8615
|
if (autonomous !== state.autonomous) {
|
|
8362
8616
|
save2.autonomous = autonomous;
|
|
8363
|
-
changed.push(`Autonomy ${(0,
|
|
8617
|
+
changed.push(`Autonomy ${(0, import_core51.autonomyLevelLabel)(autonomous)}`);
|
|
8364
8618
|
}
|
|
8365
8619
|
const level = choices.supervisor;
|
|
8366
8620
|
if (level !== state.runSettings.supervisorLevel) {
|
|
8367
8621
|
run.supervisorLevel = level;
|
|
8368
|
-
changed.push(`Supervisor ${
|
|
8622
|
+
changed.push(`Supervisor ${import_core51.SUPERVISOR_LEVEL_LABEL[level]}`);
|
|
8369
8623
|
}
|
|
8370
8624
|
if (choices.retries !== void 0 && Number(choices.retries) !== state.runSettings.supervisorRetryLimit) {
|
|
8371
8625
|
run.supervisorRetryLimit = Number(choices.retries);
|
|
@@ -8386,9 +8640,15 @@ function saveChoices(state, choices) {
|
|
|
8386
8640
|
save2.maxParallel = parallel;
|
|
8387
8641
|
changed.push(`Parallel ${parallel}`);
|
|
8388
8642
|
}
|
|
8643
|
+
const delegationTokens = Number(choices.delegation);
|
|
8644
|
+
if (choices.delegation !== void 0 && delegationTokens !== state.runSettings.delegationTokens) {
|
|
8645
|
+
run.delegationTokens = delegationTokens;
|
|
8646
|
+
changed.push(delegationTokens > 0 ? `Delegation on, ${delegationTokens} tokens` : "Delegation off");
|
|
8647
|
+
}
|
|
8389
8648
|
if (changed.length === 0) return step(state);
|
|
8390
8649
|
const effect = { ...save2, message: `Settings: ${changed.join(" \xB7 ")}` };
|
|
8391
|
-
const
|
|
8650
|
+
const delegationOn = state.runSettings.delegationTokens === 0 && (run.delegationTokens ?? 0) > 0;
|
|
8651
|
+
const risks = [...run.supervisorLevel === "act" ? [import_core51.ACT_RISK] : [], ...run.autorun ? [import_core51.AUTORUN_RISK] : [], ...delegationOn ? [import_core51.DELEGATION_RISK] : []];
|
|
8392
8652
|
if (risks.length === 0) return saveSettings(state, effect);
|
|
8393
8653
|
return step({
|
|
8394
8654
|
...state,
|
|
@@ -8500,6 +8760,8 @@ function runCommand(state, parsed) {
|
|
|
8500
8760
|
});
|
|
8501
8761
|
case "open":
|
|
8502
8762
|
return taskCommand(state, args[0], (sessionId, taskId) => openTaskView(state, sessionId, taskId));
|
|
8763
|
+
case "inbox":
|
|
8764
|
+
return openInbox(state);
|
|
8503
8765
|
case "checkpoint":
|
|
8504
8766
|
return taskCommand(state, args[0], (sessionId, taskId) => {
|
|
8505
8767
|
const answer = args[1]?.toLowerCase();
|
|
@@ -8508,6 +8770,8 @@ function runCommand(state, parsed) {
|
|
|
8508
8770
|
if (answer === "approve" && reason) return fail3(state, "Approving takes no note: reject with a reason to tell the task something.");
|
|
8509
8771
|
return answerCheckpoint(state, sessionId, taskId, answer, reason);
|
|
8510
8772
|
});
|
|
8773
|
+
case "revert":
|
|
8774
|
+
return taskCommand(state, args[0], (sessionId, taskId) => step(state, [{ type: "previewRevert", sessionId, taskId }]));
|
|
8511
8775
|
case "task-runner":
|
|
8512
8776
|
return taskRunnerCommand(state, args);
|
|
8513
8777
|
case "task-model":
|
|
@@ -8577,8 +8841,8 @@ function newSession(state) {
|
|
|
8577
8841
|
goal: "",
|
|
8578
8842
|
taskView: null,
|
|
8579
8843
|
planApproved: false,
|
|
8580
|
-
conversation:
|
|
8581
|
-
turnGate:
|
|
8844
|
+
conversation: import_core52.EMPTY_CONVERSATION,
|
|
8845
|
+
turnGate: import_core52.NO_TURN,
|
|
8582
8846
|
selectedTask: 0,
|
|
8583
8847
|
expandedTaskId: null,
|
|
8584
8848
|
taskEditor: null,
|
|
@@ -8591,16 +8855,16 @@ function newSession(state) {
|
|
|
8591
8855
|
// Prompts belong to the session that raised them; the old planner is gone
|
|
8592
8856
|
// and its pending requests deny on their own timeout.
|
|
8593
8857
|
pendingApprovals: [],
|
|
8594
|
-
queuedPrompts:
|
|
8858
|
+
queuedPrompts: import_core52.EMPTY_HOLD,
|
|
8595
8859
|
stopArmed: false,
|
|
8596
8860
|
forceSendPending: null,
|
|
8597
8861
|
overlay: state.overlay?.kind === "approval" ? null : state.overlay
|
|
8598
|
-
},
|
|
8862
|
+
}, import_core52.EMPTY_RUN), closeEffects);
|
|
8599
8863
|
}
|
|
8600
8864
|
function setPlanner(state, args) {
|
|
8601
8865
|
if (args[0]) {
|
|
8602
8866
|
const id = args[0].toLowerCase();
|
|
8603
|
-
if (!(0,
|
|
8867
|
+
if (!(0, import_core52.isPlannerBackend)(id)) {
|
|
8604
8868
|
return fail3(state, `Unknown planner: ${id}. Run /planner with no arguments to see the list.`);
|
|
8605
8869
|
}
|
|
8606
8870
|
return step(state, [{ type: "setPlanner", provider: id }]);
|
|
@@ -8611,7 +8875,7 @@ function setPlanner(state, args) {
|
|
|
8611
8875
|
function setKey(state, args) {
|
|
8612
8876
|
if (args[0] === "set" && args[1]) {
|
|
8613
8877
|
const provider = args[1].toLowerCase();
|
|
8614
|
-
if (!(0,
|
|
8878
|
+
if (!(0, import_core52.isApiProvider)(provider)) {
|
|
8615
8879
|
return fail3(state, `Unknown provider: ${provider}. Run /key with no arguments to see the list.`);
|
|
8616
8880
|
}
|
|
8617
8881
|
if (!args[2]) {
|
|
@@ -8622,16 +8886,16 @@ function setKey(state, args) {
|
|
|
8622
8886
|
}
|
|
8623
8887
|
return step(state, [{ type: "setApiKey", provider, key: args.slice(2).join(" ") }]);
|
|
8624
8888
|
}
|
|
8625
|
-
const items =
|
|
8889
|
+
const items = import_core52.PROVIDER_PRIORITY.map((id) => ({
|
|
8626
8890
|
id,
|
|
8627
|
-
label: (0,
|
|
8628
|
-
detail: (0,
|
|
8891
|
+
label: (0, import_core52.getProviderMeta)(id).label,
|
|
8892
|
+
detail: (0, import_core52.getProviderMeta)(id).apiKeyEnvVar,
|
|
8629
8893
|
selected: state.configuredProviders.includes(id)
|
|
8630
8894
|
}));
|
|
8631
8895
|
return step({ ...state, overlay: { kind: "picker", picker: picker("API provider key", items, { kind: "set-key" }) } });
|
|
8632
8896
|
}
|
|
8633
8897
|
function keyPrompt(provider) {
|
|
8634
|
-
const meta = (0,
|
|
8898
|
+
const meta = (0, import_core52.getProviderMeta)(provider);
|
|
8635
8899
|
return {
|
|
8636
8900
|
kind: "prompt",
|
|
8637
8901
|
title: `${meta.label} API key`,
|
|
@@ -8711,13 +8975,13 @@ function login(state, args) {
|
|
|
8711
8975
|
return step(say(state, "system", "Still checking which coding agents are installed. Try /login again in a moment."), [{ type: "loadRunners" }]);
|
|
8712
8976
|
}
|
|
8713
8977
|
const named = args.join(" ").trim();
|
|
8714
|
-
const runner = named ? matchRunner(state, named) : (0,
|
|
8978
|
+
const runner = named ? matchRunner(state, named) : (0, import_core52.runnerForProvider)(state.plannerProvider);
|
|
8715
8979
|
if (!runner) {
|
|
8716
8980
|
return step(say(state, "system", named ? `No installed coding agent matches "${named}". ${signInHint(state)}` : `The active planner is not a coding agent, so there is no agent to sign in. ${signInHint(state)}`));
|
|
8717
8981
|
}
|
|
8718
8982
|
const row = state.runners.find((r) => r.id === runner);
|
|
8719
8983
|
if (!row) {
|
|
8720
|
-
const label = (0,
|
|
8984
|
+
const label = (0, import_core52.getProviderMeta)(state.plannerProvider)?.label ?? runner;
|
|
8721
8985
|
return step(say(state, "system", `${label} is not installed, so there is nothing to sign in. /runners lists what is.`));
|
|
8722
8986
|
}
|
|
8723
8987
|
const command = row.auth?.loginCommand;
|
|
@@ -8838,9 +9102,14 @@ function runConfirm(state, overlay) {
|
|
|
8838
9102
|
const { taskId, direction, seen } = overlay.action;
|
|
8839
9103
|
const task = state.sessionId ? findTask(state.tasks, taskId) : void 0;
|
|
8840
9104
|
if (!state.sessionId || !task) return fail3(closed, "That task is no longer in the plan: nothing was changed.");
|
|
8841
|
-
return step(closed, [taskActionEffect(closed, state.sessionId, taskId, direction, (0,
|
|
9105
|
+
return step(closed, [taskActionEffect(closed, state.sessionId, taskId, direction, (0, import_core53.overrideNeedsConfirmation)(task) ? { seen } : void 0)]);
|
|
9106
|
+
}
|
|
9107
|
+
if (overlay.action.kind === "revert-task") {
|
|
9108
|
+
if (!state.sessionId) return step(closed);
|
|
9109
|
+
return step(closed, [{ type: "revertTask", sessionId: state.sessionId, taskId: overlay.action.taskId, seen: overlay.action.seen }]);
|
|
8842
9110
|
}
|
|
8843
9111
|
if (overlay.action.kind === "save-settings") return answerSaveSettings(state, overlay.action, true);
|
|
9112
|
+
if (overlay.action.kind === "answer-escalation") return confirmedAnswer(state, overlay.action.escalationId, overlay.action.option, overlay.action.note);
|
|
8844
9113
|
if (overlay.action.kind === "merge-run" || overlay.action.kind === "discard-run") {
|
|
8845
9114
|
return confirmedHandoff(state, overlay.action.kind);
|
|
8846
9115
|
}
|
|
@@ -8868,6 +9137,7 @@ function handlePromptKey(state, overlay, key) {
|
|
|
8868
9137
|
if (key.name === "enter") {
|
|
8869
9138
|
const value2 = overlay.value.trim();
|
|
8870
9139
|
const closed = { ...state, overlay: null };
|
|
9140
|
+
if (overlay.action.kind === "escalation-note") return noteEntered(state, overlay.action, value2);
|
|
8871
9141
|
if (!value2) return step(closed);
|
|
8872
9142
|
if (overlay.action.kind === "api-key") {
|
|
8873
9143
|
return step(closed, [{ type: "setApiKey", provider: overlay.action.provider, key: value2 }]);
|
|
@@ -8944,12 +9214,16 @@ function choose2(state, picker2, item) {
|
|
|
8944
9214
|
switch (picker2.action.kind) {
|
|
8945
9215
|
case "load-session":
|
|
8946
9216
|
return step(closed, [{ type: "loadSession", sessionId: item.id }]);
|
|
9217
|
+
case "inbox":
|
|
9218
|
+
return chooseEscalation(state, item.id);
|
|
9219
|
+
case "inbox-answer":
|
|
9220
|
+
return chooseAnswer(state, picker2.action.escalationId, item.id);
|
|
8947
9221
|
case "rewind":
|
|
8948
9222
|
return pickRewindTarget(state, Number(item.id));
|
|
8949
9223
|
case "delete-session":
|
|
8950
9224
|
return step(closed, [{ type: "deleteSession", sessionId: item.id }]);
|
|
8951
9225
|
case "set-key":
|
|
8952
|
-
return (0,
|
|
9226
|
+
return (0, import_core53.isApiProvider)(item.id) ? step({ ...state, overlay: keyPrompt(item.id) }) : step(closed);
|
|
8953
9227
|
case "choose-allowlist-runner": {
|
|
8954
9228
|
const action = { kind: "set-allowlist", runner: item.id };
|
|
8955
9229
|
const items = pickerItemsFor(state, action);
|
|
@@ -9005,6 +9279,24 @@ function choose2(state, picker2, item) {
|
|
|
9005
9279
|
}
|
|
9006
9280
|
}
|
|
9007
9281
|
|
|
9282
|
+
// src/tui/reducers/revert.ts
|
|
9283
|
+
var import_core54 = require("@ordewell/core");
|
|
9284
|
+
function revertPreviewed(state, taskId, preview) {
|
|
9285
|
+
if (!preview.revertible) return fail3(state, sanitize(preview.message));
|
|
9286
|
+
const asked = preview.tasks.find((t) => t.taskId === taskId) ?? preview.tasks.at(-1);
|
|
9287
|
+
const lines = preview.tasks.map((t) => ` ${sanitize((0, import_core54.revertListLine)(t))}`);
|
|
9288
|
+
return step({
|
|
9289
|
+
...state,
|
|
9290
|
+
overlay: {
|
|
9291
|
+
kind: "confirm",
|
|
9292
|
+
title: `Revert ${asked ? `${(0, import_core54.revertTaskRef)(asked)} ${sanitize(asked.title)}` : "this task"}?`,
|
|
9293
|
+
message: import_core54.revertEffect,
|
|
9294
|
+
note: ["Reverting:", ...lines].join("\n"),
|
|
9295
|
+
action: { kind: "revert-task", taskId, seen: preview.tasks.map((t) => t.taskId) }
|
|
9296
|
+
}
|
|
9297
|
+
});
|
|
9298
|
+
}
|
|
9299
|
+
|
|
9008
9300
|
// src/tui/reducers/compaction.ts
|
|
9009
9301
|
var running = (state) => state.conversation.blocks.some((b) => b.type === "compaction" && b.status === "running");
|
|
9010
9302
|
function followCompaction(state, message) {
|
|
@@ -9048,7 +9340,7 @@ function reduceAction(state, action) {
|
|
|
9048
9340
|
// (`task-1`), so nothing of the last one's — a merge gate, a checkpoint, its
|
|
9049
9341
|
// tasks — may carry over into this.
|
|
9050
9342
|
case "sessionStarted":
|
|
9051
|
-
return step(withRun({ ...state, sessionId: action.sessionId, goal: action.goal, accountLimits: [], servedModels: {}, taskView: null, status: "idle" },
|
|
9343
|
+
return step(withRun({ ...state, sessionId: action.sessionId, goal: action.goal, accountLimits: [], servedModels: {}, taskView: null, status: "idle" }, import_core55.EMPTY_RUN));
|
|
9052
9344
|
// A loaded session is a different one: the turn the old one had out, the
|
|
9053
9345
|
// prompts held behind it and a stop armed against it are not this one's.
|
|
9054
9346
|
// Nor is an open prompt editor: task ids repeat across plans, so the
|
|
@@ -9069,15 +9361,15 @@ function reduceAction(state, action) {
|
|
|
9069
9361
|
planner: "idle",
|
|
9070
9362
|
plannerPause: null,
|
|
9071
9363
|
plannerResend: null,
|
|
9072
|
-
turnGate:
|
|
9364
|
+
turnGate: import_core55.NO_TURN,
|
|
9073
9365
|
pendingApprovals: [],
|
|
9074
|
-
queuedPrompts:
|
|
9366
|
+
queuedPrompts: import_core55.EMPTY_HOLD,
|
|
9075
9367
|
stopArmed: false,
|
|
9076
9368
|
stopRequested: false,
|
|
9077
9369
|
forceSendPending: null,
|
|
9078
9370
|
overlay: state.overlay?.kind === "approval" ? null : state.overlay,
|
|
9079
9371
|
runSettingsKnown: false
|
|
9080
|
-
},
|
|
9372
|
+
}, import_core55.EMPTY_RUN));
|
|
9081
9373
|
// The daemon registers a session before its opening turn runs, and drops it
|
|
9082
9374
|
// itself when it refuses the turn or the goal is discarded; any other
|
|
9083
9375
|
// failure it answered leaves no conversation a reply could continue, so the
|
|
@@ -9097,20 +9389,20 @@ function reduceAction(state, action) {
|
|
|
9097
9389
|
status: "idle",
|
|
9098
9390
|
plannerPause: null,
|
|
9099
9391
|
plannerResend: null,
|
|
9100
|
-
turnGate:
|
|
9392
|
+
turnGate: import_core55.NO_TURN,
|
|
9101
9393
|
pendingApprovals: [],
|
|
9102
9394
|
stopArmed: false,
|
|
9103
9395
|
forceSendPending: null,
|
|
9104
9396
|
overlay: state.overlay?.kind === "approval" ? null : state.overlay,
|
|
9105
9397
|
runSettingsKnown: action.unreached ? state.runSettingsKnown : false
|
|
9106
|
-
},
|
|
9398
|
+
}, import_core55.EMPTY_RUN));
|
|
9107
9399
|
// Deleting the session on screen stops it in the daemon; a view left on it
|
|
9108
9400
|
// would answer every next message with a session-not-found.
|
|
9109
9401
|
case "sessionDeleted":
|
|
9110
9402
|
return action.sessionId === state.sessionId ? newSession(state) : step(state);
|
|
9111
9403
|
case "chatRestored":
|
|
9112
9404
|
if (stale(state, action.sessionId)) return step(state);
|
|
9113
|
-
return step({ ...state, conversation: (0,
|
|
9405
|
+
return step({ ...state, conversation: (0, import_core55.fromTranscript)(action.history, action.researchLog, action.plannerUsage, action.pendingTurns), scroll: 0 });
|
|
9114
9406
|
case "planRefreshed":
|
|
9115
9407
|
if (stale(state, action.sessionId)) return step(state);
|
|
9116
9408
|
return step(showPlan(state, action.plan));
|
|
@@ -9126,7 +9418,7 @@ function reduceAction(state, action) {
|
|
|
9126
9418
|
return step({ ...settled, forceSendPending: null });
|
|
9127
9419
|
}
|
|
9128
9420
|
if (state.forceSendPending) return step({ ...settled, planner: "planning", forceSendPending: null });
|
|
9129
|
-
return drainQueue(state, settled);
|
|
9421
|
+
return drainQueue(state, { ...settled, turnGate: (0, import_core55.closeTurn)(settled.turnGate) });
|
|
9130
9422
|
}
|
|
9131
9423
|
// The call that carried the turn's reply died, so nothing will say the turn
|
|
9132
9424
|
// is over but the daemon. The prompts held behind it, and the turn itself,
|
|
@@ -9153,7 +9445,7 @@ function reduceAction(state, action) {
|
|
|
9153
9445
|
const gate = action.plannerTurnId === state.turnGate.open ? state.turnGate : { open: action.plannerTurnId, stopped: null };
|
|
9154
9446
|
return step(showPlan({ ...state, turnGate: gate }, action.plan));
|
|
9155
9447
|
}
|
|
9156
|
-
const redrawn = reduceAction({ ...state, turnGate:
|
|
9448
|
+
const redrawn = reduceAction({ ...state, turnGate: import_core55.NO_TURN }, action.chat).state;
|
|
9157
9449
|
const settled = reduceAction(redrawn, { type: "planUpdated", plan: action.plan, sessionId: action.sessionId });
|
|
9158
9450
|
if (state.planner === "joined") return settled;
|
|
9159
9451
|
return { ...settled, state: say(settled.state, "system", `The reply to your last message was lost on the way${because(action.reason)}; this is what the server holds.`) };
|
|
@@ -9171,6 +9463,7 @@ function reduceAction(state, action) {
|
|
|
9171
9463
|
case "sessionMessage": {
|
|
9172
9464
|
if (stale(state, action.sessionId)) return step(state);
|
|
9173
9465
|
if (action.message.type === "planner_compaction") return followCompaction(state, action.message);
|
|
9466
|
+
if (action.message.type === "planner_detection") return step({ ...state, plannerDetecting: action.message.waiting });
|
|
9174
9467
|
const next = followSession(state, action.message);
|
|
9175
9468
|
const { sessionId } = state;
|
|
9176
9469
|
const gateClosed = action.message.type === "planner_turn_ended" && next.turnGate.open === null;
|
|
@@ -9205,15 +9498,15 @@ function reduceAction(state, action) {
|
|
|
9205
9498
|
case "taskStarted": {
|
|
9206
9499
|
if (stale(state, action.sessionId)) return step(state);
|
|
9207
9500
|
const existing = findTask(state.tasks, action.taskId);
|
|
9208
|
-
if (existing && (0,
|
|
9501
|
+
if (existing && (0, import_core33.isTaskRunning)(existing)) return step(state);
|
|
9209
9502
|
const folded = foldRun(state, {
|
|
9210
9503
|
type: "task_started",
|
|
9211
9504
|
taskId: action.taskId,
|
|
9212
9505
|
order: action.order ?? 0,
|
|
9213
9506
|
title: action.title,
|
|
9214
|
-
runner: action.runner ??
|
|
9507
|
+
runner: action.runner ?? import_core55.DEFAULT_RUNNERS[0]
|
|
9215
9508
|
});
|
|
9216
|
-
const spoken = say(folded, "system", (0,
|
|
9509
|
+
const spoken = say(folded, "system", (0, import_core55.taskStartedNotice)(action.title, action.runner));
|
|
9217
9510
|
return step({ ...spoken, status: "executing" });
|
|
9218
9511
|
}
|
|
9219
9512
|
case "taskCheckpoint":
|
|
@@ -9231,7 +9524,8 @@ function reduceAction(state, action) {
|
|
|
9231
9524
|
tasks: action.tasks,
|
|
9232
9525
|
...action.runMerge !== void 0 ? { runMerge: action.runMerge } : {},
|
|
9233
9526
|
...action.usage !== void 0 ? { usage: action.usage } : {},
|
|
9234
|
-
...action.activity !== void 0 ? { activity: action.activity } : {}
|
|
9527
|
+
...action.activity !== void 0 ? { activity: action.activity } : {},
|
|
9528
|
+
...action.escalations !== void 0 ? { escalations: action.escalations } : {}
|
|
9235
9529
|
});
|
|
9236
9530
|
const status = runStatus(next, next.tasks);
|
|
9237
9531
|
return step({ ...next, status });
|
|
@@ -9266,7 +9560,7 @@ function reduceAction(state, action) {
|
|
|
9266
9560
|
completed: state.tasks.filter((t) => t.status === "completed").length,
|
|
9267
9561
|
total: state.tasks.length,
|
|
9268
9562
|
failed: state.tasks.filter((t) => t.status === "failed").length,
|
|
9269
|
-
blocked: (0,
|
|
9563
|
+
blocked: (0, import_core55.blockingFailures)(state.tasks).size,
|
|
9270
9564
|
skipped: state.tasks.filter((t) => t.status === "skipped").length
|
|
9271
9565
|
};
|
|
9272
9566
|
const failures = `${failed > 0 ? ` \xB7 ${failed} failed` : ""}${blocked ? ` \xB7 ${blocked} blocked` : ""}${skipped ? ` \xB7 ${skipped} skipped` : ""}`;
|
|
@@ -9334,6 +9628,8 @@ function reduceAction(state, action) {
|
|
|
9334
9628
|
));
|
|
9335
9629
|
case "sessionsLoaded":
|
|
9336
9630
|
return step(refillPicker({ ...state, sessions: action.sessions }, ["load-session", "delete-session"]));
|
|
9631
|
+
case "revertPreviewed":
|
|
9632
|
+
return stale(state, action.sessionId) ? step(state) : revertPreviewed(state, action.taskId, action.preview);
|
|
9337
9633
|
case "rewindTargetsLoaded":
|
|
9338
9634
|
if (stale(state, action.sessionId)) return step(state);
|
|
9339
9635
|
if (action.pick !== void 0) return pickRewindTarget({ ...state, rewindTargets: action.targets }, action.pick);
|
|
@@ -9353,16 +9649,16 @@ function reduceAction(state, action) {
|
|
|
9353
9649
|
taskEditor: null,
|
|
9354
9650
|
status: "idle",
|
|
9355
9651
|
planner: "idle",
|
|
9356
|
-
turnGate:
|
|
9652
|
+
turnGate: import_core55.NO_TURN,
|
|
9357
9653
|
planApproved: false,
|
|
9358
9654
|
pendingApprovals: [],
|
|
9359
|
-
queuedPrompts:
|
|
9655
|
+
queuedPrompts: import_core55.EMPTY_HOLD,
|
|
9360
9656
|
stopArmed: false,
|
|
9361
9657
|
stopRequested: false,
|
|
9362
9658
|
forceSendPending: null,
|
|
9363
9659
|
overlay: state.overlay?.kind === "approval" ? null : state.overlay,
|
|
9364
9660
|
runSettingsKnown: false
|
|
9365
|
-
},
|
|
9661
|
+
}, import_core55.EMPTY_RUN));
|
|
9366
9662
|
// The rewound message comes back as a draft to edit and resend, so it lands
|
|
9367
9663
|
// as if just typed: history is not being browsed, and nothing is parked.
|
|
9368
9664
|
case "inputPrefilled":
|
|
@@ -9418,7 +9714,7 @@ function reduceAction(state, action) {
|
|
|
9418
9714
|
status: state.status === "starting" ? "idle" : state.status
|
|
9419
9715
|
};
|
|
9420
9716
|
if (!plannerInFlight(state)) return step(reported);
|
|
9421
|
-
return drainQueue(state, reported);
|
|
9717
|
+
return drainQueue(state, { ...reported, turnGate: (0, import_core55.closeTurn)(reported.turnGate) });
|
|
9422
9718
|
}
|
|
9423
9719
|
// As `failed`: an opening that let itself go asks on the welcome it left,
|
|
9424
9720
|
// never over a session loaded meanwhile.
|
|
@@ -9427,6 +9723,7 @@ function reduceAction(state, action) {
|
|
|
9427
9723
|
return step({
|
|
9428
9724
|
...state,
|
|
9429
9725
|
planner: "idle",
|
|
9726
|
+
turnGate: plannerInFlight(state) ? (0, import_core55.closeTurn)(state.turnGate) : state.turnGate,
|
|
9430
9727
|
overlay: {
|
|
9431
9728
|
kind: "confirm",
|
|
9432
9729
|
title: "Initialize new workspace?",
|
|
@@ -9459,7 +9756,7 @@ function reduceAction(state, action) {
|
|
|
9459
9756
|
// Whether there is anything to animate is the app loop's call (it owns the
|
|
9460
9757
|
// timer); a tick that arrives simply advances the frame.
|
|
9461
9758
|
case "spinnerTick":
|
|
9462
|
-
return step({ ...state, spinnerFrame: (state.spinnerFrame +
|
|
9759
|
+
return step({ ...state, spinnerFrame: (state.spinnerFrame + 1) % SPINNER_CYCLE });
|
|
9463
9760
|
// The scheduled expiry of an armed stop; a token that is no longer the live
|
|
9464
9761
|
// arm is a stale timer (the user disarmed and re-armed in between) and is
|
|
9465
9762
|
// ignored, so it cannot cut a newer arm short.
|
|
@@ -9501,7 +9798,7 @@ function runStatus(state, tasks) {
|
|
|
9501
9798
|
const activity = state.run.activity;
|
|
9502
9799
|
if (activity?.starting) return "starting";
|
|
9503
9800
|
if (activity?.running) return "executing";
|
|
9504
|
-
if (tasks.some(
|
|
9801
|
+
if (tasks.some(import_core33.isTaskRunning)) return "executing";
|
|
9505
9802
|
if (tasks.some((t) => t.status === "awaiting_user") || state.runMerge?.paused) return "executing";
|
|
9506
9803
|
return "idle";
|
|
9507
9804
|
}
|
|
@@ -9533,7 +9830,7 @@ function handleKey2(state, key) {
|
|
|
9533
9830
|
if (key.name === "wheelignored") return step(state);
|
|
9534
9831
|
if (key.name === "tab" && state.focus === "chat") {
|
|
9535
9832
|
const token = activeToken(state.editor.text, state.editor.cursor);
|
|
9536
|
-
const matches = token ? composerCompletions(token, state.taskView ? (0,
|
|
9833
|
+
const matches = token ? composerCompletions(token, state.taskView ? (0, import_core55.taskLogCommands)(state.taskView.view) : null) : [];
|
|
9537
9834
|
if (token && matches.length > 0) {
|
|
9538
9835
|
const { text: full } = state.editor;
|
|
9539
9836
|
const completed = `/${matches[0]} `;
|
|
@@ -9554,9 +9851,9 @@ function handleKey2(state, key) {
|
|
|
9554
9851
|
return scroll2 !== null ? scrollChat(state, scroll2) : step(state);
|
|
9555
9852
|
}
|
|
9556
9853
|
}
|
|
9557
|
-
if (key.name === "enter") return
|
|
9854
|
+
if (key.name === "enter") return submit2(state);
|
|
9558
9855
|
if (key.name === "ctrl-s") {
|
|
9559
|
-
return (plannerInFlight(state) || state.turnGate.open !== null) && !builtInCommand(state.editor.text) ? forceSendPlanner(state) :
|
|
9856
|
+
return (plannerInFlight(state) || state.turnGate.open !== null) && !builtInCommand(state.editor.text) ? forceSendPlanner(state) : submit2(state);
|
|
9560
9857
|
}
|
|
9561
9858
|
if (key.name === "escape") return step({ ...state, editor: { ...state.editor, text: "", cursor: 0 } });
|
|
9562
9859
|
const scroll = scrollDelta(key, state, "chat");
|
|
@@ -9570,7 +9867,7 @@ function builtInCommand(text) {
|
|
|
9570
9867
|
const known = command && findCommand(command.name);
|
|
9571
9868
|
return command && known && known.source !== "skill" ? command : null;
|
|
9572
9869
|
}
|
|
9573
|
-
function
|
|
9870
|
+
function submit2(state) {
|
|
9574
9871
|
const text = state.editor.text.trim();
|
|
9575
9872
|
if (!text) return step(state);
|
|
9576
9873
|
const cleared = { ...state, editor: commit(state.editor) };
|
|
@@ -9579,7 +9876,7 @@ function submit(state) {
|
|
|
9579
9876
|
const taskId = state.taskView.taskId;
|
|
9580
9877
|
const task = findTask(state.tasks, taskId);
|
|
9581
9878
|
const command = parseRunnerCommand(text);
|
|
9582
|
-
const offered2 = (0,
|
|
9879
|
+
const offered2 = (0, import_core55.taskLogCommands)(state.taskView.view);
|
|
9583
9880
|
if (command && offered2.length > 0 && !runnerCommandFor(offered2, command.name)) {
|
|
9584
9881
|
const notice = `/${command.name} is not a command ${task?.assignedRunner ?? "the runner"} offers this task: type / to see them.`;
|
|
9585
9882
|
return step({ ...cleared, taskView: { ...state.taskView, notice } });
|
|
@@ -9598,7 +9895,7 @@ function submit(state) {
|
|
|
9598
9895
|
const builtIn = builtInCommand(text);
|
|
9599
9896
|
if (builtIn) return runCommand(cleared, builtIn);
|
|
9600
9897
|
if (plannerInFlight(state)) {
|
|
9601
|
-
return step({ ...cleared, queuedPrompts: (0,
|
|
9898
|
+
return step({ ...cleared, queuedPrompts: (0, import_core55.holdPrompt)(state.queuedPrompts, text) });
|
|
9602
9899
|
}
|
|
9603
9900
|
const spoken = say(cleared, "user", text);
|
|
9604
9901
|
const effect = state.sessionId ? { type: "sendMessage", sessionId: state.sessionId, message: text } : { type: "startConversation", goal: text };
|
|
@@ -9618,15 +9915,9 @@ var FRAME_INTERVAL_MS = 33;
|
|
|
9618
9915
|
function createApp(deps) {
|
|
9619
9916
|
let state = initialState(deps.initial);
|
|
9620
9917
|
let exited = false;
|
|
9621
|
-
let
|
|
9918
|
+
let shownTime = "";
|
|
9622
9919
|
const spinnerInterval = setInterval(() => {
|
|
9623
|
-
if (
|
|
9624
|
-
dispatch({ type: "spinnerTick" });
|
|
9625
|
-
return;
|
|
9626
|
-
}
|
|
9627
|
-
if (style.enabled && chatActivity(state)?.busy && ++dotTicks % DOT_TICKS === 0) {
|
|
9628
|
-
dispatch({ type: "spinnerTick", by: DOT_TICKS });
|
|
9629
|
-
}
|
|
9920
|
+
if (animating(state) || activityTime(state, Date.now()) !== shownTime) dispatch({ type: "spinnerTick" });
|
|
9630
9921
|
}, 200);
|
|
9631
9922
|
let renderPending = false;
|
|
9632
9923
|
let renderTimer = null;
|
|
@@ -9640,7 +9931,8 @@ function createApp(deps) {
|
|
|
9640
9931
|
}
|
|
9641
9932
|
renderPending = false;
|
|
9642
9933
|
lastPaint = Date.now();
|
|
9643
|
-
|
|
9934
|
+
shownTime = activityTime(state, lastPaint);
|
|
9935
|
+
deps.draw(render({ ...state, now: lastPaint }));
|
|
9644
9936
|
}
|
|
9645
9937
|
function paintKey() {
|
|
9646
9938
|
if (keyBurst) {
|
|
@@ -9712,7 +10004,7 @@ function createApp(deps) {
|
|
|
9712
10004
|
|
|
9713
10005
|
// src/tui/effects.ts
|
|
9714
10006
|
var import_child_process3 = require("child_process");
|
|
9715
|
-
var
|
|
10007
|
+
var import_core56 = require("@ordewell/core");
|
|
9716
10008
|
|
|
9717
10009
|
// src/tui/inbound.ts
|
|
9718
10010
|
var CONVERSATION_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -9732,6 +10024,7 @@ var CONVERSATION_TYPES = /* @__PURE__ */ new Set([
|
|
|
9732
10024
|
"planner_usage",
|
|
9733
10025
|
"planner_liveness",
|
|
9734
10026
|
"planner_compaction",
|
|
10027
|
+
"planner_detection",
|
|
9735
10028
|
"approval_request",
|
|
9736
10029
|
"approval_settled",
|
|
9737
10030
|
"approval_decided",
|
|
@@ -10029,6 +10322,7 @@ function dispatchLifecycle(dispatch, event, sessionId) {
|
|
|
10029
10322
|
...event.runMerge ? { runMerge: event.runMerge } : {},
|
|
10030
10323
|
...event.usage ? { usage: event.usage } : {},
|
|
10031
10324
|
...event.activity ? { activity: event.activity } : {},
|
|
10325
|
+
...event.escalations ? { escalations: event.escalations } : {},
|
|
10032
10326
|
sessionId
|
|
10033
10327
|
});
|
|
10034
10328
|
return;
|
|
@@ -10234,14 +10528,35 @@ async function perform(effect, deps) {
|
|
|
10234
10528
|
}
|
|
10235
10529
|
case "taskAction": {
|
|
10236
10530
|
const { sessionId, taskId, action } = effect;
|
|
10237
|
-
const command = (0,
|
|
10238
|
-
const request = () => api.execute(sessionId, effect.confirmed ? (0,
|
|
10531
|
+
const command = (0, import_core56.taskActionCommand)(action, taskId);
|
|
10532
|
+
const request = () => api.execute(sessionId, effect.confirmed ? (0, import_core56.confirmedOverride)(command, effect.confirmed.seen) : command);
|
|
10239
10533
|
await (effect.watch ? withExecutionStream(deps, sessionId, request) : request());
|
|
10240
10534
|
await refreshPlan(deps, sessionId);
|
|
10241
10535
|
return;
|
|
10242
10536
|
}
|
|
10537
|
+
case "previewRevert":
|
|
10538
|
+
dispatch({ type: "revertPreviewed", sessionId: effect.sessionId, taskId: effect.taskId, preview: await api.revertPreview(effect.sessionId, effect.taskId) });
|
|
10539
|
+
return;
|
|
10540
|
+
// A refusal at this point (a plan that changed, a conflict) comes back as the failure the seam worded.
|
|
10541
|
+
case "revertTask": {
|
|
10542
|
+
const { message } = await api.execute(effect.sessionId, { command: "revert_task", task: effect.taskId, confirmed: true, seen: effect.seen });
|
|
10543
|
+
await refreshPlan(deps, effect.sessionId);
|
|
10544
|
+
dispatch({ type: "notice", message });
|
|
10545
|
+
return;
|
|
10546
|
+
}
|
|
10547
|
+
case "answerEscalation": {
|
|
10548
|
+
const { sessionId, id, option, note, confirmed } = effect;
|
|
10549
|
+
let said = "";
|
|
10550
|
+
const request = async () => {
|
|
10551
|
+
said = (await api.answerEscalation(sessionId, { id, option, ...note ? { note } : {}, ...confirmed ? { confirmed } : {} })).message;
|
|
10552
|
+
};
|
|
10553
|
+
await (effect.watch ? withExecutionStream(deps, sessionId, request) : request());
|
|
10554
|
+
await refreshPlan(deps, sessionId);
|
|
10555
|
+
if (said) dispatch({ type: "notice", message: said });
|
|
10556
|
+
return;
|
|
10557
|
+
}
|
|
10243
10558
|
case "addTask": {
|
|
10244
|
-
const fields = (0,
|
|
10559
|
+
const fields = (0, import_core56.newTaskFields)(effect.title);
|
|
10245
10560
|
if (!fields) return;
|
|
10246
10561
|
await api.addTask(effect.sessionId, { ...fields });
|
|
10247
10562
|
await refreshPlan(deps, effect.sessionId);
|
|
@@ -10331,11 +10646,11 @@ async function perform(effect, deps) {
|
|
|
10331
10646
|
await api.execute(effect.sessionId, { command: "interrupt_task", task: effect.taskId });
|
|
10332
10647
|
return;
|
|
10333
10648
|
case "setPlanner": {
|
|
10334
|
-
const meta = (0,
|
|
10649
|
+
const meta = (0, import_core56.getProviderMeta)(effect.provider);
|
|
10335
10650
|
if (!meta) throw new Error(`Unknown planner: ${effect.provider}`);
|
|
10336
|
-
if ((0,
|
|
10651
|
+
if ((0, import_core56.isCliProvider)(effect.provider)) {
|
|
10337
10652
|
const installed = ((await api.getRunners()).runners ?? []).map((r) => r.id);
|
|
10338
|
-
const refusal = uninstalledPlannerRefusal(effect.provider, installed,
|
|
10653
|
+
const refusal = uninstalledPlannerRefusal(effect.provider, installed, import_core56.TERMINAL_UI_COMMANDS.pickPlanner);
|
|
10339
10654
|
if (refusal) throw new Error(refusal);
|
|
10340
10655
|
}
|
|
10341
10656
|
const env = { AI_PROVIDER: effect.provider };
|
|
@@ -10345,7 +10660,7 @@ async function perform(effect, deps) {
|
|
|
10345
10660
|
dispatchSettings(deps, { aiProvider: effect.provider, orchestratorModel: recall2.model, plannerThinkingEffort: recall2.effort });
|
|
10346
10661
|
dispatch({
|
|
10347
10662
|
type: "notice",
|
|
10348
|
-
message: `${(0,
|
|
10663
|
+
message: `${(0, import_core56.isCliProvider)(effect.provider) ? `Planning with ${meta.label}: no API key needed.` : `Planner set to ${meta.label}.`} ${plannerModelNotice(recall2)}`
|
|
10349
10664
|
});
|
|
10350
10665
|
await loadModels(deps);
|
|
10351
10666
|
return;
|
|
@@ -10364,7 +10679,7 @@ async function perform(effect, deps) {
|
|
|
10364
10679
|
return;
|
|
10365
10680
|
}
|
|
10366
10681
|
case "setApiKey": {
|
|
10367
|
-
const meta = (0,
|
|
10682
|
+
const meta = (0, import_core56.isApiProvider)(effect.provider) ? (0, import_core56.getProviderMeta)(effect.provider) : void 0;
|
|
10368
10683
|
if (!meta) throw new Error(`Unknown provider: ${effect.provider}`);
|
|
10369
10684
|
await api.updateSettings({ env: { [meta.apiKeyEnvVar]: effect.key } });
|
|
10370
10685
|
deps.setEnvVar(meta.apiKeyEnvVar, effect.key);
|
|
@@ -10552,7 +10867,7 @@ async function perform(effect, deps) {
|
|
|
10552
10867
|
// cleared the run up and says so on the stream (`run_closed`); one
|
|
10553
10868
|
// mid-run leaves the run going (ADR-0020).
|
|
10554
10869
|
case "isolationMerge": {
|
|
10555
|
-
const { ok, message } = (0,
|
|
10870
|
+
const { ok, message } = (0, import_core56.mergeOutcome)(await api.mergeRun(effect.sessionId), effect.branch, effect.group === true, effect.repaired ?? []);
|
|
10556
10871
|
dispatch({ type: ok ? "notice" : "failed", message });
|
|
10557
10872
|
return;
|
|
10558
10873
|
}
|
|
@@ -10573,11 +10888,15 @@ async function perform(effect, deps) {
|
|
|
10573
10888
|
await refreshPlan(deps, sessionId);
|
|
10574
10889
|
return;
|
|
10575
10890
|
}
|
|
10576
|
-
|
|
10577
|
-
|
|
10578
|
-
|
|
10579
|
-
|
|
10891
|
+
// A refusal, a second resolver among them, comes back in the daemon's words.
|
|
10892
|
+
case "resolveConflict": {
|
|
10893
|
+
const { sessionId, taskId } = effect;
|
|
10894
|
+
const request = () => api.resolveConflictAsTask(sessionId, taskId);
|
|
10895
|
+
await (effect.watch ? withExecutionStream(deps, sessionId, request) : request());
|
|
10896
|
+
await refreshPlan(deps, sessionId);
|
|
10897
|
+
dispatch({ type: "notice", message: "Added a task that merges the conflicted branch by hand. It starts once a slot is free; when it lands, the conflicted task lands through it." });
|
|
10580
10898
|
return;
|
|
10899
|
+
}
|
|
10581
10900
|
case "deleteSession": {
|
|
10582
10901
|
const { ok } = await api.deleteSession(effect.sessionId, workspace);
|
|
10583
10902
|
await loadSessions(deps);
|
|
@@ -10630,7 +10949,7 @@ async function saveMaxParallel(deps, limit) {
|
|
|
10630
10949
|
function copySelection(deps, text) {
|
|
10631
10950
|
if (!text) return;
|
|
10632
10951
|
const lines = text.split("\n").length;
|
|
10633
|
-
const command = (0,
|
|
10952
|
+
const command = (0, import_core56.clipboardCopyCommand)(deps.hasBin);
|
|
10634
10953
|
if (command) {
|
|
10635
10954
|
try {
|
|
10636
10955
|
(deps.pipeToClipboard ?? pipeToClipboardDefault)(command, text);
|
|
@@ -10857,7 +11176,7 @@ function turnReplies(plan) {
|
|
|
10857
11176
|
const replies = [];
|
|
10858
11177
|
for (let i = history.length - 1; i >= 0; i--) {
|
|
10859
11178
|
const entry = history[i];
|
|
10860
|
-
if ((0,
|
|
11179
|
+
if ((0, import_core56.isUserMessage)(entry)) break;
|
|
10861
11180
|
if (entry.role === "assistant" && entry.kind === void 0) {
|
|
10862
11181
|
const { content, timestamp, runs } = entry;
|
|
10863
11182
|
replies.unshift({ content, ...timestamp ? { timestamp } : {}, ...runs ? { runs } : {} });
|
|
@@ -11293,9 +11612,9 @@ function openTerminal(options) {
|
|
|
11293
11612
|
}
|
|
11294
11613
|
|
|
11295
11614
|
// src/versionCheck.ts
|
|
11296
|
-
var
|
|
11615
|
+
var import_core57 = require("@ordewell/core");
|
|
11297
11616
|
function startCliVersionCheck(show3, options = {}) {
|
|
11298
|
-
return (0,
|
|
11617
|
+
return (0, import_core57.startVersionCheck)({ version: cliVersion(), ...options, ...show3 });
|
|
11299
11618
|
}
|
|
11300
11619
|
var writeLine = (line) => console.error(line);
|
|
11301
11620
|
var toStderr = { onNotice: writeLine, onUpdate: writeLine };
|
|
@@ -11303,7 +11622,7 @@ var toStderr = { onNotice: writeLine, onUpdate: writeLine };
|
|
|
11303
11622
|
// src/tui/index.ts
|
|
11304
11623
|
var DAEMON_STOP_GRACE_MS = 3e3;
|
|
11305
11624
|
async function runLogin(start, command, cwd, env) {
|
|
11306
|
-
const plan = await (0,
|
|
11625
|
+
const plan = await (0, import_core58.planDirectLaunch)(command[0], command.slice(1));
|
|
11307
11626
|
return new Promise((resolve6, reject) => {
|
|
11308
11627
|
const child = start(plan.file, plan.args, {
|
|
11309
11628
|
stdio: "inherit",
|
|
@@ -11322,7 +11641,7 @@ async function handleTui(subArgs, options = {}) {
|
|
|
11322
11641
|
}
|
|
11323
11642
|
const workspace = (0, import_path7.resolve)(flag(subArgs, "--workspace") || process.cwd());
|
|
11324
11643
|
try {
|
|
11325
|
-
(0,
|
|
11644
|
+
(0, import_core58.assertWorkspaceExists)(workspace);
|
|
11326
11645
|
} catch {
|
|
11327
11646
|
console.error(
|
|
11328
11647
|
`Workspace "${workspace}" does not exist.
|
|
@@ -11331,7 +11650,7 @@ cd into a directory that exists, or pass --workspace <path> naming one that does
|
|
|
11331
11650
|
);
|
|
11332
11651
|
process.exit(1);
|
|
11333
11652
|
}
|
|
11334
|
-
const discoveredSkills = (0,
|
|
11653
|
+
const discoveredSkills = (0, import_core58.createSkillsService)(workspace, new import_core58.EnvConfig().workspaceRepos).listSkills();
|
|
11335
11654
|
registerSkillCommands(discoveredSkills);
|
|
11336
11655
|
const taskSkills = discoveredSkills.filter((s) => s.appliesTo === "task").map((s) => ({ name: s.name, description: s.description }));
|
|
11337
11656
|
const explicitPort = flag(subArgs, "--port") || process.env.ORDEWELL_PORT;
|
|
@@ -11350,7 +11669,7 @@ cd into a directory that exists, or pass --workspace <path> naming one that does
|
|
|
11350
11669
|
},
|
|
11351
11670
|
onUpdate: (line) => app.dispatch({ type: "notice", message: line })
|
|
11352
11671
|
});
|
|
11353
|
-
const settings = new
|
|
11672
|
+
const settings = new import_core58.SettingsService();
|
|
11354
11673
|
const app = createApp({
|
|
11355
11674
|
// The level the daemon plans with: the saved one, unless the environment overrides it (ADR-0030).
|
|
11356
11675
|
// The run settings are seeded from the same file because the first frame is
|
|
@@ -11373,13 +11692,13 @@ cd into a directory that exists, or pass --workspace <path> naming one that does
|
|
|
11373
11692
|
workspace,
|
|
11374
11693
|
port,
|
|
11375
11694
|
dispatch: (action) => app.dispatch(action),
|
|
11376
|
-
newSessionId: () => (0,
|
|
11695
|
+
newSessionId: () => (0, import_core58.mintSessionId)(),
|
|
11377
11696
|
setEnvVar: (key, value) => {
|
|
11378
11697
|
writeEnvVar(findEnvFile(), key, value);
|
|
11379
11698
|
process.env[key] = value;
|
|
11380
11699
|
},
|
|
11381
11700
|
saveAutonomy: (enabled) => {
|
|
11382
|
-
const settings2 = new
|
|
11701
|
+
const settings2 = new import_core58.SettingsService();
|
|
11383
11702
|
settings2.setAutonomousMode(enabled);
|
|
11384
11703
|
return { overridden: settings2.autonomousModeOverridden() };
|
|
11385
11704
|
},
|
|
@@ -11394,7 +11713,7 @@ cd into a directory that exists, or pass --workspace <path> naming one that does
|
|
|
11394
11713
|
login: async (command) => {
|
|
11395
11714
|
let handedOver = false;
|
|
11396
11715
|
try {
|
|
11397
|
-
const env = (0,
|
|
11716
|
+
const env = (0, import_core58.withPath)(process.env, await (0, import_core58.augmentedPath)(), await (0, import_core58.workspaceEnvOf)(workspace));
|
|
11398
11717
|
loginRunning = true;
|
|
11399
11718
|
handedOver = true;
|
|
11400
11719
|
terminal?.suspend();
|
|
@@ -11505,12 +11824,15 @@ var COMMANDS = {
|
|
|
11505
11824
|
automerge: handleAutomerge,
|
|
11506
11825
|
trailer: handleTrailer,
|
|
11507
11826
|
autorun: handleAutorun,
|
|
11827
|
+
// Not offered until a delegated task exists (ADR-0055 D21).
|
|
11828
|
+
...import_core59.DELEGATION_OFFERED ? { delegation: handleDelegation } : {},
|
|
11508
11829
|
refresh: handleRefresh,
|
|
11509
11830
|
setup: handleSetup,
|
|
11510
11831
|
skills: handleSkills,
|
|
11511
11832
|
"mark-complete": handleMarkComplete,
|
|
11512
11833
|
complete: handleMarkComplete,
|
|
11513
11834
|
uncomplete: handleUncomplete,
|
|
11835
|
+
revert: handleRevert,
|
|
11514
11836
|
skip: handleSkip,
|
|
11515
11837
|
"run-task": handleRunTask,
|
|
11516
11838
|
"force-start": handleForceStart,
|
|
@@ -11539,7 +11861,7 @@ var COMMANDS = {
|
|
|
11539
11861
|
// src/main.ts
|
|
11540
11862
|
var TUI_FLAGS = /* @__PURE__ */ new Set(["--workspace", "--port"]);
|
|
11541
11863
|
async function main() {
|
|
11542
|
-
(0,
|
|
11864
|
+
(0, import_core60.migrateOldConfigDir)();
|
|
11543
11865
|
moveAutonomyToSettings();
|
|
11544
11866
|
loadEnvFile();
|
|
11545
11867
|
const argv = process.argv.slice(2);
|