@open-mercato/cli 0.7.1-develop.7193.1.910a5b0a1e → 0.7.1-develop.7195.1.b5efd1d51a

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 (169) hide show
  1. package/.turbo/turbo-build.log +6 -2
  2. package/dist/agentic/github-copilot/copilot-instructions.md.template +37 -0
  3. package/dist/agentic/github-copilot/instructions/entity-guard.instructions.md +27 -0
  4. package/dist/agentic/github-copilot/instructions/generated-guard.instructions.md +10 -0
  5. package/dist/agentic/github-copilot/mcp.json.example +13 -0
  6. package/dist/agentic/guides/module-facts.json +11527 -283
  7. package/dist/agentic/guides/module-facts.v2.json +11527 -283
  8. package/dist/agentic/guides/modules/agent_orchestrator/acl-features.md +31 -0
  9. package/dist/agentic/guides/modules/agent_orchestrator/active-extension-bindings.md +12 -0
  10. package/dist/agentic/guides/modules/agent_orchestrator/ai-agents.md +12 -0
  11. package/dist/agentic/guides/modules/agent_orchestrator/ai-tools-mcp-capabilities.md +13 -0
  12. package/dist/agentic/guides/modules/agent_orchestrator/backend-pages.md +28 -0
  13. package/dist/agentic/guides/modules/agent_orchestrator/cli-commands.md +14 -0
  14. package/dist/agentic/guides/modules/agent_orchestrator/contribution-resolutions.md +49 -0
  15. package/dist/agentic/guides/modules/agent_orchestrator/di-registrations-rich.md +42 -0
  16. package/dist/agentic/guides/modules/agent_orchestrator/di-service-tokens.md +24 -0
  17. package/dist/agentic/guides/modules/agent_orchestrator/domain-commands.md +28 -0
  18. package/dist/agentic/guides/modules/agent_orchestrator/encryption.md +20 -0
  19. package/dist/agentic/guides/modules/agent_orchestrator/entities.md +31 -0
  20. package/dist/agentic/guides/modules/agent_orchestrator/events.md +38 -0
  21. package/dist/agentic/guides/modules/agent_orchestrator/exact-override-targets.md +122 -0
  22. package/dist/agentic/guides/modules/agent_orchestrator/host-extension-points.md +10 -0
  23. package/dist/agentic/guides/modules/agent_orchestrator/index.md +31 -0
  24. package/dist/agentic/guides/modules/agent_orchestrator/owned-contract-module-metadata.md +11 -0
  25. package/dist/agentic/guides/modules/agent_orchestrator/setup.md +11 -0
  26. package/dist/agentic/guides/modules/agent_orchestrator/umes-contributions.md +49 -0
  27. package/dist/agentic/guides/modules/agent_orchestrator/umes-hosts.md +61 -0
  28. package/dist/agentic/guides/modules/agent_orchestrator/workers.md +14 -0
  29. package/dist/agentic/guides/modules/ai_assistant/di-registrations-rich.md +3 -2
  30. package/dist/agentic/guides/modules/ai_assistant/di-service-tokens.md +2 -1
  31. package/dist/agentic/guides/modules/ai_assistant/exact-override-targets.md +3 -2
  32. package/dist/agentic/guides/modules/ai_assistant/index.md +1 -1
  33. package/dist/agentic/guides/modules/api_docs/index.md +1 -1
  34. package/dist/agentic/guides/modules/api_keys/index.md +1 -1
  35. package/dist/agentic/guides/modules/attachments/index.md +1 -1
  36. package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
  37. package/dist/agentic/guides/modules/auth/index.md +1 -1
  38. package/dist/agentic/guides/modules/business_rules/index.md +1 -1
  39. package/dist/agentic/guides/modules/catalog/index.md +1 -1
  40. package/dist/agentic/guides/modules/channel_apns/index.md +1 -1
  41. package/dist/agentic/guides/modules/channel_discord/index.md +1 -1
  42. package/dist/agentic/guides/modules/channel_expo/index.md +1 -1
  43. package/dist/agentic/guides/modules/channel_fcm/index.md +1 -1
  44. package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
  45. package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
  46. package/dist/agentic/guides/modules/channel_resend/index.md +1 -1
  47. package/dist/agentic/guides/modules/channel_ses/index.md +1 -1
  48. package/dist/agentic/guides/modules/checkout/index.md +1 -1
  49. package/dist/agentic/guides/modules/communication_channels/index.md +1 -1
  50. package/dist/agentic/guides/modules/configs/index.md +1 -1
  51. package/dist/agentic/guides/modules/content/index.md +1 -1
  52. package/dist/agentic/guides/modules/currencies/index.md +1 -1
  53. package/dist/agentic/guides/modules/customer_accounts/cli-commands.md +11 -0
  54. package/dist/agentic/guides/modules/customer_accounts/exact-override-targets.md +5 -4
  55. package/dist/agentic/guides/modules/customer_accounts/index.md +3 -2
  56. package/dist/agentic/guides/modules/customer_accounts/setup.md +1 -1
  57. package/dist/agentic/guides/modules/customers/contribution-resolutions.md +1 -0
  58. package/dist/agentic/guides/modules/customers/exact-override-targets.md +1 -0
  59. package/dist/agentic/guides/modules/customers/incoming-installed-contributions.md +3 -0
  60. package/dist/agentic/guides/modules/customers/index.md +2 -2
  61. package/dist/agentic/guides/modules/customers/umes-contributions.md +1 -0
  62. package/dist/agentic/guides/modules/dashboards/index.md +1 -1
  63. package/dist/agentic/guides/modules/data_sync/index.md +1 -1
  64. package/dist/agentic/guides/modules/design_system/index.md +1 -1
  65. package/dist/agentic/guides/modules/devices/index.md +1 -1
  66. package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
  67. package/dist/agentic/guides/modules/directory/index.md +1 -1
  68. package/dist/agentic/guides/modules/documents/index.md +1 -1
  69. package/dist/agentic/guides/modules/entities/index.md +1 -1
  70. package/dist/agentic/guides/modules/eudr/index.md +1 -1
  71. package/dist/agentic/guides/modules/events/index.md +1 -1
  72. package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
  73. package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
  74. package/dist/agentic/guides/modules/generators/index.md +1 -1
  75. package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
  76. package/dist/agentic/guides/modules/integrations/index.md +1 -1
  77. package/dist/agentic/guides/modules/messages/index.md +1 -1
  78. package/dist/agentic/guides/modules/notifications/index.md +1 -1
  79. package/dist/agentic/guides/modules/onboarding/index.md +1 -1
  80. package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
  81. package/dist/agentic/guides/modules/perspectives/index.md +1 -1
  82. package/dist/agentic/guides/modules/phone_calls/index.md +1 -1
  83. package/dist/agentic/guides/modules/planner/index.md +1 -1
  84. package/dist/agentic/guides/modules/portal/index.md +1 -1
  85. package/dist/agentic/guides/modules/progress/index.md +1 -1
  86. package/dist/agentic/guides/modules/push_notifications/index.md +1 -1
  87. package/dist/agentic/guides/modules/query_index/index.md +1 -1
  88. package/dist/agentic/guides/modules/record_locks/index.md +1 -1
  89. package/dist/agentic/guides/modules/resources/index.md +1 -1
  90. package/dist/agentic/guides/modules/sales/index.md +1 -1
  91. package/dist/agentic/guides/modules/scheduler/index.md +1 -1
  92. package/dist/agentic/guides/modules/search/index.md +1 -1
  93. package/dist/agentic/guides/modules/security/index.md +1 -1
  94. package/dist/agentic/guides/modules/seeds/cli-commands.md +14 -0
  95. package/dist/agentic/guides/modules/seeds/exact-override-targets.md +14 -0
  96. package/dist/agentic/guides/modules/seeds/index.md +14 -0
  97. package/dist/agentic/guides/modules/seeds/owned-contract-module-metadata.md +11 -0
  98. package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
  99. package/dist/agentic/guides/modules/sso/index.md +1 -1
  100. package/dist/agentic/guides/modules/staff/index.md +1 -1
  101. package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
  102. package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
  103. package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
  104. package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
  105. package/dist/agentic/guides/modules/tillio/index.md +1 -1
  106. package/dist/agentic/guides/modules/translations/index.md +1 -1
  107. package/dist/agentic/guides/modules/warranty_claims/index.md +1 -1
  108. package/dist/agentic/guides/modules/webhooks/index.md +1 -1
  109. package/dist/agentic/guides/modules/wms/index.md +1 -1
  110. package/dist/agentic/guides/modules/workflows/acl-features.md +11 -1
  111. package/dist/agentic/guides/modules/workflows/active-extension-bindings.md +4 -1
  112. package/dist/agentic/guides/modules/workflows/ai-tools-mcp-capabilities.md +17 -0
  113. package/dist/agentic/guides/modules/workflows/backend-pages.md +3 -0
  114. package/dist/agentic/guides/modules/workflows/contribution-resolutions.md +24 -0
  115. package/dist/agentic/guides/modules/workflows/di-registrations-rich.md +11 -7
  116. package/dist/agentic/guides/modules/workflows/di-service-tokens.md +11 -7
  117. package/dist/agentic/guides/modules/workflows/domain-commands.md +11 -0
  118. package/dist/agentic/guides/modules/workflows/entities.md +2 -0
  119. package/dist/agentic/guides/modules/workflows/events.md +14 -8
  120. package/dist/agentic/guides/modules/workflows/exact-override-targets.md +45 -10
  121. package/dist/agentic/guides/modules/workflows/frontend-pages.md +2 -0
  122. package/dist/agentic/guides/modules/workflows/incoming-installed-contributions.md +17 -0
  123. package/dist/agentic/guides/modules/workflows/index.md +15 -12
  124. package/dist/agentic/guides/modules/workflows/notifications.md +2 -0
  125. package/dist/agentic/guides/modules/workflows/setup.md +1 -1
  126. package/dist/agentic/guides/modules/workflows/umes-contributions.md +24 -0
  127. package/dist/agentic/guides/modules/workflows/umes-hosts.md +14 -6
  128. package/dist/agentic/guides/modules/workflows/workers.md +4 -1
  129. package/dist/agentic/guides/reference-module-facts.json +2 -2
  130. package/dist/agentic/guides/reference-modules/example/index.md +1 -1
  131. package/dist/agentic/guides/upstream/AGENTS.md +3 -1
  132. package/dist/agentic/guides/upstream/manifest.json +2 -2
  133. package/dist/agentic/shared/ai/harness/design-system-inventory.json +118 -106
  134. package/dist/agentic/shared/ai/harness/source-link-inventory.json +8 -8
  135. package/dist/lib/agentic-init.js +4 -0
  136. package/dist/lib/agentic-init.js.map +2 -2
  137. package/dist/lib/agentic-setup.js +19 -5
  138. package/dist/lib/agentic-setup.js.map +2 -2
  139. package/dist/lib/generators/extensions/agent-files.js +913 -0
  140. package/dist/lib/generators/extensions/agent-files.js.map +7 -0
  141. package/dist/lib/generators/extensions/index.js +2 -0
  142. package/dist/lib/generators/extensions/index.js.map +2 -2
  143. package/dist/lib/generators/index.js +3 -1
  144. package/dist/lib/generators/index.js.map +2 -2
  145. package/dist/lib/generators/module-registry.js +40 -11
  146. package/dist/lib/generators/module-registry.js.map +2 -2
  147. package/dist/lib/generators/route-collisions.js +35 -0
  148. package/dist/lib/generators/route-collisions.js.map +7 -0
  149. package/dist/lib/generators/web-research-adapters.js +95 -0
  150. package/dist/lib/generators/web-research-adapters.js.map +7 -0
  151. package/dist/mercato.js +5 -2
  152. package/dist/mercato.js.map +2 -2
  153. package/package.json +6 -6
  154. package/src/__tests__/agent-files.test.ts +199 -0
  155. package/src/__tests__/mercato.test.ts +14 -0
  156. package/src/lib/__tests__/agentic-init.test.ts +11 -0
  157. package/src/lib/__tests__/agentic-setup.ownership.test.ts +5 -0
  158. package/src/lib/agentic-init.ts +4 -0
  159. package/src/lib/agentic-setup.ts +30 -5
  160. package/src/lib/generators/__tests__/agent-files-extension.test.ts +477 -0
  161. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +25 -8
  162. package/src/lib/generators/__tests__/route-collisions.test.ts +155 -0
  163. package/src/lib/generators/extensions/agent-files.ts +1332 -0
  164. package/src/lib/generators/extensions/index.ts +2 -0
  165. package/src/lib/generators/index.ts +1 -0
  166. package/src/lib/generators/module-registry.ts +66 -17
  167. package/src/lib/generators/route-collisions.ts +57 -0
  168. package/src/lib/generators/web-research-adapters.ts +133 -0
  169. package/src/mercato.ts +4 -1
