@band-ai/sdk 0.3.3 → 0.4.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 (97) hide show
  1. package/README.md +54 -1
  2. package/dist/{ClaudeSDKAdapter-CXud2DBE.d.ts → CopilotACPAdapter-BCpbHFB0.d.ts} +240 -19
  3. package/dist/{ClaudeSDKAdapter-Cx6zhSaG.d.cts → CopilotACPAdapter-D4VWg8lJ.d.cts} +240 -19
  4. package/dist/{opencode-2YdscIc1.d.cts → acp-client-D-I_5lK-.d.cts} +23 -4
  5. package/dist/{opencode-Bld62x5t.d.ts → acp-client-DUXyczlF.d.ts} +23 -4
  6. package/dist/acp-server-BlcYMxKt.d.ts +17 -0
  7. package/dist/acp-server-CJhclo6P.d.cts +17 -0
  8. package/dist/adapters.cjs +2217 -973
  9. package/dist/adapters.d.cts +21 -132
  10. package/dist/adapters.d.ts +21 -132
  11. package/dist/adapters.js +26 -1914
  12. package/dist/{backends-D-dBdDm-.d.ts → backends-CgsPQa9B.d.ts} +2 -2
  13. package/dist/{backends-ZLxmQrTZ.d.cts → backends-Dh2WXcHQ.d.cts} +2 -2
  14. package/dist/{chunk-JY75VSQK.js → chunk-4OVNFDD7.js} +14 -1
  15. package/dist/chunk-6BE2EG5P.js +128 -0
  16. package/dist/{chunk-U6AFCLYZ.js → chunk-6TGXV5TE.js} +1824 -514
  17. package/dist/{chunk-OH6VVVKT.js → chunk-CBCHTTUY.js} +10 -0
  18. package/dist/{chunk-K5KAEYPF.js → chunk-HNWZZXAE.js} +288 -120
  19. package/dist/chunk-I34SXMTJ.js +6 -0
  20. package/dist/{chunk-XWHWJP4I.js → chunk-JDW5WSGF.js} +2 -2
  21. package/dist/{chunk-KXJ74J73.js → chunk-JIZE4EC6.js} +4 -10
  22. package/dist/chunk-OGOXY43L.js +119 -0
  23. package/dist/{chunk-W5IAI6Z4.js → chunk-OVXVZUPO.js} +5 -5
  24. package/dist/{chunk-YJ42AZO4.js → chunk-RJNJUISR.js} +5 -2
  25. package/dist/{chunk-MQY74R7D.js → chunk-SHZBLLDQ.js} +21 -9
  26. package/dist/{chunk-JADCAMZY.js → chunk-TGDDFBYS.js} +45 -0
  27. package/dist/{chunk-US226AS4.js → chunk-UENIC5FJ.js} +14 -11
  28. package/dist/{chunk-WA7CHXUS.js → chunk-UL3Y5C4J.js} +2 -2
  29. package/dist/{chunk-WL2HNJZ6.js → chunk-VOMJRQOM.js} +3 -3
  30. package/dist/{chunk-MQBBWYRP.js → chunk-YF7K4UB3.js} +3352 -572
  31. package/dist/{chunk-OD2G5LFQ.js → chunk-ZYOXI4JL.js} +41 -2
  32. package/dist/config.cjs +2 -12
  33. package/dist/config.js +2 -2
  34. package/dist/converters.cjs +36 -11
  35. package/dist/converters.d.cts +7 -5
  36. package/dist/converters.d.ts +7 -5
  37. package/dist/converters.js +11 -13
  38. package/dist/core.cjs +239 -5
  39. package/dist/core.d.cts +71 -6
  40. package/dist/core.d.ts +71 -6
  41. package/dist/core.js +31 -6
  42. package/dist/{customTools-BzF0IISO.d.cts → customTools-Bfecd0mJ.d.cts} +1 -1
  43. package/dist/{customTools-BzF0IISO.d.ts → customTools-Bfecd0mJ.d.ts} +1 -1
  44. package/dist/{disconnectReason-IuADl6TH.d.ts → disconnectReason-Cctmg1SN.d.cts} +1 -1
  45. package/dist/{disconnectReason-IuADl6TH.d.cts → disconnectReason-Cctmg1SN.d.ts} +1 -1
  46. package/dist/{dtos-B8KU_q4d.d.ts → dtos-CjQsN1_h.d.cts} +5 -12
  47. package/dist/{dtos-B8KU_q4d.d.cts → dtos-CjQsN1_h.d.ts} +5 -12
  48. package/dist/index.cjs +5237 -1474
  49. package/dist/index.d.cts +61 -22
  50. package/dist/index.d.ts +61 -22
  51. package/dist/index.js +151 -43
  52. package/dist/linear.cjs +45 -7
  53. package/dist/linear.d.cts +4 -3
  54. package/dist/linear.d.ts +4 -3
  55. package/dist/linear.js +10 -10
  56. package/dist/mcp-claude.cjs +11 -6
  57. package/dist/mcp-claude.d.cts +4 -3
  58. package/dist/mcp-claude.d.ts +4 -3
  59. package/dist/mcp-claude.js +4 -5
  60. package/dist/mcp.cjs +13 -7
  61. package/dist/mcp.d.cts +6 -5
  62. package/dist/mcp.d.ts +6 -5
  63. package/dist/mcp.js +5 -6
  64. package/dist/{pagination-DqdkQArT.d.cts → pagination-BZDhB_nO.d.cts} +2 -2
  65. package/dist/{pagination-BgEx-HeL.d.ts → pagination-CPz0J_vQ.d.ts} +2 -2
  66. package/dist/{protocols-Bz1rxcUg.d.cts → protocols-CabgO52c.d.cts} +38 -3
  67. package/dist/{protocols-Bwwcql0h.d.ts → protocols-Dpci6uae.d.ts} +38 -3
  68. package/dist/rest.cjs +43 -1
  69. package/dist/rest.d.cts +6 -5
  70. package/dist/rest.d.ts +6 -5
  71. package/dist/rest.js +6 -5
  72. package/dist/runtime.cjs +2113 -597
  73. package/dist/runtime.d.cts +44 -25
  74. package/dist/runtime.d.ts +44 -25
  75. package/dist/runtime.js +11 -11
  76. package/dist/{schemas-BgJhlqqO.d.cts → schemas-BKnh4fKR.d.cts} +209 -24
  77. package/dist/{schemas-C4mK1WSs.d.ts → schemas-CDa3V_I5.d.ts} +209 -24
  78. package/dist/{sdk-BTciSLRQ.d.cts → sdk-CXNqzoY1.d.cts} +1 -1
  79. package/dist/{sdk-qKJtMma_.d.ts → sdk-Cin80BTC.d.ts} +1 -1
  80. package/dist/{simpleAdapter-BpT4XbZC.d.cts → simpleAdapter-D75rcz9n.d.cts} +3 -1
  81. package/dist/{simpleAdapter--wznuoOw.d.ts → simpleAdapter-eLvsAQNo.d.ts} +3 -1
  82. package/dist/testing.cjs +25 -7
  83. package/dist/testing.d.cts +5 -3
  84. package/dist/testing.d.ts +5 -3
  85. package/dist/testing.js +9 -6
  86. package/dist/{types-BivGO7I9.d.ts → types-42-HWeX8.d.cts} +10 -4
  87. package/dist/types-CKU1N0SK.d.ts +137 -0
  88. package/dist/{types-BgYFd_Yw.d.cts → types-CjxZekMH.d.ts} +10 -4
  89. package/dist/types-DtcOLALn.d.cts +137 -0
  90. package/package.json +25 -26
  91. package/dist/acp-server-CiUqN3G3.d.ts +0 -24
  92. package/dist/acp-server-Dlj7D563.d.cts +0 -24
  93. package/dist/chunk-AVVDPXP4.js +0 -39
  94. package/dist/chunk-F63TPOI3.js +0 -29
  95. package/dist/chunk-V5TSWS7P.js +0 -62
  96. package/dist/types-BNNtOXu3.d.cts +0 -446
  97. package/dist/types-DFUtpxah.d.ts +0 -446
