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
|
@@ -2510,10 +2510,18 @@ function getCommands(modes) {
|
|
|
2510
2510
|
key: "/new",
|
|
2511
2511
|
description: "Start a new thread"
|
|
2512
2512
|
},
|
|
2513
|
+
{
|
|
2514
|
+
key: "/clone",
|
|
2515
|
+
description: "Clone the current thread"
|
|
2516
|
+
},
|
|
2513
2517
|
{
|
|
2514
2518
|
key: "/threads",
|
|
2515
2519
|
description: "Switch between threads"
|
|
2516
2520
|
},
|
|
2521
|
+
{
|
|
2522
|
+
key: "/resume",
|
|
2523
|
+
description: "Alias for /threads"
|
|
2524
|
+
},
|
|
2517
2525
|
{
|
|
2518
2526
|
key: "/thread",
|
|
2519
2527
|
description: "Show current thread info"
|
|
@@ -2526,6 +2534,10 @@ function getCommands(modes) {
|
|
|
2526
2534
|
key: "/name",
|
|
2527
2535
|
description: "Rename current thread"
|
|
2528
2536
|
},
|
|
2537
|
+
{
|
|
2538
|
+
key: "/rename",
|
|
2539
|
+
description: "Alias for /name"
|
|
2540
|
+
},
|
|
2529
2541
|
{
|
|
2530
2542
|
key: "/resource",
|
|
2531
2543
|
description: "Show/switch resource ID"
|
|
@@ -20936,6 +20948,8 @@ const TRACKED_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
20936
20948
|
"memory-gateway",
|
|
20937
20949
|
"custom-providers",
|
|
20938
20950
|
"threads",
|
|
20951
|
+
"resume",
|
|
20952
|
+
"clone",
|
|
20939
20953
|
"new"
|
|
20940
20954
|
]);
|
|
20941
20955
|
/**
|
|
@@ -21007,6 +21021,7 @@ async function dispatchSlashCommand(input, state, buildCtx) {
|
|
|
21007
21021
|
await handleCloneCommand(ctx);
|
|
21008
21022
|
return true;
|
|
21009
21023
|
case "threads":
|
|
21024
|
+
case "resume":
|
|
21010
21025
|
await handleThreadsCommand(ctx);
|
|
21011
21026
|
return true;
|
|
21012
21027
|
case "thread":
|
|
@@ -21089,6 +21104,7 @@ async function dispatchSlashCommand(input, state, buildCtx) {
|
|
|
21089
21104
|
await handleDiffCommand(ctx, args[0]);
|
|
21090
21105
|
return true;
|
|
21091
21106
|
case "name":
|
|
21107
|
+
case "rename":
|
|
21092
21108
|
await handleNameCommand(ctx, args);
|
|
21093
21109
|
return true;
|
|
21094
21110
|
case "resource":
|
|
@@ -25871,120 +25887,6 @@ function formatIdleStatusTimingSegments(state, now = Date.now()) {
|
|
|
25871
25887
|
} : null;
|
|
25872
25888
|
}
|
|
25873
25889
|
//#endregion
|
|
25874
|
-
//#region src/tui/components/simple-progress.ts
|
|
25875
|
-
/**
|
|
25876
|
-
* Simple progress indicator component for single operations.
|
|
25877
|
-
* Shows a compact progress display with spinner and status text.
|
|
25878
|
-
*/
|
|
25879
|
-
/**
|
|
25880
|
-
* Lightweight progress indicator for simple operations.
|
|
25881
|
-
* Features:
|
|
25882
|
-
* - Animated spinner
|
|
25883
|
-
* - Custom status text
|
|
25884
|
-
* - Optional elapsed time
|
|
25885
|
-
* - Optional percentage display
|
|
25886
|
-
*/
|
|
25887
|
-
var SimpleProgressComponent = class extends Container {
|
|
25888
|
-
status = "";
|
|
25889
|
-
progress;
|
|
25890
|
-
startTime;
|
|
25891
|
-
spinnerFrame = 0;
|
|
25892
|
-
lastRenderTime = 0;
|
|
25893
|
-
isActive = false;
|
|
25894
|
-
options;
|
|
25895
|
-
constructor(options = {}) {
|
|
25896
|
-
super();
|
|
25897
|
-
this.options = {
|
|
25898
|
-
prefix: "",
|
|
25899
|
-
showElapsed: true,
|
|
25900
|
-
showPercentage: true,
|
|
25901
|
-
...options
|
|
25902
|
-
};
|
|
25903
|
-
this.startTime = Date.now();
|
|
25904
|
-
}
|
|
25905
|
-
/**
|
|
25906
|
-
* Start showing progress with initial status.
|
|
25907
|
-
*/
|
|
25908
|
-
start(status) {
|
|
25909
|
-
this.status = status;
|
|
25910
|
-
this.isActive = true;
|
|
25911
|
-
this.startTime = Date.now();
|
|
25912
|
-
this.rebuild();
|
|
25913
|
-
}
|
|
25914
|
-
/**
|
|
25915
|
-
* Update the status text.
|
|
25916
|
-
*/
|
|
25917
|
-
updateStatus(status) {
|
|
25918
|
-
this.status = status;
|
|
25919
|
-
this.rebuild();
|
|
25920
|
-
}
|
|
25921
|
-
/**
|
|
25922
|
-
* Update the progress percentage (0-100).
|
|
25923
|
-
*/
|
|
25924
|
-
updateProgress(progress) {
|
|
25925
|
-
this.progress = Math.min(100, Math.max(0, progress));
|
|
25926
|
-
this.rebuild();
|
|
25927
|
-
}
|
|
25928
|
-
/**
|
|
25929
|
-
* Mark the operation as complete.
|
|
25930
|
-
*/
|
|
25931
|
-
complete(message) {
|
|
25932
|
-
this.isActive = false;
|
|
25933
|
-
this.status = message || "Complete";
|
|
25934
|
-
this.progress = 100;
|
|
25935
|
-
this.rebuild();
|
|
25936
|
-
}
|
|
25937
|
-
/**
|
|
25938
|
-
* Mark the operation as failed.
|
|
25939
|
-
*/
|
|
25940
|
-
fail(error) {
|
|
25941
|
-
this.isActive = false;
|
|
25942
|
-
this.status = error;
|
|
25943
|
-
this.rebuild();
|
|
25944
|
-
}
|
|
25945
|
-
rebuild() {
|
|
25946
|
-
this.clear();
|
|
25947
|
-
if (!this.status) return;
|
|
25948
|
-
const now = Date.now();
|
|
25949
|
-
if (this.isActive && now - this.lastRenderTime > 100) {
|
|
25950
|
-
this.spinnerFrame = (this.spinnerFrame + 1) % 10;
|
|
25951
|
-
this.lastRenderTime = now;
|
|
25952
|
-
}
|
|
25953
|
-
let text = "";
|
|
25954
|
-
if (this.options.prefix) text += this.options.prefix + " ";
|
|
25955
|
-
if (this.isActive) {
|
|
25956
|
-
const spinner = this.getSpinner();
|
|
25957
|
-
text += theme.fg("accent", spinner) + " ";
|
|
25958
|
-
} else if (this.progress === 100) text += theme.fg("success", "✓") + " ";
|
|
25959
|
-
else text += theme.fg("error", "✗") + " ";
|
|
25960
|
-
text += this.status;
|
|
25961
|
-
if (this.options.showPercentage && this.progress !== void 0) text += theme.fg("dim", ` (${this.progress}%)`);
|
|
25962
|
-
if (this.options.showElapsed && this.isActive) {
|
|
25963
|
-
const elapsed = Math.floor((now - this.startTime) / 1e3);
|
|
25964
|
-
text += theme.fg("dim", ` ${elapsed}s`);
|
|
25965
|
-
}
|
|
25966
|
-
this.addChild(new Text(text, 0, 0));
|
|
25967
|
-
}
|
|
25968
|
-
getSpinner() {
|
|
25969
|
-
return [
|
|
25970
|
-
"⠋",
|
|
25971
|
-
"⠙",
|
|
25972
|
-
"⠹",
|
|
25973
|
-
"⠸",
|
|
25974
|
-
"⠼",
|
|
25975
|
-
"⠴",
|
|
25976
|
-
"⠦",
|
|
25977
|
-
"⠧",
|
|
25978
|
-
"⠇",
|
|
25979
|
-
"⠏"
|
|
25980
|
-
][this.spinnerFrame];
|
|
25981
|
-
}
|
|
25982
|
-
render(maxWidth) {
|
|
25983
|
-
this.rebuild();
|
|
25984
|
-
return super.render(maxWidth);
|
|
25985
|
-
}
|
|
25986
|
-
};
|
|
25987
|
-
//#endregion
|
|
25988
25890
|
//#region src/tui/setup.ts
|
|
25989
25891
|
/**
|
|
25990
25892
|
* TUI setup: keyboard shortcuts, layout building, autocomplete, key handlers.
|
|
@@ -26248,6 +26150,10 @@ function setupAutocomplete(state) {
|
|
|
26248
26150
|
name: "threads",
|
|
26249
26151
|
description: "Switch between threads"
|
|
26250
26152
|
},
|
|
26153
|
+
{
|
|
26154
|
+
name: "resume",
|
|
26155
|
+
description: "Alias for /threads"
|
|
26156
|
+
},
|
|
26251
26157
|
{
|
|
26252
26158
|
name: "models",
|
|
26253
26159
|
description: "Switch model pack"
|
|
@@ -26317,6 +26223,10 @@ function setupAutocomplete(state) {
|
|
|
26317
26223
|
name: "name",
|
|
26318
26224
|
description: "Rename current thread"
|
|
26319
26225
|
},
|
|
26226
|
+
{
|
|
26227
|
+
name: "rename",
|
|
26228
|
+
description: "Alias for /name"
|
|
26229
|
+
},
|
|
26320
26230
|
{
|
|
26321
26231
|
name: "resource",
|
|
26322
26232
|
description: "Show/switch resource ID (tag for sharing)"
|
|
@@ -26685,94 +26595,6 @@ function subscribeToAgentController(state, handleEvent) {
|
|
|
26685
26595
|
state.waitForAgentControllerEvents = state.options.backgroundToolsEnabled ? () => eventQueue : void 0;
|
|
26686
26596
|
state.unsubscribe = state.session.subscribe(listener);
|
|
26687
26597
|
}
|
|
26688
|
-
async function promptForThreadSelection(state) {
|
|
26689
|
-
const currentPath = state.projectInfo.rootPath;
|
|
26690
|
-
const currentResourceId = state.session.identity.getResourceId();
|
|
26691
|
-
const allThreads = await state.session.thread.list();
|
|
26692
|
-
const activeThreadId = state.session.thread.getId();
|
|
26693
|
-
const taggedThreads = allThreads.filter((t) => {
|
|
26694
|
-
const threadPath = t.metadata?.projectPath;
|
|
26695
|
-
return !!threadPath && threadPath === currentPath;
|
|
26696
|
-
});
|
|
26697
|
-
const threads = [];
|
|
26698
|
-
for (const thread of taggedThreads) {
|
|
26699
|
-
if (thread.id === activeThreadId && !thread.title) {
|
|
26700
|
-
if ((await state.session.thread.listMessages({
|
|
26701
|
-
threadId: thread.id,
|
|
26702
|
-
limit: 1
|
|
26703
|
-
})).length === 0) continue;
|
|
26704
|
-
}
|
|
26705
|
-
threads.push(thread);
|
|
26706
|
-
}
|
|
26707
|
-
if (threads.length === 0) {
|
|
26708
|
-
const [thread] = [...(await state.session.thread.list({
|
|
26709
|
-
allResources: true,
|
|
26710
|
-
metadata: { projectPath: currentPath }
|
|
26711
|
-
})).filter((t) => t.resourceId !== currentResourceId)].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
26712
|
-
if (thread) {
|
|
26713
|
-
if (await askModalQuestion(state.ui, {
|
|
26714
|
-
question: [
|
|
26715
|
-
"This directory is tagged on a different resource.",
|
|
26716
|
-
"",
|
|
26717
|
-
`Project: ${currentPath}`,
|
|
26718
|
-
`Thread: ${thread.title || thread.id}`,
|
|
26719
|
-
`Old resource: ${thread.resourceId}`,
|
|
26720
|
-
`Current resource: ${currentResourceId}`,
|
|
26721
|
-
"",
|
|
26722
|
-
"Clone this thread into the current resource and resume the clone?"
|
|
26723
|
-
].join("\n"),
|
|
26724
|
-
options: [{ label: "Clone and resume" }, { label: "Start fresh" }],
|
|
26725
|
-
selectedOptionLabel: "Clone and resume",
|
|
26726
|
-
allowCustomResponse: false,
|
|
26727
|
-
overlay: {
|
|
26728
|
-
widthPercent: 80,
|
|
26729
|
-
maxHeight: "70%"
|
|
26730
|
-
}
|
|
26731
|
-
}) === "Clone and resume") {
|
|
26732
|
-
const progress = new SimpleProgressComponent({
|
|
26733
|
-
showElapsed: false,
|
|
26734
|
-
showPercentage: false
|
|
26735
|
-
});
|
|
26736
|
-
progress.start("Cloning thread into the current resource...");
|
|
26737
|
-
showModalOverlay(state.ui, progress, {
|
|
26738
|
-
widthPercent: 70,
|
|
26739
|
-
maxHeight: "40%",
|
|
26740
|
-
minHeightPercent: .35
|
|
26741
|
-
});
|
|
26742
|
-
state.ui.requestRender();
|
|
26743
|
-
try {
|
|
26744
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
26745
|
-
progress.updateStatus("Loading cloned thread...");
|
|
26746
|
-
state.ui.requestRender();
|
|
26747
|
-
await state.session.thread.cloneToCurrentResource({
|
|
26748
|
-
threadId: thread.id,
|
|
26749
|
-
expectedResourceId: thread.resourceId,
|
|
26750
|
-
expectedProjectPath: currentPath
|
|
26751
|
-
});
|
|
26752
|
-
} finally {
|
|
26753
|
-
state.ui.hideOverlay();
|
|
26754
|
-
state.ui.requestRender();
|
|
26755
|
-
}
|
|
26756
|
-
return;
|
|
26757
|
-
}
|
|
26758
|
-
}
|
|
26759
|
-
state.pendingNewThread = true;
|
|
26760
|
-
return;
|
|
26761
|
-
}
|
|
26762
|
-
const sortedThreads = [...threads].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
26763
|
-
for (const thread of sortedThreads) try {
|
|
26764
|
-
await state.session.thread.switch({ threadId: thread.id });
|
|
26765
|
-
if (!thread.metadata?.projectPath) await state.session.thread.setSetting({
|
|
26766
|
-
key: "projectPath",
|
|
26767
|
-
value: currentPath
|
|
26768
|
-
});
|
|
26769
|
-
return;
|
|
26770
|
-
} catch (error) {
|
|
26771
|
-
if (error instanceof ThreadLockError) continue;
|
|
26772
|
-
throw error;
|
|
26773
|
-
}
|
|
26774
|
-
state.pendingNewThread = true;
|
|
26775
|
-
}
|
|
26776
26598
|
async function renderExistingTasks(state) {
|
|
26777
26599
|
try {
|
|
26778
26600
|
const tasks = state.session.displayState.get().tasks;
|
|
@@ -26953,6 +26775,239 @@ async function handleShellPassthrough(state, command) {
|
|
|
26953
26775
|
}
|
|
26954
26776
|
}
|
|
26955
26777
|
//#endregion
|
|
26778
|
+
//#region src/tui/components/simple-progress.ts
|
|
26779
|
+
/**
|
|
26780
|
+
* Simple progress indicator component for single operations.
|
|
26781
|
+
* Shows a compact progress display with spinner and status text.
|
|
26782
|
+
*/
|
|
26783
|
+
/**
|
|
26784
|
+
* Lightweight progress indicator for simple operations.
|
|
26785
|
+
* Features:
|
|
26786
|
+
* - Animated spinner
|
|
26787
|
+
* - Custom status text
|
|
26788
|
+
* - Optional elapsed time
|
|
26789
|
+
* - Optional percentage display
|
|
26790
|
+
*/
|
|
26791
|
+
var SimpleProgressComponent = class extends Container {
|
|
26792
|
+
status = "";
|
|
26793
|
+
progress;
|
|
26794
|
+
startTime;
|
|
26795
|
+
spinnerFrame = 0;
|
|
26796
|
+
lastRenderTime = 0;
|
|
26797
|
+
isActive = false;
|
|
26798
|
+
options;
|
|
26799
|
+
constructor(options = {}) {
|
|
26800
|
+
super();
|
|
26801
|
+
this.options = {
|
|
26802
|
+
prefix: "",
|
|
26803
|
+
showElapsed: true,
|
|
26804
|
+
showPercentage: true,
|
|
26805
|
+
...options
|
|
26806
|
+
};
|
|
26807
|
+
this.startTime = Date.now();
|
|
26808
|
+
}
|
|
26809
|
+
/**
|
|
26810
|
+
* Start showing progress with initial status.
|
|
26811
|
+
*/
|
|
26812
|
+
start(status) {
|
|
26813
|
+
this.status = status;
|
|
26814
|
+
this.isActive = true;
|
|
26815
|
+
this.startTime = Date.now();
|
|
26816
|
+
this.rebuild();
|
|
26817
|
+
}
|
|
26818
|
+
/**
|
|
26819
|
+
* Update the status text.
|
|
26820
|
+
*/
|
|
26821
|
+
updateStatus(status) {
|
|
26822
|
+
this.status = status;
|
|
26823
|
+
this.rebuild();
|
|
26824
|
+
}
|
|
26825
|
+
/**
|
|
26826
|
+
* Update the progress percentage (0-100).
|
|
26827
|
+
*/
|
|
26828
|
+
updateProgress(progress) {
|
|
26829
|
+
this.progress = Math.min(100, Math.max(0, progress));
|
|
26830
|
+
this.rebuild();
|
|
26831
|
+
}
|
|
26832
|
+
/**
|
|
26833
|
+
* Mark the operation as complete.
|
|
26834
|
+
*/
|
|
26835
|
+
complete(message) {
|
|
26836
|
+
this.isActive = false;
|
|
26837
|
+
this.status = message || "Complete";
|
|
26838
|
+
this.progress = 100;
|
|
26839
|
+
this.rebuild();
|
|
26840
|
+
}
|
|
26841
|
+
/**
|
|
26842
|
+
* Mark the operation as failed.
|
|
26843
|
+
*/
|
|
26844
|
+
fail(error) {
|
|
26845
|
+
this.isActive = false;
|
|
26846
|
+
this.status = error;
|
|
26847
|
+
this.rebuild();
|
|
26848
|
+
}
|
|
26849
|
+
rebuild() {
|
|
26850
|
+
this.clear();
|
|
26851
|
+
if (!this.status) return;
|
|
26852
|
+
const now = Date.now();
|
|
26853
|
+
if (this.isActive && now - this.lastRenderTime > 100) {
|
|
26854
|
+
this.spinnerFrame = (this.spinnerFrame + 1) % 10;
|
|
26855
|
+
this.lastRenderTime = now;
|
|
26856
|
+
}
|
|
26857
|
+
let text = "";
|
|
26858
|
+
if (this.options.prefix) text += this.options.prefix + " ";
|
|
26859
|
+
if (this.isActive) {
|
|
26860
|
+
const spinner = this.getSpinner();
|
|
26861
|
+
text += theme.fg("accent", spinner) + " ";
|
|
26862
|
+
} else if (this.progress === 100) text += theme.fg("success", "✓") + " ";
|
|
26863
|
+
else text += theme.fg("error", "✗") + " ";
|
|
26864
|
+
text += this.status;
|
|
26865
|
+
if (this.options.showPercentage && this.progress !== void 0) text += theme.fg("dim", ` (${this.progress}%)`);
|
|
26866
|
+
if (this.options.showElapsed && this.isActive) {
|
|
26867
|
+
const elapsed = Math.floor((now - this.startTime) / 1e3);
|
|
26868
|
+
text += theme.fg("dim", ` ${elapsed}s`);
|
|
26869
|
+
}
|
|
26870
|
+
this.addChild(new Text(text, 0, 0));
|
|
26871
|
+
}
|
|
26872
|
+
getSpinner() {
|
|
26873
|
+
return [
|
|
26874
|
+
"⠋",
|
|
26875
|
+
"⠙",
|
|
26876
|
+
"⠹",
|
|
26877
|
+
"⠸",
|
|
26878
|
+
"⠼",
|
|
26879
|
+
"⠴",
|
|
26880
|
+
"⠦",
|
|
26881
|
+
"⠧",
|
|
26882
|
+
"⠇",
|
|
26883
|
+
"⠏"
|
|
26884
|
+
][this.spinnerFrame];
|
|
26885
|
+
}
|
|
26886
|
+
render(maxWidth) {
|
|
26887
|
+
this.rebuild();
|
|
26888
|
+
return super.render(maxWidth);
|
|
26889
|
+
}
|
|
26890
|
+
};
|
|
26891
|
+
//#endregion
|
|
26892
|
+
//#region src/tui/thread-startup.ts
|
|
26893
|
+
/**
|
|
26894
|
+
* Selects the thread to open at startup. A requested thread that is missing or
|
|
26895
|
+
* locked does not throw; the session is left pending a new thread and the issue
|
|
26896
|
+
* is returned so the caller can tell the user once the UI is ready.
|
|
26897
|
+
*/
|
|
26898
|
+
async function resumeThreadOnStartup(state, requestedThreadId) {
|
|
26899
|
+
const currentPath = state.projectInfo.rootPath;
|
|
26900
|
+
const allThreads = await state.session.thread.list(requestedThreadId ? { allResources: true } : void 0);
|
|
26901
|
+
const activeThreadId = state.session.thread.getId();
|
|
26902
|
+
if (requestedThreadId) {
|
|
26903
|
+
const thread = allThreads.find((candidate) => candidate.id === requestedThreadId);
|
|
26904
|
+
if (!thread) {
|
|
26905
|
+
await startPendingNewThread(state);
|
|
26906
|
+
return {
|
|
26907
|
+
kind: "missing",
|
|
26908
|
+
threadId: requestedThreadId
|
|
26909
|
+
};
|
|
26910
|
+
}
|
|
26911
|
+
const originalResourceId = state.session.identity.getResourceId();
|
|
26912
|
+
if (thread.resourceId !== originalResourceId) await state.controller.setResourceId(state.session, { resourceId: thread.resourceId });
|
|
26913
|
+
if (requestedThreadId !== activeThreadId) try {
|
|
26914
|
+
await state.session.thread.switch({ threadId: requestedThreadId });
|
|
26915
|
+
} catch (error) {
|
|
26916
|
+
if (!(error instanceof ThreadLockError)) throw error;
|
|
26917
|
+
if (state.session.identity.getResourceId() !== originalResourceId) await state.controller.setResourceId(state.session, { resourceId: originalResourceId });
|
|
26918
|
+
await startPendingNewThread(state);
|
|
26919
|
+
return {
|
|
26920
|
+
kind: "locked",
|
|
26921
|
+
threadId: thread.id,
|
|
26922
|
+
title: thread.title || thread.id,
|
|
26923
|
+
ownerPid: error.ownerPid
|
|
26924
|
+
};
|
|
26925
|
+
}
|
|
26926
|
+
state.pendingNewThread = false;
|
|
26927
|
+
return;
|
|
26928
|
+
}
|
|
26929
|
+
const threads = allThreads.filter((thread) => thread.metadata?.projectPath === currentPath);
|
|
26930
|
+
if (threads.length === 0) {
|
|
26931
|
+
if (await cloneDriftedThread(state)) return;
|
|
26932
|
+
state.pendingNewThread = true;
|
|
26933
|
+
return;
|
|
26934
|
+
}
|
|
26935
|
+
for (const thread of [...threads].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())) {
|
|
26936
|
+
if (thread.id === state.session.thread.getId()) return;
|
|
26937
|
+
try {
|
|
26938
|
+
await state.session.thread.switch({ threadId: thread.id });
|
|
26939
|
+
return;
|
|
26940
|
+
} catch (error) {
|
|
26941
|
+
if (error instanceof ThreadLockError) continue;
|
|
26942
|
+
throw error;
|
|
26943
|
+
}
|
|
26944
|
+
}
|
|
26945
|
+
state.pendingNewThread = true;
|
|
26946
|
+
}
|
|
26947
|
+
/**
|
|
26948
|
+
* Leave the session waiting for a new thread. Session creation may already have
|
|
26949
|
+
* bound the latest project thread; unbind it (and release its lock) so the UI
|
|
26950
|
+
* and exit hint don't show a thread the user didn't ask for.
|
|
26951
|
+
*/
|
|
26952
|
+
async function startPendingNewThread(state) {
|
|
26953
|
+
state.session.thread.cleanupSubscription();
|
|
26954
|
+
await state.session.thread.clearAndReleaseLock();
|
|
26955
|
+
state.pendingNewThread = true;
|
|
26956
|
+
}
|
|
26957
|
+
async function cloneDriftedThread(state) {
|
|
26958
|
+
const currentPath = state.projectInfo.rootPath;
|
|
26959
|
+
const currentResourceId = state.session.identity.getResourceId();
|
|
26960
|
+
const thread = [...(await state.session.thread.list({
|
|
26961
|
+
allResources: true,
|
|
26962
|
+
metadata: { projectPath: currentPath }
|
|
26963
|
+
})).filter((thread) => thread.resourceId !== currentResourceId)].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())[0];
|
|
26964
|
+
if (!thread) return false;
|
|
26965
|
+
if (await askModalQuestion(state.ui, {
|
|
26966
|
+
question: [
|
|
26967
|
+
"This directory is tagged on a different resource.",
|
|
26968
|
+
"",
|
|
26969
|
+
`Project: ${currentPath}`,
|
|
26970
|
+
`Thread: ${thread.title || thread.id}`,
|
|
26971
|
+
`Old resource: ${thread.resourceId}`,
|
|
26972
|
+
`Current resource: ${currentResourceId}`,
|
|
26973
|
+
"",
|
|
26974
|
+
"Clone this thread into the current resource and resume the clone?"
|
|
26975
|
+
].join("\n"),
|
|
26976
|
+
options: [{ label: "Clone and resume" }, { label: "Start fresh" }],
|
|
26977
|
+
selectedOptionLabel: "Clone and resume",
|
|
26978
|
+
allowCustomResponse: false,
|
|
26979
|
+
overlay: {
|
|
26980
|
+
widthPercent: 80,
|
|
26981
|
+
maxHeight: "70%"
|
|
26982
|
+
}
|
|
26983
|
+
}) !== "Clone and resume") return false;
|
|
26984
|
+
const progress = new SimpleProgressComponent({
|
|
26985
|
+
showElapsed: false,
|
|
26986
|
+
showPercentage: false
|
|
26987
|
+
});
|
|
26988
|
+
progress.start("Cloning thread into the current resource...");
|
|
26989
|
+
showModalOverlay(state.ui, progress, {
|
|
26990
|
+
widthPercent: 70,
|
|
26991
|
+
maxHeight: "40%",
|
|
26992
|
+
minHeightPercent: .35
|
|
26993
|
+
});
|
|
26994
|
+
state.ui.requestRender();
|
|
26995
|
+
try {
|
|
26996
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
26997
|
+
progress.updateStatus("Loading cloned thread...");
|
|
26998
|
+
state.ui.requestRender();
|
|
26999
|
+
await state.session.thread.cloneToCurrentResource({
|
|
27000
|
+
threadId: thread.id,
|
|
27001
|
+
expectedResourceId: thread.resourceId,
|
|
27002
|
+
expectedProjectPath: currentPath
|
|
27003
|
+
});
|
|
27004
|
+
} finally {
|
|
27005
|
+
state.ui.hideOverlay();
|
|
27006
|
+
state.ui.requestRender();
|
|
27007
|
+
}
|
|
27008
|
+
return true;
|
|
27009
|
+
}
|
|
27010
|
+
//#endregion
|
|
26956
27011
|
//#region src/tui/mastra-tui.ts
|
|
26957
27012
|
/**
|
|
26958
27013
|
* Main TUI class for Mastra Code.
|
|
@@ -27100,6 +27155,7 @@ var MastraTUI = class MastraTUI {
|
|
|
27100
27155
|
clearFinishedBackgroundActivities: () => this.clearFinishedBackgroundActivity()
|
|
27101
27156
|
} : {}
|
|
27102
27157
|
});
|
|
27158
|
+
this.installUserInputHandler();
|
|
27103
27159
|
}
|
|
27104
27160
|
getCurrentThreadBackgroundActivities() {
|
|
27105
27161
|
return getBackgroundActivitiesForTarget(this.state.backgroundActivities, this.state.session.identity.getResourceId(), this.state.pendingNewThread ? null : this.state.session.thread.getId());
|
|
@@ -27349,6 +27405,10 @@ var MastraTUI = class MastraTUI {
|
|
|
27349
27405
|
updateStatusLine(this.state);
|
|
27350
27406
|
flushRender(this.state);
|
|
27351
27407
|
}
|
|
27408
|
+
/** The thread to suggest resuming on exit, or null while waiting for a new thread. */
|
|
27409
|
+
getResumeThreadId() {
|
|
27410
|
+
return this.state.pendingNewThread ? null : this.state.session.thread.getId();
|
|
27411
|
+
}
|
|
27352
27412
|
/**
|
|
27353
27413
|
* Stop the TUI and clean up.
|
|
27354
27414
|
*/
|
|
@@ -27422,7 +27482,8 @@ var MastraTUI = class MastraTUI {
|
|
|
27422
27482
|
doubleCtrlCMs: MastraTUI.DOUBLE_CTRL_C_MS
|
|
27423
27483
|
});
|
|
27424
27484
|
this.state.ui.start();
|
|
27425
|
-
await
|
|
27485
|
+
const startupResumeIssue = await resumeThreadOnStartup(this.state, this.state.options.resumeThreadId);
|
|
27486
|
+
await this.syncThreadActivePackMetadata();
|
|
27426
27487
|
subscribeToAgentController(this.state, (event) => this.handleEvent(event));
|
|
27427
27488
|
if (this.state.options.backgroundToolsEnabled) this.cleanupBackgroundCompletionListener = this.state.options.backgroundCompletionEvents?.subscribe((event) => this.handleBackgroundCompletion(event));
|
|
27428
27489
|
if (this.state.session.state.get()?.escapeAsCancel === false) this.state.editor.escapeEnabled = false;
|
|
@@ -27440,6 +27501,8 @@ var MastraTUI = class MastraTUI {
|
|
|
27440
27501
|
await renderExistingTasks(this.state);
|
|
27441
27502
|
if (this.shouldShowOnboarding()) await this.showOnboarding();
|
|
27442
27503
|
await this.showQuietModePreferencePromptIfNeeded();
|
|
27504
|
+
if (startupResumeIssue?.kind === "missing") showError(this.state, `Thread not found: ${startupResumeIssue.threadId}. Started a new thread; use /threads to pick an existing one.`);
|
|
27505
|
+
else if (startupResumeIssue?.kind === "locked") showThreadLockPrompt(this.buildCommandContext(), startupResumeIssue.title, startupResumeIssue.ownerPid, startupResumeIssue.threadId);
|
|
27443
27506
|
this.checkForUpdate().catch(() => {});
|
|
27444
27507
|
this.updateCheckTimer = setInterval(() => {
|
|
27445
27508
|
this.checkForUpdate(true);
|
|
@@ -27749,49 +27812,52 @@ var MastraTUI = class MastraTUI {
|
|
|
27749
27812
|
}
|
|
27750
27813
|
insertChatComponentWithBoundarySpacing(this.state.chatContainer, child);
|
|
27751
27814
|
}
|
|
27752
|
-
|
|
27753
|
-
|
|
27754
|
-
|
|
27755
|
-
|
|
27756
|
-
|
|
27757
|
-
|
|
27758
|
-
|
|
27759
|
-
|
|
27760
|
-
|
|
27761
|
-
|
|
27815
|
+
installUserInputHandler() {
|
|
27816
|
+
this.state.editor.onSubmit = (text) => {
|
|
27817
|
+
if (isGoalJudgeInputLocked(this.state)) {
|
|
27818
|
+
this.state.editor.setText(text);
|
|
27819
|
+
showGoalJudgeInputLockInfo(this.state);
|
|
27820
|
+
flushRender(this.state);
|
|
27821
|
+
return;
|
|
27822
|
+
}
|
|
27823
|
+
if (text.trim()) this.state.editor.addToHistory(text);
|
|
27824
|
+
this.state.editor.setText("");
|
|
27825
|
+
if (this.state.session.run.isRunning()) {
|
|
27826
|
+
if (text.startsWith("/")) {
|
|
27827
|
+
this.handleSlashCommand(text).catch((error) => {
|
|
27828
|
+
showError(this.state, error instanceof Error ? error.message : "Slash command failed");
|
|
27829
|
+
});
|
|
27762
27830
|
return;
|
|
27763
27831
|
}
|
|
27764
|
-
if (text.
|
|
27765
|
-
|
|
27766
|
-
if (this.state.session.run.isRunning()) {
|
|
27767
|
-
if (text.startsWith("/")) {
|
|
27768
|
-
this.handleSlashCommand(text).catch((error) => {
|
|
27769
|
-
showError(this.state, error instanceof Error ? error.message : "Slash command failed");
|
|
27770
|
-
});
|
|
27771
|
-
return;
|
|
27772
|
-
}
|
|
27773
|
-
if (text.startsWith("!")) {
|
|
27774
|
-
handleShellPassthrough(this.state, text.slice(1).trim());
|
|
27775
|
-
return;
|
|
27776
|
-
}
|
|
27777
|
-
const { content, images } = consumePendingImages(text, this.state.pendingImages);
|
|
27778
|
-
this.state.pendingImages = [];
|
|
27779
|
-
if (images?.length) {
|
|
27780
|
-
this.state.pendingImages = images;
|
|
27781
|
-
this.queueFollowUpMessage(text);
|
|
27782
|
-
return;
|
|
27783
|
-
}
|
|
27784
|
-
this.signalMessage(content);
|
|
27832
|
+
if (text.startsWith("!")) {
|
|
27833
|
+
handleShellPassthrough(this.state, text.slice(1).trim());
|
|
27785
27834
|
return;
|
|
27786
27835
|
}
|
|
27787
|
-
const
|
|
27788
|
-
|
|
27789
|
-
|
|
27836
|
+
const { content, images } = consumePendingImages(text, this.state.pendingImages);
|
|
27837
|
+
this.state.pendingImages = [];
|
|
27838
|
+
if (images?.length) {
|
|
27839
|
+
this.state.pendingImages = images;
|
|
27840
|
+
this.queueFollowUpMessage(text);
|
|
27790
27841
|
return;
|
|
27791
27842
|
}
|
|
27792
|
-
this.
|
|
27793
|
-
|
|
27794
|
-
}
|
|
27843
|
+
this.signalMessage(content);
|
|
27844
|
+
return;
|
|
27845
|
+
}
|
|
27846
|
+
const pending = this.pendingUserInputResolve;
|
|
27847
|
+
if (!pending) {
|
|
27848
|
+
this.queuedUserInput.push(text);
|
|
27849
|
+
return;
|
|
27850
|
+
}
|
|
27851
|
+
this.pendingUserInputResolve = void 0;
|
|
27852
|
+
pending(text);
|
|
27853
|
+
};
|
|
27854
|
+
}
|
|
27855
|
+
getUserInput() {
|
|
27856
|
+
const queued = this.queuedUserInput.shift();
|
|
27857
|
+
if (queued !== void 0) return Promise.resolve(queued);
|
|
27858
|
+
return new Promise((resolve) => {
|
|
27859
|
+
this.pendingUserInputResolve = resolve;
|
|
27860
|
+
if (!this.state.editor.onSubmit) this.installUserInputHandler();
|
|
27795
27861
|
});
|
|
27796
27862
|
}
|
|
27797
27863
|
/**
|
|
@@ -28229,4 +28295,4 @@ var MastraTUI = class MastraTUI {
|
|
|
28229
28295
|
//#endregion
|
|
28230
28296
|
export { mastraBrand as _, OMProgressComponent as a, TUI_CO_AUTHOR as b, ToolExecutionComponentEnhanced as c, detectTerminalTheme as d, applyThemeMode as f, mastra as g, getThemeMode as h, LoginDialogComponent as i, ModelSelectorComponent as l, getMarkdownTheme as m, createTUIState as n, formatOMStatus as o, getEditorTheme as p, LoginSelectorComponent as r, UserMessageComponent as s, MastraTUI as t, AssistantMessageComponent as u, restoreTerminalForeground as v, theme as y };
|
|
28231
28297
|
|
|
28232
|
-
//# sourceMappingURL=tui-
|
|
28298
|
+
//# sourceMappingURL=tui-CUNuxs-4.js.map
|