@@ -0,0 +1,913 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { countTokens } from "@open-mercato/shared/lib/ai/token-count";
4
+ import { resolveStandaloneSourceMirrorBase } from "../scanner.js";
5
+ const FRONTMATTER_RE = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/;
6
+ const JSON_FENCE_RE = /```json\s*\n([\s\S]*?)\n```/;
7
+ const LIST_KEYS = ["tools", "skills", "subAgents"];
8
+ function stripQuotes(value) {
9
+ return value.trim().replace(/^['"]/, "").replace(/['"]$/, "").trim();
10
+ }
11
+ function parseInlineList(rawValue) {
12
+ return rawValue.replace(/^\[/, "").replace(/\]$/, "").split(",").map((entry) => stripQuotes(entry)).filter(Boolean);
13
+ }
14
+ function parseAgentMarkdown(raw) {
15
+ const match = FRONTMATTER_RE.exec(raw);
16
+ if (!match) return null;
17
+ const [, frontmatterBlock, body] = match;
18
+ const meta = { tools: [], skills: [], subAgents: [], instructions: body.trim() };
19
+ const lines = frontmatterBlock.split("\n");
20
+ let index = 0;
21
+ while (index < lines.length) {
22
+ const line = lines[index];
23
+ if (!line.trim()) {
24
+ index += 1;
25
+ continue;
26
+ }
27
+ const lineMatch = /^([A-Za-z0-9_]+):\s*(.*)$/.exec(line);
28
+ if (!lineMatch) {
29
+ index += 1;
30
+ continue;
31
+ }
32
+ const key = lineMatch[1];
33
+ const rawValue = lineMatch[2].trim();
34
+ if (LIST_KEYS.includes(key)) {
35
+ const listKey = key;
36
+ if (rawValue.startsWith("[")) {
37
+ meta[listKey] = parseInlineList(rawValue);
38
+ index += 1;
39
+ continue;
40
+ }
41
+ const items = [];
42
+ index += 1;
43
+ while (index < lines.length && /^\s*-\s+/.test(lines[index])) {
44
+ items.push(stripQuotes(lines[index].replace(/^\s*-\s+/, "")));
45
+ index += 1;
46
+ }
47
+ meta[listKey] = items.filter(Boolean);
48
+ continue;
49
+ }
50
+ if (key === "maxSteps") {
51
+ const parsed = Number.parseInt(stripQuotes(rawValue), 10);
52
+ if (!Number.isNaN(parsed)) meta.maxSteps = parsed;
53
+ index += 1;
54
+ continue;
55
+ }
56
+ if (key === "id" || key === "label" || key === "description" || key === "provider" || key === "model") {
57
+ meta[key] = stripQuotes(rawValue);
58
+ }
59
+ index += 1;
60
+ }
61
+ if (!meta.id || !meta.label || !meta.description) return null;
62
+ return meta;
63
+ }
64
+ function parseOutcomeKind(frontmatterBlock) {
65
+ for (const line of frontmatterBlock.split("\n")) {
66
+ const match = /^kind:\s*(.*)$/.exec(line.trim());
67
+ if (!match) continue;
68
+ const value = stripQuotes(match[1]);
69
+ if (value === "research" || value === "proposal" || value === "artifact") return value;
70
+ return null;
71
+ }
72
+ return null;
73
+ }
74
+ function parseOutcomeMarkdown(raw) {
75
+ const frontmatterMatch = FRONTMATTER_RE.exec(raw);
76
+ if (!frontmatterMatch) return null;
77
+ const [, frontmatterBlock, body] = frontmatterMatch;
78
+ const kind = parseOutcomeKind(frontmatterBlock);
79
+ if (!kind) return null;
80
+ const fenceMatch = JSON_FENCE_RE.exec(body);
81
+ if (!fenceMatch) {
82
+ return kind === "artifact" ? { kind, prose: body.trim() } : null;
83
+ }
84
+ let parsed;
85
+ try {
86
+ parsed = JSON.parse(fenceMatch[1]);
87
+ } catch {
88
+ return null;
89
+ }
90
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
91
+ const prose = body.slice(fenceMatch.index + fenceMatch[0].length).trim();
92
+ return { kind, schema: parsed, prose };
93
+ }
94
+ const OUTCOME_UNSUPPORTED_KEYWORDS = [
95
+ "oneOf",
96
+ "anyOf",
97
+ "allOf",
98
+ "not",
99
+ "$ref",
100
+ "format",
101
+ "patternProperties",
102
+ "pattern",
103
+ "additionalItems",
104
+ "propertyNames",
105
+ "if",
106
+ "then",
107
+ "else"
108
+ ];
109
+ const OUTCOME_SUPPORTED_TYPES = ["object", "array", "string", "number", "integer", "boolean"];
110
+ function assertOutcomeSchemaSupported(node, where, path2 = "$") {
111
+ if (typeof node !== "object" || node === null || Array.isArray(node)) {
112
+ throw new Error(`[internal] malformed OUTCOME.md at ${where}: schema node at ${path2} must be an object`);
113
+ }
114
+ const schema = node;
115
+ for (const keyword of OUTCOME_UNSUPPORTED_KEYWORDS) {
116
+ if (keyword in schema) {
117
+ throw new Error(`[internal] malformed OUTCOME.md at ${where}: unsupported keyword "${keyword}" at ${path2}`);
118
+ }
119
+ }
120
+ if ("const" in schema) return;
121
+ const type = schema.type;
122
+ if (typeof type !== "string" || !OUTCOME_SUPPORTED_TYPES.includes(type)) {
123
+ throw new Error(`[internal] malformed OUTCOME.md at ${where}: missing/unsupported "type" at ${path2}`);
124
+ }
125
+ if (type === "object" && schema.properties != null) {
126
+ if (typeof schema.properties !== "object" || schema.properties === null || Array.isArray(schema.properties)) {
127
+ throw new Error(`[internal] malformed OUTCOME.md at ${where}: "properties" at ${path2} must be an object`);
128
+ }
129
+ for (const [key, child] of Object.entries(schema.properties)) {
130
+ assertOutcomeSchemaSupported(child, where, `${path2}.${key}`);
131
+ }
132
+ }
133
+ if (type === "array") {
134
+ if (schema.items == null) {
135
+ throw new Error(`[internal] malformed OUTCOME.md at ${where}: array at ${path2} requires "items"`);
136
+ }
137
+ assertOutcomeSchemaSupported(schema.items, where, `${path2}[]`);
138
+ }
139
+ }
140
+ function sanitizeAgentName(id) {
141
+ return id.replace(/[^a-z0-9_-]/gi, "_");
142
+ }
143
+ function discoverSampleInput(dir) {
144
+ const samplePath = path.join(dir, "SAMPLE.json");
145
+ if (!fs.existsSync(samplePath)) return void 0;
146
+ try {
147
+ return JSON.parse(fs.readFileSync(samplePath, "utf8"));
148
+ } catch (err) {
149
+ const detail = err instanceof Error ? err.message : String(err);
150
+ throw new Error(`[internal] malformed SAMPLE.json at ${dir}: ${detail}`);
151
+ }
152
+ }
153
+ const FACT_SOURCES = ["input", "payload", "output"];
154
+ const FACT_FORMATS = ["text", "number", "boolean", "percent"];
155
+ function discoverFacts(dir) {
156
+ const factsPath = path.join(dir, "FACTS.json");
157
+ if (!fs.existsSync(factsPath)) return void 0;
158
+ let parsed;
159
+ try {
160
+ parsed = JSON.parse(fs.readFileSync(factsPath, "utf8"));
161
+ } catch (err) {
162
+ const detail = err instanceof Error ? err.message : String(err);
163
+ throw new Error(`[internal] malformed FACTS.json at ${dir}: ${detail}`);
164
+ }
165
+ const entries = Array.isArray(parsed) ? parsed : parsed && typeof parsed === "object" && Array.isArray(parsed.facts) ? parsed.facts : null;
166
+ if (!entries) {
167
+ throw new Error(`[internal] malformed FACTS.json at ${dir}: expected an array or { "facts": [...] }`);
168
+ }
169
+ return entries.map((entry, index) => {
170
+ const fact = entry;
171
+ if (!fact || typeof fact.label !== "string" || !fact.label.trim() || typeof fact.path !== "string" || !fact.path.trim() || !FACT_SOURCES.includes(fact.source)) {
172
+ throw new Error(
173
+ `[internal] malformed FACTS.json at ${dir}: entry ${index} needs label (string), source (${FACT_SOURCES.join("|")}), path (string)`
174
+ );
175
+ }
176
+ if (fact.format !== void 0 && !FACT_FORMATS.includes(fact.format)) {
177
+ throw new Error(
178
+ `[internal] malformed FACTS.json at ${dir}: entry ${index} format must be one of ${FACT_FORMATS.join("|")}`
179
+ );
180
+ }
181
+ return {
182
+ label: fact.label.trim(),
183
+ source: fact.source,
184
+ path: fact.path.trim(),
185
+ ...fact.format !== void 0 ? { format: fact.format } : {}
186
+ };
187
+ });
188
+ }
189
+ function sanitizeSkillName(id) {
190
+ const name = id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
191
+ return name || "skill";
192
+ }
193
+ function parseSkillMarkdown(raw) {
194
+ const match = FRONTMATTER_RE.exec(raw);
195
+ if (!match) return null;
196
+ const [, frontmatterBlock, body] = match;
197
+ const meta = { tools: [] };
198
+ const lines = frontmatterBlock.split("\n");
199
+ let index = 0;
200
+ while (index < lines.length) {
201
+ const line = lines[index];
202
+ if (!line.trim()) {
203
+ index += 1;
204
+ continue;
205
+ }
206
+ const lineMatch = /^([A-Za-z0-9_]+):\s*(.*)$/.exec(line);
207
+ if (!lineMatch) {
208
+ index += 1;
209
+ continue;
210
+ }
211
+ const key = lineMatch[1];
212
+ const rawValue = lineMatch[2].trim();
213
+ if (key === "tools") {
214
+ if (rawValue.startsWith("[")) {
215
+ meta.tools = parseInlineList(rawValue);
216
+ index += 1;
217
+ continue;
218
+ }
219
+ const items = [];
220
+ index += 1;
221
+ while (index < lines.length && /^\s*-\s+/.test(lines[index])) {
222
+ items.push(stripQuotes(lines[index].replace(/^\s*-\s+/, "")));
223
+ index += 1;
224
+ }
225
+ meta.tools = items.filter(Boolean);
226
+ continue;
227
+ }
228
+ if (key === "id" || key === "label" || key === "description") {
229
+ meta[key] = stripQuotes(rawValue);
230
+ }
231
+ index += 1;
232
+ }
233
+ return { meta, body: body.trim() };
234
+ }
235
+ function listExampleBodies(skillDir) {
236
+ const examplesDir = path.join(skillDir, "examples");
237
+ if (!fs.existsSync(examplesDir) || !fs.statSync(examplesDir).isDirectory()) return [];
238
+ return fs.readdirSync(examplesDir).filter((name) => name.endsWith(".md")).sort((a, b) => a.localeCompare(b)).map((name) => fs.readFileSync(path.join(examplesDir, name), "utf8").trim()).filter(Boolean);
239
+ }
240
+ function validateScriptSource(file, source) {
241
+ const definesRun = /(^|\b)(async\s+)?function\s+run\b/.test(source) || /\brun\s*=/.test(source);
242
+ if (!definesRun) {
243
+ throw new Error(
244
+ `[internal] malformed agent script at ${file}: must define a \`run(args)\` function`
245
+ );
246
+ }
247
+ }
248
+ function listScripts(scriptsDir) {
249
+ if (!fs.existsSync(scriptsDir) || !fs.statSync(scriptsDir).isDirectory()) return [];
250
+ const scripts = [];
251
+ for (const name of fs.readdirSync(scriptsDir).filter((entry) => entry.endsWith(".ts") || entry.endsWith(".js")).sort((a, b) => a.localeCompare(b))) {
252
+ const file = path.join(scriptsDir, name);
253
+ const source = fs.readFileSync(file, "utf8");
254
+ validateScriptSource(file, source);
255
+ scripts.push({ name: name.replace(/\.(ts|js)$/, ""), source });
256
+ }
257
+ return scripts;
258
+ }
259
+ const AGENT_TOOLS_SKILL_ID = "__agent_tools__";
260
+ const TOOL_REF_RE = /^\s*\/\/\s*@ref:?\s+(\S+)/;
261
+ function discoverToolFiles(agentDir) {
262
+ const toolsBase = path.join(agentDir, "tools");
263
+ if (!fs.existsSync(toolsBase) || !fs.statSync(toolsBase).isDirectory()) {
264
+ return { refs: [], scripts: [] };
265
+ }
266
+ const refs = [];
267
+ const scripts = [];
268
+ for (const name of fs.readdirSync(toolsBase).filter((entry) => entry.endsWith(".ts") || entry.endsWith(".js")).sort((a, b) => a.localeCompare(b))) {
269
+ const file = path.join(toolsBase, name);
270
+ const source = fs.readFileSync(file, "utf8");
271
+ const firstLine = source.split("\n", 1)[0] ?? "";
272
+ const refMatch = TOOL_REF_RE.exec(firstLine);
273
+ if (refMatch) {
274
+ refs.push(refMatch[1]);
275
+ continue;
276
+ }
277
+ validateScriptSource(file, source);
278
+ scripts.push({ name: name.replace(/\.(ts|js)$/, ""), source });
279
+ }
280
+ return { refs, scripts };
281
+ }
282
+ function discoverAgentSkills(agentDir, skillIds) {
283
+ if (skillIds.length === 0) return [];
284
+ const skillsBase = path.join(agentDir, "skills");
285
+ if (!fs.existsSync(skillsBase) || !fs.statSync(skillsBase).isDirectory()) return [];
286
+ const bySkillId = /* @__PURE__ */ new Map();
287
+ for (const entry of fs.readdirSync(skillsBase, { withFileTypes: true })) {
288
+ if (!entry.isDirectory()) continue;
289
+ const skillDir = path.join(skillsBase, entry.name);
290
+ const skillPath = path.join(skillDir, "SKILL.md");
291
+ if (!fs.existsSync(skillPath)) continue;
292
+ const parsed = parseSkillMarkdown(fs.readFileSync(skillPath, "utf8"));
293
+ if (!parsed) {
294
+ throw new Error(`[internal] malformed SKILL.md at ${skillDir}: missing frontmatter block`);
295
+ }
296
+ const id = parsed.meta.id || entry.name;
297
+ const templatePath = path.join(skillDir, "TEMPLATE.md");
298
+ const template = fs.existsSync(templatePath) ? fs.readFileSync(templatePath, "utf8").trim() || void 0 : void 0;
299
+ bySkillId.set(id, {
300
+ id,
301
+ description: parsed.meta.description ?? "",
302
+ instructions: parsed.body,
303
+ template,
304
+ examples: listExampleBodies(skillDir),
305
+ tools: parsed.meta.tools,
306
+ scripts: listScripts(path.join(skillDir, "scripts")),
307
+ openCodeSkillName: sanitizeSkillName(id)
308
+ });
309
+ }
310
+ const resolved = [];
311
+ for (const skillId of skillIds) {
312
+ const skill = bySkillId.get(skillId);
313
+ if (skill) resolved.push(skill);
314
+ }
315
+ return resolved;
316
+ }
317
+ function discoverSubAgents(agentDir) {
318
+ const base = path.join(agentDir, "sub-agents");
319
+ if (!fs.existsSync(base) || !fs.statSync(base).isDirectory()) return [];
320
+ const subAgents = [];
321
+ for (const entry of fs.readdirSync(base, { withFileTypes: true })) {
322
+ if (!entry.isDirectory() || entry.name === "__tests__") continue;
323
+ const dir = path.join(base, entry.name);
324
+ const agentMdPath = path.join(dir, "AGENT.md");
325
+ const outcomePath = path.join(dir, "OUTCOME.md");
326
+ if (!fs.existsSync(agentMdPath) || !fs.existsSync(outcomePath)) {
327
+ throw new Error(
328
+ `[internal] malformed sub-agent at ${dir}: both AGENT.md and OUTCOME.md are required`
329
+ );
330
+ }
331
+ const agent = parseAgentMarkdown(fs.readFileSync(agentMdPath, "utf8"));
332
+ if (!agent) {
333
+ throw new Error(`[internal] malformed AGENT.md at ${dir}: missing id/label/description`);
334
+ }
335
+ const outcome = parseOutcomeMarkdown(fs.readFileSync(outcomePath, "utf8"));
336
+ if (!outcome) {
337
+ throw new Error(`[internal] malformed OUTCOME.md at ${dir}: missing kind or JSON-Schema block`);
338
+ }
339
+ if (outcome.schema) assertOutcomeSchemaSupported(outcome.schema, dir);
340
+ if (outcome.kind !== "research") {
341
+ throw new Error(
342
+ `[internal] sub-agent at ${dir} must be research (kind: research); only the primary proposes`
343
+ );
344
+ }
345
+ if (agent.subAgents.length > 0) {
346
+ throw new Error(
347
+ `[internal] sub-agent at ${dir} may not declare subAgents (depth cap = 1); sub-agents may not delegate further`
348
+ );
349
+ }
350
+ const skillsContent = discoverAgentSkills(dir, agent.skills);
351
+ const skillTools = skillsContent.flatMap((skill) => skill.tools);
352
+ const toolFiles = discoverToolFiles(dir);
353
+ const effectiveSkillsContent = toolFiles.scripts.length > 0 ? [
354
+ ...skillsContent,
355
+ {
356
+ id: AGENT_TOOLS_SKILL_ID,
357
+ description: "",
358
+ instructions: "",
359
+ examples: [],
360
+ tools: [],
361
+ scripts: toolFiles.scripts,
362
+ openCodeSkillName: sanitizeSkillName(AGENT_TOOLS_SKILL_ID)
363
+ }
364
+ ] : skillsContent;
365
+ const effectiveTools = Array.from(
366
+ /* @__PURE__ */ new Set([...agent.tools, ...skillTools, ...toolFiles.refs])
367
+ );
368
+ subAgents.push({
369
+ moduleId: "",
370
+ dir,
371
+ id: agent.id,
372
+ label: agent.label,
373
+ description: agent.description,
374
+ instructions: agent.instructions,
375
+ resultKind: outcome.kind,
376
+ outcomeSchema: outcome.schema,
377
+ outcomeProse: outcome.prose,
378
+ tools: effectiveTools,
379
+ skills: agent.skills,
380
+ subAgents: [],
381
+ openCodeAgentName: sanitizeAgentName(agent.id),
382
+ skillsContent: effectiveSkillsContent,
383
+ subAgentsContent: [],
384
+ mode: "subagent",
385
+ maxSteps: agent.maxSteps,
386
+ provider: agent.provider,
387
+ model: agent.model,
388
+ sampleInput: discoverSampleInput(dir),
389
+ facts: discoverFacts(dir),
390
+ tokenUsage: discoverAgentTokenUsage(dir),
391
+ sourceFiles: collectAgentFiles(dir)
392
+ });
393
+ }
394
+ return subAgents;
395
+ }
396
+ const TOKEN_TOOL_EXTENSIONS = [".ts", ".js"];
397
+ function toPosixRelativePath(relativePath) {
398
+ return relativePath.split(path.sep).join("/");
399
+ }
400
+ function tokenCountFile(agentDir, relativePath) {
401
+ const absolute = path.join(agentDir, relativePath);
402
+ if (!fs.existsSync(absolute) || !fs.statSync(absolute).isFile()) return null;
403
+ return { path: toPosixRelativePath(relativePath), tokens: countTokens(fs.readFileSync(absolute, "utf8")) };
404
+ }
405
+ function tokenListFiles(dir, extensions) {
406
+ if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) return [];
407
+ return fs.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name).filter((name) => !extensions || extensions.includes(path.extname(name))).sort((a, b) => a.localeCompare(b));
408
+ }
409
+ function tokenListDirs(dir) {
410
+ if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) return [];
411
+ return fs.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name !== "__tests__").map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
412
+ }
413
+ function tokenComputeSkills(agentDir) {
414
+ return tokenListDirs(path.join(agentDir, "skills")).map((skillId) => {
415
+ const skillRel = path.join("skills", skillId);
416
+ const files = [];
417
+ for (const name of ["SKILL.md", "TEMPLATE.md"]) {
418
+ const file = tokenCountFile(agentDir, path.join(skillRel, name));
419
+ if (file) files.push(file);
420
+ }
421
+ for (const subdir of ["examples", "scripts"]) {
422
+ for (const name of tokenListFiles(path.join(agentDir, skillRel, subdir))) {
423
+ const file = tokenCountFile(agentDir, path.join(skillRel, subdir, name));
424
+ if (file) files.push(file);
425
+ }
426
+ }
427
+ return { id: skillId, tokens: files.reduce((sum, f) => sum + f.tokens, 0), files };
428
+ });
429
+ }
430
+ function tokenComputeTools(agentDir) {
431
+ return tokenListFiles(path.join(agentDir, "tools"), TOKEN_TOOL_EXTENSIONS).map((name) => {
432
+ const rel = path.join("tools", name);
433
+ return {
434
+ name: name.replace(/\.[^.]+$/, ""),
435
+ path: toPosixRelativePath(rel),
436
+ tokens: countTokens(fs.readFileSync(path.join(agentDir, rel), "utf8"))
437
+ };
438
+ });
439
+ }
440
+ function discoverAgentTokenUsage(agentDir, depth = 0) {
441
+ const agent = tokenCountFile(agentDir, "AGENT.md")?.tokens ?? 0;
442
+ const outcome = tokenCountFile(agentDir, "OUTCOME.md")?.tokens ?? 0;
443
+ const skills = tokenComputeSkills(agentDir);
444
+ const tools = tokenComputeTools(agentDir);
445
+ const subAgents = [];
446
+ if (depth === 0) {
447
+ for (const subId of tokenListDirs(path.join(agentDir, "sub-agents"))) {
448
+ const nested = discoverAgentTokenUsage(path.join(agentDir, "sub-agents", subId), depth + 1);
449
+ subAgents.push({ id: subId, tokens: nested.total });
450
+ }
451
+ }
452
+ const self = agent + outcome + skills.reduce((sum, s) => sum + s.tokens, 0) + tools.reduce((sum, t) => sum + t.tokens, 0);
453
+ const total = self + subAgents.reduce((sum, s) => sum + s.tokens, 0);
454
+ return { total, self, agent, outcome, skills, tools, subAgents };
455
+ }
456
+ const AGENT_AUX_FILES = ["SAMPLE.json", "FACTS.json"];
457
+ function collectAgentFiles(agentDir, prefix = "", depth = 0) {
458
+ const files = [];
459
+ const add = (relativePath, inContext) => {
460
+ const absolute = path.join(agentDir, relativePath);
461
+ if (!fs.existsSync(absolute) || !fs.statSync(absolute).isFile()) return;
462
+ const content = fs.readFileSync(absolute, "utf8");
463
+ files.push({ path: toPosixRelativePath(path.join(prefix, relativePath)), content, tokens: countTokens(content), inContext });
464
+ };
465
+ add("AGENT.md", true);
466
+ add("OUTCOME.md", true);
467
+ for (const name of AGENT_AUX_FILES) add(name, false);
468
+ for (const skillId of tokenListDirs(path.join(agentDir, "skills"))) {
469
+ const skillRel = path.join("skills", skillId);
470
+ for (const name of ["SKILL.md", "TEMPLATE.md"]) add(path.join(skillRel, name), true);
471
+ for (const subdir of ["examples", "scripts"]) {
472
+ for (const name of tokenListFiles(path.join(agentDir, skillRel, subdir))) {
473
+ add(path.join(skillRel, subdir, name), true);
474
+ }
475
+ }
476
+ }
477
+ for (const name of tokenListFiles(path.join(agentDir, "tools"), TOKEN_TOOL_EXTENSIONS)) {
478
+ add(path.join("tools", name), true);
479
+ }
480
+ if (depth === 0) {
481
+ for (const subId of tokenListDirs(path.join(agentDir, "sub-agents"))) {
482
+ files.push(
483
+ ...collectAgentFiles(
484
+ path.join(agentDir, "sub-agents", subId),
485
+ path.join(prefix, "sub-agents", subId),
486
+ depth + 1
487
+ )
488
+ );
489
+ }
490
+ }
491
+ return files;
492
+ }
493
+ function renderOpenCodeSkillFile(skill) {
494
+ const frontmatter = [
495
+ "---",
496
+ `name: ${skill.openCodeSkillName}`,
497
+ `description: ${JSON.stringify(skill.description)}`,
498
+ "---"
499
+ ];
500
+ return `${frontmatter.join("\n")}
501
+ ${skill.instructions.trim()}
502
+ `;
503
+ }
504
+ function findRepoRoot(start) {
505
+ let current = path.resolve(start);
506
+ for (let depth = 0; depth < 40; depth += 1) {
507
+ if (fs.existsSync(path.join(current, "docker", "opencode")) && fs.existsSync(path.join(current, "packages"))) {
508
+ return current;
509
+ }
510
+ const parent = path.dirname(current);
511
+ if (parent === current) break;
512
+ current = parent;
513
+ }
514
+ return null;
515
+ }
516
+ function listAgentDirs(agentsBase) {
517
+ if (!fs.existsSync(agentsBase)) return [];
518
+ return fs.readdirSync(agentsBase, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name !== "__tests__").map((entry) => path.join(agentsBase, entry.name));
519
+ }
520
+ function renderToolPermissionLine(toolName) {
521
+ return ` ${JSON.stringify(toolName)}: true`;
522
+ }
523
+ const ORCHESTRATOR_MODULE_ID = "agent_orchestrator";
524
+ const OPENCODE_MCP_SERVER_KEY = "open-mercato";
525
+ const CORE_FILE_AGENT_TOOL_IDS = [
526
+ "agent_orchestrator.submit_outcome",
527
+ "agent_orchestrator.load_skill",
528
+ "agent_orchestrator.run_skill_script"
529
+ ];
530
+ const DELEGATE_AGENT_TOOL_ID = "agent_orchestrator.delegate_agent";
531
+ function toOpenCodeMcpToolId(omToolId) {
532
+ return `${OPENCODE_MCP_SERVER_KEY}_${omToolId.replace(/\./g, "_")}`;
533
+ }
534
+ function renderOpenCodeAgentFile(agent) {
535
+ const subAgentIds = agent.mode === "primary" ? agent.subAgentsContent.map((sub) => sub.id) : [];
536
+ const hasSubAgents = subAgentIds.length > 0;
537
+ const omMcpToolIds = Array.from(
538
+ /* @__PURE__ */ new Set([
539
+ ...agent.tools,
540
+ ...CORE_FILE_AGENT_TOOL_IDS,
541
+ ...hasSubAgents ? [DELEGATE_AGENT_TOOL_ID] : []
542
+ ])
543
+ );
544
+ const allowedTools = omMcpToolIds.map(toOpenCodeMcpToolId);
545
+ const modelLine = agent.provider && agent.model ? `model: ${agent.provider}/${agent.model}` : agent.model ? `model: ${agent.model}` : null;
546
+ const frontmatterLines = [
547
+ "---",
548
+ `description: ${JSON.stringify(agent.description)}`,
549
+ ...modelLine ? [modelLine] : [],
550
+ `mode: ${agent.mode}`,
551
+ "tools:",
552
+ ' "*": false',
553
+ ...allowedTools.map(renderToolPermissionLine),
554
+ "permission:",
555
+ " write: deny",
556
+ " edit: deny",
557
+ " bash: deny",
558
+ " task: deny",
559
+ "---"
560
+ ];
561
+ const terminalInstruction = `Finish by calling the \`${toOpenCodeMcpToolId("agent_orchestrator.submit_outcome")}\` tool with a value matching the outcome contract (pass it as the \`outcome\` argument). You MUST call the tool \u2014 do not answer in prose or emit the result as a code block.`;
562
+ const delegateToolName = toOpenCodeMcpToolId(DELEGATE_AGENT_TOOL_ID);
563
+ const subAgentSection = hasSubAgents ? `## Sub-agents
564
+ Delegate a sub-task by calling the \`${delegateToolName}\` tool with \`{ agentId: "<sub-agent id>", input: <sub-task input object> }\`. Issue multiple \`${delegateToolName}\` calls in the SAME step to fan out in parallel, then combine their results before submitting your outcome. Available sub-agents: ${subAgentIds.join(", ")}.` : null;
565
+ const outcomeTarget = agent.resultKind === "proposal" ? "the `proposal` object" : "the `data` object";
566
+ const outcomeSection = agent.resultKind === "artifact" ? [
567
+ "## Outcome contract",
568
+ "Write the files you produce into the run's `out/` directory, then pass this shape as the `outcome` argument of the submit_outcome tool:",
569
+ "",
570
+ "```json",
571
+ JSON.stringify(
572
+ {
573
+ artifacts: [
574
+ { fileName: "report.pdf", mimeType: "application/pdf", caption: "What this file is" }
575
+ ],
576
+ summary: "One sentence about what you produced."
577
+ },
578
+ null,
579
+ 2
580
+ ),
581
+ "```",
582
+ ...agent.outcomeProse ? ["", agent.outcomeProse] : []
583
+ ].join("\n") : [
584
+ "## Outcome contract",
585
+ `Your result MUST match this JSON Schema (${outcomeTarget}). Pass it as the \`outcome\` argument of the submit_outcome tool, as a JSON object (not a string):`,
586
+ "",
587
+ "```json",
588
+ JSON.stringify(agent.outcomeSchema, null, 2),
589
+ "```",
590
+ ...agent.outcomeProse ? ["", agent.outcomeProse] : []
591
+ ].join("\n");
592
+ const body = [
593
+ agent.instructions.trim(),
594
+ ...subAgentSection ? [subAgentSection] : [],
595
+ outcomeSection,
596
+ terminalInstruction
597
+ ].filter(Boolean).join("\n\n");
598
+ return `${frontmatterLines.join("\n")}
599
+ ${body}
600
+ `;
601
+ }
602
+ function renderDescriptor(agent, indent) {
603
+ const optional = [];
604
+ if (agent.subAgentsContent.length > 0) {
605
+ const nested = agent.subAgentsContent.map((sub) => renderDescriptor(sub, `${indent} `)).join("\n");
606
+ optional.push(`${indent} subAgentDescriptors: [
607
+ ${nested}
608
+ ${indent} ],`);
609
+ }
610
+ if (agent.maxSteps != null) optional.push(`${indent} maxSteps: ${agent.maxSteps},`);
611
+ if (agent.provider != null) optional.push(`${indent} provider: ${JSON.stringify(agent.provider)},`);
612
+ if (agent.model != null) optional.push(`${indent} model: ${JSON.stringify(agent.model)},`);
613
+ if (agent.sampleInput !== void 0) {
614
+ optional.push(`${indent} sampleInput: ${JSON.stringify(agent.sampleInput)},`);
615
+ }
616
+ if (agent.facts !== void 0) {
617
+ optional.push(`${indent} facts: ${JSON.stringify(agent.facts)},`);
618
+ }
619
+ const skillsContent = agent.skillsContent.map((skill) => ({
620
+ id: skill.id,
621
+ instructions: skill.instructions,
622
+ ...skill.template != null ? { template: skill.template } : {},
623
+ examples: skill.examples,
624
+ tools: skill.tools,
625
+ ...skill.scripts.length > 0 ? { scripts: skill.scripts } : {}
626
+ }));
627
+ return [
628
+ `${indent}{`,
629
+ `${indent} id: ${JSON.stringify(agent.id)},`,
630
+ `${indent} moduleId: ${JSON.stringify(agent.moduleId)},`,
631
+ `${indent} label: ${JSON.stringify(agent.label)},`,
632
+ `${indent} description: ${JSON.stringify(agent.description)},`,
633
+ `${indent} instructions: ${JSON.stringify(agent.instructions)},`,
634
+ `${indent} resultKind: ${JSON.stringify(agent.resultKind)},`,
635
+ ...agent.outcomeSchema ? [`${indent} outcomeSchema: ${JSON.stringify(agent.outcomeSchema)},`] : [],
636
+ `${indent} tools: ${JSON.stringify(agent.tools)},`,
637
+ `${indent} skills: ${JSON.stringify(agent.skills)},`,
638
+ `${indent} subAgents: ${JSON.stringify(agent.subAgents)},`,
639
+ `${indent} openCodeAgentName: ${JSON.stringify(agent.openCodeAgentName)},`,
640
+ `${indent} skillsContent: ${JSON.stringify(skillsContent)},`,
641
+ `${indent} tokenUsage: ${JSON.stringify(agent.tokenUsage)},`,
642
+ `${indent} sourceFiles: ${JSON.stringify(agent.sourceFiles)},`,
643
+ ...optional,
644
+ `${indent}},`
645
+ ].join("\n");
646
+ }
647
+ function renderManifest(agents) {
648
+ const descriptors = agents.map((agent) => renderDescriptor(agent, " ")).join("\n");
649
+ return `// AUTO-GENERATED by mercato generate registry \u2014 DO NOT EDIT BY HAND.
650
+ //
651
+ // Committed, generator-owned manifest of file-defined (OpenCode) agents,
652
+ // discovered from \`agents/<id>/\` directories across every enabled module. It
653
+ // stores PLAIN data (raw JSON-Schema, not a Zod instance) so this file is pure
654
+ // data and travels with the repo (survives \`yarn clean-generated\`).
655
+ // \`ensureAgentsLoaded()\` recompiles each \`outcomeSchema\` to Zod via
656
+ // \`compileOutcome\` at load time and registers it with \`runtime:'opencode'\`.
657
+ //
658
+ // Regenerate with \`yarn generate\`.
659
+ import type { JsonSchemaNode, OutcomeKind } from "../lib/sdk/outcomeSchema.js"
660
+ import type { AgentTokenUsage, FileAgentFile } from "../lib/tokens/types.js"
661
+
662
+ export type FileAgentScript = {
663
+ name: string
664
+ source: string
665
+ }
666
+
667
+ export type FileAgentSkillContent = {
668
+ id: string
669
+ instructions: string
670
+ template?: string
671
+ examples: string[]
672
+ tools: string[]
673
+ /**
674
+ * Sandboxed helper scripts (Phase 5). Carried as plain source; run server-side
675
+ * in the Code Mode \`isolated-vm\` sandbox via the \`run_skill_script\` MCP tool.
676
+ * Never copied to the OpenCode container. The synthetic skill id
677
+ * \`__agent_tools__\` carries an agent's LOCAL \`tools/*.ts\` sources.
678
+ */
679
+ scripts?: FileAgentScript[]
680
+ }
681
+
682
+ export type FileAgentFact = {
683
+ /** Human label shown in the Caseload facts grid. */
684
+ label: string
685
+ /** Where to resolve the value: run input, proposal payload, or run output. */
686
+ source: 'input' | 'payload' | 'output'
687
+ /** Dot-path into the source (array indexes allowed), e.g. "actions.0.payload.amount". */
688
+ path: string
689
+ format?: 'text' | 'number' | 'boolean' | 'percent'
690
+ }
691
+
692
+ export type FileAgentDescriptor = {
693
+ id: string
694
+ moduleId: string
695
+ label: string
696
+ description: string
697
+ instructions: string
698
+ resultKind: OutcomeKind
699
+ outcomeSchema?: JsonSchemaNode
700
+ tools: string[]
701
+ skills: string[]
702
+ subAgents: string[]
703
+ openCodeAgentName: string
704
+ skillsContent?: FileAgentSkillContent[]
705
+ /**
706
+ * Baked token-usage estimate of the agent's construction files (AGENT.md,
707
+ * OUTCOME.md, skills, tools, sub-agents), counted with the shared
708
+ * \`o200k_base\` tokenizer. Surfaced on the Agent detail page and the
709
+ * \`agent_orchestrator token-usage\` CLI. An estimate, not an exact count.
710
+ */
711
+ tokenUsage?: AgentTokenUsage
712
+ /**
713
+ * Baked raw content of every construction file (AGENT.md, OUTCOME.md, skills,
714
+ * tools, sub-agents, and auxiliary SAMPLE.json/FACTS.json), for the read-only
715
+ * Files tab. Each carries its \`o200k_base\` token count; paths are relative to
716
+ * the agent root (sub-agent files prefixed \`sub-agents/<id>/\`). Baked so the
717
+ * runtime never reads agent dirs from disk.
718
+ */
719
+ sourceFiles?: FileAgentFile[]
720
+ /**
721
+ * Nested descriptors for this agent's sub-agents (Phase 4). Each is an
722
+ * research-kind, non-delegating file agent registered individually (depth cap =
723
+ * 1). Absent for agents without sub-agents and for sub-agents themselves.
724
+ */
725
+ subAgentDescriptors?: FileAgentDescriptor[]
726
+ maxSteps?: number
727
+ provider?: string
728
+ model?: string
729
+ /**
730
+ * Optional example \`input\` for the Playground "Insert sample" button, read
731
+ * from \`agents/<id>/SAMPLE.json\`. Any JSON value the agent accepts as input.
732
+ */
733
+ sampleInput?: unknown
734
+ /**
735
+ * Optional fact declarations for the Caseload decision panel, read from
736
+ * \`agents/<id>/FACTS.json\`. Each maps a labelled dot-path into the run
737
+ * input / proposal payload / run output.
738
+ */
739
+ facts?: FileAgentFact[]
740
+ }
741
+
742
+ export const fileAgentDescriptors: FileAgentDescriptor[] = [${descriptors ? `
743
+ ${descriptors}
744
+ ` : ""}]
745
+ `;
746
+ }
747
+ function createAgentFilesExtension() {
748
+ const discovered = [];
749
+ const seenIds = /* @__PURE__ */ new Set();
750
+ let repoRoot = null;
751
+ let sawOrchestratorModule = false;
752
+ function scanAgentsTree(moduleId, baseDir) {
753
+ for (const dir of listAgentDirs(baseDir)) {
754
+ const agentMdPath = path.join(dir, "AGENT.md");
755
+ const outcomePath = path.join(dir, "OUTCOME.md");
756
+ const hasAgentMd = fs.existsSync(agentMdPath);
757
+ const hasOutcome = fs.existsSync(outcomePath);
758
+ if (!hasAgentMd && !hasOutcome) continue;
759
+ if (!hasAgentMd || !hasOutcome) {
760
+ throw new Error(
761
+ `[internal] malformed file agent at ${dir}: both AGENT.md and OUTCOME.md are required`
762
+ );
763
+ }
764
+ const agent = parseAgentMarkdown(fs.readFileSync(agentMdPath, "utf8"));
765
+ if (!agent) {
766
+ throw new Error(`[internal] malformed AGENT.md at ${dir}: missing id/label/description`);
767
+ }
768
+ const outcome = parseOutcomeMarkdown(fs.readFileSync(outcomePath, "utf8"));
769
+ if (!outcome) {
770
+ throw new Error(`[internal] malformed OUTCOME.md at ${dir}: missing kind or JSON-Schema block`);
771
+ }
772
+ if (outcome.schema) assertOutcomeSchemaSupported(outcome.schema, dir);
773
+ if (!/^[a-z0-9][a-z0-9._-]*$/.test(agent.id)) {
774
+ throw new Error(
775
+ `[internal] invalid agent id "${agent.id}" at ${dir}: use lowercase [a-z0-9._-] (e.g. "module.agent")`
776
+ );
777
+ }
778
+ if (seenIds.has(agent.id)) {
779
+ console.warn(`[internal] duplicate file-agent id "${agent.id}" at ${dir}; keeping the first, skipping this one.`);
780
+ continue;
781
+ }
782
+ seenIds.add(agent.id);
783
+ const skillsContent = discoverAgentSkills(dir, agent.skills);
784
+ const skillTools = skillsContent.flatMap((skill) => skill.tools);
785
+ const toolFiles = discoverToolFiles(dir);
786
+ const effectiveSkillsContent = toolFiles.scripts.length > 0 ? [
787
+ ...skillsContent,
788
+ {
789
+ id: AGENT_TOOLS_SKILL_ID,
790
+ description: "",
791
+ instructions: "",
792
+ examples: [],
793
+ tools: [],
794
+ scripts: toolFiles.scripts,
795
+ openCodeSkillName: sanitizeSkillName(AGENT_TOOLS_SKILL_ID)
796
+ }
797
+ ] : skillsContent;
798
+ const effectiveTools = Array.from(
799
+ /* @__PURE__ */ new Set([...agent.tools, ...skillTools, ...toolFiles.refs])
800
+ );
801
+ const subAgentsContent = discoverSubAgents(dir);
802
+ discovered.push({
803
+ moduleId,
804
+ dir,
805
+ id: agent.id,
806
+ label: agent.label,
807
+ description: agent.description,
808
+ instructions: agent.instructions,
809
+ resultKind: outcome.kind,
810
+ outcomeSchema: outcome.schema,
811
+ outcomeProse: outcome.prose,
812
+ tools: effectiveTools,
813
+ skills: agent.skills,
814
+ subAgents: agent.subAgents,
815
+ openCodeAgentName: sanitizeAgentName(agent.id),
816
+ skillsContent: effectiveSkillsContent,
817
+ subAgentsContent,
818
+ mode: "primary",
819
+ maxSteps: agent.maxSteps,
820
+ provider: agent.provider,
821
+ model: agent.model,
822
+ sampleInput: discoverSampleInput(dir),
823
+ facts: discoverFacts(dir),
824
+ tokenUsage: discoverAgentTokenUsage(dir),
825
+ sourceFiles: collectAgentFiles(dir)
826
+ });
827
+ }
828
+ }
829
+ return {
830
+ id: "registry.agent-files",
831
+ outputFiles: [],
832
+ scanModule(ctx) {
833
+ if (ctx.moduleId === ORCHESTRATOR_MODULE_ID) sawOrchestratorModule = true;
834
+ if (!repoRoot) {
835
+ repoRoot = findRepoRoot(ctx.roots.pkgBase) ?? findRepoRoot(ctx.roots.appBase);
836
+ }
837
+ const pkgScanBase = resolveStandaloneSourceMirrorBase(ctx.roots.pkgBase) ?? ctx.roots.pkgBase;
838
+ scanAgentsTree(ctx.moduleId, path.join(pkgScanBase, "agents"));
839
+ scanAgentsTree(ctx.moduleId, path.join(ctx.roots.appBase, "agents"));
840
+ },
841
+ generateOutput() {
842
+ const sorted = [...discovered].sort((a, b) => a.id.localeCompare(b.id));
843
+ if (!repoRoot) {
844
+ return /* @__PURE__ */ new Map();
845
+ }
846
+ if (!sawOrchestratorModule && sorted.length === 0) {
847
+ return /* @__PURE__ */ new Map();
848
+ }
849
+ const manifestPath = path.join(
850
+ repoRoot,
851
+ "packages",
852
+ "enterprise",
853
+ "src",
854
+ "modules",
855
+ "agent_orchestrator",
856
+ "generated",
857
+ "file-agents.generated.ts"
858
+ );
859
+ fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
860
+ fs.writeFileSync(manifestPath, renderManifest(sorted), "utf8");
861
+ const dockerAgentsDir = path.join(repoRoot, "docker", "opencode", "agents");
862
+ fs.mkdirSync(dockerAgentsDir, { recursive: true });
863
+ const desiredFiles = /* @__PURE__ */ new Map();
864
+ for (const agent of sorted) {
865
+ desiredFiles.set(`${agent.openCodeAgentName}.md`, renderOpenCodeAgentFile(agent));
866
+ for (const sub of agent.subAgentsContent) {
867
+ desiredFiles.set(`${sub.openCodeAgentName}.md`, renderOpenCodeAgentFile(sub));
868
+ }
869
+ }
870
+ for (const existing of fs.existsSync(dockerAgentsDir) ? fs.readdirSync(dockerAgentsDir) : []) {
871
+ if (existing.endsWith(".md") && !desiredFiles.has(existing)) {
872
+ fs.rmSync(path.join(dockerAgentsDir, existing));
873
+ }
874
+ }
875
+ for (const [fileName, content] of desiredFiles) {
876
+ fs.writeFileSync(path.join(dockerAgentsDir, fileName), content, "utf8");
877
+ }
878
+ const allAgents = sorted.flatMap((agent) => [agent, ...agent.subAgentsContent]);
879
+ const dockerSkillsDir = path.join(repoRoot, "docker", "opencode", "skills");
880
+ const isNativeSkill = (skill) => skill.id !== AGENT_TOOLS_SKILL_ID;
881
+ const desiredSkillNames = /* @__PURE__ */ new Set();
882
+ for (const agent of allAgents) {
883
+ for (const skill of agent.skillsContent) {
884
+ if (isNativeSkill(skill)) desiredSkillNames.add(skill.openCodeSkillName);
885
+ }
886
+ }
887
+ if (desiredSkillNames.size > 0) fs.mkdirSync(dockerSkillsDir, { recursive: true });
888
+ if (fs.existsSync(dockerSkillsDir)) {
889
+ for (const existing of fs.readdirSync(dockerSkillsDir, { withFileTypes: true })) {
890
+ if (existing.isDirectory() && !desiredSkillNames.has(existing.name)) {
891
+ fs.rmSync(path.join(dockerSkillsDir, existing.name), { recursive: true, force: true });
892
+ }
893
+ }
894
+ }
895
+ const renderedSkillNames = /* @__PURE__ */ new Set();
896
+ for (const agent of allAgents) {
897
+ for (const skill of agent.skillsContent) {
898
+ if (!isNativeSkill(skill)) continue;
899
+ if (renderedSkillNames.has(skill.openCodeSkillName)) continue;
900
+ renderedSkillNames.add(skill.openCodeSkillName);
901
+ const skillDir = path.join(dockerSkillsDir, skill.openCodeSkillName);
902
+ fs.mkdirSync(skillDir, { recursive: true });
903
+ fs.writeFileSync(path.join(skillDir, "SKILL.md"), renderOpenCodeSkillFile(skill), "utf8");
904
+ }
905
+ }
906
+ return /* @__PURE__ */ new Map();
907
+ }
908
+ };
909
+ }
910
+ export {
911
+ createAgentFilesExtension
912
+ };
913
+ //# sourceMappingURL=agent-files.js.map