@openagentpack/sdk 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -816,7 +816,21 @@ var toolsSchema = z.object({
816
816
  });
817
817
  var multiagentSchema = z.object({
818
818
  type: z.literal("coordinator"),
819
- agents: z.array(z.string())
819
+ agents: z.array(z.union([z.string().min(1), z.object({ agent_id: z.string().trim().min(1) })])).min(1)
820
+ }).superRefine((multiagent, ctx) => {
821
+ const seen = /* @__PURE__ */ new Set();
822
+ for (const [index, member] of multiagent.agents.entries()) {
823
+ const key = typeof member === "string" ? `logical:${member}` : `external:${member.agent_id}`;
824
+ if (seen.has(key)) {
825
+ const label = typeof member === "string" ? member : member.agent_id;
826
+ ctx.addIssue({
827
+ code: "custom",
828
+ path: ["agents", index],
829
+ message: `duplicates member '${label}'`
830
+ });
831
+ }
832
+ seen.add(key);
833
+ }
820
834
  });
821
835
  var agentSkillRefSchema = z.object({
822
836
  type: z.enum(["official", "custom"]),
@@ -1344,6 +1358,86 @@ function extractCreatedEventId(res) {
1344
1358
  return void 0;
1345
1359
  }
1346
1360
 
1361
+ // src/internal/multiagent/reverse-index.ts
1362
+ function buildReverseIndex(agents, project) {
1363
+ const index = /* @__PURE__ */ new Map();
1364
+ for (const raw of agents) {
1365
+ const id = raw.id;
1366
+ if (typeof id !== "string") continue;
1367
+ const metadata = raw.metadata;
1368
+ const owned = metadata?.["agents.project"] === project;
1369
+ const resource = metadata?.["agents.resource"];
1370
+ const logicalName = owned && typeof resource === "string" && resource.trim() ? resource.trim() : void 0;
1371
+ index.set(id, {
1372
+ logical_name: logicalName,
1373
+ archived: raw.archived_at != null,
1374
+ owned
1375
+ });
1376
+ }
1377
+ return index;
1378
+ }
1379
+ function memberDeclResolver(index) {
1380
+ const claims = /* @__PURE__ */ new Map();
1381
+ for (const entry of index.values()) {
1382
+ if (!entry.logical_name) continue;
1383
+ claims.set(entry.logical_name, (claims.get(entry.logical_name) ?? 0) + 1);
1384
+ }
1385
+ return (memberId) => {
1386
+ const entry = index.get(memberId);
1387
+ if (!entry) {
1388
+ throw new UserError(
1389
+ `sync.multiagent.member.unresolved: roster member '${memberId}' is missing from the /agents listing.`
1390
+ );
1391
+ }
1392
+ if (!entry.owned) return { agent_id: memberId };
1393
+ if (entry.archived) {
1394
+ throw new UserError(
1395
+ `sync.multiagent.member.archived: roster member '${memberId}' is archived; restore it before syncing.`
1396
+ );
1397
+ }
1398
+ const name = entry.logical_name;
1399
+ if (!name) {
1400
+ throw new UserError(
1401
+ `sync.multiagent.member.unresolved: roster member '${memberId}' carries no agents.resource metadata.`
1402
+ );
1403
+ }
1404
+ if ((claims.get(name) ?? 0) > 1) {
1405
+ throw new UserError(
1406
+ `sync.multiagent.member.ambiguous: logical name '${name}' is claimed by multiple remote agents.`
1407
+ );
1408
+ }
1409
+ return name;
1410
+ };
1411
+ }
1412
+ function reverseMultiagentDecl(raw, resolveMember) {
1413
+ if (raw === null || raw === void 0) return void 0;
1414
+ if (typeof raw !== "object") return void 0;
1415
+ const roster = raw;
1416
+ if (roster.type !== void 0 && roster.type !== "coordinator") {
1417
+ throw new UserError(
1418
+ `sync.multiagent.member.unresolved: remote multi-agent type '${String(roster.type)}' cannot be represented (coordinator only).`
1419
+ );
1420
+ }
1421
+ if (!Array.isArray(roster.agents) || roster.agents.length === 0) return void 0;
1422
+ if (!resolveMember) {
1423
+ throw new UserError(
1424
+ "sync.multiagent.member.unresolved: reverse-mapping a multi-agent roster requires a member name resolver."
1425
+ );
1426
+ }
1427
+ return {
1428
+ type: "coordinator",
1429
+ agents: roster.agents.map((member) => resolveMember(rosterMemberId(member)))
1430
+ };
1431
+ }
1432
+ function rosterMemberId(member) {
1433
+ if (typeof member === "string") return member;
1434
+ if (typeof member === "object" && member !== null) {
1435
+ const m = member;
1436
+ if (m.type === "agent" && typeof m.id === "string") return m.id;
1437
+ }
1438
+ throw new UserError("sync.multiagent.member.unresolved: roster member is not a representable agent reference.");
1439
+ }
1440
+
1347
1441
  // src/internal/providers/session-vault.ts
1348
1442
  function extractVaultIds(res) {
1349
1443
  const top = res.vault_ids;
@@ -1419,7 +1513,7 @@ function buildSessionInfo(res, memoryStoreIds) {
1419
1513
  attributes: res
1420
1514
  };
1421
1515
  }
1422
- async function exportRemoteResources(client, type, mappers) {
1516
+ async function exportRemoteResources(client, type, mappers, projectName) {
1423
1517
  if (type === "environment") {
1424
1518
  const envs = await client.getAllPaged("/environments");
1425
1519
  return envs.map((env) => {
@@ -1458,9 +1552,11 @@ async function exportRemoteResources(client, type, mappers) {
1458
1552
  }
1459
1553
  if (type === "agent") {
1460
1554
  const agents = await client.getAllPaged("/agents");
1555
+ const resolveMember = memberDeclResolver(buildReverseIndex(agents, projectName ?? ""));
1461
1556
  return agents.filter((agent) => !agent.archived_at).map((agent) => {
1462
1557
  const agentId = agent.id;
1463
- return { name: agentId, decl: mappers.agentToDecl(agent) };
1558
+ const name = resourceNameFromMetadata(agent.metadata, agent.name ?? agentId, agentId);
1559
+ return { name, decl: mappers.agentToDecl(agent, resolveMember) };
1464
1560
  });
1465
1561
  }
1466
1562
  return [];
@@ -1684,11 +1780,10 @@ function envToDecl(raw) {
1684
1780
  metadata: stripAgentsMetadata(raw.metadata)
1685
1781
  });
1686
1782
  }
1687
- function agentToDecl(raw) {
1783
+ function agentToDecl(raw, resolveMember) {
1688
1784
  const tools = raw.tools;
1689
1785
  const mcpServers = raw.mcp_servers;
1690
1786
  const skills = raw.skills;
1691
- const multiagent = raw.multiagent;
1692
1787
  let builtinTools;
1693
1788
  let builtinPermissions;
1694
1789
  let allToolsEnabled = false;
@@ -1720,10 +1815,6 @@ function agentToDecl(raw) {
1720
1815
  skill_id: s.skill_id
1721
1816
  }));
1722
1817
  }
1723
- let multiagentDecl;
1724
- if (multiagent && multiagent.agents?.length) {
1725
- multiagentDecl = { type: "coordinator", agents: multiagent.agents };
1726
- }
1727
1818
  let toolsDecl;
1728
1819
  if (builtinTools?.length) {
1729
1820
  toolsDecl = { builtin: builtinTools, permissions: builtinPermissions };
@@ -1740,7 +1831,7 @@ function agentToDecl(raw) {
1740
1831
  tools: toolsDecl,
1741
1832
  mcp_servers: mcpServerDecls,
1742
1833
  skills: skillDecls,
1743
- multiagent: multiagentDecl,
1834
+ multiagent: reverseMultiagentDecl(raw.multiagent, resolveMember),
1744
1835
  metadata: stripAgentsMetadata(raw.metadata)
1745
1836
  });
1746
1837
  }
@@ -1823,10 +1914,10 @@ function mapAgent(name, decl, refs, version, projectName) {
1823
1914
  skill_id: s.skill_id
1824
1915
  }));
1825
1916
  }
