spawnfile 0.1.0 → 0.1.2

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.
Files changed (107) hide show
  1. package/README.md +80 -397
  2. package/dist/cli/modelCommands.d.ts +3 -0
  3. package/dist/cli/modelCommands.js +68 -0
  4. package/dist/cli/runCli.d.ts +6 -1
  5. package/dist/cli/runCli.js +12 -67
  6. package/dist/cli/runtimeCommands.d.ts +3 -0
  7. package/dist/cli/runtimeCommands.js +20 -0
  8. package/dist/cli/surfaceCommands.d.ts +3 -0
  9. package/dist/cli/surfaceCommands.js +98 -0
  10. package/dist/compiler/agentSurfaces.js +51 -5
  11. package/dist/compiler/buildCompilePlan.js +36 -40
  12. package/dist/compiler/buildCompilePlanRuntime.d.ts +14 -0
  13. package/dist/compiler/buildCompilePlanRuntime.js +39 -0
  14. package/dist/compiler/buildCompilePlanTeams.d.ts +5 -0
  15. package/dist/compiler/buildCompilePlanTeams.js +38 -0
  16. package/dist/compiler/compilePlanHelpers.js +4 -1
  17. package/dist/compiler/compileProject.js +62 -13
  18. package/dist/compiler/compileProjectSupport.d.ts +17 -0
  19. package/dist/compiler/compileProjectSupport.js +136 -0
  20. package/dist/compiler/containerArtifacts.d.ts +6 -1
  21. package/dist/compiler/containerArtifacts.js +26 -4
  22. package/dist/compiler/containerArtifactsPlans.js +16 -1
  23. package/dist/compiler/containerArtifactsRender.d.ts +4 -2
  24. package/dist/compiler/containerArtifactsRender.js +21 -126
  25. package/dist/compiler/containerArtifactsTypes.d.ts +7 -0
  26. package/dist/compiler/containerEntrypointRender.d.ts +12 -0
  27. package/dist/compiler/containerEntrypointRender.js +186 -0
  28. package/dist/compiler/index.d.ts +2 -0
  29. package/dist/compiler/index.js +2 -0
  30. package/dist/compiler/interactiveSurfaceScopes.d.ts +2 -0
  31. package/dist/compiler/interactiveSurfaceScopes.js +21 -0
  32. package/dist/compiler/moltnetArtifacts.d.ts +27 -0
  33. package/dist/compiler/moltnetArtifacts.js +204 -0
  34. package/dist/compiler/moltnetBinaries.d.ts +4 -0
  35. package/dist/compiler/moltnetBinaries.js +103 -0
  36. package/dist/compiler/moltnetClientConfig.d.ts +11 -0
  37. package/dist/compiler/moltnetClientConfig.js +89 -0
  38. package/dist/compiler/moltnetRepresentativeResolution.d.ts +16 -0
  39. package/dist/compiler/moltnetRepresentativeResolution.js +86 -0
  40. package/dist/compiler/moltnetResolution.d.ts +3 -0
  41. package/dist/compiler/moltnetResolution.js +201 -0
  42. package/dist/compiler/runProject.js +1 -1
  43. package/dist/compiler/surfaceDefinitions.d.ts +55 -0
  44. package/dist/compiler/surfaceDefinitions.js +204 -0
  45. package/dist/compiler/teamContextHelpers.d.ts +18 -0
  46. package/dist/compiler/teamContextHelpers.js +112 -0
  47. package/dist/compiler/teamContextSupport.d.ts +4 -0
  48. package/dist/compiler/teamContextSupport.js +264 -0
  49. package/dist/compiler/teamContextSupport.testHelpers.d.ts +16 -0
  50. package/dist/compiler/teamContextSupport.testHelpers.js +68 -0
  51. package/dist/compiler/teamContextTypes.d.ts +28 -0
  52. package/dist/compiler/teamRoster.d.ts +12 -0
  53. package/dist/compiler/teamRoster.js +48 -0
  54. package/dist/compiler/teamRosterEntries.d.ts +13 -0
  55. package/dist/compiler/teamRosterEntries.js +230 -0
  56. package/dist/compiler/teamRosterTypes.d.ts +45 -0
  57. package/dist/compiler/types.d.ts +72 -6
  58. package/dist/compiler/updateProjectRuntime.d.ts +9 -0
  59. package/dist/compiler/updateProjectRuntime.js +67 -0
  60. package/dist/compiler/updateProjectSurfaces.d.ts +8 -0
  61. package/dist/compiler/updateProjectSurfaces.js +106 -0
  62. package/dist/manifest/loadManifest.js +4 -4
  63. package/dist/manifest/renderSpawnfile.js +74 -8
  64. package/dist/manifest/scaffold.js +1 -3
  65. package/dist/manifest/schemas.d.ts +227 -17
  66. package/dist/manifest/schemas.js +62 -20
  67. package/dist/manifest/surfaceSchemas.d.ts +154 -0
  68. package/dist/manifest/surfaceSchemas.js +77 -5
  69. package/dist/runtime/common.js +3 -0
  70. package/dist/runtime/openclaw/adapter.js +38 -5
  71. package/dist/runtime/openclaw/moltnet.d.ts +12 -0
  72. package/dist/runtime/openclaw/moltnet.js +124 -0
  73. package/dist/runtime/openclaw/surfaces.js +3 -0
  74. package/dist/runtime/picoclaw/adapter.js +27 -8
  75. package/dist/runtime/picoclaw/pico.d.ts +2 -0
  76. package/dist/runtime/picoclaw/pico.js +2 -0
  77. package/dist/runtime/picoclaw/surfaces.js +11 -0
  78. package/dist/runtime/tinyclaw/adapter.js +22 -8
  79. package/dist/runtime/tinyclaw/runAuth.js +28 -1
  80. package/dist/runtime/tinyclaw/surfaces.js +8 -0
  81. package/dist/runtime/types.d.ts +11 -0
  82. package/package.json +10 -3
  83. package/runtimes.yaml +4 -4
  84. package/dist/.env.example +0 -5
  85. package/dist/Dockerfile +0 -21
  86. package/dist/compiler/discordSurface.d.ts +0 -4
  87. package/dist/compiler/discordSurface.js +0 -28
  88. package/dist/container/rootfs/var/lib/spawnfile/instances/picoclaw/agent-assistant/picoclaw/config.json +0 -16
  89. package/dist/container/rootfs/var/lib/spawnfile/instances/picoclaw/agent-assistant/picoclaw/workspace/AGENTS.md +0 -1
  90. package/dist/e2e/cli.js +0 -40
  91. package/dist/e2e/dockerAuth.d.ts +0 -18
  92. package/dist/e2e/dockerAuth.js +0 -212
  93. package/dist/e2e/fixtures.d.ts +0 -2
  94. package/dist/e2e/fixtures.js +0 -49
  95. package/dist/e2e/index.d.ts +0 -4
  96. package/dist/e2e/index.js +0 -4
  97. package/dist/e2e/runtimePrompts.d.ts +0 -13
  98. package/dist/e2e/runtimePrompts.js +0 -132
  99. package/dist/e2e/scenarios.d.ts +0 -3
  100. package/dist/e2e/scenarios.js +0 -84
  101. package/dist/e2e/types.d.ts +0 -35
  102. package/dist/entrypoint.sh +0 -71
  103. package/dist/runtimes/picoclaw/agents/assistant/config.json +0 -16
  104. package/dist/runtimes/picoclaw/agents/assistant/workspace/AGENTS.md +0 -1
  105. package/dist/spawnfile-report.json +0 -71
  106. /package/dist/{e2e/cli.d.ts → compiler/teamContextTypes.js} +0 -0
  107. /package/dist/{e2e/types.js → compiler/teamRosterTypes.js} +0 -0
