u-foo 2.3.32 → 2.4.1

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 +63 -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}/daemonReconnect.js +3 -0
  54. package/src/{chat → app/chat}/dashboardView.js +2 -1
  55. package/src/app/chat/index.js +6 -0
  56. package/src/{chat → app/chat}/inputSubmitHandler.js +4 -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}/transport.js +1 -1
  64. package/src/{cli → app/cli}/ctxCoreCommands.js +3 -3
  65. package/src/{doctor/index.js → app/cli/features/doctor.js} +1 -1
  66. package/src/{init/index.js → app/cli/features/init.js} +14 -32
  67. package/src/{cli → app/cli}/groupCoreCommands.js +2 -2
  68. package/src/app/cli/index.js +9 -0
  69. package/src/{cli → app/cli}/onlineCoreCommands.js +5 -5
  70. package/src/{cli.js → app/cli/run.js} +59 -57
  71. package/src/app/index.js +6 -0
  72. package/src/code/agent.js +10 -9
  73. package/src/code/index.js +2 -0
  74. package/src/code/launcher/index.js +9 -0
  75. package/src/{agent → code/launcher}/ucode.js +7 -8
  76. package/src/{agent → code/launcher}/ucodeBootstrap.js +3 -3
  77. package/src/{agent → code/launcher}/ucodeBuild.js +2 -2
  78. package/src/{agent → code/launcher}/ucodeDoctor.js +2 -2
  79. package/src/{agent → code/launcher}/ucodeRuntimeConfig.js +1 -2
  80. package/src/code/nativeRunner.js +4 -4
  81. package/src/code/tui.js +3 -1454
  82. package/src/config.js +15 -2
  83. package/src/{bus → coordination/bus}/activate.js +2 -2
  84. package/src/{bus → coordination/bus}/daemon.js +15 -5
  85. package/src/coordination/bus/envelope.js +173 -0
  86. package/src/{bus → coordination/bus}/index.js +7 -3
  87. package/src/{bus → coordination/bus}/inject.js +11 -3
  88. package/src/{bus → coordination/bus}/message.js +1 -1
  89. package/src/coordination/bus/messageMeta.js +130 -0
  90. package/src/coordination/bus/promptEnvelope.js +65 -0
  91. package/src/{bus → coordination/bus}/shake.js +1 -1
  92. package/src/{bus → coordination/bus}/store.js +3 -3
  93. package/src/{bus → coordination/bus}/subscriber.js +2 -2
  94. package/src/{bus → coordination/bus}/utils.js +2 -2
  95. package/src/{history → coordination/history}/inputTimeline.js +5 -5
  96. package/src/coordination/index.js +10 -0
  97. package/src/{memory → coordination/memory}/historySearch.js +1 -1
  98. package/src/{memory → coordination/memory}/index.js +3 -3
  99. package/src/{report → coordination/report}/store.js +2 -2
  100. package/src/{status → coordination/status}/index.js +3 -3
  101. package/src/online/bridge.js +2 -2
  102. package/src/{controller → orchestration/controller}/flags.js +1 -1
  103. package/src/{controller → orchestration/controller}/gateRouter.js +1 -1
  104. package/src/orchestration/controller/index.js +10 -0
  105. package/src/{controller → orchestration/controller}/shadowGuard.js +1 -1
  106. package/src/orchestration/groups/bootstrap.js +3 -0
  107. package/src/orchestration/groups/index.js +10 -0
  108. package/src/orchestration/groups/promptProfiles.js +3 -0
  109. package/src/{group → orchestration/groups}/templates.js +1 -1
  110. package/src/{group → orchestration/groups}/validateTemplate.js +1 -1
  111. package/src/orchestration/index.js +7 -0
  112. package/src/orchestration/solo/index.js +3 -0
  113. package/src/{daemon → runtime/daemon}/agentProcessManager.js +1 -1
  114. package/src/{daemon → runtime/daemon}/cronOps.js +3 -2
  115. package/src/{daemon → runtime/daemon}/groupOrchestrator.js +26 -9
  116. package/src/{daemon → runtime/daemon}/index.js +105 -53
  117. package/src/{daemon → runtime/daemon}/ipcServer.js +1 -1
  118. package/src/{daemon → runtime/daemon}/nicknameScope.js +6 -3
  119. package/src/{daemon → runtime/daemon}/ops.js +48 -61
  120. package/src/{daemon → runtime/daemon}/promptLoop.js +1 -1
  121. package/src/{daemon → runtime/daemon}/promptRequest.js +7 -7
  122. package/src/runtime/daemon/providerSessions.js +230 -0
  123. package/src/{daemon → runtime/daemon}/reporting.js +4 -4
  124. package/src/{daemon → runtime/daemon}/run.js +4 -4
  125. package/src/{daemon → runtime/daemon}/soloBootstrap.js +7 -7
  126. package/src/{daemon → runtime/daemon}/status.js +5 -5
  127. package/src/runtime/index.js +10 -0
  128. package/src/{projects → runtime/projects}/registry.js +1 -1
  129. package/src/{terminal → runtime/terminal}/adapterRouter.js +0 -10
  130. package/src/{terminal → runtime/terminal}/adapters/internalAdapter.js +0 -4
  131. package/src/tools/handlers/common.js +1 -1
  132. package/src/tools/handlers/listAgents.js +1 -1
  133. package/src/tools/handlers/memory.js +3 -3
  134. package/src/tools/handlers/readBusSummary.js +1 -1
  135. package/src/tools/handlers/readOpenDecisions.js +1 -1
  136. package/src/tools/handlers/readProjectRegistry.js +1 -1
  137. package/src/tools/handlers/readPromptHistory.js +2 -2
  138. package/src/tools/schemaFixtures.js +1 -1
  139. package/src/ui/MIGRATION.md +42 -88
  140. package/src/ui/format/index.js +5 -28
  141. package/src/ui/index.js +1 -1
  142. package/src/ui/{components → ink}/ChatApp.js +812 -88
  143. package/src/ui/ink/DashboardBar.js +685 -0
  144. package/src/ui/{components → ink}/MultilineInput.js +230 -5
  145. package/src/ui/{components → ink}/UcodeApp.js +16 -7
  146. package/src/ui/{components → ink}/agentMirror.js +24 -19
  147. package/src/ui/{components → ink}/chatReducer.js +29 -7
  148. package/src/bus/messageMeta.js +0 -52
  149. package/src/chat/agentViewController.js +0 -1072
  150. package/src/chat/chatLogController.js +0 -138
  151. package/src/chat/completionController.js +0 -533
  152. package/src/chat/dashboardKeyController.js +0 -533
  153. package/src/chat/index.js +0 -2222
  154. package/src/chat/inputHistoryController.js +0 -135
  155. package/src/chat/inputListenerController.js +0 -470
  156. package/src/chat/layout.js +0 -186
  157. package/src/chat/pasteController.js +0 -81
  158. package/src/chat/statusLineController.js +0 -223
  159. package/src/chat/streamTracker.js +0 -156
  160. package/src/code/config +0 -0
  161. package/src/daemon/providerSessions.js +0 -488
  162. package/src/terminal/adapters/internalPtyAdapter.js +0 -42
  163. package/src/ui/components/DashboardBar.js +0 -417
  164. /package/src/{code/prompts → agents/prompts/native}/actions.js +0 -0
  165. /package/src/{code/prompts → agents/prompts/native}/efficiency.js +0 -0
  166. /package/src/{code/prompts → agents/prompts/native}/environment.js +0 -0
  167. /package/src/{code/prompts → agents/prompts/native}/identity.js +0 -0
  168. /package/src/{code/prompts → agents/prompts/native}/safety.js +0 -0
  169. /package/src/{code/prompts → agents/prompts/native}/sections.js +0 -0
  170. /package/src/{code/prompts → agents/prompts/native}/system.js +0 -0
  171. /package/src/{code/prompts → agents/prompts/native}/tasks.js +0 -0
  172. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/bash.js +0 -0
  173. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/edit.js +0 -0
  174. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/read.js +0 -0
  175. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/write.js +0 -0
  176. /package/src/{code/prompts → agents/prompts/native}/ufoo.js +0 -0
  177. /package/src/{group → agents/prompts}/promptProfiles.js +0 -0
  178. /package/src/{agent → agents/providers}/claudeEventTranslator.js +0 -0
  179. /package/src/{agent → agents/providers}/claudeOauthTokenReader.js +0 -0
  180. /package/src/{agent → agents/providers}/claudeSessionFiles.js +0 -0
  181. /package/src/{agent → agents/providers}/codexEventTranslator.js +0 -0
  182. /package/src/{agent → agents/providers}/credentials/claude.js +0 -0
  183. /package/src/{agent → agents/providers}/credentials/codex.js +0 -0
  184. /package/src/{agent → agents/providers}/credentials/index.js +0 -0
  185. /package/src/{chat → app/chat}/agentBar.js +0 -0
  186. /package/src/{chat → app/chat}/agentDirectory.js +0 -0
  187. /package/src/{chat → app/chat}/cronScheduler.js +0 -0
  188. /package/src/{chat → app/chat}/daemonCoordinator.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
