@dexto/tui 1.7.1 → 1.8.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 (99) hide show
  1. package/dist/agent-backend.cjs +16 -2
  2. package/dist/agent-backend.d.ts +5 -2
  3. package/dist/agent-backend.d.ts.map +1 -1
  4. package/dist/agent-backend.js +15 -2
  5. package/dist/agent-backend.test.cjs +28 -2
  6. package/dist/agent-backend.test.js +28 -2
  7. package/dist/components/ApprovalPrompt.cjs +6 -5
  8. package/dist/components/ApprovalPrompt.d.ts +1 -1
  9. package/dist/components/ApprovalPrompt.d.ts.map +1 -1
  10. package/dist/components/ApprovalPrompt.js +6 -5
  11. package/dist/components/TextBufferInput.cjs +14 -1
  12. package/dist/components/TextBufferInput.d.ts +5 -1
  13. package/dist/components/TextBufferInput.d.ts.map +1 -1
  14. package/dist/components/TextBufferInput.js +14 -1
  15. package/dist/components/chat/QueuedMessagesDisplay.cjs +17 -8
  16. package/dist/components/chat/QueuedMessagesDisplay.d.ts +7 -1
  17. package/dist/components/chat/QueuedMessagesDisplay.d.ts.map +1 -1
  18. package/dist/components/chat/QueuedMessagesDisplay.js +16 -8
  19. package/dist/components/input/InputArea.cjs +4 -0
  20. package/dist/components/input/InputArea.d.ts +5 -1
  21. package/dist/components/input/InputArea.d.ts.map +1 -1
  22. package/dist/components/input/InputArea.js +4 -0
  23. package/dist/components/modes/AlternateBufferCLI.cjs +20 -1
  24. package/dist/components/modes/AlternateBufferCLI.d.ts.map +1 -1
  25. package/dist/components/modes/AlternateBufferCLI.js +21 -2
  26. package/dist/components/modes/StaticCLI.cjs +20 -1
  27. package/dist/components/modes/StaticCLI.d.ts.map +1 -1
  28. package/dist/components/modes/StaticCLI.js +21 -2
  29. package/dist/components/overlays/LoginOverlay.cjs +2 -10
  30. package/dist/components/overlays/LoginOverlay.d.ts.map +1 -1
  31. package/dist/components/overlays/LoginOverlay.js +3 -11
  32. package/dist/containers/InputContainer.cjs +119 -18
  33. package/dist/containers/InputContainer.d.ts +6 -2
  34. package/dist/containers/InputContainer.d.ts.map +1 -1
  35. package/dist/containers/InputContainer.js +119 -18
  36. package/dist/hooks/useAgentEvents.cjs +27 -5
  37. package/dist/hooks/useAgentEvents.d.ts +3 -2
  38. package/dist/hooks/useAgentEvents.d.ts.map +1 -1
  39. package/dist/hooks/useAgentEvents.js +27 -5
  40. package/dist/hooks/useCLIState.cjs +10 -4
  41. package/dist/hooks/useCLIState.d.ts +2 -0
  42. package/dist/hooks/useCLIState.d.ts.map +1 -1
  43. package/dist/hooks/useCLIState.js +10 -4
  44. package/dist/hooks/useInputOrchestrator.cjs +15 -14
  45. package/dist/hooks/useInputOrchestrator.d.ts +6 -6
  46. package/dist/hooks/useInputOrchestrator.d.ts.map +1 -1
  47. package/dist/hooks/useInputOrchestrator.js +15 -14
  48. package/dist/host/index.cjs +6 -6
  49. package/dist/host/index.d.ts +8 -17
  50. package/dist/host/index.d.ts.map +1 -1
  51. package/dist/host/index.js +5 -5
  52. package/dist/host/index.test.cjs +47 -0
  53. package/dist/host/index.test.d.ts +2 -0
  54. package/dist/host/index.test.d.ts.map +1 -0
  55. package/dist/host/index.test.js +50 -0
  56. package/dist/index.d.cts +10 -15
  57. package/dist/interactive-commands/command-parser.cjs +1 -0
  58. package/dist/interactive-commands/command-parser.d.ts.map +1 -1
  59. package/dist/interactive-commands/command-parser.js +1 -0
  60. package/dist/interactive-commands/commands.cjs +3 -0
  61. package/dist/interactive-commands/commands.d.ts.map +1 -1
  62. package/dist/interactive-commands/commands.js +3 -0
  63. package/dist/interactive-commands/commands.test.cjs +42 -0
  64. package/dist/interactive-commands/commands.test.js +42 -0
  65. package/dist/interactive-commands/prompt-commands.cjs +4 -66
  66. package/dist/interactive-commands/prompt-commands.d.ts +1 -2
  67. package/dist/interactive-commands/prompt-commands.d.ts.map +1 -1
  68. package/dist/interactive-commands/prompt-commands.js +4 -66
  69. package/dist/interactive-commands/skill-commands.cjs +73 -0
  70. package/dist/interactive-commands/skill-commands.d.ts +9 -0
  71. package/dist/interactive-commands/skill-commands.d.ts.map +1 -0
  72. package/dist/interactive-commands/skill-commands.js +49 -0
  73. package/dist/services/processStream.cjs +23 -4
  74. package/dist/services/processStream.d.ts +3 -1
  75. package/dist/services/processStream.d.ts.map +1 -1
  76. package/dist/services/processStream.js +23 -4
  77. package/dist/services/processStream.test.cjs +52 -2
  78. package/dist/services/processStream.test.js +52 -2
  79. package/dist/state/initialState.cjs +2 -1
  80. package/dist/state/initialState.d.ts.map +1 -1
  81. package/dist/state/initialState.js +2 -1
  82. package/dist/state/reducer.cjs +10 -5
  83. package/dist/state/reducer.d.ts.map +1 -1
  84. package/dist/state/reducer.js +10 -5
  85. package/dist/state/types.d.ts +2 -0
  86. package/dist/state/types.d.ts.map +1 -1
  87. package/dist/utils/messageFormatting.cjs +0 -23
  88. package/dist/utils/messageFormatting.d.ts +0 -13
  89. package/dist/utils/messageFormatting.d.ts.map +1 -1
  90. package/dist/utils/messageFormatting.js +0 -21
  91. package/dist/utils/queuedComposerContent.cjs +148 -0
  92. package/dist/utils/queuedComposerContent.d.ts +17 -0
  93. package/dist/utils/queuedComposerContent.d.ts.map +1 -0
  94. package/dist/utils/queuedComposerContent.js +123 -0
  95. package/dist/utils/queuedComposerContent.test.cjs +176 -0
  96. package/dist/utils/queuedComposerContent.test.d.ts +2 -0
  97. package/dist/utils/queuedComposerContent.test.d.ts.map +1 -0
  98. package/dist/utils/queuedComposerContent.test.js +175 -0
  99. package/package.json +4 -4