@@ -0,0 +1,112 @@
1
+ import { createHash } from "node:crypto";
2
+ import { generateTeamRoster, listTeamRepresentatives } from "./teamRoster.js";
3
+ export const pathSafe = (value) => {
4
+ const normalized = value
5
+ .toLowerCase()
6
+ .replace(/[^a-z0-9_-]+/g, "-")
7
+ .replace(/^-+|-+$/g, "");
8
+ return normalized || "context";
9
+ };
10
+ export const shortHash = (value) => createHash("sha256").update(value).digest("hex").slice(0, 8);
11
+ export const reportKeySegment = (value) => /^[A-Za-z0-9_-]+$/.test(value)
12
+ ? value
13
+ : encodeURIComponent(value).replace(/\./g, "%2E");
14
+ export const findTeamBySource = (plan, source) => {
15
+ const node = plan.nodes.find((entry) => entry.kind === "team" && entry.value.source === source);
16
+ return node?.value.kind === "team" ? node.value : null;
17
+ };
18
+ export const findAgentBySource = (plan, source) => {
19
+ const node = plan.nodes.find((entry) => entry.kind === "agent" && entry.value.source === source);
20
+ return node?.value.kind === "agent" ? node.value : null;
21
+ };
22
+ export const getSystemTeamDoc = (teamNode) => teamNode.docs.find((doc) => doc.role === "system")?.content ?? "";
23
+ export const createContextBaseKey = (context) => context.kind === "direct"
24
+ ? pathSafe(context.teamNode.name)
25
+ : `${pathSafe(context.parentTeamNode.name)}--${pathSafe(context.representedMember.id)}`;
26
+ export const collectMoltnetBindings = (agentNode, teamSource, memberId) => (agentNode.surfaces?.moltnet ?? [])
27
+ .filter((attachment) => attachment.memberId === memberId)
28
+ .map((attachment) => ({
29
+ network: attachment.network,
30
+ rooms: [
31
+ ...(attachment.contextRooms?.[teamSource] ??
32
+ (attachment.teamSource === teamSource ? Object.keys(attachment.rooms ?? {}) : []))
33
+ ].sort()
34
+ }))
35
+ .filter((binding) => binding.rooms.length > 0);
36
+ export const createTeamCard = (contextKey, member, childTeam, representatives) => ({
37
+ content: [
38
+ `# ${childTeam.name}`,
39
+ "",
40
+ childTeam.description,
41
+ "",
42
+ childTeam.docs.find((doc) => doc.role === "identity")?.content ?? "",
43
+ "## Representatives",
44
+ "",
45
+ ...representatives.map((representative) => `- \`${representative}\``)
46
+ ].filter((line, index, lines) => line !== "" || lines[index - 1] !== "").join("\n").trimEnd() + "\n",
47
+ path: `.spawnfile/team-cards/${contextKey}/${member.id}.md`
48
+ });
49
+ const formatMoltnetBindings = (surfaces) => {
50
+ const moltnet = (surfaces &&
51
+ typeof surfaces === "object" &&
52
+ "moltnet" in surfaces &&
53
+ Array.isArray(surfaces.moltnet))
54
+ ? surfaces.moltnet
55
+ : [];
56
+ return moltnet.flatMap((binding) => {
57
+ const network = typeof binding.network === "string" ? binding.network : null;
58
+ const rooms = Array.isArray(binding.rooms)
59
+ ? binding.rooms.filter((room) => typeof room === "string")
60
+ : [];
61
+ return network
62
+ ? rooms.map((room) => ` Moltnet: \`${network}\` / room \`${room}\``)
63
+ : [];
64
+ });
65
+ };
66
+ const appendSurfaceBindingLines = (lines, entry) => {
67
+ const bindings = formatMoltnetBindings(entry.surfaces);
68
+ if (bindings.length === 0) {
69
+ return;
70
+ }
71
+ lines.push(" Surface bindings:");
72
+ lines.push(...bindings);
73
+ };
74
+ export const buildContextOrientation = (index) => {
75
+ const lines = ["# Spawnfile Team Context", ""];
76
+ if (index.direct_memberships.length > 0) {
77
+ lines.push("You are a direct member of these teams:", "");
78
+ index.direct_memberships.forEach((entry, itemIndex) => {
79
+ lines.push(`${itemIndex + 1}. Team: \`${entry.team}\``);
80
+ lines.push(` Member slot: \`${entry.member}\``);
81
+ lines.push(` Read \`${entry.team_doc}\` and \`${entry.roster}\`.`);
82
+ appendSurfaceBindingLines(lines, entry);
83
+ lines.push("");
84
+ });
85
+ }
86
+ if (index.representations.length > 0) {
87
+ lines.push("You also represent teams in parent contexts:", "");
88
+ index.representations.forEach((entry, itemIndex) => {
89
+ lines.push(`${itemIndex + 1}. Parent team: \`${entry.team}\``);
90
+ lines.push(` Represents slot: \`${entry.represents}\``);
91
+ lines.push(` Delegate role: \`${entry.delegate_role}\``);
92
+ lines.push(` Read \`${entry.team_doc}\` and \`${entry.roster}\`.`);
93
+ appendSurfaceBindingLines(lines, entry);
94
+ lines.push("");
95
+ });
96
+ }
97
+ lines.push("Use the context matching the surface or message you are handling. Do not merge team documents.");
98
+ lines.push("For machine-readable bindings, read `.spawnfile/team-contexts.yaml`.");
99
+ return `${lines.join("\n").trimEnd()}\n`;
100
+ };
101
+ export const createVisibleTeamCards = (plan, contextKey, teamNode, visibleMembers) => visibleMembers
102
+ .filter((member) => member.kind === "team")
103
+ .flatMap((member) => {
104
+ const childTeam = findTeamBySource(plan, member.nodeSource);
105
+ if (!childTeam) {
106
+ return [];
107
+ }
108
+ return [
109
+ createTeamCard(contextKey, member, childTeam, listTeamRepresentatives(plan, childTeam).map((representative) => representative.memberId))
110
+ ];
111
+ });
112
+ export const generateContextRoster = generateTeamRoster;
@@ -0,0 +1,4 @@
1
+ import type { TeamCompileSupport } from "./teamContextTypes.js";
2
+ export type { TeamCompileSupport } from "./teamContextTypes.js";
3
+ import type { CompilePlan } from "./types.js";
4
+ export declare const prepareTeamCompileSupport: (plan: CompilePlan) => Promise<TeamCompileSupport>;
@@ -0,0 +1,264 @@
1
+ import YAML from "yaml";
2
+ import { buildContextOrientation, collectMoltnetBindings, createContextBaseKey, createVisibleTeamCards, findAgentBySource, findTeamBySource, getSystemTeamDoc, reportKeySegment, shortHash } from "./teamContextHelpers.js";
3
+ import { generateTeamRoster, listTeamRepresentatives } from "./teamRoster.js";
4
+ const assignContextKeys = (contexts) => {
5
+ const baseCounts = new Map();
6
+ for (const context of contexts) {
7
+ const base = createContextBaseKey(context);
8
+ baseCounts.set(base, (baseCounts.get(base) ?? 0) + 1);
9
+ }
10
+ const used = new Set();
11
+ for (const context of contexts) {
12
+ const base = createContextBaseKey(context);
13
+ const tuple = context.kind === "direct"
14
+ ? `direct:${context.membership.teamSource}:${context.membership.memberId}`
15
+ : `representative:${context.parentTeamNode.source}:${context.representedMember.id}:${context.representativeMemberId}`;
16
+ const key = baseCounts.get(base) === 1 && !used.has(base)
17
+ ? base
18
+ : `${base}--${shortHash(tuple)}`;
19
+ context.contextKey = key;
20
+ used.add(key);
21
+ }
22
+ };
23
+ const collectAgentContexts = (plan) => {
24
+ const contextsByAgent = new Map();
25
+ const addContext = (agentSource, context) => {
26
+ const contexts = contextsByAgent.get(agentSource) ?? [];
27
+ contexts.push(context);
28
+ contextsByAgent.set(agentSource, contexts);
29
+ };
30
+ for (const membership of plan.memberships ?? []) {
31
+ const teamNode = findTeamBySource(plan, membership.teamSource);
32
+ if (teamNode) {
33
+ addContext(membership.agentSource, {
34
+ contextKey: "",
35
+ kind: "direct",
36
+ membership,
37
+ teamNode
38
+ });
39
+ }
40
+ }
41
+ for (const node of plan.nodes) {
42
+ if (node.value.kind !== "team") {
43
+ continue;
44
+ }
45
+ const parentTeamNode = node.value;
46
+ for (const member of parentTeamNode.members.filter((entry) => entry.kind === "team")) {
47
+ const childTeam = findTeamBySource(plan, member.nodeSource);
48
+ if (!childTeam) {
49
+ continue;
50
+ }
51
+ const delegateRole = member.id === parentTeamNode.lead ? "lead" : "representative";
52
+ for (const representative of listTeamRepresentatives(plan, childTeam)) {
53
+ addContext(representative.agentSource, {
54
+ contextKey: "",
55
+ delegateRole,
56
+ kind: "representative",
57
+ parentTeamNode,
58
+ representedMember: member,
59
+ representativeMemberId: representative.memberId,
60
+ representativeSource: representative.agentSource
61
+ });
62
+ }
63
+ }
64
+ }
65
+ for (const contexts of contextsByAgent.values()) {
66
+ assignContextKeys(contexts);
67
+ }
68
+ return contextsByAgent;
69
+ };
70
+ const addMapEntries = (target, key, entries) => {
71
+ if (entries.length === 0) {
72
+ return;
73
+ }
74
+ target.set(key, [...(target.get(key) ?? []), ...entries]);
75
+ };
76
+ const createTeamCapabilities = (teamNode) => [
77
+ {
78
+ key: "team.roster",
79
+ message: "Context-scoped team rosters were emitted",
80
+ outcome: "supported"
81
+ },
82
+ {
83
+ key: "team.context_orientation",
84
+ message: "Team context orientation was surfaced through runtime system instructions",
85
+ outcome: "supported"
86
+ },
87
+ {
88
+ key: "team.representatives",
89
+ message: "Representative chains were resolved",
90
+ outcome: "supported"
91
+ },
92
+ ...((teamNode.networks?.length ?? 0) > 0
93
+ ? [
94
+ {
95
+ key: "team.networks",
96
+ message: "Team networks were lowered",
97
+ outcome: "supported"
98
+ },
99
+ {
100
+ key: "team.networks.moltnet",
101
+ message: "Moltnet team networks were lowered",
102
+ outcome: "supported"
103
+ },
104
+ ...(teamNode.networks ?? []).map((network) => ({
105
+ key: `team.networks.moltnet.${reportKeySegment(network.id)}`,
106
+ message: `Moltnet network ${network.id} was lowered`,
107
+ outcome: "supported"
108
+ }))
109
+ ]
110
+ : [])
111
+ ];
112
+ const createRepresentativeDiagnostics = (plan, teamNode) => {
113
+ const diagnostics = [];
114
+ for (const member of teamNode.members.filter((entry) => entry.kind === "team")) {
115
+ const childTeam = findTeamBySource(plan, member.nodeSource);
116
+ if (!childTeam) {
117
+ continue;
118
+ }
119
+ if (childTeam.mode === "swarm" && !childTeam.externalExplicit) {
120
+ diagnostics.push({
121
+ level: "warn",
122
+ message: `Nested swarm team ${childTeam.name} is exposed without explicit external representatives`
123
+ });
124
+ }
125
+ if (teamNode.lead === member.id && !childTeam.externalExplicit) {
126
+ const representatives = listTeamRepresentatives(plan, childTeam);
127
+ if (representatives.length > 1) {
128
+ diagnostics.push({
129
+ level: "warn",
130
+ message: `Team ${teamNode.name} lead ${member.id} resolves to multiple implicit representatives`
131
+ });
132
+ }
133
+ }
134
+ }
135
+ return diagnostics;
136
+ };
137
+ const addAmbiguousBindingDiagnostics = (agentNode, contexts, diagnosticsByTeamSource) => {
138
+ const bindings = new Map();
139
+ for (const context of contexts) {
140
+ const teamSource = context.kind === "direct"
141
+ ? context.membership.teamSource
142
+ : context.parentTeamNode.source;
143
+ const memberId = context.kind === "direct"
144
+ ? context.membership.memberId
145
+ : context.representativeMemberId;
146
+ for (const binding of collectMoltnetBindings(agentNode, teamSource, memberId)) {
147
+ for (const room of binding.rooms) {
148
+ const key = `moltnet:${binding.network}:${room}`;
149
+ bindings.set(key, [...(bindings.get(key) ?? []), context]);
150
+ }
151
+ }
152
+ }
153
+ for (const [binding, bindingContexts] of bindings) {
154
+ const uniqueKeys = [...new Set(bindingContexts.map((context) => context.contextKey))];
155
+ if (uniqueKeys.length < 2) {
156
+ continue;
157
+ }
158
+ for (const context of bindingContexts) {
159
+ const teamSource = context.kind === "direct"
160
+ ? context.membership.teamSource
161
+ : context.parentTeamNode.source;
162
+ addMapEntries(diagnosticsByTeamSource, teamSource, [
163
+ {
164
+ level: "warn",
165
+ message: `Agent ${agentNode.name} maps ${binding} to multiple team contexts: ${uniqueKeys.join(", ")}`
166
+ }
167
+ ]);
168
+ }
169
+ }
170
+ };
171
+ export const prepareTeamCompileSupport = async (plan) => {
172
+ const capabilitiesByTeamSource = new Map();
173
+ const diagnosticsByTeamSource = new Map();
174
+ const filesByAgentSource = new Map();
175
+ const contextsByAgent = collectAgentContexts(plan);
176
+ for (const node of plan.nodes) {
177
+ if (node.value.kind !== "team") {
178
+ continue;
179
+ }
180
+ addMapEntries(capabilitiesByTeamSource, node.value.source, createTeamCapabilities(node.value));
181
+ addMapEntries(diagnosticsByTeamSource, node.value.source, createRepresentativeDiagnostics(plan, node.value));
182
+ }
183
+ for (const [agentSource, contexts] of contextsByAgent) {
184
+ const agentNode = findAgentBySource(plan, agentSource);
185
+ if (!agentNode) {
186
+ continue;
187
+ }
188
+ const directCount = contexts.filter((context) => context.kind === "direct").length;
189
+ const directIndex = [];
190
+ const representationIndex = [];
191
+ const files = [];
192
+ for (const context of contexts) {
193
+ if (context.kind === "direct") {
194
+ const teamDocPath = `.spawnfile/team-contexts/${context.contextKey}/TEAM.md`;
195
+ const rosterPath = `.spawnfile/rosters/${context.contextKey}.yaml`;
196
+ const generated = generateTeamRoster(context.teamNode, plan, {
197
+ contextKey: context.contextKey,
198
+ selfMemberId: context.membership.memberId,
199
+ teamSource: context.membership.teamSource
200
+ });
201
+ const aliases = directCount === 1
202
+ ? { roster: ".spawnfile/roster.yaml", team_doc: "TEAM.md" }
203
+ : undefined;
204
+ files.push({ content: getSystemTeamDoc(context.teamNode), path: teamDocPath }, { content: generated.roster, path: rosterPath }, ...createVisibleTeamCards(plan, context.contextKey, context.teamNode, generated.visibleMembers));
205
+ if (aliases) {
206
+ files.push({ content: getSystemTeamDoc(context.teamNode), path: aliases.team_doc }, { content: generated.roster, path: aliases.roster });
207
+ }
208
+ directIndex.push({
209
+ ...(aliases ? { aliases } : {}),
210
+ context_key: context.contextKey,
211
+ member: context.membership.memberId,
212
+ roster: rosterPath,
213
+ surfaces: {
214
+ moltnet: collectMoltnetBindings(agentNode, context.membership.teamSource, context.membership.memberId)
215
+ },
216
+ team: context.teamNode.name,
217
+ team_doc: teamDocPath
218
+ });
219
+ addMapEntries(diagnosticsByTeamSource, context.teamNode.source, generated.diagnostics);
220
+ }
221
+ else {
222
+ const teamDocPath = `.spawnfile/team-contexts/${context.contextKey}/TEAM.md`;
223
+ const rosterPath = `.spawnfile/rosters/${context.contextKey}.yaml`;
224
+ const generated = generateTeamRoster(context.parentTeamNode, plan, {
225
+ contextKey: context.contextKey,
226
+ delegateRole: context.delegateRole,
227
+ representedSlotId: context.representedMember.id,
228
+ selfMemberId: context.representativeMemberId,
229
+ teamSource: context.parentTeamNode.source
230
+ });
231
+ const cards = createVisibleTeamCards(plan, context.contextKey, context.parentTeamNode, generated.visibleMembers);
232
+ files.push({ content: getSystemTeamDoc(context.parentTeamNode), path: teamDocPath }, { content: generated.roster, path: rosterPath }, ...cards);
233
+ representationIndex.push({
234
+ cards: cards.map((file) => file.path),
235
+ context_key: context.contextKey,
236
+ delegate_role: context.delegateRole,
237
+ representative: context.representativeMemberId,
238
+ represents: context.representedMember.id,
239
+ roster: rosterPath,
240
+ surfaces: {
241
+ moltnet: collectMoltnetBindings(agentNode, context.parentTeamNode.source, context.representativeMemberId)
242
+ },
243
+ team: context.parentTeamNode.name,
244
+ team_doc: teamDocPath
245
+ });
246
+ addMapEntries(diagnosticsByTeamSource, context.parentTeamNode.source, generated.diagnostics);
247
+ }
248
+ }
249
+ const contextIndex = {
250
+ direct_memberships: directIndex,
251
+ representations: representationIndex
252
+ };
253
+ files.push({
254
+ content: YAML.stringify(contextIndex),
255
+ path: ".spawnfile/team-contexts.yaml"
256
+ }, {
257
+ content: buildContextOrientation(contextIndex),
258
+ path: ".spawnfile/team-contexts.md"
259
+ });
260
+ filesByAgentSource.set(agentSource, files);
261
+ addAmbiguousBindingDiagnostics(agentNode, contexts, diagnosticsByTeamSource);
262
+ }
263
+ return { capabilitiesByTeamSource, diagnosticsByTeamSource, filesByAgentSource };
264
+ };
@@ -0,0 +1,16 @@
1
+ import type { EmittedFile } from "../runtime/index.js";
2
+ import type { CompilePlan, ResolvedAgentNode, ResolvedAgentSurfaces, ResolvedMemberRef, ResolvedTeamNetwork, ResolvedTeamNode } from "./types.js";
3
+ export declare const createTestAgent: (name: string, source: string, surfaces?: ResolvedAgentSurfaces) => ResolvedAgentNode;
4
+ export declare const createTestTeam: (input: {
5
+ docs?: ResolvedTeamNode["docs"];
6
+ external?: string[];
7
+ externalExplicit?: boolean;
8
+ lead?: string | null;
9
+ members: ResolvedMemberRef[];
10
+ mode?: "hierarchical" | "swarm";
11
+ name: string;
12
+ networks?: ResolvedTeamNetwork[];
13
+ source: string;
14
+ }) => ResolvedTeamNode;
15
+ export declare const createTestPlan: (agents: ResolvedAgentNode[], teams: ResolvedTeamNode[], memberships: NonNullable<CompilePlan["memberships"]>) => CompilePlan;
16
+ export declare const findTestFile: (files: EmittedFile[], filePath: string) => EmittedFile;
@@ -0,0 +1,68 @@
1
+ export const createTestAgent = (name, source, surfaces) => ({
2
+ description: `${name} description`,
3
+ docs: [],
4
+ env: {},
5
+ execution: undefined,
6
+ kind: "agent",
7
+ mcpServers: [],
8
+ name,
9
+ policyMode: null,
10
+ policyOnDegrade: null,
11
+ runtime: { name: "openclaw", options: {} },
12
+ secrets: [],
13
+ skills: [],
14
+ source,
15
+ surfaces,
16
+ subagents: []
17
+ });
18
+ export const createTestTeam = (input) => ({
19
+ description: `${input.name} description`,
20
+ docs: input.docs ?? [
21
+ {
22
+ content: `# ${input.name} operating context\n`,
23
+ role: "system",
24
+ sourcePath: `${input.source}/TEAM.md`
25
+ }
26
+ ],
27
+ external: input.external ?? [],
28
+ externalExplicit: input.externalExplicit ?? false,
29
+ kind: "team",
30
+ lead: input.lead ?? null,
31
+ members: input.members,
32
+ mode: input.mode ?? "swarm",
33
+ name: input.name,
34
+ networks: input.networks,
35
+ policyMode: null,
36
+ policyOnDegrade: null,
37
+ shared: { env: {}, mcpServers: [], secrets: [], skills: [] },
38
+ source: input.source
39
+ });
40
+ export const createTestPlan = (agents, teams, memberships) => ({
41
+ edges: [],
42
+ memberships,
43
+ nodes: [
44
+ ...teams.map((team, index) => ({
45
+ id: `team-${index}`,
46
+ kind: "team",
47
+ runtimeName: null,
48
+ slug: team.name.toLowerCase().replaceAll(" ", "-"),
49
+ value: team
50
+ })),
51
+ ...agents.map((agent, index) => ({
52
+ id: `agent-${index}`,
53
+ kind: "agent",
54
+ runtimeName: agent.runtime.name,
55
+ slug: agent.name,
56
+ value: agent
57
+ }))
58
+ ],
59
+ root: teams[0]?.source ?? agents[0]?.source ?? "/tmp/Spawnfile",
60
+ runtimes: { openclaw: { nodeIds: agents.map((_, index) => `agent-${index}`) } }
61
+ });
62
+ export const findTestFile = (files, filePath) => {
63
+ const file = files.find((entry) => entry.path === filePath);
64
+ if (!file) {
65
+ throw new Error(`missing emitted file ${filePath}`);
66
+ }
67
+ return file;
68
+ };
@@ -0,0 +1,28 @@
1
+ import type { CapabilityReport, DiagnosticReport } from "../report/index.js";
2
+ import type { EmittedFile } from "../runtime/index.js";
3
+ import type { ResolvedMemberRef, ResolvedTeamMembershipContext, ResolvedTeamNode } from "./types.js";
4
+ export interface TeamCompileSupport {
5
+ capabilitiesByTeamSource: Map<string, CapabilityReport[]>;
6
+ diagnosticsByTeamSource: Map<string, DiagnosticReport[]>;
7
+ filesByAgentSource: Map<string, EmittedFile[]>;
8
+ }
9
+ export interface DirectContext {
10
+ contextKey: string;
11
+ kind: "direct";
12
+ membership: ResolvedTeamMembershipContext;
13
+ teamNode: ResolvedTeamNode;
14
+ }
15
+ export interface RepresentativeContext {
16
+ contextKey: string;
17
+ delegateRole: "lead" | "representative";
18
+ kind: "representative";
19
+ parentTeamNode: ResolvedTeamNode;
20
+ representedMember: ResolvedMemberRef;
21
+ representativeMemberId: string;
22
+ representativeSource: string;
23
+ }
24
+ export type AgentContext = DirectContext | RepresentativeContext;
25
+ export interface TeamContextIndex {
26
+ direct_memberships: Array<Record<string, unknown>>;
27
+ representations: Array<Record<string, unknown>>;
28
+ }
@@ -0,0 +1,12 @@
1
+ import type { DiagnosticReport } from "../report/index.js";
2
+ import { type TeamRepresentativeResolution } from "./moltnetResolution.js";
3
+ export type { GeneratedTeamRoster, GenerateTeamRosterOptions, Roster, RosterEntry, RosterRepresentativeEntry } from "./teamRosterTypes.js";
4
+ import type { GeneratedTeamRoster, GenerateTeamRosterOptions } from "./teamRosterTypes.js";
5
+ import type { CompilePlan, ResolvedTeamNode } from "./types.js";
6
+ export { getVisibleTeamMembers } from "./teamRosterEntries.js";
7
+ export declare const generateTeamRoster: (teamNode: ResolvedTeamNode, plan: CompilePlan, options: GenerateTeamRosterOptions) => GeneratedTeamRoster;
8
+ export declare const generateTeamRosters: (teamNode: ResolvedTeamNode, plan: CompilePlan) => {
9
+ diagnostics: DiagnosticReport[];
10
+ rosters: Map<string, string>;
11
+ };
12
+ export declare const listTeamRepresentatives: (plan: CompilePlan, teamNode: ResolvedTeamNode) => TeamRepresentativeResolution[];
@@ -0,0 +1,48 @@
1
+ import YAML from "yaml";
2
+ import { resolveTeamRepresentatives } from "./moltnetResolution.js";
3
+ import { collectConcreteParticipants, createCoordinationDiagnostics, createRosterEntry, getVisibleTeamMembers } from "./teamRosterEntries.js";
4
+ export { getVisibleTeamMembers } from "./teamRosterEntries.js";
5
+ export const generateTeamRoster = (teamNode, plan, options) => {
6
+ const visibleMembers = getVisibleTeamMembers(teamNode, options.selfMemberId, options.delegateRole, options.representedSlotId);
7
+ const roster = {
8
+ ...(options.representedSlotId && options.delegateRole
9
+ ? {
10
+ context_kind: "representative",
11
+ represents: {
12
+ delegate_role: options.delegateRole,
13
+ representative: options.selfMemberId,
14
+ slot: options.representedSlotId
15
+ }
16
+ }
17
+ : { context_kind: "direct" }),
18
+ lead: teamNode.lead,
19
+ members: Object.fromEntries(visibleMembers.map((member) => [
20
+ member.id,
21
+ createRosterEntry(plan, teamNode, member, options)
22
+ ])),
23
+ mode: teamNode.mode,
24
+ self: options.selfMemberId,
25
+ team: teamNode.name
26
+ };
27
+ const participants = collectConcreteParticipants(plan, teamNode, options.selfMemberId, visibleMembers, options.representedSlotId);
28
+ return {
29
+ diagnostics: createCoordinationDiagnostics(plan, teamNode, participants, options.teamSource),
30
+ roster: YAML.stringify(roster),
31
+ visibleMembers
32
+ };
33
+ };
34
+ export const generateTeamRosters = (teamNode, plan) => {
35
+ const diagnostics = [];
36
+ const rosters = new Map();
37
+ for (const member of teamNode.members.filter((entry) => entry.kind === "agent")) {
38
+ const generated = generateTeamRoster(teamNode, plan, {
39
+ contextKey: teamNode.name,
40
+ selfMemberId: member.id,
41
+ teamSource: teamNode.source
42
+ });
43
+ rosters.set(member.id, generated.roster);
44
+ diagnostics.push(...generated.diagnostics);
45
+ }
46
+ return { diagnostics, rosters };
47
+ };
48
+ export const listTeamRepresentatives = (plan, teamNode) => resolveTeamRepresentatives(plan, teamNode);
@@ -0,0 +1,13 @@
1
+ import type { DiagnosticReport } from "../report/index.js";
2
+ import type { GenerateTeamRosterOptions, RosterEntry } from "./teamRosterTypes.js";
3
+ import type { CompilePlan, ResolvedMemberRef, ResolvedTeamNode } from "./types.js";
4
+ export declare const getVisibleTeamMembers: (teamNode: ResolvedTeamNode, selfMemberId: string, delegateRole?: "lead" | "representative", representedSlotId?: string) => ResolvedMemberRef[];
5
+ export declare const createRosterEntry: (plan: CompilePlan, teamNode: ResolvedTeamNode, member: ResolvedMemberRef, options: GenerateTeamRosterOptions) => RosterEntry;
6
+ export declare const collectConcreteParticipants: (plan: CompilePlan, teamNode: ResolvedTeamNode, selfMemberId: string, visibleMembers: ResolvedMemberRef[], representedSlotId?: string) => Array<{
7
+ agentSource: string;
8
+ id: string;
9
+ }>;
10
+ export declare const createCoordinationDiagnostics: (plan: CompilePlan, teamNode: ResolvedTeamNode, participants: Array<{
11
+ agentSource: string;
12
+ id: string;
13
+ }>, contextTeamSource: string) => DiagnosticReport[];