u-foo 2.3.32 → 2.4.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 (235) hide show
  1. package/README.md +157 -213
  2. package/README.zh-CN.md +151 -197
  3. package/SKILLS/ufoo/SKILL.md +8 -8
  4. package/bin/uagy.js +69 -0
  5. package/bin/uclaude.js +2 -2
  6. package/bin/ucode.js +4 -4
  7. package/bin/ucodex.js +2 -2
  8. package/bin/ufoo.js +5 -23
  9. package/modules/AGENTS.template.md +1 -1
  10. package/modules/bus/SKILLS/ubus/SKILL.md +35 -10
  11. package/package.json +5 -5
  12. package/scripts/chat-app-smoke.js +1 -1
  13. package/scripts/global-chat-switch-benchmark.js +5 -5
  14. package/scripts/ink-demo.js +1 -1
  15. package/scripts/ink-smoke.js +1 -1
  16. package/scripts/ucode-app-smoke.js +1 -1
  17. package/src/{agent → agents/activity}/activityDetector.js +39 -2
  18. package/src/{agent → agents/activity}/activityStatePublisher.js +1 -1
  19. package/src/{agent → agents/activity}/activityStateWriter.js +2 -2
  20. package/src/{agent → agents/activity}/activityTracker.js +1 -1
  21. package/src/agents/activity/index.js +8 -0
  22. package/src/{agent → agents/controller}/controllerToolExecutor.js +4 -4
  23. package/src/agents/controller/index.js +8 -0
  24. package/src/{agent → agents/controller}/loopObservability.js +2 -2
  25. package/src/{agent → agents/controller}/loopRuntime.js +1 -1
  26. package/src/{agent → agents/controller}/ufooAgent.js +9 -9
  27. package/src/agents/index.js +10 -0
  28. package/src/agents/internal/index.js +3 -0
  29. package/src/{agent → agents/internal}/internalRunner.js +45 -22
  30. package/src/agents/launch/agyConversation.js +159 -0
  31. package/src/agents/launch/index.js +12 -0
  32. package/src/{agent → agents/launch}/launchEnvironment.js +2 -3
  33. package/src/{agent → agents/launch}/launcher.js +64 -21
  34. package/src/{agent → agents/launch}/notifier.js +23 -12
  35. package/src/{agent → agents/launch}/ptyRunner.js +44 -12
  36. package/src/{agent → agents/launch}/ptyWrapper.js +2 -2
  37. package/src/{agent → agents/launch}/publisherRouting.js +1 -1
  38. package/src/{agent → agents/launch}/readyDetector.js +23 -0
  39. package/src/{agent → agents/prompts}/defaultBootstrap.js +63 -4
  40. package/src/{group/bootstrap.js → agents/prompts/groupBootstrap.js} +41 -6
  41. package/src/agents/prompts/index.js +8 -0
  42. package/src/{code/prompts → agents/prompts/native}/index.js +1 -1
  43. package/src/{agent → agents/providers}/claudeThreadProvider.js +1 -1
  44. package/src/{agent → agents/providers}/codexThreadProvider.js +1 -1
  45. package/src/{agent → agents/providers}/directAuthStatus.js +184 -1
  46. package/src/agents/providers/index.js +13 -0
  47. package/src/{agent → agents/providers}/upstreamTransport.js +2 -2
  48. package/src/{chat → app/chat}/agentSockets.js +1 -1
  49. package/src/{chat → app/chat}/commandExecutor.js +50 -26
  50. package/src/{chat → app/chat}/commands.js +119 -5
  51. package/src/{chat → app/chat}/daemonConnection.js +1 -1
  52. package/src/{chat → app/chat}/daemonMessageRouter.js +45 -3
  53. package/src/{chat → app/chat}/dashboardView.js +2 -1
  54. package/src/app/chat/index.js +6 -0
  55. package/src/{chat → app/chat}/inputSubmitHandler.js +4 -13
  56. package/src/{chat → app/chat}/internalAgentLogHistory.js +1 -1
  57. package/src/app/chat/multiWindow/index.js +268 -0
  58. package/src/app/chat/multiWindow/paneLayout.js +84 -0
  59. package/src/app/chat/multiWindow/paneManager.js +299 -0
  60. package/src/app/chat/multiWindow/renderer.js +384 -0
  61. package/src/app/chat/multiWindow/virtualTerminal.js +327 -0
  62. package/src/{chat → app/chat}/transport.js +1 -1
  63. package/src/{cli → app/cli}/ctxCoreCommands.js +3 -3
  64. package/src/{doctor/index.js → app/cli/features/doctor.js} +1 -1
  65. package/src/{init/index.js → app/cli/features/init.js} +14 -32
  66. package/src/{cli → app/cli}/groupCoreCommands.js +2 -2
  67. package/src/app/cli/index.js +9 -0
  68. package/src/{cli → app/cli}/onlineCoreCommands.js +5 -5
  69. package/src/{cli.js → app/cli/run.js} +59 -57
  70. package/src/app/index.js +6 -0
  71. package/src/code/agent.js +10 -9
  72. package/src/code/index.js +2 -0
  73. package/src/code/launcher/index.js +9 -0
  74. package/src/{agent → code/launcher}/ucode.js +7 -8
  75. package/src/{agent → code/launcher}/ucodeBootstrap.js +3 -3
  76. package/src/{agent → code/launcher}/ucodeBuild.js +2 -2
  77. package/src/{agent → code/launcher}/ucodeDoctor.js +2 -2
  78. package/src/{agent → code/launcher}/ucodeRuntimeConfig.js +1 -2
  79. package/src/code/nativeRunner.js +4 -4
  80. package/src/code/tui.js +3 -1454
  81. package/src/config.js +15 -2
  82. package/src/{bus → coordination/bus}/activate.js +2 -2
  83. package/src/{bus → coordination/bus}/daemon.js +15 -5
  84. package/src/coordination/bus/envelope.js +173 -0
  85. package/src/{bus → coordination/bus}/index.js +7 -3
  86. package/src/{bus → coordination/bus}/inject.js +11 -3
  87. package/src/{bus → coordination/bus}/message.js +1 -1
  88. package/src/coordination/bus/messageMeta.js +130 -0
  89. package/src/coordination/bus/promptEnvelope.js +65 -0
  90. package/src/{bus → coordination/bus}/shake.js +1 -1
  91. package/src/{bus → coordination/bus}/store.js +3 -3
  92. package/src/{bus → coordination/bus}/subscriber.js +2 -2
  93. package/src/{bus → coordination/bus}/utils.js +2 -2
  94. package/src/{history → coordination/history}/inputTimeline.js +5 -5
  95. package/src/coordination/index.js +10 -0
  96. package/src/{memory → coordination/memory}/historySearch.js +1 -1
  97. package/src/{memory → coordination/memory}/index.js +3 -3
  98. package/src/{report → coordination/report}/store.js +2 -2
  99. package/src/{status → coordination/status}/index.js +3 -3
  100. package/src/online/bridge.js +2 -2
  101. package/src/{controller → orchestration/controller}/flags.js +1 -1
  102. package/src/{controller → orchestration/controller}/gateRouter.js +1 -1
  103. package/src/orchestration/controller/index.js +10 -0
  104. package/src/{controller → orchestration/controller}/shadowGuard.js +1 -1
  105. package/src/orchestration/groups/bootstrap.js +3 -0
  106. package/src/orchestration/groups/index.js +10 -0
  107. package/src/orchestration/groups/promptProfiles.js +3 -0
  108. package/src/{group → orchestration/groups}/templates.js +1 -1
  109. package/src/{group → orchestration/groups}/validateTemplate.js +1 -1
  110. package/src/orchestration/index.js +7 -0
  111. package/src/orchestration/solo/index.js +3 -0
  112. package/src/{daemon → runtime/daemon}/agentProcessManager.js +1 -1
  113. package/src/{daemon → runtime/daemon}/cronOps.js +3 -2
  114. package/src/{daemon → runtime/daemon}/groupOrchestrator.js +26 -9
  115. package/src/{daemon → runtime/daemon}/index.js +105 -53
  116. package/src/{daemon → runtime/daemon}/ipcServer.js +1 -1
  117. package/src/{daemon → runtime/daemon}/nicknameScope.js +6 -3
  118. package/src/{daemon → runtime/daemon}/ops.js +48 -61
  119. package/src/{daemon → runtime/daemon}/promptLoop.js +1 -1
  120. package/src/{daemon → runtime/daemon}/promptRequest.js +7 -7
  121. package/src/runtime/daemon/providerSessions.js +230 -0
  122. package/src/{daemon → runtime/daemon}/reporting.js +4 -4
  123. package/src/{daemon → runtime/daemon}/run.js +4 -4
  124. package/src/{daemon → runtime/daemon}/soloBootstrap.js +7 -7
  125. package/src/{daemon → runtime/daemon}/status.js +5 -5
  126. package/src/runtime/index.js +10 -0
  127. package/src/{projects → runtime/projects}/registry.js +1 -1
  128. package/src/{terminal → runtime/terminal}/adapterRouter.js +0 -10
  129. package/src/{terminal → runtime/terminal}/adapters/internalAdapter.js +0 -4
  130. package/src/tools/handlers/common.js +1 -1
  131. package/src/tools/handlers/listAgents.js +1 -1
  132. package/src/tools/handlers/memory.js +3 -3
  133. package/src/tools/handlers/readBusSummary.js +1 -1
  134. package/src/tools/handlers/readOpenDecisions.js +1 -1
  135. package/src/tools/handlers/readProjectRegistry.js +1 -1
  136. package/src/tools/handlers/readPromptHistory.js +2 -2
  137. package/src/tools/schemaFixtures.js +1 -1
  138. package/src/ui/MIGRATION.md +42 -88
  139. package/src/ui/format/index.js +5 -28
  140. package/src/ui/index.js +1 -1
  141. package/src/ui/{components → ink}/ChatApp.js +812 -88
  142. package/src/ui/ink/DashboardBar.js +685 -0
  143. package/src/ui/{components → ink}/MultilineInput.js +230 -5
  144. package/src/ui/{components → ink}/UcodeApp.js +16 -7
  145. package/src/ui/{components → ink}/agentMirror.js +24 -19
  146. package/src/ui/{components → ink}/chatReducer.js +29 -7
  147. package/src/bus/messageMeta.js +0 -52
  148. package/src/chat/agentViewController.js +0 -1072
  149. package/src/chat/chatLogController.js +0 -138
  150. package/src/chat/completionController.js +0 -533
  151. package/src/chat/dashboardKeyController.js +0 -533
  152. package/src/chat/index.js +0 -2222
  153. package/src/chat/inputHistoryController.js +0 -135
  154. package/src/chat/inputListenerController.js +0 -470
  155. package/src/chat/layout.js +0 -186
  156. package/src/chat/pasteController.js +0 -81
  157. package/src/chat/statusLineController.js +0 -223
  158. package/src/chat/streamTracker.js +0 -156
  159. package/src/code/config +0 -0
  160. package/src/daemon/providerSessions.js +0 -488
  161. package/src/terminal/adapters/internalPtyAdapter.js +0 -42
  162. package/src/ui/components/DashboardBar.js +0 -417
  163. /package/src/{code/prompts → agents/prompts/native}/actions.js +0 -0
  164. /package/src/{code/prompts → agents/prompts/native}/efficiency.js +0 -0
  165. /package/src/{code/prompts → agents/prompts/native}/environment.js +0 -0
  166. /package/src/{code/prompts → agents/prompts/native}/identity.js +0 -0
  167. /package/src/{code/prompts → agents/prompts/native}/safety.js +0 -0
  168. /package/src/{code/prompts → agents/prompts/native}/sections.js +0 -0
  169. /package/src/{code/prompts → agents/prompts/native}/system.js +0 -0
  170. /package/src/{code/prompts → agents/prompts/native}/tasks.js +0 -0
  171. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/bash.js +0 -0
  172. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/edit.js +0 -0
  173. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/read.js +0 -0
  174. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/write.js +0 -0
  175. /package/src/{code/prompts → agents/prompts/native}/ufoo.js +0 -0
  176. /package/src/{group → agents/prompts}/promptProfiles.js +0 -0
  177. /package/src/{agent → agents/providers}/claudeEventTranslator.js +0 -0
  178. /package/src/{agent → agents/providers}/claudeOauthTokenReader.js +0 -0
  179. /package/src/{agent → agents/providers}/claudeSessionFiles.js +0 -0
  180. /package/src/{agent → agents/providers}/codexEventTranslator.js +0 -0
  181. /package/src/{agent → agents/providers}/credentials/claude.js +0 -0
  182. /package/src/{agent → agents/providers}/credentials/codex.js +0 -0
  183. /package/src/{agent → agents/providers}/credentials/index.js +0 -0
  184. /package/src/{chat → app/chat}/agentBar.js +0 -0
  185. /package/src/{chat → app/chat}/agentDirectory.js +0 -0
  186. /package/src/{chat → app/chat}/cronScheduler.js +0 -0
  187. /package/src/{chat → app/chat}/daemonCoordinator.js +0 -0
  188. /package/src/{chat → app/chat}/daemonReconnect.js +0 -0
  189. /package/src/{chat → app/chat}/daemonTransport.js +0 -0
  190. /package/src/{chat → app/chat}/daemonTransportDefaults.js +0 -0
  191. /package/src/{chat → app/chat}/inputMath.js +0 -0
  192. /package/src/{chat → app/chat}/projectCloseController.js +0 -0
  193. /package/src/{chat → app/chat}/rawKeyMap.js +0 -0
  194. /package/src/{chat → app/chat}/settingsController.js +0 -0
  195. /package/src/{chat → app/chat}/shellCommand.js +0 -0
  196. /package/src/{chat → app/chat}/text.js +0 -0
  197. /package/src/{chat → app/chat}/transientAgentState.js +0 -0
  198. /package/src/{cli → app/cli}/busCoreCommands.js +0 -0
  199. /package/src/{skills/index.js → app/cli/features/skills.js} +0 -0
  200. /package/src/{bus → coordination/bus}/nickname.js +0 -0
  201. /package/src/{bus → coordination/bus}/queue.js +0 -0
  202. /package/src/{context → coordination/context}/decisions.js +0 -0
  203. /package/src/{context → coordination/context}/doctor.js +0 -0
  204. /package/src/{context → coordination/context}/index.js +0 -0
  205. /package/src/{context → coordination/context}/sync.js +0 -0
  206. /package/src/{ufoo → coordination/state}/agentRegistryDiagnostics.js +0 -0
  207. /package/src/{ufoo → coordination/state}/agentsStore.js +0 -0
  208. /package/src/{ufoo → coordination/state}/paths.js +0 -0
  209. /package/src/{controller → orchestration/controller}/launchRouting.js +0 -0
  210. /package/src/{controller → orchestration/controller}/routerFastPath.js +0 -0
  211. /package/src/{controller → orchestration/controller}/routerFinalize.js +0 -0
  212. /package/src/{group → orchestration/groups}/diagram.js +0 -0
  213. /package/src/{group → orchestration/groups}/templateValidation.js +0 -0
  214. /package/src/{solo → orchestration/solo}/commands.js +0 -0
  215. /package/src/{shared → runtime/contracts}/eventContract.js +0 -0
  216. /package/src/{shared → runtime/contracts}/ptySocketContract.js +0 -0
  217. /package/src/{providerapi → runtime/privacy}/redactor.js +0 -0
  218. /package/src/{providerapi → runtime/privacy}/shadowDiff.js +0 -0
  219. /package/src/{utils → runtime/process}/nodeExecutable.js +0 -0
  220. /package/src/{projects → runtime/projects}/identity.js +0 -0
  221. /package/src/{projects → runtime/projects}/index.js +0 -0
  222. /package/src/{projects → runtime/projects}/projectId.js +0 -0
  223. /package/src/{projects → runtime/projects}/runtimes.js +0 -0
  224. /package/src/{terminal → runtime/terminal}/adapterContract.js +0 -0
  225. /package/src/{terminal → runtime/terminal}/adapters/externalAdapter.js +0 -0
  226. /package/src/{terminal → runtime/terminal}/adapters/hostAdapter.js +0 -0
  227. /package/src/{terminal → runtime/terminal}/adapters/internalQueueAdapter.js +0 -0
  228. /package/src/{terminal → runtime/terminal}/adapters/terminalAdapter.js +0 -0
  229. /package/src/{terminal → runtime/terminal}/adapters/tmuxAdapter.js +0 -0
  230. /package/src/{terminal → runtime/terminal}/detect.js +0 -0
  231. /package/src/{terminal → runtime/terminal}/index.js +0 -0
  232. /package/src/{terminal → runtime/terminal}/iterm2.js +0 -0
  233. /package/src/{utils → ui/format}/banner.js +0 -0
  234. /package/src/{shared → ui/format}/markdownRenderer.js +0 -0
  235. /package/src/ui/{components → ink}/InkDemo.js +0 -0
