chatroom-cli 1.117.5 → 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;
@@ -112333,9 +112678,10 @@ var init_convex_agent_config_gateway = __esm(() => {
112333
112678
  model: exports_external.string(),
112334
112679
  workingDir: exports_external.string(),
112335
112680
  status: exports_external.enum(WorkspaceAgentConfigInboxStatus),
112336
- createdAt: exports_external.number()
112681
+ createdAt: exports_external.number(),
112682
+ processedAt: exports_external.number().optional()
112337
112683
  });
112338
- agentConfigInboxEventSchema = agentConfigInboxRowSchema.transform(({ _id: eventId, ...row }) => ({ ...row, eventId }));
112684
+ agentConfigInboxEventSchema = agentConfigInboxRowSchema.transform(({ _id: eventId, processedAt: _processedAt, ...row }) => ({ ...row, eventId }));
112339
112685
  });
112340
112686
 
112341
112687
  // src/daemon/services/chatroom-workspace-configuration-service/service/agent-config-registry.ts
@@ -112616,6 +112962,7 @@ function assembleDaemonSessionInit(args2) {
112616
112962
  convexUrl,
112617
112963
  backend: deps.backend,
112618
112964
  configurationService: agentConfigRegistry,
112965
+ handoffRepository: createTaskHandoffRepository(resolveTaskHandoffRepositoryPath(machineId)),
112619
112966
  logEvent: activeLogEvent ?? (async () => {
112620
112967
  return;
112621
112968
  })
@@ -112656,9 +113003,6 @@ class NetworkRetryError {
112656
113003
  this.attempt = attempt;
112657
113004
  }
112658
113005
  }
