mastracode 0.16.3-alpha.4 → 0.17.0-alpha.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # mastracode
2
2
 
3
+ ## 0.17.0-alpha.6
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies:
8
+ - @mastra/core@1.31.0-alpha.5
9
+
10
+ ## 0.17.0-alpha.5
11
+
12
+ ### Minor Changes
13
+
14
+ - Free-text answers to the `ask_user` tool can now span multiple lines. Press ([#15395](https://github.com/mastra-ai/mastra/pull/15395))
15
+ `Shift+Enter` or `\+Enter` to insert a newline, `Enter` to submit, and `Esc`
16
+ to cancel — long answers wrap inside the input box instead of scrolling
17
+ horizontally off-screen, and the raw text (including indentation and trailing
18
+ newlines) is forwarded to the agent intact.
19
+
20
+ Slash-command prompts that take short answers (paths, names, yes/no, model
21
+ picks) keep the existing single-line input, so muscle memory for those
22
+ prompts is unchanged.
23
+
24
+ Internally, this is opt-in via a new `multiline: true` flag on
25
+ `AskQuestionInlineComponent` / `AskQuestionDialogComponent`. The flag also
26
+ flows through `createStreaming` and `activate`, so the multiline editor is
27
+ available everywhere those components are mounted.
28
+
3
29
  ## 0.16.3-alpha.4
4
30
 
5
31
  ### Patch Changes
@@ -48,6 +48,87 @@ var process2__default = /*#__PURE__*/_interopDefault(process2);
48
48
  var stripAnsi__default = /*#__PURE__*/_interopDefault(stripAnsi);
49
49
  var fs4__default = /*#__PURE__*/_interopDefault(fs4);
50
50
 
51
+ var ANSI_STRIP_RE = /\x1b\[[0-9;]*m/g;
52
+ var MultilineInput = class {
53
+ editor;
54
+ _focused = false;
55
+ onSubmit;
56
+ onEscape;
57
+ allowEmptySubmit = false;
58
+ get focused() {
59
+ return this._focused;
60
+ }
61
+ set focused(value) {
62
+ this._focused = value;
63
+ }
64
+ constructor(tui, editorTheme) {
65
+ this.editor = new piTui.Editor(tui, editorTheme);
66
+ }
67
+ getText() {
68
+ return this.editor.getText();
69
+ }
70
+ setText(text) {
71
+ this.editor.setText(text);
72
+ }
73
+ handleInput(data) {
74
+ if (piTui.matchesKey(data, "escape")) {
75
+ this.onEscape?.();
76
+ return;
77
+ }
78
+ if (piTui.matchesKey(data, "enter")) {
79
+ const lines = this.editor.state?.lines;
80
+ const cursorCol = this.editor.state?.cursorCol;
81
+ const currentLine = lines?.[this.editor.state?.cursorLine] || "";
82
+ if (cursorCol > 0 && currentLine[cursorCol - 1] === "\\") {
83
+ this.editor.handleInput(data);
84
+ return;
85
+ }
86
+ const rawText = this.editor.getText();
87
+ if (rawText.trim() || this.allowEmptySubmit) {
88
+ this.onSubmit?.(rawText);
89
+ }
90
+ return;
91
+ }
92
+ if (piTui.matchesKey(data, "shift+enter")) {
93
+ this.editor.handleInput("\n");
94
+ return;
95
+ }
96
+ this.editor.handleInput(data);
97
+ }
98
+ invalidate() {
99
+ this.editor.invalidate();
100
+ }
101
+ /**
102
+ * Render the editor content, stripping the Editor's own border chrome
103
+ * so it can be embedded inside an existing bordered box.
104
+ */
105
+ render(width) {
106
+ const editorLines = this.editor.render(width);
107
+ const contentLines = [];
108
+ let pastFirstBorder = false;
109
+ for (const line of editorLines) {
110
+ const stripped = line.replace(ANSI_STRIP_RE, "");
111
+ if (stripped.length > 0 && /^─+$/.test(stripped)) {
112
+ if (!pastFirstBorder) {
113
+ pastFirstBorder = true;
114
+ }
115
+ continue;
116
+ }
117
+ if (/^─+\s*[↑↓]\s*─+$/.test(stripped.trim())) {
118
+ continue;
119
+ }
120
+ if (pastFirstBorder) {
121
+ contentLines.push(line);
122
+ }
123
+ }
124
+ if (contentLines.length === 0) {
125
+ contentLines.push("");
126
+ }
127
+ return contentLines;
128
+ }
129
+ };
130
+
131
+ // src/tui/components/ask-question-inline.ts
51
132
  var AskQuestionBorderedBox = class {
52
133
  questionLines;
53
134
  selectList;
@@ -185,10 +266,12 @@ var AskQuestionInlineComponent = class _AskQuestionInlineComponent extends piTui
185
266
  borderedBox;
186
267
  selectList;
187
268
  input;
269
+ tui;
188
270
  onSubmit;
189
271
  onCancel;
190
272
  isNegativeAnswer;
191
273
  allowEmptyInput = false;
274
+ multiline = false;
192
275
  answered = false;
193
276
  /**
194
277
  * Create a pre-answered instance for rendering from chat history.
@@ -210,8 +293,9 @@ var AskQuestionInlineComponent = class _AskQuestionInlineComponent extends piTui
210
293
  * Shows the bordered box with "…" indicator. Call updateArgs() as partial JSON
211
294
  * arrives, then activate() when the question event fires.
212
295
  */
213
- static createStreaming() {
296
+ static createStreaming(tui) {
214
297
  const component = new _AskQuestionInlineComponent();
298
+ component.tui = tui;
215
299
  return component;
216
300
  }
217
301
  _focused = false;
@@ -240,18 +324,20 @@ var AskQuestionInlineComponent = class _AskQuestionInlineComponent extends piTui
240
324
  */
241
325
  constructor(options, _ui) {
242
326
  super();
327
+ this.tui = _ui;
243
328
  if (options) {
244
329
  this.onSubmit = options.onSubmit;
245
330
  this.onCancel = options.onCancel;
246
331
  this.isNegativeAnswer = options.isNegativeAnswer;
247
332
  this.allowEmptyInput = Boolean(options.allowEmptyInput);
333
+ this.multiline = Boolean(options.multiline);
248
334
  const questionLines = options.question.split("\n");
249
335
  let hintText;
250
336
  if (options.options && options.options.length > 0) {
251
337
  hintText = "\u2191\u2193 to navigate \xB7 Enter to select \xB7 Esc to skip";
252
338
  this.buildSelectMode(options.options);
253
339
  } else {
254
- hintText = "Enter to submit \xB7 Esc to skip";
340
+ hintText = this.useMultiline() ? "Enter to submit \xB7 Shift+Enter/\\+Enter for new line \xB7 Esc to skip" : "Enter to submit \xB7 Esc to skip";
255
341
  this.buildInputMode();
256
342
  }
257
343
  this.borderedBox = new AskQuestionBorderedBox(
@@ -290,10 +376,12 @@ var AskQuestionInlineComponent = class _AskQuestionInlineComponent extends piTui
290
376
  */
291
377
  activate(options) {
292
378
  if (this.answered) return;
379
+ if (options.tui) this.tui = options.tui;
293
380
  this.onSubmit = options.onSubmit;
294
381
  this.onCancel = options.onCancel;
295
382
  this.isNegativeAnswer = options.isNegativeAnswer;
296
383
  this.allowEmptyInput = Boolean(options.allowEmptyInput);
384
+ this.multiline = Boolean(options.multiline);
297
385
  this.borderedBox.questionLines = options.question.split("\n");
298
386
  this.borderedBox.items = options.options || [];
299
387
  let hintText;
@@ -301,7 +389,7 @@ var AskQuestionInlineComponent = class _AskQuestionInlineComponent extends piTui
301
389
  hintText = "\u2191\u2193 to navigate \xB7 Enter to select \xB7 Esc to skip";
302
390
  this.buildSelectMode(options.options);
303
391
  } else {
304
- hintText = "Enter to submit \xB7 Esc to skip";
392
+ hintText = this.useMultiline() ? "Enter to submit \xB7 Shift+Enter/\\+Enter for new line \xB7 Esc to skip" : "Enter to submit \xB7 Esc to skip";
305
393
  this.buildInputMode();
306
394
  }
307
395
  this.borderedBox.setInteractive(this.selectList, this.input, hintText);
@@ -332,17 +420,40 @@ var AskQuestionInlineComponent = class _AskQuestionInlineComponent extends piTui
332
420
  this.selectList = void 0;
333
421
  this.buildInputMode();
334
422
  this.borderedBox.items = [];
335
- this.borderedBox.setInteractive(void 0, this.input, "Enter to submit \xB7 Esc to skip");
423
+ this.borderedBox.setInteractive(
424
+ void 0,
425
+ this.input,
426
+ this.useMultiline() ? "Enter to submit \xB7 Shift+Enter/\\+Enter for new line \xB7 Esc to skip" : "Enter to submit \xB7 Esc to skip"
427
+ );
428
+ }
429
+ /** Whether this prompt should render a multiline editor (vs a single-line input). */
430
+ useMultiline() {
431
+ return this.multiline && Boolean(this.tui);
336
432
  }
337
433
  buildInputMode() {
338
- this.input = new piTui.Input();
339
- this.input.onSubmit = (value) => {
340
- const trimmed = value.trim();
341
- if (trimmed || this.allowEmptyInput) {
342
- this.handleAnswer(trimmed);
343
- }
344
- };
345
- this.input.keybindings = piTui.getEditorKeybindings();
434
+ if (this.useMultiline()) {
435
+ const multilineInput = new MultilineInput(this.tui, chunkP6EKFEJB_cjs.getEditorTheme());
436
+ multilineInput.allowEmptySubmit = this.allowEmptyInput;
437
+ multilineInput.onSubmit = (value) => {
438
+ if (value.trim() || this.allowEmptyInput) {
439
+ this.handleAnswer(value);
440
+ }
441
+ };
442
+ multilineInput.onEscape = () => {
443
+ this.handleCancel();
444
+ };
445
+ this.input = multilineInput;
446
+ } else {
447
+ this.input = new piTui.Input();
448
+ this.input.onSubmit = (value) => {
449
+ const trimmed = value.trim();
450
+ if (trimmed || this.allowEmptyInput) {
451
+ this.handleAnswer(trimmed);
452
+ }
453
+ };
454
+ this.input.keybindings = piTui.getEditorKeybindings();
455
+ }
456
+ this.input.focused = this._focused;
346
457
  }
347
458
  handleAnswer(answer) {
348
459
  if (this.answered) return;
@@ -906,7 +1017,7 @@ function getInstallCommand(pm, version) {
906
1017
  }
907
1018
  function getCurrentVersion() {
908
1019
  {
909
- return "0.16.3-alpha.4";
1020
+ return "0.17.0-alpha.6";
910
1021
  }
911
1022
  }
912
1023
  async function fetchLatestVersion() {
@@ -10819,7 +10930,7 @@ function handleToolInputStart(ctx, toolCallId, toolName) {
10819
10930
  state.seenToolCallIds.add(toolCallId);
10820
10931
  }
10821
10932
  if (toolName === "ask_user") {
10822
- const askComponent = AskQuestionInlineComponent.createStreaming();
10933
+ const askComponent = AskQuestionInlineComponent.createStreaming(state.ui);
10823
10934
  ctx.addChildBeforeFollowUps(askComponent);
10824
10935
  state.lastAskUserComponent = askComponent;
10825
10936
  state.pendingAskUserComponents.set(toolCallId, askComponent);
@@ -11679,6 +11790,8 @@ var AskQuestionDialogComponent = class _AskQuestionDialogComponent extends piTui
11679
11790
  static CUSTOM_RESPONSE_VALUE = "__custom_response__";
11680
11791
  selectList;
11681
11792
  input;
11793
+ tui;
11794
+ multiline = false;
11682
11795
  onSubmit;
11683
11796
  onCancel;
11684
11797
  /** Children added by buildSelectMode/buildInputMode, tracked for removal on mode switch */
@@ -11695,6 +11808,8 @@ var AskQuestionDialogComponent = class _AskQuestionDialogComponent extends piTui
11695
11808
  super(2, 1, (text) => chunkP6EKFEJB_cjs.theme.bg("overlayBg", text));
11696
11809
  this.onSubmit = options.onSubmit;
11697
11810
  this.onCancel = options.onCancel;
11811
+ this.tui = options.tui;
11812
+ this.multiline = Boolean(options.multiline);
11698
11813
  this.addChild(new piTui.Text(chunkP6EKFEJB_cjs.theme.bold(chunkP6EKFEJB_cjs.theme.fg("accent", "Question")), 0, 0));
11699
11814
  this.addChild(new piTui.Spacer(1));
11700
11815
  for (const line of options.question.split("\n")) {
@@ -11736,14 +11851,31 @@ var AskQuestionDialogComponent = class _AskQuestionDialogComponent extends piTui
11736
11851
  this.addChild(hint);
11737
11852
  this.modeChildren.push(hint);
11738
11853
  }
11854
+ /** Whether this prompt should render a multiline editor (vs a single-line input). */
11855
+ useMultiline() {
11856
+ return this.multiline && Boolean(this.tui);
11857
+ }
11739
11858
  buildInputMode() {
11740
- this.input = new piTui.Input();
11741
- this.input.onSubmit = (value) => {
11742
- const trimmed = value.trim();
11743
- if (trimmed) {
11744
- this.onSubmit(trimmed);
11745
- }
11746
- };
11859
+ if (this.useMultiline()) {
11860
+ const multilineInput = new MultilineInput(this.tui, chunkP6EKFEJB_cjs.getEditorTheme());
11861
+ multilineInput.onSubmit = (value) => {
11862
+ if (value.trim()) {
11863
+ this.onSubmit(value);
11864
+ }
11865
+ };
11866
+ multilineInput.onEscape = () => {
11867
+ this.onCancel();
11868
+ };
11869
+ this.input = multilineInput;
11870
+ } else {
11871
+ this.input = new piTui.Input();
11872
+ this.input.onSubmit = (value) => {
11873
+ const trimmed = value.trim();
11874
+ if (trimmed) {
11875
+ this.onSubmit(trimmed);
11876
+ }
11877
+ };
11878
+ }
11747
11879
  this.modeChildren = [];
11748
11880
  const inputChild = this.input;
11749
11881
  this.addChild(inputChild);
@@ -11751,9 +11883,11 @@ var AskQuestionDialogComponent = class _AskQuestionDialogComponent extends piTui
11751
11883
  const spacer = new piTui.Spacer(1);
11752
11884
  this.addChild(spacer);
11753
11885
  this.modeChildren.push(spacer);
11754
- const hint = new piTui.Text(chunkP6EKFEJB_cjs.theme.fg("dim", " Enter to submit \xB7 Esc to skip"), 0, 0);
11886
+ const hintText = this.useMultiline() ? " Enter to submit \xB7 Shift+Enter for new line \xB7 \\+Enter for new line \xB7 Esc to skip" : " Enter to submit \xB7 Esc to skip";
11887
+ const hint = new piTui.Text(chunkP6EKFEJB_cjs.theme.fg("dim", hintText), 0, 0);
11755
11888
  this.addChild(hint);
11756
11889
  this.modeChildren.push(hint);
11890
+ this.input.focused = this._focused;
11757
11891
  }
11758
11892
  switchToCustomInput() {
11759
11893
  for (const child of this.modeChildren) {
@@ -11761,7 +11895,6 @@ var AskQuestionDialogComponent = class _AskQuestionDialogComponent extends piTui
11761
11895
  }
11762
11896
  this.selectList = void 0;
11763
11897
  this.buildInputMode();
11764
- if (this.input) this.input.focused = this._focused;
11765
11898
  }
11766
11899
  handleInput(data) {
11767
11900
  if (this.selectList) {
@@ -11796,6 +11929,8 @@ async function handleAskQuestion(ctx, questionId, question, options) {
11796
11929
  askUserComponent.activate({
11797
11930
  question,
11798
11931
  options,
11932
+ multiline: true,
11933
+ tui: state.ui,
11799
11934
  onSubmit: (answer) => {
11800
11935
  state.activeInlineQuestion = void 0;
11801
11936
  state.harness.respondToQuestion({ questionId, answer });
@@ -11815,6 +11950,7 @@ async function handleAskQuestion(ctx, questionId, question, options) {
11815
11950
  {
11816
11951
  question,
11817
11952
  options,
11953
+ multiline: true,
11818
11954
  onSubmit: (answer) => {
11819
11955
  state.activeInlineQuestion = void 0;
11820
11956
  state.harness.respondToQuestion({ questionId, answer });
@@ -11852,6 +11988,8 @@ async function handleAskQuestion(ctx, questionId, question, options) {
11852
11988
  const dialog = new AskQuestionDialogComponent({
11853
11989
  question,
11854
11990
  options,
11991
+ multiline: true,
11992
+ tui: state.ui,
11855
11993
  onSubmit: (answer) => {
11856
11994
  state.ui.hideOverlay();
11857
11995
  state.harness.respondToQuestion({ questionId, answer });
@@ -12972,7 +13110,7 @@ var IMAGE_MIME_TYPES_BY_EXTENSION = {
12972
13110
  ".heic": "image/heic",
12973
13111
  ".heif": "image/heif"
12974
13112
  };
12975
- var ANSI_STRIP_RE = /\x1b\[[0-9;]*m/g;
13113
+ var ANSI_STRIP_RE2 = /\x1b\[[0-9;]*m/g;
12976
13114
  var SLASH_CURSOR_RE = /\x1b\[7m\/\x1b\[0m/;
12977
13115
  var AT_CURSOR_RE = /\x1b\[7m@\x1b\[0m/;
12978
13116
  function parseHex(hex) {
@@ -13099,7 +13237,7 @@ var CustomEditor = class extends piTui.Editor {
13099
13237
  const scrollIndicators = [];
13100
13238
  let isTop = true;
13101
13239
  for (const line of editorLines) {
13102
- const stripped = line.replace(ANSI_STRIP_RE, "");
13240
+ const stripped = line.replace(ANSI_STRIP_RE2, "");
13103
13241
  if (stripped.length > 0 && stripped[0] === "\u2500") {
13104
13242
  if (isTop) {
13105
13243
  isTop = false;
@@ -14416,5 +14554,5 @@ exports.createTUIState = createTUIState;
14416
14554
  exports.detectTerminalTheme = detectTerminalTheme;
14417
14555
  exports.formatOMStatus = formatOMStatus;
14418
14556
  exports.getCurrentVersion = getCurrentVersion;
14419
- //# sourceMappingURL=chunk-VCJ7OH76.cjs.map
14420
- //# sourceMappingURL=chunk-VCJ7OH76.cjs.map
14557
+ //# sourceMappingURL=chunk-INCBH57P.cjs.map
14558
+ //# sourceMappingURL=chunk-INCBH57P.cjs.map