1826
- if (decl.multiagent && refs.multiagent_agent_ids?.length) {
1917
+ if (decl.multiagent && refs.multiagent) {
1827
1918
  body.multiagent = {
1828
1919
  type: "coordinator",
1829
- agents: refs.multiagent_agent_ids
1920
+ agents: refs.multiagent.members.map((member) => member.remote_id)
1830
1921
  };
1831
1922
  }
1832
1923
  return body;
@@ -2164,13 +2255,18 @@ var ClaudeAdapter = class _ClaudeAdapter {
2164
2255
  await this.client.delete(`/vaults/${id}`);
2165
2256
  }
2166
2257
  async exportResources(type) {
2167
- return exportRemoteResources(this.client, type, {
2168
- envToDecl,
2169
- vaultToDecl,
2170
- fileToDecl,
2171
- skillToDecl,
2172
- agentToDecl
2173
- });
2258
+ return exportRemoteResources(
2259
+ this.client,
2260
+ type,
2261
+ {
2262
+ envToDecl,
2263
+ vaultToDecl,
2264
+ fileToDecl,
2265
+ skillToDecl,
2266
+ agentToDecl
2267
+ },
2268
+ this.projectName
2269
+ );
2174
2270
  }
2175
2271
  async createSkill(name, _decl, files) {
2176
2272
  const formData = buildClaudeSkillFormData(name, files);
@@ -2539,6 +2635,31 @@ import { readFileSync as readFileSync2 } from "fs";
2539
2635
  import { basename as basename3, dirname as dirname5, resolve as resolve5 } from "path";
2540
2636
  import JSZip2 from "jszip";
2541
2637
 
2638
+ // src/internal/multiagent/comparable.ts
2639
+ function canonicalizeRemoteRoster(raw, materialization) {
2640
+ if (raw === null || raw === void 0) return { supported: true };
2641
+ if (typeof raw !== "object") return { supported: false };
2642
+ const roster = raw;
2643
+ if (roster.type !== "coordinator") return { supported: false };
2644
+ if (!Array.isArray(roster.agents)) return { supported: false };
2645
+ if (roster.agents.length === 0) return { supported: true };
2646
+ const memberIds = [];
2647
+ for (const entry of roster.agents) {
2648
+ if (typeof entry !== "object" || entry === null) return { supported: false };
2649
+ const member = entry;
2650
+ if (member.type !== "agent") return { supported: false };
2651
+ const id = materialization === "forward" ? member.template_id : member.id;
2652
+ if (typeof id !== "string") return { supported: false };
2653
+ memberIds.push(id);
2654
+ }
2655
+ return { supported: true, roster: { type: "coordinator", member_ids: [...new Set(memberIds)].sort() } };
2656
+ }
2657
+ function comparableMultiagentField(raw, materialization) {
2658
+ const canonical = canonicalizeRemoteRoster(raw, materialization);
2659
+ if (!canonical.supported) return { type: "unsupported" };
2660
+ return canonical.roster;
2661
+ }
2662
+
2542
2663
  // src/internal/providers/qoder/client.ts
2543
2664
  var QoderClient = class extends BaseApiClient {
2544
2665
  baseUrl;
@@ -2688,7 +2809,7 @@ function skillToDecl2(raw, name) {
2688
2809
  description: raw.description
2689
2810
  });
2690
2811
  }
2691
- function agentToDecl2(raw) {
2812
+ function agentToDecl2(raw, resolveMember) {
2692
2813
  const tools = raw.tools;
2693
2814
  const mcpServers = raw.mcp_servers;
2694
2815
  const skills = raw.skills;
@@ -2727,6 +2848,7 @@ function agentToDecl2(raw) {
2727
2848
  tools: builtinTools?.length ? { builtin: builtinTools, permissions: builtinPermissions } : void 0,
2728
2849
  mcp_servers: mcpServerDecls,
2729
2850
  skills: skillDecls,
2851
+ multiagent: reverseMultiagentDecl(raw.multiagent, resolveMember),
2730
2852
  metadata: stripAgentsMetadata(raw.metadata)
2731
2853
  });
2732
2854
  }
@@ -2835,7 +2957,7 @@ function mapDeploymentResources2(decl, refs, uploadedFiles) {
2835
2957
  }
2836
2958
  return resources;
2837
2959
  }
