u-foo 2.3.12 → 2.3.13

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.
@@ -1,7 +1,5 @@
1
1
  const fs = require("fs");
2
2
  const path = require("path");
3
- const { runCliAgent } = require("./cliRunner");
4
- const { normalizeCliOutput } = require("./normalizeOutput");
5
3
  const { buildStatus } = require("../daemon/status");
6
4
  const { getUfooPaths } = require("../ufoo/paths");
7
5
  const { normalizeGateRouterResult } = require("../controller/gateRouter");
@@ -633,7 +631,6 @@ async function runUfooAgent({
633
631
  loopRuntime = null,
634
632
  controllerMode = null,
635
633
  }) {
636
- const state = loadSessionState(projectRoot);
637
634
  const mode = String(routingMode || (routingContext && routingContext.mode) || "").trim().toLowerCase();
638
635
  const resolvedControllerMode = String(
639
636
  controllerMode
@@ -660,7 +657,6 @@ async function runUfooAgent({
660
657
  let res;
661
658
 
662
659
  const useDirectProvider = shouldUseDirectProvider(provider);
663
- let usedDirectProvider = false;
664
660
 
665
661
  if (useDirectProvider) {
666
662
  res = await runNativeRouterCall({
@@ -671,47 +667,22 @@ async function runUfooAgent({
671
667
  model,
672
668
  });
673
669
  if (!res.ok) {
670
+ // eslint-disable-next-line no-console
671
+ console.error(`[ufoo-agent] native provider failed: ${res.error || "unknown error"}`);
674
672
  return { ok: false, error: res.error };
675
673
  } else {
676
- usedDirectProvider = true;
677
674
  res = { ok: true, output: res.output, sessionId: "", provider: res.provider, model: res.model };
678
675
  }
679
676
  }
680
677
 
681
678
  if (!useDirectProvider) {
682
- res = await runCliAgent({
683
- provider,
684
- model,
685
- prompt: fullPrompt,
686
- systemPrompt,
687
- sessionId: state.data?.sessionId,
688
- disableSession: provider === "claude-cli",
689
- cwd: projectRoot,
690
- });
691
-
692
- if (!res.ok) {
693
- const msg = (res.error || "").toLowerCase();
694
- if (msg.includes("session id") || msg.includes("session-id") || msg.includes("already in use")) {
695
- res = await runCliAgent({
696
- provider,
697
- model,
698
- prompt: fullPrompt,
699
- systemPrompt,
700
- sessionId: undefined,
701
- disableSession: provider === "claude-cli",
702
- cwd: projectRoot,
703
- });
704
- }
705
- }
706
-
707
- if (!res.ok) {
708
- return { ok: false, error: res.error };
709
- }
679
+ const error = `unsupported ufoo-agent provider "${provider || ""}"; cliRunner fallback has been removed`;
680
+ // eslint-disable-next-line no-console
681
+ console.error(`[ufoo-agent] ${error}`);
682
+ return { ok: false, error };
710
683
  }
711
684
 
712
- const rawText = usedDirectProvider
713
- ? String(res.output || "").trim()
714
- : normalizeCliOutput(res.output);
685
+ const rawText = String(res.output || "").trim();
715
686
  const text = stripMarkdownFence(rawText);
716
687
  let payload = null;
717
688
  try {
@@ -442,7 +442,9 @@ function createDaemonMessageRouter(options = {}) {
442
442
  }
443
443
 
444
444
  function handleErrorMessage(msg) {
445
- resolveStatusLine(`{gray-fg}✗{/gray-fg} Error: ${msg.error}`);
445
+ const error = String(msg.error || "unknown error");
446
+ resolveStatusLine(`{gray-fg}✗{/gray-fg} Error: ${error}`);
447
+ logMessage("error", `{white-fg}✗{/white-fg} ${escapeBlessed(error)}`);
446
448
  renderScreen();
447
449
  return false;
448
450
  }
@@ -1,4 +1,4 @@
1
- const DEFAULT_MODE_OPTIONS = ["auto", "host", "terminal", "tmux", "internal"];
1
+ const DEFAULT_MODE_OPTIONS = ["auto", "host", "terminal", "tmux", "internal-pty", "internal"];
2
2
 
3
3
  function createDashboardKeyController(options = {}) {
4
4
  const {
@@ -1,6 +1,6 @@
1
1
  const { clampAgentWindowWithSelection } = require("./agentDirectory");
2
2
 
3
- const DEFAULT_MODE_OPTIONS = ["auto", "host", "terminal", "tmux", "internal"];
3
+ const DEFAULT_MODE_OPTIONS = ["auto", "host", "terminal", "tmux", "internal-pty", "internal"];
4
4
 
5
5
  function providerLabel(value) {
6
6
  if (value === "claude-cli") return "claude";
package/src/chat/index.js CHANGED
@@ -65,7 +65,7 @@ const {
65
65
  pruneTransientAgentStates,
66
66
  } = require("./transientAgentState");
67
67
 
68
- const MODE_OPTIONS = ["auto", "host", "terminal", "tmux", "internal"];
68
+ const MODE_OPTIONS = ["auto", "host", "terminal", "tmux", "internal-pty", "internal"];
69
69
 
70
70
  async function runChat(projectRoot, options = {}) {
71
71
  const globalMode = options && options.globalMode === true;
package/src/config.js CHANGED
@@ -29,6 +29,7 @@ const DEFAULT_UCODE_CONFIG = {
29
29
  function normalizeLaunchMode(value) {
30
30
  if (value === "auto") return "auto";
31
31
  if (value === "internal") return "internal";
32
+ if (value === "internal-pty") return "internal-pty";
32
33
  if (value === "tmux") return "tmux";
33
34
  if (value === "terminal") return "terminal";
34
35
  if (value === "host") return "host";
package/src/daemon/ops.js CHANGED
@@ -14,6 +14,7 @@ const {
14
14
  const {
15
15
  createSession: createHostSession,
16
16
  } = require("../terminal/adapters/hostAdapter");
17
+ const { resolveDefaultManualBootstrap } = require("../agent/defaultBootstrap");
17
18
 
18
19
  function normalizeLaunchAgent(agent = "") {
19
20
  const value = String(agent || "").trim().toLowerCase();
@@ -44,6 +45,43 @@ function toTmuxBinary(agent = "") {
44
45
  return "";
45
46
  }
46
47
 
48
+ function applyDefaultManagedBootstrap(projectRoot, normalizedAgent, args = [], extraEnv = {}) {
49
+ const agentType = toBusAgentType(normalizedAgent);
50
+ if (!agentType || agentType === "ufoo-code") {
51
+ return {
52
+ args: Array.isArray(args) ? args.slice() : [],
53
+ extraEnv: extraEnv && typeof extraEnv === "object" ? { ...extraEnv } : {},
54
+ applied: false,
55
+ };
56
+ }
57
+
58
+ const currentArgs = Array.isArray(args) ? args.slice() : [];
59
+ const currentExtraEnv = extraEnv && typeof extraEnv === "object" ? { ...extraEnv } : {};
60
+ const resolved = resolveDefaultManualBootstrap({
61
+ projectRoot,
62
+ agentType,
63
+ args: currentArgs,
64
+ env: {
65
+ ...process.env,
66
+ ...currentExtraEnv,
67
+ },
68
+ });
69
+
70
+ if (!resolved || resolved.mode === "skip") {
71
+ return { args: currentArgs, extraEnv: currentExtraEnv, applied: false };
72
+ }
73
+
74
+ return {
75
+ args: Array.isArray(resolved.args) ? resolved.args : currentArgs,
76
+ extraEnv: {
77
+ ...currentExtraEnv,
78
+ ...(resolved.env && typeof resolved.env === "object" ? resolved.env : {}),
79
+ UFOO_SKIP_DEFAULT_BOOTSTRAP: "1",
80
+ },
81
+ applied: true,
82
+ };
83
+ }
84
+
47
85
  function resolveUfooRunnerPath() {
48
86
  return path.resolve(__dirname, "../../bin/ufoo.js");
49
87
  }
@@ -116,7 +154,7 @@ function resolveHostLaunchContext(options = {}) {
116
154
 
117
155
  function resolveConfiguredLaunchMode(configuredMode = "", options = {}) {
118
156
  const mode = normalizeOptionalString(configuredMode);
119
- if (mode === "internal" || mode === "tmux" || mode === "terminal" || mode === "host") {
157
+ if (mode === "internal" || mode === "internal-pty" || mode === "tmux" || mode === "terminal" || mode === "host") {
120
158
  return mode;
121
159
  }
122
160
  const hostContext = resolveHostLaunchContext(options);
@@ -518,7 +556,9 @@ async function spawnManagedHostAgent(
518
556
  subscriberId = "";
519
557
  }
520
558
 
521
- const args = Array.isArray(extraArgs) ? extraArgs : [];
559
+ const hostBootstrap = applyDefaultManagedBootstrap(projectRoot, normalizedAgent, extraArgs, extraEnv);
560
+ const args = hostBootstrap.args;
561
+ const hostExtraEnv = hostBootstrap.extraEnv;
522
562
  const argText = args.length > 0 ? ` ${args.map(shellEscape).join(" ")}` : "";
523
563
 
524
564
  const titleCmd = buildTitleCmd(nickname);
@@ -538,8 +578,8 @@ async function spawnManagedHostAgent(
538
578
  env.UFOO_NICKNAME = nickname;
539
579
  }
540
580
  // Parse extraEnv string (e.g., "UFOO_UCODE_BOOTSTRAP_FILE=/path/to/file") and add to env
541
- if (extraEnv && typeof extraEnv === "object") {
542
- for (const [key, value] of Object.entries(extraEnv)) {
581
+ if (hostExtraEnv && typeof hostExtraEnv === "object") {
582
+ for (const [key, value] of Object.entries(hostExtraEnv)) {
543
583
  if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(String(key || ""))) {
544
584
  env[String(key)] = String(value ?? "");
545
585
  }
@@ -645,7 +685,9 @@ async function spawnInternalAgent(
645
685
  ? (count > 1 ? `${nickname}-${i + 1}` : nickname)
646
686
  : "";
647
687
  const providerSessionId = typeof options.providerSessionId === "string" ? options.providerSessionId.trim() : "";
648
- const usePty = process.env.UFOO_INTERNAL_PTY !== "0";
688
+ const usePty = typeof options.usePty === "boolean"
689
+ ? options.usePty
690
+ : process.env.UFOO_INTERNAL_PTY !== "0";
649
691
  const launchMode = usePty ? "internal-pty" : "internal";
650
692
 
651
693
  // 传递 launch_mode 和 parent PID 到 join
@@ -657,8 +699,9 @@ async function spawnInternalAgent(
657
699
  const finalNickname = joinResult.nickname || requestedNickname || "";
658
700
  bus.saveBusData();
659
701
 
702
+ const managedBootstrap = applyDefaultManagedBootstrap(projectRoot, normalizedAgent, extraArgs, extraEnv);
660
703
  const runnerCmd = usePty ? "agent-pty-runner" : "agent-runner";
661
- const args = Array.isArray(extraArgs) ? extraArgs : [];
704
+ const args = managedBootstrap.args;
662
705
  const child = spawn(process.execPath, [runner, runnerCmd, agent, ...args], {
663
706
  // 关键改动:不使用 detached,daemon 作为父进程
664
707
  detached: false,
@@ -666,7 +709,7 @@ async function spawnInternalAgent(
666
709
  cwd: projectRoot,
667
710
  env: {
668
711
  ...process.env,
669
- ...(extraEnv && typeof extraEnv === "object" ? extraEnv : {}),
712
+ ...(managedBootstrap.extraEnv && typeof managedBootstrap.extraEnv === "object" ? managedBootstrap.extraEnv : {}),
670
713
  UFOO_INTERNAL_AGENT: "1",
671
714
  UFOO_INTERNAL_PTY: usePty ? "1" : "0",
672
715
  UFOO_SUBSCRIBER_ID: subscriberId, // 直接传递 subscriber ID
@@ -888,7 +931,8 @@ async function launchAgent(projectRoot, agent, count = 1, nickname = "", process
888
931
  throw new Error(`unsupported agent type: ${agent}`);
889
932
  }
890
933
 
891
- if (mode === "internal") {
934
+ if (mode === "internal" || mode === "internal-pty") {
935
+ const usePty = mode === "internal-pty";
892
936
  const result = await spawnInternalAgent(
893
937
  projectRoot,
894
938
  normalizedAgent,
@@ -896,9 +940,10 @@ async function launchAgent(projectRoot, agent, count = 1, nickname = "", process
896
940
  nickname,
897
941
  processManager,
898
942
  launchEnvObject,
899
- extraArgs
943
+ extraArgs,
944
+ { usePty }
900
945
  );
901
- return { mode: "internal", launchScope, subscriberIds: result.subscriberIds };
946
+ return { mode, launchScope, subscriberIds: result.subscriberIds };
902
947
  }
903
948
  if (mode === "tmux") {
904
949
  // Check if tmux is available
@@ -1129,7 +1174,7 @@ async function resumeAgents(projectRoot, target = "", processManager = null) {
1129
1174
  const args = buildResumeArgs(item.agent, sessionId);
1130
1175
  let reused = false;
1131
1176
  let resumedId = item.id;
1132
- if (mode === "internal") {
1177
+ if (mode === "internal" || mode === "internal-pty") {
1133
1178
  // Internal agents have no terminal/pane to reattach. Start a fresh
1134
1179
  // daemon-managed runner and replace the old recoverable registration.
1135
1180
  // The provider session is still reused via the normal provider args.
@@ -1142,7 +1187,7 @@ async function resumeAgents(projectRoot, target = "", processManager = null) {
1142
1187
  processManager,
1143
1188
  { UFOO_SKIP_SESSION_PROBE: "1" },
1144
1189
  args,
1145
- { replaceAgentId: item.id, providerSessionId: sessionId }
1190
+ { replaceAgentId: item.id, providerSessionId: sessionId, usePty: mode === "internal-pty" }
1146
1191
  );
1147
1192
  resumedId = launchResult.subscriberIds && launchResult.subscriberIds[0]
1148
1193
  ? launchResult.subscriberIds[0]