Add configurable worktree target path and session run diagnostics
This commit is contained in:
@@ -104,3 +104,26 @@ test("resolveOpenAiApiKey prefers CODEX_API_KEY in auto mode", () => {
|
||||
|
||||
assert.equal(resolveOpenAiApiKey(config.provider), "codex-key");
|
||||
});
|
||||
|
||||
test("normalizes anthropic-prefixed CLAUDE_MODEL values", () => {
|
||||
const config = loadConfig({
|
||||
CLAUDE_MODEL: "anthropic/claude-sonnet-4-6",
|
||||
});
|
||||
|
||||
assert.equal(config.provider.claudeModel, "claude-sonnet-4-6");
|
||||
});
|
||||
|
||||
test("normalizes AGENT_WORKTREE_TARGET_PATH", () => {
|
||||
const config = loadConfig({
|
||||
AGENT_WORKTREE_TARGET_PATH: "./src/agents/",
|
||||
});
|
||||
|
||||
assert.equal(config.provisioning.gitWorktree.targetPath, "src/agents");
|
||||
});
|
||||
|
||||
test("validates AGENT_WORKTREE_TARGET_PATH against parent traversal", () => {
|
||||
assert.throws(
|
||||
() => loadConfig({ AGENT_WORKTREE_TARGET_PATH: "../secrets" }),
|
||||
/must not contain "\.\." path segments/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -18,6 +18,7 @@ function parentSnapshot(): DiscoverySnapshot {
|
||||
worktreeRoot: "/repo/.ai_ops/worktrees",
|
||||
worktreePath: "/repo/.ai_ops/worktrees/parent",
|
||||
baseRef: "HEAD",
|
||||
targetPath: "src/agents",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -55,6 +56,7 @@ test("builds deterministic child suballocation requests", () => {
|
||||
const gitRequest = requests.find((entry) => entry.kind === "git-worktree");
|
||||
assert.ok(gitRequest);
|
||||
assert.equal(typeof gitRequest.options?.rootDirectory, "string");
|
||||
assert.equal(gitRequest.options?.targetPath, "src/agents");
|
||||
|
||||
const portRequest = requests.find((entry) => entry.kind === "port-range");
|
||||
assert.ok(portRequest);
|
||||
|
||||
96
tests/run-service.test.ts
Normal file
96
tests/run-service.test.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtemp, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { resolve } from "node:path";
|
||||
import { UiRunService, readRunMetaBySession } from "../src/ui/run-service.js";
|
||||
|
||||
async function waitForTerminalRun(
|
||||
runService: UiRunService,
|
||||
runId: string,
|
||||
): Promise<"success" | "failure" | "cancelled"> {
|
||||
const maxPolls = 100;
|
||||
for (let index = 0; index < maxPolls; index += 1) {
|
||||
const run = runService.getRun(runId);
|
||||
if (run && run.status !== "running") {
|
||||
return run.status;
|
||||
}
|
||||
await new Promise((resolveWait) => setTimeout(resolveWait, 20));
|
||||
}
|
||||
throw new Error("Run did not reach a terminal status within polling window.");
|
||||
}
|
||||
|
||||
test("run service persists failure when pipeline summary is failure", async () => {
|
||||
const workspaceRoot = await mkdtemp(resolve(tmpdir(), "ai-ops-run-service-"));
|
||||
const stateRoot = resolve(workspaceRoot, "state");
|
||||
const projectContextPath = resolve(workspaceRoot, "project-context.json");
|
||||
const envPath = resolve(workspaceRoot, ".env");
|
||||
|
||||
await writeFile(
|
||||
envPath,
|
||||
[
|
||||
`AGENT_STATE_ROOT=${stateRoot}`,
|
||||
`AGENT_PROJECT_CONTEXT_PATH=${projectContextPath}`,
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const runService = new UiRunService({
|
||||
workspaceRoot,
|
||||
envFilePath: ".env",
|
||||
});
|
||||
|
||||
const manifest = {
|
||||
schemaVersion: "1",
|
||||
topologies: ["sequential"],
|
||||
personas: [
|
||||
{
|
||||
id: "writer",
|
||||
displayName: "Writer",
|
||||
systemPromptTemplate: "Write the draft",
|
||||
toolClearance: {
|
||||
allowlist: ["read_file", "write_file"],
|
||||
banlist: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
relationships: [],
|
||||
topologyConstraints: {
|
||||
maxDepth: 1,
|
||||
maxRetries: 0,
|
||||
},
|
||||
pipeline: {
|
||||
entryNodeId: "write-node",
|
||||
nodes: [
|
||||
{
|
||||
id: "write-node",
|
||||
actorId: "writer-actor",
|
||||
personaId: "writer",
|
||||
topology: {
|
||||
kind: "sequential",
|
||||
},
|
||||
constraints: {
|
||||
maxRetries: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
},
|
||||
};
|
||||
|
||||
const started = await runService.startRun({
|
||||
prompt: "force validation failure on first attempt",
|
||||
manifest,
|
||||
executionMode: "mock",
|
||||
simulateValidationNodeIds: ["write-node"],
|
||||
});
|
||||
|
||||
const terminalStatus = await waitForTerminalRun(runService, started.runId);
|
||||
assert.equal(terminalStatus, "failure");
|
||||
|
||||
const persisted = await readRunMetaBySession({
|
||||
stateRoot,
|
||||
sessionId: started.sessionId,
|
||||
});
|
||||
assert.equal(persisted?.status, "failure");
|
||||
});
|
||||
Reference in New Issue
Block a user