Skip to content

Commit 84efa3a

Browse files
authored
Merge branch 'main' into codex/channel-rename-migration
2 parents 3c9f6fd + 4d74bcc commit 84efa3a

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
@@ -246,6 +246,7 @@ agents:
246246
skills: [ <string> | { type, skill_id, version? } ]
247247
vault: <string>
248248
memory_stores: [ <string> ]
249+
environment_variables: { <key>: <string> } # Qoder only
249250
resources: [ SessionResource ]
250251
multiagent: { type: "coordinator", agents: [...] }
251252
metadata: { <key>: <string> }
@@ -266,6 +267,7 @@ agents:
266267
| `skills[]` | string \| AgentSkillRef | no | Skill name or `{ type: "official"\|"custom", skill_id, version? }`. |
267268
| `vault` | string | no | Vault name. |
268269
| `memory_stores` | string[] | no | Bound memory stores. |
270+
| `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`. |
269271
| `resources` | SessionResource[] | no | Resources attached to every managed Session created for the Agent. |
270272
| `multiagent.type` | `"coordinator"` | no | Declare a coordinator agent. |
271273
| `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
@@ -344,6 +344,13 @@ export function collectProviderCapabilities(
344344
}
345345
}
346346
for (const [name, agent] of Object.entries(config.agents ?? {})) {
347+
if (agent.environment_variables && (!agent.provider || agent.provider === providerName)) {
348+
diagnostics.error(
349+
`${providerName}.agent.environment_variables.unsupported`,
350+
`agent.${name}: environment_variables is supported only by Qoder; remove it or pin this agent to qoder.`,
351+
{ type: "agent", name, provider: providerName },
352+
);
353+
}
347354
if (agent.tunnel && (!agent.provider || agent.provider === providerName)) {
348355
diagnostics.error(
349356
`${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
@@ -255,6 +255,7 @@ const agentSchema = z.object({
255255
resources: z.array(sessionGithubRepoResourceSchema).optional(),
256256
multiagent: multiagentSchema.optional(),
257257
metadata: z.record(z.string(), z.string()).optional(),
258+
environment_variables: z.record(z.string().min(1), z.string()).optional(),
258259
delivery: z.record(z.string(), agentDeliverySchema).optional(),
259260
});
260261

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,9 @@ export class QoderAdapter implements ProviderAdapter {
797797
};
798798
if (bindings.title) body.title = bindings.title;
799799
if (bindings.metadata) body.metadata = bindings.metadata;
800+
if (bindings.environment_variables) {
801+
body.config = { environment_variables: bindings.environment_variables };
802+
}
800803
if (bindings.files?.length) {
801804
body.resources = bindings.files.map((file) => ({
802805
type: "file",

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ export function mapForwardTemplate(
479479
if (refs.tunnel_id) body.tunnel_id = refs.tunnel_id;
480480
if (projectName) body.metadata = injectMetadata(decl.metadata, projectName, name);
481481
else body.metadata = decl.metadata ?? {};
482+
if (decl.environment_variables) body.environment_variables = decl.environment_variables;
482483

483484
if (decl.tools) {
484485
body.tools = [
@@ -650,6 +651,11 @@ export function mapSession(bindings: ManagedSessionBindings): unknown {
650651
if (bindings.tunnel_id) body.tunnel_id = bindings.tunnel_id;
651652
if (bindings.title) body.title = bindings.title;
652653
if (bindings.metadata) body.metadata = bindings.metadata;
654+
if (bindings.environment_variables) {
655+
body.environment_variables = Object.entries(bindings.environment_variables)
656+
.map(([key, value]) => `${key}=${value}`)
657+
.join(";");
658+
}
653659
if (bindings.vault_ids.length) body.vault_ids = bindings.vault_ids;
654660
// Memory stores and user-uploaded files share the `resources` array (vaults are separate
655661
// 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)