reasonix 0.36.0 → 0.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6894,6 +6894,10 @@ function handleToolStart(ev, ctx) {
6894
6894
  }
6895
6895
  }
6896
6896
  function handleErrorEvent(ev, ctx) {
6897
+ ctx.setOngoingTool(null);
6898
+ ctx.setToolProgress(null);
6899
+ ctx.toolStartedAtRef.current = null;
6900
+ ctx.translator.toolAbort(ev.error ?? ev.content);
6897
6901
  ctx.log.pushError("tool error", ev.error ?? ev.content);
6898
6902
  }
6899
6903
  function handleWarningEvent(ev, ctx) {
@@ -6935,272 +6939,6 @@ function handleToolEvent(ev, ctx) {
6935
6939
  }
6936
6940
  }
6937
6941
 
6938
- // src/cli/ui/hooks/useAgentSession.ts
6939
- import { useMemo as useMemo6 } from "react";
6940
- function useAgentSession({
6941
- sessionId,
6942
- model: model2,
6943
- workspace,
6944
- branch
6945
- }) {
6946
- return useMemo6(
6947
- () => ({
6948
- id: sessionId ?? "default",
6949
- branch: branch ?? "main",
6950
- workspace,
6951
- model: model2
6952
- }),
6953
- [sessionId, branch, workspace, model2]
6954
- );
6955
- }
6956
-
6957
- // src/cli/ui/hooks/useChatScroll.ts
6958
- import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef2, useState as useState14 } from "react";
6959
- var SCROLL_PAGE_ROWS = 3;
6960
- var COALESCE_MS = 16;
6961
- function useChatScroll() {
6962
- const [scrollRows, setScrollRows] = useState14(0);
6963
- const [pinned, setPinned] = useState14(true);
6964
- const [maxScroll, setMaxScrollState] = useState14(0);
6965
- const maxScrollRef = useRef2(0);
6966
- const pendingDelta = useRef2(0);
6967
- const flushTimer = useRef2(null);
6968
- const flush = useCallback2(() => {
6969
- flushTimer.current = null;
6970
- const d = pendingDelta.current;
6971
- pendingDelta.current = 0;
6972
- if (d === 0) return;
6973
- if (d < 0) setPinned(false);
6974
- setScrollRows((o) => {
6975
- const next = Math.max(0, Math.min(maxScrollRef.current, o + d));
6976
- if (next >= maxScrollRef.current) setPinned(true);
6977
- return next;
6978
- });
6979
- }, []);
6980
- const schedule = useCallback2(
6981
- (delta) => {
6982
- pendingDelta.current += delta;
6983
- if (flushTimer.current !== null) return;
6984
- flushTimer.current = setTimeout(flush, COALESCE_MS);
6985
- },
6986
- [flush]
6987
- );
6988
- useEffect4(() => {
6989
- return () => {
6990
- if (flushTimer.current !== null) {
6991
- clearTimeout(flushTimer.current);
6992
- flushTimer.current = null;
6993
- }
6994
- };
6995
- }, []);
6996
- useEffect4(() => {
6997
- if (pinned) setScrollRows(maxScroll);
6998
- }, [pinned, maxScroll]);
6999
- useEffect4(() => {
7000
- if (scrollRows > maxScroll) setScrollRows(maxScroll);
7001
- }, [scrollRows, maxScroll]);
7002
- const scrollUp = useCallback2(() => schedule(-SCROLL_PAGE_ROWS), [schedule]);
7003
- const scrollDown = useCallback2(() => schedule(SCROLL_PAGE_ROWS), [schedule]);
7004
- const jumpToBottom = useCallback2(() => {
7005
- pendingDelta.current = 0;
7006
- if (flushTimer.current !== null) {
7007
- clearTimeout(flushTimer.current);
7008
- flushTimer.current = null;
7009
- }
7010
- setPinned(true);
7011
- }, []);
7012
- const setMaxScroll = useCallback2((rows) => {
7013
- maxScrollRef.current = rows;
7014
- setMaxScrollState(rows);
7015
- }, []);
7016
- return { scrollRows, pinned, scrollUp, scrollDown, jumpToBottom, setMaxScroll };
7017
- }
7018
-
7019
- // src/cli/ui/hooks/useCodeMode.ts
7020
- import { useCallback as useCallback3 } from "react";
7021
- function useCodeMode(opts) {
7022
- const { codeMode, pendingEdits, currentRootDir, session, syncPendingCount, recordEdit } = opts;
7023
- const codeApply = useCallback3(
7024
- (indices) => {
7025
- if (!codeMode) return "not in code mode";
7026
- const blocks = pendingEdits.current;
7027
- if (blocks.length === 0) {
7028
- return "nothing pending \u2014 the model hasn't proposed edits since the last /apply or /discard.";
7029
- }
7030
- const useSubset = indices !== void 0 && indices.length > 0;
7031
- const { selected, remaining } = useSubset ? partitionEdits(blocks, indices) : { selected: blocks, remaining: [] };
7032
- if (selected.length === 0) {
7033
- return "\u25B8 no edits matched those indices \u2014 nothing applied. Use /apply with no args to commit them all.";
7034
- }
7035
- const snaps = snapshotBeforeEdits(selected, currentRootDir);
7036
- const results = applyEditBlocks(selected, currentRootDir);
7037
- const anyApplied = results.some((r) => r.status === "applied" || r.status === "created");
7038
- if (anyApplied) recordEdit("review-apply", selected, results, snaps);
7039
- pendingEdits.current = remaining;
7040
- if (remaining.length === 0) clearPendingEdits(session ?? null);
7041
- else savePendingEdits(session ?? null, remaining);
7042
- syncPendingCount();
7043
- const tail = remaining.length > 0 ? `
7044
- \u25B8 ${remaining.length} edit block(s) still pending \u2014 /apply or /discard to clear them.` : "";
7045
- return formatEditResults(results) + tail;
7046
- },
7047
- [codeMode, currentRootDir, session, syncPendingCount, recordEdit, pendingEdits]
7048
- );
7049
- const codeDiscard = useCallback3(
7050
- (indices) => {
7051
- const blocks = pendingEdits.current;
7052
- if (blocks.length === 0) return "nothing pending to discard.";
7053
- const useSubset = indices !== void 0 && indices.length > 0;
7054
- const { selected, remaining } = useSubset ? partitionEdits(blocks, indices) : { selected: blocks, remaining: [] };
7055
- if (selected.length === 0) {
7056
- return "\u25B8 no edits matched those indices \u2014 nothing discarded.";
7057
- }
7058
- pendingEdits.current = remaining;
7059
- if (remaining.length === 0) clearPendingEdits(session ?? null);
7060
- else savePendingEdits(session ?? null, remaining);
7061
- syncPendingCount();
7062
- const tail = remaining.length > 0 ? ` (${remaining.length} block(s) still pending)` : ". Nothing was written to disk.";
7063
- return `\u25B8 discarded ${selected.length} pending edit block(s)${tail}`;
7064
- },
7065
- [session, syncPendingCount, pendingEdits]
7066
- );
7067
- return { codeApply, codeDiscard };
7068
- }
7069
-
7070
- // src/cli/ui/hooks/useInputRecall.ts
7071
- import { useCallback as useCallback4, useRef as useRef3 } from "react";
7072
- function useInputRecall(setInput) {
7073
- const promptHistory = useRef3([]);
7074
- const historyCursor = useRef3(-1);
7075
- const recallPrev = useCallback4(() => {
7076
- const hist = promptHistory.current;
7077
- if (hist.length === 0) return;
7078
- const nextCursor = Math.min(historyCursor.current + 1, hist.length - 1);
7079
- historyCursor.current = nextCursor;
7080
- setInput(hist[hist.length - 1 - nextCursor] ?? "");
7081
- }, [setInput]);
7082
- const recallNext = useCallback4(() => {
7083
- if (historyCursor.current < 0) return;
7084
- const hist = promptHistory.current;
7085
- const nextCursor = historyCursor.current - 1;
7086
- historyCursor.current = nextCursor;
7087
- setInput(nextCursor < 0 ? "" : hist[hist.length - 1 - nextCursor] ?? "");
7088
- }, [setInput]);
7089
- const pushHistory = useCallback4((text) => {
7090
- promptHistory.current.push(text);
7091
- }, []);
7092
- const resetCursor = useCallback4(() => {
7093
- historyCursor.current = -1;
7094
- }, []);
7095
- return { recallPrev, recallNext, pushHistory, resetCursor };
7096
- }
7097
-
7098
- // src/cli/ui/hooks/useLoopMode.ts
7099
- import { useCallback as useCallback5, useEffect as useEffect5, useRef as useRef4, useState as useState15 } from "react";
7100
- function useLoopMode(opts) {
7101
- const { log, busyRef, handleSubmitRef } = opts;
7102
- const [activeLoop, setActiveLoop] = useState15(null);
7103
- const activeLoopRef = useRef4(null);
7104
- const loopTimerRef = useRef4(null);
7105
- const loopFiringRef = useRef4(false);
7106
- useEffect5(() => {
7107
- activeLoopRef.current = activeLoop;
7108
- }, [activeLoop]);
7109
- const stopLoop = useCallback5(() => {
7110
- if (loopTimerRef.current) {
7111
- clearTimeout(loopTimerRef.current);
7112
- loopTimerRef.current = null;
7113
- }
7114
- const cur = activeLoopRef.current;
7115
- if (!cur) return;
7116
- setActiveLoop(null);
7117
- log.pushInfo(`\u25B8 loop stopped (after ${cur.iter} iter${cur.iter === 1 ? "" : "s"}).`);
7118
- }, [log]);
7119
- const startLoop = useCallback5((intervalMs, prompt) => {
7120
- if (loopTimerRef.current) {
7121
- clearTimeout(loopTimerRef.current);
7122
- loopTimerRef.current = null;
7123
- }
7124
- setActiveLoop({
7125
- prompt,
7126
- intervalMs,
7127
- nextFireAt: Date.now() + intervalMs,
7128
- iter: 0
7129
- });
7130
- }, []);
7131
- const getLoopStatus = useCallback5(() => {
7132
- const cur = activeLoopRef.current;
7133
- if (!cur) return null;
7134
- return {
7135
- prompt: cur.prompt,
7136
- intervalMs: cur.intervalMs,
7137
- iter: cur.iter,
7138
- nextFireMs: Math.max(0, cur.nextFireAt - Date.now())
7139
- };
7140
- }, []);
7141
- const isLoopActive = useCallback5(() => activeLoopRef.current !== null, []);
7142
- const isLoopFiring = useCallback5(() => loopFiringRef.current, []);
7143
- const clearFiringFlag = useCallback5(() => {
7144
- loopFiringRef.current = false;
7145
- }, []);
7146
- useEffect5(() => {
7147
- if (!activeLoop) return;
7148
- const delay = Math.max(0, activeLoop.nextFireAt - Date.now());
7149
- const timer = setTimeout(async () => {
7150
- loopTimerRef.current = null;
7151
- if (busyRef.current) {
7152
- setActiveLoop((cur2) => cur2 ? { ...cur2, nextFireAt: Date.now() + 1e3 } : cur2);
7153
- return;
7154
- }
7155
- const cur = activeLoopRef.current;
7156
- if (!cur) return;
7157
- const nextIter = cur.iter + 1;
7158
- setActiveLoop(
7159
- (c) => c ? { ...c, iter: nextIter, nextFireAt: Date.now() + cur.intervalMs } : c
7160
- );
7161
- log.pushInfo(`\u25B8 /loop iter ${nextIter} \u2192 ${cur.prompt}`);
7162
- loopFiringRef.current = true;
7163
- try {
7164
- await handleSubmitRef.current?.(cur.prompt);
7165
- } catch {
7166
- stopLoop();
7167
- } finally {
7168
- loopFiringRef.current = false;
7169
- }
7170
- }, delay);
7171
- loopTimerRef.current = timer;
7172
- return () => clearTimeout(timer);
7173
- }, [activeLoop, stopLoop, log, busyRef, handleSubmitRef]);
7174
- return {
7175
- startLoop,
7176
- stopLoop,
7177
- getLoopStatus,
7178
- isLoopActive,
7179
- isLoopFiring,
7180
- clearFiringFlag,
7181
- activeLoop
7182
- };
7183
- }
7184
-
7185
- // src/cli/ui/hooks/useQuit.ts
7186
- import { useCallback as useCallback6, useEffect as useEffect6 } from "react";
7187
- function useQuit(transcriptRef) {
7188
- const quitProcess = useCallback6(() => {
7189
- transcriptRef.current?.end();
7190
- process.exit(0);
7191
- }, [transcriptRef]);
7192
- useEffect6(() => {
7193
- process.on("SIGINT", quitProcess);
7194
- return () => {
7195
- process.off("SIGINT", quitProcess);
7196
- };
7197
- }, [quitProcess]);
7198
- return quitProcess;
7199
- }
7200
-
7201
- // src/cli/ui/hooks/useScrollback.ts
7202
- import { useMemo as useMemo7 } from "react";
7203
-
7204
6942
  // src/cli/ui/state/provider.tsx
