@openagentpack/sdk 0.3.1-beta-85cd4b6-20260727 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as ResourceAddress, a as ResourceType, C as CloudAgent, b as CloudEnvironment, c as CloudVault, E as EventStreamOptions, P as ProviderSessionEvent, d as EventListOptions, e as ProviderSessionEventList, f as PlannedAction, D as Diagnostic, A as AgentDefinition, g as AgentWithReadiness, h as AgentSyncRun } from './session-event-CxLg_XqS.js';
2
- export { i as ActionType, j as ActionTypeSchema, k as AgentDefinitionSchema, l as AgentDriftSeverity, m as AgentDriftSeveritySchema, n as AgentModel, o as AgentModelSchema, p as AgentReadiness, q as AgentReadinessSchema, r as AgentReadinessStatus, s as AgentReadinessStatusSchema, t as AgentRecoveryAction, u as AgentRecoveryActionSchema, v as AgentSkillRef, w as AgentSkillRefSchema, x as AgentSyncResult, y as AgentSyncResultSchema, z as AgentSyncRunSchema, B as AgentSyncStatus, F as AgentSyncStatusSchema, G as AgentWithReadinessSchema, H as CloudAgentSchema, I as CloudEnvironmentSchema, J as CloudVaultSchema, K as CreateSessionRequest, L as CreateSessionRequestSchema, M as CreateSessionResponse, N as CreateSessionResponseSchema, O as DiagnosticSchema, Q as DiagnosticSeverity, S as DiagnosticSeveritySchema, T as DriftKind, U as DriftKindSchema, V as GetSessionRequest, W as GetSessionRequestSchema, X as KNOWN_SESSION_EVENT_TYPES, Y as KnownSessionEventType, Z as ListCloudAgentsResponse, _ as ListCloudAgentsResponseSchema, $ as ListCloudEnvironmentsResponse, a0 as ListCloudEnvironmentsResponseSchema, a1 as ListSessionEventsRequest, a2 as ListSessionEventsRequestSchema, a3 as ListSessionEventsResponse, a4 as ListSessionEventsResponseSchema, a5 as ListSessionsRequest, a6 as ListSessionsRequestSchema, a7 as ListSessionsResponse, a8 as ListSessionsResponseSchema, a9 as PlanReadinessImpact, aa as PlanReadinessImpactSchema, ab as PlannedActionSchema, ac as ResourceAddressSchema, ad as ResourceTypeSchema, ae as SendEventRequest, af as SendEventRequestSchema, ag as SendEventResponse, ah as SendEventResponseSchema, ai as Session, aj as SessionAgent, ak as SessionAgentSchema, al as SessionContentBlock, am as SessionContentBlockSchema, an as SessionEvent, ao as SessionEventSchema, ap as SessionEventType, aq as SessionEventTypeName, ar as SessionEventTypeSchema, as as SessionSchema } from './session-event-CxLg_XqS.js';
1
+ import { R as ResourceAddress, a as ResourceType, C as CloudAgent, b as CloudEnvironment, c as CloudVault, E as EventStreamOptions, P as ProviderSessionEvent, d as EventListOptions, e as ProviderSessionEventList, f as PlannedAction, D as Diagnostic, A as AgentDefinition, g as AgentWithReadiness, h as AgentSyncRun } from './session-event-mzuENtyH.js';
2
+ export { i as ActionType, j as ActionTypeSchema, k as AgentDefinitionSchema, l as AgentDriftSeverity, m as AgentDriftSeveritySchema, n as AgentModel, o as AgentModelSchema, p as AgentReadiness, q as AgentReadinessSchema, r as AgentReadinessStatus, s as AgentReadinessStatusSchema, t as AgentRecoveryAction, u as AgentRecoveryActionSchema, v as AgentSkillRef, w as AgentSkillRefSchema, x as AgentSyncResult, y as AgentSyncResultSchema, z as AgentSyncRunSchema, B as AgentSyncStatus, F as AgentSyncStatusSchema, G as AgentWithReadinessSchema, H as CloudAgentSchema, I as CloudEnvironmentSchema, J as CloudVaultSchema, K as CreateSessionRequest, L as CreateSessionRequestSchema, M as CreateSessionResponse, N as CreateSessionResponseSchema, O as DiagnosticSchema, Q as DiagnosticSeverity, S as DiagnosticSeveritySchema, T as DriftKind, U as DriftKindSchema, V as GetSessionRequest, W as GetSessionRequestSchema, X as KNOWN_SESSION_EVENT_TYPES, Y as KnownSessionEventType, Z as ListCloudAgentsResponse, _ as ListCloudAgentsResponseSchema, $ as ListCloudEnvironmentsResponse, a0 as ListCloudEnvironmentsResponseSchema, a1 as ListSessionEventsRequest, a2 as ListSessionEventsRequestSchema, a3 as ListSessionEventsResponse, a4 as ListSessionEventsResponseSchema, a5 as ListSessionsRequest, a6 as ListSessionsRequestSchema, a7 as ListSessionsResponse, a8 as ListSessionsResponseSchema, a9 as PlanReadinessImpact, aa as PlanReadinessImpactSchema, ab as PlannedActionSchema, ac as ResourceAddressSchema, ad as ResourceTypeSchema, ae as SendEventRequest, af as SendEventRequestSchema, ag as SendEventResponse, ah as SendEventResponseSchema, ai as Session, aj as SessionAgent, ak as SessionAgentSchema, al as SessionContentBlock, am as SessionContentBlockSchema, an as SessionEvent, ao as SessionEventSchema, ap as SessionEventType, aq as SessionEventTypeName, ar as SessionEventTypeSchema, as as SessionSchema } from './session-event-mzuENtyH.js';
3
3
  import { P as ProviderFileInfo } from './file-URqwlxLf.js';
4
4
  import 'zod';
5
5
 
@@ -59,6 +59,8 @@ interface EnvironmentConfig {
59
59
  type: "cloud" | "self_hosted";
60
60
  networking?: NetworkingConfig;
61
61
  packages?: PackagesConfig;
62
+ /** Shell script executed by supported providers while preparing a sandbox. */
63
+ setup_script?: string;
62
64
  }
