@ai-agent-forge/agent-forge 0.85.2 → 0.85.4

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.
@@ -875,6 +875,13 @@ export class InteractiveMode {
875
875
  while (true) {
876
876
  const userInput = await this.getUserInput();
877
877
  try {
878
+ // Inputs queued during startup may be "/" commands or "!" bash
879
+ // lines; replay them through the normal submit path (they were
880
+ // queued precisely because it needs the post-startup context).
881
+ // Inputs arriving through onInputCallback were already filtered
882
+ // by the editor submit handler, so this is a no-op for them.
883
+ if (await this.dispatchCommandOrBash(userInput))
884
+ continue;
878
885
  await this.promptWithEmptyReplyRetry(userInput);
879
886
  }
880
887
  catch (error) {
@@ -1532,51 +1539,29 @@ export class InteractiveMode {
1532
1539
  // Silently ignore clipboard errors (may not have permission, etc.)
1533
1540
  }
1534
1541
  }
1535
- // Startup-time submissions of plain prompts are queued; the main loop
1536
- // dequeues and submits them automatically once startup completes.
1537
- // Commands ("/", "!") and empty input keep the pre-existing behavior:
1538
- // commands need the post-startup dispatch context, so they are restored
1539
- // into the editor for the user to submit again after startup.
1542
+ // Startup-time submissions (prompts, "/" commands, and "!" bash inputs)
1543
+ // are all queued; the main loop dequeues them automatically once startup
1544
+ // completes. Commands and bash inputs replay through the normal submit
1545
+ // path (dispatch table / bash handling) because they need the
1546
+ // post-startup context — queueing is exactly what waits for it. Restoring
1547
+ // them into the editor instead proved hazardous: the restored text
1548
+ // concatenated with the user's next keystrokes and could be submitted to
1549
+ // the model as a literal prompt. Empty input is ignored.
1540
1550
  handleStartupSubmit(text) {
1541
1551
  if (!text.trim())
1542
1552
  return;
1543
- if (text.startsWith("/") || text.startsWith("!")) {
1544
- this.editor.setText(text);
1545
- this.showStatus("Startup in progress — command restored to the editor, submit again once startup completes");
1546
- return;
1547
- }
1548
1553
  this.pendingUserInputs.push(text);
1549
1554
  this.showStatus("Startup in progress — input queued and will run when startup completes");
1550
1555
  }
1551
1556
  setupEditorSubmitHandler() {
1552
- this.defaultEditor.onSubmit = async (text) => {
1553
- text = text.trim();
1557
+ this.defaultEditor.onSubmit = async (rawText) => {
1558
+ const text = rawText.trim();
1554
1559
  if (!text)
1555
1560
  return;
1556
- // Handle /commands through the dispatch table (interactive-commands.ts)
1557
- const dispatched = this.dispatchEditorCommand(text);
1558
- if (dispatched) {
1559
- if (dispatched.pending)
1560
- await dispatched.pending;
1561
+ // Handle /commands and bash inputs ("!"/"!!"); this is the same path
1562
+ // the main loop uses to replay inputs queued during startup.
1563
+ if (await this.dispatchCommandOrBash(text))
1561
1564
  return;
1562
- }
1563
- // Handle bash command (! for normal, !! for excluded from context)
1564
- if (text.startsWith("!")) {
1565
- const isExcluded = text.startsWith("!!");
1566
- const command = isExcluded ? text.slice(2).trim() : text.slice(1).trim();
1567
- if (command) {
1568
- if (this.facade.isBashRunning()) {
1569
- this.showWarning("A bash command is already running. Press Esc to cancel it first.");
1570
- this.editor.setText(text);
1571
- return;
1572
- }
1573
- this.editor.addToHistory?.(text);
1574
- await this.handleBashCommand(command, isExcluded);
1575
- this.isBashMode = false;
1576
- this.updateEditorBorderColor();
1577
- return;
1578
- }
1579
- }
1580
1565
  // Queue input during compaction
1581
1566
  if (this.facade.isCompacting()) {
1582
1567
  this.queueCompactionMessage(text, "steer");
@@ -1604,6 +1589,39 @@ export class InteractiveMode {
1604
1589
  this.editor.addToHistory?.(text);
1605
1590
  };
1606
1591
  }
1592
+ /**
1593
+ * Shared "/"-command and bash ("!"/"!!") handling for the editor submit
1594
+ * path and the main-loop replay of inputs queued during startup. Returns
1595
+ * true when the input was handled as a command or bash invocation; false
1596
+ * means the caller should treat it as a normal prompt.
1597
+ */
1598
+ async dispatchCommandOrBash(text) {
1599
+ // Handle /commands through the dispatch table (interactive-commands.ts)
1600
+ const dispatched = this.dispatchEditorCommand(text);
1601
+ if (dispatched) {
1602
+ if (dispatched.pending)
1603
+ await dispatched.pending;
1604
+ return true;
1605
+ }
1606
+ // Handle bash command (! for normal, !! for excluded from context)
1607
+ if (text.startsWith("!")) {
1608
+ const isExcluded = text.startsWith("!!");
1609
+ const command = isExcluded ? text.slice(2).trim() : text.slice(1).trim();
1610
+ if (command) {
1611
+ if (this.facade.isBashRunning()) {
1612
+ this.showWarning("A bash command is already running. Press Esc to cancel it first.");
1613
+ this.editor.setText(text);
1614
+ return true;
1615
+ }
1616
+ this.editor.addToHistory?.(text);
1617
+ await this.handleBashCommand(command, isExcluded);
1618
+ this.isBashMode = false;
1619
+ this.updateEditorBorderColor();
1620
+ return true;
1621
+ }
1622
+ }
1623
+ return false;
1624
+ }
1607
1625
  /**
1608
1626
  * Run a /command through COMMAND_DISPATCH_ENTRIES. Returns undefined when
1609
1627
  * text is not a registered command; otherwise the command has been fully