codex-to-im 1.0.61 → 1.0.62

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 (2) hide show
  1. package/dist/daemon.mjs +66 -2
  2. package/package.json +1 -1
package/dist/daemon.mjs CHANGED
@@ -16689,7 +16689,9 @@ async function runMirrorReconcileBatch(deps) {
16689
16689
  }
16690
16690
 
16691
16691
  // src/lib/bridge/mirror-runtime.ts
16692
+ var MIRROR_ORPHAN_PROCESS_PROBE_INTERVAL_MS = 6e4;
16692
16693
  function createMirrorRuntime(getState2, options, deps) {
16694
+ const orphanProbeAt = /* @__PURE__ */ new Map();
16693
16695
  function isAtOrBefore(timestamp, boundary) {
16694
16696
  if (!timestamp) return true;
16695
16697
  const timestampMs = Date.parse(timestamp);
@@ -16776,8 +16778,35 @@ function createMirrorRuntime(getState2, options, deps) {
16776
16778
  deps.stopMirrorStreaming(existing);
16777
16779
  closeMirrorWatcher(existing);
16778
16780
  state.mirrorSubscriptions.delete(bindingId);
16781
+ orphanProbeAt.delete(bindingId);
16779
16782
  deps.syncMirrorSessionStateSafe(existing.sessionId, "mirror subscription removal");
16780
16783
  }
16784
+ async function finalizeOrphanedMirrorStream(subscription, snapshot, blocked, runtimeBusy, nowMs) {
16785
+ const pendingTurn = subscription.pendingTurn;
16786
+ if (!pendingTurn?.streamStarted || blocked || runtimeBusy) return null;
16787
+ const timeoutMs = options.streamOrphanTimeoutMs;
16788
+ const lastActivityMs = Date.parse(pendingTurn.lastActivityAt);
16789
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || !Number.isFinite(lastActivityMs)) {
16790
+ return null;
16791
+ }
16792
+ const sourceActivityMs = Math.max(lastActivityMs, snapshot.mtimeMs);
16793
+ if (nowMs - sourceActivityMs < timeoutMs) return null;
16794
+ const lastProbeAt = orphanProbeAt.get(subscription.bindingId);
16795
+ if (typeof lastProbeAt === "number" && nowMs - lastProbeAt < MIRROR_ORPHAN_PROCESS_PROBE_INTERVAL_MS) {
16796
+ return null;
16797
+ }
16798
+ orphanProbeAt.set(subscription.bindingId, nowMs);
16799
+ if (!await deps.isThreadProcessDefinitelyGone(subscription.threadId)) return null;
16800
+ const finalized = flushTimedOutMirrorTurn(subscription, timeoutMs, nowMs);
16801
+ if (!finalized) return null;
16802
+ orphanProbeAt.delete(subscription.bindingId);
16803
+ const detail = "\u684C\u9762\u7EBF\u7A0B\u957F\u65F6\u95F4\u6CA1\u6709\u65B0\u8BB0\u5F55\uFF0C\u4E14\u672C\u673A\u672A\u627E\u5230\u5BF9\u5E94\u8FDB\u7A0B\uFF1B\u5DF2\u7ED3\u675F\u5B64\u7ACB\u7684\u6D41\u5F0F\u540C\u6B65\u3002";
16804
+ deps.recordOrphanedMirrorTurn(subscription.sessionId, detail);
16805
+ console.warn(
16806
+ `[bridge-manager] Finalized orphaned mirror turn ${pendingTurn.turnId || pendingTurn.streamKey} for thread ${subscription.threadId}`
16807
+ );
16808
+ return finalized;
16809
+ }
16781
16810
  function clearDanglingMirrorThread(subscription, reason) {
16782
16811
  const { store } = getBridgeContext();
16783
16812
  const session = store.getSession(subscription.sessionId);
@@ -16835,6 +16864,7 @@ function createMirrorRuntime(getState2, options, deps) {
16835
16864
  });
16836
16865
  if (threadChanged || filePathChanged) {
16837
16866
  deps.stopMirrorStreaming(existing);
16867
+ orphanProbeAt.delete(existing.bindingId);
16838
16868
  }
16839
16869
  watchMirrorFile(existing, filePath);
16840
16870
  if (previousSessionId !== binding.codepilotSessionId) {
@@ -16934,6 +16964,20 @@ function createMirrorRuntime(getState2, options, deps) {
16934
16964
  flushTimedOutTurn: (currentSubscription) => deps.flushTimedOutMirrorTurn(currentSubscription),
16935
16965
  consumeBufferedTurns: (currentSubscription) => deps.consumeBufferedMirrorTurns(currentSubscription)
16936
16966
  });
16967
+ const runtimeBusy = session.runtime_status === "running" || session.runtime_status === "queued";
16968
+ const orphanedTurn = await finalizeOrphanedMirrorStream(
16969
+ subscription,
16970
+ snapshot,
16971
+ blocked,
16972
+ runtimeBusy,
16973
+ Date.now()
16974
+ );
16975
+ if (orphanedTurn) {
16976
+ deliveryPlan.finalizedTurns.push(orphanedTurn);
16977
+ deliveryPlan.syncReason = "mirror reconcile delivered turns";
16978
+ } else if (!subscription.pendingTurn?.streamStarted) {
16979
+ orphanProbeAt.delete(subscription.bindingId);
16980
+ }
16937
16981
  if (deliveryPlan.finalizedTurns.length > 0) {
16938
16982
  enqueuePendingMirrorDeliveries(subscription, deliveryPlan.finalizedTurns);
16939
16983
  }
@@ -17356,6 +17400,12 @@ async function probeCodexThreadProcess(threadId) {
17356
17400
  };
17357
17401
  }
