test(hermes): add bounded managed Bedrock qualification

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-07 18:57:38 -05:00
1 parent 24fe3414eb
commit bbadcc18a3
13 files changed
+225 -29

No files matched your search

+6
View File
@@ -10,6 +10,12 @@ and Daytona, with independent account/model attribution. Its registered cells
remain pending until their live evidence passes; its API-only scope leaves the
subscription and lifecycle gates separate.
The same guide registers the separate explicit-only `hermes-bedrock-connections`
suite for one exact `us-east-1` inference profile on local and Daytona. Its two
cells remain pending. They verify the explicitly selected personal grant and
public region/protocol/model routing before and after native completion, using
a short-lived AWS bearer credential and the existing budget/evidence pipeline.
Paperclip has two live eval families with different questions, owners, and
evidence. Choose the family before selecting a model, profile, or case.
@@ -709,3 +709,31 @@ because GitHub returned HTTP 429 for the pinned source archive. No native fixtur
or provider execution occurred. This is retained as a download infrastructure
failure; it is not evidence of a closure mismatch. The separate credential-free
AWS build continues against its recorded immutable source.
### Corrected cloud image and Bedrock fixture preparation, 2026-10-07
The corrected credential-free AWS build completed successfully in 11.73 minutes
at source `d389750b90787ea5240d0f8f4e92396a1d9d20a1`. Its immutable image digest is
`sha256:5f457b7aed4dc224c77125edb7b17fd1aedc9e7bad0a9ce92d454cecc660e767`.
The independent collected proof verifies fresh Linux closure
`6166fadd24dae41b9fdfd994e6c7bc129e9317ed252b7dabdd777b691ab47771`
and the original frozen dependency-lock SHA
`f5ee14ee77b1dc7771fe455d619c880fc64b1e62e40a15704addc9f7430e5c50`.
No provider credentials or local Docker were used. This proves the corrected
Linux distribution; it does not prove execution-host namespace compatibility,
browser behavior or Daytona. The ledger reserves $19 of the $25 cap after the
additional $2 cloud-build reservation; actual billing remains incomplete.
The new explicit-only `hermes-bedrock-connections` suite registers two pending
completion cells on local and Daytona. Read-only AWS discovery confirmed the
exact `us.anthropic.claude-haiku-4-5-20251001-v1:0` inference profile is active
in `us-east-1`. The fixture uses an ephemeral region-bound bearer, a public
personal Connections account and its explicitly selected grant. It verifies
the public account's owner, grant, region, protocol, authentication and model
catalog before task creation and after completion, and grades the actual native
run's selected account, grant and exact inference profile. The server inherits
no ambient AWS environment settings. It retains the existing 200-cent company
and agent budget admission and one-attempt policy. All 129 affected fixture and
catalog tests, Product E2E TypeScript, two-cell discovery and diff checks passed.
No Bedrock inference, credential refresh or Daytona execution is claimed from
this fixture preparation. Hermes remains pending qualification.
+22
View File
@@ -246,6 +246,8 @@ Shell variables take precedence over the local file. The recognized names are:
- `KIMI_MODEL_API_KEY` (local-only pending Kimi CLI/ACP profiles)
- `XAI_API_KEY` (local Grok API-key profile)
- `GROK_AUTH_JSON` (local native Grok subscription profile)
- `GEMINI_API_KEY` (Hermes Google API profile)
- `AWS_BEARER_TOKEN_BEDROCK` (explicit Hermes Bedrock profile; use a short-lived token for the declared region)
- `DAYTONA_API_KEY`
- `CURSOR_AUTH_TOKEN` (extended Cursor candidate)
- `COPILOT_GITHUB_TOKEN` (extended Copilot candidate)
@@ -1541,6 +1543,26 @@ protocols, Bedrock, refresh/revocation, images and live controls need their own
qualification. The selected candidate metadata drives pre-credential Hermes
provisioning and image/provider-pack selection for every Hermes profile.
The separate explicit-only `hermes-bedrock-connections` suite declares two
pending one-turn cells, on local and Daytona, for
`us.anthropic.claude-haiku-4-5-20251001-v1:0` in `us-east-1`.
Supply `AWS_BEARER_TOKEN_BEDROCK` ephemerally to the launcher after verifying
the runtime and AWS identity. This suite creates a personal managed connection
with Bedrock routing and selects its returned grant explicitly; routed accounts
do not become responsible-user provider defaults. Public readback before and
after the task must match the region, protocol, authentication, model catalog
and account owner. The native run must match the selected connection, grant,
owner, Hermes harness and exact inference profile. Both 200-cent budget checks,
the single-attempt rule and normal evidence/cleanup contracts also apply.
This scope proves Bedrock completion only; it does not qualify credential
refresh or make a local AWS run equivalent to Daytona. The paid GitHub workflow
does not provision this short-lived credential; use the explicit CLI path.
```sh
pnpm test:e2e:runner -- --list --suite hermes-bedrock-connections
pnpm test:e2e:runner -- --id hermes-bedrock-connections.runner-acpx-hermes-bedrock.local.hello-complete --max-automatic-retries 0
```
The separate Runner Evals `extended-harnesses` campaign lives in the private
`paperclip-evals` repository and grades semantic protocol behavior against the
mock control plane. Neither suite substitutes for the other.
+12
View File
@@ -30,6 +30,18 @@ public structured evidence. Treat changes to the workflow, harness, fixture
prompts, evidence packager, and publisher as security-sensitive production
changes.
## Short-lived Bedrock qualification
The explicit-only `hermes-bedrock-connections` suite accepts a short-lived,
region-bound `AWS_BEARER_TOKEN_BEDROCK` from the launcher. The browser fixture
posts it through the sensitive public Connections API into encrypted storage;
the agent selects that exact personal grant. The isolated server inherits no
ambient `AWS_*` values, including profile, credential-file paths, static keys,
session tokens or container-role endpoints. The selected bearer joins the
attempt redaction set. Do not persist it in catalog data or evidence, copy an
operator's AWS files, create a long-lived key for this fixture, or widen IAM
permissions. The paid GitHub workflow does not supply this credential.
## GitHub authorization
Set `RUNNER_E2E_ALLOWED_ACTOR_IDS` to a non-empty JSON array of numeric GitHub
+3 -3
View File
@@ -146,10 +146,10 @@ describe("runner E2E catalog", () => {
expect(localIntegrityTasks).toHaveLength(2);
expect(openRouterBreadthTasks).toHaveLength(3);
expect(runnerSuites.map((suite) => suite.expectedMatrixSize)).toEqual([
63, 12, 6, 8, 2, 2, 2, 40, 10, 3, 16, 16, 2, 6, 8, 46, 23, 15, 52, 6, 6, 20, 26, 52, 28, 18, 2, 6, 6, 12, 10, 48, 16, 10, 2, 1, 1, 116,
63, 12, 6, 8, 2, 2, 2, 40, 10, 2, 3, 16, 16, 2, 6, 8, 46, 23, 15, 52, 6, 6, 20, 26, 52, 28, 18, 2, 6, 6, 12, 10, 48, 16, 10, 2, 1, 1, 116,
]);
expect(validateRunnerCatalog()).toHaveLength(718);
expect(new Set(runnerMatrix.map((entry) => entry.id)).size).toBe(718);
expect(validateRunnerCatalog()).toHaveLength(720);
expect(new Set(runnerMatrix.map((entry) => entry.id)).size).toBe(720);
expect(
runnerMatrix.filter((entry) => entry.suite.id === "core-compatibility"),
).toHaveLength(48);
+27 -2
View File
@@ -5,7 +5,7 @@ import { nativeCompletionProfile, NATIVE_COMPLETION_BUDGET_CENTS } from "./nativ
import { chatConfirmationTasks } from "./chat-cases.js";
import { buildConnectionSuite } from "./connection-cases.js";
import { hiringTemplateTasks, hiringTemplateProfile, hiringTemplateDefinitionDigest } from "./hiring-template-cases.js";
import { HERMES_API_CONNECTION_BUDGET_CENTS, hermesApiConnectionChoices, hermesApiConnectionDefinitionDigest } from "./hermes-api-connections.js";
import { HERMES_API_CONNECTION_BUDGET_CENTS, hermesApiConnectionChoices, hermesApiConnectionDefinitionDigest, hermesBedrockConnectionChoice } from "./hermes-api-connections.js";
import { nativeActiveStopTasks } from "./native-active-stop-tasks.js";
import { cursorNativeTasks } from "./cursor-native-cases.js";
import { instructionPersistenceTask } from "./instruction-persistence.js";
@@ -180,6 +180,7 @@ function nativeProfile(input: {
supportedEnvironments?: readonly (typeof ENVIRONMENT_IDS)[number][];
modelQualification?: RunnerProfileFixture["modelQualification"];
ranking?: RunnerProfileFixture["ranking"];
managedConnectionRouting?: RunnerProfileFixture["managedConnectionRouting"];
}): RunnerProfileFixture {
return {
...input,
@@ -375,6 +376,15 @@ export const hermesApiConnectionProfiles: readonly RunnerProfileFixture[] = [
})),
];
/** Region-bound Bedrock credentials enter only through a managed connection. */
export const hermesBedrockConnectionProfile = nativeProfile({
id: "runner-acpx-hermes-bedrock", label: "Hermes Bedrock (candidate)",
provider: "acpx", acpxAgent: "hermes", qualificationCandidate: "hermes",
credential: hermesBedrockConnectionChoice.credential, model: hermesBedrockConnectionChoice.model,
managedConnectionRouting: structuredClone(hermesBedrockConnectionChoice.routing),
modelQualification: { source: "candidate_runner_profile", qualificationId: "hermes:v2026.9.24:bedrock:us-east-1:discovery-2026-10-07:pending" },
});
/** Narrow legacy ACP lanes used only by the explicit context-integrity matrix. */
export const legacyAcpxProfiles: readonly RunnerProfileFixture[] = [
legacyProfile({
@@ -1215,6 +1225,20 @@ export const runnerSuites: readonly RunnerSuiteFixture[] = [
coverage: "api-account-native-completion-only", sourceDigest: hermesApiConnectionDefinitionDigest,
},
},
{
id: "hermes-bedrock-connections", label: "Hermes managed Bedrock connection", manualOnly: true,
description: "One native browser task using an explicitly selected personal Bedrock account, exact inference profile and independently verified region.",
groups: ["native"], profiles: [hermesBedrockConnectionProfile], environments: runnerEnvironments,
tasks: [{ ...openRouterBreadthTasks.find(task => task.id === "hello-complete")!, automaticRetryPolicy: "single_attempt" }],
expectedMatrixSize: 2,
definitionMetadata: {
version: 1, qualification: "pending", scheduling: "explicit-only", authenticatedDiscoveryDate: "2026-10-07",
accountMethod: "api_key", accountMode: "delegated", providerTurns: 1, expectedUserSource: "public-account-owner-before-task",
routingSource: "public-selected-account-before-and-after-task", credentialScope: "ephemeral-region-bound-bearer",
budgetMonthlyCents: HERMES_API_CONNECTION_BUDGET_CENTS, maximumAttemptsPerCell: 1,
coverage: "bedrock-account-native-completion-only", sourceDigest: hermesApiConnectionDefinitionDigest,
},
},
{
id: "instruction-persistence", label: "Instruction Persistence",
description: "Agent-owned text and binary files round trip through the editor, survive a server restart and fresh task, and synchronize concurrent edits per file with last-sync-wins.",
@@ -1595,6 +1619,7 @@ export function suiteDefinitionHash(suite: RunnerSuiteFixture) {
id: profile.id,
model: profile.model,
qualification: profile.modelQualification,
...(profile.managedConnectionRouting ? { managedConnectionRouting: profile.managedConnectionRouting } : {}),
})),
environments: suite.environments.map((environment) => ({
id: environment.id,
@@ -1691,7 +1716,7 @@ function assertNoRawSecretValues(value: unknown, label: string) {
export function validateRunnerCatalog(): MatrixExecution[] {
const connectionSuite = runnerSuites.find(suite => suite.id === "provider-connections")!;
const allProfiles = [...connectionSuite.profiles, ...extendedHarnessProfiles, ...hermesApiConnectionProfiles.filter(p => !extendedHarnessProfiles.some(existing => existing.id === p.id)), ...runnerProfiles, ...legacyAcpxProfiles, ...pendingContextIntegrityProfiles, ...openRouterBreadthProfiles, ...everydayProfiles.filter(p => !runnerProfiles.some(existing => existing.id === p.id))];
const allProfiles = [...connectionSuite.profiles, ...extendedHarnessProfiles, hermesBedrockConnectionProfile, ...hermesApiConnectionProfiles.filter(p => !extendedHarnessProfiles.some(existing => existing.id === p.id)), ...runnerProfiles, ...legacyAcpxProfiles, ...pendingContextIntegrityProfiles, ...openRouterBreadthProfiles, ...everydayProfiles.filter(p => !runnerProfiles.some(existing => existing.id === p.id))];
const allTasks = [
...connectionSuite.tasks,
extendedHarnessFileTask,
+2 -2
View File
@@ -25,7 +25,7 @@ const AMBIENT_EXTERNAL_STATE_KEYS = [
"PAPERCLIP_STORAGE_S3_PREFIX",
"PAPERCLIP_STORAGE_S3_FORCE_PATH_STYLE",
] as const;
const PROVIDER_SECRET_KEY = /^(?:OPENAI|ANTHROPIC|OPENROUTER|DAYTONA|XAI|GROK|CURSOR|COPILOT|GITHUB|GH)(?:_|$)/;
const PROVIDER_SECRET_KEY = /^(?:AWS|OPENAI|ANTHROPIC|OPENROUTER|DAYTONA|XAI|GROK|CURSOR|COPILOT|GITHUB|GH)(?:_|$)/;
export function runnerE2EServerControlPaths(temporaryRoot: string) {
const controlDirectory = path.join(temporaryRoot, "control");
@@ -116,7 +116,7 @@ export function buildRunnerE2EProcessEnvironment(
const agent = execution.profile.qualificationCandidate;
if (!agent || agent === "cursor") continue;
const admittedSuite = execution.suite.id === "extended-harnesses"
|| (execution.suite.id === "hermes-api-connections" && agent === "hermes")
|| (["hermes-api-connections", "hermes-bedrock-connections"].includes(execution.suite.id) && agent === "hermes")
|| execution.suite.id === "rich-acp-warm-continuity";
if (!admittedSuite || !execution.suite.manualOnly) {
throw new Error("Candidate qualification requires an explicit provider qualification suite");
+20 -2
View File
@@ -1,25 +1,41 @@
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
import { isDeepStrictEqual } from "node:util";
import type { AiProviderRouting } from "../../packages/shared/src/ai-provider-routing.js";
import type { RunnerProfileFixture } from "./types.js";
const record = (value: unknown): Record<string, unknown> =>
value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
const present = (value: unknown) => typeof value === "string" && value.trim().length > 0;
export const HERMES_API_CONNECTION_BUDGET_CENTS = 200;
export const isHermesConnectionSuite = (suiteId: string) =>
suiteId === "hermes-api-connections" || suiteId === "hermes-bedrock-connections";
export const hermesBedrockConnectionChoice = {
credential: "AWS_BEARER_TOKEN_BEDROCK",
model: "us.anthropic.claude-haiku-4-5-20251001-v1:0",
routing: { kind: "bedrock", protocol: "bedrock", auth: "bearer", region: "us-east-1",
models: [{ id: "us.anthropic.claude-haiku-4-5-20251001-v1:0" }] },
} as const satisfies { credential: RunnerProfileFixture["credential"]; model: string; routing: AiProviderRouting };
/** Establish the intended personal-account user independently, before a paid task. */
export async function captureHermesApiAccountOwner(input: {
api: { get<T>(path: string): Promise<T> }; companyId: string; connectionId: string; provider: string;
expectedRouting?: AiProviderRouting; expectedGrantId?: string;
}) {
const listed = await input.api.get<{ currentUserId: string; connections: {
id: string; companyId: string; provider: string; method: string; ownership: string; ownerUserId?: string; status: string;
routing?: AiProviderRouting; grantId?: string;
}[] }>(`/api/companies/${input.companyId}/ai-connections`);
const matches = listed.connections.filter(connection => connection.id === input.connectionId);
const selected = matches[0];
return { expectedResponsibleUserId: selected?.ownerUserId ?? null, checks: [
return { expectedResponsibleUserId: selected?.ownerUserId ?? null,
...(input.expectedRouting ? { routing: { expected: input.expectedRouting, observed: selected?.routing ?? null } } : {}),
checks: [
{ id: "selected-account-public-readback", passed: matches.length === 1 && selected?.companyId === input.companyId && selected.provider === input.provider && selected.method === "api_key" && selected.ownership === "personal" && selected.status === "connected" },
{ id: "authenticated-caller-known", passed: present(listed.currentUserId) },
{ id: "selected-account-owned-by-caller", passed: present(selected?.ownerUserId) && selected?.ownerUserId === listed.currentUserId },
...(input.expectedRouting ? [{ id: "selected-account-routing", passed: isDeepStrictEqual(selected?.routing, input.expectedRouting) }] : []),
...(input.expectedGrantId !== undefined ? [{ id: "selected-account-grant", passed: present(input.expectedGrantId) && selected?.grantId === input.expectedGrantId }] : []),
] };
}
@@ -40,6 +56,7 @@ export async function captureHermesApiBudgets(input: {
/** Grade public run/account/model metadata; a model's completion claim cannot supply it. */
export function gradeHermesApiConnection(input: {
companyId: string; agentId: string; issueId: string; connectionId: string; provider: string; model: string; expectedResponsibleUserId: string;
accountMode?: "responsible_user" | "delegated"; expectedGrantId?: string;
runs: readonly {
companyId: string; agentId: string; status: string; runtimeMode?: string;
issueId?: string | null; responsibleUserId?: string | null;
@@ -53,7 +70,8 @@ export function gradeHermesApiConnection(input: {
return [
{ id: "one-successful-native-run", passed: input.runs.length === 1 && run?.status === "succeeded" && run.runtimeMode === "native" },
{ id: "company-agent-task-scope", passed: run?.companyId === input.companyId && run.agentId === input.agentId && run.issueId === input.issueId },
{ id: "selected-managed-api-account", passed: account.connectionId === input.connectionId && account.provider === input.provider && account.method === "api_key" && account.mode === "responsible_user" },
{ id: "selected-managed-api-account", passed: account.connectionId === input.connectionId && account.provider === input.provider && account.method === "api_key" && account.mode === (input.accountMode ?? "responsible_user") },
...(input.expectedGrantId !== undefined ? [{ id: "selected-managed-account-grant", passed: present(input.expectedGrantId) && account.grantId === input.expectedGrantId }] : []),
{ id: "responsible-user-attribution", passed: present(input.expectedResponsibleUserId) && run?.responsibleUserId === input.expectedResponsibleUserId && account.responsibleUserId === input.expectedResponsibleUserId },
{ id: "native-hermes-provider", passed: provider.kind === "acpx" && provider.agent === "hermes" && provider.model === input.model },
{ id: "exact-native-model", passed: identity.kind === "acpx" && identity.requestedModel === input.model && identity.effectiveModel === input.model },
@@ -0,0 +1,61 @@
import { describe, expect, it } from "vitest";
import { runnerMatrix, runnerSuites, suiteDefinitionHash } from "./catalog.js";
import { captureHermesApiAccountOwner, gradeHermesApiConnection, hermesBedrockConnectionChoice } from "./hermes-api-connections.js";
import { buildPaperclipServerEnvironment, buildRunnerE2EProcessEnvironment } from "./harness-env.js";
import { parseRunnerSelectors, selectRunnerExecutions } from "./selectors.js";
describe("Hermes managed Bedrock qualification", () => {
const suite = runnerSuites.find(value => value.id === "hermes-bedrock-connections")!;
const cells = runnerMatrix.filter(value => value.suite.id === suite.id);
const routing = structuredClone(hermesBedrockConnectionChoice.routing);
const model = hermesBedrockConnectionChoice.model;
it("registers only two explicit pending single-attempt cells with exact routing", () => {
expect(cells).toHaveLength(2);
expect(new Set(cells.map(value => value.environment.id))).toEqual(new Set(["local", "daytona"]));
expect(suite).toMatchObject({ manualOnly: true, definitionMetadata: { qualification: "pending", accountMode: "delegated", providerTurns: 1, maximumAttemptsPerCell: 1, budgetMonthlyCents: 200 } });
for (const cell of cells) {
expect(cell.profile).toMatchObject({ credential: "AWS_BEARER_TOKEN_BEDROCK", model, managedConnectionRouting: routing, modelQualification: { source: "candidate_runner_profile" } });
expect(cell.requiredCredentials).toContain("AWS_BEARER_TOKEN_BEDROCK");
expect(cell.task).toMatchObject({ id: "hello-complete", expectedRunCount: 1, automaticRetryPolicy: "single_attempt" });
expect(JSON.parse(buildRunnerE2EProcessEnvironment({}, [cell]).PAPERCLIP_RUNNER_ACPX_QUALIFICATION!)).toEqual([{ agent: "hermes", model }]);
}
expect(selectRunnerExecutions(parseRunnerSelectors(["--all"])).some(value => value.suite.id === suite.id)).toBe(false);
expect(() => buildRunnerE2EProcessEnvironment({}, [{ ...cells[0]!, suite: { ...suite, manualOnly: false } }])).toThrow("explicit");
});
it("records routing changes in the historical definition digest", () => {
const changed = { ...suite, profiles: suite.profiles.map(profile => ({ ...profile, managedConnectionRouting: { ...routing, region: "us-west-2" } })) };
expect(suiteDefinitionHash(changed)).not.toBe(suiteDefinitionHash(suite));
});
it("keeps ambient AWS identities and the selected bearer out of the server environment", () => {
const source = { PATH: "/fixture/bin", AWS_PROFILE: "operator", AWS_ACCESS_KEY_ID: "fixture-access", AWS_SECRET_ACCESS_KEY: "fixture-secret", AWS_SESSION_TOKEN: "fixture-session", AWS_BEARER_TOKEN_BEDROCK: "fixture-bearer", AWS_SHARED_CREDENTIALS_FILE: "/operator/credentials", AWS_CONFIG_FILE: "/operator/config", AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: "/operator/role" };
const server = buildPaperclipServerEnvironment(buildRunnerE2EProcessEnvironment(source, [cells[0]!]));
expect(server.PATH).toBe(source.PATH);
for (const key of Object.keys(source).filter(key => key.startsWith("AWS_"))) expect(server[key]).toBeUndefined();
});
it.each(["valid", "region", "protocol", "auth", "model", "missing-routing", "grant", "missing-grant"])("rejects incorrect public routing before a paid task: %s", async fault => {
const observed: Record<string, unknown> = structuredClone(routing);
if (fault === "region") observed.region = "us-west-2";
if (fault === "protocol") observed.protocol = "messages";
if (fault === "auth") observed.auth = "api_key";
if (fault === "model") observed.models = [{ id: "wrong-model" }];
const receipt = await captureHermesApiAccountOwner({ companyId: "company", connectionId: "account", provider: "anthropic", expectedRouting: routing, expectedGrantId: "grant", api: {
async get<T>() { return { currentUserId: "user", connections: [{ id: "account", companyId: "company", provider: "anthropic", method: "api_key", ownership: "personal", ownerUserId: "user", status: "connected", routing: fault === "missing-routing" ? undefined : observed, grantId: fault === "missing-grant" ? undefined : fault === "grant" ? "foreign" : "grant" }] } as T; },
} });
expect(receipt.checks.every(check => check.passed)).toBe(fault === "valid");
expect(receipt.routing?.observed).toEqual(fault === "missing-routing" ? null : observed);
});
it.each(["valid", "grant", "missing-grant", "mode", "owner", "model"])("grades the actual explicitly selected native account and inference profile: %s", fault => {
const account = { connectionId: "account", grantId: "grant", provider: "anthropic", method: "api_key", mode: "delegated", responsibleUserId: "user" };
if (fault === "grant") account.grantId = "foreign";
if (fault === "missing-grant") account.grantId = "";
if (fault === "mode") account.mode = "responsible_user";
if (fault === "owner") account.responsibleUserId = "foreign";
const checks = gradeHermesApiConnection({ companyId: "company", agentId: "agent", issueId: "task", connectionId: "account", expectedGrantId: "grant", provider: "anthropic", model, expectedResponsibleUserId: "user", accountMode: "delegated", runs: [{ companyId: "company", agentId: "agent", issueId: "task", responsibleUserId: "user", status: "succeeded", runtimeMode: "native", contextSnapshot: { aiConnection: account }, runnerProfileJson: { nativeExecutionInput: { provider: { kind: "acpx", agent: "hermes", model } }, sessionCheckpoint: { providerIdentity: { kind: "acpx", requestedModel: model, effectiveModel: fault === "model" ? "wrong-model" : model } } } }] });
expect(checks.every(check => check.passed)).toBe(fault === "valid");
});
});
+8 -4
View File
@@ -97,6 +97,7 @@ describe("live runner fixtures", () => {
["runner-acpx-hermes-api-openai", "hermes-api-connections", "hello-complete"],
["runner-acpx-hermes-api-xai", "hermes-api-connections", "hello-complete"],
["runner-acpx-hermes-api-google", "hermes-api-connections", "hello-complete"],
["runner-acpx-hermes-bedrock", "hermes-bedrock-connections", "hello-complete"],
["runner-codex", "hiring-templates", "hire-coder-template-reuse"],
["runner-acpx-claude", "hiring-templates", "hire-coder-template-reuse"],
["runner-codex", "everyday-workflows", "hire-reuse"],
@@ -116,7 +117,7 @@ describe("live runner fixtures", () => {
)!;
const provider =
profile === "runner-acpx-hermes-api-xai" ? "xai" : profile === "runner-acpx-hermes-api-google" ? "google"
: ["runner-acpx-claude", "runner-acpx-hermes-api-anthropic"].includes(profile) ? "anthropic"
: ["runner-acpx-claude", "runner-acpx-hermes-api-anthropic", "runner-acpx-hermes-bedrock"].includes(profile) ? "anthropic"
: ["runner-opencode", "runner-acpx-hermes"].includes(profile) ? "openrouter" : "openai";
let connected = false;
let companyBody: any;
@@ -141,7 +142,8 @@ describe("live runner fixtures", () => {
allAgents: false,
});
connected = true;
return { connectionId: "managed-account" };
expect(data.routing).toEqual(execution.profile.managedConnectionRouting);
return { connectionId: "managed-account", grantId: "managed-grant" };
}
return { id: "secret" };
},
@@ -159,7 +161,7 @@ describe("live runner fixtures", () => {
},
});
expect(connected).toBe(true);
if (suite === "hermes-api-connections") {
if (["hermes-api-connections", "hermes-bedrock-connections"].includes(suite)) {
expect(companyBody.budgetMonthlyCents).toBe(200);
expect(agentBody.budgetMonthlyCents).toBe(200);
}
@@ -167,7 +169,9 @@ describe("live runner fixtures", () => {
expect(agentBody.runtimeConfig.aiConnection).toEqual({
provider,
method: "api_key",
mode: "responsible_user",
...(execution.profile.managedConnectionRouting
? { mode: "delegated", connectionId: "managed-account", grantId: "managed-grant" }
: { mode: "responsible_user" }),
});
expect((fixtures as any).aiConnection.connectionId).toBe(
"managed-account",
+13 -11
View File
@@ -1,5 +1,6 @@
import { NATIVE_COMPLETION_BUDGET_CENTS } from "./native-completion-defaults.js";
import { HERMES_API_CONNECTION_BUDGET_CENTS } from "./hermes-api-connections.js";
import { HERMES_API_CONNECTION_BUDGET_CENTS, isHermesConnectionSuite } from "./hermes-api-connections.js";
import type { AiConnectionBinding } from "../../packages/shared/src/ai-connections.js";
import path from "node:path";
import { installedReleaseDaytonaPlugin } from "./installed-release.js";
import { isManagedHiringCase } from "./chat-cases.js";
@@ -40,11 +41,7 @@ interface AgentRecord {
}
interface ManagedAccountFixture {
connectionId: string;
binding: {
provider: "openai" | "anthropic" | "openrouter" | "xai" | "google";
method: "api_key";
mode: "responsible_user";
};
binding: Extract<AiConnectionBinding, { mode: "responsible_user" | "delegated" }>;
}
interface ProjectRecord {
@@ -140,7 +137,7 @@ export async function setupLiveFixtures(input: {
return api.post<CompanyRecord>("/api/companies", {
name: `Runner E2E ${execution.id} ${input.executionNonce}`,
description: "Ephemeral paid full-stack runner acceptance fixture",
budgetMonthlyCents: execution.suite.id === "hermes-api-connections" ? HERMES_API_CONNECTION_BUDGET_CENTS
budgetMonthlyCents: isHermesConnectionSuite(execution.suite.id) ? HERMES_API_CONNECTION_BUDGET_CENTS
: ["native-completion", "native-instruction-consolidation", "native-connection-guidance"].includes(execution.suite.id)
|| (execution.suite.id === "everyday-workflows" && ["hire-reuse", "delegate-feedback"].includes(execution.task.id)) ? NATIVE_COMPLETION_BUDGET_CENTS
: execution.suite.id === "task-titles" ? TASK_TITLE_BUDGET_CENTS
@@ -252,7 +249,7 @@ export async function setupLiveFixtures(input: {
async setup(resolved) {
const company = value<CompanyRecord>(resolved, "company");
const key = execution.profile.credential;
const provider = key === "ANTHROPIC_API_KEY" ? "anthropic"
const provider = key === "ANTHROPIC_API_KEY" || key === "AWS_BEARER_TOKEN_BEDROCK" ? "anthropic"
: key === "OPENROUTER_API_KEY" ? "openrouter"
: key === "OPENAI_API_KEY" ? "openai"
: key === "XAI_API_KEY" ? "xai"
@@ -260,21 +257,26 @@ export async function setupLiveFixtures(input: {
if (!provider) throw new Error(`Unsupported managed hiring credential ${key}`);
const apiKey = input.credentials[key];
if (!apiKey) throw new Error(`Missing credential ${key}`);
const account = await api.postSensitive<{ connectionId: string }>(
const routing = execution.profile.managedConnectionRouting;
const account = await api.postSensitive<{ connectionId: string; grantId: string }>(
`/api/companies/${company.id}/ai-connections`,
{
provider,
method: "api_key",
name: `Runner E2E account ${input.executionNonce}`,
ownership: "personal",
...(routing ? { routing } : {}),
apiKey,
agentIds: [],
allAgents: false,
},
);
if (routing && !account.grantId) throw new Error("Routed managed connection did not return its personal grant");
return {
connectionId: account.connectionId,
binding: { provider, method: "api_key", mode: "responsible_user" },
binding: routing
? { provider, method: "api_key", mode: "delegated", connectionId: account.connectionId, grantId: account.grantId }
: { provider, method: "api_key", mode: "responsible_user" },
};
},
});
@@ -304,7 +306,7 @@ export async function setupLiveFixtures(input: {
|| (execution.suite.id === "everyday-workflows" && ["hire-reuse", "delegate-feedback"].includes(execution.task.id))) {
agent.budgetMonthlyCents = 1_000;
}
if (execution.suite.id === "hermes-api-connections") agent.budgetMonthlyCents = HERMES_API_CONNECTION_BUDGET_CENTS;
if (isHermesConnectionSuite(execution.suite.id)) agent.budgetMonthlyCents = HERMES_API_CONNECTION_BUDGET_CENTS;
if (managedHiring) {
const account = value<ManagedAccountFixture>(resolved, "ai-connection");
const config = agent.adapterConfig as Record<string, unknown>;
+18 -5
View File
@@ -1,5 +1,5 @@
import { runPlanTaskFlow } from "./plan-task-flow.js";
import { captureHermesApiAccountOwner, captureHermesApiBudgets, gradeHermesApiConnection } from "./hermes-api-connections.js";
import { captureHermesApiAccountOwner, captureHermesApiBudgets, gradeHermesApiConnection, isHermesConnectionSuite } from "./hermes-api-connections.js";
import { assertNativeCompletionSelection, NATIVE_COMPLETION_PREFLIGHT_ENV, verifyNativeCompletionPreflight } from "./native-completion-admission.js";
import { assertNativeInstructionSelection, verifyNativeInstructionPreflight, NATIVE_INSTRUCTION_PREFLIGHT_ENV, NATIVE_INSTRUCTION_SUITE, NATIVE_INSTRUCTION_DEFAULT_SHA256 } from "./native-instruction-consolidation.js";
import { captureNativeDefault, gradeNativeDefault, nativeCompletionWorkspaceDigest } from "./native-completion-defaults.js";
@@ -895,10 +895,12 @@ for (const execution of executions) {
daytonaImage: process.env.PAPERCLIP_E2E_DAYTONA_IMAGE,
});
if (execution.suite.id === "hermes-api-connections") {
if (isHermesConnectionSuite(execution.suite.id)) {
if (!fixtures.aiConnection) throw new Error("Hermes API account is missing before task creation");
hermesApiAccountOwner = await captureHermesApiAccountOwner({ api, companyId: fixtures.company.id,
connectionId: fixtures.aiConnection.connectionId, provider: fixtures.aiConnection.binding.provider });
connectionId: fixtures.aiConnection.connectionId, provider: fixtures.aiConnection.binding.provider,
...(execution.profile.managedConnectionRouting ? { expectedRouting: execution.profile.managedConnectionRouting,
expectedGrantId: fixtures.aiConnection.binding.mode === "delegated" ? fixtures.aiConnection.binding.grantId : "" } : {}) });
await writeSanitizedJson(snapshotsDir, "hermes-api-owner-before-execution.json", hermesApiAccountOwner, secrets);
if (!hermesApiAccountOwner.checks.every(check => check.passed)) throw new Error("Hermes API owner failed admission before task creation");
const receipt = await captureHermesApiBudgets({ api, companyId: fixtures.company.id, agentId: fixtures.agent.id });
@@ -2877,11 +2879,22 @@ for (const execution of executions) {
}
}
}
if (execution.suite.id === "hermes-api-connections") {
if (isHermesConnectionSuite(execution.suite.id)) {
if (!fixtures?.aiConnection || !issue?.id || !hermesApiAccountOwner?.expectedResponsibleUserId) throw new Error("Hermes connection qualification is missing its selected account, expected user or task");
const checks = gradeHermesApiConnection({ companyId: fixtures.company.id, agentId: fixtures.agent.id,
issueId: issue.id, connectionId: fixtures.aiConnection.connectionId, provider: fixtures.aiConnection.binding.provider,
expectedResponsibleUserId: hermesApiAccountOwner.expectedResponsibleUserId, model: execution.profile.model, runs: selectedRuns });
expectedResponsibleUserId: hermesApiAccountOwner.expectedResponsibleUserId, model: execution.profile.model, runs: selectedRuns,
accountMode: fixtures.aiConnection.binding.mode,
...(execution.profile.managedConnectionRouting ? { expectedGrantId: fixtures.aiConnection.binding.mode === "delegated" ? fixtures.aiConnection.binding.grantId : "" } : {}) });
if (execution.profile.managedConnectionRouting) {
const after = await captureHermesApiAccountOwner({ api, companyId: fixtures.company.id,
connectionId: fixtures.aiConnection.connectionId, provider: fixtures.aiConnection.binding.provider,
expectedRouting: execution.profile.managedConnectionRouting,
expectedGrantId: fixtures.aiConnection.binding.mode === "delegated" ? fixtures.aiConnection.binding.grantId : "" });
checks.push(...after.checks.map(check => ({ ...check, id: `after-task-${check.id}` })),
{ id: "account-owner-preserved", passed: after.expectedResponsibleUserId === hermesApiAccountOwner.expectedResponsibleUserId });
await writeSanitizedJson(snapshotsDir, "hermes-api-owner-after-execution.json", after, secrets);
}
matcherResults.push(...checks.map(check => ({ matcher: { kind: "json_path" as const, path: `hermesConnection.${check.id}`, expected: true }, passed: check.passed, detail: "Public native run metadata must match the selected managed API account and exact model." })));
await writeSanitizedJson(snapshotsDir, "hermes-api-connection.json", { checks }, secrets);
expect(checks.every(check => check.passed), "Hermes managed account and native model attribution").toBe(true);
+5
View File
@@ -1,3 +1,5 @@
import type { AiProviderRouting } from "../../packages/shared/src/ai-provider-routing.js";
export const CREDENTIAL_NAMES = [
"OPENAI_API_KEY",
"ANTHROPIC_API_KEY",
@@ -6,6 +8,7 @@ export const CREDENTIAL_NAMES = [
"XAI_API_KEY",
"GROK_AUTH_JSON",
"GEMINI_API_KEY",
"AWS_BEARER_TOKEN_BEDROCK",
"DAYTONA_API_KEY",
"CURSOR_AUTH_TOKEN",
"COPILOT_GITHUB_TOKEN",
@@ -87,6 +90,8 @@ export interface RunnerProfileFixture {
sourceUrl: string;
};
credential: Exclude<CredentialName, "DAYTONA_API_KEY">;
/** Value-free routing saved on an explicitly selected managed connection. */
managedConnectionRouting?: AiProviderRouting;
supportedEnvironments: readonly RunnerEnvironmentId[];
expectedRuntimeMode: RunnerGeneration;
expectedRuntimeMetadata: {