fix: honor AgentRun durable dispatch boundary
This commit is contained in:
@@ -85,6 +85,20 @@ const AGENTRUN_PROVIDER_TRACE_WIRE_API = "agentrun-v01-command-result";
|
||||
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"]);
|
||||
const AGENTRUN_DURABLE_TRANSIENT_ENV_RESERVED = new Set([
|
||||
"AGENTRUN_API_KEY",
|
||||
"AUTH_PASSWORD",
|
||||
"CODEX_API_KEY",
|
||||
"CODEX_HOME",
|
||||
"GH_TOKEN",
|
||||
"GITHUB_TOKEN",
|
||||
"HOME",
|
||||
"OPENAI_API_KEY",
|
||||
"PROVIDER_TOKEN",
|
||||
"UNIDESK_AUTH_PASSWORD",
|
||||
"UNIDESK_PROVIDER_TOKEN",
|
||||
"UNIDESK_SSH_CLIENT_TOKEN"
|
||||
]);
|
||||
const HWLAB_RESOURCE_GIT_BUNDLES = Object.freeze([
|
||||
Object.freeze({ name: "hwlab-tools", subpath: "tools", target_path: "tools" }),
|
||||
Object.freeze({ name: "hwlab-agent-skills", subpath: "skills", target_path: ".agents/skills" })
|
||||
@@ -1599,22 +1613,47 @@ function buildAgentRunDurableDispatchInput({ env, traceId, backendProfile }) {
|
||||
const runtimeAuthority = requireAgentRunRuntimeAuthority(env);
|
||||
const baseTransient = buildAgentRunTransientEnv(env, { providerProfile: backendProfile, parentTraceId: traceId });
|
||||
assertAgentRunTransientRuntimeAuthority(baseTransient, runtimeAuthority);
|
||||
if (baseTransient.some((entry) => entry?.sensitive === true)) {
|
||||
throw adapterError("agentrun_durable_dispatch_secret_forbidden", "AgentRun durable dispatch input must not persist sensitive transient environment values.");
|
||||
}
|
||||
assertAgentRunDurableTransientEnv(baseTransient);
|
||||
return {
|
||||
idempotencyKey: traceId,
|
||||
namespace: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE, env.AGENTRUN_RUNTIME_NAMESPACE, DEFAULT_RUNNER_NAMESPACE),
|
||||
...(firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_RUNNER_IMAGE, env.AGENTRUN_RUNNER_IMAGE)
|
||||
? { image: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_RUNNER_IMAGE, env.AGENTRUN_RUNNER_IMAGE) }
|
||||
: {}),
|
||||
...(firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_RUNNER_SERVICE_ACCOUNT, env.AGENTRUN_RUNNER_SERVICE_ACCOUNT)
|
||||
? { serviceAccountName: firstNonEmpty(env.HWLAB_CODE_AGENT_AGENTRUN_RUNNER_SERVICE_ACCOUNT, env.AGENTRUN_RUNNER_SERVICE_ACCOUNT) }
|
||||
: {}),
|
||||
...(baseTransient.length > 0 ? { transientEnv: baseTransient } : {})
|
||||
};
|
||||
}
|
||||
|
||||
function assertAgentRunDurableTransientEnv(entries) {
|
||||
const seen = new Set();
|
||||
for (const entry of Array.isArray(entries) ? entries : []) {
|
||||
const name = firstNonEmpty(entry?.name);
|
||||
const value = firstNonEmpty(entry?.value);
|
||||
if (!/^[A-Z_][A-Z0-9_]*$/u.test(name) || !value || seen.has(name)) {
|
||||
throw adapterError("agentrun_durable_dispatch_transient_env_invalid", "AgentRun durable dispatch transient environment must contain unique non-empty environment entries.");
|
||||
}
|
||||
if (entry?.sensitive === true) {
|
||||
throw adapterError("agentrun_durable_dispatch_secret_forbidden", "AgentRun durable dispatch input must not persist sensitive transient environment values.");
|
||||
}
|
||||
if (agentRunReservedTransientEnvName(name)) {
|
||||
throw adapterError("agentrun_durable_dispatch_transient_env_reserved", `AgentRun durable dispatch transient environment cannot override runtime-owned ${name}.`);
|
||||
}
|
||||
seen.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
function agentRunReservedTransientEnvName(name) {
|
||||
return name.startsWith("AGENTRUN_")
|
||||
|| name.startsWith("KUBERNETES_")
|
||||
|| AGENTRUN_DURABLE_TRANSIENT_ENV_RESERVED.has(name)
|
||||
|| new Set([
|
||||
"HTTP_PROXY",
|
||||
"HTTPS_PROXY",
|
||||
"ALL_PROXY",
|
||||
"NO_PROXY",
|
||||
"GIT_SSH_COMMAND",
|
||||
"OTEL_SERVICE_NAME",
|
||||
"OTEL_EXPORTER_OTLP_ENDPOINT",
|
||||
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"
|
||||
]).has(name);
|
||||
}
|
||||
|
||||
function requireAgentRunDurableDispatchIntent(value) {
|
||||
const intent = value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
||||
const admittedStates = new Set(["pending", "dispatching", "retry", "dispatched"]);
|
||||
|
||||
@@ -64,7 +64,11 @@ test("fresh gpt.pika turn returns running only after durable dispatch intent is
|
||||
assert.equal(commandCall.body.dispatch.kind, "kubernetes-job");
|
||||
assert.equal(Object.hasOwn(commandCall.body.dispatch.input, "commandId"), false);
|
||||
assert.equal(commandCall.body.dispatch.input.idempotencyKey, traceId);
|
||||
assert.equal(Object.hasOwn(commandCall.body.dispatch.input, "namespace"), false);
|
||||
assert.equal(Object.hasOwn(commandCall.body.dispatch.input, "image"), false);
|
||||
assert.equal(Object.hasOwn(commandCall.body.dispatch.input, "serviceAccountName"), false);
|
||||
assert.equal(commandCall.body.dispatch.input.transientEnv.every((entry) => entry.sensitive === false), true);
|
||||
assert.equal(commandCall.body.dispatch.input.transientEnv.every((entry) => !entry.name.startsWith("AGENTRUN_") && !entry.name.startsWith("KUBERNETES_")), true);
|
||||
assert.equal(JSON.stringify(commandCall.body).includes("hwl_live_must_not_be_persisted"), false);
|
||||
assert.equal(calls.some((call) => call.path.endsWith("/runner-jobs")), false);
|
||||
assert.ok(traceStore.snapshot(traceId).events.some((event) => event.label === "agentrun:dispatch-intent:admitted"));
|
||||
@@ -222,6 +226,9 @@ function agentRunTestEnv() {
|
||||
HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID: "NC01",
|
||||
HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT: sourceCommit,
|
||||
HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE: "gpt.pika",
|
||||
HWLAB_CODE_AGENT_AGENTRUN_RUNNER_NAMESPACE: "caller-controlled-namespace-must-be-ignored",
|
||||
HWLAB_CODE_AGENT_AGENTRUN_RUNNER_IMAGE: "caller-controlled-image-must-be-ignored",
|
||||
HWLAB_CODE_AGENT_AGENTRUN_RUNNER_SERVICE_ACCOUNT: "caller-controlled-service-account-must-be-ignored",
|
||||
HWLAB_RUNTIME_API_URL: "http://hwlab-cloud-api.hwlab-v03.svc.cluster.local:6667",
|
||||
HWLAB_RUNTIME_WEB_URL: "http://hwlab-cloud-web.hwlab-v03.svc.cluster.local:8080",
|
||||
HWLAB_RUNTIME_NAMESPACE: "hwlab-v03",
|
||||
|
||||
Reference in New Issue
Block a user