chatroom-cli 1.115.3 → 1.116.0

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
@@ -27949,7 +27949,7 @@ var init_v_literals_of = __esm(() => {
27949
27949
  });
27950
27950
 
27951
27951
  // ../../services/backend/src/domain/entities/agent.ts
27952
- var AGENT_HARNESSES, AgentHarnessEnum, agentHarnessValidator, AGENT_TYPES, AgentTypeEnum, agentTypeValidator, MACHINE_COMMAND_TYPES, MachineCommandTypeEnum, machineCommandTypeValidator, MACHINE_COMMAND_STATUSES, MachineCommandStatusEnum, machineCommandStatusValidator, AGENT_START_REASONS, AgentStartReasonEnum, agentStartReasonValidator, USER_EXPLICIT_START_REASONS, EXPLICIT_DAEMON_START_REASONS, isExplicitDaemonStart = (reason) => EXPLICIT_DAEMON_START_REASONS.includes(reason), AGENT_STOP_REASONS, agentPostStopDesiredStateValidator, AgentStopReasonEnum, agentStopReasonValidator, isAgentStopReason = (value) => AGENT_STOP_REASONS.includes(value), MODEL_SOURCES, ModelSourceEnum, modelSourceValidator;
27952
+ var AGENT_HARNESSES, AgentHarnessEnum, agentHarnessValidator, AGENT_TYPES, AgentTypeEnum, agentTypeValidator, MACHINE_COMMAND_TYPES, MachineCommandTypeEnum, machineCommandTypeValidator, MACHINE_COMMAND_STATUSES, MachineCommandStatusEnum, machineCommandStatusValidator, AGENT_START_REASONS, AgentStartReasonEnum, agentStartReasonValidator, USER_EXPLICIT_START_REASONS, EXPLICIT_DAEMON_START_REASONS, isExplicitDaemonStart = (reason) => EXPLICIT_DAEMON_START_REASONS.includes(reason), AGENT_STOP_REASONS, agentPostStopDesiredStateValidator, AgentStopReasonEnum, MODEL_SOURCES, ModelSourceEnum, modelSourceValidator;
27953
27953
  var init_agent = __esm(() => {
27954
27954
  init_values();
27955
27955
  init_v_literals_of();
@@ -28017,7 +28017,6 @@ var init_agent = __esm(() => {
28017
28017
  ];
28018
28018
  agentPostStopDesiredStateValidator = v.union(v.literal("running"), v.literal("stopped"));
28019
28019
  AgentStopReasonEnum = Object.fromEntries(AGENT_STOP_REASONS.map((r) => [r, r]));
28020
- agentStopReasonValidator = v.union(...toLiteralValidators(AGENT_STOP_REASONS));
28021
28020
  MODEL_SOURCES = ["team_config", "none"];
28022
28021
  ModelSourceEnum = Object.fromEntries(MODEL_SOURCES.map((s) => [s, s]));
28023
28022
  modelSourceValidator = v.union(...toLiteralValidators(MODEL_SOURCES));
@@ -28127,7 +28126,7 @@ function normalizeAgentLifecycleFact(raw) {
28127
28126
  emittedAt: Number(rest.emittedAt)
28128
28127
  };
28129
28128
  }
28130
- if (kind === "spawned" || kind === "exited" || kind === "activity" || kind === "turn_failed") {
28129
+ if (kind === "spawned" || kind === "exited" || kind === "activity" || kind === "turn_failed" || kind === "status" || kind === "chatroom_shutdown_complete") {
28131
28130
  return rest;
28132
28131
  }
28133
28132
  throw new Error(`Unknown agent lifecycle fact kind: ${String(kind)}`);
@@ -28144,35 +28143,89 @@ function buildActivityLifecycleFact(params) {
28144
28143
  emittedAt
28145
28144
  };
28146
28145
  }
28146
+ function buildAgentStatusFact(params) {
28147
+ const emittedAt = params.emittedAt ?? Date.now();
28148
+ return {
28149
+ kind: "status",
28150
+ chatroomId: params.chatroomId,
28151
+ role: params.role,
28152
+ status: params.status,
28153
+ ...params.errorSource ? { errorSource: params.errorSource } : {},
28154
+ ...params.errorCode ? { errorCode: params.errorCode } : {},
28155
+ ...params.errorMessage ? { errorMessage: params.errorMessage } : {},
28156
+ revisionKey: buildAgentLifecycleRevisionKey("status", {
28157
+ chatroomId: params.chatroomId,
28158
+ role: params.role,
28159
+ status: params.status,
28160
+ errorCode: params.errorCode,
28161
+ emittedAt
28162
+ }),
28163
+ emittedAt
28164
+ };
28165
+ }
28147
28166
  function agentLifecycleDeliveryKey(machineId, fact) {
28148
28167
  if (fact.kind === "cleared_all_pids")
28149
28168
  return `${machineId}:__machine__`;
28169
+ if (fact.kind === "chatroom_shutdown_complete")
28170
+ return `${machineId}:${fact.chatroomId}:shutdown:${fact.commandId}`;
28150
28171
  return `${machineId}:${fact.chatroomId}:${fact.role.toLowerCase()}`;
28151
28172
  }
28152
28173
  function buildAgentLifecycleRevisionKey(prefix, parts2) {
28153
28174
  return `${prefix}:${Object.entries(parts2).filter(([, v2]) => v2 !== undefined).map(([k, v2]) => `${k}=${v2}`).join(":")}`;
28154
28175
  }
28155
28176
 
28177
+ // src/daemon/domain/entities/agent-slot.ts
28178
+ function isAgentSlotStarted(slot) {
28179
+ return slot.state === AGENT_SLOT_STATE.RUNNING && slot.pid !== undefined;
28180
+ }
28181
+ function isAgentSlotStartInFlight(slot) {
28182
+ return slot.state === AGENT_SLOT_STATE.SPAWNING;
28183
+ }
28184
+ function isAgentSlotActive(slot) {
28185
+ return isAgentSlotStarted(slot) || isAgentSlotStartInFlight(slot);
28186
+ }
28187
+ function agentKey(chatroomId, role) {
28188
+ return `${chatroomId}:${role.toLowerCase()}`;
28189
+ }
28190
+ var AGENT_SLOT_STATE, AGENT_START_DISPOSITION, idleSlot = () => ({ state: AGENT_SLOT_STATE.IDLE });
28191
+ var init_agent_slot = __esm(() => {
28192
+ AGENT_SLOT_STATE = {
28193
+ IDLE: "idle",
28194
+ SPAWNING: "spawning",
28195
+ RUNNING: "running",
28196
+ STOPPING: "stopping"
28197
+ };
28198
+ AGENT_START_DISPOSITION = {
28199
+ STARTED: "started",
28200
+ ALREADY_STARTED: "already_started",
28201
+ START_IN_PROGRESS: "start_in_progress"
28202
+ };
28203
+ });
28204
+
28156
28205
  // src/daemon/domain/usecase/check-agent-slot.ts
28157
28206
  function isSlotIdle(state) {
28158
- return state === "idle";
28207
+ return state === AGENT_SLOT_STATE.IDLE;
28159
28208
  }
28160
28209
  function isSlotSpawning(state) {
28161
- return state === "spawning";
28210
+ return state === AGENT_SLOT_STATE.SPAWNING;
28162
28211
  }
28163
28212
  function isSlotRunning(state) {
28164
- return state === "running";
28213
+ return state === AGENT_SLOT_STATE.RUNNING;
28165
28214
  }
28166
28215
  function isSlotStopping(state) {
28167
- return state === "stopping";
28216
+ return state === AGENT_SLOT_STATE.STOPPING;
28168
28217
  }
28169
28218
  function isTurnPhaseIdle(phase) {
28170
28219
  return phase === "idle";
28171
28220
  }
28172
- var init_check_agent_slot = () => {};
28221
+ var AGENT_SLOT_STATES;
28222
+ var init_check_agent_slot = __esm(() => {
28223
+ init_agent_slot();
28224
+ AGENT_SLOT_STATES = Object.values(AGENT_SLOT_STATE);
28225
+ });
28173
28226
 
28174
28227
  // src/daemon/services/task-service/domain/usecase/native-cold-session-delivery.ts
28175
- function snapshotRequestsNativeColdSession(task) {
28228
+ function taskRequestsNativeColdSession(task) {
28176
28229
  return task.requestsNativeColdSession === true;
28177
28230
  }
28178
28231
  function isColdStartEligibleSlotState(slot) {
@@ -28181,10 +28234,10 @@ function isColdStartEligibleSlotState(slot) {
28181
28234
  return isSlotIdle(slot.state);
28182
28235
  }
28183
28236
  function isNativeColdSessionDeliveryOwnedSpawn(task, slot) {
28184
- return snapshotRequestsNativeColdSession(task) && isColdStartEligibleSlotState(slot);
28237
+ return taskRequestsNativeColdSession(task) && isColdStartEligibleSlotState(slot);
28185
28238
  }
28186
28239
  function explainColdSessionDeliveryBlock(task, slot) {
28187
- if (!snapshotRequestsNativeColdSession(task))
28240
+ if (!taskRequestsNativeColdSession(task))
28188
28241
  return null;
28189
28242
  if (isChatroomStopScopeActive(task.chatroomId)) {
28190
28243
  return "chatroom_stop_scope_active";
@@ -28429,9 +28482,6 @@ var init_assigned_task = __esm(() => {
28429
28482
  });
28430
28483
 
28431
28484
  // src/daemon/services/task-service/domain/usecase/native-ready-invariant.ts
28432
- function isAgentReadyForNativeDelivery(task, slot) {
28433
- return explainAgentReadyForNativeDeliveryBlock(task, slot) === null;
28434
- }
28435
28485
  function explainAgentReadyForNativeDeliveryBlock(task, slot) {
28436
28486
  const runtimeConfig = resolveAgentRuntimeConfig(task, slot);
28437
28487
  if (!runtimeConfig)
@@ -28447,13 +28497,10 @@ function explainAgentReadyForNativeDeliveryBlock(task, slot) {
28447
28497
  return null;
28448
28498
  }
28449
28499
  if (!slot) {
28450
- return task.agentConfig.spawnedAgentPid == null ? "spawned_pid_missing" : `slot_missing (expectedPid=${task.agentConfig.spawnedAgentPid})`;
28500
+ return "slot_missing";
28451
28501
  }
28452
28502
  if (!isSlotRunning(slot.state)) {
28453
- if (task.agentConfig.spawnedAgentPid != null && slot.pid !== task.agentConfig.spawnedAgentPid) {
28454
- return `pid_mismatch (slotPid=${slot.pid ?? "none"}, expectedPid=${task.agentConfig.spawnedAgentPid})`;
28455
- }
28456
- return `slot_not_running (slotState=${slot.state}, expectedPid=${task.agentConfig.spawnedAgentPid ?? "none"})`;
28503
+ return `slot_not_running (slotState=${slot.state})`;
28457
28504
  }
28458
28505
  if (slot.pid == null) {
28459
28506
  return "slot_pid_missing";
@@ -28510,9 +28557,6 @@ var init_native_integration = __esm(() => {
28510
28557
  });
28511
28558
 
28512
28559
  // src/daemon/services/task-service/domain/usecase/native-task-injector-logic.ts
28513
- function shouldDeliverNativeTask(task, opts) {
28514
- return explainNativeDeliveryBlock(task, opts) === null;
28515
- }
28516
28560
  function explainNativeDeliveryBlock(task, opts) {
28517
28561
  if (!isDeliverableNativeTaskStatus(task.status)) {
28518
28562
  return `task_status_not_deliverable (status=${task.status})`;
@@ -28546,8 +28590,8 @@ var init_native_task_injector_logic = __esm(() => {
28546
28590
  // ../../services/backend/src/domain/entities/participant.ts
28547
28591
  var NATIVE_WAITING_ACTION = "native:waiting", NATIVE_TASK_INJECTED_ACTION = "native:task-injected", GET_NEXT_TASK_STARTED_ACTION = "get-next-task:started";
28548
28592
 
28549
- // src/infrastructure/mappers/map-assigned-task.ts
28550
- function mapAssignedTaskSnapshot(row) {
28593
+ // src/infrastructure/mappers/map-assigned-task-view.ts
28594
+ function mapAssignedTask(row) {
28551
28595
  return {
28552
28596
  taskId: row.taskId,
28553
28597
  chatroomId: row.chatroomId,
@@ -28557,11 +28601,7 @@ function mapAssignedTaskSnapshot(row) {
28557
28601
  createdAt: row.createdAt,
28558
28602
  agentConfig: {
28559
28603
  role: row.agentConfig.role,
28560
- machineId: row.agentConfig.machineId,
28561
- spawnedAgentPid: row.agentConfig.spawnedAgentPid,
28562
- desiredState: row.agentConfig.desiredState,
28563
- circuitState: row.agentConfig.circuitState,
28564
- configLifecycleRevision: row.agentConfig.configLifecycleRevision
28604
+ machineId: row.agentConfig.machineId
28565
28605
  },
28566
28606
  assignee: row.assignee,
28567
28607
  participant: row.participant,
@@ -28570,7 +28610,7 @@ function mapAssignedTaskSnapshot(row) {
28570
28610
  }
28571
28611
  function mapAssignedTaskView(row) {
28572
28612
  return {
28573
- ...mapAssignedTaskSnapshot(row),
28613
+ ...mapAssignedTask(row),
28574
28614
  taskContent: row.taskContent,
28575
28615
  taskEnvelope: row.taskEnvelope,
28576
28616
  startInNewSession: row.startInNewSession
@@ -28585,21 +28625,7 @@ function createConvexNativeTaskDeliveryGateway(backend2) {
28585
28625
  sessionId,
28586
28626
  machineId
28587
28627
  });
28588
- return rows.map((row) => ({
28589
- eventId: row._id,
28590
- machineId: row.machineId,
28591
- chatroomId: row.chatroomId,
28592
- taskId: row.taskId,
28593
- role: row.role,
28594
- ...row.assignee === undefined ? {} : {
28595
- assignee: row.assignee
28596
- },
28597
- eventType: row.eventType,
28598
- status: row.status,
28599
- createdAt: row.createdAt,
28600
- ...row.processedAt === undefined ? {} : { processedAt: row.processedAt },
28601
- task: row.task
28602
- }));
28628
+ return mapPendingTaskInboxRows(rows);
28603
28629
  },
28604
28630
  markTaskInboxEventProcessed: async ({ sessionId, machineId, eventId }) => {
28605
28631
  const result = await backend2.mutation(api.chatroomWorkspaceTaskInbox.markProcessed, {
@@ -28642,6 +28668,23 @@ function createConvexNativeTaskDeliveryGateway(backend2) {
28642
28668
  }
28643
28669
  };
28644
28670
  }
28671
+ function mapPendingTaskInboxRows(rows) {
28672
+ return rows.map((row) => ({
28673
+ eventId: row._id,
28674
+ machineId: row.machineId,
28675
+ chatroomId: row.chatroomId,
28676
+ taskId: row.taskId,
28677
+ role: row.role,
28678
+ ...row.assignee === undefined ? {} : {
28679
+ assignee: row.assignee
28680
+ },
28681
+ eventType: row.eventType,
28682
+ status: row.status,
28683
+ createdAt: row.createdAt,
28684
+ ...row.processedAt === undefined ? {} : { processedAt: row.processedAt },
28685
+ task: row.task
28686
+ }));
28687
+ }
28645
28688
  var init_convex_native_task_delivery_gateway = __esm(() => {
28646
28689
  init_api3();
28647
28690
  });
@@ -28744,8 +28787,6 @@ function stableBlockReason(reason) {
28744
28787
  "slot_missing",
28745
28788
  "slot_not_running",
28746
28789
  "slot_pid_missing",
28747
- "spawned_pid_missing",
28748
- "pid_mismatch",
28749
28790
  "harness_session_missing",
28750
28791
  "turn_not_idle",
28751
28792
  "working_dir_missing"
@@ -28797,14 +28838,17 @@ function decideNextDelivery(tasks, context5) {
28797
28838
  if (isSlotStopping(context5.slot?.state ?? "idle")) {
28798
28839
  return { kind: "wait", taskId: task.taskId, reason: "slot_stopping" };
28799
28840
  }
28800
- const coldSession = context5.snapshotRequestsNativeColdSession(task);
28801
- const startAllowed = blockReason.startsWith("slot_missing") || blockReason.startsWith("slot_not_running") || blockReason.startsWith("spawned_pid_missing");
28841
+ const coldSession = context5.taskRequestsNativeColdSession(task);
28842
+ const startAllowed = blockReason.startsWith("slot_missing") || blockReason.startsWith("slot_not_running");
28802
28843
  if (startAllowed && !coldSession && task.status === "pending" && isSlotIdle(context5.slot?.state ?? "idle") && runtimeConfig.workingDir) {
28803
28844
  return { kind: "start-agent", taskId: task.taskId };
28804
28845
  }
28805
28846
  if (blockReason.startsWith("harness_session_missing")) {
28806
28847
  return { kind: "wait", taskId: task.taskId, reason: "session_not_ready" };
28807
28848
  }
28849
+ if (blockReason.startsWith("turn_not_idle")) {
28850
+ return { kind: "wait", taskId: task.taskId, reason: "turn_not_idle" };
28851
+ }
28808
28852
  return { kind: "blocked", taskId: task.taskId, reason: stableBlockReason(blockReason) };
28809
28853
  }
28810
28854
  var init_delivery_decision = __esm(() => {
@@ -28930,7 +28974,7 @@ class NativeTaskDeliveryCoordinator {
28930
28974
  deliveryInFlight: false,
28931
28975
  agentLifecycleInFlight: isRestartOrchestratorInFlight(firstTask.chatroomId, role),
28932
28976
  isNativeHarness: taskService.isNativeHarness,
28933
- snapshotRequestsNativeColdSession: taskService.snapshotRequestsNativeColdSession,
28977
+ taskRequestsNativeColdSession: taskService.taskRequestsNativeColdSession,
28934
28978
  explainNativeDeliveryBlock: taskService.explainNativeDeliveryBlock
28935
28979
  });
28936
28980
  const attemptId = `${Date.now()}-${firstTask.taskId}`;
@@ -28970,7 +29014,6 @@ class NativeTaskDeliveryCoordinator {
28970
29014
  workingDir: runtimeConfig.workingDir,
28971
29015
  reason: AgentStartReasonEnum["platform.pending_task_wake"],
28972
29016
  wantResume: false,
28973
- lifecycleRevision: row.agentConfig.configLifecycleRevision,
28974
29017
  taskId: row.taskId
28975
29018
  }, context5.signal));
28976
29019
  console.log(`[NativeDelivery:execution] attempt=${attemptId} role=${role} chatroom=${row.chatroomId} task=${row.taskId} operation=start-agent result=${startResult && typeof startResult === "object" && "success" in startResult ? startResult.success : "completed"}`);
@@ -29043,7 +29086,7 @@ var init_native_task_delivery_coordinator = __esm(() => {
29043
29086
 
29044
29087
  // src/daemon/services/task-service/service/native-delivery/task-delivery-processor.ts
29045
29088
  async function processTasksUpdate(runtime4, effectContext2, agentMgr, runSerializedForAgent, taskService, sessionDeps, machineId, pass, lifecycleOutbox, isTaskActive, options) {
29046
- const first = options.snapshots[0];
29089
+ const first = options.tasks[0];
29047
29090
  if (!first)
29048
29091
  return;
29049
29092
  logNativeDeliveryTrigger(pass, first.agentConfig.role, first.chatroomId, first.taskId);
@@ -29060,7 +29103,6 @@ async function processTasksUpdate(runtime4, effectContext2, agentMgr, runSeriali
29060
29103
  workingDir: runtimeConfig.workingDir,
29061
29104
  reason: AgentStartReasonEnum["platform.pending_task_wake"],
29062
29105
  wantResume: false,
29063
- lifecycleRevision: task.agentConfig.configLifecycleRevision,
29064
29106
  taskId: task.taskId
29065
29107
  }, context5.signal));
29066
29108
  },
@@ -29080,7 +29122,7 @@ async function processTasksUpdate(runtime4, effectContext2, agentMgr, runSeriali
29080
29122
  }
29081
29123
  };
29082
29124
  await getNativeTaskDeliveryCoordinator().reconcileRoleTasks({
29083
- tasks: [...options.snapshots],
29125
+ tasks: [...options.tasks],
29084
29126
  pass,
29085
29127
  runtime: runtime4,
29086
29128
  effectContext: effectContext2,
@@ -29264,7 +29306,7 @@ function resolveSessionAugmentationForTask(task, role) {
29264
29306
  }
29265
29307
  return resolveSessionAugmentationForRole(role);
29266
29308
  }
29267
- function taskRequestsNativeColdSession(task) {
29309
+ function taskRequestsNativeColdSession2(task) {
29268
29310
  if (task.taskEnvelope !== undefined) {
29269
29311
  return normalizeTaskEnvelope({ taskEnvelope: task.taskEnvelope }).sessionPolicy === "new";
29270
29312
  }
@@ -29287,12 +29329,11 @@ var init_parse_session_augmentation = __esm(() => {
29287
29329
  });
29288
29330
 
29289
29331
  // ../../services/backend/config/reliability.ts
29290
- var HARNESS_SESSION_READY_TIMEOUT_MS = 5000, DAEMON_HEARTBEAT_INTERVAL_MS, DAEMON_HEARTBEAT_TTL_MS, AGENT_REQUEST_DEADLINE_MS = 120000, MACHINE_COMMAND_LEASE_RENEWAL_INTERVAL_MS = 20000, MACHINE_COMMAND_DAEMON_ROUTINE_TTL_MS, AGENT_STOP_TERMINAL_RETENTION_MS, SCOPE_TARGET_STOP_TIMEOUT_MS = 1e4, AGENT_LIFECYCLE_OPERATION_TIMEOUT_MS = 120000, CONNECTION_CLOSE_REQUEST_TTL_MS, ENHANCER_TERMINAL_JOB_RETENTION_MS;
29332
+ var HARNESS_SESSION_READY_TIMEOUT_MS = 5000, DAEMON_HEARTBEAT_INTERVAL_MS, DAEMON_HEARTBEAT_TTL_MS, AGENT_REQUEST_DEADLINE_MS = 120000, MACHINE_COMMAND_LEASE_RENEWAL_INTERVAL_MS = 20000, MACHINE_COMMAND_DAEMON_ROUTINE_TTL_MS, SCOPE_TARGET_STOP_TIMEOUT_MS = 1e4, AGENT_LIFECYCLE_OPERATION_TIMEOUT_MS = 120000, CONNECTION_CLOSE_REQUEST_TTL_MS, ENHANCER_TERMINAL_JOB_RETENTION_MS;
29291
29333
  var init_reliability = __esm(() => {
29292
29334
  DAEMON_HEARTBEAT_INTERVAL_MS = 5 * 60000;
29293
29335
  DAEMON_HEARTBEAT_TTL_MS = 6 * DAEMON_HEARTBEAT_INTERVAL_MS;
29294
29336
  MACHINE_COMMAND_DAEMON_ROUTINE_TTL_MS = 5 * 60000;
29295
- AGENT_STOP_TERMINAL_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
29296
29337
  CONNECTION_CLOSE_REQUEST_TTL_MS = 10 * 60000;
29297
29338
  ENHANCER_TERMINAL_JOB_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
29298
29339
  });
@@ -29314,7 +29355,7 @@ async function waitForHarnessSessionId(agentMgr, chatroomId, role, signal) {
29314
29355
  return null;
29315
29356
  }
29316
29357
  async function ensureColdSessionBeforeNativeInject(task, deps) {
29317
- if (!taskRequestsNativeColdSession({
29358
+ if (!taskRequestsNativeColdSession2({
29318
29359
  content: task.taskContent ?? "",
29319
29360
  taskEnvelope: task.taskEnvelope,
29320
29361
  startInNewSession: task.startInNewSession
@@ -29460,7 +29501,7 @@ function claimPendingTaskIfNeeded(task, deps) {
29460
29501
  });
29461
29502
  }
29462
29503
  function resolveHarnessSessionPolicy(task, initialHarnessSessionId) {
29463
- if (!taskRequestsNativeColdSession({
29504
+ if (!taskRequestsNativeColdSession2({
29464
29505
  content: task.taskContent ?? "",
29465
29506
  taskEnvelope: task.taskEnvelope,
29466
29507
  startInNewSession: task.startInNewSession
@@ -29674,7 +29715,7 @@ class AgentWorkManager {
29674
29715
  });
29675
29716
  this.deliveryTaskService = {
29676
29717
  isNativeHarness,
29677
- snapshotRequestsNativeColdSession,
29718
+ taskRequestsNativeColdSession,
29678
29719
  explainNativeDeliveryBlock,
29679
29720
  releaseTaskAfterTurnFailure: deps.taskService.releaseTaskAfterTurnFailure,
29680
29721
  loadAssignedTaskForAction: deps.taskService.loadAssignedTaskForAction,
@@ -29706,7 +29747,7 @@ class AgentWorkManager {
29706
29747
  }
29707
29748
  async handleAgentTurnEnded(event) {
29708
29749
  const completion = event.completion;
29709
- if (completion && completion.status !== "completed") {
29750
+ if (completion.status !== "completed") {
29710
29751
  const activeTask = this.deps.agentTaskState.get({
29711
29752
  chatroomId: event.chatroomId,
29712
29753
  role: event.role
@@ -29754,6 +29795,16 @@ class AgentWorkManager {
29754
29795
  return { kind: "release-slot" };
29755
29796
  }
29756
29797
  this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
29798
+ try {
29799
+ await this.deps.lifecycleOutbox.enqueue(buildAgentStatusFact({
29800
+ chatroomId: event.chatroomId,
29801
+ role: event.role,
29802
+ status: "waiting"
29803
+ }));
29804
+ } catch (error) {
29805
+ console.error(`[NativeDelivery:turn-ended-outbox-error] chatroom=${event.chatroomId} role=${event.role} error=${error instanceof Error ? error.stack ?? error.message : String(error)}`);
29806
+ return { kind: "hold-slot", reason: "turn-ended-outbox-enqueue-failed" };
29807
+ }
29757
29808
  this.scheduleRoleDelivery(event.chatroomId, event.role);
29758
29809
  return { kind: "release-slot" };
29759
29810
  }
@@ -29777,7 +29828,7 @@ class AgentWorkManager {
29777
29828
  }
29778
29829
  async handleTaskServiceNotification(notification) {
29779
29830
  if (notification.kind === "bootstrap") {
29780
- await this.requestReconcileForSnapshots(notification.snapshots, "bootstrap");
29831
+ await this.requestReconcileForTasks(notification.tasks, "bootstrap");
29781
29832
  return;
29782
29833
  }
29783
29834
  if (notification.event.eventType === "task_deleted" /* TaskDeleted */) {
@@ -29795,7 +29846,7 @@ class AgentWorkManager {
29795
29846
  return;
29796
29847
  this.deps.agentTaskState.clear({ chatroomId, role });
29797
29848
  const slot = this.deps.agentMgr.getSlot(chatroomId, role);
29798
- if (!slot || slot.state === "idle" || slot.state === "stopping")
29849
+ if (!slot || slot.state === AGENT_SLOT_STATE.IDLE || slot.state === AGENT_SLOT_STATE.STOPPING)
29799
29850
  return;
29800
29851
  try {
29801
29852
  await this.deps.runSerializedForAgent({ chatroomId, role }, { timeoutMs: 120000 }, (ops, context5) => ops.stopAgent({
@@ -29825,14 +29876,14 @@ class AgentWorkManager {
29825
29876
  do {
29826
29877
  const source = state.pendingSource ?? params.source;
29827
29878
  state.pendingSource = undefined;
29828
- const snapshots = this.deps.taskSnapshotState.listForRole(params.chatroomId, params.role);
29829
- if (snapshots.length === 0) {
29879
+ const tasks = this.deps.taskInboxState.listForRole(params.chatroomId, params.role);
29880
+ if (tasks.length === 0) {
29830
29881
  logNativeDeliveryDecision(source, params.role, params.chatroomId, "idle", undefined, {
29831
29882
  reason: "no_deliverable_task",
29832
29883
  attemptId: `${Date.now()}-${params.chatroomId}-${params.role}`
29833
29884
  });
29834
29885
  }
29835
- await this.reconcileRole(source, snapshots, params.onTaskDelivered);
29886
+ await this.reconcileRole(source, tasks, params.onTaskDelivered);
29836
29887
  } while (state.pendingSource !== undefined);
29837
29888
  } finally {
29838
29889
  if (this.reconcileStates.get(key) === state)
@@ -29854,8 +29905,8 @@ class AgentWorkManager {
29854
29905
  });
29855
29906
  return delivered;
29856
29907
  }
29857
- async requestReconcileForSnapshots(snapshots, source) {
29858
- const roles = new Set(snapshots.map((snapshot) => `${snapshot.chatroomId}:${snapshot.agentConfig.role.toLowerCase()}`));
29908
+ async requestReconcileForTasks(tasks, source) {
29909
+ const roles = new Set(tasks.map((snapshot) => `${snapshot.chatroomId}:${snapshot.agentConfig.role.toLowerCase()}`));
29859
29910
  await Promise.all([...roles].map((key) => {
29860
29911
  const separator = key.indexOf(":");
29861
29912
  return this.requestReconcile({
@@ -29865,11 +29916,11 @@ class AgentWorkManager {
29865
29916
  });
29866
29917
  }));
29867
29918
  }
29868
- async reconcileRole(pass, snapshots, onTaskDelivered) {
29869
- if (snapshots.length === 0)
29919
+ async reconcileRole(pass, tasks, onTaskDelivered) {
29920
+ if (tasks.length === 0)
29870
29921
  return;
29871
29922
  await processTasksUpdate(this.deps.runtime, this.deps.effectContext, this.deps.agentMgr, this.deps.runSerializedForAgent, this.deliveryTaskService, this.deps.sessionDeps, this.deps.machineId, pass, this.deps.lifecycleOutbox, ({ chatroomId, role, taskId }) => this.deps.agentTaskState.get({ chatroomId, role })?.taskId === taskId, {
29872
- snapshots,
29923
+ tasks,
29873
29924
  onTaskDelivered: (args2) => {
29874
29925
  this.recordTaskDelivered(args2);
29875
29926
  onTaskDelivered?.(args2);
@@ -29884,6 +29935,7 @@ var init_agent_work_manager = __esm(() => {
29884
29935
  init_agent();
29885
29936
  init_chatroom_workspace_task_inbox();
29886
29937
  init_esm();
29938
+ init_agent_slot();
29887
29939
  init_native_cold_session_delivery();
29888
29940
  init_native_task_injector_logic();
29889
29941
  init_convex_native_task_delivery_gateway();
@@ -30291,14 +30343,12 @@ var init_agent_task_state = () => {};
30291
30343
  function defaultNativeTurnPhase() {
30292
30344
  return "idle";
30293
30345
  }
30294
- function isNativeSlotIdleForDelivery(slot) {
30295
- if (slot?.state !== "running")
30296
- return false;
30297
- return (slot.nativeTurnPhase ?? defaultNativeTurnPhase()) === "idle";
30298
- }
30299
30346
  function setNativeTurnPhase(slot, phase) {
30300
30347
  slot.nativeTurnPhase = phase;
30301
30348
  }
30349
+ var init_native_turn_phase = __esm(() => {
30350
+ init_agent_slot();
30351
+ });
30302
30352
 
30303
30353
  // src/daemon/services/agent-process-service/infrastructure/adapters/orphan-process-tracker.ts
30304
30354
  import { createHash } from "node:crypto";
@@ -30441,9 +30491,6 @@ function normalizeAgentStopRole(role) {
30441
30491
  function buildAgentStopTargetKey(args2) {
30442
30492
  return `${args2.machineId}:${normalizeAgentStopRole(args2.role)}:${args2.pid}`;
30443
30493
  }
30444
- function buildAgentStopRevisionKey(args2) {
30445
- return `${args2.stopCommandId}:${args2.targetKey}`;
30446
- }
30447
30494
 
30448
30495
  // src/infrastructure/deps/process.ts
30449
30496
  function isProcessAlive(kill, pid) {
@@ -30675,9 +30722,6 @@ function createChatroomScopeBarrier() {
30675
30722
  }
30676
30723
  };
30677
30724
  }
30678
- function resetChatroomScopeBarrierForTests() {
30679
- activeChatroomScopeDepth.clear();
30680
- }
30681
30725
  function createStopAgentTargetsDeps(args2) {
30682
30726
  const confirmed = createStopAgentConfirmedDeps(args2.confirmedDeps);
30683
30727
  return {
@@ -30707,26 +30751,6 @@ async function runRoleScopedStop(args2) {
30707
30751
  targets
30708
30752
  });
30709
30753
  }
30710
- function createStopAgentTargetsDepsForCommand(args2) {
30711
- const base = createStopAgentTargetsDeps(args2);
30712
- return {
30713
- ...base,
30714
- buildRevisionKey: (target) => buildAgentStopRevisionKey({ stopCommandId: args2.stopCommandId, targetKey: target.targetKey })
30715
- };
30716
- }
30717
- async function runExactTargetsStop(args2) {
30718
- const deps = createStopAgentTargetsDepsForCommand(args2);
30719
- const targets = args2.targets.map((target) => ({
30720
- ...target,
30721
- agentHarness: target.agentHarness
30722
- }));
30723
- return stopAgentTargets(deps, {
30724
- chatroomId: args2.chatroomId,
30725
- scope: { kind: "chatroom" },
30726
- reason: args2.reason,
30727
- targets
30728
- });
30729
- }
30730
30754
  var activeChatroomScopeDepth;
30731
30755
  var init_execute_stop_targets_adapter = __esm(() => {
30732
30756
  init_stop_agent_confirmed_adapter();
@@ -30810,12 +30834,6 @@ var init_agent_lifecycle_types = __esm(() => {
30810
30834
  };
30811
30835
  });
30812
30836
 
30813
- // src/daemon/domain/entities/agent-slot.ts
30814
- function agentKey(chatroomId, role) {
30815
- return `${chatroomId}:${role.toLowerCase()}`;
30816
- }
30817
- var idleSlot = () => ({ state: "idle" });
30818
-
30819
30837
  // src/daemon/domain/usecase/transition-agent-slot.ts
30820
30838
  function makeError(from, event) {
30821
30839
  return {
@@ -30831,7 +30849,11 @@ function transitionFromIdle(slot, event) {
30831
30849
  return makeResult(slot);
30832
30850
  }
30833
30851
  if (event.type === "spawn_started") {
30834
- return makeResult({ ...slot, state: "spawning", pendingOperationKey: event.operationKey });
30852
+ return makeResult({
30853
+ ...slot,
30854
+ state: AGENT_SLOT_STATE.SPAWNING,
30855
+ pendingOperationKey: event.operationKey
30856
+ });
30835
30857
  }
30836
30858
  if (event.type === "process_exited") {
30837
30859
  return makeResult(slot);
@@ -30843,31 +30865,35 @@ function transitionFromSpawning(slot, event) {
30843
30865
  return makeResult({ ...slot, pendingOperationKey: event.operationKey });
30844
30866
  }
30845
30867
  if (event.type === "spawn_succeeded") {
30846
- return makeResult({ ...slot, state: "running", pid: event.pid });
30868
+ return makeResult({ ...slot, state: AGENT_SLOT_STATE.RUNNING, pid: event.pid });
30847
30869
  }
30848
30870
  if (event.type === "spawn_failed" || event.type === "process_exited") {
30849
- return makeResult({ state: "idle" });
30871
+ return makeResult({ state: AGENT_SLOT_STATE.IDLE });
30850
30872
  }
30851
30873
  return makeError(slot.state, event.type);
30852
30874
  }
30853
30875
  function transitionFromRunning(slot, event) {
30854
30876
  if (event.type === "stop_requested") {
30855
- return makeResult({ ...slot, state: "stopping", pendingOperationKey: event.operationKey });
30877
+ return makeResult({
30878
+ ...slot,
30879
+ state: AGENT_SLOT_STATE.STOPPING,
30880
+ pendingOperationKey: event.operationKey
30881
+ });
30856
30882
  }
30857
30883
  if (event.type === "process_exited") {
30858
30884
  if (slot.pid !== undefined && slot.pid !== event.pid) {
30859
30885
  return { ok: false, error: { _tag: "StalePid", expected: slot.pid, got: event.pid } };
30860
30886
  }
30861
- return makeResult({ state: "idle" });
30887
+ return makeResult({ state: AGENT_SLOT_STATE.IDLE });
30862
30888
  }
30863
30889
  if (event.type === "stale_process_detected") {
30864
- return makeResult({ state: "idle" });
30890
+ return makeResult({ state: AGENT_SLOT_STATE.IDLE });
30865
30891
  }
30866
30892
  return makeError(slot.state, event.type);
30867
30893
  }
30868
30894
  function transitionFromStopping(slot, event) {
30869
30895
  if (event.type === "stop_completed") {
30870
- return makeResult({ state: "idle" });
30896
+ return makeResult({ state: AGENT_SLOT_STATE.IDLE });
30871
30897
  }
30872
30898
  if (event.type === "process_exited") {
30873
30899
  return { ok: false, error: { _tag: "IgnoredDuplicateExit" } };
@@ -30876,29 +30902,34 @@ function transitionFromStopping(slot, event) {
30876
30902
  }
30877
30903
  function transitionSlot(slot, event) {
30878
30904
  switch (slot.state) {
30879
- case "idle":
30905
+ case AGENT_SLOT_STATE.IDLE:
30880
30906
  return transitionFromIdle(slot, event);
30881
- case "spawning":
30907
+ case AGENT_SLOT_STATE.SPAWNING:
30882
30908
  return transitionFromSpawning(slot, event);
30883
- case "running":
30909
+ case AGENT_SLOT_STATE.RUNNING:
30884
30910
  return transitionFromRunning(slot, event);
30885
- case "stopping":
30911
+ case AGENT_SLOT_STATE.STOPPING:
30886
30912
  return transitionFromStopping(slot, event);
30887
30913
  }
30888
30914
  }
30889
30915
  function shouldIgnoreProcessExit(slot, exitPid) {
30890
- if (slot.state === "stopping")
30916
+ if (slot.state === AGENT_SLOT_STATE.STOPPING)
30891
30917
  return true;
30892
30918
  if (slot.pid !== undefined && slot.pid !== exitPid)
30893
30919
  return true;
30894
30920
  return false;
30895
30921
  }
30922
+ var init_transition_agent_slot = __esm(() => {
30923
+ init_agent_slot();
30924
+ });
30896
30925
 
30897
30926
  // src/infrastructure/services/agent-lifecycle/agent-lifecycle-service.ts
30898
30927
  var AgentLifecycleServiceLive;
30899
30928
  var init_agent_lifecycle_service = __esm(() => {
30900
30929
  init_esm();
30901
30930
  init_agent_lifecycle_types();
30931
+ init_agent_slot();
30932
+ init_transition_agent_slot();
30902
30933
  AgentLifecycleServiceLive = exports_Layer.effect(AgentLifecycleService, exports_Effect.gen(function* () {
30903
30934
  const ports = yield* AgentLifecyclePorts;
30904
30935
  const slotsRef = yield* exports_Ref.make(new Map);
@@ -30955,18 +30986,16 @@ var init_agent_lifecycle_service = __esm(() => {
30955
30986
  const updatedSlot = { ...slot, harnessSessionId: spawnHandle.harnessSessionId };
30956
30987
  yield* setSlotInRef(key, updatedSlot);
30957
30988
  }
30958
- return { success: true, pid: slot.pid };
30989
+ return { success: true, pid: slot.pid, disposition: AGENT_START_DISPOSITION.STARTED };
30959
30990
  });
30960
30991
  const ensureRunning = (opts) => exports_Effect.gen(function* () {
30961
30992
  const key = agentKey(opts.chatroomId, opts.role);
30962
30993
  const currentSlot = yield* getSlotFromRef(key);
30963
- if (currentSlot && currentSlot.state !== "idle") {
30964
- if (opts.lifecycleRevision !== undefined && currentSlot.authorizedLifecycleRevision !== opts.lifecycleRevision) {
30965
- return { success: false, error: "stale_revision" };
30966
- }
30994
+ if (currentSlot && isAgentSlotActive(currentSlot)) {
30967
30995
  return {
30968
30996
  success: true,
30969
- pid: currentSlot.pid
30997
+ pid: currentSlot.pid,
30998
+ disposition: isAgentSlotStarted(currentSlot) ? AGENT_START_DISPOSITION.ALREADY_STARTED : AGENT_START_DISPOSITION.START_IN_PROGRESS
30970
30999
  };
30971
31000
  }
30972
31001
  const allowResult = ports.spawn.shouldAllowSpawn(opts.chatroomId, opts.reason);
@@ -31039,7 +31068,7 @@ var init_agent_lifecycle_service = __esm(() => {
31039
31068
  const listActive = () => exports_Ref.get(slotsRef).pipe(exports_Effect.map((map18) => {
31040
31069
  const results = [];
31041
31070
  for (const [key, slot] of map18) {
31042
- if (slot.state !== "idle") {
31071
+ if (isAgentSlotActive(slot)) {
31043
31072
  const [chatroomId, role] = key.split(":");
31044
31073
  results.push({ chatroomId, role, slot });
31045
31074
  }
@@ -31468,7 +31497,7 @@ class AgentProcessManager {
31468
31497
  return () => this.agentSessionLostHandlers.delete(handler);
31469
31498
  }
31470
31499
  clearSlotRuntimeState(slot) {
31471
- slot.state = "idle";
31500
+ slot.state = AGENT_SLOT_STATE.IDLE;
31472
31501
  slot.pid = undefined;
31473
31502
  slot.harness = undefined;
31474
31503
  slot.harnessSessionId = undefined;
@@ -31527,22 +31556,27 @@ class AgentProcessManager {
31527
31556
  } else if (slot.stopRequested) {
31528
31557
  return { success: false, error: "stop_requested" };
31529
31558
  }
31530
- if (slot.state !== "idle" && opts.lifecycleRevision !== undefined && slot.authorizedLifecycleRevision !== opts.lifecycleRevision) {
31531
- return { success: false, error: "stale_revision" };
31532
- }
31533
- if (slot.state === "running" && slot.pid && !isProcessAlive(this.deps.processes.kill, slot.pid)) {
31559
+ if (slot.state === AGENT_SLOT_STATE.RUNNING && slot.pid && !isProcessAlive(this.deps.processes.kill, slot.pid)) {
31534
31560
  this.clearSlotRuntimeState(slot);
31535
31561
  }
31536
- if (slot.state === "stopping" && (slot.stoppingSince === undefined || this.deps.clock.now() - slot.stoppingSince >= STOPPING_TIMEOUT_MS)) {
31562
+ if (slot.state === AGENT_SLOT_STATE.STOPPING && (slot.stoppingSince === undefined || this.deps.clock.now() - slot.stoppingSince >= STOPPING_TIMEOUT_MS)) {
31537
31563
  await this.forceClearStuckStoppingSlot(key, slot, opts.chatroomId, opts.role, "daemon.stop_timeout");
31538
31564
  }
31539
31565
  if (slot.pendingOperation) {
31540
- if (slot.state === "stopping") {
31566
+ if (slot.state === AGENT_SLOT_STATE.STOPPING) {
31541
31567
  await slot.pendingOperation;
31542
31568
  } else {
31543
31569
  return slot.pendingOperation;
31544
31570
  }
31545
31571
  }
31572
+ const pid = slot.pid;
31573
+ if (isAgentSlotStarted(slot) && pid !== undefined && isProcessAlive(this.deps.processes.kill, pid)) {
31574
+ return {
31575
+ success: true,
31576
+ pid,
31577
+ disposition: AGENT_START_DISPOSITION.ALREADY_STARTED
31578
+ };
31579
+ }
31546
31580
  const operation = this.executeEnsureRunning(key, slot, opts);
31547
31581
  slot.pendingOperation = operation;
31548
31582
  return operation;
@@ -31627,7 +31661,7 @@ class AgentProcessManager {
31627
31661
  }
31628
31662
  bindStopTarget(args2) {
31629
31663
  const slot = this.slots.get(agentKey2(args2.chatroomId, args2.role));
31630
- if (!slot || slot.pid !== args2.pid || slot.state !== "stopping")
31664
+ if (!slot || slot.pid !== args2.pid || slot.state !== AGENT_SLOT_STATE.STOPPING)
31631
31665
  return;
31632
31666
  slot.stopCommandId = args2.stopCommandId;
31633
31667
  slot.stopTargetKey = args2.targetKey;
@@ -31635,12 +31669,12 @@ class AgentProcessManager {
31635
31669
  async withScopedRoleStop(opts, fn2) {
31636
31670
  const key = agentKey2(opts.chatroomId, opts.role);
31637
31671
  const slot = this.slots.get(key);
31638
- if (!slot || !slot.pid || slot.state === "idle")
31672
+ if (!slot || !slot.pid || slot.state === AGENT_SLOT_STATE.IDLE)
31639
31673
  return { ok: false, reason: "no_slot" };
31640
- if (slot.state === "stopping" || slot.pendingOperation)
31674
+ if (slot.state === AGENT_SLOT_STATE.STOPPING || slot.pendingOperation)
31641
31675
  return { ok: false, reason: "concurrent" };
31642
31676
  this.markStopIntent(opts.chatroomId, opts.role, opts.reason, slot.pid);
31643
- slot.state = "stopping";
31677
+ slot.state = AGENT_SLOT_STATE.STOPPING;
31644
31678
  slot.stoppingSince = this.deps.clock.now();
31645
31679
  try {
31646
31680
  const value = await fn2();
@@ -31648,26 +31682,26 @@ class AgentProcessManager {
31648
31682
  await this.clearAgentPidQuietly(opts.chatroomId, opts.role);
31649
31683
  return { ok: true, value };
31650
31684
  } catch (error) {
31651
- slot.state = "running";
31685
+ slot.state = AGENT_SLOT_STATE.RUNNING;
31652
31686
  throw error;
31653
31687
  }
31654
31688
  }
31655
31689
  async handleStopEarlyReturns(slot, opts, key) {
31656
- if (!slot || slot.state === "idle") {
31690
+ if (!slot || slot.state === AGENT_SLOT_STATE.IDLE) {
31657
31691
  await this.killAndRecordForIdleSlot(slot, opts);
31658
31692
  return { success: true };
31659
31693
  }
31660
- if (slot.state === "stopping" && slot.pendingOperation) {
31694
+ if (slot.state === AGENT_SLOT_STATE.STOPPING && slot.pendingOperation) {
31661
31695
  await slot.pendingOperation;
31662
31696
  return { success: true };
31663
31697
  }
31664
31698
  const pid = slot.pid;
31665
31699
  if (!pid) {
31666
- slot.state = "idle";
31700
+ slot.state = AGENT_SLOT_STATE.IDLE;
31667
31701
  slot.pendingOperation = undefined;
31668
31702
  return { success: true };
31669
31703
  }
31670
- slot.state = "stopping";
31704
+ slot.state = AGENT_SLOT_STATE.STOPPING;
31671
31705
  const stopGeneration = slot.stopGeneration ?? 0;
31672
31706
  slot.stoppingSince = this.deps.clock.now();
31673
31707
  const operation = this.doStop(key, slot, pid, opts, stopGeneration);
@@ -31696,13 +31730,13 @@ class AgentProcessManager {
31696
31730
  }
31697
31731
  async runHandleAgentEnd(opts) {
31698
31732
  const slot = this.slots.get(agentKey2(opts.chatroomId, opts.role));
31699
- if (!slot || slot.pid !== opts.pid || slot.harness !== opts.harness || slot.state !== "running") {
31733
+ if (!slot || slot.pid !== opts.pid || slot.harness !== opts.harness || slot.state !== AGENT_SLOT_STATE.RUNNING) {
31700
31734
  console.log(`[AgentProcessManager] Ignoring stale agent_end: role=${opts.role} callbackPid=${opts.pid} currentPid=${slot?.pid ?? "none"} currentState=${slot?.state ?? "none"}`);
31701
31735
  return;
31702
31736
  }
31703
31737
  const capabilities = getHarnessCapabilities(opts.harness);
31704
31738
  this.updateSlotsMirror(opts.chatroomId, opts.role, {
31705
- state: slot?.state ?? "idle",
31739
+ state: slot?.state ?? AGENT_SLOT_STATE.IDLE,
31706
31740
  pid: slot?.pid,
31707
31741
  harness: slot?.harness,
31708
31742
  harnessSessionId: slot?.harnessSessionId,
@@ -31767,7 +31801,7 @@ class AgentProcessManager {
31767
31801
  async handleExit(opts) {
31768
31802
  const key = agentKey2(opts.chatroomId, opts.role);
31769
31803
  const slot = this.slots.get(key);
31770
- if (!slot || slot.pid !== opts.pid || slot.state === "stopping") {
31804
+ if (!slot || slot.pid !== opts.pid || slot.state === AGENT_SLOT_STATE.STOPPING) {
31771
31805
  return;
31772
31806
  }
31773
31807
  if (slot.stopRequested && slot.expectedStopPid === opts.pid) {
@@ -31812,7 +31846,7 @@ class AgentProcessManager {
31812
31846
  };
31813
31847
  }
31814
31848
  resetSlotAfterExit(slot) {
31815
- slot.state = "idle";
31849
+ slot.state = AGENT_SLOT_STATE.IDLE;
31816
31850
  slot.pid = undefined;
31817
31851
  slot.startedAt = undefined;
31818
31852
  slot.pendingOperation = undefined;
@@ -31857,15 +31891,15 @@ class AgentProcessManager {
31857
31891
  model: slot.model ?? "",
31858
31892
  message: classification2.message
31859
31893
  });
31860
- this.deps.backend.mutation(api.daemon.agentEvents.agentProviderUnavailable, {
31861
- sessionId: this.deps.sessionId,
31862
- machineId: this.deps.machineId,
31894
+ this.deps.lifecycleOutbox.enqueue(buildAgentStatusFact({
31863
31895
  chatroomId,
31864
31896
  role,
31865
- reason: classification2.reason,
31866
- model: slot.model ?? "",
31867
- message: classification2.message
31868
- }).catch(() => {});
31897
+ status: "error",
31898
+ errorSource: "runtime",
31899
+ errorCode: classification2.reason,
31900
+ errorMessage: `${classification2.message}${slot.model ? ` (model: ${slot.model})` : ""}`,
31901
+ emittedAt: this.deps.clock.now()
31902
+ })).catch(() => {});
31869
31903
  }
31870
31904
  getSlot(chatroomId, role) {
31871
31905
  return this.getSlotFromMirror(chatroomId, role);
@@ -31873,7 +31907,7 @@ class AgentProcessManager {
31873
31907
  listActive() {
31874
31908
  const result = [];
31875
31909
  for (const [key, slot] of this.slots) {
31876
- if (slot.state === "running" || slot.state === "spawning") {
31910
+ if (slot.state === AGENT_SLOT_STATE.RUNNING || slot.state === AGENT_SLOT_STATE.SPAWNING) {
31877
31911
  const [chatroomId, role] = key.split(":");
31878
31912
  result.push({ chatroomId, role, slot });
31879
31913
  }
@@ -31891,7 +31925,7 @@ class AgentProcessManager {
31891
31925
  async clearStuckStoppingSlot(chatroomId, role, options) {
31892
31926
  const key = agentKey2(chatroomId, role);
31893
31927
  const slot = this.slots.get(key);
31894
- if (!slot || slot.state !== "stopping") {
31928
+ if (!slot || slot.state !== AGENT_SLOT_STATE.STOPPING) {
31895
31929
  return false;
31896
31930
  }
31897
31931
  const elapsed3 = slot.stoppingSince ? this.deps.clock.now() - slot.stoppingSince : STOPPING_TIMEOUT_MS;
@@ -31967,7 +32001,7 @@ class AgentProcessManager {
31967
32001
  getOrCreateSlot(key) {
31968
32002
  let slot = this.slots.get(key);
31969
32003
  if (!slot) {
31970
- slot = { state: "idle" };
32004
+ slot = { state: AGENT_SLOT_STATE.IDLE };
31971
32005
  this.slots.set(key, slot);
31972
32006
  }
31973
32007
  return slot;
@@ -31996,9 +32030,9 @@ class AgentProcessManager {
31996
32030
  }
31997
32031
  async killInMemorySlotIfAlive(key, chatroomId, role) {
31998
32032
  const slot = this.slots.get(key);
31999
- if (slot?.pid && isProcessAlive(this.deps.processes.kill, slot.pid) && (slot.state === "running" || slot.state === "spawning")) {
32033
+ if (slot?.pid && isProcessAlive(this.deps.processes.kill, slot.pid) && (slot.state === AGENT_SLOT_STATE.RUNNING || slot.state === AGENT_SLOT_STATE.SPAWNING)) {
32000
32034
  const pid = slot.pid;
32001
- slot.state = "stopping";
32035
+ slot.state = AGENT_SLOT_STATE.STOPPING;
32002
32036
  const stopGeneration = this.bumpStopGeneration(slot);
32003
32037
  slot.stoppingSince = this.deps.clock.now();
32004
32038
  await this.doStop(key, slot, pid, { chatroomId, role, reason: "daemon.respawn" }, stopGeneration);
@@ -32022,7 +32056,7 @@ class AgentProcessManager {
32022
32056
  }
32023
32057
  const key = agentKey2(chatroomId, role);
32024
32058
  const currentSlot = this.slots.get(key);
32025
- if (currentSlot?.pid === pid && currentSlot.state !== "idle") {
32059
+ if (currentSlot?.pid === pid && currentSlot.state !== AGENT_SLOT_STATE.IDLE) {
32026
32060
  return;
32027
32061
  }
32028
32062
  await this.stopPersistedProcess(pid, harness);
@@ -32121,7 +32155,7 @@ class AgentProcessManager {
32121
32155
  }
32122
32156
  }
32123
32157
  resetSlotIdle(slot) {
32124
- slot.state = "idle";
32158
+ slot.state = AGENT_SLOT_STATE.IDLE;
32125
32159
  slot.pendingOperation = undefined;
32126
32160
  }
32127
32161
  checkRateLimitGate(opts, slot) {
@@ -32214,7 +32248,7 @@ class AgentProcessManager {
32214
32248
  return { ok: true, spawnResult };
32215
32249
  }
32216
32250
  assignRunningSlotState(key, slot, opts, spawnResult, wantResume, pid) {
32217
- slot.state = "running";
32251
+ slot.state = AGENT_SLOT_STATE.RUNNING;
32218
32252
  slot.pid = pid;
32219
32253
  slot.harness = opts.agentHarness;
32220
32254
  slot.harnessSessionId = spawnResult.harnessSessionId;
@@ -32242,12 +32276,6 @@ class AgentProcessManager {
32242
32276
  }).catch((err) => {
32243
32277
  console.log(` ⚠️ Failed to record agent.started event: ${err.message}`);
32244
32278
  });
32245
- const lifecycleRevision = slot.authorizedLifecycleRevision ?? opts.lifecycleRevision;
32246
- if (lifecycleRevision === undefined) {
32247
- this.deps.processes.kill(pid, "SIGTERM");
32248
- this.resetSlotIdle(slot);
32249
- return Promise.resolve();
32250
- }
32251
32279
  const emittedAt = this.deps.clock.now();
32252
32280
  return this.deps.lifecycleOutbox.enqueue({
32253
32281
  kind: "spawned",
@@ -32263,14 +32291,9 @@ class AgentProcessManager {
32263
32291
  pid,
32264
32292
  emittedAt
32265
32293
  }),
32266
- emittedAt,
32267
- lifecycleRevision
32268
- }).then((result) => {
32269
- if (result.rejectionReason) {
32270
- console.log(` ⚠️ Spawn rejected by backend: ${result.rejectionReason}`);
32271
- this.deps.processes.kill(pid, "SIGTERM");
32272
- this.resetSlotIdle(slot);
32273
- }
32294
+ emittedAt
32295
+ }).then(() => {
32296
+ return;
32274
32297
  }).catch((err) => console.log(` ⚠️ Failed to enqueue agent spawned lifecycle fact: ${err.message}`));
32275
32298
  }
32276
32299
  registerSpawnCallbacks(slot, opts, spawnResult, pid) {
@@ -32397,20 +32420,18 @@ class AgentProcessManager {
32397
32420
  });
32398
32421
  }
32399
32422
  async doEnsureRunning(key, slot, opts) {
32400
- slot.state = "spawning";
32423
+ slot.state = AGENT_SLOT_STATE.SPAWNING;
32401
32424
  const authorization = await this.deps.backend.mutation(api.machines.authorizeAgentStart, {
32402
32425
  sessionId: this.deps.sessionId,
32403
32426
  machineId: this.deps.machineId,
32404
32427
  chatroomId: opts.chatroomId,
32405
32428
  role: opts.role,
32406
- lifecycleRevision: opts.lifecycleRevision,
32407
32429
  taskId: opts.taskId
32408
32430
  });
32409
32431
  if (!authorization.allowed) {
32410
32432
  this.resetSlotIdle(slot);
32411
32433
  return { success: false, error: authorization.reason };
32412
32434
  }
32413
- slot.authorizedLifecycleRevision = authorization.lifecycleRevision;
32414
32435
  const wantResume = opts.wantResume;
32415
32436
  console.log(`[AgentProcessManager] harness start: role=${opts.role} harness=${opts.agentHarness} wantResume=${wantResume} reason=${opts.reason}`);
32416
32437
  try {
@@ -32427,7 +32448,11 @@ class AgentProcessManager {
32427
32448
  if (!spawn2.ok)
32428
32449
  return spawn2.result;
32429
32450
  await this.finalizeRunningSlot(key, slot, opts, spawn2.spawnResult, wantResume);
32430
- return { success: true, pid: spawn2.spawnResult.pid };
32451
+ return {
32452
+ success: true,
32453
+ pid: spawn2.spawnResult.pid,
32454
+ disposition: AGENT_START_DISPOSITION.STARTED
32455
+ };
32431
32456
  } catch (e) {
32432
32457
  this.resetSlotIdle(slot);
32433
32458
  return { success: false, error: `Unexpected error: ${e.message}` };
@@ -32461,7 +32486,7 @@ class AgentProcessManager {
32461
32486
  }
32462
32487
  }
32463
32488
  resetSlotAfterStop(slot) {
32464
- slot.state = "idle";
32489
+ slot.state = AGENT_SLOT_STATE.IDLE;
32465
32490
  slot.pid = undefined;
32466
32491
  slot.startedAt = undefined;
32467
32492
  slot.pendingOperation = undefined;
@@ -32470,8 +32495,6 @@ class AgentProcessManager {
32470
32495
  async forceClearStuckStoppingSlot(key, slot, chatroomId, role, reason, clearStopIntent = false) {
32471
32496
  const pid = slot.pid;
32472
32497
  const harness = slot.harness;
32473
- const stopCommandId = slot.stopCommandId;
32474
- const stopTargetKey = slot.stopTargetKey;
32475
32498
  const durationMs = slot.stoppingSince ? this.deps.clock.now() - slot.stoppingSince : STOPPING_TIMEOUT_MS;
32476
32499
  this.bumpStopGeneration(slot);
32477
32500
  this.clearSlotRuntimeState(slot);
@@ -32505,32 +32528,6 @@ class AgentProcessManager {
32505
32528
  };
32506
32529
  this.recordAgentExit(role, exitArgs, "Failed to record stop-timeout exit");
32507
32530
  }
32508
- if (pid && stopCommandId && stopTargetKey) {
32509
- try {
32510
- await this.deps.backend.mutation(api.agentStops.reportTargetOutcome, {
32511
- sessionId: this.deps.sessionId,
32512
- stopCommandId,
32513
- chatroomId,
32514
- machineId: this.deps.machineId,
32515
- targetKey: stopTargetKey,
32516
- role,
32517
- pid,
32518
- status: "failed",
32519
- outcome: "failed",
32520
- termination: "forced",
32521
- errorMessage: "Daemon force-cleared a timed-out stop operation"
32522
- });
32523
- await this.deps.backend.mutation(api.agentStops.completeMachineExecution, {
32524
- sessionId: this.deps.sessionId,
32525
- stopCommandId,
32526
- machineId: this.deps.machineId,
32527
- status: "failed",
32528
- errorMessage: "Daemon force-cleared a timed-out stop operation"
32529
- });
32530
- } catch (error) {
32531
- console.log(` ⚠️ Failed to finalize timed-out stop for ${role}@${chatroomId}: ${error.message}`);
32532
- }
32533
- }
32534
32531
  await this.clearAgentPidQuietly(chatroomId, role);
32535
32532
  }
32536
32533
  async doStop(key, slot, pid, opts, stopGeneration) {
@@ -32590,10 +32587,12 @@ var init_agent_process_manager = __esm(() => {
32590
32587
  init_api3();
32591
32588
  init_agent_lifecycle_runtime();
32592
32589
  init_agent_lifecycle_types();
32590
+ init_agent_slot();
32593
32591
  init_agent_stop();
32594
32592
  init_spawn_policy();
32595
32593
  init_classify_provider_error();
32596
32594
  init_native_spawn_presence();
32595
+ init_native_turn_phase();
32597
32596
  });
32598
32597
 
32599
32598
  // src/daemon/services/agent-process-service/infrastructure/harness-activity-emitter.ts
@@ -32618,10 +32617,7 @@ function createHarnessActivityEmitter() {
32618
32617
  }
32619
32618
  };
32620
32619
  }
32621
- var HARNESS_ACTIVITY_KINDS;
32622
- var init_harness_activity_emitter = __esm(() => {
32623
- HARNESS_ACTIVITY_KINDS = ["transport", "progress", "waiting", "failure"];
32624
- });
32620
+ var init_harness_activity_emitter = () => {};
32625
32621
 
32626
32622
  // src/daemon/services/agent-process-service/index.ts
32627
32623
  var init_agent_process_service2 = __esm(() => {
@@ -32629,6 +32625,7 @@ var init_agent_process_service2 = __esm(() => {
32629
32625
  init_agent_task_state();
32630
32626
  init_agent_process_command_bus();
32631
32627
  init_service();
32628
+ init_native_turn_phase();
32632
32629
  init_agent_process_manager();
32633
32630
  init_execute_stop_targets_adapter();
32634
32631
  init_harness_activity_emitter();
@@ -32643,58 +32640,58 @@ var init_agent_process_contracts = __esm(() => {
32643
32640
  init_harness_activity_emitter();
32644
32641
  });
32645
32642
 
32646
- // src/daemon/infrastructure/inbox/task-state-manager.ts
32647
- function snapshotKey(taskId, role) {
32643
+ // src/daemon/infrastructure/inbox/task-inbox-state.ts
32644
+ function taskKey(taskId, role) {
32648
32645
  return `${taskId}:${role.toLowerCase()}`;
32649
32646
  }
32650
- function isStaleStatusPatch(snapshot, status3, updatedAt) {
32651
- if (snapshot.updatedAt > updatedAt)
32647
+ function isStaleStatusPatch(current, status3, updatedAt) {
32648
+ if (current.updatedAt > updatedAt)
32652
32649
  return true;
32653
- return snapshot.updatedAt === updatedAt && snapshot.status === "completed" && status3 !== "completed";
32650
+ return current.updatedAt === updatedAt && current.status === "completed" && status3 !== "completed";
32654
32651
  }
32655
32652
 
32656
- class TaskStateManager {
32657
- snapshots = new Map;
32653
+ class TaskInboxState {
32654
+ tasks = new Map;
32658
32655
  initialized = false;
32659
- replace(snapshots) {
32660
- this.snapshots.clear();
32661
- this.upsert(snapshots);
32656
+ replace(tasks) {
32657
+ this.tasks.clear();
32658
+ this.upsert(tasks);
32662
32659
  this.initialized = true;
32663
32660
  }
32664
32661
  listForRole(chatroomId, role) {
32665
32662
  const roleLower = role.toLowerCase();
32666
- return [...this.snapshots.values()].filter((snapshot) => snapshot.chatroomId === chatroomId && snapshot.agentConfig.role.toLowerCase() === roleLower);
32663
+ return [...this.tasks.values()].filter((task) => task.chatroomId === chatroomId && task.agentConfig.role.toLowerCase() === roleLower);
32667
32664
  }
32668
32665
  isInitialized() {
32669
32666
  return this.initialized;
32670
32667
  }
32671
32668
  getForRole(chatroomId, role, taskId) {
32672
- const snapshot = this.snapshots.get(snapshotKey(taskId, role));
32673
- return snapshot?.chatroomId === chatroomId ? snapshot : null;
32669
+ const task = this.tasks.get(taskKey(taskId, role));
32670
+ return task?.chatroomId === chatroomId ? task : null;
32674
32671
  }
32675
32672
  listAll() {
32676
- return [...this.snapshots.values()];
32673
+ return [...this.tasks.values()];
32677
32674
  }
32678
- upsert(snapshots) {
32679
- for (const snapshot of snapshots) {
32680
- this.snapshots.set(snapshotKey(snapshot.taskId, snapshot.agentConfig.role), snapshot);
32675
+ upsert(tasks) {
32676
+ for (const task of tasks) {
32677
+ this.tasks.set(taskKey(task.taskId, task.agentConfig.role), task);
32681
32678
  }
32682
32679
  }
32683
32680
  remove(chatroomId, role, taskId) {
32684
- const key = snapshotKey(taskId, role);
32685
- const snapshot = this.snapshots.get(key);
32686
- if (!snapshot || snapshot.chatroomId !== chatroomId)
32681
+ const key = taskKey(taskId, role);
32682
+ const task = this.tasks.get(key);
32683
+ if (!task || task.chatroomId !== chatroomId)
32687
32684
  return false;
32688
- return this.snapshots.delete(key);
32685
+ return this.tasks.delete(key);
32689
32686
  }
32690
32687
  markStatus(chatroomId, role, taskId, status3, updatedAt) {
32691
- const key = snapshotKey(taskId, role);
32692
- const snapshot = this.snapshots.get(key);
32693
- if (!snapshot || snapshot.chatroomId !== chatroomId)
32688
+ const key = taskKey(taskId, role);
32689
+ const task = this.tasks.get(key);
32690
+ if (!task || task.chatroomId !== chatroomId)
32694
32691
  return false;
32695
- if (isStaleStatusPatch(snapshot, status3, updatedAt))
32692
+ if (isStaleStatusPatch(task, status3, updatedAt))
32696
32693
  return false;
32697
- this.snapshots.set(key, { ...snapshot, status: status3, updatedAt });
32694
+ this.tasks.set(key, { ...task, status: status3, updatedAt });
32698
32695
  return true;
32699
32696
  }
32700
32697
  }
@@ -32702,19 +32699,25 @@ class TaskStateManager {
32702
32699
  // src/daemon/services/task-service/service/task-service.ts
32703
32700
  function createTaskService(deps) {
32704
32701
  const gateway = createConvexNativeTaskDeliveryGateway(deps.backend);
32705
- const taskSnapshotState = new TaskStateManager;
32702
+ const taskInboxState = new TaskInboxState;
32706
32703
  const listeners = new Set;
32707
- let inboxPollTimer;
32708
- let inboxPollInFlight = false;
32704
+ let stopInboxWatch;
32705
+ let inboxStopped = false;
32706
+ const pendingEvents = new Map;
32707
+ const scheduledEventIds = new Set;
32708
+ const deliveredEventIds = new Set;
32709
+ const deliveryRetryTimers = new Map;
32710
+ const acknowledgementRetryTimers = new Map;
32711
+ const taskChains = new Map;
32709
32712
  const notify = async (notification) => {
32710
32713
  await Promise.all([...listeners].map((listener) => Promise.resolve(listener(notification))));
32711
32714
  };
32712
32715
  const applyInboxEvent = (event) => {
32713
32716
  if (event.eventType === "task_deleted" /* TaskDeleted */ || event.task.status === "completed") {
32714
- taskSnapshotState.remove(event.chatroomId, event.role, event.taskId);
32717
+ taskInboxState.remove(event.chatroomId, event.role, event.taskId);
32715
32718
  return true;
32716
32719
  }
32717
- taskSnapshotState.upsert([
32720
+ taskInboxState.upsert([
32718
32721
  {
32719
32722
  taskId: event.task.taskId,
32720
32723
  chatroomId: event.task.chatroomId,
@@ -32732,45 +32735,130 @@ function createTaskService(deps) {
32732
32735
  ]);
32733
32736
  return true;
32734
32737
  };
32735
- const pollTaskInbox = async () => {
32736
- if (inboxPollInFlight)
32738
+ const taskKey2 = (event) => `${event.chatroomId}:${event.role.toLowerCase()}:${event.taskId}`;
32739
+ const clearRetryTimer = (timers, eventId) => {
32740
+ const timer = timers.get(eventId);
32741
+ if (timer)
32742
+ clearTimeout(timer);
32743
+ timers.delete(eventId);
32744
+ };
32745
+ const retryDelivery = (eventId) => {
32746
+ if (inboxStopped || deliveryRetryTimers.has(eventId) || !pendingEvents.has(eventId))
32737
32747
  return;
32738
- inboxPollInFlight = true;
32748
+ const timer = setTimeout(() => {
32749
+ deliveryRetryTimers.delete(eventId);
32750
+ const event = pendingEvents.get(eventId);
32751
+ if (event)
32752
+ scheduleEvent(event);
32753
+ }, TASK_INBOX_RETRY_DELAY_MS);
32754
+ deliveryRetryTimers.set(eventId, timer);
32755
+ timer.unref?.();
32756
+ };
32757
+ const retryAcknowledgement = (eventId) => {
32758
+ if (inboxStopped || acknowledgementRetryTimers.has(eventId) || !pendingEvents.has(eventId)) {
32759
+ return;
32760
+ }
32761
+ const timer = setTimeout(() => {
32762
+ acknowledgementRetryTimers.delete(eventId);
32763
+ const event = pendingEvents.get(eventId);
32764
+ if (event)
32765
+ acknowledgeEvent(event);
32766
+ }, TASK_INBOX_RETRY_DELAY_MS);
32767
+ acknowledgementRetryTimers.set(eventId, timer);
32768
+ timer.unref?.();
32769
+ };
32770
+ const acknowledgeEvent = async (event) => {
32739
32771
  try {
32740
- const events = await gateway.listPendingTaskInboxEvents({
32772
+ await gateway.markTaskInboxEventProcessed({
32741
32773
  sessionId: deps.sessionId,
32742
- machineId: deps.machineId
32774
+ machineId: deps.machineId,
32775
+ eventId: event.eventId
32743
32776
  });
32744
- for (const event of events) {
32777
+ } catch (error) {
32778
+ console.warn("[TaskService] task inbox acknowledgement failed:", error);
32779
+ retryAcknowledgement(event.eventId);
32780
+ }
32781
+ };
32782
+ const scheduleEvent = (event) => {
32783
+ if (inboxStopped || deliveredEventIds.has(event.eventId) || scheduledEventIds.has(event.eventId)) {
32784
+ return Promise.resolve();
32785
+ }
32786
+ scheduledEventIds.add(event.eventId);
32787
+ const previous = taskChains.get(taskKey2(event)) ?? Promise.resolve();
32788
+ const current = previous.catch(() => {
32789
+ return;
32790
+ }).then(async () => {
32791
+ try {
32745
32792
  if (!applyInboxEvent(event))
32746
- continue;
32793
+ return;
32747
32794
  await notify({ kind: "inbox-event", event });
32748
- await gateway.markTaskInboxEventProcessed({
32749
- sessionId: deps.sessionId,
32750
- machineId: deps.machineId,
32751
- eventId: event.eventId
32752
- });
32795
+ deliveredEventIds.add(event.eventId);
32796
+ } catch (error) {
32797
+ console.warn("[TaskService] task inbox delivery failed:", error);
32798
+ retryDelivery(event.eventId);
32799
+ return;
32800
+ } finally {
32801
+ scheduledEventIds.delete(event.eventId);
32802
+ }
32803
+ await acknowledgeEvent(event);
32804
+ });
32805
+ taskChains.set(taskKey2(event), current);
32806
+ current.finally(() => {
32807
+ if (taskChains.get(taskKey2(event)) === current)
32808
+ taskChains.delete(taskKey2(event));
32809
+ });
32810
+ return current;
32811
+ };
32812
+ const reconcilePendingEvents = (events) => {
32813
+ const nextIds = new Set(events.map((event) => event.eventId));
32814
+ for (const eventId of pendingEvents.keys()) {
32815
+ if (!nextIds.has(eventId)) {
32816
+ pendingEvents.delete(eventId);
32817
+ deliveredEventIds.delete(eventId);
32818
+ clearRetryTimer(deliveryRetryTimers, eventId);
32819
+ clearRetryTimer(acknowledgementRetryTimers, eventId);
32753
32820
  }
32754
- } catch (error) {
32755
- console.warn("[TaskService] task inbox poll failed:", error);
32756
- } finally {
32757
- inboxPollInFlight = false;
32758
32821
  }
32822
+ return Promise.all(events.map((event) => {
32823
+ pendingEvents.set(event.eventId, event);
32824
+ return scheduleEvent(event);
32825
+ })).then(() => {
32826
+ return;
32827
+ });
32759
32828
  };
32760
32829
  const service4 = {
32761
- startTaskInbox: async () => {
32762
- await pollTaskInbox();
32763
- inboxPollTimer ??= setInterval(() => void pollTaskInbox(), 1000);
32764
- inboxPollTimer.unref?.();
32830
+ startTaskInbox: async (wsClient2) => {
32831
+ inboxStopped = false;
32832
+ if (wsClient2 && !stopInboxWatch) {
32833
+ stopInboxWatch = wsClient2.onUpdate(api.chatroomWorkspaceTaskInbox.listPending, { sessionId: deps.sessionId, machineId: deps.machineId }, (rows) => {
32834
+ reconcilePendingEvents(mapPendingTaskInboxRows(rows));
32835
+ }, (error) => console.warn(`[daemon] task-inbox watch error: ${String(error)}`));
32836
+ } else if (!wsClient2) {
32837
+ const events = await gateway.listPendingTaskInboxEvents({
32838
+ sessionId: deps.sessionId,
32839
+ machineId: deps.machineId
32840
+ });
32841
+ await reconcilePendingEvents(events);
32842
+ }
32765
32843
  },
32766
32844
  subscribe: (listener) => {
32767
32845
  listeners.add(listener);
32768
32846
  return () => listeners.delete(listener);
32769
32847
  },
32770
32848
  stopTaskInbox: () => {
32771
- if (inboxPollTimer)
32772
- clearInterval(inboxPollTimer);
32773
- inboxPollTimer = undefined;
32849
+ inboxStopped = true;
32850
+ stopInboxWatch?.();
32851
+ stopInboxWatch = undefined;
32852
+ pendingEvents.clear();
32853
+ scheduledEventIds.clear();
32854
+ deliveredEventIds.clear();
32855
+ for (const timer of deliveryRetryTimers.values())
32856
+ clearTimeout(timer);
32857
+ deliveryRetryTimers.clear();
32858
+ for (const timer of acknowledgementRetryTimers.values())
32859
+ clearTimeout(timer);
32860
+ acknowledgementRetryTimers.clear();
32861
+ taskChains.clear();
32774
32862
  },
32775
32863
  listPendingTaskInboxEvents: () => gateway.listPendingTaskInboxEvents({
32776
32864
  sessionId: deps.sessionId,
@@ -32781,9 +32869,9 @@ function createTaskService(deps) {
32781
32869
  machineId: deps.machineId,
32782
32870
  eventId
32783
32871
  }),
32784
- listTasksForRole: (chatroomId, role) => taskSnapshotState.listForRole(chatroomId, role),
32785
- listAllTasks: () => taskSnapshotState.listAll(),
32786
- taskSnapshotState,
32872
+ listTasksForRole: (chatroomId, role) => taskInboxState.listForRole(chatroomId, role),
32873
+ listAllTasks: () => taskInboxState.listAll(),
32874
+ taskInboxState,
32787
32875
  releaseTaskAfterTurnFailure: async (args2) => {
32788
32876
  const result = await gateway.releaseTaskAfterTurnFailure({
32789
32877
  sessionId: deps.sessionId,
@@ -32791,7 +32879,7 @@ function createTaskService(deps) {
32791
32879
  role: args2.role,
32792
32880
  taskId: args2.taskId
32793
32881
  });
32794
- taskSnapshotState.markStatus(args2.chatroomId, args2.role, args2.taskId, result.status, result.updatedAt);
32882
+ taskInboxState.markStatus(args2.chatroomId, args2.role, args2.taskId, result.status, result.updatedAt);
32795
32883
  return result;
32796
32884
  },
32797
32885
  loadAssignedTaskForAction: async ({ chatroomId, role, taskId }) => {
@@ -32806,8 +32894,10 @@ function createTaskService(deps) {
32806
32894
  };
32807
32895
  return service4;
32808
32896
  }
32897
+ var TASK_INBOX_RETRY_DELAY_MS = 1000;
32809
32898
  var init_task_service = __esm(() => {
32810
32899
  init_chatroom_workspace_task_inbox();
32900
+ init_api3();
32811
32901
  init_convex_native_task_delivery_gateway();
32812
32902
  });
32813
32903
 
@@ -32824,55 +32914,6 @@ var init_task_service2 = __esm(() => {
32824
32914
  });
32825
32915
 
32826
32916
  // src/daemon/services/service-interfaces.ts
32827
- var exports_service_interfaces = {};
32828
- __export(exports_service_interfaces, {
32829
- untrackChildPid: () => untrackChildPid,
32830
- trackChildPid: () => trackChildPid,
32831
- snapshotRequestsNativeColdSession: () => snapshotRequestsNativeColdSession,
32832
- shouldDeliverNativeTask: () => shouldDeliverNativeTask,
32833
- setNativeTurnPhase: () => setNativeTurnPhase,
32834
- runRoleScopedStop: () => runRoleScopedStop,
32835
- runNativeInjectionEffect: () => runNativeInjectionEffect,
32836
- runExactTargetsStop: () => runExactTargetsStop,
32837
- runConfirmedStop: () => runConfirmedStop,
32838
- resetRoleDeliveryState: () => resetRoleDeliveryState,
32839
- resetChatroomScopeBarrierForTests: () => resetChatroomScopeBarrierForTests,
32840
- reapOrphanedProcessGroupsEffect: () => reapOrphanedProcessGroupsEffect,
32841
- reapOrphanedProcessGroups: () => reapOrphanedProcessGroups,
32842
- isNativeSlotIdleForDelivery: () => isNativeSlotIdleForDelivery,
32843
- isNativeHarness: () => isNativeHarness,
32844
- isDeliverableNativeTaskStatus: () => isDeliverableNativeTaskStatus,
32845
- isChatroomStopScopeActive: () => isChatroomStopScopeActive,
32846
- isAgentReadyForNativeDelivery: () => isAgentReadyForNativeDelivery,
32847
- getChildPidsFilePath: () => getChildPidsFilePath,
32848
- forceKillAllTrackedProcessGroupsEffect: () => forceKillAllTrackedProcessGroupsEffect,
32849
- forceKillAllTrackedProcessGroups: () => forceKillAllTrackedProcessGroups,
32850
- explainNativeDeliveryBlock: () => explainNativeDeliveryBlock,
32851
- explainAgentReadyForNativeDeliveryBlock: () => explainAgentReadyForNativeDeliveryBlock,
32852
- defaultNativeTurnPhase: () => defaultNativeTurnPhase,
32853
- createTaskService: () => createTaskService,
32854
- createStopAgentTargetsDepsForCommand: () => createStopAgentTargetsDepsForCommand,
32855
- createStopAgentTargetsDeps: () => createStopAgentTargetsDeps,
32856
- createStopAgentConfirmedDeps: () => createStopAgentConfirmedDeps,
32857
- createHarnessActivityEmitter: () => createHarnessActivityEmitter,
32858
- createDaemonAuditPort: () => createDaemonAuditPort,
32859
- createConvexNativeTaskDeliveryGateway: () => createConvexNativeTaskDeliveryGateway,
32860
- createChatroomScopeBarrier: () => createChatroomScopeBarrier,
32861
- createAgentTaskStateService: () => createAgentTaskStateService,
32862
- createAgentProcessService: () => createAgentProcessService,
32863
- createAgentProcessManagerService: () => createAgentProcessManagerService,
32864
- createAgentProcessCommandBus: () => createAgentProcessCommandBus,
32865
- clearTrackedPidsEffect: () => clearTrackedPidsEffect,
32866
- clearTrackedPids: () => clearTrackedPids,
32867
- buildStopTargetDescriptor: () => buildStopTargetDescriptor,
32868
- buildNativeInjectionPrompt: () => buildNativeInjectionPrompt,
32869
- STOPPING_TIMEOUT_MS: () => STOPPING_TIMEOUT_MS,
32870
- InMemoryActiveTaskStateStore: () => InMemoryActiveTaskStateStore,
32871
- HARNESS_ACTIVITY_KINDS: () => HARNESS_ACTIVITY_KINDS,
32872
- DefaultAgentTaskStateService: () => DefaultAgentTaskStateService,
32873
- AgentWorkManager: () => AgentWorkManager,
32874
- AgentProcessManager: () => AgentProcessManager
32875
- });
32876
32917
  var init_service_interfaces = __esm(() => {
32877
32918
  init_task_service2();
32878
32919
  init_agent_process_service2();
@@ -83879,7 +83920,7 @@ async function registerAgent(chatroomId, options, deps) {
83879
83920
  var registerAgentEffect = (chatroomId, options) => exports_Effect.gen(function* () {
83880
83921
  const backend2 = yield* BackendService;
83881
83922
  const machine = yield* RegisterAgentMachineService;
83882
- const { role, type, allowTypeChange } = options;
83923
+ const { role, type } = options;
83883
83924
  const sessionId = yield* requireSessionIdEffect((a) => ({
83884
83925
  _tag: "NotAuthenticated",
83885
83926
  convexUrl: a.convexUrl,
@@ -83911,27 +83952,12 @@ var registerAgentEffect = (chatroomId, options) => exports_Effect.gen(function*
83911
83952
  return yield* exports_Effect.fail({ _tag: "MachineNotRegistered" });
83912
83953
  }
83913
83954
  const config2 = yield* machine.loadMachineConfig();
83914
- yield* backend2.mutation(api.machines.recordRemoteAgentRegistered, {
83915
- sessionId,
83916
- chatroomId,
83917
- role,
83918
- machineId
83919
- }).pipe(exports_Effect.catchAll(() => exports_Effect.succeed(undefined)));
83920
83955
  yield* exports_Effect.sync(() => {
83921
83956
  console.log(`✅ Registered as remote agent for role "${role}"`);
83922
83957
  console.log(` Machine: ${config2?.hostname ?? "unknown"} (${machineId})`);
83923
83958
  console.log(` Working directory: ${process.cwd()}`);
83924
83959
  });
83925
83960
  } else {
83926
- yield* backend2.mutation(api.machines.recordCustomAgentRegistered, {
83927
- sessionId,
83928
- chatroomId,
83929
- role,
83930
- allowTypeChange
83931
- }).pipe(exports_Effect.mapError((cause3) => ({
83932
- _tag: "RegisterFailed",
83933
- cause: cause3
83934
- })));
83935
83961
  yield* exports_Effect.sync(() => {
83936
83962
  console.log(`✅ Registered as custom agent for role "${role}"`);
83937
83963
  });
@@ -84585,11 +84611,12 @@ var getNextTaskEffect = (chatroomId, options) => exports_Effect.gen(function* ()
84585
84611
  const machineId = yield* exports_Effect.promise(() => getMachineId().then((id3) => id3 ?? undefined).catch(() => {
84586
84612
  return;
84587
84613
  }));
84588
- const teamConfigs = yield* backend2.query(api.machines.getTeamAgentConfigs, {
84614
+ const lastSentRequest = yield* backend2.query(api.agents.getLastSentLaunchRequest, {
84589
84615
  sessionId,
84590
- chatroomId
84591
- }).pipe(exports_Effect.catchAll(() => exports_Effect.succeed([])));
84592
- const participantAgentType = teamConfigs?.find((c) => c.role.toLowerCase() === options.role.toLowerCase())?.type;
84616
+ chatroomId,
84617
+ role: options.role
84618
+ }).pipe(exports_Effect.catchAll(() => exports_Effect.succeed(null)));
84619
+ const participantAgentType = lastSentRequest?.agentType;
84593
84620
  yield* backend2.mutation(api.participants.join, {
84594
84621
  sessionId,
84595
84622
  chatroomId,
@@ -84674,25 +84701,39 @@ var init_get_next_task = __esm(() => {
84674
84701
  });
84675
84702
 
84676
84703
  // ../shared/src/domain/team-presets.ts
84677
- function getTeamPreset(teamId) {
84704
+ function getTeamPresetId(teamId) {
84678
84705
  const normalized = teamId.trim().toLowerCase();
84679
- return normalized === "duo" || normalized === "solo" ? TEAM_PRESETS[normalized] : undefined;
84706
+ if (normalized === "duo" || normalized === TEAM_STRUCTURE_IDS.duo)
84707
+ return "duo";
84708
+ if (normalized === "solo" || normalized === TEAM_STRUCTURE_IDS.solo)
84709
+ return "solo";
84710
+ return;
84711
+ }
84712
+ function getTeamPreset(teamId) {
84713
+ const presetId = getTeamPresetId(teamId);
84714
+ return presetId ? TEAM_PRESETS[presetId] : undefined;
84680
84715
  }
84681
84716
  function listTeamPresetIds() {
84682
84717
  return [...TEAM_PRESET_IDS];
84683
84718
  }
84684
- var TEAM_PRESET_IDS, DEFAULT_TEAM_PRESET_ID = "duo", TEAM_PRESETS;
84719
+ var TEAM_PRESET_IDS, DEFAULT_TEAM_PRESET_ID = "duo", TEAM_STRUCTURE_IDS, TEAM_PRESETS;
84685
84720
  var init_team_presets = __esm(() => {
84686
84721
  init_agent_role();
84687
84722
  TEAM_PRESET_IDS = ["duo", "solo"];
84723
+ TEAM_STRUCTURE_IDS = {
84724
+ duo: "duo@1",
84725
+ solo: "solo@1"
84726
+ };
84688
84727
  TEAM_PRESETS = {
84689
84728
  duo: {
84729
+ structureId: TEAM_STRUCTURE_IDS.duo,
84690
84730
  name: "Duo",
84691
84731
  description: "A planner and builder working as a pair, planner as coordinator, with optional ephemeral enhancer",
84692
84732
  roles: ["planner", "enhancer", "builder"],
84693
84733
  entryPoint: "planner"
84694
84734
  },
84695
84735
  solo: {
84736
+ structureId: TEAM_STRUCTURE_IDS.solo,
84696
84737
  name: "Solo",
84697
84738
  description: "A single agent working independently, with optional ephemeral enhancer",
84698
84739
  roles: ["solo", "enhancer"],
@@ -84765,10 +84806,7 @@ async function setTeam(chatroomId, teamId, deps) {
84765
84806
  await d.backend.mutation(api.chatrooms.updateTeam, {
84766
84807
  sessionId,
84767
84808
  chatroomId,
84768
- teamId,
84769
- teamName: preset.name,
84770
- teamRoles: preset.roles,
84771
- teamEntryPoint: preset.entryPoint
84809
+ teamStructureId: teamId
84772
84810
  });
84773
84811
  console.log(`✅ Team set to ${teamId} (${preset.name})`);
84774
84812
  console.log(` Roles: ${preset.roles.join(", ")}`);
@@ -84815,7 +84853,7 @@ function configuredWorkingDir(config3) {
84815
84853
  return config3?.workingDir;
84816
84854
  }
84817
84855
  async function getConfigs(deps, sessionId, chatroomId) {
84818
- return await deps.backend.query(api.machines.getTeamAgentConfigs, {
84856
+ return await deps.backend.query(api.agents.listLastSentLaunchRequests, {
84819
84857
  sessionId,
84820
84858
  chatroomId
84821
84859
  });
@@ -84831,7 +84869,6 @@ async function getAgentConfig(chatroomId, role, deps) {
84831
84869
  console.log(` Model: ${config3.model ?? "(unset)"}`);
84832
84870
  console.log(` Working directory: ${config3.workingDir ?? "(unset)"}`);
84833
84871
  console.log(` Machine ID: ${config3.machineId ?? "(unset)"}`);
84834
- console.log(` Desired state: ${config3.desiredState ?? "(unset)"}`);
84835
84872
  }
84836
84873
  async function setAgentConfig(chatroomId, options, deps) {
84837
84874
  const d = deps ?? await createDefaultDeps9();
@@ -84839,17 +84876,19 @@ async function setAgentConfig(chatroomId, options, deps) {
84839
84876
  const machineId = await requireMachineId(d);
84840
84877
  const machineConfig = await d.machine.loadMachineConfig();
84841
84878
  const workingDir = options.workingDir ?? configuredWorkingDir(machineConfig) ?? process.cwd();
84842
- await d.backend.mutation(api.machines.saveTeamAgentConfig, {
84879
+ await d.backend.mutation(api.machines.sendCommand, {
84843
84880
  sessionId,
84844
- chatroomId,
84845
- role: options.role,
84846
- type: "remote",
84847
84881
  machineId,
84848
- agentHarness: options.harness,
84849
- model: options.model,
84850
- workingDir
84882
+ type: "start-agent",
84883
+ payload: {
84884
+ chatroomId,
84885
+ role: options.role,
84886
+ agentHarness: options.harness,
84887
+ model: options.model,
84888
+ workingDir
84889
+ }
84851
84890
  });
84852
- console.log(`✅ Agent config saved for ${options.role}`);
84891
+ console.log(`✅ Start command sent for ${options.role}`);
84853
84892
  console.log(` Harness: ${options.harness}`);
84854
84893
  console.log(` Model: ${options.model}`);
84855
84894
  console.log(` Working directory: ${workingDir}`);
@@ -89739,6 +89778,7 @@ function toConvexLifecycleFact(fact) {
89739
89778
  return {
89740
89779
  ...normalized,
89741
89780
  chatroomId: normalized.chatroomId,
89781
+ ...normalized.kind === "chatroom_shutdown_complete" ? { commandId: normalized.commandId } : {},
89742
89782
  ...(normalized.kind === "activity" || normalized.kind === "turn_failed") && normalized.taskId ? { taskId: normalized.taskId } : {}
89743
89783
  };
89744
89784
  }
@@ -89749,11 +89789,8 @@ function createAgentLifecycleSend(session2) {
89749
89789
  machineId: session2.machineId,
89750
89790
  fact: toConvexLifecycleFact(fact)
89751
89791
  };
89752
- const result = await session2.backend.mutation(api.machines.projectAgentLifecycleFact, args2);
89753
- return {
89754
- ...result,
89755
- ...result.skipped && result.rejectionReason ? { rejectionReason: result.rejectionReason } : {}
89756
- };
89792
+ const result = await session2.backend.mutation(fact.kind === "activity" ? api.machines.recordAgentActivityHeartbeat : api.machines.projectAgentLifecycleFact, args2);
89793
+ return result;
89757
89794
  };
89758
89795
  }
89759
89796
  var init_agent_lifecycle_send = __esm(() => {
@@ -89776,154 +89813,6 @@ var init_agent_lifecycle_outbox_runtime = __esm(() => {
89776
89813
  init_agent_lifecycle_send();
89777
89814
  });
89778
89815
 
89779
- // src/daemon/entry/enhancer/enhancer-spawn-registry.ts
89780
- function registerEnhancerSpawn(handle) {
89781
- const handles = byChatroom.get(handle.chatroomId) ?? new Set;
89782
- handles.add(handle);
89783
- byChatroom.set(handle.chatroomId, handles);
89784
- }
89785
- function unregisterEnhancerSpawn(handle) {
89786
- const handles = byChatroom.get(handle.chatroomId);
89787
- if (!handles)
89788
- return;
89789
- handles.delete(handle);
89790
- if (handles.size === 0)
89791
- byChatroom.delete(handle.chatroomId);
89792
- }
89793
- async function abortEnhancerSpawnsForChatroom(chatroomId) {
89794
- const handles = byChatroom.get(chatroomId);
89795
- if (!handles)
89796
- return;
89797
- await Promise.all([...handles].map(async (handle) => {
89798
- handle.abort();
89799
- await handle.stopProcess().catch(() => {
89800
- return;
89801
- });
89802
- }));
89803
- }
89804
- var byChatroom;
89805
- var init_enhancer_spawn_registry = __esm(() => {
89806
- byChatroom = new Map;
89807
- });
89808
-
89809
- // src/daemon/entry/finalize-scoped-stop-execution.ts
89810
- var exports_finalize_scoped_stop_execution = {};
89811
- __export(exports_finalize_scoped_stop_execution, {
89812
- finalizeScopedStopExecution: () => finalizeScopedStopExecution
89813
- });
89814
- async function finalizeScopedStopExecution(args2) {
89815
- for (const { target, outcome, lifecycleWarning } of args2.result.targets) {
89816
- await args2.backend.mutation(api.agentStops.reportTargetOutcome, {
89817
- sessionId: args2.sessionId,
89818
- stopCommandId: args2.stopCommandId,
89819
- chatroomId: args2.chatroomId,
89820
- machineId: args2.machineId,
89821
- targetKey: target.targetKey,
89822
- role: target.role,
89823
- pid: target.pid,
89824
- status: "completed",
89825
- outcome: outcome.kind === "stopped" ? "stopped" : "already_stopped",
89826
- termination: outcome.termination,
89827
- lifecycleWarning
89828
- });
89829
- }
89830
- for (const failure of args2.result.failures) {
89831
- await args2.backend.mutation(api.agentStops.reportTargetOutcome, {
89832
- sessionId: args2.sessionId,
89833
- stopCommandId: args2.stopCommandId,
89834
- chatroomId: args2.chatroomId,
89835
- machineId: args2.machineId,
89836
- targetKey: failure.target.targetKey,
89837
- role: failure.target.role,
89838
- pid: failure.target.pid,
89839
- status: "failed",
89840
- outcome: "failed",
89841
- errorMessage: String(failure.error)
89842
- });
89843
- }
89844
- const failed = args2.result.failures.length > 0 || args2.executionError != null;
89845
- await args2.backend.mutation(api.agentStops.completeMachineExecution, {
89846
- sessionId: args2.sessionId,
89847
- stopCommandId: args2.stopCommandId,
89848
- machineId: args2.machineId,
89849
- status: failed ? "failed" : "completed",
89850
- errorMessage: failed ? args2.executionError ? String(args2.executionError) : `${args2.result.failures.length} target(s) failed` : undefined
89851
- });
89852
- }
89853
- var init_finalize_scoped_stop_execution = __esm(() => {
89854
- init_api3();
89855
- });
89856
-
89857
- // src/daemon/entry/execute-scoped-stop-command.ts
89858
- async function executeScopedStopForCommand(args2) {
89859
- args2.apm.markChatroomStopIntent(args2.chatroomId, args2.reason);
89860
- if (args2.scope.kind === "chatroom") {
89861
- await abortEnhancerSpawnsForChatroom(args2.chatroomId);
89862
- }
89863
- const { runExactTargetsStop: runExactTargetsStop2 } = await Promise.resolve().then(() => (init_service_interfaces(), exports_service_interfaces));
89864
- const finalize = await Promise.resolve().then(() => (init_finalize_scoped_stop_execution(), exports_finalize_scoped_stop_execution));
89865
- const begun = await args2.backend.mutation(api.agentStops.beginMachineExecution, {
89866
- sessionId: args2.sessionId,
89867
- stopCommandId: args2.stopCommandId,
89868
- machineId: args2.machineId,
89869
- inboxCommandId: args2.inboxCommandId
89870
- });
89871
- if (!begun.shouldExecute)
89872
- return { stoppedCount: 0, failedCount: 0 };
89873
- for (const target of begun.targets) {
89874
- args2.apm.markStopIntent(args2.chatroomId, target.role, args2.reason, target.pid);
89875
- args2.apm.bindStopTarget?.({
89876
- chatroomId: args2.chatroomId,
89877
- role: target.role,
89878
- pid: target.pid,
89879
- stopCommandId: args2.stopCommandId,
89880
- targetKey: target.targetKey
89881
- });
89882
- }
89883
- let result = { targets: [], failures: [] };
89884
- let executionError;
89885
- try {
89886
- const targetsByRole = new Map;
89887
- for (const target of begun.targets) {
89888
- const roleTargets = targetsByRole.get(target.role) ?? [];
89889
- roleTargets.push(target);
89890
- targetsByRole.set(target.role, roleTargets);
89891
- }
89892
- const roleResults = await Promise.all([...targetsByRole.entries()].map(([role, targets]) => args2.runSerializedForAgent({ chatroomId: args2.chatroomId, role }, { timeoutMs: AGENT_LIFECYCLE_OPERATION_TIMEOUT_MS }, async (_ops, context5) => {
89893
- if (context5.signal.aborted)
89894
- throw context5.signal.reason;
89895
- return runExactTargetsStop2({
89896
- apm: args2.apm,
89897
- confirmedDeps: args2.apm.getConfirmedStopAdapterDeps(),
89898
- stopCommandId: args2.stopCommandId,
89899
- chatroomId: args2.chatroomId,
89900
- targets,
89901
- reason: args2.reason
89902
- });
89903
- })));
89904
- result = {
89905
- targets: roleResults.flatMap((roleResult) => roleResult.targets),
89906
- failures: roleResults.flatMap((roleResult) => roleResult.failures)
89907
- };
89908
- } catch (error) {
89909
- executionError = error;
89910
- console.warn("[daemon] scoped stop execution failed", error);
89911
- } finally {
89912
- await finalize.finalizeScopedStopExecution({ ...args2, result, executionError });
89913
- await args2.apm.syncSlotsAfterScopedStop(result);
89914
- }
89915
- return {
89916
- stoppedCount: result.targets.length,
89917
- failedCount: result.failures.length + (executionError != null ? 1 : 0),
89918
- executionError
89919
- };
89920
- }
89921
- var init_execute_scoped_stop_command = __esm(() => {
89922
- init_reliability();
89923
- init_api3();
89924
- init_enhancer_spawn_registry();
89925
- });
89926
-
89927
89816
  // src/daemon/entry/daemon-services.ts
89928
89817
  function DaemonMutableStateServiceLive(init2) {
89929
89818
  return exports_Layer.effect(DaemonMutableStateService, exports_Effect.gen(function* () {
@@ -89946,70 +89835,7 @@ var AgentLifecycleOutboxService, AgentLifecycleOutboxServiceLive = (registry2, m
89946
89835
  loadEventCursor: (machineId) => exports_Effect.promise(() => ops.loadEventCursor(machineId))
89947
89836
  }), DaemonSpawningService, DaemonSpawningServiceLive = (ops) => exports_Layer.succeed(DaemonSpawningService, {
89948
89837
  shouldAllowSpawn: (chatroomId, reason) => ops.shouldAllowSpawn(chatroomId, reason)
89949
- }), DaemonAgentProcessManagerService, DaemonAgentProcessManagerServiceLive = (mgr, processManagerService, sessionDeps) => exports_Layer.succeed(DaemonAgentProcessManagerService, {
89950
- executeScopedStopForCommand: (args2) => exports_Effect.promise(async () => {
89951
- if (!sessionDeps)
89952
- return { stoppedCount: 0, failedCount: 0 };
89953
- return executeScopedStopForCommand({
89954
- ...sessionDeps,
89955
- apm: mgr,
89956
- runSerializedForAgent: processManagerService.runSerializedForAgent,
89957
- ...args2
89958
- });
89959
- }),
89960
- runInboxRoleScopedStop: (event) => exports_Effect.promise(async () => {
89961
- const { runRoleScopedStop: runRoleScopedStop2 } = await Promise.resolve().then(() => (init_service_interfaces(), exports_service_interfaces));
89962
- const legacyReason = {
89963
- "team.switch": "platform.team_switch",
89964
- dedup: "platform.dedup",
89965
- "stale-config": "platform.dedup"
89966
- };
89967
- const reason = isAgentStopReason(event.reason) ? event.reason : legacyReason[event.reason] ?? "user.stop";
89968
- if (Date.now() > event.deadline)
89969
- return;
89970
- const execute = async (stopAgent, signal) => {
89971
- const result = await runRoleScopedStop2({
89972
- apm: mgr,
89973
- confirmedDeps: mgr.getConfirmedStopAdapterDeps(),
89974
- chatroomId: event.chatroomId,
89975
- role: event.role,
89976
- reason
89977
- });
89978
- if (result.targets.length === 0 && result.failures.length === 0 && event.pid) {
89979
- await stopAgent({
89980
- chatroomId: event.chatroomId,
89981
- role: event.role,
89982
- reason,
89983
- pid: event.pid
89984
- }, signal);
89985
- }
89986
- for (const failure of result.failures)
89987
- console.warn(`[daemon] scoped stop failed for ${failure.target.targetKey}`, failure.error);
89988
- };
89989
- await processManagerService.runSerializedForAgent({ chatroomId: event.chatroomId, role: event.role }, { timeoutMs: SCOPE_TARGET_STOP_TIMEOUT_MS }, async (ops, context5) => {
89990
- if (context5.signal.aborted)
89991
- throw context5.signal.reason;
89992
- await execute(ops.stopAgent, context5.signal);
89993
- });
89994
- }),
89995
- runInboxScopedStop: (event) => exports_Effect.promise(async () => {
89996
- if (!sessionDeps)
89997
- return;
89998
- const inboxCommandId = event.commandId ?? event._id;
89999
- const reason = isAgentStopReason(event.reason) ? event.reason : "user.stop";
90000
- await executeScopedStopForCommand({
90001
- sessionId: sessionDeps.sessionId,
90002
- machineId: sessionDeps.machineId,
90003
- backend: sessionDeps.backend,
90004
- apm: mgr,
90005
- stopCommandId: event.stopCommandId,
90006
- chatroomId: event.chatroomId,
90007
- scope: event.scope,
90008
- reason,
90009
- inboxCommandId,
90010
- runSerializedForAgent: processManagerService.runSerializedForAgent
90011
- });
90012
- }),
89838
+ }), DaemonAgentProcessManagerService, DaemonAgentProcessManagerServiceLive = (mgr, processManagerService) => exports_Layer.succeed(DaemonAgentProcessManagerService, {
90013
89839
  ensureRunning: (opts) => exports_Effect.promise(() => mgr.ensureRunning(opts)),
90014
89840
  stop: (opts) => exports_Effect.promise(() => mgr.stop(opts)),
90015
89841
  handleExit: (opts) => exports_Effect.promise(() => mgr.handleExit(opts)),
@@ -90024,10 +89850,7 @@ var AgentLifecycleOutboxService, AgentLifecycleOutboxServiceLive = (registry2, m
90024
89850
  }), DaemonAgentProcessManagerCommandService, DaemonAgentProcessManagerCommandServiceLive = (service4) => exports_Layer.succeed(DaemonAgentProcessManagerCommandService, service4), DaemonSessionService, DaemonMutableStateService;
90025
89851
  var init_daemon_services = __esm(() => {
90026
89852
  init_esm();
90027
- init_reliability();
90028
89853
  init_agent_lifecycle_outbox_runtime();
90029
- init_execute_scoped_stop_command();
90030
- init_agent();
90031
89854
  init_service_interfaces();
90032
89855
  AgentLifecycleOutboxService = class AgentLifecycleOutboxService extends exports_Context.Tag("AgentLifecycleOutboxService")() {
90033
89856
  };
@@ -90055,11 +89878,7 @@ function daemonSessionToLayers(init2) {
90055
89878
  return exports_Layer.mergeAll(BackendServiceLive({
90056
89879
  mutation: (e, a) => init2.backend.mutation(e, a),
90057
89880
  query: (e, a) => init2.backend.query(e, a)
90058
- }), ProcessServiceLive, ClockServiceLive, FsServiceLive, DaemonMachineServiceLive(init2.machine), AgentLifecycleOutboxServiceLive(init2.agentLifecycleOutbox, init2.machineId), DaemonSpawningServiceLive(init2.spawning), DaemonAgentProcessManagerServiceLive(init2.agentProcessManager, init2.agentProcessManagerService, {
90059
- sessionId: init2.sessionId,
90060
- machineId: init2.machineId,
90061
- backend: init2.backend
90062
- }), DaemonAgentProcessManagerCommandServiceLive(init2.agentProcessManagerService), DaemonMutableStateServiceLive({
89881
+ }), ProcessServiceLive, ClockServiceLive, FsServiceLive, DaemonMachineServiceLive(init2.machine), AgentLifecycleOutboxServiceLive(init2.agentLifecycleOutbox, init2.machineId), DaemonSpawningServiceLive(init2.spawning), DaemonAgentProcessManagerServiceLive(init2.agentProcessManager, init2.agentProcessManagerService), DaemonAgentProcessManagerCommandServiceLive(init2.agentProcessManagerService), DaemonMutableStateServiceLive({
90063
89882
  lastPushedGitState: init2.lastPushedGitState,
90064
89883
  lastPushedModels: init2.lastPushedModels,
90065
89884
  lastPushedHarnessFingerprint: init2.lastPushedHarnessFingerprint,
@@ -90080,12 +89899,16 @@ function daemonSessionToLayers(init2) {
90080
89899
  lastPushedModels: init2.lastPushedModels,
90081
89900
  lastPushedHarnessFingerprint: init2.lastPushedHarnessFingerprint,
90082
89901
  logEvent: init2.logEvent,
90083
- taskService: init2.taskService
89902
+ taskService: init2.taskService,
89903
+ lifecycleOutbox: {
89904
+ enqueue: (fact) => enqueueAgentLifecycleFact(init2.agentLifecycleOutbox, init2.machineId, fact)
89905
+ }
90084
89906
  }));
90085
89907
  }
90086
89908
  var init_daemon_layers = __esm(() => {
90087
89909
  init_esm();
90088
89910
  init_daemon_services();
89911
+ init_agent_lifecycle_outbox_runtime();
90089
89912
  init_backend();
90090
89913
  init_clock2();
90091
89914
  init_fs();
@@ -90978,10 +90801,9 @@ Run: ${formatAuthLoginCommand(convexUrl)}`);
90978
90801
  yield* connectDaemonEffect(client4, typedSessionId, machineId);
90979
90802
  return { typedSessionId, config: config3, machineId, agentServices, cachedModels };
90980
90803
  }), activeLogSink, activeLogEvent, connectDaemonEffect = (client4, sessionId, machineId) => exports_Effect.tryPromise({
90981
- try: () => client4.mutation(api.machines.updateDaemonStatus, {
90804
+ try: () => client4.mutation(api.machines.markDaemonOnline, {
90982
90805
  sessionId: asConvexSessionId(sessionId),
90983
- machineId,
90984
- connected: true
90806
+ machineId
90985
90807
  }),
90986
90808
  catch: (e) => e
90987
90809
  }).pipe(exports_Effect.catchAll((error) => {
@@ -94519,8 +94341,6 @@ var init_command_event_types = __esm(() => {
94519
94341
  DAEMON_COMMAND_EVENT_TYPES = [
94520
94342
  "agent.requestStart",
94521
94343
  "agent.restart",
94522
- "agent.requestStop",
94523
- "agent.stopScope",
94524
94344
  "daemon.ping",
94525
94345
  "daemon.gitRefresh",
94526
94346
  "daemon.workspaceListChanged",
@@ -96193,8 +96013,7 @@ async function restartAgent(deps, input) {
96193
96013
  model: input.model,
96194
96014
  workingDir: input.workingDir,
96195
96015
  correlationId: input.correlationId,
96196
- wantResume: input.wantResume,
96197
- ...input.lifecycleRevision !== undefined ? { lifecycleRevision: input.lifecycleRevision } : {}
96016
+ wantResume: input.wantResume
96198
96017
  });
96199
96018
  } catch (error) {
96200
96019
  log4(`[daemon] agent.restart failed (correlationId=${input.correlationId}) for role=${input.role}: ${error instanceof Error ? error.message : String(error)}`);
@@ -96360,13 +96179,16 @@ function createStartAgentDeps(session2, processManagerService) {
96360
96179
  machineId: session2.machineId,
96361
96180
  error: args2.error
96362
96181
  });
96363
- await session2.backend.mutation(api.daemon.agentEvents.agentStartFailed, {
96364
- sessionId: session2.sessionId,
96365
- machineId: session2.machineId,
96366
- chatroomId: args2.chatroomId,
96367
- role: args2.role,
96368
- error: args2.error
96369
- });
96182
+ if (session2.lifecycleOutbox) {
96183
+ await session2.lifecycleOutbox.enqueue(buildAgentStatusFact({
96184
+ chatroomId: args2.chatroomId,
96185
+ role: args2.role,
96186
+ status: "error",
96187
+ errorSource: "configuration",
96188
+ errorCode: "agent.startFailed",
96189
+ errorMessage: args2.error
96190
+ }));
96191
+ }
96370
96192
  } catch (err) {
96371
96193
  console.log(` ⚠️ Failed to emit startFailed event: ${err.message}`);
96372
96194
  }
@@ -96434,8 +96256,7 @@ var onRequestRestartAgentEffect = (event, nativeDelivery) => exports_Effect.gen(
96434
96256
  workingDir: event.workingDir,
96435
96257
  correlationId: event.correlationId,
96436
96258
  deadline: event.deadline,
96437
- wantResume: event.wantResume,
96438
- lifecycleRevision: event.lifecycleRevision
96259
+ wantResume: event.wantResume
96439
96260
  }));
96440
96261
  });
96441
96262
  var init_on_request_restart_agent = __esm(() => {
@@ -96461,8 +96282,7 @@ async function startAgent2(deps, input) {
96461
96282
  model: input.model,
96462
96283
  workingDir: input.workingDir,
96463
96284
  reason: input.reason,
96464
- wantResume: input.wantResume,
96465
- ...input.lifecycleRevision !== undefined ? { lifecycleRevision: input.lifecycleRevision } : {}
96285
+ wantResume: input.wantResume
96466
96286
  });
96467
96287
  } catch (error) {
96468
96288
  const message = error instanceof Error ? error.message : typeof error === "object" && error !== null && ("error" in error) ? String(error.error) : "unknown";
@@ -96508,8 +96328,7 @@ var onRequestStartAgentEffect = (event) => exports_Effect.gen(function* () {
96508
96328
  workingDir: event.workingDir,
96509
96329
  reason: event.reason,
96510
96330
  deadline: event.deadline,
96511
- wantResume: event.wantResume,
96512
- lifecycleRevision: event.lifecycleRevision
96331
+ wantResume: event.wantResume
96513
96332
  });
96514
96333
  await drainPendingEnhancerJobsIfRegistered();
96515
96334
  });
@@ -96520,31 +96339,6 @@ var init_on_request_start_agent = __esm(() => {
96520
96339
  init_daemon_services();
96521
96340
  });
96522
96341
 
96523
- // src/daemon/entry/events/agent/on-request-stop-agent.ts
96524
- var onRequestStopAgentEffect = (event) => exports_Effect.gen(function* () {
96525
- const agentMgr = yield* DaemonAgentProcessManagerService;
96526
- if (Date.now() > event.deadline)
96527
- return;
96528
- if (agentMgr.runInboxRoleScopedStop)
96529
- yield* agentMgr.runInboxRoleScopedStop(event);
96530
- });
96531
- var init_on_request_stop_agent = __esm(() => {
96532
- init_esm();
96533
- init_daemon_services();
96534
- });
96535
-
96536
- // src/daemon/entry/events/agent/on-stop-scope-agent.ts
96537
- var onStopScopeAgentEffect = (event) => exports_Effect.gen(function* () {
96538
- const agentMgr = yield* DaemonAgentProcessManagerService;
96539
- const stop4 = agentMgr.runInboxScopedStop;
96540
- if (stop4)
96541
- yield* stop4(event);
96542
- });
96543
- var init_on_stop_scope_agent = __esm(() => {
96544
- init_esm();
96545
- init_daemon_services();
96546
- });
96547
-
96548
96342
  // src/daemon/entry/handlers/ping.ts
96549
96343
  function handlePing() {
96550
96344
  console.log(` ↪ Responding: pong`);
@@ -96722,15 +96516,6 @@ function handleRequestRestartEffect(event, tracker, nativeDelivery) {
96722
96516
  tracker.commandIds.set(eventId, Date.now());
96723
96517
  });
96724
96518
  }
96725
- function handleRequestStopEffect(event, tracker) {
96726
- return exports_Effect.gen(function* () {
96727
- const eventId = event._id.toString();
96728
- if (tracker.commandIds.has(eventId))
96729
- return;
96730
- yield* onRequestStopAgentEffect(event);
96731
- tracker.commandIds.set(eventId, Date.now());
96732
- });
96733
- }
96734
96519
  function handlePingCommandEffect(event, tracker) {
96735
96520
  return exports_Effect.gen(function* () {
96736
96521
  const eventId = event._id.toString();
@@ -96884,8 +96669,6 @@ var init_command_dispatch = __esm(() => {
96884
96669
  init_pick_folder();
96885
96670
  init_on_request_restart_agent();
96886
96671
  init_on_request_start_agent();
96887
- init_on_request_stop_agent();
96888
- init_on_stop_scope_agent();
96889
96672
  init_ping();
96890
96673
  init_manager();
96891
96674
  init_convex_error();
@@ -96893,14 +96676,6 @@ var init_command_dispatch = __esm(() => {
96893
96676
  commandEventHandlers = {
96894
96677
  "agent.requestStart": handleRequestStartEffect,
96895
96678
  "agent.restart": handleRequestRestartEffect,
96896
- "agent.requestStop": handleRequestStopEffect,
96897
- "agent.stopScope": (event, tracker) => exports_Effect.gen(function* () {
96898
- const eventId = String(event.commandId ?? event._id);
96899
- if (tracker.commandIds.has(eventId))
96900
- return;
96901
- yield* onStopScopeAgentEffect(event);
96902
- tracker.commandIds.set(eventId, Date.now());
96903
- }),
96904
96679
  "daemon.ping": handlePingCommandEffect,
96905
96680
  "daemon.gitRefresh": handleGitRefreshCommandEffect,
96906
96681
  "daemon.workspaceListChanged": handleWorkspaceListChangedCommandEffect,
@@ -96956,6 +96731,36 @@ function createEnhancerLogWriter(logSink, context5, clock4 = () => Date.now()) {
96956
96731
  var ENHANCER_LOG_PREFIX = "[enhancer]";
96957
96732
  var init_enhancer_log = () => {};
96958
96733
 
96734
+ // src/daemon/entry/enhancer/enhancer-spawn-registry.ts
96735
+ function registerEnhancerSpawn(handle) {
96736
+ const handles = byChatroom.get(handle.chatroomId) ?? new Set;
96737
+ handles.add(handle);
96738
+ byChatroom.set(handle.chatroomId, handles);
96739
+ }
96740
+ function unregisterEnhancerSpawn(handle) {
96741
+ const handles = byChatroom.get(handle.chatroomId);
96742
+ if (!handles)
96743
+ return;
96744
+ handles.delete(handle);
96745
+ if (handles.size === 0)
96746
+ byChatroom.delete(handle.chatroomId);
96747
+ }
96748
+ async function abortEnhancerSpawnsForChatroom(chatroomId) {
96749
+ const handles = byChatroom.get(chatroomId);
96750
+ if (!handles)
96751
+ return;
96752
+ await Promise.all([...handles].map(async (handle) => {
96753
+ handle.abort();
96754
+ await handle.stopProcess().catch(() => {
96755
+ return;
96756
+ });
96757
+ }));
96758
+ }
96759
+ var byChatroom;
96760
+ var init_enhancer_spawn_registry = __esm(() => {
96761
+ byChatroom = new Map;
96762
+ });
96763
+
96959
96764
  // src/daemon/entry/enhancer/job-outcome-subscription.ts
96960
96765
  function subscribeToEnhancerJobOutcome(args2) {
96961
96766
  let currentState = null;
@@ -103126,7 +102931,7 @@ var init_command_run_subscription = __esm(() => {
103126
102931
  });
103127
102932
 
103128
102933
  // src/daemon/entry/task-inbox-runtime.ts
103129
- var startTaskInboxEffect = () => exports_Effect.gen(function* () {
102934
+ var startTaskInboxEffect = (wsClient2) => exports_Effect.gen(function* () {
103130
102935
  const session2 = yield* DaemonSessionService;
103131
102936
  const agentMgr = yield* DaemonAgentProcessManagerService;
103132
102937
  const commandService = yield* DaemonAgentProcessManagerCommandService;
@@ -103154,12 +102959,12 @@ var startTaskInboxEffect = () => exports_Effect.gen(function* () {
103154
102959
  runSerializedForAgent: commandService.runSerializedForAgent,
103155
102960
  sessionDeps,
103156
102961
  machineId: session2.machineId,
103157
- taskSnapshotState: session2.taskService.taskSnapshotState,
102962
+ taskInboxState: session2.taskService.taskInboxState,
103158
102963
  agentTaskState,
103159
102964
  lifecycleOutbox,
103160
102965
  taskService: session2.taskService
103161
102966
  });
103162
- yield* exports_Effect.tryPromise(() => session2.taskService.startTaskInbox()).pipe(exports_Effect.catchAll((error) => {
102967
+ yield* exports_Effect.tryPromise(() => session2.taskService.startTaskInbox(wsClient2)).pipe(exports_Effect.catchAll((error) => {
103163
102968
  console.warn("[TaskService] task inbox bootstrap failed:", error);
103164
102969
  return exports_Effect.void;
103165
102970
  }));
@@ -103532,10 +103337,9 @@ var init_on_daemon_shutdown = __esm(() => {
103532
103337
  console.log(`[${formatTimestamp()}] Shutdown stops: ${totalStopped} stopped`);
103533
103338
  }
103534
103339
  }
103535
- yield* exports_Effect.promise(() => session2.backend.mutation(api.machines.updateDaemonStatus, {
103340
+ yield* exports_Effect.promise(() => session2.backend.mutation(api.machines.markDaemonOffline, {
103536
103341
  sessionId: session2.sessionId,
103537
- machineId: session2.machineId,
103538
- connected: false
103342
+ machineId: session2.machineId
103539
103343
  }).catch(() => {}));
103540
103344
  });
103541
103345
  });
@@ -103688,7 +103492,7 @@ function createDaemonRuntime(deps) {
103688
103492
  break;
103689
103493
  }
103690
103494
  });
103691
- taskInboxHandle = yield* startTaskInboxEffect();
103495
+ taskInboxHandle = yield* startTaskInboxEffect(deps.wsClient);
103692
103496
  logObserverSubscriptionHandle = startLogObserverSubscription({ sessionId: session2.sessionId, machineId: session2.machineId }, deps.wsClient);
103693
103497
  const commandRunRuntime = yield* exports_Effect.runtime();
103694
103498
  agenticQueryWorkerHandle = startAgenticQuerySubscriptions({
@@ -104151,6 +103955,48 @@ var init_deps = __esm(() => {
104151
103955
  init_publisher_registry();
104152
103956
  });
104153
103957
 
103958
+ // src/daemon/entry/execute-chatroom-stop-command.ts
103959
+ async function executeChatroomStopCommand(args2) {
103960
+ if (args2.role)
103961
+ args2.apm.markStopIntent(args2.chatroomId, args2.role, "user.stop");
103962
+ else {
103963
+ args2.apm.markChatroomStopIntent(args2.chatroomId, "user.stop");
103964
+ await abortEnhancerSpawnsForChatroom(args2.chatroomId);
103965
+ }
103966
+ const confirmedDeps = args2.apm.getConfirmedStopAdapterDeps();
103967
+ const targets = await args2.apm.discoverStopTargets(args2.chatroomId);
103968
+ const roles = [
103969
+ ...new Set(targets.filter((target) => !args2.role || target.role.toLowerCase() === args2.role.toLowerCase()).map((target) => target.role.toLowerCase()))
103970
+ ];
103971
+ const results = await Promise.allSettled(roles.map((role) => args2.runSerializedForAgent({ chatroomId: args2.chatroomId, role }, { timeoutMs: AGENT_LIFECYCLE_OPERATION_TIMEOUT_MS }, async (_ops, context5) => {
103972
+ if (context5.signal.aborted)
103973
+ throw context5.signal.reason;
103974
+ await runRoleScopedStop({
103975
+ apm: args2.apm,
103976
+ confirmedDeps,
103977
+ chatroomId: args2.chatroomId,
103978
+ role,
103979
+ reason: "user.stop"
103980
+ });
103981
+ })));
103982
+ for (const result of results)
103983
+ if (result.status === "rejected")
103984
+ console.warn("[daemon] chatroom stop attempt failed", result.reason);
103985
+ await confirmedDeps.lifecycleOutbox.enqueue({
103986
+ kind: "chatroom_shutdown_complete",
103987
+ chatroomId: args2.chatroomId,
103988
+ commandId: args2.commandId,
103989
+ ...args2.finalizeChatroom === undefined ? {} : { finalizeChatroom: args2.finalizeChatroom },
103990
+ revisionKey: `shutdown:${args2.commandId}`,
103991
+ emittedAt: Date.now()
103992
+ });
103993
+ }
103994
+ var init_execute_chatroom_stop_command = __esm(() => {
103995
+ init_reliability();
103996
+ init_enhancer_spawn_registry();
103997
+ init_agent_process_service2();
103998
+ });
103999
+
104154
104000
  // src/daemon/entry/persistence-path.ts
104155
104001
  import { homedir as homedir10 } from "node:os";
104156
104002
  import { join as join26 } from "node:path";
@@ -104677,6 +104523,10 @@ function startAllSubscribers(deps) {
104677
104523
  const enhancerJob = startEnhancerJobSubscriber2(deps, onEvent);
104678
104524
  const gitRequest = startGitRequestSubscriber(deps, onEvent);
104679
104525
  const machineCommands = startMachineCommandInboxSubscriber(deps, async (claimed) => {
104526
+ if (claimed.type === "agent.stop") {
104527
+ await deps.onAgentStopCommand?.(claimed);
104528
+ return;
104529
+ }
104680
104530
  if (claimed.type === "daemon.workspaceListChanged") {
104681
104531
  enhancerJob.refreshChatrooms();
104682
104532
  gitRequest.refreshWorkspaces();
@@ -133877,7 +133727,18 @@ async function startDaemon() {
133877
133727
  wsClient: wsClient2,
133878
133728
  sessionId: asConvexSessionId(init2.sessionId),
133879
133729
  machineId: init2.machineId,
133880
- router: createDefaultEventRouterDeps()
133730
+ router: createDefaultEventRouterDeps(),
133731
+ onAgentStopCommand: (command) => {
133732
+ const stopCommand = command;
133733
+ return executeChatroomStopCommand({
133734
+ apm: init2.agentProcessManager,
133735
+ chatroomId: stopCommand.chatroomId,
133736
+ commandId: stopCommand.commandId,
133737
+ role: stopCommand.role,
133738
+ finalizeChatroom: stopCommand.finalizeChatroom,
133739
+ runSerializedForAgent: init2.agentProcessManagerService.runSerializedForAgent
133740
+ });
133741
+ }
133881
133742
  });
133882
133743
  console.log(`[daemon] Local web UI: http://127.0.0.1:${localWeb.port}/`);
133883
133744
  const layers = daemonSessionToLayers(init2);
@@ -133906,6 +133767,7 @@ var init_start_daemon = __esm(() => {
133906
133767
  init_daemon_runtime();
133907
133768
  init_default_router_deps();
133908
133769
  init_deps();
133770
+ init_execute_chatroom_stop_command();
133909
133771
  init_init_daemon();
133910
133772
  init_persistence_path();
133911
133773
  init_resolve_local_web_port();
@@ -134679,7 +134541,7 @@ agentConfigCommand.command("get").description("Show agent configuration for a ro
134679
134541
  const { getAgentConfig: getAgentConfig2 } = await Promise.resolve().then(() => (init_agent2(), exports_agent));
134680
134542
  await getAgentConfig2(options.chatroomId, options.role);
134681
134543
  });
134682
- agentConfigCommand.command("set").description("Save agent harness and model configuration").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Agent role").requiredOption("--harness <harness>", "Agent harness").requiredOption("--model <model>", "Model identifier").option("--working-dir <dir>", "Agent working directory").action(async (options) => {
134544
+ agentConfigCommand.command("set").description("Send a one-time agent start request with the harness and model").requiredOption("--chatroom-id <id>", "Chatroom identifier").requiredOption("--role <role>", "Agent role").requiredOption("--harness <harness>", "Agent harness").requiredOption("--model <model>", "Model identifier").option("--working-dir <dir>", "Agent working directory").action(async (options) => {
134683
134545
  await maybeRequireAuth();
134684
134546
  const { setAgentConfig: setAgentConfig2 } = await Promise.resolve().then(() => (init_agent2(), exports_agent));
134685
134547
  await setAgentConfig2(options.chatroomId, options);
@@ -135146,4 +135008,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
135146
135008
  });
135147
135009
  program2.parse();
135148
135010
 
135149
- //# debugId=7D04CA8EEEBFA97564756E2164756E21
135011
+ //# debugId=D0F3FF58E9CFA79B64756E2164756E21