@sortedyuan/maybecode 0.3.2 → 0.5.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 (295) hide show
  1. package/README.md +166 -55
  2. package/README.zh-CN.md +267 -0
  3. package/dist/application.d.ts +40 -34
  4. package/dist/application.d.ts.map +1 -1
  5. package/dist/application.js +290 -170
  6. package/dist/application.js.map +1 -1
  7. package/dist/args.d.ts +2 -2
  8. package/dist/args.d.ts.map +1 -1
  9. package/dist/args.js +50 -27
  10. package/dist/args.js.map +1 -1
  11. package/dist/bin.js +82 -71
  12. package/dist/bin.js.map +4 -4
  13. package/dist/cli/chunk-3IORPKBL.js +3572 -0
  14. package/dist/cli/chunk-3IORPKBL.js.map +7 -0
  15. package/dist/cli/{chunk-LVTRXPDS.js → chunk-4B2Z6KW7.js} +1547 -1536
  16. package/dist/cli/chunk-4B2Z6KW7.js.map +7 -0
  17. package/dist/cli/{chunk-JT6VBKOM.js → chunk-4WG6JIJJ.js} +127 -33
  18. package/dist/cli/{chunk-JT6VBKOM.js.map → chunk-4WG6JIJJ.js.map} +3 -3
  19. package/dist/cli/{chunk-K5JWPNSY.js → chunk-5SLWALZK.js} +3521 -16164
  20. package/dist/cli/chunk-5SLWALZK.js.map +7 -0
  21. package/dist/cli/chunk-MNBYXL5Y.js +132 -0
  22. package/dist/cli/chunk-MNBYXL5Y.js.map +7 -0
  23. package/dist/cli/{chunk-F4XRPLUM.js → chunk-SLWLUEHQ.js} +226 -86
  24. package/dist/cli/chunk-SLWLUEHQ.js.map +7 -0
  25. package/dist/cli/chunk-UQDZ55LQ.js +199 -0
  26. package/dist/cli/chunk-UQDZ55LQ.js.map +7 -0
  27. package/dist/cli/{chunk-7ISOHFS7.js → chunk-WMSG5ZTR.js} +717 -388
  28. package/dist/cli/chunk-WMSG5ZTR.js.map +7 -0
  29. package/dist/cli/chunk-WVIRS76R.js +4057 -0
  30. package/dist/cli/chunk-WVIRS76R.js.map +7 -0
  31. package/dist/cli/{chunk-HOU5ITUK.js → chunk-XTEMRAQW.js} +1 -1
  32. package/dist/cli/chunk-XTEMRAQW.js.map +7 -0
  33. package/dist/cli/chunk-Z3BOPBVU.js +91 -0
  34. package/dist/cli/chunk-Z3BOPBVU.js.map +7 -0
  35. package/dist/cli/{configured-WHY3GGAJ.js → configured-ACNM6TRQ.js} +10 -9
  36. package/dist/cli/{file-type-R2T7TBHI.js → file-type-HLHPV7WC.js} +817 -13
  37. package/dist/cli/file-type-HLHPV7WC.js.map +7 -0
  38. package/dist/cli/mcp-auth-XD5U7WCV.js +74 -0
  39. package/dist/cli/mcp-auth-XD5U7WCV.js.map +7 -0
  40. package/dist/cli/onboarding-WQWCPYEO.js +530 -0
  41. package/dist/cli/onboarding-WQWCPYEO.js.map +7 -0
  42. package/dist/cli/onboarding-check-HRRI5OJ7.js +32 -0
  43. package/dist/cli/onboarding-check-HRRI5OJ7.js.map +7 -0
  44. package/dist/cli/{retained-tui-VJ3VX4R3.js → retained-tui-QF5VMYLC.js} +788 -332
  45. package/dist/cli/retained-tui-QF5VMYLC.js.map +7 -0
  46. package/dist/cli/{sixel-LWTCM76G.js → sixel-ISZ4FZ3U.js} +37 -37
  47. package/dist/cli/{sixel-LWTCM76G.js.map → sixel-ISZ4FZ3U.js.map} +1 -1
  48. package/dist/cli/{team-T7G7PPNN.js → team-O2I5BXHM.js} +7 -6
  49. package/dist/cli/{team-v2-JGBBQ7FR.js → team-v2-YYO2G7DJ.js} +816 -390
  50. package/dist/cli/team-v2-YYO2G7DJ.js.map +7 -0
  51. package/dist/cli/{web-server-FOMNPY7Q.js → web-server-SXMH6FGA.js} +55 -19
  52. package/dist/cli/web-server-SXMH6FGA.js.map +7 -0
  53. package/dist/cli/{web-ui-COB6PKC5.js → web-ui-UWQUFE4W.js} +7 -6
  54. package/dist/config-schema.json +337 -0
  55. package/dist/configured.d.ts +18 -11
  56. package/dist/configured.d.ts.map +1 -1
  57. package/dist/configured.js +213 -390
  58. package/dist/configured.js.map +1 -1
  59. package/dist/controller.d.ts +19 -18
  60. package/dist/controller.d.ts.map +1 -1
  61. package/dist/delegation-commands.d.ts +1 -1
  62. package/dist/delegation-commands.d.ts.map +1 -1
  63. package/dist/delegation-commands.js +8 -8
  64. package/dist/delegation-commands.js.map +1 -1
  65. package/dist/errors.d.ts +2 -1
  66. package/dist/errors.d.ts.map +1 -1
  67. package/dist/errors.js +2 -1
  68. package/dist/errors.js.map +1 -1
  69. package/dist/events.d.ts +8 -6
  70. package/dist/events.d.ts.map +1 -1
  71. package/dist/fork-picker.d.ts.map +1 -1
  72. package/dist/fork-picker.js +19 -5
  73. package/dist/fork-picker.js.map +1 -1
  74. package/dist/git-request.d.ts +6 -6
  75. package/dist/git-request.d.ts.map +1 -1
  76. package/dist/git-request.js +28 -15
  77. package/dist/git-request.js.map +1 -1
  78. package/dist/goal-commands.d.ts +1 -1
  79. package/dist/goal-commands.d.ts.map +1 -1
  80. package/dist/goal-commands.js +12 -5
  81. package/dist/goal-commands.js.map +1 -1
  82. package/dist/index.d.ts +5 -37
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +3 -36
  85. package/dist/index.js.map +1 -1
  86. package/dist/instructions.d.ts +2 -7
  87. package/dist/instructions.d.ts.map +1 -1
  88. package/dist/instructions.js +7 -15
  89. package/dist/instructions.js.map +1 -1
  90. package/dist/keymap.d.ts +2 -2
  91. package/dist/keymap.d.ts.map +1 -1
  92. package/dist/keymap.js +2 -2
  93. package/dist/keymap.js.map +1 -1
  94. package/dist/maybecode-config.schema.json +1765 -0
  95. package/dist/mcp-auth.d.ts +1 -1
  96. package/dist/mcp-auth.d.ts.map +1 -1
  97. package/dist/mcp-auth.js +11 -8
  98. package/dist/mcp-auth.js.map +1 -1
  99. package/dist/mcp-commands.d.ts.map +1 -1
  100. package/dist/mcp-commands.js +18 -4
  101. package/dist/mcp-commands.js.map +1 -1
  102. package/dist/mcp-interaction-ui.d.ts.map +1 -1
  103. package/dist/mcp-interaction-ui.js +11 -4
  104. package/dist/mcp-interaction-ui.js.map +1 -1
  105. package/dist/media-history.d.ts +1 -1
  106. package/dist/media-history.d.ts.map +1 -1
  107. package/dist/media-history.js +8 -2
  108. package/dist/media-history.js.map +1 -1
  109. package/dist/model-picker.d.ts.map +1 -1
  110. package/dist/model-picker.js +10 -11
  111. package/dist/model-picker.js.map +1 -1
  112. package/dist/model-transition.d.ts +11 -0
  113. package/dist/model-transition.d.ts.map +1 -0
  114. package/dist/model-transition.js +48 -0
  115. package/dist/model-transition.js.map +1 -0
  116. package/dist/model.d.ts +2 -2
  117. package/dist/model.d.ts.map +1 -1
  118. package/dist/model.js +8 -4
  119. package/dist/model.js.map +1 -1
  120. package/dist/onboarding-check.d.ts +3 -0
  121. package/dist/onboarding-check.d.ts.map +1 -0
  122. package/dist/onboarding-check.js +22 -0
  123. package/dist/onboarding-check.js.map +1 -0
  124. package/dist/onboarding.d.ts +6 -0
  125. package/dist/onboarding.d.ts.map +1 -0
  126. package/dist/onboarding.js +463 -0
  127. package/dist/onboarding.js.map +1 -0
  128. package/dist/plugins/application.d.ts +6 -4
  129. package/dist/plugins/application.d.ts.map +1 -1
  130. package/dist/plugins/application.js +106 -52
  131. package/dist/plugins/application.js.map +1 -1
  132. package/dist/plugins/instructions.d.ts +1 -1
  133. package/dist/plugins/instructions.d.ts.map +1 -1
  134. package/dist/plugins/instructions.js +24 -11
  135. package/dist/plugins/instructions.js.map +1 -1
  136. package/dist/plugins/resources.d.ts +2 -2
  137. package/dist/plugins/resources.d.ts.map +1 -1
  138. package/dist/plugins/resources.js +22 -12
  139. package/dist/plugins/resources.js.map +1 -1
  140. package/dist/policy.d.ts +1 -1
  141. package/dist/policy.d.ts.map +1 -1
  142. package/dist/policy.js +59 -11
  143. package/dist/policy.js.map +1 -1
  144. package/dist/run.d.ts +2 -0
  145. package/dist/run.d.ts.map +1 -1
  146. package/dist/run.js +38 -33
  147. package/dist/run.js.map +1 -1
  148. package/dist/runtime-instructions.d.ts +1 -1
  149. package/dist/runtime-instructions.d.ts.map +1 -1
  150. package/dist/runtime-instructions.js +15 -7
  151. package/dist/runtime-instructions.js.map +1 -1
  152. package/dist/session-navigation.d.ts +6 -0
  153. package/dist/session-navigation.d.ts.map +1 -0
  154. package/dist/session-navigation.js +64 -0
  155. package/dist/session-navigation.js.map +1 -0
  156. package/dist/session-picker.d.ts +1 -1
  157. package/dist/session-picker.d.ts.map +1 -1
  158. package/dist/session-picker.js +23 -32
  159. package/dist/session-picker.js.map +1 -1
  160. package/dist/settings.d.ts +718 -0
  161. package/dist/settings.d.ts.map +1 -0
  162. package/dist/settings.js +14 -0
  163. package/dist/settings.js.map +1 -0
  164. package/dist/skills.d.ts +1 -1
  165. package/dist/skills.d.ts.map +1 -1
  166. package/dist/skills.js +10 -12
  167. package/dist/skills.js.map +1 -1
  168. package/dist/slash-commands.d.ts +2 -2
  169. package/dist/slash-commands.d.ts.map +1 -1
  170. package/dist/slash-commands.js +149 -67
  171. package/dist/slash-commands.js.map +1 -1
  172. package/dist/team-execution.d.ts +10 -0
  173. package/dist/team-execution.d.ts.map +1 -0
  174. package/dist/team-execution.js +29 -0
  175. package/dist/team-execution.js.map +1 -0
  176. package/dist/team-patches.d.ts.map +1 -1
  177. package/dist/team-patches.js +149 -52
  178. package/dist/team-patches.js.map +1 -1
  179. package/dist/team-plan.d.ts +2 -2
  180. package/dist/team-plan.d.ts.map +1 -1
  181. package/dist/team-plan.js +115 -34
  182. package/dist/team-plan.js.map +1 -1
  183. package/dist/team-recovery.d.ts +1 -1
  184. package/dist/team-recovery.d.ts.map +1 -1
  185. package/dist/team-recovery.js +40 -10
  186. package/dist/team-recovery.js.map +1 -1
  187. package/dist/team-v2.d.ts.map +1 -1
  188. package/dist/team-v2.js +335 -104
  189. package/dist/team-v2.js.map +1 -1
  190. package/dist/team-verification.d.ts +4 -2
  191. package/dist/team-verification.d.ts.map +1 -1
  192. package/dist/team-verification.js +300 -97
  193. package/dist/team-verification.js.map +1 -1
  194. package/dist/team.d.ts +2 -2
  195. package/dist/team.d.ts.map +1 -1
  196. package/dist/team.js +172 -62
  197. package/dist/team.js.map +1 -1
  198. package/dist/terminal-web.d.ts +1 -0
  199. package/dist/terminal-web.d.ts.map +1 -1
  200. package/dist/terminal-web.js +28 -10
  201. package/dist/terminal-web.js.map +1 -1
  202. package/dist/ui/actions.d.ts.map +1 -1
  203. package/dist/ui/actions.js.map +1 -1
  204. package/dist/ui/retained-tui.d.ts.map +1 -1
  205. package/dist/ui/retained-tui.js +231 -173
  206. package/dist/ui/retained-tui.js.map +1 -1
  207. package/dist/ui/{prototype-view.d.ts → terminal-view.d.ts} +7 -7
  208. package/dist/ui/terminal-view.d.ts.map +1 -0
  209. package/dist/ui/{prototype-view.js → terminal-view.js} +185 -98
  210. package/dist/ui/terminal-view.js.map +1 -0
  211. package/dist/web-commands.d.ts.map +1 -1
  212. package/dist/web-commands.js +160 -57
  213. package/dist/web-commands.js.map +1 -1
  214. package/dist/web-extension.d.ts.map +1 -1
  215. package/dist/web-extension.js +14 -4
  216. package/dist/web-extension.js.map +1 -1
  217. package/dist/web-server.d.ts.map +1 -1
  218. package/dist/web-server.js +2 -1
  219. package/dist/web-server.js.map +1 -1
  220. package/dist/web-ui.d.ts.map +1 -1
  221. package/dist/web-ui.js +167 -40
  222. package/dist/web-ui.js.map +1 -1
  223. package/dist/workspace-configuration.d.ts +7 -0
  224. package/dist/workspace-configuration.d.ts.map +1 -0
  225. package/dist/workspace-configuration.js +134 -0
  226. package/dist/workspace-configuration.js.map +1 -0
  227. package/dist/workspace-state.d.ts +20 -0
  228. package/dist/workspace-state.d.ts.map +1 -0
  229. package/dist/workspace-state.js +2 -0
  230. package/dist/workspace-state.js.map +1 -0
  231. package/dist/workspace.d.ts +26 -43
  232. package/dist/workspace.d.ts.map +1 -1
  233. package/dist/workspace.js +261 -323
  234. package/dist/workspace.js.map +1 -1
  235. package/package.json +21 -31
  236. package/dist/cli/chunk-7ISOHFS7.js.map +0 -7
  237. package/dist/cli/chunk-F4XRPLUM.js.map +0 -7
  238. package/dist/cli/chunk-H7SNBFO4.js +0 -5522
  239. package/dist/cli/chunk-H7SNBFO4.js.map +0 -7
  240. package/dist/cli/chunk-HOU5ITUK.js.map +0 -7
  241. package/dist/cli/chunk-K5JWPNSY.js.map +0 -7
  242. package/dist/cli/chunk-LVTRXPDS.js.map +0 -7
  243. package/dist/cli/chunk-VAZMCNBB.js +0 -686
  244. package/dist/cli/chunk-VAZMCNBB.js.map +0 -7
  245. package/dist/cli/chunk-WOFEI42C.js +0 -7820
  246. package/dist/cli/chunk-WOFEI42C.js.map +0 -7
  247. package/dist/cli/file-type-R2T7TBHI.js.map +0 -7
  248. package/dist/cli/mcp-auth-UNRHSIKJ.js +0 -63
  249. package/dist/cli/mcp-auth-UNRHSIKJ.js.map +0 -7
  250. package/dist/cli/retained-tui-VJ3VX4R3.js.map +0 -7
  251. package/dist/cli/team-v2-JGBBQ7FR.js.map +0 -7
  252. package/dist/cli/tui-SA74DKMA.js +0 -1705
  253. package/dist/cli/tui-SA74DKMA.js.map +0 -7
  254. package/dist/cli/web-server-FOMNPY7Q.js.map +0 -7
  255. package/dist/delegation.d.ts +0 -2
  256. package/dist/delegation.d.ts.map +0 -1
  257. package/dist/delegation.js +0 -2
  258. package/dist/delegation.js.map +0 -1
  259. package/dist/history-memory.d.ts +0 -2
  260. package/dist/history-memory.d.ts.map +0 -1
  261. package/dist/history-memory.js +0 -2
  262. package/dist/history-memory.js.map +0 -1
  263. package/dist/subagent-budget.d.ts +0 -2
  264. package/dist/subagent-budget.d.ts.map +0 -1
  265. package/dist/subagent-budget.js +0 -2
  266. package/dist/subagent-budget.js.map +0 -1
  267. package/dist/subagent-files.d.ts +0 -2
  268. package/dist/subagent-files.d.ts.map +0 -1
  269. package/dist/subagent-files.js +0 -2
  270. package/dist/subagent-files.js.map +0 -1
  271. package/dist/subagent-host.d.ts +0 -2
  272. package/dist/subagent-host.d.ts.map +0 -1
  273. package/dist/subagent-host.js +0 -2
  274. package/dist/subagent-host.js.map +0 -1
  275. package/dist/subagent-instructions.d.ts +0 -2
  276. package/dist/subagent-instructions.d.ts.map +0 -1
  277. package/dist/subagent-instructions.js +0 -2
  278. package/dist/subagent-instructions.js.map +0 -1
  279. package/dist/subagents.d.ts +0 -2
  280. package/dist/subagents.d.ts.map +0 -1
  281. package/dist/subagents.js +0 -2
  282. package/dist/subagents.js.map +0 -1
  283. package/dist/summarizer.d.ts +0 -2
  284. package/dist/summarizer.d.ts.map +0 -1
  285. package/dist/summarizer.js +0 -2
  286. package/dist/summarizer.js.map +0 -1
  287. package/dist/tui.d.ts +0 -7
  288. package/dist/tui.d.ts.map +0 -1
  289. package/dist/tui.js +0 -1109
  290. package/dist/tui.js.map +0 -1
  291. package/dist/ui/prototype-view.d.ts.map +0 -1
  292. package/dist/ui/prototype-view.js.map +0 -1
  293. /package/dist/cli/{configured-WHY3GGAJ.js.map → configured-ACNM6TRQ.js.map} +0 -0
  294. /package/dist/cli/{team-T7G7PPNN.js.map → team-O2I5BXHM.js.map} +0 -0
  295. /package/dist/cli/{web-ui-COB6PKC5.js.map → web-ui-UWQUFE4W.js.map} +0 -0
