@multiclawcli/dashboard 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tui.js +87 -5
- package/package.json +2 -2
package/dist/tui.js
CHANGED
|
@@ -123,7 +123,17 @@ var COMMANDS = [
|
|
|
123
123
|
description: "Pause queued turns, not the active agent",
|
|
124
124
|
availability: "always"
|
|
125
125
|
},
|
|
126
|
-
{
|
|
126
|
+
{
|
|
127
|
+
name: "continue",
|
|
128
|
+
description: "Resume queued turns or pass a checkpoint",
|
|
129
|
+
availability: "always"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "modify",
|
|
133
|
+
args: "<instruction>",
|
|
134
|
+
description: "Provide feedback at a checkpoint and continue",
|
|
135
|
+
availability: "running"
|
|
136
|
+
},
|
|
127
137
|
{
|
|
128
138
|
name: "respond",
|
|
129
139
|
args: "<question id> <JSON object>",
|
|
@@ -2273,6 +2283,8 @@ function App({ config, onTurnStart, sessions: providedSessions, onCleanupReady }
|
|
|
2273
2283
|
const pending = useRef([]);
|
|
2274
2284
|
const permissionSerialRef = useRef(0);
|
|
2275
2285
|
const questionSerialRef = useRef(0);
|
|
2286
|
+
const checkpointResolveRef = useRef(null);
|
|
2287
|
+
const checkpointLabelRef = useRef("");
|
|
2276
2288
|
const mountedRef = useRef(true);
|
|
2277
2289
|
const interactionsRef = useRef([]);
|
|
2278
2290
|
const [interactions, setInteractions] = useState([]);
|
|
@@ -2325,6 +2337,22 @@ function App({ config, onTurnStart, sessions: providedSessions, onCleanupReady }
|
|
|
2325
2337
|
if (mountedRef.current) setInteractions([...interactionsRef.current]);
|
|
2326
2338
|
});
|
|
2327
2339
|
}, []);
|
|
2340
|
+
const handleCheckpoint = useCallback((info, signal) => {
|
|
2341
|
+
if (closingRef.current || signal.aborted) return Promise.resolve({ action: "cancel" });
|
|
2342
|
+
return new Promise((resolve2) => {
|
|
2343
|
+
checkpointResolveRef.current = resolve2;
|
|
2344
|
+
checkpointLabelRef.current = info.label;
|
|
2345
|
+
const names = info.completedAgents.map((a) => a.name).join(", ");
|
|
2346
|
+
setNotice(
|
|
2347
|
+
`checkpoint: ${info.label} \u2014 ${names} done \xB7 /continue /modify <instruction> /cancel`
|
|
2348
|
+
);
|
|
2349
|
+
const onAbort = () => {
|
|
2350
|
+
checkpointResolveRef.current = null;
|
|
2351
|
+
resolve2({ action: "cancel" });
|
|
2352
|
+
};
|
|
2353
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2354
|
+
});
|
|
2355
|
+
}, []);
|
|
2328
2356
|
const clearInteractions = useCallback(() => {
|
|
2329
2357
|
for (const entry of [...interactionsRef.current]) entry.settle();
|
|
2330
2358
|
}, []);
|
|
@@ -2448,6 +2476,7 @@ function App({ config, onTurnStart, sessions: providedSessions, onCleanupReady }
|
|
|
2448
2476
|
logDir,
|
|
2449
2477
|
permissions: !config.permissions || config.permissions.mode === "ask" ? { mode: "ask", onRequest: requestPermission } : config.permissions,
|
|
2450
2478
|
questions: config.questions?.onRequest ? config.questions : { onRequest: requestQuestion },
|
|
2479
|
+
onCheckpoint: config.onCheckpoint ?? handleCheckpoint,
|
|
2451
2480
|
// A retry names the agents to redo; a continued conversation carries only
|
|
2452
2481
|
// sessions and still lets the leader route. Both arrive here.
|
|
2453
2482
|
...resume ? { resume } : {}
|
|
@@ -2503,7 +2532,15 @@ function App({ config, onTurnStart, sessions: providedSessions, onCleanupReady }
|
|
|
2503
2532
|
activeTurnRef.current = null;
|
|
2504
2533
|
}
|
|
2505
2534
|
},
|
|
2506
|
-
[
|
|
2535
|
+
[
|
|
2536
|
+
config,
|
|
2537
|
+
flush,
|
|
2538
|
+
onTurnStart,
|
|
2539
|
+
requestPermission,
|
|
2540
|
+
requestQuestion,
|
|
2541
|
+
handleCheckpoint,
|
|
2542
|
+
clearInteractions
|
|
2543
|
+
]
|
|
2507
2544
|
);
|
|
2508
2545
|
useEffect(() => {
|
|
2509
2546
|
if (running || busyRef.current || closingRef.current || queuePaused || awaitingAnswer || queue.length === 0)
|
|
@@ -2637,6 +2674,12 @@ function App({ config, onTurnStart, sessions: providedSessions, onCleanupReady }
|
|
|
2637
2674
|
const runCommand = useCallback(
|
|
2638
2675
|
(name, args, rawText) => {
|
|
2639
2676
|
setLine(EMPTY_LINE);
|
|
2677
|
+
if (checkpointResolveRef.current && name !== "continue" && name !== "modify" && name !== "cancel") {
|
|
2678
|
+
setNotice(
|
|
2679
|
+
`checkpoint: ${checkpointLabelRef.current} \xB7 /continue /modify <instruction> /cancel`
|
|
2680
|
+
);
|
|
2681
|
+
return;
|
|
2682
|
+
}
|
|
2640
2683
|
if (!canRun(name, running)) {
|
|
2641
2684
|
setNotice(unavailableReason(name, running));
|
|
2642
2685
|
return;
|
|
@@ -2777,7 +2820,15 @@ function App({ config, onTurnStart, sessions: providedSessions, onCleanupReady }
|
|
|
2777
2820
|
setQueuePaused(true);
|
|
2778
2821
|
setNotice("queue paused \u2014 the active turn continues");
|
|
2779
2822
|
return;
|
|
2780
|
-
case "continue":
|
|
2823
|
+
case "continue": {
|
|
2824
|
+
if (checkpointResolveRef.current) {
|
|
2825
|
+
const resolve2 = checkpointResolveRef.current;
|
|
2826
|
+
checkpointResolveRef.current = null;
|
|
2827
|
+
checkpointLabelRef.current = "";
|
|
2828
|
+
resolve2({ action: "continue" });
|
|
2829
|
+
setNotice("continuing\u2026");
|
|
2830
|
+
return;
|
|
2831
|
+
}
|
|
2781
2832
|
if (awaitingAnswerRef.current) {
|
|
2782
2833
|
setNotice("answer the leader first; queued input is not an answer");
|
|
2783
2834
|
return;
|
|
@@ -2786,6 +2837,24 @@ function App({ config, onTurnStart, sessions: providedSessions, onCleanupReady }
|
|
|
2786
2837
|
setQueuePaused(false);
|
|
2787
2838
|
setNotice("queue resumed");
|
|
2788
2839
|
return;
|
|
2840
|
+
}
|
|
2841
|
+
case "modify": {
|
|
2842
|
+
const instruction = args.join(" ");
|
|
2843
|
+
if (!instruction) {
|
|
2844
|
+
setNotice("usage: /modify <instruction> \u2014 only at a checkpoint");
|
|
2845
|
+
return;
|
|
2846
|
+
}
|
|
2847
|
+
if (!checkpointResolveRef.current) {
|
|
2848
|
+
setNotice("no checkpoint is waiting");
|
|
2849
|
+
return;
|
|
2850
|
+
}
|
|
2851
|
+
const resolve2 = checkpointResolveRef.current;
|
|
2852
|
+
checkpointResolveRef.current = null;
|
|
2853
|
+
checkpointLabelRef.current = "";
|
|
2854
|
+
resolve2({ action: "modify", instruction });
|
|
2855
|
+
setNotice("modifying and continuing\u2026");
|
|
2856
|
+
return;
|
|
2857
|
+
}
|
|
2789
2858
|
case "agents":
|
|
2790
2859
|
setNotice(
|
|
2791
2860
|
`team: ${plannedAgents(config).map((agent) => agent.id).join(", ")}`
|
|
@@ -2924,6 +2993,14 @@ function App({ config, onTurnStart, sessions: providedSessions, onCleanupReady }
|
|
|
2924
2993
|
setNotice("showing latest output");
|
|
2925
2994
|
return;
|
|
2926
2995
|
case "cancel": {
|
|
2996
|
+
if (checkpointResolveRef.current) {
|
|
2997
|
+
const resolve2 = checkpointResolveRef.current;
|
|
2998
|
+
checkpointResolveRef.current = null;
|
|
2999
|
+
checkpointLabelRef.current = "";
|
|
3000
|
+
resolve2({ action: "cancel" });
|
|
3001
|
+
setNotice("checkpoint cancelled");
|
|
3002
|
+
return;
|
|
3003
|
+
}
|
|
2927
3004
|
if (!abortRef.current) {
|
|
2928
3005
|
setNotice("nothing to cancel");
|
|
2929
3006
|
return;
|
|
@@ -2968,6 +3045,10 @@ function App({ config, onTurnStart, sessions: providedSessions, onCleanupReady }
|
|
|
2968
3045
|
runCommand(command.name, command.args, text);
|
|
2969
3046
|
return;
|
|
2970
3047
|
}
|
|
3048
|
+
if (checkpointResolveRef.current) {
|
|
3049
|
+
runCommand("modify", [text], text);
|
|
3050
|
+
return;
|
|
3051
|
+
}
|
|
2971
3052
|
setLine(EMPTY_LINE);
|
|
2972
3053
|
setNotice(void 0);
|
|
2973
3054
|
if (running || busyRef.current) {
|
|
@@ -3054,6 +3135,7 @@ function App({ config, onTurnStart, sessions: providedSessions, onCleanupReady }
|
|
|
3054
3135
|
return;
|
|
3055
3136
|
}
|
|
3056
3137
|
continuationRef.current = { turn: target.turn, sessions };
|
|
3138
|
+
dispatch({ type: "view:open", turn: target.turn });
|
|
3057
3139
|
setHistoryOpen(false);
|
|
3058
3140
|
setNotice(
|
|
3059
3141
|
`continuing turn ${target.turn} \u2014 ${count} ${count === 1 ? "session" : "sessions"} \xB7 type your next instruction`
|
|
@@ -3308,11 +3390,11 @@ function App({ config, onTurnStart, sessions: providedSessions, onCleanupReady }
|
|
|
3308
3390
|
const handlerRef = useRef(handleKey);
|
|
3309
3391
|
handlerRef.current = handleKey;
|
|
3310
3392
|
useInput((char, key) => handlerRef.current(char, key));
|
|
3311
|
-
const keys = helpOpen ? "esc close" : historyOpen ? "\u2191\u2193 select \xB7 enter open \xB7 esc close" : argOpen ? `\u2191\u2193 select \xB7 tab complete \xB7 enter ${arg?.command.name} \xB7 esc close` : menuOpen ? "\u2191\u2193 select \xB7 tab complete \xB7 enter run \xB7 esc close" : running ? interactions.length > 0 ? layout.interactionRows === 0 ? (
|
|
3393
|
+
const keys = helpOpen ? "esc close" : historyOpen ? "\u2191\u2193 select \xB7 enter open \xB7 esc close" : argOpen ? `\u2191\u2193 select \xB7 tab complete \xB7 enter ${arg?.command.name} \xB7 esc close` : menuOpen ? "\u2191\u2193 select \xB7 tab complete \xB7 enter run \xB7 esc close" : running ? checkpointResolveRef.current ? `/continue \xB7 /modify <instruction> \xB7 /cancel` : interactions.length > 0 ? layout.interactionRows === 0 ? (
|
|
3312
3394
|
// Too short to dock the bar, so the commands are the only way in.
|
|
3313
3395
|
"decide with /approve 1 \xB7 /deny \xB7 /respond"
|
|
3314
3396
|
) : interactions[0].kind === "question" ? "tab field \xB7 \u2191\u2193 pick \xB7 enter next \xB7 /dismiss" : menu.released ? "/approve 1 and /deny still reach it" : "\u2191\u2193 or 1-9 choose \xB7 enter confirm \xB7 esc to type" : "enter queues \xB7 ctrl+c cancel" : state.view ? "esc live \xB7 tab agent \xB7 ? help" : state.reply ? "enter sends \xB7 / commands \xB7 ? help" : "tab switch \xB7 / commands \xB7 ? help";
|
|
3315
|
-
const placeholder = frontQuestion !== void 0 ? formField === void 0 ? "enter to answer" : editingField !== void 0 ? `${editingField.title}${editingField.required ? " *" : ""} \u2014 enter for the next field` : `${formField.title} \u2014 \u2191\u2193 to pick \xB7 enter for the next field` : front?.kind === "permission" && !menu.released ? "choose above \xB7 esc to type here instead" : running ? RUNNING_PLACEHOLDER : awaitingAnswer ? "answer the leader\u2026 \xB7 /task starts a new task" : INPUT_PLACEHOLDER;
|
|
3397
|
+
const placeholder = frontQuestion !== void 0 ? formField === void 0 ? "enter to answer" : editingField !== void 0 ? `${editingField.title}${editingField.required ? " *" : ""} \u2014 enter for the next field` : `${formField.title} \u2014 \u2191\u2193 to pick \xB7 enter for the next field` : front?.kind === "permission" && !menu.released ? "choose above \xB7 esc to type here instead" : checkpointResolveRef.current ? `checkpoint: ${checkpointLabelRef.current} \u2014 type feedback or /continue` : running ? RUNNING_PLACEHOLDER : awaitingAnswer ? "answer the leader\u2026 \xB7 /task starts a new task" : INPUT_PLACEHOLDER;
|
|
3316
3398
|
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", height: layout.rows, children: [
|
|
3317
3399
|
/* @__PURE__ */ jsx6(
|
|
3318
3400
|
Header,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiclawcli/dashboard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./tui": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@multiclawcli/core": "0.
|
|
28
|
+
"@multiclawcli/core": "0.9.0",
|
|
29
29
|
"ink": "^8.0.0",
|
|
30
30
|
"react": "^19.3.1",
|
|
31
31
|
"string-width": "^8.3.0"
|