Q38: the prompt names skill tools the run cannot call, not a fake allowlist

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-08 07:14:41 -04:00
1 parent e726c36bac
commit f7530ba65b
4 files changed
+21 -5

No files matched your search

+6 -1
View File
@@ -41,6 +41,11 @@ export interface AgentRunLoopDeps {
onToolResults?: () => void;
}
/** Every tool the model can call in a run. */
export function runTools(externalTools: ToolDefinition[]): ToolDefinition[] {
return [...AGENT_TOOLS, ...WORKSPACE_TOOLS, ...WEB_TOOLS, ...externalTools];
}
/**
* Drives a real tool-use loop against the live provider: prompt the model with
* the agent tools, execute every tool call it makes, feed results back, and
@@ -48,7 +53,7 @@ export interface AgentRunLoopDeps {
* iteration cap is hit (which throws rather than silently truncating).
*/
export async function runAgentLoop(deps: AgentRunLoopDeps, input: AgentRunLoopInput): Promise<AgentRunLoopResult> {
const allTools = [...AGENT_TOOLS, ...WORKSPACE_TOOLS, ...WEB_TOOLS, ...(input.externalTools ?? [])];
const allTools = runTools(input.externalTools ?? []);
const messages: AgentTurnMessage[] = [{ role: "user", text: input.initialUserTurn }];
let toolCallCount = 0;
let lastRequestId = "";
+2 -1
View File
@@ -9,7 +9,7 @@ import { OrganizationService } from "./organization-service";
import { ChatService } from "./chat-service";
import { deliverChatReplyIfMissing } from "./chat-agent-wake-service";
import { SkillService } from "./skill-service";
import { runAgentLoop } from "./agent-run-loop";
import { runAgentLoop, runTools } from "./agent-run-loop";
import type { AgentToolContext } from "./agent-tools";
import {
buildAgentInstructions,
@@ -197,6 +197,7 @@ export class TaskExecutionService {
handoffLines: renderTaskHandoff(this.db, orgId, task.handoff_json),
knowledgeLines: relevantKnowledgeLines(this.db, { orgId, agentId: agent.id, projectId: task.project_id, goalId: task.goal_id, taskId: task.id }, `${task.title}\n${task.description}`),
priorCompletedRuns: this.completedRunCount(task.id, runId),
availableToolNames: runTools(externalTools).map((tool) => tool.name),
}),
toolContext,
externalTools,
+11 -2
View File
@@ -160,6 +160,14 @@ function roleVoice(input: AgentInstructionContext): string {
return "You own your craft. Ask the question early instead of guessing, do the work properly, and when you are stuck for more than a few minutes say so in the thread instead of spinning in place.";
}
function skillToolLines(toolsManifest: string, available: string[]): string[] {
const { requiredTools } = JSON.parse(toolsManifest) as { requiredTools: string[] };
const missing = requiredTools.filter((tool) => !available.includes(tool));
if (missing.length === 0) return [""];
console.warn(`[skills] run lacks tools its skills name: ${missing.join(", ")}`);
return ["", `Your skills name tools you do not have in this run: ${missing.join(", ")}. Work without them, and say so if that blocks the task.`];
}
export function buildTaskPrompt(input: {
task: TaskExecutionTaskRow;
agent: TaskExecutionAgentRow;
@@ -177,6 +185,8 @@ export function buildTaskPrompt(input: {
knowledgeLines: string[];
/** Completed runs this task already spent without reaching done. */
priorCompletedRuns: number;
/** Names of the tools the model can call this run. */
availableToolNames: string[];
}): string {
const lines = [
"Execute this AgentCompany task.",
@@ -214,8 +224,7 @@ export function buildTaskPrompt(input: {
"",
"Skills you carry this run — these are mandatory working practice, not suggestions. Apply each one to how you work and what you produce:",
input.skillRuntimeSnapshot.compiled_prompt,
"",
`Tool allowlist: ${input.skillRuntimeSnapshot.tools_manifest}`,
...skillToolLines(input.skillRuntimeSnapshot.tools_manifest, input.availableToolNames),
`Skill revisions: ${input.skillRuntimeSnapshot.source_revision_ids}`,
);
}
+2 -1
View File
@@ -135,7 +135,8 @@ describe("task execution service", () => {
expect(capturedPrompt).toContain("Execution context:");
expect(capturedPrompt).toContain("Secret scope:");
expect(capturedPrompt).toContain("Skills you carry this run");
expect(capturedPrompt).toContain("Tool allowlist:");
expect(capturedPrompt).not.toContain("Tool allowlist");
expect(capturedPrompt).toContain("Your skills name tools you do not have in this run: file_search. Work without them, and say so if that blocks the task.");
expect(state.auditEvents.some((event) => event.entityType === "task_run" && event.action === "completed")).toBe(true);
rmSync(skillDir, { recursive: true, force: true });
connection.close();