mastracode 0.44.2-alpha.0 → 0.45.0-alpha.2
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/dist/cli.cjs +60 -12
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +60 -12
- package/dist/cli.js.map +1 -1
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/resume-command.d.ts +5 -0
- package/dist/resume-command.d.ts.map +1 -0
- package/dist/tui/command-dispatch.d.ts.map +1 -1
- package/dist/tui/components/help-overlay.d.ts.map +1 -1
- package/dist/tui/mastra-tui.d.ts +3 -0
- package/dist/tui/mastra-tui.d.ts.map +1 -1
- package/dist/tui/setup.d.ts +0 -1
- package/dist/tui/setup.d.ts.map +1 -1
- package/dist/tui/state.d.ts +2 -0
- package/dist/tui/state.d.ts.map +1 -1
- package/dist/tui/thread-startup.d.ts +17 -0
- package/dist/tui/thread-startup.d.ts.map +1 -0
- package/dist/{tui-Be7-xm5L.cjs → tui-CPJv6T_R.cjs} +307 -241
- package/dist/tui-CPJv6T_R.cjs.map +1 -0
- package/dist/{tui-ClYkgBN3.js → tui-CUNuxs-4.js} +307 -241
- package/dist/tui-CUNuxs-4.js.map +1 -0
- package/dist/tui.cjs +1 -1
- package/dist/tui.js +1 -1
- package/package.json +11 -11
- package/dist/tui-Be7-xm5L.cjs.map +0 -1
- package/dist/tui-ClYkgBN3.js.map +0 -1
|
@@ -2535,10 +2535,18 @@ function getCommands(modes) {
|
|
|
2535
2535
|
key: "/new",
|
|
2536
2536
|
description: "Start a new thread"
|
|
2537
2537
|
},
|
|
2538
|
+
{
|
|
2539
|
+
key: "/clone",
|
|
2540
|
+
description: "Clone the current thread"
|
|
2541
|
+
},
|
|
2538
2542
|
{
|
|
2539
2543
|
key: "/threads",
|
|
2540
2544
|
description: "Switch between threads"
|
|
2541
2545
|
},
|
|
2546
|
+
{
|
|
2547
|
+
key: "/resume",
|
|
2548
|
+
description: "Alias for /threads"
|
|
2549
|
+
},
|
|
2542
2550
|
{
|
|
2543
2551
|
key: "/thread",
|
|
2544
2552
|
description: "Show current thread info"
|
|
@@ -2551,6 +2559,10 @@ function getCommands(modes) {
|
|
|
2551
2559
|
key: "/name",
|
|
2552
2560
|
description: "Rename current thread"
|
|
2553
2561
|
},
|
|
2562
|
+
{
|
|
2563
|
+
key: "/rename",
|
|
2564
|
+
description: "Alias for /name"
|
|
2565
|
+
},
|
|
2554
2566
|
{
|
|
2555
2567
|
key: "/resource",
|
|
2556
2568
|
description: "Show/switch resource ID"
|
|
@@ -20967,6 +20979,8 @@ const TRACKED_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
20967
20979
|
"memory-gateway",
|
|
20968
20980
|
"custom-providers",
|
|
20969
20981
|
"threads",
|
|
20982
|
+
"resume",
|
|
20983
|
+
"clone",
|
|
20970
20984
|
"new"
|
|
20971
20985
|
]);
|
|
20972
20986
|
/**
|
|
@@ -21038,6 +21052,7 @@ async function dispatchSlashCommand(input, state, buildCtx) {
|
|
|
21038
21052
|
await handleCloneCommand(ctx);
|
|
21039
21053
|
return true;
|
|
21040
21054
|
case "threads":
|
|
21055
|
+
case "resume":
|
|
21041
21056
|
await handleThreadsCommand(ctx);
|
|
21042
21057
|
return true;
|
|
21043
21058
|
case "thread":
|
|
@@ -21120,6 +21135,7 @@ async function dispatchSlashCommand(input, state, buildCtx) {
|
|
|
21120
21135
|
await handleDiffCommand(ctx, args[0]);
|
|
21121
21136
|
return true;
|
|
21122
21137
|
case "name":
|
|
21138
|
+
case "rename":
|
|
21123
21139
|
await handleNameCommand(ctx, args);
|
|
21124
21140
|
return true;
|
|
21125
21141
|
case "resource":
|
|
@@ -25902,120 +25918,6 @@ function formatIdleStatusTimingSegments(state, now = Date.now()) {
|
|
|
25902
25918
|
} : null;
|
|
25903
25919
|
}
|
|
25904
25920
|
//#endregion
|
|
25905
|
-
//#region src/tui/components/simple-progress.ts
|
|
25906
|
-
/**
|
|
25907
|
-
* Simple progress indicator component for single operations.
|
|
25908
|
-
* Shows a compact progress display with spinner and status text.
|
|
25909
|
-
*/
|
|
25910
|
-
/**
|
|
25911
|
-
* Lightweight progress indicator for simple operations.
|
|
25912
|
-
* Features:
|
|
25913
|
-
* - Animated spinner
|
|
25914
|
-
* - Custom status text
|
|
25915
|
-
* - Optional elapsed time
|
|
25916
|
-
* - Optional percentage display
|
|
25917
|
-
*/
|
|
25918
|
-
var SimpleProgressComponent = class extends _earendil_works_pi_tui.Container {
|
|
25919
|
-
status = "";
|
|
25920
|
-
progress;
|
|
25921
|
-
startTime;
|
|
25922
|
-
spinnerFrame = 0;
|
|
25923
|
-
lastRenderTime = 0;
|
|
25924
|
-
isActive = false;
|
|
25925
|
-
options;
|
|
25926
|
-
constructor(options = {}) {
|
|
25927
|
-
super();
|
|
25928
|
-
this.options = {
|
|
25929
|
-
prefix: "",
|
|
25930
|
-
showElapsed: true,
|
|
25931
|
-
showPercentage: true,
|
|
25932
|
-
...options
|
|
25933
|
-
};
|
|
25934
|
-
this.startTime = Date.now();
|
|
25935
|
-
}
|
|
25936
|
-
/**
|
|
25937
|
-
* Start showing progress with initial status.
|
|
25938
|
-
*/
|
|
25939
|
-
start(status) {
|
|
25940
|
-
this.status = status;
|
|
25941
|
-
this.isActive = true;
|
|
25942
|
-
this.startTime = Date.now();
|
|
25943
|
-
this.rebuild();
|
|
25944
|
-
}
|
|
25945
|
-
/**
|
|
25946
|
-
* Update the status text.
|
|
25947
|
-
*/
|
|
25948
|
-
updateStatus(status) {
|
|
25949
|
-
this.status = status;
|
|
25950
|
-
this.rebuild();
|
|
25951
|
-
}
|
|
25952
|
-
/**
|
|
25953
|
-
* Update the progress percentage (0-100).
|
|
25954
|
-
*/
|
|
25955
|
-
updateProgress(progress) {
|
|
25956
|
-
this.progress = Math.min(100, Math.max(0, progress));
|
|
25957
|
-
this.rebuild();
|
|
25958
|
-
}
|
|
25959
|
-
/**
|
|
25960
|
-
* Mark the operation as complete.
|
|
25961
|
-
*/
|
|
25962
|
-
complete(message) {
|
|
25963
|
-
this.isActive = false;
|
|
25964
|
-
this.status = message || "Complete";
|
|
25965
|
-
this.progress = 100;
|
|
25966
|
-
this.rebuild();
|
|
25967
|
-
}
|
|
25968
|
-
/**
|
|
25969
|
-
* Mark the operation as failed.
|
|
25970
|
-
*/
|
|
25971
|
-
fail(error) {
|
|
25972
|
-
this.isActive = false;
|
|
25973
|
-
this.status = error;
|
|
25974
|
-
this.rebuild();
|
|
25975
|
-
}
|
|
25976
|
-
rebuild() {
|
|
25977
|
-
this.clear();
|
|
25978
|
-
if (!this.status) return;
|
|
25979
|
-
const now = Date.now();
|
|
25980
|
-
if (this.isActive && now - this.lastRenderTime > 100) {
|
|
25981
|
-
this.spinnerFrame = (this.spinnerFrame + 1) % 10;
|
|
25982
|
-
this.lastRenderTime = now;
|
|
25983
|
-
}
|
|
25984
|
-
let text = "";
|
|
25985
|
-
if (this.options.prefix) text += this.options.prefix + " ";
|
|
25986
|
-
if (this.isActive) {
|
|
25987
|
-
const spinner = this.getSpinner();
|
|
25988
|
-
text += theme.fg("accent", spinner) + " ";
|
|
25989
|
-
} else if (this.progress === 100) text += theme.fg("success", "✓") + " ";
|
|
25990
|
-
else text += theme.fg("error", "✗") + " ";
|
|
25991
|
-
text += this.status;
|
|
25992
|
-
if (this.options.showPercentage && this.progress !== void 0) text += theme.fg("dim", ` (${this.progress}%)`);
|
|
25993
|
-
if (this.options.showElapsed && this.isActive) {
|
|
25994
|
-
const elapsed = Math.floor((now - this.startTime) / 1e3);
|
|
25995
|
-
text += theme.fg("dim", ` ${elapsed}s`);
|
|
25996
|
-
}
|
|
25997
|
-
this.addChild(new _earendil_works_pi_tui.Text(text, 0, 0));
|
|
25998
|
-
}
|
|
25999
|
-
getSpinner() {
|
|
26000
|
-
return [
|
|
26001
|
-
"⠋",
|
|
26002
|
-
"⠙",
|
|
26003
|
-
"⠹",
|
|
26004
|
-
"⠸",
|
|
26005
|
-
"⠼",
|
|
26006
|
-
"⠴",
|
|
26007
|
-
"⠦",
|
|
26008
|
-
"⠧",
|
|
26009
|
-
"⠇",
|
|
26010
|
-
"⠏"
|
|
26011
|
-
][this.spinnerFrame];
|
|
26012
|
-
}
|
|
26013
|
-
render(maxWidth) {
|
|
26014
|
-
this.rebuild();
|
|
26015
|
-
return super.render(maxWidth);
|
|
26016
|
-
}
|
|
26017
|
-
};
|
|
26018
|
-
//#endregion
|
|
26019
25921
|
//#region src/tui/setup.ts
|
|
26020
25922
|
/**
|
|
26021
25923
|
* TUI setup: keyboard shortcuts, layout building, autocomplete, key handlers.
|
|
@@ -26279,6 +26181,10 @@ function setupAutocomplete(state) {
|
|
|
26279
26181
|
name: "threads",
|
|
26280
26182
|
description: "Switch between threads"
|
|
26281
26183
|
},
|
|
26184
|
+
{
|
|
26185
|
+
name: "resume",
|
|
26186
|
+
description: "Alias for /threads"
|
|
26187
|
+
},
|
|
26282
26188
|
{
|
|
26283
26189
|
name: "models",
|
|
26284
26190
|
description: "Switch model pack"
|
|
@@ -26348,6 +26254,10 @@ function setupAutocomplete(state) {
|
|
|
26348
26254
|
name: "name",
|
|
26349
26255
|
description: "Rename current thread"
|
|
26350
26256
|
},
|
|
26257
|
+
{
|
|
26258
|
+
name: "rename",
|
|
26259
|
+
description: "Alias for /name"
|
|
26260
|
+
},
|
|
26351
26261
|
{
|
|
26352
26262
|
name: "resource",
|
|
26353
26263
|
description: "Show/switch resource ID (tag for sharing)"
|
|
@@ -26716,94 +26626,6 @@ function subscribeToAgentController(state, handleEvent) {
|
|
|
26716
26626
|
state.waitForAgentControllerEvents = state.options.backgroundToolsEnabled ? () => eventQueue : void 0;
|
|
26717
26627
|
state.unsubscribe = state.session.subscribe(listener);
|
|
26718
26628
|
}
|
|
26719
|
-
async function promptForThreadSelection(state) {
|
|
26720
|
-
const currentPath = state.projectInfo.rootPath;
|
|
26721
|
-
const currentResourceId = state.session.identity.getResourceId();
|
|
26722
|
-
const allThreads = await state.session.thread.list();
|
|
26723
|
-
const activeThreadId = state.session.thread.getId();
|
|
26724
|
-
const taggedThreads = allThreads.filter((t) => {
|
|
26725
|
-
const threadPath = t.metadata?.projectPath;
|
|
26726
|
-
return !!threadPath && threadPath === currentPath;
|
|
26727
|
-
});
|
|
26728
|
-
const threads = [];
|
|
26729
|
-
for (const thread of taggedThreads) {
|
|
26730
|
-
if (thread.id === activeThreadId && !thread.title) {
|
|
26731
|
-
if ((await state.session.thread.listMessages({
|
|
26732
|
-
threadId: thread.id,
|
|
26733
|
-
limit: 1
|
|
26734
|
-
})).length === 0) continue;
|
|
26735
|
-
}
|
|
26736
|
-
threads.push(thread);
|
|
26737
|
-
}
|
|
26738
|
-
if (threads.length === 0) {
|
|
26739
|
-
const [thread] = [...(await state.session.thread.list({
|
|
26740
|
-
allResources: true,
|
|
26741
|
-
metadata: { projectPath: currentPath }
|
|
26742
|
-
})).filter((t) => t.resourceId !== currentResourceId)].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
26743
|
-
if (thread) {
|
|
26744
|
-
if (await askModalQuestion(state.ui, {
|
|
26745
|
-
question: [
|
|
26746
|
-
"This directory is tagged on a different resource.",
|
|
26747
|
-
"",
|
|
26748
|
-
`Project: ${currentPath}`,
|
|
26749
|
-
`Thread: ${thread.title || thread.id}`,
|
|
26750
|
-
`Old resource: ${thread.resourceId}`,
|
|
26751
|
-
`Current resource: ${currentResourceId}`,
|
|
26752
|
-
"",
|
|
26753
|
-
"Clone this thread into the current resource and resume the clone?"
|
|
26754
|
-
].join("\n"),
|
|
26755
|
-
options: [{ label: "Clone and resume" }, { label: "Start fresh" }],
|
|
26756
|
-
selectedOptionLabel: "Clone and resume",
|
|
26757
|
-
allowCustomResponse: false,
|
|
26758
|
-
overlay: {
|
|
26759
|
-
widthPercent: 80,
|
|
26760
|
-
maxHeight: "70%"
|
|
26761
|
-
}
|
|
26762
|
-
}) === "Clone and resume") {
|
|
26763
|
-
const progress = new SimpleProgressComponent({
|
|
26764
|
-
showElapsed: false,
|
|
26765
|
-
showPercentage: false
|
|
26766
|
-
});
|
|
26767
|
-
progress.start("Cloning thread into the current resource...");
|
|
26768
|
-
showModalOverlay(state.ui, progress, {
|
|
26769
|
-
widthPercent: 70,
|
|
26770
|
-
maxHeight: "40%",
|
|
26771
|
-
minHeightPercent: .35
|
|
26772
|
-
});
|
|
26773
|
-
state.ui.requestRender();
|
|
26774
|
-
try {
|
|
26775
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
26776
|
-
progress.updateStatus("Loading cloned thread...");
|
|
26777
|
-
state.ui.requestRender();
|
|
26778
|
-
await state.session.thread.cloneToCurrentResource({
|
|
26779
|
-
threadId: thread.id,
|
|
26780
|
-
expectedResourceId: thread.resourceId,
|
|
26781
|
-
expectedProjectPath: currentPath
|
|
26782
|
-
});
|
|
26783
|
-
} finally {
|
|
26784
|
-
state.ui.hideOverlay();
|
|
26785
|
-
state.ui.requestRender();
|
|
26786
|
-
}
|
|
26787
|
-
return;
|
|
26788
|
-
}
|
|
26789
|
-
}
|
|
26790
|
-
state.pendingNewThread = true;
|
|
26791
|
-
return;
|
|
26792
|
-
}
|
|
26793
|
-
const sortedThreads = [...threads].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
26794
|
-
for (const thread of sortedThreads) try {
|
|
26795
|
-
await state.session.thread.switch({ threadId: thread.id });
|
|
26796
|
-
if (!thread.metadata?.projectPath) await state.session.thread.setSetting({
|
|
26797
|
-
key: "projectPath",
|
|
26798
|
-
value: currentPath
|
|
26799
|
-
});
|
|
26800
|
-
return;
|
|
26801
|
-
} catch (error) {
|
|
26802
|
-
if (error instanceof _mastra_code_sdk_utils_thread_lock.ThreadLockError) continue;
|
|
26803
|
-
throw error;
|
|
26804
|
-
}
|
|
26805
|
-
state.pendingNewThread = true;
|
|
26806
|
-
}
|
|
26807
26629
|
async function renderExistingTasks(state) {
|
|
26808
26630
|
try {
|
|
26809
26631
|
const tasks = state.session.displayState.get().tasks;
|
|
@@ -26984,6 +26806,239 @@ async function handleShellPassthrough(state, command) {
|
|
|
26984
26806
|
}
|
|
26985
26807
|
}
|
|
26986
26808
|
//#endregion
|
|
26809
|
+
//#region src/tui/components/simple-progress.ts
|
|
26810
|
+
/**
|
|
26811
|
+
* Simple progress indicator component for single operations.
|
|
26812
|
+
* Shows a compact progress display with spinner and status text.
|
|
26813
|
+
*/
|
|
26814
|
+
/**
|
|
26815
|
+
* Lightweight progress indicator for simple operations.
|
|
26816
|
+
* Features:
|
|
26817
|
+
* - Animated spinner
|
|
26818
|
+
* - Custom status text
|
|
26819
|
+
* - Optional elapsed time
|
|
26820
|
+
* - Optional percentage display
|
|
26821
|
+
*/
|
|
26822
|
+
var SimpleProgressComponent = class extends _earendil_works_pi_tui.Container {
|
|
26823
|
+
status = "";
|
|
26824
|
+
progress;
|
|
26825
|
+
startTime;
|
|
26826
|
+
spinnerFrame = 0;
|
|
26827
|
+
lastRenderTime = 0;
|
|
26828
|
+
isActive = false;
|
|
26829
|
+
options;
|
|
26830
|
+
constructor(options = {}) {
|
|
26831
|
+
super();
|
|
26832
|
+
this.options = {
|
|
26833
|
+
prefix: "",
|
|
26834
|
+
showElapsed: true,
|
|
26835
|
+
showPercentage: true,
|
|
26836
|
+
...options
|
|
26837
|
+
};
|
|
26838
|
+
this.startTime = Date.now();
|
|
26839
|
+
}
|
|
26840
|
+
/**
|
|
26841
|
+
* Start showing progress with initial status.
|
|
26842
|
+
*/
|
|
26843
|
+
start(status) {
|
|
26844
|
+
this.status = status;
|
|
26845
|
+
this.isActive = true;
|
|
26846
|
+
this.startTime = Date.now();
|
|
26847
|
+
this.rebuild();
|
|
26848
|
+
}
|
|
26849
|
+
/**
|
|
26850
|
+
* Update the status text.
|
|
26851
|
+
*/
|
|
26852
|
+
updateStatus(status) {
|
|
26853
|
+
this.status = status;
|
|
26854
|
+
this.rebuild();
|
|
26855
|
+
}
|
|
26856
|
+
/**
|
|
26857
|
+
* Update the progress percentage (0-100).
|
|
26858
|
+
*/
|
|
26859
|
+
updateProgress(progress) {
|
|
26860
|
+
this.progress = Math.min(100, Math.max(0, progress));
|
|
26861
|
+
this.rebuild();
|
|
26862
|
+
}
|
|
26863
|
+
/**
|
|
26864
|
+
* Mark the operation as complete.
|
|
26865
|
+
*/
|
|
26866
|
+
complete(message) {
|
|
26867
|
+
this.isActive = false;
|
|
26868
|
+
this.status = message || "Complete";
|
|
26869
|
+
this.progress = 100;
|
|
26870
|
+
this.rebuild();
|
|
26871
|
+
}
|
|
26872
|
+
/**
|
|
26873
|
+
* Mark the operation as failed.
|
|
26874
|
+
*/
|
|
26875
|
+
fail(error) {
|
|
26876
|
+
this.isActive = false;
|
|
26877
|
+
this.status = error;
|
|
26878
|
+
this.rebuild();
|
|
26879
|
+
}
|
|
26880
|
+
rebuild() {
|
|
26881
|
+
this.clear();
|
|
26882
|
+
if (!this.status) return;
|
|
26883
|
+
const now = Date.now();
|
|
26884
|
+
if (this.isActive && now - this.lastRenderTime > 100) {
|
|
26885
|
+
this.spinnerFrame = (this.spinnerFrame + 1) % 10;
|
|
26886
|
+
this.lastRenderTime = now;
|
|
26887
|
+
}
|
|
26888
|
+
let text = "";
|
|
26889
|
+
if (this.options.prefix) text += this.options.prefix + " ";
|
|
26890
|
+
if (this.isActive) {
|
|
26891
|
+
const spinner = this.getSpinner();
|
|
26892
|
+
text += theme.fg("accent", spinner) + " ";
|
|
26893
|
+
} else if (this.progress === 100) text += theme.fg("success", "✓") + " ";
|
|
26894
|
+
else text += theme.fg("error", "✗") + " ";
|
|
26895
|
+
text += this.status;
|
|
26896
|
+
if (this.options.showPercentage && this.progress !== void 0) text += theme.fg("dim", ` (${this.progress}%)`);
|
|
26897
|
+
if (this.options.showElapsed && this.isActive) {
|
|
26898
|
+
const elapsed = Math.floor((now - this.startTime) / 1e3);
|
|
26899
|
+
text += theme.fg("dim", ` ${elapsed}s`);
|
|
26900
|
+
}
|
|
26901
|
+
this.addChild(new _earendil_works_pi_tui.Text(text, 0, 0));
|
|
26902
|
+
}
|
|
26903
|
+
getSpinner() {
|
|
26904
|
+
return [
|
|
26905
|
+
"⠋",
|
|
26906
|
+
"⠙",
|
|
26907
|
+
"⠹",
|
|
26908
|
+
"⠸",
|
|
26909
|
+
"⠼",
|
|
26910
|
+
"⠴",
|
|
26911
|
+
"⠦",
|
|
26912
|
+
"⠧",
|
|
26913
|
+
"⠇",
|
|
26914
|
+
"⠏"
|
|
26915
|
+
][this.spinnerFrame];
|
|
26916
|
+
}
|
|
26917
|
+
render(maxWidth) {
|
|
26918
|
+
this.rebuild();
|
|
26919
|
+
return super.render(maxWidth);
|
|
26920
|
+
}
|
|
26921
|
+
};
|
|
26922
|
+
//#endregion
|
|
26923
|
+
//#region src/tui/thread-startup.ts
|
|
26924
|
+
/**
|
|
26925
|
+
* Selects the thread to open at startup. A requested thread that is missing or
|
|
26926
|
+
* locked does not throw; the session is left pending a new thread and the issue
|
|
26927
|
+
* is returned so the caller can tell the user once the UI is ready.
|
|
26928
|
+
*/
|
|
26929
|
+
async function resumeThreadOnStartup(state, requestedThreadId) {
|
|
26930
|
+
const currentPath = state.projectInfo.rootPath;
|
|
26931
|
+
const allThreads = await state.session.thread.list(requestedThreadId ? { allResources: true } : void 0);
|
|
26932
|
+
const activeThreadId = state.session.thread.getId();
|
|
26933
|
+
if (requestedThreadId) {
|
|
26934
|
+
const thread = allThreads.find((candidate) => candidate.id === requestedThreadId);
|
|
26935
|
+
if (!thread) {
|
|
26936
|
+
await startPendingNewThread(state);
|
|
26937
|
+
return {
|
|
26938
|
+
kind: "missing",
|
|
26939
|
+
threadId: requestedThreadId
|
|
26940
|
+
};
|
|
26941
|
+
}
|
|
26942
|
+
const originalResourceId = state.session.identity.getResourceId();
|
|
26943
|
+
if (thread.resourceId !== originalResourceId) await state.controller.setResourceId(state.session, { resourceId: thread.resourceId });
|
|
26944
|
+
if (requestedThreadId !== activeThreadId) try {
|
|
26945
|
+
await state.session.thread.switch({ threadId: requestedThreadId });
|
|
26946
|
+
} catch (error) {
|
|
26947
|
+
if (!(error instanceof _mastra_code_sdk_utils_thread_lock.ThreadLockError)) throw error;
|
|
26948
|
+
if (state.session.identity.getResourceId() !== originalResourceId) await state.controller.setResourceId(state.session, { resourceId: originalResourceId });
|
|
26949
|
+
await startPendingNewThread(state);
|
|
26950
|
+
return {
|
|
26951
|
+
kind: "locked",
|
|
26952
|
+
threadId: thread.id,
|
|
26953
|
+
title: thread.title || thread.id,
|
|
26954
|
+
ownerPid: error.ownerPid
|
|
26955
|
+
};
|
|
26956
|
+
}
|
|
26957
|
+
state.pendingNewThread = false;
|
|
26958
|
+
return;
|
|
26959
|
+
}
|
|
26960
|
+
const threads = allThreads.filter((thread) => thread.metadata?.projectPath === currentPath);
|
|
26961
|
+
if (threads.length === 0) {
|
|
26962
|
+
if (await cloneDriftedThread(state)) return;
|
|
26963
|
+
state.pendingNewThread = true;
|
|
26964
|
+
return;
|
|
26965
|
+
}
|
|
26966
|
+
for (const thread of [...threads].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())) {
|
|
26967
|
+
if (thread.id === state.session.thread.getId()) return;
|
|
26968
|
+
try {
|
|
26969
|
+
await state.session.thread.switch({ threadId: thread.id });
|
|
26970
|
+
return;
|
|
26971
|
+
} catch (error) {
|
|
26972
|
+
if (error instanceof _mastra_code_sdk_utils_thread_lock.ThreadLockError) continue;
|
|
26973
|
+
throw error;
|
|
26974
|
+
}
|
|
26975
|
+
}
|
|
26976
|
+
state.pendingNewThread = true;
|
|
26977
|
+
}
|
|
26978
|
+
/**
|
|
26979
|
+
* Leave the session waiting for a new thread. Session creation may already have
|
|
26980
|
+
* bound the latest project thread; unbind it (and release its lock) so the UI
|
|
26981
|
+
* and exit hint don't show a thread the user didn't ask for.
|
|
26982
|
+
*/
|
|
26983
|
+
async function startPendingNewThread(state) {
|
|
26984
|
+
state.session.thread.cleanupSubscription();
|
|
26985
|
+
await state.session.thread.clearAndReleaseLock();
|
|
26986
|
+
state.pendingNewThread = true;
|
|
26987
|
+
}
|
|
26988
|
+
async function cloneDriftedThread(state) {
|
|
26989
|
+
const currentPath = state.projectInfo.rootPath;
|
|
26990
|
+
const currentResourceId = state.session.identity.getResourceId();
|
|
26991
|
+
const thread = [...(await state.session.thread.list({
|
|
26992
|
+
allResources: true,
|
|
26993
|
+
metadata: { projectPath: currentPath }
|
|
26994
|
+
})).filter((thread) => thread.resourceId !== currentResourceId)].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())[0];
|
|
26995
|
+
if (!thread) return false;
|
|
26996
|
+
if (await askModalQuestion(state.ui, {
|
|
26997
|
+
question: [
|
|
26998
|
+
"This directory is tagged on a different resource.",
|
|
26999
|
+
"",
|
|
27000
|
+
`Project: ${currentPath}`,
|
|
27001
|
+
`Thread: ${thread.title || thread.id}`,
|
|
27002
|
+
`Old resource: ${thread.resourceId}`,
|
|
27003
|
+
`Current resource: ${currentResourceId}`,
|
|
27004
|
+
"",
|
|
27005
|
+
"Clone this thread into the current resource and resume the clone?"
|
|
27006
|
+
].join("\n"),
|
|
27007
|
+
options: [{ label: "Clone and resume" }, { label: "Start fresh" }],
|
|
27008
|
+
selectedOptionLabel: "Clone and resume",
|
|
27009
|
+
allowCustomResponse: false,
|
|
27010
|
+
overlay: {
|
|
27011
|
+
widthPercent: 80,
|
|
27012
|
+
maxHeight: "70%"
|
|
27013
|
+
}
|
|
27014
|
+
}) !== "Clone and resume") return false;
|
|
27015
|
+
const progress = new SimpleProgressComponent({
|
|
27016
|
+
showElapsed: false,
|
|
27017
|
+
showPercentage: false
|
|
27018
|
+
});
|
|
27019
|
+
progress.start("Cloning thread into the current resource...");
|
|
27020
|
+
showModalOverlay(state.ui, progress, {
|
|
27021
|
+
widthPercent: 70,
|
|
27022
|
+
maxHeight: "40%",
|
|
27023
|
+
minHeightPercent: .35
|
|
27024
|
+
});
|
|
27025
|
+
state.ui.requestRender();
|
|
27026
|
+
try {
|
|
27027
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
27028
|
+
progress.updateStatus("Loading cloned thread...");
|
|
27029
|
+
state.ui.requestRender();
|
|
27030
|
+
await state.session.thread.cloneToCurrentResource({
|
|
27031
|
+
threadId: thread.id,
|
|
27032
|
+
expectedResourceId: thread.resourceId,
|
|
27033
|
+
expectedProjectPath: currentPath
|
|
27034
|
+
});
|
|
27035
|
+
} finally {
|
|
27036
|
+
state.ui.hideOverlay();
|
|
27037
|
+
state.ui.requestRender();
|
|
27038
|
+
}
|
|
27039
|
+
return true;
|
|
27040
|
+
}
|
|
27041
|
+
//#endregion
|
|
26987
27042
|
//#region src/tui/mastra-tui.ts
|
|
26988
27043
|
/**
|
|
26989
27044
|
* Main TUI class for Mastra Code.
|
|
@@ -27131,6 +27186,7 @@ var MastraTUI = class MastraTUI {
|
|
|
27131
27186
|
clearFinishedBackgroundActivities: () => this.clearFinishedBackgroundActivity()
|
|
27132
27187
|
} : {}
|
|
27133
27188
|
});
|
|
27189
|
+
this.installUserInputHandler();
|
|
27134
27190
|
}
|
|
27135
27191
|
getCurrentThreadBackgroundActivities() {
|
|
27136
27192
|
return getBackgroundActivitiesForTarget(this.state.backgroundActivities, this.state.session.identity.getResourceId(), this.state.pendingNewThread ? null : this.state.session.thread.getId());
|
|
@@ -27380,6 +27436,10 @@ var MastraTUI = class MastraTUI {
|
|
|
27380
27436
|
updateStatusLine(this.state);
|
|
27381
27437
|
flushRender(this.state);
|
|
27382
27438
|
}
|
|
27439
|
+
/** The thread to suggest resuming on exit, or null while waiting for a new thread. */
|
|
27440
|
+
getResumeThreadId() {
|
|
27441
|
+
return this.state.pendingNewThread ? null : this.state.session.thread.getId();
|
|
27442
|
+
}
|
|
27383
27443
|
/**
|
|
27384
27444
|
* Stop the TUI and clean up.
|
|
27385
27445
|
*/
|
|
@@ -27453,7 +27513,8 @@ var MastraTUI = class MastraTUI {
|
|
|
27453
27513
|
doubleCtrlCMs: MastraTUI.DOUBLE_CTRL_C_MS
|
|
27454
27514
|
});
|
|
27455
27515
|
this.state.ui.start();
|
|
27456
|
-
await
|
|
27516
|
+
const startupResumeIssue = await resumeThreadOnStartup(this.state, this.state.options.resumeThreadId);
|
|
27517
|
+
await this.syncThreadActivePackMetadata();
|
|
27457
27518
|
subscribeToAgentController(this.state, (event) => this.handleEvent(event));
|
|
27458
27519
|
if (this.state.options.backgroundToolsEnabled) this.cleanupBackgroundCompletionListener = this.state.options.backgroundCompletionEvents?.subscribe((event) => this.handleBackgroundCompletion(event));
|
|
27459
27520
|
if (this.state.session.state.get()?.escapeAsCancel === false) this.state.editor.escapeEnabled = false;
|
|
@@ -27471,6 +27532,8 @@ var MastraTUI = class MastraTUI {
|
|
|
27471
27532
|
await renderExistingTasks(this.state);
|
|
27472
27533
|
if (this.shouldShowOnboarding()) await this.showOnboarding();
|
|
27473
27534
|
await this.showQuietModePreferencePromptIfNeeded();
|
|
27535
|
+
if (startupResumeIssue?.kind === "missing") showError(this.state, `Thread not found: ${startupResumeIssue.threadId}. Started a new thread; use /threads to pick an existing one.`);
|
|
27536
|
+
else if (startupResumeIssue?.kind === "locked") showThreadLockPrompt(this.buildCommandContext(), startupResumeIssue.title, startupResumeIssue.ownerPid, startupResumeIssue.threadId);
|
|
27474
27537
|
this.checkForUpdate().catch(() => {});
|
|
27475
27538
|
this.updateCheckTimer = setInterval(() => {
|
|
27476
27539
|
this.checkForUpdate(true);
|
|
@@ -27780,49 +27843,52 @@ var MastraTUI = class MastraTUI {
|
|
|
27780
27843
|
}
|
|
27781
27844
|
insertChatComponentWithBoundarySpacing(this.state.chatContainer, child);
|
|
27782
27845
|
}
|
|
27783
|
-
|
|
27784
|
-
|
|
27785
|
-
|
|
27786
|
-
|
|
27787
|
-
|
|
27788
|
-
|
|
27789
|
-
|
|
27790
|
-
|
|
27791
|
-
|
|
27792
|
-
|
|
27846
|
+
installUserInputHandler() {
|
|
27847
|
+
this.state.editor.onSubmit = (text) => {
|
|
27848
|
+
if (isGoalJudgeInputLocked(this.state)) {
|
|
27849
|
+
this.state.editor.setText(text);
|
|
27850
|
+
showGoalJudgeInputLockInfo(this.state);
|
|
27851
|
+
flushRender(this.state);
|
|
27852
|
+
return;
|
|
27853
|
+
}
|
|
27854
|
+
if (text.trim()) this.state.editor.addToHistory(text);
|
|
27855
|
+
this.state.editor.setText("");
|
|
27856
|
+
if (this.state.session.run.isRunning()) {
|
|
27857
|
+
if (text.startsWith("/")) {
|
|
27858
|
+
this.handleSlashCommand(text).catch((error) => {
|
|
27859
|
+
showError(this.state, error instanceof Error ? error.message : "Slash command failed");
|
|
27860
|
+
});
|
|
27793
27861
|
return;
|
|
27794
27862
|
}
|
|
27795
|
-
if (text.
|
|
27796
|
-
|
|
27797
|
-
if (this.state.session.run.isRunning()) {
|
|
27798
|
-
if (text.startsWith("/")) {
|
|
27799
|
-
this.handleSlashCommand(text).catch((error) => {
|
|
27800
|
-
showError(this.state, error instanceof Error ? error.message : "Slash command failed");
|
|
27801
|
-
});
|
|
27802
|
-
return;
|
|
27803
|
-
}
|
|
27804
|
-
if (text.startsWith("!")) {
|
|
27805
|
-
handleShellPassthrough(this.state, text.slice(1).trim());
|
|
27806
|
-
return;
|
|
27807
|
-
}
|
|
27808
|
-
const { content, images } = consumePendingImages(text, this.state.pendingImages);
|
|
27809
|
-
this.state.pendingImages = [];
|
|
27810
|
-
if (images?.length) {
|
|
27811
|
-
this.state.pendingImages = images;
|
|
27812
|
-
this.queueFollowUpMessage(text);
|
|
27813
|
-
return;
|
|
27814
|
-
}
|
|
27815
|
-
this.signalMessage(content);
|
|
27863
|
+
if (text.startsWith("!")) {
|
|
27864
|
+
handleShellPassthrough(this.state, text.slice(1).trim());
|
|
27816
27865
|
return;
|
|
27817
27866
|
}
|
|
27818
|
-
const
|
|
27819
|
-
|
|
27820
|
-
|
|
27867
|
+
const { content, images } = consumePendingImages(text, this.state.pendingImages);
|
|
27868
|
+
this.state.pendingImages = [];
|
|
27869
|
+
if (images?.length) {
|
|
27870
|
+
this.state.pendingImages = images;
|
|
27871
|
+
this.queueFollowUpMessage(text);
|
|
27821
27872
|
return;
|
|
27822
27873
|
}
|
|
27823
|
-
this.
|
|
27824
|
-
|
|
27825
|
-
}
|
|
27874
|
+
this.signalMessage(content);
|
|
27875
|
+
return;
|
|
27876
|
+
}
|
|
27877
|
+
const pending = this.pendingUserInputResolve;
|
|
27878
|
+
if (!pending) {
|
|
27879
|
+
this.queuedUserInput.push(text);
|
|
27880
|
+
return;
|
|
27881
|
+
}
|
|
27882
|
+
this.pendingUserInputResolve = void 0;
|
|
27883
|
+
pending(text);
|
|
27884
|
+
};
|
|
27885
|
+
}
|
|
27886
|
+
getUserInput() {
|
|
27887
|
+
const queued = this.queuedUserInput.shift();
|
|
27888
|
+
if (queued !== void 0) return Promise.resolve(queued);
|
|
27889
|
+
return new Promise((resolve) => {
|
|
27890
|
+
this.pendingUserInputResolve = resolve;
|
|
27891
|
+
if (!this.state.editor.onSubmit) this.installUserInputHandler();
|
|
27826
27892
|
});
|
|
27827
27893
|
}
|
|
27828
27894
|
/**
|
|
@@ -28379,4 +28445,4 @@ Object.defineProperty(exports, "theme", {
|
|
|
28379
28445
|
}
|
|
28380
28446
|
});
|
|
28381
28447
|
|
|
28382
|
-
//# sourceMappingURL=tui-
|
|
28448
|
+
//# sourceMappingURL=tui-CPJv6T_R.cjs.map
|