@openagentpack/sdk 0.3.2 → 0.4.0

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.js CHANGED
@@ -58,10 +58,10 @@ var BaseApiClient = class {
58
58
  if (this.isConflict(res.status, body)) throw new ConflictError(res.status, body, this.errorPrefix);
59
59
  throw new ApiError(res.status, body, this.errorPrefix);
60
60
  }
61
- async post(path, body) {
61
+ async post(path, body, options) {
62
62
  const res = await resolveFetch()(`${this.baseUrl}${path}`, {
63
63
  method: "POST",
64
- headers: this.headers(),
64
+ headers: { ...this.headers(), ...options?.headers },
65
65
  body: JSON.stringify(body)
66
66
  });
67
67
  await this.throwIfError(res);
@@ -2059,6 +2059,13 @@ function mapEnvironment2(name, decl, projectName) {
2059
2059
  metadata: injectMetadata(decl.metadata, projectName, name)
2060
2060
  };
2061
2061
  }
2062
+ function mapForwardEnvironment(name, decl, projectName) {
2063
+ const body = mapEnvironment2(name, decl, projectName);
2064
+ const config = { ...body.config };
2065
+ delete config.networking;
2066
+ body.config = config;
2067
+ return body;
2068
+ }
2062
2069
  function mapVault(name, decl, projectName) {
2063
2070
  const body = { display_name: decl.display_name };
2064
2071
  if (projectName) body.metadata = injectMetadata(decl.metadata, projectName, name);
@@ -2373,10 +2380,12 @@ function mapForwardTemplate(name, decl, refs, projectName) {
2373
2380
  environment_id: refs.environment_id,
2374
2381
  vault_ids: refs.vault_ids
2375
2382
  };
2383
+ body.files = Object.fromEntries((refs.file_ids ?? []).map((id) => [id, { enabled: true }]));
2376
2384
  if (refs.tunnel_id) body.tunnel_id = refs.tunnel_id;
2377
2385
  if (projectName) body.metadata = injectMetadata(decl.metadata, projectName, name);
2378
2386
  else body.metadata = decl.metadata ?? {};
2379
2387
  if (decl.environment_variables) body.environment_variables = decl.environment_variables;
2388
+ if (decl.managed_tool_config) body.managed_tool_config = decl.managed_tool_config;
2380
2389
  if (decl.tools) {
2381
2390
  body.tools = [
2382
2391
  {
@@ -2565,6 +2574,7 @@ var QoderAdapter = class _QoderAdapter {
2565
2574
  client;
2566
2575
  memoryApi;
2567
2576
  forwardClient;
2577
+ forwardMemoryApi;
2568
2578
  projectName;
2569
2579
  forwardSessionIds = /* @__PURE__ */ new Set();
2570
2580
  constructor(apiKey, gateway, projectName, forwardGateway) {
@@ -2586,6 +2596,19 @@ var QoderAdapter = class _QoderAdapter {
2586
2596
  apiKey,
2587
2597
  gateway: forwardGateway ?? deriveForwardGateway(gateway)
2588
2598
  });
2599
+ this.forwardMemoryApi = new ProviderMemoryApi(this.forwardClient, {
2600
+ pathStyle: "relative",
2601
+ cursorParam: "after_id",
2602
+ updatePrecondition: "content_sha256",
2603
+ prefixParam: "prefix",
2604
+ versionsSegment: "versions",
2605
+ storeMetadataMode: "merge_patch",
2606
+ supportsView: false,
2607
+ supportsMemoryMetadata: true,
2608
+ supportsPathUpdate: false,
2609
+ supportsDeletePrecondition: false,
2610
+ supportsIncludeArchived: true
2611
+ });
2589
2612
  this.projectName = projectName ?? "";
2590
2613
  }
2591
2614
  async validate() {
@@ -2600,7 +2623,7 @@ var QoderAdapter = class _QoderAdapter {
2600
2623
  file: "/files",
2601
2624
  deployment: "/deployments"
2602
2625
  };
2603
- async findResource(type, name, id) {
2626
+ async findResource(type, name, id, mode) {
2604
2627
  if (type === "template") {
2605
2628
  const raw2 = await locateRemote(this.forwardClient, "/templates", name, id, (item) => item.status !== "archived");
2606
2629
  return raw2 ? toRemoteResource(raw2) : null;
@@ -2620,7 +2643,14 @@ var QoderAdapter = class _QoderAdapter {
2620
2643
  const raw2 = await locateRemote(this.forwardClient, "/channels", name, id, () => true);
2621
2644
  return raw2 ? toRemoteResource(raw2) : null;
2622
2645
  }
2623
- const raw = await locateRemote(this.client, _QoderAdapter.ENDPOINT_MAP[type], name, id, notArchived);
2646
+ if (type === "environment" && id && mode === "auto") {
2647
+ const managed = await locateRemote(this.client, "/environments", name, id, notArchived);
2648
+ if (managed) return toRemoteResource(managed);
2649
+ const forward = await locateRemote(this.forwardClient, "/environments", name, id, notArchived);
2650
+ return forward ? toRemoteResource(forward) : null;
2651
+ }
2652
+ const client = mode === "forward" && (type === "environment" || type === "skill" || type === "vault" || type === "memory_store" || type === "file") ? this.forwardClient : this.client;
2653
+ const raw = await locateRemote(client, _QoderAdapter.ENDPOINT_MAP[type], name, id, notArchived);
2624
2654
  return raw ? toRemoteResource(raw) : null;
2625
2655
  }
2626
2656
  async listAgents(filter) {
@@ -2764,16 +2794,21 @@ var QoderAdapter = class _QoderAdapter {
2764
2794
  }
2765
2795
  if (type === "channel") {
2766
2796
  const channelConfig = raw.channel_config ?? {};
2767
- return compactDeep({
2768
- identity_id: raw.identity_id,
2769
- template_id: raw.template_id,
2797
+ const mode = raw.identity_resolution?.mode ?? "fixed";
2798
+ const normalized = {
2799
+ identity_resolution: { mode },
2770
2800
  channel_type: raw.channel_type,
2771
2801
  name: raw.name,
2772
2802
  enabled: raw.enabled,
2773
2803
  channel_config: {
2774
2804
  response_options: channelConfig.response_options ?? {}
2775
2805
  }
2776
- });
2806
+ };
2807
+ if (mode === "fixed") {
2808
+ normalized.identity_id = raw.identity_id;
2809
+ normalized.template_id = raw.template_id;
2810
+ }
2811
+ return compactDeep(normalized);
2777
2812
  }
2778
2813
  return compactDeep({
2779
2814
  description: raw.description,
@@ -2784,24 +2819,29 @@ var QoderAdapter = class _QoderAdapter {
2784
2819
  metadata: stripAgentsMetadata(raw.metadata)
2785
2820
  });
2786
2821
  }
2787
- async createEnvironment(name, decl) {
2788
- const body = mapEnvironment2(name, decl, this.projectName);
2789
- const res = await this.client.post("/environments", body);
2822
+ async createEnvironment(name, decl, mode = "managed") {
2823
+ const body = mode === "forward" ? mapForwardEnvironment(name, decl, this.projectName) : mapEnvironment2(name, decl, this.projectName);
2824
+ const res = await (mode === "forward" ? this.forwardClient : this.client).post("/environments", body);
2790
2825
  return toRemoteResource(res);
2791
2826
  }
2792
- async updateEnvironment(id, name, decl) {
2793
- const body = mapEnvironment2(name, decl, this.projectName);
2794
- const current = await this.client.get(`/environments/${id}`);
2827
+ async updateEnvironment(id, name, decl, mode = "managed") {
2828
+ const body = mode === "forward" ? mapForwardEnvironment(name, decl, this.projectName) : mapEnvironment2(name, decl, this.projectName);
2829
+ const client = mode === "forward" ? this.forwardClient : this.client;
2830
+ const current = await client.get(`/environments/${id}`);
2795
2831
  const currentMetadata = current.metadata ?? {};
2796
2832
  const metadata = { ...body.metadata ?? {} };
2797
2833
  for (const key of Object.keys(currentMetadata)) {
2798
2834
  if (!key.startsWith("agents.") && !(key in metadata)) metadata[key] = null;
2799
2835
  }
2800
2836
  body.metadata = metadata;
2801
- const res = await this.client.post(`/environments/${id}`, body);
2837
+ const res = await client.post(`/environments/${id}`, body);
2802
2838
  return toRemoteResource(res);
2803
2839
  }
2804
- async deleteEnvironment(id, cascade = false) {
2840
+ async deleteEnvironment(id, cascade = false, mode = "managed") {
2841
+ if (mode === "forward") {
2842
+ await this.forwardClient.delete(`/environments/${id}`);
2843
+ return;
2844
+ }
2805
2845
  try {
2806
2846
  await this.client.delete(`/environments/${id}`);
2807
2847
  return;
@@ -2828,17 +2868,18 @@ var QoderAdapter = class _QoderAdapter {
2828
2868
  await this.client.post(`/environments/${id}/archive`, {});
2829
2869
  }
2830
2870
  }
2831
- async createVault(name, decl) {
2871
+ async createVault(name, decl, mode = "managed") {
2872
+ const client = mode === "forward" ? this.forwardClient : this.client;
2832
2873
  const body = mapVault(name, decl, this.projectName);
2833
- const res = await this.client.post("/vaults", body);
2874
+ const res = await client.post("/vaults", body);
2834
2875
  const vaultId = res.id;
2835
2876
  for (const cred of decl.credentials ?? []) {
2836
- await this.client.post(`/vaults/${vaultId}/credentials`, mapCredential(cred));
2877
+ await client.post(`/vaults/${vaultId}/credentials`, mapCredential(cred));
2837
2878
  }
2838
2879
  return toRemoteResource(res);
2839
2880
  }
2840
- async deleteVault(id) {
2841
- await this.client.delete(`/vaults/${id}`);
2881
+ async deleteVault(id, mode = "managed") {
2882
+ await (mode === "forward" ? this.forwardClient : this.client).delete(`/vaults/${id}`);
2842
2883
  }
2843
2884
  async exportResources(type) {
2844
2885
  return exportRemoteResources(this.client, type, {
@@ -2849,17 +2890,24 @@ var QoderAdapter = class _QoderAdapter {
2849
2890
  agentToDecl: agentToDecl2
2850
2891
  });
2851
2892
  }
2852
- async createSkill(name, decl, files) {
2893
+ async createSkill(name, decl, files, mode = "managed") {
2853
2894
  const formData = await buildSkillFormData(name, decl, files);
2854
- const res = await this.client.postFormData("/skills", formData);
2895
+ const res = await (mode === "forward" ? this.forwardClient : this.client).postFormData(
2896
+ "/skills",
2897
+ formData
2898
+ );
2855
2899
  return toRemoteResource(res);
2856
2900
  }
2857
- async updateSkill(id, name, decl, files) {
2858
- await this.client.delete(`/skills/${id}`);
2859
- return this.createSkill(name, decl, files);
2901
+ async updateSkill(id, name, decl, files, mode = "managed") {
2902
+ const client = mode === "forward" ? this.forwardClient : this.client;
2903
+ const packageName = skillNameFromFiles(files) ?? name;
2904
+ const formData = await buildSkillFormData(packageName, decl, files, "files", false, true);
2905
+ await client.postFormData(`/skills/${id}/versions`, formData);
2906
+ const current = await client.get(`/skills/${id}`);
2907
+ return toRemoteResource(current);
2860
2908
  }
2861
- async deleteSkill(id) {
2862
- await this.client.delete(`/skills/${id}`);
2909
+ async deleteSkill(id, mode = "managed") {
2910
+ await (mode === "forward" ? this.forwardClient : this.client).delete(`/skills/${id}`);
2863
2911
  }
2864
2912
  async createAgent(name, decl, refs) {
2865
2913
  const body = mapAgent2(name, decl, refs, void 0, this.projectName);
@@ -2876,21 +2924,89 @@ var QoderAdapter = class _QoderAdapter {
2876
2924
  await this.client.delete(`/agents/${id}`);
2877
2925
  }
2878
2926
  async createTemplate(name, decl, refs) {
2879
- await this.registerForwardVaults(refs.vault_ids);
2880
2927
  const body = mapForwardTemplate(name, decl, refs, this.projectName);
2881
2928
  const res = await this.forwardClient.post("/templates", body);
2929
+ await this.reconcileForwardMemoryMounts(res.id, refs);
2882
2930
  return toRemoteResource(res);
2883
2931
  }
2884
2932
  async updateTemplate(id, name, decl, refs) {
2885
- await this.registerForwardVaults(refs.vault_ids);
2886
2933
  const body = mapForwardTemplate(name, decl, refs, this.projectName);
2887
2934
  if (!refs.tunnel_id) body.tunnel_id = null;
2888
2935
  const res = await this.forwardClient.post(`/templates/${id}`, body);
2936
+ await this.reconcileForwardMemoryMounts(id, refs);
2889
2937
  return toRemoteResource(res);
2890
2938
  }
2891
- async archiveTemplate(id) {
2939
+ async archiveTemplate(id, ownedMemoryStoreIds = []) {
2940
+ const owned = new Set(ownedMemoryStoreIds);
2941
+ if (owned.size > 0) {
2942
+ for (const identity of await this.forwardClient.getAllPaged("/identities")) {
2943
+ if (typeof identity.id !== "string") continue;
2944
+ const path = `/identities/${identity.id}/templates/${id}/memory_stores`;
2945
+ try {
2946
+ const mounts = await this.forwardClient.get(path);
2947
+ for (const mount of mounts.data ?? []) {
2948
+ const storeId = mount.memory_store_id;
2949
+ if (mount.system_managed !== true && typeof storeId === "string" && owned.has(storeId)) {
2950
+ await this.forwardClient.delete(`${path}/${storeId}`);
2951
+ }
2952
+ }
2953
+ } catch (error) {
2954
+ if (!ApiError.isNotFound(error)) throw error;
2955
+ }
2956
+ }
2957
+ }
2892
2958
  await this.forwardClient.post(`/templates/${id}/archive`, {});
2893
2959
  }
2960
+ async reconcileForwardMemoryMounts(templateId, refs) {
2961
+ if (refs.memory_store_ids === void 0) return;
2962
+ const desired = new Set(refs.memory_store_ids ?? []);
2963
+ if (!refs.identity_id) {
2964
+ if (desired.size > 0) throw new UserError("Qoder Forward Memory Store mounts require an Identity.");
2965
+ return;
2966
+ }
2967
+ const path = `/identities/${refs.identity_id}/templates/${templateId}/memory_stores`;
2968
+ const current = await this.forwardClient.get(path);
2969
+ const explicit = (current.data ?? []).filter((mount) => mount.system_managed !== true);
2970
+ const owned = new Set(refs.owned_memory_store_ids ?? refs.memory_store_ids ?? []);
2971
+ for (const mount of explicit) {
2972
+ const storeId = mount.memory_store_id;
2973
+ if (typeof storeId === "string" && owned.has(storeId) && !desired.has(storeId)) {
2974
+ await this.forwardClient.delete(`${path}/${storeId}`);
2975
+ }
2976
+ }
2977
+ const mounted = new Set(
2978
+ explicit.map((mount) => mount.memory_store_id).filter((id) => typeof id === "string")
2979
+ );
2980
+ for (const memoryStoreId of desired) {
2981
+ if (!mounted.has(memoryStoreId)) await this.forwardClient.post(path, { memory_store_id: memoryStoreId });
2982
+ }
2983
+ }
2984
+ async reconcileDefaultMemoryStore(identityId, templateId, desired) {
2985
+ const storeId = await this.findDefaultMemoryStoreId(identityId, templateId);
2986
+ if (!storeId) return { status: "pending" };
2987
+ const current = await this.forwardClient.get(`/memory_stores/${storeId}`);
2988
+ const descriptionChanged = desired.description !== void 0 && current.description !== desired.description;
2989
+ if (current.name === desired.name && !descriptionChanged) {
2990
+ return { status: "unchanged", memory_store_id: storeId };
2991
+ }
2992
+ await this.forwardClient.post(`/memory_stores/${storeId}`, {
2993
+ name: desired.name,
2994
+ ...desired.description !== void 0 ? { description: desired.description } : {}
2995
+ });
2996
+ return { status: "updated", memory_store_id: storeId };
2997
+ }
2998
+ async findDefaultMemoryStoreId(identityId, templateId) {
2999
+ const mounts = await this.forwardClient.get(
3000
+ `/identities/${identityId}/templates/${templateId}/memory_stores`
3001
+ );
3002
+ const writableDefault = (mounts.data ?? []).find(
3003
+ (mount) => mount.system_managed === true && mount.access === "read_write"
3004
+ );
3005
+ return typeof writableDefault?.memory_store_id === "string" ? writableDefault.memory_store_id : null;
3006
+ }
3007
+ async deleteDefaultMemoryStore(id) {
3008
+ await this.client.delete(`/memory_stores/${id}`);
3009
+ }
2894
3010
  async createIdentity(name, decl) {
2895
3011
  if (decl.identity_id) return { id: decl.identity_id, type: "identity" };
2896
3012
  const res = await this.forwardClient.post("/identities", {
@@ -2927,12 +3043,14 @@ var QoderAdapter = class _QoderAdapter {
2927
3043
  }
2928
3044
  async updateChannel(id, name, decl, refs) {
2929
3045
  const current = await this.forwardClient.get(`/channels/${id}`);
2930
- if (current.channel_type !== decl.type) {
3046
+ const currentMode = current.identity_resolution?.mode ?? "fixed";
3047
+ if (current.channel_type !== decl.type || currentMode !== (decl.mode ?? "fixed")) {
2931
3048
  await this.deleteChannel(id);
2932
3049
  return this.createChannel(name, decl, refs);
2933
3050
  }
2934
3051
  const body = this.mapChannel(name, decl, refs);
2935
3052
  delete body.channel_type;
3053
+ delete body.identity_resolution;
2936
3054
  const res = await this.forwardClient.post(`/channels/${id}`, body);
2937
3055
  return toRemoteResource(res);
2938
3056
  }
@@ -2940,9 +3058,8 @@ var QoderAdapter = class _QoderAdapter {
2940
3058
  await this.forwardClient.delete(`/channels/${id}`);
2941
3059
  }
2942
3060
  mapChannel(name, decl, refs) {
2943
- return {
2944
- identity_id: refs.identity_id,
2945
- template_id: refs.agent_id,
3061
+ const mode = decl.mode ?? "fixed";
3062
+ const body = {
2946
3063
  channel_type: decl.type,
2947
3064
  name: decl.name ?? name,
2948
3065
  enabled: decl.enabled ?? true,
@@ -2955,22 +3072,27 @@ var QoderAdapter = class _QoderAdapter {
2955
3072
  }
2956
3073
  }
2957
3074
  };
2958
- }
2959
- async registerForwardVaults(vaultIds) {
2960
- for (const id of vaultIds) {
2961
- await this.forwardClient.post("/resources/registry", {
2962
- type: "vault",
2963
- resource: { id }
2964
- });
3075
+ if (mode === "pairing") {
3076
+ body.identity_resolution = { mode: "pairing" };
3077
+ } else {
3078
+ body.identity_id = refs.identity_id;
3079
+ body.template_id = refs.agent_id;
2965
3080
  }
3081
+ return body;
2966
3082
  }
2967
- async createMemoryStore(name, decl) {
3083
+ async createMemoryStore(name, decl, mode = "managed") {
2968
3084
  const body = mapMemoryStore2(name, decl);
2969
- const res = await this.client.post("/memory_stores", body);
3085
+ const client = mode === "forward" ? this.forwardClient : this.client;
3086
+ const memoryApi = mode === "forward" ? this.forwardMemoryApi : this.memoryApi;
3087
+ const res = await client.post(
3088
+ "/memory_stores",
3089
+ body,
3090
+ mode === "forward" ? { headers: { "Idempotency-Key": crypto.randomUUID() } } : void 0
3091
+ );
2970
3092
  const storeId = res.id;
2971
3093
  try {
2972
3094
  for (const entry of decl.entries ?? []) {
2973
- await this.memoryApi.createMemory(storeId, { content: entry.content, path: entry.key });
3095
+ await memoryApi.createMemory(storeId, { content: entry.content, path: entry.key });
2974
3096
  }
2975
3097
  } catch (error) {
2976
3098
  await this.client.delete(`/memory_stores/${storeId}`).catch(() => void 0);
@@ -2978,7 +3100,7 @@ var QoderAdapter = class _QoderAdapter {
2978
3100
  }
2979
3101
  return toRemoteResource(res);
2980
3102
  }
2981
- async deleteMemoryStore(id) {
3103
+ async deleteMemoryStore(id, _mode = "managed") {
2982
3104
  await this.client.delete(`/memory_stores/${id}`);
2983
3105
  }
2984
3106
  listMemoryStores(options) {
@@ -2987,23 +3109,23 @@ var QoderAdapter = class _QoderAdapter {
2987
3109
  getMemoryStore(id) {
2988
3110
  return this.memoryApi.getStore(id);
2989
3111
  }
2990
- updateMemoryStore(id, input) {
2991
- return this.memoryApi.updateStore(id, input);
3112
+ updateMemoryStore(id, input, mode = "managed") {
3113
+ return (mode === "forward" ? this.forwardMemoryApi : this.memoryApi).updateStore(id, input);
2992
3114
  }
2993
3115
  archiveMemoryStore(id) {
2994
3116
  return this.memoryApi.archiveStore(id);
2995
3117
  }
2996
- createMemory(storeId, input) {
2997
- return this.memoryApi.createMemory(storeId, input);
3118
+ createMemory(storeId, input, mode = "managed") {
3119
+ return (mode === "forward" ? this.forwardMemoryApi : this.memoryApi).createMemory(storeId, input);
2998
3120
  }
2999
- listMemories(storeId, options) {
3000
- return this.memoryApi.listMemories(storeId, options);
3121
+ listMemories(storeId, options, mode = "managed") {
3122
+ return (mode === "forward" ? this.forwardMemoryApi : this.memoryApi).listMemories(storeId, options);
3001
3123
  }
3002
3124
  getMemory(storeId, memoryId) {
3003
3125
  return this.memoryApi.getMemory(storeId, memoryId);
3004
3126
  }
3005
- updateMemory(storeId, memoryId, input) {
3006
- return this.memoryApi.updateMemory(storeId, memoryId, input);
3127
+ updateMemory(storeId, memoryId, input, mode = "managed") {
3128
+ return (mode === "forward" ? this.forwardMemoryApi : this.memoryApi).updateMemory(storeId, memoryId, input);
3007
3129
  }
3008
3130
  deleteMemory(storeId, memoryId, expected) {
3009
3131
  return this.memoryApi.deleteMemory(storeId, memoryId, expected);
@@ -3292,30 +3414,42 @@ var QoderAdapter = class _QoderAdapter {
3292
3414
  return res.data;
3293
3415
  }
3294
3416
  // --- Files ---
3295
- async uploadFile(filePath, options) {
3417
+ async uploadFile(filePath, options, mode = "managed") {
3418
+ if (mode === "forward" && options?.purpose && !["user_upload", "session_resource"].includes(options.purpose)) {
3419
+ throw new UserError(
3420
+ `Qoder Forward File purpose must be 'user_upload' or 'session_resource', got '${options.purpose}'.`
3421
+ );
3422
+ }
3296
3423
  const resolved = resolve2(filePath);
3297
3424
  const content = readFileSync2(resolved);
3298
3425
  const fileName = options?.name ?? basename2(resolved);
3299
- return this.uploadFileContent(new Uint8Array(content), fileName, {
3300
- purpose: options?.purpose
3301
- });
3426
+ const formData = buildFileFormData(new Uint8Array(content), fileName, { purpose: options?.purpose });
3427
+ const res = await (mode === "forward" ? this.forwardClient : this.client).postFormData(
3428
+ "/files",
3429
+ formData
3430
+ );
3431
+ return toRestFileInfo(res);
3302
3432
  }
3303
3433
  async uploadFileContent(content, filename, options) {
3304
- const formData = new FormData();
3305
- const bytes = new Uint8Array(content);
3306
- formData.append(
3307
- "file",
3308
- options?.mimeType ? new File([bytes], filename, { type: options.mimeType }) : new File([bytes], filename)
3309
- );
3310
- if (filename) formData.append("name", filename);
3311
- if (options?.purpose) formData.append("purpose", options.purpose);
3434
+ const formData = buildFileFormData(content, filename, options);
3312
3435
  const res = await this.client.postFormData("/files", formData);
3313
3436
  return toRestFileInfo(res);
3314
3437
  }
3315
- async deleteFile(id) {
3316
- await this.client.delete(`/files/${id}`);
3438
+ async deleteFile(id, mode = "managed") {
3439
+ await (mode === "forward" ? this.forwardClient : this.client).delete(`/files/${id}`);
3317
3440
  }
3318
3441
  };
3442
+ function buildFileFormData(content, filename, options) {
3443
+ const formData = new FormData();
3444
+ const bytes = new Uint8Array(content);
3445
+ formData.append(
3446
+ "file",
3447
+ options?.mimeType ? new File([bytes], filename, { type: options.mimeType }) : new File([bytes], filename)
3448
+ );
3449
+ if (filename) formData.append("name", filename);
3450
+ if (options?.purpose) formData.append("purpose", options.purpose);
3451
+ return formData;
3452
+ }
3319
3453
  function toSessionInfo2(res) {
3320
3454
  return buildSessionInfo(res, (r) => r.memory_store_ids ?? []);
3321
3455
  }
@@ -3365,22 +3499,24 @@ function normalizeQoderMcpServers(value) {
3365
3499
  });
3366
3500
  });
3367
3501
  }
3368
- async function buildSkillFormData(name, decl, files) {
3502
+ async function buildSkillFormData(name, decl, files, fileField = "file", includeCreateFields = true, prefixTopLevelDirectory = false) {
3369
3503
  const zip = new JSZip2();
3370
3504
  for (const f of files) {
3371
- zip.file(f.relativePath, f.content);
3505
+ zip.file(prefixTopLevelDirectory ? `${name}/${f.relativePath}` : f.relativePath, f.content);
3372
3506
  }
3373
3507
  const zipContent = await zip.generateAsync({ type: "uint8array" });
3374
3508
  const formData = new FormData();
3375
3509
  formData.append(
3376
- "file",
3510
+ fileField,
3377
3511
  new File([new Uint8Array(zipContent)], `${name}.zip`, {
3378
3512
  type: "application/zip"
3379
3513
  })
3380
3514
  );
3381
- formData.append("name", name);
3382
- formData.append("type", "custom");
3383
- if (decl.description) formData.append("description", decl.description);
3515
+ if (includeCreateFields) {
3516
+ formData.append("name", name);
3517
+ formData.append("type", "custom");
3518
+ if (decl.description) formData.append("description", decl.description);
3519
+ }
3384
3520
  return formData;
3385
3521
  }
3386
3522
 
@@ -4057,7 +4193,7 @@ var BailianAdapter = class _BailianAdapter {
4057
4193
  }
4058
4194
  async waitForFileReady(fileId) {
4059
4195
  const start = Date.now();
4060
- let delay2 = FILE_SCAN_BACKOFF.initial;
4196
+ let delay3 = FILE_SCAN_BACKOFF.initial;
4061
4197
  let logged = false;
4062
4198
  while (true) {
4063
4199
  const detail = await this.client.get(`/files/${fileId}`);
@@ -4076,8 +4212,8 @@ var BailianAdapter = class _BailianAdapter {
4076
4212
  );
4077
4213
  return;
4078
4214
  }
4079
- await new Promise((r) => setTimeout(r, delay2));
4080
- delay2 = Math.min(delay2 * FILE_SCAN_BACKOFF.factor, FILE_SCAN_BACKOFF.max);
4215
+ await new Promise((r) => setTimeout(r, delay3));
4216
+ delay3 = Math.min(delay3 * FILE_SCAN_BACKOFF.factor, FILE_SCAN_BACKOFF.max);
4081
4217
  }
4082
4218
  }
4083
4219
  // --- Vault (Vaults API) ---
@@ -5556,6 +5692,9 @@ var agentSkillRefSchema = z5.object({
5556
5692
  var agentDeliverySchema = z5.object({
5557
5693
  type: z5.enum(["managed", "forward"])
5558
5694
  });
5695
+ var managedToolConfigSchema = z5.object({
5696
+ enabled_tools: z5.array(z5.string().min(1))
5697
+ });
5559
5698
  var sessionGithubRepoResourceSchema = z5.object({
5560
5699
  type: z5.literal("github_repository"),
5561
5700
  url: z5.string().url(),
@@ -5577,19 +5716,27 @@ var agentSchema = z5.object({
5577
5716
  mcp_servers: z5.array(mcpServerSchema).optional(),
5578
5717
  skills: z5.array(z5.union([z5.string(), agentSkillRefSchema])).optional(),
5579
5718
  vault: z5.string().optional(),
5719
+ files: z5.array(z5.string()).optional(),
5580
5720
  memory_stores: z5.array(z5.string()).optional(),
5721
+ default_memory_store: z5.object({
5722
+ name: z5.string().trim().min(1).max(255),
5723
+ description: z5.string().max(1024).optional(),
5724
+ delete_on_destroy: z5.boolean().optional().default(false)
5725
+ }).optional(),
5581
5726
  resources: z5.array(sessionGithubRepoResourceSchema).optional(),
5582
5727
  multiagent: multiagentSchema.optional(),
5583
5728
  metadata: z5.record(z5.string(), z5.string()).optional(),
5584
5729
  environment_variables: z5.record(z5.string().min(1), z5.string()).optional(),
5730
+ managed_tool_config: managedToolConfigSchema.optional(),
5585
5731
  delivery: z5.record(z5.string(), agentDeliverySchema).optional()
5586
5732
  });
5587
5733
  var channelSchema = z5.object({
5588
5734
  provider: z5.string().optional(),
5589
- agent: z5.string().min(1),
5735
+ agent: z5.string().min(1).optional(),
5590
5736
  identity: z5.string().min(1).optional(),
5591
5737
  type: z5.string().min(1),
5592
5738
  name: z5.string().trim().min(1).optional(),
5739
+ mode: z5.enum(["fixed", "pairing"]).optional().default("fixed"),
5593
5740
  enabled: z5.boolean().optional(),
5594
5741
  credentials: z5.record(z5.string(), coerceString).optional(),
5595
5742
  options: z5.record(z5.string(), z5.unknown()).optional()
@@ -5828,15 +5975,24 @@ async function computeResourceHash(address, config, basePath, state) {
5828
5975
  if (!decl) return "";
5829
5976
  if (address.type === "skill") {
5830
5977
  const skillDecl = decl;
5978
+ const apiMode = resolveQoderApiMode(address.type, address.name, address.provider, config);
5831
5979
  if (basePath) {
5832
5980
  const fileHash = computeSkillContentHash(skillDecl.source, basePath);
5833
- return contentHash({ decl, fileHash });
5981
+ return contentHash({ decl, fileHash, apiMode });
5834
5982
  }
5983
+ return contentHash({ decl, apiMode });
5984
+ }
5985
+ if (address.type === "environment" || address.type === "vault" || address.type === "memory_store") {
5986
+ return contentHash({ decl, apiMode: resolveQoderApiMode(address.type, address.name, address.provider, config) });
5835
5987
  }
5836
5988
  if (address.type === "file" && basePath) {
5837
5989
  const fileDecl = decl;
5838
5990
  const fileHash = computeLocalFileContentHash(fileDecl.source, basePath);
5839
- return contentHash({ decl, fileHash });
5991
+ return contentHash({
5992
+ decl,
5993
+ fileHash,
5994
+ apiMode: resolveQoderApiMode("file", address.name, address.provider, config)
5995
+ });
5840
5996
  }
5841
5997
  if (address.type === "deployment") {
5842
5998
  const refs = resolveDeploymentReferenceIds(decl, config, address.provider, state);
@@ -5858,13 +6014,28 @@ async function computeResourceHash(address, config, basePath, state) {
5858
6014
  }
5859
6015
  return contentHash(decl);
5860
6016
  }
6017
+ function resolveQoderApiMode(type, name, provider, config) {
6018
+ if (provider !== "qoder") return void 0;
6019
+ if (type === "environment" && config.environments?.[name]?.environment_id) return "auto";
6020
+ for (const agent of Object.values(config.agents ?? {})) {
6021
+ if (agent.provider && agent.provider !== provider) continue;
6022
+ const referenced = type === "environment" ? agent.environment === name : type === "skill" ? agent.skills?.some(
6023
+ (skill) => typeof skill === "string" ? skill === name : skill.type === "custom" && skill.skill_id === name
6024
+ ) : type === "vault" ? agent.vault === name : type === "memory_store" ? agent.memory_stores?.includes(name) : agent.files?.includes(name);
6025
+ if (referenced && agent.delivery?.qoder?.type === "forward") return "forward";
6026
+ }
6027
+ return "managed";
6028
+ }
5861
6029
  function computeReplacementFingerprint(address, config) {
5862
6030
  if (address.type !== "channel") return void 0;
5863
6031
  const decl = config.channels?.[address.name];
5864
6032
  if (!decl) return void 0;
5865
- return contentHash({ channel_type: decl.type, credentials: decl.credentials ?? {} });
6033
+ return contentHash({ channel_type: decl.type, mode: decl.mode ?? "fixed", credentials: decl.credentials ?? {} });
5866
6034
  }
5867
6035
  function resolveChannelReferenceIds(decl, config, provider, state) {
6036
+ if (decl.mode === "pairing" || !decl.agent) {
6037
+ return { mode: "pairing" };
6038
+ }
5868
6039
  const agent = config.agents?.[decl.agent];
5869
6040
  const agentType = agent?.delivery?.[provider]?.type === "forward" ? "template" : "agent";
5870
6041
  const identity = decl.identity ?? config.defaults?.identity;
@@ -5887,7 +6058,14 @@ function resolveTemplateReferenceIds(decl, config, provider, state) {
5887
6058
  environment_id: environment?.environment_id ?? (decl.environment ? state?.getResource({ type: "environment", name: decl.environment, provider })?.remote_id ?? void 0 : void 0),
5888
6059
  tunnel_id: tunnel?.tunnel_id,
5889
6060
  vault_ids: decl.vault ? [state?.getResource({ type: "vault", name: decl.vault, provider })?.remote_id ?? decl.vault] : [],
5890
- skill_ids: skillIds
6061
+ skill_ids: skillIds,
6062
+ memory_store_ids: (decl.memory_stores ?? []).map(
6063
+ (memoryStore) => state?.getResource({ type: "memory_store", name: memoryStore, provider })?.remote_id ?? memoryStore
6064
+ ),
6065
+ file_ids: (decl.files ?? []).map(
6066
+ (file) => state?.getResource({ type: "file", name: file, provider })?.remote_id ?? file
6067
+ ),
6068
+ identity_id: decl.memory_stores?.length && config.defaults?.identity ? state?.getResource({ type: "identity", name: config.defaults.identity, provider })?.remote_id : void 0
5891
6069
  };
5892
6070
  }
5893
6071
  function resolveDeploymentReferenceIds(decl, config, provider, state) {
@@ -6067,11 +6245,21 @@ function resolveTemplateRefs(agentName, config, provider, state) {
6067
6245
  const environment = config.environments?.[agent.environment];
6068
6246
  if (!environment) throw new UserError(`Environment '${agent.environment}' is not defined in config.`);
6069
6247
  const agentRefs = resolveAgentRefs(agentName, config, provider, state);
6248
+ const memoryStoreIds = (agent.memory_stores ?? []).map(
6249
+ (memoryStore) => requireRef(state, { type: "memory_store", name: memoryStore, provider })
6250
+ );
6251
+ const identityName = config.defaults?.identity;
6070
6252
  return {
6071
6253
  ...agentRefs,
6072
6254
  environment_id: environment.environment_id ?? requireRef(state, { type: "environment", name: agent.environment, provider }),
6073
6255
  ...agent.tunnel ? { tunnel_id: resolveTunnelIdFromConfig(config, agent.tunnel, provider) } : {},
6074
- vault_ids: agent.vault ? [requireRef(state, { type: "vault", name: agent.vault, provider })] : []
6256
+ vault_ids: agent.vault ? [requireRef(state, { type: "vault", name: agent.vault, provider })] : [],
6257
+ file_ids: (agent.files ?? []).map((file) => requireRef(state, { type: "file", name: file, provider })),
6258
+ memory_store_ids: memoryStoreIds,
6259
+ owned_memory_store_ids: state.listResources().filter(
6260
+ (resource) => resource.address.provider === provider && resource.address.type === "memory_store" && resource.api_mode === "forward" && typeof resource.remote_id === "string"
6261
+ ).map((resource) => resource.remote_id),
6262
+ ...identityName ? { identity_id: requireRef(state, { type: "identity", name: identityName, provider }) } : {}
6075
6263
  };
6076
6264
  }
6077
6265
  function resolveDeploymentRefs(deploymentName, config, provider, state) {
@@ -6133,6 +6321,12 @@ function resolveDeploymentRefs(deploymentName, config, provider, state) {
6133
6321
  function resolveChannelRefs(channelName, config, provider, state) {
6134
6322
  const channel = config.channels?.[channelName];
6135
6323
  if (!channel) throw new UserError(`Channel '${channelName}' not found in config`);
6324
+ if (channel.mode === "pairing") {
6325
+ return {};
6326
+ }
6327
+ if (!channel.agent) {
6328
+ throw new UserError(`Channel '${channelName}' is fixed mode and must declare agent`);
6329
+ }
6136
6330
  const agent = config.agents?.[channel.agent];
6137
6331
  if (!agent) throw new UserError(`Channel '${channelName}' references unknown agent '${channel.agent}'`);
6138
6332
  const agentType = agent.delivery?.[provider]?.type === "forward" ? "template" : "agent";
@@ -6255,6 +6449,7 @@ async function executePlan(plan, ctx, options = {}) {
6255
6449
  await runWithConcurrency(level, concurrency, runAction);
6256
6450
  await ctx.state.save();
6257
6451
  }
6452
+ await reconcileDefaultMemoryStores(ctx, new Set(plan.actions.map((action) => action.address.provider)));
6258
6453
  for (const action of deletions) {
6259
6454
  await runAction(action);
6260
6455
  await ctx.state.save();
@@ -6265,6 +6460,41 @@ async function executePlan(plan, ctx, options = {}) {
6265
6460
  partial: results.some((r) => r.status === "failed")
6266
6461
  };
6267
6462
  }
6463
+ async function reconcileDefaultMemoryStores(ctx, plannedProviders) {
6464
+ const identityName = ctx.config.defaults?.identity;
6465
+ for (const [agentName, agent] of Object.entries(ctx.config.agents ?? {})) {
6466
+ const desired = agent.default_memory_store;
6467
+ if (!desired || agent.delivery?.qoder?.type !== "forward") continue;
6468
+ const providerName = "qoder";
6469
+ if (agent.provider && agent.provider !== providerName || !plannedProviders.has(providerName)) continue;
6470
+ const provider = ctx.providers.get(providerName);
6471
+ if (!provider?.reconcileDefaultMemoryStore || !identityName) continue;
6472
+ const identityId = ctx.state.getResource({
6473
+ type: "identity",
6474
+ name: identityName,
6475
+ provider: providerName
6476
+ })?.remote_id;
6477
+ const templateId = ctx.state.getResource({ type: "template", name: agentName, provider: providerName })?.remote_id;
6478
+ if (!identityId || !templateId) continue;
6479
+ const result = await provider.reconcileDefaultMemoryStore(identityId, templateId, desired);
6480
+ const resource = { type: "template", name: agentName, provider: providerName };
6481
+ if (result.status === "pending") {
6482
+ emitRuntimeFeedback(ctx.onFeedback, {
6483
+ type: "provider_wait",
6484
+ level: "warning",
6485
+ resource,
6486
+ message: `default memory store for template.${agentName} is pending \u2014 create the first Forward Session, then run apply again`
6487
+ });
6488
+ } else if (result.status === "updated") {
6489
+ emitRuntimeFeedback(ctx.onFeedback, {
6490
+ type: "resource_action_success",
6491
+ level: "success",
6492
+ resource,
6493
+ message: `updated default memory store for template.${agentName} to "${desired.name}"`
6494
+ });
6495
+ }
6496
+ }
6497
+ }
6268
6498
  async function runWithConcurrency(items, limit, worker) {
6269
6499
  if (items.length === 0) return;
6270
6500
  let cursor = 0;
@@ -6343,6 +6573,7 @@ async function executeActionInner(action, provider, ctx) {
6343
6573
  const existing = ctx.state.getResource(address);
6344
6574
  if (!existing) return false;
6345
6575
  const id = existing.remote_id;
6576
+ const apiMode2 = existing.api_mode;
6346
6577
  if (type === "environment" || type === "identity") {
6347
6578
  const externalReference = type === "environment" ? ctx.config.environments?.[name]?.environment_id : ctx.config.identities?.[name]?.identity_id;
6348
6579
  if (existing.externally_managed || externalReference) {
@@ -6354,13 +6585,13 @@ async function executeActionInner(action, provider, ctx) {
6354
6585
  try {
6355
6586
  switch (type) {
6356
6587
  case "environment":
6357
- await provider.deleteEnvironment(id);
6588
+ await provider.deleteEnvironment(id, false, apiMode2);
6358
6589
  break;
6359
6590
  case "vault":
6360
- await provider.deleteVault(id);
6591
+ await provider.deleteVault(id, apiMode2);
6361
6592
  break;
6362
6593
  case "skill":
6363
- await provider.deleteSkill(id);
6594
+ await provider.deleteSkill(id, apiMode2);
6364
6595
  break;
6365
6596
  case "agent":
6366
6597
  await provider.deleteAgent(id);
@@ -6368,17 +6599,17 @@ async function executeActionInner(action, provider, ctx) {
6368
6599
  case "template":
6369
6600
  if (!provider.archiveTemplate)
6370
6601
  throw new UserError(`Provider '${address.provider}' does not support templates`);
6371
- await provider.archiveTemplate(id);
6602
+ await provider.archiveTemplate(id, ownedForwardMemoryStoreIds(ctx, address.provider));
6372
6603
  break;
6373
6604
  case "memory_store":
6374
6605
  if (!provider.deleteMemoryStore) throw memoryStoreUnsupported(address.provider);
6375
- await provider.deleteMemoryStore(id);
6606
+ await provider.deleteMemoryStore(id, apiMode2);
6376
6607
  break;
6377
6608
  case "deployment":
6378
6609
  await provider.deleteDeployment(id);
6379
6610
  break;
6380
6611
  case "file":
6381
- await provider.deleteFile(id);
6612
+ await provider.deleteFile(id, apiMode2);
6382
6613
  break;
6383
6614
  case "identity":
6384
6615
  if (!provider.deleteIdentity)
@@ -6407,6 +6638,9 @@ async function executeActionInner(action, provider, ctx) {
6407
6638
  const isUpdate = action.action === "update";
6408
6639
  const priorAddress = action.previousAddress ?? address;
6409
6640
  const existingId = isUpdate ? ctx.state.getResource(priorAddress)?.remote_id : void 0;
6641
+ const apiMode = resolveResourceApiMode(type, name, address.provider, ctx.config);
6642
+ const priorApiMode = ctx.state.getResource(priorAddress)?.api_mode ?? (address.provider === "qoder" ? "managed" : void 0);
6643
+ const apiModeChanged = isUpdate && apiMode !== void 0 && priorApiMode !== apiMode;
6410
6644
  let result;
6411
6645
  switch (type) {
6412
6646
  case "environment": {
@@ -6421,6 +6655,17 @@ async function executeActionInner(action, provider, ctx) {
6421
6655
  resource: action.address,
6422
6656
  message: `${action.action} ${action.address.type}.${action.address.name} (${action.address.provider}) \u2014 external reference, no remote mutation`
6423
6657
  });
6658
+ } else if (apiModeChanged) {
6659
+ try {
6660
+ result = await provider.createEnvironment(remoteName, decl, apiMode);
6661
+ } catch (err) {
6662
+ result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
6663
+ mode: apiMode,
6664
+ onExisting: (existing) => provider.updateEnvironment(existing.id, remoteName, decl, apiMode)
6665
+ });
6666
+ adopted = true;
6667
+ }
6668
+ if (existingId) await provider.deleteEnvironment(existingId, false, priorApiMode);
6424
6669
  } else if (isUpdate) {
6425
6670
  const prior = ctx.state.getResource(address);
6426
6671
  if (prior?.externally_managed) {
@@ -6428,13 +6673,14 @@ async function executeActionInner(action, provider, ctx) {
6428
6673
  `environment.${name} is recorded as an external reference (${prior.remote_id ?? "unknown id"}); refusing to modify it remotely. Restore 'environment_id' to keep it as a reference, or release it first with 'agents state rm environment.${name}' (then 'agents state import' to adopt it as a managed resource).`
6429
6674
  );
6430
6675
  }
6431
- result = await provider.updateEnvironment(existingId, remoteName, decl);
6676
+ result = await provider.updateEnvironment(existingId, remoteName, decl, apiMode);
6432
6677
  } else {
6433
6678
  try {
6434
- result = await provider.createEnvironment(remoteName, decl);
6679
+ result = await provider.createEnvironment(remoteName, decl, apiMode);
6435
6680
  } catch (err) {
6436
6681
  result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
6437
- onExisting: (existing) => provider.updateEnvironment(existing.id, remoteName, decl)
6682
+ mode: apiMode,
6683
+ onExisting: (existing) => provider.updateEnvironment(existing.id, remoteName, decl, apiMode)
6438
6684
  });
6439
6685
  adopted = true;
6440
6686
  }
@@ -6443,22 +6689,26 @@ async function executeActionInner(action, provider, ctx) {
6443
6689
  }
6444
6690
  case "vault": {
6445
6691
  const decl = ctx.config.vaults[name];
6446
- if (isUpdate) {
6692
+ if (apiModeChanged) {
6693
+ result = await provider.createVault(name, decl, apiMode);
6694
+ if (existingId) await provider.deleteVault(existingId, priorApiMode).catch(() => void 0);
6695
+ } else if (isUpdate) {
6447
6696
  try {
6448
- result = await provider.createVault(name, decl);
6449
- await provider.deleteVault(existingId);
6697
+ result = await provider.createVault(name, decl, apiMode);
6698
+ await provider.deleteVault(existingId, apiMode);
6450
6699
  } catch {
6451
- await provider.deleteVault(existingId);
6452
- result = await provider.createVault(name, decl);
6700
+ await provider.deleteVault(existingId, apiMode);
6701
+ result = await provider.createVault(name, decl, apiMode);
6453
6702
  }
6454
6703
  } else {
6455
6704
  try {
6456
- result = await provider.createVault(name, decl);
6705
+ result = await provider.createVault(name, decl, apiMode);
6457
6706
  } catch (err) {
6458
6707
  result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
6708
+ mode: apiMode,
6459
6709
  onExisting: async (existing) => {
6460
- await provider.deleteVault(existing.id);
6461
- return provider.createVault(name, decl);
6710
+ await provider.deleteVault(existing.id, apiMode);
6711
+ return provider.createVault(name, decl, apiMode);
6462
6712
  }
6463
6713
  });
6464
6714
  adopted = true;
@@ -6482,12 +6732,15 @@ async function executeActionInner(action, provider, ctx) {
6482
6732
  }
6483
6733
  const remoteName = decl.name ?? name;
6484
6734
  const files = await resolveSkillFiles(decl, ctx);
6485
- if (isUpdate) {
6486
- result = await provider.updateSkill(existingId, remoteName, decl, files);
6735
+ if (apiModeChanged) {
6736
+ result = await provider.createSkill(remoteName, decl, files, apiMode);
6737
+ if (existingId) await provider.deleteSkill(existingId, priorApiMode).catch(() => void 0);
6738
+ } else if (isUpdate) {
6739
+ result = await provider.updateSkill(existingId, remoteName, decl, files, apiMode);
6487
6740
  } else {
6488
6741
  const manifestName = skillNameFromFiles(files);
6489
6742
  const searchNames = manifestName && manifestName !== remoteName ? [remoteName, manifestName] : [remoteName];
6490
- const existing = await findExistingByNames(provider, "skill", searchNames);
6743
+ const existing = await findExistingByNames(provider, "skill", searchNames, apiMode);
6491
6744
  if (existing) {
6492
6745
  result = existing.resource;
6493
6746
  emitRuntimeFeedback(ctx.onFeedback, {
@@ -6499,9 +6752,10 @@ async function executeActionInner(action, provider, ctx) {
6499
6752
  adopted = true;
6500
6753
  } else {
6501
6754
  try {
6502
- result = await provider.createSkill(remoteName, decl, files);
6755
+ result = await provider.createSkill(remoteName, decl, files, apiMode);
6503
6756
  } catch (err) {
6504
6757
  result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
6758
+ mode: apiMode,
6505
6759
  searchNames,
6506
6760
  onExisting: async (existing2) => existing2
6507
6761
  });
@@ -6520,15 +6774,19 @@ async function executeActionInner(action, provider, ctx) {
6520
6774
  throw memoryStoreUnsupported(address.provider);
6521
6775
  }
6522
6776
  const reconcile = async (storeId) => {
6523
- const store = await provider.updateMemoryStore(storeId, {
6524
- name,
6525
- description: decl.description,
6526
- metadata: decl.metadata ?? {}
6527
- });
6777
+ const store = await provider.updateMemoryStore(
6778
+ storeId,
6779
+ {
6780
+ name,
6781
+ description: decl.description,
6782
+ metadata: decl.metadata ?? {}
6783
+ },
6784
+ apiMode
6785
+ );
6528
6786
  const current = /* @__PURE__ */ new Map();
6529
6787
  let cursor;
6530
6788
  do {
6531
- const page2 = await provider.listMemories(storeId, { limit: 100, cursor, view: "basic" });
6789
+ const page2 = await provider.listMemories(storeId, { limit: 100, cursor, view: "basic" }, apiMode);
6532
6790
  for (const memory of page2.data) {
6533
6791
  if (memory.type === "memory") current.set(memory.path, memory);
6534
6792
  }
@@ -6538,24 +6796,33 @@ async function executeActionInner(action, provider, ctx) {
6538
6796
  const existing = current.get(entry.key.replace(/^\/+/, ""));
6539
6797
  if (existing) {
6540
6798
  if (existing.content_sha256 !== sha256(entry.content)) {
6541
- await provider.updateMemory(storeId, existing.id, {
6542
- content: entry.content,
6543
- expected_content_sha256: existing.content_sha256
6544
- });
6799
+ await provider.updateMemory(
6800
+ storeId,
6801
+ existing.id,
6802
+ {
6803
+ content: entry.content,
6804
+ expected_content_sha256: existing.content_sha256
6805
+ },
6806
+ apiMode
6807
+ );
6545
6808
  }
6546
6809
  } else {
6547
- await provider.createMemory(storeId, { path: entry.key, content: entry.content });
6810
+ await provider.createMemory(storeId, { path: entry.key, content: entry.content }, apiMode);
6548
6811
  }
6549
6812
  }
6550
6813
  return store;
6551
6814
  };
6552
- if (isUpdate) {
6815
+ if (apiModeChanged) {
6816
+ result = await createMemoryStore2(name, decl, apiMode);
6817
+ if (existingId) await deleteMemoryStore2(existingId, priorApiMode).catch(() => void 0);
6818
+ } else if (isUpdate) {
6553
6819
  result = await reconcile(existingId);
6554
6820
  } else {
6555
6821
  try {
6556
- result = await createMemoryStore2(name, decl);
6822
+ result = await createMemoryStore2(name, decl, apiMode);
6557
6823
  } catch (err) {
6558
6824
  result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
6825
+ mode: apiMode,
6559
6826
  onExisting: async (existing) => reconcile(existing.id)
6560
6827
  });
6561
6828
  adopted = true;
@@ -6705,15 +6972,12 @@ async function executeActionInner(action, provider, ctx) {
6705
6972
  const oldId = ctx.state.getResource(address)?.remote_id;
6706
6973
  if (oldId) {
6707
6974
  try {
6708
- await provider.deleteFile(oldId);
6975
+ await provider.deleteFile(oldId, priorApiMode);
6709
6976
  } catch {
6710
6977
  }
6711
6978
  }
6712
6979
  }
6713
- const info = await provider.uploadFile(filePath, {
6714
- name: decl.name,
6715
- purpose: decl.purpose
6716
- });
6980
+ const info = await provider.uploadFile(filePath, { name: decl.name, purpose: decl.purpose }, apiMode);
6717
6981
  result = { id: info.id, type: "file" };
6718
6982
  break;
6719
6983
  }
@@ -6734,6 +6998,7 @@ async function executeActionInner(action, provider, ctx) {
6734
6998
  address,
6735
6999
  remote_id: result.id,
6736
7000
  externally_managed: priorResource?.externally_managed || type === "environment" && ctx.config.environments?.[name]?.environment_id || type === "identity" && ctx.config.identities?.[name]?.identity_id ? true : void 0,
7001
+ api_mode: apiMode,
6737
7002
  version: result.version,
6738
7003
  content_hash: hash,
6739
7004
  desired_hash: hash,
@@ -6748,9 +7013,37 @@ async function executeActionInner(action, provider, ctx) {
6748
7013
  if (action.previousAddress) ctx.state.removeResource(action.previousAddress);
6749
7014
  return adopted;
6750
7015
  }
6751
- async function findExistingByNames(provider, type, names) {
7016
+ function ownedForwardMemoryStoreIds(ctx, provider) {
7017
+ return ctx.state.listResources().filter(
7018
+ (resource) => resource.address.provider === provider && resource.address.type === "memory_store" && resource.api_mode === "forward" && typeof resource.remote_id === "string"
7019
+ ).map((resource) => resource.remote_id);
7020
+ }
7021
+ function resolveResourceApiMode(type, name, provider, config) {
7022
+ if (provider !== "qoder" || type !== "environment" && type !== "skill" && type !== "vault" && type !== "memory_store" && type !== "file") {
7023
+ return void 0;
7024
+ }
7025
+ if (type === "environment" && config.environments?.[name]?.environment_id) return "auto";
7026
+ let managed = false;
7027
+ let forward = false;
7028
+ for (const agent of Object.values(config.agents ?? {})) {
7029
+ if (agent.provider && agent.provider !== provider) continue;
7030
+ const referenced = type === "environment" ? agent.environment === name : type === "skill" ? agent.skills?.some(
7031
+ (skill) => typeof skill === "string" ? skill === name : skill.type === "custom" && skill.skill_id === name
7032
+ ) : type === "vault" ? agent.vault === name : type === "memory_store" ? agent.memory_stores?.includes(name) : agent.files?.includes(name);
7033
+ if (!referenced) continue;
7034
+ if (agent.delivery?.qoder?.type === "forward") forward = true;
7035
+ else managed = true;
7036
+ }
7037
+ if (managed && forward) {
7038
+ throw new UserError(
7039
+ `Qoder ${type}.${name} is referenced by both Managed and Forward agents; declare separate resources for each API domain.`
7040
+ );
7041
+ }
7042
+ return forward ? "forward" : "managed";
7043
+ }
7044
+ async function findExistingByNames(provider, type, names, mode) {
6752
7045
  for (const candidate of names) {
6753
- const found = await provider.findResource(type, candidate);
7046
+ const found = await provider.findResource(type, candidate, void 0, mode);
6754
7047
  if (found && found.id !== null) return { resource: found, name: candidate };
6755
7048
  }
6756
7049
  return null;
@@ -6758,7 +7051,7 @@ async function findExistingByNames(provider, type, names) {
6758
7051
  async function adoptOnConflict(err, address, provider, onFeedback, opts) {
6759
7052
  if (!(err instanceof ConflictError)) throw err;
6760
7053
  const candidates = opts.searchNames?.length ? opts.searchNames : [address.name];
6761
- const existing = await findExistingByNames(provider, address.type, candidates);
7054
+ const existing = await findExistingByNames(provider, address.type, candidates, opts.mode);
6762
7055
  if (!existing) throw nameReservedError(err, address, candidates.join('" / "'));
6763
7056
  emitRuntimeFeedback(onFeedback, {
6764
7057
  type: "resource_adopted",
@@ -6847,6 +7140,13 @@ function agentTargetsProvider(config, agentProvider, providerName) {
6847
7140
  return Object.hasOwn(config.providers, providerName);
6848
7141
  }
6849
7142
 
7143
+ // src/internal/core/agent-materialization.ts
7144
+ function resolveAgentMaterialization(provider, agent) {
7145
+ const mode = agent.delivery?.[provider]?.type ?? "managed";
7146
+ if (mode === "managed") return { resourceType: "agent", mode };
7147
+ return { resourceType: "template", mode };
7148
+ }
7149
+
6850
7150
  // src/internal/core/validate-config.ts
6851
7151
  function validateProjectConfig(config, options = {}) {
6852
7152
  const collector = new DiagnosticCollector();
@@ -6873,6 +7173,7 @@ function collectReferenceDiagnostics(config, diagnostics) {
6873
7173
  const skillNames = new Set(Object.keys(config.skills ?? {}));
6874
7174
  const vaultNames = new Set(Object.keys(config.vaults ?? {}));
6875
7175
  const memoryNames = new Set(Object.keys(config.memory_stores ?? {}));
7176
+ const fileNames = new Set(Object.keys(config.files ?? {}));
6876
7177
  const agentNames = new Set(Object.keys(config.agents ?? {}));
6877
7178
  const identityNames = new Set(Object.keys(config.identities ?? {}));
6878
7179
  if (config.defaults?.identity && !identityNames.has(config.defaults.identity)) {
@@ -6900,6 +7201,11 @@ function collectReferenceDiagnostics(config, diagnostics) {
6900
7201
  if (agent.vault && !vaultNames.has(agent.vault)) {
6901
7202
  diagnostics.error("config.agent.vault.unknown", `agent.${name}: references unknown vault '${agent.vault}'`);
6902
7203
  }
7204
+ for (const file of agent.files ?? []) {
7205
+ if (!fileNames.has(file)) {
7206
+ diagnostics.error("config.agent.file.unknown", `agent.${name}: references unknown file '${file}'`);
7207
+ }
7208
+ }
6903
7209
  for (const memory of agent.memory_stores ?? []) {
6904
7210
  if (!memoryNames.has(memory)) {
6905
7211
  diagnostics.error(
@@ -6931,7 +7237,10 @@ function collectReferenceDiagnostics(config, diagnostics) {
6931
7237
  }
6932
7238
  }
6933
7239
  for (const [name, channel] of Object.entries(config.channels ?? {})) {
6934
- if (!agentNames.has(channel.agent)) {
7240
+ if (channel.mode === "pairing") continue;
7241
+ if (!channel.agent) {
7242
+ diagnostics.error("config.channel.agent.required", `channel.${name}: fixed-mode channels require agent`);
7243
+ } else if (!agentNames.has(channel.agent)) {
6935
7244
  diagnostics.error("config.channel.agent.unknown", `channel.${name}: references unknown agent '${channel.agent}'`);
6936
7245
  }
6937
7246
  const identity = channel.identity ?? config.defaults?.identity;
@@ -7011,29 +7320,31 @@ function collectProviderCapabilities(config, providers, diagnostics) {
7011
7320
  continue;
7012
7321
  }
7013
7322
  if (providerName === "qoder") {
7014
- const agent = config.agents?.[channel.agent];
7015
- if (agent?.provider && agent.provider !== providerName) {
7016
- diagnostics.error(
7017
- "config.channel.agent.provider_mismatch",
7018
- `channel.${name}: agent '${channel.agent}' is pinned to provider '${agent.provider}'.`,
7019
- { type: "channel", name, provider: providerName }
7020
- );
7021
- }
7022
- const identityName = channel.identity ?? config.defaults?.identity;
7023
- const identity = identityName ? config.identities?.[identityName] : void 0;
7024
- if (identity?.provider && identity.provider !== providerName) {
7025
- diagnostics.error(
7026
- "config.channel.identity.provider_mismatch",
7027
- `channel.${name}: identity '${identityName}' is pinned to provider '${identity.provider}'.`,
7028
- { type: "channel", name, provider: providerName }
7029
- );
7030
- }
7031
- if (agent && agent.delivery?.qoder?.type !== "forward") {
7032
- diagnostics.error(
7033
- "qoder.channel.forward_template.required",
7034
- `channel.${name}: Qoder Channels require agent '${channel.agent}' to use delivery.qoder.type: forward.`,
7035
- { type: "channel", name, provider: providerName }
7036
- );
7323
+ if (channel.mode !== "pairing" && channel.agent) {
7324
+ const agent = config.agents?.[channel.agent];
7325
+ if (agent?.provider && agent.provider !== providerName) {
7326
+ diagnostics.error(
7327
+ "config.channel.agent.provider_mismatch",
7328
+ `channel.${name}: agent '${channel.agent}' is pinned to provider '${agent.provider}'.`,
7329
+ { type: "channel", name, provider: providerName }
7330
+ );
7331
+ }
7332
+ const identityName = channel.identity ?? config.defaults?.identity;
7333
+ const identity = identityName ? config.identities?.[identityName] : void 0;
7334
+ if (identity?.provider && identity.provider !== providerName) {
7335
+ diagnostics.error(
7336
+ "config.channel.identity.provider_mismatch",
7337
+ `channel.${name}: identity '${identityName}' is pinned to provider '${identity.provider}'.`,
7338
+ { type: "channel", name, provider: providerName }
7339
+ );
7340
+ }
7341
+ if (agent && agent.delivery?.qoder?.type !== "forward") {
7342
+ diagnostics.error(
7343
+ "qoder.channel.forward_template.required",
7344
+ `channel.${name}: Qoder Channels require agent '${channel.agent}' to use delivery.qoder.type: forward.`,
7345
+ { type: "channel", name, provider: providerName }
7346
+ );
7347
+ }
7037
7348
  }
7038
7349
  const requiredCredentials = {
7039
7350
  dingtalk: ["client_id", "client_secret"],
@@ -7064,6 +7375,46 @@ function collectProviderCapabilities(config, providers, diagnostics) {
7064
7375
  }
7065
7376
  }
7066
7377
  }
7378
+ if (providerName === "qoder") {
7379
+ const domains = /* @__PURE__ */ new Map();
7380
+ for (const agent of Object.values(config.agents ?? {})) {
7381
+ if (agent.provider && agent.provider !== providerName) continue;
7382
+ const mode = agent.delivery?.qoder?.type === "forward" ? "forward" : "managed";
7383
+ const refs = [
7384
+ ...agent.environment && !config.environments?.[agent.environment]?.environment_id ? [`environment:${agent.environment}`] : [],
7385
+ ...(agent.skills ?? []).flatMap(
7386
+ (skill) => typeof skill === "string" ? [`skill:${skill}`] : skill.type === "custom" ? [`skill:${skill.skill_id}`] : []
7387
+ ),
7388
+ ...agent.vault ? [`vault:${agent.vault}`] : [],
7389
+ ...(agent.memory_stores ?? []).map((store) => `memory_store:${store}`),
7390
+ ...(agent.files ?? []).map((file) => `file:${file}`)
7391
+ ];
7392
+ for (const ref of refs) {
7393
+ const modes = domains.get(ref) ?? /* @__PURE__ */ new Set();
7394
+ modes.add(mode);
7395
+ domains.set(ref, modes);
7396
+ }
7397
+ }
7398
+ for (const [ref, modes] of domains) {
7399
+ if (modes.size < 2) continue;
7400
+ const [type, name] = ref.split(":");
7401
+ diagnostics.error(
7402
+ `qoder.${type}.delivery_domain.conflict`,
7403
+ `${type}.${name}: referenced by both Managed and Forward agents; declare separate resources because Qoder uses different API domains.`,
7404
+ { type, name, provider: providerName }
7405
+ );
7406
+ }
7407
+ }
7408
+ for (const [name, agent] of Object.entries(config.agents ?? {})) {
7409
+ if (agent.provider && agent.provider !== providerName) continue;
7410
+ if (agent.files?.length && (providerName !== "qoder" || agent.delivery?.qoder?.type !== "forward")) {
7411
+ diagnostics.error(
7412
+ "config.agent.files.unsupported",
7413
+ `agent.${name}: files are supported only by Qoder Forward Templates.`,
7414
+ { type: resolveAgentMaterialization(providerName, agent).resourceType, name, provider: providerName }
7415
+ );
7416
+ }
7417
+ }
7067
7418
  for (const [name, agent] of Object.entries(config.agents ?? {})) {
7068
7419
  if (agent.provider && agent.provider !== providerName) continue;
7069
7420
  const delivery = agent.delivery?.[providerName]?.type ?? "managed";
@@ -7104,6 +7455,44 @@ function collectProviderCapabilities(config, providers, diagnostics) {
7104
7455
  address
7105
7456
  );
7106
7457
  }
7458
+ if (delivery !== "forward" && agent.managed_tool_config) {
7459
+ diagnostics.error(
7460
+ `${providerName}.agent.managed_tool_config.forward_required`,
7461
+ `agent.${name}: managed_tool_config applies to Forward Templates; set delivery.${providerName}.type: forward or remove it.`,
7462
+ address
7463
+ );
7464
+ }
7465
+ if (agent.default_memory_store) {
7466
+ if (providerName !== "qoder" || delivery !== "forward") {
7467
+ diagnostics.error(
7468
+ `${providerName}.agent.default_memory_store.forward_required`,
7469
+ `agent.${name}: default_memory_store is supported only by Qoder Forward delivery.`,
7470
+ address
7471
+ );
7472
+ } else if (!config.defaults?.identity) {
7473
+ diagnostics.error(
7474
+ "qoder.template.default_memory_store.identity.required",
7475
+ `agent.${name}: default_memory_store requires defaults.identity to select the owning Forward Identity.`,
7476
+ { type: "template", name, provider: providerName }
7477
+ );
7478
+ } else {
7479
+ const identity = config.identities?.[config.defaults.identity];
7480
+ if (identity?.provider && identity.provider !== providerName) {
7481
+ diagnostics.error(
7482
+ "qoder.template.default_memory_store.identity.provider_mismatch",
7483
+ `agent.${name}: defaults.identity '${config.defaults.identity}' is pinned to provider '${identity.provider}'.`,
7484
+ { type: "template", name, provider: providerName }
7485
+ );
7486
+ }
7487
+ if (agent.default_memory_store.delete_on_destroy && identity?.identity_id) {
7488
+ diagnostics.error(
7489
+ "qoder.template.default_memory_store.delete.external_identity",
7490
+ `agent.${name}: delete_on_destroy requires an OpenCMA-managed Identity because an external Identity keeps the default Memory Store mounted.`,
7491
+ { type: "template", name, provider: providerName }
7492
+ );
7493
+ }
7494
+ }
7495
+ }
7107
7496
  if (delivery === "forward" && !isSupported(caps, "template")) {
7108
7497
  diagnostics.error(
7109
7498
  `${providerName}.agent.delivery.forward.unsupported`,
@@ -7119,10 +7508,10 @@ function collectProviderCapabilities(config, providers, diagnostics) {
7119
7508
  { type: "template", name, provider: providerName }
7120
7509
  );
7121
7510
  }
7122
- if (agent.memory_stores?.length) {
7511
+ if (agent.memory_stores?.length && !config.defaults?.identity) {
7123
7512
  diagnostics.error(
7124
- "qoder.template.memory_store.unsupported",
7125
- `agent.${name}: memory_stores are not yet supported by Qoder Forward Template delivery.`,
7513
+ "qoder.template.memory_store.identity.required",
7514
+ `agent.${name}: Forward memory_stores require defaults.identity for the Identity/Template mount.`,
7126
7515
  { type: "template", name, provider: providerName }
7127
7516
  );
7128
7517
  }
@@ -7237,6 +7626,13 @@ function collectProviderCapabilities(config, providers, diagnostics) {
7237
7626
  { type: "agent", name, provider: providerName }
7238
7627
  );
7239
7628
  }
7629
+ if (agent.managed_tool_config && (!agent.provider || agent.provider === providerName)) {
7630
+ diagnostics.error(
7631
+ `${providerName}.agent.managed_tool_config.unsupported`,
7632
+ `agent.${name}: managed_tool_config is supported only by Qoder; remove it or pin this agent to qoder.`,
7633
+ { type: "agent", name, provider: providerName }
7634
+ );
7635
+ }
7240
7636
  if (agent.tunnel && (!agent.provider || agent.provider === providerName)) {
7241
7637
  diagnostics.error(
7242
7638
  `${providerName}.agent.tunnel.unsupported`,
@@ -7335,13 +7731,6 @@ function collectProviderCapabilities(config, providers, diagnostics) {
7335
7731
  }
7336
7732
  }
7337
7733
 
7338
- // src/internal/core/agent-materialization.ts
7339
- function resolveAgentMaterialization(provider, agent) {
7340
- const mode = agent.delivery?.[provider]?.type ?? "managed";
7341
- if (mode === "managed") return { resourceType: "agent", mode };
7342
- return { resourceType: "template", mode };
7343
- }
7344
-
7345
7734
  // src/internal/graph/dependency.ts
7346
7735
  function buildDependencyGraph(config, targetProviders) {
7347
7736
  const nodes = /* @__PURE__ */ new Map();
@@ -7409,6 +7798,13 @@ function buildDependencyGraph(config, targetProviders) {
7409
7798
  const materialization = resolveAgentMaterialization(provider, decl);
7410
7799
  const agentAddr = { type: materialization.resourceType, name, provider };
7411
7800
  addNode(agentAddr);
7801
+ if ((decl.default_memory_store || decl.memory_stores?.length) && materialization.resourceType === "template") {
7802
+ const identityName = config.defaults?.identity;
7803
+ if (identityName) {
7804
+ const identityAddr = { type: "identity", name: identityName, provider };
7805
+ if (nodes.has(addressKey(identityAddr))) addEdge(agentAddr, identityAddr);
7806
+ }
7807
+ }
7412
7808
  if (decl.environment && config.environments?.[decl.environment]) {
7413
7809
  const envAddr = {
7414
7810
  type: "environment",
@@ -7435,6 +7831,12 @@ function buildDependencyGraph(config, targetProviders) {
7435
7831
  addEdge(agentAddr, vaultAddr);
7436
7832
  }
7437
7833
  }
7834
+ if (decl.files) {
7835
+ for (const fileName of decl.files) {
7836
+ const fileAddr = { type: "file", name: fileName, provider };
7837
+ if (nodes.has(addressKey(fileAddr))) addEdge(agentAddr, fileAddr);
7838
+ }
7839
+ }
7438
7840
  if (decl.memory_stores) {
7439
7841
  for (const msName of decl.memory_stores) {
7440
7842
  const msAddr = {
@@ -7499,6 +7901,7 @@ function buildDependencyGraph(config, targetProviders) {
7499
7901
  if (decl.provider && decl.provider !== provider) continue;
7500
7902
  const channelAddr = { type: "channel", name, provider };
7501
7903
  addNode(channelAddr);
7904
+ if (decl.mode === "pairing" || !decl.agent) continue;
7502
7905
  const agentDecl = config.agents?.[decl.agent];
7503
7906
  const agentType = agentDecl ? resolveAgentMaterialization(provider, agentDecl).resourceType : "agent";
7504
7907
  const agentAddr = { type: agentType, name: decl.agent, provider };
@@ -7839,7 +8242,7 @@ async function refreshState(state, providers, options = {}) {
7839
8242
  dirty = true;
7840
8243
  continue;
7841
8244
  }
7842
- const remote = await provider.findResource(res.address.type, res.address.name, res.remote_id);
8245
+ const remote = await provider.findResource(res.address.type, res.address.name, res.remote_id, res.api_mode);
7843
8246
  if (!remote) {
7844
8247
  if (!options.quiet) {
7845
8248
  emitRuntimeFeedback(options.onFeedback, {
@@ -8397,12 +8800,61 @@ function planDestroyProjectContext(ctx) {
8397
8800
  const resources = [...ctx.state.listResources()].sort(
8398
8801
  (a, b) => (destroyOrder[a.address.type] ?? 99) - (destroyOrder[b.address.type] ?? 99)
8399
8802
  );
8400
- return { resources, executionContext: ctx };
8803
+ const identityName = ctx.config.defaults?.identity;
8804
+ const defaultMemoryStores = [];
8805
+ for (const [agentName, agent] of Object.entries(ctx.config.agents ?? {})) {
8806
+ if (!agent.default_memory_store || agent.delivery?.qoder?.type !== "forward") continue;
8807
+ if (agent.provider && agent.provider !== "qoder") continue;
8808
+ defaultMemoryStores.push({
8809
+ agentName,
8810
+ provider: "qoder",
8811
+ identityId: identityName ? ctx.state.getResource({ type: "identity", name: identityName, provider: "qoder" })?.remote_id ?? null : null,
8812
+ templateId: ctx.state.getResource({ type: "template", name: agentName, provider: "qoder" })?.remote_id ?? null,
8813
+ deleteOnDestroy: agent.default_memory_store.delete_on_destroy ?? false
8814
+ });
8815
+ }
8816
+ for (const pending of ctx.state.getStateFile().pending_default_memory_store_cleanups ?? []) {
8817
+ const existing = defaultMemoryStores.find(
8818
+ (item) => item.agentName === pending.agent_name && item.provider === pending.provider
8819
+ );
8820
+ if (existing) {
8821
+ existing.memoryStoreId = pending.remote_id;
8822
+ existing.deleteOnDestroy = true;
8823
+ } else {
8824
+ defaultMemoryStores.push({
8825
+ agentName: pending.agent_name,
8826
+ provider: pending.provider,
8827
+ identityId: pending.identity_id ?? null,
8828
+ templateId: pending.template_id ?? null,
8829
+ deleteOnDestroy: true,
8830
+ memoryStoreId: pending.remote_id
8831
+ });
8832
+ }
8833
+ }
8834
+ return { resources, defaultMemoryStores, executionContext: ctx };
8401
8835
  }
8402
8836
  async function destroyPlannedProjectResources(planned, options = {}) {
8403
8837
  const results = [];
8404
- let stateChanged = false;
8838
+ const capturedDefaults = await captureDefaultMemoryStores(planned);
8405
8839
  const ctx = planned.executionContext;
8840
+ if (capturedDefaults.some((item) => item.error)) {
8841
+ const defaultMemoryStoreResults2 = capturedDefaults.map(preflightFailureResult);
8842
+ for (const resource of planned.resources) {
8843
+ const result = {
8844
+ resource,
8845
+ status: "blocked",
8846
+ reason: "skipped",
8847
+ error: "Destroy aborted because the default Memory Store preflight failed."
8848
+ };
8849
+ results.push(result);
8850
+ options.onResourceResult?.(result);
8851
+ }
8852
+ return { ...planned, results, defaultMemoryStoreResults: defaultMemoryStoreResults2, destroyed: 0, partial: true };
8853
+ }
8854
+ if (await persistCapturedDefaultMemoryStores(ctx, capturedDefaults)) {
8855
+ await ctx.state.save();
8856
+ }
8857
+ let stateChanged = false;
8406
8858
  for (const resource of planned.resources) {
8407
8859
  options.onResourceStart?.(resource);
8408
8860
  const result = await destroyOneResource(ctx, resource, options);
@@ -8413,14 +8865,171 @@ async function destroyPlannedProjectResources(planned, options = {}) {
8413
8865
  if (stateChanged) {
8414
8866
  await ctx.state.save();
8415
8867
  }
8868
+ const defaultMemoryStoreResults = await finalizeDefaultMemoryStores(planned, capturedDefaults, options);
8869
+ if (recordFailedDefaultMemoryStoreCleanups(ctx, defaultMemoryStoreResults)) {
8870
+ await ctx.state.save();
8871
+ }
8872
+ if (clearCompletedDefaultMemoryStoreCleanups(ctx, defaultMemoryStoreResults)) {
8873
+ await ctx.state.save();
8874
+ }
8416
8875
  const destroyed = results.filter((result) => result.status === "success").length;
8417
8876
  return {
8418
8877
  ...planned,
8419
8878
  results,
8879
+ defaultMemoryStoreResults,
8420
8880
  destroyed,
8421
- partial: destroyed !== planned.resources.length
8881
+ partial: destroyed !== planned.resources.length || defaultMemoryStoreResults.some((result) => result.status === "failed")
8882
+ };
8883
+ }
8884
+ async function persistCapturedDefaultMemoryStores(ctx, captured) {
8885
+ const state = ctx.state.getStateFile();
8886
+ if (!state.pending_default_memory_store_cleanups) state.pending_default_memory_store_cleanups = [];
8887
+ const pending = state.pending_default_memory_store_cleanups;
8888
+ let changed = false;
8889
+ for (const item of captured) {
8890
+ if (!item.plan.deleteOnDestroy || !item.memoryStoreId) continue;
8891
+ if (pending.some((entry) => entry.provider === item.plan.provider && entry.remote_id === item.memoryStoreId))
8892
+ continue;
8893
+ pending.push({
8894
+ agent_name: item.plan.agentName,
8895
+ provider: item.plan.provider,
8896
+ remote_id: item.memoryStoreId,
8897
+ ...item.plan.identityId ? { identity_id: item.plan.identityId } : {},
8898
+ ...item.plan.templateId ? { template_id: item.plan.templateId } : {}
8899
+ });
8900
+ changed = true;
8901
+ }
8902
+ return changed;
8903
+ }
8904
+ function clearCompletedDefaultMemoryStoreCleanups(ctx, results) {
8905
+ const state = ctx.state.getStateFile();
8906
+ const completedIds = new Set(
8907
+ results.filter((item) => item.status === "deleted" || item.status === "already_gone").map((item) => item.memoryStoreId).filter((id) => Boolean(id))
8908
+ );
8909
+ if (completedIds.size === 0 || !state.pending_default_memory_store_cleanups?.length) return false;
8910
+ const remaining = state.pending_default_memory_store_cleanups.filter((item) => !completedIds.has(item.remote_id));
8911
+ if (remaining.length === state.pending_default_memory_store_cleanups.length) return false;
8912
+ state.pending_default_memory_store_cleanups = remaining;
8913
+ return true;
8914
+ }
8915
+ function recordFailedDefaultMemoryStoreCleanups(ctx, results) {
8916
+ const pending = ctx.state.getStateFile().pending_default_memory_store_cleanups;
8917
+ if (!pending?.length) return false;
8918
+ let changed = false;
8919
+ for (const result of results) {
8920
+ if (result.status !== "failed" || !result.memoryStoreId) continue;
8921
+ const entry = pending.find((item) => item.remote_id === result.memoryStoreId);
8922
+ if (entry && entry.last_error !== result.error) {
8923
+ entry.last_error = result.error;
8924
+ changed = true;
8925
+ }
8926
+ }
8927
+ return changed;
8928
+ }
8929
+ function preflightFailureResult(item) {
8930
+ if (!item.plan.deleteOnDestroy) return { ...item.plan, status: "retained" };
8931
+ return {
8932
+ ...item.plan,
8933
+ status: "failed",
8934
+ ...item.memoryStoreId ? { memoryStoreId: item.memoryStoreId } : {},
8935
+ error: item.error ?? "Destroy aborted because another default Memory Store preflight failed."
8422
8936
  };
8423
8937
  }
8938
+ async function captureDefaultMemoryStores(planned) {
8939
+ return Promise.all(
8940
+ planned.defaultMemoryStores.map(async (item) => {
8941
+ if (!item.deleteOnDestroy) return { plan: item };
8942
+ if (item.memoryStoreId) return { plan: item, memoryStoreId: item.memoryStoreId };
8943
+ if (!item.identityId || !item.templateId) {
8944
+ return { plan: item, error: "Cannot resolve the Identity and Template before destroy." };
8945
+ }
8946
+ try {
8947
+ const provider = getRuntimeProvider(planned.executionContext, item.provider);
8948
+ if (!provider.findDefaultMemoryStoreId || !provider.deleteDefaultMemoryStore) {
8949
+ return { plan: item, error: `Provider '${item.provider}' cannot delete a default memory store.` };
8950
+ }
8951
+ const memoryStoreId = await provider.findDefaultMemoryStoreId(item.identityId, item.templateId);
8952
+ return { plan: item, ...memoryStoreId ? { memoryStoreId } : {} };
8953
+ } catch (error) {
8954
+ return { plan: item, error: error instanceof Error ? error.message : String(error) };
8955
+ }
8956
+ })
8957
+ );
8958
+ }
8959
+ async function finalizeDefaultMemoryStores(planned, captured, options) {
8960
+ const output = [];
8961
+ for (const item of captured) {
8962
+ if (!item.plan.deleteOnDestroy) {
8963
+ output.push({ ...item.plan, status: "retained" });
8964
+ continue;
8965
+ }
8966
+ if (item.error) {
8967
+ output.push({ ...item.plan, status: "failed", error: item.error });
8968
+ continue;
8969
+ }
8970
+ if (!item.memoryStoreId) {
8971
+ output.push({ ...item.plan, status: "already_gone" });
8972
+ continue;
8973
+ }
8974
+ try {
8975
+ const provider = getRuntimeProvider(planned.executionContext, item.plan.provider);
8976
+ await deleteDefaultMemoryStoreWithArchiveFallback(
8977
+ provider,
8978
+ item.memoryStoreId,
8979
+ options.defaultMemoryStoreRetryDelaysMs ?? [1e3, 2e3, 4e3, 8e3]
8980
+ );
8981
+ output.push({ ...item.plan, status: "deleted", memoryStoreId: item.memoryStoreId });
8982
+ } catch (error) {
8983
+ if (ApiError.isNotFound(error)) {
8984
+ output.push({ ...item.plan, status: "already_gone", memoryStoreId: item.memoryStoreId });
8985
+ } else {
8986
+ output.push({
8987
+ ...item.plan,
8988
+ status: "failed",
8989
+ memoryStoreId: item.memoryStoreId,
8990
+ error: error instanceof Error ? error.message : String(error)
8991
+ });
8992
+ }
8993
+ }
8994
+ }
8995
+ return output;
8996
+ }
8997
+ async function deleteDefaultMemoryStoreWithArchiveFallback(provider, memoryStoreId, retryDelaysMs) {
8998
+ try {
8999
+ await retryDefaultMemoryStoreOperation(() => provider.deleteDefaultMemoryStore(memoryStoreId), retryDelaysMs);
9000
+ } catch (error) {
9001
+ if (isAlreadyArchivedConflict(error) && provider.deleteMemoryStore) {
9002
+ await retryDefaultMemoryStoreOperation(() => provider.deleteMemoryStore(memoryStoreId), retryDelaysMs);
9003
+ return;
9004
+ }
9005
+ if (!isStillMountedConflict(error) || !provider.archiveMemoryStore || !provider.deleteMemoryStore) throw error;
9006
+ await retryDefaultMemoryStoreOperation(() => provider.archiveMemoryStore(memoryStoreId), retryDelaysMs);
9007
+ await retryDefaultMemoryStoreOperation(() => provider.deleteMemoryStore(memoryStoreId), retryDelaysMs);
9008
+ }
9009
+ }
9010
+ async function retryDefaultMemoryStoreOperation(operation, delaysMs) {
9011
+ for (let attempt = 0; ; attempt++) {
9012
+ try {
9013
+ await operation();
9014
+ return;
9015
+ } catch (error) {
9016
+ if (!isRetryableDefaultMemoryStoreError(error) || attempt >= delaysMs.length) throw error;
9017
+ await delay(delaysMs[attempt]);
9018
+ }
9019
+ }
9020
+ }
9021
+ function isRetryableDefaultMemoryStoreError(error) {
9022
+ return error instanceof ApiError && (isStillMountedConflict(error) || isAlreadyArchivedConflict(error) || error.statusCode === 429 || [500, 502, 503].includes(error.statusCode));
9023
+ }
9024
+ function isStillMountedConflict(error) {
9025
+ return error instanceof ApiError && error.statusCode === 409 && error.responseBody.includes("still mounted");
9026
+ }
9027
+ function isAlreadyArchivedConflict(error) {
9028
+ return error instanceof ApiError && error.statusCode === 409 && error.responseBody.toLowerCase().includes("already archived");
9029
+ }
9030
+ function delay(ms) {
9031
+ return new Promise((resolve13) => setTimeout(resolve13, ms));
9032
+ }
8424
9033
  async function destroyOneResource(ctx, resource, options) {
8425
9034
  if (isExternalReference(ctx, resource)) {
8426
9035
  ctx.state.removeResource(resource.address);
@@ -8441,8 +9050,18 @@ async function destroyOneResource(ctx, resource, options) {
8441
9050
  ctx.state.removeResource(resource.address);
8442
9051
  return successResult(resource, "destroyed");
8443
9052
  }
9053
+ const apiMode = resource.api_mode === "auto" ? void 0 : resource.api_mode;
8444
9054
  try {
8445
- await deleteRemoteResource(provider, resource.address.type, resource.remote_id, options.cascade);
9055
+ await deleteRemoteResource(
9056
+ provider,
9057
+ resource.address.type,
9058
+ resource.remote_id,
9059
+ options.cascade,
9060
+ apiMode,
9061
+ ctx.state.listResources().filter(
9062
+ (candidate) => candidate.address.provider === resource.address.provider && candidate.address.type === "memory_store" && candidate.api_mode === "forward" && typeof candidate.remote_id === "string"
9063
+ ).map((candidate) => candidate.remote_id)
9064
+ );
8446
9065
  ctx.state.removeResource(resource.address);
8447
9066
  emitRuntimeFeedback(options.onFeedback, {
8448
9067
  type: "resource_action_success",
@@ -8471,7 +9090,7 @@ async function destroyOneResource(ctx, resource, options) {
8471
9090
  };
8472
9091
  if (await options.onCascadeRequired?.(blocked)) {
8473
9092
  try {
8474
- await provider.deleteEnvironment(resource.remote_id, true);
9093
+ await provider.deleteEnvironment(resource.remote_id, true, apiMode);
8475
9094
  ctx.state.removeResource(resource.address);
8476
9095
  return {
8477
9096
  ...successResult(resource, "destroyed"),
@@ -8507,29 +9126,29 @@ function failureResult(resource, error) {
8507
9126
  error: error instanceof Error ? error.message : String(error)
8508
9127
  };
8509
9128
  }
8510
- async function deleteRemoteResource(provider, type, id, cascade) {
9129
+ async function deleteRemoteResource(provider, type, id, cascade, mode, ownedMemoryStoreIds = []) {
8511
9130
  switch (type) {
8512
9131
  case "agent":
8513
9132
  await provider.deleteAgent(id);
8514
9133
  return;
8515
9134
  case "template":
8516
9135
  if (!provider.archiveTemplate) throw new UserError(`Provider does not support templates`);
8517
- await provider.archiveTemplate(id);
9136
+ await provider.archiveTemplate(id, ownedMemoryStoreIds);
8518
9137
  return;
8519
9138
  case "skill":
8520
- await provider.deleteSkill(id);
9139
+ await provider.deleteSkill(id, mode);
8521
9140
  return;
8522
9141
  case "memory_store":
8523
9142
  if (!provider.deleteMemoryStore) {
8524
9143
  throw new UserError(`Provider does not support memory stores`);
8525
9144
  }
8526
- await provider.deleteMemoryStore(id);
9145
+ await provider.deleteMemoryStore(id, mode);
8527
9146
  return;
8528
9147
  case "vault":
8529
- await provider.deleteVault(id);
9148
+ await provider.deleteVault(id, mode);
8530
9149
  return;
8531
9150
  case "environment":
8532
- await provider.deleteEnvironment(id, cascade);
9151
+ await provider.deleteEnvironment(id, cascade, mode);
8533
9152
  return;
8534
9153
  case "deployment":
8535
9154
  await provider.deleteDeployment(id);
@@ -8543,7 +9162,7 @@ async function deleteRemoteResource(provider, type, id, cascade) {
8543
9162
  await provider.deleteChannel(id);
8544
9163
  return;
8545
9164
  case "file":
8546
- await provider.deleteFile(id);
9165
+ await provider.deleteFile(id, mode);
8547
9166
  return;
8548
9167
  }
8549
9168
  }
@@ -9308,7 +9927,7 @@ async function collectEventsUntilTerminal(adapter2, sessionId, options = {}) {
9308
9927
  terminalStatus = terminalEvent.status;
9309
9928
  break;
9310
9929
  }
9311
- await delay(currentIntervalMs);
9930
+ await delay2(currentIntervalMs);
9312
9931
  currentIntervalMs = Math.min(currentIntervalMs * 2, maxPollIntervalMs);
9313
9932
  }
9314
9933
  } else {
@@ -9319,7 +9938,7 @@ async function collectEventsUntilTerminal(adapter2, sessionId, options = {}) {
9319
9938
  terminalStatus = session.status;
9320
9939
  break;
9321
9940
  }
9322
- await delay(currentIntervalMs);
9941
+ await delay2(currentIntervalMs);
9323
9942
  currentIntervalMs = Math.min(currentIntervalMs * 2, maxPollIntervalMs);
9324
9943
  }
9325
9944
  result = await adapter2.listSessionEvents(sessionId, { limit: 100 });
@@ -9486,7 +10105,7 @@ async function* streamWithResume(adapter2, sessionId, message) {
9486
10105
  }
9487
10106
  }
9488
10107
  if (!reachedTerminal) {
9489
- await delay(reconnectIntervalMs);
10108
+ await delay2(reconnectIntervalMs);
9490
10109
  reconnectIntervalMs = Math.min(reconnectIntervalMs * 2, DEFAULT_POLL_INTERVAL_MS);
9491
10110
  }
9492
10111
  }
@@ -9514,7 +10133,7 @@ function assertNotTimedOut(start, timeoutMs) {
9514
10133
  throw new UserError(`Session did not complete within the timeout (${Math.floor(timeoutMs / 1e3)} seconds).`);
9515
10134
  }
9516
10135
  }
9517
- function delay(ms) {
10136
+ function delay2(ms) {
9518
10137
  return new Promise((resolve13) => setTimeout(resolve13, ms));
9519
10138
  }
9520
10139
 
@@ -9700,6 +10319,7 @@ var StateManager = class _StateManager {
9700
10319
  address: r.address,
9701
10320
  remote_id: r.remote_id,
9702
10321
  externally_managed: r.externally_managed === true ? true : void 0,
10322
+ api_mode: r.api_mode === "forward" ? "forward" : r.api_mode === "managed" ? "managed" : void 0,
9703
10323
  version: r.version,
9704
10324
  content_hash: r.content_hash ?? r.desired_hash ?? "",
9705
10325
  desired_hash: r.desired_hash ?? r.content_hash ?? "",
@@ -9711,7 +10331,11 @@ var StateManager = class _StateManager {
9711
10331
  drift_paths: r.drift_paths,
9712
10332
  drift_status: r.drift_status
9713
10333
  }));
9714
- return new _StateManager({ resources }, path);
10334
+ const pending = Array.isArray(data.pending_default_memory_store_cleanups) ? data.pending_default_memory_store_cleanups : void 0;
10335
+ return new _StateManager(
10336
+ { resources, ...pending ? { pending_default_memory_store_cleanups: pending } : {} },
10337
+ path
10338
+ );
9715
10339
  } catch (err) {
9716
10340
  if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
9717
10341
  return _StateManager.initialize(path);