2838
- function mapAgent2(name, decl, refs, version, projectName) {
2960
+ function mapAgent2(name, decl, refs, version, projectName, operation = "create") {
2839
2961
  let model;
2840
2962
  if (typeof decl.model === "string") {
2841
2963
  model = decl.model;
@@ -2894,15 +3016,21 @@ function mapAgent2(name, decl, refs, version, projectName) {
2894
3016
  }
2895
3017
  }
2896
3018
  }
2897
- if (refs.skill_ids.length) {
2898
- body.skills = refs.skill_ids.map((s) => ({
2899
- type: s.type === "official" ? "qoder" : s.type,
2900
- skill_id: s.skill_id
2901
- }));
3019
+ body.skills = refs.skill_ids.map((s) => ({
3020
+ type: s.type === "official" ? "qoder" : s.type,
3021
+ skill_id: s.skill_id
3022
+ }));
3023
+ if (refs.multiagent) {
3024
+ body.multiagent = {
3025
+ type: "coordinator",
3026
+ agents: refs.multiagent.members.map(({ remote_id }) => ({ type: "agent", id: remote_id }))
3027
+ };
3028
+ } else if (operation === "update") {
3029
+ body.multiagent = null;
2902
3030
  }
2903
3031
  return body;
2904
3032
  }
2905
- function mapForwardTemplate(name, decl, refs, projectName) {
3033
+ function mapForwardTemplate(name, decl, refs, projectName, operation = "create") {
2906
3034
  let model;
2907
3035
  if (typeof decl.model === "string") {
2908
3036
  model = decl.model;
@@ -2964,6 +3092,14 @@ function mapForwardTemplate(name, decl, refs, projectName) {
2964
3092
  ...skill.version ? { version: skill.version } : {},
2965
3093
  enabled: true
2966
3094
  }));
3095
+ if (refs.multiagent) {
3096
+ body.multiagent = {
3097
+ type: "coordinator",
3098
+ agents: refs.multiagent.members.map(({ remote_id }) => ({ type: "agent", template_id: remote_id }))
3099
+ };
3100
+ } else if (operation === "update") {
3101
+ body.multiagent = null;
3102
+ }
2967
3103
  return body;
2968
3104
  }
2969
3105
  function mapSendMessage2(text) {
@@ -3309,7 +3445,7 @@ var QoderAdapter = class _QoderAdapter {
3309
3445
  if (!metadata || typeof metadata !== "object") return false;
3310
3446
  return metadata["agents.project"] === this.projectName && metadata["agents.resource"] === displayName;
3311
3447
  }
3312
- normalizeDesiredResource(type, name, decl) {
3448
+ normalizeDesiredResource(type, name, decl, refs) {
3313
3449
  if (type === "environment") {
3314
3450
  return this.normalizeRemote(
3315
3451
  type,
@@ -3319,10 +3455,16 @@ var QoderAdapter = class _QoderAdapter {
3319
3455
  if (type === "agent") {
3320
3456
  return this.normalizeRemote(
3321
3457
  type,
3322
- mapAgent2(name, decl, { skill_ids: [] }, void 0, this.projectName)
3458
+ mapAgent2(name, decl, refs ?? { skill_ids: [] }, void 0, this.projectName)
3459
+ );
3460
+ }
3461
+ if (type === "template") {
3462
+ if (!refs) return null;
3463
+ return this.normalizeRemote(
3464
+ type,
3465
+ mapForwardTemplate(name, decl, refs, this.projectName)
3323
3466
  );
3324
3467
  }
3325
- if (type === "template") return null;
3326
3468
  if (type === "identity") {
3327
3469
  const identity = decl;
3328
3470
  if (identity.identity_id) return null;
@@ -3350,10 +3492,10 @@ var QoderAdapter = class _QoderAdapter {
3350
3492
  description: raw.description,
3351
3493
  model: raw.model,
3352
3494
  system: raw.system,
3353
- tools: raw.tools,
3354
- mcp_servers: raw.mcp_servers,
3495
+ tools: normalizeQoderTools(raw.tools),
3496
+ mcp_servers: normalizeQoderMcpServers(raw.mcp_servers),
3355
3497
  skills: raw.skills,
3356
- multiagent: raw.multiagent,
3498
+ multiagent: comparableMultiagentField(raw.multiagent, "forward"),
3357
3499
  environment_id: raw.environment_id,
3358
3500
  tunnel_id: raw.tunnel_id,
3359
3501
  vault_ids: Array.isArray(raw.vault_ids) ? raw.vault_ids : Object.keys(raw.vaults ?? {}),
@@ -3395,6 +3537,7 @@ var QoderAdapter = class _QoderAdapter {
3395
3537
  instructions: raw.system,
3396
3538
  tools: normalizeQoderTools(raw.tools),
3397
3539
  mcp_servers: normalizeQoderMcpServers(raw.mcp_servers),
3540
+ multiagent: comparableMultiagentField(raw.multiagent, "managed"),
3398
3541
  metadata: stripAgentsMetadata(raw.metadata)
3399
3542
  });
3400
3543
  }
@@ -3473,13 +3616,18 @@ var QoderAdapter = class _QoderAdapter {
3473
3616
  await (mode === "forward" ? this.forwardClient : this.client).delete(`/vaults/${id}`);
3474
3617
  }
3475
3618
  async exportResources(type) {
3476
- return exportRemoteResources(this.client, type, {
3477
- envToDecl: envToDecl2,
3478
- vaultToDecl: vaultToDecl2,
3479
- fileToDecl: fileToDecl2,
3480
- skillToDecl: skillToDecl2,
3481
- agentToDecl: agentToDecl2
3482
- });
3619
+ return exportRemoteResources(
3620
+ this.client,
3621
+ type,
3622
+ {
3623
+ envToDecl: envToDecl2,
3624
+ vaultToDecl: vaultToDecl2,
3625
+ fileToDecl: fileToDecl2,
3626
+ skillToDecl: skillToDecl2,
3627
+ agentToDecl: agentToDecl2
3628
+ },
3629
+ this.projectName
3630
+ );
3483
3631
  }
3484
3632
  async createSkill(name, decl, files, mode = "managed") {
3485
3633
  const formData = await buildSkillFormData(name, decl, files);
@@ -3501,13 +3649,13 @@ var QoderAdapter = class _QoderAdapter {
3501
3649
  await (mode === "forward" ? this.forwardClient : this.client).delete(`/skills/${id}`);
3502
3650
  }
3503
3651
  async createAgent(name, decl, refs) {
3504
- const body = mapAgent2(name, decl, refs, void 0, this.projectName);
3652
+ const body = mapAgent2(name, decl, refs, void 0, this.projectName, "create");
3505
3653
  const res = await this.client.post("/agents", body);
3506
3654
  return toRemoteResource(res);
3507
3655
  }
3508
3656
  async updateAgent(id, name, decl, refs) {
3509
3657
  const current = await this.client.get(`/agents/${id}`);
3510
- const body = mapAgent2(name, decl, refs, current.version, this.projectName);
3658
+ const body = mapAgent2(name, decl, refs, current.version, this.projectName, "update");
3511
3659
  const res = await this.client.put(`/agents/${id}`, body);
3512
3660
  return toRemoteResource(res);
3513
3661
  }
@@ -3515,13 +3663,13 @@ var QoderAdapter = class _QoderAdapter {
3515
3663
  await this.client.delete(`/agents/${id}`);
3516
3664
  }
3517
3665
  async createTemplate(name, decl, refs) {
3518
- const body = mapForwardTemplate(name, decl, refs, this.projectName);
3666
+ const body = mapForwardTemplate(name, decl, refs, this.projectName, "create");
3519
3667
  const res = await this.forwardClient.post("/templates", body);
3520
3668
  await this.reconcileForwardMemoryMounts(res.id, refs);
3521
3669
  return toRemoteResource(res);
3522
3670
  }
3523
3671
  async updateTemplate(id, name, decl, refs) {
3524
- const body = mapForwardTemplate(name, decl, refs, this.projectName);
3672
+ const body = mapForwardTemplate(name, decl, refs, this.projectName, "update");
3525
3673
  if (!refs.tunnel_id) body.tunnel_id = null;
3526
3674
  const res = await this.forwardClient.post(`/templates/${id}`, body);
3527
3675
  await this.reconcileForwardMemoryMounts(id, refs);
@@ -4120,11 +4268,7 @@ var QODER_CAPABILITIES = {
4120
4268
  template: { tier: "native", reason: "Forward Templates API" },
4121
4269
  memory_store: { tier: "native", reason: "memory_stores API" },
4122
4270
  mcp_server: { tier: "native", reason: "mcp_servers field on agent" },
4123
- multiagent: {
4124
- tier: "unsupported",
4125
- reason: "no multiagent primitive on Qoder",
4126
- remediation: "deploy agents independently and orchestrate via MCP"
4127
- },
4271
+ multiagent: { tier: "native", reason: "coordinator + roster topology" },
4128
4272
  deployment: {
4129
4273
  tier: "native",
4130
4274
  reason: "deployments API with scheduled and manual runs"
@@ -4299,7 +4443,7 @@ function envToDecl3(raw) {
4299
4443
  metadata: stripAgentsMetadata(raw.metadata)
4300
4444
  });
4301
4445
  }
4302
- function agentToDecl3(raw) {
4446
+ function agentToDecl3(raw, resolveMember) {
4303
4447
  const tools = raw.tools;
4304
4448
  const mcpServers = raw.mcp_servers;
4305
4449
  const skills = raw.skills;
@@ -4336,10 +4480,11 @@ function agentToDecl3(raw) {
4336
4480
  tools: builtinTools?.length ? { builtin: builtinTools } : void 0,
4337
4481
  mcp_servers: mcpServerDecls,
4338
4482
  skills: skillDecls,
4483
+ multiagent: reverseMultiagentDecl(raw.multiagent, resolveMember),
4339
4484
  metadata: stripAgentsMetadata(raw.metadata)
4340
4485
  });
4341
4486
  }
4342
- function mapAgent3(name, decl, refs, version, projectName, skillVersions) {
4487
+ function mapAgent3(name, decl, refs, version, projectName, skillVersions, operation = "create") {
4343
4488
  let modelId;
4344
4489
  if (typeof decl.model === "string") {
4345
4490
  modelId = decl.model;
@@ -4360,7 +4505,17 @@ function mapAgent3(name, decl, refs, version, projectName, skillVersions) {
4360
4505
  } else if (decl.metadata) {
4361
4506
  body.metadata = decl.metadata;
4362
4507
  }
4363
- const BAILIAN_BUILTINS = /* @__PURE__ */ new Set(["bash", "read", "write", "edit", "glob", "grep", "download_file"]);
4508
+ const BAILIAN_BUILTINS = /* @__PURE__ */ new Set([
4509
+ "bash",
4510
+ "read",
4511
+ "write",
4512
+ "edit",
4513
+ "glob",
4514
+ "grep",
4515
+ "web_search",
4516
+ "web_fetch",
4517
+ "mark_artifacts"
4518
+ ]);
4364
4519
  if (decl.tools) {
4365
4520
  const toolConfigs = resolveBuiltinTools(decl.tools, {
4366
4521
  supportedWireNames: BAILIAN_BUILTINS
@@ -4404,17 +4559,24 @@ function mapAgent3(name, decl, refs, version, projectName, skillVersions) {
4404
4559
  });
4405
4560
  }
4406
4561
  }
4407
- if (refs.skill_ids.length) {
4408
- body.skills = refs.skill_ids.map((s) => ({
4409
- // Bailian's SkillType enum is "customer" | "official"; map the
4410
- // resolver's generic "custom" sentinel to "customer".
4411
- type: s.type === "custom" ? "customer" : s.type,
4412
- skill_id: s.skill_id,
4413
- // Bailian composes `{skill_id}@{version}` internally and rejects
4414
- // entries without a version. Prefer explicit external references, then
4415
- // the latest active remote version, then the common initial version.
4416
- version: s.version ?? skillVersions?.[s.skill_id] ?? "1.0"
4417
- }));
4562
+ body.skills = refs.skill_ids.map((s) => ({
4563
+ // Bailian's SkillType enum is "customer" | "official"; map the
4564
+ // resolver's generic "custom" sentinel to "customer".
4565
+ type: s.type === "custom" ? "customer" : s.type,
4566
+ skill_id: s.skill_id,
4567
+ // Bailian composes `{skill_id}@{version}` internally and rejects
4568
+ // entries without a version. Prefer explicit external references, then
4569
+ // the latest active remote version, then the common initial version.
4570
+ version: s.version ?? skillVersions?.[s.skill_id] ?? "1.0"
4571
+ }));
4572
+ if (refs.multiagent) {
4573
+ body.multiagent = {
4574
+ type: "coordinator",
4575
+ // No version: Bailian resolves the child Thread on first creation.
4576
+ agents: refs.multiagent.members.map(({ remote_id }) => ({ type: "agent", id: remote_id }))
4577
+ };
4578
+ } else if (operation === "update") {
4579
+ body.multiagent = { type: "coordinator", agents: [] };
4418
4580
  }
4419
4581
  return body;
4420
4582
  }
@@ -4551,7 +4713,7 @@ var BailianAdapter = class _BailianAdapter {
4551
4713
  snapshot: comparable
4552
4714
  };
4553
4715
  }
4554
- normalizeDesiredResource(type, name, decl) {
4716
+ normalizeDesiredResource(type, name, decl, refs) {
4555
4717
  if (type === "environment") {
4556
4718
  return this.normalizeRemote(
4557
4719
  type,
@@ -4561,7 +4723,7 @@ var BailianAdapter = class _BailianAdapter {
4561
4723
  if (type === "agent") {
4562
4724
  return this.normalizeRemote(
4563
4725
  type,
4564
- mapAgent3(name, decl, { skill_ids: [] }, void 0, this.projectName)
4726
+ mapAgent3(name, decl, refs ?? { skill_ids: [] }, void 0, this.projectName)
4565
4727
  );
4566
4728
  }
4567
4729
  return null;
@@ -4586,17 +4748,23 @@ var BailianAdapter = class _BailianAdapter {
4586
4748
  instructions: raw.system,
4587
4749
  tools: normalizeBailianTools(raw.tools),
4588
4750
  mcp_servers: normalizeBailianMcpServers(raw.mcp_servers),
4751
+ multiagent: comparableMultiagentField(raw.multiagent, "managed"),
4589
4752
  metadata: stripAgentsMetadata(raw.metadata)
4590
4753
  });
4591
4754
  }
4592
4755
  async exportResources(type) {
4593
- return exportRemoteResources(this.client, type, {
4594
- envToDecl: envToDecl3,
4595
- vaultToDecl: vaultToDecl3,
4596
- fileToDecl: fileToDecl3,
4597
- skillToDecl: skillToDecl3,
4598
- agentToDecl: agentToDecl3
4599
- });
4756
+ return exportRemoteResources(
4757
+ this.client,
4758
+ type,
4759
+ {
4760
+ envToDecl: envToDecl3,
4761
+ vaultToDecl: vaultToDecl3,
4762
+ fileToDecl: fileToDecl3,
4763
+ skillToDecl: skillToDecl3,
4764
+ agentToDecl: agentToDecl3
4765
+ },
4766
+ this.projectName
4767
+ );
4600
4768
  }
4601
4769
  // --- Environment ---
4602
4770
  async createEnvironment(name, decl) {
@@ -4627,14 +4795,14 @@ var BailianAdapter = class _BailianAdapter {
4627
4795
  // --- Agent ---
4628
4796
  async createAgent(name, decl, refs) {
4629
4797
  const skillVersions = await this.fetchSkillVersions(refs);
4630
- const body = mapAgent3(name, decl, refs, void 0, this.projectName, skillVersions);
4798
+ const body = mapAgent3(name, decl, refs, void 0, this.projectName, skillVersions, "create");
4631
4799
  const res = await this.client.post("/agents", body);
4632
4800
  return toRemoteResource(res);
4633
4801
  }
4634
4802
  async updateAgent(id, name, decl, refs) {
4635
4803
  const current = await this.client.get(`/agents/${id}`);
4636
4804
  const skillVersions = await this.fetchSkillVersions(refs);
4637
- const body = mapAgent3(name, decl, refs, current.version, this.projectName, skillVersions);
4805
+ const body = mapAgent3(name, decl, refs, current.version, this.projectName, skillVersions, "update");
4638
4806
  const res = await this.client.post(`/agents/${id}`, body);
4639
4807
  return toRemoteResource(res);
4640
4808
  }
@@ -5356,11 +5524,7 @@ var BAILIAN_CAPABILITIES = {
5356
5524
  reason: "no memory store primitive on Bailian"
5357
5525
  },
5358
5526
  mcp_server: { tier: "native", reason: "mcp_servers field on agent (official servers)" },
5359
- multiagent: {
5360
- tier: "unsupported",
5361
- reason: "no multiagent primitive on Bailian",
5362
- remediation: "deploy agents independently and orchestrate via MCP"
5363
- },
5527
+ multiagent: { tier: "native", reason: "coordinator + roster topology" },
5364
5528
  deployment: {
5365
5529
  tier: "native",
5366
5530
  reason: "deployments API with cron schedules, manual runs, pause/unpause and archive"
@@ -5513,11 +5677,10 @@ function envToDecl4(raw) {
5513
5677
  metadata: stripAgentsMetadata(raw.metadata)
5514
5678
  });
5515
5679
  }
5516
- function agentToDecl4(raw) {
5680
+ function agentToDecl4(raw, resolveMember) {
5517
5681
  const tools = raw.tools;
5518
5682
  const mcpServers = raw.mcp_servers;
5519
5683
  const skills = raw.skills;
5520
- const multiagent = raw.multiagent;
5521
5684
  let builtinTools;
5522
5685
  let builtinPermissions;
5523
5686
  let allToolsEnabled = false;
@@ -5549,14 +5712,6 @@ function agentToDecl4(raw) {
5549
5712
  skill_id: s.skill_id
5550
5713
  }));
5551
5714
  }
5552
- let multiagentDecl;
5553
- if (multiagent) {
5554
- const rawAgents = multiagent.agents;
5555
- if (rawAgents?.length) {
5556
- const ids = rawAgents.map((a) => typeof a === "string" ? a : a.id).filter(Boolean);
5557
- if (ids.length) multiagentDecl = { type: "coordinator", agents: ids };
5558
- }
5559
- }
5560
5715
  let toolsDecl;
5561
5716
  if (builtinTools?.length) {
5562
5717
  toolsDecl = { builtin: builtinTools, permissions: builtinPermissions };
@@ -5573,7 +5728,7 @@ function agentToDecl4(raw) {
5573
5728
  tools: toolsDecl,
5574
5729
  mcp_servers: mcpServerDecls,
5575
5730
  skills: skillDecls,
5576
- multiagent: multiagentDecl,
5731
+ multiagent: reverseMultiagentDecl(raw.multiagent, resolveMember),
5577
5732
  metadata: stripAgentsMetadata(raw.metadata)
5578
5733
  });
5579
5734
  }
@@ -5657,10 +5812,10 @@ function mapAgent4(name, decl, refs, version, projectName) {
5657
5812
  skill_id: s.skill_id
5658
5813
  }));
5659
5814
  }
5660
- if (decl.multiagent && refs.multiagent_agent_ids?.length) {
5815
+ if (decl.multiagent && refs.multiagent) {
5661
5816
  body.multiagent = {
5662
5817
  type: "coordinator",
5663
- agents: refs.multiagent_agent_ids.map((id) => ({ type: "agent", id }))
5818
+ agents: refs.multiagent.members.map(({ remote_id }) => ({ type: "agent", id: remote_id }))
5664
5819
  };
5665
5820
  }
5666
5821
  return body;
@@ -5912,13 +6067,18 @@ var ArkAdapter = class _ArkAdapter {
5912
6067
  }
5913
6068
  async exportResources(type) {
5914
6069
  if (type === "skill") return [];
5915
- return exportRemoteResources(this.client, type, {
5916
- envToDecl: envToDecl4,
5917
- vaultToDecl: vaultToDecl4,
5918
- fileToDecl: fileToDecl4,
5919
- skillToDecl: skillToDecl4,
5920
- agentToDecl: agentToDecl4
5921
- });
6070
+ return exportRemoteResources(
6071
+ this.client,
6072
+ type,
6073
+ {
6074
+ envToDecl: envToDecl4,
6075
+ vaultToDecl: vaultToDecl4,
6076
+ fileToDecl: fileToDecl4,
6077
+ skillToDecl: skillToDecl4,
6078
+ agentToDecl: agentToDecl4
6079
+ },
6080
+ this.projectName
6081
+ );
5922
6082
  }
5923
6083
  // Ark skills support create + get + attach only. There is no list/update/delete endpoint
5924
6084
  // (GET /skills, PUT /skills/{id}, DELETE /skills/{id} all return 404 on real probe), so:
@@ -6233,6 +6393,50 @@ function severityIcon(s) {
6233
6393
  }
6234
6394
  }
6235
6395
 
6396
+ // src/internal/multiagent/topology.ts
6397
+ var MULTIAGENT_MEMBER_LIMIT = 20;
6398
+ function collectMultiagentTopologyDiagnostics(config, diagnostics) {
6399
+ const agents = config.agents ?? {};
6400
+ const names = Object.keys(agents).sort();
6401
+ for (const name of names) {
6402
+ const decl = agents[name];
6403
+ if (!decl?.multiagent) continue;
6404
+ for (const member of decl.multiagent.agents) {
6405
+ if (typeof member !== "string") continue;
6406
+ if (agents[member]?.multiagent) {
6407
+ diagnostics.error(
6408
+ "config.agent.multiagent.nested",
6409
+ `agent.${name}: multiagent member '${member}' is itself a coordinator; nested coordinators are not supported`
6410
+ );
6411
+ }
6412
+ }
6413
+ }
6414
+ const state = /* @__PURE__ */ new Map();
6415
+ const path = [];
6416
+ const visit2 = (name) => {
6417
+ if (state.get(name) === "done") return;
6418
+ state.set(name, "visiting");
6419
+ path.push(name);
6420
+ const members = agents[name]?.multiagent?.agents ?? [];
6421
+ for (const member of members) {
6422
+ if (typeof member !== "string") continue;
6423
+ if (!agents[member]) continue;
6424
+ if (state.get(member) === "visiting") {
6425
+ const cycle = [...path.slice(path.indexOf(member)), member];
6426
+ diagnostics.error(
6427
+ "config.agent.multiagent.cycle",
6428
+ `agent.${name}: multiagent cycle detected: ${cycle.join(" -> ")}`
6429
+ );
6430
+ continue;
6431
+ }
6432
+ visit2(member);
6433
+ }
6434
+ path.pop();
6435
+ state.set(name, "done");
6436
+ };
6437
+ for (const name of names) visit2(name);
6438
+ }
6439
+
6236
6440
  // src/internal/validation/bailian.ts
6237
6441
  function findMissingBailianMcpToolConfigs(config, providerName = "bailian") {
6238
6442
  if (providerName !== "bailian") return [];
@@ -6334,6 +6538,7 @@ function collectReferenceDiagnostics(config, diagnostics) {
6334
6538
  }
6335
6539
  if (agent.multiagent) {
6336
6540
  for (const subAgent of agent.multiagent.agents) {
6541
+ if (typeof subAgent !== "string") continue;
6337
6542
  if (!agentNames.has(subAgent)) {
6338
6543
  diagnostics.error(
6339
6544
  "config.agent.multiagent.unknown",
@@ -6346,6 +6551,7 @@ function collectReferenceDiagnostics(config, diagnostics) {
6346
6551
  }
6347
6552
  }
6348
6553
  }
6554
+ collectMultiagentTopologyDiagnostics(config, diagnostics);
6349
6555
  for (const [name, deployment] of Object.entries(config.deployments ?? {})) {
6350
6556
  if (deployment.tunnel && !tunnelNames.has(deployment.tunnel)) {
6351
6557
  diagnostics.error(
@@ -6667,15 +6873,44 @@ function collectProviderCapabilities(config, providers, diagnostics) {
6667
6873
  { type: "template", name, provider: providerName }
6668
6874
  );
6669
6875
  }
6670
- if (agent.multiagent) {
6876
+ }
6877
+ if (providerName === "qoder" && agent.multiagent) {
6878
+ const coordinatorMode = resolveAgentMaterialization(providerName, agent).mode;
6879
+ for (const memberName of agent.multiagent.agents) {
6880
+ if (typeof memberName !== "string") {
6881
+ if (coordinatorMode === "forward") {
6882
+ diagnostics.error(
6883
+ "qoder.template.multiagent.external_member",
6884
+ `agent.${name}: Qoder Forward coordinator cannot reference external Managed Agent '${memberName.agent_id}'.`,
6885
+ { type: "template", name, provider: providerName }
6886
+ );
6887
+ }
6888
+ continue;
6889
+ }
6890
+ const memberDecl = config.agents?.[memberName];
6891
+ if (!memberDecl) continue;
6892
+ const memberMode = resolveAgentMaterialization(providerName, memberDecl).mode;
6893
+ if (memberMode === coordinatorMode) continue;
6671
6894
  diagnostics.error(
6672
- "qoder.template.multiagent.unsupported",
6673
- `agent.${name}: multiagent is not yet supported by Qoder Forward Template delivery.`,
6674
- { type: "template", name, provider: providerName }
6895
+ "qoder.template.multiagent.member_materialization",
6896
+ `agent.${name}: multiagent member '${memberName}' uses ${memberMode} delivery but the coordinator uses ${coordinatorMode}; Qoder multiagent members must match the coordinator's delivery type.`,
6897
+ { type: coordinatorMode === "forward" ? "template" : "agent", name, provider: providerName }
6675
6898
  );
6676
6899
  }
6677
6900
  }
6678
6901
  }
6902
+ if (providerName === "qoder" || providerName === "bailian") {
6903
+ for (const [name, agent] of Object.entries(config.agents ?? {})) {
6904
+ if (agent.provider && agent.provider !== providerName) continue;
6905
+ const size = agent.multiagent?.agents.length ?? 0;
6906
+ if (size <= MULTIAGENT_MEMBER_LIMIT) continue;
6907
+ diagnostics.error(
6908
+ `${providerName}.agent.multiagent.member_limit`,
6909
+ `agent.${name}: provider '${providerName}' supports at most ${MULTIAGENT_MEMBER_LIMIT} multiagent members; got ${size}.`,
6910
+ { type: resolveAgentMaterialization(providerName, agent).resourceType, name, provider: providerName }
6911
+ );
6912
+ }
6913
+ }
6679
6914
  if (providerName === "qoder") {
6680
6915
  for (const [name, deployment] of Object.entries(config.deployments ?? {})) {
6681
6916
  if (deployment.provider && deployment.provider !== providerName) continue;