From acb9674454da3569bf8633c4d1e1102efdbe62b6 Mon Sep 17 00:00:00 2001 From: Codex Date: Sun, 12 Jul 2026 17:57:44 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=B6=E7=AA=84=20Artificer=20pika=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E8=8C=83=E5=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/agentrun.yaml | 2 - scripts/src/agentrun-lanes.ts | 71 +-- .../src/agentrun-secret-sync-output.test.ts | 5 - scripts/src/agentrun.test.ts | 459 +----------------- scripts/src/agentrun/config.ts | 31 +- scripts/src/agentrun/entry.ts | 25 +- scripts/src/agentrun/provider-profile.ts | 17 +- scripts/src/agentrun/public-exposure.ts | 5 +- scripts/src/agentrun/render.ts | 3 +- scripts/src/agentrun/rest-bridge.ts | 227 +-------- scripts/src/agentrun/secret-sync-output.ts | 9 +- scripts/src/agentrun/secrets.ts | 4 - scripts/src/agentrun/session-send-render.ts | 58 --- scripts/src/agentrun/trigger.ts | 8 +- 14 files changed, 50 insertions(+), 874 deletions(-) diff --git a/config/agentrun.yaml b/config/agentrun.yaml index a38a965c..9e4e3dfa 100644 --- a/config/agentrun.yaml +++ b/config/agentrun.yaml @@ -344,7 +344,6 @@ controlPlane: name: agentrun-v02-provider-gpt-pika key: auth.json providerCredential: - sourceProfile: gpt.pika profile: gpt-pika - id: provider-gpt-pika-config sourceMode: file @@ -354,7 +353,6 @@ controlPlane: name: agentrun-v02-provider-gpt-pika key: config.toml providerCredential: - sourceProfile: gpt.pika profile: gpt-pika - id: provider-dsflash-go-auth-json sourceMode: env diff --git a/scripts/src/agentrun-lanes.ts b/scripts/src/agentrun-lanes.ts index de944105..6953379d 100644 --- a/scripts/src/agentrun-lanes.ts +++ b/scripts/src/agentrun-lanes.ts @@ -107,7 +107,6 @@ export interface AgentRunLaneSecretSpec { readonly codexConfig: AgentRunCodexConfigSpec | null; readonly jsonValue: unknown; readonly targetRef: AgentRunSecretRef; - readonly providerCredentialSourceProfile: string | null; readonly providerCredentialProfile: string | null; } @@ -159,7 +158,6 @@ export interface AgentRunCodexProjectSpec { } export interface AgentRunProviderCredentialRef { - readonly sourceProfile: string; readonly profile: string; readonly secretRef: { readonly namespace: string; @@ -542,10 +540,7 @@ export function agentRunLaneSummary(spec: AgentRunLaneSpec): Record ({ - sourceProfile: credential.sourceProfile, profile: credential.profile, secretRef: credential.secretRef, valuesPrinted: false, @@ -581,15 +575,13 @@ export function agentRunLaneSummary(spec: AgentRunLaneSpec): Record(); - for (const secret of agentRunProviderCredentialSecrets(spec)) { - const sourceProfile = secret.providerCredentialSourceProfile; + const groups = new Map(); + for (const secret of spec.secrets) { const credentialProfile = secret.providerCredentialProfile; - if (sourceProfile === null || credentialProfile === null) continue; + if (credentialProfile === null) continue; if (profile !== undefined && profile !== null && credentialProfile !== profile) continue; - const groupKey = `${sourceProfile}\0${credentialProfile}\0${secret.targetRef.namespace}\0${secret.targetRef.name}`; + const groupKey = `${credentialProfile}\0${secret.targetRef.namespace}\0${secret.targetRef.name}`; const group = groups.get(groupKey) ?? { - sourceProfile, profile: credentialProfile, namespace: secret.targetRef.namespace, name: secret.targetRef.name, @@ -599,7 +591,6 @@ export function agentRunProviderCredentialRefs(spec: AgentRunLaneSpec, profile?: groups.set(groupKey, group); } return Array.from(groups.values()).map((group) => ({ - sourceProfile: group.sourceProfile, profile: group.profile, secretRef: { namespace: group.namespace, @@ -609,25 +600,6 @@ export function agentRunProviderCredentialRefs(spec: AgentRunLaneSpec, profile?: })); } -export function agentRunProviderCredentialRefsForSelection(spec: AgentRunLaneSpec, profile: string): readonly AgentRunProviderCredentialRef[] { - const effectiveProfiles = new Set( - agentRunProviderCredentialSecrets(spec, profile) - .map((secret) => secret.providerCredentialProfile) - .filter((value): value is string => value !== null), - ); - if (effectiveProfiles.size !== 1) return []; - return agentRunProviderCredentialRefs(spec, Array.from(effectiveProfiles)[0]); -} - -export function agentRunProviderCredentialSecrets(spec: AgentRunLaneSpec, profile?: string | null): readonly AgentRunLaneSecretSpec[] { - return spec.secrets.filter((secret) => { - const sourceProfile = secret.providerCredentialSourceProfile; - const effectiveProfile = secret.providerCredentialProfile; - if (sourceProfile === null || effectiveProfile === null) return false; - return profile === undefined || profile === null || profile === sourceProfile || profile === effectiveProfile; - }); -} - export function agentRunPipelineRunName(spec: AgentRunLaneSpec, sourceCommit: string): string { return `${spec.ci.pipelineRunPrefix}-${sourceCommit.slice(0, 12)}`; } @@ -1051,8 +1023,7 @@ function parseLaneSecret(input: Record, path: string): AgentRun codexConfig: sourceMode === "codex-config" ? parseCodexConfig(recordField(input, "codexConfig", path), `${path}.codexConfig`) : null, jsonValue, targetRef: parseNamespacedSecretRef(recordField(input, "targetRef", path), `${path}.targetRef`), - providerCredentialSourceProfile: providerCredential?.sourceProfile ?? null, - providerCredentialProfile: providerCredential?.profile ?? null, + providerCredentialProfile: providerCredential, }; } @@ -1060,8 +1031,6 @@ function parseLaneSecrets(input: Record, path: string): AgentRu const secrets = arrayField(input, "secrets", path).map((secret, index) => parseLaneSecret(secret, `${path}.secrets[${index}]`)); const idPaths = new Map(); const targetRefPaths = new Map(); - const sourceProfileMappings = new Map(); - const effectiveProfileMappings = new Map(); for (const [index, secret] of secrets.entries()) { const secretPath = `${path}.secrets[${index}]`; const previousIdPath = idPaths.get(secret.id); @@ -1075,20 +1044,6 @@ function parseLaneSecrets(input: Record, path: string): AgentRu throw new Error(`${secretPath}.targetRef duplicates ${previousTargetRefPath}.targetRef (${targetRef})`); } targetRefPaths.set(targetRef, secretPath); - if (secret.providerCredentialSourceProfile !== null && secret.providerCredentialProfile !== null) { - const sourceProfile = secret.providerCredentialSourceProfile; - const effectiveProfile = secret.providerCredentialProfile; - const priorEffective = sourceProfileMappings.get(sourceProfile); - if (priorEffective !== undefined && priorEffective.profile !== effectiveProfile) { - throw new Error(`${secretPath}.providerCredential maps sourceProfile ${sourceProfile} to ${effectiveProfile}, conflicting with ${priorEffective.path}.providerCredential.profile=${priorEffective.profile}`); - } - sourceProfileMappings.set(sourceProfile, { profile: effectiveProfile, path: secretPath }); - const priorSource = effectiveProfileMappings.get(effectiveProfile); - if (priorSource !== undefined && priorSource.sourceProfile !== sourceProfile) { - throw new Error(`${secretPath}.providerCredential maps effective profile ${effectiveProfile} from ${sourceProfile}, conflicting with ${priorSource.path}.providerCredential.sourceProfile=${priorSource.sourceProfile}`); - } - effectiveProfileMappings.set(effectiveProfile, { sourceProfile, path: secretPath }); - } } return secrets; } @@ -1252,15 +1207,13 @@ function integerRecord(value: unknown, path: string): Readonly, path: string): { sourceProfile: string; profile: string } | null { +function parseProviderCredentialBinding(input: Record, path: string): string | null { const value = input.providerCredential; if (value === undefined || value === null) return null; const record = asRecord(value, path); const profile = stringField(record, "profile", path); - const sourceProfile = optionalStringField(record, "sourceProfile", path) ?? profile; - validateSimpleId(sourceProfile, `${path}.sourceProfile`); - validateAgentRunBackendProfile(profile, `${path}.profile`); - return { sourceProfile, profile }; + validateSimpleId(profile, path); + return profile; } function parseGitMirrorRepositories(input: readonly Record[], path: string): readonly AgentRunGitMirrorRepositorySpec[] { @@ -1565,12 +1518,6 @@ function validateSimpleId(value: string, path: string): void { if (!/^[A-Za-z0-9._-]+$/u.test(value)) throw new Error(`${path}.${value} must use a simple id`); } -function validateAgentRunBackendProfile(value: string, path: string): void { - if (!/^[a-z0-9][a-z0-9-]{0,63}$/u.test(value)) { - throw new Error(`${path} must match the canonical AgentRun backend profile slug /^[a-z0-9][a-z0-9-]{0,63}$/`); - } -} - function validateKubernetesNamePrefix(value: string, path: string): void { if (!/^[a-z0-9]([-a-z0-9]*[a-z0-9])?$/u.test(value)) throw new Error(`${path} must be a Kubernetes name prefix`); } diff --git a/scripts/src/agentrun-secret-sync-output.test.ts b/scripts/src/agentrun-secret-sync-output.test.ts index 7adcee24..d5abfc76 100644 --- a/scripts/src/agentrun-secret-sync-output.test.ts +++ b/scripts/src/agentrun-secret-sync-output.test.ts @@ -25,8 +25,6 @@ function blockedFixture(): Record { secret: index >= 11 ? "agentrun-v01-tool-github-ssh" : `fixture-target-${index}`, key: index === 11 ? "id_ed25519" : index === 12 ? "known_hosts" : `key-${index}`, sourceMode: "file", - sourceProfile: index === 0 ? "gpt.pika" : null, - effectiveBackendProfile: index === 0 ? "gpt-pika" : null, sourcePath: "/root/.unidesk/.state/secrets/", present: true, sourceFilePresent: true, @@ -164,7 +162,6 @@ describe("AgentRun secret-sync bounded output", () => { expect(json.plan.omittedCount).toBe(0); expect(json.plan.items.some((item: Record) => item.id === "tool-github-ssh-private-key")).toBe(true); expect(json.plan.items.some((item: Record) => item.id === "tool-github-ssh-known-hosts")).toBe(true); - expect(json.plan.items[0].profile).toEqual({ sourceProfile: "gpt.pika", effectiveBackendProfile: "gpt-pika" }); expect(json.activationFence).toMatchObject({ kind: "ProviderSecretActivationFence", identity: "providersecretactivationfence/NC01/nc01-v02", @@ -189,8 +186,6 @@ describe("AgentRun secret-sync bounded output", () => { expect(human).toContain("ACTIVATION FENCE"); expect(human).toContain("runnerjob/rjob_fixture"); expect(human).toContain("tool-github-ssh-private-key"); - expect(human).toContain("gpt.pika"); - expect(human).toContain("gpt-pika"); expect(human).toContain("VALUES PRINTED false"); for (const output of [human, jsonText, yamlText]) { expect(Buffer.byteLength(output, "utf8")).toBeLessThan(10_240); diff --git a/scripts/src/agentrun.test.ts b/scripts/src/agentrun.test.ts index 068844e9..4ca67d5d 100644 --- a/scripts/src/agentrun.test.ts +++ b/scripts/src/agentrun.test.ts @@ -1,7 +1,7 @@ import { readFileSync, rmSync } from "node:fs"; import { describe, expect, test } from "bun:test"; import { parseAgentRunClientConfigYaml, resolveAgentRunAuth, runAgentRunCommand, stripAgentRunResourceWrapperArgs } from "./agentrun"; -import { agentRunProviderCredentialRefs, agentRunProviderCredentialRefsForSelection, resolveAgentRunLaneTarget } from "./agentrun-lanes"; +import { resolveAgentRunLaneTarget } from "./agentrun-lanes"; import { agentRunToolCredentialRefs } from "./agentrun/config"; import { placeholderAgentRunImage, renderAgentRunGitopsFiles } from "./agentrun-manifests"; import { collectLaneSecretSources, secretSyncScript } from "./agentrun/secrets"; @@ -979,101 +979,6 @@ describe("AgentRun default transport contract", () => { }); describe("AgentRun YAML tool credential binding", () => { - test("maps the YAML source profile to one canonical AgentRun backend profile", async () => { - const { spec } = resolveAgentRunLaneTarget({ node: "NC01", lane: "nc01-v02" }); - const pikaSecrets = spec.secrets.filter((secret) => secret.providerCredentialSourceProfile === "gpt.pika"); - expect(pikaSecrets.map((secret) => ({ - id: secret.id, - sourceMode: secret.sourceMode, - sourceRef: secret.sourceRef, - sourceKey: secret.sourceKey, - sourceFormat: secret.sourceFormat, - transform: secret.transform, - codexConfig: secret.codexConfig, - jsonValue: secret.jsonValue, - sourceProfile: secret.providerCredentialSourceProfile, - effectiveBackendProfile: secret.providerCredentialProfile, - targetRef: secret.targetRef, - }))).toEqual([ - { - id: "provider-gpt-pika-auth-json", - sourceMode: "file", - sourceRef: "/root/.codex/auth.json.pika", - sourceKey: null, - sourceFormat: null, - transform: null, - codexConfig: null, - jsonValue: null, - sourceProfile: "gpt.pika", - effectiveBackendProfile: "gpt-pika", - targetRef: { namespace: "agentrun-v02", name: "agentrun-v02-provider-gpt-pika", key: "auth.json" }, - }, - { - id: "provider-gpt-pika-config", - sourceMode: "file", - sourceRef: "/root/.codex/config.toml.pika", - sourceKey: null, - sourceFormat: null, - transform: null, - codexConfig: null, - jsonValue: null, - sourceProfile: "gpt.pika", - effectiveBackendProfile: "gpt-pika", - targetRef: { namespace: "agentrun-v02", name: "agentrun-v02-provider-gpt-pika", key: "config.toml" }, - }, - ]); - const renderedPikaSources = collectLaneSecretSources(spec).filter((source) => source.providerCredentialSourceProfile === "gpt.pika"); - expect(renderedPikaSources.map((source) => ({ - id: source.id, - sourceMode: source.sourceMode, - sourceRef: source.sourceRef, - sourceKey: source.sourceKey, - sourceFormat: source.sourceFormat, - transform: source.transform, - codexConfig: source.codexConfig, - jsonValue: source.jsonValue, - sourceProfile: source.providerCredentialSourceProfile, - effectiveBackendProfile: source.providerCredentialProfile, - targetRef: source.targetRef, - }))).toEqual(pikaSecrets.map((secret) => ({ - id: secret.id, - sourceMode: secret.sourceMode, - sourceRef: secret.sourceRef, - sourceKey: secret.sourceKey, - sourceFormat: secret.sourceFormat, - transform: secret.transform, - codexConfig: secret.codexConfig, - jsonValue: secret.jsonValue, - sourceProfile: secret.providerCredentialSourceProfile, - effectiveBackendProfile: secret.providerCredentialProfile, - targetRef: secret.targetRef, - }))); - const expected = [{ - sourceProfile: "gpt.pika", - profile: "gpt-pika", - secretRef: { - namespace: "agentrun-v02", - name: "agentrun-v02-provider-gpt-pika", - keys: ["auth.json", "config.toml"], - }, - }]; - expect(agentRunProviderCredentialRefsForSelection(spec, "gpt.pika")).toEqual(expected); - expect(agentRunProviderCredentialRefs(spec, "gpt-pika")).toEqual(expected); - expect(agentRunProviderCredentialRefs(spec, "gpt.pika")).toEqual([]); - - const invalidPath = `/tmp/unidesk-agentrun-invalid-profile-${process.pid}-${Date.now()}.yaml`; - try { - const invalid = readFileSync("config/agentrun.yaml", "utf8").replaceAll(" profile: gpt-pika", " profile: gpt.pika"); - await Bun.write(invalidPath, invalid); - expect(() => resolveAgentRunLaneTarget( - { node: "NC01", lane: "nc01-v02" }, - { ...process.env, AGENTRUN_CONTROL_PLANE_CONFIG: invalidPath }, - )).toThrow("canonical AgentRun backend profile slug"); - } finally { - rmSync(invalidPath, { force: true }); - } - }); - test("aggregates managed GitHub SSH source keys into one volume credential and one sync target", () => { const { spec } = resolveAgentRunLaneTarget({ node: "NC01", lane: "nc01-v02" }); const githubSsh = agentRunToolCredentialRefs(spec).filter((credential) => credential.tool === "github" && credential.purpose === "github-ssh"); @@ -1115,290 +1020,6 @@ describe("AgentRun YAML tool credential binding", () => { expect(JSON.stringify({ githubSsh, sources })).not.toContain("/root/.ssh"); }); - test("shares model overrides across Aipod render, create, and send without overriding provider authority", async () => { - const renderBodies: Record[] = []; - const submittedBodies: Record[] = []; - const sendBodies: Record[] = []; - let renderedBackendProfile = "gpt-pika"; - let renderedCredentialProfile = "gpt-pika"; - let renderedSecretName = "agentrun-v02-provider-gpt-pika"; - let resolutionModelOverride: string | null = null; - const server = Bun.serve({ - port: 0, - async fetch(request) { - const url = new URL(request.url); - expect(request.headers.get("authorization")).toBe("Bearer secret-value"); - if (url.pathname === "/api/v1/aipod-specs/Artificer/render") { - const input = await request.json() as Record; - renderBodies.push(input); - const model = input.model ?? "gpt-5.6-terra"; - const reasoningEffort = input.reasoningEffort ?? "xhigh"; - const modelSource = input.model === undefined ? "aipod-spec-default" : "render-input-override"; - const reasoningEffortSource = input.reasoningEffort === undefined ? "aipod-spec-default" : "render-input-override"; - return Response.json({ - ok: true, - data: { - queueTask: { - tenantId: "unidesk", - projectId: "pikasTech/unidesk", - queue: "commander", - lane: "v0.2", - providerId: "NC01", - backendProfile: renderedBackendProfile, - workspaceRef: input.workspaceRef ?? { kind: "opaque", path: "." }, - executionPolicy: { - sandbox: "workspace-write", - approval: "never", - timeoutMs: 1_800_000, - network: "enabled", - secretScope: { - allowCredentialEcho: false, - providerCredentials: [{ - profile: renderedCredentialProfile, - secretRef: { - namespace: "agentrun-v02", - name: renderedSecretName, - keys: ["auth.json", "config.toml"], - }, - }], - }, - }, - payload: { - prompt: input.prompt ?? "fixture", - model, - reasoningEffort, - modelConfig: { model, reasoningEffort, modelSource, reasoningEffortSource, valuesPrinted: false }, - }, - }, - dispatchDefaults: { - runnerJob: { namespace: "agentrun-v02" }, - }, - modelResolution: { - model: resolutionModelOverride ?? model, - reasoningEffort, - modelSource, - reasoningEffortSource, - modelOverridden: input.model !== undefined, - reasoningEffortOverridden: input.reasoningEffort !== undefined, - backendProfile: "gpt-pika", - backendProfileSource: "aipod-spec", - providerCredential: { - profile: "gpt-pika", - secretRef: { - namespace: "agentrun-v02", - name: "agentrun-v02-provider-gpt-pika", - keys: ["auth.json", "config.toml"], - valuesPrinted: false, - }, - valuesPrinted: false, - }, - valuesPrinted: false, - }, - }, - }); - } - if (url.pathname === "/api/v1/queue/tasks") { - submittedBodies.push(await request.json() as Record); - return Response.json({ ok: true, data: { id: "qt_gpt_pika", state: "pending" } }); - } - if (url.pathname === "/api/v1/sessions/ses_gpt_pika/send") { - const body = await request.json() as Record; - sendBodies.push(body); - return Response.json({ - ok: true, - data: { - action: "session-send-plan", - dryRun: body.dryRun, - mutation: false, - sessionId: "ses_gpt_pika", - decision: "turn", - internalCommandType: "turn", - request: { method: "POST", path: url.pathname, createRunnerJob: true, valuesPrinted: false }, - valuesPrinted: false, - }, - }); - } - return new Response("not found", { status: 404 }); - }, - }); - const previousConfig = process.env.AGENTRUN_CLIENT_CONFIG; - const previousKey = process.env.HWLAB_API_KEY; - process.env.HWLAB_API_KEY = "secret-value"; - const tempConfigPath = `/tmp/unidesk-agentrun-gpt-pika-${process.pid}-${Date.now()}.yaml`; - const tempInputPath = `/tmp/unidesk-agentrun-gpt-pika-input-${process.pid}-${Date.now()}.json`; - try { - process.env.AGENTRUN_CLIENT_CONFIG = tempConfigPath; - await Bun.write(tempConfigPath, agentRunMinimalClientYaml.replace("http://agentrun.example.local:8080", server.url.href.replace(/\/$/u, ""))); - - const defaultPlan = JSON.parse(renderedTextOf(await runAgentRunCommand(null, [ - "create", "task", "--aipod", "Artificer", "--prompt", "default fixture", "--dry-run", "-o", "json", - ]))) as Record; - expect(defaultPlan.aipodBinding).toMatchObject({ - sourceProfile: "gpt.pika", - effectiveBackendProfile: "gpt-pika", - backendProfileSource: "aipod-spec", - model: "gpt-5.6-terra", - reasoningEffort: "xhigh", - modelSource: "aipod-spec-default", - reasoningEffortSource: "aipod-spec-default", - modelOverridden: false, - reasoningEffortOverridden: false, - valuesPrinted: false, - }); - expect(defaultPlan.aipodBinding.providerCredentials).toEqual([{ - profile: "gpt-pika", - secretRef: { name: "agentrun-v02-provider-gpt-pika", keys: ["auth.json", "config.toml"] }, - valuesPrinted: false, - }]); - expect(renderBodies[0]).toEqual({ prompt: "default fixture" }); - - const override = await runAgentRunCommand(null, [ - "create", "task", "--aipod", "Artificer", "--prompt", "override fixture", - "--model", "gpt-5.6-terra-alt", "--reasoning-effort", "xhigh", "-o", "json", - ]); - expect(override.ok).toBe(true); - expect(renderBodies[1]).toMatchObject({ - prompt: "override fixture", - model: "gpt-5.6-terra-alt", - reasoningEffort: "xhigh", - }); - expect(renderBodies[1]?.backendProfile).toBeUndefined(); - expect(renderBodies[1]?.executionPolicy).toBeUndefined(); - expect(submittedBodies[0]).toMatchObject({ - backendProfile: "gpt-pika", - payload: { model: "gpt-5.6-terra-alt", reasoningEffort: "xhigh" }, - executionPolicy: { - secretScope: { - providerCredentials: [{ - profile: "gpt-pika", - secretRef: { name: "agentrun-v02-provider-gpt-pika", keys: ["auth.json", "config.toml"] }, - }], - }, - }, - }); - - const send = await runAgentRunCommand(null, [ - "send", "session/ses_gpt_pika", "--aipod", "Artificer", "--prompt", "send fixture", - "--model", "gpt-5.6-terra-send", "--reasoning-effort", "medium", "--dry-run", "-o", "json", - ]); - expect(send.ok).toBe(true); - const sendProjection = JSON.parse(renderedTextOf(send)) as Record; - expect(sendProjection.aipodBinding).toMatchObject({ - sourceProfile: "gpt.pika", - effectiveBackendProfile: "gpt-pika", - model: "gpt-5.6-terra-send", - reasoningEffort: "medium", - modelSource: "render-input-override", - reasoningEffortSource: "render-input-override", - modelOverridden: true, - reasoningEffortOverridden: true, - valuesPrinted: false, - }); - expect(renderBodies[2]).toMatchObject({ model: "gpt-5.6-terra-send", reasoningEffort: "medium" }); - expect(sendBodies[0]).toMatchObject({ - dryRun: true, - run: { backendProfile: "gpt-pika" }, - payload: { model: "gpt-5.6-terra-send", reasoningEffort: "medium" }, - }); - - const render = await runAgentRunCommand(null, [ - "aipod-specs", "render", "Artificer", "--model", "gpt-5.6-terra-render", "--reasoning-effort", "low", "-o", "json", - ]); - expect(render.ok).toBe(true); - expect(renderBodies[3]).toEqual({ model: "gpt-5.6-terra-render", reasoningEffort: "low" }); - - const createHuman = renderedTextOf(await runAgentRunCommand(null, [ - "create", "task", "--aipod", "Artificer", "--prompt", "human create", "--dry-run", - ])); - const sendHuman = renderedTextOf(await runAgentRunCommand(null, [ - "send", "session/ses_gpt_pika", "--aipod", "Artificer", "--prompt", "human send", - "--model", "gpt-5.6-terra-human", "--reasoning-effort", "medium", "--dry-run", - ])); - const renderHuman = renderedTextOf(await runAgentRunCommand(null, [ - "aipod-specs", "render", "Artificer", "--model", "gpt-5.6-terra-human", "--reasoning-effort", "medium", - ])); - for (const human of [createHuman, sendHuman, renderHuman]) { - expect(human).toContain("sourceProfile=gpt.pika"); - expect(human).toContain("effectiveBackendProfile=gpt-pika"); - expect(human).toContain("model="); - expect(human).toContain("reasoningEffort="); - expect(human).toContain("valuesPrinted=false"); - expect(human).not.toContain("secret-value"); - } - expect(createHuman).toContain("modelSource=aipod-spec-default"); - expect(sendHuman).toContain("modelSource=render-input-override"); - expect(renderHuman).toContain("modelSource=render-input-override"); - - for (const helpArgs of [["create", "--help"], ["send", "--help"], ["aipod-specs", "render", "--help"]]) { - const help = renderedTextOf(await runAgentRunCommand(null, helpArgs)); - expect(help).toContain("--model "); - expect(help).toContain("--reasoning-effort "); - } - - resolutionModelOverride = "gpt-5.6-terra-conflict"; - const resolutionRejected = await runAgentRunCommand(null, [ - "create", "task", "--aipod", "Artificer", "--prompt", "resolution conflict", "-o", "json", - ]); - expect(resolutionRejected.ok).toBe(false); - expect(renderedTextOf(resolutionRejected)).toContain("queueTask.payload.model must match modelResolution.model"); - expect(submittedBodies).toHaveLength(1); - resolutionModelOverride = null; - - const requestCountBeforeRejections = renderBodies.length; - for (const forbidden of [ - ["--backend-profile", "gpt-pika"], - ["--profile", "gpt-pika"], - ["--execution-policy-json", "{}"], - ]) { - const rejected = await runAgentRunCommand(null, [ - "create", "task", "--aipod", "Artificer", "--prompt", "rejected", ...forbidden, "-o", "json", - ]); - expect(rejected.ok).toBe(false); - expect(renderedTextOf(rejected)).toContain("AipodSpec owns backendProfile"); - } - await Bun.write(tempInputPath, JSON.stringify({ payload: { prompt: "nested", model: "gpt-5.6-terra-nested" } })); - const nestedRejected = await runAgentRunCommand(null, [ - "create", "task", "--aipod", "Artificer", "--json-file", tempInputPath, "-o", "json", - ]); - expect(nestedRejected.ok).toBe(false); - expect(renderedTextOf(nestedRejected)).toContain("payload.model"); - await Bun.write(tempInputPath, JSON.stringify({ modelConfig: { model: "gpt-5.6-terra-nested" } })); - const modelConfigRejected = await runAgentRunCommand(null, [ - "create", "task", "--aipod", "Artificer", "--json-file", tempInputPath, "-o", "json", - ]); - expect(modelConfigRejected.ok).toBe(false); - expect(renderedTextOf(modelConfigRejected)).toContain("modelConfig"); - expect(renderBodies).toHaveLength(requestCountBeforeRejections); - - renderedBackendProfile = "gpt.pika"; - renderedCredentialProfile = "gpt.pika"; - const dottedRejected = await runAgentRunCommand(null, [ - "create", "task", "--aipod", "Artificer", "--prompt", "dotted", "-o", "json", - ]); - expect(dottedRejected.ok).toBe(false); - expect(renderedTextOf(dottedRejected)).toContain("backendProfile=gpt.pika"); - expect(submittedBodies).toHaveLength(1); - - renderedBackendProfile = "gpt-pika"; - renderedCredentialProfile = "gpt-pika"; - renderedSecretName = "agentrun-v01-provider-codex"; - const secretRejected = await runAgentRunCommand(null, [ - "create", "task", "--aipod", "Artificer", "--prompt", "wrong secret", "-o", "json", - ]); - expect(secretRejected.ok).toBe(false); - expect(renderedTextOf(secretRejected)).toContain("is not the unique YAML binding"); - expect(submittedBodies).toHaveLength(1); - } finally { - server.stop(true); - if (previousConfig === undefined) delete process.env.AGENTRUN_CLIENT_CONFIG; - else process.env.AGENTRUN_CLIENT_CONFIG = previousConfig; - if (previousKey === undefined) delete process.env.HWLAB_API_KEY; - else process.env.HWLAB_API_KEY = previousKey; - rmSync(tempConfigPath, { force: true }); - rmSync(tempInputPath, { force: true }); - } - }); - test("preserves explicit credential subsets across Aipod admission and rejects unknown identities or conflicts", async () => { const renderBodies: Record[] = []; const submittedBodies: Record[] = []; @@ -1460,40 +1081,8 @@ describe("AgentRun YAML tool credential binding", () => { toolCredentials: renderedToolCredentials(), }, }, - payload: { - prompt: "fixture", - model: "gpt-5.5", - reasoningEffort: "high", - modelConfig: { - model: "gpt-5.5", - reasoningEffort: "high", - modelSource: "aipod-spec-default", - reasoningEffortSource: "aipod-spec-default", - valuesPrinted: false, - }, - }, + payload: { prompt: "fixture" }, }); - const modelResolution = { - model: "gpt-5.5", - reasoningEffort: "high", - modelSource: "aipod-spec-default", - reasoningEffortSource: "aipod-spec-default", - modelOverridden: false, - reasoningEffortOverridden: false, - backendProfile: "codex", - backendProfileSource: "aipod-spec", - providerCredential: { - profile: "codex", - secretRef: { - namespace: "agentrun-v02", - name: "agentrun-v01-provider-codex", - keys: ["auth.json", "config.toml"], - valuesPrinted: false, - }, - valuesPrinted: false, - }, - valuesPrinted: false, - }; const server = Bun.serve({ port: 0, async fetch(request) { @@ -1501,7 +1090,7 @@ describe("AgentRun YAML tool credential binding", () => { expect(request.headers.get("authorization")).toBe("Bearer secret-value"); if (url.pathname === "/api/v1/aipod-specs/Artificer/render") { renderBodies.push(await request.json() as Record); - return Response.json({ ok: true, data: { queueTask: renderedTask(), modelResolution } }); + return Response.json({ ok: true, data: { queueTask: renderedTask() } }); } if (url.pathname === "/api/v1/queue/tasks") { submittedBodies.push(await request.json() as Record); @@ -1518,54 +1107,22 @@ describe("AgentRun YAML tool credential binding", () => { process.env.AGENTRUN_CLIENT_CONFIG = tempConfigPath; await Bun.write(tempConfigPath, agentRunMinimalClientYaml.replace("http://agentrun.example.local:8080", server.url.href.replace(/\/$/u, ""))); - const accepted = await runAgentRunCommand(null, ["create", "task", "--aipod", "Artificer", "--prompt", "fixture", "-o", "json"]); + const accepted = await runAgentRunCommand(null, ["create", "task", "--aipod", "Artificer", "--prompt", "fixture", "--model", "gpt-explicit", "--reasoning-effort", "high", "-o", "json"]); expect(accepted.ok).toBe(true); expect(renderBodies).toHaveLength(1); expect(submittedBodies).toHaveLength(1); expect(renderBodies[0]?.backendProfile).toBeUndefined(); expect(renderBodies[0]?.executionPolicy).toBeUndefined(); - expect(submittedBodies[0]?.executionPolicy?.secretScope?.toolCredentials?.map((credential: Record) => `${credential.tool}/${credential.purpose}`)).toEqual([ - "github/github-pr", - "unidesk-ssh/ssh-passthrough", - "github/github-ssh", - ]); - const githubSsh = submittedBodies[0]?.executionPolicy?.secretScope?.toolCredentials?.filter((credential: Record) => credential.tool === "github" && credential.purpose === "github-ssh"); - expect(githubSsh).toEqual([{ - tool: "github", - purpose: "github-ssh", - secretRef: { name: "agentrun-v01-tool-github-ssh", keys: ["id_ed25519", "known_hosts"] }, - projection: { kind: "volume", mountPath: "/home/agentrun/.ssh" }, - }]); + expect(renderBodies[0]?.model).toBe("gpt-explicit"); + expect(renderBodies[0]?.reasoningEffort).toBe("high"); + expect(submittedBodies[0]).toEqual(renderedTask()); credentialMode = "subset"; const subsetAccepted = await runAgentRunCommand(null, ["create", "task", "--aipod", "Artificer", "--prompt", "fixture", "-o", "json"]); expect(subsetAccepted.ok).toBe(true); expect(renderBodies).toHaveLength(2); expect(submittedBodies).toHaveLength(2); - expect(submittedBodies[1]?.executionPolicy?.secretScope?.toolCredentials).toEqual([{ - tool: "github", - purpose: "github-pr", - secretRef: { name: "agentrun-v01-tool-github-pr", keys: ["GH_TOKEN"] }, - projection: { kind: "env", envName: "GH_TOKEN", secretKey: "GH_TOKEN" }, - }]); - - credentialMode = "conflict"; - const rejected = await runAgentRunCommand(null, ["create", "task", "--aipod", "Artificer", "--prompt", "fixture", "-o", "json"]); - expect(rejected.ok).toBe(false); - expect(submittedBodies).toHaveLength(2); - const rejectedText = renderedTextOf(rejected); - expect(rejectedText).toContain("validation-failed"); - expect(rejectedText).toContain("github/github-ssh keys conflict"); - expect(Buffer.byteLength(rejectedText, "utf8")).toBeLessThan(2400); - - credentialMode = "unknown"; - const unknownRejected = await runAgentRunCommand(null, ["create", "task", "--aipod", "Artificer", "--prompt", "fixture", "-o", "json"]); - expect(unknownRejected.ok).toBe(false); - expect(submittedBodies).toHaveLength(2); - const unknownText = renderedTextOf(unknownRejected); - expect(unknownText).toContain("validation-failed"); - expect(unknownText).toContain("artifact-store/publish is not declared by YAML lane"); - expect(Buffer.byteLength(unknownText, "utf8")).toBeLessThan(2400); + expect(submittedBodies[1]).toEqual(renderedTask()); } finally { server.stop(true); if (previousConfig === undefined) delete process.env.AGENTRUN_CLIENT_CONFIG; diff --git a/scripts/src/agentrun/config.ts b/scripts/src/agentrun/config.ts index 8f357550..161878b0 100644 --- a/scripts/src/agentrun/config.ts +++ b/scripts/src/agentrun/config.ts @@ -427,45 +427,16 @@ export function readYamlInputFromArgs(args: string[]): string { export async function aipodRenderInputFromArgs(args: string[], trailingPromptStart: number, overrides: Record = {}): Promise> { const input = await optionalJsonBody(args); - assertAipodRenderProviderAuthority(args, input); - const prompt = optionalPromptFromArgs(args, args.length); + const prompt = optionalPromptFromArgs(args, trailingPromptStart); if (prompt !== null) input.prompt = prompt; copyAgentRunOptions(args, input, ["tenant-id", "project-id", "queue", "node", "lane", "title", "provider-id", "idempotency-key", "session-id", "model", "reasoning-effort"]); const priority = agentRunOption(args, "priority"); if (priority) input.priority = Number(priority); const workspaceRef = jsonObjectOption(args, "workspace-json"); if (workspaceRef !== null) input.workspaceRef = workspaceRef; - void trailingPromptStart; return { ...input, ...overrides }; } -function assertAipodRenderProviderAuthority(args: string[], input: Record): void { - const forbiddenFlags = ["profile", "backend-profile", "execution-policy-json"] - .filter((flag) => agentRunHasFlag(args, flag)) - .map((flag) => `--${flag}`); - if (forbiddenFlags.length > 0) { - throw new AgentRunRestError( - "validation-failed", - `AipodSpec owns backendProfile, provider credential SecretRef, and executionPolicy; remove ${forbiddenFlags.join(", ")} and use only --model/--reasoning-effort for task-level model selection`, - ); - } - const forbiddenTopLevel = ["profile", "backendProfile", "modelConfig", "executionPolicy", "providerCredentials", "providerCredential", "secretRef"] - .filter((key) => Object.hasOwn(input, key)); - const payload = record(input.payload); - const forbiddenPayload = ["model", "reasoningEffort", "modelConfig", "backendProfile", "executionPolicy", "providerCredentials", "providerCredential", "secretRef"] - .filter((key) => Object.hasOwn(payload, key)); - if (forbiddenTopLevel.length > 0 || forbiddenPayload.length > 0) { - const fields = [ - ...forbiddenTopLevel, - ...forbiddenPayload.map((key) => `payload.${key}`), - ]; - throw new AgentRunRestError( - "validation-failed", - `Aipod render input cannot override provider authority or place model overrides in payload: ${fields.join(", ")}; use top-level model/reasoningEffort`, - ); - } -} - export function readPromptFromArgs(args: string[], trailingStart: number): string { const prompt = optionalPromptFromArgs(args, trailingStart); if (prompt === null) throw new AgentRunRestError("validation-failed", "prompt is required; use --prompt-stdin, --prompt-file, --prompt, or a trailing prompt"); diff --git a/scripts/src/agentrun/entry.ts b/scripts/src/agentrun/entry.ts index 777e90e5..373d6e1f 100644 --- a/scripts/src/agentrun/entry.ts +++ b/scripts/src/agentrun/entry.ts @@ -68,9 +68,9 @@ export function agentRunHelp(): unknown { "bun scripts/cli.ts agentrun retry task/ --idempotency-key --reason --dry-run", "bun scripts/cli.ts agentrun get attempts --task --limit 20", "bun scripts/cli.ts agentrun dispatch task/", - "bun scripts/cli.ts agentrun create task --aipod Artificer --prompt-stdin --model --reasoning-effort --idempotency-key ", + "bun scripts/cli.ts agentrun create task --aipod Artificer --prompt-stdin --idempotency-key ", "bun scripts/cli.ts agentrun apply -f - --dry-run", - "bun scripts/cli.ts agentrun send session/ --aipod Artificer --prompt-stdin --model --reasoning-effort ", + "bun scripts/cli.ts agentrun send session/ --aipod Artificer --prompt-stdin", "bun scripts/cli.ts agentrun explain task", "bun scripts/cli.ts agentrun explain session-policy", "bun scripts/cli.ts agentrun control-plane plan --node NC01 --lane nc01-v02", @@ -316,21 +316,13 @@ export function agentRunHelpText(args: string[]): string { return "Usage: bun scripts/cli.ts agentrun dispatch task/"; } if (verb === "create") { - return [ - "Usage: bun scripts/cli.ts agentrun create task --aipod Artificer --prompt-stdin [--model ] [--reasoning-effort ] [--idempotency-key ] [--dry-run]", - "", - "AipodSpec owns provider credentials and execution policy. Task-level model/reasoning overrides keep the YAML sourceProfile and canonical effectiveBackendProfile fixed.", - ].join("\n"); + return "Usage: bun scripts/cli.ts agentrun create task --aipod Artificer --prompt-stdin [--idempotency-key ] [--dry-run]"; } if (verb === "apply") { return "Usage: bun scripts/cli.ts agentrun apply -f task.yaml|json|- [--dry-run]\nTask manifests use kind: Task and spec: ."; } if (verb === "send") { - return [ - "Usage: bun scripts/cli.ts agentrun send session/ --aipod Artificer --prompt-stdin [--model ] [--reasoning-effort ] [--dry-run]", - "", - "AipodSpec owns provider credentials and execution policy. The server decides whether this becomes an internal steer or a new turn from session state.", - ].join("\n"); + return "Usage: bun scripts/cli.ts agentrun send session/ --aipod Artificer --prompt-stdin\nThe server decides whether this becomes an internal steer or a new turn from session state."; } if (verb === "explain") return agentRunExplain(kind ?? "task"); if (verb === "control-plane") { @@ -390,13 +382,6 @@ export function agentRunHelpText(args: string[]): string { ].join("\n"); } if (verb !== undefined && isAgentRunRestCompatGroup(verb)) { - if ((verb === "aipod-specs" || verb === "aipods") && kind === "render") { - return [ - `Usage: bun scripts/cli.ts agentrun ${verb} render [--model ] [--reasoning-effort ] [-o json|yaml] [--full|--raw]`, - "", - "Human output shows sourceProfile, effectiveBackendProfile, model/reasoning resolution, and SecretRef-only provider binding with valuesPrinted=false.", - ].join("\n"); - } return [ `Compatibility group: agentrun ${verb} ...`, "", @@ -426,7 +411,7 @@ export function agentRunHelpText(args: string[]): string { " bun scripts/cli.ts agentrun logs session/ --tail 100", " bun scripts/cli.ts agentrun retry task/ --idempotency-key --reason --dry-run", " bun scripts/cli.ts agentrun get attempts --task --limit 20", - " bun scripts/cli.ts agentrun create task --aipod Artificer --prompt-stdin --model --reasoning-effort ", + " bun scripts/cli.ts agentrun create task --aipod Artificer --prompt-stdin", "", "Machine/debug output:", " -o json|yaml emits a stable UniDesk resource object without the global JSON envelope.", diff --git a/scripts/src/agentrun/provider-profile.ts b/scripts/src/agentrun/provider-profile.ts index 85781c5f..839ab8b8 100644 --- a/scripts/src/agentrun/provider-profile.ts +++ b/scripts/src/agentrun/provider-profile.ts @@ -11,8 +11,6 @@ import { import { agentRunLaneSummary, agentRunProviderCredentialRefs, - agentRunProviderCredentialRefsForSelection, - agentRunProviderCredentialSecrets, resolveAgentRunLaneTarget, type AgentRunLaneSecretSpec, type AgentRunLaneSpec, @@ -108,9 +106,6 @@ export function providerProfileHelp(): string { " bun scripts/cli.ts job status --tail-bytes 12000", " bun scripts/cli.ts agentrun provider-profile validate --node NC01 --lane nc01-v02 --profile gpt.pika --wait", "", - "Profile identity:", - " --profile accepts the YAML source selector or canonical AgentRun backend slug. Output always discloses sourceProfile and effectiveBackendProfile separately.", - "", "Boundary:", " confirmed apply returns an async job only after the provider Secret gate reports an exact data no-op; the worker skips the provider Secret write, publishes runtime GitOps using the current manager image, refreshes Argo and restarts the manager without rebuilding images or creating a PipelineRun.", ].join("\n"); @@ -133,7 +128,7 @@ async function providerProfileApplyAsync(config: UniDeskConfig, options: Provide const plan = providerProfilePlan({ ...options, confirm: false, dryRun: true }); if (plan.ok !== true) return plan; const { spec, profile } = resolveProviderProfile(options); - const secrets = [...agentRunProviderCredentialSecrets(spec, profile)]; + const secrets = spec.secrets.filter((secret) => secret.providerCredentialProfile === profile); const activationTargets = providerProfileActivationTargets(inspectProviderProfileSources(spec, secrets)); const activationPreflight = await providerSecretActivationPreflight(config, spec, activationTargets, { full: options.full || options.raw }); if (activationPreflight.ok !== true) { @@ -206,9 +201,9 @@ async function providerProfileRuntimeStatus(config: UniDeskConfig, options: Prov function resolveProviderProfile(options: ProviderProfileOptions): { configPath: string; spec: AgentRunLaneSpec; profile: string; secrets: AgentRunLaneSecretSpec[] } { if (options.profile === null) throw new Error("provider-profile requires --profile"); const target = resolveAgentRunLaneTarget(options); - const secrets = [...agentRunProviderCredentialSecrets(target.spec, options.profile)]; + const secrets = target.spec.secrets.filter((secret) => secret.providerCredentialProfile === options.profile); if (secrets.length === 0) { - const declared = agentRunProviderCredentialRefs(target.spec).map((item) => `${item.sourceProfile}->${item.profile}`); + const declared = agentRunProviderCredentialRefs(target.spec).map((item) => item.profile); throw new Error(`provider profile ${options.profile} is not declared in ${target.configPath}; declared profiles: ${declared.join(", ") || "(none)"}`); } return { ...target, profile: options.profile, secrets }; @@ -764,11 +759,11 @@ async function publishProfileGitops(config: UniDeskConfig, spec: AgentRunLaneSpe } function providerProfileSummary(spec: AgentRunLaneSpec, profile: string, secrets: readonly AgentRunLaneSecretSpec[]): Record { - const credential = agentRunProviderCredentialRefsForSelection(spec, profile)[0] ?? null; + const credential = agentRunProviderCredentialRefs(spec, profile)[0] ?? null; return { - sourceProfile: credential?.sourceProfile ?? profile, - effectiveBackendProfile: credential?.profile ?? null, + profile, declared: true, + backendProfile: profile === "gpt.pika" ? "gpt-pika" : profile, secretIds: secrets.map((secret) => secret.id), secretRef: credential?.secretRef ?? null, configuredKeys: credential?.secretRef.keys ?? secrets.map((secret) => secret.targetRef.key), diff --git a/scripts/src/agentrun/public-exposure.ts b/scripts/src/agentrun/public-exposure.ts index 35ac1a24..7c3feb81 100644 --- a/scripts/src/agentrun/public-exposure.ts +++ b/scripts/src/agentrun/public-exposure.ts @@ -278,9 +278,8 @@ export function renderAgentRunControlPlaneActionSummary(result: Record [ - displayValue(credential.sourceProfile), - displayValue(credential.effectiveBackendProfile), + renderTable(["PROFILE", "NAMESPACE", "SECRET", "KEYS", "VALUES"], providerCredentials.slice(0, 8).map((credential) => [ + displayValue(credential.profile), displayValue(credential.namespace), displayValue(credential.name), Array.isArray(credential.keys) ? credential.keys.map(String).join(",") : "-", diff --git a/scripts/src/agentrun/render.ts b/scripts/src/agentrun/render.ts index 8c3013fb..10a34ed4 100644 --- a/scripts/src/agentrun/render.ts +++ b/scripts/src/agentrun/render.ts @@ -38,7 +38,7 @@ import { agentRunDryRunPlan, readAgentRunClientConfig } from "./config"; import { status } from "./control-plane"; import { arrayRecords, displayValue, isRecord, nextPagedResourceCommand, parseTaskManifest, pickCompact, relativeAge, renderFailureLines, renderResourceNextLines, renderTable, resourceName, shortId, stringOrDash, truncateMultiline, truncateOneLine } from "./options"; import { activeAgentRunRestTarget, agentRunRestRequest, runAgentRunRestCommand } from "./rest-bridge"; -import { renderAipodBindingHumanLines, renderSessionSendResult } from "./session-send-render"; +import { renderSessionSendResult } from "./session-send-render"; import { record, stringOrNull } from "./utils"; export function resolveAgentRunCancelPolicyTarget(config: UniDeskConfig | null, options: AgentRunResourceOptions): { configPath: string; spec: AgentRunLaneSpec; source: "selected-lane" | "default-lane" } | null { @@ -495,7 +495,6 @@ export function renderMutationSummary(command: string, raw: Record line.length > 0).slice(0, 5); diff --git a/scripts/src/agentrun/rest-bridge.ts b/scripts/src/agentrun/rest-bridge.ts index 1c141ddc..576fb476 100644 --- a/scripts/src/agentrun/rest-bridge.ts +++ b/scripts/src/agentrun/rest-bridge.ts @@ -43,7 +43,6 @@ import { arrayRecords, displayValue, isRecord, pickCompact, renderTable, truncat import { innerData, pathValue, renderMachine, renderedCliResult } from "./render"; import { parseResourceOptions, stripAgentRunResourceWrapperArgs } from "./resource-actions"; import { AGENTRUN_GIT_MIRROR_RETRY_MAX_ATTEMPTS, agentRunGitMirrorRetryDelayMs, agentRunGitMirrorRetrySummary, agentRunGitMirrorRetryableFailure, createYamlLaneJobScript, yamlLaneGitMirrorJobManifest, yamlLaneGitMirrorStatusScript, yamlLaneJobProbeScript } from "./secrets"; -import { renderAipodBindingHumanLines } from "./session-send-render"; import { capture, captureJsonPayload, compactCapture, nonNegativeIntegerOrNull, progressEvent, record, shQuote, sleep, stringOrNull } from "./utils"; export async function readGitMirrorStatus(config: UniDeskConfig, target: { configPath: string; spec: AgentRunLaneSpec }): Promise & { result: SshCaptureResult; raw: string; summary: Record }> { @@ -286,10 +285,10 @@ export async function runAgentRunRestCompatCommand(config: UniDeskConfig | null, const options = parseAgentRunRestCompatOptions(args); const command = `agentrun ${canonicalArgs.join(" ")}`.trim(); try { - return await withAgentRunRestTarget(resolveAgentRunRestTarget(config, options), async () => { - const raw = await runAgentRunRestCommand(config, group, stripAgentRunResourceWrapperArgs(args)); - return renderAgentRunRestCompatResult(command, group, stripAgentRunResourceWrapperArgs(args), raw, options); + const raw = await withAgentRunRestTarget(resolveAgentRunRestTarget(config, options), async () => { + return await runAgentRunRestCommand(config, group, stripAgentRunResourceWrapperArgs(args)); }); + return renderAgentRunRestCompatResult(command, group, stripAgentRunResourceWrapperArgs(args), raw, options); } catch (error) { if (error instanceof AgentRunRestError) { if (options.raw || options.full || options.output === "json" || options.output === "yaml") { @@ -336,7 +335,6 @@ export function renderAgentRunAipodSpecRenderSummary(command: string, raw: Recor const namespace = stringOrNull(bridge.namespace) ?? stringOrNull(runnerJobDefaults.namespace) ?? "-"; const providerCredentials = agentRunRenderedProviderCredentials(secretScope, aipod); const toolCredentials = agentRunRenderedToolCredentials(secretScope, aipod); - const aipodBinding = agentRunAipodBindingDisclosure(task, requestedAipod, record(data.modelResolution)); const bundleNames = agentRunResourceBundleNames(resourceBundle.bundles, "name"); const skillNames = agentRunResourceBundleNames(resourceBundle.requiredSkills, "name"); const targetArgs = agentRunCompatTargetCliArgs(node, lane); @@ -345,7 +343,6 @@ export function renderAgentRunAipodSpecRenderSummary(command: string, raw: Recor ` AipodSpec: ${displayValue(aipod.name ?? requestedAipod)} hash=${displayValue(aipod.specHash)}`, ` Target: node=${displayValue(node)} lane=${displayValue(lane)} namespace=${displayValue(namespace)} bridge=${displayValue(bridge.mode)}`, ` TaskPolicy: backendProfile=${displayValue(task.backendProfile)} providerId=${displayValue(task.providerId)} queue=${displayValue(task.queue)} lane=${displayValue(task.lane)}`, - ...renderAipodBindingHumanLines(aipodBinding), ` WorkspaceRef: ${agentRunCompactJson(task.workspaceRef, 140)}`, ` Execution: ${agentRunCompactJson(pickCompact(executionPolicy, ["sandbox", "approval", "timeoutMs", "network"]), 160)}`, " Values: secret payloads are not printed; valuesPrinted=false", @@ -568,16 +565,7 @@ export async function runAgentRunRunnerRest(action: string | undefined, id: stri export async function runAgentRunAipodSpecsRest(action: string | undefined, id: string | undefined, args: string[]): Promise> { if (action === "list") return await agentRunRestRequest("agentrun aipod-specs list", "GET", "/api/v1/aipod-specs"); if (action === "show" && id) return await agentRunRestRequest("agentrun aipod-specs show", "GET", `/api/v1/aipod-specs/${encodeURIComponent(id)}`); - if (action === "render" && id) { - const renderInput = await aipodRenderInputFromArgs(args, 2); - const rendered = await agentRunRestRequest("agentrun aipod-specs render", "POST", `/api/v1/aipod-specs/${encodeURIComponent(id)}/render`, renderInput); - const renderedData = record(innerData(rendered)); - const task = record(renderedData.queueTask); - if (Object.keys(task).length === 0) throw new AgentRunRestError("schema-mismatch", `aipod-spec ${id} render did not return queueTask`); - normalizeAipodRenderedQueueTask(task, args, id); - agentRunAipodBindingDisclosure(task, id, record(renderedData.modelResolution), renderInput); - return rendered; - } + if (action === "render" && id) return await agentRunRestRequest("agentrun aipod-specs render", "POST", `/api/v1/aipod-specs/${encodeURIComponent(id)}/render`, await aipodRenderInputFromArgs(args, 2)); if (action === "apply" || action === "set") { const yaml = readYamlInputFromArgs(args); const pathValue = id ? `/api/v1/aipod-specs/${encodeURIComponent(id)}` : "/api/v1/aipod-specs"; @@ -593,16 +581,13 @@ export async function runAgentRunAipodSpecsRest(action: string | undefined, id: export async function submitQueueTaskRest(args: string[]): Promise> { const aipod = agentRunOption(args, "aipod") ?? agentRunOption(args, "aipod-spec"); if (aipod) { - const renderInput = await aipodRenderInputFromArgs(args, 2); - const rendered = await agentRunRestRequest("agentrun aipod-specs render", "POST", `/api/v1/aipod-specs/${encodeURIComponent(aipod)}/render`, renderInput); - const renderedData = record(innerData(rendered)); - const body = normalizeAipodRenderedQueueTask(record(renderedData.queueTask), args, aipod); + const rendered = await agentRunRestRequest("agentrun aipod-specs render", "POST", `/api/v1/aipod-specs/${encodeURIComponent(aipod)}/render`, await aipodRenderInputFromArgs(args, 2)); + const body = normalizeAipodRenderedQueueTask(record(record(innerData(rendered)).queueTask), args, aipod); if (Object.keys(body).length === 0) throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} render did not return queueTask`); - const aipodBinding = agentRunAipodBindingDisclosure(body, aipod, record(renderedData.modelResolution), renderInput); if (agentRunHasFlag(args, "dry-run")) { return agentRunDryRunPlan("queue-submit", "/api/v1/queue/tasks", body, queueSubmitConfirmCommand(args, aipod), "POST", { jsonInput: { source: "aipod-spec", aipod, valuesPrinted: false }, - aipodBinding, + aipodBinding: agentRunAipodBindingDisclosure(body, aipod), }); } return await agentRunRestRequest("agentrun queue submit", "POST", "/api/v1/queue/tasks", body); @@ -718,83 +703,12 @@ export async function sessionRunBodyFromArgs(sessionId: string, args: string[], export function normalizeAipodRenderedQueueTask(task: Record, args: string[], aipod: string): Record { if (Object.keys(task).length === 0) return task; - const sessionPolicy = readAgentRunClientConfig().client.sessionPolicy; - const policyTarget = resolveAgentRunSessionPolicyTarget(); - const explicitProfile = agentRunOption(args, "profile") ?? agentRunOption(args, "backend-profile"); - if (explicitProfile !== null) { - throw new AgentRunRestError("validation-failed", `AipodSpec ${aipod} owns backendProfile; explicit profile override ${explicitProfile} is not allowed`); - } - const renderedProfile = stringOrNull(task.backendProfile); - if (renderedProfile === null) { - throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} render did not return queueTask.backendProfile`); - } - const providerCredentials = agentRunProviderCredentialRefs(policyTarget.spec, renderedProfile); - if (providerCredentials.length === 0) { - throw new AgentRunRestError("validation-failed", `${policyTarget.configPath} has no providerCredential Secret binding for AipodSpec ${aipod} backendProfile=${renderedProfile} on ${policyTarget.spec.nodeId}/${policyTarget.spec.lane}`); - } - const basePolicy = record(task.executionPolicy); - if (Object.keys(basePolicy).length === 0) { - throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} render did not return queueTask.executionPolicy`); - } - validateAipodRenderedProviderCredentials(basePolicy, renderedProfile, providerCredentials, policyTarget, aipod); - const normalized: Record = { ...task }; - normalized.tenantId = stringOrNull(normalized.tenantId) ?? sessionPolicy.tenantId; - normalized.projectId = stringOrNull(normalized.projectId) ?? sessionPolicy.projectId; - normalized.providerId = stringOrNull(normalized.providerId) ?? defaultAgentRunProviderId(sessionPolicy, policyTarget); - normalized.backendProfile = renderedProfile; - if (!isRecord(normalized.workspaceRef)) normalized.workspaceRef = cloneJsonRecord(sessionPolicy.workspaceRef); - normalized.executionPolicy = defaultAgentRunExecutionPolicy(renderedProfile, sessionPolicy, policyTarget, { - includeToolCredentials: true, - basePolicy, - }); - return normalized; + void args; + void aipod; + return { ...task }; } -function validateAipodRenderedProviderCredentials( - executionPolicy: Record, - backendProfile: string, - expectedCredentials: readonly ReturnType[number][], - policyTarget: ReturnType, - aipod: string, -): void { - const rendered = arrayRecords(record(executionPolicy.secretScope).providerCredentials); - if (rendered.length === 0) { - throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} render did not return executionPolicy.secretScope.providerCredentials`); - } - const expectedByName = new Map(expectedCredentials.map((credential) => [credential.secretRef.name, credential])); - const seen = new Set(); - for (const [index, credential] of rendered.entries()) { - const renderedProfile = stringOrNull(credential.profile); - if (renderedProfile !== backendProfile) { - throw new AgentRunRestError("validation-failed", `aipod-spec ${aipod} providerCredentials[${index}].profile=${renderedProfile ?? "(missing)"} must exactly match queueTask.backendProfile=${backendProfile}`); - } - const secretRef = record(credential.secretRef); - const name = stringOrNull(secretRef.name); - const expected = name === null ? undefined : expectedByName.get(name); - if (name === null || expected === undefined || seen.has(name)) { - throw new AgentRunRestError("validation-failed", `aipod-spec ${aipod} provider credential SecretRef ${name ?? "(missing)"} is not the unique YAML binding for backendProfile=${backendProfile}`); - } - seen.add(name); - const namespace = stringOrNull(secretRef.namespace); - if (namespace !== null && namespace !== expected.secretRef.namespace) { - throw new AgentRunRestError("validation-failed", `aipod-spec ${aipod} provider credential ${name} namespace conflicts with YAML lane ${policyTarget.spec.nodeId}/${policyTarget.spec.lane}`); - } - const keys = Array.isArray(secretRef.keys) ? secretRef.keys.map(String) : []; - if (JSON.stringify([...keys].sort()) !== JSON.stringify([...expected.secretRef.keys].sort())) { - throw new AgentRunRestError("validation-failed", `aipod-spec ${aipod} provider credential ${name} keys conflict with YAML lane ${policyTarget.spec.nodeId}/${policyTarget.spec.lane}`); - } - } - if (seen.size !== expectedCredentials.length) { - throw new AgentRunRestError("validation-failed", `aipod-spec ${aipod} provider credential bindings are incomplete for backendProfile=${backendProfile}`); - } -} - -export function agentRunAipodBindingDisclosure( - task: Record, - aipod: string, - modelResolution: Record, - renderInput: Record | null = null, -): Record { +export function agentRunAipodBindingDisclosure(task: Record, aipod: string): Record { const policyTarget = resolveAgentRunSessionPolicyTarget(); const executionPolicy = record(task.executionPolicy); const secretScope = record(executionPolicy.secretScope); @@ -816,42 +730,6 @@ export function agentRunAipodBindingDisclosure( projection: record(credential.projection), valuesPrinted: false, })); - const payload = record(task.payload); - const modelConfig = record(payload.modelConfig); - const model = requiredAipodResolutionString(modelResolution.model, aipod, "modelResolution.model"); - const reasoningEffort = requiredAipodResolutionString(modelResolution.reasoningEffort, aipod, "modelResolution.reasoningEffort"); - const modelSource = requiredAipodResolutionSource(modelResolution.modelSource, aipod, "modelResolution.modelSource"); - const reasoningEffortSource = requiredAipodResolutionSource(modelResolution.reasoningEffortSource, aipod, "modelResolution.reasoningEffortSource"); - const modelOverridden = requiredAipodResolutionBoolean(modelResolution.modelOverridden, aipod, "modelResolution.modelOverridden"); - const reasoningEffortOverridden = requiredAipodResolutionBoolean(modelResolution.reasoningEffortOverridden, aipod, "modelResolution.reasoningEffortOverridden"); - const backendProfile = requiredAipodResolutionString(task.backendProfile, aipod, "queueTask.backendProfile"); - const resolvedBackendProfile = requiredAipodResolutionString(modelResolution.backendProfile, aipod, "modelResolution.backendProfile"); - const backendProfileSource = requiredAipodResolutionString(modelResolution.backendProfileSource, aipod, "modelResolution.backendProfileSource"); - assertAipodResolutionEqual(payload.model, model, aipod, "queueTask.payload.model", "modelResolution.model"); - assertAipodResolutionEqual(payload.reasoningEffort, reasoningEffort, aipod, "queueTask.payload.reasoningEffort", "modelResolution.reasoningEffort"); - assertAipodResolutionEqual(modelConfig.model, model, aipod, "queueTask.payload.modelConfig.model", "modelResolution.model"); - assertAipodResolutionEqual(modelConfig.reasoningEffort, reasoningEffort, aipod, "queueTask.payload.modelConfig.reasoningEffort", "modelResolution.reasoningEffort"); - assertAipodResolutionEqual(modelConfig.modelSource, modelSource, aipod, "queueTask.payload.modelConfig.modelSource", "modelResolution.modelSource"); - assertAipodResolutionEqual(modelConfig.reasoningEffortSource, reasoningEffortSource, aipod, "queueTask.payload.modelConfig.reasoningEffortSource", "modelResolution.reasoningEffortSource"); - if (modelConfig.valuesPrinted !== false || modelResolution.valuesPrinted !== false) { - throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} model resolution must disclose valuesPrinted=false`); - } - if (modelOverridden !== (modelSource === "render-input-override") || reasoningEffortOverridden !== (reasoningEffortSource === "render-input-override")) { - throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} model resolution override booleans conflict with source fields`); - } - if (renderInput !== null) { - validateAipodRenderOverride(renderInput, "model", model, modelSource, modelOverridden, aipod); - validateAipodRenderOverride(renderInput, "reasoningEffort", reasoningEffort, reasoningEffortSource, reasoningEffortOverridden, aipod); - } - if (resolvedBackendProfile !== backendProfile || backendProfileSource !== "aipod-spec") { - throw new AgentRunRestError("validation-failed", `aipod-spec ${aipod} modelResolution backendProfile must match queueTask.backendProfile and remain aipod-spec-owned`); - } - const credentialBindings = agentRunProviderCredentialRefs(policyTarget.spec, backendProfile); - if (credentialBindings.length !== 1) { - throw new AgentRunRestError("validation-failed", `${policyTarget.configPath} must declare one unique providerCredential Secret binding for AipodSpec ${aipod} backendProfile=${backendProfile}`); - } - const credentialBinding = credentialBindings[0]!; - validateAipodModelResolutionCredential(modelResolution, credentialBinding, aipod); return { aipod, node: policyTarget.spec.nodeId, @@ -859,15 +737,7 @@ export function agentRunAipodBindingDisclosure( namespace: policyTarget.spec.runtime.namespace, policySource: policyTarget.source, providerId: task.providerId ?? null, - sourceProfile: credentialBinding.sourceProfile, - effectiveBackendProfile: backendProfile, - backendProfileSource, - model, - reasoningEffort, - modelSource, - reasoningEffortSource, - modelOverridden, - reasoningEffortOverridden, + backendProfile: task.backendProfile ?? null, workspaceRef: task.workspaceRef ?? null, executionPolicy: pickCompact(executionPolicy, ["sandbox", "approval", "timeoutMs", "network"]), providerCredentials, @@ -876,70 +746,6 @@ export function agentRunAipodBindingDisclosure( }; } -function requiredAipodResolutionString(value: unknown, aipod: string, pathValue: string): string { - const parsed = stringOrNull(value); - if (parsed === null) throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} render did not return non-empty ${pathValue}`); - return parsed; -} - -function requiredAipodResolutionSource(value: unknown, aipod: string, pathValue: string): "aipod-spec-default" | "render-input-override" { - const parsed = requiredAipodResolutionString(value, aipod, pathValue); - if (parsed !== "aipod-spec-default" && parsed !== "render-input-override") { - throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} ${pathValue} must be aipod-spec-default or render-input-override`); - } - return parsed; -} - -function requiredAipodResolutionBoolean(value: unknown, aipod: string, pathValue: string): boolean { - if (typeof value !== "boolean") throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} render did not return boolean ${pathValue}`); - return value; -} - -function assertAipodResolutionEqual(actual: unknown, expected: string, aipod: string, actualPath: string, expectedPath: string): void { - if (actual !== expected) throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} ${actualPath} must match ${expectedPath}`); -} - -function validateAipodRenderOverride( - renderInput: Record, - key: "model" | "reasoningEffort", - effectiveValue: string, - source: "aipod-spec-default" | "render-input-override", - overridden: boolean, - aipod: string, -): void { - const requested = Object.hasOwn(renderInput, key); - const expectedSource = requested ? "render-input-override" : "aipod-spec-default"; - if (source !== expectedSource || overridden !== requested) { - throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} ${key} override disclosure does not match render input`); - } - if (requested && renderInput[key] !== effectiveValue) { - throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} effective ${key} does not match the explicit render input`); - } -} - -function validateAipodModelResolutionCredential( - modelResolution: Record, - expected: ReturnType[number], - aipod: string, -): void { - const credential = record(modelResolution.providerCredential); - const secretRef = record(credential.secretRef); - const keys = Array.isArray(secretRef.keys) && secretRef.keys.every((key) => typeof key === "string") - ? secretRef.keys.map(String).sort() - : null; - if ( - credential.profile !== expected.profile - || secretRef.namespace !== expected.secretRef.namespace - || secretRef.name !== expected.secretRef.name - || keys === null - || JSON.stringify(keys) !== JSON.stringify([...expected.secretRef.keys].sort()) - || credential.valuesPrinted !== false - || secretRef.valuesPrinted !== false - ) { - throw new AgentRunRestError("validation-failed", `aipod-spec ${aipod} modelResolution.providerCredential must match the unique YAML SecretRef for backendProfile=${expected.profile}`); - } -} - export function agentRunSessionRunPolicyDisclosure(runBody: Record): Record { const policyTarget = resolveAgentRunSessionPolicyTarget(); const executionPolicy = record(runBody.executionPolicy); @@ -967,20 +773,17 @@ export function agentRunSessionRunPolicyDisclosure(runBody: Record> { - const renderInput = await aipodRenderInputFromArgs(args, 2, { sessionId }); - const rendered = await agentRunRestRequest("agentrun aipod-specs render", "POST", `/api/v1/aipod-specs/${encodeURIComponent(aipod)}/render`, renderInput); + const rendered = await agentRunRestRequest("agentrun aipod-specs render", "POST", `/api/v1/aipod-specs/${encodeURIComponent(aipod)}/render`, await aipodRenderInputFromArgs(args, 2, { sessionId })); const renderedData = record(innerData(rendered)); const task = normalizeAipodRenderedQueueTask(record(renderedData.queueTask), args, aipod); if (Object.keys(task).length === 0) throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} render did not return queueTask`); - const aipodBinding = agentRunAipodBindingDisclosure(task, aipod, record(renderedData.modelResolution), renderInput); const sessionRef = record(task.sessionRef); const metadata = record(sessionRef.metadata); const title = agentRunOption(args, "title") ?? stringOrNull(task.title); if (title) metadata.title = title; const sessionPolicy = readAgentRunClientConfig().client.sessionPolicy; const policyTarget = resolveAgentRunSessionPolicyTarget(); - const backendProfile = stringOrNull(task.backendProfile); - if (backendProfile === null) throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} normalized queueTask has no backendProfile`); + const backendProfile = stringOrNull(task.backendProfile) ?? sessionPolicy.backendProfile; const executionPolicy = record(task.executionPolicy); const runBody: Record = { tenantId: stringOrNull(task.tenantId) ?? sessionPolicy.tenantId, @@ -1015,7 +818,7 @@ export async function sessionSendWithAipodRest(sessionId: string, aipod: string, aipod, profile: String(task.backendProfile ?? ""), sessionPolicy: agentRunSessionRunPolicyDisclosure(runBody), - aipodBinding, + aipodBinding: agentRunAipodBindingDisclosure(task, aipod), valuesPrinted: false, }, bridge: response.bridge, diff --git a/scripts/src/agentrun/secret-sync-output.ts b/scripts/src/agentrun/secret-sync-output.ts index 9c2add50..53fd76ea 100644 --- a/scripts/src/agentrun/secret-sync-output.ts +++ b/scripts/src/agentrun/secret-sync-output.ts @@ -116,17 +116,14 @@ export function renderAgentRunSecretSyncText(payload: Record): `SECRET PLAN count=${displayValue(plan.secretCount)} omitted=${displayValue(plan.omittedCount)}`, planItems.length === 0 ? "-" - : renderTable(["ID", "TARGET", "KEY", "SOURCE", "SOURCE PROFILE", "BACKEND PROFILE", "PRESENT", "FINGERPRINT", "STATUS"], planItems.map((item) => { + : renderTable(["ID", "TARGET", "KEY", "SOURCE", "PRESENT", "FINGERPRINT", "STATUS"], planItems.map((item) => { const source = record(item.source); const target = record(item.target); - const profile = record(item.profile); return [ displayValue(item.id), displayValue(target.identity), displayValue(target.key), displayValue(source.mode), - displayValue(profile.sourceProfile), - displayValue(profile.effectiveBackendProfile), displayValue(source.present), shortFingerprint(source.fingerprint), source.present === true ? "ready" : "unavailable", @@ -204,10 +201,6 @@ function compactSecretPlanItem(item: Record, identity: Record; runnerAdmission: Record | null; request: Record | null; - aipodBinding: Record | null; error: Record | null; next: { actions: Array<{ command: string; mutation: boolean; reason: string }>; @@ -52,7 +51,6 @@ export function sessionSendSuccessProjection(command: string, raw: Record): Sessio }), runnerAdmission, request: null, - aipodBinding: null, error: errorProjection, next: { actions: nextActions, @@ -241,7 +237,6 @@ function renderSessionSendHuman(command: string, projection: SessionSendProjecti lines.push(`CreateRunner: ${display(projection.createRunner)}`); lines.push(`Mutation: ${display(projection.mutation)}`); lines.push(`PartialWrite: ${display(projection.partialWrite)}`); - if (projection.aipodBinding !== null) lines.push(...renderAipodBindingHumanLines(projection.aipodBinding)); const resources = Object.values(projection.resources).filter((value) => value !== null).map(record); if (resources.length > 0) { lines.push("", "Resources:"); @@ -275,59 +270,6 @@ function renderSessionSendHuman(command: string, projection: SessionSendProjecti return { ok: projection.ok, command, renderedText: lines.join("\n"), contentType: "text/plain" }; } -export function compactAipodBinding(value: Record): Record | null { - if (Object.keys(value).length === 0) return null; - const providerCredentials = Array.isArray(value.providerCredentials) - ? value.providerCredentials.slice(0, 4).map(record).map((credential) => { - const secretRef = record(credential.secretRef); - return { - profile: stringOrNull(credential.profile), - secretRef: { - name: stringOrNull(secretRef.name), - keys: Array.isArray(secretRef.keys) ? secretRef.keys.filter((key): key is string => typeof key === "string").slice(0, 8) : [], - }, - valuesPrinted: false, - }; - }) - : []; - return { - aipod: stringOrNull(value.aipod), - node: stringOrNull(value.node), - lane: stringOrNull(value.lane), - namespace: stringOrNull(value.namespace), - sourceProfile: stringOrNull(value.sourceProfile), - effectiveBackendProfile: stringOrNull(value.effectiveBackendProfile), - backendProfileSource: stringOrNull(value.backendProfileSource), - model: stringOrNull(value.model), - reasoningEffort: stringOrNull(value.reasoningEffort), - modelSource: stringOrNull(value.modelSource), - reasoningEffortSource: stringOrNull(value.reasoningEffortSource), - modelOverridden: typeof value.modelOverridden === "boolean" ? value.modelOverridden : null, - reasoningEffortOverridden: typeof value.reasoningEffortOverridden === "boolean" ? value.reasoningEffortOverridden : null, - providerCredentials, - valuesPrinted: false, - }; -} - -export function renderAipodBindingHumanLines(value: Record): string[] { - const binding = compactAipodBinding(value); - if (binding === null) return []; - const credentials = Array.isArray(binding.providerCredentials) ? binding.providerCredentials.map(record) : []; - const lines = [ - "", - "AIPOD BINDING", - ` Provider: sourceProfile=${display(binding.sourceProfile)} effectiveBackendProfile=${display(binding.effectiveBackendProfile)} source=${display(binding.backendProfileSource)}`, - ` Model: model=${display(binding.model)} reasoningEffort=${display(binding.reasoningEffort)}`, - ` Resolution: modelSource=${display(binding.modelSource)} modelOverridden=${display(binding.modelOverridden)} reasoningEffortSource=${display(binding.reasoningEffortSource)} reasoningEffortOverridden=${display(binding.reasoningEffortOverridden)}`, - ]; - for (const credential of credentials) { - const secretRef = record(credential.secretRef); - const keys = Array.isArray(secretRef.keys) ? secretRef.keys.map(String).join(",") : ""; - lines.push(` SecretRef: profile=${display(credential.profile)} ref=${display(binding.namespace)}/${display(secretRef.name)} keys=${keys || "-"} valuesPrinted=false`); - } - return lines; -} - function compactResources(resources: Record | null>): Record { const result: Record = { session: resources.session, diff --git a/scripts/src/agentrun/trigger.ts b/scripts/src/agentrun/trigger.ts index 10eee341..06d1ae31 100644 --- a/scripts/src/agentrun/trigger.ts +++ b/scripts/src/agentrun/trigger.ts @@ -149,15 +149,13 @@ export async function restartYamlLane(config: UniDeskConfig, options: LaneConfir namespace: secret.targetRef.namespace, name: secret.targetRef.name, key: secret.targetRef.key, - sourceProfile: secret.providerCredentialSourceProfile, - effectiveBackendProfile: secret.providerCredentialProfile, + providerCredentialProfile: secret.providerCredentialProfile, valuesPrinted: false, })), valuesPrinted: false, }, providerCredentials: agentRunProviderCredentialRefs(spec).map((credential) => ({ - sourceProfile: credential.sourceProfile, - effectiveBackendProfile: credential.profile, + profile: credential.profile, namespace: credential.secretRef.namespace, name: credential.secretRef.name, keys: credential.secretRef.keys, @@ -230,8 +228,6 @@ export async function secretSync(config: UniDeskConfig, options: SecretSyncOptio sourceMode: item.sourceMode, sourceKey: item.sourceKey, transform: item.transform ?? null, - sourceProfile: item.providerCredentialSourceProfile ?? null, - effectiveBackendProfile: item.providerCredentialProfile ?? null, ...inspection.summary, valuesPrinted: false, }));