chatroom-cli 1.117.6 → 1.118.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
@@ -26981,6 +26981,16 @@ var init_backend = __esm(() => {
26981
26981
  };
26982
26982
  });
26983
26983
 
26984
+ // src/infrastructure/services/cli-gateway.ts
26985
+ var CliGatewayService, CliGatewayServiceLive = (gateway) => exports_Layer.succeed(CliGatewayService, {
26986
+ handoff: (args2) => gateway.handoff(args2)
26987
+ });
26988
+ var init_cli_gateway = __esm(() => {
26989
+ init_esm();
26990
+ CliGatewayService = class CliGatewayService extends exports_Context.Tag("CliGatewayService")() {
26991
+ };
26992
+ });
26993
+
26984
26994
  // src/infrastructure/services/session.ts
26985
26995
  var SessionService, SessionServiceLive = (ops) => exports_Layer.succeed(SessionService, {
26986
26996
  getSessionId: () => exports_Effect.promise(() => ops.getSessionId()),
@@ -26995,7 +27005,12 @@ var init_session = __esm(() => {
26995
27005
 
26996
27006
  // src/infrastructure/services/command-effect-helpers.ts
26997
27007
  function commandServicesLayerFromDeps(deps) {
26998
- return exports_Layer.mergeAll(BackendServiceLive({
27008
+ const gateway = deps.gateway ?? {
27009
+ handoff: async () => {
27010
+ throw new Error("CLI gateway is unavailable");
27011
+ }
27012
+ };
27013
+ return exports_Layer.mergeAll(CliGatewayServiceLive(gateway), BackendServiceLive({
26999
27014
  query: deps.backend.query,
27000
27015
  mutation: deps.backend.mutation,
27001
27016
  ...deps.backend.action ? { action: deps.backend.action } : {}
@@ -27017,6 +27032,17 @@ function requireSessionIdEffect(fail11) {
27017
27032
  return sessionId;
27018
27033
  });
27019
27034
  }
27035
+ function requireSessionForChatroomEffect(params) {
27036
+ return exports_Effect.gen(function* () {
27037
+ const sessionId = yield* requireSessionIdEffect(({ convexUrl, otherUrls }) => ({
27038
+ _tag: "NotAuthenticated",
27039
+ convexUrl,
27040
+ otherUrls
27041
+ }));
27042
+ yield* validateChatroomIdEffect(params.chatroomId, (id3) => ({ _tag: "InvalidChatroomId", id: id3 }));
27043
+ return sessionId;
27044
+ });
27045
+ }
27020
27046
  function validateChatroomIdEffect(chatroomId, fail11) {
27021
27047
  const valid = typeof chatroomId === "string" && chatroomId.length >= 20 && chatroomId.length <= 40;
27022
27048
  return valid ? exports_Effect.void : exports_Effect.fail(fail11(chatroomId));
@@ -27024,6 +27050,7 @@ function validateChatroomIdEffect(chatroomId, fail11) {
27024
27050
  var init_command_effect_helpers = __esm(() => {
27025
27051
  init_esm();
27026
27052
  init_backend();
27053
+ init_cli_gateway();
27027
27054
  init_session();
27028
27055
  });
27029
27056
 
@@ -27081,6 +27108,7 @@ var init_process = __esm(() => {
27081
27108
  // src/infrastructure/services/index.ts
27082
27109
  var init_services = __esm(() => {
27083
27110
  init_backend();
27111
+ init_cli_gateway();
27084
27112
  init_command_effect_helpers();
27085
27113
  init_clock2();
27086
27114
  init_fs();
@@ -28334,7 +28362,8 @@ function createAgentProcessManagerService(deps) {
28334
28362
  resumeTurnForSlot: (input) => deps.execution.resumeTurnForSlot(input),
28335
28363
  subscribeAgentTurnEnded: (handler) => deps.execution.subscribeAgentTurnEnded(handler),
28336
28364
  subscribeAgentStarted: (handler) => deps.execution.subscribeAgentStarted(handler),
28337
- subscribeAgentSessionLost: (handler) => deps.execution.subscribeAgentSessionLost(handler)
28365
+ subscribeAgentSessionLost: (handler) => deps.execution.subscribeAgentSessionLost(handler),
28366
+ subscribeAgentTurnProgress: (handler) => deps.execution.subscribeAgentTurnProgress(handler)
28338
28367
  };
28339
28368
  }
28340
28369
  var NATIVE_DELIVERY_SLOT_WAIT_MS = 120000, SLOT_CHANGE_POLL_MS = 100;
@@ -44651,29 +44680,24 @@ async function emitNativeWaitingAfterSpawn(ctx, harness, opts) {
44651
44680
  return false;
44652
44681
  }
44653
44682
  }
44654
- function fireTokenActivity(backend2, sessionId, chatroomId, role, now, lastReportedTokenAt, throttleMs) {
44683
+ function fireTokenActivity(onTurnProgress, chatroomId, role, now, lastReportedTokenAt, throttleMs) {
44655
44684
  const t = now();
44656
44685
  if (lastReportedTokenAt.value === 0 || t - lastReportedTokenAt.value >= throttleMs) {
44657
44686
  lastReportedTokenAt.value = t;
44658
- backend2.mutation(api.participants.recordHarnessActivity, {
44659
- sessionId,
44660
- chatroomId,
44661
- role
44662
- }).catch(() => {});
44687
+ onTurnProgress({ chatroomId, role });
44663
44688
  }
44664
44689
  }
44665
44690
  function wireTokenActivityReporting(opts) {
44666
44691
  if (!isTeamAgentRole(opts.role))
44667
44692
  return;
44693
+ const onTurnProgress = opts.onTurnProgress;
44694
+ if (!onTurnProgress)
44695
+ return;
44668
44696
  if (opts.activityEmitter) {
44669
44697
  opts.activityEmitter.onActivity((signal) => {
44670
44698
  if (signal.kind !== "progress" || !signal.isFirstForTurn)
44671
44699
  return;
44672
- opts.backend.mutation(api.participants.recordHarnessActivity, {
44673
- sessionId: opts.sessionId,
44674
- chatroomId: opts.chatroomId,
44675
- role: opts.role
44676
- }).catch(() => {});
44700
+ onTurnProgress({ chatroomId: opts.chatroomId, role: opts.role });
44677
44701
  });
44678
44702
  return;
44679
44703
  }
@@ -44684,13 +44708,12 @@ function wireTokenActivityReporting(opts) {
44684
44708
  if (!register)
44685
44709
  return;
44686
44710
  register(() => {
44687
- fireTokenActivity(opts.backend, opts.sessionId, opts.chatroomId, opts.role, now, lastReportedTokenAt, throttleMs);
44711
+ fireTokenActivity(onTurnProgress, opts.chatroomId, opts.role, now, lastReportedTokenAt, throttleMs);
44688
44712
  });
44689
44713
  }
44690
44714
  var NATIVE_TOKEN_ACTIVITY_THROTTLE_MS = 30000;
44691
44715
  var init_native_spawn_presence = __esm(() => {
44692
44716
  init_types();
44693
- init_api3();
44694
44717
  init_agent_lifecycle_fact();
44695
44718
  init_execution_kind();
44696
44719
  });
@@ -44713,6 +44736,7 @@ class AgentProcessManager {
44713
44736
  exitRetryQueue = [];
44714
44737
  exitRetryTimer = null;
44715
44738
  agentTurnEndedHandlers = new Set;
44739
+ agentTurnProgressHandlers = new Set;
44716
44740
  agentTurnEndedSequence = 0;
44717
44741
  agentStartedHandlers = new Set;
44718
44742
  agentSessionLostHandlers = new Set;
@@ -44800,6 +44824,19 @@ class AgentProcessManager {
44800
44824
  this.agentSessionLostHandlers.add(handler);
44801
44825
  return () => this.agentSessionLostHandlers.delete(handler);
44802
44826
  }
44827
+ subscribeAgentTurnProgress(handler) {
44828
+ this.agentTurnProgressHandlers.add(handler);
44829
+ return () => this.agentTurnProgressHandlers.delete(handler);
44830
+ }
44831
+ emitAgentTurnProgress(event) {
44832
+ for (const handler of this.agentTurnProgressHandlers) {
44833
+ try {
44834
+ handler(event);
44835
+ } catch (error51) {
44836
+ console.warn(`[AgentProcessManager] turn-progress handler failed for ${event.role}@${event.chatroomId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
44837
+ }
44838
+ }
44839
+ }
44803
44840
  clearSlotRuntimeState(slot) {
44804
44841
  slot.state = AGENT_SLOT_STATE.IDLE;
44805
44842
  slot.pid = undefined;
@@ -45719,13 +45756,12 @@ class AgentProcessManager {
45719
45756
  });
45720
45757
  }
45721
45758
  wireTokenActivityReporting({
45722
- backend: this.deps.backend,
45723
- sessionId: this.deps.sessionId,
45724
45759
  chatroomId: opts.chatroomId,
45725
45760
  role: opts.role,
45726
45761
  spawnResult,
45727
45762
  now: () => this.deps.clock.now(),
45728
- activityEmitter: spawnResult.activityEmitter
45763
+ activityEmitter: spawnResult.activityEmitter,
45764
+ onTurnProgress: (event) => this.emitAgentTurnProgress(event)
45729
45765
  });
45730
45766
  }
45731
45767
  async finalizeRunningSlot(key, slot, opts, spawnResult, wantResume) {
@@ -45742,7 +45778,7 @@ class AgentProcessManager {
45742
45778
  await this.emitNativeWaiting(opts.chatroomId, opts.role, opts.agentHarness);
45743
45779
  if (getHarnessCapabilities(opts.agentHarness).supportsNativeIntegration) {
45744
45780
  for (const handler of this.agentStartedHandlers) {
45745
- handler({ chatroomId: opts.chatroomId, role: opts.role }).catch((error51) => {
45781
+ handler({ chatroomId: opts.chatroomId, role: opts.role, reason: opts.reason }).catch((error51) => {
45746
45782
  console.warn(`[AgentProcessManager] native delivery after start failed for ${opts.role}@${opts.chatroomId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
45747
45783
  });
45748
45784
  }
@@ -46941,6 +46977,8 @@ function createConvexNativeTaskDeliveryGateway(backend2) {
46941
46977
  deliveryKind: "native_inject"
46942
46978
  });
46943
46979
  },
46980
+ readTask: async (args2) => await backend2.mutation(api.tasks.readTask, args2),
46981
+ markReceiptStarted: async (args2) => await backend2.mutation(api.taskDeliveryReceipts.markStarted, args2),
46944
46982
  joinWaitingParticipant: async (args2) => {
46945
46983
  await backend2.mutation(api.participants.join, {
46946
46984
  ...args2,
@@ -46987,6 +47025,7 @@ var init_convex_native_task_delivery_gateway = __esm(() => {
46987
47025
  // src/daemon/services/task-service/service/task-service.ts
46988
47026
  function createTaskService(deps) {
46989
47027
  const gateway = createConvexNativeTaskDeliveryGateway(deps.backend);
47028
+ const handoffRepository = deps.handoffRepository;
46990
47029
  const taskInboxState = new TaskInboxState;
46991
47030
  const listeners = new Set;
46992
47031
  let stopInboxWatch;
@@ -46996,6 +47035,120 @@ function createTaskService(deps) {
46996
47035
  const scheduledEventIds = new Set;
46997
47036
  const deliveredEventIds = new Set;
46998
47037
  const taskChains = new Map;
47038
+ const maxBootstrapSweepFailures = 3;
47039
+ let bootstrapSweepPending = true;
47040
+ let bootstrapSweepFailures = 0;
47041
+ let bootstrapSweepInFlight = false;
47042
+ let bootstrapSweepPromise;
47043
+ const REDIVERY_ATTEMPT_CAP = 3;
47044
+ const turnEndAttemptCounts = new Map;
47045
+ const redeliveryExhaustedKeys = new Set;
47046
+ const redeliveryKey = (chatroomId, role, taskId) => `${chatroomId}:${role.toLowerCase()}:${taskId}`;
47047
+ const recordUncoveredTurnEnd = async (args2) => {
47048
+ const key = redeliveryKey(args2.chatroomId, args2.role, args2.taskId);
47049
+ if (redeliveryExhaustedKeys.has(key))
47050
+ return { exceeded: true };
47051
+ const attempts = (turnEndAttemptCounts.get(key) ?? 0) + 1;
47052
+ turnEndAttemptCounts.set(key, attempts);
47053
+ if (attempts < REDIVERY_ATTEMPT_CAP)
47054
+ return { exceeded: false };
47055
+ redeliveryExhaustedKeys.add(key);
47056
+ try {
47057
+ await gateway.recordDeliveryFailure({
47058
+ sessionId: deps.sessionId,
47059
+ machineId: deps.machineId,
47060
+ taskId: args2.taskId,
47061
+ reason: "redelivery_exhausted"
47062
+ });
47063
+ } catch (error51) {
47064
+ console.warn(`[TaskService] redelivery-exhausted failure record failed chatroom=${args2.chatroomId} role=${args2.role} task=${args2.taskId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
47065
+ }
47066
+ console.log(`[NativeDelivery:redelivery-exhausted] chatroom=${args2.chatroomId} role=${args2.role} task=${args2.taskId} attempts=${attempts} — release/redelivery stopped; agent restart (user.start) resets`);
47067
+ return { exceeded: true };
47068
+ };
47069
+ const isRedeliveryExhausted = (args2) => redeliveryExhaustedKeys.has(redeliveryKey(args2.chatroomId, args2.role, args2.taskId));
47070
+ const clearRedeliveryTrackingForTask = (chatroomId, role, taskId) => {
47071
+ const key = redeliveryKey(chatroomId, role, taskId);
47072
+ turnEndAttemptCounts.delete(key);
47073
+ redeliveryExhaustedKeys.delete(key);
47074
+ };
47075
+ const sweepUncoveredInProgressTasks = async () => {
47076
+ let released = 0;
47077
+ let failures3 = 0;
47078
+ for (const task of taskInboxState.listAll()) {
47079
+ if (task.status !== "in_progress")
47080
+ continue;
47081
+ try {
47082
+ const handoff = await handoffRepository.getLatest(task.chatroomId, task.agentConfig.role);
47083
+ if (handoff?.taskIds.includes(task.taskId))
47084
+ continue;
47085
+ const result = await gateway.releaseTaskAfterTurnFailure({
47086
+ sessionId: deps.sessionId,
47087
+ chatroomId: task.chatroomId,
47088
+ role: task.agentConfig.role,
47089
+ taskId: task.taskId
47090
+ });
47091
+ taskInboxState.markStatus(task.chatroomId, task.agentConfig.role, task.taskId, result.status, result.updatedAt);
47092
+ released += 1;
47093
+ } catch (error51) {
47094
+ failures3 += 1;
47095
+ console.warn(`[TaskService] bootstrap task release failed chatroom=${task.chatroomId} task=${task.taskId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
47096
+ }
47097
+ }
47098
+ if (failures3 > 0)
47099
+ throw new Error(`${failures3} bootstrap task release(s) failed`);
47100
+ return released;
47101
+ };
47102
+ const releaseTask = async (args2) => {
47103
+ const result = await gateway.releaseTaskAfterTurnFailure({
47104
+ sessionId: deps.sessionId,
47105
+ chatroomId: args2.chatroomId,
47106
+ role: args2.role,
47107
+ taskId: args2.taskId
47108
+ });
47109
+ taskInboxState.markStatus(args2.chatroomId, args2.role, args2.taskId, result.status, result.updatedAt);
47110
+ const currentTask = taskInboxState.getForRole(args2.chatroomId, args2.role, args2.taskId);
47111
+ if (currentTask?.status === "pending") {
47112
+ pendingTaskReconciliationWatcher.watch(currentTask);
47113
+ } else {
47114
+ pendingTaskReconciliationWatcher.clear(args2.chatroomId, args2.role, args2.taskId);
47115
+ }
47116
+ return result;
47117
+ };
47118
+ const clearRoleRedeliveryTracking = (chatroomId, role) => {
47119
+ const prefix = `${chatroomId}:${role.toLowerCase()}:`;
47120
+ for (const key of [...turnEndAttemptCounts.keys()]) {
47121
+ if (key.startsWith(prefix))
47122
+ turnEndAttemptCounts.delete(key);
47123
+ }
47124
+ for (const key of [...redeliveryExhaustedKeys]) {
47125
+ if (key.startsWith(prefix))
47126
+ redeliveryExhaustedKeys.delete(key);
47127
+ }
47128
+ };
47129
+ const runBootstrapSweep = () => {
47130
+ if (!bootstrapSweepPending)
47131
+ return Promise.resolve();
47132
+ if (bootstrapSweepInFlight)
47133
+ return bootstrapSweepPromise ?? Promise.resolve();
47134
+ bootstrapSweepInFlight = true;
47135
+ bootstrapSweepPromise = sweepUncoveredInProgressTasks().then(() => {
47136
+ bootstrapSweepPending = false;
47137
+ bootstrapSweepFailures = 0;
47138
+ }).catch((error51) => {
47139
+ bootstrapSweepFailures += 1;
47140
+ if (bootstrapSweepFailures >= maxBootstrapSweepFailures) {
47141
+ bootstrapSweepPending = false;
47142
+ console.warn("[TaskService] bootstrap sweep giving up after 3 failed attempts");
47143
+ } else {
47144
+ console.warn(`[TaskService] bootstrap sweep failed (attempt ${bootstrapSweepFailures}/3): ${error51 instanceof Error ? error51.message : String(error51)}`);
47145
+ }
47146
+ }).finally(() => {
47147
+ bootstrapSweepInFlight = false;
47148
+ bootstrapSweepPromise = undefined;
47149
+ });
47150
+ return bootstrapSweepPromise;
47151
+ };
46999
47152
  const notifyForDelivery = async (notification) => {
47000
47153
  const results = await Promise.all([...listeners].map((listener) => Promise.resolve(listener(notification))));
47001
47154
  return {
@@ -47009,8 +47162,12 @@ function createTaskService(deps) {
47009
47162
  const applyInboxEvent = (event) => {
47010
47163
  if (event.eventType === "task_deleted" /* TaskDeleted */ || event.task.status === "completed") {
47011
47164
  taskInboxState.remove(event.chatroomId, event.role, event.taskId, event.task.updatedAt);
47165
+ clearRedeliveryTrackingForTask(event.chatroomId, event.role, event.taskId);
47012
47166
  return "handled";
47013
47167
  }
47168
+ if (event.eventType === "task_assigned" /* TaskAssigned */) {
47169
+ clearRedeliveryTrackingForTask(event.chatroomId, event.role, event.taskId);
47170
+ }
47014
47171
  return taskInboxState.upsert([
47015
47172
  {
47016
47173
  taskId: event.task.taskId,
@@ -47119,6 +47276,7 @@ function createTaskService(deps) {
47119
47276
  });
47120
47277
  const reconcileTaskStatuses = (tasks) => {
47121
47278
  taskInboxState.reconcileStatuses(tasks);
47279
+ runBootstrapSweep();
47122
47280
  for (const task of tasks) {
47123
47281
  if (task.status === "pending" || task.status === "acknowledged") {
47124
47282
  pendingTaskReconciliationWatcher.watch(task);
@@ -47158,7 +47316,9 @@ function createTaskService(deps) {
47158
47316
  machineId: deps.machineId
47159
47317
  });
47160
47318
  reconcileTaskStatuses(statusTasks);
47319
+ await runBootstrapSweep();
47161
47320
  } catch (error51) {
47321
+ bootstrapSweepPending = true;
47162
47322
  console.warn("[TaskService] task-status bootstrap failed:", error51);
47163
47323
  }
47164
47324
  if (wsClient2 && !stopInboxWatch) {
@@ -47193,26 +47353,47 @@ function createTaskService(deps) {
47193
47353
  acknowledgementRetryWatcher.stop();
47194
47354
  pendingTaskReconciliationWatcher.stop();
47195
47355
  taskChains.clear();
47356
+ handoffRepository.close();
47196
47357
  },
47197
47358
  listTasksForRole: (chatroomId, role) => taskInboxState.listForRole(chatroomId, role),
47198
47359
  listAllTasks: () => taskInboxState.listAll(),
47199
47360
  debugState: (chatroomId) => buildTaskServiceDebugState({ taskInboxState, chatroomId }),
47200
47361
  taskInboxState,
47201
- releaseTaskAfterTurnFailure: async (args2) => {
47202
- const result = await gateway.releaseTaskAfterTurnFailure({
47203
- sessionId: deps.sessionId,
47204
- chatroomId: args2.chatroomId,
47205
- role: args2.role,
47206
- taskId: args2.taskId
47207
- });
47208
- taskInboxState.markStatus(args2.chatroomId, args2.role, args2.taskId, result.status, result.updatedAt);
47209
- const currentTask = taskInboxState.getForRole(args2.chatroomId, args2.role, args2.taskId);
47210
- if (currentTask?.status === "pending") {
47211
- pendingTaskReconciliationWatcher.watch(currentTask);
47212
- } else {
47213
- pendingTaskReconciliationWatcher.clear(args2.chatroomId, args2.role, args2.taskId);
47362
+ recordHandoffOutcome: async ({
47363
+ chatroomId,
47364
+ role,
47365
+ targetRole,
47366
+ nextTask,
47367
+ taskIds: providedTaskIds
47368
+ }) => {
47369
+ const taskIds = providedTaskIds ?? taskInboxState.listForRole(chatroomId, role).filter((task) => task.status === "acknowledged" || task.status === "in_progress").map((task) => task.taskId);
47370
+ await handoffRepository.record({
47371
+ chatroomId,
47372
+ role,
47373
+ taskIds,
47374
+ ...nextTask?.taskId ? { nextTaskId: nextTask.taskId } : {},
47375
+ targetRole,
47376
+ handedOffAt: Date.now()
47377
+ });
47378
+ const now = Date.now();
47379
+ for (const taskId of taskIds)
47380
+ taskInboxState.remove(chatroomId, role, taskId, now);
47381
+ if (nextTask)
47382
+ taskInboxState.upsert([nextTask]);
47383
+ },
47384
+ getLatestHandoff: (chatroomId, role) => handoffRepository.getLatest(chatroomId, role),
47385
+ sweepUncoveredInProgressTasks,
47386
+ releaseTaskAfterTurnFailure: (args2) => releaseTask(args2),
47387
+ handleAgentRestart: async ({ chatroomId, role }) => {
47388
+ clearRoleRedeliveryTracking(chatroomId, role);
47389
+ const inFlightTasks = taskInboxState.listForRole(chatroomId, role).filter((task) => task.status === "acknowledged" || task.status === "in_progress");
47390
+ for (const task of inFlightTasks) {
47391
+ try {
47392
+ await releaseTask({ chatroomId, role, taskId: task.taskId });
47393
+ } catch (error51) {
47394
+ console.warn(`[TaskService] agent-restart release failed chatroom=${chatroomId} role=${role} task=${task.taskId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
47395
+ }
47214
47396
  }
47215
- return result;
47216
47397
  },
47217
47398
  loadAssignedTaskForAction: async ({ chatroomId, role, taskId }) => {
47218
47399
  const task = await gateway.loadAssignedTaskForAction({
@@ -47223,6 +47404,27 @@ function createTaskService(deps) {
47223
47404
  });
47224
47405
  return task?.chatroomId === chatroomId ? task : null;
47225
47406
  },
47407
+ handleAgentTurnProgress: async ({ chatroomId, role, taskId }) => {
47408
+ const task = taskInboxState.getForRole(chatroomId, role, taskId);
47409
+ if (!task)
47410
+ return;
47411
+ if (task.status !== "acknowledged" && task.status !== "in_progress")
47412
+ return;
47413
+ if (task.status === "acknowledged") {
47414
+ try {
47415
+ await gateway.readTask({ sessionId: deps.sessionId, chatroomId, role, taskId });
47416
+ taskInboxState.markStatus(chatroomId, role, taskId, "in_progress", Date.now());
47417
+ } catch (error51) {
47418
+ console.warn(`[TaskService] turn-progress read failed chatroom=${chatroomId} role=${role} task=${taskId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
47419
+ return;
47420
+ }
47421
+ }
47422
+ try {
47423
+ await gateway.markReceiptStarted({ sessionId: deps.sessionId, chatroomId, role, taskId });
47424
+ } catch (error51) {
47425
+ console.warn(`[TaskService] turn-progress receipt mark failed chatroom=${chatroomId} role=${role} task=${taskId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
47426
+ }
47427
+ },
47226
47428
  recordDeliveryFailure: ({ taskId, reason }) => gateway.recordDeliveryFailure({
47227
47429
  sessionId: deps.sessionId,
47228
47430
  machineId: deps.machineId,
@@ -47234,7 +47436,12 @@ function createTaskService(deps) {
47234
47436
  machineId: deps.machineId,
47235
47437
  taskId,
47236
47438
  ...expectedReason ? { expectedReason } : {}
47237
- })
47439
+ }),
47440
+ recordUncoveredTurnEnd,
47441
+ isRedeliveryExhausted,
47442
+ clearRedeliveryTracking: ({ chatroomId, role }) => {
47443
+ clearRoleRedeliveryTracking(chatroomId, role);
47444
+ }
47238
47445
  };
47239
47446
  return service4;
47240
47447
  }
@@ -47247,6 +47454,63 @@ var init_task_service = __esm(() => {
47247
47454
  init_convex_native_task_delivery_gateway();
47248
47455
  });
47249
47456
 
47457
+ // src/daemon/services/task-service/infrastructure/repository/task-handoff-repository.ts
47458
+ import { mkdirSync as mkdirSync2 } from "node:fs";
47459
+ import { dirname as dirname2 } from "node:path";
47460
+ import { DatabaseSync } from "node:sqlite";
47461
+ function normalizeRole(role) {
47462
+ return role.toLowerCase();
47463
+ }
47464
+ function mapRow(row) {
47465
+ const taskIds = JSON.parse(row.task_ids_json);
47466
+ if (!Array.isArray(taskIds) || !taskIds.every((taskId) => typeof taskId === "string")) {
47467
+ throw new Error("Invalid task handoff task_ids_json");
47468
+ }
47469
+ return {
47470
+ chatroomId: row.chatroom_id,
47471
+ role: row.role,
47472
+ taskIds,
47473
+ ...row.next_task_id ? { nextTaskId: row.next_task_id } : {},
47474
+ targetRole: row.target_role,
47475
+ handedOffAt: row.handed_off_at
47476
+ };
47477
+ }
47478
+ function createTaskHandoffRepository(dbPath) {
47479
+ mkdirSync2(dirname2(dbPath), { recursive: true });
47480
+ const db = new DatabaseSync(dbPath);
47481
+ db.exec(`
47482
+ CREATE TABLE IF NOT EXISTS task_handoffs (
47483
+ chatroom_id TEXT NOT NULL,
47484
+ role TEXT NOT NULL,
47485
+ task_ids_json TEXT NOT NULL,
47486
+ next_task_id TEXT,
47487
+ target_role TEXT NOT NULL,
47488
+ handed_off_at INTEGER NOT NULL,
47489
+ PRIMARY KEY (chatroom_id, role)
47490
+ )
47491
+ `);
47492
+ return {
47493
+ async record(record2) {
47494
+ db.prepare(`INSERT INTO task_handoffs
47495
+ (chatroom_id, role, task_ids_json, next_task_id, target_role, handed_off_at)
47496
+ VALUES (?, ?, ?, ?, ?, ?)
47497
+ ON CONFLICT(chatroom_id, role) DO UPDATE SET
47498
+ task_ids_json = excluded.task_ids_json,
47499
+ next_task_id = excluded.next_task_id,
47500
+ target_role = excluded.target_role,
47501
+ handed_off_at = excluded.handed_off_at`).run(record2.chatroomId, normalizeRole(record2.role), JSON.stringify(record2.taskIds), record2.nextTaskId ?? null, record2.targetRole, record2.handedOffAt);
47502
+ },
47503
+ async getLatest(chatroomId, role) {
47504
+ const row = db.prepare("SELECT chatroom_id, role, task_ids_json, next_task_id, target_role, handed_off_at FROM task_handoffs WHERE chatroom_id = ? AND role = ?").get(chatroomId, normalizeRole(role));
47505
+ return row ? mapRow(row) : null;
47506
+ },
47507
+ close() {
47508
+ db.close();
47509
+ }
47510
+ };
47511
+ }
47512
+ var init_task_handoff_repository = () => {};
47513
+
47250
47514
  // src/daemon/services/task-service/infrastructure/adapters/daemon-audit-port.ts
47251
47515
  function createDaemonAuditPort(logEvent) {
47252
47516
  return { emit: (event) => logDaemonAuditEvent(logEvent, event) };
@@ -47279,6 +47543,9 @@ function decideNextDelivery(tasks, context5) {
47279
47543
  if (!context5.isNativeHarness(context5.agentConfig.agentHarness)) {
47280
47544
  return { kind: "failed", taskId: task.taskId, reason: "unsupported_harness" };
47281
47545
  }
47546
+ if (context5.isRedeliveryExhausted(task.taskId)) {
47547
+ return { kind: "deduplicated", reason: "redelivery_exhausted", taskId: task.taskId };
47548
+ }
47282
47549
  const blockReason = context5.explainNativeDeliveryBlock(task);
47283
47550
  if (blockReason === "acknowledged_wrong_role") {
47284
47551
  return { kind: "failed", taskId: task.taskId, reason: "assigned_elsewhere" };
@@ -47394,7 +47661,8 @@ class NativeTaskDeliveryCoordinator {
47394
47661
  agentConfig,
47395
47662
  configState,
47396
47663
  isNativeHarness: taskService.isNativeHarness,
47397
- explainNativeDeliveryBlock: taskService.explainNativeDeliveryBlock
47664
+ explainNativeDeliveryBlock: taskService.explainNativeDeliveryBlock,
47665
+ isRedeliveryExhausted: (taskId) => taskService.isRedeliveryExhausted({ chatroomId: firstTask.chatroomId, role, taskId })
47398
47666
  });
47399
47667
  const attemptId = `${Date.now()}-${firstTask.taskId}`;
47400
47668
  const decisionReason = "reason" in decision ? decision.reason : undefined;
@@ -47465,6 +47733,7 @@ var init_task_service2 = __esm(() => {
47465
47733
  init_native_task_injector_logic();
47466
47734
  init_task_service();
47467
47735
  init_convex_native_task_delivery_gateway();
47736
+ init_task_handoff_repository();
47468
47737
  init_daemon_audit_port();
47469
47738
  init_native_task_delivery_coordinator();
47470
47739
  });
@@ -47567,6 +47836,7 @@ class AgentWorkManager {
47567
47836
  unsubscribeAgentTurnEnded;
47568
47837
  unsubscribeAgentStarted;
47569
47838
  unsubscribeAgentSessionLost;
47839
+ unsubscribeAgentTurnProgress;
47570
47840
  reconcileStates = new Map;
47571
47841
  unsubscribeTaskService;
47572
47842
  nativeTaskDeliveryQueue;
@@ -47599,16 +47869,33 @@ class AgentWorkManager {
47599
47869
  releaseTaskAfterTurnFailure: deps.taskService.releaseTaskAfterTurnFailure,
47600
47870
  recordDeliveryFailure: deps.taskService.recordDeliveryFailure,
47601
47871
  clearDeliveryFailure: deps.taskService.clearDeliveryFailure,
47872
+ recordUncoveredTurnEnd: deps.taskService.recordUncoveredTurnEnd,
47873
+ isRedeliveryExhausted: deps.taskService.isRedeliveryExhausted,
47874
+ clearRedeliveryTracking: deps.taskService.clearRedeliveryTracking,
47602
47875
  loadAssignedTaskForAction: deps.taskService.loadAssignedTaskForAction,
47603
47876
  deliverNativeTask: (task, harnessSessionId, onTaskDelivered) => this.nativeTaskDeliveryQueue.enqueue({ task, harnessSessionId, onTaskDelivered })
47604
47877
  };
47605
47878
  this.unsubscribeAgentTurnEnded = deps.agentMgr.subscribeAgentTurnEnded((event) => this.handleAgentTurnEnded(event));
47606
47879
  this.unsubscribeAgentStarted = deps.agentMgr.subscribeAgentStarted((event) => this.handleAgentStarted(event));
47607
47880
  this.unsubscribeAgentSessionLost = deps.agentMgr.subscribeAgentSessionLost((event) => this.handleAgentSessionLost(event));
47881
+ this.unsubscribeAgentTurnProgress = deps.agentMgr.subscribeAgentTurnProgress((event) => {
47882
+ const activeTask = this.deps.agentTaskState.get(event);
47883
+ if (!activeTask)
47884
+ return;
47885
+ this.deps.taskService.handleAgentTurnProgress({ ...event, taskId: activeTask.taskId }).catch((error51) => {
47886
+ console.warn(`[AgentProcessManager] turn-progress handling failed for ${event.role}@${event.chatroomId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
47887
+ });
47888
+ });
47608
47889
  this.unsubscribeTaskService = deps.taskService.subscribe((notification) => this.handleTaskServiceNotification(notification));
47609
47890
  }
47610
47891
  async handleAgentStarted(event) {
47611
47892
  this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
47893
+ if (event.reason === "user.start") {
47894
+ this.deps.taskService.clearRedeliveryTracking({
47895
+ chatroomId: event.chatroomId,
47896
+ role: event.role
47897
+ });
47898
+ }
47612
47899
  await this.requestReconcile({
47613
47900
  chatroomId: event.chatroomId,
47614
47901
  role: event.role,
@@ -47626,24 +47913,43 @@ class AgentWorkManager {
47626
47913
  console.warn(`[NativeDelivery:failure] role=${event.role} chatroom=${event.chatroomId} operation=session-loss-reconcile error=${error51 instanceof Error ? error51.message : String(error51)}`);
47627
47914
  });
47628
47915
  }
47916
+ async recoverTaskAfterUncoveredTurnEnd(event, activeTask, phase) {
47917
+ const { chatroomId, role } = event;
47918
+ const { exceeded } = await this.deps.taskService.recordUncoveredTurnEnd({
47919
+ chatroomId,
47920
+ role,
47921
+ taskId: activeTask.taskId
47922
+ });
47923
+ if (exceeded)
47924
+ return "exceeded";
47925
+ try {
47926
+ await this.deps.taskService.releaseTaskAfterTurnFailure({
47927
+ chatroomId,
47928
+ role,
47929
+ taskId: activeTask.taskId
47930
+ });
47931
+ } catch (error51) {
47932
+ console.error(`[NativeDelivery:${phase}-recovery-error] chatroom=${chatroomId} role=${role} task=${activeTask.taskId} turn=${event.completion.turnId} error=${error51 instanceof Error ? error51.stack ?? error51.message : String(error51)}`);
47933
+ return "recovery-failed";
47934
+ }
47935
+ return "released";
47936
+ }
47629
47937
  async handleAgentTurnEnded(event) {
47630
47938
  const completion = event.completion;
47631
47939
  if (completion.status !== "completed") {
47632
- const activeTask = this.deps.agentTaskState.get({
47940
+ const activeTask2 = this.deps.agentTaskState.get({
47633
47941
  chatroomId: event.chatroomId,
47634
47942
  role: event.role
47635
47943
  });
47636
47944
  const errorDetail = completion.error;
47637
- console.error(`[NativeDelivery:turn-failed] chatroom=${event.chatroomId} role=${event.role} task=${activeTask?.taskId ?? "none"} turn=${completion.turnId} status=${completion.status} source=${completion.source} error=${errorDetail ?? "none"}`);
47638
- if (activeTask) {
47639
- try {
47640
- await this.deps.taskService.releaseTaskAfterTurnFailure({
47641
- chatroomId: event.chatroomId,
47642
- role: event.role,
47643
- taskId: activeTask.taskId
47644
- });
47645
- } catch (error51) {
47646
- console.error(`[NativeDelivery:turn-failed-recovery-error] chatroom=${event.chatroomId} role=${event.role} task=${activeTask.taskId} turn=${completion.turnId} error=${error51 instanceof Error ? error51.stack ?? error51.message : String(error51)}`);
47945
+ console.error(`[NativeDelivery:turn-failed] chatroom=${event.chatroomId} role=${event.role} task=${activeTask2?.taskId ?? "none"} turn=${completion.turnId} status=${completion.status} source=${completion.source} error=${errorDetail ?? "none"}`);
47946
+ if (activeTask2) {
47947
+ const recovery = await this.recoverTaskAfterUncoveredTurnEnd(event, activeTask2, "turn-failed");
47948
+ if (recovery === "exceeded") {
47949
+ this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
47950
+ return { kind: "release-slot" };
47951
+ }
47952
+ if (recovery === "recovery-failed") {
47647
47953
  return { kind: "hold-slot", reason: "task-recovery-failed" };
47648
47954
  }
47649
47955
  this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
@@ -47652,7 +47958,7 @@ class AgentWorkManager {
47652
47958
  kind: "turn_failed",
47653
47959
  chatroomId: event.chatroomId,
47654
47960
  role: event.role,
47655
- ...activeTask?.taskId ? { taskId: activeTask.taskId } : {},
47961
+ ...activeTask2?.taskId ? { taskId: activeTask2.taskId } : {},
47656
47962
  ...event.slot.harnessSessionId ? { harnessSessionId: event.slot.harnessSessionId } : {},
47657
47963
  turnId: completion.turnId,
47658
47964
  status: completion.status,
@@ -47672,6 +47978,19 @@ class AgentWorkManager {
47672
47978
  }
47673
47979
  return { kind: "release-slot" };
47674
47980
  }
47981
+ const activeTask = this.deps.agentTaskState.get({
47982
+ chatroomId: event.chatroomId,
47983
+ role: event.role
47984
+ });
47985
+ if (activeTask) {
47986
+ const handoff = await this.deps.taskService.getLatestHandoff(event.chatroomId, event.role);
47987
+ if (!handoff?.taskIds.includes(activeTask.taskId)) {
47988
+ const recovery = await this.recoverTaskAfterUncoveredTurnEnd(event, activeTask, "turn-ended");
47989
+ if (recovery === "recovery-failed") {
47990
+ return { kind: "hold-slot", reason: "task-recovery-failed" };
47991
+ }
47992
+ }
47993
+ }
47675
47994
  this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
47676
47995
  try {
47677
47996
  await this.deps.lifecycleOutbox.enqueue(buildAgentStatusFact({
@@ -47696,6 +48015,7 @@ class AgentWorkManager {
47696
48015
  this.unsubscribeAgentTurnEnded();
47697
48016
  this.unsubscribeAgentStarted();
47698
48017
  this.unsubscribeAgentSessionLost();
48018
+ this.unsubscribeAgentTurnProgress();
47699
48019
  this.unsubscribeTaskService?.();
47700
48020
  this.unsubscribeTaskService = undefined;
47701
48021
  this.nativeTaskDeliveryQueue.stop();
@@ -47809,6 +48129,11 @@ class AgentWorkManager {
47809
48129
  });
47810
48130
  return delivered;
47811
48131
  }
48132
+ async handleAgentRestart(args2) {
48133
+ resetRoleDeliveryState(args2.chatroomId, args2.role);
48134
+ this.deps.agentTaskState.clear(args2);
48135
+ await this.deps.taskService.handleAgentRestart(args2);
48136
+ }
47812
48137
  async requestReconcileForTasks(tasks, source) {
47813
48138
  const roles = new Set(tasks.map((snapshot) => `${snapshot.chatroomId}:${snapshot.agentConfig.role.toLowerCase()}`));
47814
48139
  await Promise.all([...roles].map((key) => {
@@ -47844,6 +48169,7 @@ var init_agent_work_manager = __esm(() => {
47844
48169
  init_native_task_injector_logic();
47845
48170
  init_convex_native_task_delivery_gateway();
47846
48171
  init_daemon_audit_port();
48172
+ init_native_task_delivery_coordinator();
47847
48173
  init_task_delivery_processor();
47848
48174
  init_native_task_injector();
47849
48175
  });
@@ -50852,12 +51178,12 @@ var init_model_provider = () => {};
50852
51178
  // src/daemon/infrastructure/local/harness/services/cursor-sdk/cursor-sdk-package.ts
50853
51179
  import { existsSync as existsSync2, readFileSync as readFileSync4 } from "node:fs";
50854
51180
  import { createRequire as createRequire3 } from "node:module";
50855
- import { dirname as dirname2, join as join8 } from "node:path";
51181
+ import { dirname as dirname3, join as join8 } from "node:path";
50856
51182
  import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
50857
51183
  function resolveChatroomCliRoot(moduleRef = import.meta.url) {
50858
51184
  const filePath = moduleRef.startsWith("file:") ? fileURLToPath2(moduleRef) : moduleRef;
50859
- let dir = dirname2(filePath);
50860
- while (dir !== dirname2(dir)) {
51185
+ let dir = dirname3(filePath);
51186
+ while (dir !== dirname3(dir)) {
50861
51187
  const packageJsonPath = join8(dir, "package.json");
50862
51188
  if (existsSync2(packageJsonPath)) {
50863
51189
  const pkg = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
@@ -50865,7 +51191,7 @@ function resolveChatroomCliRoot(moduleRef = import.meta.url) {
50865
51191
  return dir;
50866
51192
  }
50867
51193
  }
50868
- dir = dirname2(dir);
51194
+ dir = dirname3(dir);
50869
51195
  }
50870
51196
  throw new CursorSdkPackageError(`Could not locate chatroom-cli package root while resolving @cursor/sdk. ${REINSTALL_HINT}`);
50871
51197
  }
@@ -50880,7 +51206,7 @@ function readPinnedSdkVersion(chatroomCliRoot) {
50880
51206
  return match17[1];
50881
51207
  }
50882
51208
  function readInstalledSdkVersion(entryPath) {
50883
- const packageJsonPath = join8(dirname2(entryPath), "..", "..", "package.json");
51209
+ const packageJsonPath = join8(dirname3(entryPath), "..", "..", "package.json");
50884
51210
  const pkg = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
50885
51211
  return pkg.version;
50886
51212
  }
@@ -51781,12 +52107,12 @@ var init_claude = __esm(() => {
51781
52107
  // src/daemon/infrastructure/local/harness/services/claude-sdk/claude-sdk-package.ts
51782
52108
  import { existsSync as existsSync3, readFileSync as readFileSync5 } from "node:fs";
51783
52109
  import { createRequire as createRequire4 } from "node:module";
51784
- import { dirname as dirname3, join as join9 } from "node:path";
52110
+ import { dirname as dirname4, join as join9 } from "node:path";
51785
52111
  import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "node:url";
51786
52112
  function resolveChatroomCliRoot2(moduleRef = import.meta.url) {
51787
52113
  const filePath = moduleRef.startsWith("file:") ? fileURLToPath3(moduleRef) : moduleRef;
51788
- let dir = dirname3(filePath);
51789
- while (dir !== dirname3(dir)) {
52114
+ let dir = dirname4(filePath);
52115
+ while (dir !== dirname4(dir)) {
51790
52116
  const packageJsonPath = join9(dir, "package.json");
51791
52117
  if (existsSync3(packageJsonPath)) {
51792
52118
  const pkg = JSON.parse(readFileSync5(packageJsonPath, "utf8"));
@@ -51794,7 +52120,7 @@ function resolveChatroomCliRoot2(moduleRef = import.meta.url) {
51794
52120
  return dir;
51795
52121
  }
51796
52122
  }
51797
- dir = dirname3(dir);
52123
+ dir = dirname4(dir);
51798
52124
  }
51799
52125
  throw new ClaudeSdkPackageError(`Could not locate chatroom-cli package root while resolving ${CLAUDE_AGENT_SDK_PKG}. ${REINSTALL_HINT2}`);
51800
52126
  }
@@ -51809,7 +52135,7 @@ function readPinnedSdkVersion2(chatroomCliRoot) {
51809
52135
  return match17[1];
51810
52136
  }
51811
52137
  function readInstalledSdkVersion2(entryPath) {
51812
- const packageJsonPath = join9(dirname3(entryPath), "package.json");
52138
+ const packageJsonPath = join9(dirname4(entryPath), "package.json");
51813
52139
  const pkg = JSON.parse(readFileSync5(packageJsonPath, "utf8"));
51814
52140
  return pkg.version;
51815
52141
  }
@@ -51835,7 +52161,7 @@ function resolveNativeCliBinaryPath(sdkEntryPath) {
51835
52161
  const binaryName = process.platform === "win32" ? "claude.exe" : "claude";
51836
52162
  let pkgDir;
51837
52163
  try {
51838
- pkgDir = dirname3(require3.resolve(`${platformPkg}/package.json`));
52164
+ pkgDir = dirname4(require3.resolve(`${platformPkg}/package.json`));
51839
52165
  } catch {
51840
52166
  throw new ClaudeSdkPackageError(`Native CLI binary package ${platformPkg} is not installed. ${REINSTALL_HINT2}`);
51841
52167
  }
@@ -52771,12 +53097,12 @@ var init_codex_sdk_model_variants = __esm(() => {
52771
53097
  // src/daemon/infrastructure/local/harness/services/codex-sdk/codex-sdk-package.ts
52772
53098
  import { existsSync as existsSync4, readFileSync as readFileSync6, realpathSync, statSync } from "node:fs";
52773
53099
  import { createRequire as createRequire5 } from "node:module";
52774
- import { dirname as dirname4, join as join10 } from "node:path";
53100
+ import { dirname as dirname5, join as join10 } from "node:path";
52775
53101
  import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL3 } from "node:url";
52776
53102
  function resolveChatroomCliRoot3(moduleRef = import.meta.url) {
52777
53103
  const filePath = moduleRef.startsWith("file:") ? fileURLToPath4(moduleRef) : moduleRef;
52778
- let dir = dirname4(filePath);
52779
- while (dir !== dirname4(dir)) {
53104
+ let dir = dirname5(filePath);
53105
+ while (dir !== dirname5(dir)) {
52780
53106
  const packageJsonPath = join10(dir, "package.json");
52781
53107
  if (existsSync4(packageJsonPath)) {
52782
53108
  const pkg = JSON.parse(readFileSync6(packageJsonPath, "utf8"));
@@ -52784,7 +53110,7 @@ function resolveChatroomCliRoot3(moduleRef = import.meta.url) {
52784
53110
  return dir;
52785
53111
  }
52786
53112
  }
52787
- dir = dirname4(dir);
53113
+ dir = dirname5(dir);
52788
53114
  }
52789
53115
  throw new CodexSdkPackageError(`Could not locate chatroom-cli package root while resolving @openai/codex-sdk. ${REINSTALL_HINT3}`);
52790
53116
  }
@@ -52851,7 +53177,7 @@ function addPlatformPackageJsonCandidate(candidates, candidate) {
52851
53177
  }
52852
53178
  function resolvePlatformPackageJsonCandidates(platformPkg, chatroomCliRoot, codexPackageJson, chatroomRequire, codexRequire) {
52853
53179
  const candidates = [];
52854
- const codexPackageDir = realpathSync(dirname4(codexPackageJson));
53180
+ const codexPackageDir = realpathSync(dirname5(codexPackageJson));
52855
53181
  const platformPkgBaseName = platformPkg.includes("/") ? platformPkg.slice(platformPkg.indexOf("/") + 1) : platformPkg;
52856
53182
  try {
52857
53183
  addPlatformPackageJsonCandidate(candidates, codexRequire.resolve(`${platformPkg}/package.json`));
@@ -52895,7 +53221,7 @@ function resolveCodexExecutablePath(moduleRef = import.meta.url) {
52895
53221
  throw new CodexSdkPackageError(`Native Codex CLI package ${platformPkg} is not installed. Ensure ${CODEX_NPM_NAME} is installed with optional dependencies. ${REINSTALL_HINT3}`);
52896
53222
  }
52897
53223
  for (const platformPackageJson of platformPackageJsonCandidates) {
52898
- const vendorRoot = join10(dirname4(platformPackageJson), "vendor");
53224
+ const vendorRoot = join10(dirname5(platformPackageJson), "vendor");
52899
53225
  const binaryPath = resolveNativeCodexBinary(vendorRoot, targetTriple, codexBinaryName);
52900
53226
  if (binaryPath) {
52901
53227
  return binaryPath;
@@ -52911,7 +53237,7 @@ async function importBundledCodexSdk(moduleRef = import.meta.url) {
52911
53237
  if (installedVersion !== pinnedVersion) {
52912
53238
  throw new CodexSdkPackageError(`@openai/codex-sdk@${installedVersion} does not match chatroom-cli pin (${pinnedVersion}). ${REINSTALL_HINT3}`);
52913
53239
  }
52914
- const distEntryPath = join10(dirname4(packageJsonPath), "dist", "index.js");
53240
+ const distEntryPath = join10(dirname5(packageJsonPath), "dist", "index.js");
52915
53241
  if (!existsSync4(distEntryPath)) {
52916
53242
  throw new CodexSdkPackageError(`@openai/codex-sdk entry file is missing: ${distEntryPath}. ${REINSTALL_HINT3}`);
52917
53243
  }
@@ -55022,9 +55348,9 @@ var init_session_event_forwarder = __esm(() => {
55022
55348
  });
55023
55349
 
55024
55350
  // src/daemon/infrastructure/local/harness/services/opencode-sdk/session-metadata-store.ts
55025
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
55351
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
55026
55352
  import { homedir as homedir3 } from "node:os";
55027
- import { dirname as dirname5, join as join11 } from "node:path";
55353
+ import { dirname as dirname6, join as join11 } from "node:path";
55028
55354
 
55029
55355
  class FileSessionMetadataStore {
55030
55356
  filePath;
@@ -55041,9 +55367,9 @@ class FileSessionMetadataStore {
55041
55367
  }
55042
55368
  save(sessions) {
55043
55369
  try {
55044
- const dir = dirname5(this.filePath);
55370
+ const dir = dirname6(this.filePath);
55045
55371
  if (!existsSync5(dir)) {
55046
- mkdirSync2(dir, { recursive: true });
55372
+ mkdirSync3(dir, { recursive: true });
55047
55373
  }
55048
55374
  writeFileSync2(this.filePath, JSON.stringify(sessions, null, 2));
55049
55375
  } catch {}
@@ -55591,12 +55917,12 @@ var init_opencode_sdk = __esm(() => {
55591
55917
 
55592
55918
  // src/daemon/infrastructure/local/harness/services/pi-sdk/pi-sdk-package.ts
55593
55919
  import { existsSync as existsSync6, readFileSync as readFileSync8 } from "node:fs";
55594
- import { dirname as dirname6, join as join12 } from "node:path";
55920
+ import { dirname as dirname7, join as join12 } from "node:path";
55595
55921
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL4 } from "node:url";
55596
55922
  function resolveChatroomCliRoot4(moduleRef = import.meta.url) {
55597
55923
  const filePath = moduleRef.startsWith("file:") ? fileURLToPath5(moduleRef) : moduleRef;
55598
- let dir = dirname6(filePath);
55599
- while (dir !== dirname6(dir)) {
55924
+ let dir = dirname7(filePath);
55925
+ while (dir !== dirname7(dir)) {
55600
55926
  const packageJsonPath = join12(dir, "package.json");
55601
55927
  if (existsSync6(packageJsonPath)) {
55602
55928
  const pkg = JSON.parse(readFileSync8(packageJsonPath, "utf8"));
@@ -55604,7 +55930,7 @@ function resolveChatroomCliRoot4(moduleRef = import.meta.url) {
55604
55930
  return dir;
55605
55931
  }
55606
55932
  }
55607
- dir = dirname6(dir);
55933
+ dir = dirname7(dir);
55608
55934
  }
55609
55935
  throw new PiSdkPackageError(`Could not locate chatroom-cli package root while resolving ${PI_CODING_AGENT_PKG}. ${REINSTALL_HINT4}`);
55610
55936
  }
@@ -55619,18 +55945,18 @@ function readPinnedSdkVersion4(chatroomCliRoot) {
55619
55945
  return match17[1];
55620
55946
  }
55621
55947
  function readInstalledSdkVersion4(entryPath) {
55622
- const packageJsonPath = join12(dirname6(entryPath), "..", "package.json");
55948
+ const packageJsonPath = join12(dirname7(entryPath), "..", "package.json");
55623
55949
  const pkg = JSON.parse(readFileSync8(packageJsonPath, "utf8"));
55624
55950
  return pkg.version;
55625
55951
  }
55626
55952
  function resolvePiSdkEntryPathFromNodeModules(chatroomCliRoot) {
55627
55953
  let dir = chatroomCliRoot;
55628
- while (dir !== dirname6(dir)) {
55954
+ while (dir !== dirname7(dir)) {
55629
55955
  const entryPath = join12(dir, "node_modules", PI_CODING_AGENT_PKG, "dist", "index.js");
55630
55956
  if (existsSync6(entryPath)) {
55631
55957
  return entryPath;
55632
55958
  }
55633
- dir = dirname6(dir);
55959
+ dir = dirname7(dir);
55634
55960
  }
55635
55961
  return;
55636
55962
  }
@@ -63776,15 +64102,35 @@ async function requestLocalDaemon(port, event, payload) {
63776
64102
  socket.connect();
63777
64103
  });
63778
64104
  const response = await socket.timeout(1e4).emitWithAck(event, payload);
63779
- if (!response.ok)
63780
- throw new Error(response.error?.message ?? `Daemon request failed: ${event}`);
64105
+ if (!response.ok) {
64106
+ throw toLocalDaemonServerError(response, event);
64107
+ }
63781
64108
  return response.data;
63782
64109
  } finally {
63783
64110
  socket.disconnect();
63784
64111
  }
63785
64112
  }
64113
+ function toLocalDaemonServerError(response, event) {
64114
+ const error51 = response.error ?? {};
64115
+ return new LocalDaemonServerError({
64116
+ message: error51.message ?? `Daemon request failed: ${event}`,
64117
+ code: error51.code,
64118
+ details: error51.details
64119
+ });
64120
+ }
64121
+ var LocalDaemonServerError;
63786
64122
  var init_local_daemon = __esm(() => {
63787
64123
  init_esm_debug3();
64124
+ LocalDaemonServerError = class LocalDaemonServerError extends Error {
64125
+ code;
64126
+ details;
64127
+ constructor(error51) {
64128
+ super(error51.message ?? "Daemon request failed");
64129
+ this.name = "LocalDaemonServerError";
64130
+ this.code = error51.code;
64131
+ this.details = error51.details;
64132
+ }
64133
+ };
63788
64134
  });
63789
64135
 
63790
64136
  // src/daemon/entry/resolve-local-web-port.ts
@@ -63996,12 +64342,12 @@ var init_log_schema = __esm(() => {
63996
64342
  });
63997
64343
 
63998
64344
  // src/infrastructure/log-server/open-log-database.ts
63999
- import { mkdirSync as mkdirSync3 } from "node:fs";
64000
- import { dirname as dirname7 } from "node:path";
64001
- import { DatabaseSync } from "node:sqlite";
64345
+ import { mkdirSync as mkdirSync4 } from "node:fs";
64346
+ import { dirname as dirname8 } from "node:path";
64347
+ import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
64002
64348
  function openLogDatabase(dbPath) {
64003
- mkdirSync3(dirname7(dbPath), { recursive: true });
64004
- const db = new DatabaseSync(dbPath);
64349
+ mkdirSync4(dirname8(dbPath), { recursive: true });
64350
+ const db = new DatabaseSync2(dbPath);
64005
64351
  db.exec("PRAGMA journal_mode = WAL");
64006
64352
  db.exec("PRAGMA busy_timeout = 5000");
64007
64353
  db.exec("PRAGMA synchronous = NORMAL");
@@ -64190,7 +64536,7 @@ var init_daemon_process_scan = __esm(() => {
64190
64536
 
64191
64537
  // src/commands/machine/pid.ts
64192
64538
  import { createHash as createHash2 } from "node:crypto";
64193
- import { existsSync as existsSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, mkdirSync as mkdirSync4 } from "node:fs";
64539
+ import { existsSync as existsSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, mkdirSync as mkdirSync5 } from "node:fs";
64194
64540
  import { homedir as homedir7 } from "node:os";
64195
64541
  import { join as join16 } from "node:path";
64196
64542
  function getUrlHash2() {
@@ -64202,7 +64548,7 @@ function getPidFileName() {
64202
64548
  }
64203
64549
  function ensureChatroomDir2() {
64204
64550
  if (!existsSync8(CHATROOM_DIR3)) {
64205
- mkdirSync4(CHATROOM_DIR3, { recursive: true, mode: 448 });
64551
+ mkdirSync5(CHATROOM_DIR3, { recursive: true, mode: 448 });
64206
64552
  }
64207
64553
  }
64208
64554
  function getPidFilePath() {
@@ -77212,11 +77558,11 @@ var init_pdfium_renderer = __esm(() => {
77212
77558
 
77213
77559
  // ../../node_modules/.pnpm/@llamaindex+liteparse@1.5.3/node_modules/@llamaindex/liteparse/dist/src/engines/pdf/pdfjsImporter.js
77214
77560
  import { fileURLToPath as fileURLToPath6 } from "node:url";
77215
- import { dirname as dirname8 } from "node:path";
77561
+ import { dirname as dirname9 } from "node:path";
77216
77562
  async function importPdfJs() {
77217
77563
  const pdfUrl = new URL("../../vendor/pdfjs/pdf.mjs", import.meta.url);
77218
77564
  const pdfjs = await import(pdfUrl.href);
77219
- const dirPath = dirname8(fileURLToPath6(pdfUrl));
77565
+ const dirPath = dirname9(fileURLToPath6(pdfUrl));
77220
77566
  return {
77221
77567
  fn: pdfjs.getDocument,
77222
77568
  dir: dirPath
@@ -98865,9 +99211,9 @@ function applyMarkupTags(markup, text) {
98865
99211
  }
98866
99212
 
98867
99213
  // ../../node_modules/.pnpm/@llamaindex+liteparse@1.5.3/node_modules/@llamaindex/liteparse/dist/src/processing/gridDebugLogger.js
98868
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
99214
+ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync4 } from "fs";
98869
99215
  import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
98870
- import { dirname as dirname9 } from "path";
99216
+ import { dirname as dirname10 } from "path";
98871
99217
 
98872
99218
  class GridDebugLogger {
98873
99219
  config;
@@ -99194,14 +99540,14 @@ class GridDebugLogger {
99194
99540
  flushSync() {
99195
99541
  if (!this.config.outputPath || this.entries.length === 0)
99196
99542
  return;
99197
- mkdirSync5(dirname9(this.config.outputPath), { recursive: true });
99543
+ mkdirSync6(dirname10(this.config.outputPath), { recursive: true });
99198
99544
  writeFileSync4(this.config.outputPath, this.formatEntries(), "utf-8");
99199
99545
  this.entries = [];
99200
99546
  }
99201
99547
  async flush() {
99202
99548
  if (!this.config.outputPath || this.entries.length === 0)
99203
99549
  return;
99204
- await mkdir4(dirname9(this.config.outputPath), { recursive: true });
99550
+ await mkdir4(dirname10(this.config.outputPath), { recursive: true });
99205
99551
  await writeFile5(this.config.outputPath, this.formatEntries(), "utf-8");
99206
99552
  this.entries = [];
99207
99553
  }
@@ -99252,7 +99598,7 @@ var init_gridDebugLogger = __esm(() => {
99252
99598
  });
99253
99599
 
99254
99600
  // ../../node_modules/.pnpm/@llamaindex+liteparse@1.5.3/node_modules/@llamaindex/liteparse/dist/src/processing/gridVisualizer.js
99255
- import { mkdirSync as mkdirSync6 } from "fs";
99601
+ import { mkdirSync as mkdirSync7 } from "fs";
99256
99602
  function escapeXml(s) {
99257
99603
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
99258
99604
  }
@@ -99362,7 +99708,7 @@ async function renderGridVisualization(data, outputPath) {
99362
99708
  }).composite([{ input: svgBuffer, top: 0, left: 0 }]).png({ compressionLevel: 6 }).toFile(outputPath);
99363
99709
  }
99364
99710
  async function renderAllVisualizations(pages, outputDir) {
99365
- mkdirSync6(outputDir, { recursive: true });
99711
+ mkdirSync7(outputDir, { recursive: true });
99366
99712
  const paths = [];
99367
99713
  for (const page of pages) {
99368
99714
  const filePath = `${outputDir}/page-${page.pageNum}-grid.png`;
@@ -107065,6 +107411,9 @@ function getFrontendDesignUxFlowChecklistBlock() {
107065
107411
  - **Interaction affordance:** pointer cursor (or project equivalent) on clickable elements
107066
107412
  - **Safeguards:** confirmation before destructive actions; bulk operations confirmed with scope summary`;
107067
107413
  }
107414
+ function getFrontendDesignUxTriggerDescription() {
107415
+ return "when the user request involves user interface changes";
107416
+ }
107068
107417
 
107069
107418
  // ../../services/backend/prompts/utils/enhancer-feedback-template-body.ts
107070
107419
  function getEnhancerFeedbackTemplateBody() {
@@ -107927,31 +108276,8 @@ function formatDecodeError(error51) {
107927
108276
  return message;
107928
108277
  }
107929
108278
 
107930
- // ../../services/backend/prompts/native/session-continuity.ts
107931
- function getNativeHandoffTurnEndGuidance(nextRole) {
107932
- return getHandoffTurnEndGuidance(nextRole);
107933
- }
107934
- function getHandoffTurnEndGuidance(nextRole) {
107935
- const lines = [
107936
- "",
107937
- "**Handoff complete. End your turn now — stop tool calls. The system will send you a message when further action is required.**"
107938
- ];
107939
- if (nextRole.toLowerCase() === "user") {
107940
- lines.push("The system delivers the next chatroom task when the user sends one.");
107941
- } else {
107942
- lines.push(`The system delivers \`${nextRole}\`'s handback when they finish — do not poll \`messages download\` while waiting. For history reconstruction tasks, \`messages download\` is the correct tool.`);
107943
- }
107944
- return lines.join(`
107945
- `);
107946
- }
107947
- function getNativeEnhancerRequestTurnEndGuidance() {
107948
- return [
107949
- "",
107950
- "**Handoff complete. End your turn now — stop tool calls. The system will send you a message when further action is required.**",
107951
- "Do **not** wait for enhancer input, poll, or monitor the enhancer job in this turn. The system delivers independent planning input as your next task when analysis completes."
107952
- ].join(`
107953
- `);
107954
- }
108279
+ // ../../services/backend/prompts/config/agent-general-knowledge.ts
108280
+ var init_agent_general_knowledge = () => {};
107955
108281
  // ../../services/backend/prompts/sections/activated-skills.ts
107956
108282
  var init_activated_skills = () => {};
107957
108283
 
@@ -108062,6 +108388,74 @@ var init_general_knowledge = __esm(() => {
108062
108388
  init_utils3();
108063
108389
  });
108064
108390
 
108391
+ // ../../services/backend/prompts/enhancer/system-prompt.ts
108392
+ var ENHANCER_ROLE_CONTRACT_LINES;
108393
+ var init_system_prompt = __esm(() => {
108394
+ init_stdin_heredoc();
108395
+ init_agent_general_knowledge();
108396
+ init_general_knowledge();
108397
+ ENHANCER_ROLE_CONTRACT_LINES = [
108398
+ "You are a single-turn, memoryless **design advisor**. Produce a high-intelligence first design for the user request; you are not an implementer.",
108399
+ "",
108400
+ "## Your role",
108401
+ "- Recover the authoritative user request and relevant history before analysis.",
108402
+ "- Inspect the repository and closely related files to understand existing patterns, constraints, and likely change surfaces.",
108403
+ "- Return **one** complete recommended design — not multiple options. The entry point verifies and delegates.",
108404
+ "- Complete frontend and data/query design sections at code granularity before implementation sequencing.",
108405
+ "- Use <additional-context> from the forwarded request as the entry point's goal; verify against downloaded history and repository evidence.",
108406
+ "- <user-message> is authoritative for what the user said; do not treat <additional-context> as a substitute.",
108407
+ "- Give concrete, evidence-backed design input with repo-relative file references and targeted code snippets when useful.",
108408
+ "- In <handoff-proofs>, complete **Proof of Principles** for how this design satisfies each quality principle.",
108409
+ "- Keep the user request as the north star. Tighten within its scope; do not invent requirements.",
108410
+ "- Your output becomes the first planning input to the stateful team entry point, which owns persistent memory, the ongoing plan, and execution.",
108411
+ "",
108412
+ `## Frontend design (${getFrontendDesignUxTriggerDescription()})`,
108413
+ "- Complete <handoff-frontend-design> with flows, per-flow UX quality checklist, component specs, and layout at code granularity.",
108414
+ "- Ground patterns in repository evidence — cite existing components; recommend one when multiple exist.",
108415
+ '- Write "Not Applicable." for the entire section when no UI changes are involved.',
108416
+ "",
108417
+ "## Large or multi-surface revisions",
108418
+ "- When the request involves refactoring, consolidation, or consistency across many surfaces, note in implementation sequence that the entry point should run `chatroom skill activate defragmentation` before delegating slices.",
108419
+ "",
108420
+ "## Data and query design (when persistence or query patterns change)",
108421
+ "- Complete <handoff-data-design> with schema, indexes, and query patterns within project limits.",
108422
+ '- Write "Not Applicable." for the entire section when no persistence changes are involved.',
108423
+ "",
108424
+ "## What you must NOT do",
108425
+ "- Do NOT implement changes, spawn subagents, or expand scope.",
108426
+ "- Do NOT propose multiple alternative approaches — one recommended design only.",
108427
+ "- Do NOT produce generic advice. Tie findings to user messages, repository evidence, and named files.",
108428
+ "- Do NOT treat <forwarded-request> as the only source of context; download message history first.",
108429
+ "- Output must match <output-template>. **Recommended implementation sequence** and **Files touched** are the last sections."
108430
+ ];
108431
+ });
108432
+
108433
+ // ../../services/backend/prompts/native/session-continuity.ts
108434
+ function getNativeHandoffTurnEndGuidance(nextRole) {
108435
+ return getHandoffTurnEndGuidance(nextRole);
108436
+ }
108437
+ function getHandoffTurnEndGuidance(nextRole) {
108438
+ const lines = [
108439
+ "",
108440
+ "**Handoff complete. End your turn now — stop tool calls. The system will send you a message when further action is required.**"
108441
+ ];
108442
+ if (nextRole.toLowerCase() === "user") {
108443
+ lines.push("The system delivers the next chatroom task when the user sends one.");
108444
+ } else {
108445
+ lines.push(`The system delivers \`${nextRole}\`'s handback when they finish — do not poll \`messages download\` while waiting. For history reconstruction tasks, \`messages download\` is the correct tool.`);
108446
+ }
108447
+ return lines.join(`
108448
+ `);
108449
+ }
108450
+ function getNativeEnhancerRequestTurnEndGuidance() {
108451
+ return [
108452
+ "",
108453
+ "**Handoff complete. End your turn now — stop tool calls. The system will send you a message when further action is required.**",
108454
+ "Do **not** wait for enhancer input, poll, or monitor the enhancer job in this turn. The system delivers independent planning input as your next task when analysis completes."
108455
+ ].join(`
108456
+ `);
108457
+ }
108458
+
108065
108459
  // ../../services/backend/prompts/cli/handoff/command.ts
108066
108460
  var init_command3 = __esm(() => {
108067
108461
  init_stdin_heredoc();
@@ -108098,18 +108492,6 @@ var init_builder2 = __esm(() => {
108098
108492
  init_sections();
108099
108493
  });
108100
108494
 
108101
- // ../../services/backend/prompts/enhancer/history-retrieval.ts
108102
- var init_history_retrieval = __esm(() => {
108103
- init_guidance();
108104
- });
108105
-
108106
- // ../../services/backend/prompts/cli/roles/enhancer.ts
108107
- var init_enhancer3 = __esm(() => {
108108
- init_history_retrieval();
108109
- init_env();
108110
- init_sections();
108111
- });
108112
-
108113
108495
  // ../../services/backend/prompts/cli/roles/planner.ts
108114
108496
  var init_planner2 = __esm(() => {
108115
108497
  init_env();
@@ -108127,12 +108509,18 @@ var init_solo2 = __esm(() => {
108127
108509
  init_sections();
108128
108510
  });
108129
108511
 
108130
- // ../../services/backend/prompts/cli/roles/fromContext.ts
108512
+ // ../../services/backend/prompts/teams/solo/prompts/fromContext.ts
108131
108513
  var init_fromContext = __esm(() => {
108514
+ init_solo2();
108515
+ });
108516
+
108517
+ // ../../services/backend/prompts/cli/roles/fromContext.ts
108518
+ var init_fromContext2 = __esm(() => {
108132
108519
  init_builder2();
108133
- init_enhancer3();
108134
108520
  init_planner2();
108135
- init_solo2();
108521
+ init_system_prompt();
108522
+ init_fromContext();
108523
+ init_utils3();
108136
108524
  });
108137
108525
 
108138
108526
  // ../../services/backend/prompts/teams/duo/prompts/builder.ts
@@ -108147,20 +108535,15 @@ var init_planner3 = __esm(() => {
108147
108535
  });
108148
108536
 
108149
108537
  // ../../services/backend/prompts/teams/duo/prompts/fromContext.ts
108150
- var init_fromContext2 = __esm(() => {
108538
+ var init_fromContext3 = __esm(() => {
108151
108539
  init_builder3();
108152
108540
  init_planner3();
108153
108541
  });
108154
-
108155
- // ../../services/backend/prompts/teams/solo/prompts/fromContext.ts
108156
- var init_fromContext3 = __esm(() => {
108157
- init_solo2();
108158
- });
108159
108542
  // ../../services/backend/prompts/selector-context.ts
108160
108543
  var init_selector_context = __esm(() => {
108161
- init_fromContext();
108162
108544
  init_fromContext2();
108163
108545
  init_fromContext3();
108546
+ init_fromContext();
108164
108547
  });
108165
108548
 
108166
108549
  // ../../services/backend/prompts/sections/role-guidance.ts
@@ -108177,12 +108560,14 @@ var init_role_identity = __esm(() => {
108177
108560
  });
108178
108561
 
108179
108562
  // ../../services/backend/prompts/native/system-prompt.ts
108180
- var init_system_prompt = __esm(() => {
108563
+ var init_system_prompt2 = __esm(() => {
108564
+ init_system_prompt();
108181
108565
  init_commands_reference();
108182
108566
  init_general_knowledge();
108183
108567
  init_role_guidance();
108184
108568
  init_role_identity();
108185
108569
  init_selector_context();
108570
+ init_utils3();
108186
108571
  });
108187
108572
 
108188
108573
  // ../../services/backend/prompts/native/task-started-content.ts
@@ -108265,6 +108650,7 @@ var init_generator = __esm(() => {
108265
108650
  init_command();
108266
108651
  init_reminder();
108267
108652
  init_system_prompt();
108653
+ init_system_prompt2();
108268
108654
  init_classification_guide();
108269
108655
  init_commands_reference();
108270
108656
  init_general_knowledge();
@@ -108280,6 +108666,21 @@ var init_generator = __esm(() => {
108280
108666
  init_review_guidelines();
108281
108667
  });
108282
108668
 
108669
+ // src/infrastructure/cli-gateway/local-handoff-gateway.ts
108670
+ function createLocalHandoffGateway() {
108671
+ return {
108672
+ handoff: (args2) => requestLocalDaemon(resolveLocalWebPort(), "cli.handoff", args2).catch((error51) => {
108673
+ if (error51 instanceof LocalDaemonServerError)
108674
+ throw error51;
108675
+ throw new Error(`CLI gateway unavailable. Start the Chatroom daemon and retry. ${error51 instanceof Error ? error51.message : String(error51)}`);
108676
+ })
108677
+ };
108678
+ }
108679
+ var init_local_handoff_gateway = __esm(() => {
108680
+ init_local_daemon();
108681
+ init_resolve_local_web_port();
108682
+ });
108683
+
108283
108684
  // src/infrastructure/deps/create-convex-command-deps.ts
108284
108685
  async function createConvexCommandDeps() {
108285
108686
  const client4 = await getConvexClient();
@@ -108305,7 +108706,8 @@ __export(exports_handoff, {
108305
108706
  handoff: () => handoff
108306
108707
  });
108307
108708
  async function createDefaultDeps10() {
108308
- return createConvexCommandDeps();
108709
+ const deps = await createConvexCommandDeps();
108710
+ return { backend: deps.backend, gateway: createLocalHandoffGateway(), session: deps.session };
108309
108711
  }
108310
108712
  function handleHandoffError(err) {
108311
108713
  return exports_Effect.sync(() => {
@@ -108376,27 +108778,24 @@ async function handoff(chatroomId, options, deps) {
108376
108778
  }
108377
108779
  var handoffEffect = (chatroomId, options) => exports_Effect.gen(function* () {
108378
108780
  const session2 = yield* SessionService;
108379
- const backend2 = yield* BackendService;
108781
+ const gateway = yield* CliGatewayService;
108380
108782
  const { role, message, nextRole } = options;
108381
- const sessionId = yield* requireSessionIdEffect((a) => ({
108382
- _tag: "NotAuthenticated",
108383
- convexUrl: a.convexUrl,
108384
- otherUrls: a.otherUrls
108385
- }));
108386
- yield* validateChatroomIdEffect(chatroomId, (id3) => ({
108387
- _tag: "InvalidChatroomId",
108388
- id: id3
108389
- }));
108390
- const result = yield* backend2.mutation(api.messages.handoff, {
108391
- sessionId,
108392
- chatroomId,
108393
- senderRole: role,
108394
- content: message,
108395
- targetRole: nextRole
108783
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
108784
+ const result = yield* exports_Effect.tryPromise({
108785
+ try: () => gateway.handoff({
108786
+ sessionId,
108787
+ chatroomId,
108788
+ senderRole: role,
108789
+ content: message,
108790
+ targetRole: nextRole
108791
+ }),
108792
+ catch: (cause3) => cause3 instanceof Error ? cause3 : new Error(String(cause3))
108396
108793
  }).pipe(exports_Effect.mapError((cause3) => {
108397
108794
  let errorData;
108398
108795
  if (cause3 instanceof ConvexError) {
108399
108796
  errorData = cause3.data;
108797
+ } else if (cause3 instanceof LocalDaemonServerError && cause3.details) {
108798
+ errorData = cause3.details;
108400
108799
  }
108401
108800
  return { _tag: "HandoffFailed", cause: cause3, errorData };
108402
108801
  }));
@@ -108422,7 +108821,8 @@ var init_handoff = __esm(() => {
108422
108821
  init_generator();
108423
108822
  init_values();
108424
108823
  init_esm();
108425
- init_api3();
108824
+ init_local_daemon();
108825
+ init_local_handoff_gateway();
108426
108826
  init_create_convex_command_deps();
108427
108827
  init_services();
108428
108828
  init_error_formatting();
@@ -109711,15 +110111,7 @@ async function activateSkill(chatroomId, skillId, options, deps) {
109711
110111
  }
109712
110112
  var listSkillsEffect = (chatroomId, _options) => exports_Effect.gen(function* () {
109713
110113
  const backend2 = yield* BackendService;
109714
- const sessionId = yield* requireSessionIdEffect((a) => ({
109715
- _tag: "NotAuthenticated",
109716
- convexUrl: a.convexUrl,
109717
- otherUrls: a.otherUrls
109718
- }));
109719
- yield* validateChatroomIdEffect(chatroomId, (id3) => ({
109720
- _tag: "InvalidChatroomId",
109721
- id: id3
109722
- }));
110114
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
109723
110115
  const skills = yield* backend2.query(api.skills.list, {
109724
110116
  sessionId,
109725
110117
  chatroomId
@@ -109739,15 +110131,7 @@ var listSkillsEffect = (chatroomId, _options) => exports_Effect.gen(function* ()
109739
110131
  }), activateSkillEffect = (chatroomId, skillId, options) => exports_Effect.gen(function* () {
109740
110132
  const session2 = yield* SessionService;
109741
110133
  const backend2 = yield* BackendService;
109742
- const sessionId = yield* requireSessionIdEffect((a) => ({
109743
- _tag: "NotAuthenticated",
109744
- convexUrl: a.convexUrl,
109745
- otherUrls: a.otherUrls
109746
- }));
109747
- yield* validateChatroomIdEffect(chatroomId, (id3) => ({
109748
- _tag: "InvalidChatroomId",
109749
- id: id3
109750
- }));
110134
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
109751
110135
  const convexUrl = yield* session2.getConvexUrl();
109752
110136
  const result = yield* backend2.mutation(api.skills.activate, {
109753
110137
  sessionId,
@@ -110163,15 +110547,7 @@ var PAGE_SIZE = 50, SINCE_PAGE_SIZE = 500, ABSOLUTE_MAX = 5000, DEFAULT_LIMIT =
110163
110547
  }), downloadMessagesEffect = (chatroomId, options) => exports_Effect.gen(function* () {
110164
110548
  const backend2 = yield* BackendService;
110165
110549
  const fs11 = yield* MessagesFsService;
110166
- const sessionId = yield* requireSessionIdEffect((a) => ({
110167
- _tag: "NotAuthenticated",
110168
- convexUrl: a.convexUrl,
110169
- otherUrls: a.otherUrls
110170
- }));
110171
- yield* validateChatroomIdEffect(chatroomId, (id3) => ({
110172
- _tag: "InvalidChatroomId",
110173
- id: id3
110174
- }));
110550
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
110175
110551
  const format4 = options.format ?? "linear";
110176
110552
  const maxDownload = parseLimit(options.limit);
110177
110553
  const outputDir = options.outputDir ?? resolveDownloadOutputDir(format4);
@@ -110442,15 +110818,7 @@ async function listSinceMessage(chatroomId, options, deps) {
110442
110818
  }
110443
110819
  var listBySenderRoleEffect = (chatroomId, options) => exports_Effect.gen(function* () {
110444
110820
  const backend2 = yield* BackendService;
110445
- const sessionId = yield* requireSessionIdEffect((a) => ({
110446
- _tag: "NotAuthenticated",
110447
- convexUrl: a.convexUrl,
110448
- otherUrls: a.otherUrls
110449
- }));
110450
- yield* validateChatroomIdEffect(chatroomId, (id3) => ({
110451
- _tag: "InvalidChatroomId",
110452
- id: id3
110453
- }));
110821
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
110454
110822
  const messages = yield* backend2.query(api.messages.listBySenderRole, {
110455
110823
  sessionId,
110456
110824
  chatroomId,
@@ -110485,15 +110853,7 @@ var listBySenderRoleEffect = (chatroomId, options) => exports_Effect.gen(functio
110485
110853
  });
110486
110854
  }), listSinceMessageEffect = (chatroomId, options) => exports_Effect.gen(function* () {
110487
110855
  const backend2 = yield* BackendService;
110488
- const sessionId = yield* requireSessionIdEffect((a) => ({
110489
- _tag: "NotAuthenticated",
110490
- convexUrl: a.convexUrl,
110491
- otherUrls: a.otherUrls
110492
- }));
110493
- yield* validateChatroomIdEffect(chatroomId, (id3) => ({
110494
- _tag: "InvalidChatroomId",
110495
- id: id3
110496
- }));
110856
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
110497
110857
  const messages = yield* backend2.query(api.messages.listSinceMessage, {
110498
110858
  sessionId,
110499
110859
  chatroomId,
@@ -110960,15 +111320,7 @@ async function viewManyArtifacts(chatroomId, options, deps) {
110960
111320
  }
110961
111321
  var createArtifactEffect = (chatroomId, options) => exports_Effect.gen(function* () {
110962
111322
  const backend2 = yield* BackendService;
110963
- const sessionId = yield* requireSessionIdEffect((a) => ({
110964
- _tag: "NotAuthenticated",
110965
- convexUrl: a.convexUrl,
110966
- otherUrls: a.otherUrls
110967
- }));
110968
- yield* validateChatroomIdEffect(chatroomId, (id3) => ({
110969
- _tag: "InvalidChatroomId",
110970
- id: id3
110971
- }));
111323
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
110972
111324
  if (!options.fromFile.endsWith(".md")) {
110973
111325
  return yield* exports_Effect.fail({
110974
111326
  _tag: "InvalidFileExtension",
@@ -111006,15 +111358,7 @@ var createArtifactEffect = (chatroomId, options) => exports_Effect.gen(function*
111006
111358
  return artifactId;
111007
111359
  }), viewArtifactEffect = (chatroomId, options) => exports_Effect.gen(function* () {
111008
111360
  const backend2 = yield* BackendService;
111009
- const sessionId = yield* requireSessionIdEffect((a) => ({
111010
- _tag: "NotAuthenticated",
111011
- convexUrl: a.convexUrl,
111012
- otherUrls: a.otherUrls
111013
- }));
111014
- yield* validateChatroomIdEffect(chatroomId, (id3) => ({
111015
- _tag: "InvalidChatroomId",
111016
- id: id3
111017
- }));
111361
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
111018
111362
  const artifact = yield* backend2.query(api.artifacts.get, {
111019
111363
  sessionId,
111020
111364
  artifactId: options.artifactId
@@ -111040,15 +111384,7 @@ var createArtifactEffect = (chatroomId, options) => exports_Effect.gen(function*
111040
111384
  });
111041
111385
  }), viewManyArtifactsEffect = (chatroomId, options) => exports_Effect.gen(function* () {
111042
111386
  const backend2 = yield* BackendService;
111043
- const sessionId = yield* requireSessionIdEffect((a) => ({
111044
- _tag: "NotAuthenticated",
111045
- convexUrl: a.convexUrl,
111046
- otherUrls: a.otherUrls
111047
- }));
111048
- yield* validateChatroomIdEffect(chatroomId, (id3) => ({
111049
- _tag: "InvalidChatroomId",
111050
- id: id3
111051
- }));
111387
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
111052
111388
  if (options.artifactIds.length === 0) {
111053
111389
  return yield* exports_Effect.fail({ _tag: "NoArtifactIds" });
111054
111390
  }
@@ -111446,12 +111782,12 @@ var init_durable_fifo_queue_schema = __esm(() => {
111446
111782
  });
111447
111783
 
111448
111784
  // src/daemon/infrastructure/outbox/lib/durable-fifo-queue-store.ts
111449
- import { mkdirSync as mkdirSync7 } from "node:fs";
111450
- import { dirname as dirname10 } from "node:path";
111451
- import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
111785
+ import { mkdirSync as mkdirSync8 } from "node:fs";
111786
+ import { dirname as dirname11 } from "node:path";
111787
+ import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
111452
111788
  function openDurableFifoQueueStore(dbPath) {
111453
- mkdirSync7(dirname10(dbPath), { recursive: true });
111454
- const db = new DatabaseSync2(dbPath);
111789
+ mkdirSync8(dirname11(dbPath), { recursive: true });
111790
+ const db = new DatabaseSync3(dbPath);
111455
111791
  for (const sql of DURABLE_FIFO_QUEUE_MIGRATIONS)
111456
111792
  db.exec(sql);
111457
111793
  db.prepare("UPDATE fifo_outbox_entries SET status='pending', updated_at=? WHERE status='in_flight'").run(Date.now());
@@ -111868,7 +112204,8 @@ var AgentLifecycleOutboxService, AgentLifecycleOutboxServiceLive = (registry3, m
111868
112204
  resumeTurnForSlot: (args2) => exports_Effect.promise(() => mgr.resumeTurnForSlot(args2)),
111869
112205
  subscribeAgentTurnEnded: (handler) => processManagerService.subscribeAgentTurnEnded(handler),
111870
112206
  subscribeAgentStarted: (handler) => processManagerService.subscribeAgentStarted(handler),
111871
- subscribeAgentSessionLost: (handler) => processManagerService.subscribeAgentSessionLost(handler)
112207
+ subscribeAgentSessionLost: (handler) => processManagerService.subscribeAgentSessionLost(handler),
112208
+ subscribeAgentTurnProgress: (handler) => processManagerService.subscribeAgentTurnProgress(handler)
111872
112209
  }), DaemonAgentProcessManagerCommandService, DaemonAgentProcessManagerCommandServiceLive = (service4) => exports_Layer.succeed(DaemonAgentProcessManagerCommandService, service4), DaemonSessionService, DaemonMutableStateService;
111873
112210
  var init_daemon_services = __esm(() => {
111874
112211
  init_esm();
@@ -112162,6 +112499,14 @@ var init_output_store = __esm(() => {
112162
112499
  RUN_ID_RE = /^[a-z0-9]+$/i;
112163
112500
  });
112164
112501
 
112502
+ // src/daemon/entry/task-handoff-repository-path.ts
112503
+ import { homedir as homedir9 } from "node:os";
112504
+ import { join as join22 } from "node:path";
112505
+ function resolveTaskHandoffRepositoryPath(machineId) {
112506
+ return join22(homedir9(), ".chatroom", "daemon", machineId, "task-handoffs.sqlite");
112507
+ }
112508
+ var init_task_handoff_repository_path = () => {};
112509
+
112165
112510
  // src/infrastructure/services/harness-spawning/rate-limiter.ts
112166
112511
  class SpawnRateLimiter {
112167
112512
  config;
@@ -112617,6 +112962,7 @@ function assembleDaemonSessionInit(args2) {
112617
112962
  convexUrl,
112618
112963
  backend: deps.backend,
112619
112964
  configurationService: agentConfigRegistry,
112965
+ handoffRepository: createTaskHandoffRepository(resolveTaskHandoffRepositoryPath(machineId)),
112620
112966
  logEvent: activeLogEvent ?? (async () => {
112621
112967
  return;
112622
112968
  })
@@ -112657,9 +113003,6 @@ class NetworkRetryError {
112657
113003
  this.attempt = attempt;
112658
113004
  }
112659
113005
  }
112660
- function getActiveLogSink() {
112661
- return activeLogSink;
112662
- }
112663
113006
  async function initDaemon(options = {}) {
112664
113007
  activeLogSink = options.logSink;
112665
113008
  activeLogEvent = options.logEvent;
@@ -112860,6 +113203,7 @@ var init_init_daemon = __esm(() => {
112860
113203
  init_daemon_startup_log();
112861
113204
  init_orphan_tracker();
112862
113205
  init_output_store();
113206
+ init_task_handoff_repository_path();
112863
113207
  init_pid();
112864
113208
  init_storage();
112865
113209
  init_client2();
@@ -112874,6 +113218,7 @@ var init_init_daemon = __esm(() => {
112874
113218
  init_agent_process_service2();
112875
113219
  init_chatroom_workspace_configuration_service();
112876
113220
  init_service_interfaces();
113221
+ init_task_service2();
112877
113222
  AUTH_WAIT_TIMEOUT_MS = 5 * 60 * 1000;
112878
113223
  initDaemonEffect = exports_Effect.gen(function* () {
112879
113224
  const lockAcquired = yield* exports_Effect.tryPromise({
@@ -117228,16 +117573,16 @@ var MAX_NAME_LENGTH = 256;
117228
117573
 
117229
117574
  // src/infrastructure/services/workspace/workspace-resolver.ts
117230
117575
  import { readFile as readFile7, readdir, stat as stat4 } from "node:fs/promises";
117231
- import { join as join22, basename, resolve as resolve4 } from "node:path";
117576
+ import { join as join23, basename, resolve as resolve4 } from "node:path";
117232
117577
  async function resolveGlobPatternStar(rootDir, cleaned) {
117233
- const parentDir = join22(rootDir, cleaned.slice(0, -2));
117578
+ const parentDir = join23(rootDir, cleaned.slice(0, -2));
117234
117579
  try {
117235
117580
  const entries2 = await readdir(parentDir, { withFileTypes: true });
117236
117581
  const dirs = [];
117237
117582
  for (const entry of entries2) {
117238
117583
  if (!entry.isDirectory())
117239
117584
  continue;
117240
- const dirPath = join22(parentDir, entry.name);
117585
+ const dirPath = join23(parentDir, entry.name);
117241
117586
  if (resolve4(dirPath).startsWith(resolve4(rootDir))) {
117242
117587
  dirs.push(dirPath);
117243
117588
  }
@@ -117248,7 +117593,7 @@ async function resolveGlobPatternStar(rootDir, cleaned) {
117248
117593
  }
117249
117594
  }
117250
117595
  async function resolveLiteralPath(rootDir, cleaned) {
117251
- const dir = join22(rootDir, cleaned);
117596
+ const dir = join23(rootDir, cleaned);
117252
117597
  try {
117253
117598
  if (!resolve4(dir).startsWith(resolve4(rootDir)))
117254
117599
  return [];
@@ -117268,7 +117613,7 @@ async function resolveGlobPattern(rootDir, pattern) {
117268
117613
  }
117269
117614
  async function readPackageJson(dir) {
117270
117615
  try {
117271
- const content = await readFile7(join22(dir, "package.json"), "utf-8");
117616
+ const content = await readFile7(join23(dir, "package.json"), "utf-8");
117272
117617
  const pkg = JSON.parse(content);
117273
117618
  return {
117274
117619
  name: pkg.name || basename(dir),
@@ -117280,7 +117625,7 @@ async function readPackageJson(dir) {
117280
117625
  }
117281
117626
  async function readPnpmWorkspacePatterns(rootDir) {
117282
117627
  try {
117283
- const content = await readFile7(join22(rootDir, "pnpm-workspace.yaml"), "utf-8");
117628
+ const content = await readFile7(join23(rootDir, "pnpm-workspace.yaml"), "utf-8");
117284
117629
  const patterns = [];
117285
117630
  let inPackages = false;
117286
117631
  for (const line of content.split(`
@@ -117307,7 +117652,7 @@ async function readPnpmWorkspacePatterns(rootDir) {
117307
117652
  }
117308
117653
  async function readPackageJsonWorkspacePatterns(rootDir) {
117309
117654
  try {
117310
- const content = await readFile7(join22(rootDir, "package.json"), "utf-8");
117655
+ const content = await readFile7(join23(rootDir, "package.json"), "utf-8");
117311
117656
  const pkg = JSON.parse(content);
117312
117657
  if (!pkg.workspaces)
117313
117658
  return [];
@@ -117356,11 +117701,11 @@ var init_workspace_resolver = () => {};
117356
117701
 
117357
117702
  // src/infrastructure/services/workspace/command-discovery.ts
117358
117703
  import { access as access2, readFile as readFile8 } from "node:fs/promises";
117359
- import { join as join23, relative, basename as basename2 } from "node:path";
117704
+ import { join as join24, relative, basename as basename2 } from "node:path";
117360
117705
  async function detectPackageManager(workingDir) {
117361
117706
  for (const { file: file2, manager } of LOCKFILE_MAP) {
117362
117707
  try {
117363
- await access2(join23(workingDir, file2));
117708
+ await access2(join24(workingDir, file2));
117364
117709
  return manager;
117365
117710
  } catch {}
117366
117711
  }
@@ -117429,7 +117774,7 @@ function collectRootScriptCommands(scripts, pm, scriptPrefix, rootSw) {
117429
117774
  }
117430
117775
  async function readRootPackageJson(workingDir, pm, scriptPrefix) {
117431
117776
  let rootPackageName = basename2(workingDir);
117432
- const pkg = await readJsonFile(join23(workingDir, "package.json"), "root package.json");
117777
+ const pkg = await readJsonFile(join24(workingDir, "package.json"), "root package.json");
117433
117778
  if (!pkg)
117434
117779
  return { commands: [], rootPackageName };
117435
117780
  if (pkg.name)
@@ -117445,7 +117790,7 @@ async function readRootPackageJson(workingDir, pm, scriptPrefix) {
117445
117790
  }
117446
117791
  async function readTurboJson(workingDir, _turboPrefix, _rootSubWorkspace) {
117447
117792
  const turboTaskNames = [];
117448
- const turbo = await readJsonFile(join23(workingDir, "turbo.json"), "turbo.json");
117793
+ const turbo = await readJsonFile(join24(workingDir, "turbo.json"), "turbo.json");
117449
117794
  if (!turbo?.tasks || typeof turbo.tasks !== "object")
117450
117795
  return turboTaskNames;
117451
117796
  for (const taskName of Object.keys(turbo.tasks)) {
@@ -117458,7 +117803,7 @@ async function readTurboJson(workingDir, _turboPrefix, _rootSubWorkspace) {
117458
117803
  async function readDenoJson(workingDir) {
117459
117804
  const commands = [];
117460
117805
  for (const fileName of ["deno.json", "deno.jsonc"]) {
117461
- const deno = await readJsonFile(join23(workingDir, fileName), fileName);
117806
+ const deno = await readJsonFile(join24(workingDir, fileName), fileName);
117462
117807
  if (!deno?.tasks || typeof deno.tasks !== "object")
117463
117808
  continue;
117464
117809
  const pkgName = deno.name ?? basename2(workingDir);
@@ -117481,7 +117826,7 @@ async function readMakefile(workingDir) {
117481
117826
  const commands = [];
117482
117827
  for (const fileName of ["Makefile", "makefile"]) {
117483
117828
  try {
117484
- const content = await readFile8(join23(workingDir, fileName), "utf-8");
117829
+ const content = await readFile8(join24(workingDir, fileName), "utf-8");
117485
117830
  const targets = parseMakefileTargets(content);
117486
117831
  if (targets.length === 0)
117487
117832
  continue;
@@ -118056,7 +118401,7 @@ async function runRestartOrchestrator(deps, event) {
118056
118401
  const { chatroomId, role } = event;
118057
118402
  markRestartOrchestratorInFlight(chatroomId, role, event.correlationId);
118058
118403
  try {
118059
- resetRoleDeliveryState(chatroomId, role);
118404
+ await deps.nativeDelivery.handleAgentRestart({ chatroomId, role });
118060
118405
  await emitPhase(deps, event, "reset");
118061
118406
  await emitPhase(deps, event, "spawn");
118062
118407
  const spawnResult = await deps.runSerializedForAgent({ chatroomId, role, workingDir: event.workingDir }, { timeoutMs: HARNESS_SESSION_READY_TIMEOUT_MS }, async (ops, context5) => {
@@ -118117,7 +118462,6 @@ var init_restart_orchestrator = __esm(() => {
118117
118462
  init_api3();
118118
118463
  init_convex_error();
118119
118464
  init_execution_kind();
118120
- init_service_interfaces();
118121
118465
  });
118122
118466
 
118123
118467
  // src/daemon/entry/bridge/agent-control-bridge.ts
@@ -118272,19 +118616,6 @@ async function startAgent2(deps, input) {
118272
118616
  });
118273
118617
  }
118274
118618
 
118275
- // src/daemon/entry/enhancer/enhancer-drain-registry.ts
118276
- function setEnhancerDrainHandler(fn2) {
118277
- drainFn = fn2;
118278
- }
118279
- async function drainPendingEnhancerJobsIfRegistered() {
118280
- if (drainFn)
118281
- await drainFn();
118282
- }
118283
- function clearEnhancerDrainHandlerForTests() {
118284
- drainFn = null;
118285
- }
118286
- var drainFn = null;
118287
-
118288
118619
  // src/daemon/entry/events/agent/on-request-start-agent.ts
118289
118620
  var onRequestStartAgentEffect = (event) => exports_Effect.gen(function* () {
118290
118621
  const processManagerService = yield* DaemonAgentProcessManagerCommandService;
@@ -118301,7 +118632,6 @@ var onRequestStartAgentEffect = (event) => exports_Effect.gen(function* () {
118301
118632
  deadline: event.deadline,
118302
118633
  wantResume: event.wantResume
118303
118634
  });
118304
- await drainPendingEnhancerJobsIfRegistered();
118305
118635
  });
118306
118636
  });
118307
118637
  var init_on_request_start_agent = __esm(() => {
@@ -118637,435 +118967,19 @@ async function dispatchCommandInboundEvent(event) {
118637
118967
  }
118638
118968
  var handler2;
118639
118969
 
118640
- // src/daemon/entry/enhancer/constants.ts
118641
- var ENHANCER_AGENT_ROLE = "enhancer", ENHANCER_AGENT_END_GRACE_MS = 3000;
118642
-
118643
- // src/daemon/entry/enhancer/enhancer-log.ts
118644
- function formatEnhancerLogLine(message) {
118645
- const trimmed = message.trimEnd();
118646
- if (trimmed.startsWith(ENHANCER_LOG_PREFIX))
118647
- return trimmed;
118648
- return `${ENHANCER_LOG_PREFIX} ${trimmed}`;
118649
- }
118650
- function createEnhancerLogWriter(logSink, context5, clock4 = () => Date.now()) {
118651
- return {
118652
- write(message) {
118653
- const formatted = formatEnhancerLogLine(message);
118654
- logSink?.write({
118655
- timestamp: clock4(),
118656
- level: "info",
118657
- source: `harness:${context5.harness}`,
118658
- stream: "stdout",
118659
- message: formatted,
118660
- metadata: {
118661
- chatroomId: context5.chatroomId,
118662
- role: ENHANCER_AGENT_ROLE,
118663
- harness: context5.harness,
118664
- ...context5.pid !== undefined ? { pid: context5.pid } : {}
118665
- }
118666
- });
118667
- }
118668
- };
118669
- }
118670
- var ENHANCER_LOG_PREFIX = "[enhancer]";
118671
- var init_enhancer_log = () => {};
118672
-
118673
- // src/daemon/entry/enhancer/enhancer-spawn-registry.ts
118674
- function registerEnhancerSpawn(handle) {
118675
- const handles = byChatroom.get(handle.chatroomId) ?? new Set;
118676
- handles.add(handle);
118677
- byChatroom.set(handle.chatroomId, handles);
118678
- }
118679
- function unregisterEnhancerSpawn(handle) {
118680
- const handles = byChatroom.get(handle.chatroomId);
118681
- if (!handles)
118682
- return;
118683
- handles.delete(handle);
118684
- if (handles.size === 0)
118685
- byChatroom.delete(handle.chatroomId);
118686
- }
118687
- async function abortEnhancerSpawnsForChatroom(chatroomId) {
118688
- const handles = byChatroom.get(chatroomId);
118689
- if (!handles)
118690
- return;
118691
- await Promise.all([...handles].map(async (handle) => {
118692
- handle.abort();
118693
- await handle.stopProcess().catch(() => {
118694
- return;
118695
- });
118696
- }));
118697
- }
118698
- var byChatroom;
118699
- var init_enhancer_spawn_registry = __esm(() => {
118700
- byChatroom = new Map;
118701
- });
118702
-
118703
- // src/daemon/entry/enhancer/job-outcome-subscription.ts
118704
- function subscribeToEnhancerJobOutcome(args2) {
118705
- let currentState = null;
118706
- let resolveOutcome = null;
118707
- let rejectOutcome = null;
118708
- let stopped = false;
118709
- const outcome = new Promise((resolve5, reject) => {
118710
- resolveOutcome = resolve5;
118711
- rejectOutcome = reject;
118712
- });
118713
- const unsub = args2.wsClient.onUpdate(api.web.enhancer.index.getJobOutcome, {
118714
- sessionId: args2.sessionId,
118715
- chatroomId: args2.chatroomId,
118716
- jobId: args2.jobId
118717
- }, (state) => {
118718
- if (stopped)
118719
- return;
118720
- if (!state) {
118721
- rejectOutcome?.(new Error("Enhancer job not found"));
118722
- resolveOutcome = null;
118723
- rejectOutcome = null;
118724
- return;
118725
- }
118726
- currentState = state;
118727
- if (state.status === "complete" || state.status === "failed" || state.status === "cancelled") {
118728
- resolveOutcome?.(state);
118729
- resolveOutcome = null;
118730
- rejectOutcome = null;
118731
- }
118732
- }, (error51) => {
118733
- if (stopped)
118734
- return;
118735
- rejectOutcome?.(error51);
118736
- resolveOutcome = null;
118737
- rejectOutcome = null;
118738
- });
118739
- return {
118740
- outcome,
118741
- getCurrentState: () => currentState,
118742
- stop: () => {
118743
- if (stopped)
118744
- return;
118745
- stopped = true;
118746
- unsub();
118747
- resolveOutcome = null;
118748
- rejectOutcome = null;
118749
- }
118750
- };
118751
- }
118752
- var init_job_outcome_subscription = __esm(() => {
118753
- init_api3();
118754
- });
118755
-
118756
- // src/daemon/entry/enhancer/wait-for-enhancer-job.ts
118757
- async function waitForEnhancerJobResolution(params) {
118758
- const { sessionId, chatroomId, jobId, wsClient: wsClient2, onFailure, onSalvageComplete, log: log4, signal } = params;
118759
- const writeLog = (message) => log4?.write(message);
118760
- let outcome = null;
118761
- let salvagedText = "";
118762
- let agentEndTimer = null;
118763
- let resolveWait = null;
118764
- const waitPromise = new Promise((resolve5) => {
118765
- resolveWait = resolve5;
118766
- });
118767
- const subscription = subscribeToEnhancerJobOutcome({
118768
- wsClient: wsClient2,
118769
- sessionId,
118770
- chatroomId,
118771
- jobId
118772
- });
118773
- const finish = (resolution) => {
118774
- if (outcome)
118775
- return;
118776
- outcome = resolution;
118777
- resolveWait?.();
118778
- resolveWait = null;
118779
- };
118780
- const onAbort = () => finish("aborted");
118781
- if (signal?.aborted)
118782
- onAbort();
118783
- else
118784
- signal?.addEventListener("abort", onAbort, { once: true });
118785
- const failAfterAgentEnd = () => {
118786
- writeLog("agent_end: turn ended without complete — failing terminal");
118787
- onFailure("Agent exited without completing enhancer job", true);
118788
- finish("failed");
118789
- };
118790
- subscription.outcome.then((state) => {
118791
- if (outcome)
118792
- return;
118793
- const resolution = state.status === "complete" ? "complete" : "failed";
118794
- if (resolution === "complete")
118795
- writeLog(`completed job=${jobId}`);
118796
- finish(resolution);
118797
- }).catch((error51) => {
118798
- if (outcome)
118799
- return;
118800
- writeLog(`enhancer outcome subscription failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
118801
- onFailure("Enhancer job outcome subscription failed", false);
118802
- finish("failed");
118803
- });
118804
- params.onAssistantText?.((text) => {
118805
- salvagedText += text;
118806
- });
118807
- params.onAgentEnd?.(() => {
118808
- if (outcome)
118809
- return;
118810
- agentEndTimer = setTimeout(() => {
118811
- if (outcome)
118812
- return;
118813
- const state = subscription.getCurrentState();
118814
- if (state?.status === "complete") {
118815
- finish("complete");
118816
- return;
118817
- }
118818
- if (state?.status !== "running") {
118819
- failAfterAgentEnd();
118820
- return;
118821
- }
118822
- const trimmed = salvagedText.trim();
118823
- if (!trimmed || !onSalvageComplete) {
118824
- failAfterAgentEnd();
118825
- return;
118826
- }
118827
- onSalvageComplete(trimmed).catch(() => {
118828
- failAfterAgentEnd();
118829
- });
118830
- }, ENHANCER_AGENT_END_GRACE_MS);
118831
- });
118832
- params.onExit(() => {
118833
- if (outcome)
118834
- return;
118835
- onFailure("Agent process exited without completing enhancer job", false);
118836
- finish("failed");
118837
- });
118838
- await waitPromise;
118839
- if (agentEndTimer)
118840
- clearTimeout(agentEndTimer);
118841
- subscription.stop();
118842
- signal?.removeEventListener("abort", onAbort);
118843
- return outcome ?? "failed";
118844
- }
118845
- var init_wait_for_enhancer_job = __esm(() => {
118846
- init_job_outcome_subscription();
118847
- });
118848
-
118849
- // src/daemon/entry/enhancer-inbound-registry.ts
118850
- function registerEnhancerInboundHandler(h) {
118970
+ // src/daemon/entry/file-inbound-registry.ts
118971
+ function registerFileInboundHandler(h) {
118851
118972
  handler3 = h;
118852
118973
  }
118853
- function unregisterEnhancerInboundHandler() {
118974
+ function unregisterFileInboundHandler() {
118854
118975
  handler3 = undefined;
118855
118976
  }
118856
- async function dispatchEnhancerInboundEvent(event) {
118977
+ async function dispatchFileInboundEvent(event) {
118857
118978
  if (handler3)
118858
118979
  await handler3(event);
118859
118980
  }
118860
118981
  var handler3;
118861
118982
 
118862
- // src/daemon/entry/enhancer/job-subscriber.ts
118863
- async function processEnhancerJobForSpawn(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, job, inFlight2, logSink) {
118864
- if (inFlight2.has(job.jobId))
118865
- return;
118866
- if (isChatroomStopScopeActive(job.chatroomId))
118867
- return;
118868
- inFlight2.add(job.jobId);
118869
- let claimed = false;
118870
- let chatroomId = job.chatroomId;
118871
- let jobId = job.jobId;
118872
- let spawnResult = null;
118873
- let service4 = null;
118874
- let abortController = null;
118875
- let spawnHandle = null;
118876
- let agentEndSeen = false;
118877
- let resolveAgentEnd;
118878
- let agentEndPromise = Promise.resolve();
118879
- let log4 = createEnhancerLogWriter(logSink, {
118880
- chatroomId: job.chatroomId,
118881
- harness: "unknown"
118882
- });
118883
- try {
118884
- const claim = await backend2.mutation(api.daemon.enhancer.index.claimForSpawn, {
118885
- sessionId,
118886
- jobId: job.jobId,
118887
- machineId
118888
- });
118889
- if (!claim.claimed)
118890
- return;
118891
- claimed = true;
118892
- log4.write(`claimed job=${job.jobId} chatroom=${job.chatroomId}`);
118893
- const payload = await backend2.query(api.daemon.enhancer.index.getTaskDeliveryForJob, {
118894
- sessionId,
118895
- jobId: job.jobId,
118896
- convexUrl
118897
- });
118898
- chatroomId = payload.chatroomId;
118899
- jobId = payload.jobId;
118900
- log4 = createEnhancerLogWriter(logSink, {
118901
- chatroomId: payload.chatroomId,
118902
- harness: payload.agentHarness,
118903
- pid: undefined
118904
- });
118905
- log4.write(`spawning harness=${payload.agentHarness} model=${payload.model} job=${payload.jobId}`);
118906
- service4 = agentServices.get(payload.agentHarness) ?? null;
118907
- if (!service4) {
118908
- await backend2.mutation(api.web.enhancer.index.recordAttemptFailure, {
118909
- sessionId,
118910
- chatroomId: payload.chatroomId,
118911
- jobId: payload.jobId,
118912
- error: `Harness ${payload.agentHarness} not available on machine`
118913
- });
118914
- return;
118915
- }
118916
- const spawned = await service4.spawn({
118917
- workingDir: payload.workingDir,
118918
- prompt: createSpawnPrompt(payload.taskDeliveryOutput),
118919
- systemPrompt: payload.systemPrompt,
118920
- model: payload.model,
118921
- context: {
118922
- machineId,
118923
- chatroomId: payload.chatroomId,
118924
- role: ENHANCER_AGENT_ROLE
118925
- },
118926
- resolvedConvexUrl: convexUrl
118927
- });
118928
- spawnResult = spawned;
118929
- abortController = new AbortController;
118930
- spawnHandle = {
118931
- jobId: payload.jobId,
118932
- chatroomId: payload.chatroomId,
118933
- abort: () => abortController?.abort(),
118934
- stopProcess: async () => {
118935
- await service4?.stop(spawned.pid);
118936
- }
118937
- };
118938
- registerEnhancerSpawn(spawnHandle);
118939
- log4 = createEnhancerLogWriter(logSink, {
118940
- chatroomId: payload.chatroomId,
118941
- harness: payload.agentHarness,
118942
- pid: spawned.pid
118943
- });
118944
- spawned.onLogLine?.((line) => {
118945
- log4.write(line);
118946
- });
118947
- agentEndPromise = new Promise((resolve5) => {
118948
- resolveAgentEnd = resolve5;
118949
- });
118950
- spawned.onAgentEnd?.(() => {
118951
- if (agentEndSeen)
118952
- return;
118953
- agentEndSeen = true;
118954
- resolveAgentEnd?.();
118955
- });
118956
- await waitForEnhancerJobResolution({
118957
- sessionId,
118958
- chatroomId: payload.chatroomId,
118959
- jobId: payload.jobId,
118960
- wsClient: wsClient2,
118961
- log: log4,
118962
- onAssistantText: spawned.onAssistantText ? (cb) => spawned.onAssistantText?.(cb) : undefined,
118963
- onAgentEnd: spawned.onAgentEnd ? (cb) => spawned.onAgentEnd?.(cb) : undefined,
118964
- onExit: (cb) => spawned.onExit(() => cb()),
118965
- onSalvageComplete: async (content) => {
118966
- await backend2.mutation(api.web.enhancer.index.complete, {
118967
- sessionId,
118968
- chatroomId: payload.chatroomId,
118969
- jobId: payload.jobId,
118970
- enhancedContent: content
118971
- });
118972
- },
118973
- onFailure: async (error51, forceTerminal) => {
118974
- await backend2.mutation(api.web.enhancer.index.recordAttemptFailure, {
118975
- sessionId,
118976
- chatroomId: payload.chatroomId,
118977
- jobId: payload.jobId,
118978
- error: error51,
118979
- ...forceTerminal ? { forceTerminal: true } : {}
118980
- });
118981
- },
118982
- signal: abortController.signal
118983
- });
118984
- if (!abortController.signal.aborted)
118985
- log4.write(`completed job=${jobId}`);
118986
- } catch (err) {
118987
- log4.write(`error: ${err instanceof Error ? err.message : String(err)}`);
118988
- if (claimed) {
118989
- await backend2.mutation(api.web.enhancer.index.recordAttemptFailure, {
118990
- sessionId,
118991
- chatroomId,
118992
- jobId,
118993
- error: err instanceof Error ? err.message : String(err)
118994
- });
118995
- }
118996
- } finally {
118997
- inFlight2.delete(job.jobId);
118998
- if (spawnHandle)
118999
- unregisterEnhancerSpawn(spawnHandle);
119000
- if (spawnResult && service4) {
119001
- if (!agentEndSeen)
119002
- await Promise.race([
119003
- agentEndPromise,
119004
- new Promise((resolve5) => setTimeout(resolve5, ENHANCER_AGENT_END_GRACE_MS))
119005
- ]);
119006
- try {
119007
- await service4.stop(spawnResult.pid);
119008
- } catch {}
119009
- }
119010
- }
119011
- }
119012
- function processEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, jobs, inFlight2, logSink) {
119013
- for (const job of jobs ?? []) {
119014
- processEnhancerJobForSpawn(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, job, inFlight2, logSink);
119015
- }
119016
- }
119017
- async function drainPendingEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, inFlight2, logSink) {
119018
- const jobs = await backend2.query(api.daemon.enhancer.index.pendingForMachine, {
119019
- sessionId,
119020
- machineId
119021
- });
119022
- if (!jobs?.length)
119023
- return;
119024
- processEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, jobs, inFlight2, logSink);
119025
- }
119026
- function startEnhancerJobSubscriber(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, logSink) {
119027
- const inFlight2 = new Set;
119028
- registerEnhancerInboundHandler(async () => {
119029
- await drainPendingEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, inFlight2, logSink);
119030
- });
119031
- setEnhancerDrainHandler(() => drainPendingEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, inFlight2, logSink));
119032
- return {
119033
- stop: () => {
119034
- unregisterEnhancerInboundHandler();
119035
- clearEnhancerDrainHandlerForTests();
119036
- },
119037
- drainPendingEnhancerJobs: () => drainPendingEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, inFlight2, logSink)
119038
- };
119039
- }
119040
- var init_job_subscriber = __esm(() => {
119041
- init_enhancer_log();
119042
- init_enhancer_spawn_registry();
119043
- init_wait_for_enhancer_job();
119044
- init_api3();
119045
- init_service_interfaces();
119046
- });
119047
-
119048
- // src/daemon/entry/enhancer/start-subscriptions.ts
119049
- function startEnhancerSubscriptions(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, logSink) {
119050
- return startEnhancerJobSubscriber(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, logSink);
119051
- }
119052
- var init_start_subscriptions2 = __esm(() => {
119053
- init_job_subscriber();
119054
- });
119055
-
119056
- // src/daemon/entry/file-inbound-registry.ts
119057
- function registerFileInboundHandler(h) {
119058
- handler4 = h;
119059
- }
119060
- function unregisterFileInboundHandler() {
119061
- handler4 = undefined;
119062
- }
119063
- async function dispatchFileInboundEvent(event) {
119064
- if (handler4)
119065
- await handler4(event);
119066
- }
119067
- var handler4;
119068
-
119069
118983
  // src/daemon/entry/files/file-content-classifier.ts
119070
118984
  function extensionOf(path3) {
119071
118985
  const lastDot = path3.lastIndexOf(".");
@@ -119184,7 +119098,7 @@ var init_assert_registered_working_dir = __esm(() => {
119184
119098
 
119185
119099
  // src/infrastructure/services/workspace/workspace-path-security.ts
119186
119100
  import { realpath } from "node:fs/promises";
119187
- import { dirname as dirname11, isAbsolute, relative as relative2, resolve as resolve5, sep } from "node:path";
119101
+ import { dirname as dirname12, isAbsolute, relative as relative2, resolve as resolve5, sep } from "node:path";
119188
119102
  import { gunzipSync } from "node:zlib";
119189
119103
  function validateRelativePathSegments(filePath) {
119190
119104
  if (filePath.includes("\x00"))
@@ -119212,7 +119126,7 @@ function assertContained(workspaceRoot, targetPath) {
119212
119126
  return { ok: true };
119213
119127
  }
119214
119128
  async function resolveNonExistentTarget(workspaceRoot, candidate) {
119215
- let current = dirname11(candidate);
119129
+ let current = dirname12(candidate);
119216
119130
  for (;; ) {
119217
119131
  try {
119218
119132
  const ancestorReal = await realpath(current);
@@ -119230,7 +119144,7 @@ async function resolveNonExistentTarget(workspaceRoot, candidate) {
119230
119144
  if (code2 !== "ENOENT") {
119231
119145
  return { ok: false, error: "Invalid file path" };
119232
119146
  }
119233
- const parent = dirname11(current);
119147
+ const parent = dirname12(current);
119234
119148
  if (parent === current) {
119235
119149
  return { ok: false, error: "Path escapes workspace" };
119236
119150
  }
@@ -120034,16 +119948,16 @@ var require_ignore = __commonJS((exports, module) => {
120034
119948
  // src/infrastructure/services/workspace/workspace-ignore.ts
120035
119949
  import { promises as fsPromises } from "node:fs";
120036
119950
  import { readFile as readFile10 } from "node:fs/promises";
120037
- import { homedir as homedir9 } from "node:os";
119951
+ import { homedir as homedir10 } from "node:os";
120038
119952
  import path3 from "node:path";
120039
119953
  function normalizeRelativePath(relativePath) {
120040
119954
  return relativePath.replace(/\\/g, "/").replace(/^\.?\//, "").replace(/\/+$/, "");
120041
119955
  }
120042
119956
  function expandUserPath(filePath) {
120043
119957
  if (filePath === "~")
120044
- return homedir9();
119958
+ return homedir10();
120045
119959
  if (filePath.startsWith("~/"))
120046
- return path3.join(homedir9(), filePath.slice(2));
119960
+ return path3.join(homedir10(), filePath.slice(2));
120047
119961
  return filePath;
120048
119962
  }
120049
119963
  function readExcludesFileFromGitconfig(content) {
@@ -120074,11 +119988,11 @@ async function readGitconfigExcludesFilePath(configPath) {
120074
119988
  return path3.isAbsolute(raw) ? expandUserPath(raw) : path3.resolve(path3.dirname(configPath), raw);
120075
119989
  }
120076
119990
  async function resolveGitExtraExcludeFilePaths(rootDir) {
120077
- const globalConfig2 = process.env.GIT_CONFIG_GLOBAL?.length ? process.env.GIT_CONFIG_GLOBAL : path3.join(homedir9(), ".gitconfig");
119991
+ const globalConfig2 = process.env.GIT_CONFIG_GLOBAL?.length ? process.env.GIT_CONFIG_GLOBAL : path3.join(homedir10(), ".gitconfig");
120078
119992
  const localConfig = path3.join(rootDir, ".git", "config");
120079
119993
  let excludesFile = await readGitconfigExcludesFilePath(localConfig) ?? await readGitconfigExcludesFilePath(globalConfig2);
120080
119994
  if (!excludesFile) {
120081
- excludesFile = path3.join(process.env.XDG_CONFIG_HOME?.length ? process.env.XDG_CONFIG_HOME : path3.join(homedir9(), ".config"), "git", "ignore");
119995
+ excludesFile = path3.join(process.env.XDG_CONFIG_HOME?.length ? process.env.XDG_CONFIG_HOME : path3.join(homedir10(), ".config"), "git", "ignore");
120082
119996
  }
120083
119997
  const candidates = [excludesFile, path3.join(rootDir, ".git", "info", "exclude")];
120084
119998
  const existing = [];
@@ -121214,9 +121128,9 @@ class NodeFsHandler {
121214
121128
  if (this.fsw.closed) {
121215
121129
  return;
121216
121130
  }
121217
- const dirname13 = sp.dirname(file2);
121131
+ const dirname14 = sp.dirname(file2);
121218
121132
  const basename4 = sp.basename(file2);
121219
- const parent = this.fsw._getWatchedDir(dirname13);
121133
+ const parent = this.fsw._getWatchedDir(dirname14);
121220
121134
  let prevStats = stats;
121221
121135
  if (parent.has(basename4))
121222
121136
  return;
@@ -121243,7 +121157,7 @@ class NodeFsHandler {
121243
121157
  prevStats = newStats2;
121244
121158
  }
121245
121159
  } catch (error51) {
121246
- this.fsw._remove(dirname13, basename4);
121160
+ this.fsw._remove(dirname14, basename4);
121247
121161
  }
121248
121162
  } else if (parent.has(basename4)) {
121249
121163
  const at = newStats.atimeMs;
@@ -122632,8 +122546,8 @@ function diffPathIndexes(previous, next4) {
122632
122546
  // src/infrastructure/services/workspace/workspace-sync-state.ts
122633
122547
  import { createHash as createHash7, randomUUID as randomUUID12 } from "node:crypto";
122634
122548
  import * as fs11 from "node:fs/promises";
122635
- import { homedir as homedir10 } from "node:os";
122636
- import { join as join26 } from "node:path";
122549
+ import { homedir as homedir11 } from "node:os";
122550
+ import { join as join27 } from "node:path";
122637
122551
  function workspaceKeyFor(workingDir) {
122638
122552
  const normalized = normalizeWorkingDirForLookup(workingDir);
122639
122553
  return createHash7("sha256").update(normalized).digest("hex").slice(0, 16);
@@ -122647,10 +122561,10 @@ function buildPathIndex(entries2) {
122647
122561
  }
122648
122562
  function manifestPath(machineId, workingDir) {
122649
122563
  const key = workspaceKeyFor(workingDir);
122650
- return join26(SYNC_STATE_DIR, machineId, key, "manifest.json");
122564
+ return join27(SYNC_STATE_DIR, machineId, key, "manifest.json");
122651
122565
  }
122652
122566
  async function ensureDir(filePath) {
122653
- await fs11.mkdir(join26(filePath, ".."), { recursive: true, mode: 448 });
122567
+ await fs11.mkdir(join27(filePath, ".."), { recursive: true, mode: 448 });
122654
122568
  }
122655
122569
  async function loadWorkspaceSyncManifest(machineId, workingDir) {
122656
122570
  try {
@@ -122710,7 +122624,7 @@ function entriesFromPathIndex(paths) {
122710
122624
  }
122711
122625
  var SYNC_STATE_VERSION = "2", SYNC_STATE_DIR;
122712
122626
  var init_workspace_sync_state = __esm(() => {
122713
- SYNC_STATE_DIR = join26(homedir10(), ".chatroom", "sync-state");
122627
+ SYNC_STATE_DIR = join27(homedir11(), ".chatroom", "sync-state");
122714
122628
  });
122715
122629
 
122716
122630
  // src/infrastructure/services/workspace/workspace-file-tree-coordinator.ts
@@ -123088,12 +123002,12 @@ var init_durable_coalescing_state_schema = __esm(() => {
123088
123002
  });
123089
123003
 
123090
123004
  // src/daemon/infrastructure/outbox/lib/durable-coalescing-state-store.ts
123091
- import { mkdirSync as mkdirSync8 } from "node:fs";
123092
- import { dirname as dirname14 } from "node:path";
123093
- import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
123005
+ import { mkdirSync as mkdirSync9 } from "node:fs";
123006
+ import { dirname as dirname15 } from "node:path";
123007
+ import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
123094
123008
  function openDurableCoalescingStateStore(path8) {
123095
- mkdirSync8(dirname14(path8), { recursive: true });
123096
- const db = new DatabaseSync3(path8);
123009
+ mkdirSync9(dirname15(path8), { recursive: true });
123010
+ const db = new DatabaseSync4(path8);
123097
123011
  for (const sql of DURABLE_COALESCING_STATE_MIGRATIONS)
123098
123012
  db.exec(sql);
123099
123013
  db.prepare("UPDATE coalescing_outbox_entries SET status='pending' WHERE status='in_flight'").run();
@@ -124081,7 +123995,7 @@ var init_file_write_request_priority = __esm(() => {
124081
123995
 
124082
123996
  // src/daemon/entry/files/file-write-fulfillment.ts
124083
123997
  import { access as access4, mkdir as mkdir10, rename as rename4, rm as rm4, writeFile as writeFile10 } from "node:fs/promises";
124084
- import { dirname as dirname15 } from "node:path";
123998
+ import { dirname as dirname16 } from "node:path";
124085
123999
  function isTerminalFileWriteError(errorMessage) {
124086
124000
  const terminalMessages = new Set([
124087
124001
  "Invalid file path",
@@ -124180,7 +124094,7 @@ async function validateWriteOperation(operation, absolutePath) {
124180
124094
  }
124181
124095
  async function writePayloadToDisk(absolutePath, operation, content) {
124182
124096
  if (operation === "create") {
124183
- await mkdir10(dirname15(absolutePath), { recursive: true });
124097
+ await mkdir10(dirname16(absolutePath), { recursive: true });
124184
124098
  await writeFile10(absolutePath, content, { flag: "wx" });
124185
124099
  return;
124186
124100
  }
@@ -124258,7 +124172,7 @@ async function fulfillOneFileWriteRequest(session2, request2) {
124258
124172
  });
124259
124173
  return;
124260
124174
  }
124261
- await mkdir10(dirname15(targetResolved.absolutePath), { recursive: true });
124175
+ await mkdir10(dirname16(targetResolved.absolutePath), { recursive: true });
124262
124176
  await rename4(resolved.absolutePath, targetResolved.absolutePath);
124263
124177
  await completeWriteRequest(session2, claimed._id, claimed.revision, { status: "done" });
124264
124178
  const elapsed4 = Date.now() - startTime;
@@ -125333,16 +125247,16 @@ var init_workspace_list_subscription = __esm(() => {
125333
125247
 
125334
125248
  // src/daemon/entry/workspace-git-inbound-registry.ts
125335
125249
  function registerWorkspaceGitInboundHandler(h) {
125336
- handler5 = h;
125250
+ handler4 = h;
125337
125251
  }
125338
125252
  function unregisterWorkspaceGitInboundHandler() {
125339
- handler5 = undefined;
125253
+ handler4 = undefined;
125340
125254
  }
125341
125255
  async function dispatchWorkspaceGitInboundEvent(event) {
125342
- if (handler5)
125343
- await handler5(event);
125256
+ if (handler4)
125257
+ await handler4(event);
125344
125258
  }
125345
- var handler5;
125259
+ var handler4;
125346
125260
 
125347
125261
  // src/events/lifecycle/on-daemon-shutdown.ts
125348
125262
  var onDaemonShutdownEffect;
@@ -125360,6 +125274,19 @@ var init_on_daemon_shutdown = __esm(() => {
125360
125274
  const activeAgents = agentPm.listActive();
125361
125275
  if (activeAgents.length > 0) {
125362
125276
  console.log(`[${formatTimestamp()}] Stopping ${activeAgents.length} agent(s) locally...`);
125277
+ yield* exports_Effect.promise(async () => {
125278
+ try {
125279
+ const tasksReleased = await session2.backend.mutation(api.daemon.taskStatus.releaseMachineTasks, {
125280
+ sessionId: session2.sessionId,
125281
+ machineId: session2.machineId
125282
+ });
125283
+ if (tasksReleased > 0) {
125284
+ console.log(`[${formatTimestamp()}] Released ${tasksReleased} in-flight task(s) to pending`);
125285
+ }
125286
+ } catch (error51) {
125287
+ console.log(`[${formatTimestamp()}] ⚠️ Failed to release in-flight tasks on shutdown: ${error51 instanceof Error ? error51.message : String(error51)}`);
125288
+ }
125289
+ });
125363
125290
  let totalStopped = 0;
125364
125291
  let totalFailed = 0;
125365
125292
  yield* exports_Effect.all(activeAgents.map(({ chatroomId, role }) => exports_Effect.promise(() => agentPm.stopAgent({ chatroomId, role, reason: "daemon.shutdown" })).pipe(exports_Effect.tap(() => exports_Effect.sync(() => {
@@ -125389,7 +125316,6 @@ function createDaemonRuntime(deps) {
125389
125316
  let workspaceListSubscriptionHandle = null;
125390
125317
  let logObserverSubscriptionHandle = null;
125391
125318
  let agenticQueryWorkerHandle = null;
125392
- let enhancerWorkerHandle = null;
125393
125319
  let taskInboxHandle = null;
125394
125320
  let agentConfigRegistryHandle = null;
125395
125321
  const activeSessions = new Map;
@@ -125430,7 +125356,6 @@ function createDaemonRuntime(deps) {
125430
125356
  taskInboxHandle?.stop();
125431
125357
  logObserverSubscriptionHandle?.stop();
125432
125358
  agenticQueryWorkerHandle?.stop();
125433
- enhancerWorkerHandle?.stop();
125434
125359
  };
125435
125360
  const shutdown = async () => {
125436
125361
  if (isShuttingDown)
@@ -125548,7 +125473,6 @@ function createDaemonRuntime(deps) {
125548
125473
  backend: session2.backend,
125549
125474
  convexUrl: session2.convexUrl
125550
125475
  }, activeSessions, harnesses);
125551
- enhancerWorkerHandle = startEnhancerSubscriptions(session2.sessionId, session2.machineId, session2.convexUrl, deps.wsClient, session2.backend, session2.agentServices, getActiveLogSink());
125552
125476
  console.log(`
125553
125477
  Listening for commands...`);
125554
125478
  console.log(`Press Ctrl+C to stop
@@ -125585,7 +125509,6 @@ var init_daemon_runtime = __esm(() => {
125585
125509
  init_start_subscriptions();
125586
125510
  init_command_dispatch();
125587
125511
  init_daemon_services();
125588
- init_start_subscriptions2();
125589
125512
  init_file_content_subscription();
125590
125513
  init_file_tree_subscription();
125591
125514
  init_file_write_subscription();
@@ -125593,7 +125516,6 @@ var init_daemon_runtime = __esm(() => {
125593
125516
  init_orphan_tracker();
125594
125517
  init_command_run_subscription();
125595
125518
  init_log_observer_sync();
125596
- init_init_daemon();
125597
125519
  init_task_inbox_runtime();
125598
125520
  init_git_subscription();
125599
125521
  init_api3();
@@ -125636,21 +125558,6 @@ function createCommandRouterDeps() {
125636
125558
  }
125637
125559
  var init_command_bridge = () => {};
125638
125560
 
125639
- // src/daemon/domain/usecase/process-enhancer-job.ts
125640
- async function processEnhancerJobInbound(deps, event) {
125641
- await deps.dispatchInbound(event);
125642
- }
125643
-
125644
- // src/daemon/entry/bridge/enhancer-bridge.ts
125645
- function createEnhancerRouterDeps() {
125646
- return {
125647
- deliverInbound: async (event) => {
125648
- await processEnhancerJobInbound({ dispatchInbound: dispatchEnhancerInboundEvent }, event);
125649
- }
125650
- };
125651
- }
125652
- var init_enhancer_bridge = () => {};
125653
-
125654
125561
  // src/daemon/domain/usecase/fulfill-file-content-request.ts
125655
125562
  async function fulfillFileContentRequest(deps, event) {
125656
125563
  await deps.dispatchInbound(event);
@@ -125720,14 +125627,12 @@ function createDefaultEventRouterDeps() {
125720
125627
  command: createCommandRouterDeps(),
125721
125628
  workspaceGit: createWorkspaceGitRouterDeps(),
125722
125629
  file: createFileRouterDeps(),
125723
- agenticQuery: createAgenticQueryRouterDeps(),
125724
- enhancer: createEnhancerRouterDeps()
125630
+ agenticQuery: createAgenticQueryRouterDeps()
125725
125631
  };
125726
125632
  }
125727
125633
  var init_default_router_deps = __esm(() => {
125728
125634
  init_agentic_query_bridge();
125729
125635
  init_command_bridge();
125730
- init_enhancer_bridge();
125731
125636
  init_file_bridge();
125732
125637
  init_workspace_git_bridge();
125733
125638
  });
@@ -126008,7 +125913,6 @@ async function executeChatroomStopCommand(args2) {
126008
125913
  args2.apm.markStopIntent(args2.chatroomId, args2.role, "user.stop", undefined, args2.workingDir);
126009
125914
  else if (!args2.workingDir) {
126010
125915
  args2.apm.markChatroomStopIntent(args2.chatroomId, "user.stop");
126011
- await abortEnhancerSpawnsForChatroom(args2.chatroomId);
126012
125916
  }
126013
125917
  const confirmedDeps = args2.apm.getConfirmedStopAdapterDeps();
126014
125918
  const targets = await args2.apm.discoverStopTargets(args2.chatroomId);
@@ -126041,15 +125945,14 @@ async function executeChatroomStopCommand(args2) {
126041
125945
  }
126042
125946
  var init_execute_chatroom_stop_command = __esm(() => {
126043
125947
  init_reliability();
126044
- init_enhancer_spawn_registry();
126045
125948
  init_agent_process_service2();
126046
125949
  });
126047
125950
 
126048
125951
  // src/daemon/entry/persistence-path.ts
126049
- import { homedir as homedir11 } from "node:os";
126050
- import { join as join27 } from "node:path";
125952
+ import { homedir as homedir12 } from "node:os";
125953
+ import { join as join28 } from "node:path";
126051
125954
  function resolvePersistenceDbPath(machineId) {
126052
- return join27(homedir11(), ".chatroom", "daemon", machineId, "events.sqlite");
125955
+ return join28(homedir12(), ".chatroom", "daemon", machineId, "events.sqlite");
126053
125956
  }
126054
125957
  var init_persistence_path = () => {};
126055
125958
 
@@ -126067,13 +125970,6 @@ async function handleCommandInbound(deps, event) {
126067
125970
  }
126068
125971
  }
126069
125972
 
126070
- // src/daemon/domain/usecase/handle-enhancer-inbound.ts
126071
- async function handleEnhancerInbound(deps, event) {
126072
- if (deps.deliverInbound) {
126073
- await deps.deliverInbound(event);
126074
- }
126075
- }
126076
-
126077
125973
  // src/daemon/domain/usecase/handle-file-inbound.ts
126078
125974
  async function handleFileInbound(deps, event) {
126079
125975
  if (deps.deliverInbound) {
@@ -126109,9 +126005,6 @@ async function routeInboundEvent(deps, event) {
126109
126005
  case "agentic-query.prompt":
126110
126006
  await handleAgenticQueryInbound(deps.agenticQuery, event);
126111
126007
  break;
126112
- case "enhancer.job-assigned":
126113
- await handleEnhancerInbound(deps.enhancer, event);
126114
- break;
126115
126008
  default:
126116
126009
  }
126117
126010
  }
@@ -126219,80 +126112,6 @@ var init_command_run = __esm(() => {
126219
126112
  init_api3();
126220
126113
  });
126221
126114
 
126222
- // src/daemon/infrastructure/convex/subscribers/enhancer-job.ts
126223
- function startEnhancerJobSubscriber2(deps, onEvent) {
126224
- const seen = new Set;
126225
- const watchers = new Map;
126226
- let stopped = false;
126227
- let refreshInFlight = false;
126228
- const onPendingJobs = (jobs) => {
126229
- if (!jobs?.length)
126230
- return;
126231
- for (const job of jobs) {
126232
- if (job == null || typeof job !== "object" || !("jobId" in job))
126233
- continue;
126234
- if (seen.has(job.jobId))
126235
- continue;
126236
- seen.add(job.jobId);
126237
- onEvent({ type: "enhancer.job-assigned", jobId: job.jobId });
126238
- }
126239
- };
126240
- const onError3 = (err) => {
126241
- console.warn(`[daemon] enhancer-job subscriber error: ${err instanceof Error ? err.message : String(err)}`);
126242
- };
126243
- const watchChatroom = (chatroomId) => {
126244
- if (watchers.has(chatroomId) || stopped)
126245
- return;
126246
- const unsub = deps.wsClient.onUpdate(api.daemon.enhancer.index.pendingForChatroom, {
126247
- sessionId: deps.sessionId,
126248
- machineId: deps.machineId,
126249
- chatroomId
126250
- }, onPendingJobs, onError3);
126251
- watchers.set(chatroomId, unsub);
126252
- };
126253
- const refreshChatrooms = async () => {
126254
- if (refreshInFlight)
126255
- return;
126256
- refreshInFlight = true;
126257
- try {
126258
- const workspaces = await deps.wsClient.query(api.workspaces.listWorkspacesForMachine, {
126259
- sessionId: deps.sessionId,
126260
- machineId: deps.machineId
126261
- });
126262
- if (stopped)
126263
- return;
126264
- const roomIds = [
126265
- ...new Set((workspaces ?? []).map((ws) => ws.chatroomId))
126266
- ];
126267
- for (const [roomId, unsub] of watchers) {
126268
- if (!roomIds.includes(roomId)) {
126269
- unsub();
126270
- watchers.delete(roomId);
126271
- }
126272
- }
126273
- for (const roomId of roomIds)
126274
- watchChatroom(roomId);
126275
- } catch (error51) {
126276
- console.warn(`[daemon] enhancer-job room refresh failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
126277
- } finally {
126278
- refreshInFlight = false;
126279
- }
126280
- };
126281
- refreshChatrooms();
126282
- return {
126283
- async stop() {
126284
- stopped = true;
126285
- for (const unsub of watchers.values())
126286
- unsub();
126287
- watchers.clear();
126288
- },
126289
- refreshChatrooms
126290
- };
126291
- }
126292
- var init_enhancer_job = __esm(() => {
126293
- init_api3();
126294
- });
126295
-
126296
126115
  // src/daemon/infrastructure/convex/subscribers/pending-file-request-dedup.ts
126297
126116
  function pendingConvexId(req) {
126298
126117
  if (req._id == null)
@@ -126609,7 +126428,6 @@ function startAllSubscribers(deps) {
126609
126428
  const onEvent = (event) => {
126610
126429
  routeInboundEvent(deps.router, event);
126611
126430
  };
126612
- const enhancerJob = startEnhancerJobSubscriber2(deps, onEvent);
126613
126431
  const gitRequest = startGitRequestSubscriber(deps, onEvent);
126614
126432
  const machineCommands = startMachineCommandInboxSubscriber(deps, async (claimed) => {
126615
126433
  if (claimed.type === "agent.stop") {
@@ -126640,8 +126458,7 @@ function startAllSubscribers(deps) {
126640
126458
  fileContent.stop(),
126641
126459
  fileWrite.stop(),
126642
126460
  agenticQuerySession.stop(),
126643
- agenticQueryPrompt.stop(),
126644
- enhancerJob.stop()
126461
+ agenticQueryPrompt.stop()
126645
126462
  ]);
126646
126463
  }
126647
126464
  };
@@ -126651,7 +126468,6 @@ var init_subscriber_registry = __esm(() => {
126651
126468
  init_agentic_query_prompt();
126652
126469
  init_agentic_query_session();
126653
126470
  init_command_run();
126654
- init_enhancer_job();
126655
126471
  init_file_content_request();
126656
126472
  init_file_tree_release_request();
126657
126473
  init_file_tree_request();
@@ -126700,12 +126516,12 @@ var init_schema = __esm(() => {
126700
126516
  });
126701
126517
 
126702
126518
  // src/daemon/infrastructure/persistence/open-database.ts
126703
- import { mkdirSync as mkdirSync9 } from "node:fs";
126704
- import { dirname as dirname16 } from "node:path";
126705
- import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
126519
+ import { mkdirSync as mkdirSync10 } from "node:fs";
126520
+ import { dirname as dirname17 } from "node:path";
126521
+ import { DatabaseSync as DatabaseSync5 } from "node:sqlite";
126706
126522
  function openDatabase(dbPath) {
126707
- mkdirSync9(dirname16(dbPath), { recursive: true });
126708
- const db = new DatabaseSync4(dbPath);
126523
+ mkdirSync10(dirname17(dbPath), { recursive: true });
126524
+ const db = new DatabaseSync5(dbPath);
126709
126525
  for (const sql of MIGRATIONS2) {
126710
126526
  db.exec(sql);
126711
126527
  }
@@ -134200,19 +134016,19 @@ function routeRequest(req, res, deps) {
134200
134016
 
134201
134017
  // src/daemon/local-web/server/serve-static.ts
134202
134018
  import { existsSync as existsSync10, readFileSync as readFileSync11 } from "node:fs";
134203
- import { dirname as dirname17, join as join28 } from "node:path";
134019
+ import { dirname as dirname18, join as join29 } from "node:path";
134204
134020
  import { fileURLToPath as fileURLToPath7 } from "node:url";
134205
134021
  function clientDistCandidates(here) {
134206
134022
  return [
134207
- join28(here, "client/build"),
134208
- join28(here, "../client/build"),
134209
- join28(here, "../src/daemon/local-web/client/build")
134023
+ join29(here, "client/build"),
134024
+ join29(here, "../client/build"),
134025
+ join29(here, "../src/daemon/local-web/client/build")
134210
134026
  ];
134211
134027
  }
134212
- function resolveClientDistDir(here = dirname17(fileURLToPath7(import.meta.url))) {
134028
+ function resolveClientDistDir(here = dirname18(fileURLToPath7(import.meta.url))) {
134213
134029
  const candidates = clientDistCandidates(here);
134214
134030
  for (const dir of candidates) {
134215
- if (existsSync10(join28(dir, "index.html")))
134031
+ if (existsSync10(join29(dir, "index.html")))
134216
134032
  return dir;
134217
134033
  }
134218
134034
  return candidates[0];
@@ -134225,7 +134041,7 @@ function tryServeStatic(req, res, distDir) {
134225
134041
  return false;
134226
134042
  }
134227
134043
  const safePath = pathname === "/" ? "/index.html" : pathname;
134228
- const filePath = join28(distDir, safePath);
134044
+ const filePath = join29(distDir, safePath);
134229
134045
  if (!filePath.startsWith(distDir)) {
134230
134046
  res.writeHead(403);
134231
134047
  res.end();
@@ -134242,7 +134058,7 @@ function tryServeStatic(req, res, distDir) {
134242
134058
  }
134243
134059
  return true;
134244
134060
  }
134245
- const indexPath = join28(distDir, "index.html");
134061
+ const indexPath = join29(distDir, "index.html");
134246
134062
  if (existsSync10(indexPath)) {
134247
134063
  res.writeHead(200, { "Content-Type": "text/html" });
134248
134064
  if (req.method === "HEAD") {
@@ -134291,6 +134107,14 @@ function normalizeError(err) {
134291
134107
  details: err.flatten()
134292
134108
  };
134293
134109
  }
134110
+ if (err instanceof ConvexError) {
134111
+ const data = err.data;
134112
+ return {
134113
+ code: data.code ?? "internal_error",
134114
+ message: data.message ?? err.message,
134115
+ details: err.data
134116
+ };
134117
+ }
134294
134118
  if (err instanceof Error) {
134295
134119
  const code2 = "code" in err && typeof err.code === "string" ? err.code : "internal_error";
134296
134120
  if (code2 === "not_found" || code2 === "validation_error" || code2 === "unauthorized") {
@@ -134301,6 +134125,7 @@ function normalizeError(err) {
134301
134125
  return { code: "internal_error", message: "Unknown error" };
134302
134126
  }
134303
134127
  var init_normalize_error = __esm(() => {
134128
+ init_values();
134304
134129
  init_zod();
134305
134130
  });
134306
134131
 
@@ -134431,6 +134256,16 @@ function extractAck(args2) {
134431
134256
  }
134432
134257
  return { payload: args2[0], ack: undefined };
134433
134258
  }
134259
+ async function resolveDebugStatePayload(deps, payload) {
134260
+ if (!deps.debugState)
134261
+ throw new Error("daemon debug state is not configured");
134262
+ const { chatroomId } = debugStateInputSchema.parse(payload ?? {});
134263
+ const data = await deps.debugState(chatroomId);
134264
+ if (data && typeof data === "object" && deps.cliGateway) {
134265
+ data.cliGateway = deps.cliGateway.debugState();
134266
+ }
134267
+ return data;
134268
+ }
134434
134269
  function registerSocketHandlers(io2, deps) {
134435
134270
  const logHistory = deps.logRepo ? createLogHistoryUseCase({ reader: deps.logRepo }) : undefined;
134436
134271
  const eventStreamHistory = deps.logRepo ? createEventStreamHistoryUseCase({ reader: deps.logRepo }) : undefined;
@@ -134450,13 +134285,22 @@ function registerSocketHandlers(io2, deps) {
134450
134285
  callAck(ack, { ok: false, error: normalizeError(err) });
134451
134286
  }
134452
134287
  });
134288
+ socket.on("cli.handoff", async (...args2) => {
134289
+ const { payload, ack } = extractAck(args2);
134290
+ try {
134291
+ if (!deps.cliGateway)
134292
+ throw new Error("CLI gateway is not configured");
134293
+ const data = await deps.cliGateway.handoff(payload);
134294
+ callAck(ack, { ok: true, data });
134295
+ } catch (err) {
134296
+ callAck(ack, { ok: false, error: normalizeError(err) });
134297
+ }
134298
+ });
134453
134299
  socket.on("daemon.debug.state", async (...args2) => {
134454
134300
  const { payload, ack } = extractAck(args2);
134455
134301
  try {
134456
- if (!deps.debugState)
134457
- throw new Error("daemon debug state is not configured");
134458
- const { chatroomId } = debugStateInputSchema.parse(payload ?? {});
134459
- callAck(ack, { ok: true, data: await deps.debugState(chatroomId) });
134302
+ const data = await resolveDebugStatePayload(deps, payload);
134303
+ callAck(ack, { ok: true, data });
134460
134304
  } catch (err) {
134461
134305
  callAck(ack, { ok: false, error: normalizeError(err) });
134462
134306
  }
@@ -134640,7 +134484,8 @@ async function startLocalWebServer(config4, deps = {}) {
134640
134484
  eventStreamHub: deps.eventStreamHub,
134641
134485
  backend: deps.backend,
134642
134486
  sessionId: deps.sessionId,
134643
- debugState: deps.debugState
134487
+ debugState: deps.debugState,
134488
+ cliGateway: deps.cliGateway
134644
134489
  });
134645
134490
  return {
134646
134491
  port: boundPort,
@@ -134696,6 +134541,80 @@ function createLogStreamHub() {
134696
134541
  };
134697
134542
  }
134698
134543
 
134544
+ // src/daemon/services/cli-gateway-service/index.ts
134545
+ function createCliGatewayService(deps) {
134546
+ const logs2 = [];
134547
+ const writeLog = (message) => {
134548
+ logs2.push({ timestamp: Date.now(), message });
134549
+ if (logs2.length > 100)
134550
+ logs2.shift();
134551
+ (deps.log ?? console.log)(`[CliGateway:${message}]`);
134552
+ };
134553
+ return {
134554
+ handoff: async (args2) => {
134555
+ writeLog(`request handoff chatroom=${args2.chatroomId} role=${args2.senderRole}`);
134556
+ const taskIdsBeforeCommit = getActiveTaskIdsForRole(deps, args2);
134557
+ const result = await commitHandoffMutation(deps, args2, writeLog);
134558
+ writeLog(`backend result success=${result.success}`);
134559
+ if (result.success) {
134560
+ await syncTaskServiceAfterHandoff(deps, args2, result, taskIdsBeforeCommit, writeLog);
134561
+ }
134562
+ return result;
134563
+ },
134564
+ debugState: () => ({ port: deps.port, logs: [...logs2] })
134565
+ };
134566
+ }
134567
+ function getActiveTaskIdsForRole(deps, args2) {
134568
+ return deps.taskService.listTasksForRole?.(args2.chatroomId, args2.senderRole).filter((task) => task.status === "acknowledged" || task.status === "in_progress").map((task) => task.taskId) ?? [];
134569
+ }
134570
+ async function commitHandoffMutation(deps, args2, writeLog) {
134571
+ try {
134572
+ return await deps.backend.mutation(api.messages.handoff, args2);
134573
+ } catch (error51) {
134574
+ writeLog(`backend failure=${error51 instanceof Error ? error51.message : String(error51)}`);
134575
+ throw error51;
134576
+ }
134577
+ }
134578
+ async function syncTaskServiceAfterHandoff(deps, args2, result, taskIdsBeforeCommit, writeLog) {
134579
+ try {
134580
+ const nextTask = await adoptNextHandoffTask(deps, args2, result);
134581
+ await deps.taskService.recordHandoffOutcome({
134582
+ chatroomId: args2.chatroomId,
134583
+ role: args2.senderRole,
134584
+ targetRole: args2.targetRole,
134585
+ nextTask,
134586
+ taskIds: taskIdsBeforeCommit
134587
+ });
134588
+ writeLog(`task-service updated chatroom=${args2.chatroomId} role=${args2.senderRole}` + (nextTask ? ` adopted=${nextTask.taskId}` : ""));
134589
+ } catch (error51) {
134590
+ writeLog(`post-commit sync failure chatroom=${args2.chatroomId} error=${error51 instanceof Error ? error51.message : String(error51)}`);
134591
+ await recordFallbackHandoffOutcome(deps, args2, writeLog);
134592
+ }
134593
+ }
134594
+ async function adoptNextHandoffTask(deps, args2, result) {
134595
+ if (!result.newTaskId || args2.targetRole.toLowerCase() === "user")
134596
+ return;
134597
+ return await deps.taskService.loadAssignedTaskForAction({
134598
+ chatroomId: args2.chatroomId,
134599
+ role: args2.targetRole,
134600
+ taskId: result.newTaskId
134601
+ }) ?? undefined;
134602
+ }
134603
+ async function recordFallbackHandoffOutcome(deps, args2, writeLog) {
134604
+ try {
134605
+ await deps.taskService.recordHandoffOutcome({
134606
+ chatroomId: args2.chatroomId,
134607
+ role: args2.senderRole,
134608
+ targetRole: args2.targetRole
134609
+ });
134610
+ } catch (cleanupError) {
134611
+ writeLog(`post-commit cleanup failure chatroom=${args2.chatroomId} error=${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`);
134612
+ }
134613
+ }
134614
+ var init_cli_gateway_service = __esm(() => {
134615
+ init_api3();
134616
+ });
134617
+
134699
134618
  // src/daemon/entry/start-daemon.ts
134700
134619
  async function startDaemon() {
134701
134620
  let resolveBoundPort;
@@ -134730,6 +134649,11 @@ async function startDaemon() {
134730
134649
  logEvent: init2.logEvent
134731
134650
  });
134732
134651
  const localWebPort = resolveLocalWebPort();
134652
+ const cliGateway = createCliGatewayService({
134653
+ backend: init2.backend,
134654
+ port: localWebPort,
134655
+ taskService: init2.taskService
134656
+ });
134733
134657
  const debugState = async (chatroomId) => {
134734
134658
  const persisted = await loadDaemonState(init2.machineId);
134735
134659
  const persistedAgents = Object.fromEntries(Object.entries(persisted?.agents ?? {}).filter(([key]) => key.startsWith(`${chatroomId}/`)));
@@ -134787,7 +134711,8 @@ async function startDaemon() {
134787
134711
  eventStreamHub,
134788
134712
  backend: init2.backend,
134789
134713
  sessionId: init2.sessionId,
134790
- debugState
134714
+ debugState,
134715
+ cliGateway
134791
134716
  });
134792
134717
  resolveBoundPort(localWeb.port);
134793
134718
  const subscribers = startAllSubscribers({
@@ -134848,6 +134773,7 @@ var init_start_daemon = __esm(() => {
134848
134773
  init_log_repository();
134849
134774
  init_socket3();
134850
134775
  init_create_local_web_server();
134776
+ init_cli_gateway_service();
134851
134777
  });
134852
134778
 
134853
134779
  // src/commands/machine/daemon-start/index.ts
@@ -136104,4 +136030,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
136104
136030
  });
136105
136031
  program2.parse();
136106
136032
 
136107
- //# debugId=812FE18B15DD283564756E2164756E21
136033
+ //# debugId=CB5CA35B81D3EFBC64756E2164756E21