63
65
  interface TunnelDecl {
64
66
  name?: string;
@@ -146,6 +148,8 @@ interface AgentDecl {
146
148
  resources?: SessionResourceDecl[];
147
149
  multiagent?: MultiagentDecl;
148
150
  metadata?: Record<string, string>;
151
+ /** Qoder runtime environment variables. Forward delivery stores these as Template defaults. */
152
+ environment_variables?: Record<string, string>;
149
153
  /** Provider-specific remote materialization. Omitted means the existing managed Agent resource. */
150
154
  delivery?: Record<ProviderName, AgentDeliveryDecl>;
151
155
  }
@@ -418,6 +422,8 @@ interface CommonSessionBindings {
418
422
  resources?: SessionResource[];
419
423
  title?: string;
420
424
  metadata?: Record<string, string>;
425
+ /** Provider-neutral representation; Qoder maps it to each Session API's wire shape. */
426
+ environment_variables?: Record<string, string>;
421
427
  }
422
428
  interface ManagedSessionBindings extends CommonSessionBindings {
423
429
  /** Omitted for backward compatibility; omitted always means managed. */
@@ -498,6 +504,8 @@ interface ResourceState {
498
504
  desired_readiness_baseline?: ResourceReadinessBaseline;
499
505
  remote_hash?: string;
500
506
  remote_snapshot?: unknown;
507
+ /** Non-reversible declaration fingerprint used to infer safe logical renames. */
508
+ replacement_fingerprint?: string;
501
509
  drift_paths?: string[];
502
510
  drift_status?: "in_sync" | "drifted" | "missing" | "unchecked";
503
511
  }
@@ -685,7 +693,7 @@ interface ProviderAdapter {
685
693
  getMemoryVersion?(storeId: string, versionId: string): Promise<MemoryVersionInfo>;
686
694
  redactMemoryVersion?(storeId: string, versionId: string): Promise<MemoryVersionInfo>;
687
695
  createDeployment(name: string, decl: DeploymentDecl, refs: ResolvedDeploymentRefs, basePath: string): Promise<RemoteResource>;
688
- updateDeployment(id: string, name: string, decl: DeploymentDecl, refs: ResolvedDeploymentRefs, basePath: string): Promise<RemoteResource>;
696
+ updateDeployment(id: string, name: string, decl: DeploymentDecl, refs: ResolvedDeploymentRefs, basePath: string, preparedFiles?: ReadonlyMap<string, string>): Promise<RemoteResource>;
689
697
  deleteDeployment(id: string): Promise<void>;
690
698
  runDeployment(ctx: DeploymentContext): Promise<DeploymentRunResult$1>;
691
699
  getDeployment(ctx: DeploymentContext): Promise<DeploymentInfo$1>;
@@ -1289,6 +1297,8 @@ interface SessionCreateOptions {
1289
1297
  title?: string;
1290
1298
  provider?: string;
1291
1299
  metadata?: Record<string, string>;
1300
+ /** Session-level environment variables. Currently supported by Qoder. */
1301
+ environmentVariables?: Record<string, string>;
1292
1302
  }
1293
1303
  declare function resolveSessionProvider(agentName: string, config: ProjectConfig, overrideProvider?: string): string;
1294
1304
 
package/dist/index.js CHANGED
@@ -395,6 +395,24 @@ function skillNameFromFiles(files) {
395
395
  return match?.[1]?.replace(/^["']|["']$/g, "") || void 0;
396
396
  }
397
397
 
398
+ // src/internal/providers/deployment-conflict.ts
399
+ var DeploymentCreateConflictError = class extends ConflictError {
400
+ constructor(conflict, preparedFiles) {
401
+ super(conflict.statusCode, conflict.responseBody, "Deployment create");
402
+ this.preparedFiles = preparedFiles;
403
+ this.name = conflict.name;
404
+ this.message = conflict.message;
405
+ this.stack = conflict.stack;
406
+ }
407
+ preparedFiles;
408
+ };
409
+ function preserveDeploymentFilesOnConflict(error, preparedFiles) {
410
+ if (error instanceof ConflictError && preparedFiles.size > 0) {
411
+ throw new DeploymentCreateConflictError(error, preparedFiles);
412
+ }
413
+ throw error;
414
+ }
415
+
398
416
  // src/internal/providers/memory-api.ts
399
417
  function query(path, values) {
400
418
  const params = new URLSearchParams();
@@ -1736,11 +1754,15 @@ var ClaudeAdapter = class _ClaudeAdapter {
1736
1754
  async createDeployment(name, decl, refs, basePath) {
1737
1755
  const uploaded = await this.uploadDeploymentFiles(decl, basePath);
1738
1756
  const body = mapDeployment(name, decl, refs, this.projectName, uploaded);
1739
- const res = await this.client.post("/deployments", body);
1740
- return toRemoteResource(res);
1757
+ try {
1758
+ const res = await this.client.post("/deployments", body);
1759
+ return toRemoteResource(res);
1760
+ } catch (error) {
1761
+ preserveDeploymentFilesOnConflict(error, uploaded);
1762
+ }
1741
1763
  }
1742
- async updateDeployment(id, name, decl, refs, basePath) {
1743
- const uploaded = await this.uploadDeploymentFiles(decl, basePath);
1764
+ async updateDeployment(id, name, decl, refs, basePath, preparedFiles) {
1765
+ const uploaded = preparedFiles ? new Map(preparedFiles) : await this.uploadDeploymentFiles(decl, basePath);
1744
1766
  const current = await this.client.get(`/deployments/${id}`);
1745
1767
  if (current.schedule && !decl.schedule) {
1746
1768
  throw new UserError(
@@ -2013,12 +2035,23 @@ function normalizeToolNameForQoder(name) {
2013
2035
  function normalizeToolNameFromQoder(name) {
2014
2036
  return name.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
2015
2037
  }
2038
+ function normalizeEnvironmentPackages(value) {
2039
+ if (!value || typeof value !== "object") return void 0;
2040
+ const raw = value;
2041
+ const packages = {};
2042
+ for (const key of ["apt", "npm", "pip"]) {
2043
+ if (Array.isArray(raw[key]) && raw[key].length > 0) packages[key] = raw[key];
2044
+ }
2045
+ return Object.keys(packages).length > 0 ? packages : void 0;
2046
+ }
2016
2047
  function mapEnvironment2(name, decl, projectName) {
2017
2048
  const envType = decl.config.type ?? "cloud";
2018
2049
  const config = { type: envType };
2019
2050
  if (decl.config.networking) config.networking = decl.config.networking;
2020
2051
  else if (envType === "cloud") config.networking = { type: "unrestricted" };
2021
- if (decl.config.packages) config.packages = decl.config.packages;
2052
+ const packages = normalizeEnvironmentPackages(decl.config.packages);
2053
+ if (packages) config.packages = packages;
2054
+ if (decl.config.setup_script !== void 0) config.setup_script = decl.config.setup_script;
2022
2055
  return {
2023
2056
  name,
2024
2057
  description: decl.description ?? "",
@@ -2099,7 +2132,8 @@ function envToDecl2(raw) {
2099
2132
  config: {
2100
2133
  type: config.type ?? "cloud",
2101
2134
  networking: config.networking,
2102
- packages: config.packages
2135
+ packages: normalizeEnvironmentPackages(config.packages),
2136
+ setup_script: config.setup_script
2103
2137
  },
2104
2138
  metadata: stripAgentsMetadata(raw.metadata)
2105
2139
  });
@@ -2342,6 +2376,7 @@ function mapForwardTemplate(name, decl, refs, projectName) {
2342
2376
  if (refs.tunnel_id) body.tunnel_id = refs.tunnel_id;
2343
2377
  if (projectName) body.metadata = injectMetadata(decl.metadata, projectName, name);
2344
2378
  else body.metadata = decl.metadata ?? {};
2379
+ if (decl.environment_variables) body.environment_variables = decl.environment_variables;
2345
2380
  if (decl.tools) {
2346
2381
  body.tools = [
2347
2382
  {
@@ -2482,6 +2517,9 @@ function mapSession2(bindings) {
2482
2517
  if (bindings.tunnel_id) body.tunnel_id = bindings.tunnel_id;
2483
2518
  if (bindings.title) body.title = bindings.title;
2484
2519
  if (bindings.metadata) body.metadata = bindings.metadata;
2520
+ if (bindings.environment_variables) {
2521
+ body.environment_variables = Object.entries(bindings.environment_variables).map(([key, value]) => `${key}=${value}`).join(";");
2522
+ }
2485
2523
  if (bindings.vault_ids.length) body.vault_ids = bindings.vault_ids;
2486
2524
  const resources = [];
2487
2525
  for (const id of bindings.memory_store_ids) resources.push({ type: "memory_store", memory_store_id: id });
@@ -2691,14 +2729,10 @@ var QoderAdapter = class _QoderAdapter {
2691
2729
  }
2692
2730
  normalizeRemote(type, raw) {
2693
2731
  if (type === "environment") {
2694
- const config = raw.config ?? {};
2732
+ const normalized = envToDecl2(raw);
2695
2733
  return compactDeep({
2696
2734
  description: raw.description,
2697
- config: {
2698
- type: config.type ?? "cloud",
2699
- networking: config.networking,
2700
- packages: config.packages
2701
- },
2735
+ config: normalized.config,
2702
2736
  metadata: stripAgentsMetadata(raw.metadata)
2703
2737
  });
2704
2738
  }
@@ -2757,7 +2791,14 @@ var QoderAdapter = class _QoderAdapter {
2757
2791
  }
2758
2792
  async updateEnvironment(id, name, decl) {
2759
2793
  const body = mapEnvironment2(name, decl, this.projectName);
2760
- const res = await this.client.put(`/environments/${id}`, body);
2794
+ const current = await this.client.get(`/environments/${id}`);
2795
+ const currentMetadata = current.metadata ?? {};
2796
+ const metadata = { ...body.metadata ?? {} };
2797
+ for (const key of Object.keys(currentMetadata)) {
2798
+ if (!key.startsWith("agents.") && !(key in metadata)) metadata[key] = null;
2799
+ }
2800
+ body.metadata = metadata;
2801
+ const res = await this.client.post(`/environments/${id}`, body);
2761
2802
  return toRemoteResource(res);
2762
2803
  }
2763
2804
  async deleteEnvironment(id, cascade = false) {
@@ -2979,11 +3020,15 @@ var QoderAdapter = class _QoderAdapter {
2979
3020
  async createDeployment(name, decl, refs, basePath) {
2980
3021
  const uploaded = await this.uploadDeploymentFiles(decl, basePath);
2981
3022
  const body = mapDeployment2(name, decl, refs, this.projectName, uploaded);
2982
- const res = await this.client.post("/deployments", body);
2983
- return toRemoteResource(res);
3023
+ try {
3024
+ const res = await this.client.post("/deployments", body);
3025
+ return toRemoteResource(res);
3026
+ } catch (error) {
3027
+ preserveDeploymentFilesOnConflict(error, uploaded);
3028
+ }
2984
3029
  }
2985
- async updateDeployment(id, name, decl, refs, basePath) {
2986
- const uploaded = await this.uploadDeploymentFiles(decl, basePath);
3030
+ async updateDeployment(id, name, decl, refs, basePath, preparedFiles) {
3031
+ const uploaded = preparedFiles ? new Map(preparedFiles) : await this.uploadDeploymentFiles(decl, basePath);
2987
3032
  const current = await this.client.get(`/deployments/${id}`);
2988
3033
  if (current.schedule && !decl.schedule) {
2989
3034
  throw new UserError(
@@ -3091,6 +3136,9 @@ var QoderAdapter = class _QoderAdapter {
3091
3136
  };
3092
3137
  if (bindings.title) body2.title = bindings.title;
3093
3138
  if (bindings.metadata) body2.metadata = bindings.metadata;
3139
+ if (bindings.environment_variables) {
3140
+ body2.config = { environment_variables: bindings.environment_variables };
3141
+ }
3094
3142
  if (bindings.files?.length) {
3095
3143
  body2.resources = bindings.files.map((file) => ({
3096
3144
  type: "file",
@@ -3581,7 +3629,9 @@ function mapAgent3(name, decl, refs, version, projectName, skillVersions) {
3581
3629
  }
3582
3630
  const BAILIAN_BUILTINS = /* @__PURE__ */ new Set(["bash", "read", "write", "edit", "glob", "grep", "download_file"]);
3583
3631
  if (decl.tools) {
3584
- const toolConfigs = resolveBuiltinTools(decl.tools, { supportedWireNames: BAILIAN_BUILTINS }).map((tool) => ({
3632
+ const toolConfigs = resolveBuiltinTools(decl.tools, {
3633
+ supportedWireNames: BAILIAN_BUILTINS
3634
+ }).map((tool) => ({
3585
3635
  name: tool.wireName,
3586
3636
  enabled: true
3587
3637
  }));
@@ -3655,30 +3705,60 @@ function mapSession3(bindings) {
3655
3705
  if (bindings.memory_store_ids.length) body.memory_store_ids = bindings.memory_store_ids;
3656
3706
  return body;
3657
3707
  }
3658
- function mapDeploymentToSession(decl, refs, fileIds) {
3708
+ function mapDeployment3(name, decl, refs, projectName, uploadedFiles) {
3709
+ const agent = { id: refs.agent_id };
3710
+ if (refs.agent_version !== void 0) agent.version = refs.agent_version;
3659
3711
  const body = {
3660
- agent: refs.agent_id,
3661
- environment_id: refs.environment_id
3712
+ name,
3713
+ agent,
3714
+ environment_id: refs.environment_id,
3715
+ initial_events: mapMessageEvents(decl.initial_events)
3662
3716
  };
3663
- if (decl.description) body.title = decl.description;
3664
- if (fileIds.length) {
3665
- const fileResources = (decl.resources ?? []).filter((r) => r.type === "file");
3666
- body.resources = fileIds.map((id, index) => {
3667
- const entry = { type: "file", file_id: id };
3668
- const mountPath = fileResources[index]?.mount_path;
3669
- if (mountPath) entry.mount_path = mountPath;
3670
- return entry;
3671
- });
3717
+ if (decl.description) body.description = decl.description;
3718
+ if (refs.vault_ids.length) body.vault_ids = refs.vault_ids;
3719
+ const resources = mapDeploymentResources3(decl, uploadedFiles);
3720
+ if (resources.length) body.resources = resources;
3721
+ if (decl.schedule) {
3722
+ body.schedule = {
3723
+ type: "cron",
3724
+ expression: decl.schedule.expression,
3725
+ timezone: decl.schedule.timezone
3726
+ };
3672
3727
  }
3728
+ if (projectName) {
3729
+ body.metadata = injectMetadata(decl.metadata, projectName, name);
3730
+ } else if (decl.metadata) {
3731
+ body.metadata = decl.metadata;
3732
+ }
3733
+ return body;
3734
+ }
3735
+ function mapDeploymentUpdate3(name, decl, refs, projectName, uploadedFiles, existingMetadata) {
3736
+ const body = mapDeployment3(name, decl, refs, projectName, uploadedFiles);
3737
+ body.description = decl.description ?? "";
3738
+ body.vault_ids = refs.vault_ids;
3739
+ body.resources = mapDeploymentResources3(decl, uploadedFiles);
3740
+ if (!decl.schedule) body.schedule = null;
3741
+ if (!projectName && !decl.metadata && existingMetadata) body.metadata = existingMetadata;
3673
3742
  return body;
3674
3743
  }
3675
- function mapInitialEvents2(events) {
3676
- const input = events.filter((e) => e.type === "user.message" || e.type === "system.message").map((e) => ({
3744
+ function mapDeploymentResources3(decl, uploadedFiles) {
3745
+ const resources = [];
3746
+ for (const resource of decl.resources ?? []) {
3747
+ if (resource.type !== "file") continue;
3748
+ const fileId = resource.file_id ?? (resource.source ? uploadedFiles?.get(resource.source) : void 0);
3749
+ if (!fileId) continue;
3750
+ const entry = { type: "file", file_id: fileId };
3751
+ if (resource.mount_path) entry.mount_path = resolveSandboxMountPath("bailian", resource.mount_path);
3752
+ resources.push(entry);
3753
+ }
3754
+ return resources;
3755
+ }
3756
+ function mapMessageEvents(events) {
3757
+ return events.filter((event) => event.type === "user.message" || event.type === "system.message").map((event) => ({
3677
3758
  role: "user",
3678
3759
  type: "message",
3679
- content: [{ type: "text", text: e.content }]
3760
+ content: [{ type: "text", text: event.content }]
3680
3761
  }));
3681
- return { input };
3682
3762
  }
3683
3763
  function mapSendMessage3(text) {
3684
3764
  return {
@@ -3710,7 +3790,8 @@ var BailianAdapter = class _BailianAdapter {
3710
3790
  agent: "/agents",
3711
3791
  skill: "/skills",
3712
3792
  vault: "/vaults",
3713
- file: "/files"
3793
+ file: "/files",
3794
+ deployment: "/deployments"
3714
3795
  };
3715
3796
  async findResource(type, name, id) {
3716
3797
  const accept = type === "agent" ? (r) => !isArchivedAgent(r) : void 0;
@@ -4055,46 +4136,93 @@ var BailianAdapter = class _BailianAdapter {
4055
4136
  async deleteCredential(vaultId, credentialId) {
4056
4137
  await this.client.delete(`/vaults/${vaultId}/credentials/${credentialId}`);
4057
4138
  }
4058
- // --- Deployment (emulated) ---
4059
- async createDeployment(_name, _decl, _refs, _basePath) {
4060
- return { id: null, type: "deployment" };
4139
+ // --- Deployment ---
4140
+ async createDeployment(name, decl, refs, basePath) {
4141
+ const uploaded = await this.uploadDeploymentFiles(decl, basePath);
4142
+ const body = mapDeployment3(name, decl, refs, this.projectName, uploaded);
4143
+ try {
4144
+ const res = await this.client.post("/deployments", body);
4145
+ return toRemoteResource(res);
4146
+ } catch (error) {
4147
+ preserveDeploymentFilesOnConflict(error, uploaded);
4148
+ }
4061
4149
  }
4062
- async updateDeployment(_id, _name, _decl, _refs, _basePath) {
4063
- return { id: null, type: "deployment" };
4150
+ async updateDeployment(id, name, decl, refs, basePath, preparedFiles) {
4151
+ if (!id) return this.createDeployment(name, decl, refs, basePath);
4152
+ const current = await this.client.get(`/deployments/${id}`);
4153
+ const uploaded = preparedFiles ? new Map(preparedFiles) : await this.uploadDeploymentFiles(decl, basePath);
4154
+ const body = mapDeploymentUpdate3(
4155
+ name,
4156
+ decl,
4157
+ refs,
4158
+ this.projectName,
4159
+ uploaded,
4160
+ current.metadata
4161
+ );
4162
+ const res = await this.client.post(`/deployments/${id}`, body);
4163
+ return toRemoteResource(res);
4064
4164
  }
4065
- async deleteDeployment(_id) {
4165
+ async deleteDeployment(id) {
4166
+ await this.client.post(`/deployments/${id}/archive`, {});
4066
4167
  }
4067
4168
  async runDeployment(ctx) {
4068
- const fileIds = [];
4069
- for (const r of ctx.decl.resources ?? []) {
4070
- if (r.type === "file") {
4071
- if (r.file_id) {
4072
- fileIds.push(r.file_id);
4073
- } else if (r.source) {
4074
- fileIds.push(await this.uploadSessionFile(r.source, ctx.basePath));
4075
- }
4076
- }
4077
- }
4078
- const body = mapDeploymentToSession(ctx.decl, ctx.refs, fileIds);
4079
- const sessionRes = await this.client.post("/sessions", body);
4080
- const sessionId = sessionRes.id;
4081
- const eventsBody = mapInitialEvents2(ctx.decl.initial_events);
4082
- const input = eventsBody.input;
4083
- if (input.length) {
4084
- await this.client.post(`/sessions/${sessionId}/events`, eventsBody);
4169
+ if (!ctx.id) {
4170
+ throw new UserError(`Deployment '${ctx.name}' has no remote id; run \`agents apply\` first.`);
4085
4171
  }
4086
- return { session_id: sessionId };
4172
+ const res = await this.client.post(`/deployments/${ctx.id}/run`, {});
4173
+ return {
4174
+ run_id: res.id,
4175
+ session_id: res.session_id ?? null,
4176
+ error: res.error ?? void 0
4177
+ };
4087
4178
  }
4088
4179
  async getDeployment(ctx) {
4089
- const plan = mapDeploymentToSession(ctx.decl, ctx.refs, []);
4180
+ if (!ctx.id) {
4181
+ throw new UserError(`Deployment '${ctx.name}' has no remote id; run \`agents apply\` first.`);
4182
+ }
4183
+ const res = await this.client.get(`/deployments/${ctx.id}`);
4184
+ return toDeploymentInfo3(res);
4185
+ }
4186
+ async listDeployments(filter) {
4187
+ const params = new URLSearchParams();
4188
+ if (filter?.agent_id) params.set("agent_id", filter.agent_id);
4189
+ if (filter?.status) params.set("status", filter.status);
4190
+ if (filter?.include_archived) params.set("include_archived", "true");
4191
+ if (filter?.limit) params.set("limit", String(filter.limit));
4192
+ if (filter?.page) params.set("page", filter.page);
4193
+ if (filter?.created_at_gte) params.set("created_at[gte]", filter.created_at_gte);
4194
+ if (filter?.created_at_lte) params.set("created_at[lte]", filter.created_at_lte);
4195
+ const query2 = params.toString();
4196
+ const res = await this.client.get(`/deployments${query2 ? `?${query2}` : ""}`);
4197
+ const nextPage = res.next_page ?? void 0;
4090
4198
  return {
4091
- id: ctx.id,
4092
- status: "emulated (local)",
4093
- schedule: ctx.decl.schedule,
4094
- attributes: { materialization_plan: plan }
4199
+ deployments: (res.data ?? []).map(toDeploymentInfo3),
4200
+ has_more: nextPage != null,
4201
+ next_page: nextPage
4095
4202
  };
4096
4203
  }
4097
- async uploadSessionFile(source, basePath) {
4204
+ async pauseDeployment(ctx) {
4205
+ return this.setDeploymentPaused(ctx, true);
4206
+ }
4207
+ async unpauseDeployment(ctx) {
4208
+ return this.setDeploymentPaused(ctx, false);
4209
+ }
4210
+ async setDeploymentPaused(ctx, paused) {
4211
+ if (!ctx.id) throw new UserError(`Deployment '${ctx.name}' has no remote id; run \`agents apply\` first.`);
4212
+ const action = paused ? "pause" : "unpause";
4213
+ const res = await this.client.post(`/deployments/${ctx.id}/${action}`, {});
4214
+ return toDeploymentInfo3(res);
4215
+ }
4216
+ async uploadDeploymentFiles(decl, basePath) {
4217
+ const uploaded = /* @__PURE__ */ new Map();
4218
+ for (const resource of decl.resources ?? []) {
4219
+ if (resource.type === "file" && !resource.file_id && resource.source && !uploaded.has(resource.source)) {
4220
+ uploaded.set(resource.source, await this.uploadDeploymentFile(resource.source, basePath));
4221
+ }
4222
+ }
4223
+ return uploaded;
4224
+ }
4225
+ async uploadDeploymentFile(source, basePath) {
4098
4226
  const fullPath = resolve3(dirname3(basePath), source);
4099
4227
  const content = readFileSync3(fullPath);
4100
4228
  const formData = new FormData();
@@ -4190,6 +4318,19 @@ var BailianAdapter = class _BailianAdapter {
4190
4318
  function isArchivedAgent(raw) {
4191
4319
  return typeof raw.archived_at === "string" && raw.archived_at.trim().length > 0;
4192
4320
  }
4321
+ function toDeploymentInfo3(res) {
4322
+ const schedule = res.schedule;
4323
+ return {
4324
+ id: res.id ?? null,
4325
+ status: res.status ?? "unknown",
4326
+ paused_reason: res.paused_reason ?? void 0,
4327
+ schedule: schedule ? {
4328
+ expression: schedule.expression,
4329
+ timezone: schedule.timezone
4330
+ } : void 0,
4331
+ attributes: res
4332
+ };
4333
+ }
4193
4334
  function toSessionInfo3(res) {
4194
4335
  return buildSessionInfo(res, () => []);
4195
4336
  }
@@ -4286,9 +4427,8 @@ var BAILIAN_CAPABILITIES = {
4286
4427
  remediation: "deploy agents independently and orchestrate via MCP"
4287
4428
  },
4288
4429
  deployment: {
4289
- tier: "emulated",
4290
- reason: "no deployment primitive on Bailian; expanded into a session at run time",
4291
- remediation: "scheduling and outcome rubrics are not enforced server-side \u2014 use external cron/CI for always-on or scheduled runs"
4430
+ tier: "native",
4431
+ reason: "deployments API with cron schedules, manual runs, pause/unpause and archive"
4292
4432
  },
4293
4433
  session: { tier: "native", reason: "sessions API" },
4294
4434
  identity: { tier: "unsupported", reason: "no mapped Identity primitive on Bailian" },
@@ -4590,7 +4730,7 @@ function mapAgent4(name, decl, refs, version, projectName) {
4590
4730
  }
4591
4731
  return body;
4592
4732
  }
4593
- function mapDeploymentToSession2(decl, refs, fileIds) {
4733
+ function mapDeploymentToSession(decl, refs, fileIds) {
4594
4734
  const body = {
4595
4735
  agent: refs.agent_id,
4596
4736
  environment_id: refs.environment_id
@@ -4953,7 +5093,7 @@ var ArkAdapter = class _ArkAdapter {
4953
5093
  }
4954
5094
  }
4955
5095
  }
4956
- const body = mapDeploymentToSession2(ctx.decl, ctx.refs, fileIds);
5096
+ const body = mapDeploymentToSession(ctx.decl, ctx.refs, fileIds);
4957
5097
  const sessionRes = await this.client.post("/sessions", body);
4958
5098
  const sessionId = sessionRes.id;
4959
5099
  const events = ctx.decl.initial_events.filter((e) => e.type === "user.message" || e.type === "system.message").map((e) => ({
@@ -4966,7 +5106,7 @@ var ArkAdapter = class _ArkAdapter {
4966
5106
  return { session_id: sessionId };
4967
5107
  }
4968
5108
  async getDeployment(ctx) {
4969
- const plan = mapDeploymentToSession2(ctx.decl, ctx.refs, []);
5109
+ const plan = mapDeploymentToSession(ctx.decl, ctx.refs, []);
4970
5110
  return {
4971
5111
  id: ctx.id,
4972
5112
  status: "emulated (local)",
@@ -5239,6 +5379,9 @@ var packagesSchema = z5.object({
5239
5379
  gem: z5.array(z5.string()).optional(),
5240
5380
  go: z5.array(z5.string()).optional()
5241
5381
  });
5382
+ var setupScriptSchema = z5.string().refine((value) => new TextEncoder().encode(value).byteLength <= 64 * 1024, {
5383
+ message: "setup_script must not exceed 65536 UTF-8 bytes"
5384
+ });
5242
5385
  var environmentSchema = z5.object({
5243
5386
  name: z5.string().optional(),
5244
5387
  description: z5.string().optional(),
@@ -5248,7 +5391,8 @@ var environmentSchema = z5.object({
5248
5391
  config: z5.object({
5249
5392
  type: z5.enum(["cloud", "self_hosted"]),
5250
5393
  networking: networkingSchema.optional(),
5251
- packages: packagesSchema.optional()
5394
+ packages: packagesSchema.optional(),
5395
+ setup_script: setupScriptSchema.optional()
5252
5396
  }),
5253
5397
  metadata: z5.record(z5.string(), z5.string()).optional()
5254
5398
  });
@@ -5437,6 +5581,7 @@ var agentSchema = z5.object({
5437
5581
  resources: z5.array(sessionGithubRepoResourceSchema).optional(),
5438
5582
  multiagent: multiagentSchema.optional(),
5439
5583
  metadata: z5.record(z5.string(), z5.string()).optional(),
5584
+ environment_variables: z5.record(z5.string().min(1), z5.string()).optional(),
5440
5585
  delivery: z5.record(z5.string(), agentDeliverySchema).optional()
5441
5586
  });
5442
5587
  var channelSchema = z5.object({
@@ -5688,9 +5833,15 @@ async function computeResourceHash(address, config, basePath, state) {
5688
5833
  return contentHash({ decl, fileHash });
5689
5834
  }
5690
5835
  }
5836
+ if (address.type === "file" && basePath) {
5837
+ const fileDecl = decl;
5838
+ const fileHash = computeLocalFileContentHash(fileDecl.source, basePath);
5839
+ return contentHash({ decl, fileHash });
5840
+ }
5691
5841
  if (address.type === "deployment") {
5692
5842
  const refs = resolveDeploymentReferenceIds(decl, config, address.provider, state);
5693
- if (refs) return contentHash({ decl, refs });
5843
+ const sourceHashes = basePath ? computeDeploymentSourceHashes(decl, basePath) : void 0;
5844
+ if (refs || sourceHashes) return contentHash({ decl, refs, sourceHashes });
5694
5845
  }
5695
5846
  if (address.type === "template") {
5696
5847
  const refs = resolveTemplateReferenceIds(decl, config, address.provider, state);
@@ -5707,6 +5858,12 @@ async function computeResourceHash(address, config, basePath, state) {
5707
5858
  }
5708
5859
  return contentHash(decl);
5709
5860
  }
5861
+ function computeReplacementFingerprint(address, config) {
5862
+ if (address.type !== "channel") return void 0;
5863
+ const decl = config.channels?.[address.name];
5864
+ if (!decl) return void 0;
5865
+ return contentHash({ channel_type: decl.type, credentials: decl.credentials ?? {} });
5866
+ }
5710
5867
  function resolveChannelReferenceIds(decl, config, provider, state) {
5711
5868
  const agent = config.agents?.[decl.agent];
5712
5869
  const agentType = agent?.delivery?.[provider]?.type === "forward" ? "template" : "agent";
@@ -5745,6 +5902,23 @@ function resolveDeploymentReferenceIds(decl, config, provider, state) {
5745
5902
  function getDeclaration(address, config) {
5746
5903
  return getResourceDeclaration(address, config);
5747
5904
  }
5905
+ function computeDeploymentSourceHashes(decl, basePath) {
5906
+ const sources = [
5907
+ ...new Set(
5908
+ (decl.resources ?? []).flatMap(
5909
+ (resource) => resource.type === "file" && !resource.file_id && resource.source ? [resource.source] : []
5910
+ )
5911
+ )
5912
+ ];
5913
+ if (sources.length === 0) return void 0;
5914
+ return Object.fromEntries(sources.map((source) => [source, computeLocalFileContentHash(source, basePath)]));
5915
+ }
5916
+ function computeLocalFileContentHash(source, basePath) {
5917
+ const fullPath = resolve9(dirname8(basePath), source);
5918
+ const stat = statSync2(fullPath, { throwIfNoEntry: false });
5919
+ if (!stat?.isFile()) return "";
5920
+ return contentHash(readFileSync6(fullPath).toString("base64"));
5921
+ }
5748
5922
  function computeSkillContentHash(source, basePath) {
5749
5923
  const fullPath = resolve9(dirname8(basePath), source);
5750
5924
  const stat = statSync2(fullPath, { throwIfNoEntry: false });
@@ -6157,8 +6331,8 @@ async function executeAction(action, provider, ctx) {
6157
6331
  resource: action.address,
6158
6332
  message: `update ${action.address.type}.${action.address.name} (${action.address.provider}) \u2014 not found remotely, recreating`
6159
6333
  });
6160
- ctx.state.removeResource(action.address);
6161
- return executeActionInner({ ...action, action: "create" }, provider, ctx);
6334
+ ctx.state.removeResource(action.previousAddress ?? action.address);
6335
+ return executeActionInner({ ...action, action: "create", previousAddress: void 0 }, provider, ctx);
6162
6336
  }
6163
6337
  }
6164
6338
  async function executeActionInner(action, provider, ctx) {
@@ -6231,7 +6405,8 @@ async function executeActionInner(action, provider, ctx) {
6231
6405
  return false;
6232
6406
  }
6233
6407
  const isUpdate = action.action === "update";
6234
- const existingId = isUpdate ? ctx.state.getResource(address)?.remote_id : void 0;
6408
+ const priorAddress = action.previousAddress ?? address;
6409
+ const existingId = isUpdate ? ctx.state.getResource(priorAddress)?.remote_id : void 0;
6235
6410
  let result;
6236
6411
  switch (type) {
6237
6412
  case "environment": {
@@ -6489,16 +6664,36 @@ async function executeActionInner(action, provider, ctx) {
6489
6664
  case "deployment": {
6490
6665
  const decl = ctx.config.deployments[name];
6491
6666
  const refs = resolveDeploymentRefs(name, ctx.config, address.provider, ctx.state);
6492
- if (isUpdate) {
6493
- result = await provider.updateDeployment(existingId, name, decl, refs, ctx.configPath ?? "");
6494
- } else {
6667
+ const hasLocalFileSources = decl.resources?.some(
6668
+ (resource) => resource.type === "file" && !resource.file_id && Boolean(resource.source)
6669
+ );
6670
+ const materializeDeployment = async () => {
6495
6671
  try {
6496
- result = await provider.createDeployment(name, decl, refs, ctx.configPath ?? "");
6672
+ return await provider.createDeployment(name, decl, refs, ctx.configPath ?? "");
6497
6673
  } catch (err) {
6498
- result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
6499
- onExisting: (existing) => provider.updateDeployment(existing.id, name, decl, refs, ctx.configPath ?? "")
6674
+ const preparedFiles = err instanceof DeploymentCreateConflictError ? err.preparedFiles : void 0;
6675
+ const existing = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
6676
+ onExisting: (existing2) => provider.updateDeployment(existing2.id, name, decl, refs, ctx.configPath ?? "", preparedFiles)
6500
6677
  });
6501
6678
  adopted = true;
6679
+ return existing;
6680
+ }
6681
+ };
6682
+ if (isUpdate && existingId) {
6683
+ result = await provider.updateDeployment(existingId, name, decl, refs, ctx.configPath ?? "");
6684
+ } else {
6685
+ const existing = hasLocalFileSources ? await findExistingByNames(provider, "deployment", [name]) : null;
6686
+ if (existing) {
6687
+ result = await provider.updateDeployment(existing.resource.id, name, decl, refs, ctx.configPath ?? "");
6688
+ emitRuntimeFeedback(ctx.onFeedback, {
6689
+ type: "resource_adopted",
6690
+ level: "info",
6691
+ resource: address,
6692
+ message: `adopt deployment.${name} (${address.provider}) \u2014 already existed remotely as "${existing.name}"`
6693
+ });
6694
+ adopted = true;
6695
+ } else {
6696
+ result = await materializeDeployment();
6502
6697
  }
6503
6698
  }
6504
6699
  break;
@@ -6534,7 +6729,7 @@ async function executeActionInner(action, provider, ctx) {
6534
6729
  remoteHash = contentHash(remote.comparable);
6535
6730
  remoteSnapshot = remote.snapshot ?? remote.comparable;
6536
6731
  }
6537
- const priorResource = ctx.state.getResource(address);
6732
+ const priorResource = ctx.state.getResource(priorAddress);
6538
6733
  ctx.state.setResource({
6539
6734
  address,
6540
6735
  remote_id: result.id,
@@ -6546,9 +6741,11 @@ async function executeActionInner(action, provider, ctx) {
6546
6741
  desired_readiness_baseline: buildReadinessBaseline(getResourceDeclaration(address, ctx.config)),
6547
6742
  remote_hash: remoteHash,
6548
6743
  remote_snapshot: remoteSnapshot,
6744
+ replacement_fingerprint: computeReplacementFingerprint(address, ctx.config),
6549
6745
  drift_paths: [],
6550
6746
  drift_status: remoteHash ? "in_sync" : void 0
6551
6747
  });
6748
+ if (action.previousAddress) ctx.state.removeResource(action.previousAddress);
6552
6749
  return adopted;
6553
6750
  }
6554
6751
  async function findExistingByNames(provider, type, names) {
@@ -6573,6 +6770,11 @@ async function adoptOnConflict(err, address, provider, onFeedback, opts) {
6573
6770
  }
6574
6771
  function nameReservedError(err, address, searchName) {
6575
6772
  const detail = err instanceof ApiError ? err.message : String(err);
6773
+ if (address.type === "channel" && err instanceof ApiError && err.responseBody.includes("CHANNEL_CREDENTIAL_CONFLICT")) {
6774
+ return new UserError(
6775
+ `${address.provider} rejected channel.${address.name} because its credentials are already used by another Channel. Keep the existing Channel address so it can be updated in place, remove the old Channel first, or use a different credential set. (${detail})`
6776
+ );
6777
+ }
6576
6778
  return new UserError(
6577
6779
  `${address.provider} reported ${address.type} "${searchName}" already exists, but it could not be found remotely to adopt. This usually means it was recently deleted and the provider still reserves the name. Wait for the provider to release the name, or use a different name for ${address.type}.${address.name}. (${detail})`
6578
6780
  );
@@ -6757,6 +6959,37 @@ function collectProviderCapabilities(config, providers, diagnostics) {
6757
6959
  continue;
6758
6960
  }
6759
6961
  const caps = def.capabilities;
6962
+ for (const [name, environment] of Object.entries(config.environments ?? {})) {
6963
+ if (environment.provider && environment.provider !== providerName) continue;
6964
+ if (environment.environment_id) continue;
6965
+ const address = { type: "environment", name, provider: providerName };
6966
+ if (environment.config.setup_script !== void 0 && providerName !== "qoder") {
6967
+ diagnostics.error(
6968
+ `${providerName}.environment.setup_script.unsupported`,
6969
+ `environment.${name}: provider '${providerName}' does not support setup_script; remove it or pin this environment to qoder.`,
6970
+ address
6971
+ );
6972
+ }
6973
+ if (providerName === "qoder") {
6974
+ if (environment.config.type === "self_hosted" && (environment.config.networking !== void 0 || environment.config.packages !== void 0)) {
6975
+ diagnostics.error(
6976
+ "qoder.environment.self_hosted.config.unsupported",
6977
+ `environment.${name}: Qoder self_hosted environments accept only config.type and config.setup_script; remove networking and packages.`,
6978
+ address
6979
+ );
6980
+ }
6981
+ const unsupported = ["cargo", "gem", "go"].filter(
6982
+ (key) => (environment.config.packages?.[key]?.length ?? 0) > 0
6983
+ );
6984
+ if (unsupported.length > 0) {
6985
+ diagnostics.error(
6986
+ "qoder.environment.packages.unsupported",
6987
+ `environment.${name}: Qoder accepts only apt, npm, and pip package declarations; remove ${unsupported.join(", ")} or install them from setup_script.`,
6988
+ address
6989
+ );
6990
+ }
6991
+ }
6992
+ }
6760
6993
  for (const [name, identity] of Object.entries(config.identities ?? {})) {
6761
6994
  if (identity.provider && identity.provider !== providerName) continue;
6762
6995
  if (!isSupported(caps, "identity")) {
@@ -6923,6 +7156,68 @@ function collectProviderCapabilities(config, providers, diagnostics) {
6923
7156
  }
6924
7157
  }
6925
7158
  }
7159
+ if (providerName === "bailian") {
7160
+ for (const [name, deployment] of Object.entries(config.deployments ?? {})) {
7161
+ if (deployment.provider && deployment.provider !== providerName) continue;
7162
+ const addr = {
7163
+ type: "deployment",
7164
+ name,
7165
+ provider: providerName
7166
+ };
7167
+ if (deployment.initial_events?.some((event) => event.type === "user.define_outcome")) {
7168
+ diagnostics.warning(
7169
+ `${providerName}.deployment.define_outcome_unsupported`,
7170
+ "Outcome rubrics (user.define_outcome) are dropped from the Bailian deployment payload; the run executes without rubric grading.",
7171
+ addr
7172
+ );
7173
+ }
7174
+ if (!deployment.initial_events?.some((event) => event.type === "user.message" || event.type === "system.message")) {
7175
+ diagnostics.error(
7176
+ `${providerName}.deployment.initial_events.message_required`,
7177
+ `deployment.${name}: Bailian requires at least one user.message or system.message initial event; user.define_outcome events are dropped.`,
7178
+ addr
7179
+ );
7180
+ }
7181
+ if (deployment.resources?.some((resource) => resource.type === "github_repository")) {
7182
+ diagnostics.warning(
7183
+ `${providerName}.deployment.github_repository_unsupported`,
7184
+ "Bailian deployment resources accept files only; github_repository resources are dropped. Clone the repository inside the session instead.",
7185
+ addr
7186
+ );
7187
+ }
7188
+ const mountPrefix = providerMountPrefix(providerName);
7189
+ const normalizedMountPaths = /* @__PURE__ */ new Set();
7190
+ for (const resource of deployment.resources ?? []) {
7191
+ if (resource.type !== "file") continue;
7192
+ if (!resource.mount_path?.trim()) {
7193
+ diagnostics.error(
7194
+ `${providerName}.deployment.file.mount_path.required`,
7195
+ `deployment.${name}: Bailian file resources require mount_path.`,
7196
+ addr
7197
+ );
7198
+ continue;
7199
+ }
7200
+ if (mountPrefix && resource.mount_path.startsWith("/") && resource.mount_path !== mountPrefix && !resource.mount_path.startsWith(`${mountPrefix}/`)) {
7201
+ diagnostics.error(
7202
+ `${providerName}.deployment.file.mount_path.invalid`,
7203
+ `deployment.${name}: Bailian file mount_path must start with '${mountPrefix}/'.`,
7204
+ addr
7205
+ );
7206
+ continue;
7207
+ }
7208
+ const normalizedMountPath = resolveSandboxMountPath(providerName, resource.mount_path);
7209
+ if (normalizedMountPaths.has(normalizedMountPath)) {
7210
+ diagnostics.error(
7211
+ `${providerName}.deployment.file.mount_path.duplicate`,
7212
+ `deployment.${name}: Bailian file mount_path '${normalizedMountPath}' is duplicated after normalization.`,
7213
+ addr
7214
+ );
7215
+ } else {
7216
+ normalizedMountPaths.add(normalizedMountPath);
7217
+ }
7218
+ }
7219
+ }
7220
+ }
6926
7221
  if (providerName !== "qoder") {
6927
7222
  for (const [name, env] of Object.entries(config.environments ?? {})) {
6928
7223
  if (env.environment_id) continue;
@@ -6935,6 +7230,13 @@ function collectProviderCapabilities(config, providers, diagnostics) {
6935
7230
  }
6936
7231
  }
6937
7232
  for (const [name, agent] of Object.entries(config.agents ?? {})) {
7233
+ if (agent.environment_variables && (!agent.provider || agent.provider === providerName)) {
7234
+ diagnostics.error(
7235
+ `${providerName}.agent.environment_variables.unsupported`,
7236
+ `agent.${name}: environment_variables is supported only by Qoder; remove it or pin this agent to qoder.`,
7237
+ { type: "agent", name, provider: providerName }
7238
+ );
7239
+ }
6938
7240
  if (agent.tunnel && (!agent.provider || agent.provider === providerName)) {
6939
7241
  diagnostics.error(
6940
7242
  `${providerName}.agent.tunnel.unsupported`,
@@ -6995,7 +7297,11 @@ function collectProviderCapabilities(config, providers, diagnostics) {
6995
7297
  if (config.deployments && caps.deployment.tier === "emulated") {
6996
7298
  for (const [name, dep] of Object.entries(config.deployments)) {
6997
7299
  if (dep.provider && dep.provider !== providerName) continue;
6998
- const addr = { type: "deployment", name, provider: providerName };
7300
+ const addr = {
7301
+ type: "deployment",
7302
+ name,
7303
+ provider: providerName
7304
+ };
6999
7305
  if (dep.schedule) {
7000
7306
  diagnostics.warning(
7001
7307
  `${providerName}.deployment.schedule_unsupported`,
@@ -7254,6 +7560,7 @@ async function buildPlan(config, state, options = {}) {
7254
7560
  const desiredHash = await computeResourceHash(address, config, options.configPath, hashStateLookup);
7255
7561
  const existing = stateIndex.get(key);
7256
7562
  const deps = getDependencies(address, graph);
7563
+ const needsNativeDeploymentMaterialization = address.type === "deployment" && existing?.remote_id === null && getProvider(address.provider)?.capabilities.deployment.tier === "native";
7257
7564
  if (address.type === "environment" && existing) {
7258
7565
  const envDecl = config.environments?.[address.name];
7259
7566
  if (existing.externally_managed && envDecl && !envDecl.environment_id) {
@@ -7305,6 +7612,17 @@ async function buildPlan(config, state, options = {}) {
7305
7612
  after: { content_hash: desiredHash },
7306
7613
  dependencies: deps
7307
7614
  });
7615
+ } else if (needsNativeDeploymentMaterialization) {
7616
+ actions.push({
7617
+ action: "update",
7618
+ address,
7619
+ driftKind: "none",
7620
+ readinessImpact: "blocking",
7621
+ reason: "Materialize legacy state as a native deployment",
7622
+ before: { content_hash: existing.desired_hash ?? existing.content_hash },
7623
+ after: { content_hash: desiredHash },
7624
+ dependencies: deps
7625
+ });
7308
7626
  } else if ((existing.desired_hash ?? existing.content_hash) !== desiredHash && existing.drift_status === "drifted") {
7309
7627
  const changedPaths = collectChangedPaths(address, config, existing, true);
7310
7628
  actions.push({
@@ -7377,8 +7695,62 @@ async function buildPlan(config, state, options = {}) {
7377
7695
  dependencies: replacement ? [replacement] : []
7378
7696
  });
7379
7697
  }
7698
+ coalesceChannelRenames(actions, config, state);
7380
7699
  return { actions, diagnostics: diagnostics.getAll() };
7381
7700
  }
7701
+ function coalesceChannelRenames(actions, config, state) {
7702
+ const creates = actions.filter((action) => action.action === "create" && action.address.type === "channel");
7703
+ const deletes = actions.filter((action) => action.action === "delete" && action.address.type === "channel");
7704
+ const stateByAddress = new Map(state.resources.map((resource) => [addressKey(resource.address), resource]));
7705
+ const matchedDeletes = /* @__PURE__ */ new Set();
7706
+ for (const create of creates) {
7707
+ const desiredType = config.channels?.[create.address.name]?.type;
7708
+ if (!desiredType) continue;
7709
+ const desiredFingerprint = computeReplacementFingerprint(create.address, config);
7710
+ const candidates = deletes.filter((deletion2) => {
7711
+ if (matchedDeletes.has(deletion2) || deletion2.address.provider !== create.address.provider) return false;
7712
+ const prior2 = stateByAddress.get(addressKey(deletion2.address));
7713
+ const snapshot = prior2?.remote_snapshot;
7714
+ if (snapshot?.channel_type !== desiredType) return false;
7715
+ return !prior2?.replacement_fingerprint || prior2.replacement_fingerprint === desiredFingerprint;
7716
+ });
7717
+ if (candidates.length !== 1) continue;
7718
+ const deletion = candidates[0];
7719
+ const prior = stateByAddress.get(addressKey(deletion.address));
7720
+ const competingCreates = creates.filter(
7721
+ (candidate) => candidate !== create && candidate.address.provider === create.address.provider && config.channels?.[candidate.address.name]?.type === desiredType && (!prior?.replacement_fingerprint || computeReplacementFingerprint(candidate.address, config) === prior.replacement_fingerprint)
7722
+ );
7723
+ if (competingCreates.length > 0) continue;
7724
+ create.action = "update";
7725
+ create.previousAddress = deletion.address;
7726
+ create.before = deletion.before;
7727
+ create.driftKind = "local";
7728
+ create.reason = `Channel key renamed from '${deletion.address.name}' (remote resource retained)`;
7729
+ protectRenamedChannelDependencies(actions, stateByAddress, deletion, create);
7730
+ matchedDeletes.add(deletion);
7731
+ }
7732
+ for (let index = actions.length - 1; index >= 0; index--) {
7733
+ if (matchedDeletes.has(actions[index])) actions.splice(index, 1);
7734
+ }
7735
+ }
7736
+ function protectRenamedChannelDependencies(actions, stateByAddress, deletion, replacement) {
7737
+ const prior = stateByAddress.get(addressKey(deletion.address));
7738
+ const snapshot = prior?.remote_snapshot;
7739
+ const referencedIds = new Set(
7740
+ [snapshot?.identity_id, snapshot?.template_id].filter((id) => typeof id === "string")
7741
+ );
7742
+ if (referencedIds.size === 0) return;
7743
+ for (const action of actions) {
7744
+ if (action.action !== "delete" || action.address.type !== "identity" && action.address.type !== "template" || action.address.provider !== replacement.address.provider) {
7745
+ continue;
7746
+ }
7747
+ const dependency = stateByAddress.get(addressKey(action.address));
7748
+ if (!dependency?.remote_id || !referencedIds.has(dependency.remote_id)) continue;
7749
+ if (!action.dependencies.some((address) => addressKey(address) === addressKey(replacement.address))) {
7750
+ action.dependencies.push(replacement.address);
7751
+ }
7752
+ }
7753
+ }
7382
7754
  function deliveryReplacementAddress(address, graph) {
7383
7755
  if (address.type !== "agent" && address.type !== "template") return void 0;
7384
7756
  const replacementType = address.type === "agent" ? "template" : "agent";
@@ -8374,6 +8746,10 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
8374
8746
  throw new UserError(`Agent '${agentName}' not found in config. Available agents: ${available || "(none)"}`);
8375
8747
  }
8376
8748
  const sessionResources = options.resources ?? agent.resources;
8749
+ const environmentVariables = options.environmentVariables ?? agent.environment_variables;
8750
+ if (environmentVariables && provider !== "qoder") {
8751
+ throw new UserError("Session environment variables are supported only by Qoder.");
8752
+ }
8377
8753
  const providerFeatures = getProvider(provider)?.features;
8378
8754
  for (const resource of sessionResources ?? []) {
8379
8755
  if (!providerFeatures?.session_resources.includes(resource.type)) {
@@ -8402,7 +8778,8 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
8402
8778
  identity_id: identityId,
8403
8779
  files: (options.files ?? []).map((file) => ({ file_id: file.fileId, mount_path: file.mountPath })),
8404
8780
  title: options.title,
8405
- metadata: options.metadata
8781
+ metadata: options.metadata,
8782
+ environment_variables: environmentVariables
8406
8783
  };
8407
8784
  }
8408
8785
  const agentId = requireRef(state, { type: "agent", name: agentName, provider });
@@ -8447,7 +8824,8 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
8447
8824
  files: (options.files ?? []).map((f) => ({ file_id: f.fileId, mount_path: f.mountPath })),
8448
8825
  resources: sessionResources,
8449
8826
  title: options.title,
8450
- metadata: options.metadata
8827
+ metadata: options.metadata,
8828
+ environment_variables: environmentVariables
8451
8829
  };
8452
8830
  }
8453
8831
  function resolveTunnelId(agent, config, options, provider) {
@@ -8848,7 +9226,8 @@ async function createSessionForAgent(ctx, options = {}) {
8848
9226
  files: options.files,
8849
9227
  resources: options.resources,
8850
9228
  title: options.title,
8851
- metadata: options.metadata
9229
+ metadata: options.metadata,
9230
+ environmentVariables: options.environmentVariables
8852
9231
  });
8853
9232
  const session = await adapter2.createSession(bindings);
8854
9233
  return { agentName, provider, session };
@@ -8867,7 +9246,8 @@ async function startSessionRun(ctx, prompt, options = {}) {
8867
9246
  files: options.files,
8868
9247
  resources: options.resources,
8869
9248
  title: options.title,
8870
- metadata: options.metadata
9249
+ metadata: options.metadata,
9250
+ environmentVariables: options.environmentVariables
8871
9251
  });
8872
9252
  const session = await adapter2.createSession(bindings);
8873
9253
  return {
@@ -9327,6 +9707,7 @@ var StateManager = class _StateManager {
9327
9707
  desired_readiness_baseline: r.desired_readiness_baseline,
9328
9708
  remote_hash: r.remote_hash,
9329
9709
  remote_snapshot: r.remote_snapshot,
9710
+ replacement_fingerprint: r.replacement_fingerprint,
9330
9711
  drift_paths: r.drift_paths,
9331
9712
  drift_status: r.drift_status
9332
9713
  }));
@@ -9528,6 +9909,8 @@ var PlanReadinessImpactSchema = z6.enum(["none", "non_blocking", "blocking"]);
9528
9909
  var PlannedActionSchema = z6.object({
9529
9910
  action: ActionTypeSchema,
9530
9911
  address: ResourceAddressSchema,
9912
+ /** Existing state address to retain when this action is an inferred logical rename. */
9913
+ previousAddress: ResourceAddressSchema.optional(),
9531
9914
  reason: z6.string(),
9532
9915
  driftKind: DriftKindSchema.optional(),
9533
9916
  readinessImpact: PlanReadinessImpactSchema.optional(),
@@ -105,6 +105,22 @@ declare const PlannedActionSchema: z.ZodObject<{
105
105
  name: z.ZodString;
106
106
  provider: z.ZodString;
107
107
  }, z.core.$strip>;
108
+ previousAddress: z.ZodOptional<z.ZodObject<{
109
+ type: z.ZodEnum<{
110
+ file: "file";
111
+ memory_store: "memory_store";
112
+ environment: "environment";
113
+ vault: "vault";
114
+ skill: "skill";
115
+ agent: "agent";
116
+ template: "template";
117
+ deployment: "deployment";
118
+ identity: "identity";
119
+ channel: "channel";
120
+ }>;
121
+ name: z.ZodString;
122
+ provider: z.ZodString;
123
+ }, z.core.$strip>>;
108
124
  reason: z.ZodString;
109
125
  driftKind: z.ZodOptional<z.ZodEnum<{
110
126
  none: "none";
@@ -265,6 +281,22 @@ declare const AgentReadinessSchema: z.ZodObject<{
265
281
  name: z.ZodString;
266
282
  provider: z.ZodString;
267
283
  }, z.core.$strip>;
284
+ previousAddress: z.ZodOptional<z.ZodObject<{
285
+ type: z.ZodEnum<{
286
+ file: "file";
287
+ memory_store: "memory_store";
288
+ environment: "environment";
289
+ vault: "vault";
290
+ skill: "skill";
291
+ agent: "agent";
292
+ template: "template";
293
+ deployment: "deployment";
294
+ identity: "identity";
295
+ channel: "channel";
296
+ }>;
297
+ name: z.ZodString;
298
+ provider: z.ZodString;
299
+ }, z.core.$strip>>;
268
300
  reason: z.ZodString;
269
301
  driftKind: z.ZodOptional<z.ZodEnum<{
270
302
  none: "none";
@@ -399,6 +431,22 @@ declare const AgentWithReadinessSchema: z.ZodObject<{
399
431
  name: z.ZodString;
400
432
  provider: z.ZodString;
401
433
  }, z.core.$strip>;
434
+ previousAddress: z.ZodOptional<z.ZodObject<{
435
+ type: z.ZodEnum<{
436
+ file: "file";
437
+ memory_store: "memory_store";
438
+ environment: "environment";
439
+ vault: "vault";
440
+ skill: "skill";
441
+ agent: "agent";
442
+ template: "template";
443
+ deployment: "deployment";
444
+ identity: "identity";
445
+ channel: "channel";
446
+ }>;
447
+ name: z.ZodString;
448
+ provider: z.ZodString;
449
+ }, z.core.$strip>>;
402
450
  reason: z.ZodString;
403
451
  driftKind: z.ZodOptional<z.ZodEnum<{
404
452
  none: "none";
@@ -561,6 +609,22 @@ declare const AgentSyncResultSchema: z.ZodObject<{
561
609
  name: z.ZodString;
562
610
  provider: z.ZodString;
563
611
  }, z.core.$strip>;
612
+ previousAddress: z.ZodOptional<z.ZodObject<{
613
+ type: z.ZodEnum<{
614
+ file: "file";
615
+ memory_store: "memory_store";
616
+ environment: "environment";
617
+ vault: "vault";
618
+ skill: "skill";
619
+ agent: "agent";
620
+ template: "template";
621
+ deployment: "deployment";
622
+ identity: "identity";
623
+ channel: "channel";
624
+ }>;
625
+ name: z.ZodString;
626
+ provider: z.ZodString;
627
+ }, z.core.$strip>>;
564
628
  reason: z.ZodString;
565
629
  driftKind: z.ZodOptional<z.ZodEnum<{
566
630
  none: "none";
@@ -632,6 +696,22 @@ declare const AgentSyncRunSchema: z.ZodObject<{
632
696
  name: z.ZodString;
633
697
  provider: z.ZodString;
634
698
  }, z.core.$strip>;
699
+ previousAddress: z.ZodOptional<z.ZodObject<{
700
+ type: z.ZodEnum<{
701
+ file: "file";
702
+ memory_store: "memory_store";
703
+ environment: "environment";
704
+ vault: "vault";
705
+ skill: "skill";
706
+ agent: "agent";
707
+ template: "template";
708
+ deployment: "deployment";
709
+ identity: "identity";
710
+ channel: "channel";
711
+ }>;
712
+ name: z.ZodString;
713
+ provider: z.ZodString;
714
+ }, z.core.$strip>>;
635
715
  reason: z.ZodString;
636
716
  driftKind: z.ZodOptional<z.ZodEnum<{
637
717
  none: "none";
@@ -712,6 +792,22 @@ declare const AgentSyncRunSchema: z.ZodObject<{
712
792
  name: z.ZodString;
713
793
  provider: z.ZodString;
714
794
  }, z.core.$strip>;
795
+ previousAddress: z.ZodOptional<z.ZodObject<{
796
+ type: z.ZodEnum<{
797
+ file: "file";
798
+ memory_store: "memory_store";
799
+ environment: "environment";
800
+ vault: "vault";
801
+ skill: "skill";
802
+ agent: "agent";
803
+ template: "template";
804
+ deployment: "deployment";
805
+ identity: "identity";
806
+ channel: "channel";
807
+ }>;
808
+ name: z.ZodString;
809
+ provider: z.ZodString;
810
+ }, z.core.$strip>>;
715
811
  reason: z.ZodString;
716
812
  driftKind: z.ZodOptional<z.ZodEnum<{
717
813
  none: "none";
@@ -768,6 +864,22 @@ declare const AgentSyncRunSchema: z.ZodObject<{
768
864
  name: z.ZodString;
769
865
  provider: z.ZodString;
770
866
  }, z.core.$strip>;
867
+ previousAddress: z.ZodOptional<z.ZodObject<{
868
+ type: z.ZodEnum<{
869
+ file: "file";
870
+ memory_store: "memory_store";
871
+ environment: "environment";
872
+ vault: "vault";
873
+ skill: "skill";
874
+ agent: "agent";
875
+ template: "template";
876
+ deployment: "deployment";
877
+ identity: "identity";
878
+ channel: "channel";
879
+ }>;
880
+ name: z.ZodString;
881
+ provider: z.ZodString;
882
+ }, z.core.$strip>>;
771
883
  reason: z.ZodString;
772
884
  driftKind: z.ZodOptional<z.ZodEnum<{
773
885
  none: "none";
@@ -1,4 +1,4 @@
1
- import { P as ProviderSessionEvent, an as SessionEvent } from './session-event-CxLg_XqS.js';
1
+ import { P as ProviderSessionEvent, an as SessionEvent } from './session-event-mzuENtyH.js';
2
2
  import 'zod';
3
3
 
4
4
  declare function sanitizeSessionEvents(events: ProviderSessionEvent[], options?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openagentpack/sdk",
3
- "version": "0.3.1-beta-85cd4b6-20260727",
3
+ "version": "0.3.2",
4
4
  "description": "OpenAgentPack SDK (Node-compatible runtime)",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [