@bluefly/openstandardagents 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/.version.json +3 -3
  2. package/CHANGELOG.md +43 -12
  3. package/README.md +31 -26
  4. package/bin/postinstall +0 -0
  5. package/dist/.version.json +3 -3
  6. package/dist/adapters/a2a/a2a-protocol.js +4 -2
  7. package/dist/adapters/a2a/a2a-tool.js +4 -2
  8. package/dist/adapters/a2a/mcp-integration.d.ts +2 -1
  9. package/dist/adapters/a2a/mcp-integration.js +6 -3
  10. package/dist/adapters/browser/browser-exporter.d.ts +26 -0
  11. package/dist/adapters/browser/browser-exporter.js +73 -0
  12. package/dist/adapters/browser/browser-runner.d.ts +23 -0
  13. package/dist/adapters/browser/browser-runner.js +46 -0
  14. package/dist/adapters/browser/index.d.ts +9 -0
  15. package/dist/adapters/browser/index.js +9 -0
  16. package/dist/adapters/docker/index.d.ts +2 -0
  17. package/dist/adapters/docker/index.js +2 -0
  18. package/dist/adapters/docker/openclaw-bridge.d.ts +57 -0
  19. package/dist/adapters/docker/openclaw-bridge.js +173 -0
  20. package/dist/adapters/drupal/index.d.ts +1 -0
  21. package/dist/adapters/drupal/index.js +2 -0
  22. package/dist/adapters/drupal/twig-renderer.d.ts +23 -0
  23. package/dist/adapters/drupal/twig-renderer.js +99 -0
  24. package/dist/adapters/gitlab/agent-generator.js +2 -1
  25. package/dist/api/index.js +2 -1
  26. package/dist/api/routes/mcp.router.js +3 -1
  27. package/dist/api/routes/wizard.router.js +3 -1
  28. package/dist/cli/commands/agent/discover-type.command.js +1 -1
  29. package/dist/cli/commands/agent-card.command.js +37 -10
  30. package/dist/cli/commands/agents-sync.command.d.ts +2 -2
  31. package/dist/cli/commands/agents-sync.command.js +27 -17
  32. package/dist/cli/commands/catalog/config.js +1 -1
  33. package/dist/cli/commands/catalog/validate.command.js +2 -2
  34. package/dist/cli/commands/config.command.js +2 -2
  35. package/dist/cli/commands/daemon.command.js +32 -8
  36. package/dist/cli/commands/discover.d.ts +1 -1
  37. package/dist/cli/commands/discover.js +16 -8
  38. package/dist/cli/commands/economics.command.d.ts +9 -0
  39. package/dist/cli/commands/economics.command.js +113 -0
  40. package/dist/cli/commands/export.command.js +6 -3
  41. package/dist/cli/commands/mcp.command.js +3 -1
  42. package/dist/cli/commands/memory.command.d.ts +18 -0
  43. package/dist/cli/commands/memory.command.js +168 -0
  44. package/dist/cli/commands/publish.command.js +7 -4
  45. package/dist/cli/commands/serve-builder-routes.js +1 -1
  46. package/dist/cli/commands/usie-skills.command.d.ts +24 -0
  47. package/dist/cli/commands/usie-skills.command.js +297 -0
  48. package/dist/cli/commands/validate.command.js +8 -1
  49. package/dist/cli/commands/verify.d.ts +3 -3
  50. package/dist/cli/commands/verify.js +12 -6
  51. package/dist/cli/commands/workspace.command.d.ts +1 -0
  52. package/dist/cli/commands/workspace.command.js +28 -4
  53. package/dist/cli/index.js +12 -0
  54. package/dist/cli/workspace-validate.d.ts +23 -0
  55. package/dist/cli/workspace-validate.js +117 -0
  56. package/dist/data/platform-matrix.js +1 -4
  57. package/dist/generated/types.d.ts +97 -97
  58. package/dist/index.d.ts +2 -0
  59. package/dist/index.js +2 -0
  60. package/dist/mcp-server/index.js +658 -982
  61. package/dist/mesh/discovery-gkg.d.ts +26 -0
  62. package/dist/mesh/discovery-gkg.js +92 -0
  63. package/dist/messenger/Handler/AgentBatchHandler.js +3 -2
  64. package/dist/messenger/Handler/AgentExecutionHandler.js +6 -1
  65. package/dist/package.json +20 -4
  66. package/dist/sdks/shared/types.d.ts +1 -1
  67. package/dist/services/agent-card-generator.js +6 -2
  68. package/dist/services/daemon/audit-log.service.js +3 -1
  69. package/dist/services/daemon/execution.service.js +8 -4
  70. package/dist/services/daemon/fs-watcher.service.js +6 -7
  71. package/dist/services/daemon/pairing.service.js +2 -1
  72. package/dist/services/daemon/skill-aggregator.service.js +105 -21
  73. package/dist/services/daemon/sse-endpoints.js +1 -1
  74. package/dist/services/daemon/ws-server.js +10 -3
  75. package/dist/services/governance/cedar-provider.js +12 -8
  76. package/dist/services/governance/cedar-validator.service.js +1 -1
  77. package/dist/services/mcp/bridge.service.js +40 -9
  78. package/dist/services/openapi-extensions-validation.d.ts +20 -0
  79. package/dist/services/openapi-extensions-validation.js +193 -0
  80. package/dist/services/release-automation/merge-request.service.d.ts +4 -4
  81. package/dist/services/release-automation/release-buttons.js +3 -3
  82. package/dist/services/release-automation/schemas/release.schema.d.ts +3 -3
  83. package/dist/services/runtime/openai.adapter.d.ts +46 -13
  84. package/dist/services/runtime/openai.adapter.js +169 -131
  85. package/dist/services/skill-registry.service.d.ts +1 -1
  86. package/dist/services/skills-pipeline/skills-research.service.js +47 -7
  87. package/dist/services/trust/trust.service.js +6 -4
  88. package/dist/services/validation-zod.service.js +3 -22
  89. package/dist/services/validators/index.d.ts +1 -0
  90. package/dist/services/validators/index.js +1 -0
  91. package/dist/services/validators/registry.d.ts +21 -0
  92. package/dist/services/validators/registry.js +42 -0
  93. package/dist/skills/test-skill/package.json +1 -1
  94. package/dist/spec/extensions/cognition.schema.json +87 -0
  95. package/dist/spec/layer4-economics/duadp-examples.json +44 -0
  96. package/dist/spec/v0.4/agent.schema.json +14 -0
  97. package/dist/spec/v0.5/agent-builder-openapi.yaml +230 -0
  98. package/dist/spec/v0.5/agent.schema.json +32 -1
  99. package/dist/spec/v0.5/extensions/cognition/cognition.schema.json +78 -1
  100. package/dist/spec/v0.5/extensions/economics/context-pack.schema.json +91 -0
  101. package/dist/spec/v0.5/extensions/economics/execution-profile.schema.json +148 -0
  102. package/dist/spec/v0.5/extensions/economics/failure-semantics.schema.json +32 -0
  103. package/dist/spec/v0.5/extensions/economics/replay-packet.schema.json +120 -0
  104. package/dist/spec/v0.5/extensions/mcp/README.md +1 -1
  105. package/dist/spec/v0.5/memory-hierarchy.yaml +120 -0
  106. package/dist/spec/v1/agent-card.schema.json +254 -0
  107. package/dist/types/cognition.zod.d.ts +312 -0
  108. package/dist/types/cognition.zod.js +223 -0
  109. package/dist/types/identity.zod.d.ts +5 -5
  110. package/dist/types/index.d.ts +53 -7
  111. package/dist/types/index.js +4 -2
  112. package/dist/types/personality.zod.d.ts +3 -3
  113. package/dist/utils/http-client.d.ts +22 -0
  114. package/dist/utils/http-client.js +51 -0
  115. package/dist/utils/index.d.ts +3 -0
  116. package/dist/utils/index.js +3 -0
  117. package/dist/utils/proxy-resolver.d.ts +36 -0
  118. package/dist/utils/proxy-resolver.js +59 -0
  119. package/dist/utils/user-agent.d.ts +11 -0
  120. package/dist/utils/user-agent.js +17 -0
  121. package/dist/validation/version-compliance.js +1 -1
  122. package/examples/agentscope/react-assistant/README.md +1 -1
  123. package/examples/agentscope/react-assistant/agent.ossa.yaml +1 -1
  124. package/examples/drupal/drupal-contributor-agent/.eslintrc.json +58 -0
  125. package/examples/drupal/drupal-contributor-agent/.prettierrc.json +10 -0
  126. package/examples/drupal/drupal-contributor-agent/package.json +55 -0
  127. package/examples/drupal/drupal-contributor-agent/src/core/index.ts +10 -0
  128. package/examples/drupal/drupal-contributor-agent/src/index.ts +17 -0
  129. package/examples/drupal/drupal-contributor-agent/src/types/index.ts +180 -0
  130. package/examples/drupal/drupal-contributor-agent/tsconfig.json +36 -0
  131. package/examples/getting-started/01-minimal-agent.ossa.yaml +1 -1
  132. package/examples/getting-started/02-agent-with-tools.ossa.yaml +1 -1
  133. package/examples/getting-started/03-agent-with-safety.ossa.yaml +1 -1
  134. package/examples/getting-started/04-agent-with-messaging.ossa.yaml +1 -1
  135. package/examples/getting-started/05-workflow-composition.ossa.yaml +1 -1
  136. package/examples/getting-started/hello-world-complete.ossa.yaml +1 -1
  137. package/examples/reference-implementations/python-client/examples/basic_usage.py +0 -0
  138. package/examples/reference-implementations/python-client/examples/publish_agent.py +0 -0
  139. package/openapi/agent-cognition-sessions.yaml +580 -0
  140. package/openapi/agent-crud.yaml +20 -20
  141. package/openapi/core/ossa-registry-api.openapi.yaml +1 -1
  142. package/openapi/ossa-cli-enhancements.openapi.yaml +1 -1
  143. package/openapi/release-automation.openapi.yaml +1 -1
  144. package/openapi/schemas/common/economics.yaml +98 -0
  145. package/openapi/uadp-asyncapi.yaml +1 -1
  146. package/openapi/uadp-openapi.yaml +2 -2
  147. package/package.json +114 -96
  148. package/spec/extensions/cognition.schema.json +87 -0
  149. package/spec/layer4-economics/duadp-examples.json +44 -0
  150. package/spec/v0.4/agent.schema.json +14 -0
  151. package/spec/v0.5/agent-builder-openapi.yaml +230 -0
  152. package/spec/v0.5/agent.schema.json +32 -1
  153. package/spec/v0.5/extensions/cognition/cognition.schema.json +78 -1
  154. package/spec/v0.5/extensions/economics/context-pack.schema.json +91 -0
  155. package/spec/v0.5/extensions/economics/execution-profile.schema.json +148 -0
  156. package/spec/v0.5/extensions/economics/failure-semantics.schema.json +32 -0
  157. package/spec/v0.5/extensions/economics/replay-packet.schema.json +120 -0
  158. package/spec/v0.5/extensions/mcp/README.md +1 -1
  159. package/spec/v0.5/memory-hierarchy.yaml +120 -0
  160. package/spec/v1/agent-card.schema.json +254 -0
  161. package/dist/adapters/a2a/__tests__/mcp-integration.spec.d.ts +0 -5
  162. package/dist/adapters/a2a/__tests__/mcp-integration.spec.js +0 -268
  163. package/dist/adapters/a2a/__tests__/mcp-transport.spec.d.ts +0 -5
  164. package/dist/adapters/a2a/__tests__/mcp-transport.spec.js +0 -203
  165. package/dist/mcp-server/__tests__/mcp-server.spec.d.ts +0 -8
  166. package/dist/mcp-server/__tests__/mcp-server.spec.js +0 -566
  167. package/dist/validation/__tests__/error-codes.test.d.ts +0 -5
  168. package/dist/validation/__tests__/error-codes.test.js +0 -252
  169. package/dist/version-management/core/version-manager.test.d.ts +0 -2
  170. package/dist/version-management/core/version-manager.test.js +0 -210
@@ -20,38 +20,35 @@
20
20
  import 'reflect-metadata';
21
21
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
22
22
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
23
- import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
24
- import axios from 'axios';
23
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
24
+ import { z } from 'zod';
25
+ import pino from 'pino';
25
26
  import fg from 'fast-glob';
26
27
  import yaml from 'js-yaml';
27
- import { spawnSync } from 'node:child_process';
28
+ import axios from 'axios';
29
+ import semver from 'semver';
28
30
  import * as fs from 'node:fs';
29
31
  import * as path from 'node:path';
30
- import pino from 'pino';
31
- import semver from 'semver';
32
- import { z } from 'zod';
33
- import { registry as convertRegistry, initializeAdapters, } from '../adapters/index.js';
34
- import { getAgentTypeConfigs, getDefaultAgentKind, getDefaultAgentVersion, getDefaultDescriptionTemplate, getDefaultRoleTemplate, } from '../config/defaults.js';
35
32
  import { container } from '../di-container.js';
36
33
  import { ManifestRepository } from '../repositories/manifest.repository.js';
37
- import { AgentCardGenerator } from '../services/agent-card-generator.js';
34
+ import { ValidationService } from '../services/validation.service.js';
38
35
  import { MigrationTransformService } from '../services/migration-transform.service.js';
