Files
pikasTech-agentrun/src/selftest/cases/76-aipod-spec.ts
T
2026-07-13 10:36:04 +02:00

337 lines
27 KiB
TypeScript

import assert from "node:assert/strict";
import { spawn } from "node:child_process";
import { copyFile, mkdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
import { ManagerClient } from "../../mgr/client.js";
import { startManagerServer } from "../../mgr/server.js";
import { MemoryAgentRunStore } from "../../mgr/store.js";
import type { JsonRecord } from "../../common/types.js";
import { validateAipodSpec } from "../../common/aipod-specs.js";
import { validateCreateCommand, validateCreateRun, validatePrimaryWorkspaceContract, validateResourceBundleRef } from "../../common/validation.js";
import { classifyGitStderr, resolveGitBundleFetchSource } from "../../runner/resource-bundle.js";
import { assertNoSecretLeak, loadArtificerImageRef, type SelfTestCase } from "../harness.js";
const selfTest: SelfTestCase = async (context) => {
const aipodSpecDir = path.join(context.tmp, "aipod-specs-76");
const artificerSpecFile = path.join(aipodSpecDir, "artificer.yaml");
await mkdir(aipodSpecDir, { recursive: true });
await copyFile(path.join(context.root, "config", "aipods", "artificer.yaml"), artificerSpecFile);
const store = new MemoryAgentRunStore();
const server = await startManagerServer({
port: 0,
host: "127.0.0.1",
sourceCommit: "self-test",
store,
aipodSpecDir,
sessionPvcOptions: {
kubectlHandler: ({ args }) => args[0] === "get"
? { stdout: JSON.stringify({ status: { phase: "Bound", capacity: { storage: "1Gi" } } }), stderr: "", exitCode: 0 }
: { stdout: "{}", stderr: "", exitCode: 0 },
},
runnerJobDefaults: {
namespace: "agentrun-v02",
managerUrl: "http://agentrun-mgr.agentrun-v02.svc.cluster.local:8080",
image: "agentrun-selftest:latest",
serviceAccountName: "agentrun-v02-runner",
jobNamePrefix: "agentrun-v02-runner",
runnerApiKeySecretRef: { name: "agentrun-v02-api-key", key: "HWLAB_API_KEY" },
lane: "v0.2",
},
});
try {
const client = new ManagerClient(server.baseUrl);
const artificerImageRef = await loadArtificerImageRef(context.root);
const parsedWithoutBunGlobal = await runNodeParserCompat(context);
assert.equal(parsedWithoutBunGlobal.name, "Artificer");
assert.equal(parsedWithoutBunGlobal.hasBunGlobal, false);
const listed = await client.get("/api/v1/aipod-specs") as JsonRecord;
assert.equal(listed.action, "aipod-spec-list");
assert.equal((listed.items as JsonRecord[]).some((item) => item.name === "Artificer"), true);
const shown = await client.get("/api/v1/aipod-specs/Artificer") as JsonRecord;
const shownItem = shown.item as JsonRecord;
assert.equal(shownItem.backendProfile, "gpt-pika");
assert.equal(shownItem.providerId, "NC01");
assert.equal(shownItem.lane, "v0.2");
assert.equal(((shownItem.model as JsonRecord).model), "gpt-5.6-sol");
assert.equal(((shownItem.model as JsonRecord).reasoningEffort), "medium");
assert.deepEqual(shownItem.gitIdentity, {
name: "AgentRun Codex",
email: "agentrun-codex@users.noreply.github.com",
source: `${artificerSpecFile}#spec.executionPolicy.gitIdentity`,
valuesPrinted: true,
});
const shownModelDefaults = shownItem.modelDefaults as JsonRecord;
assert.equal(shownModelDefaults.modelSource, "aipod-spec-default");
assert.equal(shownModelDefaults.reasoningEffortSource, "aipod-spec-default");
const shownProviderCredentials = shownItem.providerCredentials as JsonRecord;
const shownProviderCredential = (shownProviderCredentials.items as JsonRecord[]).find((item) => item.profile === "gpt-pika");
assert.equal(shownProviderCredential?.name, "agentrun-v02-provider-gpt-pika");
assert.equal(shownProviderCredential?.namespace, "agentrun-v02");
const shownImageRef = shownItem.imageRef as JsonRecord;
assert.equal(shownImageRef.kind, "env-image-dockerfile");
assert.equal(shownImageRef.repoUrl, "git@github.com:pikasTech/agentrun.git");
assert.equal(shownImageRef.commitId, artificerImageRef.commitId);
assert.equal(shownImageRef.dockerfilePath, "deploy/container/Containerfile");
assert.equal(Object.hasOwn(shownItem.resourceBundleRef as JsonRecord, "gitMirror"), false);
const rendered = await client.post("/api/v1/aipod-specs/Artificer/render", { prompt: "处理 pikasTech/unidesk#245", idempotencyKey: "selftest-aipod-artificer" }) as JsonRecord;
assert.equal(rendered.action, "aipod-spec-render");
const task = rendered.queueTask as JsonRecord;
assert.equal(task.backendProfile, "gpt-pika");
assert.equal(task.providerId, "NC01");
assert.equal(task.lane, "v0.2");
assert.equal(task.idempotencyKey, "selftest-aipod-artificer");
const workspaceRef = task.workspaceRef as JsonRecord;
assert.deepEqual(workspaceRef, { kind: "opaque", path: "." });
const sessionRef = task.sessionRef as JsonRecord;
assert.match(String(sessionRef.sessionId), /^sess_artificer_[a-f0-9]{24}$/u, "Artificer queue task should default to a resumable session");
assert.equal(sessionRef.conversationId, sessionRef.sessionId, "default Artificer conversation should match the generated session");
const taskImageRef = ((task.metadata as JsonRecord).aipodImageRef as JsonRecord);
assert.equal(taskImageRef.kind, "env-image-dockerfile");
assert.equal(taskImageRef.valuesPrinted, false);
const runnerDefaults = ((rendered.dispatchDefaults as JsonRecord).runnerJob as JsonRecord);
assert.equal(((runnerDefaults.imageRef as JsonRecord).kind), "env-image-dockerfile");
assert.equal(((runnerDefaults.imageRef as JsonRecord).dockerfilePath), "deploy/container/Containerfile");
assert.equal(runnerDefaults.namespace, "agentrun-v02");
const taskPayload = task.payload as JsonRecord;
assert.equal(taskPayload.model, "gpt-5.6-sol");
assert.equal(taskPayload.reasoningEffort, "medium");
assert.equal((taskPayload.modelConfig as JsonRecord).modelSource, "aipod-spec-default");
assert.equal((taskPayload.modelConfig as JsonRecord).reasoningEffortSource, "aipod-spec-default");
const modelResolution = rendered.modelResolution as JsonRecord;
assert.equal(modelResolution.model, "gpt-5.6-sol");
assert.equal(modelResolution.reasoningEffort, "medium");
assert.equal(modelResolution.modelSource, "aipod-spec-default");
assert.equal(modelResolution.reasoningEffortSource, "aipod-spec-default");
assert.equal(modelResolution.backendProfile, "gpt-pika");
assert.equal(modelResolution.backendProfileSource, "aipod-spec");
assert.equal(modelResolution.valuesPrinted, false);
const policy = task.executionPolicy as JsonRecord;
assert.equal(policy.timeoutMs, 7_200_000, "Artificer timeout budget must come from the owning AipodSpec YAML");
assert.deepEqual(policy.gitIdentity, { name: "AgentRun Codex", email: "agentrun-codex@users.noreply.github.com" });
const secretScope = policy.secretScope as JsonRecord;
const providerCredentials = secretScope.providerCredentials as JsonRecord[];
const providerCredential = providerCredentials.find((item) => item.profile === "gpt-pika") as JsonRecord | undefined;
assert.equal(((providerCredential?.secretRef as JsonRecord).name), "agentrun-v02-provider-gpt-pika");
assert.equal(((providerCredential?.secretRef as JsonRecord).namespace), "agentrun-v02");
assert.deepEqual(((providerCredential?.secretRef as JsonRecord).keys), ["auth.json", "config.toml"]);
assert.equal(runnerDefaults.namespace, (providerCredential?.secretRef as JsonRecord).namespace, "runner Pod and gpt-pika Secret must share a namespace");
const disclosedProviderCredential = modelResolution.providerCredential as JsonRecord;
assert.equal(disclosedProviderCredential.profile, "gpt-pika");
assert.deepEqual(disclosedProviderCredential.secretRef, { name: "agentrun-v02-provider-gpt-pika", namespace: "agentrun-v02", keys: ["auth.json", "config.toml"], valuesPrinted: false });
const toolCredentials = secretScope.toolCredentials as JsonRecord[];
assert.equal(toolCredentials.some((item) => item.tool === "github" && ((item.projection as JsonRecord).kind) === "env" && ((item.projection as JsonRecord).envName) === "GH_TOKEN" && ((item.secretRef as JsonRecord).name) === "agentrun-v01-tool-github-pr"), true);
assert.equal(toolCredentials.some((item) => item.tool === "unidesk-ssh" && ((item.projection as JsonRecord).envName) === "UNIDESK_SSH_CLIENT_TOKEN"), true);
const githubSshCredential = toolCredentials.find((item) => item.tool === "github" && item.purpose === "github-ssh") as JsonRecord | undefined;
assert.equal(((githubSshCredential?.projection as JsonRecord).kind), "volume");
assert.equal(((githubSshCredential?.projection as JsonRecord).mountPath), "/home/agentrun/.ssh");
assert.deepEqual(((githubSshCredential?.secretRef as JsonRecord).keys), ["id_ed25519", "known_hosts"]);
const bundle = task.resourceBundleRef as JsonRecord;
assert.equal(Object.hasOwn(bundle, "gitMirror"), false);
const bundles = bundle.bundles as JsonRecord[];
const toolBundle = bundles.find((item) => item.name === "agentrun-runner-tools") as JsonRecord | undefined;
assert.equal(toolBundle?.repoUrl, "git@github.com:pikasTech/agentrun.git");
assert.equal(toolBundle?.ref, "v0.2");
assert.equal(toolBundle?.subpath, "tools");
assert.equal(toolBundle?.targetPath, "tools");
assert.equal((bundle.requiredSkills as JsonRecord[]).some((item) => item.name === "dad-dev"), true);
assert.equal((bundle.requiredSkills as JsonRecord[]).some((item) => item.name === "unidesk-gh"), true);
assertNoSecretLeak(rendered);
const modelOverride = await client.post("/api/v1/aipod-specs/Artificer/render", { prompt: "override model", model: "gpt-5.4", reasoningEffort: "high" }) as JsonRecord;
const modelOverrideTask = modelOverride.queueTask as JsonRecord;
const modelOverridePayload = modelOverrideTask.payload as JsonRecord;
const modelOverrideResolution = modelOverride.modelResolution as JsonRecord;
assert.equal(modelOverrideTask.backendProfile, "gpt-pika");
assert.equal(modelOverridePayload.model, "gpt-5.4");
assert.equal(modelOverridePayload.reasoningEffort, "high");
assert.equal(modelOverrideResolution.modelSource, "render-input-override");
assert.equal(modelOverrideResolution.reasoningEffortSource, "render-input-override");
assert.deepEqual((modelOverrideResolution.providerCredential as JsonRecord).secretRef, disclosedProviderCredential.secretRef);
assert.deepEqual(((modelOverrideTask.executionPolicy as JsonRecord).secretScope as JsonRecord).providerCredentials, providerCredentials);
const reasoningOverride = await client.post("/api/v1/aipod-specs/Artificer/render", { prompt: "override reasoning", reasoningEffort: "high" }) as JsonRecord;
const reasoningOverridePayload = (reasoningOverride.queueTask as JsonRecord).payload as JsonRecord;
const reasoningOverrideResolution = reasoningOverride.modelResolution as JsonRecord;
assert.equal(reasoningOverridePayload.model, "gpt-5.6-sol");
assert.equal(reasoningOverridePayload.reasoningEffort, "high");
assert.equal(reasoningOverrideResolution.modelSource, "aipod-spec-default");
assert.equal(reasoningOverrideResolution.reasoningEffortSource, "render-input-override");
await assert.rejects(() => client.post("/api/v1/aipod-specs/Artificer/render", { model: "" }), /safe non-empty model id/u);
await assert.rejects(() => client.post("/api/v1/aipod-specs/Artificer/render", { reasoningEffort: "XHIGH" }), /lowercase reasoning effort id/u);
await assert.rejects(() => client.post("/api/v1/aipod-specs/Artificer/render", { backendProfile: "codex" }), /cannot override backendProfile gpt-pika with codex/u);
await assert.rejects(() => client.post("/api/v1/aipod-specs/Artificer/render", { backendProfile: "gpt.pika" }), /must be a lowercase slug/u);
await assert.rejects(() => client.post("/api/v1/aipod-specs/Artificer/render", { executionPolicy: {} }), /cannot override Aipod control fields: executionPolicy/u);
await assert.rejects(() => client.post("/api/v1/aipod-specs/Artificer/render", { modelConfig: { model: "other" } }), /cannot override Aipod control fields: modelConfig/u);
await assert.rejects(() => client.post("/api/v1/aipod-specs/Artificer/render", { secretRef: { name: "other" } }), /cannot override Aipod control fields: secretRef/u);
await assert.rejects(() => client.post("/api/v1/aipod-specs/Artificer/render", { payload: { backendProfile: "codex" } }), /cannot set Aipod control fields: backendProfile/u);
await assert.rejects(() => client.post("/api/v1/aipod-specs/Artificer/render", { payload: { modelConfig: { model: "other" } } }), /cannot set Aipod control fields: modelConfig/u);
await assert.rejects(() => client.post("/api/v1/aipod-specs/Artificer/render", { payload: { providerCredentials: [{ profile: "codex" }] } }), /cannot set Aipod control fields: providerCredentials/u);
const originalSpecDocument = parseYaml(await readFile(artificerSpecFile, "utf8")) as JsonRecord;
const missingCredentialDocument = structuredClone(originalSpecDocument);
const missingCredentialPolicy = ((missingCredentialDocument.spec as JsonRecord).executionPolicy as JsonRecord);
((missingCredentialPolicy.secretScope as JsonRecord).providerCredentials) = [];
assert.throws(() => validateAipodSpec(missingCredentialDocument, "selftest-missing-gpt-pika-credential"), /requires exactly one matching provider credential/u);
const payloadDefaultsBypassDocument = structuredClone(originalSpecDocument);
((payloadDefaultsBypassDocument.spec as JsonRecord).payloadDefaults as JsonRecord).modelConfig = { model: "bypass" };
assert.throws(() => validateAipodSpec(payloadDefaultsBypassDocument, "selftest-payload-defaults-bypass"), /cannot set Aipod control fields: modelConfig/u);
const invalidGitIdentityDocument = structuredClone(originalSpecDocument);
(((invalidGitIdentityDocument.spec as JsonRecord).executionPolicy as JsonRecord).gitIdentity as JsonRecord).email = "not-an-email";
assert.throws(() => validateAipodSpec(invalidGitIdentityDocument, "selftest-invalid-git-identity"), /valid single-line email address/u);
const mirrored = resolveGitBundleFetchSource("git@github.com:pikasTech/unidesk.git", { baseUrl: "http://mirror.example.test/root/" }, {});
assert.equal(mirrored.fetchRepoUrl, "http://mirror.example.test/root/pikasTech/unidesk.git");
assert.equal(mirrored.mirrorUsed, true);
const defaultMirror = resolveGitBundleFetchSource("https://github.com/pikasTech/unidesk.git", undefined, { AGENTRUN_GIT_MIRROR_BASE_URL: "http://mirror.example.test/base" });
assert.equal(defaultMirror.fetchRepoUrl, "http://mirror.example.test/base/pikasTech/unidesk.git");
assert.equal(defaultMirror.mirrorUsed, true);
const nonGithub = resolveGitBundleFetchSource("ssh://git@example.test/repo.git", { baseUrl: "http://mirror.example.test" }, {});
assert.equal(nonGithub.fetchRepoUrl, "ssh://git@example.test/repo.git");
assert.equal(nonGithub.mirrorUsed, false);
assert.equal(classifyGitStderr("fatal: repository 'http://mirror.example.test/pikasTech/agent_skills.git/' not found\n"), "repository-not-found");
assert.equal(classifyGitStderr("fatal: couldn't find remote ref missing-branch\n"), "source-ref-not-found");
assert.equal(classifyGitStderr("fatal: Authentication failed for 'https://example.test/repo.git/'\n"), "authentication-failed");
assert.throws(() => validateResourceBundleRef({ kind: "gitbundle", repoUrl: "git@github.com:pikasTech/unidesk.git", ref: "master", gitMirror: { enabled: false }, bundles: [{ subpath: ".", targetPath: "." }] }), /resourceBundleRef.gitMirror is removed/u);
assert.throws(() => validateResourceBundleRef({ kind: "gitbundle", repoUrl: "git@github.com:pikasTech/unidesk.git", bundles: [{ subpath: "tools", targetPath: "tools" }] }), /must declare ref or a full commitId/u);
assert.throws(() => validateResourceBundleRef({ kind: "gitbundle", repoUrl: "git@github.com:pikasTech/unidesk.git", ref: "master", bundles: [{ repoUrl: "git@github.com:pikasTech/agentrun.git", subpath: "tools", targetPath: "tools" }] }), /different repoUrl and must declare its own ref or commitId/u);
assert.throws(() => validateResourceBundleRef({ kind: "gitbundle", repoUrl: "git@github.com:pikasTech/unidesk.git", ref: "master", bundles: [{ subpath: "tools", targetPath: "tools" }, { subpath: ".agents/skills", targetPath: "tools" }] }), /target_path conflicts/u);
assert.throws(() => validateResourceBundleRef({ kind: "gitbundle", repoUrl: "git@github.com:pikasTech/unidesk.git", ref: "master", bundles: [{ subpath: ".git", targetPath: ".git" }] }), /cannot replace primary workspace Git metadata/u);
const primaryContractBundle = validateResourceBundleRef({ kind: "gitbundle", repoUrl: "git@github.com:pikasTech/unidesk.git", ref: "master", bundles: [{ subpath: "tools", targetPath: "tools" }] });
assert.ok(primaryContractBundle);
assert.doesNotThrow(() => validatePrimaryWorkspaceContract({ kind: "opaque", path: "." }, primaryContractBundle));
assert.deepEqual(validatePrimaryWorkspaceContract({ kind: "opaque", path: ".", repo: "pikasTech/unidesk" }, primaryContractBundle), { kind: "opaque", path: "." });
assert.deepEqual(validatePrimaryWorkspaceContract({ kind: "opaque", branch: "master" }, primaryContractBundle), { kind: "opaque", path: "." });
const submitPlan = await runCliJson(context, server.baseUrl, ["queue", "submit", "--aipod", "Artificer", "--prompt", "处理 pikasTech/unidesk#245", "--model", "gpt-5.4", "--reasoning-effort", "high", "--idempotency-key", "selftest-aipod-cli", "--dry-run"]);
assert.equal(submitPlan.ok, true);
assert.equal(((submitPlan.data as JsonRecord).action), "queue-submit-plan");
assert.equal((((submitPlan.data as JsonRecord).jsonInput as JsonRecord).source), "aipod-spec");
const request = ((submitPlan.data as JsonRecord).request as JsonRecord);
assert.equal(((request.body as JsonRecord).idempotencyKey), "selftest-aipod-cli");
const submitModelResolution = (submitPlan.data as JsonRecord).modelResolution as JsonRecord;
assert.equal(submitModelResolution.model, "gpt-5.4");
assert.equal(submitModelResolution.reasoningEffort, "high");
assert.match(String(((submitPlan.data as JsonRecord).next as JsonRecord).confirm), /--model gpt-5\.4 --reasoning-effort high/u);
const sendPlan = await runCliJson(context, server.baseUrl, ["sessions", "send", "--aipod", "Artificer", "--prompt", "dry-run session", "--model", "gpt-5.4", "--reasoning-effort", "medium", "--no-runner-job", "--dry-run"]);
assert.equal(sendPlan.ok, true);
const sendPlanData = sendPlan.data as JsonRecord;
assert.equal(sendPlanData.action, "session-send-plan");
assert.equal(sendPlanData.profile, "gpt-pika");
assert.equal((sendPlanData.modelResolution as JsonRecord).model, "gpt-5.4");
assert.equal((sendPlanData.modelResolution as JsonRecord).reasoningEffort, "medium");
assert.equal((((sendPlanData.modelResolution as JsonRecord).providerCredential as JsonRecord).secretRef as JsonRecord).name, "agentrun-v02-provider-gpt-pika");
const cancelledSessionId = "sess_artificer_cancelled_cli_selftest";
const cancelledConversationId = "conv_artificer_cancelled_cli_selftest";
const cancelledThreadId = "thread_artificer_cancelled_cli_selftest";
const cancelledRun = store.createRun(validateCreateRun({
tenantId: "unidesk",
projectId: "pikainc/selfmedia",
providerId: task.providerId,
backendProfile: task.backendProfile,
workspaceRef: task.workspaceRef,
sessionRef: {
sessionId: cancelledSessionId,
conversationId: cancelledConversationId,
threadId: cancelledThreadId,
expiresAt: "2030-01-01T00:00:00.000Z",
metadata: { lastTurnId: "turn_cancelled_cli_selftest", durable: true },
},
executionPolicy: task.executionPolicy,
resourceBundleRef: task.resourceBundleRef,
traceSink: { kind: "queue", taskId: "qt_cancelled_cli_selftest", aipod: "Artificer" },
}));
const cancelledCommand = store.createCommand(cancelledRun.id, validateCreateCommand({ type: "turn", payload: { prompt: "cancelled artificer turn" } }));
store.finishCommand(cancelledCommand.id, { terminalStatus: "cancelled", failureKind: "cancelled", failureMessage: "self-test cancellation" });
store.finishRun(cancelledRun.id, { terminalStatus: "cancelled", failureKind: "cancelled", failureMessage: "self-test cancellation" });
assert.equal(store.getSession(cancelledSessionId)?.lastRunId, cancelledRun.id);
const conflict = await runCliFailureJson(context, server.baseUrl, ["sessions", "send", cancelledSessionId, "--aipod", "Artificer", "--project-id", "pikasTech/unidesk", "--prompt", "must remain fail closed", "--no-runner-job"]);
assert.equal(conflict.ok, false);
assert.match(JSON.stringify(conflict), /tenant-policy-denied/u);
assert.equal(store.getSession(cancelledSessionId)?.lastRunId, cancelledRun.id, "boundary conflict must not mutate the cancelled session");
const continued = await runCliJson(context, server.baseUrl, ["sessions", "send", cancelledSessionId, "--aipod", "Artificer", "--prompt", "continue cancelled selfmedia task", "--no-runner-job"]);
assert.equal(continued.ok, true);
const continuedData = continued.data as JsonRecord;
assert.equal(continuedData.action, "session-send");
assert.equal(continuedData.decision, "turn");
const continuedSession = store.getSession(cancelledSessionId);
assert.notEqual(continuedSession?.lastRunId, cancelledRun.id);
const continuedRun = store.getRun(String(continuedSession?.lastRunId));
assert.equal(continuedRun.tenantId, "unidesk");
assert.equal(continuedRun.projectId, "pikainc/selfmedia");
assert.equal(continuedRun.sessionRef?.sessionId, cancelledSessionId);
assert.equal(continuedRun.sessionRef?.conversationId, cancelledConversationId);
assert.equal(continuedRun.sessionRef?.threadId, cancelledThreadId);
assert.equal(continuedRun.sessionRef?.metadata?.lastTurnId, "turn_cancelled_cli_selftest");
assert.equal((continuedRun.traceSink as JsonRecord).aipod, "Artificer");
const help = await runCliJson(context, server.baseUrl, ["help"]);
const commands = ((help.data as JsonRecord).commands as string[]) ?? [];
assert.equal(commands.some((item) => item.includes("aipod-specs render <name>")), true);
assert.equal(commands.some((item) => item.includes("queue submit --aipod <name>")), true);
assert.equal(commands.some((item) => item.includes("queue submit --aipod <name>") && item.includes("--model <model>") && item.includes("--reasoning-effort <effort>")), true);
assert.equal(commands.some((item) => item.includes("sessions send") && item.includes("--model <model>") && item.includes("--reasoning-effort <effort>")), true);
assertNoSecretLeak(submitPlan);
const durableTask = await client.post("/api/v1/queue/tasks", rendered.queueTask) as JsonRecord;
const changedSpecDocument = structuredClone(originalSpecDocument);
((changedSpecDocument.spec as JsonRecord).model as JsonRecord).model = "gpt-5.7-terra";
((changedSpecDocument.spec as JsonRecord).model as JsonRecord).reasoningEffort = "low";
await writeFile(artificerSpecFile, stringifyYaml(changedSpecDocument), "utf8");
const changedRender = await client.post("/api/v1/aipod-specs/Artificer/render", { prompt: "new yaml default" }) as JsonRecord;
assert.equal(((changedRender.queueTask as JsonRecord).payload as JsonRecord).model, "gpt-5.7-terra");
const storedTask = await client.get(`/api/v1/queue/tasks/${String(durableTask.id)}`) as JsonRecord;
assert.equal((storedTask.payload as JsonRecord).model, "gpt-5.6-sol");
assert.equal((storedTask.payload as JsonRecord).reasoningEffort, "medium");
assert.deepEqual((storedTask.payload as JsonRecord).modelConfig, taskPayload.modelConfig);
return { name: "aipod-spec", tests: ["aipod-spec-yaml-parser-runtime-compatible", "aipod-spec-artificer-image-ref-render", "aipod-spec-artificer-v02-runtime-authority", "aipod-spec-gpt-pika-secret-namespace", "aipod-spec-model-defaults", "aipod-spec-model-reasoning-overrides", "aipod-spec-model-override-keeps-provider", "aipod-spec-control-override-fail-closed", "aipod-spec-payload-control-bypass-fail-closed", "aipod-spec-missing-gpt-pika-credential", "aipod-spec-durable-task-model-snapshot", "aipod-spec-primary-workspace-contract", "aipod-spec-artificer-github-url-render", "aipod-spec-artificer-github-ssh-required-keys", "aipod-spec-git-mirror-url", "git-fetch-stderr-classification", "resource-bundle-source-authority-validation", "resource-bundle-target-conflict-validation", "queue-submit-aipod-dry-run", "session-send-aipod-model-dry-run", "session-send-aipod-cancelled-durable-identity", "session-send-aipod-boundary-conflict-fail-closed", "aipod-cli-help"] };
} finally {
await new Promise<void>((resolve) => server.server.close(() => resolve()));
}
};
export default selfTest;
async function runNodeParserCompat(context: { root: string }): Promise<JsonRecord> {
const script = `import { readFileSync } from "node:fs";
import { parseAipodSpecYaml } from "./src/common/aipod-specs.ts";
const spec = parseAipodSpecYaml(readFileSync("config/aipods/artificer.yaml", "utf8"), "selftest-no-bun-yaml");
console.log(JSON.stringify({ name: spec.metadata.name, hasBunGlobal: typeof globalThis.Bun !== "undefined" }));`;
const proc = spawn("node", ["--import", "tsx", "--eval", script], { cwd: context.root, stdio: ["ignore", "pipe", "pipe"] });
const [stdout, stderr, code] = await Promise.all([readStream(proc.stdout), readStream(proc.stderr), new Promise<number | null>((resolve) => proc.on("close", resolve))]);
assert.equal(code, 0, stderr || stdout);
return JSON.parse(stdout) as JsonRecord;
}
async function runCliJson(context: { root: string }, managerUrl: string, args: string[]): Promise<JsonRecord> {
const proc = spawn(process.execPath, [`${context.root}/scripts/agentrun-cli.ts`, "--manager-url", managerUrl, ...args], { stdio: ["ignore", "pipe", "pipe"] });
const [stdout, stderr, code] = await Promise.all([readStream(proc.stdout), readStream(proc.stderr), new Promise<number | null>((resolve) => proc.on("close", resolve))]);
assert.equal(code, 0, stderr || stdout);
return JSON.parse(stdout) as JsonRecord;
}
async function runCliFailureJson(context: { root: string }, managerUrl: string, args: string[]): Promise<JsonRecord> {
const proc = spawn(process.execPath, [`${context.root}/scripts/agentrun-cli.ts`, "--manager-url", managerUrl, ...args], { stdio: ["ignore", "pipe", "pipe"] });
const [stdout, stderr, code] = await Promise.all([readStream(proc.stdout), readStream(proc.stderr), new Promise<number | null>((resolve) => proc.on("close", resolve))]);
assert.notEqual(code, 0, stderr || stdout);
return JSON.parse(stdout) as JsonRecord;
}
async function readStream(stream: NodeJS.ReadableStream): Promise<string> {
const chunks: Buffer[] = [];
stream.on("data", (chunk: Buffer | string) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
await new Promise<void>((resolve, reject) => {
stream.on("end", resolve);
stream.on("error", reject);
});
return Buffer.concat(chunks).toString("utf8");
}