code-agent: inherit child profile via env

This commit is contained in:
Codex Agent
2026-06-08 08:18:13 +08:00
parent 7408ba9da7
commit 6d1b1fd7ea
5 changed files with 43 additions and 11 deletions
@@ -297,7 +297,7 @@ export async function submitAgentRunChatTurn({ params = {}, options = {}, traceI
});
let runnerJob = null;
try {
const runnerJobInput = buildAgentRunRunnerJobInput({ env, traceId, commandId, ownerApiKey, toolCapabilities });
const runnerJobInput = buildAgentRunRunnerJobInput({ env, traceId, commandId, ownerApiKey, toolCapabilities, backendProfile });
runnerJob = await agentRunJson(fetchImpl, managerUrl, `/api/v1/runs/${encodeURIComponent(reusable.mapping.runId)}/runner-jobs`, {
method: "POST",
body: runnerJobInput,
@@ -395,7 +395,7 @@ export async function submitAgentRunChatTurn({ params = {}, options = {}, traceI
message: "AgentRun command " + commandId + " created; hwlab-cloud-api will start a runner Job explicitly without relying on scheduler automation.",
runId, commandId, backendProfile, waitingFor: "agentrun-runner-job-create", valuesPrinted: false,
});
const runnerJobInput = buildAgentRunRunnerJobInput({ env, traceId, commandId, ownerApiKey, toolCapabilities });
const runnerJobInput = buildAgentRunRunnerJobInput({ env, traceId, commandId, ownerApiKey, toolCapabilities, backendProfile });
try {
runnerJob = await agentRunJson(fetchImpl, managerUrl, "/api/v1/runs/" + encodeURIComponent(runId) + "/runner-jobs", { method: "POST", body: runnerJobInput, timeoutMs });
} catch (error) {
@@ -933,8 +933,8 @@ async function resolveOwnerApiKey({ params, options, now }) {
if (!key) return "";
return text(key.displaySecret ?? "");
}
function buildAgentRunRunnerJobInput({ env, traceId, commandId, ownerApiKey, toolCapabilities = null }) {
const baseTransient = buildAgentRunTransientEnv(env);
function buildAgentRunRunnerJobInput({ env, traceId, commandId, ownerApiKey, toolCapabilities = null, backendProfile }) {
const baseTransient = buildAgentRunTransientEnv(env, { providerProfile: backendProfile, parentTraceId: traceId });
const hwpodAllowed = toolCapabilityAllowed(toolCapabilities, "hwpod");
const transientEnv = ownerApiKey && hwpodAllowed
? baseTransient.concat([{ name: "HWLAB_API_KEY", value: ownerApiKey, sensitive: true }])
@@ -953,7 +953,9 @@ function buildAgentRunRunnerJobInput({ env, traceId, commandId, ownerApiKey, too
};
}
function buildAgentRunTransientEnv(env = process.env) {
function buildAgentRunTransientEnv(env = process.env, options = {}) {
const providerProfile = firstNonEmpty(options.providerProfile);
const parentTraceId = firstNonEmpty(options.parentTraceId);
const entries = [];
for (const name of [
"HWLAB_RUNTIME_API_URL",
@@ -967,6 +969,8 @@ function buildAgentRunTransientEnv(env = process.env) {
const value = firstNonEmpty(env[name]);
if (value) entries.push({ name, value, sensitive: false });
}
if (providerProfile) entries.push({ name: "HWLAB_CODE_AGENT_PROVIDER_PROFILE", value: providerProfile, sensitive: false });
if (parentTraceId) entries.push({ name: "HWLAB_CODE_AGENT_PARENT_TRACE_ID", value: parentTraceId, sensitive: false });
return entries;
}
+12 -1
View File
@@ -26,7 +26,9 @@ bun scripts/hwlab-code-agent-cli.ts result <traceId>
bun scripts/hwlab-code-agent-cli.ts trace <traceId> [--full]
```
输出均为 JSON。`spawn` 创建 session + 提交 prompt,立即返回 `sessionId` + `traceId``poll` 阻塞轮询至 terminal 或超时
`spawn` 的 profile 解析只认两种来源:显式 `--profile`,或环境变量 `HWLAB_CODE_AGENT_PROVIDER_PROFILE`。未提供时直接返回 `provider_profile_required`,不再静默回落 `deepseek`
输出均为 JSON。`spawn` 创建 session + 提交 prompt,立即返回 `sessionId` + `traceId`,并暴露 `resolvedProviderProfile``profileSource``parentTraceId``poll` 阻塞轮询至 terminal 或超时。
### hwpod spec 自动继承
@@ -45,6 +47,15 @@ caserun 注入 .hwlab/hwpod-spec.yaml 到 Leader workspace
-> Coder/Reviewer 拿到相同 spec
```
provider profile 继承链:
```
外层 AgentRun runner env 注入 HWLAB_CODE_AGENT_PROVIDER_PROFILE=当前 providerProfile
-> Leader 调用 hwlab-code-agent spawn 且未显式传 --profile
-> spawn 从同一环境变量继承 profile
-> 子 agent 与父 agent 使用同一个 providerProfile
```
---
## 外部管理 CLIG14 workspace 透传)
-1
View File
@@ -1,6 +1,5 @@
{
"apiBaseUrl": "http://74.48.78.17:19666",
"defaultProviderProfile": "deepseek",
"defaultProjectId": "prj_hwpod_workbench",
"defaultPollTimeoutMs": 300000
}
@@ -67,7 +67,7 @@ async function main() {
process.stderr.write(JSON.stringify({
ok: false,
usage: {
spawn: "bun scripts/hwlab-code-agent-cli.ts spawn --message '...' [--profile deepseek]",
spawn: "HWLAB_CODE_AGENT_PROVIDER_PROFILE=PROFILE bun scripts/hwlab-code-agent-cli.ts spawn --message '...' [--profile PROFILE]",
poll: "bun scripts/hwlab-code-agent-cli.ts poll <traceId> [--timeout 600000]",
result: "bun scripts/hwlab-code-agent-cli.ts result <traceId>",
trace: "bun scripts/hwlab-code-agent-cli.ts trace <traceId> [--full]"
+21 -3
View File
@@ -13,11 +13,13 @@ const configPath = path.join(SKILL_ROOT, "config.json");
interface Config {
apiBaseUrl: string;
defaultProviderProfile: string;
defaultProjectId: string;
defaultPollTimeoutMs: number;
}
const INHERITED_PROVIDER_PROFILE_ENV = "HWLAB_CODE_AGENT_PROVIDER_PROFILE";
const PARENT_TRACE_ID_ENV = "HWLAB_CODE_AGENT_PARENT_TRACE_ID";
let _cfg: Config | null = null;
function config(): Config {
if (!_cfg) {
@@ -27,11 +29,22 @@ function config(): Config {
throw new Error(`hwlab-code-agent: failed to load config from ${configPath}`);
}
if (!_cfg.apiBaseUrl) throw new Error("hwlab-code-agent: config.apiBaseUrl is required");
if (!_cfg.defaultProviderProfile) throw new Error("hwlab-code-agent: config.defaultProviderProfile is required");
}
return _cfg;
}
function textValue(value: string | undefined): string {
return String(value ?? "").trim();
}
function resolveProviderProfile(args: Record<string, string | undefined>) {
const explicit = textValue(args.profile || args.providerProfile);
if (explicit) return { profile: explicit, source: "explicit" };
const inherited = textValue(process.env[INHERITED_PROVIDER_PROFILE_ENV]);
if (inherited) return { profile: inherited, source: "env" };
fail("provider_profile_required", `spawn requires --profile or ${INHERITED_PROVIDER_PROFILE_ENV}`);
}
// ---- workspace files auto-inheritance ----
type WorkspaceFile = { path: string; content: string; encoding: "utf8" };
@@ -97,7 +110,8 @@ export interface SpawnResult {
export async function spawn(args: Record<string, string | undefined>): Promise<void> {
const cfg = config();
const profile = args.profile || cfg.defaultProviderProfile;
const resolvedProfile = resolveProviderProfile(args);
const profile = resolvedProfile.profile;
const projectId = args.projectId || cfg.defaultProjectId;
const message = args.message || (args.messageFile ? readFileSync(args.messageFile, "utf-8") : null);
if (!message) fail("message_required", "--message or --message-file required");
@@ -130,6 +144,10 @@ export async function spawn(args: Record<string, string | undefined>): Promise<v
out({
ok: true, action: "spawn",
sessionId, traceId, conversationId,
providerProfile: profile,
resolvedProviderProfile: profile,
profileSource: resolvedProfile.source,
parentTraceId: textValue(process.env[PARENT_TRACE_ID_ENV]) || null,
accepted: r.ok,
acceptedBody: b,
pollCommand: `bun scripts/hwlab-code-agent-cli.ts poll ${traceId}`,