36
+ import { VersionDetectionService } from '../services/version-detection.service.js';
37
+ import { AgentCardGenerator } from '../services/agent-card-generator.js';
39
38
  import { RegistryService } from '../services/registry.service.js';
40
- import { ValidationService } from '../services/validation.service.js';
41
- import { AnthropicValidator } from '../services/validators/anthropic.validator.js';
42
- import { AutoGenValidator } from '../services/validators/autogen.validator.js';
43
- import { CrewAIValidator } from '../services/validators/crewai.validator.js';
39
+ import { getApiVersion, getVersion } from '../utils/version.js';
40
+ import { getDefaultAgentVersion, getDefaultAgentKind, getDefaultRoleTemplate, getDefaultDescriptionTemplate, getAgentTypeConfigs, } from '../config/defaults.js';
44
41
  import { CursorValidator } from '../services/validators/cursor.validator.js';
45
- import { KagentValidator } from '../services/validators/kagent.validator.js';
42
+ import { OpenAIValidator } from '../services/validators/openai.validator.js';
43
+ import { CrewAIValidator } from '../services/validators/crewai.validator.js';
46
44
  import { LangChainValidator } from '../services/validators/langchain.validator.js';
45
+ import { AnthropicValidator } from '../services/validators/anthropic.validator.js';
47
46
  import { LangflowValidator } from '../services/validators/langflow.validator.js';
48
- import { LangGraphValidator } from '../services/validators/langgraph.validator.js';
49
- import { LlamaIndexValidator } from '../services/validators/llamaindex.validator.js';
50
- import { OpenAIValidator } from '../services/validators/openai.validator.js';
47
+ import { AutoGenValidator } from '../services/validators/autogen.validator.js';
51
48
  import { VercelAIValidator } from '../services/validators/vercel-ai.validator.js';
52
- import { VersionDetectionService } from '../services/version-detection.service.js';
53
- import { scanManifests } from '../utils/manifest-scanner.js';
54
- import { getApiVersion, getVersion } from '../utils/version.js';
49
+ import { LlamaIndexValidator } from '../services/validators/llamaindex.validator.js';
50
+ import { LangGraphValidator } from '../services/validators/langgraph.validator.js';
51
+ import { KagentValidator } from '../services/validators/kagent.validator.js';
55
52
  // ---------------------------------------------------------------------------
56
53
  // Logging — pino (structured JSON to stderr so MCP stdio stays clean)
57
54
  // ---------------------------------------------------------------------------
@@ -69,39 +66,23 @@ const ValidateInput = z.object({
69
66
  path: z.string().min(1, 'path is required'),
70
67
  platform: z
71
68
  .enum([
72
- 'kagent',
73
- 'langchain',
74
- 'crewai',
75
- 'docker',
76
- 'kubernetes',
77
- 'gitlab-duo',
78
- 'anthropic',
69
+ 'kagent', 'langchain', 'crewai', 'docker', 'kubernetes', 'gitlab-duo',
70
+ 'anthropic', 'cursor', 'openai_agents', 'langflow', 'autogen',
71
+ 'vercel_ai', 'llamaindex', 'langgraph',
79
72
  ])
80
73
  .optional(),
81
74
  strict: z.boolean().optional().default(false),
82
75
  });
83
76
  const ScaffoldInput = z.object({
84
- name: z
85
- .string()
86
- .min(1)
87
- .regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, 'Must be DNS-1123 format'),
77
+ name: z.string().min(1).regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, 'Must be DNS-1123 format'),
88
78
  output_dir: z.string().optional().default('.agents'),
89
79
  description: z.string().optional(),
90
80
  role: z.string().optional(),
91
81
  type: z
92
- .enum([
93
- 'worker',
94
- 'orchestrator',
95
- 'reviewer',
96
- 'analyzer',
97
- 'executor',
98
- 'approver',
99
- ])
82
+ .enum(['worker', 'orchestrator', 'reviewer', 'analyzer', 'executor', 'approver'])
100
83
  .optional()
101
84
  .default('worker'),
102
85
  version: z.string().optional(),
103
- source_project_id: z.string().optional(),
104
- execution_node_id: z.string().optional(),
105
86
  });
106
87
  const GenerateInput = z.object({
107
88
  path: z.string().min(1, 'path is required'),
@@ -134,7 +115,6 @@ const ConvertInput = z.object({
134
115
  'semantic-kernel',
135
116
  'a2a',
136
117
  'agent-card',
137
- 'claude-agent-sdk',
138
118
  ]),
139
119
  output_dir: z.string().optional(),
140
120
  });
@@ -161,8 +141,6 @@ const migrationTransformService = container.get(MigrationTransformService);
161
141
  const versionDetectionService = container.get(VersionDetectionService);
162
142
  const registryService = container.get(RegistryService);
163
143
  const agentCardGenerator = new AgentCardGenerator();
164
- // Initialize adapter registry (config-only adapters for MCP convert)
165
- initializeAdapters();
166
144
  // ---------------------------------------------------------------------------
167
145
  // MCP Server
168
146
  // ---------------------------------------------------------------------------
