chatroom-cli 1.55.5 → 1.55.7

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
 
@@ -27143,6 +27327,96 @@ var init_opencode = __esm(() => {
27143
27327
  init_opencode_agent_service();
27144
27328
  });
27145
27329
 
27330
+ // src/infrastructure/services/remote-agents/agent-log-format.ts
27331
+ function buildAgentLogPrefix(agent, context5) {
27332
+ const roleTag = context5.role ?? "unknown";
27333
+ const chatroomSuffix = context5.chatroomId ? `@${context5.chatroomId.slice(-6)}` : "";
27334
+ return `[${agent}:${roleTag}${chatroomSuffix}`;
27335
+ }
27336
+ function formatAgentLogLine(prefix, kind, payload) {
27337
+ return payload !== undefined && payload !== "" ? `${prefix} ${kind}] ${payload}` : `${prefix} ${kind}]`;
27338
+ }
27339
+ function formatTimestampedLogLine(role, kind, payload, now) {
27340
+ const ts = now ? now() : new Date().toISOString();
27341
+ return `[${ts}] role:${role} ${kind}]${payload ? ` ${payload}` : ""}`;
27342
+ }
27343
+ function formatBashRunningPayload(command) {
27344
+ return `${BASH_RUNNING_PREFIX} ${command}`;
27345
+ }
27346
+ function isBashLikeToolName(name) {
27347
+ return /bash|shell|terminal|command/i.test(name);
27348
+ }
27349
+ function extractBashCommandFromToolInput(name, input) {
27350
+ if (!isBashLikeToolName(name))
27351
+ return null;
27352
+ if (input && typeof input === "object" && "command" in input) {
27353
+ return String(input.command);
27354
+ }
27355
+ if (typeof input === "string")
27356
+ return input;
27357
+ return null;
27358
+ }
27359
+ function resolveBashCommandForLog(name, input) {
27360
+ if (!isBashLikeToolName(name))
27361
+ return null;
27362
+ const extracted = extractBashCommandFromToolInput(name, input);
27363
+ if (extracted !== null)
27364
+ return extracted;
27365
+ if (input != null)
27366
+ return JSON.stringify(input);
27367
+ return "";
27368
+ }
27369
+ function extractBashCommandFromCursorToolCall(toolCall) {
27370
+ if (!toolCall || typeof toolCall !== "object")
27371
+ return null;
27372
+ for (const [key, value] of Object.entries(toolCall)) {
27373
+ if (!isBashLikeToolName(key) || !value || typeof value !== "object")
27374
+ continue;
27375
+ const args2 = value.args;
27376
+ if (args2 && typeof args2 === "object" && "command" in args2) {
27377
+ return String(args2.command);
27378
+ }
27379
+ }
27380
+ return null;
27381
+ }
27382
+ function appendToolInputToPayload(base, input, toolName) {
27383
+ if (!input || typeof input === "object" && Object.keys(input).length === 0) {
27384
+ return base;
27385
+ }
27386
+ const inp = input;
27387
+ if (toolName === "bash" && typeof inp.command === "string") {
27388
+ return `${base}: ${inp.command}`;
27389
+ }
27390
+ const inputStr = typeof inp === "string" ? inp : JSON.stringify(inp);
27391
+ return `${base}: ${inputStr}`;
27392
+ }
27393
+ function createAgentLogWriter(prefix, options) {
27394
+ const target = options?.target ?? process.stdout;
27395
+ const emitLogLine = options?.emitLogLine;
27396
+ const writeLine = (formatted) => {
27397
+ target.write(`${formatted}
27398
+ `);
27399
+ emitLogLine?.(formatted);
27400
+ };
27401
+ return {
27402
+ write(kind, payload) {
27403
+ writeLine(formatAgentLogLine(prefix, kind, payload));
27404
+ },
27405
+ writeLine,
27406
+ flushBufferedLines(buffer, kind) {
27407
+ if (!buffer)
27408
+ return buffer;
27409
+ for (const line of buffer.split(`
27410
+ `)) {
27411
+ if (line)
27412
+ writeLine(formatAgentLogLine(prefix, kind, line));
27413
+ }
27414
+ return "";
27415
+ }
27416
+ };
27417
+ }
27418
+ var BASH_TOOL_KIND = "tool: bash", BASH_RUNNING_PREFIX = "running:";
27419
+
27146
27420
  // src/infrastructure/services/remote-agents/pi/pi-rpc-reader.ts
27147
27421
  import { createInterface } from "node:readline";
27148
27422
 
@@ -27281,21 +27555,30 @@ var init_pi_agent_service = __esm(() => {
27281
27555
  async resumeFromDaemonMemory(options, stored) {
27282
27556
  const { prompt, systemPrompt, model, context: context5 } = options;
27283
27557
  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
- });
27558
+ let childProcess;
27559
+ try {
27560
+ childProcess = this.spawnPiRpcProcess({
27561
+ workingDir: stored.workingDir,
27562
+ systemPrompt,
27563
+ model: modelForSession,
27564
+ sessionId: stored.harnessSessionId
27565
+ });
27566
+ await this.waitForSpawnReady(childProcess);
27567
+ await this.writePrompt(childProcess, prompt);
27568
+ return this.wireRpcProcess({
27569
+ childProcess,
27570
+ context: context5,
27571
+ workingDir: stored.workingDir,
27572
+ model: modelForSession,
27573
+ harnessSessionId: stored.harnessSessionId
27574
+ });
27575
+ } catch (err) {
27576
+ const reason = err instanceof Error ? err.message : String(err);
27577
+ process.stderr.write(`[${new Date().toISOString()}] role:${context5.role} daemon-resume-fallback] ${reason} — cold spawning
27578
+ `);
27579
+ childProcess?.kill();
27580
+ return this.spawn(options);
27581
+ }
27299
27582
  }
