fix: expose blocked AgentRun cancel disposition (#2092)

This commit is contained in:
Lyon
2026-06-25 07:28:16 +08:00
committed by GitHub
parent ec41a065f8
commit 5bcffeb263
6 changed files with 704 additions and 420 deletions
+56 -420
View File
@@ -1,7 +1,7 @@
// SPEC: PJ2026-0104010803 Workbench唯一投影 draft-2026-06-19-p1-agentrun-incremental-cursor; draft-2026-06-20-p2-terminal-outbox-recovery; PJ2026-010205 HWLAB接入 draft-2026-06-17-r0; PJ2026-01060505 Workbench Performance draft-2026-06-17-p0.
// Responsibility: AgentRun v0.1 adapter, incremental Workbench projection cursor sync, and upstream timing projection for Cloud API observability.
import { createHash, randomUUID } from "node:crypto";
import { randomUUID } from "node:crypto";
import { defaultCodeAgentTraceStore } from "./code-agent-trace-store.ts";
import { decorateCodeAgentSession } from "./code-agent-session-lifecycle.ts";
@@ -11,30 +11,55 @@ import {
safeOpaqueId,
safeSessionId,
safeTraceId,
text,
truthyFlag
text
} from "./server-http-utils.ts";
import { backendPerformanceRouteTemplate, currentBackendPerformanceContext } from "./backend-performance.ts";
import { currentBackendPerformanceContext } from "./backend-performance.ts";
import {
DEFAULT_AGENTRUN_MGR_URL,
agentRunJson,
isAgentRunManagerInternalServiceHost,
resolveAgentRunManagerUrl
} from "./code-agent-agentrun-runtime.ts";
import {
agentRunAlreadyTerminalCancelPayload,
agentRunCancelBlockedPayload,
agentRunCancelTerminalStatus
} from "./code-agent-agentrun-cancel.ts";
import {
DEFAULT_AGENTRUN_REPO_URL,
agentRunAvailabilityBlocker,
agentRunProviderIdOrNull,
agentRunTraceMeta,
backendForBackendProfile,
modelForBackendProfile,
providerForBackendProfile,
requireAgentRunSourceCommit,
resolveAgentRunBackendProfile,
resolveAgentRunProviderId,
resolveAgentRunRepoUrl
} from "./code-agent-agentrun-profile.ts";
import {
agentRunPromptEventFields,
agentRunPromptFields,
agentRunPromptMetadataFromParams,
agentRunPromptMetadataFromSession,
agentRunPromptMetadataFromText,
messageAuthorityTextValue
} from "./code-agent-agentrun-prompt.ts";
import { agentRunResultSyncDeferred, buildAgentRunProjectionEventsFetchPlan, buildWorkbenchProjectionStateUpdate } from "./workbench-projection-cursor.ts";
import { codeAgentOtelTraceFields, codeAgentOtelTraceContext, emitCodeAgentOtelSpan } from "./otel-trace.ts";
const ADAPTER_ID = "agentrun-v01";
const DEFAULT_AGENTRUN_MGR_URL = "http://agentrun-mgr.agentrun-v01.svc.cluster.local:8080";
const AGENTRUN_MANAGER_HOST_PATTERN = /^agentrun-mgr\.[a-z0-9]([-a-z0-9]*[a-z0-9])?\.svc\.cluster\.local$/u;
const DEFAULT_TENANT_ID = "hwlab";
const DEFAULT_PROJECT_ID = "pikasTech/HWLAB";
const DEFAULT_REPO_URL = "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git";
const DEFAULT_RUNNER_NAMESPACE = "agentrun-v01";
const DEFAULT_TIMEOUT_MS = 1_200_000;
const AGENTRUN_BACKEND_PREFIX = ADAPTER_ID;
const AGENTRUN_RUNNER_KIND = "agentrun-v01-shared-runner";
const AGENTRUN_SESSION_MODE = "agentrun-v01-durable-session";
const AGENTRUN_IMPLEMENTATION_TYPE = "agentrun-v01-shared-execution-infra";
const AGENTRUN_CAPABILITY_LEVEL = "agentrun-v01-shared-code-agent-session";
const AGENTRUN_PROVIDER_TRACE_PROTOCOL = "agentrun-v01-jsonrpc";
const AGENTRUN_PROVIDER_TRACE_WIRE_API = "agentrun-v01-command-result";
const AGENTRUN_BACKEND_ALIASES = Object.freeze({ "codex-api": "codex", codex: "codex" });
const AGENTRUN_BACKEND_PROFILE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/u;
const THREAD_CONTINUITY_POLICY = "hwlab-agentrun-v01-reuse-runner-thread";
const SESSION_POLICY_RUN_LOCAL = "hwlab-agentrun-v01-session-runner-reuse";
const TERMINAL_RUN_STATUSES = new Set(["completed", "failed", "blocked", "cancelled", "canceled"]);
@@ -932,7 +957,7 @@ export async function refreshAgentRunTrace({ traceId, result = null, options = {
}
export async function cancelAgentRunChatTurn({ traceId, currentResult = null, options = {}, traceStore = defaultCodeAgentTraceStore }) {
const mapped = currentResult ?? await loadPersistedAgentRunResult(traceId, options);
let mapped = currentResult ?? await loadPersistedAgentRunResult(traceId, options);
if (!mapped?.agentRun?.commandId) return null;
const env = options.env ?? process.env;
const timeoutMs = parsePositiveInteger(env.HWLAB_CODE_AGENT_AGENTRUN_HTTP_TIMEOUT_MS, 20_000);
@@ -947,12 +972,25 @@ export async function cancelAgentRunChatTurn({ traceId, currentResult = null, op
};
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
const managerUrl = resolveAgentRunManagerUrl(env, mapped.agentRun.managerUrl);
await agentRunJson(fetchImpl, managerUrl, `/api/v1/commands/${encodeURIComponent(mapped.agentRun.commandId)}/cancel`, {
method: "POST",
body: { reason: "hwlab-user-cancel", traceId },
timeoutMs: cancelTimeoutMs,
env
});
try {
const synced = await syncAgentRunChatResult({ traceId, currentResult: mapped, options: cancelOptions, traceStore, forceResultSync: true });
mapped = synced?.result ?? synced ?? mapped;
} catch {
// Cancel must still expose the command cancel disposition; result refresh failures are reported by the normal trace/result paths.
}
const terminalStatus = agentRunCancelTerminalStatus(mapped);
if (terminalStatus) return agentRunAlreadyTerminalCancelPayload({ traceId, payload: mapped, terminalStatus, options: cancelOptions, traceStore });
const cancelPath = `/api/v1/commands/${encodeURIComponent(mapped.agentRun.commandId)}/cancel`;
try {
await agentRunJson(fetchImpl, managerUrl, cancelPath, {
method: "POST",
body: { reason: "hwlab-user-cancel", traceId },
timeoutMs: cancelTimeoutMs,
env
});
} catch (error) {
return agentRunCancelBlockedPayload({ traceId, payload: mapped, error, cancelPath, options: cancelOptions, traceStore });
}
traceStore.append(traceId, agentRunTraceEvent({
type: "cancel",
status: "canceled",
@@ -989,45 +1027,6 @@ export async function cancelAgentRunChatTurn({ traceId, currentResult = null, op
return payload;
}
function agentRunCancelTerminalStatus(payload = null) {
if (!payload || typeof payload !== "object") return null;
for (const value of [payload.agentRun?.terminalStatus, payload.agentRun?.commandState, payload.agentRun?.commandStatus]) {
const status = normalizeAgentRunStatus(value);
if (isAgentRunTerminalStatus(status)) return status;
}
return null;
}
function agentRunAlreadyTerminalCancelPayload({ traceId, payload, terminalStatus, options = {}, traceStore = defaultCodeAgentTraceStore } = {}) {
const normalizedTerminalStatus = normalizeAgentRunStatus(terminalStatus);
const agentRun = payload?.agentRun && typeof payload.agentRun === "object" ? payload.agentRun : {};
traceStore.append(traceId, agentRunTraceEvent({
type: "cancel",
status: normalizedTerminalStatus || "completed",
label: "agentrun:cancel:already_terminal",
message: "AgentRun command is already terminal; HWLAB did not forward cancel or overwrite the terminal Workbench projection.",
runId: agentRun.runId,
commandId: agentRun.commandId,
terminal: true,
valuesPrinted: false
}, agentRun));
const payloadStatus = normalizeAgentRunStatus(payload?.status);
const status = isAgentRunTerminalStatus(payloadStatus) ? payloadStatus : normalizedTerminalStatus || payloadStatus || "completed";
const result = {
...payload,
status,
canceled: false,
alreadyTerminal: true,
cancelStatus: "already_terminal",
updatedAt: nowIso(options.now),
runnerTrace: traceStore.snapshot(traceId),
agentRun: { ...agentRun, terminalStatus: agentRun.terminalStatus ?? normalizedTerminalStatus, valuesPrinted: false },
valuesPrinted: false
};
options.codeAgentChatResults?.set?.(traceId, result);
return result;
}
export async function steerAgentRunChatTurn({ traceId, currentResult = null, params = {}, options = {}, traceStore = defaultCodeAgentTraceStore }) {
const mapped = currentResult ?? await loadPersistedAgentRunResult(traceId, options);
if (!safeTraceId(traceId) || !mapped?.agentRun?.runId || !mapped?.agentRun?.commandId) return null;
@@ -1197,7 +1196,7 @@ export function agentRunSessionEvidence(payload = {}) {
namespace: payload.agentRun.namespace ?? null,
backendProfile: payload.agentRun.backendProfile ?? null,
managerUrl: payload.agentRun.managerUrl ?? DEFAULT_AGENTRUN_MGR_URL,
repoUrl: payload.agentRun.repoUrl ?? DEFAULT_REPO_URL,
repoUrl: payload.agentRun.repoUrl ?? DEFAULT_AGENTRUN_REPO_URL,
status: payload.agentRun.status ?? null,
runStatus: payload.agentRun.runStatus ?? null,
commandState: payload.agentRun.commandState ?? null,
@@ -1386,147 +1385,6 @@ function rejectRemovedResourceWorkspaceFiles(value) {
throw adapterError("legacy_workspace_files_removed", "workspaceFiles/resourceWorkspaceFiles are removed; use AgentRun ResourceBundleRef kind=gitbundle with bundles[]");
}
function agentRunPromptMetadataFromParams(params = {}, traceId = null) {
return agentRunPromptMetadataFromText(firstNonEmpty(params.message, params.prompt, params.text), traceId, "hwlab-code-agent-request", { fullText: true });
}
function agentRunPromptMetadataFromSession(session = null, traceId = null) {
const snapshot = session?.session && typeof session.session === "object" ? session.session : null;
const fromTraceResult = agentRunPromptMetadataFromPayload(snapshot?.traceResults?.[traceId], traceId);
if (fromTraceResult) return fromTraceResult;
const messageText = agentRunPromptTextFromMessages(snapshot?.messages, traceId);
if (messageText) return agentRunPromptMetadataFromText(messageText, traceId, "agent-session-owner-message", { fullText: true });
const preview = firstNonEmpty(snapshot?.firstUserMessagePreview);
return preview ? agentRunPromptMetadataFromText(preview, traceId, "agent-session-first-user-preview", { fullText: false }) : null;
}
function agentRunPromptMetadataFromPayload(payload = null, traceId = null) {
if (!payload || typeof payload !== "object") return null;
const prompt = payload.prompt && typeof payload.prompt === "object" ? payload.prompt : null;
const textValue = firstNonEmpty(prompt?.text, prompt?.summary, prompt?.preview, payload.promptSummary, payload.promptPreview);
const fromText = agentRunPromptMetadataFromText(textValue, traceId, prompt?.source ?? payload.promptSource ?? "agentrun-prompt-metadata", { fullText: Boolean(prompt?.text) });
return agentRunPromptMetadataFromParts({
traceId,
source: prompt?.source ?? payload.promptSource ?? fromText?.prompt?.source ?? "agentrun-prompt-metadata",
promptId: firstNonEmpty(payload.promptId, prompt?.id, fromText?.promptId),
promptSha256: firstNonEmpty(payload.promptSha256, prompt?.sha256, fromText?.promptSha256),
promptSummary: firstNonEmpty(payload.promptSummary, prompt?.summary, fromText?.promptSummary),
promptPreview: firstNonEmpty(payload.promptPreview, prompt?.preview, fromText?.promptPreview),
textChars: Number.isInteger(prompt?.textChars) ? prompt.textChars : fromText?.prompt?.textChars
});
}
function agentRunPromptTextFromMessages(messages = [], traceId = null) {
const list = Array.isArray(messages) ? messages : [];
const resolvedTraceId = safeTraceId(traceId);
const exact = list.find((message) => String(message?.role ?? "").toLowerCase() === "user" && (!resolvedTraceId || safeTraceId(message?.traceId) === resolvedTraceId));
const fallback = list.find((message) => String(message?.role ?? "").toLowerCase() === "user");
return firstNonEmpty(exact?.text, exact?.content, fallback?.text, fallback?.content);
}
function agentRunPromptMetadataFromText(value, traceId = null, source = "hwlab-code-agent-request", options = {}) {
const promptText = promptTextValue(value);
if (!promptText) return null;
return agentRunPromptMetadataFromParts({
traceId,
source,
promptSha256: options.fullText === true ? sha256Text(promptText) : undefined,
promptSummary: promptText,
promptPreview: promptText,
textChars: options.fullText === true ? promptText.length : undefined
});
}
function agentRunPromptMetadataFromParts({ traceId = null, source = "hwlab-code-agent-request", promptId = null, promptSha256 = null, promptSummary = null, promptPreview = null, textChars = undefined } = {}) {
const digest = firstNonEmpty(promptSha256);
const summary = clippedPromptText(firstNonEmpty(promptSummary, promptPreview), 240);
const preview = clippedPromptText(firstNonEmpty(promptPreview, promptSummary), 500);
const explicitId = firstNonEmpty(promptId);
if (!explicitId && !digest && !summary && !preview) return null;
const id = explicitId || agentRunPromptId(traceId, digest);
const prompt = compactObject({
id,
sha256: digest || undefined,
summary,
preview,
textChars: Number.isInteger(textChars) ? textChars : undefined,
source,
valuesPrinted: false
});
return compactObject({
promptId: id,
promptSha256: digest || undefined,
promptSummary: summary,
promptPreview: preview,
prompt,
valuesPrinted: false
}) ?? null;
}
function agentRunPromptFields(metadata = null) {
if (!metadata) return {};
return compactObject({
promptId: metadata.promptId,
promptSha256: metadata.promptSha256,
promptSummary: metadata.promptSummary,
promptPreview: metadata.promptPreview,
prompt: metadata.prompt
}) ?? {};
}
function agentRunPromptEventFields(metadata = null) {
if (!metadata) return {};
return compactObject({
promptId: metadata.promptId,
promptSha256: metadata.promptSha256,
promptSummary: metadata.promptSummary,
promptPreview: metadata.promptPreview
}) ?? {};
}
function agentRunPromptId(traceId = null, digest = null) {
const traceSuffix = safeTraceId(traceId)?.slice(4).replace(/[^A-Za-z0-9_.:-]/gu, "_");
const digestSuffix = firstNonEmpty(digest)?.slice(0, 16);
const suffix = firstNonEmpty(traceSuffix, digestSuffix);
return suffix ? `prm_${suffix}` : undefined;
}
function clippedPromptText(value, limit) {
const promptText = promptTextValue(value);
if (!promptText) return undefined;
return promptText.length > limit ? `${promptText.slice(0, limit)}...` : promptText;
}
function promptTextValue(value) {
const extracted = promptTextRaw(value).replace(/\s+/gu, " ").trim();
if (!extracted || extracted === "[object Object]") return "";
return extracted;
}
function messageAuthorityTextValue(value) {
const extracted = promptTextRaw(value).replace(/\r\n?/gu, "\n");
if (!extracted.trim() || extracted.trim() === "[object Object]") return "";
return extracted;
}
function promptTextRaw(value) {
if (value === undefined || value === null) return "";
if (typeof value === "string") return value;
if (typeof value === "number" || typeof value === "boolean") return String(value);
if (Array.isArray(value)) return value.map(promptTextRaw).filter(Boolean).join("\n");
if (typeof value === "object") {
for (const key of ["text", "content", "message", "prompt", "value", "summary", "preview", "title"]) {
const text = promptTextRaw(value[key]);
if (text) return text;
}
}
return "";
}
function sha256Text(value) {
return createHash("sha256").update(String(value)).digest("hex");
}
function adapterError(code, message, details = {}) {
return Object.assign(new Error(message), {
code,
@@ -2765,129 +2623,11 @@ function agentRunCommandExecutionEvent(base, payload = {}) {
};
}
async function agentRunJson(fetchImpl, managerUrl, path, { method = "GET", body, timeoutMs = 20_000, env = process.env } = {}) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), timeoutMs);
const performanceContext = currentBackendPerformanceContext();
const startedAt = nowMs();
let statusCode = 0;
let outcome = "ok";
try {
const headers: Record<string, string> = {};
const apiKey = resolveAgentRunApiKey(env);
if (body !== undefined) headers["content-type"] = "application/json";
if (apiKey) headers.authorization = `Bearer ${apiKey}`;
const response = await fetchImpl(`${managerUrl}${path}`, {
method,
headers: Object.keys(headers).length === 0 ? undefined : headers,
body: body === undefined ? undefined : JSON.stringify(body),
signal: controller.signal
});
statusCode = Number(response.status ?? 0);
const text = await response.text();
const parsed = text ? JSON.parse(text) : {};
if (!response.ok || parsed?.ok === false) {
outcome = "error";
throw Object.assign(new Error(parsed?.message ?? `AgentRun ${method} ${path} failed with HTTP ${response.status}`), {
code: parsed?.failureKind ?? "agentrun_request_failed",
statusCode: response.status,
agentRunError: parsed
});
}
return parsed?.ok === true && Object.hasOwn(parsed, "data") ? parsed.data : parsed;
} catch (error) {
if (error?.name === "AbortError") {
outcome = "timeout";
statusCode = 504;
throw Object.assign(new Error(`AgentRun ${method} ${path} timed out after ${timeoutMs}ms`), agentRunHttpErrorContext({ managerUrl, path, method, timeoutMs, code: "agentrun_timeout", statusCode: 504, category: "upstream-timeout" }));
}
if (outcome === "ok") outcome = "error";
throw error;
} finally {
clearTimeout(timeout);
performanceContext?.recordUpstream?.({
component: "agentrun",
operation: agentRunOperationName(method, path),
route: backendPerformanceRouteTemplate(path),
method,
statusClass: statusClass(statusCode),
outcome,
durationMs: nowMs() - startedAt
});
}
}
function agentRunOperationName(method, path) {
const route = backendPerformanceRouteTemplate(path).replace(/^\/api\/v1\//u, "").replace(/\/:id/gu, "");
return `${String(method ?? "GET").toLowerCase()}_${route || "root"}`.replace(/[^a-z0-9]+/giu, "_").replace(/^_+|_+$/gu, "").toLowerCase();
}
function statusClass(value) {
const status = Number(value);
if (!Number.isFinite(status) || status <= 0) return "unknown";
return `${Math.floor(status / 100)}xx`;
}
function nowMs() {
if (typeof performance !== "undefined" && typeof performance.now === "function") return performance.now();
return Date.now();
}
function agentRunHttpErrorContext({ managerUrl, path, method, timeoutMs, code, statusCode, category }) {
return {
code,
statusCode,
layer: "agentrun",
category,
retryable: true,
method,
route: path,
path,
timeoutMs,
timeoutStage: "agentrun-http",
managerHost: safeUrlHost(managerUrl),
valuesPrinted: false
};
}
function safeUrlHost(value) {
try { return new URL(value).host; } catch { return null; }
}
function resolveAgentRunManagerUrl(env = process.env, override = null) {
const raw = firstNonEmpty(override, env.AGENTRUN_MGR_URL, env.HWLAB_CODE_AGENT_AGENTRUN_MGR_URL, DEFAULT_AGENTRUN_MGR_URL);
const url = new URL(raw);
const host = url.hostname.toLowerCase();
const allowedByTest = truthyFlag(env.HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL) && ["127.0.0.1", "localhost"].includes(host);
if (url.protocol !== "http:" || (!isAgentRunManagerInternalServiceHost(host) && !allowedByTest)) {
throw Object.assign(new Error(`AGENTRUN_MGR_URL must use internal k3s Service DNS agentrun-mgr.<namespace>.svc.cluster.local; got ${redactUrl(raw)}`), {
code: "agentrun_internal_url_required",
statusCode: 500
});
}
url.pathname = url.pathname.replace(/\/+$/u, "");
url.search = "";
url.hash = "";
return url.toString().replace(/\/+$/u, "");
}
function resolveAgentRunApiKey(env = process.env) {
return firstNonEmpty(env.AGENTRUN_API_KEY, env.HWLAB_CODE_AGENT_AGENTRUN_API_KEY, env.HWLAB_API_KEY);
}
function isAgentRunManagerInternalServiceHost(host) {
return AGENTRUN_MANAGER_HOST_PATTERN.test(String(host ?? "").trim().toLowerCase());
}
function resolveAgentRunBackendProfile(env = process.env, params = {}) {
const requested = String(params.providerProfile ?? params.codeAgentProviderProfile ?? env.HWLAB_CODE_AGENT_AGENTRUN_BACKEND_PROFILE ?? env.HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE ?? "deepseek").trim().toLowerCase();
const fallback = String(env.HWLAB_CODE_AGENT_AGENTRUN_DEFAULT_BACKEND_PROFILE ?? "deepseek").trim().toLowerCase() || "deepseek";
const resolved = requested === "runtime-default" ? fallback : requested;
if (!resolved || resolved === "runtime-default") return "deepseek";
const mapped = AGENTRUN_BACKEND_ALIASES[resolved] ?? resolved;
return AGENTRUN_BACKEND_PROFILE_ID_PATTERN.test(mapped) ? mapped : "deepseek";
}
function agentRunMapping({ env, managerUrl, backendProfile, run, command, runnerJob, traceId, startedAt, params = {} }) {
const threadReused = Boolean(safeOpaqueId(params.threadId));
const runnerJobCreated = Boolean(runnerJob);
@@ -3096,110 +2836,6 @@ function agentRunRunnerSummary(mapping = {}) {
};
}
function agentRunTraceMeta(env = process.env) {
return {
runnerKind: AGENTRUN_RUNNER_KIND,
workspace: resolveAgentRunRepoUrl(env),
sandbox: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_SANDBOX, env.HWLAB_CODE_AGENT_CODEX_SANDBOX, "danger-full-access"),
protocol: AGENTRUN_PROVIDER_TRACE_PROTOCOL,
sessionMode: AGENTRUN_SESSION_MODE,
implementationType: AGENTRUN_IMPLEMENTATION_TYPE
};
}
function backendForBackendProfile(profile) {
return `${AGENTRUN_BACKEND_PREFIX}/${resolveAgentRunBackendProfile({}, { providerProfile: profile })}`;
}
function resolveAgentRunRepoUrl(env = process.env) {
const raw = firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_REPO_URL, env.HWLAB_BOOT_READ_URL, DEFAULT_REPO_URL);
const url = new URL(raw);
const host = url.hostname.toLowerCase();
const allowedHost = "git-mirror-http.devops-infra.svc.cluster.local";
const allowedByTest = truthyFlag(env.HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL) && ["127.0.0.1", "localhost"].includes(host);
if (url.protocol !== "http:" || (host !== allowedHost && !allowedByTest)) {
throw Object.assign(new Error(`HWLAB_CODE_AGENT_AGENTRUN_REPO_URL must use internal k3s git mirror ${DEFAULT_REPO_URL}; got ${redactUrl(raw)}`), {
code: "agentrun_internal_repo_url_required",
statusCode: 500
});
}
url.pathname = url.pathname.replace(/\/+$/u, "");
url.search = "";
url.hash = "";
return url.toString().replace(/\/+$/u, "");
}
function requireAgentRunSourceCommit(env) {
const text = String(env.HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT ?? "").trim().toLowerCase();
if (/^[0-9a-f]{40}$/u.test(text)) return text;
throw Object.assign(new Error("HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT must be a full 40-character source commit before AgentRun dispatch"), {
code: "agentrun_bundle_source_commit_invalid",
statusCode: 500
});
}
function resolveAgentRunProviderId(env = process.env) {
const providerId = firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID, env.AGENTRUN_PROVIDER_ID);
if (!providerId) {
throw Object.assign(new Error("HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID must be configured from the runtime lane node id before AgentRun dispatch"), {
code: "agentrun_provider_id_required",
statusCode: 500
});
}
if (!/^[A-Za-z0-9._-]+$/u.test(providerId)) {
throw Object.assign(new Error("HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID must be a simple node id"), {
code: "agentrun_provider_id_invalid",
statusCode: 500
});
}
return providerId;
}
function agentRunProviderIdOrNull(env = process.env) {
try {
return resolveAgentRunProviderId(env);
} catch {
return null;
}
}
function modelForBackendProfile(profile, env = process.env) {
const resolved = resolveAgentRunBackendProfile({}, { providerProfile: profile });
if (resolved === "codex") return firstNonEmpty(env.HWLAB_CODE_AGENT_CODEX_API_MODEL, env.HWLAB_CODE_AGENT_MODEL, "gpt-5.5");
if (resolved === "minimax-m3") return firstNonEmpty(env.HWLAB_CODE_AGENT_MINIMAX_M3_MODEL, "MiniMax-M3");
if (resolved === "deepseek") return firstNonEmpty(env.HWLAB_CODE_AGENT_DEEPSEEK_MODEL, "deepseek-chat");
if (resolved === "dsflash-go") return firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_DSFLASH_GO_MODEL, env.HWLAB_CODE_AGENT_DSFLASH_GO_MODEL, "deepseek-v4-flash");
const profileEnvKey = dynamicBackendProfileEnvKey(resolved);
return firstNonEmpty(env[`HWLAB_CODE_AGENT_AGENTRUN_${profileEnvKey}_MODEL`], env[`HWLAB_CODE_AGENT_${profileEnvKey}_MODEL`], resolved);
}
function providerForBackendProfile(profile) {
const resolved = resolveAgentRunBackendProfile({}, { providerProfile: profile });
if (resolved === "codex") return "codex-api";
return resolved;
}
function dynamicBackendProfileEnvKey(profile) {
return String(profile ?? "")
.trim()
.toUpperCase()
.replace(/[^A-Z0-9]+/gu, "_")
.replace(/^_+|_+$/gu, "") || "PROFILE";
}
function agentRunAvailabilityBlocker(error, scope) {
return {
code: error?.code ?? `agentrun_${String(scope).replace(/[^a-z0-9]+/giu, "_")}_blocked`,
type: "agent_blocker",
scope: `agentrun-${scope}`,
status: "open",
sourceIssue: "pikasTech/HWLAB#879",
summary: error?.message ?? `AgentRun ${scope} is not configured correctly.` ,
secretMaterialRead: false,
valuesRedacted: true
};
}
function hostForUrl(value) {
if (!value) return null;
try {