@cleocode/lafs-protocol 1.2.3 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,204 +1,242 @@
1
1
  /**
2
2
  * LAFS Agent Discovery - Express/Fastify Middleware
3
- * Serves discovery document at /.well-known/lafs.json
3
+ * Serves A2A-compliant Agent Card at /.well-known/agent-card.json
4
+ * Maintains backward compatibility with legacy /.well-known/lafs.json
5
+ *
6
+ * A2A v1.0+ Compliant Implementation
7
+ * Reference: specs/external/agent-discovery.md
4
8
  */
5
9
  import { createRequire } from "node:module";
6
10
  import { createHash } from "crypto";
7
- import { readFileSync } from "fs";
8
- import { fileURLToPath } from "url";
9
- import { dirname, join } from "path";
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = dirname(__filename);
12
- // Handle ESM/CommonJS interop for AJV
11
+ import { buildLafsExtension } from './a2a/extensions.js';
13
12
  const require = createRequire(import.meta.url);
14
- const AjvModule = require("ajv");
15
- const AddFormatsModule = require("ajv-formats");
16
- const AjvCtor = (typeof AjvModule === "function" ? AjvModule : AjvModule.default);
17
- const addFormats = (typeof AddFormatsModule === "function" ? AddFormatsModule : AddFormatsModule.default);
18
- let ajvInstance = null;
19
- let validateDiscovery = null;
20
- /**
21
- * Initialize AJV validator for discovery documents
22
- */
23
- function initValidator() {
24
- if (ajvInstance && validateDiscovery)
25
- return;
26
- ajvInstance = new AjvCtor({ strict: true, allErrors: true });
27
- addFormats(ajvInstance);
28
- try {
29
- // Try to load schema from schemas directory
30
- const schemaPath = join(__dirname, "..", "..", "schemas", "v1", "discovery.schema.json");
31
- const schema = JSON.parse(readFileSync(schemaPath, "utf-8"));
32
- validateDiscovery = ajvInstance.compile(schema);
33
- }
34
- catch (e) {
35
- // Fallback to inline schema if file not found
36
- const fallbackSchema = {
37
- $schema: "http://json-schema.org/draft-07/schema#",
38
- type: "object",
39
- required: ["$schema", "lafs_version", "service", "capabilities", "endpoints"],
40
- properties: {
41
- $schema: { type: "string", format: "uri" },
42
- lafs_version: { type: "string", pattern: "^\\d+\\.\\d+\\.\\d+$" },
43
- service: {
44
- type: "object",
45
- required: ["name", "version"],
46
- properties: {
47
- name: { type: "string", minLength: 1 },
48
- version: { type: "string", pattern: "^\\d+\\.\\d+\\.\\d+$" },
49
- description: { type: "string" }
50
- }
51
- },
52
- capabilities: {
53
- type: "array",
54
- items: {
55
- type: "object",
56
- required: ["name", "version", "operations"],
57
- properties: {
58
- name: { type: "string", minLength: 1 },
59
- version: { type: "string", pattern: "^\\d+\\.\\d+\\.\\d+$" },
60
- description: { type: "string" },
61
- operations: { type: "array", items: { type: "string" } },
62
- optional: { type: "boolean" }
63
- }
64
- }
65
- },
66
- endpoints: {
67
- type: "object",
68
- required: ["envelope", "discovery"],
69
- properties: {
70
- envelope: { type: "string", minLength: 1 },
71
- context: { type: "string", minLength: 1 },
72
- discovery: { type: "string", minLength: 1 }
73
- }
74
- }
75
- }
76
- };
77
- validateDiscovery = ajvInstance.compile(fallbackSchema);
78
- }
13
+ // Resolve package.json from project root (works from both src/ and dist/src/)
14
+ let pkg;
15
+ try {
16
+ pkg = require('../package.json');
17
+ }
18
+ catch {
19
+ pkg = require('../../package.json');
79
20
  }
21
+ // ============================================================================
22
+ // Utility Functions
23
+ // ============================================================================
80
24
  /**
81
25
  * Build absolute URL from base and path
82
26
  */
83
27
  function buildUrl(base, path, req) {
84
- // If path is already absolute, return it
85
28
  if (path.startsWith("http://") || path.startsWith("https://")) {
86
29
  return path;
87
30
  }
88
- // If base is provided, use it
89
31
  if (base) {
90
32
  const separator = base.endsWith("/") || path.startsWith("/") ? "" : "/";
91
33
  return `${base}${separator}${path}`;
92
34
  }
93
- // Otherwise try to construct from request
94
35
  if (req) {
95
36
  const protocol = req.headers["x-forwarded-proto"] || req.protocol || "http";
96
37
  const host = req.headers.host || "localhost";
97
38
  const separator = path.startsWith("/") ? "" : "/";
98
39
  return `${protocol}://${host}${separator}${path}`;
99
40
  }
100
- // Fallback to relative path
101
41
  return path.startsWith("/") ? path : `/${path}`;
102
42
  }
103
43
  /**
104
- * Generate ETag from document content
44
+ * Generate ETag from content
105
45
  */
106
46
  function generateETag(content) {
107
47
  return `"${createHash("sha256").update(content).digest("hex").slice(0, 32)}"`;
108
48
  }
109
49
  /**
110
- * Build discovery document from configuration
50
+ * Build A2A Agent Card from configuration
51
+ */
52
+ function buildAgentCard(config, req) {
53
+ const schemaUrl = config.schemaUrl || "https://lafs.dev/schemas/v1/agent-card.schema.json";
54
+ // Handle legacy config migration
55
+ if (config.service && !config.agent) {
56
+ console.warn("[DEPRECATION] Using legacy 'service' config. Migrate to 'agent' format for A2A v1.0+ compliance.");
57
+ return {
58
+ $schema: schemaUrl,
59
+ name: config.service.name,
60
+ description: config.service.description || "LAFS-compliant agent",
61
+ version: config.lafsVersion || config.service.version || "1.0.0",
62
+ url: config.endpoints?.envelope
63
+ ? buildUrl(config.baseUrl, config.endpoints.envelope, req)
64
+ : buildUrl(config.baseUrl, "/", req),
65
+ capabilities: {
66
+ streaming: false,
67
+ pushNotifications: false,
68
+ extendedAgentCard: false,
69
+ extensions: []
70
+ },
71
+ defaultInputModes: ["application/json"],
72
+ defaultOutputModes: ["application/json"],
73
+ skills: (config.capabilities || []).map(cap => ({
74
+ id: cap.name.toLowerCase().replace(/\s+/g, "-"),
75
+ name: cap.name,
76
+ description: cap.description || `${cap.name} capability`,
77
+ tags: cap.operations || [],
78
+ examples: []
79
+ }))
80
+ };
81
+ }
82
+ // Standard A2A v1.0 Agent Card (agent is guaranteed present; legacy path returned above)
83
+ const agent = config.agent;
84
+ const card = {
85
+ $schema: schemaUrl,
86
+ ...agent,
87
+ url: agent.url || buildUrl(config.baseUrl, "/", req)
88
+ };
89
+ // Auto-include LAFS extension if configured
90
+ if (config.autoIncludeLafsExtension) {
91
+ const lafsOptions = typeof config.autoIncludeLafsExtension === 'object'
92
+ ? config.autoIncludeLafsExtension
93
+ : undefined;
94
+ const ext = buildLafsExtension(lafsOptions);
95
+ if (!card.capabilities.extensions) {
96
+ card.capabilities.extensions = [];
97
+ }
98
+ card.capabilities.extensions.push({
99
+ uri: ext.uri,
100
+ description: ext.description ?? 'LAFS envelope protocol for structured agent responses',
101
+ required: ext.required ?? false,
102
+ params: ext.params,
103
+ });
104
+ }
105
+ return card;
106
+ }
107
+ /**
108
+ * Build legacy discovery document for backward compatibility
109
+ * @deprecated Will be removed in v2.0.0
111
110
  */
112
- function buildDiscoveryDocument(config, req) {
111
+ function buildLegacyDiscoveryDocument(config, req) {
113
112
  const schemaUrl = config.schemaUrl || "https://lafs.dev/schemas/v1/discovery.schema.json";
114
- const lafsVersion = config.lafsVersion || "1.0.0";
113
+ const lafsVersion = config.lafsVersion || pkg.version;
115
114
  return {
116
115
  $schema: schemaUrl,
117
116
  lafs_version: lafsVersion,
118
- service: config.service,
119
- capabilities: config.capabilities,
117
+ service: config.service || {
118
+ name: config.agent.name,
119
+ version: config.agent.version,
120
+ description: config.agent.description
121
+ },
122
+ capabilities: config.capabilities || config.agent.skills.map(skill => ({
123
+ name: skill.name,
124
+ version: config.agent.version,
125
+ description: skill.description,
126
+ operations: skill.tags,
127
+ optional: false
128
+ })),
120
129
  endpoints: {
121
- envelope: buildUrl(config.baseUrl, config.endpoints.envelope, req),
122
- context: config.endpoints.context
123
- ? buildUrl(config.baseUrl, config.endpoints.context, req)
124
- : undefined,
125
- discovery: config.endpoints.discovery
126
- ? buildUrl(config.baseUrl, config.endpoints.discovery, req)
127
- : buildUrl(config.baseUrl, "/.well-known/lafs.json", req)
130
+ envelope: buildUrl(config.baseUrl, config.endpoints?.envelope || config.agent.url, req),
131
+ context: config.endpoints?.context ? buildUrl(config.baseUrl, config.endpoints.context, req) : undefined,
132
+ discovery: config.endpoints?.discovery || buildUrl(config.baseUrl, "/.well-known/lafs.json", req)
128
133
  }
129
134
  };
130
135
  }
136
+ // ============================================================================
137
+ // Middleware
138
+ // ============================================================================
131
139
  /**
132
- * Validate discovery document against schema
133
- */
134
- function validateDocument(doc) {
135
- initValidator();
136
- if (!validateDiscovery) {
137
- throw new Error("Discovery document validator not initialized");
138
- }
139
- const valid = validateDiscovery(doc);
140
- if (!valid) {
141
- const errors = validateDiscovery.errors;
142
- const errorMessages = errors?.map((e) => `${e.instancePath || "root"}: ${e.message}`).join("; ");
143
- throw new Error(`Discovery document validation failed: ${errorMessages}`);
144
- }
145
- }
146
- /**
147
- * Create Express middleware for serving LAFS discovery document
140
+ * Create Express middleware for serving A2A Agent Card
141
+ *
142
+ * Serves A2A-compliant Agent Card at /.well-known/agent-card.json
143
+ * Maintains backward compatibility with legacy /.well-known/lafs.json
148
144
  *
149
- * @param config - Discovery configuration
145
+ * @param config - Discovery configuration (A2A v1.0 format)
150
146
  * @param options - Middleware options
151
147
  * @returns Express RequestHandler
152
148
  *
153
149
  * @example
154
150
  * ```typescript
155
151
  * import express from "express";
156
- * import { discoveryMiddleware } from "./discovery.js";
152
+ * import { discoveryMiddleware } from "@cleocode/lafs-protocol/discovery";
157
153
  *
158
154
  * const app = express();
159
155
  *
160
156
  * app.use(discoveryMiddleware({
161
- * service: {
162
- * name: "my-lafs-service",
157
+ * agent: {
158
+ * name: "my-lafs-agent",
159
+ * description: "A LAFS-compliant agent with A2A support",
163
160
  * version: "1.0.0",
164
- * description: "A LAFS-compliant API service"
165
- * },
166
- * capabilities: [
167
- * {
168
- * name: "envelope-processor",
169
- * version: "1.0.0",
170
- * operations: ["process", "validate"],
171
- * description: "Process LAFS envelopes"
172
- * }
173
- * ],
174
- * endpoints: {
175
- * envelope: "/api/v1/envelope",
176
- * context: "/api/v1/context"
161
+ * url: "https://api.example.com",
162
+ * capabilities: {
163
+ * streaming: true,
164
+ * pushNotifications: false,
165
+ * extensions: []
166
+ * },
167
+ * defaultInputModes: ["application/json", "text/plain"],
168
+ * defaultOutputModes: ["application/json"],
169
+ * skills: [
170
+ * {
171
+ * id: "envelope-processor",
172
+ * name: "Envelope Processor",
173
+ * description: "Process LAFS envelopes",
174
+ * tags: ["lafs", "envelope", "validation"],
175
+ * examples: ["Validate this envelope", "Process envelope data"]
176
+ * }
177
+ * ]
177
178
  * }
178
179
  * }));
179
180
  * ```
180
181
  */
181
182
  export function discoveryMiddleware(config, options = {}) {
182
- const path = options.path || "/.well-known/lafs.json";
183
+ const path = options.path || "/.well-known/agent-card.json";
184
+ const legacyPath = options.legacyPath || "/.well-known/lafs.json";
185
+ // Disable legacy path by default when a custom path is set, unless explicitly enabled
186
+ const enableLegacyPath = options.enableLegacyPath ?? !options.path;
183
187
  const enableHead = options.enableHead !== false;
184
188
  const enableEtag = options.enableEtag !== false;
185
189
  const cacheMaxAge = config.cacheMaxAge || 3600;
186
190
  // Validate configuration
187
- if (!config.service?.name || !config.service?.version) {
188
- throw new Error("Discovery config requires service.name and service.version");
191
+ if (!config.agent && !config.service) {
192
+ throw new Error("Discovery config requires 'agent' (A2A v1.0) or 'service' (legacy) configuration");
189
193
  }
190
- if (!Array.isArray(config.capabilities)) {
191
- throw new Error("Discovery config requires capabilities array");
194
+ // Validate legacy service config fields
195
+ if (config.service) {
196
+ if (!config.service.name) {
197
+ throw new Error("Discovery config requires 'service.name'");
198
+ }
199
+ if (!config.service.version) {
200
+ throw new Error("Discovery config requires 'service.version'");
201
+ }
192
202
  }
193
- if (!config.endpoints?.envelope) {
194
- throw new Error("Discovery config requires endpoints.envelope");
203
+ // Validate legacy capabilities/endpoints when using service config
204
+ if (config.service && !config.agent) {
205
+ if (config.capabilities === undefined || config.capabilities === null) {
206
+ throw new Error("Discovery config requires 'capabilities' when using legacy 'service' config");
207
+ }
208
+ if (!config.endpoints?.envelope) {
209
+ throw new Error("Discovery config requires 'endpoints.envelope' when using legacy 'service' config");
210
+ }
211
+ }
212
+ // Cache serialized documents to ensure consistent ETags across GET/HEAD
213
+ let cachedPrimaryJson = null;
214
+ let cachedLegacyJson = null;
215
+ function getSerializedDoc(isLegacy, req) {
216
+ if (isLegacy) {
217
+ if (!cachedLegacyJson) {
218
+ cachedLegacyJson = JSON.stringify(buildLegacyDiscoveryDocument(config, req), null, 2);
219
+ }
220
+ return cachedLegacyJson;
221
+ }
222
+ if (!cachedPrimaryJson) {
223
+ cachedPrimaryJson = JSON.stringify(buildAgentCard(config, req), null, 2);
224
+ }
225
+ return cachedPrimaryJson;
195
226
  }
196
227
  return function discoveryHandler(req, res, next) {
197
- // Only handle requests to the discovery path
198
- if (req.path !== path) {
228
+ const isPrimaryPath = req.path === path;
229
+ const isLegacyPath = enableLegacyPath && req.path === legacyPath;
230
+ // Only handle requests to discovery paths
231
+ if (!isPrimaryPath && !isLegacyPath) {
199
232
  next();
200
233
  return;
201
234
  }
235
+ // Log deprecation warning for legacy path
236
+ if (isLegacyPath) {
237
+ console.warn(`[DEPRECATION] Accessing legacy discovery endpoint ${legacyPath}. ` +
238
+ `Migrate to ${path} for A2A v1.0+ compliance. Legacy support will be removed in v2.0.0.`);
239
+ }
202
240
  // Handle HEAD requests
203
241
  if (req.method === "HEAD") {
204
242
  if (!enableHead) {
@@ -208,23 +246,13 @@ export function discoveryMiddleware(config, options = {}) {
208
246
  });
209
247
  return;
210
248
  }
211
- // For HEAD, we still need to build the document to get the ETag
212
- const doc = buildDiscoveryDocument(config, req);
213
- const json = JSON.stringify(doc);
214
- // Generate stable ETag from config hash (not request-dependent document)
215
- const configHash = generateETag(JSON.stringify({
216
- schemaUrl: config.schemaUrl,
217
- lafsVersion: config.lafsVersion,
218
- service: config.service,
219
- capabilities: config.capabilities,
220
- endpoints: config.endpoints,
221
- cacheMaxAge: config.cacheMaxAge
222
- }));
223
- const etag = enableEtag ? configHash : undefined;
249
+ const json = getSerializedDoc(isLegacyPath, req);
250
+ const etag = enableEtag ? generateETag(json) : undefined;
224
251
  res.set({
225
252
  "Content-Type": "application/json",
226
253
  "Cache-Control": `public, max-age=${cacheMaxAge}`,
227
254
  ...(etag && { "ETag": etag }),
255
+ ...(isLegacyPath && { "Deprecation": "true", "Sunset": "Sat, 31 Dec 2025 23:59:59 GMT" }),
228
256
  "Content-Length": Buffer.byteLength(json)
229
257
  });
230
258
  res.status(200).end();
@@ -239,23 +267,8 @@ export function discoveryMiddleware(config, options = {}) {
239
267
  return;
240
268
  }
241
269
  try {
242
- // Build discovery document
243
- const doc = buildDiscoveryDocument(config, req);
244
- // Validate against schema
245
- validateDocument(doc);
246
- // Serialize document
247
- const json = JSON.stringify(doc);
248
- // Generate ETag from config hash (stable) rather than request-dependent document
249
- // This ensures ETag is consistent across requests even when URLs are constructed from request
250
- const configHash = generateETag(JSON.stringify({
251
- schemaUrl: config.schemaUrl,
252
- lafsVersion: config.lafsVersion,
253
- service: config.service,
254
- capabilities: config.capabilities,
255
- endpoints: config.endpoints,
256
- cacheMaxAge: config.cacheMaxAge
257
- }));
258
- const etag = enableEtag ? configHash : undefined;
270
+ const json = getSerializedDoc(isLegacyPath, req);
271
+ const etag = enableEtag ? generateETag(json) : undefined;
259
272
  // Check If-None-Match for conditional request
260
273
  if (enableEtag && req.headers["if-none-match"] === etag) {
261
274
  res.status(304).end();
@@ -270,6 +283,12 @@ export function discoveryMiddleware(config, options = {}) {
270
283
  if (etag) {
271
284
  headers["ETag"] = etag;
272
285
  }
286
+ // Add deprecation headers for legacy path
287
+ if (isLegacyPath) {
288
+ headers["Deprecation"] = "true";
289
+ headers["Sunset"] = "Sat, 31 Dec 2025 23:59:59 GMT";
290
+ headers["Link"] = `<${buildUrl(config.baseUrl, path, req)}>; rel="successor-version"`;
291
+ }
273
292
  res.set(headers);
274
293
  res.status(200).send(json);
275
294
  }
@@ -279,18 +298,17 @@ export function discoveryMiddleware(config, options = {}) {
279
298
  };
280
299
  }
281
300
  /**
282
- * Fastify plugin for LAFS discovery (for Fastify users)
301
+ * Fastify plugin for A2A Agent Card discovery
283
302
  *
284
303
  * @param fastify - Fastify instance
285
304
  * @param options - Plugin options
286
305
  */
287
306
  export async function discoveryFastifyPlugin(fastify, options) {
288
- const path = options.path || "/.well-known/lafs.json";
307
+ const path = options.path || "/.well-known/agent-card.json";
289
308
  const config = options.config;
290
309
  const cacheMaxAge = config.cacheMaxAge || 3600;
291
310
  const handler = async (request, reply) => {
292
- const doc = buildDiscoveryDocument(config, request.raw);
293
- validateDocument(doc);
311
+ const doc = buildAgentCard(config, request.raw);
294
312
  const json = JSON.stringify(doc);
295
313
  const etag = generateETag(json);
296
314
  reply.header("Content-Type", "application/json");
@@ -301,4 +319,32 @@ export async function discoveryFastifyPlugin(fastify, options) {
301
319
  // Note: Actual route registration depends on Fastify's API
302
320
  // This is a type-safe signature for the plugin
303
321
  }
322
+ // ============================================================================
323
+ // Breaking Changes Documentation
324
+ // ============================================================================
325
+ /**
326
+ * BREAKING CHANGES v1.2.3 → v2.0.0:
327
+ *
328
+ * 1. Discovery Endpoint Path
329
+ * - OLD: /.well-known/lafs.json
330
+ * - NEW: /.well-known/agent-card.json
331
+ * - MIGRATION: Update client code to use new path
332
+ * - BACKWARD COMPAT: Legacy path still works but logs deprecation warning
333
+ *
334
+ * 2. Discovery Document Format
335
+ * - OLD: DiscoveryDocument interface (lafs_version, service, capabilities, endpoints)
336
+ * - NEW: AgentCard interface (A2A v1.0 compliant)
337
+ * - MIGRATION: Update config from 'service' to 'agent' format
338
+ * - BACKWARD COMPAT: Legacy config format automatically converted with warning
339
+ *
340
+ * 3. Type Names
341
+ * - Capability → AgentSkill (renamed to align with A2A spec)
342
+ * - ServiceConfig → AgentCard (renamed)
343
+ * - All old types marked as @deprecated
344
+ *
345
+ * 4. Removed in v2.0.0
346
+ * - Legacy path support will be removed
347
+ * - Old type definitions will be removed
348
+ * - Automatic config migration will be removed
349
+ */
304
350
  export default discoveryMiddleware;
@@ -3,6 +3,15 @@
3
3
  *
4
4
  * Provides health check endpoints for monitoring and orchestration
5
5
  */
6
+ import { createRequire } from 'node:module';
7
+ const require = createRequire(import.meta.url);
8
+ let pkg;
9
+ try {
10
+ pkg = require('../../package.json');
11
+ }
12
+ catch {
13
+ pkg = require('../../../package.json');
14
+ }
6
15
  /**
7
16
  * Health check middleware for Express applications
8
17
  *
@@ -70,7 +79,7 @@ export function healthCheck(config = {}) {
70
79
  const health = {
71
80
  status,
72
81
  timestamp,
73
- version: process.env.npm_package_version || '1.1.0',
82
+ version: pkg.version,
74
83
  uptime: Math.floor((Date.now() - startTime) / 1000),
75
84
  checks: checkResults
76
85
  };
@@ -10,4 +10,6 @@ export * from "./discovery.js";
10
10
  export * from "./health/index.js";
11
11
  export * from "./shutdown/index.js";
12
12
  export * from "./circuit-breaker/index.js";
13
- export * from "./a2a/index.js";
13
+ export { LafsA2AResult, createLafsArtifact, createTextArtifact, createFileArtifact, isExtensionRequired, getExtensionParams, AGENT_CARD_PATH, HTTP_EXTENSION_HEADER, LAFS_EXTENSION_URI, A2A_EXTENSIONS_HEADER, parseExtensionsHeader, negotiateExtensions, formatExtensionsHeader, buildLafsExtension, ExtensionSupportRequiredError, extensionNegotiationMiddleware, TERMINAL_STATES, INTERRUPTED_STATES, VALID_TRANSITIONS, isValidTransition, isTerminalState, isInterruptedState, InvalidStateTransitionError, TaskImmutabilityError, TaskNotFoundError, TaskManager, attachLafsEnvelope, } from "./a2a/index.js";
14
+ export type { LafsA2AConfig, LafsSendMessageParams, LafsExtensionParams, ExtensionNegotiationResult, BuildLafsExtensionOptions, ExtensionNegotiationMiddlewareOptions, CreateTaskOptions, ListTasksOptions, ListTasksResult, } from "./a2a/index.js";
15
+ export type { Task, TaskState, TaskStatus, Artifact, Part, Message, PushNotificationConfig, MessageSendConfiguration, TaskStatusUpdateEvent, TaskArtifactUpdateEvent, SendMessageResponse, SendMessageSuccessResponse, JSONRPCErrorResponse, TextPart, DataPart, FilePart, } from "./a2a/index.js";
package/dist/src/index.js CHANGED
@@ -12,4 +12,13 @@ export * from "./health/index.js";
12
12
  export * from "./shutdown/index.js";
13
13
  export * from "./circuit-breaker/index.js";
14
14
  // A2A Integration
15
- export * from "./a2a/index.js";
15
+ // Explicitly re-export to avoid naming conflicts with discovery types
16
+ // (AgentCard, AgentSkill, AgentCapabilities, AgentExtension).
17
+ // For full A2A types, import from '@cleocode/lafs-protocol/a2a'.
18
+ export {
19
+ // Bridge
20
+ LafsA2AResult, createLafsArtifact, createTextArtifact, createFileArtifact, isExtensionRequired, getExtensionParams,
21
+ // Extensions (T098)
22
+ LAFS_EXTENSION_URI, A2A_EXTENSIONS_HEADER, parseExtensionsHeader, negotiateExtensions, formatExtensionsHeader, buildLafsExtension, ExtensionSupportRequiredError, extensionNegotiationMiddleware,
23
+ // Task Lifecycle (T099)
24
+ TERMINAL_STATES, INTERRUPTED_STATES, VALID_TRANSITIONS, isValidTransition, isTerminalState, isInterruptedState, InvalidStateTransitionError, TaskImmutabilityError, TaskNotFoundError, TaskManager, attachLafsEnvelope, } from "./a2a/index.js";
@@ -7,6 +7,7 @@ export interface Warning {
7
7
  replacement?: string;
8
8
  removeBy?: string;
9
9
  }
10
+ export type MVILevel = 'minimal' | 'standard' | 'full' | 'custom';
10
11
  export interface LAFSMeta {
11
12
  specVersion: string;
12
13
  schemaVersion: string;
@@ -15,7 +16,7 @@ export interface LAFSMeta {
15
16
  requestId: string;
16
17
  transport: LAFSTransport;
17
18
  strict: boolean;
18
- mvi: 'minimal' | 'standard' | 'full' | 'custom';
19
+ mvi: MVILevel;
19
20
  contextVersion: number;
20
21
  /** Session identifier for correlating multi-step agent workflows */
21
22
  sessionId?: string;
package/lafs.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # LAFS: LLM-Agent-First Specification
2
2
 
3
3
  > 📚 **Documentation:** https://codluv.gitbook.io/lafs-protocol/
4
- > **Version:** 1.2.3 | **Status:** Production Ready
4
+ > **Version:** 1.3.1 | **Status:** Production Ready
5
5
 
6
6
  ## 1. Scope
7
7
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleocode/lafs-protocol",
3
- "version": "1.2.3",
3
+ "version": "1.3.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "LLM-Agent-First Specification schemas and conformance tooling",
@@ -10,6 +10,18 @@
10
10
  ".": {
11
11
  "import": "./dist/src/index.js",
12
12
  "types": "./dist/src/index.d.ts"
13
+ },
14
+ "./discovery": {
15
+ "import": "./dist/src/discovery.js",
16
+ "types": "./dist/src/discovery.d.ts"
17
+ },
18
+ "./a2a": {
19
+ "import": "./dist/src/a2a/index.js",
20
+ "types": "./dist/src/a2a/index.d.ts"
21
+ },
22
+ "./a2a/bindings": {
23
+ "import": "./dist/src/a2a/bindings/index.js",
24
+ "types": "./dist/src/a2a/bindings/index.d.ts"
13
25
  }
14
26
  },
15
27
  "files": [
@@ -34,6 +46,7 @@
34
46
  "lafs-conformance": "dist/src/cli.js"
35
47
  },
36
48
  "scripts": {
49
+ "version": "node scripts/sync-version.mjs",
37
50
  "build": "rm -rf dist && tsc -p tsconfig.build.json",
38
51
  "prepack": "npm run build",
39
52
  "typecheck": "tsc --noEmit",