@aiwg/cli 2026.8.16 → 2026.8.18

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 (55) hide show
  1. package/bin/aiwg.mjs +24 -2
  2. package/dist/src/a2a/agent-card.js +4 -1
  3. package/dist/src/a2a/client.js +148 -68
  4. package/dist/src/a2a/codecs.js +480 -0
  5. package/dist/src/a2a/events.js +226 -0
  6. package/dist/src/a2a/hitl-driver.js +8 -6
  7. package/dist/src/a2a/hitl.js +2 -1
  8. package/dist/src/a2a/http.js +85 -5
  9. package/dist/src/a2a/protocol.js +136 -0
  10. package/dist/src/a2a/types.js +4 -14
  11. package/dist/src/a2a/webhook.js +101 -4
  12. package/dist/src/artifacts/browser-export.js +1 -0
  13. package/dist/src/artifacts/cli.js +1 -1
  14. package/dist/src/artifacts/fortemi-core-query-adapter.js +6 -0
  15. package/dist/src/artifacts/types.js +73 -1
  16. package/dist/src/audit/operator-decision.js +15 -1
  17. package/dist/src/channel/manager.mjs +89 -17
  18. package/dist/src/cli/agent-spawn.js +4 -2
  19. package/dist/src/cli/handlers/cockpit.js +41 -0
  20. package/dist/src/cli/handlers/help.js +1 -1
  21. package/dist/src/cli/handlers/index.js +3 -1
  22. package/dist/src/cli/handlers/installation.js +79 -0
  23. package/dist/src/cli/handlers/ralph.js +2 -1
  24. package/dist/src/cli/handlers/refresh.js +4 -2
  25. package/dist/src/cli/handlers/runtime-info.js +9 -1
  26. package/dist/src/cli/handlers/sdlc-accelerate.js +2 -1
  27. package/dist/src/cli/handlers/serve.js +107 -4
  28. package/dist/src/cli/handlers/session.js +12 -26
  29. package/dist/src/cli/handlers/use.js +117 -12
  30. package/dist/src/cli/handlers/utilities.js +4 -0
  31. package/dist/src/cli/handlers/version.js +4 -0
  32. package/dist/src/cli/handlers/workspace.js +24 -2
  33. package/dist/src/cli/services/deployment-verification.js +9 -2
  34. package/dist/src/cockpit/doctor.js +257 -0
  35. package/dist/src/config/aiwg-config.js +36 -3
  36. package/dist/src/config/user-config-dir.mjs +29 -0
  37. package/dist/src/config/user-config.js +4 -22
  38. package/dist/src/extensions/commands/definitions.js +20 -1
  39. package/dist/src/features/catalog.js +2 -1
  40. package/dist/src/flow/graph-metadata.js +56 -0
  41. package/dist/src/installation/manager.mjs +243 -0
  42. package/dist/src/providers/transformation-receipt-integration.js +130 -3
  43. package/dist/src/security/artifact-verifier.js +7 -1
  44. package/dist/src/serve/a2a-terminal-observer.js +28 -5
  45. package/dist/src/serve/dispatch-router.js +32 -4
  46. package/dist/src/serve/executor-registry.js +29 -0
  47. package/dist/src/serve/mission-conductor.js +15 -1
  48. package/dist/src/serve/pty-bridge.js +6 -11
  49. package/dist/src/serve/stack-adapters.js +2 -1
  50. package/dist/src/serve/telemetry.js +5 -1
  51. package/dist/src/skills/run.js +15 -6
  52. package/dist/src/update/checker.mjs +16 -15
  53. package/dist/src/update/notifier.mjs +8 -3
  54. package/dist/src/update/service.mjs +49 -5
  55. package/package.json +2 -1
