@cleocode/lafs-protocol 0.5.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +7 -3
  3. package/dist/examples/discovery-server.d.ts +8 -0
  4. package/dist/examples/discovery-server.js +216 -0
  5. package/dist/examples/mcp-lafs-client.d.ts +10 -0
  6. package/dist/examples/mcp-lafs-client.js +427 -0
  7. package/dist/examples/mcp-lafs-server.d.ts +10 -0
  8. package/dist/examples/mcp-lafs-server.js +358 -0
  9. package/dist/schemas/v1/envelope.schema.json +0 -0
  10. package/dist/schemas/v1/error-registry.json +0 -0
  11. package/dist/src/a2a/bridge.d.ts +129 -0
  12. package/dist/src/a2a/bridge.js +173 -0
  13. package/dist/src/a2a/index.d.ts +36 -0
  14. package/dist/src/a2a/index.js +36 -0
  15. package/dist/src/budgetEnforcement.d.ts +84 -0
  16. package/dist/src/budgetEnforcement.js +328 -0
  17. package/dist/src/circuit-breaker/index.d.ts +121 -0
  18. package/dist/src/circuit-breaker/index.js +249 -0
  19. package/dist/src/cli.d.ts +0 -0
  20. package/dist/src/cli.js +0 -0
  21. package/dist/src/conformance.d.ts +0 -0
  22. package/dist/src/conformance.js +0 -0
  23. package/dist/src/discovery.d.ts +127 -0
  24. package/dist/src/discovery.js +304 -0
  25. package/dist/src/errorRegistry.d.ts +0 -0
  26. package/dist/src/errorRegistry.js +0 -0
  27. package/dist/src/flagSemantics.d.ts +0 -0
  28. package/dist/src/flagSemantics.js +0 -0
  29. package/dist/src/health/index.d.ts +105 -0
  30. package/dist/src/health/index.js +211 -0
  31. package/dist/src/index.d.ts +8 -0
  32. package/dist/src/index.js +10 -0
  33. package/dist/src/mcpAdapter.d.ts +28 -0
  34. package/dist/src/mcpAdapter.js +281 -0
  35. package/dist/src/shutdown/index.d.ts +69 -0
  36. package/dist/src/shutdown/index.js +160 -0
  37. package/dist/src/tokenEstimator.d.ts +87 -0
  38. package/dist/src/tokenEstimator.js +238 -0
  39. package/dist/src/types.d.ts +25 -0
  40. package/dist/src/types.js +0 -0
  41. package/dist/src/validateEnvelope.d.ts +0 -0
  42. package/dist/src/validateEnvelope.js +0 -0
  43. package/lafs.md +167 -0
  44. package/package.json +10 -4
  45. package/schemas/v1/context-ledger.schema.json +0 -0
  46. package/schemas/v1/discovery.schema.json +132 -0
  47. package/schemas/v1/envelope.schema.json +0 -0
  48. package/schemas/v1/error-registry.json +0 -0