@@ -113,7 +113,17 @@ function buildParticipantsMessage(participants) {
113
113
  return lines.join("\n");
114
114
  }
115
115
 
116
+ // src/core/deferred.ts
117
+ function createDeferred() {
118
+ let resolve;
119
+ const promise = new Promise((innerResolve) => {
120
+ resolve = innerResolve;
121
+ });
122
+ return { promise, resolve };
123
+ }
124
+
116
125
  export {
126
+ createDeferred,
117
127
  HistoryProvider,
118
128
  SYNTHETIC_SENDER_TYPE,
119
129
  SYNTHETIC_CONTACT_EVENTS_SENDER_ID,
@@ -1,21 +1,34 @@
1
1
  import {
2
- SimpleAdapter
3
- } from "./chunk-AVVDPXP4.js";
2
+ FAILURE_CODE_TIMEOUT,
3
+ SimpleAdapter,
4
+ agentFailure,
5
+ deliverReply,
6
+ reportProviderTurnFailure,
7
+ reportTurnFailure
8
+ } from "./chunk-OGOXY43L.js";
4
9
  import {
5
10
  renderSystemPrompt
6
- } from "./chunk-YJ42AZO4.js";
11
+ } from "./chunk-RJNJUISR.js";
7
12
  import {
8
- asErrorMessage,
9
13
  asNonEmptyString,
10
- asOptionalRecord
11
- } from "./chunk-KXJ74J73.js";
14
+ asOptionalRecord,
15
+ asString
16
+ } from "./chunk-JIZE4EC6.js";
12
17
  import {
13
- NoopLogger
14
- } from "./chunk-OD2G5LFQ.js";
18
+ asErrorMessage
19
+ } from "./chunk-6BE2EG5P.js";
20
+ import {
21
+ FAILURE_METADATA_KEY
22
+ } from "./chunk-TGDDFBYS.js";
23
+ import {
24
+ GATEWAY_FREE_TEXT_SENSITIVE_KEY_TERMS,
25
+ resolveLogger
26
+ } from "./chunk-ZYOXI4JL.js";
15
27
  import {
16
28
  UnsupportedFeatureError,
17
- ValidationError
18
- } from "./chunk-JY75VSQK.js";
29
+ ValidationError,
30
+ rethrowIfRecoverableTurnFailure
31
+ } from "./chunk-4OVNFDD7.js";
19
32
 
