@chenmk/superflow 0.4.0 → 0.5.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 (241) hide show
  1. package/INSTALL.en.md +54 -12
  2. package/INSTALL.md +59 -33
  3. package/README.md +178 -54
  4. package/README.zh-CN.md +156 -51
  5. package/assets/brand/managed-review-loop.png +0 -0
  6. package/assets/manifest.json +10 -5
  7. package/assets/scripts/superflow-coding-ready.mjs +135 -0
  8. package/assets/scripts/superflow-dependency-update-hook.sh +81 -8
  9. package/assets/scripts/superflow-document-audit.mjs +240 -0
  10. package/assets/scripts/superflow-environment-preflight.mjs +398 -0
  11. package/assets/scripts/superflow-hook-guard.sh +40 -1
  12. package/assets/scripts/superflow-managed-command-guard.py +309 -0
  13. package/assets/scripts/superflow-managed-executor-preflight.mjs +421 -0
  14. package/assets/scripts/superflow-managed-owner-verification.sh +117 -0
  15. package/assets/scripts/superflow-managed-work-check.mjs +287 -26
  16. package/assets/scripts/superflow-managed-work-guard.sh +6 -92
  17. package/assets/skills/superflow-clarify/SKILL.md +14 -0
  18. package/assets/skills/superflow-design/SKILL.md +38 -13
  19. package/assets/skills/superflow-docs/SKILL.md +93 -2
  20. package/assets/skills/superflow-pipeline/SKILL.md +167 -10
  21. package/assets/skills/superflow-pipeline/references/batch-prompt-template.md +17 -9
  22. package/assets/skills/superflow-pipeline/references/document-readiness.md +157 -0
  23. package/assets/skills/superflow-pipeline/references/document-templates.md +9 -5
  24. package/assets/skills/superflow-pipeline/references/feature-gated-workflow.md +14 -0
  25. package/assets/skills/superflow-pipeline/references/implementation-prompt-template.md +20 -11
  26. package/assets/skills/superflow-pipeline/references/managed-work.md +123 -15
  27. package/assets/skills/superflow-pipeline/references/orchestration.md +2 -2
  28. package/assets/skills/superflow-pipeline/references/p0-baseline-template.md +3 -2
  29. package/assets/skills/superflow-pipeline/references/quality-gate.md +7 -5
  30. package/assets/skills/superflow-pipeline/references/reviewer-checklist.md +7 -0
  31. package/assets/skills/superflow-pipeline/scripts/superflow-guard.sh +205 -9
  32. package/assets/skills/superflow-pipeline/scripts/superflow-handoff.sh +7 -4
  33. package/assets/skills/superflow-requirement-review/SKILL.md +178 -0
  34. package/assets/skills/superflow-requirement-review/agents/openai.yaml +4 -0
  35. package/assets/skills-en/superflow-clarify/SKILL.md +17 -0
  36. package/assets/skills-en/superflow-design/SKILL.md +31 -12
  37. package/assets/skills-en/superflow-docs/SKILL.md +99 -0
  38. package/assets/skills-en/superflow-pipeline/SKILL.md +163 -9
  39. package/assets/skills-en/superflow-pipeline/references/batch-prompt-template.md +4 -0
  40. package/assets/skills-en/superflow-pipeline/references/document-readiness.md +125 -0
  41. package/assets/skills-en/superflow-pipeline/references/document-templates.md +10 -0
  42. package/assets/skills-en/superflow-pipeline/references/feature-gated-workflow.md +7 -0
  43. package/assets/skills-en/superflow-pipeline/references/implementation-prompt-template.md +7 -0
  44. package/assets/skills-en/superflow-pipeline/references/managed-work.md +160 -10
  45. package/assets/skills-en/superflow-pipeline/references/quality-gate.md +12 -0
  46. package/assets/skills-en/superflow-pipeline/references/reviewer-checklist.md +7 -0
  47. package/assets/skills-en/superflow-pipeline/scripts/superflow-guard.sh +205 -9
  48. package/assets/skills-en/superflow-pipeline/scripts/superflow-handoff.sh +7 -4
  49. package/assets/skills-en/superflow-requirement-review/SKILL.md +99 -0
  50. package/assets/skills-en/superflow-requirement-review/agents/openai.yaml +4 -0
  51. package/dist/app/cli.js +64 -9
  52. package/dist/app/cli.js.map +1 -1
  53. package/dist/app/commands/change-guard.js +19 -2
  54. package/dist/app/commands/change-guard.js.map +1 -1
  55. package/dist/app/commands/check.js +107 -45
  56. package/dist/app/commands/check.js.map +1 -1
  57. package/dist/app/commands/docs.js +7 -3
  58. package/dist/app/commands/docs.js.map +1 -1
  59. package/dist/app/commands/doctor.js +174 -198
  60. package/dist/app/commands/doctor.js.map +1 -1
  61. package/dist/app/commands/eval.js +64 -0
  62. package/dist/app/commands/eval.js.map +1 -0
  63. package/dist/app/commands/implement.js +7 -3
  64. package/dist/app/commands/implement.js.map +1 -1
  65. package/dist/app/commands/init.js +12 -24
  66. package/dist/app/commands/init.js.map +1 -1
  67. package/dist/app/commands/mcp.js +167 -0
  68. package/dist/app/commands/mcp.js.map +1 -0
  69. package/dist/app/commands/pipeline.js +652 -18
  70. package/dist/app/commands/pipeline.js.map +1 -1
  71. package/dist/app/commands/requirement-review.js +6 -0
  72. package/dist/app/commands/requirement-review.js.map +1 -0
  73. package/dist/app/commands/status.js +66 -3
  74. package/dist/app/commands/status.js.map +1 -1
  75. package/dist/app/commands/uninstall.js +3 -3
  76. package/dist/app/commands/uninstall.js.map +1 -1
  77. package/dist/app/commands/update.js +83 -9
  78. package/dist/app/commands/update.js.map +1 -1
  79. package/dist/domains/agent.js +3 -11
  80. package/dist/domains/agent.js.map +1 -1
  81. package/dist/domains/config/cli-help.js +54 -12
  82. package/dist/domains/config/cli-help.js.map +1 -1
  83. package/dist/domains/config/i18n.js +6 -8
  84. package/dist/domains/config/i18n.js.map +1 -1
  85. package/dist/domains/config/manifest.js +0 -3
  86. package/dist/domains/config/manifest.js.map +1 -1
  87. package/dist/domains/deps.js +68 -7
  88. package/dist/domains/deps.js.map +1 -1
  89. package/dist/domains/managed-work/completion-policy.js +174 -0
  90. package/dist/domains/managed-work/completion-policy.js.map +1 -0
  91. package/dist/domains/managed-work/context-manifest.js +208 -0
  92. package/dist/domains/managed-work/context-manifest.js.map +1 -0
  93. package/dist/domains/managed-work/contract.js +135 -4
  94. package/dist/domains/managed-work/contract.js.map +1 -1
  95. package/dist/domains/managed-work/control-signals.js +34 -0
  96. package/dist/domains/managed-work/control-signals.js.map +1 -0
  97. package/dist/domains/managed-work/control.js +628 -0
  98. package/dist/domains/managed-work/control.js.map +1 -0
  99. package/dist/domains/managed-work/evaluation.js +497 -0
  100. package/dist/domains/managed-work/evaluation.js.map +1 -0
  101. package/dist/domains/managed-work/evidence-path.js +14 -0
  102. package/dist/domains/managed-work/evidence-path.js.map +1 -0
  103. package/dist/domains/managed-work/execution-contract.js +167 -0
  104. package/dist/domains/managed-work/execution-contract.js.map +1 -0
  105. package/dist/domains/managed-work/executor-policy.js +65 -0
  106. package/dist/domains/managed-work/executor-policy.js.map +1 -0
  107. package/dist/domains/managed-work/failure.js +65 -0
  108. package/dist/domains/managed-work/failure.js.map +1 -0
  109. package/dist/domains/managed-work/host-review.js +77 -0
  110. package/dist/domains/managed-work/host-review.js.map +1 -0
  111. package/dist/domains/managed-work/human-messages.js +31 -0
  112. package/dist/domains/managed-work/human-messages.js.map +1 -0
  113. package/dist/domains/managed-work/input.js +29 -3
  114. package/dist/domains/managed-work/input.js.map +1 -1
  115. package/dist/domains/managed-work/journal.js +77 -3
  116. package/dist/domains/managed-work/journal.js.map +1 -1
  117. package/dist/domains/managed-work/lock.js +67 -1
  118. package/dist/domains/managed-work/lock.js.map +1 -1
  119. package/dist/domains/managed-work/notifications.js +0 -15
  120. package/dist/domains/managed-work/notifications.js.map +1 -1
  121. package/dist/domains/managed-work/pair-admission.js +108 -0
  122. package/dist/domains/managed-work/pair-admission.js.map +1 -0
  123. package/dist/domains/managed-work/prompts.js +257 -22
  124. package/dist/domains/managed-work/prompts.js.map +1 -1
  125. package/dist/domains/managed-work/redaction.js +90 -0
  126. package/dist/domains/managed-work/redaction.js.map +1 -0
  127. package/dist/domains/managed-work/review-convergence.js +41 -0
  128. package/dist/domains/managed-work/review-convergence.js.map +1 -0
  129. package/dist/domains/managed-work/review-facts.js +181 -0
  130. package/dist/domains/managed-work/review-facts.js.map +1 -0
  131. package/dist/domains/managed-work/rule-selection.js +84 -0
  132. package/dist/domains/managed-work/rule-selection.js.map +1 -0
  133. package/dist/domains/managed-work/runner.js +1967 -175
  134. package/dist/domains/managed-work/runner.js.map +1 -1
  135. package/dist/domains/managed-work/schemas.js +72 -2
  136. package/dist/domains/managed-work/schemas.js.map +1 -1
  137. package/dist/domains/managed-work/service.js +82 -13
  138. package/dist/domains/managed-work/service.js.map +1 -1
  139. package/dist/domains/managed-work/state.js +195 -5
  140. package/dist/domains/managed-work/state.js.map +1 -1
  141. package/dist/domains/managed-work/storage.js +66 -24
  142. package/dist/domains/managed-work/storage.js.map +1 -1
  143. package/dist/domains/managed-work/types.js +12 -9
  144. package/dist/domains/managed-work/types.js.map +1 -1
  145. package/dist/domains/managed-work/verification-categories.js +39 -0
  146. package/dist/domains/managed-work/verification-categories.js.map +1 -0
  147. package/dist/domains/managed-work/verification-policy.js +116 -0
  148. package/dist/domains/managed-work/verification-policy.js.map +1 -0
  149. package/dist/domains/managed-work/wait.js +21 -3
  150. package/dist/domains/managed-work/wait.js.map +1 -1
  151. package/dist/domains/managed-work/workspace-binding.js +133 -0
  152. package/dist/domains/managed-work/workspace-binding.js.map +1 -0
  153. package/dist/domains/sdd-readiness.js +44 -0
  154. package/dist/domains/sdd-readiness.js.map +1 -0
  155. package/dist/domains/skill/deploy.js +0 -6
  156. package/dist/domains/skill/deploy.js.map +1 -1
  157. package/dist/domains/skill/scripts.js +0 -8
  158. package/dist/domains/skill/scripts.js.map +1 -1
  159. package/dist/domains/state.js +1 -2
  160. package/dist/domains/state.js.map +1 -1
  161. package/dist/mcp/runtime-identity.js +21 -0
  162. package/dist/mcp/runtime-identity.js.map +1 -0
  163. package/dist/mcp/server.js +369 -0
  164. package/dist/mcp/server.js.map +1 -0
  165. package/dist/platform/agent-process.js +499 -38
  166. package/dist/platform/agent-process.js.map +1 -1
  167. package/dist/platform/executable.js +28 -0
  168. package/dist/platform/executable.js.map +1 -0
  169. package/dist/platform/paths.js +1 -17
  170. package/dist/platform/paths.js.map +1 -1
  171. package/dist/platform/process-tree.js +44 -0
  172. package/dist/platform/process-tree.js.map +1 -0
  173. package/dist/platform/process.js +1 -1
  174. package/dist/platform/process.js.map +1 -1
  175. package/package.json +16 -7
  176. package/scripts/check-design-principles.mjs +107 -0
  177. package/scripts/check-managed-work-design.mjs +507 -0
  178. package/scripts/clean-dist.js +9 -0
  179. package/scripts/ensure-bin-modes.js +11 -0
  180. package/dist/cli/index.js +0 -193
  181. package/dist/cli/index.js.map +0 -1
  182. package/dist/commands/archive.js +0 -6
  183. package/dist/commands/archive.js.map +0 -1
  184. package/dist/commands/clarify.js +0 -6
  185. package/dist/commands/clarify.js.map +0 -1
  186. package/dist/commands/design.js +0 -28
  187. package/dist/commands/design.js.map +0 -1
  188. package/dist/commands/docs.js +0 -6
  189. package/dist/commands/docs.js.map +0 -1
  190. package/dist/commands/doctor.js +0 -494
  191. package/dist/commands/doctor.js.map +0 -1
  192. package/dist/commands/implement.js +0 -6
  193. package/dist/commands/implement.js.map +0 -1
  194. package/dist/commands/init.js +0 -520
  195. package/dist/commands/init.js.map +0 -1
  196. package/dist/commands/pipeline.js +0 -6
  197. package/dist/commands/pipeline.js.map +0 -1
  198. package/dist/commands/scan.js +0 -63
  199. package/dist/commands/scan.js.map +0 -1
  200. package/dist/commands/status.js +0 -173
  201. package/dist/commands/status.js.map +0 -1
  202. package/dist/commands/uninstall.js +0 -214
  203. package/dist/commands/uninstall.js.map +0 -1
  204. package/dist/commands/update.js +0 -193
  205. package/dist/commands/update.js.map +0 -1
  206. package/dist/commands/verify.js +0 -6
  207. package/dist/commands/verify.js.map +0 -1
  208. package/dist/core/assets.js +0 -27
  209. package/dist/core/assets.js.map +0 -1
  210. package/dist/core/cli-help.js +0 -98
  211. package/dist/core/cli-help.js.map +0 -1
  212. package/dist/core/context.js +0 -123
  213. package/dist/core/context.js.map +0 -1
  214. package/dist/core/dependencies.js +0 -146
  215. package/dist/core/dependencies.js.map +0 -1
  216. package/dist/core/detect.js +0 -64
  217. package/dist/core/detect.js.map +0 -1
  218. package/dist/core/i18n.js +0 -105
  219. package/dist/core/i18n.js.map +0 -1
  220. package/dist/core/integrity.js +0 -46
  221. package/dist/core/integrity.js.map +0 -1
  222. package/dist/core/manifest.js +0 -21
  223. package/dist/core/manifest.js.map +0 -1
  224. package/dist/core/prompts.js +0 -20
  225. package/dist/core/prompts.js.map +0 -1
  226. package/dist/core/registry.js +0 -134
  227. package/dist/core/registry.js.map +0 -1
  228. package/dist/core/rules.js +0 -17
  229. package/dist/core/rules.js.map +0 -1
  230. package/dist/core/scripts.js +0 -48
  231. package/dist/core/scripts.js.map +0 -1
  232. package/dist/core/skill-check.js +0 -32
  233. package/dist/core/skill-check.js.map +0 -1
  234. package/dist/core/skills.js +0 -62
  235. package/dist/core/skills.js.map +0 -1
  236. package/dist/core/state.js +0 -54
  237. package/dist/core/state.js.map +0 -1
  238. package/dist/utils/path.js +0 -11
  239. package/dist/utils/path.js.map +0 -1
  240. package/dist/utils/shell.js +0 -29
  241. package/dist/utils/shell.js.map +0 -1