@@ -0,0 +1,358 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP-LAFS Server Example
4
+ *
5
+ * A working MCP server that wraps all tool responses in LAFS-compliant envelopes.
6
+ * Demonstrates how LAFS complements MCP by adding structured metadata and budget enforcement.
7
+ *
8
+ * Usage: npx ts-node examples/mcp-lafs-server.ts
9
+ */
10
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
11
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
12
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
13
+ import { wrapMCPResult } from "../src/mcpAdapter.js";
14
+ const simulatedDatabase = new Map([
15
+ ["1", { id: "1", name: "Product A", value: 100, createdAt: new Date().toISOString() }],
16
+ ["2", { id: "2", name: "Product B", value: 200, createdAt: new Date().toISOString() }],
17
+ ["3", { id: "3", name: "Product C", value: 300, createdAt: new Date().toISOString() }],
18
+ ]);
19
+ // Tool definitions
20
+ const TOOLS = [
21
+ {
22
+ name: "weather",
23
+ description: "Get current weather for a location",
24
+ inputSchema: {
25
+ type: "object",
26
+ properties: {
27
+ location: {
28
+ type: "string",
29
+ description: "City name or coordinates",
30
+ },
31
+ units: {
32
+ type: "string",
33
+ enum: ["celsius", "fahrenheit"],
34
+ description: "Temperature units",
35
+ default: "celsius",
36
+ },
37
+ _budget: {
38
+ type: "number",
39
+ description: "Token budget for response (LAFS extension)",
40
+ minimum: 10,
41
+ maximum: 10000,
42
+ },
43
+ },
44
+ required: ["location"],
45
+ },
46
+ },
47
+ {
48
+ name: "calculator",
49
+ description: "Perform mathematical calculations",
50
+ inputSchema: {
51
+ type: "object",
52
+ properties: {
53
+ operation: {
54
+ type: "string",
55
+ enum: ["add", "subtract", "multiply", "divide", "power", "sqrt"],
56
+ description: "Mathematical operation to perform",
57
+ },
58
+ a: {
59
+ type: "number",
60
+ description: "First operand",
61
+ },
62
+ b: {
63
+ type: "number",
64
+ description: "Second operand (not needed for sqrt)",
65
+ },
66
+ _budget: {
67
+ type: "number",
68
+ description: "Token budget for response (LAFS extension)",
69
+ minimum: 10,
70
+ maximum: 1000,
71
+ },
72
+ },
73
+ required: ["operation", "a"],
74
+ },
75
+ },
76
+ {
77
+ name: "database_query",
78
+ description: "Query the simulated database",
79
+ inputSchema: {
80
+ type: "object",
81
+ properties: {
82
+ action: {
83
+ type: "string",
84
+ enum: ["get", "list", "search"],
85
+ description: "Query action to perform",
86
+ },
87
+ id: {
88
+ type: "string",
89
+ description: "Record ID (for get action)",
90
+ },
91
+ query: {
92
+ type: "string",
93
+ description: "Search query (for search action)",
94
+ },
95
+ limit: {
96
+ type: "number",
97
+ description: "Maximum results to return",
98
+ default: 10,
99
+ },
100
+ _budget: {
101
+ type: "number",
102
+ description: "Token budget for response (LAFS extension)",
103
+ minimum: 10,
104
+ maximum: 5000,
105
+ },
106
+ },
107
+ required: ["action"],
108
+ },
109
+ },
110
+ ];
111
+ // Weather simulation
112
+ async function getWeather(location, units) {
113
+ // Simulate API call delay
114
+ await new Promise((resolve) => setTimeout(resolve, 100));
115
+ // Generate deterministic but varied weather based on location
116
+ const hash = location.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0);
117
+ const conditions = ["sunny", "cloudy", "rainy", "partly cloudy", "clear"];
118
+ const condition = conditions[hash % conditions.length];
119
+ // Temperature based on condition and some randomness
120
+ let baseTemp = 20; // celsius
121
+ if (condition === "sunny")
122
+ baseTemp = 25;
123
+ if (condition === "rainy")
124
+ baseTemp = 15;
125
+ if (condition === "clear")
126
+ baseTemp = 22;
127
+ const tempC = baseTemp + (hash % 10) - 5;
128
+ const tempF = Math.round((tempC * 9) / 5 + 32);
129
+ return {
130
+ location,
131
+ temperature: units === "fahrenheit" ? tempF : tempC,
132
+ temperatureUnit: units,
133
+ conditions: condition,
134
+ humidity: 40 + (hash % 50),
135
+ windSpeed: 5 + (hash % 20),
136
+ windUnit: "km/h",
137
+ forecast: [
138
+ { day: "Today", high: tempC + 2, low: tempC - 3, condition },
139
+ { day: "Tomorrow", high: tempC + 1, low: tempC - 4, condition: conditions[(hash + 1) % conditions.length] },
140
+ { day: "Day after", high: tempC + 3, low: tempC - 2, condition: conditions[(hash + 2) % conditions.length] },
141
+ ],
142
+ };
143
+ }
144
+ // Calculator implementation
145
+ function calculate(operation, a, b) {
146
+ let result;
147
+ let expression;
148
+ switch (operation) {
149
+ case "add":
150
+ if (b === undefined)
151
+ throw new Error("Second operand (b) required for addition");
152
+ result = a + b;
153
+ expression = `${a} + ${b}`;
154
+ break;
155
+ case "subtract":
156
+ if (b === undefined)
157
+ throw new Error("Second operand (b) required for subtraction");
158
+ result = a - b;
159
+ expression = `${a} - ${b}`;
160
+ break;
161
+ case "multiply":
162
+ if (b === undefined)
163
+ throw new Error("Second operand (b) required for multiplication");
164
+ result = a * b;
165
+ expression = `${a} * ${b}`;
166
+ break;
167
+ case "divide":
168
+ if (b === undefined)
169
+ throw new Error("Second operand (b) required for division");
170
+ if (b === 0)
171
+ throw new Error("Cannot divide by zero");
172
+ result = a / b;
173
+ expression = `${a} / ${b}`;
174
+ break;
175
+ case "power":
176
+ if (b === undefined)
177
+ throw new Error("Second operand (b) required for power operation");
178
+ result = Math.pow(a, b);
179
+ expression = `${a} ^ ${b}`;
180
+ break;
181
+ case "sqrt":
182
+ if (a < 0)
183
+ throw new Error("Cannot calculate square root of negative number");
184
+ result = Math.sqrt(a);
185
+ expression = `sqrt(${a})`;
186
+ break;
187
+ default:
188
+ throw new Error(`Unknown operation: ${operation}`);
189
+ }
190
+ return {
191
+ operation,
192
+ expression,
193
+ operands: { a, b },
194
+ result,
195
+ resultType: Number.isInteger(result) ? "integer" : "float",
196
+ };
197
+ }
198
+ // Database operations
199
+ function databaseQuery(action, id, query, limit) {
200
+ switch (action) {
201
+ case "get": {
202
+ if (!id) {
203
+ throw new Error("ID required for get action");
204
+ }
205
+ const record = simulatedDatabase.get(id);
206
+ if (!record) {
207
+ throw new Error(`Record with ID '${id}' not found`);
208
+ }
209
+ return {
210
+ action,
211
+ record,
212
+ found: true,
213
+ };
214
+ }
215
+ case "list": {
216
+ const records = Array.from(simulatedDatabase.values()).slice(0, limit ?? 10);
217
+ return {
218
+ action,
219
+ records,
220
+ count: records.length,
221
+ total: simulatedDatabase.size,
222
+ };
223
+ }
224
+ case "search": {
225
+ if (!query) {
226
+ throw new Error("Query required for search action");
227
+ }
228
+ const queryLower = query.toLowerCase();
229
+ const records = Array.from(simulatedDatabase.values())
230
+ .filter((r) => r.name.toLowerCase().includes(queryLower))
231
+ .slice(0, limit ?? 10);
232
+ return {
233
+ action,
234
+ query,
235
+ records,
236
+ count: records.length,
237
+ total: simulatedDatabase.size,
238
+ };
239
+ }
240
+ default:
241
+ throw new Error(`Unknown action: ${action}`);
242
+ }
243
+ }
244
+ // Create MCP server
245
+ const server = new Server({
246
+ name: "lafs-mcp-server",
247
+ version: "1.0.0",
248
+ }, {
249
+ capabilities: {
250
+ tools: {},
251
+ },
252
+ });
253
+ // Handle tool listing
254
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
255
+ return {
256
+ tools: TOOLS,
257
+ };
258
+ });
259
+ // Handle tool calls
260
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
261
+ const { name, arguments: args } = request.params;
262
+ const budget = typeof args?._budget === "number" ? args._budget : undefined;
263
+ try {
264
+ let result;
265
+ switch (name) {
266
+ case "weather": {
267
+ const location = String(args?.location ?? "");
268
+ const units = String(args?.units ?? "celsius");
269
+ if (!location) {
270
+ throw new Error("Location is required");
271
+ }
272
+ result = await getWeather(location, units);
273
+ break;
274
+ }
275
+ case "calculator": {
276
+ const operation = String(args?.operation ?? "");
277
+ const a = Number(args?.a);
278
+ const b = args?.b !== undefined ? Number(args?.b) : undefined;
279
+ if (!operation || Number.isNaN(a)) {
280
+ throw new Error("Operation and operand 'a' are required");
281
+ }
282
+ result = calculate(operation, a, b);
283
+ break;
284
+ }
285
+ case "database_query": {
286
+ const action = String(args?.action ?? "");
287
+ const id = args?.id !== undefined ? String(args?.id) : undefined;
288
+ const query = args?.query !== undefined ? String(args?.query) : undefined;
289
+ const limit = args?.limit !== undefined ? Number(args?.limit) : undefined;
290
+ if (!action) {
291
+ throw new Error("Action is required");
292
+ }
293
+ result = databaseQuery(action, id, query, limit);
294
+ break;
295
+ }
296
+ default:
297
+ throw new Error(`Unknown tool: ${name}`);
298
+ }
299
+ // Create MCP result
300
+ const mcpResult = {
301
+ content: [
302
+ {
303
+ type: "text",
304
+ text: JSON.stringify(result, null, 2),
305
+ },
306
+ ],
307
+ isError: false,
308
+ };
309
+ // Wrap in LAFS envelope
310
+ const envelope = wrapMCPResult(mcpResult, `tools/${name}`, budget);
311
+ // Return the LAFS envelope as text content
312
+ return {
313
+ content: [
314
+ {
315
+ type: "text",
316
+ text: JSON.stringify(envelope),
317
+ },
318
+ ],
319
+ };
320
+ }
321
+ catch (error) {
322
+ // Create error MCP result
323
+ const errorMessage = error instanceof Error ? error.message : String(error);
324
+ const mcpResult = {
325
+ content: [
326
+ {
327
+ type: "text",
328
+ text: errorMessage,
329
+ },
330
+ ],
331
+ isError: true,
332
+ };
333
+ // Wrap in LAFS error envelope
334
+ const envelope = wrapMCPResult(mcpResult, `tools/${name}`, budget);
335
+ return {
336
+ content: [
337
+ {
338
+ type: "text",
339
+ text: JSON.stringify(envelope),
340
+ },
341
+ ],
342
+ isError: true,
343
+ };
344
+ }
345
+ });
346
+ // Start server
347
+ async function main() {
348
+ const transport = new StdioServerTransport();
349
+ console.error("LAFS-MCP Server starting...");
350
+ console.error("Available tools: weather, calculator, database_query");
351
+ console.error("All responses are wrapped in LAFS-compliant envelopes");
352
+ await server.connect(transport);
353
+ console.error("LAFS-MCP Server running on stdio");
354
+ }
355
+ main().catch((error) => {
356
+ console.error("Fatal error:", error);
357
+ process.exit(1);
358
+ });
File without changes
File without changes
@@ -0,0 +1,129 @@
1
+ /**
2
+ * LAFS A2A Bridge
3
+ *
4
+ * Integration with official @a2a-js/sdk for Agent-to-Agent communication.
5
+ * LAFS provides envelope wrapping and token budget support.
6
+ */
7
+ import { A2AClient } from '@a2a-js/sdk/client';
8
+ import { Artifact, Part, SendMessageResponse, JSONRPCErrorResponse } from '@a2a-js/sdk';
9
+ export interface LafsA2AConfig {
10
+ defaultBudget?: {
11
+ maxTokens?: number;
12
+ maxItems?: number;
13
+ };
14
+ envelopeResponses?: boolean;
15
+ }
16
+ export interface LafsEnvelope {
17
+ $schema: string;
18
+ _meta: {
19
+ specVersion: string;
20
+ operation: string;
21
+ requestId: string;
22
+ mvi: string;
23
+ _tokenEstimate?: {
24
+ estimated: number;
25
+ budget?: number;
26
+ };
27
+ };
28
+ success: boolean;
29
+ result: unknown;
30
+ error: null | {
31
+ code: string;
32
+ message: string;
33
+ category: string;
34
+ retryable: boolean;
35
+ };
36
+ }
37
+ /**
38
+ * Wrap A2A client with LAFS envelope support
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * import { ClientFactory } from '@a2a-js/sdk/client';
43
+ * import { withLafsEnvelope } from '@lafs/envelope/a2a';
44
+ *
45
+ * const factory = new ClientFactory();
46
+ * const a2aClient = await factory.createFromUrl('http://localhost:4000');
47
+ *
48
+ * const client = withLafsEnvelope(a2aClient, {
49
+ * envelopeResponses: true,
50
+ * defaultBudget: { maxTokens: 4000 }
51
+ * });
52
+ *
53
+ * const result = await client.sendMessage({
54
+ * message: { role: 'user', parts: [{ text: 'Hello' }] }
55
+ * });
56
+ *
57
+ * // Access LAFS envelope
58
+ * const envelope = result.getLafsEnvelope();
59
+ * console.log(envelope._meta._tokenEstimate);
60
+ * ```
61
+ */
62
+ export declare function withLafsEnvelope(client: A2AClient, config?: LafsA2AConfig): LafsA2AClient;
63
+ export declare class LafsA2AClient {
64
+ private client;
65
+ private config;
66
+ constructor(client: A2AClient, config: LafsA2AConfig);
67
+ sendMessage(params: {
68
+ message: {
69
+ role: 'user' | 'agent';
70
+ parts: Part[];
71
+ };
72
+ budget?: {
73
+ maxTokens?: number;
74
+ maxItems?: number;
75
+ };
76
+ }): Promise<LafsA2AResult>;
77
+ private generateId;
78
+ }
79
+ export declare class LafsA2AResult {
80
+ private result;
81
+ private budget;
82
+ constructor(result: SendMessageResponse, budget: {
83
+ maxTokens?: number;
84
+ maxItems?: number;
85
+ });
86
+ /**
87
+ * Get the underlying A2A result
88
+ */
89
+ getA2AResult(): SendMessageResponse;
90
+ /**
91
+ * Check if result is an error
92
+ */
93
+ isError(): boolean;
94
+ /**
95
+ * Get error details if result is an error
96
+ */
97
+ getError(): JSONRPCErrorResponse | null;
98
+ /**
99
+ * Extract LAFS envelope from A2A artifact
100
+ */
101
+ getLafsEnvelope(): LafsEnvelope | null;
102
+ /**
103
+ * Check if result contains LAFS envelope
104
+ */
105
+ hasLafsEnvelope(): boolean;
106
+ /**
107
+ * Get token estimate from envelope
108
+ */
109
+ getTokenEstimate(): {
110
+ estimated: number;
111
+ budget?: number;
112
+ } | null;
113
+ private isLafsEnvelope;
114
+ }
115
+ /**
116
+ * Create a LAFS artifact for A2A
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * const artifact = createLafsArtifact({
121
+ * success: true,
122
+ * result: { data: '...' },
123
+ * meta: { operation: 'analysis.run' }
124
+ * });
125
+ *
126
+ * task.artifacts.push(artifact);
127
+ * ```
128
+ */
129
+ export declare function createLafsArtifact(envelope: LafsEnvelope): Artifact;
@@ -0,0 +1,173 @@
1
+ /**
2
+ * LAFS A2A Bridge
3
+ *
4
+ * Integration with official @a2a-js/sdk for Agent-to-Agent communication.
5
+ * LAFS provides envelope wrapping and token budget support.
6
+ */
7
+ /**
8
+ * Wrap A2A client with LAFS envelope support
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { ClientFactory } from '@a2a-js/sdk/client';
13
+ * import { withLafsEnvelope } from '@lafs/envelope/a2a';
14
+ *
15
+ * const factory = new ClientFactory();
16
+ * const a2aClient = await factory.createFromUrl('http://localhost:4000');
17
+ *
18
+ * const client = withLafsEnvelope(a2aClient, {
19
+ * envelopeResponses: true,
20
+ * defaultBudget: { maxTokens: 4000 }
21
+ * });
22
+ *
23
+ * const result = await client.sendMessage({
24
+ * message: { role: 'user', parts: [{ text: 'Hello' }] }
25
+ * });
26
+ *
27
+ * // Access LAFS envelope
28
+ * const envelope = result.getLafsEnvelope();
29
+ * console.log(envelope._meta._tokenEstimate);
30
+ * ```
31
+ */
32
+ export function withLafsEnvelope(client, config = {}) {
33
+ return new LafsA2AClient(client, config);
34
+ }
35
+ export class LafsA2AClient {
36
+ client;
37
+ config;
38
+ constructor(client, config) {
39
+ this.client = client;
40
+ this.config = config;
41
+ }
42
+ async sendMessage(params) {
43
+ // Merge budget with defaults
44
+ const budget = {
45
+ ...this.config.defaultBudget,
46
+ ...params.budget
47
+ };
48
+ // Send via official A2A SDK
49
+ const result = await this.client.sendMessage({
50
+ message: {
51
+ kind: 'message',
52
+ messageId: this.generateId(),
53
+ role: params.message.role,
54
+ parts: params.message.parts
55
+ }
56
+ });
57
+ // Wrap result with LAFS envelope support
58
+ return new LafsA2AResult(result, budget);
59
+ }
60
+ generateId() {
61
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
62
+ const r = Math.random() * 16 | 0;
63
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
64
+ return v.toString(16);
65
+ });
66
+ }
67
+ }
68
+ export class LafsA2AResult {
69
+ result;
70
+ budget;
71
+ constructor(result, budget) {
72
+ this.result = result;
73
+ this.budget = budget;
74
+ }
75
+ /**
76
+ * Get the underlying A2A result
77
+ */
78
+ getA2AResult() {
79
+ return this.result;
80
+ }
81
+ /**
82
+ * Check if result is an error
83
+ */
84
+ isError() {
85
+ return 'error' in this.result;
86
+ }
87
+ /**
88
+ * Get error details if result is an error
89
+ */
90
+ getError() {
91
+ if (this.isError()) {
92
+ return this.result;
93
+ }
94
+ return null;
95
+ }
96
+ /**
97
+ * Extract LAFS envelope from A2A artifact
98
+ */
99
+ getLafsEnvelope() {
100
+ if (this.isError()) {
101
+ return null;
102
+ }
103
+ const successResult = this.result;
104
+ // Check if result is a Task
105
+ if (successResult.result?.kind !== 'task') {
106
+ return null;
107
+ }
108
+ const task = successResult.result;
109
+ if (!task.artifacts || task.artifacts.length === 0) {
110
+ return null;
111
+ }
112
+ // Find LAFS envelope in artifacts
113
+ for (const artifact of task.artifacts) {
114
+ for (const part of artifact.parts) {
115
+ if (part.kind === 'data' && this.isLafsEnvelope(part.data)) {
116
+ return part.data;
117
+ }
118
+ }
119
+ }
120
+ return null;
121
+ }
122
+ /**
123
+ * Check if result contains LAFS envelope
124
+ */
125
+ hasLafsEnvelope() {
126
+ return this.getLafsEnvelope() !== null;
127
+ }
128
+ /**
129
+ * Get token estimate from envelope
130
+ */
131
+ getTokenEstimate() {
132
+ const envelope = this.getLafsEnvelope();
133
+ return envelope?._meta?._tokenEstimate ?? null;
134
+ }
135
+ isLafsEnvelope(data) {
136
+ return (typeof data === 'object' &&
137
+ data !== null &&
138
+ '$schema' in data &&
139
+ '_meta' in data &&
140
+ 'success' in data);
141
+ }
142
+ }
143
+ /**
144
+ * Create a LAFS artifact for A2A
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * const artifact = createLafsArtifact({
149
+ * success: true,
150
+ * result: { data: '...' },
151
+ * meta: { operation: 'analysis.run' }
152
+ * });
153
+ *
154
+ * task.artifacts.push(artifact);
155
+ * ```
156
+ */
157
+ export function createLafsArtifact(envelope) {
158
+ return {
159
+ artifactId: generateId(),
160
+ name: 'lafs_response',
161
+ parts: [{
162
+ kind: 'data',
163
+ data: envelope
164
+ }]
165
+ };
166
+ }
167
+ function generateId() {
168
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
169
+ const r = Math.random() * 16 | 0;
170
+ const v = c === 'x' ? r : (r & 0x3 | 0x8);
171
+ return v.toString(16);
172
+ });
173
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * LAFS Agent-to-Agent (A2A) Integration
3
+ *
4
+ * This module provides integration between LAFS and the official
5
+ * @a2a-js/sdk for Agent-to-Agent communication.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { ClientFactory } from '@a2a-js/sdk/client';
10
+ * import { withLafsEnvelope } from '@cleocode/lafs-protocol/a2a';
11
+ *
12
+ * // Create official A2A client
13
+ * const factory = new ClientFactory();
14
+ * const a2aClient = await factory.createFromUrl('http://agent.example.com');
15
+ *
16
+ * // Wrap with LAFS support
17
+ * const client = withLafsEnvelope(a2aClient, {
18
+ * defaultBudget: { maxTokens: 4000 }
19
+ * });
20
+ *
21
+ * // Send message
22
+ * const result = await client.sendMessage({
23
+ * message: {
24
+ * role: 'user',
25
+ * parts: [{ text: 'Analyze data' }]
26
+ * }
27
+ * });
28
+ *
29
+ * // Extract LAFS envelope from response
30
+ * const envelope = result.getLafsEnvelope();
31
+ * if (envelope) {
32
+ * console.log(envelope._meta._tokenEstimate);
33
+ * }
34
+ * ```
35
+ */
36
+ export { withLafsEnvelope, LafsA2AClient, LafsA2AResult, createLafsArtifact, type LafsA2AConfig, type LafsEnvelope } from './bridge.js';