Skip to content

Commit 5fd7da5

Browse files
authored
Merge branch 'main' into codex/qoder-environment-setup-script
2 parents 10fc902 + 4d74bcc commit 5fd7da5

16 files changed

Lines changed: 95 additions & 10 deletions

File tree

bun.lock

Lines changed: 8 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/guides/deploy-to-qoder.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,23 @@ providers:
2727

2828
A `deployment run` on Qoder creates a native Deployment Run and associated Session. Cron schedules run server-side.
2929

30+
## Runtime environment variables
31+
32+
Declare `environment_variables` on an Agent to inject variables into its Qoder runtime:
33+
34+
```yaml
35+
agents:
36+
assistant:
37+
model: { qoder: auto }
38+
instructions: Help the user.
39+
environment: dev
40+
environment_variables:
41+
FEATURE_FLAG: "on"
42+
LOG_LEVEL: debug
43+
```
44+
45+
OpenAgentPack maps this to each Qoder API's native shape: a top-level object on Forward Templates, `config.environment_variables` on Forward Sessions, and the required `KEY=VALUE;...` string on managed Sessions. Other providers reject this Qoder-specific field during validation.
46+
3047
## Tool naming
3148

3249
Qoder uses PascalCase tool names natively (`Read`, `Glob`, `Grep`, `WebFetch`, `WebSearch`, `Write`, `Edit`, `Bash`). Write tools **lowercase** in config and OpenAgentPack converts them automatically when applying to Qoder — this keeps the same config portable to Bailian, Claude, and Volcengine Ark.

