u-foo 2.3.31 → 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 (236) 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 +9 -5
  12. package/scripts/chat-app-smoke.js +30 -0
  13. package/scripts/global-chat-switch-benchmark.js +5 -5
  14. package/scripts/ink-demo.js +23 -0
  15. package/scripts/ink-smoke.js +30 -0
  16. package/scripts/ucode-app-smoke.js +36 -0
  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 +56 -28
  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 +54 -4
  53. package/src/{chat → app/chat}/daemonTransport.js +2 -1
  54. package/src/{chat → app/chat}/dashboardView.js +2 -21
  55. package/src/app/chat/index.js +6 -0
  56. package/src/{chat → app/chat}/inputSubmitHandler.js +38 -13
  57. package/src/{chat → app/chat}/internalAgentLogHistory.js +1 -1
  58. package/src/app/chat/multiWindow/index.js +268 -0
  59. package/src/app/chat/multiWindow/paneLayout.js +84 -0
  60. package/src/app/chat/multiWindow/paneManager.js +299 -0
  61. package/src/app/chat/multiWindow/renderer.js +384 -0
  62. package/src/app/chat/multiWindow/virtualTerminal.js +327 -0
  63. package/src/{chat → app/chat}/projectCloseController.js +1 -1
  64. package/src/app/chat/shellCommand.js +42 -0
  65. package/src/{chat → app/chat}/transport.js +16 -3
  66. package/src/{cli → app/cli}/ctxCoreCommands.js +3 -3
  67. package/src/{doctor/index.js → app/cli/features/doctor.js} +1 -1
  68. package/src/{init/index.js → app/cli/features/init.js} +14 -32
  69. package/src/{cli → app/cli}/groupCoreCommands.js +2 -2
  70. package/src/app/cli/index.js +9 -0
  71. package/src/{cli → app/cli}/onlineCoreCommands.js +5 -5
  72. package/src/{cli.js → app/cli/run.js} +62 -59
  73. package/src/app/index.js +6 -0
  74. package/src/code/agent.js +10 -9
  75. package/src/code/index.js +2 -0
  76. package/src/code/launcher/index.js +9 -0
  77. package/src/{agent → code/launcher}/ucode.js +7 -8
  78. package/src/{agent → code/launcher}/ucodeBootstrap.js +3 -3
  79. package/src/{agent → code/launcher}/ucodeBuild.js +2 -2
  80. package/src/{agent → code/launcher}/ucodeDoctor.js +2 -2
  81. package/src/{agent → code/launcher}/ucodeRuntimeConfig.js +1 -2
  82. package/src/code/nativeRunner.js +4 -4
  83. package/src/code/taskDecomposer.js +5 -4
  84. package/src/code/tui.js +39 -1997
  85. package/src/config.js +15 -2
  86. package/src/{bus → coordination/bus}/activate.js +2 -2
  87. package/src/{bus → coordination/bus}/daemon.js +15 -5
  88. package/src/coordination/bus/envelope.js +173 -0
  89. package/src/{bus → coordination/bus}/index.js +7 -3
  90. package/src/{bus → coordination/bus}/inject.js +11 -3
  91. package/src/{bus → coordination/bus}/message.js +1 -1
  92. package/src/coordination/bus/messageMeta.js +130 -0
  93. package/src/coordination/bus/promptEnvelope.js +65 -0
  94. package/src/{bus → coordination/bus}/shake.js +1 -1
  95. package/src/{bus → coordination/bus}/store.js +3 -3
  96. package/src/{bus → coordination/bus}/subscriber.js +2 -2
  97. package/src/{bus → coordination/bus}/utils.js +2 -2
  98. package/src/{history → coordination/history}/inputTimeline.js +5 -5
  99. package/src/coordination/index.js +10 -0
  100. package/src/{memory → coordination/memory}/historySearch.js +1 -1
  101. package/src/{memory → coordination/memory}/index.js +3 -3
  102. package/src/{report → coordination/report}/store.js +2 -2
  103. package/src/{ufoo → coordination/state}/agentRegistryDiagnostics.js +43 -0
  104. package/src/{status → coordination/status}/index.js +3 -3
  105. package/src/online/bridge.js +2 -2
  106. package/src/{controller → orchestration/controller}/flags.js +1 -1
  107. package/src/{controller → orchestration/controller}/gateRouter.js +1 -1
  108. package/src/orchestration/controller/index.js +10 -0
  109. package/src/{controller → orchestration/controller}/shadowGuard.js +1 -1
  110. package/src/orchestration/groups/bootstrap.js +3 -0
  111. package/src/orchestration/groups/index.js +10 -0
  112. package/src/orchestration/groups/promptProfiles.js +3 -0
  113. package/src/{group → orchestration/groups}/templates.js +1 -1
  114. package/src/{group → orchestration/groups}/validateTemplate.js +1 -1
  115. package/src/orchestration/index.js +7 -0
  116. package/src/orchestration/solo/index.js +3 -0
  117. package/src/{daemon → runtime/daemon}/agentProcessManager.js +1 -1
  118. package/src/{daemon → runtime/daemon}/cronOps.js +3 -2
  119. package/src/{daemon → runtime/daemon}/groupOrchestrator.js +26 -9
  120. package/src/{daemon → runtime/daemon}/index.js +273 -79
  121. package/src/{daemon → runtime/daemon}/ipcServer.js +24 -2
  122. package/src/{daemon → runtime/daemon}/nicknameScope.js +6 -3
  123. package/src/{daemon → runtime/daemon}/ops.js +48 -61
  124. package/src/{daemon → runtime/daemon}/promptLoop.js +1 -1
  125. package/src/{daemon → runtime/daemon}/promptRequest.js +13 -8
  126. package/src/runtime/daemon/providerSessions.js +230 -0
  127. package/src/{daemon → runtime/daemon}/reporting.js +4 -4
  128. package/src/{daemon → runtime/daemon}/run.js +12 -5
  129. package/src/{daemon → runtime/daemon}/soloBootstrap.js +7 -7
  130. package/src/{daemon → runtime/daemon}/status.js +5 -5
  131. package/src/runtime/index.js +10 -0
  132. package/src/runtime/process/nodeExecutable.js +26 -0
  133. package/src/{projects → runtime/projects}/registry.js +1 -1
  134. package/src/{projects → runtime/projects}/runtimes.js +1 -1
  135. package/src/{terminal → runtime/terminal}/adapterRouter.js +0 -10
  136. package/src/{terminal → runtime/terminal}/adapters/internalAdapter.js +0 -4
  137. package/src/tools/handlers/common.js +1 -1
  138. package/src/tools/handlers/listAgents.js +1 -1
  139. package/src/tools/handlers/memory.js +3 -3
  140. package/src/tools/handlers/readBusSummary.js +1 -1
  141. package/src/tools/handlers/readOpenDecisions.js +1 -1
  142. package/src/tools/handlers/readProjectRegistry.js +1 -1
  143. package/src/tools/handlers/readPromptHistory.js +2 -2
  144. package/src/tools/schemaFixtures.js +1 -1
  145. package/src/ui/MIGRATION.md +336 -0
  146. package/src/ui/format/index.js +974 -0
  147. package/src/ui/index.js +9 -0
  148. package/src/ui/ink/ChatApp.js +3674 -0
  149. package/src/ui/ink/DashboardBar.js +685 -0
  150. package/src/ui/ink/InkDemo.js +96 -0
  151. package/src/ui/ink/MultilineInput.js +612 -0
  152. package/src/ui/ink/UcodeApp.js +822 -0
  153. package/src/ui/ink/agentMirror.js +730 -0
  154. package/src/ui/ink/chatReducer.js +359 -0
  155. package/src/ui/runInk.js +57 -0
  156. package/src/bus/messageMeta.js +0 -52
  157. package/src/chat/agentViewController.js +0 -1072
  158. package/src/chat/chatLogController.js +0 -138
  159. package/src/chat/completionController.js +0 -533
  160. package/src/chat/dashboardKeyController.js +0 -573
  161. package/src/chat/index.js +0 -2214
  162. package/src/chat/inputHistoryController.js +0 -135
  163. package/src/chat/inputListenerController.js +0 -470
  164. package/src/chat/layout.js +0 -186
  165. package/src/chat/pasteController.js +0 -81
  166. package/src/chat/statusLineController.js +0 -223
  167. package/src/chat/streamTracker.js +0 -156
  168. package/src/code/config +0 -0
  169. package/src/daemon/providerSessions.js +0 -488
  170. package/src/terminal/adapters/internalPtyAdapter.js +0 -42
  171. /package/src/{code/prompts → agents/prompts/native}/actions.js +0 -0
  172. /package/src/{code/prompts → agents/prompts/native}/efficiency.js +0 -0
  173. /package/src/{code/prompts → agents/prompts/native}/environment.js +0 -0
  174. /package/src/{code/prompts → agents/prompts/native}/identity.js +0 -0
  175. /package/src/{code/prompts → agents/prompts/native}/safety.js +0 -0
  176. /package/src/{code/prompts → agents/prompts/native}/sections.js +0 -0
  177. /package/src/{code/prompts → agents/prompts/native}/system.js +0 -0
  178. /package/src/{code/prompts → agents/prompts/native}/tasks.js +0 -0
  179. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/bash.js +0 -0
  180. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/edit.js +0 -0
  181. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/read.js +0 -0
  182. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/write.js +0 -0
  183. /package/src/{code/prompts → agents/prompts/native}/ufoo.js +0 -0
  184. /package/src/{group → agents/prompts}/promptProfiles.js +0 -0
  185. /package/src/{agent → agents/providers}/claudeEventTranslator.js +0 -0
  186. /package/src/{agent → agents/providers}/claudeOauthTokenReader.js +0 -0
  187. /package/src/{agent → agents/providers}/claudeSessionFiles.js +0 -0
  188. /package/src/{agent → agents/providers}/codexEventTranslator.js +0 -0
  189. /package/src/{agent → agents/providers}/credentials/claude.js +0 -0
  190. /package/src/{agent → agents/providers}/credentials/codex.js +0 -0
  191. /package/src/{agent → agents/providers}/credentials/index.js +0 -0
  192. /package/src/{chat → app/chat}/agentBar.js +0 -0
  193. /package/src/{chat → app/chat}/agentDirectory.js +0 -0
  194. /package/src/{chat → app/chat}/cronScheduler.js +0 -0
  195. /package/src/{chat → app/chat}/daemonCoordinator.js +0 -0
  196. /package/src/{chat → app/chat}/daemonReconnect.js +0 -0
  197. /package/src/{chat → app/chat}/daemonTransportDefaults.js +0 -0
  198. /package/src/{chat → app/chat}/inputMath.js +0 -0
  199. /package/src/{chat → app/chat}/rawKeyMap.js +0 -0
  200. /package/src/{chat → app/chat}/settingsController.js +0 -0
  201. /package/src/{chat → app/chat}/text.js +0 -0
  202. /package/src/{chat → app/chat}/transientAgentState.js +0 -0
  203. /package/src/{cli → app/cli}/busCoreCommands.js +0 -0
  204. /package/src/{skills/index.js → app/cli/features/skills.js} +0 -0
  205. /package/src/{bus → coordination/bus}/nickname.js +0 -0
  206. /package/src/{bus → coordination/bus}/queue.js +0 -0
  207. /package/src/{context → coordination/context}/decisions.js +0 -0
  208. /package/src/{context → coordination/context}/doctor.js +0 -0
  209. /package/src/{context → coordination/context}/index.js +0 -0
  210. /package/src/{context → coordination/context}/sync.js +0 -0
  211. /package/src/{ufoo → coordination/state}/agentsStore.js +0 -0
  212. /package/src/{ufoo → coordination/state}/paths.js +0 -0
  213. /package/src/{controller → orchestration/controller}/launchRouting.js +0 -0
  214. /package/src/{controller → orchestration/controller}/routerFastPath.js +0 -0
  215. /package/src/{controller → orchestration/controller}/routerFinalize.js +0 -0
  216. /package/src/{group → orchestration/groups}/diagram.js +0 -0
  217. /package/src/{group → orchestration/groups}/templateValidation.js +0 -0
  218. /package/src/{solo → orchestration/solo}/commands.js +0 -0
  219. /package/src/{shared → runtime/contracts}/eventContract.js +0 -0
  220. /package/src/{shared → runtime/contracts}/ptySocketContract.js +0 -0
  221. /package/src/{providerapi → runtime/privacy}/redactor.js +0 -0
  222. /package/src/{providerapi → runtime/privacy}/shadowDiff.js +0 -0
  223. /package/src/{projects → runtime/projects}/identity.js +0 -0
  224. /package/src/{projects → runtime/projects}/index.js +0 -0
  225. /package/src/{projects → runtime/projects}/projectId.js +0 -0
  226. /package/src/{terminal → runtime/terminal}/adapterContract.js +0 -0
  227. /package/src/{terminal → runtime/terminal}/adapters/externalAdapter.js +0 -0
  228. /package/src/{terminal → runtime/terminal}/adapters/hostAdapter.js +0 -0
  229. /package/src/{terminal → runtime/terminal}/adapters/internalQueueAdapter.js +0 -0
  230. /package/src/{terminal → runtime/terminal}/adapters/terminalAdapter.js +0 -0
  231. /package/src/{terminal → runtime/terminal}/adapters/tmuxAdapter.js +0 -0
  232. /package/src/{terminal → runtime/terminal}/detect.js +0 -0
  233. /package/src/{terminal → runtime/terminal}/index.js +0 -0
  234. /package/src/{terminal → runtime/terminal}/iterm2.js +0 -0
  235. /package/src/{utils → ui/format}/banner.js +0 -0
  236. /package/src/{shared → ui/format}/markdownRenderer.js +0 -0
