@agents24/chat-react 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export * from "./controller";
2
2
  export * from "./context-window";
3
3
  export * from "./latest-thread-scroller";
4
4
  export * from "./message-lifecycle";
5
+ export * from "./mcp-oauth";
5
6
  export * from "./model";
6
7
  export * from "./renderers";
7
8
  export * from "./sse";
package/dist/index.js CHANGED
@@ -152,16 +152,16 @@ var STAGE_PRIORITY = {
152
152
  function numberOrNull(value) {
153
153
  return typeof value === "number" && Number.isFinite(value) ? value : null;
154
154
  }
155
- function stagePriority(window) {
156
- return STAGE_PRIORITY[window?.stage || "sent_prompt"] ?? STAGE_PRIORITY.sent_prompt;
155
+ function stagePriority(window2) {
156
+ return STAGE_PRIORITY[window2?.stage || "sent_prompt"] ?? STAGE_PRIORITY.sent_prompt;
157
157
  }
158
- function hasRenderableContextWindow(window) {
159
- const maxTokens = window?.max_tokens;
158
+ function hasRenderableContextWindow(window2) {
159
+ const maxTokens = window2?.max_tokens;
160
160
  return typeof maxTokens === "number" && Number.isFinite(maxTokens) && maxTokens > 0;
161
161
  }
162
- function windowWeight(window) {
163
- if (!window) return [0, 0];
164
- return [stagePriority(window), SOURCE_PRIORITY[window.source] || 0];
162
+ function windowWeight(window2) {
163
+ if (!window2) return [0, 0];
164
+ return [stagePriority(window2), SOURCE_PRIORITY[window2.source] || 0];
165
165
  }
166
166
  function normalizeContextCompression(value) {
167
167
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
@@ -314,6 +314,22 @@ var assistantTextFromEvents = (events) => {
314
314
  };
315
315
  var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
316
316
  var optionalString = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
317
+ var HITL_ACTIONS_BY_KIND = {
318
+ tool_review: ["approve", "reject"],
319
+ mcp_auth: ["connect", "skip"],
320
+ user_approval: ["approve", "reject"],
321
+ app_data_permission: ["approve", "reject"]
322
+ };
323
+ var HITL_STATUSES = ["pending", "resolved", "expired", "cancelled", "invalidated"];
324
+ function exactHitlActions(kind, value) {
325
+ if (!Array.isArray(value)) throw new Error("Invalid V2 HITL response block.");
326
+ const actions = value.map((item) => String(item));
327
+ const expected = HITL_ACTIONS_BY_KIND[kind];
328
+ if (actions.length !== expected.length || actions.some((action, index) => action !== expected[index])) {
329
+ throw new Error("Invalid V2 HITL response block.");
330
+ }
331
+ return actions;
332
+ }
317
333
  var toolStateFromStatus = (status) => {
318
334
  const normalized = String(status || "").trim().toLowerCase();
319
335
  if (["failed", "error"].includes(normalized)) return "output-error";
@@ -420,17 +436,19 @@ var partsFromResponseBlocks = (blocks, fallbackText) => {
420
436
  const interruptId = optionalString(block.interruptId) || optionalString(hitl.interrupt_id);
421
437
  const hitlKind = optionalString(block.hitlKind) || optionalString(hitl.kind);
422
438
  const status = optionalString(block.status) || optionalString(hitl.status) || "pending";
423
- const allowedActions = Array.isArray(hitl.allowed_actions) ? hitl.allowed_actions.filter((value) => ["approve", "reject", "connect", "skip"].includes(String(value))) : [];
424
- if (!interruptId || allowedActions.length === 0 || !["tool_review", "mcp_auth", "user_approval", "app_data_permission"].includes(String(hitlKind))) {
439
+ if (!interruptId || !["tool_review", "mcp_auth", "user_approval", "app_data_permission"].includes(String(hitlKind)) || !HITL_STATUSES.includes(status)) {
425
440
  throw new Error("Invalid V2 HITL response block.");
426
441
  }
442
+ const kind = hitlKind;
443
+ const allowedActions = exactHitlActions(kind, hitl.allowed_actions);
427
444
  const resolution = asRecord(block.resolution);
445
+ const resolver = asRecord(resolution?.resolver);
428
446
  parts.push({
429
447
  id,
430
448
  type: "hitl",
431
449
  kind: "hitl",
432
450
  interruptId,
433
- hitlKind,
451
+ hitlKind: kind,
434
452
  message: optionalString(hitl.message) || optionalString(block.text) || "Input is required to continue.",
435
453
  allowedActions,
436
454
  status,
@@ -440,7 +458,10 @@ var partsFromResponseBlocks = (blocks, fallbackText) => {
440
458
  outcome: optionalString(resolution.outcome),
441
459
  reason: optionalString(resolution.reason),
442
460
  resolvedAt: optionalString(resolution.resolved_at),
443
- resolver: asRecord(resolution.resolver)
461
+ resolver: resolver ? {
462
+ principalType: optionalString(resolver.principal_type) || optionalString(resolver.principalType) || null,
463
+ principalId: optionalString(resolver.principal_id) || optionalString(resolver.principalId) || null
464
+ } : null
444
465
  } : null,
445
466
  raw: block
446
467
  });
@@ -1546,6 +1567,59 @@ function useAgents24ChatController(options) {
1546
1567
  ]);
1547
1568
  }
1548
1569
 
1570
+ // src/mcp-oauth.ts
1571
+ var CONNECTION_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
1572
+ function normalizedOrigin(value) {
1573
+ try {
1574
+ return new URL(value).origin;
1575
+ } catch {
1576
+ throw new Error("MCP authorization returned an invalid callback origin.");
1577
+ }
1578
+ }
1579
+ function waitForMcpOauthComplete({
1580
+ popup,
1581
+ callbackOrigin,
1582
+ serverId,
1583
+ timeoutMs = 12e4
1584
+ }) {
1585
+ if (!popup) return Promise.reject(new Error("Could not open the MCP authorization popup."));
1586
+ const expectedOrigin = normalizedOrigin(callbackOrigin);
1587
+ const expectedServerId = String(serverId || "").trim();
1588
+ if (!expectedServerId) return Promise.reject(new Error("MCP authorization is missing its server identity."));
1589
+ return new Promise((resolve, reject) => {
1590
+ let settled = false;
1591
+ const finish = (result) => {
1592
+ if (settled) return;
1593
+ settled = true;
1594
+ window.clearTimeout(timeout);
1595
+ window.clearInterval(closeTimer);
1596
+ window.removeEventListener("message", onMessage);
1597
+ if (result instanceof Error) reject(result);
1598
+ else resolve(result);
1599
+ };
1600
+ const onMessage = (event) => {
1601
+ if (event.source !== popup || event.origin !== expectedOrigin) return;
1602
+ const data = event.data;
1603
+ if (data?.type !== "mcp-oauth-complete" || data.server_id !== expectedServerId) return;
1604
+ if (!data.success) {
1605
+ finish(new Error("MCP authorization failed."));
1606
+ return;
1607
+ }
1608
+ const connectionId = String(data.connection_id || "").trim();
1609
+ if (!CONNECTION_ID_PATTERN.test(connectionId)) return;
1610
+ finish({ connectionId });
1611
+ };
1612
+ const timeout = window.setTimeout(
1613
+ () => finish(new Error("MCP authorization timed out.")),
1614
+ Math.max(1, timeoutMs)
1615
+ );
1616
+ const closeTimer = window.setInterval(() => {
1617
+ if (popup.closed) finish(new Error("MCP authorization window was closed before connecting."));
1618
+ }, 500);
1619
+ window.addEventListener("message", onMessage);
1620
+ });
1621
+ }
1622
+
1549
1623
  // src/renderers.tsx
1550
1624
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
1551
1625
  var DefaultToolPart = ({ part }) => {
@@ -1853,6 +1927,7 @@ export {
1853
1927
  useMessageScroller,
1854
1928
  useMessageScrollerScrollable,
1855
1929
  useMessageScrollerVisibility,
1856
- useStreamingText
1930
+ useStreamingText,
1931
+ waitForMcpOauthComplete
1857
1932
  };
1858
1933
  //# sourceMappingURL=index.js.map