@@ -184,28 +162,17 @@ const tools = [
184
162
  inputSchema: {
185
163
  type: 'object',
186
164
  properties: {
187
- path: {
188
- type: 'string',
189
- description: 'Path to manifest file (.ossa.yaml, .ossa.yml, .json)',
190
- },
165
+ path: { type: 'string', description: 'Path to manifest file (.ossa.yaml, .ossa.yml, .json)' },
191
166
  platform: {
192
167
  type: 'string',
193
168
  enum: [
194
- 'kagent',
195
- 'langchain',
196
- 'crewai',
197
- 'docker',
198
- 'kubernetes',
199
- 'gitlab-duo',
200
- 'anthropic',
169
+ 'kagent', 'langchain', 'crewai', 'docker', 'kubernetes', 'gitlab-duo',
170
+ 'anthropic', 'cursor', 'openai_agents', 'langflow', 'autogen',
171
+ 'vercel_ai', 'llamaindex', 'langgraph',
201
172
  ],
202
- description: 'Optional platform-specific validation',
203
- },
204
- strict: {
205
- type: 'boolean',
206
- description: 'Enable strict mode (warnings become errors)',
207
- default: false,
173
+ description: 'Optional platform-specific validation (runs targeted validator for this platform)',
208
174
  },
175
+ strict: { type: 'boolean', description: 'Enable strict mode (warnings become errors)', default: false },
209
176
  },
210
177
  required: ['path'],
211
178
  },
@@ -216,33 +183,16 @@ const tools = [
216
183
  inputSchema: {
217
184
  type: 'object',
218
185
  properties: {
219
- name: {
220
- type: 'string',
221
- description: 'Agent name (DNS-1123: lowercase alphanumeric + hyphens)',
222
- },
223
- output_dir: {
224
- type: 'string',
225
- description: 'Parent directory (default: .agents)',
226
- default: '.agents',
227
- },
186
+ name: { type: 'string', description: 'Agent name (DNS-1123: lowercase alphanumeric + hyphens)' },
187
+ output_dir: { type: 'string', description: 'Parent directory (default: .agents)', default: '.agents' },
228
188
  description: { type: 'string', description: 'Short description' },
229
189
  role: { type: 'string', description: 'System prompt / role' },
230
190
  type: {
231
191
  type: 'string',
232
- enum: [
233
- 'worker',
234
- 'orchestrator',
235
- 'reviewer',
236
- 'analyzer',
237
- 'executor',
238
- 'approver',
239
- ],
192
+ enum: ['worker', 'orchestrator', 'reviewer', 'analyzer', 'executor', 'approver'],
240
193
  default: 'worker',
241
194
  },
242
- version: {
243
- type: 'string',
244
- description: 'Initial version (default: 1.0.0)',
245
- },
195
+ version: { type: 'string', description: 'Initial version (default: 1.0.0)' },
246
196
  },
247
197
  required: ['name'],
248
198
  },
@@ -254,10 +204,7 @@ const tools = [
254
204
  type: 'object',
255
205
  properties: {
256
206
  path: { type: 'string', description: 'Path to OSSA manifest file' },
257
- output_dir: {
258
- type: 'string',
259
- description: 'Optional directory to write agent-card.json',
260
- },
207
+ output_dir: { type: 'string', description: 'Optional directory to write agent-card.json' },
261
208
  },
262
209
  required: ['path'],
263
210
  },
@@ -269,15 +216,8 @@ const tools = [
269
216
  type: 'object',
270
217
  properties: {
271
218
  path: { type: 'string', description: 'Path to OSSA manifest file' },
272
- registry_url: {
273
- type: 'string',
274
- description: 'Registry base URL (or set REGISTRY_URL / AGENT_REGISTRY_URL env)',
275
- },
276
- dry_run: {
277
- type: 'boolean',
278
- description: 'Preview publish payload without sending',
279
- default: false,
280
- },
219
+ registry_url: { type: 'string', description: 'Registry base URL (or set REGISTRY_URL / AGENT_REGISTRY_URL env)' },
220
+ dry_run: { type: 'boolean', description: 'Preview publish payload without sending', default: false },
281
221
  },
282
222
  required: ['path'],
283
223
  },
@@ -288,16 +228,8 @@ const tools = [
288
228
  inputSchema: {
289
229
  type: 'object',
290
230
  properties: {
291
- directory: {
292
- type: 'string',
293
- description: 'Root directory to scan (default: cwd)',
294
- default: '.',
295
- },
296
- recursive: {
297
- type: 'boolean',
298
- description: 'Scan subdirectories',
299
- default: true,
300
- },
231
+ directory: { type: 'string', description: 'Root directory to scan (default: cwd)', default: '.' },
232
+ recursive: { type: 'boolean', description: 'Scan subdirectories', default: true },
301
233
  format: {
302
234
  type: 'string',
303
235
  enum: ['summary', 'detailed', 'json'],
@@ -327,25 +259,10 @@ const tools = [
327
259
  path: { type: 'string', description: 'Path to OSSA manifest file' },
328
260
  target: {
329
261
  type: 'string',
330
- enum: [
331
- 'kagent',
332
- 'docker',
333
- 'langchain',
334
- 'crewai',
335
- 'gitlab-duo',
336
- 'anthropic',
337
- 'openai',
338
- 'autogen',
339
- 'semantic-kernel',
340
- 'a2a',
341
- 'agent-card',
342
- ],
262
+ enum: ['kagent', 'docker', 'langchain', 'crewai', 'gitlab-duo', 'anthropic', 'openai', 'autogen', 'semantic-kernel', 'a2a', 'agent-card'],
343
263
  description: 'Target format',
344
264
  },
345
- output_dir: {
346
- type: 'string',
347
- description: 'Optional directory to write converted output',
348
- },
265
+ output_dir: { type: 'string', description: 'Optional directory to write converted output' },
349
266
  },
350
267
  required: ['path', 'target'],
351
268
  },
@@ -361,10 +278,7 @@ const tools = [
361
278
  enum: ['init', 'discover', 'status'],
362
279
  description: 'Workspace action',
363
280
  },
364
- directory: {
365
- type: 'string',
366
- description: 'Root directory to scan (default: current dir)',
367
- },
281
+ directory: { type: 'string', description: 'Root directory to scan (default: current dir)' },
368
282
  name: { type: 'string', description: 'Workspace name (for init)' },
369
283
  },
370
284
  required: ['action'],
@@ -394,10 +308,7 @@ const tools = [
394
308
  description: 'Target OSSA version (default: ossa/v0.4)',
395
309
  default: 'ossa/v0.4',
396
310
  },
397
- output_dir: {
398
- type: 'string',
399
- description: 'Optional directory to write migrated manifest',
400
- },
311
+ output_dir: { type: 'string', description: 'Optional directory to write migrated manifest' },
401
312
  },
402
313
  required: ['path'],
403
314
  },
@@ -451,20 +362,11 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
451
362
  const schemaContent = fs.existsSync(schemaPath)
452
363
  ? fs.readFileSync(schemaPath, 'utf8')
453
364
  : JSON.stringify({ error: 'Schema file not found', path: schemaPath });
454
- return {
455
- contents: [
456
- {
457
- uri: request.params.uri,
458
- mimeType: 'application/schema+json',
459
- text: schemaContent,
460
- },
461
- ],
462
- };
365
+ return { contents: [{ uri: request.params.uri, mimeType: 'application/schema+json', text: schemaContent }] };
463
366
  }
464
367
  case 'ossa://template/minimal':
465
368
  return {
466
- contents: [
467
- {
369
+ contents: [{
468
370
  uri: request.params.uri,
469
371
  mimeType: 'text/yaml',
470
372
  text: `apiVersion: ossa/v0.4
@@ -476,13 +378,11 @@ metadata:
476
378
  spec:
477
379
  role: You are a helpful assistant.
478
380
  `,
479
- },
480
- ],
381
+ }],
481
382
  };
482
383
  case 'ossa://template/full':
483
384
  return {
484
- contents: [
485
- {
385
+ contents: [{
486
386
  uri: request.params.uri,
487
387
  mimeType: 'text/yaml',
488
388
  text: `apiVersion: ossa/v0.4
@@ -576,13 +476,11 @@ token_efficiency:
576
476
  - model: claude-sonnet-4-20250514
577
477
  max_tokens: 4096
578
478
  `,
579
- },
580
- ],
479
+ }],
581
480
  };
582
481
  case 'ossa://guide/mcp-ossa-a2a':
583
482
  return {
584
- contents: [
585
- {
483
+ contents: [{
586
484
  uri: request.params.uri,
587
485
  mimeType: 'text/markdown',
588
486
  text: `# MCP → OSSA → A2A: The Agent Stack
@@ -650,150 +548,34 @@ npx ossa convert .agents/my-agent/manifest.ossa.yaml --target agent-card
650
548
  - npm: https://www.npmjs.com/package/@bluefly/openstandardagents
651
549
  - MCP Server: \`npx ossa-mcp\` (this server)
652
550
  `,
653
- },
654
- ],
551
+ }],
655
552
  };
656
553
  case 'ossa://platforms/supported':
657
554
  return {
658
- contents: [
659
- {
555
+ contents: [{
660
556
  uri: request.params.uri,
661
557
  mimeType: 'application/json',
662
558
  text: JSON.stringify({
663
559
  platforms: [
664
- {
665
- id: 'kagent',
666
- name: 'kagent.dev',
667
- type: 'kubernetes',
668
- docs: 'https://kagent.dev',
669
- format: 'v1alpha2 CRD (Agent + ModelConfig)',
670
- },
671
- {
672
- id: 'docker',
673
- name: 'Docker',
674
- type: 'container',
675
- docs: 'https://docs.docker.com',
676
- format: 'docker-compose.yml',
677
- },
678
- {
679
- id: 'openai',
680
- name: 'OpenAI',
681
- type: 'llm',
682
- sdk: { npm: 'openai', pip: 'openai' },
683
- docs: 'https://platform.openai.com/docs',
684
- format: 'Assistants / function_calling',
685
- },
686
- {
687
- id: 'anthropic',
688
- name: 'Anthropic',
689
- type: 'llm',
690
- sdk: { npm: '@anthropic-ai/sdk', pip: 'anthropic' },
691
- docs: 'https://docs.anthropic.com',
692
- format: 'Messages API + tool_use',
693
- },
694
- {
695
- id: 'google_genai',
696
- name: 'Google Gemini',
697
- type: 'llm',
698
- sdk: {
699
- npm: '@google/generative-ai',
700
- pip: 'google-generativeai',
701
- },
702
- docs: 'https://ai.google.dev',
703
- format: 'GenerativeAI',
704
- },
705
- {
706
- id: 'langchain',
707
- name: 'LangChain',
708
- type: 'framework',
709
- sdk: {
710
- npm: ['langchain', '@langchain/core'],
711
- pip: ['langchain', 'langchain-openai'],
712
- },
713
- docs: 'https://js.langchain.com',
714
- format: 'Agent + StructuredTool',
715
- },
716
- {
717
- id: 'langflow',
718
- name: 'LangFlow',
719
- type: 'visual',
720
- sdk: { pip: 'langflow' },
721
- docs: 'https://docs.langflow.org',
722
- format: 'Custom component',
723
- },
724
- {
725
- id: 'crewai',
726
- name: 'CrewAI',
727
- type: 'framework',
728
- sdk: { pip: 'crewai' },
729
- docs: 'https://docs.crewai.com',
730
- format: 'Agent YAML',
731
- },
732
- {
733
- id: 'autogen',
734
- name: 'AutoGen',
735
- type: 'framework',
736
- sdk: { pip: 'autogen-agentchat' },
737
- docs: 'https://microsoft.github.io/autogen',
738
- format: 'ConversableAgent',
739
- },
740
- {
741
- id: 'semantic-kernel',
742
- name: 'Semantic Kernel',
743
- type: 'framework',
744
- sdk: { npm: 'semantic-kernel', pip: 'semantic-kernel' },
745
- docs: 'https://learn.microsoft.com/en-us/semantic-kernel',
746
- format: 'Agent + Plugins',
747
- },
748
- {
749
- id: 'llamaindex',
750
- name: 'LlamaIndex',
751
- type: 'framework',
752
- sdk: { npm: 'llamaindex', pip: 'llama-index' },
753
- docs: 'https://docs.llamaindex.ai',
754
- format: 'Agent config',
755
- },
756
- {
757
- id: 'dspy',
758
- name: 'DSPy',
759
- type: 'framework',
760
- sdk: { pip: 'dspy' },
761
- docs: 'https://dspy.ai',
762
- format: 'Module config',
763
- },
764
- {
765
- id: 'gitlab-duo',
766
- name: 'GitLab Duo',
767
- type: 'devops',
768
- docs: 'https://docs.gitlab.com/ee/user/gitlab_duo',
769
- format: 'Duo agent YAML',
770
- },
771
- {
772
- id: 'agent-card',
773
- name: 'Universal Agent Card',
774
- type: 'universal',
775
- docs: 'https://openstandardagents.org',
776
- format: 'Cross-platform JSON with all 12 adapters',
777
- },
778
- {
779
- id: 'claude-agent-sdk',
780
- name: 'Claude Agent SDK',
781
- type: 'agent-sdk',
782
- sdk: {
783
- npm: '@anthropic-ai/claude-agent-sdk',
784
- pip: 'claude-agent-sdk',
785
- go: 'github.com/M1n9X/claude-agent-sdk-go',
786
- rust: 'claude_agent',
787
- },
788
- docs: 'https://docs.claude.com/en/api/agent-sdk/overview',
789
- format: 'Runnable Agent SDK app (TS/PY/Go/Rust)',
790
- },
560
+ { id: 'kagent', name: 'kagent.dev', type: 'kubernetes', docs: 'https://kagent.dev', format: 'v1alpha2 CRD (Agent + ModelConfig)' },
561
+ { id: 'docker', name: 'Docker', type: 'container', docs: 'https://docs.docker.com', format: 'docker-compose.yml' },
562
+ { id: 'openai', name: 'OpenAI', type: 'llm', sdk: { npm: 'openai', pip: 'openai' }, docs: 'https://platform.openai.com/docs', format: 'Assistants / function_calling' },
563
+ { id: 'anthropic', name: 'Anthropic', type: 'llm', sdk: { npm: '@anthropic-ai/sdk', pip: 'anthropic' }, docs: 'https://docs.anthropic.com', format: 'Messages API + tool_use' },
564
+ { id: 'google_genai', name: 'Google Gemini', type: 'llm', sdk: { npm: '@google/generative-ai', pip: 'google-generativeai' }, docs: 'https://ai.google.dev', format: 'GenerativeAI' },
565
+ { id: 'langchain', name: 'LangChain', type: 'framework', sdk: { npm: ['langchain', '@langchain/core'], pip: ['langchain', 'langchain-openai'] }, docs: 'https://js.langchain.com', format: 'Agent + StructuredTool' },
566
+ { id: 'langflow', name: 'LangFlow', type: 'visual', sdk: { pip: 'langflow' }, docs: 'https://docs.langflow.org', format: 'Custom component' },
567
+ { id: 'crewai', name: 'CrewAI', type: 'framework', sdk: { pip: 'crewai' }, docs: 'https://docs.crewai.com', format: 'Agent YAML' },
568
+ { id: 'autogen', name: 'AutoGen', type: 'framework', sdk: { pip: 'autogen-agentchat' }, docs: 'https://microsoft.github.io/autogen', format: 'ConversableAgent' },
569
+ { id: 'semantic-kernel', name: 'Semantic Kernel', type: 'framework', sdk: { npm: 'semantic-kernel', pip: 'semantic-kernel' }, docs: 'https://learn.microsoft.com/en-us/semantic-kernel', format: 'Agent + Plugins' },
570
+ { id: 'llamaindex', name: 'LlamaIndex', type: 'framework', sdk: { npm: 'llamaindex', pip: 'llama-index' }, docs: 'https://docs.llamaindex.ai', format: 'Agent config' },
571
+ { id: 'dspy', name: 'DSPy', type: 'framework', sdk: { pip: 'dspy' }, docs: 'https://dspy.ai', format: 'Module config' },
572
+ { id: 'gitlab-duo', name: 'GitLab Duo', type: 'devops', docs: 'https://docs.gitlab.com/ee/user/gitlab_duo', format: 'Duo agent YAML' },
573
+ { id: 'agent-card', name: 'Universal Agent Card', type: 'universal', docs: 'https://openstandardagents.org', format: 'Cross-platform JSON with all 12 adapters' },
791
574
  ],
792
- total: 15,
575
+ total: 14,
793
576
  ossa_version: 'ossa/v0.4',
794
577
  }, null, 2),
795
- },
796
- ],
578
+ }],
797
579
  };
798
580
  default:
799
581
  throw new Error(`Unknown resource: ${request.params.uri}`);
@@ -808,16 +590,8 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
808
590
  name: 'create-agent',
809
591
  description: 'Create a new OSSA agent from a description of what it should do',
810
592
  arguments: [
811
- {
812
- name: 'description',
813
- description: 'What the agent should do',
814
- required: true,
815
- },
816
- {
817
- name: 'name',
818
- description: 'Agent name (DNS-1123 format)',
819
- required: false,
820
- },
593
+ { name: 'description', description: 'What the agent should do', required: true },
594
+ { name: 'name', description: 'Agent name (DNS-1123 format)', required: false },
821
595
  ],
822
596
  },
823
597
  {
@@ -846,8 +620,7 @@ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
846
620
  case 'create-agent':
847
621
  return {
848
622
  description: 'Create a new OSSA agent',
849
- messages: [
850
- {
623
+ messages: [{
851
624
  role: 'user',
852
625
  content: {
853
626
  type: 'text',
@@ -857,14 +630,12 @@ Use the ossa_scaffold tool to create the directory structure, then customize the
857
630
 
858
631
  After scaffolding, read the manifest, customize it with appropriate tools, capabilities, and LLM config, then validate it with ossa_validate.`,
859
632
  },
860
- },
861
- ],
633
+ }],
862
634
  };
863
635
  case 'convert-for-platform':
864
636
  return {
865
637
  description: 'Convert manifest to target platform',
866
- messages: [
867
- {
638
+ messages: [{
868
639
  role: 'user',
869
640
  content: {
870
641
  type: 'text',
@@ -872,14 +643,12 @@ After scaffolding, read the manifest, customize it with appropriate tools, capab
872
643
 
873
644
  Use ossa_convert with the target platform. If converting to agent-card, explain the cross-platform adapters available (OpenAI, Anthropic, LangChain, CrewAI, etc.) and the SDK packages needed.`,
874
645
  },
875
- },
876
- ],
646
+ }],
877
647
  };
878
648
  case 'explain-manifest':
879
649
  return {
880
650
  description: 'Explain an OSSA manifest',
881
- messages: [
882
- {
651
+ messages: [{
883
652
  role: 'user',
884
653
  content: {
885
654
  type: 'text',
@@ -890,14 +659,12 @@ Use ossa_convert with the target platform. If converting to agent-card, explain
890
659
  4. Its autonomy level and safety constraints
891
660
  5. How it can be deployed (platforms it supports)`,
892
661
  },
893
- },
894
- ],
662
+ }],
895
663
  };
896
664
  case 'what-is-ossa':
897
665
  return {
898
666
  description: 'Explain OSSA',
899
- messages: [
900
- {
667
+ messages: [{
901
668
  role: 'user',
902
669
  content: {
903
670
  type: 'text',
@@ -910,8 +677,7 @@ Read the ossa://guide/mcp-ossa-a2a resource for the full explanation. Key points
910
677
 
911
678
  Show the supported platforms using the ossa://platforms/supported resource.`,
912
679
  },
913
- },
914
- ],
680
+ }],
915
681
  };
916
682
  default:
917
683
  throw new Error(`Unknown prompt: ${request.params.name}`);
@@ -1006,10 +772,7 @@ async function handleValidate(args) {
1006
772
  }
1007
773
  // Merge platform results into base results
1008
774
  if (platformErrors.length) {
1009
- result.errors = [
1010
- ...(result.errors || []),
1011
- ...platformErrors,
1012
- ];
775
+ result.errors = [...(result.errors || []), ...platformErrors];
1013
776
  result.valid = false;
1014
777
  }
1015
778
  if (platformWarnings.length) {
@@ -1020,10 +783,7 @@ async function handleValidate(args) {
1020
783
  // Strict mode: promote warnings to errors
1021
784
  if (input.strict && result.warnings?.length) {
1022
785
  const promoted = result.warnings.map((w) => `[strict] ${w}`);
1023
- result.errors = [
1024
- ...(result.errors || []),
1025
- ...promoted,
1026
- ];
786
+ result.errors = [...(result.errors || []), ...promoted];
1027
787
  result.valid = !result.errors?.length;
1028
788
  }
1029
789
  return okResponse({
@@ -1032,13 +792,11 @@ async function handleValidate(args) {
1032
792
  warnings: result.warnings || [],
1033
793
  manifest_path: manifestPath,
1034
794
  platform: input.platform || null,
1035
- platform_validation: input.platform
1036
- ? {
1037
- platform: input.platform,
1038
- errors: platformErrors,
1039
- warnings: platformWarnings,
1040
- }
1041
- : undefined,
795
+ platform_validation: input.platform ? {
796
+ platform: input.platform,
797
+ errors: platformErrors,
798
+ warnings: platformWarnings,
799
+ } : undefined,
1042
800
  });
1043
801
  }
1044
802
  // ---------------------------------------------------------------------------
@@ -1060,17 +818,11 @@ async function handleScaffold(args) {
1060
818
  name: input.name,
1061
819
  version: input.version || getDefaultAgentVersion(),
1062
820
  description: input.description || getDefaultDescriptionTemplate(input.name),
1063
- mesh_bindings: {
1064
- source_project_id: input.source_project_id || process.env.OSSA_DEFAULT_SOURCE_PROJECT_ID || '',
1065
- execution_node_id: input.execution_node_id || process.env.OSSA_DEFAULT_EXECUTION_NODE_ID || '',
1066
- },
1067
821
  },
1068
822
  spec: {
1069
823
  role: input.role || getDefaultRoleTemplate(input.name),
1070
824
  llm: { provider: 'openai', model: '${LLM_MODEL:-gpt-4}' },
1071
- tools: typeConfig.capabilityName
1072
- ? [{ type: 'capability', name: typeConfig.capabilityName }]
1073
- : [],
825
+ tools: typeConfig.capabilityName ? [{ type: 'capability', name: typeConfig.capabilityName }] : [],
1074
826
  },
1075
827
  };
1076
828
  // Create directory structure
@@ -1108,11 +860,7 @@ async function handleGenerate(args) {
1108
860
  fs.mkdirSync(wellKnown, { recursive: true });
1109
861
  const cardPath = path.join(wellKnown, 'agent-card.json');
1110
862
  fs.writeFileSync(cardPath, result.json ?? JSON.stringify(result.card, null, 2), 'utf8');
1111
- return okResponse({
1112
- success: true,
1113
- agent_card: result.card,
1114
- written_to: cardPath,
1115
- });
863
+ return okResponse({ success: true, agent_card: result.card, written_to: cardPath });
1116
864
  }
1117
865
  return okResponse({ success: true, agent_card: result.card });
1118
866
  }
@@ -1131,9 +879,7 @@ async function handlePublish(args) {
1131
879
  };
1132
880
  // Dry run — show what would happen for both local and remote
1133
881
  if (input.dry_run) {
1134
- const registryUrl = input.registry_url ||
1135
- process.env.REGISTRY_URL ||
1136
- process.env.AGENT_REGISTRY_URL;
882
+ const registryUrl = input.registry_url || process.env.REGISTRY_URL || process.env.AGENT_REGISTRY_URL;
1137
883
  return okResponse({
1138
884
  dry_run: true,
1139
885
  payload,
@@ -1143,10 +889,7 @@ async function handlePublish(args) {
1143
889
  version: manifest.metadata?.version || '1.0.0',
1144
890
  },
1145
891
  remote_publish: registryUrl
1146
- ? {
1147
- registry_url: registryUrl,
1148
- url: registryUrl.replace(/\/?$/, '/api/v1/agents'),
1149
- }
892
+ ? { registry_url: registryUrl, url: registryUrl.replace(/\/?$/, '/api/v1/agents') }
1150
893
  : null,
1151
894
  message: 'Payload that would be sent to local and/or remote registry',
1152
895
  });
@@ -1173,9 +916,7 @@ async function handlePublish(args) {
1173
916
  error: err instanceof Error ? err.message : String(err),
1174
917
  };
1175
918
  }
1176
- const registryUrl = input.registry_url ||
1177
- process.env.REGISTRY_URL ||
1178
- process.env.AGENT_REGISTRY_URL;
919
+ const registryUrl = input.registry_url || process.env.REGISTRY_URL || process.env.AGENT_REGISTRY_URL;
1179
920
  // Remote HTTP registry (if configured)
1180
921
  if (registryUrl) {
1181
922
  const url = registryUrl.replace(/\/?$/, '/api/v1/agents');
@@ -1186,74 +927,66 @@ async function handlePublish(args) {
1186
927
  });
1187
928
  return okResponse({
1188
929
  success: res.status >= 200 && res.status < 300,
1189
- status: res.status,
1190
- registry_url: registryUrl,
1191
- data: res.data,
1192
- local_publish: localResult,
1193
- });
1194
- }
1195
- // arctl CLI fallback
1196
- const arctl = spawnSync('arctl', ['publish', manifestPath], {
1197
- encoding: 'utf8',
1198
- timeout: 30000,
1199
- });
1200
- if (arctl.status === 0) {
1201
- return okResponse({
1202
- success: true,
1203
- method: 'arctl',
1204
- stdout: arctl.stdout?.trim(),
1205
- local_publish: localResult,
1206
- });
1207
- }
1208
- // No remote configured — return local result
1209
- if (localResult?.success) {
1210
- return okResponse({
1211
- success: true,
1212
- method: 'local',
1213
- local_publish: localResult,
1214
- message: 'Published to local registry. Set REGISTRY_URL for remote publish.',
930
+ local: localResult,
931
+ remote: {
932
+ success: res.status >= 200 && res.status < 300,
933
+ status: res.status,
934
+ registry_url: registryUrl,
935
+ data: res.data,
936
+ },
1215
937
  });
1216
938
  }