17358
17402
  }
17403
+ async function isCodexThreadProcessDefinitelyGone(threadId, probe = probeCodexThreadProcess) {
17404
+ const threadProbe = await probe(threadId);
17405
+ if (threadProbe.status !== "not_found") return false;
17406
+ const hostProbe = await probe("app-server");
17407
+ return hostProbe.status === "not_found";
17408
+ }
17359
17409
 
17360
17410
  // src/lib/bridge/session-health-reducer.ts
17361
17411
  var HEALTH_RECENT_PROGRESS_MS = 10 * 60 * 1e3;
@@ -17495,7 +17545,9 @@ function computeBaseDiagnosis(session, nowMs) {
17495
17545
  const sdkSessionId = trimOrNull(session.sdk_session_id);
17496
17546
  const lastProgressMs = parseIsoMs(lastProgressAt || void 0);
17497
17547
  const previousStatus = session.health_status || "idle";
17498
- if (!isRunningRuntimeStatus(runtimeStatus) && isRunningHealthStatus(previousStatus)) {
17548
+ const terminalHealth = previousStatus === "completed" || previousStatus === "failed" || previousStatus === "aborted";
17549
+ const activeDesktopMirror = session.thread_origin === "desktop" && session.mirror_status === "watching" && isRunningHealthStatus(previousStatus);
17550
+ if (!isRunningRuntimeStatus(runtimeStatus) && !terminalHealth && !activeDesktopMirror) {
17499
17551
  return {
17500
17552
  sessionId: session.id,
17501
17553
  checkedAt: null,
@@ -17583,6 +17635,12 @@ function applyProcessProbeDiagnosis(diagnosis, processProbe) {
17583
17635
  processProbe: null
17584
17636
  };
17585
17637
  }
17638
+ if (!isRunningHealthStatus(diagnosis.healthStatus)) {
17639
+ return {
17640
+ ...diagnosis,
17641
+ processProbe
17642
+ };
17643
+ }
17586
17644
  if (processProbe.status === "alive") {
17587
17645
  const healthStatus = diagnosis.activeToolName ? "waiting_tool" : "slow_observed";
17588
17646
  const healthReason = diagnosis.activeToolName ? "\u68C0\u6D4B\u5230\u672C\u673A\u7EBF\u7A0B\u8FDB\u7A0B\u4ECD\u5728\u8FD0\u884C\uFF0C\u5F53\u524D\u66F4\u50CF\u662F\u957F\u65F6\u5DE5\u5177\u6267\u884C\u3002" : "\u68C0\u6D4B\u5230\u672C\u673A\u7EBF\u7A0B\u8FDB\u7A0B\u4ECD\u5728\u8FD0\u884C\uFF0C\u5F53\u524D\u66F4\u50CF\u662F\u957F\u65F6\u4EFB\u52A1\u3002";
@@ -18076,6 +18134,7 @@ var MIRROR_PROMPT_MATCH_GRACE_MS = 12e4;
18076
18134
  var DESKTOP_TERMINAL_FINALIZATION_TIMEOUT_MS = 3e4;
18077
18135
  var MIRROR_STREAM_STATUS_IDLE_START_MS = 18e4;
18078
18136
  var MIRROR_STREAM_STATUS_HEARTBEAT_MS = 1e4;
18137
+ var MIRROR_STREAM_ORPHAN_TIMEOUT_MS = 30 * 6e4;
18079
18138
  var MIRROR_TURN_BUFFER_TIMEOUT_MS = 10 * 6e4;
18080
18139
  function describeUnknownError(error) {
18081
18140
  if (error instanceof Error) {
@@ -18305,7 +18364,8 @@ var MIRROR_RUNTIME = createMirrorRuntime(getState, {
18305
18364
  watchDebounceMs: MIRROR_WATCH_DEBOUNCE_MS,
18306
18365
  danglingThreadRetryLimit: DANGLING_MIRROR_THREAD_RETRY_LIMIT,
18307
18366
  failureSuspendThreshold: MIRROR_FAILURE_SUSPEND_THRESHOLD,
18308
- failureSuspendMs: MIRROR_FAILURE_SUSPEND_MS
18367
+ failureSuspendMs: MIRROR_FAILURE_SUSPEND_MS,
18368
+ streamOrphanTimeoutMs: MIRROR_STREAM_ORPHAN_TIMEOUT_MS
18309
18369
  }, {
18310
18370
  nowIso: nowIso3,
18311
18371
  describeUnknownError,
@@ -18315,6 +18375,10 @@ var MIRROR_RUNTIME = createMirrorRuntime(getState, {
18315
18375
  observeSessionHealthRecords: (sessionId, threadId, records) => {
18316
18376
  SESSION_HEALTH_RUNTIME.observeDesktopMirrorRecords(sessionId, threadId, records);
18317
18377
  },
18378
+ recordOrphanedMirrorTurn: (sessionId, detail) => {
18379
+ SESSION_HEALTH_RUNTIME.recordInteractiveEnd(sessionId, "aborted", detail);
18380
+ },
18381
+ isThreadProcessDefinitelyGone: isCodexThreadProcessDefinitelyGone,
18318
18382
  routeDesktopRecords: (sessionId, threadId, records) => routeDesktopRecords(
18319
18383
  sessionId,
18320
18384
  threadId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-to-im",
3
- "version": "1.0.61",
3
+ "version": "1.0.62",
4
4
  "description": "Installable Codex-to-IM bridge with local setup UI and background service",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/zhangle1987/codex-to-im#readme",