@openagentpack/sdk 0.3.0 → 0.3.1

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
@@ -24,6 +24,178 @@ var UserError = class extends Error {
24
24
  }
25
25
  };
26
26
 
27
+ // src/internal/transport.ts
28
+ var defaultFetch;
29
+ function setDefaultFetch(fetchImpl) {
30
+ defaultFetch = fetchImpl;
31
+ }
32
+ function resolveFetch() {
33
+ return defaultFetch ?? ((input, init) => fetch(input, init));
34
+ }
35
+
36
+ // src/internal/providers/base-client.ts
37
+ var ApiError = class _ApiError extends Error {
38
+ constructor(statusCode, responseBody, prefix) {
39
+ super(`${prefix} ${statusCode}: ${responseBody}`);
40
+ this.statusCode = statusCode;
41
+ this.responseBody = responseBody;
42
+ }
43
+ statusCode;
44
+ responseBody;
45
+ static isNotFound(err) {
46
+ return err instanceof _ApiError && err.statusCode === 404;
47
+ }
48
+ };
49
+ var ConflictError = class extends ApiError {
50
+ };
51
+ var BaseApiClient = class {
52
+ isConflict(_status, _body) {
53
+ return false;
54
+ }
55
+ async throwIfError(res) {
56
+ if (res.ok) return;
57
+ const body = await res.text();
58
+ if (this.isConflict(res.status, body)) throw new ConflictError(res.status, body, this.errorPrefix);
59
+ throw new ApiError(res.status, body, this.errorPrefix);
60
+ }
61
+ async post(path, body) {
62
+ const res = await resolveFetch()(`${this.baseUrl}${path}`, {
63
+ method: "POST",
64
+ headers: this.headers(),
65
+ body: JSON.stringify(body)
66
+ });
67
+ await this.throwIfError(res);
68
+ return res.json();
69
+ }
70
+ async put(path, body) {
71
+ const res = await resolveFetch()(`${this.baseUrl}${path}`, {
72
+ method: "PUT",
73
+ headers: this.headers(),
74
+ body: JSON.stringify(body)
75
+ });
76
+ await this.throwIfError(res);
77
+ return res.json();
78
+ }
79
+ async delete(path) {
80
+ const res = await resolveFetch()(`${this.baseUrl}${path}`, {
81
+ method: "DELETE",
82
+ headers: this.headers()
83
+ });
84
+ await this.throwIfError(res);
85
+ }
86
+ async get(path) {
87
+ const res = await resolveFetch()(`${this.baseUrl}${path}`, {
88
+ method: "GET",
89
+ headers: this.headers()
90
+ });
91
+ await this.throwIfError(res);
92
+ return res.json();
93
+ }
94
+ async getBuffer(path) {
95
+ const res = await resolveFetch()(`${this.baseUrl}${path}`, {
96
+ method: "GET",
97
+ headers: this.headers()
98
+ });
99
+ await this.throwIfError(res);
100
+ return Buffer.from(await res.arrayBuffer());
101
+ }
102
+ async *sse(path, options) {
103
+ const controller = new AbortController();
104
+ const res = await resolveFetch()(`${this.baseUrl}${path}`, {
105
+ method: "GET",
106
+ headers: { ...this.headers(), Accept: "text/event-stream", ...options?.headers },
107
+ signal: controller.signal
108
+ });
109
+ await this.throwIfError(res);
110
+ const reader = res.body.getReader();
111
+ const decoder = new TextDecoder();
112
+ let buffer = "";
113
+ try {
114
+ while (true) {
115
+ const { done, value } = await reader.read();
116
+ if (done) break;
117
+ buffer += decoder.decode(value, { stream: true });
118
+ let boundary = buffer.indexOf("\n\n");
119
+ while (boundary !== -1) {
120
+ const frame = buffer.slice(0, boundary);
121
+ buffer = buffer.slice(boundary + 2);
122
+ boundary = buffer.indexOf("\n\n");
123
+ const dataLines = [];
124
+ let sseId;
125
+ for (const line of frame.split("\n")) {
126
+ if (line.startsWith(":")) continue;
127
+ if (line.startsWith("event:") && line.slice(6).trim() === "heartbeat") {
128
+ dataLines.length = 0;
129
+ break;
130
+ }
131
+ if (line.startsWith("data:")) {
132
+ dataLines.push(line.slice(5).trimStart());
133
+ }
134
+ if (line.startsWith("id:")) {
135
+ sseId = line.slice(3).trimStart();
136
+ }
137
+ }
138
+ if (dataLines.length === 0) continue;
139
+ const json = dataLines.join("\n");
140
+ try {
141
+ const parsed = JSON.parse(json);
142
+ if (sseId !== void 0 && parsed.id === void 0) {
143
+ parsed.id = sseId;
144
+ }
145
+ yield parsed;
146
+ } catch {
147
+ }
148
+ }
149
+ }
150
+ } finally {
151
+ try {
152
+ await reader.cancel();
153
+ } catch {
154
+ }
155
+ controller.abort();
156
+ reader.releaseLock();
157
+ }
158
+ }
159
+ async postFormData(path, formData) {
160
+ const { "Content-Type": _contentType, ...headers } = this.headers();
161
+ const res = await resolveFetch()(`${this.baseUrl}${path}`, {
162
+ method: "POST",
163
+ headers,
164
+ body: formData
165
+ });
166
+ await this.throwIfError(res);
167
+ return res.json();
168
+ }
169
+ async getAllPaged(path) {
170
+ const all = [];
171
+ let cursor;
172
+ for (; ; ) {
173
+ const sep = path.includes("?") ? "&" : "?";
174
+ const param = this.paginationStrategy === "page" ? "page" : "after_id";
175
+ const url = cursor ? `${path}${sep}limit=100&${param}=${encodeURIComponent(cursor)}` : `${path}${sep}limit=100`;
176
+ const res = await this.get(url);
177
+ const data = res.data ?? [];
178
+ all.push(...data);
179
+ if (data.length === 0) break;
180
+ if (this.paginationStrategy === "page") {
181
+ if (!res.next_page) break;
182
+ cursor = res.next_page;
183
+ } else {
184
+ if (!res.has_more || !res.last_id) break;
185
+ cursor = res.last_id;
186
+ }
187
+ }
188
+ return all;
189
+ }
190
+ };
191
+ function toRemoteResource(res) {
192
+ return {
193
+ id: res.id,
194
+ type: res.type,
195
+ version: res.version
196
+ };
197
+ }
198
+
27
199
  // src/internal/providers/capabilities.ts
