@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
@@ -1,19 +1,22 @@
1
1
  import { FullscreenRenderer, NodeTerminalDriver, TuiRuntime, TerminalImages, createTerminalClipboard, } from "@sortedyuan/tui";
2
2
  import { TranscriptStore } from "@sortedyuan/tui/transcript";
3
+ import { omitUndefined } from "@sortedyuan/may/core";
3
4
  import { userInfo } from "node:os";
4
5
  import { createMaybeCodeSlashCommandSuggester, executeMaybeCodeSlashCommand, formatMaybeCodeMcpStatus, parseMaybeCodeSlashCommand, } from "../slash-commands.js";
5
- import { MaybeCodePrototypeView } from "./prototype-view.js";
6
+ import { MaybeCodeTerminalView } from "./terminal-view.js";
6
7
  import { presentMcpInteraction } from "../mcp-interaction-ui.js";
7
8
  import { MaybeCodeTerminalWeb } from "../terminal-web.js";
8
9
  import { formatGoal } from "../goal-commands.js";
9
- import { delegationOrder, formatDelegationTree, renderDelegationTask, } from "../delegation-commands.js";
10
+ import { delegationOrder, formatDelegationTree, renderDelegationTask } from "../delegation-commands.js";
10
11
  /** Experimental retained-screen MaybeCode frontend. */
