chatroom-cli 1.55.5 → 1.55.6

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.js CHANGED
@@ -16656,13 +16656,15 @@ var init_fiberRefs = __esm(() => {
16656
16656
  });
16657
16657
 
16658
16658
  // ../../node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberRefs.js
16659
- var get9, getOrDefault2, joinAs2, setAll2, updateManyAs2, empty20;
16659
+ var delete_2, get9, getOrDefault2, joinAs2, setAll2, updateAs2, updateManyAs2, empty20;
16660
16660
  var init_FiberRefs = __esm(() => {
16661
16661
  init_fiberRefs();
16662
+ delete_2 = delete_;
16662
16663
  get9 = get8;
16663
16664
  getOrDefault2 = getOrDefault;
16664
16665
  joinAs2 = joinAs;
16665
16666
  setAll2 = setAll;
16667
+ updateAs2 = updateAs;
16666
16668
  updateManyAs2 = updateManyAs;
16667
16669
  empty20 = empty19;
16668
16670
  });
@@ -21657,12 +21659,13 @@ var init_ScheduleDecision = __esm(() => {
21657
21659
  });
21658
21660
 
21659
21661
  // ../../node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Scope.js
21660
- var Scope, close, fork2;
21662
+ var Scope, close, extend2, fork2;
21661
21663
  var init_Scope = __esm(() => {
21662
21664
  init_core();
21663
21665
  init_fiberRuntime();
21664
21666
  Scope = scopeTag;
21665
21667
  close = scopeClose;
21668
+ extend2 = scopeExtend;
21666
21669
  fork2 = scopeFork;
21667
21670
  });
21668
21671
 