package/bin/aiwg.mjs CHANGED
@@ -46,7 +46,18 @@ function detectVersionChannel(version) {
46
46
  if (version.includes('-alpha.')) return 'alpha';
47
47
  if (version.includes('-nightly.')) return 'nightly';
48
48
  try {
49
- const raw = readFileSync(path.join(os.homedir(), '.aiwg', 'channel.json'), 'utf8');
49
+ const legacyDir = path.join(os.homedir(), '.aiwg');
50
+ const xdgDir = path.join(os.homedir(), '.config', 'aiwg');
51
+ const configDir = process.env.AIWG_CONFIG
52
+ ? path.resolve(process.env.AIWG_CONFIG)
53
+ : existsSync(legacyDir) ? legacyDir : existsSync(xdgDir) ? xdgDir : legacyDir;
54
+ const installationFile = path.join(configDir, 'installation.json');
55
+ if (existsSync(installationFile)) {
56
+ const installation = JSON.parse(readFileSync(installationFile, 'utf8'));
57
+ if (installation?.runMode === 'development') return 'dev';
58
+ if (typeof installation?.channel === 'string') return installation.channel;
59
+ }
60
+ const raw = readFileSync(path.join(configDir, 'channel.json'), 'utf8');
50
61
  const cfg = JSON.parse(raw);
51
62
  if (cfg?.devMode) return 'dev';
52
63
  if (typeof cfg?.channel === 'string') return cfg.channel;
@@ -124,6 +135,7 @@ const FAST_HELP_TEXT = `
124
135
 
125
136
  VALIDATION
126
137
  validate-metadata [path] Validate AIWG component metadata (defaults to agentic/code)
138
+ installation <action> Inspect/adopt/switch canonical global installation
127
139
  verify <artifact> Verify DSSE provenance using an explicit versioned trust root
128
140
  verify trust <action> Bootstrap, update, or inspect artifact trust state
129
141
  context-firewall [scan] Audit provider context, trust, drift, poisoning signals, and budget
@@ -368,9 +380,19 @@ async function main() {
368
380
 
369
381
  // Resolve the active router once. In dev mode this points into the checkout,
370
382
  // while packageRoot still points at the globally installed launcher.
371
- const routerPath = await resolveRouterPath();
383
+ const routerPath = args[0] === 'installation'
384
+ ? path.join(packageRoot, 'dist', 'src', 'cli', 'router.js')
385
+ : await resolveRouterPath();
372
386
  const activePackageRoot = path.resolve(path.dirname(routerPath), '..', '..', '..');
373
387
 
388
+ // Fail closed when a different installation wins PATH resolution. Recovery
389
+ // commands remain reachable so an operator can explicitly adopt or switch.
390
+ if (args[0] !== 'installation') {
391
+ const identityPath = path.join(activePackageRoot, 'dist', 'src', 'installation', 'manager.mjs');
392
+ const { assertCanonicalInstallation } = await import(pathToFileURL(identityPath).href);
393
+ assertCanonicalInstallation({ actualRoot: activePackageRoot });
394
+ }
395
+
374
396
  // Wire up the logger level from -v/-vv/--quiet/AIWG_LOG_LEVEL before any
375
397
  // handler runs, and stamp the top-level invocation ID so the logger can
376
398
  // tag every record with it.
@@ -11,6 +11,7 @@
11
11
  // The card declares required + optional extensions, supported transports,
12
12
  // and skills.
13
13
  import { loadJwkSet, verifyAgentCardSignature } from './jws.js';
14
+ import { normalizeAgentCard } from './protocol.js';
14
15
  const DEFAULT_TTL_MS = 5 * 60 * 1000;
15
16
  export class AgentCardCache {
16
17
  ttlMs;
@@ -83,8 +84,9 @@ export async function fetchAgentCard(host, instanceId, opts = {}) {
83
84
  catch (err) {
84
85
  throw new Error(`fetchAgentCard: invalid JSON from ${url}: ${err.message}`);
85
86
  }
87
+ const normalized = normalizeAgentCard(card);
86
88
  if (opts.skipVerify) {
87
- return { card, raw, verifiedAt: new Date().toISOString() };
89
+ return { card, normalized, raw, verifiedAt: new Date().toISOString() };
88
90
  }
89
91
  let jwks = opts.jwks;
90
92
  if (!jwks) {
@@ -98,6 +100,7 @@ export async function fetchAgentCard(host, instanceId, opts = {}) {
98
100
  verifyAgentCardSignature(raw, jwks);
99
101
  const verified = {
100
102
  card,
103
+ normalized,
101
104
  raw,
102
105
  verifiedAt: new Date().toISOString(),
103
106
  };
@@ -15,6 +15,9 @@
15
15
  // GET /agents/{id}/v1/extendedAgentCard (extended card)
16
16
  // GET /agents/{id}/v1/card (legacy extended card)
17
17
  import { A2AError, A2AHttpClient } from './http.js';
18
+ import { decodePushNotificationConfig, decodeSendMessageResponse, decodeTask, encodeMessage, encodePushNotificationConfig, } from './codecs.js';
19
+ import { A2AEventReconciler, decodeStreamResponse } from './events.js';
20
+ import { normalizeAgentCard, selectAgentInterface } from './protocol.js';
18
21
  export const A2A_RUNTIME_V1 = 'https://agentic-sandbox.aiwg.io/extensions/runtime/v1';
19
22
  export const A2A_IDEMPOTENCY_V1 = 'https://agentic-sandbox.aiwg.io/extensions/idempotency/v1';
20
23
  export const A2A_HITL_PROMPT_V1 = 'https://agentic-sandbox.aiwg.io/extensions/hitl-prompt/v1';
@@ -25,21 +28,84 @@ export const A2A_PTY_EXTENSIONS_V1 = 'https://agentic-sandbox.aiwg.io/extensions
25
28
  export const DEFAULT_REQUIRED_EXTENSIONS = [A2A_RUNTIME_V1, A2A_IDEMPOTENCY_V1];
26
29
  export class A2AClient {
27
30
  instanceId;
31
+ protocolVersion;
32
+ protocolPolicy;
33
+ selectedInterface;
28
34
  http;
29
35
  extensionSet;
36
+ fallbackClient;
37
+ allowProtocolFallback;
38
+ onProtocolFallback;
30
39
  constructor(opts) {
31
40
  this.instanceId = opts.instanceId;
41
+ this.protocolPolicy = opts.protocolPolicy ?? opts.protocolVersion ?? '0.3';
42
+ if (this.protocolPolicy === 'auto' && !opts.selectedInterface) {
43
+ throw new Error('A2AClient protocolPolicy=auto requires A2AClient.negotiate() or selectedInterface');
44
+ }
45
+ this.selectedInterface = opts.selectedInterface;
46
+ this.protocolVersion = opts.selectedInterface?.protocolVersion ?? opts.protocolVersion ?? '0.3';
47
+ this.allowProtocolFallback = opts.allowProtocolFallback ?? false;
48
+ this.onProtocolFallback = opts.onProtocolFallback;
32
49
  const required = opts.requiredExtensions ?? DEFAULT_REQUIRED_EXTENSIONS;
33
50
  const optional = opts.optionalExtensions ?? [];
34
51
  this.extensionSet = [...required, ...optional];
35
52
  this.http = new A2AHttpClient({
36
53
  ...opts,
54
+ baseUrl: opts.selectedInterface?.url ?? opts.baseUrl,
55
+ protocolVersion: this.protocolVersion,
37
56
  defaultExtensions: this.extensionSet,
38
57
  });
58
+ if (opts.selectedInterface && opts.onProtocolSelection) {
59
+ opts.onProtocolSelection({
60
+ selected: this.protocolVersion,
61
+ interface: opts.selectedInterface,
62
+ policy: this.protocolPolicy,
63
+ });
64
+ }
65
+ }
66
+ /** Discover, normalize, and select an AgentCard interface deterministically. */
67
+ static async negotiate(opts) {
68
+ const policy = opts.protocolPolicy ?? 'auto';
69
+ const { selectedInterface: _ignoredSelectedInterface, protocolVersion: _ignoredProtocolVersion, onProtocolSelection, ...negotiationOpts } = opts;
70
+ const discovery = new A2AClient({ ...negotiationOpts, protocolPolicy: '0.3', protocolVersion: '0.3' });
71
+ const card = await discovery.getAgentCard();
72
+ const normalized = normalizeAgentCard(card);
73
+ const selected = selectAgentInterface(normalized, { policy });
74
+ const client = new A2AClient({
75
+ ...negotiationOpts,
76
+ protocolPolicy: policy,
77
+ protocolVersion: selected.protocolVersion,
78
+ selectedInterface: selected,
79
+ });
80
+ if (policy === 'auto' && selected.protocolVersion === '1.0' && opts.allowProtocolFallback) {
81
+ try {
82
+ const fallback = selectAgentInterface(normalized, { policy: '0.3' });
83
+ client.fallbackClient = new A2AClient({
84
+ ...negotiationOpts,
85
+ protocolPolicy: '0.3',
86
+ protocolVersion: '0.3',
87
+ selectedInterface: fallback,
88
+ allowProtocolFallback: false,
89
+ });
90
+ }
91
+ catch {
92
+ // No compatible 0.3 interface; auto remains 1.0-only.
93
+ }
94
+ }
95
+ // Fire once for the interface actually selected for use; the dormant
96
+ // downgrade adapter is not itself a selection event.
97
+ onProtocolSelection?.({ selected: selected.protocolVersion, interface: selected, policy });
98
+ return client;
39
99
  }
40
100
  agentPath() {
41
101
  return `/agents/${encodeURIComponent(this.instanceId)}/v1`;
42
102
  }
103
+ operationPath(v1Path, legacyPath) {
104
+ if (this.selectedInterface) {
105
+ return this.protocolVersion === '1.0' ? `/${v1Path}` : `/v1/${legacyPath}`;
106
+ }
107
+ return `${this.agentPath()}/${legacyPath}`;
108
+ }
43
109
  // ---------- AgentCard ----------
44
110
  /**
45
111
  * Fetch the well-known unsigned card. Callers that need verification should
@@ -115,15 +181,32 @@ export class A2AClient {
115
181
  * `idempotentReplayed: true`.
116
182
  */
117
183
  async sendMessage(message, opts = {}) {
118
- const path = `${this.agentPath()}/messages:send`;
184
+ const path = this.operationPath('message:send', 'messages:send');
119
185
  const requestOptions = {
120
186
  method: 'POST',
121
- body: { message },
187
+ body: { message: encodeMessage(this.protocolVersion, message) },
122
188
  extensions: opts.extensions ? [...opts.extensions] : this.extensionSet,
123
189
  };
124
190
  if (opts.signal)
125
191
  requestOptions.signal = opts.signal;
126
- const resp = await this.http.request(path, requestOptions);
192
+ let resp;
193
+ try {
194
+ resp = await this.http.request(path, requestOptions);
195
+ }
196
+ catch (error) {
197
+ if (error instanceof A2AError
198
+ && error.versionNotSupported
199
+ && this.protocolVersion === '1.0'
200
+ && this.protocolPolicy === 'auto'
201
+ && this.allowProtocolFallback
202
+ && this.fallbackClient) {
203
+ const reason = `${error.problem.type}: ${error.problem.detail ?? error.problem.title}`;
204
+ this.onProtocolFallback?.({ from: '1.0', to: '0.3', reason });
205
+ const fallback = await this.fallbackClient.sendMessage(message, opts);
206
+ return { ...fallback, fallbackReason: reason };
207
+ }
208
+ throw error;
209
+ }
127
210
  if (!resp.body) {
128
211
  throw new A2AError(resp.status, path, {
129
212
  type: 'about:blank',
@@ -131,15 +214,18 @@ export class A2AClient {
131
214
  code: 'aiwg.empty_send_message',
132
215
  });
133
216
  }
217
+ const task = decodeSendMessageResponse(this.protocolVersion, resp.body);
134
218
  return {
135
- task: resp.body,
219
+ task,
136
220
  idempotentReplayed: resp.idempotentReplayed,
137
221
  activatedExtensions: resp.activatedExtensions,
222
+ protocolVersion: this.protocolVersion,
223
+ ...(this.selectedInterface ? { selectedInterface: this.selectedInterface } : {}),
138
224
  };
139
225
  }
140
226
  // ---------- Tasks ----------
141
227
  async getTask(taskId, opts = {}) {
142
- const path = `${this.agentPath()}/tasks/${encodeURIComponent(taskId)}`;
228
+ const path = this.operationPath(`tasks/${encodeURIComponent(taskId)}`, `tasks/${encodeURIComponent(taskId)}`);
143
229
  const requestOptions = { method: 'GET' };
144
230
  if (opts.signal)
145
231
  requestOptions.signal = opts.signal;
@@ -151,24 +237,27 @@ export class A2AClient {
151
237
  code: 'aiwg.empty_get_task',
152
238
  });
153
239
  }
154
- return resp.body;
240
+ return decodeTask(this.protocolVersion, resp.body);
155
241
  }
156
242
  /** List tasks for this instance, optionally filtered by state. */
157
243
  async listTasks(filter = {}) {
158
244
  const params = new URLSearchParams();
159
245
  if (filter.state)
160
- params.set('state', filter.state);
246
+ params.set(this.protocolVersion === '1.0' ? 'status' : 'state', filter.state);
161
247
  if (filter.limit !== undefined)
162
- params.set('limit', String(filter.limit));
248
+ params.set(this.protocolVersion === '1.0' ? 'pageSize' : 'limit', String(filter.limit));
163
249
  const query = params.toString();
164
- const path = `${this.agentPath()}/tasks${query ? '?' + query : ''}`;
250
+ const basePath = this.operationPath('tasks', 'tasks');
251
+ const path = `${basePath}${query ? '?' + query : ''}`;
165
252
  const resp = await this.http.request(path, { method: 'GET' });
166
253
  if (!resp.body)
167
254
  return [];
168
- return Array.isArray(resp.body) ? resp.body : resp.body.tasks ?? [];
255
+ const raw = Array.isArray(resp.body) ? resp.body : resp.body.tasks ?? [];
256
+ return raw.map((task, index) => decodeTask(this.protocolVersion, task, `$.tasks[${index}]`));
169
257
  }
170
258
  async cancelTask(taskId, opts = {}) {
171
- const path = `${this.agentPath()}/tasks/${encodeURIComponent(taskId)}/cancel`;
259
+ const encoded = encodeURIComponent(taskId);
260
+ const path = this.operationPath(`tasks/${encoded}:cancel`, `tasks/${encoded}/cancel`);
172
261
  const requestOptions = {
173
262
  method: 'POST',
174
263
  body: {},
@@ -184,7 +273,11 @@ export class A2AClient {
184
273
  code: 'aiwg.empty_cancel_task',
185
274
  });
186
275
  }
187
- return resp.body;
276
+ const body = this.protocolVersion === '1.0'
277
+ && resp.body && typeof resp.body === 'object' && 'task' in resp.body
278
+ ? resp.body.task
279
+ : resp.body;
280
+ return decodeTask(this.protocolVersion, body);
188
281
  }
189
282
  // ---------- Task subscription (SSE) ----------
190
283
  /**
@@ -205,7 +298,9 @@ export class A2AClient {
205
298
  params.set('replay_from', String(opts.replayFromSeq));
206
299
  }
207
300
  const query = params.toString();
208
- const path = `${this.agentPath()}/tasks/${encodeURIComponent(taskId)}/subscribe${query ? '?' + query : ''}`;
301
+ const encoded = encodeURIComponent(taskId);
302
+ const basePath = this.operationPath(`tasks/${encoded}:subscribe`, `tasks/${encoded}/subscribe`);
303
+ const path = `${basePath}${query ? '?' + query : ''}`;
209
304
  const controller = new AbortController();
210
305
  const externalSignal = opts.signal;
211
306
  if (externalSignal) {
@@ -215,10 +310,11 @@ export class A2AClient {
215
310
  externalSignal.addEventListener('abort', () => controller.abort(), { once: true });
216
311
  }
217
312
  const http = this.http;
313
+ const protocolVersion = this.protocolVersion;
218
314
  let cancelled = false;
219
315
  async function* iterate() {
220
316
  const resp = await http.request(path, {
221
- method: 'GET',
317
+ method: protocolVersion === '1.0' ? 'POST' : 'GET',
222
318
  headers: { accept: 'text/event-stream' },
223
319
  signal: controller.signal,
224
320
  raw: true,
@@ -238,12 +334,21 @@ export class A2AClient {
238
334
  code: 'aiwg.subscribe_empty_body',
239
335
  });
240
336
  }
337
+ const reconciler = new A2AEventReconciler({
338
+ taskId,
339
+ requireInitialSnapshot: protocolVersion === '1.0',
340
+ });
241
341
  for await (const frame of parseEventStream(resp.rawBody)) {
242
342
  if (cancelled)
243
343
  return;
244
- const event = decodeStreamEvent(frame);
245
- if (event)
246
- yield event;
344
+ const event = decodeStreamEvent(protocolVersion, frame);
345
+ if (!event)
346
+ continue;
347
+ const accepted = reconciler.accept(event);
348
+ if (accepted)
349
+ yield accepted;
350
+ if (reconciler.isTerminal())
351
+ return;
247
352
  }
248
353
  }
249
354
  const generator = iterate();
@@ -259,10 +364,10 @@ export class A2AClient {
259
364
  }
260
365
  // ---------- Push notification configs ----------
261
366
  async createPushNotificationConfig(taskId, config) {
262
- const path = `${this.agentPath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs`;
367
+ const path = this.operationPath(`tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs`, `tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs`);
263
368
  const resp = await this.http.request(path, {
264
369
  method: 'POST',
265
- body: config,
370
+ body: encodePushNotificationConfig(this.protocolVersion, config),
266
371
  extensions: [...this.extensionSet],
267
372
  });
268
373
  if (!resp.body) {
@@ -272,10 +377,11 @@ export class A2AClient {
272
377
  code: 'aiwg.empty_push_config_create',
273
378
  });
274
379
  }
275
- return resp.body;
380
+ return decodePushNotificationConfig(this.protocolVersion, resp.body);
276
381
  }
277
382
  async getPushNotificationConfig(taskId, configId) {
278
- const path = `${this.agentPath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`;
383
+ const suffix = `tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`;
384
+ const path = this.operationPath(suffix, suffix);
279
385
  const resp = await this.http.request(path, { method: 'GET' });
280
386
  if (!resp.body) {
281
387
  throw new A2AError(resp.status, path, {
@@ -284,10 +390,11 @@ export class A2AClient {
284
390
  code: 'aiwg.empty_push_config_get',
285
391
  });
286
392
  }
287
- return resp.body;
393
+ return decodePushNotificationConfig(this.protocolVersion, resp.body);
288
394
  }
289
395
  async deletePushNotificationConfig(taskId, configId) {
290
- const path = `${this.agentPath()}/tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`;
396
+ const suffix = `tasks/${encodeURIComponent(taskId)}/pushNotificationConfigs/${encodeURIComponent(configId)}`;
397
+ const path = this.operationPath(suffix, suffix);
291
398
  await this.http.request(path, {
292
399
  method: 'DELETE',
293
400
  extensions: [...this.extensionSet],
@@ -357,55 +464,28 @@ export async function* parseEventStream(stream) {
357
464
  reader.releaseLock();
358
465
  }
359
466
  }
360
- function decodeStreamEvent(frame) {
467
+ export function decodeStreamEvent(protocolVersion, frame) {
361
468
  if (!frame.data)
362
469
  return null;
470
+ let obj;
363
471
  try {
364
- const obj = JSON.parse(frame.data);
365
- // Prefer the `kind` field if present (matches StreamEvent discriminator).
366
- // Otherwise fall back to `event:` header from the frame.
367
- const kindFromBody = typeof obj['kind'] === 'string' ? obj['kind'] : undefined;
368
- const kind = kindFromBody ?? frame.event;
369
- if (!kind)
370
- return null;
371
- switch (kind) {
372
- case 'task-state':
373
- if (obj['task']) {
374
- return { kind: 'task-state', task: obj['task'] };
375
- }
376
- return null;
377
- case 'status-update':
378
- if (typeof obj['taskId'] === 'string' && obj['status']) {
379
- const out = {
380
- kind: 'status-update',
381
- taskId: obj['taskId'],
382
- status: obj['status'],
383
- };
384
- if (typeof obj['final'] === 'boolean') {
385
- out.final = obj['final'];
386
- }
387
- return out;
388
- }
389
- return null;
390
- case 'artifact-update':
391
- if (typeof obj['taskId'] === 'string' && obj['artifact']) {
392
- const out = {
393
- kind: 'artifact-update',
394
- taskId: obj['taskId'],
395
- artifact: obj['artifact'],
396
- };
397
- if (typeof obj['append'] === 'boolean') {
398
- out.append = obj['append'];
399
- }
400
- return out;
401
- }
402
- return null;
403
- default:
404
- return null;
405
- }
472
+ obj = JSON.parse(frame.data);
406
473
  }
407
- catch {
408
- return null;
474
+ catch (error) {
475
+ throw new A2AError(502, 'sse', {
476
+ type: 'about:blank',
477
+ title: 'Invalid SSE JSON',
478
+ detail: error.message,
479
+ code: 'aiwg.invalid_stream_json',
480
+ });
409
481
  }
482
+ const sequence = frame.id !== undefined && /^\d+$/.test(frame.id)
483
+ ? Number(frame.id)
484
+ : undefined;
485
+ return decodeStreamResponse(protocolVersion, obj, {
486
+ ...(frame.event ? { eventName: frame.event } : {}),
487
+ ...(frame.id ? { eventId: frame.id } : {}),
488
+ ...(sequence !== undefined ? { sequence } : {}),
489
+ });
410
490
  }
411
491
  //# sourceMappingURL=client.js.map