@@ -35,6 +35,7 @@ const COMMAND_TREE = {
35
35
  stop: { desc: "Stop cron task by id or all" },
36
36
  },
37
37
  },
38
+ "/clear": { desc: "Clear chat log on screen" },
38
39
  "/group": {
39
40
  desc: "Agent group orchestration",
40
41
  children: {
@@ -47,13 +48,15 @@ const COMMAND_TREE = {
47
48
  },
48
49
  },
49
50
  "/init": { desc: "Initialize modules" },
51
+ "/multi": { desc: "Toggle multi-window agent view" },
50
52
  "/open": { desc: "Open project path in global mode" },
51
53
  "/launch": {
52
54
  desc: "Launch new agent",
53
55
  children: {
54
- claude: { desc: "Launch Claude agent" },
55
- codex: { desc: "Launch Codex agent" },
56
- ucode: { desc: "Launch ucode core agent" },
56
+ claude: { desc: "Launch Claude agent", order: 1 },
57
+ codex: { desc: "Launch Codex agent", order: 2 },
58
+ agy: { desc: "Launch Antigravity (agy) agent", order: 3 },
59
+ ucode: { desc: "Launch ucode core agent", order: 4 },
57
60
  },
58
61
  },
59
62
  "/project": {
@@ -136,7 +139,7 @@ const COMMAND_TREE = {
136
139
  },
137
140
  },
138
141
  "/status": { desc: "Status display" },
139
- "/ufoo": { desc: "ufoo protocol (session marker)" },
142
+ "/ufoo": { desc: "ufoo protocol" },
140
143
  };
