fix: 修复 runner Codex shell 工具环境
This commit is contained in:
+12
-3
@@ -248,7 +248,7 @@ async function sessionTurn(args: ParsedArgs, positionalSessionId: string | null)
|
||||
copyOptionalFlag(args, runnerBody, "attempt-id", "attemptId");
|
||||
copyOptionalFlag(args, runnerBody, "runner-id", "runnerId");
|
||||
copyOptionalFlag(args, runnerBody, "source-commit", "sourceCommit");
|
||||
copyOptionalFlag(args, runnerBody, "runner-manager-url", "managerUrl");
|
||||
copyRunnerManagerUrlFlag(args, runnerBody);
|
||||
copyOptionalFlag(args, runnerBody, "service-account-name", "serviceAccountName");
|
||||
const runnerIdempotencyKey = optionalFlag(args, "runner-idempotency-key");
|
||||
if (runnerIdempotencyKey) runnerBody.idempotencyKey = runnerIdempotencyKey;
|
||||
@@ -314,7 +314,7 @@ async function dispatchQueueTask(args: ParsedArgs, taskId: string): Promise<Json
|
||||
copy("attempt-id", "attemptId");
|
||||
copy("runner-id", "runnerId");
|
||||
copy("source-commit", "sourceCommit");
|
||||
copy("runner-manager-url", "managerUrl");
|
||||
copyRunnerManagerUrlFlag(args, body);
|
||||
copy("service-account-name", "serviceAccountName");
|
||||
return client(args).post(`/api/v1/queue/tasks/${encodeURIComponent(taskId)}/dispatch`, body);
|
||||
}
|
||||
@@ -346,7 +346,7 @@ async function renderRunnerJob(args: ParsedArgs): Promise<JsonRecord> {
|
||||
if (attemptId) body.attemptId = attemptId;
|
||||
if (runnerId) body.runnerId = runnerId;
|
||||
if (sourceCommit) body.sourceCommit = sourceCommit;
|
||||
if (runnerManagerUrl) body.managerUrl = runnerManagerUrl;
|
||||
if (runnerManagerUrl) body.managerUrl = resolveRunnerManagerUrlFlag(args, runnerManagerUrl);
|
||||
if (idempotencyKey) body.idempotencyKey = idempotencyKey;
|
||||
return await client(args).post(`/api/v1/runs/${encodeURIComponent(runId)}/runner-jobs`, body) as JsonRecord;
|
||||
}
|
||||
@@ -754,6 +754,15 @@ function copyOptionalFlag(args: ParsedArgs, target: JsonRecord, flagName: string
|
||||
if (value) target[key] = value;
|
||||
}
|
||||
|
||||
function copyRunnerManagerUrlFlag(args: ParsedArgs, target: JsonRecord): void {
|
||||
const value = optionalFlag(args, "runner-manager-url");
|
||||
if (value) target.managerUrl = resolveRunnerManagerUrlFlag(args, value);
|
||||
}
|
||||
|
||||
function resolveRunnerManagerUrlFlag(args: ParsedArgs, value: string): string {
|
||||
return value === "auto" ? managerUrl(args) : value;
|
||||
}
|
||||
|
||||
function readerQuery(args: ParsedArgs): string {
|
||||
const readerId = optionalFlag(args, "reader-id");
|
||||
return readerId ? `?readerId=${encodeURIComponent(readerId)}` : "";
|
||||
|
||||
@@ -46,12 +46,15 @@ export function createBackendSession(run: RunRecord, options: BackendAdapterOpti
|
||||
|
||||
export function backendTurnOptions(run: RunRecord, command: CommandRecord, options: BackendAdapterOptions = {}): CodexStdioTurnOptions {
|
||||
const prompt = typeof command.payload.prompt === "string" ? command.payload.prompt : JSON.stringify(command.payload);
|
||||
const sandboxOverride = codexShellSandboxOverride(options.env ?? process.env);
|
||||
const turnOptions: CodexStdioTurnOptions = {
|
||||
backendProfile: run.backendProfile,
|
||||
prompt,
|
||||
cwd: options.workspacePath ?? (typeof run.workspaceRef.path === "string" ? run.workspaceRef.path : process.cwd()),
|
||||
approvalPolicy: run.executionPolicy.approval,
|
||||
sandbox: run.executionPolicy.sandbox,
|
||||
sandbox: sandboxOverride ?? run.executionPolicy.sandbox,
|
||||
requestedSandbox: run.executionPolicy.sandbox,
|
||||
sandboxOverrideSource: sandboxOverride ? "AGENTRUN_CODEX_SHELL_SANDBOX" : null,
|
||||
timeoutMs: run.executionPolicy.timeoutMs,
|
||||
};
|
||||
if (typeof command.payload.model === "string") turnOptions.model = command.payload.model;
|
||||
@@ -67,3 +70,8 @@ export function backendTurnOptions(run: RunRecord, command: CommandRecord, optio
|
||||
if (options.onActiveTurn) turnOptions.onActiveTurn = options.onActiveTurn;
|
||||
return turnOptions;
|
||||
}
|
||||
|
||||
function codexShellSandboxOverride(env: NodeJS.ProcessEnv): string | null {
|
||||
const value = env.AGENTRUN_CODEX_SHELL_SANDBOX?.trim();
|
||||
return value && value.length > 0 ? value : null;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ export interface CodexStdioTurnOptions {
|
||||
threadId?: string;
|
||||
approvalPolicy: string;
|
||||
sandbox: string;
|
||||
requestedSandbox?: string;
|
||||
sandboxOverrideSource?: string | null;
|
||||
timeoutMs: number;
|
||||
command?: string;
|
||||
args?: string[];
|
||||
@@ -1065,6 +1067,12 @@ function backendMetadata(options: CodexStdioTurnOptions): JsonRecord {
|
||||
backendKind: spec?.backendKind ?? "codex-app-server-stdio",
|
||||
protocol: spec?.protocol ?? codexProtocol,
|
||||
transport: spec?.transport ?? "stdio",
|
||||
sandbox: {
|
||||
requested: options.requestedSandbox ?? options.sandbox,
|
||||
effective: options.sandbox,
|
||||
overrideSource: options.sandboxOverrideSource ?? null,
|
||||
valuesPrinted: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { backendProfileSpec } from "../common/backend-profiles.js";
|
||||
|
||||
const defaultBootRepoUrl = "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/agentrun.git";
|
||||
const defaultResourceBinPath = "/usr/local/bin";
|
||||
const defaultCodexShellSandbox = "danger-full-access";
|
||||
|
||||
export interface RunnerJobRenderOptions {
|
||||
run: RunRecord;
|
||||
@@ -177,6 +178,7 @@ function runnerEnv(options: RunnerJobRenderOptions, context: { namespace: string
|
||||
{ name: "AGENTRUN_RUNNER_ID", value: context.runnerId },
|
||||
{ name: "AGENTRUN_BACKEND_PROFILE", value: options.run.backendProfile },
|
||||
{ name: "AGENTRUN_EXECUTION_POLICY_JSON", value: JSON.stringify(options.run.executionPolicy) },
|
||||
{ name: "AGENTRUN_CODEX_SHELL_SANDBOX", value: codexShellSandbox(options.run.executionPolicy) },
|
||||
{ name: "AGENTRUN_SESSION_REF_JSON", value: JSON.stringify(options.run.sessionRef ?? null) },
|
||||
{ name: "AGENTRUN_RESOURCE_BUNDLE_JSON", value: JSON.stringify(options.run.resourceBundleRef ?? null) },
|
||||
{ name: "AGENTRUN_WORKSPACE_ROOT", value: "/home/agentrun/workspaces" },
|
||||
@@ -205,6 +207,11 @@ function runnerEnv(options: RunnerJobRenderOptions, context: { namespace: string
|
||||
];
|
||||
}
|
||||
|
||||
function codexShellSandbox(policy: ExecutionPolicy): string {
|
||||
if (policy.sandbox === "workspace-write") return defaultCodexShellSandbox;
|
||||
return policy.sandbox;
|
||||
}
|
||||
|
||||
function toolCredentialEnvVars(items: ToolCredentialProjection[]): JsonRecord[] {
|
||||
return items.map((item) => ({
|
||||
name: item.envName,
|
||||
|
||||
@@ -90,7 +90,7 @@ export async function materializeResourceBundle(resourceBundleRef: ResourceBundl
|
||||
};
|
||||
const defaultCheckout = await checkoutFor(defaultSource);
|
||||
const materializedBundles = await materializeGitBundles(workspacePath, resourceBundleRef, defaultSource, defaultCheckout, checkoutFor);
|
||||
const tools = await prepareGitBundleTools(workspacePath);
|
||||
const tools = await prepareGitBundleTools(workspacePath, env);
|
||||
const skills = await discoverGitBundleSkills(workspacePath);
|
||||
const prompts = await materializePromptRefs(defaultCheckout.checkoutPath, resourceBundleRef.promptRefs ?? []);
|
||||
const initialPrompt = assembleInitialPrompt(prompts.items, skills.items);
|
||||
@@ -193,28 +193,50 @@ function optionalNonEmpty(value: unknown): string | undefined {
|
||||
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
||||
}
|
||||
|
||||
async function prepareGitBundleTools(workspacePath: string): Promise<{ binPath?: string; event: JsonRecord }> {
|
||||
const binPath = path.join(workspacePath, "tools");
|
||||
async function prepareGitBundleTools(workspacePath: string, env: NodeJS.ProcessEnv): Promise<{ binPath?: string; event: JsonRecord }> {
|
||||
const sourceBinPath = path.join(workspacePath, "tools");
|
||||
const installedBinPath = optionalNonEmpty(env.AGENTRUN_RESOURCE_BIN_PATH);
|
||||
const runtimeBinPath = installedBinPath ?? sourceBinPath;
|
||||
let entries;
|
||||
try {
|
||||
entries = await readdir(binPath, { withFileTypes: true });
|
||||
entries = await readdir(sourceBinPath, { withFileTypes: true });
|
||||
} catch (error) {
|
||||
if (error && typeof error === "object" && "code" in error && (error as { code?: unknown }).code === "ENOENT") return { event: { count: 0, names: [], binPath: null, valuesPrinted: false } };
|
||||
if (error && typeof error === "object" && "code" in error && (error as { code?: unknown }).code === "ENOENT") return { event: { count: 0, names: [], binPath: null, sourceBinPath: null, installedBinPath: null, installed: false, valuesPrinted: false } };
|
||||
throw error;
|
||||
}
|
||||
const names: string[] = [];
|
||||
const items: JsonRecord[] = [];
|
||||
if (installedBinPath) await mkdir(installedBinPath, { recursive: true });
|
||||
for (const entry of entries) {
|
||||
if (!entry.isFile()) continue;
|
||||
const filePath = path.join(binPath, entry.name);
|
||||
const filePath = path.join(sourceBinPath, entry.name);
|
||||
const text = await readFile(filePath, "utf8");
|
||||
const firstLine = text.split(/\r?\n/u, 1)[0] ?? "";
|
||||
if (!firstLine.startsWith("#!")) continue;
|
||||
await chmod(filePath, 0o755);
|
||||
if (installedBinPath) {
|
||||
const targetPath = path.join(installedBinPath, entry.name);
|
||||
if (targetPath !== filePath) {
|
||||
await cp(filePath, targetPath, { force: true, dereference: false });
|
||||
await chmod(targetPath, 0o755);
|
||||
}
|
||||
}
|
||||
names.push(entry.name);
|
||||
items.push({ name: entry.name, sha256: sha256Text(text), bytes: Buffer.byteLength(text, "utf8"), shebang: firstLine.slice(0, 80), valuesPrinted: false });
|
||||
}
|
||||
return { binPath, event: { count: names.length, names, items, binPath: pathSummary(binPath), valuesPrinted: false } };
|
||||
return {
|
||||
...(names.length > 0 ? { binPath: runtimeBinPath } : {}),
|
||||
event: {
|
||||
count: names.length,
|
||||
names,
|
||||
items,
|
||||
binPath: names.length > 0 ? pathSummary(runtimeBinPath) : null,
|
||||
sourceBinPath: pathSummary(sourceBinPath),
|
||||
installedBinPath: installedBinPath ? pathSummary(installedBinPath) : null,
|
||||
installed: Boolean(installedBinPath && names.length > 0),
|
||||
valuesPrinted: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function materializePromptRefs(checkoutPath: string, refs: NonNullable<ResourceBundleRef["promptRefs"]>): Promise<{ items: MaterializedPromptRef[]; event: JsonRecord }> {
|
||||
|
||||
@@ -42,6 +42,7 @@ const selfTest: SelfTestCase = async (context) => {
|
||||
assertRunnerJobUsesWritableCodexHome(rendered.manifest as JsonRecord, context.codexHome, "codex-0", "/var/run/agentrun/secrets/codex-0");
|
||||
assertRunnerJobUsesToolCredential(rendered, "GH_TOKEN", "agentrun-v01-tool-github-pr", "GH_TOKEN");
|
||||
assertRunnerJobUsesToolCredential(rendered, "UNIDESK_SSH_CLIENT_TOKEN", "agentrun-v01-tool-unidesk-ssh", "UNIDESK_SSH_CLIENT_TOKEN");
|
||||
assert.equal(runnerEnvValue(rendered.manifest as JsonRecord, "AGENTRUN_CODEX_SHELL_SANDBOX"), "danger-full-access");
|
||||
assert.equal(runnerEnvValue(rendered.manifest as JsonRecord, "HWLAB_API_KEY"), "REDACTED");
|
||||
assert.deepEqual((((rendered.transientEnv as JsonRecord).names) as string[]), ["HWLAB_API_KEY"]);
|
||||
assertNoSecretLeak(rendered);
|
||||
@@ -227,7 +228,7 @@ console.log(JSON.stringify({ apiVersion: manifest.apiVersion, kind: manifest.kin
|
||||
assert.equal(envMap.get("AGENTRUN_SESSION_PVC_NAMESPACE"), "agentrun-v01");
|
||||
assert.equal(envMap.get("AGENTRUN_SESSION_PVC_MOUNT_PATH"), "/home/agentrun/.codex-codex/sessions");
|
||||
assert.equal(envMap.get("AGENTRUN_CODEX_ROLLOUT_SUBDIR"), "sessions");
|
||||
return { name: "runner-k8s-job", tests: ["runner-k8s-job-dry-run", "runner-k8s-job-deepseek-profile-dry-run", "runner-k8s-job-minimax-m3-profile-dry-run", "runner-k8s-job-dsflash-go-profile-dry-run", "runner-k8s-job-dsflash-go-legacy-secretref-normalized", "runner-k8s-job-create-api", "runner-k8s-job-retention-ttl", "runner-job-transient-env", "runner-job-tool-credential-env", "runner-job-unidesk-ssh-tool-credential-env", "runner-job-unidesk-ssh-transient-env-denied", "runner-k8s-job-session-pvc-volume-and-env"] };
|
||||
return { name: "runner-k8s-job", tests: ["runner-k8s-job-dry-run", "runner-k8s-job-codex-shell-sandbox-env", "runner-k8s-job-deepseek-profile-dry-run", "runner-k8s-job-minimax-m3-profile-dry-run", "runner-k8s-job-dsflash-go-profile-dry-run", "runner-k8s-job-dsflash-go-legacy-secretref-normalized", "runner-k8s-job-create-api", "runner-k8s-job-retention-ttl", "runner-job-transient-env", "runner-job-tool-credential-env", "runner-job-unidesk-ssh-tool-credential-env", "runner-job-unidesk-ssh-transient-env-denied", "runner-k8s-job-session-pvc-volume-and-env"] };
|
||||
} finally {
|
||||
await new Promise<void>((resolve) => server.server.close(() => resolve()));
|
||||
}
|
||||
|
||||
@@ -27,6 +27,15 @@ const selfTest: SelfTestCase = async (context) => {
|
||||
const finalCommand = await client.get(`/api/v1/runs/${happy.runId}/commands/${happy.commandId}`) as { state?: string };
|
||||
assert.equal(finalCommand.state, "completed");
|
||||
|
||||
const sandboxOverride = await createRunWithCommand(client, context, "hello sandbox override", "selftest-sandbox-override", 15_000);
|
||||
const sandboxOverrideResult = await runOnce({ managerUrl: server.baseUrl, runId: sandboxOverride.runId, codexCommand: context.fakeCodexCommand, codexArgs: context.fakeCodexArgs, codexHome: context.codexHome, env: { CODEX_HOME: context.codexHome, AGENTRUN_CODEX_SHELL_SANDBOX: "danger-full-access", AGENTRUN_FAKE_CODEX_MODE: "require-danger-sandbox" }, oneShot: true });
|
||||
assert.equal(sandboxOverrideResult.terminalStatus, "completed");
|
||||
const sandboxEvents = await client.get(`/api/v1/runs/${sandboxOverride.runId}/events?afterSeq=0&limit=100`) as { items?: Array<{ type: string; payload: JsonRecord }> };
|
||||
const sandboxStarting = sandboxEvents.items?.find((event) => event.type === "backend_status" && event.payload.phase === "codex-app-server-starting");
|
||||
assert.equal(((sandboxStarting?.payload.sandbox as JsonRecord | undefined)?.requested), "workspace-write");
|
||||
assert.equal(((sandboxStarting?.payload.sandbox as JsonRecord | undefined)?.effective), "danger-full-access");
|
||||
assert.equal(((sandboxStarting?.payload.sandbox as JsonRecord | undefined)?.overrideSource), "AGENTRUN_CODEX_SHELL_SANDBOX");
|
||||
|
||||
await runLeaseConflictRecoveryCase({ client, managerUrl: server.baseUrl, context });
|
||||
|
||||
const projectedHome = path.join(context.tmp, "runtime-codex-home");
|
||||
@@ -230,7 +239,7 @@ const selfTest: SelfTestCase = async (context) => {
|
||||
await runSessionStorageSubdirCase({ client, managerUrl: server.baseUrl, context });
|
||||
await runSessionStorageNoSecretLeakCase({ client, managerUrl: server.baseUrl, context });
|
||||
|
||||
return { name: "codex-stdio", tests: ["runner-lease-heartbeat", "runner-lease-conflict-recovery", "codex-stdio-fake-turn", "codex-stdio-projected-writable-home", "codex-stdio-deepseek-profile-fake-turn", "codex-stdio-dsflash-go-profile-fake-turn", "codex-stdio-dsflash-go-config-metadata", "codex-stdio-minimax-m3-profile-fake-turn", "codex-stdio-deepseek-missing-secret-no-fallback", "codex-stdio-minimax-m3-missing-secret-no-fallback", "codex-stdio-config-model-authoritative", "codex-stdio-explicit-model-forwarded", "codex-stdio-final-agent-message-only", "codex-stdio-web-search-progress", "codex-stdio-stale-thread-resume-failed", "codex-stdio-live-tool-events", "codex-stdio-noisy-reasoning-suppression", "codex-stdio-missing-turn-result", "codex-stdio-provider-auth-failed", "codex-stdio-provider-rate-limited", "codex-stdio-provider-invalid-tool-call", "codex-stdio-provider-compact-unsupported", "codex-stdio-provider-503-rpc-error", "codex-stdio-provider-503-terminal", "codex-stdio-provider-503-retry-event", "codex-stdio-invalid-json", "codex-stdio-timeout", "codex-stdio-idle-timeout-progress-refresh", "codex-stdio-command-failure-keeps-run-open", "codex-stdio-secret-unavailable", "codex-stdio-spawn-failure"] };
|
||||
return { name: "codex-stdio", tests: ["runner-lease-heartbeat", "runner-lease-conflict-recovery", "codex-stdio-fake-turn", "codex-stdio-k8s-sandbox-override", "codex-stdio-projected-writable-home", "codex-stdio-deepseek-profile-fake-turn", "codex-stdio-dsflash-go-profile-fake-turn", "codex-stdio-dsflash-go-config-metadata", "codex-stdio-minimax-m3-profile-fake-turn", "codex-stdio-deepseek-missing-secret-no-fallback", "codex-stdio-minimax-m3-missing-secret-no-fallback", "codex-stdio-config-model-authoritative", "codex-stdio-explicit-model-forwarded", "codex-stdio-final-agent-message-only", "codex-stdio-web-search-progress", "codex-stdio-stale-thread-resume-failed", "codex-stdio-live-tool-events", "codex-stdio-noisy-reasoning-suppression", "codex-stdio-missing-turn-result", "codex-stdio-provider-auth-failed", "codex-stdio-provider-rate-limited", "codex-stdio-provider-invalid-tool-call", "codex-stdio-provider-compact-unsupported", "codex-stdio-provider-503-rpc-error", "codex-stdio-provider-503-terminal", "codex-stdio-provider-503-retry-event", "codex-stdio-invalid-json", "codex-stdio-timeout", "codex-stdio-idle-timeout-progress-refresh", "codex-stdio-command-failure-keeps-run-open", "codex-stdio-secret-unavailable", "codex-stdio-spawn-failure"] };
|
||||
} finally {
|
||||
await new Promise<void>((resolve) => server.server.close(() => resolve()));
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { execFile as execFileCallback } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { access, chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { startManagerServer } from "../../mgr/server.js";
|
||||
import { ManagerClient } from "../../mgr/client.js";
|
||||
@@ -79,8 +79,12 @@ console.log(JSON.stringify({ apiVersion: manifest.apiVersion, kind: manifest.kin
|
||||
);
|
||||
|
||||
const sessionRun = await createHwlabRun(client, context, bundle, "hwlab-session-resume", "hello session", "hwlab-command-session");
|
||||
const runResult = await runOnce({ managerUrl: server.baseUrl, runId: sessionRun.runId, codexCommand: context.fakeCodexCommand, codexArgs: context.fakeCodexArgs, codexHome: context.codexHome, env: { CODEX_HOME: context.codexHome, AGENTRUN_WORKSPACE_ROOT: path.join(context.tmp, "workspaces") }, oneShot: true });
|
||||
const resourceBinPath = path.join(context.tmp, "resource-bin");
|
||||
const runResult = await runOnce({ managerUrl: server.baseUrl, runId: sessionRun.runId, codexCommand: context.fakeCodexCommand, codexArgs: context.fakeCodexArgs, codexHome: context.codexHome, env: { CODEX_HOME: context.codexHome, AGENTRUN_WORKSPACE_ROOT: path.join(context.tmp, "workspaces"), AGENTRUN_RESOURCE_BIN_PATH: resourceBinPath }, oneShot: true });
|
||||
assert.equal(runResult.terminalStatus, "completed");
|
||||
await access(path.join(resourceBinPath, "hwpod"));
|
||||
const resourceBinExec = await execFile(path.join(resourceBinPath, "hwpod"), ["--selftest"]);
|
||||
assert.match(resourceBinExec.stdout, /hwpod-selftest/u);
|
||||
const session = await store.getSession("hwlab-session-resume");
|
||||
assert.equal(session?.threadId, "thread_selftest_1");
|
||||
const resultEnvelope = await client.get(`/api/v1/runs/${sessionRun.runId}/commands/${sessionRun.commandId}/result`) as JsonRecord;
|
||||
@@ -93,6 +97,7 @@ console.log(JSON.stringify({ apiVersion: manifest.apiVersion, kind: manifest.kin
|
||||
assert.deepEqual(resultBundleTargets, ["tools", ".agents/skills"]);
|
||||
const materialized = ((resultEnvelope.resourceBundleRef as JsonRecord).materialized as JsonRecord);
|
||||
assert.deepEqual(((materialized.tools as JsonRecord).names), ["hwpod"]);
|
||||
assert.equal(((materialized.tools as JsonRecord).installed), true);
|
||||
assert.deepEqual(((materialized.skillDirs as JsonRecord).names), ["hwpod-cli", "hwpod-ctl"]);
|
||||
assertNoSecretLeak(resultEnvelope);
|
||||
|
||||
|
||||
@@ -23,6 +23,10 @@ for await (const line of rl) {
|
||||
}
|
||||
if (message.method === "thread/start") {
|
||||
observedThreadModel = Object.hasOwn(message.params ?? {}, "model");
|
||||
if (mode === "require-danger-sandbox" && message.params?.sandbox !== "danger-full-access") {
|
||||
respond(message.id, null, { code: -32000, message: `thread/start expected danger-full-access sandbox, got ${String(message.params?.sandbox ?? "missing")}` });
|
||||
continue;
|
||||
}
|
||||
if (mode === "reject-unexpected-model" && observedThreadModel) {
|
||||
respond(message.id, null, { code: -32000, message: "thread/start unexpectedly included model" });
|
||||
continue;
|
||||
@@ -39,6 +43,10 @@ for await (const line of rl) {
|
||||
}
|
||||
if (message.method === "thread/resume") {
|
||||
observedThreadModel = Object.hasOwn(message.params ?? {}, "model");
|
||||
if (mode === "require-danger-sandbox" && message.params?.sandbox !== "danger-full-access") {
|
||||
respond(message.id, null, { code: -32000, message: `thread/resume expected danger-full-access sandbox, got ${String(message.params?.sandbox ?? "missing")}` });
|
||||
continue;
|
||||
}
|
||||
if (mode === "resume-no-rollout") {
|
||||
respond(message.id, null, { code: -32000, message: `no rollout found for thread id ${String(message.params?.threadId ?? "unknown")}` });
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user