docs/reference/configuration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ agents:
252252
skills: [ <string> | { type, skill_id, version? } ]
253253
vault: <string>
254254
memory_stores: [ <string> ]
255+
environment_variables: { <key>: <string> } # Qoder only
255256
resources: [ SessionResource ]
256257
multiagent: { type: "coordinator", agents: [...] }
257258
metadata: { <key>: <string> }
@@ -272,6 +273,7 @@ agents:
272273
| `skills[]` | string \| AgentSkillRef | no | Skill name or `{ type: "official"\|"custom", skill_id, version? }`. |
273274
| `vault` | string | no | Vault name. |
274275
| `memory_stores` | string[] | no | Bound memory stores. |
276+
| `environment_variables` | map<string,string> | no | Qoder runtime variables. Managed Sessions use Qoder's `KEY=VALUE;...` wire format; Forward Templates store the map as defaults and Forward Sessions send it under `config.environment_variables`. |
275277
| `resources` | SessionResource[] | no | Resources attached to every managed Session created for the Agent. |
276278
| `multiagent.type` | `"coordinator"` | no | Declare a coordinator agent. |
277279
| `multiagent.agents` | string[] | yes (with multiagent) | Agents it orchestrates. |

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,16 @@
6767
"@changesets/cli": "^2.31.0",
6868
"@types/bun": "^1.3.14",
6969
"dependency-cruiser": "^17.4.3",
70-
"js-yaml": "4.3.0",
70+
"js-yaml": "4.3.1",
7171
"tsup": "^8.5.1",
7272
"typescript": "^6.0.3"
7373
},
7474
"overrides": {
75-
"brace-expansion": "5.0.8",
75+
"brace-expansion": "5.0.9",
7676
"esbuild": "0.28.1",
7777
"fast-equals": "5.3.3",
78-
"js-yaml": "4.3.0",
78+
"js-yaml": "4.3.1",
79+
"nanoid": "3.3.17",
7980
"postcss": "8.5.23"
8081
},
8182
"trustedDependencies": [

packages/sdk/src/internal/core/session-runtime.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ export async function createSessionForAgent(
154154
resources: options.resources,
155155
title: options.title,
156156
metadata: options.metadata,
157+
environmentVariables: options.environmentVariables,
157158
});
158159
const session = await adapter.createSession(bindings);
159160
return { agentName, provider, session };
@@ -178,6 +179,7 @@ export async function startSessionRun(
178179
resources: options.resources,
179180
title: options.title,
180181
metadata: options.metadata,
182+
environmentVariables: options.environmentVariables,
181183
});
182184
const session = await adapter.createSession(bindings);
183185
return {

packages/sdk/src/internal/core/validate-config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,13 @@ export function collectProviderCapabilities(
379379
}
380380
}
381381
for (const [name, agent] of Object.entries(config.agents ?? {})) {
382+
if (agent.environment_variables && (!agent.provider || agent.provider === providerName)) {
383+
diagnostics.error(
384+
`${providerName}.agent.environment_variables.unsupported`,
385+
`agent.${name}: environment_variables is supported only by Qoder; remove it or pin this agent to qoder.`,
386+
{ type: "agent", name, provider: providerName },
387+
);
388+
}
382389
if (agent.tunnel && (!agent.provider || agent.provider === providerName)) {
383390
diagnostics.error(
384391
`${providerName}.agent.tunnel.unsupported`,

packages/sdk/src/internal/parser/schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ const agentSchema = z.object({
260260
resources: z.array(sessionGithubRepoResourceSchema).optional(),
261261
multiagent: multiagentSchema.optional(),
262262
metadata: z.record(z.string(), z.string()).optional(),
263+
environment_variables: z.record(z.string().min(1), z.string()).optional(),
263264
delivery: z.record(z.string(), agentDeliverySchema).optional(),
264265
});
265266

packages/sdk/src/internal/providers/qoder/adapter.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,9 @@ export class QoderAdapter implements ProviderAdapter {
800800
};
801801
if (bindings.title) body.title = bindings.title;
802802
if (bindings.metadata) body.metadata = bindings.metadata;
803+
if (bindings.environment_variables) {
804+
body.config = { environment_variables: bindings.environment_variables };
805+
}
803806
if (bindings.files?.length) {
804807
body.resources = bindings.files.map((file) => ({
805808
type: "file",

packages/sdk/src/internal/providers/qoder/mapper.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ export function mapForwardTemplate(
492492
if (refs.tunnel_id) body.tunnel_id = refs.tunnel_id;
493493
if (projectName) body.metadata = injectMetadata(decl.metadata, projectName, name);
494494
else body.metadata = decl.metadata ?? {};
495+
if (decl.environment_variables) body.environment_variables = decl.environment_variables;
495496

496497
if (decl.tools) {
497498
body.tools = [
@@ -663,6 +664,11 @@ export function mapSession(bindings: ManagedSessionBindings): unknown {
663664
if (bindings.tunnel_id) body.tunnel_id = bindings.tunnel_id;
664665
if (bindings.title) body.title = bindings.title;
665666
if (bindings.metadata) body.metadata = bindings.metadata;
667+
if (bindings.environment_variables) {
668+
body.environment_variables = Object.entries(bindings.environment_variables)
669+
.map(([key, value]) => `${key}=${value}`)
670+
.join(";");
671+
}
666672
if (bindings.vault_ids.length) body.vault_ids = bindings.vault_ids;
667673
// Memory stores and user-uploaded files share the `resources` array (vaults are separate
668674
// via `vault_ids`). Every entry needs a non-empty `type`; file shape mirrors qoder's

packages/sdk/src/internal/session/session-manager.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ export interface SessionCreateOptions {
3737
title?: string;
3838
provider?: string;
3939
metadata?: Record<string, string>;
40+
/** Session-level environment variables. Currently supported by Qoder. */
41+
environmentVariables?: Record<string, string>;
4042
}
4143

4244
export function resolveSessionProvider(agentName: string, config: ProjectConfig, overrideProvider?: string): string {
@@ -71,6 +73,10 @@ export function buildSessionBindings(
7173
throw new UserError(`Agent '${agentName}' not found in config. Available agents: ${available || "(none)"}`);
7274
}
7375
const sessionResources = options.resources ?? agent.resources;
76+
const environmentVariables = options.environmentVariables ?? agent.environment_variables;
77+
if (environmentVariables && provider !== "qoder") {
78+
throw new UserError("Session environment variables are supported only by Qoder.");
79+
}
7480
const providerFeatures = getProvider(provider)?.features;
7581
for (const resource of sessionResources ?? []) {
7682
if (!providerFeatures?.session_resources.includes(resource.type)) {
@@ -102,6 +108,7 @@ export function buildSessionBindings(
102108
files: (options.files ?? []).map((file) => ({ file_id: file.fileId, mount_path: file.mountPath })),
103109
title: options.title,
104110
metadata: options.metadata,
111+
environment_variables: environmentVariables,
105112
};
106113
}
107114

@@ -157,6 +164,7 @@ export function buildSessionBindings(
157164
resources: sessionResources,
158165
title: options.title,
159166
metadata: options.metadata,
167+
environment_variables: environmentVariables,
160168
};
161169
}
162170

0 commit comments

Comments
 (0)