@dreb/coding-agent 2.46.0 → 2.47.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 (48) hide show
  1. package/README.md +4 -2
  2. package/dist/core/agent-session.d.ts.map +1 -1
  3. package/dist/core/agent-session.js +19 -8
  4. package/dist/core/agent-session.js.map +1 -1
  5. package/dist/core/extensions/index.d.ts +1 -1
  6. package/dist/core/extensions/index.d.ts.map +1 -1
  7. package/dist/core/extensions/index.js.map +1 -1
  8. package/dist/core/extensions/runner.d.ts +7 -0
  9. package/dist/core/extensions/runner.d.ts.map +1 -1
  10. package/dist/core/extensions/runner.js +10 -0
  11. package/dist/core/extensions/runner.js.map +1 -1
  12. package/dist/core/extensions/types.d.ts +35 -0
  13. package/dist/core/extensions/types.d.ts.map +1 -1
  14. package/dist/core/extensions/types.js.map +1 -1
  15. package/dist/core/sdk.d.ts.map +1 -1
  16. package/dist/core/sdk.js +1 -0
  17. package/dist/core/sdk.js.map +1 -1
  18. package/dist/core/session-manager.d.ts.map +1 -1
  19. package/dist/core/session-manager.js +38 -2
  20. package/dist/core/session-manager.js.map +1 -1
  21. package/dist/core/tools/ask-user.d.ts +45 -0
  22. package/dist/core/tools/ask-user.d.ts.map +1 -0
  23. package/dist/core/tools/ask-user.js +210 -0
  24. package/dist/core/tools/ask-user.js.map +1 -0
  25. package/dist/core/tools/index.d.ts +11 -0
  26. package/dist/core/tools/index.d.ts.map +1 -1
  27. package/dist/core/tools/index.js +8 -0
  28. package/dist/core/tools/index.js.map +1 -1
  29. package/dist/modes/interactive/components/ask-user.d.ts +59 -0
  30. package/dist/modes/interactive/components/ask-user.d.ts.map +1 -0
  31. package/dist/modes/interactive/components/ask-user.js +240 -0
  32. package/dist/modes/interactive/components/ask-user.js.map +1 -0
  33. package/dist/modes/interactive/interactive-mode.d.ts +12 -0
  34. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  35. package/dist/modes/interactive/interactive-mode.js +92 -5
  36. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  37. package/dist/modes/rpc/rpc-mode.d.ts +16 -1
  38. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  39. package/dist/modes/rpc/rpc-mode.js +112 -59
  40. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  41. package/dist/modes/rpc/rpc-types.d.ts +24 -0
  42. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  43. package/dist/modes/rpc/rpc-types.js.map +1 -1
  44. package/docs/dashboard.md +1 -1
  45. package/docs/extensions.md +33 -3
  46. package/docs/rpc.md +45 -6
  47. package/docs/session.md +1 -1
  48. package/package.json +1 -1
@@ -35,6 +35,7 @@ import { parseGitUrl } from "../../utils/git.js";
35
35
  import { extractCopyableText, extractThinkingText, getMessagePreview, getMessageRoleLabel, toSingleLinePreview, } from "../../utils/message-text.js";
36
36
  import { ensureTool } from "../../utils/tools-manager.js";
37
37
  import { ArminComponent } from "./components/armin.js";
38
+ import { AskUserComponent } from "./components/ask-user.js";
38
39
  import { AssistantMessageComponent } from "./components/assistant-message.js";
39
40
  import { BashExecutionComponent } from "./components/bash-execution.js";
40
41
  import { BorderedLoader } from "./components/bordered-loader.js";