@@ -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 = "") {
@@ -106,6 +112,7 @@ async function withCapturedConsole(capture, fn) {
106
112
  }
107
113
 
108
114
  function createCommandExecutor(options = {}) {
115
+ const hasRestartDaemon = typeof options.restartDaemon === "function";
109
116
  const {
110
117
  projectRoot,
111
118
  getActiveProjectRoot = () => projectRoot,
@@ -147,6 +154,7 @@ function createCommandExecutor(options = {}) {
147
154
  resolveTerminalApp = defaultResolveTerminalApp,
148
155
  sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
149
156
  schedule = (fn, ms) => setTimeout(fn, ms),
157
+ clearLog = null,
150
158
  } = options;
151
159
 
152
160
  if (!projectRoot) {
@@ -248,6 +256,18 @@ function createCommandExecutor(options = {}) {
248
256
  }
249
257
 
250
258
  if (subcommand === "restart") {
259
+ if (hasRestartDaemon) {
260
+ statusMsg("{gray-fg}⚙{/gray-fg} Restarting daemon...");
261
+ await restartDaemon(targetRoot);
262
+ await sleep(500);
263
+ if (isDaemonRunning(targetRoot)) {
264
+ statusMsg("{gray-fg}✓{/gray-fg} Daemon restarted");
265
+ } else {
266
+ statusMsg("{gray-fg}✗{/gray-fg} Failed to restart daemon");
267
+ }
268
+ return;
269
+ }
270
+
251
271
  statusMsg("{gray-fg}⚙{/gray-fg} Restarting daemon...");
252
272
  stopDaemon(targetRoot, { source: "chat-command:/daemon restart" });
253
273
  await sleep(500);
@@ -479,14 +499,17 @@ function createCommandExecutor(options = {}) {
479
499
  if (args.length === 0) {
480
500
  logMessage(
481
501
  "error",
482
- "{white-fg}✗{/white-fg} Usage: /launch <claude|codex|ucode> [nickname=<name>] [profile=<id>] [count=<n>] [scope=inplace|window]"
502
+ "{white-fg}✗{/white-fg} Usage: /launch <claude|codex|agy|ucode> [nickname=<name>] [profile=<id>] [count=<n>] [scope=inplace|window]"
483
503
  );
484
504
  return;
485
505
  }
486
506
 
487
- const agentType = String(args[0] || "").trim().toLowerCase();
488
- if (agentType !== "claude" && agentType !== "codex" && agentType !== "ucode") {
489
- logMessage("error", "{white-fg}✗{/white-fg} Unknown agent type. Use: claude, codex, or ucode");
507
+ const agentTypeInput = String(args[0] || "").trim().toLowerCase();
508
+ // Accept friendly aliases the same way `ufoo launch` does in cli.js.
509
+ let agentType = agentTypeInput;
510
+ if (agentTypeInput === "antigravity" || agentTypeInput === "uagy") agentType = "agy";
511
+ if (agentType !== "claude" && agentType !== "codex" && agentType !== "agy" && agentType !== "ucode") {
512
+ logMessage("error", "{white-fg}✗{/white-fg} Unknown agent type. Use: claude, codex, agy, or ucode");
490
513
  return;
491
514
  }
492
515
  const normalizedAgent = agentType === "ucode" ? "ufoo" : agentType;
@@ -661,7 +684,7 @@ function createCommandExecutor(options = {}) {
661
684
 
662
685
  const profile = String(args[1] || "").trim();
663
686
  if (!profile) {
664
- logMessage("error", "{white-fg}✗{/white-fg} Usage: /solo run <prompt-profile> [agent=codex|claude|ucode] [nickname=<name>] [scope=inplace|window]");
687
+ logMessage("error", "{white-fg}✗{/white-fg} Usage: /solo run <prompt-profile> [agent=codex|claude|agy|ucode] [nickname=<name>] [scope=inplace|window]");
665
688
  return;
666
689
  }
667
690
 
@@ -1255,13 +1278,15 @@ function createCommandExecutor(options = {}) {
1255
1278
  logMessage("system", ` • provider: ${key}`);
1256
1279
  logMessage("system", ` • model: ${model || `(unset, recommended ${defaultAgentModelForProvider(provider)})`}`);
1257
1280
  logMessage("system", ` • defaults: codex=${SETTINGS_MODEL_DEFAULTS.agent.codex}, claude=${SETTINGS_MODEL_DEFAULTS.agent.claude}`);
1258
- logMessage("system", " • use: /settings agent set provider=<codex|claude> model=<id>");
1281
+ logMessage("system", " • use: /settings agent set provider=<codex|claude|agy> model=<id>");
1259
1282
  return;
1260
1283
  }
1261
1284
 
1262
- if (action === "codex" || action === "claude") {
1285
+ if (action === "codex" || action === "claude" || action === "agy") {
1263
1286
  const kv = parseKeyValueArgs(args.slice(1));
1264
- const provider = action === "claude" ? "claude-cli" : "codex-cli";
1287
+ const provider = action === "claude"
1288
+ ? "claude-cli"
1289
+ : (action === "agy" ? "agy-cli" : "codex-cli");
1265
1290
  const model = String(kv.model || defaultAgentModelForProvider(provider)).trim();
1266
1291
  saveConfig(projectRoot, {
1267
1292
  agentProvider: provider,
@@ -1447,10 +1472,8 @@ function createCommandExecutor(options = {}) {
1447
1472
  }
1448
1473
 
1449
1474
  async function handleUfooCommand(args = []) {
1450
- // Handle /ufoo command (session marker from daemon)
1451
- // When daemon sends /ufoo <marker>, we should just check for pending messages
1475
+ // Legacy /ufoo <single-token> compatibility: check pending messages silently.
1452
1476
  if (args.length > 0) {
1453
- // This is a probe marker, check for pending messages
1454
1477
  const subscriberId = process.env.UFOO_SUBSCRIBER_ID;
1455
1478
  if (subscriberId) {
1456
1479
  try {
@@ -1464,7 +1487,7 @@ function createCommandExecutor(options = {}) {
1464
1487
  // Ignore errors when checking messages
1465
1488
  }
1466
1489
  }
1467
- // Don't log anything else for probe markers
1490
+ // Do not log anything else for legacy single-token input.
1468
1491
  return;
1469
1492
  }
1470
1493
 
@@ -1566,6 +1589,20 @@ function createCommandExecutor(options = {}) {
1566
1589
  const { command, args } = parsed;
1567
1590
 
1568
1591
  switch (command) {
1592
+ case "clear":
1593
+ if (typeof clearLog === "function") {
1594
+ clearLog();
1595
+ } else {
1596
+ logMessage("error", "{white-fg}✗{/white-fg} /clear is not available in this view");
1597
+ }
1598
+ return true;
1599
+ case "multi":
1600
+ if (typeof options.toggleMultiWindow === "function") {
1601
+ options.toggleMultiWindow();
1602
+ } else {
1603
+ logMessage("error", "{white-fg}✗{/white-fg} Multi-window mode is not available");
1604
+ }
1605
+ return true;
1569
1606
  case "doctor":
1570
1607
  await handleDoctorCommand();
1571
1608
  return true;