141
144
 
142
145
  const COMMAND_ORDER = ["/launch", "/group", "/bus", "/ctx"];
@@ -196,10 +199,120 @@ function parseCommand(text) {
196
199
  return { command, args };
197
200
  }
198
201
 
202
+ function parseCommandOptions(args = []) {
203
+ const options = {};
204
+ const positional = [];
205
+ for (const arg of args) {
206
+ const raw = String(arg || "").trim();
207
+ if (!raw) continue;
208
+ if (raw.includes("=")) {
209
+ const [key, value] = raw.split("=", 2);
210
+ options[String(key || "").trim().toLowerCase()] = String(value || "").trim();
211
+ } else {
212
+ positional.push(raw);
213
+ }
214
+ }
215
+ return { options, positional };
216
+ }
217
+
218
+ function normalizeAgentLabel(value = "") {
219
+ const raw = String(value || "").trim().toLowerCase();
220
+ if (raw === "claude" || raw === "uclaude") return "claude";
221
+ if (raw === "codex" || raw === "ucodex") return "codex";
222
+ if (raw === "agy" || raw === "antigravity" || raw === "uagy") return "agy";
223
+ if (raw === "ucode" || raw === "ufoo") return "ufoo";
224
+ return raw || "agent";
225
+ }
226
+
227
+ function describeCommandForChat(text) {
228
+ const parsed = parseCommand(String(text || "").trim());
229
+ if (!parsed) return "";
230
+ const command = String(parsed.command || "").trim().toLowerCase();
231
+ const args = parsed.args || [];
232
+ const sub = String(args[0] || "").trim().toLowerCase();
233
+ const { options, positional } = parseCommandOptions(args);
234
+
235
+ if (command === "launch") {
236
+ const agent = normalizeAgentLabel(args[0]);
237
+ const nickname = options.nickname || options.nick || options.name || "";
238
+ const profile = options.profile || options.prompt_profile || "";
239
+ const count = parseInt(options.count || "1", 10);
240
+ const scope = options.scope || options.launch_scope || positional.slice(1).find((item) =>
241
+ /^(window|new-window|separate|inplace|same|current|tab|pane)$/i.test(item)
242
+ ) || "";
243
+ const base = nickname
244
+ ? `Launching a ${agent} named ${nickname}`
245
+ : (Number.isFinite(count) && count > 1 ? `Launching ${count} ${agent} agents` : `Launching a ${agent} agent`);
246
+ return `${base}${profile ? ` with profile ${profile}` : ""}${/^(window|new-window|separate)$/i.test(scope) ? " in a new window" : ""}`;
247
+ }
248
+
249
+ if (command === "group") {
250
+ if (sub === "run") return `Launching group ${args[1] || "template"}`;
251
+ if (sub === "status") return `Checking group ${args[1] || "status"}`;
252
+ if (sub === "stop") return `Stopping group ${args[1] || "run"}`;
253
+ if (sub === "diagram") return `Showing group diagram${args[1] ? ` for ${args[1]}` : ""}`;
254
+ if (sub === "template" || sub === "templates") return "Browsing group templates";
255
+ return "Managing agent groups";
256
+ }
257
+
258
+ if (command === "bus") {
259
+ if (sub === "send") return `Sending a message to ${args[1] || "an agent"}`;
260
+ if (sub === "rename") return `Renaming ${args[1] || "an agent"}${args[2] ? ` to ${args[2]}` : ""}`;
261
+ if (sub === "activate") return `Activating ${args[1] || "an agent"}`;
262
+ if (sub === "list") return "Listing active agents";
263
+ if (sub === "status") return "Checking bus status";
264
+ return "Using the event bus";
265
+ }
266
+
267
+ if (command === "daemon") {
268
+ if (sub === "start") return "Starting the ufoo daemon";
269
+ if (sub === "stop") return "Stopping the ufoo daemon";
270
+ if (sub === "restart") return "Restarting the ufoo daemon";
271
+ if (sub === "status") return "Checking daemon status";
272
+ return "Managing the ufoo daemon";
273
+ }
274
+
275
+ if (command === "project") {
276
+ if (sub === "switch") return `Switching to project ${args[1] || ""}`.trim();
277
+ if (sub === "current") return "Showing current project";
278
+ if (sub === "list") return "Listing running projects";
279
+ return "Managing projects";
280
+ }
281
+
282
+ if (command === "solo") {
283
+ if (sub === "run") return `Launching solo role ${args[1] || "agent"}`;
284
+ if (sub === "list") return "Listing solo roles";
285
+ return "Managing solo agents";
286
+ }
287
+
288
+ if (command === "role") {
289
+ if (sub === "assign") return `Assigning role ${args[2] || ""}${args[1] ? ` to ${args[1]}` : ""}`.trim();
290
+ if (sub === "list") return "Listing roles";
291
+ return "Managing roles";
292
+ }
293
+
294
+ if (command === "cron") {
295
+ if (sub === "start") return "Creating a cron task";
296
+ if (sub === "stop") return `Stopping cron task ${args[1] || ""}`.trim();
297
+ if (sub === "list") return "Listing cron tasks";
298
+ return "Managing cron tasks";
299
+ }
300
+
301
+ if (command === "settings") return !sub || sub === "show" ? "Showing settings" : "Updating settings";
302
+ if (command === "ctx") return `Checking context${sub ? ` ${sub}` : ""}`;
303
+ if (command === "doctor") return "Running ufoo diagnostics";
304
+ if (command === "clear") return "Clearing the chat log";
305
+ if (command === "multi") return "Toggling multi-pane view";
306
+ if (command === "open") return `Opening project ${args[0] || ""}`.trim();
307
+ if (command === "resume") return args[0] === "list" ? "Listing recoverable agents" : `Resuming ${args[0] || "agents"}`;
308
+ if (command === "init") return "Initializing ufoo modules";
309
+
310
+ return `Running /${command}`;
311
+ }
312
+
199
313
  function shouldEchoCommandInChat(text) {
200
314
  const parsed = parseCommand(String(text || "").trim());
201
315
  if (!parsed) return true;
202
- if (parsed.command === "group" && parsed.args[0] === "run") return false;
203
316
  return true;
204
317
  }
