@hai.ai/jacs 0.6.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.
package/src/a2a.d.ts ADDED
@@ -0,0 +1,79 @@
1
+ export const A2A_PROTOCOL_VERSION: string;
2
+ export const JACS_EXTENSION_URI: string;
3
+
4
+ export class A2AAgentInterface {
5
+ constructor(url: string, protocolBinding: string, tenant?: string | null);
6
+ url: string;
7
+ protocolBinding: string;
8
+ tenant?: string;
9
+ }
10
+
11
+ export class A2AAgentSkill {
12
+ constructor(opts: {
13
+ id: string;
14
+ name: string;
15
+ description: string;
16
+ tags: string[];
17
+ examples?: string[] | null;
18
+ inputModes?: string[] | null;
19
+ outputModes?: string[] | null;
20
+ security?: unknown[] | null;
21
+ });
22
+ }
23
+
24
+ export class A2AAgentExtension {
25
+ constructor(uri: string, description?: string | null, required?: boolean | null);
26
+ }
27
+
28
+ export class A2AAgentCapabilities {
29
+ constructor(opts?: {
30
+ streaming?: boolean | null;
31
+ pushNotifications?: boolean | null;
32
+ extendedAgentCard?: boolean | null;
33
+ extensions?: A2AAgentExtension[] | null;
34
+ });
35
+ }
36
+
37
+ export class A2AAgentCardSignature {
38
+ constructor(jws: string, keyId?: string | null);
39
+ }
40
+
41
+ export class A2AAgentCard {
42
+ constructor(opts: {
43
+ name: string;
44
+ description: string;
45
+ version: string;
46
+ protocolVersions: string[];
47
+ supportedInterfaces: A2AAgentInterface[];
48
+ defaultInputModes: string[];
49
+ defaultOutputModes: string[];
50
+ capabilities: A2AAgentCapabilities;
51
+ skills: A2AAgentSkill[];
52
+ provider?: unknown;
53
+ documentationUrl?: string | null;
54
+ iconUrl?: string | null;
55
+ securitySchemes?: Record<string, unknown> | null;
56
+ security?: unknown[] | null;
57
+ signatures?: A2AAgentCardSignature[] | null;
58
+ metadata?: Record<string, unknown> | null;
59
+ });
60
+ }
61
+
62
+ export class JACSA2AIntegration {
63
+ constructor(jacsConfigPath?: string | null);
64
+ exportAgentCard(agentData: Record<string, unknown>): A2AAgentCard;
65
+ createExtensionDescriptor(): Record<string, unknown>;
66
+ wrapArtifactWithProvenance(
67
+ artifact: Record<string, unknown>,
68
+ artifactType: string,
69
+ parentSignatures?: Record<string, unknown>[] | null,
70
+ ): Record<string, unknown>;
71
+ verifyWrappedArtifact(wrappedArtifact: Record<string, unknown>): Record<string, unknown>;
72
+ createChainOfCustody(artifacts: Record<string, unknown>[]): Record<string, unknown>;
73
+ generateWellKnownDocuments(
74
+ agentCard: A2AAgentCard,
75
+ jwsSignature: string,
76
+ publicKeyB64: string,
77
+ agentData: Record<string, unknown>,
78
+ ): Record<string, unknown>;
79
+ }
package/src/a2a.js ADDED
@@ -0,0 +1,617 @@
1
+ /**
2
+ * JACS A2A (Agent-to-Agent) Protocol Integration for Node.js
3
+ *
4
+ * This module provides Node.js bindings for JACS's A2A protocol integration,
5
+ * enabling JACS agents to participate in the Agent-to-Agent communication protocol.
6
+ *
7
+ * Implements A2A protocol v0.4.0 (September 2025).
8
+ */
9
+
10
+ const { v4: uuidv4 } = require('uuid');
11
+ const { createPublicKey } = require('crypto');
12
+ const jacs = require('../index');
13
+
14
+ /**
15
+ * A2A protocol version (v0.4.0)
16
+ */
17
+ const A2A_PROTOCOL_VERSION = '0.4.0';
18
+
19
+ /**
20
+ * JACS extension URI for A2A
21
+ */
22
+ const JACS_EXTENSION_URI = 'urn:hai.ai:jacs-provenance-v1';
23
+
24
+ /**
25
+ * A2A Agent Interface (v0.4.0)
26
+ */
27
+ class A2AAgentInterface {
28
+ constructor(url, protocolBinding, tenant = null) {
29
+ this.url = url;
30
+ this.protocolBinding = protocolBinding;
31
+ if (tenant) {
32
+ this.tenant = tenant;
33
+ }
34
+ }
35
+ }
36
+
37
+ /**
38
+ * A2A Agent Skill (v0.4.0)
39
+ */
40
+ class A2AAgentSkill {
41
+ constructor({ id, name, description, tags, examples = null, inputModes = null, outputModes = null, security = null }) {
42
+ this.id = id;
43
+ this.name = name;
44
+ this.description = description;
45
+ this.tags = tags;
46
+ if (examples) this.examples = examples;
47
+ if (inputModes) this.inputModes = inputModes;
48
+ if (outputModes) this.outputModes = outputModes;
49
+ if (security) this.security = security;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * A2A Agent Extension (v0.4.0)
55
+ */
56
+ class A2AAgentExtension {
57
+ constructor(uri, description = null, required = null) {
58
+ this.uri = uri;
59
+ if (description !== null) this.description = description;
60
+ if (required !== null) this.required = required;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * A2A Agent Capabilities (v0.4.0)
66
+ */
67
+ class A2AAgentCapabilities {
68
+ constructor({ streaming = null, pushNotifications = null, extendedAgentCard = null, extensions = null } = {}) {
69
+ if (streaming !== null) this.streaming = streaming;
70
+ if (pushNotifications !== null) this.pushNotifications = pushNotifications;
71
+ if (extendedAgentCard !== null) this.extendedAgentCard = extendedAgentCard;
72
+ if (extensions) this.extensions = extensions;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * A2A Agent Card Signature (v0.4.0)
78
+ */
79
+ class A2AAgentCardSignature {
80
+ constructor(jws, keyId = null) {
81
+ this.jws = jws;
82
+ if (keyId) this.keyId = keyId;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * A2A Agent Card (v0.4.0)
88
+ *
89
+ * Published at /.well-known/agent-card.json for zero-config discovery.
90
+ */
91
+ class A2AAgentCard {
92
+ constructor({
93
+ name,
94
+ description,
95
+ version,
96
+ protocolVersions,
97
+ supportedInterfaces,
98
+ defaultInputModes,
99
+ defaultOutputModes,
100
+ capabilities,
101
+ skills,
102
+ provider = null,
103
+ documentationUrl = null,
104
+ iconUrl = null,
105
+ securitySchemes = null,
106
+ security = null,
107
+ signatures = null,
108
+ metadata = null
109
+ }) {
110
+ this.name = name;
111
+ this.description = description;
112
+ this.version = version;
113
+ this.protocolVersions = protocolVersions;
114
+ this.supportedInterfaces = supportedInterfaces;
115
+ this.defaultInputModes = defaultInputModes;
116
+ this.defaultOutputModes = defaultOutputModes;
117
+ this.capabilities = capabilities;
118
+ this.skills = skills;
119
+ if (provider) this.provider = provider;
120
+ if (documentationUrl) this.documentationUrl = documentationUrl;
121
+ if (iconUrl) this.iconUrl = iconUrl;
122
+ if (securitySchemes) this.securitySchemes = securitySchemes;
123
+ if (security) this.security = security;
124
+ if (signatures) this.signatures = signatures;
125
+ if (metadata) this.metadata = metadata;
126
+ }
127
+ }
128
+
129
+ /**
130
+ * JACS A2A Integration class (v0.4.0)
131
+ */
132
+ class JACSA2AIntegration {
133
+ constructor(jacsConfigPath = null) {
134
+ if (jacsConfigPath) {
135
+ jacs.load(jacsConfigPath);
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Export a JACS agent as an A2A Agent Card (v0.4.0)
141
+ * @param {Object} agentData - JACS agent data
142
+ * @returns {A2AAgentCard} A2A Agent Card
143
+ */
144
+ exportAgentCard(agentData) {
145
+ const agentId = agentData.jacsId || 'unknown';
146
+ const agentName = agentData.jacsName || 'Unnamed JACS Agent';
147
+ const agentDescription = agentData.jacsDescription || 'JACS-enabled agent';
148
+ const agentVersion = agentData.jacsVersion || '1';
149
+
150
+ // Build supported interfaces from jacsAgentDomain or agent ID
151
+ const domain = agentData.jacsAgentDomain;
152
+ const baseUrl = domain
153
+ ? `https://${domain}/agent/${agentId}`
154
+ : `https://agent-${agentId}.example.com`;
155
+
156
+ const supportedInterfaces = [
157
+ new A2AAgentInterface(baseUrl, 'jsonrpc')
158
+ ];
159
+
160
+ // Convert JACS services to A2A skills
161
+ const skills = this._convertServicesToSkills(agentData.jacsServices || []);
162
+
163
+ // Define security schemes as a keyed map
164
+ const securitySchemes = {
165
+ 'bearer-jwt': {
166
+ type: 'http',
167
+ scheme: 'Bearer',
168
+ bearerFormat: 'JWT'
169
+ },
170
+ 'api-key': {
171
+ type: 'apiKey',
172
+ in: 'header',
173
+ name: 'X-API-Key'
174
+ }
175
+ };
176
+
177
+ // Create JACS extension
178
+ const jacsExtension = new A2AAgentExtension(
179
+ JACS_EXTENSION_URI,
180
+ 'JACS cryptographic document signing and verification',
181
+ false
182
+ );
183
+
184
+ const capabilities = new A2AAgentCapabilities({
185
+ extensions: [jacsExtension]
186
+ });
187
+
188
+ // Create metadata
189
+ const metadata = {
190
+ jacsAgentType: agentData.jacsAgentType,
191
+ jacsId: agentId,
192
+ jacsVersion: agentData.jacsVersion
193
+ };
194
+
195
+ return new A2AAgentCard({
196
+ name: agentName,
197
+ description: agentDescription,
198
+ version: String(agentVersion),
199
+ protocolVersions: [A2A_PROTOCOL_VERSION],
200
+ supportedInterfaces,
201
+ defaultInputModes: ['text/plain', 'application/json'],
202
+ defaultOutputModes: ['text/plain', 'application/json'],
203
+ capabilities,
204
+ skills,
205
+ securitySchemes,
206
+ metadata
207
+ });
208
+ }
209
+
210
+ /**
211
+ * Convert JACS services to A2A skills (v0.4.0)
212
+ * @private
213
+ */
214
+ _convertServicesToSkills(services) {
215
+ const skills = [];
216
+
217
+ for (const service of services) {
218
+ const serviceName = service.name || service.serviceDescription || 'unnamed_service';
219
+ const serviceDesc = service.serviceDescription || 'No description';
220
+
221
+ const tools = service.tools || [];
222
+ if (tools.length > 0) {
223
+ for (const tool of tools) {
224
+ if (tool.function) {
225
+ const fnName = tool.function.name || serviceName;
226
+ const fnDesc = tool.function.description || serviceDesc;
227
+
228
+ skills.push(new A2AAgentSkill({
229
+ id: this._slugify(fnName),
230
+ name: fnName,
231
+ description: fnDesc,
232
+ tags: this._deriveTags(serviceName, fnName),
233
+ }));
234
+ }
235
+ }
236
+ } else {
237
+ skills.push(new A2AAgentSkill({
238
+ id: this._slugify(serviceName),
239
+ name: serviceName,
240
+ description: serviceDesc,
241
+ tags: this._deriveTags(serviceName, serviceName),
242
+ }));
243
+ }
244
+ }
245
+
246
+ // Add default verification skill if none exist
247
+ if (skills.length === 0) {
248
+ skills.push(new A2AAgentSkill({
249
+ id: 'verify-signature',
250
+ name: 'verify_signature',
251
+ description: 'Verify JACS document signatures',
252
+ tags: ['jacs', 'verification', 'cryptography'],
253
+ examples: [
254
+ 'Verify a signed JACS document',
255
+ 'Check document signature integrity'
256
+ ],
257
+ inputModes: ['application/json'],
258
+ outputModes: ['application/json'],
259
+ }));
260
+ }
261
+
262
+ return skills;
263
+ }
264
+
265
+ /**
266
+ * Create JACS extension descriptor for A2A
267
+ * @returns {Object} Extension descriptor
268
+ */
269
+ createExtensionDescriptor() {
270
+ return {
271
+ uri: JACS_EXTENSION_URI,
272
+ name: 'JACS Document Provenance',
273
+ version: '1.0',
274
+ a2aProtocolVersion: A2A_PROTOCOL_VERSION,
275
+ description: 'Provides cryptographic document signing and verification with post-quantum support',
276
+ specification: 'https://hai.ai/jacs/specs/a2a-extension',
277
+ capabilities: {
278
+ documentSigning: {
279
+ description: 'Sign documents with JACS signatures',
280
+ algorithms: ['dilithium', 'falcon', 'sphincs+', 'rsa', 'ecdsa'],
281
+ formats: ['jacs-v1', 'jws-detached']
282
+ },
283
+ documentVerification: {
284
+ description: 'Verify JACS signatures on documents',
285
+ offlineCapable: true,
286
+ chainOfCustody: true
287
+ },
288
+ postQuantumCrypto: {
289
+ description: 'Support for quantum-resistant signatures',
290
+ algorithms: ['dilithium', 'falcon', 'sphincs+']
291
+ }
292
+ },
293
+ endpoints: {
294
+ sign: {
295
+ path: '/jacs/sign',
296
+ method: 'POST',
297
+ description: 'Sign a document with JACS'
298
+ },
299
+ verify: {
300
+ path: '/jacs/verify',
301
+ method: 'POST',
302
+ description: 'Verify a JACS signature'
303
+ },
304
+ publicKey: {
305
+ path: '/.well-known/jacs-pubkey.json',
306
+ method: 'GET',
307
+ description: "Retrieve agent's public key"
308
+ }
309
+ }
310
+ };
311
+ }
312
+
313
+ /**
314
+ * Wrap an A2A artifact with JACS provenance signature
315
+ * @param {Object} artifact - The A2A artifact to wrap
316
+ * @param {string} artifactType - Type of artifact (e.g., "task", "message")
317
+ * @param {Array} parentSignatures - Optional parent signatures for chain of custody
318
+ * @returns {Object} JACS-wrapped artifact with signature
319
+ */
320
+ wrapArtifactWithProvenance(artifact, artifactType, parentSignatures = null) {
321
+ const wrapped = {
322
+ jacsId: uuidv4(),
323
+ jacsVersion: uuidv4(),
324
+ jacsType: `a2a-${artifactType}`,
325
+ jacsLevel: 'artifact',
326
+ jacsVersionDate: new Date().toISOString(),
327
+ $schema: 'https://hai.ai/schemas/header/v1/header.schema.json',
328
+ a2aArtifact: artifact
329
+ };
330
+
331
+ if (parentSignatures) {
332
+ wrapped.jacsParentSignatures = parentSignatures;
333
+ }
334
+
335
+ return jacs.signRequest(wrapped);
336
+ }
337
+
338
+ /**
339
+ * Verify a JACS-wrapped A2A artifact
340
+ * @param {Object} wrappedArtifact - The wrapped artifact to verify
341
+ * @returns {Object} Verification result
342
+ */
343
+ verifyWrappedArtifact(wrappedArtifact) {
344
+ return this._verifyWrappedArtifactInternal(wrappedArtifact, new Set());
345
+ }
346
+
347
+ /**
348
+ * Create a chain of custody document for multi-agent workflows
349
+ * @param {Array} artifacts - List of JACS-wrapped artifacts
350
+ * @returns {Object} Chain of custody document
351
+ */
352
+ createChainOfCustody(artifacts) {
353
+ const chain = [];
354
+
355
+ for (const artifact of artifacts) {
356
+ if (artifact.jacsSignature) {
357
+ const entry = {
358
+ artifactId: artifact.jacsId,
359
+ artifactType: artifact.jacsType,
360
+ timestamp: artifact.jacsVersionDate,
361
+ agentId: artifact.jacsSignature.agentID,
362
+ agentVersion: artifact.jacsSignature.agentVersion,
363
+ signatureHash: artifact.jacsSignature.publicKeyHash
364
+ };
365
+ chain.push(entry);
366
+ }
367
+ }
368
+
369
+ return {
370
+ chainOfCustody: chain,
371
+ created: new Date().toISOString(),
372
+ totalArtifacts: chain.length
373
+ };
374
+ }
375
+
376
+ /**
377
+ * Generate .well-known documents for A2A integration (v0.4.0)
378
+ * @param {A2AAgentCard} agentCard - The A2A Agent Card
379
+ * @param {string} jwsSignature - JWS signature of the Agent Card
380
+ * @param {string} publicKeyB64 - Base64-encoded public key
381
+ * @param {Object} agentData - JACS agent data
382
+ * @returns {Object} Map of paths to document contents
383
+ */
384
+ generateWellKnownDocuments(agentCard, jwsSignature, publicKeyB64, agentData) {
385
+ const documents = {};
386
+ const keyAlgorithm = agentData.keyAlgorithm || 'RSA-PSS';
387
+ const postQuantum = /(pq|dilithium|falcon|sphincs|ml-dsa|pq2025)/i.test(keyAlgorithm);
388
+
389
+ // 1. Agent Card with embedded signature (v0.4.0)
390
+ const cardObj = JSON.parse(JSON.stringify(agentCard));
391
+ cardObj.signatures = [{ jws: jwsSignature }];
392
+ documents['/.well-known/agent-card.json'] = cardObj;
393
+
394
+ // 2. JWK Set for A2A verifiers
395
+ documents['/.well-known/jwks.json'] = this._buildJwks(publicKeyB64, agentData);
396
+
397
+ // 3. JACS Agent Descriptor
398
+ documents['/.well-known/jacs-agent.json'] = {
399
+ jacsVersion: '1.0',
400
+ agentId: agentData.jacsId,
401
+ agentVersion: agentData.jacsVersion,
402
+ agentType: agentData.jacsAgentType,
403
+ publicKeyHash: jacs.hashString(publicKeyB64),
404
+ keyAlgorithm,
405
+ capabilities: {
406
+ signing: true,
407
+ verification: true,
408
+ postQuantum
409
+ },
410
+ schemas: {
411
+ agent: 'https://hai.ai/schemas/agent/v1/agent.schema.json',
412
+ header: 'https://hai.ai/schemas/header/v1/header.schema.json',
413
+ signature: 'https://hai.ai/schemas/components/signature/v1/signature.schema.json'
414
+ },
415
+ endpoints: {
416
+ verify: '/jacs/verify',
417
+ sign: '/jacs/sign',
418
+ agent: '/jacs/agent'
419
+ }
420
+ };
421
+
422
+ // 4. JACS Public Key
423
+ documents['/.well-known/jacs-pubkey.json'] = {
424
+ publicKey: publicKeyB64,
425
+ publicKeyHash: jacs.hashString(publicKeyB64),
426
+ algorithm: keyAlgorithm,
427
+ agentId: agentData.jacsId,
428
+ agentVersion: agentData.jacsVersion,
429
+ timestamp: new Date().toISOString()
430
+ };
431
+
432
+ // 5. Extension descriptor
433
+ documents['/.well-known/jacs-extension.json'] = this.createExtensionDescriptor();
434
+
435
+ return documents;
436
+ }
437
+
438
+ /**
439
+ * Internal recursive verifier with cycle protection for parent signature chains.
440
+ * @private
441
+ */
442
+ _verifyWrappedArtifactInternal(wrappedArtifact, visited) {
443
+ const artifactId = wrappedArtifact && wrappedArtifact.jacsId;
444
+ if (artifactId && visited.has(artifactId)) {
445
+ throw new Error(`Cycle detected in parent signature chain at artifact ${artifactId}`);
446
+ }
447
+ if (artifactId) {
448
+ visited.add(artifactId);
449
+ }
450
+
451
+ try {
452
+ const isValid = jacs.verifyResponse(wrappedArtifact);
453
+ const signatureInfo = wrappedArtifact.jacsSignature || {};
454
+
455
+ const result = {
456
+ valid: isValid,
457
+ signerId: signatureInfo.agentID || 'unknown',
458
+ signerVersion: signatureInfo.agentVersion || 'unknown',
459
+ artifactType: wrappedArtifact.jacsType || 'unknown',
460
+ timestamp: wrappedArtifact.jacsVersionDate || '',
461
+ originalArtifact: wrappedArtifact.a2aArtifact || {}
462
+ };
463
+
464
+ const parents = wrappedArtifact.jacsParentSignatures;
465
+ if (Array.isArray(parents) && parents.length > 0) {
466
+ const parentResults = parents.map((parent, index) => {
467
+ try {
468
+ const parentResult = this._verifyWrappedArtifactInternal(parent, visited);
469
+ return {
470
+ index,
471
+ artifactId: parent.jacsId || 'unknown',
472
+ valid: !!parentResult.valid,
473
+ parentSignaturesValid: parentResult.parentSignaturesValid !== false
474
+ };
475
+ } catch (error) {
476
+ return {
477
+ index,
478
+ artifactId: parent && parent.jacsId ? parent.jacsId : 'unknown',
479
+ valid: false,
480
+ parentSignaturesValid: false,
481
+ error: error instanceof Error ? error.message : String(error)
482
+ };
483
+ }
484
+ });
485
+
486
+ result.parentSignaturesCount = parentResults.length;
487
+ result.parentVerificationResults = parentResults;
488
+ result.parentSignaturesValid = parentResults.every(
489
+ (entry) => entry.valid && entry.parentSignaturesValid
490
+ );
491
+ }
492
+
493
+ return result;
494
+ } finally {
495
+ if (artifactId) {
496
+ visited.delete(artifactId);
497
+ }
498
+ }
499
+ }
500
+
501
+ /**
502
+ * Build a JWKS document from a base64-encoded public key.
503
+ * @private
504
+ */
505
+ _buildJwks(publicKeyB64, agentData = {}) {
506
+ if (agentData.jwks && Array.isArray(agentData.jwks.keys)) {
507
+ return agentData.jwks;
508
+ }
509
+ if (agentData.jwk && typeof agentData.jwk === 'object') {
510
+ return { keys: [agentData.jwk] };
511
+ }
512
+
513
+ const keyAlgorithm = String(agentData.keyAlgorithm || '').toLowerCase();
514
+ const kid = String(agentData.jacsId || 'jacs-agent');
515
+
516
+ try {
517
+ const keyBytes = Buffer.from(publicKeyB64, 'base64');
518
+ if (keyBytes.length === 32) {
519
+ return {
520
+ keys: [{
521
+ kty: 'OKP',
522
+ crv: 'Ed25519',
523
+ x: keyBytes.toString('base64url'),
524
+ kid,
525
+ use: 'sig',
526
+ alg: 'EdDSA'
527
+ }]
528
+ };
529
+ }
530
+
531
+ let keyObject;
532
+ try {
533
+ keyObject = createPublicKey({ key: keyBytes, format: 'der', type: 'spki' });
534
+ } catch {
535
+ keyObject = createPublicKey(keyBytes.toString('utf8'));
536
+ }
537
+
538
+ const jwk = keyObject.export({ format: 'jwk' });
539
+ const alg = this._inferJwsAlg(keyAlgorithm, jwk);
540
+ return {
541
+ keys: [{
542
+ ...jwk,
543
+ kid,
544
+ use: 'sig',
545
+ ...(alg ? { alg } : {})
546
+ }]
547
+ };
548
+ } catch {
549
+ return { keys: [] };
550
+ }
551
+ }
552
+
553
+ /**
554
+ * Infer a JWS `alg` for a generated JWK.
555
+ * @private
556
+ */
557
+ _inferJwsAlg(keyAlgorithm, jwk) {
558
+ if (keyAlgorithm.includes('ring-ed25519') || keyAlgorithm.includes('ed25519')) {
559
+ return 'EdDSA';
560
+ }
561
+ if (keyAlgorithm.includes('rsa')) {
562
+ return 'RS256';
563
+ }
564
+ if (keyAlgorithm.includes('ecdsa') || keyAlgorithm.includes('es256')) {
565
+ return 'ES256';
566
+ }
567
+ if (jwk && jwk.kty === 'RSA') {
568
+ return 'RS256';
569
+ }
570
+ if (jwk && jwk.kty === 'OKP' && jwk.crv === 'Ed25519') {
571
+ return 'EdDSA';
572
+ }
573
+ if (jwk && jwk.kty === 'EC' && jwk.crv === 'P-256') {
574
+ return 'ES256';
575
+ }
576
+ return undefined;
577
+ }
578
+
579
+ /**
580
+ * Convert a name to a URL-friendly slug for skill IDs.
581
+ * @private
582
+ */
583
+ _slugify(name) {
584
+ return name
585
+ .toLowerCase()
586
+ .replace(/[\s_]+/g, '-')
587
+ .replace(/[^a-z0-9-]/g, '');
588
+ }
589
+
590
+ /**
591
+ * Derive tags from service/function context.
592
+ * @private
593
+ */
594
+ _deriveTags(serviceName, fnName) {
595
+ const tags = ['jacs'];
596
+ const serviceSlug = this._slugify(serviceName);
597
+ const fnSlug = this._slugify(fnName);
598
+ if (serviceSlug !== fnSlug) {
599
+ tags.push(serviceSlug);
600
+ }
601
+ tags.push(fnSlug);
602
+ return tags;
603
+ }
604
+ }
605
+
606
+ // Export classes and functions
607
+ module.exports = {
608
+ JACSA2AIntegration,
609
+ A2AAgentInterface,
610
+ A2AAgentSkill,
611
+ A2AAgentExtension,
612
+ A2AAgentCapabilities,
613
+ A2AAgentCardSignature,
614
+ A2AAgentCard,
615
+ A2A_PROTOCOL_VERSION,
616
+ JACS_EXTENSION_URI
617
+ };