@@ -1,6 +1,7 @@
1
1
  const path = require("path");
2
2
  const { startDaemon, stopDaemon, isRunning } = require("./index");
3
- const { loadConfig, defaultAgentModelForProvider } = require("../config");
3
+ const { loadConfig, defaultAgentModelForProvider } = require("../../config");
4
+ const { resolveNodeExecutable } = require("../process/nodeExecutable");
4
5
 
5
6
  function runDaemonCli(argv) {
6
7
  const cmd = argv[1] || "start";
@@ -19,7 +20,7 @@ function runDaemonCli(argv) {
19
20
  if (isRunning(projectRoot)) return;
20
21
  if (!process.env.UFOO_DAEMON_CHILD) {
21
22
  const { spawn } = require("child_process");
22
- const child = spawn(process.execPath, [path.join(__dirname, "..", "..", "bin", "ufoo.js"), "daemon", "start"], {
23
+ const child = spawn(resolveNodeExecutable(), [path.join(__dirname, "..", "..", "..", "bin", "ufoo.js"), "daemon", "start"], {
23
24
  detached: true,
24
25
  stdio: "ignore",
25
26
  env: { ...process.env, UFOO_DAEMON_CHILD: "1" },
@@ -32,25 +33,31 @@ function runDaemonCli(argv) {
32
33
  return;
33
34
  }
34
35
  if (cmd === "stop" || cmd === "--stop") {
35
- stopDaemon(projectRoot);
36
+ if (!stopDaemon(projectRoot, { source: process.env.UFOO_DAEMON_STOP_SOURCE || `daemon-cli:${cmd} pid=${process.pid}` })) {
37
+ process.exitCode = 1;
38
+ }
36
39
  return;
37
40
  }
38
41
  if (cmd === "restart" || cmd === "--restart") {
39
42
  // Stop if running
40
43
  if (isRunning(projectRoot)) {
41
- stopDaemon(projectRoot);
44
+ const stopped = stopDaemon(projectRoot, { source: process.env.UFOO_DAEMON_STOP_SOURCE || `daemon-cli:${cmd} pid=${process.pid}` });
42
45
  // Wait for clean shutdown
43
46
  let attempts = 0;
44
47
  while (isRunning(projectRoot) && attempts < 50) {
45
48
  attempts++;
46
49
  require("child_process").spawnSync("sleep", ["0.1"]);
47
50
  }
51
+ if (!stopped && isRunning(projectRoot)) {
52
+ process.exitCode = 1;
53
+ return;
54
+ }
48
55
  }
49
56
  // Start fresh daemon
50
57
  if (!process.env.UFOO_DAEMON_CHILD) {
51
58
  const { spawn } = require("child_process");
52
59
  const childEnv = { ...process.env, UFOO_DAEMON_CHILD: "1" };
53
- const child = spawn(process.execPath, [path.join(__dirname, "..", "..", "bin", "ufoo.js"), "daemon", "start"], {
60
+ const child = spawn(resolveNodeExecutable(), [path.join(__dirname, "..", "..", "..", "bin", "ufoo.js"), "daemon", "start"], {
54
61
  detached: true,
55
62
  stdio: "ignore",
56
63
  env: childEnv,
@@ -1,24 +1,24 @@
1
1
  "use strict";
2
2
 
3
3
  const path = require("path");
4
- const EventBus = require("../bus");
5
- const { prepareUcodeBootstrap } = require("../agent/ucodeBootstrap");
6
- const { isMetaActive } = require("../bus/utils");
7
- const { getUfooPaths } = require("../ufoo/paths");
4
+ const EventBus = require("../../coordination/bus");
5
+ const { prepareUcodeBootstrap } = require("../../code/launcher/ucodeBootstrap");
6
+ const { isMetaActive } = require("../../coordination/bus/utils");
7
+ const { getUfooPaths } = require("../../coordination/state/paths");
8
8
  const {
9
9
  applyProjectNicknamePrefix,
10
10
  resolveDisplayNickname,
11
11
  resolveScopedNickname,
12
12
  } = require("./nicknameScope");
13
- const { loadAgentsData, saveAgentsData } = require("../ufoo/agentsStore");
13
+ const { loadAgentsData, saveAgentsData } = require("../../coordination/state/agentsStore");
14
14
  const {
15
15
  loadPromptProfileRegistry,
16
16
  resolvePromptProfileReference,
17
- } = require("../group/promptProfiles");
17
+ } = require("../../orchestration/groups/promptProfiles");
18
18
  const {
19
19
  buildSoloPromptMetadata,
20
20
  composeSoloBootstrapPrompt,
21
- } = require("../group/bootstrap");
21
+ } = require("../../orchestration/groups/bootstrap");
22
22
 
23
23
  function sleep(ms) {
24
24
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -1,10 +1,10 @@
1
1
  const fs = require("fs");
2
2
  const path = require("path");
3
- const { getUfooPaths } = require("../ufoo/paths");
4
- const { isMetaActive } = require("../bus/utils");
3
+ const { getUfooPaths } = require("../../coordination/state/paths");
4
+ const { isMetaActive } = require("../../coordination/bus/utils");
5
5
  const { resolveDisplayNickname, resolveScopedNickname } = require("./nicknameScope");
6
- const { readReportSummary, countControllerInboxEntries } = require("../report/store");
7
- const { readRecentLoopSummary } = require("../agent/loopObservability");
6
+ const { readReportSummary, countControllerInboxEntries } = require("../../coordination/report/store");
7
+ const { readRecentLoopSummary } = require("../../agents/controller/loopObservability");
8
8
 
9
9
  function readBus(projectRoot) {
10
10
  const busPath = getUfooPaths(projectRoot).agentsFile;
@@ -16,7 +16,7 @@ function readBus(projectRoot) {
16
16
  }
17
17
 
18
18
  function readDecisions(projectRoot) {
19
- const DecisionsManager = require("../context/decisions");
19
+ const DecisionsManager = require("../../coordination/context/decisions");
20
20
  const manager = new DecisionsManager(projectRoot);
21
21
  const dir = manager.decisionsDir;
22
22
  let open = 0;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ projects: require("./projects"),
5
+ terminal: require("./terminal"),
6
+ contracts: {
7
+ ...require("./contracts/eventContract"),
8
+ ...require("./contracts/ptySocketContract"),
9
+ },
10
+ };
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+
5
+ function resolveNodeExecutable(options = {}) {
6
+ const {
7
+ execPath = process.execPath,
8
+ env = process.env,
9
+ fsModule = fs,
10
+ } = options;
11
+ const override = String((env && env.UFOO_NODE_EXECUTABLE) || "").trim();
12
+ if (override) return override;
13
+ const current = String(execPath || "").trim();
14
+ if (current) {
15
+ try {
16
+ if (fsModule.existsSync(current)) return current;
17
+ } catch {
18
+ // Fall through to PATH lookup.
19
+ }
20
+ }
21
+ return "node";
22
+ }
23
+
24
+ module.exports = {
25
+ resolveNodeExecutable,
26
+ };
@@ -2,7 +2,7 @@ const fs = require("fs");
2
2
  const os = require("os");
3
3
  const path = require("path");
4
4
  const { canonicalProjectRoot, buildProjectId, trimTrailingSlashes } = require("./projectId");
5
- const { getUfooPaths } = require("../ufoo/paths");
5
+ const { getUfooPaths } = require("../../coordination/state/paths");
6
6
 
7
7
  const DEFAULT_STALE_TTL_MS = 30 * 1000;
8
8
  const DEFAULT_TMP_CLEANUP_AGE_MS = 5 * 60 * 1000;
@@ -17,7 +17,7 @@ function filterVisibleProjectRuntimes(rows = []) {
17
17
  const sourceRows = Array.isArray(rows) ? rows : [];
18
18
  return sourceRows.filter((row) => {
19
19
  const status = String((row && row.status) || "").trim().toLowerCase();
20
- return status !== "stopped";
20
+ return status === "running";
21
21
  });
22
22
  }
23
23
 
@@ -5,7 +5,6 @@ const {
5
5
  const { createTerminalAdapter } = require("./adapters/terminalAdapter");
6
6
  const { createTmuxAdapter } = require("./adapters/tmuxAdapter");
7
7
  const { createInternalQueueAdapter } = require("./adapters/internalQueueAdapter");
8
- const { createInternalPtyAdapter } = require("./adapters/internalPtyAdapter");
9
8
  const { createHostAdapter } = require("./adapters/hostAdapter");
10
9
 
11
10
  function createTerminalAdapterRouter(options = {}) {
@@ -65,15 +64,6 @@ function createTerminalAdapterRouter(options = {}) {
65
64
  });
66
65
  }
67
66
 
68
- if (launchMode === "internal-pty") {
69
- return createInternalPtyAdapter({
70
- sendRaw,
71
- sendResize,
72
- requestSnapshot,
73
- createAdapter,
74
- });
75
- }
76
-
77
67
  if (launchMode === "internal") {
78
68
  return createInternalQueueAdapter({
79
69
  sendRaw,
@@ -1,10 +1,6 @@
1
- const { createInternalPtyAdapter } = require("./internalPtyAdapter");
2
1
  const { createInternalQueueAdapter } = require("./internalQueueAdapter");
3
2
 
4
3
  function createInternalAdapter(options = {}) {
5
- if (options.usePty) {
6
- return createInternalPtyAdapter(options);
7
- }
8
4
  return createInternalQueueAdapter(options);
9
5
  }
10
6
 
@@ -1,4 +1,4 @@
1
- const EventBus = require("../../bus");
1
+ const EventBus = require("../../coordination/bus");
2
2
  const { CALLER_TIERS, normalizeCallerTier } = require("../types");
3
3
 
4
4
  function extractAuditFields(ctx = {}) {
@@ -1,4 +1,4 @@
1
- const { buildStatus } = require("../../daemon/status");
1
+ const { buildStatus } = require("../../runtime/daemon/status");
2
2
 
3
3
  function listAgentsHandler(ctx = {}) {
4
4
  const status = buildStatus(ctx.projectRoot);
@@ -1,6 +1,6 @@
1
- const MemoryManager = require("../../memory");
2
- const { estimateTokens } = require("../../memory");
3
- const { searchHistory } = require("../../memory/historySearch");
1
+ const MemoryManager = require("../../coordination/memory");
2
+ const { estimateTokens } = require("../../coordination/memory");
3
+ const { searchHistory } = require("../../coordination/memory/historySearch");
4
4
  const { buildToolError, extractAuditFields, requireSubscriber } = require("./common");
5
5
 
6
6
  const BANNED_TIME_PATTERNS = [
@@ -1,4 +1,4 @@
1
- const { buildStatus } = require("../../daemon/status");
1
+ const { buildStatus } = require("../../runtime/daemon/status");
2
2
 
3
3
  function readBusSummaryHandler(ctx = {}) {
4
4
  const status = buildStatus(ctx.projectRoot);
@@ -1,4 +1,4 @@
1
- const DecisionsManager = require("../../context/decisions");
1
+ const DecisionsManager = require("../../coordination/context/decisions");
2
2
 
3
3
  function readOpenDecisionsHandler(ctx = {}, args = {}) {
4
4
  const limit = Number.isFinite(Number(args.limit)) && Number(args.limit) > 0
@@ -1,4 +1,4 @@
1
- const { listProjectRuntimes } = require("../../projects/registry");
1
+ const { listProjectRuntimes } = require("../../runtime/projects/registry");
2
2
 
3
3
  function readProjectRegistryHandler(_ctx = {}, args = {}) {
4
4
  const validate = args.validate !== false;
@@ -1,8 +1,8 @@
1
1
  const fs = require("fs");
2
2
  const path = require("path");
3
3
 
4
- const { buildStatus } = require("../../daemon/status");
5
- const { getUfooPaths } = require("../../ufoo/paths");
4
+ const { buildStatus } = require("../../runtime/daemon/status");
5
+ const { getUfooPaths } = require("../../coordination/state/paths");
6
6
 
7
7
  function clipPromptText(value = "", maxChars = 240) {
8
8
  const text = String(value || "").replace(/\s+/g, " ").trim();
@@ -456,7 +456,7 @@ const LAUNCH_AGENT_SCHEMA = Object.freeze({
456
456
  properties: Object.freeze({
457
457
  agent: Object.freeze({
458
458
  type: "string",
459
- enum: Object.freeze(["codex", "claude", "ucode"]),
459
+ enum: Object.freeze(["codex", "claude", "ucode", "agy"]),
460
460
  }),
461
461
  count: Object.freeze({ type: "integer", minimum: 1 }),
462
462
  nickname: Object.freeze({ type: "string" }),
@@ -0,0 +1,336 @@
1
+ # Ink TUI Migration Plan
2
+
3
+ Status: Ink is the only TUI for chat and ucode. The legacy blessed renderer
4
+ has been removed after the parity close-out.
5
+
6
+ ## Why
7
+
8
+ The removed legacy chat, ucode and internal-agent TUIs used blessed. Blessed
9
+ is an unmaintained imperative widget tree with no modern equivalent of
10
+ React's component model, and it was awkward to extend (manual layout math,
11
+ manual redraws, no useful test harness).
12
+
13
+ ink (the React-for-terminals library, what Claude Code, Codex CLI fronts
14
+ and the Gemini CLI all use) gives us declarative components, flexbox
15
+ layout, hooks, and proper isolation of pure logic from rendering.
16
+
17
+ ## Approach
18
+
19
+ - Ink is the renderer for both chat and ucode.
20
+ - Pure helpers live in `src/ui/format/`, so behaviour parity is enforced by
21
+ test rather than copy/paste.
22
+ - Components live in `src/ui/ink/`, written in plain JS via
23
+ `React.createElement` (no JSX, no build step) so jest stays vanilla.
24
+ - ink is loaded through `src/ui/runInk.js`, a thin CJS→ESM bridge so the
25
+ rest of the codebase stays CommonJS.
26
+
27
+ ## Progress
28
+
29
+ - **P0** ✅ ink + react deps, runtime bridge, `<InkDemo>` smoke harness,
30
+ pure helpers extracted to `src/ui/format/`.
31
+ - **P1** ✅ ucode TUI ported to ink.
32
+ - **P2** ✅ folded into P3.6 (internal agent view).
33
+ - **P3** ✅ chat TUI ported to ink. Daemon
34
+ connection, dashboard (5 views), tool-merge, status spinner, history,
35
+ agent selection, raw-PTY mirror and internal bus agent view are wired.
36
+ - **P4** ✅ parity close-out complete: full `commandExecutor` dispatch,
37
+ `daemonMessageRouter` callback coverage, persisted history, BUS streams,
38
+ transient agent state, loop summary, project rail switching, cron/settings
39
+ dashboard actions, completion popup, and default Ink entrypoints.
40
+
41
+ ## P1 ucode TUI — what's wired
42
+
43
+ | Feature | Status |
44
+ |---|---|
45
+ | Banner + version + session id header | ✅ |
46
+ | Scrolling `<Static>` log (1000 line cap) | ✅ |
47
+ | Multiline input (cursor math, Ctrl+A/E/B/F/D/H/K/U/W, Meta+B/F/D, `\\\n` continuation, Alt+Enter newline, CJK wrap) | ✅ |
48
+ | Up/Down history walk + agent-selection mode | ✅ |
49
+ | Ctrl+C exit, Ctrl+O expand last tool group | ✅ |
50
+ | Tool merge/freeze/expand state machine | ✅ |
51
+ | Spinner + phase status line (request/thinking/text/tool labels) | ✅ |
52
+ | Esc abort with `AbortController` and "Cancelling..." status | ✅ |
53
+ | Agents footer with single-line truncation + `+N more` hint | ✅ |
54
+ | `runSingleCommand` empty/exit/probe/help/error/tool/nl/ubus/resume/nl_bg kinds | ✅ |
55
+ | Background tasks ("BG x/y/z" suffix) | ✅ |
56
+ | ubus / resume / nl_bg branches | ✅ |
57
+ | autoBus polling | ✅ |
58
+
59
+ ## Real-TTY checklist
60
+
61
+ ```sh
62
+ ./bin/ucode.js
63
+ ```
64
+
65
+ ### Editor
66
+
67
+ - [ ] Type, see characters appear with the cursor on the next cell.
68
+ - [ ] Backspace deletes one cell back; cursor stays correct on CJK.
69
+ - [ ] Left/Right arrows move the cursor; resetting preferred col.
70
+ - [ ] Up/Down on a single line walks input history.
71
+ - [ ] Up/Down on a multiline value moves between visual rows.
72
+ - [ ] `\` followed by Enter inserts a newline; Enter alone submits.
73
+ - [ ] Alt+Enter inserts a newline.
74
+ - [ ] Ctrl+A / Ctrl+E jump to row start / end.
75
+ - [ ] Ctrl+W deletes the previous word (also Meta+Backspace).
76
+ - [ ] Long pasted text doesn't lock up the renderer.
77
+ - [ ] Resize the terminal — input frame and footer span the new width.
78
+
79
+ ### Status line
80
+
81
+ - [ ] Shows `UCODE · Ready` while idle.
82
+ - [ ] Shows a spinning indicator + phase ("Waiting for model...",
83
+ "Thinking...", "Generating response...", "Calling X...") during
84
+ `runNaturalLanguageTask`.
85
+ - [ ] Appends `(<elapsed> s, esc cancel)` when a task is in flight.
86
+ - [ ] Esc on a running task flips to "Cancelling..." then back to Ready.
87
+
88
+ ### Tool calls
89
+
90
+ - [ ] Single tool call renders one line (`· tool · detail`).
91
+ - [ ] Two+ consecutive tool calls collapse to one row + `(Ctrl+O expand)`.
92
+ - [ ] Ctrl+O expands the most recent group with `│`/`└` branch markers
93
+ and only fires once per group.
94
+ - [ ] When text arrives between tool calls, the previous group freezes
95
+ into the log and a fresh group starts on the next tool call.
96
+
97
+ ### Agents footer
98
+
99
+ - [ ] Shows `Agents: none │ No target agents` when nothing's online.
100
+ - [ ] Shows `Agents: @x @y ... │ ↓ select target · ←/→ switch` otherwise.
101
+ - [ ] At narrow widths, the row stays single-line, drops trailing chips
102
+ and emits ` +N more`.
103
+ - [ ] Down enters selection mode (first chip inverse). Left/Right cycle.
104
+ Up exits. Prompt prefix changes to `›@<name> ` when locked.
105
+
106
+ ### Smoke
107
+
108
+ - [ ] `node scripts/ucode-app-smoke.js` exits 0.
109
+ - [ ] `npx jest --silent` shows the pre-existing 5 OAuth failures only;
110
+ every ink suite passes.
111
+
112
+ ## Decision log
113
+
114
+ - **Don't fork ink.** Claude-code-fixed inlines a customised ink under
115
+ `src/ink/`; we don't need React 19 / ConcurrentRoot / IDE bridging,
116
+ so depending on the public `ink@5` keeps maintenance cost low.
117
+ - **Don't add JSX.** `React.createElement` keeps jest CJS happy and
118
+ avoids a build step. We can revisit if any single component grows
119
+ past ~600 lines and readability suffers.
120
+ - **Don't enable `--experimental-vm-modules` for jest.** The risk of
121
+ surprise ESM behaviour across the existing 1800-test suite is too
122
+ high. Render coverage stays in `scripts/*-smoke.js`; component logic
123
+ is exercised by pure-function tests.
124
+ - **Codex isn't a useful reference.** Its TUI is a Rust ratatui app
125
+ (`codex-rs/tui`), not React-based. The architectural principle worth
126
+ borrowing is its hard split between TUI and core protocol.
127
+
128
+ ## P2 dropped, folded into P3
129
+
130
+ The internal-agent view in chat is not an independent program. It is now
131
+ an Ink chat sub-mode, so the earlier plan to mount a separate subtree was
132
+ folded into P3.6.
133
+
134
+ ## P3 audit (chat TUI surface)
135
+
136
+ Source: the current Ink host in `src/ui/ink/ChatApp.js`, with
137
+ shared daemon and command helpers under `src/app/chat/`. Highlights:
138
+
139
+ ### Lifecycle
140
+ - Public entrypoint `runChat(projectRoot, { globalMode })` from
141
+ `src/app/chat/index.js`; it delegates directly to the Ink ChatApp.
142
+ - Runners injected via closures: `daemonCoordinator.send`,
143
+ `executeCommand`, `inputSubmitHandler.handleSubmit`,
144
+ `daemonMessageRouter.handleMessage`.
145
+
146
+ ### View state machine
147
+ - `dashboardView` ∈ `projects | agents | mode | provider | cron`
148
+ - `focusMode` ∈ `input | dashboard` — toggled by Tab and arrow keys
149
+ - `globalMode` (boolean) + `globalScope` ∈ `controller | project` —
150
+ `globalMode=true` enables a multi-project rail; Esc/Enter walk the
151
+ scope ladder.
152
+ - `enterDashboardMode()` / `exitDashboardMode()` are the two transition
153
+ points; `setGlobalScope()` runs an async, debounced project switch.
154
+
155
+ ### Input
156
+ - Submit accepts `@mention`, `@target`, `/command`, plain text and
157
+ numeric disambiguation (when `pending.disambiguate` is set).
158
+ - Editor keys cover Ctrl+A/E/B/F/D/H/K/U/W, Meta+B/F/D, arrows
159
+ (cursor + history when empty), Esc (3-layer: clear @target →
160
+ exit project scope → cancel input), bracketed paste, Tab (toggle
161
+ dashboard), PgUp/PgDn (scroll log).
162
+ - Completion fires on `/` and `@` with sources from command registry,
163
+ group templates, solo profiles and agent mentions; Up arrow jumps to
164
+ the latest suggestion.
165
+ - History is per-project, persisted to `input-history.jsonl` with
166
+ draft restoration on project switch.
167
+
168
+ ### Daemon stack
169
+ - `daemonTransport` owns the socket path and retry policy.
170
+ - `daemonConnection` owns the queue + lifecycle (`connect`, `send`,
171
+ `requestStatus`, `close`, `markExit`, `switchConnection`,
172
+ `getState`).
173
+ - `daemonCoordinator` orchestrates project switches with a serialised
174
+ Promise chain.
175
+ - `daemonMessageRouter.handleMessage(msg)` is a stateless dispatcher
176
+ that turns daemon responses into log appends, dashboard updates, PTY
177
+ writes and transient agent state changes.
178
+ - `daemonReconnect.restartDaemonFlow()` provides a per-project lock for
179
+ daemon restarts.
180
+
181
+ ### Shared helpers
182
+ - `cronScheduler` — `/cron start|stop|list` + the cron dashboard view.
183
+ - `settingsController` — launch mode / agent provider,
184
+ with daemon restart on mode/provider change. `autoResume` stays config/command-driven.
185
+ - `transientAgentState` — TTL-bounded `working / waiting_input /
186
+ blocked` markers per agent.
187
+ - `projectCloseController` — `requestCloseProject(index)` runs daemon
188
+ stop + project switch.
189
+ - `agentDirectory` — agent label resolution + window clamping (pure).
190
+ - `internalAgentLogHistory` — bus log replay for internal agents.
191
+
192
+ ### Internal-agent sub-view
193
+ - Ink owns the agent sub-view inside ChatApp.
194
+ - PTY mirror mode uses `agentSockets.connectOutput/Input` and
195
+ `requestSnapshot`.
196
+ - Embedded bus mode keeps its own input value, cursor, log and status.
197
+ - `agentSockets.createAgentSockets` owns the PTY/bus socket
198
+ lifecycle.
199
+
200
+ ### Layout (Ink)
201
+ - `ChatApp` owns the chat surface as a React tree under
202
+ `src/ui/ink/`.
203
+ - Dashboard, log, status, completion, internal-agent panes and input are
204
+ rendered from React state with ink flex layout. No blessed widget geometry
205
+ or controller layer remains.
206
+
207
+ ### Commands
208
+ `/bus`, `/ctx`, `/daemon`, `/doctor`, `/cron`, `/group`, `/init`,
209
+ `/open`, `/launch`, `/project`, `/role`, `/solo`, `/settings`,
210
+ `/help`, plus nested subcommands (`/bus activate|list|rename|send|status`,
211
+ `/cron start|list|stop`, etc.). `commandExecutor.executeCommand(text)`
212
+ is the single dispatch point. `parseCommand(text)`, `parseAtTarget(text)`
213
+ and `shouldEchoCommandInChat(text)` are pure.
214
+
215
+ ### Cross-cutting
216
+ - `text.js`: `escapeBlessed`, `stripBlessedTags`, `stripAnsi`,
217
+ `truncateAnsi`, `decodeEscapedNewlines`. The blessed-tag helpers are now
218
+ compatibility shims for older daemon/router log strings; ink call sites
219
+ strip or normalize those tags before rendering. The ANSI helpers stay
220
+ relevant.
221
+ - `rawKeyMap.keyToRaw(ch, key)`: converts ink-style key events to
222
+ PTY bytes for the agent view. Stays as-is.
223
+ - `transport.js`: `startDaemon`, `stopDaemon`, `connectWithRetry`.
224
+ Framework-agnostic, no migration needed.
225
+
226
+ ### Removal notes
227
+ 1. **Entry**: `src/app/chat/index.js` delegates directly to `runChatInk()`.
228
+ 2. **ucode**: `src/code/tui.js` is a compatibility export wrapper around
229
+ `src/ui/format/` and `runUcodeInkTui()`.
230
+ 3. **Controllers**: the blessed widget controllers and their tests were
231
+ removed with the fallback path.
232
+ 4. **Markup**: old brace-tag helpers remain only where shared daemon/chat
233
+ helpers still emit or sanitize legacy log markup.
234
+
235
+ ### P3 phase plan
236
+
237
+ | Step | Goal | Status |
238
+ |---|---|---|
239
+ | P3.1 | This audit | ✅ |
240
+ | P3.2 | Ink-only `runChat()` entrypoint | ✅ |
241
+ | P3.3 | ChatApp shell (banner + log + input + status) | ✅ |
242
+ | P3.4 | Five dashboard views as React components | ✅ |
243
+ | P3.5 | Daemon connection + PROMPT/BUS_SEND wiring | ✅ |
244
+ | P3.6 | Raw-PTY internal agent view as a ChatApp mode | ✅ |
245
+ | P3.7 | Real-TTY checklist | ✅ |
246
+
247
+ ## P3 chat TUI — what's wired
248
+
249
+ | Feature | Status |
250
+ |---|---|
251
+ | Banner header (project + global mode + scope) | ✅ |
252
+ | Scrolling `<Static>` log (1000 line cap) | ✅ |
253
+ | Multiline input (P1 MultilineInput component) | ✅ |
254
+ | 5 dashboard views (projects/agents/mode/provider/cron) | ✅ |
255
+ | Tab toggles input/dashboard focus | ✅ |
256
+ | Up/Down history walk + agent selection mode | ✅ |
257
+ | Left/Right cycle agents while selected | ✅ |
258
+ | Spinner + phase status line | ✅ |
259
+ | Tool-merge state machine + Ctrl+O expand | ✅ |
260
+ | Daemon connect / send / status poll | ✅ |
261
+ | `PROMPT` for free text, `BUS_SEND` for `@target` | ✅ |
262
+ | `BUS_SEND_OK` / `RESPONSE` / `ERROR` / `STATUS` / `BUS` envelopes | ✅ |
263
+ | Raw PTY agent mirror (Enter on selected agent, Esc to leave) | ✅ |
264
+ | `daemonMessageRouter` (markdown streams, transient state, bus subview) | ✅ |
265
+ | `commandExecutor` full slash-command dispatch (`/cron`, `/group`, `/role`, `/settings` …) | ✅ |
266
+ | Slash + `@` autocomplete | ✅ |
267
+ | Input history persisted file load/save | ✅ |
268
+ | Cron dashboard actions | ✅ |
269
+ | Settings dashboard actions (launch mode, provider) | ✅ |
270
+
271
+ ## Real-TTY checklist for chat
272
+
273
+ ```sh
274
+ ./bin/ufoo.js chat # project mode
275
+ ./bin/ufoo.js chat --global # global controller mode
276
+ ```
277
+
278
+ ### Layout
279
+ - [ ] Banner shows the active project + global/project tag.
280
+ - [ ] `Agents:` footer, status line above input, log fills the rest.
281
+ - [ ] Resize the terminal — input frame and footer stay single-line.
282
+
283
+ ### Input + history
284
+ - [ ] Type, Enter sends a `PROMPT`. Backspace, arrows, Ctrl+A/E etc.
285
+ behave the same as the ucode editor.
286
+ - [ ] Up/Down on an empty draft walks the in-memory history.
287
+ - [ ] `\` + Enter inserts a newline.
288
+ - [ ] Esc clears any active agent selection.
289
+
290
+ ### Daemon
291
+ - [ ] On launch the daemon spawns automatically (look for the socket
292
+ under `~/.ufoo` or your project's `.ufoo`).
293
+ - [ ] Send a free-text message — daemon answers, status flips to
294
+ `Working on task...` and back to Ready.
295
+ - [ ] Type `@<agent> hi` (or select with arrow keys) — message is
296
+ sent via `BUS_SEND`, ack arrives as `✓ Message delivered`.
297
+
298
+ ### Agents footer
299
+ - [ ] Tab into the dashboard, ↓ enters agent selection (first item
300
+ inverse), ←/→ cycles, ↑ exits.
301
+ - [ ] Enter on a selected agent attaches to its PTY (cleared screen +
302
+ scroll region + bottom hint bar). Esc returns to chat without
303
+ losing the previous draft or log.
304
+
305
+ ### Tool-merge
306
+ - [ ] Daemon-driven tool calls collapse and `(Ctrl+O expand)` works
307
+ the same as ucode.
308
+
309
+ ### Smoke
310
+ - [ ] `node scripts/chat-app-smoke.js` exits 0.
311
+ - [ ] `node scripts/ucode-app-smoke.js` exits 0.
312
+ - [ ] `npx jest --silent` shows the pre-existing 5 OAuth failures
313
+ only; every ink suite passes.
314
+
315
+ ## P4 close-out
316
+
317
+ - **STATUS handler fix** — chat now reads `msg.data.active` /
318
+ `msg.data.active_meta` / `msg.data.cron.tasks` so the agents and cron
319
+ counts in the footer actually update.
320
+ - **Slash command dispatch** — `createCommandExecutor` is wired in Ink
321
+ with daemon stop/start/restart, cron IPC, project switching and agent
322
+ activation callbacks.
323
+ - **Input history persistence** — `<projectRoot>/.ufoo/chat/input-history.jsonl`
324
+ is loaded on mount and appended on every submit.
325
+ - **Daemon message routing** — Ink routes daemon envelopes through
326
+ `daemonMessageRouter`, including BUS phase status, transient states,
327
+ pending delivery markers, streams, close/launch refreshes and loop
328
+ summary dashboard display.
329
+ - **Inline completion popup** — `/<prefix>` matches commands from
330
+ `COMMAND_REGISTRY`; `@<prefix>` matches the live agents list. Tab
331
+ accepts the top suggestion. Pure helper `buildCompletions` lives in
332
+ `src/ui/format` with full jest coverage.
333
+ - **Exit hygiene** — Ctrl+C now flushes `\x1b[2J\x1b[H` so the shell
334
+ prompt comes back to a clean screen instead of sitting under the
335
+ final ink frame; `runUcodeInkTui` returns `{ code: 0 }` so
336
+ `agent.js`'s `process.exit(res.code)` no longer crashes.