@openagentpack/sdk 0.3.1 → 0.4.0-beta-2472720-20260827
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/{chunk-GAAU67HX.js → chunk-6PDVAFLK.js} +13 -0
- package/dist/{file-URqwlxLf.d.ts → file-Cj5-Vx27.d.ts} +5 -0
- package/dist/file-lifecycle.d.ts +1 -1
- package/dist/index.d.ts +370 -48
- package/dist/index.js +2486 -632
- package/dist/{session-event-CxLg_XqS.d.ts → session-event-DHLkM9iF.d.ts} +132 -8
- package/dist/session-events.d.ts +1 -1
- package/dist/session-events.js +2 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
toSessionEvent
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6PDVAFLK.js";
|
|
4
4
|
import {
|
|
5
5
|
defaultFileUploadPurpose,
|
|
6
6
|
enrichProviderFileInfo
|
|
@@ -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();
|
|
@@ -610,6 +628,13 @@ async function listSessionEventsPaged(client, sessionId, options, toEvent, confi
|
|
|
610
628
|
const params = new URLSearchParams();
|
|
611
629
|
if (options?.limit) params.set("limit", String(options.limit));
|
|
612
630
|
if (options?.order) params.set("order", options.order);
|
|
631
|
+
if (config?.forwardTypes !== false) {
|
|
632
|
+
for (const type of options?.types ?? []) params.append("types", type);
|
|
633
|
+
}
|
|
634
|
+
if (options?.created_at_gt) params.set("created_at[gt]", options.created_at_gt);
|
|
635
|
+
if (options?.created_at_gte) params.set("created_at[gte]", options.created_at_gte);
|
|
636
|
+
if (options?.created_at_lt) params.set("created_at[lt]", options.created_at_lt);
|
|
637
|
+
if (options?.created_at_lte) params.set("created_at[lte]", options.created_at_lte);
|
|
613
638
|
const pageCursor = options?.page_token ?? options?.page;
|
|
614
639
|
if (pageCursor) params.set("page", pageCursor);
|
|
615
640
|
if (config?.forwardAfterId && options?.after_id) params.set("after_id", options.after_id);
|
|
@@ -776,7 +801,8 @@ function toCloudAgent(raw) {
|
|
|
776
801
|
workspace_id: raw.workspace_id,
|
|
777
802
|
created_at: raw.created_at,
|
|
778
803
|
updated_at: raw.updated_at,
|
|
779
|
-
archived_at: raw.archived_at ?? null
|
|
804
|
+
archived_at: raw.archived_at ?? null,
|
|
805
|
+
attributes: raw
|
|
780
806
|
};
|
|
781
807
|
}
|
|
782
808
|
function toCloudEnvironment(raw) {
|
|
@@ -792,7 +818,8 @@ function toCloudEnvironment(raw) {
|
|
|
792
818
|
workspace_id: raw.workspace_id,
|
|
793
819
|
created_at: raw.created_at,
|
|
794
820
|
updated_at: raw.updated_at,
|
|
795
|
-
archived_at: raw.archived_at ?? null
|
|
821
|
+
archived_at: raw.archived_at ?? null,
|
|
822
|
+
attributes: raw
|
|
796
823
|
};
|
|
797
824
|
}
|
|
798
825
|
function toCloudVault(raw) {
|
|
@@ -803,7 +830,8 @@ function toCloudVault(raw) {
|
|
|
803
830
|
type: raw.type,
|
|
804
831
|
created_at: raw.created_at ?? null,
|
|
805
832
|
updated_at: raw.updated_at ?? null,
|
|
806
|
-
archived_at: raw.archived_at ?? null
|
|
833
|
+
archived_at: raw.archived_at ?? null,
|
|
834
|
+
attributes: raw
|
|
807
835
|
};
|
|
808
836
|
}
|
|
809
837
|
function toRestFileInfo(res) {
|
|
@@ -815,7 +843,9 @@ function toRestFileInfo(res) {
|
|
|
815
843
|
created_at: res.created_at,
|
|
816
844
|
downloadable: res.downloadable,
|
|
817
845
|
status: res.status,
|
|
818
|
-
purpose: res.purpose
|
|
846
|
+
purpose: res.purpose,
|
|
847
|
+
scope: res.scope,
|
|
848
|
+
attributes: res
|
|
819
849
|
};
|
|
820
850
|
}
|
|
821
851
|
function toRestSkillInfo(res) {
|
|
@@ -828,7 +858,8 @@ function toRestSkillInfo(res) {
|
|
|
828
858
|
status: "active",
|
|
829
859
|
latest_version: res.latest_version,
|
|
830
860
|
created_at: res.created_at,
|
|
831
|
-
updated_at: res.updated_at
|
|
861
|
+
updated_at: res.updated_at,
|
|
862
|
+
attributes: res
|
|
832
863
|
};
|
|
833
864
|
}
|
|
834
865
|
|
|
@@ -1220,7 +1251,7 @@ function mapAgent(name, decl, refs, version, projectName) {
|
|
|
1220
1251
|
}
|
|
1221
1252
|
}
|
|
1222
1253
|
const body = {
|
|
1223
|
-
name,
|
|
1254
|
+
name: decl.name ?? name,
|
|
1224
1255
|
model: modelValue,
|
|
1225
1256
|
system: decl.instructions
|
|
1226
1257
|
};
|
|
@@ -1736,11 +1767,15 @@ var ClaudeAdapter = class _ClaudeAdapter {
|
|
|
1736
1767
|
async createDeployment(name, decl, refs, basePath) {
|
|
1737
1768
|
const uploaded = await this.uploadDeploymentFiles(decl, basePath);
|
|
1738
1769
|
const body = mapDeployment(name, decl, refs, this.projectName, uploaded);
|
|
1739
|
-
|
|
1740
|
-
|
|
1770
|
+
try {
|
|
1771
|
+
const res = await this.client.post("/deployments", body);
|
|
1772
|
+
return toRemoteResource(res);
|
|
1773
|
+
} catch (error) {
|
|
1774
|
+
preserveDeploymentFilesOnConflict(error, uploaded);
|
|
1775
|
+
}
|
|
1741
1776
|
}
|
|
1742
|
-
async updateDeployment(id, name, decl, refs, basePath) {
|
|
1743
|
-
const uploaded = await this.uploadDeploymentFiles(decl, basePath);
|
|
1777
|
+
async updateDeployment(id, name, decl, refs, basePath, preparedFiles) {
|
|
1778
|
+
const uploaded = preparedFiles ? new Map(preparedFiles) : await this.uploadDeploymentFiles(decl, basePath);
|
|
1744
1779
|
const current = await this.client.get(`/deployments/${id}`);
|
|
1745
1780
|
if (current.schedule && !decl.schedule) {
|
|
1746
1781
|
throw new UserError(
|
|
@@ -2013,12 +2048,23 @@ function normalizeToolNameForQoder(name) {
|
|
|
2013
2048
|
function normalizeToolNameFromQoder(name) {
|
|
2014
2049
|
return name.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
|
|
2015
2050
|
}
|
|
2051
|
+
function normalizeEnvironmentPackages(value) {
|
|
2052
|
+
if (!value || typeof value !== "object") return void 0;
|
|
2053
|
+
const raw = value;
|
|
2054
|
+
const packages = {};
|
|
2055
|
+
for (const key of ["apt", "npm", "pip"]) {
|
|
2056
|
+
if (Array.isArray(raw[key]) && raw[key].length > 0) packages[key] = raw[key];
|
|
2057
|
+
}
|
|
2058
|
+
return Object.keys(packages).length > 0 ? packages : void 0;
|
|
2059
|
+
}
|
|
2016
2060
|
function mapEnvironment2(name, decl, projectName) {
|
|
2017
2061
|
const envType = decl.config.type ?? "cloud";
|
|
2018
2062
|
const config = { type: envType };
|
|
2019
2063
|
if (decl.config.networking) config.networking = decl.config.networking;
|
|
2020
2064
|
else if (envType === "cloud") config.networking = { type: "unrestricted" };
|
|
2021
|
-
|
|
2065
|
+
const packages = normalizeEnvironmentPackages(decl.config.packages);
|
|
2066
|
+
if (packages) config.packages = packages;
|
|
2067
|
+
if (decl.config.setup_script !== void 0) config.setup_script = decl.config.setup_script;
|
|
2022
2068
|
return {
|
|
2023
2069
|
name,
|
|
2024
2070
|
description: decl.description ?? "",
|
|
@@ -2026,6 +2072,13 @@ function mapEnvironment2(name, decl, projectName) {
|
|
|
2026
2072
|
metadata: injectMetadata(decl.metadata, projectName, name)
|
|
2027
2073
|
};
|
|
2028
2074
|
}
|
|
2075
|
+
function mapForwardEnvironment(name, decl, projectName) {
|
|
2076
|
+
const body = mapEnvironment2(name, decl, projectName);
|
|
2077
|
+
const config = { ...body.config };
|
|
2078
|
+
delete config.networking;
|
|
2079
|
+
body.config = config;
|
|
2080
|
+
return body;
|
|
2081
|
+
}
|
|
2029
2082
|
function mapVault(name, decl, projectName) {
|
|
2030
2083
|
const body = { display_name: decl.display_name };
|
|
2031
2084
|
if (projectName) body.metadata = injectMetadata(decl.metadata, projectName, name);
|
|
@@ -2099,7 +2152,8 @@ function envToDecl2(raw) {
|
|
|
2099
2152
|
config: {
|
|
2100
2153
|
type: config.type ?? "cloud",
|
|
2101
2154
|
networking: config.networking,
|
|
2102
|
-
packages: config.packages
|
|
2155
|
+
packages: normalizeEnvironmentPackages(config.packages),
|
|
2156
|
+
setup_script: config.setup_script
|
|
2103
2157
|
},
|
|
2104
2158
|
metadata: stripAgentsMetadata(raw.metadata)
|
|
2105
2159
|
});
|
|
@@ -2142,6 +2196,7 @@ function agentToDecl2(raw) {
|
|
|
2142
2196
|
}));
|
|
2143
2197
|
}
|
|
2144
2198
|
return compactDeep({
|
|
2199
|
+
name: raw.name,
|
|
2145
2200
|
description: raw.description,
|
|
2146
2201
|
model: raw.model,
|
|
2147
2202
|
instructions: raw.system,
|
|
@@ -2266,7 +2321,7 @@ function mapAgent2(name, decl, refs, version, projectName) {
|
|
|
2266
2321
|
model = typeof qoderModel === "string" ? qoderModel : qoderModel.id;
|
|
2267
2322
|
}
|
|
2268
2323
|
const body = {
|
|
2269
|
-
name,
|
|
2324
|
+
name: decl.name ?? name,
|
|
2270
2325
|
model,
|
|
2271
2326
|
system: decl.instructions
|
|
2272
2327
|
};
|
|
@@ -2339,9 +2394,12 @@ function mapForwardTemplate(name, decl, refs, projectName) {
|
|
|
2339
2394
|
environment_id: refs.environment_id,
|
|
2340
2395
|
vault_ids: refs.vault_ids
|
|
2341
2396
|
};
|
|
2397
|
+
body.files = Object.fromEntries((refs.file_ids ?? []).map((id) => [id, { enabled: true }]));
|
|
2342
2398
|
if (refs.tunnel_id) body.tunnel_id = refs.tunnel_id;
|
|
2343
2399
|
if (projectName) body.metadata = injectMetadata(decl.metadata, projectName, name);
|
|
2344
2400
|
else body.metadata = decl.metadata ?? {};
|
|
2401
|
+
if (decl.environment_variables) body.environment_variables = decl.environment_variables;
|
|
2402
|
+
if (decl.managed_tool_config) body.managed_tool_config = decl.managed_tool_config;
|
|
2345
2403
|
if (decl.tools) {
|
|
2346
2404
|
body.tools = [
|
|
2347
2405
|
{
|
|
@@ -2482,6 +2540,9 @@ function mapSession2(bindings) {
|
|
|
2482
2540
|
if (bindings.tunnel_id) body.tunnel_id = bindings.tunnel_id;
|
|
2483
2541
|
if (bindings.title) body.title = bindings.title;
|
|
2484
2542
|
if (bindings.metadata) body.metadata = bindings.metadata;
|
|
2543
|
+
if (bindings.environment_variables) {
|
|
2544
|
+
body.environment_variables = Object.entries(bindings.environment_variables).map(([key, value]) => `${key}=${value}`).join(";");
|
|
2545
|
+
}
|
|
2485
2546
|
if (bindings.vault_ids.length) body.vault_ids = bindings.vault_ids;
|
|
2486
2547
|
const resources = [];
|
|
2487
2548
|
for (const id of bindings.memory_store_ids) resources.push({ type: "memory_store", memory_store_id: id });
|
|
@@ -2527,6 +2588,7 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2527
2588
|
client;
|
|
2528
2589
|
memoryApi;
|
|
2529
2590
|
forwardClient;
|
|
2591
|
+
forwardMemoryApi;
|
|
2530
2592
|
projectName;
|
|
2531
2593
|
forwardSessionIds = /* @__PURE__ */ new Set();
|
|
2532
2594
|
constructor(apiKey, gateway, projectName, forwardGateway) {
|
|
@@ -2548,6 +2610,19 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2548
2610
|
apiKey,
|
|
2549
2611
|
gateway: forwardGateway ?? deriveForwardGateway(gateway)
|
|
2550
2612
|
});
|
|
2613
|
+
this.forwardMemoryApi = new ProviderMemoryApi(this.forwardClient, {
|
|
2614
|
+
pathStyle: "relative",
|
|
2615
|
+
cursorParam: "after_id",
|
|
2616
|
+
updatePrecondition: "content_sha256",
|
|
2617
|
+
prefixParam: "prefix",
|
|
2618
|
+
versionsSegment: "versions",
|
|
2619
|
+
storeMetadataMode: "merge_patch",
|
|
2620
|
+
supportsView: false,
|
|
2621
|
+
supportsMemoryMetadata: true,
|
|
2622
|
+
supportsPathUpdate: false,
|
|
2623
|
+
supportsDeletePrecondition: false,
|
|
2624
|
+
supportsIncludeArchived: true
|
|
2625
|
+
});
|
|
2551
2626
|
this.projectName = projectName ?? "";
|
|
2552
2627
|
}
|
|
2553
2628
|
async validate() {
|
|
@@ -2562,7 +2637,7 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2562
2637
|
file: "/files",
|
|
2563
2638
|
deployment: "/deployments"
|
|
2564
2639
|
};
|
|
2565
|
-
async findResource(type, name, id) {
|
|
2640
|
+
async findResource(type, name, id, mode) {
|
|
2566
2641
|
if (type === "template") {
|
|
2567
2642
|
const raw2 = await locateRemote(this.forwardClient, "/templates", name, id, (item) => item.status !== "archived");
|
|
2568
2643
|
return raw2 ? toRemoteResource(raw2) : null;
|
|
@@ -2582,7 +2657,14 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2582
2657
|
const raw2 = await locateRemote(this.forwardClient, "/channels", name, id, () => true);
|
|
2583
2658
|
return raw2 ? toRemoteResource(raw2) : null;
|
|
2584
2659
|
}
|
|
2585
|
-
|
|
2660
|
+
if (type === "environment" && id && mode === "auto") {
|
|
2661
|
+
const managed = await locateRemote(this.client, "/environments", name, id, notArchived);
|
|
2662
|
+
if (managed) return toRemoteResource(managed);
|
|
2663
|
+
const forward = await locateRemote(this.forwardClient, "/environments", name, id, notArchived);
|
|
2664
|
+
return forward ? toRemoteResource(forward) : null;
|
|
2665
|
+
}
|
|
2666
|
+
const client = mode === "forward" && (type === "environment" || type === "skill" || type === "vault" || type === "memory_store" || type === "file") ? this.forwardClient : this.client;
|
|
2667
|
+
const raw = await locateRemote(client, _QoderAdapter.ENDPOINT_MAP[type], name, id, notArchived);
|
|
2586
2668
|
return raw ? toRemoteResource(raw) : null;
|
|
2587
2669
|
}
|
|
2588
2670
|
async listAgents(filter) {
|
|
@@ -2691,14 +2773,10 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2691
2773
|
}
|
|
2692
2774
|
normalizeRemote(type, raw) {
|
|
2693
2775
|
if (type === "environment") {
|
|
2694
|
-
const
|
|
2776
|
+
const normalized = envToDecl2(raw);
|
|
2695
2777
|
return compactDeep({
|
|
2696
2778
|
description: raw.description,
|
|
2697
|
-
config:
|
|
2698
|
-
type: config.type ?? "cloud",
|
|
2699
|
-
networking: config.networking,
|
|
2700
|
-
packages: config.packages
|
|
2701
|
-
},
|
|
2779
|
+
config: normalized.config,
|
|
2702
2780
|
metadata: stripAgentsMetadata(raw.metadata)
|
|
2703
2781
|
});
|
|
2704
2782
|
}
|
|
@@ -2730,18 +2808,24 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2730
2808
|
}
|
|
2731
2809
|
if (type === "channel") {
|
|
2732
2810
|
const channelConfig = raw.channel_config ?? {};
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2811
|
+
const mode = raw.identity_resolution?.mode ?? "fixed";
|
|
2812
|
+
const normalized = {
|
|
2813
|
+
identity_resolution: { mode },
|
|
2736
2814
|
channel_type: raw.channel_type,
|
|
2737
2815
|
name: raw.name,
|
|
2738
2816
|
enabled: raw.enabled,
|
|
2739
2817
|
channel_config: {
|
|
2740
2818
|
response_options: channelConfig.response_options ?? {}
|
|
2741
2819
|
}
|
|
2742
|
-
}
|
|
2820
|
+
};
|
|
2821
|
+
if (mode === "fixed") {
|
|
2822
|
+
normalized.identity_id = raw.identity_id;
|
|
2823
|
+
normalized.template_id = raw.template_id;
|
|
2824
|
+
}
|
|
2825
|
+
return compactDeep(normalized);
|
|
2743
2826
|
}
|
|
2744
2827
|
return compactDeep({
|
|
2828
|
+
name: raw.name,
|
|
2745
2829
|
description: raw.description,
|
|
2746
2830
|
model: normalizeModel(raw.model),
|
|
2747
2831
|
instructions: raw.system,
|
|
@@ -2750,17 +2834,41 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2750
2834
|
metadata: stripAgentsMetadata(raw.metadata)
|
|
2751
2835
|
});
|
|
2752
2836
|
}
|
|
2753
|
-
async createEnvironment(name, decl) {
|
|
2754
|
-
const body = mapEnvironment2(name, decl, this.projectName);
|
|
2755
|
-
const res = await this.client.post("/environments", body);
|
|
2837
|
+
async createEnvironment(name, decl, mode = "managed") {
|
|
2838
|
+
const body = mode === "forward" ? mapForwardEnvironment(name, decl, this.projectName) : mapEnvironment2(name, decl, this.projectName);
|
|
2839
|
+
const res = await (mode === "forward" ? this.forwardClient : this.client).post("/environments", body);
|
|
2756
2840
|
return toRemoteResource(res);
|
|
2757
2841
|
}
|
|
2758
|
-
async updateEnvironment(id, name, decl) {
|
|
2759
|
-
const body = mapEnvironment2(name, decl, this.projectName);
|
|
2760
|
-
const
|
|
2842
|
+
async updateEnvironment(id, name, decl, mode = "managed") {
|
|
2843
|
+
const body = mode === "forward" ? mapForwardEnvironment(name, decl, this.projectName) : mapEnvironment2(name, decl, this.projectName);
|
|
2844
|
+
const client = mode === "forward" ? this.forwardClient : this.client;
|
|
2845
|
+
const current = await client.get(`/environments/${id}`);
|
|
2846
|
+
const currentMetadata = current.metadata ?? {};
|
|
2847
|
+
const metadata = { ...body.metadata ?? {} };
|
|
2848
|
+
for (const key of Object.keys(currentMetadata)) {
|
|
2849
|
+
if (!key.startsWith("agents.") && !(key in metadata)) metadata[key] = null;
|
|
2850
|
+
}
|
|
2851
|
+
body.metadata = metadata;
|
|
2852
|
+
const res = await client.post(`/environments/${id}`, body);
|
|
2761
2853
|
return toRemoteResource(res);
|
|
2762
2854
|
}
|
|
2763
|
-
async deleteEnvironment(id, cascade = false) {
|
|
2855
|
+
async deleteEnvironment(id, cascade = false, mode = "managed") {
|
|
2856
|
+
if (mode === "forward") {
|
|
2857
|
+
try {
|
|
2858
|
+
await this.forwardClient.delete(`/environments/${id}`);
|
|
2859
|
+
return;
|
|
2860
|
+
} catch (err) {
|
|
2861
|
+
const isConflict = err instanceof ApiError && (err.statusCode === 409 || err.responseBody.includes("in use"));
|
|
2862
|
+
if (!isConflict) throw err;
|
|
2863
|
+
if (!cascade) {
|
|
2864
|
+
throw new UserError(
|
|
2865
|
+
`Environment ${id} is referenced by one or more Forward sessions. Use --cascade to archive the environment.`
|
|
2866
|
+
);
|
|
2867
|
+
}
|
|
2868
|
+
await this.forwardClient.post(`/environments/${id}/archive`, {});
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2764
2872
|
try {
|
|
2765
2873
|
await this.client.delete(`/environments/${id}`);
|
|
2766
2874
|
return;
|
|
@@ -2787,17 +2895,18 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2787
2895
|
await this.client.post(`/environments/${id}/archive`, {});
|
|
2788
2896
|
}
|
|
2789
2897
|
}
|
|
2790
|
-
async createVault(name, decl) {
|
|
2898
|
+
async createVault(name, decl, mode = "managed") {
|
|
2899
|
+
const client = mode === "forward" ? this.forwardClient : this.client;
|
|
2791
2900
|
const body = mapVault(name, decl, this.projectName);
|
|
2792
|
-
const res = await
|
|
2901
|
+
const res = await client.post("/vaults", body);
|
|
2793
2902
|
const vaultId = res.id;
|
|
2794
2903
|
for (const cred of decl.credentials ?? []) {
|
|
2795
|
-
await
|
|
2904
|
+
await client.post(`/vaults/${vaultId}/credentials`, mapCredential(cred));
|
|
2796
2905
|
}
|
|
2797
2906
|
return toRemoteResource(res);
|
|
2798
2907
|
}
|
|
2799
|
-
async deleteVault(id) {
|
|
2800
|
-
await this.client.delete(`/vaults/${id}`);
|
|
2908
|
+
async deleteVault(id, mode = "managed") {
|
|
2909
|
+
await (mode === "forward" ? this.forwardClient : this.client).delete(`/vaults/${id}`);
|
|
2801
2910
|
}
|
|
2802
2911
|
async exportResources(type) {
|
|
2803
2912
|
return exportRemoteResources(this.client, type, {
|
|
@@ -2808,17 +2917,24 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2808
2917
|
agentToDecl: agentToDecl2
|
|
2809
2918
|
});
|
|
2810
2919
|
}
|
|
2811
|
-
async createSkill(name, decl, files) {
|
|
2920
|
+
async createSkill(name, decl, files, mode = "managed") {
|
|
2812
2921
|
const formData = await buildSkillFormData(name, decl, files);
|
|
2813
|
-
const res = await this.client.postFormData(
|
|
2922
|
+
const res = await (mode === "forward" ? this.forwardClient : this.client).postFormData(
|
|
2923
|
+
"/skills",
|
|
2924
|
+
formData
|
|
2925
|
+
);
|
|
2814
2926
|
return toRemoteResource(res);
|
|
2815
2927
|
}
|
|
2816
|
-
async updateSkill(id, name, decl, files) {
|
|
2817
|
-
|
|
2818
|
-
|
|
2928
|
+
async updateSkill(id, name, decl, files, mode = "managed") {
|
|
2929
|
+
const client = mode === "forward" ? this.forwardClient : this.client;
|
|
2930
|
+
const packageName = skillNameFromFiles(files) ?? name;
|
|
2931
|
+
const formData = await buildSkillFormData(packageName, decl, files, "files", false, true);
|
|
2932
|
+
await client.postFormData(`/skills/${id}/versions`, formData);
|
|
2933
|
+
const current = await client.get(`/skills/${id}`);
|
|
2934
|
+
return toRemoteResource(current);
|
|
2819
2935
|
}
|
|
2820
|
-
async deleteSkill(id) {
|
|
2821
|
-
await this.client.delete(`/skills/${id}`);
|
|
2936
|
+
async deleteSkill(id, mode = "managed") {
|
|
2937
|
+
await (mode === "forward" ? this.forwardClient : this.client).delete(`/skills/${id}`);
|
|
2822
2938
|
}
|
|
2823
2939
|
async createAgent(name, decl, refs) {
|
|
2824
2940
|
const body = mapAgent2(name, decl, refs, void 0, this.projectName);
|
|
@@ -2835,21 +2951,89 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2835
2951
|
await this.client.delete(`/agents/${id}`);
|
|
2836
2952
|
}
|
|
2837
2953
|
async createTemplate(name, decl, refs) {
|
|
2838
|
-
await this.registerForwardVaults(refs.vault_ids);
|
|
2839
2954
|
const body = mapForwardTemplate(name, decl, refs, this.projectName);
|
|
2840
2955
|
const res = await this.forwardClient.post("/templates", body);
|
|
2956
|
+
await this.reconcileForwardMemoryMounts(res.id, refs);
|
|
2841
2957
|
return toRemoteResource(res);
|
|
2842
2958
|
}
|
|
2843
2959
|
async updateTemplate(id, name, decl, refs) {
|
|
2844
|
-
await this.registerForwardVaults(refs.vault_ids);
|
|
2845
2960
|
const body = mapForwardTemplate(name, decl, refs, this.projectName);
|
|
2846
2961
|
if (!refs.tunnel_id) body.tunnel_id = null;
|
|
2847
2962
|
const res = await this.forwardClient.post(`/templates/${id}`, body);
|
|
2963
|
+
await this.reconcileForwardMemoryMounts(id, refs);
|
|
2848
2964
|
return toRemoteResource(res);
|
|
2849
2965
|
}
|
|
2850
|
-
async archiveTemplate(id) {
|
|
2966
|
+
async archiveTemplate(id, ownedMemoryStoreIds = []) {
|
|
2967
|
+
const owned = new Set(ownedMemoryStoreIds);
|
|
2968
|
+
if (owned.size > 0) {
|
|
2969
|
+
for (const identity of await this.forwardClient.getAllPaged("/identities")) {
|
|
2970
|
+
if (typeof identity.id !== "string") continue;
|
|
2971
|
+
const path = `/identities/${identity.id}/templates/${id}/memory_stores`;
|
|
2972
|
+
try {
|
|
2973
|
+
const mounts = await this.forwardClient.get(path);
|
|
2974
|
+
for (const mount of mounts.data ?? []) {
|
|
2975
|
+
const storeId = mount.memory_store_id;
|
|
2976
|
+
if (mount.system_managed !== true && typeof storeId === "string" && owned.has(storeId)) {
|
|
2977
|
+
await this.forwardClient.delete(`${path}/${storeId}`);
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
} catch (error) {
|
|
2981
|
+
if (!ApiError.isNotFound(error)) throw error;
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2851
2985
|
await this.forwardClient.post(`/templates/${id}/archive`, {});
|
|
2852
2986
|
}
|
|
2987
|
+
async reconcileForwardMemoryMounts(templateId, refs) {
|
|
2988
|
+
if (refs.memory_store_ids === void 0) return;
|
|
2989
|
+
const desired = new Set(refs.memory_store_ids ?? []);
|
|
2990
|
+
if (!refs.identity_id) {
|
|
2991
|
+
if (desired.size > 0) throw new UserError("Qoder Forward Memory Store mounts require an Identity.");
|
|
2992
|
+
return;
|
|
2993
|
+
}
|
|
2994
|
+
const path = `/identities/${refs.identity_id}/templates/${templateId}/memory_stores`;
|
|
2995
|
+
const current = await this.forwardClient.get(path);
|
|
2996
|
+
const explicit = (current.data ?? []).filter((mount) => mount.system_managed !== true);
|
|
2997
|
+
const owned = new Set(refs.owned_memory_store_ids ?? refs.memory_store_ids ?? []);
|
|
2998
|
+
for (const mount of explicit) {
|
|
2999
|
+
const storeId = mount.memory_store_id;
|
|
3000
|
+
if (typeof storeId === "string" && owned.has(storeId) && !desired.has(storeId)) {
|
|
3001
|
+
await this.forwardClient.delete(`${path}/${storeId}`);
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
const mounted = new Set(
|
|
3005
|
+
explicit.map((mount) => mount.memory_store_id).filter((id) => typeof id === "string")
|
|
3006
|
+
);
|
|
3007
|
+
for (const memoryStoreId of desired) {
|
|
3008
|
+
if (!mounted.has(memoryStoreId)) await this.forwardClient.post(path, { memory_store_id: memoryStoreId });
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
async reconcileDefaultMemoryStore(identityId, templateId, desired) {
|
|
3012
|
+
const storeId = await this.findDefaultMemoryStoreId(identityId, templateId);
|
|
3013
|
+
if (!storeId) return { status: "pending" };
|
|
3014
|
+
const current = await this.forwardClient.get(`/memory_stores/${storeId}`);
|
|
3015
|
+
const descriptionChanged = desired.description !== void 0 && current.description !== desired.description;
|
|
3016
|
+
if (current.name === desired.name && !descriptionChanged) {
|
|
3017
|
+
return { status: "unchanged", memory_store_id: storeId };
|
|
3018
|
+
}
|
|
3019
|
+
await this.forwardClient.post(`/memory_stores/${storeId}`, {
|
|
3020
|
+
name: desired.name,
|
|
3021
|
+
...desired.description !== void 0 ? { description: desired.description } : {}
|
|
3022
|
+
});
|
|
3023
|
+
return { status: "updated", memory_store_id: storeId };
|
|
3024
|
+
}
|
|
3025
|
+
async findDefaultMemoryStoreId(identityId, templateId) {
|
|
3026
|
+
const mounts = await this.forwardClient.get(
|
|
3027
|
+
`/identities/${identityId}/templates/${templateId}/memory_stores`
|
|
3028
|
+
);
|
|
3029
|
+
const writableDefault = (mounts.data ?? []).find(
|
|
3030
|
+
(mount) => mount.system_managed === true && mount.access === "read_write"
|
|
3031
|
+
);
|
|
3032
|
+
return typeof writableDefault?.memory_store_id === "string" ? writableDefault.memory_store_id : null;
|
|
3033
|
+
}
|
|
3034
|
+
async deleteDefaultMemoryStore(id) {
|
|
3035
|
+
await this.client.delete(`/memory_stores/${id}`);
|
|
3036
|
+
}
|
|
2853
3037
|
async createIdentity(name, decl) {
|
|
2854
3038
|
if (decl.identity_id) return { id: decl.identity_id, type: "identity" };
|
|
2855
3039
|
const res = await this.forwardClient.post("/identities", {
|
|
@@ -2886,12 +3070,14 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2886
3070
|
}
|
|
2887
3071
|
async updateChannel(id, name, decl, refs) {
|
|
2888
3072
|
const current = await this.forwardClient.get(`/channels/${id}`);
|
|
2889
|
-
|
|
3073
|
+
const currentMode = current.identity_resolution?.mode ?? "fixed";
|
|
3074
|
+
if (current.channel_type !== decl.type || currentMode !== (decl.mode ?? "fixed")) {
|
|
2890
3075
|
await this.deleteChannel(id);
|
|
2891
3076
|
return this.createChannel(name, decl, refs);
|
|
2892
3077
|
}
|
|
2893
3078
|
const body = this.mapChannel(name, decl, refs);
|
|
2894
3079
|
delete body.channel_type;
|
|
3080
|
+
delete body.identity_resolution;
|
|
2895
3081
|
const res = await this.forwardClient.post(`/channels/${id}`, body);
|
|
2896
3082
|
return toRemoteResource(res);
|
|
2897
3083
|
}
|
|
@@ -2899,9 +3085,8 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2899
3085
|
await this.forwardClient.delete(`/channels/${id}`);
|
|
2900
3086
|
}
|
|
2901
3087
|
mapChannel(name, decl, refs) {
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
template_id: refs.agent_id,
|
|
3088
|
+
const mode = decl.mode ?? "fixed";
|
|
3089
|
+
const body = {
|
|
2905
3090
|
channel_type: decl.type,
|
|
2906
3091
|
name: decl.name ?? name,
|
|
2907
3092
|
enabled: decl.enabled ?? true,
|
|
@@ -2914,22 +3099,27 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2914
3099
|
}
|
|
2915
3100
|
}
|
|
2916
3101
|
};
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
resource: { id }
|
|
2923
|
-
});
|
|
3102
|
+
if (mode === "pairing") {
|
|
3103
|
+
body.identity_resolution = { mode: "pairing" };
|
|
3104
|
+
} else {
|
|
3105
|
+
body.identity_id = refs.identity_id;
|
|
3106
|
+
body.template_id = refs.agent_id;
|
|
2924
3107
|
}
|
|
3108
|
+
return body;
|
|
2925
3109
|
}
|
|
2926
|
-
async createMemoryStore(name, decl) {
|
|
3110
|
+
async createMemoryStore(name, decl, mode = "managed") {
|
|
2927
3111
|
const body = mapMemoryStore2(name, decl);
|
|
2928
|
-
const
|
|
3112
|
+
const client = mode === "forward" ? this.forwardClient : this.client;
|
|
3113
|
+
const memoryApi = mode === "forward" ? this.forwardMemoryApi : this.memoryApi;
|
|
3114
|
+
const res = await client.post(
|
|
3115
|
+
"/memory_stores",
|
|
3116
|
+
body,
|
|
3117
|
+
mode === "forward" ? { headers: { "Idempotency-Key": crypto.randomUUID() } } : void 0
|
|
3118
|
+
);
|
|
2929
3119
|
const storeId = res.id;
|
|
2930
3120
|
try {
|
|
2931
3121
|
for (const entry of decl.entries ?? []) {
|
|
2932
|
-
await
|
|
3122
|
+
await memoryApi.createMemory(storeId, { content: entry.content, path: entry.key });
|
|
2933
3123
|
}
|
|
2934
3124
|
} catch (error) {
|
|
2935
3125
|
await this.client.delete(`/memory_stores/${storeId}`).catch(() => void 0);
|
|
@@ -2937,7 +3127,7 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2937
3127
|
}
|
|
2938
3128
|
return toRemoteResource(res);
|
|
2939
3129
|
}
|
|
2940
|
-
async deleteMemoryStore(id) {
|
|
3130
|
+
async deleteMemoryStore(id, _mode = "managed") {
|
|
2941
3131
|
await this.client.delete(`/memory_stores/${id}`);
|
|
2942
3132
|
}
|
|
2943
3133
|
listMemoryStores(options) {
|
|
@@ -2946,23 +3136,23 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2946
3136
|
getMemoryStore(id) {
|
|
2947
3137
|
return this.memoryApi.getStore(id);
|
|
2948
3138
|
}
|
|
2949
|
-
updateMemoryStore(id, input) {
|
|
2950
|
-
return this.memoryApi.updateStore(id, input);
|
|
3139
|
+
updateMemoryStore(id, input, mode = "managed") {
|
|
3140
|
+
return (mode === "forward" ? this.forwardMemoryApi : this.memoryApi).updateStore(id, input);
|
|
2951
3141
|
}
|
|
2952
3142
|
archiveMemoryStore(id) {
|
|
2953
3143
|
return this.memoryApi.archiveStore(id);
|
|
2954
3144
|
}
|
|
2955
|
-
createMemory(storeId, input) {
|
|
2956
|
-
return this.memoryApi.createMemory(storeId, input);
|
|
3145
|
+
createMemory(storeId, input, mode = "managed") {
|
|
3146
|
+
return (mode === "forward" ? this.forwardMemoryApi : this.memoryApi).createMemory(storeId, input);
|
|
2957
3147
|
}
|
|
2958
|
-
listMemories(storeId, options) {
|
|
2959
|
-
return this.memoryApi.listMemories(storeId, options);
|
|
3148
|
+
listMemories(storeId, options, mode = "managed") {
|
|
3149
|
+
return (mode === "forward" ? this.forwardMemoryApi : this.memoryApi).listMemories(storeId, options);
|
|
2960
3150
|
}
|
|
2961
3151
|
getMemory(storeId, memoryId) {
|
|
2962
3152
|
return this.memoryApi.getMemory(storeId, memoryId);
|
|
2963
3153
|
}
|
|
2964
|
-
updateMemory(storeId, memoryId, input) {
|
|
2965
|
-
return this.memoryApi.updateMemory(storeId, memoryId, input);
|
|
3154
|
+
updateMemory(storeId, memoryId, input, mode = "managed") {
|
|
3155
|
+
return (mode === "forward" ? this.forwardMemoryApi : this.memoryApi).updateMemory(storeId, memoryId, input);
|
|
2966
3156
|
}
|
|
2967
3157
|
deleteMemory(storeId, memoryId, expected) {
|
|
2968
3158
|
return this.memoryApi.deleteMemory(storeId, memoryId, expected);
|
|
@@ -2979,11 +3169,15 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2979
3169
|
async createDeployment(name, decl, refs, basePath) {
|
|
2980
3170
|
const uploaded = await this.uploadDeploymentFiles(decl, basePath);
|
|
2981
3171
|
const body = mapDeployment2(name, decl, refs, this.projectName, uploaded);
|
|
2982
|
-
|
|
2983
|
-
|
|
3172
|
+
try {
|
|
3173
|
+
const res = await this.client.post("/deployments", body);
|
|
3174
|
+
return toRemoteResource(res);
|
|
3175
|
+
} catch (error) {
|
|
3176
|
+
preserveDeploymentFilesOnConflict(error, uploaded);
|
|
3177
|
+
}
|
|
2984
3178
|
}
|
|
2985
|
-
async updateDeployment(id, name, decl, refs, basePath) {
|
|
2986
|
-
const uploaded = await this.uploadDeploymentFiles(decl, basePath);
|
|
3179
|
+
async updateDeployment(id, name, decl, refs, basePath, preparedFiles) {
|
|
3180
|
+
const uploaded = preparedFiles ? new Map(preparedFiles) : await this.uploadDeploymentFiles(decl, basePath);
|
|
2987
3181
|
const current = await this.client.get(`/deployments/${id}`);
|
|
2988
3182
|
if (current.schedule && !decl.schedule) {
|
|
2989
3183
|
throw new UserError(
|
|
@@ -3091,6 +3285,9 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
3091
3285
|
};
|
|
3092
3286
|
if (bindings.title) body2.title = bindings.title;
|
|
3093
3287
|
if (bindings.metadata) body2.metadata = bindings.metadata;
|
|
3288
|
+
if (bindings.environment_variables) {
|
|
3289
|
+
body2.config = { environment_variables: bindings.environment_variables };
|
|
3290
|
+
}
|
|
3094
3291
|
if (bindings.files?.length) {
|
|
3095
3292
|
body2.resources = bindings.files.map((file) => ({
|
|
3096
3293
|
type: "file",
|
|
@@ -3244,30 +3441,42 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
3244
3441
|
return res.data;
|
|
3245
3442
|
}
|
|
3246
3443
|
// --- Files ---
|
|
3247
|
-
async uploadFile(filePath, options) {
|
|
3444
|
+
async uploadFile(filePath, options, mode = "managed") {
|
|
3445
|
+
if (mode === "forward" && options?.purpose && !["user_upload", "session_resource"].includes(options.purpose)) {
|
|
3446
|
+
throw new UserError(
|
|
3447
|
+
`Qoder Forward File purpose must be 'user_upload' or 'session_resource', got '${options.purpose}'.`
|
|
3448
|
+
);
|
|
3449
|
+
}
|
|
3248
3450
|
const resolved = resolve2(filePath);
|
|
3249
3451
|
const content = readFileSync2(resolved);
|
|
3250
3452
|
const fileName = options?.name ?? basename2(resolved);
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3453
|
+
const formData = buildFileFormData(new Uint8Array(content), fileName, { purpose: options?.purpose });
|
|
3454
|
+
const res = await (mode === "forward" ? this.forwardClient : this.client).postFormData(
|
|
3455
|
+
"/files",
|
|
3456
|
+
formData
|
|
3457
|
+
);
|
|
3458
|
+
return toRestFileInfo(res);
|
|
3254
3459
|
}
|
|
3255
3460
|
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);
|
|
3461
|
+
const formData = buildFileFormData(content, filename, options);
|
|
3264
3462
|
const res = await this.client.postFormData("/files", formData);
|
|
3265
3463
|
return toRestFileInfo(res);
|
|
3266
3464
|
}
|
|
3267
|
-
async deleteFile(id) {
|
|
3268
|
-
await this.client.delete(`/files/${id}`);
|
|
3465
|
+
async deleteFile(id, mode = "managed") {
|
|
3466
|
+
await (mode === "forward" ? this.forwardClient : this.client).delete(`/files/${id}`);
|
|
3269
3467
|
}
|
|
3270
3468
|
};
|
|
3469
|
+
function buildFileFormData(content, filename, options) {
|
|
3470
|
+
const formData = new FormData();
|
|
3471
|
+
const bytes = new Uint8Array(content);
|
|
3472
|
+
formData.append(
|
|
3473
|
+
"file",
|
|
3474
|
+
options?.mimeType ? new File([bytes], filename, { type: options.mimeType }) : new File([bytes], filename)
|
|
3475
|
+
);
|
|
3476
|
+
if (filename) formData.append("name", filename);
|
|
3477
|
+
if (options?.purpose) formData.append("purpose", options.purpose);
|
|
3478
|
+
return formData;
|
|
3479
|
+
}
|
|
3271
3480
|
function toSessionInfo2(res) {
|
|
3272
3481
|
return buildSessionInfo(res, (r) => r.memory_store_ids ?? []);
|
|
3273
3482
|
}
|
|
@@ -3317,22 +3526,24 @@ function normalizeQoderMcpServers(value) {
|
|
|
3317
3526
|
});
|
|
3318
3527
|
});
|
|
3319
3528
|
}
|
|
3320
|
-
async function buildSkillFormData(name, decl, files) {
|
|
3529
|
+
async function buildSkillFormData(name, decl, files, fileField = "file", includeCreateFields = true, prefixTopLevelDirectory = false) {
|
|
3321
3530
|
const zip = new JSZip2();
|
|
3322
3531
|
for (const f of files) {
|
|
3323
|
-
zip.file(f.relativePath, f.content);
|
|
3532
|
+
zip.file(prefixTopLevelDirectory ? `${name}/${f.relativePath}` : f.relativePath, f.content);
|
|
3324
3533
|
}
|
|
3325
3534
|
const zipContent = await zip.generateAsync({ type: "uint8array" });
|
|
3326
3535
|
const formData = new FormData();
|
|
3327
3536
|
formData.append(
|
|
3328
|
-
|
|
3537
|
+
fileField,
|
|
3329
3538
|
new File([new Uint8Array(zipContent)], `${name}.zip`, {
|
|
3330
3539
|
type: "application/zip"
|
|
3331
3540
|
})
|
|
3332
3541
|
);
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3542
|
+
if (includeCreateFields) {
|
|
3543
|
+
formData.append("name", name);
|
|
3544
|
+
formData.append("type", "custom");
|
|
3545
|
+
if (decl.description) formData.append("description", decl.description);
|
|
3546
|
+
}
|
|
3336
3547
|
return formData;
|
|
3337
3548
|
}
|
|
3338
3549
|
|
|
@@ -3447,7 +3658,7 @@ function mapVault2(name, decl, projectName) {
|
|
|
3447
3658
|
}
|
|
3448
3659
|
function mapCredential2(cred) {
|
|
3449
3660
|
if (cred.type === "environment_variable") {
|
|
3450
|
-
|
|
3661
|
+
const body2 = {
|
|
3451
3662
|
auth: {
|
|
3452
3663
|
type: "environment_variable",
|
|
3453
3664
|
secret_name: cred.secret_name,
|
|
@@ -3456,8 +3667,10 @@ function mapCredential2(cred) {
|
|
|
3456
3667
|
},
|
|
3457
3668
|
display_name: cred.name
|
|
3458
3669
|
};
|
|
3670
|
+
if (cred.metadata) body2.metadata = cred.metadata;
|
|
3671
|
+
return body2;
|
|
3459
3672
|
}
|
|
3460
|
-
|
|
3673
|
+
const body = {
|
|
3461
3674
|
auth: {
|
|
3462
3675
|
type: cred.type,
|
|
3463
3676
|
token: cred.access_token,
|
|
@@ -3465,6 +3678,8 @@ function mapCredential2(cred) {
|
|
|
3465
3678
|
},
|
|
3466
3679
|
display_name: cred.name
|
|
3467
3680
|
};
|
|
3681
|
+
if (cred.metadata) body.metadata = cred.metadata;
|
|
3682
|
+
return body;
|
|
3468
3683
|
}
|
|
3469
3684
|
function credToDecl3(raw, vaultName) {
|
|
3470
3685
|
const auth = raw.auth ?? {};
|
|
@@ -3474,6 +3689,7 @@ function credToDecl3(raw, vaultName) {
|
|
|
3474
3689
|
return {
|
|
3475
3690
|
name,
|
|
3476
3691
|
type: "static_bearer",
|
|
3692
|
+
metadata: stripAgentsMetadata(raw.metadata),
|
|
3477
3693
|
mcp_server_url: auth.mcp_server_url ?? "",
|
|
3478
3694
|
access_token: placeholder
|
|
3479
3695
|
};
|
|
@@ -3482,6 +3698,7 @@ function credToDecl3(raw, vaultName) {
|
|
|
3482
3698
|
return {
|
|
3483
3699
|
name,
|
|
3484
3700
|
type: "environment_variable",
|
|
3701
|
+
metadata: stripAgentsMetadata(raw.metadata),
|
|
3485
3702
|
secret_name: auth.secret_name ?? name,
|
|
3486
3703
|
secret_value: placeholder,
|
|
3487
3704
|
networking: networking ?? { type: "unrestricted" }
|
|
@@ -3549,6 +3766,7 @@ function agentToDecl3(raw) {
|
|
|
3549
3766
|
const model = raw.model;
|
|
3550
3767
|
const modelValue = typeof model === "object" && model ? model.id : model;
|
|
3551
3768
|
return compactDeep({
|
|
3769
|
+
name: raw.name,
|
|
3552
3770
|
description: raw.description,
|
|
3553
3771
|
model: modelValue,
|
|
3554
3772
|
instructions: raw.system,
|
|
@@ -3568,7 +3786,7 @@ function mapAgent3(name, decl, refs, version, projectName, skillVersions) {
|
|
|
3568
3786
|
modelId = typeof bailianModel === "string" ? bailianModel : bailianModel.id;
|
|
3569
3787
|
}
|
|
3570
3788
|
const body = {
|
|
3571
|
-
name,
|
|
3789
|
+
name: decl.name ?? name,
|
|
3572
3790
|
model: { id: modelId },
|
|
3573
3791
|
system: decl.instructions
|
|
3574
3792
|
};
|
|
@@ -3581,7 +3799,9 @@ function mapAgent3(name, decl, refs, version, projectName, skillVersions) {
|
|
|
3581
3799
|
}
|
|
3582
3800
|
const BAILIAN_BUILTINS = /* @__PURE__ */ new Set(["bash", "read", "write", "edit", "glob", "grep", "download_file"]);
|
|
3583
3801
|
if (decl.tools) {
|
|
3584
|
-
const toolConfigs = resolveBuiltinTools(decl.tools, {
|
|
3802
|
+
const toolConfigs = resolveBuiltinTools(decl.tools, {
|
|
3803
|
+
supportedWireNames: BAILIAN_BUILTINS
|
|
3804
|
+
}).map((tool) => ({
|
|
3585
3805
|
name: tool.wireName,
|
|
3586
3806
|
enabled: true
|
|
3587
3807
|
}));
|
|
@@ -3655,30 +3875,60 @@ function mapSession3(bindings) {
|
|
|
3655
3875
|
if (bindings.memory_store_ids.length) body.memory_store_ids = bindings.memory_store_ids;
|
|
3656
3876
|
return body;
|
|
3657
3877
|
}
|
|
3658
|
-
function
|
|
3878
|
+
function mapDeployment3(name, decl, refs, projectName, uploadedFiles) {
|
|
3879
|
+
const agent = { id: refs.agent_id };
|
|
3880
|
+
if (refs.agent_version !== void 0) agent.version = refs.agent_version;
|
|
3659
3881
|
const body = {
|
|
3660
|
-
|
|
3661
|
-
|
|
3882
|
+
name: decl.name ?? name,
|
|
3883
|
+
agent,
|
|
3884
|
+
environment_id: refs.environment_id,
|
|
3885
|
+
initial_events: mapMessageEvents(decl.initial_events)
|
|
3662
3886
|
};
|
|
3663
|
-
if (decl.description) body.
|
|
3664
|
-
if (
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3887
|
+
if (decl.description) body.description = decl.description;
|
|
3888
|
+
if (refs.vault_ids.length) body.vault_ids = refs.vault_ids;
|
|
3889
|
+
const resources = mapDeploymentResources3(decl, uploadedFiles);
|
|
3890
|
+
if (resources.length) body.resources = resources;
|
|
3891
|
+
if (decl.schedule) {
|
|
3892
|
+
body.schedule = {
|
|
3893
|
+
type: "cron",
|
|
3894
|
+
expression: decl.schedule.expression,
|
|
3895
|
+
timezone: decl.schedule.timezone
|
|
3896
|
+
};
|
|
3897
|
+
}
|
|
3898
|
+
if (projectName) {
|
|
3899
|
+
body.metadata = injectMetadata(decl.metadata, projectName, name);
|
|
3900
|
+
} else if (decl.metadata) {
|
|
3901
|
+
body.metadata = decl.metadata;
|
|
3672
3902
|
}
|
|
3673
3903
|
return body;
|
|
3674
3904
|
}
|
|
3675
|
-
function
|
|
3676
|
-
const
|
|
3905
|
+
function mapDeploymentUpdate3(name, decl, refs, projectName, uploadedFiles, existingMetadata) {
|
|
3906
|
+
const body = mapDeployment3(name, decl, refs, projectName, uploadedFiles);
|
|
3907
|
+
body.description = decl.description ?? "";
|
|
3908
|
+
body.vault_ids = refs.vault_ids;
|
|
3909
|
+
body.resources = mapDeploymentResources3(decl, uploadedFiles);
|
|
3910
|
+
if (!decl.schedule) body.schedule = null;
|
|
3911
|
+
if (!projectName && !decl.metadata && existingMetadata) body.metadata = existingMetadata;
|
|
3912
|
+
return body;
|
|
3913
|
+
}
|
|
3914
|
+
function mapDeploymentResources3(decl, uploadedFiles) {
|
|
3915
|
+
const resources = [];
|
|
3916
|
+
for (const resource of decl.resources ?? []) {
|
|
3917
|
+
if (resource.type !== "file") continue;
|
|
3918
|
+
const fileId = resource.file_id ?? (resource.source ? uploadedFiles?.get(resource.source) : void 0);
|
|
3919
|
+
if (!fileId) continue;
|
|
3920
|
+
const entry = { type: "file", file_id: fileId };
|
|
3921
|
+
if (resource.mount_path) entry.mount_path = resolveSandboxMountPath("bailian", resource.mount_path);
|
|
3922
|
+
resources.push(entry);
|
|
3923
|
+
}
|
|
3924
|
+
return resources;
|
|
3925
|
+
}
|
|
3926
|
+
function mapMessageEvents(events) {
|
|
3927
|
+
return events.filter((event) => event.type === "user.message" || event.type === "system.message").map((event) => ({
|
|
3677
3928
|
role: "user",
|
|
3678
3929
|
type: "message",
|
|
3679
|
-
content: [{ type: "text", text:
|
|
3930
|
+
content: [{ type: "text", text: event.content }]
|
|
3680
3931
|
}));
|
|
3681
|
-
return { input };
|
|
3682
3932
|
}
|
|
3683
3933
|
function mapSendMessage3(text) {
|
|
3684
3934
|
return {
|
|
@@ -3710,7 +3960,8 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
3710
3960
|
agent: "/agents",
|
|
3711
3961
|
skill: "/skills",
|
|
3712
3962
|
vault: "/vaults",
|
|
3713
|
-
file: "/files"
|
|
3963
|
+
file: "/files",
|
|
3964
|
+
deployment: "/deployments"
|
|
3714
3965
|
};
|
|
3715
3966
|
async findResource(type, name, id) {
|
|
3716
3967
|
const accept = type === "agent" ? (r) => !isArchivedAgent(r) : void 0;
|
|
@@ -3766,6 +4017,7 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
3766
4017
|
});
|
|
3767
4018
|
}
|
|
3768
4019
|
return compactDeep({
|
|
4020
|
+
name: raw.name,
|
|
3769
4021
|
description: raw.description,
|
|
3770
4022
|
model: normalizeBailianModel(raw.model),
|
|
3771
4023
|
instructions: raw.system,
|
|
@@ -3797,6 +4049,18 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
3797
4049
|
async deleteEnvironment(id) {
|
|
3798
4050
|
await this.client.delete(`/environments/${id}`);
|
|
3799
4051
|
}
|
|
4052
|
+
async listEnvironmentResources(options) {
|
|
4053
|
+
const params = cursorParams(options);
|
|
4054
|
+
if (options?.include_archived !== void 0) {
|
|
4055
|
+
params.set("include_archived", String(options.include_archived));
|
|
4056
|
+
}
|
|
4057
|
+
const res = await this.client.get(withQuery("/environments", params));
|
|
4058
|
+
return cursorPage(res, toCloudEnvironment);
|
|
4059
|
+
}
|
|
4060
|
+
async getRemoteEnvironment(id) {
|
|
4061
|
+
const res = await this.client.get(`/environments/${id}`);
|
|
4062
|
+
return toCloudEnvironment(res);
|
|
4063
|
+
}
|
|
3800
4064
|
// --- Agent ---
|
|
3801
4065
|
async createAgent(name, decl, refs) {
|
|
3802
4066
|
const skillVersions = await this.fetchSkillVersions(refs);
|
|
@@ -3845,6 +4109,25 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
3845
4109
|
async deleteAgent(id) {
|
|
3846
4110
|
await this.client.post(`/agents/${id}/archive`, {});
|
|
3847
4111
|
}
|
|
4112
|
+
async listAgentResources(options) {
|
|
4113
|
+
const params = cursorParams(options);
|
|
4114
|
+
if (options?.include_archived !== void 0) {
|
|
4115
|
+
params.set("include_archived", String(options.include_archived));
|
|
4116
|
+
}
|
|
4117
|
+
const res = await this.client.get(withQuery("/agents", params));
|
|
4118
|
+
return cursorPage(res, toCloudAgent);
|
|
4119
|
+
}
|
|
4120
|
+
async getRemoteAgent(id, version) {
|
|
4121
|
+
const params = new URLSearchParams();
|
|
4122
|
+
if (version !== void 0) params.set("version", String(version));
|
|
4123
|
+
const res = await this.client.get(withQuery(`/agents/${id}`, params));
|
|
4124
|
+
return toCloudAgent(res);
|
|
4125
|
+
}
|
|
4126
|
+
async listAgentVersions(id, options) {
|
|
4127
|
+
const params = cursorParams(options);
|
|
4128
|
+
const res = await this.client.get(withQuery(`/agents/${id}/versions`, params));
|
|
4129
|
+
return cursorPage(res, toCloudAgent);
|
|
4130
|
+
}
|
|
3848
4131
|
// --- Skill (2-step: Files API → Skills API) ---
|
|
3849
4132
|
async createSkill(name, decl, files) {
|
|
3850
4133
|
const fileId = await this.uploadSkillZip(name, decl, files);
|
|
@@ -3943,10 +4226,31 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
3943
4226
|
const all = await this.client.getAllPaged(path);
|
|
3944
4227
|
return all.map(toBailianSkillInfo);
|
|
3945
4228
|
}
|
|
4229
|
+
async listSkillResources(options) {
|
|
4230
|
+
const params = cursorParams(options);
|
|
4231
|
+
if (options?.source === "official") params.set("source", "official");
|
|
4232
|
+
if (options?.source === "custom") params.set("source", "customer");
|
|
4233
|
+
const res = await this.client.get(withQuery("/skills", params));
|
|
4234
|
+
return cursorPage(res, toBailianSkillInfo);
|
|
4235
|
+
}
|
|
3946
4236
|
async getSkillInfo(id) {
|
|
3947
4237
|
const res = await this.client.get(`/skills/${id}`);
|
|
3948
4238
|
return toBailianSkillInfo(res);
|
|
3949
4239
|
}
|
|
4240
|
+
async listSkillVersions(id, options) {
|
|
4241
|
+
const params = cursorParams(options);
|
|
4242
|
+
const res = await this.client.get(withQuery(`/skills/${id}/versions`, params));
|
|
4243
|
+
return cursorPage(res, toSkillVersionInfo);
|
|
4244
|
+
}
|
|
4245
|
+
async getSkillVersion(id, version) {
|
|
4246
|
+
const res = await this.client.get(`/skills/${id}/versions/${encodeURIComponent(version)}`);
|
|
4247
|
+
return toSkillVersionInfo(res);
|
|
4248
|
+
}
|
|
4249
|
+
async getSkillDownloadInfo(id, version) {
|
|
4250
|
+
return await this.client.get(
|
|
4251
|
+
`/skills/${id}/versions/${encodeURIComponent(version)}/content`
|
|
4252
|
+
);
|
|
4253
|
+
}
|
|
3950
4254
|
// Non-blocking create: file already uploaded → POST /skills with retry for residual OSS
|
|
3951
4255
|
// replication lag (postSkillWithRetry covers SKILL_FILE_NOT_FOUND for a few seconds), and return
|
|
3952
4256
|
// the initial status WITHOUT waiting for the security scan. Callers MUST ensure the file has
|
|
@@ -3976,7 +4280,7 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
3976
4280
|
}
|
|
3977
4281
|
async waitForFileReady(fileId) {
|
|
3978
4282
|
const start = Date.now();
|
|
3979
|
-
let
|
|
4283
|
+
let delay3 = FILE_SCAN_BACKOFF.initial;
|
|
3980
4284
|
let logged = false;
|
|
3981
4285
|
while (true) {
|
|
3982
4286
|
const detail = await this.client.get(`/files/${fileId}`);
|
|
@@ -3995,8 +4299,8 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
3995
4299
|
);
|
|
3996
4300
|
return;
|
|
3997
4301
|
}
|
|
3998
|
-
await new Promise((r) => setTimeout(r,
|
|
3999
|
-
|
|
4302
|
+
await new Promise((r) => setTimeout(r, delay3));
|
|
4303
|
+
delay3 = Math.min(delay3 * FILE_SCAN_BACKOFF.factor, FILE_SCAN_BACKOFF.max);
|
|
4000
4304
|
}
|
|
4001
4305
|
}
|
|
4002
4306
|
// --- Vault (Vaults API) ---
|
|
@@ -4018,6 +4322,18 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
4018
4322
|
const all = await this.client.getAllPaged("/vaults");
|
|
4019
4323
|
return all.map(toCloudVault);
|
|
4020
4324
|
}
|
|
4325
|
+
async listVaultResources(options) {
|
|
4326
|
+
const params = cursorParams(options);
|
|
4327
|
+
if (options?.include_archived !== void 0) {
|
|
4328
|
+
params.set("include_archived", String(options.include_archived));
|
|
4329
|
+
}
|
|
4330
|
+
const res = await this.client.get(withQuery("/vaults", params));
|
|
4331
|
+
return cursorPage(res, toCloudVault);
|
|
4332
|
+
}
|
|
4333
|
+
async getRemoteVault(id) {
|
|
4334
|
+
const res = await this.client.get(`/vaults/${id}`);
|
|
4335
|
+
return toCloudVault(res);
|
|
4336
|
+
}
|
|
4021
4337
|
async getVault(id) {
|
|
4022
4338
|
return await this.client.get(`/vaults/${id}`);
|
|
4023
4339
|
}
|
|
@@ -4039,7 +4355,21 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
4039
4355
|
}
|
|
4040
4356
|
async listCredentials(vaultId) {
|
|
4041
4357
|
const all = await this.client.getAllPaged(`/vaults/${vaultId}/credentials`);
|
|
4042
|
-
return all.map(
|
|
4358
|
+
return all.map((raw) => {
|
|
4359
|
+
const auth = raw.auth ?? {};
|
|
4360
|
+
const networking = auth.networking;
|
|
4361
|
+
const metadata = raw.metadata;
|
|
4362
|
+
return {
|
|
4363
|
+
id: raw.id,
|
|
4364
|
+
display_name: raw.display_name ?? raw.id,
|
|
4365
|
+
auth_type: auth.type ?? "",
|
|
4366
|
+
secret_name: auth.secret_name,
|
|
4367
|
+
networking_type: networking?.type,
|
|
4368
|
+
metadata: metadata ? Object.fromEntries(
|
|
4369
|
+
Object.entries(metadata).filter((entry) => typeof entry[1] === "string")
|
|
4370
|
+
) : void 0
|
|
4371
|
+
};
|
|
4372
|
+
});
|
|
4043
4373
|
}
|
|
4044
4374
|
async getCredential(vaultId, credentialId) {
|
|
4045
4375
|
return await this.client.get(`/vaults/${vaultId}/credentials/${credentialId}`);
|
|
@@ -4055,46 +4385,116 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
4055
4385
|
async deleteCredential(vaultId, credentialId) {
|
|
4056
4386
|
await this.client.delete(`/vaults/${vaultId}/credentials/${credentialId}`);
|
|
4057
4387
|
}
|
|
4058
|
-
// --- Deployment
|
|
4059
|
-
async createDeployment(
|
|
4060
|
-
|
|
4388
|
+
// --- Deployment ---
|
|
4389
|
+
async createDeployment(name, decl, refs, basePath) {
|
|
4390
|
+
const uploaded = await this.uploadDeploymentFiles(decl, basePath);
|
|
4391
|
+
const body = mapDeployment3(name, decl, refs, this.projectName, uploaded);
|
|
4392
|
+
try {
|
|
4393
|
+
const res = await this.client.post("/deployments", body);
|
|
4394
|
+
return toRemoteResource(res);
|
|
4395
|
+
} catch (error) {
|
|
4396
|
+
preserveDeploymentFilesOnConflict(error, uploaded);
|
|
4397
|
+
}
|
|
4061
4398
|
}
|
|
4062
|
-
async updateDeployment(
|
|
4063
|
-
|
|
4399
|
+
async updateDeployment(id, name, decl, refs, basePath, preparedFiles) {
|
|
4400
|
+
if (!id) return this.createDeployment(name, decl, refs, basePath);
|
|
4401
|
+
const current = await this.client.get(`/deployments/${id}`);
|
|
4402
|
+
const uploaded = preparedFiles ? new Map(preparedFiles) : await this.uploadDeploymentFiles(decl, basePath);
|
|
4403
|
+
const body = mapDeploymentUpdate3(
|
|
4404
|
+
name,
|
|
4405
|
+
decl,
|
|
4406
|
+
refs,
|
|
4407
|
+
this.projectName,
|
|
4408
|
+
uploaded,
|
|
4409
|
+
current.metadata
|
|
4410
|
+
);
|
|
4411
|
+
const res = await this.client.post(`/deployments/${id}`, body);
|
|
4412
|
+
return toRemoteResource(res);
|
|
4064
4413
|
}
|
|
4065
|
-
async deleteDeployment(
|
|
4414
|
+
async deleteDeployment(id) {
|
|
4415
|
+
await this.client.post(`/deployments/${id}/archive`, {});
|
|
4066
4416
|
}
|
|
4067
4417
|
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);
|
|
4418
|
+
if (!ctx.id) {
|
|
4419
|
+
throw new UserError(`Deployment '${ctx.name}' has no remote id; run \`agents apply\` first.`);
|
|
4085
4420
|
}
|
|
4086
|
-
|
|
4421
|
+
const res = await this.client.post(`/deployments/${ctx.id}/run`, {});
|
|
4422
|
+
return toDeploymentRunResult(res);
|
|
4087
4423
|
}
|
|
4088
4424
|
async getDeployment(ctx) {
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4425
|
+
if (!ctx.id) {
|
|
4426
|
+
throw new UserError(`Deployment '${ctx.name}' has no remote id; run \`agents apply\` first.`);
|
|
4427
|
+
}
|
|
4428
|
+
const res = await this.client.get(`/deployments/${ctx.id}`);
|
|
4429
|
+
return toDeploymentInfo3(res);
|
|
4430
|
+
}
|
|
4431
|
+
async listDeployments(filter) {
|
|
4432
|
+
const params = new URLSearchParams();
|
|
4433
|
+
if (filter?.agent_id) params.set("agent_id", filter.agent_id);
|
|
4434
|
+
if (filter?.keyword) params.set("keyword", filter.keyword);
|
|
4435
|
+
if (filter?.status) params.set("status", filter.status);
|
|
4436
|
+
if (filter?.include_archived) params.set("include_archived", "true");
|
|
4437
|
+
if (filter?.limit) params.set("limit", String(filter.limit));
|
|
4438
|
+
if (filter?.page) params.set("page", filter.page);
|
|
4439
|
+
if (filter?.created_at_gte) params.set("created_at[gte]", filter.created_at_gte);
|
|
4440
|
+
if (filter?.created_at_lte) params.set("created_at[lte]", filter.created_at_lte);
|
|
4441
|
+
const query2 = params.toString();
|
|
4442
|
+
const res = await this.client.get(`/deployments${query2 ? `?${query2}` : ""}`);
|
|
4443
|
+
const nextPage = res.next_page ?? void 0;
|
|
4444
|
+
return {
|
|
4445
|
+
deployments: (res.data ?? []).map(toDeploymentInfo3),
|
|
4446
|
+
has_more: nextPage != null,
|
|
4447
|
+
next_page: nextPage
|
|
4448
|
+
};
|
|
4449
|
+
}
|
|
4450
|
+
async getDeploymentById(id) {
|
|
4451
|
+
const res = await this.client.get(`/deployments/${id}`);
|
|
4452
|
+
return toDeploymentInfo3(res);
|
|
4453
|
+
}
|
|
4454
|
+
async runDeploymentById(id) {
|
|
4455
|
+
const res = await this.client.post(`/deployments/${id}/run`, {});
|
|
4456
|
+
return toDeploymentRunResult(res);
|
|
4457
|
+
}
|
|
4458
|
+
async pauseDeploymentById(id) {
|
|
4459
|
+
const res = await this.client.post(`/deployments/${id}/pause`, {});
|
|
4460
|
+
return toDeploymentInfo3(res);
|
|
4461
|
+
}
|
|
4462
|
+
async unpauseDeploymentById(id) {
|
|
4463
|
+
const res = await this.client.post(`/deployments/${id}/unpause`, {});
|
|
4464
|
+
return toDeploymentInfo3(res);
|
|
4465
|
+
}
|
|
4466
|
+
async listDeploymentRuns(deploymentId, options) {
|
|
4467
|
+
const res = await this.client.get(
|
|
4468
|
+
withQuery(`/deployments/${deploymentId}/runs`, cursorParams(options))
|
|
4469
|
+
);
|
|
4470
|
+
return cursorPage(res, toDeploymentRunInfo);
|
|
4471
|
+
}
|
|
4472
|
+
async getDeploymentRun(runId) {
|
|
4473
|
+
const res = await this.client.get(`/deployment_runs/${runId}`);
|
|
4474
|
+
return toDeploymentRunInfo(res);
|
|
4475
|
+
}
|
|
4476
|
+
async pauseDeployment(ctx) {
|
|
4477
|
+
return this.setDeploymentPaused(ctx, true);
|
|
4478
|
+
}
|
|
4479
|
+
async unpauseDeployment(ctx) {
|
|
4480
|
+
return this.setDeploymentPaused(ctx, false);
|
|
4481
|
+
}
|
|
4482
|
+
async setDeploymentPaused(ctx, paused) {
|
|
4483
|
+
if (!ctx.id) throw new UserError(`Deployment '${ctx.name}' has no remote id; run \`agents apply\` first.`);
|
|
4484
|
+
const action = paused ? "pause" : "unpause";
|
|
4485
|
+
const res = await this.client.post(`/deployments/${ctx.id}/${action}`, {});
|
|
4486
|
+
return toDeploymentInfo3(res);
|
|
4487
|
+
}
|
|
4488
|
+
async uploadDeploymentFiles(decl, basePath) {
|
|
4489
|
+
const uploaded = /* @__PURE__ */ new Map();
|
|
4490
|
+
for (const resource of decl.resources ?? []) {
|
|
4491
|
+
if (resource.type === "file" && !resource.file_id && resource.source && !uploaded.has(resource.source)) {
|
|
4492
|
+
uploaded.set(resource.source, await this.uploadDeploymentFile(resource.source, basePath));
|
|
4493
|
+
}
|
|
4494
|
+
}
|
|
4495
|
+
return uploaded;
|
|
4496
|
+
}
|
|
4497
|
+
async uploadDeploymentFile(source, basePath) {
|
|
4098
4498
|
const fullPath = resolve3(dirname3(basePath), source);
|
|
4099
4499
|
const content = readFileSync3(fullPath);
|
|
4100
4500
|
const formData = new FormData();
|
|
@@ -4127,6 +4527,9 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
4127
4527
|
async listSessions(filter) {
|
|
4128
4528
|
const params = new URLSearchParams();
|
|
4129
4529
|
if (filter?.agent_id) params.set("agent_id", filter.agent_id);
|
|
4530
|
+
for (const status of filter?.statuses ?? []) params.append("statuses[]", status);
|
|
4531
|
+
if (filter?.created_at_gte) params.set("created_at[gte]", filter.created_at_gte);
|
|
4532
|
+
if (filter?.created_at_lte) params.set("created_at[lte]", filter.created_at_lte);
|
|
4130
4533
|
if (filter?.limit) params.set("limit", String(filter.limit));
|
|
4131
4534
|
if (filter?.page) params.set("page", filter.page);
|
|
4132
4535
|
const qs = params.toString();
|
|
@@ -4143,6 +4546,15 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
4143
4546
|
const res = await this.client.get(`/sessions/${id}`);
|
|
4144
4547
|
return toSessionInfo3(res);
|
|
4145
4548
|
}
|
|
4549
|
+
async updateSession(id, input) {
|
|
4550
|
+
const res = await this.client.post(`/sessions/${id}`, input);
|
|
4551
|
+
if (!res.agent || !res.environment_id) return this.getSession(id);
|
|
4552
|
+
return toSessionInfo3(res);
|
|
4553
|
+
}
|
|
4554
|
+
async archiveSession(id) {
|
|
4555
|
+
const res = await this.client.post(`/sessions/${id}/archive`, {});
|
|
4556
|
+
return toSessionInfo3(res);
|
|
4557
|
+
}
|
|
4146
4558
|
async deleteSession(id) {
|
|
4147
4559
|
await this.client.delete(`/sessions/${id}`);
|
|
4148
4560
|
}
|
|
@@ -4151,13 +4563,20 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
4151
4563
|
const res = await this.client.post(`/sessions/${sessionId}/events`, body);
|
|
4152
4564
|
return extractCreatedEventId(res);
|
|
4153
4565
|
}
|
|
4566
|
+
async sendSessionEvents(sessionId, events) {
|
|
4567
|
+
const res = await this.client.post(`/sessions/${sessionId}/events`, { input: events });
|
|
4568
|
+
return {
|
|
4569
|
+
event_ids: extractCreatedEventIds(res),
|
|
4570
|
+
attributes: res
|
|
4571
|
+
};
|
|
4572
|
+
}
|
|
4154
4573
|
async *streamSessionEvents(sessionId, _options) {
|
|
4155
4574
|
for await (const raw of this.client.sse(`/sessions/${sessionId}/events/stream`)) {
|
|
4156
4575
|
yield toSessionEvent(raw);
|
|
4157
4576
|
}
|
|
4158
4577
|
}
|
|
4159
4578
|
async listSessionEvents(sessionId, options) {
|
|
4160
|
-
return listSessionEventsPaged(this.client, sessionId, options, toSessionEvent);
|
|
4579
|
+
return listSessionEventsPaged(this.client, sessionId, options, toSessionEvent, { forwardTypes: false });
|
|
4161
4580
|
}
|
|
4162
4581
|
// --- Files ---
|
|
4163
4582
|
async uploadFile(filePath, options) {
|
|
@@ -4183,6 +4602,15 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
4183
4602
|
const all = await this.client.getAllPaged("/files");
|
|
4184
4603
|
return all.map(toRestFileInfo);
|
|
4185
4604
|
}
|
|
4605
|
+
async listFileResources(options) {
|
|
4606
|
+
const params = cursorParams(options);
|
|
4607
|
+
if (options?.scope_id) params.set("scope_id", options.scope_id);
|
|
4608
|
+
const res = await this.client.get(withQuery("/files", params));
|
|
4609
|
+
return cursorPage(res, toRestFileInfo);
|
|
4610
|
+
}
|
|
4611
|
+
async downloadFileContent(id) {
|
|
4612
|
+
return new Uint8Array(await this.client.getBuffer(`/files/${id}/content`));
|
|
4613
|
+
}
|
|
4186
4614
|
async deleteFile(id) {
|
|
4187
4615
|
await this.client.delete(`/files/${id}`);
|
|
4188
4616
|
}
|
|
@@ -4190,6 +4618,19 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
4190
4618
|
function isArchivedAgent(raw) {
|
|
4191
4619
|
return typeof raw.archived_at === "string" && raw.archived_at.trim().length > 0;
|
|
4192
4620
|
}
|
|
4621
|
+
function toDeploymentInfo3(res) {
|
|
4622
|
+
const schedule = res.schedule;
|
|
4623
|
+
return {
|
|
4624
|
+
id: res.id ?? null,
|
|
4625
|
+
status: res.status ?? "unknown",
|
|
4626
|
+
paused_reason: res.paused_reason ?? void 0,
|
|
4627
|
+
schedule: schedule ? {
|
|
4628
|
+
expression: schedule.expression,
|
|
4629
|
+
timezone: schedule.timezone
|
|
4630
|
+
} : void 0,
|
|
4631
|
+
attributes: res
|
|
4632
|
+
};
|
|
4633
|
+
}
|
|
4193
4634
|
function toSessionInfo3(res) {
|
|
4194
4635
|
return buildSessionInfo(res, () => []);
|
|
4195
4636
|
}
|
|
@@ -4204,9 +4645,77 @@ function toBailianSkillInfo(res) {
|
|
|
4204
4645
|
status: skillStatusFromString(res.status),
|
|
4205
4646
|
latest_version: res.latest_version,
|
|
4206
4647
|
created_at: res.created_at,
|
|
4207
|
-
updated_at: res.updated_at
|
|
4648
|
+
updated_at: res.updated_at,
|
|
4649
|
+
attributes: res
|
|
4650
|
+
};
|
|
4651
|
+
}
|
|
4652
|
+
function cursorParams(options) {
|
|
4653
|
+
const params = new URLSearchParams();
|
|
4654
|
+
if (options?.limit !== void 0) params.set("limit", String(options.limit));
|
|
4655
|
+
if (options?.page) params.set("page", options.page);
|
|
4656
|
+
return params;
|
|
4657
|
+
}
|
|
4658
|
+
function withQuery(path, params) {
|
|
4659
|
+
const query2 = params.toString();
|
|
4660
|
+
return query2 ? `${path}?${query2}` : path;
|
|
4661
|
+
}
|
|
4662
|
+
function cursorPage(res, mapper) {
|
|
4663
|
+
const nextPage = res.next_page ?? void 0;
|
|
4664
|
+
return {
|
|
4665
|
+
data: (res.data ?? []).map(mapper),
|
|
4666
|
+
has_more: res.has_more ?? nextPage !== void 0,
|
|
4667
|
+
next_page: nextPage
|
|
4668
|
+
};
|
|
4669
|
+
}
|
|
4670
|
+
function toSkillVersionInfo(res) {
|
|
4671
|
+
return {
|
|
4672
|
+
id: res.id,
|
|
4673
|
+
skill_id: res.skill_id ?? "",
|
|
4674
|
+
type: res.type ?? "skill_version",
|
|
4675
|
+
name: res.name,
|
|
4676
|
+
description: res.description,
|
|
4677
|
+
version: res.version ?? "",
|
|
4678
|
+
status: res.status,
|
|
4679
|
+
created_at: res.created_at,
|
|
4680
|
+
updated_at: res.updated_at,
|
|
4681
|
+
additional_properties: res.additional_properties,
|
|
4682
|
+
attributes: res
|
|
4683
|
+
};
|
|
4684
|
+
}
|
|
4685
|
+
function toDeploymentRunResult(res) {
|
|
4686
|
+
return {
|
|
4687
|
+
run_id: res.id,
|
|
4688
|
+
session_id: res.session_id ?? null,
|
|
4689
|
+
error: res.error ?? void 0
|
|
4690
|
+
};
|
|
4691
|
+
}
|
|
4692
|
+
function toDeploymentRunInfo(res) {
|
|
4693
|
+
return {
|
|
4694
|
+
id: res.id ?? "",
|
|
4695
|
+
deployment_id: res.deployment_id,
|
|
4696
|
+
session_id: res.session_id ?? void 0,
|
|
4697
|
+
status: res.status,
|
|
4698
|
+
error: res.error ?? void 0,
|
|
4699
|
+
created_at: res.created_at,
|
|
4700
|
+
updated_at: res.updated_at,
|
|
4701
|
+
attributes: res
|
|
4208
4702
|
};
|
|
4209
4703
|
}
|
|
4704
|
+
function extractCreatedEventIds(res) {
|
|
4705
|
+
const ids = /* @__PURE__ */ new Set();
|
|
4706
|
+
const topLevel = extractCreatedEventId(res);
|
|
4707
|
+
if (topLevel) ids.add(topLevel);
|
|
4708
|
+
for (const key of ["data", "events"]) {
|
|
4709
|
+
const items = res[key];
|
|
4710
|
+
if (!Array.isArray(items)) continue;
|
|
4711
|
+
for (const item of items) {
|
|
4712
|
+
if (!item || typeof item !== "object") continue;
|
|
4713
|
+
const id = item.id;
|
|
4714
|
+
if (typeof id === "string") ids.add(id);
|
|
4715
|
+
}
|
|
4716
|
+
}
|
|
4717
|
+
return Array.from(ids);
|
|
4718
|
+
}
|
|
4210
4719
|
function normalizeBailianModel(value) {
|
|
4211
4720
|
if (value && typeof value === "object" && "id" in value) {
|
|
4212
4721
|
return value.id;
|
|
@@ -4286,9 +4795,8 @@ var BAILIAN_CAPABILITIES = {
|
|
|
4286
4795
|
remediation: "deploy agents independently and orchestrate via MCP"
|
|
4287
4796
|
},
|
|
4288
4797
|
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"
|
|
4798
|
+
tier: "native",
|
|
4799
|
+
reason: "deployments API with cron schedules, manual runs, pause/unpause and archive"
|
|
4292
4800
|
},
|
|
4293
4801
|
session: { tier: "native", reason: "sessions API" },
|
|
4294
4802
|
identity: { tier: "unsupported", reason: "no mapped Identity primitive on Bailian" },
|
|
@@ -4533,7 +5041,7 @@ function mapAgent4(name, decl, refs, version, projectName) {
|
|
|
4533
5041
|
if (!arkModel) throw new UserError(`No Ark model specified for agent '${name}'`);
|
|
4534
5042
|
const modelValue = typeof arkModel === "string" ? { id: arkModel } : { id: arkModel.id };
|
|
4535
5043
|
const body = {
|
|
4536
|
-
name,
|
|
5044
|
+
name: decl.name ?? name,
|
|
4537
5045
|
model: modelValue,
|
|
4538
5046
|
system: decl.instructions
|
|
4539
5047
|
};
|
|
@@ -4590,7 +5098,7 @@ function mapAgent4(name, decl, refs, version, projectName) {
|
|
|
4590
5098
|
}
|
|
4591
5099
|
return body;
|
|
4592
5100
|
}
|
|
4593
|
-
function
|
|
5101
|
+
function mapDeploymentToSession(decl, refs, fileIds) {
|
|
4594
5102
|
const body = {
|
|
4595
5103
|
agent: refs.agent_id,
|
|
4596
5104
|
environment_id: refs.environment_id
|
|
@@ -4953,7 +5461,7 @@ var ArkAdapter = class _ArkAdapter {
|
|
|
4953
5461
|
}
|
|
4954
5462
|
}
|
|
4955
5463
|
}
|
|
4956
|
-
const body =
|
|
5464
|
+
const body = mapDeploymentToSession(ctx.decl, ctx.refs, fileIds);
|
|
4957
5465
|
const sessionRes = await this.client.post("/sessions", body);
|
|
4958
5466
|
const sessionId = sessionRes.id;
|
|
4959
5467
|
const events = ctx.decl.initial_events.filter((e) => e.type === "user.message" || e.type === "system.message").map((e) => ({
|
|
@@ -4966,7 +5474,7 @@ var ArkAdapter = class _ArkAdapter {
|
|
|
4966
5474
|
return { session_id: sessionId };
|
|
4967
5475
|
}
|
|
4968
5476
|
async getDeployment(ctx) {
|
|
4969
|
-
const plan =
|
|
5477
|
+
const plan = mapDeploymentToSession(ctx.decl, ctx.refs, []);
|
|
4970
5478
|
return {
|
|
4971
5479
|
id: ctx.id,
|
|
4972
5480
|
status: "emulated (local)",
|
|
@@ -5239,6 +5747,9 @@ var packagesSchema = z5.object({
|
|
|
5239
5747
|
gem: z5.array(z5.string()).optional(),
|
|
5240
5748
|
go: z5.array(z5.string()).optional()
|
|
5241
5749
|
});
|
|
5750
|
+
var setupScriptSchema = z5.string().refine((value) => new TextEncoder().encode(value).byteLength <= 64 * 1024, {
|
|
5751
|
+
message: "setup_script must not exceed 65536 UTF-8 bytes"
|
|
5752
|
+
});
|
|
5242
5753
|
var environmentSchema = z5.object({
|
|
5243
5754
|
name: z5.string().optional(),
|
|
5244
5755
|
description: z5.string().optional(),
|
|
@@ -5248,7 +5759,8 @@ var environmentSchema = z5.object({
|
|
|
5248
5759
|
config: z5.object({
|
|
5249
5760
|
type: z5.enum(["cloud", "self_hosted"]),
|
|
5250
5761
|
networking: networkingSchema.optional(),
|
|
5251
|
-
packages: packagesSchema.optional()
|
|
5762
|
+
packages: packagesSchema.optional(),
|
|
5763
|
+
setup_script: setupScriptSchema.optional()
|
|
5252
5764
|
}),
|
|
5253
5765
|
metadata: z5.record(z5.string(), z5.string()).optional()
|
|
5254
5766
|
});
|
|
@@ -5263,6 +5775,7 @@ var coerceString = z5.union([z5.string(), z5.number()]).transform(String);
|
|
|
5263
5775
|
var staticBearerCredentialSchema = z5.object({
|
|
5264
5776
|
name: z5.string(),
|
|
5265
5777
|
type: z5.literal("static_bearer"),
|
|
5778
|
+
metadata: z5.record(z5.string(), z5.string()).optional(),
|
|
5266
5779
|
mcp_server_url: z5.string(),
|
|
5267
5780
|
access_token: coerceString,
|
|
5268
5781
|
protocol: z5.enum(["sse", "streamable_http"]).optional()
|
|
@@ -5270,6 +5783,7 @@ var staticBearerCredentialSchema = z5.object({
|
|
|
5270
5783
|
var environmentVariableCredentialSchema = z5.object({
|
|
5271
5784
|
name: z5.string(),
|
|
5272
5785
|
type: z5.literal("environment_variable"),
|
|
5786
|
+
metadata: z5.record(z5.string(), z5.string()).optional(),
|
|
5273
5787
|
secret_name: z5.string(),
|
|
5274
5788
|
secret_value: coerceString,
|
|
5275
5789
|
networking: z5.object({ type: z5.enum(["unrestricted", "limited"]) }).optional()
|
|
@@ -5412,6 +5926,9 @@ var agentSkillRefSchema = z5.object({
|
|
|
5412
5926
|
var agentDeliverySchema = z5.object({
|
|
5413
5927
|
type: z5.enum(["managed", "forward"])
|
|
5414
5928
|
});
|
|
5929
|
+
var managedToolConfigSchema = z5.object({
|
|
5930
|
+
enabled_tools: z5.array(z5.string().min(1))
|
|
5931
|
+
});
|
|
5415
5932
|
var sessionGithubRepoResourceSchema = z5.object({
|
|
5416
5933
|
type: z5.literal("github_repository"),
|
|
5417
5934
|
url: z5.string().url(),
|
|
@@ -5433,18 +5950,27 @@ var agentSchema = z5.object({
|
|
|
5433
5950
|
mcp_servers: z5.array(mcpServerSchema).optional(),
|
|
5434
5951
|
skills: z5.array(z5.union([z5.string(), agentSkillRefSchema])).optional(),
|
|
5435
5952
|
vault: z5.string().optional(),
|
|
5953
|
+
files: z5.array(z5.string()).optional(),
|
|
5436
5954
|
memory_stores: z5.array(z5.string()).optional(),
|
|
5955
|
+
default_memory_store: z5.object({
|
|
5956
|
+
name: z5.string().trim().min(1).max(255),
|
|
5957
|
+
description: z5.string().max(1024).optional(),
|
|
5958
|
+
delete_on_destroy: z5.boolean().optional().default(false)
|
|
5959
|
+
}).optional(),
|
|
5437
5960
|
resources: z5.array(sessionGithubRepoResourceSchema).optional(),
|
|
5438
5961
|
multiagent: multiagentSchema.optional(),
|
|
5439
5962
|
metadata: z5.record(z5.string(), z5.string()).optional(),
|
|
5963
|
+
environment_variables: z5.record(z5.string().min(1), z5.string()).optional(),
|
|
5964
|
+
managed_tool_config: managedToolConfigSchema.optional(),
|
|
5440
5965
|
delivery: z5.record(z5.string(), agentDeliverySchema).optional()
|
|
5441
5966
|
});
|
|
5442
5967
|
var channelSchema = z5.object({
|
|
5443
5968
|
provider: z5.string().optional(),
|
|
5444
|
-
agent: z5.string().min(1),
|
|
5969
|
+
agent: z5.string().min(1).optional(),
|
|
5445
5970
|
identity: z5.string().min(1).optional(),
|
|
5446
5971
|
type: z5.string().min(1),
|
|
5447
5972
|
name: z5.string().trim().min(1).optional(),
|
|
5973
|
+
mode: z5.enum(["fixed", "pairing"]).optional().default("fixed"),
|
|
5448
5974
|
enabled: z5.boolean().optional(),
|
|
5449
5975
|
credentials: z5.record(z5.string(), coerceString).optional(),
|
|
5450
5976
|
options: z5.record(z5.string(), z5.unknown()).optional()
|
|
@@ -5491,6 +6017,7 @@ var scheduleSchema = z5.object({
|
|
|
5491
6017
|
timezone: z5.string()
|
|
5492
6018
|
});
|
|
5493
6019
|
var deploymentSchema = z5.object({
|
|
6020
|
+
name: z5.string().optional(),
|
|
5494
6021
|
agent: z5.string(),
|
|
5495
6022
|
agent_version: z5.number().int().optional(),
|
|
5496
6023
|
environment: z5.string().optional(),
|
|
@@ -5683,14 +6210,29 @@ async function computeResourceHash(address, config, basePath, state) {
|
|
|
5683
6210
|
if (!decl) return "";
|
|
5684
6211
|
if (address.type === "skill") {
|
|
5685
6212
|
const skillDecl = decl;
|
|
6213
|
+
const apiMode = resolveQoderApiMode(address.type, address.name, address.provider, config);
|
|
5686
6214
|
if (basePath) {
|
|
5687
6215
|
const fileHash = computeSkillContentHash(skillDecl.source, basePath);
|
|
5688
|
-
return contentHash({ decl, fileHash });
|
|
6216
|
+
return contentHash({ decl, fileHash, apiMode });
|
|
5689
6217
|
}
|
|
6218
|
+
return contentHash({ decl, apiMode });
|
|
6219
|
+
}
|
|
6220
|
+
if (address.type === "environment" || address.type === "vault" || address.type === "memory_store") {
|
|
6221
|
+
return contentHash({ decl, apiMode: resolveQoderApiMode(address.type, address.name, address.provider, config) });
|
|
6222
|
+
}
|
|
6223
|
+
if (address.type === "file" && basePath) {
|
|
6224
|
+
const fileDecl = decl;
|
|
6225
|
+
const fileHash = computeLocalFileContentHash(fileDecl.source, basePath);
|
|
6226
|
+
return contentHash({
|
|
6227
|
+
decl,
|
|
6228
|
+
fileHash,
|
|
6229
|
+
apiMode: resolveQoderApiMode("file", address.name, address.provider, config)
|
|
6230
|
+
});
|
|
5690
6231
|
}
|
|
5691
6232
|
if (address.type === "deployment") {
|
|
5692
6233
|
const refs = resolveDeploymentReferenceIds(decl, config, address.provider, state);
|
|
5693
|
-
|
|
6234
|
+
const sourceHashes = basePath ? computeDeploymentSourceHashes(decl, basePath) : void 0;
|
|
6235
|
+
if (refs || sourceHashes) return contentHash({ decl, refs, sourceHashes });
|
|
5694
6236
|
}
|
|
5695
6237
|
if (address.type === "template") {
|
|
5696
6238
|
const refs = resolveTemplateReferenceIds(decl, config, address.provider, state);
|
|
@@ -5707,7 +6249,28 @@ async function computeResourceHash(address, config, basePath, state) {
|
|
|
5707
6249
|
}
|
|
5708
6250
|
return contentHash(decl);
|
|
5709
6251
|
}
|
|
6252
|
+
function resolveQoderApiMode(type, name, provider, config) {
|
|
6253
|
+
if (provider !== "qoder") return void 0;
|
|
6254
|
+
if (type === "environment" && config.environments?.[name]?.environment_id) return "auto";
|
|
6255
|
+
for (const agent of Object.values(config.agents ?? {})) {
|
|
6256
|
+
if (agent.provider && agent.provider !== provider) continue;
|
|
6257
|
+
const referenced = type === "environment" ? agent.environment === name : type === "skill" ? agent.skills?.some(
|
|
6258
|
+
(skill) => typeof skill === "string" ? skill === name : skill.type === "custom" && skill.skill_id === name
|
|
6259
|
+
) : type === "vault" ? agent.vault === name : type === "memory_store" ? agent.memory_stores?.includes(name) : agent.files?.includes(name);
|
|
6260
|
+
if (referenced && agent.delivery?.qoder?.type === "forward") return "forward";
|
|
6261
|
+
}
|
|
6262
|
+
return "managed";
|
|
6263
|
+
}
|
|
6264
|
+
function computeReplacementFingerprint(address, config) {
|
|
6265
|
+
if (address.type !== "channel") return void 0;
|
|
6266
|
+
const decl = config.channels?.[address.name];
|
|
6267
|
+
if (!decl) return void 0;
|
|
6268
|
+
return contentHash({ channel_type: decl.type, mode: decl.mode ?? "fixed", credentials: decl.credentials ?? {} });
|
|
6269
|
+
}
|
|
5710
6270
|
function resolveChannelReferenceIds(decl, config, provider, state) {
|
|
6271
|
+
if (decl.mode === "pairing" || !decl.agent) {
|
|
6272
|
+
return { mode: "pairing" };
|
|
6273
|
+
}
|
|
5711
6274
|
const agent = config.agents?.[decl.agent];
|
|
5712
6275
|
const agentType = agent?.delivery?.[provider]?.type === "forward" ? "template" : "agent";
|
|
5713
6276
|
const identity = decl.identity ?? config.defaults?.identity;
|
|
@@ -5730,7 +6293,14 @@ function resolveTemplateReferenceIds(decl, config, provider, state) {
|
|
|
5730
6293
|
environment_id: environment?.environment_id ?? (decl.environment ? state?.getResource({ type: "environment", name: decl.environment, provider })?.remote_id ?? void 0 : void 0),
|
|
5731
6294
|
tunnel_id: tunnel?.tunnel_id,
|
|
5732
6295
|
vault_ids: decl.vault ? [state?.getResource({ type: "vault", name: decl.vault, provider })?.remote_id ?? decl.vault] : [],
|
|
5733
|
-
skill_ids: skillIds
|
|
6296
|
+
skill_ids: skillIds,
|
|
6297
|
+
memory_store_ids: (decl.memory_stores ?? []).map(
|
|
6298
|
+
(memoryStore) => state?.getResource({ type: "memory_store", name: memoryStore, provider })?.remote_id ?? memoryStore
|
|
6299
|
+
),
|
|
6300
|
+
file_ids: (decl.files ?? []).map(
|
|
6301
|
+
(file) => state?.getResource({ type: "file", name: file, provider })?.remote_id ?? file
|
|
6302
|
+
),
|
|
6303
|
+
identity_id: decl.memory_stores?.length && config.defaults?.identity ? state?.getResource({ type: "identity", name: config.defaults.identity, provider })?.remote_id : void 0
|
|
5734
6304
|
};
|
|
5735
6305
|
}
|
|
5736
6306
|
function resolveDeploymentReferenceIds(decl, config, provider, state) {
|
|
@@ -5745,6 +6315,23 @@ function resolveDeploymentReferenceIds(decl, config, provider, state) {
|
|
|
5745
6315
|
function getDeclaration(address, config) {
|
|
5746
6316
|
return getResourceDeclaration(address, config);
|
|
5747
6317
|
}
|
|
6318
|
+
function computeDeploymentSourceHashes(decl, basePath) {
|
|
6319
|
+
const sources = [
|
|
6320
|
+
...new Set(
|
|
6321
|
+
(decl.resources ?? []).flatMap(
|
|
6322
|
+
(resource) => resource.type === "file" && !resource.file_id && resource.source ? [resource.source] : []
|
|
6323
|
+
)
|
|
6324
|
+
)
|
|
6325
|
+
];
|
|
6326
|
+
if (sources.length === 0) return void 0;
|
|
6327
|
+
return Object.fromEntries(sources.map((source) => [source, computeLocalFileContentHash(source, basePath)]));
|
|
6328
|
+
}
|
|
6329
|
+
function computeLocalFileContentHash(source, basePath) {
|
|
6330
|
+
const fullPath = resolve9(dirname8(basePath), source);
|
|
6331
|
+
const stat = statSync2(fullPath, { throwIfNoEntry: false });
|
|
6332
|
+
if (!stat?.isFile()) return "";
|
|
6333
|
+
return contentHash(readFileSync6(fullPath).toString("base64"));
|
|
6334
|
+
}
|
|
5748
6335
|
function computeSkillContentHash(source, basePath) {
|
|
5749
6336
|
const fullPath = resolve9(dirname8(basePath), source);
|
|
5750
6337
|
const stat = statSync2(fullPath, { throwIfNoEntry: false });
|
|
@@ -5893,11 +6480,21 @@ function resolveTemplateRefs(agentName, config, provider, state) {
|
|
|
5893
6480
|
const environment = config.environments?.[agent.environment];
|
|
5894
6481
|
if (!environment) throw new UserError(`Environment '${agent.environment}' is not defined in config.`);
|
|
5895
6482
|
const agentRefs = resolveAgentRefs(agentName, config, provider, state);
|
|
6483
|
+
const memoryStoreIds = (agent.memory_stores ?? []).map(
|
|
6484
|
+
(memoryStore) => requireRef(state, { type: "memory_store", name: memoryStore, provider })
|
|
6485
|
+
);
|
|
6486
|
+
const identityName = config.defaults?.identity;
|
|
5896
6487
|
return {
|
|
5897
6488
|
...agentRefs,
|
|
5898
6489
|
environment_id: environment.environment_id ?? requireRef(state, { type: "environment", name: agent.environment, provider }),
|
|
5899
6490
|
...agent.tunnel ? { tunnel_id: resolveTunnelIdFromConfig(config, agent.tunnel, provider) } : {},
|
|
5900
|
-
vault_ids: agent.vault ? [requireRef(state, { type: "vault", name: agent.vault, provider })] : []
|
|
6491
|
+
vault_ids: agent.vault ? [requireRef(state, { type: "vault", name: agent.vault, provider })] : [],
|
|
6492
|
+
file_ids: (agent.files ?? []).map((file) => requireRef(state, { type: "file", name: file, provider })),
|
|
6493
|
+
memory_store_ids: memoryStoreIds,
|
|
6494
|
+
owned_memory_store_ids: state.listResources().filter(
|
|
6495
|
+
(resource) => resource.address.provider === provider && resource.address.type === "memory_store" && resource.api_mode === "forward" && typeof resource.remote_id === "string"
|
|
6496
|
+
).map((resource) => resource.remote_id),
|
|
6497
|
+
...identityName ? { identity_id: requireRef(state, { type: "identity", name: identityName, provider }) } : {}
|
|
5901
6498
|
};
|
|
5902
6499
|
}
|
|
5903
6500
|
function resolveDeploymentRefs(deploymentName, config, provider, state) {
|
|
@@ -5959,6 +6556,12 @@ function resolveDeploymentRefs(deploymentName, config, provider, state) {
|
|
|
5959
6556
|
function resolveChannelRefs(channelName, config, provider, state) {
|
|
5960
6557
|
const channel = config.channels?.[channelName];
|
|
5961
6558
|
if (!channel) throw new UserError(`Channel '${channelName}' not found in config`);
|
|
6559
|
+
if (channel.mode === "pairing") {
|
|
6560
|
+
return {};
|
|
6561
|
+
}
|
|
6562
|
+
if (!channel.agent) {
|
|
6563
|
+
throw new UserError(`Channel '${channelName}' is fixed mode and must declare agent`);
|
|
6564
|
+
}
|
|
5962
6565
|
const agent = config.agents?.[channel.agent];
|
|
5963
6566
|
if (!agent) throw new UserError(`Channel '${channelName}' references unknown agent '${channel.agent}'`);
|
|
5964
6567
|
const agentType = agent.delivery?.[provider]?.type === "forward" ? "template" : "agent";
|
|
@@ -5982,8 +6585,61 @@ function resolveTunnelIdFromConfig(config, tunnelName, provider) {
|
|
|
5982
6585
|
}
|
|
5983
6586
|
|
|
5984
6587
|
// src/internal/executor/skill-resolver.ts
|
|
6588
|
+
import { dirname as dirname9 } from "path";
|
|
6589
|
+
|
|
6590
|
+
// src/internal/core/skill-source.ts
|
|
5985
6591
|
import { readFileSync as readFileSync7, statSync as statSync3 } from "fs";
|
|
5986
|
-
import {
|
|
6592
|
+
import { basename as basename6, resolve as resolve10 } from "path";
|
|
6593
|
+
import { parse } from "yaml";
|
|
6594
|
+
async function inspectSkillSource(source, options = {}) {
|
|
6595
|
+
if (/^https?:\/\//i.test(source)) {
|
|
6596
|
+
throw new UserError("Skill source inspection only accepts a local directory, zip, or SKILL.md file.");
|
|
6597
|
+
}
|
|
6598
|
+
const sourcePath = resolve10(options.basePath ?? process.cwd(), source);
|
|
6599
|
+
const sourceStat = statSync3(sourcePath, { throwIfNoEntry: false });
|
|
6600
|
+
if (!sourceStat) throw new UserError(`Skill source not found: ${source}`);
|
|
6601
|
+
let files;
|
|
6602
|
+
if (sourceStat.isDirectory()) {
|
|
6603
|
+
files = collectFiles(sourcePath, "");
|
|
6604
|
+
} else if (sourceStat.isFile() && sourcePath.toLowerCase().endsWith(".zip")) {
|
|
6605
|
+
files = await extractSkillZipFiles(readFileSync7(sourcePath));
|
|
6606
|
+
} else if (sourceStat.isFile() && basename6(sourcePath).toLowerCase() === "skill.md") {
|
|
6607
|
+
files = [{ relativePath: "SKILL.md", content: readFileSync7(sourcePath) }];
|
|
6608
|
+
} else {
|
|
6609
|
+
throw new UserError("Skill source must be a directory, .zip archive, or SKILL.md file.");
|
|
6610
|
+
}
|
|
6611
|
+
const normalizedFiles = normalizeSkillRoot(files);
|
|
6612
|
+
const manifest = normalizedFiles.find((file) => file.relativePath === "SKILL.md");
|
|
6613
|
+
const name = parseSkillManifestName(manifest.content);
|
|
6614
|
+
return { name, sourcePath, files: normalizedFiles };
|
|
6615
|
+
}
|
|
6616
|
+
function normalizeSkillRoot(files) {
|
|
6617
|
+
if (files.some((file) => file.relativePath === "SKILL.md")) return files;
|
|
6618
|
+
const manifests = files.filter((file) => file.relativePath.endsWith("/SKILL.md"));
|
|
6619
|
+
if (manifests.length === 0) throw new UserError("Skill source does not contain SKILL.md.");
|
|
6620
|
+
if (manifests.length > 1) {
|
|
6621
|
+
throw new UserError("Skill source contains multiple SKILL.md files and has no unambiguous root.");
|
|
6622
|
+
}
|
|
6623
|
+
const prefix = manifests[0].relativePath.slice(0, -"SKILL.md".length);
|
|
6624
|
+
return files.filter((file) => file.relativePath.startsWith(prefix)).map((file) => ({ ...file, relativePath: file.relativePath.slice(prefix.length) }));
|
|
6625
|
+
}
|
|
6626
|
+
function parseSkillManifestName(content) {
|
|
6627
|
+
const text = content.toString("utf8");
|
|
6628
|
+
const frontmatter = text.match(/^---\s*\r?\n([\s\S]*?)\r?\n---(?:\s*\r?\n|$)/);
|
|
6629
|
+
if (!frontmatter) throw new UserError("SKILL.md must start with YAML frontmatter containing a name.");
|
|
6630
|
+
let manifest;
|
|
6631
|
+
try {
|
|
6632
|
+
manifest = parse(frontmatter[1]);
|
|
6633
|
+
} catch (error) {
|
|
6634
|
+
throw new UserError(`Invalid SKILL.md frontmatter: ${error instanceof Error ? error.message : String(error)}`);
|
|
6635
|
+
}
|
|
6636
|
+
const rawName = manifest && typeof manifest === "object" ? manifest.name : void 0;
|
|
6637
|
+
const name = typeof rawName === "string" ? rawName.trim() : "";
|
|
6638
|
+
if (!name) throw new UserError("SKILL.md frontmatter must contain a non-empty name.");
|
|
6639
|
+
return name;
|
|
6640
|
+
}
|
|
6641
|
+
|
|
6642
|
+
// src/internal/executor/skill-resolver.ts
|
|
5987
6643
|
async function resolveSkillFiles(decl, ctx) {
|
|
5988
6644
|
if (/^https?:\/\//i.test(decl.source)) {
|
|
5989
6645
|
const res = await resolveFetch()(decl.source);
|
|
@@ -5991,19 +6647,7 @@ async function resolveSkillFiles(decl, ctx) {
|
|
|
5991
6647
|
return extractSkillZipFiles(Buffer.from(await res.arrayBuffer()));
|
|
5992
6648
|
}
|
|
5993
6649
|
if (!ctx.configPath) return [];
|
|
5994
|
-
|
|
5995
|
-
const stat = statSync3(sourcePath, { throwIfNoEntry: false });
|
|
5996
|
-
if (stat?.isDirectory()) {
|
|
5997
|
-
return collectFiles(sourcePath, "");
|
|
5998
|
-
}
|
|
5999
|
-
if (stat?.isFile()) {
|
|
6000
|
-
if (sourcePath.endsWith(".zip")) {
|
|
6001
|
-
return extractSkillZipFiles(readFileSync7(sourcePath));
|
|
6002
|
-
}
|
|
6003
|
-
const content = readFileSync7(sourcePath);
|
|
6004
|
-
return [{ relativePath: "SKILL.md", content }];
|
|
6005
|
-
}
|
|
6006
|
-
return [];
|
|
6650
|
+
return (await inspectSkillSource(decl.source, { basePath: dirname9(ctx.configPath) })).files;
|
|
6007
6651
|
}
|
|
6008
6652
|
|
|
6009
6653
|
// src/internal/executor/executor.ts
|
|
@@ -6081,6 +6725,7 @@ async function executePlan(plan, ctx, options = {}) {
|
|
|
6081
6725
|
await runWithConcurrency(level, concurrency, runAction);
|
|
6082
6726
|
await ctx.state.save();
|
|
6083
6727
|
}
|
|
6728
|
+
await reconcileDefaultMemoryStores(ctx, new Set(plan.actions.map((action) => action.address.provider)));
|
|
6084
6729
|
for (const action of deletions) {
|
|
6085
6730
|
await runAction(action);
|
|
6086
6731
|
await ctx.state.save();
|
|
@@ -6091,6 +6736,41 @@ async function executePlan(plan, ctx, options = {}) {
|
|
|
6091
6736
|
partial: results.some((r) => r.status === "failed")
|
|
6092
6737
|
};
|
|
6093
6738
|
}
|
|
6739
|
+
async function reconcileDefaultMemoryStores(ctx, plannedProviders) {
|
|
6740
|
+
const identityName = ctx.config.defaults?.identity;
|
|
6741
|
+
for (const [agentName, agent] of Object.entries(ctx.config.agents ?? {})) {
|
|
6742
|
+
const desired = agent.default_memory_store;
|
|
6743
|
+
if (!desired || agent.delivery?.qoder?.type !== "forward") continue;
|
|
6744
|
+
const providerName = "qoder";
|
|
6745
|
+
if (agent.provider && agent.provider !== providerName || !plannedProviders.has(providerName)) continue;
|
|
6746
|
+
const provider = ctx.providers.get(providerName);
|
|
6747
|
+
if (!provider?.reconcileDefaultMemoryStore || !identityName) continue;
|
|
6748
|
+
const identityId = ctx.state.getResource({
|
|
6749
|
+
type: "identity",
|
|
6750
|
+
name: identityName,
|
|
6751
|
+
provider: providerName
|
|
6752
|
+
})?.remote_id;
|
|
6753
|
+
const templateId = ctx.state.getResource({ type: "template", name: agentName, provider: providerName })?.remote_id;
|
|
6754
|
+
if (!identityId || !templateId) continue;
|
|
6755
|
+
const result = await provider.reconcileDefaultMemoryStore(identityId, templateId, desired);
|
|
6756
|
+
const resource = { type: "template", name: agentName, provider: providerName };
|
|
6757
|
+
if (result.status === "pending") {
|
|
6758
|
+
emitRuntimeFeedback(ctx.onFeedback, {
|
|
6759
|
+
type: "provider_wait",
|
|
6760
|
+
level: "warning",
|
|
6761
|
+
resource,
|
|
6762
|
+
message: `default memory store for template.${agentName} is pending \u2014 create the first Forward Session, then run apply again`
|
|
6763
|
+
});
|
|
6764
|
+
} else if (result.status === "updated") {
|
|
6765
|
+
emitRuntimeFeedback(ctx.onFeedback, {
|
|
6766
|
+
type: "resource_action_success",
|
|
6767
|
+
level: "success",
|
|
6768
|
+
resource,
|
|
6769
|
+
message: `updated default memory store for template.${agentName} to "${desired.name}"`
|
|
6770
|
+
});
|
|
6771
|
+
}
|
|
6772
|
+
}
|
|
6773
|
+
}
|
|
6094
6774
|
async function runWithConcurrency(items, limit, worker) {
|
|
6095
6775
|
if (items.length === 0) return;
|
|
6096
6776
|
let cursor = 0;
|
|
@@ -6157,8 +6837,8 @@ async function executeAction(action, provider, ctx) {
|
|
|
6157
6837
|
resource: action.address,
|
|
6158
6838
|
message: `update ${action.address.type}.${action.address.name} (${action.address.provider}) \u2014 not found remotely, recreating`
|
|
6159
6839
|
});
|
|
6160
|
-
ctx.state.removeResource(action.address);
|
|
6161
|
-
return executeActionInner({ ...action, action: "create" }, provider, ctx);
|
|
6840
|
+
ctx.state.removeResource(action.previousAddress ?? action.address);
|
|
6841
|
+
return executeActionInner({ ...action, action: "create", previousAddress: void 0 }, provider, ctx);
|
|
6162
6842
|
}
|
|
6163
6843
|
}
|
|
6164
6844
|
async function executeActionInner(action, provider, ctx) {
|
|
@@ -6169,6 +6849,7 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6169
6849
|
const existing = ctx.state.getResource(address);
|
|
6170
6850
|
if (!existing) return false;
|
|
6171
6851
|
const id = existing.remote_id;
|
|
6852
|
+
const apiMode2 = existing.api_mode;
|
|
6172
6853
|
if (type === "environment" || type === "identity") {
|
|
6173
6854
|
const externalReference = type === "environment" ? ctx.config.environments?.[name]?.environment_id : ctx.config.identities?.[name]?.identity_id;
|
|
6174
6855
|
if (existing.externally_managed || externalReference) {
|
|
@@ -6180,13 +6861,13 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6180
6861
|
try {
|
|
6181
6862
|
switch (type) {
|
|
6182
6863
|
case "environment":
|
|
6183
|
-
await provider.deleteEnvironment(id);
|
|
6864
|
+
await provider.deleteEnvironment(id, false, apiMode2);
|
|
6184
6865
|
break;
|
|
6185
6866
|
case "vault":
|
|
6186
|
-
await provider.deleteVault(id);
|
|
6867
|
+
await provider.deleteVault(id, apiMode2);
|
|
6187
6868
|
break;
|
|
6188
6869
|
case "skill":
|
|
6189
|
-
await provider.deleteSkill(id);
|
|
6870
|
+
await provider.deleteSkill(id, apiMode2);
|
|
6190
6871
|
break;
|
|
6191
6872
|
case "agent":
|
|
6192
6873
|
await provider.deleteAgent(id);
|
|
@@ -6194,17 +6875,17 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6194
6875
|
case "template":
|
|
6195
6876
|
if (!provider.archiveTemplate)
|
|
6196
6877
|
throw new UserError(`Provider '${address.provider}' does not support templates`);
|
|
6197
|
-
await provider.archiveTemplate(id);
|
|
6878
|
+
await provider.archiveTemplate(id, ownedForwardMemoryStoreIds(ctx, address.provider));
|
|
6198
6879
|
break;
|
|
6199
6880
|
case "memory_store":
|
|
6200
6881
|
if (!provider.deleteMemoryStore) throw memoryStoreUnsupported(address.provider);
|
|
6201
|
-
await provider.deleteMemoryStore(id);
|
|
6882
|
+
await provider.deleteMemoryStore(id, apiMode2);
|
|
6202
6883
|
break;
|
|
6203
6884
|
case "deployment":
|
|
6204
6885
|
await provider.deleteDeployment(id);
|
|
6205
6886
|
break;
|
|
6206
6887
|
case "file":
|
|
6207
|
-
await provider.deleteFile(id);
|
|
6888
|
+
await provider.deleteFile(id, apiMode2);
|
|
6208
6889
|
break;
|
|
6209
6890
|
case "identity":
|
|
6210
6891
|
if (!provider.deleteIdentity)
|
|
@@ -6231,7 +6912,11 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6231
6912
|
return false;
|
|
6232
6913
|
}
|
|
6233
6914
|
const isUpdate = action.action === "update";
|
|
6234
|
-
const
|
|
6915
|
+
const priorAddress = action.previousAddress ?? address;
|
|
6916
|
+
const existingId = isUpdate ? ctx.state.getResource(priorAddress)?.remote_id : void 0;
|
|
6917
|
+
const apiMode = resolveResourceApiMode(type, name, address.provider, ctx.config);
|
|
6918
|
+
const priorApiMode = ctx.state.getResource(priorAddress)?.api_mode ?? (address.provider === "qoder" ? "managed" : void 0);
|
|
6919
|
+
const apiModeChanged = isUpdate && apiMode !== void 0 && priorApiMode !== apiMode;
|
|
6235
6920
|
let result;
|
|
6236
6921
|
switch (type) {
|
|
6237
6922
|
case "environment": {
|
|
@@ -6246,6 +6931,17 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6246
6931
|
resource: action.address,
|
|
6247
6932
|
message: `${action.action} ${action.address.type}.${action.address.name} (${action.address.provider}) \u2014 external reference, no remote mutation`
|
|
6248
6933
|
});
|
|
6934
|
+
} else if (apiModeChanged) {
|
|
6935
|
+
try {
|
|
6936
|
+
result = await provider.createEnvironment(remoteName, decl, apiMode);
|
|
6937
|
+
} catch (err) {
|
|
6938
|
+
result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
|
|
6939
|
+
mode: apiMode,
|
|
6940
|
+
onExisting: (existing) => provider.updateEnvironment(existing.id, remoteName, decl, apiMode)
|
|
6941
|
+
});
|
|
6942
|
+
adopted = true;
|
|
6943
|
+
}
|
|
6944
|
+
if (existingId) await provider.deleteEnvironment(existingId, false, priorApiMode);
|
|
6249
6945
|
} else if (isUpdate) {
|
|
6250
6946
|
const prior = ctx.state.getResource(address);
|
|
6251
6947
|
if (prior?.externally_managed) {
|
|
@@ -6253,13 +6949,14 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6253
6949
|
`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
6950
|
);
|
|
6255
6951
|
}
|
|
6256
|
-
result = await provider.updateEnvironment(existingId, remoteName, decl);
|
|
6952
|
+
result = await provider.updateEnvironment(existingId, remoteName, decl, apiMode);
|
|
6257
6953
|
} else {
|
|
6258
6954
|
try {
|
|
6259
|
-
result = await provider.createEnvironment(remoteName, decl);
|
|
6955
|
+
result = await provider.createEnvironment(remoteName, decl, apiMode);
|
|
6260
6956
|
} catch (err) {
|
|
6261
6957
|
result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
|
|
6262
|
-
|
|
6958
|
+
mode: apiMode,
|
|
6959
|
+
onExisting: (existing) => provider.updateEnvironment(existing.id, remoteName, decl, apiMode)
|
|
6263
6960
|
});
|
|
6264
6961
|
adopted = true;
|
|
6265
6962
|
}
|
|
@@ -6268,22 +6965,26 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6268
6965
|
}
|
|
6269
6966
|
case "vault": {
|
|
6270
6967
|
const decl = ctx.config.vaults[name];
|
|
6271
|
-
if (
|
|
6968
|
+
if (apiModeChanged) {
|
|
6969
|
+
result = await provider.createVault(name, decl, apiMode);
|
|
6970
|
+
if (existingId) await provider.deleteVault(existingId, priorApiMode).catch(() => void 0);
|
|
6971
|
+
} else if (isUpdate) {
|
|
6272
6972
|
try {
|
|
6273
|
-
result = await provider.createVault(name, decl);
|
|
6274
|
-
await provider.deleteVault(existingId);
|
|
6973
|
+
result = await provider.createVault(name, decl, apiMode);
|
|
6974
|
+
await provider.deleteVault(existingId, apiMode);
|
|
6275
6975
|
} catch {
|
|
6276
|
-
await provider.deleteVault(existingId);
|
|
6277
|
-
result = await provider.createVault(name, decl);
|
|
6976
|
+
await provider.deleteVault(existingId, apiMode);
|
|
6977
|
+
result = await provider.createVault(name, decl, apiMode);
|
|
6278
6978
|
}
|
|
6279
6979
|
} else {
|
|
6280
6980
|
try {
|
|
6281
|
-
result = await provider.createVault(name, decl);
|
|
6981
|
+
result = await provider.createVault(name, decl, apiMode);
|
|
6282
6982
|
} catch (err) {
|
|
6283
6983
|
result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
|
|
6984
|
+
mode: apiMode,
|
|
6284
6985
|
onExisting: async (existing) => {
|
|
6285
|
-
await provider.deleteVault(existing.id);
|
|
6286
|
-
return provider.createVault(name, decl);
|
|
6986
|
+
await provider.deleteVault(existing.id, apiMode);
|
|
6987
|
+
return provider.createVault(name, decl, apiMode);
|
|
6287
6988
|
}
|
|
6288
6989
|
});
|
|
6289
6990
|
adopted = true;
|
|
@@ -6307,12 +7008,15 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6307
7008
|
}
|
|
6308
7009
|
const remoteName = decl.name ?? name;
|
|
6309
7010
|
const files = await resolveSkillFiles(decl, ctx);
|
|
6310
|
-
if (
|
|
6311
|
-
result = await provider.
|
|
7011
|
+
if (apiModeChanged) {
|
|
7012
|
+
result = await provider.createSkill(remoteName, decl, files, apiMode);
|
|
7013
|
+
if (existingId) await provider.deleteSkill(existingId, priorApiMode).catch(() => void 0);
|
|
7014
|
+
} else if (isUpdate) {
|
|
7015
|
+
result = await provider.updateSkill(existingId, remoteName, decl, files, apiMode);
|
|
6312
7016
|
} else {
|
|
6313
7017
|
const manifestName = skillNameFromFiles(files);
|
|
6314
7018
|
const searchNames = manifestName && manifestName !== remoteName ? [remoteName, manifestName] : [remoteName];
|
|
6315
|
-
const existing = await findExistingByNames(provider, "skill", searchNames);
|
|
7019
|
+
const existing = await findExistingByNames(provider, "skill", searchNames, apiMode);
|
|
6316
7020
|
if (existing) {
|
|
6317
7021
|
result = existing.resource;
|
|
6318
7022
|
emitRuntimeFeedback(ctx.onFeedback, {
|
|
@@ -6324,9 +7028,10 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6324
7028
|
adopted = true;
|
|
6325
7029
|
} else {
|
|
6326
7030
|
try {
|
|
6327
|
-
result = await provider.createSkill(remoteName, decl, files);
|
|
7031
|
+
result = await provider.createSkill(remoteName, decl, files, apiMode);
|
|
6328
7032
|
} catch (err) {
|
|
6329
7033
|
result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
|
|
7034
|
+
mode: apiMode,
|
|
6330
7035
|
searchNames,
|
|
6331
7036
|
onExisting: async (existing2) => existing2
|
|
6332
7037
|
});
|
|
@@ -6345,15 +7050,19 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6345
7050
|
throw memoryStoreUnsupported(address.provider);
|
|
6346
7051
|
}
|
|
6347
7052
|
const reconcile = async (storeId) => {
|
|
6348
|
-
const store = await provider.updateMemoryStore(
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
7053
|
+
const store = await provider.updateMemoryStore(
|
|
7054
|
+
storeId,
|
|
7055
|
+
{
|
|
7056
|
+
name,
|
|
7057
|
+
description: decl.description,
|
|
7058
|
+
metadata: decl.metadata ?? {}
|
|
7059
|
+
},
|
|
7060
|
+
apiMode
|
|
7061
|
+
);
|
|
6353
7062
|
const current = /* @__PURE__ */ new Map();
|
|
6354
7063
|
let cursor;
|
|
6355
7064
|
do {
|
|
6356
|
-
const page2 = await provider.listMemories(storeId, { limit: 100, cursor, view: "basic" });
|
|
7065
|
+
const page2 = await provider.listMemories(storeId, { limit: 100, cursor, view: "basic" }, apiMode);
|
|
6357
7066
|
for (const memory of page2.data) {
|
|
6358
7067
|
if (memory.type === "memory") current.set(memory.path, memory);
|
|
6359
7068
|
}
|
|
@@ -6363,24 +7072,33 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6363
7072
|
const existing = current.get(entry.key.replace(/^\/+/, ""));
|
|
6364
7073
|
if (existing) {
|
|
6365
7074
|
if (existing.content_sha256 !== sha256(entry.content)) {
|
|
6366
|
-
await provider.updateMemory(
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
7075
|
+
await provider.updateMemory(
|
|
7076
|
+
storeId,
|
|
7077
|
+
existing.id,
|
|
7078
|
+
{
|
|
7079
|
+
content: entry.content,
|
|
7080
|
+
expected_content_sha256: existing.content_sha256
|
|
7081
|
+
},
|
|
7082
|
+
apiMode
|
|
7083
|
+
);
|
|
6370
7084
|
}
|
|
6371
7085
|
} else {
|
|
6372
|
-
await provider.createMemory(storeId, { path: entry.key, content: entry.content });
|
|
7086
|
+
await provider.createMemory(storeId, { path: entry.key, content: entry.content }, apiMode);
|
|
6373
7087
|
}
|
|
6374
7088
|
}
|
|
6375
7089
|
return store;
|
|
6376
7090
|
};
|
|
6377
|
-
if (
|
|
7091
|
+
if (apiModeChanged) {
|
|
7092
|
+
result = await createMemoryStore2(name, decl, apiMode);
|
|
7093
|
+
if (existingId) await deleteMemoryStore2(existingId, priorApiMode).catch(() => void 0);
|
|
7094
|
+
} else if (isUpdate) {
|
|
6378
7095
|
result = await reconcile(existingId);
|
|
6379
7096
|
} else {
|
|
6380
7097
|
try {
|
|
6381
|
-
result = await createMemoryStore2(name, decl);
|
|
7098
|
+
result = await createMemoryStore2(name, decl, apiMode);
|
|
6382
7099
|
} catch (err) {
|
|
6383
7100
|
result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
|
|
7101
|
+
mode: apiMode,
|
|
6384
7102
|
onExisting: async (existing) => reconcile(existing.id)
|
|
6385
7103
|
});
|
|
6386
7104
|
adopted = true;
|
|
@@ -6489,16 +7207,36 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6489
7207
|
case "deployment": {
|
|
6490
7208
|
const decl = ctx.config.deployments[name];
|
|
6491
7209
|
const refs = resolveDeploymentRefs(name, ctx.config, address.provider, ctx.state);
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
7210
|
+
const hasLocalFileSources = decl.resources?.some(
|
|
7211
|
+
(resource) => resource.type === "file" && !resource.file_id && Boolean(resource.source)
|
|
7212
|
+
);
|
|
7213
|
+
const materializeDeployment = async () => {
|
|
6495
7214
|
try {
|
|
6496
|
-
|
|
7215
|
+
return await provider.createDeployment(name, decl, refs, ctx.configPath ?? "");
|
|
6497
7216
|
} catch (err) {
|
|
6498
|
-
|
|
6499
|
-
|
|
7217
|
+
const preparedFiles = err instanceof DeploymentCreateConflictError ? err.preparedFiles : void 0;
|
|
7218
|
+
const existing = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
|
|
7219
|
+
onExisting: (existing2) => provider.updateDeployment(existing2.id, name, decl, refs, ctx.configPath ?? "", preparedFiles)
|
|
7220
|
+
});
|
|
7221
|
+
adopted = true;
|
|
7222
|
+
return existing;
|
|
7223
|
+
}
|
|
7224
|
+
};
|
|
7225
|
+
if (isUpdate && existingId) {
|
|
7226
|
+
result = await provider.updateDeployment(existingId, name, decl, refs, ctx.configPath ?? "");
|
|
7227
|
+
} else {
|
|
7228
|
+
const existing = hasLocalFileSources ? await findExistingByNames(provider, "deployment", [name]) : null;
|
|
7229
|
+
if (existing) {
|
|
7230
|
+
result = await provider.updateDeployment(existing.resource.id, name, decl, refs, ctx.configPath ?? "");
|
|
7231
|
+
emitRuntimeFeedback(ctx.onFeedback, {
|
|
7232
|
+
type: "resource_adopted",
|
|
7233
|
+
level: "info",
|
|
7234
|
+
resource: address,
|
|
7235
|
+
message: `adopt deployment.${name} (${address.provider}) \u2014 already existed remotely as "${existing.name}"`
|
|
6500
7236
|
});
|
|
6501
7237
|
adopted = true;
|
|
7238
|
+
} else {
|
|
7239
|
+
result = await materializeDeployment();
|
|
6502
7240
|
}
|
|
6503
7241
|
}
|
|
6504
7242
|
break;
|
|
@@ -6510,15 +7248,12 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6510
7248
|
const oldId = ctx.state.getResource(address)?.remote_id;
|
|
6511
7249
|
if (oldId) {
|
|
6512
7250
|
try {
|
|
6513
|
-
await provider.deleteFile(oldId);
|
|
7251
|
+
await provider.deleteFile(oldId, priorApiMode);
|
|
6514
7252
|
} catch {
|
|
6515
7253
|
}
|
|
6516
7254
|
}
|
|
6517
7255
|
}
|
|
6518
|
-
const info = await provider.uploadFile(filePath, {
|
|
6519
|
-
name: decl.name,
|
|
6520
|
-
purpose: decl.purpose
|
|
6521
|
-
});
|
|
7256
|
+
const info = await provider.uploadFile(filePath, { name: decl.name, purpose: decl.purpose }, apiMode);
|
|
6522
7257
|
result = { id: info.id, type: "file" };
|
|
6523
7258
|
break;
|
|
6524
7259
|
}
|
|
@@ -6534,11 +7269,12 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6534
7269
|
remoteHash = contentHash(remote.comparable);
|
|
6535
7270
|
remoteSnapshot = remote.snapshot ?? remote.comparable;
|
|
6536
7271
|
}
|
|
6537
|
-
const priorResource = ctx.state.getResource(
|
|
7272
|
+
const priorResource = ctx.state.getResource(priorAddress);
|
|
6538
7273
|
ctx.state.setResource({
|
|
6539
7274
|
address,
|
|
6540
7275
|
remote_id: result.id,
|
|
6541
7276
|
externally_managed: priorResource?.externally_managed || type === "environment" && ctx.config.environments?.[name]?.environment_id || type === "identity" && ctx.config.identities?.[name]?.identity_id ? true : void 0,
|
|
7277
|
+
api_mode: apiMode,
|
|
6542
7278
|
version: result.version,
|
|
6543
7279
|
content_hash: hash,
|
|
6544
7280
|
desired_hash: hash,
|
|
@@ -6546,14 +7282,44 @@ async function executeActionInner(action, provider, ctx) {
|
|
|
6546
7282
|
desired_readiness_baseline: buildReadinessBaseline(getResourceDeclaration(address, ctx.config)),
|
|
6547
7283
|
remote_hash: remoteHash,
|
|
6548
7284
|
remote_snapshot: remoteSnapshot,
|
|
7285
|
+
replacement_fingerprint: computeReplacementFingerprint(address, ctx.config),
|
|
6549
7286
|
drift_paths: [],
|
|
6550
7287
|
drift_status: remoteHash ? "in_sync" : void 0
|
|
6551
7288
|
});
|
|
7289
|
+
if (action.previousAddress) ctx.state.removeResource(action.previousAddress);
|
|
6552
7290
|
return adopted;
|
|
6553
7291
|
}
|
|
6554
|
-
|
|
7292
|
+
function ownedForwardMemoryStoreIds(ctx, provider) {
|
|
7293
|
+
return ctx.state.listResources().filter(
|
|
7294
|
+
(resource) => resource.address.provider === provider && resource.address.type === "memory_store" && resource.api_mode === "forward" && typeof resource.remote_id === "string"
|
|
7295
|
+
).map((resource) => resource.remote_id);
|
|
7296
|
+
}
|
|
7297
|
+
function resolveResourceApiMode(type, name, provider, config) {
|
|
7298
|
+
if (provider !== "qoder" || type !== "environment" && type !== "skill" && type !== "vault" && type !== "memory_store" && type !== "file") {
|
|
7299
|
+
return void 0;
|
|
7300
|
+
}
|
|
7301
|
+
if (type === "environment" && config.environments?.[name]?.environment_id) return "auto";
|
|
7302
|
+
let managed = false;
|
|
7303
|
+
let forward = false;
|
|
7304
|
+
for (const agent of Object.values(config.agents ?? {})) {
|
|
7305
|
+
if (agent.provider && agent.provider !== provider) continue;
|
|
7306
|
+
const referenced = type === "environment" ? agent.environment === name : type === "skill" ? agent.skills?.some(
|
|
7307
|
+
(skill) => typeof skill === "string" ? skill === name : skill.type === "custom" && skill.skill_id === name
|
|
7308
|
+
) : type === "vault" ? agent.vault === name : type === "memory_store" ? agent.memory_stores?.includes(name) : agent.files?.includes(name);
|
|
7309
|
+
if (!referenced) continue;
|
|
7310
|
+
if (agent.delivery?.qoder?.type === "forward") forward = true;
|
|
7311
|
+
else managed = true;
|
|
7312
|
+
}
|
|
7313
|
+
if (managed && forward) {
|
|
7314
|
+
throw new UserError(
|
|
7315
|
+
`Qoder ${type}.${name} is referenced by both Managed and Forward agents; declare separate resources for each API domain.`
|
|
7316
|
+
);
|
|
7317
|
+
}
|
|
7318
|
+
return forward ? "forward" : "managed";
|
|
7319
|
+
}
|
|
7320
|
+
async function findExistingByNames(provider, type, names, mode) {
|
|
6555
7321
|
for (const candidate of names) {
|
|
6556
|
-
const found = await provider.findResource(type, candidate);
|
|
7322
|
+
const found = await provider.findResource(type, candidate, void 0, mode);
|
|
6557
7323
|
if (found && found.id !== null) return { resource: found, name: candidate };
|
|
6558
7324
|
}
|
|
6559
7325
|
return null;
|
|
@@ -6561,7 +7327,7 @@ async function findExistingByNames(provider, type, names) {
|
|
|
6561
7327
|
async function adoptOnConflict(err, address, provider, onFeedback, opts) {
|
|
6562
7328
|
if (!(err instanceof ConflictError)) throw err;
|
|
6563
7329
|
const candidates = opts.searchNames?.length ? opts.searchNames : [address.name];
|
|
6564
|
-
const existing = await findExistingByNames(provider, address.type, candidates);
|
|
7330
|
+
const existing = await findExistingByNames(provider, address.type, candidates, opts.mode);
|
|
6565
7331
|
if (!existing) throw nameReservedError(err, address, candidates.join('" / "'));
|
|
6566
7332
|
emitRuntimeFeedback(onFeedback, {
|
|
6567
7333
|
type: "resource_adopted",
|
|
@@ -6573,68 +7339,303 @@ async function adoptOnConflict(err, address, provider, onFeedback, opts) {
|
|
|
6573
7339
|
}
|
|
6574
7340
|
function nameReservedError(err, address, searchName) {
|
|
6575
7341
|
const detail = err instanceof ApiError ? err.message : String(err);
|
|
7342
|
+
if (address.type === "channel" && err instanceof ApiError && err.responseBody.includes("CHANNEL_CREDENTIAL_CONFLICT")) {
|
|
7343
|
+
return new UserError(
|
|
7344
|
+
`${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})`
|
|
7345
|
+
);
|
|
7346
|
+
}
|
|
6576
7347
|
return new UserError(
|
|
6577
7348
|
`${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
7349
|
);
|
|
6579
7350
|
}
|
|
6580
7351
|
|
|
6581
|
-
// src/internal/
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
}
|
|
6587
|
-
warning(code, message, resource) {
|
|
6588
|
-
this.items.push({ severity: "warning", code, message, resource });
|
|
6589
|
-
}
|
|
6590
|
-
info(code, message, resource) {
|
|
6591
|
-
this.items.push({ severity: "info", code, message, resource });
|
|
6592
|
-
}
|
|
6593
|
-
hasErrors() {
|
|
6594
|
-
return this.items.some((d) => d.severity === "error");
|
|
6595
|
-
}
|
|
6596
|
-
getAll() {
|
|
6597
|
-
return [...this.items];
|
|
6598
|
-
}
|
|
6599
|
-
format() {
|
|
6600
|
-
if (this.items.length === 0) return "";
|
|
6601
|
-
const lines = [];
|
|
6602
|
-
for (const d of this.items) {
|
|
6603
|
-
const icon = severityIcon(d.severity);
|
|
6604
|
-
const addr = d.resource ? ` ${d.resource.type}.${d.resource.name} (${d.resource.provider})` : "";
|
|
6605
|
-
lines.push(`${icon} ${d.code}`);
|
|
6606
|
-
if (addr) lines.push(` Resource:${addr}`);
|
|
6607
|
-
lines.push(` ${d.message}`);
|
|
6608
|
-
lines.push("");
|
|
6609
|
-
}
|
|
6610
|
-
return lines.join("\n");
|
|
6611
|
-
}
|
|
6612
|
-
};
|
|
6613
|
-
function severityIcon(s) {
|
|
6614
|
-
switch (s) {
|
|
6615
|
-
case "error":
|
|
6616
|
-
return "\u2717";
|
|
6617
|
-
case "warning":
|
|
6618
|
-
return "\u26A0";
|
|
6619
|
-
case "info":
|
|
6620
|
-
return "\u2139";
|
|
6621
|
-
}
|
|
7352
|
+
// src/internal/core/agent-materialization.ts
|
|
7353
|
+
function resolveAgentMaterialization(provider, agent) {
|
|
7354
|
+
const mode = agent.delivery?.[provider]?.type ?? "managed";
|
|
7355
|
+
if (mode === "managed") return { resourceType: "agent", mode };
|
|
7356
|
+
return { resourceType: "template", mode };
|
|
6622
7357
|
}
|
|
6623
7358
|
|
|
6624
|
-
// src/internal/
|
|
6625
|
-
function
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
if (
|
|
6630
|
-
|
|
6631
|
-
for (const
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
}
|
|
7359
|
+
// src/internal/graph/dependency.ts
|
|
7360
|
+
function collectDependencyClosure(graph, roots) {
|
|
7361
|
+
const selected = /* @__PURE__ */ new Map();
|
|
7362
|
+
function visit(address) {
|
|
7363
|
+
const key = addressKey(address);
|
|
7364
|
+
if (selected.has(key)) return;
|
|
7365
|
+
selected.set(key, graph.nodes.get(key) ?? address);
|
|
7366
|
+
for (const dependencyKey of graph.edges.get(key) ?? []) {
|
|
7367
|
+
const dependency = graph.nodes.get(dependencyKey);
|
|
7368
|
+
if (dependency) visit(dependency);
|
|
6635
7369
|
}
|
|
6636
7370
|
}
|
|
6637
|
-
|
|
7371
|
+
for (const root of roots) visit(root);
|
|
7372
|
+
return [...selected.values()];
|
|
7373
|
+
}
|
|
7374
|
+
function buildDependencyGraph(config, targetProviders) {
|
|
7375
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
7376
|
+
const edges = /* @__PURE__ */ new Map();
|
|
7377
|
+
function addNode(addr) {
|
|
7378
|
+
const key = addressKey(addr);
|
|
7379
|
+
nodes.set(key, addr);
|
|
7380
|
+
if (!edges.has(key)) edges.set(key, /* @__PURE__ */ new Set());
|
|
7381
|
+
}
|
|
7382
|
+
function addEdge(from, to) {
|
|
7383
|
+
const fromKey = addressKey(from);
|
|
7384
|
+
const toKey = addressKey(to);
|
|
7385
|
+
edges.get(fromKey)?.add(toKey);
|
|
7386
|
+
}
|
|
7387
|
+
for (const provider of targetProviders) {
|
|
7388
|
+
const def = getProvider(provider);
|
|
7389
|
+
const caps = def?.capabilities;
|
|
7390
|
+
if (config.environments) {
|
|
7391
|
+
for (const name of Object.keys(config.environments)) {
|
|
7392
|
+
const decl = config.environments[name];
|
|
7393
|
+
if (decl.provider && decl.provider !== provider) continue;
|
|
7394
|
+
addNode({ type: "environment", name, provider });
|
|
7395
|
+
}
|
|
7396
|
+
}
|
|
7397
|
+
if (config.memory_stores) {
|
|
7398
|
+
for (const name of Object.keys(config.memory_stores)) {
|
|
7399
|
+
const decl = config.memory_stores[name];
|
|
7400
|
+
if (decl.provider && decl.provider !== provider) continue;
|
|
7401
|
+
if (!isSupported(caps, "memory_store")) continue;
|
|
7402
|
+
addNode({ type: "memory_store", name, provider });
|
|
7403
|
+
}
|
|
7404
|
+
}
|
|
7405
|
+
if (config.vaults) {
|
|
7406
|
+
for (const name of Object.keys(config.vaults)) {
|
|
7407
|
+
const decl = config.vaults[name];
|
|
7408
|
+
if (decl.provider && decl.provider !== provider) continue;
|
|
7409
|
+
addNode({ type: "vault", name, provider });
|
|
7410
|
+
}
|
|
7411
|
+
}
|
|
7412
|
+
if (config.skills) {
|
|
7413
|
+
for (const name of Object.keys(config.skills)) {
|
|
7414
|
+
const decl = config.skills[name];
|
|
7415
|
+
if (decl.provider && decl.provider !== provider) continue;
|
|
7416
|
+
addNode({ type: "skill", name, provider });
|
|
7417
|
+
}
|
|
7418
|
+
}
|
|
7419
|
+
if (config.files) {
|
|
7420
|
+
for (const name of Object.keys(config.files)) {
|
|
7421
|
+
const decl = config.files[name];
|
|
7422
|
+
if (decl.provider && decl.provider !== provider) continue;
|
|
7423
|
+
addNode({ type: "file", name, provider });
|
|
7424
|
+
}
|
|
7425
|
+
}
|
|
7426
|
+
if (config.identities && isSupported(caps, "identity")) {
|
|
7427
|
+
for (const name of Object.keys(config.identities)) {
|
|
7428
|
+
const decl = config.identities[name];
|
|
7429
|
+
if (decl.provider && decl.provider !== provider) continue;
|
|
7430
|
+
addNode({ type: "identity", name, provider });
|
|
7431
|
+
}
|
|
7432
|
+
}
|
|
7433
|
+
if (config.agents) {
|
|
7434
|
+
for (const name of Object.keys(config.agents)) {
|
|
7435
|
+
const decl = config.agents[name];
|
|
7436
|
+
if (decl.provider && decl.provider !== provider) continue;
|
|
7437
|
+
const materialization = resolveAgentMaterialization(provider, decl);
|
|
7438
|
+
const agentAddr = { type: materialization.resourceType, name, provider };
|
|
7439
|
+
addNode(agentAddr);
|
|
7440
|
+
if ((decl.default_memory_store || decl.memory_stores?.length) && materialization.resourceType === "template") {
|
|
7441
|
+
const identityName = config.defaults?.identity;
|
|
7442
|
+
if (identityName) {
|
|
7443
|
+
const identityAddr = { type: "identity", name: identityName, provider };
|
|
7444
|
+
if (nodes.has(addressKey(identityAddr))) addEdge(agentAddr, identityAddr);
|
|
7445
|
+
}
|
|
7446
|
+
}
|
|
7447
|
+
if (decl.environment && config.environments?.[decl.environment]) {
|
|
7448
|
+
const envAddr = {
|
|
7449
|
+
type: "environment",
|
|
7450
|
+
name: decl.environment,
|
|
7451
|
+
provider
|
|
7452
|
+
};
|
|
7453
|
+
if (nodes.has(addressKey(envAddr))) {
|
|
7454
|
+
addEdge(agentAddr, envAddr);
|
|
7455
|
+
}
|
|
7456
|
+
}
|
|
7457
|
+
if (decl.skills) {
|
|
7458
|
+
for (const skillRef of decl.skills) {
|
|
7459
|
+
if (typeof skillRef !== "string") continue;
|
|
7460
|
+
const skillName = skillRef;
|
|
7461
|
+
const skillAddr = { type: "skill", name: skillName, provider };
|
|
7462
|
+
if (nodes.has(addressKey(skillAddr))) {
|
|
7463
|
+
addEdge(agentAddr, skillAddr);
|
|
7464
|
+
}
|
|
7465
|
+
}
|
|
7466
|
+
}
|
|
7467
|
+
if (decl.vault) {
|
|
7468
|
+
const vaultAddr = { type: "vault", name: decl.vault, provider };
|
|
7469
|
+
if (nodes.has(addressKey(vaultAddr))) {
|
|
7470
|
+
addEdge(agentAddr, vaultAddr);
|
|
7471
|
+
}
|
|
7472
|
+
}
|
|
7473
|
+
if (decl.files) {
|
|
7474
|
+
for (const fileName of decl.files) {
|
|
7475
|
+
const fileAddr = { type: "file", name: fileName, provider };
|
|
7476
|
+
if (nodes.has(addressKey(fileAddr))) addEdge(agentAddr, fileAddr);
|
|
7477
|
+
}
|
|
7478
|
+
}
|
|
7479
|
+
if (decl.memory_stores) {
|
|
7480
|
+
for (const msName of decl.memory_stores) {
|
|
7481
|
+
const msAddr = {
|
|
7482
|
+
type: "memory_store",
|
|
7483
|
+
name: msName,
|
|
7484
|
+
provider
|
|
7485
|
+
};
|
|
7486
|
+
if (nodes.has(addressKey(msAddr))) {
|
|
7487
|
+
addEdge(agentAddr, msAddr);
|
|
7488
|
+
}
|
|
7489
|
+
}
|
|
7490
|
+
}
|
|
7491
|
+
if (decl.multiagent && isSupported(caps, "multiagent")) {
|
|
7492
|
+
for (const subName of decl.multiagent.agents) {
|
|
7493
|
+
const subDecl = config.agents[subName];
|
|
7494
|
+
const subType = subDecl ? resolveAgentMaterialization(provider, subDecl).resourceType : "agent";
|
|
7495
|
+
const subAddr = { type: subType, name: subName, provider };
|
|
7496
|
+
addEdge(agentAddr, subAddr);
|
|
7497
|
+
}
|
|
7498
|
+
}
|
|
7499
|
+
}
|
|
7500
|
+
}
|
|
7501
|
+
if (config.deployments) {
|
|
7502
|
+
for (const name of Object.keys(config.deployments)) {
|
|
7503
|
+
const decl = config.deployments[name];
|
|
7504
|
+
if (decl.provider && decl.provider !== provider) continue;
|
|
7505
|
+
const depAddr = { type: "deployment", name, provider };
|
|
7506
|
+
addNode(depAddr);
|
|
7507
|
+
const agentDecl = config.agents?.[decl.agent];
|
|
7508
|
+
const agentType = agentDecl ? resolveAgentMaterialization(provider, agentDecl).resourceType : "agent";
|
|
7509
|
+
const agentAddr = { type: agentType, name: decl.agent, provider };
|
|
7510
|
+
if (nodes.has(addressKey(agentAddr))) addEdge(depAddr, agentAddr);
|
|
7511
|
+
if (decl.environment) {
|
|
7512
|
+
const envAddr = { type: "environment", name: decl.environment, provider };
|
|
7513
|
+
if (nodes.has(addressKey(envAddr))) addEdge(depAddr, envAddr);
|
|
7514
|
+
}
|
|
7515
|
+
if (decl.vaults) {
|
|
7516
|
+
for (const vName of decl.vaults) {
|
|
7517
|
+
const vAddr = { type: "vault", name: vName, provider };
|
|
7518
|
+
if (nodes.has(addressKey(vAddr))) addEdge(depAddr, vAddr);
|
|
7519
|
+
}
|
|
7520
|
+
}
|
|
7521
|
+
if (decl.memory_stores) {
|
|
7522
|
+
for (const msName of decl.memory_stores) {
|
|
7523
|
+
const msAddr = { type: "memory_store", name: msName, provider };
|
|
7524
|
+
if (nodes.has(addressKey(msAddr))) addEdge(depAddr, msAddr);
|
|
7525
|
+
}
|
|
7526
|
+
}
|
|
7527
|
+
if (decl.resources) {
|
|
7528
|
+
for (const r of decl.resources) {
|
|
7529
|
+
if (r.type === "memory_store") {
|
|
7530
|
+
const msAddr = { type: "memory_store", name: r.memory_store, provider };
|
|
7531
|
+
if (nodes.has(addressKey(msAddr))) addEdge(depAddr, msAddr);
|
|
7532
|
+
}
|
|
7533
|
+
}
|
|
7534
|
+
}
|
|
7535
|
+
}
|
|
7536
|
+
}
|
|
7537
|
+
if (config.channels && isSupported(caps, "channel")) {
|
|
7538
|
+
for (const name of Object.keys(config.channels)) {
|
|
7539
|
+
const decl = config.channels[name];
|
|
7540
|
+
if (decl.provider && decl.provider !== provider) continue;
|
|
7541
|
+
const channelAddr = { type: "channel", name, provider };
|
|
7542
|
+
addNode(channelAddr);
|
|
7543
|
+
if (decl.mode === "pairing" || !decl.agent) continue;
|
|
7544
|
+
const agentDecl = config.agents?.[decl.agent];
|
|
7545
|
+
const agentType = agentDecl ? resolveAgentMaterialization(provider, agentDecl).resourceType : "agent";
|
|
7546
|
+
const agentAddr = { type: agentType, name: decl.agent, provider };
|
|
7547
|
+
if (nodes.has(addressKey(agentAddr))) addEdge(channelAddr, agentAddr);
|
|
7548
|
+
const identityName = decl.identity ?? config.defaults?.identity;
|
|
7549
|
+
if (identityName) {
|
|
7550
|
+
const identityAddr = { type: "identity", name: identityName, provider };
|
|
7551
|
+
if (nodes.has(addressKey(identityAddr))) addEdge(channelAddr, identityAddr);
|
|
7552
|
+
}
|
|
7553
|
+
}
|
|
7554
|
+
}
|
|
7555
|
+
}
|
|
7556
|
+
return { nodes, edges };
|
|
7557
|
+
}
|
|
7558
|
+
function topologicalSort(graph) {
|
|
7559
|
+
const visited = /* @__PURE__ */ new Set();
|
|
7560
|
+
const sorted = [];
|
|
7561
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
7562
|
+
function visit(key) {
|
|
7563
|
+
if (visited.has(key)) return;
|
|
7564
|
+
if (visiting.has(key)) {
|
|
7565
|
+
throw new UserError(`Circular dependency detected involving: ${key}`);
|
|
7566
|
+
}
|
|
7567
|
+
visiting.add(key);
|
|
7568
|
+
const deps = graph.edges.get(key) ?? /* @__PURE__ */ new Set();
|
|
7569
|
+
for (const dep of deps) {
|
|
7570
|
+
visit(dep);
|
|
7571
|
+
}
|
|
7572
|
+
visiting.delete(key);
|
|
7573
|
+
visited.add(key);
|
|
7574
|
+
sorted.push(graph.nodes.get(key));
|
|
7575
|
+
}
|
|
7576
|
+
for (const key of graph.nodes.keys()) {
|
|
7577
|
+
visit(key);
|
|
7578
|
+
}
|
|
7579
|
+
return sorted;
|
|
7580
|
+
}
|
|
7581
|
+
|
|
7582
|
+
// src/internal/diagnostics/diagnostics.ts
|
|
7583
|
+
var DiagnosticCollector = class {
|
|
7584
|
+
items = [];
|
|
7585
|
+
error(code, message, resource) {
|
|
7586
|
+
this.items.push({ severity: "error", code, message, resource });
|
|
7587
|
+
}
|
|
7588
|
+
warning(code, message, resource) {
|
|
7589
|
+
this.items.push({ severity: "warning", code, message, resource });
|
|
7590
|
+
}
|
|
7591
|
+
info(code, message, resource) {
|
|
7592
|
+
this.items.push({ severity: "info", code, message, resource });
|
|
7593
|
+
}
|
|
7594
|
+
hasErrors() {
|
|
7595
|
+
return this.items.some((d) => d.severity === "error");
|
|
7596
|
+
}
|
|
7597
|
+
getAll() {
|
|
7598
|
+
return [...this.items];
|
|
7599
|
+
}
|
|
7600
|
+
format() {
|
|
7601
|
+
if (this.items.length === 0) return "";
|
|
7602
|
+
const lines = [];
|
|
7603
|
+
for (const d of this.items) {
|
|
7604
|
+
const icon = severityIcon(d.severity);
|
|
7605
|
+
const addr = d.resource ? ` ${d.resource.type}.${d.resource.name} (${d.resource.provider})` : "";
|
|
7606
|
+
lines.push(`${icon} ${d.code}`);
|
|
7607
|
+
if (addr) lines.push(` Resource:${addr}`);
|
|
7608
|
+
lines.push(` ${d.message}`);
|
|
7609
|
+
lines.push("");
|
|
7610
|
+
}
|
|
7611
|
+
return lines.join("\n");
|
|
7612
|
+
}
|
|
7613
|
+
};
|
|
7614
|
+
function severityIcon(s) {
|
|
7615
|
+
switch (s) {
|
|
7616
|
+
case "error":
|
|
7617
|
+
return "\u2717";
|
|
7618
|
+
case "warning":
|
|
7619
|
+
return "\u26A0";
|
|
7620
|
+
case "info":
|
|
7621
|
+
return "\u2139";
|
|
7622
|
+
}
|
|
7623
|
+
}
|
|
7624
|
+
|
|
7625
|
+
// src/internal/validation/bailian.ts
|
|
7626
|
+
function findMissingBailianMcpToolConfigs(config, providerName = "bailian") {
|
|
7627
|
+
if (providerName !== "bailian") return [];
|
|
7628
|
+
const missing = [];
|
|
7629
|
+
for (const [agentName, agent] of Object.entries(config.agents ?? {})) {
|
|
7630
|
+
if (!agentTargetsProvider(config, agent.provider, providerName)) continue;
|
|
7631
|
+
const configuredToolServers = new Set((agent.tools?.mcp ?? []).map((tool) => tool.mcp_server_name));
|
|
7632
|
+
for (const server of agent.mcp_servers ?? []) {
|
|
7633
|
+
if (!configuredToolServers.has(server.name)) {
|
|
7634
|
+
missing.push({ agentName, serverName: server.name });
|
|
7635
|
+
}
|
|
7636
|
+
}
|
|
7637
|
+
}
|
|
7638
|
+
return missing;
|
|
6638
7639
|
}
|
|
6639
7640
|
function agentTargetsProvider(config, agentProvider, providerName) {
|
|
6640
7641
|
if (agentProvider) return agentProvider === providerName;
|
|
@@ -6671,6 +7672,7 @@ function collectReferenceDiagnostics(config, diagnostics) {
|
|
|
6671
7672
|
const skillNames = new Set(Object.keys(config.skills ?? {}));
|
|
6672
7673
|
const vaultNames = new Set(Object.keys(config.vaults ?? {}));
|
|
6673
7674
|
const memoryNames = new Set(Object.keys(config.memory_stores ?? {}));
|
|
7675
|
+
const fileNames = new Set(Object.keys(config.files ?? {}));
|
|
6674
7676
|
const agentNames = new Set(Object.keys(config.agents ?? {}));
|
|
6675
7677
|
const identityNames = new Set(Object.keys(config.identities ?? {}));
|
|
6676
7678
|
if (config.defaults?.identity && !identityNames.has(config.defaults.identity)) {
|
|
@@ -6698,6 +7700,11 @@ function collectReferenceDiagnostics(config, diagnostics) {
|
|
|
6698
7700
|
if (agent.vault && !vaultNames.has(agent.vault)) {
|
|
6699
7701
|
diagnostics.error("config.agent.vault.unknown", `agent.${name}: references unknown vault '${agent.vault}'`);
|
|
6700
7702
|
}
|
|
7703
|
+
for (const file of agent.files ?? []) {
|
|
7704
|
+
if (!fileNames.has(file)) {
|
|
7705
|
+
diagnostics.error("config.agent.file.unknown", `agent.${name}: references unknown file '${file}'`);
|
|
7706
|
+
}
|
|
7707
|
+
}
|
|
6701
7708
|
for (const memory of agent.memory_stores ?? []) {
|
|
6702
7709
|
if (!memoryNames.has(memory)) {
|
|
6703
7710
|
diagnostics.error(
|
|
@@ -6729,7 +7736,10 @@ function collectReferenceDiagnostics(config, diagnostics) {
|
|
|
6729
7736
|
}
|
|
6730
7737
|
}
|
|
6731
7738
|
for (const [name, channel] of Object.entries(config.channels ?? {})) {
|
|
6732
|
-
if (
|
|
7739
|
+
if (channel.mode === "pairing") continue;
|
|
7740
|
+
if (!channel.agent) {
|
|
7741
|
+
diagnostics.error("config.channel.agent.required", `channel.${name}: fixed-mode channels require agent`);
|
|
7742
|
+
} else if (!agentNames.has(channel.agent)) {
|
|
6733
7743
|
diagnostics.error("config.channel.agent.unknown", `channel.${name}: references unknown agent '${channel.agent}'`);
|
|
6734
7744
|
}
|
|
6735
7745
|
const identity = channel.identity ?? config.defaults?.identity;
|
|
@@ -6757,6 +7767,37 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6757
7767
|
continue;
|
|
6758
7768
|
}
|
|
6759
7769
|
const caps = def.capabilities;
|
|
7770
|
+
for (const [name, environment] of Object.entries(config.environments ?? {})) {
|
|
7771
|
+
if (environment.provider && environment.provider !== providerName) continue;
|
|
7772
|
+
if (environment.environment_id) continue;
|
|
7773
|
+
const address = { type: "environment", name, provider: providerName };
|
|
7774
|
+
if (environment.config.setup_script !== void 0 && providerName !== "qoder") {
|
|
7775
|
+
diagnostics.error(
|
|
7776
|
+
`${providerName}.environment.setup_script.unsupported`,
|
|
7777
|
+
`environment.${name}: provider '${providerName}' does not support setup_script; remove it or pin this environment to qoder.`,
|
|
7778
|
+
address
|
|
7779
|
+
);
|
|
7780
|
+
}
|
|
7781
|
+
if (providerName === "qoder") {
|
|
7782
|
+
if (environment.config.type === "self_hosted" && (environment.config.networking !== void 0 || environment.config.packages !== void 0)) {
|
|
7783
|
+
diagnostics.error(
|
|
7784
|
+
"qoder.environment.self_hosted.config.unsupported",
|
|
7785
|
+
`environment.${name}: Qoder self_hosted environments accept only config.type and config.setup_script; remove networking and packages.`,
|
|
7786
|
+
address
|
|
7787
|
+
);
|
|
7788
|
+
}
|
|
7789
|
+
const unsupported = ["cargo", "gem", "go"].filter(
|
|
7790
|
+
(key) => (environment.config.packages?.[key]?.length ?? 0) > 0
|
|
7791
|
+
);
|
|
7792
|
+
if (unsupported.length > 0) {
|
|
7793
|
+
diagnostics.error(
|
|
7794
|
+
"qoder.environment.packages.unsupported",
|
|
7795
|
+
`environment.${name}: Qoder accepts only apt, npm, and pip package declarations; remove ${unsupported.join(", ")} or install them from setup_script.`,
|
|
7796
|
+
address
|
|
7797
|
+
);
|
|
7798
|
+
}
|
|
7799
|
+
}
|
|
7800
|
+
}
|
|
6760
7801
|
for (const [name, identity] of Object.entries(config.identities ?? {})) {
|
|
6761
7802
|
if (identity.provider && identity.provider !== providerName) continue;
|
|
6762
7803
|
if (!isSupported(caps, "identity")) {
|
|
@@ -6778,29 +7819,31 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6778
7819
|
continue;
|
|
6779
7820
|
}
|
|
6780
7821
|
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
|
-
|
|
7822
|
+
if (channel.mode !== "pairing" && channel.agent) {
|
|
7823
|
+
const agent = config.agents?.[channel.agent];
|
|
7824
|
+
if (agent?.provider && agent.provider !== providerName) {
|
|
7825
|
+
diagnostics.error(
|
|
7826
|
+
"config.channel.agent.provider_mismatch",
|
|
7827
|
+
`channel.${name}: agent '${channel.agent}' is pinned to provider '${agent.provider}'.`,
|
|
7828
|
+
{ type: "channel", name, provider: providerName }
|
|
7829
|
+
);
|
|
7830
|
+
}
|
|
7831
|
+
const identityName = channel.identity ?? config.defaults?.identity;
|
|
7832
|
+
const identity = identityName ? config.identities?.[identityName] : void 0;
|
|
7833
|
+
if (identity?.provider && identity.provider !== providerName) {
|
|
7834
|
+
diagnostics.error(
|
|
7835
|
+
"config.channel.identity.provider_mismatch",
|
|
7836
|
+
`channel.${name}: identity '${identityName}' is pinned to provider '${identity.provider}'.`,
|
|
7837
|
+
{ type: "channel", name, provider: providerName }
|
|
7838
|
+
);
|
|
7839
|
+
}
|
|
7840
|
+
if (agent && agent.delivery?.qoder?.type !== "forward") {
|
|
7841
|
+
diagnostics.error(
|
|
7842
|
+
"qoder.channel.forward_template.required",
|
|
7843
|
+
`channel.${name}: Qoder Channels require agent '${channel.agent}' to use delivery.qoder.type: forward.`,
|
|
7844
|
+
{ type: "channel", name, provider: providerName }
|
|
7845
|
+
);
|
|
7846
|
+
}
|
|
6804
7847
|
}
|
|
6805
7848
|
const requiredCredentials = {
|
|
6806
7849
|
dingtalk: ["client_id", "client_secret"],
|
|
@@ -6831,6 +7874,46 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6831
7874
|
}
|
|
6832
7875
|
}
|
|
6833
7876
|
}
|
|
7877
|
+
if (providerName === "qoder") {
|
|
7878
|
+
const domains = /* @__PURE__ */ new Map();
|
|
7879
|
+
for (const agent of Object.values(config.agents ?? {})) {
|
|
7880
|
+
if (agent.provider && agent.provider !== providerName) continue;
|
|
7881
|
+
const mode = agent.delivery?.qoder?.type === "forward" ? "forward" : "managed";
|
|
7882
|
+
const refs = [
|
|
7883
|
+
...agent.environment && !config.environments?.[agent.environment]?.environment_id ? [`environment:${agent.environment}`] : [],
|
|
7884
|
+
...(agent.skills ?? []).flatMap(
|
|
7885
|
+
(skill) => typeof skill === "string" ? [`skill:${skill}`] : skill.type === "custom" ? [`skill:${skill.skill_id}`] : []
|
|
7886
|
+
),
|
|
7887
|
+
...agent.vault ? [`vault:${agent.vault}`] : [],
|
|
7888
|
+
...(agent.memory_stores ?? []).map((store) => `memory_store:${store}`),
|
|
7889
|
+
...(agent.files ?? []).map((file) => `file:${file}`)
|
|
7890
|
+
];
|
|
7891
|
+
for (const ref of refs) {
|
|
7892
|
+
const modes = domains.get(ref) ?? /* @__PURE__ */ new Set();
|
|
7893
|
+
modes.add(mode);
|
|
7894
|
+
domains.set(ref, modes);
|
|
7895
|
+
}
|
|
7896
|
+
}
|
|
7897
|
+
for (const [ref, modes] of domains) {
|
|
7898
|
+
if (modes.size < 2) continue;
|
|
7899
|
+
const [type, name] = ref.split(":");
|
|
7900
|
+
diagnostics.error(
|
|
7901
|
+
`qoder.${type}.delivery_domain.conflict`,
|
|
7902
|
+
`${type}.${name}: referenced by both Managed and Forward agents; declare separate resources because Qoder uses different API domains.`,
|
|
7903
|
+
{ type, name, provider: providerName }
|
|
7904
|
+
);
|
|
7905
|
+
}
|
|
7906
|
+
}
|
|
7907
|
+
for (const [name, agent] of Object.entries(config.agents ?? {})) {
|
|
7908
|
+
if (agent.provider && agent.provider !== providerName) continue;
|
|
7909
|
+
if (agent.files?.length && (providerName !== "qoder" || agent.delivery?.qoder?.type !== "forward")) {
|
|
7910
|
+
diagnostics.error(
|
|
7911
|
+
"config.agent.files.unsupported",
|
|
7912
|
+
`agent.${name}: files are supported only by Qoder Forward Templates.`,
|
|
7913
|
+
{ type: resolveAgentMaterialization(providerName, agent).resourceType, name, provider: providerName }
|
|
7914
|
+
);
|
|
7915
|
+
}
|
|
7916
|
+
}
|
|
6834
7917
|
for (const [name, agent] of Object.entries(config.agents ?? {})) {
|
|
6835
7918
|
if (agent.provider && agent.provider !== providerName) continue;
|
|
6836
7919
|
const delivery = agent.delivery?.[providerName]?.type ?? "managed";
|
|
@@ -6871,6 +7954,44 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6871
7954
|
address
|
|
6872
7955
|
);
|
|
6873
7956
|
}
|
|
7957
|
+
if (delivery !== "forward" && agent.managed_tool_config) {
|
|
7958
|
+
diagnostics.error(
|
|
7959
|
+
`${providerName}.agent.managed_tool_config.forward_required`,
|
|
7960
|
+
`agent.${name}: managed_tool_config applies to Forward Templates; set delivery.${providerName}.type: forward or remove it.`,
|
|
7961
|
+
address
|
|
7962
|
+
);
|
|
7963
|
+
}
|
|
7964
|
+
if (agent.default_memory_store) {
|
|
7965
|
+
if (providerName !== "qoder" || delivery !== "forward") {
|
|
7966
|
+
diagnostics.error(
|
|
7967
|
+
`${providerName}.agent.default_memory_store.forward_required`,
|
|
7968
|
+
`agent.${name}: default_memory_store is supported only by Qoder Forward delivery.`,
|
|
7969
|
+
address
|
|
7970
|
+
);
|
|
7971
|
+
} else if (!config.defaults?.identity) {
|
|
7972
|
+
diagnostics.error(
|
|
7973
|
+
"qoder.template.default_memory_store.identity.required",
|
|
7974
|
+
`agent.${name}: default_memory_store requires defaults.identity to select the owning Forward Identity.`,
|
|
7975
|
+
{ type: "template", name, provider: providerName }
|
|
7976
|
+
);
|
|
7977
|
+
} else {
|
|
7978
|
+
const identity = config.identities?.[config.defaults.identity];
|
|
7979
|
+
if (identity?.provider && identity.provider !== providerName) {
|
|
7980
|
+
diagnostics.error(
|
|
7981
|
+
"qoder.template.default_memory_store.identity.provider_mismatch",
|
|
7982
|
+
`agent.${name}: defaults.identity '${config.defaults.identity}' is pinned to provider '${identity.provider}'.`,
|
|
7983
|
+
{ type: "template", name, provider: providerName }
|
|
7984
|
+
);
|
|
7985
|
+
}
|
|
7986
|
+
if (agent.default_memory_store.delete_on_destroy && identity?.identity_id) {
|
|
7987
|
+
diagnostics.error(
|
|
7988
|
+
"qoder.template.default_memory_store.delete.external_identity",
|
|
7989
|
+
`agent.${name}: delete_on_destroy requires an OpenCMA-managed Identity because an external Identity keeps the default Memory Store mounted.`,
|
|
7990
|
+
{ type: "template", name, provider: providerName }
|
|
7991
|
+
);
|
|
7992
|
+
}
|
|
7993
|
+
}
|
|
7994
|
+
}
|
|
6874
7995
|
if (delivery === "forward" && !isSupported(caps, "template")) {
|
|
6875
7996
|
diagnostics.error(
|
|
6876
7997
|
`${providerName}.agent.delivery.forward.unsupported`,
|
|
@@ -6886,10 +8007,10 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6886
8007
|
{ type: "template", name, provider: providerName }
|
|
6887
8008
|
);
|
|
6888
8009
|
}
|
|
6889
|
-
if (agent.memory_stores?.length) {
|
|
8010
|
+
if (agent.memory_stores?.length && !config.defaults?.identity) {
|
|
6890
8011
|
diagnostics.error(
|
|
6891
|
-
"qoder.template.memory_store.
|
|
6892
|
-
`agent.${name}: memory_stores
|
|
8012
|
+
"qoder.template.memory_store.identity.required",
|
|
8013
|
+
`agent.${name}: Forward memory_stores require defaults.identity for the Identity/Template mount.`,
|
|
6893
8014
|
{ type: "template", name, provider: providerName }
|
|
6894
8015
|
);
|
|
6895
8016
|
}
|
|
@@ -6923,6 +8044,68 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6923
8044
|
}
|
|
6924
8045
|
}
|
|
6925
8046
|
}
|
|
8047
|
+
if (providerName === "bailian") {
|
|
8048
|
+
for (const [name, deployment] of Object.entries(config.deployments ?? {})) {
|
|
8049
|
+
if (deployment.provider && deployment.provider !== providerName) continue;
|
|
8050
|
+
const addr = {
|
|
8051
|
+
type: "deployment",
|
|
8052
|
+
name,
|
|
8053
|
+
provider: providerName
|
|
8054
|
+
};
|
|
8055
|
+
if (deployment.initial_events?.some((event) => event.type === "user.define_outcome")) {
|
|
8056
|
+
diagnostics.warning(
|
|
8057
|
+
`${providerName}.deployment.define_outcome_unsupported`,
|
|
8058
|
+
"Outcome rubrics (user.define_outcome) are dropped from the Bailian deployment payload; the run executes without rubric grading.",
|
|
8059
|
+
addr
|
|
8060
|
+
);
|
|
8061
|
+
}
|
|
8062
|
+
if (!deployment.initial_events?.some((event) => event.type === "user.message" || event.type === "system.message")) {
|
|
8063
|
+
diagnostics.error(
|
|
8064
|
+
`${providerName}.deployment.initial_events.message_required`,
|
|
8065
|
+
`deployment.${name}: Bailian requires at least one user.message or system.message initial event; user.define_outcome events are dropped.`,
|
|
8066
|
+
addr
|
|
8067
|
+
);
|
|
8068
|
+
}
|
|
8069
|
+
if (deployment.resources?.some((resource) => resource.type === "github_repository")) {
|
|
8070
|
+
diagnostics.warning(
|
|
8071
|
+
`${providerName}.deployment.github_repository_unsupported`,
|
|
8072
|
+
"Bailian deployment resources accept files only; github_repository resources are dropped. Clone the repository inside the session instead.",
|
|
8073
|
+
addr
|
|
8074
|
+
);
|
|
8075
|
+
}
|
|
8076
|
+
const mountPrefix = providerMountPrefix(providerName);
|
|
8077
|
+
const normalizedMountPaths = /* @__PURE__ */ new Set();
|
|
8078
|
+
for (const resource of deployment.resources ?? []) {
|
|
8079
|
+
if (resource.type !== "file") continue;
|
|
8080
|
+
if (!resource.mount_path?.trim()) {
|
|
8081
|
+
diagnostics.error(
|
|
8082
|
+
`${providerName}.deployment.file.mount_path.required`,
|
|
8083
|
+
`deployment.${name}: Bailian file resources require mount_path.`,
|
|
8084
|
+
addr
|
|
8085
|
+
);
|
|
8086
|
+
continue;
|
|
8087
|
+
}
|
|
8088
|
+
if (mountPrefix && resource.mount_path.startsWith("/") && resource.mount_path !== mountPrefix && !resource.mount_path.startsWith(`${mountPrefix}/`)) {
|
|
8089
|
+
diagnostics.error(
|
|
8090
|
+
`${providerName}.deployment.file.mount_path.invalid`,
|
|
8091
|
+
`deployment.${name}: Bailian file mount_path must start with '${mountPrefix}/'.`,
|
|
8092
|
+
addr
|
|
8093
|
+
);
|
|
8094
|
+
continue;
|
|
8095
|
+
}
|
|
8096
|
+
const normalizedMountPath = resolveSandboxMountPath(providerName, resource.mount_path);
|
|
8097
|
+
if (normalizedMountPaths.has(normalizedMountPath)) {
|
|
8098
|
+
diagnostics.error(
|
|
8099
|
+
`${providerName}.deployment.file.mount_path.duplicate`,
|
|
8100
|
+
`deployment.${name}: Bailian file mount_path '${normalizedMountPath}' is duplicated after normalization.`,
|
|
8101
|
+
addr
|
|
8102
|
+
);
|
|
8103
|
+
} else {
|
|
8104
|
+
normalizedMountPaths.add(normalizedMountPath);
|
|
8105
|
+
}
|
|
8106
|
+
}
|
|
8107
|
+
}
|
|
8108
|
+
}
|
|
6926
8109
|
if (providerName !== "qoder") {
|
|
6927
8110
|
for (const [name, env] of Object.entries(config.environments ?? {})) {
|
|
6928
8111
|
if (env.environment_id) continue;
|
|
@@ -6935,6 +8118,20 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6935
8118
|
}
|
|
6936
8119
|
}
|
|
6937
8120
|
for (const [name, agent] of Object.entries(config.agents ?? {})) {
|
|
8121
|
+
if (agent.environment_variables && (!agent.provider || agent.provider === providerName)) {
|
|
8122
|
+
diagnostics.error(
|
|
8123
|
+
`${providerName}.agent.environment_variables.unsupported`,
|
|
8124
|
+
`agent.${name}: environment_variables is supported only by Qoder; remove it or pin this agent to qoder.`,
|
|
8125
|
+
{ type: "agent", name, provider: providerName }
|
|
8126
|
+
);
|
|
8127
|
+
}
|
|
8128
|
+
if (agent.managed_tool_config && (!agent.provider || agent.provider === providerName)) {
|
|
8129
|
+
diagnostics.error(
|
|
8130
|
+
`${providerName}.agent.managed_tool_config.unsupported`,
|
|
8131
|
+
`agent.${name}: managed_tool_config is supported only by Qoder; remove it or pin this agent to qoder.`,
|
|
8132
|
+
{ type: "agent", name, provider: providerName }
|
|
8133
|
+
);
|
|
8134
|
+
}
|
|
6938
8135
|
if (agent.tunnel && (!agent.provider || agent.provider === providerName)) {
|
|
6939
8136
|
diagnostics.error(
|
|
6940
8137
|
`${providerName}.agent.tunnel.unsupported`,
|
|
@@ -6995,7 +8192,11 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
6995
8192
|
if (config.deployments && caps.deployment.tier === "emulated") {
|
|
6996
8193
|
for (const [name, dep] of Object.entries(config.deployments)) {
|
|
6997
8194
|
if (dep.provider && dep.provider !== providerName) continue;
|
|
6998
|
-
const addr = {
|
|
8195
|
+
const addr = {
|
|
8196
|
+
type: "deployment",
|
|
8197
|
+
name,
|
|
8198
|
+
provider: providerName
|
|
8199
|
+
};
|
|
6999
8200
|
if (dep.schedule) {
|
|
7000
8201
|
diagnostics.warning(
|
|
7001
8202
|
`${providerName}.deployment.schedule_unsupported`,
|
|
@@ -7014,238 +8215,39 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
7014
8215
|
diagnostics.warning(
|
|
7015
8216
|
`${providerName}.deployment.github_repository_unsupported`,
|
|
7016
8217
|
"github_repository resources are not provisioned on this provider; clone the repository inside the session instead.",
|
|
7017
|
-
addr
|
|
7018
|
-
);
|
|
7019
|
-
}
|
|
7020
|
-
if (dep.resources?.some((r) => r.type === "memory_store" && (r.access === "read_only" || r.instructions))) {
|
|
7021
|
-
diagnostics.warning(
|
|
7022
|
-
`${providerName}.deployment.memory_store_options_unsupported`,
|
|
7023
|
-
"Per-resource memory_store access/instructions are not honored on this provider; the store is attached with default access.",
|
|
7024
|
-
addr
|
|
7025
|
-
);
|
|
7026
|
-
}
|
|
7027
|
-
}
|
|
7028
|
-
}
|
|
7029
|
-
}
|
|
7030
|
-
}
|
|
7031
|
-
|
|
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
|
-
// src/internal/graph/dependency.ts
|
|
7040
|
-
function buildDependencyGraph(config, targetProviders) {
|
|
7041
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
7042
|
-
const edges = /* @__PURE__ */ new Map();
|
|
7043
|
-
function addNode(addr) {
|
|
7044
|
-
const key = addressKey(addr);
|
|
7045
|
-
nodes.set(key, addr);
|
|
7046
|
-
if (!edges.has(key)) edges.set(key, /* @__PURE__ */ new Set());
|
|
7047
|
-
}
|
|
7048
|
-
function addEdge(from, to) {
|
|
7049
|
-
const fromKey = addressKey(from);
|
|
7050
|
-
const toKey = addressKey(to);
|
|
7051
|
-
edges.get(fromKey)?.add(toKey);
|
|
7052
|
-
}
|
|
7053
|
-
for (const provider of targetProviders) {
|
|
7054
|
-
const def = getProvider(provider);
|
|
7055
|
-
const caps = def?.capabilities;
|
|
7056
|
-
if (config.environments) {
|
|
7057
|
-
for (const name of Object.keys(config.environments)) {
|
|
7058
|
-
const decl = config.environments[name];
|
|
7059
|
-
if (decl.provider && decl.provider !== provider) continue;
|
|
7060
|
-
addNode({ type: "environment", name, provider });
|
|
7061
|
-
}
|
|
7062
|
-
}
|
|
7063
|
-
if (config.memory_stores) {
|
|
7064
|
-
for (const name of Object.keys(config.memory_stores)) {
|
|
7065
|
-
const decl = config.memory_stores[name];
|
|
7066
|
-
if (decl.provider && decl.provider !== provider) continue;
|
|
7067
|
-
if (!isSupported(caps, "memory_store")) continue;
|
|
7068
|
-
addNode({ type: "memory_store", name, provider });
|
|
7069
|
-
}
|
|
7070
|
-
}
|
|
7071
|
-
if (config.vaults) {
|
|
7072
|
-
for (const name of Object.keys(config.vaults)) {
|
|
7073
|
-
const decl = config.vaults[name];
|
|
7074
|
-
if (decl.provider && decl.provider !== provider) continue;
|
|
7075
|
-
addNode({ type: "vault", name, provider });
|
|
7076
|
-
}
|
|
7077
|
-
}
|
|
7078
|
-
if (config.skills) {
|
|
7079
|
-
for (const name of Object.keys(config.skills)) {
|
|
7080
|
-
const decl = config.skills[name];
|
|
7081
|
-
if (decl.provider && decl.provider !== provider) continue;
|
|
7082
|
-
addNode({ type: "skill", name, provider });
|
|
7083
|
-
}
|
|
7084
|
-
}
|
|
7085
|
-
if (config.files) {
|
|
7086
|
-
for (const name of Object.keys(config.files)) {
|
|
7087
|
-
const decl = config.files[name];
|
|
7088
|
-
if (decl.provider && decl.provider !== provider) continue;
|
|
7089
|
-
addNode({ type: "file", name, provider });
|
|
7090
|
-
}
|
|
7091
|
-
}
|
|
7092
|
-
if (config.identities && isSupported(caps, "identity")) {
|
|
7093
|
-
for (const name of Object.keys(config.identities)) {
|
|
7094
|
-
const decl = config.identities[name];
|
|
7095
|
-
if (decl.provider && decl.provider !== provider) continue;
|
|
7096
|
-
addNode({ type: "identity", name, provider });
|
|
7097
|
-
}
|
|
7098
|
-
}
|
|
7099
|
-
if (config.agents) {
|
|
7100
|
-
for (const name of Object.keys(config.agents)) {
|
|
7101
|
-
const decl = config.agents[name];
|
|
7102
|
-
if (decl.provider && decl.provider !== provider) continue;
|
|
7103
|
-
const materialization = resolveAgentMaterialization(provider, decl);
|
|
7104
|
-
const agentAddr = { type: materialization.resourceType, name, provider };
|
|
7105
|
-
addNode(agentAddr);
|
|
7106
|
-
if (decl.environment && config.environments?.[decl.environment]) {
|
|
7107
|
-
const envAddr = {
|
|
7108
|
-
type: "environment",
|
|
7109
|
-
name: decl.environment,
|
|
7110
|
-
provider
|
|
7111
|
-
};
|
|
7112
|
-
if (nodes.has(addressKey(envAddr))) {
|
|
7113
|
-
addEdge(agentAddr, envAddr);
|
|
7114
|
-
}
|
|
7115
|
-
}
|
|
7116
|
-
if (decl.skills) {
|
|
7117
|
-
for (const skillRef of decl.skills) {
|
|
7118
|
-
if (typeof skillRef !== "string") continue;
|
|
7119
|
-
const skillName = skillRef;
|
|
7120
|
-
const skillAddr = { type: "skill", name: skillName, provider };
|
|
7121
|
-
if (nodes.has(addressKey(skillAddr))) {
|
|
7122
|
-
addEdge(agentAddr, skillAddr);
|
|
7123
|
-
}
|
|
7124
|
-
}
|
|
7125
|
-
}
|
|
7126
|
-
if (decl.vault) {
|
|
7127
|
-
const vaultAddr = { type: "vault", name: decl.vault, provider };
|
|
7128
|
-
if (nodes.has(addressKey(vaultAddr))) {
|
|
7129
|
-
addEdge(agentAddr, vaultAddr);
|
|
7130
|
-
}
|
|
7131
|
-
}
|
|
7132
|
-
if (decl.memory_stores) {
|
|
7133
|
-
for (const msName of decl.memory_stores) {
|
|
7134
|
-
const msAddr = {
|
|
7135
|
-
type: "memory_store",
|
|
7136
|
-
name: msName,
|
|
7137
|
-
provider
|
|
7138
|
-
};
|
|
7139
|
-
if (nodes.has(addressKey(msAddr))) {
|
|
7140
|
-
addEdge(agentAddr, msAddr);
|
|
7141
|
-
}
|
|
7142
|
-
}
|
|
7143
|
-
}
|
|
7144
|
-
if (decl.multiagent && isSupported(caps, "multiagent")) {
|
|
7145
|
-
for (const subName of decl.multiagent.agents) {
|
|
7146
|
-
const subDecl = config.agents[subName];
|
|
7147
|
-
const subType = subDecl ? resolveAgentMaterialization(provider, subDecl).resourceType : "agent";
|
|
7148
|
-
const subAddr = { type: subType, name: subName, provider };
|
|
7149
|
-
addEdge(agentAddr, subAddr);
|
|
7150
|
-
}
|
|
7151
|
-
}
|
|
7152
|
-
}
|
|
7153
|
-
}
|
|
7154
|
-
if (config.deployments) {
|
|
7155
|
-
for (const name of Object.keys(config.deployments)) {
|
|
7156
|
-
const decl = config.deployments[name];
|
|
7157
|
-
if (decl.provider && decl.provider !== provider) continue;
|
|
7158
|
-
const depAddr = { type: "deployment", name, provider };
|
|
7159
|
-
addNode(depAddr);
|
|
7160
|
-
const agentDecl = config.agents?.[decl.agent];
|
|
7161
|
-
const agentType = agentDecl ? resolveAgentMaterialization(provider, agentDecl).resourceType : "agent";
|
|
7162
|
-
const agentAddr = { type: agentType, name: decl.agent, provider };
|
|
7163
|
-
if (nodes.has(addressKey(agentAddr))) addEdge(depAddr, agentAddr);
|
|
7164
|
-
if (decl.environment) {
|
|
7165
|
-
const envAddr = { type: "environment", name: decl.environment, provider };
|
|
7166
|
-
if (nodes.has(addressKey(envAddr))) addEdge(depAddr, envAddr);
|
|
7167
|
-
}
|
|
7168
|
-
if (decl.vaults) {
|
|
7169
|
-
for (const vName of decl.vaults) {
|
|
7170
|
-
const vAddr = { type: "vault", name: vName, provider };
|
|
7171
|
-
if (nodes.has(addressKey(vAddr))) addEdge(depAddr, vAddr);
|
|
7172
|
-
}
|
|
7173
|
-
}
|
|
7174
|
-
if (decl.memory_stores) {
|
|
7175
|
-
for (const msName of decl.memory_stores) {
|
|
7176
|
-
const msAddr = { type: "memory_store", name: msName, provider };
|
|
7177
|
-
if (nodes.has(addressKey(msAddr))) addEdge(depAddr, msAddr);
|
|
7178
|
-
}
|
|
7179
|
-
}
|
|
7180
|
-
if (decl.resources) {
|
|
7181
|
-
for (const r of decl.resources) {
|
|
7182
|
-
if (r.type === "memory_store") {
|
|
7183
|
-
const msAddr = { type: "memory_store", name: r.memory_store, provider };
|
|
7184
|
-
if (nodes.has(addressKey(msAddr))) addEdge(depAddr, msAddr);
|
|
7185
|
-
}
|
|
7186
|
-
}
|
|
8218
|
+
addr
|
|
8219
|
+
);
|
|
7187
8220
|
}
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
const channelAddr = { type: "channel", name, provider };
|
|
7195
|
-
addNode(channelAddr);
|
|
7196
|
-
const agentDecl = config.agents?.[decl.agent];
|
|
7197
|
-
const agentType = agentDecl ? resolveAgentMaterialization(provider, agentDecl).resourceType : "agent";
|
|
7198
|
-
const agentAddr = { type: agentType, name: decl.agent, provider };
|
|
7199
|
-
if (nodes.has(addressKey(agentAddr))) addEdge(channelAddr, agentAddr);
|
|
7200
|
-
const identityName = decl.identity ?? config.defaults?.identity;
|
|
7201
|
-
if (identityName) {
|
|
7202
|
-
const identityAddr = { type: "identity", name: identityName, provider };
|
|
7203
|
-
if (nodes.has(addressKey(identityAddr))) addEdge(channelAddr, identityAddr);
|
|
8221
|
+
if (dep.resources?.some((r) => r.type === "memory_store" && (r.access === "read_only" || r.instructions))) {
|
|
8222
|
+
diagnostics.warning(
|
|
8223
|
+
`${providerName}.deployment.memory_store_options_unsupported`,
|
|
8224
|
+
"Per-resource memory_store access/instructions are not honored on this provider; the store is attached with default access.",
|
|
8225
|
+
addr
|
|
8226
|
+
);
|
|
7204
8227
|
}
|
|
7205
8228
|
}
|
|
7206
8229
|
}
|
|
7207
8230
|
}
|
|
7208
|
-
return { nodes, edges };
|
|
7209
|
-
}
|
|
7210
|
-
function topologicalSort(graph) {
|
|
7211
|
-
const visited = /* @__PURE__ */ new Set();
|
|
7212
|
-
const sorted = [];
|
|
7213
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
7214
|
-
function visit(key) {
|
|
7215
|
-
if (visited.has(key)) return;
|
|
7216
|
-
if (visiting.has(key)) {
|
|
7217
|
-
throw new UserError(`Circular dependency detected involving: ${key}`);
|
|
7218
|
-
}
|
|
7219
|
-
visiting.add(key);
|
|
7220
|
-
const deps = graph.edges.get(key) ?? /* @__PURE__ */ new Set();
|
|
7221
|
-
for (const dep of deps) {
|
|
7222
|
-
visit(dep);
|
|
7223
|
-
}
|
|
7224
|
-
visiting.delete(key);
|
|
7225
|
-
visited.add(key);
|
|
7226
|
-
sorted.push(graph.nodes.get(key));
|
|
7227
|
-
}
|
|
7228
|
-
for (const key of graph.nodes.keys()) {
|
|
7229
|
-
visit(key);
|
|
7230
|
-
}
|
|
7231
|
-
return sorted;
|
|
7232
8231
|
}
|
|
7233
8232
|
|
|
7234
8233
|
// src/internal/planner/planner.ts
|
|
7235
8234
|
async function buildPlan(config, state, options = {}) {
|
|
8235
|
+
const scopedConfig = options.resourceAddresses ? selectProjectConfig(config, options.resourceAddresses) : config;
|
|
8236
|
+
const resourceKeys = options.resourceAddresses ? new Set(options.resourceAddresses.map(addressKey)) : void 0;
|
|
8237
|
+
const scopedState = resourceKeys ? { ...state, resources: state.resources.filter((resource) => resourceKeys.has(addressKey(resource.address))) } : state;
|
|
7236
8238
|
const diagnostics = new DiagnosticCollector();
|
|
7237
8239
|
const actions = [];
|
|
7238
|
-
const targetProviders = options.providers ?? resolveTargetProviders(
|
|
7239
|
-
collectReferenceDiagnostics(
|
|
7240
|
-
collectProviderCapabilities(
|
|
7241
|
-
const graph = buildDependencyGraph(
|
|
8240
|
+
const targetProviders = options.providers ?? resolveTargetProviders(scopedConfig);
|
|
8241
|
+
collectReferenceDiagnostics(scopedConfig, diagnostics);
|
|
8242
|
+
collectProviderCapabilities(scopedConfig, targetProviders, diagnostics);
|
|
8243
|
+
const graph = buildDependencyGraph(scopedConfig, targetProviders);
|
|
7242
8244
|
const sorted = topologicalSort(graph);
|
|
7243
8245
|
const stateIndex = /* @__PURE__ */ new Map();
|
|
7244
|
-
for (const res of
|
|
8246
|
+
for (const res of scopedState.resources) {
|
|
7245
8247
|
stateIndex.set(addressKey(res.address), res);
|
|
7246
8248
|
}
|
|
7247
8249
|
const remoteIdLookup = /* @__PURE__ */ new Map();
|
|
7248
|
-
for (const res of
|
|
8250
|
+
for (const res of scopedState.resources) {
|
|
7249
8251
|
remoteIdLookup.set(addressKey(res.address), res);
|
|
7250
8252
|
}
|
|
7251
8253
|
const hashStateLookup = { getResource: (addr) => remoteIdLookup.get(addressKey(addr)) };
|
|
@@ -7254,6 +8256,7 @@ async function buildPlan(config, state, options = {}) {
|
|
|
7254
8256
|
const desiredHash = await computeResourceHash(address, config, options.configPath, hashStateLookup);
|
|
7255
8257
|
const existing = stateIndex.get(key);
|
|
7256
8258
|
const deps = getDependencies(address, graph);
|
|
8259
|
+
const needsNativeDeploymentMaterialization = address.type === "deployment" && existing?.remote_id === null && getProvider(address.provider)?.capabilities.deployment.tier === "native";
|
|
7257
8260
|
if (address.type === "environment" && existing) {
|
|
7258
8261
|
const envDecl = config.environments?.[address.name];
|
|
7259
8262
|
if (existing.externally_managed && envDecl && !envDecl.environment_id) {
|
|
@@ -7305,6 +8308,17 @@ async function buildPlan(config, state, options = {}) {
|
|
|
7305
8308
|
after: { content_hash: desiredHash },
|
|
7306
8309
|
dependencies: deps
|
|
7307
8310
|
});
|
|
8311
|
+
} else if (needsNativeDeploymentMaterialization) {
|
|
8312
|
+
actions.push({
|
|
8313
|
+
action: "update",
|
|
8314
|
+
address,
|
|
8315
|
+
driftKind: "none",
|
|
8316
|
+
readinessImpact: "blocking",
|
|
8317
|
+
reason: "Materialize legacy state as a native deployment",
|
|
8318
|
+
before: { content_hash: existing.desired_hash ?? existing.content_hash },
|
|
8319
|
+
after: { content_hash: desiredHash },
|
|
8320
|
+
dependencies: deps
|
|
8321
|
+
});
|
|
7308
8322
|
} else if ((existing.desired_hash ?? existing.content_hash) !== desiredHash && existing.drift_status === "drifted") {
|
|
7309
8323
|
const changedPaths = collectChangedPaths(address, config, existing, true);
|
|
7310
8324
|
actions.push({
|
|
@@ -7377,8 +8391,106 @@ async function buildPlan(config, state, options = {}) {
|
|
|
7377
8391
|
dependencies: replacement ? [replacement] : []
|
|
7378
8392
|
});
|
|
7379
8393
|
}
|
|
8394
|
+
coalesceChannelRenames(actions, scopedConfig, scopedState);
|
|
7380
8395
|
return { actions, diagnostics: diagnostics.getAll() };
|
|
7381
8396
|
}
|
|
8397
|
+
function selectProjectConfig(config, addresses) {
|
|
8398
|
+
const providers = new Set(addresses.map((address) => address.provider));
|
|
8399
|
+
const namesByType = /* @__PURE__ */ new Map();
|
|
8400
|
+
for (const address of addresses) {
|
|
8401
|
+
const names = namesByType.get(address.type) ?? /* @__PURE__ */ new Set();
|
|
8402
|
+
names.add(address.name);
|
|
8403
|
+
namesByType.set(address.type, names);
|
|
8404
|
+
}
|
|
8405
|
+
const pick = (record, names) => {
|
|
8406
|
+
if (!record || !names?.size) return void 0;
|
|
8407
|
+
const selected = Object.fromEntries(Object.entries(record).filter(([name]) => names.has(name)));
|
|
8408
|
+
return Object.keys(selected).length > 0 ? selected : void 0;
|
|
8409
|
+
};
|
|
8410
|
+
const agentNames = /* @__PURE__ */ new Set([...namesByType.get("agent") ?? [], ...namesByType.get("template") ?? []]);
|
|
8411
|
+
const selectedAgents = pick(config.agents, agentNames);
|
|
8412
|
+
const tunnelNames = /* @__PURE__ */ new Set();
|
|
8413
|
+
for (const agent of Object.values(selectedAgents ?? {})) {
|
|
8414
|
+
if (agent.tunnel) tunnelNames.add(agent.tunnel);
|
|
8415
|
+
}
|
|
8416
|
+
for (const deployment of Object.values(pick(config.deployments, namesByType.get("deployment")) ?? {})) {
|
|
8417
|
+
if (deployment.tunnel) tunnelNames.add(deployment.tunnel);
|
|
8418
|
+
}
|
|
8419
|
+
const identityNames = namesByType.get("identity");
|
|
8420
|
+
const selectedProviders = Object.fromEntries(
|
|
8421
|
+
Object.entries(config.providers).filter(([providerName]) => providers.has(providerName))
|
|
8422
|
+
);
|
|
8423
|
+
const defaultProvider = providers.size === 1 ? [...providers][0] : config.defaults?.provider;
|
|
8424
|
+
const defaultIdentity = config.defaults?.identity && identityNames?.has(config.defaults.identity) ? config.defaults.identity : void 0;
|
|
8425
|
+
return {
|
|
8426
|
+
...config,
|
|
8427
|
+
providers: selectedProviders,
|
|
8428
|
+
defaults: defaultProvider || defaultIdentity ? { provider: defaultProvider, identity: defaultIdentity } : void 0,
|
|
8429
|
+
environments: pick(config.environments, namesByType.get("environment")),
|
|
8430
|
+
tunnels: pick(config.tunnels, tunnelNames),
|
|
8431
|
+
vaults: pick(config.vaults, namesByType.get("vault")),
|
|
8432
|
+
memory_stores: pick(config.memory_stores, namesByType.get("memory_store")),
|
|
8433
|
+
skills: pick(config.skills, namesByType.get("skill")),
|
|
8434
|
+
files: pick(config.files, namesByType.get("file")),
|
|
8435
|
+
identities: pick(config.identities, identityNames),
|
|
8436
|
+
agents: selectedAgents,
|
|
8437
|
+
channels: pick(config.channels, namesByType.get("channel")),
|
|
8438
|
+
deployments: pick(config.deployments, namesByType.get("deployment"))
|
|
8439
|
+
};
|
|
8440
|
+
}
|
|
8441
|
+
function coalesceChannelRenames(actions, config, state) {
|
|
8442
|
+
const creates = actions.filter((action) => action.action === "create" && action.address.type === "channel");
|
|
8443
|
+
const deletes = actions.filter((action) => action.action === "delete" && action.address.type === "channel");
|
|
8444
|
+
const stateByAddress = new Map(state.resources.map((resource) => [addressKey(resource.address), resource]));
|
|
8445
|
+
const matchedDeletes = /* @__PURE__ */ new Set();
|
|
8446
|
+
for (const create of creates) {
|
|
8447
|
+
const desiredType = config.channels?.[create.address.name]?.type;
|
|
8448
|
+
if (!desiredType) continue;
|
|
8449
|
+
const desiredFingerprint = computeReplacementFingerprint(create.address, config);
|
|
8450
|
+
const candidates = deletes.filter((deletion2) => {
|
|
8451
|
+
if (matchedDeletes.has(deletion2) || deletion2.address.provider !== create.address.provider) return false;
|
|
8452
|
+
const prior2 = stateByAddress.get(addressKey(deletion2.address));
|
|
8453
|
+
const snapshot = prior2?.remote_snapshot;
|
|
8454
|
+
if (snapshot?.channel_type !== desiredType) return false;
|
|
8455
|
+
return !prior2?.replacement_fingerprint || prior2.replacement_fingerprint === desiredFingerprint;
|
|
8456
|
+
});
|
|
8457
|
+
if (candidates.length !== 1) continue;
|
|
8458
|
+
const deletion = candidates[0];
|
|
8459
|
+
const prior = stateByAddress.get(addressKey(deletion.address));
|
|
8460
|
+
const competingCreates = creates.filter(
|
|
8461
|
+
(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)
|
|
8462
|
+
);
|
|
8463
|
+
if (competingCreates.length > 0) continue;
|
|
8464
|
+
create.action = "update";
|
|
8465
|
+
create.previousAddress = deletion.address;
|
|
8466
|
+
create.before = deletion.before;
|
|
8467
|
+
create.driftKind = "local";
|
|
8468
|
+
create.reason = `Channel key renamed from '${deletion.address.name}' (remote resource retained)`;
|
|
8469
|
+
protectRenamedChannelDependencies(actions, stateByAddress, deletion, create);
|
|
8470
|
+
matchedDeletes.add(deletion);
|
|
8471
|
+
}
|
|
8472
|
+
for (let index = actions.length - 1; index >= 0; index--) {
|
|
8473
|
+
if (matchedDeletes.has(actions[index])) actions.splice(index, 1);
|
|
8474
|
+
}
|
|
8475
|
+
}
|
|
8476
|
+
function protectRenamedChannelDependencies(actions, stateByAddress, deletion, replacement) {
|
|
8477
|
+
const prior = stateByAddress.get(addressKey(deletion.address));
|
|
8478
|
+
const snapshot = prior?.remote_snapshot;
|
|
8479
|
+
const referencedIds = new Set(
|
|
8480
|
+
[snapshot?.identity_id, snapshot?.template_id].filter((id) => typeof id === "string")
|
|
8481
|
+
);
|
|
8482
|
+
if (referencedIds.size === 0) return;
|
|
8483
|
+
for (const action of actions) {
|
|
8484
|
+
if (action.action !== "delete" || action.address.type !== "identity" && action.address.type !== "template" || action.address.provider !== replacement.address.provider) {
|
|
8485
|
+
continue;
|
|
8486
|
+
}
|
|
8487
|
+
const dependency = stateByAddress.get(addressKey(action.address));
|
|
8488
|
+
if (!dependency?.remote_id || !referencedIds.has(dependency.remote_id)) continue;
|
|
8489
|
+
if (!action.dependencies.some((address) => addressKey(address) === addressKey(replacement.address))) {
|
|
8490
|
+
action.dependencies.push(replacement.address);
|
|
8491
|
+
}
|
|
8492
|
+
}
|
|
8493
|
+
}
|
|
7382
8494
|
function deliveryReplacementAddress(address, graph) {
|
|
7383
8495
|
if (address.type !== "agent" && address.type !== "template") return void 0;
|
|
7384
8496
|
const replacementType = address.type === "agent" ? "template" : "agent";
|
|
@@ -7405,6 +8517,9 @@ async function refreshState(state, providers, options = {}) {
|
|
|
7405
8517
|
const errors = [];
|
|
7406
8518
|
let dirty = false;
|
|
7407
8519
|
for (const res of resources) {
|
|
8520
|
+
if (options.resourceKeys && !options.resourceKeys.has(addressKey(res.address))) {
|
|
8521
|
+
continue;
|
|
8522
|
+
}
|
|
7408
8523
|
if (options.targetProviders && !options.targetProviders.includes(res.address.provider)) {
|
|
7409
8524
|
continue;
|
|
7410
8525
|
}
|
|
@@ -7467,7 +8582,7 @@ async function refreshState(state, providers, options = {}) {
|
|
|
7467
8582
|
dirty = true;
|
|
7468
8583
|
continue;
|
|
7469
8584
|
}
|
|
7470
|
-
const remote = await provider.findResource(res.address.type, res.address.name, res.remote_id);
|
|
8585
|
+
const remote = await provider.findResource(res.address.type, res.address.name, res.remote_id, res.api_mode);
|
|
7471
8586
|
if (!remote) {
|
|
7472
8587
|
if (!options.quiet) {
|
|
7473
8588
|
emitRuntimeFeedback(options.onFeedback, {
|
|
@@ -7518,12 +8633,19 @@ async function refreshState(state, providers, options = {}) {
|
|
|
7518
8633
|
}
|
|
7519
8634
|
|
|
7520
8635
|
// src/internal/core/resource-runtime.ts
|
|
8636
|
+
async function planProjectWithStateBackend(input, options = {}) {
|
|
8637
|
+
return readProjectRuntime(input, (ctx) => planProjectContext(ctx, options));
|
|
8638
|
+
}
|
|
7521
8639
|
async function syncProjectResourcesWithStateBackend(input, options = {}) {
|
|
7522
8640
|
return writeProjectRuntime(input, async (ctx) => {
|
|
7523
8641
|
const planned = await planProjectContext(ctx, options);
|
|
7524
8642
|
if (options.refreshOnly) {
|
|
7525
8643
|
return { planned };
|
|
7526
8644
|
}
|
|
8645
|
+
if (options.mode === "create-only") {
|
|
8646
|
+
const errorDiagnostic = planned.plan.diagnostics.find((diagnostic) => diagnostic.severity === "error");
|
|
8647
|
+
if (errorDiagnostic) throw new UserError(errorDiagnostic.message);
|
|
8648
|
+
}
|
|
7527
8649
|
return {
|
|
7528
8650
|
planned,
|
|
7529
8651
|
execution: await executePlannedProject(planned, {
|
|
@@ -7591,25 +8713,98 @@ async function importResource(ctx, address, remoteId, options = {}) {
|
|
|
7591
8713
|
return resource;
|
|
7592
8714
|
}
|
|
7593
8715
|
async function planProjectContext(ctx, options = {}) {
|
|
7594
|
-
|
|
8716
|
+
if (options.mode === "create-only" && !options.scope) {
|
|
8717
|
+
throw new UserError("Resource create-only mode requires an explicit resource scope.");
|
|
8718
|
+
}
|
|
8719
|
+
let targetProviders = resolveTargetProviders2(options.provider);
|
|
8720
|
+
if (!targetProviders && options.scope) {
|
|
8721
|
+
targetProviders = [...new Set(options.scope.roots.map((root) => root.provider))];
|
|
8722
|
+
}
|
|
8723
|
+
const selectedAddresses = options.scope ? resolvePlanScope(ctx, targetProviders ?? [], options.scope) : void 0;
|
|
8724
|
+
const resourceKeys = selectedAddresses ? new Set(selectedAddresses.map(addressKey)) : void 0;
|
|
7595
8725
|
const refreshResult = options.refresh !== false && ctx.state.listResources().length > 0 ? await refreshState(ctx.state, ctx.providers, {
|
|
7596
8726
|
targetProviders,
|
|
8727
|
+
resourceKeys,
|
|
7597
8728
|
config: ctx.config,
|
|
7598
8729
|
quiet: options.quiet ?? true,
|
|
7599
8730
|
onFeedback: options.onFeedback
|
|
7600
8731
|
}) : void 0;
|
|
7601
|
-
|
|
8732
|
+
let plan = await buildPlan(ctx.config, ctx.state.getStateFile(), {
|
|
7602
8733
|
providers: targetProviders,
|
|
7603
|
-
configPath: ctx.configPath
|
|
8734
|
+
configPath: ctx.configPath,
|
|
8735
|
+
resourceAddresses: selectedAddresses
|
|
7604
8736
|
});
|
|
8737
|
+
if (options.mode === "create-only" && options.scope) {
|
|
8738
|
+
plan = enforceCreateOnlyPlan(plan, options.scope, toResourceRefreshResult(refreshResult));
|
|
8739
|
+
}
|
|
7605
8740
|
return {
|
|
7606
8741
|
executionContext: ctx,
|
|
7607
8742
|
plan,
|
|
7608
8743
|
refreshResult: toResourceRefreshResult(refreshResult),
|
|
7609
8744
|
targetProviders,
|
|
7610
|
-
destructiveActions: selectDestructive(plan.actions)
|
|
8745
|
+
destructiveActions: selectDestructive(plan.actions),
|
|
8746
|
+
selectedAddresses,
|
|
8747
|
+
mode: options.mode
|
|
8748
|
+
};
|
|
8749
|
+
}
|
|
8750
|
+
function enforceCreateOnlyPlan(plan, scope, refreshResult) {
|
|
8751
|
+
const reasons = [];
|
|
8752
|
+
const rootKeys = new Set(scope.roots.map(addressKey));
|
|
8753
|
+
const refreshError = refreshResult?.errors[0];
|
|
8754
|
+
if (refreshError) {
|
|
8755
|
+
reasons.push(
|
|
8756
|
+
`Cannot verify scoped dependencies because refresh failed for ${addressKey(refreshError.resource.address)}: ${refreshError.error}`
|
|
8757
|
+
);
|
|
8758
|
+
}
|
|
8759
|
+
for (const root of scope.roots) {
|
|
8760
|
+
const rootKey = addressKey(root);
|
|
8761
|
+
const rootAction = plan.actions.find((action) => addressKey(action.address) === rootKey);
|
|
8762
|
+
if (!rootAction) {
|
|
8763
|
+
reasons.push(`Scoped plan did not contain target resource ${rootKey}.`);
|
|
8764
|
+
} else if (rootAction.action !== "create") {
|
|
8765
|
+
reasons.push(`Target resource ${rootKey} must be new, but the scoped plan requires '${rootAction.action}'.`);
|
|
8766
|
+
}
|
|
8767
|
+
}
|
|
8768
|
+
const dependencyChanges = plan.actions.filter(
|
|
8769
|
+
(action) => !rootKeys.has(addressKey(action.address)) && action.action !== "no-op"
|
|
8770
|
+
);
|
|
8771
|
+
if (dependencyChanges.length > 0) {
|
|
8772
|
+
const labels = dependencyChanges.map((action) => `${addressKey(action.address)} (${action.action})`).join(", ");
|
|
8773
|
+
reasons.push(`Create-only requires every scoped dependency to be up-to-date. Reconcile first: ${labels}.`);
|
|
8774
|
+
}
|
|
8775
|
+
if (reasons.length === 0) return plan;
|
|
8776
|
+
return {
|
|
8777
|
+
...plan,
|
|
8778
|
+
diagnostics: [
|
|
8779
|
+
...plan.diagnostics,
|
|
8780
|
+
{
|
|
8781
|
+
severity: "error",
|
|
8782
|
+
code: "resource.create_only.blocked",
|
|
8783
|
+
message: reasons.join(" "),
|
|
8784
|
+
resource: scope.roots[0]
|
|
8785
|
+
}
|
|
8786
|
+
]
|
|
7611
8787
|
};
|
|
7612
8788
|
}
|
|
8789
|
+
function resolvePlanScope(ctx, targetProviders, scope) {
|
|
8790
|
+
if (scope.roots.length === 0) {
|
|
8791
|
+
throw new UserError("Resource plan scope requires at least one root address.");
|
|
8792
|
+
}
|
|
8793
|
+
for (const root of scope.roots) {
|
|
8794
|
+
if (!targetProviders.includes(root.provider)) {
|
|
8795
|
+
throw new UserError(
|
|
8796
|
+
`Scoped resource ${addressKey(root)} is outside the selected provider set: ${targetProviders.join(", ")}.`
|
|
8797
|
+
);
|
|
8798
|
+
}
|
|
8799
|
+
}
|
|
8800
|
+
const graph = buildDependencyGraph(ctx.config, targetProviders);
|
|
8801
|
+
for (const root of scope.roots) {
|
|
8802
|
+
if (!graph.nodes.has(addressKey(root))) {
|
|
8803
|
+
throw new UserError(`Scoped resource ${addressKey(root)} is not declared in the project config.`);
|
|
8804
|
+
}
|
|
8805
|
+
}
|
|
8806
|
+
return scope.includeDependencies === false ? [...scope.roots] : collectDependencyClosure(graph, scope.roots);
|
|
8807
|
+
}
|
|
7613
8808
|
async function executePlannedProject(planned, options = {}) {
|
|
7614
8809
|
const decision = await decideDestructive(planned.destructiveActions, {
|
|
7615
8810
|
policy: options.policy,
|
|
@@ -7653,13 +8848,6 @@ async function decideDestructive(destructiveActions, options = {}) {
|
|
|
7653
8848
|
return "blocked";
|
|
7654
8849
|
}
|
|
7655
8850
|
}
|
|
7656
|
-
function replaceResourcePlan(planned, plan) {
|
|
7657
|
-
return {
|
|
7658
|
-
...planned,
|
|
7659
|
-
plan,
|
|
7660
|
-
destructiveActions: selectDestructive(plan.actions)
|
|
7661
|
-
};
|
|
7662
|
-
}
|
|
7663
8851
|
function toResourceRefreshResult(result) {
|
|
7664
8852
|
if (!result) return void 0;
|
|
7665
8853
|
return {
|
|
@@ -8008,6 +9196,354 @@ function buildDeploymentContext(ctx, name, provider) {
|
|
|
8008
9196
|
};
|
|
8009
9197
|
}
|
|
8010
9198
|
|
|
9199
|
+
// src/internal/core/managed-api-runtime.ts
|
|
9200
|
+
function resolveAdapter(ctx, provider) {
|
|
9201
|
+
if (provider) return getRuntimeProvider(ctx, provider);
|
|
9202
|
+
const configured = Array.from(ctx.providers.keys());
|
|
9203
|
+
if (configured.length === 1) return getRuntimeProvider(ctx, configured[0]);
|
|
9204
|
+
throw new UserError("Multiple providers configured. Use --provider to specify one.");
|
|
9205
|
+
}
|
|
9206
|
+
function requiredMethod(value, provider, operation) {
|
|
9207
|
+
if (value) return value;
|
|
9208
|
+
throw new UserError(`Provider '${provider}' does not support ${operation}.`);
|
|
9209
|
+
}
|
|
9210
|
+
async function listRemoteAgents(ctx, options = {}) {
|
|
9211
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9212
|
+
const method2 = requiredMethod(adapter2.listAgentResources, adapter2.name, "agent listing");
|
|
9213
|
+
return method2.call(adapter2, options);
|
|
9214
|
+
}
|
|
9215
|
+
async function getRemoteAgent(ctx, id, options = {}) {
|
|
9216
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9217
|
+
const method2 = requiredMethod(adapter2.getRemoteAgent, adapter2.name, "agent lookup");
|
|
9218
|
+
return method2.call(adapter2, id, options.version);
|
|
9219
|
+
}
|
|
9220
|
+
async function listRemoteAgentVersions(ctx, id, options = {}) {
|
|
9221
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9222
|
+
const method2 = requiredMethod(adapter2.listAgentVersions, adapter2.name, "agent version listing");
|
|
9223
|
+
return method2.call(adapter2, id, options);
|
|
9224
|
+
}
|
|
9225
|
+
async function listRemoteEnvironments(ctx, options = {}) {
|
|
9226
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9227
|
+
const method2 = requiredMethod(adapter2.listEnvironmentResources, adapter2.name, "environment listing");
|
|
9228
|
+
return method2.call(adapter2, options);
|
|
9229
|
+
}
|
|
9230
|
+
async function getRemoteEnvironment(ctx, id, options = {}) {
|
|
9231
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9232
|
+
const method2 = requiredMethod(adapter2.getRemoteEnvironment, adapter2.name, "environment lookup");
|
|
9233
|
+
return method2.call(adapter2, id);
|
|
9234
|
+
}
|
|
9235
|
+
async function listRemoteVaults(ctx, options = {}) {
|
|
9236
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9237
|
+
const method2 = requiredMethod(adapter2.listVaultResources, adapter2.name, "vault listing");
|
|
9238
|
+
return method2.call(adapter2, options);
|
|
9239
|
+
}
|
|
9240
|
+
async function getRemoteVault(ctx, id, options = {}) {
|
|
9241
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9242
|
+
const method2 = requiredMethod(adapter2.getRemoteVault, adapter2.name, "vault lookup");
|
|
9243
|
+
return method2.call(adapter2, id);
|
|
9244
|
+
}
|
|
9245
|
+
async function listRemoteSkills(ctx, options = {}) {
|
|
9246
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9247
|
+
const method2 = requiredMethod(adapter2.listSkillResources, adapter2.name, "skill listing");
|
|
9248
|
+
return method2.call(adapter2, options);
|
|
9249
|
+
}
|
|
9250
|
+
async function getRemoteSkill(ctx, id, options = {}) {
|
|
9251
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9252
|
+
const method2 = requiredMethod(adapter2.getSkillInfo, adapter2.name, "skill lookup");
|
|
9253
|
+
return method2.call(adapter2, id);
|
|
9254
|
+
}
|
|
9255
|
+
async function listRemoteSkillVersions(ctx, id, options = {}) {
|
|
9256
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9257
|
+
const method2 = requiredMethod(adapter2.listSkillVersions, adapter2.name, "skill version listing");
|
|
9258
|
+
return method2.call(adapter2, id, options);
|
|
9259
|
+
}
|
|
9260
|
+
async function getRemoteSkillVersion(ctx, id, version, options = {}) {
|
|
9261
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9262
|
+
const method2 = requiredMethod(adapter2.getSkillVersion, adapter2.name, "skill version lookup");
|
|
9263
|
+
return method2.call(adapter2, id, version);
|
|
9264
|
+
}
|
|
9265
|
+
async function getRemoteSkillDownloadInfo(ctx, id, version, options = {}) {
|
|
9266
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9267
|
+
const method2 = requiredMethod(adapter2.getSkillDownloadInfo, adapter2.name, "skill version download");
|
|
9268
|
+
return method2.call(adapter2, id, version);
|
|
9269
|
+
}
|
|
9270
|
+
async function downloadRemoteSkill(ctx, id, version, options = {}) {
|
|
9271
|
+
const info = await getRemoteSkillDownloadInfo(ctx, id, version, options);
|
|
9272
|
+
const response = await resolveFetch()(info.file_url, { method: "GET" });
|
|
9273
|
+
if (!response.ok) {
|
|
9274
|
+
throw new ApiError(response.status, await response.text(), "Skill download failed");
|
|
9275
|
+
}
|
|
9276
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
9277
|
+
}
|
|
9278
|
+
async function listRemoteFiles(ctx, options = {}) {
|
|
9279
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9280
|
+
const method2 = requiredMethod(adapter2.listFileResources, adapter2.name, "file listing");
|
|
9281
|
+
return method2.call(adapter2, options);
|
|
9282
|
+
}
|
|
9283
|
+
async function downloadRemoteFile(ctx, id, options = {}) {
|
|
9284
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9285
|
+
const method2 = requiredMethod(adapter2.downloadFileContent, adapter2.name, "file content download");
|
|
9286
|
+
return method2.call(adapter2, id);
|
|
9287
|
+
}
|
|
9288
|
+
async function updateRemoteSession(ctx, id, input, options = {}) {
|
|
9289
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9290
|
+
const method2 = requiredMethod(adapter2.updateSession, adapter2.name, "session update");
|
|
9291
|
+
return method2.call(adapter2, id, input);
|
|
9292
|
+
}
|
|
9293
|
+
async function archiveRemoteSession(ctx, id, options = {}) {
|
|
9294
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9295
|
+
const method2 = requiredMethod(adapter2.archiveSession, adapter2.name, "session archive");
|
|
9296
|
+
return method2.call(adapter2, id);
|
|
9297
|
+
}
|
|
9298
|
+
async function sendRemoteSessionEvents(ctx, id, events, options = {}) {
|
|
9299
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9300
|
+
const method2 = requiredMethod(adapter2.sendSessionEvents, adapter2.name, "generic session event sending");
|
|
9301
|
+
return method2.call(adapter2, id, events);
|
|
9302
|
+
}
|
|
9303
|
+
async function listRemoteDeployments(ctx, options = {}) {
|
|
9304
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9305
|
+
const method2 = requiredMethod(adapter2.listDeployments, adapter2.name, "deployment listing");
|
|
9306
|
+
return method2.call(adapter2, options);
|
|
9307
|
+
}
|
|
9308
|
+
async function getRemoteDeployment(ctx, id, options = {}) {
|
|
9309
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9310
|
+
const method2 = requiredMethod(adapter2.getDeploymentById, adapter2.name, "deployment lookup");
|
|
9311
|
+
return method2.call(adapter2, id);
|
|
9312
|
+
}
|
|
9313
|
+
async function runRemoteDeployment(ctx, id, options = {}) {
|
|
9314
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9315
|
+
const method2 = requiredMethod(adapter2.runDeploymentById, adapter2.name, "deployment run");
|
|
9316
|
+
return method2.call(adapter2, id);
|
|
9317
|
+
}
|
|
9318
|
+
async function setRemoteDeploymentPaused(ctx, id, paused, options = {}) {
|
|
9319
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9320
|
+
const method2 = paused ? adapter2.pauseDeploymentById : adapter2.unpauseDeploymentById;
|
|
9321
|
+
return requiredMethod(method2, adapter2.name, `${paused ? "pausing" : "unpausing"} deployments`).call(adapter2, id);
|
|
9322
|
+
}
|
|
9323
|
+
async function listRemoteDeploymentRuns(ctx, deploymentId, options = {}) {
|
|
9324
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9325
|
+
const method2 = requiredMethod(adapter2.listDeploymentRuns, adapter2.name, "deployment run listing");
|
|
9326
|
+
return method2.call(adapter2, deploymentId, options);
|
|
9327
|
+
}
|
|
9328
|
+
async function getRemoteDeploymentRun(ctx, runId, options = {}) {
|
|
9329
|
+
const adapter2 = resolveAdapter(ctx, options.provider);
|
|
9330
|
+
const method2 = requiredMethod(adapter2.getDeploymentRun, adapter2.name, "deployment run lookup");
|
|
9331
|
+
return method2.call(adapter2, runId);
|
|
9332
|
+
}
|
|
9333
|
+
var API_KEY = { supported: true, auth: "api_key" };
|
|
9334
|
+
var CLIENT_SEARCH = {
|
|
9335
|
+
supported: true,
|
|
9336
|
+
auth: "api_key",
|
|
9337
|
+
reason: "Client-side filtering over the corresponding list API."
|
|
9338
|
+
};
|
|
9339
|
+
var UNSUPPORTED_MODEL = {
|
|
9340
|
+
supported: false,
|
|
9341
|
+
auth: null,
|
|
9342
|
+
reason: "The public Managed Agents API does not expose a model catalog endpoint."
|
|
9343
|
+
};
|
|
9344
|
+
var UNSUPPORTED_THREAD = {
|
|
9345
|
+
supported: false,
|
|
9346
|
+
auth: null,
|
|
9347
|
+
reason: "Managed Agents exposes child-thread ids and lifecycle events through Session Events, but no independent Thread resource API."
|
|
9348
|
+
};
|
|
9349
|
+
var UNSUPPORTED_OAUTH = {
|
|
9350
|
+
supported: false,
|
|
9351
|
+
auth: null,
|
|
9352
|
+
reason: "The public Managed Agents data plane does not expose MCP OAuth login."
|
|
9353
|
+
};
|
|
9354
|
+
var BAILIAN_OPERATIONS = {
|
|
9355
|
+
"agent.create": { ...API_KEY, reason: "YAML declaration plus scoped create-only apply." },
|
|
9356
|
+
"agent.list": API_KEY,
|
|
9357
|
+
"agent.get": API_KEY,
|
|
9358
|
+
"agent.search": CLIENT_SEARCH,
|
|
9359
|
+
"agent.versions": API_KEY,
|
|
9360
|
+
"environment.create": { ...API_KEY, reason: "YAML declaration plus scoped create-only apply." },
|
|
9361
|
+
"environment.list": API_KEY,
|
|
9362
|
+
"environment.get": API_KEY,
|
|
9363
|
+
"environment.search": CLIENT_SEARCH,
|
|
9364
|
+
"skill.create": { ...API_KEY, reason: "YAML declaration plus scoped create-only apply." },
|
|
9365
|
+
"skill.list": API_KEY,
|
|
9366
|
+
"skill.get": API_KEY,
|
|
9367
|
+
"skill.search": CLIENT_SEARCH,
|
|
9368
|
+
"skill.versions": API_KEY,
|
|
9369
|
+
"skill.download": API_KEY,
|
|
9370
|
+
"vault.create": { ...API_KEY, reason: "YAML declaration plus scoped create-only apply." },
|
|
9371
|
+
"vault.credential.create": { ...API_KEY, reason: "YAML declaration plus scoped Vault transaction." },
|
|
9372
|
+
"vault.list": API_KEY,
|
|
9373
|
+
"vault.get": API_KEY,
|
|
9374
|
+
"vault.search": CLIENT_SEARCH,
|
|
9375
|
+
"deployment.create": { ...API_KEY, reason: "YAML declaration plus scoped create-only apply." },
|
|
9376
|
+
"deployment.list": API_KEY,
|
|
9377
|
+
"deployment.get": API_KEY,
|
|
9378
|
+
"deployment.search": { ...API_KEY, reason: "Maps to the server-side keyword parameter." },
|
|
9379
|
+
"deployment.runs.list": API_KEY,
|
|
9380
|
+
"deployment.runs.get": API_KEY,
|
|
9381
|
+
"deployment.run": API_KEY,
|
|
9382
|
+
"deployment.pause": API_KEY,
|
|
9383
|
+
"deployment.unpause": API_KEY,
|
|
9384
|
+
"session.create": API_KEY,
|
|
9385
|
+
"session.list": API_KEY,
|
|
9386
|
+
"session.get": API_KEY,
|
|
9387
|
+
"session.search": CLIENT_SEARCH,
|
|
9388
|
+
"session.update": API_KEY,
|
|
9389
|
+
"session.archive": API_KEY,
|
|
9390
|
+
"session.delete": API_KEY,
|
|
9391
|
+
"session.run": { ...API_KEY, reason: "Client composition of session create and event send/stream." },
|
|
9392
|
+
"session.event.send": API_KEY,
|
|
9393
|
+
"session.event.list": API_KEY,
|
|
9394
|
+
"session.event.stream": API_KEY,
|
|
9395
|
+
"session.debug": { ...API_KEY, reason: "Client-side aggregation of supported read APIs." },
|
|
9396
|
+
"session.export": { ...API_KEY, reason: "Client-side export of supported read APIs." },
|
|
9397
|
+
"file.upload": API_KEY,
|
|
9398
|
+
"file.list": API_KEY,
|
|
9399
|
+
"file.get": API_KEY,
|
|
9400
|
+
"file.search": CLIENT_SEARCH,
|
|
9401
|
+
"file.download": API_KEY,
|
|
9402
|
+
"file.delete": API_KEY,
|
|
9403
|
+
"model.list": UNSUPPORTED_MODEL,
|
|
9404
|
+
"model.search": UNSUPPORTED_MODEL,
|
|
9405
|
+
"session_thread.list": UNSUPPORTED_THREAD,
|
|
9406
|
+
"session_thread.get": UNSUPPORTED_THREAD,
|
|
9407
|
+
"session_thread.archive": UNSUPPORTED_THREAD,
|
|
9408
|
+
"session_thread.events": UNSUPPORTED_THREAD,
|
|
9409
|
+
"mcp.oauth_login": UNSUPPORTED_OAUTH
|
|
9410
|
+
};
|
|
9411
|
+
function getManagedAgentProviderCapabilities(provider) {
|
|
9412
|
+
if (provider === "bailian") {
|
|
9413
|
+
return { provider, operations: { ...BAILIAN_OPERATIONS } };
|
|
9414
|
+
}
|
|
9415
|
+
return {
|
|
9416
|
+
provider,
|
|
9417
|
+
operations: {
|
|
9418
|
+
"managed_agent.api": {
|
|
9419
|
+
supported: false,
|
|
9420
|
+
auth: null,
|
|
9421
|
+
reason: "The operation-level API command surface is currently implemented for Bailian only."
|
|
9422
|
+
}
|
|
9423
|
+
}
|
|
9424
|
+
};
|
|
9425
|
+
}
|
|
9426
|
+
|
|
9427
|
+
// src/internal/core/vault-credential-runtime.ts
|
|
9428
|
+
async function planVaultCredentialCreate(ctx, vaultName, credentialName, options = {}) {
|
|
9429
|
+
const prepared = await prepareVaultCredentialCreate(ctx, vaultName, credentialName, options);
|
|
9430
|
+
return {
|
|
9431
|
+
provider: prepared.provider,
|
|
9432
|
+
vault: vaultName,
|
|
9433
|
+
vaultRemoteId: prepared.vaultState.remote_id,
|
|
9434
|
+
credential: credentialName,
|
|
9435
|
+
remoteChecked: prepared.remoteChecked,
|
|
9436
|
+
reuseRemote: Boolean(prepared.remoteCredential),
|
|
9437
|
+
remoteCredentialId: prepared.remoteCredential?.id
|
|
9438
|
+
};
|
|
9439
|
+
}
|
|
9440
|
+
async function planVaultCredentialCreateWithStateBackend(input, vaultName, credentialName, options = {}) {
|
|
9441
|
+
return readProjectRuntime(input, (ctx) => planVaultCredentialCreate(ctx, vaultName, credentialName, options));
|
|
9442
|
+
}
|
|
9443
|
+
async function createVaultCredential(ctx, vaultName, credentialName, options = {}) {
|
|
9444
|
+
if (options.checkRemote === false) {
|
|
9445
|
+
throw new UserError("Vault Credential create requires the remote duplicate check.");
|
|
9446
|
+
}
|
|
9447
|
+
const prepared = await prepareVaultCredentialCreate(ctx, vaultName, credentialName, options);
|
|
9448
|
+
const remote = prepared.remoteCredential ?? await prepared.adapter.createCredential(prepared.vaultState.remote_id, prepared.credential);
|
|
9449
|
+
if (!remote.id) throw new UserError("Credential create returned no remote id.");
|
|
9450
|
+
const address = { type: "vault", name: vaultName, provider: prepared.provider };
|
|
9451
|
+
const desiredHash = await computeResourceHash(address, ctx.config, ctx.configPath, ctx.state);
|
|
9452
|
+
ctx.state.setResource({
|
|
9453
|
+
...prepared.vaultState,
|
|
9454
|
+
content_hash: desiredHash,
|
|
9455
|
+
desired_hash: desiredHash,
|
|
9456
|
+
drift_paths: [],
|
|
9457
|
+
drift_status: "unchecked"
|
|
9458
|
+
});
|
|
9459
|
+
await ctx.state.save();
|
|
9460
|
+
return {
|
|
9461
|
+
provider: prepared.provider,
|
|
9462
|
+
vault: vaultName,
|
|
9463
|
+
vaultRemoteId: prepared.vaultState.remote_id,
|
|
9464
|
+
credential: credentialName,
|
|
9465
|
+
remoteChecked: prepared.remoteChecked,
|
|
9466
|
+
credentialId: remote.id,
|
|
9467
|
+
reuseRemote: Boolean(prepared.remoteCredential),
|
|
9468
|
+
remoteCredentialId: prepared.remoteCredential?.id,
|
|
9469
|
+
adopted: Boolean(prepared.remoteCredential)
|
|
9470
|
+
};
|
|
9471
|
+
}
|
|
9472
|
+
async function createVaultCredentialWithStateBackend(input, vaultName, credentialName, options = {}) {
|
|
9473
|
+
return writeProjectRuntime(input, (ctx) => createVaultCredential(ctx, vaultName, credentialName, options));
|
|
9474
|
+
}
|
|
9475
|
+
async function prepareVaultCredentialCreate(ctx, vaultName, credentialName, options) {
|
|
9476
|
+
const vault = ctx.config.vaults?.[vaultName];
|
|
9477
|
+
if (!vault) throw new UserError(`Vault '${vaultName}' is not declared in the project config.`);
|
|
9478
|
+
const provider = resolveVaultProvider(ctx, vault.provider, options.provider);
|
|
9479
|
+
const credential = vault.credentials.at(-1);
|
|
9480
|
+
if (!credential || credential.name !== credentialName) {
|
|
9481
|
+
throw new UserError(`Credential '${credentialName}' must be the final newly appended entry in vault.${vaultName}.`);
|
|
9482
|
+
}
|
|
9483
|
+
if (vault.credentials.slice(0, -1).some((entry) => entry.name === credentialName)) {
|
|
9484
|
+
throw new UserError(`Vault '${vaultName}' already declares a credential named '${credentialName}'.`);
|
|
9485
|
+
}
|
|
9486
|
+
const address = { type: "vault", name: vaultName, provider };
|
|
9487
|
+
const priorConfig = structuredClone(ctx.config);
|
|
9488
|
+
priorConfig.vaults[vaultName] = {
|
|
9489
|
+
...priorConfig.vaults[vaultName],
|
|
9490
|
+
credentials: priorConfig.vaults[vaultName].credentials.slice(0, -1)
|
|
9491
|
+
};
|
|
9492
|
+
const priorPlan = await planProjectContext(
|
|
9493
|
+
{ ...ctx, config: priorConfig },
|
|
9494
|
+
{
|
|
9495
|
+
provider,
|
|
9496
|
+
scope: { roots: [address] },
|
|
9497
|
+
refresh: options.refresh,
|
|
9498
|
+
quiet: options.quiet ?? true
|
|
9499
|
+
}
|
|
9500
|
+
);
|
|
9501
|
+
const refreshError = priorPlan.refreshResult?.errors[0];
|
|
9502
|
+
if (refreshError) {
|
|
9503
|
+
throw new UserError(`Cannot verify vault.${vaultName} because refresh failed: ${refreshError.error}`);
|
|
9504
|
+
}
|
|
9505
|
+
const errorDiagnostic = priorPlan.plan.diagnostics.find((diagnostic) => diagnostic.severity === "error");
|
|
9506
|
+
if (errorDiagnostic) throw new UserError(errorDiagnostic.message);
|
|
9507
|
+
const vaultAction = priorPlan.plan.actions.find(
|
|
9508
|
+
(action) => action.address.type === "vault" && action.address.name === vaultName
|
|
9509
|
+
);
|
|
9510
|
+
if (vaultAction?.action !== "no-op") {
|
|
9511
|
+
throw new UserError(
|
|
9512
|
+
`vault.${vaultName} must be tracked and up-to-date before adding a credential; current action is '${vaultAction?.action ?? "missing"}'.`
|
|
9513
|
+
);
|
|
9514
|
+
}
|
|
9515
|
+
const vaultState = ctx.state.getResource(address);
|
|
9516
|
+
if (!vaultState?.remote_id) throw new UserError(`vault.${vaultName} has no tracked remote id.`);
|
|
9517
|
+
const adapter2 = getRuntimeProvider(ctx, provider);
|
|
9518
|
+
if (!adapter2.createCredential || !adapter2.listCredentials) {
|
|
9519
|
+
throw new UserError(`Provider '${provider}' does not support scoped Vault Credential create.`);
|
|
9520
|
+
}
|
|
9521
|
+
const remoteChecked = options.checkRemote !== false;
|
|
9522
|
+
const sameName = remoteChecked ? (await adapter2.listCredentials(vaultState.remote_id)).filter(
|
|
9523
|
+
(remoteCredential) => remoteCredential.display_name === credentialName
|
|
9524
|
+
) : [];
|
|
9525
|
+
const exact = sameName.find((remoteCredential) => credentialMatches(remoteCredential, credential));
|
|
9526
|
+
if (sameName.length > 0 && !exact) {
|
|
9527
|
+
throw new UserError(`Vault '${vaultName}' already has a different remote credential named '${credentialName}'.`);
|
|
9528
|
+
}
|
|
9529
|
+
return { provider, credential, vaultState, adapter: adapter2, remoteChecked, remoteCredential: exact };
|
|
9530
|
+
}
|
|
9531
|
+
function resolveVaultProvider(ctx, declaredProvider, requestedProvider) {
|
|
9532
|
+
if (requestedProvider && declaredProvider && requestedProvider !== declaredProvider) {
|
|
9533
|
+
throw new UserError(
|
|
9534
|
+
`Vault provider '${declaredProvider}' does not match requested provider '${requestedProvider}'.`
|
|
9535
|
+
);
|
|
9536
|
+
}
|
|
9537
|
+
const provider = requestedProvider ?? declaredProvider ?? ctx.config.defaults?.provider;
|
|
9538
|
+
if (provider && provider !== "all") return provider;
|
|
9539
|
+
const configured = [...ctx.providers.keys()];
|
|
9540
|
+
if (configured.length === 1) return configured[0];
|
|
9541
|
+
throw new UserError("Cannot infer one provider for Vault Credential create.");
|
|
9542
|
+
}
|
|
9543
|
+
function credentialMatches(remote, desired) {
|
|
9544
|
+
return remote.auth_type === desired.type && remote.secret_name === desired.secret_name && (remote.networking_type ?? "unrestricted") === (desired.networking?.type ?? "unrestricted");
|
|
9545
|
+
}
|
|
9546
|
+
|
|
8011
9547
|
// src/internal/core/destroy-runtime.ts
|
|
8012
9548
|
var destroyOrder = {
|
|
8013
9549
|
deployment: 0,
|
|
@@ -8025,12 +9561,64 @@ function planDestroyProjectContext(ctx) {
|
|
|
8025
9561
|
const resources = [...ctx.state.listResources()].sort(
|
|
8026
9562
|
(a, b) => (destroyOrder[a.address.type] ?? 99) - (destroyOrder[b.address.type] ?? 99)
|
|
8027
9563
|
);
|
|
8028
|
-
|
|
9564
|
+
const identityName = ctx.config.defaults?.identity;
|
|
9565
|
+
const defaultMemoryStores = [];
|
|
9566
|
+
for (const [agentName, agent] of Object.entries(ctx.config.agents ?? {})) {
|
|
9567
|
+
if (!agent.default_memory_store || agent.delivery?.qoder?.type !== "forward") continue;
|
|
9568
|
+
if (agent.provider && agent.provider !== "qoder") continue;
|
|
9569
|
+
const identityId = identityName ? ctx.state.getResource({ type: "identity", name: identityName, provider: "qoder" })?.remote_id ?? null : null;
|
|
9570
|
+
const templateId = ctx.state.getResource({ type: "template", name: agentName, provider: "qoder" })?.remote_id ?? null;
|
|
9571
|
+
if (!identityId && !templateId) continue;
|
|
9572
|
+
defaultMemoryStores.push({
|
|
9573
|
+
agentName,
|
|
9574
|
+
provider: "qoder",
|
|
9575
|
+
identityId,
|
|
9576
|
+
templateId,
|
|
9577
|
+
deleteOnDestroy: agent.default_memory_store.delete_on_destroy ?? false
|
|
9578
|
+
});
|
|
9579
|
+
}
|
|
9580
|
+
for (const pending of ctx.state.getStateFile().pending_default_memory_store_cleanups ?? []) {
|
|
9581
|
+
const existing = defaultMemoryStores.find(
|
|
9582
|
+
(item) => item.agentName === pending.agent_name && item.provider === pending.provider
|
|
9583
|
+
);
|
|
9584
|
+
if (existing) {
|
|
9585
|
+
existing.memoryStoreId = pending.remote_id;
|
|
9586
|
+
existing.deleteOnDestroy = true;
|
|
9587
|
+
} else {
|
|
9588
|
+
defaultMemoryStores.push({
|
|
9589
|
+
agentName: pending.agent_name,
|
|
9590
|
+
provider: pending.provider,
|
|
9591
|
+
identityId: pending.identity_id ?? null,
|
|
9592
|
+
templateId: pending.template_id ?? null,
|
|
9593
|
+
deleteOnDestroy: true,
|
|
9594
|
+
memoryStoreId: pending.remote_id
|
|
9595
|
+
});
|
|
9596
|
+
}
|
|
9597
|
+
}
|
|
9598
|
+
return { resources, defaultMemoryStores, executionContext: ctx };
|
|
8029
9599
|
}
|
|
8030
9600
|
async function destroyPlannedProjectResources(planned, options = {}) {
|
|
8031
9601
|
const results = [];
|
|
8032
|
-
|
|
9602
|
+
const capturedDefaults = await captureDefaultMemoryStores(planned);
|
|
8033
9603
|
const ctx = planned.executionContext;
|
|
9604
|
+
if (capturedDefaults.some((item) => item.error)) {
|
|
9605
|
+
const defaultMemoryStoreResults2 = capturedDefaults.map(preflightFailureResult);
|
|
9606
|
+
for (const resource of planned.resources) {
|
|
9607
|
+
const result = {
|
|
9608
|
+
resource,
|
|
9609
|
+
status: "blocked",
|
|
9610
|
+
reason: "skipped",
|
|
9611
|
+
error: "Destroy aborted because the default Memory Store preflight failed."
|
|
9612
|
+
};
|
|
9613
|
+
results.push(result);
|
|
9614
|
+
options.onResourceResult?.(result);
|
|
9615
|
+
}
|
|
9616
|
+
return { ...planned, results, defaultMemoryStoreResults: defaultMemoryStoreResults2, destroyed: 0, partial: true };
|
|
9617
|
+
}
|
|
9618
|
+
if (await persistCapturedDefaultMemoryStores(ctx, capturedDefaults)) {
|
|
9619
|
+
await ctx.state.save();
|
|
9620
|
+
}
|
|
9621
|
+
let stateChanged = false;
|
|
8034
9622
|
for (const resource of planned.resources) {
|
|
8035
9623
|
options.onResourceStart?.(resource);
|
|
8036
9624
|
const result = await destroyOneResource(ctx, resource, options);
|
|
@@ -8041,14 +9629,171 @@ async function destroyPlannedProjectResources(planned, options = {}) {
|
|
|
8041
9629
|
if (stateChanged) {
|
|
8042
9630
|
await ctx.state.save();
|
|
8043
9631
|
}
|
|
9632
|
+
const defaultMemoryStoreResults = await finalizeDefaultMemoryStores(planned, capturedDefaults, options);
|
|
9633
|
+
if (recordFailedDefaultMemoryStoreCleanups(ctx, defaultMemoryStoreResults)) {
|
|
9634
|
+
await ctx.state.save();
|
|
9635
|
+
}
|
|
9636
|
+
if (clearCompletedDefaultMemoryStoreCleanups(ctx, defaultMemoryStoreResults)) {
|
|
9637
|
+
await ctx.state.save();
|
|
9638
|
+
}
|
|
8044
9639
|
const destroyed = results.filter((result) => result.status === "success").length;
|
|
8045
9640
|
return {
|
|
8046
9641
|
...planned,
|
|
8047
9642
|
results,
|
|
9643
|
+
defaultMemoryStoreResults,
|
|
8048
9644
|
destroyed,
|
|
8049
|
-
partial: destroyed !== planned.resources.length
|
|
9645
|
+
partial: destroyed !== planned.resources.length || defaultMemoryStoreResults.some((result) => result.status === "failed")
|
|
9646
|
+
};
|
|
9647
|
+
}
|
|
9648
|
+
async function persistCapturedDefaultMemoryStores(ctx, captured) {
|
|
9649
|
+
const state = ctx.state.getStateFile();
|
|
9650
|
+
if (!state.pending_default_memory_store_cleanups) state.pending_default_memory_store_cleanups = [];
|
|
9651
|
+
const pending = state.pending_default_memory_store_cleanups;
|
|
9652
|
+
let changed = false;
|
|
9653
|
+
for (const item of captured) {
|
|
9654
|
+
if (!item.plan.deleteOnDestroy || !item.memoryStoreId) continue;
|
|
9655
|
+
if (pending.some((entry) => entry.provider === item.plan.provider && entry.remote_id === item.memoryStoreId))
|
|
9656
|
+
continue;
|
|
9657
|
+
pending.push({
|
|
9658
|
+
agent_name: item.plan.agentName,
|
|
9659
|
+
provider: item.plan.provider,
|
|
9660
|
+
remote_id: item.memoryStoreId,
|
|
9661
|
+
...item.plan.identityId ? { identity_id: item.plan.identityId } : {},
|
|
9662
|
+
...item.plan.templateId ? { template_id: item.plan.templateId } : {}
|
|
9663
|
+
});
|
|
9664
|
+
changed = true;
|
|
9665
|
+
}
|
|
9666
|
+
return changed;
|
|
9667
|
+
}
|
|
9668
|
+
function clearCompletedDefaultMemoryStoreCleanups(ctx, results) {
|
|
9669
|
+
const state = ctx.state.getStateFile();
|
|
9670
|
+
const completedIds = new Set(
|
|
9671
|
+
results.filter((item) => item.status === "deleted" || item.status === "already_gone").map((item) => item.memoryStoreId).filter((id) => Boolean(id))
|
|
9672
|
+
);
|
|
9673
|
+
if (completedIds.size === 0 || !state.pending_default_memory_store_cleanups?.length) return false;
|
|
9674
|
+
const remaining = state.pending_default_memory_store_cleanups.filter((item) => !completedIds.has(item.remote_id));
|
|
9675
|
+
if (remaining.length === state.pending_default_memory_store_cleanups.length) return false;
|
|
9676
|
+
state.pending_default_memory_store_cleanups = remaining;
|
|
9677
|
+
return true;
|
|
9678
|
+
}
|
|
9679
|
+
function recordFailedDefaultMemoryStoreCleanups(ctx, results) {
|
|
9680
|
+
const pending = ctx.state.getStateFile().pending_default_memory_store_cleanups;
|
|
9681
|
+
if (!pending?.length) return false;
|
|
9682
|
+
let changed = false;
|
|
9683
|
+
for (const result of results) {
|
|
9684
|
+
if (result.status !== "failed" || !result.memoryStoreId) continue;
|
|
9685
|
+
const entry = pending.find((item) => item.remote_id === result.memoryStoreId);
|
|
9686
|
+
if (entry && entry.last_error !== result.error) {
|
|
9687
|
+
entry.last_error = result.error;
|
|
9688
|
+
changed = true;
|
|
9689
|
+
}
|
|
9690
|
+
}
|
|
9691
|
+
return changed;
|
|
9692
|
+
}
|
|
9693
|
+
function preflightFailureResult(item) {
|
|
9694
|
+
if (!item.plan.deleteOnDestroy) return { ...item.plan, status: "retained" };
|
|
9695
|
+
return {
|
|
9696
|
+
...item.plan,
|
|
9697
|
+
status: "failed",
|
|
9698
|
+
...item.memoryStoreId ? { memoryStoreId: item.memoryStoreId } : {},
|
|
9699
|
+
error: item.error ?? "Destroy aborted because another default Memory Store preflight failed."
|
|
8050
9700
|
};
|
|
8051
9701
|
}
|
|
9702
|
+
async function captureDefaultMemoryStores(planned) {
|
|
9703
|
+
return Promise.all(
|
|
9704
|
+
planned.defaultMemoryStores.map(async (item) => {
|
|
9705
|
+
if (!item.deleteOnDestroy) return { plan: item };
|
|
9706
|
+
if (item.memoryStoreId) return { plan: item, memoryStoreId: item.memoryStoreId };
|
|
9707
|
+
if (!item.identityId || !item.templateId) {
|
|
9708
|
+
return { plan: item, error: "Cannot resolve the Identity and Template before destroy." };
|
|
9709
|
+
}
|
|
9710
|
+
try {
|
|
9711
|
+
const provider = getRuntimeProvider(planned.executionContext, item.provider);
|
|
9712
|
+
if (!provider.findDefaultMemoryStoreId || !provider.deleteDefaultMemoryStore) {
|
|
9713
|
+
return { plan: item, error: `Provider '${item.provider}' cannot delete a default memory store.` };
|
|
9714
|
+
}
|
|
9715
|
+
const memoryStoreId = await provider.findDefaultMemoryStoreId(item.identityId, item.templateId);
|
|
9716
|
+
return { plan: item, ...memoryStoreId ? { memoryStoreId } : {} };
|
|
9717
|
+
} catch (error) {
|
|
9718
|
+
return { plan: item, error: error instanceof Error ? error.message : String(error) };
|
|
9719
|
+
}
|
|
9720
|
+
})
|
|
9721
|
+
);
|
|
9722
|
+
}
|
|
9723
|
+
async function finalizeDefaultMemoryStores(planned, captured, options) {
|
|
9724
|
+
const output = [];
|
|
9725
|
+
for (const item of captured) {
|
|
9726
|
+
if (!item.plan.deleteOnDestroy) {
|
|
9727
|
+
output.push({ ...item.plan, status: "retained" });
|
|
9728
|
+
continue;
|
|
9729
|
+
}
|
|
9730
|
+
if (item.error) {
|
|
9731
|
+
output.push({ ...item.plan, status: "failed", error: item.error });
|
|
9732
|
+
continue;
|
|
9733
|
+
}
|
|
9734
|
+
if (!item.memoryStoreId) {
|
|
9735
|
+
output.push({ ...item.plan, status: "already_gone" });
|
|
9736
|
+
continue;
|
|
9737
|
+
}
|
|
9738
|
+
try {
|
|
9739
|
+
const provider = getRuntimeProvider(planned.executionContext, item.plan.provider);
|
|
9740
|
+
await deleteDefaultMemoryStoreWithArchiveFallback(
|
|
9741
|
+
provider,
|
|
9742
|
+
item.memoryStoreId,
|
|
9743
|
+
options.defaultMemoryStoreRetryDelaysMs ?? [1e3, 2e3, 4e3, 8e3]
|
|
9744
|
+
);
|
|
9745
|
+
output.push({ ...item.plan, status: "deleted", memoryStoreId: item.memoryStoreId });
|
|
9746
|
+
} catch (error) {
|
|
9747
|
+
if (ApiError.isNotFound(error)) {
|
|
9748
|
+
output.push({ ...item.plan, status: "already_gone", memoryStoreId: item.memoryStoreId });
|
|
9749
|
+
} else {
|
|
9750
|
+
output.push({
|
|
9751
|
+
...item.plan,
|
|
9752
|
+
status: "failed",
|
|
9753
|
+
memoryStoreId: item.memoryStoreId,
|
|
9754
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9755
|
+
});
|
|
9756
|
+
}
|
|
9757
|
+
}
|
|
9758
|
+
}
|
|
9759
|
+
return output;
|
|
9760
|
+
}
|
|
9761
|
+
async function deleteDefaultMemoryStoreWithArchiveFallback(provider, memoryStoreId, retryDelaysMs) {
|
|
9762
|
+
try {
|
|
9763
|
+
await retryDefaultMemoryStoreOperation(() => provider.deleteDefaultMemoryStore(memoryStoreId), retryDelaysMs);
|
|
9764
|
+
} catch (error) {
|
|
9765
|
+
if (isAlreadyArchivedConflict(error) && provider.deleteMemoryStore) {
|
|
9766
|
+
await retryDefaultMemoryStoreOperation(() => provider.deleteMemoryStore(memoryStoreId), retryDelaysMs);
|
|
9767
|
+
return;
|
|
9768
|
+
}
|
|
9769
|
+
if (!isStillMountedConflict(error) || !provider.archiveMemoryStore || !provider.deleteMemoryStore) throw error;
|
|
9770
|
+
await retryDefaultMemoryStoreOperation(() => provider.archiveMemoryStore(memoryStoreId), retryDelaysMs);
|
|
9771
|
+
await retryDefaultMemoryStoreOperation(() => provider.deleteMemoryStore(memoryStoreId), retryDelaysMs);
|
|
9772
|
+
}
|
|
9773
|
+
}
|
|
9774
|
+
async function retryDefaultMemoryStoreOperation(operation, delaysMs) {
|
|
9775
|
+
for (let attempt = 0; ; attempt++) {
|
|
9776
|
+
try {
|
|
9777
|
+
await operation();
|
|
9778
|
+
return;
|
|
9779
|
+
} catch (error) {
|
|
9780
|
+
if (!isRetryableDefaultMemoryStoreError(error) || attempt >= delaysMs.length) throw error;
|
|
9781
|
+
await delay(delaysMs[attempt]);
|
|
9782
|
+
}
|
|
9783
|
+
}
|
|
9784
|
+
}
|
|
9785
|
+
function isRetryableDefaultMemoryStoreError(error) {
|
|
9786
|
+
return error instanceof ApiError && (isStillMountedConflict(error) || isAlreadyArchivedConflict(error) || error.statusCode === 429 || [500, 502, 503].includes(error.statusCode));
|
|
9787
|
+
}
|
|
9788
|
+
function isStillMountedConflict(error) {
|
|
9789
|
+
return error instanceof ApiError && error.statusCode === 409 && error.responseBody.includes("still mounted");
|
|
9790
|
+
}
|
|
9791
|
+
function isAlreadyArchivedConflict(error) {
|
|
9792
|
+
return error instanceof ApiError && error.statusCode === 409 && error.responseBody.toLowerCase().includes("already archived");
|
|
9793
|
+
}
|
|
9794
|
+
function delay(ms) {
|
|
9795
|
+
return new Promise((resolve13) => setTimeout(resolve13, ms));
|
|
9796
|
+
}
|
|
8052
9797
|
async function destroyOneResource(ctx, resource, options) {
|
|
8053
9798
|
if (isExternalReference(ctx, resource)) {
|
|
8054
9799
|
ctx.state.removeResource(resource.address);
|
|
@@ -8069,8 +9814,18 @@ async function destroyOneResource(ctx, resource, options) {
|
|
|
8069
9814
|
ctx.state.removeResource(resource.address);
|
|
8070
9815
|
return successResult(resource, "destroyed");
|
|
8071
9816
|
}
|
|
9817
|
+
const apiMode = resource.api_mode === "auto" ? void 0 : resource.api_mode;
|
|
8072
9818
|
try {
|
|
8073
|
-
await deleteRemoteResource(
|
|
9819
|
+
await deleteRemoteResource(
|
|
9820
|
+
provider,
|
|
9821
|
+
resource.address.type,
|
|
9822
|
+
resource.remote_id,
|
|
9823
|
+
options.cascade,
|
|
9824
|
+
apiMode,
|
|
9825
|
+
ctx.state.listResources().filter(
|
|
9826
|
+
(candidate) => candidate.address.provider === resource.address.provider && candidate.address.type === "memory_store" && candidate.api_mode === "forward" && typeof candidate.remote_id === "string"
|
|
9827
|
+
).map((candidate) => candidate.remote_id)
|
|
9828
|
+
);
|
|
8074
9829
|
ctx.state.removeResource(resource.address);
|
|
8075
9830
|
emitRuntimeFeedback(options.onFeedback, {
|
|
8076
9831
|
type: "resource_action_success",
|
|
@@ -8099,7 +9854,7 @@ async function destroyOneResource(ctx, resource, options) {
|
|
|
8099
9854
|
};
|
|
8100
9855
|
if (await options.onCascadeRequired?.(blocked)) {
|
|
8101
9856
|
try {
|
|
8102
|
-
await provider.deleteEnvironment(resource.remote_id, true);
|
|
9857
|
+
await provider.deleteEnvironment(resource.remote_id, true, apiMode);
|
|
8103
9858
|
ctx.state.removeResource(resource.address);
|
|
8104
9859
|
return {
|
|
8105
9860
|
...successResult(resource, "destroyed"),
|
|
@@ -8135,29 +9890,29 @@ function failureResult(resource, error) {
|
|
|
8135
9890
|
error: error instanceof Error ? error.message : String(error)
|
|
8136
9891
|
};
|
|
8137
9892
|
}
|
|
8138
|
-
async function deleteRemoteResource(provider, type, id, cascade) {
|
|
9893
|
+
async function deleteRemoteResource(provider, type, id, cascade, mode, ownedMemoryStoreIds = []) {
|
|
8139
9894
|
switch (type) {
|
|
8140
9895
|
case "agent":
|
|
8141
9896
|
await provider.deleteAgent(id);
|
|
8142
9897
|
return;
|
|
8143
9898
|
case "template":
|
|
8144
9899
|
if (!provider.archiveTemplate) throw new UserError(`Provider does not support templates`);
|
|
8145
|
-
await provider.archiveTemplate(id);
|
|
9900
|
+
await provider.archiveTemplate(id, ownedMemoryStoreIds);
|
|
8146
9901
|
return;
|
|
8147
9902
|
case "skill":
|
|
8148
|
-
await provider.deleteSkill(id);
|
|
9903
|
+
await provider.deleteSkill(id, mode);
|
|
8149
9904
|
return;
|
|
8150
9905
|
case "memory_store":
|
|
8151
9906
|
if (!provider.deleteMemoryStore) {
|
|
8152
9907
|
throw new UserError(`Provider does not support memory stores`);
|
|
8153
9908
|
}
|
|
8154
|
-
await provider.deleteMemoryStore(id);
|
|
9909
|
+
await provider.deleteMemoryStore(id, mode);
|
|
8155
9910
|
return;
|
|
8156
9911
|
case "vault":
|
|
8157
|
-
await provider.deleteVault(id);
|
|
9912
|
+
await provider.deleteVault(id, mode);
|
|
8158
9913
|
return;
|
|
8159
9914
|
case "environment":
|
|
8160
|
-
await provider.deleteEnvironment(id, cascade);
|
|
9915
|
+
await provider.deleteEnvironment(id, cascade, mode);
|
|
8161
9916
|
return;
|
|
8162
9917
|
case "deployment":
|
|
8163
9918
|
await provider.deleteDeployment(id);
|
|
@@ -8171,7 +9926,7 @@ async function deleteRemoteResource(provider, type, id, cascade) {
|
|
|
8171
9926
|
await provider.deleteChannel(id);
|
|
8172
9927
|
return;
|
|
8173
9928
|
case "file":
|
|
8174
|
-
await provider.deleteFile(id);
|
|
9929
|
+
await provider.deleteFile(id, mode);
|
|
8175
9930
|
return;
|
|
8176
9931
|
}
|
|
8177
9932
|
}
|
|
@@ -8331,6 +10086,7 @@ function buildAgentDecl(base, input) {
|
|
|
8331
10086
|
});
|
|
8332
10087
|
const agent = {
|
|
8333
10088
|
...base ?? {},
|
|
10089
|
+
name: input.name ?? base?.name,
|
|
8334
10090
|
description: input.description ?? base?.description,
|
|
8335
10091
|
model,
|
|
8336
10092
|
instructions: input.instructions ?? base?.instructions ?? "",
|
|
@@ -8374,6 +10130,10 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
|
|
|
8374
10130
|
throw new UserError(`Agent '${agentName}' not found in config. Available agents: ${available || "(none)"}`);
|
|
8375
10131
|
}
|
|
8376
10132
|
const sessionResources = options.resources ?? agent.resources;
|
|
10133
|
+
const environmentVariables = options.environmentVariables ?? agent.environment_variables;
|
|
10134
|
+
if (environmentVariables && provider !== "qoder") {
|
|
10135
|
+
throw new UserError("Session environment variables are supported only by Qoder.");
|
|
10136
|
+
}
|
|
8377
10137
|
const providerFeatures = getProvider(provider)?.features;
|
|
8378
10138
|
for (const resource of sessionResources ?? []) {
|
|
8379
10139
|
if (!providerFeatures?.session_resources.includes(resource.type)) {
|
|
@@ -8402,7 +10162,8 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
|
|
|
8402
10162
|
identity_id: identityId,
|
|
8403
10163
|
files: (options.files ?? []).map((file) => ({ file_id: file.fileId, mount_path: file.mountPath })),
|
|
8404
10164
|
title: options.title,
|
|
8405
|
-
metadata: options.metadata
|
|
10165
|
+
metadata: options.metadata,
|
|
10166
|
+
environment_variables: environmentVariables
|
|
8406
10167
|
};
|
|
8407
10168
|
}
|
|
8408
10169
|
const agentId = requireRef(state, { type: "agent", name: agentName, provider });
|
|
@@ -8447,7 +10208,8 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
|
|
|
8447
10208
|
files: (options.files ?? []).map((f) => ({ file_id: f.fileId, mount_path: f.mountPath })),
|
|
8448
10209
|
resources: sessionResources,
|
|
8449
10210
|
title: options.title,
|
|
8450
|
-
metadata: options.metadata
|
|
10211
|
+
metadata: options.metadata,
|
|
10212
|
+
environment_variables: environmentVariables
|
|
8451
10213
|
};
|
|
8452
10214
|
}
|
|
8453
10215
|
function resolveTunnelId(agent, config, options, provider) {
|
|
@@ -8561,28 +10323,36 @@ async function listAgentsWithReadiness(ctx, options = {}) {
|
|
|
8561
10323
|
}
|
|
8562
10324
|
async function planAgentResources(ctx, agentId, options = {}) {
|
|
8563
10325
|
const agent = getAgent(ctx, agentId);
|
|
10326
|
+
const rootAddress = collectAgentAddresses(ctx.config, agent.agentName, agent.provider)[0];
|
|
8564
10327
|
const planned = await planProjectContext(ctx, {
|
|
8565
10328
|
provider: agent.provider,
|
|
10329
|
+
scope: { roots: [rootAddress] },
|
|
10330
|
+
mode: options.mode,
|
|
8566
10331
|
refresh: options.refresh,
|
|
8567
10332
|
quiet: options.quiet ?? true
|
|
8568
10333
|
});
|
|
8569
|
-
const actions =
|
|
10334
|
+
const actions = planned.plan.actions;
|
|
10335
|
+
const diagnostics = planned.plan.diagnostics;
|
|
8570
10336
|
return {
|
|
8571
10337
|
agentId,
|
|
8572
10338
|
provider: agent.provider,
|
|
8573
10339
|
actions,
|
|
8574
|
-
diagnostics
|
|
10340
|
+
diagnostics,
|
|
8575
10341
|
destructiveActions: selectDestructive(actions),
|
|
8576
10342
|
planned
|
|
8577
10343
|
};
|
|
8578
10344
|
}
|
|
10345
|
+
async function planAgentResourcesWithStateBackend(input, agentId, options = {}) {
|
|
10346
|
+
return readProjectRuntime(input, (ctx) => planAgentResources(ctx, agentId, options));
|
|
10347
|
+
}
|
|
8579
10348
|
async function syncAgentResources(ctx, agentId, options = {}) {
|
|
8580
10349
|
return runAgentSync(ctx, agentId, options);
|
|
8581
10350
|
}
|
|
8582
10351
|
async function runAgentSync(ctx, agentId, options) {
|
|
8583
10352
|
const fullPlan = await planAgentResources(ctx, agentId, {
|
|
8584
10353
|
refresh: options.refresh,
|
|
8585
|
-
quiet: options.quiet
|
|
10354
|
+
quiet: options.quiet,
|
|
10355
|
+
mode: options.mode
|
|
8586
10356
|
});
|
|
8587
10357
|
const actions = fullPlan.actions;
|
|
8588
10358
|
const destructiveActions = fullPlan.destructiveActions;
|
|
@@ -8616,11 +10386,7 @@ async function runAgentSync(ctx, agentId, options) {
|
|
|
8616
10386
|
};
|
|
8617
10387
|
}
|
|
8618
10388
|
try {
|
|
8619
|
-
const
|
|
8620
|
-
diagnostics: fullPlan.diagnostics,
|
|
8621
|
-
actions: scopePlanActions(fullPlan.planned.plan, actions)
|
|
8622
|
-
};
|
|
8623
|
-
const execution = await executePlannedProject(replaceResourcePlan(fullPlan.planned, scopedPlan), {
|
|
10389
|
+
const execution = await executePlannedProject(fullPlan.planned, {
|
|
8624
10390
|
policy: "force"
|
|
8625
10391
|
});
|
|
8626
10392
|
const results = toAgentSyncResults(execution);
|
|
@@ -8724,29 +10490,6 @@ function getAgentReadinessFromPlan(ctx, agentId, plan) {
|
|
|
8724
10490
|
plannedActions: relevantActions
|
|
8725
10491
|
};
|
|
8726
10492
|
}
|
|
8727
|
-
function filterAgentActions(ctx, agent, plan) {
|
|
8728
|
-
const relevantKeys = agentAddressKeys(ctx, agent);
|
|
8729
|
-
return plan.actions.filter(
|
|
8730
|
-
(action) => relevantKeys.has(addressKey(action.address)) || action.dependencies.some((dependency) => relevantKeys.has(addressKey(dependency)))
|
|
8731
|
-
);
|
|
8732
|
-
}
|
|
8733
|
-
function filterAgentDiagnostics(ctx, agent, plan) {
|
|
8734
|
-
const relevantKeys = agentAddressKeys(ctx, agent);
|
|
8735
|
-
return plan.diagnostics.filter(
|
|
8736
|
-
(diagnostic) => !diagnostic.resource || relevantKeys.has(addressKey(diagnostic.resource))
|
|
8737
|
-
);
|
|
8738
|
-
}
|
|
8739
|
-
function agentAddressKeys(ctx, agent) {
|
|
8740
|
-
return new Set(collectAgentAddresses(ctx.config, agent.agentName, agent.provider).map(addressKey));
|
|
8741
|
-
}
|
|
8742
|
-
function scopePlanActions(fullPlan, agentActions) {
|
|
8743
|
-
const allowedKeys = new Set(agentActions.filter((action) => action.action !== "no-op").map(actionKey));
|
|
8744
|
-
return fullPlan.actions.filter((action) => action.action === "no-op" || allowedKeys.has(actionKey(action)));
|
|
8745
|
-
}
|
|
8746
|
-
function actionKey(action) {
|
|
8747
|
-
const address = action.address;
|
|
8748
|
-
return `${action.action}:${address.provider}:${address.type}:${address.name}`;
|
|
8749
|
-
}
|
|
8750
10493
|
function isNonBlockingAgentDrift(action) {
|
|
8751
10494
|
if (action.action === "no-op") return true;
|
|
8752
10495
|
return action.readinessImpact === "non_blocking";
|
|
@@ -8758,33 +10501,13 @@ function collectAgentAddresses(config, agentName, provider) {
|
|
|
8758
10501
|
}
|
|
8759
10502
|
const resolvedProvider = provider ?? resolveSessionProvider(agentName, config, void 0);
|
|
8760
10503
|
const materialization = resolveAgentMaterialization(resolvedProvider, agent);
|
|
8761
|
-
const
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
provider: resolvedProvider
|
|
8769
|
-
});
|
|
8770
|
-
}
|
|
8771
|
-
if (agent.vault) {
|
|
8772
|
-
addresses.push({ type: "vault", name: agent.vault, provider: resolvedProvider });
|
|
8773
|
-
}
|
|
8774
|
-
for (const name of agent.memory_stores ?? []) {
|
|
8775
|
-
addresses.push({ type: "memory_store", name, provider: resolvedProvider });
|
|
8776
|
-
}
|
|
8777
|
-
for (const skill of agent.skills ?? []) {
|
|
8778
|
-
if (typeof skill === "string") {
|
|
8779
|
-
addresses.push({ type: "skill", name: skill, provider: resolvedProvider });
|
|
8780
|
-
}
|
|
8781
|
-
}
|
|
8782
|
-
for (const subAgent of agent.multiagent?.agents ?? []) {
|
|
8783
|
-
const subDecl = config.agents?.[subAgent];
|
|
8784
|
-
const subType = subDecl ? resolveAgentMaterialization(resolvedProvider, subDecl).resourceType : "agent";
|
|
8785
|
-
addresses.push({ type: subType, name: subAgent, provider: resolvedProvider });
|
|
8786
|
-
}
|
|
8787
|
-
return addresses;
|
|
10504
|
+
const rootAddress = {
|
|
10505
|
+
type: materialization.resourceType,
|
|
10506
|
+
name: agentName,
|
|
10507
|
+
provider: resolvedProvider
|
|
10508
|
+
};
|
|
10509
|
+
const graph = buildDependencyGraph(config, [resolvedProvider]);
|
|
10510
|
+
return collectDependencyClosure(graph, [rootAddress]);
|
|
8788
10511
|
}
|
|
8789
10512
|
function toAgentDefinition(config, agentName, agent) {
|
|
8790
10513
|
return {
|
|
@@ -8848,7 +10571,8 @@ async function createSessionForAgent(ctx, options = {}) {
|
|
|
8848
10571
|
files: options.files,
|
|
8849
10572
|
resources: options.resources,
|
|
8850
10573
|
title: options.title,
|
|
8851
|
-
metadata: options.metadata
|
|
10574
|
+
metadata: options.metadata,
|
|
10575
|
+
environmentVariables: options.environmentVariables
|
|
8852
10576
|
});
|
|
8853
10577
|
const session = await adapter2.createSession(bindings);
|
|
8854
10578
|
return { agentName, provider, session };
|
|
@@ -8867,7 +10591,8 @@ async function startSessionRun(ctx, prompt, options = {}) {
|
|
|
8867
10591
|
files: options.files,
|
|
8868
10592
|
resources: options.resources,
|
|
8869
10593
|
title: options.title,
|
|
8870
|
-
metadata: options.metadata
|
|
10594
|
+
metadata: options.metadata,
|
|
10595
|
+
environmentVariables: options.environmentVariables
|
|
8871
10596
|
});
|
|
8872
10597
|
const session = await adapter2.createSession(bindings);
|
|
8873
10598
|
return {
|
|
@@ -8928,7 +10653,7 @@ async function collectEventsUntilTerminal(adapter2, sessionId, options = {}) {
|
|
|
8928
10653
|
terminalStatus = terminalEvent.status;
|
|
8929
10654
|
break;
|
|
8930
10655
|
}
|
|
8931
|
-
await
|
|
10656
|
+
await delay2(currentIntervalMs);
|
|
8932
10657
|
currentIntervalMs = Math.min(currentIntervalMs * 2, maxPollIntervalMs);
|
|
8933
10658
|
}
|
|
8934
10659
|
} else {
|
|
@@ -8939,7 +10664,7 @@ async function collectEventsUntilTerminal(adapter2, sessionId, options = {}) {
|
|
|
8939
10664
|
terminalStatus = session.status;
|
|
8940
10665
|
break;
|
|
8941
10666
|
}
|
|
8942
|
-
await
|
|
10667
|
+
await delay2(currentIntervalMs);
|
|
8943
10668
|
currentIntervalMs = Math.min(currentIntervalMs * 2, maxPollIntervalMs);
|
|
8944
10669
|
}
|
|
8945
10670
|
result = await adapter2.listSessionEvents(sessionId, { limit: 100 });
|
|
@@ -9009,12 +10734,20 @@ async function listSessionEvents(ctx, sessionId, options = {}) {
|
|
|
9009
10734
|
const listOptions = {};
|
|
9010
10735
|
if (options.limit !== void 0) listOptions.limit = options.limit;
|
|
9011
10736
|
if (options.order !== void 0) listOptions.order = options.order;
|
|
10737
|
+
if (options.types !== void 0) listOptions.types = options.types;
|
|
10738
|
+
if (options.created_at_gt !== void 0) listOptions.created_at_gt = options.created_at_gt;
|
|
10739
|
+
if (options.created_at_gte !== void 0) listOptions.created_at_gte = options.created_at_gte;
|
|
10740
|
+
if (options.created_at_lt !== void 0) listOptions.created_at_lt = options.created_at_lt;
|
|
10741
|
+
if (options.created_at_lte !== void 0) listOptions.created_at_lte = options.created_at_lte;
|
|
9012
10742
|
const pageToken = options.page_token ?? options.page;
|
|
9013
10743
|
if (pageToken !== void 0) listOptions.page_token = pageToken;
|
|
9014
|
-
|
|
10744
|
+
const result = await resolveDirectAdapter(ctx, options.provider).listSessionEvents(
|
|
9015
10745
|
sessionId,
|
|
9016
10746
|
Object.keys(listOptions).length > 0 ? listOptions : void 0
|
|
9017
10747
|
);
|
|
10748
|
+
if (!options.types || options.types.length === 0) return result;
|
|
10749
|
+
const allowedTypes = new Set(options.types);
|
|
10750
|
+
return { ...result, events: result.events.filter((event) => allowedTypes.has(event.raw_type)) };
|
|
9018
10751
|
}
|
|
9019
10752
|
async function uploadFile(ctx, content, filename, options = {}) {
|
|
9020
10753
|
const adapter2 = resolveDirectAdapter(ctx, options.provider);
|
|
@@ -9064,12 +10797,89 @@ async function deleteSkill(ctx, id, options = {}) {
|
|
|
9064
10797
|
await resolveDirectAdapter(ctx, options.provider).deleteSkill(id);
|
|
9065
10798
|
}
|
|
9066
10799
|
function streamSessionEvents(ctx, sessionId, options = {}) {
|
|
10800
|
+
const adapter2 = resolveDirectAdapter(ctx, options.provider);
|
|
10801
|
+
if (options.after_id && !adapter2.eventResume) {
|
|
10802
|
+
return replaySessionEventsByPolling(adapter2, sessionId, options.after_id);
|
|
10803
|
+
}
|
|
9067
10804
|
const streamOptions = {};
|
|
9068
10805
|
if (options.after_id !== void 0) streamOptions.after_id = options.after_id;
|
|
9069
|
-
return
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
);
|
|
10806
|
+
return adapter2.streamSessionEvents(sessionId, Object.keys(streamOptions).length > 0 ? streamOptions : void 0);
|
|
10807
|
+
}
|
|
10808
|
+
async function* replaySessionEventsByPolling(adapter2, sessionId, afterId) {
|
|
10809
|
+
const history = await listAllSessionEventHistory(adapter2, sessionId);
|
|
10810
|
+
const markerIndex = history.findIndex((event) => event.id === afterId);
|
|
10811
|
+
if (markerIndex < 0) {
|
|
10812
|
+
throw new UserError(`Session event '${afterId}' was not found; list session events and choose a valid event id.`);
|
|
10813
|
+
}
|
|
10814
|
+
const knownEventKeys = new Set(history.slice(0, markerIndex + 1).map(sessionEventKey));
|
|
10815
|
+
for (const event of history.slice(markerIndex + 1)) {
|
|
10816
|
+
const eventKey = sessionEventKey(event);
|
|
10817
|
+
if (knownEventKeys.has(eventKey)) continue;
|
|
10818
|
+
knownEventKeys.add(eventKey);
|
|
10819
|
+
yield event;
|
|
10820
|
+
if (event.type === "status" && isTerminalSessionStatus(event.status)) return;
|
|
10821
|
+
}
|
|
10822
|
+
while (true) {
|
|
10823
|
+
const newEvents = await listNewSessionEvents(adapter2, sessionId, knownEventKeys);
|
|
10824
|
+
for (const event of newEvents) {
|
|
10825
|
+
const eventKey = sessionEventKey(event);
|
|
10826
|
+
if (knownEventKeys.has(eventKey)) continue;
|
|
10827
|
+
knownEventKeys.add(eventKey);
|
|
10828
|
+
yield event;
|
|
10829
|
+
if (event.type === "status" && isTerminalSessionStatus(event.status)) return;
|
|
10830
|
+
}
|
|
10831
|
+
const session = await adapter2.getSession(sessionId);
|
|
10832
|
+
if (isTerminalSessionStatus(session.status)) return;
|
|
10833
|
+
await delay2(DEFAULT_POLL_INTERVAL_MS);
|
|
10834
|
+
}
|
|
10835
|
+
}
|
|
10836
|
+
async function listAllSessionEventHistory(adapter2, sessionId) {
|
|
10837
|
+
const events = [];
|
|
10838
|
+
const seenPageTokens = /* @__PURE__ */ new Set();
|
|
10839
|
+
let pageToken;
|
|
10840
|
+
while (true) {
|
|
10841
|
+
const result = await adapter2.listSessionEvents(sessionId, {
|
|
10842
|
+
limit: 100,
|
|
10843
|
+
order: "asc",
|
|
10844
|
+
...pageToken ? { page_token: pageToken } : {}
|
|
10845
|
+
});
|
|
10846
|
+
events.push(...result.events);
|
|
10847
|
+
if (!result.next_page) return events;
|
|
10848
|
+
if (seenPageTokens.has(result.next_page)) {
|
|
10849
|
+
throw new UserError("Session event pagination returned a repeated cursor.");
|
|
10850
|
+
}
|
|
10851
|
+
seenPageTokens.add(result.next_page);
|
|
10852
|
+
pageToken = result.next_page;
|
|
10853
|
+
}
|
|
10854
|
+
}
|
|
10855
|
+
async function listNewSessionEvents(adapter2, sessionId, knownEventKeys) {
|
|
10856
|
+
const descendingEvents = [];
|
|
10857
|
+
const seenPageTokens = /* @__PURE__ */ new Set();
|
|
10858
|
+
let pageToken;
|
|
10859
|
+
while (true) {
|
|
10860
|
+
const result = await adapter2.listSessionEvents(sessionId, {
|
|
10861
|
+
limit: 100,
|
|
10862
|
+
order: "desc",
|
|
10863
|
+
...pageToken ? { page_token: pageToken } : {}
|
|
10864
|
+
});
|
|
10865
|
+
let reachedKnownEvent = false;
|
|
10866
|
+
for (const event of result.events) {
|
|
10867
|
+
if (knownEventKeys.has(sessionEventKey(event))) {
|
|
10868
|
+
reachedKnownEvent = true;
|
|
10869
|
+
break;
|
|
10870
|
+
}
|
|
10871
|
+
descendingEvents.push(event);
|
|
10872
|
+
}
|
|
10873
|
+
if (reachedKnownEvent || !result.next_page) return descendingEvents.reverse();
|
|
10874
|
+
if (seenPageTokens.has(result.next_page)) {
|
|
10875
|
+
throw new UserError("Session event pagination returned a repeated cursor.");
|
|
10876
|
+
}
|
|
10877
|
+
seenPageTokens.add(result.next_page);
|
|
10878
|
+
pageToken = result.next_page;
|
|
10879
|
+
}
|
|
10880
|
+
}
|
|
10881
|
+
function sessionEventKey(event) {
|
|
10882
|
+
return event.id ?? `raw:${event.raw_type}:${JSON.stringify(event.raw)}`;
|
|
9073
10883
|
}
|
|
9074
10884
|
function resolveDirectAdapter(ctx, overrideProvider) {
|
|
9075
10885
|
if (overrideProvider) return getRuntimeProvider(ctx, overrideProvider);
|
|
@@ -9106,7 +10916,7 @@ async function* streamWithResume(adapter2, sessionId, message) {
|
|
|
9106
10916
|
}
|
|
9107
10917
|
}
|
|
9108
10918
|
if (!reachedTerminal) {
|
|
9109
|
-
await
|
|
10919
|
+
await delay2(reconnectIntervalMs);
|
|
9110
10920
|
reconnectIntervalMs = Math.min(reconnectIntervalMs * 2, DEFAULT_POLL_INTERVAL_MS);
|
|
9111
10921
|
}
|
|
9112
10922
|
}
|
|
@@ -9134,7 +10944,7 @@ function assertNotTimedOut(start, timeoutMs) {
|
|
|
9134
10944
|
throw new UserError(`Session did not complete within the timeout (${Math.floor(timeoutMs / 1e3)} seconds).`);
|
|
9135
10945
|
}
|
|
9136
10946
|
}
|
|
9137
|
-
function
|
|
10947
|
+
function delay2(ms) {
|
|
9138
10948
|
return new Promise((resolve13) => setTimeout(resolve13, ms));
|
|
9139
10949
|
}
|
|
9140
10950
|
|
|
@@ -9320,6 +11130,7 @@ var StateManager = class _StateManager {
|
|
|
9320
11130
|
address: r.address,
|
|
9321
11131
|
remote_id: r.remote_id,
|
|
9322
11132
|
externally_managed: r.externally_managed === true ? true : void 0,
|
|
11133
|
+
api_mode: r.api_mode === "forward" ? "forward" : r.api_mode === "managed" ? "managed" : void 0,
|
|
9323
11134
|
version: r.version,
|
|
9324
11135
|
content_hash: r.content_hash ?? r.desired_hash ?? "",
|
|
9325
11136
|
desired_hash: r.desired_hash ?? r.content_hash ?? "",
|
|
@@ -9327,10 +11138,15 @@ var StateManager = class _StateManager {
|
|
|
9327
11138
|
desired_readiness_baseline: r.desired_readiness_baseline,
|
|
9328
11139
|
remote_hash: r.remote_hash,
|
|
9329
11140
|
remote_snapshot: r.remote_snapshot,
|
|
11141
|
+
replacement_fingerprint: r.replacement_fingerprint,
|
|
9330
11142
|
drift_paths: r.drift_paths,
|
|
9331
11143
|
drift_status: r.drift_status
|
|
9332
11144
|
}));
|
|
9333
|
-
|
|
11145
|
+
const pending = Array.isArray(data.pending_default_memory_store_cleanups) ? data.pending_default_memory_store_cleanups : void 0;
|
|
11146
|
+
return new _StateManager(
|
|
11147
|
+
{ resources, ...pending ? { pending_default_memory_store_cleanups: pending } : {} },
|
|
11148
|
+
path
|
|
11149
|
+
);
|
|
9334
11150
|
} catch (err) {
|
|
9335
11151
|
if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
|
|
9336
11152
|
return _StateManager.initialize(path);
|
|
@@ -9528,6 +11344,8 @@ var PlanReadinessImpactSchema = z6.enum(["none", "non_blocking", "blocking"]);
|
|
|
9528
11344
|
var PlannedActionSchema = z6.object({
|
|
9529
11345
|
action: ActionTypeSchema,
|
|
9530
11346
|
address: ResourceAddressSchema,
|
|
11347
|
+
/** Existing state address to retain when this action is an inferred logical rename. */
|
|
11348
|
+
previousAddress: ResourceAddressSchema.optional(),
|
|
9531
11349
|
reason: z6.string(),
|
|
9532
11350
|
driftKind: DriftKindSchema.optional(),
|
|
9533
11351
|
readinessImpact: PlanReadinessImpactSchema.optional(),
|
|
@@ -9593,7 +11411,8 @@ var CloudAgentSchema = z6.object({
|
|
|
9593
11411
|
workspace_id: z6.string().optional(),
|
|
9594
11412
|
created_at: z6.string().optional(),
|
|
9595
11413
|
updated_at: z6.string().optional(),
|
|
9596
|
-
archived_at: z6.string().nullish()
|
|
11414
|
+
archived_at: z6.string().nullish(),
|
|
11415
|
+
attributes: z6.record(z6.string(), z6.unknown()).optional()
|
|
9597
11416
|
});
|
|
9598
11417
|
var ListCloudAgentsResponseSchema = z6.object({
|
|
9599
11418
|
agents: z6.array(CloudAgentSchema)
|
|
@@ -9610,7 +11429,8 @@ var CloudEnvironmentSchema = z6.object({
|
|
|
9610
11429
|
workspace_id: z6.string().optional(),
|
|
9611
11430
|
created_at: z6.string().optional(),
|
|
9612
11431
|
updated_at: z6.string().optional(),
|
|
9613
|
-
archived_at: z6.string().nullish()
|
|
11432
|
+
archived_at: z6.string().nullish(),
|
|
11433
|
+
attributes: z6.record(z6.string(), z6.unknown()).optional()
|
|
9614
11434
|
});
|
|
9615
11435
|
var ListCloudEnvironmentsResponseSchema = z6.object({
|
|
9616
11436
|
environments: z6.array(CloudEnvironmentSchema)
|
|
@@ -9622,7 +11442,8 @@ var CloudVaultSchema = z6.object({
|
|
|
9622
11442
|
type: z6.string().optional(),
|
|
9623
11443
|
created_at: z6.string().nullish(),
|
|
9624
11444
|
updated_at: z6.string().nullish(),
|
|
9625
|
-
archived_at: z6.string().nullish()
|
|
11445
|
+
archived_at: z6.string().nullish(),
|
|
11446
|
+
attributes: z6.record(z6.string(), z6.unknown()).optional()
|
|
9626
11447
|
});
|
|
9627
11448
|
var ListCloudVaultsResponseSchema = z6.object({
|
|
9628
11449
|
vaults: z6.array(CloudVaultSchema)
|
|
@@ -9802,6 +11623,7 @@ export {
|
|
|
9802
11623
|
applyProviderConfigToEnv,
|
|
9803
11624
|
archiveCloudAgent,
|
|
9804
11625
|
archiveMemoryStore,
|
|
11626
|
+
archiveRemoteSession,
|
|
9805
11627
|
areRuntimeCredentialsReady,
|
|
9806
11628
|
batchCreateMemories,
|
|
9807
11629
|
bootstrapRuntimeCredentials,
|
|
@@ -9815,6 +11637,8 @@ export {
|
|
|
9815
11637
|
createProjectRuntime,
|
|
9816
11638
|
createSessionForAgent,
|
|
9817
11639
|
createSkillFromFileId,
|
|
11640
|
+
createVaultCredential,
|
|
11641
|
+
createVaultCredentialWithStateBackend,
|
|
9818
11642
|
decideDestructive,
|
|
9819
11643
|
deleteCloudEnvironment,
|
|
9820
11644
|
deleteCloudVault,
|
|
@@ -9824,6 +11648,8 @@ export {
|
|
|
9824
11648
|
deleteSession,
|
|
9825
11649
|
deleteSkill,
|
|
9826
11650
|
destroyPlannedProjectResources,
|
|
11651
|
+
downloadRemoteFile,
|
|
11652
|
+
downloadRemoteSkill,
|
|
9827
11653
|
executePlannedProject,
|
|
9828
11654
|
extractSkillZipFiles,
|
|
9829
11655
|
getAgent,
|
|
@@ -9831,13 +11657,23 @@ export {
|
|
|
9831
11657
|
getDeploymentRuntimeProviderForContext,
|
|
9832
11658
|
getFileDownloadUrl,
|
|
9833
11659
|
getFileInfo,
|
|
11660
|
+
getManagedAgentProviderCapabilities,
|
|
9834
11661
|
getMemory,
|
|
9835
11662
|
getMemoryProviderCapabilities,
|
|
9836
11663
|
getMemoryStore,
|
|
9837
11664
|
getMemoryVersion,
|
|
11665
|
+
getRemoteAgent,
|
|
11666
|
+
getRemoteDeployment,
|
|
11667
|
+
getRemoteDeploymentRun,
|
|
11668
|
+
getRemoteEnvironment,
|
|
11669
|
+
getRemoteSkill,
|
|
11670
|
+
getRemoteSkillDownloadInfo,
|
|
11671
|
+
getRemoteSkillVersion,
|
|
11672
|
+
getRemoteVault,
|
|
9838
11673
|
getSession,
|
|
9839
11674
|
getSkillInfo,
|
|
9840
11675
|
importResource,
|
|
11676
|
+
inspectSkillSource,
|
|
9841
11677
|
isTerminalSessionStatus,
|
|
9842
11678
|
listAgentsWithReadiness,
|
|
9843
11679
|
listCloudAgents,
|
|
@@ -9850,7 +11686,16 @@ export {
|
|
|
9850
11686
|
listMemoryVersions,
|
|
9851
11687
|
listProviderModelsForContext,
|
|
9852
11688
|
listProviderNames,
|
|
11689
|
+
listRemoteAgentVersions,
|
|
11690
|
+
listRemoteAgents,
|
|
11691
|
+
listRemoteDeploymentRuns,
|
|
11692
|
+
listRemoteDeployments,
|
|
9853
11693
|
listRemoteDeploymentsForContext,
|
|
11694
|
+
listRemoteEnvironments,
|
|
11695
|
+
listRemoteFiles,
|
|
11696
|
+
listRemoteSkillVersions,
|
|
11697
|
+
listRemoteSkills,
|
|
11698
|
+
listRemoteVaults,
|
|
9854
11699
|
listSessionEvents,
|
|
9855
11700
|
listSessionSummaries,
|
|
9856
11701
|
listSkills,
|
|
@@ -9860,8 +11705,13 @@ export {
|
|
|
9860
11705
|
migrateConfig,
|
|
9861
11706
|
parseStateAddress,
|
|
9862
11707
|
pauseDeploymentForContext,
|
|
11708
|
+
planAgentResources,
|
|
11709
|
+
planAgentResourcesWithStateBackend,
|
|
9863
11710
|
planDestroyProjectContext,
|
|
9864
11711
|
planProjectContext,
|
|
11712
|
+
planProjectWithStateBackend,
|
|
11713
|
+
planVaultCredentialCreate,
|
|
11714
|
+
planVaultCredentialCreateWithStateBackend,
|
|
9865
11715
|
prepareInitialSessionPrompt,
|
|
9866
11716
|
preparePromptForProvider,
|
|
9867
11717
|
prependFileHint,
|
|
@@ -9877,9 +11727,12 @@ export {
|
|
|
9877
11727
|
resolveSyncProvider,
|
|
9878
11728
|
rewriteFileMentions,
|
|
9879
11729
|
runDeploymentForContext,
|
|
11730
|
+
runRemoteDeployment,
|
|
11731
|
+
sendRemoteSessionEvents,
|
|
9880
11732
|
sendSessionMessagePolling,
|
|
9881
11733
|
sendSessionMessageStreaming,
|
|
9882
11734
|
setDefaultFetch,
|
|
11735
|
+
setRemoteDeploymentPaused,
|
|
9883
11736
|
startSessionRun,
|
|
9884
11737
|
startSessionRunPolling,
|
|
9885
11738
|
streamSessionEvents,
|
|
@@ -9889,6 +11742,7 @@ export {
|
|
|
9889
11742
|
syncProviderResourcesFromEnv,
|
|
9890
11743
|
updateMemory,
|
|
9891
11744
|
updateMemoryStore,
|
|
11745
|
+
updateRemoteSession,
|
|
9892
11746
|
uploadFile,
|
|
9893
11747
|
validateProjectConfig,
|
|
9894
11748
|
writeProjectRuntime
|