@openagentpack/sdk 0.3.0-beta-fb9e73b-20260721 → 0.3.0-beta-e537ab3-20260722

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -298,6 +298,7 @@ var BaseApiClient = class {
298
298
  buffer = buffer.slice(boundary + 2);
299
299
  boundary = buffer.indexOf("\n\n");
300
300
  const dataLines = [];
301
+ let sseId;
301
302
  for (const line of frame.split("\n")) {
302
303
  if (line.startsWith(":")) continue;
303
304
  if (line.startsWith("event:") && line.slice(6).trim() === "heartbeat") {
@@ -307,11 +308,18 @@ var BaseApiClient = class {
307
308
  if (line.startsWith("data:")) {
308
309
  dataLines.push(line.slice(5).trimStart());
309
310
  }
311
+ if (line.startsWith("id:")) {
312
+ sseId = line.slice(3).trimStart();
313
+ }
310
314
  }
311
315
  if (dataLines.length === 0) continue;
312
316
  const json = dataLines.join("\n");
313
317
  try {
314
- yield JSON.parse(json);
318
+ const parsed = JSON.parse(json);
319
+ if (sseId !== void 0 && parsed.id === void 0) {
320
+ parsed.id = sseId;
321
+ }
322
+ yield parsed;
315
323
  } catch {
316
324
  }
317
325
  }
@@ -849,8 +857,193 @@ function stripAgentsMetadata(value) {
849
857
  return Object.keys(out).length > 0 ? out : void 0;
850
858
  }
851
859
 
860
+ // src/internal/utils/sandbox-mount.ts
861
+ var PROVIDER_MOUNT_PREFIXES = {
862
+ qoder: "/data",
863
+ claude: "/workspace",
864
+ bailian: "/mnt",
865
+ ark: "/mnt"
866
+ };
867
+ function joinAbsolute(prefix, sub) {
868
+ const left = prefix.replace(/\/+$/, "");
869
+ const right = sub.replace(/^\/+/, "");
870
+ return `${left}/${right}`;
871
+ }
872
+ function quoteShellWord(value) {
873
+ return `'${value.replaceAll("'", `'"'"'`)}'`;
874
+ }
875
+ function stripUrlQueryAndFragment(value) {
876
+ const queryIndex = value.indexOf("?");
877
+ const fragmentIndex = value.indexOf("#");
878
+ if (queryIndex === -1) {
879
+ return fragmentIndex === -1 ? value : value.slice(0, fragmentIndex);
880
+ }
881
+ if (fragmentIndex === -1) return value.slice(0, queryIndex);
882
+ return value.slice(0, Math.min(queryIndex, fragmentIndex));
883
+ }
884
+ function stripGitSuffix(value) {
885
+ return value.toLowerCase().endsWith(".git") ? value.slice(0, -4) : value;
886
+ }
887
+ function lastRemotePathSegment(value) {
888
+ const lastSlash = value.lastIndexOf("/");
889
+ const lastColon = value.lastIndexOf(":");
890
+ return value.slice(Math.max(lastSlash, lastColon) + 1).trim();
891
+ }
892
+ function providerMountPrefix(provider) {
893
+ return PROVIDER_MOUNT_PREFIXES[provider];
894
+ }
895
+ function resolveSandboxMountPath(provider, mountPath) {
896
+ const prefix = providerMountPrefix(provider);
897
+ if (!prefix) return mountPath;
898
+ if (mountPath === prefix || mountPath.startsWith(`${prefix}/`)) return mountPath;
899
+ if (mountPath.startsWith("/")) {
900
+ throw new UserError(`${provider} mount_path must start with '${prefix}/'; received '${mountPath}'.`);
901
+ }
902
+ return joinAbsolute(prefix, mountPath);
903
+ }
904
+ function resolveRepositoryMountPath(provider, resource) {
905
+ const prefix = providerMountPrefix(provider);
906
+ if (!prefix) throw new UserError(`Provider '${provider}' has no declared mount path prefix.`);
907
+ if (resource.mount_path) {
908
+ if (resource.mount_path !== prefix && !resource.mount_path.startsWith(`${prefix}/`)) {
909
+ throw new UserError(`${provider} Git repository Session resource mount_path must start with '${prefix}/'.`);
910
+ }
911
+ return resource.mount_path;
912
+ }
913
+ const repositoryName = stripGitSuffix(lastRemotePathSegment(stripUrlQueryAndFragment(resource.url)));
914
+ if (!repositoryName) {
915
+ throw new UserError(`Cannot derive a ${provider} Git repository mount path from URL '${resource.url}'.`);
916
+ }
917
+ return provider === "qoder" ? `${prefix}/workspace/${repositoryName}` : `${prefix}/${repositoryName}`;
918
+ }
919
+ function composeFileMountHint(files, provider) {
920
+ if (!files || files.length === 0) return "";
921
+ const lines = files.map((f) => `- ${resolveSandboxMountPath(provider, f.mount_path)}`);
922
+ return [
923
+ "The user uploaded files. They are available at the following sandbox paths:",
924
+ ...lines,
925
+ "Read them from these paths when relevant."
926
+ ].join("\n");
927
+ }
928
+ function prependFileHint(prompt, files, provider) {
929
+ const hint = composeFileMountHint(files, provider);
930
+ if (!hint) return prompt;
931
+ return `${hint}
932
+
933
+ ${prompt}`;
934
+ }
935
+ var FILE_MENTION_START = "\u27E6file:";
936
+ var FILE_MENTION_END = "\u27E7";
937
+ function rewriteFileMentions(prompt, provider) {
938
+ let cursor = 0;
939
+ let rewritten = "";
940
+ while (cursor < prompt.length) {
941
+ const start = prompt.indexOf(FILE_MENTION_START, cursor);
942
+ if (start === -1) {
943
+ rewritten += prompt.slice(cursor);
944
+ break;
945
+ }
946
+ const mountPathStart = start + FILE_MENTION_START.length;
947
+ const end = prompt.indexOf(FILE_MENTION_END, mountPathStart);
948
+ if (end === -1) {
949
+ rewritten += prompt.slice(cursor);
950
+ break;
951
+ }
952
+ rewritten += prompt.slice(cursor, start);
953
+ rewritten += resolveSandboxMountPath(provider, prompt.slice(mountPathStart, end));
954
+ cursor = end + FILE_MENTION_END.length;
955
+ }
956
+ return rewritten;
957
+ }
958
+ function preparePromptForProvider(prompt, files, provider) {
959
+ return prependFileHint(rewriteFileMentions(prompt, provider), files, provider);
960
+ }
961
+ function prepareInitialSessionPrompt(prompt, bindings, provider) {
962
+ const prepared = preparePromptForProvider(prompt, bindings.files, provider);
963
+ const repositories = (bindings.resources ?? []).filter(
964
+ (resource) => resource.type === "github_repository"
965
+ );
966
+ if (repositories.length === 0) return prepared;
967
+ const paths = repositories.map((resource) => resolveRepositoryMountPath(provider, resource));
968
+ if (paths.length === 1) {
969
+ const path = paths[0];
970
+ return [
971
+ `The Git working tree for this task is mounted at \`${path}\`.`,
972
+ "Work only inside this directory unless the user explicitly requests otherwise.",
973
+ "Prefix every shell command with:",
974
+ `cd -- ${quoteShellWord(path)} &&`,
975
+ `Use absolute paths under \`${path}\` for non-shell file tools.`,
976
+ "",
977
+ prepared
978
+ ].join("\n");
979
+ }
980
+ const lines = ["Git working trees for this task are mounted at:", ...paths.map((path) => `- ${path}`)];
981
+ lines.push("Choose the appropriate working tree for the task before inspecting or modifying files.");
982
+ return `${lines.join("\n")}
983
+
984
+ ${prepared}`;
985
+ }
986
+
987
+ // src/internal/utils/tool-permissions.ts
988
+ function canonicalToolName(name) {
989
+ return name.trim().replace(/[^a-zA-Z0-9]+/g, "").toLowerCase();
990
+ }
991
+ function resolveBuiltinTools(tools, options = {}) {
992
+ const permissionByName = /* @__PURE__ */ new Map();
993
+ for (const [name, permission] of Object.entries(tools.permissions ?? {})) {
994
+ permissionByName.set(canonicalToolName(name), permission);
995
+ }
996
+ const supportedByName = options.supportedWireNames ? new Map([...options.supportedWireNames].map((name) => [canonicalToolName(name), name])) : void 0;
997
+ return tools.builtin.flatMap((configuredName) => {
998
+ const candidate = options.toWireName?.(configuredName) ?? configuredName;
999
+ const wireName = supportedByName?.get(canonicalToolName(candidate)) ?? (supportedByName ? void 0 : candidate);
1000
+ if (!wireName) return [];
1001
+ return [
1002
+ {
1003
+ configuredName,
1004
+ wireName,
1005
+ permission: permissionByName.get(canonicalToolName(configuredName)) ?? tools.default_permission ?? "allow"
1006
+ }
1007
+ ];
1008
+ });
1009
+ }
1010
+ function toPermissionPolicy(permission) {
1011
+ return { type: permission === "ask" ? "always_ask" : "always_allow" };
1012
+ }
1013
+ function permissionOverridesFromWire(configs, toConfigName = (name) => name) {
1014
+ const permissions = {};
1015
+ for (const config of configs) {
1016
+ if (config.enabled === false) continue;
1017
+ const rawPolicy = config.permission_policy;
1018
+ const type = typeof rawPolicy === "string" ? rawPolicy : rawPolicy && typeof rawPolicy === "object" ? rawPolicy.type : void 0;
1019
+ if (type === "always_ask") permissions[toConfigName(config.name)] = "ask";
1020
+ else if (type === "always_allow") permissions[toConfigName(config.name)] = "allow";
1021
+ }
1022
+ return Object.keys(permissions).length ? permissions : void 0;
1023
+ }
1024
+
1025
+ // src/internal/providers/session-resource-mapper.ts
1026
+ function resolveGithubRepositoryMountPath(provider, resource) {
1027
+ return resolveRepositoryMountPath(provider, resource);
1028
+ }
1029
+ function mapGithubRepositorySessionResource(resource, options = {}) {
1030
+ const entry = {
1031
+ type: "github_repository",
1032
+ url: options.mapUrl?.(resource.url) ?? resource.url,
1033
+ authorization_token: resource.authorization_token
1034
+ };
1035
+ if (resource.checkout?.branch) entry.checkout = { type: "branch", name: resource.checkout.branch };
1036
+ else if (resource.checkout?.commit) entry.checkout = { type: "commit", sha: resource.checkout.commit };
1037
+ const mountPath = options.mapMountPath?.(resource) ?? resource.mount_path;
1038
+ if (mountPath) entry.mount_path = mountPath;
1039
+ return entry;
1040
+ }
1041
+
852
1042
  // src/internal/providers/claude/mapper.ts
853
1043
  var CLAUDE_BUILTINS = /* @__PURE__ */ new Set(["read", "write", "edit", "bash", "glob", "grep", "web_search", "web_fetch"]);
1044
+ function normalizeGithubRepositoryUrlForClaude(url) {
1045
+ return url.replace(/\.git\/?$/, "");
1046
+ }
854
1047
  function credToDecl(raw, vaultName) {
855
1048
  const auth = raw.auth ?? {};
856
1049
  const name = raw.display_name || raw.id || "credential";
@@ -921,6 +1114,7 @@ function agentToDecl(raw) {
921
1114
  const skills = raw.skills;
922
1115
  const multiagent = raw.multiagent;
923
1116
  let builtinTools;
1117
+ let builtinPermissions;
924
1118
  let allToolsEnabled = false;
925
1119
  if (tools?.length) {
926
1120
  const toolset = tools.find((t) => t.type === "agent_toolset_20260401");
@@ -929,6 +1123,7 @@ function agentToDecl(raw) {
929
1123
  const configs = toolset.configs ?? [];
930
1124
  if (configs.length > 0) {
931
1125
  builtinTools = configs.filter((c) => c.enabled !== false).map((c) => c.name);
1126
+ builtinPermissions = permissionOverridesFromWire(configs);
932
1127
  } else if (defaultConfig?.enabled) {
933
1128
  allToolsEnabled = true;
934
1129
  }
@@ -955,7 +1150,7 @@ function agentToDecl(raw) {
955
1150
  }
956
1151
  let toolsDecl;
957
1152
  if (builtinTools?.length) {
958
- toolsDecl = { builtin: builtinTools };
1153
+ toolsDecl = { builtin: builtinTools, permissions: builtinPermissions };
959
1154
  } else if (allToolsEnabled) {
960
1155
  toolsDecl = {
961
1156
  builtin: ["read", "write", "edit", "bash", "glob", "grep", "web_search", "web_fetch"]
@@ -1015,16 +1210,11 @@ function mapAgent(name, decl, refs, version, projectName) {
1015
1210
  body.metadata = decl.metadata;
1016
1211
  }
1017
1212
  if (decl.tools) {
1018
- const toolConfigs = decl.tools.builtin.filter((toolName) => CLAUDE_BUILTINS.has(toolName)).map((toolName) => {
1019
- const permission = decl.tools?.permissions?.[toolName] ?? "allow";
1020
- return {
1021
- name: toolName,
1022
- enabled: true,
1023
- permission_policy: {
1024
- type: permission === "ask" ? "always_ask" : "always_allow"
1025
- }
1026
- };
1027
- });
1213
+ const toolConfigs = resolveBuiltinTools(decl.tools, { supportedWireNames: CLAUDE_BUILTINS }).map((tool) => ({
1214
+ name: tool.wireName,
1215
+ enabled: true,
1216
+ permission_policy: toPermissionPolicy(tool.permission)
1217
+ }));
1028
1218
  body.tools = [
1029
1219
  {
1030
1220
  type: "agent_toolset_20260401",
@@ -1090,6 +1280,23 @@ function mapDeployment(name, decl, refs, projectName, uploadedFiles) {
1090
1280
  }
1091
1281
  return body;
1092
1282
  }
1283
+ function mapDeploymentUpdate(name, decl, refs, projectName, uploadedFiles, existingMetadata) {
1284
+ const body = mapDeployment(name, decl, refs, projectName, uploadedFiles);
1285
+ body.vault_ids = refs.vault_ids;
1286
+ body.resources = mapDeploymentResources(decl, refs, uploadedFiles);
1287
+ if (decl.schedule) {
1288
+ body.schedule = { type: "cron", expression: decl.schedule.expression, timezone: decl.schedule.timezone };
1289
+ }
1290
+ body.description = decl.description ?? "";
1291
+ const desiredMetadata = projectName ? injectMetadata(decl.metadata, projectName, name) : decl.metadata ?? {};
1292
+ body.metadata = {
1293
+ ...Object.fromEntries(
1294
+ Object.keys(existingMetadata ?? {}).filter((key) => !(key in desiredMetadata)).map((key) => [key, null])
1295
+ ),
1296
+ ...desiredMetadata
1297
+ };
1298
+ return body;
1299
+ }
1093
1300
  function mapInitialEvents(events) {
1094
1301
  return events.map((ev) => {
1095
1302
  if (ev.type === "user.message" || ev.type === "system.message") {
@@ -1123,7 +1330,7 @@ function mapDeploymentResources(decl, refs, uploadedFiles) {
1123
1330
  } else if (r.type === "github_repository") {
1124
1331
  const entry = {
1125
1332
  type: "github_repository",
1126
- url: r.url
1333
+ url: normalizeGithubRepositoryUrlForClaude(r.url)
1127
1334
  };
1128
1335
  if (r.authorization_token) entry.authorization_token = r.authorization_token;
1129
1336
  if (r.checkout?.branch) {
@@ -1252,8 +1459,16 @@ function mapSession(bindings) {
1252
1459
  resources.push({
1253
1460
  type: "file",
1254
1461
  file_id: f.file_id,
1255
- mount_path: f.mount_path
1462
+ mount_path: resolveSandboxMountPath("claude", f.mount_path)
1256
1463
  });
1464
+ for (const resource of bindings.resources ?? []) {
1465
+ resources.push(
1466
+ mapGithubRepositorySessionResource(resource, {
1467
+ mapUrl: normalizeGithubRepositoryUrlForClaude,
1468
+ mapMountPath: (item) => resolveGithubRepositoryMountPath("claude", item)
1469
+ })
1470
+ );
1471
+ }
1257
1472
  if (resources.length) body.resources = resources;
1258
1473
  return body;
1259
1474
  }
@@ -1504,7 +1719,20 @@ var ClaudeAdapter = class _ClaudeAdapter {
1504
1719
  }
1505
1720
  async updateDeployment(id, name, decl, refs, basePath) {
1506
1721
  const uploaded = await this.uploadDeploymentFiles(decl, basePath);
1507
- const body = mapDeployment(name, decl, refs, this.projectName, uploaded);
1722
+ const current = await this.client.get(`/deployments/${id}`);
1723
+ if (current.schedule && !decl.schedule) {
1724
+ throw new UserError(
1725
+ `Deployment '${name}' cannot remove its schedule through the documented Claude update API; archive and recreate it as a manual deployment.`
1726
+ );
1727
+ }
1728
+ const body = mapDeploymentUpdate(
1729
+ name,
1730
+ decl,
1731
+ refs,
1732
+ this.projectName,
1733
+ uploaded,
1734
+ current.metadata
1735
+ );
1508
1736
  const res = await this.client.post(`/deployments/${id}`, body);
1509
1737
  return toRemoteResource(res);
1510
1738
  }
@@ -1556,6 +1784,36 @@ var ClaudeAdapter = class _ClaudeAdapter {
1556
1784
  attributes: res
1557
1785
  };
1558
1786
  }
1787
+ async listDeployments(filter) {
1788
+ const params = new URLSearchParams();
1789
+ if (filter?.agent_id) params.set("agent_id", filter.agent_id);
1790
+ if (filter?.status) params.set("status", filter.status);
1791
+ if (filter?.include_archived) params.set("include_archived", "true");
1792
+ if (filter?.limit) params.set("limit", String(filter.limit));
1793
+ if (filter?.page) params.set("page", filter.page);
1794
+ if (filter?.created_at_gte) params.set("created_at[gte]", filter.created_at_gte);
1795
+ if (filter?.created_at_lte) params.set("created_at[lte]", filter.created_at_lte);
1796
+ const query2 = params.toString();
1797
+ const res = await this.client.get(`/deployments${query2 ? `?${query2}` : ""}`);
1798
+ const nextPage = res.next_page ?? void 0;
1799
+ return {
1800
+ deployments: (res.data ?? []).map(toDeploymentInfo),
1801
+ has_more: nextPage !== void 0,
1802
+ next_page: nextPage
1803
+ };
1804
+ }
1805
+ async pauseDeployment(ctx) {
1806
+ return this.setDeploymentPaused(ctx, true);
1807
+ }
1808
+ async unpauseDeployment(ctx) {
1809
+ return this.setDeploymentPaused(ctx, false);
1810
+ }
1811
+ async setDeploymentPaused(ctx, paused) {
1812
+ if (!ctx.id) throw new UserError(`Deployment '${ctx.name}' has no remote id; run \`agents apply\` first.`);
1813
+ const action = paused ? "pause" : "unpause";
1814
+ const res = await this.client.post(`/deployments/${ctx.id}/${action}`, {});
1815
+ return toDeploymentInfo(res);
1816
+ }
1559
1817
  async createSession(bindings) {
1560
1818
  if (bindings.delivery === "forward") throw new UserError("Claude does not support Forward sessions.");
1561
1819
  const body = mapSession(bindings);
@@ -1627,6 +1885,16 @@ var ClaudeAdapter = class _ClaudeAdapter {
1627
1885
  await this.client.delete(`/files/${id}`);
1628
1886
  }
1629
1887
  };
1888
+ function toDeploymentInfo(res) {
1889
+ const sched = res.schedule;
1890
+ return {
1891
+ id: res.id ?? null,
1892
+ status: res.status ?? "unknown",
1893
+ paused_reason: res.paused_reason ?? void 0,
1894
+ schedule: sched ? { expression: sched.expression, timezone: sched.timezone } : void 0,
1895
+ attributes: res
1896
+ };
1897
+ }
1630
1898
  function toSessionInfo(res) {
1631
1899
  return buildSessionInfo(
1632
1900
  res,
@@ -1679,6 +1947,7 @@ registerProvider({
1679
1947
  name: "claude",
1680
1948
  configSchema: claudeConfigSchema,
1681
1949
  capabilities: CLAUDE_CAPABILITIES,
1950
+ features: { tool_permissions: true, session_resources: ["github_repository"] },
1682
1951
  createAdapter: (config, projectName) => {
1683
1952
  const c = config;
1684
1953
  return new ClaudeAdapter(c.api_key, c.beta, projectName);
@@ -1687,7 +1956,7 @@ registerProvider({
1687
1956
 
1688
1957
  // src/internal/providers/qoder/adapter.ts
1689
1958
  import { readFileSync as readFileSync2 } from "fs";
1690
- import { basename as basename3, dirname as dirname2, resolve as resolve2 } from "path";
1959
+ import { basename as basename2, dirname as dirname2, resolve as resolve2 } from "path";
1691
1960
  import JSZip2 from "jszip";
1692
1961
 
1693
1962
  // src/internal/providers/qoder/client.ts
@@ -1712,57 +1981,6 @@ var QoderClient = class extends BaseApiClient {
1712
1981
  }
1713
1982
  };
1714
1983
 
1715
- // src/internal/utils/sandbox-mount.ts
1716
- var AGENTS_SESSION_PREFIX = "/mnt/session";
1717
- function joinAbsolute(prefix, sub) {
1718
- const left = prefix.replace(/\/+$/, "");
1719
- const right = sub.replace(/^\/+/, "");
1720
- return `${left}/${right}`;
1721
- }
1722
- function basename2(p) {
1723
- const trimmed = p.replace(/\/+$/, "");
1724
- const idx = trimmed.lastIndexOf("/");
1725
- return idx === -1 ? trimmed : trimmed.slice(idx + 1);
1726
- }
1727
- function resolveSandboxMountPath(provider, mountPath) {
1728
- switch (provider) {
1729
- case "ark":
1730
- case "bailian":
1731
- case "claude":
1732
- return joinAbsolute(AGENTS_SESSION_PREFIX, mountPath);
1733
- case "qoder":
1734
- return joinAbsolute("/data", basename2(mountPath));
1735
- default:
1736
- return mountPath;
1737
- }
1738
- }
1739
- function composeFileMountHint(files, provider) {
1740
- if (!files || files.length === 0) return "";
1741
- const lines = files.map((f) => `- ${resolveSandboxMountPath(provider, f.mount_path)}`);
1742
- return [
1743
- "The user uploaded files. They are available at the following sandbox paths:",
1744
- ...lines,
1745
- "Read them from these paths when relevant."
1746
- ].join("\n");
1747
- }
1748
- function prependFileHint(prompt, files, provider) {
1749
- const hint = composeFileMountHint(files, provider);
1750
- if (!hint) return prompt;
1751
- return `${hint}
1752
-
1753
- ${prompt}`;
1754
- }
1755
- var FILE_MENTION_SENTINEL_RE = /\u27E6file:(.+?)\u27E7/g;
1756
- function rewriteFileMentions(prompt, provider) {
1757
- return prompt.replace(
1758
- FILE_MENTION_SENTINEL_RE,
1759
- (_match, mountPath) => resolveSandboxMountPath(provider, mountPath)
1760
- );
1761
- }
1762
- function preparePromptForProvider(prompt, files, provider) {
1763
- return prependFileHint(rewriteFileMentions(prompt, provider), files, provider);
1764
- }
1765
-
1766
1984
  // src/internal/providers/qoder/mapper.ts
1767
1985
  function toPascalCase(name) {
1768
1986
  return name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[^a-zA-Z0-9]+/g, " ").trim().split(/\s+/).map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join("");
@@ -1875,6 +2093,7 @@ function agentToDecl2(raw) {
1875
2093
  const mcpServers = raw.mcp_servers;
1876
2094
  const skills = raw.skills;
1877
2095
  let builtinTools;
2096
+ let builtinPermissions;
1878
2097
  if (tools?.length) {
1879
2098
  const toolset = tools.find((t) => t.type === "agent_toolset_20260401");
1880
2099
  if (toolset && Array.isArray(toolset.enabled_tools)) {
@@ -1882,6 +2101,7 @@ function agentToDecl2(raw) {
1882
2101
  } else if (toolset) {
1883
2102
  const configs = toolset.configs ?? [];
1884
2103
  builtinTools = configs.filter((c) => c.enabled !== false).map((c) => normalizeToolNameFromQoder(c.name));
2104
+ builtinPermissions = permissionOverridesFromWire(configs, normalizeToolNameFromQoder);
1885
2105
  }
1886
2106
  }
1887
2107
  let mcpServerDecls;
@@ -1903,7 +2123,7 @@ function agentToDecl2(raw) {
1903
2123
  description: raw.description,
1904
2124
  model: raw.model,
1905
2125
  instructions: raw.system,
1906
- tools: builtinTools?.length ? { builtin: builtinTools } : void 0,
2126
+ tools: builtinTools?.length ? { builtin: builtinTools, permissions: builtinPermissions } : void 0,
1907
2127
  mcp_servers: mcpServerDecls,
1908
2128
  skills: skillDecls,
1909
2129
  metadata: stripAgentsMetadata(raw.metadata)
@@ -1934,6 +2154,7 @@ function mapDeployment2(name, decl, refs, projectName, uploadedFiles) {
1934
2154
  };
1935
2155
  }
1936
2156
  if (decl.description) body.description = decl.description;
2157
+ if (decl.environment_variables !== void 0) body.environment_variables = decl.environment_variables;
1937
2158
  if (projectName) {
1938
2159
  body.metadata = injectMetadata(decl.metadata, projectName, name);
1939
2160
  } else if (decl.metadata) {
@@ -1941,6 +2162,24 @@ function mapDeployment2(name, decl, refs, projectName, uploadedFiles) {
1941
2162
  }
1942
2163
  return body;
1943
2164
  }
2165
+ function mapDeploymentUpdate2(name, decl, refs, projectName, uploadedFiles, existingMetadata) {
2166
+ const body = mapDeployment2(name, decl, refs, projectName, uploadedFiles);
2167
+ body.vault_ids = refs.vault_ids;
2168
+ body.resources = mapDeploymentResources2(decl, refs, uploadedFiles);
2169
+ if (decl.schedule) {
2170
+ body.schedule = { type: "cron", expression: decl.schedule.expression, timezone: decl.schedule.timezone };
2171
+ }
2172
+ body.description = decl.description ?? "";
2173
+ body.environment_variables = decl.environment_variables ?? null;
2174
+ const desiredMetadata = projectName ? injectMetadata(decl.metadata, projectName, name) : decl.metadata ?? {};
2175
+ body.metadata = {
2176
+ ...Object.fromEntries(
2177
+ Object.keys(existingMetadata ?? {}).filter((key) => !(key in desiredMetadata)).map((key) => [key, null])
2178
+ ),
2179
+ ...desiredMetadata
2180
+ };
2181
+ return body;
2182
+ }
1944
2183
  function mapDeploymentInitialEvents(events) {
1945
2184
  return events.map((ev) => {
1946
2185
  if (ev.type === "user.message" || ev.type === "system.message") {
@@ -2017,11 +2256,14 @@ function mapAgent2(name, decl, refs, version, projectName) {
2017
2256
  body.metadata = decl.metadata;
2018
2257
  }
2019
2258
  if (decl.tools) {
2020
- const enabledTools = decl.tools.builtin.map((t) => normalizeToolNameForQoder(t));
2021
2259
  body.tools = [
2022
2260
  {
2023
2261
  type: "agent_toolset_20260401",
2024
- enabled_tools: enabledTools
2262
+ configs: resolveBuiltinTools(decl.tools, { toWireName: normalizeToolNameForQoder }).map((tool) => ({
2263
+ name: tool.wireName,
2264
+ enabled: true,
2265
+ permission_policy: toPermissionPolicy(tool.permission)
2266
+ }))
2025
2267
  }
2026
2268
  ];
2027
2269
  } else {
@@ -2079,19 +2321,14 @@ function mapForwardTemplate(name, decl, refs, projectName) {
2079
2321
  if (projectName) body.metadata = injectMetadata(decl.metadata, projectName, name);
2080
2322
  else body.metadata = decl.metadata ?? {};
2081
2323
  if (decl.tools) {
2082
- const permissions = decl.tools.permissions ?? {};
2083
2324
  body.tools = [
2084
2325
  {
2085
2326
  type: "agent_toolset_20260401",
2086
- configs: decl.tools.builtin.map((tool) => {
2087
- const normalized = normalizeToolNameForQoder(tool);
2088
- const policy = permissions[tool] ?? permissions[tool.toLowerCase()] ?? permissions[normalized];
2089
- return {
2090
- name: normalized,
2091
- enabled: true,
2092
- ...policy ? { permission_policy: { type: policy === "ask" ? "always_ask" : "always_allow" } } : {}
2093
- };
2094
- })
2327
+ configs: resolveBuiltinTools(decl.tools, { toWireName: normalizeToolNameForQoder }).map((tool) => ({
2328
+ name: tool.wireName,
2329
+ enabled: true,
2330
+ permission_policy: toPermissionPolicy(tool.permission)
2331
+ }))
2095
2332
  }
2096
2333
  ];
2097
2334
  } else {
@@ -2147,6 +2384,7 @@ function toSessionEvent3(raw) {
2147
2384
  const rawType = raw.type ?? "";
2148
2385
  const type = QODER_EVENT_MAP[rawType] ?? "unknown";
2149
2386
  const event = { type, raw_type: rawType, raw };
2387
+ if (typeof raw.id === "string") event.id = raw.id;
2150
2388
  if (typeof raw.role === "string") event.role = raw.role;
2151
2389
  if (type === "message") {
2152
2390
  event.role = roleFromType2(rawType, raw.role);
@@ -2157,12 +2395,15 @@ function toSessionEvent3(raw) {
2157
2395
  } else if (type === "tool_result") {
2158
2396
  event.content = extractContentText2(raw);
2159
2397
  } else if (type === "status") {
2398
+ const stopReason = extractStopReason2(raw.stop_reason);
2160
2399
  if (rawType === "session.thread_status_idle") {
2161
2400
  event.status = "running";
2401
+ } else if (rawType === "session.status_idle" && stopReason === "requires_action") {
2402
+ event.status = "running";
2162
2403
  } else {
2163
2404
  event.status = rawType.includes("idle") ? "idle" : rawType.includes("terminated") ? "terminated" : "running";
2164
2405
  }
2165
- event.stop_reason = extractStopReason2(raw.stop_reason);
2406
+ event.stop_reason = stopReason;
2166
2407
  } else if (type === "error") {
2167
2408
  event.content = extractErrorMessage2(raw);
2168
2409
  }
@@ -2224,6 +2465,13 @@ function mapSession2(bindings) {
2224
2465
  for (const id of bindings.memory_store_ids) resources.push({ type: "memory_store", memory_store_id: id });
2225
2466
  for (const f of bindings.files ?? [])
2226
2467
  resources.push({ type: "file", file_id: f.file_id, mount_path: resolveSandboxMountPath("qoder", f.mount_path) });
2468
+ for (const resource of bindings.resources ?? []) {
2469
+ resources.push(
2470
+ mapGithubRepositorySessionResource(resource, {
2471
+ mapMountPath: (item) => resolveGithubRepositoryMountPath("qoder", item)
2472
+ })
2473
+ );
2474
+ }
2227
2475
  if (resources.length) body.resources = resources;
2228
2476
  return body;
2229
2477
  }
@@ -2234,6 +2482,16 @@ function deriveForwardGateway(cloudGateway) {
2234
2482
  const trimmed = cloudGateway.replace(/\/$/, "");
2235
2483
  return trimmed.endsWith("/cloud") ? `${trimmed.slice(0, -"/cloud".length)}/forward` : `${trimmed}/forward`;
2236
2484
  }
2485
+ function toDeploymentInfo2(res) {
2486
+ const sched = res.schedule;
2487
+ return {
2488
+ id: res.id ?? null,
2489
+ status: res.status ?? "unknown",
2490
+ paused_reason: res.paused_reason ?? void 0,
2491
+ schedule: sched ? { expression: sched.expression, timezone: sched.timezone } : void 0,
2492
+ attributes: res
2493
+ };
2494
+ }
2237
2495
  var QoderAdapter = class _QoderAdapter {
2238
2496
  name = "qoder";
2239
2497
  eventResume = true;
@@ -2279,8 +2537,8 @@ var QoderAdapter = class _QoderAdapter {
2279
2537
  vault: "/vaults",
2280
2538
  skill: "/skills",
2281
2539
  memory_store: "/memory_stores",
2282
- file: "/files"
2283
- // deployment omitted: emulated on Qoder, no remote listing endpoint
2540
+ file: "/files",
2541
+ deployment: "/deployments"
2284
2542
  };
2285
2543
  async findResource(type, name, id) {
2286
2544
  if (type === "template") {
@@ -2704,7 +2962,20 @@ var QoderAdapter = class _QoderAdapter {
2704
2962
  }
2705
2963
  async updateDeployment(id, name, decl, refs, basePath) {
2706
2964
  const uploaded = await this.uploadDeploymentFiles(decl, basePath);
2707
- const body = mapDeployment2(name, decl, refs, this.projectName, uploaded);
2965
+ const current = await this.client.get(`/deployments/${id}`);
2966
+ if (current.schedule && !decl.schedule) {
2967
+ throw new UserError(
2968
+ `Deployment '${name}' cannot remove its schedule through the documented Qoder update API; archive and recreate it as a manual deployment.`
2969
+ );
2970
+ }
2971
+ const body = mapDeploymentUpdate2(
2972
+ name,
2973
+ decl,
2974
+ refs,
2975
+ this.projectName,
2976
+ uploaded,
2977
+ current.metadata
2978
+ );
2708
2979
  const res = await this.client.post(`/deployments/${id}`, body);
2709
2980
  return toRemoteResource(res);
2710
2981
  }
@@ -2739,6 +3010,35 @@ var QoderAdapter = class _QoderAdapter {
2739
3010
  attributes: res
2740
3011
  };
2741
3012
  }
3013
+ async listDeployments(filter) {
3014
+ const params = new URLSearchParams();
3015
+ if (filter?.agent_id) params.set("agent_id", filter.agent_id);
3016
+ if (filter?.status) params.set("status", filter.status);
3017
+ if (filter?.include_archived) params.set("include_archived", "true");
3018
+ if (filter?.limit) params.set("limit", String(filter.limit));
3019
+ if (filter?.page) params.set("page", filter.page);
3020
+ if (filter?.created_at_gte) params.set("created_at[gte]", filter.created_at_gte);
3021
+ if (filter?.created_at_lte) params.set("created_at[lte]", filter.created_at_lte);
3022
+ const query2 = params.toString();
3023
+ const res = await this.client.get(`/deployments${query2 ? `?${query2}` : ""}`);
3024
+ return {
3025
+ deployments: (res.data ?? []).map(toDeploymentInfo2),
3026
+ has_more: Boolean(res.has_more),
3027
+ next_page: res.next_page ?? void 0
3028
+ };
3029
+ }
3030
+ async pauseDeployment(ctx) {
3031
+ return this.setDeploymentPaused(ctx, true);
3032
+ }
3033
+ async unpauseDeployment(ctx) {
3034
+ return this.setDeploymentPaused(ctx, false);
3035
+ }
3036
+ async setDeploymentPaused(ctx, paused) {
3037
+ if (!ctx.id) throw new UserError(`Deployment '${ctx.name}' has no remote id; run \`agents apply\` first.`);
3038
+ const action = paused ? "pause" : "unpause";
3039
+ const res = await this.client.post(`/deployments/${ctx.id}/${action}`, {});
3040
+ return toDeploymentInfo2(res);
3041
+ }
2742
3042
  async uploadDeploymentFiles(decl, basePath) {
2743
3043
  const map = /* @__PURE__ */ new Map();
2744
3044
  for (const r of decl.resources ?? []) {
@@ -2752,7 +3052,7 @@ var QoderAdapter = class _QoderAdapter {
2752
3052
  const fullPath = resolve2(dirname2(basePath), source);
2753
3053
  const content = readFileSync2(fullPath);
2754
3054
  const formData = new FormData();
2755
- formData.append("file", new File([new Uint8Array(content)], basename3(fullPath)));
3055
+ formData.append("file", new File([new Uint8Array(content)], basename2(fullPath)));
2756
3056
  formData.append("purpose", "session_resource");
2757
3057
  const res = await this.client.postFormData("/files", formData);
2758
3058
  return res.file_id ?? res.id;
@@ -2925,7 +3225,7 @@ var QoderAdapter = class _QoderAdapter {
2925
3225
  async uploadFile(filePath, options) {
2926
3226
  const resolved = resolve2(filePath);
2927
3227
  const content = readFileSync2(resolved);
2928
- const fileName = options?.name ?? basename3(resolved);
3228
+ const fileName = options?.name ?? basename2(resolved);
2929
3229
  return this.uploadFileContent(new Uint8Array(content), fileName, {
2930
3230
  purpose: options?.purpose
2931
3231
  });
@@ -3050,6 +3350,7 @@ registerProvider({
3050
3350
  name: "qoder",
3051
3351
  configSchema: qoderConfigSchema,
3052
3352
  capabilities: QODER_CAPABILITIES,
3353
+ features: { tool_permissions: true, session_resources: ["github_repository"] },
3053
3354
  createAdapter: (config, projectName) => {
3054
3355
  const c = config;
3055
3356
  return new QoderAdapter(c.api_key, c.gateway, projectName, c.forward_gateway);
@@ -3058,7 +3359,7 @@ registerProvider({
3058
3359
 
3059
3360
  // src/internal/providers/bailian/adapter.ts
3060
3361
  import { readFileSync as readFileSync3 } from "fs";
3061
- import { basename as basename4, dirname as dirname3, extname, resolve as resolve3 } from "path";
3362
+ import { basename as basename3, dirname as dirname3, extname, resolve as resolve3 } from "path";
3062
3363
  import JSZip3 from "jszip";
3063
3364
 
3064
3365
  // src/internal/providers/bailian/client.ts
@@ -3252,8 +3553,8 @@ function mapAgent3(name, decl, refs, version, projectName, skillVersions) {
3252
3553
  }
3253
3554
  const BAILIAN_BUILTINS = /* @__PURE__ */ new Set(["bash", "read", "write", "edit", "glob", "grep", "download_file"]);
3254
3555
  if (decl.tools) {
3255
- const toolConfigs = decl.tools.builtin.filter((t) => BAILIAN_BUILTINS.has(t)).map((toolName) => ({
3256
- name: toolName,
3556
+ const toolConfigs = resolveBuiltinTools(decl.tools, { supportedWireNames: BAILIAN_BUILTINS }).map((tool) => ({
3557
+ name: tool.wireName,
3257
3558
  enabled: true
3258
3559
  }));
3259
3560
  body.tools = [
@@ -3315,7 +3616,13 @@ function mapSession3(bindings) {
3315
3616
  if (bindings.metadata) body.metadata = bindings.metadata;
3316
3617
  if (bindings.vault_ids.length) body.vault_ids = bindings.vault_ids;
3317
3618
  const resources = [];
3318
- for (const f of bindings.files ?? []) resources.push({ type: "file", file_id: f.file_id, mount_path: f.mount_path });
3619
+ for (const f of bindings.files ?? []) {
3620
+ resources.push({
3621
+ type: "file",
3622
+ file_id: f.file_id,
3623
+ mount_path: resolveSandboxMountPath("bailian", f.mount_path)
3624
+ });
3625
+ }
3319
3626
  if (resources.length) body.resources = resources;
3320
3627
  if (bindings.memory_store_ids.length) body.memory_store_ids = bindings.memory_store_ids;
3321
3628
  return body;
@@ -3763,7 +4070,7 @@ var BailianAdapter = class _BailianAdapter {
3763
4070
  const fullPath = resolve3(dirname3(basePath), source);
3764
4071
  const content = readFileSync3(fullPath);
3765
4072
  const formData = new FormData();
3766
- formData.append("file", new File([new Uint8Array(content)], basename4(fullPath)));
4073
+ formData.append("file", new File([new Uint8Array(content)], basename3(fullPath)));
3767
4074
  const res = await this.client.postFormData("/files", formData);
3768
4075
  const fileId = res.id;
3769
4076
  await this.waitForFileAvailable(fileId);
@@ -3828,7 +4135,7 @@ var BailianAdapter = class _BailianAdapter {
3828
4135
  async uploadFile(filePath, options) {
3829
4136
  const resolved = resolve3(filePath);
3830
4137
  const content = readFileSync3(resolved);
3831
- const fileName = options?.name ?? basename4(resolved);
4138
+ const fileName = options?.name ?? basename3(resolved);
3832
4139
  return this.uploadFileContent(new Uint8Array(content), fileName, {
3833
4140
  purpose: options?.purpose
3834
4141
  });
@@ -3973,6 +4280,7 @@ registerProvider({
3973
4280
  name: "bailian",
3974
4281
  configSchema: bailianConfigSchema,
3975
4282
  capabilities: BAILIAN_CAPABILITIES,
4283
+ features: { tool_permissions: false, session_resources: [] },
3976
4284
  createAdapter: (config, projectName) => {
3977
4285
  const c = config;
3978
4286
  return new BailianAdapter(c.api_key, c.workspace_id, c.base_url, projectName);
@@ -3981,7 +4289,7 @@ registerProvider({
3981
4289
 
3982
4290
  // src/internal/providers/ark/adapter.ts
3983
4291
  import { readFileSync as readFileSync4 } from "fs";
3984
- import { basename as basename5, dirname as dirname4, resolve as resolve4 } from "path";
4292
+ import { basename as basename4, dirname as dirname4, resolve as resolve4 } from "path";
3985
4293
  import JSZip4 from "jszip";
3986
4294
 
3987
4295
  // src/internal/providers/resource-naming.ts
@@ -4106,6 +4414,7 @@ function agentToDecl4(raw) {
4106
4414
  const skills = raw.skills;
4107
4415
  const multiagent = raw.multiagent;
4108
4416
  let builtinTools;
4417
+ let builtinPermissions;
4109
4418
  let allToolsEnabled = false;
4110
4419
  if (tools?.length) {
4111
4420
  const toolset = tools.find((t) => t.type === "agent_toolset_20260701");
@@ -4114,6 +4423,7 @@ function agentToDecl4(raw) {
4114
4423
  const configs = toolset.configs ?? [];
4115
4424
  if (configs.length > 0) {
4116
4425
  builtinTools = configs.filter((c) => c.enabled !== false).map((c) => c.name);
4426
+ builtinPermissions = permissionOverridesFromWire(configs);
4117
4427
  } else if (defaultConfig?.enabled) {
4118
4428
  allToolsEnabled = true;
4119
4429
  }
@@ -4144,7 +4454,7 @@ function agentToDecl4(raw) {
4144
4454
  }
4145
4455
  let toolsDecl;
4146
4456
  if (builtinTools?.length) {
4147
- toolsDecl = { builtin: builtinTools };
4457
+ toolsDecl = { builtin: builtinTools, permissions: builtinPermissions };
4148
4458
  } else if (allToolsEnabled) {
4149
4459
  toolsDecl = {
4150
4460
  builtin: ["read", "write", "edit", "bash", "glob", "grep", "web_search", "web_fetch"]
@@ -4205,16 +4515,11 @@ function mapAgent4(name, decl, refs, version, projectName) {
4205
4515
  body.metadata = decl.metadata;
4206
4516
  }
4207
4517
  if (decl.tools) {
4208
- const toolConfigs = decl.tools.builtin.filter((toolName) => ARK_BUILTINS.has(toolName)).map((toolName) => {
4209
- const permission = decl.tools?.permissions?.[toolName] ?? "allow";
4210
- return {
4211
- name: toolName,
4212
- enabled: true,
4213
- permission_policy: {
4214
- type: permission === "ask" ? "always_ask" : "always_allow"
4215
- }
4216
- };
4217
- });
4518
+ const toolConfigs = resolveBuiltinTools(decl.tools, { supportedWireNames: ARK_BUILTINS }).map((tool) => ({
4519
+ name: tool.wireName,
4520
+ enabled: true,
4521
+ permission_policy: toPermissionPolicy(tool.permission)
4522
+ }));
4218
4523
  body.tools = [
4219
4524
  {
4220
4525
  type: "agent_toolset_20260701",
@@ -4369,7 +4674,7 @@ function mapSession4(bindings) {
4369
4674
  resources.push({
4370
4675
  type: "file",
4371
4676
  file_id: f.file_id,
4372
- mount_path: f.mount_path
4677
+ mount_path: resolveSandboxMountPath("ark", f.mount_path)
4373
4678
  });
4374
4679
  if (resources.length) body.resources = resources;
4375
4680
  return body;
@@ -4643,7 +4948,7 @@ var ArkAdapter = class _ArkAdapter {
4643
4948
  const fullPath = resolve4(dirname4(basePath), source);
4644
4949
  const content = readFileSync4(fullPath);
4645
4950
  const formData = new FormData();
4646
- formData.append("file", new File([new Uint8Array(content)], basename5(fullPath)));
4951
+ formData.append("file", new File([new Uint8Array(content)], basename4(fullPath)));
4647
4952
  formData.append("purpose", "agent");
4648
4953
  const res = await this.client.postFormData("/files", formData);
4649
4954
  return res.file_id ?? res.id;
@@ -4692,7 +4997,7 @@ var ArkAdapter = class _ArkAdapter {
4692
4997
  async uploadFile(filePath, options) {
4693
4998
  const resolved = resolve4(filePath);
4694
4999
  const content = readFileSync4(resolved);
4695
- const fileName = options?.name ?? basename5(resolved);
5000
+ const fileName = options?.name ?? basename4(resolved);
4696
5001
  return this.uploadFileContent(new Uint8Array(content), fileName, {
4697
5002
  purpose: options?.purpose
4698
5003
  });
@@ -4773,6 +5078,7 @@ registerProvider({
4773
5078
  name: "ark",
4774
5079
  configSchema: arkConfigSchema,
4775
5080
  capabilities: ARK_CAPABILITIES,
5081
+ features: { tool_permissions: true, session_resources: [] },
4776
5082
  createAdapter: (config, projectName) => {
4777
5083
  const c = config;
4778
5084
  return new ArkAdapter(c.api_key, projectName);
@@ -4885,7 +5191,7 @@ async function resolveFileReferences(config, configPath) {
4885
5191
  }
4886
5192
 
4887
5193
  // src/internal/parser/resolve-project-config.ts
4888
- import { basename as basename6, dirname as dirname7, resolve as resolve7 } from "path";
5194
+ import { basename as basename5, dirname as dirname7, resolve as resolve7 } from "path";
4889
5195
 
4890
5196
  // src/internal/parser/schema.ts
4891
5197
  import { z as z5 } from "zod";
@@ -5030,8 +5336,32 @@ var mcpToolkitSchema = z5.object({
5030
5336
  }));
5031
5337
  var toolsSchema = z5.object({
5032
5338
  builtin: z5.array(z5.string()),
5339
+ default_permission: z5.enum(["allow", "ask"]).optional(),
5033
5340
  mcp: z5.array(mcpToolkitSchema).optional(),
5034
5341
  permissions: z5.record(z5.string(), z5.enum(["allow", "ask"])).optional()
5342
+ }).superRefine((tools, ctx) => {
5343
+ const enabled = new Set(tools.builtin.map(canonicalToolName));
5344
+ const seen = /* @__PURE__ */ new Map();
5345
+ for (const key of Object.keys(tools.permissions ?? {})) {
5346
+ const canonical = canonicalToolName(key);
5347
+ const previous = seen.get(canonical);
5348
+ if (previous) {
5349
+ ctx.addIssue({
5350
+ code: "custom",
5351
+ path: ["permissions", key],
5352
+ message: `duplicates permission key '${previous}' after tool-name normalization`
5353
+ });
5354
+ } else {
5355
+ seen.set(canonical, key);
5356
+ }
5357
+ if (!enabled.has(canonical)) {
5358
+ ctx.addIssue({
5359
+ code: "custom",
5360
+ path: ["permissions", key],
5361
+ message: `references tool '${key}' which is not enabled in tools.builtin`
5362
+ });
5363
+ }
5364
+ }
5035
5365
  });
5036
5366
  var multiagentSchema = z5.object({
5037
5367
  type: z5.literal("coordinator"),
@@ -5052,6 +5382,15 @@ var agentSkillRefSchema = z5.object({
5052
5382
  var agentDeliverySchema = z5.object({
5053
5383
  type: z5.enum(["managed", "forward"])
5054
5384
  });
5385
+ var sessionGithubRepoResourceSchema = z5.object({
5386
+ type: z5.literal("github_repository"),
5387
+ url: z5.string().url(),
5388
+ checkout: z5.object({ branch: z5.string().min(1).optional(), commit: z5.string().min(1).optional() }).refine((value) => !(value.branch && value.commit), {
5389
+ message: "checkout accepts either branch or commit, not both"
5390
+ }).optional(),
5391
+ mount_path: z5.string().optional(),
5392
+ authorization_token: z5.string().min(1)
5393
+ });
5055
5394
  var agentSchema = z5.object({
5056
5395
  name: z5.string().optional(),
5057
5396
  description: z5.string().optional(),
@@ -5065,6 +5404,7 @@ var agentSchema = z5.object({
5065
5404
  skills: z5.array(z5.union([z5.string(), agentSkillRefSchema])).optional(),
5066
5405
  vault: z5.string().optional(),
5067
5406
  memory_stores: z5.array(z5.string()).optional(),
5407
+ resources: z5.array(sessionGithubRepoResourceSchema).optional(),
5068
5408
  multiagent: multiagentSchema.optional(),
5069
5409
  metadata: z5.record(z5.string(), z5.string()).optional(),
5070
5410
  delivery: z5.record(z5.string(), agentDeliverySchema).optional()
@@ -5132,7 +5472,8 @@ var deploymentSchema = z5.object({
5132
5472
  schedule: scheduleSchema.optional(),
5133
5473
  description: z5.string().optional(),
5134
5474
  provider: z5.string().optional(),
5135
- metadata: z5.record(z5.string(), z5.string()).optional()
5475
+ metadata: z5.record(z5.string(), z5.string()).optional(),
5476
+ environment_variables: z5.string().optional()
5136
5477
  });
5137
5478
  var projectConfigSchema = z5.object({
5138
5479
  version: z5.string(),
@@ -5203,7 +5544,7 @@ async function loadConfig(filePath, resolveEnv = false) {
5203
5544
  // src/internal/parser/resolve-project-config.ts
5204
5545
  async function resolveProjectConfig(filePath, options = {}) {
5205
5546
  const configPath = resolve7(filePath);
5206
- const projectName = options.projectName ?? basename6(dirname7(configPath));
5547
+ const projectName = options.projectName ?? basename5(dirname7(configPath));
5207
5548
  const { config: parsed, errors } = await loadConfig(configPath, options.resolveEnv ?? true);
5208
5549
  if (errors.length > 0) {
5209
5550
  throw new UserError(errors.join("\n"));
@@ -6463,6 +6804,43 @@ function collectProviderCapabilities(config, providers, diagnostics) {
6463
6804
  for (const [name, agent] of Object.entries(config.agents ?? {})) {
6464
6805
  if (agent.provider && agent.provider !== providerName) continue;
6465
6806
  const delivery = agent.delivery?.[providerName]?.type ?? "managed";
6807
+ const address = {
6808
+ type: delivery === "forward" ? "template" : "agent",
6809
+ name,
6810
+ provider: providerName
6811
+ };
6812
+ const asksForApproval = agent.tools?.default_permission === "ask" || Object.values(agent.tools?.permissions ?? {}).some((permission) => permission === "ask");
6813
+ if (asksForApproval && !def.features.tool_permissions) {
6814
+ diagnostics.error(
6815
+ `${providerName}.agent.tool_permissions.unsupported`,
6816
+ `agent.${name}: provider '${providerName}' cannot enforce interactive tool permission 'ask'.`,
6817
+ address
6818
+ );
6819
+ }
6820
+ for (const resource of agent.resources ?? []) {
6821
+ if (!def.features.session_resources.includes(resource.type)) {
6822
+ diagnostics.error(
6823
+ `${providerName}.agent.session_resource.${resource.type}.unsupported`,
6824
+ `agent.${name}: provider '${providerName}' does not support Session resource type '${resource.type}'.`,
6825
+ address
6826
+ );
6827
+ }
6828
+ const mountPrefix = providerMountPrefix(providerName);
6829
+ if (mountPrefix && resource.mount_path && resource.mount_path !== mountPrefix && !resource.mount_path.startsWith(`${mountPrefix}/`)) {
6830
+ diagnostics.error(
6831
+ `${providerName}.agent.session_resource.mount_path.invalid`,
6832
+ `agent.${name}: ${providerName} Session resource mount_path must start with '${mountPrefix}/'.`,
6833
+ address
6834
+ );
6835
+ }
6836
+ }
6837
+ if (delivery === "forward" && agent.resources?.length) {
6838
+ diagnostics.error(
6839
+ `${providerName}.template.session_resources.unsupported`,
6840
+ `agent.${name}: Forward delivery cannot attach Agent Session resources; use managed delivery.`,
6841
+ address
6842
+ );
6843
+ }
6466
6844
  if (delivery === "forward" && !isSupported(caps, "template")) {
6467
6845
  diagnostics.error(
6468
6846
  `${providerName}.agent.delivery.forward.unsupported`,
@@ -6536,6 +6914,14 @@ function collectProviderCapabilities(config, providers, diagnostics) {
6536
6914
  }
6537
6915
  }
6538
6916
  for (const [name, deployment] of Object.entries(config.deployments ?? {})) {
6917
+ if (deployment.provider && deployment.provider !== providerName) continue;
6918
+ if (deployment.environment_variables !== void 0) {
6919
+ diagnostics.error(
6920
+ `${providerName}.deployment.environment_variables.unsupported`,
6921
+ `deployment.${name}: environment_variables is supported only by Qoder deployments; remove it or pin this deployment to the qoder provider.`,
6922
+ { type: "deployment", name, provider: providerName }
6923
+ );
6924
+ }
6539
6925
  if (deployment.tunnel && (!deployment.provider || deployment.provider === providerName)) {
6540
6926
  diagnostics.error(
6541
6927
  `${providerName}.deployment.tunnel.unsupported`,
@@ -7498,6 +7884,12 @@ async function readYaml(path) {
7498
7884
  }
7499
7885
 
7500
7886
  // src/internal/core/deployment-runtime.ts
7887
+ async function listRemoteDeploymentsForContext(ctx, provider, filter) {
7888
+ const adapter2 = getRuntimeProvider(ctx, provider);
7889
+ if (!adapter2.listDeployments)
7890
+ throw new UserError(`Provider '${provider}' does not support remote deployment listing.`);
7891
+ return adapter2.listDeployments(filter);
7892
+ }
7501
7893
  function listDeploymentsForContext(ctx, providerFilter) {
7502
7894
  let rows = ctx.state.listResources().filter((resource) => resource.address.type === "deployment");
7503
7895
  if (providerFilter) {
@@ -7542,6 +7934,14 @@ async function runDeploymentForContext(ctx, name, adapter2, resolvedProvider) {
7542
7934
  result: await effectiveAdapter.runDeployment(depCtx)
7543
7935
  };
7544
7936
  }
7937
+ async function pauseDeploymentForContext(ctx, name, paused, resolvedProvider) {
7938
+ const provider = resolvedProvider ?? resolveDeploymentProvider(name, ctx.config);
7939
+ const adapter2 = getRuntimeProvider(ctx, provider);
7940
+ const operation = paused ? adapter2.pauseDeployment : adapter2.unpauseDeployment;
7941
+ if (!operation)
7942
+ throw new UserError(`Provider '${provider}' does not support ${paused ? "pausing" : "unpausing"} deployments.`);
7943
+ return operation.call(adapter2, buildDeploymentContext(ctx, name, provider));
7944
+ }
7545
7945
  function getDeploymentRuntimeProviderForContext(ctx, name, overrideProvider) {
7546
7946
  return resolveDeploymentProvider(name, ctx.config, overrideProvider);
7547
7947
  }
@@ -7943,7 +8343,21 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
7943
8343
  const available = Object.keys(config.agents ?? {}).join(", ");
7944
8344
  throw new UserError(`Agent '${agentName}' not found in config. Available agents: ${available || "(none)"}`);
7945
8345
  }
8346
+ const sessionResources = options.resources ?? agent.resources;
8347
+ const providerFeatures = getProvider(provider)?.features;
8348
+ for (const resource of sessionResources ?? []) {
8349
+ if (!providerFeatures?.session_resources.includes(resource.type)) {
8350
+ throw new UserError(
8351
+ `Provider '${provider}' does not support Session resource type '${resource.type}' for agent '${agentName}'.`
8352
+ );
8353
+ }
8354
+ }
7946
8355
  if (resolveAgentMaterialization(provider, agent).resourceType === "template") {
8356
+ if (sessionResources?.length) {
8357
+ throw new UserError(
8358
+ `Forward session for '${agentName}' cannot attach Agent resources. Use managed delivery for GitHub repositories.`
8359
+ );
8360
+ }
7947
8361
  const templateId = requireRef(state, { type: "template", name: agentName, provider });
7948
8362
  const defaultIdentity = config.defaults?.identity;
7949
8363
  const identityId = options.identityId ?? (defaultIdentity ? requireRef(state, { type: "identity", name: defaultIdentity, provider }) : void 0);
@@ -8001,6 +8415,7 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
8001
8415
  vault_ids: vaultIds,
8002
8416
  memory_store_ids: memoryStoreIds,
8003
8417
  files: (options.files ?? []).map((f) => ({ file_id: f.fileId, mount_path: f.mountPath })),
8418
+ resources: sessionResources,
8004
8419
  title: options.title,
8005
8420
  metadata: options.metadata
8006
8421
  };
@@ -8370,6 +8785,7 @@ function toAgentSkillRef(skill) {
8370
8785
  var TERMINAL_SESSION_STATUSES = /* @__PURE__ */ new Set(["idle", "completed", "failed", "terminated", "deleted"]);
8371
8786
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
8372
8787
  var DEFAULT_POLL_TIMEOUT_MS = 10 * 60 * 1e3;
8788
+ var POLL_INITIAL_INTERVAL_MS = 300;
8373
8789
  function resolveAgentName(agents, agentName) {
8374
8790
  if (agentName) return agentName;
8375
8791
  const names = Object.keys(agents ?? {});
@@ -8400,6 +8816,7 @@ async function createSessionForAgent(ctx, options = {}) {
8400
8816
  vaultIds: options.vaultIds,
8401
8817
  memoryStores: options.memoryStores,
8402
8818
  files: options.files,
8819
+ resources: options.resources,
8403
8820
  title: options.title,
8404
8821
  metadata: options.metadata
8405
8822
  });
@@ -8418,6 +8835,7 @@ async function startSessionRun(ctx, prompt, options = {}) {
8418
8835
  vaultIds: options.vaultIds,
8419
8836
  memoryStores: options.memoryStores,
8420
8837
  files: options.files,
8838
+ resources: options.resources,
8421
8839
  title: options.title,
8422
8840
  metadata: options.metadata
8423
8841
  });
@@ -8426,7 +8844,7 @@ async function startSessionRun(ctx, prompt, options = {}) {
8426
8844
  agentName,
8427
8845
  provider,
8428
8846
  session,
8429
- events: streamMessageEvents(adapter2, session.id, preparePromptForProvider(prompt, bindings.files, provider))
8847
+ events: streamMessageEvents(adapter2, session.id, prepareInitialSessionPrompt(prompt, bindings, provider))
8430
8848
  };
8431
8849
  }
8432
8850
  function streamMessageEvents(adapter2, sessionId, message) {
@@ -8440,15 +8858,12 @@ async function sendSessionMessageStreaming(ctx, sessionId, message, options = {}
8440
8858
  return streamMessageEvents(adapter2, sessionId, message);
8441
8859
  }
8442
8860
  async function startSessionRunPolling(ctx, prompt, options = {}) {
8443
- const run = await createSessionForAgent(ctx, options);
8444
- const adapter2 = getRuntimeProvider(ctx, run.provider);
8445
- const hintedPrompt = preparePromptForProvider(
8446
- prompt,
8447
- options.files?.map((f) => ({ mount_path: f.mountPath })),
8448
- run.provider
8449
- );
8450
- const collected = await sendSessionMessageAndCollectEvents(adapter2, run.session.id, hintedPrompt, options);
8451
- return { ...run, ...collected };
8861
+ const { agentName, provider, adapter: adapter2 } = resolveSessionRuntime(ctx, options);
8862
+ const bindings = buildSessionBindings(agentName, ctx.config, provider, ctx.state, options);
8863
+ const session = await adapter2.createSession(bindings);
8864
+ const initialPrompt = prepareInitialSessionPrompt(prompt, bindings, provider);
8865
+ const collected = await sendSessionMessageAndCollectEvents(adapter2, session.id, initialPrompt, options);
8866
+ return { agentName, provider, session, ...collected };
8452
8867
  }
8453
8868
  async function sendSessionMessageAndCollectEvents(adapter2, sessionId, message, options = {}) {
8454
8869
  const eventId = await adapter2.sendSessionMessage(sessionId, message);
@@ -8464,8 +8879,9 @@ async function sendSessionMessagePolling(ctx, sessionId, message, options = {})
8464
8879
  }
8465
8880
  async function collectEventsUntilTerminal(adapter2, sessionId, options = {}) {
8466
8881
  const start = Date.now();
8467
- const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
8882
+ const maxPollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
8468
8883
  const pollTimeoutMs = options.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
8884
+ let currentIntervalMs = Math.min(POLL_INITIAL_INTERVAL_MS, maxPollIntervalMs);
8469
8885
  let terminalStatus = "idle";
8470
8886
  let result;
8471
8887
  if (options.afterId) {
@@ -8482,7 +8898,8 @@ async function collectEventsUntilTerminal(adapter2, sessionId, options = {}) {
8482
8898
  terminalStatus = terminalEvent.status;
8483
8899
  break;
8484
8900
  }
8485
- await delay(pollIntervalMs);
8901
+ await delay(currentIntervalMs);
8902
+ currentIntervalMs = Math.min(currentIntervalMs * 2, maxPollIntervalMs);
8486
8903
  }
8487
8904
  } else {
8488
8905
  while (true) {
@@ -8492,7 +8909,8 @@ async function collectEventsUntilTerminal(adapter2, sessionId, options = {}) {
8492
8909
  terminalStatus = session.status;
8493
8910
  break;
8494
8911
  }
8495
- await delay(pollIntervalMs);
8912
+ await delay(currentIntervalMs);
8913
+ currentIntervalMs = Math.min(currentIntervalMs * 2, maxPollIntervalMs);
8496
8914
  }
8497
8915
  result = await adapter2.listSessionEvents(sessionId, { limit: 100 });
8498
8916
  }
@@ -8640,7 +9058,28 @@ function buildAgentNameByRemoteId(ctx, provider) {
8640
9058
  }
8641
9059
  async function* streamWithResume(adapter2, sessionId, message) {
8642
9060
  const eventId = await adapter2.sendSessionMessage(sessionId, message);
8643
- yield* adapter2.streamSessionEvents(sessionId, eventId ? { after_id: eventId } : void 0);
9061
+ let lastEventId = eventId;
9062
+ let reachedTerminal = false;
9063
+ let reconnectIntervalMs = POLL_INITIAL_INTERVAL_MS;
9064
+ const start = Date.now();
9065
+ while (!reachedTerminal) {
9066
+ assertNotTimedOut(start, DEFAULT_POLL_TIMEOUT_MS);
9067
+ for await (const event of adapter2.streamSessionEvents(
9068
+ sessionId,
9069
+ lastEventId ? { after_id: lastEventId } : void 0
9070
+ )) {
9071
+ if (event.id) lastEventId = event.id;
9072
+ yield event;
9073
+ if (event.type === "status" && isTerminalSessionStatus(event.status)) {
9074
+ reachedTerminal = true;
9075
+ break;
9076
+ }
9077
+ }
9078
+ if (!reachedTerminal) {
9079
+ await delay(reconnectIntervalMs);
9080
+ reconnectIntervalMs = Math.min(reconnectIntervalMs * 2, DEFAULT_POLL_INTERVAL_MS);
9081
+ }
9082
+ }
8644
9083
  }
8645
9084
  async function* streamConnectBeforeSend(adapter2, sessionId, message) {
8646
9085
  const iterator = adapter2.streamSessionEvents(sessionId)[Symbol.asyncIterator]();
@@ -9375,6 +9814,7 @@ export {
9375
9814
  listMemoryVersions,
9376
9815
  listProviderModelsForContext,
9377
9816
  listProviderNames,
9817
+ listRemoteDeploymentsForContext,
9378
9818
  listSessionEvents,
9379
9819
  listSessionSummaries,
9380
9820
  listSkills,
@@ -9383,8 +9823,10 @@ export {
9383
9823
  loadProviderConfigIntoEnvSync,
9384
9824
  migrateConfig,
9385
9825
  parseStateAddress,
9826
+ pauseDeploymentForContext,
9386
9827
  planDestroyProjectContext,
9387
9828
  planProjectContext,
9829
+ prepareInitialSessionPrompt,
9388
9830
  preparePromptForProvider,
9389
9831
  prependFileHint,
9390
9832
  providerConfigPath,
@@ -9394,6 +9836,7 @@ export {
9394
9836
  resolveProjectConfig,
9395
9837
  resolveProjectConfigFromObject,
9396
9838
  resolveProviderConfigFromEnv,
9839
+ resolveRepositoryMountPath,
9397
9840
  resolveSessionProvider,
9398
9841
  resolveSyncProvider,
9399
9842
  rewriteFileMentions,