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(); }