@junctionpanel/server 0.1.96 → 0.1.98

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.
@@ -960,6 +960,7 @@ export class AgentManager {
960
960
  mutableAgent.pendingRun = null;
961
961
  mutableAgent.lifecycle = error ? "error" : "idle";
962
962
  mutableAgent.lastError = error;
963
+ self.touchUpdatedAt(mutableAgent);
963
964
  const persistenceHandle = mutableAgent.session.describePersistence() ??
964
965
  (mutableAgent.runtimeInfo?.sessionId
965
966
  ? { provider: mutableAgent.provider, sessionId: mutableAgent.runtimeInfo.sessionId }
@@ -1407,12 +1408,13 @@ export class AgentManager {
1407
1408
  unsubscribe = this.subscribe((event) => {
1408
1409
  if (event.type === "agent_state") {
1409
1410
  currentStatus = event.agent.lifecycle;
1411
+ const hasPendingRunForEvent = "pendingRun" in event.agent && Boolean(event.agent.pendingRun);
1410
1412
  const pending = this.peekPendingPermission(event.agent);
1411
1413
  if (pending) {
1412
1414
  finish(pending);
1413
1415
  return;
1414
1416
  }
1415
- if (isAgentBusy(event.agent.lifecycle)) {
1417
+ if (isAgentBusy(event.agent.lifecycle) || hasPendingRunForEvent) {
1416
1418
  hasStarted = true;
1417
1419
  return;
1418
1420
  }
@@ -1665,6 +1667,52 @@ export class AgentManager {
1665
1667
  await this.persistSnapshot(agent);
1666
1668
  }
1667
1669
  }
1670
+ shouldNormalizeInterruptedProviderFailure(agent, event) {
1671
+ const hadExplicitUserInterrupt = this.pendingUserInterruptAgents.has(agent.id) ||
1672
+ this.userInterruptedAgents.has(agent.id);
1673
+ return (hadExplicitUserInterrupt &&
1674
+ event.provider === "claude" &&
1675
+ event.error === "Claude stream ended before terminal result");
1676
+ }
1677
+ applyCanceledTurnEvent(agent, event, options) {
1678
+ if (this.pendingUserInterruptAgents.delete(agent.id)) {
1679
+ this.userInterruptedAgents.add(agent.id);
1680
+ }
1681
+ const pendingRequestIds = Array.from(agent.pendingPermissions.keys());
1682
+ const pendingRefresh = this.refreshPendingPermissionsFromSession(agent);
1683
+ if (agent.lifecycle !== "idle") {
1684
+ agent.lifecycle = "idle";
1685
+ }
1686
+ agent.activeTurnStartedAt = null;
1687
+ agent.lastError = undefined;
1688
+ if (!options?.fromHistory) {
1689
+ this.finalizeHarnessTurn(agent, "canceled");
1690
+ const timestamp = new Date().toISOString();
1691
+ const harnessExtra = getHarnessSessionExtra(agent.config);
1692
+ setHarnessSessionExtra(agent.config, {
1693
+ resumeState: touchHarnessResumeState(harnessExtra?.resumeState, {
1694
+ status: "live",
1695
+ source: harnessExtra?.resumeState?.source ?? "snapshot",
1696
+ timestamp,
1697
+ recoveryHint: "The run was interrupted before completion.",
1698
+ }),
1699
+ });
1700
+ }
1701
+ if (!pendingRefresh.hasPendingPermissions) {
1702
+ for (const requestId of pendingRequestIds) {
1703
+ this.resolvePendingPermission(agent, requestId, { behavior: "deny", message: "Interrupted" }, {
1704
+ provider: event.provider,
1705
+ dispatchEvent: !options?.fromHistory,
1706
+ dispatchTimeline: !options?.fromHistory,
1707
+ syncHarness: !options?.fromHistory,
1708
+ dispatchHarnessEvent: !options?.fromHistory,
1709
+ transitionRunToStreaming: false,
1710
+ });
1711
+ }
1712
+ }
1713
+ this.touchUpdatedAt(agent);
1714
+ this.emitState(agent);
1715
+ }
1668
1716
  handleStreamEvent(agent, event, options) {
1669
1717
  // Only update timestamp for live events, not history replay
1670
1718
  if (!options?.fromHistory) {
@@ -1763,11 +1811,12 @@ export class AgentManager {
1763
1811
  }
1764
1812
  }
1765
1813
  agent.activeTurnStartedAt = null;
1766
- if (!agent.pendingRun && agent.lifecycle !== "idle") {
1814
+ if (agent.lifecycle !== "idle") {
1767
1815
  agent.lifecycle = "idle";
1768
1816
  shouldEmitState = true;
1769
1817
  }
1770
1818
  if (shouldEmitState) {
1819
+ this.touchUpdatedAt(agent);
1771
1820
  this.emitState(agent);
1772
1821
  }
1773
1822
  void this.refreshRuntimeInfo(agent);
@@ -1775,9 +1824,18 @@ export class AgentManager {
1775
1824
  }
1776
1825
  break;
1777
1826
  case "turn_failed":
1827
+ if (this.shouldNormalizeInterruptedProviderFailure(agent, event)) {
1828
+ this.applyCanceledTurnEvent(agent, {
1829
+ type: "turn_canceled",
1830
+ provider: event.provider,
1831
+ reason: "Interrupted",
1832
+ }, options);
1833
+ shouldPersistSnapshot = true;
1834
+ break;
1835
+ }
1778
1836
  this.pendingUserInterruptAgents.delete(agent.id);
1779
1837
  this.userInterruptedAgents.delete(agent.id);
1780
- if (!agent.pendingRun) {
1838
+ if (agent.lifecycle !== "error") {
1781
1839
  agent.lifecycle = "error";
1782
1840
  }
1783
1841
  agent.activeTurnStartedAt = null;
@@ -1802,49 +1860,13 @@ export class AgentManager {
1802
1860
  transitionRunToStreaming: false,
1803
1861
  });
1804
1862
  }
1863
+ this.touchUpdatedAt(agent);
1805
1864
  this.emitState(agent);
1806
1865
  shouldPersistSnapshot = true;
1807
1866
  break;
1808
1867
  case "turn_canceled":
1809
- {
1810
- if (this.pendingUserInterruptAgents.delete(agent.id)) {
1811
- this.userInterruptedAgents.add(agent.id);
1812
- }
1813
- const pendingRequestIds = Array.from(agent.pendingPermissions.keys());
1814
- const pendingRefresh = this.refreshPendingPermissionsFromSession(agent);
1815
- if (!agent.pendingRun) {
1816
- agent.lifecycle = "idle";
1817
- }
1818
- agent.activeTurnStartedAt = null;
1819
- agent.lastError = undefined;
1820
- if (!options?.fromHistory) {
1821
- this.finalizeHarnessTurn(agent, "canceled");
1822
- const timestamp = new Date().toISOString();
1823
- const harnessExtra = getHarnessSessionExtra(agent.config);
1824
- setHarnessSessionExtra(agent.config, {
1825
- resumeState: touchHarnessResumeState(harnessExtra?.resumeState, {
1826
- status: "live",
1827
- source: harnessExtra?.resumeState?.source ?? "snapshot",
1828
- timestamp,
1829
- recoveryHint: "The run was interrupted before completion.",
1830
- }),
1831
- });
1832
- }
1833
- if (!pendingRefresh.hasPendingPermissions) {
1834
- for (const requestId of pendingRequestIds) {
1835
- this.resolvePendingPermission(agent, requestId, { behavior: "deny", message: "Interrupted" }, {
1836
- provider: event.provider,
1837
- dispatchEvent: !options?.fromHistory,
1838
- dispatchTimeline: !options?.fromHistory,
1839
- syncHarness: !options?.fromHistory,
1840
- dispatchHarnessEvent: !options?.fromHistory,
1841
- transitionRunToStreaming: false,
1842
- });
1843
- }
1844
- }
1845
- this.emitState(agent);
1846
- shouldPersistSnapshot = true;
1847
- }
1868
+ this.applyCanceledTurnEvent(agent, event, options);
1869
+ shouldPersistSnapshot = true;
1848
1870
  break;
1849
1871
  case "turn_started":
1850
1872
  this.pendingUserInterruptAgents.delete(agent.id);