feat(backend): queue-time runtime/profile validation; job.runtime authoritative in worker
This commit is contained in:
@@ -38,7 +38,7 @@ export type Claim = {
|
||||
| 'cloudflare_ai_gateway'
|
||||
| 'custom_openai_compatible'
|
||||
| 'opencode_openai_login';
|
||||
authType: 'api_key' | 'opencode_auth_json' | 'none';
|
||||
authType: 'api_key' | 'opencode_auth_json' | 'anthropic_oauth_json' | 'none';
|
||||
secret?: string;
|
||||
baseUrl?: string;
|
||||
model: string;
|
||||
@@ -172,9 +172,9 @@ export const codexModelArgs = (claim: Claim) =>
|
||||
|
||||
// Claude Code OAuth profiles store their credentials as a JSON blob written to
|
||||
// `~/.claude/.credentials.json`; API-key profiles authenticate via
|
||||
// ANTHROPIC_API_KEY. Mirrors the OpenCode adapter's `opencode_auth_json` signal.
|
||||
// ANTHROPIC_API_KEY. Keyed on the Anthropic OAuth snapshot type.
|
||||
export const isClaudeOAuthProfile = (claim: Claim) =>
|
||||
claim.aiProviderProfile?.authType === 'opencode_auth_json';
|
||||
claim.aiProviderProfile?.authType === 'anthropic_oauth_json';
|
||||
|
||||
// The `claude --model` flag expects a bare model id (e.g. `claude-sonnet-4`),
|
||||
// so strip any `provider/` prefix like codexModel does.
|
||||
|
||||
@@ -16,6 +16,7 @@ import { api } from '@spoon/backend/convex/_generated/api.js';
|
||||
import type { NormalizedAgentEvent } from './agent-events';
|
||||
import type { AdapterWorkspace, Claim } from './runtime/provider';
|
||||
import type { BoxHandle } from './user-container';
|
||||
import type { AgentRuntimeName } from './runtime/agent-runtime';
|
||||
import { getAdapter } from './runtime/agent-runtime';
|
||||
import { resolveTurnOutcome } from './runtime/turn-outcome';
|
||||
import './runtime/register';
|
||||
@@ -35,10 +36,7 @@ import {
|
||||
listWorkspaceContainerNames,
|
||||
runExecInContainer,
|
||||
} from './runtime/docker';
|
||||
import {
|
||||
collectJsonStringValues,
|
||||
isCodexLoginProfile,
|
||||
} from './runtime/provider';
|
||||
import { collectJsonStringValues } from './runtime/provider';
|
||||
import { acquireUserBox, runningBoxUsernames } from './user-container';
|
||||
import { fetchUserEnvironment, materializeUserHome } from './user-environment';
|
||||
import { teardownWorkspace } from './workspace-teardown';
|
||||
@@ -876,10 +874,9 @@ const runClaim = async (claim: Claim) => {
|
||||
].filter(Boolean);
|
||||
const redact = createRedactor(secretValues);
|
||||
let acquiredBoxHandle: BoxHandle | undefined;
|
||||
// job.runtime is authoritative (validated against the profile at queue time).
|
||||
const runtime = (claim.job.runtime ?? 'opencode') as AgentRuntimeName;
|
||||
try {
|
||||
if ((claim.job.runtime ?? 'opencode') !== 'opencode') {
|
||||
throw new Error('Legacy OpenAI direct jobs are no longer supported.');
|
||||
}
|
||||
await updateStatus(jobId, 'preparing');
|
||||
await appendEvent(jobId, 'info', 'clone', 'Creating installation token.');
|
||||
if (!claim.github.installationId) {
|
||||
@@ -937,9 +934,7 @@ const runClaim = async (claim: Claim) => {
|
||||
boxHandle,
|
||||
githubToken,
|
||||
redact,
|
||||
// job.runtime becomes authoritative in Task 8; until then resolve from
|
||||
// the profile so dispatch stays byte-identical to today.
|
||||
runtime: isCodexLoginProfile(claim) ? 'codex' : 'opencode',
|
||||
runtime,
|
||||
};
|
||||
if (userEnv) {
|
||||
await appendEvent(
|
||||
@@ -956,15 +951,7 @@ const runClaim = async (claim: Claim) => {
|
||||
redact,
|
||||
});
|
||||
}
|
||||
if (isCodexLoginProfile(claim)) {
|
||||
await getAdapter(workspace.runtime).prepareAuth(workspace);
|
||||
await appendEvent(
|
||||
jobId,
|
||||
'info',
|
||||
'clone',
|
||||
'Prepared Codex auth JSON for the isolated workspace.',
|
||||
);
|
||||
}
|
||||
await getAdapter(runtime).prepareAuth(workspace);
|
||||
await materializeEnvFile(workspace);
|
||||
const detected = await detectPackageCommands(repoDir);
|
||||
await addArtifact({
|
||||
@@ -1617,9 +1604,9 @@ export const startWorker = async () => {
|
||||
await sleep(env.pollMs);
|
||||
continue;
|
||||
}
|
||||
// The Convex claim still carries the legacy `openai_direct` runtime
|
||||
// literal, which `runClaim` rejects. Narrow to the shared `Claim` shape
|
||||
// (widened to the codex/opencode/claude union) at this boundary.
|
||||
// The generated `Doc<'agentJobs'>.runtime` still includes the legacy
|
||||
// `openai_direct` literal (queue-time validation now excludes it), so
|
||||
// narrow to the worker's codex/opencode/claude `Claim` shape here.
|
||||
await runClaim(claim as Claim);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
Reference in New Issue
Block a user