@ordewell/cli 0.10.5 → 0.10.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +198 -229
- package/package.json +3 -3
package/dist/main.js
CHANGED
|
@@ -631,18 +631,18 @@ var ApiClient = class _ApiClient {
|
|
|
631
631
|
return this.call("POST", `/api/plans/${sessionId}/review/approve`, "Approve failed");
|
|
632
632
|
}
|
|
633
633
|
markTaskComplete(sessionId, taskId) {
|
|
634
|
-
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/complete`, "Mark complete failed");
|
|
634
|
+
return this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/complete`, "Mark complete failed");
|
|
635
635
|
}
|
|
636
636
|
/** Skip: end the task without landing its work, so what depends on it goes ahead once the work it waited on is done; never a mark complete. */
|
|
637
637
|
skipTask(sessionId, taskId, reason) {
|
|
638
|
-
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/skip`, "Skip failed", reason ? { reason } : {});
|
|
638
|
+
return this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/skip`, "Skip failed", reason ? { reason } : {});
|
|
639
639
|
}
|
|
640
640
|
markTaskIncomplete(sessionId, taskId) {
|
|
641
|
-
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/uncomplete`, "Mark not done failed");
|
|
641
|
+
return this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/uncomplete`, "Mark not done failed");
|
|
642
642
|
}
|
|
643
|
-
/** Keep a task paused on a usage limit from resuming on its own;
|
|
643
|
+
/** Keep a task paused on a usage limit from resuming on its own; a task with none scheduled is refused with the reason. */
|
|
644
644
|
cancelResume(sessionId, taskId) {
|
|
645
|
-
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/resume/cancel`, "Cancel resume failed");
|
|
645
|
+
return this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/resume/cancel`, "Cancel resume failed");
|
|
646
646
|
}
|
|
647
647
|
/** Keep what waits on the planner's usage limit from being sent at the reset; `cancelled` is false when nothing waited. */
|
|
648
648
|
cancelPlannerResend(sessionId) {
|
|
@@ -650,55 +650,55 @@ var ApiClient = class _ApiClient {
|
|
|
650
650
|
}
|
|
651
651
|
/** Resume a task paused on a usage limit now, in its saved session, once the user says the limit has reset. */
|
|
652
652
|
resumeNow(sessionId, taskId) {
|
|
653
|
-
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/resume/now`, "Resume failed");
|
|
653
|
+
return this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/resume/now`, "Resume failed");
|
|
654
654
|
}
|
|
655
655
|
/** run | force-start | retry | cancel — real orchestrator work, not a status patch. */
|
|
656
656
|
taskControl(sessionId, taskId, action) {
|
|
657
|
-
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/${action}`, `${action} failed`);
|
|
657
|
+
return this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/${action}`, `${action} failed`);
|
|
658
658
|
}
|
|
659
659
|
/** A user message to a structured task: delivered now if it waits for input, else queued behind its turn. */
|
|
660
660
|
sendTaskMessage(sessionId, taskId, text) {
|
|
661
|
-
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/messages`, "Send message failed", { text });
|
|
661
|
+
return this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/messages`, "Send message failed", { text });
|
|
662
662
|
}
|
|
663
663
|
/** Force send: interrupt the task's running turn and deliver `text` next, ahead of anything queued (ADR-0023, F1). */
|
|
664
664
|
forceSendTaskMessage(sessionId, taskId, text) {
|
|
665
|
-
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/messages/now`, "Send now failed", { text });
|
|
665
|
+
return this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/messages/now`, "Send now failed", { text });
|
|
666
666
|
}
|
|
667
667
|
/** Run the task runner's own command `/name args` as a turn of its own (#66). */
|
|
668
668
|
invokeTaskCommand(sessionId, taskId, name, args) {
|
|
669
|
-
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/commands`, "Command failed", { name, args });
|
|
669
|
+
return this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/commands`, "Command failed", { name, args });
|
|
670
670
|
}
|
|
671
|
-
/** Force send a message still queued;
|
|
671
|
+
/** Force send a message still queued; one the runner already has is refused with the reason. */
|
|
672
672
|
forceSendQueuedTaskMessage(sessionId, taskId, messageId) {
|
|
673
|
-
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/messages/${encodeURIComponent(messageId)}/now`, "Send now failed");
|
|
673
|
+
return this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/messages/${encodeURIComponent(messageId)}/now`, "Send now failed");
|
|
674
674
|
}
|
|
675
675
|
/** Continue a finished structured task in its saved session, with `text` as its next turn. */
|
|
676
676
|
continueTask(sessionId, taskId, text) {
|
|
677
|
-
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/continue`, "Continue failed", { text });
|
|
677
|
+
return this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/continue`, "Continue failed", { text });
|
|
678
678
|
}
|
|
679
|
-
/**
|
|
679
|
+
/** Take back a queued message; one already delivered is refused with the reason. */
|
|
680
680
|
removeQueuedTaskMessage(sessionId, taskId, messageId) {
|
|
681
|
-
return this.call("DELETE", `/api/plans/${sessionId}/tasks/${taskId}/messages/${encodeURIComponent(messageId)}`, "Remove message failed");
|
|
681
|
+
return this.call("DELETE", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/messages/${encodeURIComponent(messageId)}`, "Remove message failed");
|
|
682
682
|
}
|
|
683
683
|
interruptTask(sessionId, taskId) {
|
|
684
|
-
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/interrupt`, "Interrupt failed");
|
|
684
|
+
return this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/interrupt`, "Interrupt failed");
|
|
685
685
|
}
|
|
686
686
|
/** Let a task go on from the checkpoint it waits at; refused, with the reason, when none waits. */
|
|
687
687
|
approveTaskCheckpoint(sessionId, taskId) {
|
|
688
|
-
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/checkpoint/approve`, "Approve checkpoint failed");
|
|
688
|
+
return this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/checkpoint/approve`, "Approve checkpoint failed");
|
|
689
689
|
}
|
|
690
690
|
/** Turn a task back at its checkpoint; `reason` is what the agent is told. */
|
|
691
691
|
rejectTaskCheckpoint(sessionId, taskId, reason) {
|
|
692
|
-
return this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/checkpoint/reject`, "Reject checkpoint failed", reason ? { reason } : {});
|
|
692
|
+
return this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/checkpoint/reject`, "Reject checkpoint failed", reason ? { reason } : {});
|
|
693
693
|
}
|
|
694
694
|
addTask(sessionId, task) {
|
|
695
695
|
return this.call("POST", `/api/plans/${sessionId}/tasks`, "Add task failed", task);
|
|
696
696
|
}
|
|
697
697
|
updateTask(sessionId, taskId, changes) {
|
|
698
|
-
return this.call("PUT", `/api/plans/${sessionId}/tasks/${taskId}`, "Update task failed", changes);
|
|
698
|
+
return this.call("PUT", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}`, "Update task failed", changes);
|
|
699
699
|
}
|
|
700
700
|
removeTask(sessionId, taskId) {
|
|
701
|
-
return this.call("DELETE", `/api/plans/${sessionId}/tasks/${taskId}`, "Remove task failed");
|
|
701
|
+
return this.call("DELETE", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}`, "Remove task failed");
|
|
702
702
|
}
|
|
703
703
|
/**
|
|
704
704
|
* Make a saved session live on the server. `getSession` only reads the file;
|
|
@@ -708,7 +708,7 @@ var ApiClient = class _ApiClient {
|
|
|
708
708
|
async adoptSession(sessionId, workspace) {
|
|
709
709
|
const qs = workspace ? `?workspace=${encodeURIComponent(workspace)}` : "";
|
|
710
710
|
const res = await this.call("POST", `/api/sessions/${sessionId}/load${qs}`, "Failed to load session");
|
|
711
|
-
return { plan: res.plan, goal: res.goal ?? "" };
|
|
711
|
+
return { plan: res.plan, goal: res.goal ?? "", ...res.run ? { run: res.run } : {} };
|
|
712
712
|
}
|
|
713
713
|
deleteSession(sessionId, workspace) {
|
|
714
714
|
const qs = workspace ? `?workspace=${encodeURIComponent(workspace)}` : "";
|
|
@@ -739,7 +739,7 @@ var ApiClient = class _ApiClient {
|
|
|
739
739
|
}
|
|
740
740
|
/** The ancestors a task waits on at its merge gate (ADR-0020); empty when nothing gates it. */
|
|
741
741
|
async getMergeGate(sessionId, taskId) {
|
|
742
|
-
const res = await this.call("GET", `/api/plans/${sessionId}/tasks/${taskId}/merge-gate`, "Could not read the merge gate");
|
|
742
|
+
const res = await this.call("GET", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/merge-gate`, "Could not read the merge gate");
|
|
743
743
|
return res.mergeGate ?? [];
|
|
744
744
|
}
|
|
745
745
|
async reviewRunDiff(sessionId) {
|
|
@@ -769,7 +769,7 @@ var ApiClient = class _ApiClient {
|
|
|
769
769
|
await this.call("POST", `/api/plans/${sessionId}/isolation/${segment}`, failure);
|
|
770
770
|
}
|
|
771
771
|
async resolveConflictAsTask(sessionId, taskId) {
|
|
772
|
-
const res = await this.call("POST", `/api/plans/${sessionId}/tasks/${taskId}/resolve-conflict`, "Could not add a resolver task");
|
|
772
|
+
const res = await this.call("POST", `/api/plans/${sessionId}/tasks/${encodeURIComponent(taskId)}/resolve-conflict`, "Could not add a resolver task");
|
|
773
773
|
return res.plan;
|
|
774
774
|
}
|
|
775
775
|
async closeSession(sessionId) {
|
|
@@ -798,13 +798,13 @@ var ApiClient = class _ApiClient {
|
|
|
798
798
|
async getTaskLogAttempts(sessionId, taskId, workspace) {
|
|
799
799
|
const qs = workspace ? `?workspace=${encodeURIComponent(workspace)}` : "";
|
|
800
800
|
const res = await this.call("GET", `/api/sessions/${sessionId}/tasks/${encodeURIComponent(taskId)}/log${qs}`, "Could not read the task log");
|
|
801
|
-
return res
|
|
801
|
+
return res;
|
|
802
802
|
}
|
|
803
803
|
/** One attempt's saved log, for `replayTaskLog`. */
|
|
804
804
|
async getTaskLog(sessionId, taskId, attempt, workspace) {
|
|
805
805
|
const qs = workspace ? `?workspace=${encodeURIComponent(workspace)}` : "";
|
|
806
806
|
const res = await this.call("GET", `/api/sessions/${sessionId}/tasks/${encodeURIComponent(taskId)}/log/${attempt}${qs}`, "Could not read the task log");
|
|
807
|
-
return res
|
|
807
|
+
return res;
|
|
808
808
|
}
|
|
809
809
|
/**
|
|
810
810
|
* The one place a session socket is built, so the token travels on both
|
|
@@ -1045,12 +1045,7 @@ function allTasksOf(plan) {
|
|
|
1045
1045
|
return (0, import_core5.rejoinPlanTree)(tasks, executionLog);
|
|
1046
1046
|
}
|
|
1047
1047
|
function resolveTaskId(plan, identifier) {
|
|
1048
|
-
|
|
1049
|
-
const byRef = (0, import_core5.resolveTaskRef)(allTasks, identifier);
|
|
1050
|
-
if (byRef) return byRef.id;
|
|
1051
|
-
const prefixMatches = allTasks.filter((t) => typeof t.id === "string" && t.id.startsWith(identifier));
|
|
1052
|
-
if (prefixMatches.length === 1) return prefixMatches[0].id;
|
|
1053
|
-
return void 0;
|
|
1048
|
+
return (0, import_core5.resolveUserTaskRef)(allTasksOf(plan), identifier)?.id;
|
|
1054
1049
|
}
|
|
1055
1050
|
|
|
1056
1051
|
// src/utils/session.ts
|
|
@@ -1863,7 +1858,8 @@ async function handleRun(subArgs, injectedApi) {
|
|
|
1863
1858
|
const onBlocked = hasFlag(subArgs, "--stash") ? "stash" : hasFlag(subArgs, "--without-isolation") ? "shared" : void 0;
|
|
1864
1859
|
await followExecution(api, sessionId, async () => {
|
|
1865
1860
|
try {
|
|
1866
|
-
await api.executePlan(sessionId);
|
|
1861
|
+
const started = await api.executePlan(sessionId);
|
|
1862
|
+
if (started.message) console.error(started.message);
|
|
1867
1863
|
} catch (err) {
|
|
1868
1864
|
console.error(`Failed to start execution: ${err.message}`);
|
|
1869
1865
|
process.exit(1);
|
|
@@ -2808,7 +2804,7 @@ function servedApprovalRecord(plan) {
|
|
|
2808
2804
|
}
|
|
2809
2805
|
|
|
2810
2806
|
// src/commands/stop.ts
|
|
2811
|
-
async function handleStop(subArgs) {
|
|
2807
|
+
async function handleStop(subArgs, injectedApi) {
|
|
2812
2808
|
if (subArgs.includes("--server") || subArgs[0] === "server") {
|
|
2813
2809
|
const stopped = await stopDaemon(resolvePort(subArgs));
|
|
2814
2810
|
if (!stopped) process.exit(1);
|
|
@@ -2825,10 +2821,9 @@ async function handleStop(subArgs) {
|
|
|
2825
2821
|
sessionId = last.sessionId;
|
|
2826
2822
|
console.error(`Stopping last session: ${sessionId}`);
|
|
2827
2823
|
}
|
|
2828
|
-
const
|
|
2829
|
-
const
|
|
2830
|
-
|
|
2831
|
-
console.log("Execution stopped.");
|
|
2824
|
+
const api = await connect(subArgs, injectedApi);
|
|
2825
|
+
const { message } = await api.stopExecution(sessionId);
|
|
2826
|
+
console.log(message ?? "Execution stopped.");
|
|
2832
2827
|
}
|
|
2833
2828
|
|
|
2834
2829
|
// src/commands/web.ts
|
|
@@ -3243,7 +3238,7 @@ var PAST_TENSE = {
|
|
|
3243
3238
|
retry: "Task retried.",
|
|
3244
3239
|
cancel: "Task cancelled."
|
|
3245
3240
|
};
|
|
3246
|
-
async function
|
|
3241
|
+
async function sessionAndRef(subArgs, usage, injectedApi) {
|
|
3247
3242
|
const workspace = flag(subArgs, "--workspace") || process.cwd();
|
|
3248
3243
|
let sessionId = flag(subArgs, "--session-id");
|
|
3249
3244
|
if (!sessionId) {
|
|
@@ -3254,12 +3249,20 @@ async function withResolvedTask(subArgs, usage, injectedApi, run) {
|
|
|
3254
3249
|
}
|
|
3255
3250
|
sessionId = last.sessionId;
|
|
3256
3251
|
}
|
|
3257
|
-
const
|
|
3258
|
-
if (!
|
|
3252
|
+
const ref = positionals(subArgs)[0];
|
|
3253
|
+
if (!ref) {
|
|
3259
3254
|
console.error(usage);
|
|
3260
3255
|
process.exit(1);
|
|
3261
3256
|
}
|
|
3262
3257
|
const api = injectedApi || new ApiClient(await ensureDaemon(resolvePort(subArgs)));
|
|
3258
|
+
return { api, sessionId, ref, workspace };
|
|
3259
|
+
}
|
|
3260
|
+
async function withTaskRef(subArgs, usage, injectedApi, run) {
|
|
3261
|
+
const { api, sessionId, ref } = await sessionAndRef(subArgs, usage, injectedApi);
|
|
3262
|
+
await run(api, sessionId, ref);
|
|
3263
|
+
}
|
|
3264
|
+
async function withResolvedTask(subArgs, usage, injectedApi, run) {
|
|
3265
|
+
const { api, sessionId, ref, workspace } = await sessionAndRef(subArgs, usage, injectedApi);
|
|
3263
3266
|
let plan;
|
|
3264
3267
|
try {
|
|
3265
3268
|
plan = (await api.getSession(sessionId, workspace)).plan;
|
|
@@ -3267,35 +3270,37 @@ async function withResolvedTask(subArgs, usage, injectedApi, run) {
|
|
|
3267
3270
|
console.error(`Failed to load session: ${err.message}`);
|
|
3268
3271
|
process.exit(1);
|
|
3269
3272
|
}
|
|
3270
|
-
const taskId = resolveTaskId(plan,
|
|
3273
|
+
const taskId = resolveTaskId(plan, ref);
|
|
3271
3274
|
if (!taskId) {
|
|
3272
|
-
console.error(`Task not found: "${
|
|
3275
|
+
console.error(`Task not found: "${ref}"`);
|
|
3273
3276
|
process.exit(1);
|
|
3274
3277
|
}
|
|
3275
3278
|
await run(api, sessionId, taskId, plan);
|
|
3276
3279
|
}
|
|
3277
3280
|
var PASSES_GATE = /* @__PURE__ */ new Set(["run", "force-start"]);
|
|
3278
|
-
async function confirmPastGate(subArgs, api, sessionId,
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
if (
|
|
3281
|
+
async function confirmPastGate(subArgs, api, sessionId, ref, confirm2) {
|
|
3282
|
+
const unmerged = await api.getMergeGate(sessionId, ref).catch(() => []);
|
|
3283
|
+
if (unmerged.length === 0) return false;
|
|
3284
|
+
if (hasFlag(subArgs, "--yes")) return true;
|
|
3282
3285
|
const question = `${(0, import_core21.pastGateConfirmation)(unmerged.map((d) => (0, import_core21.titledTaskRef)(d)))} Start it anyway?`;
|
|
3283
3286
|
if (!await confirm2(question)) fail("Not started: nothing was changed. Pass --yes to start it without a prompt.");
|
|
3287
|
+
return true;
|
|
3284
3288
|
}
|
|
3285
3289
|
function makeHandler(action, command = action) {
|
|
3286
3290
|
const yes = PASSES_GATE.has(action) ? " [--yes]" : "";
|
|
3287
3291
|
return async function handle(subArgs, injectedApi, confirm2 = askYesNo) {
|
|
3288
|
-
await
|
|
3292
|
+
await withTaskRef(
|
|
3289
3293
|
subArgs,
|
|
3290
3294
|
`Usage: ordewell ${command} <task-id-or-order> [--session-id <id>] [--workspace /path]${yes}`,
|
|
3291
3295
|
injectedApi,
|
|
3292
|
-
async (api, sessionId,
|
|
3293
|
-
|
|
3296
|
+
async (api, sessionId, ref) => {
|
|
3297
|
+
const pastGate = PASSES_GATE.has(action) ? await confirmPastGate(subArgs, api, sessionId, ref, confirm2) : false;
|
|
3298
|
+
const control = pastGate && action === "run" ? "force-start" : action;
|
|
3294
3299
|
try {
|
|
3295
|
-
const { queued } = await api.taskControl(sessionId,
|
|
3296
|
-
console.log(queued ?? PAST_TENSE[
|
|
3300
|
+
const { message, queued } = await api.taskControl(sessionId, ref, control);
|
|
3301
|
+
console.log(message ?? queued ?? PAST_TENSE[control]);
|
|
3297
3302
|
} catch (err) {
|
|
3298
|
-
console.error(`Failed to ${
|
|
3303
|
+
console.error(`Failed to ${control} task: ${err.message}`);
|
|
3299
3304
|
process.exit(1);
|
|
3300
3305
|
}
|
|
3301
3306
|
}
|
|
@@ -3309,10 +3314,10 @@ async function handleContinue(subArgs, injectedApi) {
|
|
|
3309
3314
|
console.error(usage);
|
|
3310
3315
|
process.exit(1);
|
|
3311
3316
|
}
|
|
3312
|
-
await
|
|
3317
|
+
await withTaskRef(subArgs, usage, injectedApi, async (api, sessionId, ref) => {
|
|
3313
3318
|
try {
|
|
3314
|
-
await api.continueTask(sessionId,
|
|
3315
|
-
console.log("Task continued.");
|
|
3319
|
+
const sent = await api.continueTask(sessionId, ref, message);
|
|
3320
|
+
console.log(sent.message ?? "Task continued.");
|
|
3316
3321
|
} catch (err) {
|
|
3317
3322
|
console.error(`Failed to continue task: ${err.message}`);
|
|
3318
3323
|
process.exit(1);
|
|
@@ -3332,11 +3337,10 @@ async function handleCheckpoint(subArgs, injectedApi) {
|
|
|
3332
3337
|
${usage}`);
|
|
3333
3338
|
process.exit(1);
|
|
3334
3339
|
}
|
|
3335
|
-
await
|
|
3340
|
+
await withTaskRef(subArgs, usage, injectedApi, async (api, sessionId, ref) => {
|
|
3336
3341
|
try {
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
console.log(answer === "approve" ? "Checkpoint approved." : "Checkpoint rejected.");
|
|
3342
|
+
const sent = answer === "approve" ? await api.approveTaskCheckpoint(sessionId, ref) : await api.rejectTaskCheckpoint(sessionId, ref, reason || void 0);
|
|
3343
|
+
console.log(sent.message ?? (answer === "approve" ? "Checkpoint approved." : "Checkpoint rejected."));
|
|
3340
3344
|
} catch (err) {
|
|
3341
3345
|
console.error(`Failed to ${answer} checkpoint: ${err.message}`);
|
|
3342
3346
|
process.exit(1);
|
|
@@ -3350,14 +3354,14 @@ var handleCancel = makeHandler("cancel");
|
|
|
3350
3354
|
|
|
3351
3355
|
// src/commands/mark-complete.ts
|
|
3352
3356
|
async function handleMarkComplete(subArgs, api) {
|
|
3353
|
-
await
|
|
3357
|
+
await withTaskRef(
|
|
3354
3358
|
subArgs,
|
|
3355
3359
|
"Usage: ordewell mark-complete <task-id-or-order> [--session-id <id>]",
|
|
3356
3360
|
api,
|
|
3357
|
-
async (client, sessionId,
|
|
3361
|
+
async (client, sessionId, ref) => {
|
|
3358
3362
|
try {
|
|
3359
|
-
await client.markTaskComplete(sessionId,
|
|
3360
|
-
console.log("Task marked complete.");
|
|
3363
|
+
const done = await client.markTaskComplete(sessionId, ref);
|
|
3364
|
+
console.log(done.message ?? "Task marked complete.");
|
|
3361
3365
|
} catch (err) {
|
|
3362
3366
|
console.error(`Failed to mark task complete: ${err.message}`);
|
|
3363
3367
|
process.exit(1);
|
|
@@ -3366,14 +3370,14 @@ async function handleMarkComplete(subArgs, api) {
|
|
|
3366
3370
|
);
|
|
3367
3371
|
}
|
|
3368
3372
|
async function handleUncomplete(subArgs, api) {
|
|
3369
|
-
await
|
|
3373
|
+
await withTaskRef(
|
|
3370
3374
|
subArgs,
|
|
3371
3375
|
"Usage: ordewell uncomplete <task-id-or-order> [--session-id <id>]",
|
|
3372
3376
|
api,
|
|
3373
|
-
async (client, sessionId,
|
|
3377
|
+
async (client, sessionId, ref) => {
|
|
3374
3378
|
try {
|
|
3375
|
-
await client.markTaskIncomplete(sessionId,
|
|
3376
|
-
console.log("Task marked not done.");
|
|
3379
|
+
const done = await client.markTaskIncomplete(sessionId, ref);
|
|
3380
|
+
console.log(done.message ?? "Task marked not done.");
|
|
3377
3381
|
} catch (err) {
|
|
3378
3382
|
console.error(`Failed to mark task not done: ${err.message}`);
|
|
3379
3383
|
process.exit(1);
|
|
@@ -3382,14 +3386,14 @@ async function handleUncomplete(subArgs, api) {
|
|
|
3382
3386
|
);
|
|
3383
3387
|
}
|
|
3384
3388
|
async function handleSkip(subArgs, api) {
|
|
3385
|
-
await
|
|
3389
|
+
await withTaskRef(
|
|
3386
3390
|
subArgs,
|
|
3387
3391
|
"Usage: ordewell skip <task-id-or-order> [--reason <text>] [--session-id <id>]",
|
|
3388
3392
|
api,
|
|
3389
|
-
async (client, sessionId,
|
|
3393
|
+
async (client, sessionId, ref) => {
|
|
3390
3394
|
try {
|
|
3391
|
-
await client.skipTask(sessionId,
|
|
3392
|
-
console.log("Task skipped: none of its work lands, and the tasks that depend on it go ahead without it once the work it waited on is done.");
|
|
3395
|
+
const done = await client.skipTask(sessionId, ref, flag(subArgs, "--reason"));
|
|
3396
|
+
console.log(done.message ?? "Task skipped: none of its work lands, and the tasks that depend on it go ahead without it once the work it waited on is done.");
|
|
3393
3397
|
} catch (err) {
|
|
3394
3398
|
console.error(`Failed to skip task: ${err.message}`);
|
|
3395
3399
|
process.exit(1);
|
|
@@ -3674,9 +3678,6 @@ function handoffArrived(state, handoff) {
|
|
|
3674
3678
|
);
|
|
3675
3679
|
return state.overlay ? told : { ...told, overlay: { kind: "handoff", index: 0, diff: null } };
|
|
3676
3680
|
}
|
|
3677
|
-
function withDeferral(fresh, known) {
|
|
3678
|
-
return known?.deferredTo && handoffBranch(known) === handoffBranch(fresh) ? { ...fresh, deferredTo: known.deferredTo } : fresh;
|
|
3679
|
-
}
|
|
3680
3681
|
function runHandoffAction(state, id) {
|
|
3681
3682
|
const { sessionId } = state;
|
|
3682
3683
|
const handoff = currentHandoff(state);
|
|
@@ -3745,8 +3746,7 @@ function confirmedHandoff(state, kind) {
|
|
|
3745
3746
|
sessionId,
|
|
3746
3747
|
branch: handoffBranch(handoff),
|
|
3747
3748
|
...(0, import_core10.isRepoGroup)(handoff) ? { group: true } : {},
|
|
3748
|
-
...repaired.length > 0 ? { repaired } : {}
|
|
3749
|
-
...midRun(state) ? { midRun: true } : {}
|
|
3749
|
+
...repaired.length > 0 ? { repaired } : {}
|
|
3750
3750
|
} : { type: "isolationDiscard", sessionId, branch: handoffBranch(handoff) };
|
|
3751
3751
|
return { state: closed, effects: [effect] };
|
|
3752
3752
|
}
|
|
@@ -4147,21 +4147,23 @@ async function handleApprove(subArgs, injectedApi) {
|
|
|
4147
4147
|
const sessionId = resolveSessionId(subArgs);
|
|
4148
4148
|
const api = await connect(subArgs, injectedApi);
|
|
4149
4149
|
await followExecution(api, sessionId, async () => {
|
|
4150
|
+
let approved;
|
|
4150
4151
|
try {
|
|
4151
|
-
await api.approveReview(sessionId);
|
|
4152
|
+
approved = await api.approveReview(sessionId);
|
|
4152
4153
|
} catch (err) {
|
|
4153
4154
|
console.error(`Failed to approve: ${err.message}`);
|
|
4154
4155
|
process.exit(1);
|
|
4155
4156
|
}
|
|
4156
|
-
console.error("Plan approved: continuing execution.");
|
|
4157
|
+
console.error(approved.message ?? "Plan approved: continuing execution.");
|
|
4157
4158
|
});
|
|
4158
4159
|
}
|
|
4159
4160
|
|
|
4160
4161
|
// src/commands/task-assign.ts
|
|
4161
4162
|
var import_core29 = require("@ordewell/core");
|
|
4162
|
-
var
|
|
4163
|
+
var import_plan_utils3 = require("@ordewell/core/plan-utils");
|
|
4163
4164
|
|
|
4164
4165
|
// src/tui/taskAssignment.ts
|
|
4166
|
+
var import_plan_utils = require("@ordewell/core/plan-utils");
|
|
4165
4167
|
function runnerServes(runner, model) {
|
|
4166
4168
|
return !runner || !model.runners || model.runners.length === 0 || model.runners.includes(runner);
|
|
4167
4169
|
}
|
|
@@ -4184,7 +4186,7 @@ function effortsForTask(models, task) {
|
|
|
4184
4186
|
}
|
|
4185
4187
|
function assignedModelFor(model, currentEffort) {
|
|
4186
4188
|
const variants = model.variants ?? [];
|
|
4187
|
-
const thinkingEffort =
|
|
4189
|
+
const thinkingEffort = (0, import_plan_utils.clampThinkingEffort)(currentEffort, variants);
|
|
4188
4190
|
return {
|
|
4189
4191
|
modelId: model.id,
|
|
4190
4192
|
modelLabel: model.label,
|
|
@@ -4198,6 +4200,7 @@ function modesForTask(modesByRunner, task) {
|
|
|
4198
4200
|
}
|
|
4199
4201
|
|
|
4200
4202
|
// src/tui/state.ts
|
|
4203
|
+
var import_plan_utils2 = require("@ordewell/core/plan-utils");
|
|
4201
4204
|
var import_core28 = require("@ordewell/core");
|
|
4202
4205
|
|
|
4203
4206
|
// src/tui/editor.ts
|
|
@@ -4394,12 +4397,7 @@ function resumesTask(task) {
|
|
|
4394
4397
|
return task?.status === "awaiting_user" && task.awaitingReason === "usage-limit";
|
|
4395
4398
|
}
|
|
4396
4399
|
function findTask(tasks, id) {
|
|
4397
|
-
|
|
4398
|
-
if (task.id === id) return task;
|
|
4399
|
-
const found = findTask(task.subtasks ?? [], id);
|
|
4400
|
-
if (found) return found;
|
|
4401
|
-
}
|
|
4402
|
-
return void 0;
|
|
4400
|
+
return (0, import_plan_utils2.taskPlacement)(tasks, id)?.task;
|
|
4403
4401
|
}
|
|
4404
4402
|
function refFor(state, task) {
|
|
4405
4403
|
return (0, import_core28.taskRefOf)(state.tasks, task.id) ?? (0, import_core28.taskRef)(task);
|
|
@@ -4485,8 +4483,8 @@ function initialState(overrides = {}) {
|
|
|
4485
4483
|
}
|
|
4486
4484
|
|
|
4487
4485
|
// src/commands/task-assign.ts
|
|
4488
|
-
var refOf = (tasks, task) => (0,
|
|
4489
|
-
var namedOf = (tasks, task) => (0,
|
|
4486
|
+
var refOf = (tasks, task) => (0, import_plan_utils3.taskRefOf)(tasks, task.id) ?? (0, import_plan_utils3.taskRef)(task);
|
|
4487
|
+
var namedOf = (tasks, task) => (0, import_plan_utils3.titledTaskRefOf)(tasks, task.id) ?? (0, import_plan_utils3.titledTaskRef)(task);
|
|
4490
4488
|
async function withTask(subArgs, usage, injectedApi, run, { joinValue = false } = {}) {
|
|
4491
4489
|
await withResolvedTask(subArgs, usage, injectedApi, async (api, sessionId, taskId, plan) => {
|
|
4492
4490
|
const tasks = taskViews(plan);
|
|
@@ -4548,8 +4546,9 @@ Model \xB7 ${namedOf(tasks, task)}${task.assignedRunner ? ` (${task.assignedRunn
|
|
|
4548
4546
|
const assignedModel = assignedModelFor(model, task.assignedModel?.thinkingEffort);
|
|
4549
4547
|
await api.updateTask(sessionId, task.id, { assignedModel });
|
|
4550
4548
|
console.log(`Task ${refOf(tasks, task)} model set to ${model.label}.`);
|
|
4551
|
-
|
|
4552
|
-
|
|
4549
|
+
const was = task.assignedModel?.thinkingEffort;
|
|
4550
|
+
if (was && assignedModel.thinkingEffort !== was) {
|
|
4551
|
+
console.log(` ${model.label} does not expose "${was}", so the effort is now ${assignedModel.thinkingEffort ? `"${assignedModel.thinkingEffort}", its nearest level` : "the runner default"}.`);
|
|
4553
4552
|
}
|
|
4554
4553
|
});
|
|
4555
4554
|
}
|
|
@@ -4625,15 +4624,16 @@ Mode \xB7 ${namedOf(tasks, task)}${task.assignedRunner ? ` (${task.assignedRunne
|
|
|
4625
4624
|
var DEPS_USAGE = "Usage: ordewell task-deps <task-id-or-order> [<id,id,\u2026>|none] [--session-id <id>]";
|
|
4626
4625
|
async function handleTaskDeps(subArgs, injectedApi) {
|
|
4627
4626
|
await withTask(subArgs, DEPS_USAGE, injectedApi, async (api, sessionId, task, tasks, value) => {
|
|
4628
|
-
const candidates = (0,
|
|
4627
|
+
const candidates = (0, import_plan_utils3.dependencyCandidates)(tasks, task.id);
|
|
4628
|
+
const held = task.dependencies.filter((id) => !candidates.some((c) => c.id === id)).flatMap((id) => (0, import_plan_utils3.resolveTaskRef)(tasks, id) ?? []);
|
|
4629
4629
|
if (!value) {
|
|
4630
4630
|
console.log(`
|
|
4631
4631
|
Dependencies \xB7 ${namedOf(tasks, task)}
|
|
4632
4632
|
`);
|
|
4633
4633
|
if (candidates.length === 0) {
|
|
4634
|
-
console.log(`
|
|
4634
|
+
console.log(` No earlier siblings to offer as dependencies for ${refOf(tasks, task)}.`);
|
|
4635
4635
|
}
|
|
4636
|
-
for (const c of candidates) {
|
|
4636
|
+
for (const c of [...candidates, ...held]) {
|
|
4637
4637
|
const chosen = task.dependencies.includes(c.id);
|
|
4638
4638
|
console.log(` ${chosen ? "*" : " "} ${c.id} ${namedOf(tasks, c)}${c.status === "completed" ? " (already completed)" : ""}`);
|
|
4639
4639
|
}
|
|
@@ -4641,8 +4641,8 @@ Dependencies \xB7 ${namedOf(tasks, task)}
|
|
|
4641
4641
|
* = current. ${DEPS_USAGE}`);
|
|
4642
4642
|
return;
|
|
4643
4643
|
}
|
|
4644
|
-
const dependencies = value.toLowerCase() === "none" ? [] : value.split(",").map((s) => s.trim()).filter(Boolean).map((token) =>
|
|
4645
|
-
const check = (0,
|
|
4644
|
+
const dependencies = value.toLowerCase() === "none" ? [] : value.split(",").map((s) => s.trim()).filter(Boolean).map((token) => (0, import_plan_utils3.resolveUserTaskRef)(tasks, token)?.id ?? token);
|
|
4645
|
+
const check = (0, import_plan_utils3.canSetDependencies)(tasks, task.id, dependencies);
|
|
4646
4646
|
if (!check.ok) fail(check.error ?? "Invalid dependencies.");
|
|
4647
4647
|
await api.updateTask(sessionId, task.id, { dependencies });
|
|
4648
4648
|
console.log(dependencies.length > 0 ? `Task ${refOf(tasks, task)} now depends on ${dependencies.length} task${dependencies.length === 1 ? "" : "s"}.` : `Task ${refOf(tasks, task)} no longer depends on anything.`);
|
|
@@ -4871,7 +4871,7 @@ var import_core33 = require("@ordewell/core");
|
|
|
4871
4871
|
|
|
4872
4872
|
// src/tui/reducers/pickers.ts
|
|
4873
4873
|
var import_core31 = require("@ordewell/core");
|
|
4874
|
-
var
|
|
4874
|
+
var import_plan_utils4 = require("@ordewell/core/plan-utils");
|
|
4875
4875
|
var DEFAULT_EFFORT = "__runner_default__";
|
|
4876
4876
|
function picker(title, items, action, extra = {}) {
|
|
4877
4877
|
return { title, items, filter: "", index: 0, multi: false, chosen: [], filterable: false, action, ...extra };
|
|
@@ -4980,7 +4980,7 @@ function pickerItemsFor(state, action) {
|
|
|
4980
4980
|
}));
|
|
4981
4981
|
}
|
|
4982
4982
|
if (action.kind === "set-task-deps") {
|
|
4983
|
-
return (0,
|
|
4983
|
+
return (0, import_plan_utils4.dependencyCandidates)(state.tasks, action.taskId).map((candidate) => ({
|
|
4984
4984
|
id: candidate.id,
|
|
4985
4985
|
label: titledRefFor(state, candidate),
|
|
4986
4986
|
detail: candidate.status === "completed" ? "already completed" : void 0
|
|
@@ -6651,12 +6651,7 @@ function openTaskView(state, sessionId, taskId) {
|
|
|
6651
6651
|
if (!task) return step(say(state, "system", `No task ${taskId} in this plan.`));
|
|
6652
6652
|
const taskView = {
|
|
6653
6653
|
taskId,
|
|
6654
|
-
|
|
6655
|
-
attempts: [],
|
|
6656
|
-
attempt: 0,
|
|
6657
|
-
pending: [],
|
|
6658
|
-
loaded: false,
|
|
6659
|
-
followLatest: true,
|
|
6654
|
+
...import_core40.EMPTY_TASK_LOG_FOLLOW,
|
|
6660
6655
|
queuedIndex: null
|
|
6661
6656
|
};
|
|
6662
6657
|
return step({ ...state, taskView, focus: "chat", scroll: 0 }, [{ type: "openTaskLog", sessionId, taskId }]);
|
|
@@ -6672,67 +6667,35 @@ function clampQueueIndex(tv) {
|
|
|
6672
6667
|
const max = tv.view.queued.length - 1;
|
|
6673
6668
|
return tv.queuedIndex <= max ? tv : { ...tv, queuedIndex: max };
|
|
6674
6669
|
}
|
|
6675
|
-
function applyLive(tv, attempt, events) {
|
|
6676
|
-
if (attempt <= 0) return tv;
|
|
6677
|
-
let next = tv;
|
|
6678
|
-
if (attempt > next.attempt) {
|
|
6679
|
-
if (!next.followLatest) {
|
|
6680
|
-
return next.attempts.includes(attempt) ? next : { ...next, attempts: [...next.attempts, attempt] };
|
|
6681
|
-
}
|
|
6682
|
-
next = {
|
|
6683
|
-
...next,
|
|
6684
|
-
view: import_core40.EMPTY_TASK_LOG,
|
|
6685
|
-
attempt,
|
|
6686
|
-
attempts: next.attempts.includes(attempt) ? next.attempts : [...next.attempts, attempt]
|
|
6687
|
-
};
|
|
6688
|
-
}
|
|
6689
|
-
if (attempt < next.attempt) return next;
|
|
6690
|
-
return clampQueueIndex({ ...next, view: events.reduce(import_core40.reduceTaskLog, next.view) });
|
|
6691
|
-
}
|
|
6692
6670
|
function taskLogAbandoned(state) {
|
|
6693
6671
|
const tv = state.taskView;
|
|
6694
6672
|
if (!tv || tv.loaded) return state;
|
|
6695
|
-
|
|
6696
|
-
for (const batch of tv.pending) next = applyLive(next, batch.attempt, batch.events);
|
|
6697
|
-
return { ...state, taskView: clampQueueIndex(next) };
|
|
6673
|
+
return { ...state, taskView: clampQueueIndex({ ...tv, ...(0, import_core40.followTaskLog)(tv, { type: "loaded", attempt: tv.attempt, events: [], abandoned: true }) }) };
|
|
6698
6674
|
}
|
|
6699
6675
|
function taskLogArrived(state, action) {
|
|
6700
6676
|
const tv = state.taskView;
|
|
6701
6677
|
if (!tv || tv.taskId !== action.taskId) return state;
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
}
|
|
6705
|
-
return { ...state, taskView: applyLive(tv, action.attempt, action.events) };
|
|
6678
|
+
const next = (0, import_core40.followTaskLog)(tv, { ...action, type: "live" });
|
|
6679
|
+
return { ...state, taskView: next === tv ? tv : clampQueueIndex({ ...tv, ...next }) };
|
|
6706
6680
|
}
|
|
6707
6681
|
function announceApprovals(state, action) {
|
|
6708
6682
|
if (state.taskView?.taskId === action.taskId) return state;
|
|
6709
6683
|
const task = findTask(state.tasks, action.taskId);
|
|
6710
|
-
const way = task ? `tab to the plan, select task ${task
|
|
6711
|
-
const spoken = action.events.reduce((s, event) => event.type === "approval_requested" && !event.decided ? say(s, "system", `Task ${task
|
|
6684
|
+
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";
|
|
6685
|
+
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_core40.runnerToolSubject)(event.tool, event.args)}: ${way}, then ctrl-y allows or ctrl-g denies`) : s, state);
|
|
6712
6686
|
return spoken !== state ? { ...spoken, scroll: state.scroll } : spoken;
|
|
6713
6687
|
}
|
|
6714
6688
|
function announceCheckpoint(state, action) {
|
|
6715
6689
|
const task = findTask(state.tasks, action.taskId) ?? state.tasks.find((t) => t.title === action.title);
|
|
6716
|
-
const label = task ? `Task ${task
|
|
6717
|
-
const where = task ? `tab to the plan, select task ${task
|
|
6690
|
+
const label = task ? `Task ${refFor(state, task)}` : action.title;
|
|
6691
|
+
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";
|
|
6718
6692
|
const spoken = say(state, "system", `${label} asks: ${(0, import_core40.truncateCheckpointSummary)(action.summary)}: ${where}`);
|
|
6719
6693
|
return { ...spoken, scroll: state.scroll };
|
|
6720
6694
|
}
|
|
6721
6695
|
function taskLogLoaded(state, action) {
|
|
6722
6696
|
const tv = state.taskView;
|
|
6723
6697
|
if (!tv || tv.taskId !== action.taskId) return state;
|
|
6724
|
-
|
|
6725
|
-
...tv,
|
|
6726
|
-
view: (0, import_core40.replayTaskLog)(action.events),
|
|
6727
|
-
attempts: action.attempts ?? tv.attempts,
|
|
6728
|
-
attempt: action.attempt
|
|
6729
|
-
} : { ...tv, attempts: action.attempts ?? tv.attempts };
|
|
6730
|
-
let next = { ...loaded, pending: [], loaded: true };
|
|
6731
|
-
for (const batch of tv.pending) {
|
|
6732
|
-
if (batch.attempt === action.attempt) continue;
|
|
6733
|
-
next = applyLive(next, batch.attempt, batch.events);
|
|
6734
|
-
}
|
|
6735
|
-
return { ...state, taskView: clampQueueIndex(next) };
|
|
6698
|
+
return { ...state, taskView: clampQueueIndex({ ...tv, ...(0, import_core40.followTaskLog)(tv, { ...action, type: "loaded" }) }) };
|
|
6736
6699
|
}
|
|
6737
6700
|
function answerApproval(state, tv, sessionId, decision) {
|
|
6738
6701
|
const approval = waitingApproval(tv);
|
|
@@ -6776,7 +6739,7 @@ function handleTaskViewKey(state, key) {
|
|
|
6776
6739
|
const tv = state.taskView;
|
|
6777
6740
|
if (!tv || !state.sessionId) return null;
|
|
6778
6741
|
if (key.name === "alt-left" || key.name === "alt-right") {
|
|
6779
|
-
const index = tv.attempts.indexOf(tv.attempt);
|
|
6742
|
+
const index = tv.attempts.indexOf(tv.loadingAttempt ?? tv.attempt);
|
|
6780
6743
|
if (index < 0) return null;
|
|
6781
6744
|
const target = key.name === "alt-left" ? index - 1 : index + 1;
|
|
6782
6745
|
const attempt = tv.attempts[target];
|
|
@@ -6784,7 +6747,7 @@ function handleTaskViewKey(state, key) {
|
|
|
6784
6747
|
return step(say(state, "system", target < 0 ? "This is the first attempt." : "This is the latest attempt."));
|
|
6785
6748
|
}
|
|
6786
6749
|
return step(
|
|
6787
|
-
{ ...state, taskView: { ...tv,
|
|
6750
|
+
{ ...state, taskView: { ...tv, ...(0, import_core40.followTaskLog)(tv, { type: "picked", attempt }) } },
|
|
6788
6751
|
[{ type: "loadTaskAttempt", sessionId: state.sessionId, taskId: tv.taskId, attempt }]
|
|
6789
6752
|
);
|
|
6790
6753
|
}
|
|
@@ -6816,7 +6779,7 @@ function handleTaskViewKey(state, key) {
|
|
|
6816
6779
|
|
|
6817
6780
|
// src/tui/reducers/taskEdits.ts
|
|
6818
6781
|
var import_core41 = require("@ordewell/core");
|
|
6819
|
-
var
|
|
6782
|
+
var import_plan_utils5 = require("@ordewell/core/plan-utils");
|
|
6820
6783
|
function openTaskRunnerPicker(state, task) {
|
|
6821
6784
|
if (task.type !== "ai") return fail3(state, "Manual tasks do not run on an executor, so they have no runner.");
|
|
6822
6785
|
const action = { kind: "set-task-runner", taskId: task.id };
|
|
@@ -6838,7 +6801,7 @@ function confirmRemoveTask(state, task) {
|
|
|
6838
6801
|
overlay: {
|
|
6839
6802
|
kind: "confirm",
|
|
6840
6803
|
title: `Remove ${titledRefFor(state, task)}?`,
|
|
6841
|
-
message: (0,
|
|
6804
|
+
message: (0, import_plan_utils5.dependentsNotice)((0, import_plan_utils5.dependentsOf)(state.tasks, task.id), state.tasks) ?? "This cannot be undone.",
|
|
6842
6805
|
action: { kind: "remove-task", taskId: task.id }
|
|
6843
6806
|
}
|
|
6844
6807
|
});
|
|
@@ -6862,7 +6825,7 @@ function confirmForceStartPastGate(state, task) {
|
|
|
6862
6825
|
overlay: {
|
|
6863
6826
|
kind: "confirm",
|
|
6864
6827
|
title: `Force start ${titledRefFor(state, task)}?`,
|
|
6865
|
-
message: (0,
|
|
6828
|
+
message: (0, import_plan_utils5.pastGateConfirmation)((0, import_plan_utils5.titledRefs)(task.mergeGate ?? [], state.tasks), "It"),
|
|
6866
6829
|
action: { kind: "force-start-gated", taskId: task.id }
|
|
6867
6830
|
}
|
|
6868
6831
|
});
|
|
@@ -6870,16 +6833,16 @@ function confirmForceStartPastGate(state, task) {
|
|
|
6870
6833
|
function openTaskDepsPicker(state, task) {
|
|
6871
6834
|
const action = { kind: "set-task-deps", taskId: task.id };
|
|
6872
6835
|
const items = pickerItemsFor(state, action);
|
|
6873
|
-
if (items.length === 0) return fail3(state, `
|
|
6836
|
+
if (items.length === 0) return fail3(state, `No earlier siblings to offer as dependencies for ${refFor(state, task)}.`);
|
|
6874
6837
|
return step({
|
|
6875
6838
|
...state,
|
|
6876
6839
|
overlay: {
|
|
6877
6840
|
kind: "picker",
|
|
6878
6841
|
picker: picker(`Depends on \xB7 ${titledRefFor(state, task)}`, items, action, {
|
|
6879
|
-
hint: "
|
|
6842
|
+
hint: "Offers earlier siblings; existing dependencies are kept.",
|
|
6880
6843
|
multi: true,
|
|
6881
6844
|
filterable: true,
|
|
6882
|
-
chosen: task.dependencies
|
|
6845
|
+
chosen: task.dependencies
|
|
6883
6846
|
})
|
|
6884
6847
|
}
|
|
6885
6848
|
});
|
|
@@ -7097,14 +7060,7 @@ function taskCommand(state, token, run) {
|
|
|
7097
7060
|
});
|
|
7098
7061
|
}
|
|
7099
7062
|
function resolveTaskId2(state, token) {
|
|
7100
|
-
|
|
7101
|
-
if (byId) return byId.id;
|
|
7102
|
-
const order = Number(token);
|
|
7103
|
-
if (Number.isInteger(order)) {
|
|
7104
|
-
const byOrder = state.tasks.find((t) => t.order === order);
|
|
7105
|
-
if (byOrder) return byOrder.id;
|
|
7106
|
-
}
|
|
7107
|
-
return null;
|
|
7063
|
+
return (0, import_plan_utils5.resolveUserTaskRef)(state.tasks, token)?.id ?? null;
|
|
7108
7064
|
}
|
|
7109
7065
|
|
|
7110
7066
|
// src/tui/reducers/planPane.ts
|
|
@@ -7349,28 +7305,6 @@ function foldRun(state, input) {
|
|
|
7349
7305
|
const run = (0, import_core44.reduceRun)(state.run, input);
|
|
7350
7306
|
return run === state.run ? state : withRun(state, run);
|
|
7351
7307
|
}
|
|
7352
|
-
function mapRunTasks(tasks, map) {
|
|
7353
|
-
return tasks.map((task) => ({ ...map(task), subtasks: mapRunTasks(task.subtasks, map) }));
|
|
7354
|
-
}
|
|
7355
|
-
function applyIsolationRecord(state, plan) {
|
|
7356
|
-
const record = isolationOfPlan(plan);
|
|
7357
|
-
if (!record) return state;
|
|
7358
|
-
const { run } = state;
|
|
7359
|
-
return withRun(state, {
|
|
7360
|
-
...run,
|
|
7361
|
-
tasks: mapRunTasks(run.tasks, (task) => ({ ...task, isolation: record.tasks[task.id] ?? { state: "none" } })),
|
|
7362
|
-
handoff: withDeferral(record.handoff, run.handoff)
|
|
7363
|
-
});
|
|
7364
|
-
}
|
|
7365
|
-
function clearRunIsolation(run) {
|
|
7366
|
-
return {
|
|
7367
|
-
...run,
|
|
7368
|
-
handoff: null,
|
|
7369
|
-
runMerge: null,
|
|
7370
|
-
mergeResult: null,
|
|
7371
|
-
tasks: mapRunTasks(run.tasks, ({ isolation: _dropped, ...task }) => task)
|
|
7372
|
-
};
|
|
7373
|
-
}
|
|
7374
7308
|
function applySettings(state, settings) {
|
|
7375
7309
|
return {
|
|
7376
7310
|
...state,
|
|
@@ -8241,6 +8175,24 @@ function reduceAction(state, action) {
|
|
|
8241
8175
|
// tasks — may carry over into this.
|
|
8242
8176
|
case "sessionStarted":
|
|
8243
8177
|
return step(withRun({ ...state, sessionId: action.sessionId, goal: action.goal, taskView: null }, import_core49.EMPTY_RUN));
|
|
8178
|
+
// A loaded session is a different one: the turn the old one had out, the
|
|
8179
|
+
// prompts held behind it and a stop armed against it are not this one's.
|
|
8180
|
+
// (`sessionStarted` cannot reset them: `startConversation` dispatches it
|
|
8181
|
+
// as its own turn begins.)
|
|
8182
|
+
case "sessionLoaded":
|
|
8183
|
+
return step(withRun({
|
|
8184
|
+
...state,
|
|
8185
|
+
sessionId: action.sessionId,
|
|
8186
|
+
goal: action.goal,
|
|
8187
|
+
taskView: null,
|
|
8188
|
+
planner: "idle",
|
|
8189
|
+
turnGate: import_core49.NO_TURN,
|
|
8190
|
+
pendingApprovals: [],
|
|
8191
|
+
queuedPrompts: import_core49.EMPTY_HOLD,
|
|
8192
|
+
stopArmed: false,
|
|
8193
|
+
forceSendPending: null,
|
|
8194
|
+
overlay: state.overlay?.kind === "approval" ? null : state.overlay
|
|
8195
|
+
}, import_core49.EMPTY_RUN));
|
|
8244
8196
|
// The daemon registers a session only once planning succeeds; when it does
|
|
8245
8197
|
// not, holding on to the id would send every next message to a session the
|
|
8246
8198
|
// server never had.
|
|
@@ -8250,6 +8202,7 @@ function reduceAction(state, action) {
|
|
|
8250
8202
|
sessionId: null,
|
|
8251
8203
|
goal: "",
|
|
8252
8204
|
taskView: null,
|
|
8205
|
+
turnGate: import_core49.NO_TURN,
|
|
8253
8206
|
pendingApprovals: [],
|
|
8254
8207
|
queuedPrompts: import_core49.EMPTY_HOLD,
|
|
8255
8208
|
stopArmed: false,
|
|
@@ -8263,6 +8216,9 @@ function reduceAction(state, action) {
|
|
|
8263
8216
|
case "chatRestored":
|
|
8264
8217
|
if (stale(state, action.sessionId)) return step(state);
|
|
8265
8218
|
return step({ ...state, conversation: (0, import_core49.fromTranscript)(action.history, action.researchLog, action.plannerUsage, action.pendingTurns), scroll: 0 });
|
|
8219
|
+
case "planRefreshed":
|
|
8220
|
+
if (stale(state, action.sessionId)) return step(state);
|
|
8221
|
+
return step(showPlan(state, action.plan));
|
|
8266
8222
|
case "planUpdated": {
|
|
8267
8223
|
if (stale(state, action.sessionId)) return step(state);
|
|
8268
8224
|
const settled = { ...showPlan(state, action.plan), planner: "idle" };
|
|
@@ -8349,10 +8305,16 @@ function reduceAction(state, action) {
|
|
|
8349
8305
|
if (stale(state, action.sessionId)) return step(state);
|
|
8350
8306
|
return step(showDiff(state, action.diff));
|
|
8351
8307
|
}
|
|
8352
|
-
|
|
8308
|
+
// What Merge all did, folded the way the Session says it: a merge that
|
|
8309
|
+
// leaves the run going keeps the marks (ADR-0020), and only `run_closed`
|
|
8310
|
+
// takes them (below).
|
|
8311
|
+
case "isolationMerged": {
|
|
8353
8312
|
if (stale(state, action.sessionId)) return step(state);
|
|
8354
|
-
|
|
8355
|
-
|
|
8313
|
+
return step(withoutHandoffOverlay(foldRun(state, { type: "isolation_merge", result: action.result })));
|
|
8314
|
+
}
|
|
8315
|
+
case "runClosed": {
|
|
8316
|
+
if (stale(state, action.sessionId)) return step(state);
|
|
8317
|
+
return step(withoutHandoffOverlay(foldRun(state, { type: "run_closed" })));
|
|
8356
8318
|
}
|
|
8357
8319
|
case "executionComplete": {
|
|
8358
8320
|
if (stale(state, action.sessionId)) return step(state);
|
|
@@ -8412,9 +8374,9 @@ function reduceAction(state, action) {
|
|
|
8412
8374
|
if (stale(state, action.sessionId)) return step(state);
|
|
8413
8375
|
if (action.pick !== void 0) return pickRewindTarget({ ...state, rewindTargets: action.targets }, action.pick);
|
|
8414
8376
|
return step(refillPicker({ ...state, rewindTargets: action.targets }, ["rewind"]));
|
|
8415
|
-
// A fork holds no run, whatever the session it came from was
|
|
8416
|
-
// original keeps
|
|
8417
|
-
// arrives next, onto an empty run view.
|
|
8377
|
+
// A fork holds no run and no turn, whatever the session it came from was
|
|
8378
|
+
// doing — the original keeps both, and its events are stale from here on.
|
|
8379
|
+
// Its plan arrives next, onto an empty run view.
|
|
8418
8380
|
case "sessionForked":
|
|
8419
8381
|
return step(withRun({
|
|
8420
8382
|
...state,
|
|
@@ -8423,6 +8385,7 @@ function reduceAction(state, action) {
|
|
|
8423
8385
|
taskView: null,
|
|
8424
8386
|
status: "idle",
|
|
8425
8387
|
planner: "idle",
|
|
8388
|
+
turnGate: import_core49.NO_TURN,
|
|
8426
8389
|
planApproved: false,
|
|
8427
8390
|
pendingApprovals: [],
|
|
8428
8391
|
queuedPrompts: import_core49.EMPTY_HOLD,
|
|
@@ -8488,8 +8451,11 @@ function reduceAction(state, action) {
|
|
|
8488
8451
|
return step(disarmStop(state));
|
|
8489
8452
|
}
|
|
8490
8453
|
}
|
|
8454
|
+
function withoutHandoffOverlay(state) {
|
|
8455
|
+
return state.handoff || state.overlay?.kind !== "handoff" ? state : { ...state, overlay: null };
|
|
8456
|
+
}
|
|
8491
8457
|
function showPlan(state, plan) {
|
|
8492
|
-
const folded =
|
|
8458
|
+
const folded = foldRun(state, planSnapshot(plan));
|
|
8493
8459
|
const { tasks } = folded;
|
|
8494
8460
|
const expandedTaskId = state.expandedTaskId && findTask(tasks, state.expandedTaskId) ? state.expandedTaskId : null;
|
|
8495
8461
|
const next = {
|
|
@@ -8847,6 +8813,7 @@ function createInbound(dispatch, sessionId) {
|
|
|
8847
8813
|
const delivered = new Delivered(256);
|
|
8848
8814
|
const copies = new Copies(64);
|
|
8849
8815
|
const shown = new WindowSet(64);
|
|
8816
|
+
const shownTexts = new WindowSet(64);
|
|
8850
8817
|
const pending = new WindowSet(16);
|
|
8851
8818
|
const DELTA_DEBOUNCE_MS = 75;
|
|
8852
8819
|
let held = null;
|
|
@@ -8875,7 +8842,8 @@ function createInbound(dispatch, sessionId) {
|
|
|
8875
8842
|
timer = setTimeout(flush, DELTA_DEBOUNCE_MS);
|
|
8876
8843
|
};
|
|
8877
8844
|
const showReply = (message) => {
|
|
8878
|
-
shown.add(message.content);
|
|
8845
|
+
shown.add(replyKey(message.content, message.timestamp));
|
|
8846
|
+
shownTexts.add(message.content);
|
|
8879
8847
|
dispatch({ type: "sessionMessage", message, sessionId });
|
|
8880
8848
|
};
|
|
8881
8849
|
const fresh = (id, event) => {
|
|
@@ -8947,14 +8915,15 @@ function createInbound(dispatch, sessionId) {
|
|
|
8947
8915
|
const id = `e${++subscription}`;
|
|
8948
8916
|
return (event) => receive("execution", id, event);
|
|
8949
8917
|
},
|
|
8950
|
-
backfill: (content, timestamp) => {
|
|
8951
|
-
if (shown.has(content)) return;
|
|
8952
|
-
const message = { type: "planner_message", content, timestamp: timestamp ?? (/* @__PURE__ */ new Date()).toISOString() };
|
|
8918
|
+
backfill: (content, timestamp, runs) => {
|
|
8919
|
+
if (timestamp ? shown.has(replyKey(content, timestamp)) : shownTexts.has(content)) return;
|
|
8920
|
+
const message = { type: "planner_message", content, timestamp: timestamp ?? (/* @__PURE__ */ new Date()).toISOString(), ...runs ? { runs } : {} };
|
|
8953
8921
|
showReply(message);
|
|
8954
8922
|
if (timestamp) pending.add(replyKey(content, timestamp));
|
|
8955
8923
|
},
|
|
8956
8924
|
redrawn: (content, timestamp) => {
|
|
8957
|
-
shown.add(content);
|
|
8925
|
+
shown.add(replyKey(content, timestamp));
|
|
8926
|
+
shownTexts.add(content);
|
|
8958
8927
|
pending.add(replyKey(content, timestamp));
|
|
8959
8928
|
},
|
|
8960
8929
|
flush,
|
|
@@ -9026,7 +8995,7 @@ function dispatchLifecycle(dispatch, event, sessionId) {
|
|
|
9026
8995
|
// its only reader, and the reducer drops it unless that view is open on
|
|
9027
8996
|
// this task. The run's watcher itself does nothing with it.
|
|
9028
8997
|
case "task_log":
|
|
9029
|
-
dispatch({ type: "taskLog", taskId: String(event.taskId), attempt: event.attempt, events: event.events, sessionId });
|
|
8998
|
+
dispatch({ type: "taskLog", taskId: String(event.taskId), attempt: event.attempt, events: event.events, position: stampOf(event) ?? void 0, sessionId });
|
|
9030
8999
|
return;
|
|
9031
9000
|
case "task_updated":
|
|
9032
9001
|
dispatch({ type: "taskUpdated", taskId: String(event.taskId), changes: event.changes, sessionId });
|
|
@@ -9036,10 +9005,13 @@ function dispatchLifecycle(dispatch, event, sessionId) {
|
|
|
9036
9005
|
// does with it.
|
|
9037
9006
|
case "task_output":
|
|
9038
9007
|
return;
|
|
9039
|
-
// Its asker already has the words, from the merge request itself
|
|
9040
|
-
//
|
|
9008
|
+
// Its asker already has the words, from the merge request itself. Whether
|
|
9009
|
+
// the merge ended the run is the Session's to say, in `run_closed`.
|
|
9041
9010
|
case "isolation_merge":
|
|
9042
|
-
|
|
9011
|
+
dispatch({ type: "isolationMerged", result: event.result, sessionId });
|
|
9012
|
+
return;
|
|
9013
|
+
case "run_closed":
|
|
9014
|
+
dispatch({ type: "runClosed", sessionId });
|
|
9043
9015
|
return;
|
|
9044
9016
|
default: {
|
|
9045
9017
|
const unhandled = event;
|
|
@@ -9136,9 +9108,10 @@ async function perform(effect, deps) {
|
|
|
9136
9108
|
case "execute":
|
|
9137
9109
|
await withExecutionStream(deps, effect.sessionId, () => api.executePlan(effect.sessionId));
|
|
9138
9110
|
return;
|
|
9111
|
+
// The seam keeps its own line of a Stop in the timeline and the stream
|
|
9112
|
+
// announces the run's end, so this adds none (ADR-0029).
|
|
9139
9113
|
case "stopExecution":
|
|
9140
9114
|
await api.stopExecution(effect.sessionId);
|
|
9141
|
-
dispatch({ type: "notice", message: "Execution stopped." });
|
|
9142
9115
|
return;
|
|
9143
9116
|
case "cancelPlanning": {
|
|
9144
9117
|
const { cancelled } = await api.cancelPlanning(effect.sessionId);
|
|
@@ -9156,8 +9129,7 @@ async function perform(effect, deps) {
|
|
|
9156
9129
|
case "taskAction": {
|
|
9157
9130
|
const { sessionId, taskId, action } = effect;
|
|
9158
9131
|
if (action === "cancel-resume") {
|
|
9159
|
-
|
|
9160
|
-
dispatch({ type: "notice", message: cancelled ? "Resume cancelled: retry the task once the limit resets." : "That task had no resume scheduled." });
|
|
9132
|
+
await api.cancelResume(sessionId, taskId);
|
|
9161
9133
|
await refreshPlan(deps, sessionId);
|
|
9162
9134
|
return;
|
|
9163
9135
|
}
|
|
@@ -9215,19 +9187,19 @@ async function perform(effect, deps) {
|
|
|
9215
9187
|
// The saved log is read off disk (ADR-0018, P1) so the view opens with the
|
|
9216
9188
|
// task's history; the live `task_log` stream then catches it up in place.
|
|
9217
9189
|
case "openTaskLog": {
|
|
9218
|
-
const attempts = await api.getTaskLogAttempts(effect.sessionId, effect.taskId, workspace);
|
|
9190
|
+
const { attempts, position } = await api.getTaskLogAttempts(effect.sessionId, effect.taskId, workspace);
|
|
9219
9191
|
if (attempts.length === 0) {
|
|
9220
|
-
dispatch({ type: "taskLogLoaded", taskId: effect.taskId, attempts: [], attempt: 0, events: [], sessionId: effect.sessionId });
|
|
9192
|
+
dispatch({ type: "taskLogLoaded", taskId: effect.taskId, attempts: [], attempt: 0, events: [], position, sessionId: effect.sessionId });
|
|
9221
9193
|
return;
|
|
9222
9194
|
}
|
|
9223
9195
|
const attempt = attempts[attempts.length - 1];
|
|
9224
|
-
const
|
|
9225
|
-
dispatch({ type: "taskLogLoaded", taskId: effect.taskId, attempts,
|
|
9196
|
+
const log = await api.getTaskLog(effect.sessionId, effect.taskId, attempt, workspace);
|
|
9197
|
+
dispatch({ type: "taskLogLoaded", taskId: effect.taskId, attempts, ...log, sessionId: effect.sessionId });
|
|
9226
9198
|
return;
|
|
9227
9199
|
}
|
|
9228
9200
|
case "loadTaskAttempt": {
|
|
9229
|
-
const
|
|
9230
|
-
dispatch({ type: "taskLogLoaded", taskId: effect.taskId,
|
|
9201
|
+
const log = await api.getTaskLog(effect.sessionId, effect.taskId, effect.attempt, workspace);
|
|
9202
|
+
dispatch({ type: "taskLogLoaded", taskId: effect.taskId, ...log, sessionId: effect.sessionId });
|
|
9231
9203
|
return;
|
|
9232
9204
|
}
|
|
9233
9205
|
// The daemon queues the message (or delivers it to a waiting task) and
|
|
@@ -9247,24 +9219,20 @@ async function perform(effect, deps) {
|
|
|
9247
9219
|
await refreshPlan(deps, sessionId);
|
|
9248
9220
|
return;
|
|
9249
9221
|
}
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9222
|
+
// One already delivered is refused in the daemon's words.
|
|
9223
|
+
case "removeTaskMessage":
|
|
9224
|
+
await api.removeQueuedTaskMessage(effect.sessionId, effect.taskId, effect.messageId);
|
|
9253
9225
|
return;
|
|
9254
|
-
}
|
|
9255
9226
|
// The task log shows the forced message at the head of the queue, then
|
|
9256
9227
|
// the interrupt, then the turn it opens; nothing to dispatch here.
|
|
9257
9228
|
case "forceSendTaskMessage":
|
|
9258
9229
|
await api.forceSendTaskMessage(effect.sessionId, effect.taskId, effect.text);
|
|
9259
9230
|
return;
|
|
9260
|
-
case "forceSendQueuedTaskMessage":
|
|
9261
|
-
|
|
9262
|
-
if (!sent) dispatch({ type: "notice", message: "That message has already reached the runner." });
|
|
9231
|
+
case "forceSendQueuedTaskMessage":
|
|
9232
|
+
await api.forceSendQueuedTaskMessage(effect.sessionId, effect.taskId, effect.messageId);
|
|
9263
9233
|
return;
|
|
9264
|
-
}
|
|
9265
9234
|
case "interruptTask":
|
|
9266
9235
|
await api.interruptTask(effect.sessionId, effect.taskId);
|
|
9267
|
-
dispatch({ type: "notice", message: "Interrupting the task\u2026" });
|
|
9268
9236
|
return;
|
|
9269
9237
|
case "setModel": {
|
|
9270
9238
|
const settled = await api.updateSettings({ orchestratorModel: effect.modelId });
|
|
@@ -9404,11 +9372,12 @@ async function perform(effect, deps) {
|
|
|
9404
9372
|
else unfollow(deps, effect.sessionId);
|
|
9405
9373
|
throw err;
|
|
9406
9374
|
}
|
|
9407
|
-
const { plan, goal } = adopted2;
|
|
9408
|
-
dispatch({ type: "
|
|
9375
|
+
const { plan, goal, run } = adopted2;
|
|
9376
|
+
dispatch({ type: "sessionLoaded", sessionId: effect.sessionId, goal });
|
|
9409
9377
|
dispatch(restoredChat(plan, effect.sessionId));
|
|
9410
9378
|
inbound.releaseSnapshot(true);
|
|
9411
9379
|
dispatch({ type: "planUpdated", plan, sessionId: effect.sessionId });
|
|
9380
|
+
if (run) dispatch({ type: "runSnapshot", view: run, sessionId: effect.sessionId });
|
|
9412
9381
|
dispatch({ type: "notice", message: `Loaded "${goal || effect.sessionId}".` });
|
|
9413
9382
|
return;
|
|
9414
9383
|
}
|
|
@@ -9469,16 +9438,15 @@ async function perform(effect, deps) {
|
|
|
9469
9438
|
// A conflict or a refusal is an answer, not a fault: either way the user's
|
|
9470
9439
|
// tree is exactly as it was, and the words say what to do next. A full
|
|
9471
9440
|
// merge of a settled run leaves nothing to hand over — the daemon has
|
|
9472
|
-
// cleared the run up
|
|
9441
|
+
// cleared the run up and says so on the stream (`run_closed`); one
|
|
9442
|
+
// mid-run leaves the run going (ADR-0020).
|
|
9473
9443
|
case "isolationMerge": {
|
|
9474
9444
|
const { ok, message } = mergeOutcome(await api.mergeRun(effect.sessionId), effect.branch, effect.group === true, effect.repaired ?? []);
|
|
9475
|
-
if (ok && !effect.midRun) dispatch({ type: "runCleared", sessionId: effect.sessionId });
|
|
9476
9445
|
dispatch({ type: ok ? "notice" : "failed", message });
|
|
9477
9446
|
return;
|
|
9478
9447
|
}
|
|
9479
9448
|
case "isolationDiscard":
|
|
9480
9449
|
await api.discardRun(effect.sessionId);
|
|
9481
|
-
dispatch({ type: "runCleared", sessionId: effect.sessionId });
|
|
9482
9450
|
dispatch({ type: "notice", message: `Discarded the run and ${effect.branch}.` });
|
|
9483
9451
|
return;
|
|
9484
9452
|
case "isolationCleanup":
|
|
@@ -9626,8 +9594,7 @@ async function converse(deps, sessionId, call) {
|
|
|
9626
9594
|
await Promise.all([stream.ready, follow(deps, sessionId)]);
|
|
9627
9595
|
const plan = await call();
|
|
9628
9596
|
inbound.flush();
|
|
9629
|
-
const reply
|
|
9630
|
-
if (reply) inbound.backfill(reply.content, reply.timestamp);
|
|
9597
|
+
for (const reply of turnReplies(plan)) inbound.backfill(reply.content, reply.timestamp, reply.runs);
|
|
9631
9598
|
deps.dispatch({ type: "planUpdated", plan, sessionId });
|
|
9632
9599
|
} finally {
|
|
9633
9600
|
inbound.flush();
|
|
@@ -9645,17 +9612,18 @@ function restoredChat(plan, sessionId) {
|
|
|
9645
9612
|
sessionId
|
|
9646
9613
|
};
|
|
9647
9614
|
}
|
|
9648
|
-
function
|
|
9615
|
+
function turnReplies(plan) {
|
|
9649
9616
|
const history = plan?.conversationHistory ?? [];
|
|
9617
|
+
const replies = [];
|
|
9650
9618
|
for (let i = history.length - 1; i >= 0; i--) {
|
|
9651
9619
|
const entry = history[i];
|
|
9652
|
-
if ((0, import_core50.isUserMessage)(entry))
|
|
9620
|
+
if ((0, import_core50.isUserMessage)(entry)) break;
|
|
9653
9621
|
if (entry.role === "assistant" && entry.kind === void 0) {
|
|
9654
|
-
const { content, timestamp } = entry;
|
|
9655
|
-
|
|
9622
|
+
const { content, timestamp, runs } = entry;
|
|
9623
|
+
replies.unshift({ content, ...timestamp ? { timestamp } : {}, ...runs ? { runs } : {} });
|
|
9656
9624
|
}
|
|
9657
9625
|
}
|
|
9658
|
-
return
|
|
9626
|
+
return replies;
|
|
9659
9627
|
}
|
|
9660
9628
|
function compactionSummary(plan) {
|
|
9661
9629
|
const history = plan?.conversationHistory ?? [];
|
|
@@ -9663,8 +9631,9 @@ function compactionSummary(plan) {
|
|
|
9663
9631
|
return entry ? { content: entry.content, ...entry.timestamp ? { timestamp: entry.timestamp } : {} } : null;
|
|
9664
9632
|
}
|
|
9665
9633
|
async function refreshPlan(deps, sessionId) {
|
|
9666
|
-
const { plan } = await deps.api.getSession(sessionId, deps.workspace);
|
|
9667
|
-
deps.dispatch({ type: "
|
|
9634
|
+
const { plan, run } = await deps.api.getSession(sessionId, deps.workspace);
|
|
9635
|
+
deps.dispatch({ type: "planRefreshed", plan, sessionId });
|
|
9636
|
+
if (run) deps.dispatch({ type: "runSnapshot", view: run, sessionId });
|
|
9668
9637
|
}
|
|
9669
9638
|
async function loadModels(deps) {
|
|
9670
9639
|
deps.dispatch({ type: "modelsLoaded", ...normalizeCatalog(await deps.api.getModels()) });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ordewell/cli",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.7",
|
|
4
4
|
"description": "Ordewell CLI and TUI — plan, run and verify coding-agent tasks from the terminal.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Alessandro Costanzo Ciano",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"typecheck": "tsc --noEmit"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@ordewell/core": "^0.10.
|
|
55
|
-
"@ordewell/web": "^0.10.
|
|
54
|
+
"@ordewell/core": "^0.10.7",
|
|
55
|
+
"@ordewell/web": "^0.10.7",
|
|
56
56
|
"string-width": "^7.2.0",
|
|
57
57
|
"ws": "^8.18.0"
|
|
58
58
|
},
|