@ai-matrx/agents 0.23.0 → 0.24.1

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.cjs CHANGED
@@ -56,6 +56,7 @@ __export(index_exports, {
56
56
  fetchWithMatrxProtocolFallback: () => fetchWithMatrxProtocolFallback,
57
57
  followRuntimeOperationEvents: () => followRuntimeOperationEvents,
58
58
  followRuntimeOperationToEnd: () => followRuntimeOperationToEnd,
59
+ followWorkflowRunEvents: () => followWorkflowRunEvents,
59
60
  getRuntimeOperationStatus: () => getRuntimeOperationStatus,
60
61
  getRuntimeOperationsByLink: () => getRuntimeOperationsByLink,
61
62
  imageRoleVerdict: () => imageRoleVerdict,
@@ -86,6 +87,7 @@ __export(index_exports, {
86
87
  projectWorkflowNodeEvent: () => projectWorkflowNodeEvent,
87
88
  providerSessionFailureBody: () => providerSessionFailureBody,
88
89
  readImageRoleLimits: () => readImageRoleLimits,
90
+ readLiveRunRejoin: () => readLiveRunRejoin,
89
91
  readLiveRunRequestId: () => readLiveRunRequestId,
90
92
  readMatrxErrorCode: () => readMatrxErrorCode,
91
93
  readMatrxNdjsonStream: () => readMatrxNdjsonStream,
@@ -1585,17 +1587,37 @@ function readMatrxErrorCode(error) {
1585
1587
  }
1586
1588
  return null;
1587
1589
  }
1588
- function readLiveRunRequestId(error) {
1589
- if (statusOf(error) !== 409) return null;
1590
- for (const candidate of detailCandidates(bodyOf(error))) {
1591
- if (candidate.code !== MATRX_RUN_IN_PROGRESS) continue;
1592
- const live = nonBlank(candidate.live_request_id);
1593
- if (live) return live.trim();
1590
+ function firstString(candidates, key) {
1591
+ for (const candidate of candidates) {
1592
+ const value = nonBlank(candidate[key]);
1593
+ if (value) return value.trim();
1594
1594
  }
1595
1595
  return null;
1596
1596
  }
1597
+ function readLiveRunRejoin(error) {
1598
+ if (statusOf(error) !== 409) return null;
1599
+ const candidates = detailCandidates(bodyOf(error));
1600
+ if (candidates.length === 0) return null;
1601
+ const code = readMatrxErrorCode(error);
1602
+ if (code !== MATRX_RUN_IN_PROGRESS && code !== MATRX_RESUME_CONFLICT) return null;
1603
+ const liveRequestId = firstString(candidates, "live_request_id");
1604
+ const pathFromBody = firstString(candidates, "rejoin_path");
1605
+ const rejoinPath = pathFromBody && pathFromBody.startsWith("/") ? pathFromBody : liveRequestId ? runtimeOperationRejoinPath(liveRequestId) : null;
1606
+ if (!rejoinPath) return null;
1607
+ const root = bodyOf(error);
1608
+ return {
1609
+ liveRequestId,
1610
+ rejoinPath,
1611
+ runId: firstString(candidates, "run_id"),
1612
+ code,
1613
+ body: isRecord4(root) ? root : {}
1614
+ };
1615
+ }
1616
+ function readLiveRunRequestId(error) {
1617
+ return readLiveRunRejoin(error)?.liveRequestId ?? null;
1618
+ }
1597
1619
  function isResumeConflict(error) {
1598
- return statusOf(error) === 409 && readMatrxErrorCode(error) === MATRX_RESUME_CONFLICT;
1620
+ return statusOf(error) === 409 && readMatrxErrorCode(error) === MATRX_RESUME_CONFLICT && readLiveRunRejoin(error) === null;
1599
1621
  }
1600
1622
  function isLiveStreamUnavailable(error) {
1601
1623
  return statusOf(error) === 409 && readMatrxErrorCode(error) === MATRX_LIVE_STREAM_UNAVAILABLE;
@@ -1625,54 +1647,110 @@ function withRunOrganization(transport, organizationId) {
1625
1647
  })
1626
1648
  };
1627
1649
  }
1650
+ async function followWorkflowRunEvents(transport, runId, options) {
1651
+ const headers = { Accept: "text/event-stream" };
1652
+ if (options.lastEventId) headers["Last-Event-ID"] = String(options.lastEventId);
1653
+ let response;
1654
+ try {
1655
+ response = await requestStream(
1656
+ transport,
1657
+ `/runs/${encodePathSegment(runId)}/events/stream`,
1658
+ {
1659
+ method: "GET",
1660
+ headers,
1661
+ ...options.signal ? { signal: options.signal } : {}
1662
+ }
1663
+ );
1664
+ } catch (error) {
1665
+ if (options.signal?.aborted) return { ended: false };
1666
+ throw error;
1667
+ }
1668
+ try {
1669
+ for await (const frame of readMatrxSseStream(
1670
+ response.body
1671
+ )) {
1672
+ if (frame.event === "end") return { ended: true };
1673
+ if (frame.data === null) continue;
1674
+ let parsed;
1675
+ try {
1676
+ parsed = JSON.parse(frame.data);
1677
+ } catch {
1678
+ continue;
1679
+ }
1680
+ if (!isRecord4(parsed)) continue;
1681
+ options.onEvent(parsed, parsed.event === "node_stream" ? null : frame.seq);
1682
+ }
1683
+ } catch (error) {
1684
+ if (!options.signal?.aborted) throw error;
1685
+ }
1686
+ return { ended: false };
1687
+ }
1628
1688
  async function drain(handle, onEnvelope) {
1629
1689
  for await (const envelope of handle.events) onEnvelope(envelope);
1630
1690
  }
1631
1691
  async function resumeOrRejoin(transport, resumeCall, options) {
1632
- const { onEnvelope, organizationId, onRejoin, onOperationEvent, follow, ...rest } = options;
1692
+ const {
1693
+ onEnvelope,
1694
+ organizationId,
1695
+ onRejoin,
1696
+ onOperationEvent,
1697
+ onWorkflowEvent,
1698
+ follow,
1699
+ ...rest
1700
+ } = options;
1633
1701
  const bound = organizationId ? withRunOrganization(transport, organizationId) : transport;
1634
1702
  const streamOptions = {
1635
1703
  ...rest,
1636
1704
  ...options.signal ? { signal: options.signal } : {}
1637
1705
  };
1638
- let liveRequestId;
1706
+ const signalOnly = options.signal ? { signal: options.signal } : {};
1707
+ let rejoin;
1639
1708
  try {
1640
1709
  const handle = await resumeCall(bound, streamOptions);
1641
1710
  await drain(handle, onEnvelope);
1642
1711
  return { kind: "resumed", requestId: handle.requestId };
1643
1712
  } catch (error) {
1644
- if (error instanceof MatrxApiError && isResumeConflict(error)) {
1645
- return { kind: "resume_conflict", error };
1713
+ const target = readLiveRunRejoin(error);
1714
+ if (!target) {
1715
+ if (error instanceof MatrxApiError && isResumeConflict(error)) {
1716
+ return { kind: "resume_conflict", error };
1717
+ }
1718
+ throw error;
1646
1719
  }
1647
- const live = readLiveRunRequestId(error);
1648
- if (!live) throw error;
1649
- liveRequestId = live;
1720
+ rejoin = target;
1650
1721
  }
1651
- onRejoin?.(liveRequestId);
1722
+ onRejoin?.(rejoin);
1723
+ let unavailableBody;
1652
1724
  try {
1653
- const handle = await rejoinRuntimeOperation(bound, liveRequestId, streamOptions);
1725
+ const handle = await openMatrxStream(bound, rejoin.rejoinPath, {
1726
+ ...streamOptions,
1727
+ // The rejoin routes take no body model; the reference client posts {}.
1728
+ body: {}
1729
+ });
1654
1730
  await drain(handle, onEnvelope);
1655
- return { kind: "rejoined", liveRequestId };
1731
+ return { kind: "rejoined", rejoin };
1656
1732
  } catch (error) {
1657
1733
  if (!isLiveStreamUnavailable(error)) throw error;
1734
+ unavailableBody = bodyOf(error);
1658
1735
  }
1659
- const statusView = await getRuntimeOperationStatus(bound, liveRequestId, {
1660
- ...options.signal ? { signal: options.signal } : {}
1661
- });
1736
+ const runId = firstString(detailCandidates(unavailableBody), "run_id") ?? rejoin.runId;
1737
+ if (runId && rejoin.rejoinPath.startsWith("/runtime/operations/")) {
1738
+ const result2 = await followWorkflowRunEvents(bound, runId, {
1739
+ ...signalOnly,
1740
+ onEvent: (event, seq) => onWorkflowEvent?.(event, seq)
1741
+ });
1742
+ return { kind: "followed_workflow", rejoin, runId, ended: result2.ended };
1743
+ }
1744
+ const liveRequestId = rejoin.liveRequestId;
1745
+ const statusView = liveRequestId ? await getRuntimeOperationStatus(bound, liveRequestId, signalOnly) : null;
1662
1746
  const operation = statusView?.operations.find((op) => !op.is_terminal) ?? statusView?.operations[0] ?? null;
1663
1747
  if (!operation) {
1664
- return {
1665
- kind: "followed",
1666
- liveRequestId,
1667
- executionId: null,
1668
- ended: false,
1669
- status: null
1670
- };
1748
+ return { kind: "followed", rejoin, executionId: null, ended: false, status: null };
1671
1749
  }
1672
1750
  if (operation.is_terminal) {
1673
1751
  return {
1674
1752
  kind: "followed",
1675
- liveRequestId,
1753
+ rejoin,
1676
1754
  executionId: operation.execution_id,
1677
1755
  ended: true,
1678
1756
  status: operation.status
@@ -1681,12 +1759,12 @@ async function resumeOrRejoin(transport, resumeCall, options) {
1681
1759
  const result = await followRuntimeOperationToEnd(bound, operation.execution_id, {
1682
1760
  ...follow,
1683
1761
  lastEventSeq: operation.last_event_seq,
1684
- ...options.signal ? { signal: options.signal } : {},
1762
+ ...signalOnly,
1685
1763
  onEvent: (event) => onOperationEvent?.(event)
1686
1764
  });
1687
1765
  return {
1688
1766
  kind: "followed",
1689
- liveRequestId,
1767
+ rejoin,
1690
1768
  executionId: operation.execution_id,
1691
1769
  ended: result.ended,
1692
1770
  status: result.status