20
33
  // src/adapters/shared/history.ts
21
34
  function findLatestTaskMetadata(raw, predicate) {
@@ -32,6 +45,76 @@ function findLatestTaskMetadata(raw, predicate) {
32
45
  }
33
46
  return null;
34
47
  }
48
+ var UNKNOWN_SENDER = "Unknown";
49
+ function mergeConsecutiveSameRole(turns) {
50
+ const merged = [];
51
+ let attributedTo = "";
52
+ for (const turn of turns) {
53
+ if (!turn.content) {
54
+ continue;
55
+ }
56
+ const previous = merged[merged.length - 1];
57
+ if (!previous || previous.role !== turn.role) {
58
+ merged.push({ ...turn });
59
+ attributedTo = turn.sender ?? "";
60
+ continue;
61
+ }
62
+ const speaker = turn.sender ?? "";
63
+ previous.content += speaker === attributedTo ? `
64
+ ${turn.content}` : `
65
+ ${nameContribution(turn.content, speaker)}`;
66
+ attributedTo = speaker;
67
+ }
68
+ return merged;
69
+ }
70
+ function nameContribution(content, speaker) {
71
+ if (speaker !== "" && content.startsWith(`[${speaker}]: `)) {
72
+ return content;
73
+ }
74
+ return `[${speaker || UNKNOWN_SENDER}]: ${content}`;
75
+ }
76
+ function pairUserAssistantTurns(turns) {
77
+ const paired = [];
78
+ let index = 0;
79
+ while (index < turns.length) {
80
+ const current = turns[index];
81
+ if (current.role !== "user" || !current.content) {
82
+ index += 1;
83
+ continue;
84
+ }
85
+ const next = turns[index + 1];
86
+ if (next && next.role === "assistant" && next.content) {
87
+ paired.push(current, next);
88
+ index += 2;
89
+ continue;
90
+ }
91
+ if (index === turns.length - 1) {
92
+ paired.push(current);
93
+ }
94
+ index += 1;
95
+ }
96
+ return paired;
97
+ }
98
+ function takeRecentTurns(turns, limit) {
99
+ const cap = Number.isNaN(limit) ? 0 : Math.floor(limit);
100
+ if (cap <= 0) {
101
+ return [];
102
+ }
103
+ if (turns.length <= cap) {
104
+ return [...turns];
105
+ }
106
+ const truncated = turns.slice(-cap);
107
+ if (truncated[0]?.role === "assistant") {
108
+ truncated.shift();
109
+ }
110
+ return truncated;
111
+ }
112
+ function selectCompleteExchanges(history, limit) {
113
+ return takeRecentTurns(
114
+ pairUserAssistantTurns(mergeConsecutiveSameRole(history)),
115
+ limit
116
+ );
117
+ }
35
118
 
36
119
  // src/converters/shared.ts