@@ -2,30 +2,97 @@ import { existsSync, readFileSync, unlinkSync } from "fs";
2
2
  import path from "path";
3
3
  import spawn from "cross-spawn";
4
4
  import { managedText } from "../domains/managed-work/i18n.js";
5
+ import { resolveExecutableShim } from "./executable.js";
6
+ import { stopProcessTree } from "./process-tree.js";
7
+ const HOST_SUPERVISION_INTERVAL_MS = 10 * 60_000;
8
+ const CLAUDE_MANAGED_AUTO_COMPACT_WINDOW = "200000";
9
+ const CLAUDE_MANAGED_AUTO_COMPACT_PERCENT = "70";
10
+ /**
11
+ * GUI-launched MCP servers do not reliably inherit an interactive shell PATH.
12
+ * Supplement it only with standard executable locations, never a user's home
13
+ * directory or a tool-specific private shim.
14
+ */
15
+ export function managedExecutablePath(basePath, platform = process.platform, directoryExists = existsSync) {
16
+ const delimiter = platform === "win32" ? ";" : ":";
17
+ const standardDirectories = platform === "darwin"
18
+ ? ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"]
19
+ : platform === "win32"
20
+ ? []
21
+ : ["/usr/local/bin", "/usr/bin", "/bin"];
22
+ const existing = (basePath ?? "").split(delimiter).filter(Boolean);
23
+ const known = new Set(existing);
24
+ for (const directory of standardDirectories) {
25
+ if (directoryExists(directory) && !known.has(directory)) {
26
+ existing.push(directory);
27
+ known.add(directory);
28
+ }
29
+ }
30
+ return existing.join(delimiter);
31
+ }
5
32
  export class LocalAgentInvoker {
6
33
  async invoke(invocation) {
7
34
  return runAgentProcess(invocation);
8
35
  }
9
36
  }
