u-foo 2.3.32 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/README.md +157 -213
  2. package/README.zh-CN.md +151 -197
  3. package/SKILLS/ufoo/SKILL.md +8 -8
  4. package/bin/uagy.js +69 -0
  5. package/bin/uclaude.js +2 -2
  6. package/bin/ucode.js +4 -4
  7. package/bin/ucodex.js +2 -2
  8. package/bin/ufoo.js +5 -23
  9. package/modules/AGENTS.template.md +1 -1
  10. package/modules/bus/SKILLS/ubus/SKILL.md +35 -10
  11. package/package.json +5 -5
  12. package/scripts/chat-app-smoke.js +1 -1
  13. package/scripts/global-chat-switch-benchmark.js +5 -5
  14. package/scripts/ink-demo.js +1 -1
  15. package/scripts/ink-smoke.js +1 -1
  16. package/scripts/ucode-app-smoke.js +1 -1
  17. package/src/{agent → agents/activity}/activityDetector.js +39 -2
  18. package/src/{agent → agents/activity}/activityStatePublisher.js +1 -1
  19. package/src/{agent → agents/activity}/activityStateWriter.js +2 -2
  20. package/src/{agent → agents/activity}/activityTracker.js +1 -1
  21. package/src/agents/activity/index.js +8 -0
  22. package/src/{agent → agents/controller}/controllerToolExecutor.js +4 -4
  23. package/src/agents/controller/index.js +8 -0
  24. package/src/{agent → agents/controller}/loopObservability.js +2 -2
  25. package/src/{agent → agents/controller}/loopRuntime.js +1 -1
  26. package/src/{agent → agents/controller}/ufooAgent.js +9 -9
  27. package/src/agents/index.js +10 -0
  28. package/src/agents/internal/index.js +3 -0
  29. package/src/{agent → agents/internal}/internalRunner.js +45 -22
  30. package/src/agents/launch/agyConversation.js +159 -0
  31. package/src/agents/launch/index.js +12 -0
  32. package/src/{agent → agents/launch}/launchEnvironment.js +2 -3
  33. package/src/{agent → agents/launch}/launcher.js +64 -21
  34. package/src/{agent → agents/launch}/notifier.js +23 -12
  35. package/src/{agent → agents/launch}/ptyRunner.js +44 -12
  36. package/src/{agent → agents/launch}/ptyWrapper.js +2 -2
  37. package/src/{agent → agents/launch}/publisherRouting.js +1 -1
  38. package/src/{agent → agents/launch}/readyDetector.js +23 -0
  39. package/src/{agent → agents/prompts}/defaultBootstrap.js +63 -4
  40. package/src/{group/bootstrap.js → agents/prompts/groupBootstrap.js} +41 -6
  41. package/src/agents/prompts/index.js +8 -0
  42. package/src/{code/prompts → agents/prompts/native}/index.js +1 -1
  43. package/src/{agent → agents/providers}/claudeThreadProvider.js +1 -1
  44. package/src/{agent → agents/providers}/codexThreadProvider.js +1 -1
  45. package/src/{agent → agents/providers}/directAuthStatus.js +184 -1
  46. package/src/agents/providers/index.js +13 -0
  47. package/src/{agent → agents/providers}/upstreamTransport.js +2 -2
  48. package/src/{chat → app/chat}/agentSockets.js +1 -1
  49. package/src/{chat → app/chat}/commandExecutor.js +50 -26
  50. package/src/{chat → app/chat}/commands.js +119 -5
  51. package/src/{chat → app/chat}/daemonConnection.js +1 -1
  52. package/src/{chat → app/chat}/daemonMessageRouter.js +45 -3
  53. package/src/{chat → app/chat}/dashboardView.js +2 -1
  54. package/src/app/chat/index.js +6 -0
  55. package/src/{chat → app/chat}/inputSubmitHandler.js +4 -13
  56. package/src/{chat → app/chat}/internalAgentLogHistory.js +1 -1
  57. package/src/app/chat/multiWindow/index.js +268 -0
  58. package/src/app/chat/multiWindow/paneLayout.js +84 -0
  59. package/src/app/chat/multiWindow/paneManager.js +299 -0
  60. package/src/app/chat/multiWindow/renderer.js +384 -0
  61. package/src/app/chat/multiWindow/virtualTerminal.js +327 -0
  62. package/src/{chat → app/chat}/transport.js +1 -1
  63. package/src/{cli → app/cli}/ctxCoreCommands.js +3 -3
  64. package/src/{doctor/index.js → app/cli/features/doctor.js} +1 -1
  65. package/src/{init/index.js → app/cli/features/init.js} +14 -32
  66. package/src/{cli → app/cli}/groupCoreCommands.js +2 -2
  67. package/src/app/cli/index.js +9 -0
  68. package/src/{cli → app/cli}/onlineCoreCommands.js +5 -5
  69. package/src/{cli.js → app/cli/run.js} +59 -57
  70. package/src/app/index.js +6 -0
  71. package/src/code/agent.js +10 -9
  72. package/src/code/index.js +2 -0
  73. package/src/code/launcher/index.js +9 -0
  74. package/src/{agent → code/launcher}/ucode.js +7 -8
  75. package/src/{agent → code/launcher}/ucodeBootstrap.js +3 -3
  76. package/src/{agent → code/launcher}/ucodeBuild.js +2 -2
  77. package/src/{agent → code/launcher}/ucodeDoctor.js +2 -2
  78. package/src/{agent → code/launcher}/ucodeRuntimeConfig.js +1 -2
  79. package/src/code/nativeRunner.js +4 -4
  80. package/src/code/tui.js +3 -1454
  81. package/src/config.js +15 -2
  82. package/src/{bus → coordination/bus}/activate.js +2 -2
  83. package/src/{bus → coordination/bus}/daemon.js +15 -5
  84. package/src/coordination/bus/envelope.js +173 -0
  85. package/src/{bus → coordination/bus}/index.js +7 -3
  86. package/src/{bus → coordination/bus}/inject.js +11 -3
  87. package/src/{bus → coordination/bus}/message.js +1 -1
  88. package/src/coordination/bus/messageMeta.js +130 -0
  89. package/src/coordination/bus/promptEnvelope.js +65 -0
  90. package/src/{bus → coordination/bus}/shake.js +1 -1
  91. package/src/{bus → coordination/bus}/store.js +3 -3
  92. package/src/{bus → coordination/bus}/subscriber.js +2 -2
  93. package/src/{bus → coordination/bus}/utils.js +2 -2
  94. package/src/{history → coordination/history}/inputTimeline.js +5 -5
  95. package/src/coordination/index.js +10 -0
  96. package/src/{memory → coordination/memory}/historySearch.js +1 -1
  97. package/src/{memory → coordination/memory}/index.js +3 -3
  98. package/src/{report → coordination/report}/store.js +2 -2
  99. package/src/{status → coordination/status}/index.js +3 -3
  100. package/src/online/bridge.js +2 -2
  101. package/src/{controller → orchestration/controller}/flags.js +1 -1
  102. package/src/{controller → orchestration/controller}/gateRouter.js +1 -1
  103. package/src/orchestration/controller/index.js +10 -0
  104. package/src/{controller → orchestration/controller}/shadowGuard.js +1 -1
  105. package/src/orchestration/groups/bootstrap.js +3 -0
  106. package/src/orchestration/groups/index.js +10 -0
  107. package/src/orchestration/groups/promptProfiles.js +3 -0
  108. package/src/{group → orchestration/groups}/templates.js +1 -1
  109. package/src/{group → orchestration/groups}/validateTemplate.js +1 -1
  110. package/src/orchestration/index.js +7 -0
  111. package/src/orchestration/solo/index.js +3 -0
  112. package/src/{daemon → runtime/daemon}/agentProcessManager.js +1 -1
  113. package/src/{daemon → runtime/daemon}/cronOps.js +3 -2
  114. package/src/{daemon → runtime/daemon}/groupOrchestrator.js +26 -9
  115. package/src/{daemon → runtime/daemon}/index.js +105 -53
  116. package/src/{daemon → runtime/daemon}/ipcServer.js +1 -1
  117. package/src/{daemon → runtime/daemon}/nicknameScope.js +6 -3
  118. package/src/{daemon → runtime/daemon}/ops.js +48 -61
  119. package/src/{daemon → runtime/daemon}/promptLoop.js +1 -1
  120. package/src/{daemon → runtime/daemon}/promptRequest.js +7 -7
  121. package/src/runtime/daemon/providerSessions.js +230 -0
  122. package/src/{daemon → runtime/daemon}/reporting.js +4 -4
  123. package/src/{daemon → runtime/daemon}/run.js +4 -4
  124. package/src/{daemon → runtime/daemon}/soloBootstrap.js +7 -7
  125. package/src/{daemon → runtime/daemon}/status.js +5 -5
  126. package/src/runtime/index.js +10 -0
  127. package/src/{projects → runtime/projects}/registry.js +1 -1
  128. package/src/{terminal → runtime/terminal}/adapterRouter.js +0 -10
  129. package/src/{terminal → runtime/terminal}/adapters/internalAdapter.js +0 -4
  130. package/src/tools/handlers/common.js +1 -1
  131. package/src/tools/handlers/listAgents.js +1 -1
  132. package/src/tools/handlers/memory.js +3 -3
  133. package/src/tools/handlers/readBusSummary.js +1 -1
  134. package/src/tools/handlers/readOpenDecisions.js +1 -1
  135. package/src/tools/handlers/readProjectRegistry.js +1 -1
  136. package/src/tools/handlers/readPromptHistory.js +2 -2
  137. package/src/tools/schemaFixtures.js +1 -1
  138. package/src/ui/MIGRATION.md +42 -88
  139. package/src/ui/format/index.js +5 -28
  140. package/src/ui/index.js +1 -1
  141. package/src/ui/{components → ink}/ChatApp.js +812 -88
  142. package/src/ui/ink/DashboardBar.js +685 -0
  143. package/src/ui/{components → ink}/MultilineInput.js +230 -5
  144. package/src/ui/{components → ink}/UcodeApp.js +16 -7
  145. package/src/ui/{components → ink}/agentMirror.js +24 -19
  146. package/src/ui/{components → ink}/chatReducer.js +29 -7
  147. package/src/bus/messageMeta.js +0 -52
  148. package/src/chat/agentViewController.js +0 -1072
  149. package/src/chat/chatLogController.js +0 -138
  150. package/src/chat/completionController.js +0 -533
  151. package/src/chat/dashboardKeyController.js +0 -533
  152. package/src/chat/index.js +0 -2222
  153. package/src/chat/inputHistoryController.js +0 -135
  154. package/src/chat/inputListenerController.js +0 -470
  155. package/src/chat/layout.js +0 -186
  156. package/src/chat/pasteController.js +0 -81
  157. package/src/chat/statusLineController.js +0 -223
  158. package/src/chat/streamTracker.js +0 -156
  159. package/src/code/config +0 -0
  160. package/src/daemon/providerSessions.js +0 -488
  161. package/src/terminal/adapters/internalPtyAdapter.js +0 -42
  162. package/src/ui/components/DashboardBar.js +0 -417
  163. /package/src/{code/prompts → agents/prompts/native}/actions.js +0 -0
  164. /package/src/{code/prompts → agents/prompts/native}/efficiency.js +0 -0
  165. /package/src/{code/prompts → agents/prompts/native}/environment.js +0 -0
  166. /package/src/{code/prompts → agents/prompts/native}/identity.js +0 -0
  167. /package/src/{code/prompts → agents/prompts/native}/safety.js +0 -0
  168. /package/src/{code/prompts → agents/prompts/native}/sections.js +0 -0
  169. /package/src/{code/prompts → agents/prompts/native}/system.js +0 -0
  170. /package/src/{code/prompts → agents/prompts/native}/tasks.js +0 -0
  171. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/bash.js +0 -0
  172. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/edit.js +0 -0
  173. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/read.js +0 -0
  174. /package/src/{code/prompts → agents/prompts/native}/toolDescriptions/write.js +0 -0
  175. /package/src/{code/prompts → agents/prompts/native}/ufoo.js +0 -0
  176. /package/src/{group → agents/prompts}/promptProfiles.js +0 -0
  177. /package/src/{agent → agents/providers}/claudeEventTranslator.js +0 -0
  178. /package/src/{agent → agents/providers}/claudeOauthTokenReader.js +0 -0
  179. /package/src/{agent → agents/providers}/claudeSessionFiles.js +0 -0
  180. /package/src/{agent → agents/providers}/codexEventTranslator.js +0 -0
  181. /package/src/{agent → agents/providers}/credentials/claude.js +0 -0
  182. /package/src/{agent → agents/providers}/credentials/codex.js +0 -0
  183. /package/src/{agent → agents/providers}/credentials/index.js +0 -0
  184. /package/src/{chat → app/chat}/agentBar.js +0 -0
  185. /package/src/{chat → app/chat}/agentDirectory.js +0 -0
  186. /package/src/{chat → app/chat}/cronScheduler.js +0 -0
  187. /package/src/{chat → app/chat}/daemonCoordinator.js +0 -0
  188. /package/src/{chat → app/chat}/daemonReconnect.js +0 -0
  189. /package/src/{chat → app/chat}/daemonTransport.js +0 -0
  190. /package/src/{chat → app/chat}/daemonTransportDefaults.js +0 -0
  191. /package/src/{chat → app/chat}/inputMath.js +0 -0
  192. /package/src/{chat → app/chat}/projectCloseController.js +0 -0
  193. /package/src/{chat → app/chat}/rawKeyMap.js +0 -0
  194. /package/src/{chat → app/chat}/settingsController.js +0 -0
  195. /package/src/{chat → app/chat}/shellCommand.js +0 -0
  196. /package/src/{chat → app/chat}/text.js +0 -0
  197. /package/src/{chat → app/chat}/transientAgentState.js +0 -0
  198. /package/src/{cli → app/cli}/busCoreCommands.js +0 -0
  199. /package/src/{skills/index.js → app/cli/features/skills.js} +0 -0
  200. /package/src/{bus → coordination/bus}/nickname.js +0 -0
  201. /package/src/{bus → coordination/bus}/queue.js +0 -0
  202. /package/src/{context → coordination/context}/decisions.js +0 -0
  203. /package/src/{context → coordination/context}/doctor.js +0 -0
  204. /package/src/{context → coordination/context}/index.js +0 -0
  205. /package/src/{context → coordination/context}/sync.js +0 -0
  206. /package/src/{ufoo → coordination/state}/agentRegistryDiagnostics.js +0 -0
  207. /package/src/{ufoo → coordination/state}/agentsStore.js +0 -0
  208. /package/src/{ufoo → coordination/state}/paths.js +0 -0
  209. /package/src/{controller → orchestration/controller}/launchRouting.js +0 -0
  210. /package/src/{controller → orchestration/controller}/routerFastPath.js +0 -0
  211. /package/src/{controller → orchestration/controller}/routerFinalize.js +0 -0
  212. /package/src/{group → orchestration/groups}/diagram.js +0 -0
  213. /package/src/{group → orchestration/groups}/templateValidation.js +0 -0
  214. /package/src/{solo → orchestration/solo}/commands.js +0 -0
  215. /package/src/{shared → runtime/contracts}/eventContract.js +0 -0
  216. /package/src/{shared → runtime/contracts}/ptySocketContract.js +0 -0
  217. /package/src/{providerapi → runtime/privacy}/redactor.js +0 -0
  218. /package/src/{providerapi → runtime/privacy}/shadowDiff.js +0 -0
  219. /package/src/{utils → runtime/process}/nodeExecutable.js +0 -0
  220. /package/src/{projects → runtime/projects}/identity.js +0 -0
  221. /package/src/{projects → runtime/projects}/index.js +0 -0
  222. /package/src/{projects → runtime/projects}/projectId.js +0 -0
  223. /package/src/{projects → runtime/projects}/runtimes.js +0 -0
  224. /package/src/{terminal → runtime/terminal}/adapterContract.js +0 -0
  225. /package/src/{terminal → runtime/terminal}/adapters/externalAdapter.js +0 -0
  226. /package/src/{terminal → runtime/terminal}/adapters/hostAdapter.js +0 -0
  227. /package/src/{terminal → runtime/terminal}/adapters/internalQueueAdapter.js +0 -0
  228. /package/src/{terminal → runtime/terminal}/adapters/terminalAdapter.js +0 -0
  229. /package/src/{terminal → runtime/terminal}/adapters/tmuxAdapter.js +0 -0
  230. /package/src/{terminal → runtime/terminal}/detect.js +0 -0
  231. /package/src/{terminal → runtime/terminal}/index.js +0 -0
  232. /package/src/{terminal → runtime/terminal}/iterm2.js +0 -0
  233. /package/src/{utils → ui/format}/banner.js +0 -0
  234. /package/src/{shared → ui/format}/markdownRenderer.js +0 -0
  235. /package/src/ui/{components → ink}/InkDemo.js +0 -0
