@openagentpack/sdk 0.0.2 → 0.2.0-beta-ddef91c-20260720
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-7ECSYAQA.js → chunk-CHGDM6TX.js} +0 -12
- package/dist/{chunk-TUW6XLDT.js → chunk-OUHQYIG4.js} +1 -1
- package/dist/file-lifecycle.js +2 -2
- package/dist/index.d.ts +78 -8
- package/dist/index.js +966 -128
- package/dist/scan-lifecycle.d.ts +2 -11
- package/dist/scan-lifecycle.js +1 -5
- package/dist/{session-event-CmiO8Gqv.d.ts → session-event-40SgJsNS.d.ts} +81 -11
- package/dist/session-events.d.ts +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
defaultFileUploadPurpose,
|
|
6
6
|
enrichProviderFileInfo
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-OUHQYIG4.js";
|
|
8
8
|
import {
|
|
9
9
|
FILE_SCAN_BACKOFF,
|
|
10
10
|
SCAN_FILE_TIMEOUT_MS,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
classifySkillScan,
|
|
15
15
|
pollUntil,
|
|
16
16
|
skillStatusFromString
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-CHGDM6TX.js";
|
|
18
18
|
|
|
19
19
|
// src/internal/errors.ts
|
|
20
20
|
var UserError = class extends Error {
|
|
@@ -34,6 +34,7 @@ var REQUIRED_METHODS_BY_KIND = {
|
|
|
34
34
|
vault: ["createVault", "deleteVault"],
|
|
35
35
|
skill: ["createSkill", "updateSkill", "deleteSkill"],
|
|
36
36
|
agent: ["createAgent", "updateAgent", "deleteAgent"],
|
|
37
|
+
template: ["createTemplate", "updateTemplate", "archiveTemplate"],
|
|
37
38
|
memory_store: ["createMemoryStore", "deleteMemoryStore"],
|
|
38
39
|
deployment: ["createDeployment", "updateDeployment", "deleteDeployment", "runDeployment", "getDeployment"],
|
|
39
40
|
session: ["createSession", "listSessions", "getSession", "deleteSession", "sendSessionMessage"]
|
|
@@ -88,7 +89,8 @@ var PROVIDER_ENV_VARS = {
|
|
|
88
89
|
},
|
|
89
90
|
qoder: {
|
|
90
91
|
api_key: { env: ["QODER_PAT", "QODER_API_KEY"], required: true },
|
|
91
|
-
gateway: { env: ["QODER_GATEWAY"], required: false }
|
|
92
|
+
gateway: { env: ["QODER_GATEWAY"], required: false },
|
|
93
|
+
forward_gateway: { env: ["QODER_FORWARD_GATEWAY"], required: false }
|
|
92
94
|
},
|
|
93
95
|
claude: {
|
|
94
96
|
api_key: { env: ["ANTHROPIC_API_KEY", "CLAUDE_API_KEY"], required: true },
|
|
@@ -261,11 +263,11 @@ var BaseApiClient = class {
|
|
|
261
263
|
await this.throwIfError(res);
|
|
262
264
|
return Buffer.from(await res.arrayBuffer());
|
|
263
265
|
}
|
|
264
|
-
async *sse(path) {
|
|
266
|
+
async *sse(path, options) {
|
|
265
267
|
const controller = new AbortController();
|
|
266
268
|
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
267
269
|
method: "GET",
|
|
268
|
-
headers: { ...this.headers(), Accept: "text/event-stream" },
|
|
270
|
+
headers: { ...this.headers(), Accept: "text/event-stream", ...options?.headers },
|
|
269
271
|
signal: controller.signal
|
|
270
272
|
});
|
|
271
273
|
await this.throwIfError(res);
|
|
@@ -417,6 +419,9 @@ function secretPlaceholder(vaultName, credName) {
|
|
|
417
419
|
}
|
|
418
420
|
|
|
419
421
|
// src/internal/providers/shared.ts
|
|
422
|
+
function notArchived(raw) {
|
|
423
|
+
return raw.archived_at === null || raw.archived_at === void 0;
|
|
424
|
+
}
|
|
420
425
|
async function locateRemote(client, endpoint, name, id, accept) {
|
|
421
426
|
if (!endpoint) return null;
|
|
422
427
|
const ok = accept ?? (() => true);
|
|
@@ -438,6 +443,7 @@ function buildSessionInfo(res, memoryStoreIds) {
|
|
|
438
443
|
id: res.id,
|
|
439
444
|
agent_id: agent?.id ?? res.agent_id ?? "",
|
|
440
445
|
environment_id: res.environment_id,
|
|
446
|
+
tunnel_id: res.tunnel_id ?? void 0,
|
|
441
447
|
status: res.status,
|
|
442
448
|
title: res.title,
|
|
443
449
|
vault_ids: extractVaultIds(res),
|
|
@@ -1052,7 +1058,7 @@ var ClaudeAdapter = class _ClaudeAdapter {
|
|
|
1052
1058
|
file: "/files"
|
|
1053
1059
|
};
|
|
1054
1060
|
async findResource(type, name, id) {
|
|
1055
|
-
const raw = await locateRemote(this.client, _ClaudeAdapter.ENDPOINT_MAP[type], name, id);
|
|
1061
|
+
const raw = await locateRemote(this.client, _ClaudeAdapter.ENDPOINT_MAP[type], name, id, notArchived);
|
|
1056
1062
|
return raw ? toRemoteResource(raw) : null;
|
|
1057
1063
|
}
|
|
1058
1064
|
async listAgents(filter) {
|
|
@@ -1246,6 +1252,7 @@ var ClaudeAdapter = class _ClaudeAdapter {
|
|
|
1246
1252
|
};
|
|
1247
1253
|
}
|
|
1248
1254
|
async createSession(bindings) {
|
|
1255
|
+
if (bindings.delivery === "forward") throw new UserError("Claude does not support Forward sessions.");
|
|
1249
1256
|
const body = mapSession(bindings);
|
|
1250
1257
|
const res = await this.client.post("/sessions", body);
|
|
1251
1258
|
return toSessionInfo(res);
|
|
@@ -1345,10 +1352,11 @@ var CLAUDE_CAPABILITIES = {
|
|
|
1345
1352
|
vault: { tier: "native", reason: "vaults API" },
|
|
1346
1353
|
skill: { tier: "native", reason: "skills API with files[] upload" },
|
|
1347
1354
|
agent: { tier: "native", reason: "managed agents API" },
|
|
1355
|
+
template: { tier: "unsupported", reason: "no Forward Template equivalent on Claude" },
|
|
1348
1356
|
memory_store: {
|
|
1349
1357
|
tier: "unsupported",
|
|
1350
|
-
reason: "
|
|
1351
|
-
remediation: "use skill knowledge or MCP
|
|
1358
|
+
reason: "Claude exposes Memory Stores, but the OpenAgentPack adapter has not implemented them yet",
|
|
1359
|
+
remediation: "use skill knowledge or MCP until Claude Memory Store support is added to the adapter"
|
|
1352
1360
|
},
|
|
1353
1361
|
mcp_server: { tier: "native", reason: "mcp_servers field on agent" },
|
|
1354
1362
|
multiagent: { tier: "native", reason: "coordinator + roster topology" },
|
|
@@ -1390,6 +1398,9 @@ var QoderClient = class extends BaseApiClient {
|
|
|
1390
1398
|
this.baseUrl = config.gateway ?? "https://api.qoder.com/api/v1/cloud";
|
|
1391
1399
|
this.apiKey = config.apiKey;
|
|
1392
1400
|
}
|
|
1401
|
+
isConflict(status) {
|
|
1402
|
+
return status === 409;
|
|
1403
|
+
}
|
|
1393
1404
|
headers() {
|
|
1394
1405
|
return {
|
|
1395
1406
|
"Content-Type": "application/json",
|
|
@@ -1460,14 +1471,15 @@ function normalizeToolNameFromQoder(name) {
|
|
|
1460
1471
|
return name.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
|
|
1461
1472
|
}
|
|
1462
1473
|
function mapEnvironment2(name, decl, projectName) {
|
|
1474
|
+
const envType = decl.config.type ?? "cloud";
|
|
1475
|
+
const config = { type: envType };
|
|
1476
|
+
if (decl.config.networking) config.networking = decl.config.networking;
|
|
1477
|
+
else if (envType === "cloud") config.networking = { type: "unrestricted" };
|
|
1478
|
+
if (decl.config.packages) config.packages = decl.config.packages;
|
|
1463
1479
|
return {
|
|
1464
1480
|
name,
|
|
1465
1481
|
description: decl.description ?? "",
|
|
1466
|
-
config
|
|
1467
|
-
type: "cloud",
|
|
1468
|
-
networking: decl.config.networking ?? { type: "unrestricted" },
|
|
1469
|
-
packages: decl.config.packages
|
|
1470
|
-
},
|
|
1482
|
+
config,
|
|
1471
1483
|
metadata: injectMetadata(decl.metadata, projectName, name)
|
|
1472
1484
|
};
|
|
1473
1485
|
}
|
|
@@ -1600,6 +1612,85 @@ function mapMemoryStore(name, decl) {
|
|
|
1600
1612
|
description: decl.description
|
|
1601
1613
|
};
|
|
1602
1614
|
}
|
|
1615
|
+
function mapDeployment2(name, decl, refs, projectName, uploadedFiles) {
|
|
1616
|
+
const body = {
|
|
1617
|
+
name,
|
|
1618
|
+
agent: refs.agent_version !== void 0 ? { id: refs.agent_id, type: "agent", version: refs.agent_version } : refs.agent_id,
|
|
1619
|
+
environment_id: refs.environment_id,
|
|
1620
|
+
initial_events: mapDeploymentInitialEvents(decl.initial_events)
|
|
1621
|
+
};
|
|
1622
|
+
if (refs.vault_ids.length) body.vault_ids = refs.vault_ids;
|
|
1623
|
+
const resources = mapDeploymentResources2(decl, refs, uploadedFiles);
|
|
1624
|
+
if (resources.length) body.resources = resources;
|
|
1625
|
+
if (decl.schedule) {
|
|
1626
|
+
body.schedule = {
|
|
1627
|
+
type: "cron",
|
|
1628
|
+
expression: decl.schedule.expression,
|
|
1629
|
+
timezone: decl.schedule.timezone
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
if (decl.description) body.description = decl.description;
|
|
1633
|
+
if (projectName) {
|
|
1634
|
+
body.metadata = injectMetadata(decl.metadata, projectName, name);
|
|
1635
|
+
} else if (decl.metadata) {
|
|
1636
|
+
body.metadata = decl.metadata;
|
|
1637
|
+
}
|
|
1638
|
+
return body;
|
|
1639
|
+
}
|
|
1640
|
+
function mapDeploymentInitialEvents(events) {
|
|
1641
|
+
return events.map((ev) => {
|
|
1642
|
+
if (ev.type === "user.message" || ev.type === "system.message") {
|
|
1643
|
+
return { type: ev.type, content: [{ type: "text", text: ev.content }] };
|
|
1644
|
+
}
|
|
1645
|
+
const out = { type: "user.define_outcome" };
|
|
1646
|
+
if (ev.description) out.description = ev.description;
|
|
1647
|
+
if (ev.rubric) {
|
|
1648
|
+
out.rubric = { type: "text", content: ev.rubric };
|
|
1649
|
+
} else if (ev.rubric_file) {
|
|
1650
|
+
out.rubric = { type: "file", file_id: ev.rubric_file };
|
|
1651
|
+
}
|
|
1652
|
+
if (ev.max_iterations !== void 0) out.max_iterations = ev.max_iterations;
|
|
1653
|
+
return out;
|
|
1654
|
+
});
|
|
1655
|
+
}
|
|
1656
|
+
function mapDeploymentResources2(decl, refs, uploadedFiles) {
|
|
1657
|
+
const resources = [];
|
|
1658
|
+
const seenStores = /* @__PURE__ */ new Set();
|
|
1659
|
+
for (const r of decl.resources ?? []) {
|
|
1660
|
+
if (r.type === "file") {
|
|
1661
|
+
const fileId = r.file_id ?? (r.source ? uploadedFiles?.get(r.source) : void 0);
|
|
1662
|
+
if (fileId) {
|
|
1663
|
+
const entry = { type: "file", file_id: fileId };
|
|
1664
|
+
if (r.mount_path) entry.mount_path = r.mount_path;
|
|
1665
|
+
resources.push(entry);
|
|
1666
|
+
}
|
|
1667
|
+
} else if (r.type === "github_repository") {
|
|
1668
|
+
const entry = { type: "github_repository", url: r.url };
|
|
1669
|
+
if (r.authorization_token) entry.authorization_token = r.authorization_token;
|
|
1670
|
+
if (r.checkout?.branch) entry.checkout = { type: "branch", name: r.checkout.branch };
|
|
1671
|
+
else if (r.checkout?.commit) entry.checkout = { type: "commit", sha: r.checkout.commit };
|
|
1672
|
+
if (r.mount_path) entry.mount_path = r.mount_path;
|
|
1673
|
+
resources.push(entry);
|
|
1674
|
+
} else if (r.type === "memory_store") {
|
|
1675
|
+
const id = refs.memory_store_ids[r.memory_store];
|
|
1676
|
+
if (id && !seenStores.has(id)) {
|
|
1677
|
+
seenStores.add(id);
|
|
1678
|
+
const entry = { type: "memory_store", memory_store_id: id };
|
|
1679
|
+
if (r.access) entry.access = r.access;
|
|
1680
|
+
if (r.instructions) entry.instructions = r.instructions;
|
|
1681
|
+
resources.push(entry);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
for (const m of decl.memory_stores ?? []) {
|
|
1686
|
+
const id = refs.memory_store_ids[m];
|
|
1687
|
+
if (id && !seenStores.has(id)) {
|
|
1688
|
+
seenStores.add(id);
|
|
1689
|
+
resources.push({ type: "memory_store", memory_store_id: id });
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
return resources;
|
|
1693
|
+
}
|
|
1603
1694
|
function mapAgent2(name, decl, refs, version, projectName) {
|
|
1604
1695
|
let model;
|
|
1605
1696
|
if (typeof decl.model === "string") {
|
|
@@ -1663,6 +1754,72 @@ function mapAgent2(name, decl, refs, version, projectName) {
|
|
|
1663
1754
|
}
|
|
1664
1755
|
return body;
|
|
1665
1756
|
}
|
|
1757
|
+
function mapForwardTemplate(name, decl, refs, projectName) {
|
|
1758
|
+
let model;
|
|
1759
|
+
if (typeof decl.model === "string") {
|
|
1760
|
+
model = decl.model;
|
|
1761
|
+
} else {
|
|
1762
|
+
const qoderModel = decl.model.qoder;
|
|
1763
|
+
if (!qoderModel) throw new UserError(`No Qoder model specified for template '${name}'`);
|
|
1764
|
+
model = typeof qoderModel === "string" ? qoderModel : qoderModel.id;
|
|
1765
|
+
}
|
|
1766
|
+
const body = {
|
|
1767
|
+
name,
|
|
1768
|
+
description: decl.description ?? "",
|
|
1769
|
+
model,
|
|
1770
|
+
system: decl.instructions,
|
|
1771
|
+
environment_id: refs.environment_id,
|
|
1772
|
+
vault_ids: refs.vault_ids
|
|
1773
|
+
};
|
|
1774
|
+
if (refs.tunnel_id) body.tunnel_id = refs.tunnel_id;
|
|
1775
|
+
if (projectName) body.metadata = injectMetadata(decl.metadata, projectName, name);
|
|
1776
|
+
else body.metadata = decl.metadata ?? {};
|
|
1777
|
+
if (decl.tools) {
|
|
1778
|
+
const permissions = decl.tools.permissions ?? {};
|
|
1779
|
+
body.tools = [
|
|
1780
|
+
{
|
|
1781
|
+
type: "agent_toolset_20260401",
|
|
1782
|
+
configs: decl.tools.builtin.map((tool) => {
|
|
1783
|
+
const normalized = normalizeToolNameForQoder(tool);
|
|
1784
|
+
const policy = permissions[tool] ?? permissions[tool.toLowerCase()] ?? permissions[normalized];
|
|
1785
|
+
return {
|
|
1786
|
+
name: normalized,
|
|
1787
|
+
enabled: true,
|
|
1788
|
+
...policy ? { permission_policy: { type: policy === "ask" ? "always_ask" : "always_allow" } } : {}
|
|
1789
|
+
};
|
|
1790
|
+
})
|
|
1791
|
+
}
|
|
1792
|
+
];
|
|
1793
|
+
} else {
|
|
1794
|
+
body.tools = [{ type: "agent_toolset_20260401" }];
|
|
1795
|
+
}
|
|
1796
|
+
body.mcp_servers = (decl.mcp_servers ?? []).map((server) => {
|
|
1797
|
+
if (server.type === "official" || !server.url) {
|
|
1798
|
+
throw new UserError(`Qoder MCP server '${server.name}' requires a url`);
|
|
1799
|
+
}
|
|
1800
|
+
return { name: server.name, type: "http", url: server.url };
|
|
1801
|
+
});
|
|
1802
|
+
if (decl.mcp_servers?.length) {
|
|
1803
|
+
const tools = body.tools;
|
|
1804
|
+
for (const server of decl.mcp_servers) {
|
|
1805
|
+
const toolkit = decl.tools?.mcp?.find((item) => item.mcp_server_name === server.name);
|
|
1806
|
+
if (toolkit) {
|
|
1807
|
+
tools.push({
|
|
1808
|
+
type: "mcp_toolset",
|
|
1809
|
+
mcp_server_name: server.name,
|
|
1810
|
+
configs: toolkit.configs
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
body.skills = refs.skill_ids.map((skill) => ({
|
|
1816
|
+
type: skill.type === "official" ? "qoder" : skill.type,
|
|
1817
|
+
skill_id: skill.skill_id,
|
|
1818
|
+
...skill.version ? { version: skill.version } : {},
|
|
1819
|
+
enabled: true
|
|
1820
|
+
}));
|
|
1821
|
+
return body;
|
|
1822
|
+
}
|
|
1666
1823
|
function mapSendMessage2(text) {
|
|
1667
1824
|
return {
|
|
1668
1825
|
events: [{ type: "user.message", content: [{ type: "text", text }] }]
|
|
@@ -1673,9 +1830,12 @@ var QODER_EVENT_MAP = {
|
|
|
1673
1830
|
"user.message": "message",
|
|
1674
1831
|
"agent.tool_use": "tool_use",
|
|
1675
1832
|
"agent.tool_result": "tool_result",
|
|
1833
|
+
"agent.mcp_tool_use": "tool_use",
|
|
1834
|
+
"agent.mcp_tool_result": "tool_result",
|
|
1676
1835
|
"agent.thinking": "thinking",
|
|
1677
1836
|
"session.status_idle": "status",
|
|
1678
1837
|
"session.status_running": "status",
|
|
1838
|
+
"session.status_terminated": "status",
|
|
1679
1839
|
"session.thread_status_idle": "status",
|
|
1680
1840
|
"session.error": "error"
|
|
1681
1841
|
};
|
|
@@ -1752,6 +1912,7 @@ function mapSession2(bindings) {
|
|
|
1752
1912
|
agent: bindings.agent_id,
|
|
1753
1913
|
environment_id: bindings.environment_id
|
|
1754
1914
|
};
|
|
1915
|
+
if (bindings.tunnel_id) body.tunnel_id = bindings.tunnel_id;
|
|
1755
1916
|
if (bindings.title) body.title = bindings.title;
|
|
1756
1917
|
if (bindings.metadata) body.metadata = bindings.metadata;
|
|
1757
1918
|
if (bindings.vault_ids.length) body.vault_ids = bindings.vault_ids;
|
|
@@ -1762,36 +1923,28 @@ function mapSession2(bindings) {
|
|
|
1762
1923
|
if (resources.length) body.resources = resources;
|
|
1763
1924
|
return body;
|
|
1764
1925
|
}
|
|
1765
|
-
function mapDeploymentToSession(decl, refs, fileIds) {
|
|
1766
|
-
const body = {
|
|
1767
|
-
agent: refs.agent_id,
|
|
1768
|
-
environment_id: refs.environment_id
|
|
1769
|
-
};
|
|
1770
|
-
if (decl.description) body.title = decl.description;
|
|
1771
|
-
if (refs.vault_ids.length) body.vault_ids = refs.vault_ids;
|
|
1772
|
-
const resources = Object.values(refs.memory_store_ids).map((id) => ({
|
|
1773
|
-
type: "memory_store",
|
|
1774
|
-
memory_store_id: id
|
|
1775
|
-
}));
|
|
1776
|
-
const fileResources = (decl.resources ?? []).filter((r) => r.type === "file");
|
|
1777
|
-
fileIds.forEach((id, index) => {
|
|
1778
|
-
const entry = { type: "file", file_id: id };
|
|
1779
|
-
const mountPath = fileResources[index]?.mount_path;
|
|
1780
|
-
if (mountPath) entry.mount_path = mountPath;
|
|
1781
|
-
resources.push(entry);
|
|
1782
|
-
});
|
|
1783
|
-
if (resources.length) body.resources = resources;
|
|
1784
|
-
return body;
|
|
1785
|
-
}
|
|
1786
1926
|
|
|
1787
1927
|
// src/internal/providers/qoder/adapter.ts
|
|
1928
|
+
function deriveForwardGateway(cloudGateway) {
|
|
1929
|
+
if (!cloudGateway) return "https://api.qoder.com/api/v1/forward";
|
|
1930
|
+
const trimmed = cloudGateway.replace(/\/$/, "");
|
|
1931
|
+
return trimmed.endsWith("/cloud") ? `${trimmed.slice(0, -"/cloud".length)}/forward` : `${trimmed}/forward`;
|
|
1932
|
+
}
|
|
1933
|
+
var QODER_DEFAULT_IDENTITY_EXTERNAL_ID = "__qca_admin_identity__";
|
|
1788
1934
|
var QoderAdapter = class _QoderAdapter {
|
|
1789
1935
|
name = "qoder";
|
|
1790
1936
|
eventResume = true;
|
|
1791
1937
|
client;
|
|
1938
|
+
forwardClient;
|
|
1792
1939
|
projectName;
|
|
1793
|
-
|
|
1940
|
+
forwardSessionIds = /* @__PURE__ */ new Set();
|
|
1941
|
+
defaultForwardIdentityId;
|
|
1942
|
+
constructor(apiKey, gateway, projectName, forwardGateway) {
|
|
1794
1943
|
this.client = new QoderClient({ apiKey, gateway });
|
|
1944
|
+
this.forwardClient = new QoderClient({
|
|
1945
|
+
apiKey,
|
|
1946
|
+
gateway: forwardGateway ?? deriveForwardGateway(gateway)
|
|
1947
|
+
});
|
|
1795
1948
|
this.projectName = projectName ?? "";
|
|
1796
1949
|
}
|
|
1797
1950
|
async validate() {
|
|
@@ -1807,7 +1960,11 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
1807
1960
|
// deployment omitted: emulated on Qoder, no remote listing endpoint
|
|
1808
1961
|
};
|
|
1809
1962
|
async findResource(type, name, id) {
|
|
1810
|
-
|
|
1963
|
+
if (type === "template") {
|
|
1964
|
+
const raw2 = await locateRemote(this.forwardClient, "/templates", name, id, (item) => item.status !== "archived");
|
|
1965
|
+
return raw2 ? toRemoteResource(raw2) : null;
|
|
1966
|
+
}
|
|
1967
|
+
const raw = await locateRemote(this.client, _QoderAdapter.ENDPOINT_MAP[type], name, id, notArchived);
|
|
1811
1968
|
return raw ? toRemoteResource(raw) : null;
|
|
1812
1969
|
}
|
|
1813
1970
|
async listAgents(filter) {
|
|
@@ -1852,14 +2009,21 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
1852
2009
|
return toRestSkillInfo(res);
|
|
1853
2010
|
}
|
|
1854
2011
|
getDriftSupport(type) {
|
|
1855
|
-
if (type === "agent" || type === "environment") return "full";
|
|
2012
|
+
if (type === "agent" || type === "environment" || type === "template") return "full";
|
|
1856
2013
|
if (type === "deployment") return "unsupported";
|
|
1857
2014
|
return _QoderAdapter.ENDPOINT_MAP[type] ? "existence" : "unsupported";
|
|
1858
2015
|
}
|
|
1859
2016
|
async readComparableResource(type, id, name) {
|
|
1860
|
-
if (type !== "agent" && type !== "environment") return null;
|
|
1861
|
-
const
|
|
1862
|
-
const
|
|
2017
|
+
if (type !== "agent" && type !== "environment" && type !== "template") return null;
|
|
2018
|
+
const isTemplate = type === "template";
|
|
2019
|
+
const endpoint = type === "agent" ? "/agents" : type === "environment" ? "/environments" : "/templates";
|
|
2020
|
+
const raw = await locateRemote(
|
|
2021
|
+
isTemplate ? this.forwardClient : this.client,
|
|
2022
|
+
endpoint,
|
|
2023
|
+
name,
|
|
2024
|
+
id,
|
|
2025
|
+
isTemplate ? (item) => item.status !== "archived" : notArchived
|
|
2026
|
+
);
|
|
1863
2027
|
if (!raw) return null;
|
|
1864
2028
|
const comparable = this.normalizeRemote(type, raw);
|
|
1865
2029
|
return {
|
|
@@ -1883,6 +2047,7 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
1883
2047
|
mapAgent2(name, decl, { skill_ids: [] }, void 0, this.projectName)
|
|
1884
2048
|
);
|
|
1885
2049
|
}
|
|
2050
|
+
if (type === "template") return null;
|
|
1886
2051
|
return null;
|
|
1887
2052
|
}
|
|
1888
2053
|
normalizeRemote(type, raw) {
|
|
@@ -1898,6 +2063,24 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
1898
2063
|
metadata: stripAgentsMetadata(raw.metadata)
|
|
1899
2064
|
});
|
|
1900
2065
|
}
|
|
2066
|
+
if (type === "template") {
|
|
2067
|
+
return compactDeep({
|
|
2068
|
+
name: raw.name,
|
|
2069
|
+
description: raw.description,
|
|
2070
|
+
model: raw.model,
|
|
2071
|
+
system: raw.system,
|
|
2072
|
+
tools: raw.tools,
|
|
2073
|
+
mcp_servers: raw.mcp_servers,
|
|
2074
|
+
skills: raw.skills,
|
|
2075
|
+
multiagent: raw.multiagent,
|
|
2076
|
+
environment_id: raw.environment_id,
|
|
2077
|
+
tunnel_id: raw.tunnel_id,
|
|
2078
|
+
vault_ids: Array.isArray(raw.vault_ids) ? raw.vault_ids : Object.keys(raw.vaults ?? {}),
|
|
2079
|
+
files: raw.files,
|
|
2080
|
+
environment_variables: raw.environment_variables,
|
|
2081
|
+
metadata: stripAgentsMetadata(raw.metadata)
|
|
2082
|
+
});
|
|
2083
|
+
}
|
|
1901
2084
|
return compactDeep({
|
|
1902
2085
|
description: raw.description,
|
|
1903
2086
|
model: normalizeModel(raw.model),
|
|
@@ -1991,6 +2174,30 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
1991
2174
|
async deleteAgent(id) {
|
|
1992
2175
|
await this.client.delete(`/agents/${id}`);
|
|
1993
2176
|
}
|
|
2177
|
+
async createTemplate(name, decl, refs) {
|
|
2178
|
+
await this.registerForwardVaults(refs.vault_ids);
|
|
2179
|
+
const body = mapForwardTemplate(name, decl, refs, this.projectName);
|
|
2180
|
+
const res = await this.forwardClient.post("/templates", body);
|
|
2181
|
+
return toRemoteResource(res);
|
|
2182
|
+
}
|
|
2183
|
+
async updateTemplate(id, name, decl, refs) {
|
|
2184
|
+
await this.registerForwardVaults(refs.vault_ids);
|
|
2185
|
+
const body = mapForwardTemplate(name, decl, refs, this.projectName);
|
|
2186
|
+
if (!refs.tunnel_id) body.tunnel_id = null;
|
|
2187
|
+
const res = await this.forwardClient.post(`/templates/${id}`, body);
|
|
2188
|
+
return toRemoteResource(res);
|
|
2189
|
+
}
|
|
2190
|
+
async archiveTemplate(id) {
|
|
2191
|
+
await this.forwardClient.post(`/templates/${id}/archive`, {});
|
|
2192
|
+
}
|
|
2193
|
+
async registerForwardVaults(vaultIds) {
|
|
2194
|
+
for (const id of vaultIds) {
|
|
2195
|
+
await this.forwardClient.post("/resources/registry", {
|
|
2196
|
+
type: "vault",
|
|
2197
|
+
resource: { id }
|
|
2198
|
+
});
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
1994
2201
|
async createMemoryStore(name, decl) {
|
|
1995
2202
|
const body = mapMemoryStore(name, decl);
|
|
1996
2203
|
const res = await this.client.post("/memory_stores", body);
|
|
@@ -2008,46 +2215,58 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2008
2215
|
async deleteMemoryStore(id) {
|
|
2009
2216
|
await this.client.delete(`/memory_stores/${id}`);
|
|
2010
2217
|
}
|
|
2011
|
-
async createDeployment(
|
|
2012
|
-
|
|
2218
|
+
async createDeployment(name, decl, refs, basePath) {
|
|
2219
|
+
const uploaded = await this.uploadDeploymentFiles(decl, basePath);
|
|
2220
|
+
const body = mapDeployment2(name, decl, refs, this.projectName, uploaded);
|
|
2221
|
+
const res = await this.client.post("/deployments", body);
|
|
2222
|
+
return toRemoteResource(res);
|
|
2013
2223
|
}
|
|
2014
|
-
async updateDeployment(
|
|
2015
|
-
|
|
2224
|
+
async updateDeployment(id, name, decl, refs, basePath) {
|
|
2225
|
+
const uploaded = await this.uploadDeploymentFiles(decl, basePath);
|
|
2226
|
+
const body = mapDeployment2(name, decl, refs, this.projectName, uploaded);
|
|
2227
|
+
const res = await this.client.post(`/deployments/${id}`, body);
|
|
2228
|
+
return toRemoteResource(res);
|
|
2016
2229
|
}
|
|
2017
|
-
async deleteDeployment(
|
|
2230
|
+
async deleteDeployment(id) {
|
|
2231
|
+
await this.client.post(`/deployments/${id}/archive`, {});
|
|
2018
2232
|
}
|
|
2019
2233
|
async runDeployment(ctx) {
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
if (r.type === "file") {
|
|
2023
|
-
if (r.file_id) {
|
|
2024
|
-
fileIds.push(r.file_id);
|
|
2025
|
-
} else if (r.source) {
|
|
2026
|
-
fileIds.push(await this.uploadSessionFile(r.source, ctx.basePath));
|
|
2027
|
-
}
|
|
2028
|
-
}
|
|
2029
|
-
}
|
|
2030
|
-
const body = mapDeploymentToSession(ctx.decl, ctx.refs, fileIds);
|
|
2031
|
-
const sessionRes = await this.client.post("/sessions", body);
|
|
2032
|
-
const sessionId = sessionRes.id;
|
|
2033
|
-
const events = ctx.decl.initial_events.filter((e) => e.type === "user.message" || e.type === "system.message").map((e) => ({
|
|
2034
|
-
type: "user.message",
|
|
2035
|
-
content: [{ type: "text", text: e.content }]
|
|
2036
|
-
}));
|
|
2037
|
-
if (events.length) {
|
|
2038
|
-
await this.client.post(`/sessions/${sessionId}/events`, { events });
|
|
2234
|
+
if (!ctx.id) {
|
|
2235
|
+
throw new UserError(`Deployment '${ctx.name}' has no remote id; run \`agents apply\` first.`);
|
|
2039
2236
|
}
|
|
2040
|
-
|
|
2237
|
+
const res = await this.client.post(`/deployments/${ctx.id}/run`, {});
|
|
2238
|
+
return {
|
|
2239
|
+
run_id: res.id,
|
|
2240
|
+
session_id: res.session_id ?? null,
|
|
2241
|
+
error: res.error ?? void 0
|
|
2242
|
+
};
|
|
2041
2243
|
}
|
|
2042
2244
|
async getDeployment(ctx) {
|
|
2043
|
-
|
|
2245
|
+
if (!ctx.id) {
|
|
2246
|
+
throw new UserError(`Deployment '${ctx.name}' has no remote id; run \`agents apply\` first.`);
|
|
2247
|
+
}
|
|
2248
|
+
const res = await this.client.get(`/deployments/${ctx.id}`);
|
|
2249
|
+
const sched = res.schedule;
|
|
2044
2250
|
return {
|
|
2045
|
-
id:
|
|
2046
|
-
status:
|
|
2047
|
-
|
|
2048
|
-
|
|
2251
|
+
id: res.id,
|
|
2252
|
+
status: res.status ?? "unknown",
|
|
2253
|
+
paused_reason: res.paused_reason,
|
|
2254
|
+
schedule: sched ? {
|
|
2255
|
+
expression: sched.expression,
|
|
2256
|
+
timezone: sched.timezone
|
|
2257
|
+
} : void 0,
|
|
2258
|
+
attributes: res
|
|
2049
2259
|
};
|
|
2050
2260
|
}
|
|
2261
|
+
async uploadDeploymentFiles(decl, basePath) {
|
|
2262
|
+
const map = /* @__PURE__ */ new Map();
|
|
2263
|
+
for (const r of decl.resources ?? []) {
|
|
2264
|
+
if (r.type === "file" && !r.file_id && r.source && !map.has(r.source)) {
|
|
2265
|
+
map.set(r.source, await this.uploadSessionFile(r.source, basePath));
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
return map;
|
|
2269
|
+
}
|
|
2051
2270
|
async uploadSessionFile(source, basePath) {
|
|
2052
2271
|
const fullPath = resolve2(dirname2(basePath), source);
|
|
2053
2272
|
const content = readFileSync2(fullPath);
|
|
@@ -2058,11 +2277,73 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2058
2277
|
return res.file_id ?? res.id;
|
|
2059
2278
|
}
|
|
2060
2279
|
async createSession(bindings) {
|
|
2280
|
+
if (bindings.delivery === "forward") {
|
|
2281
|
+
const identityId = bindings.identity_id ?? await this.resolveDefaultForwardIdentityId();
|
|
2282
|
+
const body2 = {
|
|
2283
|
+
identity_id: identityId,
|
|
2284
|
+
template_id: bindings.template_id,
|
|
2285
|
+
incremental_streaming_enabled: false
|
|
2286
|
+
};
|
|
2287
|
+
if (bindings.title) body2.title = bindings.title;
|
|
2288
|
+
if (bindings.metadata) body2.metadata = bindings.metadata;
|
|
2289
|
+
if (bindings.files?.length) {
|
|
2290
|
+
body2.resources = bindings.files.map((file) => ({
|
|
2291
|
+
type: "file",
|
|
2292
|
+
file_id: file.file_id,
|
|
2293
|
+
mount_path: file.mount_path
|
|
2294
|
+
}));
|
|
2295
|
+
}
|
|
2296
|
+
const res2 = await this.forwardClient.post("/sessions", body2);
|
|
2297
|
+
const info = toForwardSessionInfo(res2, bindings);
|
|
2298
|
+
this.forwardSessionIds.add(info.id);
|
|
2299
|
+
return info;
|
|
2300
|
+
}
|
|
2061
2301
|
const body = mapSession2(bindings);
|
|
2062
2302
|
const res = await this.client.post("/sessions", body);
|
|
2063
2303
|
return toSessionInfo2(res);
|
|
2064
2304
|
}
|
|
2305
|
+
async resolveDefaultForwardIdentityId() {
|
|
2306
|
+
if (this.defaultForwardIdentityId) return this.defaultForwardIdentityId;
|
|
2307
|
+
let afterId;
|
|
2308
|
+
do {
|
|
2309
|
+
const params = new URLSearchParams({ limit: "100" });
|
|
2310
|
+
if (afterId) params.set("after_id", afterId);
|
|
2311
|
+
const res = await this.forwardClient.get(`/identities?${params}`);
|
|
2312
|
+
const identities = res.data ?? [];
|
|
2313
|
+
const match = identities.find(
|
|
2314
|
+
(identity) => identity.external_id === QODER_DEFAULT_IDENTITY_EXTERNAL_ID && identity.enabled !== false && identity.archived !== true
|
|
2315
|
+
);
|
|
2316
|
+
if (typeof match?.id === "string") {
|
|
2317
|
+
this.defaultForwardIdentityId = match.id;
|
|
2318
|
+
return match.id;
|
|
2319
|
+
}
|
|
2320
|
+
const hasMore = res.has_more ?? false;
|
|
2321
|
+
const nextId = hasMore ? res.last_id ?? void 0 : void 0;
|
|
2322
|
+
if (!nextId || nextId === afterId) break;
|
|
2323
|
+
afterId = nextId;
|
|
2324
|
+
} while (afterId);
|
|
2325
|
+
throw new UserError(
|
|
2326
|
+
`Qoder default Forward Identity '${QODER_DEFAULT_IDENTITY_EXTERNAL_ID}' was not found. Ask Qoder to provision it, set defaults.session.qoder.identity_id, or pass --identity-id.`
|
|
2327
|
+
);
|
|
2328
|
+
}
|
|
2065
2329
|
async listSessions(filter) {
|
|
2330
|
+
if (filter?.agent_id?.startsWith("tmpl_")) {
|
|
2331
|
+
const params2 = new URLSearchParams({ template_id: filter.agent_id });
|
|
2332
|
+
if (filter.limit) params2.set("limit", String(filter.limit));
|
|
2333
|
+
if (filter.page) params2.set("after_id", filter.page);
|
|
2334
|
+
const res2 = await this.forwardClient.get(`/sessions?${params2}`);
|
|
2335
|
+
const data2 = res2.data ?? [];
|
|
2336
|
+
const hasMore = res2.has_more ?? false;
|
|
2337
|
+
const nextPage2 = hasMore ? res2.last_id ?? void 0 : void 0;
|
|
2338
|
+
for (const item of data2) {
|
|
2339
|
+
if (typeof item.id === "string") this.forwardSessionIds.add(item.id);
|
|
2340
|
+
}
|
|
2341
|
+
return {
|
|
2342
|
+
sessions: data2.map((item) => toForwardSessionInfo(item)),
|
|
2343
|
+
has_more: hasMore,
|
|
2344
|
+
next_page: nextPage2
|
|
2345
|
+
};
|
|
2346
|
+
}
|
|
2066
2347
|
const params = new URLSearchParams();
|
|
2067
2348
|
if (filter?.agent_id) params.set("agent_id", filter.agent_id);
|
|
2068
2349
|
if (filter?.limit) params.set("limit", String(filter.limit));
|
|
@@ -2077,34 +2358,105 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2077
2358
|
};
|
|
2078
2359
|
}
|
|
2079
2360
|
async getSession(id) {
|
|
2080
|
-
|
|
2081
|
-
|
|
2361
|
+
if (this.forwardSessionIds.has(id)) return this.getForwardSession(id);
|
|
2362
|
+
try {
|
|
2363
|
+
const res = await this.client.get(`/sessions/${id}`);
|
|
2364
|
+
return toSessionInfo2(res);
|
|
2365
|
+
} catch (error) {
|
|
2366
|
+
if (!ApiError.isNotFound(error)) throw error;
|
|
2367
|
+
return this.getForwardSession(id);
|
|
2368
|
+
}
|
|
2082
2369
|
}
|
|
2083
2370
|
async deleteSession(id) {
|
|
2084
|
-
|
|
2371
|
+
if (this.forwardSessionIds.has(id)) {
|
|
2372
|
+
await this.forwardClient.post(`/sessions/${id}/archive`, {});
|
|
2373
|
+
return;
|
|
2374
|
+
}
|
|
2375
|
+
try {
|
|
2376
|
+
await this.client.delete(`/sessions/${id}`);
|
|
2377
|
+
} catch (error) {
|
|
2378
|
+
if (!ApiError.isNotFound(error)) throw error;
|
|
2379
|
+
await this.forwardClient.post(`/sessions/${id}/archive`, {});
|
|
2380
|
+
this.forwardSessionIds.add(id);
|
|
2381
|
+
}
|
|
2085
2382
|
}
|
|
2086
2383
|
async sendSessionMessage(sessionId, message) {
|
|
2087
2384
|
const body = mapSendMessage2(message);
|
|
2088
|
-
|
|
2089
|
-
|
|
2385
|
+
if (this.forwardSessionIds.has(sessionId)) {
|
|
2386
|
+
const res = await this.forwardClient.post(`/sessions/${sessionId}/events`, body);
|
|
2387
|
+
return extractCreatedEventId(res);
|
|
2388
|
+
}
|
|
2389
|
+
try {
|
|
2390
|
+
const res = await this.client.post(`/sessions/${sessionId}/events`, body);
|
|
2391
|
+
return extractCreatedEventId(res);
|
|
2392
|
+
} catch (error) {
|
|
2393
|
+
if (!ApiError.isNotFound(error)) throw error;
|
|
2394
|
+
const res = await this.forwardClient.post(`/sessions/${sessionId}/events`, body);
|
|
2395
|
+
this.forwardSessionIds.add(sessionId);
|
|
2396
|
+
return extractCreatedEventId(res);
|
|
2397
|
+
}
|
|
2090
2398
|
}
|
|
2091
2399
|
async *streamSessionEvents(sessionId, options) {
|
|
2400
|
+
if (this.forwardSessionIds.has(sessionId)) {
|
|
2401
|
+
yield* this.streamForwardSessionEvents(sessionId, options);
|
|
2402
|
+
return;
|
|
2403
|
+
}
|
|
2092
2404
|
const path = `/sessions/${sessionId}/events/stream`;
|
|
2093
2405
|
let skipping = !!options?.after_id;
|
|
2094
2406
|
const afterId = options?.after_id;
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2407
|
+
try {
|
|
2408
|
+
for await (const raw of this.client.sse(path)) {
|
|
2409
|
+
if (skipping) {
|
|
2410
|
+
const eventId = raw.id;
|
|
2411
|
+
if (eventId === afterId) {
|
|
2412
|
+
skipping = false;
|
|
2413
|
+
}
|
|
2414
|
+
continue;
|
|
2100
2415
|
}
|
|
2101
|
-
|
|
2416
|
+
yield toSessionEvent3(raw);
|
|
2102
2417
|
}
|
|
2103
|
-
|
|
2418
|
+
} catch (error) {
|
|
2419
|
+
if (!ApiError.isNotFound(error)) throw error;
|
|
2420
|
+
this.forwardSessionIds.add(sessionId);
|
|
2421
|
+
yield* this.streamForwardSessionEvents(sessionId, options);
|
|
2104
2422
|
}
|
|
2105
2423
|
}
|
|
2106
2424
|
async listSessionEvents(sessionId, options) {
|
|
2107
|
-
|
|
2425
|
+
if (this.forwardSessionIds.has(sessionId)) return this.listForwardSessionEvents(sessionId, options);
|
|
2426
|
+
try {
|
|
2427
|
+
return await listSessionEventsPaged(this.client, sessionId, options, toSessionEvent3, { forwardAfterId: true });
|
|
2428
|
+
} catch (error) {
|
|
2429
|
+
if (!ApiError.isNotFound(error)) throw error;
|
|
2430
|
+
this.forwardSessionIds.add(sessionId);
|
|
2431
|
+
return this.listForwardSessionEvents(sessionId, options);
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
async getForwardSession(id) {
|
|
2435
|
+
const res = await this.forwardClient.get(`/sessions/${id}`);
|
|
2436
|
+
this.forwardSessionIds.add(id);
|
|
2437
|
+
return toForwardSessionInfo(res);
|
|
2438
|
+
}
|
|
2439
|
+
async *streamForwardSessionEvents(sessionId, options) {
|
|
2440
|
+
const headers = options?.after_id ? { "Last-Event-ID": options.after_id } : void 0;
|
|
2441
|
+
for await (const raw of this.forwardClient.sse(`/sessions/${sessionId}/events/stream`, { headers })) {
|
|
2442
|
+
yield toSessionEvent3(raw);
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
async listForwardSessionEvents(sessionId, options) {
|
|
2446
|
+
const params = new URLSearchParams();
|
|
2447
|
+
if (options?.limit) params.set("limit", String(options.limit));
|
|
2448
|
+
if (options?.order) params.set("order", options.order);
|
|
2449
|
+
const afterId = options?.after_id ?? options?.page_token ?? options?.page;
|
|
2450
|
+
if (afterId) params.set("after_id", afterId);
|
|
2451
|
+
const query = params.toString();
|
|
2452
|
+
const res = await this.forwardClient.get(`/sessions/${sessionId}/events${query ? `?${query}` : ""}`);
|
|
2453
|
+
const data = res.data ?? [];
|
|
2454
|
+
const hasMore = res.has_more ?? false;
|
|
2455
|
+
return {
|
|
2456
|
+
events: data.map(toSessionEvent3),
|
|
2457
|
+
has_more: hasMore,
|
|
2458
|
+
next_page: hasMore ? res.last_id ?? void 0 : void 0
|
|
2459
|
+
};
|
|
2108
2460
|
}
|
|
2109
2461
|
async listModels() {
|
|
2110
2462
|
const res = await this.client.get("/models");
|
|
@@ -2138,6 +2490,24 @@ var QoderAdapter = class _QoderAdapter {
|
|
|
2138
2490
|
function toSessionInfo2(res) {
|
|
2139
2491
|
return buildSessionInfo(res, (r) => r.memory_store_ids ?? []);
|
|
2140
2492
|
}
|
|
2493
|
+
function toForwardSessionInfo(res, bindings) {
|
|
2494
|
+
const template = res.template ?? {};
|
|
2495
|
+
const templateId = res.template_id ?? template.id ?? bindings?.template_id ?? "";
|
|
2496
|
+
const environmentId = res.environment_id ?? template.environment_id ?? "";
|
|
2497
|
+
return {
|
|
2498
|
+
id: res.id,
|
|
2499
|
+
agent_id: templateId,
|
|
2500
|
+
environment_id: environmentId,
|
|
2501
|
+
tunnel_id: res.tunnel_id ?? template.tunnel_id,
|
|
2502
|
+
status: res.status ?? "unknown",
|
|
2503
|
+
title: res.title,
|
|
2504
|
+
vault_ids: res.vault_ids ?? [],
|
|
2505
|
+
memory_store_ids: res.memory_store_ids ?? [],
|
|
2506
|
+
created_at: res.created_at ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
2507
|
+
updated_at: res.updated_at ?? res.created_at ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
2508
|
+
attributes: res
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2141
2511
|
function normalizeModel(value) {
|
|
2142
2512
|
if (value && typeof value === "object" && "id" in value) {
|
|
2143
2513
|
return value.id;
|
|
@@ -2191,6 +2561,7 @@ var QODER_CAPABILITIES = {
|
|
|
2191
2561
|
vault: { tier: "native", reason: "vaults + MCP credentials" },
|
|
2192
2562
|
skill: { tier: "native", reason: "skills API with zip upload" },
|
|
2193
2563
|
agent: { tier: "native", reason: "agents API" },
|
|
2564
|
+
template: { tier: "native", reason: "Forward Templates API" },
|
|
2194
2565
|
memory_store: { tier: "native", reason: "memory_stores API" },
|
|
2195
2566
|
mcp_server: { tier: "native", reason: "mcp_servers field on agent" },
|
|
2196
2567
|
multiagent: {
|
|
@@ -2199,9 +2570,8 @@ var QODER_CAPABILITIES = {
|
|
|
2199
2570
|
remediation: "deploy agents independently and orchestrate via MCP"
|
|
2200
2571
|
},
|
|
2201
2572
|
deployment: {
|
|
2202
|
-
tier: "
|
|
2203
|
-
reason: "
|
|
2204
|
-
remediation: "scheduling and outcome rubrics are not enforced server-side \u2014 use external cron/CI for always-on or scheduled runs"
|
|
2573
|
+
tier: "native",
|
|
2574
|
+
reason: "deployments API with scheduled and manual runs"
|
|
2205
2575
|
},
|
|
2206
2576
|
session: { tier: "native", reason: "sessions API" }
|
|
2207
2577
|
};
|
|
@@ -2210,7 +2580,8 @@ var QODER_CAPABILITIES = {
|
|
|
2210
2580
|
import { z as z2 } from "zod";
|
|
2211
2581
|
var qoderConfigSchema = z2.object({
|
|
2212
2582
|
api_key: z2.string(),
|
|
2213
|
-
gateway: z2.string().optional()
|
|
2583
|
+
gateway: z2.string().optional(),
|
|
2584
|
+
forward_gateway: z2.string().optional()
|
|
2214
2585
|
});
|
|
2215
2586
|
|
|
2216
2587
|
// src/internal/providers/qoder/index.ts
|
|
@@ -2220,7 +2591,7 @@ registerProvider({
|
|
|
2220
2591
|
capabilities: QODER_CAPABILITIES,
|
|
2221
2592
|
createAdapter: (config, projectName) => {
|
|
2222
2593
|
const c = config;
|
|
2223
|
-
return new QoderAdapter(c.api_key, c.gateway, projectName);
|
|
2594
|
+
return new QoderAdapter(c.api_key, c.gateway, projectName, c.forward_gateway);
|
|
2224
2595
|
}
|
|
2225
2596
|
});
|
|
2226
2597
|
|
|
@@ -2488,7 +2859,7 @@ function mapSession3(bindings) {
|
|
|
2488
2859
|
if (bindings.memory_store_ids.length) body.memory_store_ids = bindings.memory_store_ids;
|
|
2489
2860
|
return body;
|
|
2490
2861
|
}
|
|
2491
|
-
function
|
|
2862
|
+
function mapDeploymentToSession(decl, refs, fileIds) {
|
|
2492
2863
|
const body = {
|
|
2493
2864
|
agent: refs.agent_id,
|
|
2494
2865
|
environment_id: refs.environment_id
|
|
@@ -2908,7 +3279,7 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
2908
3279
|
}
|
|
2909
3280
|
}
|
|
2910
3281
|
}
|
|
2911
|
-
const body =
|
|
3282
|
+
const body = mapDeploymentToSession(ctx.decl, ctx.refs, fileIds);
|
|
2912
3283
|
const sessionRes = await this.client.post("/sessions", body);
|
|
2913
3284
|
const sessionId = sessionRes.id;
|
|
2914
3285
|
const eventsBody = mapInitialEvents2(ctx.decl.initial_events);
|
|
@@ -2919,7 +3290,7 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
2919
3290
|
return { session_id: sessionId };
|
|
2920
3291
|
}
|
|
2921
3292
|
async getDeployment(ctx) {
|
|
2922
|
-
const plan =
|
|
3293
|
+
const plan = mapDeploymentToSession(ctx.decl, ctx.refs, []);
|
|
2923
3294
|
return {
|
|
2924
3295
|
id: ctx.id,
|
|
2925
3296
|
status: "emulated (local)",
|
|
@@ -2939,6 +3310,7 @@ var BailianAdapter = class _BailianAdapter {
|
|
|
2939
3310
|
}
|
|
2940
3311
|
// --- Session ---
|
|
2941
3312
|
async createSession(bindings) {
|
|
3313
|
+
if (bindings.delivery === "forward") throw new UserError("Bailian does not support Forward sessions.");
|
|
2942
3314
|
const body = mapSession3(bindings);
|
|
2943
3315
|
const res = await this.client.post("/sessions", body);
|
|
2944
3316
|
return toSessionInfo3(res);
|
|
@@ -3106,6 +3478,7 @@ var BAILIAN_CAPABILITIES = {
|
|
|
3106
3478
|
vault: { tier: "native", reason: "vaults + credentials API (static_bearer MCP credentials)" },
|
|
3107
3479
|
skill: { tier: "native", reason: "skills API with 2-step zip upload via Files API" },
|
|
3108
3480
|
agent: { tier: "native", reason: "agents API with versioned updates" },
|
|
3481
|
+
template: { tier: "unsupported", reason: "no Forward Template equivalent on Bailian" },
|
|
3109
3482
|
memory_store: {
|
|
3110
3483
|
tier: "unsupported",
|
|
3111
3484
|
reason: "no memory store primitive on Bailian"
|
|
@@ -3418,7 +3791,7 @@ function mapAgent4(name, decl, refs, version, projectName) {
|
|
|
3418
3791
|
}
|
|
3419
3792
|
return body;
|
|
3420
3793
|
}
|
|
3421
|
-
function
|
|
3794
|
+
function mapDeploymentToSession2(decl, refs, fileIds) {
|
|
3422
3795
|
const body = {
|
|
3423
3796
|
agent: refs.agent_id,
|
|
3424
3797
|
environment_id: refs.environment_id
|
|
@@ -3736,7 +4109,7 @@ var ArkAdapter = class _ArkAdapter {
|
|
|
3736
4109
|
}
|
|
3737
4110
|
}
|
|
3738
4111
|
}
|
|
3739
|
-
const body =
|
|
4112
|
+
const body = mapDeploymentToSession2(ctx.decl, ctx.refs, fileIds);
|
|
3740
4113
|
const sessionRes = await this.client.post("/sessions", body);
|
|
3741
4114
|
const sessionId = sessionRes.id;
|
|
3742
4115
|
const events = ctx.decl.initial_events.filter((e) => e.type === "user.message" || e.type === "system.message").map((e) => ({
|
|
@@ -3749,7 +4122,7 @@ var ArkAdapter = class _ArkAdapter {
|
|
|
3749
4122
|
return { session_id: sessionId };
|
|
3750
4123
|
}
|
|
3751
4124
|
async getDeployment(ctx) {
|
|
3752
|
-
const plan =
|
|
4125
|
+
const plan = mapDeploymentToSession2(ctx.decl, ctx.refs, []);
|
|
3753
4126
|
return {
|
|
3754
4127
|
id: ctx.id,
|
|
3755
4128
|
status: "emulated (local)",
|
|
@@ -3767,6 +4140,7 @@ var ArkAdapter = class _ArkAdapter {
|
|
|
3767
4140
|
return res.file_id ?? res.id;
|
|
3768
4141
|
}
|
|
3769
4142
|
async createSession(bindings) {
|
|
4143
|
+
if (bindings.delivery === "forward") throw new UserError("Ark does not support Forward sessions.");
|
|
3770
4144
|
const body = mapSession4(bindings);
|
|
3771
4145
|
const res = await this.client.post("/sessions", body);
|
|
3772
4146
|
return toSessionInfo4(res);
|
|
@@ -3866,6 +4240,7 @@ var ARK_CAPABILITIES = {
|
|
|
3866
4240
|
vault: { tier: "native", reason: "vaults API" },
|
|
3867
4241
|
skill: { tier: "native", reason: "skills API with single-zip upload (create + get + attach only)" },
|
|
3868
4242
|
agent: { tier: "native", reason: "managed agents API" },
|
|
4243
|
+
template: { tier: "unsupported", reason: "no Forward Template equivalent on Ark" },
|
|
3869
4244
|
memory_store: { tier: "native", reason: "memory_stores API" },
|
|
3870
4245
|
mcp_server: { tier: "native", reason: "mcp_servers field on agent" },
|
|
3871
4246
|
multiagent: { tier: "native", reason: "coordinator + roster topology" },
|
|
@@ -4021,13 +4396,22 @@ var environmentSchema = z5.object({
|
|
|
4021
4396
|
name: z5.string().optional(),
|
|
4022
4397
|
description: z5.string().optional(),
|
|
4023
4398
|
provider: z5.string().optional(),
|
|
4399
|
+
/** Pre-existing provider environment id (e.g. env_00xxxx). When set, the environment is treated as an external reference and will not be created/updated/deleted by OpenCMA. */
|
|
4400
|
+
environment_id: z5.string().optional(),
|
|
4024
4401
|
config: z5.object({
|
|
4025
|
-
type: z5.
|
|
4402
|
+
type: z5.enum(["cloud", "self_hosted"]),
|
|
4026
4403
|
networking: networkingSchema.optional(),
|
|
4027
4404
|
packages: packagesSchema.optional()
|
|
4028
4405
|
}),
|
|
4029
4406
|
metadata: z5.record(z5.string(), z5.string()).optional()
|
|
4030
4407
|
});
|
|
4408
|
+
var tunnelSchema = z5.object({
|
|
4409
|
+
name: z5.string().optional(),
|
|
4410
|
+
description: z5.string().optional(),
|
|
4411
|
+
/** Pre-existing Qoder tunnel id (e.g. tnl_00xxxx). Tunnels are allocated by Qoder BYOC admin and referenced, not created. */
|
|
4412
|
+
tunnel_id: z5.string(),
|
|
4413
|
+
metadata: z5.record(z5.string(), z5.string()).optional()
|
|
4414
|
+
});
|
|
4031
4415
|
var coerceString = z5.union([z5.string(), z5.number()]).transform(String);
|
|
4032
4416
|
var staticBearerCredentialSchema = z5.object({
|
|
4033
4417
|
name: z5.string(),
|
|
@@ -4136,12 +4520,16 @@ var agentSkillRefSchema = z5.object({
|
|
|
4136
4520
|
skill_id: String(s.skill_id ?? s.code),
|
|
4137
4521
|
version: s.version
|
|
4138
4522
|
}));
|
|
4523
|
+
var agentDeliverySchema = z5.object({
|
|
4524
|
+
type: z5.enum(["managed", "forward"])
|
|
4525
|
+
});
|
|
4139
4526
|
var agentSchema = z5.object({
|
|
4140
4527
|
name: z5.string().optional(),
|
|
4141
4528
|
description: z5.string().optional(),
|
|
4142
4529
|
model: z5.union([z5.string(), z5.record(z5.string(), z5.string())]),
|
|
4143
4530
|
instructions: z5.string(),
|
|
4144
4531
|
environment: z5.string().optional(),
|
|
4532
|
+
tunnel: z5.string().optional(),
|
|
4145
4533
|
provider: z5.string().optional(),
|
|
4146
4534
|
tools: toolsSchema.optional(),
|
|
4147
4535
|
mcp_servers: z5.array(mcpServerSchema).optional(),
|
|
@@ -4149,7 +4537,8 @@ var agentSchema = z5.object({
|
|
|
4149
4537
|
vault: z5.string().optional(),
|
|
4150
4538
|
memory_stores: z5.array(z5.string()).optional(),
|
|
4151
4539
|
multiagent: multiagentSchema.optional(),
|
|
4152
|
-
metadata: z5.record(z5.string(), z5.string()).optional()
|
|
4540
|
+
metadata: z5.record(z5.string(), z5.string()).optional(),
|
|
4541
|
+
delivery: z5.record(z5.string(), agentDeliverySchema).optional()
|
|
4153
4542
|
});
|
|
4154
4543
|
var deploymentFileResourceSchema = z5.object({
|
|
4155
4544
|
type: z5.literal("file"),
|
|
@@ -4196,6 +4585,7 @@ var deploymentSchema = z5.object({
|
|
|
4196
4585
|
agent: z5.string(),
|
|
4197
4586
|
agent_version: z5.number().int().optional(),
|
|
4198
4587
|
environment: z5.string().optional(),
|
|
4588
|
+
tunnel: z5.string().optional(),
|
|
4199
4589
|
vaults: z5.array(z5.string()).optional(),
|
|
4200
4590
|
memory_stores: z5.array(z5.string()).optional(),
|
|
4201
4591
|
resources: z5.array(deploymentResourceSchema).optional(),
|
|
@@ -4209,9 +4599,15 @@ var projectConfigSchema = z5.object({
|
|
|
4209
4599
|
version: z5.string(),
|
|
4210
4600
|
providers: z5.record(z5.string(), z5.unknown()),
|
|
4211
4601
|
defaults: z5.object({
|
|
4212
|
-
provider: z5.string().optional()
|
|
4602
|
+
provider: z5.string().optional(),
|
|
4603
|
+
session: z5.object({
|
|
4604
|
+
qoder: z5.object({
|
|
4605
|
+
identity_id: z5.string().min(1).optional()
|
|
4606
|
+
}).optional()
|
|
4607
|
+
}).optional()
|
|
4213
4608
|
}).optional(),
|
|
4214
4609
|
environments: z5.record(z5.string(), environmentSchema).optional(),
|
|
4610
|
+
tunnels: z5.record(z5.string(), tunnelSchema).optional(),
|
|
4215
4611
|
vaults: z5.record(z5.string(), vaultSchema).optional(),
|
|
4216
4612
|
memory_stores: z5.record(z5.string(), memoryStoreSchema).optional(),
|
|
4217
4613
|
skills: z5.record(z5.string(), skillSchema).optional(),
|
|
@@ -4328,6 +4724,7 @@ function getResourceDeclaration(address, config) {
|
|
|
4328
4724
|
case "skill":
|
|
4329
4725
|
return config.skills?.[name] ?? null;
|
|
4330
4726
|
case "agent":
|
|
4727
|
+
case "template":
|
|
4331
4728
|
return config.agents?.[name] ?? null;
|
|
4332
4729
|
case "file":
|
|
4333
4730
|
return config.files?.[name] ?? null;
|
|
@@ -4369,7 +4766,7 @@ function collectFiles(dir, base) {
|
|
|
4369
4766
|
}
|
|
4370
4767
|
|
|
4371
4768
|
// src/internal/planner/hasher.ts
|
|
4372
|
-
async function computeResourceHash(address, config, basePath) {
|
|
4769
|
+
async function computeResourceHash(address, config, basePath, state) {
|
|
4373
4770
|
const decl = getDeclaration(address, config);
|
|
4374
4771
|
if (!decl) return "";
|
|
4375
4772
|
if (address.type === "skill") {
|
|
@@ -4379,8 +4776,42 @@ async function computeResourceHash(address, config, basePath) {
|
|
|
4379
4776
|
return contentHash({ decl, fileHash });
|
|
4380
4777
|
}
|
|
4381
4778
|
}
|
|
4779
|
+
if (address.type === "deployment") {
|
|
4780
|
+
const refs = resolveDeploymentReferenceIds(decl, config, address.provider, state);
|
|
4781
|
+
if (refs) return contentHash({ decl, refs });
|
|
4782
|
+
}
|
|
4783
|
+
if (address.type === "template") {
|
|
4784
|
+
const refs = resolveTemplateReferenceIds(decl, config, address.provider, state);
|
|
4785
|
+
return contentHash({ decl, refs });
|
|
4786
|
+
}
|
|
4382
4787
|
return contentHash(decl);
|
|
4383
4788
|
}
|
|
4789
|
+
function resolveTemplateReferenceIds(decl, config, provider, state) {
|
|
4790
|
+
const environment = decl.environment ? config.environments?.[decl.environment] : void 0;
|
|
4791
|
+
const tunnel = decl.tunnel ? config.tunnels?.[decl.tunnel] : void 0;
|
|
4792
|
+
const skillIds = (decl.skills ?? []).map((skill) => {
|
|
4793
|
+
if (typeof skill === "string") {
|
|
4794
|
+
return state?.getResource({ type: "skill", name: skill, provider })?.remote_id ?? skill;
|
|
4795
|
+
}
|
|
4796
|
+
if (skill.type === "official") return `${skill.type}:${skill.skill_id}:${skill.version ?? ""}`;
|
|
4797
|
+
return state?.getResource({ type: "skill", name: skill.skill_id, provider })?.remote_id ?? `${skill.type}:${skill.skill_id}:${skill.version ?? ""}`;
|
|
4798
|
+
});
|
|
4799
|
+
return {
|
|
4800
|
+
environment_id: environment?.environment_id ?? (decl.environment ? state?.getResource({ type: "environment", name: decl.environment, provider })?.remote_id ?? void 0 : void 0),
|
|
4801
|
+
tunnel_id: tunnel?.tunnel_id,
|
|
4802
|
+
vault_ids: decl.vault ? [state?.getResource({ type: "vault", name: decl.vault, provider })?.remote_id ?? decl.vault] : [],
|
|
4803
|
+
skill_ids: skillIds
|
|
4804
|
+
};
|
|
4805
|
+
}
|
|
4806
|
+
function resolveDeploymentReferenceIds(decl, config, provider, state) {
|
|
4807
|
+
const agent = config.agents?.[decl.agent];
|
|
4808
|
+
const envName = decl.environment ?? agent?.environment;
|
|
4809
|
+
if (!envName) return void 0;
|
|
4810
|
+
const envDecl = config.environments?.[envName];
|
|
4811
|
+
return {
|
|
4812
|
+
environment_id: envDecl?.environment_id ?? (envDecl ? state?.getResource({ type: "environment", name: envName, provider })?.remote_id ?? void 0 : void 0)
|
|
4813
|
+
};
|
|
4814
|
+
}
|
|
4384
4815
|
function getDeclaration(address, config) {
|
|
4385
4816
|
return getResourceDeclaration(address, config);
|
|
4386
4817
|
}
|
|
@@ -4402,15 +4833,65 @@ function computeSkillContentHash(source, basePath) {
|
|
|
4402
4833
|
return "";
|
|
4403
4834
|
}
|
|
4404
4835
|
|
|
4836
|
+
// src/internal/planner/plan-semantics.ts
|
|
4837
|
+
var NON_BLOCKING_ROOT_FIELDS = /* @__PURE__ */ new Set(["description", "metadata"]);
|
|
4838
|
+
function diffChangedPaths(before, after, prefix = "") {
|
|
4839
|
+
if (Object.is(before, after)) return [];
|
|
4840
|
+
if (Array.isArray(before) || Array.isArray(after)) {
|
|
4841
|
+
return structurallyEqual(before, after) ? [] : [prefix || "$root"];
|
|
4842
|
+
}
|
|
4843
|
+
if (isRecord(before) && isRecord(after)) {
|
|
4844
|
+
const paths = [];
|
|
4845
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
4846
|
+
for (const key of [...keys].sort()) {
|
|
4847
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
4848
|
+
paths.push(...diffChangedPaths(before[key], after[key], path));
|
|
4849
|
+
}
|
|
4850
|
+
return paths;
|
|
4851
|
+
}
|
|
4852
|
+
return [prefix || "$root"];
|
|
4853
|
+
}
|
|
4854
|
+
function classifyReadinessImpact(action, changedPaths) {
|
|
4855
|
+
if (action === "no-op") return "none";
|
|
4856
|
+
if (action !== "update" || !changedPaths || changedPaths.length === 0) return "blocking";
|
|
4857
|
+
return changedPaths.every(isNonBlockingPath) ? "non_blocking" : "blocking";
|
|
4858
|
+
}
|
|
4859
|
+
function buildReadinessBaseline(declaration) {
|
|
4860
|
+
const record = isRecord(declaration) ? declaration : {};
|
|
4861
|
+
const { description: _description, metadata: _metadata, ...operational } = record;
|
|
4862
|
+
return {
|
|
4863
|
+
operational_hash: contentHash(operational),
|
|
4864
|
+
description_hash: contentHash(record.description ?? null),
|
|
4865
|
+
metadata_hash: contentHash(record.metadata ?? null)
|
|
4866
|
+
};
|
|
4867
|
+
}
|
|
4868
|
+
function diffReadinessBaseline(before, after) {
|
|
4869
|
+
const paths = [];
|
|
4870
|
+
if (before.operational_hash !== after.operational_hash) paths.push("$operational");
|
|
4871
|
+
if (before.description_hash !== after.description_hash) paths.push("description");
|
|
4872
|
+
if (before.metadata_hash !== after.metadata_hash) paths.push("metadata");
|
|
4873
|
+
return paths;
|
|
4874
|
+
}
|
|
4875
|
+
function isNonBlockingPath(path) {
|
|
4876
|
+
const root = path.split(".", 1)[0];
|
|
4877
|
+
return root !== void 0 && NON_BLOCKING_ROOT_FIELDS.has(root);
|
|
4878
|
+
}
|
|
4879
|
+
function isRecord(value) {
|
|
4880
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4881
|
+
}
|
|
4882
|
+
function structurallyEqual(left, right) {
|
|
4883
|
+
return contentHash(left) === contentHash(right);
|
|
4884
|
+
}
|
|
4885
|
+
|
|
4405
4886
|
// src/internal/providers/drift-support.ts
|
|
4406
4887
|
function supportsFullDrift(adapter, type) {
|
|
4407
4888
|
return adapter.getDriftSupport?.(type) === "full" && typeof adapter.readComparableResource === "function";
|
|
4408
4889
|
}
|
|
4409
4890
|
async function readComparableIfSupported(adapter, type, id, name) {
|
|
4410
|
-
|
|
4411
|
-
if (typeof
|
|
4891
|
+
if (!supportsFullDrift(adapter, type)) return null;
|
|
4892
|
+
if (typeof adapter.readComparableResource !== "function") return null;
|
|
4412
4893
|
try {
|
|
4413
|
-
return await
|
|
4894
|
+
return await adapter.readComparableResource(type, id, name);
|
|
4414
4895
|
} catch {
|
|
4415
4896
|
return null;
|
|
4416
4897
|
}
|
|
@@ -4473,6 +4954,22 @@ function resolveAgentRefs(agentName, config, provider, state) {
|
|
|
4473
4954
|
}
|
|
4474
4955
|
return refs;
|
|
4475
4956
|
}
|
|
4957
|
+
function resolveTemplateRefs(agentName, config, provider, state) {
|
|
4958
|
+
const agent = config.agents?.[agentName];
|
|
4959
|
+
if (!agent) throw new UserError(`Agent '${agentName}' not found in config`);
|
|
4960
|
+
if (!agent.environment) {
|
|
4961
|
+
throw new UserError(`Forward template '${agentName}' must declare an environment.`);
|
|
4962
|
+
}
|
|
4963
|
+
const environment = config.environments?.[agent.environment];
|
|
4964
|
+
if (!environment) throw new UserError(`Environment '${agent.environment}' is not defined in config.`);
|
|
4965
|
+
const agentRefs = resolveAgentRefs(agentName, config, provider, state);
|
|
4966
|
+
return {
|
|
4967
|
+
...agentRefs,
|
|
4968
|
+
environment_id: environment.environment_id ?? requireRef(state, { type: "environment", name: agent.environment, provider }),
|
|
4969
|
+
...agent.tunnel ? { tunnel_id: resolveTunnelIdFromConfig(config, agent.tunnel, provider) } : {},
|
|
4970
|
+
vault_ids: agent.vault ? [requireRef(state, { type: "vault", name: agent.vault, provider })] : []
|
|
4971
|
+
};
|
|
4972
|
+
}
|
|
4476
4973
|
function resolveDeploymentRefs(deploymentName, config, provider, state) {
|
|
4477
4974
|
const dep = config.deployments?.[deploymentName];
|
|
4478
4975
|
if (!dep) throw new UserError(`Deployment '${deploymentName}' not found in config`);
|
|
@@ -4491,11 +4988,17 @@ function resolveDeploymentRefs(deploymentName, config, provider, state) {
|
|
|
4491
4988
|
`Deployment '${deploymentName}' has no environment and agent '${dep.agent}' does not declare one`
|
|
4492
4989
|
);
|
|
4493
4990
|
}
|
|
4494
|
-
const
|
|
4991
|
+
const envDecl = config.environments?.[envName];
|
|
4992
|
+
if (!envDecl) {
|
|
4993
|
+
throw new UserError(`Environment '${envName}' is not defined in config.`);
|
|
4994
|
+
}
|
|
4995
|
+
const environment_id = envDecl.environment_id ?? requireRef(state, {
|
|
4495
4996
|
type: "environment",
|
|
4496
4997
|
name: envName,
|
|
4497
4998
|
provider
|
|
4498
4999
|
});
|
|
5000
|
+
const tunnelName = dep.tunnel ?? agent.tunnel;
|
|
5001
|
+
const tunnel_id = tunnelName ? resolveTunnelIdFromConfig(config, tunnelName, provider) : void 0;
|
|
4499
5002
|
const vaultNames = dep.vaults ?? (agent.vault ? [agent.vault] : []);
|
|
4500
5003
|
const vault_ids = vaultNames.map((v) => requireRef(state, { type: "vault", name: v, provider }));
|
|
4501
5004
|
const msNames = /* @__PURE__ */ new Set();
|
|
@@ -4518,10 +5021,21 @@ function resolveDeploymentRefs(deploymentName, config, provider, state) {
|
|
|
4518
5021
|
agent_id,
|
|
4519
5022
|
agent_version: dep.agent_version,
|
|
4520
5023
|
environment_id,
|
|
5024
|
+
tunnel_id,
|
|
4521
5025
|
vault_ids,
|
|
4522
5026
|
memory_store_ids
|
|
4523
5027
|
};
|
|
4524
5028
|
}
|
|
5029
|
+
function resolveTunnelIdFromConfig(config, tunnelName, provider) {
|
|
5030
|
+
if (provider !== "qoder") {
|
|
5031
|
+
throw new UserError("Tunnels are supported only by Qoder BYOC sessions.");
|
|
5032
|
+
}
|
|
5033
|
+
const tunnel = config.tunnels?.[tunnelName];
|
|
5034
|
+
if (!tunnel) {
|
|
5035
|
+
throw new UserError(`Tunnel '${tunnelName}' is not defined in config. Declare it under the 'tunnels:' section.`);
|
|
5036
|
+
}
|
|
5037
|
+
return tunnel.tunnel_id;
|
|
5038
|
+
}
|
|
4525
5039
|
|
|
4526
5040
|
// src/internal/executor/skill-resolver.ts
|
|
4527
5041
|
import { readFileSync as readFileSync7, statSync as statSync3 } from "fs";
|
|
@@ -4562,6 +5076,17 @@ async function executePlan(plan, ctx, options = {}) {
|
|
|
4562
5076
|
const concurrency = clampConcurrency(options.concurrency);
|
|
4563
5077
|
const resultsByKey = /* @__PURE__ */ new Map();
|
|
4564
5078
|
const failed = /* @__PURE__ */ new Set();
|
|
5079
|
+
let stateUpdated = false;
|
|
5080
|
+
for (const action of plan.actions) {
|
|
5081
|
+
if (action.address.type !== "environment") continue;
|
|
5082
|
+
const decl = ctx.config.environments?.[action.address.name];
|
|
5083
|
+
const existing = ctx.state.getResource(action.address);
|
|
5084
|
+
if (decl?.environment_id && existing && !existing.externally_managed) {
|
|
5085
|
+
ctx.state.setResource({ ...existing, externally_managed: true });
|
|
5086
|
+
stateUpdated = true;
|
|
5087
|
+
}
|
|
5088
|
+
}
|
|
5089
|
+
if (stateUpdated) await ctx.state.save();
|
|
4565
5090
|
const actionable = plan.actions.filter((a) => a.action !== "no-op");
|
|
4566
5091
|
const mutations = actionable.filter((a) => a.action !== "delete");
|
|
4567
5092
|
const deletions = actionable.filter((a) => a.action === "delete");
|
|
@@ -4677,6 +5202,22 @@ function buildActionLevels(actions) {
|
|
|
4677
5202
|
return levels;
|
|
4678
5203
|
}
|
|
4679
5204
|
async function executeAction(action, provider, ctx) {
|
|
5205
|
+
try {
|
|
5206
|
+
return await executeActionInner(action, provider, ctx);
|
|
5207
|
+
} catch (err) {
|
|
5208
|
+
if (action.action !== "update" || !ApiError.isNotFound(err)) throw err;
|
|
5209
|
+
emitRuntimeFeedback(ctx.onFeedback, {
|
|
5210
|
+
type: "resource_already_gone",
|
|
5211
|
+
level: "warning",
|
|
5212
|
+
action,
|
|
5213
|
+
resource: action.address,
|
|
5214
|
+
message: `update ${action.address.type}.${action.address.name} (${action.address.provider}) \u2014 not found remotely, recreating`
|
|
5215
|
+
});
|
|
5216
|
+
ctx.state.removeResource(action.address);
|
|
5217
|
+
return executeActionInner({ ...action, action: "create" }, provider, ctx);
|
|
5218
|
+
}
|
|
5219
|
+
}
|
|
5220
|
+
async function executeActionInner(action, provider, ctx) {
|
|
4680
5221
|
const { address } = action;
|
|
4681
5222
|
const { type, name } = address;
|
|
4682
5223
|
let adopted = false;
|
|
@@ -4684,6 +5225,13 @@ async function executeAction(action, provider, ctx) {
|
|
|
4684
5225
|
const existing = ctx.state.getResource(address);
|
|
4685
5226
|
if (!existing) return false;
|
|
4686
5227
|
const id = existing.remote_id;
|
|
5228
|
+
if (type === "environment") {
|
|
5229
|
+
const decl = ctx.config.environments?.[name];
|
|
5230
|
+
if (existing.externally_managed || decl?.environment_id) {
|
|
5231
|
+
ctx.state.removeResource(address);
|
|
5232
|
+
return false;
|
|
5233
|
+
}
|
|
5234
|
+
}
|
|
4687
5235
|
if (id !== null) {
|
|
4688
5236
|
try {
|
|
4689
5237
|
switch (type) {
|
|
@@ -4699,6 +5247,11 @@ async function executeAction(action, provider, ctx) {
|
|
|
4699
5247
|
case "agent":
|
|
4700
5248
|
await provider.deleteAgent(id);
|
|
4701
5249
|
break;
|
|
5250
|
+
case "template":
|
|
5251
|
+
if (!provider.archiveTemplate)
|
|
5252
|
+
throw new UserError(`Provider '${address.provider}' does not support templates`);
|
|
5253
|
+
await provider.archiveTemplate(id);
|
|
5254
|
+
break;
|
|
4702
5255
|
case "memory_store":
|
|
4703
5256
|
if (!provider.deleteMemoryStore) throw memoryStoreUnsupported(address.provider);
|
|
4704
5257
|
await provider.deleteMemoryStore(id);
|
|
@@ -4730,7 +5283,22 @@ async function executeAction(action, provider, ctx) {
|
|
|
4730
5283
|
case "environment": {
|
|
4731
5284
|
const decl = ctx.config.environments[name];
|
|
4732
5285
|
const remoteName = decl.name ?? name;
|
|
4733
|
-
if (
|
|
5286
|
+
if (decl.environment_id) {
|
|
5287
|
+
result = { id: decl.environment_id, type: "environment" };
|
|
5288
|
+
emitRuntimeFeedback(ctx.onFeedback, {
|
|
5289
|
+
type: "resource_action_success",
|
|
5290
|
+
level: "info",
|
|
5291
|
+
action,
|
|
5292
|
+
resource: action.address,
|
|
5293
|
+
message: `${action.action} ${action.address.type}.${action.address.name} (${action.address.provider}) \u2014 external reference, no remote mutation`
|
|
5294
|
+
});
|
|
5295
|
+
} else if (isUpdate) {
|
|
5296
|
+
const prior = ctx.state.getResource(address);
|
|
5297
|
+
if (prior?.externally_managed) {
|
|
5298
|
+
throw new UserError(
|
|
5299
|
+
`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).`
|
|
5300
|
+
);
|
|
5301
|
+
}
|
|
4734
5302
|
result = await provider.updateEnvironment(existingId, remoteName, decl);
|
|
4735
5303
|
} else {
|
|
4736
5304
|
try {
|
|
@@ -4860,6 +5428,29 @@ async function executeAction(action, provider, ctx) {
|
|
|
4860
5428
|
}
|
|
4861
5429
|
break;
|
|
4862
5430
|
}
|
|
5431
|
+
case "template": {
|
|
5432
|
+
const createTemplate = provider.createTemplate?.bind(provider);
|
|
5433
|
+
const updateTemplate = provider.updateTemplate?.bind(provider);
|
|
5434
|
+
if (!createTemplate || !updateTemplate) {
|
|
5435
|
+
throw new UserError(`Provider '${address.provider}' does not support templates`);
|
|
5436
|
+
}
|
|
5437
|
+
const decl = ctx.config.agents[name];
|
|
5438
|
+
const remoteName = decl.name ?? name;
|
|
5439
|
+
const refs = resolveTemplateRefs(name, ctx.config, address.provider, ctx.state);
|
|
5440
|
+
if (isUpdate) {
|
|
5441
|
+
result = await updateTemplate(existingId, remoteName, decl, refs);
|
|
5442
|
+
} else {
|
|
5443
|
+
try {
|
|
5444
|
+
result = await createTemplate(remoteName, decl, refs);
|
|
5445
|
+
} catch (err) {
|
|
5446
|
+
result = await adoptOnConflict(err, address, provider, ctx.onFeedback, {
|
|
5447
|
+
onExisting: (existing) => updateTemplate(existing.id, remoteName, decl, refs)
|
|
5448
|
+
});
|
|
5449
|
+
adopted = true;
|
|
5450
|
+
}
|
|
5451
|
+
}
|
|
5452
|
+
break;
|
|
5453
|
+
}
|
|
4863
5454
|
case "deployment": {
|
|
4864
5455
|
const decl = ctx.config.deployments[name];
|
|
4865
5456
|
const refs = resolveDeploymentRefs(name, ctx.config, address.provider, ctx.state);
|
|
@@ -4899,7 +5490,7 @@ async function executeAction(action, provider, ctx) {
|
|
|
4899
5490
|
default:
|
|
4900
5491
|
throw new UserError(`Unknown resource type: ${type}`);
|
|
4901
5492
|
}
|
|
4902
|
-
const hash = await computeResourceHash(address, ctx.config, ctx.configPath);
|
|
5493
|
+
const hash = await computeResourceHash(address, ctx.config, ctx.configPath, ctx.state);
|
|
4903
5494
|
const comparableHash = computeComparableDesiredHash(address, ctx.config, provider);
|
|
4904
5495
|
let remoteHash = comparableHash;
|
|
4905
5496
|
let remoteSnapshot;
|
|
@@ -4908,15 +5499,19 @@ async function executeAction(action, provider, ctx) {
|
|
|
4908
5499
|
remoteHash = contentHash(remote.comparable);
|
|
4909
5500
|
remoteSnapshot = remote.snapshot ?? remote.comparable;
|
|
4910
5501
|
}
|
|
5502
|
+
const priorResource = ctx.state.getResource(address);
|
|
4911
5503
|
ctx.state.setResource({
|
|
4912
5504
|
address,
|
|
4913
5505
|
remote_id: result.id,
|
|
5506
|
+
externally_managed: priorResource?.externally_managed || type === "environment" && ctx.config.environments?.[name]?.environment_id ? true : void 0,
|
|
4914
5507
|
version: result.version,
|
|
4915
5508
|
content_hash: hash,
|
|
4916
5509
|
desired_hash: hash,
|
|
4917
5510
|
desired_comparable_hash: remoteHash,
|
|
5511
|
+
desired_readiness_baseline: buildReadinessBaseline(getResourceDeclaration(address, ctx.config)),
|
|
4918
5512
|
remote_hash: remoteHash,
|
|
4919
5513
|
remote_snapshot: remoteSnapshot,
|
|
5514
|
+
drift_paths: [],
|
|
4920
5515
|
drift_status: remoteHash ? "in_sync" : void 0
|
|
4921
5516
|
});
|
|
4922
5517
|
return adopted;
|
|
@@ -5037,6 +5632,7 @@ function resolveTargetProviders(config) {
|
|
|
5037
5632
|
}
|
|
5038
5633
|
function collectReferenceDiagnostics(config, diagnostics) {
|
|
5039
5634
|
const envNames = new Set(Object.keys(config.environments ?? {}));
|
|
5635
|
+
const tunnelNames = new Set(Object.keys(config.tunnels ?? {}));
|
|
5040
5636
|
const skillNames = new Set(Object.keys(config.skills ?? {}));
|
|
5041
5637
|
const vaultNames = new Set(Object.keys(config.vaults ?? {}));
|
|
5042
5638
|
const memoryNames = new Set(Object.keys(config.memory_stores ?? {}));
|
|
@@ -5048,6 +5644,9 @@ function collectReferenceDiagnostics(config, diagnostics) {
|
|
|
5048
5644
|
`agent.${name}: references unknown environment '${agent.environment}'`
|
|
5049
5645
|
);
|
|
5050
5646
|
}
|
|
5647
|
+
if (agent.tunnel && !tunnelNames.has(agent.tunnel)) {
|
|
5648
|
+
diagnostics.error("config.agent.tunnel.unknown", `agent.${name}: references unknown tunnel '${agent.tunnel}'`);
|
|
5649
|
+
}
|
|
5051
5650
|
for (const skill of agent.skills ?? []) {
|
|
5052
5651
|
if (typeof skill !== "string") continue;
|
|
5053
5652
|
if (!skillNames.has(skill)) {
|
|
@@ -5079,6 +5678,14 @@ function collectReferenceDiagnostics(config, diagnostics) {
|
|
|
5079
5678
|
}
|
|
5080
5679
|
}
|
|
5081
5680
|
}
|
|
5681
|
+
for (const [name, deployment] of Object.entries(config.deployments ?? {})) {
|
|
5682
|
+
if (deployment.tunnel && !tunnelNames.has(deployment.tunnel)) {
|
|
5683
|
+
diagnostics.error(
|
|
5684
|
+
"config.deployment.tunnel.unknown",
|
|
5685
|
+
`deployment.${name}: references unknown tunnel '${deployment.tunnel}'`
|
|
5686
|
+
);
|
|
5687
|
+
}
|
|
5688
|
+
}
|
|
5082
5689
|
}
|
|
5083
5690
|
function collectProviderCapabilities(config, providers, diagnostics) {
|
|
5084
5691
|
for (const providerName of providers) {
|
|
@@ -5091,6 +5698,91 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
5091
5698
|
continue;
|
|
5092
5699
|
}
|
|
5093
5700
|
const caps = def.capabilities;
|
|
5701
|
+
for (const [name, agent] of Object.entries(config.agents ?? {})) {
|
|
5702
|
+
if (agent.provider && agent.provider !== providerName) continue;
|
|
5703
|
+
const delivery = agent.delivery?.[providerName]?.type ?? "managed";
|
|
5704
|
+
if (delivery === "forward" && !isSupported(caps, "template")) {
|
|
5705
|
+
diagnostics.error(
|
|
5706
|
+
`${providerName}.agent.delivery.forward.unsupported`,
|
|
5707
|
+
`agent.${name}: provider '${providerName}' does not support delivery type 'forward'. Supported delivery types: managed.`,
|
|
5708
|
+
{ type: "agent", name, provider: providerName }
|
|
5709
|
+
);
|
|
5710
|
+
}
|
|
5711
|
+
if (delivery === "forward" && providerName === "qoder") {
|
|
5712
|
+
if (!agent.environment) {
|
|
5713
|
+
diagnostics.error(
|
|
5714
|
+
"qoder.template.environment.required",
|
|
5715
|
+
`agent.${name}: Qoder Forward delivery requires an environment.`,
|
|
5716
|
+
{ type: "template", name, provider: providerName }
|
|
5717
|
+
);
|
|
5718
|
+
}
|
|
5719
|
+
if (agent.memory_stores?.length) {
|
|
5720
|
+
diagnostics.error(
|
|
5721
|
+
"qoder.template.memory_store.unsupported",
|
|
5722
|
+
`agent.${name}: memory_stores are not yet supported by Qoder Forward Template delivery.`,
|
|
5723
|
+
{ type: "template", name, provider: providerName }
|
|
5724
|
+
);
|
|
5725
|
+
}
|
|
5726
|
+
if (agent.multiagent) {
|
|
5727
|
+
diagnostics.error(
|
|
5728
|
+
"qoder.template.multiagent.unsupported",
|
|
5729
|
+
`agent.${name}: multiagent is not yet supported by Qoder Forward Template delivery.`,
|
|
5730
|
+
{ type: "template", name, provider: providerName }
|
|
5731
|
+
);
|
|
5732
|
+
}
|
|
5733
|
+
}
|
|
5734
|
+
}
|
|
5735
|
+
if (providerName === "qoder") {
|
|
5736
|
+
for (const [name, deployment] of Object.entries(config.deployments ?? {})) {
|
|
5737
|
+
if (deployment.provider && deployment.provider !== providerName) continue;
|
|
5738
|
+
const tunnel = deployment.tunnel ?? config.agents?.[deployment.agent]?.tunnel;
|
|
5739
|
+
const referencedAgent = config.agents?.[deployment.agent];
|
|
5740
|
+
if (referencedAgent?.delivery?.qoder?.type === "forward") {
|
|
5741
|
+
diagnostics.error(
|
|
5742
|
+
"qoder.deployment.forward_template.unsupported",
|
|
5743
|
+
`deployment.${name}: managed deployments cannot reference Forward-delivered agent '${deployment.agent}'.`,
|
|
5744
|
+
{ type: "deployment", name, provider: providerName }
|
|
5745
|
+
);
|
|
5746
|
+
}
|
|
5747
|
+
if (tunnel) {
|
|
5748
|
+
diagnostics.warning(
|
|
5749
|
+
`${providerName}.deployment.tunnel.unsupported`,
|
|
5750
|
+
`deployment.${name}: Qoder's deployment API does not accept tunnel_id; scheduled and triggered runs execute in the deployment's environment but without the BYOC tunnel. Create sessions directly for private-network MCP access.`,
|
|
5751
|
+
{ type: "deployment", name, provider: providerName }
|
|
5752
|
+
);
|
|
5753
|
+
}
|
|
5754
|
+
}
|
|
5755
|
+
}
|
|
5756
|
+
if (providerName !== "qoder") {
|
|
5757
|
+
for (const [name, env] of Object.entries(config.environments ?? {})) {
|
|
5758
|
+
if (env.environment_id) continue;
|
|
5759
|
+
if (env.config.type === "self_hosted" && (!env.provider || env.provider === providerName)) {
|
|
5760
|
+
diagnostics.error(
|
|
5761
|
+
`${providerName}.environment.self_hosted.unsupported`,
|
|
5762
|
+
`environment.${name}: self_hosted environments are supported only by Qoder BYOC; use type 'cloud' or pin this environment to the qoder provider.`,
|
|
5763
|
+
{ type: "environment", name, provider: providerName }
|
|
5764
|
+
);
|
|
5765
|
+
}
|
|
5766
|
+
}
|
|
5767
|
+
for (const [name, agent] of Object.entries(config.agents ?? {})) {
|
|
5768
|
+
if (agent.tunnel && (!agent.provider || agent.provider === providerName)) {
|
|
5769
|
+
diagnostics.error(
|
|
5770
|
+
`${providerName}.agent.tunnel.unsupported`,
|
|
5771
|
+
"Tunnels are supported only by Qoder BYOC sessions.",
|
|
5772
|
+
{ type: "agent", name, provider: providerName }
|
|
5773
|
+
);
|
|
5774
|
+
}
|
|
5775
|
+
}
|
|
5776
|
+
for (const [name, deployment] of Object.entries(config.deployments ?? {})) {
|
|
5777
|
+
if (deployment.tunnel && (!deployment.provider || deployment.provider === providerName)) {
|
|
5778
|
+
diagnostics.error(
|
|
5779
|
+
`${providerName}.deployment.tunnel.unsupported`,
|
|
5780
|
+
"Tunnels are supported only by Qoder BYOC sessions.",
|
|
5781
|
+
{ type: "deployment", name, provider: providerName }
|
|
5782
|
+
);
|
|
5783
|
+
}
|
|
5784
|
+
}
|
|
5785
|
+
}
|
|
5094
5786
|
for (const missing of findMissingBailianMcpToolConfigs(config, providerName)) {
|
|
5095
5787
|
diagnostics.error(
|
|
5096
5788
|
`${providerName}.agent.mcp_toolkit_missing`,
|
|
@@ -5159,6 +5851,13 @@ function collectProviderCapabilities(config, providers, diagnostics) {
|
|
|
5159
5851
|
}
|
|
5160
5852
|
}
|
|
5161
5853
|
|
|
5854
|
+
// src/internal/core/agent-materialization.ts
|
|
5855
|
+
function resolveAgentMaterialization(provider, agent) {
|
|
5856
|
+
const mode = agent.delivery?.[provider]?.type ?? "managed";
|
|
5857
|
+
if (mode === "managed") return { resourceType: "agent", mode };
|
|
5858
|
+
return { resourceType: "template", mode };
|
|
5859
|
+
}
|
|
5860
|
+
|
|
5162
5861
|
// src/internal/graph/dependency.ts
|
|
5163
5862
|
function buildDependencyGraph(config, targetProviders) {
|
|
5164
5863
|
const nodes = /* @__PURE__ */ new Map();
|
|
@@ -5216,7 +5915,8 @@ function buildDependencyGraph(config, targetProviders) {
|
|
|
5216
5915
|
for (const name of Object.keys(config.agents)) {
|
|
5217
5916
|
const decl = config.agents[name];
|
|
5218
5917
|
if (decl.provider && decl.provider !== provider) continue;
|
|
5219
|
-
const
|
|
5918
|
+
const materialization = resolveAgentMaterialization(provider, decl);
|
|
5919
|
+
const agentAddr = { type: materialization.resourceType, name, provider };
|
|
5220
5920
|
addNode(agentAddr);
|
|
5221
5921
|
if (decl.environment && config.environments?.[decl.environment]) {
|
|
5222
5922
|
const envAddr = {
|
|
@@ -5258,7 +5958,9 @@ function buildDependencyGraph(config, targetProviders) {
|
|
|
5258
5958
|
}
|
|
5259
5959
|
if (decl.multiagent && isSupported(caps, "multiagent")) {
|
|
5260
5960
|
for (const subName of decl.multiagent.agents) {
|
|
5261
|
-
const
|
|
5961
|
+
const subDecl = config.agents[subName];
|
|
5962
|
+
const subType = subDecl ? resolveAgentMaterialization(provider, subDecl).resourceType : "agent";
|
|
5963
|
+
const subAddr = { type: subType, name: subName, provider };
|
|
5262
5964
|
addEdge(agentAddr, subAddr);
|
|
5263
5965
|
}
|
|
5264
5966
|
}
|
|
@@ -5270,7 +5972,9 @@ function buildDependencyGraph(config, targetProviders) {
|
|
|
5270
5972
|
if (decl.provider && decl.provider !== provider) continue;
|
|
5271
5973
|
const depAddr = { type: "deployment", name, provider };
|
|
5272
5974
|
addNode(depAddr);
|
|
5273
|
-
const
|
|
5975
|
+
const agentDecl = config.agents?.[decl.agent];
|
|
5976
|
+
const agentType = agentDecl ? resolveAgentMaterialization(provider, agentDecl).resourceType : "agent";
|
|
5977
|
+
const agentAddr = { type: agentType, name: decl.agent, provider };
|
|
5274
5978
|
if (nodes.has(addressKey(agentAddr))) addEdge(depAddr, agentAddr);
|
|
5275
5979
|
if (decl.environment) {
|
|
5276
5980
|
const envAddr = { type: "environment", name: decl.environment, provider };
|
|
@@ -5338,26 +6042,57 @@ async function buildPlan(config, state, options = {}) {
|
|
|
5338
6042
|
for (const res of state.resources) {
|
|
5339
6043
|
stateIndex.set(addressKey(res.address), res);
|
|
5340
6044
|
}
|
|
6045
|
+
const remoteIdLookup = /* @__PURE__ */ new Map();
|
|
6046
|
+
for (const res of state.resources) {
|
|
6047
|
+
remoteIdLookup.set(addressKey(res.address), res);
|
|
6048
|
+
}
|
|
6049
|
+
const hashStateLookup = { getResource: (addr) => remoteIdLookup.get(addressKey(addr)) };
|
|
5341
6050
|
for (const address of sorted) {
|
|
5342
6051
|
const key = addressKey(address);
|
|
5343
|
-
const desiredHash = await computeResourceHash(address, config, options.configPath);
|
|
6052
|
+
const desiredHash = await computeResourceHash(address, config, options.configPath, hashStateLookup);
|
|
5344
6053
|
const existing = stateIndex.get(key);
|
|
5345
6054
|
const deps = getDependencies(address, graph);
|
|
6055
|
+
if (address.type === "environment" && existing) {
|
|
6056
|
+
const envDecl = config.environments?.[address.name];
|
|
6057
|
+
if (existing.externally_managed && envDecl && !envDecl.environment_id) {
|
|
6058
|
+
diagnostics.error(
|
|
6059
|
+
"plan.environment.ownership_transition",
|
|
6060
|
+
`environment.${address.name} is recorded as an external reference (${existing.remote_id ?? "unknown id"}); removing 'environment_id' would make OpenCMA modify and eventually delete a remote environment it does not own. Restore 'environment_id' to keep it as a reference, or release it first with 'agents state rm environment.${address.name}' (then 'agents state import' to adopt the remote as a managed resource).`,
|
|
6061
|
+
address
|
|
6062
|
+
);
|
|
6063
|
+
stateIndex.delete(key);
|
|
6064
|
+
continue;
|
|
6065
|
+
}
|
|
6066
|
+
if (!existing.externally_managed && existing.remote_id && envDecl?.environment_id && envDecl.environment_id !== existing.remote_id) {
|
|
6067
|
+
diagnostics.warning(
|
|
6068
|
+
"plan.environment.ownership_orphan",
|
|
6069
|
+
`environment.${address.name}: switching to external reference '${envDecl.environment_id}' orphans the previously managed remote environment '${existing.remote_id}' \u2014 it will no longer be tracked or deletable by OpenCMA.`,
|
|
6070
|
+
address
|
|
6071
|
+
);
|
|
6072
|
+
}
|
|
6073
|
+
}
|
|
6074
|
+
const isExternalEnv = address.type === "environment" && Boolean(config.environments?.[address.name]?.environment_id);
|
|
6075
|
+
const createReason = isExternalEnv ? "Record external environment reference (no remote mutation)" : "Resource does not exist in state";
|
|
6076
|
+
const updateSuffix = isExternalEnv ? " \u2014 external reference, no remote mutation" : "";
|
|
5346
6077
|
if (!existing) {
|
|
5347
6078
|
actions.push({
|
|
5348
6079
|
action: "create",
|
|
5349
6080
|
address,
|
|
5350
6081
|
driftKind: "none",
|
|
5351
|
-
|
|
6082
|
+
readinessImpact: "blocking",
|
|
6083
|
+
reason: createReason,
|
|
5352
6084
|
after: { content_hash: desiredHash },
|
|
5353
6085
|
dependencies: deps
|
|
5354
6086
|
});
|
|
5355
6087
|
} else if ((existing.desired_hash ?? existing.content_hash) !== desiredHash && existing.drift_status === "drifted") {
|
|
6088
|
+
const changedPaths = collectChangedPaths(address, config, existing, true);
|
|
5356
6089
|
actions.push({
|
|
5357
6090
|
action: "update",
|
|
5358
6091
|
address,
|
|
5359
6092
|
driftKind: "both",
|
|
5360
|
-
|
|
6093
|
+
readinessImpact: classifyReadinessImpact("update", changedPaths),
|
|
6094
|
+
changedPaths,
|
|
6095
|
+
reason: `Local config changed and remote drift detected${updateSuffix}`,
|
|
5361
6096
|
before: {
|
|
5362
6097
|
content_hash: existing.desired_hash ?? existing.content_hash,
|
|
5363
6098
|
remote_hash: existing.remote_hash,
|
|
@@ -5367,21 +6102,27 @@ async function buildPlan(config, state, options = {}) {
|
|
|
5367
6102
|
dependencies: deps
|
|
5368
6103
|
});
|
|
5369
6104
|
} else if ((existing.desired_hash ?? existing.content_hash) !== desiredHash) {
|
|
6105
|
+
const changedPaths = collectChangedPaths(address, config, existing, false);
|
|
5370
6106
|
actions.push({
|
|
5371
6107
|
action: "update",
|
|
5372
6108
|
address,
|
|
5373
6109
|
driftKind: "local",
|
|
5374
|
-
|
|
6110
|
+
readinessImpact: classifyReadinessImpact("update", changedPaths),
|
|
6111
|
+
changedPaths,
|
|
6112
|
+
reason: `Local config changed${updateSuffix}`,
|
|
5375
6113
|
before: { content_hash: existing.desired_hash ?? existing.content_hash },
|
|
5376
6114
|
after: { content_hash: desiredHash },
|
|
5377
6115
|
dependencies: deps
|
|
5378
6116
|
});
|
|
5379
6117
|
} else if (existing.drift_status === "drifted") {
|
|
6118
|
+
const changedPaths = existing.drift_paths;
|
|
5380
6119
|
actions.push({
|
|
5381
6120
|
action: "update",
|
|
5382
6121
|
address,
|
|
5383
6122
|
driftKind: "remote",
|
|
5384
|
-
|
|
6123
|
+
readinessImpact: classifyReadinessImpact("update", changedPaths),
|
|
6124
|
+
changedPaths,
|
|
6125
|
+
reason: `Remote drift detected${updateSuffix}`,
|
|
5385
6126
|
before: {
|
|
5386
6127
|
content_hash: existing.desired_hash ?? existing.content_hash,
|
|
5387
6128
|
remote_hash: existing.remote_hash,
|
|
@@ -5395,6 +6136,7 @@ async function buildPlan(config, state, options = {}) {
|
|
|
5395
6136
|
action: "no-op",
|
|
5396
6137
|
address,
|
|
5397
6138
|
driftKind: "none",
|
|
6139
|
+
readinessImpact: "none",
|
|
5398
6140
|
reason: existing.drift_status === "unchecked" ? "No changes detected (remote content drift unchecked)" : "No changes detected",
|
|
5399
6141
|
dependencies: deps
|
|
5400
6142
|
});
|
|
@@ -5403,17 +6145,32 @@ async function buildPlan(config, state, options = {}) {
|
|
|
5403
6145
|
}
|
|
5404
6146
|
const toDelete = Array.from(stateIndex.values()).reverse();
|
|
5405
6147
|
for (const res of toDelete) {
|
|
6148
|
+
const replacement = deliveryReplacementAddress(res.address, graph);
|
|
5406
6149
|
actions.push({
|
|
5407
6150
|
action: "delete",
|
|
5408
6151
|
address: res.address,
|
|
5409
6152
|
driftKind: "none",
|
|
5410
|
-
|
|
6153
|
+
readinessImpact: "blocking",
|
|
6154
|
+
reason: res.externally_managed ? "Remove local reference only \u2014 externally managed remote resource is left intact" : "Resource removed from configuration",
|
|
5411
6155
|
before: { content_hash: res.desired_hash ?? res.content_hash },
|
|
5412
|
-
dependencies: []
|
|
6156
|
+
dependencies: replacement ? [replacement] : []
|
|
5413
6157
|
});
|
|
5414
6158
|
}
|
|
5415
6159
|
return { actions, diagnostics: diagnostics.getAll() };
|
|
5416
6160
|
}
|
|
6161
|
+
function deliveryReplacementAddress(address, graph) {
|
|
6162
|
+
if (address.type !== "agent" && address.type !== "template") return void 0;
|
|
6163
|
+
const replacementType = address.type === "agent" ? "template" : "agent";
|
|
6164
|
+
const candidate = { ...address, type: replacementType };
|
|
6165
|
+
return graph.nodes.has(addressKey(candidate)) ? candidate : void 0;
|
|
6166
|
+
}
|
|
6167
|
+
function collectChangedPaths(address, config, existing, includeRemote) {
|
|
6168
|
+
const current = buildReadinessBaseline(getResourceDeclaration(address, config));
|
|
6169
|
+
const localPaths = existing.desired_readiness_baseline ? diffReadinessBaseline(existing.desired_readiness_baseline, current) : void 0;
|
|
6170
|
+
if (!includeRemote) return localPaths;
|
|
6171
|
+
if (!localPaths && !existing.drift_paths) return void 0;
|
|
6172
|
+
return [.../* @__PURE__ */ new Set([...localPaths ?? [], ...existing.drift_paths ?? []])].sort();
|
|
6173
|
+
}
|
|
5417
6174
|
function getDependencies(address, graph) {
|
|
5418
6175
|
const key = addressKey(address);
|
|
5419
6176
|
const depKeys = graph.edges.get(key) ?? /* @__PURE__ */ new Set();
|
|
@@ -5454,16 +6211,19 @@ async function refreshState(state, providers, options = {}) {
|
|
|
5454
6211
|
const decl = options.config ? getResourceDeclaration(res.address, options.config) : null;
|
|
5455
6212
|
const desiredComparable = decl ? provider.normalizeDesiredResource(res.address.type, res.address.name, decl) : null;
|
|
5456
6213
|
const desiredComparableHash = desiredComparable === null ? void 0 : contentHash(desiredComparable);
|
|
5457
|
-
const baselineHash = res.desired_comparable_hash ?? desiredComparableHash;
|
|
6214
|
+
const baselineHash = res.desired_comparable_hash ?? desiredComparableHash ?? remoteHash;
|
|
5458
6215
|
const driftStatus = baselineHash && remoteHash !== baselineHash ? "drifted" : "in_sync";
|
|
6216
|
+
const comparisonBaseline = res.remote_snapshot ?? desiredComparable;
|
|
6217
|
+
const driftPaths = driftStatus === "drifted" && comparisonBaseline != null ? diffChangedPaths(comparisonBaseline, remote2.comparable) : [];
|
|
5459
6218
|
state.setResource({
|
|
5460
6219
|
...res,
|
|
5461
6220
|
version: remote2.version ?? res.version,
|
|
5462
6221
|
remote_id: remote2.id,
|
|
5463
6222
|
desired_hash: res.desired_hash ?? res.content_hash,
|
|
5464
|
-
desired_comparable_hash:
|
|
6223
|
+
desired_comparable_hash: baselineHash,
|
|
5465
6224
|
remote_hash: remoteHash,
|
|
5466
6225
|
remote_snapshot: remote2.snapshot ?? remote2.comparable,
|
|
6226
|
+
drift_paths: driftPaths,
|
|
5467
6227
|
drift_status: driftStatus
|
|
5468
6228
|
});
|
|
5469
6229
|
dirty = true;
|
|
@@ -5553,7 +6313,14 @@ async function syncProjectResourcesWithStateBackend(input, options = {}) {
|
|
|
5553
6313
|
};
|
|
5554
6314
|
});
|
|
5555
6315
|
}
|
|
5556
|
-
var IMPORTABLE_RESOURCE_TYPES = /* @__PURE__ */ new Set([
|
|
6316
|
+
var IMPORTABLE_RESOURCE_TYPES = /* @__PURE__ */ new Set([
|
|
6317
|
+
"environment",
|
|
6318
|
+
"vault",
|
|
6319
|
+
"memory_store",
|
|
6320
|
+
"skill",
|
|
6321
|
+
"agent",
|
|
6322
|
+
"template"
|
|
6323
|
+
]);
|
|
5557
6324
|
async function importResource(ctx, address, remoteId, options = {}) {
|
|
5558
6325
|
if (!IMPORTABLE_RESOURCE_TYPES.has(address.type)) {
|
|
5559
6326
|
throw new UserError(
|
|
@@ -5580,12 +6347,21 @@ async function importResource(ctx, address, remoteId, options = {}) {
|
|
|
5580
6347
|
if (!contentHash2) {
|
|
5581
6348
|
throw new UserError(`Planned ${address.type}.${address.name} is missing a content hash.`);
|
|
5582
6349
|
}
|
|
6350
|
+
const provider = ctx.providers.get(address.provider);
|
|
6351
|
+
const remote = provider ? await readComparableIfSupported(provider, address.type, remoteId, address.name) : null;
|
|
6352
|
+
const remoteHash = remote ? contentHash(remote.comparable) : void 0;
|
|
5583
6353
|
const resource = {
|
|
5584
6354
|
address,
|
|
5585
6355
|
remote_id: remoteId,
|
|
5586
|
-
|
|
6356
|
+
externally_managed: address.type === "environment" && ctx.config.environments?.[address.name]?.environment_id ? true : void 0,
|
|
6357
|
+
version: options.resourceVersion ?? remote?.version,
|
|
5587
6358
|
content_hash: contentHash2,
|
|
5588
|
-
desired_hash: contentHash2
|
|
6359
|
+
desired_hash: contentHash2,
|
|
6360
|
+
desired_comparable_hash: remoteHash,
|
|
6361
|
+
desired_readiness_baseline: buildReadinessBaseline(getResourceDeclaration(address, ctx.config)),
|
|
6362
|
+
remote_hash: remoteHash,
|
|
6363
|
+
remote_snapshot: remote ? remote.snapshot ?? remote.comparable : void 0,
|
|
6364
|
+
drift_status: remoteHash ? "in_sync" : void 0
|
|
5589
6365
|
};
|
|
5590
6366
|
ctx.state.setResource(resource);
|
|
5591
6367
|
await ctx.state.save();
|
|
@@ -5999,6 +6775,7 @@ function buildDeploymentContext(ctx, name, provider) {
|
|
|
5999
6775
|
var destroyOrder = {
|
|
6000
6776
|
deployment: 0,
|
|
6001
6777
|
agent: 1,
|
|
6778
|
+
template: 1,
|
|
6002
6779
|
skill: 2,
|
|
6003
6780
|
memory_store: 3,
|
|
6004
6781
|
vault: 4,
|
|
@@ -6034,6 +6811,10 @@ async function destroyPlannedProjectResources(planned, options = {}) {
|
|
|
6034
6811
|
};
|
|
6035
6812
|
}
|
|
6036
6813
|
async function destroyOneResource(ctx, resource, options) {
|
|
6814
|
+
if (isExternalEnvironment(ctx, resource)) {
|
|
6815
|
+
ctx.state.removeResource(resource.address);
|
|
6816
|
+
return successResult(resource, "reference_removed");
|
|
6817
|
+
}
|
|
6037
6818
|
let provider;
|
|
6038
6819
|
try {
|
|
6039
6820
|
provider = getRuntimeProvider(ctx, resource.address.provider);
|
|
@@ -6094,6 +6875,9 @@ async function destroyOneResource(ctx, resource, options) {
|
|
|
6094
6875
|
return failureResult(resource, error);
|
|
6095
6876
|
}
|
|
6096
6877
|
}
|
|
6878
|
+
function isExternalEnvironment(ctx, resource) {
|
|
6879
|
+
return resource.address.type === "environment" && (resource.externally_managed || Boolean(ctx.config.environments?.[resource.address.name]?.environment_id));
|
|
6880
|
+
}
|
|
6097
6881
|
function successResult(resource, reason) {
|
|
6098
6882
|
return { resource, status: "success", reason };
|
|
6099
6883
|
}
|
|
@@ -6110,6 +6894,10 @@ async function deleteRemoteResource(provider, type, id, cascade) {
|
|
|
6110
6894
|
case "agent":
|
|
6111
6895
|
await provider.deleteAgent(id);
|
|
6112
6896
|
return;
|
|
6897
|
+
case "template":
|
|
6898
|
+
if (!provider.archiveTemplate) throw new UserError(`Provider does not support templates`);
|
|
6899
|
+
await provider.archiveTemplate(id);
|
|
6900
|
+
return;
|
|
6113
6901
|
case "skill":
|
|
6114
6902
|
await provider.deleteSkill(id);
|
|
6115
6903
|
return;
|
|
@@ -6207,6 +6995,7 @@ function buildAgentDecl(base, input) {
|
|
|
6207
6995
|
model,
|
|
6208
6996
|
instructions: input.instructions ?? base?.instructions ?? "",
|
|
6209
6997
|
...input.environment ? { environment: input.environment } : {},
|
|
6998
|
+
...input.vault ? { vault: input.vault } : {},
|
|
6210
6999
|
provider: input.provider ?? base?.provider,
|
|
6211
7000
|
tools: {
|
|
6212
7001
|
...base?.tools ?? { builtin: [] },
|
|
@@ -6244,6 +7033,18 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
|
|
|
6244
7033
|
const available = Object.keys(config.agents ?? {}).join(", ");
|
|
6245
7034
|
throw new UserError(`Agent '${agentName}' not found in config. Available agents: ${available || "(none)"}`);
|
|
6246
7035
|
}
|
|
7036
|
+
if (resolveAgentMaterialization(provider, agent).resourceType === "template") {
|
|
7037
|
+
const templateId = requireRef(state, { type: "template", name: agentName, provider });
|
|
7038
|
+
const identityId = options.identityId ?? config.defaults?.session?.qoder?.identity_id;
|
|
7039
|
+
return {
|
|
7040
|
+
delivery: "forward",
|
|
7041
|
+
template_id: templateId,
|
|
7042
|
+
...identityId ? { identity_id: identityId } : {},
|
|
7043
|
+
files: (options.files ?? []).map((file) => ({ file_id: file.fileId, mount_path: file.mountPath })),
|
|
7044
|
+
title: options.title,
|
|
7045
|
+
metadata: options.metadata
|
|
7046
|
+
};
|
|
7047
|
+
}
|
|
6247
7048
|
const agentId = requireRef(state, { type: "agent", name: agentName, provider });
|
|
6248
7049
|
const agentState = state.getResource({ type: "agent", name: agentName, provider });
|
|
6249
7050
|
const envName = options.environment ?? agent.environment;
|
|
@@ -6255,8 +7056,10 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
|
|
|
6255
7056
|
throw new UserError(`Agent '${agentName}' has no environment declared and --environment was not specified.`);
|
|
6256
7057
|
}
|
|
6257
7058
|
validateResourceInConfig(envName, "environment", config.environments);
|
|
6258
|
-
|
|
7059
|
+
const envDecl = config.environments[envName];
|
|
7060
|
+
environmentId = envDecl.environment_id ?? requireRef(state, { type: "environment", name: envName, provider });
|
|
6259
7061
|
}
|
|
7062
|
+
const tunnelId = resolveTunnelId(agent, config, options, provider);
|
|
6260
7063
|
let vaultIds;
|
|
6261
7064
|
if (options.vaultIds) {
|
|
6262
7065
|
vaultIds = options.vaultIds;
|
|
@@ -6278,6 +7081,7 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
|
|
|
6278
7081
|
agent_id: agentId,
|
|
6279
7082
|
agent_version: agentState?.version,
|
|
6280
7083
|
environment_id: environmentId,
|
|
7084
|
+
tunnel_id: tunnelId,
|
|
6281
7085
|
vault_ids: vaultIds,
|
|
6282
7086
|
memory_store_ids: memoryStoreIds,
|
|
6283
7087
|
files: (options.files ?? []).map((f) => ({ file_id: f.fileId, mount_path: f.mountPath })),
|
|
@@ -6285,6 +7089,19 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
|
|
|
6285
7089
|
metadata: options.metadata
|
|
6286
7090
|
};
|
|
6287
7091
|
}
|
|
7092
|
+
function resolveTunnelId(agent, config, options, provider) {
|
|
7093
|
+
const tunnelName = options.tunnel ?? agent.tunnel;
|
|
7094
|
+
if (!options.tunnelId && !tunnelName) return void 0;
|
|
7095
|
+
if (provider !== "qoder") {
|
|
7096
|
+
throw new UserError("Tunnels are supported only by Qoder BYOC sessions.");
|
|
7097
|
+
}
|
|
7098
|
+
if (options.tunnelId) return options.tunnelId;
|
|
7099
|
+
const tunnel = config.tunnels?.[tunnelName];
|
|
7100
|
+
if (!tunnel) {
|
|
7101
|
+
throw new UserError(`Tunnel '${tunnelName}' is not defined in config. Declare it under the 'tunnels:' section.`);
|
|
7102
|
+
}
|
|
7103
|
+
return tunnel.tunnel_id;
|
|
7104
|
+
}
|
|
6288
7105
|
function validateResourceInConfig(name, type, resources) {
|
|
6289
7106
|
if (!resources?.[name]) {
|
|
6290
7107
|
throw new UserError(`${type} '${name}' is not defined in config.`);
|
|
@@ -6571,9 +7388,7 @@ function actionKey(action) {
|
|
|
6571
7388
|
}
|
|
6572
7389
|
function isNonBlockingAgentDrift(action) {
|
|
6573
7390
|
if (action.action === "no-op") return true;
|
|
6574
|
-
|
|
6575
|
-
const reason = action.reason.toLowerCase();
|
|
6576
|
-
return reason.includes("metadata") || reason.includes("description");
|
|
7391
|
+
return action.readinessImpact === "non_blocking";
|
|
6577
7392
|
}
|
|
6578
7393
|
function collectAgentAddresses(config, agentName, provider) {
|
|
6579
7394
|
const agent = config.agents?.[agentName];
|
|
@@ -6581,7 +7396,10 @@ function collectAgentAddresses(config, agentName, provider) {
|
|
|
6581
7396
|
throw new UserError(`Agent '${agentName}' not found in config.`);
|
|
6582
7397
|
}
|
|
6583
7398
|
const resolvedProvider = provider ?? resolveSessionProvider(agentName, config, void 0);
|
|
6584
|
-
const
|
|
7399
|
+
const materialization = resolveAgentMaterialization(resolvedProvider, agent);
|
|
7400
|
+
const addresses = [
|
|
7401
|
+
{ type: materialization.resourceType, name: agentName, provider: resolvedProvider }
|
|
7402
|
+
];
|
|
6585
7403
|
if (agent.environment) {
|
|
6586
7404
|
addresses.push({
|
|
6587
7405
|
type: "environment",
|
|
@@ -6601,7 +7419,9 @@ function collectAgentAddresses(config, agentName, provider) {
|
|
|
6601
7419
|
}
|
|
6602
7420
|
}
|
|
6603
7421
|
for (const subAgent of agent.multiagent?.agents ?? []) {
|
|
6604
|
-
|
|
7422
|
+
const subDecl = config.agents?.[subAgent];
|
|
7423
|
+
const subType = subDecl ? resolveAgentMaterialization(resolvedProvider, subDecl).resourceType : "agent";
|
|
7424
|
+
addresses.push({ type: subType, name: subAgent, provider: resolvedProvider });
|
|
6605
7425
|
}
|
|
6606
7426
|
return addresses;
|
|
6607
7427
|
}
|
|
@@ -6655,8 +7475,11 @@ function resolveSessionRuntime(ctx, target = {}) {
|
|
|
6655
7475
|
async function createSessionForAgent(ctx, options = {}) {
|
|
6656
7476
|
const { agentName, provider, adapter } = resolveSessionRuntime(ctx, options);
|
|
6657
7477
|
const bindings = buildSessionBindings(agentName, ctx.config, provider, ctx.state, {
|
|
7478
|
+
identityId: options.identityId,
|
|
6658
7479
|
environment: options.environment,
|
|
6659
7480
|
environmentId: options.environmentId,
|
|
7481
|
+
tunnel: options.tunnel,
|
|
7482
|
+
tunnelId: options.tunnelId,
|
|
6660
7483
|
vault: options.vault,
|
|
6661
7484
|
vaultIds: options.vaultIds,
|
|
6662
7485
|
memoryStores: options.memoryStores,
|
|
@@ -6670,8 +7493,11 @@ async function createSessionForAgent(ctx, options = {}) {
|
|
|
6670
7493
|
async function startSessionRun(ctx, prompt, options = {}) {
|
|
6671
7494
|
const { agentName, provider, adapter } = resolveSessionRuntime(ctx, options);
|
|
6672
7495
|
const bindings = buildSessionBindings(agentName, ctx.config, provider, ctx.state, {
|
|
7496
|
+
identityId: options.identityId,
|
|
6673
7497
|
environment: options.environment,
|
|
6674
7498
|
environmentId: options.environmentId,
|
|
7499
|
+
tunnel: options.tunnel,
|
|
7500
|
+
tunnelId: options.tunnelId,
|
|
6675
7501
|
vault: options.vault,
|
|
6676
7502
|
vaultIds: options.vaultIds,
|
|
6677
7503
|
memoryStores: options.memoryStores,
|
|
@@ -6764,13 +7590,17 @@ async function listSessionsForProject(ctx, options = {}) {
|
|
|
6764
7590
|
const resolved = resolveSessionRuntime(ctx, options);
|
|
6765
7591
|
provider = resolved.provider;
|
|
6766
7592
|
agentName = resolved.agentName;
|
|
7593
|
+
const agentDecl = ctx.config.agents?.[resolved.agentName];
|
|
7594
|
+
const resourceType = agentDecl ? resolveAgentMaterialization(provider, agentDecl).resourceType : "agent";
|
|
6767
7595
|
const state = ctx.state.getResource({
|
|
6768
|
-
type:
|
|
7596
|
+
type: resourceType,
|
|
6769
7597
|
name: resolved.agentName,
|
|
6770
7598
|
provider
|
|
6771
7599
|
});
|
|
6772
7600
|
if (!state?.remote_id) {
|
|
6773
|
-
throw new UserError(
|
|
7601
|
+
throw new UserError(
|
|
7602
|
+
`${resourceType === "template" ? "Template" : "Agent"} '${resolved.agentName}' not found in state. Run \`agents apply\` first.`
|
|
7603
|
+
);
|
|
6774
7604
|
}
|
|
6775
7605
|
agentId = state.remote_id;
|
|
6776
7606
|
} else if (options.provider) {
|
|
@@ -6886,7 +7716,7 @@ function resolveDirectAdapter(ctx, overrideProvider) {
|
|
|
6886
7716
|
function buildAgentNameByRemoteId(ctx, provider) {
|
|
6887
7717
|
const names = /* @__PURE__ */ new Map();
|
|
6888
7718
|
for (const resource of ctx.state.listResources()) {
|
|
6889
|
-
if (resource.address.type === "agent" && resource.address.provider === provider && resource.remote_id) {
|
|
7719
|
+
if ((resource.address.type === "agent" || resource.address.type === "template") && resource.address.provider === provider && resource.remote_id) {
|
|
6890
7720
|
names.set(resource.remote_id, resource.address.name);
|
|
6891
7721
|
}
|
|
6892
7722
|
}
|
|
@@ -7100,12 +7930,15 @@ var StateManager = class _StateManager {
|
|
|
7100
7930
|
const resources = raw.map((r) => ({
|
|
7101
7931
|
address: r.address,
|
|
7102
7932
|
remote_id: r.remote_id,
|
|
7933
|
+
externally_managed: r.externally_managed === true ? true : void 0,
|
|
7103
7934
|
version: r.version,
|
|
7104
7935
|
content_hash: r.content_hash ?? r.desired_hash ?? "",
|
|
7105
7936
|
desired_hash: r.desired_hash ?? r.content_hash ?? "",
|
|
7106
7937
|
desired_comparable_hash: r.desired_comparable_hash,
|
|
7938
|
+
desired_readiness_baseline: r.desired_readiness_baseline,
|
|
7107
7939
|
remote_hash: r.remote_hash,
|
|
7108
7940
|
remote_snapshot: r.remote_snapshot,
|
|
7941
|
+
drift_paths: r.drift_paths,
|
|
7109
7942
|
drift_status: r.drift_status
|
|
7110
7943
|
}));
|
|
7111
7944
|
return new _StateManager({ resources }, path);
|
|
@@ -7282,6 +8115,7 @@ var ResourceTypeSchema = z6.enum([
|
|
|
7282
8115
|
"memory_store",
|
|
7283
8116
|
"skill",
|
|
7284
8117
|
"agent",
|
|
8118
|
+
"template",
|
|
7285
8119
|
"deployment",
|
|
7286
8120
|
"file"
|
|
7287
8121
|
]);
|
|
@@ -7299,11 +8133,14 @@ var DiagnosticSchema = z6.object({
|
|
|
7299
8133
|
});
|
|
7300
8134
|
var ActionTypeSchema = z6.enum(["create", "update", "delete", "no-op"]);
|
|
7301
8135
|
var DriftKindSchema = z6.enum(["none", "local", "remote", "both"]);
|
|
8136
|
+
var PlanReadinessImpactSchema = z6.enum(["none", "non_blocking", "blocking"]);
|
|
7302
8137
|
var PlannedActionSchema = z6.object({
|
|
7303
8138
|
action: ActionTypeSchema,
|
|
7304
8139
|
address: ResourceAddressSchema,
|
|
7305
8140
|
reason: z6.string(),
|
|
7306
8141
|
driftKind: DriftKindSchema.optional(),
|
|
8142
|
+
readinessImpact: PlanReadinessImpactSchema.optional(),
|
|
8143
|
+
changedPaths: z6.array(z6.string()).optional(),
|
|
7307
8144
|
before: z6.record(z6.string(), z6.unknown()).optional(),
|
|
7308
8145
|
after: z6.record(z6.string(), z6.unknown()).optional(),
|
|
7309
8146
|
dependencies: z6.array(ResourceAddressSchema)
|
|
@@ -7556,6 +8393,7 @@ export {
|
|
|
7556
8393
|
ListSessionsRequestSchema,
|
|
7557
8394
|
ListSessionsResponseSchema,
|
|
7558
8395
|
LocalFileStateBackend,
|
|
8396
|
+
PlanReadinessImpactSchema,
|
|
7559
8397
|
PlannedActionSchema,
|
|
7560
8398
|
ResourceAddressSchema,
|
|
7561
8399
|
ResourceTypeSchema,
|