28
200
  function isSupported(caps, kind) {
29
201
  const tier = caps?.[kind]?.tier;
@@ -80,6 +252,16 @@ function getProvider(name) {
80
252
  function allProviders() {
81
253
  return Array.from(registry.values());
82
254
  }
255
+ function parseProviderConfig(def, rawConfig) {
256
+ const result = def.configSchema.safeParse(rawConfig);
257
+ if (result.success) return result.data;
258
+ const details = result.error.issues.map((issue) => {
259
+ const path = issue.path.join(".");
260
+ return path ? `${path}: ${issue.message}` : issue.message;
261
+ });
262
+ throw new UserError(`Provider '${def.name}' config invalid:
263
+ ${details.join("\n")}`);
264
+ }
83
265
  function buildProviders(providersConfig, projectName) {
84
266
  const adapters = /* @__PURE__ */ new Map();
85
267
  for (const [name, rawConfig] of Object.entries(providersConfig)) {
@@ -87,7 +269,7 @@ function buildProviders(providersConfig, projectName) {
87
269
  if (!def) {
88
270
  throw new UserError(`Unknown provider '${name}'. Registered: ${Array.from(registry.keys()).join(", ")}`);
89
271
  }
90
- const parsed = def.configSchema.parse(rawConfig);
272
+ const parsed = parseProviderConfig(def, rawConfig);
91
273
  const adapter2 = def.createAdapter(parsed, projectName);
92
274
  validateProviderFacets(def, adapter2);
93
275
  adapters.set(name, adapter2);
@@ -97,8 +279,11 @@ function buildProviders(providersConfig, projectName) {
97
279
  var PROVIDER_ENV_VARS = {
98
280
  bailian: {
99
281
  api_key: { env: ["DASHSCOPE_API_KEY", "BAILIAN_API_KEY"], required: true },
100
- workspace_id: { env: ["BAILIAN_WORKSPACE_ID"], required: true },
101
- base_url: { env: ["BAILIAN_BASE_URL"], required: false }
282
+ // Neither workspace_id nor base_url is required on its own; the config schema
283
+ // enforces "at least one" so a host can supply just base_url (BAILIAN_BASE_URL).
284
+ // base_url is the preferred placeholder emitted by `agents sync`.
285
+ workspace_id: { env: ["BAILIAN_WORKSPACE_ID"], required: false },
286
+ base_url: { env: ["BAILIAN_BASE_URL"], required: false, placeholder: true }
102
287
  },
103
288
  qoder: {
104
289
  api_key: { env: ["QODER_PAT", "QODER_API_KEY"], required: true },
@@ -117,8 +302,8 @@ function placeholderProviderConfig(providerName) {
117
302
  const envMap = PROVIDER_ENV_VARS[providerName];
118
303
  if (!envMap) return {};
119
304
  const out = {};
120
- for (const [field, { env, required }] of Object.entries(envMap)) {
121
- if (required && env[0]) out[field] = `\${${env[0]}}`;
305
+ for (const [field, { env, required, placeholder }] of Object.entries(envMap)) {
306
+ if ((required || placeholder) && env[0]) out[field] = `\${${env[0]}}`;
122
307
  }
123
308
  return out;
124
309
  }
@@ -156,7 +341,7 @@ function buildProviderFromEnv(providerName, projectName) {
156
341
  throw new UserError(`Unknown provider '${providerName}'. Registered: ${Array.from(registry.keys()).join(", ")}`);
157
342
  }
158
343
  const config = resolveProviderConfigFromEnv(providerName);
159
- const parsed = def.configSchema.parse(config);
344
+ const parsed = parseProviderConfig(def, config);
160
345
  const adapter2 = def.createAdapter(parsed, projectName);
161
346
  validateProviderFacets(def, adapter2);
162
347
  return adapter2;
@@ -210,161 +395,6 @@ function skillNameFromFiles(files) {
210
395
  return match?.[1]?.replace(/^["']|["']$/g, "") || void 0;
211
396
  }
212
397
 
213
- // src/internal/providers/base-client.ts
214
- var ApiError = class _ApiError extends Error {
215
- constructor(statusCode, responseBody, prefix) {
216
- super(`${prefix} ${statusCode}: ${responseBody}`);
217
- this.statusCode = statusCode;
218
- this.responseBody = responseBody;
219
- }
220
- statusCode;
221
- responseBody;
222
- static isNotFound(err) {
223
- return err instanceof _ApiError && err.statusCode === 404;
224
- }
225
- };
226
- var ConflictError = class extends ApiError {
227
- };
228
- var BaseApiClient = class {
229
- isConflict(_status, _body) {
230
- return false;
231
- }
232
- async throwIfError(res) {
233
- if (res.ok) return;
234
- const body = await res.text();
235
- if (this.isConflict(res.status, body)) throw new ConflictError(res.status, body, this.errorPrefix);
236
- throw new ApiError(res.status, body, this.errorPrefix);
237
- }
238
- async post(path, body) {
239
- const res = await fetch(`${this.baseUrl}${path}`, {
240
- method: "POST",
241
- headers: this.headers(),
242
- body: JSON.stringify(body)
243
- });
244
- await this.throwIfError(res);
245
- return res.json();
246
- }
247
- async put(path, body) {
248
- const res = await fetch(`${this.baseUrl}${path}`, {
249
- method: "PUT",
250
- headers: this.headers(),
251
- body: JSON.stringify(body)
252
- });
253
- await this.throwIfError(res);
254
- return res.json();
255
- }
256
- async delete(path) {
257
- const res = await fetch(`${this.baseUrl}${path}`, {
258
- method: "DELETE",
259
- headers: this.headers()
260
- });
261
- await this.throwIfError(res);
262
- }
263
- async get(path) {
264
- const res = await fetch(`${this.baseUrl}${path}`, {
265
- method: "GET",
266
- headers: this.headers()
267
- });
268
- await this.throwIfError(res);
269
- return res.json();
270
- }
271
- async getBuffer(path) {
272
- const res = await fetch(`${this.baseUrl}${path}`, {
273
- method: "GET",
274
- headers: this.headers()
275
- });
276
- await this.throwIfError(res);
277
- return Buffer.from(await res.arrayBuffer());
278
- }
279
- async *sse(path, options) {
280
- const controller = new AbortController();
281
- const res = await fetch(`${this.baseUrl}${path}`, {
282
- method: "GET",
283
- headers: { ...this.headers(), Accept: "text/event-stream", ...options?.headers },
284
- signal: controller.signal
285
- });
286
- await this.throwIfError(res);
287
- const reader = res.body.getReader();
288
- const decoder = new TextDecoder();
289
- let buffer = "";
290
- try {
291
- while (true) {
292
- const { done, value } = await reader.read();
293
- if (done) break;
294
- buffer += decoder.decode(value, { stream: true });
295
- let boundary = buffer.indexOf("\n\n");
296
- while (boundary !== -1) {
297
- const frame = buffer.slice(0, boundary);
298
- buffer = buffer.slice(boundary + 2);
299
- boundary = buffer.indexOf("\n\n");
300
- const dataLines = [];
301
- for (const line of frame.split("\n")) {
302
- if (line.startsWith(":")) continue;
303
- if (line.startsWith("event:") && line.slice(6).trim() === "heartbeat") {
304
- dataLines.length = 0;
305
- break;
306
- }
307
- if (line.startsWith("data:")) {
308
- dataLines.push(line.slice(5).trimStart());
309
- }
310
- }
311
- if (dataLines.length === 0) continue;
312
- const json = dataLines.join("\n");
313
- try {
314
- yield JSON.parse(json);
315
- } catch {
316
- }
317
- }
318
- }
319
- } finally {
320
- try {
321
- await reader.cancel();
322
- } catch {
323
- }
324
- controller.abort();
325
- reader.releaseLock();
326
- }
327
- }
328
- async postFormData(path, formData) {
329
- const { "Content-Type": _contentType, ...headers } = this.headers();
330
- const res = await fetch(`${this.baseUrl}${path}`, {
331
- method: "POST",
332
- headers,
333
- body: formData
334
- });
335
- await this.throwIfError(res);
336
- return res.json();
337
- }
338
- async getAllPaged(path) {
339
- const all = [];
340
- let cursor;
341
- for (; ; ) {
342
- const sep = path.includes("?") ? "&" : "?";
343
- const param = this.paginationStrategy === "page" ? "page" : "after_id";
344
- const url = cursor ? `${path}${sep}limit=100&${param}=${encodeURIComponent(cursor)}` : `${path}${sep}limit=100`;
345
- const res = await this.get(url);
346
- const data = res.data ?? [];
347
- all.push(...data);
348
- if (data.length === 0) break;
349
- if (this.paginationStrategy === "page") {
350
- if (!res.next_page) break;
351
- cursor = res.next_page;
352
- } else {
353
- if (!res.has_more || !res.last_id) break;
354
- cursor = res.last_id;
355
- }
356
- }
357
- return all;
358
- }
359
- };
360
- function toRemoteResource(res) {
361
- return {
362
- id: res.id,
363
- type: res.type,
364
- version: res.version
365
- };
366
- }
367
-
368
398
  // src/internal/providers/memory-api.ts
369
399
  function query(path, values) {
370
400
  const params = new URLSearchParams();
@@ -849,8 +879,193 @@ function stripAgentsMetadata(value) {
849
879
  return Object.keys(out).length > 0 ? out : void 0;
850
880
  }
851
881
 
882
+ // src/internal/utils/sandbox-mount.ts
883
+ var PROVIDER_MOUNT_PREFIXES = {
884
+ qoder: "/data",
885
+ claude: "/workspace",
886
+ bailian: "/mnt",
887
+ ark: "/mnt"
888
+ };
889
+ function joinAbsolute(prefix, sub) {
890
+ const left = prefix.replace(/\/+$/, "");
891
+ const right = sub.replace(/^\/+/, "");
892
+ return `${left}/${right}`;
893
+ }
894
+ function quoteShellWord(value) {
895
+ return `'${value.replaceAll("'", `'"'"'`)}'`;
896
+ }
897
+ function stripUrlQueryAndFragment(value) {
898
+ const queryIndex = value.indexOf("?");
899
+ const fragmentIndex = value.indexOf("#");
900
+ if (queryIndex === -1) {
901
+ return fragmentIndex === -1 ? value : value.slice(0, fragmentIndex);
902
+ }
903
+ if (fragmentIndex === -1) return value.slice(0, queryIndex);
904
+ return value.slice(0, Math.min(queryIndex, fragmentIndex));
905
+ }
906
+ function stripGitSuffix(value) {
907
+ return value.toLowerCase().endsWith(".git") ? value.slice(0, -4) : value;
908
+ }
909
+ function lastRemotePathSegment(value) {
910
+ const lastSlash = value.lastIndexOf("/");
911
+ const lastColon = value.lastIndexOf(":");
912
+ return value.slice(Math.max(lastSlash, lastColon) + 1).trim();
913
+ }
914
+ function providerMountPrefix(provider) {
915
+ return PROVIDER_MOUNT_PREFIXES[provider];
916
+ }
917
+ function resolveSandboxMountPath(provider, mountPath) {
918
+ const prefix = providerMountPrefix(provider);
919
+ if (!prefix) return mountPath;
920
+ if (mountPath === prefix || mountPath.startsWith(`${prefix}/`)) return mountPath;
921
+ if (mountPath.startsWith("/")) {
922
+ throw new UserError(`${provider} mount_path must start with '${prefix}/'; received '${mountPath}'.`);
923
+ }
924
+ return joinAbsolute(prefix, mountPath);
925
+ }
926
+ function resolveRepositoryMountPath(provider, resource) {
927
+ const prefix = providerMountPrefix(provider);
928
+ if (!prefix) throw new UserError(`Provider '${provider}' has no declared mount path prefix.`);
929
+ if (resource.mount_path) {
930
+ if (resource.mount_path !== prefix && !resource.mount_path.startsWith(`${prefix}/`)) {
931
+ throw new UserError(`${provider} Git repository Session resource mount_path must start with '${prefix}/'.`);
932
+ }
933
+ return resource.mount_path;
934
+ }
935
+ const repositoryName = stripGitSuffix(lastRemotePathSegment(stripUrlQueryAndFragment(resource.url)));
936
+ if (!repositoryName) {
937
+ throw new UserError(`Cannot derive a ${provider} Git repository mount path from URL '${resource.url}'.`);
938
+ }
939
+ return provider === "qoder" ? `${prefix}/workspace/${repositoryName}` : `${prefix}/${repositoryName}`;
940
+ }
941
+ function composeFileMountHint(files, provider) {
942
+ if (!files || files.length === 0) return "";
943
+ const lines = files.map((f) => `- ${resolveSandboxMountPath(provider, f.mount_path)}`);
944
+ return [
945
+ "The user uploaded files. They are available at the following sandbox paths:",
946
+ ...lines,
947
+ "Read them from these paths when relevant."
948
+ ].join("\n");
949
+ }
950
+ function prependFileHint(prompt, files, provider) {
951
+ const hint = composeFileMountHint(files, provider);
952
+ if (!hint) return prompt;
953
+ return `${hint}
954
+
955
+ ${prompt}`;
956
+ }
957
+ var FILE_MENTION_START = "\u27E6file:";
958
+ var FILE_MENTION_END = "\u27E7";
959
+ function rewriteFileMentions(prompt, provider) {
960
+ let cursor = 0;
961
+ let rewritten = "";
962
+ while (cursor < prompt.length) {
963
+ const start = prompt.indexOf(FILE_MENTION_START, cursor);
964
+ if (start === -1) {
965
+ rewritten += prompt.slice(cursor);
966
+ break;
967
+ }
968
+ const mountPathStart = start + FILE_MENTION_START.length;
969
+ const end = prompt.indexOf(FILE_MENTION_END, mountPathStart);
970
+ if (end === -1) {
971
+ rewritten += prompt.slice(cursor);
972
+ break;
973
+ }
974
+ rewritten += prompt.slice(cursor, start);
975
+ rewritten += resolveSandboxMountPath(provider, prompt.slice(mountPathStart, end));
976
+ cursor = end + FILE_MENTION_END.length;
977
+ }
978
+ return rewritten;
979
+ }
980
+ function preparePromptForProvider(prompt, files, provider) {
981
+ return prependFileHint(rewriteFileMentions(prompt, provider), files, provider);
982
+ }
983
+ function prepareInitialSessionPrompt(prompt, bindings, provider) {
984
+ const prepared = preparePromptForProvider(prompt, bindings.files, provider);
985
+ const repositories = (bindings.resources ?? []).filter(
986
+ (resource) => resource.type === "github_repository"
987
+ );
988
+ if (repositories.length === 0) return prepared;
989
+ const paths = repositories.map((resource) => resolveRepositoryMountPath(provider, resource));
990
+ if (paths.length === 1) {
991
+ const path = paths[0];
992
+ return [
993
+ `The Git working tree for this task is mounted at \`${path}\`.`,
994
+ "Work only inside this directory unless the user explicitly requests otherwise.",
995
+ "Prefix every shell command with:",
996
+ `cd -- ${quoteShellWord(path)} &&`,
997
+ `Use absolute paths under \`${path}\` for non-shell file tools.`,
998
+ "",
999
+ prepared
1000
+ ].join("\n");
1001
+ }
1002
+ const lines = ["Git working trees for this task are mounted at:", ...paths.map((path) => `- ${path}`)];
1003
+ lines.push("Choose the appropriate working tree for the task before inspecting or modifying files.");
1004
+ return `${lines.join("\n")}
1005
+
1006
+ ${prepared}`;
1007
+ }
1008
+
1009
+ // src/internal/utils/tool-permissions.ts
1010
+ function canonicalToolName(name) {
1011
+ return name.trim().replace(/[^a-zA-Z0-9]+/g, "").toLowerCase();
1012
+ }
1013
+ function resolveBuiltinTools(tools, options = {}) {
1014
+ const permissionByName = /* @__PURE__ */ new Map();
1015
+ for (const [name, permission] of Object.entries(tools.permissions ?? {})) {
1016
+ permissionByName.set(canonicalToolName(name), permission);
1017
+ }
1018
+ const supportedByName = options.supportedWireNames ? new Map([...options.supportedWireNames].map((name) => [canonicalToolName(name), name])) : void 0;
1019
+ return tools.builtin.flatMap((configuredName) => {
1020
+ const candidate = options.toWireName?.(configuredName) ?? configuredName;
1021
+ const wireName = supportedByName?.get(canonicalToolName(candidate)) ?? (supportedByName ? void 0 : candidate);
1022
+ if (!wireName) return [];
1023
+ return [
1024
+ {
1025
+ configuredName,
1026
+ wireName,
1027
+ permission: permissionByName.get(canonicalToolName(configuredName)) ?? tools.default_permission ?? "allow"
1028
+ }
1029
+ ];
1030
+ });
1031
+ }
1032
+ function toPermissionPolicy(permission) {
1033
+ return { type: permission === "ask" ? "always_ask" : "always_allow" };
1034
+ }
1035
+ function permissionOverridesFromWire(configs, toConfigName = (name) => name) {
1036
+ const permissions = {};
1037
+ for (const config of configs) {
1038
+ if (config.enabled === false) continue;
1039
+ const rawPolicy = config.permission_policy;
1040
+ const type = typeof rawPolicy === "string" ? rawPolicy : rawPolicy && typeof rawPolicy === "object" ? rawPolicy.type : void 0;
1041
+ if (type === "always_ask") permissions[toConfigName(config.name)] = "ask";
1042
+ else if (type === "always_allow") permissions[toConfigName(config.name)] = "allow";
1043
+ }
1044
+ return Object.keys(permissions).length ? permissions : void 0;
1045
+ }
1046
+
1047
+ // src/internal/providers/session-resource-mapper.ts
1048
+ function resolveGithubRepositoryMountPath(provider, resource) {
1049
+ return resolveRepositoryMountPath(provider, resource);
1050
+ }
1051
+ function mapGithubRepositorySessionResource(resource, options = {}) {
1052
+ const entry = {
1053
+ type: "github_repository",
1054
+ url: options.mapUrl?.(resource.url) ?? resource.url,
1055
+ authorization_token: resource.authorization_token
1056
+ };
1057
+ if (resource.checkout?.branch) entry.checkout = { type: "branch", name: resource.checkout.branch };
1058
+ else if (resource.checkout?.commit) entry.checkout = { type: "commit", sha: resource.checkout.commit };
1059
+ const mountPath = options.mapMountPath?.(resource) ?? resource.mount_path;
1060
+ if (mountPath) entry.mount_path = mountPath;
1061
+ return entry;
1062
+ }
1063
+
852
1064
  // src/internal/providers/claude/mapper.ts
853
1065
  var CLAUDE_BUILTINS = /* @__PURE__ */ new Set(["read", "write", "edit", "bash", "glob", "grep", "web_search", "web_fetch"]);
1066
+ function normalizeGithubRepositoryUrlForClaude(url) {
1067
+ return url.replace(/\.git\/?$/, "");
1068
+ }
854
1069
  function credToDecl(raw, vaultName) {
855
1070
  const auth = raw.auth ?? {};
856
1071
  const name = raw.display_name || raw.id || "credential";
@@ -921,6 +1136,7 @@ function agentToDecl(raw) {
921
1136
  const skills = raw.skills;
922
1137
  const multiagent = raw.multiagent;
923
1138
  let builtinTools;
1139
+ let builtinPermissions;
924
1140
  let allToolsEnabled = false;
925
1141
  if (tools?.length) {
926
1142
  const toolset = tools.find((t) => t.type === "agent_toolset_20260401");
@@ -929,6 +1145,7 @@ function agentToDecl(raw) {
929
1145
  const configs = toolset.configs ?? [];
930
1146
  if (configs.length > 0) {
931
1147
  builtinTools = configs.filter((c) => c.enabled !== false).map((c) => c.name);
1148
+ builtinPermissions = permissionOverridesFromWire(configs);
932
1149
  } else if (defaultConfig?.enabled) {
933
1150
  allToolsEnabled = true;
934
1151
  }
@@ -955,7 +1172,7 @@ function agentToDecl(raw) {
955
1172
  }
956
1173
  let toolsDecl;
957
1174
  if (builtinTools?.length) {
958
- toolsDecl = { builtin: builtinTools };
1175
+ toolsDecl = { builtin: builtinTools, permissions: builtinPermissions };
959
1176
  } else if (allToolsEnabled) {
960
1177
  toolsDecl = {
961
1178
  builtin: ["read", "write", "edit", "bash", "glob", "grep", "web_search", "web_fetch"]
@@ -1015,16 +1232,11 @@ function mapAgent(name, decl, refs, version, projectName) {
1015
1232
  body.metadata = decl.metadata;
1016
1233
  }
1017
1234
  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
- });
1235
+ const toolConfigs = resolveBuiltinTools(decl.tools, { supportedWireNames: CLAUDE_BUILTINS }).map((tool) => ({
1236
+ name: tool.wireName,
1237
+ enabled: true,
1238
+ permission_policy: toPermissionPolicy(tool.permission)
1239
+ }));
1028
1240
  body.tools = [
1029
1241
  {
1030
1242
  type: "agent_toolset_20260401",
@@ -1140,7 +1352,7 @@ function mapDeploymentResources(decl, refs, uploadedFiles) {
1140
1352
  } else if (r.type === "github_repository") {
1141
1353
  const entry = {
1142
1354
  type: "github_repository",
1143
- url: r.url
1355
+ url: normalizeGithubRepositoryUrlForClaude(r.url)
1144
1356
  };
1145
1357
  if (r.authorization_token) entry.authorization_token = r.authorization_token;
1146
1358
  if (r.checkout?.branch) {
@@ -1269,8 +1481,16 @@ function mapSession(bindings) {
1269
1481
  resources.push({
1270
1482
  type: "file",
1271
1483
  file_id: f.file_id,
1272
- mount_path: f.mount_path
1484
+ mount_path: resolveSandboxMountPath("claude", f.mount_path)
1273
1485
  });
1486
+ for (const resource of bindings.resources ?? []) {
1487
+ resources.push(
1488
+ mapGithubRepositorySessionResource(resource, {
1489
+ mapUrl: normalizeGithubRepositoryUrlForClaude,
1490
+ mapMountPath: (item) => resolveGithubRepositoryMountPath("claude", item)
1491
+ })
1492
+ );
1493
+ }
1274
1494
  if (resources.length) body.resources = resources;
1275
1495
  return body;
1276
1496
  }
@@ -1749,6 +1969,7 @@ registerProvider({
1749
1969
  name: "claude",
1750
1970
  configSchema: claudeConfigSchema,
1751
1971
  capabilities: CLAUDE_CAPABILITIES,
1972
+ features: { tool_permissions: true, session_resources: ["github_repository"] },
1752
1973
  createAdapter: (config, projectName) => {
1753
1974
  const c = config;
1754
1975
  return new ClaudeAdapter(c.api_key, c.beta, projectName);
@@ -1757,7 +1978,7 @@ registerProvider({
1757
1978
 
1758
1979
  // src/internal/providers/qoder/adapter.ts
1759
1980
  import { readFileSync as readFileSync2 } from "fs";
1760
- import { basename as basename3, dirname as dirname2, resolve as resolve2 } from "path";
1981
+ import { basename as basename2, dirname as dirname2, resolve as resolve2 } from "path";
1761
1982
  import JSZip2 from "jszip";
1762
1983
 
1763
1984
  // src/internal/providers/qoder/client.ts
@@ -1782,57 +2003,6 @@ var QoderClient = class extends BaseApiClient {
1782
2003
  }
1783
2004
  };
1784
2005
 
1785
- // src/internal/utils/sandbox-mount.ts
1786
- var AGENTS_SESSION_PREFIX = "/mnt/session";
1787
- function joinAbsolute(prefix, sub) {
1788
- const left = prefix.replace(/\/+$/, "");
1789
- const right = sub.replace(/^\/+/, "");
1790
- return `${left}/${right}`;
1791
- }
1792
- function basename2(p) {
1793
- const trimmed = p.replace(/\/+$/, "");
1794
- const idx = trimmed.lastIndexOf("/");
1795
- return idx === -1 ? trimmed : trimmed.slice(idx + 1);
1796
- }
1797
- function resolveSandboxMountPath(provider, mountPath) {
1798
- switch (provider) {
1799
- case "ark":
1800
- case "bailian":
1801
- case "claude":
1802
- return joinAbsolute(AGENTS_SESSION_PREFIX, mountPath);
1803
- case "qoder":
1804
- return joinAbsolute("/data", basename2(mountPath));
1805
- default:
1806
- return mountPath;
1807
- }
1808
- }
1809
- function composeFileMountHint(files, provider) {
1810
- if (!files || files.length === 0) return "";
1811
- const lines = files.map((f) => `- ${resolveSandboxMountPath(provider, f.mount_path)}`);
1812
- return [
1813
- "The user uploaded files. They are available at the following sandbox paths:",
1814
- ...lines,
1815
- "Read them from these paths when relevant."
1816
- ].join("\n");
1817
- }
1818
- function prependFileHint(prompt, files, provider) {
1819
- const hint = composeFileMountHint(files, provider);
1820
- if (!hint) return prompt;
1821
- return `${hint}
1822
-
1823
- ${prompt}`;
1824
- }
1825
- var FILE_MENTION_SENTINEL_RE = /\u27E6file:(.+?)\u27E7/g;
1826
- function rewriteFileMentions(prompt, provider) {
1827
- return prompt.replace(
1828
- FILE_MENTION_SENTINEL_RE,
1829
- (_match, mountPath) => resolveSandboxMountPath(provider, mountPath)
1830
- );
1831
- }
1832
- function preparePromptForProvider(prompt, files, provider) {
1833
- return prependFileHint(rewriteFileMentions(prompt, provider), files, provider);
1834
- }
1835
-
1836
2006
  // src/internal/providers/qoder/mapper.ts
1837
2007
  function toPascalCase(name) {
1838
2008
  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("");
@@ -1945,6 +2115,7 @@ function agentToDecl2(raw) {
1945
2115
  const mcpServers = raw.mcp_servers;
1946
2116
  const skills = raw.skills;
1947
2117
  let builtinTools;
2118
+ let builtinPermissions;
1948
2119
  if (tools?.length) {
1949
2120
  const toolset = tools.find((t) => t.type === "agent_toolset_20260401");
1950
2121
  if (toolset && Array.isArray(toolset.enabled_tools)) {
@@ -1952,6 +2123,7 @@ function agentToDecl2(raw) {
1952
2123
  } else if (toolset) {
1953
2124
  const configs = toolset.configs ?? [];
1954
2125
  builtinTools = configs.filter((c) => c.enabled !== false).map((c) => normalizeToolNameFromQoder(c.name));
2126
+ builtinPermissions = permissionOverridesFromWire(configs, normalizeToolNameFromQoder);
1955
2127
  }
1956
2128
  }
1957
2129
  let mcpServerDecls;
@@ -1973,7 +2145,7 @@ function agentToDecl2(raw) {
1973
2145
  description: raw.description,
1974
2146
  model: raw.model,
1975
2147
  instructions: raw.system,
1976
- tools: builtinTools?.length ? { builtin: builtinTools } : void 0,
2148
+ tools: builtinTools?.length ? { builtin: builtinTools, permissions: builtinPermissions } : void 0,
1977
2149
  mcp_servers: mcpServerDecls,
1978
2150
  skills: skillDecls,
1979
2151
  metadata: stripAgentsMetadata(raw.metadata)
@@ -2106,11 +2278,14 @@ function mapAgent2(name, decl, refs, version, projectName) {
2106
2278
  body.metadata = decl.metadata;
2107
2279
  }
2108
2280
  if (decl.tools) {
2109
- const enabledTools = decl.tools.builtin.map((t) => normalizeToolNameForQoder(t));
2110
2281
  body.tools = [
2111
2282
  {
2112
2283
  type: "agent_toolset_20260401",
2113
- enabled_tools: enabledTools
2284
+ configs: resolveBuiltinTools(decl.tools, { toWireName: normalizeToolNameForQoder }).map((tool) => ({
2285
+ name: tool.wireName,
2286
+ enabled: true,
2287
+ permission_policy: toPermissionPolicy(tool.permission)
2288
+ }))
2114
2289
  }
2115
2290
  ];
2116
2291
  } else {
@@ -2168,19 +2343,14 @@ function mapForwardTemplate(name, decl, refs, projectName) {
2168
2343
  if (projectName) body.metadata = injectMetadata(decl.metadata, projectName, name);
2169
2344
  else body.metadata = decl.metadata ?? {};
2170
2345
  if (decl.tools) {
2171
- const permissions = decl.tools.permissions ?? {};
2172
2346
  body.tools = [
2173
2347
  {
2174
2348
  type: "agent_toolset_20260401",
2175
- configs: decl.tools.builtin.map((tool) => {
2176
- const normalized = normalizeToolNameForQoder(tool);
2177
- const policy = permissions[tool] ?? permissions[tool.toLowerCase()] ?? permissions[normalized];
2178
- return {
2179
- name: normalized,
2180
- enabled: true,
2181
- ...policy ? { permission_policy: { type: policy === "ask" ? "always_ask" : "always_allow" } } : {}
2182
- };
2183
- })
2349
+ configs: resolveBuiltinTools(decl.tools, { toWireName: normalizeToolNameForQoder }).map((tool) => ({
2350
+ name: tool.wireName,
2351
+ enabled: true,
2352
+ permission_policy: toPermissionPolicy(tool.permission)
2353
+ }))
2184
2354
  }
2185
2355
  ];
2186
2356
  } else {
@@ -2236,6 +2406,7 @@ function toSessionEvent3(raw) {
2236
2406
  const rawType = raw.type ?? "";
2237
2407
  const type = QODER_EVENT_MAP[rawType] ?? "unknown";
2238
2408
  const event = { type, raw_type: rawType, raw };
2409
+ if (typeof raw.id === "string") event.id = raw.id;
2239
2410
  if (typeof raw.role === "string") event.role = raw.role;
2240
2411
  if (type === "message") {
2241
2412
  event.role = roleFromType2(rawType, raw.role);
@@ -2246,12 +2417,15 @@ function toSessionEvent3(raw) {
2246
2417
  } else if (type === "tool_result") {
2247
2418
  event.content = extractContentText2(raw);
2248
2419
  } else if (type === "status") {
2420
+ const stopReason = extractStopReason2(raw.stop_reason);
2249
2421
  if (rawType === "session.thread_status_idle") {
2250
2422
  event.status = "running";
2423
+ } else if (rawType === "session.status_idle" && stopReason === "requires_action") {
2424
+ event.status = "running";
2251
2425
  } else {
2252
2426
  event.status = rawType.includes("idle") ? "idle" : rawType.includes("terminated") ? "terminated" : "running";
2253
2427
  }
2254
- event.stop_reason = extractStopReason2(raw.stop_reason);
2428
+ event.stop_reason = stopReason;
2255
2429
  } else if (type === "error") {
2256
2430
  event.content = extractErrorMessage2(raw);
2257
2431
  }
@@ -2313,6 +2487,13 @@ function mapSession2(bindings) {
2313
2487
  for (const id of bindings.memory_store_ids) resources.push({ type: "memory_store", memory_store_id: id });
2314
2488
  for (const f of bindings.files ?? [])
2315
2489
  resources.push({ type: "file", file_id: f.file_id, mount_path: resolveSandboxMountPath("qoder", f.mount_path) });
2490
+ for (const resource of bindings.resources ?? []) {
2491
+ resources.push(
2492
+ mapGithubRepositorySessionResource(resource, {
2493
+ mapMountPath: (item) => resolveGithubRepositoryMountPath("qoder", item)
2494
+ })
2495
+ );
2496
+ }
2316
2497
  if (resources.length) body.resources = resources;
2317
2498
  return body;
2318
2499
  }
@@ -2893,7 +3074,7 @@ var QoderAdapter = class _QoderAdapter {
2893
3074
  const fullPath = resolve2(dirname2(basePath), source);
2894
3075
  const content = readFileSync2(fullPath);
2895
3076
  const formData = new FormData();
2896
- formData.append("file", new File([new Uint8Array(content)], basename3(fullPath)));
3077
+ formData.append("file", new File([new Uint8Array(content)], basename2(fullPath)));
2897
3078
  formData.append("purpose", "session_resource");
2898
3079
  const res = await this.client.postFormData("/files", formData);
2899
3080
  return res.file_id ?? res.id;
@@ -3066,7 +3247,7 @@ var QoderAdapter = class _QoderAdapter {
3066
3247
  async uploadFile(filePath, options) {
3067
3248
  const resolved = resolve2(filePath);
3068
3249
  const content = readFileSync2(resolved);
3069
- const fileName = options?.name ?? basename3(resolved);
3250
+ const fileName = options?.name ?? basename2(resolved);
3070
3251
  return this.uploadFileContent(new Uint8Array(content), fileName, {
3071
3252
  purpose: options?.purpose
3072
3253
  });
@@ -3191,6 +3372,7 @@ registerProvider({
3191
3372
  name: "qoder",
3192
3373
  configSchema: qoderConfigSchema,
3193
3374
  capabilities: QODER_CAPABILITIES,
3375
+ features: { tool_permissions: true, session_resources: ["github_repository"] },
3194
3376
  createAdapter: (config, projectName) => {
3195
3377
  const c = config;
3196
3378
  return new QoderAdapter(c.api_key, c.gateway, projectName, c.forward_gateway);
@@ -3199,7 +3381,7 @@ registerProvider({
3199
3381
 
3200
3382
  // src/internal/providers/bailian/adapter.ts
3201
3383
  import { readFileSync as readFileSync3 } from "fs";
3202
- import { basename as basename4, dirname as dirname3, extname, resolve as resolve3 } from "path";
3384
+ import { basename as basename3, dirname as dirname3, extname, resolve as resolve3 } from "path";
3203
3385
  import JSZip3 from "jszip";
3204
3386
 
3205
3387
  // src/internal/providers/bailian/client.ts
@@ -3211,7 +3393,13 @@ var BailianClient = class extends BaseApiClient {
3211
3393
  constructor(config) {
3212
3394
  super();
3213
3395
  this.apiKey = config.apiKey;
3214
- this.baseUrl = config.baseUrl ?? `https://${config.workspaceId}.cn-beijing.maas.aliyuncs.com/api/v1/agentstudio`;
3396
+ if (config.baseUrl) {
3397
+ this.baseUrl = config.baseUrl;
3398
+ } else if (config.workspaceId) {
3399
+ this.baseUrl = `https://${config.workspaceId}.cn-beijing.maas.aliyuncs.com/api/v1/agentstudio`;
3400
+ } else {
3401
+ throw new UserError("bailian provider requires either base_url or workspace_id");
3402
+ }
3215
3403
  }
3216
3404
  headers() {
3217
3405
  return {
@@ -3393,8 +3581,8 @@ function mapAgent3(name, decl, refs, version, projectName, skillVersions) {
3393
3581
  }
3394
3582
  const BAILIAN_BUILTINS = /* @__PURE__ */ new Set(["bash", "read", "write", "edit", "glob", "grep", "download_file"]);
3395
3583
  if (decl.tools) {
3396
- const toolConfigs = decl.tools.builtin.filter((t) => BAILIAN_BUILTINS.has(t)).map((toolName) => ({
3397
- name: toolName,
3584
+ const toolConfigs = resolveBuiltinTools(decl.tools, { supportedWireNames: BAILIAN_BUILTINS }).map((tool) => ({
3585
+ name: tool.wireName,
3398
3586
  enabled: true
3399
3587
  }));
3400
3588
  body.tools = [
@@ -3456,7 +3644,13 @@ function mapSession3(bindings) {
3456
3644
  if (bindings.metadata) body.metadata = bindings.metadata;
3457
3645
  if (bindings.vault_ids.length) body.vault_ids = bindings.vault_ids;
3458
3646
  const resources = [];
3459
- for (const f of bindings.files ?? []) resources.push({ type: "file", file_id: f.file_id, mount_path: f.mount_path });
3647
+ for (const f of bindings.files ?? []) {
3648
+ resources.push({
3649
+ type: "file",
3650
+ file_id: f.file_id,
3651
+ mount_path: resolveSandboxMountPath("bailian", f.mount_path)
3652
+ });
3653
+ }
3460
3654
  if (resources.length) body.resources = resources;
3461
3655
  if (bindings.memory_store_ids.length) body.memory_store_ids = bindings.memory_store_ids;
3462
3656
  return body;
@@ -3904,7 +4098,7 @@ var BailianAdapter = class _BailianAdapter {
3904
4098
  const fullPath = resolve3(dirname3(basePath), source);
3905
4099
  const content = readFileSync3(fullPath);
3906
4100
  const formData = new FormData();
3907
- formData.append("file", new File([new Uint8Array(content)], basename4(fullPath)));
4101
+ formData.append("file", new File([new Uint8Array(content)], basename3(fullPath)));
3908
4102
  const res = await this.client.postFormData("/files", formData);
3909
4103
  const fileId = res.id;
3910
4104
  await this.waitForFileAvailable(fileId);
@@ -3969,7 +4163,7 @@ var BailianAdapter = class _BailianAdapter {
3969
4163
  async uploadFile(filePath, options) {
3970
4164
  const resolved = resolve3(filePath);
3971
4165
  const content = readFileSync3(resolved);
3972
- const fileName = options?.name ?? basename4(resolved);
4166
+ const fileName = options?.name ?? basename3(resolved);
3973
4167
  return this.uploadFileContent(new Uint8Array(content), fileName, {
3974
4168
  purpose: options?.purpose
3975
4169
  });
@@ -4105,8 +4299,10 @@ var BAILIAN_CAPABILITIES = {
4105
4299
  import { z as z3 } from "zod";
4106
4300
  var bailianConfigSchema = z3.object({
4107
4301
  api_key: z3.string(),
4108
- workspace_id: z3.string(),
4302
+ workspace_id: z3.string().optional(),
4109
4303
  base_url: z3.string().optional()
4304
+ }).refine((config) => Boolean(config.workspace_id || config.base_url), {
4305
+ message: "either workspace_id or base_url is required"
4110
4306
  });
4111
4307
 
4112
4308
  // src/internal/providers/bailian/index.ts
@@ -4114,6 +4310,7 @@ registerProvider({
4114
4310
  name: "bailian",
4115
4311
  configSchema: bailianConfigSchema,
4116
4312
  capabilities: BAILIAN_CAPABILITIES,
4313
+ features: { tool_permissions: false, session_resources: [] },
4117
4314
  createAdapter: (config, projectName) => {
4118
4315
  const c = config;
4119
4316
  return new BailianAdapter(c.api_key, c.workspace_id, c.base_url, projectName);
@@ -4122,7 +4319,7 @@ registerProvider({
4122
4319
 
4123
4320
  // src/internal/providers/ark/adapter.ts
4124
4321
  import { readFileSync as readFileSync4 } from "fs";
4125
- import { basename as basename5, dirname as dirname4, resolve as resolve4 } from "path";
4322
+ import { basename as basename4, dirname as dirname4, resolve as resolve4 } from "path";
4126
4323
  import JSZip4 from "jszip";
4127
4324
 
4128
4325
  // src/internal/providers/resource-naming.ts
@@ -4247,6 +4444,7 @@ function agentToDecl4(raw) {
4247
4444
  const skills = raw.skills;
4248
4445
  const multiagent = raw.multiagent;
4249
4446
  let builtinTools;
4447
+ let builtinPermissions;
4250
4448
  let allToolsEnabled = false;
4251
4449
  if (tools?.length) {
4252
4450
  const toolset = tools.find((t) => t.type === "agent_toolset_20260701");
@@ -4255,6 +4453,7 @@ function agentToDecl4(raw) {
4255
4453
  const configs = toolset.configs ?? [];
4256
4454
  if (configs.length > 0) {
4257
4455
  builtinTools = configs.filter((c) => c.enabled !== false).map((c) => c.name);
4456
+ builtinPermissions = permissionOverridesFromWire(configs);
4258
4457
  } else if (defaultConfig?.enabled) {
4259
4458
  allToolsEnabled = true;
4260
4459
  }
@@ -4285,7 +4484,7 @@ function agentToDecl4(raw) {
4285
4484
  }
4286
4485
  let toolsDecl;
4287
4486
  if (builtinTools?.length) {
4288
- toolsDecl = { builtin: builtinTools };
4487
+ toolsDecl = { builtin: builtinTools, permissions: builtinPermissions };
4289
4488
  } else if (allToolsEnabled) {
4290
4489
  toolsDecl = {
4291
4490
  builtin: ["read", "write", "edit", "bash", "glob", "grep", "web_search", "web_fetch"]
@@ -4346,16 +4545,11 @@ function mapAgent4(name, decl, refs, version, projectName) {
4346
4545
  body.metadata = decl.metadata;
4347
4546
  }
4348
4547
  if (decl.tools) {
4349
- const toolConfigs = decl.tools.builtin.filter((toolName) => ARK_BUILTINS.has(toolName)).map((toolName) => {
4350
- const permission = decl.tools?.permissions?.[toolName] ?? "allow";
4351
- return {
4352
- name: toolName,
4353
- enabled: true,
4354
- permission_policy: {
4355
- type: permission === "ask" ? "always_ask" : "always_allow"
4356
- }
4357
- };
4358
- });
4548
+ const toolConfigs = resolveBuiltinTools(decl.tools, { supportedWireNames: ARK_BUILTINS }).map((tool) => ({
4549
+ name: tool.wireName,
4550
+ enabled: true,
4551
+ permission_policy: toPermissionPolicy(tool.permission)
4552
+ }));
4359
4553
  body.tools = [
4360
4554
  {
4361
4555
  type: "agent_toolset_20260701",
@@ -4510,7 +4704,7 @@ function mapSession4(bindings) {
4510
4704
  resources.push({
4511
4705
  type: "file",
4512
4706
  file_id: f.file_id,
4513
- mount_path: f.mount_path
4707
+ mount_path: resolveSandboxMountPath("ark", f.mount_path)
4514
4708
  });
4515
4709
  if (resources.length) body.resources = resources;
4516
4710
  return body;
@@ -4784,7 +4978,7 @@ var ArkAdapter = class _ArkAdapter {
4784
4978
  const fullPath = resolve4(dirname4(basePath), source);
4785
4979
  const content = readFileSync4(fullPath);
4786
4980
  const formData = new FormData();
4787
- formData.append("file", new File([new Uint8Array(content)], basename5(fullPath)));
4981
+ formData.append("file", new File([new Uint8Array(content)], basename4(fullPath)));
4788
4982
  formData.append("purpose", "agent");
4789
4983
  const res = await this.client.postFormData("/files", formData);
4790
4984
  return res.file_id ?? res.id;
@@ -4833,7 +5027,7 @@ var ArkAdapter = class _ArkAdapter {
4833
5027
  async uploadFile(filePath, options) {
4834
5028
  const resolved = resolve4(filePath);
4835
5029
  const content = readFileSync4(resolved);
4836
- const fileName = options?.name ?? basename5(resolved);
5030
+ const fileName = options?.name ?? basename4(resolved);
4837
5031
  return this.uploadFileContent(new Uint8Array(content), fileName, {
4838
5032
  purpose: options?.purpose
4839
5033
  });
@@ -4914,6 +5108,7 @@ registerProvider({
4914
5108
  name: "ark",
4915
5109
  configSchema: arkConfigSchema,
4916
5110
  capabilities: ARK_CAPABILITIES,
5111
+ features: { tool_permissions: true, session_resources: [] },
4917
5112
  createAdapter: (config, projectName) => {
4918
5113
  const c = config;
4919
5114
  return new ArkAdapter(c.api_key, projectName);
@@ -5026,7 +5221,7 @@ async function resolveFileReferences(config, configPath) {
5026
5221
  }
5027
5222
 
5028
5223
  // src/internal/parser/resolve-project-config.ts
5029
- import { basename as basename6, dirname as dirname7, resolve as resolve7 } from "path";
5224
+ import { basename as basename5, dirname as dirname7, resolve as resolve7 } from "path";
5030
5225
 
5031
5226
  // src/internal/parser/schema.ts
5032
5227
  import { z as z5 } from "zod";
@@ -5171,8 +5366,32 @@ var mcpToolkitSchema = z5.object({
5171
5366
  }));
5172
5367
  var toolsSchema = z5.object({
5173
5368
  builtin: z5.array(z5.string()),
5369
+ default_permission: z5.enum(["allow", "ask"]).optional(),
5174
5370
  mcp: z5.array(mcpToolkitSchema).optional(),
5175
5371
  permissions: z5.record(z5.string(), z5.enum(["allow", "ask"])).optional()
5372
+ }).superRefine((tools, ctx) => {
5373
+ const enabled = new Set(tools.builtin.map(canonicalToolName));
5374
+ const seen = /* @__PURE__ */ new Map();
5375
+ for (const key of Object.keys(tools.permissions ?? {})) {
5376
+ const canonical = canonicalToolName(key);
5377
+ const previous = seen.get(canonical);
5378
+ if (previous) {
5379
+ ctx.addIssue({
5380
+ code: "custom",
5381
+ path: ["permissions", key],
5382
+ message: `duplicates permission key '${previous}' after tool-name normalization`
5383
+ });
5384
+ } else {
5385
+ seen.set(canonical, key);
5386
+ }
5387
+ if (!enabled.has(canonical)) {
5388
+ ctx.addIssue({
5389
+ code: "custom",
5390
+ path: ["permissions", key],
5391
+ message: `references tool '${key}' which is not enabled in tools.builtin`
5392
+ });
5393
+ }
5394
+ }
5176
5395
  });
5177
5396
  var multiagentSchema = z5.object({
5178
5397
  type: z5.literal("coordinator"),
@@ -5193,6 +5412,15 @@ var agentSkillRefSchema = z5.object({
5193
5412
  var agentDeliverySchema = z5.object({
5194
5413
  type: z5.enum(["managed", "forward"])
5195
5414
  });
5415
+ var sessionGithubRepoResourceSchema = z5.object({
5416
+ type: z5.literal("github_repository"),
5417
+ url: z5.string().url(),
5418
+ checkout: z5.object({ branch: z5.string().min(1).optional(), commit: z5.string().min(1).optional() }).refine((value) => !(value.branch && value.commit), {
5419
+ message: "checkout accepts either branch or commit, not both"
5420
+ }).optional(),
5421
+ mount_path: z5.string().optional(),
5422
+ authorization_token: z5.string().min(1)
5423
+ });
5196
5424
  var agentSchema = z5.object({
5197
5425
  name: z5.string().optional(),
5198
5426
  description: z5.string().optional(),
@@ -5206,6 +5434,7 @@ var agentSchema = z5.object({
5206
5434
  skills: z5.array(z5.union([z5.string(), agentSkillRefSchema])).optional(),
5207
5435
  vault: z5.string().optional(),
5208
5436
  memory_stores: z5.array(z5.string()).optional(),
5437
+ resources: z5.array(sessionGithubRepoResourceSchema).optional(),
5209
5438
  multiagent: multiagentSchema.optional(),
5210
5439
  metadata: z5.record(z5.string(), z5.string()).optional(),
5211
5440
  delivery: z5.record(z5.string(), agentDeliverySchema).optional()
@@ -5345,7 +5574,7 @@ async function loadConfig(filePath, resolveEnv = false) {
5345
5574
  // src/internal/parser/resolve-project-config.ts
5346
5575
  async function resolveProjectConfig(filePath, options = {}) {
5347
5576
  const configPath = resolve7(filePath);
5348
- const projectName = options.projectName ?? basename6(dirname7(configPath));
5577
+ const projectName = options.projectName ?? basename5(dirname7(configPath));
5349
5578
  const { config: parsed, errors } = await loadConfig(configPath, options.resolveEnv ?? true);
5350
5579
  if (errors.length > 0) {
5351
5580
  throw new UserError(errors.join("\n"));
@@ -5757,7 +5986,7 @@ import { readFileSync as readFileSync7, statSync as statSync3 } from "fs";
5757
5986
  import { dirname as dirname9, resolve as resolve10 } from "path";
5758
5987
  async function resolveSkillFiles(decl, ctx) {
5759
5988
  if (/^https?:\/\//i.test(decl.source)) {
5760
- const res = await fetch(decl.source);
5989
+ const res = await resolveFetch()(decl.source);
5761
5990
  if (!res.ok) throw new Error(`skill source \u4E0B\u8F7D\u5931\u8D25\uFF1A${res.status} ${decl.source}`);
5762
5991
  return extractSkillZipFiles(Buffer.from(await res.arrayBuffer()));
5763
5992
  }
@@ -6605,6 +6834,43 @@ function collectProviderCapabilities(config, providers, diagnostics) {
6605
6834
  for (const [name, agent] of Object.entries(config.agents ?? {})) {
6606
6835
  if (agent.provider && agent.provider !== providerName) continue;
6607
6836
  const delivery = agent.delivery?.[providerName]?.type ?? "managed";
6837
+ const address = {
6838
+ type: delivery === "forward" ? "template" : "agent",
6839
+ name,
6840
+ provider: providerName
6841
+ };
6842
+ const asksForApproval = agent.tools?.default_permission === "ask" || Object.values(agent.tools?.permissions ?? {}).some((permission) => permission === "ask");
6843
+ if (asksForApproval && !def.features.tool_permissions) {
6844
+ diagnostics.error(
6845
+ `${providerName}.agent.tool_permissions.unsupported`,
6846
+ `agent.${name}: provider '${providerName}' cannot enforce interactive tool permission 'ask'.`,
6847
+ address
6848
+ );
6849
+ }
6850
+ for (const resource of agent.resources ?? []) {
6851
+ if (!def.features.session_resources.includes(resource.type)) {
6852
+ diagnostics.error(
6853
+ `${providerName}.agent.session_resource.${resource.type}.unsupported`,
6854
+ `agent.${name}: provider '${providerName}' does not support Session resource type '${resource.type}'.`,
6855
+ address
6856
+ );
6857
+ }
6858
+ const mountPrefix = providerMountPrefix(providerName);
6859
+ if (mountPrefix && resource.mount_path && resource.mount_path !== mountPrefix && !resource.mount_path.startsWith(`${mountPrefix}/`)) {
6860
+ diagnostics.error(
6861
+ `${providerName}.agent.session_resource.mount_path.invalid`,
6862
+ `agent.${name}: ${providerName} Session resource mount_path must start with '${mountPrefix}/'.`,
6863
+ address
6864
+ );
6865
+ }
6866
+ }
6867
+ if (delivery === "forward" && agent.resources?.length) {
6868
+ diagnostics.error(
6869
+ `${providerName}.template.session_resources.unsupported`,
6870
+ `agent.${name}: Forward delivery cannot attach Agent Session resources; use managed delivery.`,
6871
+ address
6872
+ );
6873
+ }
6608
6874
  if (delivery === "forward" && !isSupported(caps, "template")) {
6609
6875
  diagnostics.error(
6610
6876
  `${providerName}.agent.delivery.forward.unsupported`,
@@ -8107,7 +8373,21 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
8107
8373
  const available = Object.keys(config.agents ?? {}).join(", ");
8108
8374
  throw new UserError(`Agent '${agentName}' not found in config. Available agents: ${available || "(none)"}`);
8109
8375
  }
8376
+ const sessionResources = options.resources ?? agent.resources;
8377
+ const providerFeatures = getProvider(provider)?.features;
8378
+ for (const resource of sessionResources ?? []) {
8379
+ if (!providerFeatures?.session_resources.includes(resource.type)) {
8380
+ throw new UserError(
8381
+ `Provider '${provider}' does not support Session resource type '${resource.type}' for agent '${agentName}'.`
8382
+ );
8383
+ }
8384
+ }
8110
8385
  if (resolveAgentMaterialization(provider, agent).resourceType === "template") {
8386
+ if (sessionResources?.length) {
8387
+ throw new UserError(
8388
+ `Forward session for '${agentName}' cannot attach Agent resources. Use managed delivery for GitHub repositories.`
8389
+ );
8390
+ }
8111
8391
  const templateId = requireRef(state, { type: "template", name: agentName, provider });
8112
8392
  const defaultIdentity = config.defaults?.identity;
8113
8393
  const identityId = options.identityId ?? (defaultIdentity ? requireRef(state, { type: "identity", name: defaultIdentity, provider }) : void 0);
@@ -8165,6 +8445,7 @@ function buildSessionBindings(agentName, config, provider, state, options = {})
8165
8445
  vault_ids: vaultIds,
8166
8446
  memory_store_ids: memoryStoreIds,
8167
8447
  files: (options.files ?? []).map((f) => ({ file_id: f.fileId, mount_path: f.mountPath })),
8448
+ resources: sessionResources,
8168
8449
  title: options.title,
8169
8450
  metadata: options.metadata
8170
8451
  };
@@ -8534,6 +8815,7 @@ function toAgentSkillRef(skill) {
8534
8815
  var TERMINAL_SESSION_STATUSES = /* @__PURE__ */ new Set(["idle", "completed", "failed", "terminated", "deleted"]);
8535
8816
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
8536
8817
  var DEFAULT_POLL_TIMEOUT_MS = 10 * 60 * 1e3;
8818
+ var POLL_INITIAL_INTERVAL_MS = 300;
8537
8819
  function resolveAgentName(agents, agentName) {
8538
8820
  if (agentName) return agentName;
8539
8821
  const names = Object.keys(agents ?? {});
@@ -8564,6 +8846,7 @@ async function createSessionForAgent(ctx, options = {}) {
8564
8846
  vaultIds: options.vaultIds,
8565
8847
  memoryStores: options.memoryStores,
8566
8848
  files: options.files,
8849
+ resources: options.resources,
8567
8850
  title: options.title,
8568
8851
  metadata: options.metadata
8569
8852
  });
@@ -8582,6 +8865,7 @@ async function startSessionRun(ctx, prompt, options = {}) {
8582
8865
  vaultIds: options.vaultIds,
8583
8866
  memoryStores: options.memoryStores,
8584
8867
  files: options.files,
8868
+ resources: options.resources,
8585
8869
  title: options.title,
8586
8870
  metadata: options.metadata
8587
8871
  });
@@ -8590,7 +8874,7 @@ async function startSessionRun(ctx, prompt, options = {}) {
8590
8874
  agentName,
8591
8875
  provider,
8592
8876
  session,
8593
- events: streamMessageEvents(adapter2, session.id, preparePromptForProvider(prompt, bindings.files, provider))
8877
+ events: streamMessageEvents(adapter2, session.id, prepareInitialSessionPrompt(prompt, bindings, provider))
8594
8878
  };
8595
8879
  }
8596
8880
  function streamMessageEvents(adapter2, sessionId, message) {
@@ -8604,15 +8888,12 @@ async function sendSessionMessageStreaming(ctx, sessionId, message, options = {}
8604
8888
  return streamMessageEvents(adapter2, sessionId, message);
8605
8889
  }
8606
8890
  async function startSessionRunPolling(ctx, prompt, options = {}) {
8607
- const run = await createSessionForAgent(ctx, options);
8608
- const adapter2 = getRuntimeProvider(ctx, run.provider);
8609
- const hintedPrompt = preparePromptForProvider(
8610
- prompt,
8611
- options.files?.map((f) => ({ mount_path: f.mountPath })),
8612
- run.provider
8613
- );
8614
- const collected = await sendSessionMessageAndCollectEvents(adapter2, run.session.id, hintedPrompt, options);
8615
- return { ...run, ...collected };
8891
+ const { agentName, provider, adapter: adapter2 } = resolveSessionRuntime(ctx, options);
8892
+ const bindings = buildSessionBindings(agentName, ctx.config, provider, ctx.state, options);
8893
+ const session = await adapter2.createSession(bindings);
8894
+ const initialPrompt = prepareInitialSessionPrompt(prompt, bindings, provider);
8895
+ const collected = await sendSessionMessageAndCollectEvents(adapter2, session.id, initialPrompt, options);
8896
+ return { agentName, provider, session, ...collected };
8616
8897
  }
8617
8898
  async function sendSessionMessageAndCollectEvents(adapter2, sessionId, message, options = {}) {
8618
8899
  const eventId = await adapter2.sendSessionMessage(sessionId, message);
@@ -8628,8 +8909,9 @@ async function sendSessionMessagePolling(ctx, sessionId, message, options = {})
8628
8909
  }
8629
8910
  async function collectEventsUntilTerminal(adapter2, sessionId, options = {}) {
8630
8911
  const start = Date.now();
8631
- const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
8912
+ const maxPollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
8632
8913
  const pollTimeoutMs = options.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
8914
+ let currentIntervalMs = Math.min(POLL_INITIAL_INTERVAL_MS, maxPollIntervalMs);
8633
8915
  let terminalStatus = "idle";
8634
8916
  let result;
8635
8917
  if (options.afterId) {
@@ -8646,7 +8928,8 @@ async function collectEventsUntilTerminal(adapter2, sessionId, options = {}) {
8646
8928
  terminalStatus = terminalEvent.status;
8647
8929
  break;
8648
8930
  }
8649
- await delay(pollIntervalMs);
8931
+ await delay(currentIntervalMs);
8932
+ currentIntervalMs = Math.min(currentIntervalMs * 2, maxPollIntervalMs);
8650
8933
  }
8651
8934
  } else {
8652
8935
  while (true) {
@@ -8656,7 +8939,8 @@ async function collectEventsUntilTerminal(adapter2, sessionId, options = {}) {
8656
8939
  terminalStatus = session.status;
8657
8940
  break;
8658
8941
  }
8659
- await delay(pollIntervalMs);
8942
+ await delay(currentIntervalMs);
8943
+ currentIntervalMs = Math.min(currentIntervalMs * 2, maxPollIntervalMs);
8660
8944
  }
8661
8945
  result = await adapter2.listSessionEvents(sessionId, { limit: 100 });
8662
8946
  }
@@ -8804,7 +9088,28 @@ function buildAgentNameByRemoteId(ctx, provider) {
8804
9088
  }
8805
9089
  async function* streamWithResume(adapter2, sessionId, message) {
8806
9090
  const eventId = await adapter2.sendSessionMessage(sessionId, message);
8807
- yield* adapter2.streamSessionEvents(sessionId, eventId ? { after_id: eventId } : void 0);
9091
+ let lastEventId = eventId;
9092
+ let reachedTerminal = false;
9093
+ let reconnectIntervalMs = POLL_INITIAL_INTERVAL_MS;
9094
+ const start = Date.now();
9095
+ while (!reachedTerminal) {
9096
+ assertNotTimedOut(start, DEFAULT_POLL_TIMEOUT_MS);
9097
+ for await (const event of adapter2.streamSessionEvents(
9098
+ sessionId,
9099
+ lastEventId ? { after_id: lastEventId } : void 0
9100
+ )) {
9101
+ if (event.id) lastEventId = event.id;
9102
+ yield event;
9103
+ if (event.type === "status" && isTerminalSessionStatus(event.status)) {
9104
+ reachedTerminal = true;
9105
+ break;
9106
+ }
9107
+ }
9108
+ if (!reachedTerminal) {
9109
+ await delay(reconnectIntervalMs);
9110
+ reconnectIntervalMs = Math.min(reconnectIntervalMs * 2, DEFAULT_POLL_INTERVAL_MS);
9111
+ }
9112
+ }
8808
9113
  }
8809
9114
  async function* streamConnectBeforeSend(adapter2, sessionId, message) {
8810
9115
  const iterator = adapter2.streamSessionEvents(sessionId)[Symbol.asyncIterator]();
@@ -8861,7 +9166,11 @@ function resolveActiveProvider() {
8861
9166
  }
8862
9167
  function areRuntimeCredentialsReady() {
8863
9168
  const provider = resolveActiveProvider();
8864
- return AGENTS_PROVIDER_FIELDS[provider].every((field) => process.env[field.key]?.trim());
9169
+ return AGENTS_PROVIDER_FIELDS[provider].every((field) => {
9170
+ if (process.env[field.key]?.trim()) return true;
9171
+ if (field.key === "BAILIAN_WORKSPACE_ID") return Boolean(process.env.BAILIAN_BASE_URL?.trim());
9172
+ return false;
9173
+ });
8865
9174
  }
8866
9175
  function isProvider(value) {
8867
9176
  return AGENTS_CONFIG_PROVIDERS.includes(value);
@@ -9458,9 +9767,11 @@ export {
9458
9767
  AgentSyncRunSchema,
9459
9768
  AgentSyncStatusSchema,
9460
9769
  AgentWithReadinessSchema,
9770
+ ApiError,
9461
9771
  CloudAgentSchema,
9462
9772
  CloudEnvironmentSchema,
9463
9773
  CloudVaultSchema,
9774
+ ConflictError,
9464
9775
  CreateSessionRequestSchema,
9465
9776
  CreateSessionResponseSchema,
9466
9777
  DiagnosticSchema,
@@ -9551,6 +9862,7 @@ export {
9551
9862
  pauseDeploymentForContext,
9552
9863
  planDestroyProjectContext,
9553
9864
  planProjectContext,
9865
+ prepareInitialSessionPrompt,
9554
9866
  preparePromptForProvider,
9555
9867
  prependFileHint,
9556
9868
  providerConfigPath,
@@ -9560,12 +9872,14 @@ export {
9560
9872
  resolveProjectConfig,
9561
9873
  resolveProjectConfigFromObject,
9562
9874
  resolveProviderConfigFromEnv,
9875
+ resolveRepositoryMountPath,
9563
9876
  resolveSessionProvider,
9564
9877
  resolveSyncProvider,
9565
9878
  rewriteFileMentions,
9566
9879
  runDeploymentForContext,
9567
9880
  sendSessionMessagePolling,
9568
9881
  sendSessionMessageStreaming,
9882
+ setDefaultFetch,
9569
9883
  startSessionRun,
9570
9884
  startSessionRunPolling,
9571
9885
  streamSessionEvents,