fix: unify agentrun session send cli

This commit is contained in:
Codex
2026-06-11 14:29:46 +00:00
parent f8dcdf4139
commit 3d0faf557e
5 changed files with 163 additions and 116 deletions
+141 -93
View File
@@ -39,7 +39,7 @@ const gitMirrorRepositories: readonly GitMirrorRepositorySpec[] = [
export function agentRunHelp(): unknown {
return {
command: "agentrun get|describe|events|logs|result|ack|cancel|dispatch|create|apply|steer|send|explain",
command: "agentrun get|describe|events|logs|result|ack|cancel|dispatch|create|apply|send|explain",
output: "human by default; use -o json|yaml or --raw for machine/debug output",
usage: [
"bun scripts/cli.ts agentrun get tasks --queue commander --limit 20",
@@ -54,7 +54,6 @@ export function agentRunHelp(): unknown {
"bun scripts/cli.ts agentrun dispatch task/<taskId>",
"bun scripts/cli.ts agentrun create task --aipod Artificer --prompt-stdin --idempotency-key <key>",
"bun scripts/cli.ts agentrun apply -f - --dry-run",
"bun scripts/cli.ts agentrun steer session/<sessionId> --prompt-stdin",
"bun scripts/cli.ts agentrun send session/<sessionId> --aipod Artificer --prompt-stdin",
"bun scripts/cli.ts agentrun explain task",
"bun scripts/cli.ts agentrun control-plane status",
@@ -78,7 +77,7 @@ export function agentRunHelp(): unknown {
],
resources: ["task/qt", "run", "command/cmd", "runnerjob/rjob", "session/ses", "aipodspec/aps"],
description: "Operate AgentRun v0.1 through Kubernetes-style resource verbs. Human output is compact by default; -o json|yaml returns the UniDesk render-only client schema, and --raw exposes the direct AgentRun REST envelope.",
legacyCompatibility: "queue/runs/commands/runner/sessions/aipod-specs remain as compatibility groups backed by direct HTTP; new commander work should use get/describe/events/logs/result/ack/cancel/dispatch/create/apply/steer/send.",
legacyCompatibility: "queue/runs/commands/runner/sessions/aipod-specs remain as compatibility groups backed by direct HTTP; new commander work should use get/describe/events/logs/result/ack/cancel/dispatch/create/apply/send. sessions turn/steer are removed; use send only.",
};
}
@@ -150,7 +149,6 @@ function isResourceVerb(value: string | undefined): value is AgentRunResourceVer
|| value === "dispatch"
|| value === "create"
|| value === "apply"
|| value === "steer"
|| value === "send"
|| value === "explain";
}
@@ -211,11 +209,8 @@ function agentRunHelpText(args: string[]): string {
if (verb === "apply") {
return "Usage: bun scripts/cli.ts agentrun apply -f task.yaml|json|- [--dry-run]\nTask manifests use kind: Task and spec: <AgentRun task payload>.";
}
if (verb === "steer") {
return "Usage: bun scripts/cli.ts agentrun steer session/<sessionId> --prompt-stdin";
}
if (verb === "send") {
return "Usage: bun scripts/cli.ts agentrun send session/<sessionId> --aipod Artificer --prompt-stdin";
return "Usage: bun scripts/cli.ts agentrun send session/<sessionId> --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") {
@@ -254,7 +249,7 @@ function agentRunHelpText(args: string[]): string {
return [
"Usage: bun scripts/cli.ts agentrun <verb> <resource> [options]",
"",
"Verbs: get, describe, events, logs, result, ack, cancel, dispatch, create, apply, steer, send, explain",
"Verbs: get, describe, events, logs, result, ack, cancel, dispatch, create, apply, send, explain",
"Resources: task/qt, run, command/cmd, runnerjob/rjob, session/ses, aipodspec/aps",
"",
"Common:",
@@ -299,8 +294,7 @@ async function runAgentRunResourceCommand(config: UniDeskConfig | null, verb: Ag
if (verb === "dispatch") return await resourceDispatch(config, command, action, bridgeActionArgs, options);
if (verb === "create") return await resourceCreate(config, command, action, bridgeActionArgs, options);
if (verb === "apply") return await resourceApply(config, command, bridgeActionArgs, options);
if (verb === "steer") return await resourceSessionPromptCommand(config, command, "steer", action, bridgeActionArgs, options);
if (verb === "send") return await resourceSessionPromptCommand(config, command, "turn", action, bridgeActionArgs, options);
if (verb === "send") return await resourceSessionPromptCommand(config, command, action, bridgeActionArgs, options);
} catch (error) {
if (error instanceof AgentRunRestError) return renderAgentRunRestError(command, error, options);
return renderedCliResult(false, command, `Error: ${error instanceof Error ? error.message : String(error)}`);
@@ -620,12 +614,12 @@ async function resourceApply(config: UniDeskConfig | null, command: string, args
return renderMutationSummary(command, result, options, `${options.dryRun ? "Dry-run applied" : "Applied"} task manifest`, options.dryRun ? [rerunWithoutDryRun(command)] : undefined);
}
async function resourceSessionPromptCommand(config: UniDeskConfig | null, command: string, officialAction: "steer" | "turn", action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise<RenderedCliResult> {
async function resourceSessionPromptCommand(config: UniDeskConfig | null, command: string, action: string | undefined, args: string[], options: AgentRunResourceOptions): Promise<RenderedCliResult> {
const ref = parseResourceRef(action, args, "session");
if (ref.kind !== "session") throw new Error(`${officialAction === "steer" ? "steer" : "send"} requires session/<sessionId>`);
const sessionArgs = [officialAction, ref.name, ...stripLeadingResource(args, ref.name)];
if (ref.kind !== "session") throw new Error("send requires session/<sessionId>");
const sessionArgs = ["send", ref.name, ...stripLeadingResource(args, ref.name)];
const result = await runAgentRunRestCommand(config, "sessions", sessionArgs);
return renderMutationSummary(command, result, options, officialAction === "steer" ? "Steer submitted" : "Session turn submitted");
return renderMutationSummary(command, result, options, options.dryRun ? "Session send plan" : "Session send submitted");
}
function renderedCliResult(ok: boolean, command: string, renderedText: string, contentType: RenderedCliResult["contentType"] = "text/plain"): RenderedCliResult {
@@ -675,7 +669,20 @@ function renderResultSummary(command: string, raw: Record<string, unknown>, opti
`State: ${displayValue(data.state ?? data.status ?? data.terminalStatus ?? "-")}`,
`OK: ${String(raw.ok !== false)}`,
];
const final = stringOrNull(data.finalResponse) ?? stringOrNull(data.output) ?? stringOrNull(data.summary) ?? stringOrNull(data.result);
const authority = stringOrNull(data.finalResponseAuthority);
const fallback = typeof data.finalResponseFallback === "boolean" ? data.finalResponseFallback : null;
const needsContinuation = typeof data.needsContinuation === "boolean" ? data.needsContinuation : null;
if (authority !== null) lines.push(`Authority: ${authority}`);
if (fallback !== null) lines.push(`Fallback: ${String(fallback)}`);
if (needsContinuation !== null) lines.push(`NeedsContinuation: ${String(needsContinuation)}`);
const finalRecord = record(data.finalResponse);
const final = stringOrNull(data.finalResponse)
?? stringOrNull(finalRecord.content)
?? stringOrNull(finalRecord.text)
?? stringOrNull(finalRecord.message)
?? stringOrNull(data.output)
?? stringOrNull(data.summary)
?? stringOrNull(data.result);
if (final !== null) lines.push("", truncateMultiline(final, options.fullText ? 8000 : 1600));
else {
const failure = renderFailureLines(data);
@@ -694,6 +701,12 @@ function renderMutationSummary(command: string, raw: Record<string, unknown>, op
`OK: ${String(raw.ok !== false)}`,
];
if (id !== null) lines.push(`Name: ${id}`);
const decision = stringOrNull(data.decision);
const internalCommandType = stringOrNull(data.internalCommandType);
if (data.dryRun !== undefined) lines.push(`DryRun: ${String(data.dryRun)}`);
if (data.mutation !== undefined) lines.push(`Mutation: ${String(data.mutation)}`);
if (decision !== null) lines.push(`Decision: ${decision}`);
if (internalCommandType !== null) lines.push(`InternalCommandType: ${internalCommandType}`);
const next = record(raw.next ?? data.next);
const nextLines = (overrideNextLines ?? Object.values(next).map(String)).filter((line) => line.length > 0).slice(0, 5);
if (nextLines.length > 0) lines.push("", "Next:", ...nextLines.map((line) => ` ${line}`));
@@ -3342,8 +3355,7 @@ async function runAgentRunSessionsRest(action: string | undefined, id: string |
if (agentRunHasFlag(args, "dry-run")) return agentRunDryRunPlan("session-cancel", `/api/v1/sessions/${encodeURIComponent(id)}/control`, body, `bun scripts/cli.ts agentrun sessions cancel ${id}`);
return await agentRunRestRequest("agentrun sessions cancel", "POST", `/api/v1/sessions/${encodeURIComponent(id)}/control`, body);
}
if (action === "steer" && id) return await sessionSteerRest(id, args);
if (action === "turn") return await sessionTurnRest(id ?? null, args);
if (action === "send" && id) return await sessionSendRest(id, args);
throw new AgentRunRestError("validation-failed", `unsupported sessions command: ${[action, id].filter(Boolean).join(" ") || "(empty)"}`);
}
@@ -3436,64 +3448,85 @@ async function mutateQueueTaskRest(action: string, taskId: string, suffix: strin
return await agentRunRestRequest(`agentrun queue ${suffix}`, "POST", pathValue, body);
}
async function sessionSteerRest(sessionId: string, args: string[]): Promise<Record<string, unknown>> {
const session = record(innerData(await agentRunRestRequest("agentrun sessions show", "GET", `/api/v1/sessions/${encodeURIComponent(sessionId)}${agentRunQuery(args, ["reader-id"])}`)));
const runId = stringOrNull(session.activeRunId) ?? stringOrNull(session.lastRunId);
if (runId === null) throw new AgentRunRestError("validation-failed", `session ${sessionId} has no run to steer`);
const prompt = readPromptFromArgs(args, 2);
const body: Record<string, unknown> = { type: "steer", payload: { prompt } };
const idempotencyKey = agentRunOption(args, "idempotency-key");
if (idempotencyKey) body.idempotencyKey = idempotencyKey;
const command = await agentRunRestRequest("agentrun sessions steer", "POST", `/api/v1/runs/${encodeURIComponent(runId)}/commands`, body);
return { ok: command.ok !== false, command: "agentrun sessions steer", data: { action: "session-steer", sessionId, runId, command: innerData(command) }, bridge: command.bridge };
async function sessionSendRest(sessionId: string, args: string[]): Promise<Record<string, unknown>> {
const aipod = agentRunOption(args, "aipod") ?? agentRunOption(args, "aipod-spec");
if (aipod) return await sessionSendWithAipodRest(sessionId, aipod, args);
const input = await optionalJsonBody(args);
const prompt = optionalPromptFromArgs(args, 2);
const sendBody = await sessionSendBodyFromRunInput(sessionId, args, input, sessionSendPayloadFromInput(input, prompt));
const response = await agentRunRestRequest("agentrun sessions send", "POST", `/api/v1/sessions/${encodeURIComponent(sessionId)}/send`, sendBody);
return { ok: response.ok !== false, command: "agentrun sessions send", data: innerData(response), bridge: response.bridge };
}
async function sessionTurnRest(positionalSessionId: string | null, args: string[]): Promise<Record<string, unknown>> {
const aipod = agentRunOption(args, "aipod") ?? agentRunOption(args, "aipod-spec");
if (aipod) return await sessionTurnWithAipodRest(positionalSessionId, aipod, args);
const sessionId = positionalSessionId ?? agentRunOption(args, "session-id") ?? newAgentRunSessionId();
const body = await optionalJsonBody(args);
const profile = agentRunOption(args, "profile") ?? agentRunOption(args, "backend-profile") ?? stringOrNull(body.backendProfile) ?? "codex";
const prompt = readPromptFromArgs(args, positionalSessionId ? 2 : 1);
body.tenantId = agentRunOption(args, "tenant-id") ?? stringOrNull(body.tenantId) ?? "unidesk";
body.projectId = agentRunOption(args, "project-id") ?? stringOrNull(body.projectId) ?? "default";
function sessionSendPayloadFromInput(input: Record<string, unknown>, prompt: string | null): Record<string, unknown> {
const payload = record(input.payload);
if (Object.keys(payload).length > 0) return payload;
const text = prompt ?? stringOrNull(input.prompt) ?? stringOrNull(input.message) ?? stringOrNull(input.text);
if (text === null) throw new AgentRunRestError("validation-failed", "send requires payload or non-empty prompt/message/text; use --prompt-stdin, --prompt-file, --prompt, a trailing prompt, or JSON payload");
return { prompt: text };
}
async function sessionSendBodyFromRunInput(sessionId: string, args: string[], input: Record<string, unknown>, payload: Record<string, unknown>): Promise<Record<string, unknown>> {
const fullSendBody = isExplicitSessionSendBody(input);
const runInput = fullSendBody ? record(input.run ?? input.runBase) : input;
const runBody = await sessionRunBodyFromArgs(sessionId, args, runInput);
const runnerDefaults = fullSendBody ? record(input.runnerJob) : {};
const runnerJob = await sessionRunnerJobBody(args, runnerDefaults);
const body: Record<string, unknown> = fullSendBody ? { ...input } : {};
body.run = runBody;
body.payload = record(input.payload);
if (Object.keys(record(body.payload)).length === 0) body.payload = payload;
body.createRunnerJob = agentRunHasFlag(args, "no-runner-job") ? false : input.createRunnerJob !== false;
body.runnerJob = runnerJob;
body.dryRun = agentRunHasFlag(args, "dry-run");
const commandIdempotencyKey = agentRunOption(args, "command-idempotency-key") ?? agentRunOption(args, "idempotency-key");
if (commandIdempotencyKey) body.commandIdempotencyKey = commandIdempotencyKey;
return body;
}
function isExplicitSessionSendBody(input: Record<string, unknown>): boolean {
return isRecord(input.run)
|| isRecord(input.runBase)
|| isRecord(input.payload)
|| isRecord(input.runnerJob)
|| input.createRunnerJob !== undefined
|| input.commandIdempotencyKey !== undefined;
}
async function sessionRunBodyFromArgs(sessionId: string, args: string[], input: Record<string, unknown>): Promise<Record<string, unknown>> {
const existing = await fetchAgentRunSessionOrNull(sessionId, args);
const profile = agentRunOption(args, "profile")
?? agentRunOption(args, "backend-profile")
?? stringOrNull(input.backendProfile)
?? stringOrNull(existing?.backendProfile)
?? "codex";
const body: Record<string, unknown> = { ...input };
body.tenantId = agentRunOption(args, "tenant-id") ?? stringOrNull(body.tenantId) ?? stringOrNull(existing?.tenantId) ?? "unidesk";
body.projectId = agentRunOption(args, "project-id") ?? stringOrNull(body.projectId) ?? stringOrNull(existing?.projectId) ?? "default";
body.providerId = agentRunOption(args, "provider-id") ?? stringOrNull(body.providerId) ?? "G14";
body.backendProfile = profile;
body.workspaceRef = jsonObjectOption(args, "workspace-json") ?? record(body.workspaceRef);
if (Object.keys(record(body.workspaceRef)).length === 0) body.workspaceRef = { kind: "opaque", path: "." };
body.executionPolicy = jsonObjectOption(args, "execution-policy-json") ?? record(body.executionPolicy);
if (Object.keys(record(body.executionPolicy)).length === 0) body.executionPolicy = defaultAgentRunExecutionPolicy(profile);
const sessionRef = record(body.sessionRef);
const metadata = record(sessionRef.metadata);
const inheritedSessionRef = existing === null ? {} : {
conversationId: existing.conversationId,
threadId: existing.threadId,
metadata: existing.metadata,
};
const sessionRef = { ...inheritedSessionRef, ...record(body.sessionRef) };
const metadata = { ...record(inheritedSessionRef.metadata), ...record(sessionRef.metadata) };
const title = agentRunOption(args, "title");
if (title) metadata.title = title;
body.sessionRef = { ...sessionRef, sessionId, metadata };
await ensureAgentRunSession(sessionId, body);
const run = await agentRunRestRequest("agentrun runs create", "POST", "/api/v1/runs", body);
const runData = record(innerData(run));
const runId = stringOrNull(runData.id) ?? requiredContext("session turn", "run id returned by AgentRun server");
const commandBody: Record<string, unknown> = { type: "turn", payload: { prompt } };
const commandIdempotencyKey = agentRunOption(args, "command-idempotency-key") ?? agentRunOption(args, "idempotency-key");
if (commandIdempotencyKey) commandBody.idempotencyKey = commandIdempotencyKey;
const command = await agentRunRestRequest("agentrun commands create", "POST", `/api/v1/runs/${encodeURIComponent(runId)}/commands`, commandBody);
const commandId = stringOrNull(record(innerData(command)).id);
let runnerJob: Record<string, unknown> | null = null;
if (!agentRunHasFlag(args, "no-runner-job") && commandId !== null) {
const runnerBody = await optionalRunnerJsonBody(args);
runnerBody.commandId = commandId;
copyAgentRunOptions(args, runnerBody, ["image", "namespace", "attempt-id", "runner-id", "source-commit", "service-account-name"]);
runnerJob = await agentRunRestRequest("agentrun runner job", "POST", `/api/v1/runs/${encodeURIComponent(runId)}/runner-jobs`, runnerBody);
}
return { ok: true, command: "agentrun sessions turn", data: { action: "session-turn", sessionId, profile, run: innerData(run), command: innerData(command), runnerJob: runnerJob ? innerData(runnerJob) : null, valuesPrinted: false }, bridge: runnerJob?.bridge ?? command.bridge ?? run.bridge };
return body;
}
async function sessionTurnWithAipodRest(positionalSessionId: string | null, aipod: string, args: string[]): Promise<Record<string, unknown>> {
const sessionId = positionalSessionId ?? agentRunOption(args, "session-id") ?? newAgentRunSessionId();
const rendered = await agentRunRestRequest("agentrun aipod-specs render", "POST", `/api/v1/aipod-specs/${encodeURIComponent(aipod)}/render`, await aipodRenderInputFromArgs(args, positionalSessionId ? 2 : 1, { sessionId }));
async function sessionSendWithAipodRest(sessionId: string, aipod: string, args: string[]): Promise<Record<string, unknown>> {
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 = record(renderedData.queueTask);
if (Object.keys(task).length === 0) throw new AgentRunRestError("schema-mismatch", `aipod-spec ${aipod} render did not return queueTask`);
await ensureAgentRunSession(sessionId, task);
const sessionRef = record(task.sessionRef);
const metadata = record(sessionRef.metadata);
const title = agentRunOption(args, "title") ?? stringOrNull(task.title);
@@ -3509,37 +3542,38 @@ async function sessionTurnWithAipodRest(positionalSessionId: string | null, aipo
resourceBundleRef: task.resourceBundleRef,
traceSink: { kind: "aipod-session", aipod, sessionId, valuesPrinted: false },
};
const run = await agentRunRestRequest("agentrun runs create", "POST", "/api/v1/runs", runBody);
const runId = stringOrNull(record(innerData(run)).id) ?? requiredContext("session turn", "run id returned by AgentRun server");
const commandBody: Record<string, unknown> = { type: "turn", payload: task.payload };
const runnerDefaults = record(record(renderedData.dispatchDefaults).runnerJob);
const sendBody: Record<string, unknown> = {
run: runBody,
payload: task.payload,
createRunnerJob: !agentRunHasFlag(args, "no-runner-job"),
runnerJob: await sessionRunnerJobBody(args, runnerDefaults),
dryRun: agentRunHasFlag(args, "dry-run"),
};
const commandIdempotencyKey = agentRunOption(args, "command-idempotency-key") ?? agentRunOption(args, "idempotency-key");
if (commandIdempotencyKey) commandBody.idempotencyKey = commandIdempotencyKey;
const command = await agentRunRestRequest("agentrun commands create", "POST", `/api/v1/runs/${encodeURIComponent(runId)}/commands`, commandBody);
const commandId = stringOrNull(record(innerData(command)).id);
let runnerJob: Record<string, unknown> | null = null;
if (!agentRunHasFlag(args, "no-runner-job") && commandId !== null) {
const runnerDefaults = record(record(renderedData.dispatchDefaults).runnerJob);
const runnerBody = { ...runnerDefaults, ...(await optionalRunnerJsonBody(args)), commandId };
copyAgentRunOptions(args, runnerBody, ["image", "namespace", "attempt-id", "runner-id", "source-commit", "service-account-name"]);
runnerJob = await agentRunRestRequest("agentrun runner job", "POST", `/api/v1/runs/${encodeURIComponent(runId)}/runner-jobs`, runnerBody);
}
return { ok: true, command: "agentrun sessions turn", data: { action: "session-turn", aipod, sessionId, profile: String(task.backendProfile ?? ""), run: innerData(run), command: innerData(command), runnerJob: runnerJob ? innerData(runnerJob) : null, valuesPrinted: false }, bridge: runnerJob?.bridge ?? command.bridge ?? run.bridge };
if (commandIdempotencyKey) sendBody.commandIdempotencyKey = commandIdempotencyKey;
const response = await agentRunRestRequest("agentrun sessions send", "POST", `/api/v1/sessions/${encodeURIComponent(sessionId)}/send`, sendBody);
const data = record(innerData(response));
return { ok: response.ok !== false, command: "agentrun sessions send", data: { ...data, aipod, profile: String(task.backendProfile ?? ""), valuesPrinted: false }, bridge: response.bridge };
}
async function ensureAgentRunSession(sessionId: string, source: Record<string, unknown>): Promise<void> {
async function fetchAgentRunSessionOrNull(sessionId: string, args: string[]): Promise<Record<string, unknown> | null> {
try {
await agentRunRestRequest("agentrun sessions storage", "GET", `/api/v1/sessions/${encodeURIComponent(sessionId)}/storage`);
return;
} catch {
// Missing storage is recovered by creating the session; other failures will surface on create.
return record(innerData(await agentRunRestRequest("agentrun sessions show", "GET", `/api/v1/sessions/${encodeURIComponent(sessionId)}${agentRunQuery(args, ["reader-id"])}`)));
} catch (error) {
if (error instanceof AgentRunRestError && error.httpStatus === 404) return null;
throw error;
}
await agentRunRestRequest("agentrun sessions create", "POST", "/api/v1/sessions", {
sessionId,
tenantId: source.tenantId ?? "unidesk",
projectId: source.projectId ?? "default",
backendProfile: source.backendProfile ?? "codex",
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
});
}
async function sessionRunnerJobBody(args: string[], defaults: Record<string, unknown> = {}): Promise<Record<string, unknown>> {
const runnerBody = { ...defaults, ...(await optionalRunnerJsonBody(args)) };
copyAgentRunOptions(args, runnerBody, ["image", "namespace", "attempt-id", "runner-id", "source-commit", "service-account-name"]);
const managerUrl = agentRunOption(args, "runner-manager-url");
if (managerUrl !== null) runnerBody.managerUrl = managerUrl;
const runnerIdempotencyKey = agentRunOption(args, "runner-idempotency-key");
if (runnerIdempotencyKey !== null) runnerBody.idempotencyKey = runnerIdempotencyKey;
return runnerBody;
}
async function agentRunRestRequest(command: string, method: AgentRunHttpMethod, pathValue: string, body?: unknown): Promise<Record<string, unknown>> {
@@ -3967,12 +4001,26 @@ function jsonInputDisclosureFromArgs(args: string[]): Record<string, unknown> {
};
}
function newAgentRunSessionId(): string {
return `sess_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
}
function defaultAgentRunExecutionPolicy(profile: string): Record<string, unknown> {
return { backendProfile: profile, valuesPrinted: false };
const keys = profile === "sub2api" || profile === "codex" ? ["auth.json", "config.toml"] : ["auth.json", "config.toml"];
return {
sandbox: "workspace-write",
approval: "never",
timeoutMs: 900000,
network: "enabled",
secretScope: {
allowCredentialEcho: false,
providerCredentials: [
{
profile,
secretRef: {
name: `agentrun-v01-provider-${profile}`,
keys,
},
},
],
},
};
}
function safeAgentRunEnvelope(envelope: Record<string, unknown>): Record<string, unknown> {
@@ -4046,7 +4094,7 @@ type AgentRunHttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
type AgentRunFailureKind = "auth-missing" | "auth-failed" | "agentrun-unreachable" | "schema-mismatch" | "unsupported-version" | "validation-failed";
type AgentRunRestCompatGroup = "queue" | "sessions" | "aipod-specs" | "aipods" | "runs" | "commands" | "runner";
type AgentRunResourceVerb = "get" | "describe" | "events" | "logs" | "result" | "ack" | "cancel" | "dispatch" | "create" | "apply" | "steer" | "send" | "explain";
type AgentRunResourceVerb = "get" | "describe" | "events" | "logs" | "result" | "ack" | "cancel" | "dispatch" | "create" | "apply" | "send" | "explain";
type AgentRunResourceKind = "task" | "run" | "command" | "runnerjob" | "session" | "aipodspec";
type AgentRunOutputMode = "human" | "wide" | "name" | "json" | "yaml";
@@ -4471,7 +4519,7 @@ function unsupported(args: string[]): RenderedCliResult {
`Error: unsupported AgentRun command: ${command}`,
"",
"Supported resource commands:",
" agentrun get|describe|events|logs|result|ack|cancel|dispatch|create|apply|steer|send",
" agentrun get|describe|events|logs|result|ack|cancel|dispatch|create|apply|send",
"",
"Compatibility bridge groups:",
" agentrun aipod-specs|queue|runs|commands|runner|sessions",
+6 -6
View File
@@ -57,14 +57,14 @@ export function rootHelp(): unknown {
{ command: "commander contract|plan --dry-run|smoke --dry-run|approval request --dry-run", description: "Host Codex commander skeleton contract, no-daemon smoke plan, and dry-run approval preview without live bridges or message sends." },
{ command: "hwlab nodes control-plane|git-mirror|secret --node G14 --lane v03", description: "Manage HWLAB node/lane runtime prerequisites for v0.3+ with the node identity passed as data instead of a command family." },
{ command: "hwlab g14 monitor-prs | hwlab g14 control-plane status|apply|trigger-current|runtime-migration|cleanup-runs|cleanup-released-pvs | hwlab g14 git-mirror status|apply|sync|flush | hwlab g14 tools-image status|build", description: "Start the legacy G14 PR monitor, run bounded v0.2 Tekton/Argo control-plane, manual PipelineRun trigger, runtime migration, CI workspace retention, manual devops-infra git mirror/relay maintenance, or fixed HWLAB CI tools image actions; long confirmed trigger/sync/flush actions return async jobs by default." },
{ command: "agentrun get|describe|events|logs|result|ack|cancel|dispatch|create|apply|steer|send|control-plane|git-mirror", description: "Use AgentRun v0.1 resource primitives with low-noise human output by default; legacy bridge groups remain available for raw compatibility." },
{ command: "agentrun get|describe|events|logs|result|ack|cancel|dispatch|create|apply|send|control-plane|git-mirror", description: "Use AgentRun v0.1 resource primitives with low-noise human output by default; session follow-up uses send only and the server decides internal steer vs turn." },
{ command: "platform-infra sub2api plan|apply|status|validate|codex-pool", description: "Deploy Sub2API in G14 platform-infra, manage the YAML-controlled Codex upstream pool, expose the unified API, and inspect marker sentinel state with low-noise reports without printing API keys." },
{ command: "hwlab cd audit --env dev | hwlab cd status --env dev | hwlab cd apply --env dev --dry-run", description: "Legacy D601 HWLAB DEV CD wrapper kept for explicit old-path diagnostics; current HWLAB rollout uses G14 GitOps." },
{ command: "code-agent-sandbox", description: "Independent Code Agent Sandbox service skeleton for adapter, mode, and credential-boundary diagnostics." },
{ command: "schedule list|get|runs|run|retry-run|delete", description: "Manage backend-core scheduled tasks and run history; schedule run <id> supports --wait-ms N and retry-run reuses the failed run's schedule." },
{ command: "schedule upsert-pgdata-backup [--time HH:MM] [--remote-base /SERVER_DATA/UNIDESK_PG_DATA]", description: "Create or update the daily PGDATA physical backup task that uploads monthly rotated archives to Baidu Netdisk." },
{ command: "codex deploy <commitId> [--provider-id D601] [--timeout-ms N]", description: "Disabled legacy Code Queue deploy path; use the dev-only artifact consumer instead." },
{ command: "codex submit|steer|resume|queue create|queue merge|move", description: "Frozen legacy Code Queue write commands; use agentrun create/apply/steer/send for new commander work. Historical codex task/tasks/output/read/unread/queues remain available for archive troubleshooting." },
{ command: "codex submit|steer|resume|queue create|queue merge|move", description: "Frozen legacy Code Queue write commands; use agentrun create/apply/send for new commander work. Historical codex task/tasks/output/read/unread/queues remain available for archive troubleshooting." },
{ command: "codex skills-sync --dry-run [--full]", description: "Inspect the controlled runner skills hostPath lifecycle contract without copying files, restarting services, reading secrets, or mutating live runner paths." },
{ command: "codex execution-plane [--full|--raw]", description: "Read-only D601 native k3s Code Queue execution-plane inspection; compares formal deployments, deprecated Compose residuals, commit markers, pod digest, and mounted worktree HEAD." },
{ command: "codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N] [--full|--raw]", description: "Read-only PR admission check with compact commander output by default; use --full or --raw to expand the full runtime preflight, tool, and observation payload." },
@@ -75,7 +75,7 @@ export function rootHelp(): unknown {
{ command: "codex read <taskId>", description: "Mark one reviewed terminal task read and return terminal metadata plus final response; prompt/tool logs stay behind drill-down commands." },
{ command: "codex dev-ready", description: "Fetch execution-container readiness, including sanitized skill injection status from /api/dev-ready." },
{ command: "codex judge <taskId> --attempt N [--dry-run] [--include-prompt]", description: "Replay one stored Code Queue attempt through the same judge context builder and MiniMax judge call path used by the live queue worker." },
{ command: "codex steer <taskId> / codex resume <taskId>", description: "Frozen legacy execution mutation entries; use agentrun steer/send/logs/events/result/ack/cancel against AgentRun resources instead." },
{ command: "codex steer <taskId> / codex resume <taskId>", description: "Frozen legacy execution mutation entries; use agentrun send/logs/events/result/ack/cancel against AgentRun resources instead." },
{ command: "codex steer-confirm <taskId> --steer-id <id> [--raw]", description: "Read-only lookup for a steerId in task trace so deliveryUnconfirmed can be resolved without resending the corrective prompt." },
{ command: "codex interrupt|cancel <taskId>", description: "Request interrupt for a running Code Queue task, or cancel a queued/retry_wait task, through the same private proxy." },
{ command: "codex queues [--full|--all] [--limit N] [--page N|--offset N]", description: "Read legacy Code Queue archive summaries. Legacy queue create/merge and move are frozen; use agentrun create/apply/get/cancel for new work." },
@@ -391,7 +391,7 @@ function codexHelp(): unknown {
"bun scripts/cli.ts codex execution-plane [--full|--raw]",
"bun scripts/cli.ts codex pr-preflight [--remote] [--push-dry-run --push-dry-run-ref refs/heads/probe/<name>] [--pr-create-dry-run --pr-create-dry-run-head <head>] [--issue N] [--full|--raw]",
"bun scripts/cli.ts codex judge <taskId> --attempt N [--dry-run] [--include-prompt]",
"bun scripts/cli.ts agentrun steer session/<sessionId> --prompt-stdin",
"bun scripts/cli.ts agentrun send session/<sessionId> --prompt-stdin",
"bun scripts/cli.ts codex steer <taskId> # frozen legacy write entry",
"bun scripts/cli.ts codex resume <taskId> # frozen legacy write entry",
"bun scripts/cli.ts codex steer-confirm <taskId> --steer-id <id> [--raw]",
@@ -463,7 +463,7 @@ function codexHelp(): unknown {
redline: "data.supervisor.activeRunning.redline names the count field, routine target, burst redline, hard redline, and decisionReady flag.",
limitSemantics: "filters.requestedLimit preserves the user input; filters.limit/effectiveLimit shows the capped query budget; section outputBudget/rowPage show returned-row caps.",
},
description: "Operate legacy Code Queue as a read-only archive through bounded task/output/read/unread/queues views. New task dispatch, retry/resume, steer, queue mutation, move, and workdir mutation are frozen and replaced by AgentRun resource primitives via bun scripts/cli.ts agentrun get|describe|events|logs|result|ack|cancel|dispatch|create|apply|steer|send.",
description: "Operate legacy Code Queue as a read-only archive through bounded task/output/read/unread/queues views. New task dispatch, retry/resume, queue mutation, move, and workdir mutation are frozen and replaced by AgentRun resource primitives via bun scripts/cli.ts agentrun get|describe|events|logs|result|ack|cancel|dispatch|create|apply|send.",
};
}
@@ -578,7 +578,7 @@ function artifactRegistryHelp(): unknown {
function agentRunHelpSummary(): unknown {
return {
command: "agentrun get|describe|events|logs|result|ack|cancel|dispatch|create|apply|steer|send|control-plane|git-mirror",
command: "agentrun get|describe|events|logs|result|ack|cancel|dispatch|create|apply|send|control-plane|git-mirror",
output: "human by default; use -o json|yaml or --raw",
usage: [
"bun scripts/cli.ts agentrun get tasks --queue commander --limit 20",