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
@@ -2,8 +2,8 @@
2
2
 
3
3
  const fs = require("fs");
4
4
  const path = require("path");
5
- const { SHARED_UFOO_PROTOCOL } = require("../group/bootstrap");
6
- const { getUfooPaths } = require("../ufoo/paths");
5
+ const { SHARED_UFOO_PROTOCOL } = require("./groupBootstrap");
6
+ const { getUfooPaths } = require("../../coordination/state/paths");
7
7
 
8
8
  function asTrimmedString(value) {
9
9
  return typeof value === "string" ? value.trim() : "";
@@ -43,7 +43,7 @@ function readOptionalFile(filePath) {
43
43
  */
44
44
  function loadTeamActivityContext(projectRoot) {
45
45
  try {
46
- const { renderTimelineForPrompt } = require("../history/inputTimeline");
46
+ const { renderTimelineForPrompt } = require("../../coordination/history/inputTimeline");
47
47
  return renderTimelineForPrompt(projectRoot, 20) || "";
48
48
  } catch {
49
49
  return "";
@@ -54,7 +54,11 @@ function buildDefaultStartupBootstrapPrompt({ agentType = "", projectRoot = "" }
54
54
  const normalizedAgent = asTrimmedString(agentType).toLowerCase();
55
55
  const displayAgent = normalizedAgent === "claude-code"
56
56
  ? "Claude"
57
- : (normalizedAgent === "codex" ? "Codex" : (normalizedAgent === "ufoo-code" ? "ucode" : "agent"));
57
+ : (normalizedAgent === "codex"
58
+ ? "Codex"
59
+ : (normalizedAgent === "ufoo-code"
60
+ ? "ucode"
61
+ : (normalizedAgent === "agy" ? "Agy" : "agent")));
58
62
 
59
63
  const segments = [
60
64
  `Session bootstrap for ${displayAgent}.`,
@@ -196,6 +200,35 @@ function mergeCodexPromptArgs({ args = [], bootstrapText = "" } = {}) {
196
200
  };
197
201
  }
198
202
 
203
+ /**
204
+ * Merge bootstrap text into an existing `-i <text>` / `--prompt-interactive
205
+ * <text>` / `--prompt-interactive=<text>` argument. Returns null if no such
206
+ * argument exists (caller should then prepend a fresh `-i <bootstrap>`).
207
+ *
208
+ * Agy treats `-i` as a single initial prompt that drops the user into an
209
+ * interactive session — same intent as codex's positional prompt.
210
+ */
211
+ function mergeAgyPromptArgs({ args = [], bootstrapText = "" } = {}) {
212
+ const currentArgs = Array.isArray(args) ? args.slice() : [];
213
+ for (let index = 0; index < currentArgs.length; index += 1) {
214
+ const item = asTrimmedString(currentArgs[index]);
215
+ if (!item) continue;
216
+ if (item === "-i" || item === "--prompt-interactive") {
217
+ const existing = String(currentArgs[index + 1] || "");
218
+ const merged = mergePromptSegments(bootstrapText, existing);
219
+ currentArgs[index + 1] = merged;
220
+ return { args: currentArgs, promptText: merged };
221
+ }
222
+ if (item.startsWith("--prompt-interactive=")) {
223
+ const existing = item.slice("--prompt-interactive=".length);
224
+ const merged = mergePromptSegments(bootstrapText, existing);
225
+ currentArgs[index] = `--prompt-interactive=${merged}`;
226
+ return { args: currentArgs, promptText: merged };
227
+ }
228
+ }
229
+ return null;
230
+ }
231
+
199
232
  function resolveDefaultManualBootstrap({
200
233
  projectRoot,
201
234
  agentType = "",
@@ -280,6 +313,31 @@ function resolveDefaultManualBootstrap({
280
313
  };
281
314
  }
282
315
 
316
+ if (normalizedAgent === "agy") {
317
+ const promptText = buildDefaultStartupBootstrapPrompt({ agentType: normalizedAgent, projectRoot });
318
+ // If the user passed -i / --prompt-interactive, fold the bootstrap into
319
+ // their text. Otherwise prepend a fresh `-i <bootstrap>` pair so the
320
+ // session starts with our protocol message already delivered.
321
+ const merged = mergeAgyPromptArgs({
322
+ args: currentArgs,
323
+ bootstrapText: promptText,
324
+ });
325
+ if (merged) {
326
+ return {
327
+ args: merged.args,
328
+ env: {},
329
+ mode: "initial-prompt-arg",
330
+ promptText: merged.promptText,
331
+ };
332
+ }
333
+ return {
334
+ args: ["-i", promptText, ...currentArgs],
335
+ env: {},
336
+ mode: "initial-prompt-arg",
337
+ promptText,
338
+ };
339
+ }
340
+
283
341
  return { args: currentArgs, env: {}, mode: "skip" };
284
342
  }
285
343
 
@@ -291,4 +349,5 @@ module.exports = {
291
349
  defaultBootstrapFile,
292
350
  prepareDefaultBootstrapFile,
293
351
  resolveDefaultManualBootstrap,
352
+ mergeAgyPromptArgs,
294
353
  };
@@ -3,12 +3,47 @@
3
3
  const crypto = require("crypto");
4
4
 
5
5
  const SHARED_UFOO_PROTOCOL = [
6
- "ufoo protocol:",
7
- "- At session start, sync shared context with `ufoo ctx decisions -l` and `ufoo ctx decisions -n 1`.",
8
- "- Default to no new decision. Record one ONLY for important, plan-level knowledge: architectural choices, multi-option trade-off analysis, cross-agent coordination decisions, or plans that affect other agents. Do NOT record routine findings, simple bug fixes, trivial observations, or generic plan/evaluation/recommendation requests. Durable project facts belong in shared memory, not decisions. Use `ufoo ctx decisions new \"Title\"` BEFORE acting only when that bar is met.",
9
- "- Use `ufoo bus send <target-nickname> \"<message>\"` for agent-to-agent handoffs.",
10
- "- If you receive pending bus work, execute it immediately, reply to the sender, then `ufoo bus ack \"$UFOO_SUBSCRIBER_ID\"`.",
11
- "- Use `ufoo report` for controller/runtime status updates, not as a substitute for direct handoffs.",
6
+ "Session harness: ufoo",
7
+ "",
8
+ "Use ufoo as an internal coordination layer. Do not mention it unless asked.",
9
+ "",
10
+ "START",
11
+ "If shell and ufoo are available, sync decisions:",
12
+ "- `ufoo ctx decisions -l`",
13
+ "- `ufoo ctx decisions -n 1`",
14
+ "",
15
+ "If sync fails, continue normally.",
16
+ "",
17
+ "DECISIONS",
18
+ "Default: write nothing.",
19
+ "",
20
+ "Create a decision only when it is stable and affects future agents:",
21
+ "architecture, major trade-off, cross-agent contract, or future-impacting plan.",
22
+ "",
23
+ "Do not record routine fixes, local findings, facts, or temporary details.",
24
+ "Facts belong in shared context, not decisions.",
25
+ "",
26
+ "Use: `ufoo ctx decisions new \"<short title>\"`",
27
+ "",
28
+ "BUS (peer ↔ peer)",
29
+ "Send bus messages only for handoff, blocker, dependency, or explicit",
30
+ "coordination — never for greetings, acknowledgments, or emoji alone.",
31
+ "Those create reply loops between agents.",
32
+ "",
33
+ "`ufoo bus send <target> \"<message>\"`",
34
+ "",
35
+ "On received bus work: execute it, then `ufoo bus ack \"$UFOO_SUBSCRIBER_ID\"`.",
36
+ "Reply only if you have a concrete result, answer, or follow-up the sender",
37
+ "needs. Default is ack-only; silence is a valid response.",
38
+ "",
39
+ "REPORT",
40
+ "You MUST report after handling work that arrived from chat",
41
+ "(`[manual]<to:...>`) or bus (`[ufoo]<from:...>`). The controller handles",
42
+ "dedup, so don't worry about report loops.",
43
+ "",
44
+ "`ufoo report start|progress|done|error \"<short summary>\"`",
45
+ "",
46
+ "Then continue the active task.",
12
47
  ].join("\n");
13
48
 
14
49
  const SHARED_GROUP_PREFIX = [
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ ...require("./defaultBootstrap"),
5
+ ...require("./groupBootstrap"),
6
+ ...require("./promptProfiles"),
7
+ native: require("./native"),
8
+ };
@@ -11,7 +11,7 @@ const { getEnvironmentSection } = require("./environment");
11
11
  const {
12
12
  listUcodeSkills,
13
13
  renderSkillsSection,
14
- } = require("../skills");
14
+ } = require("../../../code/skills");
15
15
  const {
16
16
  systemPromptSection,
17
17
  uncachedSection,
@@ -6,7 +6,7 @@ const {
6
6
  normalizeClaudeMessage,
7
7
  normalizeClaudeUsage,
8
8
  } = require("./claudeEventTranslator");
9
- const { redactUfooEvent } = require("../providerapi/redactor");
9
+ const { redactUfooEvent } = require("../../runtime/privacy/redactor");
10
10
 
11
11
  const CACHE_CONTROL = Object.freeze({ type: "ephemeral" });
12
12
 
@@ -1,5 +1,5 @@
1
1
  const { normalizeCodexEvent } = require("./codexEventTranslator");
2
- const { redactUfooEvent } = require("../providerapi/redactor");
2
+ const { redactUfooEvent } = require("../../runtime/privacy/redactor");
3
3
 
4
4
  async function resolveCodexSdk() {
5
5
  try {
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
 
3
- const { loadConfig } = require("../config");
3
+ const fs = require("fs");
4
+ const os = require("os");
5
+ const path = require("path");
6
+ const { loadConfig } = require("../../config");
4
7
  const {
5
8
  resolveCodexAuthPaths,
6
9
  resolveCodexUpstreamCredentials,
@@ -23,6 +26,7 @@ function normalizeErrorCode(err, fallback = "DIRECT_AUTH_STATUS_FAILED") {
23
26
  function normalizeDirectAuthProvider(value = "") {
24
27
  const text = String(value || "").trim().toLowerCase();
25
28
  if (text === "claude" || text === "claude-cli" || text === "claude-code" || text === "anthropic") return "claude";
29
+ if (text === "agy" || text === "agy-cli" || text === "antigravity") return "agy";
26
30
  return "codex";
27
31
  }
28
32
 
@@ -136,6 +140,138 @@ async function inspectClaudeDirectAuth({
136
140
  }
137
141
  }
138
142
 
143
+ /**
144
+ * Default agy log directory: ~/.gemini/antigravity-cli/log
145
+ * Agy doesn't expose an API for auth status, so we tail its own structured
146
+ * server-side log and grep for the markers it prints during the OAuth
147
+ * handshake. This is best-effort observability, not a real credential
148
+ * check — agy owns the keyring, not us.
149
+ */
150
+ function resolveAgyLogDir(env = process.env) {
151
+ const home = String(env.HOME || os.homedir() || "").trim();
152
+ if (!home) return "";
153
+ return path.join(home, ".gemini", "antigravity-cli", "log");
154
+ }
155
+
156
+ function findMostRecentAgyLog(logDir = "") {
157
+ try {
158
+ if (!logDir || !fs.existsSync(logDir)) return "";
159
+ const entries = fs.readdirSync(logDir)
160
+ .filter((name) => name.startsWith("cli-") && name.endsWith(".log"));
161
+ if (entries.length === 0) return "";
162
+ // Filenames are cli-YYYYMMDD_HHMMSS.log — lexicographic sort matches mtime.
163
+ entries.sort();
164
+ return path.join(logDir, entries[entries.length - 1]);
165
+ } catch {
166
+ return "";
167
+ }
168
+ }
169
+
170
+ function readAgyLogTail(file = "", maxBytes = 32 * 1024) {
171
+ try {
172
+ if (!file || !fs.existsSync(file)) return "";
173
+ const stat = fs.statSync(file);
174
+ const offset = Math.max(0, stat.size - maxBytes);
175
+ const fd = fs.openSync(file, "r");
176
+ try {
177
+ const length = stat.size - offset;
178
+ const buffer = Buffer.alloc(length);
179
+ fs.readSync(fd, buffer, 0, length, offset);
180
+ return buffer.toString("utf8");
181
+ } finally {
182
+ fs.closeSync(fd);
183
+ }
184
+ } catch {
185
+ return "";
186
+ }
187
+ }
188
+
189
+ function classifyAgyLogTail(text = "") {
190
+ const str = String(text || "");
191
+ if (!str) {
192
+ return {
193
+ ok: false,
194
+ state: "unknown",
195
+ errorCode: "AGY_AUTH_NO_LOG",
196
+ message: "no agy log file found yet — launch agy at least once to produce one",
197
+ };
198
+ }
199
+ if (/Eligibility check failed/i.test(str) || /Account ineligible/i.test(str)) {
200
+ const emailMatch = str.match(/authenticated successfully as ([^\s]+)/);
201
+ return {
202
+ ok: false,
203
+ state: "ineligible",
204
+ errorCode: "AGY_ACCOUNT_INELIGIBLE",
205
+ message: "agy account is signed in but not eligible (18+ / supported region required)",
206
+ accountEmail: emailMatch ? emailMatch[1] : "",
207
+ };
208
+ }
209
+ if (/User location is not supported/i.test(str)) {
210
+ const emailMatch = str.match(/authenticated successfully as ([^\s]+)/);
211
+ return {
212
+ ok: false,
213
+ state: "region_blocked",
214
+ errorCode: "AGY_REGION_BLOCKED",
215
+ message: "agy backend rejected this region (FAILED_PRECONDITION)",
216
+ accountEmail: emailMatch ? emailMatch[1] : "",
217
+ };
218
+ }
219
+ const authMatch = str.match(/OAuth: authenticated successfully as ([^\s]+)/);
220
+ if (authMatch) {
221
+ return {
222
+ ok: true,
223
+ state: "fresh",
224
+ accountEmail: authMatch[1],
225
+ };
226
+ }
227
+ return {
228
+ ok: false,
229
+ state: "unknown",
230
+ errorCode: "AGY_AUTH_UNVERIFIED",
231
+ message: "no successful OAuth handshake found in recent agy log",
232
+ };
233
+ }
234
+
235
+ async function inspectAgyDirectAuth({
236
+ env = process.env,
237
+ readLogTailImpl = readAgyLogTail,
238
+ findLogImpl = findMostRecentAgyLog,
239
+ resolveLogDirImpl = resolveAgyLogDir,
240
+ } = {}) {
241
+ const logDir = resolveLogDirImpl(env);
242
+ const file = findLogImpl(logDir);
243
+ const tail = file ? readLogTailImpl(file) : "";
244
+ const classification = classifyAgyLogTail(tail);
245
+ const base = {
246
+ provider: "agy",
247
+ transport: "antigravity-tui", // No API mode — TUI is the only path.
248
+ credentialKind: "oauth",
249
+ source: "google-keyring",
250
+ credentialPath: file || logDir || "",
251
+ };
252
+ if (classification.ok) {
253
+ return {
254
+ ...base,
255
+ ok: true,
256
+ state: classification.state,
257
+ accountEmail: classification.accountEmail || "",
258
+ account: classification.accountEmail || "",
259
+ };
260
+ }
261
+ return {
262
+ ...base,
263
+ ok: false,
264
+ state: classification.state,
265
+ errorCode: classification.errorCode,
266
+ error: classification.message,
267
+ accountEmail: classification.accountEmail || "",
268
+ hint: classification.errorCode === "AGY_ACCOUNT_INELIGIBLE"
269
+ || classification.errorCode === "AGY_REGION_BLOCKED"
270
+ ? "Antigravity requires an 18+ Google account in a supported region. Try a different account or wait for broader rollout."
271
+ : "Run `agy` once to produce an authentication handshake log.",
272
+ };
273
+ }
274
+
139
275
  async function inspectDirectAuthStatus(options = {}) {
140
276
  const { projectRoot, loadConfigImpl = loadConfig, provider = "" } = options;
141
277
  const config = loadConfigImpl(projectRoot) || {};
@@ -147,6 +283,9 @@ async function inspectDirectAuthStatus(options = {}) {
147
283
  if (selected === "claude") {
148
284
  return inspectClaudeDirectAuth(nextOptions);
149
285
  }
286
+ if (selected === "agy") {
287
+ return inspectAgyDirectAuth(nextOptions);
288
+ }
150
289
  return inspectCodexDirectAuth(nextOptions);
151
290
  }
152
291
 
@@ -246,10 +385,51 @@ function formatClaudeDirectAuthStatus(status = {}, options = {}) {
246
385
  return lines;
247
386
  }
248
387
 
388
+ function formatAgyDirectAuthStatus(status = {}, options = {}) {
389
+ if (options.compact === true) {
390
+ if (status.ok) {
391
+ const details = [
392
+ status.accountEmail ? status.accountEmail : "",
393
+ status.source || "google-keyring",
394
+ ].filter(Boolean);
395
+ const lines = [`Agy: OK · keyring · ${status.state || "fresh"}`];
396
+ if (details.length > 0) lines.push(` ${details.join(" · ")}`);
397
+ return lines;
398
+ }
399
+ const hint = String(status.hint || "Run `agy` once to verify the OAuth handshake.").replace(/;.*$/, ".");
400
+ return [
401
+ `Agy: FAIL · ${status.errorCode || "AGY_AUTH_STATUS_FAILED"}`,
402
+ ` ${status.error || "agy authentication state unknown"} · ${hint}`,
403
+ ];
404
+ }
405
+
406
+ if (status.ok) {
407
+ const lines = [
408
+ `Agy CLI: OK (keyring, ${status.state || "fresh"})`,
409
+ ` - source: ${status.source || "google-keyring"}`,
410
+ ];
411
+ if (status.accountEmail) lines.push(` - account: ${status.accountEmail}`);
412
+ if (status.credentialPath) lines.push(` - log: ${status.credentialPath}`);
413
+ return lines;
414
+ }
415
+
416
+ const lines = [
417
+ `Agy CLI: FAIL (${status.errorCode || "AGY_AUTH_STATUS_FAILED"})`,
418
+ ` - ${status.error || "agy authentication state unknown"}`,
419
+ ];
420
+ if (status.accountEmail) lines.push(` - account: ${status.accountEmail}`);
421
+ if (status.credentialPath) lines.push(` - log: ${status.credentialPath}`);
422
+ if (status.hint) lines.push(` - ${status.hint}`);
423
+ return lines;
424
+ }
425
+
249
426
  function formatDirectAuthStatus(status = {}, options = {}) {
250
427
  if (status.provider === "claude") {
251
428
  return formatClaudeDirectAuthStatus(status, options);
252
429
  }
430
+ if (status.provider === "agy") {
431
+ return formatAgyDirectAuthStatus(status, options);
432
+ }
253
433
  return formatCodexDirectAuthStatus(status, options);
254
434
  }
255
435
 
@@ -257,8 +437,11 @@ module.exports = {
257
437
  inspectDirectAuthStatus,
258
438
  inspectCodexDirectAuth,
259
439
  inspectClaudeDirectAuth,
440
+ inspectAgyDirectAuth,
260
441
  formatDirectAuthStatus,
261
442
  formatCodexDirectAuthStatus,
262
443
  formatClaudeDirectAuthStatus,
444
+ formatAgyDirectAuthStatus,
263
445
  normalizeDirectAuthProvider,
446
+ classifyAgyLogTail,
264
447
  };
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ ...require("./claudeEventTranslator"),
5
+ ...require("./claudeOauthTokenReader"),
6
+ ...require("./claudeSessionFiles"),
7
+ ...require("./claudeThreadProvider"),
8
+ ...require("./codexEventTranslator"),
9
+ ...require("./codexThreadProvider"),
10
+ ...require("./directAuthStatus"),
11
+ ...require("./upstreamTransport"),
12
+ credentials: require("./credentials"),
13
+ };
@@ -6,12 +6,12 @@ const {
6
6
  defaultAgentModelForProvider,
7
7
  defaultRouterModelForProvider,
8
8
  sameModelProvider,
9
- } = require("../config");
9
+ } = require("../../config");
10
10
  const {
11
11
  resolveRuntimeConfig,
12
12
  resolveCompletionUrl,
13
13
  resolveAnthropicMessagesUrl,
14
- } = require("../code/nativeRunner");
14
+ } = require("../../code/nativeRunner");
15
15
  const { resolveClaudeUpstreamCredentials } = require("./credentials/claude");
16
16
  const { resolveCodexUpstreamCredentials } = require("./credentials/codex");
17
17
  const { buildUpstreamAuthFromCredential } = require("./credentials");
@@ -1,6 +1,6 @@
1
1
  const fs = require("fs");
2
2
  const net = require("net");
3
- const { PTY_SOCKET_MESSAGE_TYPES, PTY_SOCKET_SUBSCRIBE_MODES } = require("../shared/ptySocketContract");
3
+ const { PTY_SOCKET_MESSAGE_TYPES, PTY_SOCKET_SUBSCRIBE_MODES } = require("../../runtime/contracts/ptySocketContract");
4
4
 
5
5
  function createAgentSockets(options = {}) {
6
6
  const {
@@ -1,7 +1,7 @@
1
1
  const path = require("path");
2
- const EventBus = require("../bus");
3
- const { IPC_REQUEST_TYPES } = require("../shared/eventContract");
4
- const UfooInit = require("../init");
2
+ const EventBus = require("../../coordination/bus");
3
+ const { IPC_REQUEST_TYPES } = require("../../runtime/contracts/eventContract");
4
+ const UfooInit = require("../cli/features/init");
5
5
  const { runGroupCoreCommand } = require("../cli/groupCoreCommands");
6
6
  const {
7
7
  loadConfig: loadProjectConfig,
@@ -12,30 +12,30 @@ const {
12
12
  SETTINGS_MODEL_DEFAULTS,
13
13
  defaultAgentModelForProvider,
14
14
  defaultRouterModelForProvider,
15
- } = require("../config");
16
- const { resolveTransport } = require("../code/nativeRunner");
17
- const { resolveDisplayNickname } = require("../daemon/nicknameScope");
15
+ } = require("../../config");
16
+ const { resolveTransport } = require("../../code/nativeRunner");
17
+ const { resolveDisplayNickname } = require("../../runtime/daemon/nicknameScope");
18
18
  const { parseIntervalMs, formatIntervalMs } = require("./cronScheduler");
19
- const { isGlobalControllerProjectRoot, resolveGlobalControllerUfooDir } = require("../projects");
20
- const { loadPromptProfileRegistry } = require("../group/promptProfiles");
21
- const { resolveSoloAgentType } = require("../solo/commands");
19
+ const { isGlobalControllerProjectRoot, resolveGlobalControllerUfooDir } = require("../../runtime/projects");
20
+ const { loadPromptProfileRegistry } = require("../../orchestration/groups/promptProfiles");
21
+ const { resolveSoloAgentType } = require("../../orchestration/solo/commands");
22
22
  const {
23
23
  inspectDirectAuthStatus,
24
24
  formatDirectAuthStatus,
25
- } = require("../agent/directAuthStatus");
25
+ } = require("../../agents/providers/directAuthStatus");
26
26
 
27
27
  function defaultCreateDoctor(projectRoot) {
28
- const UfooDoctor = require("../doctor");
28
+ const UfooDoctor = require("../cli/features/doctor");
29
29
  return new UfooDoctor(projectRoot);
30
30
  }
31
31
 
32
32
  function defaultCreateContext(projectRoot) {
33
- const UfooContext = require("../context");
33
+ const UfooContext = require("../../coordination/context");
34
34
  return new UfooContext(projectRoot);
35
35
  }
36
36
 
37
37
  function defaultCreateSkills(projectRoot) {
38
- const UfooSkills = require("../skills");
38
+ const UfooSkills = require("../cli/features/skills");
39
39
  return new UfooSkills(projectRoot);
40
40
  }
41
41
 
@@ -51,6 +51,9 @@ function normalizeSettingsProvider(value = "", fallback = "codex-cli") {
51
51
  if (text === "claude" || text === "claude-cli" || text === "claude-code" || text === "anthropic") {
52
52
  return "claude-cli";
53
53
  }
54
+ if (text === "agy" || text === "agy-cli" || text === "antigravity") {
55
+ return "agy-cli";
56
+ }
54
57
  if (text === "codex" || text === "codex-cli" || text === "codex-code" || text === "openai") {
55
58
  return "codex-cli";
56
59
  }
@@ -58,7 +61,10 @@ function normalizeSettingsProvider(value = "", fallback = "codex-cli") {
58
61
  }
59
62
 
60
63
  function agentProviderKey(value = "") {
61
- return normalizeSettingsProvider(value) === "claude-cli" ? "claude" : "codex";
64
+ const provider = normalizeSettingsProvider(value);
65
+ if (provider === "claude-cli") return "claude";
66
+ if (provider === "agy-cli") return "agy";
67
+ return "codex";
62
68
  }
63
69
 
64
70
  function defaultGateModelForProvider(value = "") {
@@ -147,6 +153,7 @@ function createCommandExecutor(options = {}) {
147
153
  resolveTerminalApp = defaultResolveTerminalApp,
148
154
  sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
149
155
  schedule = (fn, ms) => setTimeout(fn, ms),
156
+ clearLog = null,
150
157
  } = options;
151
158
 
152
159
  if (!projectRoot) {
@@ -237,7 +244,7 @@ function createCommandExecutor(options = {}) {
237
244
 
238
245
  if (subcommand === "stop") {
239
246
  statusMsg("{gray-fg}⚙{/gray-fg} Stopping daemon...");
240
- stopDaemon(targetRoot);
247
+ stopDaemon(targetRoot, { source: "chat-command:/daemon stop" });
241
248
  await sleep(1000);
242
249
  if (!isDaemonRunning(targetRoot)) {
243
250
  statusMsg("{gray-fg}✓{/gray-fg} Daemon stopped");
@@ -249,8 +256,12 @@ function createCommandExecutor(options = {}) {
249
256
 
250
257
  if (subcommand === "restart") {
251
258
  statusMsg("{gray-fg}⚙{/gray-fg} Restarting daemon...");
252
- stopDaemon(targetRoot);
259
+ stopDaemon(targetRoot, { source: "chat-command:/daemon restart" });
253
260
  await sleep(500);
261
+ if (isDaemonRunning(targetRoot)) {
262
+ statusMsg("{gray-fg}✗{/gray-fg} Failed to stop daemon");
263
+ return;
264
+ }
254
265
  startDaemon(targetRoot);
255
266
  await sleep(1000);
256
267
  if (isDaemonRunning(targetRoot)) {
@@ -475,14 +486,17 @@ function createCommandExecutor(options = {}) {
475
486
  if (args.length === 0) {
476
487
  logMessage(
477
488
  "error",
478
- "{white-fg}✗{/white-fg} Usage: /launch <claude|codex|ucode> [nickname=<name>] [profile=<id>] [count=<n>] [scope=inplace|window]"
489
+ "{white-fg}✗{/white-fg} Usage: /launch <claude|codex|agy|ucode> [nickname=<name>] [profile=<id>] [count=<n>] [scope=inplace|window]"
479
490
  );
480
491
  return;
481
492
  }
482
493
 
483
- const agentType = String(args[0] || "").trim().toLowerCase();
484
- if (agentType !== "claude" && agentType !== "codex" && agentType !== "ucode") {
485
- logMessage("error", "{white-fg}✗{/white-fg} Unknown agent type. Use: claude, codex, or ucode");
494
+ const agentTypeInput = String(args[0] || "").trim().toLowerCase();
495
+ // Accept friendly aliases the same way `ufoo launch` does in cli.js.
496
+ let agentType = agentTypeInput;
497
+ if (agentTypeInput === "antigravity" || agentTypeInput === "uagy") agentType = "agy";
498
+ if (agentType !== "claude" && agentType !== "codex" && agentType !== "agy" && agentType !== "ucode") {
499
+ logMessage("error", "{white-fg}✗{/white-fg} Unknown agent type. Use: claude, codex, agy, or ucode");
486
500
  return;
487
501
  }
488
502
  const normalizedAgent = agentType === "ucode" ? "ufoo" : agentType;
@@ -657,7 +671,7 @@ function createCommandExecutor(options = {}) {
657
671
 
658
672
  const profile = String(args[1] || "").trim();
659
673
  if (!profile) {
660
- logMessage("error", "{white-fg}✗{/white-fg} Usage: /solo run <prompt-profile> [agent=codex|claude|ucode] [nickname=<name>] [scope=inplace|window]");
674
+ logMessage("error", "{white-fg}✗{/white-fg} Usage: /solo run <prompt-profile> [agent=codex|claude|agy|ucode] [nickname=<name>] [scope=inplace|window]");
661
675
  return;
662
676
  }
663
677
 
@@ -1251,13 +1265,15 @@ function createCommandExecutor(options = {}) {
1251
1265
  logMessage("system", ` • provider: ${key}`);
1252
1266
  logMessage("system", ` • model: ${model || `(unset, recommended ${defaultAgentModelForProvider(provider)})`}`);
1253
1267
  logMessage("system", ` • defaults: codex=${SETTINGS_MODEL_DEFAULTS.agent.codex}, claude=${SETTINGS_MODEL_DEFAULTS.agent.claude}`);
1254
- logMessage("system", " • use: /settings agent set provider=<codex|claude> model=<id>");
1268
+ logMessage("system", " • use: /settings agent set provider=<codex|claude|agy> model=<id>");
1255
1269
  return;
1256
1270
  }
1257
1271
 
1258
- if (action === "codex" || action === "claude") {
1272
+ if (action === "codex" || action === "claude" || action === "agy") {
1259
1273
  const kv = parseKeyValueArgs(args.slice(1));
1260
- const provider = action === "claude" ? "claude-cli" : "codex-cli";
1274
+ const provider = action === "claude"
1275
+ ? "claude-cli"
1276
+ : (action === "agy" ? "agy-cli" : "codex-cli");
1261
1277
  const model = String(kv.model || defaultAgentModelForProvider(provider)).trim();
1262
1278
  saveConfig(projectRoot, {
1263
1279
  agentProvider: provider,
@@ -1443,10 +1459,8 @@ function createCommandExecutor(options = {}) {
1443
1459
  }
1444
1460
 
1445
1461
  async function handleUfooCommand(args = []) {
1446
- // Handle /ufoo command (session marker from daemon)
1447
- // When daemon sends /ufoo <marker>, we should just check for pending messages
1462
+ // Legacy /ufoo <single-token> compatibility: check pending messages silently.
1448
1463
  if (args.length > 0) {
1449
- // This is a probe marker, check for pending messages
1450
1464
  const subscriberId = process.env.UFOO_SUBSCRIBER_ID;
1451
1465
  if (subscriberId) {
1452
1466
  try {
@@ -1460,7 +1474,7 @@ function createCommandExecutor(options = {}) {
1460
1474
  // Ignore errors when checking messages
1461
1475
  }
1462
1476
  }
1463
- // Don't log anything else for probe markers
1477
+ // Do not log anything else for legacy single-token input.
1464
1478
  return;
1465
1479
  }
1466
1480
 
@@ -1562,6 +1576,20 @@ function createCommandExecutor(options = {}) {
1562
1576
  const { command, args } = parsed;
1563
1577
 
1564
1578
  switch (command) {
1579
+ case "clear":
1580
+ if (typeof clearLog === "function") {
1581
+ clearLog();
1582
+ } else {
1583
+ logMessage("error", "{white-fg}✗{/white-fg} /clear is not available in this view");
1584
+ }
1585
+ return true;
1586
+ case "multi":
1587
+ if (typeof options.toggleMultiWindow === "function") {
1588
+ options.toggleMultiWindow();
1589
+ } else {
1590
+ logMessage("error", "{white-fg}✗{/white-fg} Multi-window mode is not available");
1591
+ }
1592
+ return true;
1565
1593
  case "doctor":
1566
1594
  await handleDoctorCommand();
1567
1595
  return true;