112659
- function getActiveLogSink() {
112660
- return activeLogSink;
112661
- }
112662
113006
  async function initDaemon(options = {}) {
112663
113007
  activeLogSink = options.logSink;
112664
113008
  activeLogEvent = options.logEvent;
@@ -112859,6 +113203,7 @@ var init_init_daemon = __esm(() => {
112859
113203
  init_daemon_startup_log();
112860
113204
  init_orphan_tracker();
112861
113205
  init_output_store();
113206
+ init_task_handoff_repository_path();
112862
113207
  init_pid();
112863
113208
  init_storage();
112864
113209
  init_client2();
@@ -112873,6 +113218,7 @@ var init_init_daemon = __esm(() => {
112873
113218
  init_agent_process_service2();
112874
113219
  init_chatroom_workspace_configuration_service();
112875
113220
  init_service_interfaces();
113221
+ init_task_service2();
112876
113222
  AUTH_WAIT_TIMEOUT_MS = 5 * 60 * 1000;
112877
113223
  initDaemonEffect = exports_Effect.gen(function* () {
112878
113224
  const lockAcquired = yield* exports_Effect.tryPromise({
@@ -117227,16 +117573,16 @@ var MAX_NAME_LENGTH = 256;
117227
117573
 
117228
117574
  // src/infrastructure/services/workspace/workspace-resolver.ts
117229
117575
  import { readFile as readFile7, readdir, stat as stat4 } from "node:fs/promises";
117230
- import { join as join22, basename, resolve as resolve4 } from "node:path";
117576
+ import { join as join23, basename, resolve as resolve4 } from "node:path";
117231
117577
  async function resolveGlobPatternStar(rootDir, cleaned) {
117232
- const parentDir = join22(rootDir, cleaned.slice(0, -2));
117578
+ const parentDir = join23(rootDir, cleaned.slice(0, -2));
117233
117579
  try {
117234
117580
  const entries2 = await readdir(parentDir, { withFileTypes: true });
117235
117581
  const dirs = [];
117236
117582
  for (const entry of entries2) {
117237
117583
  if (!entry.isDirectory())
117238
117584
  continue;
117239
- const dirPath = join22(parentDir, entry.name);
117585
+ const dirPath = join23(parentDir, entry.name);
117240
117586
  if (resolve4(dirPath).startsWith(resolve4(rootDir))) {
117241
117587
  dirs.push(dirPath);
117242
117588
  }
@@ -117247,7 +117593,7 @@ async function resolveGlobPatternStar(rootDir, cleaned) {
117247
117593
  }
117248
117594
  }
117249
117595
  async function resolveLiteralPath(rootDir, cleaned) {
117250
- const dir = join22(rootDir, cleaned);
117596
+ const dir = join23(rootDir, cleaned);
117251
117597
  try {
117252
117598
  if (!resolve4(dir).startsWith(resolve4(rootDir)))
117253
117599
  return [];
@@ -117267,7 +117613,7 @@ async function resolveGlobPattern(rootDir, pattern) {
117267
117613
  }
117268
117614
  async function readPackageJson(dir) {
117269
117615
  try {
117270
- const content = await readFile7(join22(dir, "package.json"), "utf-8");
117616
+ const content = await readFile7(join23(dir, "package.json"), "utf-8");
117271
117617
  const pkg = JSON.parse(content);
117272
117618
  return {
117273
117619
  name: pkg.name || basename(dir),
@@ -117279,7 +117625,7 @@ async function readPackageJson(dir) {
117279
117625
  }
117280
117626
  async function readPnpmWorkspacePatterns(rootDir) {
117281
117627
  try {
117282
- const content = await readFile7(join22(rootDir, "pnpm-workspace.yaml"), "utf-8");
117628
+ const content = await readFile7(join23(rootDir, "pnpm-workspace.yaml"), "utf-8");
117283
117629
  const patterns = [];
117284
117630
  let inPackages = false;
117285
117631
  for (const line of content.split(`
@@ -117306,7 +117652,7 @@ async function readPnpmWorkspacePatterns(rootDir) {
117306
117652
  }
117307
117653
  async function readPackageJsonWorkspacePatterns(rootDir) {
117308
117654
  try {
117309
- const content = await readFile7(join22(rootDir, "package.json"), "utf-8");
117655
+ const content = await readFile7(join23(rootDir, "package.json"), "utf-8");
117310
117656
  const pkg = JSON.parse(content);
117311
117657
  if (!pkg.workspaces)
117312
117658
  return [];
@@ -117355,11 +117701,11 @@ var init_workspace_resolver = () => {};
117355
117701
 
117356
117702
  // src/infrastructure/services/workspace/command-discovery.ts
117357
117703
  import { access as access2, readFile as readFile8 } from "node:fs/promises";
117358
- import { join as join23, relative, basename as basename2 } from "node:path";
117704
+ import { join as join24, relative, basename as basename2 } from "node:path";
117359
117705
  async function detectPackageManager(workingDir) {
117360
117706
  for (const { file: file2, manager } of LOCKFILE_MAP) {
117361
117707
  try {
117362
- await access2(join23(workingDir, file2));
117708
+ await access2(join24(workingDir, file2));
117363
117709
  return manager;
117364
117710
  } catch {}
117365
117711
  }
@@ -117428,7 +117774,7 @@ function collectRootScriptCommands(scripts, pm, scriptPrefix, rootSw) {
117428
117774
  }
117429
117775
  async function readRootPackageJson(workingDir, pm, scriptPrefix) {
117430
117776
  let rootPackageName = basename2(workingDir);
117431
- const pkg = await readJsonFile(join23(workingDir, "package.json"), "root package.json");
117777
+ const pkg = await readJsonFile(join24(workingDir, "package.json"), "root package.json");
117432
117778
  if (!pkg)
117433
117779
  return { commands: [], rootPackageName };
117434
117780
  if (pkg.name)
@@ -117444,7 +117790,7 @@ async function readRootPackageJson(workingDir, pm, scriptPrefix) {
117444
117790
  }
117445
117791
  async function readTurboJson(workingDir, _turboPrefix, _rootSubWorkspace) {
117446
117792
  const turboTaskNames = [];
117447
- const turbo = await readJsonFile(join23(workingDir, "turbo.json"), "turbo.json");
117793
+ const turbo = await readJsonFile(join24(workingDir, "turbo.json"), "turbo.json");
117448
117794
  if (!turbo?.tasks || typeof turbo.tasks !== "object")
117449
117795
  return turboTaskNames;
117450
117796
  for (const taskName of Object.keys(turbo.tasks)) {
@@ -117457,7 +117803,7 @@ async function readTurboJson(workingDir, _turboPrefix, _rootSubWorkspace) {
117457
117803
  async function readDenoJson(workingDir) {
117458
117804
  const commands = [];
117459
117805
  for (const fileName of ["deno.json", "deno.jsonc"]) {
117460
- const deno = await readJsonFile(join23(workingDir, fileName), fileName);
117806
+ const deno = await readJsonFile(join24(workingDir, fileName), fileName);
117461
117807
  if (!deno?.tasks || typeof deno.tasks !== "object")
117462
117808
  continue;
117463
117809
  const pkgName = deno.name ?? basename2(workingDir);
@@ -117480,7 +117826,7 @@ async function readMakefile(workingDir) {
117480
117826
  const commands = [];
117481
117827
  for (const fileName of ["Makefile", "makefile"]) {
117482
117828
  try {
117483
- const content = await readFile8(join23(workingDir, fileName), "utf-8");
117829
+ const content = await readFile8(join24(workingDir, fileName), "utf-8");
117484
117830
  const targets = parseMakefileTargets(content);
117485
117831
  if (targets.length === 0)
117486
117832
  continue;
@@ -118055,7 +118401,7 @@ async function runRestartOrchestrator(deps, event) {
118055
118401
  const { chatroomId, role } = event;
118056
118402
  markRestartOrchestratorInFlight(chatroomId, role, event.correlationId);
118057
118403
  try {
118058
- resetRoleDeliveryState(chatroomId, role);
118404
+ await deps.nativeDelivery.handleAgentRestart({ chatroomId, role });
118059
118405
  await emitPhase(deps, event, "reset");
118060
118406
  await emitPhase(deps, event, "spawn");
118061
118407
  const spawnResult = await deps.runSerializedForAgent({ chatroomId, role, workingDir: event.workingDir }, { timeoutMs: HARNESS_SESSION_READY_TIMEOUT_MS }, async (ops, context5) => {
@@ -118116,7 +118462,6 @@ var init_restart_orchestrator = __esm(() => {
118116
118462
  init_api3();
118117
118463
  init_convex_error();
118118
118464
  init_execution_kind();
118119
- init_service_interfaces();
118120
118465
  });
118121
118466
 
118122
118467
  // src/daemon/entry/bridge/agent-control-bridge.ts
@@ -118271,19 +118616,6 @@ async function startAgent2(deps, input) {
118271
118616
  });
118272
118617
  }
118273
118618
 
118274
- // src/daemon/entry/enhancer/enhancer-drain-registry.ts
118275
- function setEnhancerDrainHandler(fn2) {
118276
- drainFn = fn2;
118277
- }
118278
- async function drainPendingEnhancerJobsIfRegistered() {
118279
- if (drainFn)
118280
- await drainFn();
118281
- }
118282
- function clearEnhancerDrainHandlerForTests() {
118283
- drainFn = null;
118284
- }
118285
- var drainFn = null;
118286
-
118287
118619
  // src/daemon/entry/events/agent/on-request-start-agent.ts
118288
118620
  var onRequestStartAgentEffect = (event) => exports_Effect.gen(function* () {
118289
118621
  const processManagerService = yield* DaemonAgentProcessManagerCommandService;
@@ -118300,7 +118632,6 @@ var onRequestStartAgentEffect = (event) => exports_Effect.gen(function* () {
118300
118632
  deadline: event.deadline,
118301
118633
  wantResume: event.wantResume
118302
118634
  });
118303
- await drainPendingEnhancerJobsIfRegistered();
118304
118635
  });
118305
118636
  });
118306
118637
  var init_on_request_start_agent = __esm(() => {
@@ -118636,435 +118967,19 @@ async function dispatchCommandInboundEvent(event) {
118636
118967
  }
118637
118968
  var handler2;
118638
118969
 
118639
- // src/daemon/entry/enhancer/constants.ts
118640
- var ENHANCER_AGENT_ROLE = "enhancer", ENHANCER_AGENT_END_GRACE_MS = 3000;
118641
-
118642
- // src/daemon/entry/enhancer/enhancer-log.ts
118643
- function formatEnhancerLogLine(message) {
118644
- const trimmed = message.trimEnd();
118645
- if (trimmed.startsWith(ENHANCER_LOG_PREFIX))
118646
- return trimmed;
118647
- return `${ENHANCER_LOG_PREFIX} ${trimmed}`;
118648
- }
118649
- function createEnhancerLogWriter(logSink, context5, clock4 = () => Date.now()) {
118650
- return {
118651
- write(message) {
118652
- const formatted = formatEnhancerLogLine(message);
118653
- logSink?.write({
118654
- timestamp: clock4(),
118655
- level: "info",
118656
- source: `harness:${context5.harness}`,
118657
- stream: "stdout",
118658
- message: formatted,
118659
- metadata: {
118660
- chatroomId: context5.chatroomId,
118661
- role: ENHANCER_AGENT_ROLE,
118662
- harness: context5.harness,
118663
- ...context5.pid !== undefined ? { pid: context5.pid } : {}
118664
- }
118665
- });
118666
- }
118667
- };
118668
- }
118669
- var ENHANCER_LOG_PREFIX = "[enhancer]";
118670
- var init_enhancer_log = () => {};
118671
-
118672
- // src/daemon/entry/enhancer/enhancer-spawn-registry.ts
118673
- function registerEnhancerSpawn(handle) {
118674
- const handles = byChatroom.get(handle.chatroomId) ?? new Set;
118675
- handles.add(handle);
118676
- byChatroom.set(handle.chatroomId, handles);
118677
- }
118678
- function unregisterEnhancerSpawn(handle) {
118679
- const handles = byChatroom.get(handle.chatroomId);
118680
- if (!handles)
118681
- return;
118682
- handles.delete(handle);
118683
- if (handles.size === 0)
118684
- byChatroom.delete(handle.chatroomId);
118685
- }
118686
- async function abortEnhancerSpawnsForChatroom(chatroomId) {
118687
- const handles = byChatroom.get(chatroomId);
118688
- if (!handles)
118689
- return;
118690
- await Promise.all([...handles].map(async (handle) => {
118691
- handle.abort();
118692
- await handle.stopProcess().catch(() => {
118693
- return;
118694
- });
118695
- }));
118696
- }
118697
- var byChatroom;
118698
- var init_enhancer_spawn_registry = __esm(() => {
118699
- byChatroom = new Map;
118700
- });
118701
-
118702
- // src/daemon/entry/enhancer/job-outcome-subscription.ts
118703
- function subscribeToEnhancerJobOutcome(args2) {
118704
- let currentState = null;
118705
- let resolveOutcome = null;
118706
- let rejectOutcome = null;
118707
- let stopped = false;
118708
- const outcome = new Promise((resolve5, reject) => {
118709
- resolveOutcome = resolve5;
118710
- rejectOutcome = reject;
118711
- });
118712
- const unsub = args2.wsClient.onUpdate(api.web.enhancer.index.getJobOutcome, {
118713
- sessionId: args2.sessionId,
118714
- chatroomId: args2.chatroomId,
118715
- jobId: args2.jobId
118716
- }, (state) => {
118717
- if (stopped)
118718
- return;
118719
- if (!state) {
118720
- rejectOutcome?.(new Error("Enhancer job not found"));
118721
- resolveOutcome = null;
118722
- rejectOutcome = null;
118723
- return;
118724
- }
118725
- currentState = state;
118726
- if (state.status === "complete" || state.status === "failed" || state.status === "cancelled") {
118727
- resolveOutcome?.(state);
118728
- resolveOutcome = null;
118729
- rejectOutcome = null;
118730
- }
118731
- }, (error51) => {
118732
- if (stopped)
118733
- return;
118734
- rejectOutcome?.(error51);
118735
- resolveOutcome = null;
118736
- rejectOutcome = null;
118737
- });
118738
- return {
118739
- outcome,
118740
- getCurrentState: () => currentState,
118741
- stop: () => {
118742
- if (stopped)
118743
- return;
118744
- stopped = true;
118745
- unsub();
118746
- resolveOutcome = null;
118747
- rejectOutcome = null;
118748
- }
118749
- };
118750
- }
118751
- var init_job_outcome_subscription = __esm(() => {
118752
- init_api3();
118753
- });
118754
-
118755
- // src/daemon/entry/enhancer/wait-for-enhancer-job.ts
118756
- async function waitForEnhancerJobResolution(params) {
118757
- const { sessionId, chatroomId, jobId, wsClient: wsClient2, onFailure, onSalvageComplete, log: log4, signal } = params;
118758
- const writeLog = (message) => log4?.write(message);
118759
- let outcome = null;
118760
- let salvagedText = "";
118761
- let agentEndTimer = null;
118762
- let resolveWait = null;
118763
- const waitPromise = new Promise((resolve5) => {
118764
- resolveWait = resolve5;
118765
- });
118766
- const subscription = subscribeToEnhancerJobOutcome({
118767
- wsClient: wsClient2,
118768
- sessionId,
118769
- chatroomId,
118770
- jobId
118771
- });
118772
- const finish = (resolution) => {
118773
- if (outcome)
118774
- return;
118775
- outcome = resolution;
118776
- resolveWait?.();
118777
- resolveWait = null;
118778
- };
118779
- const onAbort = () => finish("aborted");
118780
- if (signal?.aborted)
118781
- onAbort();
118782
- else
118783
- signal?.addEventListener("abort", onAbort, { once: true });
118784
- const failAfterAgentEnd = () => {
118785
- writeLog("agent_end: turn ended without complete — failing terminal");
118786
- onFailure("Agent exited without completing enhancer job", true);
118787
- finish("failed");
118788
- };
118789
- subscription.outcome.then((state) => {
118790
- if (outcome)
118791
- return;
118792
- const resolution = state.status === "complete" ? "complete" : "failed";
118793
- if (resolution === "complete")
118794
- writeLog(`completed job=${jobId}`);
118795
- finish(resolution);
118796
- }).catch((error51) => {
118797
- if (outcome)
118798
- return;
118799
- writeLog(`enhancer outcome subscription failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
118800
- onFailure("Enhancer job outcome subscription failed", false);
118801
- finish("failed");
118802
- });
118803
- params.onAssistantText?.((text) => {
118804
- salvagedText += text;
118805
- });
118806
- params.onAgentEnd?.(() => {
118807
- if (outcome)
118808
- return;
118809
- agentEndTimer = setTimeout(() => {
118810
- if (outcome)
118811
- return;
118812
- const state = subscription.getCurrentState();
118813
- if (state?.status === "complete") {
118814
- finish("complete");
118815
- return;
118816
- }
118817
- if (state?.status !== "running") {
118818
- failAfterAgentEnd();
118819
- return;
118820
- }
118821
- const trimmed = salvagedText.trim();
118822
- if (!trimmed || !onSalvageComplete) {
118823
- failAfterAgentEnd();
118824
- return;
118825
- }
118826
- onSalvageComplete(trimmed).catch(() => {
118827
- failAfterAgentEnd();
118828
- });
118829
- }, ENHANCER_AGENT_END_GRACE_MS);
118830
- });
118831
- params.onExit(() => {
118832
- if (outcome)
118833
- return;
118834
- onFailure("Agent process exited without completing enhancer job", false);
118835
- finish("failed");
118836
- });
118837
- await waitPromise;
118838
- if (agentEndTimer)
118839
- clearTimeout(agentEndTimer);
118840
- subscription.stop();
118841
- signal?.removeEventListener("abort", onAbort);
118842
- return outcome ?? "failed";
118843
- }
118844
- var init_wait_for_enhancer_job = __esm(() => {
118845
- init_job_outcome_subscription();
118846
- });
118847
-
118848
- // src/daemon/entry/enhancer-inbound-registry.ts
118849
- function registerEnhancerInboundHandler(h) {
118970
+ // src/daemon/entry/file-inbound-registry.ts
118971
+ function registerFileInboundHandler(h) {
118850
118972
  handler3 = h;
118851
118973
  }
118852
- function unregisterEnhancerInboundHandler() {
118974
+ function unregisterFileInboundHandler() {
118853
118975
  handler3 = undefined;
118854
118976
  }
118855
- async function dispatchEnhancerInboundEvent(event) {
118977
+ async function dispatchFileInboundEvent(event) {
118856
118978
  if (handler3)
118857
118979
  await handler3(event);
118858
118980
  }
118859
118981
  var handler3;
118860
118982
 
118861
- // src/daemon/entry/enhancer/job-subscriber.ts
118862
- async function processEnhancerJobForSpawn(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, job, inFlight2, logSink) {
118863
- if (inFlight2.has(job.jobId))
118864
- return;
118865
- if (isChatroomStopScopeActive(job.chatroomId))
118866
- return;
118867
- inFlight2.add(job.jobId);
118868
- let claimed = false;
118869
- let chatroomId = job.chatroomId;
118870
- let jobId = job.jobId;
118871
- let spawnResult = null;
118872
- let service4 = null;
118873
- let abortController = null;
118874
- let spawnHandle = null;
118875
- let agentEndSeen = false;
118876
- let resolveAgentEnd;
118877
- let agentEndPromise = Promise.resolve();
118878
- let log4 = createEnhancerLogWriter(logSink, {
118879
- chatroomId: job.chatroomId,
118880
- harness: "unknown"
118881
- });
118882
- try {
118883
- const claim = await backend2.mutation(api.daemon.enhancer.index.claimForSpawn, {
118884
- sessionId,
118885
- jobId: job.jobId,
118886
- machineId
118887
- });
118888
- if (!claim.claimed)
118889
- return;
118890
- claimed = true;
118891
- log4.write(`claimed job=${job.jobId} chatroom=${job.chatroomId}`);
118892
- const payload = await backend2.query(api.daemon.enhancer.index.getTaskDeliveryForJob, {
118893
- sessionId,
118894
- jobId: job.jobId,
118895
- convexUrl
118896
- });
118897
- chatroomId = payload.chatroomId;
118898
- jobId = payload.jobId;
118899
- log4 = createEnhancerLogWriter(logSink, {
118900
- chatroomId: payload.chatroomId,
118901
- harness: payload.agentHarness,
118902
- pid: undefined
118903
- });
118904
- log4.write(`spawning harness=${payload.agentHarness} model=${payload.model} job=${payload.jobId}`);
118905
- service4 = agentServices.get(payload.agentHarness) ?? null;
118906
- if (!service4) {
118907
- await backend2.mutation(api.web.enhancer.index.recordAttemptFailure, {
118908
- sessionId,
118909
- chatroomId: payload.chatroomId,
118910
- jobId: payload.jobId,
118911
- error: `Harness ${payload.agentHarness} not available on machine`
118912
- });
118913
- return;
118914
- }
118915
- const spawned = await service4.spawn({
118916
- workingDir: payload.workingDir,
118917
- prompt: createSpawnPrompt(payload.taskDeliveryOutput),
118918
- systemPrompt: payload.systemPrompt,
118919
- model: payload.model,
118920
- context: {
118921
- machineId,
118922
- chatroomId: payload.chatroomId,
118923
- role: ENHANCER_AGENT_ROLE
118924
- },
118925
- resolvedConvexUrl: convexUrl
118926
- });
118927
- spawnResult = spawned;
118928
- abortController = new AbortController;
118929
- spawnHandle = {
118930
- jobId: payload.jobId,
118931
- chatroomId: payload.chatroomId,
118932
- abort: () => abortController?.abort(),
118933
- stopProcess: async () => {
118934
- await service4?.stop(spawned.pid);
118935
- }
118936
- };
118937
- registerEnhancerSpawn(spawnHandle);
118938
- log4 = createEnhancerLogWriter(logSink, {
118939
- chatroomId: payload.chatroomId,
118940
- harness: payload.agentHarness,
118941
- pid: spawned.pid
118942
- });
118943
- spawned.onLogLine?.((line) => {
118944
- log4.write(line);
118945
- });
118946
- agentEndPromise = new Promise((resolve5) => {
118947
- resolveAgentEnd = resolve5;
118948
- });
118949
- spawned.onAgentEnd?.(() => {
118950
- if (agentEndSeen)
118951
- return;
118952
- agentEndSeen = true;
118953
- resolveAgentEnd?.();
118954
- });
118955
- await waitForEnhancerJobResolution({
118956
- sessionId,
118957
- chatroomId: payload.chatroomId,
118958
- jobId: payload.jobId,
118959
- wsClient: wsClient2,
118960
- log: log4,
118961
- onAssistantText: spawned.onAssistantText ? (cb) => spawned.onAssistantText?.(cb) : undefined,
118962
- onAgentEnd: spawned.onAgentEnd ? (cb) => spawned.onAgentEnd?.(cb) : undefined,
118963
- onExit: (cb) => spawned.onExit(() => cb()),
118964
- onSalvageComplete: async (content) => {
118965
- await backend2.mutation(api.web.enhancer.index.complete, {
118966
- sessionId,
118967
- chatroomId: payload.chatroomId,
118968
- jobId: payload.jobId,
118969
- enhancedContent: content
118970
- });
118971
- },
118972
- onFailure: async (error51, forceTerminal) => {
118973
- await backend2.mutation(api.web.enhancer.index.recordAttemptFailure, {
118974
- sessionId,
118975
- chatroomId: payload.chatroomId,
118976
- jobId: payload.jobId,
118977
- error: error51,
118978
- ...forceTerminal ? { forceTerminal: true } : {}
118979
- });
118980
- },
118981
- signal: abortController.signal
118982
- });
118983
- if (!abortController.signal.aborted)
118984
- log4.write(`completed job=${jobId}`);
118985
- } catch (err) {
118986
- log4.write(`error: ${err instanceof Error ? err.message : String(err)}`);
118987
- if (claimed) {
118988
- await backend2.mutation(api.web.enhancer.index.recordAttemptFailure, {
118989
- sessionId,
118990
- chatroomId,
118991
- jobId,
118992
- error: err instanceof Error ? err.message : String(err)
118993
- });
118994
- }
118995
- } finally {
118996
- inFlight2.delete(job.jobId);
118997
- if (spawnHandle)
118998
- unregisterEnhancerSpawn(spawnHandle);
118999
- if (spawnResult && service4) {
119000
- if (!agentEndSeen)
119001
- await Promise.race([
119002
- agentEndPromise,
119003
- new Promise((resolve5) => setTimeout(resolve5, ENHANCER_AGENT_END_GRACE_MS))
119004
- ]);
119005
- try {
119006
- await service4.stop(spawnResult.pid);
119007
- } catch {}
119008
- }
119009
- }
119010
- }
119011
- function processEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, jobs, inFlight2, logSink) {
119012
- for (const job of jobs ?? []) {
119013
- processEnhancerJobForSpawn(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, job, inFlight2, logSink);
119014
- }
119015
- }
119016
- async function drainPendingEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, inFlight2, logSink) {
119017
- const jobs = await backend2.query(api.daemon.enhancer.index.pendingForMachine, {
119018
- sessionId,
119019
- machineId
119020
- });
119021
- if (!jobs?.length)
119022
- return;
119023
- processEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, jobs, inFlight2, logSink);
119024
- }
119025
- function startEnhancerJobSubscriber(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, logSink) {
119026
- const inFlight2 = new Set;
119027
- registerEnhancerInboundHandler(async () => {
119028
- await drainPendingEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, inFlight2, logSink);
119029
- });
119030
- setEnhancerDrainHandler(() => drainPendingEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, inFlight2, logSink));
119031
- return {
119032
- stop: () => {
119033
- unregisterEnhancerInboundHandler();
119034
- clearEnhancerDrainHandlerForTests();
119035
- },
119036
- drainPendingEnhancerJobs: () => drainPendingEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, inFlight2, logSink)
119037
- };
119038
- }
119039
- var init_job_subscriber = __esm(() => {
119040
- init_enhancer_log();
119041
- init_enhancer_spawn_registry();
119042
- init_wait_for_enhancer_job();
119043
- init_api3();
119044
- init_service_interfaces();
119045
- });
119046
-
119047
- // src/daemon/entry/enhancer/start-subscriptions.ts
119048
- function startEnhancerSubscriptions(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, logSink) {
119049
- return startEnhancerJobSubscriber(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, logSink);
119050
- }
119051
- var init_start_subscriptions2 = __esm(() => {
119052
- init_job_subscriber();
119053
- });
119054
-
119055
- // src/daemon/entry/file-inbound-registry.ts
119056
- function registerFileInboundHandler(h) {
119057
- handler4 = h;
119058
- }
119059
- function unregisterFileInboundHandler() {
119060
- handler4 = undefined;
119061
- }
119062
- async function dispatchFileInboundEvent(event) {
119063
- if (handler4)
119064
- await handler4(event);
119065
- }
119066
- var handler4;
119067
-
119068
118983
  // src/daemon/entry/files/file-content-classifier.ts
119069
118984
  function extensionOf(path3) {
119070
118985
  const lastDot = path3.lastIndexOf(".");
@@ -119183,7 +119098,7 @@ var init_assert_registered_working_dir = __esm(() => {
119183
119098
 
119184
119099
  // src/infrastructure/services/workspace/workspace-path-security.ts
119185
119100
  import { realpath } from "node:fs/promises";
119186
- 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";
119187
119102
  import { gunzipSync } from "node:zlib";
119188
119103
  function validateRelativePathSegments(filePath) {
119189
119104
  if (filePath.includes("\x00"))
@@ -119211,7 +119126,7 @@ function assertContained(workspaceRoot, targetPath) {
119211
119126
  return { ok: true };
119212
119127
  }
119213
119128
  async function resolveNonExistentTarget(workspaceRoot, candidate) {
119214
- let current = dirname11(candidate);
119129
+ let current = dirname12(candidate);
119215
119130
  for (;; ) {
119216
119131
  try {
119217
119132
  const ancestorReal = await realpath(current);
@@ -119229,7 +119144,7 @@ async function resolveNonExistentTarget(workspaceRoot, candidate) {
119229
119144
  if (code2 !== "ENOENT") {
119230
119145
  return { ok: false, error: "Invalid file path" };
119231
119146
  }
119232
- const parent = dirname11(current);
119147
+ const parent = dirname12(current);
119233
119148
  if (parent === current) {
119234
119149
  return { ok: false, error: "Path escapes workspace" };
119235
119150
  }
@@ -120033,16 +119948,16 @@ var require_ignore = __commonJS((exports, module) => {
120033
119948
  // src/infrastructure/services/workspace/workspace-ignore.ts
120034
119949
  import { promises as fsPromises } from "node:fs";
120035
119950
  import { readFile as readFile10 } from "node:fs/promises";
120036
- import { homedir as homedir9 } from "node:os";
119951
+ import { homedir as homedir10 } from "node:os";
120037
119952
  import path3 from "node:path";
120038
119953
  function normalizeRelativePath(relativePath) {
120039
119954
  return relativePath.replace(/\\/g, "/").replace(/^\.?\//, "").replace(/\/+$/, "");
120040
119955
  }
120041
119956
  function expandUserPath(filePath) {
120042
119957
  if (filePath === "~")
120043
- return homedir9();
119958
+ return homedir10();
120044
119959
  if (filePath.startsWith("~/"))
120045
- return path3.join(homedir9(), filePath.slice(2));
119960
+ return path3.join(homedir10(), filePath.slice(2));
120046
119961
  return filePath;
120047
119962
  }
120048
119963
  function readExcludesFileFromGitconfig(content) {
@@ -120073,11 +119988,11 @@ async function readGitconfigExcludesFilePath(configPath) {
120073
119988
  return path3.isAbsolute(raw) ? expandUserPath(raw) : path3.resolve(path3.dirname(configPath), raw);
120074
119989
  }
120075
119990
  async function resolveGitExtraExcludeFilePaths(rootDir) {
120076
- 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");
120077
119992
  const localConfig = path3.join(rootDir, ".git", "config");
120078
119993
  let excludesFile = await readGitconfigExcludesFilePath(localConfig) ?? await readGitconfigExcludesFilePath(globalConfig2);
120079
119994
  if (!excludesFile) {
120080
- 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");
120081
119996
  }
120082
119997
  const candidates = [excludesFile, path3.join(rootDir, ".git", "info", "exclude")];
120083
119998
  const existing = [];
@@ -121213,9 +121128,9 @@ class NodeFsHandler {
121213
121128
  if (this.fsw.closed) {
121214
121129
  return;
121215
121130
  }
121216
- const dirname13 = sp.dirname(file2);
121131
+ const dirname14 = sp.dirname(file2);
121217
121132
  const basename4 = sp.basename(file2);
121218
- const parent = this.fsw._getWatchedDir(dirname13);
121133
+ const parent = this.fsw._getWatchedDir(dirname14);
121219
121134
  let prevStats = stats;
121220
121135
  if (parent.has(basename4))
121221
121136
  return;
@@ -121242,7 +121157,7 @@ class NodeFsHandler {
121242
121157
  prevStats = newStats2;
121243
121158
  }
121244
121159
  } catch (error51) {
121245
- this.fsw._remove(dirname13, basename4);
121160
+ this.fsw._remove(dirname14, basename4);
121246
121161
  }
121247
121162
  } else if (parent.has(basename4)) {
121248
121163
  const at = newStats.atimeMs;
@@ -122631,8 +122546,8 @@ function diffPathIndexes(previous, next4) {
122631
122546
  // src/infrastructure/services/workspace/workspace-sync-state.ts
122632
122547
  import { createHash as createHash7, randomUUID as randomUUID12 } from "node:crypto";
122633
122548
  import * as fs11 from "node:fs/promises";
122634
- import { homedir as homedir10 } from "node:os";
122635
- import { join as join26 } from "node:path";
122549
+ import { homedir as homedir11 } from "node:os";
122550
+ import { join as join27 } from "node:path";
122636
122551
  function workspaceKeyFor(workingDir) {
122637
122552
  const normalized = normalizeWorkingDirForLookup(workingDir);
122638
122553
  return createHash7("sha256").update(normalized).digest("hex").slice(0, 16);
@@ -122646,10 +122561,10 @@ function buildPathIndex(entries2) {
122646
122561
  }
122647
122562
  function manifestPath(machineId, workingDir) {
122648
122563
  const key = workspaceKeyFor(workingDir);
122649
- return join26(SYNC_STATE_DIR, machineId, key, "manifest.json");
122564
+ return join27(SYNC_STATE_DIR, machineId, key, "manifest.json");
122650
122565
  }
122651
122566
  async function ensureDir(filePath) {
122652
- await fs11.mkdir(join26(filePath, ".."), { recursive: true, mode: 448 });
122567
+ await fs11.mkdir(join27(filePath, ".."), { recursive: true, mode: 448 });
122653
122568
  }
122654
122569
  async function loadWorkspaceSyncManifest(machineId, workingDir) {
122655
122570
  try {
@@ -122709,7 +122624,7 @@ function entriesFromPathIndex(paths) {
122709
122624
  }
122710
122625
  var SYNC_STATE_VERSION = "2", SYNC_STATE_DIR;
122711
122626
  var init_workspace_sync_state = __esm(() => {
122712
- SYNC_STATE_DIR = join26(homedir10(), ".chatroom", "sync-state");
122627
+ SYNC_STATE_DIR = join27(homedir11(), ".chatroom", "sync-state");
122713
122628
  });
122714
122629
 
122715
122630
  // src/infrastructure/services/workspace/workspace-file-tree-coordinator.ts
@@ -123087,12 +123002,12 @@ var init_durable_coalescing_state_schema = __esm(() => {
123087
123002
  });
123088
123003
 
123089
123004
  // src/daemon/infrastructure/outbox/lib/durable-coalescing-state-store.ts
123090
- import { mkdirSync as mkdirSync8 } from "node:fs";
123091
- import { dirname as dirname14 } from "node:path";
123092
- 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";
123093
123008
  function openDurableCoalescingStateStore(path8) {
123094
- mkdirSync8(dirname14(path8), { recursive: true });
123095
- const db = new DatabaseSync3(path8);
123009
+ mkdirSync9(dirname15(path8), { recursive: true });
123010
+ const db = new DatabaseSync4(path8);
123096
123011
  for (const sql of DURABLE_COALESCING_STATE_MIGRATIONS)
123097
123012
  db.exec(sql);
123098
123013
  db.prepare("UPDATE coalescing_outbox_entries SET status='pending' WHERE status='in_flight'").run();
@@ -124080,7 +123995,7 @@ var init_file_write_request_priority = __esm(() => {
124080
123995
 
124081
123996
  // src/daemon/entry/files/file-write-fulfillment.ts
124082
123997
  import { access as access4, mkdir as mkdir10, rename as rename4, rm as rm4, writeFile as writeFile10 } from "node:fs/promises";
124083
- import { dirname as dirname15 } from "node:path";
123998
+ import { dirname as dirname16 } from "node:path";
124084
123999
  function isTerminalFileWriteError(errorMessage) {
124085
124000
  const terminalMessages = new Set([
124086
124001
  "Invalid file path",
@@ -124179,7 +124094,7 @@ async function validateWriteOperation(operation, absolutePath) {
124179
124094
  }
124180
124095
  async function writePayloadToDisk(absolutePath, operation, content) {
124181
124096
  if (operation === "create") {
124182
- await mkdir10(dirname15(absolutePath), { recursive: true });
124097
+ await mkdir10(dirname16(absolutePath), { recursive: true });
124183
124098
  await writeFile10(absolutePath, content, { flag: "wx" });
124184
124099
  return;
124185
124100
  }
@@ -124257,7 +124172,7 @@ async function fulfillOneFileWriteRequest(session2, request2) {
124257
124172
  });
124258
124173
  return;
124259
124174
  }
124260
- await mkdir10(dirname15(targetResolved.absolutePath), { recursive: true });
124175
+ await mkdir10(dirname16(targetResolved.absolutePath), { recursive: true });
124261
124176
  await rename4(resolved.absolutePath, targetResolved.absolutePath);
124262
124177
  await completeWriteRequest(session2, claimed._id, claimed.revision, { status: "done" });
124263
124178
  const elapsed4 = Date.now() - startTime;
@@ -125332,16 +125247,16 @@ var init_workspace_list_subscription = __esm(() => {
125332
125247
 
125333
125248
  // src/daemon/entry/workspace-git-inbound-registry.ts
125334
125249
  function registerWorkspaceGitInboundHandler(h) {
125335
- handler5 = h;
125250
+ handler4 = h;
125336
125251
  }
125337
125252
  function unregisterWorkspaceGitInboundHandler() {
125338
- handler5 = undefined;
125253
+ handler4 = undefined;
125339
125254
  }
125340
125255
  async function dispatchWorkspaceGitInboundEvent(event) {
125341
- if (handler5)
125342
- await handler5(event);
125256
+ if (handler4)
125257
+ await handler4(event);
125343
125258
  }
125344
- var handler5;
125259
+ var handler4;
125345
125260
 
125346
125261
  // src/events/lifecycle/on-daemon-shutdown.ts
125347
125262
  var onDaemonShutdownEffect;
@@ -125359,6 +125274,19 @@ var init_on_daemon_shutdown = __esm(() => {
125359
125274
  const activeAgents = agentPm.listActive();
125360
125275
  if (activeAgents.length > 0) {
125361
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
+ });
125362
125290
  let totalStopped = 0;
125363
125291
  let totalFailed = 0;
125364
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(() => {
@@ -125388,7 +125316,6 @@ function createDaemonRuntime(deps) {
125388
125316
  let workspaceListSubscriptionHandle = null;
125389
125317
  let logObserverSubscriptionHandle = null;
125390
125318
  let agenticQueryWorkerHandle = null;
125391
- let enhancerWorkerHandle = null;
125392
125319
  let taskInboxHandle = null;
125393
125320
  let agentConfigRegistryHandle = null;
125394
125321
  const activeSessions = new Map;
@@ -125429,7 +125356,6 @@ function createDaemonRuntime(deps) {
125429
125356
  taskInboxHandle?.stop();
125430
125357
  logObserverSubscriptionHandle?.stop();
125431
125358
  agenticQueryWorkerHandle?.stop();
125432
- enhancerWorkerHandle?.stop();
125433
125359
  };
125434
125360
  const shutdown = async () => {
125435
125361
  if (isShuttingDown)
@@ -125547,7 +125473,6 @@ function createDaemonRuntime(deps) {
125547
125473
  backend: session2.backend,
125548
125474
  convexUrl: session2.convexUrl
125549
125475
  }, activeSessions, harnesses);
125550
- enhancerWorkerHandle = startEnhancerSubscriptions(session2.sessionId, session2.machineId, session2.convexUrl, deps.wsClient, session2.backend, session2.agentServices, getActiveLogSink());
125551
125476
  console.log(`
125552
125477
  Listening for commands...`);
125553
125478
  console.log(`Press Ctrl+C to stop
@@ -125584,7 +125509,6 @@ var init_daemon_runtime = __esm(() => {
125584
125509
  init_start_subscriptions();
125585
125510
  init_command_dispatch();
125586
125511
  init_daemon_services();
125587
- init_start_subscriptions2();
125588
125512
  init_file_content_subscription();
125589
125513
  init_file_tree_subscription();
125590
125514
  init_file_write_subscription();
@@ -125592,7 +125516,6 @@ var init_daemon_runtime = __esm(() => {
125592
125516
  init_orphan_tracker();
125593
125517
  init_command_run_subscription();
125594
125518
  init_log_observer_sync();
125595
- init_init_daemon();
125596
125519
  init_task_inbox_runtime();
125597
125520
  init_git_subscription();
125598
125521
  init_api3();
@@ -125635,21 +125558,6 @@ function createCommandRouterDeps() {
125635
125558
  }
125636
125559
  var init_command_bridge = () => {};
125637
125560
 
125638
- // src/daemon/domain/usecase/process-enhancer-job.ts
125639
- async function processEnhancerJobInbound(deps, event) {
125640
- await deps.dispatchInbound(event);
125641
- }
125642
-
125643
- // src/daemon/entry/bridge/enhancer-bridge.ts
125644
- function createEnhancerRouterDeps() {
125645
- return {
125646
- deliverInbound: async (event) => {
125647
- await processEnhancerJobInbound({ dispatchInbound: dispatchEnhancerInboundEvent }, event);
125648
- }
125649
- };
125650
- }
125651
- var init_enhancer_bridge = () => {};
125652
-
125653
125561
  // src/daemon/domain/usecase/fulfill-file-content-request.ts
125654
125562
  async function fulfillFileContentRequest(deps, event) {
125655
125563
  await deps.dispatchInbound(event);
@@ -125719,14 +125627,12 @@ function createDefaultEventRouterDeps() {
125719
125627
  command: createCommandRouterDeps(),
125720
125628
  workspaceGit: createWorkspaceGitRouterDeps(),
125721
125629
  file: createFileRouterDeps(),
125722
- agenticQuery: createAgenticQueryRouterDeps(),
125723
- enhancer: createEnhancerRouterDeps()
125630
+ agenticQuery: createAgenticQueryRouterDeps()
125724
125631
  };
125725
125632
  }
125726
125633
  var init_default_router_deps = __esm(() => {
125727
125634
  init_agentic_query_bridge();
125728
125635
  init_command_bridge();
125729
- init_enhancer_bridge();
125730
125636
  init_file_bridge();
125731
125637
  init_workspace_git_bridge();
125732
125638
  });
@@ -126007,7 +125913,6 @@ async function executeChatroomStopCommand(args2) {
126007
125913
  args2.apm.markStopIntent(args2.chatroomId, args2.role, "user.stop", undefined, args2.workingDir);
126008
125914
  else if (!args2.workingDir) {
126009
125915
  args2.apm.markChatroomStopIntent(args2.chatroomId, "user.stop");
126010
- await abortEnhancerSpawnsForChatroom(args2.chatroomId);
126011
125916
  }
126012
125917
  const confirmedDeps = args2.apm.getConfirmedStopAdapterDeps();
126013
125918
  const targets = await args2.apm.discoverStopTargets(args2.chatroomId);
@@ -126040,15 +125945,14 @@ async function executeChatroomStopCommand(args2) {
126040
125945
  }
126041
125946
  var init_execute_chatroom_stop_command = __esm(() => {
126042
125947
  init_reliability();
126043
- init_enhancer_spawn_registry();
126044
125948
  init_agent_process_service2();
126045
125949
  });
126046
125950
 
126047
125951
  // src/daemon/entry/persistence-path.ts
126048
- import { homedir as homedir11 } from "node:os";
126049
- import { join as join27 } from "node:path";
125952
+ import { homedir as homedir12 } from "node:os";
125953
+ import { join as join28 } from "node:path";
126050
125954
  function resolvePersistenceDbPath(machineId) {
126051
- return join27(homedir11(), ".chatroom", "daemon", machineId, "events.sqlite");
125955
+ return join28(homedir12(), ".chatroom", "daemon", machineId, "events.sqlite");
126052
125956
  }
126053
125957
  var init_persistence_path = () => {};
126054
125958
 
@@ -126066,13 +125970,6 @@ async function handleCommandInbound(deps, event) {
126066
125970
  }
126067
125971
  }
126068
125972
 
126069
- // src/daemon/domain/usecase/handle-enhancer-inbound.ts
126070
- async function handleEnhancerInbound(deps, event) {
126071
- if (deps.deliverInbound) {
126072
- await deps.deliverInbound(event);
126073
- }
126074
- }
126075
-
126076
125973
  // src/daemon/domain/usecase/handle-file-inbound.ts
126077
125974
  async function handleFileInbound(deps, event) {
126078
125975
  if (deps.deliverInbound) {
@@ -126108,9 +126005,6 @@ async function routeInboundEvent(deps, event) {
126108
126005
  case "agentic-query.prompt":
126109
126006
  await handleAgenticQueryInbound(deps.agenticQuery, event);
126110
126007
  break;
126111
- case "enhancer.job-assigned":
126112
- await handleEnhancerInbound(deps.enhancer, event);
126113
- break;
126114
126008
  default:
126115
126009
  }
126116
126010
  }
@@ -126218,80 +126112,6 @@ var init_command_run = __esm(() => {
126218
126112
  init_api3();
126219
126113
  });
126220
126114
 
126221
- // src/daemon/infrastructure/convex/subscribers/enhancer-job.ts
126222
- function startEnhancerJobSubscriber2(deps, onEvent) {
126223
- const seen = new Set;
126224
- const watchers = new Map;
126225
- let stopped = false;
126226
- let refreshInFlight = false;
126227
- const onPendingJobs = (jobs) => {
126228
- if (!jobs?.length)
126229
- return;
126230
- for (const job of jobs) {
126231
- if (job == null || typeof job !== "object" || !("jobId" in job))
126232
- continue;
126233
- if (seen.has(job.jobId))
126234
- continue;
126235
- seen.add(job.jobId);
126236
- onEvent({ type: "enhancer.job-assigned", jobId: job.jobId });
126237
- }
126238
- };
126239
- const onError3 = (err) => {
126240
- console.warn(`[daemon] enhancer-job subscriber error: ${err instanceof Error ? err.message : String(err)}`);
126241
- };
126242
- const watchChatroom = (chatroomId) => {
126243
- if (watchers.has(chatroomId) || stopped)
126244
- return;
126245
- const unsub = deps.wsClient.onUpdate(api.daemon.enhancer.index.pendingForChatroom, {
126246
- sessionId: deps.sessionId,
126247
- machineId: deps.machineId,
126248
- chatroomId
126249
- }, onPendingJobs, onError3);
126250
- watchers.set(chatroomId, unsub);
126251
- };
126252
- const refreshChatrooms = async () => {
126253
- if (refreshInFlight)
126254
- return;
126255
- refreshInFlight = true;
126256
- try {
126257
- const workspaces = await deps.wsClient.query(api.workspaces.listWorkspacesForMachine, {
126258
- sessionId: deps.sessionId,
126259
- machineId: deps.machineId
126260
- });
126261
- if (stopped)
126262
- return;
126263
- const roomIds = [
126264
- ...new Set((workspaces ?? []).map((ws) => ws.chatroomId))
126265
- ];
126266
- for (const [roomId, unsub] of watchers) {
126267
- if (!roomIds.includes(roomId)) {
126268
- unsub();
126269
- watchers.delete(roomId);
126270
- }
126271
- }
126272
- for (const roomId of roomIds)
126273
- watchChatroom(roomId);
126274
- } catch (error51) {
126275
- console.warn(`[daemon] enhancer-job room refresh failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
126276
- } finally {
126277
- refreshInFlight = false;
126278
- }
126279
- };
126280
- refreshChatrooms();
126281
- return {
126282
- async stop() {
126283
- stopped = true;
126284
- for (const unsub of watchers.values())
126285
- unsub();
126286
- watchers.clear();
126287
- },
126288
- refreshChatrooms
126289
- };
126290
- }
126291
- var init_enhancer_job = __esm(() => {
126292
- init_api3();
126293
- });
126294
-
126295
126115
  // src/daemon/infrastructure/convex/subscribers/pending-file-request-dedup.ts
126296
126116
  function pendingConvexId(req) {
126297
126117
  if (req._id == null)
@@ -126608,7 +126428,6 @@ function startAllSubscribers(deps) {
126608
126428
  const onEvent = (event) => {
126609
126429
  routeInboundEvent(deps.router, event);
126610
126430
  };
126611
- const enhancerJob = startEnhancerJobSubscriber2(deps, onEvent);
126612
126431
  const gitRequest = startGitRequestSubscriber(deps, onEvent);
126613
126432
  const machineCommands = startMachineCommandInboxSubscriber(deps, async (claimed) => {
126614
126433
  if (claimed.type === "agent.stop") {
@@ -126639,8 +126458,7 @@ function startAllSubscribers(deps) {
126639
126458
  fileContent.stop(),
126640
126459
  fileWrite.stop(),
126641
126460
  agenticQuerySession.stop(),
126642
- agenticQueryPrompt.stop(),
126643
- enhancerJob.stop()
126461
+ agenticQueryPrompt.stop()
126644
126462
  ]);
126645
126463
  }
126646
126464
  };
@@ -126650,7 +126468,6 @@ var init_subscriber_registry = __esm(() => {
126650
126468
  init_agentic_query_prompt();
126651
126469
  init_agentic_query_session();
126652
126470
  init_command_run();
126653
- init_enhancer_job();
126654
126471
  init_file_content_request();
126655
126472
  init_file_tree_release_request();
126656
126473
  init_file_tree_request();
@@ -126699,12 +126516,12 @@ var init_schema = __esm(() => {
126699
126516
  });
126700
126517
 
126701
126518
  // src/daemon/infrastructure/persistence/open-database.ts
126702
- import { mkdirSync as mkdirSync9 } from "node:fs";
126703
- import { dirname as dirname16 } from "node:path";
126704
- 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";
126705
126522
  function openDatabase(dbPath) {
126706
- mkdirSync9(dirname16(dbPath), { recursive: true });
126707
- const db = new DatabaseSync4(dbPath);
126523
+ mkdirSync10(dirname17(dbPath), { recursive: true });
126524
+ const db = new DatabaseSync5(dbPath);
126708
126525
  for (const sql of MIGRATIONS2) {
126709
126526
  db.exec(sql);
126710
126527
  }
@@ -134199,19 +134016,19 @@ function routeRequest(req, res, deps) {
134199
134016
 
134200
134017
  // src/daemon/local-web/server/serve-static.ts
134201
134018
  import { existsSync as existsSync10, readFileSync as readFileSync11 } from "node:fs";
134202
- import { dirname as dirname17, join as join28 } from "node:path";
134019
+ import { dirname as dirname18, join as join29 } from "node:path";
134203
134020
  import { fileURLToPath as fileURLToPath7 } from "node:url";
134204
134021
  function clientDistCandidates(here) {
134205
134022
  return [
134206
- join28(here, "client/build"),
134207
- join28(here, "../client/build"),
134208
- 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")
134209
134026
  ];
134210
134027
  }
134211
- function resolveClientDistDir(here = dirname17(fileURLToPath7(import.meta.url))) {
134028
+ function resolveClientDistDir(here = dirname18(fileURLToPath7(import.meta.url))) {
134212
134029
  const candidates = clientDistCandidates(here);
134213
134030
  for (const dir of candidates) {
134214
- if (existsSync10(join28(dir, "index.html")))
134031
+ if (existsSync10(join29(dir, "index.html")))
134215
134032
  return dir;
134216
134033
  }
134217
134034
  return candidates[0];
@@ -134224,7 +134041,7 @@ function tryServeStatic(req, res, distDir) {
134224
134041
  return false;
134225
134042
  }
134226
134043
  const safePath = pathname === "/" ? "/index.html" : pathname;
134227
- const filePath = join28(distDir, safePath);
134044
+ const filePath = join29(distDir, safePath);
134228
134045
  if (!filePath.startsWith(distDir)) {
134229
134046
  res.writeHead(403);
134230
134047
  res.end();
@@ -134241,7 +134058,7 @@ function tryServeStatic(req, res, distDir) {
134241
134058
  }
134242
134059
  return true;
134243
134060
  }
134244
- const indexPath = join28(distDir, "index.html");
134061
+ const indexPath = join29(distDir, "index.html");
134245
134062
  if (existsSync10(indexPath)) {
134246
134063
  res.writeHead(200, { "Content-Type": "text/html" });
134247
134064
  if (req.method === "HEAD") {
@@ -134290,6 +134107,14 @@ function normalizeError(err) {
134290
134107
  details: err.flatten()
134291
134108
  };
134292
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
+ }
134293
134118
  if (err instanceof Error) {
134294
134119
  const code2 = "code" in err && typeof err.code === "string" ? err.code : "internal_error";
134295
134120
  if (code2 === "not_found" || code2 === "validation_error" || code2 === "unauthorized") {
@@ -134300,6 +134125,7 @@ function normalizeError(err) {
134300
134125
  return { code: "internal_error", message: "Unknown error" };
134301
134126
  }
134302
134127
  var init_normalize_error = __esm(() => {
134128
+ init_values();
134303
134129
  init_zod();
134304
134130
  });
134305
134131
 
@@ -134430,6 +134256,16 @@ function extractAck(args2) {
134430
134256
  }
134431
134257
  return { payload: args2[0], ack: undefined };
134432
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
+ }
134433
134269
  function registerSocketHandlers(io2, deps) {
134434
134270
  const logHistory = deps.logRepo ? createLogHistoryUseCase({ reader: deps.logRepo }) : undefined;
134435
134271
  const eventStreamHistory = deps.logRepo ? createEventStreamHistoryUseCase({ reader: deps.logRepo }) : undefined;
@@ -134449,13 +134285,22 @@ function registerSocketHandlers(io2, deps) {
134449
134285
  callAck(ack, { ok: false, error: normalizeError(err) });
134450
134286
  }
134451
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
+ });
134452
134299
  socket.on("daemon.debug.state", async (...args2) => {
134453
134300
  const { payload, ack } = extractAck(args2);
134454
134301
  try {
134455
- if (!deps.debugState)
134456
- throw new Error("daemon debug state is not configured");
134457
- const { chatroomId } = debugStateInputSchema.parse(payload ?? {});
134458
- callAck(ack, { ok: true, data: await deps.debugState(chatroomId) });
134302
+ const data = await resolveDebugStatePayload(deps, payload);
134303
+ callAck(ack, { ok: true, data });
134459
134304
  } catch (err) {
134460
134305
  callAck(ack, { ok: false, error: normalizeError(err) });
134461
134306
  }
@@ -134639,7 +134484,8 @@ async function startLocalWebServer(config4, deps = {}) {
134639
134484
  eventStreamHub: deps.eventStreamHub,
134640
134485
  backend: deps.backend,
134641
134486
  sessionId: deps.sessionId,
134642
- debugState: deps.debugState
134487
+ debugState: deps.debugState,
134488
+ cliGateway: deps.cliGateway
134643
134489
  });
134644
134490
  return {
134645
134491
  port: boundPort,
@@ -134695,6 +134541,80 @@ function createLogStreamHub() {
134695
134541
  };
134696
134542
  }
134697
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
+
134698
134618
  // src/daemon/entry/start-daemon.ts
134699
134619
  async function startDaemon() {
134700
134620
  let resolveBoundPort;
@@ -134729,6 +134649,11 @@ async function startDaemon() {
134729
134649
  logEvent: init2.logEvent
134730
134650
  });
134731
134651
  const localWebPort = resolveLocalWebPort();
134652
+ const cliGateway = createCliGatewayService({
134653
+ backend: init2.backend,
134654
+ port: localWebPort,
134655
+ taskService: init2.taskService
134656
+ });
134732
134657
  const debugState = async (chatroomId) => {
134733
134658
  const persisted = await loadDaemonState(init2.machineId);
134734
134659
  const persistedAgents = Object.fromEntries(Object.entries(persisted?.agents ?? {}).filter(([key]) => key.startsWith(`${chatroomId}/`)));
@@ -134786,7 +134711,8 @@ async function startDaemon() {
134786
134711
  eventStreamHub,
134787
134712
  backend: init2.backend,
134788
134713
  sessionId: init2.sessionId,
134789
- debugState
134714
+ debugState,
134715
+ cliGateway
134790
134716
  });
134791
134717
  resolveBoundPort(localWeb.port);
134792
134718
  const subscribers = startAllSubscribers({
@@ -134847,6 +134773,7 @@ var init_start_daemon = __esm(() => {
134847
134773
  init_log_repository();
134848
134774
  init_socket3();
134849
134775
  init_create_local_web_server();
134776
+ init_cli_gateway_service();
134850
134777
  });
134851
134778
 
134852
134779
  // src/commands/machine/daemon-start/index.ts
@@ -136103,4 +136030,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
136103
136030
  });
136104
136031
  program2.parse();
136105
136032
 
136106
- //# debugId=F7E2E413529D2D3564756E2164756E21
136033
+ //# debugId=CB5CA35B81D3EFBC64756E2164756E21