939
+ // No remote registry — return local result only
1217
940
  return okResponse({
1218
- published: false,
1219
- message: 'No registry configured. Set REGISTRY_URL or install arctl.',
1220
- local_publish: localResult,
1221
- next_steps: [
1222
- 'Set REGISTRY_URL or AGENT_REGISTRY_URL environment variable',
1223
- 'Or install arctl: npm install -g @agentregistry/arctl',
1224
- 'Then: arctl publish ' + manifestPath,
1225
- ],
941
+ success: localResult?.success === true,
942
+ local: localResult,
943
+ remote: null,
944
+ message: localResult?.success
945
+ ? 'Published to local registry. Set REGISTRY_URL for remote publishing.'
946
+ : 'Local publish failed. Set REGISTRY_URL for remote publishing.',
1226
947
  });
1227
948
  }
1228
949
  // ---------------------------------------------------------------------------
1229
- // ossa_list — workspace discovery via shared manifest scanner
1230
- // Uses src/utils/manifest-scanner.ts for consistent glob patterns and parsing.
950
+ // ossa_list — workspace discovery via fast-glob
951
+ // HACK: Inline fast-glob scan instead of AgentsMdDiscoveryService.
952
+ // The DI service does heavier work (repo analysis, AGENTS.md generation)
953
+ // that we don't need for a simple MCP list response.
954
+ // TODO(#443): Extract lightweight scan into a shared utility.
1231
955
  // ---------------------------------------------------------------------------
