agentgui 1.0.1008 → 1.0.1009

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.1008",
3
+ "version": "1.0.1009",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "electron/main.js",
@@ -1945,11 +1945,12 @@ function chatMain() {
1945
1945
  children: [Spinner({ key: 'trspin', size: 'sm' })] }));
1946
1946
  }
1947
1947
  if (state.chat.confirmingEdit) {
1948
- banners.push(Alert({ key: 'confedit', kind: 'warn', title: 'Edit this message?',
1948
+ const isRetry = state.chat.confirmingEdit.kind === 'retry';
1949
+ banners.push(Alert({ key: 'confedit', kind: 'warn', title: isRetry ? 'Retry this turn?' : 'Edit this message?',
1949
1950
  children: [
1950
- h('span', { key: 'cetext' }, 'Editing will remove the later turns - continue? '),
1951
+ h('span', { key: 'cetext' }, (isRetry ? 'Retrying' : 'Editing') + ' will remove the later turns - continue? '),
1951
1952
  Btn({ key: 'ceno', onClick: cancelEditAndResend, children: 'cancel' }),
1952
- Btn({ key: 'ceyes', danger: true, onClick: confirmEditAndResend, children: 'continue' })] }));
1953
+ Btn({ key: 'ceyes', danger: true, onClick: confirmEditAndResend, children: isRetry ? 'retry' : 'continue' })] }));
1953
1954
  }
1954
1955
  const lastMsg = state.chat.messages.length ? state.chat.messages[state.chat.messages.length - 1] : null;
1955
1956
  const lastErr = lastMsg ? lastMsg.error : null;
@@ -2041,7 +2042,10 @@ function chatMain() {
2041
2042
  // edit-and-resend a user message (two-step: arm a confirm banner first,
2042
2043
  // since the truncation destroys the later turns).
2043
2044
  onCopyMessage: (m) => copyMessageText(m),
2044
- onRetryMessage: () => retryLastTurn(),
2045
+ // Mid-thread retry (gui-completion #7): the kit now offers retry on
2046
+ // EVERY assistant turn, not just the last - pass the specific message
2047
+ // through so retryTurn truncates from the right position.
2048
+ onRetryMessage: (m) => retryTurn(m),
2045
2049
  confirmEdit: true,
2046
2050
  onArmEdit: (m) => armEditAndResend(m),
2047
2051
  onEditMessage: (m) => armEditAndResend(m),
@@ -2334,28 +2338,56 @@ function chatFollowups() {
2334
2338
  return result;
2335
2339
  }
2336
2340
  // Retry the last assistant turn: drop it and re-send the preceding user message.
2337
- function retryLastTurn() {
2338
- if (!canSend() && !state.chat.busy) { /* allow when idle */ }
2341
+ // Retry a turn: truncates the transcript back to (and resends) the user
2342
+ // message that preceded the given assistant message, discarding it and
2343
+ // everything after - the same truncate+resend mechanism edit-and-resend
2344
+ // uses. `m` is optional (falls back to the trailing assistant turn) so this
2345
+ // also serves as "retry last turn" for onRetryMessage on a dangling user
2346
+ // message with no reply yet. Mid-thread retry (any assistant turn, not just
2347
+ // the last) was a gui-completion #7 finding - the kit's per-message retry
2348
+ // action is no longer gated to the last message index for assistant turns.
2349
+ function retryTurn(m) {
2339
2350
  if (state.chat.busy) return;
2340
- // The armed edit confirm refers to indices this retry is about to shift.
2341
- state.chat.confirmingEdit = null;
2342
2351
  const msgs = state.chat.messages;
2343
- // Find the trailing assistant turn and the user message before it.
2344
- let ai = msgs.length - 1;
2345
- while (ai >= 0 && msgs[ai].role !== 'assistant') ai--;
2352
+ let ai = m ? msgs.indexOf(m) : -1;
2353
+ if (ai < 0) {
2354
+ // Fall back to the trailing assistant turn (no specific message given,
2355
+ // or the given message somehow isn't in the current transcript).
2356
+ ai = msgs.length - 1;
2357
+ while (ai >= 0 && msgs[ai].role !== 'assistant') ai--;
2358
+ }
2346
2359
  if (ai < 0) return;
2347
2360
  let ui = ai - 1;
2348
2361
  while (ui >= 0 && msgs[ui].role !== 'user') ui--;
2349
2362
  if (ui < 0) return;
2363
+ // Retrying anything before the trailing turn silently discards every LATER
2364
+ // turn too - the same destructive-truncation risk edit-and-resend already
2365
+ // confirms for, so mid-thread retry gets the identical arm-then-confirm
2366
+ // banner instead of firing immediately.
2367
+ const discardsLaterTurns = ai < msgs.length - 1;
2368
+ if (discardsLaterTurns) {
2369
+ state.chat.confirmingEdit = { idx: ui, text: msgs[ui].content || '', kind: 'retry' };
2370
+ render();
2371
+ return;
2372
+ }
2373
+ state.chat.confirmingEdit = null;
2374
+ executeTruncateAndResend(ui);
2375
+ }
2376
+ function executeTruncateAndResend(ui) {
2377
+ const msgs = state.chat.messages;
2350
2378
  const userText = msgs[ui].content || '';
2351
- // Drop the user+assistant pair (and anything after) and resend the user text.
2352
2379
  state.chat.messages = msgs.slice(0, ui);
2353
2380
  state.chat.totalCost = computeTotalCost(); // discarded turns leave the spend
2381
+ // Never --resume a session whose tail diverged from what the server saw -
2382
+ // a mid-thread retry/edit truncates history the resumed session still has.
2383
+ state.chat.resumeSid = null;
2384
+ state.chat.resumeNote = null;
2354
2385
  state.chat.draft = userText;
2355
2386
  persistChat();
2356
2387
  if (canSend()) sendChat();
2357
2388
  else render();
2358
2389
  }
2390
+ function retryLastTurn() { retryTurn(); }
2359
2391
  // Edit-and-resend a user message: two-step. The edit click ARMS a confirmation
2360
2392
  // (truncating destroys the later turns), the banner's continue executes it.
2361
2393
  function armEditAndResend(m) {
@@ -2369,6 +2401,9 @@ function confirmEditAndResend() {
2369
2401
  const ce = state.chat.confirmingEdit;
2370
2402
  if (!ce || state.chat.busy) return;
2371
2403
  state.chat.confirmingEdit = null;
2404
+ // Retry resends the SAME text immediately (no edit step); edit-and-resend
2405
+ // populates the draft so the user can change it before sending.
2406
+ if (ce.kind === 'retry') { executeTruncateAndResend(ce.idx); return; }
2372
2407
  state.chat.messages = state.chat.messages.slice(0, ce.idx);
2373
2408
  state.chat.totalCost = computeTotalCost(); // discarded turns leave the spend
2374
2409
  // Never --resume a session whose tail diverged from what the server saw.