@chenmk/superflow 0.3.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.
- package/INSTALL.en.md +54 -12
- package/INSTALL.md +59 -33
- package/README.md +192 -49
- package/README.zh-CN.md +166 -46
- package/assets/brand/managed-review-loop.png +0 -0
- package/assets/manifest.json +15 -9
- package/assets/prompts/superflow-design.md +3 -0
- package/assets/prompts/superflow-docs.md +4 -1
- package/assets/scripts/superflow-coding-ready.mjs +135 -0
- package/assets/scripts/superflow-dependency-update-hook.sh +81 -8
- package/assets/scripts/superflow-document-audit.mjs +240 -0
- package/assets/scripts/superflow-environment-preflight.mjs +398 -0
- package/assets/scripts/superflow-hook-guard.sh +40 -1
- package/assets/scripts/superflow-managed-command-guard.py +309 -0
- package/assets/scripts/superflow-managed-executor-preflight.mjs +421 -0
- package/assets/scripts/superflow-managed-owner-verification.sh +117 -0
- package/assets/scripts/superflow-managed-work-check.mjs +455 -0
- package/assets/scripts/superflow-managed-work-guard.sh +17 -0
- package/assets/skills/openspec-propose/SKILL.md +11 -0
- package/assets/skills/superflow-clarify/SKILL.md +14 -0
- package/assets/skills/superflow-design/SKILL.md +46 -12
- package/assets/skills/superflow-docs/SKILL.md +109 -2
- package/assets/skills/superflow-pipeline/SKILL.md +205 -2
- package/assets/skills/superflow-pipeline/references/batch-prompt-template.md +17 -9
- package/assets/skills/superflow-pipeline/references/document-readiness.md +157 -0
- package/assets/skills/superflow-pipeline/references/document-templates.md +17 -5
- package/assets/skills/superflow-pipeline/references/feature-gated-workflow.md +14 -0
- package/assets/skills/superflow-pipeline/references/implementation-prompt-template.md +20 -11
- package/assets/skills/superflow-pipeline/references/managed-work.md +166 -0
- package/assets/skills/superflow-pipeline/references/orchestration.md +2 -2
- package/assets/skills/superflow-pipeline/references/p0-baseline-template.md +3 -2
- package/assets/skills/superflow-pipeline/references/quality-gate.md +8 -5
- package/assets/skills/superflow-pipeline/references/reviewer-checklist.md +7 -0
- package/assets/skills/superflow-pipeline/references/superpower-technical-design-template.md +10 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-archive.sh +1 -1
- package/assets/skills/superflow-pipeline/scripts/superflow-guard.sh +282 -16
- package/assets/skills/superflow-pipeline/scripts/superflow-handoff.sh +7 -4
- package/assets/skills/superflow-requirement-review/SKILL.md +178 -0
- package/assets/skills/superflow-requirement-review/agents/openai.yaml +4 -0
- package/assets/skills-en/openspec-propose/SKILL.md +14 -0
- package/assets/skills-en/superflow-clarify/SKILL.md +17 -0
- package/assets/skills-en/superflow-design/SKILL.md +40 -11
- package/assets/skills-en/superflow-docs/SKILL.md +115 -0
- package/assets/skills-en/superflow-pipeline/SKILL.md +202 -0
- package/assets/skills-en/superflow-pipeline/references/batch-prompt-template.md +4 -0
- package/assets/skills-en/superflow-pipeline/references/document-readiness.md +125 -0
- package/assets/skills-en/superflow-pipeline/references/document-templates.md +15 -0
- package/assets/skills-en/superflow-pipeline/references/feature-gated-workflow.md +7 -0
- package/assets/skills-en/superflow-pipeline/references/implementation-prompt-template.md +7 -0
- package/assets/skills-en/superflow-pipeline/references/managed-work.md +184 -0
- package/assets/skills-en/superflow-pipeline/references/quality-gate.md +20 -0
- package/assets/skills-en/superflow-pipeline/references/reviewer-checklist.md +7 -0
- package/assets/skills-en/superflow-pipeline/references/superpower-technical-design-template.md +11 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-archive.sh +1 -1
- package/assets/skills-en/superflow-pipeline/scripts/superflow-guard.sh +279 -14
- package/assets/skills-en/superflow-pipeline/scripts/superflow-handoff.sh +7 -4
- package/assets/skills-en/superflow-requirement-review/SKILL.md +99 -0
- package/assets/skills-en/superflow-requirement-review/agents/openai.yaml +4 -0
- package/dist/app/cli.js +176 -103
- package/dist/app/cli.js.map +1 -1
- package/dist/app/commands/change-guard.js +19 -2
- package/dist/app/commands/change-guard.js.map +1 -1
- package/dist/app/commands/check.js +107 -45
- package/dist/app/commands/check.js.map +1 -1
- package/dist/app/commands/docs.js +7 -3
- package/dist/app/commands/docs.js.map +1 -1
- package/dist/app/commands/doctor.js +174 -198
- package/dist/app/commands/doctor.js.map +1 -1
- package/dist/app/commands/eval.js +64 -0
- package/dist/app/commands/eval.js.map +1 -0
- package/dist/app/commands/implement.js +7 -3
- package/dist/app/commands/implement.js.map +1 -1
- package/dist/app/commands/init.js +16 -24
- package/dist/app/commands/init.js.map +1 -1
- package/dist/app/commands/managed-service.js +5 -0
- package/dist/app/commands/managed-service.js.map +1 -0
- package/dist/app/commands/mcp.js +167 -0
- package/dist/app/commands/mcp.js.map +1 -0
- package/dist/app/commands/pipeline.js +757 -3
- package/dist/app/commands/pipeline.js.map +1 -1
- package/dist/app/commands/requirement-review.js +6 -0
- package/dist/app/commands/requirement-review.js.map +1 -0
- package/dist/app/commands/status.js +262 -77
- package/dist/app/commands/status.js.map +1 -1
- package/dist/app/commands/uninstall.js +8 -5
- package/dist/app/commands/uninstall.js.map +1 -1
- package/dist/app/commands/update.js +114 -15
- package/dist/app/commands/update.js.map +1 -1
- package/dist/domains/agent.js +3 -11
- package/dist/domains/agent.js.map +1 -1
- package/dist/domains/config/cli-help.js +145 -73
- package/dist/domains/config/cli-help.js.map +1 -1
- package/dist/domains/config/i18n.js +6 -8
- package/dist/domains/config/i18n.js.map +1 -1
- package/dist/domains/config/manifest.js +0 -3
- package/dist/domains/config/manifest.js.map +1 -1
- package/dist/domains/deps.js +70 -8
- package/dist/domains/deps.js.map +1 -1
- package/dist/domains/hook.js +71 -38
- package/dist/domains/hook.js.map +1 -1
- package/dist/domains/managed-work/completion-policy.js +174 -0
- package/dist/domains/managed-work/completion-policy.js.map +1 -0
- package/dist/domains/managed-work/context-manifest.js +208 -0
- package/dist/domains/managed-work/context-manifest.js.map +1 -0
- package/dist/domains/managed-work/contract.js +349 -0
- package/dist/domains/managed-work/contract.js.map +1 -0
- package/dist/domains/managed-work/control-signals.js +34 -0
- package/dist/domains/managed-work/control-signals.js.map +1 -0
- package/dist/domains/managed-work/control.js +628 -0
- package/dist/domains/managed-work/control.js.map +1 -0
- package/dist/domains/managed-work/evaluation.js +497 -0
- package/dist/domains/managed-work/evaluation.js.map +1 -0
- package/dist/domains/managed-work/evidence-path.js +14 -0
- package/dist/domains/managed-work/evidence-path.js.map +1 -0
- package/dist/domains/managed-work/execution-contract.js +167 -0
- package/dist/domains/managed-work/execution-contract.js.map +1 -0
- package/dist/domains/managed-work/executor-policy.js +65 -0
- package/dist/domains/managed-work/executor-policy.js.map +1 -0
- package/dist/domains/managed-work/failure.js +65 -0
- package/dist/domains/managed-work/failure.js.map +1 -0
- package/dist/domains/managed-work/host-review.js +77 -0
- package/dist/domains/managed-work/host-review.js.map +1 -0
- package/dist/domains/managed-work/human-messages.js +31 -0
- package/dist/domains/managed-work/human-messages.js.map +1 -0
- package/dist/domains/managed-work/i18n.js +12 -0
- package/dist/domains/managed-work/i18n.js.map +1 -0
- package/dist/domains/managed-work/input.js +137 -0
- package/dist/domains/managed-work/input.js.map +1 -0
- package/dist/domains/managed-work/journal.js +196 -0
- package/dist/domains/managed-work/journal.js.map +1 -0
- package/dist/domains/managed-work/lock.js +106 -0
- package/dist/domains/managed-work/lock.js.map +1 -0
- package/dist/domains/managed-work/notifications.js +38 -0
- package/dist/domains/managed-work/notifications.js.map +1 -0
- package/dist/domains/managed-work/pair-admission.js +108 -0
- package/dist/domains/managed-work/pair-admission.js.map +1 -0
- package/dist/domains/managed-work/paths.js +18 -0
- package/dist/domains/managed-work/paths.js.map +1 -0
- package/dist/domains/managed-work/prompts.js +453 -0
- package/dist/domains/managed-work/prompts.js.map +1 -0
- package/dist/domains/managed-work/redaction.js +90 -0
- package/dist/domains/managed-work/redaction.js.map +1 -0
- package/dist/domains/managed-work/review-convergence.js +41 -0
- package/dist/domains/managed-work/review-convergence.js.map +1 -0
- package/dist/domains/managed-work/review-facts.js +181 -0
- package/dist/domains/managed-work/review-facts.js.map +1 -0
- package/dist/domains/managed-work/rule-selection.js +84 -0
- package/dist/domains/managed-work/rule-selection.js.map +1 -0
- package/dist/domains/managed-work/runner.js +2418 -0
- package/dist/domains/managed-work/runner.js.map +1 -0
- package/dist/domains/managed-work/schemas.js +155 -0
- package/dist/domains/managed-work/schemas.js.map +1 -0
- package/dist/domains/managed-work/service.js +190 -0
- package/dist/domains/managed-work/service.js.map +1 -0
- package/dist/domains/managed-work/state.js +280 -0
- package/dist/domains/managed-work/state.js.map +1 -0
- package/dist/domains/managed-work/storage.js +223 -0
- package/dist/domains/managed-work/storage.js.map +1 -0
- package/dist/domains/managed-work/types.js +19 -0
- package/dist/domains/managed-work/types.js.map +1 -0
- package/dist/domains/managed-work/verification-categories.js +39 -0
- package/dist/domains/managed-work/verification-categories.js.map +1 -0
- package/dist/domains/managed-work/verification-policy.js +116 -0
- package/dist/domains/managed-work/verification-policy.js.map +1 -0
- package/dist/domains/managed-work/wait.js +58 -0
- package/dist/domains/managed-work/wait.js.map +1 -0
- package/dist/domains/managed-work/workspace-binding.js +133 -0
- package/dist/domains/managed-work/workspace-binding.js.map +1 -0
- package/dist/domains/sdd-readiness.js +44 -0
- package/dist/domains/sdd-readiness.js.map +1 -0
- package/dist/domains/skill/deploy.js +0 -6
- package/dist/domains/skill/deploy.js.map +1 -1
- package/dist/domains/skill/scripts.js +0 -8
- package/dist/domains/skill/scripts.js.map +1 -1
- package/dist/domains/state.js +1 -2
- package/dist/domains/state.js.map +1 -1
- package/dist/mcp/runtime-identity.js +21 -0
- package/dist/mcp/runtime-identity.js.map +1 -0
- package/dist/mcp/server.js +369 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/platform/agent-process.js +769 -0
- package/dist/platform/agent-process.js.map +1 -0
- package/dist/platform/executable.js +28 -0
- package/dist/platform/executable.js.map +1 -0
- package/dist/platform/paths.js +1 -17
- package/dist/platform/paths.js.map +1 -1
- package/dist/platform/process-liveness.js +10 -0
- package/dist/platform/process-liveness.js.map +1 -0
- package/dist/platform/process-tree.js +44 -0
- package/dist/platform/process-tree.js.map +1 -0
- package/dist/platform/process.js +1 -1
- package/dist/platform/process.js.map +1 -1
- package/dist/platform/version-check.js +30 -9
- package/dist/platform/version-check.js.map +1 -1
- package/package.json +16 -7
- package/scripts/check-design-principles.mjs +107 -0
- package/scripts/check-managed-work-design.mjs +507 -0
- package/scripts/clean-dist.js +9 -0
- package/scripts/ensure-bin-modes.js +11 -0
- package/dist/cli/index.js +0 -193
- package/dist/cli/index.js.map +0 -1
- package/dist/commands/archive.js +0 -6
- package/dist/commands/archive.js.map +0 -1
- package/dist/commands/clarify.js +0 -6
- package/dist/commands/clarify.js.map +0 -1
- package/dist/commands/design.js +0 -28
- package/dist/commands/design.js.map +0 -1
- package/dist/commands/docs.js +0 -6
- package/dist/commands/docs.js.map +0 -1
- package/dist/commands/doctor.js +0 -494
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/implement.js +0 -6
- package/dist/commands/implement.js.map +0 -1
- package/dist/commands/init.js +0 -520
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/pipeline.js +0 -6
- package/dist/commands/pipeline.js.map +0 -1
- package/dist/commands/scan.js +0 -63
- package/dist/commands/scan.js.map +0 -1
- package/dist/commands/status.js +0 -173
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/uninstall.js +0 -214
- package/dist/commands/uninstall.js.map +0 -1
- package/dist/commands/update.js +0 -193
- package/dist/commands/update.js.map +0 -1
- package/dist/commands/verify.js +0 -6
- package/dist/commands/verify.js.map +0 -1
- package/dist/core/assets.js +0 -27
- package/dist/core/assets.js.map +0 -1
- package/dist/core/cli-help.js +0 -98
- package/dist/core/cli-help.js.map +0 -1
- package/dist/core/context.js +0 -123
- package/dist/core/context.js.map +0 -1
- package/dist/core/dependencies.js +0 -146
- package/dist/core/dependencies.js.map +0 -1
- package/dist/core/detect.js +0 -64
- package/dist/core/detect.js.map +0 -1
- package/dist/core/i18n.js +0 -105
- package/dist/core/i18n.js.map +0 -1
- package/dist/core/integrity.js +0 -46
- package/dist/core/integrity.js.map +0 -1
- package/dist/core/manifest.js +0 -21
- package/dist/core/manifest.js.map +0 -1
- package/dist/core/prompts.js +0 -20
- package/dist/core/prompts.js.map +0 -1
- package/dist/core/registry.js +0 -134
- package/dist/core/registry.js.map +0 -1
- package/dist/core/rules.js +0 -17
- package/dist/core/rules.js.map +0 -1
- package/dist/core/scripts.js +0 -48
- package/dist/core/scripts.js.map +0 -1
- package/dist/core/skill-check.js +0 -32
- package/dist/core/skill-check.js.map +0 -1
- package/dist/core/skills.js +0 -62
- package/dist/core/skills.js.map +0 -1
- package/dist/core/state.js +0 -54
- package/dist/core/state.js.map +0 -1
- package/dist/utils/path.js +0 -11
- package/dist/utils/path.js.map +0 -1
- package/dist/utils/shell.js +0 -29
- package/dist/utils/shell.js.map +0 -1
|
@@ -0,0 +1,769 @@
|
|
|
1
|
+
import { existsSync, readFileSync, unlinkSync } from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import spawn from "cross-spawn";
|
|
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
|
+
}
|
|
32
|
+
export class LocalAgentInvoker {
|
|
33
|
+
async invoke(invocation) {
|
|
34
|
+
return runAgentProcess(invocation);
|
|
35
|
+
}
|
|
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
|
+
}
|
|
70
|
+
export function buildAgentCommand(invocation) {
|
|
71
|
+
if (invocation.agent === "codex")
|
|
72
|
+
return buildCodexCommand(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
|
+
};
|
|
84
|
+
}
|
|
85
|
+
async function runAgentProcess(invocation) {
|
|
86
|
+
const command = buildAgentCommand(invocation);
|
|
87
|
+
const environment = buildAgentEnvironment(invocation);
|
|
88
|
+
const executable = resolveExecutableShim(command.command, environment);
|
|
89
|
+
const processCommand = wrapWithSleepPrevention(executable, command.args);
|
|
90
|
+
if (command.outputFile && existsSync(command.outputFile))
|
|
91
|
+
unlinkSync(command.outputFile);
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
const child = spawn(processCommand.command, processCommand.args, {
|
|
94
|
+
cwd: invocation.projectRoot,
|
|
95
|
+
env: environment,
|
|
96
|
+
detached: process.platform !== "win32",
|
|
97
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
98
|
+
shell: false,
|
|
99
|
+
});
|
|
100
|
+
let stdout = "";
|
|
101
|
+
let stderr = "";
|
|
102
|
+
let lineBuffer = "";
|
|
103
|
+
let sessionId = invocation.sessionId;
|
|
104
|
+
let lastOutputAt = Date.now();
|
|
105
|
+
let lastClockCheckAt = lastOutputAt;
|
|
106
|
+
let activeInvocationMs = 0;
|
|
107
|
+
let finished = false;
|
|
108
|
+
let telemetry = initialTelemetry();
|
|
109
|
+
let usage = emptyUsage();
|
|
110
|
+
invocation.onTelemetry?.(telemetry);
|
|
111
|
+
invocation.onProcess?.(child.pid ?? null);
|
|
112
|
+
const warningTimer = setTimeout(() => {
|
|
113
|
+
if (finished)
|
|
114
|
+
return;
|
|
115
|
+
accountClockGap();
|
|
116
|
+
invocation.onProgress?.(managedText(invocation.language, "连续一段时间没有新进展,后台仍在观察进程", "No new progress for a while; the background service is still observing the process"));
|
|
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);
|
|
136
|
+
let stalledTimer = setTimeout(onStalled, invocation.timeout.stalledMs);
|
|
137
|
+
const hardTimer = setInterval(() => {
|
|
138
|
+
if (finished)
|
|
139
|
+
return;
|
|
140
|
+
accountClockGap();
|
|
141
|
+
if (activeInvocationMs < invocation.timeout.hardMs)
|
|
142
|
+
return;
|
|
143
|
+
stopChild(child.pid);
|
|
144
|
+
finishError(new Error(managedText(invocation.language, `单次 Agent 调用超过 ${invocation.timeout.hardMs}ms`, `Single Agent invocation exceeded ${invocation.timeout.hardMs}ms`)));
|
|
145
|
+
}, 250);
|
|
146
|
+
if (invocation.sessionId)
|
|
147
|
+
invocation.onSession?.(invocation.sessionId);
|
|
148
|
+
child.stdout?.on("data", (chunk) => {
|
|
149
|
+
if (finished)
|
|
150
|
+
return;
|
|
151
|
+
accountClockGap();
|
|
152
|
+
const text = chunk.toString();
|
|
153
|
+
stdout += text;
|
|
154
|
+
lastOutputAt = Date.now();
|
|
155
|
+
clearTimeout(stalledTimer);
|
|
156
|
+
stalledTimer = setTimeout(onStalled, invocation.timeout.stalledMs);
|
|
157
|
+
lineBuffer += text;
|
|
158
|
+
const lines = lineBuffer.split(/\r?\n/);
|
|
159
|
+
lineBuffer = lines.pop() ?? "";
|
|
160
|
+
for (const line of lines) {
|
|
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
|
+
}
|
|
172
|
+
const discoveredSession = extractSessionId(event);
|
|
173
|
+
if (!sessionId && discoveredSession) {
|
|
174
|
+
sessionId = discoveredSession;
|
|
175
|
+
invocation.onSession?.(discoveredSession);
|
|
176
|
+
}
|
|
177
|
+
const progress = extractProgress(event);
|
|
178
|
+
if (progress)
|
|
179
|
+
invocation.onProgress?.(progress);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
child.stderr?.on("data", (chunk) => {
|
|
183
|
+
if (finished)
|
|
184
|
+
return;
|
|
185
|
+
accountClockGap();
|
|
186
|
+
stderr += chunk.toString();
|
|
187
|
+
lastOutputAt = Date.now();
|
|
188
|
+
});
|
|
189
|
+
child.on("error", finishError);
|
|
190
|
+
child.on("close", (code) => {
|
|
191
|
+
if (finished)
|
|
192
|
+
return;
|
|
193
|
+
cleanupTimers();
|
|
194
|
+
finished = true;
|
|
195
|
+
if ((code ?? 1) !== 0) {
|
|
196
|
+
reject(new AgentInvocationFailure(formatAgentFailure(invocation.agent, code, stdout, stderr, invocation.language), stdout, stderr, sessionId, usage, telemetry));
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
const output = extractStructuredOutput(invocation.agent, stdout, command.outputFile, invocation.language);
|
|
201
|
+
const resolvedSession = sessionId ??
|
|
202
|
+
extractSessionFromLines(stdout) ??
|
|
203
|
+
command.initialSessionId;
|
|
204
|
+
if (!resolvedSession)
|
|
205
|
+
throw new Error(managedText(invocation.language, "Agent 输出中缺少可恢复 session ID", "Agent output is missing a resumable session ID"));
|
|
206
|
+
resolve({
|
|
207
|
+
sessionId: resolvedSession,
|
|
208
|
+
output,
|
|
209
|
+
stdout,
|
|
210
|
+
stderr,
|
|
211
|
+
exitCode: code ?? 0,
|
|
212
|
+
usage,
|
|
213
|
+
telemetry,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
reject(new AgentInvocationFailure(error.message, stdout, stderr, sessionId, usage, telemetry));
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
child.stdin?.end(agentProcessInput(invocation));
|
|
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
|
+
}
|
|
230
|
+
if (Date.now() - lastOutputAt < invocation.timeout.stalledMs)
|
|
231
|
+
return;
|
|
232
|
+
stopChild(child.pid);
|
|
233
|
+
finishError(new Error(managedText(invocation.language, "Agent 长时间没有输出,已保存现场并中断", "Agent produced no output for too long; state was preserved and the process was interrupted")));
|
|
234
|
+
}
|
|
235
|
+
function finishError(error) {
|
|
236
|
+
if (finished)
|
|
237
|
+
return;
|
|
238
|
+
finished = true;
|
|
239
|
+
cleanupTimers();
|
|
240
|
+
reject(error instanceof AgentInvocationFailure
|
|
241
|
+
? error
|
|
242
|
+
: new AgentInvocationFailure(error.message, stdout, stderr, sessionId, usage, telemetry));
|
|
243
|
+
}
|
|
244
|
+
function cleanupTimers() {
|
|
245
|
+
clearTimeout(warningTimer);
|
|
246
|
+
clearTimeout(stalledTimer);
|
|
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;
|
|
263
|
+
}
|
|
264
|
+
});
|
|
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
|
+
}
|
|
401
|
+
export function formatAgentFailure(agent, code, stdout, stderr, language) {
|
|
402
|
+
const details = [stderr.trim(), extractFailureFromEvents(stdout)]
|
|
403
|
+
.filter(Boolean)
|
|
404
|
+
.join("\n")
|
|
405
|
+
.slice(-2_000);
|
|
406
|
+
return managedText(language, `${agent} 调用失败,退出码 ${code ?? 1}: ${redactFailure(details) || "未返回错误详情"}`, `${agent} invocation failed with exit code ${code ?? 1}: ${redactFailure(details) || "no error details returned"}`);
|
|
407
|
+
}
|
|
408
|
+
function extractFailureFromEvents(stdout) {
|
|
409
|
+
const lines = stdout.split(/\r?\n/).filter(Boolean);
|
|
410
|
+
const messages = [];
|
|
411
|
+
for (let index = lines.length - 1; index >= 0 && messages.length < 4; index--) {
|
|
412
|
+
const event = safeJson(lines[index]);
|
|
413
|
+
if (!event)
|
|
414
|
+
continue;
|
|
415
|
+
for (const key of [
|
|
416
|
+
"error",
|
|
417
|
+
"message",
|
|
418
|
+
"result",
|
|
419
|
+
"reason",
|
|
420
|
+
"terminal_reason",
|
|
421
|
+
"subtype",
|
|
422
|
+
"stop_reason",
|
|
423
|
+
]) {
|
|
424
|
+
const value = event[key];
|
|
425
|
+
if (typeof value === "string" && value.trim())
|
|
426
|
+
messages.push(value.trim());
|
|
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
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return messages.join("\n");
|
|
437
|
+
}
|
|
438
|
+
function redactFailure(value) {
|
|
439
|
+
return value
|
|
440
|
+
.replace(/(authorization\s*[:=]\s*bearer\s+)[^\s"']+/gi, "$1<redacted>")
|
|
441
|
+
.replace(/(["']?(?:token|password|secret|cookie|api[_-]?key)["']?\s*[:=]\s*["']?)[^\s"',}]+/gi, "$1<redacted>");
|
|
442
|
+
}
|
|
443
|
+
function buildCodexCommand(invocation) {
|
|
444
|
+
const outputFile = path.join(path.dirname(invocation.schemaPath), `${invocation.role}-last.json`);
|
|
445
|
+
if (invocation.sessionId) {
|
|
446
|
+
return {
|
|
447
|
+
command: "codex",
|
|
448
|
+
args: [
|
|
449
|
+
"exec",
|
|
450
|
+
"resume",
|
|
451
|
+
...(invocation.role === "executor" ? ["--disable", "memories"] : []),
|
|
452
|
+
invocation.sessionId,
|
|
453
|
+
"--json",
|
|
454
|
+
"--output-schema",
|
|
455
|
+
invocation.schemaPath,
|
|
456
|
+
"-o",
|
|
457
|
+
outputFile,
|
|
458
|
+
"-",
|
|
459
|
+
],
|
|
460
|
+
outputFile,
|
|
461
|
+
initialSessionId: invocation.sessionId,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
const additionalRoots = invocation.writableRoots.flatMap((root) => [
|
|
465
|
+
"--add-dir",
|
|
466
|
+
root,
|
|
467
|
+
]);
|
|
468
|
+
return {
|
|
469
|
+
command: "codex",
|
|
470
|
+
args: [
|
|
471
|
+
"exec",
|
|
472
|
+
...(invocation.role === "executor" ? ["--disable", "memories"] : []),
|
|
473
|
+
"-C",
|
|
474
|
+
invocation.projectRoot,
|
|
475
|
+
"--sandbox",
|
|
476
|
+
invocation.role === "supervisor" ? "read-only" : "workspace-write",
|
|
477
|
+
...additionalRoots,
|
|
478
|
+
"--json",
|
|
479
|
+
"--output-schema",
|
|
480
|
+
invocation.schemaPath,
|
|
481
|
+
"-o",
|
|
482
|
+
outputFile,
|
|
483
|
+
"-",
|
|
484
|
+
],
|
|
485
|
+
outputFile,
|
|
486
|
+
initialSessionId: null,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
function buildClaudeCommand(invocation) {
|
|
490
|
+
const schema = readFileSync(invocation.schemaPath, "utf-8");
|
|
491
|
+
const args = ["--bare", "-p"];
|
|
492
|
+
let initialSessionId = invocation.sessionId;
|
|
493
|
+
if (invocation.sessionId)
|
|
494
|
+
args.push("--resume", invocation.sessionId);
|
|
495
|
+
else {
|
|
496
|
+
initialSessionId = cryptoRandomUuid();
|
|
497
|
+
args.push("--session-id", initialSessionId);
|
|
498
|
+
}
|
|
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
|
+
}
|
|
511
|
+
for (const root of invocation.writableRoots)
|
|
512
|
+
args.push("--add-dir", root);
|
|
513
|
+
return { command: "claude", args, outputFile: null, initialSessionId };
|
|
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
|
+
}
|
|
591
|
+
function extractStructuredOutput(agent, stdout, outputFile, language) {
|
|
592
|
+
if (agent === "codex" && outputFile && existsSync(outputFile)) {
|
|
593
|
+
return parseStructuredValue(readFileSync(outputFile, "utf-8"), language);
|
|
594
|
+
}
|
|
595
|
+
const events = stdout.split(/\r?\n/).map(safeJson).filter(Boolean);
|
|
596
|
+
for (let index = events.length - 1; index >= 0; index--) {
|
|
597
|
+
const event = events[index];
|
|
598
|
+
for (const key of ["structured_output", "structuredOutput", "result"]) {
|
|
599
|
+
if (event[key] !== undefined)
|
|
600
|
+
return parseStructuredValue(event[key], language);
|
|
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
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return parseStructuredValue(stdout, language);
|
|
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
|
+
}
|
|
636
|
+
function parseStructuredValue(value, language) {
|
|
637
|
+
if (typeof value === "object" && value !== null)
|
|
638
|
+
return value;
|
|
639
|
+
if (typeof value !== "string") {
|
|
640
|
+
throw new Error(managedText(language, "Agent 未返回结构化结果", "Agent did not return structured output"));
|
|
641
|
+
}
|
|
642
|
+
const trimmed = value.trim();
|
|
643
|
+
try {
|
|
644
|
+
return JSON.parse(trimmed);
|
|
645
|
+
}
|
|
646
|
+
catch {
|
|
647
|
+
const match = trimmed.match(/\{[\s\S]*\}/);
|
|
648
|
+
if (!match) {
|
|
649
|
+
throw new Error(managedText(language, "Agent 最终输出不是 JSON", "Agent final output is not JSON"));
|
|
650
|
+
}
|
|
651
|
+
return JSON.parse(match[0]);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
function extractSessionFromLines(stdout) {
|
|
655
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
656
|
+
const sessionId = extractSessionId(safeJson(line));
|
|
657
|
+
if (sessionId)
|
|
658
|
+
return sessionId;
|
|
659
|
+
}
|
|
660
|
+
return null;
|
|
661
|
+
}
|
|
662
|
+
function extractSessionId(value) {
|
|
663
|
+
if (!value || typeof value !== "object")
|
|
664
|
+
return null;
|
|
665
|
+
const object = value;
|
|
666
|
+
for (const key of [
|
|
667
|
+
"session_id",
|
|
668
|
+
"sessionId",
|
|
669
|
+
"sessionID",
|
|
670
|
+
"thread_id",
|
|
671
|
+
"threadId",
|
|
672
|
+
]) {
|
|
673
|
+
if (typeof object[key] === "string" && isUuid(object[key]))
|
|
674
|
+
return object[key];
|
|
675
|
+
}
|
|
676
|
+
for (const child of Object.values(object)) {
|
|
677
|
+
const nested = extractSessionId(child);
|
|
678
|
+
if (nested)
|
|
679
|
+
return nested;
|
|
680
|
+
}
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
export function extractProgress(value) {
|
|
684
|
+
if (!value || typeof value !== "object")
|
|
685
|
+
return null;
|
|
686
|
+
const stageHint = extractToolStageHint(value);
|
|
687
|
+
if (stageHint)
|
|
688
|
+
return `superflow-stage:${stageHint}`;
|
|
689
|
+
const object = value;
|
|
690
|
+
for (const key of ["message", "summary", "text"]) {
|
|
691
|
+
if (typeof object[key] === "string" && object[key].length > 0) {
|
|
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;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return null;
|
|
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
|
+
}
|
|
752
|
+
function safeJson(value) {
|
|
753
|
+
try {
|
|
754
|
+
return JSON.parse(value);
|
|
755
|
+
}
|
|
756
|
+
catch {
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
function stopChild(pid) {
|
|
761
|
+
stopProcessTree(pid);
|
|
762
|
+
}
|
|
763
|
+
function isUuid(value) {
|
|
764
|
+
return (typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f-]{27}$/i.test(value));
|
|
765
|
+
}
|
|
766
|
+
function cryptoRandomUuid() {
|
|
767
|
+
return globalThis.crypto.randomUUID();
|
|
768
|
+
}
|
|
769
|
+
//# sourceMappingURL=agent-process.js.map
|