package/dist/tui.js DELETED
@@ -1,1109 +0,0 @@
1
- import { userInfo } from "node:os";
2
- import { sanitizeTerminalText, TerminalImages, encodeImage, workspaceGitLabel } from "@sortedyuan/tui";
3
- import { createMaybeCodeSlashCommandSuggester, executeMaybeCodeSlashCommand, formatMaybeCodeMcpStatus, parseMaybeCodeSlashCommand, } from "./slash-commands.js";
4
- import { createNodeTerminal } from "@sortedyuan/tui/node-terminal";
5
- import { runModelPicker } from "./model-picker.js";
6
- import { runSessionPicker } from "./session-picker.js";
7
- import { runForkPicker, runChangesPicker } from "./fork-picker.js";
8
- import { presentMcpInteraction } from "./mcp-interaction-ui.js";
9
- import { MaybeCodeTerminalWeb } from "./terminal-web.js";
10
- import { formatGoal } from "./goal-commands.js";
11
- import { renderDelegationTask } from "./delegation-commands.js";
12
- export async function runTerminalUI(app, options = {}) {
13
- const terminal = options.terminal ?? createNodeTerminal();
14
- terminal.imageSupport?.query();
15
- await terminal.imageSupport?.ready();
16
- let terminalClosed = false;
17
- const closeTerminal = () => {
18
- if (terminalClosed)
19
- return;
20
- terminalClosed = true;
21
- terminal.close();
22
- };
23
- const renderer = new TerminalRenderer(terminal);
24
- const slashCommandSuggestions = createMaybeCodeSlashCommandSuggester(app);
25
- let activeQuestion;
26
- let inputQuestionController;
27
- let inputContinuation = false;
28
- let gitLabel = "";
29
- let interactionDone = Promise.resolve();
30
- const pendingRuns = new Set();
31
- const observeRun = (result) => {
32
- const pending = result.then(() => undefined, error => {
33
- if (!terminalClosed && !isCancellation(error))
34
- terminal.write(`\nError: ${sanitizeTerminalText(errorMessage(error))}\n`);
35
- }).finally(() => pendingRuns.delete(pending));
36
- pendingRuns.add(pending);
37
- };
38
- let exit = false;
39
- const handleProcessSignal = () => {
40
- exit = true;
41
- if (app.isRunning)
42
- app.cancel("Interrupted by process signal");
43
- activeQuestion?.abort();
44
- closeTerminal();
45
- };
46
- process.on("SIGINT", handleProcessSignal);
47
- process.on("SIGTERM", handleProcessSignal);
48
- const question = async (prompt, questionOptions = {}) => {
49
- inputQuestionController?.abort();
50
- const previousInteraction = interactionDone;
51
- let release;
52
- interactionDone = new Promise(resolve => { release = resolve; });
53
- const controller = new AbortController();
54
- activeQuestion = controller;
55
- try {
56
- await previousInteraction;
57
- return await terminal.question(prompt, {
58
- ...questionOptions,
59
- signal: questionOptions.signal === undefined ? controller.signal : AbortSignal.any([controller.signal, questionOptions.signal]),
60
- });
61
- }
62
- finally {
63
- release();
64
- if (activeQuestion === controller)
65
- activeQuestion = undefined;
66
- }
67
- };
68
- const inputQuestion = async (prompt, questionOptions = {}) => {
69
- await interactionDone;
70
- const controller = new AbortController();
71
- inputQuestionController = controller;
72
- inputContinuation = prompt.endsWith("... ");
73
- activeQuestion = controller;
74
- try {
75
- return await terminal.question(prompt, { ...questionOptions, signal: controller.signal });
76
- }
77
- finally {
78
- if (activeQuestion === controller)
79
- activeQuestion = undefined;
80
- if (inputQuestionController === controller)
81
- inputQuestionController = undefined;
82
- }
83
- };
84
- const removeInterrupt = terminal.onInterrupt?.(() => {
85
- if (app.isRunning) {
86
- terminal.write("\nCancelling current operation...\n");
87
- app.cancel("Interrupted");
88
- activeQuestion?.abort();
89
- }
90
- else {
91
- exit = true;
92
- activeQuestion?.abort();
93
- }
94
- });
95
- const web = new MaybeCodeTerminalWeb(app, handleProcessSignal);
96
- const eventTask = consumeEvents(app, renderer, question, web.events, () => {
97
- if (inputQuestionController !== undefined)
98
- terminal.updatePrompt?.(inputPrompt(app.permissionMode === "yolo", inputContinuation, gitLabel));
99
- });
100
- const refreshWorkspace = async () => {
101
- if (!app.getWorkspaceGit || terminalClosed)
102
- return;
103
- try {
104
- gitLabel = workspaceGitLabel(await app.getWorkspaceGit());
105
- }
106
- catch (error) {
107
- gitLabel = `Git 错误:${sanitizeTerminalText(errorMessage(error))}`;
108
- }
109
- if (inputQuestionController)
110
- terminal.updatePrompt?.(inputPrompt(app.permissionMode === "yolo", inputContinuation, gitLabel));
111
- };
112
- await refreshWorkspace();
113
- let refreshingWorkspace = false;
114
- const workspaceTimer = app.getWorkspaceGit ? setInterval(() => {
115
- if (refreshingWorkspace || terminalClosed)
116
- return;
117
- refreshingWorkspace = true;
118
- void refreshWorkspace().finally(() => { refreshingWorkspace = false; });
119
- }, 2_000) : undefined;
120
- terminal.write(sanitizeTerminalText(`MaybeCode\nWorkspace: ${app.workspace}\n` +
121
- `Model: ${modelLabel(app)}\n` +
122
- (app.permissionMode === "yolo" ? "YOLO · Auto-approve\n" : "") +
123
- `${renderInstructionSources(app)}` +
124
- "Type /help for commands. End a line with \\ for multiline input.\n"));
125
- try {
126
- while (!exit) {
127
- let input;
128
- try {
129
- input = await readInput(inputQuestion, slashCommandSuggestions, () => app.permissionMode === "yolo", () => gitLabel);
130
- }
131
- catch (error) {
132
- if (isAbortError(error)) {
133
- if (exit)
134
- break;
135
- continue;
136
- }
137
- throw error;
138
- }
139
- if (input === "")
140
- continue;
141
- terminal.addHistory?.(input);
142
- if (input.startsWith("/")) {
143
- try {
144
- exit = await handleCommand(input, app, terminal, question, web, observeRun);
145
- }
146
- catch (error) {
147
- if (!isCancellation(error) && !isAbortError(error)) {
148
- terminal.write(`\nError: ${sanitizeTerminalText(errorMessage(error))}\n`);
149
- }
150
- }
151
- continue;
152
- }
153
- try {
154
- const run = await app.submit({ input });
155
- observeRun(run.result);
156
- }
157
- catch (error) {
158
- if (!isCancellation(error)) {
159
- terminal.write(`\nError: ${sanitizeTerminalText(errorMessage(error))}\n`);
160
- }
161
- }
162
- }
163
- }
164
- finally {
165
- process.removeListener("SIGINT", handleProcessSignal);
166
- if (workspaceTimer)
167
- clearInterval(workspaceTimer);
168
- process.removeListener("SIGTERM", handleProcessSignal);
169
- activeQuestion?.abort();
170
- removeInterrupt?.();
171
- // Restore readline/raw terminal state before potentially slow application
172
- // cancellation and event-drain work.
173
- closeTerminal();
174
- await web.close();
175
- await Promise.all(pendingRuns);
176
- await eventTask;
177
- }
178
- }
179
- async function consumeEvents(app, renderer, question, events, permissionModeChanged) {
180
- const pending = new Map();
181
- let lastGoalDisplay = "";
182
- let prompts = Promise.resolve();
183
- const enqueue = (id, work, expiresAt) => {
184
- const controller = new AbortController();
185
- pending.set(id, controller);
186
- const signal = expiresAt === undefined ? controller.signal : AbortSignal.any([controller.signal, AbortSignal.timeout(Math.max(0, Math.ceil(expiresAt - Date.now())))]);
187
- prompts = prompts.then(async () => { if (!signal.aborted)
188
- await work(signal); }).catch(() => {
189
- if (!signal.aborted) {
190
- renderer.write("\nInteraction could not be completed.\n");
191
- app.cancel("User interaction is unavailable");
192
- }
193
- }).finally(() => pending.delete(id));
194
- };
195
- for await (const event of events) {
196
- if (event.type === "goal.changed") {
197
- const display = `${event.goal.id}:${event.goal.status}:${event.goal.progress}`;
198
- if (display !== lastGoalDisplay) {
199
- lastGoalDisplay = display;
200
- renderer.write(`\n${sanitizeTerminalText(formatGoal(event.goal))}\n`);
201
- }
202
- }
203
- else if (event.type === "permission-mode.changed") {
204
- permissionModeChanged();
205
- renderer.write(event.mode === "yolo" ? "\nYOLO enabled\n" : "\nYOLO disabled\n");
206
- }
207
- else if (event.type === "run.event") {
208
- await renderer.runEvent(event.event);
209
- }
210
- else if (event.type === "permission.event") {
211
- if (event.event.type === "approval.requested") {
212
- enqueue(`approval:${event.event.request.id}`, (signal) => handlePermissionEvent(event.event, app, renderer, (prompt, options) => question(prompt, { ...options, signal })));
213
- }
214
- else {
215
- if (event.event.type === "approval.resolved" || event.event.type === "approval.cancelled")
216
- pending.get(`approval:${event.event.requestId}`)?.abort();
217
- renderer.permissionEvent(event.event);
218
- }
219
- }
220
- else if (event.type === "mcp.interaction.requested") {
221
- enqueue(event.request.id, (signal) => presentMcpInteraction(event.request, app, (prompt, signal) => question(prompt, { history: false, signal }), signal), event.request.expiresAt);
222
- }
223
- else if (event.type === "mcp.interaction.settled") {
224
- pending.get(event.requestId)?.abort();
225
- }
226
- else if (event.type === "change.preview") {
227
- renderer.changePreview(event);
228
- }
229
- else if (event.type === "context.compacted") {
230
- renderer.contextCompacted(event);
231
- }
232
- else if (event.type === "context.compaction.failed") {
233
- renderer.contextCompactionFailed(event);
234
- }
235
- else if (event.type === "model.changed") {
236
- renderer.modelChanged(event.model);
237
- }
238
- else if (event.type === "model.default.changed") {
239
- renderer.defaultModelChanged(event.profile);
240
- }
241
- else if (event.type === "delegation.started" ||
242
- event.type === "delegation.finished") {
243
- renderer.delegationLifecycle(event, app.delegationNotifications === true);
244
- }
245
- else if (event.type === "delegation.event") {
246
- const inner = event.event;
247
- if (inner.type === "permission.event") {
248
- if (inner.event.type === "approval.requested") {
249
- enqueue(`approval:${inner.event.request.id}`, (signal) => handlePermissionEvent(inner.event, app, renderer, (prompt, options) => question(prompt, { ...options, signal })));
250
- }
251
- else {
252
- if (inner.event.type === "approval.resolved" || inner.event.type === "approval.cancelled") {
253
- pending.get(`approval:${inner.event.requestId}`)?.abort();
254
- }
255
- renderer.permissionEvent(inner.event);
256
- }
257
- }
258
- else {
259
- await renderer.delegationEvent(event);
260
- }
261
- }
262
- else if (event.type === "delegation.updated") {
263
- renderer.delegationState(event.state, app.delegationNotifications === true);
264
- }
265
- else if (event.type === "mcp.resource.updated" ||
266
- event.type === "mcp.resource.watch-closed" ||
267
- event.type === "mcp.server.connected" ||
268
- event.type === "mcp.server.catalog-updated" ||
269
- event.type === "mcp.server.failed" ||
270
- event.type === "mcp.server.disconnected") {
271
- renderer.mcpEvent(event);
272
- }
273
- else if (event.type === "workspace.git.changed") {
274
- if (event.checkpoint.error)
275
- renderer.write(`\nGit checkpoint:${sanitizeTerminalText(event.checkpoint.error)}\n`);
276
- }
277
- else if (event.type === "session.changed") {
278
- await renderer.sessionChanged(event, app);
279
- }
280
- }
281
- for (const controller of pending.values())
282
- controller.abort();
283
- await prompts;
284
- }
285
- async function handlePermissionEvent(event, app, renderer, question) {
286
- if (event.type !== "approval.requested") {
287
- renderer.permissionEvent(event);
288
- return;
289
- }
290
- renderer.approvalRequest(event.request);
291
- while (true) {
292
- const choices = event.request.grantKey === undefined
293
- ? "[a]llow once / [d]eny"
294
- : permissionChoices(event.request.tool.name) + (event.request.persistent ? ` / [p]ersistent (${event.request.persistent.description})` : "");
295
- let answer;
296
- try {
297
- answer = (await question(`${choices}: `, { history: false }))
298
- .trim()
299
- .toLowerCase();
300
- }
301
- catch (error) {
302
- if (isAbortError(error))
303
- return;
304
- throw error;
305
- }
306
- const decision = answer === "a" || answer === "allow" || answer === "y"
307
- ? "allow"
308
- : answer === "d" || answer === "deny" || answer === "n"
309
- ? "deny"
310
- : answer === "s" || answer === "session"
311
- ? "allow-session"
312
- : answer === "p" || answer === "persistent"
313
- ? "allow-persistent"
314
- : undefined;
315
- if (decision === undefined) {
316
- renderer.write(event.request.persistent ? "请输入 a、s、p 或 d。\n" : "请输入 a、s 或 d。\n");
317
- continue;
318
- }
319
- if (decision === "allow-session" && event.request.grantKey === undefined) {
320
- renderer.write("This request cannot be granted for the session.\n");
321
- continue;
322
- }
323
- if (decision === "allow-persistent" && !event.request.persistent) {
324
- renderer.write("此请求没有可保存的授权范围。\n");
325
- continue;
326
- }
327
- await app.resolveApproval(event.request.id, decision, decision === "allow-persistent" ? { createdBy: `local:${userInfo().username}` } : undefined);
328
- return;
329
- }
330
- }
331
- async function handleCommand(input, app, terminal, question, web, observeRun) {
332
- const parsed = parseMaybeCodeSlashCommand(input);
333
- if (parsed.type === "command" &&
334
- parsed.definition.name === "/retry" &&
335
- parsed.arguments.length === 0) {
336
- terminal.write("\nRetrying the latest failed run...\n");
337
- }
338
- if (parsed.type === "command" &&
339
- parsed.definition.name === "/compact" &&
340
- parsed.arguments.length === 0) {
341
- terminal.write("\nPruning old tool results and summarizing context...\n");
342
- }
343
- const result = await executeMaybeCodeSlashCommand(input, app);
344
- if (result.type === "web.requested") {
345
- terminal.write(`\nWeb UI 已打开:${await web.open()}\n`);
346
- return false;
347
- }
348
- return renderSlashCommandResult(result, app, terminal, question, observeRun);
349
- }
350
- async function renderSlashCommandResult(result, app, terminal, question, observeRun) {
351
- switch (result.type) {
352
- case "exit":
353
- return true;
354
- case "help":
355
- terminal.write(`\n${renderSlashCommandHelp(result.commands)}`);
356
- break;
357
- case "instructions":
358
- terminal.write(sanitizeTerminalText(`\n${renderInstructionSources(app)}` +
359
- `Effective instructions:\n---\n${result.instructions.effective}\n---\n`));
360
- break;
361
- case "mcp.run-started":
362
- case "skill.run-started":
363
- case "retry.started":
364
- observeRun(result.run.result);
365
- break;
366
- case "status":
367
- terminal.write(`\n${renderStatus(app, result.inspection)}`);
368
- break;
369
- case "display":
370
- case "mcp.display":
371
- terminal.write(`\n${sanitizeTerminalText(result.text)}\n`);
372
- break;
373
- case "mcp.status":
374
- terminal.write(`\n${sanitizeTerminalText(formatMaybeCodeMcpStatus(result.servers))}\n`);
375
- break;
376
- case "context":
377
- terminal.write(result.inspection === undefined
378
- ? "\nContext inspection is not supported by the active context.\n"
379
- : `\n${renderContextInspection(result.inspection)}`);
380
- break;
381
- case "compacted":
382
- terminal.write(`\n${renderCompactionResult(result.result)}`);
383
- break;
384
- case "session.created":
385
- terminal.write(`\nCreated session ${result.sessionId}\n`);
386
- break;
387
- case "session.selection.requested": {
388
- const selection = await runSessionPicker({
389
- controller: app,
390
- terminal,
391
- sessions: result.sessions,
392
- question: (prompt) => question(prompt, { history: false }),
393
- });
394
- if (selection.type === "empty") {
395
- terminal.write("\nNo sessions found for this workspace.\n");
396
- }
397
- else if (selection.type === "resume") {
398
- await app.resumeSession(selection.sessionId);
399
- terminal.write(`\nResumed session ${selection.sessionId}\n`);
400
- }
401
- break;
402
- }
403
- case "fork.selection.requested": {
404
- const selection = await runForkPicker({ points: result.points, currentSessionId: app.sessionId, terminal, question: prompt => question(prompt, { history: false }) });
405
- if (selection) {
406
- if (!app.forkSession)
407
- throw new Error("Session 分支能力不可用。");
408
- const id = await app.forkSession(selection.pointId, selection.mode);
409
- terminal.write(sanitizeTerminalText(`\nSession 分支已创建:${id}\n`));
410
- }
411
- break;
412
- }
413
- case "changes.selection.requested":
414
- await runChangesPicker(terminal, result.diff, app);
415
- break;
416
- case "worktrees.display":
417
- terminal.write(sanitizeTerminalText(`\n${result.worktrees.length ? result.worktrees.map(item => `${item.id}\n${item.path}\n${item.branch} · ${item.status} · ${item.commit.slice(0, 12)}`).join("\n\n") : "没有已登记的 worktree。"}\n`));
418
- break;
419
- case "session.resumed":
420
- terminal.write(`\nResumed session ${result.sessionId}\n`);
421
- break;
422
- case "model.selection.requested": {
423
- let models = result.models;
424
- while (true) {
425
- const selection = await runModelPicker({
426
- controller: app,
427
- terminal,
428
- models,
429
- question: (prompt) => question(prompt, { history: false }),
430
- });
431
- if (selection.type === "empty") {
432
- terminal.write("\nNo model profiles are configured.\n");
433
- break;
434
- }
435
- if (selection.type === "cancelled")
436
- break;
437
- if (selection.type === "select") {
438
- await app.switchModel(selection.profile);
439
- break;
440
- }
441
- await app.setDefaultModel(selection.profile);
442
- models = await app.listModels();
443
- }
444
- break;
445
- }
446
- case "model.switched":
447
- break;
448
- case "model.not-found":
449
- terminal.write(`\nNo model profile starts with: ` +
450
- `${sanitizeTerminalText(result.query)}\n`);
451
- break;
452
- case "effort.selection.requested": {
453
- if (result.state.status !== "known") {
454
- terminal.write(`\n${renderReasoningEffortState(result.state)}\n`);
455
- break;
456
- }
457
- const choices = ["default", ...result.state.efforts];
458
- terminal.write(`\n${renderReasoningEffortState(result.state)}`);
459
- for (const [index, effort] of choices.entries()) {
460
- terminal.write(` ${index + 1}. ${sanitizeTerminalText(effort)}\n`);
461
- }
462
- const answer = (await question("Select an effort number or name (Enter to cancel): ", { history: false })).trim().toLowerCase();
463
- if (answer !== "") {
464
- const numeric = Number(answer);
465
- const selected = Number.isSafeInteger(numeric) && numeric > 0
466
- ? choices[numeric - 1]
467
- : choices.find((choice) => choice.startsWith(answer));
468
- if (selected === undefined) {
469
- terminal.write(`\nNo reasoning effort starts with: ` +
470
- `${sanitizeTerminalText(answer)}\n`);
471
- }
472
- else {
473
- const state = await app.setReasoningEffort(selected === "default" ? undefined : selected);
474
- terminal.write(`\n${renderReasoningEffortChanged(state)}\n`);
475
- }
476
- }
477
- break;
478
- }
479
- case "effort.changed":
480
- terminal.write(`\n${renderReasoningEffortChanged(result.state)}\n`);
481
- break;
482
- case "effort.not-found":
483
- terminal.write(`\nNo reasoning effort starts with: ` +
484
- `${sanitizeTerminalText(result.query)}\n`);
485
- break;
486
- case "usage":
487
- terminal.write(`\nUsage: ${sanitizeTerminalText(result.usage)}\n`);
488
- break;
489
- case "unknown":
490
- terminal.write(`\nUnknown command: ${sanitizeTerminalText(result.command)}. ` +
491
- "Type /help.\n");
492
- break;
493
- }
494
- return false;
495
- }
496
- function renderReasoningEffortState(state) {
497
- if (state.status === "unknown") {
498
- return "Reasoning effort capability is unknown for the active model.";
499
- }
500
- if (state.status === "unsupported") {
501
- return "The active model does not support effort-based reasoning.";
502
- }
503
- const current = sanitizeTerminalText(state.effectiveEffort ?? "provider/model default");
504
- return `Reasoning effort (source: ${sanitizeTerminalText(state.source)}, ` +
505
- `current: ${current})\n`;
506
- }
507
- function renderReasoningEffortChanged(state) {
508
- const effort = sanitizeTerminalText(state.effectiveEffort ?? "provider/model default");
509
- return `Reasoning effort: ${effort}${state.overridden ? " (runtime override)" : ""}`;
510
- }
511
- function renderSlashCommandHelp(commands) {
512
- const entries = commands.map((command) => {
513
- const aliases = command.aliases === undefined
514
- ? ""
515
- : ` (alias ${command.aliases.join(", ")})`;
516
- return ` ${command.usage}${aliases}\n ${command.description}`;
517
- }).join("\n");
518
- return `Commands:\n${entries}\n` +
519
- " Ctrl+C\n Cancel the active operation, or exit while idle\n" +
520
- " Multiline\n End a line with \\ to continue on the next line\n";
521
- }
522
- function inputPrompt(yolo, continuation, gitLabel = "") {
523
- return `${continuation ? "" : "\n"}${!continuation && gitLabel ? `[${gitLabel}] ` : ""}${yolo ? "YOLO · Auto-approve " : ""}${continuation ? "... " : "> "}`;
524
- }
525
- async function readInput(question, suggestions, yolo, gitLabel = () => "") {
526
- const lines = [];
527
- while (true) {
528
- let line = await question(inputPrompt(yolo(), lines.length > 0, gitLabel()), {
529
- history: false,
530
- ...(lines.length === 0 && suggestions !== undefined
531
- ? { suggestions }
532
- : {}),
533
- });
534
- if (lines.length === 0 && suggestions !== undefined) {
535
- const candidates = await suggestions(line);
536
- line = candidates.length === 1 ? candidates[0].value : line;
537
- }
538
- const continuation = removeLineContinuation(line);
539
- lines.push(continuation.text);
540
- if (!continuation.continued)
541
- return lines.join("\n").trim();
542
- }
543
- }
544
- function removeLineContinuation(line) {
545
- let backslashes = 0;
546
- for (let index = line.length - 1; index >= 0 && line[index] === "\\"; index--) {
547
- backslashes += 1;
548
- }
549
- if (backslashes % 2 === 0)
550
- return { text: line, continued: false };
551
- return { text: line.slice(0, -1), continued: true };
552
- }
553
- class TerminalRenderer {
554
- terminal;
555
- images;
556
- textStarted = false;
557
- reasoningStarted = false;
558
- changePreviews = new Map();
559
- delegationStatus = new Map();
560
- lastDelegationKey = "";
561
- label = "";
562
- toolOutputState = new Map();
563
- constructor(terminal) {
564
- this.terminal = terminal;
565
- this.images = new TerminalImages(undefined, terminal.imageSupport?.protocol, { cellSize: () => terminal.imageSupport?.cellSize });
566
- }
567
- write(text) {
568
- this.terminal.write(text);
569
- }
570
- tagged(text) {
571
- return this.label === "" ? text : `${this.label} ${text}`;
572
- }
573
- async runEvent(event) {
574
- switch (event.type) {
575
- case "input.received":
576
- this.endReasoning();
577
- for (const message of event.messages) {
578
- this.terminal.write(`\nYou: ${sanitizeTerminalText(textFromContent(message.content))}\n`);
579
- }
580
- break;
581
- case "model.started":
582
- this.textStarted = false;
583
- this.reasoningStarted = false;
584
- break;
585
- case "model.reasoning.delta":
586
- if (!this.reasoningStarted) {
587
- this.terminal.write(`\n${this.dim("[thinking] ")}`);
588
- this.reasoningStarted = true;
589
- }
590
- this.terminal.write(this.dim(sanitizeTerminalText(event.delta)));
591
- break;
592
- case "model.text.delta":
593
- this.endReasoning();
594
- if (!this.textStarted) {
595
- this.terminal.write(this.label === "" ? "\nMaybeCode: " : `\n${this.label} Sub-agent: `);
596
- this.textStarted = true;
597
- }
598
- this.terminal.write(sanitizeTerminalText(event.delta));
599
- break;
600
- case "model.retrying":
601
- this.endReasoning();
602
- if (this.textStarted)
603
- this.terminal.write("\n");
604
- this.terminal.write(`\nModel request failed: ${sanitizeTerminalText(event.error.message)}. ` +
605
- `Retrying in ${formatDelay(event.delayMs)} ` +
606
- `(attempt ${event.attempt}/${event.maxAttempts})...\n`);
607
- this.textStarted = false;
608
- this.reasoningStarted = false;
609
- break;
610
- case "model.completed": {
611
- this.endReasoning();
612
- if (event.message.content.some(part => part.type === "image")) {
613
- this.terminal.write("\nMaybeCode · 完整图文回复:\n");
614
- await this.renderContent(event.message.content);
615
- break;
616
- }
617
- const text = textFromContent(event.message.content);
618
- if (!this.textStarted && text !== "") {
619
- this.terminal.write(`\nMaybeCode: ${sanitizeTerminalText(text)}`);
620
- this.textStarted = true;
621
- }
622
- if (this.textStarted)
623
- this.terminal.write("\n");
624
- break;
625
- }
626
- case "tool.started":
627
- this.terminal.write(`\n${this.tagged(`→ ${sanitizeTerminalText(event.call.name)}${toolInputSummary(event.call.name, event.call.input)}`)}\n`);
628
- break;
629
- case "tool.output.delta": {
630
- const key = toolCallKey(event.runId, event.call.id);
631
- const previous = this.toolOutputState.get(key);
632
- if (previous !== undefined &&
633
- !previous.endsWithNewline &&
634
- previous.channel !== event.channel) {
635
- this.terminal.write("\n");
636
- }
637
- if (previous === undefined || previous.endsWithNewline ||
638
- previous.channel !== event.channel) {
639
- this.terminal.write(event.channel === undefined
640
- ? ""
641
- : `[${sanitizeTerminalText(event.channel)}] `);
642
- }
643
- this.terminal.write(sanitizeTerminalText(event.delta));
644
- this.toolOutputState.set(key, {
645
- endsWithNewline: event.delta.endsWith("\n"),
646
- ...(event.channel === undefined ? {} : { channel: event.channel }),
647
- });
648
- break;
649
- }
650
- case "tool.progress":
651
- this.endToolOutput(event.runId, event.call.id);
652
- this.terminal.write(`↳ ${sanitizeTerminalText(event.call.name)}: ` +
653
- `${sanitizeTerminalText(event.message)}\n`);
654
- break;
655
- case "tool.completed":
656
- this.endToolOutput(event.runId, event.call.id);
657
- this.renderToolCompleted(event);
658
- if (event.content.some(part => part.type === "image"))
659
- await this.renderContent(event.content);
660
- break;
661
- case "tool.failed":
662
- this.endToolOutput(event.runId, event.call.id);
663
- this.changePreviews.delete(toolCallKey(event.runId, event.call.id));
664
- this.terminal.write(`\n${this.tagged(`✗ ${sanitizeTerminalText(event.call.name)}: ` +
665
- `${sanitizeTerminalText(event.error.message)}`)}\n`);
666
- break;
667
- case "run.completed":
668
- case "run.yielded":
669
- this.clearRunPreviews(event.runId);
670
- break;
671
- case "run.failed":
672
- this.clearRunPreviews(event.runId);
673
- this.terminal.write(`\n${this.tagged(`Run failed: ${sanitizeTerminalText(event.error.message)}`)}\n`);
674
- break;
675
- case "run.cancelled":
676
- this.clearRunPreviews(event.runId);
677
- this.terminal.write(`\n${this.tagged(`Run cancelled${event.reason === undefined
678
- ? ""
679
- : `: ${sanitizeTerminalText(event.reason)}`}`)}\n`);
680
- break;
681
- }
682
- }
683
- async renderContent(content) {
684
- await this.images.prepare(content);
685
- for (const part of content) {
686
- if (part.type === "text")
687
- this.terminal.write(sanitizeTerminalText(part.text) + "\n");
688
- if (part.type === "image") {
689
- const result = this.images.render(part.source, { width: process.stdout.columns || 80, height: 30 });
690
- const image = result.images?.[0];
691
- this.terminal.write(result.lines.slice(0, image?.y ?? result.lines.length).join("\n") + "\n");
692
- if (image) {
693
- this.terminal.write("\n".repeat(image.rows) + `\x1b[${image.rows}A\x1b7` + encodeImage(image) + `\x1b8\x1b[${image.rows}B\r`);
694
- }
695
- }
696
- }
697
- }
698
- changePreview(event) {
699
- this.changePreviews.set(toolCallKey(event.runId, event.toolCallId), event.preview);
700
- if (event.preview.status === "unavailable") {
701
- this.terminal.write(`\nDiff unavailable for ${event.preview.tool} ` +
702
- `${sanitizeTerminalText(event.preview.path)}: ` +
703
- `${sanitizeTerminalText(event.preview.reason)}\n`);
704
- return;
705
- }
706
- this.terminal.write(`\nChange preview: ${sanitizeTerminalText(event.preview.path)} ` +
707
- `(${changeKindLabel(event.preview.kind)})\n`);
708
- this.terminal.write(event.preview.diff === ""
709
- ? "(no content changes)\n"
710
- : `${sanitizeTerminalText(event.preview.diff)}\n`);
711
- }
712
- contextCompacted(event) {
713
- const saved = Math.max(0, event.before.estimatedTokens - event.after.estimatedTokens);
714
- this.terminal.write(`\nContext automatically compacted with ${event.strategy}: ` +
715
- `${event.before.messageCount} -> ${event.after.messageCount} messages, ` +
716
- `~${formatNumber(saved)} tokens saved.\n`);
717
- }
718
- contextCompactionFailed(event) {
719
- const next = event.continuing
720
- ? " Trying the next strategy."
721
- : " No fallback strategies remain.";
722
- const message = sanitizeTerminalText(event.error.message)
723
- .replace(/[.!?]+$/u, "");
724
- this.terminal.write(`\nAutomatic context compaction failed with ${event.strategy} at ` +
725
- `~${formatNumber(event.before.effectiveTokens)} tokens: ` +
726
- `${message}.${next}\n`);
727
- }
728
- permissionEvent(event) {
729
- if (event.type === "approval.resolved") {
730
- this.terminal.write(`Permission: ${event.decision}\n`);
731
- }
732
- else if (event.type === "approval.cancelled") {
733
- this.terminal.write("Permission request cancelled.\n");
734
- }
735
- }
736
- approvalRequest(request) {
737
- const preview = this.changePreviews.get(toolCallKey(request.context.runId, request.context.toolCallId));
738
- if (preview !== undefined) {
739
- this.terminal.write(`\nApproval required for ${sanitizeTerminalText(request.tool.name)}: ` +
740
- `${sanitizeTerminalText(preview.path)}\n`);
741
- return;
742
- }
743
- this.terminal.write(`\nApproval required for ${sanitizeTerminalText(request.tool.name)}:\n` +
744
- `${sanitizeTerminalText(truncate(stringify(request.input), 1200))}\n`);
745
- }
746
- async sessionChanged(event, app) {
747
- this.terminal.write(`\nSession ${event.resumed ? "resumed" : "started"}: ${event.sessionId}\n`);
748
- if (!event.resumed)
749
- return;
750
- if (app.sessionId !== event.sessionId)
751
- return;
752
- const history = await app.history();
753
- const steering = new Map(history.flatMap(event => event.type === "input.steering.queued" ? [[event.input.inputId, event.input.message]] : []));
754
- for (const event of history) {
755
- if (event.type === "assistant.completed" && event.message.content.some(part => part.type === "image"))
756
- await this.renderContent(event.message.content);
757
- else
758
- renderHistory([event], this.terminal, steering);
759
- }
760
- }
761
- modelChanged(model) {
762
- const profile = model.profile === undefined
763
- ? ""
764
- : ` ${sanitizeTerminalText(model.profile)}`;
765
- this.terminal.write(`\nModel switched to${profile}: ` +
766
- `${sanitizeTerminalText(model.provider)}/` +
767
- `${sanitizeTerminalText(model.model)}\n`);
768
- }
769
- defaultModelChanged(profile) {
770
- this.terminal.write(`\nDefault model set to ${sanitizeTerminalText(profile)}\n`);
771
- }
772
- /** 子 Agent 请求的开始与结束,包含整棵任务树。 */
773
- delegationLifecycle(event, enabled) {
774
- if (event.type === "delegation.started") {
775
- this.delegationStatus.clear();
776
- this.lastDelegationKey = "";
777
- }
778
- if (!enabled)
779
- return;
780
- this.write(`\n${event.type === "delegation.started" ? "Sub-agent request" : "Sub-agent request finished"} ` +
781
- `${sanitizeTerminalText(event.state.requestId)}\n`);
782
- for (const task of delegationOrder(event.state.tasks)) {
783
- this.write(`${sanitizeTerminalText(renderDelegationTask(task))}\n`);
784
- }
785
- }
786
- /** 子任务运行期间的状态变化;状态未变化时不重复显示。 */
787
- delegationState(state, enabled) {
788
- const key = `${state.tasks.map((task) => `${task.id}:${task.status}`).join("|")}#${state.status}`;
789
- if (key === this.lastDelegationKey)
790
- return;
791
- this.lastDelegationKey = key;
792
- for (const task of delegationOrder(state.tasks)) {
793
- if (this.delegationStatus.get(task.id) === task.status)
794
- continue;
795
- this.delegationStatus.set(task.id, task.status);
796
- if (enabled)
797
- this.write(`${sanitizeTerminalText(renderDelegationTask(task))}\n`);
798
- }
799
- }
800
- /** 一个子 Agent Run 事件,带上任务身份前缀。 */
801
- async delegationEvent(event) {
802
- if (event.event.type !== "run.event") {
803
- this.endReasoning();
804
- this.write(`\n[${event.taskId}] ${sanitizeTerminalText(describeApplicationEvent(event.event))}\n`);
805
- return;
806
- }
807
- this.label = `[${event.taskId}]`;
808
- try {
809
- await this.runEvent(event.event.event);
810
- }
811
- finally {
812
- this.label = "";
813
- }
814
- }
815
- mcpEvent(event) {
816
- if (event.type === "mcp.resource.updated" || event.type === "mcp.resource.watch-closed") {
817
- this.terminal.write(`\n${sanitizeTerminalText(`${event.type}: ${event.serverId} ${event.uri}${event.type === "mcp.resource.watch-closed" ? ` (${event.reason})` : ""}`)}\n`);
818
- return;
819
- }
820
- if (event.type === "mcp.server.catalog-updated") {
821
- this.terminal.write(`\nMCP catalog updated: ${sanitizeTerminalText(event.serverId)} (revision ${event.revision})\n`);
822
- return;
823
- }
824
- if (event.type === "mcp.server.connected") {
825
- this.terminal.write(`\nMCP server connected: ${sanitizeTerminalText(event.serverId)} ` +
826
- `(${event.toolNames.length} tools)\n`);
827
- return;
828
- }
829
- if (event.type === "mcp.server.failed") {
830
- this.terminal.write(`\nMCP server failed: ${sanitizeTerminalText(event.serverId)}: ` +
831
- `${sanitizeTerminalText(event.diagnostic.message)}\n`);
832
- return;
833
- }
834
- this.terminal.write(`\nMCP server disconnected: ${sanitizeTerminalText(event.serverId)}\n`);
835
- }
836
- endReasoning() {
837
- if (!this.reasoningStarted)
838
- return;
839
- this.terminal.write(this.terminal.colors ? "\x1b[0m\n" : "\n");
840
- this.reasoningStarted = false;
841
- }
842
- dim(text) {
843
- return this.terminal.colors ? `\x1b[2m${text}\x1b[0m` : text;
844
- }
845
- renderToolCompleted(event) {
846
- const key = toolCallKey(event.runId, event.call.id);
847
- const preview = this.changePreviews.get(key);
848
- this.changePreviews.delete(key);
849
- if (preview?.status === "ready") {
850
- this.terminal.write(`${this.tagged(`✓ ${sanitizeTerminalText(event.call.name)}: ` +
851
- `${sanitizeTerminalText(preview.path)} ` +
852
- `(${changeKindLabel(preview.kind)}, ` +
853
- `+${preview.additions} -${preview.deletions})`)}\n`);
854
- return;
855
- }
856
- this.terminal.write(`${this.tagged(`${toolResultMarker(event.output)} ${sanitizeTerminalText(event.call.name)}${toolResultSummary(event.output)}`)}\n`);
857
- }
858
- clearRunPreviews(runId) {
859
- const prefix = `${runId}:`;
860
- for (const key of this.changePreviews.keys()) {
861
- if (key.startsWith(prefix))
862
- this.changePreviews.delete(key);
863
- }
864
- for (const key of this.toolOutputState.keys()) {
865
- if (key.startsWith(prefix))
866
- this.toolOutputState.delete(key);
867
- }
868
- }
869
- endToolOutput(runId, toolCallId) {
870
- const key = toolCallKey(runId, toolCallId);
871
- const state = this.toolOutputState.get(key);
872
- this.toolOutputState.delete(key);
873
- if (state?.endsWithNewline === false)
874
- this.terminal.write("\n");
875
- }
876
- }
877
- function renderHistory(history, terminal, steering) {
878
- for (const event of history) {
879
- if (event.type === "input.submitted") {
880
- const text = textFromContent(event.message.content);
881
- if (text !== "")
882
- terminal.write(`You: ${sanitizeTerminalText(text)}\n`);
883
- }
884
- else if (event.type === "input.steering.delivered") {
885
- for (const inputId of event.inputIds) {
886
- const message = steering.get(inputId);
887
- if (!message)
888
- throw new Error(`Missing steering input: ${inputId}`);
889
- const text = textFromContent(message.content);
890
- if (text !== "")
891
- terminal.write(`You: ${sanitizeTerminalText(text)}\n`);
892
- }
893
- }
894
- else if (event.type === "assistant.completed") {
895
- const text = textFromContent(event.message.content);
896
- if (text !== "") {
897
- terminal.write(`MaybeCode: ${sanitizeTerminalText(text)}\n`);
898
- }
899
- }
900
- else if (event.type === "tool.completed") {
901
- terminal.write(`${toolResultMarker(event.output)} ${sanitizeTerminalText(event.call.name)}${toolResultSummary(event.output)}\n`);
902
- }
903
- else if (event.type === "tool.failed") {
904
- terminal.write(`✗ ${sanitizeTerminalText(event.call.name)}: ` +
905
- `${sanitizeTerminalText(event.error.message)}\n`);
906
- }
907
- }
908
- }
909
- function textFromContent(content) {
910
- return content
911
- .filter((part) => part.type === "text")
912
- .map((part) => part.text)
913
- .join("");
914
- }
915
- function toolResultSummary(output) {
916
- if (typeof output !== "object" || output === null)
917
- return "";
918
- if ("path" in output && typeof output.path === "string") {
919
- return `: ${sanitizeTerminalText(output.path)}`;
920
- }
921
- if ("exitCode" in output && typeof output.exitCode === "number") {
922
- return `: exit ${output.exitCode}`;
923
- }
924
- return "";
925
- }
926
- function toolResultMarker(output) {
927
- if (typeof output === "object" &&
928
- output !== null &&
929
- "exitCode" in output &&
930
- typeof output.exitCode === "number" &&
931
- output.exitCode !== 0) {
932
- return "✗";
933
- }
934
- return "✓";
935
- }
936
- function permissionChoices(toolName) {
937
- const scope = toolName === "shell" || toolName === "bash"
938
- ? "same command"
939
- : toolName === "edit" || toolName === "write"
940
- ? "same path"
941
- : "same operation";
942
- return `[a]llow once / allow ${scope} for [s]ession / [d]eny`;
943
- }
944
- function toolInputSummary(toolName, input) {
945
- if ((toolName === "edit" || toolName === "write") &&
946
- typeof input === "object" &&
947
- input !== null &&
948
- "path" in input &&
949
- typeof input.path === "string") {
950
- return `: ${sanitizeTerminalText(input.path)}`;
951
- }
952
- return ` ${sanitizeTerminalText(truncate(stringify(input), 800))}`;
953
- }
954
- function toolCallKey(runId, toolCallId) {
955
- return `${runId}:${toolCallId}`;
956
- }
957
- function delegationOrder(tasks) {
958
- return [...tasks].sort((left, right) => left.depth - right.depth || left.id.localeCompare(right.id));
959
- }
960
- function describeApplicationEvent(event) {
961
- if (event.type === "context.compacted") {
962
- return `context compacted with ${event.strategy}: ${event.before.messageCount} -> ${event.after.messageCount} messages`;
963
- }
964
- if (event.type === "context.compaction.failed") {
965
- return `context compaction failed with ${event.strategy}: ${event.error.message}`;
966
- }
967
- if (event.type === "tool.presentation")
968
- return `tool presentation recorded`;
969
- return "session event";
970
- }
971
- function renderInstructionSources(app) {
972
- const system = app.instructions.system.source;
973
- const runtime = app.instructions.runtime?.source;
974
- const projects = app.instructions.projects;
975
- return `Instructions:\n system: ${instructionSourceLabel(system)}\n` +
976
- ` runtime: ${runtime === undefined ? "none" : instructionSourceLabel(runtime)}\n` +
977
- ` project: ${projects.length === 0 ? "none" : projects.map(project => instructionSourceLabel(project.source)).join("\n ")}\n`;
978
- }
979
- function renderStatus(app, inspection) {
980
- let output = `Status:\n` +
981
- ` model: ${modelLabel(app)}\n` +
982
- ` session: ${sanitizeTerminalText(app.sessionId)}\n` +
983
- ` workspace: ${sanitizeTerminalText(app.workspace)}\n` +
984
- ` permissions: ${app.permissionMode === "yolo" ? "YOLO · Auto-approve" : "Default"}\n`;
985
- if (inspection === undefined)
986
- return `${output} context: unavailable\n`;
987
- output += ` context: ~${formatNumber(inspection.effectiveTokens)}`;
988
- if (inspection.contextWindowTokens !== undefined) {
989
- output += ` / ${formatNumber(inspection.contextWindowTokens)} tokens ` +
990
- `(${((inspection.usageRatio ?? 0) * 100).toFixed(1)}%)`;
991
- }
992
- else {
993
- output += ` tokens (${inspection.measurementMethod})`;
994
- }
995
- return `${output}\n`;
996
- }
997
- function modelLabel(app) {
998
- if (app.modelInfo === undefined)
999
- return "custom";
1000
- const endpoint = `${sanitizeTerminalText(app.modelInfo.provider)}/` +
1001
- sanitizeTerminalText(app.modelInfo.model);
1002
- return app.modelInfo.profile === undefined
1003
- ? endpoint
1004
- : `${sanitizeTerminalText(app.modelInfo.profile)} (${endpoint})`;
1005
- }
1006
- function renderContextInspection(inspection) {
1007
- const roles = inspection.messagesByRole;
1008
- let output = `Context:\n` +
1009
- ` messages: ${inspection.messageCount} ` +
1010
- `(system ${roles.system}, user ${roles.user}, ` +
1011
- `assistant ${roles.assistant}, tool ${roles.tool})\n` +
1012
- ` instructions: ${formatBytes(inspection.instructionsBytes)}\n` +
1013
- ` message data: ${formatBytes(inspection.messageBytes)}\n` +
1014
- ` total: ${formatBytes(inspection.totalBytes)}\n` +
1015
- ` estimated tokens: ~${inspection.estimatedTokens} ` +
1016
- `(${inspection.tokenEstimateMethod})\n`;
1017
- if (inspection.contextWindowTokens === undefined) {
1018
- output += ` effective usage: ~${formatNumber(inspection.effectiveTokens)} ` +
1019
- `(${inspection.measurementMethod})\n`;
1020
- }
1021
- else {
1022
- output += ` effective usage: ~${formatNumber(inspection.effectiveTokens)} / ` +
1023
- `${formatNumber(inspection.contextWindowTokens)} ` +
1024
- `(${((inspection.usageRatio ?? 0) * 100).toFixed(1)}%)\n`;
1025
- }
1026
- if (inspection.measurementMethod === "measured+estimated") {
1027
- output += ` measurement: ${formatNumber(inspection.measuredInputTokens ?? 0)} measured + ~${formatNumber(inspection.estimatedTailTokens ?? 0)} estimated tail\n`;
1028
- }
1029
- else {
1030
- output += " measurement: estimated only\n";
1031
- }
1032
- if (inspection.remainingTokens !== undefined) {
1033
- output += ` remaining: ${formatNumber(inspection.remainingTokens)} tokens\n`;
1034
- }
1035
- if (inspection.inputBudgetTokens !== undefined) {
1036
- output += ` input budget: ${formatNumber(inspection.inputBudgetTokens)} tokens`;
1037
- if ((inspection.reservedTokens ?? 0) > 0) {
1038
- output += ` (${formatNumber(inspection.reservedTokens ?? 0)} reserved)`;
1039
- }
1040
- output += "\n";
1041
- }
1042
- if (inspection.compactTriggerTokens !== undefined) {
1043
- output += ` compaction threshold: ${formatNumber(inspection.compactTriggerTokens)} tokens (${inspection.shouldCompact === true ? "reached" : "not reached"})\n`;
1044
- }
1045
- return output;
1046
- }
1047
- function renderCompactionResult(result) {
1048
- if (!result.changed) {
1049
- return `No context changes were eligible for ${result.strategy}.\n`;
1050
- }
1051
- const saved = Math.max(0, result.before.estimatedTokens - result.after.estimatedTokens);
1052
- const ratio = result.before.estimatedTokens === 0
1053
- ? 0
1054
- : saved / result.before.estimatedTokens * 100;
1055
- return `Context compacted with ${result.strategy}:\n` +
1056
- ` messages: ${result.before.messageCount} -> ${result.after.messageCount}\n` +
1057
- ` estimated tokens: ~${formatNumber(result.before.estimatedTokens)} -> ` +
1058
- `~${formatNumber(result.after.estimatedTokens)}\n` +
1059
- ` saved: ~${formatNumber(saved)} tokens (${ratio.toFixed(1)}%)\n`;
1060
- }
1061
- function formatNumber(value) {
1062
- return value.toLocaleString("en-US");
1063
- }
1064
- function formatDelay(milliseconds) {
1065
- return milliseconds < 1000
1066
- ? `${milliseconds}ms`
1067
- : `${(milliseconds / 1000).toFixed(milliseconds % 1000 === 0 ? 0 : 1)}s`;
1068
- }
1069
- function formatBytes(bytes) {
1070
- if (bytes < 1024)
1071
- return `${bytes} B`;
1072
- return `${(bytes / 1024).toFixed(1)} KiB`;
1073
- }
1074
- function instructionSourceLabel(source) {
1075
- return source.type === "file" ? source.path : source.type;
1076
- }
1077
- function changeKindLabel(kind) {
1078
- switch (kind) {
1079
- case "create":
1080
- return "created";
1081
- case "update":
1082
- return "updated";
1083
- case "no-change":
1084
- return "unchanged";
1085
- }
1086
- }
1087
- function stringify(value) {
1088
- try {
1089
- return JSON.stringify(value, undefined, 2);
1090
- }
1091
- catch {
1092
- return String(value);
1093
- }
1094
- }
1095
- function truncate(text, limit) {
1096
- return text.length <= limit ? text : `${text.slice(0, limit)}…`;
1097
- }
1098
- function isAbortError(error) {
1099
- return error instanceof Error && error.name === "AbortError";
1100
- }
1101
- function isCancellation(error) {
1102
- return error instanceof Error && error.name === "RunCancelledError";
1103
- }
1104
- function errorMessage(error) {
1105
- if (error instanceof Error)
1106
- return error.message;
1107
- return typeof error === "string" ? error : "Unknown error";
1108
- }
1109
- //# sourceMappingURL=tui.js.map