feat: support live inherited auth scenarios

This commit is contained in:
Shakker 2026-05-01 17:39:07 +01:00
parent 203982cb43
commit 3b805dbc3a
No known key found for this signature in database
3 changed files with 105 additions and 4 deletions

View File

@ -0,0 +1,62 @@
{
"id": "dashboard-session-send-turn-existing-user",
"surface": "dashboard-session-send-turn",
"title": "Dashboard Session Send Existing User Turn",
"objective": "Clone an existing OpenClaw env, move the clone to the target runtime, send a dashboard-style user message through Gateway sessions.send, and measure response latency without mutating the durable source env.",
"tags": ["agent", "message", "dashboard", "sessions", "gateway", "providers", "existing-user", "live"],
"timeoutMs": 240000,
"agent": {
"expectedText": "KOVA_AGENT_OK"
},
"thresholds": {
"upgradeMs": 120000,
"gatewayReadyMs": 45000,
"agentTurnMs": 45000,
"preProviderMs": 10000,
"providerFinalMs": 3000,
"preProviderDominanceRatio": 0.8,
"statusMs": 10000,
"peakRssMb": 900,
"missingDependencyErrors": 0,
"pluginLoadFailures": 0
},
"phases": [
{
"id": "clone",
"title": "Clone Existing Env",
"intent": "Clone a durable existing user env into a disposable Kova env before runtime or message testing.",
"commands": ["ocm env clone {sourceEnv} {env} --json"],
"evidence": ["source env", "clone root", "cloned OpenClaw config"]
},
{
"id": "upgrade",
"title": "Move Clone To Target Runtime",
"intent": "Run the real OCM upgrade path so the cloned user state runs the requested OpenClaw target runtime.",
"commands": ["ocm upgrade {env} {upgradeSelector} --json", "ocm service status {env} --json"],
"evidence": ["upgrade JSON", "runtime binding", "post-upgrade service state"]
},
{
"id": "gateway-start",
"title": "Start Gateway",
"intent": "Start the cloned gateway after upgrade and wait for readiness before sending a dashboard-style message.",
"commands": ["ocm service install {env} --json", "ocm service start {env} --json"],
"evidence": ["gateway service installed", "gateway service started", "startup readiness"]
},
{
"id": "dashboard-session-turn",
"title": "Dashboard Session Message",
"intent": "Exercise Gateway sessions.send on cloned user state and verify final assistant text appears in chat history.",
"commands": [
"node {kovaRoot}/support/run-dashboard-session-send-turn.mjs --env {env} --session-key kova-dashboard-existing-user --message 'Reply with exact ASCII text KOVA_AGENT_OK only.' --expected-text KOVA_AGENT_OK --timeout 120000"
],
"evidence": ["sessions.create timing", "sessions.send timing", "time to assistant text", "provider timing", "gateway health after turn", "role resource samples"]
},
{
"id": "post-dashboard-health",
"title": "Post-Dashboard Gateway Health",
"intent": "Verify the cloned gateway remains responsive after the dashboard-style turn and collect logs for embedded-run/liveness evidence.",
"commands": ["ocm @{env} -- status", "ocm logs {env} --tail 300 --raw"],
"evidence": ["gateway status", "embedded-run traces", "liveness warnings", "plugin errors", "memory after dashboard turn"]
}
]
}

View File

@ -77,6 +77,31 @@ export async function resolveRunAuthContext(flags = {}) {
const store = await loadCredentialStore();
const live = await verifyLiveCredentialStatus(liveCredentialStatus(store));
if (requestedMode === "live" && !live.available && flags.source_env) {
const inheritedLive = {
available: true,
providerId: null,
method: "source-env",
externalCli: null,
fallbackFrom: null,
fallbackPolicy: null,
envVars: [],
reason: `inherited from cloned source env ${flags.source_env}`,
verification: {
checked: false,
status: "inherited-source-env",
reason: "Kova will clone the source env and preserve its OpenClaw auth/config state"
}
};
return {
schemaVersion: "kova.auth.context.v1",
requestedMode,
credentialStore: credentialStoreSummary(store),
liveEnv: store.liveEnv,
live: inheritedLive,
redactionValues: secretValues(store.liveEnv)
};
}
if (requestedMode === "live" && !live.available) {
throw new Error(`--auth live requires configured live credentials: ${live.reason}`);
}
@ -129,8 +154,8 @@ export function scenarioAuthPolicy(context, scenario, state) {
externalCli: live.externalCli ?? null,
fallbackFrom: live.fallbackFrom ?? null,
fallbackPolicy: live.fallbackPolicy ?? null,
setup: true,
setupKind: liveAuthSetupKind(live),
setup: live.method !== "source-env",
setupKind: live.method === "source-env" ? "source-env-inherited" : liveAuthSetupKind(live),
commandEnv: env,
redactionValues: [...(context.auth?.redactionValues ?? []), ...secretValues(env)],
summary: authDisplay({
@ -140,8 +165,8 @@ export function scenarioAuthPolicy(context, scenario, state) {
externalCli: live.externalCli ?? null,
fallbackFrom: live.fallbackFrom ?? null,
fallbackPolicy: live.fallbackPolicy ?? null,
setup: true,
setupKind: liveAuthSetupKind(live),
setup: live.method !== "source-env",
setupKind: live.method === "source-env" ? "source-env-inherited" : liveAuthSetupKind(live),
envVars: live.envVars
})
};

View File

@ -186,6 +186,20 @@ export async function runSelfCheck(flags = {}) {
throw new Error(`missing auth override should not inject auth phases: ${phaseIds.join(", ")}`);
}
}));
checks.push(await jsonCommandCheck("run-auth-live-source-env-json", `node bin/kova.mjs run --auth live --target runtime:stable --scenario dashboard-session-send-turn-existing-user --source-env 'Team Env' --report-dir ${quoteShell(tmp)} --json`, async (data) => {
const report = JSON.parse(await readFile(data.jsonPath, "utf8"));
const record = report.records?.[0];
assertEqual(record?.auth?.mode, "live", "source-env live auth mode");
assertEqual(record?.auth?.source, "source-env", "source-env live auth source");
assertEqual(record?.auth?.setup, false, "source-env live auth does not patch config");
const phaseIds = record?.phases?.map((phase) => phase.id) ?? [];
if (phaseIds.includes("auth-setup") || phaseIds.includes("auth-prepare")) {
throw new Error(`source-env live auth should not inject auth phases: ${phaseIds.join(", ")}`);
}
const commands = record?.phases?.flatMap((phase) => phase.commands ?? []) ?? [];
assertEqual(commands.some((command) => command.includes("ocm env clone 'Team Env'")), true, "source env clone command present");
assertEqual(commands.some((command) => command.includes("run-dashboard-session-send-turn.mjs")), true, "dashboard session helper command present");
}));
for (const item of [
["agent-gateway-rpc-turn", "agent-gateway-rpc-turn", "ocm @"],
["dashboard-session-send-turn", "dashboard-session-send-turn", "run-dashboard-session-send-turn.mjs"],