1183 lines
40 KiB
JavaScript
1183 lines
40 KiB
JavaScript
import { randomUUID } from "node:crypto";
|
||
|
||
import { CLOUD_API_SERVICE_ID } from "../audit/index.mjs";
|
||
|
||
export const HARDWARE_INVOKE_SHELL_METHOD = "hardware.invoke.shell";
|
||
export const CODE_AGENT_HARDWARE_PROVIDER = "hwlab-hardware-capability";
|
||
export const CODE_AGENT_HARDWARE_MODEL = "controlled-hardware-capability";
|
||
export const CODE_AGENT_HARDWARE_BACKEND = "hwlab-cloud-api/hardware.invoke.shell";
|
||
export const CODE_AGENT_HARDWARE_RUNNER_KIND = "hwlab-hardware-capability-runner";
|
||
export const CODE_AGENT_HARDWARE_SANDBOX = "cloud-api-capability-route-only";
|
||
export const CODE_AGENT_HARDWARE_SESSION_MODE = "controlled-hardware-capability-route";
|
||
export const CODE_AGENT_HARDWARE_IMPLEMENTATION_TYPE = "cloud-api-hardware-capability-runner";
|
||
export const CODE_AGENT_HARDWARE_CAPABILITY_EXECUTED = "pc-gateway-shell-executed";
|
||
export const CODE_AGENT_HARDWARE_CAPABILITY_BLOCKED = "blocked";
|
||
|
||
const OUTPUT_LIMIT = 4000;
|
||
const COMMAND_SUMMARY_LIMIT = 320;
|
||
const DEFAULT_DEV_MVP_PC_GATEWAY_TOPOLOGY = Object.freeze([Object.freeze({
|
||
projectId: "prj_mvp_topology",
|
||
gatewaySessionId: "gws_DESKTOP-1MHOD9I",
|
||
resourceId: "res_windows_host",
|
||
capabilityId: "cap_windows_cmd_exec",
|
||
capabilityName: "shell.exec",
|
||
gatewayLabel: "DESKTOP-1MHOD9I",
|
||
topologySource: "dev-mvp-pc-gateway-topology"
|
||
})]);
|
||
|
||
const HARDWARE_LIMITATION_FLAGS = Object.freeze([
|
||
"cloud-api-hardware-capability-only",
|
||
"no-direct-gateway-link",
|
||
"no-direct-box-link",
|
||
"no-direct-patch-panel-link",
|
||
"no-openai-text-fallback",
|
||
"registered-capability-required"
|
||
]);
|
||
|
||
export function detectHardwareCapabilityIntent(message, { params = {}, env = process.env, topology } = {}) {
|
||
const text = String(message ?? "").trim();
|
||
if (!mentionsPcGatewayShellCapability(text, params)) return null;
|
||
|
||
const command = normalizeWindowsCommand(
|
||
params.input?.command ??
|
||
params.command ??
|
||
extractWindowsCommand(text)
|
||
);
|
||
const explicit = {
|
||
projectId: cleanProtocolId(params.projectId, "prj") ?? extractProtocolField(text, "projectId", "prj"),
|
||
gatewaySessionId: cleanProtocolId(params.gatewaySessionId, "gws") ?? extractProtocolField(text, "gatewaySessionId", "gws") ?? extractProtocolField(text, "session", "gws"),
|
||
resourceId: cleanProtocolId(params.resourceId, "res") ?? extractProtocolField(text, "resourceId", "res"),
|
||
capabilityId: cleanProtocolId(params.capabilityId, "cap") ?? extractProtocolField(text, "capabilityId", "cap")
|
||
};
|
||
const resolution = resolveHardwareTopology(explicit, { topology, env });
|
||
|
||
if (resolution.blocker) {
|
||
return {
|
||
kind: "hardware_shell",
|
||
toolName: HARDWARE_INVOKE_SHELL_METHOD,
|
||
action: "shell.exec",
|
||
command,
|
||
request: null,
|
||
topology: resolution,
|
||
blocker: resolution.blocker
|
||
};
|
||
}
|
||
|
||
if (!command) {
|
||
return {
|
||
kind: "hardware_shell",
|
||
toolName: HARDWARE_INVOKE_SHELL_METHOD,
|
||
action: "shell.exec",
|
||
request: resolution.request,
|
||
topology: resolution,
|
||
blocker: hardwareBlocker({
|
||
code: "hardware_command_missing",
|
||
layer: "intent",
|
||
category: "needs_command",
|
||
summary: "PC gateway shell.exec request did not include a Windows cmd command.",
|
||
userMessage: "PC gateway shell.exec 请求缺少 Windows cmd 命令;请明确给出 cmd /c ...。",
|
||
retryable: false
|
||
})
|
||
};
|
||
}
|
||
|
||
if (!isAllowedWindowsCmd(command)) {
|
||
return {
|
||
kind: "hardware_shell",
|
||
toolName: HARDWARE_INVOKE_SHELL_METHOD,
|
||
action: "shell.exec",
|
||
command,
|
||
request: resolution.request,
|
||
topology: resolution,
|
||
blocker: hardwareBlocker({
|
||
code: "hardware_command_unsupported",
|
||
layer: "security",
|
||
category: "security_boundary",
|
||
summary: "Only explicit Windows cmd /c commands are allowed for the registered PC gateway shell.exec capability.",
|
||
userMessage: "该能力只允许已登记 PC gateway 的 Windows cmd /c 命令;不会执行 bash、sh、kubectl、PowerShell 或任意 shell。",
|
||
retryable: false
|
||
})
|
||
};
|
||
}
|
||
|
||
return {
|
||
kind: "hardware_shell",
|
||
toolName: HARDWARE_INVOKE_SHELL_METHOD,
|
||
action: "shell.exec",
|
||
command,
|
||
request: resolution.request,
|
||
topology: resolution
|
||
};
|
||
}
|
||
|
||
export async function callCodeAgentHardwareRunner({
|
||
intent,
|
||
conversationId,
|
||
sessionId,
|
||
traceId,
|
||
now,
|
||
workspace,
|
||
sessionRegistry,
|
||
gatewayRegistry,
|
||
invokeHardwareShellRpc,
|
||
traceRecorder
|
||
} = {}) {
|
||
const startedAt = nowIso(now);
|
||
const resolvedWorkspace = workspace ?? process.cwd();
|
||
const request = intent?.request ?? null;
|
||
const requestId = `req_code_agent_hw_shell_${randomUUID()}`;
|
||
const actorId = "usr_code_agent";
|
||
const acquire = acquireHardwareSession(sessionRegistry, {
|
||
conversationId,
|
||
sessionId,
|
||
traceId,
|
||
workspace: resolvedWorkspace,
|
||
now
|
||
});
|
||
let session = acquire.session;
|
||
const preflightBlocker = intent?.blocker ?? acquire.blocker ?? hardwareRegistryBlocker({ request, gatewayRegistry, traceId });
|
||
|
||
traceRecorder?.append({
|
||
type: "tool_call",
|
||
status: "started",
|
||
label: "tool:hardware.invoke.shell:started",
|
||
toolName: HARDWARE_INVOKE_SHELL_METHOD,
|
||
waitingFor: HARDWARE_INVOKE_SHELL_METHOD
|
||
});
|
||
|
||
if (preflightBlocker) {
|
||
session = releaseHardwareSession(sessionRegistry, session, { now, traceId, conversationId });
|
||
const finishedAt = nowIso(now);
|
||
const runnerResult = hardwareBlockedRunnerResult({
|
||
request,
|
||
command: intent?.command,
|
||
blocker: preflightBlocker,
|
||
traceId,
|
||
requestId,
|
||
actorId,
|
||
startedAt,
|
||
finishedAt,
|
||
workspace: resolvedWorkspace,
|
||
session,
|
||
topology: intent?.topology
|
||
});
|
||
appendHardwareTraceCompletion(traceRecorder, runnerResult);
|
||
return runnerResult;
|
||
}
|
||
|
||
if (typeof invokeHardwareShellRpc !== "function") {
|
||
session = releaseHardwareSession(sessionRegistry, session, { now, traceId, conversationId });
|
||
const finishedAt = nowIso(now);
|
||
const runnerResult = hardwareBlockedRunnerResult({
|
||
request,
|
||
command: intent?.command,
|
||
blocker: hardwareBlocker({
|
||
code: "hardware_dispatch_unavailable",
|
||
layer: "dispatch",
|
||
category: "needs_config",
|
||
summary: "Code Agent hardware runner has no cloud-api JSON-RPC dispatcher.",
|
||
userMessage: "Code Agent 硬件能力 runner 缺少 cloud-api 内部 dispatch 入口;本次未执行。",
|
||
retryable: false,
|
||
traceId
|
||
}),
|
||
traceId,
|
||
requestId,
|
||
actorId,
|
||
startedAt,
|
||
finishedAt,
|
||
workspace: resolvedWorkspace,
|
||
session,
|
||
topology: intent?.topology
|
||
});
|
||
appendHardwareTraceCompletion(traceRecorder, runnerResult);
|
||
return runnerResult;
|
||
}
|
||
|
||
let rpcResponse;
|
||
try {
|
||
rpcResponse = await invokeHardwareShellRpc({
|
||
method: HARDWARE_INVOKE_SHELL_METHOD,
|
||
params: {
|
||
projectId: request.projectId,
|
||
gatewaySessionId: request.gatewaySessionId,
|
||
resourceId: request.resourceId,
|
||
capabilityId: request.capabilityId,
|
||
input: {
|
||
command: intent.command
|
||
}
|
||
},
|
||
traceId,
|
||
requestId,
|
||
actorId,
|
||
serviceId: CLOUD_API_SERVICE_ID
|
||
});
|
||
} catch (error) {
|
||
rpcResponse = {
|
||
error: {
|
||
code: "hardware_dispatch_exception",
|
||
message: "hardware.invoke.shell dispatch threw before returning JSON-RPC",
|
||
data: {
|
||
reason: error?.message ?? "dispatch exception"
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
session = releaseHardwareSession(sessionRegistry, session, { now, traceId, conversationId });
|
||
const finishedAt = nowIso(now);
|
||
const runnerResult = hardwareRunnerResultFromRpc({
|
||
request,
|
||
command: intent.command,
|
||
rpcResponse,
|
||
traceId,
|
||
requestId,
|
||
actorId,
|
||
startedAt,
|
||
finishedAt,
|
||
workspace: resolvedWorkspace,
|
||
session,
|
||
topology: intent?.topology
|
||
});
|
||
appendHardwareTraceCompletion(traceRecorder, runnerResult);
|
||
return runnerResult;
|
||
}
|
||
|
||
export function createCodeAgentHardwareInvokeShellRpc({ handleJsonRpcRequest, runtimeStore, env, dbProbe, gatewayRegistry } = {}) {
|
||
if (typeof handleJsonRpcRequest !== "function") return null;
|
||
return async ({ method = HARDWARE_INVOKE_SHELL_METHOD, params = {}, traceId, requestId, actorId, serviceId = CLOUD_API_SERVICE_ID } = {}) => handleJsonRpcRequest(
|
||
{
|
||
jsonrpc: "2.0",
|
||
id: requestId || `req_code_agent_hw_shell_${randomUUID()}`,
|
||
method,
|
||
params,
|
||
meta: {
|
||
traceId: traceId || `trc_code_agent_hw_shell_${randomUUID()}`,
|
||
actorId,
|
||
serviceId,
|
||
environment: "dev"
|
||
}
|
||
},
|
||
{
|
||
adapter: "code-agent-internal",
|
||
runtimeStore,
|
||
env,
|
||
dbProbe,
|
||
gatewayRegistry
|
||
}
|
||
);
|
||
}
|
||
|
||
export function codeAgentHardwareRunnerDescriptor({ workspace, session = null, capabilityLevel = CODE_AGENT_HARDWARE_CAPABILITY_EXECUTED } = {}) {
|
||
return {
|
||
kind: CODE_AGENT_HARDWARE_RUNNER_KIND,
|
||
provider: CODE_AGENT_HARDWARE_PROVIDER,
|
||
backend: CODE_AGENT_HARDWARE_BACKEND,
|
||
workspace: workspace ?? process.cwd(),
|
||
sandbox: CODE_AGENT_HARDWARE_SANDBOX,
|
||
session: CODE_AGENT_HARDWARE_SESSION_MODE,
|
||
sessionMode: CODE_AGENT_HARDWARE_SESSION_MODE,
|
||
sessionId: session?.sessionId ?? null,
|
||
turn: session?.turn ?? null,
|
||
sessionReused: session?.reused ?? false,
|
||
implementationType: CODE_AGENT_HARDWARE_IMPLEMENTATION_TYPE,
|
||
codexStdio: false,
|
||
longLivedSession: false,
|
||
durableSession: false,
|
||
writeCapable: true,
|
||
readOnly: false,
|
||
capabilityLevel,
|
||
toolPolicy: {
|
||
allowed: [`internal-json-rpc ${HARDWARE_INVOKE_SHELL_METHOD}`],
|
||
blocked: ["direct-gateway-link", "direct-box-link", "direct-patch-panel-link", "openai-text-fallback", "unregistered-shell"]
|
||
},
|
||
runnerLimitations: [...HARDWARE_LIMITATION_FLAGS],
|
||
safety: {
|
||
secretsRead: false,
|
||
secretValuesPrinted: false,
|
||
kubeconfigRead: false,
|
||
cloudApiRouteOnly: true,
|
||
sourceServiceId: CLOUD_API_SERVICE_ID,
|
||
directGatewayCallsAllowed: false,
|
||
directBoxCallsAllowed: false,
|
||
directPatchPanelCallsAllowed: false,
|
||
openAiFallbackAllowed: false,
|
||
outputLimitBytes: OUTPUT_LIMIT
|
||
}
|
||
};
|
||
}
|
||
|
||
function hardwareRunnerResultFromRpc({
|
||
request,
|
||
command,
|
||
rpcResponse,
|
||
traceId,
|
||
requestId,
|
||
actorId,
|
||
startedAt,
|
||
finishedAt,
|
||
workspace,
|
||
session,
|
||
topology
|
||
}) {
|
||
const rpcError = rpcResponse?.error ?? null;
|
||
if (rpcError) {
|
||
const reason = rpcError.data?.reason ?? rpcError.message ?? "JSON-RPC dispatch failed";
|
||
return hardwareBlockedRunnerResult({
|
||
request,
|
||
command,
|
||
blocker: hardwareBlocker({
|
||
code: String(reason).includes("unknown serviceId") ? "hardware_rpc_service_id_invalid" : "hardware_json_rpc_invalid",
|
||
layer: "json-rpc",
|
||
category: "json_rpc_meta",
|
||
summary: reason,
|
||
userMessage: String(reason).includes("unknown serviceId")
|
||
? "cloud-api 内部 JSON-RPC serviceId 不在冻结列表中,本次未执行硬件能力。"
|
||
: "cloud-api 内部 JSON-RPC 请求未通过协议校验,本次未执行硬件能力。",
|
||
retryable: false,
|
||
traceId,
|
||
reason
|
||
}),
|
||
traceId,
|
||
requestId,
|
||
actorId,
|
||
startedAt,
|
||
finishedAt,
|
||
workspace,
|
||
session,
|
||
topology,
|
||
rpcError
|
||
});
|
||
}
|
||
|
||
const result = rpcResponse?.result ?? {};
|
||
const dispatch = result.dispatch ?? {};
|
||
const dispatchStatus = dispatch.dispatchStatus ?? result.status ?? "unknown";
|
||
const exitCode = Number.isInteger(dispatch.exitCode) ? dispatch.exitCode : null;
|
||
const stdout = safeOutput(dispatch.stdout ?? dispatch.output?.stdout ?? "");
|
||
const stderr = safeOutput(dispatch.stderr ?? dispatch.output?.stderr ?? "");
|
||
const shellExecuted = dispatch.shellExecuted === true;
|
||
const succeeded = shellExecuted && exitCode === 0 && ["succeeded", "completed"].includes(String(dispatchStatus).toLowerCase());
|
||
const operationId = result.operationId ?? dispatch.operationId ?? null;
|
||
|
||
if (!succeeded) {
|
||
return hardwareBlockedRunnerResult({
|
||
request,
|
||
command,
|
||
blocker: hardwareFailureBlocker({ dispatchStatus, exitCode, result, dispatch, traceId }),
|
||
traceId,
|
||
requestId,
|
||
actorId,
|
||
startedAt,
|
||
finishedAt,
|
||
workspace,
|
||
session,
|
||
topology,
|
||
result,
|
||
dispatch,
|
||
operationId,
|
||
dispatchStatus,
|
||
exitCode,
|
||
stdout,
|
||
stderr
|
||
});
|
||
}
|
||
|
||
const stdoutSummary = summarizeOutput(stdout);
|
||
const stderrSummary = summarizeOutput(stderr);
|
||
const toolCall = hardwareToolCall({
|
||
request,
|
||
command,
|
||
status: "completed",
|
||
traceId,
|
||
requestId,
|
||
actorId,
|
||
operationId,
|
||
dispatchStatus,
|
||
exitCode,
|
||
stdout,
|
||
stderr,
|
||
stdoutSummary,
|
||
stderrSummary,
|
||
capabilityLevel: CODE_AGENT_HARDWARE_CAPABILITY_EXECUTED,
|
||
startedAt,
|
||
finishedAt
|
||
});
|
||
const runnerTrace = hardwareRunnerTrace({
|
||
traceId,
|
||
startedAt,
|
||
finishedAt,
|
||
workspace,
|
||
session,
|
||
request,
|
||
operationId,
|
||
dispatchStatus,
|
||
exitCode,
|
||
status: "completed",
|
||
capabilityLevel: CODE_AGENT_HARDWARE_CAPABILITY_EXECUTED,
|
||
events: ["request:accepted", "runner:hardware-capability:selected", "tool:hardware.invoke.shell:started", "tool:hardware.invoke.shell:completed"]
|
||
});
|
||
|
||
return {
|
||
provider: CODE_AGENT_HARDWARE_PROVIDER,
|
||
model: CODE_AGENT_HARDWARE_MODEL,
|
||
backend: CODE_AGENT_HARDWARE_BACKEND,
|
||
content: hardwareReply({ request, operationId, dispatchStatus, exitCode, stdoutSummary, stderrSummary, blocked: false }),
|
||
workspace,
|
||
sandbox: CODE_AGENT_HARDWARE_SANDBOX,
|
||
session,
|
||
sessionMode: CODE_AGENT_HARDWARE_SESSION_MODE,
|
||
sessionReuse: session ? sessionReuseEvidence(session) : null,
|
||
implementationType: CODE_AGENT_HARDWARE_IMPLEMENTATION_TYPE,
|
||
runnerLimitations: [...HARDWARE_LIMITATION_FLAGS],
|
||
codexStdioFeasibility: skippedCodexStdioFeasibility({ workspace, reason: "hardware_capability_deterministic_route" }),
|
||
longLivedSessionGate: hardwareLongLivedGate(session),
|
||
toolCalls: [toolCall],
|
||
skills: hardwareSkills({ status: "used", blockers: [] }),
|
||
runner: codeAgentHardwareRunnerDescriptor({ workspace, session, capabilityLevel: CODE_AGENT_HARDWARE_CAPABILITY_EXECUTED }),
|
||
runnerTrace,
|
||
capabilityLevel: CODE_AGENT_HARDWARE_CAPABILITY_EXECUTED,
|
||
blockers: [],
|
||
route: HARDWARE_INVOKE_SHELL_METHOD,
|
||
toolName: HARDWARE_INVOKE_SHELL_METHOD,
|
||
operationId,
|
||
dispatchStatus,
|
||
exitCode,
|
||
stdoutSummary,
|
||
stderrSummary,
|
||
hardware: hardwareStructuredResult({ request, command, result, dispatch, operationId, dispatchStatus, exitCode, stdoutSummary, stderrSummary, topology }),
|
||
providerTrace: {
|
||
runnerKind: CODE_AGENT_HARDWARE_RUNNER_KIND,
|
||
codexStdio: false,
|
||
transport: "internal-json-rpc",
|
||
serviceId: CLOUD_API_SERVICE_ID,
|
||
method: HARDWARE_INVOKE_SHELL_METHOD,
|
||
traceId,
|
||
operationId,
|
||
dispatchStatus,
|
||
exitCode,
|
||
capabilityLevel: CODE_AGENT_HARDWARE_CAPABILITY_EXECUTED,
|
||
fallbackUsed: false
|
||
}
|
||
};
|
||
}
|
||
|
||
function hardwareBlockedRunnerResult({
|
||
request,
|
||
command,
|
||
blocker,
|
||
traceId,
|
||
requestId,
|
||
actorId,
|
||
startedAt,
|
||
finishedAt,
|
||
workspace,
|
||
session,
|
||
topology,
|
||
result = null,
|
||
dispatch = {},
|
||
operationId = null,
|
||
dispatchStatus = null,
|
||
exitCode = null,
|
||
stdout = "",
|
||
stderr = "",
|
||
rpcError = null
|
||
}) {
|
||
const normalizedBlocker = {
|
||
...blocker,
|
||
traceId,
|
||
route: HARDWARE_INVOKE_SHELL_METHOD,
|
||
toolName: HARDWARE_INVOKE_SHELL_METHOD
|
||
};
|
||
const stdoutSummary = summarizeOutput(stdout);
|
||
const stderrSummary = summarizeOutput(stderr || normalizedBlocker.summary || normalizedBlocker.code);
|
||
const toolCall = hardwareToolCall({
|
||
request,
|
||
command,
|
||
status: "blocked",
|
||
traceId,
|
||
requestId,
|
||
actorId,
|
||
operationId,
|
||
dispatchStatus,
|
||
exitCode,
|
||
stdout,
|
||
stderr,
|
||
stdoutSummary,
|
||
stderrSummary,
|
||
capabilityLevel: CODE_AGENT_HARDWARE_CAPABILITY_BLOCKED,
|
||
blocker: normalizedBlocker,
|
||
startedAt,
|
||
finishedAt
|
||
});
|
||
const runnerTrace = hardwareRunnerTrace({
|
||
traceId,
|
||
startedAt,
|
||
finishedAt,
|
||
workspace,
|
||
session,
|
||
request,
|
||
operationId,
|
||
dispatchStatus,
|
||
exitCode,
|
||
status: "blocked",
|
||
capabilityLevel: CODE_AGENT_HARDWARE_CAPABILITY_BLOCKED,
|
||
blocker: normalizedBlocker,
|
||
events: ["request:accepted", "runner:hardware-capability:selected", "tool:hardware.invoke.shell:blocked", `blocker:${normalizedBlocker.code}`]
|
||
});
|
||
|
||
return {
|
||
provider: CODE_AGENT_HARDWARE_PROVIDER,
|
||
model: CODE_AGENT_HARDWARE_MODEL,
|
||
backend: CODE_AGENT_HARDWARE_BACKEND,
|
||
content: hardwareReply({ request, operationId, dispatchStatus, exitCode, stdoutSummary, stderrSummary, blocker: normalizedBlocker, blocked: true }),
|
||
workspace,
|
||
sandbox: CODE_AGENT_HARDWARE_SANDBOX,
|
||
session,
|
||
sessionMode: CODE_AGENT_HARDWARE_SESSION_MODE,
|
||
sessionReuse: session ? sessionReuseEvidence(session) : null,
|
||
implementationType: CODE_AGENT_HARDWARE_IMPLEMENTATION_TYPE,
|
||
runnerLimitations: [...HARDWARE_LIMITATION_FLAGS],
|
||
codexStdioFeasibility: skippedCodexStdioFeasibility({ workspace, reason: "hardware_capability_deterministic_route" }),
|
||
longLivedSessionGate: hardwareLongLivedGate(session),
|
||
toolCalls: [toolCall],
|
||
skills: hardwareSkills({ status: "blocked", blockers: [normalizedBlocker] }),
|
||
runner: codeAgentHardwareRunnerDescriptor({ workspace, session, capabilityLevel: CODE_AGENT_HARDWARE_CAPABILITY_BLOCKED }),
|
||
runnerTrace,
|
||
capabilityLevel: CODE_AGENT_HARDWARE_CAPABILITY_BLOCKED,
|
||
blockers: [normalizedBlocker],
|
||
blocker: normalizedBlocker,
|
||
route: HARDWARE_INVOKE_SHELL_METHOD,
|
||
toolName: HARDWARE_INVOKE_SHELL_METHOD,
|
||
operationId,
|
||
dispatchStatus,
|
||
exitCode,
|
||
stdoutSummary,
|
||
stderrSummary,
|
||
hardware: hardwareStructuredResult({ request, command, result, dispatch, operationId, dispatchStatus, exitCode, stdoutSummary, stderrSummary, topology, blocker: normalizedBlocker, rpcError }),
|
||
providerTrace: {
|
||
runnerKind: CODE_AGENT_HARDWARE_RUNNER_KIND,
|
||
codexStdio: false,
|
||
transport: "internal-json-rpc",
|
||
serviceId: CLOUD_API_SERVICE_ID,
|
||
method: HARDWARE_INVOKE_SHELL_METHOD,
|
||
traceId,
|
||
operationId,
|
||
dispatchStatus,
|
||
exitCode,
|
||
capabilityLevel: CODE_AGENT_HARDWARE_CAPABILITY_BLOCKED,
|
||
fallbackUsed: false,
|
||
blocker: normalizedBlocker
|
||
}
|
||
};
|
||
}
|
||
|
||
function hardwareToolCall({
|
||
request,
|
||
command,
|
||
status,
|
||
traceId,
|
||
requestId,
|
||
actorId,
|
||
operationId,
|
||
dispatchStatus,
|
||
exitCode,
|
||
stdout,
|
||
stderr,
|
||
stdoutSummary,
|
||
stderrSummary,
|
||
capabilityLevel,
|
||
blocker = null,
|
||
startedAt,
|
||
finishedAt
|
||
}) {
|
||
return {
|
||
id: `tool_${randomUUID()}`,
|
||
type: "cloud-api-rpc",
|
||
name: HARDWARE_INVOKE_SHELL_METHOD,
|
||
status,
|
||
method: HARDWARE_INVOKE_SHELL_METHOD,
|
||
route: HARDWARE_INVOKE_SHELL_METHOD,
|
||
serviceId: CLOUD_API_SERVICE_ID,
|
||
sourceServiceId: CLOUD_API_SERVICE_ID,
|
||
requestId,
|
||
actorId,
|
||
traceId,
|
||
projectId: request?.projectId ?? null,
|
||
gatewaySessionId: request?.gatewaySessionId ?? null,
|
||
resourceId: request?.resourceId ?? null,
|
||
capabilityId: request?.capabilityId ?? null,
|
||
capabilityName: "shell.exec",
|
||
command: command ? redactCommand(command) : null,
|
||
operationId,
|
||
dispatchStatus,
|
||
exitCode,
|
||
stdout: boundOutput(stdout).text,
|
||
stdoutSummary,
|
||
stderrSummary,
|
||
outputTruncated: boundOutput(stdout).truncated || boundOutput(stderr).truncated,
|
||
capabilityLevel,
|
||
blocker,
|
||
blockers: blocker ? [blocker] : [],
|
||
startedAt,
|
||
finishedAt,
|
||
safety: {
|
||
cloudApiRouteOnly: true,
|
||
directGatewayCalls: false,
|
||
directBoxCalls: false,
|
||
directPatchPanelCalls: false,
|
||
openAiFallbackUsed: false,
|
||
serviceIdFrozen: true
|
||
}
|
||
};
|
||
}
|
||
|
||
function hardwareRunnerTrace({
|
||
traceId,
|
||
startedAt,
|
||
finishedAt,
|
||
workspace,
|
||
session,
|
||
request,
|
||
operationId,
|
||
dispatchStatus,
|
||
exitCode,
|
||
status,
|
||
capabilityLevel,
|
||
events,
|
||
blocker = null
|
||
}) {
|
||
return {
|
||
traceId,
|
||
runnerKind: CODE_AGENT_HARDWARE_RUNNER_KIND,
|
||
workspace,
|
||
sandbox: CODE_AGENT_HARDWARE_SANDBOX,
|
||
sessionMode: CODE_AGENT_HARDWARE_SESSION_MODE,
|
||
sessionId: session?.sessionId ?? null,
|
||
sessionStatus: session?.status ?? null,
|
||
turn: session?.turn ?? null,
|
||
sessionReused: session?.reused ?? false,
|
||
implementationType: CODE_AGENT_HARDWARE_IMPLEMENTATION_TYPE,
|
||
limitations: [...HARDWARE_LIMITATION_FLAGS],
|
||
startedAt,
|
||
finishedAt,
|
||
events,
|
||
eventLabels: events,
|
||
method: HARDWARE_INVOKE_SHELL_METHOD,
|
||
serviceId: CLOUD_API_SERVICE_ID,
|
||
projectId: request?.projectId ?? null,
|
||
gatewaySessionId: request?.gatewaySessionId ?? null,
|
||
resourceId: request?.resourceId ?? null,
|
||
capabilityId: request?.capabilityId ?? null,
|
||
operationId,
|
||
dispatchStatus,
|
||
exitCode,
|
||
accepted: status === "completed",
|
||
status,
|
||
capabilityLevel,
|
||
blocker,
|
||
blockers: blocker ? [blocker] : [],
|
||
outputTruncated: false,
|
||
valuesPrinted: false,
|
||
directGatewayCalls: false,
|
||
directBoxCalls: false,
|
||
directPatchPanelCalls: false,
|
||
fallbackUsed: false,
|
||
note: "Controlled hardware capability route uses cloud-api internal JSON-RPC hardware.invoke.shell with frozen serviceId; Code Agent does not call gateway directly."
|
||
};
|
||
}
|
||
|
||
function hardwareStructuredResult({ request, command, result, dispatch = {}, operationId, dispatchStatus, exitCode, stdoutSummary, stderrSummary, topology, blocker = null, rpcError = null }) {
|
||
return {
|
||
type: blocker ? "hardware_capability_blocker" : "hardware_capability_result",
|
||
status: blocker ? "blocked" : "succeeded",
|
||
method: HARDWARE_INVOKE_SHELL_METHOD,
|
||
serviceId: CLOUD_API_SERVICE_ID,
|
||
projectId: request?.projectId ?? null,
|
||
gatewaySessionId: request?.gatewaySessionId ?? null,
|
||
resourceId: request?.resourceId ?? null,
|
||
capabilityId: request?.capabilityId ?? null,
|
||
capabilityName: "shell.exec",
|
||
capabilityLevel: blocker ? CODE_AGENT_HARDWARE_CAPABILITY_BLOCKED : CODE_AGENT_HARDWARE_CAPABILITY_EXECUTED,
|
||
operationId,
|
||
dispatchStatus,
|
||
exitCode,
|
||
stdoutSummary,
|
||
stderrSummary,
|
||
command: command ? redactCommand(command) : null,
|
||
topology: {
|
||
source: topology?.source ?? null,
|
||
fallbackUsed: topology?.fallbackUsed === true,
|
||
candidates: topology?.candidateCount ?? null
|
||
},
|
||
dispatch,
|
||
result,
|
||
rpcError: rpcError ? {
|
||
code: rpcError.code,
|
||
message: rpcError.message,
|
||
reason: rpcError.data?.reason ?? null
|
||
} : null,
|
||
blocker,
|
||
path: {
|
||
summary: "Code Agent -> cloud-api internal JSON-RPC -> hardware.invoke.shell -> registered PC gateway",
|
||
segments: ["Code Agent", "cloud-api", "hardware.invoke.shell", "PC gateway"],
|
||
cloudApiOnly: true,
|
||
directGatewayCalls: false,
|
||
directBoxCalls: false,
|
||
directPatchPanelCalls: false,
|
||
openAiFallbackUsed: false
|
||
}
|
||
};
|
||
}
|
||
|
||
function hardwareReply({ request, operationId, dispatchStatus, exitCode, stdoutSummary, stderrSummary, blocker = null, blocked }) {
|
||
const lines = [];
|
||
if (blocked) {
|
||
lines.push(`PC gateway shell.exec 阻塞:${blocker.userMessage ?? blocker.summary ?? blocker.code}。`);
|
||
lines.push(`Blocker: ${blocker.code};layer=${blocker.layer ?? "unknown"};reason=${blocker.reason ?? blocker.summary ?? blocker.code}。`);
|
||
} else {
|
||
lines.push("PC gateway shell.exec 已通过 cloud-api 受控能力路由返回。");
|
||
}
|
||
lines.push(`路径:Code Agent -> cloud-api internal JSON-RPC -> hardware.invoke.shell;未直连 gateway/box/patch-panel,未使用 OpenAI text fallback。`);
|
||
lines.push(`目标:projectId=${request?.projectId ?? "null"};gatewaySessionId=${request?.gatewaySessionId ?? "null"};resourceId=${request?.resourceId ?? "null"};capabilityId=${request?.capabilityId ?? "null"}。`);
|
||
lines.push(`结果:operationId=${operationId ?? "null"};dispatchStatus=${dispatchStatus ?? "null"};exitCode=${exitCode ?? "null"}。`);
|
||
if (stdoutSummary) lines.push(`stdout 摘要:${stdoutSummary}`);
|
||
if (stderrSummary) lines.push(`stderr 摘要:${stderrSummary}`);
|
||
return boundOutput(lines.join("\n")).text;
|
||
}
|
||
|
||
function hardwareFailureBlocker({ dispatchStatus, exitCode, result, dispatch, traceId }) {
|
||
const status = String(dispatchStatus ?? "").toLowerCase();
|
||
if (status === "not_connected") {
|
||
return hardwareBlocker({
|
||
code: "hardware_gateway_offline",
|
||
layer: "gateway",
|
||
category: "gateway_offline",
|
||
summary: dispatch.message ?? "gateway is not connected",
|
||
userMessage: "目标 PC gateway 当前不在线或已过期,本次未执行。",
|
||
retryable: true,
|
||
traceId
|
||
});
|
||
}
|
||
if (status === "timed_out") {
|
||
return hardwareBlocker({
|
||
code: "hardware_dispatch_failed",
|
||
layer: "dispatch",
|
||
category: "timeout",
|
||
summary: dispatch.message ?? "gateway dispatch timed out",
|
||
userMessage: "cloud-api 已派发到 gateway,但等待回传超时。",
|
||
retryable: true,
|
||
traceId
|
||
});
|
||
}
|
||
if (Number.isInteger(exitCode) && exitCode !== 0) {
|
||
return hardwareBlocker({
|
||
code: "hardware_shell_failed",
|
||
layer: "gateway-shell",
|
||
category: "command_failed",
|
||
summary: `Windows cmd exited with ${exitCode}`,
|
||
userMessage: `Windows cmd 已执行但退出码为 ${exitCode}。`,
|
||
retryable: true,
|
||
traceId
|
||
});
|
||
}
|
||
return hardwareBlocker({
|
||
code: "hardware_dispatch_failed",
|
||
layer: "dispatch",
|
||
category: "dispatch_failed",
|
||
summary: dispatch.message ?? result?.status ?? "hardware.invoke.shell dispatch did not report a successful shell execution",
|
||
userMessage: "hardware.invoke.shell 未返回成功执行结果,本次按 dispatch failed 处理。",
|
||
retryable: true,
|
||
traceId
|
||
});
|
||
}
|
||
|
||
function hardwareRegistryBlocker({ request, gatewayRegistry, traceId }) {
|
||
if (!request || !gatewayRegistry || typeof gatewayRegistry.getSession !== "function") return null;
|
||
const session = gatewayRegistry.getSession(request.gatewaySessionId);
|
||
if (!session) {
|
||
return hardwareBlocker({
|
||
code: "hardware_session_missing",
|
||
layer: "gateway-registry",
|
||
category: "session_missing",
|
||
summary: `gateway session ${request.gatewaySessionId} is not registered in the cloud-api gateway registry`,
|
||
userMessage: "目标 gateway session 未在 cloud-api gateway registry 中登记。",
|
||
retryable: true,
|
||
traceId
|
||
});
|
||
}
|
||
if (typeof gatewayRegistry.isOnline === "function" && !gatewayRegistry.isOnline(request.gatewaySessionId)) {
|
||
return hardwareBlocker({
|
||
code: "hardware_gateway_offline",
|
||
layer: "gateway-registry",
|
||
category: "gateway_offline",
|
||
summary: `gateway session ${request.gatewaySessionId} is registered but offline or stale`,
|
||
userMessage: "目标 gateway 已登记但当前离线或心跳过期。",
|
||
retryable: true,
|
||
traceId
|
||
});
|
||
}
|
||
if (session.resourceId && session.resourceId !== request.resourceId) {
|
||
return hardwareBlocker({
|
||
code: "hardware_resource_missing",
|
||
layer: "gateway-registry",
|
||
category: "resource_missing",
|
||
summary: `resource ${request.resourceId} is not registered under gateway session ${request.gatewaySessionId}`,
|
||
userMessage: "目标 resourceId 不属于当前 gateway session。",
|
||
retryable: false,
|
||
traceId
|
||
});
|
||
}
|
||
if (Array.isArray(session.capabilities) && session.capabilities.length > 0) {
|
||
const found = session.capabilities.some((capability) => {
|
||
if (typeof capability === "string") return capability === request.capabilityId || capability === "shell.exec";
|
||
return capability?.capabilityId === request.capabilityId || capability?.id === request.capabilityId || capability?.name === "shell.exec";
|
||
});
|
||
if (!found) {
|
||
return hardwareBlocker({
|
||
code: "hardware_capability_missing",
|
||
layer: "gateway-registry",
|
||
category: "capability_missing",
|
||
summary: `capability ${request.capabilityId} is not registered on gateway session ${request.gatewaySessionId}`,
|
||
userMessage: "目标 shell.exec capability 未在该 gateway session 上登记。",
|
||
retryable: false,
|
||
traceId
|
||
});
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function resolveHardwareTopology(explicit, { topology, env = process.env } = {}) {
|
||
const explicitComplete = ["projectId", "gatewaySessionId", "resourceId", "capabilityId"].every((field) => explicit[field]);
|
||
const candidates = hardwareTopologyCandidates({ topology, env });
|
||
const matches = candidates.filter((candidate) => topologyMatchesExplicit(candidate, explicit));
|
||
|
||
if (explicitComplete && (candidates.length === 0 || matches.length <= 1)) {
|
||
const matched = matches[0] ?? {};
|
||
return {
|
||
ok: true,
|
||
source: Object.keys(explicit).every((key) => explicit[key] === matched[key]) ? matched.topologySource ?? "configured-topology" : "explicit-fields",
|
||
fallbackUsed: false,
|
||
candidateCount: matches.length,
|
||
request: {
|
||
projectId: explicit.projectId,
|
||
gatewaySessionId: explicit.gatewaySessionId,
|
||
resourceId: explicit.resourceId,
|
||
capabilityId: explicit.capabilityId
|
||
}
|
||
};
|
||
}
|
||
|
||
if (matches.length === 1) {
|
||
const match = matches[0];
|
||
return {
|
||
ok: true,
|
||
source: match.topologySource ?? "configured-topology",
|
||
fallbackUsed: !explicitComplete,
|
||
candidateCount: 1,
|
||
request: {
|
||
projectId: explicit.projectId ?? match.projectId,
|
||
gatewaySessionId: explicit.gatewaySessionId ?? match.gatewaySessionId,
|
||
resourceId: explicit.resourceId ?? match.resourceId,
|
||
capabilityId: explicit.capabilityId ?? match.capabilityId
|
||
}
|
||
};
|
||
}
|
||
|
||
const missing = Object.entries(explicit).filter(([, value]) => !value).map(([key]) => key);
|
||
if (matches.length > 1) {
|
||
return {
|
||
ok: false,
|
||
source: "ambiguous-topology",
|
||
fallbackUsed: false,
|
||
candidateCount: matches.length,
|
||
blocker: hardwareBlocker({
|
||
code: "hardware_topology_ambiguous",
|
||
layer: "topology",
|
||
category: "topology_ambiguous",
|
||
summary: `PC gateway shell.exec topology is not unique; ${matches.length} candidates match the request.`,
|
||
userMessage: "PC gateway shell.exec 拓扑不唯一,请显式提供 projectId/gatewaySessionId/resourceId/capabilityId。",
|
||
retryable: false,
|
||
missing
|
||
})
|
||
};
|
||
}
|
||
|
||
const explicitAny = Object.values(explicit).some(Boolean);
|
||
return {
|
||
ok: false,
|
||
source: "missing-topology",
|
||
fallbackUsed: false,
|
||
candidateCount: 0,
|
||
blocker: hardwareBlocker({
|
||
code: explicitAny ? "hardware_capability_missing" : "hardware_topology_missing",
|
||
layer: "topology",
|
||
category: explicitAny ? "capability_missing" : "topology_missing",
|
||
summary: explicitAny
|
||
? "No registered PC gateway shell.exec topology matches the explicit request."
|
||
: "PC gateway shell.exec request does not include enough identifiers and no unique topology is available.",
|
||
userMessage: explicitAny
|
||
? "显式请求的 PC gateway shell.exec capability 未登记或不匹配。"
|
||
: "缺少 PC gateway shell.exec 拓扑字段,且当前没有唯一可解析拓扑。",
|
||
retryable: false,
|
||
missing
|
||
})
|
||
};
|
||
}
|
||
|
||
function hardwareTopologyCandidates({ topology, env = process.env } = {}) {
|
||
if (Array.isArray(topology)) return topology.map(normalizeTopologyCandidate).filter(Boolean);
|
||
if (Array.isArray(topology?.candidates)) return topology.candidates.map(normalizeTopologyCandidate).filter(Boolean);
|
||
if (Array.isArray(topology?.capabilities)) return topology.capabilities.map(normalizeTopologyCandidate).filter(Boolean);
|
||
if (topology?.pcGatewayShell) return [normalizeTopologyCandidate(topology.pcGatewayShell)].filter(Boolean);
|
||
|
||
const envCandidate = normalizeTopologyCandidate({
|
||
projectId: env.HWLAB_CODE_AGENT_PC_GATEWAY_PROJECT_ID,
|
||
gatewaySessionId: env.HWLAB_CODE_AGENT_PC_GATEWAY_SESSION_ID,
|
||
resourceId: env.HWLAB_CODE_AGENT_PC_GATEWAY_RESOURCE_ID,
|
||
capabilityId: env.HWLAB_CODE_AGENT_PC_GATEWAY_CAPABILITY_ID,
|
||
topologySource: "env:HWLAB_CODE_AGENT_PC_GATEWAY_*"
|
||
});
|
||
if (envCandidate) return [envCandidate];
|
||
|
||
return DEFAULT_DEV_MVP_PC_GATEWAY_TOPOLOGY.map(normalizeTopologyCandidate).filter(Boolean);
|
||
}
|
||
|
||
function normalizeTopologyCandidate(candidate = {}) {
|
||
const normalized = {
|
||
projectId: cleanProtocolId(candidate.projectId, "prj"),
|
||
gatewaySessionId: cleanProtocolId(candidate.gatewaySessionId, "gws"),
|
||
resourceId: cleanProtocolId(candidate.resourceId, "res"),
|
||
capabilityId: cleanProtocolId(candidate.capabilityId, "cap"),
|
||
capabilityName: candidate.capabilityName ?? candidate.name ?? "shell.exec",
|
||
topologySource: candidate.topologySource ?? candidate.source ?? "configured-topology"
|
||
};
|
||
if (!normalized.projectId || !normalized.gatewaySessionId || !normalized.resourceId || !normalized.capabilityId) return null;
|
||
if (normalized.capabilityName !== "shell.exec" && normalized.capabilityId !== "cap_windows_cmd_exec") return null;
|
||
return normalized;
|
||
}
|
||
|
||
function topologyMatchesExplicit(candidate, explicit) {
|
||
return Object.entries(explicit).every(([field, value]) => !value || candidate[field] === value);
|
||
}
|
||
|
||
function mentionsPcGatewayShellCapability(text, params = {}) {
|
||
if (params.capabilityId || params.gatewaySessionId || params.resourceId || params.input?.command || params.command) {
|
||
return true;
|
||
}
|
||
return /PC\s*gateway|Windows\s+cmd|cmd(?:\.exe)?\s*\/c|shell\.exec|hardware\.invoke\.shell|cap_windows_cmd_exec|res_windows_host|gws_DESKTOP/iu.test(text);
|
||
}
|
||
|
||
function extractProtocolField(text, field, prefix) {
|
||
const aliases = field === "gatewaySessionId"
|
||
? "(?:gatewaySessionId|gatewaySession|session)"
|
||
: field;
|
||
const match = String(text ?? "").match(new RegExp(`${aliases}\\s*[:=]\\s*([A-Za-z][A-Za-z0-9._:-]+)`, "iu"));
|
||
return cleanProtocolId(match?.[1], prefix);
|
||
}
|
||
|
||
function extractWindowsCommand(text) {
|
||
const match = String(text ?? "").match(/\bcmd(?:\.exe)?\s*\/c\s+[\s\S]+$/iu);
|
||
if (!match) return null;
|
||
return trimCommandTail(match[0]);
|
||
}
|
||
|
||
function trimCommandTail(command) {
|
||
return String(command ?? "")
|
||
.replace(/(?:。|;)\s*(?:返回|请返回|输出|并返回|$)[\s\S]*$/u, "")
|
||
.replace(/\s+(?:返回|请返回|输出|并返回)\s+(?:stdout|stderr|exitCode|退出码)[\s\S]*$/iu, "")
|
||
.replace(/[。;]\s*$/u, "")
|
||
.trim();
|
||
}
|
||
|
||
function normalizeWindowsCommand(command) {
|
||
if (typeof command !== "string") return null;
|
||
const trimmed = trimCommandTail(command);
|
||
return trimmed || null;
|
||
}
|
||
|
||
function isAllowedWindowsCmd(command) {
|
||
const value = String(command ?? "").trim();
|
||
if (!/^cmd(?:\.exe)?\s+\/c\s+\S/iu.test(value)) return false;
|
||
return !/\b(?:bash|sh|kubectl|powershell|pwsh)\b|sh\s+-c/iu.test(value);
|
||
}
|
||
|
||
function acquireHardwareSession(sessionRegistry, { conversationId, sessionId, traceId, workspace, now } = {}) {
|
||
if (!sessionRegistry || typeof sessionRegistry.acquire !== "function") return { session: null, blocker: null };
|
||
const acquired = sessionRegistry.acquire({
|
||
conversationId,
|
||
sessionId,
|
||
workspace,
|
||
sandbox: CODE_AGENT_HARDWARE_SANDBOX,
|
||
runnerKind: CODE_AGENT_HARDWARE_RUNNER_KIND,
|
||
sessionMode: CODE_AGENT_HARDWARE_SESSION_MODE,
|
||
capabilityLevel: CODE_AGENT_HARDWARE_CAPABILITY_EXECUTED,
|
||
implementationType: CODE_AGENT_HARDWARE_IMPLEMENTATION_TYPE,
|
||
traceId,
|
||
now
|
||
});
|
||
if (acquired.ok) return { session: acquired.session, blocker: null };
|
||
return {
|
||
session: acquired.session ?? null,
|
||
blocker: hardwareBlocker({
|
||
code: acquired.code ?? "hardware_session_blocked",
|
||
layer: "session",
|
||
category: "session_blocked",
|
||
summary: acquired.message ?? "Code Agent hardware capability session could not be acquired.",
|
||
userMessage: "Code Agent 硬件能力 session 当前不可用。",
|
||
retryable: true,
|
||
traceId
|
||
})
|
||
};
|
||
}
|
||
|
||
function releaseHardwareSession(sessionRegistry, session, { now, traceId, conversationId } = {}) {
|
||
if (!session || !sessionRegistry || typeof sessionRegistry.release !== "function") return session;
|
||
return sessionRegistry.release(session.sessionId, {
|
||
now,
|
||
traceId,
|
||
conversationId,
|
||
reused: session.reused,
|
||
status: "idle"
|
||
}) ?? session;
|
||
}
|
||
|
||
function appendHardwareTraceCompletion(traceRecorder, runnerResult) {
|
||
traceRecorder?.append({
|
||
type: "tool_call",
|
||
status: runnerResult.capabilityLevel === CODE_AGENT_HARDWARE_CAPABILITY_EXECUTED ? "completed" : "blocked",
|
||
label: runnerResult.capabilityLevel === CODE_AGENT_HARDWARE_CAPABILITY_EXECUTED
|
||
? "tool:hardware.invoke.shell:completed"
|
||
: "tool:hardware.invoke.shell:blocked",
|
||
toolName: HARDWARE_INVOKE_SHELL_METHOD,
|
||
outputSummary: `dispatchStatus=${runnerResult.dispatchStatus ?? "null"}; exitCode=${runnerResult.exitCode ?? "null"}; operationId=${runnerResult.operationId ?? "null"}`,
|
||
terminal: false
|
||
});
|
||
}
|
||
|
||
function skippedCodexStdioFeasibility({ workspace, reason }) {
|
||
return {
|
||
checked: true,
|
||
skipped: true,
|
||
reason,
|
||
sourceIssue: "pikasTech/HWLAB#460",
|
||
status: "skipped",
|
||
ready: false,
|
||
canStartLongLivedCodexStdio: false,
|
||
commandProbe: {
|
||
ready: false,
|
||
skipped: true,
|
||
reason
|
||
},
|
||
workspace: workspace ?? process.cwd(),
|
||
sandbox: CODE_AGENT_HARDWARE_SANDBOX,
|
||
blockers: [],
|
||
blockerCodes: [],
|
||
summary: "Registered hardware capability requests use deterministic cloud-api JSON-RPC route; Codex stdio and text fallback are not part of this control decision."
|
||
};
|
||
}
|
||
|
||
function hardwareLongLivedGate(session) {
|
||
return {
|
||
status: "skipped",
|
||
ready: false,
|
||
provider: CODE_AGENT_HARDWARE_PROVIDER,
|
||
runnerKind: CODE_AGENT_HARDWARE_RUNNER_KIND,
|
||
sessionId: session?.sessionId ?? null,
|
||
sessionMode: CODE_AGENT_HARDWARE_SESSION_MODE,
|
||
implementationType: CODE_AGENT_HARDWARE_IMPLEMENTATION_TYPE,
|
||
blockers: [],
|
||
reason: "hardware_capability_deterministic_route"
|
||
};
|
||
}
|
||
|
||
function hardwareSkills({ status, blockers }) {
|
||
return {
|
||
status,
|
||
items: [{
|
||
name: HARDWARE_INVOKE_SHELL_METHOD,
|
||
summary: "Controlled cloud-api hardware capability route for registered PC gateway shell.exec.",
|
||
capabilityLevel: status === "used" ? CODE_AGENT_HARDWARE_CAPABILITY_EXECUTED : CODE_AGENT_HARDWARE_CAPABILITY_BLOCKED
|
||
}],
|
||
count: 1,
|
||
blockers
|
||
};
|
||
}
|
||
|
||
function hardwareBlocker({ code, layer, category, summary, userMessage, retryable, traceId = null, reason = null, missing = [] }) {
|
||
return {
|
||
code,
|
||
layer,
|
||
category,
|
||
retryable: Boolean(retryable),
|
||
summary: redactText(summary),
|
||
message: redactText(summary),
|
||
userMessage,
|
||
reason: reason ? redactText(reason) : undefined,
|
||
traceId,
|
||
route: HARDWARE_INVOKE_SHELL_METHOD,
|
||
toolName: HARDWARE_INVOKE_SHELL_METHOD,
|
||
missing
|
||
};
|
||
}
|
||
|
||
function sessionReuseEvidence(session) {
|
||
return {
|
||
conversationId: session.conversationId,
|
||
sessionId: session.sessionId,
|
||
mapped: true,
|
||
reused: session.reused,
|
||
turn: session.turn,
|
||
previousTurns: Math.max(0, session.turn - 1),
|
||
workspace: session.workspace,
|
||
createdAt: session.createdAt,
|
||
updatedAt: session.updatedAt,
|
||
idleTimeoutMs: session.idleTimeoutMs,
|
||
expiresAt: session.expiresAt,
|
||
lastTraceId: session.lastTraceId,
|
||
status: session.status
|
||
};
|
||
}
|
||
|
||
function safeOutput(value) {
|
||
return redactText(value);
|
||
}
|
||
|
||
function summarizeOutput(value) {
|
||
const text = String(value ?? "").trim().replace(/\s+/gu, " ");
|
||
return text.length > COMMAND_SUMMARY_LIMIT ? `${text.slice(0, COMMAND_SUMMARY_LIMIT)}...` : text;
|
||
}
|
||
|
||
function boundOutput(value, maxLength = OUTPUT_LIMIT) {
|
||
const text = String(value ?? "");
|
||
if (text.length <= maxLength) return { text, truncated: false };
|
||
return {
|
||
text: `${text.slice(0, maxLength)}\n[output truncated at ${maxLength} bytes]`,
|
||
truncated: true
|
||
};
|
||
}
|
||
|
||
function redactCommand(value) {
|
||
return summarizeOutput(redactText(value));
|
||
}
|
||
|
||
function redactText(value) {
|
||
return String(value ?? "")
|
||
.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gu, "Bearer ***")
|
||
.replace(/sk-[A-Za-z0-9._-]+/gu, "sk-***")
|
||
.replace(/([A-Za-z0-9_]*TOKEN[A-Za-z0-9_]*=)[^\s]+/giu, "$1***")
|
||
.replace(/([A-Za-z0-9_]*KEY[A-Za-z0-9_]*=)[^\s]+/giu, "$1***");
|
||
}
|
||
|
||
function cleanProtocolId(value, prefix) {
|
||
if (typeof value !== "string") return null;
|
||
const trimmed = value.trim();
|
||
if (!trimmed) return null;
|
||
if (/^[a-z][a-z0-9]*_[A-Za-z0-9._:-]+$/u.test(trimmed)) return trimmed;
|
||
return `${prefix}_${trimmed.replace(/[^A-Za-z0-9._:-]/gu, "-").slice(0, 80)}`;
|
||
}
|
||
|
||
function nowIso(now) {
|
||
return typeof now === "function" ? now() : new Date().toISOString();
|
||
}
|