11
12
  export async function runRetainedTerminalUI(app, options = {}) {
12
13
  const terminal = options.terminal ?? new NodeTerminalDriver();
13
14
  const renderer = options.renderer ?? new FullscreenRenderer(terminal);
14
15
  const store = new TranscriptStore();
15
16
  const displayedSteering = new Set();
16
- const images = new TerminalImages(undefined, terminal.imageSupport?.protocol, { cellSize: () => terminal.imageSupport?.cellSize });
17
+ const images = new TerminalImages(undefined, terminal.imageSupport?.protocol, {
18
+ cellSize: () => terminal.imageSupport?.cellSize,
19
+ });
17
20
  try {
18
21
  const history = await app.history();
19
22
  for (const event of history)
@@ -40,10 +43,12 @@ export async function runRetainedTerminalUI(app, options = {}) {
40
43
  let eventFailure;
41
44
  const pendingRuns = new Set();
42
45
  const observeRun = (result) => {
43
- const pending = result.then(() => undefined, error => {
46
+ const pending = result
47
+ .then(() => undefined, (error) => {
44
48
  if (!closing && !isCancellation(error))
45
49
  store.appendNotice("error", errorMessage(error));
46
- }).finally(() => {
50
+ })
51
+ .finally(() => {
47
52
  pendingRuns.delete(pending);
48
53
  if (!closing)
49
54
  view.setStatus(app.isRunning ? "Running" : "Ready");
@@ -51,7 +56,7 @@ export async function runRetainedTerminalUI(app, options = {}) {
51
56
  pendingRuns.add(pending);
52
57
  };
53
58
  let resolveExit;
54
- const exited = new Promise((resolve) => resolveExit = resolve);
59
+ const exited = new Promise((resolve) => (resolveExit = resolve));
55
60
  const finish = () => {
56
61
  if (closing)
57
62
  return;
@@ -65,13 +70,11 @@ export async function runRetainedTerminalUI(app, options = {}) {
65
70
  app.cancel("Interrupted by process signal");
66
71
  finish();
67
72
  };
68
- const view = new MaybeCodePrototypeView({
73
+ const view = new MaybeCodeTerminalView({
69
74
  isRunning: () => app.isRunning,
70
75
  permissionMode: () => app.permissionMode,
71
76
  clipboard: options.clipboard ?? createTerminalClipboard({ output: terminal }),
72
- keymap: options.keymap ?? {
73
- ...(process.env.MAY_TUI_LEADER === undefined ? {} : { leader: process.env.MAY_TUI_LEADER }),
74
- },
77
+ keymap: options.keymap ?? omitUndefined({ leader: process.env.MAY_TUI_LEADER }),
75
78
  images,
76
79
  store,
77
80
  workspace: app.workspace,
@@ -106,7 +109,7 @@ export async function runRetainedTerminalUI(app, options = {}) {
106
109
  });
107
110
  process.on("SIGINT", handleProcessSignal);
108
111
  process.on("SIGTERM", handleProcessSignal);
109
- void refreshModelLabel(view, app);
112
+ await refreshModelLabel(view, app);
110
113
  const refreshWorkspace = async () => {
111
114
  if (!app.getWorkspaceGit || closing)
112
115
  return;
@@ -119,17 +122,27 @@ export async function runRetainedTerminalUI(app, options = {}) {
119
122
  };
120
123
  await refreshWorkspace();
121
124
  let refreshingWorkspace = false;
122
- const workspaceTimer = app.getWorkspaceGit ? setInterval(() => {
123
- if (refreshingWorkspace || closing)
124
- return;
125
- refreshingWorkspace = true;
126
- void refreshWorkspace().finally(() => { refreshingWorkspace = false; });
127
- }, 2_000) : undefined;
125
+ let workspaceTask;
126
+ const workspaceTimer = app.getWorkspaceGit
127
+ ? setInterval(() => {
128
+ if (refreshingWorkspace || closing)
129
+ return;
130
+ refreshingWorkspace = true;
131
+ workspaceTask = refreshWorkspace()
132
+ .catch((error) => {
133
+ eventFailure = error;
134
+ finish();
135
+ })
136
+ .finally(() => {
137
+ refreshingWorkspace = false;
138
+ });
139
+ }, 2_000)
140
+ : undefined;
128
141
  runtime = new TuiRuntime({ terminal, renderer, root: view });
129
142
  let eventTask;
130
143
  try {
131
144
  runtime.start();
132
- eventTask = consumeEvents(app, store, view, () => closing, web.events, images, displayedSteering).catch((error) => {
145
+ eventTask = consumeEvents(app, store, view, () => closing, web.events, images, displayedSteering, finish).catch((error) => {
133
146
  eventFailure = error;
134
147
  store.appendNotice("error", `Event stream failed: ${errorMessage(error)}`);
135
148
  finish();
@@ -144,164 +157,218 @@ export async function runRetainedTerminalUI(app, options = {}) {
144
157
  process.removeListener("SIGTERM", handleProcessSignal);
145
158
  runtime.stop();
146
159
  view.dispose();
147
- await web.close();
148
- await Promise.all(pendingRuns);
149
- await eventTask;
160
+ const results = await Promise.allSettled([
161
+ web.close(),
162
+ ...pendingRuns,
163
+ ...(workspaceTask ? [workspaceTask] : []),
164
+ ...(eventTask ? [eventTask] : []),
165
+ ]);
166
+ const failures = results.flatMap((result) => (result.status === "rejected" ? [result.reason] : []));
167
+ if (failures.length) {
168
+ if (eventFailure !== undefined)
169
+ failures.push(eventFailure);
170
+ throw new AggregateError(failures, "MaybeCode terminal failed to close");
171
+ }
150
172
  }
151
173
  if (eventFailure !== undefined)
152
174
  throw eventFailure;
153
175
  }
154
- async function consumeEvents(app, store, view, isClosing, events, images, displayedSteering) {
176
+ async function consumeEvents(app, store, view, isClosing, events, images, displayedSteering, onStopped) {
155
177
  const approvals = new Set();
178
+ const approvalErrors = [];
179
+ const trackApproval = (operation, finish) => {
180
+ const task = operation
181
+ .catch((error) => {
182
+ approvalErrors.push(error);
183
+ onStopped();
184
+ })
185
+ .finally(() => {
186
+ finish?.();
187
+ approvals.delete(task);
188
+ });
189
+ approvals.add(task);
190
+ };
156
191
  const interactions = new Map();
157
192
  const delegationStatus = new Map();
158
193
  let lastGoalDisplay = "";
159
- for await (const event of events) {
160
- if (event.type === "run.event") {
161
- if (event.event.type === "run.started") {
162
- view.setStatus("Running");
163
- const unread = app.listSteeringInputs?.().filter(input => input.status === "delivered" && !displayedSteering.has(input.inputId)) ?? [];
164
- if (unread.length > 0) {
165
- const history = await app.history();
166
- const runId = event.event.runId;
167
- const runIndex = history.findIndex(item => item.type === "run.started" && item.runId === runId);
168
- const submitted = runIndex < 0 ? undefined : history.slice(0, runIndex).reverse().find(item => item.type === "input.submitted");
169
- if (submitted?.type === "input.submitted" && submitted.inputId && unread.some(input => input.inputId === submitted.inputId)) {
170
- store.appendUser(submitted.message.content.filter(part => part.type === "text").map(part => part.text).join(""));
171
- displayedSteering.add(submitted.inputId);
194
+ try {
195
+ for await (const event of events) {
196
+ if (event.type === "run.event") {
197
+ if (event.event.type === "run.started") {
198
+ view.setStatus("Running");
199
+ const unread = app.application.agent
200
+ .listSteeringInputs()
201
+ .filter((input) => input.status === "delivered" && !displayedSteering.has(input.inputId));
202
+ if (unread.length > 0) {
203
+ const history = await app.history();
204
+ const runId = event.event.runId;
205
+ const runIndex = history.findIndex((item) => item.type === "run.started" && item.runId === runId);
206
+ const submitted = runIndex < 0
207
+ ? undefined
208
+ : history
209
+ .slice(0, runIndex)
210
+ .reverse()
211
+ .find((item) => item.type === "input.submitted");
212
+ if (submitted?.type === "input.submitted" &&
213
+ submitted.inputId &&
214
+ unread.some((input) => input.inputId === submitted.inputId)) {
215
+ store.appendUser(submitted.message.content
216
+ .filter((part) => part.type === "text")
217
+ .map((part) => part.text)
218
+ .join(""));
219
+ displayedSteering.add(submitted.inputId);
220
+ }
221
+ }
222
+ }
223
+ if (event.event.type === "input.received") {
224
+ for (const input of app.application.agent.listSteeringInputs()) {
225
+ if (input.status === "delivered" && input.runId === event.event.runId)
226
+ displayedSteering.add(input.inputId);
172
227
  }
173
228
  }
229
+ // 主 Run 让出时子任务还在运行,状态不能显示为 Ready。
230
+ if (event.event.type === "run.completed" ||
231
+ event.event.type === "run.yielded" ||
232
+ event.event.type === "run.cancelled") {
233
+ view.setStatus(app.isRunning ? "Delegating" : "Ready");
234
+ }
235
+ if (event.event.type === "run.failed")
236
+ view.setStatus(`Error: ${event.event.error.message}`);
237
+ }
238
+ if (event.type === "permission-mode.changed") {
239
+ view.setStatus(event.mode === "yolo" ? "YOLO enabled" : "YOLO disabled");
174
240
  }
175
- if (event.event.type === "input.received") {
176
- for (const input of app.listSteeringInputs?.() ?? []) {
177
- if (input.status === "delivered" && input.runId === event.event.runId)
178
- displayedSteering.add(input.inputId);
241
+ if (event.type === "run.event" && event.event.type === "model.completed")
242
+ await images.prepare(event.event.message.content);
243
+ if (event.type === "run.event" && event.event.type === "tool.completed")
244
+ await images.prepare(event.event.content);
245
+ if (event.type === "goal.changed") {
246
+ view.setStatus(`Goal ${event.goal.status}`);
247
+ const display = `${event.goal.id}:${event.goal.status}:${event.goal.progress}`;
248
+ if (display !== lastGoalDisplay) {
249
+ lastGoalDisplay = display;
250
+ store.appendNotice("info", formatGoal(event.goal));
179
251
  }
180
252
  }
181
- // 主 Run 让出时子任务还在运行,状态不能显示为 Ready。
182
- if (event.event.type === "run.completed" || event.event.type === "run.yielded" || event.event.type === "run.cancelled") {
183
- view.setStatus(app.isRunning ? "Delegating" : "Ready");
253
+ if (event.type === "delegation.started" || event.type === "delegation.finished") {
254
+ if (event.type === "delegation.started")
255
+ delegationStatus.clear();
256
+ const label = event.type === "delegation.started" ? "Sub-agent request" : "Sub-agent request finished";
257
+ if (app.delegationNotifications === true)
258
+ store.appendNotice("info", `${label}\n${formatDelegationTree(event.state)}`);
259
+ view.setStatus(event.type === "delegation.started" ? "Delegating" : "Ready");
184
260
  }
185
- if (event.event.type === "run.failed")
186
- view.setStatus(`Error: ${event.event.error.message}`);
187
- }
188
- if (event.type === "permission-mode.changed") {
189
- view.setStatus(event.mode === "yolo" ? "YOLO enabled" : "YOLO disabled");
190
- }
191
- if (event.type === "run.event" && event.event.type === "model.completed")
192
- await images.prepare(event.event.message.content);
193
- if (event.type === "run.event" && event.event.type === "tool.completed")
194
- await images.prepare(event.event.content);
195
- if (event.type === "goal.changed") {
196
- view.setStatus(`Goal ${event.goal.status}`);
197
- const display = `${event.goal.id}:${event.goal.status}:${event.goal.progress}`;
198
- if (display !== lastGoalDisplay) {
199
- lastGoalDisplay = display;
200
- store.appendNotice("info", formatGoal(event.goal));
261
+ else if (event.type === "delegation.updated") {
262
+ // 同一任务的重复状态不显示。
263
+ for (const task of delegationOrder(event.state.tasks)) {
264
+ const previous = delegationStatus.get(task.id);
265
+ delegationStatus.set(task.id, task.status);
266
+ if (app.delegationNotifications !== true || previous === task.status)
267
+ continue;
268
+ store.appendNotice(["failed", "cancelled", "recovery-required"].includes(task.status) ? "warning" : "info", renderDelegationTask(task));
269
+ }
201
270
  }
202
- }
203
- if (event.type === "delegation.started" || event.type === "delegation.finished") {
204
- if (event.type === "delegation.started")
205
- delegationStatus.clear();
206
- const label = event.type === "delegation.started" ? "Sub-agent request" : "Sub-agent request finished";
207
- if (app.delegationNotifications === true)
208
- store.appendNotice("info", `${label}\n${formatDelegationTree(event.state)}`);
209
- view.setStatus(event.type === "delegation.started" ? "Delegating" : "Ready");
210
- }
211
- else if (event.type === "delegation.updated") {
212
- // 同一任务的重复状态不显示。
213
- for (const task of delegationOrder(event.state.tasks)) {
214
- const previous = delegationStatus.get(task.id);
215
- delegationStatus.set(task.id, task.status);
216
- if (app.delegationNotifications !== true || previous === task.status)
217
- continue;
218
- store.appendNotice(["failed", "cancelled", "recovery-required"].includes(task.status) ? "warning" : "info", renderDelegationTask(task));
271
+ applyMaybeCodeEvent(store, event);
272
+ if (event.type === "mcp.interaction.requested") {
273
+ const controller = new AbortController();
274
+ interactions.set(event.request.id, controller);
275
+ const signal = AbortSignal.any([
276
+ controller.signal,
277
+ AbortSignal.timeout(Math.max(0, Math.ceil(event.request.expiresAt - Date.now()))),
278
+ ]);
279
+ trackApproval(presentMcpInteraction(event.request, app, (prompt, signal) => view.requestMcpInput(prompt, signal), signal).catch(() => {
280
+ if (!isClosing() && !signal.aborted) {
281
+ store.appendNotice("warning", "MCP interaction could not be completed");
282
+ app.cancel("User interaction is unavailable");
283
+ }
284
+ }), () => {
285
+ interactions.delete(event.request.id);
286
+ });
219
287
  }
220
- }
221
- applyMaybeCodeEvent(store, event);
222
- if (event.type === "mcp.interaction.requested") {
223
- const controller = new AbortController();
224
- interactions.set(event.request.id, controller);
225
- const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(Math.max(0, Math.ceil(event.request.expiresAt - Date.now())))]);
226
- const task = presentMcpInteraction(event.request, app, (prompt, signal) => view.requestMcpInput(prompt, signal), signal)
227
- .catch(() => {
228
- if (!isClosing() && !signal.aborted) {
229
- store.appendNotice("warning", "MCP interaction could not be completed");
230
- app.cancel("User interaction is unavailable");
288
+ else if (event.type === "mcp.interaction.settled")
289
+ interactions.get(event.requestId)?.abort();
290
+ if (event.type === "model.changed") {
291
+ await refreshModelLabel(view, app);
292
+ }
293
+ if (event.type === "session.changed" && event.resumed) {
294
+ const history = await app.history();
295
+ for (const event of history)
296
+ if (event.type === "assistant.completed" || event.type === "input.submitted")
297
+ await images.prepare(event.message.content);
298
+ for (const event of history)
299
+ if (event.type === "tool.completed" && event.content)
300
+ await images.prepare(event.content);
301
+ store.loadHistory(history);
302
+ displayedSteering.clear();
303
+ for (const event of history) {
304
+ if (event.type === "input.submitted" && event.inputId)
305
+ displayedSteering.add(event.inputId);
306
+ if (event.type === "input.steering.delivered")
307
+ for (const inputId of event.inputIds)
308
+ displayedSteering.add(inputId);
231
309
  }
232
- })
233
- .finally(() => interactions.delete(event.request.id));
234
- approvals.add(task);
235
- void task.finally(() => approvals.delete(task));
236
- }
237
- else if (event.type === "mcp.interaction.settled")
238
- interactions.get(event.requestId)?.abort();
239
- if (event.type === "model.changed") {
240
- void refreshModelLabel(view, app);
241
- }
242
- if (event.type === "session.changed" && event.resumed) {
243
- const history = await app.history();
244
- for (const event of history)
245
- if (event.type === "assistant.completed" || event.type === "input.submitted")
246
- await images.prepare(event.message.content);
247
- for (const event of history)
248
- if (event.type === "tool.completed" && event.content)
249
- await images.prepare(event.content);
250
- store.loadHistory(history);
251
- displayedSteering.clear();
252
- for (const event of history) {
253
- if (event.type === "input.submitted" && event.inputId)
254
- displayedSteering.add(event.inputId);
255
- if (event.type === "input.steering.delivered")
256
- for (const inputId of event.inputIds)
257
- displayedSteering.add(inputId);
310
+ store.appendNotice("info", "Session resumed");
311
+ continue;
258
312
  }
259
- store.appendNotice("info", "Session resumed");
260
- continue;
261
- }
262
- if (event.type === "permission.event" &&
263
- event.event.type === "approval.requested") {
264
- const task = presentApproval(app, view, store, event.event.request, isClosing, approvals);
265
- approvals.add(task);
266
- void task.finally(() => approvals.delete(task));
267
- }
268
- else if (event.type === "permission.event" &&
269
- (event.event.type === "approval.resolved" ||
270
- event.event.type === "approval.cancelled")) {
271
- view.dismissApproval(event.event.requestId);
272
- }
273
- if (event.type === "delegation.event" && event.event.type === "permission.event") {
274
- // 子任务审批:包在外层事件里,同样需要界面与路由。
275
- const inner = event.event.event;
276
- if (inner.type === "approval.requested") {
277
- store.appendNotice("info", `子任务 ${event.taskId} 请求批准 ${inner.request.tool.name}`);
278
- const task = presentApproval(app, view, store, inner.request, isClosing, approvals);
279
- approvals.add(task);
280
- void task.finally(() => approvals.delete(task));
313
+ if (event.type === "permission.event" && event.event.type === "approval.requested") {
314
+ trackApproval(presentApproval(app, view, store, event.event.request, isClosing));
315
+ }
316
+ else if (event.type === "permission.event" &&
317
+ (event.event.type === "approval.resolved" || event.event.type === "approval.cancelled")) {
318
+ view.dismissApproval(event.event.requestId);
281
319
  }
282
- else if (inner.type === "approval.resolved" || inner.type === "approval.cancelled") {
283
- view.dismissApproval(inner.requestId);
320
+ if (event.type === "delegation.event" && event.event.type === "permission.event") {
321
+ // 子任务审批:包在外层事件里,同样需要界面与路由。
322
+ const inner = event.event.event;
323
+ if (inner.type === "approval.requested") {
324
+ store.appendNotice("info", `子任务 ${event.taskId} 请求批准 ${inner.request.tool.name}`);
325
+ trackApproval(presentApproval(app, view, store, inner.request, isClosing));
326
+ }
327
+ else if (inner.type === "approval.resolved" || inner.type === "approval.cancelled") {
328
+ view.dismissApproval(inner.requestId);
329
+ }
284
330
  }
285
331
  }
286
332
  }
287
- for (const controller of interactions.values())
288
- controller.abort();
289
- await Promise.all(approvals);
333
+ finally {
334
+ onStopped();
335
+ for (const controller of interactions.values())
336
+ controller.abort();
337
+ await Promise.all(approvals);
338
+ if (approvalErrors.length)
339
+ throw new AggregateError(approvalErrors, "MaybeCode approval operations failed");
340
+ }
290
341
  }
291
342
  /** 打开审批对话框并把决定交回宿主,由宿主路由到提出请求的 Session。 */
292
- function presentApproval(app, view, store, request, isClosing, approvals) {
293
- return view.requestApproval(request).then(async (decision) => {
343
+ function presentApproval(app, view, store, request, isClosing) {
344
+ return view
345
+ .requestApproval(request)
346
+ .then(async (decision) => {
294
347
  if (decision !== undefined && !isClosing()) {
295
348
  await app.resolveApproval(request.id, decision, decision === "allow-persistent" ? { createdBy: `local:${userInfo().username}` } : undefined);
296
349
  }
297
- }).catch((error) => {
350
+ })
351
+ .catch((error) => {
298
352
  if (!isClosing()) {
299
353
  store.appendNotice("error", `Approval failed: ${errorMessage(error)}`);
300
354
  }
355
+ else if (!isCancellation(error)) {
356
+ throw error;
357
+ }
301
358
  });
302
359
  }
303
360
  function applyMaybeCodeEvent(store, event) {
304
361
  switch (event.type) {
362
+ case "delegation.finished":
363
+ case "delegation.started":
364
+ case "delegation.updated":
365
+ case "goal.changed":
366
+ case "mcp.interaction.requested":
367
+ case "mcp.interaction.settled":
368
+ case "permission-mode.changed":
369
+ case "session.forked":
370
+ case "workspace.git.changed":
371
+ break;
305
372
  case "run.event":
306
373
  store.applyMayEvent(event.event);
307
374
  break;
@@ -323,9 +390,7 @@ function applyMaybeCodeEvent(store, event) {
323
390
  store.appendNotice("info", `Session ${event.resumed ? "resumed" : "started"}: ${event.sessionId}`);
324
391
  break;
325
392
  case "model.changed": {
326
- const profile = event.model.profile === undefined
327
- ? ""
328
- : ` profile ${event.model.profile}`;
393
+ const profile = event.model.profile === undefined ? "" : ` profile ${event.model.profile}`;
329
394
  store.appendNotice("info", `Model switched to${profile}: ${event.model.provider}/${event.model.model}`);
330
395
  break;
331
396
  }
@@ -339,8 +404,7 @@ function applyMaybeCodeEvent(store, event) {
339
404
  store.appendNotice("info", `MCP resource watch closed: ${event.serverId} ${event.uri} (${event.reason})`);
340
405
  break;
341
406
  case "mcp.server.connected":
342
- store.appendNotice("info", `MCP server connected: ${event.serverId} ` +
343
- `(${event.toolNames.length} tools)`);
407
+ store.appendNotice("info", `MCP server connected: ${event.serverId} ` + `(${event.toolNames.length} tools)`);
344
408
  break;
345
409
  case "mcp.server.catalog-updated":
346
410
  store.appendNotice("info", `MCP catalog updated: ${event.serverId} (revision ${event.revision})`);
@@ -362,9 +426,7 @@ function applyMaybeCodeEvent(store, event) {
362
426
  }
363
427
  async function handleCommand(input, app, store, view, finish, web, observeRun) {
364
428
  const parsed = parseMaybeCodeSlashCommand(input);
365
- if (parsed.type === "command" &&
366
- parsed.definition.name === "/compact" &&
367
- parsed.arguments.length === 0) {
429
+ if (parsed.type === "command" && parsed.definition.name === "/compact" && parsed.arguments.length === 0) {
368
430
  store.appendNotice("info", "Pruning old tool results and summarizing context");
369
431
  }
370
432
  const result = await executeMaybeCodeSlashCommand(input, app);
@@ -435,12 +497,18 @@ async function presentCommandResult(result, app, store, view, finish, observeRun
435
497
  }
436
498
  case "changes.selection.requested":
437
499
  await view.requestChanges(result.diff, {
438
- ...(result.diff.runId && app.previewRestore ? { onRestore: async (path) => (await app.previewRestore(result.diff.runId, [path])).diff } : {}),
500
+ ...(result.diff.runId && app.previewRestore
501
+ ? { onRestore: async (path) => (await app.previewRestore(result.diff.runId, [path])).diff }
502
+ : {}),
439
503
  ...(app.restoreFiles ? { onApply: (previewId) => app.restoreFiles(previewId) } : {}),
440
504
  });
441
505
  break;
442
506
  case "worktrees.display":
443
- store.appendNotice("info", 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。");
507
+ store.appendNotice("info", result.worktrees.length
508
+ ? result.worktrees
509
+ .map((item) => `${item.id}\n${item.path}\n${item.branch} · ${item.status} · ${item.commit.slice(0, 12)}`)
510
+ .join("\n\n")
511
+ : "没有已登记的 worktree。");
444
512
  break;
445
513
  case "session.resumed":
446
514
  store.appendNotice("info", `Resumed session ${result.sessionId}`);
@@ -493,7 +561,7 @@ async function runEffortDialog(view, app, store, state) {
493
561
  async function runModelDialog(view, app, store, initialModels) {
494
562
  let models = initialModels;
495
563
  while (true) {
496
- const action = await view.requestModelAction(models, app.modelInfo?.profile);
564
+ const action = await view.requestModelAction(models, app.application.modelInfo?.profile);
497
565
  if (action === undefined)
498
566
  return;
499
567
  try {
@@ -529,9 +597,7 @@ async function runSessionDialog(view, app, store, initialSessions) {
529
597
  }
530
598
  else {
531
599
  const deleted = await app.deleteSession(action.sessionId);
532
- store.appendNotice(deleted ? "info" : "warning", deleted
533
- ? `Deleted session ${action.sessionId}`
534
- : `Session ${action.sessionId} was not found`);
600
+ store.appendNotice(deleted ? "info" : "warning", deleted ? `Deleted session ${action.sessionId}` : `Session ${action.sessionId} was not found`);
535
601
  }
536
602
  sessions = await app.listSessions();
537
603
  }
@@ -544,19 +610,15 @@ async function runSessionDialog(view, app, store, initialSessions) {
544
610
  function inspectionText(inspection) {
545
611
  if (inspection === undefined)
546
612
  return "\nContext: unavailable";
547
- const window = inspection.contextWindowTokens === undefined
548
- ? ""
549
- : ` / ${inspection.contextWindowTokens}`;
550
- return `\nContext: ~${inspection.effectiveTokens}${window} tokens, ` +
551
- `${inspection.messageCount} messages`;
613
+ const window = inspection.contextWindowTokens === undefined ? "" : ` / ${inspection.contextWindowTokens}`;
614
+ return `\nContext: ~${inspection.effectiveTokens}${window} tokens, ` + `${inspection.messageCount} messages`;
552
615
  }
553
616
  function modelLabel(app, effort = "loading") {
554
- if (app.modelInfo === undefined)
617
+ const info = app.application.modelInfo;
618
+ if (info === undefined)
555
619
  return "unknown · effort: unknown";
556
- const endpoint = `${app.modelInfo.provider}/${app.modelInfo.model}`;
557
- const model = app.modelInfo.profile === undefined
558
- ? endpoint
559
- : `${app.modelInfo.profile} (${endpoint})`;
620
+ const endpoint = `${info.provider}/${info.model}`;
621
+ const model = info.profile === undefined ? endpoint : `${info.profile} (${endpoint})`;
560
622
  return `${model} · effort: ${reasoningEffortLabel(effort)}`;
561
623
  }
562
624
  function reasoningEffortLabel(state) {
@@ -583,10 +645,8 @@ async function refreshModelLabel(view, app) {
583
645
  view.setModel(label);
584
646
  }
585
647
  function modelIdentity(app) {
586
- const model = app.modelInfo;
587
- return model === undefined
588
- ? ""
589
- : `${model.profile ?? ""}\u0000${model.provider}\u0000${model.model}`;
648
+ const model = app.application.modelInfo;
649
+ return model === undefined ? "" : `${model.profile ?? ""}\u0000${model.provider}\u0000${model.model}`;
590
650
  }
591
651
  function unknownReasoningEffort() {
592
652
  return {
@@ -606,9 +666,7 @@ function reasoningEffortChanged(state) {
606
666
  return `Reasoning effort: ${effort}${state.overridden ? " (runtime override)" : ""}`;
607
667
  }
608
668
  function isCancellation(error) {
609
- return error instanceof Error &&
610
- (error.name === "AbortError" ||
611
- ("code" in error && error.code === "RUN_CANCELLED"));
669
+ return error instanceof Error && (error.name === "AbortError" || ("code" in error && error.code === "RUN_CANCELLED"));
612
670
  }
613
671
  function errorMessage(error) {
614
672
  return error instanceof Error ? error.message : String(error);