37
120
  function asOptionalString(value) {
@@ -280,6 +363,7 @@ import { randomUUID } from "crypto";
280
363
  var TERMINAL_STATES = /* @__PURE__ */ new Set(["completed", "failed", "canceled", "rejected", "auth-required"]);
281
364
  var DEFAULT_MAX_STREAM_EVENTS = 1e4;
282
365
  var A2AAdapter = class extends SimpleAdapter {
366
+ provider = "a2a";
283
367
  remoteUrl;
284
368
  authHeaders;
285
369
  streaming;
@@ -299,20 +383,20 @@ var A2AAdapter = class extends SimpleAdapter {
299
383
  this.streaming = options.streaming ?? true;
300
384
  this.clientFactory = options.clientFactory;
301
385
  this.maxStreamEvents = normalizeMaxStreamEvents(options.maxStreamEvents);
302
- this.logger = options.logger ?? new NoopLogger();
386
+ this.logger = resolveLogger(options.logger);
303
387
  }
304
388
  async onStarted(agentName, agentDescription) {
305
389
  await super.onStarted(agentName, agentDescription);
306
390
  this.client = await this.createClient();
307
391
  }
308
392
  async onMessage(message, tools, history, _participantsMessage, _contactsMessage, context) {
309
- const client = this.client ?? await this.createClient();
310
- this.client = client;
311
- if (context.isSessionBootstrap) {
312
- await this.rehydrateFromHistory(context.roomId, history, client);
313
- }
314
- const request = this.toSendParams(message, context.roomId);
315
393
  try {
394
+ const client = this.client ?? await this.createClient();
395
+ this.client = client;
396
+ if (context.isSessionBootstrap) {
397
+ await this.rehydrateFromHistory(context.roomId, history, client);
398
+ }
399
+ const request = this.toSendParams(message, context.roomId);
316
400
  if (this.streaming) {
317
401
  let streamEventCount = 0;
318
402
  for await (const event of client.sendMessageStream(request)) {
@@ -323,6 +407,7 @@ var A2AAdapter = class extends SimpleAdapter {
323
407
  try {
324
408
  await this.handleEvent(event, tools, context.roomId, message.senderId);
325
409
  } catch (handleError) {
410
+ rethrowIfRecoverableTurnFailure(handleError);
326
411
  this.logger.error("A2A stream event handling failed; continuing stream", {
327
412
  roomId: context.roomId,
328
413
  remoteUrl: this.remoteUrl,
@@ -336,24 +421,11 @@ var A2AAdapter = class extends SimpleAdapter {
336
421
  const response = await client.sendMessage(request);
337
422
  await this.handleEvent(response, tools, context.roomId, message.senderId);
338
423
  } catch (error) {
339
- const errorMessage = asErrorMessage(error);
340
- this.logger.error("A2A adapter request failed", {
424
+ rethrowIfRecoverableTurnFailure(error);
425
+ await reportProviderTurnFailure(tools, this.logger, this.provider, "A2A adapter request failed", error, {
341
426
  roomId: context.roomId,
342
- remoteUrl: this.remoteUrl,
343
- error
427
+ remoteUrl: this.remoteUrl
344
428
  });
345
- try {
346
- await tools.sendEvent(`A2A agent error: ${errorMessage}`, "error", {
347
- a2a_error: errorMessage
348
- });
349
- } catch (eventError) {
350
- this.logger.warn("A2A adapter failed to emit error event", {
351
- roomId: context.roomId,
352
- remoteUrl: this.remoteUrl,
353
- error: eventError
354
- });
355
- }
356
- throw error instanceof Error ? error : new Error(errorMessage);
357
429
  }
358
430
  }
359
431
  async onCleanup(roomId) {
@@ -373,7 +445,7 @@ var A2AAdapter = class extends SimpleAdapter {
373
445
  if (isMessageEvent(event)) {
374
446
  const text = extractMessageText(event);
375
447
  if (text) {
376
- await tools.sendMessage(text, [{ id: senderId }]);
448
+ await deliverReply(tools, text, [{ id: senderId }]);
377
449
  }
378
450
  return;
379
451
  }
@@ -388,7 +460,7 @@ var A2AAdapter = class extends SimpleAdapter {
388
460
  if (isArtifactUpdateEvent(event)) {
389
461
  const text = extractArtifactText(event.artifact);
390
462
  if (text) {
391
- await tools.sendMessage(text, [{ id: senderId }]);
463
+ await deliverReply(tools, text, [{ id: senderId }]);
392
464
  }
393
465
  }
394
466
  }
@@ -459,23 +531,29 @@ var A2AAdapter = class extends SimpleAdapter {
459
531
  }
460
532
  if (input.state === "input-required") {
461
533
  const text = extractMessageText(input.statusMessage) ?? "Please provide more information.";
462
- await input.tools.sendMessage(text, [input.sender]);
534
+ await deliverReply(input.tools, text, [input.sender]);
463
535
  await this.emitTaskEvent(input.tools, input.contextId, input.taskId, input.state);
464
536
  return;
465
537
  }
466
538
  if (input.state === "completed") {
467
- if (input.completedMessage) {
468
- await input.tools.sendMessage(input.completedMessage, [input.sender]);
539
+ try {
540
+ if (input.completedMessage) {
541
+ await deliverReply(input.tools, input.completedMessage, [input.sender]);
542
+ }
543
+ await this.emitTaskEvent(input.tools, input.contextId, input.taskId, input.state);
544
+ } finally {
545
+ this.clearTaskTracking(input.key, input.roomId);
469
546
  }
470
- await this.emitTaskEvent(input.tools, input.contextId, input.taskId, input.state);
471
- this.clearTaskTracking(input.key, input.roomId);
472
547
  return;
473
548
  }
474
549
  if (TERMINAL_STATES.has(input.state)) {
475
550
  const text = extractMessageText(input.statusMessage) ?? `A2A task ${input.state}`;
476
- await input.tools.sendEvent(text, "error", { a2a_state: input.state });
477
- await this.emitTaskEvent(input.tools, input.contextId, input.taskId, input.state);
478
- this.clearTaskTracking(input.key, input.roomId);
551
+ try {
552
+ await this.emitTaskEvent(input.tools, input.contextId, input.taskId, input.state);
553
+ } finally {
554
+ this.clearTaskTracking(input.key, input.roomId);
555
+ }
556
+ await reportTurnFailure(input.tools, agentFailure(this.provider, text, input.state), this.logger, { roomId: input.roomId });
479
557
  }
480
558
  }
481
559
  async emitTaskEvent(tools, contextId, taskId, state) {
@@ -851,15 +929,57 @@ function buildStatusEvent(input) {
851
929
  };
852
930
  }
853
931
 
932
+ // src/adapters/a2a-gateway/failure.ts
933
+ import { AgentFailure } from "@band-ai/band-sdk-core";
934
+ var PROVIDER = "a2a-gateway";
935
+ var SENSITIVE_VALUE_PATTERN = new RegExp(
936
+ `(${GATEWAY_FREE_TEXT_SENSITIVE_KEY_TERMS})"?\\s*(?:[A-Za-z]+\\s*)?[:=]\\s*"?(?:[A-Za-z][\\w-]*\\s+)?[^\\s,;"]+`,
937
+ "gi"
938
+ );
939
+ function buildGatewayFailureMetadata(error, code, message = sanitizeGatewayErrorMessage(error)) {
940
+ return {
941
+ [FAILURE_METADATA_KEY]: new AgentFailure(
942
+ PROVIDER,
943
+ message,
944
+ code ?? (error instanceof Error ? error.name : "UnknownError")
945
+ ).toObject()
946
+ };
947
+ }
948
+ function sanitizeGatewayErrorMessage(error) {
949
+ const rawMessage = error instanceof Error ? error.message : String(error);
950
+ const trimmed = rawMessage.trim();
951
+ if (!trimmed) {
952
+ return "Unknown error";
953
+ }
954
+ const withBearerRedaction = trimmed.replace(/Bearer\s+[^\s,;]+/gi, "Bearer [REDACTED]").replace(SENSITIVE_VALUE_PATTERN, "$1=[REDACTED]");
955
+ const maxLength = 240;
956
+ if (withBearerRedaction.length <= maxLength) {
957
+ return withBearerRedaction;
958
+ }
959
+ return `${withBearerRedaction.slice(0, maxLength - 3)}...`;
960
+ }
961
+ function sanitizeForwardedFailure(value) {
962
+ const record = asOptionalRecord(value);
963
+ if (!record) {
964
+ return void 0;
965
+ }
966
+ const provider = asString(record.provider) ?? "unknown";
967
+ const code = asString(record.code) ?? void 0;
968
+ const message = sanitizeGatewayErrorMessage(asString(record.message) ?? "Unknown error");
969
+ return new AgentFailure(provider, message, code).toObject();
970
+ }
971
+
854
972
  // src/adapters/a2a-gateway/server.ts
855
973
  var GatewayPeerExecutor = class {
856
974
  peer;
857
975
  onRequest;
858
976
  onCancel;
977
+ logger;
859
978
  constructor(options) {
860
979
  this.peer = options.peer;
861
980
  this.onRequest = options.onRequest;
862
981
  this.onCancel = options.onCancel;
982
+ this.logger = options.logger;
863
983
  }
864
984
  async execute(requestContext, eventBus) {
865
985
  const taskId = asNonEmptyString(requestContext.taskId) ?? randomUUID3();
@@ -879,6 +999,12 @@ var GatewayPeerExecutor = class {
879
999
  }
880
1000
  }
881
1001
  } catch (error) {
1002
+ this.logger.warn("a2a_gateway.peer_request_failed", {
1003
+ peerId: this.peer.id,
1004
+ taskId,
1005
+ contextId,
1006
+ error
1007
+ });
882
1008
  eventBus.publish(
883
1009
  buildStatusEvent({
884
1010
  taskId,
@@ -886,7 +1012,7 @@ var GatewayPeerExecutor = class {
886
1012
  state: "failed",
887
1013
  final: true,
888
1014
  text: "Peer request failed.",
889
- metadata: buildGatewayExecutionFailureMetadata(error)
1015
+ metadata: buildGatewayFailureMetadata(error)
890
1016
  })
891
1017
  );
892
1018
  } finally {
@@ -914,10 +1040,12 @@ var GatewayPeerExecutor = class {
914
1040
  };
915
1041
  var GatewayServer = class {
916
1042
  options;
1043
+ logger;
917
1044
  server = null;
918
1045
  started = false;
919
1046
  constructor(options) {
920
1047
  this.options = options;
1048
+ this.logger = resolveLogger(options.logger);
921
1049
  }
922
1050
  async start() {
923
1051
  if (this.started) {
@@ -987,7 +1115,8 @@ var GatewayServer = class {
987
1115
  new GatewayPeerExecutor({
988
1116
  peer,
989
1117
  onRequest: this.options.onRequest,
990
- onCancel: this.options.onCancel
1118
+ onCancel: this.options.onCancel,
1119
+ logger: this.logger
991
1120
  })
992
1121
  );
993
1122
  const userBuilder = createUserBuilder(
@@ -1244,25 +1373,6 @@ function verifyBearerAuthorization(headers, authToken) {
1244
1373
  }
1245
1374
  return safeHeaderEquals(authorization, `Bearer ${authToken}`);
1246
1375
  }
1247
- function buildGatewayExecutionFailureMetadata(error) {
1248
- return {
1249
- error_type: error instanceof Error ? error.name : "UnknownError",
1250
- error_message: sanitizeGatewayErrorMessage(error)
1251
- };
1252
- }
1253
- function sanitizeGatewayErrorMessage(error) {
1254
- const rawMessage = error instanceof Error ? error.message : String(error);
1255
- const trimmed = rawMessage.trim();
1256
- if (!trimmed) {
1257
- return "Unknown error";
1258
- }
1259
- const withBearerRedaction = trimmed.replace(/Bearer\s+[^\s,;]+/gi, "Bearer [REDACTED]").replace(/(token|authorization|api[_-]?key)\s*[:=]\s*[^\s,;]+/gi, "$1=[REDACTED]");
1260
- const maxLength = 240;
1261
- if (withBearerRedaction.length <= maxLength) {
1262
- return withBearerRedaction;
1263
- }
1264
- return `${withBearerRedaction.slice(0, maxLength - 3)}...`;
1265
- }
1266
1376
  function safeHeaderEquals(left, right) {
1267
1377
  const leftBuffer = Buffer.from(left);
1268
1378
  const rightBuffer = Buffer.from(right);
@@ -1281,6 +1391,7 @@ var DEFAULT_RESPONSE_TIMEOUT_MS = 12e4;
1281
1391
  var DEFAULT_PEER_PAGE_SIZE = 100;
1282
1392
  var DEFAULT_MAX_PEER_PAGES = 25;
1283
1393
  var A2AGatewayAdapter = class extends SimpleAdapter {
1394
+ provider = PROVIDER;
1284
1395
  bandRest;
1285
1396
  gatewayUrl;
1286
1397
  host;
@@ -1291,6 +1402,7 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
1291
1402
  peerPageSize;
1292
1403
  maxPeerPages;
1293
1404
  serverFactory;
1405
+ logger;
1294
1406
  peersBySlug = /* @__PURE__ */ new Map();
1295
1407
  peersById = /* @__PURE__ */ new Map();
1296
1408
  contextToRoom = /* @__PURE__ */ new Map();
@@ -1312,6 +1424,7 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
1312
1424
  this.peerPageSize = options.peerPageSize ?? DEFAULT_PEER_PAGE_SIZE;
1313
1425
  this.maxPeerPages = options.maxPeerPages ?? DEFAULT_MAX_PEER_PAGES;
1314
1426
  this.serverFactory = options.serverFactory ?? createGatewayServer;
1427
+ this.logger = resolveLogger(options.logger);
1315
1428
  if (this.allowUnauthenticatedLoopback && !isLoopbackHost2(this.host)) {
1316
1429
  throw new ValidationError(
1317
1430
  "A2AGatewayAdapter `allowUnauthenticatedLoopback` may only be enabled for loopback hosts."
@@ -1333,7 +1446,8 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
1333
1446
  onCancel: async (request) => {
1334
1447
  const canonicalPeerId = this.resolveCanonicalPeerId(request);
1335
1448
  this.cancelPendingTask(request.taskId, canonicalPeerId ?? request.peerId);
1336
- }
1449
+ },
1450
+ logger: this.logger
1337
1451
  });
1338
1452
  await this.server.start();
1339
1453
  }
@@ -1415,12 +1529,15 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
1415
1529
  async *handleGatewayRequest(request) {
1416
1530
  const peer = this.resolveGatewayPeer(request);
1417
1531
  if (!peer) {
1532
+ const text = `Peer not found: ${request.peerId}`;
1533
+ this.logger.warn("a2a_gateway.peer_not_found", { peerId: request.peerId, taskId: request.taskId });
1418
1534
  yield buildStatusEvent({
1419
1535
  taskId: request.taskId,
1420
1536
  contextId: request.contextId,
1421
1537
  state: "failed",
1422
1538
  final: true,
1423
- text: `Peer not found: ${request.peerId}`
1539
+ text,
1540
+ metadata: buildGatewayFailureMetadata(text, "peer_not_found")
1424
1541
  });
1425
1542
  return;
1426
1543
  }
@@ -1471,12 +1588,20 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
1471
1588
  });
1472
1589
  } catch (error) {
1473
1590
  this.removePending(pending);
1591
+ const text = sanitizeGatewayErrorMessage(error);
1592
+ this.logger.warn("a2a_gateway.relay_failed", {
1593
+ roomId,
1594
+ peerId: peer.id,
1595
+ taskId: pending.taskId,
1596
+ error
1597
+ });
1474
1598
  yield buildStatusEvent({
1475
1599
  taskId: pending.taskId,
1476
1600
  contextId: pending.contextId,
1477
1601
  state: "failed",
1478
1602
  final: true,
1479
- text: error instanceof Error ? error.message : String(error)
1603
+ text,
1604
+ metadata: buildGatewayFailureMetadata(error, void 0, text)
1480
1605
  });
1481
1606
  return;
1482
1607
  }
@@ -1484,12 +1609,20 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
1484
1609
  const next = await pending.queue.dequeue(this.responseTimeoutMs);
1485
1610
  if (!next) {
1486
1611
  this.removePending(pending);
1612
+ const text = "Timed out waiting for a Band peer response.";
1613
+ this.logger.warn("a2a_gateway.peer_response_timeout", {
1614
+ roomId,
1615
+ peerId: peer.id,
1616
+ taskId: pending.taskId,
1617
+ responseTimeoutMs: this.responseTimeoutMs
1618
+ });
1487
1619
  yield buildStatusEvent({
1488
1620
  taskId: pending.taskId,
1489
1621
  contextId: pending.contextId,
1490
1622
  state: "failed",
1491
1623
  final: true,
1492
- text: "Timed out waiting for a Band peer response."
1624
+ text,
1625
+ metadata: buildGatewayFailureMetadata(text, FAILURE_CODE_TIMEOUT)
1493
1626
  });
1494
1627
  return;
1495
1628
  }
@@ -1770,17 +1903,22 @@ function toStatusUpdateEvent(message, taskId, contextId) {
1770
1903
  } else {
1771
1904
  state = "working";
1772
1905
  }
1906
+ const failure = sanitizeForwardedFailure(message.metadata?.[FAILURE_METADATA_KEY]);
1773
1907
  return buildStatusEvent({
1774
1908
  taskId,
1775
1909
  contextId,
1776
1910
  state,
1777
1911
  final,
1778
- text: message.content,
1912
+ // A room's own "error" event reaches an external A2A client verbatim
1913
+ // otherwise — sanitize it the same way a gateway-originated failure is,
1914
+ // since neither is guaranteed to be pre-redacted upstream.
1915
+ text: normalizedType === "error" ? sanitizeGatewayErrorMessage(message.content) : message.content,
1779
1916
  metadata: {
1780
1917
  band_message_id: message.id,
1781
1918
  band_message_type: message.messageType,
1782
1919
  band_sender_id: message.senderId,
1783
- band_room_id: message.roomId
1920
+ band_room_id: message.roomId,
1921
+ ...failure !== void 0 ? { [FAILURE_METADATA_KEY]: failure } : {}
1784
1922
  }
1785
1923
  });
1786
1924
  }
@@ -1914,6 +2052,7 @@ var LazyAsyncValue = class {
1914
2052
  var DEFAULT_RESPONSE_TIMEOUT_SECONDS = 120;
1915
2053
  var DEFAULT_MAX_HISTORY_MESSAGES = 100;
1916
2054
  var ParlantAdapter = class extends SimpleAdapter {
2055
+ provider = "parlant";
1917
2056
  environment;
1918
2057
  baseUrl;
1919
2058
  agentId;
@@ -1949,7 +2088,7 @@ var ParlantAdapter = class extends SimpleAdapter {
1949
2088
  this.responseTimeoutSeconds = options.responseTimeoutSeconds ?? DEFAULT_RESPONSE_TIMEOUT_SECONDS;
1950
2089
  this.maxHistoryMessages = options.maxHistoryMessages ?? DEFAULT_MAX_HISTORY_MESSAGES;
1951
2090
  this.clientFactory = options.clientFactory;
1952
- this.logger = options.logger ?? new NoopLogger();
2091
+ this.logger = resolveLogger(options.logger);
1953
2092
  this.clientLoader = new LazyAsyncValue({
1954
2093
  load: async () => this.createClient(),
1955
2094
  onRejected: (error) => {
@@ -2006,40 +2145,27 @@ var ParlantAdapter = class extends SimpleAdapter {
2006
2145
  createdEvent.offset
2007
2146
  );
2008
2147
  if (!reply) {
2009
- await tools.sendEvent(
2010
- "Parlant did not return a response before timeout.",
2011
- "error",
2012
- {
2013
- parlant_session_id: sessionId,
2014
- parlant_timeout_seconds: this.responseTimeoutSeconds
2015
- }
2148
+ return reportTurnFailure(
2149
+ tools,
2150
+ agentFailure(this.provider, "Parlant did not return a response before timeout.", FAILURE_CODE_TIMEOUT),
2151
+ this.logger,
2152
+ { roomId: context.roomId, agentId: this.agentId }
2016
2153
  );
2017
- return;
2018
2154
  }
2019
- await tools.sendMessage(reply, [{ id: message.senderId }]);
2155
+ await deliverReply(tools, reply, [{ id: message.senderId }]);
2020
2156
  } catch (error) {
2021
- const errorMessage = asErrorMessage(error);
2157
+ rethrowIfRecoverableTurnFailure(error);
2022
2158
  this.logger.error("Parlant adapter request failed", {
2023
2159
  roomId: context.roomId,
2024
2160
  agentId: this.agentId,
2025
2161
  error
2026
2162
  });
2027
- try {
2028
- await tools.sendEvent(
2029
- `Parlant adapter error: ${errorMessage}`,
2030
- "error",
2031
- {
2032
- parlant_error: errorMessage
2033
- }
2034
- );
2035
- } catch (eventError) {
2036
- this.logger.warn("Parlant adapter failed to emit error event", {
2037
- roomId: context.roomId,
2038
- agentId: this.agentId,
2039
- error: eventError
2040
- });
2041
- }
2042
- throw error instanceof Error ? error : new Error(errorMessage);
2163
+ await reportTurnFailure(
2164
+ tools,
2165
+ agentFailure(this.provider, asErrorMessage(error)),
2166
+ this.logger,
2167
+ { roomId: context.roomId, agentId: this.agentId }
2168
+ );
2043
2169
  }
2044
2170
  }
2045
2171
  async onCleanup(roomId) {
@@ -2151,8 +2277,9 @@ var ParlantAdapter = class extends SimpleAdapter {
2151
2277
  if (history.length === 0) {
2152
2278
  return;
2153
2279
  }
2154
- const completeHistory = selectCompleteExchanges(history).slice(
2155
- -this.maxHistoryMessages
2280
+ const completeHistory = selectCompleteExchanges(
2281
+ history,
2282
+ this.maxHistoryMessages
2156
2283
  );
2157
2284
  let failedEvents = 0;
2158
2285
  for (const item of completeHistory) {
@@ -2283,26 +2410,6 @@ var ParlantAdapter = class extends SimpleAdapter {
2283
2410
  return factory();
2284
2411
  }
2285
2412
  };
2286
- function selectCompleteExchanges(history) {
2287
- const complete = [];
2288
- let index = 0;
2289
- while (index < history.length) {
2290
- const current = history[index];
2291
- if (current.role === "user" && current.content) {
2292
- const next = history[index + 1];
2293
- if (next && next.role === "assistant" && next.content) {
2294
- complete.push(current);
2295
- complete.push(next);
2296
- index += 2;
2297
- continue;
2298
- }
2299
- index += 1;
2300
- continue;
2301
- }
2302
- index += 1;
2303
- }
2304
- return complete;
2305
- }
2306
2413
  function buildUserMessage(input) {
2307
2414
  const updates = [];
2308
2415
  if (input.participantsMessage) {
@@ -2480,8 +2587,67 @@ function buildClaudeSdkText(raw, agentName) {
2480
2587
  return lines.join("\n");
2481
2588
  }
2482
2589
 
2590
+ // src/converters/acp-client.ts
2591
+ var ACPClientHistoryConverter = class {
2592
+ convert(raw) {
2593
+ const roomToSession = {};
2594
+ for (const entry of raw) {
2595
+ if (entry.message_type !== "task") {
2596
+ continue;
2597
+ }
2598
+ const metadataRaw = entry.metadata;
2599
+ if (!metadataRaw || typeof metadataRaw !== "object" || Array.isArray(metadataRaw)) {
2600
+ continue;
2601
+ }
2602
+ const metadata = metadataRaw;
2603
+ const sessionId = metadata.acp_client_session_id;
2604
+ const roomId = metadata.acp_client_room_id;
2605
+ if (typeof sessionId === "string" && typeof roomId === "string" && sessionId && roomId && roomId === entry.room_id) {
2606
+ roomToSession[roomId] = sessionId;
2607
+ }
2608
+ }
2609
+ return { roomToSession };
2610
+ }
2611
+ };
2612
+
2613
+ // src/converters/acp-server.ts
2614
+ var ACPServerHistoryConverter = class {
2615
+ convert(raw) {
2616
+ const sessionToRoom = {};
2617
+ const sessionCwd = {};
2618
+ const sessionMcpServers = {};
2619
+ for (const entry of raw) {
2620
+ const metadataRaw = entry.metadata;
2621
+ if (!metadataRaw || typeof metadataRaw !== "object" || Array.isArray(metadataRaw)) {
2622
+ continue;
2623
+ }
2624
+ const metadata = metadataRaw;
2625
+ const sessionId = metadata.acp_session_id;
2626
+ if (typeof sessionId !== "string" || sessionId.length === 0) {
2627
+ continue;
2628
+ }
2629
+ const roomId = typeof metadata.acp_room_id === "string" ? metadata.acp_room_id : typeof entry.room_id === "string" ? entry.room_id : null;
2630
+ if (roomId) {
2631
+ sessionToRoom[sessionId] = roomId;
2632
+ }
2633
+ if (typeof metadata.acp_cwd === "string" && metadata.acp_cwd.length > 0) {
2634
+ sessionCwd[sessionId] = metadata.acp_cwd;
2635
+ }
2636
+ if (Array.isArray(metadata.acp_mcp_servers)) {
2637
+ sessionMcpServers[sessionId] = metadata.acp_mcp_servers.filter((server) => !!server && typeof server === "object" && !Array.isArray(server));
2638
+ }
2639
+ }
2640
+ return {
2641
+ sessionToRoom,
2642
+ sessionCwd,
2643
+ sessionMcpServers
2644
+ };
2645
+ }
2646
+ };
2647
+
2483
2648
  export {
2484
2649
  findLatestTaskMetadata,
2650
+ selectCompleteExchanges,
2485
2651
  LazyAsyncValue,
2486
2652
  asOptionalString,
2487
2653
  parseDate,
@@ -2501,5 +2667,7 @@ export {
2501
2667
  OpencodeHistoryConverter,
2502
2668
  extractOpencodeSessionId,
2503
2669
  ClaudeSDKHistoryConverter,
2504
- extractClaudeSessionId
2670
+ extractClaudeSessionId,
2671
+ ACPClientHistoryConverter,
2672
+ ACPServerHistoryConverter
2505
2673
  };
@@ -0,0 +1,6 @@
1
+ // src/core/index.ts
2
+ import { AgentFailure } from "@band-ai/band-sdk-core";
3
+
4
+ export {
5
+ AgentFailure
6
+ };