package/src/config.js CHANGED
@@ -8,10 +8,16 @@ const SETTINGS_MODEL_DEFAULTS = Object.freeze({
8
8
  agent: Object.freeze({
9
9
  codex: "gpt-5.5",
10
10
  claude: "opus-4.7",
11
+ // agy (Antigravity CLI) only supports model selection via in-REPL
12
+ // `/model` slash command, which persists across launches. There is no
13
+ // command-line flag for model, so the value here is a placeholder for
14
+ // display; we never pass it on the agy command line.
15
+ agy: "",
11
16
  }),
12
17
  router: Object.freeze({
13
18
  codex: "gpt-5.3-codex-spark",
14
19
  claude: "sonnet-4.7",
20
+ agy: "",
15
21
  }),
16
22
  });
17
23
 
@@ -42,7 +48,6 @@ const DEFAULT_UCODE_CONFIG = {
42
48
  function normalizeLaunchMode(value) {
43
49
  if (value === "auto") return "auto";
44
50
  if (value === "internal") return "internal";
45
- if (value === "internal-pty") return "internal-pty";
46
51
  if (value === "tmux") return "tmux";
47
52
  if (value === "terminal") return "terminal";
48
53
  if (value === "host") return "host";
@@ -51,12 +56,14 @@ function normalizeLaunchMode(value) {
51
56
 
52
57
  function normalizeAgentProvider(value) {
53
58
  if (value === "claude-cli") return "claude-cli";
59
+ if (value === "agy-cli" || value === "agy" || value === "antigravity") return "agy-cli";
54
60
  return "codex-cli";
55
61
  }
56
62
 
57
63
  function providerKey(value = "") {
58
64
  const text = String(value || "").trim().toLowerCase();
59
65
  if (text === "claude" || text === "claude-cli" || text === "claude-code" || text === "anthropic") return "claude";
66
+ if (text === "agy" || text === "agy-cli" || text === "antigravity") return "agy";
60
67
  return "codex";
61
68
  }
62
69
 
@@ -69,7 +76,12 @@ function defaultAgentModelForProvider(value = "") {
69
76
  }
70
77
 
71
78
  function defaultRouterProviderForAgentProvider(value = "") {
72
- return providerKey(value) === "claude" ? "claude" : "codex";
79
+ const key = providerKey(value);
80
+ if (key === "claude") return "claude";
81
+ // agy has no router-model API; fall back to codex (the controller still
82
+ // routes via codex/claude regardless of which agent provider runs).
83
+ if (key === "agy") return "codex";
84
+ return "codex";
73
85
  }
74
86
 
75
87
  function defaultRouterModelForProvider(value = "") {
@@ -256,6 +268,7 @@ module.exports = {
256
268
  saveGlobalUcodeConfig,
257
269
  normalizeLaunchMode,
258
270
  normalizeAgentProvider,
271
+ providerKey,
259
272
  sameModelProvider,
260
273
  defaultAgentModelForProvider,
261
274
  defaultRouterProviderForAgentProvider,
@@ -1,7 +1,7 @@
1
1
  const fs = require("fs");
2
- const { getUfooPaths } = require("../ufoo/paths");
2
+ const { getUfooPaths } = require("../state/paths");
3
3
  const { spawn, spawnSync } = require("child_process");
4
- const { createTerminalAdapterRouter } = require("../terminal/adapterRouter");
4
+ const { createTerminalAdapterRouter } = require("../../runtime/terminal/adapterRouter");
5
5
 
6
6
  /**
7
7
  * 激活指定 agent 的终端
@@ -4,8 +4,15 @@ const { readJSON, writeJSON, isPidAlive, isAgentPidAlive, isMetaActive, ensureDi
4
4
  const Injector = require("./inject");
5
5
  const QueueManager = require("./queue");
6
6
  const MessageManager = require("./message");
7
- const { createTerminalAdapterRouter } = require("../terminal/adapterRouter");
8
- const { INJECTION_MODES, getInjectionModeFromEvent } = require("./messageMeta");
7
+ const { createTerminalAdapterRouter } = require("../../runtime/terminal/adapterRouter");
8
+ const {
9
+ INJECTION_MODES,
10
+ getInjectionModeFromEvent,
11
+ } = require("./messageMeta");
12
+ const {
13
+ buildPromptInjectionText,
14
+ shouldRenderPromptEnvelope,
15
+ } = require("./promptEnvelope");
9
16
 
10
17
  function isBusyActivityState(value = "") {
11
18
  const state = String(value || "").trim().toLowerCase();
@@ -128,7 +135,7 @@ class BusDaemon {
128
135
  const child = spawn(
129
136
  process.execPath,
130
137
  [
131
- path.join(__dirname, "..", "..", "bin", "ufoo.js"),
138
+ path.join(__dirname, "..", "..", "..", "bin", "ufoo.js"),
132
139
  "bus",
133
140
  "daemon",
134
141
  "--interval",
@@ -293,7 +300,7 @@ class BusDaemon {
293
300
  const launchMode = meta?.launch_mode || "";
294
301
  // Delivery ownership:
295
302
  // - notifier/injector: terminal/tmux
296
- // - internal queue loop: internal/internal-pty
303
+ // - internal queue loop: internal
297
304
  // Bus daemon only handles legacy/unknown launch modes.
298
305
  const adapter = this.adapterRouter.getAdapter({ launchMode, agentId: subscriber, meta });
299
306
  const { supportsNotifierInjector, supportsInternalQueueLoop } = adapter.capabilities;
@@ -348,8 +355,9 @@ class BusDaemon {
348
355
  continue;
349
356
  }
350
357
  try {
358
+ const injectionText = buildPromptInjectionText(evt, subscriber, busData.agents || {});
351
359
  // eslint-disable-next-line no-await-in-loop
352
- await this.injector.inject(subscriber, String(evt.data.message));
360
+ await this.injector.inject(subscriber, injectionText);
353
361
  deliveredCount += 1;
354
362
  currentActivityState = "working";
355
363
  this.lastWorkingAt.set(subscriber, Date.now());
@@ -523,3 +531,5 @@ class BusDaemon {
523
531
  }
524
532
 
525
533
  module.exports = BusDaemon;
534
+ module.exports.buildPromptInjectionText = buildPromptInjectionText;
535
+ module.exports.shouldRenderPromptEnvelope = shouldRenderPromptEnvelope;
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+
3
+ const ACTION_TAGS = ["reply", "report", "fyi"];
4
+ const ACTION_TAG_SET = new Set(ACTION_TAGS);
5
+ const MUTUALLY_EXCLUSIVE_WITH_FYI = new Set(["reply", "report"]);
6
+
7
+ const TAG_RENDER_ORDER = ["reply", "report", "fyi"];
8
+ const MAX_TAG_COUNT = 8;
9
+
10
+ const TASK_ID_PATTERN = /^[A-Za-z0-9_.\-]+$/;
11
+ const TASK_ID_MAX_LENGTH = 64;
12
+
13
+ function asTrimmedString(value) {
14
+ return typeof value === "string" ? value.trim() : "";
15
+ }
16
+
17
+ function normalizeTagList(input) {
18
+ if (input == null) return [];
19
+ const list = Array.isArray(input) ? input : String(input).split(/[\s,]+/);
20
+ const seen = new Set();
21
+ const out = [];
22
+ for (const item of list) {
23
+ const tag = asTrimmedString(item).toLowerCase();
24
+ if (!tag) continue;
25
+ if (seen.has(tag)) continue;
26
+ seen.add(tag);
27
+ out.push(tag);
28
+ }
29
+ return out;
30
+ }
31
+
32
+ function validateTags(tags) {
33
+ if (tags.length > MAX_TAG_COUNT) {
34
+ throw new Error(`Too many tags (max ${MAX_TAG_COUNT}): ${tags.join(", ")}`);
35
+ }
36
+ for (const tag of tags) {
37
+ if (!ACTION_TAG_SET.has(tag)) {
38
+ throw new Error(`Unknown tag "${tag}". Allowed action tags: ${ACTION_TAGS.join(", ")}`);
39
+ }
40
+ }
41
+ if (tags.includes("fyi")) {
42
+ const conflicts = tags.filter((tag) => MUTUALLY_EXCLUSIVE_WITH_FYI.has(tag));
43
+ if (conflicts.length > 0) {
44
+ throw new Error(`[fyi] is mutually exclusive with [${conflicts.join("], [")}]`);
45
+ }
46
+ }
47
+ }
48
+
49
+ function validateTaskId(taskId) {
50
+ const id = asTrimmedString(taskId);
51
+ if (!id) return "";
52
+ if (id.length > TASK_ID_MAX_LENGTH) {
53
+ throw new Error(`task_id too long (max ${TASK_ID_MAX_LENGTH}): "${id}"`);
54
+ }
55
+ if (!TASK_ID_PATTERN.test(id)) {
56
+ throw new Error(`task_id contains invalid characters (allowed: A-Z a-z 0-9 _ . -): "${id}"`);
57
+ }
58
+ return id;
59
+ }
60
+
61
+ function sortTagsForRender(tags) {
62
+ const order = new Map(TAG_RENDER_ORDER.map((tag, index) => [tag, index]));
63
+ return tags.slice().sort((a, b) => {
64
+ const ai = order.has(a) ? order.get(a) : TAG_RENDER_ORDER.length;
65
+ const bi = order.has(b) ? order.get(b) : TAG_RENDER_ORDER.length;
66
+ if (ai !== bi) return ai - bi;
67
+ return a.localeCompare(b);
68
+ });
69
+ }
70
+
71
+ function parseTagsFromOptions(options = {}) {
72
+ const opts = options && typeof options === "object" ? options : {};
73
+ const collected = [];
74
+ if (opts.reply) collected.push("reply");
75
+ if (opts.report) collected.push("report");
76
+ if (opts.fyi) collected.push("fyi");
77
+
78
+ if (Array.isArray(opts.tags)) {
79
+ for (const tag of opts.tags) {
80
+ const value = asTrimmedString(tag).toLowerCase();
81
+ if (value) collected.push(value);
82
+ }
83
+ } else if (typeof opts.tags === "string") {
84
+ for (const tag of opts.tags.split(/[\s,]+/)) {
85
+ const value = asTrimmedString(tag).toLowerCase();
86
+ if (value) collected.push(value);
87
+ }
88
+ }
89
+
90
+ const tags = normalizeTagList(collected);
91
+ validateTags(tags);
92
+
93
+ const taskId = validateTaskId(opts.taskId || opts.task_id || opts.task);
94
+ const reportTo = asTrimmedString(opts.reportTo || opts.report_to);
95
+
96
+ return { tags, taskId, reportTo };
97
+ }
98
+
99
+ function formatTagList({ tags = [], taskId = "" } = {}) {
100
+ const segments = [];
101
+ for (const tag of sortTagsForRender(tags)) {
102
+ segments.push(`[${tag}]`);
103
+ }
104
+ const id = asTrimmedString(taskId);
105
+ if (id) segments.push(`[task:${id}]`);
106
+ return segments.join(" ");
107
+ }
108
+
109
+ function buildPartyLabel({ id = "", nickname = "" } = {}) {
110
+ const trimmedId = asTrimmedString(id);
111
+ const trimmedNickname = asTrimmedString(nickname);
112
+ if (!trimmedId && !trimmedNickname) return "";
113
+ if (trimmedId && trimmedNickname && trimmedId !== trimmedNickname) {
114
+ return `${trimmedId}(${trimmedNickname})`;
115
+ }
116
+ return trimmedId || trimmedNickname;
117
+ }
118
+
119
+ function renderHeader({
120
+ kind = "bus",
121
+ fromId = "",
122
+ fromNickname = "",
123
+ toId = "",
124
+ toNickname = "",
125
+ tags = [],
126
+ taskId = "",
127
+ } = {}) {
128
+ let prefix;
129
+ let partyLabel;
130
+ if (kind === "manual") {
131
+ prefix = "[manual]<to:";
132
+ partyLabel = buildPartyLabel({ id: toId, nickname: toNickname });
133
+ } else {
134
+ prefix = "[ufoo]<from:";
135
+ partyLabel = buildPartyLabel({ id: fromId, nickname: fromNickname });
136
+ }
137
+ if (!partyLabel) partyLabel = "unknown";
138
+
139
+ const tagSegment = formatTagList({ tags, taskId });
140
+ const head = `${prefix}${partyLabel}>`;
141
+ return tagSegment ? `${head} ${tagSegment}` : head;
142
+ }
143
+
144
+ function renderEnvelope({
145
+ kind = "bus",
146
+ fromId = "",
147
+ fromNickname = "",
148
+ toId = "",
149
+ toNickname = "",
150
+ tags = [],
151
+ taskId = "",
152
+ message = "",
153
+ } = {}) {
154
+ const header = renderHeader({ kind, fromId, fromNickname, toId, toNickname, tags, taskId });
155
+ const body = String(message || "");
156
+ return body ? `${header}\n${body}` : header;
157
+ }
158
+
159
+ module.exports = {
160
+ ACTION_TAGS,
161
+ TAG_RENDER_ORDER,
162
+ MAX_TAG_COUNT,
163
+ TASK_ID_PATTERN,
164
+ TASK_ID_MAX_LENGTH,
165
+ normalizeTagList,
166
+ validateTags,
167
+ validateTaskId,
168
+ sortTagsForRender,
169
+ parseTagsFromOptions,
170
+ formatTagList,
171
+ renderHeader,
172
+ renderEnvelope,
173
+ };
@@ -16,7 +16,7 @@ const {
16
16
  sleep,
17
17
  } = require("./utils");
18
18
  const { shakeTerminalByTty } = require("./shake");
19
- const { resolveDisplayNickname } = require("../daemon/nicknameScope");
19
+ const { resolveDisplayNickname } = require("../../runtime/daemon/nicknameScope");
20
20
  const QueueManager = require("./queue");
21
21
  const SubscriberManager = require("./subscriber");
22
22
  const MessageManager = require("./message");
@@ -319,7 +319,11 @@ class EventBus {
319
319
 
320
320
  try {
321
321
  const eventName = options.event || "message";
322
- const data = options.data || { message };
322
+ const data = options.data && typeof options.data === "object"
323
+ ? { ...options.data }
324
+ : { message };
325
+ if (options.rawInject === true) data.raw_inject = true;
326
+ if (options.envelope === false) data.envelope = false;
323
327
  const result = eventName === "message"
324
328
  ? await this.messageManager.send(target, message, publisher, {
325
329
  data,
@@ -639,7 +643,7 @@ class EventBus {
639
643
  ensureDir(logDir);
640
644
 
641
645
  const args = [
642
- path.join(__dirname, "..", "..", "bin", "ufoo.js"),
646
+ path.join(__dirname, "..", "..", "..", "bin", "ufoo.js"),
643
647
  "bus",
644
648
  "alert",
645
649
  subscriber,
@@ -3,7 +3,7 @@ const fs = require("fs");
3
3
  const net = require("net");
4
4
  const path = require("path");
5
5
  const { subscriberToSafeName, isValidTty } = require("./utils");
6
- const { createTerminalAdapterRouter } = require("../terminal/adapterRouter");
6
+ const { createTerminalAdapterRouter } = require("../../runtime/terminal/adapterRouter");
7
7
 
8
8
  const SHOULD_LOG_INJECT = process.env.UFOO_INJECT_DEBUG === "1";
9
9
  const logInject = (message) => {
@@ -415,10 +415,18 @@ class Injector {
415
415
  throw new Error(`Inject disabled for ${subscriber}. ufoo-code consumes bus internally.`);
416
416
  }
417
417
 
418
- // 确定注入命令(codex 用 "ubus",claude-code 用 "/ubus")
418
+ // 确定注入命令:
419
+ // - codex: 裸 "ubus"(codex 没有 slash-command 命名空间)
420
+ // - agy: 裸 "ubus"(agy 的 `/` 是它自己的 slash-command 命名空间,
421
+ // "/ubus" 会被识别为 unknown slash command 而不是 prompt)
422
+ // - claude-code 及其他: "/ubus"
419
423
  const command = commandOverride
420
424
  ? String(commandOverride)
421
- : (subscriber.startsWith("codex:") ? "ubus" : "/ubus");
425
+ : (
426
+ subscriber.startsWith("codex:") || subscriber.startsWith("agy:")
427
+ ? "ubus"
428
+ : "/ubus"
429
+ );
422
430
 
423
431
  const meta = this.getAgentMeta(subscriber) || {};
424
432
  const launchMode = meta.launch_mode || "";
@@ -12,7 +12,7 @@ const {
12
12
  } = require("./utils");
13
13
  const NicknameManager = require("./nickname");
14
14
  const { buildMessageData } = require("./messageMeta");
15
- const { getUfooPaths } = require("../ufoo/paths");
15
+ const { getUfooPaths } = require("../state/paths");
16
16
 
17
17
  const SEQ_LOCK_TIMEOUT_MS = 5000;
18
18
  const SEQ_LOCK_POLL_MS = 25;
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+
3
+ const {
4
+ parseTagsFromOptions,
5
+ validateTags,
6
+ validateTaskId,
7
+ normalizeTagList,
8
+ } = require("./envelope");
9
+
10
+ const INJECTION_MODES = {
11
+ IMMEDIATE: "immediate",
12
+ QUEUED: "queued",
13
+ };
14
+
15
+ function normalizeInjectionMode(value, fallback = INJECTION_MODES.IMMEDIATE) {
16
+ const raw = String(value || "").trim().toLowerCase();
17
+ if (raw === INJECTION_MODES.QUEUED) return INJECTION_MODES.QUEUED;
18
+ if (raw === INJECTION_MODES.IMMEDIATE) return INJECTION_MODES.IMMEDIATE;
19
+ return fallback;
20
+ }
21
+
22
+ function normalizeMessageSource(value) {
23
+ const raw = String(value || "").trim();
24
+ return raw || "";
25
+ }
26
+
27
+ function asTrimmedString(value) {
28
+ return typeof value === "string" ? value.trim() : "";
29
+ }
30
+
31
+ function resolveTagsFromOptions(options = {}) {
32
+ const dataIn = options && typeof options.data === "object" && options.data ? options.data : {};
33
+ const hasFlagOption = "reply" in options || "report" in options || "fyi" in options;
34
+ const hasTagsOption = "tags" in options;
35
+ const hasTaskOption = "taskId" in options || "task_id" in options || "task" in options;
36
+ const hasReportToOption = "reportTo" in options || "report_to" in options;
37
+
38
+ if (hasFlagOption || hasTagsOption || hasTaskOption || hasReportToOption) {
39
+ return parseTagsFromOptions(options);
40
+ }
41
+
42
+ const tags = normalizeTagList(dataIn.tags);
43
+ validateTags(tags);
44
+ const taskId = validateTaskId(dataIn.task_id);
45
+ const reportTo = asTrimmedString(dataIn.report_to);
46
+ return { tags, taskId, reportTo };
47
+ }
48
+
49
+ function buildMessageData(message, options = {}) {
50
+ const base = options && typeof options.data === "object" && options.data
51
+ ? { ...options.data }
52
+ : {};
53
+ const data = { ...base, message };
54
+ data.injection_mode = normalizeInjectionMode(
55
+ options.injectionMode || data.injection_mode,
56
+ INJECTION_MODES.IMMEDIATE,
57
+ );
58
+ const source = normalizeMessageSource(options.source || data.source);
59
+ if (source) {
60
+ data.source = source;
61
+ } else {
62
+ delete data.source;
63
+ }
64
+
65
+ const { tags, taskId, reportTo } = resolveTagsFromOptions(options);
66
+
67
+ if (tags.length > 0) {
68
+ data.tags = tags;
69
+ } else {
70
+ delete data.tags;
71
+ }
72
+
73
+ if (taskId) {
74
+ data.task_id = taskId;
75
+ } else {
76
+ delete data.task_id;
77
+ }
78
+
79
+ if (reportTo) {
80
+ data.report_to = reportTo;
81
+ } else {
82
+ delete data.report_to;
83
+ }
84
+
85
+ return data;
86
+ }
87
+
88
+ function getInjectionModeFromEvent(evt, fallback = INJECTION_MODES.IMMEDIATE) {
89
+ const data = evt && typeof evt.data === "object" && evt.data ? evt.data : {};
90
+ return normalizeInjectionMode(
91
+ data.injection_mode || evt?.injection_mode,
92
+ fallback,
93
+ );
94
+ }
95
+
96
+ function getTagsFromEvent(evt) {
97
+ const data = evt && typeof evt.data === "object" && evt.data ? evt.data : {};
98
+ const tags = normalizeTagList(data.tags);
99
+ try {
100
+ validateTags(tags);
101
+ } catch {
102
+ return [];
103
+ }
104
+ return tags;
105
+ }
106
+
107
+ function getTaskIdFromEvent(evt) {
108
+ const data = evt && typeof evt.data === "object" && evt.data ? evt.data : {};
109
+ try {
110
+ return validateTaskId(data.task_id);
111
+ } catch {
112
+ return "";
113
+ }
114
+ }
115
+
116
+ function getReportToFromEvent(evt) {
117
+ const data = evt && typeof evt.data === "object" && evt.data ? evt.data : {};
118
+ return asTrimmedString(data.report_to);
119
+ }
120
+
121
+ module.exports = {
122
+ INJECTION_MODES,
123
+ normalizeInjectionMode,
124
+ normalizeMessageSource,
125
+ buildMessageData,
126
+ getInjectionModeFromEvent,
127
+ getTagsFromEvent,
128
+ getTaskIdFromEvent,
129
+ getReportToFromEvent,
130
+ };
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+
3
+ const { getTagsFromEvent, getTaskIdFromEvent } = require("./messageMeta");
4
+ const { renderEnvelope } = require("./envelope");
5
+
6
+ const MANUAL_INJECTION_SOURCES = new Set([
7
+ "chat-direct",
8
+ "chat-internal-agent-view",
9
+ "chat-manual",
10
+ "manual",
11
+ ]);
12
+
13
+ function asTrimmedString(value) {
14
+ return typeof value === "string" ? value.trim() : "";
15
+ }
16
+
17
+ function shouldRenderPromptEnvelope(evt = {}) {
18
+ const data = evt && typeof evt.data === "object" && evt.data ? evt.data : {};
19
+ return data.raw_inject !== true
20
+ && data.rawInject !== true
21
+ && data.envelope !== false
22
+ && data.prompt_envelope !== false;
23
+ }
24
+
25
+ function getPublisherId(evt = {}) {
26
+ const publisher = evt.publisher;
27
+ if (typeof publisher === "string") return publisher;
28
+ if (publisher && typeof publisher === "object") {
29
+ return asTrimmedString(publisher.subscriber || publisher.id || publisher.nickname);
30
+ }
31
+ return "";
32
+ }
33
+
34
+ function getAgentNickname(meta = {}) {
35
+ return asTrimmedString(meta.display_nickname || meta.nickname || meta.scoped_nickname);
36
+ }
37
+
38
+ function buildPromptInjectionText(evt = {}, subscriber = "", agents = {}) {
39
+ const data = evt && typeof evt.data === "object" && evt.data ? evt.data : {};
40
+ const message = String(data.message || "");
41
+ if (!shouldRenderPromptEnvelope(evt)) return message;
42
+
43
+ const source = asTrimmedString(data.source).toLowerCase();
44
+ const kind = MANUAL_INJECTION_SOURCES.has(source) ? "manual" : "bus";
45
+ const publisherId = getPublisherId(evt);
46
+ const publisherMeta = publisherId && agents ? agents[publisherId] : null;
47
+ const targetMeta = subscriber && agents ? agents[subscriber] : null;
48
+
49
+ return renderEnvelope({
50
+ kind,
51
+ fromId: publisherId,
52
+ fromNickname: publisherMeta ? getAgentNickname(publisherMeta) : "",
53
+ toId: subscriber,
54
+ toNickname: targetMeta ? getAgentNickname(targetMeta) : "",
55
+ tags: getTagsFromEvent(evt),
56
+ taskId: getTaskIdFromEvent(evt),
57
+ message,
58
+ });
59
+ }
60
+
61
+ module.exports = {
62
+ buildPromptInjectionText,
63
+ getPublisherId,
64
+ shouldRenderPromptEnvelope,
65
+ };
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  const fs = require("fs");
10
- const { isITerm2 } = require("../terminal/detect");
10
+ const { isITerm2 } = require("../../runtime/terminal/detect");
11
11
 
12
12
  function shakeTerminalByTty(ttyPath, options = {}) {
13
13
  if (!ttyPath) return false;
@@ -3,9 +3,9 @@
3
3
  const fs = require("fs");
4
4
  const path = require("path");
5
5
  const { getTimestamp, ensureDir, safeNameToSubscriber, getTtyProcessInfo } = require("./utils");
6
- const { getUfooPaths } = require("../ufoo/paths");
7
- const { loadAgentsData, saveAgentsData } = require("../ufoo/agentsStore");
8
- const { appendAgentRegistryDiagnostic } = require("../ufoo/agentRegistryDiagnostics");
6
+ const { getUfooPaths } = require("../state/paths");
7
+ const { loadAgentsData, saveAgentsData } = require("../state/agentsStore");
8
+ const { appendAgentRegistryDiagnostic } = require("../state/agentRegistryDiagnostics");
9
9
 
10
10
  function readQueueTty(queueDir) {
11
11
  try {
@@ -2,7 +2,7 @@ const fs = require("fs");
2
2
  const { getTimestamp, isAgentPidAlive, isMetaActive, isValidTty, getTtyProcessInfo } = require("./utils");
3
3
  const NicknameManager = require("./nickname");
4
4
  const { spawnSync } = require("child_process");
5
- const { appendAgentRegistryDiagnostic } = require("../ufoo/agentRegistryDiagnostics");
5
+ const { appendAgentRegistryDiagnostic } = require("../state/agentRegistryDiagnostics");
6
6
 
7
7
  function detectTerminalAppFromEnv() {
8
8
  const termProgram = process.env.TERM_PROGRAM || "";
@@ -92,7 +92,7 @@ function getJoinedPid() {
92
92
 
93
93
  function isInternalLaunchMode(meta) {
94
94
  const mode = String(meta?.launch_mode || "").trim();
95
- return mode === "internal" || mode === "internal-pty";
95
+ return mode === "internal";
96
96
  }
97
97
 
98
98
  function hasProviderSession(meta) {
@@ -2,8 +2,8 @@ const crypto = require("crypto");
2
2
  const fs = require("fs");
3
3
  const path = require("path");
4
4
  const { spawnSync } = require("child_process");
5
- const { redactSecrets } = require("../providerapi/redactor");
6
- const { appendAgentRegistryDiagnostic } = require("../ufoo/agentRegistryDiagnostics");
5
+ const { redactSecrets } = require("../../runtime/privacy/redactor");
6
+ const { appendAgentRegistryDiagnostic } = require("../state/agentRegistryDiagnostics");
7
7
 
8
8
  /**
9
9
  * 获取当前 UTC 时间戳(ISO 8601 格式)
@@ -3,9 +3,9 @@
3
3
  const fs = require("fs");
4
4
  const os = require("os");
5
5
  const path = require("path");
6
- const { getUfooPaths } = require("../ufoo/paths");
7
- const { loadAgentsData } = require("../ufoo/agentsStore");
8
- const { redactSecrets, redactString } = require("../providerapi/redactor");
6
+ const { getUfooPaths } = require("../state/paths");
7
+ const { loadAgentsData } = require("../state/agentsStore");
8
+ const { redactSecrets, redactString } = require("../../runtime/privacy/redactor");
9
9
 
10
10
  const HISTORY_DEBUG = process.env.UFOO_HISTORY_DEBUG === "1";
11
11
  const debugLog = (...args) => { if (HISTORY_DEBUG) console.error("[history]", ...args); };
@@ -223,7 +223,7 @@ function extractUserText(record) {
223
223
  return "";
224
224
  }
225
225
 
226
- function isProbeMarker(text) {
226
+ function isLegacyUfooMarker(text) {
227
227
  return /^\/ufoo\s+\S+$/.test(text) || /^\$ufoo\s+\S+$/.test(text);
228
228
  }
229
229
 
@@ -321,7 +321,7 @@ function collectClaudeManualInputs(projectRoot, watermark = {}) {
321
321
  if (new Date(record.timestamp).getTime() <= cutoffMs) return;
322
322
  }
323
323
  const text = extractUserText(record);
324
- if (!text || isProbeMarker(text)) return;
324
+ if (!text || isLegacyUfooMarker(text)) return;
325
325
  entries.push({
326
326
  ts: record.timestamp || "",
327
327
  source: "manual",
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ bus: require("./bus"),
5
+ context: require("./context"),
6
+ history: require("./history/inputTimeline"),
7
+ memory: require("./memory"),
8
+ report: require("./report/store"),
9
+ status: require("./status"),
10
+ };
@@ -2,7 +2,7 @@ const fs = require("fs");
2
2
  const os = require("os");
3
3
  const path = require("path");
4
4
 
5
- const { redactSecrets, redactString } = require("../providerapi/redactor");
5
+ const { redactSecrets, redactString } = require("../../runtime/privacy/redactor");
6
6
 
7
7
  const MAX_RESULTS = 3;
8
8
  const MAX_TOTAL_TEXT_CHARS = 2000;