205
318
 
@@ -222,6 +335,7 @@ module.exports = {
222
335
  sortCommands,
223
336
  buildCommandRegistry,
224
337
  parseCommand,
338
+ describeCommandForChat,
225
339
  shouldEchoCommandInChat,
226
340
  parseAtTarget,
227
341
  };
@@ -1,4 +1,4 @@
1
- const { IPC_REQUEST_TYPES } = require("../shared/eventContract");
1
+ const { IPC_REQUEST_TYPES } = require("../../runtime/contracts/eventContract");
2
2
 
3
3
  function createDaemonConnection(options = {}) {
4
4
  const {
@@ -1,5 +1,5 @@
1
- const { IPC_RESPONSE_TYPES, BUS_STATUS_PHASES } = require("../shared/eventContract");
2
- const { renderMarkdownLines } = require("../shared/markdownRenderer");
1
+ const { IPC_RESPONSE_TYPES, BUS_STATUS_PHASES } = require("../../runtime/contracts/eventContract");
2
+ const { renderMarkdownLines } = require("../../ui/format/markdownRenderer");
3
3
  const { decodeEscapedNewlines } = require("./text");
4
4
 
5
5
  function createDaemonMessageRouter(options = {}) {
@@ -47,6 +47,45 @@ function createDaemonMessageRouter(options = {}) {
47
47
  return `{cyan-fg}${escapedLabel}{/cyan-fg} {gray-fg}·{/gray-fg} `;
48
48
  }
49
49
 
50
+ function launchTargetLabel(result = {}, renameResult = null) {
51
+ const nickname = (renameResult && renameResult.ok && renameResult.nickname)
52
+ || result.nickname
53
+ || "";
54
+ if (nickname) return nickname;
55
+ const ids = Array.isArray(result.subscriber_ids) ? result.subscriber_ids : [];
56
+ return ids[0] || result.agent_id || "";
57
+ }
58
+
59
+ function buildLifecycleSummary(payload = {}, msg = {}) {
60
+ const ops = Array.isArray(payload.ops) ? payload.ops : [];
61
+ const results = Array.isArray(msg.opsResults) ? msg.opsResults : [];
62
+ if (ops.length === 0) return "";
63
+ const launchOp = ops.find((op) => op && op.action === "launch");
64
+ if (launchOp) {
65
+ const launchResult = results.find((item) => item && item.action === "launch") || {};
66
+ const renameResult = results.find((item) => item && item.action === "rename" && item.ok);
67
+ if (launchResult.ok === false) {
68
+ return `Launch failed: ${launchResult.error || "unknown error"}`;
69
+ }
70
+ const agent = String(launchResult.agent || launchOp.agent || "agent").trim();
71
+ const count = Number(launchResult.count || launchOp.count || 1);
72
+ const ids = Array.isArray(launchResult.subscriber_ids) ? launchResult.subscriber_ids.filter(Boolean) : [];
73
+ const target = launchTargetLabel(launchResult, renameResult);
74
+ if (launchResult.skipped && target) return `Reused ${agent}:${target}`;
75
+ if (ids.length > 1) return `Launched ${ids.length} ${agent} agents: ${ids.join(", ")}`;
76
+ if (target) return `Launched ${agent}:${target}`;
77
+ if (count > 1) return `Launched ${count} ${agent} agents`;
78
+ return `Launched a ${agent} agent`;
79
+ }
80
+ const closeOp = ops.find((op) => op && op.action === "close");
81
+ if (closeOp) {
82
+ const closeResult = results.find((item) => item && item.action === "close") || {};
83
+ if (closeResult.ok === false) return `Close failed: ${closeResult.error || "unknown error"}`;
84
+ return `Closed ${closeResult.agent_id || closeOp.agent_id || closeOp.target || "agent"}`;
85
+ }
86
+ return "";
87
+ }
88
+
50
89
  function normalizeDisplayMessage(raw) {
51
90
  let displayMessage = raw || "";
52
91
  let streamPayload = null;
@@ -223,7 +262,10 @@ function createDaemonMessageRouter(options = {}) {
223
262
  /^Group started\b/i.test(replyText)
224
263
  );
225
264
  // Suppress lifecycle confirmations from chat history — status line plus structured payload is enough.
226
- if (!isLifecycleStatusOnly && !isGroupStartedConfirmation) {
265
+ if (isLifecycleStatusOnly) {
266
+ const summary = buildLifecycleSummary(payload, msg);
267
+ if (summary) logMessage("reply", `${speakerPrefix("ufoo", "white")}${escapeBlessed(summary)}`);
268
+ } else if (!isGroupStartedConfirmation) {
227
269
  logMessage("reply", `${speakerPrefix("ufoo", "white")}${escapeBlessed(replyText)}`);
228
270
  }
229
271
  }
@@ -1,9 +1,10 @@
1
1
  const { clampAgentWindowWithSelection } = require("./agentDirectory");
2
2
 
3
- const DEFAULT_MODE_OPTIONS = ["auto", "host", "terminal", "tmux", "internal-pty", "internal"];
3
+ const DEFAULT_MODE_OPTIONS = ["auto", "host", "terminal", "tmux", "internal"];
4
4
 
5
5
  function providerLabel(value) {
6
6
  if (value === "claude-cli") return "claude";
7
+ if (value === "agy-cli" || value === "agy" || value === "antigravity") return "agy";
7
8
  if (value === "ucode" || value === "ufoo" || value === "ufoo-code") return "ucode";
8
9
  return "codex";
9
10
  }
@@ -0,0 +1,6 @@
1
+ async function runChat(projectRoot, options = {}) {
2
+ const { runChatInk } = require("../../ui/ink/ChatApp");
3
+ return runChatInk(projectRoot, options);
4
+ }
5
+
6
+ module.exports = { runChat };
@@ -1,6 +1,6 @@
1
- const { IPC_REQUEST_TYPES } = require("../shared/eventContract");
1
+ const { IPC_REQUEST_TYPES } = require("../../runtime/contracts/eventContract");
2
2
  const { decodeEscapedNewlines } = require("./text");
3
- const { shouldEchoCommandInChat } = require("./commands");
3
+ const { describeCommandForChat, shouldEchoCommandInChat } = require("./commands");
4
4
  const { parseShellCommand, runShellCommand: defaultRunShellCommand } = require("./shellCommand");
5
5
 
6
6
  function createInputSubmitHandler(options = {}) {
@@ -20,8 +20,6 @@ function createInputSubmitHandler(options = {}) {
20
20
  enterAgentView = () => {},
21
21
  getAgentAdapter = () => null,
22
22
  activateAgent = async () => {},
23
- getInjectSockPath = () => "",
24
- existsSync = () => false,
25
23
  commitInputHistory = () => {},
26
24
  focusInput = () => {},
27
25
  renderScreen = () => {}, // Add renderScreen callback
@@ -42,17 +40,9 @@ function createInputSubmitHandler(options = {}) {
42
40
  async function tryActivateTargetAgent(agentId) {
43
41
  const adapter = getAgentAdapter(agentId);
44
42
  const capabilities = adapter && adapter.capabilities ? adapter.capabilities : null;
45
- const sockPath = getInjectSockPath(agentId);
46
- const supportsSocket = Boolean(capabilities && capabilities.supportsSocketProtocol);
47
43
  const supportsActivate = Boolean(capabilities && capabilities.supportsActivate);
48
44
  const supportsInternalQueue = Boolean(capabilities && capabilities.supportsInternalQueueLoop);
49
45
 
50
- if (existsSync(sockPath) && supportsSocket) {
51
- clearTargetAgent();
52
- enterAgentView(agentId);
53
- return true;
54
- }
55
-
56
46
  if (supportsActivate) {
57
47
  clearTargetAgent();
58
48
  try {
@@ -179,7 +169,8 @@ function createInputSubmitHandler(options = {}) {
179
169
 
180
170
  if (text.startsWith("/")) {
181
171
  if (shouldEchoCommandInChat(text)) {
182
- logMessage("user", userEcho(text));
172
+ const commandSummary = describeCommandForChat(text);
173
+ logMessage("user", userEcho(commandSummary || text));
183
174
  renderScreen(); // Render slash command immediately
184
175
  }
185
176
  try {
@@ -2,7 +2,7 @@
2
2
 
3
3
  const fs = require("fs");
4
4
  const path = require("path");
5
- const { getUfooPaths } = require("../ufoo/paths");
5
+ const { getUfooPaths } = require("../../coordination/state/paths");
6
6
 
7
7
  function stripAnsi(text = "") {
8
8
  return String(text || "").replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "")
@@ -0,0 +1,268 @@
1
+ const { calculatePaneLayout } = require("./paneLayout");
2
+ const { createPaneManager } = require("./paneManager");
3
+ const { createRenderer } = require("./renderer");
4
+
5
+ function createMultiWindowController(options = {}) {
6
+ const {
7
+ processStdout = process.stdout,
8
+ getRows = () => process.stdout.rows || 24,
9
+ getCols = () => process.stdout.columns || 80,
10
+ getInjectSockPath = () => "",
11
+ getActiveAgents = () => [],
12
+ getAgentPaneOptions = () => ({}),
13
+ getChatLogLines = () => [],
14
+ getStatusText = () => "",
15
+ getPromptPrefix = () => "› ",
16
+ getCurrentDraft = () => "",
17
+ getCursorPos = () => 0,
18
+ getCompletions = () => ({ items: [], index: -1, windowStart: 0, pageSize: 8 }),
19
+ getAgentLabel = (id) => id,
20
+ getInternalPaneInfo = () => ({}),
21
+ getDashboardLines = () => [],
22
+ getTerminalFocused = () => false,
23
+ freezeScreen = () => {},
24
+ restoreTerminal = () => {},
25
+ onExit = () => {},
26
+ onFocusAgent = () => {},
27
+ onInternalSubmit = () => {},
28
+ } = options;
29
+
30
+ let active = false;
31
+ let renderThrottleTimer = null;
32
+ let dirtyPanes = new Set();
33
+ let lastCompletionPopup = null;
34
+ const renderer = createRenderer({ write: (d) => processStdout.write(d) });
35
+ const paneManager = createPaneManager({
36
+ getInjectSockPath,
37
+ onInternalSubmit,
38
+ onPaneOutput: (agentId) => {
39
+ if (!active) return;
40
+ if (getTerminalFocused()) {
41
+ renderSinglePane(agentId);
42
+ } else {
43
+ dirtyPanes.add(agentId);
44
+ if (!renderThrottleTimer) {
45
+ renderThrottleTimer = setTimeout(() => {
46
+ renderThrottleTimer = null;
47
+ const panes = [...dirtyPanes];
48
+ dirtyPanes.clear();
49
+ for (const id of panes) renderSinglePane(id);
50
+ }, 200);
51
+ }
52
+ }
53
+ },
54
+ });
55
+
56
+ function enter() {
57
+ const agents = getActiveAgents();
58
+ if (agents.length === 0) return false;
59
+ if (active) return false;
60
+ active = true;
61
+ freezeScreen(true);
62
+ renderer.hideCursor();
63
+ renderer.clear();
64
+ syncAgents();
65
+ renderAll();
66
+ return true;
67
+ }
68
+
69
+ function exit() {
70
+ if (!active) return;
71
+ active = false;
72
+ if (renderThrottleTimer) {
73
+ clearTimeout(renderThrottleTimer);
74
+ renderThrottleTimer = null;
75
+ dirtyPanes.clear();
76
+ }
77
+ paneManager.disconnectAll();
78
+ renderer.showCursor();
79
+ restoreTerminal();
80
+ freezeScreen(false);
81
+ onExit();
82
+ }
83
+
84
+ function syncAgents() {
85
+ const agents = getActiveAgents();
86
+ const current = new Set(paneManager.getAgentIds());
87
+ const layout = calculatePaneLayout(getCols(), getRows(), agents.length);
88
+
89
+ for (let i = 0; i < agents.length; i++) {
90
+ const id = agents[i];
91
+ const pane = layout.agentPanes[i];
92
+ if (!pane) continue;
93
+ const innerW = Math.max(1, pane.width - 2);
94
+ const innerH = Math.max(1, pane.height - 2);
95
+ if (!current.has(id)) {
96
+ paneManager.addAgent(id, innerW, innerH, getAgentPaneOptions(id) || {});
97
+ } else {
98
+ paneManager.sendResize(id, innerW, innerH);
99
+ }
100
+ }
101
+ for (const id of current) {
102
+ if (!agents.includes(id)) {
103
+ paneManager.removeAgent(id);
104
+ }
105
+ }
106
+ }
107
+
108
+ function renderSinglePane(agentId) {
109
+ if (!active) return;
110
+ try {
111
+ const agents = paneManager.getAgentIds();
112
+ const layout = calculatePaneLayout(getCols(), getRows(), agents.length);
113
+ const idx = agents.indexOf(agentId);
114
+ if (idx < 0 || !layout.agentPanes[idx]) return;
115
+ const pane = paneManager.getPane(agentId);
116
+ if (!pane) return;
117
+ const isFocused = getTerminalFocused() && agentId === paneManager.getFocused();
118
+ if (pane.mode === "internal" && typeof renderer.renderInternalPane === "function") {
119
+ renderer.renderInternalPane(pane.vt, layout.agentPanes[idx], isFocused, {
120
+ label: getAgentLabel(agentId),
121
+ ...(getInternalPaneInfo(agentId) || {}),
122
+ input: pane.internalInput || "",
123
+ cursor: pane.internalCursor || 0,
124
+ });
125
+ } else {
126
+ renderer.renderPane(pane.vt, layout.agentPanes[idx], isFocused, getAgentLabel(agentId));
127
+ }
128
+ } catch {
129
+ // swallow render errors to prevent crash
130
+ }
131
+ }
132
+
133
+ function renderAll() {
134
+ if (!active) return;
135
+ try {
136
+ const agents = paneManager.getAgentIds();
137
+ const layout = calculatePaneLayout(getCols(), getRows(), agents.length);
138
+ const cols = getCols();
139
+
140
+ renderer.renderChatLog(layout.chatPane, getChatLogLines());
141
+
142
+ const focused = getTerminalFocused() ? paneManager.getFocused() : null;
143
+ for (let i = 0; i < agents.length; i++) {
144
+ const pane = paneManager.getPane(agents[i]);
145
+ if (!pane || !layout.agentPanes[i]) continue;
146
+ if (pane.mode === "internal" && typeof renderer.renderInternalPane === "function") {
147
+ renderer.renderInternalPane(pane.vt, layout.agentPanes[i], agents[i] === focused, {
148
+ label: getAgentLabel(agents[i]),
149
+ ...(getInternalPaneInfo(agents[i]) || {}),
150
+ input: pane.internalInput || "",
151
+ cursor: pane.internalCursor || 0,
152
+ });
153
+ } else {
154
+ renderer.renderPane(pane.vt, layout.agentPanes[i], agents[i] === focused, getAgentLabel(agents[i]));
155
+ }
156
+ }
157
+
158
+ const chatFocused = !getTerminalFocused();
159
+ if (layout.separatorPane) {
160
+ renderer.renderSeparator(layout.separatorPane, chatFocused);
161
+ }
162
+ if (layout.statusPane) {
163
+ renderer.renderStatusLine(layout.statusPane, getStatusText());
164
+ }
165
+ if (layout.inputPane) {
166
+ renderer.renderInputPrompt(layout.inputPane, getPromptPrefix(), getCurrentDraft(), getCursorPos());
167
+ }
168
+ if (layout.inputSepPane) {
169
+ renderer.renderSeparator(layout.inputSepPane, chatFocused);
170
+ }
171
+ if (layout.dashboardPane) {
172
+ const lines = getDashboardLines();
173
+ renderer.renderDashboard(layout.dashboardPane, lines);
174
+ }
175
+
176
+ const cmp = getCompletions();
177
+ let nextCompletionPopup = null;
178
+ if (lastCompletionPopup && typeof renderer.clearRows === "function") {
179
+ renderer.clearRows(
180
+ lastCompletionPopup.top,
181
+ lastCompletionPopup.height,
182
+ lastCompletionPopup.width,
183
+ lastCompletionPopup.left || 0
184
+ );
185
+ }
186
+ if (cmp && Array.isArray(cmp.items) && cmp.items.length > 0 && layout.inputPane) {
187
+ const start = Math.min(cmp.windowStart || 0, Math.max(0, cmp.items.length - (cmp.pageSize || 8)));
188
+ const end = Math.min(cmp.items.length, start + (cmp.pageSize || 8));
189
+ const visible = cmp.items.slice(start, end);
190
+ const popupTop = layout.inputPane.top - visible.length - 1;
191
+ if (popupTop >= 0) {
192
+ nextCompletionPopup = { top: popupTop, left: 0, width: cols, height: visible.length + 1 };
193
+ renderer.renderSeparator({ top: popupTop, left: 0, width: cols });
194
+ for (let i = 0; i < visible.length; i++) {
195
+ const idx = start + i;
196
+ const selected = idx === cmp.index;
197
+ const label = visible[i].label || "";
198
+ const desc = visible[i].description || "";
199
+ const line = selected
200
+ ? `\x1b[7;36m${label}\x1b[0m \x1b[90m${desc}\x1b[0m`
201
+ : `\x1b[90m${label} ${desc}\x1b[0m`;
202
+ const pad = Math.max(0, cols - renderer.visibleLength(line));
203
+ renderer.write(renderer.moveTo(popupTop + 1 + i, 0) + line + " ".repeat(pad) + "\x1b[0m");
204
+ }
205
+ }
206
+ }
207
+ lastCompletionPopup = nextCompletionPopup;
208
+ } catch {
209
+ // swallow render errors to prevent crash
210
+ }
211
+ }
212
+
213
+ function handleKey(key) {
214
+ if (!active) return false;
215
+
216
+ if (key.name === "c" && key.ctrl) {
217
+ return false;
218
+ }
219
+
220
+ if (key.name === "w" && key.ctrl) {
221
+ paneManager.cycleFocus();
222
+ renderAll();
223
+ return true;
224
+ }
225
+
226
+ if (key.name === "q" && key.ctrl) {
227
+ exit();
228
+ return true;
229
+ }
230
+
231
+ return false;
232
+ }
233
+
234
+ function focusAgent(agentId) {
235
+ if (!active) return;
236
+ const agents = paneManager.getAgentIds();
237
+ if (!agents.includes(agentId)) return;
238
+ paneManager.setFocused(agentId);
239
+ onFocusAgent(agentId);
240
+ renderAll();
241
+ }
242
+
243
+ function handleResize() {
244
+ if (!active) return;
245
+ syncAgents();
246
+ renderer.clear();
247
+ renderAll();
248
+ }
249
+
250
+ function isActive() { return active; }
251
+
252
+ return {
253
+ enter,
254
+ exit,
255
+ isActive,
256
+ handleKey,
257
+ handleResize,
258
+ syncAgents,
259
+ renderAll,
260
+ focusAgent,
261
+ sendInput: (data) => paneManager.sendInput(data),
262
+ writeToPane: (agentId, data) => paneManager.writeToPane(agentId, data),
263
+ getFocused: () => paneManager.getFocused(),
264
+ getAgentIds: () => paneManager.getAgentIds(),
265
+ };
266
+ }
267
+
268
+ module.exports = { createMultiWindowController };
@@ -0,0 +1,84 @@
1
+ function calculatePaneLayout(termCols, termRows, agentCount) {
2
+ const bottomRows = 5;
3
+ const safeRows = termRows - 1;
4
+ const contentHeight = Math.max(1, safeRows - bottomRows);
5
+ const bottomTop = contentHeight;
6
+
7
+ const statusPane = { top: bottomTop, left: 0, width: termCols };
8
+ const separatorPane = { top: bottomTop + 1, left: 0, width: termCols };
9
+ const inputPane = { top: bottomTop + 2, left: 0, width: termCols };
10
+ const inputSepPane = { top: bottomTop + 3, left: 0, width: termCols };
11
+ const dashboardPane = { top: bottomTop + 4, left: 0, width: termCols };
12
+
13
+ if (agentCount <= 0) {
14
+ return {
15
+ separatorPane,
16
+ statusPane,
17
+ inputPane,
18
+ inputSepPane,
19
+ dashboardPane,
20
+ chatPane: { top: 0, left: 0, width: termCols, height: contentHeight },
21
+ agentPanes: [],
22
+ };
23
+ }
24
+
25
+ const chatWidth = Math.floor(termCols / 3);
26
+ const rightLeft = chatWidth + 1;
27
+ const rightWidth = termCols - chatWidth - 1;
28
+
29
+ const chatPane = { top: 0, left: 0, width: chatWidth, height: contentHeight };
30
+
31
+ if (rightWidth < 4 || contentHeight < 3) {
32
+ return {
33
+ separatorPane,
34
+ statusPane,
35
+ inputPane,
36
+ inputSepPane,
37
+ dashboardPane,
38
+ chatPane: { top: 0, left: 0, width: termCols, height: contentHeight },
39
+ agentPanes: [],
40
+ };
41
+ }
42
+
43
+ const agentPanes = layoutAgentPanes(rightLeft, rightWidth, contentHeight, agentCount, 0);
44
+ return { separatorPane, statusPane, inputPane, inputSepPane, dashboardPane, chatPane, agentPanes };
45
+ }
46
+
47
+ function layoutAgentPanes(left, width, height, count, topOffset = 0) {
48
+ if (count === 1) {
49
+ return [{ top: topOffset, left, width, height }];
50
+ }
51
+ if (count === 2) {
52
+ const h1 = Math.floor(height / 2);
53
+ return [
54
+ { top: topOffset, left, width, height: h1 },
55
+ { top: topOffset + h1, left, width, height: height - h1 },
56
+ ];
57
+ }
58
+
59
+ const rowCount = Math.ceil(count / 2);
60
+ const rowHeight = Math.floor(height / rowCount);
61
+ const panes = [];
62
+ let placed = 0;
63
+
64
+ for (let row = 0; row < rowCount; row++) {
65
+ const rowTop = topOffset + row * rowHeight;
66
+ const actualHeight = row === rowCount - 1 ? height - row * rowHeight : rowHeight;
67
+ const remaining = count - placed;
68
+ const isOddRow = remaining % 2 === 1 && row === 0 && count % 2 === 1;
69
+
70
+ if (isOddRow) {
71
+ panes.push({ top: rowTop, left, width, height: actualHeight });
72
+ placed++;
73
+ } else {
74
+ const halfWidth = Math.floor(width / 2);
75
+ panes.push({ top: rowTop, left, width: halfWidth, height: actualHeight });
76
+ panes.push({ top: rowTop, left: left + halfWidth + 1, width: width - halfWidth - 1, height: actualHeight });
77
+ placed += 2;
78
+ }
79
+ }
80
+
81
+ return panes;
82
+ }
83
+
84
+ module.exports = { calculatePaneLayout };