7205
6943
  import React29 from "react";
7206
6944
 
@@ -7536,62 +7274,337 @@ function initialState(session, cards = []) {
7536
7274
  };
7537
7275
  }
7538
7276
 
7539
- // src/cli/ui/state/store.ts
7540
- function createStore(session, initialCards) {
7541
- let state = initialState(session, initialCards);
7542
- const stateListeners = /* @__PURE__ */ new Set();
7543
- const eventListeners = /* @__PURE__ */ new Set();
7544
- return {
7545
- getState() {
7546
- return state;
7547
- },
7548
- dispatch(event) {
7549
- state = reduce(state, event);
7550
- for (const listener of stateListeners) listener();
7551
- for (const listener of eventListeners) listener(event);
7277
+ // src/cli/ui/state/store.ts
7278
+ function createStore(session, initialCards) {
7279
+ let state = initialState(session, initialCards);
7280
+ const stateListeners = /* @__PURE__ */ new Set();
7281
+ const eventListeners = /* @__PURE__ */ new Set();
7282
+ return {
7283
+ getState() {
7284
+ return state;
7285
+ },
7286
+ dispatch(event) {
7287
+ state = reduce(state, event);
7288
+ for (const listener of stateListeners) listener();
7289
+ for (const listener of eventListeners) listener(event);
7290
+ },
7291
+ subscribe(listener) {
7292
+ stateListeners.add(listener);
7293
+ return () => {
7294
+ stateListeners.delete(listener);
7295
+ };
7296
+ },
7297
+ onEvent(listener) {
7298
+ eventListeners.add(listener);
7299
+ return () => {
7300
+ eventListeners.delete(listener);
7301
+ };
7302
+ }
7303
+ };
7304
+ }
7305
+
7306
+ // src/cli/ui/state/provider.tsx
7307
+ var StoreCtx = React29.createContext(null);
7308
+ function AgentStoreProvider({
7309
+ session,
7310
+ initialCards,
7311
+ children
7312
+ }) {
7313
+ const initialCardsRef = React29.useRef(initialCards);
7314
+ const store = React29.useMemo(() => createStore(session, initialCardsRef.current), [session]);
7315
+ return /* @__PURE__ */ React29.createElement(StoreCtx.Provider, { value: store }, children);
7316
+ }
7317
+ function useAgentStore() {
7318
+ const store = React29.useContext(StoreCtx);
7319
+ if (!store) throw new Error("useAgentStore must be used inside AgentStoreProvider");
7320
+ return store;
7321
+ }
7322
+ function useAgentState(selector) {
7323
+ const store = useAgentStore();
7324
+ const subscribe = React29.useCallback((cb) => store.subscribe(cb), [store]);
7325
+ const getSnapshot = React29.useCallback(() => selector(store.getState()), [store, selector]);
7326
+ return React29.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
7327
+ }
7328
+ function useDispatch() {
7329
+ return useAgentStore().dispatch;
7330
+ }
7331
+
7332
+ // src/cli/ui/hooks/useActivityPhase.ts
7333
+ function deriveActivityLabel(cards) {
7334
+ if (cards.some((c) => c.kind === "reasoning" && c.streaming)) return "thinking\u2026";
7335
+ const last = cards[cards.length - 1];
7336
+ if (!last || last.kind === "user") return "waiting for model\u2026";
7337
+ return "processing\u2026";
7338
+ }
7339
+ function useActivityLabel() {
7340
+ return useAgentState((s) => deriveActivityLabel(s.cards));
7341
+ }
7342
+
7343
+ // src/cli/ui/hooks/useAgentSession.ts
7344
+ import { useMemo as useMemo6 } from "react";
7345
+ function useAgentSession({
7346
+ sessionId,
7347
+ model: model2,
7348
+ workspace,
7349
+ branch
7350
+ }) {
7351
+ return useMemo6(
7352
+ () => ({
7353
+ id: sessionId ?? "default",
7354
+ branch: branch ?? "main",
7355
+ workspace,
7356
+ model: model2
7357
+ }),
7358
+ [sessionId, branch, workspace, model2]
7359
+ );
7360
+ }
7361
+
7362
+ // src/cli/ui/hooks/useChatScroll.ts
7363
+ import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef2, useState as useState14 } from "react";
7364
+ var SCROLL_PAGE_ROWS = 3;
7365
+ var COALESCE_MS = 16;
7366
+ function useChatScroll() {
7367
+ const [scrollRows, setScrollRows] = useState14(0);
7368
+ const [pinned, setPinned] = useState14(true);
7369
+ const [maxScroll, setMaxScrollState] = useState14(0);
7370
+ const maxScrollRef = useRef2(0);
7371
+ const pendingDelta = useRef2(0);
7372
+ const flushTimer = useRef2(null);
7373
+ const flush = useCallback2(() => {
7374
+ flushTimer.current = null;
7375
+ const d = pendingDelta.current;
7376
+ pendingDelta.current = 0;
7377
+ if (d === 0) return;
7378
+ if (d < 0) setPinned(false);
7379
+ setScrollRows((o) => {
7380
+ const next = Math.max(0, Math.min(maxScrollRef.current, o + d));
7381
+ if (next >= maxScrollRef.current) setPinned(true);
7382
+ return next;
7383
+ });
7384
+ }, []);
7385
+ const schedule = useCallback2(
7386
+ (delta) => {
7387
+ pendingDelta.current += delta;
7388
+ if (flushTimer.current !== null) return;
7389
+ flushTimer.current = setTimeout(flush, COALESCE_MS);
7390
+ },
7391
+ [flush]
7392
+ );
7393
+ useEffect4(() => {
7394
+ return () => {
7395
+ if (flushTimer.current !== null) {
7396
+ clearTimeout(flushTimer.current);
7397
+ flushTimer.current = null;
7398
+ }
7399
+ };
7400
+ }, []);
7401
+ useEffect4(() => {
7402
+ if (pinned) setScrollRows(maxScroll);
7403
+ }, [pinned, maxScroll]);
7404
+ useEffect4(() => {
7405
+ if (scrollRows > maxScroll) setScrollRows(maxScroll);
7406
+ }, [scrollRows, maxScroll]);
7407
+ const scrollUp = useCallback2(() => schedule(-SCROLL_PAGE_ROWS), [schedule]);
7408
+ const scrollDown = useCallback2(() => schedule(SCROLL_PAGE_ROWS), [schedule]);
7409
+ const jumpToBottom = useCallback2(() => {
7410
+ pendingDelta.current = 0;
7411
+ if (flushTimer.current !== null) {
7412
+ clearTimeout(flushTimer.current);
7413
+ flushTimer.current = null;
7414
+ }
7415
+ setPinned(true);
7416
+ }, []);
7417
+ const setMaxScroll = useCallback2((rows) => {
7418
+ maxScrollRef.current = rows;
7419
+ setMaxScrollState(rows);
7420
+ }, []);
7421
+ return { scrollRows, pinned, scrollUp, scrollDown, jumpToBottom, setMaxScroll };
7422
+ }
7423
+
7424
+ // src/cli/ui/hooks/useCodeMode.ts
7425
+ import { useCallback as useCallback3 } from "react";
7426
+ function useCodeMode(opts) {
7427
+ const { codeMode, pendingEdits, currentRootDir, session, syncPendingCount, recordEdit } = opts;
7428
+ const codeApply = useCallback3(
7429
+ (indices) => {
7430
+ if (!codeMode) return "not in code mode";
7431
+ const blocks = pendingEdits.current;
7432
+ if (blocks.length === 0) {
7433
+ return "nothing pending \u2014 the model hasn't proposed edits since the last /apply or /discard.";
7434
+ }
7435
+ const useSubset = indices !== void 0 && indices.length > 0;
7436
+ const { selected, remaining } = useSubset ? partitionEdits(blocks, indices) : { selected: blocks, remaining: [] };
7437
+ if (selected.length === 0) {
7438
+ return "\u25B8 no edits matched those indices \u2014 nothing applied. Use /apply with no args to commit them all.";
7439
+ }
7440
+ const snaps = snapshotBeforeEdits(selected, currentRootDir);
7441
+ const results = applyEditBlocks(selected, currentRootDir);
7442
+ const anyApplied = results.some((r) => r.status === "applied" || r.status === "created");
7443
+ if (anyApplied) recordEdit("review-apply", selected, results, snaps);
7444
+ pendingEdits.current = remaining;
7445
+ if (remaining.length === 0) clearPendingEdits(session ?? null);
7446
+ else savePendingEdits(session ?? null, remaining);
7447
+ syncPendingCount();
7448
+ const tail = remaining.length > 0 ? `
7449
+ \u25B8 ${remaining.length} edit block(s) still pending \u2014 /apply or /discard to clear them.` : "";
7450
+ return formatEditResults(results) + tail;
7552
7451
  },
7553
- subscribe(listener) {
7554
- stateListeners.add(listener);
7555
- return () => {
7556
- stateListeners.delete(listener);
7557
- };
7452
+ [codeMode, currentRootDir, session, syncPendingCount, recordEdit, pendingEdits]
7453
+ );
7454
+ const codeDiscard = useCallback3(
7455
+ (indices) => {
7456
+ const blocks = pendingEdits.current;
7457
+ if (blocks.length === 0) return "nothing pending to discard.";
7458
+ const useSubset = indices !== void 0 && indices.length > 0;
7459
+ const { selected, remaining } = useSubset ? partitionEdits(blocks, indices) : { selected: blocks, remaining: [] };
7460
+ if (selected.length === 0) {
7461
+ return "\u25B8 no edits matched those indices \u2014 nothing discarded.";
7462
+ }
7463
+ pendingEdits.current = remaining;
7464
+ if (remaining.length === 0) clearPendingEdits(session ?? null);
7465
+ else savePendingEdits(session ?? null, remaining);
7466
+ syncPendingCount();
7467
+ const tail = remaining.length > 0 ? ` (${remaining.length} block(s) still pending)` : ". Nothing was written to disk.";
7468
+ return `\u25B8 discarded ${selected.length} pending edit block(s)${tail}`;
7558
7469
  },
7559
- onEvent(listener) {
7560
- eventListeners.add(listener);
7561
- return () => {
7562
- eventListeners.delete(listener);
7563
- };
7564
- }
7565
- };
7470
+ [session, syncPendingCount, pendingEdits]
7471
+ );
7472
+ return { codeApply, codeDiscard };
7566
7473
  }
7567
7474
 
7568
- // src/cli/ui/state/provider.tsx
7569
- var StoreCtx = React29.createContext(null);
7570
- function AgentStoreProvider({
7571
- session,
7572
- initialCards,
7573
- children
7574
- }) {
7575
- const initialCardsRef = React29.useRef(initialCards);
7576
- const store = React29.useMemo(() => createStore(session, initialCardsRef.current), [session]);
7577
- return /* @__PURE__ */ React29.createElement(StoreCtx.Provider, { value: store }, children);
7578
- }
7579
- function useAgentStore() {
7580
- const store = React29.useContext(StoreCtx);
7581
- if (!store) throw new Error("useAgentStore must be used inside AgentStoreProvider");
7582
- return store;
7475
+ // src/cli/ui/hooks/useInputRecall.ts
7476
+ import { useCallback as useCallback4, useRef as useRef3 } from "react";
7477
+ function useInputRecall(setInput) {
7478
+ const promptHistory = useRef3([]);
7479
+ const historyCursor = useRef3(-1);
7480
+ const recallPrev = useCallback4(() => {
7481
+ const hist = promptHistory.current;
7482
+ if (hist.length === 0) return;
7483
+ const nextCursor = Math.min(historyCursor.current + 1, hist.length - 1);
7484
+ historyCursor.current = nextCursor;
7485
+ setInput(hist[hist.length - 1 - nextCursor] ?? "");
7486
+ }, [setInput]);
7487
+ const recallNext = useCallback4(() => {
7488
+ if (historyCursor.current < 0) return;
7489
+ const hist = promptHistory.current;
7490
+ const nextCursor = historyCursor.current - 1;
7491
+ historyCursor.current = nextCursor;
7492
+ setInput(nextCursor < 0 ? "" : hist[hist.length - 1 - nextCursor] ?? "");
7493
+ }, [setInput]);
7494
+ const pushHistory = useCallback4((text) => {
7495
+ promptHistory.current.push(text);
7496
+ }, []);
7497
+ const resetCursor = useCallback4(() => {
7498
+ historyCursor.current = -1;
7499
+ }, []);
7500
+ return { recallPrev, recallNext, pushHistory, resetCursor };
7583
7501
  }
7584
- function useAgentState(selector) {
7585
- const store = useAgentStore();
7586
- const subscribe = React29.useCallback((cb) => store.subscribe(cb), [store]);
7587
- const getSnapshot = React29.useCallback(() => selector(store.getState()), [store, selector]);
7588
- return React29.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
7502
+
7503
+ // src/cli/ui/hooks/useLoopMode.ts
7504
+ import { useCallback as useCallback5, useEffect as useEffect5, useRef as useRef4, useState as useState15 } from "react";
7505
+ function useLoopMode(opts) {
7506
+ const { log, busyRef, handleSubmitRef } = opts;
7507
+ const [activeLoop, setActiveLoop] = useState15(null);
7508
+ const activeLoopRef = useRef4(null);
7509
+ const loopTimerRef = useRef4(null);
7510
+ const loopFiringRef = useRef4(false);
7511
+ useEffect5(() => {
7512
+ activeLoopRef.current = activeLoop;
7513
+ }, [activeLoop]);
7514
+ const stopLoop = useCallback5(() => {
7515
+ if (loopTimerRef.current) {
7516
+ clearTimeout(loopTimerRef.current);
7517
+ loopTimerRef.current = null;
7518
+ }
7519
+ const cur = activeLoopRef.current;
7520
+ if (!cur) return;
7521
+ setActiveLoop(null);
7522
+ log.pushInfo(`\u25B8 loop stopped (after ${cur.iter} iter${cur.iter === 1 ? "" : "s"}).`);
7523
+ }, [log]);
7524
+ const startLoop = useCallback5((intervalMs, prompt) => {
7525
+ if (loopTimerRef.current) {
7526
+ clearTimeout(loopTimerRef.current);
7527
+ loopTimerRef.current = null;
7528
+ }
7529
+ setActiveLoop({
7530
+ prompt,
7531
+ intervalMs,
7532
+ nextFireAt: Date.now() + intervalMs,
7533
+ iter: 0
7534
+ });
7535
+ }, []);
7536
+ const getLoopStatus = useCallback5(() => {
7537
+ const cur = activeLoopRef.current;
7538
+ if (!cur) return null;
7539
+ return {
7540
+ prompt: cur.prompt,
7541
+ intervalMs: cur.intervalMs,
7542
+ iter: cur.iter,
7543
+ nextFireMs: Math.max(0, cur.nextFireAt - Date.now())
7544
+ };
7545
+ }, []);
7546
+ const isLoopActive = useCallback5(() => activeLoopRef.current !== null, []);
7547
+ const isLoopFiring = useCallback5(() => loopFiringRef.current, []);
7548
+ const clearFiringFlag = useCallback5(() => {
7549
+ loopFiringRef.current = false;
7550
+ }, []);
7551
+ useEffect5(() => {
7552
+ if (!activeLoop) return;
7553
+ const delay = Math.max(0, activeLoop.nextFireAt - Date.now());
7554
+ const timer = setTimeout(async () => {
7555
+ loopTimerRef.current = null;
7556
+ if (busyRef.current) {
7557
+ setActiveLoop((cur2) => cur2 ? { ...cur2, nextFireAt: Date.now() + 1e3 } : cur2);
7558
+ return;
7559
+ }
7560
+ const cur = activeLoopRef.current;
7561
+ if (!cur) return;
7562
+ const nextIter = cur.iter + 1;
7563
+ setActiveLoop(
7564
+ (c) => c ? { ...c, iter: nextIter, nextFireAt: Date.now() + cur.intervalMs } : c
7565
+ );
7566
+ log.pushInfo(`\u25B8 /loop iter ${nextIter} \u2192 ${cur.prompt}`);
7567
+ loopFiringRef.current = true;
7568
+ try {
7569
+ await handleSubmitRef.current?.(cur.prompt);
7570
+ } catch {
7571
+ stopLoop();
7572
+ } finally {
7573
+ loopFiringRef.current = false;
7574
+ }
7575
+ }, delay);
7576
+ loopTimerRef.current = timer;
7577
+ return () => clearTimeout(timer);
7578
+ }, [activeLoop, stopLoop, log, busyRef, handleSubmitRef]);
7579
+ return {
7580
+ startLoop,
7581
+ stopLoop,
7582
+ getLoopStatus,
7583
+ isLoopActive,
7584
+ isLoopFiring,
7585
+ clearFiringFlag,
7586
+ activeLoop
7587
+ };
7589
7588
  }
7590
- function useDispatch() {
7591
- return useAgentStore().dispatch;
7589
+
7590
+ // src/cli/ui/hooks/useQuit.ts
7591
+ import { useCallback as useCallback6, useEffect as useEffect6 } from "react";
7592
+ function useQuit(transcriptRef) {
7593
+ const quitProcess = useCallback6(() => {
7594
+ transcriptRef.current?.end();
7595
+ process.exit(0);
7596
+ }, [transcriptRef]);
7597
+ useEffect6(() => {
7598
+ process.on("SIGINT", quitProcess);
7599
+ return () => {
7600
+ process.off("SIGINT", quitProcess);
7601
+ };
7602
+ }, [quitProcess]);
7603
+ return quitProcess;
7592
7604
  }
7593
7605
 
7594
7606
  // src/cli/ui/hooks/useScrollback.ts
7607
+ import { useMemo as useMemo7 } from "react";
7595
7608
  var seq = 0;
7596
7609
  function nextId2(prefix) {
7597
7610
  seq += 1;
@@ -11921,6 +11934,16 @@ var TurnTranslator = class {
11921
11934
  this.toolCardId = null;
11922
11935
  }
11923
11936
  }
11937
+ toolAbort(output) {
11938
+ if (this.toolCardId) {
11939
+ this.log.endTool(this.toolCardId, {
11940
+ output,
11941
+ elapsedMs: Date.now() - this.toolStartedAt,
11942
+ aborted: true
11943
+ });
11944
+ this.toolCardId = null;
11945
+ }
11946
+ }
11924
11947
  toolRetry(attempt, max) {
11925
11948
  if (this.toolCardId) this.log.retryTool(this.toolCardId, attempt, max);
11926
11949
  }
@@ -12859,6 +12882,7 @@ function AppInner({
12859
12882
  (s) => s.cards.some((c) => c.kind === "user" || c.kind === "streaming")
12860
12883
  );
12861
12884
  const isStreaming = useAgentState((s) => s.cards.some((c) => c.kind === "streaming" && !c.done));
12885
+ const activityLabel = useActivityLabel();
12862
12886
  const chatScroll = useChatScroll();
12863
12887
  const [input, setInput] = useState20("");
12864
12888
  const [busy, setBusy] = useState20(false);
@@ -13906,7 +13930,7 @@ function AppInner({
13906
13930
  }
13907
13931
  if (text.startsWith("/") && !text.includes(" ")) {
13908
13932
  const typed = text.slice(1).toLowerCase();
13909
- const matches = suggestSlashCommands(typed, !!codeMode);
13933
+ const matches = suggestSlashCommands(typed, !!codeMode, slashUsage);
13910
13934
  const exact = matches.find((m) => m.cmd === typed);
13911
13935
  if (!exact && matches.length > 0) {
13912
13936
  const chosen = matches[slashSelected] ?? matches[0];
@@ -14346,7 +14370,13 @@ function AppInner({
14346
14370
  codeModeOn: !!codeMode
14347
14371
  });
14348
14372
  } else if (ev.role === "error") {
14349
- handleErrorEvent(ev, { log });
14373
+ handleErrorEvent(ev, {
14374
+ log,
14375
+ setOngoingTool,
14376
+ setToolProgress,
14377
+ toolStartedAtRef,
14378
+ translator
14379
+ });
14350
14380
  } else if (ev.role === "warning") {
14351
14381
  handleWarningEvent(ev, { log, setTurnOnPro });
14352
14382
  }
@@ -14400,6 +14430,7 @@ function AppInner({
14400
14430
  planMode,
14401
14431
  session,
14402
14432
  slashSelected,
14433
+ slashUsage,
14403
14434
  atState,
14404
14435
  atSelected,
14405
14436
  pickAtMention,
@@ -14859,7 +14890,7 @@ function AppInner({
14859
14890
  dashboardUrl,
14860
14891
  languageVersion
14861
14892
  }
14862
- ) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && ongoingTool ? /* @__PURE__ */ React60.createElement(OngoingToolRow, { tool: ongoingTool, progress: toolProgress }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && subagentActivities.length > 0 ? /* @__PURE__ */ React60.createElement(SubagentLiveStack, { activities: subagentActivities, max: 3 }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && !ongoingTool && statusLine ? /* @__PURE__ */ React60.createElement(ThinkingRow, { text: statusLine }) : null, !PLAIN_UI && undoBanner && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && !pendingChoice && !stagedChoiceCustom && !pendingRevision && !stagedCheckpointRevise && !pendingCheckpoint ? /* @__PURE__ */ React60.createElement(UndoBanner, { banner: undoBanner }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && busy && !isStreaming && !ongoingTool && !statusLine ? /* @__PURE__ */ React60.createElement(ThinkingRow, { text: "processing\u2026" }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview ? /* @__PURE__ */ React60.createElement(PlanLiveRow, null) : null, /* @__PURE__ */ React60.createElement(ToastRail, null)), stagedInput ? /* @__PURE__ */ React60.createElement(
14893
+ ) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && ongoingTool ? /* @__PURE__ */ React60.createElement(OngoingToolRow, { tool: ongoingTool, progress: toolProgress }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && subagentActivities.length > 0 ? /* @__PURE__ */ React60.createElement(SubagentLiveStack, { activities: subagentActivities, max: 3 }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && !ongoingTool && statusLine ? /* @__PURE__ */ React60.createElement(ThinkingRow, { text: statusLine }) : null, !PLAIN_UI && undoBanner && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && !pendingChoice && !stagedChoiceCustom && !pendingRevision && !stagedCheckpointRevise && !pendingCheckpoint ? /* @__PURE__ */ React60.createElement(UndoBanner, { banner: undoBanner }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && busy && !isStreaming && !ongoingTool && !statusLine ? /* @__PURE__ */ React60.createElement(ThinkingRow, { text: activityLabel }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview ? /* @__PURE__ */ React60.createElement(PlanLiveRow, null) : null, /* @__PURE__ */ React60.createElement(ToastRail, null)), stagedInput ? /* @__PURE__ */ React60.createElement(
14863
14894
  PlanRefineInput,
14864
14895
  {
14865
14896
  mode: stagedInput.mode,
@@ -15600,4 +15631,4 @@ async function chatCommand(opts) {
15600
15631
  export {
15601
15632
  chatCommand
15602
15633
  };
15603
- //# sourceMappingURL=chunk-EN4LAZW5.js.map
15634
+ //# sourceMappingURL=chunk-3OBWN2NH.js.map