37
+ export class AgentInvocationFailure extends Error {
38
+ stdout;
39
+ stderr;
40
+ sessionId;
41
+ usage;
42
+ telemetry;
43
+ constructor(message, stdout, stderr, sessionId, usage, telemetry) {
44
+ super(message);
45
+ this.stdout = stdout;
46
+ this.stderr = stderr;
47
+ this.sessionId = sessionId;
48
+ this.usage = usage;
49
+ this.telemetry = telemetry;
50
+ this.name = "AgentInvocationFailure";
51
+ }
52
+ }
53
+ export function classifyUnresumableSessionFailure(input) {
54
+ const evidence = input.message.toLowerCase();
55
+ const resumeFailure = /\b(?:failed|failure|error|rejected|unable|cannot|can't)\b.{0,80}\b(?:resume|resuming)\b/.test(evidence) ||
56
+ /\b(?:resume|resuming)\b.{0,80}\b(?:failed|failure|error|rejected|unable|cannot|can't|history exceeds)\b/.test(evidence);
57
+ if (!resumeFailure)
58
+ return null;
59
+ if (/(?:(?:session|conversation)(?:\s+id)?\s+(?:not found|does not exist|expired)|unknown\s+(?:session|conversation))/.test(evidence)) {
60
+ return "session_not_found";
61
+ }
62
+ if (/(?:(?:invalid|malformed)\s+(?:session|conversation)(?:\s+id)?|(?:session|conversation)\s+id\s+is\s+invalid)/.test(evidence)) {
63
+ return "session_invalid";
64
+ }
65
+ if (/context window|context limit|too large|too long/.test(evidence)) {
66
+ return "resume_context_limit";
67
+ }
68
+ return null;
69
+ }
10
70
  export function buildAgentCommand(invocation) {
11
71
  if (invocation.agent === "codex")
12
72
  return buildCodexCommand(invocation);
13
- return buildClaudeCommand(invocation);
73
+ return invocation.agent === "claude"
74
+ ? buildClaudeCommand(invocation)
75
+ : buildCodexCommand(invocation);
76
+ }
77
+ export function wrapWithSleepPrevention(command, args, platform = process.platform, caffeinateAvailable = existsSync("/usr/bin/caffeinate")) {
78
+ if (platform !== "darwin" || !caffeinateAvailable)
79
+ return { command, args };
80
+ return {
81
+ command: "/usr/bin/caffeinate",
82
+ args: ["-im", command, ...args],
83
+ };
14
84
  }
15
85
  async function runAgentProcess(invocation) {
16
86
  const command = buildAgentCommand(invocation);
87
+ const environment = buildAgentEnvironment(invocation);
88
+ const executable = resolveExecutableShim(command.command, environment);
89
+ const processCommand = wrapWithSleepPrevention(executable, command.args);
17
90
  if (command.outputFile && existsSync(command.outputFile))
18
91
  unlinkSync(command.outputFile);
19
92
  return new Promise((resolve, reject) => {
20
- const child = spawn(command.command, command.args, {
93
+ const child = spawn(processCommand.command, processCommand.args, {
21
94
  cwd: invocation.projectRoot,
22
- env: {
23
- ...process.env,
24
- SUPERFLOW_MANAGED_TASK_ID: invocation.taskId,
25
- SUPERFLOW_MANAGED_RUN_ID: invocation.runId,
26
- SUPERFLOW_MANAGED_ROLE: invocation.role,
27
- SUPERFLOW_MANAGED_PROJECT_ROOT: invocation.projectRoot,
28
- },
95
+ env: environment,
29
96
  detached: process.platform !== "win32",
30
97
  stdio: ["pipe", "pipe", "pipe"],
31
98
  shell: false,
@@ -35,18 +102,53 @@ async function runAgentProcess(invocation) {
35
102
  let lineBuffer = "";
36
103
  let sessionId = invocation.sessionId;
37
104
  let lastOutputAt = Date.now();
105
+ let lastClockCheckAt = lastOutputAt;
106
+ let activeInvocationMs = 0;
38
107
  let finished = false;
108
+ let telemetry = initialTelemetry();
109
+ let usage = emptyUsage();
110
+ invocation.onTelemetry?.(telemetry);
111
+ invocation.onProcess?.(child.pid ?? null);
39
112
  const warningTimer = setTimeout(() => {
113
+ if (finished)
114
+ return;
115
+ accountClockGap();
40
116
  invocation.onProgress?.(managedText(invocation.language, "连续一段时间没有新进展,后台仍在观察进程", "No new progress for a while; the background service is still observing the process"));
41
117
  }, invocation.timeout.warningMs);
118
+ const heartbeatTimer = setInterval(() => {
119
+ if (finished)
120
+ return;
121
+ accountClockGap();
122
+ const elapsed = Date.now() - lastOutputAt;
123
+ invocation.onProgress?.(managedText(invocation.language, `Agent 心跳:阶段 ${telemetry.phase},距最近输出 ${Math.round(elapsed / 1_000)} 秒,最近进展原因 ${telemetry.lastProgressReason}`, `Agent heartbeat: phase ${telemetry.phase}, ${Math.round(elapsed / 1_000)} seconds since output, last progress reason ${telemetry.lastProgressReason}`));
124
+ }, 60_000);
125
+ const supervisionTimer = setInterval(() => {
126
+ if (finished)
127
+ return;
128
+ accountClockGap();
129
+ invocation.onProgress?.(`superflow-supervision:${JSON.stringify({
130
+ phase: telemetry.phase,
131
+ lastProgressAt: telemetry.lastProgressAt,
132
+ lastProgressReason: telemetry.lastProgressReason,
133
+ secondsSinceOutput: Math.round((Date.now() - lastOutputAt) / 1_000),
134
+ })}`);
135
+ }, HOST_SUPERVISION_INTERVAL_MS);
42
136
  let stalledTimer = setTimeout(onStalled, invocation.timeout.stalledMs);
43
- const hardTimer = setTimeout(() => {
137
+ const hardTimer = setInterval(() => {
138
+ if (finished)
139
+ return;
140
+ accountClockGap();
141
+ if (activeInvocationMs < invocation.timeout.hardMs)
142
+ return;
44
143
  stopChild(child.pid);
45
144
  finishError(new Error(managedText(invocation.language, `单次 Agent 调用超过 ${invocation.timeout.hardMs}ms`, `Single Agent invocation exceeded ${invocation.timeout.hardMs}ms`)));
46
- }, invocation.timeout.hardMs);
145
+ }, 250);
47
146
  if (invocation.sessionId)
48
147
  invocation.onSession?.(invocation.sessionId);
49
148
  child.stdout?.on("data", (chunk) => {
149
+ if (finished)
150
+ return;
151
+ accountClockGap();
50
152
  const text = chunk.toString();
51
153
  stdout += text;
52
154
  lastOutputAt = Date.now();
@@ -57,6 +159,16 @@ async function runAgentProcess(invocation) {
57
159
  lineBuffer = lines.pop() ?? "";
58
160
  for (const line of lines) {
59
161
  const event = safeJson(line);
162
+ if (event) {
163
+ telemetry = updateTelemetry(telemetry, event);
164
+ usage = updateAgentUsage(usage, event);
165
+ invocation.onTelemetry?.(telemetry);
166
+ }
167
+ if (invocation.agent === "claude" && shouldStopClaudeRetry(event)) {
168
+ stopChild(child.pid);
169
+ finishError(new Error(managedText(invocation.language, "Claude 底层模型连续出现 3 次临时 429/529/overloaded,已熔断本次调用", "Claude's provider returned 3 consecutive transient 429/529/overloaded retries; this invocation was stopped")));
170
+ return;
171
+ }
60
172
  const discoveredSession = extractSessionId(event);
61
173
  if (!sessionId && discoveredSession) {
62
174
  sessionId = discoveredSession;
@@ -68,6 +180,9 @@ async function runAgentProcess(invocation) {
68
180
  }
69
181
  });
70
182
  child.stderr?.on("data", (chunk) => {
183
+ if (finished)
184
+ return;
185
+ accountClockGap();
71
186
  stderr += chunk.toString();
72
187
  lastOutputAt = Date.now();
73
188
  });
@@ -78,7 +193,7 @@ async function runAgentProcess(invocation) {
78
193
  cleanupTimers();
79
194
  finished = true;
80
195
  if ((code ?? 1) !== 0) {
81
- reject(new Error(formatAgentFailure(invocation.agent, code, stdout, stderr, invocation.language)));
196
+ reject(new AgentInvocationFailure(formatAgentFailure(invocation.agent, code, stdout, stderr, invocation.language), stdout, stderr, sessionId, usage, telemetry));
82
197
  return;
83
198
  }
84
199
  try {
@@ -94,14 +209,24 @@ async function runAgentProcess(invocation) {
94
209
  stdout,
95
210
  stderr,
96
211
  exitCode: code ?? 0,
212
+ usage,
213
+ telemetry,
97
214
  });
98
215
  }
99
216
  catch (error) {
100
- reject(error);
217
+ reject(new AgentInvocationFailure(error.message, stdout, stderr, sessionId, usage, telemetry));
101
218
  }
102
219
  });
103
- child.stdin?.end(invocation.prompt);
220
+ child.stdin?.end(agentProcessInput(invocation));
104
221
  function onStalled() {
222
+ if (finished)
223
+ return;
224
+ const slept = accountClockGap();
225
+ if (slept) {
226
+ clearTimeout(stalledTimer);
227
+ stalledTimer = setTimeout(onStalled, invocation.timeout.stalledMs);
228
+ return;
229
+ }
105
230
  if (Date.now() - lastOutputAt < invocation.timeout.stalledMs)
106
231
  return;
107
232
  stopChild(child.pid);
@@ -112,15 +237,167 @@ async function runAgentProcess(invocation) {
112
237
  return;
113
238
  finished = true;
114
239
  cleanupTimers();
115
- reject(error);
240
+ reject(error instanceof AgentInvocationFailure
241
+ ? error
242
+ : new AgentInvocationFailure(error.message, stdout, stderr, sessionId, usage, telemetry));
116
243
  }
117
244
  function cleanupTimers() {
118
245
  clearTimeout(warningTimer);
119
246
  clearTimeout(stalledTimer);
120
- clearTimeout(hardTimer);
247
+ clearInterval(hardTimer);
248
+ clearInterval(heartbeatTimer);
249
+ clearInterval(supervisionTimer);
250
+ invocation.onProcess?.(null);
251
+ }
252
+ function accountClockGap() {
253
+ const now = Date.now();
254
+ const delta = Math.max(0, now - lastClockCheckAt);
255
+ lastClockCheckAt = now;
256
+ const slept = delta > 5_000;
257
+ if (slept) {
258
+ lastOutputAt += delta;
259
+ return true;
260
+ }
261
+ activeInvocationMs += delta;
262
+ return false;
121
263
  }
122
264
  });
123
265
  }
266
+ export function buildAgentEnvironment(invocation, baseEnv = process.env) {
267
+ return {
268
+ ...baseEnv,
269
+ PATH: managedExecutablePath(baseEnv.PATH),
270
+ SUPERFLOW_MANAGED_TASK_ID: invocation.taskId,
271
+ SUPERFLOW_MANAGED_RUN_ID: invocation.runId,
272
+ SUPERFLOW_MANAGED_ROLE: invocation.role,
273
+ SUPERFLOW_MANAGED_PROJECT_ROOT: invocation.projectRoot,
274
+ SUPERFLOW_MANAGED_CONTEXT_MANIFEST: path.join(invocation.projectRoot, ".superflow", "tasks", invocation.taskId, "context-manifest.json"),
275
+ ...(invocation.agent === "claude" && invocation.role === "executor"
276
+ ? {
277
+ CLAUDE_CODE_AUTO_COMPACT_WINDOW: baseEnv.CLAUDE_CODE_AUTO_COMPACT_WINDOW ??
278
+ CLAUDE_MANAGED_AUTO_COMPACT_WINDOW,
279
+ CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: baseEnv.CLAUDE_AUTOCOMPACT_PCT_OVERRIDE ??
280
+ CLAUDE_MANAGED_AUTO_COMPACT_PERCENT,
281
+ }
282
+ : {}),
283
+ };
284
+ }
285
+ export function initialTelemetry() {
286
+ const now = new Date().toISOString();
287
+ return {
288
+ phase: "model_waiting",
289
+ model: null,
290
+ tools: [],
291
+ toolUses: [],
292
+ plugins: [],
293
+ lastOutputAt: now,
294
+ lastProgressAt: now,
295
+ lastProgressReason: "invocation_started",
296
+ permissionDenials: 0,
297
+ };
298
+ }
299
+ function emptyUsage() {
300
+ return {
301
+ inputTokens: null,
302
+ outputTokens: null,
303
+ cacheReadTokens: null,
304
+ cacheWriteTokens: null,
305
+ costUsd: null,
306
+ };
307
+ }
308
+ export function updateTelemetry(current, event) {
309
+ const now = new Date().toISOString();
310
+ const next = { ...current, lastOutputAt: now };
311
+ if (Array.isArray(event.permission_denials)) {
312
+ next.permissionDenials = Math.max(next.permissionDenials ?? 0, event.permission_denials.length);
313
+ }
314
+ if (event.type === "system" && event.subtype === "init") {
315
+ next.model = stringValue(event.model) ?? next.model;
316
+ next.tools = stringArray(event.tools);
317
+ next.plugins = stringArray(event.plugins);
318
+ next.lastProgressAt = now;
319
+ next.lastProgressReason = "system_init";
320
+ }
321
+ const toolNames = extractToolNames(event);
322
+ if (event.type === "result") {
323
+ next.phase = "model_waiting";
324
+ next.lastProgressAt = now;
325
+ next.lastProgressReason = "agent_result";
326
+ }
327
+ else if (toolNames.length > 0) {
328
+ next.toolUses = [...new Set([...next.toolUses, ...toolNames])];
329
+ next.phase = toolNames.some((name) => /^(write|edit|apply_patch)$/i.test(name))
330
+ ? "workspace_writing"
331
+ : "tool_running";
332
+ next.lastProgressAt = now;
333
+ next.lastProgressReason = `tool_use:${toolNames.join(",")}`;
334
+ }
335
+ else if (/permission|approval/i.test(`${event.type} ${event.subtype}`)) {
336
+ next.phase = "awaiting_local_approval";
337
+ next.lastProgressReason = "local_approval";
338
+ }
339
+ else if (event.type === "assistant" || event.type === "message") {
340
+ next.phase = "model_waiting";
341
+ next.lastProgressAt = now;
342
+ next.lastProgressReason = "model_output";
343
+ }
344
+ return next;
345
+ }
346
+ function extractToolNames(value) {
347
+ if (!value || typeof value !== "object")
348
+ return [];
349
+ const object = value;
350
+ const names = [];
351
+ if ((object.type === "tool_use" || object.type === "tool_call") &&
352
+ typeof object.name === "string") {
353
+ names.push(object.name);
354
+ }
355
+ for (const child of Object.values(object)) {
356
+ if (Array.isArray(child)) {
357
+ for (const item of child)
358
+ names.push(...extractToolNames(item));
359
+ }
360
+ else if (child && typeof child === "object") {
361
+ names.push(...extractToolNames(child));
362
+ }
363
+ }
364
+ return names;
365
+ }
366
+ function extractUsage(event) {
367
+ const usage = event.usage && typeof event.usage === "object"
368
+ ? event.usage
369
+ : {};
370
+ return {
371
+ inputTokens: numberValue(usage.input_tokens ?? usage.inputTokens),
372
+ outputTokens: numberValue(usage.output_tokens ?? usage.outputTokens),
373
+ cacheReadTokens: numberValue(usage.cache_read_input_tokens ?? usage.cacheReadTokens),
374
+ cacheWriteTokens: numberValue(usage.cache_creation_input_tokens ?? usage.cacheWriteTokens),
375
+ costUsd: numberValue(event.total_cost_usd ?? event.cost_usd),
376
+ };
377
+ }
378
+ export function updateAgentUsage(current, event) {
379
+ return mergeUsage(current, extractUsage(event));
380
+ }
381
+ function mergeUsage(current, next) {
382
+ return {
383
+ inputTokens: next.inputTokens ?? current.inputTokens,
384
+ outputTokens: next.outputTokens ?? current.outputTokens,
385
+ cacheReadTokens: next.cacheReadTokens ?? current.cacheReadTokens,
386
+ cacheWriteTokens: next.cacheWriteTokens ?? current.cacheWriteTokens,
387
+ costUsd: next.costUsd ?? current.costUsd,
388
+ };
389
+ }
390
+ function stringValue(value) {
391
+ return typeof value === "string" && value.trim() ? value : null;
392
+ }
393
+ function stringArray(value) {
394
+ if (!Array.isArray(value))
395
+ return [];
396
+ return value.filter((item) => typeof item === "string");
397
+ }
398
+ function numberValue(value) {
399
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
400
+ }
124
401
  export function formatAgentFailure(agent, code, stdout, stderr, language) {
125
402
  const details = [stderr.trim(), extractFailureFromEvents(stdout)]
126
403
  .filter(Boolean)
@@ -135,11 +412,26 @@ function extractFailureFromEvents(stdout) {
135
412
  const event = safeJson(lines[index]);
136
413
  if (!event)
137
414
  continue;
138
- for (const key of ["error", "message", "result", "reason"]) {
415
+ for (const key of [
416
+ "error",
417
+ "message",
418
+ "result",
419
+ "reason",
420
+ "terminal_reason",
421
+ "subtype",
422
+ "stop_reason",
423
+ ]) {
139
424
  const value = event[key];
140
425
  if (typeof value === "string" && value.trim())
141
426
  messages.push(value.trim());
142
427
  }
428
+ if (Array.isArray(event.errors)) {
429
+ for (const value of event.errors) {
430
+ if (typeof value === "string" && value.trim()) {
431
+ messages.push(value.trim());
432
+ }
433
+ }
434
+ }
143
435
  }
144
436
  return messages.join("\n");
145
437
  }
@@ -156,6 +448,7 @@ function buildCodexCommand(invocation) {
156
448
  args: [
157
449
  "exec",
158
450
  "resume",
451
+ ...(invocation.role === "executor" ? ["--disable", "memories"] : []),
159
452
  invocation.sessionId,
160
453
  "--json",
161
454
  "--output-schema",
@@ -176,6 +469,7 @@ function buildCodexCommand(invocation) {
176
469
  command: "codex",
177
470
  args: [
178
471
  "exec",
472
+ ...(invocation.role === "executor" ? ["--disable", "memories"] : []),
179
473
  "-C",
180
474
  invocation.projectRoot,
181
475
  "--sandbox",
@@ -194,7 +488,7 @@ function buildCodexCommand(invocation) {
194
488
  }
195
489
  function buildClaudeCommand(invocation) {
196
490
  const schema = readFileSync(invocation.schemaPath, "utf-8");
197
- const args = ["-p"];
491
+ const args = ["--bare", "-p"];
198
492
  let initialSessionId = invocation.sessionId;
199
493
  if (invocation.sessionId)
200
494
  args.push("--resume", invocation.sessionId);
@@ -202,11 +496,98 @@ function buildClaudeCommand(invocation) {
202
496
  initialSessionId = cryptoRandomUuid();
203
497
  args.push("--session-id", initialSessionId);
204
498
  }
205
- args.push("--output-format", "stream-json", "--verbose", "--json-schema", schema, "--permission-mode", invocation.role === "supervisor" ? "plan" : "auto");
499
+ args.push(...(invocation.systemPromptPath
500
+ ? ["--append-system-prompt-file", invocation.systemPromptPath]
501
+ : []), "--output-format", "stream-json", "--verbose", "--include-partial-messages", "--json-schema", schema, "--tools", claudeTools(invocation.role));
502
+ if (invocation.role === "executor") {
503
+ args.push("--dangerously-skip-permissions");
504
+ }
505
+ else {
506
+ args.push("--permission-mode", "dontAsk", "--allowedTools", ...claudeAllowedTools(invocation.role));
507
+ }
508
+ if (invocation.role === "supervisor") {
509
+ args.push("--max-turns", "20");
510
+ }
206
511
  for (const root of invocation.writableRoots)
207
512
  args.push("--add-dir", root);
208
513
  return { command: "claude", args, outputFile: null, initialSessionId };
209
514
  }
515
+ export function agentProcessInput(invocation) {
516
+ if (invocation.agent === "codex")
517
+ return invocation.prompt;
518
+ if (invocation.agent === "claude" && invocation.promptPath) {
519
+ return `Read ${invocation.promptPath} completely and execute it.`;
520
+ }
521
+ return invocation.prompt;
522
+ }
523
+ function claudeTools(role) {
524
+ return role === "supervisor"
525
+ ? "Read,Grep,Glob,Bash"
526
+ : "Read,Write,Edit,Grep,Glob,Bash";
527
+ }
528
+ function claudeAllowedTools(role) {
529
+ const readOnly = [
530
+ "Read",
531
+ "Grep",
532
+ "Glob",
533
+ "Bash(git diff *)",
534
+ "Bash(git status *)",
535
+ "Bash(git log *)",
536
+ "Bash(git show *)",
537
+ "Bash(xmllint *)",
538
+ ];
539
+ if (role === "supervisor")
540
+ return readOnly;
541
+ return [
542
+ ...readOnly,
543
+ "Write",
544
+ "Edit",
545
+ "Bash(mvn *)",
546
+ "Bash(JAVA_HOME=* mvn *)",
547
+ "Bash(JAVA_HOME=* */mvn *)",
548
+ "Bash(java *)",
549
+ "Bash(JAVA_HOME=* java *)",
550
+ "Bash(/usr/libexec/java_home *)",
551
+ "Bash(command -v java*)",
552
+ "Bash(command -v mvn*)",
553
+ "Bash(which java*)",
554
+ "Bash(which mvn*)",
555
+ "Bash(npm *)",
556
+ "Bash(npx *)",
557
+ "Bash(pnpm *)",
558
+ "Bash(yarn *)",
559
+ "Bash(gradle *)",
560
+ "Bash(./gradlew *)",
561
+ "Bash(openspec *)",
562
+ "Bash(bash .claude/scripts/superflow-*.sh)",
563
+ "Bash(bash .claude/scripts/superflow-*.sh *)",
564
+ "Bash(python3 .claude/scripts/superflow-*.py)",
565
+ "Bash(python3 .claude/scripts/superflow-*.py *)",
566
+ "Bash(node .claude/scripts/superflow-*.mjs)",
567
+ "Bash(node .claude/scripts/superflow-*.mjs *)",
568
+ "Bash(PORT=* npm *)",
569
+ "Bash(node --version*)",
570
+ "Bash(npm --version*)",
571
+ "Bash(curl --version*)",
572
+ "Bash(which curl*)",
573
+ "Bash(command -v curl*)",
574
+ "Bash(curl http://localhost*)",
575
+ "Bash(curl http://127.0.0.1*)",
576
+ "Bash(lsof *)",
577
+ "Bash(ps *)",
578
+ "Bash(kill *)",
579
+ ];
580
+ }
581
+ export function shouldStopClaudeRetry(event) {
582
+ if (event?.type !== "system" ||
583
+ event.subtype !== "api_retry" ||
584
+ typeof event.attempt !== "number" ||
585
+ event.attempt < 3) {
586
+ return false;
587
+ }
588
+ const detail = `${String(event.error_status ?? "")} ${String(event.error ?? "")}`;
589
+ return /\b(?:429|529)\b|overloaded|rate.?limit|访问量过大|暂时.*限流/i.test(detail);
590
+ }
210
591
  function extractStructuredOutput(agent, stdout, outputFile, language) {
211
592
  if (agent === "codex" && outputFile && existsSync(outputFile)) {
212
593
  return parseStructuredValue(readFileSync(outputFile, "utf-8"), language);
@@ -218,9 +599,40 @@ function extractStructuredOutput(agent, stdout, outputFile, language) {
218
599
  if (event[key] !== undefined)
219
600
  return parseStructuredValue(event[key], language);
220
601
  }
602
+ const text = extractEventText(event);
603
+ if (text) {
604
+ try {
605
+ return parseStructuredValue(text, language);
606
+ }
607
+ catch {
608
+ // Continue to earlier events before falling back to the whole stream.
609
+ }
610
+ }
221
611
  }
222
612
  return parseStructuredValue(stdout, language);
223
613
  }
614
+ function extractEventText(value) {
615
+ if (!value || typeof value !== "object")
616
+ return null;
617
+ const object = value;
618
+ if (typeof object.text === "string" && object.text.trim())
619
+ return object.text;
620
+ for (const child of Object.values(object)) {
621
+ if (Array.isArray(child)) {
622
+ for (let index = child.length - 1; index >= 0; index--) {
623
+ const text = extractEventText(child[index]);
624
+ if (text)
625
+ return text;
626
+ }
627
+ }
628
+ else if (child && typeof child === "object") {
629
+ const text = extractEventText(child);
630
+ if (text)
631
+ return text;
632
+ }
633
+ }
634
+ return null;
635
+ }
224
636
  function parseStructuredValue(value, language) {
225
637
  if (typeof value === "object" && value !== null)
226
638
  return value;
@@ -251,7 +663,13 @@ function extractSessionId(value) {
251
663
  if (!value || typeof value !== "object")
252
664
  return null;
253
665
  const object = value;
254
- for (const key of ["session_id", "sessionId", "thread_id", "threadId"]) {
666
+ for (const key of [
667
+ "session_id",
668
+ "sessionId",
669
+ "sessionID",
670
+ "thread_id",
671
+ "threadId",
672
+ ]) {
255
673
  if (typeof object[key] === "string" && isUuid(object[key]))
256
674
  return object[key];
257
675
  }
@@ -262,17 +680,75 @@ function extractSessionId(value) {
262
680
  }
263
681
  return null;
264
682
  }
265
- function extractProgress(value) {
683
+ export function extractProgress(value) {
266
684
  if (!value || typeof value !== "object")
267
685
  return null;
686
+ const stageHint = extractToolStageHint(value);
687
+ if (stageHint)
688
+ return `superflow-stage:${stageHint}`;
268
689
  const object = value;
269
690
  for (const key of ["message", "summary", "text"]) {
270
691
  if (typeof object[key] === "string" && object[key].length > 0) {
271
- return object[key].slice(0, 240);
692
+ return object[key];
693
+ }
694
+ }
695
+ return null;
696
+ }
697
+ function extractToolStageHint(value) {
698
+ if (!value || typeof value !== "object")
699
+ return null;
700
+ const object = value;
701
+ const type = stringValue(object.type);
702
+ const name = stringValue(object.name);
703
+ if (["tool_use", "tool_call"].includes(type ?? "") &&
704
+ /^(?:write|edit|apply_patch)$/i.test(name ?? "")) {
705
+ return "implementation";
706
+ }
707
+ if (["tool_use", "tool_call"].includes(type ?? "") &&
708
+ /^(?:bash|shell|terminal)$/i.test(name ?? "")) {
709
+ const input = object.input;
710
+ const command = input && typeof input === "object"
711
+ ? stringValue(input.command)
712
+ : null;
713
+ if (command)
714
+ return stageFromCommand(command);
715
+ }
716
+ for (const child of Object.values(object)) {
717
+ if (Array.isArray(child)) {
718
+ for (const item of child) {
719
+ const stage = extractToolStageHint(item);
720
+ if (stage)
721
+ return stage;
722
+ }
723
+ }
724
+ else if (child && typeof child === "object") {
725
+ const stage = extractToolStageHint(child);
726
+ if (stage)
727
+ return stage;
272
728
  }
273
729
  }
274
730
  return null;
275
731
  }
732
+ function stageFromCommand(command) {
733
+ const value = command.toLowerCase();
734
+ if (/preflight|diff --check/.test(value))
735
+ return "delivery_self_check";
736
+ if (/drop table|redis.*(?:del|exists)|kill|pkill|cleanup/.test(value)) {
737
+ return "cleanup";
738
+ }
739
+ if (/\bcurl\b|\bwget\b|http:\/\/|https:\/\//.test(value)) {
740
+ return "http_e2e";
741
+ }
742
+ if (/java -jar|spring-boot:run|bootrun|npm run (?:dev|start)/.test(value)) {
743
+ return "application_startup";
744
+ }
745
+ if (/mvn.*package|gradle.*build|npm.*build/.test(value))
746
+ return "package";
747
+ if (/mvn.*test|gradle.*test|vitest|jest|pytest/.test(value)) {
748
+ return "unit_test";
749
+ }
750
+ return null;
751
+ }
276
752
  function safeJson(value) {
277
753
  try {
278
754
  return JSON.parse(value);
@@ -282,22 +758,7 @@ function safeJson(value) {
282
758
  }
283
759
  }
284
760
  function stopChild(pid) {
285
- if (!pid)
286
- return;
287
- try {
288
- if (process.platform === "win32")
289
- process.kill(pid, "SIGTERM");
290
- else
291
- process.kill(-pid, "SIGTERM");
292
- }
293
- catch {
294
- try {
295
- process.kill(pid, "SIGTERM");
296
- }
297
- catch {
298
- // Process already exited.
299
- }
300
- }
761
+ stopProcessTree(pid);
301
762
  }
302
763
  function isUuid(value) {
303
764
  return (typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f-]{27}$/i.test(value));