@markusylisiurunen/tau 0.2.57 → 0.2.59
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/README.md +1 -1
- package/dist/core/async/telegram.js +48 -12
- package/dist/core/async/telegram.js.map +1 -1
- package/dist/core/commands/registry.js +1 -1
- package/dist/core/commands/registry.js.map +1 -1
- package/dist/core/session/session_engine.js +6 -1
- package/dist/core/session/session_engine.js.map +1 -1
- package/dist/core/utils/context.js +1 -1
- package/dist/core/utils/context.js.map +1 -1
- package/dist/core/utils/context_builder.js +14 -0
- package/dist/core/utils/context_builder.js.map +1 -1
- package/dist/core/version.js +1 -1
- package/dist/tui/chat_controller.js +114 -31
- package/dist/tui/chat_controller.js.map +1 -1
- package/package.json +3 -4
|
@@ -19,7 +19,7 @@ import { TOOL_NAME_BASH, TOOL_NAME_EDIT } from "../core/tools/tool_names.js";
|
|
|
19
19
|
import { REASONING_LEVELS, } from "../core/types.js";
|
|
20
20
|
import { resolveAgentCwd, resolveSandboxPath } from "../core/utils/agent_environment.js";
|
|
21
21
|
import { findAgentsFilesFromCwdToHome } from "../core/utils/agents_files.js";
|
|
22
|
-
import { buildProjectContextBlock, buildSkillsIndexBlock, findAgentsFilesInScopeDetailed, formatCwdChangeNotice, formatRiskLevelChangeNotice, } from "../core/utils/context.js";
|
|
22
|
+
import { buildProjectContextBlock, buildSkillsIndexBlock, findAgentsFilesInScopeDetailed, formatCwdChangeNotice, formatProjectContextChangeNotice, formatRiskLevelChangeNotice, } from "../core/utils/context.js";
|
|
23
23
|
import { formatAdaptiveNumber, formatCwd, formatTokenWindow } from "../core/utils/format.js";
|
|
24
24
|
import { getGitRoot } from "../core/utils/git.js";
|
|
25
25
|
import { buildLineDiff, collapseLongUnchangedDiffRuns } from "../core/utils/line_diff.js";
|
|
@@ -99,8 +99,7 @@ export class ChatController {
|
|
|
99
99
|
showThinking = false;
|
|
100
100
|
compactToolUi = true;
|
|
101
101
|
commandHint;
|
|
102
|
-
|
|
103
|
-
currentTurnAbort;
|
|
102
|
+
activeBusyTask;
|
|
104
103
|
riskLevel = "read-only";
|
|
105
104
|
projectContextBlock;
|
|
106
105
|
projectFiles = [];
|
|
@@ -111,6 +110,7 @@ export class ChatController {
|
|
|
111
110
|
agentsConfigErrors;
|
|
112
111
|
pendingRiskLevelChange;
|
|
113
112
|
pendingCwdChange;
|
|
113
|
+
pendingProjectContextChange;
|
|
114
114
|
expandedFilesInCurrentPrompt = new Set();
|
|
115
115
|
expandedSkillsInCurrentPrompt = new Set();
|
|
116
116
|
assistantState;
|
|
@@ -327,7 +327,7 @@ export class ChatController {
|
|
|
327
327
|
void this.runSpeakTransition(() => this.stopSpeakCapture());
|
|
328
328
|
return;
|
|
329
329
|
}
|
|
330
|
-
this.
|
|
330
|
+
this.interruptActiveTask();
|
|
331
331
|
}
|
|
332
332
|
onEvent(event) {
|
|
333
333
|
switch (event.type) {
|
|
@@ -710,6 +710,7 @@ export class ChatController {
|
|
|
710
710
|
}
|
|
711
711
|
const nextCwd = this.deps.env.cwd();
|
|
712
712
|
const previousAgentCwd = this.agentCwd;
|
|
713
|
+
const previousProjectContextBlock = this.projectContextBlock;
|
|
713
714
|
this.agentCwd =
|
|
714
715
|
this.sandboxEnabled && this.sandboxRootReal
|
|
715
716
|
? resolveSandboxPath({
|
|
@@ -723,6 +724,7 @@ export class ChatController {
|
|
|
723
724
|
sandboxConfig: this.config.sandbox,
|
|
724
725
|
});
|
|
725
726
|
this.refreshProjectContext(nextCwd);
|
|
727
|
+
this.updatePendingProjectContextChange(previousProjectContextBlock, this.projectContextBlock);
|
|
726
728
|
this.projectFiles = [];
|
|
727
729
|
this.refreshProjectFilesInBackground();
|
|
728
730
|
this.expandedFilesInCurrentPrompt.clear();
|
|
@@ -795,20 +797,47 @@ export class ChatController {
|
|
|
795
797
|
readFile: this.deps.fs.readFile,
|
|
796
798
|
});
|
|
797
799
|
}
|
|
798
|
-
|
|
799
|
-
if (
|
|
800
|
+
updatePendingProjectContextChange(previous, next) {
|
|
801
|
+
if (previous === next) {
|
|
800
802
|
return;
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
this.
|
|
805
|
-
this.runtime.interruptTurn();
|
|
806
|
-
this.view.addSystemMessage("interrupted", "error");
|
|
803
|
+
}
|
|
804
|
+
const from = this.pendingProjectContextChange?.from ?? previous;
|
|
805
|
+
if (from === next) {
|
|
806
|
+
this.pendingProjectContextChange = undefined;
|
|
807
807
|
return;
|
|
808
808
|
}
|
|
809
|
-
|
|
809
|
+
this.pendingProjectContextChange = { from, to: next };
|
|
810
|
+
}
|
|
811
|
+
createAbortBusyTask() {
|
|
812
|
+
const abortController = new AbortController();
|
|
813
|
+
return {
|
|
814
|
+
signal: abortController.signal,
|
|
815
|
+
busyTask: {
|
|
816
|
+
requestInterrupt: () => {
|
|
817
|
+
if (abortController.signal.aborted) {
|
|
818
|
+
return false;
|
|
819
|
+
}
|
|
820
|
+
abortController.abort();
|
|
821
|
+
return true;
|
|
822
|
+
},
|
|
823
|
+
},
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
beginBusyTask(task) {
|
|
827
|
+
this.activeBusyTask = task;
|
|
828
|
+
}
|
|
829
|
+
endBusyTask(task) {
|
|
830
|
+
if (this.activeBusyTask === task) {
|
|
831
|
+
this.activeBusyTask = undefined;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
interruptActiveTask() {
|
|
835
|
+
if (!this.isStreaming)
|
|
836
|
+
return;
|
|
837
|
+
if (!this.activeBusyTask)
|
|
838
|
+
return;
|
|
839
|
+
if (!this.activeBusyTask.requestInterrupt())
|
|
810
840
|
return;
|
|
811
|
-
this.currentTurnAbort?.abort();
|
|
812
841
|
this.view.addSystemMessage("interrupted", "error");
|
|
813
842
|
}
|
|
814
843
|
// Input Handling --------------------------------------------------------------------------------
|
|
@@ -1116,8 +1145,14 @@ export class ChatController {
|
|
|
1116
1145
|
if (this.pendingCwdChange) {
|
|
1117
1146
|
notices.push(formatCwdChangeNotice(this.pendingCwdChange));
|
|
1118
1147
|
}
|
|
1148
|
+
if (this.pendingProjectContextChange) {
|
|
1149
|
+
notices.push(formatProjectContextChangeNotice({
|
|
1150
|
+
projectContextBlock: this.pendingProjectContextChange.to,
|
|
1151
|
+
}));
|
|
1152
|
+
}
|
|
1119
1153
|
this.pendingRiskLevelChange = undefined;
|
|
1120
1154
|
this.pendingCwdChange = undefined;
|
|
1155
|
+
this.pendingProjectContextChange = undefined;
|
|
1121
1156
|
const systemNotice = notices.length > 0 ? notices.join("\n") : undefined;
|
|
1122
1157
|
const baseTextForModel = opts?.textForModel ?? text;
|
|
1123
1158
|
const textForModel = systemNotice ? `${systemNotice}\n\n${baseTextForModel}` : baseTextForModel;
|
|
@@ -1541,6 +1576,9 @@ export class ChatController {
|
|
|
1541
1576
|
this.isBashMode = false;
|
|
1542
1577
|
this.isBashIncognito = false;
|
|
1543
1578
|
this.isMemoryMode = false;
|
|
1579
|
+
this.pendingRiskLevelChange = undefined;
|
|
1580
|
+
this.pendingCwdChange = undefined;
|
|
1581
|
+
this.pendingProjectContextChange = undefined;
|
|
1544
1582
|
try {
|
|
1545
1583
|
const report = await this.refreshReloadableContent("new-session");
|
|
1546
1584
|
this.applyReloadMessages(report.messages);
|
|
@@ -1603,32 +1641,46 @@ export class ChatController {
|
|
|
1603
1641
|
this.view.addSystemMessage("summarizing session...", "success");
|
|
1604
1642
|
this.isStreaming = true;
|
|
1605
1643
|
this.view.startWorkingIcon();
|
|
1644
|
+
const { busyTask, signal } = this.createAbortBusyTask();
|
|
1645
|
+
this.beginBusyTask(busyTask);
|
|
1606
1646
|
try {
|
|
1607
1647
|
const result = await this.engine.compact({
|
|
1608
1648
|
mode: "only-summary",
|
|
1609
1649
|
guidance,
|
|
1650
|
+
signal,
|
|
1610
1651
|
});
|
|
1611
1652
|
this.applyCompactedHistoryUi(result.compactionMessage);
|
|
1612
1653
|
this.view.addSystemMessage("session compacted. previous context has been summarized.", "success");
|
|
1613
1654
|
}
|
|
1614
1655
|
catch (err) {
|
|
1615
|
-
|
|
1656
|
+
if (!signal.aborted) {
|
|
1657
|
+
this.handleCompactionError(err);
|
|
1658
|
+
}
|
|
1616
1659
|
}
|
|
1617
1660
|
finally {
|
|
1661
|
+
this.endBusyTask(busyTask);
|
|
1618
1662
|
this.view.stopWorkingIcon();
|
|
1619
1663
|
this.isStreaming = false;
|
|
1620
1664
|
this.view.requestRender();
|
|
1621
|
-
|
|
1665
|
+
if (signal.aborted) {
|
|
1666
|
+
this.dequeueQueuedUserMessagesIntoEditor();
|
|
1667
|
+
}
|
|
1668
|
+
else {
|
|
1669
|
+
void this.drainQueuedUserMessages();
|
|
1670
|
+
}
|
|
1622
1671
|
}
|
|
1623
1672
|
}
|
|
1624
1673
|
async compactSessionSummaryAndLast(guidance) {
|
|
1625
1674
|
this.view.addSystemMessage("summarizing session...", "success");
|
|
1626
1675
|
this.isStreaming = true;
|
|
1627
1676
|
this.view.startWorkingIcon();
|
|
1677
|
+
const { busyTask, signal } = this.createAbortBusyTask();
|
|
1678
|
+
this.beginBusyTask(busyTask);
|
|
1628
1679
|
try {
|
|
1629
1680
|
const result = await this.engine.compact({
|
|
1630
1681
|
mode: "with-last-assistant",
|
|
1631
1682
|
guidance,
|
|
1683
|
+
signal,
|
|
1632
1684
|
});
|
|
1633
1685
|
this.applyCompactedHistoryUi(result.compactionMessage);
|
|
1634
1686
|
const successText = result.includedLastAssistant
|
|
@@ -1637,13 +1689,21 @@ export class ChatController {
|
|
|
1637
1689
|
this.view.addSystemMessage(successText, "success");
|
|
1638
1690
|
}
|
|
1639
1691
|
catch (err) {
|
|
1640
|
-
|
|
1692
|
+
if (!signal.aborted) {
|
|
1693
|
+
this.handleCompactionError(err);
|
|
1694
|
+
}
|
|
1641
1695
|
}
|
|
1642
1696
|
finally {
|
|
1697
|
+
this.endBusyTask(busyTask);
|
|
1643
1698
|
this.view.stopWorkingIcon();
|
|
1644
1699
|
this.isStreaming = false;
|
|
1645
1700
|
this.view.requestRender();
|
|
1646
|
-
|
|
1701
|
+
if (signal.aborted) {
|
|
1702
|
+
this.dequeueQueuedUserMessagesIntoEditor();
|
|
1703
|
+
}
|
|
1704
|
+
else {
|
|
1705
|
+
void this.drainQueuedUserMessages();
|
|
1706
|
+
}
|
|
1647
1707
|
}
|
|
1648
1708
|
}
|
|
1649
1709
|
pruneToolResults(strategy, extra) {
|
|
@@ -1815,13 +1875,15 @@ export class ChatController {
|
|
|
1815
1875
|
this.view.addSystemMessage("sampling prune candidates...", "success");
|
|
1816
1876
|
this.isStreaming = true;
|
|
1817
1877
|
this.view.startWorkingIcon();
|
|
1878
|
+
const { busyTask, signal } = this.createAbortBusyTask();
|
|
1879
|
+
this.beginBusyTask(busyTask);
|
|
1818
1880
|
try {
|
|
1819
1881
|
const prompt = this.buildSmartPrunePrompt({
|
|
1820
1882
|
history,
|
|
1821
1883
|
targetTokens,
|
|
1822
1884
|
guidance: parsed.guidance,
|
|
1823
1885
|
});
|
|
1824
|
-
const selection = await this.requestSmartPruneSelection(prompt);
|
|
1886
|
+
const selection = await this.requestSmartPruneSelection(prompt, signal);
|
|
1825
1887
|
if (selection.length === 0) {
|
|
1826
1888
|
const summary = getEditSummary();
|
|
1827
1889
|
if (this.hasAnyPrunedEditChanges(summary)) {
|
|
@@ -1858,13 +1920,21 @@ export class ChatController {
|
|
|
1858
1920
|
this.view.addSystemMessage(this.buildPruneSummaryMessage(summary, toPrune.length, prunedBytes), "success");
|
|
1859
1921
|
}
|
|
1860
1922
|
catch (err) {
|
|
1861
|
-
|
|
1923
|
+
if (!signal.aborted) {
|
|
1924
|
+
this.view.addSystemMessage(`prune failed: ${err.message}`, "error");
|
|
1925
|
+
}
|
|
1862
1926
|
}
|
|
1863
1927
|
finally {
|
|
1928
|
+
this.endBusyTask(busyTask);
|
|
1864
1929
|
this.view.stopWorkingIcon();
|
|
1865
1930
|
this.isStreaming = false;
|
|
1866
1931
|
this.view.requestRender();
|
|
1867
|
-
|
|
1932
|
+
if (signal.aborted) {
|
|
1933
|
+
this.dequeueQueuedUserMessagesIntoEditor();
|
|
1934
|
+
}
|
|
1935
|
+
else {
|
|
1936
|
+
void this.drainQueuedUserMessages();
|
|
1937
|
+
}
|
|
1868
1938
|
}
|
|
1869
1939
|
}
|
|
1870
1940
|
parsePruneFraction(extra) {
|
|
@@ -2301,7 +2371,7 @@ export class ChatController {
|
|
|
2301
2371
|
}
|
|
2302
2372
|
return selected;
|
|
2303
2373
|
}
|
|
2304
|
-
async requestSmartPruneSelection(prompt) {
|
|
2374
|
+
async requestSmartPruneSelection(prompt, signal) {
|
|
2305
2375
|
let apiKey;
|
|
2306
2376
|
try {
|
|
2307
2377
|
apiKey = await this.credentialResolver.getApiKey(this.currentPersona.model.provider, { sessionId: this.engine.sessionId });
|
|
@@ -2335,6 +2405,7 @@ export class ChatController {
|
|
|
2335
2405
|
}, {
|
|
2336
2406
|
...(reasoning ? { reasoning } : {}),
|
|
2337
2407
|
sessionId: `tau-prune-${randomUUID()}`,
|
|
2408
|
+
...(signal ? { signal } : {}),
|
|
2338
2409
|
...(apiKey && { apiKey }),
|
|
2339
2410
|
});
|
|
2340
2411
|
const final = await stream.result();
|
|
@@ -2655,9 +2726,23 @@ export class ChatController {
|
|
|
2655
2726
|
this.view.startWorkingIcon();
|
|
2656
2727
|
this.startTurnTimer();
|
|
2657
2728
|
this.assistantState = undefined;
|
|
2658
|
-
this.assistantTurnInterruptRequested = false;
|
|
2659
2729
|
this.startTurnCaffeinate();
|
|
2660
2730
|
let runResult = { aborted: false };
|
|
2731
|
+
let interruptRequested = false;
|
|
2732
|
+
const busyTask = {
|
|
2733
|
+
requestInterrupt: () => {
|
|
2734
|
+
if (interruptRequested) {
|
|
2735
|
+
return false;
|
|
2736
|
+
}
|
|
2737
|
+
const interrupted = this.runtime.interruptTurn();
|
|
2738
|
+
if (!interrupted) {
|
|
2739
|
+
return false;
|
|
2740
|
+
}
|
|
2741
|
+
interruptRequested = true;
|
|
2742
|
+
return true;
|
|
2743
|
+
},
|
|
2744
|
+
};
|
|
2745
|
+
this.beginBusyTask(busyTask);
|
|
2661
2746
|
try {
|
|
2662
2747
|
runResult = await this.runtime.runTurn((event) => this.onEvent(event));
|
|
2663
2748
|
}
|
|
@@ -2666,13 +2751,13 @@ export class ChatController {
|
|
|
2666
2751
|
this.view.addSystemMessage(message, "error");
|
|
2667
2752
|
}
|
|
2668
2753
|
finally {
|
|
2754
|
+
this.endBusyTask(busyTask);
|
|
2669
2755
|
await this.stopTurnCaffeinate();
|
|
2670
2756
|
const reason = runResult.aborted ? "aborted" : "interrupted";
|
|
2671
2757
|
this.view.finalizeToolUiPending(reason);
|
|
2672
2758
|
this.view.stopWorkingIcon();
|
|
2673
2759
|
this.stopTurnTimer();
|
|
2674
2760
|
this.isStreaming = false;
|
|
2675
|
-
this.assistantTurnInterruptRequested = false;
|
|
2676
2761
|
this.view.clearToolUiTransientState();
|
|
2677
2762
|
this.pendingIdleNotification = true;
|
|
2678
2763
|
this.view.requestRender();
|
|
@@ -2687,8 +2772,8 @@ export class ChatController {
|
|
|
2687
2772
|
// Direct Bash Execution (user ! commands) -------------------------------------------------------
|
|
2688
2773
|
async runBashCommand(command, opts) {
|
|
2689
2774
|
this.isStreaming = true;
|
|
2690
|
-
const
|
|
2691
|
-
this.
|
|
2775
|
+
const { busyTask, signal } = this.createAbortBusyTask();
|
|
2776
|
+
this.beginBusyTask(busyTask);
|
|
2692
2777
|
let wasAborted = false;
|
|
2693
2778
|
this.startTurnTimer();
|
|
2694
2779
|
const toolCallId = `bash-user-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -2705,7 +2790,7 @@ export class ChatController {
|
|
|
2705
2790
|
const startedAt = Date.now();
|
|
2706
2791
|
const { output, exitCode, truncated: captureTruncated, } = await this.toolBackend.runBash(command, {
|
|
2707
2792
|
cwd: effectiveWorkingDirectory,
|
|
2708
|
-
signal
|
|
2793
|
+
signal,
|
|
2709
2794
|
});
|
|
2710
2795
|
const durationMs = Math.max(0, Date.now() - startedAt);
|
|
2711
2796
|
const truncationInfo = await prepareBashOutput(output, captureTruncated, getBashOutputPolicy({ mode: "user" }), this.toolBackend);
|
|
@@ -2744,11 +2829,9 @@ export class ChatController {
|
|
|
2744
2829
|
this.refreshStatus();
|
|
2745
2830
|
}
|
|
2746
2831
|
finally {
|
|
2747
|
-
wasAborted =
|
|
2832
|
+
wasAborted = signal.aborted;
|
|
2833
|
+
this.endBusyTask(busyTask);
|
|
2748
2834
|
this.isStreaming = false;
|
|
2749
|
-
if (this.currentTurnAbort === abortController) {
|
|
2750
|
-
this.currentTurnAbort = undefined;
|
|
2751
|
-
}
|
|
2752
2835
|
this.stopTurnTimer();
|
|
2753
2836
|
this.view.requestRender();
|
|
2754
2837
|
if (wasAborted) {
|