@@ -22156,13 +22159,13 @@ var makeDual = (f) => function() {
22156
22159
  const error = new AsyncFiberExceptionImpl(fiber);
22157
22160
  Error.stackTraceLimit = limit;
22158
22161
  return error;
22159
- }, FiberFailureId, FiberFailureCauseId, FiberFailureImpl, fiberFailure = (cause2) => {
22162
+ }, isAsyncFiberException = (u) => isTagged(u, "AsyncFiberException") && ("fiber" in u), FiberFailureId, FiberFailureCauseId, FiberFailureImpl, fiberFailure = (cause2) => {
22160
22163
  const limit = Error.stackTraceLimit;
22161
22164
  Error.stackTraceLimit = 0;
22162
22165
  const error = new FiberFailureImpl(cause2);
22163
22166
  Error.stackTraceLimit = limit;
22164
22167
  return error;
22165
- }, fastPath = (effect) => {
22168
+ }, isFiberFailure = (u) => hasProperty(u, FiberFailureId), fastPath = (effect) => {
22166
22169
  const op = effect;
22167
22170
  switch (op._op) {
22168
22171
  case "Failure":
@@ -22182,7 +22185,7 @@ var makeDual = (f) => function() {
22182
22185
  return exitFail(new NoSuchElementException);
22183
22186
  }
22184
22187
  }
22185
- }, unsafeRunSyncExit, unsafeRunPromise, unsafeRunPromiseExit, make36 = (options) => new RuntimeImpl(options.context, options.runtimeFlags, options.fiberRefs), runtime2 = () => withFiberRuntime((state, status3) => succeed(new RuntimeImpl(state.getFiberRef(currentContext), status3.runtimeFlags, state.getFiberRefs()))), defaultRuntimeFlags, defaultRuntime, unsafeRunEffect, unsafeForkEffect, unsafeRunPromiseEffect, unsafeRunPromiseExitEffect, unsafeRunSyncEffect, unsafeRunSyncExitEffect, asyncEffect = (register) => suspend(() => {
22188
+ }, unsafeRunSyncExit, unsafeRunPromise, unsafeRunPromiseExit, make36 = (options) => new RuntimeImpl(options.context, options.runtimeFlags, options.fiberRefs), runtime2 = () => withFiberRuntime((state, status3) => succeed(new RuntimeImpl(state.getFiberRef(currentContext), status3.runtimeFlags, state.getFiberRefs()))), defaultRuntimeFlags, defaultRuntime, updateRuntimeFlags2, disableRuntimeFlag, enableRuntimeFlag, updateContext2, provideService2, updateFiberRefs2, setFiberRef, deleteFiberRef, unsafeRunEffect, unsafeForkEffect, unsafeRunPromiseEffect, unsafeRunPromiseExitEffect, unsafeRunSyncEffect, unsafeRunSyncExitEffect, asyncEffect = (register) => suspend(() => {
22186
22189
  let cleanup = undefined;
22187
22190
  return flatMap7(deferredMake(), (deferred) => flatMap7(runtime2(), (runtime3) => uninterruptibleMask((restore) => zipRight(fork(restore(matchCauseEffect(register((cb) => unsafeRunCallback(runtime3)(intoDeferred(cb, deferred))), {
22188
22191
  onFailure: (cause2) => deferredFailCause(deferred, cause2),
@@ -22203,6 +22206,7 @@ var init_runtime = __esm(() => {
22203
22206
  init_Inspectable();
22204
22207
  init_Option();
22205
22208
  init_Pipeable();
22209
+ init_Predicate();
22206
22210
  init_Scheduler();
22207
22211
  init_Scope();
22208
22212
  init_cause();
@@ -22355,6 +22359,30 @@ var init_runtime = __esm(() => {
22355
22359
  runtimeFlags: defaultRuntimeFlags,
22356
22360
  fiberRefs: /* @__PURE__ */ empty20()
22357
22361
  });
22362
+ updateRuntimeFlags2 = /* @__PURE__ */ dual(2, (self2, f) => make36({
22363
+ context: self2.context,
22364
+ runtimeFlags: f(self2.runtimeFlags),
22365
+ fiberRefs: self2.fiberRefs
22366
+ }));
22367
+ disableRuntimeFlag = /* @__PURE__ */ dual(2, (self2, flag) => updateRuntimeFlags2(self2, disable2(flag)));
22368
+ enableRuntimeFlag = /* @__PURE__ */ dual(2, (self2, flag) => updateRuntimeFlags2(self2, enable2(flag)));
22369
+ updateContext2 = /* @__PURE__ */ dual(2, (self2, f) => make36({
22370
+ context: f(self2.context),
22371
+ runtimeFlags: self2.runtimeFlags,
22372
+ fiberRefs: self2.fiberRefs
22373
+ }));
22374
+ provideService2 = /* @__PURE__ */ dual(3, (self2, tag, service) => updateContext2(self2, add4(tag, service)));
22375
+ updateFiberRefs2 = /* @__PURE__ */ dual(2, (self2, f) => make36({
22376
+ context: self2.context,
22377
+ runtimeFlags: self2.runtimeFlags,
22378
+ fiberRefs: f(self2.fiberRefs)
22379
+ }));
22380
+ setFiberRef = /* @__PURE__ */ dual(3, (self2, fiberRef, value) => updateFiberRefs2(self2, updateAs2({
22381
+ fiberId: none4,
22382
+ fiberRef,
22383
+ value
22384
+ })));
22385
+ deleteFiberRef = /* @__PURE__ */ dual(2, (self2, fiberRef) => updateFiberRefs2(self2, delete_2(fiberRef)));
22358
22386
  unsafeRunEffect = /* @__PURE__ */ unsafeRunCallback(defaultRuntime);
22359
22387
  unsafeForkEffect = /* @__PURE__ */ unsafeFork2(defaultRuntime);
22360
22388
  unsafeRunPromiseEffect = /* @__PURE__ */ unsafeRunPromise(defaultRuntime);
@@ -23824,7 +23852,7 @@ var ScheduleSymbolKey = "effect/Schedule", ScheduleTypeId, isSchedule = (u) => h
23824
23852
  const end3 = endOfMinute(minute0);
23825
23853
  const interval = make33(start3, end3);
23826
23854
  return succeed([[end3, n + 1], n, continueWith2(interval)]);
23827
- }), modifyDelay, modifyDelayEffect, onDecision, passthrough2 = (self2) => makeWithState(self2.initial, (now, input, state) => pipe(self2.step(now, input, state), map9(([state2, _, decision]) => [state2, input, decision]))), provideContext3, provideService2, recurUntil = (f) => untilInput(identity2(), f), recurUntilEffect = (f) => untilInputEffect(identity2(), f), recurUntilOption = (pf) => untilOutput(map14(identity2(), pf), isSome2), recurUpTo = (durationInput) => {
23855
+ }), modifyDelay, modifyDelayEffect, onDecision, passthrough2 = (self2) => makeWithState(self2.initial, (now, input, state) => pipe(self2.step(now, input, state), map9(([state2, _, decision]) => [state2, input, decision]))), provideContext3, provideService3, recurUntil = (f) => untilInput(identity2(), f), recurUntilEffect = (f) => untilInputEffect(identity2(), f), recurUntilOption = (pf) => untilOutput(map14(identity2(), pf), isSome2), recurUpTo = (durationInput) => {
23828
23856
  const duration2 = decode(durationInput);
23829
23857
  return whileOutput(elapsed, (elapsed) => lessThan2(elapsed, duration2));
23830
23858
  }, recurWhile = (f) => whileInput(identity2(), f), recurWhileEffect = (f) => whileInputEffect(identity2(), f), recurs = (n) => whileOutput(forever2, (out) => out < n), reduce10, reduceEffect2, repetitions = (self2) => reduce10(self2, 0, (n, _) => n + 1), resetAfter, resetWhen, run, runLoop = (self2, now, inputs, state, acc) => {
@@ -24160,7 +24188,7 @@ var init_schedule = __esm(() => {
24160
24188
  })));
24161
24189
  onDecision = /* @__PURE__ */ dual(2, (self2, f) => makeWithState(self2.initial, (now, input, state) => flatMap7(self2.step(now, input, state), ([state2, out, decision]) => as2(f(out, decision), [state2, out, decision]))));
24162
24190
  provideContext3 = /* @__PURE__ */ dual(2, (self2, context3) => makeWithState(self2.initial, (now, input, state) => provideContext(self2.step(now, input, state), context3)));
24163
- provideService2 = /* @__PURE__ */ dual(3, (self2, tag, service2) => makeWithState(self2.initial, (now, input, state) => contextWithEffect((env) => provideContext(self2.step(now, input, state), add4(env, tag, service2)))));
24191
+ provideService3 = /* @__PURE__ */ dual(3, (self2, tag, service2) => makeWithState(self2.initial, (now, input, state) => contextWithEffect((env) => provideContext(self2.step(now, input, state), add4(env, tag, service2)))));
24164
24192
  reduce10 = /* @__PURE__ */ dual(3, (self2, zero2, f) => reduceEffect2(self2, zero2, (z, out) => sync(() => f(z, out))));
24165
24193
  reduceEffect2 = /* @__PURE__ */ dual(3, (self2, zero2, f) => makeWithState([self2.initial, zero2], (now, input, [s, z]) => flatMap7(self2.step(now, input, s), ([s2, out, decision]) => isDone4(decision) ? succeed([[s2, z], z, decision]) : map9(f(z, out), (z2) => [[s2, z2], z, decision]))));
24166
24194
  resetAfter = /* @__PURE__ */ dual(2, (self2, durationInput) => {
@@ -25051,7 +25079,7 @@ __export(exports_Effect, {
25051
25079
  using: () => using2,
25052
25080
  useSpan: () => useSpan2,
25053
25081
  updateService: () => updateService2,
25054
- updateFiberRefs: () => updateFiberRefs2,
25082
+ updateFiberRefs: () => updateFiberRefs3,
25055
25083
  unsandbox: () => unsandbox2,
25056
25084
  unsafeMakeSemaphore: () => unsafeMakeSemaphore2,
25057
25085
  unsafeMakeLatch: () => unsafeMakeLatch2,
@@ -25140,7 +25168,7 @@ __export(exports_Effect, {
25140
25168
  raceAll: () => raceAll2,
25141
25169
  race: () => race2,
25142
25170
  provideServiceEffect: () => provideServiceEffect2,
25143
- provideService: () => provideService3,
25171
+ provideService: () => provideService4,
25144
25172
  provide: () => provide2,
25145
25173
  promise: () => promise2,
25146
25174
  patchRuntimeFlags: () => patchRuntimeFlags,
@@ -25391,7 +25419,7 @@ ${endStackCall}`;
25391
25419
  };
25392
25420
  return withSpan3(effect, options.spanName, opts);
25393
25421
  }
25394
- var EffectTypeId3, isEffect2, cachedWithTTL, cachedInvalidateWithTTL2, cached3, cachedFunction2, once3, all6, allWith2, allSuccesses2, dropUntil2, dropWhile2, takeUntil2, takeWhile2, every5, exists2, filter7, filterMap4, findFirst4, forEach5, head4, mergeAll5, partition4, reduce11, reduceWhile2, reduceRight3, reduceEffect3, replicate2, replicateEffect2, validateAll2, validateFirst2, async, asyncEffect2, custom2, withFiberRuntime2, fail9, failSync3, failCause8, failCauseSync3, die5, dieMessage2, dieSync3, gen2, never4, none9, promise2, succeed9, succeedNone2, succeedSome2, suspend3, sync4, _void, yieldNow4, _catch2, catchAll3, catchAllCause3, catchAllDefect2, catchIf2, catchSome2, catchSomeCause2, catchSomeDefect2, catchTag2, catchTags2, cause2, eventually2, ignore2, ignoreLogged2, parallelErrors2, sandbox2, retry2, withExecutionPlan2, retryOrElse, try_2, tryMap2, tryMapPromise2, tryPromise2, unsandbox2, allowInterrupt2, checkInterruptible2, disconnect2, interrupt7, interruptWith2, interruptible4, interruptibleMask2, onInterrupt2, uninterruptible2, uninterruptibleMask3, liftPredicate2, as5, asSome2, asSomeError2, asVoid3, flip2, flipWith2, map15, mapAccum3, mapBoth3, mapError3, mapErrorCause2, merge7, negate2, acquireRelease2, acquireReleaseInterruptible2, acquireUseRelease2, addFinalizer2, ensuring3, onError2, onExit3, parallelFinalizers2, sequentialFinalizers2, finalizersMask2, scope3, scopeWith2, scopedWith2, scoped3, using2, withEarlyRelease2, awaitAllChildren2, daemonChildren2, descriptor2, descriptorWith2, diffFiberRefs2, ensuringChild2, ensuringChildren2, fiberId2, fiberIdWith2, fork3, forkDaemon2, forkAll2, forkIn2, forkScoped2, forkWithErrorHandler2, fromFiber2, fromFiberEffect2, supervised2, transplant2, withConcurrency2, withScheduler2, withSchedulingPriority2, withMaxOpsBeforeYield2, clock2, clockWith4, withClockScoped2, withClock2, console3, consoleWith2, withConsoleScoped2, withConsole2, delay2, sleep4, timed2, timedWith2, timeout2, timeoutOption2, timeoutFail2, timeoutFailCause2, timeoutTo2, configProviderWith2, withConfigProvider2, withConfigProviderScoped2, context3, contextWith2, contextWithEffect2, mapInputContext3, provide2, provideService3, provideServiceEffect2, serviceFunction2, serviceFunctionEffect2, serviceFunctions2, serviceConstants2, serviceMembers2, serviceOption2, serviceOptional2, updateService2, Do2, bind3, bindAll2, bindTo3, let_3, option2, either4, exit2, intoDeferred2, if_2, filterOrDie2, filterOrDieMessage2, filterOrElse2, filterOrFail2, filterEffectOrElse2, filterEffectOrFail2, unless2, unlessEffect2, when2, whenEffect2, whenFiberRef2, whenRef2, flatMap11, andThen6, flatten8, race2, raceAll2, raceFirst2, raceWith2, summarized2, tap3, tapBoth2, tapDefect2, tapError3, tapErrorTag2, tapErrorCause3, forever3, iterate2, loop2, repeat, repeatN2, repeatOrElse, schedule, scheduleForked2, scheduleFrom, whileLoop2, getFiberRefs, inheritFiberRefs2, locally, locallyWith, locallyScoped, locallyScopedWith, patchFiberRefs2, setFiberRefs2, updateFiberRefs2, isFailure4, isSuccess3, match15, matchCause3, matchCauseEffect3, matchEffect2, log3, logWithLevel2 = (level, ...message) => logWithLevel(level)(...message), logTrace2, logDebug2, logInfo2, logWarning2, logError2, logFatal2, withLogSpan2, annotateLogs3, annotateLogsScoped2, logAnnotations2, withUnhandledErrorLogLevel2, whenLogLevel2, orDie3, orDieWith2, orElse5, orElseFail2, orElseSucceed2, firstSuccessOf2, random3, randomWith2, withRandom2, withRandomFixed, withRandomScoped2, runtime3, getRuntimeFlags, patchRuntimeFlags, withRuntimeFlagsPatch, withRuntimeFlagsPatchScoped, tagMetrics2, labelMetrics2, tagMetricsScoped2, labelMetricsScoped2, metricLabels2, withMetric2, unsafeMakeSemaphore2, makeSemaphore2, unsafeMakeLatch2, makeLatch2, runFork2, runCallback, runPromise, runPromiseExit, runSync, runSyncExit, validate2, validateWith2, zip5, zipLeft4, zipRight4, zipWith7, ap, blocked2, runRequestBlock2, step3, request, cacheRequestResult, withRequestBatching2, withRequestCaching2, withRequestCache2, tracer2, tracerWith4, withTracer2, withTracerScoped2, withTracerEnabled2, withTracerTiming2, annotateSpans3, annotateCurrentSpan2, currentSpan2, currentPropagatedSpan2, currentParentSpan2, spanAnnotations2, spanLinks2, linkSpans2, linkSpanCurrent2, makeSpan2, makeSpanScoped2, useSpan2, withSpan3, functionWithSpan2, withSpanScoped2, withParentSpan3, fromNullable3, optionFromOptional2, transposeOption = (self2) => {
25422
+ var EffectTypeId3, isEffect2, cachedWithTTL, cachedInvalidateWithTTL2, cached3, cachedFunction2, once3, all6, allWith2, allSuccesses2, dropUntil2, dropWhile2, takeUntil2, takeWhile2, every5, exists2, filter7, filterMap4, findFirst4, forEach5, head4, mergeAll5, partition4, reduce11, reduceWhile2, reduceRight3, reduceEffect3, replicate2, replicateEffect2, validateAll2, validateFirst2, async, asyncEffect2, custom2, withFiberRuntime2, fail9, failSync3, failCause8, failCauseSync3, die5, dieMessage2, dieSync3, gen2, never4, none9, promise2, succeed9, succeedNone2, succeedSome2, suspend3, sync4, _void, yieldNow4, _catch2, catchAll3, catchAllCause3, catchAllDefect2, catchIf2, catchSome2, catchSomeCause2, catchSomeDefect2, catchTag2, catchTags2, cause2, eventually2, ignore2, ignoreLogged2, parallelErrors2, sandbox2, retry2, withExecutionPlan2, retryOrElse, try_2, tryMap2, tryMapPromise2, tryPromise2, unsandbox2, allowInterrupt2, checkInterruptible2, disconnect2, interrupt7, interruptWith2, interruptible4, interruptibleMask2, onInterrupt2, uninterruptible2, uninterruptibleMask3, liftPredicate2, as5, asSome2, asSomeError2, asVoid3, flip2, flipWith2, map15, mapAccum3, mapBoth3, mapError3, mapErrorCause2, merge7, negate2, acquireRelease2, acquireReleaseInterruptible2, acquireUseRelease2, addFinalizer2, ensuring3, onError2, onExit3, parallelFinalizers2, sequentialFinalizers2, finalizersMask2, scope3, scopeWith2, scopedWith2, scoped3, using2, withEarlyRelease2, awaitAllChildren2, daemonChildren2, descriptor2, descriptorWith2, diffFiberRefs2, ensuringChild2, ensuringChildren2, fiberId2, fiberIdWith2, fork3, forkDaemon2, forkAll2, forkIn2, forkScoped2, forkWithErrorHandler2, fromFiber2, fromFiberEffect2, supervised2, transplant2, withConcurrency2, withScheduler2, withSchedulingPriority2, withMaxOpsBeforeYield2, clock2, clockWith4, withClockScoped2, withClock2, console3, consoleWith2, withConsoleScoped2, withConsole2, delay2, sleep4, timed2, timedWith2, timeout2, timeoutOption2, timeoutFail2, timeoutFailCause2, timeoutTo2, configProviderWith2, withConfigProvider2, withConfigProviderScoped2, context3, contextWith2, contextWithEffect2, mapInputContext3, provide2, provideService4, provideServiceEffect2, serviceFunction2, serviceFunctionEffect2, serviceFunctions2, serviceConstants2, serviceMembers2, serviceOption2, serviceOptional2, updateService2, Do2, bind3, bindAll2, bindTo3, let_3, option2, either4, exit2, intoDeferred2, if_2, filterOrDie2, filterOrDieMessage2, filterOrElse2, filterOrFail2, filterEffectOrElse2, filterEffectOrFail2, unless2, unlessEffect2, when2, whenEffect2, whenFiberRef2, whenRef2, flatMap11, andThen6, flatten8, race2, raceAll2, raceFirst2, raceWith2, summarized2, tap3, tapBoth2, tapDefect2, tapError3, tapErrorTag2, tapErrorCause3, forever3, iterate2, loop2, repeat, repeatN2, repeatOrElse, schedule, scheduleForked2, scheduleFrom, whileLoop2, getFiberRefs, inheritFiberRefs2, locally, locallyWith, locallyScoped, locallyScopedWith, patchFiberRefs2, setFiberRefs2, updateFiberRefs3, isFailure4, isSuccess3, match15, matchCause3, matchCauseEffect3, matchEffect2, log3, logWithLevel2 = (level, ...message) => logWithLevel(level)(...message), logTrace2, logDebug2, logInfo2, logWarning2, logError2, logFatal2, withLogSpan2, annotateLogs3, annotateLogsScoped2, logAnnotations2, withUnhandledErrorLogLevel2, whenLogLevel2, orDie3, orDieWith2, orElse5, orElseFail2, orElseSucceed2, firstSuccessOf2, random3, randomWith2, withRandom2, withRandomFixed, withRandomScoped2, runtime3, getRuntimeFlags, patchRuntimeFlags, withRuntimeFlagsPatch, withRuntimeFlagsPatchScoped, tagMetrics2, labelMetrics2, tagMetricsScoped2, labelMetricsScoped2, metricLabels2, withMetric2, unsafeMakeSemaphore2, makeSemaphore2, unsafeMakeLatch2, makeLatch2, runFork2, runCallback, runPromise, runPromiseExit, runSync, runSyncExit, validate2, validateWith2, zip5, zipLeft4, zipRight4, zipWith7, ap, blocked2, runRequestBlock2, step3, request, cacheRequestResult, withRequestBatching2, withRequestCaching2, withRequestCache2, tracer2, tracerWith4, withTracer2, withTracerScoped2, withTracerEnabled2, withTracerTiming2, annotateSpans3, annotateCurrentSpan2, currentSpan2, currentPropagatedSpan2, currentParentSpan2, spanAnnotations2, spanLinks2, linkSpans2, linkSpanCurrent2, makeSpan2, makeSpanScoped2, useSpan2, withSpan3, functionWithSpan2, withSpanScoped2, withParentSpan3, fromNullable3, optionFromOptional2, transposeOption = (self2) => {
25395
25423
  return isNone(self2) ? succeedNone2 : map15(self2.value, some);
25396
25424
  }, transposeMapOption, makeTagProxy = (TagClass) => {
25397
25425
  const cache = new Map;
@@ -25777,7 +25805,7 @@ var init_Effect = __esm(() => {
25777
25805
  contextWithEffect2 = contextWithEffect;
25778
25806
  mapInputContext3 = mapInputContext;
25779
25807
  provide2 = effect_provide;
25780
- provideService3 = provideService;
25808
+ provideService4 = provideService;
25781
25809
  provideServiceEffect2 = provideServiceEffect;
25782
25810
  serviceFunction2 = serviceFunction;
25783
25811
  serviceFunctionEffect2 = serviceFunctionEffect;
@@ -25841,7 +25869,7 @@ var init_Effect = __esm(() => {
25841
25869
  locallyScopedWith = fiberRefLocallyScopedWith;
25842
25870
  patchFiberRefs2 = patchFiberRefs;
25843
25871
  setFiberRefs2 = setFiberRefs;
25844
- updateFiberRefs2 = updateFiberRefs;
25872
+ updateFiberRefs3 = updateFiberRefs;
25845
25873
  isFailure4 = isFailure3;
25846
25874
  isSuccess3 = isSuccess;
25847
25875
  match15 = match9;
@@ -26133,6 +26161,160 @@ var init_Layer = __esm(() => {
26133
26161
  updateService3 = /* @__PURE__ */ dual(3, (layer, tag, f) => provide3(layer, map16(context4(), (c) => add4(c, tag, f(unsafeGet4(c, tag))))));
26134
26162
  });
26135
26163
 
26164
+ // ../../node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/managedRuntime.js
26165
+ function provide4(managed, effect2) {
26166
+ return flatMap7(managed.runtimeEffect, (rt) => withFiberRuntime((fiber) => {
26167
+ fiber.setFiberRefs(rt.fiberRefs);
26168
+ fiber.currentRuntimeFlags = rt.runtimeFlags;
26169
+ return provideContext(effect2, rt.context);
26170
+ }));
26171
+ }
26172
+ var isManagedRuntime = (u) => hasProperty(u, TypeId12), ManagedRuntimeProto, make39 = (layer, memoMap) => {
26173
+ memoMap = memoMap ?? unsafeMakeMemoMap();
26174
+ const scope5 = unsafeRunSyncEffect(scopeMake());
26175
+ let buildFiber;
26176
+ const runtimeEffect = suspend(() => {
26177
+ if (!buildFiber) {
26178
+ const scheduler = new SyncScheduler;
26179
+ buildFiber = unsafeForkEffect(tap(extend2(toRuntimeWithMemoMap(layer, memoMap), scope5), (rt) => {
26180
+ self2.cachedRuntime = rt;
26181
+ }), {
26182
+ scope: scope5,
26183
+ scheduler
26184
+ });
26185
+ scheduler.flush();
26186
+ }
26187
+ return flatten4(buildFiber.await);
26188
+ });
26189
+ const self2 = Object.assign(Object.create(ManagedRuntimeProto), {
26190
+ memoMap,
26191
+ scope: scope5,
26192
+ runtimeEffect,
26193
+ cachedRuntime: undefined,
26194
+ runtime() {
26195
+ return self2.cachedRuntime === undefined ? unsafeRunPromiseEffect(self2.runtimeEffect) : Promise.resolve(self2.cachedRuntime);
26196
+ },
26197
+ dispose() {
26198
+ return unsafeRunPromiseEffect(self2.disposeEffect);
26199
+ },
26200
+ disposeEffect: suspend(() => {
26201
+ self2.runtimeEffect = die2("ManagedRuntime disposed");
26202
+ self2.cachedRuntime = undefined;
26203
+ return close(self2.scope, exitVoid);
26204
+ }),
26205
+ runFork(effect2, options) {
26206
+ return self2.cachedRuntime === undefined ? unsafeForkEffect(provide4(self2, effect2), options) : unsafeFork2(self2.cachedRuntime)(effect2, options);
26207
+ },
26208
+ runSyncExit(effect2) {
26209
+ return self2.cachedRuntime === undefined ? unsafeRunSyncExitEffect(provide4(self2, effect2)) : unsafeRunSyncExit(self2.cachedRuntime)(effect2);
26210
+ },
26211
+ runSync(effect2) {
26212
+ return self2.cachedRuntime === undefined ? unsafeRunSyncEffect(provide4(self2, effect2)) : unsafeRunSync(self2.cachedRuntime)(effect2);
26213
+ },
26214
+ runPromiseExit(effect2, options) {
26215
+ return self2.cachedRuntime === undefined ? unsafeRunPromiseExitEffect(provide4(self2, effect2), options) : unsafeRunPromiseExit(self2.cachedRuntime)(effect2, options);
26216
+ },
26217
+ runCallback(effect2, options) {
26218
+ return self2.cachedRuntime === undefined ? unsafeRunCallback(defaultRuntime)(provide4(self2, effect2), options) : unsafeRunCallback(self2.cachedRuntime)(effect2, options);
26219
+ },
26220
+ runPromise(effect2, options) {
26221
+ return self2.cachedRuntime === undefined ? unsafeRunPromiseEffect(provide4(self2, effect2), options) : unsafeRunPromise(self2.cachedRuntime)(effect2, options);
26222
+ }
26223
+ });
26224
+ return self2;
26225
+ };
26226
+ var init_managedRuntime = __esm(() => {
26227
+ init_Effectable();
26228
+ init_Pipeable();
26229
+ init_Predicate();
26230
+ init_Scheduler();
26231
+ init_Scope();
26232
+ init_core();
26233
+ init_fiberRuntime();
26234
+ init_layer();
26235
+ init_circular2();
26236
+ init_runtime();
26237
+ ManagedRuntimeProto = {
26238
+ ...CommitPrototype2,
26239
+ [TypeId12]: TypeId12,
26240
+ pipe() {
26241
+ return pipeArguments(this, arguments);
26242
+ },
26243
+ commit() {
26244
+ return this.runtimeEffect;
26245
+ }
26246
+ };
26247
+ });
26248
+
26249
+ // ../../node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/ManagedRuntime.js
26250
+ var exports_ManagedRuntime = {};
26251
+ __export(exports_ManagedRuntime, {
26252
+ make: () => make40,
26253
+ isManagedRuntime: () => isManagedRuntime2,
26254
+ TypeId: () => TypeId17
26255
+ });
26256
+ var TypeId17, isManagedRuntime2, make40;
26257
+ var init_ManagedRuntime = __esm(() => {
26258
+ init_managedRuntime();
26259
+ init_circular2();
26260
+ TypeId17 = TypeId12;
26261
+ isManagedRuntime2 = isManagedRuntime;
26262
+ make40 = make39;
26263
+ });
26264
+
26265
+ // ../../node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Runtime.js
26266
+ var exports_Runtime = {};
26267
+ __export(exports_Runtime, {
26268
+ updateRuntimeFlags: () => updateRuntimeFlags3,
26269
+ updateFiberRefs: () => updateFiberRefs4,
26270
+ updateContext: () => updateContext3,
26271
+ setFiberRef: () => setFiberRef2,
26272
+ runSyncExit: () => runSyncExit2,
26273
+ runSync: () => runSync2,
26274
+ runPromiseExit: () => runPromiseExit2,
26275
+ runPromise: () => runPromise2,
26276
+ runFork: () => runFork3,
26277
+ runCallback: () => runCallback2,
26278
+ provideService: () => provideService5,
26279
+ makeFiberFailure: () => makeFiberFailure,
26280
+ make: () => make41,
26281
+ isFiberFailure: () => isFiberFailure2,
26282
+ isAsyncFiberException: () => isAsyncFiberException2,
26283
+ enableRuntimeFlag: () => enableRuntimeFlag2,
26284
+ disableRuntimeFlag: () => disableRuntimeFlag2,
26285
+ deleteFiberRef: () => deleteFiberRef2,
26286
+ defaultRuntimeFlags: () => defaultRuntimeFlags2,
26287
+ defaultRuntime: () => defaultRuntime2,
26288
+ FiberFailureId: () => FiberFailureId2,
26289
+ FiberFailureCauseId: () => FiberFailureCauseId2
26290
+ });
26291
+ var runFork3, runSyncExit2, runSync2, runCallback2, runPromise2, runPromiseExit2, defaultRuntime2, defaultRuntimeFlags2, make41, FiberFailureId2, FiberFailureCauseId2, isAsyncFiberException2, isFiberFailure2, makeFiberFailure, updateRuntimeFlags3, enableRuntimeFlag2, disableRuntimeFlag2, updateContext3, provideService5, updateFiberRefs4, setFiberRef2, deleteFiberRef2;
26292
+ var init_Runtime = __esm(() => {
26293
+ init_runtime();
26294
+ runFork3 = unsafeFork2;
26295
+ runSyncExit2 = unsafeRunSyncExit;
26296
+ runSync2 = unsafeRunSync;
26297
+ runCallback2 = unsafeRunCallback;
26298
+ runPromise2 = unsafeRunPromise;
26299
+ runPromiseExit2 = unsafeRunPromiseExit;
26300
+ defaultRuntime2 = defaultRuntime;
26301
+ defaultRuntimeFlags2 = defaultRuntimeFlags;
26302
+ make41 = make36;
26303
+ FiberFailureId2 = /* @__PURE__ */ Symbol.for("effect/Runtime/FiberFailure");
26304
+ FiberFailureCauseId2 = FiberFailureCauseId;
26305
+ isAsyncFiberException2 = isAsyncFiberException;
26306
+ isFiberFailure2 = isFiberFailure;
26307
+ makeFiberFailure = fiberFailure;
26308
+ updateRuntimeFlags3 = updateRuntimeFlags2;
26309
+ enableRuntimeFlag2 = enableRuntimeFlag;
26310
+ disableRuntimeFlag2 = disableRuntimeFlag;
26311
+ updateContext3 = updateContext2;
26312
+ provideService5 = provideService2;
26313
+ updateFiberRefs4 = updateFiberRefs2;
26314
+ setFiberRef2 = setFiberRef;
26315
+ deleteFiberRef2 = deleteFiberRef;
26316
+ });
26317
+
26136
26318
  // ../../node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Schedule.js
26137
26319
  var exports_Schedule = {};
26138
26320
  __export(exports_Schedule, {
@@ -26173,7 +26355,7 @@ __export(exports_Schedule, {
26173
26355
  recurUntilOption: () => recurUntilOption2,
26174
26356
  recurUntilEffect: () => recurUntilEffect2,
26175
26357
  recurUntil: () => recurUntil2,
26176
- provideService: () => provideService4,
26358
+ provideService: () => provideService6,
26177
26359
  provideContext: () => provideContext4,
26178
26360
  passthrough: () => passthrough4,
26179
26361
  once: () => once4,
@@ -26238,7 +26420,7 @@ __export(exports_Schedule, {
26238
26420
  ScheduleDriverTypeId: () => ScheduleDriverTypeId2,
26239
26421
  CurrentIterationMetadata: () => CurrentIterationMetadata2
26240
26422
  });
26241
- var ScheduleTypeId2, ScheduleDriverTypeId2, makeWithState2, isSchedule2, addDelay2, addDelayEffect2, andThen7, andThenEither2, as6, asVoid4, bothInOut2, check2, checkEffect2, collectAllInputs2, collectAllOutputs2, collectUntil2, collectUntilEffect2, collectWhile2, collectWhileEffect2, compose2, mapInput4, mapInputEffect2, mapInputContext4, count2, cron2, secondOfMinute2, minuteOfHour2, hourOfDay2, dayOfMonth2, dayOfWeek2, delayed2, delayedEffect2, delayedSchedule2, delays2, mapBoth4, mapBothEffect2, driver2, duration2, either5, eitherWith2, elapsed2, ensuring4, exponential3, fibonacci2, fixed4, forever4, fromDelay2, fromDelays2, fromFunction4, identity3, passthrough4, intersect6, intersectWith2, jittered2, jitteredWith2, linear2, map17, mapEffect4, modifyDelay2, modifyDelayEffect2, onDecision2, once4, provideContext4, provideService4, recurUntil2, recurUntilEffect2, recurUntilOption2, recurUpTo2, recurWhile2, recurWhileEffect2, recurs2, reduce12, reduceEffect4, repeatForever, repetitions2, resetAfter2, resetWhen2, run2, spaced2, stop2, succeed11, sync6, tapInput2, tapOutput2, unfold3, union9, unionWith3, untilInput2, untilInputEffect2, untilOutput2, untilOutputEffect2, upTo2, whileInput2, whileInputEffect2, whileOutput2, whileOutputEffect2, windowed2, zipLeft5, zipRight5, zipWith9, CurrentIterationMetadata2;
26423
+ var ScheduleTypeId2, ScheduleDriverTypeId2, makeWithState2, isSchedule2, addDelay2, addDelayEffect2, andThen7, andThenEither2, as6, asVoid4, bothInOut2, check2, checkEffect2, collectAllInputs2, collectAllOutputs2, collectUntil2, collectUntilEffect2, collectWhile2, collectWhileEffect2, compose2, mapInput4, mapInputEffect2, mapInputContext4, count2, cron2, secondOfMinute2, minuteOfHour2, hourOfDay2, dayOfMonth2, dayOfWeek2, delayed2, delayedEffect2, delayedSchedule2, delays2, mapBoth4, mapBothEffect2, driver2, duration2, either5, eitherWith2, elapsed2, ensuring4, exponential3, fibonacci2, fixed4, forever4, fromDelay2, fromDelays2, fromFunction4, identity3, passthrough4, intersect6, intersectWith2, jittered2, jitteredWith2, linear2, map17, mapEffect4, modifyDelay2, modifyDelayEffect2, onDecision2, once4, provideContext4, provideService6, recurUntil2, recurUntilEffect2, recurUntilOption2, recurUpTo2, recurWhile2, recurWhileEffect2, recurs2, reduce12, reduceEffect4, repeatForever, repetitions2, resetAfter2, resetWhen2, run2, spaced2, stop2, succeed11, sync6, tapInput2, tapOutput2, unfold3, union9, unionWith3, untilInput2, untilInputEffect2, untilOutput2, untilOutputEffect2, upTo2, whileInput2, whileInputEffect2, whileOutput2, whileOutputEffect2, windowed2, zipLeft5, zipRight5, zipWith9, CurrentIterationMetadata2;
26242
26424
  var init_Schedule = __esm(() => {
26243
26425
  init_schedule();
26244
26426
  ScheduleTypeId2 = ScheduleTypeId;
@@ -26304,7 +26486,7 @@ var init_Schedule = __esm(() => {
26304
26486
  onDecision2 = onDecision;
26305
26487
  once4 = once2;
26306
26488
  provideContext4 = provideContext3;
26307
- provideService4 = provideService2;
26489
+ provideService6 = provideService3;
26308
26490
  recurUntil2 = recurUntil;
26309
26491
  recurUntilEffect2 = recurUntilEffect;
26310
26492
  recurUntilOption2 = recurUntilOption;
@@ -26352,7 +26534,9 @@ var init_esm = __esm(() => {
26352
26534
  init_Effect();
26353
26535
  init_Fiber();
26354
26536
  init_Layer();
26537
+ init_ManagedRuntime();
26355
26538
  init_Ref();
26539
+ init_Runtime();
26356
26540
  init_Schedule();
26357
26541
  });
26358
26542
 
@@ -27281,21 +27465,30 @@ var init_pi_agent_service = __esm(() => {
27281
27465
  async resumeFromDaemonMemory(options, stored) {
27282
27466
  const { prompt, systemPrompt, model, context: context5 } = options;
27283
27467
  const modelForSession = model ?? stored.model;
27284
- const childProcess = this.spawnPiRpcProcess({
27285
- workingDir: stored.workingDir,
27286
- systemPrompt,
27287
- model: modelForSession,
27288
- sessionId: stored.harnessSessionId
27289
- });
27290
- await this.waitForSpawnReady(childProcess);
27291
- await this.writePrompt(childProcess, prompt);
27292
- return this.wireRpcProcess({
27293
- childProcess,
27294
- context: context5,
27295
- workingDir: stored.workingDir,
27296
- model: modelForSession,
27297
- harnessSessionId: stored.harnessSessionId
27298
- });
27468
+ let childProcess;
27469
+ try {
27470
+ childProcess = this.spawnPiRpcProcess({
27471
+ workingDir: stored.workingDir,
27472
+ systemPrompt,
27473
+ model: modelForSession,
27474
+ sessionId: stored.harnessSessionId
27475
+ });
27476
+ await this.waitForSpawnReady(childProcess);
27477
+ await this.writePrompt(childProcess, prompt);
27478
+ return this.wireRpcProcess({
27479
+ childProcess,
27480
+ context: context5,
27481
+ workingDir: stored.workingDir,
27482
+ model: modelForSession,
27483
+ harnessSessionId: stored.harnessSessionId
27484
+ });
27485
+ } catch (err) {
27486
+ const reason = err instanceof Error ? err.message : String(err);
27487
+ process.stderr.write(`[${new Date().toISOString()}] role:${context5.role} daemon-resume-fallback] ${reason} — cold spawning
27488
+ `);
27489
+ childProcess?.kill();
27490
+ return this.spawn(options);
27491
+ }
27299
27492
  }
27300
27493
  async resumeTurn(pid, prompt) {
27301
27494
  const child = this.childProcesses.get(pid);
@@ -28901,9 +29094,12 @@ ${options.prompt}`;
28901
29094
  local: { cwd: stored.workingDir, settingSources: [] }
28902
29095
  }), AGENT_CREATE_TIMEOUT_MS, "Agent.resume");
28903
29096
  } catch (err) {
29097
+ const reason = err instanceof Error ? err.message : String(err);
29098
+ process.stderr.write(`[${new Date().toISOString()}] role:${context5.role} daemon-resume-fallback] ${reason} — cold spawning
29099
+ `);
28904
29100
  keeper.kill();
28905
29101
  this.deleteProcess(pid);
28906
- throw err;
29102
+ return this.spawn(options);
28907
29103
  }
28908
29104
  return this.startRunningSession({
28909
29105
  pid,
@@ -31184,13 +31380,23 @@ function writeLogLine(target, options, kind, payload) {
31184
31380
  `);
31185
31381
  options.onLogLine?.(line);
31186
31382
  }
31187
- function isUsageLimitError(error) {
31188
- if (!error || typeof error !== "object")
31383
+ function isTerminalProviderError(error) {
31384
+ if (!error || typeof error !== "object") {
31385
+ if (typeof error === "string") {
31386
+ return matchesTerminalProviderErrorText(error);
31387
+ }
31189
31388
  return false;
31389
+ }
31190
31390
  const e = error;
31191
31391
  const name = String(e.name ?? e.type ?? "").toLowerCase();
31192
- const message = String(e.data?.message ?? e.message ?? "").toLowerCase();
31193
- return name.includes("usagelimit") || message.includes("usage limit") || message.includes("enable usage from your available balance");
31392
+ const message = String(e.data?.message ?? e.message ?? e.responseBody ?? "").toLowerCase();
31393
+ const blob = `${name}
31394
+ ${message}`;
31395
+ return matchesTerminalProviderErrorText(blob);
31396
+ }
31397
+ function matchesTerminalProviderErrorText(blob) {
31398
+ const text = blob.toLowerCase();
31399
+ return text.includes("usagelimit") || text.includes("usage limit") || text.includes("enable usage from your available balance") || text.includes("rate limit") || text.includes("ratelimit") || text.includes("too many requests") || text.includes("x-ratelimit-exceeded") || text.includes("weekly rate limit") || text.includes("exceeded your weekly");
31194
31400
  }
31195
31401
  function eventSessionId(event) {
31196
31402
  const p = event.properties;
@@ -31325,8 +31531,8 @@ function startSessionEventForwarder(client4, options) {
31325
31531
  payload += ` [command: ${props.command}]`;
31326
31532
  }
31327
31533
  writeLogLine(errorTarget, options, "error", payload);
31328
- if (isUsageLimitError(err)) {
31329
- writeLogLine(target, options, "agent_end", "reason: usage_limit_reached");
31534
+ if (isTerminalProviderError(err)) {
31535
+ writeLogLine(target, options, "agent_end", "reason: provider_rate_limit");
31330
31536
  for (const cb of agentEndCallbacks)
31331
31537
  cb();
31332
31538
  }
@@ -31339,6 +31545,11 @@ function startSessionEventForwarder(client4, options) {
31339
31545
  } catch (err) {
31340
31546
  const message = err instanceof Error ? err.message : String(err);
31341
31547
  writeLogLine(errorTarget, options, "error", message);
31548
+ if (isTerminalProviderError(err)) {
31549
+ writeLogLine(target, options, "agent_end", "reason: provider_rate_limit");
31550
+ for (const cb of agentEndCallbacks)
31551
+ cb();
31552
+ }
31342
31553
  } finally {
31343
31554
  doneResolve();
31344
31555
  }
@@ -31424,6 +31635,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
31424
31635
  init_parse_listening_url();
31425
31636
  init_session_metadata_store();
31426
31637
  OpenCodeSdkAgentService = class OpenCodeSdkAgentService extends BaseCLIAgentService {
31638
+ agentEndCallbacksByPid = new Map;
31427
31639
  id = "opencode-sdk";
31428
31640
  displayName = "OpenCode (SDK)";
31429
31641
  command = OPENCODE_COMMAND2;
@@ -31462,8 +31674,8 @@ var init_opencode_sdk_agent_service = __esm(() => {
31462
31674
  } catch (err) {
31463
31675
  console.warn(`[opencode-sdk] session.abort for pid=${pid} sessionId=${meta.sessionId} failed (continuing with SIGTERM):`, err instanceof Error ? err.message : err);
31464
31676
  }
31677
+ this.sessionStore.remove(meta.sessionId);
31465
31678
  }
31466
- this.sessionStore.remove(meta.sessionId);
31467
31679
  }
31468
31680
  await super.stop(pid);
31469
31681
  }
@@ -31548,6 +31760,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
31548
31760
  this.forwarders.delete(pid);
31549
31761
  }
31550
31762
  this.sessionStore.remove(sessionId);
31763
+ this.agentEndCallbacksByPid.delete(pid);
31551
31764
  this.deleteProcess(pid);
31552
31765
  cb({ code: code2, signal, context: context5 });
31553
31766
  });
@@ -31556,6 +31769,9 @@ var init_opencode_sdk_agent_service = __esm(() => {
31556
31769
  outputCallbacks.push(cb);
31557
31770
  },
31558
31771
  onAgentEnd: (cb) => {
31772
+ const callbacks = this.agentEndCallbacksByPid.get(pid) ?? [];
31773
+ callbacks.push(cb);
31774
+ this.agentEndCallbacksByPid.set(pid, callbacks);
31559
31775
  forwarder?.onAgentEnd(cb);
31560
31776
  },
31561
31777
  onLogLine: (cb) => {
@@ -31573,6 +31789,54 @@ var init_opencode_sdk_agent_service = __esm(() => {
31573
31789
  }
31574
31790
  };
31575
31791
  }
31792
+ async startFreshSessionOnServe(args2) {
31793
+ const existingForwarder = this.forwarders.get(args2.pid);
31794
+ existingForwarder?.stop();
31795
+ this.forwarders.delete(args2.pid);
31796
+ const client4 = createOpencodeClient({ baseUrl: args2.baseUrl });
31797
+ const sessionCreateResult = await withTimeout2(client4.session.create({ body: {} }), SESSION_CREATE_TIMEOUT_MS, "session.create");
31798
+ if (!sessionCreateResult.data?.id) {
31799
+ throw new Error("Failed to create session during resume fallback");
31800
+ }
31801
+ const newSessionId2 = sessionCreateResult.data.id;
31802
+ const forwarder = startSessionEventForwarder(client4, {
31803
+ sessionId: newSessionId2,
31804
+ role: args2.context.role
31805
+ });
31806
+ const callbacks = this.agentEndCallbacksByPid.get(args2.pid) ?? [];
31807
+ for (const cb of callbacks) {
31808
+ forwarder.onAgentEnd(cb);
31809
+ }
31810
+ this.forwarders.set(args2.pid, forwarder);
31811
+ if (args2.oldSessionId) {
31812
+ this.sessionStore.remove(args2.oldSessionId);
31813
+ }
31814
+ this.sessionStore.upsert({
31815
+ sessionId: newSessionId2,
31816
+ machineId: args2.context.machineId,
31817
+ chatroomId: args2.context.chatroomId,
31818
+ role: args2.context.role,
31819
+ agentName: args2.agentName,
31820
+ ...args2.model ? { model: args2.model } : {},
31821
+ pid: args2.pid,
31822
+ createdAt: new Date().toISOString(),
31823
+ baseUrl: args2.baseUrl
31824
+ });
31825
+ const modelParts = args2.model ? parseModelId(args2.model) : undefined;
31826
+ await withTimeout2(client4.session.promptAsync({
31827
+ path: { id: newSessionId2 },
31828
+ body: {
31829
+ agent: args2.agentName,
31830
+ parts: [{ type: "text", text: args2.prompt }],
31831
+ ...modelParts ? { model: modelParts } : {},
31832
+ tools: {
31833
+ task: false,
31834
+ question: false,
31835
+ external_directory: false
31836
+ }
31837
+ }
31838
+ }), PROMPT_ASYNC_TIMEOUT_MS, "session.promptAsync");
31839
+ }
31576
31840
  async resumeFromDaemonMemory(options, session2) {
31577
31841
  const { prompt, systemPrompt, model, context: context5 } = options;
31578
31842
  const sessionId = session2.harnessSessionId;
@@ -31624,11 +31888,11 @@ var init_opencode_sdk_agent_service = __esm(() => {
31624
31888
  }), PROMPT_ASYNC_TIMEOUT_MS, "session.promptAsync");
31625
31889
  } catch (err) {
31626
31890
  const reason = err instanceof Error ? err.message : String(err);
31627
- process.stderr.write(`[${new Date().toISOString()}] role:${context5.role} resume-error] ${reason}
31891
+ process.stderr.write(`[${new Date().toISOString()}] role:${context5.role} daemon-resume-fallback] ${reason} — cold spawning
31628
31892
  `);
31629
31893
  forwarder?.stop();
31630
31894
  childProcess.kill();
31631
- throw err;
31895
+ return this.spawn(options);
31632
31896
  }
31633
31897
  return this.registerRunningSession({
31634
31898
  childProcess,
@@ -31733,23 +31997,45 @@ var init_opencode_sdk_agent_service = __esm(() => {
31733
31997
  async resumeTurn(pid, prompt) {
31734
31998
  const meta = this.sessionStore.findByPid(pid);
31735
31999
  if (!meta) {
31736
- throw new Error(`No opencode-sdk session metadata for pid=${pid}`);
32000
+ process.stderr.write(`[${new Date().toISOString()}] opencode-sdk resumeTurn: no metadata for pid=${pid}, skipping
32001
+ `);
32002
+ return;
31737
32003
  }
31738
32004
  const client4 = createOpencodeClient({ baseUrl: meta.baseUrl });
31739
32005
  const modelParts = meta.model ? parseModelId(meta.model) : undefined;
31740
- await withTimeout2(client4.session.promptAsync({
31741
- path: { id: meta.sessionId },
31742
- body: {
31743
- agent: meta.agentName,
31744
- parts: [{ type: "text", text: prompt }],
31745
- ...modelParts ? { model: modelParts } : {},
31746
- tools: {
31747
- task: false,
31748
- question: false,
31749
- external_directory: false
32006
+ const context5 = {
32007
+ machineId: meta.machineId,
32008
+ chatroomId: meta.chatroomId,
32009
+ role: meta.role
32010
+ };
32011
+ try {
32012
+ await withTimeout2(client4.session.promptAsync({
32013
+ path: { id: meta.sessionId },
32014
+ body: {
32015
+ agent: meta.agentName,
32016
+ parts: [{ type: "text", text: prompt }],
32017
+ ...modelParts ? { model: modelParts } : {},
32018
+ tools: {
32019
+ task: false,
32020
+ question: false,
32021
+ external_directory: false
32022
+ }
31750
32023
  }
31751
- }
31752
- }), PROMPT_ASYNC_TIMEOUT_MS, "session.promptAsync");
32024
+ }), PROMPT_ASYNC_TIMEOUT_MS, "session.promptAsync");
32025
+ } catch (err) {
32026
+ const reason = err instanceof Error ? err.message : String(err);
32027
+ process.stderr.write(`[${new Date().toISOString()}] role:${meta.role} resume-fallback] ${reason} — starting fresh session
32028
+ `);
32029
+ await this.startFreshSessionOnServe({
32030
+ pid,
32031
+ baseUrl: meta.baseUrl,
32032
+ context: context5,
32033
+ agentName: meta.agentName,
32034
+ model: meta.model,
32035
+ prompt,
32036
+ oldSessionId: meta.sessionId
32037
+ });
32038
+ }
31753
32039
  }
31754
32040
  };
31755
32041
  });
@@ -41454,51 +41740,51 @@ var require_resize = __commonJS((exports, module) => {
41454
41740
  }
41455
41741
  return this;
41456
41742
  }
41457
- function extend2(extend3) {
41458
- if (is.integer(extend3) && extend3 > 0) {
41459
- this.options.extendTop = extend3;
41460
- this.options.extendBottom = extend3;
41461
- this.options.extendLeft = extend3;
41462
- this.options.extendRight = extend3;
41463
- } else if (is.object(extend3)) {
41464
- if (is.defined(extend3.top)) {
41465
- if (is.integer(extend3.top) && extend3.top >= 0) {
41466
- this.options.extendTop = extend3.top;
41743
+ function extend3(extend4) {
41744
+ if (is.integer(extend4) && extend4 > 0) {
41745
+ this.options.extendTop = extend4;
41746
+ this.options.extendBottom = extend4;
41747
+ this.options.extendLeft = extend4;
41748
+ this.options.extendRight = extend4;
41749
+ } else if (is.object(extend4)) {
41750
+ if (is.defined(extend4.top)) {
41751
+ if (is.integer(extend4.top) && extend4.top >= 0) {
41752
+ this.options.extendTop = extend4.top;
41467
41753
  } else {
41468
- throw is.invalidParameterError("top", "positive integer", extend3.top);
41754
+ throw is.invalidParameterError("top", "positive integer", extend4.top);
41469
41755
  }
41470
41756
  }
41471
- if (is.defined(extend3.bottom)) {
41472
- if (is.integer(extend3.bottom) && extend3.bottom >= 0) {
41473
- this.options.extendBottom = extend3.bottom;
41757
+ if (is.defined(extend4.bottom)) {
41758
+ if (is.integer(extend4.bottom) && extend4.bottom >= 0) {
41759
+ this.options.extendBottom = extend4.bottom;
41474
41760
  } else {
41475
- throw is.invalidParameterError("bottom", "positive integer", extend3.bottom);
41761
+ throw is.invalidParameterError("bottom", "positive integer", extend4.bottom);
41476
41762
  }
41477
41763
  }
41478
- if (is.defined(extend3.left)) {
41479
- if (is.integer(extend3.left) && extend3.left >= 0) {
41480
- this.options.extendLeft = extend3.left;
41764
+ if (is.defined(extend4.left)) {
41765
+ if (is.integer(extend4.left) && extend4.left >= 0) {
41766
+ this.options.extendLeft = extend4.left;
41481
41767
  } else {
41482
- throw is.invalidParameterError("left", "positive integer", extend3.left);
41768
+ throw is.invalidParameterError("left", "positive integer", extend4.left);
41483
41769
  }
41484
41770
  }
41485
- if (is.defined(extend3.right)) {
41486
- if (is.integer(extend3.right) && extend3.right >= 0) {
41487
- this.options.extendRight = extend3.right;
41771
+ if (is.defined(extend4.right)) {
41772
+ if (is.integer(extend4.right) && extend4.right >= 0) {
41773
+ this.options.extendRight = extend4.right;
41488
41774
  } else {
41489
- throw is.invalidParameterError("right", "positive integer", extend3.right);
41775
+ throw is.invalidParameterError("right", "positive integer", extend4.right);
41490
41776
  }
41491
41777
  }
41492
- this._setBackgroundColourOption("extendBackground", extend3.background);
41493
- if (is.defined(extend3.extendWith)) {
41494
- if (is.string(extendWith[extend3.extendWith])) {
41495
- this.options.extendWith = extendWith[extend3.extendWith];
41778
+ this._setBackgroundColourOption("extendBackground", extend4.background);
41779
+ if (is.defined(extend4.extendWith)) {
41780
+ if (is.string(extendWith[extend4.extendWith])) {
41781
+ this.options.extendWith = extendWith[extend4.extendWith];
41496
41782
  } else {
41497
- throw is.invalidParameterError("extendWith", "one of: background, copy, repeat, mirror", extend3.extendWith);
41783
+ throw is.invalidParameterError("extendWith", "one of: background, copy, repeat, mirror", extend4.extendWith);
41498
41784
  }
41499
41785
  }
41500
41786
  } else {
41501
- throw is.invalidParameterError("extend", "integer or object", extend3);
41787
+ throw is.invalidParameterError("extend", "integer or object", extend4);
41502
41788
  }
41503
41789
  return this;
41504
41790
  }
@@ -41554,7 +41840,7 @@ var require_resize = __commonJS((exports, module) => {
41554
41840
  module.exports = (Sharp) => {
41555
41841
  Object.assign(Sharp.prototype, {
41556
41842
  resize,
41557
- extend: extend2,
41843
+ extend: extend3,
41558
41844
  extract,
41559
41845
  trim
41560
41846
  });
@@ -53499,7 +53785,7 @@ var toString, getPrototypeOf, iterator, toStringTag, kindOf, kindOfTest = (type)
53499
53785
  return kind === "formdata" || kind === "object" && isFunction3(thing.toString) && thing.toString() === "[object FormData]";
53500
53786
  }, isURLSearchParams, isReadableStream, isRequest3, isResponse, isHeaders, trim = (str) => {
53501
53787
  return str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "");
53502
- }, _global, isContextDefined = (context5) => !isUndefined(context5) && context5 !== _global, extend2 = (a, b, thisArg, { allOwnKeys } = {}) => {
53788
+ }, _global, isContextDefined = (context5) => !isUndefined(context5) && context5 !== _global, extend3 = (a, b, thisArg, { allOwnKeys } = {}) => {
53503
53789
  forEach6(b, (val, key) => {
53504
53790
  if (thisArg && isFunction3(val)) {
53505
53791
  Object.defineProperty(a, key, {
@@ -53753,7 +54039,7 @@ var init_utils = __esm(() => {
53753
54039
  isFileList,
53754
54040
  forEach: forEach6,
53755
54041
  merge: merge9,
53756
- extend: extend2,
54042
+ extend: extend3,
53757
54043
  trim,
53758
54044
  stripBOM,
53759
54045
  inherits,
@@ -65184,7 +65470,7 @@ var require_common = __commonJS((exports, module) => {
65184
65470
  debug.namespace = namespace;
65185
65471
  debug.useColors = createDebug.useColors();
65186
65472
  debug.color = createDebug.selectColor(namespace);
65187
- debug.extend = extend3;
65473
+ debug.extend = extend4;
65188
65474
  debug.destroy = createDebug.destroy;
65189
65475
  Object.defineProperty(debug, "enabled", {
65190
65476
  enumerable: true,
@@ -65208,7 +65494,7 @@ var require_common = __commonJS((exports, module) => {
65208
65494
  }
65209
65495
  return debug;
65210
65496
  }
65211
- function extend3(namespace, delimiter) {
65497
+ function extend4(namespace, delimiter) {
65212
65498
  const newDebug = createDebug(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace);
65213
65499
  newDebug.log = this.log;
65214
65500
  return newDebug;
@@ -82834,15 +83120,10 @@ var init_on_daemon_shutdown = __esm(() => {
82834
83120
  const activeAgents = agentPm.listActive();
82835
83121
  if (activeAgents.length > 0) {
82836
83122
  console.log(`[${formatTimestamp()}] Stopping ${activeAgents.length} agent(s)...`);
82837
- yield* exports_Effect.promise(() => Promise.allSettled(activeAgents.map(async ({ chatroomId, role, slot }) => {
83123
+ yield* exports_Effect.all(activeAgents.map(({ chatroomId, role, slot }) => {
82838
83124
  const pid = slot.pid;
82839
- try {
82840
- await exports_Effect.runPromise(agentPm.stop({ chatroomId, role, reason: "daemon.shutdown" }));
82841
- console.log(` Stopped ${role} (PID ${pid})`);
82842
- } catch (e) {
82843
- console.log(` ⚠️ Failed to stop ${role}: ${e.message}`);
82844
- }
82845
- })));
83125
+ return agentPm.stop({ chatroomId, role, reason: "daemon.shutdown" }).pipe(exports_Effect.tap(() => exports_Effect.sync(() => console.log(` Stopped ${role} (PID ${pid})`))), exports_Effect.catchAll((e) => exports_Effect.sync(() => console.log(` ⚠️ Failed to stop ${role}: ${e.message}`))));
83126
+ }), { concurrency: "unbounded" });
82846
83127
  console.log(`[${formatTimestamp()}] All agents stopped`);
82847
83128
  }
82848
83129
  yield* exports_Effect.promise(() => session2.backend.mutation(api.machines.updateDaemonStatus, {
@@ -84924,36 +85205,45 @@ async function pushSingleWorkspaceGitStateImpl(ctx, workingDir) {
84924
85205
  const stateKey = makeGitStateKey(ctx.machineId, workingDir);
84925
85206
  const isRepo = await isGitRepo(workingDir);
84926
85207
  if (!isRepo) {
84927
- const stateHash2 = "not_found";
84928
- if (ctx.lastPushedGitState.get(stateKey) === stateHash2)
84929
- return;
84930
- await ctx.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
84931
- sessionId: ctx.sessionId,
84932
- machineId: ctx.machineId,
84933
- workingDir,
84934
- status: "not_found"
84935
- });
84936
- ctx.lastPushedGitState.set(stateKey, stateHash2);
85208
+ await pushNotFoundGitState(ctx, workingDir, stateKey);
84937
85209
  return;
84938
85210
  }
84939
85211
  const branchResult = await getBranch(workingDir);
84940
85212
  if (branchResult.status === "error") {
84941
- const stateHash2 = `error:${branchResult.message}`;
84942
- if (ctx.lastPushedGitState.get(stateKey) === stateHash2)
84943
- return;
84944
- await ctx.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
84945
- sessionId: ctx.sessionId,
84946
- machineId: ctx.machineId,
84947
- workingDir,
84948
- status: "error",
84949
- errorMessage: branchResult.message
84950
- });
84951
- ctx.lastPushedGitState.set(stateKey, stateHash2);
85213
+ await pushErrorGitState(ctx, workingDir, stateKey, branchResult.message);
84952
85214
  return;
84953
85215
  }
84954
85216
  if (branchResult.status === "not_found") {
84955
85217
  return;
84956
85218
  }
85219
+ await pushAvailableGitState(ctx, workingDir, stateKey, branchResult);
85220
+ }
85221
+ async function pushNotFoundGitState(ctx, workingDir, stateKey) {
85222
+ const stateHash = "not_found";
85223
+ if (ctx.lastPushedGitState.get(stateKey) === stateHash)
85224
+ return;
85225
+ await ctx.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
85226
+ sessionId: ctx.sessionId,
85227
+ machineId: ctx.machineId,
85228
+ workingDir,
85229
+ status: "not_found"
85230
+ });
85231
+ ctx.lastPushedGitState.set(stateKey, stateHash);
85232
+ }
85233
+ async function pushErrorGitState(ctx, workingDir, stateKey, message) {
85234
+ const stateHash = `error:${message}`;
85235
+ if (ctx.lastPushedGitState.get(stateKey) === stateHash)
85236
+ return;
85237
+ await ctx.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
85238
+ sessionId: ctx.sessionId,
85239
+ machineId: ctx.machineId,
85240
+ workingDir,
85241
+ status: "error",
85242
+ errorMessage: message
85243
+ });
85244
+ ctx.lastPushedGitState.set(stateKey, stateHash);
85245
+ }
85246
+ async function pushAvailableGitState(ctx, workingDir, stateKey, branchResult) {
84957
85247
  const branch = branchResult.branch;
84958
85248
  const allFields = [branchField, ...GIT_STATE_FIELDS, ...makeBranchDependentFields(branch)];
84959
85249
  const pipeline2 = new GitStatePipeline(allFields);
@@ -84990,16 +85280,12 @@ async function pushSingleWorkspaceGitStateImpl(ctx, workingDir) {
84990
85280
  }
84991
85281
  }
84992
85282
  }
84993
- var lastFullPushMs, branchField, GIT_STATE_FIELDS, pushSingleWorkspaceGitSummaryForObservedEffect = (workingDir, reason = "safety-poll") => exports_Effect.gen(function* () {
84994
- const session2 = yield* DaemonSessionService;
84995
- const mutable = yield* DaemonMutableStateService;
84996
- const lastPushedGitState = yield* exports_Ref.get(mutable.lastPushedGitState);
84997
- const stateKey = makeGitStateKey(session2.machineId, workingDir);
84998
- const isRepo = yield* exports_Effect.promise(() => isGitRepo(workingDir));
84999
- if (!isRepo) {
85283
+ function pushObservedNotRepoEffect(session2, lastPushedGitState, stateKey, workingDir, reason) {
85284
+ return exports_Effect.gen(function* () {
85000
85285
  const stateHash = "not_found";
85001
- if (reason !== "refresh" && lastPushedGitState.get(stateKey) === stateHash)
85286
+ if (reason !== "refresh" && lastPushedGitState.get(stateKey) === stateHash) {
85002
85287
  return;
85288
+ }
85003
85289
  yield* exports_Effect.promise(() => session2.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
85004
85290
  sessionId: session2.sessionId,
85005
85291
  machineId: session2.machineId,
@@ -85007,21 +85293,69 @@ var lastFullPushMs, branchField, GIT_STATE_FIELDS, pushSingleWorkspaceGitSummary
85007
85293
  status: "not_found"
85008
85294
  }));
85009
85295
  lastPushedGitState.set(stateKey, stateHash);
85010
- return;
85011
- }
85012
- const branchResult = yield* exports_Effect.promise(() => getBranch(workingDir));
85013
- if (branchResult.status === "error") {
85014
- const stateHash = `error:${branchResult.message}`;
85015
- if (reason !== "refresh" && lastPushedGitState.get(stateKey) === stateHash)
85296
+ });
85297
+ }
85298
+ function pushObservedBranchErrorEffect(session2, lastPushedGitState, stateKey, workingDir, reason, message) {
85299
+ return exports_Effect.gen(function* () {
85300
+ const stateHash = `error:${message}`;
85301
+ if (reason !== "refresh" && lastPushedGitState.get(stateKey) === stateHash) {
85016
85302
  return;
85303
+ }
85017
85304
  yield* exports_Effect.promise(() => session2.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
85018
85305
  sessionId: session2.sessionId,
85019
85306
  machineId: session2.machineId,
85020
85307
  workingDir,
85021
85308
  status: "error",
85022
- errorMessage: branchResult.message
85309
+ errorMessage: message
85023
85310
  }));
85024
85311
  lastPushedGitState.set(stateKey, stateHash);
85312
+ });
85313
+ }
85314
+ function pushObservedFullGitStateEffect(session2, lastPushedGitState, stateKey, workingDir, branch, reason) {
85315
+ return exports_Effect.gen(function* () {
85316
+ yield* exports_Effect.promise(() => pushSingleWorkspaceGitStateImpl(buildGitStateDeps(session2, lastPushedGitState), workingDir));
85317
+ lastFullPushMs.set(stateKey, Date.now());
85318
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed full git state pushed: ${workingDir} (${branch})${reason === "refresh" ? " [refresh]" : ""}`);
85319
+ });
85320
+ }
85321
+ function pushObservedSlimGitSummaryEffect(session2, lastPushedGitState, stateKey, workingDir, branch, branchResult, reason) {
85322
+ return exports_Effect.gen(function* () {
85323
+ const slimFields = [
85324
+ branchField,
85325
+ ...GIT_STATE_FIELDS.filter((f) => f.includeInSlim),
85326
+ ...makeBranchDependentFields(branch)
85327
+ ];
85328
+ const pipeline2 = new GitStatePipeline(slimFields);
85329
+ const preCollected = new Map([["branch", branchResult]]);
85330
+ const values3 = yield* exports_Effect.promise(() => pipeline2.collect(workingDir, preCollected));
85331
+ const hash2 = pipeline2.computeHash(values3, true);
85332
+ if (reason !== "refresh" && lastPushedGitState.get(stateKey) === hash2) {
85333
+ return;
85334
+ }
85335
+ yield* exports_Effect.promise(() => session2.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
85336
+ sessionId: session2.sessionId,
85337
+ machineId: session2.machineId,
85338
+ workingDir,
85339
+ status: "available",
85340
+ ...pipeline2.toMutationArgs(values3, true)
85341
+ }));
85342
+ lastPushedGitState.set(stateKey, hash2);
85343
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed git summary pushed: ${workingDir} (${branch}${values3.get("isDirty") ? ", dirty" : ", clean"})${reason === "refresh" ? " [refresh]" : ""}`);
85344
+ });
85345
+ }
85346
+ var lastFullPushMs, branchField, GIT_STATE_FIELDS, pushSingleWorkspaceGitSummaryForObservedEffect = (workingDir, reason = "safety-poll") => exports_Effect.gen(function* pushObservedGitSummaryForObserved() {
85347
+ const session2 = yield* DaemonSessionService;
85348
+ const mutable = yield* DaemonMutableStateService;
85349
+ const lastPushedGitState = yield* exports_Ref.get(mutable.lastPushedGitState);
85350
+ const stateKey = makeGitStateKey(session2.machineId, workingDir);
85351
+ const isRepo = yield* exports_Effect.promise(() => isGitRepo(workingDir));
85352
+ if (!isRepo) {
85353
+ yield* pushObservedNotRepoEffect(session2, lastPushedGitState, stateKey, workingDir, reason);
85354
+ return;
85355
+ }
85356
+ const branchResult = yield* exports_Effect.promise(() => getBranch(workingDir));
85357
+ if (branchResult.status === "error") {
85358
+ yield* pushObservedBranchErrorEffect(session2, lastPushedGitState, stateKey, workingDir, reason, branchResult.message);
85025
85359
  return;
85026
85360
  }
85027
85361
  if (branchResult.status === "not_found") {
@@ -85031,32 +85365,10 @@ var lastFullPushMs, branchField, GIT_STATE_FIELDS, pushSingleWorkspaceGitSummary
85031
85365
  const now = Date.now();
85032
85366
  const lastFull = lastFullPushMs.get(stateKey) ?? 0;
85033
85367
  if (now - lastFull >= OBSERVED_FULL_PUSH_INTERVAL_MS) {
85034
- yield* exports_Effect.promise(() => pushSingleWorkspaceGitStateImpl(buildGitStateDeps(session2, lastPushedGitState), workingDir));
85035
- lastFullPushMs.set(stateKey, now);
85036
- console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed full git state pushed: ${workingDir} (${branch})${reason === "refresh" ? " [refresh]" : ""}`);
85037
- return;
85038
- }
85039
- const slimFields = [
85040
- branchField,
85041
- ...GIT_STATE_FIELDS.filter((f) => f.includeInSlim),
85042
- ...makeBranchDependentFields(branch)
85043
- ];
85044
- const pipeline2 = new GitStatePipeline(slimFields);
85045
- const preCollected = new Map([["branch", branchResult]]);
85046
- const values3 = yield* exports_Effect.promise(() => pipeline2.collect(workingDir, preCollected));
85047
- const hash2 = pipeline2.computeHash(values3, true);
85048
- if (reason !== "refresh" && lastPushedGitState.get(stateKey) === hash2) {
85368
+ yield* pushObservedFullGitStateEffect(session2, lastPushedGitState, stateKey, workingDir, branch, reason);
85049
85369
  return;
85050
85370
  }
85051
- yield* exports_Effect.promise(() => session2.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
85052
- sessionId: session2.sessionId,
85053
- machineId: session2.machineId,
85054
- workingDir,
85055
- status: "available",
85056
- ...pipeline2.toMutationArgs(values3, true)
85057
- }));
85058
- lastPushedGitState.set(stateKey, hash2);
85059
- console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed git summary pushed: ${workingDir} (${branch}${values3.get("isDirty") ? ", dirty" : ", clean"})${reason === "refresh" ? " [refresh]" : ""}`);
85371
+ yield* pushObservedSlimGitSummaryEffect(session2, lastPushedGitState, stateKey, workingDir, branch, branchResult, reason);
85060
85372
  }), pushGitStateEffect, pushSingleWorkspaceGitStateEffect = (workingDir) => exports_Effect.gen(function* () {
85061
85373
  const session2 = yield* DaemonSessionService;
85062
85374
  const mutable = yield* DaemonMutableStateService;
@@ -85272,14 +85584,12 @@ async function processPRAction(deps, req) {
85272
85584
  timeout: EXEC_TIMEOUT_MS
85273
85585
  });
85274
85586
  console.log(`[${formatTimestamp()}] ✅ PR action: ${action} on #${prNumber}${result.stdout ? ` — ${result.stdout.trim()}` : ""}`);
85275
- await exports_Effect.runPromise(pushGitStateEffect.pipe(exports_Effect.provide(exports_Layer.mergeAll(exports_Layer.succeed(DaemonSessionService, deps), DaemonMutableStateServiceLive({
85587
+ exports_Runtime.runFork(deps.runtime)(pushGitStateEffect.pipe(exports_Effect.provide(exports_Layer.mergeAll(exports_Layer.succeed(DaemonSessionService, deps), DaemonMutableStateServiceLive({
85276
85588
  lastPushedGitState: deps.lastPushedGitState,
85277
85589
  lastPushedModels: null,
85278
85590
  lastPushedHarnessFingerprint: null,
85279
85591
  workspaceListStore: deps.workspaceListStore
85280
- }))))).catch((err) => {
85281
- console.warn(`[${formatTimestamp()}] ⚠️ Failed to refresh git state after PR action: ${getErrorMessage(err)}`);
85282
- });
85592
+ }))), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[${formatTimestamp()}] ⚠️ Failed to refresh git state after PR action: ${getErrorMessage(err)}`)))));
85283
85593
  }
85284
85594
  async function processPRCommits(deps, req) {
85285
85595
  const prNumber = req.prNumber;
@@ -85304,32 +85614,35 @@ async function processCommitDetail(deps, req) {
85304
85614
  getCommitDetail(req.workingDir, req.sha),
85305
85615
  getCommitMetadata(req.workingDir, req.sha)
85306
85616
  ]);
85617
+ await upsertCommitDetailResult(deps, req, result, metadata);
85618
+ if (result.status === "available" || result.status === "truncated") {
85619
+ const compressed = gzipSync2(Buffer.from(result.content));
85620
+ console.log(`[${formatTimestamp()}] \uD83D\uDD0D Commit detail pushed: ${req.sha.slice(0, 7)} in ${req.workingDir} (${(Buffer.byteLength(result.content) / 1024).toFixed(1)}KB → ${(compressed.length / 1024).toFixed(1)}KB gzip)`);
85621
+ }
85622
+ }
85623
+ async function upsertCommitDetailResult(deps, req, result, metadata) {
85624
+ const baseArgs = {
85625
+ sessionId: deps.sessionId,
85626
+ machineId: deps.machineId,
85627
+ workingDir: req.workingDir,
85628
+ sha: req.sha,
85629
+ message: metadata?.message,
85630
+ body: metadata?.body,
85631
+ author: metadata?.author,
85632
+ date: metadata?.date
85633
+ };
85307
85634
  if (result.status === "not_found") {
85308
85635
  await deps.backend.mutation(api.workspaces.upsertCommitDetailV2, {
85309
- sessionId: deps.sessionId,
85310
- machineId: deps.machineId,
85311
- workingDir: req.workingDir,
85312
- sha: req.sha,
85313
- status: "not_found",
85314
- message: metadata?.message,
85315
- body: metadata?.body,
85316
- author: metadata?.author,
85317
- date: metadata?.date
85636
+ ...baseArgs,
85637
+ status: "not_found"
85318
85638
  });
85319
85639
  return;
85320
85640
  }
85321
85641
  if (result.status === "error") {
85322
85642
  await deps.backend.mutation(api.workspaces.upsertCommitDetailV2, {
85323
- sessionId: deps.sessionId,
85324
- machineId: deps.machineId,
85325
- workingDir: req.workingDir,
85326
- sha: req.sha,
85643
+ ...baseArgs,
85327
85644
  status: "error",
85328
- errorMessage: result.message,
85329
- message: metadata?.message,
85330
- body: metadata?.body,
85331
- author: metadata?.author,
85332
- date: metadata?.date
85645
+ errorMessage: result.message
85333
85646
  });
85334
85647
  return;
85335
85648
  }
@@ -85337,20 +85650,12 @@ async function processCommitDetail(deps, req) {
85337
85650
  const compressed = gzipSync2(Buffer.from(result.content));
85338
85651
  const diffContentCompressed = compressed.toString("base64");
85339
85652
  await deps.backend.mutation(api.workspaces.upsertCommitDetailV2, {
85340
- sessionId: deps.sessionId,
85341
- machineId: deps.machineId,
85342
- workingDir: req.workingDir,
85343
- sha: req.sha,
85653
+ ...baseArgs,
85344
85654
  status: "available",
85345
85655
  data: { compression: "gzip", content: diffContentCompressed },
85346
85656
  truncated: result.truncated,
85347
- message: metadata?.message,
85348
- body: metadata?.body,
85349
- author: metadata?.author,
85350
- date: metadata?.date,
85351
85657
  diffStat
85352
85658
  });
85353
- console.log(`[${formatTimestamp()}] \uD83D\uDD0D Commit detail pushed: ${req.sha.slice(0, 7)} in ${req.workingDir} (${(Buffer.byteLength(result.content) / 1024).toFixed(1)}KB → ${(compressed.length / 1024).toFixed(1)}KB gzip)`);
85354
85659
  }
85355
85660
  async function processMoreCommits(deps, req) {
85356
85661
  const offset = req.offset ?? 0;
@@ -85387,11 +85692,33 @@ async function processRecentCommits(deps, req) {
85387
85692
  });
85388
85693
  console.log(`[${formatTimestamp()}] \uD83D\uDCDC Recent commits pushed: ${req.workingDir} (${commits.length} commits)`);
85389
85694
  }
85695
+ function dispatchGitRequest(deps, req) {
85696
+ switch (req.requestType) {
85697
+ case "full_diff":
85698
+ return exports_Effect.promise(() => processFullDiff(deps, req));
85699
+ case "commit_detail":
85700
+ return exports_Effect.promise(() => processCommitDetail(deps, req));
85701
+ case "more_commits":
85702
+ return exports_Effect.promise(() => processMoreCommits(deps, req));
85703
+ case "pr_diff":
85704
+ return exports_Effect.promise(() => processPRDiff(deps, req));
85705
+ case "pr_action":
85706
+ return exports_Effect.promise(() => processPRAction(deps, req));
85707
+ case "pr_commits":
85708
+ return exports_Effect.promise(() => processPRCommits(deps, req));
85709
+ case "all_pull_requests":
85710
+ return exports_Effect.promise(() => processAllPullRequests(deps, req));
85711
+ case "recent_commits":
85712
+ return exports_Effect.promise(() => processRecentCommits(deps, req));
85713
+ }
85714
+ }
85390
85715
  var EXEC_TIMEOUT_MS = 60000, startGitRequestSubscriptionEffect = (wsClient2) => exports_Effect.gen(function* () {
85391
85716
  const session2 = yield* DaemonSessionService;
85717
+ const runtime4 = yield* exports_Effect.runtime();
85392
85718
  const processedRequestIds = new Map;
85393
85719
  const DEDUP_TTL_MS = 5 * 60 * 1000;
85394
- let processing = false;
85720
+ const sessionWithRuntime = { ...session2, runtime: runtime4 };
85721
+ const processingState = { isProcessing: false };
85395
85722
  session2.backend.mutation(api.workspaces.resetProcessingRequests, {
85396
85723
  sessionId: session2.sessionId,
85397
85724
  machineId: session2.machineId
@@ -85408,16 +85735,14 @@ var EXEC_TIMEOUT_MS = 60000, startGitRequestSubscriptionEffect = (wsClient2) =>
85408
85735
  }, (requests) => {
85409
85736
  if (!requests || requests.length === 0)
85410
85737
  return;
85411
- const logger = session2.logger ?? console;
85738
+ const logger = sessionWithRuntime.logger ?? console;
85412
85739
  logger.log(`[${formatTimestamp()}] \uD83D\uDCEC Git subscription: received ${requests.length} pending request(s)`);
85413
- if (processing)
85740
+ if (processingState.isProcessing)
85414
85741
  return;
85415
- processing = true;
85416
- exports_Effect.runPromise(processRequestsEffect(requests, processedRequestIds, DEDUP_TTL_MS).pipe(exports_Effect.provideService(DaemonSessionService, session2))).catch((err) => {
85417
- console.warn(`[${formatTimestamp()}] ⚠️ Git request processing failed: ${getErrorMessage(err)}`);
85418
- }).finally(() => {
85419
- processing = false;
85420
- });
85742
+ processingState.isProcessing = true;
85743
+ exports_Runtime.runFork(runtime4)(processRequestsEffect(requests, processedRequestIds, DEDUP_TTL_MS, runtime4).pipe(exports_Effect.provideService(DaemonSessionService, sessionWithRuntime), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[${formatTimestamp()}] ⚠️ Git request processing failed: ${getErrorMessage(err)}`))), exports_Effect.ensuring(exports_Effect.sync(() => {
85744
+ processingState.isProcessing = false;
85745
+ }))));
85421
85746
  }, (err) => {
85422
85747
  console.warn(`[${formatTimestamp()}] ⚠️ Git request subscription error: ${getErrorMessage(err)}`);
85423
85748
  });
@@ -85428,7 +85753,7 @@ var EXEC_TIMEOUT_MS = 60000, startGitRequestSubscriptionEffect = (wsClient2) =>
85428
85753
  console.log(`[${formatTimestamp()}] \uD83D\uDD00 Git request subscription stopped`);
85429
85754
  }
85430
85755
  };
85431
- }), processRequestsEffect = (requests, processedRequestIds, dedupTtlMs) => exports_Effect.gen(function* () {
85756
+ }), processRequestsEffect = (requests, processedRequestIds, dedupTtlMs, runtime4) => exports_Effect.gen(function* () {
85432
85757
  const session2 = yield* DaemonSessionService;
85433
85758
  const evictBefore = Date.now() - dedupTtlMs;
85434
85759
  for (const [id3, ts] of processedRequestIds) {
@@ -85448,32 +85773,8 @@ var EXEC_TIMEOUT_MS = 60000, startGitRequestSubscriptionEffect = (wsClient2) =>
85448
85773
  }));
85449
85774
  const logger = session2.logger ?? console;
85450
85775
  logger.log(`[${formatTimestamp()}] ⚙️ Processing git request: type=${req.requestType}, id=${requestId}`);
85451
- switch (req.requestType) {
85452
- case "full_diff":
85453
- yield* exports_Effect.promise(() => processFullDiff(session2, req));
85454
- break;
85455
- case "commit_detail":
85456
- yield* exports_Effect.promise(() => processCommitDetail(session2, req));
85457
- break;
85458
- case "more_commits":
85459
- yield* exports_Effect.promise(() => processMoreCommits(session2, req));
85460
- break;
85461
- case "pr_diff":
85462
- yield* exports_Effect.promise(() => processPRDiff(session2, req));
85463
- break;
85464
- case "pr_action":
85465
- yield* exports_Effect.promise(() => processPRAction(session2, req));
85466
- break;
85467
- case "pr_commits":
85468
- yield* exports_Effect.promise(() => processPRCommits(session2, req));
85469
- break;
85470
- case "all_pull_requests":
85471
- yield* exports_Effect.promise(() => processAllPullRequests(session2, req));
85472
- break;
85473
- case "recent_commits":
85474
- yield* exports_Effect.promise(() => processRecentCommits(session2, req));
85475
- break;
85476
- }
85776
+ const sessionWithRuntime = { ...session2, runtime: runtime4 };
85777
+ yield* dispatchGitRequest(sessionWithRuntime, req);
85477
85778
  yield* exports_Effect.promise(() => session2.backend.mutation(api.workspaces.updateRequestStatus, {
85478
85779
  sessionId: session2.sessionId,
85479
85780
  requestId: req._id,
@@ -85653,6 +85954,26 @@ function startCommandSubscriber(session2, wsClient2, deps) {
85653
85954
  });
85654
85955
  return { stop: unsub };
85655
85956
  }
85957
+ async function dispatchPendingCommand(session2, deps, cmd) {
85958
+ switch (cmd.type) {
85959
+ case "refreshCapabilities":
85960
+ await handleRefreshCapabilities(session2, deps, cmd);
85961
+ break;
85962
+ case "refreshSessionTitle":
85963
+ await handleRefreshSessionTitle(session2, deps, cmd);
85964
+ break;
85965
+ default:
85966
+ await markFailed(session2, cmd._id, `Unknown command type: ${cmd.type}`);
85967
+ }
85968
+ }
85969
+ async function processPendingCommand(session2, deps, cmd, now) {
85970
+ if (now - cmd.createdAt > DIRECT_HARNESS_COMMAND_TTL_MS) {
85971
+ console.log(`[direct-harness] Discarding stale command ${cmd._id} (type=${cmd.type}, age=${now - cmd.createdAt}ms)`);
85972
+ await markFailed(session2, cmd._id, "Command expired (TTL)");
85973
+ return;
85974
+ }
85975
+ await dispatchPendingCommand(session2, deps, cmd);
85976
+ }
85656
85977
  async function drain(session2, deps, processed) {
85657
85978
  const pending3 = await session2.backend.query(api.daemon.directHarness.commands.listPendingCommands, { sessionId: session2.sessionId, machineId: session2.machineId });
85658
85979
  if (!pending3 || pending3.length === 0)
@@ -85663,21 +85984,7 @@ async function drain(session2, deps, processed) {
85663
85984
  continue;
85664
85985
  processed.add(cmd._id);
85665
85986
  try {
85666
- if (now - cmd.createdAt > DIRECT_HARNESS_COMMAND_TTL_MS) {
85667
- console.log(`[direct-harness] Discarding stale command ${cmd._id} (type=${cmd.type}, age=${now - cmd.createdAt}ms)`);
85668
- await markFailed(session2, cmd._id, "Command expired (TTL)");
85669
- continue;
85670
- }
85671
- switch (cmd.type) {
85672
- case "refreshCapabilities":
85673
- await handleRefreshCapabilities(session2, deps, cmd);
85674
- break;
85675
- case "refreshSessionTitle":
85676
- await handleRefreshSessionTitle(session2, deps, cmd);
85677
- break;
85678
- default:
85679
- await markFailed(session2, cmd._id, `Unknown command type: ${cmd.type}`);
85680
- }
85987
+ await processPendingCommand(session2, deps, cmd, now);
85681
85988
  } catch (err) {
85682
85989
  const message = err instanceof Error ? err.message : String(err);
85683
85990
  console.warn(`[direct-harness] Command ${cmd._id} failed: ${message}`);
@@ -86500,74 +86807,73 @@ async function drain2(session2, deps) {
86500
86807
  }
86501
86808
  }
86502
86809
  }
86503
- async function processSessionMessages(session2, deps, rowId, messages, info) {
86504
- let handle = deps.activeSessions.get(rowId);
86505
- if (!handle) {
86506
- const opencodeSessionId = info?.opencodeSessionId;
86507
- if (!opencodeSessionId) {
86508
- console.warn(`[direct-harness] Session ${rowId} not yet open — waiting for session-subscriber`);
86509
- return;
86510
- }
86511
- const workspaceId = info?.workspaceId;
86512
- if (!workspaceId) {
86513
- console.warn(`[direct-harness] Cannot resume session ${rowId}: no workspace info`);
86514
- return;
86515
- }
86516
- let harness = deps.harnesses.get(workspaceId);
86517
- if (harness && !harness.isAlive()) {
86518
- harness.close().catch(() => {});
86519
- deps.harnesses.delete(workspaceId);
86520
- harness = undefined;
86810
+ async function resumeSessionHandle(session2, deps, rowId, info) {
86811
+ const opencodeSessionId = info.opencodeSessionId;
86812
+ if (!opencodeSessionId) {
86813
+ console.warn(`[direct-harness] Session ${rowId} not yet open — waiting for session-subscriber`);
86814
+ return null;
86815
+ }
86816
+ const workspaceId = info.workspaceId;
86817
+ if (!workspaceId) {
86818
+ console.warn(`[direct-harness] Cannot resume session ${rowId}: no workspace info`);
86819
+ return null;
86820
+ }
86821
+ let harness = deps.harnesses.get(workspaceId);
86822
+ if (harness && !harness.isAlive()) {
86823
+ harness.close().catch(() => {});
86824
+ deps.harnesses.delete(workspaceId);
86825
+ harness = undefined;
86826
+ }
86827
+ if (!harness) {
86828
+ const workspace = await session2.backend.query(api.workspaces.getWorkspaceById, {
86829
+ sessionId: session2.sessionId,
86830
+ workspaceId
86831
+ });
86832
+ if (!workspace) {
86833
+ console.warn(`[direct-harness] Cannot resume session ${rowId}: workspace not found`);
86834
+ return null;
86521
86835
  }
86522
- if (!harness) {
86523
- const workspace = await session2.backend.query(api.workspaces.getWorkspaceById, {
86524
- sessionId: session2.sessionId,
86525
- workspaceId
86526
- });
86527
- if (!workspace) {
86528
- console.warn(`[direct-harness] Cannot resume session ${rowId}: workspace not found`);
86529
- return;
86836
+ harness = await startOpencodeSdkHarness({
86837
+ type: "opencode",
86838
+ workingDir: workspace.workingDir,
86839
+ workspaceId
86840
+ });
86841
+ deps.harnesses.set(workspaceId, harness);
86842
+ }
86843
+ try {
86844
+ return await resumeSession({
86845
+ harness,
86846
+ journalFactory: deps.journalFactory,
86847
+ chunkExtractor: createOpencodeSdkChunkExtractor()
86848
+ }, { harnessSessionId: rowId, opencodeSessionId, workspaceId });
86849
+ } catch (err) {
86850
+ const message = err instanceof Error ? err.message : String(err);
86851
+ console.warn(`[direct-harness] Cannot resume session ${rowId}: ${message}`);
86852
+ await deps.sessionRepository.markFailed(rowId).catch(() => {});
86853
+ return null;
86854
+ }
86855
+ }
86856
+ function wireResumedSessionEvents(handle, deps, info) {
86857
+ const idleConfig = {
86858
+ agent: info.lastUsedConfig.agent ?? "build",
86859
+ model: info.lastUsedConfig.model
86860
+ };
86861
+ let lastBoundKey = null;
86862
+ handle.session.onEvent((event) => {
86863
+ const turn = handle.currentTurn;
86864
+ if (turn?.messageId !== null && turn?.messageId !== undefined) {
86865
+ const key = `${turn.turnId}:${turn.messageId}`;
86866
+ if (key !== lastBoundKey) {
86867
+ lastBoundKey = key;
86868
+ deps.sessionRepository.bindTurnMessageId(turn.turnId, turn.messageId).catch((err) => console.warn("[direct-harness] bindTurnMessageId error (resume):", err));
86530
86869
  }
86531
- harness = await startOpencodeSdkHarness({
86532
- type: "opencode",
86533
- workingDir: workspace.workingDir,
86534
- workspaceId
86535
- });
86536
- deps.harnesses.set(workspaceId, harness);
86537
86870
  }
86538
- try {
86539
- handle = await resumeSession({
86540
- harness,
86541
- journalFactory: deps.journalFactory,
86542
- chunkExtractor: createOpencodeSdkChunkExtractor()
86543
- }, { harnessSessionId: rowId, opencodeSessionId, workspaceId });
86544
- } catch (err) {
86545
- const message = err instanceof Error ? err.message : String(err);
86546
- console.warn(`[direct-harness] Cannot resume session ${rowId}: ${message}`);
86547
- await deps.sessionRepository.markFailed(rowId).catch(() => {});
86548
- return;
86871
+ if (event.type === "session.idle") {
86872
+ handleSessionIdle(handle, handle.journal, idleConfig, deps.sessionRepository).catch((err) => console.warn("[direct-harness] idle handler error (resume):", err));
86549
86873
  }
86550
- deps.activeSessions.set(rowId, handle);
86551
- await deps.sessionRepository.markActive(rowId).catch(() => {});
86552
- const idleConfig = {
86553
- agent: info?.lastUsedConfig.agent ?? "build",
86554
- model: info?.lastUsedConfig.model
86555
- };
86556
- let lastBoundKey = null;
86557
- handle.session.onEvent((event) => {
86558
- const turn = handle.currentTurn;
86559
- if (turn?.messageId !== null && turn?.messageId !== undefined) {
86560
- const key = `${turn.turnId}:${turn.messageId}`;
86561
- if (key !== lastBoundKey) {
86562
- lastBoundKey = key;
86563
- deps.sessionRepository.bindTurnMessageId(turn.turnId, turn.messageId).catch((err) => console.warn("[direct-harness] bindTurnMessageId error (resume):", err));
86564
- }
86565
- }
86566
- if (event.type === "session.idle") {
86567
- handleSessionIdle(handle, handle.journal, idleConfig, deps.sessionRepository).catch((err) => console.warn("[direct-harness] idle handler error (resume):", err));
86568
- }
86569
- });
86570
- }
86874
+ });
86875
+ }
86876
+ async function deliverPendingMessages(handle, deps, rowId, messages, info) {
86571
86877
  for (const msg of messages) {
86572
86878
  const override = info?.lastUsedConfig ?? { agent: "build" };
86573
86879
  try {
@@ -86591,6 +86897,24 @@ async function processSessionMessages(session2, deps, rowId, messages, info) {
86591
86897
  }
86592
86898
  }
86593
86899
  }
86900
+ async function processSessionMessages(session2, deps, rowId, messages, info) {
86901
+ let handle = deps.activeSessions.get(rowId);
86902
+ if (!handle) {
86903
+ if (!info) {
86904
+ console.warn(`[direct-harness] Session ${rowId} not yet open — waiting for session-subscriber`);
86905
+ return;
86906
+ }
86907
+ const resumed = await resumeSessionHandle(session2, deps, rowId, info);
86908
+ if (!resumed) {
86909
+ return;
86910
+ }
86911
+ handle = resumed;
86912
+ deps.activeSessions.set(rowId, handle);
86913
+ await deps.sessionRepository.markActive(rowId).catch(() => {});
86914
+ wireResumedSessionEvents(handle, deps, info);
86915
+ }
86916
+ await deliverPendingMessages(handle, deps, rowId, messages, info);
86917
+ }
86594
86918
  var init_prompt_subscriber = __esm(() => {
86595
86919
  init_idle_handler();
86596
86920
  init_api3();
@@ -86619,6 +86943,60 @@ function startSessionSubscriber(daemonSession, wsClient2, deps) {
86619
86943
  });
86620
86944
  return { stop: unsub };
86621
86945
  }
86946
+ async function getOrCreateHarness(daemonSession, deps, session2, workspace) {
86947
+ let harness = deps.harnesses.get(session2.workspaceId);
86948
+ if (harness && !harness.isAlive()) {
86949
+ console.warn(`[direct-harness] Harness for workspace ${session2.workspaceId} is no longer alive — restarting`);
86950
+ harness.close().catch(() => {});
86951
+ deps.harnesses.delete(session2.workspaceId);
86952
+ harness = undefined;
86953
+ }
86954
+ if (!harness) {
86955
+ harness = await startOpencodeSdkHarness({
86956
+ type: "opencode",
86957
+ workingDir: workspace.workingDir,
86958
+ workspaceId: session2.workspaceId
86959
+ });
86960
+ deps.harnesses.set(session2.workspaceId, harness);
86961
+ }
86962
+ return harness;
86963
+ }
86964
+ function recordLiveSessionChunk(event, handle, journal, extractChunk, deps) {
86965
+ const chunk2 = extractChunk(event);
86966
+ if (chunk2 === null) {
86967
+ return;
86968
+ }
86969
+ journal.record({
86970
+ content: chunk2.content,
86971
+ timestamp: Date.now(),
86972
+ messageId: chunk2.messageId,
86973
+ partType: chunk2.partType
86974
+ });
86975
+ if (handle.currentTurn && handle.currentTurn.messageId === null) {
86976
+ handle.currentTurn.messageId = chunk2.messageId;
86977
+ deps.sessionRepository.bindTurnMessageId(handle.currentTurn.turnId, chunk2.messageId).catch((err) => console.warn("[direct-harness] bindTurnMessageId error:", err));
86978
+ }
86979
+ }
86980
+ function handleLiveSessionTitleUpdate(event, deps, rowId, liveSession) {
86981
+ if (event.type !== "session.updated") {
86982
+ return;
86983
+ }
86984
+ const info = event.payload.info;
86985
+ const newTitle = info?.title;
86986
+ if (!newTitle || newTitle === liveSession.sessionTitle) {
86987
+ return;
86988
+ }
86989
+ liveSession.setTitle?.(newTitle);
86990
+ deps.sessionRepository.updateSessionTitle(rowId, newTitle).catch((err) => console.warn("[direct-harness] updateSessionTitle error:", err));
86991
+ }
86992
+ function handleLiveSessionEvent(event, ctx) {
86993
+ const { handle, journal, extractChunk, idleConfig, deps, rowId, liveSession } = ctx;
86994
+ recordLiveSessionChunk(event, handle, journal, extractChunk, deps);
86995
+ if (event.type === "session.idle") {
86996
+ handleSessionIdle(handle, journal, idleConfig, deps.sessionRepository).catch((err) => console.warn("[direct-harness] idle handler error:", err));
86997
+ }
86998
+ handleLiveSessionTitleUpdate(event, deps, rowId, liveSession);
86999
+ }
86622
87000
  async function processOne(daemonSession, deps, session2) {
86623
87001
  const rowId = session2._id;
86624
87002
  try {
@@ -86631,21 +87009,7 @@ async function processOne(daemonSession, deps, session2) {
86631
87009
  await deps.sessionRepository.markFailed(rowId);
86632
87010
  return;
86633
87011
  }
86634
- let harness = deps.harnesses.get(session2.workspaceId);
86635
- if (harness && !harness.isAlive()) {
86636
- console.warn(`[direct-harness] Harness for workspace ${session2.workspaceId} is no longer alive — restarting`);
86637
- harness.close().catch(() => {});
86638
- deps.harnesses.delete(session2.workspaceId);
86639
- harness = undefined;
86640
- }
86641
- if (!harness) {
86642
- harness = await startOpencodeSdkHarness({
86643
- type: "opencode",
86644
- workingDir: workspace.workingDir,
86645
- workspaceId: session2.workspaceId
86646
- });
86647
- deps.harnesses.set(session2.workspaceId, harness);
86648
- }
87012
+ const harness = await getOrCreateHarness(daemonSession, deps, session2, workspace);
86649
87013
  const liveSession = await harness.newSession({
86650
87014
  agent: session2.opencode?.lastUsedConfig.agent ?? "build",
86651
87015
  harnessSessionId: rowId
@@ -86677,30 +87041,15 @@ async function processOne(daemonSession, deps, session2) {
86677
87041
  close: close2
86678
87042
  };
86679
87043
  unsubscribeEvents = liveSession.onEvent((event) => {
86680
- const chunk2 = extractChunk(event);
86681
- if (chunk2 !== null) {
86682
- journal.record({
86683
- content: chunk2.content,
86684
- timestamp: Date.now(),
86685
- messageId: chunk2.messageId,
86686
- partType: chunk2.partType
86687
- });
86688
- if (handle.currentTurn && handle.currentTurn.messageId === null) {
86689
- handle.currentTurn.messageId = chunk2.messageId;
86690
- deps.sessionRepository.bindTurnMessageId(handle.currentTurn.turnId, chunk2.messageId).catch((err) => console.warn("[direct-harness] bindTurnMessageId error:", err));
86691
- }
86692
- }
86693
- if (event.type === "session.idle") {
86694
- handleSessionIdle(handle, journal, idleConfig, deps.sessionRepository).catch((err) => console.warn("[direct-harness] idle handler error:", err));
86695
- }
86696
- if (event.type === "session.updated") {
86697
- const info = event.payload.info;
86698
- const newTitle = info?.title;
86699
- if (newTitle && newTitle !== liveSession.sessionTitle) {
86700
- liveSession.setTitle?.(newTitle);
86701
- deps.sessionRepository.updateSessionTitle(rowId, newTitle).catch((err) => console.warn("[direct-harness] updateSessionTitle error:", err));
86702
- }
86703
- }
87044
+ handleLiveSessionEvent(event, {
87045
+ handle,
87046
+ journal,
87047
+ extractChunk,
87048
+ idleConfig,
87049
+ deps,
87050
+ rowId,
87051
+ liveSession
87052
+ });
86704
87053
  });
86705
87054
  deps.activeSessions.set(rowId, handle);
86706
87055
  try {
@@ -87528,16 +87877,14 @@ var init_state_recovery = __esm(() => {
87528
87877
  for (const config3 of configsResult.configs) {
87529
87878
  if (config3.machineId === session2.machineId && config3.workingDir) {
87530
87879
  registeredCount++;
87531
- exports_Effect.runPromise(exports_Effect.tryPromise(() => session2.backend.mutation(api.workspaces.registerWorkspace, {
87880
+ yield* exports_Effect.forkDaemon(exports_Effect.tryPromise(() => session2.backend.mutation(api.workspaces.registerWorkspace, {
87532
87881
  sessionId: session2.sessionId,
87533
87882
  chatroomId,
87534
87883
  machineId: session2.machineId,
87535
87884
  workingDir: config3.workingDir,
87536
87885
  hostname: session2.config?.hostname ?? "unknown",
87537
87886
  registeredBy: config3.role
87538
- }))).catch((err) => {
87539
- console.warn(`[daemon] ⚠️ Failed to register workspace on recovery: ${err.message}`);
87540
- });
87887
+ })).pipe(exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[daemon] ⚠️ Failed to register workspace on recovery: ${err.message}`)))));
87541
87888
  }
87542
87889
  }
87543
87890
  }), () => exports_Effect.void);
@@ -87608,16 +87955,18 @@ class DaemonEventBus {
87608
87955
  }
87609
87956
 
87610
87957
  // src/events/daemon/agent/on-agent-exited.ts
87611
- async function handleAgentExited(deps, payload) {
87612
- await deps.handleExit({
87958
+ var onAgentExitedEffect = (payload) => exports_Effect.gen(function* () {
87959
+ const apm = yield* DaemonAgentProcessManagerService;
87960
+ yield* apm.handleExit({
87613
87961
  chatroomId: payload.chatroomId,
87614
87962
  role: payload.role,
87615
87963
  pid: payload.pid,
87616
87964
  code: payload.code,
87617
87965
  signal: payload.signal
87618
87966
  });
87619
- }
87967
+ });
87620
87968
  var init_on_agent_exited = __esm(() => {
87969
+ init_esm();
87621
87970
  init_daemon_services();
87622
87971
  });
87623
87972
 
@@ -87638,9 +87987,11 @@ var init_on_agent_stopped = () => {};
87638
87987
  // src/events/daemon/register-listeners.ts
87639
87988
  var registerEventListenersEffect = () => exports_Effect.gen(function* () {
87640
87989
  const session2 = yield* DaemonSessionService;
87641
- const apm = yield* DaemonAgentProcessManagerService;
87990
+ const runtime4 = yield* exports_Effect.runtime();
87642
87991
  const unsubs = [];
87643
- unsubs.push(session2.events.on("agent:exited", (payload) => handleAgentExited({ handleExit: (opts) => exports_Effect.runPromise(apm.handleExit(opts)) }, payload)));
87992
+ unsubs.push(session2.events.on("agent:exited", (payload) => {
87993
+ exports_Runtime.runFork(runtime4)(onAgentExitedEffect(payload));
87994
+ }));
87644
87995
  unsubs.push(session2.events.on("agent:started", (payload) => logAgentStarted(payload)));
87645
87996
  unsubs.push(session2.events.on("agent:stopped", (payload) => logAgentStopped(payload)));
87646
87997
  return () => {
@@ -87960,9 +88311,131 @@ var init_decide_resume_path = __esm(() => {
87960
88311
  ]);
87961
88312
  });
87962
88313
 
88314
+ // src/domain/agent-lifecycle/entities/agent-slot.ts
88315
+ function agentKey2(chatroomId, role) {
88316
+ return `${chatroomId}:${role.toLowerCase()}`;
88317
+ }
88318
+ var idleSlot = () => ({ state: "idle" });
88319
+ // src/domain/agent-lifecycle/policies/slot-transitions.ts
88320
+ function makeError(tag, from, event) {
88321
+ return {
88322
+ ok: false,
88323
+ error: { _tag: tag, from, event }
88324
+ };
88325
+ }
88326
+ function makeResult(slot) {
88327
+ return { ok: true, slot };
88328
+ }
88329
+ function transitionFromIdle(slot, event) {
88330
+ if (event.type === "ensure_running_requested") {
88331
+ return makeResult(slot);
88332
+ }
88333
+ if (event.type === "spawn_started") {
88334
+ return makeResult({ ...slot, state: "spawning", pendingOperationKey: event.operationKey });
88335
+ }
88336
+ if (event.type === "process_exited") {
88337
+ return makeResult(slot);
88338
+ }
88339
+ return makeError("InvalidTransition", slot.state, event.type);
88340
+ }
88341
+ function transitionFromSpawning(slot, event) {
88342
+ if (event.type === "spawn_started") {
88343
+ return makeResult({ ...slot, pendingOperationKey: event.operationKey });
88344
+ }
88345
+ if (event.type === "spawn_succeeded") {
88346
+ return makeResult({ ...slot, state: "running", pid: event.pid });
88347
+ }
88348
+ if (event.type === "spawn_failed" || event.type === "process_exited") {
88349
+ return makeResult({ state: "idle" });
88350
+ }
88351
+ return makeError("InvalidTransition", slot.state, event.type);
88352
+ }
88353
+ function transitionFromRunning(slot, event) {
88354
+ if (event.type === "stop_requested") {
88355
+ return makeResult({ ...slot, state: "stopping", pendingOperationKey: event.operationKey });
88356
+ }
88357
+ if (event.type === "process_exited") {
88358
+ if (slot.pid !== undefined && slot.pid !== event.pid) {
88359
+ return { ok: false, error: { _tag: "StalePid", expected: slot.pid, got: event.pid } };
88360
+ }
88361
+ return makeResult({ state: "idle" });
88362
+ }
88363
+ if (event.type === "stale_process_detected") {
88364
+ return makeResult({ state: "idle" });
88365
+ }
88366
+ return makeError("InvalidTransition", slot.state, event.type);
88367
+ }
88368
+ function transitionFromStopping(slot, event) {
88369
+ if (event.type === "stop_completed") {
88370
+ return makeResult({ state: "idle" });
88371
+ }
88372
+ if (event.type === "process_exited") {
88373
+ return { ok: false, error: { _tag: "IgnoredDuplicateExit" } };
88374
+ }
88375
+ return makeError("InvalidTransition", slot.state, event.type);
88376
+ }
88377
+ function transitionSlot(slot, event) {
88378
+ switch (slot.state) {
88379
+ case "idle":
88380
+ return transitionFromIdle(slot, event);
88381
+ case "spawning":
88382
+ return transitionFromSpawning(slot, event);
88383
+ case "running":
88384
+ return transitionFromRunning(slot, event);
88385
+ case "stopping":
88386
+ return transitionFromStopping(slot, event);
88387
+ }
88388
+ }
88389
+ function shouldIgnoreProcessExit(slot, exitPid) {
88390
+ if (slot.state === "stopping")
88391
+ return true;
88392
+ if (slot.pid !== undefined && slot.pid !== exitPid)
88393
+ return true;
88394
+ return false;
88395
+ }
88396
+
88397
+ // src/domain/agent-lifecycle/policies/restart-decision.ts
88398
+ function decideRestartAfterExit(input) {
88399
+ if (!shouldAutoRestartAfterProcessExit(input.stopReason)) {
88400
+ return { _tag: "NoRestart", reason: `Intentional stop: ${input.stopReason}` };
88401
+ }
88402
+ if (!input.harness || !input.workingDir) {
88403
+ return { _tag: "NoRestart", reason: "Missing harness or workingDir" };
88404
+ }
88405
+ if (input.isPermanentFailure) {
88406
+ return {
88407
+ _tag: "NoRestart",
88408
+ reason: input.permanentFailureMessage ?? "Permanent failure"
88409
+ };
88410
+ }
88411
+ if (input.backoffWaitMs && input.backoffWaitMs > 0) {
88412
+ return {
88413
+ _tag: "ScheduleRetry",
88414
+ waitMs: input.backoffWaitMs,
88415
+ spawnReason: "platform.crash_recovery",
88416
+ wantResume: input.wantResume
88417
+ };
88418
+ }
88419
+ return {
88420
+ _tag: "RestartNow",
88421
+ spawnReason: "platform.crash_recovery",
88422
+ wantResume: input.wantResume,
88423
+ bypassConcurrentLimit: true
88424
+ };
88425
+ }
88426
+ var init_restart_decision = __esm(() => {
88427
+ init_decide_resume_path();
88428
+ });
88429
+
88430
+ // src/domain/agent-lifecycle/policies/spawn-gate.ts
88431
+ function shouldBypassConcurrentLimit(spawnReason) {
88432
+ return spawnReason.startsWith("user.") || spawnReason === "platform.crash_recovery";
88433
+ }
88434
+
87963
88435
  // src/domain/agent-lifecycle/index.ts
87964
88436
  var init_agent_lifecycle = __esm(() => {
87965
88437
  init_decide_resume_path();
88438
+ init_restart_decision();
87966
88439
  });
87967
88440
 
87968
88441
  // src/domain/agent-lifecycle/policies/append-recent-log-line.ts
@@ -88158,6 +88631,90 @@ class RapidResumeTracker {
88158
88631
  }
88159
88632
  var RAPID_RESUME_WINDOW_MS = 30000, RAPID_RESUME_THRESHOLD = 5;
88160
88633
 
88634
+ // src/infrastructure/services/agent-lifecycle/agent-lifecycle-port-adapters.ts
88635
+ function createSpawnPort(spawning) {
88636
+ return {
88637
+ shouldAllowSpawn: (chatroomId, reason, options) => spawning.shouldAllowSpawn(chatroomId, reason, options),
88638
+ recordSpawn: (chatroomId) => exports_Effect.sync(() => {
88639
+ spawning.recordSpawn(chatroomId);
88640
+ }),
88641
+ recordExit: (chatroomId) => exports_Effect.sync(() => {
88642
+ spawning.recordExit(chatroomId);
88643
+ })
88644
+ };
88645
+ }
88646
+ function createHarnessSpawnPort(deps) {
88647
+ return {
88648
+ spawn: (args2) => exports_Effect.tryPromise({
88649
+ try: async () => {
88650
+ const service3 = deps.agentServices.get(args2.harness);
88651
+ if (!service3) {
88652
+ throw new Error(`Unknown agent harness: ${args2.harness}`);
88653
+ }
88654
+ const result = await service3.spawn({
88655
+ workingDir: args2.workingDir,
88656
+ prompt: args2.prompt,
88657
+ systemPrompt: args2.systemPrompt ?? "",
88658
+ model: args2.model,
88659
+ context: {
88660
+ machineId: deps.machineId,
88661
+ chatroomId: args2.chatroomId,
88662
+ role: args2.role
88663
+ }
88664
+ });
88665
+ return {
88666
+ pid: result.pid,
88667
+ harnessSessionId: result.harnessSessionId,
88668
+ onAgentEnd: (cb) => {
88669
+ result.onAgentEnd?.(cb);
88670
+ },
88671
+ onLogLine: result.onLogLine ? (lineCb) => {
88672
+ result.onLogLine?.((line) => {
88673
+ lineCb(line);
88674
+ });
88675
+ } : undefined
88676
+ };
88677
+ },
88678
+ catch: (e) => e instanceof Error ? e : new Error(String(e))
88679
+ }),
88680
+ stop: (pid, opts) => exports_Effect.tryPromise({
88681
+ try: async () => {
88682
+ for (const service3 of deps.agentServices.values()) {
88683
+ try {
88684
+ await service3.stop(pid, opts);
88685
+ return;
88686
+ } catch {}
88687
+ }
88688
+ },
88689
+ catch: (e) => e instanceof Error ? e : new Error(String(e))
88690
+ }),
88691
+ isAlive: (pid) => exports_Effect.sync(() => {
88692
+ return isProcessAlive((p) => process.kill(p, 0), pid);
88693
+ })
88694
+ };
88695
+ }
88696
+ function createAgentLifecyclePorts(deps) {
88697
+ return {
88698
+ spawn: createSpawnPort(deps.spawning),
88699
+ harness: createHarnessSpawnPort(deps),
88700
+ sessionId: deps.sessionId,
88701
+ machineId: deps.machineId
88702
+ };
88703
+ }
88704
+ var init_agent_lifecycle_port_adapters = __esm(() => {
88705
+ init_esm();
88706
+ });
88707
+
88708
+ // src/infrastructure/services/agent-lifecycle/agent-lifecycle-types.ts
88709
+ var AgentLifecyclePorts, AgentLifecycleService;
88710
+ var init_agent_lifecycle_types = __esm(() => {
88711
+ init_esm();
88712
+ AgentLifecyclePorts = class AgentLifecyclePorts extends exports_Context.Tag("AgentLifecyclePorts")() {
88713
+ };
88714
+ AgentLifecycleService = class AgentLifecycleService extends exports_Context.Tag("AgentLifecycleService")() {
88715
+ };
88716
+ });
88717
+
88161
88718
  // src/infrastructure/services/remote-agents/spawn-prompt.ts
88162
88719
  function createSpawnPrompt(raw) {
88163
88720
  const trimmed = raw?.trim();
@@ -88165,8 +88722,264 @@ function createSpawnPrompt(raw) {
88165
88722
  }
88166
88723
  var DEFAULT_TRIGGER_PROMPT = "Please read your system prompt carefully and follow the Getting Started instructions.";
88167
88724
 
88725
+ // src/infrastructure/services/agent-lifecycle/agent-lifecycle-service.ts
88726
+ var AgentLifecycleServiceLive;
88727
+ var init_agent_lifecycle_service = __esm(() => {
88728
+ init_esm();
88729
+ init_agent_lifecycle_types();
88730
+ init_agent_lifecycle();
88731
+ AgentLifecycleServiceLive = exports_Layer.effect(AgentLifecycleService, exports_Effect.gen(function* () {
88732
+ const ports = yield* AgentLifecyclePorts;
88733
+ const slotsRef = yield* exports_Ref.make(new Map);
88734
+ const getSlotFromRef = (key) => exports_Ref.get(slotsRef).pipe(exports_Effect.map((map18) => map18.get(key)));
88735
+ const setSlotInRef = (key, slot) => exports_Ref.update(slotsRef, (map18) => map18.set(key, slot));
88736
+ const removeSlotFromRef = (key) => exports_Ref.update(slotsRef, (map18) => {
88737
+ const next4 = new Map(map18);
88738
+ next4.delete(key);
88739
+ return next4;
88740
+ });
88741
+ const spawnAndRegister = (key, opts) => exports_Effect.gen(function* () {
88742
+ let slot = {
88743
+ ...idleSlot(),
88744
+ harness: opts.agentHarness,
88745
+ model: opts.model,
88746
+ workingDir: opts.workingDir,
88747
+ wantResume: opts.wantResume,
88748
+ _initPrompt: opts.initPrompt ?? "",
88749
+ _systemPrompt: opts.systemPrompt
88750
+ };
88751
+ const startedResult = transitionSlot(slot, {
88752
+ type: "spawn_started",
88753
+ operationKey: opts.reason
88754
+ });
88755
+ if (!startedResult.ok) {
88756
+ return { success: false, error: "spawn_failed" };
88757
+ }
88758
+ slot = startedResult.slot;
88759
+ const spawnHandle = yield* ports.harness.spawn({
88760
+ harness: opts.agentHarness,
88761
+ chatroomId: opts.chatroomId,
88762
+ role: opts.role,
88763
+ workingDir: opts.workingDir,
88764
+ model: opts.model,
88765
+ prompt: createSpawnPrompt(opts.initPrompt),
88766
+ systemPrompt: opts.systemPrompt
88767
+ }).pipe(exports_Effect.catchAll(() => exports_Effect.succeed(null)));
88768
+ if (!spawnHandle) {
88769
+ return { success: false, error: "spawn_failed" };
88770
+ }
88771
+ const succeededResult = transitionSlot(slot, {
88772
+ type: "spawn_succeeded",
88773
+ pid: spawnHandle.pid
88774
+ });
88775
+ if (!succeededResult.ok) {
88776
+ return { success: false, error: "spawn_failed" };
88777
+ }
88778
+ slot = succeededResult.slot;
88779
+ yield* ports.spawn.recordSpawn(opts.chatroomId);
88780
+ yield* setSlotInRef(key, slot);
88781
+ if (spawnHandle) {
88782
+ spawnHandle.onAgentEnd(() => {});
88783
+ }
88784
+ if (spawnHandle && spawnHandle.harnessSessionId) {
88785
+ const updatedSlot = { ...slot, harnessSessionId: spawnHandle.harnessSessionId };
88786
+ yield* setSlotInRef(key, updatedSlot);
88787
+ }
88788
+ return { success: true, pid: slot.pid };
88789
+ });
88790
+ const ensureRunning = (opts) => exports_Effect.gen(function* () {
88791
+ const key = agentKey2(opts.chatroomId, opts.role);
88792
+ const currentSlot = yield* getSlotFromRef(key);
88793
+ if (currentSlot && currentSlot.state !== "idle") {
88794
+ return {
88795
+ success: true,
88796
+ pid: currentSlot.pid
88797
+ };
88798
+ }
88799
+ const bypass = shouldBypassConcurrentLimit(opts.reason);
88800
+ const allowResult = ports.spawn.shouldAllowSpawn(opts.chatroomId, opts.reason, bypass ? { bypassConcurrentLimit: true } : undefined);
88801
+ if (!allowResult.allowed) {
88802
+ const error = allowResult.retryAfterMs ? "rate_limited" : "backoff";
88803
+ return { success: false, error };
88804
+ }
88805
+ const result = yield* spawnAndRegister(key, opts);
88806
+ if (!result.success && result.error) {
88807
+ yield* exports_Effect.logError(`Agent spawn failed for ${key}: ${result.error}`);
88808
+ }
88809
+ return result;
88810
+ });
88811
+ const stop4 = (opts) => exports_Effect.gen(function* () {
88812
+ const key = agentKey2(opts.chatroomId, opts.role);
88813
+ const slot = yield* getSlotFromRef(key);
88814
+ if (!slot) {
88815
+ return { success: false };
88816
+ }
88817
+ const stoppingResult = transitionSlot(slot, {
88818
+ type: "stop_requested",
88819
+ operationKey: opts.reason
88820
+ });
88821
+ if (!stoppingResult.ok) {
88822
+ return { success: false };
88823
+ }
88824
+ const stoppingSlot = stoppingResult.slot;
88825
+ yield* setSlotInRef(key, stoppingSlot);
88826
+ if (stoppingSlot.pid) {
88827
+ yield* ports.harness.stop(stoppingSlot.pid, { preserveForResume: false }).pipe(exports_Effect.ignore);
88828
+ }
88829
+ yield* ports.spawn.recordExit(opts.chatroomId);
88830
+ const completedResult = transitionSlot(stoppingSlot, {
88831
+ type: "stop_completed"
88832
+ });
88833
+ if (completedResult.ok) {
88834
+ yield* setSlotInRef(key, completedResult.slot);
88835
+ }
88836
+ return { success: true };
88837
+ });
88838
+ const executeRestart = (slot, chatroomId, role) => exports_Effect.gen(function* () {
88839
+ const restartOutcome = decideRestartAfterExit({
88840
+ stopReason: resolveStopReason(slot._stopReasonCode ?? 0, slot._stopReasonSignal ?? null),
88841
+ harness: slot.harness,
88842
+ workingDir: slot.workingDir,
88843
+ wantResume: slot.wantResume ?? false,
88844
+ isPermanentFailure: false,
88845
+ restartAllowed: true
88846
+ });
88847
+ yield* dispatchRestartOutcome(restartOutcome, slot, chatroomId, role);
88848
+ });
88849
+ const dispatchRestartOutcome = (outcome, slot, chatroomId, role) => exports_Effect.gen(function* () {
88850
+ switch (outcome._tag) {
88851
+ case "RestartNow": {
88852
+ yield* handleRestartNow(slot, chatroomId, role, outcome);
88853
+ break;
88854
+ }
88855
+ case "ScheduleRetry": {
88856
+ yield* handleScheduleRetry(slot, chatroomId, role, outcome);
88857
+ break;
88858
+ }
88859
+ case "NoRestart": {
88860
+ break;
88861
+ }
88862
+ }
88863
+ });
88864
+ const handleRestartNow = (slot, chatroomId, role, outcome) => exports_Effect.gen(function* () {
88865
+ if (!slot.harness) {
88866
+ yield* exports_Effect.logError(`Agent restart failed for ${chatroomId}:${role}: missing harness`);
88867
+ return;
88868
+ }
88869
+ const restartResult = yield* ensureRunning({
88870
+ chatroomId,
88871
+ role,
88872
+ agentHarness: slot.harness,
88873
+ workingDir: slot.workingDir ?? "",
88874
+ reason: outcome.spawnReason,
88875
+ wantResume: outcome.wantResume,
88876
+ initPrompt: slot._initPrompt,
88877
+ systemPrompt: slot._systemPrompt
88878
+ });
88879
+ if (!restartResult.success && restartResult.error) {
88880
+ yield* exports_Effect.logError(`Agent restart failed for ${chatroomId}:${role}: ${restartResult.error}`);
88881
+ }
88882
+ });
88883
+ const handleScheduleRetry = (slot, chatroomId, role, outcome) => exports_Effect.gen(function* () {
88884
+ if (!slot.harness) {
88885
+ yield* exports_Effect.logError(`Agent restart failed for ${chatroomId}:${role}: missing harness`);
88886
+ return;
88887
+ }
88888
+ yield* exports_Effect.forkDaemon(exports_Effect.sleep(exports_Duration.millis(outcome.waitMs)).pipe(exports_Effect.as(ensureRunning({
88889
+ chatroomId,
88890
+ role,
88891
+ agentHarness: slot.harness,
88892
+ workingDir: slot.workingDir ?? "",
88893
+ reason: outcome.spawnReason,
88894
+ wantResume: outcome.wantResume,
88895
+ initPrompt: slot._initPrompt,
88896
+ systemPrompt: slot._systemPrompt
88897
+ }))));
88898
+ });
88899
+ const handleExit = (opts) => exports_Effect.gen(function* () {
88900
+ const key = agentKey2(opts.chatroomId, opts.role);
88901
+ const slot = yield* getSlotFromRef(key);
88902
+ if (!slot) {
88903
+ return;
88904
+ }
88905
+ if (shouldIgnoreProcessExit(slot, opts.pid)) {
88906
+ return;
88907
+ }
88908
+ const stopReason = resolveStopReason(opts.code, opts.signal);
88909
+ const transitionResult = transitionSlot(slot, {
88910
+ type: "process_exited",
88911
+ pid: opts.pid
88912
+ });
88913
+ if (!transitionResult.ok) {
88914
+ return;
88915
+ }
88916
+ const exitedSlot = {
88917
+ ...transitionResult.slot,
88918
+ _stopReasonCode: opts.code,
88919
+ _stopReasonSignal: opts.signal,
88920
+ harness: slot.harness,
88921
+ workingDir: slot.workingDir,
88922
+ wantResume: slot.wantResume,
88923
+ _initPrompt: slot._initPrompt,
88924
+ _systemPrompt: slot._systemPrompt
88925
+ };
88926
+ yield* setSlotInRef(key, exitedSlot);
88927
+ yield* executeRestart(exitedSlot, opts.chatroomId, opts.role);
88928
+ const restartOutcome = decideRestartAfterExit({
88929
+ stopReason,
88930
+ harness: slot.harness,
88931
+ workingDir: slot.workingDir,
88932
+ wantResume: slot.wantResume ?? false,
88933
+ isPermanentFailure: false,
88934
+ restartAllowed: true
88935
+ });
88936
+ if (restartOutcome._tag === "NoRestart") {
88937
+ yield* removeSlotFromRef(key);
88938
+ }
88939
+ yield* ports.spawn.recordExit(opts.chatroomId);
88940
+ });
88941
+ const getSlot = (chatroomId, role) => getSlotFromRef(agentKey2(chatroomId, role));
88942
+ const listActive = () => exports_Ref.get(slotsRef).pipe(exports_Effect.map((map18) => {
88943
+ const results = [];
88944
+ for (const [key, slot] of map18) {
88945
+ if (slot.state !== "idle") {
88946
+ const [chatroomId, role] = key.split(":");
88947
+ results.push({ chatroomId, role, slot });
88948
+ }
88949
+ }
88950
+ return results;
88951
+ }));
88952
+ return {
88953
+ ensureRunning,
88954
+ stop: stop4,
88955
+ handleExit,
88956
+ getSlot,
88957
+ listActive
88958
+ };
88959
+ }));
88960
+ });
88961
+
88962
+ // src/infrastructure/services/agent-lifecycle/agent-lifecycle-runtime.ts
88963
+ function createAgentLifecycleRuntime(deps) {
88964
+ const layer = exports_Layer.provide(AgentLifecycleServiceLive, exports_Layer.succeed(AgentLifecyclePorts, createAgentLifecyclePorts(deps)));
88965
+ const runtime4 = exports_ManagedRuntime.make(layer);
88966
+ return {
88967
+ runtime: runtime4,
88968
+ runPromise(effect2) {
88969
+ return runtime4.runPromise(effect2);
88970
+ },
88971
+ dispose: () => runtime4.dispose()
88972
+ };
88973
+ }
88974
+ var init_agent_lifecycle_runtime = __esm(() => {
88975
+ init_esm();
88976
+ init_agent_lifecycle_port_adapters();
88977
+ init_agent_lifecycle_service();
88978
+ init_agent_lifecycle_types();
88979
+ });
88980
+
88168
88981
  // src/infrastructure/services/agent-process-manager/agent-process-manager.ts
88169
- function agentKey2(chatroomId, role) {
88982
+ function agentKey3(chatroomId, role) {
88170
88983
  return `${chatroomId}:${role.toLowerCase()}`;
88171
88984
  }
88172
88985
 
@@ -88177,17 +88990,47 @@ class AgentProcessManager {
88177
88990
  exitRetryQueue = [];
88178
88991
  exitRetryTimer = null;
88179
88992
  turnEndQueue = new TurnEndQueue;
88993
+ lifecycle;
88180
88994
  constructor(deps) {
88181
88995
  this.deps = {
88182
88996
  ...deps,
88183
88997
  resumeStormTracker: deps.resumeStormTracker ?? new RapidResumeTracker
88184
88998
  };
88999
+ const portAdapterDeps = {
89000
+ spawning: this.deps.spawning,
89001
+ agentServices: this.deps.agentServices,
89002
+ sessionId: this.deps.sessionId,
89003
+ machineId: this.deps.machineId,
89004
+ onAgentEnd: (args2) => void this.runHandleAgentEnd(args2)
89005
+ };
89006
+ this.lifecycle = createAgentLifecycleRuntime(portAdapterDeps);
89007
+ }
89008
+ updateSlotsMirror(chatroomId, role, slot) {
89009
+ const key = agentKey3(chatroomId, role);
89010
+ const existing = this.slots.get(key);
89011
+ if (!existing || existing.state !== slot.state || existing.pid !== slot.pid) {
89012
+ this.slots.set(key, {
89013
+ state: slot.state,
89014
+ pid: slot.pid,
89015
+ harness: slot.harness,
89016
+ harnessSessionId: slot.harnessSessionId,
89017
+ model: slot.model,
89018
+ workingDir: slot.workingDir,
89019
+ startedAt: slot.startedAt,
89020
+ resumeInFlight: slot.resumeInFlight,
89021
+ recentLogLines: slot.recentLogLines,
89022
+ wantResume: slot.wantResume
89023
+ });
89024
+ }
89025
+ }
89026
+ getSlotFromMirror(chatroomId, role) {
89027
+ return this.slots.get(agentKey3(chatroomId, role));
88185
89028
  }
88186
- async whenTurnEndsIdle() {
88187
- await this.turnEndQueue.whenIdle();
89029
+ whenTurnEndsIdle() {
89030
+ return this.turnEndQueue.whenIdle();
88188
89031
  }
88189
89032
  async ensureRunning(opts) {
88190
- const key = agentKey2(opts.chatroomId, opts.role);
89033
+ const key = agentKey3(opts.chatroomId, opts.role);
88191
89034
  const slot = this.getOrCreateSlot(key);
88192
89035
  if (slot.state === "running" && slot.pid && !isProcessAlive(this.deps.processes.kill, slot.pid)) {
88193
89036
  slot.state = "idle";
@@ -88211,30 +89054,21 @@ class AgentProcessManager {
88211
89054
  return operation;
88212
89055
  }
88213
89056
  async stop(opts) {
88214
- const key = agentKey2(opts.chatroomId, opts.role);
89057
+ const key = agentKey3(opts.chatroomId, opts.role);
88215
89058
  const slot = this.slots.get(key);
89059
+ const earlyResult = await this.handleStopEarlyReturns(slot, opts, key);
89060
+ if (earlyResult) {
89061
+ return earlyResult;
89062
+ }
89063
+ const actualSlot = slot;
89064
+ if (actualSlot.pendingOperation) {
89065
+ await actualSlot.pendingOperation;
89066
+ }
89067
+ return { success: true };
89068
+ }
89069
+ async handleStopEarlyReturns(slot, opts, key) {
88216
89070
  if (!slot || slot.state === "idle") {
88217
- const eventPid = opts.pid;
88218
- if (eventPid && eventPid > 0) {
88219
- try {
88220
- this.deps.processes.kill(eventPid, "SIGTERM");
88221
- } catch {}
88222
- }
88223
- const exitArgs1 = {
88224
- sessionId: this.deps.sessionId,
88225
- machineId: this.deps.machineId,
88226
- chatroomId: opts.chatroomId,
88227
- role: opts.role,
88228
- pid: eventPid ?? 0,
88229
- stopReason: opts.reason,
88230
- exitCode: undefined,
88231
- signal: undefined,
88232
- agentHarness: undefined
88233
- };
88234
- this.deps.backend.mutation(api.machines.recordAgentExited, exitArgs1).catch((err) => {
88235
- console.log(` ⚠️ Failed to record agent exit (idle cleanup): ${err.message}`);
88236
- this.queueExitRetry({ role: opts.role, args: exitArgs1 });
88237
- });
89071
+ await this.killAndRecordForIdleSlot(slot, opts);
88238
89072
  return { success: true };
88239
89073
  }
88240
89074
  if (slot.state === "stopping" && slot.pendingOperation) {
@@ -88250,14 +89084,48 @@ class AgentProcessManager {
88250
89084
  slot.state = "stopping";
88251
89085
  const operation = this.doStop(key, slot, pid, opts);
88252
89086
  slot.pendingOperation = operation;
88253
- await operation;
88254
- return { success: true };
89087
+ return null;
89088
+ }
89089
+ async killAndRecordForIdleSlot(slot, opts) {
89090
+ const eventPid = opts.pid;
89091
+ if (eventPid && eventPid > 0) {
89092
+ try {
89093
+ this.deps.processes.kill(eventPid, "SIGTERM");
89094
+ } catch {}
89095
+ }
89096
+ const exitArgs1 = {
89097
+ sessionId: this.deps.sessionId,
89098
+ machineId: this.deps.machineId,
89099
+ chatroomId: opts.chatroomId,
89100
+ role: opts.role,
89101
+ pid: eventPid ?? 0,
89102
+ stopReason: opts.reason,
89103
+ exitCode: undefined,
89104
+ signal: undefined,
89105
+ agentHarness: undefined
89106
+ };
89107
+ this.deps.backend.mutation(api.machines.recordAgentExited, exitArgs1).catch((err) => {
89108
+ console.log(` ⚠️ Failed to record agent exit (idle cleanup): ${err.message}`);
89109
+ this.queueExitRetry({ role: opts.role, args: exitArgs1 });
89110
+ });
88255
89111
  }
88256
89112
  async runHandleAgentEnd(opts) {
88257
- const slot = this.slots.get(agentKey2(opts.chatroomId, opts.role));
89113
+ const slot = this.slots.get(agentKey3(opts.chatroomId, opts.role));
88258
89114
  const service3 = this.deps.agentServices.get(opts.harness);
88259
89115
  const capabilities = getHarnessCapabilities(opts.harness);
88260
89116
  const supportsSessionResume = capabilities.supportsSessionResume && typeof service3?.resumeTurn === "function";
89117
+ this.updateSlotsMirror(opts.chatroomId, opts.role, {
89118
+ state: slot?.state ?? "idle",
89119
+ pid: slot?.pid,
89120
+ harness: slot?.harness,
89121
+ harnessSessionId: slot?.harnessSessionId,
89122
+ model: slot?.model,
89123
+ workingDir: slot?.workingDir,
89124
+ startedAt: slot?.startedAt,
89125
+ resumeInFlight: slot?.resumeInFlight,
89126
+ recentLogLines: slot?.recentLogLines,
89127
+ wantResume: slot?.wantResume
89128
+ });
88261
89129
  console.log(`[AgentProcessManager] lifecycle.turn.completed: role=${opts.role} pid=${opts.pid} harness=${opts.harness} supportsResume=${supportsSessionResume}`);
88262
89130
  const result = await handleTurnCompleted({
88263
89131
  resumeStormTracker: this.deps.resumeStormTracker,
@@ -88296,37 +89164,76 @@ class AgentProcessManager {
88296
89164
  }
88297
89165
  }
88298
89166
  async handleExit(opts) {
88299
- const key = agentKey2(opts.chatroomId, opts.role);
89167
+ const key = agentKey3(opts.chatroomId, opts.role);
88300
89168
  const slot = this.slots.get(key);
88301
- if (!slot || slot.pid !== opts.pid) {
88302
- return;
88303
- }
88304
- if (slot.state === "stopping") {
89169
+ if (!slot || slot.pid !== opts.pid || slot.state === "stopping") {
88305
89170
  return;
88306
89171
  }
88307
89172
  const stopReason = resolveStopReason(opts.code, opts.signal);
88308
89173
  this.deps.spawning.recordExit(opts.chatroomId);
88309
- const harness = slot.harness;
88310
- const model = slot.model;
88311
- const workingDir = slot.workingDir;
88312
- const harnessSessionId = slot.harnessSessionId;
88313
- const wantResume = slot.wantResume;
88314
- const recentLogLines = slot.recentLogLines;
88315
- if (harness && harnessSessionId && getHarnessCapabilities(harness).supportsSessionResume && shouldRetainHarnessSessionForReconnect(stopReason)) {
88316
- const service3 = this.deps.agentServices.get(harness);
88317
- const harnessMeta = service3 ? this.readHarnessReconnectMetadata(service3, opts.pid) : undefined;
88318
- this.recordLastHarnessSession(key, {
88319
- harnessSessionId,
88320
- harness,
88321
- agentName: harnessMeta?.agentName ?? "",
88322
- workingDir: workingDir ?? "",
88323
- model: model ?? harnessMeta?.model
89174
+ const ctx = this.captureExitContext(slot, opts, stopReason);
89175
+ await this.preserveHarnessSessionOnExit(key, slot, ctx);
89176
+ const lifecyclePromise = this.lifecycle.runPromise(exports_Effect.gen(function* () {
89177
+ const svc = yield* AgentLifecycleService;
89178
+ yield* svc.handleExit({
89179
+ chatroomId: opts.chatroomId,
89180
+ role: opts.role,
89181
+ pid: opts.pid,
89182
+ code: opts.code,
89183
+ signal: opts.signal
88324
89184
  });
89185
+ }));
89186
+ this.resetSlotAfterExit(slot);
89187
+ await this.emitExitEvent(slot, opts, ctx);
89188
+ try {
89189
+ await this.deps.persistence.clearAgentPid(this.deps.machineId, opts.chatroomId, opts.role);
89190
+ } catch {}
89191
+ this.untrackAllServices(opts.pid);
89192
+ lifecyclePromise.then(() => this.dispatchRestartAfterExit(opts, ctx, key)).catch(() => {});
89193
+ }
89194
+ captureExitContext(slot, opts, stopReason) {
89195
+ return {
89196
+ harness: slot.harness,
89197
+ model: slot.model,
89198
+ workingDir: slot.workingDir,
89199
+ harnessSessionId: slot.harnessSessionId,
89200
+ wantResume: slot.wantResume,
89201
+ recentLogLines: slot.recentLogLines,
89202
+ stopReason
89203
+ };
89204
+ }
89205
+ recordExitHarnessSession(key, slot, harness, harnessSessionId, ctx) {
89206
+ const service3 = this.deps.agentServices.get(harness);
89207
+ const harnessMeta = service3 && slot.pid ? this.readHarnessReconnectMetadata(service3, slot.pid) : undefined;
89208
+ this.recordLastHarnessSession(key, {
89209
+ harnessSessionId,
89210
+ harness,
89211
+ agentName: harnessMeta?.agentName ?? "",
89212
+ workingDir: ctx.workingDir ?? "",
89213
+ model: ctx.model ?? harnessMeta?.model
89214
+ });
89215
+ }
89216
+ async preserveHarnessSessionOnExit(key, slot, ctx) {
89217
+ const { harness, harnessSessionId, stopReason } = ctx;
89218
+ if (!harness || !harnessSessionId) {
89219
+ return;
88325
89220
  }
89221
+ if (!getHarnessCapabilities(harness).supportsSessionResume) {
89222
+ return;
89223
+ }
89224
+ if (!shouldRetainHarnessSessionForReconnect(stopReason)) {
89225
+ return;
89226
+ }
89227
+ this.recordExitHarnessSession(key, slot, harness, harnessSessionId, ctx);
89228
+ }
89229
+ resetSlotAfterExit(slot) {
88326
89230
  slot.state = "idle";
88327
89231
  slot.pid = undefined;
88328
89232
  slot.startedAt = undefined;
88329
89233
  slot.pendingOperation = undefined;
89234
+ }
89235
+ async emitExitEvent(slot, opts, ctx) {
89236
+ const stopReason = ctx.stopReason;
88330
89237
  const exitArgs2 = {
88331
89238
  sessionId: this.deps.sessionId,
88332
89239
  machineId: this.deps.machineId,
@@ -88337,42 +89244,36 @@ class AgentProcessManager {
88337
89244
  stopSignal: stopReason === "agent_process.signal" ? opts.signal ?? undefined : undefined,
88338
89245
  exitCode: opts.code ?? undefined,
88339
89246
  signal: opts.signal ?? undefined,
88340
- agentHarness: harness
89247
+ agentHarness: ctx.harness
88341
89248
  };
88342
89249
  this.deps.backend.mutation(api.machines.recordAgentExited, exitArgs2).catch((err) => {
88343
89250
  console.log(` ⚠️ Failed to record agent exit event: ${err.message}`);
88344
89251
  this.queueExitRetry({ role: opts.role, args: exitArgs2 });
88345
89252
  });
88346
- try {
88347
- await this.deps.persistence.clearAgentPid(this.deps.machineId, opts.chatroomId, opts.role);
88348
- } catch {}
89253
+ }
89254
+ untrackAllServices(pid) {
88349
89255
  for (const service3 of this.deps.agentServices.values()) {
88350
- service3.untrack(opts.pid);
89256
+ service3.untrack(pid);
88351
89257
  }
88352
- if (!shouldAutoRestartAfterProcessExit(stopReason)) {
88353
- if (stopReason === "user.stop" || stopReason === "platform.team_switch" || stopReason === "daemon.shutdown") {
89258
+ }
89259
+ dispatchRestartAfterExit(opts, ctx, _key) {
89260
+ const stopReasonForRestart = resolveStopReason(opts.code, opts.signal);
89261
+ if (!shouldAutoRestartAfterProcessExit(stopReasonForRestart)) {
89262
+ if (stopReasonForRestart === "user.stop" || stopReasonForRestart === "platform.team_switch" || stopReasonForRestart === "daemon.shutdown") {
88354
89263
  this.deps.crashLoop.clear(opts.chatroomId, opts.role);
88355
89264
  }
88356
89265
  return;
88357
89266
  }
89267
+ this.maybeRestartAgent(opts, ctx);
89268
+ }
89269
+ maybeRestartAgent(opts, ctx) {
89270
+ const { harness, model, workingDir, recentLogLines } = ctx;
88358
89271
  if (!harness || !workingDir) {
88359
89272
  console.log(`[AgentProcessManager] ⚠️ Cannot restart — missing harness or workingDir ` + `(role: ${opts.role}, harness: ${harness ?? "none"}, workingDir: ${workingDir ?? "none"})`);
88360
89273
  return;
88361
89274
  }
88362
89275
  if (isPermanentHarnessFailure(recentLogLines ?? [])) {
88363
- const error = formatPermanentHarnessFailureMessage(recentLogLines ?? []);
88364
- console.log(`[AgentProcessManager] ⛔ Skipping restart — ${error}`);
88365
- this.deps.crashLoop.clear(opts.chatroomId, opts.role);
88366
- this.clearLastHarnessSession(key);
88367
- this.deps.backend.mutation(api.machines.emitAgentStartFailed, {
88368
- sessionId: this.deps.sessionId,
88369
- machineId: this.deps.machineId,
88370
- chatroomId: opts.chatroomId,
88371
- role: opts.role,
88372
- error
88373
- }).catch((emitErr) => {
88374
- console.log(` ⚠️ Failed to emit startFailed event: ${emitErr.message}`);
88375
- });
89276
+ this.handlePermanentFailureForRestart(opts, recentLogLines);
88376
89277
  return;
88377
89278
  }
88378
89279
  this.ensureRunning({
@@ -88382,22 +89283,39 @@ class AgentProcessManager {
88382
89283
  model,
88383
89284
  workingDir,
88384
89285
  reason: "platform.crash_recovery",
88385
- wantResume: wantResume ?? true
89286
+ wantResume: ctx.wantResume ?? true
88386
89287
  }).catch((err) => {
88387
89288
  console.log(` ⚠️ Failed to restart agent: ${err.message}`);
88388
- this.deps.backend.mutation(api.machines.emitAgentStartFailed, {
88389
- sessionId: this.deps.sessionId,
88390
- machineId: this.deps.machineId,
88391
- chatroomId: opts.chatroomId,
88392
- role: opts.role,
88393
- error: err.message
88394
- }).catch((emitErr) => {
88395
- console.log(` ⚠️ Failed to emit startFailed event: ${emitErr.message}`);
88396
- });
89289
+ this.emitStartFailedEvent(opts.role, opts.chatroomId, err.message);
88397
89290
  });
88398
89291
  }
89292
+ handlePermanentFailureForRestart(opts, recentLogLines) {
89293
+ const error = formatPermanentHarnessFailureMessage(recentLogLines ?? []);
89294
+ console.log(`[AgentProcessManager] ⛔ Skipping restart — ${error}`);
89295
+ this.deps.crashLoop.clear(opts.chatroomId, opts.role);
89296
+ const key = agentKey3(opts.chatroomId, opts.role);
89297
+ this.clearLastHarnessSession(key);
89298
+ this.deps.backend.mutation(api.machines.emitAgentStartFailed, {
89299
+ sessionId: this.deps.sessionId,
89300
+ machineId: this.deps.machineId,
89301
+ chatroomId: opts.chatroomId,
89302
+ role: opts.role,
89303
+ error
89304
+ }).catch((emitErr) => {
89305
+ console.log(` ⚠️ Failed to emit startFailed event: ${emitErr.message}`);
89306
+ });
89307
+ }
89308
+ emitStartFailedEvent(role, chatroomId, error) {
89309
+ this.deps.backend.mutation(api.machines.emitAgentStartFailed, {
89310
+ sessionId: this.deps.sessionId,
89311
+ machineId: this.deps.machineId,
89312
+ chatroomId,
89313
+ role,
89314
+ error
89315
+ }).catch(() => {});
89316
+ }
88399
89317
  getSlot(chatroomId, role) {
88400
- return this.slots.get(agentKey2(chatroomId, role));
89318
+ return this.getSlotFromMirror(chatroomId, role);
88401
89319
  }
88402
89320
  listActive() {
88403
89321
  const result = [];
@@ -88468,13 +89386,19 @@ class AgentProcessManager {
88468
89386
  untrackChildPid(pid);
88469
89387
  }
88470
89388
  async killExistingBeforeSpawn(chatroomId, role) {
88471
- const key = agentKey2(chatroomId, role);
89389
+ const key = agentKey3(chatroomId, role);
89390
+ await this.killInMemorySlotIfAlive(key, chatroomId, role);
89391
+ await this.killPersistedProcessIfAlive(chatroomId, role);
89392
+ }
89393
+ async killInMemorySlotIfAlive(key, chatroomId, role) {
88472
89394
  const slot = this.slots.get(key);
88473
89395
  if (slot?.pid && isProcessAlive(this.deps.processes.kill, slot.pid) && (slot.state === "running" || slot.state === "spawning")) {
88474
- const pid2 = slot.pid;
89396
+ const pid = slot.pid;
88475
89397
  slot.state = "stopping";
88476
- await this.doStop(key, slot, pid2, { chatroomId, role, reason: "daemon.respawn" });
89398
+ await this.doStop(key, slot, pid, { chatroomId, role, reason: "daemon.respawn" });
88477
89399
  }
89400
+ }
89401
+ async killPersistedProcessIfAlive(chatroomId, role) {
88478
89402
  let entries2 = [];
88479
89403
  try {
88480
89404
  entries2 = await this.deps.persistence.listAgentEntries(this.deps.machineId);
@@ -88487,11 +89411,10 @@ class AgentProcessManager {
88487
89411
  }
88488
89412
  const { pid, harness } = persisted.entry;
88489
89413
  if (!isProcessAlive(this.deps.processes.kill, pid)) {
88490
- try {
88491
- await this.deps.persistence.clearAgentPid(this.deps.machineId, chatroomId, role);
88492
- } catch {}
89414
+ await this.deps.persistence.clearAgentPid(this.deps.machineId, chatroomId, role).catch(() => {});
88493
89415
  return;
88494
89416
  }
89417
+ const key = agentKey3(chatroomId, role);
88495
89418
  const currentSlot = this.slots.get(key);
88496
89419
  if (currentSlot?.pid === pid && currentSlot.state !== "idle") {
88497
89420
  return;
@@ -88574,30 +89497,20 @@ class AgentProcessManager {
88574
89497
  }
88575
89498
  }
88576
89499
  async tryDaemonMemoryResume(opts) {
88577
- const capabilities = getHarnessCapabilities(opts.agentHarness);
88578
- if (!capabilities.supportsSessionResume) {
89500
+ const validationResult = this.validateDaemonMemoryResumePreconditions(opts);
89501
+ if (validationResult) {
88579
89502
  return null;
88580
89503
  }
88581
89504
  const stored = this.lastHarnessSessions.get(opts.key);
88582
89505
  if (!stored) {
88583
89506
  return null;
88584
89507
  }
88585
- if (stored.workingDir !== opts.workingDir) {
88586
- this.clearLastHarnessSession(opts.key);
88587
- await this.emitSessionResumeFailed(opts.chatroomId, opts.role, "working directory changed", stored.harnessSessionId);
88588
- return null;
88589
- }
88590
- if (stored.harness !== opts.agentHarness || !stored.agentName) {
88591
- this.clearLastHarnessSession(opts.key);
88592
- await this.emitSessionResumeFailed(opts.chatroomId, opts.role, stored.harness !== opts.agentHarness ? "harness changed" : "incomplete session in daemon memory", stored.harnessSessionId);
88593
- return null;
88594
- }
88595
- if (!opts.service.resumeFromDaemonMemory) {
88596
- await this.emitSessionResumeFailed(opts.chatroomId, opts.role, "daemon-memory session resume not yet supported", stored.harnessSessionId);
89508
+ const resumeFromDaemonMemory = opts.service.resumeFromDaemonMemory;
89509
+ if (!resumeFromDaemonMemory) {
88597
89510
  return null;
88598
89511
  }
88599
89512
  try {
88600
- const spawnResult = await opts.service.resumeFromDaemonMemory({
89513
+ const spawnResult = await resumeFromDaemonMemory({
88601
89514
  workingDir: stored.workingDir,
88602
89515
  prompt: createSpawnPrompt(opts.initPrompt),
88603
89516
  systemPrompt: opts.systemPrompt,
@@ -88621,6 +89534,31 @@ class AgentProcessManager {
88621
89534
  return null;
88622
89535
  }
88623
89536
  }
89537
+ validateDaemonMemoryResumePreconditions(opts) {
89538
+ const capabilities = getHarnessCapabilities(opts.agentHarness);
89539
+ if (!capabilities.supportsSessionResume) {
89540
+ return null;
89541
+ }
89542
+ const stored = this.lastHarnessSessions.get(opts.key);
89543
+ if (!stored) {
89544
+ return null;
89545
+ }
89546
+ if (stored.workingDir !== opts.workingDir) {
89547
+ this.clearLastHarnessSession(opts.key);
89548
+ this.emitSessionResumeFailed(opts.chatroomId, opts.role, "working directory changed", stored.harnessSessionId);
89549
+ return "working directory changed";
89550
+ }
89551
+ if (stored.harness !== opts.agentHarness || !stored.agentName) {
89552
+ this.clearLastHarnessSession(opts.key);
89553
+ this.emitSessionResumeFailed(opts.chatroomId, opts.role, stored.harness !== opts.agentHarness ? "harness changed" : "incomplete session in daemon memory", stored.harnessSessionId);
89554
+ return "validation failed";
89555
+ }
89556
+ if (!opts.service.resumeFromDaemonMemory) {
89557
+ this.emitSessionResumeFailed(opts.chatroomId, opts.role, "daemon-memory session resume not yet supported", stored.harnessSessionId);
89558
+ return "not supported";
89559
+ }
89560
+ return null;
89561
+ }
88624
89562
  async emitSessionResumed(chatroomId, role, harnessSessionId) {
88625
89563
  try {
88626
89564
  await this.deps.backend.mutation(api.machines.emitSessionResumed, {
@@ -88650,195 +89588,248 @@ class AgentProcessManager {
88650
89588
  console.log(` ⚠️ Failed to emit sessionResumeFailed event: ${err.message}`);
88651
89589
  }
88652
89590
  }
88653
- async doEnsureRunning(key, slot, opts) {
88654
- slot.state = "spawning";
88655
- const wantResume = opts.wantResume;
88656
- console.log(`[AgentProcessManager] harness start: role=${opts.role} harness=${opts.agentHarness} wantResume=${wantResume} reason=${opts.reason}`);
89591
+ resetSlotIdle(slot) {
89592
+ slot.state = "idle";
89593
+ slot.pendingOperation = undefined;
89594
+ }
89595
+ checkRateLimitGate(opts, slot) {
89596
+ const spawnCheck = this.deps.spawning.shouldAllowSpawn(opts.chatroomId, opts.reason, {
89597
+ bypassConcurrentLimit: opts.reason.startsWith("user.")
89598
+ });
89599
+ if (!spawnCheck.allowed) {
89600
+ this.resetSlotIdle(slot);
89601
+ return { success: false, error: "rate_limited" };
89602
+ }
89603
+ return null;
89604
+ }
89605
+ checkCrashLoopGate(opts, slot) {
89606
+ if (opts.reason !== "platform.crash_recovery") {
89607
+ return null;
89608
+ }
89609
+ const loopCheck = this.deps.crashLoop.record(opts.chatroomId, opts.role, this.deps.clock.now());
89610
+ if (loopCheck.allowed) {
89611
+ return null;
89612
+ }
89613
+ if (loopCheck.waitMs !== undefined && loopCheck.waitMs > 0) {
89614
+ console.log(` ⏳ Agent restart backoff: waiting ${loopCheck.waitMs}ms before retry`);
89615
+ this.resetSlotIdle(slot);
89616
+ return { success: false, error: "backoff" };
89617
+ }
89618
+ this.deps.backend.mutation(api.machines.emitRestartLimitReached, {
89619
+ sessionId: this.deps.sessionId,
89620
+ machineId: this.deps.machineId,
89621
+ chatroomId: opts.chatroomId,
89622
+ role: opts.role,
89623
+ restartCount: loopCheck.restartCount,
89624
+ windowMs: loopCheck.windowMs
89625
+ }).catch((err) => {
89626
+ console.log(` ⚠️ Failed to emit restartLimitReached event: ${err.message}`);
89627
+ });
89628
+ this.resetSlotIdle(slot);
89629
+ return { success: false, error: "crash_loop" };
89630
+ }
89631
+ async validateWorkingDirGate(opts, slot) {
89632
+ try {
89633
+ const dirStat = await this.deps.fs.stat(opts.workingDir);
89634
+ if (!dirStat.isDirectory()) {
89635
+ this.resetSlotIdle(slot);
89636
+ return {
89637
+ success: false,
89638
+ error: `Working directory is not a directory: ${opts.workingDir}`
89639
+ };
89640
+ }
89641
+ } catch {
89642
+ this.resetSlotIdle(slot);
89643
+ return { success: false, error: `Working directory does not exist: ${opts.workingDir}` };
89644
+ }
89645
+ return null;
89646
+ }
89647
+ async fetchInitPromptResult(opts, slot) {
89648
+ let initPromptResult;
88657
89649
  try {
88658
- const spawnCheck = this.deps.spawning.shouldAllowSpawn(opts.chatroomId, opts.reason, {
88659
- bypassConcurrentLimit: opts.reason.startsWith("user.")
89650
+ initPromptResult = await this.deps.backend.query(api.messages.getInitPrompt, {
89651
+ sessionId: this.deps.sessionId,
89652
+ chatroomId: opts.chatroomId,
89653
+ role: opts.role,
89654
+ convexUrl: this.deps.convexUrl
88660
89655
  });
88661
- if (!spawnCheck.allowed) {
88662
- slot.state = "idle";
88663
- slot.pendingOperation = undefined;
88664
- return { success: false, error: "rate_limited" };
88665
- }
88666
- if (opts.reason === "platform.crash_recovery") {
88667
- const loopCheck = this.deps.crashLoop.record(opts.chatroomId, opts.role, this.deps.clock.now());
88668
- if (!loopCheck.allowed) {
88669
- if (loopCheck.waitMs !== undefined && loopCheck.waitMs > 0) {
88670
- console.log(` ⏳ Agent restart backoff: waiting ${loopCheck.waitMs}ms before retry`);
88671
- slot.state = "idle";
88672
- slot.pendingOperation = undefined;
88673
- return { success: false, error: "backoff" };
88674
- }
88675
- this.deps.backend.mutation(api.machines.emitRestartLimitReached, {
88676
- sessionId: this.deps.sessionId,
89656
+ } catch (e) {
89657
+ this.resetSlotIdle(slot);
89658
+ return {
89659
+ ok: false,
89660
+ result: { success: false, error: `Failed to fetch init prompt: ${e.message}` }
89661
+ };
89662
+ }
89663
+ if (!initPromptResult?.prompt) {
89664
+ this.resetSlotIdle(slot);
89665
+ return {
89666
+ ok: false,
89667
+ result: { success: false, error: "Failed to fetch init prompt from backend" }
89668
+ };
89669
+ }
89670
+ return {
89671
+ ok: true,
89672
+ initialMessage: initPromptResult.initialMessage,
89673
+ rolePrompt: initPromptResult.rolePrompt
89674
+ };
89675
+ }
89676
+ async spawnAgentForEnsureRunning(key, slot, opts, initPrompt, wantResume) {
89677
+ const service3 = this.deps.agentServices.get(opts.agentHarness);
89678
+ if (!service3) {
89679
+ this.resetSlotIdle(slot);
89680
+ return {
89681
+ ok: false,
89682
+ result: { success: false, error: `Unknown agent harness: ${opts.agentHarness}` }
89683
+ };
89684
+ }
89685
+ let spawnResult;
89686
+ const resumePath = decideResumePathOnRestart({
89687
+ supportsSessionResume: getHarnessCapabilities(opts.agentHarness).supportsSessionResume,
89688
+ wantResume,
89689
+ hasStoredSnapshot: this.lastHarnessSessions.has(key)
89690
+ });
89691
+ if (resumePath === "daemon_memory") {
89692
+ spawnResult = await this.tryDaemonMemoryResume({
89693
+ key,
89694
+ chatroomId: opts.chatroomId,
89695
+ role: opts.role,
89696
+ agentHarness: opts.agentHarness,
89697
+ workingDir: opts.workingDir,
89698
+ model: opts.model,
89699
+ initPrompt: initPrompt.initialMessage,
89700
+ systemPrompt: initPrompt.rolePrompt,
89701
+ service: service3
89702
+ }) ?? undefined;
89703
+ }
89704
+ if (!spawnResult) {
89705
+ try {
89706
+ spawnResult = await service3.spawn({
89707
+ workingDir: opts.workingDir,
89708
+ prompt: createSpawnPrompt(initPrompt.initialMessage),
89709
+ systemPrompt: initPrompt.rolePrompt,
89710
+ model: opts.model,
89711
+ context: {
88677
89712
  machineId: this.deps.machineId,
88678
89713
  chatroomId: opts.chatroomId,
88679
- role: opts.role,
88680
- restartCount: loopCheck.restartCount,
88681
- windowMs: loopCheck.windowMs
88682
- }).catch((err) => {
88683
- console.log(` ⚠️ Failed to emit restartLimitReached event: ${err.message}`);
88684
- });
88685
- slot.state = "idle";
88686
- slot.pendingOperation = undefined;
88687
- return { success: false, error: "crash_loop" };
88688
- }
88689
- }
88690
- try {
88691
- const dirStat = await this.deps.fs.stat(opts.workingDir);
88692
- if (!dirStat.isDirectory()) {
88693
- slot.state = "idle";
88694
- slot.pendingOperation = undefined;
88695
- return {
88696
- success: false,
88697
- error: `Working directory is not a directory: ${opts.workingDir}`
88698
- };
88699
- }
88700
- } catch {
88701
- slot.state = "idle";
88702
- slot.pendingOperation = undefined;
88703
- return { success: false, error: `Working directory does not exist: ${opts.workingDir}` };
88704
- }
88705
- let initPromptResult;
88706
- try {
88707
- initPromptResult = await this.deps.backend.query(api.messages.getInitPrompt, {
88708
- sessionId: this.deps.sessionId,
88709
- chatroomId: opts.chatroomId,
88710
- role: opts.role,
88711
- convexUrl: this.deps.convexUrl
89714
+ role: opts.role
89715
+ }
88712
89716
  });
88713
89717
  } catch (e) {
88714
- slot.state = "idle";
88715
- slot.pendingOperation = undefined;
88716
- return { success: false, error: `Failed to fetch init prompt: ${e.message}` };
88717
- }
88718
- if (!initPromptResult?.prompt) {
88719
- slot.state = "idle";
88720
- slot.pendingOperation = undefined;
88721
- return { success: false, error: "Failed to fetch init prompt from backend" };
88722
- }
88723
- const service3 = this.deps.agentServices.get(opts.agentHarness);
88724
- if (!service3) {
88725
- slot.state = "idle";
88726
- slot.pendingOperation = undefined;
88727
- return { success: false, error: `Unknown agent harness: ${opts.agentHarness}` };
89718
+ this.resetSlotIdle(slot);
89719
+ return {
89720
+ ok: false,
89721
+ result: { success: false, error: `Failed to spawn agent: ${e.message}` }
89722
+ };
88728
89723
  }
88729
- let spawnResult;
88730
- const resumePath = decideResumePathOnRestart({
88731
- supportsSessionResume: getHarnessCapabilities(opts.agentHarness).supportsSessionResume,
88732
- wantResume,
88733
- hasStoredSnapshot: this.lastHarnessSessions.has(key)
89724
+ }
89725
+ return { ok: true, spawnResult };
89726
+ }
89727
+ assignRunningSlotState(key, slot, opts, spawnResult, wantResume, pid) {
89728
+ slot.state = "running";
89729
+ slot.pid = pid;
89730
+ slot.harness = opts.agentHarness;
89731
+ slot.harnessSessionId = spawnResult.harnessSessionId;
89732
+ if (spawnResult.harnessSessionId) {
89733
+ this.recordLastHarnessSession(key, {
89734
+ harnessSessionId: spawnResult.harnessSessionId,
89735
+ harness: opts.agentHarness,
89736
+ agentName: spawnResult.harnessReconnect?.agentName ?? "",
89737
+ workingDir: opts.workingDir,
89738
+ model: opts.model ?? spawnResult.harnessReconnect?.model
88734
89739
  });
88735
- if (resumePath === "daemon_memory") {
88736
- spawnResult = await this.tryDaemonMemoryResume({
88737
- key,
88738
- chatroomId: opts.chatroomId,
88739
- role: opts.role,
88740
- agentHarness: opts.agentHarness,
88741
- workingDir: opts.workingDir,
88742
- model: opts.model,
88743
- initPrompt: initPromptResult.initialMessage,
88744
- systemPrompt: initPromptResult.rolePrompt,
88745
- service: service3
88746
- }) ?? undefined;
88747
- }
88748
- if (!spawnResult) {
88749
- try {
88750
- spawnResult = await service3.spawn({
88751
- workingDir: opts.workingDir,
88752
- prompt: createSpawnPrompt(initPromptResult.initialMessage),
88753
- systemPrompt: initPromptResult.rolePrompt,
88754
- model: opts.model,
88755
- context: {
88756
- machineId: this.deps.machineId,
88757
- chatroomId: opts.chatroomId,
88758
- role: opts.role
88759
- }
88760
- });
88761
- } catch (e) {
88762
- slot.state = "idle";
88763
- slot.pendingOperation = undefined;
88764
- return { success: false, error: `Failed to spawn agent: ${e.message}` };
88765
- }
88766
- }
88767
- const { pid } = spawnResult;
88768
- this.deps.spawning.recordSpawn(opts.chatroomId);
88769
- slot.state = "running";
88770
- slot.pid = pid;
88771
- slot.harness = opts.agentHarness;
88772
- slot.harnessSessionId = spawnResult.harnessSessionId;
88773
- if (spawnResult.harnessSessionId) {
88774
- this.recordLastHarnessSession(key, {
88775
- harnessSessionId: spawnResult.harnessSessionId,
88776
- harness: opts.agentHarness,
88777
- agentName: spawnResult.harnessReconnect?.agentName ?? "",
88778
- workingDir: opts.workingDir,
88779
- model: opts.model ?? spawnResult.harnessReconnect?.model
88780
- });
88781
- }
88782
- slot.model = opts.model;
88783
- slot.wantResume = wantResume;
88784
- slot.workingDir = opts.workingDir;
88785
- slot.startedAt = this.deps.clock.now();
88786
- slot.pendingOperation = undefined;
88787
- slot.recentLogLines = [];
88788
- this.deps.resumeStormTracker.reset(opts.chatroomId, opts.role);
88789
- if (spawnResult.onLogLine) {
88790
- spawnResult.onLogLine((line) => appendRecentLogLine(slot, line));
88791
- }
88792
- this.deps.backend.mutation(api.machines.updateSpawnedAgent, {
88793
- sessionId: this.deps.sessionId,
88794
- machineId: this.deps.machineId,
89740
+ }
89741
+ slot.model = opts.model;
89742
+ slot.wantResume = wantResume;
89743
+ slot.workingDir = opts.workingDir;
89744
+ slot.startedAt = this.deps.clock.now();
89745
+ slot.pendingOperation = undefined;
89746
+ slot.recentLogLines = [];
89747
+ this.deps.resumeStormTracker.reset(opts.chatroomId, opts.role);
89748
+ }
89749
+ emitSpawnedAgentUpdate(opts, spawnResult, pid) {
89750
+ this.deps.backend.mutation(api.machines.updateSpawnedAgent, {
89751
+ sessionId: this.deps.sessionId,
89752
+ machineId: this.deps.machineId,
89753
+ chatroomId: opts.chatroomId,
89754
+ role: opts.role,
89755
+ pid,
89756
+ model: opts.model,
89757
+ reason: opts.reason,
89758
+ ...spawnResult.harnessSessionId ? { harnessSessionId: spawnResult.harnessSessionId } : {}
89759
+ }).catch((err) => {
89760
+ console.log(` ⚠️ Failed to update PID in backend: ${err.message}`);
89761
+ });
89762
+ }
89763
+ registerSpawnCallbacks(slot, opts, spawnResult, pid) {
89764
+ if (spawnResult.onLogLine) {
89765
+ spawnResult.onLogLine((line) => appendRecentLogLine(slot, line));
89766
+ }
89767
+ spawnResult.onExit(({ code: code2, signal }) => {
89768
+ this.handleExit({
88795
89769
  chatroomId: opts.chatroomId,
88796
89770
  role: opts.role,
88797
89771
  pid,
88798
- model: opts.model,
88799
- reason: opts.reason,
88800
- ...spawnResult.harnessSessionId ? { harnessSessionId: spawnResult.harnessSessionId } : {}
88801
- }).catch((err) => {
88802
- console.log(` ⚠️ Failed to update PID in backend: ${err.message}`);
89772
+ code: code2,
89773
+ signal
88803
89774
  });
88804
- try {
88805
- await this.deps.persistence.persistAgentPid(this.deps.machineId, opts.chatroomId, opts.role, pid, opts.agentHarness);
88806
- } catch {}
88807
- spawnResult.onExit(({ code: code2, signal }) => {
88808
- this.handleExit({
89775
+ });
89776
+ if (spawnResult.onAgentEnd) {
89777
+ spawnResult.onAgentEnd(() => {
89778
+ this.turnEndQueue.enqueue(() => this.runHandleAgentEnd({
88809
89779
  chatroomId: opts.chatroomId,
88810
89780
  role: opts.role,
88811
89781
  pid,
88812
- code: code2,
88813
- signal
88814
- });
89782
+ harness: opts.agentHarness
89783
+ }));
88815
89784
  });
88816
- if (spawnResult.onAgentEnd) {
88817
- spawnResult.onAgentEnd(() => {
88818
- this.turnEndQueue.enqueue(() => this.runHandleAgentEnd({
88819
- chatroomId: opts.chatroomId,
88820
- role: opts.role,
88821
- pid,
88822
- harness: opts.agentHarness
88823
- }));
88824
- });
89785
+ }
89786
+ let lastReportedTokenAt = 0;
89787
+ spawnResult.onOutput(() => {
89788
+ const now = this.deps.clock.now();
89789
+ if (now - lastReportedTokenAt >= 30000) {
89790
+ lastReportedTokenAt = now;
89791
+ this.deps.backend.mutation(api.participants.updateTokenActivity, {
89792
+ sessionId: this.deps.sessionId,
89793
+ chatroomId: opts.chatroomId,
89794
+ role: opts.role
89795
+ }).catch(() => {});
88825
89796
  }
88826
- let lastReportedTokenAt = 0;
88827
- spawnResult.onOutput(() => {
88828
- const now = this.deps.clock.now();
88829
- if (now - lastReportedTokenAt >= 30000) {
88830
- lastReportedTokenAt = now;
88831
- this.deps.backend.mutation(api.participants.updateTokenActivity, {
88832
- sessionId: this.deps.sessionId,
88833
- chatroomId: opts.chatroomId,
88834
- role: opts.role
88835
- }).catch(() => {});
88836
- }
88837
- });
88838
- return { success: true, pid };
89797
+ });
89798
+ }
89799
+ async finalizeRunningSlot(key, slot, opts, spawnResult, wantResume) {
89800
+ const { pid } = spawnResult;
89801
+ this.deps.spawning.recordSpawn(opts.chatroomId);
89802
+ this.assignRunningSlotState(key, slot, opts, spawnResult, wantResume, pid);
89803
+ this.emitSpawnedAgentUpdate(opts, spawnResult, pid);
89804
+ try {
89805
+ await this.deps.persistence.persistAgentPid(this.deps.machineId, opts.chatroomId, opts.role, pid, opts.agentHarness);
89806
+ } catch {}
89807
+ this.registerSpawnCallbacks(slot, opts, spawnResult, pid);
89808
+ }
89809
+ async doEnsureRunning(key, slot, opts) {
89810
+ slot.state = "spawning";
89811
+ const wantResume = opts.wantResume;
89812
+ console.log(`[AgentProcessManager] harness start: role=${opts.role} harness=${opts.agentHarness} wantResume=${wantResume} reason=${opts.reason}`);
89813
+ try {
89814
+ const rateLimit = this.checkRateLimitGate(opts, slot);
89815
+ if (rateLimit)
89816
+ return rateLimit;
89817
+ const crashLoop = this.checkCrashLoopGate(opts, slot);
89818
+ if (crashLoop)
89819
+ return crashLoop;
89820
+ const workingDir = await this.validateWorkingDirGate(opts, slot);
89821
+ if (workingDir)
89822
+ return workingDir;
89823
+ const initPrompt = await this.fetchInitPromptResult(opts, slot);
89824
+ if (!initPrompt.ok)
89825
+ return initPrompt.result;
89826
+ const spawn5 = await this.spawnAgentForEnsureRunning(key, slot, opts, initPrompt, wantResume);
89827
+ if (!spawn5.ok)
89828
+ return spawn5.result;
89829
+ await this.finalizeRunningSlot(key, slot, opts, spawn5.spawnResult, wantResume);
89830
+ return { success: true, pid: spawn5.spawnResult.pid };
88839
89831
  } catch (e) {
88840
- slot.state = "idle";
88841
- slot.pendingOperation = undefined;
89832
+ this.resetSlotIdle(slot);
88842
89833
  return { success: false, error: `Unexpected error: ${e.message}` };
88843
89834
  }
88844
89835
  }
@@ -88851,64 +89842,75 @@ class AgentProcessManager {
88851
89842
  readHarnessReconnectMetadata(service3, pid) {
88852
89843
  return service3.getHarnessReconnectContext?.(pid);
88853
89844
  }
88854
- async doStop(key, slot, pid, opts) {
88855
- try {
88856
- const harness = slot.harness;
88857
- const service3 = harness ? this.deps.agentServices.get(harness) : undefined;
88858
- const supportsResume = harness ? getHarnessCapabilities(harness).supportsSessionResume : false;
88859
- const preserveForResume = shouldPreserveHarnessTeardown(opts.reason, supportsResume, Boolean(slot.harnessSessionId));
88860
- if (harness && slot.harnessSessionId) {
88861
- if (preserveForResume) {
88862
- const harnessMeta = service3 ? this.readHarnessReconnectMetadata(service3, pid) : undefined;
88863
- this.recordLastHarnessSession(key, {
88864
- harnessSessionId: slot.harnessSessionId,
88865
- harness,
88866
- agentName: harnessMeta?.agentName ?? "",
88867
- workingDir: slot.workingDir ?? "",
88868
- model: slot.model ?? harnessMeta?.model
88869
- });
88870
- } else {
88871
- this.clearLastHarnessSession(key);
88872
- }
88873
- } else if (!preserveForResume) {
89845
+ shouldPreserveHarnessOnStop(slot, opts) {
89846
+ const harness = slot.harness;
89847
+ const supportsResume = harness ? getHarnessCapabilities(harness).supportsSessionResume : false;
89848
+ return shouldPreserveHarnessTeardown(opts.reason, supportsResume, Boolean(slot.harnessSessionId));
89849
+ }
89850
+ recordHarnessSessionOnStop(key, slot, pid, service3) {
89851
+ const harness = slot.harness;
89852
+ const harnessMeta = service3 ? this.readHarnessReconnectMetadata(service3, pid) : undefined;
89853
+ this.recordLastHarnessSession(key, {
89854
+ harnessSessionId: slot.harnessSessionId,
89855
+ harness,
89856
+ agentName: harnessMeta?.agentName ?? "",
89857
+ workingDir: slot.workingDir ?? "",
89858
+ model: slot.model ?? harnessMeta?.model
89859
+ });
89860
+ }
89861
+ updateHarnessSessionOnStop(key, slot, pid, service3, preserveForResume) {
89862
+ const harness = slot.harness;
89863
+ if (harness && slot.harnessSessionId) {
89864
+ if (preserveForResume) {
89865
+ this.recordHarnessSessionOnStop(key, slot, pid, service3);
89866
+ } else {
88874
89867
  this.clearLastHarnessSession(key);
88875
89868
  }
88876
- if (service3) {
88877
- await service3.stop(pid, { preserveForResume });
88878
- service3.untrack(pid);
88879
- } else {
88880
- try {
88881
- this.deps.processes.kill(-pid, "SIGTERM");
88882
- } catch {}
88883
- let dead = false;
88884
- for (let i2 = 0;i2 < 20; i2++) {
88885
- await this.deps.clock.delay(500);
88886
- if (!isProcessAlive(this.deps.processes.kill, pid)) {
88887
- dead = true;
88888
- break;
88889
- }
88890
- }
88891
- if (!dead) {
88892
- try {
88893
- this.deps.processes.kill(-pid, "SIGKILL");
88894
- } catch {}
88895
- for (let i2 = 0;i2 < 10; i2++) {
88896
- await this.deps.clock.delay(500);
88897
- if (!isProcessAlive(this.deps.processes.kill, pid)) {
88898
- dead = true;
88899
- break;
88900
- }
88901
- }
88902
- }
88903
- for (const svc of this.deps.agentServices.values()) {
88904
- svc.untrack(pid);
89869
+ return;
89870
+ }
89871
+ if (!preserveForResume) {
89872
+ this.clearLastHarnessSession(key);
89873
+ }
89874
+ }
89875
+ preserveOrClearHarnessSessionOnStop(key, slot, pid, opts, service3) {
89876
+ const preserveForResume = this.shouldPreserveHarnessOnStop(slot, opts);
89877
+ this.updateHarnessSessionOnStop(key, slot, pid, service3, preserveForResume);
89878
+ return preserveForResume;
89879
+ }
89880
+ async killProcessWithFallback(pid) {
89881
+ try {
89882
+ this.deps.processes.kill(-pid, "SIGTERM");
89883
+ } catch {}
89884
+ let dead = false;
89885
+ for (let i2 = 0;i2 < 20; i2++) {
89886
+ await this.deps.clock.delay(500);
89887
+ if (!isProcessAlive(this.deps.processes.kill, pid)) {
89888
+ dead = true;
89889
+ break;
89890
+ }
89891
+ }
89892
+ if (!dead) {
89893
+ try {
89894
+ this.deps.processes.kill(-pid, "SIGKILL");
89895
+ } catch {}
89896
+ for (let i2 = 0;i2 < 10; i2++) {
89897
+ await this.deps.clock.delay(500);
89898
+ if (!isProcessAlive(this.deps.processes.kill, pid)) {
89899
+ break;
88905
89900
  }
88906
89901
  }
88907
- } catch {}
89902
+ }
89903
+ for (const svc of this.deps.agentServices.values()) {
89904
+ svc.untrack(pid);
89905
+ }
89906
+ }
89907
+ resetSlotAfterStop(slot) {
88908
89908
  slot.state = "idle";
88909
89909
  slot.pid = undefined;
88910
89910
  slot.startedAt = undefined;
88911
89911
  slot.pendingOperation = undefined;
89912
+ }
89913
+ recordStopExit(slot, pid, opts) {
88912
89914
  const exitArgs3 = {
88913
89915
  sessionId: this.deps.sessionId,
88914
89916
  machineId: this.deps.machineId,
@@ -88924,6 +89926,21 @@ class AgentProcessManager {
88924
89926
  console.log(` ⚠️ Failed to record agent exit event: ${err.message}`);
88925
89927
  this.queueExitRetry({ role: opts.role, args: exitArgs3 });
88926
89928
  });
89929
+ }
89930
+ async doStop(key, slot, pid, opts) {
89931
+ try {
89932
+ const harness = slot.harness;
89933
+ const service3 = harness ? this.deps.agentServices.get(harness) : undefined;
89934
+ const preserveForResume = this.preserveOrClearHarnessSessionOnStop(key, slot, pid, opts, service3);
89935
+ if (service3) {
89936
+ await service3.stop(pid, { preserveForResume });
89937
+ service3.untrack(pid);
89938
+ } else {
89939
+ await this.killProcessWithFallback(pid);
89940
+ }
89941
+ } catch {}
89942
+ this.resetSlotAfterStop(slot);
89943
+ this.recordStopExit(slot, pid, opts);
88927
89944
  try {
88928
89945
  await this.deps.persistence.clearAgentPid(this.deps.machineId, opts.chatroomId, opts.role);
88929
89946
  } catch {}
@@ -88934,12 +89951,15 @@ var AGENT_EXIT_RETRY_INTERVAL_MS = 1e4;
88934
89951
  var init_agent_process_manager = __esm(() => {
88935
89952
  init_generator();
88936
89953
  init_types();
89954
+ init_esm();
88937
89955
  init_turn_completed_backend();
88938
89956
  init_api3();
88939
89957
  init_orphan_tracker();
88940
89958
  init_agent_lifecycle();
88941
89959
  init_classify_resume_storm_reason();
88942
89960
  init_handle_turn_completed();
89961
+ init_agent_lifecycle_runtime();
89962
+ init_agent_lifecycle_types();
88943
89963
  });
88944
89964
 
88945
89965
  // src/infrastructure/services/harness-spawning/rate-limiter.ts
@@ -89505,6 +90525,7 @@ var init_models_refresh = __esm(() => {
89505
90525
  var startObservedSyncSubscriptionEffect = (wsClient2) => exports_Effect.gen(function* () {
89506
90526
  const session2 = yield* DaemonSessionService;
89507
90527
  const effectContext2 = yield* exports_Effect.context();
90528
+ const runtime4 = yield* exports_Effect.runtime();
89508
90529
  console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Starting observed-sync subscription (reactive)`);
89509
90530
  const observedWorkingDirs = new Map;
89510
90531
  const chatroomRefreshState = new Map;
@@ -89560,7 +90581,7 @@ var startObservedSyncSubscriptionEffect = (wsClient2) => exports_Effect.gen(func
89560
90581
  console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed-sync subscription stopped`);
89561
90582
  }
89562
90583
  };
89563
- function handleObservedChange(observed) {
90584
+ function collectWorkingDirChanges(observed) {
89564
90585
  const newWorkingDirs = new Set;
89565
90586
  const refreshedWorkingDirs = new Set;
89566
90587
  for (const chatroom of observed) {
@@ -89578,55 +90599,77 @@ var startObservedSyncSubscriptionEffect = (wsClient2) => exports_Effect.gen(func
89578
90599
  newWorkingDirs.add(wd);
89579
90600
  }
89580
90601
  }
90602
+ return { newWorkingDirs, refreshedWorkingDirs };
90603
+ }
90604
+ function pruneStaleChatroomRefreshState(observed) {
89581
90605
  for (const [chatroomId] of chatroomRefreshState) {
89582
90606
  const stillObserved = observed.some((c) => c.chatroomId === chatroomId);
89583
90607
  if (!stillObserved) {
89584
90608
  chatroomRefreshState.delete(chatroomId);
89585
90609
  }
89586
90610
  }
90611
+ }
90612
+ function removeUnobservedWorkingDirs(newWorkingDirs) {
89587
90613
  const currentWorkingDirs = new Set(observedWorkingDirs.keys());
89588
- let addedCount = 0;
89589
90614
  let removedCount = 0;
89590
90615
  for (const wd of currentWorkingDirs) {
89591
- if (!newWorkingDirs.has(wd)) {
89592
- const state = observedWorkingDirs.get(wd);
89593
- if (state) {
89594
- clearInterval(state.intervalHandle);
89595
- observedWorkingDirs.delete(wd);
89596
- const skips = skippedPushCount.get(wd) ?? 0;
89597
- if (skips > 0) {
89598
- console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Stopped observing ${wd} (skipped ${skips} overlapping pushes)`);
89599
- } else {
89600
- console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Stopped observing ${wd}`);
89601
- }
89602
- skippedPushCount.delete(wd);
89603
- pendingRefresh.delete(wd);
89604
- removedCount++;
89605
- }
90616
+ if (newWorkingDirs.has(wd)) {
90617
+ continue;
90618
+ }
90619
+ const state = observedWorkingDirs.get(wd);
90620
+ if (!state) {
90621
+ continue;
90622
+ }
90623
+ clearInterval(state.intervalHandle);
90624
+ observedWorkingDirs.delete(wd);
90625
+ const skips = skippedPushCount.get(wd) ?? 0;
90626
+ if (skips > 0) {
90627
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Stopped observing ${wd} (skipped ${skips} overlapping pushes)`);
90628
+ } else {
90629
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Stopped observing ${wd}`);
89606
90630
  }
90631
+ skippedPushCount.delete(wd);
90632
+ pendingRefresh.delete(wd);
90633
+ removedCount++;
89607
90634
  }
90635
+ return removedCount;
90636
+ }
90637
+ function addNewlyObservedWorkingDirs(newWorkingDirs) {
90638
+ let addedCount = 0;
89608
90639
  for (const wd of newWorkingDirs) {
90640
+ if (observedWorkingDirs.has(wd)) {
90641
+ continue;
90642
+ }
90643
+ observedWorkingDirs.set(wd, {
90644
+ intervalHandle: setInterval(() => {
90645
+ schedulePushForWorkingDir(wd, "safety-poll");
90646
+ }, OBSERVED_SAFETY_POLL_MS),
90647
+ pushInFlight: false
90648
+ });
90649
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Started observing ${wd}`);
90650
+ schedulePushForWorkingDir(wd, "safety-poll");
90651
+ addedCount++;
90652
+ }
90653
+ return addedCount;
90654
+ }
90655
+ function triggerRefreshedWorkingDirs(refreshedWorkingDirs) {
90656
+ for (const wd of refreshedWorkingDirs) {
89609
90657
  if (!observedWorkingDirs.has(wd)) {
89610
- observedWorkingDirs.set(wd, {
89611
- intervalHandle: setInterval(() => {
89612
- schedulePushForWorkingDir(wd, "safety-poll");
89613
- }, OBSERVED_SAFETY_POLL_MS),
89614
- pushInFlight: false
89615
- });
89616
- console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Started observing ${wd}`);
89617
- schedulePushForWorkingDir(wd, "safety-poll");
89618
- addedCount++;
90658
+ continue;
89619
90659
  }
90660
+ console.log(`[${formatTimestamp()}] \uD83D\uDD04 Refresh triggered for ${wd}`);
90661
+ schedulePushForWorkingDir(wd, "refresh");
89620
90662
  }
90663
+ }
90664
+ function handleObservedChange(observed) {
90665
+ const { newWorkingDirs, refreshedWorkingDirs } = collectWorkingDirChanges(observed);
90666
+ pruneStaleChatroomRefreshState(observed);
90667
+ const removedCount = removeUnobservedWorkingDirs(newWorkingDirs);
90668
+ const addedCount = addNewlyObservedWorkingDirs(newWorkingDirs);
89621
90669
  if (addedCount > 0 || removedCount > 0) {
89622
90670
  console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observing ${observedWorkingDirs.size} working dir(s)`);
89623
90671
  }
89624
- for (const wd of refreshedWorkingDirs) {
89625
- if (observedWorkingDirs.has(wd)) {
89626
- console.log(`[${formatTimestamp()}] \uD83D\uDD04 Refresh triggered for ${wd}`);
89627
- schedulePushForWorkingDir(wd, "refresh");
89628
- }
89629
- }
90672
+ triggerRefreshedWorkingDirs(refreshedWorkingDirs);
89630
90673
  }
89631
90674
  function schedulePushForWorkingDir(workingDir, reason = "safety-poll") {
89632
90675
  if (stopped)
@@ -89644,9 +90687,13 @@ var startObservedSyncSubscriptionEffect = (wsClient2) => exports_Effect.gen(func
89644
90687
  return;
89645
90688
  }
89646
90689
  state.pushInFlight = true;
89647
- pushForWorkingDir(workingDir, reason).catch((err) => {
89648
- console.warn(`[${formatTimestamp()}] ⚠️ Push failed for ${workingDir}: ${getErrorMessage(err)}`);
89649
- }).finally(() => {
90690
+ pushForWorkingDir(workingDir, reason);
90691
+ }
90692
+ function pushForWorkingDir(workingDir, reason = "safety-poll") {
90693
+ exports_Runtime.runFork(runtime4)(exports_Effect.all([
90694
+ pushSingleWorkspaceGitSummaryForObservedEffect(workingDir, reason),
90695
+ pushSingleWorkspaceCommandsEffect(workingDir)
90696
+ ], { concurrency: "unbounded" }).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[${formatTimestamp()}] ⚠️ Observed sync failed for ${workingDir}: ${getErrorMessage(err)}`))), exports_Effect.ensuring(exports_Effect.sync(() => {
89650
90697
  const s = observedWorkingDirs.get(workingDir);
89651
90698
  if (s) {
89652
90699
  s.pushInFlight = false;
@@ -89655,15 +90702,7 @@ var startObservedSyncSubscriptionEffect = (wsClient2) => exports_Effect.gen(func
89655
90702
  schedulePushForWorkingDir(workingDir, "refresh");
89656
90703
  }
89657
90704
  }
89658
- });
89659
- }
89660
- async function pushForWorkingDir(workingDir, reason = "safety-poll") {
89661
- await exports_Effect.runPromise(pushSingleWorkspaceGitSummaryForObservedEffect(workingDir, reason).pipe(exports_Effect.provide(effectContext2))).catch((err) => {
89662
- console.warn(`[${formatTimestamp()}] ⚠️ Observed git summary push failed for ${workingDir}: ${getErrorMessage(err)}`);
89663
- });
89664
- await exports_Effect.runPromise(pushSingleWorkspaceCommandsEffect(workingDir).pipe(exports_Effect.provide(effectContext2))).catch((err) => {
89665
- console.warn(`[${formatTimestamp()}] ⚠️ Command sync failed for ${workingDir}: ${getErrorMessage(err)}`);
89666
- });
90705
+ }))));
89667
90706
  }
89668
90707
  });
89669
90708
  var init_observed_sync = __esm(() => {
@@ -89736,36 +90775,21 @@ var init_workspace_list_subscription = __esm(() => {
89736
90775
  });
89737
90776
 
89738
90777
  // src/commands/machine/daemon-start/command-loop.ts
89739
- function evictStaleDedupEntries(tracker) {
89740
- const evictBefore = Date.now() - AGENT_REQUEST_DEADLINE_MS;
89741
- for (const [id3, ts] of tracker.commandIds) {
89742
- if (ts < evictBefore)
89743
- tracker.commandIds.delete(id3);
89744
- }
89745
- for (const [id3, ts] of tracker.pingIds) {
89746
- if (ts < evictBefore)
89747
- tracker.pingIds.delete(id3);
89748
- }
89749
- for (const [id3, ts] of tracker.gitRefreshIds) {
89750
- if (ts < evictBefore)
89751
- tracker.gitRefreshIds.delete(id3);
89752
- }
89753
- for (const [id3, ts] of tracker.capabilitiesRefreshIds) {
90778
+ function evictStaleEntries(entries2, evictBefore) {
90779
+ for (const [id3, ts] of entries2) {
89754
90780
  if (ts < evictBefore)
89755
- tracker.capabilitiesRefreshIds.delete(id3);
89756
- }
89757
- for (const [id3, ts] of tracker.localActionIds) {
89758
- if (ts < evictBefore)
89759
- tracker.localActionIds.delete(id3);
89760
- }
89761
- for (const [id3, ts] of tracker.commandRunIds) {
89762
- if (ts < evictBefore)
89763
- tracker.commandRunIds.delete(id3);
89764
- }
89765
- for (const [id3, ts] of tracker.commandStopIds) {
89766
- if (ts < evictBefore)
89767
- tracker.commandStopIds.delete(id3);
90781
+ entries2.delete(id3);
89768
90782
  }
90783
+ }
90784
+ function evictStaleDedupEntries(tracker) {
90785
+ const evictBefore = Date.now() - AGENT_REQUEST_DEADLINE_MS;
90786
+ evictStaleEntries(tracker.commandIds, evictBefore);
90787
+ evictStaleEntries(tracker.pingIds, evictBefore);
90788
+ evictStaleEntries(tracker.gitRefreshIds, evictBefore);
90789
+ evictStaleEntries(tracker.capabilitiesRefreshIds, evictBefore);
90790
+ evictStaleEntries(tracker.localActionIds, evictBefore);
90791
+ evictStaleEntries(tracker.commandRunIds, evictBefore);
90792
+ evictStaleEntries(tracker.commandStopIds, evictBefore);
89769
90793
  processManager.evictStalePendingStops();
89770
90794
  }
89771
90795
  function handleRequestStartEffect(event, tracker) {
@@ -89936,6 +90960,7 @@ var init_command_loop = __esm(() => {
89936
90960
  startCommandLoopEffect = exports_Effect.gen(function* () {
89937
90961
  const session2 = yield* DaemonSessionService;
89938
90962
  const effectContext2 = yield* exports_Effect.context();
90963
+ const runtime4 = yield* exports_Effect.runtime();
89939
90964
  const observedSyncEnabled = featureFlags.observedSyncEnabled ?? false;
89940
90965
  let heartbeatCount = 0;
89941
90966
  const heartbeatTimer = setInterval(() => {
@@ -89946,15 +90971,9 @@ var init_command_loop = __esm(() => {
89946
90971
  heartbeatCount++;
89947
90972
  console.log(`[${formatTimestamp()}] \uD83D\uDC93 Daemon heartbeat #${heartbeatCount} OK`);
89948
90973
  if (!observedSyncEnabled) {
89949
- exports_Effect.runPromise(pushGitStateEffect.pipe(exports_Effect.provide(effectContext2))).catch((err) => {
89950
- console.warn(`[${formatTimestamp()}] ⚠️ Git state push failed: ${getErrorMessage(err)}`);
89951
- });
89952
- exports_Effect.runPromise(pushCommandsEffect.pipe(exports_Effect.provide(effectContext2))).catch((err) => {
89953
- console.warn(`[${formatTimestamp()}] ⚠️ Command sync failed: ${getErrorMessage(err)}`);
89954
- });
89955
- exports_Effect.runPromise(syncCommitDetailsEffect().pipe(exports_Effect.provide(effectContext2))).catch((err) => {
89956
- console.warn(`[${formatTimestamp()}] ⚠️ Commit detail sync failed: ${getErrorMessage(err)}`);
89957
- });
90974
+ exports_Runtime.runFork(runtime4)(exports_Effect.all([pushGitStateEffect, pushCommandsEffect, syncCommitDetailsEffect()], {
90975
+ concurrency: "unbounded"
90976
+ }).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[${formatTimestamp()}] ⚠️ Heartbeat sync failed: ${getErrorMessage(err)}`)))));
89958
90977
  }
89959
90978
  }).catch((err) => {
89960
90979
  console.warn(`[${formatTimestamp()}] ⚠️ Daemon heartbeat failed: ${getErrorMessage(err)}`);
@@ -89976,9 +90995,9 @@ var init_command_loop = __esm(() => {
89976
90995
  if (observedSyncEnabled) {
89977
90996
  console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed-sync enabled, skipping immediate push`);
89978
90997
  } else {
89979
- exports_Effect.runPromise(pushGitStateEffect.pipe(exports_Effect.provide(effectContext2))).catch(() => {});
89980
- exports_Effect.runPromise(pushCommandsEffect.pipe(exports_Effect.provide(effectContext2))).catch(() => {});
89981
- exports_Effect.runPromise(syncCommitDetailsEffect().pipe(exports_Effect.provide(effectContext2))).catch(() => {});
90998
+ exports_Runtime.runFork(runtime4)(exports_Effect.all([pushGitStateEffect, pushCommandsEffect, syncCommitDetailsEffect()], {
90999
+ concurrency: "unbounded"
91000
+ }).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[${formatTimestamp()}] ⚠️ Startup sync failed: ${getErrorMessage(err)}`)))));
89982
91001
  }
89983
91002
  const PROCESS_KILL_TIMEOUT_MS = 6000;
89984
91003
  const CLOSE_TIMEOUT_MS = 3000;
@@ -90011,43 +91030,46 @@ var init_command_loop = __esm(() => {
90011
91030
  return;
90012
91031
  isShuttingDown = true;
90013
91032
  console.log(`
90014
- [${formatTimestamp()}] Shutting down... (press Ctrl+C again to force)`);
91033
+ [${formatTimestamp()}] Shutting down... (press Ctrl+B again to force)`);
91034
+ const watchdog = setupShutdownWatchdog();
91035
+ clearInterval(heartbeatTimer);
91036
+ stopSubscriptions();
91037
+ await runDaemonShutdownEffect();
91038
+ await closeAllSessionsAndHarnesses();
91039
+ clearTimeout(watchdog);
91040
+ releaseLock();
91041
+ process.exit(0);
91042
+ };
91043
+ const setupShutdownWatchdog = () => {
90015
91044
  const watchdog = setTimeout(() => {
90016
91045
  console.error(`[${formatTimestamp()}] Shutdown timed out — forcing exit.`);
90017
91046
  forceExit(1);
90018
91047
  }, SHUTDOWN_WATCHDOG_MS);
90019
91048
  watchdog.unref?.();
90020
- clearInterval(heartbeatTimer);
90021
- if (gitSubscriptionHandle)
90022
- gitSubscriptionHandle.stop();
90023
- if (fileContentSubscriptionHandle)
90024
- fileContentSubscriptionHandle.stop();
90025
- if (fileTreeSubscriptionHandle)
90026
- fileTreeSubscriptionHandle.stop();
90027
- if (workspaceListSubscriptionHandle)
90028
- workspaceListSubscriptionHandle.stop();
90029
- if (observedSyncSubscriptionHandle)
90030
- observedSyncSubscriptionHandle.stop();
90031
- if (logObserverSubscriptionHandle)
90032
- logObserverSubscriptionHandle.stop();
90033
- if (pendingPromptSubscriptionHandle)
90034
- pendingPromptSubscriptionHandle.stop();
90035
- if (pendingHarnessSessionSubscriptionHandle)
90036
- pendingHarnessSessionSubscriptionHandle.stop();
90037
- if (commandSubscriptionHandle)
90038
- commandSubscriptionHandle.stop();
90039
- if (lifecycleManager)
90040
- lifecycleManager.stopMonitoring();
91049
+ return watchdog;
91050
+ };
91051
+ const stopSubscriptions = () => {
91052
+ gitSubscriptionHandle?.stop();
91053
+ fileContentSubscriptionHandle?.stop();
91054
+ fileTreeSubscriptionHandle?.stop();
91055
+ workspaceListSubscriptionHandle?.stop();
91056
+ observedSyncSubscriptionHandle?.stop();
91057
+ logObserverSubscriptionHandle?.stop();
91058
+ pendingPromptSubscriptionHandle?.stop();
91059
+ pendingHarnessSessionSubscriptionHandle?.stop();
91060
+ commandSubscriptionHandle?.stop();
91061
+ lifecycleManager?.stopMonitoring();
91062
+ };
91063
+ const runDaemonShutdownEffect = async () => {
90041
91064
  await withTimeout3(exports_Effect.runPromise(onDaemonShutdownEffect.pipe(exports_Effect.provide(effectContext2))), PROCESS_KILL_TIMEOUT_MS);
91065
+ };
91066
+ const closeAllSessionsAndHarnesses = async () => {
90042
91067
  for (const handle of activeSessions.values()) {
90043
91068
  await withTimeout3(handle.close(), CLOSE_TIMEOUT_MS);
90044
91069
  }
90045
91070
  for (const harness of harnesses.values()) {
90046
91071
  await withTimeout3(harness.close(), CLOSE_TIMEOUT_MS);
90047
91072
  }
90048
- clearTimeout(watchdog);
90049
- releaseLock();
90050
- process.exit(0);
90051
91073
  };
90052
91074
  const handleSignal = (signal) => {
90053
91075
  signalCount += 1;
@@ -91316,4 +92338,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
91316
92338
  });
91317
92339
  program2.parse();
91318
92340
 
91319
- //# debugId=C790B7A198F818CB64756E2164756E21
92341
+ //# debugId=D908929E6EC55FCC64756E2164756E21