Files
pikasTech-HWLAB/web/hwlab-cloud-web/code-agent-facts.mjs
T
2026-05-23 15:26:15 +00:00

312 lines
11 KiB
JavaScript

const M3_IO_ROUTE = "/v1/m3/io";
const TEXT_FALLBACK_RUNNER = "openai-responses-fallback";
const CODEX_ONE_SHOT_RUNNER = "codex-cli-one-shot-ephemeral";
const READONLY_SESSION_MODE = "controlled-readonly-session-registry";
const CODEX_STDIO_SESSION_MODE = "codex-mcp-stdio-long-lived";
const HWLAB_SKILL_RUNNER = "hwlab-m3-io-skill-cli";
export function codeAgentFactsFromMessage(message) {
if (!hasCodeAgentFields(message)) return null;
const classification = classifyCodeAgentFacts(message);
const rows = [
{
label: "runner/provider/backend",
value: compactFields([
field("runner", message.runner?.kind ?? runnerKindFromMessage(message)),
field("provider", message.provider),
field("backend", message.backend)
])
},
{
label: "session",
value: compactFields([
field("id", message.session?.sessionId ?? message.sessionId ?? message.runner?.sessionId),
field("status", message.session?.status),
field("sessionMode", message.sessionMode ?? message.runner?.sessionMode ?? message.runner?.session)
])
},
{
label: "workspace",
value: message.workspace ?? message.runner?.workspace ?? "none"
},
{
label: "sandbox",
value: message.sandbox ?? message.runner?.sandbox ?? "none"
},
{
label: "capabilityLevel",
value: message.capabilityLevel ?? message.runner?.capabilityLevel ?? "unknown"
},
{
label: "toolCalls",
value: toolCallsSummary(message.toolCalls)
},
{
label: "skills",
value: skillsSummary(message.skills)
},
{
label: "runnerTrace/traceId",
value: compactFields([
field("traceId", message.traceId),
field("runnerTrace", runnerTraceSummary(message.runnerTrace))
])
}
];
return {
...classification,
rows,
hwlabApiFacts: hwlabApiFactsFromMessage(message)
};
}
export function classifyCodeAgentFacts(message) {
const runnerKind = runnerKindFromMessage(message);
const provider = String(message?.provider ?? "").trim();
const sessionMode = String(message?.sessionMode ?? message?.runner?.sessionMode ?? message?.runner?.session ?? "").trim();
const implementationType = String(message?.implementationType ?? message?.runner?.implementationType ?? "").trim();
const capabilityLevel = String(message?.capabilityLevel ?? message?.runner?.capabilityLevel ?? "").trim();
const longLivedGate = message?.longLivedSessionGate;
if (provider === "codex-stdio" && longLivedGate?.status === "pass" && longLivedGate?.pass === true) {
return {
kind: "codex-stdio-long-lived",
statusLabel: "完整 Code Agent session",
tone: "dev-live",
summary: "Codex stdio long-lived session gate passed; session lifecycle and trace capture are available.",
fullCodeAgent: true,
codexSessionGatePass: true
};
}
if (provider === "hwlab-skill-cli" || runnerKind === HWLAB_SKILL_RUNNER || hasM3IoToolCall(message?.toolCalls)) {
return {
kind: "hwlab-skill-cli",
statusLabel: "HWLAB Skill CLI",
tone: "source",
summary: "Controlled Skill CLI -> HWLAB API path only; this is not OpenAI fallback and not a generic Codex session.",
fullCodeAgent: false,
codexSessionGatePass: false
};
}
if (runnerKind === CODEX_ONE_SHOT_RUNNER || sessionMode === "ephemeral-one-shot" || implementationType === "codex-cli-one-shot-ephemeral") {
return {
kind: "stateless-one-shot",
statusLabel: "stateless-one-shot",
tone: "source",
summary: "Ephemeral one-shot runner result; do not treat it as a reusable or long-lived Code Agent session.",
fullCodeAgent: false,
codexSessionGatePass: false
};
}
if (runnerKind === TEXT_FALLBACK_RUNNER || provider === "openai-responses" || capabilityLevel === "text-chat-only") {
return {
kind: "text-chat-only",
statusLabel: "openai-responses-fallback / text-chat-only",
tone: "source",
summary: "Text chat fallback can answer ordinary messages, but it has no workspace, tools, skills, or Codex session gate.",
fullCodeAgent: false,
codexSessionGatePass: false
};
}
if (provider === "codex-readonly-runner" || runnerKind === "hwlab-readonly-runner" || sessionMode === READONLY_SESSION_MODE) {
return {
kind: READONLY_SESSION_MODE,
statusLabel: "只读 session registry",
tone: "source",
summary: "Bounded read-only tools and session mapping are available, but Codex stdio, write capability, and durable session remain blocked.",
fullCodeAgent: false,
codexSessionGatePass: false
};
}
if (provider === "codex-stdio" || sessionMode === CODEX_STDIO_SESSION_MODE) {
return {
kind: "codex-stdio-blocked",
statusLabel: "Codex stdio blocked",
tone: "blocked",
summary: "Codex stdio fields are present, but the long-lived session gate has not passed.",
fullCodeAgent: false,
codexSessionGatePass: false
};
}
return {
kind: "unknown-code-agent-path",
statusLabel: "Code Agent facts",
tone: message?.status === "failed" ? "blocked" : "source",
summary: "Provider response is shown with its raw runner/session facts; completion is not promoted without a passing gate.",
fullCodeAgent: false,
codexSessionGatePass: false
};
}
export function hwlabApiFactsFromMessage(message) {
const facts = [];
for (const toolCall of Array.isArray(message?.toolCalls) ? message.toolCalls : []) {
const parsed = parseToolStdout(toolCall.stdout);
const route = firstText(toolCall.route, toolCall.hwlabApi?.route, parsed?.route);
if (route !== M3_IO_ROUTE) continue;
const blocker = firstText(
toolCall.blocker?.code,
toolCall.capabilityBlocker?.code,
toolCall.trustBlocker?.code,
firstBlockerCode(toolCall.blockers),
parsed?.blocker?.code,
parsed?.blocker,
parsed?.durable?.blocker
);
facts.push({
tool: firstText(toolCall.name, "skill-cli"),
route,
method: firstText(toolCall.method, parsed?.method, "POST"),
operationId: nullableText(toolCall.operationId ?? parsed?.operationId),
traceId: nullableText(toolCall.traceId ?? parsed?.traceId ?? message?.traceId),
auditId: nullableText(toolCall.auditId ?? toolCall.audit?.auditId ?? parsed?.auditId ?? parsed?.audit?.auditId),
evidenceId: nullableText(toolCall.evidenceId ?? toolCall.evidence?.evidenceId ?? parsed?.evidenceId ?? parsed?.evidence?.evidenceId),
accepted: toolCall.accepted ?? parsed?.accepted ?? null,
readback: toolCall.readback ?? parsed?.readback ?? parsed?.result?.targetReadback ?? null,
blocker: nullableText(blocker),
status: firstText(toolCall.status, parsed?.status, "unknown")
});
}
return facts;
}
export function compactHwlabApiFact(fact) {
if (!fact) return "";
return compactFields([
field("route", fact.route),
field("method", fact.method),
field("operationId", fact.operationId),
field("traceId", fact.traceId),
field("auditId", fact.auditId),
field("evidenceId", fact.evidenceId),
field("blocker", fact.blocker)
]);
}
export function toolCallsSummary(toolCalls) {
if (!Array.isArray(toolCalls) || toolCalls.length === 0) return "none";
const completed = toolCalls.filter((tool) => tool?.status === "completed").length;
const names = toolCalls
.map((tool) => compactFields([
firstText(tool?.name, "tool"),
tool?.status ? `(${tool.status})` : null,
tool?.route ? `@${tool.route}` : null
], ""))
.filter(Boolean)
.slice(0, 4)
.join(", ");
return `${completed}/${toolCalls.length}${names ? ` ${names}` : ""}`;
}
export function skillsSummary(skills) {
if (!skills || typeof skills !== "object") return "none";
const count = skills.totalCount ?? skills.count ?? (Array.isArray(skills.items) ? skills.items.length : 0);
const names = Array.isArray(skills.items)
? skills.items.map((item) => item?.name).filter(Boolean).slice(0, 3).join(",")
: "";
const blockers = Array.isArray(skills.blockers)
? skills.blockers.map((blocker) => blocker?.code).filter(Boolean).slice(0, 2).join(",")
: "";
return compactFields([
skills.status ?? "unknown",
`count=${count}`,
names ? `items=${names}` : null,
blockers ? `blockers=${blockers}` : null
], ":");
}
export function runnerTraceSummary(runnerTrace) {
if (!runnerTrace || typeof runnerTrace !== "object") return "none";
return compactFields([
field("traceId", runnerTrace.traceId),
field("runnerKind", runnerTrace.runnerKind),
field("sessionMode", runnerTrace.sessionMode),
typeof runnerTrace.turn === "number" ? field("turn", runnerTrace.turn) : null,
field("route", runnerTrace.route),
field("status", runnerTrace.status)
]);
}
function hasCodeAgentFields(message) {
return Boolean(
message &&
typeof message === "object" &&
(
message.provider ||
message.backend ||
message.runner ||
message.session ||
message.sessionMode ||
message.capabilityLevel ||
Array.isArray(message.toolCalls) ||
message.runnerTrace
)
);
}
function hasM3IoToolCall(toolCalls) {
return Array.isArray(toolCalls) && toolCalls.some((toolCall) =>
toolCall?.route === M3_IO_ROUTE ||
toolCall?.hwlabApi?.route === M3_IO_ROUTE ||
parseToolStdout(toolCall?.stdout)?.route === M3_IO_ROUTE
);
}
function runnerKindFromMessage(message) {
return firstText(
message?.runner?.kind,
message?.runnerTrace?.runnerKind,
message?.provider === "openai-responses" ? TEXT_FALLBACK_RUNNER : null
);
}
function parseToolStdout(stdout) {
if (typeof stdout !== "string" || !stdout.trim()) return null;
const text = stdout.trim();
const direct = parseJson(text);
if (direct) return direct;
const start = text.indexOf("{");
const end = text.lastIndexOf("}");
if (start === -1 || end <= start) return null;
return parseJson(text.slice(start, end + 1));
}
function parseJson(text) {
try {
const parsed = JSON.parse(text);
return parsed && typeof parsed === "object" ? parsed : null;
} catch {
return null;
}
}
function firstBlockerCode(blockers) {
if (!Array.isArray(blockers)) return null;
return blockers.map((blocker) => blocker?.code).find(Boolean) ?? null;
}
function compactFields(fields, separator = " / ") {
return fields.filter((item) => item !== null && item !== undefined && String(item).trim()).map((item) => String(item).trim()).join(separator);
}
function field(label, value) {
const normalized = nullableText(value);
return normalized ? `${label}=${normalized}` : null;
}
function firstText(...values) {
return values.map(nullableText).find(Boolean) ?? null;
}
function nullableText(value) {
if (value === undefined || value === null || value === "") return null;
return String(value).replace(/\s+/gu, " ").trim();
}