1232
956
  async function handleList(args) {
1233
957
  const input = ListInput.parse(args);
1234
958
  const baseDir = resolvePath(input.directory);
1235
- const results = await scanManifests(baseDir, {
1236
- recursive: input.recursive,
1237
- includeAgentsDirs: true,
959
+ const patterns = input.recursive
960
+ ? ['**/*.ossa.yaml', '**/*.ossa.yml', '**/.agents/*/manifest.ossa.yaml']
961
+ : ['*.ossa.yaml', '*.ossa.yml', '.agents/*/manifest.ossa.yaml'];
962
+ const files = await fg(patterns, {
963
+ cwd: baseDir,
1238
964
  absolute: true,
965
+ ignore: ['**/node_modules/**', '**/dist/**', '**/.git/**'],
1239
966
  });
1240
- const agents = results.map((r) => r.error
1241
- ? { name: r.name, path: r.path, error: r.error }
1242
- : {
1243
- name: r.name,
1244
- version: r.version,
1245
- path: r.path,
1246
- kind: r.kind,
1247
- apiVersion: r.apiVersion,
1248
- description: r.description,
1249
- });
967
+ const agents = [];
968
+ for (const file of files) {
969
+ try {
970
+ const raw = fs.readFileSync(file, 'utf8');
971
+ const doc = yaml.load(raw);
972
+ const meta = doc.metadata;
973
+ agents.push({
974
+ name: meta?.name || path.basename(path.dirname(file)),
975
+ version: meta?.version || 'unknown',
976
+ path: file,
977
+ kind: doc.kind || 'Agent',
978
+ apiVersion: doc.apiVersion || 'unknown',
979
+ description: meta?.description || '',
980
+ });
981
+ }
982
+ catch {
983
+ agents.push({ name: path.basename(file), path: file, error: 'Failed to parse' });
984
+ }
985
+ }
1250
986
  if (input.format === 'json') {
1251
987
  return okResponse({ count: agents.length, agents });
1252
988
  }
1253
989
  if (input.format === 'detailed') {
1254
- const patterns = input.recursive
1255
- ? ['**/*.ossa.yaml', '**/*.ossa.yml', '**/.agents/*/manifest.ossa.yaml']
1256
- : ['*.ossa.yaml', '*.ossa.yml', '.agents/*/manifest.ossa.yaml'];
1257
990
  return okResponse({
1258
991
  count: agents.length,
1259
992
  agents,
@@ -1304,21 +1037,12 @@ async function handleInspect(args) {
1304
1037
  name: meta?.name,
1305
1038
  version: versionStr,
1306
1039
  version_analysis: parsed
1307
- ? {
1308
- major: parsed.major,
1309
- minor: parsed.minor,
1310
- patch: parsed.patch,
1311
- prerelease: parsed.prerelease,
1312
- }
1040
+ ? { major: parsed.major, minor: parsed.minor, patch: parsed.patch, prerelease: parsed.prerelease }
1313
1041
  : null,
1314
1042
  kind: manifest.kind,
1315
1043
  apiVersion: manifest.apiVersion,
1316
- mesh_bindings: meta?.mesh_bindings || null,
1317
1044
  description: meta?.description,
1318
- role: spec?.role
1319
- ? String(spec.role).substring(0, 200) +
1320
- (String(spec.role).length > 200 ? '...' : '')
1321
- : null,
1045
+ role: spec?.role ? String(spec.role).substring(0, 200) + (String(spec.role).length > 200 ? '...' : '') : null,
1322
1046
  llm: spec?.llm || null,
1323
1047
  tools: toolSummary,
1324
1048
  tool_count: specTools.length,
@@ -1339,524 +1063,552 @@ async function handleInspect(args) {
1339
1063
  // ---------------------------------------------------------------------------
1340
1064
  // ossa_convert — export to target platform format
1341
1065
  //
1342
- // Most targets delegate to the adapter registry's toConfig() method (#442).
1343
- // a2a/agent-card and claude-agent-sdk remain inline due to their complexity
1344
- // (cross-platform card generation and SDK-specific config respectively).
1066
+ // HACK: These inline adapters produce lightweight JSON configs suitable for
1067
+ // MCP tool responses. The CLI export adapters (src/adapters/) generate full
1068
+ // multi-file project scaffolds (Dockerfile, docker-compose, Python packages,
1069
+ // etc.) which are too heavy for MCP responses. This is intentional divergence.
1070
+ // TODO(#442): Create a shared "config-only" export mode in the adapter classes
1071
+ // so both CLI and MCP can use the same code path.
1345
1072
  // ---------------------------------------------------------------------------
1346
1073
  async function handleConvert(args) {
1347
1074
  const input = ConvertInput.parse(args);
1348
1075
  const manifestPath = resolvePath(input.path);
1349
1076
  const manifest = await manifestRepo.load(manifestPath);
1350
- const meta = manifest.metadata || {
1351
- name: path.basename(manifestPath, '.ossa.yaml'),
1352
- version: '0.0.0',
1353
- };
1077
+ const meta = manifest.metadata || { name: path.basename(manifestPath, '.ossa.yaml'), version: '0.0.0' };
1354
1078
  let converted;
1355
1079
  let filename;
1356
- // a2a/agent-card is special: complex cross-platform card stays inline
1357
- if (input.target === 'a2a' || input.target === 'agent-card') {
1358
- // Build comprehensive cross-platform agent card
1359
- // This is THE universal discovery format: MCP → OSSA → A2A
1360
- let cardResult = {
1361
- success: false,
1362
- errors: [],
1363
- };
1364
- try {
1365
- cardResult = agentCardGenerator.generate(manifest);
1080
+ switch (input.target) {
1081
+ case 'kagent': {
1082
+ // Generate kagent.dev v1alpha2 format (actual CRD that kagent controller understands)
1083
+ const kagentExtensions = manifest.extensions;
1084
+ const kagentExt = kagentExtensions?.kagent;
1085
+ const kagentNs = kagentExt?.kubernetes?.namespace || 'kagent';
1086
+ const a2aConfig = kagentExt?.a2aConfig;
1087
+ // Map OSSA tools to kagent McpServer tool format
1088
+ const ossaTools = (manifest.spec?.tools || []);
1089
+ const kagentTools = ossaTools.map((t) => {
1090
+ if (t.type === 'mcp') {
1091
+ return {
1092
+ type: 'McpServer',
1093
+ mcpServer: {
1094
+ name: t.server || t.name || 'tool-server',
1095
+ kind: 'RemoteMCPServer',
1096
+ ...(t.toolNames ? { toolNames: t.toolNames } : {}),
1097
+ },
1098
+ };
1099
+ }
1100
+ return {
1101
+ type: 'McpServer',
1102
+ mcpServer: {
1103
+ name: t.name || 'tool-server',
1104
+ kind: 'RemoteMCPServer',
1105
+ },
1106
+ };
1107
+ });
1108
+ // Build the v1alpha2 Agent CRD
1109
+ converted = {
1110
+ apiVersion: 'kagent.dev/v1alpha2',
1111
+ kind: 'Agent',
1112
+ metadata: {
1113
+ name: meta.name,
1114
+ namespace: kagentNs,
1115
+ labels: {
1116
+ 'ossa.dev/name': meta.name,
1117
+ 'ossa.dev/version': meta.version || '0.0.0',
1118
+ 'app.kubernetes.io/managed-by': 'ossa',
1119
+ },
1120
+ },
1121
+ spec: {
1122
+ description: meta.description || '',
1123
+ type: 'Declarative',
1124
+ declarative: {
1125
+ modelConfig: `${meta.name}-model-config`,
1126
+ systemMessage: manifest.spec?.role || '',
1127
+ tools: kagentTools,
1128
+ ...(a2aConfig ? { a2aConfig } : {}),
1129
+ },
1130
+ },
1131
+ };
1132
+ // Also generate the ModelConfig companion resource
1133
+ const modelConfig = {
1134
+ apiVersion: 'kagent.dev/v1alpha2',
1135
+ kind: 'ModelConfig',
1136
+ metadata: {
1137
+ name: `${meta.name}-model-config`,
1138
+ namespace: kagentNs,
1139
+ },
1140
+ spec: {
1141
+ provider: manifest.spec?.llm?.provider || 'openai',
1142
+ model: manifest.spec?.llm?.model || 'gpt-4',
1143
+ ...(manifest.spec?.llm?.temperature != null ? { temperature: manifest.spec.llm.temperature } : {}),
1144
+ ...(manifest.spec?.llm?.maxTokens != null ? { maxTokens: manifest.spec.llm.maxTokens } : {}),
1145
+ },
1146
+ };
1147
+ // Return both resources in a multi-document YAML
1148
+ converted = {
1149
+ _ossa_multi_resource: true,
1150
+ resources: [converted, modelConfig],
1151
+ agent: converted,
1152
+ modelConfig,
1153
+ };
1154
+ filename = `${meta.name}.kagent.yaml`;
1155
+ break;
1366
1156
  }
1367
- catch {
1368
- // AgentCardGenerator may fail on some manifests — continue building the card
1157
+ case 'docker': {
1158
+ converted = {
1159
+ version: '3.8',
1160
+ services: {
1161
+ [meta.name]: {
1162
+ image: `ossa/${meta.name}:${meta.version || 'latest'}`,
1163
+ environment: {
1164
+ AGENT_NAME: meta.name,
1165
+ LLM_PROVIDER: manifest.spec?.llm?.provider || 'openai',
1166
+ LLM_MODEL: manifest.spec?.llm?.model || 'gpt-4',
1167
+ },
1168
+ labels: {
1169
+ 'ossa.name': meta.name,
1170
+ 'ossa.version': meta.version || '0.0.0',
1171
+ 'ossa.kind': manifest.kind || 'Agent',
1172
+ },
1173
+ },
1174
+ },
1175
+ };
1176
+ filename = `docker-compose.${meta.name}.yml`;
1177
+ break;
1369
1178
  }
1370
- const tools = (manifest.spec?.tools || []);
1371
- const role = manifest.spec?.role || '';
1372
- const llm = manifest.spec?.llm;
1373
- const provider = llm?.provider || 'openai';
1374
- const model = llm?.model || 'gpt-4';
1375
- const description = meta.description || '';
1376
- // Build universal tool schemas for cross-platform use
1377
- const universalTools = tools.map((t) => ({
1378
- name: t.name || 'unnamed',
1379
- description: t.description || '',
1380
- inputSchema: (t.inputSchema ||
1381
- t.input_schema ||
1382
- t.parameters || { type: 'object', properties: {} }),
1383
- ...(t.outputSchema || t.output_schema
1384
- ? { outputSchema: t.outputSchema || t.output_schema }
1385
- : {}),
1386
- ...(t.type ? { type: t.type } : {}),
1387
- ...(t.server ? { mcpServer: t.server } : {}),
1388
- }));
1389
- // A2A skills from tools (Google A2A agent card format)
1390
- const a2aSkills = universalTools.map((t) => ({
1391
- id: t.name,
1392
- name: t.name,
1393
- description: t.description,
1394
- ...(t.inputSchema ? { inputModes: ['application/json'] } : {}),
1395
- outputModes: ['application/json', 'text/plain'],
1396
- }));
1397
- // MCP tool format (Model Context Protocol)
1398
- const mcpTools = universalTools.map((t) => ({
1399
- name: t.name,
1400
- description: t.description,
1401
- inputSchema: t.inputSchema,
1402
- }));
1403
- // OpenAI function_calling format
1404
- const openaiTools = universalTools.map((t) => ({
1405
- type: 'function',
1406
- function: {
1407
- name: t.name,
1408
- description: t.description,
1409
- parameters: t.inputSchema,
1410
- },
1411
- }));
1412
- // Anthropic tool_use format
1413
- const anthropicTools = universalTools.map((t) => ({
1414
- name: t.name,
1415
- description: t.description,
1416
- input_schema: t.inputSchema,
1417
- }));
1418
- // LangChain StructuredTool format
1419
- const langchainTools = universalTools.map((t) => ({
1420
- _type: 'structured_tool',
1421
- name: t.name,
1422
- description: t.description,
1423
- args_schema: t.inputSchema,
1424
- }));
1425
- // CrewAI tool format
1426
- const crewaiTools = universalTools.map((t) => ({
1427
- name: t.name,
1428
- description: t.description,
1429
- ...(t.inputSchema ? { args_schema: t.inputSchema } : {}),
1430
- }));
1431
- // LangFlow component format
1432
- const langflowComponent = {
1433
- display_name: meta.name,
1434
- description,
1435
- documentation: `https://openstandardagents.org/agents/${meta.name}`,
1436
- template: {
1437
- system_message: { type: 'str', value: role },
1438
- model_name: { type: 'str', value: model },
1439
- provider: { type: 'str', value: provider },
1440
- tools: { type: 'list', value: universalTools.map((t) => t.name) },
1441
- },
1442
- };
1443
- // AutoGen agent format
1444
- const autogenConfig = {
1445
- name: meta.name,
1446
- description,
1447
- system_message: role,
1448
- llm_config: {
1449
- config_list: [
1179
+ case 'langchain': {
1180
+ converted = {
1181
+ _type: 'agent',
1182
+ name: meta.name,
1183
+ description: meta.description || '',
1184
+ llm: {
1185
+ _type: manifest.spec?.llm?.provider === 'anthropic' ? 'ChatAnthropic' : 'ChatOpenAI',
1186
+ model_name: manifest.spec?.llm?.model || 'gpt-4',
1187
+ },
1188
+ system_message: manifest.spec?.role || '',
1189
+ tools: (manifest.spec?.tools || []).map((t) => ({
1190
+ name: t.name,
1191
+ type: t.type,
1192
+ })),
1193
+ };
1194
+ filename = `${meta.name}.langchain.json`;
1195
+ break;
1196
+ }
1197
+ case 'crewai': {
1198
+ converted = {
1199
+ agents: [
1450
1200
  {
1451
- model,
1452
- api_type: provider === 'anthropic' ? 'anthropic' : 'openai',
1201
+ role: meta.name,
1202
+ goal: meta.description || '',
1203
+ backstory: manifest.spec?.role || '',
1204
+ llm: manifest.spec?.llm?.model || 'gpt-4',
1205
+ tools: (manifest.spec?.tools || []).map((t) => t.name),
1453
1206
  },
1454
1207
  ],
1455
- },
1456
- ...(universalTools.length > 0
1457
- ? {
1458
- tools: universalTools.map((t) => ({
1459
- name: t.name,
1460
- description: t.description,
1461
- })),
1462
- }
1463
- : {}),
1464
- };
1465
- // Semantic Kernel agent format
1466
- const semanticKernelConfig = {
1467
- name: meta.name,
1468
- description,
1469
- instructions: role,
1470
- model: { id: model, provider },
1471
- plugins: universalTools.map((t) => ({
1472
- name: t.name,
1473
- description: t.description,
1474
- parameters: t.inputSchema,
1475
- })),
1476
- };
1477
- // kagent.dev CRD reference (not full CRD — use target: kagent for that)
1478
- const kagentRef = {
1479
- apiVersion: 'kagent.dev/v1alpha2',
1480
- kind: 'Agent',
1481
- metadata: { name: meta.name },
1482
- spec: {
1483
- type: 'Declarative',
1484
- declarative: {
1485
- modelConfig: `${meta.name}-model-config`,
1486
- systemMessage: role,
1208
+ };
1209
+ filename = `${meta.name}.crewai.yaml`;
1210
+ break;
1211
+ }
1212
+ case 'gitlab-duo': {
1213
+ converted = {
1214
+ name: meta.name,
1215
+ description: meta.description || '',
1216
+ system_prompt: manifest.spec?.role || '',
1217
+ model: manifest.spec?.llm?.model || 'claude-sonnet-4-20250514',
1218
+ tools: (manifest.spec?.tools || []).map((t) => ({
1219
+ name: t.name,
1220
+ })),
1221
+ };
1222
+ filename = `${meta.name}.duo-agent.yaml`;
1223
+ break;
1224
+ }
1225
+ case 'anthropic': {
1226
+ converted = {
1227
+ name: meta.name,
1228
+ description: meta.description || '',
1229
+ model: manifest.spec?.llm?.model || 'claude-sonnet-4-20250514',
1230
+ system: manifest.spec?.role || '',
1231
+ tools: (manifest.spec?.tools || []).map((t) => ({
1232
+ name: t.name,
1233
+ description: '',
1234
+ input_schema: { type: 'object', properties: {} },
1235
+ })),
1236
+ max_tokens: 4096,
1237
+ };
1238
+ filename = `${meta.name}.anthropic.json`;
1239
+ break;
1240
+ }
1241
+ case 'openai': {
1242
+ // OpenAI Assistants / function_calling format
1243
+ const oaiTools = (manifest.spec?.tools || []);
1244
+ converted = {
1245
+ model: manifest.spec?.llm?.model || 'gpt-4',
1246
+ name: meta.name,
1247
+ description: meta.description || '',
1248
+ instructions: manifest.spec?.role || '',
1249
+ tools: oaiTools.map((t) => ({
1250
+ type: 'function',
1251
+ function: {
1252
+ name: t.name || 'unnamed',
1253
+ description: t.description || '',
1254
+ parameters: t.inputSchema || t.input_schema || t.parameters || { type: 'object', properties: {} },
1255
+ },
1256
+ })),
1257
+ metadata: {
1258
+ ossa_version: manifest.apiVersion || 'ossa/v0.4',
1259
+ ossa_name: meta.name,
1487
1260
  },
1488
- },
1489
- };
1490
- // Extensions from OSSA manifest
1491
- const extensions = manifest.extensions;
1492
- const a2aExt = extensions?.a2a;
1493
- const mcpExt = extensions?.mcp;
1494
- // Build the comprehensive agent card
1495
- converted = {
1496
- // --- Core identity (OSSA source of truth) ---
1497
- name: meta.name,
1498
- version: meta.version || '1.0.0',
1499
- description,
1500
- url: `https://openstandardagents.org/agents/${meta.name}`,
1501
- ossaVersion: manifest.apiVersion || 'ossa/v0.4',
1502
- kind: manifest.kind || 'Agent',
1503
- // --- The OSSA contract (identity + capabilities + constraints) ---
1504
- ossa: {
1505
- role,
1506
- capabilities: (manifest.spec?.capabilities || []),
1507
- ...(manifest.spec?.autonomy
1508
- ? { autonomy: manifest.spec.autonomy }
1509
- : {}),
1510
- ...(manifest.spec?.safety ? { safety: manifest.spec.safety } : {}),
1511
- ...(manifest.spec?.observability
1512
- ? { observability: manifest.spec.observability }
1513
- : {}),
1514
- ...(manifest.spec?.access
1515
- ? { access: manifest.spec.access }
1261
+ };
1262
+ filename = `${meta.name}.openai.json`;
1263
+ break;
1264
+ }
1265
+ case 'autogen': {
1266
+ // Microsoft AutoGen agent config
1267
+ const autogenTools = (manifest.spec?.tools || []);
1268
+ converted = {
1269
+ name: meta.name,
1270
+ description: meta.description || '',
1271
+ system_message: manifest.spec?.role || '',
1272
+ llm_config: {
1273
+ config_list: [
1274
+ {
1275
+ model: manifest.spec?.llm?.model || 'gpt-4',
1276
+ api_type: manifest.spec?.llm?.provider === 'anthropic' ? 'anthropic' : 'openai',
1277
+ },
1278
+ ],
1279
+ ...(manifest.spec?.llm?.temperature != null ? { temperature: manifest.spec.llm.temperature } : {}),
1280
+ ...(manifest.spec?.llm?.maxTokens != null ? { max_tokens: manifest.spec.llm.maxTokens } : {}),
1281
+ },
1282
+ ...(autogenTools.length > 0
1283
+ ? {
1284
+ tools: autogenTools.map((t) => ({
1285
+ name: t.name || 'unnamed',
1286
+ description: t.description || '',
1287
+ ...(t.inputSchema || t.input_schema ? { parameters: t.inputSchema || t.input_schema } : {}),
1288
+ })),
1289
+ }
1516
1290
  : {}),
1517
- },
1518
- // --- A2A discovery (Google Agent-to-Agent protocol) ---
1519
- capabilities: {
1520
- streaming: true,
1521
- pushNotifications: false,
1522
- stateTransitionHistory: true,
1523
- },
1524
- skills: a2aSkills,
1525
- defaultInputModes: ['application/json', 'text/plain'],
1526
- defaultOutputModes: ['application/json', 'text/plain'],
1527
- // --- MCP tools (Model Context Protocol) ---
1528
- mcp: {
1529
- tools: mcpTools,
1530
- ...(mcpExt?.servers ? { servers: mcpExt.servers } : {}),
1531
- ...(mcpExt?.resources ? { resources: mcpExt.resources } : {}),
1532
- },
1533
- // --- Platform adapters (use these directly in your framework) ---
1534
- // Each adapter includes: sdk (npm/pip packages), config (ready-to-use), usage (code snippet)
1535
- adapters: {
1536
- openai: {
1537
- sdk: {
1538
- npm: 'openai',
1539
- pip: 'openai',
1540
- docs: 'https://platform.openai.com/docs/api-reference',
1541
- },
1542
- config: {
1543
- model,
1544
- messages: [{ role: 'system', content: role }],
1545
- tools: openaiTools,
1546
- tool_choice: 'auto',
1547
- },
1548
- usage: `import OpenAI from 'openai';\nconst client = new OpenAI();\nconst response = await client.chat.completions.create(config);`,
1291
+ metadata: {
1292
+ ossa_version: manifest.apiVersion || 'ossa/v0.4',
1549
1293
  },
1550
- anthropic: {
1551
- sdk: {
1552
- npm: '@anthropic-ai/sdk',
1553
- pip: 'anthropic',
1554
- docs: 'https://docs.anthropic.com/en/api',
1555
- },
1556
- config: {
1557
- model: provider === 'anthropic' ? model : 'claude-sonnet-4-20250514',
1558
- system: role,
1559
- tools: anthropicTools,
1560
- max_tokens: llm?.maxTokens || 4096,
1294
+ };
1295
+ filename = `${meta.name}.autogen.json`;
1296
+ break;
1297
+ }
1298
+ case 'semantic-kernel': {
1299
+ // Microsoft Semantic Kernel agent config
1300
+ const skTools = (manifest.spec?.tools || []);
1301
+ converted = {
1302
+ name: meta.name,
1303
+ description: meta.description || '',
1304
+ instructions: manifest.spec?.role || '',
1305
+ execution_settings: {
1306
+ default: {
1307
+ model_id: manifest.spec?.llm?.model || 'gpt-4',
1308
+ service_id: manifest.spec?.llm?.provider || 'openai',
1309
+ ...(manifest.spec?.llm?.temperature != null ? { temperature: manifest.spec.llm.temperature } : {}),
1310
+ ...(manifest.spec?.llm?.maxTokens != null ? { max_tokens: manifest.spec.llm.maxTokens } : {}),
1561
1311
  },
1562
- usage: `import Anthropic from '@anthropic-ai/sdk';\nconst client = new Anthropic();\nconst response = await client.messages.create(config);`,
1563
1312
  },
1564
- google_genai: {
1565
- sdk: {
1566
- npm: '@google/generative-ai',
1567
- pip: 'google-generativeai',
1568
- docs: 'https://ai.google.dev/docs',
1569
- },
1570
- config: {
1571
- model: 'gemini-2.0-flash',
1572
- systemInstruction: role,
1573
- tools: [
1574
- { functionDeclarations: openaiTools.map((t) => t.function) },
1575
- ],
1313
+ plugins: skTools.map((t) => ({
1314
+ name: t.name || 'unnamed',
1315
+ description: t.description || '',
1316
+ parameters: t.inputSchema || t.input_schema || t.parameters || { type: 'object', properties: {} },
1317
+ })),
1318
+ metadata: {
1319
+ ossa_version: manifest.apiVersion || 'ossa/v0.4',
1320
+ },
1321
+ };
1322
+ filename = `${meta.name}.semantic-kernel.json`;
1323
+ break;
1324
+ }
1325
+ case 'a2a':
1326
+ case 'agent-card': {
1327
+ // Build comprehensive cross-platform agent card
1328
+ // This is THE universal discovery format: MCP → OSSA → A2A
1329
+ let cardResult = { success: false, errors: [] };
1330
+ try {
1331
+ cardResult = agentCardGenerator.generate(manifest);
1332
+ }
1333
+ catch {
1334
+ // AgentCardGenerator may fail on some manifests — continue building the card
1335
+ }
1336
+ const tools = (manifest.spec?.tools || []);
1337
+ const role = manifest.spec?.role || '';
1338
+ const llm = manifest.spec?.llm;
1339
+ const provider = llm?.provider || 'openai';
1340
+ const model = llm?.model || 'gpt-4';
1341
+ const description = meta.description || '';
1342
+ // Build universal tool schemas for cross-platform use
1343
+ const universalTools = tools.map((t) => ({
1344
+ name: t.name || 'unnamed',
1345
+ description: t.description || '',
1346
+ inputSchema: (t.inputSchema || t.input_schema || t.parameters || { type: 'object', properties: {} }),
1347
+ ...(t.outputSchema || t.output_schema ? { outputSchema: t.outputSchema || t.output_schema } : {}),
1348
+ ...(t.type ? { type: t.type } : {}),
1349
+ ...(t.server ? { mcpServer: t.server } : {}),
1350
+ }));
1351
+ // A2A skills from tools (Google A2A agent card format)
1352
+ const a2aSkills = universalTools.map((t) => ({
1353
+ id: t.name,
1354
+ name: t.name,
1355
+ description: t.description,
1356
+ ...(t.inputSchema ? { inputModes: ['application/json'] } : {}),
1357
+ outputModes: ['application/json', 'text/plain'],
1358
+ }));
1359
+ // MCP tool format (Model Context Protocol)
1360
+ const mcpTools = universalTools.map((t) => ({
1361
+ name: t.name,
1362
+ description: t.description,
1363
+ inputSchema: t.inputSchema,
1364
+ }));
1365
+ // OpenAI function_calling format
1366
+ const openaiTools = universalTools.map((t) => ({
1367
+ type: 'function',
1368
+ function: {
1369
+ name: t.name,
1370
+ description: t.description,
1371
+ parameters: t.inputSchema,
1372
+ },
1373
+ }));
1374
+ // Anthropic tool_use format
1375
+ const anthropicTools = universalTools.map((t) => ({
1376
+ name: t.name,
1377
+ description: t.description,
1378
+ input_schema: t.inputSchema,
1379
+ }));
1380
+ // LangChain StructuredTool format
1381
+ const langchainTools = universalTools.map((t) => ({
1382
+ _type: 'structured_tool',
1383
+ name: t.name,
1384
+ description: t.description,
1385
+ args_schema: t.inputSchema,
1386
+ }));
1387
+ // CrewAI tool format
1388
+ const crewaiTools = universalTools.map((t) => ({
1389
+ name: t.name,
1390
+ description: t.description,
1391
+ ...(t.inputSchema ? { args_schema: t.inputSchema } : {}),
1392
+ }));
1393
+ // LangFlow component format
1394
+ const langflowComponent = {
1395
+ display_name: meta.name,
1396
+ description,
1397
+ documentation: `https://openstandardagents.org/agents/${meta.name}`,
1398
+ template: {
1399
+ system_message: { type: 'str', value: role },
1400
+ model_name: { type: 'str', value: model },
1401
+ provider: { type: 'str', value: provider },
1402
+ tools: { type: 'list', value: universalTools.map((t) => t.name) },
1403
+ },
1404
+ };
1405
+ // AutoGen agent format
1406
+ const autogenConfig = {
1407
+ name: meta.name,
1408
+ description,
1409
+ system_message: role,
1410
+ llm_config: {
1411
+ config_list: [{ model, api_type: provider === 'anthropic' ? 'anthropic' : 'openai' }],
1412
+ },
1413
+ ...(universalTools.length > 0 ? { tools: universalTools.map((t) => ({ name: t.name, description: t.description })) } : {}),
1414
+ };
1415
+ // Semantic Kernel agent format
1416
+ const semanticKernelConfig = {
1417
+ name: meta.name,
1418
+ description,
1419
+ instructions: role,
1420
+ model: { id: model, provider },
1421
+ plugins: universalTools.map((t) => ({
1422
+ name: t.name,
1423
+ description: t.description,
1424
+ parameters: t.inputSchema,
1425
+ })),
1426
+ };
1427
+ // kagent.dev CRD reference (not full CRD — use target: kagent for that)
1428
+ const kagentRef = {
1429
+ apiVersion: 'kagent.dev/v1alpha2',
1430
+ kind: 'Agent',
1431
+ metadata: { name: meta.name },
1432
+ spec: {
1433
+ type: 'Declarative',
1434
+ declarative: {
1435
+ modelConfig: `${meta.name}-model-config`,
1436
+ systemMessage: role,
1576
1437
  },
1577
- usage: `import { GoogleGenerativeAI } from '@google/generative-ai';\nconst genAI = new GoogleGenerativeAI(apiKey);\nconst model = genAI.getGenerativeModel(config);`,
1578
1438
  },
1579
- langchain: {
1580
- sdk: {
1581
- npm: [
1582
- 'langchain',
1583
- '@langchain/core',
1584
- '@langchain/openai',
1585
- '@langchain/anthropic',
1586
- ],
1587
- pip: ['langchain', 'langchain-openai', 'langchain-anthropic'],
1588
- docs: 'https://js.langchain.com/docs/',
1439
+ };
1440
+ // Extensions from OSSA manifest
1441
+ const extensions = manifest.extensions;
1442
+ const a2aExt = extensions?.a2a;
1443
+ const mcpExt = extensions?.mcp;
1444
+ // Build the comprehensive agent card
1445
+ converted = {
1446
+ // --- Core identity (OSSA source of truth) ---
1447
+ name: meta.name,
1448
+ version: meta.version || '1.0.0',
1449
+ description,
1450
+ url: `https://openstandardagents.org/agents/${meta.name}`,
1451
+ ossaVersion: manifest.apiVersion || 'ossa/v0.4',
1452
+ kind: manifest.kind || 'Agent',
1453
+ // --- The OSSA contract (identity + capabilities + constraints) ---
1454
+ ossa: {
1455
+ role,
1456
+ capabilities: (manifest.spec?.capabilities || []),
1457
+ ...(manifest.spec?.autonomy ? { autonomy: manifest.spec.autonomy } : {}),
1458
+ ...(manifest.spec?.safety ? { safety: manifest.spec.safety } : {}),
1459
+ ...(manifest.spec?.observability ? { observability: manifest.spec.observability } : {}),
1460
+ ...(manifest.spec?.access ? { access: manifest.spec.access } : {}),
1461
+ },
1462
+ // --- A2A discovery (Google Agent-to-Agent protocol) ---
1463
+ capabilities: {
1464
+ streaming: true,
1465
+ pushNotifications: false,
1466
+ stateTransitionHistory: true,
1467
+ },
1468
+ skills: a2aSkills,
1469
+ defaultInputModes: ['application/json', 'text/plain'],
1470
+ defaultOutputModes: ['application/json', 'text/plain'],
1471
+ // --- MCP tools (Model Context Protocol) ---
1472
+ mcp: {
1473
+ tools: mcpTools,
1474
+ ...(mcpExt?.servers ? { servers: mcpExt.servers } : {}),
1475
+ ...(mcpExt?.resources ? { resources: mcpExt.resources } : {}),
1476
+ },
1477
+ // --- Platform adapters (use these directly in your framework) ---
1478
+ // Each adapter includes: sdk (npm/pip packages), config (ready-to-use), usage (code snippet)
1479
+ adapters: {
1480
+ openai: {
1481
+ sdk: { npm: 'openai', pip: 'openai', docs: 'https://platform.openai.com/docs/api-reference' },
1482
+ config: {
1483
+ model,
1484
+ messages: [{ role: 'system', content: role }],
1485
+ tools: openaiTools,
1486
+ tool_choice: 'auto',
1487
+ },
1488
+ usage: `import OpenAI from 'openai';\nconst client = new OpenAI();\nconst response = await client.chat.completions.create(config);`,
1589
1489
  },
1590
- config: {
1591
- _type: 'agent',
1592
- name: meta.name,
1593
- llm: {
1594
- _type: provider === 'anthropic' ? 'ChatAnthropic' : 'ChatOpenAI',
1595
- model_name: model,
1490
+ anthropic: {
1491
+ sdk: { npm: '@anthropic-ai/sdk', pip: 'anthropic', docs: 'https://docs.anthropic.com/en/api' },
1492
+ config: {
1493
+ model: provider === 'anthropic' ? model : 'claude-sonnet-4-20250514',
1494
+ system: role,
1495
+ tools: anthropicTools,
1496
+ max_tokens: llm?.maxTokens || 4096,
1596
1497
  },
1597
- system_message: role,
1598
- tools: langchainTools,
1498
+ usage: `import Anthropic from '@anthropic-ai/sdk';\nconst client = new Anthropic();\nconst response = await client.messages.create(config);`,
1599
1499
  },
1600
- usage: `from langchain.agents import create_tool_calling_agent\nagent = create_tool_calling_agent(llm, tools, prompt)`,
1601
- },
1602
- langflow: {
1603
- sdk: { pip: 'langflow', docs: 'https://docs.langflow.org/' },
1604
- config: langflowComponent,
1605
- usage: 'Import as custom component in LangFlow UI or use langflow CLI',
1606
- },
1607
- crewai: {
1608
- sdk: { pip: 'crewai', docs: 'https://docs.crewai.com/' },
1609
- config: {
1610
- agents: [
1611
- {
1612
- role: meta.name,
1613
- goal: description,
1614
- backstory: role,
1615
- llm: model,
1616
- tools: crewaiTools,
1500
+ google_genai: {
1501
+ sdk: { npm: '@google/generative-ai', pip: 'google-generativeai', docs: 'https://ai.google.dev/docs' },
1502
+ config: {
1503
+ model: 'gemini-2.0-flash',
1504
+ systemInstruction: role,
1505
+ tools: [{ functionDeclarations: openaiTools.map((t) => t.function) }],
1506
+ },
1507
+ usage: `import { GoogleGenerativeAI } from '@google/generative-ai';\nconst genAI = new GoogleGenerativeAI(apiKey);\nconst model = genAI.getGenerativeModel(config);`,
1508
+ },
1509
+ langchain: {
1510
+ sdk: {
1511
+ npm: ['langchain', '@langchain/core', '@langchain/openai', '@langchain/anthropic'],
1512
+ pip: ['langchain', 'langchain-openai', 'langchain-anthropic'],
1513
+ docs: 'https://js.langchain.com/docs/',
1514
+ },
1515
+ config: {
1516
+ _type: 'agent',
1517
+ name: meta.name,
1518
+ llm: {
1519
+ _type: provider === 'anthropic' ? 'ChatAnthropic' : 'ChatOpenAI',
1520
+ model_name: model,
1617
1521
  },
1618
- ],
1522
+ system_message: role,
1523
+ tools: langchainTools,
1524
+ },
1525
+ usage: `from langchain.agents import create_tool_calling_agent\nagent = create_tool_calling_agent(llm, tools, prompt)`,
1619
1526
  },
1620
- usage: `from crewai import Agent\nagent = Agent(role=config['role'], goal=config['goal'], backstory=config['backstory'])`,
1621
- },
1622
- autogen: {
1623
- sdk: {
1624
- pip: 'autogen-agentchat',
1625
- docs: 'https://microsoft.github.io/autogen/',
1527
+ langflow: {
1528
+ sdk: { pip: 'langflow', docs: 'https://docs.langflow.org/' },
1529
+ config: langflowComponent,
1530
+ usage: 'Import as custom component in LangFlow UI or use langflow CLI',
1626
1531
  },
1627
- config: autogenConfig,
1628
- usage: `from autogen import ConversableAgent\nagent = ConversableAgent(name=config['name'], system_message=config['system_message'], llm_config=config['llm_config'])`,
1629
- },
1630
- semantic_kernel: {
1631
- sdk: {
1632
- npm: 'semantic-kernel',
1633
- pip: 'semantic-kernel',
1634
- docs: 'https://learn.microsoft.com/en-us/semantic-kernel/',
1532
+ crewai: {
1533
+ sdk: { pip: 'crewai', docs: 'https://docs.crewai.com/' },
1534
+ config: {
1535
+ agents: [{
1536
+ role: meta.name,
1537
+ goal: description,
1538
+ backstory: role,
1539
+ llm: model,
1540
+ tools: crewaiTools,
1541
+ }],
1542
+ },
1543
+ usage: `from crewai import Agent\nagent = Agent(role=config['role'], goal=config['goal'], backstory=config['backstory'])`,
1635
1544
  },
1636
- config: semanticKernelConfig,
1637
- usage: `import semantic_kernel as sk\nkernel = sk.Kernel()\nkernel.add_chat_service("default", OpenAIChatCompletion(config['execution_settings']['default']['model_id']))`,
1638
- },
1639
- llamaindex: {
1640
- sdk: {
1641
- npm: 'llamaindex',
1642
- pip: 'llama-index',
1643
- docs: 'https://docs.llamaindex.ai/',
1545
+ autogen: {
1546
+ sdk: { pip: 'autogen-agentchat', docs: 'https://microsoft.github.io/autogen/' },
1547
+ config: autogenConfig,
1548
+ usage: `from autogen import ConversableAgent\nagent = ConversableAgent(name=config['name'], system_message=config['system_message'], llm_config=config['llm_config'])`,
1644
1549
  },
1645
- config: {
1646
- name: meta.name,
1647
- description,
1648
- system_prompt: role,
1649
- llm: { model, provider },
1650
- tools: universalTools.map((t) => ({
1651
- name: t.name,
1652
- description: t.description,
1653
- fn_schema: t.inputSchema,
1654
- })),
1550
+ semantic_kernel: {
1551
+ sdk: { npm: 'semantic-kernel', pip: 'semantic-kernel', docs: 'https://learn.microsoft.com/en-us/semantic-kernel/' },
1552
+ config: semanticKernelConfig,
1553
+ usage: `import semantic_kernel as sk\nkernel = sk.Kernel()\nkernel.add_chat_service("default", OpenAIChatCompletion(config['execution_settings']['default']['model_id']))`,
1655
1554
  },
1656
- usage: `from llama_index.agent.openai import OpenAIAgent\nagent = OpenAIAgent.from_tools(tools, system_prompt=config['system_prompt'])`,
1657
- },
1658
- dspy: {
1659
- sdk: { pip: 'dspy', docs: 'https://dspy.ai/' },
1660
- config: {
1661
- name: meta.name,
1662
- instructions: role,
1663
- lm: `${provider}/${model}`,
1664
- tools: universalTools.map((t) => t.name),
1555
+ llamaindex: {
1556
+ sdk: { npm: 'llamaindex', pip: 'llama-index', docs: 'https://docs.llamaindex.ai/' },
1557
+ config: {
1558
+ name: meta.name,
1559
+ description,
1560
+ system_prompt: role,
1561
+ llm: { model, provider },
1562
+ tools: universalTools.map((t) => ({ name: t.name, description: t.description, fn_schema: t.inputSchema })),
1563
+ },
1564
+ usage: `from llama_index.agent.openai import OpenAIAgent\nagent = OpenAIAgent.from_tools(tools, system_prompt=config['system_prompt'])`,
1665
1565
  },
1666
- usage: `import dspy\nlm = dspy.LM('${provider}/${model}')\ndspy.configure(lm=lm)`,
1667
- },
1668
- kagent: {
1669
- sdk: { docs: 'https://kagent.dev/docs' },
1670
- config: kagentRef,
1671
- usage: 'kubectl apply -f <converted>.kagent.yaml # Use target: kagent for full CRD output',
1672
- },
1673
- gitlab_duo: {
1674
- sdk: { docs: 'https://docs.gitlab.com/ee/user/gitlab_duo/' },
1675
- config: {
1676
- name: meta.name,
1677
- description,
1678
- system_prompt: role,
1679
- model: provider === 'anthropic' ? model : 'claude-sonnet-4-20250514',
1680
- tools: universalTools.map((t) => ({ name: t.name })),
1566
+ dspy: {
1567
+ sdk: { pip: 'dspy', docs: 'https://dspy.ai/' },
1568
+ config: {
1569
+ name: meta.name,
1570
+ instructions: role,
1571
+ lm: `${provider}/${model}`,
1572
+ tools: universalTools.map((t) => t.name),
1573
+ },
1574
+ usage: `import dspy\nlm = dspy.LM('${provider}/${model}')\ndspy.configure(lm=lm)`,
1681
1575
  },
1682
- usage: 'Place in .gitlab/duo/agents/ directory',
1683
- },
1684
- claude_agent_sdk: {
1685
- sdk: {
1686
- npm: '@anthropic-ai/claude-agent-sdk',
1687
- pip: 'claude-agent-sdk',
1688
- go: 'github.com/M1n9X/claude-agent-sdk-go',
1689
- rust: 'claude_agent',
1690
- docs: 'https://docs.claude.com/en/api/agent-sdk/overview',
1576
+ kagent: {
1577
+ sdk: { docs: 'https://kagent.dev/docs' },
1578
+ config: kagentRef,
1579
+ usage: 'kubectl apply -f <converted>.kagent.yaml # Use target: kagent for full CRD output',
1691
1580
  },
1692
- config: {
1693
- systemPrompt: role,
1694
- model: provider === 'anthropic' ? model : 'claude-sonnet-4-20250514',
1695
- allowedTools: universalTools.map((t) => t.name),
1581
+ gitlab_duo: {
1582
+ sdk: { docs: 'https://docs.gitlab.com/ee/user/gitlab_duo/' },
1583
+ config: {
1584
+ name: meta.name,
1585
+ description,
1586
+ system_prompt: role,
1587
+ model: provider === 'anthropic' ? model : 'claude-sonnet-4-20250514',
1588
+ tools: universalTools.map((t) => ({ name: t.name })),
1589
+ },
1590
+ usage: 'Place in .gitlab/duo/agents/ directory',
1696
1591
  },
1697
- usage: `import { query } from '@anthropic-ai/claude-agent-sdk';\nconst conversation = query({ prompt, options: config });\nfor await (const msg of conversation) { /* handle */ }`,
1698
1592
  },
1699
- },
1700
- // --- A2A protocol details (if defined in OSSA extensions) ---
1701
- ...(a2aExt
1702
- ? {
1593
+ // --- A2A protocol details (if defined in OSSA extensions) ---
1594
+ ...(a2aExt ? {
1703
1595
  a2a: {
1704
1596
  ...(a2aExt.protocol ? { protocol: a2aExt.protocol } : {}),
1705
1597
  ...(a2aExt.endpoints ? { endpoints: a2aExt.endpoints } : {}),
1706
1598
  ...(a2aExt.routing ? { routing: a2aExt.routing } : {}),
1707
1599
  ...(a2aExt.delegation ? { delegation: a2aExt.delegation } : {}),
1708
1600
  },
1709
- }
1710
- : {}),
1711
- // --- Full OSSA agent card (from generator) ---
1712
- ...(cardResult.success && cardResult.card
1713
- ? {
1601
+ } : {}),
1602
+ // --- Full OSSA agent card (from generator) ---
1603
+ ...(cardResult.success && cardResult.card ? {
1714
1604
  _agentCard: cardResult.card,
1715
- }
1716
- : {}),
1717
- };
1718
- filename = 'agent-card.json';
1719
- }
1720
- else if (input.target === 'claude-agent-sdk') {
1721
- // Generate Claude Agent SDK application config (TypeScript + Python)
1722
- const sdkTools = (manifest.spec?.tools || []);
1723
- const sdkLlm = manifest.spec?.llm;
1724
- const sdkModel = (typeof sdkLlm === 'string' ? sdkLlm : sdkLlm?.model) ||
1725
- 'claude-sonnet-4-20250514';
1726
- const sdkRole = manifest.spec?.role || '';
1727
- const sdkCaps = (manifest.spec?.capabilities || []).map((c) => typeof c === 'string' ? c : c.name || '');
1728
- // Map capabilities to Claude Agent SDK built-in tools
1729
- const builtInTools = [];
1730
- const capToolMap = {
1731
- 'web-search': ['WebSearch', 'WebFetch'],
1732
- 'file-access': ['Read', 'Write', 'Edit', 'Glob', 'Grep'],
1733
- 'file-read': ['Read', 'Glob', 'Grep'],
1734
- 'file-write': ['Read', 'Write', 'Edit'],
1735
- 'code-execution': ['Bash'],
1736
- shell: ['Bash'],
1737
- bash: ['Bash'],
1738
- 'code-analysis': ['Read', 'Glob', 'Grep'],
1739
- explore: ['Read', 'Glob', 'Grep'],
1740
- };
1741
- for (const cap of sdkCaps) {
1742
- const mapped = capToolMap[cap];
1743
- if (mapped) {
1744
- for (const t of mapped) {
1745
- if (!builtInTools.includes(t))
1746
- builtInTools.push(t);
1747
- }
1748
- }
1749
- }
1750
- // Map MCP servers from tools
1751
- const mcpServers = {};
1752
- const customTools = [];
1753
- for (const tool of sdkTools) {
1754
- if (tool.type === 'mcp' && tool.server) {
1755
- const sname = String(tool.server);
1756
- if (!mcpServers[sname]) {
1757
- mcpServers[sname] = {
1758
- type: tool.transport || 'stdio',
1759
- ...(tool.command ? { command: tool.command } : {}),
1760
- ...(tool.args ? { args: tool.args } : {}),
1761
- ...(tool.url ? { url: tool.url } : {}),
1762
- };
1763
- }
1764
- }
1765
- else {
1766
- customTools.push({
1767
- name: tool.name || 'unnamed',
1768
- description: tool.description || '',
1769
- inputSchema: tool.inputSchema ||
1770
- tool.input_schema ||
1771
- tool.parameters || { type: 'object', properties: {} },
1772
- });
1773
- }
1774
- }
1775
- // Map autonomy to permission mode
1776
- const sdkAutonomy = manifest.spec?.autonomy;
1777
- let permissionMode = 'default';
1778
- if (sdkAutonomy) {
1779
- const level = sdkAutonomy.level || '';
1780
- if (level === 'full' || level === 'autonomous')
1781
- permissionMode = 'bypassPermissions';
1782
- else if (level === 'supervised')
1783
- permissionMode = 'acceptEdits';
1784
- else if (level === 'planning')
1785
- permissionMode = 'planMode';
1605
+ } : {}),
1606
+ };
1607
+ filename = 'agent-card.json';
1608
+ break;
1786
1609
  }
1787
- // Resolve Claude model name
1788
- let claudeModel = 'claude-sonnet-4-20250514';
1789
- if (sdkModel.includes('opus'))
1790
- claudeModel = 'claude-opus-4-20250514';
1791
- else if (sdkModel.includes('haiku'))
1792
- claudeModel = 'claude-haiku-4-5-20251001';
1793
- else if (sdkModel.includes('sonnet'))
1794
- claudeModel = 'claude-sonnet-4-20250514';
1795
- else if (sdkModel.includes('claude'))
1796
- claudeModel = sdkModel;
1797
- converted = {
1798
- name: meta.name,
1799
- version: meta.version || '1.0.0',
1800
- description: meta.description || '',
1801
- ossaVersion: manifest.apiVersion || 'ossa/v0.4',
1802
- // Claude Agent SDK configuration
1803
- sdk: {
1804
- typescript: {
1805
- package: '@anthropic-ai/claude-agent-sdk',
1806
- install: 'npm install @anthropic-ai/claude-agent-sdk',
1807
- docs: 'https://docs.claude.com/en/api/agent-sdk/typescript',
1808
- },
1809
- python: {
1810
- package: 'claude-agent-sdk',
1811
- install: 'pip install claude-agent-sdk',
1812
- docs: 'https://docs.claude.com/en/api/agent-sdk/python',
1813
- },
1814
- go: {
1815
- package: 'github.com/M1n9X/claude-agent-sdk-go',
1816
- install: 'go get github.com/M1n9X/claude-agent-sdk-go',
1817
- docs: 'https://github.com/M1n9X/claude-agent-sdk-go',
1818
- community: true,
1819
- },
1820
- rust: {
1821
- package: 'claude_agent',
1822
- install: 'cargo add claude_agent',
1823
- docs: 'https://crates.io/crates/claude_agent',
1824
- community: true,
1825
- },
1826
- },
1827
- // Agent options (ready to use in SDK)
1828
- options: {
1829
- systemPrompt: sdkRole,
1830
- model: claudeModel,
1831
- permissionMode,
1832
- ...(builtInTools.length > 0 ? { allowedTools: builtInTools } : {}),
1833
- ...(Object.keys(mcpServers).length > 0 ? { mcpServers } : {}),
1834
- },
1835
- // Custom tools (need implementation)
1836
- ...(customTools.length > 0 ? { customTools } : {}),
1837
- // TypeScript usage
1838
- usage: {
1839
- typescript: `import { query } from '@anthropic-ai/claude-agent-sdk';\n\nconst conversation = query({\n prompt: 'Your prompt here',\n options: ${JSON.stringify({ systemPrompt: sdkRole.substring(0, 80) + '...', model: claudeModel, permissionMode }, null, 4)}\n});\n\nfor await (const message of conversation) {\n if (message.type === 'assistant') {\n for (const block of message.message.content) {\n if (block.type === 'text') process.stdout.write(block.text);\n }\n }\n}`,
1840
- python: `from claude_agent_sdk import query\n\nasync for message in query(\n prompt="Your prompt here",\n options=ClaudeAgentOptions(\n system_prompt=${JSON.stringify(sdkRole.substring(0, 80) + '...')},\n model="${claudeModel}",\n permission_mode="${permissionMode}",\n )\n):\n if message.type == "assistant":\n for block in message.message.content:\n if hasattr(block, "text"):\n print(block.text, end="")`,
1841
- },
1842
- };
1843
- filename = `${meta.name}.claude-agent-sdk.json`;
1844
- }
1845
- else {
1846
- // Delegate to adapter registry (config-only + full adapters with toConfig())
1847
- initializeAdapters();
1848
- const adapter = convertRegistry.getAdapter(input.target);
1849
- if (!adapter) {
1610
+ default:
1850
1611
  return errResponse(`Unknown target: ${input.target}`);
1851
- }
1852
- try {
1853
- const result = await adapter.toConfig(manifest);
1854
- converted = result.config;
1855
- filename = result.filename;
1856
- }
1857
- catch (err) {
1858
- return errResponse(`toConfig() failed for target "${input.target}": ${err instanceof Error ? err.message : String(err)}`);
1859
- }
1860
1612
  }
1861
1613
  // Optionally write to disk
1862
1614
  if (input.output_dir) {
@@ -1878,11 +1630,7 @@ async function handleConvert(args) {
1878
1630
  content = JSON.stringify(converted, null, 2);
1879
1631
  }
1880
1632
  fs.writeFileSync(outPath, content, 'utf8');
1881
- return okResponse({
1882
- target: input.target,
1883
- written_to: outPath,
1884
- content: converted,
1885
- });
1633
+ return okResponse({ target: input.target, written_to: outPath, content: converted });
1886
1634
  }
1887
1635
  return okResponse({ target: input.target, filename, content: converted });
1888
1636
  }
@@ -1897,11 +1645,7 @@ async function handleWorkspace(args) {
1897
1645
  const wsDir = path.join(dir, '.agents-workspace');
1898
1646
  const registryDir = path.join(wsDir, 'registry');
1899
1647
  if (fs.existsSync(wsDir)) {
1900
- return okResponse({
1901
- action: 'init',
1902
- status: 'already_exists',
1903
- path: wsDir,
1904
- });
1648
+ return okResponse({ action: 'init', status: 'already_exists', path: wsDir });
1905
1649
  }
1906
1650
  fs.mkdirSync(registryDir, { recursive: true });
1907
1651
  const wsName = input.name || path.basename(dir);
@@ -1913,31 +1657,13 @@ async function handleWorkspace(args) {
1913
1657
  });
1914
1658
  fs.writeFileSync(path.join(registryDir, 'index.yaml'), indexYaml);
1915
1659
  fs.writeFileSync(path.join(wsDir, 'README.md'), `# OSSA Workspace: ${wsName}\n\nGenerated by OSSA MCP Server.\n\nRun \`ossa workspace discover\` to scan for agents.\n`);
1916
- return okResponse({
1917
- action: 'init',
1918
- status: 'created',
1919
- path: wsDir,
1920
- name: wsName,
1921
- });
1660
+ return okResponse({ action: 'init', status: 'created', path: wsDir, name: wsName });
1922
1661
  }
1923
1662
  case 'discover': {
1924
1663
  // Scan for all *.ossa.yaml manifests
1925
- const patterns = [
1926
- '**/*.ossa.yaml',
1927
- '**/*.ossa.yml',
1928
- '**/.agents/*/manifest.ossa.yaml',
1929
- ];
1930
- const ignorePatterns = [
1931
- '**/node_modules/**',
1932
- '**/dist/**',
1933
- '**/.git/**',
1934
- '**/coverage/**',
1935
- ];
1936
- const files = await fg(patterns, {
1937
- cwd: dir,
1938
- ignore: ignorePatterns,
1939
- absolute: true,
1940
- });
1664
+ const patterns = ['**/*.ossa.yaml', '**/*.ossa.yml', '**/.agents/*/manifest.ossa.yaml'];
1665
+ const ignorePatterns = ['**/node_modules/**', '**/dist/**', '**/.git/**', '**/coverage/**'];
1666
+ const files = await fg(patterns, { cwd: dir, ignore: ignorePatterns, absolute: true });
1941
1667
  const agents = [];
1942
1668
  for (const f of files) {
1943
1669
  try {
@@ -1965,12 +1691,7 @@ async function handleWorkspace(args) {
1965
1691
  workspace: path.basename(dir),
1966
1692
  discovered: new Date().toISOString(),
1967
1693
  count: agents.length,
1968
- agents: agents.map((a) => ({
1969
- name: a.name,
1970
- version: a.version,
1971
- kind: a.kind,
1972
- path: a.path,
1973
- })),
1694
+ agents: agents.map((a) => ({ name: a.name, version: a.version, kind: a.kind, path: a.path })),
1974
1695
  };
1975
1696
  fs.writeFileSync(path.join(registryDir, 'index.yaml'), yaml.dump(index));
1976
1697
  }
@@ -1980,18 +1701,10 @@ async function handleWorkspace(args) {
1980
1701
  const wsDir = path.join(dir, '.agents-workspace');
1981
1702
  const indexPath = path.join(wsDir, 'registry', 'index.yaml');
1982
1703
  if (!fs.existsSync(indexPath)) {
1983
- return okResponse({
1984
- action: 'status',
1985
- initialized: false,
1986
- message: 'Run ossa_workspace with action: init first',
1987
- });
1704
+ return okResponse({ action: 'status', initialized: false, message: 'Run ossa_workspace with action: init first' });
1988
1705
  }
1989
1706
  const indexContent = yaml.load(fs.readFileSync(indexPath, 'utf8'));
1990
- return okResponse({
1991
- action: 'status',
1992
- initialized: true,
1993
- workspace: indexContent,
1994
- });
1707
+ return okResponse({ action: 'status', initialized: true, workspace: indexContent });
1995
1708
  }
1996
1709
  }
1997
1710
  }
@@ -2001,10 +1714,7 @@ async function handleWorkspace(args) {
2001
1714
  /** Recursive deep diff — same algorithm as diff.command.ts (DRY) */
2002
1715
  function deepDiff(obj1, obj2, prefix = '') {
2003
1716
  const changes = [];
2004
- const allKeys = new Set([
2005
- ...Object.keys(obj1 || {}),
2006
- ...Object.keys(obj2 || {}),
2007
- ]);
1717
+ const allKeys = new Set([...Object.keys(obj1 || {}), ...Object.keys(obj2 || {})]);
2008
1718
  for (const key of allKeys) {
2009
1719
  const fieldPath = prefix ? `${prefix}.${key}` : key;
2010
1720
  const val1 = obj1?.[key];
@@ -2015,21 +1725,13 @@ function deepDiff(obj1, obj2, prefix = '') {
2015
1725
  else if (!(key in (obj2 || {}))) {
2016
1726
  changes.push({ type: 'removed', path: fieldPath, oldValue: val1 });
2017
1727
  }
2018
- else if (typeof val1 === 'object' &&
2019
- typeof val2 === 'object' &&
2020
- val1 !== null &&
2021
- val2 !== null &&
2022
- !Array.isArray(val1) &&
2023
- !Array.isArray(val2)) {
1728
+ else if (typeof val1 === 'object' && typeof val2 === 'object' &&
1729
+ val1 !== null && val2 !== null &&
1730
+ !Array.isArray(val1) && !Array.isArray(val2)) {
2024
1731
  changes.push(...deepDiff(val1, val2, fieldPath));
2025
1732
  }
2026
1733
  else if (JSON.stringify(val1) !== JSON.stringify(val2)) {
2027
- changes.push({
2028
- type: 'modified',
2029
- path: fieldPath,
2030
- oldValue: val1,
2031
- newValue: val2,
2032
- });
1734
+ changes.push({ type: 'modified', path: fieldPath, oldValue: val1, newValue: val2 });
2033
1735
  }
2034
1736
  }
2035
1737
  return changes;
@@ -2038,8 +1740,7 @@ function deepDiff(obj1, obj2, prefix = '') {
2038
1740
  function isBreakingChange(change) {
2039
1741
  if (change.type === 'removed')
2040
1742
  return true;
2041
- if (change.path.includes('metadata.name') ||
2042
- change.path.includes('metadata.version'))
1743
+ if (change.path.includes('metadata.name') || change.path.includes('metadata.version'))
2043
1744
  return true;
2044
1745
  if (change.path.includes('spec.role'))
2045
1746
  return true;
@@ -2081,13 +1782,8 @@ async function handleMigrate(args) {
2081
1782
  const detectionResult = await versionDetectionService.detectVersion(manifest);
2082
1783
  const currentVersion = detectionResult.version || manifest.apiVersion || 'unknown';
2083
1784
  const targetVersion = input.target_version;
2084
- if (currentVersion === targetVersion ||
2085
- `ossa/${currentVersion}` === targetVersion) {
2086
- return okResponse({
2087
- migrated: false,
2088
- reason: `Already at ${targetVersion}`,
2089
- manifest_path: manifestPath,
2090
- });
1785
+ if (currentVersion === targetVersion || `ossa/${currentVersion}` === targetVersion) {
1786
+ return okResponse({ migrated: false, reason: `Already at ${targetVersion}`, manifest_path: manifestPath });
2091
1787
  }
2092
1788
  // Strip 'ossa/' prefix for MigrationTransformService (expects '0.3.3', not 'ossa/v0.3.3')
2093
1789
  const fromVer = currentVersion.replace(/^ossa\/v?/, '');
@@ -2117,26 +1813,11 @@ async function handleMigrate(args) {
2117
1813
  const outDir = resolvePath(input.output_dir);
2118
1814
  fs.mkdirSync(outDir, { recursive: true });
2119
1815
  const outPath = path.join(outDir, path.basename(manifestPath));
2120
- const output = yaml.dump(migrated, {
2121
- lineWidth: 120,
2122
- noRefs: true,
2123
- });
1816
+ const output = yaml.dump(migrated, { lineWidth: 120, noRefs: true });
2124
1817
  fs.writeFileSync(outPath, output, 'utf8');
2125
- return okResponse({
2126
- migrated: true,
2127
- from: currentVersion,
2128
- to: targetVersion,
2129
- migrations,
2130
- written_to: outPath,
2131
- });
1818
+ return okResponse({ migrated: true, from: currentVersion, to: targetVersion, migrations, written_to: outPath });
2132
1819
  }
2133
- return okResponse({
2134
- migrated: true,
2135
- from: currentVersion,
2136
- to: targetVersion,
2137
- migrations,
2138
- manifest: migrated,
2139
- });
1820
+ return okResponse({ migrated: true, from: currentVersion, to: targetVersion, migrations, manifest: migrated });
2140
1821
  }
2141
1822
  // ---------------------------------------------------------------------------
2142
1823
  // Helpers
@@ -2166,12 +1847,7 @@ function okResponse(data) {
2166
1847
  }
2167
1848
  function errResponse(message) {
2168
1849
  return {
2169
- content: [
2170
- {
2171
- type: 'text',
2172
- text: JSON.stringify({ error: message }, null, 2),
2173
- },
2174
- ],
1850
+ content: [{ type: 'text', text: JSON.stringify({ error: message }, null, 2) }],
2175
1851
  isError: true,
2176
1852
  };
2177
1853
  }