Enforce resolved execution context for deterministic actor policy

This commit is contained in:
2026-02-23 17:51:09 -05:00
parent 94c79d9dd7
commit 4f5ff16b45
9 changed files with 371 additions and 93 deletions

View File

@@ -193,7 +193,10 @@ test("runs DAG pipeline with state-dependent routing and retry behavior", async
},
coder: async (input): Promise<ActorExecutionResult> => {
assert.match(input.prompt, /AIOPS-123/);
assert.deepEqual(input.toolClearance.allowlist, ["read_file", "write_file"]);
assert.deepEqual(input.executionContext.allowedTools, ["read_file", "write_file"]);
assert.equal(input.executionContext.phase, "coder-1");
assert.equal(typeof input.executionContext.modelConstraint, "string");
assert.ok(input.executionContext.modelConstraint.length > 0);
assert.ok(input.security);
coderAttempts += 1;
if (coderAttempts === 1) {
@@ -254,7 +257,7 @@ test("runs DAG pipeline with state-dependent routing and retry behavior", async
assert.deepEqual(engine.planChildPersonas({ parentPersonaId: "task", depth: 1 }), ["coder"]);
});
test("injects mcp registry/config helpers and enforces Claude tool gate in actor executor", async () => {
test("injects resolved mcp/helpers and enforces Claude tool gate in actor executor", async () => {
const workspaceRoot = await mkdtemp(resolve(tmpdir(), "ai-ops-workspace-"));
const stateRoot = await mkdtemp(resolve(tmpdir(), "ai-ops-session-state-"));
const projectContextPath = resolve(stateRoot, "project-context.json");
@@ -302,6 +305,7 @@ test("injects mcp registry/config helpers and enforces Claude tool gate in actor
id: "task",
displayName: "Task",
systemPromptTemplate: "Task executor",
modelConstraint: "claude-3-haiku",
toolClearance: {
allowlist: ["read_file", "write_file"],
banlist: ["rm"],
@@ -340,7 +344,11 @@ test("injects mcp registry/config helpers and enforces Claude tool gate in actor
},
actorExecutors: {
task_actor: async (input) => {
assert.equal(input.mcp.registry, customRegistry);
assert.deepEqual(input.executionContext.allowedTools, ["read_file", "write_file"]);
assert.equal(input.executionContext.phase, "task-node");
assert.equal(input.executionContext.modelConstraint, "claude-3-haiku");
assert.equal(input.executionContext.security.worktreePath, workspaceRoot);
assert.equal(input.executionContext.security.violationMode, "hard_abort");
const codexConfig = input.mcp.resolveConfig({
providerHint: "codex",
@@ -350,7 +358,11 @@ test("injects mcp registry/config helpers and enforces Claude tool gate in actor
];
assert.ok(codexServer);
assert.deepEqual(codexServer.enabled_tools, ["read_file", "write_file"]);
assert.deepEqual(codexServer.disabled_tools, ["rm"]);
assert.deepEqual(input.mcp.allowedTools, ["read_file", "write_file"]);
assert.deepEqual(
input.mcp.filterToolsForProvider(["read_file", "search", "write_file"]),
["read_file", "write_file"],
);
const claudeConfig = input.mcp.resolveConfig({
providerHint: "claude",
@@ -371,25 +383,31 @@ test("injects mcp registry/config helpers and enforces Claude tool gate in actor
toolUseID: "allow-1",
});
const denyBlocked = await canUseTool(
"mcp__claude-task-master__rm",
{},
{
signal: new AbortController().signal,
toolUseID: "deny-1",
},
await assert.rejects(
() =>
canUseTool(
"mcp__claude-task-master__rm",
{},
{
signal: new AbortController().signal,
toolUseID: "deny-1",
},
),
/Tool .* is not present in allowlist/,
);
assert.equal(denyBlocked.behavior, "deny");
const denyMissingAllowlist = await canUseTool(
"mcp__claude-task-master__search",
{},
{
signal: new AbortController().signal,
toolUseID: "deny-2",
},
await assert.rejects(
() =>
canUseTool(
"mcp__claude-task-master__search",
{},
{
signal: new AbortController().signal,
toolUseID: "deny-2",
},
),
/Tool .* is not present in allowlist/,
);
assert.equal(denyMissingAllowlist.behavior, "deny");
return {
status: "success",