@@ -110,7 +110,7 @@ function createPromptCommand(promptInfo) {
110
110
  description: promptInfo.description || `Execute ${baseName} prompt`,
111
111
  usage: `/${commandName} [context]`,
112
112
  category: "Dynamic Prompts",
113
- handler: async (args, agent, ctx) => {
113
+ handler: async (args, agent, _ctx) => {
114
114
  try {
115
115
  const { argMap, context: contextString } = splitPromptArguments(args);
116
116
  const resolveOptions = {};
@@ -121,58 +121,6 @@ function createPromptCommand(promptInfo) {
121
121
  resolveOptions.context = contextString;
122
122
  }
123
123
  const result = await agent.resolvePrompt(internalName, resolveOptions);
124
- if (ctx.sessionId) {
125
- if (result.model) {
126
- try {
127
- await agent.switchLLM({ model: result.model }, ctx.sessionId);
128
- } catch (modelError) {
129
- agent.logger.warn("Failed to switch model for prompt override", {
130
- model: result.model,
131
- error: modelError instanceof Error ? modelError.message : String(modelError)
132
- });
133
- }
134
- }
135
- if (result.allowedTools && result.allowedTools.length > 0) {
136
- try {
137
- agent.toolManager.addSessionAutoApproveTools(
138
- ctx.sessionId,
139
- result.allowedTools
140
- );
141
- } catch (toolError) {
142
- agent.logger.warn("Failed to set auto-approve tools for prompt", {
143
- tools: result.allowedTools,
144
- error: toolError instanceof Error ? toolError.message : String(toolError)
145
- });
146
- }
147
- }
148
- }
149
- if (result.context !== "fork" && result.toolkits && result.toolkits.length > 0) {
150
- if (!agent.loadToolkits) {
151
- return formatForInkCli(
152
- `\u274C Skill '${commandName}' requires toolkits (${result.toolkits.join(", ")}), but this agent does not support dynamic tool loading.`
153
- );
154
- }
155
- try {
156
- await agent.loadToolkits(result.toolkits);
157
- } catch (error) {
158
- return formatForInkCli(
159
- `\u274C Failed to load toolkits for skill '${commandName}': ${error instanceof Error ? error.message : String(error)}`
160
- );
161
- }
162
- }
163
- if (result.context === "fork") {
164
- const skillName = internalName;
165
- const taskContext = contextString || "";
166
- const instructionText = `<skill-invocation>
167
- Execute the fork skill: ${commandName}
168
- ${taskContext ? `Task context: ${taskContext}` : ""}
169
-
170
- Call the invoke_skill tool immediately with:
171
- - skill: "${skillName}"
172
- ${taskContext ? `- taskContext: "${taskContext}"` : ""}
173
- </skill-invocation>`;
174
- return createSendMessageMarker(instructionText);
175
- }
176
124
  let finalText = result.text;
177
125
  if (result.resources.length > 0) {
178
126
  const resourceRefs = result.resources.map((uri) => `@<${uri}>`).join(" ");
@@ -181,20 +129,10 @@ ${taskContext ? `- taskContext: "${taskContext}"` : ""}
181
129
  ${resourceRefs}` : resourceRefs;
182
130
  }
183
131
  if (finalText.trim()) {
184
- const taskContext = contextString || "";
185
- const wrappedText = `<skill-invocation>
186
- Execute the inline skill: ${commandName}
187
- ${taskContext ? `Task context: ${taskContext}` : ""}
188
-
189
- skill: "${internalName}"
190
- </skill-invocation>
191
-
192
- ${finalText.trim()}`;
193
- return createSendMessageMarker(wrappedText);
194
- } else {
195
- const warningMsg = `\u26A0\uFE0F Prompt '${commandName}' returned no content`;
196
- return formatForInkCli(warningMsg);
132
+ return createSendMessageMarker(finalText.trim());
197
133
  }
134
+ const warningMsg = `\u26A0\uFE0F Prompt '${commandName}' returned no content`;
135
+ return formatForInkCli(warningMsg);
198
136
  } catch (error) {
199
137
  const errorMessage = error instanceof Error ? error.message : String(error);
200
138
  agent.logger.error(
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var skill_commands_exports = {};
20
+ __export(skill_commands_exports, {
21
+ skillCommands: () => skillCommands
22
+ });
23
+ module.exports = __toCommonJS(skill_commands_exports);
24
+ var import_format_output = require("./utils/format-output.js");
25
+ const skillCommands = [
26
+ {
27
+ name: "skills",
28
+ description: "List available skills, or read one skill by id",
29
+ usage: "/skills [skill-id]",
30
+ category: "Skill Management",
31
+ handler: async (args, agent, _ctx) => {
32
+ const skillManager = agent.skillManager;
33
+ if (!skillManager) {
34
+ return (0, import_format_output.formatForInkCli)("\u26A0\uFE0F Skills are not available for this chat target.");
35
+ }
36
+ const skillId = args[0];
37
+ try {
38
+ if (skillId) {
39
+ const skill = await skillManager.get(skillId);
40
+ if (!skill) {
41
+ return (0, import_format_output.formatForInkCli)(`\u26A0\uFE0F Skill '${skillId}' not found`);
42
+ }
43
+ const outputLines2 = [`
44
+ \u{1F9E9} ${skill.displayName}`, `ID: ${skill.id}`];
45
+ if (skill.description) {
46
+ outputLines2.push(`Description: ${skill.description}`);
47
+ }
48
+ outputLines2.push("", skill.instructions);
49
+ return (0, import_format_output.formatForInkCli)(outputLines2.join("\n"));
50
+ }
51
+ const skills = await skillManager.list();
52
+ if (skills.length === 0) {
53
+ return (0, import_format_output.formatForInkCli)("\n\u26A0\uFE0F No skills available");
54
+ }
55
+ const outputLines = ["\n\u{1F9E9} Available Skills:\n"];
56
+ for (const skill of skills) {
57
+ const desc = skill.description ? ` - ${skill.description}` : "";
58
+ outputLines.push(` ${skill.displayName} (${skill.id})${desc}`);
59
+ }
60
+ outputLines.push("", `Total: ${skills.length} skills`);
61
+ return (0, import_format_output.formatForInkCli)(outputLines.join("\n"));
62
+ } catch (error) {
63
+ const errorMsg = `Error loading skills: ${error instanceof Error ? error.message : String(error)}`;
64
+ agent.logger.error(errorMsg);
65
+ return (0, import_format_output.formatForInkCli)(`\u274C ${errorMsg}`);
66
+ }
67
+ }
68
+ }
69
+ ];
70
+ // Annotate the CommonJS export names for ESM import in node:
71
+ 0 && (module.exports = {
72
+ skillCommands
73
+ });
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Skill Commands Module
3
+ *
4
+ * Skills are first-class agent capabilities backed by SkillManager. They are
5
+ * listed/read separately from slash prompt commands.
6
+ */
7
+ import type { CommandDefinition } from './command-parser.js';
8
+ export declare const skillCommands: CommandDefinition[];
9
+ //# sourceMappingURL=skill-commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-commands.d.ts","sourceRoot":"","sources":["../../src/interactive-commands/skill-commands.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAkB,iBAAiB,EAAwB,MAAM,qBAAqB,CAAC;AAInG,eAAO,MAAM,aAAa,EAAE,iBAAiB,EAoD5C,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { formatForInkCli } from "./utils/format-output.js";
2
+ const skillCommands = [
3
+ {
4
+ name: "skills",
5
+ description: "List available skills, or read one skill by id",
6
+ usage: "/skills [skill-id]",
7
+ category: "Skill Management",
8
+ handler: async (args, agent, _ctx) => {
9
+ const skillManager = agent.skillManager;
10
+ if (!skillManager) {
11
+ return formatForInkCli("\u26A0\uFE0F Skills are not available for this chat target.");
12
+ }
13
+ const skillId = args[0];
14
+ try {
15
+ if (skillId) {
16
+ const skill = await skillManager.get(skillId);
17
+ if (!skill) {
18
+ return formatForInkCli(`\u26A0\uFE0F Skill '${skillId}' not found`);
19
+ }
20
+ const outputLines2 = [`
21
+ \u{1F9E9} ${skill.displayName}`, `ID: ${skill.id}`];
22
+ if (skill.description) {
23
+ outputLines2.push(`Description: ${skill.description}`);
24
+ }
25
+ outputLines2.push("", skill.instructions);
26
+ return formatForInkCli(outputLines2.join("\n"));
27
+ }
28
+ const skills = await skillManager.list();
29
+ if (skills.length === 0) {
30
+ return formatForInkCli("\n\u26A0\uFE0F No skills available");
31
+ }
32
+ const outputLines = ["\n\u{1F9E9} Available Skills:\n"];
33
+ for (const skill of skills) {
34
+ const desc = skill.description ? ` - ${skill.description}` : "";
35
+ outputLines.push(` ${skill.displayName} (${skill.id})${desc}`);
36
+ }
37
+ outputLines.push("", `Total: ${skills.length} skills`);
38
+ return formatForInkCli(outputLines.join("\n"));
39
+ } catch (error) {
40
+ const errorMsg = `Error loading skills: ${error instanceof Error ? error.message : String(error)}`;
41
+ agent.logger.error(errorMsg);
42
+ return formatForInkCli(`\u274C ${errorMsg}`);
43
+ }
44
+ }
45
+ }
46
+ ];
47
+ export {
48
+ skillCommands
49
+ };
@@ -102,6 +102,7 @@ async function processStream(iterator, setters, options) {
102
102
  setDequeuedBuffer,
103
103
  setUi,
104
104
  setSession: _setSession,
105
+ setSteerMessages,
105
106
  setQueuedMessages,
106
107
  setApproval,
107
108
  setApprovalQueue
@@ -139,6 +140,24 @@ async function processStream(iterator, setters, options) {
139
140
  return `${prefix}: ${content}`;
140
141
  }).join("\n\n");
141
142
  };
143
+ const removeDequeuedMessagesFromState = (event) => {
144
+ const dequeuedIds = new Set(
145
+ event.ids ?? event.messages?.map((message) => message.id) ?? []
146
+ );
147
+ if (dequeuedIds.size === 0) {
148
+ if (event.queue === "steer") {
149
+ setSteerMessages([]);
150
+ } else {
151
+ setQueuedMessages([]);
152
+ }
153
+ return;
154
+ }
155
+ if (event.queue === "steer") {
156
+ setSteerMessages((prev) => prev.filter((message) => !dequeuedIds.has(message.id)));
157
+ } else {
158
+ setQueuedMessages((prev) => prev.filter((message) => !dequeuedIds.has(message.id)));
159
+ }
160
+ };
142
161
  const finalizeMessage = (messageId, updates = {}) => {
143
162
  const msg = localPending.find((m) => m.id === messageId);
144
163
  if (msg) {
@@ -707,7 +726,7 @@ ${import_chalk.default.dim(callDescription)}`;
707
726
  }
708
727
  ]);
709
728
  }
710
- setQueuedMessages([]);
729
+ removeDequeuedMessagesFromState(event);
711
730
  setUi((prev) => ({ ...prev, isProcessing: true }));
712
731
  break;
713
732
  }
