Q83: a model passes an agent check before it does agent work

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-08 14:19:48 -04:00
1 parent 4e232b241f
commit 665bf3cae1
35 files changed
+350 -47

No files matched your search

+58
View File
@@ -0,0 +1,58 @@
import { randomInt } from "node:crypto";
import type { DatabaseSync } from "node:sqlite";
import type { AgentTurnMessage, AgentTurnResult, ToolDefinition } from "./provider-tool-turn";
export const MODEL_CHECK_TOOL = "confirm_ready";
export interface ModelCheckTurn {
code: string;
instructions: string;
messages: AgentTurnMessage[];
tools: ToolDefinition[];
}
/** One turn that needs what agent work needs: system instructions, a tool, and arguments taken from the conversation. */
export function modelCheckTurn(): ModelCheckTurn {
const code = String(randomInt(100_000, 1_000_000));
return {
code,
instructions: `You are being checked for agent work. Answer only by calling ${MODEL_CHECK_TOOL} with the code from the user's message; never answer in text.`,
messages: [{ role: "user", text: `The code is ${code}.` }],
tools: [{
name: MODEL_CHECK_TOOL,
description: "Confirms the agent is ready by echoing the code.",
parameters: { type: "object", properties: { code: { type: "string", description: "The code from the user's message." } }, required: ["code"] },
}],
};
}
/** Why the answer shows the model cannot do agent work on this interface, or null when it passed. */
export function modelCheckProblem(result: Pick<AgentTurnResult, "toolCalls">, code: string): string | null {
const call = result.toolCalls.find((candidate) => candidate.name === MODEL_CHECK_TOOL);
if (!call) return result.toolCalls.length > 0 ? `it called ${result.toolCalls[0].name}, a tool it was not given` : "it answered in text instead of calling the tool it was given";
if (call.argumentsError) return `its tool arguments were ${call.argumentsError}`;
if (String(call.arguments.code) !== code) return "it called the tool without the code its instructions asked for";
return null;
}
export type ModelCheck = { passed: true } | { passed: false; detail: string };
export function readModelCheck(db: DatabaseSync, providerId: string, model: string, credentialId: string): ModelCheck | null {
const row = db.prepare("select passed, detail from model_checks where provider_id = ? and model = ? and credential_id = ?")
.get(providerId, model, credentialId) as { passed: number; detail: string | null } | undefined;
if (!row) return null;
if (row.passed === 1) return { passed: true };
if (!row.detail) throw new Error(`The failed agent check of ${model} has no reason recorded.`);
return { passed: false, detail: row.detail };
}
export function recordModelCheck(db: DatabaseSync, input: { orgId: string; providerId: string; model: string; credentialId: string; problem: string | null; now: Date }): void {
db.prepare(`
insert into model_checks(provider_id, model, credential_id, org_id, passed, detail, checked_at) values (?, ?, ?, ?, ?, ?, ?)
on conflict(provider_id, model, credential_id) do update set passed = excluded.passed, detail = excluded.detail, checked_at = excluded.checked_at
`).run(input.providerId, input.model, input.credentialId, input.orgId, input.problem ? 0 : 1, input.problem, input.now.toISOString());
}
export function modelCheckFailure(providerLabel: string, model: string, detail: string): string {
return `${model} on ${providerLabel} failed the agent check: ${detail}. Pick another model, or check it again in Settings.`;
}
+62 -12
View File
@@ -18,6 +18,8 @@ import { resolveTurnCredential } from "./provider-tool-turns/credential-resolver
import { currentRunId } from "./run-control";
import { budgetBlock, isPriced, promptChars, providerPrices } from "./spend";
import { assertModelSelectable, effectiveModel } from "./task-run-preparation";
import { modelCheckFailure, modelCheckProblem, modelCheckTurn, readModelCheck, recordModelCheck } from "./model-check";
import type { ModelCheckResult } from "../../shared/desktop-api";
import {
HEALTH_STATE_HEALTHY,
WORKSPACE_SCOPES,
@@ -113,7 +115,6 @@ export class ProviderResponseRunner {
}): Promise<AgentTurnResult & { requestId: string }> {
const provider = loadConfiguredProvider(this.deps.db, input.orgId, input.providerId);
const workspaceId = this.normalizeWorkspaceId(input.orgId, input.workspaceId ?? null);
this.enforcePolicy(input.orgId, provider, PROVIDER_EXECUTION_MODE.FOREGROUND, workspaceId);
if (!supportsToolTurn(provider.provider_key)) {
throw new Error("Agent execution is not available for this provider; assign the agent an OpenAI, Anthropic, xAI, Groq, Mistral, Cerebras, OpenRouter, GPT account, GitHub Copilot, Google Antigravity, or Claude Pro provider.");
}
@@ -134,32 +135,67 @@ export class ProviderResponseRunner {
if (!model) throw new Error("Provider model is required.");
const owner: ProviderRow & { model: string } = { ...provider, model };
const turn = this.deps.toolTurn ?? selectToolTurn(provider.provider_key);
const blocked = agent ? budgetBlock(this.deps.db, agent.id, isPriced(providerPrices(this.deps.db, provider.id)), new Date()) : null;
const caller = { orgId: input.orgId, owner, workspaceId, agent, taskRunId, turn };
const known = readModelCheck(this.deps.db, provider.id, model, requireCredentialId(provider));
const problem = known ? (known.passed ? null : known.detail) : await this.probeModel(caller);
if (problem) throw new Error(modelCheckFailure(provider.provider_key, model, problem));
return this.callTurn(caller, { instructions: input.instructions, messages: input.messages, tools: input.tools });
}
/** Probes a model for agent work and remembers the verdict; a request that fails records nothing. */
private async probeModel(caller: TurnCaller): Promise<string | null> {
const probe = modelCheckTurn();
const problem = modelCheckProblem(await this.callTurn(caller, probe), probe.code);
const { owner } = caller;
recordModelCheck(this.deps.db, { orgId: caller.orgId, providerId: owner.id, model: owner.model, credentialId: requireCredentialId(owner), problem, now: new Date() });
console.info(`[model-check] ${owner.provider_key} ${owner.model}: ${problem ?? "passed"}`);
return problem;
}
/** Runs the agent check of a provider's model now, replacing any earlier result. */
async checkModel(input: { orgId: string; providerId: string; model: string }): Promise<ModelCheckResult> {
const provider = loadConfiguredProvider(this.deps.db, input.orgId, input.providerId);
if (!supportsToolTurn(provider.provider_key)) throw new Error("Agent execution is not available for this provider.");
const model = input.model.trim();
if (!model) throw new Error("A model is required.");
assertModelSelectable(provider.provider_key, model, provider.model);
const owner = { ...provider, model };
const turn = this.deps.toolTurn ?? selectToolTurn(provider.provider_key);
const problem = await this.probeModel({ orgId: input.orgId, owner, workspaceId: null, agent: null, taskRunId: null, turn });
return problem === null ? { passed: true } : { passed: false, detail: problem };
}
private async callTurn(caller: TurnCaller, request: { instructions: string | null; messages: AgentTurnMessage[]; tools: ToolDefinition[] }): Promise<AgentTurnResult & { requestId: string }> {
const { orgId, owner, workspaceId, agent, taskRunId, turn } = caller;
const model = owner.model;
// Every request, a probe or the work, passes the provider's policy and quota and the agent's budget.
this.enforcePolicy(orgId, owner, PROVIDER_EXECUTION_MODE.FOREGROUND, workspaceId);
const blocked = agent ? budgetBlock(this.deps.db, agent.id, isPriced(providerPrices(this.deps.db, owner.id)), new Date()) : null;
if (blocked) throw new Error(blocked);
// Everything the model reads: instructions, the conversation, and the tool definitions.
const turnInputChars = (input.instructions?.length ?? 0) + JSON.stringify(input.messages).length + JSON.stringify(input.tools).length;
const turnInputChars = (request.instructions?.length ?? 0) + JSON.stringify(request.messages).length + JSON.stringify(request.tools).length;
const requestId = this.deps.requestStore.create({
agentId: agent?.id ?? null,
taskRunId,
department: agent?.department ?? null,
orgId: input.orgId,
orgId,
provider: owner,
model,
workspaceId,
promptSha256: hashPrompt(JSON.stringify(input.messages)),
promptSha256: hashPrompt(JSON.stringify(request.messages)),
inputCharCount: turnInputChars,
executionMode: PROVIDER_EXECUTION_MODE.FOREGROUND,
createdAt: new Date().toISOString(),
});
const startedAt = Date.now();
try {
const credential = await resolveTurnCredential({ authBroker: this.deps.authBroker, integrationKeys: this.deps.integrationKeys, orgId: input.orgId, provider });
const result = await turn({ credential, model, instructions: input.instructions, messages: input.messages, tools: input.tools });
recordObservedRateLimit(this.deps.rateLimitRegistry, input.orgId, owner, model, result.rateLimitState ?? null);
const credential = await resolveTurnCredential({ authBroker: this.deps.authBroker, integrationKeys: this.deps.integrationKeys, orgId, provider: owner });
const result = await turn({ credential, model, instructions: request.instructions, messages: request.messages, tools: request.tools });
recordObservedRateLimit(this.deps.rateLimitRegistry, orgId, owner, model, result.rateLimitState ?? null);
// Tool calls are output the model wrote, as much as its text.
const outputChars = result.text.length + (result.toolCalls.length > 0 ? JSON.stringify(result.toolCalls).length : 0);
this.deps.requestStore.markSucceeded({
orgId: input.orgId,
orgId,
requestId,
responseText: result.text,
rawResponseJson: result.rawResponseJson,
@@ -168,7 +204,7 @@ export class ProviderResponseRunner {
completedAt: new Date().toISOString(),
});
this.deps.responseObserver.recordSuccess({
orgId: input.orgId,
orgId,
provider: owner,
requestId,
executionMode: PROVIDER_EXECUTION_MODE.FOREGROUND,
@@ -176,13 +212,13 @@ export class ProviderResponseRunner {
action: "response_created",
});
this.deps.requestStore.settleUsage({
orgId: input.orgId, requestId, provider: owner, rawResponseJson: result.rawResponseJson,
orgId, requestId, provider: owner, rawResponseJson: result.rawResponseJson,
inputCharCount: turnInputChars, outputCharCount: outputChars,
});
return { ...result, requestId };
} catch (error) {
this.deps.responseObserver.recordFailure({
orgId: input.orgId,
orgId,
provider: owner,
requestId,
executionMode: PROVIDER_EXECUTION_MODE.FOREGROUND,
@@ -391,3 +427,17 @@ interface PreparedProviderExecution {
adapter: ProviderAdapterRegistry[string];
credential: { secret: string };
}
interface TurnCaller {
orgId: string;
owner: ProviderRow & { model: string };
workspaceId: string | null;
agent: { id: string; department: string } | null;
taskRunId: string | null;
turn: ToolTurnFn;
}
function requireCredentialId(provider: ProviderRow): string {
if (!provider.credential_id) throw new Error("Provider credential is required.");
return provider.credential_id;
}
+5
View File
@@ -18,6 +18,7 @@ import { ProviderResponseRunner, type ProviderResponseCreateInput } from "./prov
import { launchProviderSignInWindow, type LaunchProviderSignInWindowInput } from "./provider-sign-in-window";
import type { ToolTurnFn } from "./provider-tool-turn";
import { stopRun } from "./run-control";
import type { ModelCheckResult } from "../../shared/desktop-api";
import {
AUTH_STATE_CONFIGURED,
AUTH_STATE_REVOKED,
@@ -194,6 +195,10 @@ export class ProviderService {
return adapter.listModels({ authProfile, model: provider.model });
}
checkModel(input: { orgId: string; providerId: string; model: string }): Promise<ModelCheckResult> {
return this.runner.checkModel(input);
}
async checkHealth(orgId: string, providerId: string): Promise<void> {
const provider = loadConfiguredProvider(this.db, orgId, providerId);
const adapter = requireAdapter(this.adapters, provider.provider_key, provider.auth_type);