@multiclawcli/dashboard 0.8.2 → 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.
Files changed (2) hide show
  1. package/dist/tui.js +87 -5
  2. 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
- { name: "continue", description: "Resume queued turns after a checkpoint", availability: "idle" },
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
- [config, flush, onTurnStart, requestPermission, requestQuestion, clearInteractions]
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.8.2",
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.8.2",
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"