27300
27583
  async resumeTurn(pid, prompt) {
27301
27584
  const child = this.childProcesses.get(pid);
@@ -27423,9 +27706,7 @@ var init_pi_agent_service = __esm(() => {
27423
27706
  this.childProcesses.set(pid, childProcess);
27424
27707
  this.trackedSessions.set(pid, { harnessSessionId, workingDir, model });
27425
27708
  const entry = this.registerProcess(pid, context5);
27426
- const roleTag = context5.role ?? "unknown";
27427
- const chatroomSuffix = context5.chatroomId ? `@${context5.chatroomId.slice(-6)}` : "";
27428
- const logPrefix = `[pi:${roleTag}${chatroomSuffix}`;
27709
+ const logPrefix = buildAgentLogPrefix("pi", context5);
27429
27710
  const outputCallbacks = [];
27430
27711
  const logLineCallbacks = [];
27431
27712
  const agentEndCallbacks = [];
@@ -27461,11 +27742,7 @@ var init_pi_agent_service = __esm(() => {
27461
27742
  onOutput,
27462
27743
  onLogLine
27463
27744
  };
27464
- const writeFormattedLogLine = (formatted) => {
27465
- process.stdout.write(`${formatted}
27466
- `);
27467
- emitLogLine(formatted);
27468
- };
27745
+ const log4 = createAgentLogWriter(logPrefix, { emitLogLine });
27469
27746
  const onStderrData = (chunk2) => {
27470
27747
  entry.lastOutputAt = Date.now();
27471
27748
  for (const cb of outputCallbacks)
@@ -27486,15 +27763,9 @@ var init_pi_agent_service = __esm(() => {
27486
27763
  let textBuffer = "";
27487
27764
  let thinkingBuffer = "";
27488
27765
  const flushBufferedLines = (buffer, kind, clear) => {
27489
- if (!buffer)
27490
- return;
27491
- for (const line of buffer.split(`
27492
- `)) {
27493
- if (line) {
27494
- writeFormattedLogLine(`${logPrefix} ${kind}] ${line}`);
27495
- }
27496
- }
27766
+ const remaining = log4.flushBufferedLines(buffer, kind);
27497
27767
  clear();
27768
+ return remaining;
27498
27769
  };
27499
27770
  const flushText = () => flushBufferedLines(textBuffer, "text", () => {
27500
27771
  textBuffer = "";
@@ -27529,20 +27800,24 @@ var init_pi_agent_service = __esm(() => {
27529
27800
  reader.onAgentEnd(() => {
27530
27801
  flushText();
27531
27802
  flushThinking();
27532
- process.stdout.write(`${logPrefix} agent_end]
27533
- `);
27803
+ log4.write("agent_end");
27534
27804
  for (const cb of agentEndCallbacks)
27535
27805
  cb();
27536
27806
  });
27537
27807
  reader.onToolCall((name, toolArgs) => {
27538
27808
  flushText();
27539
27809
  flushThinking();
27810
+ const bashCmd = resolveBashCommandForLog(name, toolArgs);
27811
+ if (bashCmd !== null) {
27812
+ log4.write(BASH_TOOL_KIND, formatBashRunningPayload(bashCmd));
27813
+ return;
27814
+ }
27540
27815
  const argsStr = toolArgs != null ? ` args: ${JSON.stringify(toolArgs)}` : "";
27541
- writeFormattedLogLine(`${logPrefix} tool: ${name}${argsStr}]`);
27816
+ log4.write("tool", `${name}${argsStr}`);
27542
27817
  });
27543
27818
  reader.onToolResult((name, result) => {
27544
27819
  const resultStr = typeof result === "string" ? result : JSON.stringify(result);
27545
- writeFormattedLogLine(`${logPrefix} tool_result: ${name} result: ${resultStr}]`);
27820
+ log4.write("tool_result", `${name} result: ${resultStr}`);
27546
27821
  });
27547
27822
  attachStderr(childProcess.stderr);
27548
27823
  return {
@@ -27748,9 +28023,7 @@ ${options.prompt}`;
27748
28023
  const pid = childProcess.pid;
27749
28024
  const context5 = options.context;
27750
28025
  const entry = this.registerProcess(pid, context5);
27751
- const roleTag = context5.role ?? "unknown";
27752
- const chatroomSuffix = context5.chatroomId ? `@${context5.chatroomId.slice(-6)}` : "";
27753
- const logPrefix = `[cursor:${roleTag}${chatroomSuffix}`;
28026
+ const logPrefix = buildAgentLogPrefix("cursor", context5);
27754
28027
  const outputCallbacks = [];
27755
28028
  if (childProcess.stdout) {
27756
28029
  const reader = new CursorStreamReader(childProcess.stdout);
@@ -27761,7 +28034,7 @@ ${options.prompt}`;
27761
28034
  for (const line of textBuffer.split(`
27762
28035
  `)) {
27763
28036
  if (line)
27764
- process.stdout.write(`${logPrefix} text] ${line}
28037
+ process.stdout.write(`${formatAgentLogLine(logPrefix, "text", line)}
27765
28038
  `);
27766
28039
  }
27767
28040
  textBuffer = "";
@@ -27782,17 +28055,23 @@ ${options.prompt}`;
27782
28055
  });
27783
28056
  reader.onAgentEnd(() => {
27784
28057
  flushText();
27785
- process.stdout.write(`${logPrefix} agent_end]
28058
+ process.stdout.write(`${formatAgentLogLine(logPrefix, "agent_end")}
27786
28059
  `);
27787
28060
  });
27788
28061
  reader.onToolCall((callId, toolCall) => {
27789
28062
  flushText();
27790
- process.stdout.write(`${logPrefix} tool: ${callId} ${JSON.stringify(toolCall)}]
28063
+ const bashCmd = extractBashCommandFromCursorToolCall(toolCall);
28064
+ if (bashCmd !== null) {
28065
+ process.stdout.write(`${formatAgentLogLine(logPrefix, BASH_TOOL_KIND, formatBashRunningPayload(bashCmd))}
28066
+ `);
28067
+ return;
28068
+ }
28069
+ process.stdout.write(`${formatAgentLogLine(logPrefix, "tool", `${callId} ${JSON.stringify(toolCall)}`)}
27791
28070
  `);
27792
28071
  });
27793
28072
  reader.onToolResult((callId) => {
27794
28073
  flushText();
27795
- process.stdout.write(`${logPrefix} tool_result: ${callId}]
28074
+ process.stdout.write(`${formatAgentLogLine(logPrefix, "tool_result", callId)}
27796
28075
  `);
27797
28076
  });
27798
28077
  if (childProcess.stderr) {
@@ -28183,9 +28462,7 @@ var init_claude_code_agent_service = __esm(() => {
28183
28462
  const pid = childProcess.pid;
28184
28463
  const context5 = options.context;
28185
28464
  const entry = this.registerProcess(pid, context5);
28186
- const roleTag = context5.role ?? "unknown";
28187
- const chatroomSuffix = context5.chatroomId ? `@${context5.chatroomId.slice(-6)}` : "";
28188
- const logPrefix = `[claude:${roleTag}${chatroomSuffix}]`;
28465
+ const logPrefix = buildAgentLogPrefix("claude", context5);
28189
28466
  const outputCallbacks = [];
28190
28467
  if (childProcess.stdout) {
28191
28468
  const reader = new ClaudeStreamReader(childProcess.stdout);
@@ -28197,7 +28474,7 @@ var init_claude_code_agent_service = __esm(() => {
28197
28474
  for (const line of textBuffer.split(`
28198
28475
  `)) {
28199
28476
  if (line)
28200
- process.stdout.write(`${logPrefix} text] ${line}
28477
+ process.stdout.write(`${formatAgentLogLine(logPrefix, "text", line)}
28201
28478
  `);
28202
28479
  }
28203
28480
  textBuffer = "";
@@ -28208,7 +28485,7 @@ var init_claude_code_agent_service = __esm(() => {
28208
28485
  for (const line of thinkingBuffer.split(`
28209
28486
  `)) {
28210
28487
  if (line)
28211
- process.stdout.write(`${logPrefix} thinking] ${line}
28488
+ process.stdout.write(`${formatAgentLogLine(logPrefix, "thinking", line)}
28212
28489
  `);
28213
28490
  }
28214
28491
  thinkingBuffer = "";
@@ -28239,8 +28516,16 @@ var init_claude_code_agent_service = __esm(() => {
28239
28516
  });
28240
28517
  reader.onToolUse((name, input) => {
28241
28518
  entry.lastOutputAt = Date.now();
28519
+ const bashCmd = extractBashCommandFromToolInput(name, input);
28520
+ if (bashCmd !== null) {
28521
+ process.stdout.write(`${formatAgentLogLine(logPrefix, BASH_TOOL_KIND, formatBashRunningPayload(bashCmd))}
28522
+ `);
28523
+ for (const cb of outputCallbacks)
28524
+ cb();
28525
+ return;
28526
+ }
28242
28527
  const inputStr = JSON.stringify(input);
28243
- process.stdout.write(`${logPrefix} tool] ${name}(${inputStr.slice(0, 100)}${inputStr.length > 100 ? "..." : ""})
28528
+ process.stdout.write(`${formatAgentLogLine(logPrefix, "tool", `${name}(${inputStr.slice(0, 100)}${inputStr.length > 100 ? "..." : ""})`)}
28244
28529
  `);
28245
28530
  for (const cb of outputCallbacks)
28246
28531
  cb();
@@ -28633,19 +28918,25 @@ class CursorSdkStreamAdapter {
28633
28918
  case "assistant":
28634
28919
  this.handleAssistant(message);
28635
28920
  break;
28636
- case "tool_call":
28921
+ case "tool_call": {
28637
28922
  this.flushText();
28638
- this.writeLine(`${this.logPrefix} tool: ${message.call_id} ${message.name} ${JSON.stringify({ status: message.status, args: message.args })}]`);
28923
+ const bashCmd = extractBashCommandFromToolInput(message.name, message.args);
28924
+ if (bashCmd !== null) {
28925
+ this.writeLine(formatAgentLogLine(this.logPrefix, BASH_TOOL_KIND, formatBashRunningPayload(bashCmd)));
28926
+ break;
28927
+ }
28928
+ this.writeLine(formatAgentLogLine(this.logPrefix, `tool: ${message.call_id} ${message.name} ${JSON.stringify({ status: message.status, args: message.args })}`));
28639
28929
  break;
28930
+ }
28640
28931
  case "status":
28641
- this.writeLine(`${this.logPrefix} status: ${message.status}]`);
28932
+ this.writeLine(formatAgentLogLine(this.logPrefix, `status: ${message.status}`));
28642
28933
  break;
28643
28934
  case "thinking":
28644
- this.writeLine(`${this.logPrefix} thinking] ${message.text}`);
28935
+ this.writeLine(formatAgentLogLine(this.logPrefix, "thinking", message.text));
28645
28936
  break;
28646
28937
  case "system":
28647
28938
  if (message.subtype === "init") {
28648
- this.writeLine(`${this.logPrefix} system: init]`);
28939
+ this.writeLine(formatAgentLogLine(this.logPrefix, "system: init"));
28649
28940
  }
28650
28941
  break;
28651
28942
  default:
@@ -28676,7 +28967,7 @@ class CursorSdkStreamAdapter {
28676
28967
  for (const line of this.textBuffer.split(`
28677
28968
  `)) {
28678
28969
  if (line)
28679
- this.writeLine(`${this.logPrefix} text] ${line}`);
28970
+ this.writeLine(formatAgentLogLine(this.logPrefix, "text", line));
28680
28971
  }
28681
28972
  this.textBuffer = "";
28682
28973
  }
@@ -28685,7 +28976,7 @@ class CursorSdkStreamAdapter {
28685
28976
  return;
28686
28977
  this.agentEndEmitted = true;
28687
28978
  this.flushText();
28688
- this.writeLine(`${this.logPrefix} agent_end]`);
28979
+ this.writeLine(formatAgentLogLine(this.logPrefix, "agent_end"));
28689
28980
  for (const cb of this.agentEndCallbacks)
28690
28981
  cb();
28691
28982
  }
@@ -28699,6 +28990,7 @@ class CursorSdkStreamAdapter {
28699
28990
  cb();
28700
28991
  }
28701
28992
  }
28993
+ var init_cursor_sdk_stream_adapter = () => {};
28702
28994
 
28703
28995
  // src/infrastructure/services/remote-agents/cursor-sdk/cursor-sdk-agent-service.ts
28704
28996
  import { randomUUID } from "node:crypto";
@@ -28758,16 +29050,11 @@ function waitForResumeOrAbort(session2) {
28758
29050
  })
28759
29051
  ]);
28760
29052
  }
28761
- function buildLogPrefix(context5) {
28762
- const roleTag = context5.role ?? "unknown";
28763
- const chatroomSuffix = context5.chatroomId ? `@${context5.chatroomId.slice(-6)}` : "";
28764
- return `[cursor-sdk:${roleTag}${chatroomSuffix}`;
28765
- }
28766
29053
  function resolveModelId(model) {
28767
29054
  return model ? resolveCursorSdkModel(model) : DEFAULT_MODEL;
28768
29055
  }
28769
29056
  function writeSpawnError(logPrefix, err, emitLogLine) {
28770
- const line = `${logPrefix} spawn-error] ${formatCursorSdkLoadError(err)}`;
29057
+ const line = formatAgentLogLine(logPrefix, "spawn-error", formatCursorSdkLoadError(err));
28771
29058
  process.stderr.write(`${line}
28772
29059
  `);
28773
29060
  emitLogLine?.(line);
@@ -28779,6 +29066,7 @@ var init_cursor_sdk_agent_service = __esm(() => {
28779
29066
  init_base_cli_agent_service();
28780
29067
  init_detection_result();
28781
29068
  init_cursor_sdk_package();
29069
+ init_cursor_sdk_stream_adapter();
28782
29070
  CursorSdkAgentService = class CursorSdkAgentService extends BaseCLIAgentService {
28783
29071
  id = "cursor-sdk";
28784
29072
  displayName = "Cursor (SDK)";
@@ -28901,9 +29189,12 @@ ${options.prompt}`;
28901
29189
  local: { cwd: stored.workingDir, settingSources: [] }
28902
29190
  }), AGENT_CREATE_TIMEOUT_MS, "Agent.resume");
28903
29191
  } catch (err) {
29192
+ const reason = err instanceof Error ? err.message : String(err);
29193
+ process.stderr.write(`[${new Date().toISOString()}] role:${context5.role} daemon-resume-fallback] ${reason} — cold spawning
29194
+ `);
28904
29195
  keeper.kill();
28905
29196
  this.deleteProcess(pid);
28906
- throw err;
29197
+ return this.spawn(options);
28907
29198
  }
28908
29199
  return this.startRunningSession({
28909
29200
  pid,
@@ -28943,7 +29234,7 @@ ${options.prompt}`;
28943
29234
  forceFirstTurn
28944
29235
  } = args2;
28945
29236
  const entry = this.registerProcess(pid, context5);
28946
- const logPrefix = buildLogPrefix(context5);
29237
+ const logPrefix = buildAgentLogPrefix("cursor-sdk", context5);
28947
29238
  const session2 = {
28948
29239
  agent,
28949
29240
  keeper,
@@ -29070,7 +29361,7 @@ ${options.prompt}`;
29070
29361
  adapter.flushPendingOutput();
29071
29362
  if (result.status === "error") {
29072
29363
  exitCode = 2;
29073
- const runErrorLine = `${logPrefix} run-error] run ${result.id} failed`;
29364
+ const runErrorLine = formatAgentLogLine(logPrefix, "run-error", `run ${result.id} failed`);
29074
29365
  process.stderr.write(`${runErrorLine}
29075
29366
  `);
29076
29367
  emitLogLine(runErrorLine);
@@ -29166,6 +29457,7 @@ ${options.prompt}`;
29166
29457
  // src/infrastructure/services/remote-agents/cursor-sdk/index.ts
29167
29458
  var init_cursor_sdk = __esm(() => {
29168
29459
  init_cursor_sdk_agent_service();
29460
+ init_cursor_sdk_stream_adapter();
29169
29461
  });
29170
29462
 
29171
29463
  // ../../node_modules/.pnpm/@opencode-ai+sdk@1.15.11/node_modules/@opencode-ai/sdk/dist/gen/types.gen.js
@@ -31175,8 +31467,7 @@ function selectAgent(agents) {
31175
31467
 
31176
31468
  // src/infrastructure/services/remote-agents/opencode-sdk/session-event-forwarder.ts
31177
31469
  function formatLogLine(options, kind, payload) {
31178
- const ts = options.now ? options.now() : new Date().toISOString();
31179
- return `[${ts}] role:${options.role} ${kind}]${payload ? ` ${payload}` : ""}`;
31470
+ return formatTimestampedLogLine(options.role, kind, payload, options.now);
31180
31471
  }
31181
31472
  function writeLogLine(target, options, kind, payload) {
31182
31473
  const line = formatLogLine(options, kind, payload);
@@ -31184,13 +31475,23 @@ function writeLogLine(target, options, kind, payload) {
31184
31475
  `);
31185
31476
  options.onLogLine?.(line);
31186
31477
  }
31187
- function isUsageLimitError(error) {
31188
- if (!error || typeof error !== "object")
31478
+ function isTerminalProviderError(error) {
31479
+ if (!error || typeof error !== "object") {
31480
+ if (typeof error === "string") {
31481
+ return matchesTerminalProviderErrorText(error);
31482
+ }
31189
31483
  return false;
31484
+ }
31190
31485
  const e = error;
31191
31486
  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");
31487
+ const message = String(e.data?.message ?? e.message ?? e.responseBody ?? "").toLowerCase();
31488
+ const blob = `${name}
31489
+ ${message}`;
31490
+ return matchesTerminalProviderErrorText(blob);
31491
+ }
31492
+ function matchesTerminalProviderErrorText(blob) {
31493
+ const text = blob.toLowerCase();
31494
+ 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
31495
  }
31195
31496
  function eventSessionId(event) {
31196
31497
  const p = event.properties;
@@ -31256,25 +31557,14 @@ function startSessionEventForwarder(client4, options) {
31256
31557
  writeLogLine(target, options, "thinking", chunk2);
31257
31558
  }
31258
31559
  } else if (part?.type === "tool" && part.tool) {
31259
- let appendInput = function(base, input, tool) {
31260
- if (!input || typeof input === "object" && Object.keys(input).length === 0) {
31261
- return base;
31262
- }
31263
- const inp = input;
31264
- if (tool === "bash" && typeof inp.command === "string") {
31265
- return `${base}: ${inp.command}`;
31266
- }
31267
- const inputStr = typeof inp === "string" ? inp : JSON.stringify(inp);
31268
- return `${base}: ${inputStr}`;
31269
- };
31270
31560
  const state = typeof props?.state === "string" ? props.state : typeof part.state?.status === "string" ? part.state.status : "started";
31271
31561
  let payload = state;
31272
31562
  if (part.state?.input) {
31273
- payload = appendInput(payload, part.state.input, part.tool);
31563
+ payload = appendToolInputToPayload(payload, part.state.input, part.tool);
31274
31564
  }
31275
31565
  if (state === "completed" && part.state?.time?.start !== undefined && part.state?.time?.end !== undefined) {
31276
31566
  const duration3 = ((part.state.time.end - part.state.time.start) / 1000).toFixed(1);
31277
- payload = appendInput(`${state} (${duration3}s)`, part.state.input, part.tool);
31567
+ payload = appendToolInputToPayload(`${state} (${duration3}s)`, part.state.input, part.tool);
31278
31568
  }
31279
31569
  const callID = part.callID ?? "unknown";
31280
31570
  const seenKey = `${callID}:${state}`;
@@ -31325,8 +31615,8 @@ function startSessionEventForwarder(client4, options) {
31325
31615
  payload += ` [command: ${props.command}]`;
31326
31616
  }
31327
31617
  writeLogLine(errorTarget, options, "error", payload);
31328
- if (isUsageLimitError(err)) {
31329
- writeLogLine(target, options, "agent_end", "reason: usage_limit_reached");
31618
+ if (isTerminalProviderError(err)) {
31619
+ writeLogLine(target, options, "agent_end", "reason: provider_rate_limit");
31330
31620
  for (const cb of agentEndCallbacks)
31331
31621
  cb();
31332
31622
  }
@@ -31339,6 +31629,11 @@ function startSessionEventForwarder(client4, options) {
31339
31629
  } catch (err) {
31340
31630
  const message = err instanceof Error ? err.message : String(err);
31341
31631
  writeLogLine(errorTarget, options, "error", message);
31632
+ if (isTerminalProviderError(err)) {
31633
+ writeLogLine(target, options, "agent_end", "reason: provider_rate_limit");
31634
+ for (const cb of agentEndCallbacks)
31635
+ cb();
31636
+ }
31342
31637
  } finally {
31343
31638
  doneResolve();
31344
31639
  }
@@ -31354,6 +31649,7 @@ function startSessionEventForwarder(client4, options) {
31354
31649
  }
31355
31650
  };
31356
31651
  }
31652
+ var init_session_event_forwarder = () => {};
31357
31653
 
31358
31654
  // src/infrastructure/services/remote-agents/opencode-sdk/session-metadata-store.ts
31359
31655
  import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "node:fs";
@@ -31422,8 +31718,10 @@ var init_opencode_sdk_agent_service = __esm(() => {
31422
31718
  init_dist();
31423
31719
  init_base_cli_agent_service();
31424
31720
  init_parse_listening_url();
31721
+ init_session_event_forwarder();
31425
31722
  init_session_metadata_store();
31426
31723
  OpenCodeSdkAgentService = class OpenCodeSdkAgentService extends BaseCLIAgentService {
31724
+ agentEndCallbacksByPid = new Map;
31427
31725
  id = "opencode-sdk";
31428
31726
  displayName = "OpenCode (SDK)";
31429
31727
  command = OPENCODE_COMMAND2;
@@ -31462,8 +31760,8 @@ var init_opencode_sdk_agent_service = __esm(() => {
31462
31760
  } catch (err) {
31463
31761
  console.warn(`[opencode-sdk] session.abort for pid=${pid} sessionId=${meta.sessionId} failed (continuing with SIGTERM):`, err instanceof Error ? err.message : err);
31464
31762
  }
31763
+ this.sessionStore.remove(meta.sessionId);
31465
31764
  }
31466
- this.sessionStore.remove(meta.sessionId);
31467
31765
  }
31468
31766
  await super.stop(pid);
31469
31767
  }
@@ -31548,6 +31846,7 @@ var init_opencode_sdk_agent_service = __esm(() => {
31548
31846
  this.forwarders.delete(pid);
31549
31847
  }
31550
31848
  this.sessionStore.remove(sessionId);
31849
+ this.agentEndCallbacksByPid.delete(pid);
31551
31850
  this.deleteProcess(pid);
31552
31851
  cb({ code: code2, signal, context: context5 });
31553
31852
  });
@@ -31556,6 +31855,9 @@ var init_opencode_sdk_agent_service = __esm(() => {
31556
31855
  outputCallbacks.push(cb);
31557
31856
  },
31558
31857
  onAgentEnd: (cb) => {
31858
+ const callbacks = this.agentEndCallbacksByPid.get(pid) ?? [];
31859
+ callbacks.push(cb);
31860
+ this.agentEndCallbacksByPid.set(pid, callbacks);
31559
31861
  forwarder?.onAgentEnd(cb);
31560
31862
  },
31561
31863
  onLogLine: (cb) => {
@@ -31573,6 +31875,54 @@ var init_opencode_sdk_agent_service = __esm(() => {
31573
31875
  }
31574
31876
  };
31575
31877
  }
31878
+ async startFreshSessionOnServe(args2) {
31879
+ const existingForwarder = this.forwarders.get(args2.pid);
31880
+ existingForwarder?.stop();
31881
+ this.forwarders.delete(args2.pid);
31882
+ const client4 = createOpencodeClient({ baseUrl: args2.baseUrl });
31883
+ const sessionCreateResult = await withTimeout2(client4.session.create({ body: {} }), SESSION_CREATE_TIMEOUT_MS, "session.create");
31884
+ if (!sessionCreateResult.data?.id) {
31885
+ throw new Error("Failed to create session during resume fallback");
31886
+ }
31887
+ const newSessionId2 = sessionCreateResult.data.id;
31888
+ const forwarder = startSessionEventForwarder(client4, {
31889
+ sessionId: newSessionId2,
31890
+ role: args2.context.role
31891
+ });
31892
+ const callbacks = this.agentEndCallbacksByPid.get(args2.pid) ?? [];
31893
+ for (const cb of callbacks) {
31894
+ forwarder.onAgentEnd(cb);
31895
+ }
31896
+ this.forwarders.set(args2.pid, forwarder);
31897
+ if (args2.oldSessionId) {
31898
+ this.sessionStore.remove(args2.oldSessionId);
31899
+ }
31900
+ this.sessionStore.upsert({
31901
+ sessionId: newSessionId2,
31902
+ machineId: args2.context.machineId,
31903
+ chatroomId: args2.context.chatroomId,
31904
+ role: args2.context.role,
31905
+ agentName: args2.agentName,
31906
+ ...args2.model ? { model: args2.model } : {},
31907
+ pid: args2.pid,
31908
+ createdAt: new Date().toISOString(),
31909
+ baseUrl: args2.baseUrl
31910
+ });
31911
+ const modelParts = args2.model ? parseModelId(args2.model) : undefined;
31912
+ await withTimeout2(client4.session.promptAsync({
31913
+ path: { id: newSessionId2 },
31914
+ body: {
31915
+ agent: args2.agentName,
31916
+ parts: [{ type: "text", text: args2.prompt }],
31917
+ ...modelParts ? { model: modelParts } : {},
31918
+ tools: {
31919
+ task: false,
31920
+ question: false,
31921
+ external_directory: false
31922
+ }
31923
+ }
31924
+ }), PROMPT_ASYNC_TIMEOUT_MS, "session.promptAsync");
31925
+ }
31576
31926
  async resumeFromDaemonMemory(options, session2) {
31577
31927
  const { prompt, systemPrompt, model, context: context5 } = options;
31578
31928
  const sessionId = session2.harnessSessionId;
@@ -31624,11 +31974,11 @@ var init_opencode_sdk_agent_service = __esm(() => {
31624
31974
  }), PROMPT_ASYNC_TIMEOUT_MS, "session.promptAsync");
31625
31975
  } catch (err) {
31626
31976
  const reason = err instanceof Error ? err.message : String(err);
31627
- process.stderr.write(`[${new Date().toISOString()}] role:${context5.role} resume-error] ${reason}
31977
+ process.stderr.write(`[${new Date().toISOString()}] role:${context5.role} daemon-resume-fallback] ${reason} — cold spawning
31628
31978
  `);
31629
31979
  forwarder?.stop();
31630
31980
  childProcess.kill();
31631
- throw err;
31981
+ return this.spawn(options);
31632
31982
  }
31633
31983
  return this.registerRunningSession({
31634
31984
  childProcess,
@@ -31733,23 +32083,45 @@ var init_opencode_sdk_agent_service = __esm(() => {
31733
32083
  async resumeTurn(pid, prompt) {
31734
32084
  const meta = this.sessionStore.findByPid(pid);
31735
32085
  if (!meta) {
31736
- throw new Error(`No opencode-sdk session metadata for pid=${pid}`);
32086
+ process.stderr.write(`[${new Date().toISOString()}] opencode-sdk resumeTurn: no metadata for pid=${pid}, skipping
32087
+ `);
32088
+ return;
31737
32089
  }
31738
32090
  const client4 = createOpencodeClient({ baseUrl: meta.baseUrl });
31739
32091
  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
32092
+ const context5 = {
32093
+ machineId: meta.machineId,
32094
+ chatroomId: meta.chatroomId,
32095
+ role: meta.role
32096
+ };
32097
+ try {
32098
+ await withTimeout2(client4.session.promptAsync({
32099
+ path: { id: meta.sessionId },
32100
+ body: {
32101
+ agent: meta.agentName,
32102
+ parts: [{ type: "text", text: prompt }],
32103
+ ...modelParts ? { model: modelParts } : {},
32104
+ tools: {
32105
+ task: false,
32106
+ question: false,
32107
+ external_directory: false
32108
+ }
31750
32109
  }
31751
- }
31752
- }), PROMPT_ASYNC_TIMEOUT_MS, "session.promptAsync");
32110
+ }), PROMPT_ASYNC_TIMEOUT_MS, "session.promptAsync");
32111
+ } catch (err) {
32112
+ const reason = err instanceof Error ? err.message : String(err);
32113
+ process.stderr.write(`[${new Date().toISOString()}] role:${meta.role} resume-fallback] ${reason} — starting fresh session
32114
+ `);
32115
+ await this.startFreshSessionOnServe({
32116
+ pid,
32117
+ baseUrl: meta.baseUrl,
32118
+ context: context5,
32119
+ agentName: meta.agentName,
32120
+ model: meta.model,
32121
+ prompt,
32122
+ oldSessionId: meta.sessionId
32123
+ });
32124
+ }
31753
32125
  }
31754
32126
  };
31755
32127
  });
@@ -41454,51 +41826,51 @@ var require_resize = __commonJS((exports, module) => {
41454
41826
  }
41455
41827
  return this;
41456
41828
  }
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;
41829
+ function extend3(extend4) {
41830
+ if (is.integer(extend4) && extend4 > 0) {
41831
+ this.options.extendTop = extend4;
41832
+ this.options.extendBottom = extend4;
41833
+ this.options.extendLeft = extend4;
41834
+ this.options.extendRight = extend4;
41835
+ } else if (is.object(extend4)) {
41836
+ if (is.defined(extend4.top)) {
41837
+ if (is.integer(extend4.top) && extend4.top >= 0) {
41838
+ this.options.extendTop = extend4.top;
41467
41839
  } else {
41468
- throw is.invalidParameterError("top", "positive integer", extend3.top);
41840
+ throw is.invalidParameterError("top", "positive integer", extend4.top);
41469
41841
  }
41470
41842
  }
41471
- if (is.defined(extend3.bottom)) {
41472
- if (is.integer(extend3.bottom) && extend3.bottom >= 0) {
41473
- this.options.extendBottom = extend3.bottom;
41843
+ if (is.defined(extend4.bottom)) {
41844
+ if (is.integer(extend4.bottom) && extend4.bottom >= 0) {
41845
+ this.options.extendBottom = extend4.bottom;
41474
41846
  } else {
41475
- throw is.invalidParameterError("bottom", "positive integer", extend3.bottom);
41847
+ throw is.invalidParameterError("bottom", "positive integer", extend4.bottom);
41476
41848
  }
41477
41849
  }
41478
- if (is.defined(extend3.left)) {
41479
- if (is.integer(extend3.left) && extend3.left >= 0) {
41480
- this.options.extendLeft = extend3.left;
41850
+ if (is.defined(extend4.left)) {
41851
+ if (is.integer(extend4.left) && extend4.left >= 0) {
41852
+ this.options.extendLeft = extend4.left;
41481
41853
  } else {
41482
- throw is.invalidParameterError("left", "positive integer", extend3.left);
41854
+ throw is.invalidParameterError("left", "positive integer", extend4.left);
41483
41855
  }
41484
41856
  }
41485
- if (is.defined(extend3.right)) {
41486
- if (is.integer(extend3.right) && extend3.right >= 0) {
41487
- this.options.extendRight = extend3.right;
41857
+ if (is.defined(extend4.right)) {
41858
+ if (is.integer(extend4.right) && extend4.right >= 0) {
41859
+ this.options.extendRight = extend4.right;
41488
41860
  } else {
41489
- throw is.invalidParameterError("right", "positive integer", extend3.right);
41861
+ throw is.invalidParameterError("right", "positive integer", extend4.right);
41490
41862
  }
41491
41863
  }
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];
41864
+ this._setBackgroundColourOption("extendBackground", extend4.background);
41865
+ if (is.defined(extend4.extendWith)) {
41866
+ if (is.string(extendWith[extend4.extendWith])) {
41867
+ this.options.extendWith = extendWith[extend4.extendWith];
41496
41868
  } else {
41497
- throw is.invalidParameterError("extendWith", "one of: background, copy, repeat, mirror", extend3.extendWith);
41869
+ throw is.invalidParameterError("extendWith", "one of: background, copy, repeat, mirror", extend4.extendWith);
41498
41870
  }
41499
41871
  }
41500
41872
  } else {
41501
- throw is.invalidParameterError("extend", "integer or object", extend3);
41873
+ throw is.invalidParameterError("extend", "integer or object", extend4);
41502
41874
  }
41503
41875
  return this;
41504
41876
  }
@@ -41554,7 +41926,7 @@ var require_resize = __commonJS((exports, module) => {
41554
41926
  module.exports = (Sharp) => {
41555
41927
  Object.assign(Sharp.prototype, {
41556
41928
  resize,
41557
- extend: extend2,
41929
+ extend: extend3,
41558
41930
  extract,
41559
41931
  trim
41560
41932
  });
@@ -53499,7 +53871,7 @@ var toString, getPrototypeOf, iterator, toStringTag, kindOf, kindOfTest = (type)
53499
53871
  return kind === "formdata" || kind === "object" && isFunction3(thing.toString) && thing.toString() === "[object FormData]";
53500
53872
  }, isURLSearchParams, isReadableStream, isRequest3, isResponse, isHeaders, trim = (str) => {
53501
53873
  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 } = {}) => {
53874
+ }, _global, isContextDefined = (context5) => !isUndefined(context5) && context5 !== _global, extend3 = (a, b, thisArg, { allOwnKeys } = {}) => {
53503
53875
  forEach6(b, (val, key) => {
53504
53876
  if (thisArg && isFunction3(val)) {
53505
53877
  Object.defineProperty(a, key, {
@@ -53753,7 +54125,7 @@ var init_utils = __esm(() => {
53753
54125
  isFileList,
53754
54126
  forEach: forEach6,
53755
54127
  merge: merge9,
53756
- extend: extend2,
54128
+ extend: extend3,
53757
54129
  trim,
53758
54130
  stripBOM,
53759
54131
  inherits,
@@ -65184,7 +65556,7 @@ var require_common = __commonJS((exports, module) => {
65184
65556
  debug.namespace = namespace;
65185
65557
  debug.useColors = createDebug.useColors();
65186
65558
  debug.color = createDebug.selectColor(namespace);
65187
- debug.extend = extend3;
65559
+ debug.extend = extend4;
65188
65560
  debug.destroy = createDebug.destroy;
65189
65561
  Object.defineProperty(debug, "enabled", {
65190
65562
  enumerable: true,
@@ -65208,7 +65580,7 @@ var require_common = __commonJS((exports, module) => {
65208
65580
  }
65209
65581
  return debug;
65210
65582
  }
65211
- function extend3(namespace, delimiter) {
65583
+ function extend4(namespace, delimiter) {
65212
65584
  const newDebug = createDebug(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace);
65213
65585
  newDebug.log = this.log;
65214
65586
  return newDebug;
@@ -82834,15 +83206,10 @@ var init_on_daemon_shutdown = __esm(() => {
82834
83206
  const activeAgents = agentPm.listActive();
82835
83207
  if (activeAgents.length > 0) {
82836
83208
  console.log(`[${formatTimestamp()}] Stopping ${activeAgents.length} agent(s)...`);
82837
- yield* exports_Effect.promise(() => Promise.allSettled(activeAgents.map(async ({ chatroomId, role, slot }) => {
83209
+ yield* exports_Effect.all(activeAgents.map(({ chatroomId, role, slot }) => {
82838
83210
  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
- })));
83211
+ 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}`))));
83212
+ }), { concurrency: "unbounded" });
82846
83213
  console.log(`[${formatTimestamp()}] All agents stopped`);
82847
83214
  }
82848
83215
  yield* exports_Effect.promise(() => session2.backend.mutation(api.machines.updateDaemonStatus, {
@@ -84924,36 +85291,45 @@ async function pushSingleWorkspaceGitStateImpl(ctx, workingDir) {
84924
85291
  const stateKey = makeGitStateKey(ctx.machineId, workingDir);
84925
85292
  const isRepo = await isGitRepo(workingDir);
84926
85293
  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);
85294
+ await pushNotFoundGitState(ctx, workingDir, stateKey);
84937
85295
  return;
84938
85296
  }
84939
85297
  const branchResult = await getBranch(workingDir);
84940
85298
  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);
85299
+ await pushErrorGitState(ctx, workingDir, stateKey, branchResult.message);
84952
85300
  return;
84953
85301
  }
84954
85302
  if (branchResult.status === "not_found") {
84955
85303
  return;
84956
85304
  }
85305
+ await pushAvailableGitState(ctx, workingDir, stateKey, branchResult);
85306
+ }
85307
+ async function pushNotFoundGitState(ctx, workingDir, stateKey) {
85308
+ const stateHash = "not_found";
85309
+ if (ctx.lastPushedGitState.get(stateKey) === stateHash)
85310
+ return;
85311
+ await ctx.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
85312
+ sessionId: ctx.sessionId,
85313
+ machineId: ctx.machineId,
85314
+ workingDir,
85315
+ status: "not_found"
85316
+ });
85317
+ ctx.lastPushedGitState.set(stateKey, stateHash);
85318
+ }
85319
+ async function pushErrorGitState(ctx, workingDir, stateKey, message) {
85320
+ const stateHash = `error:${message}`;
85321
+ if (ctx.lastPushedGitState.get(stateKey) === stateHash)
85322
+ return;
85323
+ await ctx.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
85324
+ sessionId: ctx.sessionId,
85325
+ machineId: ctx.machineId,
85326
+ workingDir,
85327
+ status: "error",
85328
+ errorMessage: message
85329
+ });
85330
+ ctx.lastPushedGitState.set(stateKey, stateHash);
85331
+ }
85332
+ async function pushAvailableGitState(ctx, workingDir, stateKey, branchResult) {
84957
85333
  const branch = branchResult.branch;
84958
85334
  const allFields = [branchField, ...GIT_STATE_FIELDS, ...makeBranchDependentFields(branch)];
84959
85335
  const pipeline2 = new GitStatePipeline(allFields);
@@ -84990,16 +85366,12 @@ async function pushSingleWorkspaceGitStateImpl(ctx, workingDir) {
84990
85366
  }
84991
85367
  }
84992
85368
  }
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) {
85369
+ function pushObservedNotRepoEffect(session2, lastPushedGitState, stateKey, workingDir, reason) {
85370
+ return exports_Effect.gen(function* () {
85000
85371
  const stateHash = "not_found";
85001
- if (reason !== "refresh" && lastPushedGitState.get(stateKey) === stateHash)
85372
+ if (reason !== "refresh" && lastPushedGitState.get(stateKey) === stateHash) {
85002
85373
  return;
85374
+ }
85003
85375
  yield* exports_Effect.promise(() => session2.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
85004
85376
  sessionId: session2.sessionId,
85005
85377
  machineId: session2.machineId,
@@ -85007,21 +85379,69 @@ var lastFullPushMs, branchField, GIT_STATE_FIELDS, pushSingleWorkspaceGitSummary
85007
85379
  status: "not_found"
85008
85380
  }));
85009
85381
  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)
85382
+ });
85383
+ }
85384
+ function pushObservedBranchErrorEffect(session2, lastPushedGitState, stateKey, workingDir, reason, message) {
85385
+ return exports_Effect.gen(function* () {
85386
+ const stateHash = `error:${message}`;
85387
+ if (reason !== "refresh" && lastPushedGitState.get(stateKey) === stateHash) {
85016
85388
  return;
85389
+ }
85017
85390
  yield* exports_Effect.promise(() => session2.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
85018
85391
  sessionId: session2.sessionId,
85019
85392
  machineId: session2.machineId,
85020
85393
  workingDir,
85021
85394
  status: "error",
85022
- errorMessage: branchResult.message
85395
+ errorMessage: message
85023
85396
  }));
85024
85397
  lastPushedGitState.set(stateKey, stateHash);
85398
+ });
85399
+ }
85400
+ function pushObservedFullGitStateEffect(session2, lastPushedGitState, stateKey, workingDir, branch, reason) {
85401
+ return exports_Effect.gen(function* () {
85402
+ yield* exports_Effect.promise(() => pushSingleWorkspaceGitStateImpl(buildGitStateDeps(session2, lastPushedGitState), workingDir));
85403
+ lastFullPushMs.set(stateKey, Date.now());
85404
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed full git state pushed: ${workingDir} (${branch})${reason === "refresh" ? " [refresh]" : ""}`);
85405
+ });
85406
+ }
85407
+ function pushObservedSlimGitSummaryEffect(session2, lastPushedGitState, stateKey, workingDir, branch, branchResult, reason) {
85408
+ return exports_Effect.gen(function* () {
85409
+ const slimFields = [
85410
+ branchField,
85411
+ ...GIT_STATE_FIELDS.filter((f) => f.includeInSlim),
85412
+ ...makeBranchDependentFields(branch)
85413
+ ];
85414
+ const pipeline2 = new GitStatePipeline(slimFields);
85415
+ const preCollected = new Map([["branch", branchResult]]);
85416
+ const values3 = yield* exports_Effect.promise(() => pipeline2.collect(workingDir, preCollected));
85417
+ const hash2 = pipeline2.computeHash(values3, true);
85418
+ if (reason !== "refresh" && lastPushedGitState.get(stateKey) === hash2) {
85419
+ return;
85420
+ }
85421
+ yield* exports_Effect.promise(() => session2.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
85422
+ sessionId: session2.sessionId,
85423
+ machineId: session2.machineId,
85424
+ workingDir,
85425
+ status: "available",
85426
+ ...pipeline2.toMutationArgs(values3, true)
85427
+ }));
85428
+ lastPushedGitState.set(stateKey, hash2);
85429
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed git summary pushed: ${workingDir} (${branch}${values3.get("isDirty") ? ", dirty" : ", clean"})${reason === "refresh" ? " [refresh]" : ""}`);
85430
+ });
85431
+ }
85432
+ var lastFullPushMs, branchField, GIT_STATE_FIELDS, pushSingleWorkspaceGitSummaryForObservedEffect = (workingDir, reason = "safety-poll") => exports_Effect.gen(function* pushObservedGitSummaryForObserved() {
85433
+ const session2 = yield* DaemonSessionService;
85434
+ const mutable = yield* DaemonMutableStateService;
85435
+ const lastPushedGitState = yield* exports_Ref.get(mutable.lastPushedGitState);
85436
+ const stateKey = makeGitStateKey(session2.machineId, workingDir);
85437
+ const isRepo = yield* exports_Effect.promise(() => isGitRepo(workingDir));
85438
+ if (!isRepo) {
85439
+ yield* pushObservedNotRepoEffect(session2, lastPushedGitState, stateKey, workingDir, reason);
85440
+ return;
85441
+ }
85442
+ const branchResult = yield* exports_Effect.promise(() => getBranch(workingDir));
85443
+ if (branchResult.status === "error") {
85444
+ yield* pushObservedBranchErrorEffect(session2, lastPushedGitState, stateKey, workingDir, reason, branchResult.message);
85025
85445
  return;
85026
85446
  }
85027
85447
  if (branchResult.status === "not_found") {
@@ -85031,32 +85451,10 @@ var lastFullPushMs, branchField, GIT_STATE_FIELDS, pushSingleWorkspaceGitSummary
85031
85451
  const now = Date.now();
85032
85452
  const lastFull = lastFullPushMs.get(stateKey) ?? 0;
85033
85453
  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]" : ""}`);
85454
+ yield* pushObservedFullGitStateEffect(session2, lastPushedGitState, stateKey, workingDir, branch, reason);
85037
85455
  return;
85038
85456
  }
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) {
85049
- return;
85050
- }
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]" : ""}`);
85457
+ yield* pushObservedSlimGitSummaryEffect(session2, lastPushedGitState, stateKey, workingDir, branch, branchResult, reason);
85060
85458
  }), pushGitStateEffect, pushSingleWorkspaceGitStateEffect = (workingDir) => exports_Effect.gen(function* () {
85061
85459
  const session2 = yield* DaemonSessionService;
85062
85460
  const mutable = yield* DaemonMutableStateService;
@@ -85272,14 +85670,12 @@ async function processPRAction(deps, req) {
85272
85670
  timeout: EXEC_TIMEOUT_MS
85273
85671
  });
85274
85672
  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({
85673
+ exports_Runtime.runFork(deps.runtime)(pushGitStateEffect.pipe(exports_Effect.provide(exports_Layer.mergeAll(exports_Layer.succeed(DaemonSessionService, deps), DaemonMutableStateServiceLive({
85276
85674
  lastPushedGitState: deps.lastPushedGitState,
85277
85675
  lastPushedModels: null,
85278
85676
  lastPushedHarnessFingerprint: null,
85279
85677
  workspaceListStore: deps.workspaceListStore
85280
- }))))).catch((err) => {
85281
- console.warn(`[${formatTimestamp()}] ⚠️ Failed to refresh git state after PR action: ${getErrorMessage(err)}`);
85282
- });
85678
+ }))), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[${formatTimestamp()}] ⚠️ Failed to refresh git state after PR action: ${getErrorMessage(err)}`)))));
85283
85679
  }
85284
85680
  async function processPRCommits(deps, req) {
85285
85681
  const prNumber = req.prNumber;
@@ -85304,32 +85700,35 @@ async function processCommitDetail(deps, req) {
85304
85700
  getCommitDetail(req.workingDir, req.sha),
85305
85701
  getCommitMetadata(req.workingDir, req.sha)
85306
85702
  ]);
85703
+ await upsertCommitDetailResult(deps, req, result, metadata);
85704
+ if (result.status === "available" || result.status === "truncated") {
85705
+ const compressed = gzipSync2(Buffer.from(result.content));
85706
+ 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)`);
85707
+ }
85708
+ }
85709
+ async function upsertCommitDetailResult(deps, req, result, metadata) {
85710
+ const baseArgs = {
85711
+ sessionId: deps.sessionId,
85712
+ machineId: deps.machineId,
85713
+ workingDir: req.workingDir,
85714
+ sha: req.sha,
85715
+ message: metadata?.message,
85716
+ body: metadata?.body,
85717
+ author: metadata?.author,
85718
+ date: metadata?.date
85719
+ };
85307
85720
  if (result.status === "not_found") {
85308
85721
  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
85722
+ ...baseArgs,
85723
+ status: "not_found"
85318
85724
  });
85319
85725
  return;
85320
85726
  }
85321
85727
  if (result.status === "error") {
85322
85728
  await deps.backend.mutation(api.workspaces.upsertCommitDetailV2, {
85323
- sessionId: deps.sessionId,
85324
- machineId: deps.machineId,
85325
- workingDir: req.workingDir,
85326
- sha: req.sha,
85729
+ ...baseArgs,
85327
85730
  status: "error",
85328
- errorMessage: result.message,
85329
- message: metadata?.message,
85330
- body: metadata?.body,
85331
- author: metadata?.author,
85332
- date: metadata?.date
85731
+ errorMessage: result.message
85333
85732
  });
85334
85733
  return;
85335
85734
  }
@@ -85337,20 +85736,12 @@ async function processCommitDetail(deps, req) {
85337
85736
  const compressed = gzipSync2(Buffer.from(result.content));
85338
85737
  const diffContentCompressed = compressed.toString("base64");
85339
85738
  await deps.backend.mutation(api.workspaces.upsertCommitDetailV2, {
85340
- sessionId: deps.sessionId,
85341
- machineId: deps.machineId,
85342
- workingDir: req.workingDir,
85343
- sha: req.sha,
85739
+ ...baseArgs,
85344
85740
  status: "available",
85345
85741
  data: { compression: "gzip", content: diffContentCompressed },
85346
85742
  truncated: result.truncated,
85347
- message: metadata?.message,
85348
- body: metadata?.body,
85349
- author: metadata?.author,
85350
- date: metadata?.date,
85351
85743
  diffStat
85352
85744
  });
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
85745
  }
85355
85746
  async function processMoreCommits(deps, req) {
85356
85747
  const offset = req.offset ?? 0;
@@ -85387,11 +85778,33 @@ async function processRecentCommits(deps, req) {
85387
85778
  });
85388
85779
  console.log(`[${formatTimestamp()}] \uD83D\uDCDC Recent commits pushed: ${req.workingDir} (${commits.length} commits)`);
85389
85780
  }
85781
+ function dispatchGitRequest(deps, req) {
85782
+ switch (req.requestType) {
85783
+ case "full_diff":
85784
+ return exports_Effect.promise(() => processFullDiff(deps, req));
85785
+ case "commit_detail":
85786
+ return exports_Effect.promise(() => processCommitDetail(deps, req));
85787
+ case "more_commits":
85788
+ return exports_Effect.promise(() => processMoreCommits(deps, req));
85789
+ case "pr_diff":
85790
+ return exports_Effect.promise(() => processPRDiff(deps, req));
85791
+ case "pr_action":
85792
+ return exports_Effect.promise(() => processPRAction(deps, req));
85793
+ case "pr_commits":
85794
+ return exports_Effect.promise(() => processPRCommits(deps, req));
85795
+ case "all_pull_requests":
85796
+ return exports_Effect.promise(() => processAllPullRequests(deps, req));
85797
+ case "recent_commits":
85798
+ return exports_Effect.promise(() => processRecentCommits(deps, req));
85799
+ }
85800
+ }
85390
85801
  var EXEC_TIMEOUT_MS = 60000, startGitRequestSubscriptionEffect = (wsClient2) => exports_Effect.gen(function* () {
85391
85802
  const session2 = yield* DaemonSessionService;
85803
+ const runtime4 = yield* exports_Effect.runtime();
85392
85804
  const processedRequestIds = new Map;
85393
85805
  const DEDUP_TTL_MS = 5 * 60 * 1000;
85394
- let processing = false;
85806
+ const sessionWithRuntime = { ...session2, runtime: runtime4 };
85807
+ const processingState = { isProcessing: false };
85395
85808
  session2.backend.mutation(api.workspaces.resetProcessingRequests, {
85396
85809
  sessionId: session2.sessionId,
85397
85810
  machineId: session2.machineId
@@ -85408,16 +85821,14 @@ var EXEC_TIMEOUT_MS = 60000, startGitRequestSubscriptionEffect = (wsClient2) =>
85408
85821
  }, (requests) => {
85409
85822
  if (!requests || requests.length === 0)
85410
85823
  return;
85411
- const logger = session2.logger ?? console;
85824
+ const logger = sessionWithRuntime.logger ?? console;
85412
85825
  logger.log(`[${formatTimestamp()}] \uD83D\uDCEC Git subscription: received ${requests.length} pending request(s)`);
85413
- if (processing)
85826
+ if (processingState.isProcessing)
85414
85827
  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
- });
85828
+ processingState.isProcessing = true;
85829
+ 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(() => {
85830
+ processingState.isProcessing = false;
85831
+ }))));
85421
85832
  }, (err) => {
85422
85833
  console.warn(`[${formatTimestamp()}] ⚠️ Git request subscription error: ${getErrorMessage(err)}`);
85423
85834
  });
@@ -85428,7 +85839,7 @@ var EXEC_TIMEOUT_MS = 60000, startGitRequestSubscriptionEffect = (wsClient2) =>
85428
85839
  console.log(`[${formatTimestamp()}] \uD83D\uDD00 Git request subscription stopped`);
85429
85840
  }
85430
85841
  };
85431
- }), processRequestsEffect = (requests, processedRequestIds, dedupTtlMs) => exports_Effect.gen(function* () {
85842
+ }), processRequestsEffect = (requests, processedRequestIds, dedupTtlMs, runtime4) => exports_Effect.gen(function* () {
85432
85843
  const session2 = yield* DaemonSessionService;
85433
85844
  const evictBefore = Date.now() - dedupTtlMs;
85434
85845
  for (const [id3, ts] of processedRequestIds) {
@@ -85448,32 +85859,8 @@ var EXEC_TIMEOUT_MS = 60000, startGitRequestSubscriptionEffect = (wsClient2) =>
85448
85859
  }));
85449
85860
  const logger = session2.logger ?? console;
85450
85861
  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
- }
85862
+ const sessionWithRuntime = { ...session2, runtime: runtime4 };
85863
+ yield* dispatchGitRequest(sessionWithRuntime, req);
85477
85864
  yield* exports_Effect.promise(() => session2.backend.mutation(api.workspaces.updateRequestStatus, {
85478
85865
  sessionId: session2.sessionId,
85479
85866
  requestId: req._id,
@@ -85653,6 +86040,26 @@ function startCommandSubscriber(session2, wsClient2, deps) {
85653
86040
  });
85654
86041
  return { stop: unsub };
85655
86042
  }
86043
+ async function dispatchPendingCommand(session2, deps, cmd) {
86044
+ switch (cmd.type) {
86045
+ case "refreshCapabilities":
86046
+ await handleRefreshCapabilities(session2, deps, cmd);
86047
+ break;
86048
+ case "refreshSessionTitle":
86049
+ await handleRefreshSessionTitle(session2, deps, cmd);
86050
+ break;
86051
+ default:
86052
+ await markFailed(session2, cmd._id, `Unknown command type: ${cmd.type}`);
86053
+ }
86054
+ }
86055
+ async function processPendingCommand(session2, deps, cmd, now) {
86056
+ if (now - cmd.createdAt > DIRECT_HARNESS_COMMAND_TTL_MS) {
86057
+ console.log(`[direct-harness] Discarding stale command ${cmd._id} (type=${cmd.type}, age=${now - cmd.createdAt}ms)`);
86058
+ await markFailed(session2, cmd._id, "Command expired (TTL)");
86059
+ return;
86060
+ }
86061
+ await dispatchPendingCommand(session2, deps, cmd);
86062
+ }
85656
86063
  async function drain(session2, deps, processed) {
85657
86064
  const pending3 = await session2.backend.query(api.daemon.directHarness.commands.listPendingCommands, { sessionId: session2.sessionId, machineId: session2.machineId });
85658
86065
  if (!pending3 || pending3.length === 0)
@@ -85663,21 +86070,7 @@ async function drain(session2, deps, processed) {
85663
86070
  continue;
85664
86071
  processed.add(cmd._id);
85665
86072
  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
- }
86073
+ await processPendingCommand(session2, deps, cmd, now);
85681
86074
  } catch (err) {
85682
86075
  const message = err instanceof Error ? err.message : String(err);
85683
86076
  console.warn(`[direct-harness] Command ${cmd._id} failed: ${message}`);
@@ -86500,74 +86893,73 @@ async function drain2(session2, deps) {
86500
86893
  }
86501
86894
  }
86502
86895
  }
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;
86896
+ async function resumeSessionHandle(session2, deps, rowId, info) {
86897
+ const opencodeSessionId = info.opencodeSessionId;
86898
+ if (!opencodeSessionId) {
86899
+ console.warn(`[direct-harness] Session ${rowId} not yet open — waiting for session-subscriber`);
86900
+ return null;
86901
+ }
86902
+ const workspaceId = info.workspaceId;
86903
+ if (!workspaceId) {
86904
+ console.warn(`[direct-harness] Cannot resume session ${rowId}: no workspace info`);
86905
+ return null;
86906
+ }
86907
+ let harness = deps.harnesses.get(workspaceId);
86908
+ if (harness && !harness.isAlive()) {
86909
+ harness.close().catch(() => {});
86910
+ deps.harnesses.delete(workspaceId);
86911
+ harness = undefined;
86912
+ }
86913
+ if (!harness) {
86914
+ const workspace = await session2.backend.query(api.workspaces.getWorkspaceById, {
86915
+ sessionId: session2.sessionId,
86916
+ workspaceId
86917
+ });
86918
+ if (!workspace) {
86919
+ console.warn(`[direct-harness] Cannot resume session ${rowId}: workspace not found`);
86920
+ return null;
86521
86921
  }
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;
86922
+ harness = await startOpencodeSdkHarness({
86923
+ type: "opencode",
86924
+ workingDir: workspace.workingDir,
86925
+ workspaceId
86926
+ });
86927
+ deps.harnesses.set(workspaceId, harness);
86928
+ }
86929
+ try {
86930
+ return await resumeSession({
86931
+ harness,
86932
+ journalFactory: deps.journalFactory,
86933
+ chunkExtractor: createOpencodeSdkChunkExtractor()
86934
+ }, { harnessSessionId: rowId, opencodeSessionId, workspaceId });
86935
+ } catch (err) {
86936
+ const message = err instanceof Error ? err.message : String(err);
86937
+ console.warn(`[direct-harness] Cannot resume session ${rowId}: ${message}`);
86938
+ await deps.sessionRepository.markFailed(rowId).catch(() => {});
86939
+ return null;
86940
+ }
86941
+ }
86942
+ function wireResumedSessionEvents(handle, deps, info) {
86943
+ const idleConfig = {
86944
+ agent: info.lastUsedConfig.agent ?? "build",
86945
+ model: info.lastUsedConfig.model
86946
+ };
86947
+ let lastBoundKey = null;
86948
+ handle.session.onEvent((event) => {
86949
+ const turn = handle.currentTurn;
86950
+ if (turn?.messageId !== null && turn?.messageId !== undefined) {
86951
+ const key = `${turn.turnId}:${turn.messageId}`;
86952
+ if (key !== lastBoundKey) {
86953
+ lastBoundKey = key;
86954
+ deps.sessionRepository.bindTurnMessageId(turn.turnId, turn.messageId).catch((err) => console.warn("[direct-harness] bindTurnMessageId error (resume):", err));
86530
86955
  }
86531
- harness = await startOpencodeSdkHarness({
86532
- type: "opencode",
86533
- workingDir: workspace.workingDir,
86534
- workspaceId
86535
- });
86536
- deps.harnesses.set(workspaceId, harness);
86537
86956
  }
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;
86957
+ if (event.type === "session.idle") {
86958
+ handleSessionIdle(handle, handle.journal, idleConfig, deps.sessionRepository).catch((err) => console.warn("[direct-harness] idle handler error (resume):", err));
86549
86959
  }
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
- }
86960
+ });
86961
+ }
86962
+ async function deliverPendingMessages(handle, deps, rowId, messages, info) {
86571
86963
  for (const msg of messages) {
86572
86964
  const override = info?.lastUsedConfig ?? { agent: "build" };
86573
86965
  try {
@@ -86591,6 +86983,24 @@ async function processSessionMessages(session2, deps, rowId, messages, info) {
86591
86983
  }
86592
86984
  }
86593
86985
  }
86986
+ async function processSessionMessages(session2, deps, rowId, messages, info) {
86987
+ let handle = deps.activeSessions.get(rowId);
86988
+ if (!handle) {
86989
+ if (!info) {
86990
+ console.warn(`[direct-harness] Session ${rowId} not yet open — waiting for session-subscriber`);
86991
+ return;
86992
+ }
86993
+ const resumed = await resumeSessionHandle(session2, deps, rowId, info);
86994
+ if (!resumed) {
86995
+ return;
86996
+ }
86997
+ handle = resumed;
86998
+ deps.activeSessions.set(rowId, handle);
86999
+ await deps.sessionRepository.markActive(rowId).catch(() => {});
87000
+ wireResumedSessionEvents(handle, deps, info);
87001
+ }
87002
+ await deliverPendingMessages(handle, deps, rowId, messages, info);
87003
+ }
86594
87004
  var init_prompt_subscriber = __esm(() => {
86595
87005
  init_idle_handler();
86596
87006
  init_api3();
@@ -86619,6 +87029,60 @@ function startSessionSubscriber(daemonSession, wsClient2, deps) {
86619
87029
  });
86620
87030
  return { stop: unsub };
86621
87031
  }
87032
+ async function getOrCreateHarness(daemonSession, deps, session2, workspace) {
87033
+ let harness = deps.harnesses.get(session2.workspaceId);
87034
+ if (harness && !harness.isAlive()) {
87035
+ console.warn(`[direct-harness] Harness for workspace ${session2.workspaceId} is no longer alive — restarting`);
87036
+ harness.close().catch(() => {});
87037
+ deps.harnesses.delete(session2.workspaceId);
87038
+ harness = undefined;
87039
+ }
87040
+ if (!harness) {
87041
+ harness = await startOpencodeSdkHarness({
87042
+ type: "opencode",
87043
+ workingDir: workspace.workingDir,
87044
+ workspaceId: session2.workspaceId
87045
+ });
87046
+ deps.harnesses.set(session2.workspaceId, harness);
87047
+ }
87048
+ return harness;
87049
+ }
87050
+ function recordLiveSessionChunk(event, handle, journal, extractChunk, deps) {
87051
+ const chunk2 = extractChunk(event);
87052
+ if (chunk2 === null) {
87053
+ return;
87054
+ }
87055
+ journal.record({
87056
+ content: chunk2.content,
87057
+ timestamp: Date.now(),
87058
+ messageId: chunk2.messageId,
87059
+ partType: chunk2.partType
87060
+ });
87061
+ if (handle.currentTurn && handle.currentTurn.messageId === null) {
87062
+ handle.currentTurn.messageId = chunk2.messageId;
87063
+ deps.sessionRepository.bindTurnMessageId(handle.currentTurn.turnId, chunk2.messageId).catch((err) => console.warn("[direct-harness] bindTurnMessageId error:", err));
87064
+ }
87065
+ }
87066
+ function handleLiveSessionTitleUpdate(event, deps, rowId, liveSession) {
87067
+ if (event.type !== "session.updated") {
87068
+ return;
87069
+ }
87070
+ const info = event.payload.info;
87071
+ const newTitle = info?.title;
87072
+ if (!newTitle || newTitle === liveSession.sessionTitle) {
87073
+ return;
87074
+ }
87075
+ liveSession.setTitle?.(newTitle);
87076
+ deps.sessionRepository.updateSessionTitle(rowId, newTitle).catch((err) => console.warn("[direct-harness] updateSessionTitle error:", err));
87077
+ }
87078
+ function handleLiveSessionEvent(event, ctx) {
87079
+ const { handle, journal, extractChunk, idleConfig, deps, rowId, liveSession } = ctx;
87080
+ recordLiveSessionChunk(event, handle, journal, extractChunk, deps);
87081
+ if (event.type === "session.idle") {
87082
+ handleSessionIdle(handle, journal, idleConfig, deps.sessionRepository).catch((err) => console.warn("[direct-harness] idle handler error:", err));
87083
+ }
87084
+ handleLiveSessionTitleUpdate(event, deps, rowId, liveSession);
87085
+ }
86622
87086
  async function processOne(daemonSession, deps, session2) {
86623
87087
  const rowId = session2._id;
86624
87088
  try {
@@ -86631,21 +87095,7 @@ async function processOne(daemonSession, deps, session2) {
86631
87095
  await deps.sessionRepository.markFailed(rowId);
86632
87096
  return;
86633
87097
  }
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
- }
87098
+ const harness = await getOrCreateHarness(daemonSession, deps, session2, workspace);
86649
87099
  const liveSession = await harness.newSession({
86650
87100
  agent: session2.opencode?.lastUsedConfig.agent ?? "build",
86651
87101
  harnessSessionId: rowId
@@ -86677,30 +87127,15 @@ async function processOne(daemonSession, deps, session2) {
86677
87127
  close: close2
86678
87128
  };
86679
87129
  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
- }
87130
+ handleLiveSessionEvent(event, {
87131
+ handle,
87132
+ journal,
87133
+ extractChunk,
87134
+ idleConfig,
87135
+ deps,
87136
+ rowId,
87137
+ liveSession
87138
+ });
86704
87139
  });
86705
87140
  deps.activeSessions.set(rowId, handle);
86706
87141
  try {
@@ -87528,16 +87963,14 @@ var init_state_recovery = __esm(() => {
87528
87963
  for (const config3 of configsResult.configs) {
87529
87964
  if (config3.machineId === session2.machineId && config3.workingDir) {
87530
87965
  registeredCount++;
87531
- exports_Effect.runPromise(exports_Effect.tryPromise(() => session2.backend.mutation(api.workspaces.registerWorkspace, {
87966
+ yield* exports_Effect.forkDaemon(exports_Effect.tryPromise(() => session2.backend.mutation(api.workspaces.registerWorkspace, {
87532
87967
  sessionId: session2.sessionId,
87533
87968
  chatroomId,
87534
87969
  machineId: session2.machineId,
87535
87970
  workingDir: config3.workingDir,
87536
87971
  hostname: session2.config?.hostname ?? "unknown",
87537
87972
  registeredBy: config3.role
87538
- }))).catch((err) => {
87539
- console.warn(`[daemon] ⚠️ Failed to register workspace on recovery: ${err.message}`);
87540
- });
87973
+ })).pipe(exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[daemon] ⚠️ Failed to register workspace on recovery: ${err.message}`)))));
87541
87974
  }
87542
87975
  }
87543
87976
  }), () => exports_Effect.void);
@@ -87608,16 +88041,18 @@ class DaemonEventBus {
87608
88041
  }
87609
88042
 
87610
88043
  // src/events/daemon/agent/on-agent-exited.ts
87611
- async function handleAgentExited(deps, payload) {
87612
- await deps.handleExit({
88044
+ var onAgentExitedEffect = (payload) => exports_Effect.gen(function* () {
88045
+ const apm = yield* DaemonAgentProcessManagerService;
88046
+ yield* apm.handleExit({
87613
88047
  chatroomId: payload.chatroomId,
87614
88048
  role: payload.role,
87615
88049
  pid: payload.pid,
87616
88050
  code: payload.code,
87617
88051
  signal: payload.signal
87618
88052
  });
87619
- }
88053
+ });
87620
88054
  var init_on_agent_exited = __esm(() => {
88055
+ init_esm();
87621
88056
  init_daemon_services();
87622
88057
  });
87623
88058
 
@@ -87638,9 +88073,11 @@ var init_on_agent_stopped = () => {};
87638
88073
  // src/events/daemon/register-listeners.ts
87639
88074
  var registerEventListenersEffect = () => exports_Effect.gen(function* () {
87640
88075
  const session2 = yield* DaemonSessionService;
87641
- const apm = yield* DaemonAgentProcessManagerService;
88076
+ const runtime4 = yield* exports_Effect.runtime();
87642
88077
  const unsubs = [];
87643
- unsubs.push(session2.events.on("agent:exited", (payload) => handleAgentExited({ handleExit: (opts) => exports_Effect.runPromise(apm.handleExit(opts)) }, payload)));
88078
+ unsubs.push(session2.events.on("agent:exited", (payload) => {
88079
+ exports_Runtime.runFork(runtime4)(onAgentExitedEffect(payload));
88080
+ }));
87644
88081
  unsubs.push(session2.events.on("agent:started", (payload) => logAgentStarted(payload)));
87645
88082
  unsubs.push(session2.events.on("agent:stopped", (payload) => logAgentStopped(payload)));
87646
88083
  return () => {
@@ -87960,9 +88397,131 @@ var init_decide_resume_path = __esm(() => {
87960
88397
  ]);
87961
88398
  });
87962
88399
 
88400
+ // src/domain/agent-lifecycle/entities/agent-slot.ts
88401
+ function agentKey2(chatroomId, role) {
88402
+ return `${chatroomId}:${role.toLowerCase()}`;
88403
+ }
88404
+ var idleSlot = () => ({ state: "idle" });
88405
+ // src/domain/agent-lifecycle/policies/slot-transitions.ts
88406
+ function makeError(tag, from, event) {
88407
+ return {
88408
+ ok: false,
88409
+ error: { _tag: tag, from, event }
88410
+ };
88411
+ }
88412
+ function makeResult(slot) {
88413
+ return { ok: true, slot };
88414
+ }
88415
+ function transitionFromIdle(slot, event) {
88416
+ if (event.type === "ensure_running_requested") {
88417
+ return makeResult(slot);
88418
+ }
88419
+ if (event.type === "spawn_started") {
88420
+ return makeResult({ ...slot, state: "spawning", pendingOperationKey: event.operationKey });
88421
+ }
88422
+ if (event.type === "process_exited") {
88423
+ return makeResult(slot);
88424
+ }
88425
+ return makeError("InvalidTransition", slot.state, event.type);
88426
+ }
88427
+ function transitionFromSpawning(slot, event) {
88428
+ if (event.type === "spawn_started") {
88429
+ return makeResult({ ...slot, pendingOperationKey: event.operationKey });
88430
+ }
88431
+ if (event.type === "spawn_succeeded") {
88432
+ return makeResult({ ...slot, state: "running", pid: event.pid });
88433
+ }
88434
+ if (event.type === "spawn_failed" || event.type === "process_exited") {
88435
+ return makeResult({ state: "idle" });
88436
+ }
88437
+ return makeError("InvalidTransition", slot.state, event.type);
88438
+ }
88439
+ function transitionFromRunning(slot, event) {
88440
+ if (event.type === "stop_requested") {
88441
+ return makeResult({ ...slot, state: "stopping", pendingOperationKey: event.operationKey });
88442
+ }
88443
+ if (event.type === "process_exited") {
88444
+ if (slot.pid !== undefined && slot.pid !== event.pid) {
88445
+ return { ok: false, error: { _tag: "StalePid", expected: slot.pid, got: event.pid } };
88446
+ }
88447
+ return makeResult({ state: "idle" });
88448
+ }
88449
+ if (event.type === "stale_process_detected") {
88450
+ return makeResult({ state: "idle" });
88451
+ }
88452
+ return makeError("InvalidTransition", slot.state, event.type);
88453
+ }
88454
+ function transitionFromStopping(slot, event) {
88455
+ if (event.type === "stop_completed") {
88456
+ return makeResult({ state: "idle" });
88457
+ }
88458
+ if (event.type === "process_exited") {
88459
+ return { ok: false, error: { _tag: "IgnoredDuplicateExit" } };
88460
+ }
88461
+ return makeError("InvalidTransition", slot.state, event.type);
88462
+ }
88463
+ function transitionSlot(slot, event) {
88464
+ switch (slot.state) {
88465
+ case "idle":
88466
+ return transitionFromIdle(slot, event);
88467
+ case "spawning":
88468
+ return transitionFromSpawning(slot, event);
88469
+ case "running":
88470
+ return transitionFromRunning(slot, event);
88471
+ case "stopping":
88472
+ return transitionFromStopping(slot, event);
88473
+ }
88474
+ }
88475
+ function shouldIgnoreProcessExit(slot, exitPid) {
88476
+ if (slot.state === "stopping")
88477
+ return true;
88478
+ if (slot.pid !== undefined && slot.pid !== exitPid)
88479
+ return true;
88480
+ return false;
88481
+ }
88482
+
88483
+ // src/domain/agent-lifecycle/policies/restart-decision.ts
88484
+ function decideRestartAfterExit(input) {
88485
+ if (!shouldAutoRestartAfterProcessExit(input.stopReason)) {
88486
+ return { _tag: "NoRestart", reason: `Intentional stop: ${input.stopReason}` };
88487
+ }
88488
+ if (!input.harness || !input.workingDir) {
88489
+ return { _tag: "NoRestart", reason: "Missing harness or workingDir" };
88490
+ }
88491
+ if (input.isPermanentFailure) {
88492
+ return {
88493
+ _tag: "NoRestart",
88494
+ reason: input.permanentFailureMessage ?? "Permanent failure"
88495
+ };
88496
+ }
88497
+ if (input.backoffWaitMs && input.backoffWaitMs > 0) {
88498
+ return {
88499
+ _tag: "ScheduleRetry",
88500
+ waitMs: input.backoffWaitMs,
88501
+ spawnReason: "platform.crash_recovery",
88502
+ wantResume: input.wantResume
88503
+ };
88504
+ }
88505
+ return {
88506
+ _tag: "RestartNow",
88507
+ spawnReason: "platform.crash_recovery",
88508
+ wantResume: input.wantResume,
88509
+ bypassConcurrentLimit: true
88510
+ };
88511
+ }
88512
+ var init_restart_decision = __esm(() => {
88513
+ init_decide_resume_path();
88514
+ });
88515
+
88516
+ // src/domain/agent-lifecycle/policies/spawn-gate.ts
88517
+ function shouldBypassConcurrentLimit(spawnReason) {
88518
+ return spawnReason.startsWith("user.") || spawnReason === "platform.crash_recovery";
88519
+ }
88520
+
87963
88521
  // src/domain/agent-lifecycle/index.ts
87964
88522
  var init_agent_lifecycle = __esm(() => {
87965
88523
  init_decide_resume_path();
88524
+ init_restart_decision();
87966
88525
  });
87967
88526
 
87968
88527
  // src/domain/agent-lifecycle/policies/append-recent-log-line.ts
@@ -88158,6 +88717,90 @@ class RapidResumeTracker {
88158
88717
  }
88159
88718
  var RAPID_RESUME_WINDOW_MS = 30000, RAPID_RESUME_THRESHOLD = 5;
88160
88719
 
88720
+ // src/infrastructure/services/agent-lifecycle/agent-lifecycle-port-adapters.ts
88721
+ function createSpawnPort(spawning) {
88722
+ return {
88723
+ shouldAllowSpawn: (chatroomId, reason, options) => spawning.shouldAllowSpawn(chatroomId, reason, options),
88724
+ recordSpawn: (chatroomId) => exports_Effect.sync(() => {
88725
+ spawning.recordSpawn(chatroomId);
88726
+ }),
88727
+ recordExit: (chatroomId) => exports_Effect.sync(() => {
88728
+ spawning.recordExit(chatroomId);
88729
+ })
88730
+ };
88731
+ }
88732
+ function createHarnessSpawnPort(deps) {
88733
+ return {
88734
+ spawn: (args2) => exports_Effect.tryPromise({
88735
+ try: async () => {
88736
+ const service3 = deps.agentServices.get(args2.harness);
88737
+ if (!service3) {
88738
+ throw new Error(`Unknown agent harness: ${args2.harness}`);
88739
+ }
88740
+ const result = await service3.spawn({
88741
+ workingDir: args2.workingDir,
88742
+ prompt: args2.prompt,
88743
+ systemPrompt: args2.systemPrompt ?? "",
88744
+ model: args2.model,
88745
+ context: {
88746
+ machineId: deps.machineId,
88747
+ chatroomId: args2.chatroomId,
88748
+ role: args2.role
88749
+ }
88750
+ });
88751
+ return {
88752
+ pid: result.pid,
88753
+ harnessSessionId: result.harnessSessionId,
88754
+ onAgentEnd: (cb) => {
88755
+ result.onAgentEnd?.(cb);
88756
+ },
88757
+ onLogLine: result.onLogLine ? (lineCb) => {
88758
+ result.onLogLine?.((line) => {
88759
+ lineCb(line);
88760
+ });
88761
+ } : undefined
88762
+ };
88763
+ },
88764
+ catch: (e) => e instanceof Error ? e : new Error(String(e))
88765
+ }),
88766
+ stop: (pid, opts) => exports_Effect.tryPromise({
88767
+ try: async () => {
88768
+ for (const service3 of deps.agentServices.values()) {
88769
+ try {
88770
+ await service3.stop(pid, opts);
88771
+ return;
88772
+ } catch {}
88773
+ }
88774
+ },
88775
+ catch: (e) => e instanceof Error ? e : new Error(String(e))
88776
+ }),
88777
+ isAlive: (pid) => exports_Effect.sync(() => {
88778
+ return isProcessAlive((p) => process.kill(p, 0), pid);
88779
+ })
88780
+ };
88781
+ }
88782
+ function createAgentLifecyclePorts(deps) {
88783
+ return {
88784
+ spawn: createSpawnPort(deps.spawning),
88785
+ harness: createHarnessSpawnPort(deps),
88786
+ sessionId: deps.sessionId,
88787
+ machineId: deps.machineId
88788
+ };
88789
+ }
88790
+ var init_agent_lifecycle_port_adapters = __esm(() => {
88791
+ init_esm();
88792
+ });
88793
+
88794
+ // src/infrastructure/services/agent-lifecycle/agent-lifecycle-types.ts
88795
+ var AgentLifecyclePorts, AgentLifecycleService;
88796
+ var init_agent_lifecycle_types = __esm(() => {
88797
+ init_esm();
88798
+ AgentLifecyclePorts = class AgentLifecyclePorts extends exports_Context.Tag("AgentLifecyclePorts")() {
88799
+ };
88800
+ AgentLifecycleService = class AgentLifecycleService extends exports_Context.Tag("AgentLifecycleService")() {
88801
+ };
88802
+ });
88803
+
88161
88804
  // src/infrastructure/services/remote-agents/spawn-prompt.ts
88162
88805
  function createSpawnPrompt(raw) {
88163
88806
  const trimmed = raw?.trim();
@@ -88165,8 +88808,264 @@ function createSpawnPrompt(raw) {
88165
88808
  }
88166
88809
  var DEFAULT_TRIGGER_PROMPT = "Please read your system prompt carefully and follow the Getting Started instructions.";
88167
88810
 
88811
+ // src/infrastructure/services/agent-lifecycle/agent-lifecycle-service.ts
88812
+ var AgentLifecycleServiceLive;
88813
+ var init_agent_lifecycle_service = __esm(() => {
88814
+ init_esm();
88815
+ init_agent_lifecycle_types();
88816
+ init_agent_lifecycle();
88817
+ AgentLifecycleServiceLive = exports_Layer.effect(AgentLifecycleService, exports_Effect.gen(function* () {
88818
+ const ports = yield* AgentLifecyclePorts;
88819
+ const slotsRef = yield* exports_Ref.make(new Map);
88820
+ const getSlotFromRef = (key) => exports_Ref.get(slotsRef).pipe(exports_Effect.map((map18) => map18.get(key)));
88821
+ const setSlotInRef = (key, slot) => exports_Ref.update(slotsRef, (map18) => map18.set(key, slot));
88822
+ const removeSlotFromRef = (key) => exports_Ref.update(slotsRef, (map18) => {
88823
+ const next4 = new Map(map18);
88824
+ next4.delete(key);
88825
+ return next4;
88826
+ });
88827
+ const spawnAndRegister = (key, opts) => exports_Effect.gen(function* () {
88828
+ let slot = {
88829
+ ...idleSlot(),
88830
+ harness: opts.agentHarness,
88831
+ model: opts.model,
88832
+ workingDir: opts.workingDir,
88833
+ wantResume: opts.wantResume,
88834
+ _initPrompt: opts.initPrompt ?? "",
88835
+ _systemPrompt: opts.systemPrompt
88836
+ };
88837
+ const startedResult = transitionSlot(slot, {
88838
+ type: "spawn_started",
88839
+ operationKey: opts.reason
88840
+ });
88841
+ if (!startedResult.ok) {
88842
+ return { success: false, error: "spawn_failed" };
88843
+ }
88844
+ slot = startedResult.slot;
88845
+ const spawnHandle = yield* ports.harness.spawn({
88846
+ harness: opts.agentHarness,
88847
+ chatroomId: opts.chatroomId,
88848
+ role: opts.role,
88849
+ workingDir: opts.workingDir,
88850
+ model: opts.model,
88851
+ prompt: createSpawnPrompt(opts.initPrompt),
88852
+ systemPrompt: opts.systemPrompt
88853
+ }).pipe(exports_Effect.catchAll(() => exports_Effect.succeed(null)));
88854
+ if (!spawnHandle) {
88855
+ return { success: false, error: "spawn_failed" };
88856
+ }
88857
+ const succeededResult = transitionSlot(slot, {
88858
+ type: "spawn_succeeded",
88859
+ pid: spawnHandle.pid
88860
+ });
88861
+ if (!succeededResult.ok) {
88862
+ return { success: false, error: "spawn_failed" };
88863
+ }
88864
+ slot = succeededResult.slot;
88865
+ yield* ports.spawn.recordSpawn(opts.chatroomId);
88866
+ yield* setSlotInRef(key, slot);
88867
+ if (spawnHandle) {
88868
+ spawnHandle.onAgentEnd(() => {});
88869
+ }
88870
+ if (spawnHandle && spawnHandle.harnessSessionId) {
88871
+ const updatedSlot = { ...slot, harnessSessionId: spawnHandle.harnessSessionId };
88872
+ yield* setSlotInRef(key, updatedSlot);
88873
+ }
88874
+ return { success: true, pid: slot.pid };
88875
+ });
88876
+ const ensureRunning = (opts) => exports_Effect.gen(function* () {
88877
+ const key = agentKey2(opts.chatroomId, opts.role);
88878
+ const currentSlot = yield* getSlotFromRef(key);
88879
+ if (currentSlot && currentSlot.state !== "idle") {
88880
+ return {
88881
+ success: true,
88882
+ pid: currentSlot.pid
88883
+ };
88884
+ }
88885
+ const bypass = shouldBypassConcurrentLimit(opts.reason);
88886
+ const allowResult = ports.spawn.shouldAllowSpawn(opts.chatroomId, opts.reason, bypass ? { bypassConcurrentLimit: true } : undefined);
88887
+ if (!allowResult.allowed) {
88888
+ const error = allowResult.retryAfterMs ? "rate_limited" : "backoff";
88889
+ return { success: false, error };
88890
+ }
88891
+ const result = yield* spawnAndRegister(key, opts);
88892
+ if (!result.success && result.error) {
88893
+ yield* exports_Effect.logError(`Agent spawn failed for ${key}: ${result.error}`);
88894
+ }
88895
+ return result;
88896
+ });
88897
+ const stop4 = (opts) => exports_Effect.gen(function* () {
88898
+ const key = agentKey2(opts.chatroomId, opts.role);
88899
+ const slot = yield* getSlotFromRef(key);
88900
+ if (!slot) {
88901
+ return { success: false };
88902
+ }
88903
+ const stoppingResult = transitionSlot(slot, {
88904
+ type: "stop_requested",
88905
+ operationKey: opts.reason
88906
+ });
88907
+ if (!stoppingResult.ok) {
88908
+ return { success: false };
88909
+ }
88910
+ const stoppingSlot = stoppingResult.slot;
88911
+ yield* setSlotInRef(key, stoppingSlot);
88912
+ if (stoppingSlot.pid) {
88913
+ yield* ports.harness.stop(stoppingSlot.pid, { preserveForResume: false }).pipe(exports_Effect.ignore);
88914
+ }
88915
+ yield* ports.spawn.recordExit(opts.chatroomId);
88916
+ const completedResult = transitionSlot(stoppingSlot, {
88917
+ type: "stop_completed"
88918
+ });
88919
+ if (completedResult.ok) {
88920
+ yield* setSlotInRef(key, completedResult.slot);
88921
+ }
88922
+ return { success: true };
88923
+ });
88924
+ const executeRestart = (slot, chatroomId, role) => exports_Effect.gen(function* () {
88925
+ const restartOutcome = decideRestartAfterExit({
88926
+ stopReason: resolveStopReason(slot._stopReasonCode ?? 0, slot._stopReasonSignal ?? null),
88927
+ harness: slot.harness,
88928
+ workingDir: slot.workingDir,
88929
+ wantResume: slot.wantResume ?? false,
88930
+ isPermanentFailure: false,
88931
+ restartAllowed: true
88932
+ });
88933
+ yield* dispatchRestartOutcome(restartOutcome, slot, chatroomId, role);
88934
+ });
88935
+ const dispatchRestartOutcome = (outcome, slot, chatroomId, role) => exports_Effect.gen(function* () {
88936
+ switch (outcome._tag) {
88937
+ case "RestartNow": {
88938
+ yield* handleRestartNow(slot, chatroomId, role, outcome);
88939
+ break;
88940
+ }
88941
+ case "ScheduleRetry": {
88942
+ yield* handleScheduleRetry(slot, chatroomId, role, outcome);
88943
+ break;
88944
+ }
88945
+ case "NoRestart": {
88946
+ break;
88947
+ }
88948
+ }
88949
+ });
88950
+ const handleRestartNow = (slot, chatroomId, role, outcome) => exports_Effect.gen(function* () {
88951
+ if (!slot.harness) {
88952
+ yield* exports_Effect.logError(`Agent restart failed for ${chatroomId}:${role}: missing harness`);
88953
+ return;
88954
+ }
88955
+ const restartResult = yield* ensureRunning({
88956
+ chatroomId,
88957
+ role,
88958
+ agentHarness: slot.harness,
88959
+ workingDir: slot.workingDir ?? "",
88960
+ reason: outcome.spawnReason,
88961
+ wantResume: outcome.wantResume,
88962
+ initPrompt: slot._initPrompt,
88963
+ systemPrompt: slot._systemPrompt
88964
+ });
88965
+ if (!restartResult.success && restartResult.error) {
88966
+ yield* exports_Effect.logError(`Agent restart failed for ${chatroomId}:${role}: ${restartResult.error}`);
88967
+ }
88968
+ });
88969
+ const handleScheduleRetry = (slot, chatroomId, role, outcome) => exports_Effect.gen(function* () {
88970
+ if (!slot.harness) {
88971
+ yield* exports_Effect.logError(`Agent restart failed for ${chatroomId}:${role}: missing harness`);
88972
+ return;
88973
+ }
88974
+ yield* exports_Effect.forkDaemon(exports_Effect.sleep(exports_Duration.millis(outcome.waitMs)).pipe(exports_Effect.as(ensureRunning({
88975
+ chatroomId,
88976
+ role,
88977
+ agentHarness: slot.harness,
88978
+ workingDir: slot.workingDir ?? "",
88979
+ reason: outcome.spawnReason,
88980
+ wantResume: outcome.wantResume,
88981
+ initPrompt: slot._initPrompt,
88982
+ systemPrompt: slot._systemPrompt
88983
+ }))));
88984
+ });
88985
+ const handleExit = (opts) => exports_Effect.gen(function* () {
88986
+ const key = agentKey2(opts.chatroomId, opts.role);
88987
+ const slot = yield* getSlotFromRef(key);
88988
+ if (!slot) {
88989
+ return;
88990
+ }
88991
+ if (shouldIgnoreProcessExit(slot, opts.pid)) {
88992
+ return;
88993
+ }
88994
+ const stopReason = resolveStopReason(opts.code, opts.signal);
88995
+ const transitionResult = transitionSlot(slot, {
88996
+ type: "process_exited",
88997
+ pid: opts.pid
88998
+ });
88999
+ if (!transitionResult.ok) {
89000
+ return;
89001
+ }
89002
+ const exitedSlot = {
89003
+ ...transitionResult.slot,
89004
+ _stopReasonCode: opts.code,
89005
+ _stopReasonSignal: opts.signal,
89006
+ harness: slot.harness,
89007
+ workingDir: slot.workingDir,
89008
+ wantResume: slot.wantResume,
89009
+ _initPrompt: slot._initPrompt,
89010
+ _systemPrompt: slot._systemPrompt
89011
+ };
89012
+ yield* setSlotInRef(key, exitedSlot);
89013
+ yield* executeRestart(exitedSlot, opts.chatroomId, opts.role);
89014
+ const restartOutcome = decideRestartAfterExit({
89015
+ stopReason,
89016
+ harness: slot.harness,
89017
+ workingDir: slot.workingDir,
89018
+ wantResume: slot.wantResume ?? false,
89019
+ isPermanentFailure: false,
89020
+ restartAllowed: true
89021
+ });
89022
+ if (restartOutcome._tag === "NoRestart") {
89023
+ yield* removeSlotFromRef(key);
89024
+ }
89025
+ yield* ports.spawn.recordExit(opts.chatroomId);
89026
+ });
89027
+ const getSlot = (chatroomId, role) => getSlotFromRef(agentKey2(chatroomId, role));
89028
+ const listActive = () => exports_Ref.get(slotsRef).pipe(exports_Effect.map((map18) => {
89029
+ const results = [];
89030
+ for (const [key, slot] of map18) {
89031
+ if (slot.state !== "idle") {
89032
+ const [chatroomId, role] = key.split(":");
89033
+ results.push({ chatroomId, role, slot });
89034
+ }
89035
+ }
89036
+ return results;
89037
+ }));
89038
+ return {
89039
+ ensureRunning,
89040
+ stop: stop4,
89041
+ handleExit,
89042
+ getSlot,
89043
+ listActive
89044
+ };
89045
+ }));
89046
+ });
89047
+
89048
+ // src/infrastructure/services/agent-lifecycle/agent-lifecycle-runtime.ts
89049
+ function createAgentLifecycleRuntime(deps) {
89050
+ const layer = exports_Layer.provide(AgentLifecycleServiceLive, exports_Layer.succeed(AgentLifecyclePorts, createAgentLifecyclePorts(deps)));
89051
+ const runtime4 = exports_ManagedRuntime.make(layer);
89052
+ return {
89053
+ runtime: runtime4,
89054
+ runPromise(effect2) {
89055
+ return runtime4.runPromise(effect2);
89056
+ },
89057
+ dispose: () => runtime4.dispose()
89058
+ };
89059
+ }
89060
+ var init_agent_lifecycle_runtime = __esm(() => {
89061
+ init_esm();
89062
+ init_agent_lifecycle_port_adapters();
89063
+ init_agent_lifecycle_service();
89064
+ init_agent_lifecycle_types();
89065
+ });
89066
+
88168
89067
  // src/infrastructure/services/agent-process-manager/agent-process-manager.ts
88169
- function agentKey2(chatroomId, role) {
89068
+ function agentKey3(chatroomId, role) {
88170
89069
  return `${chatroomId}:${role.toLowerCase()}`;
88171
89070
  }
88172
89071
 
@@ -88177,17 +89076,47 @@ class AgentProcessManager {
88177
89076
  exitRetryQueue = [];
88178
89077
  exitRetryTimer = null;
88179
89078
  turnEndQueue = new TurnEndQueue;
89079
+ lifecycle;
88180
89080
  constructor(deps) {
88181
89081
  this.deps = {
88182
89082
  ...deps,
88183
89083
  resumeStormTracker: deps.resumeStormTracker ?? new RapidResumeTracker
88184
89084
  };
89085
+ const portAdapterDeps = {
89086
+ spawning: this.deps.spawning,
89087
+ agentServices: this.deps.agentServices,
89088
+ sessionId: this.deps.sessionId,
89089
+ machineId: this.deps.machineId,
89090
+ onAgentEnd: (args2) => void this.runHandleAgentEnd(args2)
89091
+ };
89092
+ this.lifecycle = createAgentLifecycleRuntime(portAdapterDeps);
89093
+ }
89094
+ updateSlotsMirror(chatroomId, role, slot) {
89095
+ const key = agentKey3(chatroomId, role);
89096
+ const existing = this.slots.get(key);
89097
+ if (!existing || existing.state !== slot.state || existing.pid !== slot.pid) {
89098
+ this.slots.set(key, {
89099
+ state: slot.state,
89100
+ pid: slot.pid,
89101
+ harness: slot.harness,
89102
+ harnessSessionId: slot.harnessSessionId,
89103
+ model: slot.model,
89104
+ workingDir: slot.workingDir,
89105
+ startedAt: slot.startedAt,
89106
+ resumeInFlight: slot.resumeInFlight,
89107
+ recentLogLines: slot.recentLogLines,
89108
+ wantResume: slot.wantResume
89109
+ });
89110
+ }
89111
+ }
89112
+ getSlotFromMirror(chatroomId, role) {
89113
+ return this.slots.get(agentKey3(chatroomId, role));
88185
89114
  }
88186
- async whenTurnEndsIdle() {
88187
- await this.turnEndQueue.whenIdle();
89115
+ whenTurnEndsIdle() {
89116
+ return this.turnEndQueue.whenIdle();
88188
89117
  }
88189
89118
  async ensureRunning(opts) {
88190
- const key = agentKey2(opts.chatroomId, opts.role);
89119
+ const key = agentKey3(opts.chatroomId, opts.role);
88191
89120
  const slot = this.getOrCreateSlot(key);
88192
89121
  if (slot.state === "running" && slot.pid && !isProcessAlive(this.deps.processes.kill, slot.pid)) {
88193
89122
  slot.state = "idle";
@@ -88211,30 +89140,21 @@ class AgentProcessManager {
88211
89140
  return operation;
88212
89141
  }
88213
89142
  async stop(opts) {
88214
- const key = agentKey2(opts.chatroomId, opts.role);
89143
+ const key = agentKey3(opts.chatroomId, opts.role);
88215
89144
  const slot = this.slots.get(key);
89145
+ const earlyResult = await this.handleStopEarlyReturns(slot, opts, key);
89146
+ if (earlyResult) {
89147
+ return earlyResult;
89148
+ }
89149
+ const actualSlot = slot;
89150
+ if (actualSlot.pendingOperation) {
89151
+ await actualSlot.pendingOperation;
89152
+ }
89153
+ return { success: true };
89154
+ }
89155
+ async handleStopEarlyReturns(slot, opts, key) {
88216
89156
  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
- });
89157
+ await this.killAndRecordForIdleSlot(slot, opts);
88238
89158
  return { success: true };
88239
89159
  }
88240
89160
  if (slot.state === "stopping" && slot.pendingOperation) {
@@ -88250,14 +89170,48 @@ class AgentProcessManager {
88250
89170
  slot.state = "stopping";
88251
89171
  const operation = this.doStop(key, slot, pid, opts);
88252
89172
  slot.pendingOperation = operation;
88253
- await operation;
88254
- return { success: true };
89173
+ return null;
89174
+ }
89175
+ async killAndRecordForIdleSlot(slot, opts) {
89176
+ const eventPid = opts.pid;
89177
+ if (eventPid && eventPid > 0) {
89178
+ try {
89179
+ this.deps.processes.kill(eventPid, "SIGTERM");
89180
+ } catch {}
89181
+ }
89182
+ const exitArgs1 = {
89183
+ sessionId: this.deps.sessionId,
89184
+ machineId: this.deps.machineId,
89185
+ chatroomId: opts.chatroomId,
89186
+ role: opts.role,
89187
+ pid: eventPid ?? 0,
89188
+ stopReason: opts.reason,
89189
+ exitCode: undefined,
89190
+ signal: undefined,
89191
+ agentHarness: undefined
89192
+ };
89193
+ this.deps.backend.mutation(api.machines.recordAgentExited, exitArgs1).catch((err) => {
89194
+ console.log(` ⚠️ Failed to record agent exit (idle cleanup): ${err.message}`);
89195
+ this.queueExitRetry({ role: opts.role, args: exitArgs1 });
89196
+ });
88255
89197
  }
88256
89198
  async runHandleAgentEnd(opts) {
88257
- const slot = this.slots.get(agentKey2(opts.chatroomId, opts.role));
89199
+ const slot = this.slots.get(agentKey3(opts.chatroomId, opts.role));
88258
89200
  const service3 = this.deps.agentServices.get(opts.harness);
88259
89201
  const capabilities = getHarnessCapabilities(opts.harness);
88260
89202
  const supportsSessionResume = capabilities.supportsSessionResume && typeof service3?.resumeTurn === "function";
89203
+ this.updateSlotsMirror(opts.chatroomId, opts.role, {
89204
+ state: slot?.state ?? "idle",
89205
+ pid: slot?.pid,
89206
+ harness: slot?.harness,
89207
+ harnessSessionId: slot?.harnessSessionId,
89208
+ model: slot?.model,
89209
+ workingDir: slot?.workingDir,
89210
+ startedAt: slot?.startedAt,
89211
+ resumeInFlight: slot?.resumeInFlight,
89212
+ recentLogLines: slot?.recentLogLines,
89213
+ wantResume: slot?.wantResume
89214
+ });
88261
89215
  console.log(`[AgentProcessManager] lifecycle.turn.completed: role=${opts.role} pid=${opts.pid} harness=${opts.harness} supportsResume=${supportsSessionResume}`);
88262
89216
  const result = await handleTurnCompleted({
88263
89217
  resumeStormTracker: this.deps.resumeStormTracker,
@@ -88296,37 +89250,76 @@ class AgentProcessManager {
88296
89250
  }
88297
89251
  }
88298
89252
  async handleExit(opts) {
88299
- const key = agentKey2(opts.chatroomId, opts.role);
89253
+ const key = agentKey3(opts.chatroomId, opts.role);
88300
89254
  const slot = this.slots.get(key);
88301
- if (!slot || slot.pid !== opts.pid) {
88302
- return;
88303
- }
88304
- if (slot.state === "stopping") {
89255
+ if (!slot || slot.pid !== opts.pid || slot.state === "stopping") {
88305
89256
  return;
88306
89257
  }
88307
89258
  const stopReason = resolveStopReason(opts.code, opts.signal);
88308
89259
  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
89260
+ const ctx = this.captureExitContext(slot, opts, stopReason);
89261
+ await this.preserveHarnessSessionOnExit(key, slot, ctx);
89262
+ const lifecyclePromise = this.lifecycle.runPromise(exports_Effect.gen(function* () {
89263
+ const svc = yield* AgentLifecycleService;
89264
+ yield* svc.handleExit({
89265
+ chatroomId: opts.chatroomId,
89266
+ role: opts.role,
89267
+ pid: opts.pid,
89268
+ code: opts.code,
89269
+ signal: opts.signal
88324
89270
  });
89271
+ }));
89272
+ this.resetSlotAfterExit(slot);
89273
+ await this.emitExitEvent(slot, opts, ctx);
89274
+ try {
89275
+ await this.deps.persistence.clearAgentPid(this.deps.machineId, opts.chatroomId, opts.role);
89276
+ } catch {}
89277
+ this.untrackAllServices(opts.pid);
89278
+ lifecyclePromise.then(() => this.dispatchRestartAfterExit(opts, ctx, key)).catch(() => {});
89279
+ }
89280
+ captureExitContext(slot, opts, stopReason) {
89281
+ return {
89282
+ harness: slot.harness,
89283
+ model: slot.model,
89284
+ workingDir: slot.workingDir,
89285
+ harnessSessionId: slot.harnessSessionId,
89286
+ wantResume: slot.wantResume,
89287
+ recentLogLines: slot.recentLogLines,
89288
+ stopReason
89289
+ };
89290
+ }
89291
+ recordExitHarnessSession(key, slot, harness, harnessSessionId, ctx) {
89292
+ const service3 = this.deps.agentServices.get(harness);
89293
+ const harnessMeta = service3 && slot.pid ? this.readHarnessReconnectMetadata(service3, slot.pid) : undefined;
89294
+ this.recordLastHarnessSession(key, {
89295
+ harnessSessionId,
89296
+ harness,
89297
+ agentName: harnessMeta?.agentName ?? "",
89298
+ workingDir: ctx.workingDir ?? "",
89299
+ model: ctx.model ?? harnessMeta?.model
89300
+ });
89301
+ }
89302
+ async preserveHarnessSessionOnExit(key, slot, ctx) {
89303
+ const { harness, harnessSessionId, stopReason } = ctx;
89304
+ if (!harness || !harnessSessionId) {
89305
+ return;
88325
89306
  }
89307
+ if (!getHarnessCapabilities(harness).supportsSessionResume) {
89308
+ return;
89309
+ }
89310
+ if (!shouldRetainHarnessSessionForReconnect(stopReason)) {
89311
+ return;
89312
+ }
89313
+ this.recordExitHarnessSession(key, slot, harness, harnessSessionId, ctx);
89314
+ }
89315
+ resetSlotAfterExit(slot) {
88326
89316
  slot.state = "idle";
88327
89317
  slot.pid = undefined;
88328
89318
  slot.startedAt = undefined;
88329
89319
  slot.pendingOperation = undefined;
89320
+ }
89321
+ async emitExitEvent(slot, opts, ctx) {
89322
+ const stopReason = ctx.stopReason;
88330
89323
  const exitArgs2 = {
88331
89324
  sessionId: this.deps.sessionId,
88332
89325
  machineId: this.deps.machineId,
@@ -88337,42 +89330,36 @@ class AgentProcessManager {
88337
89330
  stopSignal: stopReason === "agent_process.signal" ? opts.signal ?? undefined : undefined,
88338
89331
  exitCode: opts.code ?? undefined,
88339
89332
  signal: opts.signal ?? undefined,
88340
- agentHarness: harness
89333
+ agentHarness: ctx.harness
88341
89334
  };
88342
89335
  this.deps.backend.mutation(api.machines.recordAgentExited, exitArgs2).catch((err) => {
88343
89336
  console.log(` ⚠️ Failed to record agent exit event: ${err.message}`);
88344
89337
  this.queueExitRetry({ role: opts.role, args: exitArgs2 });
88345
89338
  });
88346
- try {
88347
- await this.deps.persistence.clearAgentPid(this.deps.machineId, opts.chatroomId, opts.role);
88348
- } catch {}
89339
+ }
89340
+ untrackAllServices(pid) {
88349
89341
  for (const service3 of this.deps.agentServices.values()) {
88350
- service3.untrack(opts.pid);
89342
+ service3.untrack(pid);
88351
89343
  }
88352
- if (!shouldAutoRestartAfterProcessExit(stopReason)) {
88353
- if (stopReason === "user.stop" || stopReason === "platform.team_switch" || stopReason === "daemon.shutdown") {
89344
+ }
89345
+ dispatchRestartAfterExit(opts, ctx, _key) {
89346
+ const stopReasonForRestart = resolveStopReason(opts.code, opts.signal);
89347
+ if (!shouldAutoRestartAfterProcessExit(stopReasonForRestart)) {
89348
+ if (stopReasonForRestart === "user.stop" || stopReasonForRestart === "platform.team_switch" || stopReasonForRestart === "daemon.shutdown") {
88354
89349
  this.deps.crashLoop.clear(opts.chatroomId, opts.role);
88355
89350
  }
88356
89351
  return;
88357
89352
  }
89353
+ this.maybeRestartAgent(opts, ctx);
89354
+ }
89355
+ maybeRestartAgent(opts, ctx) {
89356
+ const { harness, model, workingDir, recentLogLines } = ctx;
88358
89357
  if (!harness || !workingDir) {
88359
89358
  console.log(`[AgentProcessManager] ⚠️ Cannot restart — missing harness or workingDir ` + `(role: ${opts.role}, harness: ${harness ?? "none"}, workingDir: ${workingDir ?? "none"})`);
88360
89359
  return;
88361
89360
  }
88362
89361
  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
- });
89362
+ this.handlePermanentFailureForRestart(opts, recentLogLines);
88376
89363
  return;
88377
89364
  }
88378
89365
  this.ensureRunning({
@@ -88382,22 +89369,39 @@ class AgentProcessManager {
88382
89369
  model,
88383
89370
  workingDir,
88384
89371
  reason: "platform.crash_recovery",
88385
- wantResume: wantResume ?? true
89372
+ wantResume: ctx.wantResume ?? true
88386
89373
  }).catch((err) => {
88387
89374
  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
- });
89375
+ this.emitStartFailedEvent(opts.role, opts.chatroomId, err.message);
89376
+ });
89377
+ }
89378
+ handlePermanentFailureForRestart(opts, recentLogLines) {
89379
+ const error = formatPermanentHarnessFailureMessage(recentLogLines ?? []);
89380
+ console.log(`[AgentProcessManager] ⛔ Skipping restart — ${error}`);
89381
+ this.deps.crashLoop.clear(opts.chatroomId, opts.role);
89382
+ const key = agentKey3(opts.chatroomId, opts.role);
89383
+ this.clearLastHarnessSession(key);
89384
+ this.deps.backend.mutation(api.machines.emitAgentStartFailed, {
89385
+ sessionId: this.deps.sessionId,
89386
+ machineId: this.deps.machineId,
89387
+ chatroomId: opts.chatroomId,
89388
+ role: opts.role,
89389
+ error
89390
+ }).catch((emitErr) => {
89391
+ console.log(` ⚠️ Failed to emit startFailed event: ${emitErr.message}`);
88397
89392
  });
88398
89393
  }
89394
+ emitStartFailedEvent(role, chatroomId, error) {
89395
+ this.deps.backend.mutation(api.machines.emitAgentStartFailed, {
89396
+ sessionId: this.deps.sessionId,
89397
+ machineId: this.deps.machineId,
89398
+ chatroomId,
89399
+ role,
89400
+ error
89401
+ }).catch(() => {});
89402
+ }
88399
89403
  getSlot(chatroomId, role) {
88400
- return this.slots.get(agentKey2(chatroomId, role));
89404
+ return this.getSlotFromMirror(chatroomId, role);
88401
89405
  }
88402
89406
  listActive() {
88403
89407
  const result = [];
@@ -88468,13 +89472,19 @@ class AgentProcessManager {
88468
89472
  untrackChildPid(pid);
88469
89473
  }
88470
89474
  async killExistingBeforeSpawn(chatroomId, role) {
88471
- const key = agentKey2(chatroomId, role);
89475
+ const key = agentKey3(chatroomId, role);
89476
+ await this.killInMemorySlotIfAlive(key, chatroomId, role);
89477
+ await this.killPersistedProcessIfAlive(chatroomId, role);
89478
+ }
89479
+ async killInMemorySlotIfAlive(key, chatroomId, role) {
88472
89480
  const slot = this.slots.get(key);
88473
89481
  if (slot?.pid && isProcessAlive(this.deps.processes.kill, slot.pid) && (slot.state === "running" || slot.state === "spawning")) {
88474
- const pid2 = slot.pid;
89482
+ const pid = slot.pid;
88475
89483
  slot.state = "stopping";
88476
- await this.doStop(key, slot, pid2, { chatroomId, role, reason: "daemon.respawn" });
89484
+ await this.doStop(key, slot, pid, { chatroomId, role, reason: "daemon.respawn" });
88477
89485
  }
89486
+ }
89487
+ async killPersistedProcessIfAlive(chatroomId, role) {
88478
89488
  let entries2 = [];
88479
89489
  try {
88480
89490
  entries2 = await this.deps.persistence.listAgentEntries(this.deps.machineId);
@@ -88487,11 +89497,10 @@ class AgentProcessManager {
88487
89497
  }
88488
89498
  const { pid, harness } = persisted.entry;
88489
89499
  if (!isProcessAlive(this.deps.processes.kill, pid)) {
88490
- try {
88491
- await this.deps.persistence.clearAgentPid(this.deps.machineId, chatroomId, role);
88492
- } catch {}
89500
+ await this.deps.persistence.clearAgentPid(this.deps.machineId, chatroomId, role).catch(() => {});
88493
89501
  return;
88494
89502
  }
89503
+ const key = agentKey3(chatroomId, role);
88495
89504
  const currentSlot = this.slots.get(key);
88496
89505
  if (currentSlot?.pid === pid && currentSlot.state !== "idle") {
88497
89506
  return;
@@ -88574,30 +89583,20 @@ class AgentProcessManager {
88574
89583
  }
88575
89584
  }
88576
89585
  async tryDaemonMemoryResume(opts) {
88577
- const capabilities = getHarnessCapabilities(opts.agentHarness);
88578
- if (!capabilities.supportsSessionResume) {
89586
+ const validationResult = this.validateDaemonMemoryResumePreconditions(opts);
89587
+ if (validationResult) {
88579
89588
  return null;
88580
89589
  }
88581
89590
  const stored = this.lastHarnessSessions.get(opts.key);
88582
89591
  if (!stored) {
88583
89592
  return null;
88584
89593
  }
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);
89594
+ const resumeFromDaemonMemory = opts.service.resumeFromDaemonMemory;
89595
+ if (!resumeFromDaemonMemory) {
88597
89596
  return null;
88598
89597
  }
88599
89598
  try {
88600
- const spawnResult = await opts.service.resumeFromDaemonMemory({
89599
+ const spawnResult = await resumeFromDaemonMemory({
88601
89600
  workingDir: stored.workingDir,
88602
89601
  prompt: createSpawnPrompt(opts.initPrompt),
88603
89602
  systemPrompt: opts.systemPrompt,
@@ -88621,6 +89620,31 @@ class AgentProcessManager {
88621
89620
  return null;
88622
89621
  }
88623
89622
  }
89623
+ validateDaemonMemoryResumePreconditions(opts) {
89624
+ const capabilities = getHarnessCapabilities(opts.agentHarness);
89625
+ if (!capabilities.supportsSessionResume) {
89626
+ return null;
89627
+ }
89628
+ const stored = this.lastHarnessSessions.get(opts.key);
89629
+ if (!stored) {
89630
+ return null;
89631
+ }
89632
+ if (stored.workingDir !== opts.workingDir) {
89633
+ this.clearLastHarnessSession(opts.key);
89634
+ this.emitSessionResumeFailed(opts.chatroomId, opts.role, "working directory changed", stored.harnessSessionId);
89635
+ return "working directory changed";
89636
+ }
89637
+ if (stored.harness !== opts.agentHarness || !stored.agentName) {
89638
+ this.clearLastHarnessSession(opts.key);
89639
+ this.emitSessionResumeFailed(opts.chatroomId, opts.role, stored.harness !== opts.agentHarness ? "harness changed" : "incomplete session in daemon memory", stored.harnessSessionId);
89640
+ return "validation failed";
89641
+ }
89642
+ if (!opts.service.resumeFromDaemonMemory) {
89643
+ this.emitSessionResumeFailed(opts.chatroomId, opts.role, "daemon-memory session resume not yet supported", stored.harnessSessionId);
89644
+ return "not supported";
89645
+ }
89646
+ return null;
89647
+ }
88624
89648
  async emitSessionResumed(chatroomId, role, harnessSessionId) {
88625
89649
  try {
88626
89650
  await this.deps.backend.mutation(api.machines.emitSessionResumed, {
@@ -88650,195 +89674,248 @@ class AgentProcessManager {
88650
89674
  console.log(` ⚠️ Failed to emit sessionResumeFailed event: ${err.message}`);
88651
89675
  }
88652
89676
  }
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}`);
89677
+ resetSlotIdle(slot) {
89678
+ slot.state = "idle";
89679
+ slot.pendingOperation = undefined;
89680
+ }
89681
+ checkRateLimitGate(opts, slot) {
89682
+ const spawnCheck = this.deps.spawning.shouldAllowSpawn(opts.chatroomId, opts.reason, {
89683
+ bypassConcurrentLimit: opts.reason.startsWith("user.")
89684
+ });
89685
+ if (!spawnCheck.allowed) {
89686
+ this.resetSlotIdle(slot);
89687
+ return { success: false, error: "rate_limited" };
89688
+ }
89689
+ return null;
89690
+ }
89691
+ checkCrashLoopGate(opts, slot) {
89692
+ if (opts.reason !== "platform.crash_recovery") {
89693
+ return null;
89694
+ }
89695
+ const loopCheck = this.deps.crashLoop.record(opts.chatroomId, opts.role, this.deps.clock.now());
89696
+ if (loopCheck.allowed) {
89697
+ return null;
89698
+ }
89699
+ if (loopCheck.waitMs !== undefined && loopCheck.waitMs > 0) {
89700
+ console.log(` ⏳ Agent restart backoff: waiting ${loopCheck.waitMs}ms before retry`);
89701
+ this.resetSlotIdle(slot);
89702
+ return { success: false, error: "backoff" };
89703
+ }
89704
+ this.deps.backend.mutation(api.machines.emitRestartLimitReached, {
89705
+ sessionId: this.deps.sessionId,
89706
+ machineId: this.deps.machineId,
89707
+ chatroomId: opts.chatroomId,
89708
+ role: opts.role,
89709
+ restartCount: loopCheck.restartCount,
89710
+ windowMs: loopCheck.windowMs
89711
+ }).catch((err) => {
89712
+ console.log(` ⚠️ Failed to emit restartLimitReached event: ${err.message}`);
89713
+ });
89714
+ this.resetSlotIdle(slot);
89715
+ return { success: false, error: "crash_loop" };
89716
+ }
89717
+ async validateWorkingDirGate(opts, slot) {
88657
89718
  try {
88658
- const spawnCheck = this.deps.spawning.shouldAllowSpawn(opts.chatroomId, opts.reason, {
88659
- bypassConcurrentLimit: opts.reason.startsWith("user.")
89719
+ const dirStat = await this.deps.fs.stat(opts.workingDir);
89720
+ if (!dirStat.isDirectory()) {
89721
+ this.resetSlotIdle(slot);
89722
+ return {
89723
+ success: false,
89724
+ error: `Working directory is not a directory: ${opts.workingDir}`
89725
+ };
89726
+ }
89727
+ } catch {
89728
+ this.resetSlotIdle(slot);
89729
+ return { success: false, error: `Working directory does not exist: ${opts.workingDir}` };
89730
+ }
89731
+ return null;
89732
+ }
89733
+ async fetchInitPromptResult(opts, slot) {
89734
+ let initPromptResult;
89735
+ try {
89736
+ initPromptResult = await this.deps.backend.query(api.messages.getInitPrompt, {
89737
+ sessionId: this.deps.sessionId,
89738
+ chatroomId: opts.chatroomId,
89739
+ role: opts.role,
89740
+ convexUrl: this.deps.convexUrl
88660
89741
  });
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,
89742
+ } catch (e) {
89743
+ this.resetSlotIdle(slot);
89744
+ return {
89745
+ ok: false,
89746
+ result: { success: false, error: `Failed to fetch init prompt: ${e.message}` }
89747
+ };
89748
+ }
89749
+ if (!initPromptResult?.prompt) {
89750
+ this.resetSlotIdle(slot);
89751
+ return {
89752
+ ok: false,
89753
+ result: { success: false, error: "Failed to fetch init prompt from backend" }
89754
+ };
89755
+ }
89756
+ return {
89757
+ ok: true,
89758
+ initialMessage: initPromptResult.initialMessage,
89759
+ rolePrompt: initPromptResult.rolePrompt
89760
+ };
89761
+ }
89762
+ async spawnAgentForEnsureRunning(key, slot, opts, initPrompt, wantResume) {
89763
+ const service3 = this.deps.agentServices.get(opts.agentHarness);
89764
+ if (!service3) {
89765
+ this.resetSlotIdle(slot);
89766
+ return {
89767
+ ok: false,
89768
+ result: { success: false, error: `Unknown agent harness: ${opts.agentHarness}` }
89769
+ };
89770
+ }
89771
+ let spawnResult;
89772
+ const resumePath = decideResumePathOnRestart({
89773
+ supportsSessionResume: getHarnessCapabilities(opts.agentHarness).supportsSessionResume,
89774
+ wantResume,
89775
+ hasStoredSnapshot: this.lastHarnessSessions.has(key)
89776
+ });
89777
+ if (resumePath === "daemon_memory") {
89778
+ spawnResult = await this.tryDaemonMemoryResume({
89779
+ key,
89780
+ chatroomId: opts.chatroomId,
89781
+ role: opts.role,
89782
+ agentHarness: opts.agentHarness,
89783
+ workingDir: opts.workingDir,
89784
+ model: opts.model,
89785
+ initPrompt: initPrompt.initialMessage,
89786
+ systemPrompt: initPrompt.rolePrompt,
89787
+ service: service3
89788
+ }) ?? undefined;
89789
+ }
89790
+ if (!spawnResult) {
89791
+ try {
89792
+ spawnResult = await service3.spawn({
89793
+ workingDir: opts.workingDir,
89794
+ prompt: createSpawnPrompt(initPrompt.initialMessage),
89795
+ systemPrompt: initPrompt.rolePrompt,
89796
+ model: opts.model,
89797
+ context: {
88677
89798
  machineId: this.deps.machineId,
88678
89799
  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
89800
+ role: opts.role
89801
+ }
88712
89802
  });
88713
89803
  } 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}` };
89804
+ this.resetSlotIdle(slot);
89805
+ return {
89806
+ ok: false,
89807
+ result: { success: false, error: `Failed to spawn agent: ${e.message}` }
89808
+ };
88728
89809
  }
88729
- let spawnResult;
88730
- const resumePath = decideResumePathOnRestart({
88731
- supportsSessionResume: getHarnessCapabilities(opts.agentHarness).supportsSessionResume,
88732
- wantResume,
88733
- hasStoredSnapshot: this.lastHarnessSessions.has(key)
89810
+ }
89811
+ return { ok: true, spawnResult };
89812
+ }
89813
+ assignRunningSlotState(key, slot, opts, spawnResult, wantResume, pid) {
89814
+ slot.state = "running";
89815
+ slot.pid = pid;
89816
+ slot.harness = opts.agentHarness;
89817
+ slot.harnessSessionId = spawnResult.harnessSessionId;
89818
+ if (spawnResult.harnessSessionId) {
89819
+ this.recordLastHarnessSession(key, {
89820
+ harnessSessionId: spawnResult.harnessSessionId,
89821
+ harness: opts.agentHarness,
89822
+ agentName: spawnResult.harnessReconnect?.agentName ?? "",
89823
+ workingDir: opts.workingDir,
89824
+ model: opts.model ?? spawnResult.harnessReconnect?.model
88734
89825
  });
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,
89826
+ }
89827
+ slot.model = opts.model;
89828
+ slot.wantResume = wantResume;
89829
+ slot.workingDir = opts.workingDir;
89830
+ slot.startedAt = this.deps.clock.now();
89831
+ slot.pendingOperation = undefined;
89832
+ slot.recentLogLines = [];
89833
+ this.deps.resumeStormTracker.reset(opts.chatroomId, opts.role);
89834
+ }
89835
+ emitSpawnedAgentUpdate(opts, spawnResult, pid) {
89836
+ this.deps.backend.mutation(api.machines.updateSpawnedAgent, {
89837
+ sessionId: this.deps.sessionId,
89838
+ machineId: this.deps.machineId,
89839
+ chatroomId: opts.chatroomId,
89840
+ role: opts.role,
89841
+ pid,
89842
+ model: opts.model,
89843
+ reason: opts.reason,
89844
+ ...spawnResult.harnessSessionId ? { harnessSessionId: spawnResult.harnessSessionId } : {}
89845
+ }).catch((err) => {
89846
+ console.log(` ⚠️ Failed to update PID in backend: ${err.message}`);
89847
+ });
89848
+ }
89849
+ registerSpawnCallbacks(slot, opts, spawnResult, pid) {
89850
+ if (spawnResult.onLogLine) {
89851
+ spawnResult.onLogLine((line) => appendRecentLogLine(slot, line));
89852
+ }
89853
+ spawnResult.onExit(({ code: code2, signal }) => {
89854
+ this.handleExit({
88795
89855
  chatroomId: opts.chatroomId,
88796
89856
  role: opts.role,
88797
89857
  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}`);
89858
+ code: code2,
89859
+ signal
88803
89860
  });
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({
89861
+ });
89862
+ if (spawnResult.onAgentEnd) {
89863
+ spawnResult.onAgentEnd(() => {
89864
+ this.turnEndQueue.enqueue(() => this.runHandleAgentEnd({
88809
89865
  chatroomId: opts.chatroomId,
88810
89866
  role: opts.role,
88811
89867
  pid,
88812
- code: code2,
88813
- signal
88814
- });
89868
+ harness: opts.agentHarness
89869
+ }));
88815
89870
  });
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
- });
89871
+ }
89872
+ let lastReportedTokenAt = 0;
89873
+ spawnResult.onOutput(() => {
89874
+ const now = this.deps.clock.now();
89875
+ if (now - lastReportedTokenAt >= 30000) {
89876
+ lastReportedTokenAt = now;
89877
+ this.deps.backend.mutation(api.participants.updateTokenActivity, {
89878
+ sessionId: this.deps.sessionId,
89879
+ chatroomId: opts.chatroomId,
89880
+ role: opts.role
89881
+ }).catch(() => {});
88825
89882
  }
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 };
89883
+ });
89884
+ }
89885
+ async finalizeRunningSlot(key, slot, opts, spawnResult, wantResume) {
89886
+ const { pid } = spawnResult;
89887
+ this.deps.spawning.recordSpawn(opts.chatroomId);
89888
+ this.assignRunningSlotState(key, slot, opts, spawnResult, wantResume, pid);
89889
+ this.emitSpawnedAgentUpdate(opts, spawnResult, pid);
89890
+ try {
89891
+ await this.deps.persistence.persistAgentPid(this.deps.machineId, opts.chatroomId, opts.role, pid, opts.agentHarness);
89892
+ } catch {}
89893
+ this.registerSpawnCallbacks(slot, opts, spawnResult, pid);
89894
+ }
89895
+ async doEnsureRunning(key, slot, opts) {
89896
+ slot.state = "spawning";
89897
+ const wantResume = opts.wantResume;
89898
+ console.log(`[AgentProcessManager] harness start: role=${opts.role} harness=${opts.agentHarness} wantResume=${wantResume} reason=${opts.reason}`);
89899
+ try {
89900
+ const rateLimit = this.checkRateLimitGate(opts, slot);
89901
+ if (rateLimit)
89902
+ return rateLimit;
89903
+ const crashLoop = this.checkCrashLoopGate(opts, slot);
89904
+ if (crashLoop)
89905
+ return crashLoop;
89906
+ const workingDir = await this.validateWorkingDirGate(opts, slot);
89907
+ if (workingDir)
89908
+ return workingDir;
89909
+ const initPrompt = await this.fetchInitPromptResult(opts, slot);
89910
+ if (!initPrompt.ok)
89911
+ return initPrompt.result;
89912
+ const spawn5 = await this.spawnAgentForEnsureRunning(key, slot, opts, initPrompt, wantResume);
89913
+ if (!spawn5.ok)
89914
+ return spawn5.result;
89915
+ await this.finalizeRunningSlot(key, slot, opts, spawn5.spawnResult, wantResume);
89916
+ return { success: true, pid: spawn5.spawnResult.pid };
88839
89917
  } catch (e) {
88840
- slot.state = "idle";
88841
- slot.pendingOperation = undefined;
89918
+ this.resetSlotIdle(slot);
88842
89919
  return { success: false, error: `Unexpected error: ${e.message}` };
88843
89920
  }
88844
89921
  }
@@ -88851,64 +89928,75 @@ class AgentProcessManager {
88851
89928
  readHarnessReconnectMetadata(service3, pid) {
88852
89929
  return service3.getHarnessReconnectContext?.(pid);
88853
89930
  }
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) {
89931
+ shouldPreserveHarnessOnStop(slot, opts) {
89932
+ const harness = slot.harness;
89933
+ const supportsResume = harness ? getHarnessCapabilities(harness).supportsSessionResume : false;
89934
+ return shouldPreserveHarnessTeardown(opts.reason, supportsResume, Boolean(slot.harnessSessionId));
89935
+ }
89936
+ recordHarnessSessionOnStop(key, slot, pid, service3) {
89937
+ const harness = slot.harness;
89938
+ const harnessMeta = service3 ? this.readHarnessReconnectMetadata(service3, pid) : undefined;
89939
+ this.recordLastHarnessSession(key, {
89940
+ harnessSessionId: slot.harnessSessionId,
89941
+ harness,
89942
+ agentName: harnessMeta?.agentName ?? "",
89943
+ workingDir: slot.workingDir ?? "",
89944
+ model: slot.model ?? harnessMeta?.model
89945
+ });
89946
+ }
89947
+ updateHarnessSessionOnStop(key, slot, pid, service3, preserveForResume) {
89948
+ const harness = slot.harness;
89949
+ if (harness && slot.harnessSessionId) {
89950
+ if (preserveForResume) {
89951
+ this.recordHarnessSessionOnStop(key, slot, pid, service3);
89952
+ } else {
88874
89953
  this.clearLastHarnessSession(key);
88875
89954
  }
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);
89955
+ return;
89956
+ }
89957
+ if (!preserveForResume) {
89958
+ this.clearLastHarnessSession(key);
89959
+ }
89960
+ }
89961
+ preserveOrClearHarnessSessionOnStop(key, slot, pid, opts, service3) {
89962
+ const preserveForResume = this.shouldPreserveHarnessOnStop(slot, opts);
89963
+ this.updateHarnessSessionOnStop(key, slot, pid, service3, preserveForResume);
89964
+ return preserveForResume;
89965
+ }
89966
+ async killProcessWithFallback(pid) {
89967
+ try {
89968
+ this.deps.processes.kill(-pid, "SIGTERM");
89969
+ } catch {}
89970
+ let dead = false;
89971
+ for (let i2 = 0;i2 < 20; i2++) {
89972
+ await this.deps.clock.delay(500);
89973
+ if (!isProcessAlive(this.deps.processes.kill, pid)) {
89974
+ dead = true;
89975
+ break;
89976
+ }
89977
+ }
89978
+ if (!dead) {
89979
+ try {
89980
+ this.deps.processes.kill(-pid, "SIGKILL");
89981
+ } catch {}
89982
+ for (let i2 = 0;i2 < 10; i2++) {
89983
+ await this.deps.clock.delay(500);
89984
+ if (!isProcessAlive(this.deps.processes.kill, pid)) {
89985
+ break;
88905
89986
  }
88906
89987
  }
88907
- } catch {}
89988
+ }
89989
+ for (const svc of this.deps.agentServices.values()) {
89990
+ svc.untrack(pid);
89991
+ }
89992
+ }
89993
+ resetSlotAfterStop(slot) {
88908
89994
  slot.state = "idle";
88909
89995
  slot.pid = undefined;
88910
89996
  slot.startedAt = undefined;
88911
89997
  slot.pendingOperation = undefined;
89998
+ }
89999
+ recordStopExit(slot, pid, opts) {
88912
90000
  const exitArgs3 = {
88913
90001
  sessionId: this.deps.sessionId,
88914
90002
  machineId: this.deps.machineId,
@@ -88924,6 +90012,21 @@ class AgentProcessManager {
88924
90012
  console.log(` ⚠️ Failed to record agent exit event: ${err.message}`);
88925
90013
  this.queueExitRetry({ role: opts.role, args: exitArgs3 });
88926
90014
  });
90015
+ }
90016
+ async doStop(key, slot, pid, opts) {
90017
+ try {
90018
+ const harness = slot.harness;
90019
+ const service3 = harness ? this.deps.agentServices.get(harness) : undefined;
90020
+ const preserveForResume = this.preserveOrClearHarnessSessionOnStop(key, slot, pid, opts, service3);
90021
+ if (service3) {
90022
+ await service3.stop(pid, { preserveForResume });
90023
+ service3.untrack(pid);
90024
+ } else {
90025
+ await this.killProcessWithFallback(pid);
90026
+ }
90027
+ } catch {}
90028
+ this.resetSlotAfterStop(slot);
90029
+ this.recordStopExit(slot, pid, opts);
88927
90030
  try {
88928
90031
  await this.deps.persistence.clearAgentPid(this.deps.machineId, opts.chatroomId, opts.role);
88929
90032
  } catch {}
@@ -88934,12 +90037,15 @@ var AGENT_EXIT_RETRY_INTERVAL_MS = 1e4;
88934
90037
  var init_agent_process_manager = __esm(() => {
88935
90038
  init_generator();
88936
90039
  init_types();
90040
+ init_esm();
88937
90041
  init_turn_completed_backend();
88938
90042
  init_api3();
88939
90043
  init_orphan_tracker();
88940
90044
  init_agent_lifecycle();
88941
90045
  init_classify_resume_storm_reason();
88942
90046
  init_handle_turn_completed();
90047
+ init_agent_lifecycle_runtime();
90048
+ init_agent_lifecycle_types();
88943
90049
  });
88944
90050
 
88945
90051
  // src/infrastructure/services/harness-spawning/rate-limiter.ts
@@ -89505,6 +90611,7 @@ var init_models_refresh = __esm(() => {
89505
90611
  var startObservedSyncSubscriptionEffect = (wsClient2) => exports_Effect.gen(function* () {
89506
90612
  const session2 = yield* DaemonSessionService;
89507
90613
  const effectContext2 = yield* exports_Effect.context();
90614
+ const runtime4 = yield* exports_Effect.runtime();
89508
90615
  console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Starting observed-sync subscription (reactive)`);
89509
90616
  const observedWorkingDirs = new Map;
89510
90617
  const chatroomRefreshState = new Map;
@@ -89560,7 +90667,7 @@ var startObservedSyncSubscriptionEffect = (wsClient2) => exports_Effect.gen(func
89560
90667
  console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed-sync subscription stopped`);
89561
90668
  }
89562
90669
  };
89563
- function handleObservedChange(observed) {
90670
+ function collectWorkingDirChanges(observed) {
89564
90671
  const newWorkingDirs = new Set;
89565
90672
  const refreshedWorkingDirs = new Set;
89566
90673
  for (const chatroom of observed) {
@@ -89578,55 +90685,77 @@ var startObservedSyncSubscriptionEffect = (wsClient2) => exports_Effect.gen(func
89578
90685
  newWorkingDirs.add(wd);
89579
90686
  }
89580
90687
  }
90688
+ return { newWorkingDirs, refreshedWorkingDirs };
90689
+ }
90690
+ function pruneStaleChatroomRefreshState(observed) {
89581
90691
  for (const [chatroomId] of chatroomRefreshState) {
89582
90692
  const stillObserved = observed.some((c) => c.chatroomId === chatroomId);
89583
90693
  if (!stillObserved) {
89584
90694
  chatroomRefreshState.delete(chatroomId);
89585
90695
  }
89586
90696
  }
90697
+ }
90698
+ function removeUnobservedWorkingDirs(newWorkingDirs) {
89587
90699
  const currentWorkingDirs = new Set(observedWorkingDirs.keys());
89588
- let addedCount = 0;
89589
90700
  let removedCount = 0;
89590
90701
  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
- }
90702
+ if (newWorkingDirs.has(wd)) {
90703
+ continue;
89606
90704
  }
90705
+ const state = observedWorkingDirs.get(wd);
90706
+ if (!state) {
90707
+ continue;
90708
+ }
90709
+ clearInterval(state.intervalHandle);
90710
+ observedWorkingDirs.delete(wd);
90711
+ const skips = skippedPushCount.get(wd) ?? 0;
90712
+ if (skips > 0) {
90713
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Stopped observing ${wd} (skipped ${skips} overlapping pushes)`);
90714
+ } else {
90715
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Stopped observing ${wd}`);
90716
+ }
90717
+ skippedPushCount.delete(wd);
90718
+ pendingRefresh.delete(wd);
90719
+ removedCount++;
89607
90720
  }
90721
+ return removedCount;
90722
+ }
90723
+ function addNewlyObservedWorkingDirs(newWorkingDirs) {
90724
+ let addedCount = 0;
89608
90725
  for (const wd of newWorkingDirs) {
90726
+ if (observedWorkingDirs.has(wd)) {
90727
+ continue;
90728
+ }
90729
+ observedWorkingDirs.set(wd, {
90730
+ intervalHandle: setInterval(() => {
90731
+ schedulePushForWorkingDir(wd, "safety-poll");
90732
+ }, OBSERVED_SAFETY_POLL_MS),
90733
+ pushInFlight: false
90734
+ });
90735
+ console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Started observing ${wd}`);
90736
+ schedulePushForWorkingDir(wd, "safety-poll");
90737
+ addedCount++;
90738
+ }
90739
+ return addedCount;
90740
+ }
90741
+ function triggerRefreshedWorkingDirs(refreshedWorkingDirs) {
90742
+ for (const wd of refreshedWorkingDirs) {
89609
90743
  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++;
90744
+ continue;
89619
90745
  }
90746
+ console.log(`[${formatTimestamp()}] \uD83D\uDD04 Refresh triggered for ${wd}`);
90747
+ schedulePushForWorkingDir(wd, "refresh");
89620
90748
  }
90749
+ }
90750
+ function handleObservedChange(observed) {
90751
+ const { newWorkingDirs, refreshedWorkingDirs } = collectWorkingDirChanges(observed);
90752
+ pruneStaleChatroomRefreshState(observed);
90753
+ const removedCount = removeUnobservedWorkingDirs(newWorkingDirs);
90754
+ const addedCount = addNewlyObservedWorkingDirs(newWorkingDirs);
89621
90755
  if (addedCount > 0 || removedCount > 0) {
89622
90756
  console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observing ${observedWorkingDirs.size} working dir(s)`);
89623
90757
  }
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
- }
90758
+ triggerRefreshedWorkingDirs(refreshedWorkingDirs);
89630
90759
  }
89631
90760
  function schedulePushForWorkingDir(workingDir, reason = "safety-poll") {
89632
90761
  if (stopped)
@@ -89644,9 +90773,13 @@ var startObservedSyncSubscriptionEffect = (wsClient2) => exports_Effect.gen(func
89644
90773
  return;
89645
90774
  }
89646
90775
  state.pushInFlight = true;
89647
- pushForWorkingDir(workingDir, reason).catch((err) => {
89648
- console.warn(`[${formatTimestamp()}] ⚠️ Push failed for ${workingDir}: ${getErrorMessage(err)}`);
89649
- }).finally(() => {
90776
+ pushForWorkingDir(workingDir, reason);
90777
+ }
90778
+ function pushForWorkingDir(workingDir, reason = "safety-poll") {
90779
+ exports_Runtime.runFork(runtime4)(exports_Effect.all([
90780
+ pushSingleWorkspaceGitSummaryForObservedEffect(workingDir, reason),
90781
+ pushSingleWorkspaceCommandsEffect(workingDir)
90782
+ ], { 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
90783
  const s = observedWorkingDirs.get(workingDir);
89651
90784
  if (s) {
89652
90785
  s.pushInFlight = false;
@@ -89655,15 +90788,7 @@ var startObservedSyncSubscriptionEffect = (wsClient2) => exports_Effect.gen(func
89655
90788
  schedulePushForWorkingDir(workingDir, "refresh");
89656
90789
  }
89657
90790
  }
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
- });
90791
+ }))));
89667
90792
  }
89668
90793
  });
89669
90794
  var init_observed_sync = __esm(() => {
@@ -89736,36 +90861,21 @@ var init_workspace_list_subscription = __esm(() => {
89736
90861
  });
89737
90862
 
89738
90863
  // 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) {
90864
+ function evictStaleEntries(entries2, evictBefore) {
90865
+ for (const [id3, ts] of entries2) {
89742
90866
  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) {
89754
- 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);
90867
+ entries2.delete(id3);
89768
90868
  }
90869
+ }
90870
+ function evictStaleDedupEntries(tracker) {
90871
+ const evictBefore = Date.now() - AGENT_REQUEST_DEADLINE_MS;
90872
+ evictStaleEntries(tracker.commandIds, evictBefore);
90873
+ evictStaleEntries(tracker.pingIds, evictBefore);
90874
+ evictStaleEntries(tracker.gitRefreshIds, evictBefore);
90875
+ evictStaleEntries(tracker.capabilitiesRefreshIds, evictBefore);
90876
+ evictStaleEntries(tracker.localActionIds, evictBefore);
90877
+ evictStaleEntries(tracker.commandRunIds, evictBefore);
90878
+ evictStaleEntries(tracker.commandStopIds, evictBefore);
89769
90879
  processManager.evictStalePendingStops();
89770
90880
  }
89771
90881
  function handleRequestStartEffect(event, tracker) {
@@ -89936,6 +91046,7 @@ var init_command_loop = __esm(() => {
89936
91046
  startCommandLoopEffect = exports_Effect.gen(function* () {
89937
91047
  const session2 = yield* DaemonSessionService;
89938
91048
  const effectContext2 = yield* exports_Effect.context();
91049
+ const runtime4 = yield* exports_Effect.runtime();
89939
91050
  const observedSyncEnabled = featureFlags.observedSyncEnabled ?? false;
89940
91051
  let heartbeatCount = 0;
89941
91052
  const heartbeatTimer = setInterval(() => {
@@ -89946,15 +91057,9 @@ var init_command_loop = __esm(() => {
89946
91057
  heartbeatCount++;
89947
91058
  console.log(`[${formatTimestamp()}] \uD83D\uDC93 Daemon heartbeat #${heartbeatCount} OK`);
89948
91059
  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
- });
91060
+ exports_Runtime.runFork(runtime4)(exports_Effect.all([pushGitStateEffect, pushCommandsEffect, syncCommitDetailsEffect()], {
91061
+ concurrency: "unbounded"
91062
+ }).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[${formatTimestamp()}] ⚠️ Heartbeat sync failed: ${getErrorMessage(err)}`)))));
89958
91063
  }
89959
91064
  }).catch((err) => {
89960
91065
  console.warn(`[${formatTimestamp()}] ⚠️ Daemon heartbeat failed: ${getErrorMessage(err)}`);
@@ -89976,9 +91081,9 @@ var init_command_loop = __esm(() => {
89976
91081
  if (observedSyncEnabled) {
89977
91082
  console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed-sync enabled, skipping immediate push`);
89978
91083
  } 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(() => {});
91084
+ exports_Runtime.runFork(runtime4)(exports_Effect.all([pushGitStateEffect, pushCommandsEffect, syncCommitDetailsEffect()], {
91085
+ concurrency: "unbounded"
91086
+ }).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[${formatTimestamp()}] ⚠️ Startup sync failed: ${getErrorMessage(err)}`)))));
89982
91087
  }
89983
91088
  const PROCESS_KILL_TIMEOUT_MS = 6000;
89984
91089
  const CLOSE_TIMEOUT_MS = 3000;
@@ -90011,43 +91116,46 @@ var init_command_loop = __esm(() => {
90011
91116
  return;
90012
91117
  isShuttingDown = true;
90013
91118
  console.log(`
90014
- [${formatTimestamp()}] Shutting down... (press Ctrl+C again to force)`);
91119
+ [${formatTimestamp()}] Shutting down... (press Ctrl+B again to force)`);
91120
+ const watchdog = setupShutdownWatchdog();
91121
+ clearInterval(heartbeatTimer);
91122
+ stopSubscriptions();
91123
+ await runDaemonShutdownEffect();
91124
+ await closeAllSessionsAndHarnesses();
91125
+ clearTimeout(watchdog);
91126
+ releaseLock();
91127
+ process.exit(0);
91128
+ };
91129
+ const setupShutdownWatchdog = () => {
90015
91130
  const watchdog = setTimeout(() => {
90016
91131
  console.error(`[${formatTimestamp()}] Shutdown timed out — forcing exit.`);
90017
91132
  forceExit(1);
90018
91133
  }, SHUTDOWN_WATCHDOG_MS);
90019
91134
  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();
91135
+ return watchdog;
91136
+ };
91137
+ const stopSubscriptions = () => {
91138
+ gitSubscriptionHandle?.stop();
91139
+ fileContentSubscriptionHandle?.stop();
91140
+ fileTreeSubscriptionHandle?.stop();
91141
+ workspaceListSubscriptionHandle?.stop();
91142
+ observedSyncSubscriptionHandle?.stop();
91143
+ logObserverSubscriptionHandle?.stop();
91144
+ pendingPromptSubscriptionHandle?.stop();
91145
+ pendingHarnessSessionSubscriptionHandle?.stop();
91146
+ commandSubscriptionHandle?.stop();
91147
+ lifecycleManager?.stopMonitoring();
91148
+ };
91149
+ const runDaemonShutdownEffect = async () => {
90041
91150
  await withTimeout3(exports_Effect.runPromise(onDaemonShutdownEffect.pipe(exports_Effect.provide(effectContext2))), PROCESS_KILL_TIMEOUT_MS);
91151
+ };
91152
+ const closeAllSessionsAndHarnesses = async () => {
90042
91153
  for (const handle of activeSessions.values()) {
90043
91154
  await withTimeout3(handle.close(), CLOSE_TIMEOUT_MS);
90044
91155
  }
90045
91156
  for (const harness of harnesses.values()) {
90046
91157
  await withTimeout3(harness.close(), CLOSE_TIMEOUT_MS);
90047
91158
  }
90048
- clearTimeout(watchdog);
90049
- releaseLock();
90050
- process.exit(0);
90051
91159
  };
90052
91160
  const handleSignal = (signal) => {
90053
91161
  signalCount += 1;
@@ -91316,4 +92424,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
91316
92424
  });
91317
92425
  program2.parse();
91318
92426
 
91319
- //# debugId=C790B7A198F818CB64756E2164756E21
92427
+ //# debugId=77F10DD437D49D8264756E2164756E21