chatroom-cli 1.57.0 → 1.57.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -29173,7 +29173,7 @@ function writeSpawnError(logPrefix, err, emitLogLine) {
29173
29173
  emitLogLine?.(line);
29174
29174
  console.error(`[${new Date().toISOString()}] ${logPrefix} spawn-error]`, err);
29175
29175
  }
29176
- var NO_SUBAGENT_DIRECTIVE2 = "NEVER spawn subagents. Follow the chatroom instructions strictly.", _sdkCache, _sdkLoadError, CURSOR_SDK_COMMAND = "cursor-sdk", DEFAULT_MODEL = "composer-2.5", AGENT_CREATE_TIMEOUT_MS = 60000, SEND_TIMEOUT_MS = 60000, RUN_WAIT_TIMEOUT_MS = 3600000, MODELS_LIST_TIMEOUT_MS = 1e4, RUN_CANCEL_TIMEOUT_MS = 5000, cachedSdkPackageVersion, CursorSdkAgentService;
29176
+ var NO_SUBAGENT_DIRECTIVE2 = "NEVER spawn subagents. Follow the chatroom instructions strictly.", _sdkCache, _sdkLoadError, CURSOR_SDK_COMMAND = "cursor-sdk", DEFAULT_MODEL = "composer-2.5", AGENT_CREATE_TIMEOUT_MS = 60000, SEND_TIMEOUT_MS = 60000, RUN_WAIT_TIMEOUT_MS = 3600000, MODELS_LIST_TIMEOUT_MS = 60000, RUN_CANCEL_TIMEOUT_MS = 5000, cachedSdkPackageVersion, CursorSdkAgentService;
29177
29177
  var init_cursor_sdk_agent_service = __esm(() => {
29178
29178
  init_esm();
29179
29179
  init_base_cli_agent_service();
@@ -80624,19 +80624,51 @@ function isDaemonRunning() {
80624
80624
  removePid();
80625
80625
  return { running: false, pid: null };
80626
80626
  }
80627
- function acquireLock() {
80628
- const { running: running3, pid } = isDaemonRunning();
80627
+ function tryAcquireLock() {
80628
+ const { running: running3 } = isDaemonRunning();
80629
80629
  if (running3) {
80630
- console.error(`❌ Daemon already running for ${getConvexUrl()} (PID: ${pid})`);
80631
80630
  return false;
80632
80631
  }
80633
80632
  writePid();
80634
80633
  return true;
80635
80634
  }
80635
+ function logWaitingForShutdown(pid) {
80636
+ console.error(`⏳ Waiting for previous daemon to shut down for ${getConvexUrl()} (PID: ${pid})...`);
80637
+ }
80638
+ function logDaemonAlreadyRunning(pid) {
80639
+ console.error(`❌ Daemon already running for ${getConvexUrl()} (PID: ${pid})`);
80640
+ }
80641
+ async function waitForLockOrTimeout(deadline, intervalMs, sleep5) {
80642
+ let loggedWait = false;
80643
+ while (Date.now() < deadline) {
80644
+ if (tryAcquireLock()) {
80645
+ return true;
80646
+ }
80647
+ const { pid } = isDaemonRunning();
80648
+ if (pid !== null && !loggedWait) {
80649
+ logWaitingForShutdown(pid);
80650
+ loggedWait = true;
80651
+ }
80652
+ await sleep5(intervalMs);
80653
+ }
80654
+ return false;
80655
+ }
80656
+ async function acquireLockWithRetry(options) {
80657
+ const intervalMs = options?.intervalMs ?? LOCK_RETRY_INTERVAL_MS;
80658
+ const maxWaitMs = options?.maxWaitMs ?? LOCK_RETRY_MAX_WAIT_MS;
80659
+ const sleep5 = options?.sleep ?? ((ms) => new Promise((resolve3) => setTimeout(resolve3, ms)));
80660
+ const deadline = Date.now() + maxWaitMs;
80661
+ if (await waitForLockOrTimeout(deadline, intervalMs, sleep5)) {
80662
+ return true;
80663
+ }
80664
+ const { pid } = isDaemonRunning();
80665
+ logDaemonAlreadyRunning(pid);
80666
+ return false;
80667
+ }
80636
80668
  function releaseLock() {
80637
80669
  removePid();
80638
80670
  }
80639
- var CHATROOM_DIR4;
80671
+ var CHATROOM_DIR4, LOCK_RETRY_INTERVAL_MS = 500, LOCK_RETRY_MAX_WAIT_MS = 15000;
80640
80672
  var init_pid = __esm(() => {
80641
80673
  init_client2();
80642
80674
  CHATROOM_DIR4 = join16(homedir6(), ".chatroom");
@@ -84918,6 +84950,315 @@ class TurnEndQueue {
84918
84950
  }
84919
84951
  }
84920
84952
 
84953
+ // src/commands/machine/daemon-start/native-delivery-ledger.ts
84954
+ class NativeDeliveryLedger {
84955
+ delivered = new Set;
84956
+ inFlight = new Set;
84957
+ deliveryKey(taskId, harnessSessionId) {
84958
+ return `${taskId}\x00${harnessSessionId}`;
84959
+ }
84960
+ isDelivered(taskId, harnessSessionId) {
84961
+ return this.delivered.has(this.deliveryKey(taskId, harnessSessionId));
84962
+ }
84963
+ tryAcquire(taskId, harnessSessionId) {
84964
+ const key = this.deliveryKey(taskId, harnessSessionId);
84965
+ if (this.delivered.has(key) || this.inFlight.has(key)) {
84966
+ return false;
84967
+ }
84968
+ this.inFlight.add(key);
84969
+ return true;
84970
+ }
84971
+ markDelivered(taskId, harnessSessionId) {
84972
+ const key = this.deliveryKey(taskId, harnessSessionId);
84973
+ this.inFlight.delete(key);
84974
+ this.delivered.add(key);
84975
+ }
84976
+ clearDelivery(taskId, harnessSessionId) {
84977
+ const key = this.deliveryKey(taskId, harnessSessionId);
84978
+ this.inFlight.delete(key);
84979
+ this.delivered.delete(key);
84980
+ }
84981
+ clearSession(harnessSessionId) {
84982
+ const suffix = `\x00${harnessSessionId}`;
84983
+ for (const key of [...this.delivered, ...this.inFlight]) {
84984
+ if (key.endsWith(suffix)) {
84985
+ this.delivered.delete(key);
84986
+ this.inFlight.delete(key);
84987
+ }
84988
+ }
84989
+ }
84990
+ }
84991
+
84992
+ // src/domain/native-integration/predicates.ts
84993
+ function isNativeInjectableAliveRunning(task) {
84994
+ const { agentConfig, status: status3 } = task;
84995
+ if (!isNativeHarness(agentConfig.agentHarness))
84996
+ return false;
84997
+ if (agentConfig.spawnedAgentPid == null || agentConfig.desiredState !== "running") {
84998
+ return false;
84999
+ }
85000
+ if (status3 === "pending")
85001
+ return true;
85002
+ if (status3 === "acknowledged") {
85003
+ const assignedTo = task.assignedTo?.toLowerCase();
85004
+ return assignedTo === agentConfig.role.toLowerCase();
85005
+ }
85006
+ return false;
85007
+ }
85008
+ function isInjectableNativeAction(action) {
85009
+ if (action == null)
85010
+ return true;
85011
+ return action === NATIVE_WAITING_ACTION;
85012
+ }
85013
+ function isNativeIdleAfterTaskComplete(participant) {
85014
+ return participant.lastSeenAction === NATIVE_TASK_INJECTED_ACTION && participant.lastStatus === "task.completed";
85015
+ }
85016
+ function isNativeAcknowledgedInjectionRetry(task) {
85017
+ if (task.status !== "acknowledged")
85018
+ return false;
85019
+ const assignedTo = task.assignedTo?.toLowerCase();
85020
+ if (assignedTo !== task.agentConfig.role.toLowerCase())
85021
+ return false;
85022
+ return task.participant?.lastSeenAction === NATIVE_TASK_INJECTED_ACTION;
85023
+ }
85024
+ function isStaleCliGetNextTaskWaiting(task) {
85025
+ const lastSeenAt = task.participant?.lastSeenAt ?? 0;
85026
+ return task.participant?.lastSeenAction === "get-next-task:started" && task.createdAt > lastSeenAt;
85027
+ }
85028
+ function isCliIdleNotListening(task, now, thresholdMs) {
85029
+ const lastSeenAt = task.participant?.lastSeenAt ?? 0;
85030
+ if (lastSeenAt === 0)
85031
+ return now - task.createdAt > thresholdMs;
85032
+ return now - lastSeenAt > thresholdMs;
85033
+ }
85034
+ var init_predicates = __esm(() => {
85035
+ init_types();
85036
+ init_participant();
85037
+ });
85038
+
85039
+ // src/infrastructure/services/remote-agents/spawn-prompt.ts
85040
+ function createSpawnPrompt(raw, opts) {
85041
+ if (opts?.nativeBootstrap) {
85042
+ return NATIVE_BOOTSTRAP_PROMPT;
85043
+ }
85044
+ const trimmed = raw?.trim();
85045
+ return trimmed && trimmed.length > 0 ? trimmed : DEFAULT_TRIGGER_PROMPT;
85046
+ }
85047
+ var DEFAULT_TRIGGER_PROMPT = "Please read your system prompt carefully and follow the Getting Started instructions.", NATIVE_BOOTSTRAP_PROMPT = "Focus on your role responsibilities. Complete each task via handoff.";
85048
+
85049
+ // src/domain/native-integration/spawn-policy.ts
85050
+ function shouldDeferInitialTurn(harness) {
85051
+ return isNativeHarness(harness);
85052
+ }
85053
+ function resolveNativeSpawnPolicy(harness, initMessage) {
85054
+ const deferInitialTurn = shouldDeferInitialTurn(harness);
85055
+ return {
85056
+ deferInitialTurn,
85057
+ prompt: createSpawnPrompt(initMessage, { nativeBootstrap: deferInitialTurn })
85058
+ };
85059
+ }
85060
+ var init_spawn_policy = __esm(() => {
85061
+ init_types();
85062
+ });
85063
+
85064
+ // src/domain/native-integration/index.ts
85065
+ var init_native_integration = __esm(() => {
85066
+ init_types();
85067
+ init_predicates();
85068
+ init_spawn_policy();
85069
+ });
85070
+
85071
+ // src/commands/machine/daemon-start/native-task-injector-logic.ts
85072
+ function shouldDeliverNativeTask(task, opts) {
85073
+ if (!isNativeInjectableAliveRunning(task))
85074
+ return false;
85075
+ if (!opts.harnessSessionId)
85076
+ return false;
85077
+ if (opts.ledger.isDelivered(task.taskId, opts.harnessSessionId))
85078
+ return false;
85079
+ return isInjectableNativeAction(task.participant?.lastSeenAction) || isNativeIdleAfterTaskComplete(task.participant ?? {}) || isNativeAcknowledgedInjectionRetry(task);
85080
+ }
85081
+ function buildNativeInjectionPrompt(params) {
85082
+ const { taskDeliveryOutput, compressMode } = params;
85083
+ if (compressMode === "new_session") {
85084
+ return [
85085
+ "⚠️ Context was compacted. Run `chatroom get-system-prompt` only if role instructions are missing.",
85086
+ "",
85087
+ taskDeliveryOutput
85088
+ ].join(`
85089
+ `);
85090
+ }
85091
+ return taskDeliveryOutput;
85092
+ }
85093
+ var init_native_task_injector_logic = __esm(() => {
85094
+ init_predicates();
85095
+ init_native_integration();
85096
+ });
85097
+
85098
+ // ../../services/backend/src/domain/handoff/parse-compress-context.ts
85099
+ function findSectionIndex(content, headings) {
85100
+ const indices = headings.map((heading) => content.indexOf(heading)).filter((idx) => idx !== -1);
85101
+ return indices.length === 0 ? -1 : Math.min(...indices);
85102
+ }
85103
+ function normalizeMode(raw) {
85104
+ const value = raw.toLowerCase();
85105
+ if (value === "reset")
85106
+ return "new_session";
85107
+ if (value === "none")
85108
+ return value;
85109
+ return DEFAULT_MODE;
85110
+ }
85111
+ function extractSectionBody(content, sectionIdx) {
85112
+ const matchedHeading = SECTION_HEADINGS.find((h) => content.indexOf(h, sectionIdx) === sectionIdx) ?? SECTION_HEADINGS[0];
85113
+ const afterSection = content.slice(sectionIdx);
85114
+ const nextHeading = afterSection.slice(matchedHeading.length).search(/\n## /);
85115
+ return nextHeading === -1 ? afterSection : afterSection.slice(0, matchedHeading.length + nextHeading);
85116
+ }
85117
+ function parseCompressContext(handoffContent) {
85118
+ const sectionIdx = findSectionIndex(handoffContent, SECTION_HEADINGS);
85119
+ if (sectionIdx === -1)
85120
+ return DEFAULT_MODE;
85121
+ const match17 = extractSectionBody(handoffContent, sectionIdx).match(DATA_TAG);
85122
+ if (!match17)
85123
+ return DEFAULT_MODE;
85124
+ return normalizeMode(match17[1]);
85125
+ }
85126
+ function compressContextToWantResume(mode) {
85127
+ return mode === "none";
85128
+ }
85129
+ var SECTION_HEADINGS, DATA_TAG, DEFAULT_MODE = "new_session";
85130
+ var init_parse_compress_context = __esm(() => {
85131
+ SECTION_HEADINGS = ["## Session Management", "## Restart new context"];
85132
+ DATA_TAG = /\/\/\s*data:agent\.compress_context=(new_session|reset|none)\b/i;
85133
+ });
85134
+
85135
+ // src/commands/machine/daemon-start/native-task-injector.ts
85136
+ function runNativeInjectionEffect(task, harnessSessionId, deps, ledger) {
85137
+ return exports_Effect.gen(function* () {
85138
+ if (!shouldDeliverNativeTask(task, { ledger, harnessSessionId })) {
85139
+ return;
85140
+ }
85141
+ const { chatroomId, taskId, taskContent, agentConfig, status: status3 } = task;
85142
+ const { role } = agentConfig;
85143
+ if (!ledger.tryAcquire(taskId, harnessSessionId)) {
85144
+ return;
85145
+ }
85146
+ if (status3 === "pending") {
85147
+ const claimResult = yield* exports_Effect.tryPromise({
85148
+ try: () => deps.backend.mutation(api.tasks.claimTask, {
85149
+ sessionId: deps.sessionId,
85150
+ chatroomId,
85151
+ role,
85152
+ taskId
85153
+ }),
85154
+ catch: (err) => err
85155
+ }).pipe(exports_Effect.either);
85156
+ if (claimResult._tag === "Left") {
85157
+ ledger.clearDelivery(taskId, harnessSessionId);
85158
+ return yield* exports_Effect.fail(claimResult.left);
85159
+ }
85160
+ }
85161
+ const deliveryResult = yield* exports_Effect.tryPromise({
85162
+ try: () => deps.backend.query(api.messages.getTaskDeliveryPrompt, {
85163
+ sessionId: deps.sessionId,
85164
+ chatroomId,
85165
+ role,
85166
+ taskId,
85167
+ convexUrl: deps.convexUrl
85168
+ }),
85169
+ catch: (err) => err
85170
+ }).pipe(exports_Effect.either);
85171
+ if (deliveryResult._tag === "Left") {
85172
+ ledger.clearDelivery(taskId, harnessSessionId);
85173
+ return yield* exports_Effect.fail(deliveryResult.left);
85174
+ }
85175
+ const delivery = deliveryResult.right;
85176
+ const prompt = buildNativeInjectionPrompt({
85177
+ taskDeliveryOutput: delivery.fullCliOutput,
85178
+ compressMode: parseCompressContext(taskContent)
85179
+ });
85180
+ yield* exports_Effect.tryPromise({
85181
+ try: () => deps.backend.mutation(api.participants.join, {
85182
+ sessionId: deps.sessionId,
85183
+ chatroomId,
85184
+ role,
85185
+ action: NATIVE_TASK_INJECTED_ACTION,
85186
+ taskId
85187
+ }),
85188
+ catch: (err) => err
85189
+ }).pipe(exports_Effect.tapError(() => exports_Effect.sync(() => ledger.clearDelivery(taskId, harnessSessionId))));
85190
+ const resumeResult = yield* exports_Effect.tryPromise({
85191
+ try: () => deps.agentMgr.resumeTurnForSlot({ chatroomId, role, prompt }),
85192
+ catch: (err) => err
85193
+ }).pipe(exports_Effect.either);
85194
+ if (resumeResult._tag === "Left") {
85195
+ ledger.clearDelivery(taskId, harnessSessionId);
85196
+ console.warn(`[NativeTaskInjector] resumeTurn failed for ${role}@${chatroomId}: ${getErrorMessage(resumeResult.left)}`);
85197
+ return;
85198
+ }
85199
+ ledger.markDelivered(taskId, harnessSessionId);
85200
+ });
85201
+ }
85202
+ var init_native_task_injector = __esm(() => {
85203
+ init_participant();
85204
+ init_parse_compress_context();
85205
+ init_esm();
85206
+ init_native_task_injector_logic();
85207
+ init_api3();
85208
+ init_convex_error();
85209
+ });
85210
+
85211
+ // src/commands/machine/daemon-start/native-task-delivery-coordinator.ts
85212
+ class NativeTaskDeliveryCoordinator {
85213
+ ledger;
85214
+ constructor(ledger = new NativeDeliveryLedger) {
85215
+ this.ledger = ledger;
85216
+ }
85217
+ onSessionLost(params) {
85218
+ if (params.harnessSessionId) {
85219
+ this.ledger.clearSession(params.harnessSessionId);
85220
+ }
85221
+ }
85222
+ reconcileAssignedTasks(params) {
85223
+ const { tasks, runtime: runtime4, effectContext: effectContext2, agentMgr, sessionDeps } = params;
85224
+ for (const task of tasks) {
85225
+ const slot = agentMgr.getSlot(task.chatroomId, task.agentConfig.role);
85226
+ const harnessSessionId = slot?.harnessSessionId;
85227
+ if (!shouldDeliverNativeTask(task, {
85228
+ ledger: this.ledger,
85229
+ harnessSessionId
85230
+ })) {
85231
+ continue;
85232
+ }
85233
+ if (!harnessSessionId) {
85234
+ continue;
85235
+ }
85236
+ exports_Runtime.runFork(runtime4)(runNativeInjectionEffect(task, harnessSessionId, {
85237
+ sessionId: sessionDeps.sessionId,
85238
+ backend: sessionDeps.backend,
85239
+ agentMgr: {
85240
+ resumeTurnForSlot: (args2) => exports_Effect.runPromise(agentMgr.resumeTurnForSlot(args2))
85241
+ },
85242
+ convexUrl: sessionDeps.convexUrl
85243
+ }, this.ledger).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[NativeTaskDelivery] delivery failed for ${task.agentConfig.role}@${task.chatroomId}: ${getErrorMessage(err)}`)))));
85244
+ }
85245
+ }
85246
+ }
85247
+ function getNativeTaskDeliveryCoordinator() {
85248
+ coordinator ??= new NativeTaskDeliveryCoordinator;
85249
+ return coordinator;
85250
+ }
85251
+ function notifyNativeSessionLost(params) {
85252
+ getNativeTaskDeliveryCoordinator().onSessionLost(params);
85253
+ }
85254
+ var coordinator;
85255
+ var init_native_task_delivery_coordinator = __esm(() => {
85256
+ init_esm();
85257
+ init_native_task_injector_logic();
85258
+ init_native_task_injector();
85259
+ init_convex_error();
85260
+ });
85261
+
84921
85262
  // src/domain/agent-lifecycle/entities/stop-reason.ts
84922
85263
  function resolveStopReason(code2, signal) {
84923
85264
  if (signal !== null)
@@ -85089,6 +85430,45 @@ var init_agent_lifecycle = __esm(() => {
85089
85430
  init_restart_decision();
85090
85431
  });
85091
85432
 
85433
+ // src/domain/agent-lifecycle/policies/cursor-sdk-run-error.ts
85434
+ function isCursorSdkRunErrorInLogs(logLines) {
85435
+ return logLines.some((line) => line.includes(" run-error]"));
85436
+ }
85437
+ function formatCursorSdkRunErrorMessage(logLines) {
85438
+ const line = [...logLines].reverse().find((l) => l.includes(" run-error]"));
85439
+ return line?.trim() ?? "Cursor SDK run failed";
85440
+ }
85441
+
85442
+ // src/commands/machine/daemon-start/native-harness-session-exit.ts
85443
+ function isNativeHarnessSessionDiscardedOnExit(ctx) {
85444
+ const { harness, harnessSessionId, stopReason, recentLogLines, supportsDaemonMemoryResume } = ctx;
85445
+ if (!harness || !harnessSessionId) {
85446
+ return false;
85447
+ }
85448
+ if (isCursorSdkRunErrorInLogs(recentLogLines ?? [])) {
85449
+ return true;
85450
+ }
85451
+ if (!supportsDaemonMemoryResume) {
85452
+ return true;
85453
+ }
85454
+ return !shouldRetainHarnessSessionForReconnect(stopReason);
85455
+ }
85456
+ function notifyNativeHarnessSessionLostOnExit(ctx) {
85457
+ if (!ctx.harness || !ctx.harnessSessionId || !getHarnessCapabilities(ctx.harness).supportsNativeIntegration || !isNativeHarnessSessionDiscardedOnExit(ctx)) {
85458
+ return;
85459
+ }
85460
+ notifyNativeSessionLost({
85461
+ chatroomId: ctx.chatroomId,
85462
+ role: ctx.role,
85463
+ harnessSessionId: ctx.harnessSessionId
85464
+ });
85465
+ }
85466
+ var init_native_harness_session_exit = __esm(() => {
85467
+ init_types();
85468
+ init_native_task_delivery_coordinator();
85469
+ init_agent_lifecycle();
85470
+ });
85471
+
85092
85472
  // src/domain/agent-lifecycle/policies/classify-resume-storm-reason.ts
85093
85473
  function classifyResumeStormReason(logLines) {
85094
85474
  const blob = logLines.join(`
@@ -85209,15 +85589,6 @@ function appendRecentLogLine(slot, line) {
85209
85589
  }
85210
85590
  var RECENT_LOG_LINE_CAP2 = 100;
85211
85591
 
85212
- // src/domain/agent-lifecycle/policies/cursor-sdk-run-error.ts
85213
- function isCursorSdkRunErrorInLogs(logLines) {
85214
- return logLines.some((line) => line.includes(" run-error]"));
85215
- }
85216
- function formatCursorSdkRunErrorMessage(logLines) {
85217
- const line = [...logLines].reverse().find((l) => l.includes(" run-error]"));
85218
- return line?.trim() ?? "Cursor SDK run failed";
85219
- }
85220
-
85221
85592
  // src/domain/agent-lifecycle/use-cases/handle-turn-completed.ts
85222
85593
  async function handleTurnCompleted(deps, input, slot) {
85223
85594
  if (slot?.resumeInFlight) {
@@ -85278,31 +85649,6 @@ var init_handle_turn_completed = __esm(() => {
85278
85649
  init_terminal_provider_error();
85279
85650
  });
85280
85651
 
85281
- // src/infrastructure/services/remote-agents/spawn-prompt.ts
85282
- function createSpawnPrompt(raw, opts) {
85283
- if (opts?.nativeBootstrap) {
85284
- return NATIVE_BOOTSTRAP_PROMPT;
85285
- }
85286
- const trimmed = raw?.trim();
85287
- return trimmed && trimmed.length > 0 ? trimmed : DEFAULT_TRIGGER_PROMPT;
85288
- }
85289
- var DEFAULT_TRIGGER_PROMPT = "Please read your system prompt carefully and follow the Getting Started instructions.", NATIVE_BOOTSTRAP_PROMPT = "Focus on your role responsibilities. Complete each task via handoff.";
85290
-
85291
- // src/domain/native-integration/spawn-policy.ts
85292
- function shouldDeferInitialTurn(harness) {
85293
- return isNativeHarness(harness);
85294
- }
85295
- function resolveNativeSpawnPolicy(harness, initMessage) {
85296
- const deferInitialTurn = shouldDeferInitialTurn(harness);
85297
- return {
85298
- deferInitialTurn,
85299
- prompt: createSpawnPrompt(initMessage, { nativeBootstrap: deferInitialTurn })
85300
- };
85301
- }
85302
- var init_spawn_policy = __esm(() => {
85303
- init_types();
85304
- });
85305
-
85306
85652
  // src/infrastructure/deps/process.ts
85307
85653
  function isProcessAlive(kill, pid) {
85308
85654
  try {
@@ -85921,6 +86267,15 @@ class AgentProcessManager {
85921
86267
  }
85922
86268
  const stopReason = resolveStopReason(opts.code, opts.signal);
85923
86269
  const ctx = this.captureExitContext(slot, opts, stopReason);
86270
+ notifyNativeHarnessSessionLostOnExit({
86271
+ chatroomId: opts.chatroomId,
86272
+ role: opts.role,
86273
+ harness: ctx.harness,
86274
+ harnessSessionId: ctx.harnessSessionId,
86275
+ stopReason: ctx.stopReason,
86276
+ recentLogLines: ctx.recentLogLines,
86277
+ supportsDaemonMemoryResume: Boolean(ctx.harness && this.deps.agentServices.get(ctx.harness)?.resumeFromDaemonMemory)
86278
+ });
85924
86279
  await this.preserveHarnessSessionOnExit(key, slot, ctx);
85925
86280
  const lifecyclePromise = this.lifecycle.runPromise(exports_Effect.gen(function* () {
85926
86281
  const svc = yield* AgentLifecycleService;
@@ -86021,11 +86376,11 @@ class AgentProcessManager {
86021
86376
  }
86022
86377
  this.maybeRestartAgent(opts, ctx);
86023
86378
  }
86024
- clearHarnessSessionAfterRunError(key, role, recentLogLines) {
86379
+ clearHarnessSessionAfterRunError(key, recentLogLines) {
86025
86380
  if (!isCursorSdkRunErrorInLogs(recentLogLines)) {
86026
86381
  return false;
86027
86382
  }
86028
- console.log(`[AgentProcessManager] cursor-sdk run-error — cold restarting ${role}: ${formatCursorSdkRunErrorMessage(recentLogLines)}`);
86383
+ console.log(`[AgentProcessManager] cursor-sdk run-error — cold restarting ${key}: ${formatCursorSdkRunErrorMessage(recentLogLines)}`);
86029
86384
  this.clearLastHarnessSession(key);
86030
86385
  return true;
86031
86386
  }
@@ -86041,7 +86396,7 @@ class AgentProcessManager {
86041
86396
  this.handlePermanentFailureForRestart(opts, recentLogLines, ctx.terminalProviderFailureHandled);
86042
86397
  return;
86043
86398
  }
86044
- const coldRestartAfterRunError = this.clearHarnessSessionAfterRunError(key, opts.role, logs);
86399
+ const coldRestartAfterRunError = this.clearHarnessSessionAfterRunError(key, logs);
86045
86400
  this.ensureRunning({
86046
86401
  chatroomId: opts.chatroomId,
86047
86402
  role: opts.role,
@@ -86050,6 +86405,10 @@ class AgentProcessManager {
86050
86405
  workingDir,
86051
86406
  reason: "platform.crash_recovery",
86052
86407
  wantResume: coldRestartAfterRunError ? false : ctx.wantResume ?? true
86408
+ }).then((result) => {
86409
+ if (!result.success) {
86410
+ console.log(`[AgentProcessManager] ⚠️ Agent restart did not complete for ${opts.role}: ${result.error ?? "unknown"}`);
86411
+ }
86053
86412
  }).catch((err) => {
86054
86413
  console.log(` ⚠️ Failed to restart agent: ${err.message}`);
86055
86414
  this.emitStartFailedEvent(opts.role, opts.chatroomId, err.message);
@@ -86739,6 +87098,7 @@ var init_agent_process_manager = __esm(() => {
86739
87098
  init_turn_completed_backend();
86740
87099
  init_api3();
86741
87100
  init_orphan_tracker();
87101
+ init_native_harness_session_exit();
86742
87102
  init_agent_lifecycle();
86743
87103
  init_abort_resume_storm();
86744
87104
  init_classify_resume_storm_reason();
@@ -86832,7 +87192,7 @@ var init_harness_spawning = __esm(() => {
86832
87192
  });
86833
87193
 
86834
87194
  // src/commands/machine/daemon-start/handlers/daemon-startup-log.ts
86835
- var logStartupEffect = (availableModels) => exports_Effect.gen(function* () {
87195
+ var logStartupEffect = (cachedModels) => exports_Effect.gen(function* () {
86836
87196
  const session2 = yield* DaemonSessionService;
86837
87197
  yield* exports_Effect.sync(() => {
86838
87198
  console.log(`[${formatTimestamp()}] \uD83D\uDE80 Daemon started`);
@@ -86840,7 +87200,7 @@ var logStartupEffect = (availableModels) => exports_Effect.gen(function* () {
86840
87200
  console.log(` Machine ID: ${session2.machineId}`);
86841
87201
  console.log(` Hostname: ${session2.config?.hostname ?? "unknown"}`);
86842
87202
  console.log(` Available harnesses: ${session2.config?.availableHarnesses.join(", ") || "none"}`);
86843
- console.log(` Available models: ${Object.keys(availableModels).length > 0 ? `${Object.values(availableModels).flat().length} models across ${Object.keys(availableModels).join(", ")}` : "none discovered"}`);
87203
+ console.log(` Available models: ${Object.keys(cachedModels).length > 0 ? `${Object.values(cachedModels).flat().length} cached across ${Object.keys(cachedModels).join(", ")} (refreshing in background)` : "discovering in background"}`);
86844
87204
  console.log(` PID: ${process.pid}`);
86845
87205
  });
86846
87206
  });
@@ -86852,6 +87212,23 @@ var init_daemon_startup_log = __esm(() => {
86852
87212
 
86853
87213
  // src/commands/machine/daemon-start/init.ts
86854
87214
  import { stat as stat3 } from "node:fs/promises";
87215
+ async function discoverModelsForHarness(harness, service3) {
87216
+ const installed2 = await service3.isInstalled();
87217
+ if (!installed2) {
87218
+ return { harness, models: [], installed: false };
87219
+ }
87220
+ try {
87221
+ const models = await service3.listModels();
87222
+ return { harness, models, installed: true };
87223
+ } catch (reason) {
87224
+ console.warn(JSON.stringify({
87225
+ event: "discover-models-error",
87226
+ harness,
87227
+ reason: getErrorMessage(reason)
87228
+ }));
87229
+ return { harness, models: [], installed: true };
87230
+ }
87231
+ }
86855
87232
  async function discoverModels(agentServices) {
86856
87233
  return exports_Effect.runPromise(discoverModelsEffect(agentServices));
86857
87234
  }
@@ -86886,6 +87263,51 @@ function createDefaultDeps18() {
86886
87263
  agentProcessManager: null
86887
87264
  };
86888
87265
  }
87266
+ function assembleDaemonSessionInit(args2) {
87267
+ const {
87268
+ client: client4,
87269
+ typedSessionId,
87270
+ machineId,
87271
+ config: config3,
87272
+ convexUrl,
87273
+ agentServices,
87274
+ cachedModels,
87275
+ deps
87276
+ } = args2;
87277
+ deps.backend.mutation = (endpoint, args3) => client4.mutation(endpoint, args3);
87278
+ deps.backend.query = (endpoint, args3) => client4.query(endpoint, args3);
87279
+ deps.agentProcessManager = new AgentProcessManager({
87280
+ agentServices,
87281
+ backend: deps.backend,
87282
+ sessionId: typedSessionId,
87283
+ machineId,
87284
+ processes: deps.processes,
87285
+ clock: deps.clock,
87286
+ fs: deps.fs,
87287
+ persistence: deps.machine,
87288
+ spawning: deps.spawning,
87289
+ crashLoop: new CrashLoopTracker,
87290
+ convexUrl
87291
+ });
87292
+ return {
87293
+ client: client4,
87294
+ sessionId: typedSessionId,
87295
+ machineId,
87296
+ config: config3,
87297
+ convexUrl,
87298
+ backend: deps.backend,
87299
+ fs: deps.fs,
87300
+ machine: deps.machine,
87301
+ spawning: deps.spawning,
87302
+ agentProcessManager: deps.agentProcessManager,
87303
+ events: new DaemonEventBus,
87304
+ agentServices,
87305
+ lastPushedGitState: new Map,
87306
+ lastPushedModels: Object.keys(cachedModels).length > 0 ? cachedModels : null,
87307
+ lastPushedHarnessFingerprint: harnessCapabilitiesFingerprint(config3.availableHarnesses, config3.harnessVersions),
87308
+ logger: console
87309
+ };
87310
+ }
86889
87311
 
86890
87312
  class NetworkRetryError {
86891
87313
  cause;
@@ -86900,21 +87322,11 @@ async function initDaemon() {
86900
87322
  return exports_Effect.runPromise(initDaemonEffect);
86901
87323
  }
86902
87324
  var discoverModelsEffect = (agentServices) => exports_Effect.gen(function* () {
86903
- const discoverOne = ([harness, service3]) => exports_Effect.promise(() => service3.isInstalled()).pipe(exports_Effect.flatMap((installed2) => {
86904
- if (!installed2) {
86905
- return exports_Effect.succeed(undefined);
87325
+ const discoverOne = ([harness, service3]) => exports_Effect.promise(() => discoverModelsForHarness(harness, service3)).pipe(exports_Effect.map((result) => {
87326
+ if (!result.installed) {
87327
+ return;
86906
87328
  }
86907
- return exports_Effect.tryPromise({
86908
- try: () => service3.listModels(),
86909
- catch: (reason) => reason
86910
- }).pipe(exports_Effect.map((models) => ({ harness, models })), exports_Effect.catchAll((reason) => {
86911
- console.warn(JSON.stringify({
86912
- event: "discover-models-error",
86913
- harness,
86914
- reason: getErrorMessage(reason)
86915
- }));
86916
- return exports_Effect.succeed({ harness, models: [] });
86917
- }));
87329
+ return { harness: result.harness, models: result.models };
86918
87330
  }));
86919
87331
  const results = yield* exports_Effect.forEach(Array.from(agentServices.entries()), discoverOne, {
86920
87332
  concurrency: "unbounded"
@@ -87011,21 +87423,28 @@ Run: chatroom auth login`);
87011
87423
  return yield* exports_Effect.die(new Error("Machine config missing after ensureMachineRegistered — this should not happen"));
87012
87424
  }
87013
87425
  return config3;
87014
- }), registerCapabilitiesEffect = (client4, sessionId, config3, agentServices) => exports_Effect.gen(function* () {
87426
+ }), registerMachineEffect = (client4, sessionId, config3) => exports_Effect.catchAll(exports_Effect.tryPromise(() => client4.mutation(api.machines.register, {
87427
+ sessionId,
87428
+ machineId: config3.machineId,
87429
+ hostname: config3.hostname,
87430
+ os: config3.os,
87431
+ availableHarnesses: config3.availableHarnesses,
87432
+ harnessVersions: config3.harnessVersions
87433
+ })), (error) => exports_Effect.sync(() => {
87434
+ console.warn(`⚠️ Machine registration update failed: ${getErrorMessage(error)}`);
87435
+ })), fetchCachedMachineModelsEffect = (client4, sessionId, machineId) => exports_Effect.tryPromise({
87436
+ try: () => client4.query(api.machines.getMachineModels, { sessionId, machineId }),
87437
+ catch: (e) => e
87438
+ }).pipe(exports_Effect.map((result) => result?.availableModels ?? {}), exports_Effect.catchAll(() => exports_Effect.succeed({}))), connectOnceEffect = (client4, sessionId, convexUrl) => exports_Effect.gen(function* () {
87439
+ const typedSessionId = yield* validateSessionEffect(client4, sessionId, convexUrl);
87440
+ const config3 = yield* setupMachineEffect();
87015
87441
  const { machineId } = config3;
87016
- const availableModels = yield* discoverModelsEffect(agentServices);
87017
- yield* exports_Effect.catchAll(exports_Effect.tryPromise(() => client4.mutation(api.machines.register, {
87018
- sessionId,
87019
- machineId,
87020
- hostname: config3.hostname,
87021
- os: config3.os,
87022
- availableHarnesses: config3.availableHarnesses,
87023
- harnessVersions: config3.harnessVersions,
87024
- availableModels
87025
- })), (error) => exports_Effect.sync(() => {
87026
- console.warn(`⚠️ Machine registration update failed: ${getErrorMessage(error)}`);
87027
- }));
87028
- return availableModels;
87442
+ initHarnessRegistry();
87443
+ const agentServices = new Map(getAllHarnesses().map((s) => [s.id, s]));
87444
+ yield* registerMachineEffect(client4, typedSessionId, config3);
87445
+ const cachedModels = yield* fetchCachedMachineModelsEffect(client4, typedSessionId, machineId);
87446
+ yield* connectDaemonEffect(client4, typedSessionId, machineId);
87447
+ return { typedSessionId, config: config3, machineId, agentServices, cachedModels };
87029
87448
  }), connectDaemonEffect = (client4, sessionId, machineId) => exports_Effect.tryPromise({
87030
87449
  try: () => client4.mutation(api.machines.updateDaemonStatus, {
87031
87450
  sessionId,
@@ -87070,14 +87489,7 @@ Run: chatroom auth login`);
87070
87489
  const attemptRef = yield* exports_Ref.make(0);
87071
87490
  const connectOnce = exports_Effect.gen(function* () {
87072
87491
  yield* exports_Ref.update(attemptRef, (n) => n + 1);
87073
- const typedSessionId = yield* validateSessionEffect(client4, sessionId, convexUrl);
87074
- const config3 = yield* setupMachineEffect();
87075
- const { machineId } = config3;
87076
- initHarnessRegistry();
87077
- const agentServices = new Map(getAllHarnesses().map((s) => [s.id, s]));
87078
- const availableModels = yield* registerCapabilitiesEffect(client4, typedSessionId, config3, agentServices);
87079
- yield* connectDaemonEffect(client4, typedSessionId, machineId);
87080
- return { typedSessionId, config: config3, machineId, agentServices, availableModels };
87492
+ return yield* connectOnceEffect(client4, sessionId, convexUrl);
87081
87493
  }).pipe(exports_Effect.catchAll((error) => exports_Effect.flatMap(exports_Ref.get(attemptRef), (currentAttempt) => isNetworkError(error) ? exports_Effect.fail(new NetworkRetryError(error, currentAttempt)) : exports_Effect.die(error))));
87082
87494
  const retrySchedule2 = exports_Schedule.fixed(exports_Duration.millis(CONNECTION_RETRY_INTERVAL_MS));
87083
87495
  const connectWithRetry = connectOnce.pipe(exports_Effect.tapError((retryErr) => exports_Effect.gen(function* () {
@@ -87119,7 +87531,11 @@ var init_init2 = __esm(() => {
87119
87531
  init_spawn_env();
87120
87532
  AUTH_WAIT_TIMEOUT_MS = 5 * 60 * 1000;
87121
87533
  initDaemonEffect = exports_Effect.gen(function* () {
87122
- if (!acquireLock()) {
87534
+ const lockAcquired = yield* exports_Effect.tryPromise({
87535
+ try: () => acquireLockWithRetry(),
87536
+ catch: (e) => e
87537
+ });
87538
+ if (!lockAcquired) {
87123
87539
  return yield* exports_Effect.sync(() => {
87124
87540
  process.exit(1);
87125
87541
  });
@@ -87142,44 +87558,19 @@ var init_init2 = __esm(() => {
87142
87558
  try: () => getConvexClient(),
87143
87559
  catch: (e) => e
87144
87560
  });
87145
- const { typedSessionId, config: config3, machineId, agentServices, availableModels } = yield* connectWithRetryEffect(client4, sessionId, convexUrl);
87146
- const deps = createDefaultDeps18();
87147
- deps.backend.mutation = (endpoint, args2) => client4.mutation(endpoint, args2);
87148
- deps.backend.query = (endpoint, args2) => client4.query(endpoint, args2);
87149
- deps.agentProcessManager = new AgentProcessManager({
87150
- agentServices,
87151
- backend: deps.backend,
87152
- sessionId: typedSessionId,
87153
- machineId,
87154
- processes: deps.processes,
87155
- clock: deps.clock,
87156
- fs: deps.fs,
87157
- persistence: deps.machine,
87158
- spawning: deps.spawning,
87159
- crashLoop: new CrashLoopTracker,
87160
- convexUrl
87161
- });
87162
- const events = new DaemonEventBus;
87163
- const init2 = {
87561
+ const { typedSessionId, config: config3, machineId, agentServices, cachedModels } = yield* connectWithRetryEffect(client4, sessionId, convexUrl);
87562
+ const init2 = assembleDaemonSessionInit({
87164
87563
  client: client4,
87165
- sessionId: typedSessionId,
87564
+ typedSessionId,
87166
87565
  machineId,
87167
87566
  config: config3,
87168
87567
  convexUrl,
87169
- backend: deps.backend,
87170
- fs: deps.fs,
87171
- machine: deps.machine,
87172
- spawning: deps.spawning,
87173
- agentProcessManager: deps.agentProcessManager,
87174
- events,
87175
87568
  agentServices,
87176
- lastPushedGitState: new Map,
87177
- lastPushedModels: availableModels,
87178
- lastPushedHarnessFingerprint: harnessCapabilitiesFingerprint(config3.availableHarnesses, config3.harnessVersions),
87179
- logger: console
87180
- };
87569
+ cachedModels,
87570
+ deps: createDefaultDeps18()
87571
+ });
87181
87572
  yield* registerEventListenersEffect().pipe(exports_Effect.provide(daemonSessionToLayers(init2)));
87182
- yield* logStartupEffect(availableModels).pipe(exports_Effect.provide(daemonSessionToLayers(init2)));
87573
+ yield* logStartupEffect(cachedModels).pipe(exports_Effect.provide(daemonSessionToLayers(init2)));
87183
87574
  yield* recoverStateEffect(init2);
87184
87575
  return init2;
87185
87576
  });
@@ -87229,7 +87620,70 @@ function logRefreshOutcome(diff8, totalCount) {
87229
87620
  const summary5 = totalCount > 0 ? `${totalCount} models` : "none discovered";
87230
87621
  console.log(`[${formatTimestamp()}] \uD83D\uDD04 Model refresh pushed: ${summary5}`);
87231
87622
  }
87232
- var refreshModelsEffect;
87623
+ function logPushOutcome(modelDiff, totalCount, options) {
87624
+ if (options?.logPrefix) {
87625
+ console.log(`[${formatTimestamp()}] \uD83D\uDD04 ${options.logPrefix}: ${totalCount > 0 ? `${totalCount} models` : "none discovered"}`);
87626
+ return;
87627
+ }
87628
+ logRefreshOutcome(modelDiff, totalCount);
87629
+ }
87630
+ var pushModelsSnapshotMutationEffect = (models, modelDiff, nextHarnessFingerprint, ctxConfig, options) => exports_Effect.gen(function* () {
87631
+ const session2 = yield* DaemonSessionService;
87632
+ yield* exports_Effect.tryPromise({
87633
+ try: async () => session2.backend.mutation(api.machines.refreshCapabilities, {
87634
+ sessionId: session2.sessionId,
87635
+ machineId: session2.machineId,
87636
+ availableHarnesses: ctxConfig.availableHarnesses,
87637
+ harnessVersions: ctxConfig.harnessVersions,
87638
+ availableModels: models
87639
+ }),
87640
+ catch: (e) => e
87641
+ });
87642
+ const totalCount = Object.values(models).flat().length;
87643
+ logPushOutcome(modelDiff, totalCount, options);
87644
+ return {
87645
+ kind: "pushed",
87646
+ snapshot: {
87647
+ lastPushedModels: models,
87648
+ lastPushedHarnessFingerprint: nextHarnessFingerprint
87649
+ }
87650
+ };
87651
+ }), pushModelsSnapshotIfChangedEffect = (models, options) => exports_Effect.gen(function* () {
87652
+ const session2 = yield* DaemonSessionService;
87653
+ const mutable = yield* DaemonMutableStateService;
87654
+ if (!session2.config) {
87655
+ return { kind: "skipped_no_changes" };
87656
+ }
87657
+ const lastPushedModels = yield* exports_Ref.get(mutable.lastPushedModels);
87658
+ const lastPushedHarnessFingerprint = yield* exports_Ref.get(mutable.lastPushedHarnessFingerprint);
87659
+ const modelDiff = diffModels(lastPushedModels, models);
87660
+ const nextHarnessFingerprint = harnessCapabilitiesFingerprint(session2.config.availableHarnesses, session2.config.harnessVersions);
87661
+ if (!modelDiff.hasChanges && !harnessFingerprintChanged(lastPushedHarnessFingerprint, nextHarnessFingerprint)) {
87662
+ return { kind: "skipped_no_changes" };
87663
+ }
87664
+ return yield* pushModelsSnapshotMutationEffect(models, modelDiff, nextHarnessFingerprint, session2.config, options);
87665
+ }).pipe(exports_Effect.catchAll((error) => exports_Effect.sync(() => {
87666
+ const message = getErrorMessage(error);
87667
+ console.warn(`[${formatTimestamp()}] ⚠️ Model refresh failed: ${message}`);
87668
+ return { kind: "failed", message };
87669
+ }))), refreshModelsEffect, discoverAndPushHarnessModelsEffect = (harness, service3) => exports_Effect.gen(function* () {
87670
+ const mutable = yield* DaemonMutableStateService;
87671
+ const result = yield* exports_Effect.promise(() => discoverModelsForHarness(harness, service3));
87672
+ const current = (yield* exports_Ref.get(mutable.lastPushedModels)) ?? {};
87673
+ const next4 = { ...current };
87674
+ if (result.installed) {
87675
+ next4[harness] = result.models;
87676
+ } else {
87677
+ delete next4[harness];
87678
+ }
87679
+ const pushOutcome = yield* pushModelsSnapshotIfChangedEffect(next4, {
87680
+ logPrefix: `${harness} models updated`
87681
+ });
87682
+ if (pushOutcome.kind === "pushed") {
87683
+ yield* exports_Ref.set(mutable.lastPushedModels, pushOutcome.snapshot.lastPushedModels);
87684
+ yield* exports_Ref.set(mutable.lastPushedHarnessFingerprint, pushOutcome.snapshot.lastPushedHarnessFingerprint);
87685
+ }
87686
+ }), startBackgroundModelDiscoveryEffect;
87233
87687
  var init_models_refresh = __esm(() => {
87234
87688
  init_esm();
87235
87689
  init_daemon_services();
@@ -87244,8 +87698,6 @@ var init_models_refresh = __esm(() => {
87244
87698
  return { kind: "noop" };
87245
87699
  }
87246
87700
  const ctxConfig = session2.config;
87247
- const lastPushedModels = yield* exports_Ref.get(mutable.lastPushedModels);
87248
- const lastPushedHarnessFingerprint = yield* exports_Ref.get(mutable.lastPushedHarnessFingerprint);
87249
87701
  const outcome = yield* exports_Effect.gen(function* () {
87250
87702
  const models = yield* exports_Effect.tryPromise({
87251
87703
  try: async () => discoverModels(session2.agentServices),
@@ -87257,31 +87709,7 @@ var init_models_refresh = __esm(() => {
87257
87709
  });
87258
87710
  ctxConfig.availableHarnesses = freshConfig.availableHarnesses;
87259
87711
  ctxConfig.harnessVersions = freshConfig.harnessVersions;
87260
- const modelDiff = diffModels(lastPushedModels, models);
87261
- const nextHarnessFingerprint = harnessCapabilitiesFingerprint(ctxConfig.availableHarnesses, ctxConfig.harnessVersions);
87262
- const fingerprintChanged = harnessFingerprintChanged(lastPushedHarnessFingerprint, nextHarnessFingerprint);
87263
- if (!modelDiff.hasChanges && !fingerprintChanged) {
87264
- return { kind: "skipped_no_changes" };
87265
- }
87266
- const totalCount = Object.values(models).flat().length;
87267
- yield* exports_Effect.tryPromise({
87268
- try: async () => session2.backend.mutation(api.machines.refreshCapabilities, {
87269
- sessionId: session2.sessionId,
87270
- machineId: session2.machineId,
87271
- availableHarnesses: ctxConfig.availableHarnesses,
87272
- harnessVersions: ctxConfig.harnessVersions,
87273
- availableModels: models
87274
- }),
87275
- catch: (e) => e
87276
- });
87277
- logRefreshOutcome(modelDiff, totalCount);
87278
- return {
87279
- kind: "pushed",
87280
- snapshot: {
87281
- lastPushedModels: models,
87282
- lastPushedHarnessFingerprint: nextHarnessFingerprint
87283
- }
87284
- };
87712
+ return yield* pushModelsSnapshotIfChangedEffect(models);
87285
87713
  }).pipe(exports_Effect.catchAll((error) => exports_Effect.sync(() => {
87286
87714
  const message = getErrorMessage(error);
87287
87715
  console.warn(`[${formatTimestamp()}] ⚠️ Model refresh failed: ${message}`);
@@ -87290,9 +87718,22 @@ var init_models_refresh = __esm(() => {
87290
87718
  if (outcome.kind === "pushed") {
87291
87719
  yield* exports_Ref.set(mutable.lastPushedModels, outcome.snapshot.lastPushedModels);
87292
87720
  yield* exports_Ref.set(mutable.lastPushedHarnessFingerprint, outcome.snapshot.lastPushedHarnessFingerprint);
87721
+ return {
87722
+ kind: "pushed",
87723
+ snapshot: outcome.snapshot
87724
+ };
87725
+ }
87726
+ if (outcome.kind === "skipped_no_changes") {
87727
+ return { kind: "skipped_no_changes" };
87293
87728
  }
87294
87729
  return outcome;
87295
87730
  });
87731
+ startBackgroundModelDiscoveryEffect = exports_Effect.gen(function* () {
87732
+ const session2 = yield* DaemonSessionService;
87733
+ if (!session2.config)
87734
+ return;
87735
+ yield* exports_Effect.forEach(Array.from(session2.agentServices.entries()), ([harness, service3]) => discoverAndPushHarnessModelsEffect(harness, service3), { concurrency: "unbounded" });
87736
+ });
87296
87737
  });
87297
87738
 
87298
87739
  // src/commands/machine/daemon-start/observed-sync.ts
@@ -87489,225 +87930,6 @@ var init_observed_sync = __esm(() => {
87489
87930
  init_convex_error();
87490
87931
  });
87491
87932
 
87492
- // ../../services/backend/src/domain/handoff/parse-compress-context.ts
87493
- function findSectionIndex(content, headings) {
87494
- const indices = headings.map((heading) => content.indexOf(heading)).filter((idx) => idx !== -1);
87495
- return indices.length === 0 ? -1 : Math.min(...indices);
87496
- }
87497
- function normalizeMode(raw) {
87498
- const value = raw.toLowerCase();
87499
- if (value === "reset")
87500
- return "new_session";
87501
- if (value === "none")
87502
- return value;
87503
- return DEFAULT_MODE;
87504
- }
87505
- function extractSectionBody(content, sectionIdx) {
87506
- const matchedHeading = SECTION_HEADINGS.find((h) => content.indexOf(h, sectionIdx) === sectionIdx) ?? SECTION_HEADINGS[0];
87507
- const afterSection = content.slice(sectionIdx);
87508
- const nextHeading = afterSection.slice(matchedHeading.length).search(/\n## /);
87509
- return nextHeading === -1 ? afterSection : afterSection.slice(0, matchedHeading.length + nextHeading);
87510
- }
87511
- function parseCompressContext(handoffContent) {
87512
- const sectionIdx = findSectionIndex(handoffContent, SECTION_HEADINGS);
87513
- if (sectionIdx === -1)
87514
- return DEFAULT_MODE;
87515
- const match17 = extractSectionBody(handoffContent, sectionIdx).match(DATA_TAG);
87516
- if (!match17)
87517
- return DEFAULT_MODE;
87518
- return normalizeMode(match17[1]);
87519
- }
87520
- function compressContextToWantResume(mode) {
87521
- return mode === "none";
87522
- }
87523
- var SECTION_HEADINGS, DATA_TAG, DEFAULT_MODE = "new_session";
87524
- var init_parse_compress_context = __esm(() => {
87525
- SECTION_HEADINGS = ["## Session Management", "## Restart new context"];
87526
- DATA_TAG = /\/\/\s*data:agent\.compress_context=(new_session|reset|none)\b/i;
87527
- });
87528
-
87529
- // src/domain/native-integration/predicates.ts
87530
- function isNativePendingAliveRunning(task) {
87531
- const { agentConfig, status: status3 } = task;
87532
- return isNativeHarness(agentConfig.agentHarness) && status3 === "pending" && agentConfig.spawnedAgentPid != null && agentConfig.desiredState === "running";
87533
- }
87534
- function isNativeInjectableAliveRunning(task) {
87535
- const { agentConfig, status: status3 } = task;
87536
- if (!isNativeHarness(agentConfig.agentHarness))
87537
- return false;
87538
- if (agentConfig.spawnedAgentPid == null || agentConfig.desiredState !== "running") {
87539
- return false;
87540
- }
87541
- if (status3 === "pending")
87542
- return true;
87543
- if (status3 === "acknowledged") {
87544
- const assignedTo = task.assignedTo?.toLowerCase();
87545
- return assignedTo === agentConfig.role.toLowerCase();
87546
- }
87547
- return false;
87548
- }
87549
- function isInjectableNativeAction(action) {
87550
- if (action == null)
87551
- return true;
87552
- return action === NATIVE_WAITING_ACTION;
87553
- }
87554
- function isNativeIdleAfterTaskComplete(participant) {
87555
- return participant.lastSeenAction === NATIVE_TASK_INJECTED_ACTION && participant.lastStatus === "task.completed";
87556
- }
87557
- function isNativeAcknowledgedInjectionRetry(task) {
87558
- if (task.status !== "acknowledged")
87559
- return false;
87560
- const assignedTo = task.assignedTo?.toLowerCase();
87561
- if (assignedTo !== task.agentConfig.role.toLowerCase())
87562
- return false;
87563
- return task.participant?.lastSeenAction === NATIVE_TASK_INJECTED_ACTION;
87564
- }
87565
- function isStaleCliGetNextTaskWaiting(task) {
87566
- const lastSeenAt = task.participant?.lastSeenAt ?? 0;
87567
- return task.participant?.lastSeenAction === "get-next-task:started" && task.createdAt > lastSeenAt;
87568
- }
87569
- function isCliIdleNotListening(task, now, thresholdMs) {
87570
- const lastSeenAt = task.participant?.lastSeenAt ?? 0;
87571
- if (lastSeenAt === 0)
87572
- return now - task.createdAt > thresholdMs;
87573
- return now - lastSeenAt > thresholdMs;
87574
- }
87575
- var init_predicates = __esm(() => {
87576
- init_types();
87577
- init_participant();
87578
- });
87579
-
87580
- // src/domain/native-integration/index.ts
87581
- var init_native_integration = __esm(() => {
87582
- init_types();
87583
- init_predicates();
87584
- init_spawn_policy();
87585
- });
87586
-
87587
- // src/commands/machine/daemon-start/native-task-injector-logic.ts
87588
- function shouldInjectNativeTask(task, opts) {
87589
- if (!isNativeInjectableAliveRunning(task))
87590
- return false;
87591
- if (opts?.alreadyInjectedTaskIds?.has(task.taskId))
87592
- return false;
87593
- return isInjectableNativeAction(task.participant?.lastSeenAction) || isNativeIdleAfterTaskComplete(task.participant ?? {}) || isNativeAcknowledgedInjectionRetry(task);
87594
- }
87595
- function buildNativeInjectionPrompt(params) {
87596
- const { taskDeliveryOutput, compressMode } = params;
87597
- if (compressMode === "new_session") {
87598
- return [
87599
- "⚠️ Context was compacted. Run `chatroom get-system-prompt` only if role instructions are missing.",
87600
- "",
87601
- taskDeliveryOutput
87602
- ].join(`
87603
- `);
87604
- }
87605
- return taskDeliveryOutput;
87606
- }
87607
-
87608
- class NativeInjectionDedup {
87609
- injected = new Set;
87610
- inFlight = new Set;
87611
- tryAcquire(taskId) {
87612
- if (this.has(taskId) || this.inFlight.has(taskId)) {
87613
- return false;
87614
- }
87615
- this.inFlight.add(taskId);
87616
- return true;
87617
- }
87618
- markInjected(taskId) {
87619
- this.inFlight.delete(taskId);
87620
- this.injected.add(taskId);
87621
- }
87622
- has(taskId) {
87623
- return this.injected.has(taskId);
87624
- }
87625
- clear(taskId) {
87626
- this.inFlight.delete(taskId);
87627
- this.injected.delete(taskId);
87628
- }
87629
- }
87630
- var init_native_task_injector_logic = __esm(() => {
87631
- init_predicates();
87632
- init_native_integration();
87633
- });
87634
-
87635
- // src/commands/machine/daemon-start/native-task-injector.ts
87636
- function runNativeInjectionEffect(task, deps, dedup) {
87637
- return exports_Effect.gen(function* () {
87638
- if (!shouldInjectNativeTask(task, { alreadyInjectedTaskIds: dedup })) {
87639
- return;
87640
- }
87641
- const { chatroomId, taskId, taskContent, agentConfig, status: status3 } = task;
87642
- const { role } = agentConfig;
87643
- if (!dedup.tryAcquire(taskId)) {
87644
- return;
87645
- }
87646
- if (status3 === "pending") {
87647
- const claimResult = yield* exports_Effect.tryPromise({
87648
- try: () => deps.backend.mutation(api.tasks.claimTask, {
87649
- sessionId: deps.sessionId,
87650
- chatroomId,
87651
- role,
87652
- taskId
87653
- }),
87654
- catch: (err) => err
87655
- }).pipe(exports_Effect.either);
87656
- if (claimResult._tag === "Left") {
87657
- dedup.clear(taskId);
87658
- return yield* exports_Effect.fail(claimResult.left);
87659
- }
87660
- }
87661
- const deliveryResult = yield* exports_Effect.tryPromise({
87662
- try: () => deps.backend.query(api.messages.getTaskDeliveryPrompt, {
87663
- sessionId: deps.sessionId,
87664
- chatroomId,
87665
- role,
87666
- taskId,
87667
- convexUrl: deps.convexUrl
87668
- }),
87669
- catch: (err) => err
87670
- }).pipe(exports_Effect.either);
87671
- if (deliveryResult._tag === "Left") {
87672
- dedup.clear(taskId);
87673
- return yield* exports_Effect.fail(deliveryResult.left);
87674
- }
87675
- const delivery = deliveryResult.right;
87676
- const prompt = buildNativeInjectionPrompt({
87677
- taskDeliveryOutput: delivery.fullCliOutput,
87678
- compressMode: parseCompressContext(taskContent)
87679
- });
87680
- yield* exports_Effect.tryPromise({
87681
- try: () => deps.backend.mutation(api.participants.join, {
87682
- sessionId: deps.sessionId,
87683
- chatroomId,
87684
- role,
87685
- action: NATIVE_TASK_INJECTED_ACTION,
87686
- taskId
87687
- }),
87688
- catch: (err) => err
87689
- }).pipe(exports_Effect.tapError(() => exports_Effect.sync(() => dedup.clear(taskId))));
87690
- const resumeResult = yield* exports_Effect.tryPromise({
87691
- try: () => deps.agentMgr.resumeTurnForSlot({ chatroomId, role, prompt }),
87692
- catch: (err) => err
87693
- }).pipe(exports_Effect.either);
87694
- if (resumeResult._tag === "Left") {
87695
- dedup.clear(taskId);
87696
- console.warn(`[NativeTaskInjector] resumeTurn failed for ${role}@${chatroomId}: ${getErrorMessage(resumeResult.left)}`);
87697
- return;
87698
- }
87699
- dedup.markInjected(taskId);
87700
- });
87701
- }
87702
- var init_native_task_injector = __esm(() => {
87703
- init_participant();
87704
- init_parse_compress_context();
87705
- init_esm();
87706
- init_native_task_injector_logic();
87707
- init_api3();
87708
- init_convex_error();
87709
- });
87710
-
87711
87933
  // src/commands/machine/daemon-start/task-monitor-logic.ts
87712
87934
  function isPendingAliveRunningTask(task) {
87713
87935
  const { agentConfig, status: status3 } = task;
@@ -87722,18 +87944,38 @@ function isSlotUnavailableForPid(slot, pid, isPidAlive) {
87722
87944
  }
87723
87945
  return !isPidAlive(pid);
87724
87946
  }
87725
- function isNativeAgentLocallyUnavailable(task, health) {
87726
- if (!isNativePendingAliveRunning(task))
87727
- return false;
87728
- const pid = task.agentConfig.spawnedAgentPid;
87729
- if (pid == null)
87730
- return false;
87947
+ function isNativeRevivableTaskStatus(task) {
87948
+ const { status: status3 } = task;
87949
+ if (status3 === "pending") {
87950
+ return task.agentConfig.spawnedAgentPid != null;
87951
+ }
87952
+ if (status3 === "acknowledged") {
87953
+ return task.assignedTo?.toLowerCase() === task.agentConfig.role.toLowerCase();
87954
+ }
87955
+ return false;
87956
+ }
87957
+ function isNativeAgentSlotDown(task, health) {
87731
87958
  const slot = health.getSlot(task.chatroomId, task.agentConfig.role);
87959
+ if (slot?.state === "spawning")
87960
+ return false;
87961
+ const pid = task.agentConfig.spawnedAgentPid ?? slot?.pid;
87962
+ if (pid == null) {
87963
+ return slot?.state !== "running";
87964
+ }
87732
87965
  return isSlotUnavailableForPid(slot, pid, health.isPidAlive);
87733
87966
  }
87967
+ function isNativeActiveTaskAgentDown(task, health) {
87968
+ if (!isNativeHarness(task.agentConfig.agentHarness))
87969
+ return false;
87970
+ if (task.agentConfig.desiredState !== "running")
87971
+ return false;
87972
+ if (!isNativeRevivableTaskStatus(task))
87973
+ return false;
87974
+ return isNativeAgentSlotDown(task, health);
87975
+ }
87734
87976
  function listNativeTasksNeedingRevive(tasks, health, now, cooldown) {
87735
87977
  return tasks.filter((task) => {
87736
- if (!isNativeAgentLocallyUnavailable(task, health))
87978
+ if (!isNativeActiveTaskAgentDown(task, health))
87737
87979
  return false;
87738
87980
  const { chatroomId, agentConfig } = task;
87739
87981
  if (!agentConfig.workingDir)
@@ -87799,16 +88041,6 @@ var init_task_monitor_logic = __esm(() => {
87799
88041
  function resolveTaskWantResume(task) {
87800
88042
  return compressContextToWantResume(parseCompressContext(task.taskContent ?? ""));
87801
88043
  }
87802
- function runNativeInjectionFork(task, runtime4, effectContext2, dedup, agentMgr, session2) {
87803
- exports_Runtime.runFork(runtime4)(runNativeInjectionEffect(task, {
87804
- sessionId: session2.sessionId,
87805
- backend: session2.backend,
87806
- agentMgr: {
87807
- resumeTurnForSlot: (args2) => exports_Effect.runPromise(agentMgr.resumeTurnForSlot(args2))
87808
- },
87809
- convexUrl: session2.convexUrl
87810
- }, dedup).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[TaskMonitor] native injection failed for ${task.agentConfig.role}@${task.chatroomId}: ${getErrorMessage(err)}`)))));
87811
- }
87812
88044
  function buildCliNudgeLogLine(task) {
87813
88045
  const { chatroomId, agentConfig } = task;
87814
88046
  const { role } = agentConfig;
@@ -87817,13 +88049,25 @@ function buildCliNudgeLogLine(task) {
87817
88049
  const wantResume = resolveTaskWantResume(task);
87818
88050
  return `[TaskMonitor] nudging ${role}@${chatroomId} — pending task ${task.taskId}, lastSeenAction=${lastSeenAction}, compress_context=${compressMode}, wantResume=${wantResume}`;
87819
88051
  }
87820
- function executeCliNudge(task, runtime4, effectContext2, agentMgr) {
88052
+ function resolveTaskRunnerContext(task) {
87821
88053
  const { chatroomId, agentConfig } = task;
87822
88054
  const { role } = agentConfig;
87823
88055
  const workingDir = agentConfig.workingDir;
87824
88056
  if (!workingDir)
87825
88057
  return;
87826
- const wantResume = resolveTaskWantResume(task);
88058
+ return {
88059
+ chatroomId,
88060
+ agentConfig,
88061
+ role,
88062
+ workingDir,
88063
+ wantResume: resolveTaskWantResume(task)
88064
+ };
88065
+ }
88066
+ function executeCliNudge(task, runtime4, effectContext2, agentMgr) {
88067
+ const ctx = resolveTaskRunnerContext(task);
88068
+ if (!ctx)
88069
+ return;
88070
+ const { chatroomId, agentConfig, role, workingDir, wantResume } = ctx;
87827
88071
  exports_Runtime.runFork(runtime4)(exports_Effect.gen(function* () {
87828
88072
  yield* agentMgr.stop({ chatroomId, role, reason: "platform.task_monitor_nudge" });
87829
88073
  yield* agentMgr.ensureRunning({
@@ -87838,12 +88082,10 @@ function executeCliNudge(task, runtime4, effectContext2, agentMgr) {
87838
88082
  }).pipe(exports_Effect.provide(effectContext2), exports_Effect.catchAll((err) => exports_Effect.sync(() => console.warn(`[TaskMonitor] nudge failed for ${role}@${chatroomId}: ${getErrorMessage(err)}`)))));
87839
88083
  }
87840
88084
  function runNativeReviveEffect(task, runtime4, effectContext2, agentMgr) {
87841
- const { chatroomId, agentConfig } = task;
87842
- const { role } = agentConfig;
87843
- const workingDir = agentConfig.workingDir;
87844
- if (!workingDir)
88085
+ const ctx = resolveTaskRunnerContext(task);
88086
+ if (!ctx)
87845
88087
  return;
87846
- const wantResume = resolveTaskWantResume(task);
88088
+ const { chatroomId, agentConfig, role, workingDir, wantResume } = ctx;
87847
88089
  console.log(`[TaskMonitor] native revive ${role}@${chatroomId} — backend PID stale or missing locally for pending task ${task.taskId}`);
87848
88090
  exports_Runtime.runFork(runtime4)(exports_Effect.gen(function* () {
87849
88091
  yield* agentMgr.ensureRunning({
@@ -87871,21 +88113,20 @@ function reviveNativeTasks(tasks, localHealth, now, cooldown, runtime4, effectCo
87871
88113
  runNativeReviveEffect(task, runtime4, effectContext2, agentMgr);
87872
88114
  }
87873
88115
  }
87874
- function injectNativeTasks(tasks, runtime4, effectContext2, dedup, agentMgr, sessionDeps) {
87875
- for (const task of tasks) {
87876
- if (shouldInjectNativeTask(task, { alreadyInjectedTaskIds: dedup })) {
87877
- runNativeInjectionFork(task, runtime4, effectContext2, dedup, agentMgr, sessionDeps);
87878
- }
87879
- }
87880
- }
87881
- function processTasksUpdate(tasks, runtime4, effectContext2, dedup, cooldown, agentMgr, sessionDeps) {
88116
+ function processTasksUpdate(tasks, runtime4, effectContext2, cooldown, agentMgr, sessionDeps) {
87882
88117
  const now = Date.now();
87883
88118
  const localHealth = {
87884
88119
  getSlot: (chatroomId, role) => agentMgr.getSlot(chatroomId, role),
87885
88120
  isPidAlive: (pid) => isProcessAlive((p) => process.kill(p, 0), pid)
87886
88121
  };
87887
88122
  reviveNativeTasks(tasks, localHealth, now, cooldown, runtime4, effectContext2, agentMgr);
87888
- injectNativeTasks(tasks, runtime4, effectContext2, dedup, agentMgr, sessionDeps);
88123
+ getNativeTaskDeliveryCoordinator().reconcileAssignedTasks({
88124
+ tasks,
88125
+ runtime: runtime4,
88126
+ effectContext: effectContext2,
88127
+ agentMgr,
88128
+ sessionDeps
88129
+ });
87889
88130
  nudgeStuckTasks(tasks, now, cooldown, runtime4, effectContext2, agentMgr);
87890
88131
  }
87891
88132
  var startTaskMonitorSubscriptionEffect = (wsClient2) => exports_Effect.gen(function* () {
@@ -87895,7 +88136,6 @@ var startTaskMonitorSubscriptionEffect = (wsClient2) => exports_Effect.gen(funct
87895
88136
  const runtime4 = yield* exports_Effect.runtime();
87896
88137
  console.log(`[${formatTimestamp()}] \uD83D\uDCCB Starting task-monitor subscription (reactive)`);
87897
88138
  const cooldown = new NudgeCooldown;
87898
- const dedup = new NativeInjectionDedup;
87899
88139
  let stopped = false;
87900
88140
  const sessionDeps = {
87901
88141
  sessionId: session2.sessionId,
@@ -87908,7 +88148,7 @@ var startTaskMonitorSubscriptionEffect = (wsClient2) => exports_Effect.gen(funct
87908
88148
  const onTasksUpdate = (result) => {
87909
88149
  if (stopped || !result?.tasks?.length)
87910
88150
  return;
87911
- processTasksUpdate(result.tasks, runtime4, effectContext2, dedup, cooldown, agentMgr, sessionDeps);
88151
+ processTasksUpdate(result.tasks, runtime4, effectContext2, cooldown, agentMgr, sessionDeps);
87912
88152
  };
87913
88153
  const unsubscribe = wsClient2.onUpdate(api.machines.getAssignedTasks, { sessionId: session2.sessionId, machineId: session2.machineId }, onTasksUpdate, (err) => console.warn(`[${formatTimestamp()}] Task-monitor subscription error: ${getErrorMessage(err)}`));
87914
88154
  return {
@@ -87923,8 +88163,7 @@ var init_task_monitor = __esm(() => {
87923
88163
  init_parse_compress_context();
87924
88164
  init_esm();
87925
88165
  init_daemon_services();
87926
- init_native_task_injector_logic();
87927
- init_native_task_injector();
88166
+ init_native_task_delivery_coordinator();
87928
88167
  init_task_monitor_logic();
87929
88168
  init_api3();
87930
88169
  init_convex_error();
@@ -88362,13 +88601,16 @@ Listening for commands...`);
88362
88601
  // src/commands/machine/daemon-start/index.ts
88363
88602
  async function daemonStart() {
88364
88603
  const init2 = await initDaemon();
88365
- await exports_Effect.runPromise(startCommandLoopEffect.pipe(exports_Effect.provide(daemonSessionToLayers(init2))));
88604
+ const layers = daemonSessionToLayers(init2);
88605
+ exports_Effect.runFork(startBackgroundModelDiscoveryEffect.pipe(exports_Effect.provide(layers)));
88606
+ await exports_Effect.runPromise(startCommandLoopEffect.pipe(exports_Effect.provide(layers)));
88366
88607
  }
88367
88608
  var init_daemon_start = __esm(() => {
88368
88609
  init_esm();
88369
88610
  init_command_loop();
88370
88611
  init_daemon_layers();
88371
88612
  init_init2();
88613
+ init_models_refresh();
88372
88614
  });
88373
88615
 
88374
88616
  // src/commands/machine/daemon-stop.ts
@@ -89423,4 +89665,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
89423
89665
  });
89424
89666
  program2.parse();
89425
89667
 
89426
- //# debugId=0ABED035EA994A7864756E2164756E21
89668
+ //# debugId=CFDE8D88B080E55664756E2164756E21