@@ -723,7 +742,7 @@ ${import_chalk.default.dim(callDescription)}`;
723
742
  }
724
743
  ]);
725
744
  }
726
- setQueuedMessages([]);
745
+ removeDequeuedMessagesFromState(event);
727
746
  setUi((prev) => ({ ...prev, isProcessing: true }));
728
747
  break;
729
748
  }
@@ -738,7 +757,7 @@ ${import_chalk.default.dim(callDescription)}`;
738
757
  const bypassPermissions = options.bypassPermissionsRef.current;
739
758
  const autoApproveEdits = options.autoApproveEditsRef.current;
740
759
  const { eventBus } = options;
741
- if (bypassPermissions && (event.type === import_core.ApprovalType.TOOL_APPROVAL || event.type === import_core.ApprovalType.COMMAND_CONFIRMATION || event.type === import_core.ApprovalType.DIRECTORY_ACCESS)) {
760
+ if (bypassPermissions && (event.type === import_core.ApprovalType.TOOL_APPROVAL || event.type === import_core.ApprovalType.COMMAND_APPROVAL || event.type === import_core.ApprovalType.DIRECTORY_ACCESS)) {
742
761
  if (event.type === import_core.ApprovalType.TOOL_APPROVAL) {
743
762
  const { toolName } = event.metadata;
744
763
  if (toolName === "plan_create" || toolName === "plan_review") {
@@ -777,7 +796,7 @@ ${import_chalk.default.dim(callDescription)}`;
777
796
  approvalIdToToolCallId.set(event.approvalId, toolCallId);
778
797
  updatePendingStatus(`tool-${toolCallId}`, "pending_approval");
779
798
  }
780
- if (event.type === import_core.ApprovalType.TOOL_APPROVAL || event.type === import_core.ApprovalType.COMMAND_CONFIRMATION || event.type === import_core.ApprovalType.ELICITATION || event.type === import_core.ApprovalType.DIRECTORY_ACCESS) {
799
+ if (event.type === import_core.ApprovalType.TOOL_APPROVAL || event.type === import_core.ApprovalType.COMMAND_APPROVAL || event.type === import_core.ApprovalType.ELICITATION || event.type === import_core.ApprovalType.DIRECTORY_ACCESS) {
781
800
  const newApproval = {
782
801
  approvalId: event.approvalId,
783
802
  type: event.type,
@@ -36,7 +36,9 @@ export interface ProcessStreamSetters {
36
36
  setUi: React.Dispatch<React.SetStateAction<UIState>>;
37
37
  /** Setter for session state (for session switch on compaction) */
38
38
  setSession: React.Dispatch<React.SetStateAction<import('../state/types.js').SessionState>>;
39
- /** Setter for queued messages (cleared when dequeued) */
39
+ /** Setter for active-turn steer messages */
40
+ setSteerMessages: React.Dispatch<React.SetStateAction<import('@dexto/core').QueuedMessage[]>>;
41
+ /** Setter for queued follow-up messages */
40
42
  setQueuedMessages: React.Dispatch<React.SetStateAction<import('@dexto/core').QueuedMessage[]>>;
41
43
  /** Setter for current approval request (for approval UI) */
42
44
  setApproval: React.Dispatch<React.SetStateAction<ApprovalRequest | null>>;
@@ -1 +1 @@
1
- {"version":3,"file":"processStream.d.ts","sourceRoot":"","sources":["../../src/services/processStream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAC;AAGvE,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAc,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAoEvE;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,2DAA2D;IAC3D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,oFAAoF;IACpF,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACpE,iFAAiF;IACjF,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACnE,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,kEAAkE;IAClE,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,mBAAmB,EAAE,YAAY,CAAC,CAAC,CAAC;IAC3F,yDAAyD;IACzD,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,aAAa,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;IAC/F,4DAA4D;IAC5D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,uDAAuD;IACvD,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;CAC7E;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,2FAA2F;IAC3F,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2FAA2F;IAC3F,mBAAmB,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAC1C,wFAAwF;IACxF,oBAAoB,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3C,yDAAyD;IACzD,QAAQ,EAAE,IAAI,CAAC,OAAO,aAAa,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;IAC5D,8DAA8D;IAC9D,YAAY,CAAC,EAAE,OAAO,+BAA+B,EAAE,wBAAwB,CAAC;IAChF,kEAAkE;IAClE,QAAQ,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,mBAAmB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;CAC3F;AAoDD;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAC/B,QAAQ,EAAE,qBAAqB,CAAC,cAAc,CAAC,EAC/C,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,oBAAoB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAksCf"}
1
+ {"version":3,"file":"processStream.d.ts","sourceRoot":"","sources":["../../src/services/processStream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAC;AAGvE,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAc,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAoEvE;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,2DAA2D;IAC3D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,oFAAoF;IACpF,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACpE,iFAAiF;IACjF,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACnE,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,kEAAkE;IAClE,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,mBAAmB,EAAE,YAAY,CAAC,CAAC,CAAC;IAC3F,4CAA4C;IAC5C,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,aAAa,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;IAC9F,2CAA2C;IAC3C,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,aAAa,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;IAC/F,4DAA4D;IAC5D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,uDAAuD;IACvD,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;CAC7E;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,2FAA2F;IAC3F,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2FAA2F;IAC3F,mBAAmB,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAC1C,wFAAwF;IACxF,oBAAoB,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAC3C,yDAAyD;IACzD,QAAQ,EAAE,IAAI,CAAC,OAAO,aAAa,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;IAC5D,8DAA8D;IAC9D,YAAY,CAAC,EAAE,OAAO,+BAA+B,EAAE,wBAAwB,CAAC;IAChF,kEAAkE;IAClE,QAAQ,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,mBAAmB,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;CAC3F;AAoDD;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAC/B,QAAQ,EAAE,qBAAqB,CAAC,cAAc,CAAC,EAC/C,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,oBAAoB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAwtCf"}
@@ -69,6 +69,7 @@ async function processStream(iterator, setters, options) {
69
69
  setDequeuedBuffer,
70
70
  setUi,
71
71
  setSession: _setSession,
72
+ setSteerMessages,
72
73
  setQueuedMessages,
73
74
  setApproval,
74
75
  setApprovalQueue
@@ -106,6 +107,24 @@ async function processStream(iterator, setters, options) {
106
107
  return `${prefix}: ${content}`;
107
108
  }).join("\n\n");
108
109
  };
110
+ const removeDequeuedMessagesFromState = (event) => {
111
+ const dequeuedIds = new Set(
112
+ event.ids ?? event.messages?.map((message) => message.id) ?? []
113
+ );
114
+ if (dequeuedIds.size === 0) {
115
+ if (event.queue === "steer") {
116
+ setSteerMessages([]);
117
+ } else {
118
+ setQueuedMessages([]);
119
+ }
120
+ return;
121
+ }
122
+ if (event.queue === "steer") {
123
+ setSteerMessages((prev) => prev.filter((message) => !dequeuedIds.has(message.id)));
124
+ } else {
125
+ setQueuedMessages((prev) => prev.filter((message) => !dequeuedIds.has(message.id)));
126
+ }
127
+ };
109
128
  const finalizeMessage = (messageId, updates = {}) => {
110
129
  const msg = localPending.find((m) => m.id === messageId);
111
130
  if (msg) {
@@ -674,7 +693,7 @@ ${chalk.dim(callDescription)}`;
674
693
  }
675
694
  ]);
676
695
  }
