@openagentpack/sdk 0.3.1 → 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.d.ts +96 -27
- package/dist/index.js +1272 -265
- package/dist/{session-event-CxLg_XqS.d.ts → session-event--Mxe1bnT.d.ts} +142 -30
- package/dist/session-events.d.ts +1 -1
- package/package.json +1 -1
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);
|
|
@@ -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
|
-
|
|
1740
|
-
|
|
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
|
-
|
|
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 ?? "",
|
|
@@ -2026,6 +2059,13 @@ function mapEnvironment2(name, decl, projectName) {
|
|
|
2026
2059
|
metadata: injectMetadata(decl.metadata, projectName, name)
|
|
2027
2060
|
};
|
|
2028
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
|
+
}
|
|
2029
2069
|
function mapVault(name, decl, projectName) {
|
|
2030
2070
|
const body = { display_name: decl.display_name };
|
|
2031
2071
|
if (projectName) body.metadata = injectMetadata(decl.metadata, projectName, name);
|
|
@@ -2099,7 +2139,8 @@ function envToDecl2(raw) {
|
|
|
2099
2139
|
config: {
|
|
2100
2140
|
type: config.type ?? "cloud",
|
|
2101
2141
|
networking: config.networking,
|
|
2102
|
-
packages: config.packages
|
|
2142
|
+
packages: normalizeEnvironmentPackages(config.packages),
|
|
2143
|
+
setup_script: config.setup_script
|
|
2103
2144
|
},
|
|
2104
2145
|
metadata: stripAgentsMetadata(raw.metadata)
|
|
2105
2146
|
});
|
|
@@ -2339,9 +2380,12 @@ function mapForwardTemplate(name, decl, refs, projectName) {
|
|
|
2339
2380
|
environment_id: refs.environment_id,
|
|
2340
2381
|
vault_ids: refs.vault_ids
|
|
2341
2382
|
};
|
|
2383
|
+
body.files = Object.fromEntries((refs.file_ids ?? []).map((id) => [id, { enabled: true }]));
|
|
2342
2384
|
if (refs.tunnel_id) body.tunnel_id = refs.tunnel_id;
|
|
2343
2385
|
if (projectName) body.metadata = injectMetadata(decl.metadata, projectName, name);
|
|
2344
2386
|
else body.metadata = decl.metadata ?? {};
|
|
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;
|
|
2345
2389
|
if (decl.tools) {
|
|
2346
2390
|
body.tools = [
|
|
2347
2391
|
{
|
|
@@ -2482,6 +2526,9 @@ function mapSession2(bindings) {
|
|
|
2482
2526
|
if (bindings.tunnel_id) body.tunnel_id = bindings.tunnel_id;
|
|
2483
2527
|
if (bindings.title) body.title = bindings.title;
|
|
2484
2528
|
if (bindings.metadata) body.metadata = bindings.metadata;
|
|
2529
|
+
if (bindings.environment_variables) {
|
|
2530
|
+
body.environment_variables = Object.entries(bindings.environment_variables).map(([key, value]) => `${key}=${value}`).join(";");
|
|
2531
|
+
}
|
|
2485
2532
|
if (bindings.vault_ids.length) body.vault_ids = bindings.vault_ids;
|
|
2486
2533
|
const resources = [];
|
|
2487
2534
|
for (const id of bindings.memory_store_ids) resources.push({ type: "memory_store", memory_store_id: id });
|
|
@@ -2527,6 +2574,7 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2527
2574
|
client;
|
|
2528
2575
|
memoryApi;
|
|
2529
2576
|
forwardClient;
|
|
2577
|
+
forwardMemoryApi;
|
|
2530
2578
|
projectName;
|
|
2531
2579
|
forwardSessionIds = /* @__PURE__ */ new Set();
|
|
2532
2580
|
constructor(apiKey, gateway, projectName, forwardGateway) {
|
|
@@ -2548,6 +2596,19 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2548
2596
|
apiKey,
|
|
2549
2597
|
gateway: forwardGateway ?? deriveForwardGateway(gateway)
|
|
2550
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
|
+
});
|
|
2551
2612
|
this.projectName = projectName ?? "";
|
|
2552
2613
|
}
|
|
2553
2614
|
async validate() {
|
|
@@ -2562,7 +2623,7 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2562
2623
|
file: "/files",
|
|
2563
2624
|
deployment: "/deployments"
|
|
2564
2625
|
};
|
|
2565
|
-
async findResource(type, name, id) {
|
|
2626
|
+
async findResource(type, name, id, mode) {
|
|
2566
2627
|
if (type === "template") {
|
|
2567
2628
|
const raw2 = await locateRemote(this.forwardClient, "/templates", name, id, (item) => item.status !== "archived");
|
|
2568
2629
|
return raw2 ? toRemoteResource(raw2) : null;
|
|
@@ -2582,7 +2643,14 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2582
2643
|
const raw2 = await locateRemote(this.forwardClient, "/channels", name, id, () => true);
|
|
2583
2644
|
return raw2 ? toRemoteResource(raw2) : null;
|
|
2584
2645
|
}
|
|
2585
|
-
|
|
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);
|
|
2586
2654
|
return raw ? toRemoteResource(raw) : null;
|
|
2587
2655
|
}
|
|
2588
2656
|
async listAgents(filter) {
|
|
@@ -2691,14 +2759,10 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2691
2759
|
}
|
|
2692
2760
|
normalizeRemote(type, raw) {
|
|
2693
2761
|
if (type === "environment") {
|
|
2694
|
-
const
|
|
2762
|
+
const normalized = envToDecl2(raw);
|
|
2695
2763
|
return compactDeep({
|
|
2696
2764
|
description: raw.description,
|
|
2697
|
-
config:
|
|
2698
|
-
type: config.type ?? "cloud",
|
|
2699
|
-
networking: config.networking,
|
|
2700
|
-
packages: config.packages
|
|
2701
|
-
},
|
|
2765
|
+
config: normalized.config,
|
|
2702
2766
|
metadata: stripAgentsMetadata(raw.metadata)
|
|
2703
2767
|
});
|
|
2704
2768
|
}
|
|
@@ -2730,16 +2794,21 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2730
2794
|
}
|
|
2731
2795
|
if (type === "channel") {
|
|
2732
2796
|
const channelConfig = raw.channel_config ?? {};
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2797
|
+
const mode = raw.identity_resolution?.mode ?? "fixed";
|
|
2798
|
+
const normalized = {
|
|
2799
|
+
identity_resolution: { mode },
|
|
2736
2800
|
channel_type: raw.channel_type,
|
|
2737
2801
|
name: raw.name,
|
|
2738
2802
|
enabled: raw.enabled,
|
|
2739
2803
|
channel_config: {
|
|
2740
2804
|
response_options: channelConfig.response_options ?? {}
|
|
2741
2805
|
}
|
|
2742
|
-
}
|
|
2806
|
+
};
|
|
2807
|
+
if (mode === "fixed") {
|
|
2808
|
+
normalized.identity_id = raw.identity_id;
|
|
2809
|
+
normalized.template_id = raw.template_id;
|
|
2810
|
+
}
|
|
2811
|
+
return compactDeep(normalized);
|
|
2743
2812
|
}
|
|
2744
2813
|
return compactDeep({
|
|
2745
2814
|
description: raw.description,
|
|
@@ -2750,17 +2819,29 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2750
2819
|
metadata: stripAgentsMetadata(raw.metadata)
|
|
2751
2820
|
});
|
|
2752
2821
|
}
|
|
2753
|
-
async createEnvironment(name, decl) {
|
|
2754
|
-
const body = mapEnvironment2(name, decl, this.projectName);
|
|
2755
|
-
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);
|
|
2756
2825
|
return toRemoteResource(res);
|
|
2757
2826
|
}
|
|
2758
|
-
async updateEnvironment(id, name, decl) {
|
|
2759
|
-
const body = mapEnvironment2(name, decl, this.projectName);
|
|
2760
|
-
const
|
|
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}`);
|
|
2831
|
+
const currentMetadata = current.metadata ?? {};
|
|
2832
|
+
const metadata = { ...body.metadata ?? {} };
|
|
2833
|
+
for (const key of Object.keys(currentMetadata)) {
|
|
2834
|
+
if (!key.startsWith("agents.") && !(key in metadata)) metadata[key] = null;
|
|
2835
|
+
}
|
|
2836
|
+
body.metadata = metadata;
|
|
2837
|
+
const res = await client.post(`/environments/${id}`, body);
|
|
2761
2838
|
return toRemoteResource(res);
|
|
2762
2839
|
}
|
|
2763
|
-
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
|
+
}
|
|
2764
2845
|
try {
|
|
2765
2846
|
await this.client.delete(`/environments/${id}`);
|
|
2766
2847
|
return;
|
|
@@ -2787,17 +2868,18 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2787
2868
|
await this.client.post(`/environments/${id}/archive`, {});
|
|
2788
2869
|
}
|
|
2789
2870
|
}
|
|
2790
|
-
async createVault(name, decl) {
|
|
2871
|
+
async createVault(name, decl, mode = "managed") {
|
|
2872
|
+
const client = mode === "forward" ? this.forwardClient : this.client;
|
|
2791
2873
|
const body = mapVault(name, decl, this.projectName);
|
|
2792
|
-
const res = await
|
|
2874
|
+
const res = await client.post("/vaults", body);
|
|
2793
2875
|
const vaultId = res.id;
|
|
2794
2876
|
for (const cred of decl.credentials ?? []) {
|
|
2795
|
-
await
|
|
2877
|
+
await client.post(`/vaults/${vaultId}/credentials`, mapCredential(cred));
|
|
2796
2878
|
}
|
|
2797
2879
|
return toRemoteResource(res);
|
|
2798
2880
|
}
|
|
2799
|
-
async deleteVault(id) {
|
|
2800
|
-
await this.client.delete(`/vaults/${id}`);
|
|
2881
|
+
async deleteVault(id, mode = "managed") {
|
|
2882
|
+
await (mode === "forward" ? this.forwardClient : this.client).delete(`/vaults/${id}`);
|
|
2801
2883
|
}
|
|
2802
2884
|
async exportResources(type) {
|
|
2803
2885
|
return exportRemoteResources(this.client, type, {
|
|
@@ -2808,17 +2890,24 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2808
2890
|
agentToDecl: agentToDecl2
|
|
2809
2891
|
});
|
|
2810
2892
|
}
|
|
2811
|
-
async createSkill(name, decl, files) {
|
|
2893
|
+
async createSkill(name, decl, files, mode = "managed") {
|
|
2812
2894
|
const formData = await buildSkillFormData(name, decl, files);
|
|
2813
|
-
const res = await this.client.postFormData(
|
|
2895
|
+
const res = await (mode === "forward" ? this.forwardClient : this.client).postFormData(
|
|
2896
|
+
"/skills",
|
|
2897
|
+
formData
|
|
2898
|
+
);
|
|
2814
2899
|
return toRemoteResource(res);
|
|
2815
2900
|
}
|
|
2816
|
-
async updateSkill(id, name, decl, files) {
|
|
2817
|
-
|
|
2818
|
-
|
|
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);
|
|
2819
2908
|
}
|
|
2820
|
-
async deleteSkill(id) {
|
|
2821
|
-
await this.client.delete(`/skills/${id}`);
|
|
2909
|
+
async deleteSkill(id, mode = "managed") {
|
|
2910
|
+
await (mode === "forward" ? this.forwardClient : this.client).delete(`/skills/${id}`);
|
|
2822
2911
|
}
|
|
2823
2912
|
async createAgent(name, decl, refs) {
|
|
2824
2913
|
const body = mapAgent2(name, decl, refs, void 0, this.projectName);
|
|
@@ -2835,21 +2924,89 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2835
2924
|
await this.client.delete(`/agents/${id}`);
|
|
2836
2925
|
}
|
|
2837
2926
|
async createTemplate(name, decl, refs) {
|
|
2838
|
-
await this.registerForwardVaults(refs.vault_ids);
|
|
2839
2927
|
const body = mapForwardTemplate(name, decl, refs, this.projectName);
|
|
2840
2928
|
const res = await this.forwardClient.post("/templates", body);
|
|
2929
|
+
await this.reconcileForwardMemoryMounts(res.id, refs);
|
|
2841
2930
|
return toRemoteResource(res);
|
|
2842
2931
|
}
|
|
2843
2932
|
async updateTemplate(id, name, decl, refs) {
|
|
2844
|
-
await this.registerForwardVaults(refs.vault_ids);
|
|
2845
2933
|
const body = mapForwardTemplate(name, decl, refs, this.projectName);
|
|
2846
2934
|
if (!refs.tunnel_id) body.tunnel_id = null;
|
|
2847
2935
|
const res = await this.forwardClient.post(`/templates/${id}`, body);
|
|
2936
|
+
await this.reconcileForwardMemoryMounts(id, refs);
|
|
2848
2937
|
return toRemoteResource(res);
|
|
2849
2938
|
}
|
|
2850
|
-
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
|
+
}
|
|
2851
2958
|
await this.forwardClient.post(`/templates/${id}/archive`, {});
|
|
2852
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
|
+
}
|
|
2853
3010
|
async createIdentity(name, decl) {
|
|
2854
3011
|
if (decl.identity_id) return { id: decl.identity_id, type: "identity" };
|
|
2855
3012
|
const res = await this.forwardClient.post("/identities", {
|
|
@@ -2886,12 +3043,14 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2886
3043
|
}
|
|
2887
3044
|
async updateChannel(id, name, decl, refs) {
|
|
2888
3045
|
const current = await this.forwardClient.get(`/channels/${id}`);
|
|
2889
|
-
|
|
3046
|
+
const currentMode = current.identity_resolution?.mode ?? "fixed";
|
|
3047
|
+
if (current.channel_type !== decl.type || currentMode !== (decl.mode ?? "fixed")) {
|
|
2890
3048
|
await this.deleteChannel(id);
|
|
2891
3049
|
return this.createChannel(name, decl, refs);
|
|
2892
3050
|
}
|
|
2893
3051
|
const body = this.mapChannel(name, decl, refs);
|
|
2894
3052
|
delete body.channel_type;
|
|
3053
|
+
delete body.identity_resolution;
|
|
2895
3054
|
const res = await this.forwardClient.post(`/channels/${id}`, body);
|
|
2896
3055
|
return toRemoteResource(res);
|
|
2897
3056
|
}
|
|
@@ -2899,9 +3058,8 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2899
3058
|
await this.forwardClient.delete(`/channels/${id}`);
|
|
2900
3059
|
}
|
|
2901
3060
|
mapChannel(name, decl, refs) {
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
template_id: refs.agent_id,
|
|
3061
|
+
const mode = decl.mode ?? "fixed";
|
|
3062
|
+
const body = {
|
|
2905
3063
|
channel_type: decl.type,
|
|
2906
3064
|
name: decl.name ?? name,
|
|
2907
3065
|
enabled: decl.enabled ?? true,
|
|
@@ -2914,22 +3072,27 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2914
3072
|
}
|
|
2915
3073
|
}
|
|
2916
3074
|
};
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
resource: { id }
|
|
2923
|
-
});
|
|
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;
|
|
2924
3080
|
}
|
|
3081
|
+
return body;
|
|
2925
3082
|
}
|
|
2926
|
-
async createMemoryStore(name, decl) {
|
|
3083
|
+
async createMemoryStore(name, decl, mode = "managed") {
|
|
2927
3084
|
const body = mapMemoryStore2(name, decl);
|
|
2928
|
-
const
|
|
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
|
+
);
|
|
2929
3092
|
const storeId = res.id;
|
|
2930
3093
|
try {
|
|
2931
3094
|
for (const entry of decl.entries ?? []) {
|
|
2932
|
-
await
|
|
3095
|
+
await memoryApi.createMemory(storeId, { content: entry.content, path: entry.key });
|
|
2933
3096
|
}
|
|
2934
3097
|
} catch (error) {
|
|
2935
3098
|
await this.client.delete(`/memory_stores/${storeId}`).catch(() => void 0);
|
|
@@ -2937,7 +3100,7 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2937
3100
|
}
|
|
2938
3101
|
return toRemoteResource(res);
|
|
2939
3102
|
}
|
|
2940
|
-
async deleteMemoryStore(id) {
|
|
3103
|
+
async deleteMemoryStore(id, _mode = "managed") {
|
|
2941
3104
|
await this.client.delete(`/memory_stores/${id}`);
|
|
2942
3105
|
}
|
|
2943
3106
|
listMemoryStores(options) {
|
|
@@ -2946,23 +3109,23 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2946
3109
|
getMemoryStore(id) {
|
|
2947
3110
|
return this.memoryApi.getStore(id);
|
|
2948
3111
|
}
|
|
2949
|
-
updateMemoryStore(id, input) {
|
|
2950
|
-
return this.memoryApi.updateStore(id, input);
|
|
3112
|
+
updateMemoryStore(id, input, mode = "managed") {
|
|
3113
|
+
return (mode === "forward" ? this.forwardMemoryApi : this.memoryApi).updateStore(id, input);
|
|
2951
3114
|
}
|
|
2952
3115
|
archiveMemoryStore(id) {
|
|
2953
3116
|
return this.memoryApi.archiveStore(id);
|
|
2954
3117
|
}
|
|
2955
|
-
createMemory(storeId, input) {
|
|
2956
|
-
return this.memoryApi.createMemory(storeId, input);
|
|
3118
|
+
createMemory(storeId, input, mode = "managed") {
|
|
3119
|
+
return (mode === "forward" ? this.forwardMemoryApi : this.memoryApi).createMemory(storeId, input);
|
|
2957
3120
|
}
|
|
2958
|
-
listMemories(storeId, options) {
|
|
2959
|
-
return this.memoryApi.listMemories(storeId, options);
|
|
3121
|
+
listMemories(storeId, options, mode = "managed") {
|
|
3122
|
+
return (mode === "forward" ? this.forwardMemoryApi : this.memoryApi).listMemories(storeId, options);
|
|
2960
3123
|
}
|
|
2961
3124
|
getMemory(storeId, memoryId) {
|
|
2962
3125
|
return this.memoryApi.getMemory(storeId, memoryId);
|
|
2963
3126
|
}
|
|
2964
|
-
updateMemory(storeId, memoryId, input) {
|
|
2965
|
-
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);
|
|
2966
3129
|
}
|
|
2967
3130
|
deleteMemory(storeId, memoryId, expected) {
|
|
2968
3131
|
return this.memoryApi.deleteMemory(storeId, memoryId, expected);
|
|
@@ -2979,11 +3142,15 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2979
3142
|
async createDeployment(name, decl, refs, basePath) {
|
|
2980
3143
|
const uploaded = await this.uploadDeploymentFiles(decl, basePath);
|
|
2981
3144
|
const body = mapDeployment2(name, decl, refs, this.projectName, uploaded);
|
|
2982
|
-
|
|
2983
|
-
|
|
3145
|
+
try {
|
|
3146
|
+
const res = await this.client.post("/deployments", body);
|
|
3147
|
+
return toRemoteResource(res);
|
|
3148
|
+
} catch (error) {
|
|
3149
|
+
preserveDeploymentFilesOnConflict(error, uploaded);
|
|
3150
|
+
}
|
|
2984
3151
|
}
|
|
2985
|
-
async updateDeployment(id, name, decl, refs, basePath) {
|
|
2986
|
-
const uploaded = await this.uploadDeploymentFiles(decl, basePath);
|
|
3152
|
+
async updateDeployment(id, name, decl, refs, basePath, preparedFiles) {
|
|
3153
|
+
const uploaded = preparedFiles ? new Map(preparedFiles) : await this.uploadDeploymentFiles(decl, basePath);
|
|
2987
3154
|
const current = await this.client.get(`/deployments/${id}`);
|
|
2988
3155
|
if (current.schedule && !decl.schedule) {
|
|
2989
3156
|
throw new UserError(
|
|
@@ -3091,6 +3258,9 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
3091
3258
|
};
|
|
3092
3259
|
if (bindings.title) body2.title = bindings.title;
|
|
3093
3260
|
if (bindings.metadata) body2.metadata = bindings.metadata;
|
|
3261
|
+
if (bindings.environment_variables) {
|
|
3262
|
+
body2.config = { environment_variables: bindings.environment_variables };
|
|
3263
|
+
}
|
|
3094
3264
|
if (bindings.files?.length) {
|
|
3095
3265
|
body2.resources = bindings.files.map((file) => ({
|
|
3096
3266
|
type: "file",
|
|
@@ -3244,30 +3414,42 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
3244
3414
|
return res.data;
|
|
3245
3415
|
}
|
|
3246
3416
|
// --- Files ---
|
|
3247
|
-
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
|
+
}
|
|
3248
3423
|
const resolved = resolve2(filePath);
|
|
3249
3424
|
const content = readFileSync2(resolved);
|
|
3250
3425
|
const fileName = options?.name ?? basename2(resolved);
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
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);
|
|
3254
3432
|
}
|
|
3255
3433
|
async uploadFileContent(content, filename, options) {
|
|
3256
|
-
const formData =
|
|
3257
|
-
const bytes = new Uint8Array(content);
|
|
3258
|
-
formData.append(
|
|
3259
|
-
"file",
|
|
3260
|
-
options?.mimeType ? new File([bytes], filename, { type: options.mimeType }) : new File([bytes], filename)
|
|
3261
|
-
);
|
|
3262
|
-
if (filename) formData.append("name", filename);
|
|
3263
|
-
if (options?.purpose) formData.append("purpose", options.purpose);
|
|
3434
|
+
const formData = buildFileFormData(content, filename, options);
|
|
3264
3435
|
const res = await this.client.postFormData("/files", formData);
|
|
3265
3436
|
return toRestFileInfo(res);
|
|
3266
3437
|
}
|
|
3267
|
-
async deleteFile(id) {
|
|
3268
|
-
await this.client.delete(`/files/${id}`);
|
|
3438
|
+
async deleteFile(id, mode = "managed") {
|
|
3439
|
+
await (mode === "forward" ? this.forwardClient : this.client).delete(`/files/${id}`);
|
|
3269
3440
|
}
|
|
3270
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
|
+
}
|
|
3271
3453
|
function toSessionInfo2(res) {
|
|
3272
3454
|
return buildSessionInfo(res, (r) => r.memory_store_ids ?? []);
|
|
3273
3455
|
}
|
|
@@ -3317,22 +3499,24 @@ function normalizeQoderMcpServers(value) {
|
|
|
3317
3499
|
});
|
|
3318
3500
|
});
|
|
3319
3501
|
}
|
|
3320
|
-
async function buildSkillFormData(name, decl, files) {
|
|
3502
|
+
async function buildSkillFormData(name, decl, files, fileField = "file", includeCreateFields = true, prefixTopLevelDirectory = false) {
|
|
3321
3503
|
const zip = new JSZip2();
|
|
3322
3504
|
for (const f of files) {
|
|
3323
|
-
zip.file(f.relativePath, f.content);
|
|
3505
|
+
zip.file(prefixTopLevelDirectory ? `${name}/${f.relativePath}` : f.relativePath, f.content);
|
|
3324
3506
|
}
|
|
3325
3507
|
const zipContent = await zip.generateAsync({ type: "uint8array" });
|
|
3326
3508
|
const formData = new FormData();
|
|
3327
3509
|
formData.append(
|
|
3328
|
-
|
|
3510
|
+
fileField,
|
|
3329
3511
|
new File([new Uint8Array(zipContent)], `${name}.zip`, {
|
|
3330
3512
|
type: "application/zip"
|
|
3331
3513
|
})
|
|
3332
3514
|
);
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3515
|
+
if (includeCreateFields) {
|
|
3516
|
+
formData.append("name", name);
|
|
3517
|
+
formData.append("type", "custom");
|
|
3518
|
+
if (decl.description) formData.append("description", decl.description);
|
|
3519
|
+
}
|
|
3336
3520
|
return formData;
|
|
3337
3521
|
}
|
|
3338
3522
|
|
|
@@ -3581,7 +3765,9 @@ function mapAgent3(name, decl, refs, version, projectName, skillVersions) {
|
|
|
3581
3765
|
}
|
|
3582
3766
|
const BAILIAN_BUILTINS = /* @__PURE__ */ new Set(["bash", "read", "write", "edit", "glob", "grep", "download_file"]);
|
|
3583
3767
|
if (decl.tools) {
|
|
3584
|
-
const toolConfigs = resolveBuiltinTools(decl.tools, {
|
|
3768
|
+
const toolConfigs = resolveBuiltinTools(decl.tools, {
|
|
3769
|
+
supportedWireNames: BAILIAN_BUILTINS
|
|
3770
|
+
}).map((tool) => ({
|
|
3585
3771
|
name: tool.wireName,
|
|
3586
3772
|
enabled: true
|
|
3587
3773
|
}));
|
|
@@ -3655,30 +3841,60 @@ function mapSession3(bindings) {
|
|
|
3655
3841
|
if (bindings.memory_store_ids.length) body.memory_store_ids = bindings.memory_store_ids;
|
|
3656
3842
|
return body;
|
|
3657
3843
|
}
|
|
3658
|
-
function
|
|
3844
|
+
function mapDeployment3(name, decl, refs, projectName, uploadedFiles) {
|
|
3845
|
+
const agent = { id: refs.agent_id };
|
|
3846
|
+
if (refs.agent_version !== void 0) agent.version = refs.agent_version;
|
|
3659
3847
|
const body = {
|
|
3660
|
-
|
|
3661
|
-
|
|
3848
|
+
name,
|
|
3849
|
+
agent,
|
|
3850
|
+
environment_id: refs.environment_id,
|
|
3851
|
+
initial_events: mapMessageEvents(decl.initial_events)
|
|
3662
3852
|
};
|
|
3663
|
-
if (decl.description) body.
|
|
3664
|
-
if (
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3853
|
+
if (decl.description) body.description = decl.description;
|
|
3854
|
+
if (refs.vault_ids.length) body.vault_ids = refs.vault_ids;
|
|
3855
|
+
const resources = mapDeploymentResources3(decl, uploadedFiles);
|
|
3856
|
+
if (resources.length) body.resources = resources;
|
|
3857
|
+
if (decl.schedule) {
|
|
3858
|
+
body.schedule = {
|
|
3859
|
+
type: "cron",
|
|
3860
|
+
expression: decl.schedule.expression,
|
|
3861
|
+
timezone: decl.schedule.timezone
|
|
3862
|
+
};
|
|
3863
|
+
}
|
|
3864
|
+
if (projectName) {
|
|
3865
|
+
body.metadata = injectMetadata(decl.metadata, projectName, name);
|
|
3866
|
+
} else if (decl.metadata) {
|
|
3867
|
+
body.metadata = decl.metadata;
|
|
3672
3868
|
}
|
|
3673
3869
|
return body;
|
|
3674
3870
|
}
|
|
3675
|
-
function
|
|
3676
|
-
const
|
|
3871
|
+
function mapDeploymentUpdate3(name, decl, refs, projectName, uploadedFiles, existingMetadata) {
|
|
3872
|
+
const body = mapDeployment3(name, decl, refs, projectName, uploadedFiles);
|
|
3873
|
+
body.description = decl.description ?? "";
|
|
3874
|
+
body.vault_ids = refs.vault_ids;
|
|
3875
|
+
body.resources = mapDeploymentResources3(decl, uploadedFiles);
|
|
3876
|
+
if (!decl.schedule) body.schedule = null;
|
|
3877
|
+
if (!projectName && !decl.metadata && existingMetadata) body.metadata = existingMetadata;
|
|
3878
|
+
return body;
|
|
3879
|
+
}
|
|
3880
|
+
function mapDeploymentResources3(decl, uploadedFiles) {
|
|
3881
|
+
const resources = [];
|
|
3882
|
+
for (const resource of decl.resources ?? []) {
|
|
3883
|
+
if (resource.type !== "file") continue;
|
|
3884
|
+
const fileId = resource.file_id ?? (resource.source ? uploadedFiles?.get(resource.source) : void 0);
|
|
3885
|
+
if (!fileId) continue;
|
|
3886
|
+
const entry = { type: "file", file_id: fileId };
|
|
3887
|
+
if (resource.mount_path) entry.mount_path = resolveSandboxMountPath("bailian", resource.mount_path);
|
|
3888
|
+
resources.push(entry);
|
|
3889
|
+
}
|
|
3890
|
+
return resources;
|
|
3891
|
+
}
|
|
3892
|
+
function mapMessageEvents(events) {
|
|
3893
|
+
return events.filter((event) => event.type === "user.message" || event.type === "system.message").map((event) => ({
|
|
3677
3894
|
role: "user",
|
|
3678
3895
|
type: "message",
|
|
3679
|
-
content: [{ type: "text", text:
|
|
3896
|
+
content: [{ type: "text", text: event.content }]
|
|
3680
3897
|
}));
|
|
3681
|
-
return { input };
|
|
3682
3898
|
}
|
|
3683
3899
|
function mapSendMessage3(text) {
|
|
3684
3900
|
return {
|
|
@@ -3710,7 +3926,8 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
3710
3926
|
agent: "/agents",
|
|
3711
3927
|
skill: "/skills",
|
|
3712
3928
|
vault: "/vaults",
|
|
3713
|
-
file: "/files"
|
|
3929
|
+
file: "/files",
|
|
3930
|
+
deployment: "/deployments"
|
|
3714
3931
|
};
|
|
3715
3932
|
async findResource(type, name, id) {
|
|
3716
3933
|
const accept = type === "agent" ? (r) => !isArchivedAgent(r) : void 0;
|
|
@@ -3976,7 +4193,7 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
3976
4193
|
}
|
|
3977
4194
|
async waitForFileReady(fileId) {
|
|
3978
4195
|
const start = Date.now();
|
|
3979
|
-
let
|
|
4196
|
+
let delay3 = FILE_SCAN_BACKOFF.initial;
|
|
3980
4197
|
let logged = false;
|
|
3981
4198
|
while (true) {
|
|
3982
4199
|
const detail = await this.client.get(`/files/${fileId}`);
|
|
@@ -3995,8 +4212,8 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
3995
4212
|
);
|
|
3996
4213
|
return;
|
|
3997
4214
|
}
|
|
3998
|
-
await new Promise((r) => setTimeout(r,
|
|
3999
|
-
|
|
4215
|
+
await new Promise((r) => setTimeout(r, delay3));
|
|
4216
|
+
delay3 = Math.min(delay3 * FILE_SCAN_BACKOFF.factor, FILE_SCAN_BACKOFF.max);
|
|
4000
4217
|
}
|
|
4001
4218
|
}
|
|
4002
4219
|
// --- Vault (Vaults API) ---
|
|
@@ -4055,46 +4272,93 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
4055
4272
|
async deleteCredential(vaultId, credentialId) {
|
|
4056
4273
|
await this.client.delete(`/vaults/${vaultId}/credentials/${credentialId}`);
|
|
4057
4274
|
}
|
|
4058
|
-
// --- Deployment
|
|
4059
|
-
async createDeployment(
|
|
4060
|
-
|
|
4275
|
+
// --- Deployment ---
|
|
4276
|
+
async createDeployment(name, decl, refs, basePath) {
|
|
4277
|
+
const uploaded = await this.uploadDeploymentFiles(decl, basePath);
|
|
4278
|
+
const body = mapDeployment3(name, decl, refs, this.projectName, uploaded);
|
|
4279
|
+
try {
|
|
4280
|
+
const res = await this.client.post("/deployments", body);
|
|
4281
|
+
return toRemoteResource(res);
|
|
4282
|
+
} catch (error) {
|
|
4283
|
+
preserveDeploymentFilesOnConflict(error, uploaded);
|
|
4284
|
+
}
|
|
4061
4285
|
}
|
|
4062
|
-
async updateDeployment(
|
|
4063
|
-
|
|
4286
|
+
async updateDeployment(id, name, decl, refs, basePath, preparedFiles) {
|
|
4287
|
+
if (!id) return this.createDeployment(name, decl, refs, basePath);
|
|
4288
|
+
const current = await this.client.get(`/deployments/${id}`);
|
|
4289
|
+
const uploaded = preparedFiles ? new Map(preparedFiles) : await this.uploadDeploymentFiles(decl, basePath);
|
|
4290
|
+
const body = mapDeploymentUpdate3(
|
|
4291
|
+
name,
|
|
4292
|
+
decl,
|
|
4293
|
+
refs,
|
|
4294
|
+
this.projectName,
|
|
4295
|
+
uploaded,
|
|
4296
|
+
current.metadata
|
|
4297
|
+
);
|
|
4298
|
+
const res = await this.client.post(`/deployments/${id}`, body);
|
|
4299
|
+
return toRemoteResource(res);
|
|
4064
4300
|
}
|
|
4065
|
-
async deleteDeployment(
|
|
4301
|
+
async deleteDeployment(id) {
|
|
4302
|
+
await this.client.post(`/deployments/${id}/archive`, {});
|
|
4066
4303
|
}
|
|
4067
4304
|
async runDeployment(ctx) {
|
|
4068
|
-
|
|
4069
|
-
|
|
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);
|
|
4305
|
+
if (!ctx.id) {
|
|
4306
|
+
throw new UserError(`Deployment '${ctx.name}' has no remote id; run \`agents apply\` first.`);
|
|
4085
4307
|
}
|
|
4086
|
-
|
|
4308
|
+
const res = await this.client.post(`/deployments/${ctx.id}/run`, {});
|
|
4309
|
+
return {
|
|
4310
|
+
run_id: res.id,
|
|
4311
|
+
session_id: res.session_id ?? null,
|
|
4312
|
+
error: res.error ?? void 0
|
|
4313
|
+
};
|
|
4087
4314
|
}
|
|
4088
4315
|
async getDeployment(ctx) {
|
|
4089
|
-
|
|
4316
|
+
if (!ctx.id) {
|
|
4317
|
+
throw new UserError(`Deployment '${ctx.name}' has no remote id; run \`agents apply\` first.`);
|
|
4318
|
+
}
|
|
4319
|
+
const res = await this.client.get(`/deployments/${ctx.id}`);
|
|
4320
|
+
return toDeploymentInfo3(res);
|
|
4321
|
+
}
|
|
4322
|
+
async listDeployments(filter) {
|
|
4323
|
+
const params = new URLSearchParams();
|
|
4324
|
+
if (filter?.agent_id) params.set("agent_id", filter.agent_id);
|
|
4325
|
+
if (filter?.status) params.set("status", filter.status);
|
|
4326
|
+
if (filter?.include_archived) params.set("include_archived", "true");
|
|
4327
|
+
if (filter?.limit) params.set("limit", String(filter.limit));
|
|
4328
|
+
if (filter?.page) params.set("page", filter.page);
|
|
4329
|
+
if (filter?.created_at_gte) params.set("created_at[gte]", filter.created_at_gte);
|
|
4330
|
+
if (filter?.created_at_lte) params.set("created_at[lte]", filter.created_at_lte);
|
|
4331
|
+
const query2 = params.toString();
|
|
4332
|
+
const res = await this.client.get(`/deployments${query2 ? `?${query2}` : ""}`);
|
|
4333
|
+
const nextPage = res.next_page ?? void 0;
|
|
4090
4334
|
return {
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
attributes: { materialization_plan: plan }
|
|
4335
|
+
deployments: (res.data ?? []).map(toDeploymentInfo3),
|
|
4336
|
+
has_more: nextPage != null,
|
|
4337
|
+
next_page: nextPage
|
|
4095
4338
|
};
|
|
4096
4339
|
}
|
|
4097
|
-
async
|
|
4340
|
+
async pauseDeployment(ctx) {
|
|
4341
|
+
return this.setDeploymentPaused(ctx, true);
|
|
4342
|
+
}
|
|
4343
|
+
async unpauseDeployment(ctx) {
|
|
4344
|
+
return this.setDeploymentPaused(ctx, false);
|
|
4345
|
+
}
|
|
4346
|
+
async setDeploymentPaused(ctx, paused) {
|
|
4347
|
+
if (!ctx.id) throw new UserError(`Deployment '${ctx.name}' has no remote id; run \`agents apply\` first.`);
|
|
4348
|
+
const action = paused ? "pause" : "unpause";
|
|
4349
|
+
const res = await this.client.post(`/deployments/${ctx.id}/${action}`, {});
|
|
4350
|
+
return toDeploymentInfo3(res);
|
|
4351
|
+
}
|
|
4352
|
+
async uploadDeploymentFiles(decl, basePath) {
|
|
4353
|
+
const uploaded = /* @__PURE__ */ new Map();
|
|
4354
|
+
for (const resource of decl.resources ?? []) {
|
|
4355
|
+
if (resource.type === "file" && !resource.file_id && resource.source && !uploaded.has(resource.source)) {
|
|
4356
|
+
uploaded.set(resource.source, await this.uploadDeploymentFile(resource.source, basePath));
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4359
|
+
return uploaded;
|
|
4360
|
+
}
|
|
4361
|
+
async uploadDeploymentFile(source, basePath) {
|
|
4098
4362
|
const fullPath = resolve3(dirname3(basePath), source);
|
|
4099
4363
|
const content = readFileSync3(fullPath);
|
|
4100
4364
|
const formData = new FormData();
|
|
@@ -4190,10 +4454,23 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
4190
4454
|
function isArchivedAgent(raw) {
|
|
4191
4455
|
return typeof raw.archived_at === "string" && raw.archived_at.trim().length > 0;
|
|
4192
4456
|
}
|
|
4193
|
-
function
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4457
|
+
function toDeploymentInfo3(res) {
|
|
4458
|
+
const schedule = res.schedule;
|
|
4459
|
+
return {
|
|
4460
|
+
id: res.id ?? null,
|
|
4461
|
+
status: res.status ?? "unknown",
|
|
4462
|
+
paused_reason: res.paused_reason ?? void 0,
|
|
4463
|
+
schedule: schedule ? {
|
|
4464
|
+
expression: schedule.expression,
|
|
4465
|
+
timezone: schedule.timezone
|
|
4466
|
+
} : void 0,
|
|
4467
|
+
attributes: res
|
|
4468
|
+
};
|
|
4469
|
+
}
|
|
4470
|
+
function toSessionInfo3(res) {
|
|
4471
|
+
return buildSessionInfo(res, () => []);
|
|
4472
|
+
}
|
|
4473
|
+
function toBailianSkillInfo(res) {
|
|
4197
4474
|
const rawSource = String(res.source ?? "").toLowerCase();
|
|
4198
4475
|
const source = rawSource === "customer" ? "custom" : "official";
|
|
4199
4476
|
return {
|
|
@@ -4286,9 +4563,8 @@ var BAILIAN_CAPABILITIES = {
|
|
|
4286
4563
|
remediation: "deploy agents independently and orchestrate via MCP"
|
|
4287
4564
|
},
|
|
4288
4565
|
deployment: {
|
|
4289
|
-
tier: "
|
|
4290
|
-
reason: "
|
|
4291
|
-
remediation: "scheduling and outcome rubrics are not enforced server-side \u2014 use external cron/CI for always-on or scheduled runs"
|
|
4566
|
+
tier: "native",
|
|
4567
|
+
reason: "deployments API with cron schedules, manual runs, pause/unpause and archive"
|
|
4292
4568
|
},
|
|
4293
4569
|
session: { tier: "native", reason: "sessions API" },
|
|
4294
4570
|
identity: { tier: "unsupported", reason: "no mapped Identity primitive on Bailian" },
|
|
@@ -4590,7 +4866,7 @@ function mapAgent4(name, decl, refs, version, projectName) {
|
|
|
4590
4866
|
}
|
|
4591
4867
|
return body;
|
|
4592
4868
|
}
|
|
4593
|
-
function
|
|
4869
|
+
function mapDeploymentToSession(decl, refs, fileIds) {
|
|
4594
4870
|
const body = {
|
|
4595
4871
|
agent: refs.agent_id,
|
|
4596
4872
|
environment_id: refs.environment_id
|
|
@@ -4953,7 +5229,7 @@ var ArkAdapter = class _ArkAdapter {
|
|
|
4953
5229
|
}
|
|
4954
5230
|
}
|
|
4955
5231
|
}
|
|
4956
|
-
const body =
|
|
5232
|
+
const body = mapDeploymentToSession(ctx.decl, ctx.refs, fileIds);
|
|
4957
5233
|
const sessionRes = await this.client.post("/sessions", body);
|
|
4958
5234
|
const sessionId = sessionRes.id;
|
|
4959
5235
|
const events = ctx.decl.initial_events.filter((e) => e.type === "user.message" || e.type === "system.message").map((e) => ({
|
|
@@ -4966,7 +5242,7 @@ var ArkAdapter = class _ArkAdapter {
|
|
|
4966
5242
|
return { session_id: sessionId };
|
|
4967
5243
|
}
|
|
4968
5244
|
async getDeployment(ctx) {
|
|
4969
|
-
const plan =
|
|
5245
|
+
const plan = mapDeploymentToSession(ctx.decl, ctx.refs, []);
|
|
4970
5246
|
return {
|
|
4971
5247
|
id: ctx.id,
|
|
4972
5248
|
status: "emulated (local)",
|
|
@@ -5239,6 +5515,9 @@ var packagesSchema = z5.object({
|
|
|
5239
5515
|
gem: z5.array(z5.string()).optional(),
|
|
5240
5516
|
go: z5.array(z5.string()).optional()
|
|
5241
5517
|
});
|
|
5518
|
+
var setupScriptSchema = z5.string().refine((value) => new TextEncoder().encode(value).byteLength <= 64 * 1024, {
|
|
5519
|
+
message: "setup_script must not exceed 65536 UTF-8 bytes"
|
|
5520
|
+
});
|
|
5242
5521
|
var environmentSchema = z5.object({
|
|
5243
5522
|
name: z5.string().optional(),
|
|
5244
5523
|
description: z5.string().optional(),
|
|
@@ -5248,7 +5527,8 @@ var environmentSchema = z5.object({
|
|
|
5248
5527
|
config: z5.object({
|
|
5249
5528
|
type: z5.enum(["cloud", "self_hosted"]),
|
|
5250
5529
|
networking: networkingSchema.optional(),
|
|
5251
|
-
packages: packagesSchema.optional()
|
|
5530
|
+
packages: packagesSchema.optional(),
|
|
5531
|
+
setup_script: setupScriptSchema.optional()
|
|
5252
5532
|
}),
|
|
5253
5533
|
metadata: z5.record(z5.string(), z5.string()).optional()
|
|
5254
5534
|
});
|
|
@@ -5412,6 +5692,9 @@ var agentSkillRefSchema = z5.object({
|
|
|
5412
5692
|
var agentDeliverySchema = z5.object({
|
|
5413
5693
|
type: z5.enum(["managed", "forward"])
|
|
5414
5694
|
});
|
|
5695
|
+
var managedToolConfigSchema = z5.object({
|
|
5696
|
+
enabled_tools: z5.array(z5.string().min(1))
|
|
5697
|
+
});
|
|
5415
5698
|
var sessionGithubRepoResourceSchema = z5.object({
|
|
5416
5699
|
type: z5.literal("github_repository"),
|
|
5417
5700
|
url: z5.string().url(),
|
|
@@ -5433,18 +5716,27 @@ var agentSchema = z5.object({
|
|
|
5433
5716
|
mcp_servers: z5.array(mcpServerSchema).optional(),
|
|
5434
5717
|
skills: z5.array(z5.union([z5.string(), agentSkillRefSchema])).optional(),
|
|
5435
5718
|
vault: z5.string().optional(),
|
|
5719
|
+
files: z5.array(z5.string()).optional(),
|
|
5436
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(),
|
|
5437
5726
|
resources: z5.array(sessionGithubRepoResourceSchema).optional(),
|
|
5438
5727
|
multiagent: multiagentSchema.optional(),
|
|
5439
5728
|
metadata: z5.record(z5.string(), z5.string()).optional(),
|
|
5729
|
+
environment_variables: z5.record(z5.string().min(1), z5.string()).optional(),
|
|
5730
|
+
managed_tool_config: managedToolConfigSchema.optional(),
|
|
5440
5731
|
delivery: z5.record(z5.string(), agentDeliverySchema).optional()
|
|
5441
5732
|
});
|
|
5442
5733
|
var channelSchema = z5.object({
|
|
5443
5734
|
provider: z5.string().optional(),
|
|
5444
|
-
agent: z5.string().min(1),
|
|
5735
|
+
agent: z5.string().min(1).optional(),
|
|
5445
5736
|
identity: z5.string().min(1).optional(),
|
|
5446
5737
|
type: z5.string().min(1),
|
|
5447
5738
|
name: z5.string().trim().min(1).optional(),
|
|
5739
|
+
mode: z5.enum(["fixed", "pairing"]).optional().default("fixed"),
|
|
5448
5740
|
enabled: z5.boolean().optional(),
|
|
5449
5741
|
credentials: z5.record(z5.string(), coerceString).optional(),
|
|
5450
5742
|
options: z5.record(z5.string(), z5.unknown()).optional()
|
|
@@ -5683,14 +5975,29 @@ async function computeResourceHash(address, config, basePath, state) {
|
|
|
5683
5975
|
if (!decl) return "";
|
|
5684
5976
|
if (address.type === "skill") {
|
|
5685
5977
|
const skillDecl = decl;
|
|
5978
|
+
const apiMode = resolveQoderApiMode(address.type, address.name, address.provider, config);
|
|
5686
5979
|
if (basePath) {
|
|
5687
5980
|
const fileHash = computeSkillContentHash(skillDecl.source, basePath);
|
|
5688
|
-
return contentHash({ decl, fileHash });
|
|
5981
|
+
return contentHash({ decl, fileHash, apiMode });
|
|
5689
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) });
|
|
5987
|
+
}
|
|
5988
|
+
if (address.type === "file" && basePath) {
|
|
5989
|
+
const fileDecl = decl;
|
|
5990
|
+
const fileHash = computeLocalFileContentHash(fileDecl.source, basePath);
|
|
5991
|
+
return contentHash({
|
|
5992
|
+
decl,
|
|
5993
|
+
fileHash,
|
|
5994
|
+
apiMode: resolveQoderApiMode("file", address.name, address.provider, config)
|
|
5995
|
+
});
|
|
5690
5996
|
}
|
|
5691
5997
|
if (address.type === "deployment") {
|
|
5692
5998
|
const refs = resolveDeploymentReferenceIds(decl, config, address.provider, state);
|
|
5693
|
-
|
|
5999
|
+
const sourceHashes = basePath ? computeDeploymentSourceHashes(decl, basePath) : void 0;
|
|
6000
|
+
if (refs || sourceHashes) return contentHash({ decl, refs, sourceHashes });
|
|
5694
6001
|
}
|
|
5695
6002
|
if (address.type === "template") {
|
|
5696
6003
|
const refs = resolveTemplateReferenceIds(decl, config, address.provider, state);
|
|
@@ -5707,7 +6014,28 @@ async function computeResourceHash(address, config, basePath, state) {
|
|
|
5707
6014
|
}
|
|
5708
6015
|
return contentHash(decl);
|
|
5709
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
|
+
}
|
|
6029
|
+
function computeReplacementFingerprint(address, config) {
|
|
6030
|
+
if (address.type !== "channel") return void 0;
|
|
6031
|
+
const decl = config.channels?.[address.name];
|
|
6032
|
+
if (!decl) return void 0;
|
|
6033
|
+
return contentHash({ channel_type: decl.type, mode: decl.mode ?? "fixed", credentials: decl.credentials ?? {} });
|
|
6034
|
+
}
|
|
5710
6035
|
function resolveChannelReferenceIds(decl, config, provider, state) {
|
|
6036
|
+
if (decl.mode === "pairing" || !decl.agent) {
|
|
6037
|
+
return { mode: "pairing" };
|
|
6038
|
+
}
|
|
5711
6039
|
const agent = config.agents?.[decl.agent];
|
|
5712
6040
|
const agentType = agent?.delivery?.[provider]?.type === "forward" ? "template" : "agent";
|
|
5713
6041
|
const identity = decl.identity ?? config.defaults?.identity;
|
|
@@ -5730,7 +6058,14 @@ function resolveTemplateReferenceIds(decl, config, provider, state) {
|
|
|
5730
6058
|
environment_id: environment?.environment_id ?? (decl.environment ? state?.getResource({ type: "environment", name: decl.environment, provider })?.remote_id ?? void 0 : void 0),
|
|
5731
6059
|
tunnel_id: tunnel?.tunnel_id,
|
|
5732
6060
|
vault_ids: decl.vault ? [state?.getResource({ type: "vault", name: decl.vault, provider })?.remote_id ?? decl.vault] : [],
|
|
5733
|
-
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
|
|
5734
6069
|
};
|
|
5735
6070
|
}
|
|
5736
6071
|
function resolveDeploymentReferenceIds(decl, config, provider, state) {
|
|
@@ -5745,6 +6080,23 @@ function resolveDeploymentReferenceIds(decl, config, provider, state) {
|
|
|
5745
6080
|
function getDeclaration(address, config) {
|
|
5746
6081
|
return getResourceDeclaration(address, config);
|
|
5747
6082
|
}
|
|
6083
|
+
function computeDeploymentSourceHashes(decl, basePath) {
|
|
6084
|
+
const sources = [
|
|
6085
|
+
...new Set(
|
|
6086
|
+
(decl.resources ?? []).flatMap(
|
|
6087
|
+
(resource) => resource.type === "file" && !resource.file_id && resource.source ? [resource.source] : []
|
|
6088
|
+
)
|
|
6089
|
+
)
|
|
6090
|
+
];
|
|
6091
|
+
if (sources.length === 0) return void 0;
|
|
6092
|
+
return Object.fromEntries(sources.map((source) => [source, computeLocalFileContentHash(source, basePath)]));
|
|
6093
|
+
}
|
|
6094
|
+
function computeLocalFileContentHash(source, basePath) {
|
|
6095
|
+
const fullPath = resolve9(dirname8(basePath), source);
|
|
6096
|
+
const stat = statSync2(fullPath, { throwIfNoEntry: false });
|
|
6097
|
+
if (!stat?.isFile()) return "";
|
|
6098
|
+
return contentHash(readFileSync6(fullPath).toString("base64"));
|
|
6099
|
+
}
|
|
5748
6100
|
function computeSkillContentHash(source, basePath) {
|
|
5749
6101
|
const fullPath = resolve9(dirname8(basePath), source);
|
|
5750
6102
|
const stat = statSync2(fullPath, { throwIfNoEntry: false });
|
|
@@ -5893,11 +6245,21 @@ function resolveTemplateRefs(agentName, config, provider, state) {
|
|
|
5893
6245
|
const environment = config.environments?.[agent.environment];
|
|
5894
6246
|
if (!environment) throw new UserError(`Environment '${agent.environment}' is not defined in config.`);
|
|
5895
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;
|
|
5896
6252
|
return {
|
|
5897
6253
|
...agentRefs,
|
|
5898
6254
|
environment_id: environment.environment_id ?? requireRef(state, { type: "environment", name: agent.environment, provider }),
|
|
5899
6255
|
...agent.tunnel ? { tunnel_id: resolveTunnelIdFromConfig(config, agent.tunnel, provider) } : {},
|
|
5900
|
-
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 }) } : {}
|
|
5901
6263
|
};
|
|
5902
6264
|
}
|
|
5903
6265
|
function resolveDeploymentRefs(deploymentName, config, provider, state) {
|
|
@@ -5959,6 +6321,12 @@ function resolveDeploymentRefs(deploymentName, config, provider, state) {
|
|
|
5959
6321
|
function resolveChannelRefs(channelName, config, provider, state) {
|
|
5960
6322
|
const channel = config.channels?.[channelName];
|
|
5961
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
|
+
}
|
|
5962
6330
|
const agent = config.agents?.[channel.agent];
|
|
5963
6331
|
if (!agent) throw new UserError(`Channel '${channelName}' references unknown agent '${channel.agent}'`);
|
|
5964
6332
|
const agentType = agent.delivery?.[provider]?.type === "forward" ? "template" : "agent";
|
|
@@ -6081,6 +6449,7 @@ async function executePlan(plan, ctx, options = {}) {
|
|
|
6081
6449
|
await runWithConcurrency(level, concurrency, runAction);
|
|
6082
6450
|
await ctx.state.save();
|
|
6083
6451
|
}
|
|
6452
|
+
await reconcileDefaultMemoryStores(ctx, new Set(plan.actions.map((action) => action.address.provider)));
|
|
6084
6453
|
for (const action of deletions) {
|
|
6085
6454
|
await runAction(action);
|
|
6086
6455
|
await ctx.state.save();
|
|
@@ -6091,6 +6460,41 @@ async function executePlan(plan, ctx, options = {}) {
|
|
|
6091
6460
|
partial: results.some((r) => r.status === "failed")
|
|
6092
6461
|
};
|
|
6093
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
|
+
}
|
|
6094
6498
|
async function runWithConcurrency(items, limit, worker) {
|
|
6095
6499
|
if (items.length === 0) return;
|
|
6096
6500
|
let cursor = 0;
|
|
@@ -6157,8 +6561,8 @@ async function executeAction(action, provider, ctx) {
|
|
|
6157
6561
|
resource: action.address,
|
|
6158
6562
|
message: `update ${action.address.type}.${action.address.name} (${action.address.provider}) \u2014 not found remotely, recreating`
|
|
6159
6563
|
});
|
|
6160
|
-
ctx.state.removeResource(action.address);
|
|
6161
|
-
return executeActionInner({ ...action, action: "create" }, provider, ctx);
|
|
6564
|
+
ctx.state.removeResource(action.previousAddress ?? action.address);
|
|
6565
|
+
return executeActionInner({ ...action, action: "create", previousAddress: void 0 }, provider, ctx);
|
|
6162
6566
|
}
|
|
6163
6567
|
}
|
|
6164
6568
|
async function executeActionInner(action, provider, ctx) {
|
|
@@ -6169,6 +6573,7 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6169
6573
|
const existing = ctx.state.getResource(address);
|
|
6170
6574
|
if (!existing) return false;
|
|
6171
6575
|
const id = existing.remote_id;
|
|
6576
|
+
const apiMode2 = existing.api_mode;
|
|
6172
6577
|
if (type === "environment" || type === "identity") {
|
|
6173
6578
|
const externalReference = type === "environment" ? ctx.config.environments?.[name]?.environment_id : ctx.config.identities?.[name]?.identity_id;
|
|
6174
6579
|
if (existing.externally_managed || externalReference) {
|
|
@@ -6180,13 +6585,13 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6180
6585
|
try {
|
|
6181
6586
|
switch (type) {
|
|
6182
6587
|
case "environment":
|
|
6183
|
-
await provider.deleteEnvironment(id);
|
|
6588
|
+
await provider.deleteEnvironment(id, false, apiMode2);
|
|
6184
6589
|
break;
|
|
6185
6590
|
case "vault":
|
|
6186
|
-
await provider.deleteVault(id);
|
|
6591
|
+
await provider.deleteVault(id, apiMode2);
|
|
6187
6592
|
break;
|
|
6188
6593
|
case "skill":
|
|
6189
|
-
await provider.deleteSkill(id);
|
|
6594
|
+
await provider.deleteSkill(id, apiMode2);
|
|
6190
6595
|
break;
|
|
6191
6596
|
case "agent":
|
|
6192
6597
|
await provider.deleteAgent(id);
|
|
@@ -6194,17 +6599,17 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6194
6599
|
case "template":
|
|
6195
6600
|
if (!provider.archiveTemplate)
|
|
6196
6601
|
throw new UserError(`Provider '${address.provider}' does not support templates`);
|
|
6197
|
-
await provider.archiveTemplate(id);
|
|
6602
|
+
await provider.archiveTemplate(id, ownedForwardMemoryStoreIds(ctx, address.provider));
|
|
6198
6603
|
break;
|
|
6199
6604
|
case "memory_store":
|
|
6200
6605
|
if (!provider.deleteMemoryStore) throw memoryStoreUnsupported(address.provider);
|
|
6201
|
-
await provider.deleteMemoryStore(id);
|
|
6606
|
+
await provider.deleteMemoryStore(id, apiMode2);
|
|
6202
6607
|
break;
|
|
6203
6608
|
case "deployment":
|
|
6204
6609
|
await provider.deleteDeployment(id);
|
|
6205
6610
|
break;
|
|
6206
6611
|
case "file":
|
|
6207
|
-
await provider.deleteFile(id);
|
|
6612
|
+
await provider.deleteFile(id, apiMode2);
|
|
6208
6613
|
break;
|
|
6209
6614
|
case "identity":
|
|
6210
6615
|
if (!provider.deleteIdentity)
|
|
@@ -6231,7 +6636,11 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6231
6636
|
return false;
|
|
6232
6637
|
}
|
|
6233
6638
|
const isUpdate = action.action === "update";
|
|
6234
|
-
const
|
|
6639
|
+
const priorAddress = action.previousAddress ?? address;
|
|
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;
|
|
6235
6644
|
let result;
|
|
6236
6645
|
switch (type) {
|
|
6237
6646
|
case "environment": {
|
|
@@ -6246,6 +6655,17 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6246
6655
|
resource: action.address,
|
|
6247
6656
|
message: `${action.action} ${action.address.type}.${action.address.name} (${action.address.provider}) \u2014 external reference, no remote mutation`
|
|
6248
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);
|
|
6249
6669
|
} else if (isUpdate) {
|
|
6250
6670
|
const prior = ctx.state.getResource(address);
|
|
6251
6671
|
if (prior?.externally_managed) {
|
|
@@ -6253,13 +6673,14 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6253
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).`
|
|
6254
6674
|
);
|
|
6255
6675
|
}
|
|
6256
|
-
result = await provider.updateEnvironment(existingId, remoteName, decl);
|
|
6676
|
+
result = await provider.updateEnvironment(existingId, remoteName, decl, apiMode);
|
|
6257
6677
|
} else {
|
|
6258
6678
|
try {
|
|
6259
|
-
result = await provider.createEnvironment(remoteName, decl);
|
|
6679
|
+
result = await provider.createEnvironment(remoteName, decl, apiMode);
|
|
6260
6680
|
} catch (err) {
|
|
6261
6681
|
result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
|
|
6262
|
-
|
|
6682
|
+
mode: apiMode,
|
|
6683
|
+
onExisting: (existing) => provider.updateEnvironment(existing.id, remoteName, decl, apiMode)
|
|
6263
6684
|
});
|
|
6264
6685
|
adopted = true;
|
|
6265
6686
|
}
|
|
@@ -6268,22 +6689,26 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6268
6689
|
}
|
|
6269
6690
|
case "vault": {
|
|
6270
6691
|
const decl = ctx.config.vaults[name];
|
|
6271
|
-
if (
|
|
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) {
|
|
6272
6696
|
try {
|
|
6273
|
-
result = await provider.createVault(name, decl);
|
|
6274
|
-
await provider.deleteVault(existingId);
|
|
6697
|
+
result = await provider.createVault(name, decl, apiMode);
|
|
6698
|
+
await provider.deleteVault(existingId, apiMode);
|
|
6275
6699
|
} catch {
|
|
6276
|
-
await provider.deleteVault(existingId);
|
|
6277
|
-
result = await provider.createVault(name, decl);
|
|
6700
|
+
await provider.deleteVault(existingId, apiMode);
|
|
6701
|
+
result = await provider.createVault(name, decl, apiMode);
|
|
6278
6702
|
}
|
|
6279
6703
|
} else {
|
|
6280
6704
|
try {
|
|
6281
|
-
result = await provider.createVault(name, decl);
|
|
6705
|
+
result = await provider.createVault(name, decl, apiMode);
|
|
6282
6706
|
} catch (err) {
|
|
6283
6707
|
result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
|
|
6708
|
+
mode: apiMode,
|
|
6284
6709
|
onExisting: async (existing) => {
|
|
6285
|
-
await provider.deleteVault(existing.id);
|
|
6286
|
-
return provider.createVault(name, decl);
|
|
6710
|
+
await provider.deleteVault(existing.id, apiMode);
|
|
6711
|
+
return provider.createVault(name, decl, apiMode);
|
|
6287
6712
|
}
|
|
6288
6713
|
});
|
|
6289
6714
|
adopted = true;
|
|
@@ -6307,12 +6732,15 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6307
6732
|
}
|
|
6308
6733
|
const remoteName = decl.name ?? name;
|
|
6309
6734
|
const files = await resolveSkillFiles(decl, ctx);
|
|
6310
|
-
if (
|
|
6311
|
-
result = await provider.
|
|
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);
|
|
6312
6740
|
} else {
|
|
6313
6741
|
const manifestName = skillNameFromFiles(files);
|
|
6314
6742
|
const searchNames = manifestName && manifestName !== remoteName ? [remoteName, manifestName] : [remoteName];
|
|
6315
|
-
const existing = await findExistingByNames(provider, "skill", searchNames);
|
|
6743
|
+
const existing = await findExistingByNames(provider, "skill", searchNames, apiMode);
|
|
6316
6744
|
if (existing) {
|
|
6317
6745
|
result = existing.resource;
|
|
6318
6746
|
emitRuntimeFeedback(ctx.onFeedback, {
|
|
@@ -6324,9 +6752,10 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6324
6752
|
adopted = true;
|
|
6325
6753
|
} else {
|
|
6326
6754
|
try {
|
|
6327
|
-
result = await provider.createSkill(remoteName, decl, files);
|
|
6755
|
+
result = await provider.createSkill(remoteName, decl, files, apiMode);
|
|
6328
6756
|
} catch (err) {
|
|
6329
6757
|
result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
|
|
6758
|
+
mode: apiMode,
|
|
6330
6759
|
searchNames,
|
|
6331
6760
|
onExisting: async (existing2) => existing2
|
|
6332
6761
|
});
|
|
@@ -6345,15 +6774,19 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6345
6774
|
throw memoryStoreUnsupported(address.provider);
|
|
6346
6775
|
}
|
|
6347
6776
|
const reconcile = async (storeId) => {
|
|
6348
|
-
const store = await provider.updateMemoryStore(
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6777
|
+
const store = await provider.updateMemoryStore(
|
|
6778
|
+
storeId,
|
|
6779
|
+
{
|
|
6780
|
+
name,
|
|
6781
|
+
description: decl.description,
|
|
6782
|
+
metadata: decl.metadata ?? {}
|
|
6783
|
+
},
|
|
6784
|
+
apiMode
|
|
6785
|
+
);
|
|
6353
6786
|
const current = /* @__PURE__ */ new Map();
|
|
6354
6787
|
let cursor;
|
|
6355
6788
|
do {
|
|
6356
|
-
const page2 = await provider.listMemories(storeId, { limit: 100, cursor, view: "basic" });
|
|
6789
|
+
const page2 = await provider.listMemories(storeId, { limit: 100, cursor, view: "basic" }, apiMode);
|
|
6357
6790
|
for (const memory of page2.data) {
|
|
6358
6791
|
if (memory.type === "memory") current.set(memory.path, memory);
|
|
6359
6792
|
}
|
|
@@ -6363,24 +6796,33 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6363
6796
|
const existing = current.get(entry.key.replace(/^\/+/, ""));
|
|
6364
6797
|
if (existing) {
|
|
6365
6798
|
if (existing.content_sha256 !== sha256(entry.content)) {
|
|
6366
|
-
await provider.updateMemory(
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
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
|
+
);
|
|
6370
6808
|
}
|
|
6371
6809
|
} else {
|
|
6372
|
-
await provider.createMemory(storeId, { path: entry.key, content: entry.content });
|
|
6810
|
+
await provider.createMemory(storeId, { path: entry.key, content: entry.content }, apiMode);
|
|
6373
6811
|
}
|
|
6374
6812
|
}
|
|
6375
6813
|
return store;
|
|
6376
6814
|
};
|
|
6377
|
-
if (
|
|
6815
|
+
if (apiModeChanged) {
|
|
6816
|
+
result = await createMemoryStore2(name, decl, apiMode);
|
|
6817
|
+
if (existingId) await deleteMemoryStore2(existingId, priorApiMode).catch(() => void 0);
|
|
6818
|
+
} else if (isUpdate) {
|
|
6378
6819
|
result = await reconcile(existingId);
|
|
6379
6820
|
} else {
|
|
6380
6821
|
try {
|
|
6381
|
-
result = await createMemoryStore2(name, decl);
|
|
6822
|
+
result = await createMemoryStore2(name, decl, apiMode);
|
|
6382
6823
|
} catch (err) {
|
|
6383
6824
|
result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
|
|
6825
|
+
mode: apiMode,
|
|
6384
6826
|
onExisting: async (existing) => reconcile(existing.id)
|
|
6385
6827
|
});
|
|
6386
6828
|
adopted = true;
|
|
@@ -6489,16 +6931,36 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6489
6931
|
case "deployment": {
|
|
6490
6932
|
const decl = ctx.config.deployments[name];
|
|
6491
6933
|
const refs = resolveDeploymentRefs(name, ctx.config, address.provider, ctx.state);
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6934
|
+
const hasLocalFileSources = decl.resources?.some(
|
|
6935
|
+
(resource) => resource.type === "file" && !resource.file_id && Boolean(resource.source)
|
|
6936
|
+
);
|
|
6937
|
+
const materializeDeployment = async () => {
|
|
6495
6938
|
try {
|
|
6496
|
-
|
|
6939
|
+
return await provider.createDeployment(name, decl, refs, ctx.configPath ?? "");
|
|
6497
6940
|
} catch (err) {
|
|
6498
|
-
|
|
6499
|
-
|
|
6941
|
+
const preparedFiles = err instanceof DeploymentCreateConflictError ? err.preparedFiles : void 0;
|
|
6942
|
+
const existing = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
|
|
6943
|
+
onExisting: (existing2) => provider.updateDeployment(existing2.id, name, decl, refs, ctx.configPath ?? "", preparedFiles)
|
|
6500
6944
|
});
|
|
6501
6945
|
adopted = true;
|
|
6946
|
+
return existing;
|
|
6947
|
+
}
|
|
6948
|
+
};
|
|
6949
|
+
if (isUpdate && existingId) {
|
|
6950
|
+
result = await provider.updateDeployment(existingId, name, decl, refs, ctx.configPath ?? "");
|
|
6951
|
+
} else {
|
|
6952
|
+
const existing = hasLocalFileSources ? await findExistingByNames(provider, "deployment", [name]) : null;
|
|
6953
|
+
if (existing) {
|
|
6954
|
+
result = await provider.updateDeployment(existing.resource.id, name, decl, refs, ctx.configPath ?? "");
|
|
6955
|
+
emitRuntimeFeedback(ctx.onFeedback, {
|
|
6956
|
+
type: "resource_adopted",
|
|
6957
|
+
level: "info",
|
|
6958
|
+
resource: address,
|
|
6959
|
+
message: `adopt deployment.${name} (${address.provider}) \u2014 already existed remotely as "${existing.name}"`
|
|
6960
|
+
});
|
|
6961
|
+
adopted = true;
|
|
6962
|
+
} else {
|
|
6963
|
+
result = await materializeDeployment();
|
|
6502
6964
|
}
|
|
6503
6965
|
}
|
|
6504
6966
|
break;
|
|
@@ -6510,15 +6972,12 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6510
6972
|
const oldId = ctx.state.getResource(address)?.remote_id;
|
|
6511
6973
|
if (oldId) {
|
|
6512
6974
|
try {
|
|
6513
|
-
await provider.deleteFile(oldId);
|
|
6975
|
+
await provider.deleteFile(oldId, priorApiMode);
|
|
6514
6976
|
} catch {
|
|
6515
6977
|
}
|
|
6516
6978
|
}
|
|
6517
6979
|
}
|
|
6518
|
-
const info = await provider.uploadFile(filePath, {
|
|
6519
|
-
name: decl.name,
|
|
6520
|
-
purpose: decl.purpose
|
|
6521
|
-
});
|
|
6980
|
+
const info = await provider.uploadFile(filePath, { name: decl.name, purpose: decl.purpose }, apiMode);
|
|
6522
6981
|
result = { id: info.id, type: "file" };
|
|
6523
6982
|
break;
|
|
6524
6983
|
}
|
|
@@ -6534,11 +6993,12 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6534
6993
|
remoteHash = contentHash(remote.comparable);
|
|
6535
6994
|
remoteSnapshot = remote.snapshot ?? remote.comparable;
|
|
6536
6995
|
}
|
|
6537
|
-
const priorResource = ctx.state.getResource(
|
|
6996
|
+
const priorResource = ctx.state.getResource(priorAddress);
|
|
6538
6997
|
ctx.state.setResource({
|
|
6539
6998
|
address,
|
|
6540
6999
|
remote_id: result.id,
|
|
6541
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,
|
|
6542
7002
|
version: result.version,
|
|
6543
7003
|
content_hash: hash,
|
|
6544
7004
|
desired_hash: hash,
|
|
@@ -6546,14 +7006,44 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6546
7006
|
desired_readiness_baseline: buildReadinessBaseline(getResourceDeclaration(address, ctx.config)),
|
|
6547
7007
|
remote_hash: remoteHash,
|
|
6548
7008
|
remote_snapshot: remoteSnapshot,
|
|
7009
|
+
replacement_fingerprint: computeReplacementFingerprint(address, ctx.config),
|
|
6549
7010
|
drift_paths: [],
|
|
6550
7011
|
drift_status: remoteHash ? "in_sync" : void 0
|
|
6551
7012
|
});
|
|
7013
|
+
if (action.previousAddress) ctx.state.removeResource(action.previousAddress);
|
|
6552
7014
|
return adopted;
|
|
6553
7015
|
}
|
|
6554
|
-
|
|
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) {
|
|
6555
7045
|
for (const candidate of names) {
|
|
6556
|
-
const found = await provider.findResource(type, candidate);
|
|
7046
|
+
const found = await provider.findResource(type, candidate, void 0, mode);
|
|
6557
7047
|
if (found && found.id !== null) return { resource: found, name: candidate };
|
|
6558
7048
|
}
|
|
6559
7049
|
return null;
|
|
@@ -6561,7 +7051,7 @@ async function findExistingByNames(provider, type, names) {
|
|
|
6561
7051
|
async function adoptOnConflict(err, address, provider, onFeedback, opts) {
|
|
6562
7052
|
if (!(err instanceof ConflictError)) throw err;
|
|
6563
7053
|
const candidates = opts.searchNames?.length ? opts.searchNames : [address.name];
|
|
6564
|
-
const existing = await findExistingByNames(provider, address.type, candidates);
|
|
7054
|
+
const existing = await findExistingByNames(provider, address.type, candidates, opts.mode);
|
|
6565
7055
|
if (!existing) throw nameReservedError(err, address, candidates.join('" / "'));
|
|
6566
7056
|
emitRuntimeFeedback(onFeedback, {
|
|
6567
7057
|
type: "resource_adopted",
|
|
@@ -6573,6 +7063,11 @@ async function adoptOnConflict(err, address, provider, onFeedback, opts) {
|
|
|
6573
7063
|
}
|
|
6574
7064
|
function nameReservedError(err, address, searchName) {
|
|
6575
7065
|
const detail = err instanceof ApiError ? err.message : String(err);
|
|
7066
|
+
if (address.type === "channel" && err instanceof ApiError && err.responseBody.includes("CHANNEL_CREDENTIAL_CONFLICT")) {
|
|
7067
|
+
return new UserError(
|
|
7068
|
+
`${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})`
|
|
7069
|
+
);
|
|
7070
|
+
}
|
|
6576
7071
|
return new UserError(
|
|
6577
7072
|
`${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
7073
|
);
|
|
@@ -6645,6 +7140,13 @@ function agentTargetsProvider(config, agentProvider, providerName) {
|
|
|
6645
7140
|
return Object.hasOwn(config.providers, providerName);
|
|
6646
7141
|
}
|
|
6647
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
|
+
|
|
6648
7150
|
// src/internal/core/validate-config.ts
|
|
6649
7151
|
function validateProjectConfig(config, options = {}) {
|
|
6650
7152
|
const collector = new DiagnosticCollector();
|
|
@@ -6671,6 +7173,7 @@ function collectReferenceDiagnostics(config, diagnostics) {
|
|
|
6671
7173
|
const skillNames = new Set(Object.keys(config.skills ?? {}));
|
|
6672
7174
|
const vaultNames = new Set(Object.keys(config.vaults ?? {}));
|
|
6673
7175
|
const memoryNames = new Set(Object.keys(config.memory_stores ?? {}));
|
|
7176
|
+
const fileNames = new Set(Object.keys(config.files ?? {}));
|
|
6674
7177
|
const agentNames = new Set(Object.keys(config.agents ?? {}));
|
|
6675
7178
|
const identityNames = new Set(Object.keys(config.identities ?? {}));
|
|
6676
7179
|
if (config.defaults?.identity && !identityNames.has(config.defaults.identity)) {
|
|
@@ -6698,6 +7201,11 @@ function collectReferenceDiagnostics(config, diagnostics) {
|
|
|
6698
7201
|
if (agent.vault && !vaultNames.has(agent.vault)) {
|
|
6699
7202
|
diagnostics.error("config.agent.vault.unknown", `agent.${name}: references unknown vault '${agent.vault}'`);
|
|
6700
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
|
+
}
|
|
6701
7209
|
for (const memory of agent.memory_stores ?? []) {
|
|
6702
7210
|
if (!memoryNames.has(memory)) {
|
|
6703
7211
|
diagnostics.error(
|
|
@@ -6729,7 +7237,10 @@ function collectReferenceDiagnostics(config, diagnostics) {
|
|
|
6729
7237
|
}
|
|
6730
7238
|
}
|
|
6731
7239
|
for (const [name, channel] of Object.entries(config.channels ?? {})) {
|
|
6732
|
-
if (
|
|
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)) {
|
|
6733
7244
|
diagnostics.error("config.channel.agent.unknown", `channel.${name}: references unknown agent '${channel.agent}'`);
|
|
6734
7245
|
}
|
|
6735
7246
|
const identity = channel.identity ?? config.defaults?.identity;
|
|
@@ -6757,6 +7268,37 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6757
7268
|
continue;
|
|
6758
7269
|
}
|
|
6759
7270
|
const caps = def.capabilities;
|
|
7271
|
+
for (const [name, environment] of Object.entries(config.environments ?? {})) {
|
|
7272
|
+
if (environment.provider && environment.provider !== providerName) continue;
|
|
7273
|
+
if (environment.environment_id) continue;
|
|
7274
|
+
const address = { type: "environment", name, provider: providerName };
|
|
7275
|
+
if (environment.config.setup_script !== void 0 && providerName !== "qoder") {
|
|
7276
|
+
diagnostics.error(
|
|
7277
|
+
`${providerName}.environment.setup_script.unsupported`,
|
|
7278
|
+
`environment.${name}: provider '${providerName}' does not support setup_script; remove it or pin this environment to qoder.`,
|
|
7279
|
+
address
|
|
7280
|
+
);
|
|
7281
|
+
}
|
|
7282
|
+
if (providerName === "qoder") {
|
|
7283
|
+
if (environment.config.type === "self_hosted" && (environment.config.networking !== void 0 || environment.config.packages !== void 0)) {
|
|
7284
|
+
diagnostics.error(
|
|
7285
|
+
"qoder.environment.self_hosted.config.unsupported",
|
|
7286
|
+
`environment.${name}: Qoder self_hosted environments accept only config.type and config.setup_script; remove networking and packages.`,
|
|
7287
|
+
address
|
|
7288
|
+
);
|
|
7289
|
+
}
|
|
7290
|
+
const unsupported = ["cargo", "gem", "go"].filter(
|
|
7291
|
+
(key) => (environment.config.packages?.[key]?.length ?? 0) > 0
|
|
7292
|
+
);
|
|
7293
|
+
if (unsupported.length > 0) {
|
|
7294
|
+
diagnostics.error(
|
|
7295
|
+
"qoder.environment.packages.unsupported",
|
|
7296
|
+
`environment.${name}: Qoder accepts only apt, npm, and pip package declarations; remove ${unsupported.join(", ")} or install them from setup_script.`,
|
|
7297
|
+
address
|
|
7298
|
+
);
|
|
7299
|
+
}
|
|
7300
|
+
}
|
|
7301
|
+
}
|
|
6760
7302
|
for (const [name, identity] of Object.entries(config.identities ?? {})) {
|
|
6761
7303
|
if (identity.provider && identity.provider !== providerName) continue;
|
|
6762
7304
|
if (!isSupported(caps, "identity")) {
|
|
@@ -6778,29 +7320,31 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6778
7320
|
continue;
|
|
6779
7321
|
}
|
|
6780
7322
|
if (providerName === "qoder") {
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
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
|
+
}
|
|
6804
7348
|
}
|
|
6805
7349
|
const requiredCredentials = {
|
|
6806
7350
|
dingtalk: ["client_id", "client_secret"],
|
|
@@ -6831,6 +7375,46 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6831
7375
|
}
|
|
6832
7376
|
}
|
|
6833
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
|
+
}
|
|
6834
7418
|
for (const [name, agent] of Object.entries(config.agents ?? {})) {
|
|
6835
7419
|
if (agent.provider && agent.provider !== providerName) continue;
|
|
6836
7420
|
const delivery = agent.delivery?.[providerName]?.type ?? "managed";
|
|
@@ -6871,6 +7455,44 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6871
7455
|
address
|
|
6872
7456
|
);
|
|
6873
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
|
+
}
|
|
6874
7496
|
if (delivery === "forward" && !isSupported(caps, "template")) {
|
|
6875
7497
|
diagnostics.error(
|
|
6876
7498
|
`${providerName}.agent.delivery.forward.unsupported`,
|
|
@@ -6886,10 +7508,10 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6886
7508
|
{ type: "template", name, provider: providerName }
|
|
6887
7509
|
);
|
|
6888
7510
|
}
|
|
6889
|
-
if (agent.memory_stores?.length) {
|
|
7511
|
+
if (agent.memory_stores?.length && !config.defaults?.identity) {
|
|
6890
7512
|
diagnostics.error(
|
|
6891
|
-
"qoder.template.memory_store.
|
|
6892
|
-
`agent.${name}: memory_stores
|
|
7513
|
+
"qoder.template.memory_store.identity.required",
|
|
7514
|
+
`agent.${name}: Forward memory_stores require defaults.identity for the Identity/Template mount.`,
|
|
6893
7515
|
{ type: "template", name, provider: providerName }
|
|
6894
7516
|
);
|
|
6895
7517
|
}
|
|
@@ -6923,6 +7545,68 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6923
7545
|
}
|
|
6924
7546
|
}
|
|
6925
7547
|
}
|
|
7548
|
+
if (providerName === "bailian") {
|
|
7549
|
+
for (const [name, deployment] of Object.entries(config.deployments ?? {})) {
|
|
7550
|
+
if (deployment.provider && deployment.provider !== providerName) continue;
|
|
7551
|
+
const addr = {
|
|
7552
|
+
type: "deployment",
|
|
7553
|
+
name,
|
|
7554
|
+
provider: providerName
|
|
7555
|
+
};
|
|
7556
|
+
if (deployment.initial_events?.some((event) => event.type === "user.define_outcome")) {
|
|
7557
|
+
diagnostics.warning(
|
|
7558
|
+
`${providerName}.deployment.define_outcome_unsupported`,
|
|
7559
|
+
"Outcome rubrics (user.define_outcome) are dropped from the Bailian deployment payload; the run executes without rubric grading.",
|
|
7560
|
+
addr
|
|
7561
|
+
);
|
|
7562
|
+
}
|
|
7563
|
+
if (!deployment.initial_events?.some((event) => event.type === "user.message" || event.type === "system.message")) {
|
|
7564
|
+
diagnostics.error(
|
|
7565
|
+
`${providerName}.deployment.initial_events.message_required`,
|
|
7566
|
+
`deployment.${name}: Bailian requires at least one user.message or system.message initial event; user.define_outcome events are dropped.`,
|
|
7567
|
+
addr
|
|
7568
|
+
);
|
|
7569
|
+
}
|
|
7570
|
+
if (deployment.resources?.some((resource) => resource.type === "github_repository")) {
|
|
7571
|
+
diagnostics.warning(
|
|
7572
|
+
`${providerName}.deployment.github_repository_unsupported`,
|
|
7573
|
+
"Bailian deployment resources accept files only; github_repository resources are dropped. Clone the repository inside the session instead.",
|
|
7574
|
+
addr
|
|
7575
|
+
);
|
|
7576
|
+
}
|
|
7577
|
+
const mountPrefix = providerMountPrefix(providerName);
|
|
7578
|
+
const normalizedMountPaths = /* @__PURE__ */ new Set();
|
|
7579
|
+
for (const resource of deployment.resources ?? []) {
|
|
7580
|
+
if (resource.type !== "file") continue;
|
|
7581
|
+
if (!resource.mount_path?.trim()) {
|
|
7582
|
+
diagnostics.error(
|
|
7583
|
+
`${providerName}.deployment.file.mount_path.required`,
|
|
7584
|
+
`deployment.${name}: Bailian file resources require mount_path.`,
|
|
7585
|
+
addr
|
|
7586
|
+
);
|
|
7587
|
+
continue;
|
|
7588
|
+
}
|
|
7589
|
+
if (mountPrefix && resource.mount_path.startsWith("/") && resource.mount_path !== mountPrefix && !resource.mount_path.startsWith(`${mountPrefix}/`)) {
|
|
7590
|
+
diagnostics.error(
|
|
7591
|
+
`${providerName}.deployment.file.mount_path.invalid`,
|
|
7592
|
+
`deployment.${name}: Bailian file mount_path must start with '${mountPrefix}/'.`,
|
|
7593
|
+
addr
|
|
7594
|
+
);
|
|
7595
|
+
continue;
|
|
7596
|
+
}
|
|
7597
|
+
const normalizedMountPath = resolveSandboxMountPath(providerName, resource.mount_path);
|
|
7598
|
+
if (normalizedMountPaths.has(normalizedMountPath)) {
|
|
7599
|
+
diagnostics.error(
|
|
7600
|
+
`${providerName}.deployment.file.mount_path.duplicate`,
|
|
7601
|
+
`deployment.${name}: Bailian file mount_path '${normalizedMountPath}' is duplicated after normalization.`,
|
|
7602
|
+
addr
|
|
7603
|
+
);
|
|
7604
|
+
} else {
|
|
7605
|
+
normalizedMountPaths.add(normalizedMountPath);
|
|
7606
|
+
}
|
|
7607
|
+
}
|
|
7608
|
+
}
|
|
7609
|
+
}
|
|
6926
7610
|
if (providerName !== "qoder") {
|
|
6927
7611
|
for (const [name, env] of Object.entries(config.environments ?? {})) {
|
|
6928
7612
|
if (env.environment_id) continue;
|
|
@@ -6935,6 +7619,20 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6935
7619
|
}
|
|
6936
7620
|
}
|
|
6937
7621
|
for (const [name, agent] of Object.entries(config.agents ?? {})) {
|
|
7622
|
+
if (agent.environment_variables && (!agent.provider || agent.provider === providerName)) {
|
|
7623
|
+
diagnostics.error(
|
|
7624
|
+
`${providerName}.agent.environment_variables.unsupported`,
|
|
7625
|
+
`agent.${name}: environment_variables is supported only by Qoder; remove it or pin this agent to qoder.`,
|
|
7626
|
+
{ type: "agent", name, provider: providerName }
|
|
7627
|
+
);
|
|
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
|
+
}
|
|
6938
7636
|
if (agent.tunnel && (!agent.provider || agent.provider === providerName)) {
|
|
6939
7637
|
diagnostics.error(
|
|
6940
7638
|
`${providerName}.agent.tunnel.unsupported`,
|
|
@@ -6995,7 +7693,11 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6995
7693
|
if (config.deployments && caps.deployment.tier === "emulated") {
|
|
6996
7694
|
for (const [name, dep] of Object.entries(config.deployments)) {
|
|
6997
7695
|
if (dep.provider && dep.provider !== providerName) continue;
|
|
6998
|
-
const addr = {
|
|
7696
|
+
const addr = {
|
|
7697
|
+
type: "deployment",
|
|
7698
|
+
name,
|
|
7699
|
+
provider: providerName
|
|
7700
|
+
};
|
|
6999
7701
|
if (dep.schedule) {
|
|
7000
7702
|
diagnostics.warning(
|
|
7001
7703
|
`${providerName}.deployment.schedule_unsupported`,
|
|
@@ -7029,13 +7731,6 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
7029
7731
|
}
|
|
7030
7732
|
}
|
|
7031
7733
|
|
|
7032
|
-
// src/internal/core/agent-materialization.ts
|
|
7033
|
-
function resolveAgentMaterialization(provider, agent) {
|
|
7034
|
-
const mode = agent.delivery?.[provider]?.type ?? "managed";
|
|
7035
|
-
if (mode === "managed") return { resourceType: "agent", mode };
|
|
7036
|
-
return { resourceType: "template", mode };
|
|
7037
|
-
}
|
|
7038
|
-
|
|
7039
7734
|
// src/internal/graph/dependency.ts
|
|
7040
7735
|
function buildDependencyGraph(config, targetProviders) {
|
|
7041
7736
|
const nodes = /* @__PURE__ */ new Map();
|
|
@@ -7103,6 +7798,13 @@ function buildDependencyGraph(config, targetProviders) {
|
|
|
7103
7798
|
const materialization = resolveAgentMaterialization(provider, decl);
|
|
7104
7799
|
const agentAddr = { type: materialization.resourceType, name, provider };
|
|
7105
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
|
+
}
|
|
7106
7808
|
if (decl.environment && config.environments?.[decl.environment]) {
|
|
7107
7809
|
const envAddr = {
|
|
7108
7810
|
type: "environment",
|
|
@@ -7129,6 +7831,12 @@ function buildDependencyGraph(config, targetProviders) {
|
|
|
7129
7831
|
addEdge(agentAddr, vaultAddr);
|
|
7130
7832
|
}
|
|
7131
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
|
+
}
|
|
7132
7840
|
if (decl.memory_stores) {
|
|
7133
7841
|
for (const msName of decl.memory_stores) {
|
|
7134
7842
|
const msAddr = {
|
|
@@ -7193,6 +7901,7 @@ function buildDependencyGraph(config, targetProviders) {
|
|
|
7193
7901
|
if (decl.provider && decl.provider !== provider) continue;
|
|
7194
7902
|
const channelAddr = { type: "channel", name, provider };
|
|
7195
7903
|
addNode(channelAddr);
|
|
7904
|
+
if (decl.mode === "pairing" || !decl.agent) continue;
|
|
7196
7905
|
const agentDecl = config.agents?.[decl.agent];
|
|
7197
7906
|
const agentType = agentDecl ? resolveAgentMaterialization(provider, agentDecl).resourceType : "agent";
|
|
7198
7907
|
const agentAddr = { type: agentType, name: decl.agent, provider };
|
|
@@ -7254,6 +7963,7 @@ async function buildPlan(config, state, options = {}) {
|
|
|
7254
7963
|
const desiredHash = await computeResourceHash(address, config, options.configPath, hashStateLookup);
|
|
7255
7964
|
const existing = stateIndex.get(key);
|
|
7256
7965
|
const deps = getDependencies(address, graph);
|
|
7966
|
+
const needsNativeDeploymentMaterialization = address.type === "deployment" && existing?.remote_id === null && getProvider(address.provider)?.capabilities.deployment.tier === "native";
|
|
7257
7967
|
if (address.type === "environment" && existing) {
|
|
7258
7968
|
const envDecl = config.environments?.[address.name];
|
|
7259
7969
|
if (existing.externally_managed && envDecl && !envDecl.environment_id) {
|
|
@@ -7305,6 +8015,17 @@ async function buildPlan(config, state, options = {}) {
|
|
|
7305
8015
|
after: { content_hash: desiredHash },
|
|
7306
8016
|
dependencies: deps
|
|
7307
8017
|
});
|
|
8018
|
+
} else if (needsNativeDeploymentMaterialization) {
|
|
8019
|
+
actions.push({
|
|
8020
|
+
action: "update",
|
|
8021
|
+
address,
|
|
8022
|
+
driftKind: "none",
|
|
8023
|
+
readinessImpact: "blocking",
|
|
8024
|
+
reason: "Materialize legacy state as a native deployment",
|
|
8025
|
+
before: { content_hash: existing.desired_hash ?? existing.content_hash },
|
|
8026
|
+
after: { content_hash: desiredHash },
|
|
8027
|
+
dependencies: deps
|
|
8028
|
+
});
|
|
7308
8029
|
} else if ((existing.desired_hash ?? existing.content_hash) !== desiredHash && existing.drift_status === "drifted") {
|
|
7309
8030
|
const changedPaths = collectChangedPaths(address, config, existing, true);
|
|
7310
8031
|
actions.push({
|
|
@@ -7377,8 +8098,62 @@ async function buildPlan(config, state, options = {}) {
|
|
|
7377
8098
|
dependencies: replacement ? [replacement] : []
|
|
7378
8099
|
});
|
|
7379
8100
|
}
|
|
8101
|
+
coalesceChannelRenames(actions, config, state);
|
|
7380
8102
|
return { actions, diagnostics: diagnostics.getAll() };
|
|
7381
8103
|
}
|
|
8104
|
+
function coalesceChannelRenames(actions, config, state) {
|
|
8105
|
+
const creates = actions.filter((action) => action.action === "create" && action.address.type === "channel");
|
|
8106
|
+
const deletes = actions.filter((action) => action.action === "delete" && action.address.type === "channel");
|
|
8107
|
+
const stateByAddress = new Map(state.resources.map((resource) => [addressKey(resource.address), resource]));
|
|
8108
|
+
const matchedDeletes = /* @__PURE__ */ new Set();
|
|
8109
|
+
for (const create of creates) {
|
|
8110
|
+
const desiredType = config.channels?.[create.address.name]?.type;
|
|
8111
|
+
if (!desiredType) continue;
|
|
8112
|
+
const desiredFingerprint = computeReplacementFingerprint(create.address, config);
|
|
8113
|
+
const candidates = deletes.filter((deletion2) => {
|
|
8114
|
+
if (matchedDeletes.has(deletion2) || deletion2.address.provider !== create.address.provider) return false;
|
|
8115
|
+
const prior2 = stateByAddress.get(addressKey(deletion2.address));
|
|
8116
|
+
const snapshot = prior2?.remote_snapshot;
|
|
8117
|
+
if (snapshot?.channel_type !== desiredType) return false;
|
|
8118
|
+
return !prior2?.replacement_fingerprint || prior2.replacement_fingerprint === desiredFingerprint;
|
|
8119
|
+
});
|
|
8120
|
+
if (candidates.length !== 1) continue;
|
|
8121
|
+
const deletion = candidates[0];
|
|
8122
|
+
const prior = stateByAddress.get(addressKey(deletion.address));
|
|
8123
|
+
const competingCreates = creates.filter(
|
|
8124
|
+
(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)
|
|
8125
|
+
);
|
|
8126
|
+
if (competingCreates.length > 0) continue;
|
|
8127
|
+
create.action = "update";
|
|
8128
|
+
create.previousAddress = deletion.address;
|
|
8129
|
+
create.before = deletion.before;
|
|
8130
|
+
create.driftKind = "local";
|
|
8131
|
+
create.reason = `Channel key renamed from '${deletion.address.name}' (remote resource retained)`;
|
|
8132
|
+
protectRenamedChannelDependencies(actions, stateByAddress, deletion, create);
|
|
8133
|
+
matchedDeletes.add(deletion);
|
|
8134
|
+
}
|
|
8135
|
+
for (let index = actions.length - 1; index >= 0; index--) {
|
|
8136
|
+
if (matchedDeletes.has(actions[index])) actions.splice(index, 1);
|
|
8137
|
+
}
|
|
8138
|
+
}
|
|
8139
|
+
function protectRenamedChannelDependencies(actions, stateByAddress, deletion, replacement) {
|
|
8140
|
+
const prior = stateByAddress.get(addressKey(deletion.address));
|
|
8141
|
+
const snapshot = prior?.remote_snapshot;
|
|
8142
|
+
const referencedIds = new Set(
|
|
8143
|
+
[snapshot?.identity_id, snapshot?.template_id].filter((id) => typeof id === "string")
|
|
8144
|
+
);
|
|
8145
|
+
if (referencedIds.size === 0) return;
|
|
8146
|
+
for (const action of actions) {
|
|
8147
|
+
if (action.action !== "delete" || action.address.type !== "identity" && action.address.type !== "template" || action.address.provider !== replacement.address.provider) {
|
|
8148
|
+
continue;
|
|
8149
|
+
}
|
|
8150
|
+
const dependency = stateByAddress.get(addressKey(action.address));
|
|
8151
|
+
if (!dependency?.remote_id || !referencedIds.has(dependency.remote_id)) continue;
|
|
8152
|
+
if (!action.dependencies.some((address) => addressKey(address) === addressKey(replacement.address))) {
|
|
8153
|
+
action.dependencies.push(replacement.address);
|
|
8154
|
+
}
|
|
8155
|
+
}
|
|
8156
|
+
}
|
|
7382
8157
|
function deliveryReplacementAddress(address, graph) {
|
|
7383
8158
|
if (address.type !== "agent" && address.type !== "template") return void 0;
|
|
7384
8159
|
const replacementType = address.type === "agent" ? "template" : "agent";
|
|
@@ -7467,7 +8242,7 @@ async function refreshState(state, providers, options = {}) {
|
|
|
7467
8242
|
dirty = true;
|
|
7468
8243
|
continue;
|
|
7469
8244
|
}
|
|
7470
|
-
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);
|
|
7471
8246
|
if (!remote) {
|
|
7472
8247
|
if (!options.quiet) {
|
|
7473
8248
|
emitRuntimeFeedback(options.onFeedback, {
|
|
@@ -8025,12 +8800,61 @@ function planDestroyProjectContext(ctx) {
|
|
|
8025
8800
|
const resources = [...ctx.state.listResources()].sort(
|
|
8026
8801
|
(a, b) => (destroyOrder[a.address.type] ?? 99) - (destroyOrder[b.address.type] ?? 99)
|
|
8027
8802
|
);
|
|
8028
|
-
|
|
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 };
|
|
8029
8835
|
}
|
|
8030
8836
|
async function destroyPlannedProjectResources(planned, options = {}) {
|
|
8031
8837
|
const results = [];
|
|
8032
|
-
|
|
8838
|
+
const capturedDefaults = await captureDefaultMemoryStores(planned);
|
|
8033
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;
|
|
8034
8858
|
for (const resource of planned.resources) {
|
|
8035
8859
|
options.onResourceStart?.(resource);
|
|
8036
8860
|
const result = await destroyOneResource(ctx, resource, options);
|
|
@@ -8041,14 +8865,171 @@ async function destroyPlannedProjectResources(planned, options = {}) {
|
|
|
8041
8865
|
if (stateChanged) {
|
|
8042
8866
|
await ctx.state.save();
|
|
8043
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
|
+
}
|
|
8044
8875
|
const destroyed = results.filter((result) => result.status === "success").length;
|
|
8045
8876
|
return {
|
|
8046
8877
|
...planned,
|
|
8047
8878
|
results,
|
|
8879
|
+
defaultMemoryStoreResults,
|
|
8048
8880
|
destroyed,
|
|
8049
|
-
partial: destroyed !== planned.resources.length
|
|
8881
|
+
partial: destroyed !== planned.resources.length || defaultMemoryStoreResults.some((result) => result.status === "failed")
|
|
8050
8882
|
};
|
|
8051
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."
|
|
8936
|
+
};
|
|
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
|
+
}
|
|
8052
9033
|
async function destroyOneResource(ctx, resource, options) {
|
|
8053
9034
|
if (isExternalReference(ctx, resource)) {
|
|
8054
9035
|
ctx.state.removeResource(resource.address);
|
|
@@ -8069,8 +9050,18 @@ async function destroyOneResource(ctx, resource, options) {
|
|
|
8069
9050
|
ctx.state.removeResource(resource.address);
|
|
8070
9051
|
return successResult(resource, "destroyed");
|
|
8071
9052
|
}
|
|
9053
|
+
const apiMode = resource.api_mode === "auto" ? void 0 : resource.api_mode;
|
|
8072
9054
|
try {
|
|
8073
|
-
await deleteRemoteResource(
|
|
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
|
+
);
|
|
8074
9065
|
ctx.state.removeResource(resource.address);
|
|
8075
9066
|
emitRuntimeFeedback(options.onFeedback, {
|
|
8076
9067
|
type: "resource_action_success",
|
|
@@ -8099,7 +9090,7 @@ async function destroyOneResource(ctx, resource, options) {
|
|
|
8099
9090
|
};
|
|
8100
9091
|
if (await options.onCascadeRequired?.(blocked)) {
|
|
8101
9092
|
try {
|
|
8102
|
-
await provider.deleteEnvironment(resource.remote_id, true);
|
|
9093
|
+
await provider.deleteEnvironment(resource.remote_id, true, apiMode);
|
|
8103
9094
|
ctx.state.removeResource(resource.address);
|
|
8104
9095
|
return {
|
|
8105
9096
|
...successResult(resource, "destroyed"),
|
|
@@ -8135,29 +9126,29 @@ function failureResult(resource, error) {
|
|
|
8135
9126
|
error: error instanceof Error ? error.message : String(error)
|
|
8136
9127
|
};
|
|
8137
9128
|
}
|
|
8138
|
-
async function deleteRemoteResource(provider, type, id, cascade) {
|
|
9129
|
+
async function deleteRemoteResource(provider, type, id, cascade, mode, ownedMemoryStoreIds = []) {
|
|
8139
9130
|
switch (type) {
|
|
8140
9131
|
case "agent":
|
|
8141
9132
|
await provider.deleteAgent(id);
|
|
8142
9133
|
return;
|
|
8143
9134
|
case "template":
|
|
8144
9135
|
if (!provider.archiveTemplate) throw new UserError(`Provider does not support templates`);
|
|
8145
|
-
await provider.archiveTemplate(id);
|
|
9136
|
+
await provider.archiveTemplate(id, ownedMemoryStoreIds);
|
|
8146
9137
|
return;
|
|
8147
9138
|
case "skill":
|
|
8148
|
-
await provider.deleteSkill(id);
|
|
9139
|
+
await provider.deleteSkill(id, mode);
|
|
8149
9140
|
return;
|
|
8150
9141
|
case "memory_store":
|
|
8151
9142
|
if (!provider.deleteMemoryStore) {
|
|
8152
9143
|
throw new UserError(`Provider does not support memory stores`);
|
|
8153
9144
|
}
|
|
8154
|
-
await provider.deleteMemoryStore(id);
|
|
9145
|
+
await provider.deleteMemoryStore(id, mode);
|
|
8155
9146
|
return;
|
|
8156
9147
|
case "vault":
|
|
8157
|
-
await provider.deleteVault(id);
|
|
9148
|
+
await provider.deleteVault(id, mode);
|
|
8158
9149
|
return;
|
|
8159
9150
|
case "environment":
|
|
8160
|
-
await provider.deleteEnvironment(id, cascade);
|
|
9151
|
+
await provider.deleteEnvironment(id, cascade, mode);
|
|
8161
9152
|
return;
|
|
8162
9153
|
case "deployment":
|
|
8163
9154
|
await provider.deleteDeployment(id);
|
|
@@ -8171,7 +9162,7 @@ async function deleteRemoteResource(provider, type, id, cascade) {
|
|
|
8171
9162
|
await provider.deleteChannel(id);
|
|
8172
9163
|
return;
|
|
8173
9164
|
case "file":
|
|
8174
|
-
await provider.deleteFile(id);
|
|
9165
|
+
await provider.deleteFile(id, mode);
|
|
8175
9166
|
return;
|
|
8176
9167
|
}
|
|
8177
9168
|
}
|
|
@@ -8374,6 +9365,10 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
|
|
|
8374
9365
|
throw new UserError(`Agent '${agentName}' not found in config. Available agents: ${available || "(none)"}`);
|
|
8375
9366
|
}
|
|
8376
9367
|
const sessionResources = options.resources ?? agent.resources;
|
|
9368
|
+
const environmentVariables = options.environmentVariables ?? agent.environment_variables;
|
|
9369
|
+
if (environmentVariables && provider !== "qoder") {
|
|
9370
|
+
throw new UserError("Session environment variables are supported only by Qoder.");
|
|
9371
|
+
}
|
|
8377
9372
|
const providerFeatures = getProvider(provider)?.features;
|
|
8378
9373
|
for (const resource of sessionResources ?? []) {
|
|
8379
9374
|
if (!providerFeatures?.session_resources.includes(resource.type)) {
|
|
@@ -8402,7 +9397,8 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
|
|
|
8402
9397
|
identity_id: identityId,
|
|
8403
9398
|
files: (options.files ?? []).map((file) => ({ file_id: file.fileId, mount_path: file.mountPath })),
|
|
8404
9399
|
title: options.title,
|
|
8405
|
-
metadata: options.metadata
|
|
9400
|
+
metadata: options.metadata,
|
|
9401
|
+
environment_variables: environmentVariables
|
|
8406
9402
|
};
|
|
8407
9403
|
}
|
|
8408
9404
|
const agentId = requireRef(state, { type: "agent", name: agentName, provider });
|
|
@@ -8447,7 +9443,8 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
|
|
|
8447
9443
|
files: (options.files ?? []).map((f) => ({ file_id: f.fileId, mount_path: f.mountPath })),
|
|
8448
9444
|
resources: sessionResources,
|
|
8449
9445
|
title: options.title,
|
|
8450
|
-
metadata: options.metadata
|
|
9446
|
+
metadata: options.metadata,
|
|
9447
|
+
environment_variables: environmentVariables
|
|
8451
9448
|
};
|
|
8452
9449
|
}
|
|
8453
9450
|
function resolveTunnelId(agent, config, options, provider) {
|
|
@@ -8848,7 +9845,8 @@ async function createSessionForAgent(ctx, options = {}) {
|
|
|
8848
9845
|
files: options.files,
|
|
8849
9846
|
resources: options.resources,
|
|
8850
9847
|
title: options.title,
|
|
8851
|
-
metadata: options.metadata
|
|
9848
|
+
metadata: options.metadata,
|
|
9849
|
+
environmentVariables: options.environmentVariables
|
|
8852
9850
|
});
|
|
8853
9851
|
const session = await adapter2.createSession(bindings);
|
|
8854
9852
|
return { agentName, provider, session };
|
|
@@ -8867,7 +9865,8 @@ async function startSessionRun(ctx, prompt, options = {}) {
|
|
|
8867
9865
|
files: options.files,
|
|
8868
9866
|
resources: options.resources,
|
|
8869
9867
|
title: options.title,
|
|
8870
|
-
metadata: options.metadata
|
|
9868
|
+
metadata: options.metadata,
|
|
9869
|
+
environmentVariables: options.environmentVariables
|
|
8871
9870
|
});
|
|
8872
9871
|
const session = await adapter2.createSession(bindings);
|
|
8873
9872
|
return {
|
|
@@ -8928,7 +9927,7 @@ async function collectEventsUntilTerminal(adapter2, sessionId, options = {}) {
|
|
|
8928
9927
|
terminalStatus = terminalEvent.status;
|
|
8929
9928
|
break;
|
|
8930
9929
|
}
|
|
8931
|
-
await
|
|
9930
|
+
await delay2(currentIntervalMs);
|
|
8932
9931
|
currentIntervalMs = Math.min(currentIntervalMs * 2, maxPollIntervalMs);
|
|
8933
9932
|
}
|
|
8934
9933
|
} else {
|
|
@@ -8939,7 +9938,7 @@ async function collectEventsUntilTerminal(adapter2, sessionId, options = {}) {
|
|
|
8939
9938
|
terminalStatus = session.status;
|
|
8940
9939
|
break;
|
|
8941
9940
|
}
|
|
8942
|
-
await
|
|
9941
|
+
await delay2(currentIntervalMs);
|
|
8943
9942
|
currentIntervalMs = Math.min(currentIntervalMs * 2, maxPollIntervalMs);
|
|
8944
9943
|
}
|
|
8945
9944
|
result = await adapter2.listSessionEvents(sessionId, { limit: 100 });
|
|
@@ -9106,7 +10105,7 @@ async function* streamWithResume(adapter2, sessionId, message) {
|
|
|
9106
10105
|
}
|
|
9107
10106
|
}
|
|
9108
10107
|
if (!reachedTerminal) {
|
|
9109
|
-
await
|
|
10108
|
+
await delay2(reconnectIntervalMs);
|
|
9110
10109
|
reconnectIntervalMs = Math.min(reconnectIntervalMs * 2, DEFAULT_POLL_INTERVAL_MS);
|
|
9111
10110
|
}
|
|
9112
10111
|
}
|
|
@@ -9134,7 +10133,7 @@ function assertNotTimedOut(start, timeoutMs) {
|
|
|
9134
10133
|
throw new UserError(`Session did not complete within the timeout (${Math.floor(timeoutMs / 1e3)} seconds).`);
|
|
9135
10134
|
}
|
|
9136
10135
|
}
|
|
9137
|
-
function
|
|
10136
|
+
function delay2(ms) {
|
|
9138
10137
|
return new Promise((resolve13) => setTimeout(resolve13, ms));
|
|
9139
10138
|
}
|
|
9140
10139
|
|
|
@@ -9320,6 +10319,7 @@ var StateManager = class _StateManager {
|
|
|
9320
10319
|
address: r.address,
|
|
9321
10320
|
remote_id: r.remote_id,
|
|
9322
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,
|
|
9323
10323
|
version: r.version,
|
|
9324
10324
|
content_hash: r.content_hash ?? r.desired_hash ?? "",
|
|
9325
10325
|
desired_hash: r.desired_hash ?? r.content_hash ?? "",
|
|
@@ -9327,10 +10327,15 @@ var StateManager = class _StateManager {
|
|
|
9327
10327
|
desired_readiness_baseline: r.desired_readiness_baseline,
|
|
9328
10328
|
remote_hash: r.remote_hash,
|
|
9329
10329
|
remote_snapshot: r.remote_snapshot,
|
|
10330
|
+
replacement_fingerprint: r.replacement_fingerprint,
|
|
9330
10331
|
drift_paths: r.drift_paths,
|
|
9331
10332
|
drift_status: r.drift_status
|
|
9332
10333
|
}));
|
|
9333
|
-
|
|
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
|
+
);
|
|
9334
10339
|
} catch (err) {
|
|
9335
10340
|
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
9336
10341
|
return _StateManager.initialize(path);
|
|
@@ -9528,6 +10533,8 @@ var PlanReadinessImpactSchema = z6.enum(["none", "non_blocking", "blocking"]);
|
|
|
9528
10533
|
var PlannedActionSchema = z6.object({
|
|
9529
10534
|
action: ActionTypeSchema,
|
|
9530
10535
|
address: ResourceAddressSchema,
|
|
10536
|
+
/** Existing state address to retain when this action is an inferred logical rename. */
|
|
10537
|
+
previousAddress: ResourceAddressSchema.optional(),
|
|
9531
10538
|
reason: z6.string(),
|
|
9532
10539
|
driftKind: DriftKindSchema.optional(),
|
|
9533
10540
|
readinessImpact: PlanReadinessImpactSchema.optional(),
|