@@ -136,6 +137,8 @@ export class InteractiveMode {
136
137
  extensionSelector = undefined;
137
138
  extensionInput = undefined;
138
139
  extensionEditor = undefined;
140
+ extensionAsk = undefined;
141
+ extensionDialogQueue = Promise.resolve();
139
142
  extensionTerminalInputUnsubscribers = new Set();
140
143
  // Extension widgets (components rendered above/below the editor)
141
144
  extensionWidgetsAbove = new Map();
@@ -1184,6 +1187,9 @@ export class InteractiveMode {
1184
1187
  if (this.extensionEditor) {
1185
1188
  this.hideExtensionEditor();
1186
1189
  }
1190
+ if (this.extensionAsk) {
1191
+ this.hideExtensionAsk();
1192
+ }
1187
1193
  this.ui.hideOverlay();
1188
1194
  this.clearExtensionTerminalInputListeners();
1189
1195
  this.setExtensionFooter(undefined);
@@ -1406,9 +1412,22 @@ export class InteractiveMode {
1406
1412
  */
1407
1413
  createExtensionUIContext() {
1408
1414
  return {
1409
- select: (title, options, opts) => this.showExtensionSelector(title, options, opts),
1410
- confirm: (title, message, opts) => this.showExtensionConfirm(title, message, opts),
1411
- input: (title, placeholder, opts) => this.showExtensionInput(title, placeholder, opts),
1415
+ select: (title, options, opts) => this.enqueueExtensionDialog(() => this.showExtensionSelector(title, options, opts), {
1416
+ signal: opts?.signal,
1417
+ abortedValue: undefined,
1418
+ }),
1419
+ confirm: (title, message, opts) => this.enqueueExtensionDialog(() => this.showExtensionConfirm(title, message, opts), {
1420
+ signal: opts?.signal,
1421
+ abortedValue: false,
1422
+ }),
1423
+ input: (title, placeholder, opts) => this.enqueueExtensionDialog(() => this.showExtensionInput(title, placeholder, opts), {
1424
+ signal: opts?.signal,
1425
+ abortedValue: undefined,
1426
+ }),
1427
+ ask: (request, opts) => this.enqueueExtensionDialog(() => this.showExtensionAsk(request, opts), {
1428
+ signal: opts?.signal,
1429
+ abortedValue: undefined,
1430
+ }),
1412
1431
  notify: (message, type) => this.showExtensionNotify(message, type),
1413
1432
  onTerminalInput: (handler) => this.addExtensionTerminalInputListener(handler),
1414
1433
  setStatus: (key, text) => this.setExtensionStatus(key, text),
@@ -1417,11 +1436,11 @@ export class InteractiveMode {
1417
1436
  setFooter: (factory) => this.setExtensionFooter(factory),
1418
1437
  setHeader: (factory) => this.setExtensionHeader(factory),
1419
1438
  setTitle: (title) => this.ui.terminal.setTitle(title),
1420
- custom: (factory, options) => this.showExtensionCustom(factory, options),
1439
+ custom: (factory, options) => this.enqueueExtensionDialog(() => this.showExtensionCustom(factory, options)),
1421
1440
  pasteToEditor: (text) => this.editor.handleInput(`\x1b[200~${text}\x1b[201~`),
1422
1441
  setEditorText: (text) => this.editor.setText(text),
1423
1442
  getEditorText: () => this.editor.getExpandedText?.() ?? this.editor.getText(),
1424
- editor: (title, prefill) => this.showExtensionEditor(title, prefill),
1443
+ editor: (title, prefill) => this.enqueueExtensionDialog(() => this.showExtensionEditor(title, prefill)),
1425
1444
  setEditorComponent: (factory) => this.setCustomEditorComponent(factory),
1426
1445
  get theme() {
1427
1446
  return theme;
@@ -1447,6 +1466,32 @@ export class InteractiveMode {
1447
1466
  setToolsExpanded: (expanded) => this.setToolsExpanded(expanded),
1448
1467
  };
1449
1468
  }
1469
+ /** Serialize every blocking extension dialog that can take over keyboard focus. */
1470
+ enqueueExtensionDialog(open, opts) {
1471
+ if (!opts?.signal) {
1472
+ const result = this.extensionDialogQueue.then(open, open);
1473
+ this.extensionDialogQueue = result.then(() => undefined, () => undefined);
1474
+ return result;
1475
+ }
1476
+ const { signal, abortedValue } = opts;
1477
+ if (signal.aborted)
1478
+ return Promise.resolve(abortedValue);
1479
+ // Settle the caller as soon as a queued dialog aborts, even if an earlier
1480
+ // unbounded dialog still owns the UI. Its queue slot remains as a no-op so
1481
+ // later dialogs keep their FIFO order.
1482
+ let onQueuedAbort;
1483
+ const aborted = new Promise((resolve) => {
1484
+ onQueuedAbort = () => resolve(abortedValue);
1485
+ signal.addEventListener("abort", onQueuedAbort, { once: true });
1486
+ });
1487
+ const run = () => {
1488
+ signal.removeEventListener("abort", onQueuedAbort);
1489
+ return signal.aborted ? Promise.resolve(abortedValue) : open();
1490
+ };
1491
+ const queued = this.extensionDialogQueue.then(run, run);
1492
+ this.extensionDialogQueue = queued.then(() => undefined, () => undefined);
1493
+ return Promise.race([queued, aborted]).finally(() => signal.removeEventListener("abort", onQueuedAbort));
1494
+ }
1450
1495
  /**
1451
1496
  * Show a selector for extensions.
1452
1497
  */
@@ -1528,6 +1573,48 @@ export class InteractiveMode {
1528
1573
  this.extensionInput = undefined;
1529
1574
  this.restoreEditorComponent();
1530
1575
  }
1576
+ /**
1577
+ * Show a rich ask_user question for extensions/built-in tools.
1578
+ */
1579
+ showExtensionAsk(request, opts) {
1580
+ return new Promise((resolve) => {
1581
+ if (opts?.signal?.aborted) {
1582
+ resolve(undefined);
1583
+ return;
1584
+ }
1585
+ const onAbort = () => {
1586
+ this.hideExtensionAsk();
1587
+ resolve(undefined);
1588
+ };
1589
+ opts?.signal?.addEventListener("abort", onAbort, { once: true });
1590
+ this.extensionAsk = new AskUserComponent(request, (result) => {
1591
+ opts?.signal?.removeEventListener("abort", onAbort);
1592
+ this.hideExtensionAsk();
1593
+ resolve(result);
1594
+ }, () => {
1595
+ opts?.signal?.removeEventListener("abort", onAbort);
1596
+ this.hideExtensionAsk();
1597
+ // The question-level escape action means stop the whole turn, not
1598
+ // continue without an answer. Mirror the default editor's Escape
1599
+ // behavior, including background-agent and queued-message cleanup.
1600
+ this.cancelBackgroundAgents();
1601
+ this.restoreQueuedMessagesToEditor({ abort: true });
1602
+ resolve(undefined);
1603
+ }, { tui: this.ui, timeout: opts?.timeout });
1604
+ this.editorContainer.clear();
1605
+ this.editorContainer.addChild(this.extensionAsk);
1606
+ this.ui.setFocus(this.extensionAsk);
1607
+ this.ui.requestRender();
1608
+ });
1609
+ }
1610
+ /**
1611
+ * Hide the ask_user question.
1612
+ */
1613
+ hideExtensionAsk() {
1614
+ this.extensionAsk?.dispose();
1615
+ this.extensionAsk = undefined;
1616
+ this.restoreEditorComponent();
1617
+ }
1531
1618
  /**
1532
1619
  * Show a multi-line editor for extensions (with Ctrl+G support).
1533
1620
  */