677
- setQueuedMessages([]);
696
+ removeDequeuedMessagesFromState(event);
678
697
  setUi((prev) => ({ ...prev, isProcessing: true }));
679
698
  break;
680
699
  }
@@ -690,7 +709,7 @@ ${chalk.dim(callDescription)}`;
690
709
  }
691
710
  ]);
692
711
  }
693
- setQueuedMessages([]);
712
+ removeDequeuedMessagesFromState(event);
694
713
  setUi((prev) => ({ ...prev, isProcessing: true }));
695
714
  break;
696
715
  }
@@ -705,7 +724,7 @@ ${chalk.dim(callDescription)}`;
705
724
  const bypassPermissions = options.bypassPermissionsRef.current;
706
725
  const autoApproveEdits = options.autoApproveEditsRef.current;
707
726
  const { eventBus } = options;
708
- if (bypassPermissions && (event.type === ApprovalTypeEnum.TOOL_APPROVAL || event.type === ApprovalTypeEnum.COMMAND_CONFIRMATION || event.type === ApprovalTypeEnum.DIRECTORY_ACCESS)) {
727
+ if (bypassPermissions && (event.type === ApprovalTypeEnum.TOOL_APPROVAL || event.type === ApprovalTypeEnum.COMMAND_APPROVAL || event.type === ApprovalTypeEnum.DIRECTORY_ACCESS)) {
709
728
  if (event.type === ApprovalTypeEnum.TOOL_APPROVAL) {
710
729
  const { toolName } = event.metadata;
711
730
  if (toolName === "plan_create" || toolName === "plan_review") {
@@ -744,7 +763,7 @@ ${chalk.dim(callDescription)}`;
744
763
  approvalIdToToolCallId.set(event.approvalId, toolCallId);
745
764
  updatePendingStatus(`tool-${toolCallId}`, "pending_approval");
746
765
  }
747
- if (event.type === ApprovalTypeEnum.TOOL_APPROVAL || event.type === ApprovalTypeEnum.COMMAND_CONFIRMATION || event.type === ApprovalTypeEnum.ELICITATION || event.type === ApprovalTypeEnum.DIRECTORY_ACCESS) {
766
+ if (event.type === ApprovalTypeEnum.TOOL_APPROVAL || event.type === ApprovalTypeEnum.COMMAND_APPROVAL || event.type === ApprovalTypeEnum.ELICITATION || event.type === ApprovalTypeEnum.DIRECTORY_ACCESS) {
748
767
  const newApproval = {
749
768
  approvalId: event.approvalId,
750
769
  type: event.type,
@@ -25,10 +25,15 @@ async function* eventStream(events) {
25
25
  yield event;
26
26
  }
27
27
  }
28
- function createSetters() {
28
+ function createSetters({
29
+ steerMessages: initialSteerMessages = [],
30
+ queuedMessages: initialQueuedMessages = []
31
+ } = {}) {
29
32
  const messages = createState([]);
30
33
  const pendingMessages = createState([]);
31
34
  const dequeuedBuffer = createState([]);
35
+ const steerMessages = createState(initialSteerMessages);
36
+ const queuedMessages = createState(initialQueuedMessages);
32
37
  const ui = createState({
33
38
  isProcessing: false,
34
39
  isCancelling: false,
@@ -71,6 +76,8 @@ function createSetters() {
71
76
  return {
72
77
  getMessages: messages.get,
73
78
  getPendingMessages: pendingMessages.get,
79
+ getSteerMessages: steerMessages.get,
80
+ getQueuedMessages: queuedMessages.get,
74
81
  getUi: ui.get,
75
82
  setters: {
76
83
  setMessages: messages.set,
@@ -78,7 +85,8 @@ function createSetters() {
78
85
  setDequeuedBuffer: dequeuedBuffer.set,
79
86
  setUi: ui.set,
80
87
  setSession: session.set,
81
- setQueuedMessages: createNoopDispatch(),
88
+ setSteerMessages: steerMessages.set,
89
+ setQueuedMessages: queuedMessages.set,
82
90
  setApproval: createNoopDispatch(),
83
91
  setApprovalQueue: createNoopDispatch()
84
92
  }
@@ -88,6 +96,47 @@ function createSetters() {
88
96
  (0, import_vitest.beforeEach)(() => {
89
97
  captureAnalyticsMock.mockClear();
90
98
  });
99
+ (0, import_vitest.it)("removes only dequeued queue ids from steer and follow-up state", async () => {
100
+ const steer = {
101
+ id: "steer-1",
102
+ content: [{ type: "text", text: "active steer" }],
103
+ queuedAt: Date.now(),
104
+ kind: "default"
105
+ };
106
+ const followUp = {
107
+ id: "follow-up-1",
108
+ content: [{ type: "text", text: "later" }],
109
+ queuedAt: Date.now(),
110
+ kind: "default"
111
+ };
112
+ const { getSteerMessages, getQueuedMessages, setters } = createSetters({
113
+ steerMessages: [steer],
114
+ queuedMessages: [followUp]
115
+ });
116
+ await (0, import_processStream.processStream)(
117
+ eventStream([
118
+ {
119
+ name: "message:dequeued",
120
+ sessionId: "test-session",
121
+ count: 1,
122
+ ids: [steer.id],
123
+ queue: "steer",
124
+ coalesced: false,
125
+ content: steer.content,
126
+ messages: [steer]
127
+ }
128
+ ]),
129
+ setters,
130
+ {
131
+ useStreaming: true,
132
+ autoApproveEditsRef: { current: false },
133
+ bypassPermissionsRef: { current: false },
134
+ eventBus: { emit: import_vitest.vi.fn() }
135
+ }
136
+ );
137
+ (0, import_vitest.expect)(getSteerMessages()).toEqual([]);
138
+ (0, import_vitest.expect)(getQueuedMessages()).toEqual([followUp]);
139
+ });
91
140
  (0, import_vitest.it)("attaches streamed reasoning chunks to the assistant message", async () => {
92
141
  const { getMessages, getPendingMessages, setters } = createSetters();
93
142
  const events = [
@@ -234,6 +283,7 @@ function createSetters() {
234
283
  name: "llm:tool-call",
235
284
  sessionId: "test-session",
236
285
  toolName: "test-tool",
286
+ callId: "call-1",
237
287
  args: {}
238
288
  },
239
289
  {
@@ -24,10 +24,15 @@ async function* eventStream(events) {
24
24
  yield event;
25
25
  }
26
26
  }
27
- function createSetters() {
27
+ function createSetters({
28
+ steerMessages: initialSteerMessages = [],
29
+ queuedMessages: initialQueuedMessages = []
30
+ } = {}) {
28
31
  const messages = createState([]);
29
32
  const pendingMessages = createState([]);
30
33
  const dequeuedBuffer = createState([]);
34
+ const steerMessages = createState(initialSteerMessages);
35
+ const queuedMessages = createState(initialQueuedMessages);
31
36
  const ui = createState({
32
37
  isProcessing: false,
33
38
  isCancelling: false,
@@ -70,6 +75,8 @@ function createSetters() {
70
75
  return {
71
76
  getMessages: messages.get,
72
77
  getPendingMessages: pendingMessages.get,
78
+ getSteerMessages: steerMessages.get,
79
+ getQueuedMessages: queuedMessages.get,
73
80
  getUi: ui.get,
74
81
  setters: {
75
82
  setMessages: messages.set,
@@ -77,7 +84,8 @@ function createSetters() {
77
84
  setDequeuedBuffer: dequeuedBuffer.set,
78
85
  setUi: ui.set,
79
86
  setSession: session.set,
80
- setQueuedMessages: createNoopDispatch(),
87
+ setSteerMessages: steerMessages.set,
88
+ setQueuedMessages: queuedMessages.set,
81
89
  setApproval: createNoopDispatch(),
82
90
  setApprovalQueue: createNoopDispatch()
83
91
  }
@@ -87,6 +95,47 @@ describe("processStream (reasoning)", () => {
87
95
  beforeEach(() => {
88
96
  captureAnalyticsMock.mockClear();
89
97
  });
98
+ it("removes only dequeued queue ids from steer and follow-up state", async () => {
99
+ const steer = {
100
+ id: "steer-1",
101
+ content: [{ type: "text", text: "active steer" }],
102
+ queuedAt: Date.now(),
103
+ kind: "default"
104
+ };
105
+ const followUp = {
106
+ id: "follow-up-1",
107
+ content: [{ type: "text", text: "later" }],
108
+ queuedAt: Date.now(),
109
+ kind: "default"
110
+ };
111
+ const { getSteerMessages, getQueuedMessages, setters } = createSetters({
112
+ steerMessages: [steer],
113
+ queuedMessages: [followUp]
114
+ });
115
+ await processStream(
116
+ eventStream([
117
+ {
118
+ name: "message:dequeued",
119
+ sessionId: "test-session",
120
+ count: 1,
121
+ ids: [steer.id],
122
+ queue: "steer",
123
+ coalesced: false,
124
+ content: steer.content,
125
+ messages: [steer]
126
+ }
127
+ ]),
128
+ setters,
129
+ {
130
+ useStreaming: true,
131
+ autoApproveEditsRef: { current: false },
132
+ bypassPermissionsRef: { current: false },
133
+ eventBus: { emit: vi.fn() }
134
+ }
135
+ );
136
+ expect(getSteerMessages()).toEqual([]);
137
+ expect(getQueuedMessages()).toEqual([followUp]);
138
+ });
90
139
  it("attaches streamed reasoning chunks to the assistant message", async () => {
91
140
  const { getMessages, getPendingMessages, setters } = createSetters();
92
141
  const events = [
@@ -233,6 +282,7 @@ describe("processStream (reasoning)", () => {
233
282
  name: "llm:tool-call",
234
283
  sessionId: "test-session",
235
284
  toolName: "test-tool",
285
+ callId: "call-1",
236
286
  args: {}
237
287
  },
238
288
  {
@@ -30,7 +30,8 @@ function createInitialState(initialModelName = "") {
30
30
  draftBeforeHistory: "",
31
31
  images: [],
32
32
  pastedBlocks: [],
33
- pasteCounter: 0
33
+ pasteCounter: 0,
34
+ editingQueuedFollowUp: false
34
35
  },
35
36
  ui: {
36
37
  isProcessing: false,
@@ -1 +1 @@
1
- {"version":3,"file":"initialState.d.ts","sourceRoot":"","sources":["../../src/state/initialState.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,gBAAgB,GAAE,MAAW,GAAG,QAAQ,CAoD1E"}
1
+ {"version":3,"file":"initialState.d.ts","sourceRoot":"","sources":["../../src/state/initialState.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,gBAAgB,GAAE,MAAW,GAAG,QAAQ,CAqD1E"}
@@ -7,7 +7,8 @@ function createInitialState(initialModelName = "") {
7
7
  draftBeforeHistory: "",
8
8
  images: [],
9
9
  pastedBlocks: [],
10
- pasteCounter: 0
10
+ pasteCounter: 0,
11
+ editingQueuedFollowUp: false
11
12
  },
12
13
  ui: {
13
14
  isProcessing: false,
@@ -44,7 +44,8 @@ function cliReducer(state, action) {
44
44
  input: {
45
45
  ...state.input,
46
46
  value: "",
47
- historyIndex: -1
47
+ historyIndex: -1,
48
+ editingQueuedFollowUp: false
48
49
  }
49
50
  };
50
51
  case "INPUT_HISTORY_NAVIGATE": {
@@ -66,7 +67,8 @@ function cliReducer(state, action) {
66
67
  input: {
67
68
  ...state.input,
68
69
  value: "",
69
- historyIndex: -1
70
+ historyIndex: -1,
71
+ editingQueuedFollowUp: false
70
72
  }
71
73
  };
72
74
  }
@@ -78,7 +80,8 @@ function cliReducer(state, action) {
78
80
  input: {
79
81
  ...state.input,
80
82
  value: historyItem || "",
81
- historyIndex: newIndex
83
+ historyIndex: newIndex,
84
+ editingQueuedFollowUp: false
82
85
  }
83
86
  };
84
87
  }
@@ -87,7 +90,8 @@ function cliReducer(state, action) {
87
90
  ...state,
88
91
  input: {
89
92
  ...state.input,
90
- historyIndex: -1
93
+ historyIndex: -1,
94
+ editingQueuedFollowUp: false
91
95
  }
92
96
  };
93
97
  case "INPUT_HISTORY_ADD": {
@@ -101,7 +105,8 @@ function cliReducer(state, action) {
101
105
  ...state.input,
102
106
  history: [...history, action.value].slice(-100),
103
107
  // Keep last 100
104
- historyIndex: -1
108
+ historyIndex: -1,
109
+ editingQueuedFollowUp: false
105
110
  }
106
111
  };
107
112
  }
@@ -1 +1 @@
1
- {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/state/reducer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,GAAG,QAAQ,CAkWvE"}
1
+ {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/state/reducer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,GAAG,QAAQ,CAuWvE"}