chatroom-cli 1.117.6 → 1.119.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();
@@ -27764,6 +27792,100 @@ var init_base_cli_agent_service = __esm(() => {
27764
27792
  retrySchedule = exports_Schedule.exponential(exports_Duration.millis(DETECTION_RETRY_POLICY.initialDelayMs), DETECTION_RETRY_POLICY.backoffFactor).pipe(exports_Schedule.either(exports_Schedule.spaced(exports_Duration.millis(DETECTION_RETRY_POLICY.maxDelayMs))), exports_Schedule.compose(exports_Schedule.recurs(DETECTION_RETRY_POLICY.maxAttempts - 1)));
27765
27793
  });
27766
27794
 
27795
+ // ../../services/backend/src/domain/entities/_shared/v-literals-of.ts
27796
+ var toLiteralValidators = (values3) => values3.map((v_) => v.literal(v_));
27797
+ var init_v_literals_of = __esm(() => {
27798
+ init_values();
27799
+ });
27800
+
27801
+ // ../../services/backend/src/domain/entities/agent.ts
27802
+ var AGENT_HARNESSES, AgentHarnessEnum, agentHarnessValidator, AGENT_TYPES, AgentTypeEnum, agentTypeValidator, MACHINE_COMMAND_TYPES, MachineCommandTypeEnum, machineCommandTypeValidator, MACHINE_COMMAND_STATUSES, MachineCommandStatusEnum, machineCommandStatusValidator, AgentStartReasonCode, AGENT_START_REASONS, DaemonStartReasonCode, agentStartReasonValidator, USER_EXPLICIT_START_REASONS, EXPLICIT_DAEMON_START_REASONS, isExplicitDaemonStart = (reason) => EXPLICIT_DAEMON_START_REASONS.includes(reason), AGENT_STOP_REASONS, agentPostStopDesiredStateValidator, AgentStopReasonEnum, MODEL_SOURCES, ModelSourceEnum, modelSourceValidator;
27803
+ var init_agent = __esm(() => {
27804
+ init_values();
27805
+ init_v_literals_of();
27806
+ AGENT_HARNESSES = [
27807
+ "opencode",
27808
+ "opencode-sdk",
27809
+ "pi",
27810
+ "pi-sdk",
27811
+ "cursor",
27812
+ "cursor-sdk",
27813
+ "claude",
27814
+ "claude-sdk",
27815
+ "codex-sdk",
27816
+ "copilot",
27817
+ "commandcode"
27818
+ ];
27819
+ AgentHarnessEnum = Object.fromEntries(AGENT_HARNESSES.map((h) => [h, h]));
27820
+ agentHarnessValidator = v.union(...toLiteralValidators(AGENT_HARNESSES));
27821
+ AGENT_TYPES = ["remote", "custom"];
27822
+ AgentTypeEnum = Object.fromEntries(AGENT_TYPES.map((t) => [t, t]));
27823
+ agentTypeValidator = v.union(...toLiteralValidators(AGENT_TYPES));
27824
+ MACHINE_COMMAND_TYPES = ["start-agent", "restart-agent", "ping", "status"];
27825
+ MachineCommandTypeEnum = Object.fromEntries(MACHINE_COMMAND_TYPES.map((c) => [c, c]));
27826
+ machineCommandTypeValidator = v.union(...toLiteralValidators(MACHINE_COMMAND_TYPES));
27827
+ MACHINE_COMMAND_STATUSES = ["pending", "completed", "failed"];
27828
+ MachineCommandStatusEnum = Object.fromEntries(MACHINE_COMMAND_STATUSES.map((s) => [s, s]));
27829
+ machineCommandStatusValidator = v.union(...toLiteralValidators(MACHINE_COMMAND_STATUSES));
27830
+ AgentStartReasonCode = {
27831
+ USER_START: "user.start",
27832
+ USER_RESTART: "user.restart",
27833
+ USER_MANUAL_SPAWN: "user.manual_spawn",
27834
+ PLATFORM_TASK_MONITOR_NUDGE: "platform.task_monitor_nudge",
27835
+ PLATFORM_TASK_START_IN_NEW_SESSION: "platform.task_start_in_new_session",
27836
+ PLATFORM_PENDING_TASK_WAKE: "platform.pending_task_wake",
27837
+ PLATFORM_AUTO_RESTART_ON_NEW_CONTEXT: "platform.auto_restart_on_new_context",
27838
+ PLATFORM_TEAM_SWITCH: "platform.team_switch",
27839
+ TEST: "test"
27840
+ };
27841
+ AGENT_START_REASONS = [
27842
+ AgentStartReasonCode.USER_START,
27843
+ AgentStartReasonCode.USER_RESTART,
27844
+ AgentStartReasonCode.USER_MANUAL_SPAWN,
27845
+ AgentStartReasonCode.PLATFORM_TASK_MONITOR_NUDGE,
27846
+ AgentStartReasonCode.PLATFORM_TASK_START_IN_NEW_SESSION,
27847
+ AgentStartReasonCode.PLATFORM_PENDING_TASK_WAKE,
27848
+ AgentStartReasonCode.PLATFORM_AUTO_RESTART_ON_NEW_CONTEXT,
27849
+ AgentStartReasonCode.PLATFORM_TEAM_SWITCH,
27850
+ AgentStartReasonCode.TEST
27851
+ ];
27852
+ DaemonStartReasonCode = {
27853
+ RESPAWN: "daemon.respawn"
27854
+ };
27855
+ agentStartReasonValidator = v.union(...toLiteralValidators(AGENT_START_REASONS));
27856
+ USER_EXPLICIT_START_REASONS = [
27857
+ AgentStartReasonCode.USER_START,
27858
+ AgentStartReasonCode.USER_RESTART
27859
+ ];
27860
+ EXPLICIT_DAEMON_START_REASONS = [
27861
+ ...USER_EXPLICIT_START_REASONS,
27862
+ AgentStartReasonCode.USER_MANUAL_SPAWN,
27863
+ AgentStartReasonCode.PLATFORM_TASK_MONITOR_NUDGE,
27864
+ AgentStartReasonCode.PLATFORM_PENDING_TASK_WAKE,
27865
+ AgentStartReasonCode.PLATFORM_TASK_START_IN_NEW_SESSION,
27866
+ DaemonStartReasonCode.RESPAWN
27867
+ ];
27868
+ AGENT_STOP_REASONS = [
27869
+ "user.stop",
27870
+ "user.restart",
27871
+ "platform.dedup",
27872
+ "platform.team_switch",
27873
+ "platform.task_monitor_nudge",
27874
+ "platform.task_start_in_new_session",
27875
+ "daemon.respawn",
27876
+ "daemon.shutdown",
27877
+ "daemon.stop_timeout",
27878
+ "platform.ephemeral_task_complete",
27879
+ "platform.task_cancelled",
27880
+ "test"
27881
+ ];
27882
+ agentPostStopDesiredStateValidator = v.union(v.literal("running"), v.literal("stopped"));
27883
+ AgentStopReasonEnum = Object.fromEntries(AGENT_STOP_REASONS.map((r) => [r, r]));
27884
+ MODEL_SOURCES = ["team_config", "none"];
27885
+ ModelSourceEnum = Object.fromEntries(MODEL_SOURCES.map((s) => [s, s]));
27886
+ modelSourceValidator = v.union(...toLiteralValidators(MODEL_SOURCES));
27887
+ });
27888
+
27767
27889
  // ../../services/backend/src/domain/entities/harness/claude-sdk.config.ts
27768
27890
  var claudeSdkCapabilities;
27769
27891
  var init_claude_sdk_config = __esm(() => {
@@ -28230,7 +28352,7 @@ function createAgentProcessManagerService(deps) {
28230
28352
  await awaitWithinDeadline(deps.execution.stop({
28231
28353
  chatroomId: input.chatroomId,
28232
28354
  role: input.role,
28233
- reason: "daemon.respawn",
28355
+ reason: DaemonStartReasonCode.RESPAWN,
28234
28356
  pid: slot.pid,
28235
28357
  workingDir: input.workingDir
28236
28358
  }), remaining, input.signal);
@@ -28334,11 +28456,13 @@ function createAgentProcessManagerService(deps) {
28334
28456
  resumeTurnForSlot: (input) => deps.execution.resumeTurnForSlot(input),
28335
28457
  subscribeAgentTurnEnded: (handler) => deps.execution.subscribeAgentTurnEnded(handler),
28336
28458
  subscribeAgentStarted: (handler) => deps.execution.subscribeAgentStarted(handler),
28337
- subscribeAgentSessionLost: (handler) => deps.execution.subscribeAgentSessionLost(handler)
28459
+ subscribeAgentSessionLost: (handler) => deps.execution.subscribeAgentSessionLost(handler),
28460
+ subscribeAgentTurnProgress: (handler) => deps.execution.subscribeAgentTurnProgress(handler)
28338
28461
  };
28339
28462
  }
28340
28463
  var NATIVE_DELIVERY_SLOT_WAIT_MS = 120000, SLOT_CHANGE_POLL_MS = 100;
28341
28464
  var init_agent_process_manager_service = __esm(() => {
28465
+ init_agent();
28342
28466
  init_types();
28343
28467
  init_check_agent_slot();
28344
28468
  });
@@ -28743,86 +28867,6 @@ var init_native_turn_phase = __esm(() => {
28743
28867
  init_agent_slot();
28744
28868
  });
28745
28869
 
28746
- // ../../services/backend/src/domain/entities/_shared/v-literals-of.ts
28747
- var toLiteralValidators = (values3) => values3.map((v_) => v.literal(v_));
28748
- var init_v_literals_of = __esm(() => {
28749
- init_values();
28750
- });
28751
-
28752
- // ../../services/backend/src/domain/entities/agent.ts
28753
- var AGENT_HARNESSES, AgentHarnessEnum, agentHarnessValidator, AGENT_TYPES, AgentTypeEnum, agentTypeValidator, MACHINE_COMMAND_TYPES, MachineCommandTypeEnum, machineCommandTypeValidator, MACHINE_COMMAND_STATUSES, MachineCommandStatusEnum, machineCommandStatusValidator, AGENT_START_REASONS, AgentStartReasonEnum, agentStartReasonValidator, USER_EXPLICIT_START_REASONS, EXPLICIT_DAEMON_START_REASONS, isExplicitDaemonStart = (reason) => EXPLICIT_DAEMON_START_REASONS.includes(reason), AGENT_STOP_REASONS, agentPostStopDesiredStateValidator, AgentStopReasonEnum, MODEL_SOURCES, ModelSourceEnum, modelSourceValidator;
28754
- var init_agent = __esm(() => {
28755
- init_values();
28756
- init_v_literals_of();
28757
- AGENT_HARNESSES = [
28758
- "opencode",
28759
- "opencode-sdk",
28760
- "pi",
28761
- "pi-sdk",
28762
- "cursor",
28763
- "cursor-sdk",
28764
- "claude",
28765
- "claude-sdk",
28766
- "codex-sdk",
28767
- "copilot",
28768
- "commandcode"
28769
- ];
28770
- AgentHarnessEnum = Object.fromEntries(AGENT_HARNESSES.map((h) => [h, h]));
28771
- agentHarnessValidator = v.union(...toLiteralValidators(AGENT_HARNESSES));
28772
- AGENT_TYPES = ["remote", "custom"];
28773
- AgentTypeEnum = Object.fromEntries(AGENT_TYPES.map((t) => [t, t]));
28774
- agentTypeValidator = v.union(...toLiteralValidators(AGENT_TYPES));
28775
- MACHINE_COMMAND_TYPES = ["start-agent", "restart-agent", "ping", "status"];
28776
- MachineCommandTypeEnum = Object.fromEntries(MACHINE_COMMAND_TYPES.map((c) => [c, c]));
28777
- machineCommandTypeValidator = v.union(...toLiteralValidators(MACHINE_COMMAND_TYPES));
28778
- MACHINE_COMMAND_STATUSES = ["pending", "completed", "failed"];
28779
- MachineCommandStatusEnum = Object.fromEntries(MACHINE_COMMAND_STATUSES.map((s) => [s, s]));
28780
- machineCommandStatusValidator = v.union(...toLiteralValidators(MACHINE_COMMAND_STATUSES));
28781
- AGENT_START_REASONS = [
28782
- "user.start",
28783
- "user.restart",
28784
- "user.manual_spawn",
28785
- "platform.task_monitor_nudge",
28786
- "platform.task_start_in_new_session",
28787
- "platform.pending_task_wake",
28788
- "platform.auto_restart_on_new_context",
28789
- "platform.team_switch",
28790
- "test"
28791
- ];
28792
- AgentStartReasonEnum = Object.fromEntries(AGENT_START_REASONS.map((r) => [r, r]));
28793
- agentStartReasonValidator = v.union(...toLiteralValidators(AGENT_START_REASONS));
28794
- USER_EXPLICIT_START_REASONS = [
28795
- AgentStartReasonEnum["user.start"],
28796
- AgentStartReasonEnum["user.restart"]
28797
- ];
28798
- EXPLICIT_DAEMON_START_REASONS = [
28799
- ...USER_EXPLICIT_START_REASONS,
28800
- AgentStartReasonEnum["user.manual_spawn"],
28801
- AgentStartReasonEnum["platform.task_monitor_nudge"],
28802
- AgentStartReasonEnum["platform.task_start_in_new_session"],
28803
- "daemon.respawn"
28804
- ];
28805
- AGENT_STOP_REASONS = [
28806
- "user.stop",
28807
- "user.restart",
28808
- "platform.dedup",
28809
- "platform.team_switch",
28810
- "platform.task_monitor_nudge",
28811
- "platform.task_start_in_new_session",
28812
- "daemon.respawn",
28813
- "daemon.shutdown",
28814
- "daemon.stop_timeout",
28815
- "platform.ephemeral_task_complete",
28816
- "platform.task_cancelled",
28817
- "test"
28818
- ];
28819
- agentPostStopDesiredStateValidator = v.union(v.literal("running"), v.literal("stopped"));
28820
- AgentStopReasonEnum = Object.fromEntries(AGENT_STOP_REASONS.map((r) => [r, r]));
28821
- MODEL_SOURCES = ["team_config", "none"];
28822
- ModelSourceEnum = Object.fromEntries(MODEL_SOURCES.map((s) => [s, s]));
28823
- modelSourceValidator = v.union(...toLiteralValidators(MODEL_SOURCES));
28824
- });
28825
-
28826
28870
  // src/daemon/services/agent-process-service/infrastructure/adapters/orphan-process-tracker.ts
28827
28871
  import { createHash } from "node:crypto";
28828
28872
  import {
@@ -43645,7 +43689,7 @@ var init_agent_lifecycle_fact_schema = __esm(() => {
43645
43689
  ...agent,
43646
43690
  kind: exports_external.literal("status"),
43647
43691
  status: exports_external.enum(["offline", "starting", "waiting", "working", "stopping", "error"]),
43648
- errorSource: exports_external.enum(["configuration", "runtime", "task", "enhancer", "stop"]).optional(),
43692
+ errorSource: exports_external.enum(["configuration", "runtime", "task", "stop"]).optional(),
43649
43693
  errorCode: exports_external.string().optional(),
43650
43694
  errorMessage: exports_external.string().optional()
43651
43695
  }),
@@ -44651,29 +44695,24 @@ async function emitNativeWaitingAfterSpawn(ctx, harness, opts) {
44651
44695
  return false;
44652
44696
  }
44653
44697
  }
44654
- function fireTokenActivity(backend2, sessionId, chatroomId, role, now, lastReportedTokenAt, throttleMs) {
44698
+ function fireTokenActivity(onTurnProgress, chatroomId, role, now, lastReportedTokenAt, throttleMs) {
44655
44699
  const t = now();
44656
44700
  if (lastReportedTokenAt.value === 0 || t - lastReportedTokenAt.value >= throttleMs) {
44657
44701
  lastReportedTokenAt.value = t;
44658
- backend2.mutation(api.participants.recordHarnessActivity, {
44659
- sessionId,
44660
- chatroomId,
44661
- role
44662
- }).catch(() => {});
44702
+ onTurnProgress({ chatroomId, role });
44663
44703
  }
44664
44704
  }
44665
44705
  function wireTokenActivityReporting(opts) {
44666
44706
  if (!isTeamAgentRole(opts.role))
44667
44707
  return;
44708
+ const onTurnProgress = opts.onTurnProgress;
44709
+ if (!onTurnProgress)
44710
+ return;
44668
44711
  if (opts.activityEmitter) {
44669
44712
  opts.activityEmitter.onActivity((signal) => {
44670
44713
  if (signal.kind !== "progress" || !signal.isFirstForTurn)
44671
44714
  return;
44672
- opts.backend.mutation(api.participants.recordHarnessActivity, {
44673
- sessionId: opts.sessionId,
44674
- chatroomId: opts.chatroomId,
44675
- role: opts.role
44676
- }).catch(() => {});
44715
+ onTurnProgress({ chatroomId: opts.chatroomId, role: opts.role });
44677
44716
  });
44678
44717
  return;
44679
44718
  }
@@ -44684,13 +44723,12 @@ function wireTokenActivityReporting(opts) {
44684
44723
  if (!register)
44685
44724
  return;
44686
44725
  register(() => {
44687
- fireTokenActivity(opts.backend, opts.sessionId, opts.chatroomId, opts.role, now, lastReportedTokenAt, throttleMs);
44726
+ fireTokenActivity(onTurnProgress, opts.chatroomId, opts.role, now, lastReportedTokenAt, throttleMs);
44688
44727
  });
44689
44728
  }
44690
44729
  var NATIVE_TOKEN_ACTIVITY_THROTTLE_MS = 30000;
44691
44730
  var init_native_spawn_presence = __esm(() => {
44692
44731
  init_types();
44693
- init_api3();
44694
44732
  init_agent_lifecycle_fact();
44695
44733
  init_execution_kind();
44696
44734
  });
@@ -44713,6 +44751,7 @@ class AgentProcessManager {
44713
44751
  exitRetryQueue = [];
44714
44752
  exitRetryTimer = null;
44715
44753
  agentTurnEndedHandlers = new Set;
44754
+ agentTurnProgressHandlers = new Set;
44716
44755
  agentTurnEndedSequence = 0;
44717
44756
  agentStartedHandlers = new Set;
44718
44757
  agentSessionLostHandlers = new Set;
@@ -44800,6 +44839,19 @@ class AgentProcessManager {
44800
44839
  this.agentSessionLostHandlers.add(handler);
44801
44840
  return () => this.agentSessionLostHandlers.delete(handler);
44802
44841
  }
44842
+ subscribeAgentTurnProgress(handler) {
44843
+ this.agentTurnProgressHandlers.add(handler);
44844
+ return () => this.agentTurnProgressHandlers.delete(handler);
44845
+ }
44846
+ emitAgentTurnProgress(event) {
44847
+ for (const handler of this.agentTurnProgressHandlers) {
44848
+ try {
44849
+ handler(event);
44850
+ } catch (error51) {
44851
+ console.warn(`[AgentProcessManager] turn-progress handler failed for ${event.role}@${event.chatroomId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
44852
+ }
44853
+ }
44854
+ }
44803
44855
  clearSlotRuntimeState(slot) {
44804
44856
  slot.state = AGENT_SLOT_STATE.IDLE;
44805
44857
  slot.pid = undefined;
@@ -45373,7 +45425,7 @@ class AgentProcessManager {
45373
45425
  slot.state = AGENT_SLOT_STATE.STOPPING;
45374
45426
  const stopGeneration = this.bumpStopGeneration(slot);
45375
45427
  slot.stoppingSince = this.deps.clock.now();
45376
- await this.doStop(key, slot, pid, { chatroomId, role, reason: "daemon.respawn", workingDir }, stopGeneration);
45428
+ await this.doStop(key, slot, pid, { chatroomId, role, reason: DaemonStartReasonCode.RESPAWN, workingDir }, stopGeneration);
45377
45429
  }
45378
45430
  }
45379
45431
  async killPersistedProcessIfAlive(chatroomId, role, workingDir) {
@@ -45719,13 +45771,12 @@ class AgentProcessManager {
45719
45771
  });
45720
45772
  }
45721
45773
  wireTokenActivityReporting({
45722
- backend: this.deps.backend,
45723
- sessionId: this.deps.sessionId,
45724
45774
  chatroomId: opts.chatroomId,
45725
45775
  role: opts.role,
45726
45776
  spawnResult,
45727
45777
  now: () => this.deps.clock.now(),
45728
- activityEmitter: spawnResult.activityEmitter
45778
+ activityEmitter: spawnResult.activityEmitter,
45779
+ onTurnProgress: (event) => this.emitAgentTurnProgress(event)
45729
45780
  });
45730
45781
  }
45731
45782
  async finalizeRunningSlot(key, slot, opts, spawnResult, wantResume) {
@@ -45742,7 +45793,7 @@ class AgentProcessManager {
45742
45793
  await this.emitNativeWaiting(opts.chatroomId, opts.role, opts.agentHarness);
45743
45794
  if (getHarnessCapabilities(opts.agentHarness).supportsNativeIntegration) {
45744
45795
  for (const handler of this.agentStartedHandlers) {
45745
- handler({ chatroomId: opts.chatroomId, role: opts.role }).catch((error51) => {
45796
+ handler({ chatroomId: opts.chatroomId, role: opts.role, reason: opts.reason }).catch((error51) => {
45746
45797
  console.warn(`[AgentProcessManager] native delivery after start failed for ${opts.role}@${opts.chatroomId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
45747
45798
  });
45748
45799
  }
@@ -45763,6 +45814,9 @@ class AgentProcessManager {
45763
45814
  }
45764
45815
  async doEnsureRunning(key, slot, opts) {
45765
45816
  slot.state = AGENT_SLOT_STATE.SPAWNING;
45817
+ const rateLimit = this.checkRateLimitGate(opts, slot);
45818
+ if (rateLimit)
45819
+ return rateLimit;
45766
45820
  const authorization = await this.deps.backend.mutation(api.machines.authorizeAgentStart, {
45767
45821
  sessionId: this.deps.sessionId,
45768
45822
  machineId: this.deps.machineId,
@@ -45777,9 +45831,6 @@ class AgentProcessManager {
45777
45831
  const wantResume = opts.wantResume;
45778
45832
  console.log(`[AgentProcessManager] harness start: role=${opts.role} harness=${opts.agentHarness} wantResume=${wantResume} reason=${opts.reason}`);
45779
45833
  try {
45780
- const rateLimit = this.checkRateLimitGate(opts, slot);
45781
- if (rateLimit)
45782
- return rateLimit;
45783
45834
  const workingDir = await this.validateWorkingDirGate(opts, slot);
45784
45835
  if (workingDir)
45785
45836
  return workingDir;
@@ -46193,7 +46244,7 @@ async function ensureColdSessionBeforeNativeInject(task, deps) {
46193
46244
  agentHarness,
46194
46245
  model,
46195
46246
  workingDir,
46196
- reason: AgentStartReasonEnum["platform.task_start_in_new_session"],
46247
+ reason: AgentStartReasonCode.PLATFORM_TASK_START_IN_NEW_SESSION,
46197
46248
  wantResume: false
46198
46249
  }, context5.signal);
46199
46250
  return waitForHarnessSessionId(deps.agentMgr, chatroomId, role, context5.signal);
@@ -46941,6 +46992,8 @@ function createConvexNativeTaskDeliveryGateway(backend2) {
46941
46992
  deliveryKind: "native_inject"
46942
46993
  });
46943
46994
  },
46995
+ readTask: async (args2) => await backend2.mutation(api.tasks.readTask, args2),
46996
+ markReceiptStarted: async (args2) => await backend2.mutation(api.taskDeliveryReceipts.markStarted, args2),
46944
46997
  joinWaitingParticipant: async (args2) => {
46945
46998
  await backend2.mutation(api.participants.join, {
46946
46999
  ...args2,
@@ -46987,6 +47040,7 @@ var init_convex_native_task_delivery_gateway = __esm(() => {
46987
47040
  // src/daemon/services/task-service/service/task-service.ts
46988
47041
  function createTaskService(deps) {
46989
47042
  const gateway = createConvexNativeTaskDeliveryGateway(deps.backend);
47043
+ const handoffRepository = deps.handoffRepository;
46990
47044
  const taskInboxState = new TaskInboxState;
46991
47045
  const listeners = new Set;
46992
47046
  let stopInboxWatch;
@@ -46996,6 +47050,120 @@ function createTaskService(deps) {
46996
47050
  const scheduledEventIds = new Set;
46997
47051
  const deliveredEventIds = new Set;
46998
47052
  const taskChains = new Map;
47053
+ const maxBootstrapSweepFailures = 3;
47054
+ let bootstrapSweepPending = true;
47055
+ let bootstrapSweepFailures = 0;
47056
+ let bootstrapSweepInFlight = false;
47057
+ let bootstrapSweepPromise;
47058
+ const REDIVERY_ATTEMPT_CAP = 3;
47059
+ const turnEndAttemptCounts = new Map;
47060
+ const redeliveryExhaustedKeys = new Set;
47061
+ const redeliveryKey = (chatroomId, role, taskId) => `${chatroomId}:${role.toLowerCase()}:${taskId}`;
47062
+ const recordUncoveredTurnEnd = async (args2) => {
47063
+ const key = redeliveryKey(args2.chatroomId, args2.role, args2.taskId);
47064
+ if (redeliveryExhaustedKeys.has(key))
47065
+ return { exceeded: true };
47066
+ const attempts = (turnEndAttemptCounts.get(key) ?? 0) + 1;
47067
+ turnEndAttemptCounts.set(key, attempts);
47068
+ if (attempts < REDIVERY_ATTEMPT_CAP)
47069
+ return { exceeded: false };
47070
+ redeliveryExhaustedKeys.add(key);
47071
+ try {
47072
+ await gateway.recordDeliveryFailure({
47073
+ sessionId: deps.sessionId,
47074
+ machineId: deps.machineId,
47075
+ taskId: args2.taskId,
47076
+ reason: "redelivery_exhausted"
47077
+ });
47078
+ } catch (error51) {
47079
+ console.warn(`[TaskService] redelivery-exhausted failure record failed chatroom=${args2.chatroomId} role=${args2.role} task=${args2.taskId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
47080
+ }
47081
+ console.log(`[NativeDelivery:redelivery-exhausted] chatroom=${args2.chatroomId} role=${args2.role} task=${args2.taskId} attempts=${attempts} — release/redelivery stopped; agent restart (user.start) resets`);
47082
+ return { exceeded: true };
47083
+ };
47084
+ const isRedeliveryExhausted = (args2) => redeliveryExhaustedKeys.has(redeliveryKey(args2.chatroomId, args2.role, args2.taskId));
47085
+ const clearRedeliveryTrackingForTask = (chatroomId, role, taskId) => {
47086
+ const key = redeliveryKey(chatroomId, role, taskId);
47087
+ turnEndAttemptCounts.delete(key);
47088
+ redeliveryExhaustedKeys.delete(key);
47089
+ };
47090
+ const sweepUncoveredInProgressTasks = async () => {
47091
+ let released = 0;
47092
+ let failures3 = 0;
47093
+ for (const task of taskInboxState.listAll()) {
47094
+ if (task.status !== "in_progress")
47095
+ continue;
47096
+ try {
47097
+ const handoff = await handoffRepository.getLatest(task.chatroomId, task.agentConfig.role);
47098
+ if (handoff?.taskIds.includes(task.taskId))
47099
+ continue;
47100
+ const result = await gateway.releaseTaskAfterTurnFailure({
47101
+ sessionId: deps.sessionId,
47102
+ chatroomId: task.chatroomId,
47103
+ role: task.agentConfig.role,
47104
+ taskId: task.taskId
47105
+ });
47106
+ taskInboxState.markStatus(task.chatroomId, task.agentConfig.role, task.taskId, result.status, result.updatedAt);
47107
+ released += 1;
47108
+ } catch (error51) {
47109
+ failures3 += 1;
47110
+ console.warn(`[TaskService] bootstrap task release failed chatroom=${task.chatroomId} task=${task.taskId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
47111
+ }
47112
+ }
47113
+ if (failures3 > 0)
47114
+ throw new Error(`${failures3} bootstrap task release(s) failed`);
47115
+ return released;
47116
+ };
47117
+ const releaseTask = async (args2) => {
47118
+ const result = await gateway.releaseTaskAfterTurnFailure({
47119
+ sessionId: deps.sessionId,
47120
+ chatroomId: args2.chatroomId,
47121
+ role: args2.role,
47122
+ taskId: args2.taskId
47123
+ });
47124
+ taskInboxState.markStatus(args2.chatroomId, args2.role, args2.taskId, result.status, result.updatedAt);
47125
+ const currentTask = taskInboxState.getForRole(args2.chatroomId, args2.role, args2.taskId);
47126
+ if (currentTask?.status === "pending") {
47127
+ pendingTaskReconciliationWatcher.watch(currentTask);
47128
+ } else {
47129
+ pendingTaskReconciliationWatcher.clear(args2.chatroomId, args2.role, args2.taskId);
47130
+ }
47131
+ return result;
47132
+ };
47133
+ const clearRoleRedeliveryTracking = (chatroomId, role) => {
47134
+ const prefix = `${chatroomId}:${role.toLowerCase()}:`;
47135
+ for (const key of [...turnEndAttemptCounts.keys()]) {
47136
+ if (key.startsWith(prefix))
47137
+ turnEndAttemptCounts.delete(key);
47138
+ }
47139
+ for (const key of [...redeliveryExhaustedKeys]) {
47140
+ if (key.startsWith(prefix))
47141
+ redeliveryExhaustedKeys.delete(key);
47142
+ }
47143
+ };
47144
+ const runBootstrapSweep = () => {
47145
+ if (!bootstrapSweepPending)
47146
+ return Promise.resolve();
47147
+ if (bootstrapSweepInFlight)
47148
+ return bootstrapSweepPromise ?? Promise.resolve();
47149
+ bootstrapSweepInFlight = true;
47150
+ bootstrapSweepPromise = sweepUncoveredInProgressTasks().then(() => {
47151
+ bootstrapSweepPending = false;
47152
+ bootstrapSweepFailures = 0;
47153
+ }).catch((error51) => {
47154
+ bootstrapSweepFailures += 1;
47155
+ if (bootstrapSweepFailures >= maxBootstrapSweepFailures) {
47156
+ bootstrapSweepPending = false;
47157
+ console.warn("[TaskService] bootstrap sweep giving up after 3 failed attempts");
47158
+ } else {
47159
+ console.warn(`[TaskService] bootstrap sweep failed (attempt ${bootstrapSweepFailures}/3): ${error51 instanceof Error ? error51.message : String(error51)}`);
47160
+ }
47161
+ }).finally(() => {
47162
+ bootstrapSweepInFlight = false;
47163
+ bootstrapSweepPromise = undefined;
47164
+ });
47165
+ return bootstrapSweepPromise;
47166
+ };
46999
47167
  const notifyForDelivery = async (notification) => {
47000
47168
  const results = await Promise.all([...listeners].map((listener) => Promise.resolve(listener(notification))));
47001
47169
  return {
@@ -47009,8 +47177,12 @@ function createTaskService(deps) {
47009
47177
  const applyInboxEvent = (event) => {
47010
47178
  if (event.eventType === "task_deleted" /* TaskDeleted */ || event.task.status === "completed") {
47011
47179
  taskInboxState.remove(event.chatroomId, event.role, event.taskId, event.task.updatedAt);
47180
+ clearRedeliveryTrackingForTask(event.chatroomId, event.role, event.taskId);
47012
47181
  return "handled";
47013
47182
  }
47183
+ if (event.eventType === "task_assigned" /* TaskAssigned */) {
47184
+ clearRedeliveryTrackingForTask(event.chatroomId, event.role, event.taskId);
47185
+ }
47014
47186
  return taskInboxState.upsert([
47015
47187
  {
47016
47188
  taskId: event.task.taskId,
@@ -47119,6 +47291,7 @@ function createTaskService(deps) {
47119
47291
  });
47120
47292
  const reconcileTaskStatuses = (tasks) => {
47121
47293
  taskInboxState.reconcileStatuses(tasks);
47294
+ runBootstrapSweep();
47122
47295
  for (const task of tasks) {
47123
47296
  if (task.status === "pending" || task.status === "acknowledged") {
47124
47297
  pendingTaskReconciliationWatcher.watch(task);
@@ -47158,7 +47331,9 @@ function createTaskService(deps) {
47158
47331
  machineId: deps.machineId
47159
47332
  });
47160
47333
  reconcileTaskStatuses(statusTasks);
47334
+ await runBootstrapSweep();
47161
47335
  } catch (error51) {
47336
+ bootstrapSweepPending = true;
47162
47337
  console.warn("[TaskService] task-status bootstrap failed:", error51);
47163
47338
  }
47164
47339
  if (wsClient2 && !stopInboxWatch) {
@@ -47193,26 +47368,47 @@ function createTaskService(deps) {
47193
47368
  acknowledgementRetryWatcher.stop();
47194
47369
  pendingTaskReconciliationWatcher.stop();
47195
47370
  taskChains.clear();
47371
+ handoffRepository.close();
47196
47372
  },
47197
47373
  listTasksForRole: (chatroomId, role) => taskInboxState.listForRole(chatroomId, role),
47198
47374
  listAllTasks: () => taskInboxState.listAll(),
47199
47375
  debugState: (chatroomId) => buildTaskServiceDebugState({ taskInboxState, chatroomId }),
47200
47376
  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);
47377
+ recordHandoffOutcome: async ({
47378
+ chatroomId,
47379
+ role,
47380
+ targetRole,
47381
+ nextTask,
47382
+ taskIds: providedTaskIds
47383
+ }) => {
47384
+ const taskIds = providedTaskIds ?? taskInboxState.listForRole(chatroomId, role).filter((task) => task.status === "acknowledged" || task.status === "in_progress").map((task) => task.taskId);
47385
+ await handoffRepository.record({
47386
+ chatroomId,
47387
+ role,
47388
+ taskIds,
47389
+ ...nextTask?.taskId ? { nextTaskId: nextTask.taskId } : {},
47390
+ targetRole,
47391
+ handedOffAt: Date.now()
47392
+ });
47393
+ const now = Date.now();
47394
+ for (const taskId of taskIds)
47395
+ taskInboxState.remove(chatroomId, role, taskId, now);
47396
+ if (nextTask)
47397
+ taskInboxState.upsert([nextTask]);
47398
+ },
47399
+ getLatestHandoff: (chatroomId, role) => handoffRepository.getLatest(chatroomId, role),
47400
+ sweepUncoveredInProgressTasks,
47401
+ releaseTaskAfterTurnFailure: (args2) => releaseTask(args2),
47402
+ handleAgentRestart: async ({ chatroomId, role }) => {
47403
+ clearRoleRedeliveryTracking(chatroomId, role);
47404
+ const inFlightTasks = taskInboxState.listForRole(chatroomId, role).filter((task) => task.status === "acknowledged" || task.status === "in_progress");
47405
+ for (const task of inFlightTasks) {
47406
+ try {
47407
+ await releaseTask({ chatroomId, role, taskId: task.taskId });
47408
+ } catch (error51) {
47409
+ console.warn(`[TaskService] agent-restart release failed chatroom=${chatroomId} role=${role} task=${task.taskId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
47410
+ }
47214
47411
  }
47215
- return result;
47216
47412
  },
47217
47413
  loadAssignedTaskForAction: async ({ chatroomId, role, taskId }) => {
47218
47414
  const task = await gateway.loadAssignedTaskForAction({
@@ -47223,6 +47419,27 @@ function createTaskService(deps) {
47223
47419
  });
47224
47420
  return task?.chatroomId === chatroomId ? task : null;
47225
47421
  },
47422
+ handleAgentTurnProgress: async ({ chatroomId, role, taskId }) => {
47423
+ const task = taskInboxState.getForRole(chatroomId, role, taskId);
47424
+ if (!task)
47425
+ return;
47426
+ if (task.status !== "acknowledged" && task.status !== "in_progress")
47427
+ return;
47428
+ if (task.status === "acknowledged") {
47429
+ try {
47430
+ await gateway.readTask({ sessionId: deps.sessionId, chatroomId, role, taskId });
47431
+ taskInboxState.markStatus(chatroomId, role, taskId, "in_progress", Date.now());
47432
+ } catch (error51) {
47433
+ console.warn(`[TaskService] turn-progress read failed chatroom=${chatroomId} role=${role} task=${taskId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
47434
+ return;
47435
+ }
47436
+ }
47437
+ try {
47438
+ await gateway.markReceiptStarted({ sessionId: deps.sessionId, chatroomId, role, taskId });
47439
+ } catch (error51) {
47440
+ console.warn(`[TaskService] turn-progress receipt mark failed chatroom=${chatroomId} role=${role} task=${taskId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
47441
+ }
47442
+ },
47226
47443
  recordDeliveryFailure: ({ taskId, reason }) => gateway.recordDeliveryFailure({
47227
47444
  sessionId: deps.sessionId,
47228
47445
  machineId: deps.machineId,
@@ -47234,7 +47451,12 @@ function createTaskService(deps) {
47234
47451
  machineId: deps.machineId,
47235
47452
  taskId,
47236
47453
  ...expectedReason ? { expectedReason } : {}
47237
- })
47454
+ }),
47455
+ recordUncoveredTurnEnd,
47456
+ isRedeliveryExhausted,
47457
+ clearRedeliveryTracking: ({ chatroomId, role }) => {
47458
+ clearRoleRedeliveryTracking(chatroomId, role);
47459
+ }
47238
47460
  };
47239
47461
  return service4;
47240
47462
  }
@@ -47247,6 +47469,63 @@ var init_task_service = __esm(() => {
47247
47469
  init_convex_native_task_delivery_gateway();
47248
47470
  });
47249
47471
 
47472
+ // src/daemon/services/task-service/infrastructure/repository/task-handoff-repository.ts
47473
+ import { mkdirSync as mkdirSync2 } from "node:fs";
47474
+ import { dirname as dirname2 } from "node:path";
47475
+ import { DatabaseSync } from "node:sqlite";
47476
+ function normalizeRole(role) {
47477
+ return role.toLowerCase();
47478
+ }
47479
+ function mapRow(row) {
47480
+ const taskIds = JSON.parse(row.task_ids_json);
47481
+ if (!Array.isArray(taskIds) || !taskIds.every((taskId) => typeof taskId === "string")) {
47482
+ throw new Error("Invalid task handoff task_ids_json");
47483
+ }
47484
+ return {
47485
+ chatroomId: row.chatroom_id,
47486
+ role: row.role,
47487
+ taskIds,
47488
+ ...row.next_task_id ? { nextTaskId: row.next_task_id } : {},
47489
+ targetRole: row.target_role,
47490
+ handedOffAt: row.handed_off_at
47491
+ };
47492
+ }
47493
+ function createTaskHandoffRepository(dbPath) {
47494
+ mkdirSync2(dirname2(dbPath), { recursive: true });
47495
+ const db = new DatabaseSync(dbPath);
47496
+ db.exec(`
47497
+ CREATE TABLE IF NOT EXISTS task_handoffs (
47498
+ chatroom_id TEXT NOT NULL,
47499
+ role TEXT NOT NULL,
47500
+ task_ids_json TEXT NOT NULL,
47501
+ next_task_id TEXT,
47502
+ target_role TEXT NOT NULL,
47503
+ handed_off_at INTEGER NOT NULL,
47504
+ PRIMARY KEY (chatroom_id, role)
47505
+ )
47506
+ `);
47507
+ return {
47508
+ async record(record2) {
47509
+ db.prepare(`INSERT INTO task_handoffs
47510
+ (chatroom_id, role, task_ids_json, next_task_id, target_role, handed_off_at)
47511
+ VALUES (?, ?, ?, ?, ?, ?)
47512
+ ON CONFLICT(chatroom_id, role) DO UPDATE SET
47513
+ task_ids_json = excluded.task_ids_json,
47514
+ next_task_id = excluded.next_task_id,
47515
+ target_role = excluded.target_role,
47516
+ handed_off_at = excluded.handed_off_at`).run(record2.chatroomId, normalizeRole(record2.role), JSON.stringify(record2.taskIds), record2.nextTaskId ?? null, record2.targetRole, record2.handedOffAt);
47517
+ },
47518
+ async getLatest(chatroomId, role) {
47519
+ 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));
47520
+ return row ? mapRow(row) : null;
47521
+ },
47522
+ close() {
47523
+ db.close();
47524
+ }
47525
+ };
47526
+ }
47527
+ var init_task_handoff_repository = () => {};
47528
+
47250
47529
  // src/daemon/services/task-service/infrastructure/adapters/daemon-audit-port.ts
47251
47530
  function createDaemonAuditPort(logEvent) {
47252
47531
  return { emit: (event) => logDaemonAuditEvent(logEvent, event) };
@@ -47279,6 +47558,9 @@ function decideNextDelivery(tasks, context5) {
47279
47558
  if (!context5.isNativeHarness(context5.agentConfig.agentHarness)) {
47280
47559
  return { kind: "failed", taskId: task.taskId, reason: "unsupported_harness" };
47281
47560
  }
47561
+ if (context5.isRedeliveryExhausted(task.taskId)) {
47562
+ return { kind: "deduplicated", reason: "redelivery_exhausted", taskId: task.taskId };
47563
+ }
47282
47564
  const blockReason = context5.explainNativeDeliveryBlock(task);
47283
47565
  if (blockReason === "acknowledged_wrong_role") {
47284
47566
  return { kind: "failed", taskId: task.taskId, reason: "assigned_elsewhere" };
@@ -47394,7 +47676,8 @@ class NativeTaskDeliveryCoordinator {
47394
47676
  agentConfig,
47395
47677
  configState,
47396
47678
  isNativeHarness: taskService.isNativeHarness,
47397
- explainNativeDeliveryBlock: taskService.explainNativeDeliveryBlock
47679
+ explainNativeDeliveryBlock: taskService.explainNativeDeliveryBlock,
47680
+ isRedeliveryExhausted: (taskId) => taskService.isRedeliveryExhausted({ chatroomId: firstTask.chatroomId, role, taskId })
47398
47681
  });
47399
47682
  const attemptId = `${Date.now()}-${firstTask.taskId}`;
47400
47683
  const decisionReason = "reason" in decision ? decision.reason : undefined;
@@ -47465,6 +47748,7 @@ var init_task_service2 = __esm(() => {
47465
47748
  init_native_task_injector_logic();
47466
47749
  init_task_service();
47467
47750
  init_convex_native_task_delivery_gateway();
47751
+ init_task_handoff_repository();
47468
47752
  init_daemon_audit_port();
47469
47753
  init_native_task_delivery_coordinator();
47470
47754
  });
@@ -47485,7 +47769,7 @@ async function processTasksUpdate(taskService, configurationService, pass, isTas
47485
47769
  agentHarness: agentConfig.agentHarness,
47486
47770
  model: agentConfig.model ?? "",
47487
47771
  workingDir: agentConfig.workingDir,
47488
- reason: AgentStartReasonEnum["platform.pending_task_wake"],
47772
+ reason: AgentStartReasonCode.PLATFORM_PENDING_TASK_WAKE,
47489
47773
  wantResume: false,
47490
47774
  taskId: task.taskId
47491
47775
  };
@@ -47567,6 +47851,7 @@ class AgentWorkManager {
47567
47851
  unsubscribeAgentTurnEnded;
47568
47852
  unsubscribeAgentStarted;
47569
47853
  unsubscribeAgentSessionLost;
47854
+ unsubscribeAgentTurnProgress;
47570
47855
  reconcileStates = new Map;
47571
47856
  unsubscribeTaskService;
47572
47857
  nativeTaskDeliveryQueue;
@@ -47599,16 +47884,33 @@ class AgentWorkManager {
47599
47884
  releaseTaskAfterTurnFailure: deps.taskService.releaseTaskAfterTurnFailure,
47600
47885
  recordDeliveryFailure: deps.taskService.recordDeliveryFailure,
47601
47886
  clearDeliveryFailure: deps.taskService.clearDeliveryFailure,
47887
+ recordUncoveredTurnEnd: deps.taskService.recordUncoveredTurnEnd,
47888
+ isRedeliveryExhausted: deps.taskService.isRedeliveryExhausted,
47889
+ clearRedeliveryTracking: deps.taskService.clearRedeliveryTracking,
47602
47890
  loadAssignedTaskForAction: deps.taskService.loadAssignedTaskForAction,
47603
47891
  deliverNativeTask: (task, harnessSessionId, onTaskDelivered) => this.nativeTaskDeliveryQueue.enqueue({ task, harnessSessionId, onTaskDelivered })
47604
47892
  };
47605
47893
  this.unsubscribeAgentTurnEnded = deps.agentMgr.subscribeAgentTurnEnded((event) => this.handleAgentTurnEnded(event));
47606
47894
  this.unsubscribeAgentStarted = deps.agentMgr.subscribeAgentStarted((event) => this.handleAgentStarted(event));
47607
47895
  this.unsubscribeAgentSessionLost = deps.agentMgr.subscribeAgentSessionLost((event) => this.handleAgentSessionLost(event));
47896
+ this.unsubscribeAgentTurnProgress = deps.agentMgr.subscribeAgentTurnProgress((event) => {
47897
+ const activeTask = this.deps.agentTaskState.get(event);
47898
+ if (!activeTask)
47899
+ return;
47900
+ this.deps.taskService.handleAgentTurnProgress({ ...event, taskId: activeTask.taskId }).catch((error51) => {
47901
+ console.warn(`[AgentProcessManager] turn-progress handling failed for ${event.role}@${event.chatroomId}: ${error51 instanceof Error ? error51.message : String(error51)}`);
47902
+ });
47903
+ });
47608
47904
  this.unsubscribeTaskService = deps.taskService.subscribe((notification) => this.handleTaskServiceNotification(notification));
47609
47905
  }
47610
47906
  async handleAgentStarted(event) {
47611
47907
  this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
47908
+ if (event.reason === "user.start") {
47909
+ this.deps.taskService.clearRedeliveryTracking({
47910
+ chatroomId: event.chatroomId,
47911
+ role: event.role
47912
+ });
47913
+ }
47612
47914
  await this.requestReconcile({
47613
47915
  chatroomId: event.chatroomId,
47614
47916
  role: event.role,
@@ -47626,24 +47928,43 @@ class AgentWorkManager {
47626
47928
  console.warn(`[NativeDelivery:failure] role=${event.role} chatroom=${event.chatroomId} operation=session-loss-reconcile error=${error51 instanceof Error ? error51.message : String(error51)}`);
47627
47929
  });
47628
47930
  }
47931
+ async recoverTaskAfterUncoveredTurnEnd(event, activeTask, phase) {
47932
+ const { chatroomId, role } = event;
47933
+ const { exceeded } = await this.deps.taskService.recordUncoveredTurnEnd({
47934
+ chatroomId,
47935
+ role,
47936
+ taskId: activeTask.taskId
47937
+ });
47938
+ if (exceeded)
47939
+ return "exceeded";
47940
+ try {
47941
+ await this.deps.taskService.releaseTaskAfterTurnFailure({
47942
+ chatroomId,
47943
+ role,
47944
+ taskId: activeTask.taskId
47945
+ });
47946
+ } catch (error51) {
47947
+ 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)}`);
47948
+ return "recovery-failed";
47949
+ }
47950
+ return "released";
47951
+ }
47629
47952
  async handleAgentTurnEnded(event) {
47630
47953
  const completion = event.completion;
47631
47954
  if (completion.status !== "completed") {
47632
- const activeTask = this.deps.agentTaskState.get({
47955
+ const activeTask2 = this.deps.agentTaskState.get({
47633
47956
  chatroomId: event.chatroomId,
47634
47957
  role: event.role
47635
47958
  });
47636
47959
  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)}`);
47960
+ 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"}`);
47961
+ if (activeTask2) {
47962
+ const recovery = await this.recoverTaskAfterUncoveredTurnEnd(event, activeTask2, "turn-failed");
47963
+ if (recovery === "exceeded") {
47964
+ this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
47965
+ return { kind: "release-slot" };
47966
+ }
47967
+ if (recovery === "recovery-failed") {
47647
47968
  return { kind: "hold-slot", reason: "task-recovery-failed" };
47648
47969
  }
47649
47970
  this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
@@ -47652,7 +47973,7 @@ class AgentWorkManager {
47652
47973
  kind: "turn_failed",
47653
47974
  chatroomId: event.chatroomId,
47654
47975
  role: event.role,
47655
- ...activeTask?.taskId ? { taskId: activeTask.taskId } : {},
47976
+ ...activeTask2?.taskId ? { taskId: activeTask2.taskId } : {},
47656
47977
  ...event.slot.harnessSessionId ? { harnessSessionId: event.slot.harnessSessionId } : {},
47657
47978
  turnId: completion.turnId,
47658
47979
  status: completion.status,
@@ -47672,6 +47993,19 @@ class AgentWorkManager {
47672
47993
  }
47673
47994
  return { kind: "release-slot" };
47674
47995
  }
47996
+ const activeTask = this.deps.agentTaskState.get({
47997
+ chatroomId: event.chatroomId,
47998
+ role: event.role
47999
+ });
48000
+ if (activeTask) {
48001
+ const handoff = await this.deps.taskService.getLatestHandoff(event.chatroomId, event.role);
48002
+ if (!handoff?.taskIds.includes(activeTask.taskId)) {
48003
+ const recovery = await this.recoverTaskAfterUncoveredTurnEnd(event, activeTask, "turn-ended");
48004
+ if (recovery === "recovery-failed") {
48005
+ return { kind: "hold-slot", reason: "task-recovery-failed" };
48006
+ }
48007
+ }
48008
+ }
47675
48009
  this.deps.agentTaskState.clear({ chatroomId: event.chatroomId, role: event.role });
47676
48010
  try {
47677
48011
  await this.deps.lifecycleOutbox.enqueue(buildAgentStatusFact({
@@ -47696,6 +48030,7 @@ class AgentWorkManager {
47696
48030
  this.unsubscribeAgentTurnEnded();
47697
48031
  this.unsubscribeAgentStarted();
47698
48032
  this.unsubscribeAgentSessionLost();
48033
+ this.unsubscribeAgentTurnProgress();
47699
48034
  this.unsubscribeTaskService?.();
47700
48035
  this.unsubscribeTaskService = undefined;
47701
48036
  this.nativeTaskDeliveryQueue.stop();
@@ -47809,6 +48144,11 @@ class AgentWorkManager {
47809
48144
  });
47810
48145
  return delivered;
47811
48146
  }
48147
+ async handleAgentRestart(args2) {
48148
+ resetRoleDeliveryState(args2.chatroomId, args2.role);
48149
+ this.deps.agentTaskState.clear(args2);
48150
+ await this.deps.taskService.handleAgentRestart(args2);
48151
+ }
47812
48152
  async requestReconcileForTasks(tasks, source) {
47813
48153
  const roles = new Set(tasks.map((snapshot) => `${snapshot.chatroomId}:${snapshot.agentConfig.role.toLowerCase()}`));
47814
48154
  await Promise.all([...roles].map((key) => {
@@ -47844,6 +48184,7 @@ var init_agent_work_manager = __esm(() => {
47844
48184
  init_native_task_injector_logic();
47845
48185
  init_convex_native_task_delivery_gateway();
47846
48186
  init_daemon_audit_port();
48187
+ init_native_task_delivery_coordinator();
47847
48188
  init_task_delivery_processor();
47848
48189
  init_native_task_injector();
47849
48190
  });
@@ -50852,12 +51193,12 @@ var init_model_provider = () => {};
50852
51193
  // src/daemon/infrastructure/local/harness/services/cursor-sdk/cursor-sdk-package.ts
50853
51194
  import { existsSync as existsSync2, readFileSync as readFileSync4 } from "node:fs";
50854
51195
  import { createRequire as createRequire3 } from "node:module";
50855
- import { dirname as dirname2, join as join8 } from "node:path";
51196
+ import { dirname as dirname3, join as join8 } from "node:path";
50856
51197
  import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
50857
51198
  function resolveChatroomCliRoot(moduleRef = import.meta.url) {
50858
51199
  const filePath = moduleRef.startsWith("file:") ? fileURLToPath2(moduleRef) : moduleRef;
50859
- let dir = dirname2(filePath);
50860
- while (dir !== dirname2(dir)) {
51200
+ let dir = dirname3(filePath);
51201
+ while (dir !== dirname3(dir)) {
50861
51202
  const packageJsonPath = join8(dir, "package.json");
50862
51203
  if (existsSync2(packageJsonPath)) {
50863
51204
  const pkg = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
@@ -50865,7 +51206,7 @@ function resolveChatroomCliRoot(moduleRef = import.meta.url) {
50865
51206
  return dir;
50866
51207
  }
50867
51208
  }
50868
- dir = dirname2(dir);
51209
+ dir = dirname3(dir);
50869
51210
  }
50870
51211
  throw new CursorSdkPackageError(`Could not locate chatroom-cli package root while resolving @cursor/sdk. ${REINSTALL_HINT}`);
50871
51212
  }
@@ -50880,7 +51221,7 @@ function readPinnedSdkVersion(chatroomCliRoot) {
50880
51221
  return match17[1];
50881
51222
  }
50882
51223
  function readInstalledSdkVersion(entryPath) {
50883
- const packageJsonPath = join8(dirname2(entryPath), "..", "..", "package.json");
51224
+ const packageJsonPath = join8(dirname3(entryPath), "..", "..", "package.json");
50884
51225
  const pkg = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
50885
51226
  return pkg.version;
50886
51227
  }
@@ -51781,12 +52122,12 @@ var init_claude = __esm(() => {
51781
52122
  // src/daemon/infrastructure/local/harness/services/claude-sdk/claude-sdk-package.ts
51782
52123
  import { existsSync as existsSync3, readFileSync as readFileSync5 } from "node:fs";
51783
52124
  import { createRequire as createRequire4 } from "node:module";
51784
- import { dirname as dirname3, join as join9 } from "node:path";
52125
+ import { dirname as dirname4, join as join9 } from "node:path";
51785
52126
  import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL2 } from "node:url";
51786
52127
  function resolveChatroomCliRoot2(moduleRef = import.meta.url) {
51787
52128
  const filePath = moduleRef.startsWith("file:") ? fileURLToPath3(moduleRef) : moduleRef;
51788
- let dir = dirname3(filePath);
51789
- while (dir !== dirname3(dir)) {
52129
+ let dir = dirname4(filePath);
52130
+ while (dir !== dirname4(dir)) {
51790
52131
  const packageJsonPath = join9(dir, "package.json");
51791
52132
  if (existsSync3(packageJsonPath)) {
51792
52133
  const pkg = JSON.parse(readFileSync5(packageJsonPath, "utf8"));
@@ -51794,7 +52135,7 @@ function resolveChatroomCliRoot2(moduleRef = import.meta.url) {
51794
52135
  return dir;
51795
52136
  }
51796
52137
  }
51797
- dir = dirname3(dir);
52138
+ dir = dirname4(dir);
51798
52139
  }
51799
52140
  throw new ClaudeSdkPackageError(`Could not locate chatroom-cli package root while resolving ${CLAUDE_AGENT_SDK_PKG}. ${REINSTALL_HINT2}`);
51800
52141
  }
@@ -51809,7 +52150,7 @@ function readPinnedSdkVersion2(chatroomCliRoot) {
51809
52150
  return match17[1];
51810
52151
  }
51811
52152
  function readInstalledSdkVersion2(entryPath) {
51812
- const packageJsonPath = join9(dirname3(entryPath), "package.json");
52153
+ const packageJsonPath = join9(dirname4(entryPath), "package.json");
51813
52154
  const pkg = JSON.parse(readFileSync5(packageJsonPath, "utf8"));
51814
52155
  return pkg.version;
51815
52156
  }
@@ -51835,7 +52176,7 @@ function resolveNativeCliBinaryPath(sdkEntryPath) {
51835
52176
  const binaryName = process.platform === "win32" ? "claude.exe" : "claude";
51836
52177
  let pkgDir;
51837
52178
  try {
51838
- pkgDir = dirname3(require3.resolve(`${platformPkg}/package.json`));
52179
+ pkgDir = dirname4(require3.resolve(`${platformPkg}/package.json`));
51839
52180
  } catch {
51840
52181
  throw new ClaudeSdkPackageError(`Native CLI binary package ${platformPkg} is not installed. ${REINSTALL_HINT2}`);
51841
52182
  }
@@ -52771,12 +53112,12 @@ var init_codex_sdk_model_variants = __esm(() => {
52771
53112
  // src/daemon/infrastructure/local/harness/services/codex-sdk/codex-sdk-package.ts
52772
53113
  import { existsSync as existsSync4, readFileSync as readFileSync6, realpathSync, statSync } from "node:fs";
52773
53114
  import { createRequire as createRequire5 } from "node:module";
52774
- import { dirname as dirname4, join as join10 } from "node:path";
53115
+ import { dirname as dirname5, join as join10 } from "node:path";
52775
53116
  import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL3 } from "node:url";
52776
53117
  function resolveChatroomCliRoot3(moduleRef = import.meta.url) {
52777
53118
  const filePath = moduleRef.startsWith("file:") ? fileURLToPath4(moduleRef) : moduleRef;
52778
- let dir = dirname4(filePath);
52779
- while (dir !== dirname4(dir)) {
53119
+ let dir = dirname5(filePath);
53120
+ while (dir !== dirname5(dir)) {
52780
53121
  const packageJsonPath = join10(dir, "package.json");
52781
53122
  if (existsSync4(packageJsonPath)) {
52782
53123
  const pkg = JSON.parse(readFileSync6(packageJsonPath, "utf8"));
@@ -52784,7 +53125,7 @@ function resolveChatroomCliRoot3(moduleRef = import.meta.url) {
52784
53125
  return dir;
52785
53126
  }
52786
53127
  }
52787
- dir = dirname4(dir);
53128
+ dir = dirname5(dir);
52788
53129
  }
52789
53130
  throw new CodexSdkPackageError(`Could not locate chatroom-cli package root while resolving @openai/codex-sdk. ${REINSTALL_HINT3}`);
52790
53131
  }
@@ -52851,7 +53192,7 @@ function addPlatformPackageJsonCandidate(candidates, candidate) {
52851
53192
  }
52852
53193
  function resolvePlatformPackageJsonCandidates(platformPkg, chatroomCliRoot, codexPackageJson, chatroomRequire, codexRequire) {
52853
53194
  const candidates = [];
52854
- const codexPackageDir = realpathSync(dirname4(codexPackageJson));
53195
+ const codexPackageDir = realpathSync(dirname5(codexPackageJson));
52855
53196
  const platformPkgBaseName = platformPkg.includes("/") ? platformPkg.slice(platformPkg.indexOf("/") + 1) : platformPkg;
52856
53197
  try {
52857
53198
  addPlatformPackageJsonCandidate(candidates, codexRequire.resolve(`${platformPkg}/package.json`));
@@ -52895,7 +53236,7 @@ function resolveCodexExecutablePath(moduleRef = import.meta.url) {
52895
53236
  throw new CodexSdkPackageError(`Native Codex CLI package ${platformPkg} is not installed. Ensure ${CODEX_NPM_NAME} is installed with optional dependencies. ${REINSTALL_HINT3}`);
52896
53237
  }
52897
53238
  for (const platformPackageJson of platformPackageJsonCandidates) {
52898
- const vendorRoot = join10(dirname4(platformPackageJson), "vendor");
53239
+ const vendorRoot = join10(dirname5(platformPackageJson), "vendor");
52899
53240
  const binaryPath = resolveNativeCodexBinary(vendorRoot, targetTriple, codexBinaryName);
52900
53241
  if (binaryPath) {
52901
53242
  return binaryPath;
@@ -52911,7 +53252,7 @@ async function importBundledCodexSdk(moduleRef = import.meta.url) {
52911
53252
  if (installedVersion !== pinnedVersion) {
52912
53253
  throw new CodexSdkPackageError(`@openai/codex-sdk@${installedVersion} does not match chatroom-cli pin (${pinnedVersion}). ${REINSTALL_HINT3}`);
52913
53254
  }
52914
- const distEntryPath = join10(dirname4(packageJsonPath), "dist", "index.js");
53255
+ const distEntryPath = join10(dirname5(packageJsonPath), "dist", "index.js");
52915
53256
  if (!existsSync4(distEntryPath)) {
52916
53257
  throw new CodexSdkPackageError(`@openai/codex-sdk entry file is missing: ${distEntryPath}. ${REINSTALL_HINT3}`);
52917
53258
  }
@@ -55022,9 +55363,9 @@ var init_session_event_forwarder = __esm(() => {
55022
55363
  });
55023
55364
 
55024
55365
  // 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";
55366
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync2 } from "node:fs";
55026
55367
  import { homedir as homedir3 } from "node:os";
55027
- import { dirname as dirname5, join as join11 } from "node:path";
55368
+ import { dirname as dirname6, join as join11 } from "node:path";
55028
55369
 
55029
55370
  class FileSessionMetadataStore {
55030
55371
  filePath;
@@ -55041,9 +55382,9 @@ class FileSessionMetadataStore {
55041
55382
  }
55042
55383
  save(sessions) {
55043
55384
  try {
55044
- const dir = dirname5(this.filePath);
55385
+ const dir = dirname6(this.filePath);
55045
55386
  if (!existsSync5(dir)) {
55046
- mkdirSync2(dir, { recursive: true });
55387
+ mkdirSync3(dir, { recursive: true });
55047
55388
  }
55048
55389
  writeFileSync2(this.filePath, JSON.stringify(sessions, null, 2));
55049
55390
  } catch {}
@@ -55591,12 +55932,12 @@ var init_opencode_sdk = __esm(() => {
55591
55932
 
55592
55933
  // src/daemon/infrastructure/local/harness/services/pi-sdk/pi-sdk-package.ts
55593
55934
  import { existsSync as existsSync6, readFileSync as readFileSync8 } from "node:fs";
55594
- import { dirname as dirname6, join as join12 } from "node:path";
55935
+ import { dirname as dirname7, join as join12 } from "node:path";
55595
55936
  import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL4 } from "node:url";
55596
55937
  function resolveChatroomCliRoot4(moduleRef = import.meta.url) {
55597
55938
  const filePath = moduleRef.startsWith("file:") ? fileURLToPath5(moduleRef) : moduleRef;
55598
- let dir = dirname6(filePath);
55599
- while (dir !== dirname6(dir)) {
55939
+ let dir = dirname7(filePath);
55940
+ while (dir !== dirname7(dir)) {
55600
55941
  const packageJsonPath = join12(dir, "package.json");
55601
55942
  if (existsSync6(packageJsonPath)) {
55602
55943
  const pkg = JSON.parse(readFileSync8(packageJsonPath, "utf8"));
@@ -55604,7 +55945,7 @@ function resolveChatroomCliRoot4(moduleRef = import.meta.url) {
55604
55945
  return dir;
55605
55946
  }
55606
55947
  }
55607
- dir = dirname6(dir);
55948
+ dir = dirname7(dir);
55608
55949
  }
55609
55950
  throw new PiSdkPackageError(`Could not locate chatroom-cli package root while resolving ${PI_CODING_AGENT_PKG}. ${REINSTALL_HINT4}`);
55610
55951
  }
@@ -55619,18 +55960,18 @@ function readPinnedSdkVersion4(chatroomCliRoot) {
55619
55960
  return match17[1];
55620
55961
  }
55621
55962
  function readInstalledSdkVersion4(entryPath) {
55622
- const packageJsonPath = join12(dirname6(entryPath), "..", "package.json");
55963
+ const packageJsonPath = join12(dirname7(entryPath), "..", "package.json");
55623
55964
  const pkg = JSON.parse(readFileSync8(packageJsonPath, "utf8"));
55624
55965
  return pkg.version;
55625
55966
  }
55626
55967
  function resolvePiSdkEntryPathFromNodeModules(chatroomCliRoot) {
55627
55968
  let dir = chatroomCliRoot;
55628
- while (dir !== dirname6(dir)) {
55969
+ while (dir !== dirname7(dir)) {
55629
55970
  const entryPath = join12(dir, "node_modules", PI_CODING_AGENT_PKG, "dist", "index.js");
55630
55971
  if (existsSync6(entryPath)) {
55631
55972
  return entryPath;
55632
55973
  }
55633
- dir = dirname6(dir);
55974
+ dir = dirname7(dir);
55634
55975
  }
55635
55976
  return;
55636
55977
  }
@@ -63776,15 +64117,35 @@ async function requestLocalDaemon(port, event, payload) {
63776
64117
  socket.connect();
63777
64118
  });
63778
64119
  const response = await socket.timeout(1e4).emitWithAck(event, payload);
63779
- if (!response.ok)
63780
- throw new Error(response.error?.message ?? `Daemon request failed: ${event}`);
64120
+ if (!response.ok) {
64121
+ throw toLocalDaemonServerError(response, event);
64122
+ }
63781
64123
  return response.data;
63782
64124
  } finally {
63783
64125
  socket.disconnect();
63784
64126
  }
63785
64127
  }
64128
+ function toLocalDaemonServerError(response, event) {
64129
+ const error51 = response.error ?? {};
64130
+ return new LocalDaemonServerError({
64131
+ message: error51.message ?? `Daemon request failed: ${event}`,
64132
+ code: error51.code,
64133
+ details: error51.details
64134
+ });
64135
+ }
64136
+ var LocalDaemonServerError;
63786
64137
  var init_local_daemon = __esm(() => {
63787
64138
  init_esm_debug3();
64139
+ LocalDaemonServerError = class LocalDaemonServerError extends Error {
64140
+ code;
64141
+ details;
64142
+ constructor(error51) {
64143
+ super(error51.message ?? "Daemon request failed");
64144
+ this.name = "LocalDaemonServerError";
64145
+ this.code = error51.code;
64146
+ this.details = error51.details;
64147
+ }
64148
+ };
63788
64149
  });
63789
64150
 
63790
64151
  // src/daemon/entry/resolve-local-web-port.ts
@@ -63996,12 +64357,12 @@ var init_log_schema = __esm(() => {
63996
64357
  });
63997
64358
 
63998
64359
  // 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";
64360
+ import { mkdirSync as mkdirSync4 } from "node:fs";
64361
+ import { dirname as dirname8 } from "node:path";
64362
+ import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
64002
64363
  function openLogDatabase(dbPath) {
64003
- mkdirSync3(dirname7(dbPath), { recursive: true });
64004
- const db = new DatabaseSync(dbPath);
64364
+ mkdirSync4(dirname8(dbPath), { recursive: true });
64365
+ const db = new DatabaseSync2(dbPath);
64005
64366
  db.exec("PRAGMA journal_mode = WAL");
64006
64367
  db.exec("PRAGMA busy_timeout = 5000");
64007
64368
  db.exec("PRAGMA synchronous = NORMAL");
@@ -64190,7 +64551,7 @@ var init_daemon_process_scan = __esm(() => {
64190
64551
 
64191
64552
  // src/commands/machine/pid.ts
64192
64553
  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";
64554
+ import { existsSync as existsSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, mkdirSync as mkdirSync5 } from "node:fs";
64194
64555
  import { homedir as homedir7 } from "node:os";
64195
64556
  import { join as join16 } from "node:path";
64196
64557
  function getUrlHash2() {
@@ -64202,7 +64563,7 @@ function getPidFileName() {
64202
64563
  }
64203
64564
  function ensureChatroomDir2() {
64204
64565
  if (!existsSync8(CHATROOM_DIR3)) {
64205
- mkdirSync4(CHATROOM_DIR3, { recursive: true, mode: 448 });
64566
+ mkdirSync5(CHATROOM_DIR3, { recursive: true, mode: 448 });
64206
64567
  }
64207
64568
  }
64208
64569
  function getPidFilePath() {
@@ -77212,11 +77573,11 @@ var init_pdfium_renderer = __esm(() => {
77212
77573
 
77213
77574
  // ../../node_modules/.pnpm/@llamaindex+liteparse@1.5.3/node_modules/@llamaindex/liteparse/dist/src/engines/pdf/pdfjsImporter.js
77214
77575
  import { fileURLToPath as fileURLToPath6 } from "node:url";
77215
- import { dirname as dirname8 } from "node:path";
77576
+ import { dirname as dirname9 } from "node:path";
77216
77577
  async function importPdfJs() {
77217
77578
  const pdfUrl = new URL("../../vendor/pdfjs/pdf.mjs", import.meta.url);
77218
77579
  const pdfjs = await import(pdfUrl.href);
77219
- const dirPath = dirname8(fileURLToPath6(pdfUrl));
77580
+ const dirPath = dirname9(fileURLToPath6(pdfUrl));
77220
77581
  return {
77221
77582
  fn: pdfjs.getDocument,
77222
77583
  dir: dirPath
@@ -98865,9 +99226,9 @@ function applyMarkupTags(markup, text) {
98865
99226
  }
98866
99227
 
98867
99228
  // ../../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";
99229
+ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync4 } from "fs";
98869
99230
  import { mkdir as mkdir4, writeFile as writeFile5 } from "fs/promises";
98870
- import { dirname as dirname9 } from "path";
99231
+ import { dirname as dirname10 } from "path";
98871
99232
 
98872
99233
  class GridDebugLogger {
98873
99234
  config;
@@ -99194,14 +99555,14 @@ class GridDebugLogger {
99194
99555
  flushSync() {
99195
99556
  if (!this.config.outputPath || this.entries.length === 0)
99196
99557
  return;
99197
- mkdirSync5(dirname9(this.config.outputPath), { recursive: true });
99558
+ mkdirSync6(dirname10(this.config.outputPath), { recursive: true });
99198
99559
  writeFileSync4(this.config.outputPath, this.formatEntries(), "utf-8");
99199
99560
  this.entries = [];
99200
99561
  }
99201
99562
  async flush() {
99202
99563
  if (!this.config.outputPath || this.entries.length === 0)
99203
99564
  return;
99204
- await mkdir4(dirname9(this.config.outputPath), { recursive: true });
99565
+ await mkdir4(dirname10(this.config.outputPath), { recursive: true });
99205
99566
  await writeFile5(this.config.outputPath, this.formatEntries(), "utf-8");
99206
99567
  this.entries = [];
99207
99568
  }
@@ -99252,7 +99613,7 @@ var init_gridDebugLogger = __esm(() => {
99252
99613
  });
99253
99614
 
99254
99615
  // ../../node_modules/.pnpm/@llamaindex+liteparse@1.5.3/node_modules/@llamaindex/liteparse/dist/src/processing/gridVisualizer.js
99255
- import { mkdirSync as mkdirSync6 } from "fs";
99616
+ import { mkdirSync as mkdirSync7 } from "fs";
99256
99617
  function escapeXml(s) {
99257
99618
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
99258
99619
  }
@@ -99362,7 +99723,7 @@ async function renderGridVisualization(data, outputPath) {
99362
99723
  }).composite([{ input: svgBuffer, top: 0, left: 0 }]).png({ compressionLevel: 6 }).toFile(outputPath);
99363
99724
  }
99364
99725
  async function renderAllVisualizations(pages, outputDir) {
99365
- mkdirSync6(outputDir, { recursive: true });
99726
+ mkdirSync7(outputDir, { recursive: true });
99366
99727
  const paths = [];
99367
99728
  for (const page of pages) {
99368
99729
  const filePath = `${outputDir}/page-${page.pageNum}-grid.png`;
@@ -106625,15 +106986,15 @@ var init_team_presets = __esm(() => {
106625
106986
  duo: {
106626
106987
  structureId: TEAM_STRUCTURE_IDS.duo,
106627
106988
  name: "Duo",
106628
- description: "A planner and builder working as a pair, planner as coordinator, with optional ephemeral enhancer",
106629
- roles: ["planner", "enhancer", "builder"],
106989
+ description: "A planner and builder working as a pair, planner as coordinator, with optional ephemeral architect and optional ephemeral uiux-engineer roles",
106990
+ roles: ["planner", "architect", "uiux-engineer", "builder"],
106630
106991
  entryPoint: "planner"
106631
106992
  },
106632
106993
  solo: {
106633
106994
  structureId: TEAM_STRUCTURE_IDS.solo,
106634
106995
  name: "Solo",
106635
- description: "A single agent working independently, with optional ephemeral enhancer",
106636
- roles: ["solo", "enhancer"],
106996
+ description: "A single agent working independently, with optional ephemeral architect and optional ephemeral uiux-engineer roles",
106997
+ roles: ["solo", "architect", "uiux-engineer"],
106637
106998
  entryPoint: "solo"
106638
106999
  }
106639
107000
  };
@@ -106829,6 +107190,22 @@ var init_agent2 = __esm(() => {
106829
107190
  init_storage2();
106830
107191
  });
106831
107192
 
107193
+ // ../../services/backend/prompts/cli/handoff/command.ts
107194
+ function handoffCommand(params) {
107195
+ const prefix = params.cliEnvPrefix || "";
107196
+ const chatroomId = params.chatroomId || "<chatroom-id>";
107197
+ const role = params.role || "<role>";
107198
+ const nextRole = params.nextRole || "<target>";
107199
+ const placeholder = params.messagePlaceholder ?? "[Your message here]";
107200
+ const commandPrefix = `${prefix}chatroom handoff --chatroom-id="${chatroomId}" --role="${role}" --next-role="${nextRole}"`;
107201
+ return formatStdinHeredocCommand(commandPrefix, HANDOFF_STDIN_DELIMITER, placeholder, {
107202
+ messageMarker: HANDOFF_MESSAGE_MARKER
107203
+ });
107204
+ }
107205
+ var init_command2 = __esm(() => {
107206
+ init_stdin_heredoc();
107207
+ });
107208
+
106832
107209
  // ../../services/backend/prompts/native/handoff-visibility.ts
106833
107210
  function recipientLabel(toRole) {
106834
107211
  return toRole.toLowerCase() === "user" ? "The user" : `The \`${toRole}\` agent`;
@@ -106852,6 +107229,285 @@ Put your **complete** deliverable in the handoff message — not in session text
106852
107229
  ---`;
106853
107230
  }
106854
107231
 
107232
+ // ../../services/backend/prompts/utils/file-reference-guidance.ts
107233
+ function getFileReferenceGuidanceComment() {
107234
+ return `<!-- File references (clickable in workspace UI): use repo-relative paths with a file extension — e.g. \`apps/webapp/src/modules/chatroom/foo.ts\` or [apps/webapp/src/foo.ts](apps/webapp/src/foo.ts). Avoid absolute paths, file:// prefixes, and paths without / or extension. -->`;
107235
+ }
107236
+ function getFileReferenceProofOfCompletionExample() {
107237
+ return `${getFileReferenceGuidanceComment()}
107238
+ - \`apps/webapp/src/path/to/file.ts\` — <what changed and why>`;
107239
+ }
107240
+
107241
+ // ../../services/backend/prompts/teams/architect-handoff-templates.ts
107242
+ function normalizeEntryPointRole(entryPointRole) {
107243
+ return entryPointRole.trim().toLowerCase() || "planner";
107244
+ }
107245
+ function getArchitectFocus() {
107246
+ return "module boundaries, APIs, schemas, indexes and queries, invariants, failure and recovery handling, tests, and implementation sequence";
107247
+ }
107248
+ function getArchitectRequirements() {
107249
+ return `
107250
+ - Cite repository evidence for domain model/entity ownership, lifecycle, invariants, identifiers, and relationships.
107251
+ - Define service and module boundaries, public and internal APIs, function signatures, and ownership of side effects.
107252
+ - Analyze concurrency and races: concurrent writers, idempotency, ordering and monotonicity, retries, transactions, and failure/recovery behavior.
107253
+ - Design read patterns first: primary read paths, indexes, projections/read models, query shapes, scan and transaction limits, and invalidation/subscription scope.
107254
+ - Account for Convex reactivity and bandwidth: query invalidation patterns, avoiding unnecessary reactive fanout, and separating high-frequency heartbeat/liveness/status fields from low-frequency identity/configuration fields.
107255
+ - Specify concrete schema, query, and mutation changes; migration/backfill sequence; tests; and verification order.`;
107256
+ }
107257
+ function getEntryPointToArchitectHandoffTemplate(entryPointRole) {
107258
+ const entryPoint = normalizeEntryPointRole(entryPointRole);
107259
+ return `${getHandoffRecipientVisibilityCallout("architect")}
107260
+
107261
+ ## Handoff Template (${entryPoint} → architect)
107262
+
107263
+ The automatically injected \`<user-message>\` is the authoritative request. Add only concise context that the Architect needs to design within the existing repository and team scope.
107264
+
107265
+ Provide the request, relevant constraints, and the files or product surfaces that need inspection. Ask for one evidence-backed design focused on ${getArchitectFocus()}.
107266
+
107267
+ After completing the design, hand it back to \`${entryPoint}\` with the normal handoff command. Stop after the successful handoff; do not implement the design or delegate further work.
107268
+
107269
+ \`\`\`bash
107270
+ ${handoffCommand({
107271
+ chatroomId: "<chatroom-id>",
107272
+ role: "architect",
107273
+ nextRole: entryPoint,
107274
+ cliEnvPrefix: ""
107275
+ })}
107276
+ \`\`\``;
107277
+ }
107278
+ function getArchitectToEntryPointHandoffTemplate(entryPointRole) {
107279
+ return renderArchitectToEntryPointHandoffTemplate(normalizeEntryPointRole(entryPointRole));
107280
+ }
107281
+ function getGenericArchitectToEntryPointHandoffTemplate() {
107282
+ return renderArchitectToEntryPointHandoffTemplate("<entry-point-role>", true);
107283
+ }
107284
+ function renderArchitectToEntryPointHandoffTemplate(entryPoint, generic = false) {
107285
+ const sharedContractsHeading = !generic && entryPoint === "planner" ? "## Shared contracts (planner-owned)" : "## Shared contracts (configured entry-point-owned)";
107286
+ const placeholderInstruction = generic ? " Replace `<entry-point-role>` with the configured entry-point role before running it." : "";
107287
+ return `${getHandoffRecipientVisibilityCallout(entryPoint)}
107288
+
107289
+ ## Handoff Template (Architect → Entry Point)
107290
+
107291
+ Return exactly one recommended, evidence-backed design brief for the authoritative user request to the configured entry point. This is an advisory handback: do not implement code, edit files, spawn subagents, present alternatives, or expand scope. The injected \`<handoff-templates>\` block is the authoritative structure for this handoff; complete every section in order. Use \`Not Applicable.\` only when a section is genuinely inapplicable, with no explanation or filler.
107292
+
107293
+ ## Summary
107294
+ <describe the design problem being solved, the repository/product surface where it fits, and the evidence-backed context; do not refer to hidden session text>
107295
+
107296
+ ## Goal
107297
+ <state the one-sentence outcome this design delivers>
107298
+
107299
+ ## Key Knowledge for High Quality Bar
107300
+ <cite the inspected repository patterns and complete the architect checklist below; do not provide a high-level essay>
107301
+ - Produce exactly one recommended design for ${getArchitectFocus()}.
107302
+ - Name concrete repository paths, existing abstractions, APIs/types, state/data flow, acceptance criteria, risks, and verification steps.
107303
+ ${getArchitectRequirements()}
107304
+
107305
+ ## Force Multipliers
107306
+ <name the existing abstractions, patterns, utilities, and platform conventions to reuse, and state why each reduces implementation risk or duplication>
107307
+ - Keep the design within the repository's established source-of-truth boundaries and avoid introducing a new registry or abstraction without evidence.
107308
+
107309
+ ## Files to implement (exhaustive, file-level)
107310
+ List every file or product surface the implementer must create or modify. Mark each \`(Required)\` or \`(Optional)\`. For every file, give the exact responsibility, exported types/signatures/query shape, state/data/event boundaries, and target code detailed enough to implement without guessing.
107311
+ ${getFileReferenceGuidanceComment()}
107312
+
107313
+ ### \`<repo-relative/path.ext>\` (Required)
107314
+ **Change:** <precise add/modify/remove responsibility and why>
107315
+
107316
+ \`\`\`text
107317
+ // Target code, API/type signature, query shape, or migration/backfill step.
107318
+ // Enough detail that implementation requires no invention.
107319
+ \`\`\`
107320
+
107321
+ <add one block per file; do not collapse multiple files into a directory or vague layer>
107322
+
107323
+ ${sharedContractsHeading}
107324
+ <list cross-file interfaces, schemas, API contracts, state/data boundaries, or write exactly \`Not Applicable.\` when genuinely inapplicable>
107325
+
107326
+ ### Interfaces & types
107327
+ \`\`\`typescript
107328
+ // Shared signatures, schemas, query/mutation shapes, or state contracts.
107329
+ \`\`\`
107330
+
107331
+ ### Reference snippets
107332
+ \`\`\`typescript
107333
+ // Canonical imports, call patterns, or data-flow examples.
107334
+ \`\`\`
107335
+
107336
+ ## Requirements (acceptance criteria)
107337
+ - <one concrete, verifiable requirement per bullet; include exact data shape, API contract, migration result, and failure behavior>
107338
+ - Include repository test coverage for domain, concurrency, read-pattern, migration, and recovery risk areas.
107339
+ - Verify the implemented user-facing entry point or task-delivery path end-to-end; unit tests alone are insufficient.
107340
+
107341
+ ## What to avoid
107342
+ - Do not implement code or edit files as the architect; this handoff is advisory only.
107343
+ - Do not present alternative designs, generic advice, or requirements without concrete evidence and file-level consequences.
107344
+ - Do not invent APIs, schema fields, query shapes, indexes, or source-of-truth locations when existing repository patterns apply.
107345
+ - <explicit task-specific anti-patterns, scope boundaries, and migration/source-of-truth pitfalls>
107346
+
107347
+ ## Skills to activate
107348
+ - <relevant repository skills and why they apply, or write exactly \`Not Applicable.\` when none apply>
107349
+
107350
+ ## Out of scope
107351
+ - <explicit files, flows, behavior, or migration work the implementer must not touch>
107352
+
107353
+ ## Handoff
107354
+ Run the normal handoff command to the configured entry point with this complete design, then stop.${placeholderInstruction}
107355
+
107356
+ \`\`\`bash
107357
+ ${handoffCommand({
107358
+ chatroomId: "<chatroom-id>",
107359
+ role: "architect",
107360
+ nextRole: entryPoint,
107361
+ cliEnvPrefix: ""
107362
+ })}
107363
+ \`\`\``;
107364
+ }
107365
+ var init_architect_handoff_templates = __esm(() => {
107366
+ init_command2();
107367
+ });
107368
+
107369
+ // ../../services/backend/prompts/teams/uiux-engineer-handoff-templates.ts
107370
+ function normalizeEntryPointRole2(entryPointRole) {
107371
+ return entryPointRole.trim().toLowerCase() || "planner";
107372
+ }
107373
+ function getUiuxEngineerFocus() {
107374
+ return "complete user flows, loading/empty/error/success states, component ownership, theme tokens and layout, keyboard and accessibility behavior, responsive details, and UI tests";
107375
+ }
107376
+ function getUiuxEngineerRequirements() {
107377
+ return `
107378
+ - Specify complete user flows with explicit loading, empty, error, success, disabled, and retry states where applicable.
107379
+ - Define exact keyboard shortcuts, focus order, keyboard navigation, focus restoration, and accessible semantics and labels.
107380
+ - Define component hierarchy and ownership, props/state/events, data boundaries, and interaction behavior.
107381
+ - Choose concrete existing ShadCN components with the Base UI backend and lucide-react/react-icons conventions; do not write "use an appropriate component."
107382
+ - Specify Tailwind utility classes or semantic theme-token classes for layout, spacing, sizing, typography, states, responsive behavior, and dark mode.
107383
+ - Specify responsive breakpoints/layout behavior, visual feedback, destructive-action safeguards, exact files/components, and the UI/integration/accessibility test plan.`;
107384
+ }
107385
+ function getEntryPointToUiuxEngineerHandoffTemplate(entryPointRole) {
107386
+ const entryPoint = normalizeEntryPointRole2(entryPointRole);
107387
+ return `${getHandoffRecipientVisibilityCallout("uiux-engineer")}
107388
+
107389
+ ## Handoff Template (${entryPoint} → uiux-engineer)
107390
+
107391
+ The automatically injected \`<user-message>\` is the authoritative request. Add only concise context that the UI/UX Engineer needs to design within the existing repository and team scope.
107392
+
107393
+ Provide the request, relevant constraints, and the files or product surfaces that need inspection. Ask for one evidence-backed design focused on ${getUiuxEngineerFocus()}.
107394
+
107395
+ After completing the design, hand it back to \`${entryPoint}\` with the normal handoff command. Stop after the successful handoff; do not implement the design or delegate further work.
107396
+
107397
+ \`\`\`bash
107398
+ ${handoffCommand({
107399
+ chatroomId: "<chatroom-id>",
107400
+ role: "uiux-engineer",
107401
+ nextRole: entryPoint,
107402
+ cliEnvPrefix: ""
107403
+ })}
107404
+ \`\`\``;
107405
+ }
107406
+ function getUiuxEngineerToEntryPointHandoffTemplate(entryPointRole) {
107407
+ return renderUiuxEngineerToEntryPointHandoffTemplate(normalizeEntryPointRole2(entryPointRole));
107408
+ }
107409
+ function getGenericUiuxEngineerToEntryPointHandoffTemplate() {
107410
+ return renderUiuxEngineerToEntryPointHandoffTemplate("<entry-point-role>", true);
107411
+ }
107412
+ function renderUiuxEngineerToEntryPointHandoffTemplate(entryPoint, generic = false) {
107413
+ const sharedContractsHeading = !generic && entryPoint === "planner" ? "## Shared contracts (planner-owned)" : "## Shared contracts (configured entry-point-owned)";
107414
+ const placeholderInstruction = generic ? " Replace `<entry-point-role>` with the configured entry-point role before running it." : "";
107415
+ return `${getHandoffRecipientVisibilityCallout(entryPoint)}
107416
+
107417
+ ## Handoff Template (UI/UX Engineer → Entry Point)
107418
+
107419
+ Return exactly one recommended, evidence-backed design brief for the authoritative user request to the configured entry point. This is an advisory handback: do not implement code, edit files, spawn subagents, present alternatives, or expand scope. The injected \`<handoff-templates>\` block is the authoritative structure for this handoff; complete every section in order. Use \`Not Applicable.\` only when a section is genuinely inapplicable, with no explanation or filler.
107420
+
107421
+ ## Summary
107422
+ <describe the design problem being solved, the repository/product surface where it fits, and the evidence-backed context; do not refer to hidden session text>
107423
+
107424
+ ## Goal
107425
+ <state the one-sentence outcome this design delivers>
107426
+
107427
+ ## Key Knowledge for High Quality Bar
107428
+ <cite the inspected repository patterns and complete the UI/UX engineer checklist below; do not provide a high-level essay>
107429
+ - Produce exactly one recommended design for ${getUiuxEngineerFocus()}.
107430
+ - Name concrete repository paths, existing components/tokens, props/state/events, data boundaries, acceptance criteria, risks, and verification steps.
107431
+ ${getUiuxEngineerRequirements()}
107432
+
107433
+ ## Force Multipliers
107434
+ <name the existing components, patterns, tokens, utilities, and platform conventions to reuse, and state why each reduces implementation risk or duplication>
107435
+ - Keep the design within the repository's established source-of-truth boundaries and avoid introducing a new registry or abstraction without evidence.
107436
+
107437
+ ## Files to implement (exhaustive, file-level)
107438
+ List every file or product surface the implementer must create or modify. Mark each \`(Required)\` or \`(Optional)\`. For every file, give the exact responsibility, exported types/signatures/props, state/data/event boundaries, component hierarchy, and target Tailwind/theme-token snippet detailed enough to implement without guessing.
107439
+ ${getFileReferenceGuidanceComment()}
107440
+
107441
+ ### \`<repo-relative/path.ext>\` (Required)
107442
+ **Change:** <precise add/modify/remove responsibility and why>
107443
+
107444
+ \`\`\`text
107445
+ // Component hierarchy, props/state/events, test structure, or exact
107446
+ // Tailwind/theme-token classes. Enough detail that implementation requires no invention.
107447
+ \`\`\`
107448
+
107449
+ <add one block per file; do not collapse multiple files into a directory or vague layer>
107450
+
107451
+ ${sharedContractsHeading}
107452
+ <list cross-file interfaces, component/data contracts, state boundaries, or write exactly \`Not Applicable.\` when genuinely inapplicable>
107453
+
107454
+ ### Interfaces & types
107455
+ \`\`\`typescript
107456
+ // Shared props, state, events, query shapes, or accessibility contracts.
107457
+ \`\`\`
107458
+
107459
+ ### Reference snippets
107460
+ \`\`\`tsx
107461
+ // Canonical component imports, hook usage, keyboard wiring, or layout examples.
107462
+ \`\`\`
107463
+
107464
+ ## Requirements (acceptance criteria)
107465
+ - <one concrete, verifiable requirement per bullet; include exact states, interactions, semantics, classes/tokens, and responsive behavior>
107466
+ - Include UI, integration, and accessibility test coverage for the design's risk areas.
107467
+ - Verify the implemented user-facing entry point end-to-end; unit tests alone are insufficient.
107468
+
107469
+ ## What to avoid
107470
+ - Do not implement code or edit files as the UI/UX engineer; this handoff is advisory only.
107471
+ - Do not present alternative designs, generic advice, or requirements without concrete evidence and file-level consequences.
107472
+ - Do not invent components, props, tokens, keyboard behavior, or source-of-truth locations when existing repository patterns apply.
107473
+ - <explicit task-specific anti-patterns, scope boundaries, and destructive-action/source-of-truth pitfalls>
107474
+
107475
+ ## Skills to activate
107476
+ - <relevant repository skills and why they apply, or write exactly \`Not Applicable.\` when none apply>
107477
+
107478
+ ## Out of scope
107479
+ - <explicit files, flows, behavior, or migration work the implementer must not touch>
107480
+
107481
+ ## Handoff
107482
+ Run the normal handoff command to the configured entry point with this complete design, then stop.${placeholderInstruction}
107483
+
107484
+ \`\`\`bash
107485
+ ${handoffCommand({
107486
+ chatroomId: "<chatroom-id>",
107487
+ role: "uiux-engineer",
107488
+ nextRole: entryPoint,
107489
+ cliEnvPrefix: ""
107490
+ })}
107491
+ \`\`\``;
107492
+ }
107493
+ var init_uiux_engineer_handoff_templates = __esm(() => {
107494
+ init_command2();
107495
+ });
107496
+
107497
+ // ../../services/backend/prompts/teams/duo/handoff-templates/architect.ts
107498
+ var duoArchitectHandoffContract;
107499
+ var init_architect = __esm(() => {
107500
+ init_architect_handoff_templates();
107501
+ duoArchitectHandoffContract = {
107502
+ role: "architect",
107503
+ receivesFrom: ["planner"],
107504
+ returnsTo: ["planner"],
107505
+ outboundTemplates: {
107506
+ planner: () => getArchitectToEntryPointHandoffTemplate("planner")
107507
+ }
107508
+ };
107509
+ });
107510
+
106855
107511
  // ../../services/backend/prompts/utils/code-change-verification.ts
106856
107512
  var CODE_CHANGE_VERIFICATION_CONFIRMATION = "- [ ] I confirm that I have run typecheck and tests for the project (only required if code changes were made)";
106857
107513
 
@@ -106865,15 +107521,6 @@ ${getDelegationBriefDisclosureComment()}`;
106865
107521
  }
106866
107522
  var DELEGATION_BRIEF_DISCLOSURE_CHECKBOX = "- [ ] I confirm the delegation brief is fully met: all (Required) files done, verified end-to-end, acceptance criteria pass";
106867
107523
 
106868
- // ../../services/backend/prompts/utils/file-reference-guidance.ts
106869
- function getFileReferenceGuidanceComment() {
106870
- return `<!-- File references (clickable in workspace UI): use repo-relative paths with a file extension — e.g. \`apps/webapp/src/modules/chatroom/foo.ts\` or [apps/webapp/src/foo.ts](apps/webapp/src/foo.ts). Avoid absolute paths, file:// prefixes, and paths without / or extension. -->`;
106871
- }
106872
- function getFileReferenceProofOfCompletionExample() {
106873
- return `${getFileReferenceGuidanceComment()}
106874
- - \`apps/webapp/src/path/to/file.ts\` — <what changed and why>`;
106875
- }
106876
-
106877
107524
  // ../../services/backend/prompts/utils/handoff-section-guidance.ts
106878
107525
  function getHandoffNotApplicableSectionComment(context5) {
106879
107526
  return `<!-- REQUIRED. ${context5}, or write exactly "${HANDOFF_NOT_APPLICABLE_EXACT_TEXT}" with no explanation. Do not omit this section. -->`;
@@ -107014,214 +107661,6 @@ var init_builder = __esm(() => {
107014
107661
  };
107015
107662
  });
107016
107663
 
107017
- // ../../services/backend/src/domain/usecase/enhancer/enhancer-handoff-content.ts
107018
- var ENHANCER_USER_MESSAGE_PLACEHOLDER = "{{USER_MESSAGE}}";
107019
-
107020
- // ../../services/backend/prompts/utils/data-query-design.ts
107021
- function getDataQueryDesignTemplateBlock() {
107022
- return `## Persistent state and query pattern design
107023
-
107024
- **Goal:** Small updates must not cause large cache invalidations. High-frequency writes use projections to smaller tables.
107025
-
107026
- ### 1. Sources of concern
107027
- | Source | Write frequency | Read pattern | Risk |
107028
- |--------|---------------|--------------|------|
107029
- | \`<table/mutation>\` | \`<frequency>\` | \`<pattern>\` | \`<scan / hot partition>\` |
107030
-
107031
- ### 2. Schema design
107032
- **Hot path:** \`<table>\` — fields, projection from \`<source>\`
107033
- **Cold path:** \`<table>\` — …
107034
-
107035
- \`\`\`typescript
107036
- // target schema shape
107037
- \`\`\`
107038
-
107039
- ### 3. Index design (within limits)
107040
- | Table | Index | Serves query | Budget |
107041
- |-------|-------|--------------|--------|
107042
-
107043
- ### 4. Query design (within limits)
107044
- | Query | Index | Rows scanned | Timeout | Invalidation scope |
107045
- |-------|-------|--------------|---------|-------------------|
107046
-
107047
- \`\`\`typescript
107048
- // target query signature
107049
- \`\`\`
107050
-
107051
- <!-- Write exactly "Not Applicable." for the entire section if no persistence changes -->`;
107052
- }
107053
-
107054
- // ../../services/backend/prompts/utils/frontend-design-ux-checklist.ts
107055
- function getFrontendDesignUxPlanningPrinciplesBlock() {
107056
- return `<!-- Ground every flow in user history and repository patterns. Recommend one existing pattern when multiple exist; do not prescribe style choices the project has not adopted. -->`;
107057
- }
107058
- function getFrontendDesignUxFlowChecklistBlock() {
107059
- return `**UX quality (complete for every interactive step in this flow):**
107060
- - **States:** loading | empty | error | success — no blank panels, silent failures, or missing retry affordances
107061
- - **Layout:** stable across async transitions — no layout shift when content arrives or state changes
107062
- - **Patterns:** consistent with existing project components — cite the chosen pattern and repo-relative file
107063
- - **Shortcuts:** aligned with project keyboard/shortcut conventions; document tab order and gaps
107064
- - **Feedback:** timely response for async user actions
107065
- - **Interaction affordance:** pointer cursor (or project equivalent) on clickable elements
107066
- - **Safeguards:** confirmation before destructive actions; bulk operations confirmed with scope summary`;
107067
- }
107068
-
107069
- // ../../services/backend/prompts/utils/enhancer-feedback-template-body.ts
107070
- function getEnhancerFeedbackTemplateBody() {
107071
- return `<handoff-overview>
107072
- ## Summary
107073
- <one paragraph: what we're building and the single design direction>
107074
-
107075
- ## User intent and constraints
107076
- <explicit requirements from user messages; hard constraints that must not be violated>
107077
- </handoff-overview>
107078
-
107079
- <!-- UI collapses proofs, direction, frontend design, data design, and notes by default; overview and action required are expanded -->
107080
-
107081
- <handoff-proofs>
107082
- ## Repository evidence
107083
- <files read, current behavior verified, patterns reused — cite repo-relative paths and short snippets only>
107084
-
107085
- ${getHandoffQualityPrinciplesSectionBlock("design")}
107086
- </handoff-proofs>
107087
-
107088
- <handoff-direction>
107089
- ## Recommended design
107090
- <!-- ONE design only. No Option A/B/C. -->
107091
-
107092
- <2–4 sentences: the chosen architecture and why it fits the request and existing system>
107093
- </handoff-direction>
107094
-
107095
- <handoff-frontend-design>
107096
- ## Frontend / user-centric design
107097
-
107098
- ${getFrontendDesignUxPlanningPrinciplesBlock()}
107099
-
107100
- ### Flow 1: <name>
107101
- **Entry:** User visits \`<route/page>\` from \`<source>\`.
107102
-
107103
- | Step | User action | System response | UI state |
107104
- |------|-------------|-----------------|----------|
107105
- | 1 | User clicks \`<element label>\` | \`<navigation / modal / fetch>\` | \`<loading → success/error>\` |
107106
-
107107
- ${getFrontendDesignUxFlowChecklistBlock()}
107108
-
107109
- **Expected interactions per element:**
107110
- - \`<ElementName>\` (\`apps/webapp/src/...\`): click → \`<handler>\`; keyboard: \`<tab order>\`; disabled when \`<condition>\`
107111
-
107112
- ### Element, style, and layout specification
107113
-
107114
- #### \`<ComponentName>\` — \`apps/webapp/src/path/to/Component.tsx\`
107115
- **Change:** <create | modify>
107116
-
107117
- **Layout:** \`<div className="...">\` — flex/grid, gaps, breakpoints
107118
-
107119
- **States:** loading | empty | error | success
107120
-
107121
- \`\`\`tsx
107122
- export function ComponentName({ ... }: Props) {
107123
- // target structure — props, classNames, branches
107124
- }
107125
- \`\`\`
107126
-
107127
- <!-- Repeat per component; write exactly "Not Applicable." for the entire section if no UI -->
107128
- </handoff-frontend-design>
107129
-
107130
- <handoff-data-design>
107131
- ${getDataQueryDesignTemplateBlock()}
107132
- </handoff-data-design>
107133
-
107134
- <handoff-notes>
107135
- ## Open questions for user
107136
- <decisions only the user can make, or write "Not Applicable.">
107137
- </handoff-notes>
107138
-
107139
- <handoff-action>
107140
- ## Recommended implementation sequence
107141
- <!-- Ordered slices for the planner — references files from design sections. For large or multi-surface revisions, activate the defragmentation skill before delegating. -->
107142
-
107143
- 1. …
107144
- 2. …
107145
-
107146
- ## Files touched (index)
107147
- - \`apps/webapp/src/...\` — …
107148
- - \`services/backend/convex/...\` — …
107149
- </handoff-action>`;
107150
- }
107151
- var init_enhancer_feedback_template_body = __esm(() => {
107152
- init_handoff_quality_principles();
107153
- });
107154
-
107155
- // ../../services/backend/prompts/enhancer/handoff-templates.ts
107156
- function roleLabel(role) {
107157
- return role.charAt(0).toUpperCase() + role.slice(1).toLowerCase();
107158
- }
107159
- function getEntryPointToEnhancerHandoffTemplate(entryPointRole) {
107160
- const label = roleLabel(entryPointRole);
107161
- return `**Planning Request (${label} → Enhancer)** — after reading the user message and any pinned chatroom context, fill only <additional-context> below. The system injects the user message automatically — do not copy it.
107162
-
107163
- \`\`\`markdown
107164
- <user-message>
107165
- <!-- Injected automatically from the originating user message — do not edit or copy the user message here -->
107166
- ${ENHANCER_USER_MESSAGE_PLACEHOLDER}
107167
- </user-message>
107168
-
107169
- <additional-context>
107170
- ## Goal
107171
- <what the user wants in plain language — one short paragraph>
107172
- </additional-context>
107173
- \`\`\`
107174
-
107175
- After handoff succeeds, **end your turn immediately**. The enhancer independently downloads the originating message history and returns design input as your next ${entryPointRole.toLowerCase()} task.`;
107176
- }
107177
- function getEnhancerToEntryPointHandoffTemplate(entryPointRole) {
107178
- const normalizedRole = entryPointRole.toLowerCase();
107179
- const label = roleLabel(entryPointRole);
107180
- return `**Design Input (Enhancer → ${label})**
107181
-
107182
- You are the design authority for this request. Recover conversation history, inspect the repository, and return **one** complete design — not options. The ${normalizedRole} agent verifies your design and delegates implementation.
107183
-
107184
- **Rules:**
107185
- - Design first — complete frontend and data/query sections before implementation sequencing.
107186
- - **No alternative approaches** — one \`Recommended design\` only.
107187
- - Frontend and data sections: code granularity (component names, props, classes, file paths, schema, indexes, queries).
107188
- - Complete per-flow **UX quality** checklist in frontend design — states, layout stability, patterns, safeguards.
107189
- - For large or multi-surface revisions, note in implementation sequence that the entry point should activate the \`defragmentation\` skill.
107190
- - In \`<handoff-proofs>\`, complete **Proof of Principles** for how this design satisfies each quality principle (or "Not Applicable.").
107191
- - Write "Not Applicable." only when a major design section truly does not apply.
107192
-
107193
- \`\`\`markdown
107194
- ${getEnhancerFeedbackTemplateBody()}
107195
- \`\`\`
107196
-
107197
- Return only the design input markdown — no preamble. Follow this structure; use "Not Applicable." where a major section does not apply.`;
107198
- }
107199
- var init_handoff_templates = __esm(() => {
107200
- init_enhancer_feedback_template_body();
107201
- });
107202
-
107203
- // ../../services/backend/prompts/teams/duo/handoff-templates/enhancer-to-planner.ts
107204
- function getEnhancerToPlannerHandoffTemplate() {
107205
- return getEnhancerToEntryPointHandoffTemplate("planner");
107206
- }
107207
- var init_enhancer_to_planner = __esm(() => {
107208
- init_handoff_templates();
107209
- });
107210
-
107211
- // ../../services/backend/prompts/teams/duo/handoff-templates/enhancer.ts
107212
- var duoEnhancerHandoffContract;
107213
- var init_enhancer = __esm(() => {
107214
- init_enhancer_to_planner();
107215
- duoEnhancerHandoffContract = {
107216
- role: "enhancer",
107217
- receivesFrom: ["planner"],
107218
- returnsTo: ["planner"],
107219
- outboundTemplates: {
107220
- planner: () => getEnhancerToPlannerHandoffTemplate()
107221
- }
107222
- };
107223
- });
107224
-
107225
107664
  // ../../services/backend/prompts/teams/duo/handoff-templates/planner-to-builder.ts
107226
107665
  function getPlannerToBuilderHandoffTemplate() {
107227
107666
  return `${getHandoffRecipientVisibilityCallout("builder")}
@@ -107298,14 +107737,6 @@ Keep one slice ≈ one focused review surface. Delegate slices incrementally —
107298
107737
  }
107299
107738
  var init_planner_to_builder = () => {};
107300
107739
 
107301
- // ../../services/backend/prompts/teams/duo/handoff-templates/planner-to-enhancer.ts
107302
- function getPlannerToEnhancerHandoffTemplate() {
107303
- return getEntryPointToEnhancerHandoffTemplate("planner");
107304
- }
107305
- var init_planner_to_enhancer = __esm(() => {
107306
- init_handoff_templates();
107307
- });
107308
-
107309
107740
  // ../../services/backend/prompts/utils/context-disclosure.ts
107310
107741
  function getContextReadDisclosureComment(params = {}) {
107311
107742
  const command = contextReadCommand(params);
@@ -107493,16 +107924,18 @@ var init_chat_handoff_template = () => {};
107493
107924
  var duoPlannerHandoffContract;
107494
107925
  var init_planner = __esm(() => {
107495
107926
  init_planner_to_builder();
107496
- init_planner_to_enhancer();
107497
107927
  init_planner_to_user();
107498
107928
  init_chat_handoff_template();
107929
+ init_architect_handoff_templates();
107930
+ init_uiux_engineer_handoff_templates();
107499
107931
  duoPlannerHandoffContract = {
107500
107932
  role: "planner",
107501
- receivesFrom: ["user", "builder", "enhancer"],
107502
- returnsTo: ["builder", "enhancer", "user"],
107933
+ receivesFrom: ["user", "builder", "architect", "uiux-engineer"],
107934
+ returnsTo: ["builder", "architect", "uiux-engineer", "user"],
107503
107935
  outboundTemplates: {
107504
107936
  builder: () => getPlannerToBuilderHandoffTemplate(),
107505
- enhancer: () => getPlannerToEnhancerHandoffTemplate(),
107937
+ architect: () => getEntryPointToArchitectHandoffTemplate("planner"),
107938
+ "uiux-engineer": () => getEntryPointToUiuxEngineerHandoffTemplate("planner"),
107506
107939
  user: (query) => query.conversationMode === "chat" ? getChatToUserHandoffTemplate() : getPlannerToUserReportTemplate({
107507
107940
  chatroomId: query.chatroomId,
107508
107941
  role: query.role,
@@ -107512,6 +107945,20 @@ var init_planner = __esm(() => {
107512
107945
  };
107513
107946
  });
107514
107947
 
107948
+ // ../../services/backend/prompts/teams/duo/handoff-templates/uiux-engineer.ts
107949
+ var duoUiuxEngineerHandoffContract;
107950
+ var init_uiux_engineer = __esm(() => {
107951
+ init_uiux_engineer_handoff_templates();
107952
+ duoUiuxEngineerHandoffContract = {
107953
+ role: "uiux-engineer",
107954
+ receivesFrom: ["planner"],
107955
+ returnsTo: ["planner"],
107956
+ outboundTemplates: {
107957
+ planner: () => getUiuxEngineerToEntryPointHandoffTemplate("planner")
107958
+ }
107959
+ };
107960
+ });
107961
+
107515
107962
  // ../../services/backend/prompts/cli/handoff-templates/contracts.ts
107516
107963
  function validateRoleHandoffContracts(contracts) {
107517
107964
  const byRole = new Map;
@@ -107552,14 +107999,16 @@ function getDuoHandoffTemplate(query) {
107552
107999
  return getter?.(query) ?? null;
107553
108000
  }
107554
108001
  var DUO_ROLE_HANDOFF_CONTRACTS, validatedDuoRoleHandoffContracts;
107555
- var init_handoff_templates2 = __esm(() => {
108002
+ var init_handoff_templates = __esm(() => {
108003
+ init_architect();
107556
108004
  init_builder();
107557
- init_enhancer();
107558
108005
  init_planner();
108006
+ init_uiux_engineer();
107559
108007
  DUO_ROLE_HANDOFF_CONTRACTS = [
107560
108008
  duoPlannerHandoffContract,
107561
108009
  duoBuilderHandoffContract,
107562
- duoEnhancerHandoffContract
108010
+ duoArchitectHandoffContract,
108011
+ duoUiuxEngineerHandoffContract
107563
108012
  ];
107564
108013
  validatedDuoRoleHandoffContracts = (() => {
107565
108014
  validateRoleHandoffContracts(DUO_ROLE_HANDOFF_CONTRACTS);
@@ -107567,16 +108016,16 @@ var init_handoff_templates2 = __esm(() => {
107567
108016
  })();
107568
108017
  });
107569
108018
 
107570
- // ../../services/backend/prompts/teams/solo/handoff-templates/enhancer.ts
107571
- var soloEnhancerHandoffContract;
107572
- var init_enhancer2 = __esm(() => {
107573
- init_handoff_templates();
107574
- soloEnhancerHandoffContract = {
107575
- role: "enhancer",
108019
+ // ../../services/backend/prompts/teams/solo/handoff-templates/architect.ts
108020
+ var soloArchitectHandoffContract;
108021
+ var init_architect2 = __esm(() => {
108022
+ init_architect_handoff_templates();
108023
+ soloArchitectHandoffContract = {
108024
+ role: "architect",
107576
108025
  receivesFrom: ["solo"],
107577
108026
  returnsTo: ["solo"],
107578
108027
  outboundTemplates: {
107579
- solo: () => getEnhancerToEntryPointHandoffTemplate("solo")
108028
+ solo: () => getArchitectToEntryPointHandoffTemplate("solo")
107580
108029
  }
107581
108030
  };
107582
108031
  });
@@ -107599,14 +108048,16 @@ var init_solo_to_user = __esm(() => {
107599
108048
  var soloHandoffContract;
107600
108049
  var init_solo = __esm(() => {
107601
108050
  init_solo_to_user();
107602
- init_handoff_templates();
107603
108051
  init_chat_handoff_template();
108052
+ init_architect_handoff_templates();
108053
+ init_uiux_engineer_handoff_templates();
107604
108054
  soloHandoffContract = {
107605
108055
  role: "solo",
107606
- receivesFrom: ["user", "enhancer"],
107607
- returnsTo: ["user", "enhancer"],
108056
+ receivesFrom: ["user", "architect", "uiux-engineer"],
108057
+ returnsTo: ["architect", "uiux-engineer", "user"],
107608
108058
  outboundTemplates: {
107609
- enhancer: () => getEntryPointToEnhancerHandoffTemplate("solo"),
108059
+ architect: () => getEntryPointToArchitectHandoffTemplate("solo"),
108060
+ "uiux-engineer": () => getEntryPointToUiuxEngineerHandoffTemplate("solo"),
107610
108061
  user: (query) => query.conversationMode === "chat" ? getChatToUserHandoffTemplate() : getSoloToUserReportTemplate({
107611
108062
  chatroomId: query.chatroomId,
107612
108063
  role: query.role,
@@ -107616,6 +108067,20 @@ var init_solo = __esm(() => {
107616
108067
  };
107617
108068
  });
107618
108069
 
108070
+ // ../../services/backend/prompts/teams/solo/handoff-templates/uiux-engineer.ts
108071
+ var soloUiuxEngineerHandoffContract;
108072
+ var init_uiux_engineer2 = __esm(() => {
108073
+ init_uiux_engineer_handoff_templates();
108074
+ soloUiuxEngineerHandoffContract = {
108075
+ role: "uiux-engineer",
108076
+ receivesFrom: ["solo"],
108077
+ returnsTo: ["solo"],
108078
+ outboundTemplates: {
108079
+ solo: () => getUiuxEngineerToEntryPointHandoffTemplate("solo")
108080
+ }
108081
+ };
108082
+ });
108083
+
107619
108084
  // ../../services/backend/prompts/teams/solo/handoff-templates/index.ts
107620
108085
  function listSoloRoleHandoffContracts() {
107621
108086
  return validatedSoloRoleHandoffContracts;
@@ -107628,12 +108093,14 @@ function getSoloHandoffTemplate(query) {
107628
108093
  return getter?.(query) ?? null;
107629
108094
  }
107630
108095
  var SOLO_ROLE_HANDOFF_CONTRACTS, validatedSoloRoleHandoffContracts;
107631
- var init_handoff_templates3 = __esm(() => {
107632
- init_enhancer2();
108096
+ var init_handoff_templates2 = __esm(() => {
108097
+ init_architect2();
107633
108098
  init_solo();
108099
+ init_uiux_engineer2();
107634
108100
  SOLO_ROLE_HANDOFF_CONTRACTS = [
107635
108101
  soloHandoffContract,
107636
- soloEnhancerHandoffContract
108102
+ soloArchitectHandoffContract,
108103
+ soloUiuxEngineerHandoffContract
107637
108104
  ];
107638
108105
  validatedSoloRoleHandoffContracts = (() => {
107639
108106
  validateRoleHandoffContracts(SOLO_ROLE_HANDOFF_CONTRACTS);
@@ -107691,13 +108158,21 @@ function getHandoffTemplate(query) {
107691
108158
  return getSoloHandoffTemplate(query);
107692
108159
  return null;
107693
108160
  }
107694
- var init_handoff_templates4 = __esm(() => {
108161
+ var init_handoff_templates3 = __esm(() => {
108162
+ init_handoff_templates();
107695
108163
  init_handoff_templates2();
107696
- init_handoff_templates3();
107697
108164
  });
107698
108165
 
107699
108166
  // ../../services/backend/prompts/cli/handoff/view-template.ts
107700
108167
  function viewHandoffTemplate(params) {
108168
+ if (params.nextRole === undefined) {
108169
+ const role = params.role.trim().toLowerCase();
108170
+ if (role === "architect")
108171
+ return getGenericArchitectToEntryPointHandoffTemplate();
108172
+ if (role === "uiux-engineer")
108173
+ return getGenericUiuxEngineerToEntryPointHandoffTemplate();
108174
+ throw new Error(`Role-only handoff template viewing is supported only for architect or uiux-engineer, not ${params.role}`);
108175
+ }
107701
108176
  const template = getHandoffTemplate({
107702
108177
  fromRole: params.role,
107703
108178
  toRole: params.nextRole,
@@ -107710,7 +108185,9 @@ function viewHandoffTemplate(params) {
107710
108185
  return template;
107711
108186
  }
107712
108187
  var init_view_template2 = __esm(() => {
107713
- init_handoff_templates4();
108188
+ init_architect_handoff_templates();
108189
+ init_uiux_engineer_handoff_templates();
108190
+ init_handoff_templates3();
107714
108191
  });
107715
108192
 
107716
108193
  // src/commands/handoff/view-template.ts
@@ -107755,12 +108232,12 @@ function listHandoffTemplatesCommand(params) {
107755
108232
  });
107756
108233
  if (!listing) {
107757
108234
  const teamId = params.teamId ?? "duo";
107758
- throw new Error(`No handoff contract for role "${params.role}" in team "${teamId}". Known roles: planner, builder, enhancer (duo) or solo, enhancer (solo).`);
108235
+ throw new Error(`No handoff contract for role "${params.role}" in team "${teamId}". Known roles: planner, builder (duo) or solo.`);
107759
108236
  }
107760
108237
  return formatHandoffTemplateListing(listing);
107761
108238
  }
107762
108239
  var init_list_templates = __esm(() => {
107763
- init_handoff_templates4();
108240
+ init_handoff_templates3();
107764
108241
  });
107765
108242
 
107766
108243
  // src/commands/handoff/list-templates.ts
@@ -107944,36 +108421,26 @@ function getHandoffTurnEndGuidance(nextRole) {
107944
108421
  return lines.join(`
107945
108422
  `);
107946
108423
  }
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
- }
107955
108424
  // ../../services/backend/prompts/sections/activated-skills.ts
107956
108425
  var init_activated_skills = () => {};
107957
108426
 
107958
108427
  // ../../services/backend/prompts/cli/backlog/command.ts
107959
- var init_command2 = __esm(() => {
108428
+ var init_command3 = __esm(() => {
107960
108429
  init_stdin_heredoc();
107961
108430
  });
107962
108431
 
107963
108432
  // ../../services/backend/src/domain/usecase/skills/modules/backlog/index.ts
107964
108433
  var init_backlog = __esm(() => {
107965
- init_command2();
108434
+ init_command3();
107966
108435
  });
107967
108436
 
107968
108437
  // ../../services/backend/src/domain/usecase/skills/modules/code-review/index.ts
107969
108438
  var init_code_review = () => {};
107970
-
107971
108439
  // ../../services/backend/src/domain/usecase/skills/modules/data-design/index.ts
107972
108440
  var init_data_design = () => {};
107973
108441
 
107974
108442
  // ../../services/backend/src/domain/usecase/skills/modules/defragmentation/index.ts
107975
108443
  var init_defragmentation = () => {};
107976
-
107977
108444
  // ../../services/backend/src/domain/usecase/skills/modules/user-centricity/index.ts
107978
108445
  var init_user_centricity = () => {};
107979
108446
 
@@ -108062,19 +108529,17 @@ var init_general_knowledge = __esm(() => {
108062
108529
  init_utils3();
108063
108530
  });
108064
108531
 
108065
- // ../../services/backend/prompts/cli/handoff/command.ts
108066
- var init_command3 = __esm(() => {
108067
- init_stdin_heredoc();
108068
- });
108069
-
108070
108532
  // ../../services/backend/prompts/sections/commands-reference.ts
108071
108533
  var init_commands_reference = __esm(() => {
108072
108534
  init_general_knowledge();
108073
108535
  init_command();
108074
108536
  init_reminder();
108075
- init_command3();
108537
+ init_command2();
108076
108538
  init_utils3();
108077
108539
  });
108540
+
108541
+ // ../../services/backend/prompts/cli/roles/architect.ts
108542
+ var init_architect3 = () => {};
108078
108543
  // ../../services/backend/prompts/cli/sections/core-responsibilities.ts
108079
108544
  var init_core_responsibilities = () => {};
108080
108545
  // ../../services/backend/prompts/cli/sections/handoff-rules.ts
@@ -108098,24 +108563,15 @@ var init_builder2 = __esm(() => {
108098
108563
  init_sections();
108099
108564
  });
108100
108565
 
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
108566
  // ../../services/backend/prompts/cli/roles/planner.ts
108114
108567
  var init_planner2 = __esm(() => {
108115
108568
  init_env();
108116
108569
  init_sections();
108117
108570
  });
108118
108571
 
108572
+ // ../../services/backend/prompts/cli/roles/uiux-engineer.ts
108573
+ var init_uiux_engineer3 = () => {};
108574
+
108119
108575
  // ../../services/backend/prompts/cli/roles/planner-guidance-context.ts
108120
108576
  var init_planner_guidance_context = __esm(() => {
108121
108577
  init_env();
@@ -108127,12 +108583,18 @@ var init_solo2 = __esm(() => {
108127
108583
  init_sections();
108128
108584
  });
108129
108585
 
108130
- // ../../services/backend/prompts/cli/roles/fromContext.ts
108586
+ // ../../services/backend/prompts/teams/solo/prompts/fromContext.ts
108131
108587
  var init_fromContext = __esm(() => {
108588
+ init_solo2();
108589
+ });
108590
+
108591
+ // ../../services/backend/prompts/cli/roles/fromContext.ts
108592
+ var init_fromContext2 = __esm(() => {
108593
+ init_architect3();
108132
108594
  init_builder2();
108133
- init_enhancer3();
108134
108595
  init_planner2();
108135
- init_solo2();
108596
+ init_uiux_engineer3();
108597
+ init_fromContext();
108136
108598
  });
108137
108599
 
108138
108600
  // ../../services/backend/prompts/teams/duo/prompts/builder.ts
@@ -108147,20 +108609,15 @@ var init_planner3 = __esm(() => {
108147
108609
  });
108148
108610
 
108149
108611
  // ../../services/backend/prompts/teams/duo/prompts/fromContext.ts
108150
- var init_fromContext2 = __esm(() => {
108612
+ var init_fromContext3 = __esm(() => {
108151
108613
  init_builder3();
108152
108614
  init_planner3();
108153
108615
  });
108154
-
108155
- // ../../services/backend/prompts/teams/solo/prompts/fromContext.ts
108156
- var init_fromContext3 = __esm(() => {
108157
- init_solo2();
108158
- });
108159
108616
  // ../../services/backend/prompts/selector-context.ts
108160
108617
  var init_selector_context = __esm(() => {
108161
- init_fromContext();
108162
108618
  init_fromContext2();
108163
108619
  init_fromContext3();
108620
+ init_fromContext();
108164
108621
  });
108165
108622
 
108166
108623
  // ../../services/backend/prompts/sections/role-guidance.ts
@@ -108169,7 +108626,9 @@ var init_role_guidance = __esm(() => {
108169
108626
  });
108170
108627
 
108171
108628
  // ../../services/backend/prompts/templates.ts
108172
- var init_templates = () => {};
108629
+ var init_templates = __esm(() => {
108630
+ init_agent_role();
108631
+ });
108173
108632
 
108174
108633
  // ../../services/backend/prompts/sections/role-identity.ts
108175
108634
  var init_role_identity = __esm(() => {
@@ -108216,40 +108675,12 @@ var init_session_vs_chatroom_task = () => {};
108216
108675
  // ../../services/backend/prompts/review-guidelines/index.ts
108217
108676
  var init_review_guidelines = () => {};
108218
108677
  // ../../services/backend/prompts/generator.ts
108219
- function isEnhancerRequestQueuedHandoff(params) {
108220
- return params.enhancerRequestQueued === true && params.nextRole.toLowerCase() === "enhancer";
108221
- }
108222
- function getEnhancerRequestQueuedConfirmationLines(nativeIntegration) {
108223
- const turnEndRule = nativeIntegration ? "**Handoff complete. End your turn now — stop tool calls. The system will send you a message when further action is required.** Do **not** wait for enhancer input, poll, monitor the enhancer, or re-submit the handoff. The system delivers independent planning input as your next task when analysis completes." : "**Run get-next-task now and end your turn — stop tool calls. The system will send you a message when further action is required.** Do **not** wait for enhancer input, poll, monitor the enhancer, or re-submit the handoff.";
108224
- return [
108225
- "✅ User request queued for handoff enhancer",
108226
- "",
108227
- "The user request was sent to the handoff enhancer (async). You will receive independent planning input as your next task when analysis completes.",
108228
- turnEndRule
108229
- ];
108230
- }
108231
108678
  function generateHandoffOutput(params) {
108232
- const {
108233
- role,
108234
- nextRole,
108235
- chatroomId,
108236
- convexUrl,
108237
- supportsNativeIntegration,
108238
- enhancerRequestQueued
108239
- } = params;
108679
+ const { role, nextRole, chatroomId, convexUrl, supportsNativeIntegration } = params;
108240
108680
  const cliEnvPrefix = getCliEnvPrefix(convexUrl);
108241
- const enhancerRequest = isEnhancerRequestQueuedHandoff({
108242
- nextRole,
108243
- enhancerRequestQueued
108244
- });
108245
- const lines = [];
108246
- if (enhancerRequest) {
108247
- lines.push(...getEnhancerRequestQueuedConfirmationLines(supportsNativeIntegration));
108248
- } else {
108249
- lines.push(`✅ Chatroom task completed and handed off to ${nextRole}`);
108250
- }
108681
+ const lines = [`✅ Chatroom task completed and handed off to ${nextRole}`];
108251
108682
  if (supportsNativeIntegration) {
108252
- lines.push(enhancerRequest ? getNativeEnhancerRequestTurnEndGuidance() : getNativeHandoffTurnEndGuidance(nextRole));
108683
+ lines.push(getNativeHandoffTurnEndGuidance(nextRole));
108253
108684
  } else {
108254
108685
  lines.push("");
108255
108686
  lines.push("✅ Level B complete (chatroom task handed off).");
@@ -108280,6 +108711,21 @@ var init_generator = __esm(() => {
108280
108711
  init_review_guidelines();
108281
108712
  });
108282
108713
 
108714
+ // src/infrastructure/cli-gateway/local-handoff-gateway.ts
108715
+ function createLocalHandoffGateway() {
108716
+ return {
108717
+ handoff: (args2) => requestLocalDaemon(resolveLocalWebPort(), "cli.handoff", args2).catch((error51) => {
108718
+ if (error51 instanceof LocalDaemonServerError)
108719
+ throw error51;
108720
+ throw new Error(`CLI gateway unavailable. Start the Chatroom daemon and retry. ${error51 instanceof Error ? error51.message : String(error51)}`);
108721
+ })
108722
+ };
108723
+ }
108724
+ var init_local_handoff_gateway = __esm(() => {
108725
+ init_local_daemon();
108726
+ init_resolve_local_web_port();
108727
+ });
108728
+
108283
108729
  // src/infrastructure/deps/create-convex-command-deps.ts
108284
108730
  async function createConvexCommandDeps() {
108285
108731
  const client4 = await getConvexClient();
@@ -108305,7 +108751,8 @@ __export(exports_handoff, {
108305
108751
  handoff: () => handoff
108306
108752
  });
108307
108753
  async function createDefaultDeps10() {
108308
- return createConvexCommandDeps();
108754
+ const deps = await createConvexCommandDeps();
108755
+ return { backend: deps.backend, gateway: createLocalHandoffGateway(), session: deps.session };
108309
108756
  }
108310
108757
  function handleHandoffError(err) {
108311
108758
  return exports_Effect.sync(() => {
@@ -108376,27 +108823,24 @@ async function handoff(chatroomId, options, deps) {
108376
108823
  }
108377
108824
  var handoffEffect = (chatroomId, options) => exports_Effect.gen(function* () {
108378
108825
  const session2 = yield* SessionService;
108379
- const backend2 = yield* BackendService;
108826
+ const gateway = yield* CliGatewayService;
108380
108827
  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
108828
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
108829
+ const result = yield* exports_Effect.tryPromise({
108830
+ try: () => gateway.handoff({
108831
+ sessionId,
108832
+ chatroomId,
108833
+ senderRole: role,
108834
+ content: message,
108835
+ targetRole: nextRole
108836
+ }),
108837
+ catch: (cause3) => cause3 instanceof Error ? cause3 : new Error(String(cause3))
108396
108838
  }).pipe(exports_Effect.mapError((cause3) => {
108397
108839
  let errorData;
108398
108840
  if (cause3 instanceof ConvexError) {
108399
108841
  errorData = cause3.data;
108842
+ } else if (cause3 instanceof LocalDaemonServerError && cause3.details) {
108843
+ errorData = cause3.details;
108400
108844
  }
108401
108845
  return { _tag: "HandoffFailed", cause: cause3, errorData };
108402
108846
  }));
@@ -108413,8 +108857,7 @@ var handoffEffect = (chatroomId, options) => exports_Effect.gen(function* () {
108413
108857
  nextRole,
108414
108858
  chatroomId,
108415
108859
  convexUrl,
108416
- supportsNativeIntegration: result.supportsNativeIntegration,
108417
- enhancerRequestQueued: result.enhancerRequestQueued === true
108860
+ supportsNativeIntegration: result.supportsNativeIntegration
108418
108861
  }));
108419
108862
  });
108420
108863
  });
@@ -108422,7 +108865,8 @@ var init_handoff = __esm(() => {
108422
108865
  init_generator();
108423
108866
  init_values();
108424
108867
  init_esm();
108425
- init_api3();
108868
+ init_local_daemon();
108869
+ init_local_handoff_gateway();
108426
108870
  init_create_convex_command_deps();
108427
108871
  init_services();
108428
108872
  init_error_formatting();
@@ -109711,15 +110155,7 @@ async function activateSkill(chatroomId, skillId, options, deps) {
109711
110155
  }
109712
110156
  var listSkillsEffect = (chatroomId, _options) => exports_Effect.gen(function* () {
109713
110157
  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
- }));
110158
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
109723
110159
  const skills = yield* backend2.query(api.skills.list, {
109724
110160
  sessionId,
109725
110161
  chatroomId
@@ -109739,15 +110175,7 @@ var listSkillsEffect = (chatroomId, _options) => exports_Effect.gen(function* ()
109739
110175
  }), activateSkillEffect = (chatroomId, skillId, options) => exports_Effect.gen(function* () {
109740
110176
  const session2 = yield* SessionService;
109741
110177
  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
- }));
110178
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
109751
110179
  const convexUrl = yield* session2.getConvexUrl();
109752
110180
  const result = yield* backend2.mutation(api.skills.activate, {
109753
110181
  sessionId,
@@ -110163,15 +110591,7 @@ var PAGE_SIZE = 50, SINCE_PAGE_SIZE = 500, ABSOLUTE_MAX = 5000, DEFAULT_LIMIT =
110163
110591
  }), downloadMessagesEffect = (chatroomId, options) => exports_Effect.gen(function* () {
110164
110592
  const backend2 = yield* BackendService;
110165
110593
  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
- }));
110594
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
110175
110595
  const format4 = options.format ?? "linear";
110176
110596
  const maxDownload = parseLimit(options.limit);
110177
110597
  const outputDir = options.outputDir ?? resolveDownloadOutputDir(format4);
@@ -110442,15 +110862,7 @@ async function listSinceMessage(chatroomId, options, deps) {
110442
110862
  }
110443
110863
  var listBySenderRoleEffect = (chatroomId, options) => exports_Effect.gen(function* () {
110444
110864
  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
- }));
110865
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
110454
110866
  const messages = yield* backend2.query(api.messages.listBySenderRole, {
110455
110867
  sessionId,
110456
110868
  chatroomId,
@@ -110485,15 +110897,7 @@ var listBySenderRoleEffect = (chatroomId, options) => exports_Effect.gen(functio
110485
110897
  });
110486
110898
  }), listSinceMessageEffect = (chatroomId, options) => exports_Effect.gen(function* () {
110487
110899
  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
- }));
110900
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
110497
110901
  const messages = yield* backend2.query(api.messages.listSinceMessage, {
110498
110902
  sessionId,
110499
110903
  chatroomId,
@@ -110960,15 +111364,7 @@ async function viewManyArtifacts(chatroomId, options, deps) {
110960
111364
  }
110961
111365
  var createArtifactEffect = (chatroomId, options) => exports_Effect.gen(function* () {
110962
111366
  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
- }));
111367
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
110972
111368
  if (!options.fromFile.endsWith(".md")) {
110973
111369
  return yield* exports_Effect.fail({
110974
111370
  _tag: "InvalidFileExtension",
@@ -111006,15 +111402,7 @@ var createArtifactEffect = (chatroomId, options) => exports_Effect.gen(function*
111006
111402
  return artifactId;
111007
111403
  }), viewArtifactEffect = (chatroomId, options) => exports_Effect.gen(function* () {
111008
111404
  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
- }));
111405
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
111018
111406
  const artifact = yield* backend2.query(api.artifacts.get, {
111019
111407
  sessionId,
111020
111408
  artifactId: options.artifactId
@@ -111040,15 +111428,7 @@ var createArtifactEffect = (chatroomId, options) => exports_Effect.gen(function*
111040
111428
  });
111041
111429
  }), viewManyArtifactsEffect = (chatroomId, options) => exports_Effect.gen(function* () {
111042
111430
  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
- }));
111431
+ const sessionId = yield* requireSessionForChatroomEffect({ chatroomId });
111052
111432
  if (options.artifactIds.length === 0) {
111053
111433
  return yield* exports_Effect.fail({ _tag: "NoArtifactIds" });
111054
111434
  }
@@ -111446,12 +111826,12 @@ var init_durable_fifo_queue_schema = __esm(() => {
111446
111826
  });
111447
111827
 
111448
111828
  // 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";
111829
+ import { mkdirSync as mkdirSync8 } from "node:fs";
111830
+ import { dirname as dirname11 } from "node:path";
111831
+ import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
111452
111832
  function openDurableFifoQueueStore(dbPath) {
111453
- mkdirSync7(dirname10(dbPath), { recursive: true });
111454
- const db = new DatabaseSync2(dbPath);
111833
+ mkdirSync8(dirname11(dbPath), { recursive: true });
111834
+ const db = new DatabaseSync3(dbPath);
111455
111835
  for (const sql of DURABLE_FIFO_QUEUE_MIGRATIONS)
111456
111836
  db.exec(sql);
111457
111837
  db.prepare("UPDATE fifo_outbox_entries SET status='pending', updated_at=? WHERE status='in_flight'").run(Date.now());
@@ -111868,7 +112248,8 @@ var AgentLifecycleOutboxService, AgentLifecycleOutboxServiceLive = (registry3, m
111868
112248
  resumeTurnForSlot: (args2) => exports_Effect.promise(() => mgr.resumeTurnForSlot(args2)),
111869
112249
  subscribeAgentTurnEnded: (handler) => processManagerService.subscribeAgentTurnEnded(handler),
111870
112250
  subscribeAgentStarted: (handler) => processManagerService.subscribeAgentStarted(handler),
111871
- subscribeAgentSessionLost: (handler) => processManagerService.subscribeAgentSessionLost(handler)
112251
+ subscribeAgentSessionLost: (handler) => processManagerService.subscribeAgentSessionLost(handler),
112252
+ subscribeAgentTurnProgress: (handler) => processManagerService.subscribeAgentTurnProgress(handler)
111872
112253
  }), DaemonAgentProcessManagerCommandService, DaemonAgentProcessManagerCommandServiceLive = (service4) => exports_Layer.succeed(DaemonAgentProcessManagerCommandService, service4), DaemonSessionService, DaemonMutableStateService;
111873
112254
  var init_daemon_services = __esm(() => {
111874
112255
  init_esm();
@@ -112162,6 +112543,14 @@ var init_output_store = __esm(() => {
112162
112543
  RUN_ID_RE = /^[a-z0-9]+$/i;
112163
112544
  });
112164
112545
 
112546
+ // src/daemon/entry/task-handoff-repository-path.ts
112547
+ import { homedir as homedir9 } from "node:os";
112548
+ import { join as join22 } from "node:path";
112549
+ function resolveTaskHandoffRepositoryPath(machineId) {
112550
+ return join22(homedir9(), ".chatroom", "daemon", machineId, "task-handoffs.sqlite");
112551
+ }
112552
+ var init_task_handoff_repository_path = () => {};
112553
+
112165
112554
  // src/infrastructure/services/harness-spawning/rate-limiter.ts
112166
112555
  class SpawnRateLimiter {
112167
112556
  config;
@@ -112617,6 +113006,7 @@ function assembleDaemonSessionInit(args2) {
112617
113006
  convexUrl,
112618
113007
  backend: deps.backend,
112619
113008
  configurationService: agentConfigRegistry,
113009
+ handoffRepository: createTaskHandoffRepository(resolveTaskHandoffRepositoryPath(machineId)),
112620
113010
  logEvent: activeLogEvent ?? (async () => {
112621
113011
  return;
112622
113012
  })
@@ -112657,9 +113047,6 @@ class NetworkRetryError {
112657
113047
  this.attempt = attempt;
112658
113048
  }
112659
113049
  }
112660
- function getActiveLogSink() {
112661
- return activeLogSink;
112662
- }
112663
113050
  async function initDaemon(options = {}) {
112664
113051
  activeLogSink = options.logSink;
112665
113052
  activeLogEvent = options.logEvent;
@@ -112860,6 +113247,7 @@ var init_init_daemon = __esm(() => {
112860
113247
  init_daemon_startup_log();
112861
113248
  init_orphan_tracker();
112862
113249
  init_output_store();
113250
+ init_task_handoff_repository_path();
112863
113251
  init_pid();
112864
113252
  init_storage();
112865
113253
  init_client2();
@@ -112874,6 +113262,7 @@ var init_init_daemon = __esm(() => {
112874
113262
  init_agent_process_service2();
112875
113263
  init_chatroom_workspace_configuration_service();
112876
113264
  init_service_interfaces();
113265
+ init_task_service2();
112877
113266
  AUTH_WAIT_TIMEOUT_MS = 5 * 60 * 1000;
112878
113267
  initDaemonEffect = exports_Effect.gen(function* () {
112879
113268
  const lockAcquired = yield* exports_Effect.tryPromise({
@@ -117228,16 +117617,16 @@ var MAX_NAME_LENGTH = 256;
117228
117617
 
117229
117618
  // src/infrastructure/services/workspace/workspace-resolver.ts
117230
117619
  import { readFile as readFile7, readdir, stat as stat4 } from "node:fs/promises";
117231
- import { join as join22, basename, resolve as resolve4 } from "node:path";
117620
+ import { join as join23, basename, resolve as resolve4 } from "node:path";
117232
117621
  async function resolveGlobPatternStar(rootDir, cleaned) {
117233
- const parentDir = join22(rootDir, cleaned.slice(0, -2));
117622
+ const parentDir = join23(rootDir, cleaned.slice(0, -2));
117234
117623
  try {
117235
117624
  const entries2 = await readdir(parentDir, { withFileTypes: true });
117236
117625
  const dirs = [];
117237
117626
  for (const entry of entries2) {
117238
117627
  if (!entry.isDirectory())
117239
117628
  continue;
117240
- const dirPath = join22(parentDir, entry.name);
117629
+ const dirPath = join23(parentDir, entry.name);
117241
117630
  if (resolve4(dirPath).startsWith(resolve4(rootDir))) {
117242
117631
  dirs.push(dirPath);
117243
117632
  }
@@ -117248,7 +117637,7 @@ async function resolveGlobPatternStar(rootDir, cleaned) {
117248
117637
  }
117249
117638
  }
117250
117639
  async function resolveLiteralPath(rootDir, cleaned) {
117251
- const dir = join22(rootDir, cleaned);
117640
+ const dir = join23(rootDir, cleaned);
117252
117641
  try {
117253
117642
  if (!resolve4(dir).startsWith(resolve4(rootDir)))
117254
117643
  return [];
@@ -117268,7 +117657,7 @@ async function resolveGlobPattern(rootDir, pattern) {
117268
117657
  }
117269
117658
  async function readPackageJson(dir) {
117270
117659
  try {
117271
- const content = await readFile7(join22(dir, "package.json"), "utf-8");
117660
+ const content = await readFile7(join23(dir, "package.json"), "utf-8");
117272
117661
  const pkg = JSON.parse(content);
117273
117662
  return {
117274
117663
  name: pkg.name || basename(dir),
@@ -117280,7 +117669,7 @@ async function readPackageJson(dir) {
117280
117669
  }
117281
117670
  async function readPnpmWorkspacePatterns(rootDir) {
117282
117671
  try {
117283
- const content = await readFile7(join22(rootDir, "pnpm-workspace.yaml"), "utf-8");
117672
+ const content = await readFile7(join23(rootDir, "pnpm-workspace.yaml"), "utf-8");
117284
117673
  const patterns = [];
117285
117674
  let inPackages = false;
117286
117675
  for (const line of content.split(`
@@ -117307,7 +117696,7 @@ async function readPnpmWorkspacePatterns(rootDir) {
117307
117696
  }
117308
117697
  async function readPackageJsonWorkspacePatterns(rootDir) {
117309
117698
  try {
117310
- const content = await readFile7(join22(rootDir, "package.json"), "utf-8");
117699
+ const content = await readFile7(join23(rootDir, "package.json"), "utf-8");
117311
117700
  const pkg = JSON.parse(content);
117312
117701
  if (!pkg.workspaces)
117313
117702
  return [];
@@ -117356,11 +117745,11 @@ var init_workspace_resolver = () => {};
117356
117745
 
117357
117746
  // src/infrastructure/services/workspace/command-discovery.ts
117358
117747
  import { access as access2, readFile as readFile8 } from "node:fs/promises";
117359
- import { join as join23, relative, basename as basename2 } from "node:path";
117748
+ import { join as join24, relative, basename as basename2 } from "node:path";
117360
117749
  async function detectPackageManager(workingDir) {
117361
117750
  for (const { file: file2, manager } of LOCKFILE_MAP) {
117362
117751
  try {
117363
- await access2(join23(workingDir, file2));
117752
+ await access2(join24(workingDir, file2));
117364
117753
  return manager;
117365
117754
  } catch {}
117366
117755
  }
@@ -117429,7 +117818,7 @@ function collectRootScriptCommands(scripts, pm, scriptPrefix, rootSw) {
117429
117818
  }
117430
117819
  async function readRootPackageJson(workingDir, pm, scriptPrefix) {
117431
117820
  let rootPackageName = basename2(workingDir);
117432
- const pkg = await readJsonFile(join23(workingDir, "package.json"), "root package.json");
117821
+ const pkg = await readJsonFile(join24(workingDir, "package.json"), "root package.json");
117433
117822
  if (!pkg)
117434
117823
  return { commands: [], rootPackageName };
117435
117824
  if (pkg.name)
@@ -117445,7 +117834,7 @@ async function readRootPackageJson(workingDir, pm, scriptPrefix) {
117445
117834
  }
117446
117835
  async function readTurboJson(workingDir, _turboPrefix, _rootSubWorkspace) {
117447
117836
  const turboTaskNames = [];
117448
- const turbo = await readJsonFile(join23(workingDir, "turbo.json"), "turbo.json");
117837
+ const turbo = await readJsonFile(join24(workingDir, "turbo.json"), "turbo.json");
117449
117838
  if (!turbo?.tasks || typeof turbo.tasks !== "object")
117450
117839
  return turboTaskNames;
117451
117840
  for (const taskName of Object.keys(turbo.tasks)) {
@@ -117458,7 +117847,7 @@ async function readTurboJson(workingDir, _turboPrefix, _rootSubWorkspace) {
117458
117847
  async function readDenoJson(workingDir) {
117459
117848
  const commands = [];
117460
117849
  for (const fileName of ["deno.json", "deno.jsonc"]) {
117461
- const deno = await readJsonFile(join23(workingDir, fileName), fileName);
117850
+ const deno = await readJsonFile(join24(workingDir, fileName), fileName);
117462
117851
  if (!deno?.tasks || typeof deno.tasks !== "object")
117463
117852
  continue;
117464
117853
  const pkgName = deno.name ?? basename2(workingDir);
@@ -117481,7 +117870,7 @@ async function readMakefile(workingDir) {
117481
117870
  const commands = [];
117482
117871
  for (const fileName of ["Makefile", "makefile"]) {
117483
117872
  try {
117484
- const content = await readFile8(join23(workingDir, fileName), "utf-8");
117873
+ const content = await readFile8(join24(workingDir, fileName), "utf-8");
117485
117874
  const targets = parseMakefileTargets(content);
117486
117875
  if (targets.length === 0)
117487
117876
  continue;
@@ -118056,7 +118445,7 @@ async function runRestartOrchestrator(deps, event) {
118056
118445
  const { chatroomId, role } = event;
118057
118446
  markRestartOrchestratorInFlight(chatroomId, role, event.correlationId);
118058
118447
  try {
118059
- resetRoleDeliveryState(chatroomId, role);
118448
+ await deps.nativeDelivery.handleAgentRestart({ chatroomId, role });
118060
118449
  await emitPhase(deps, event, "reset");
118061
118450
  await emitPhase(deps, event, "spawn");
118062
118451
  const spawnResult = await deps.runSerializedForAgent({ chatroomId, role, workingDir: event.workingDir }, { timeoutMs: HARNESS_SESSION_READY_TIMEOUT_MS }, async (ops, context5) => {
@@ -118067,7 +118456,7 @@ async function runRestartOrchestrator(deps, event) {
118067
118456
  agentHarness: event.agentHarness,
118068
118457
  model: event.model,
118069
118458
  workingDir: event.workingDir,
118070
- reason: "user.restart",
118459
+ reason: AgentStartReasonCode.USER_RESTART,
118071
118460
  wantResume: event.wantResume
118072
118461
  }, context5.signal);
118073
118462
  });
@@ -118081,7 +118470,7 @@ async function runRestartOrchestrator(deps, event) {
118081
118470
  await deps.runSerializedForAgent({ chatroomId, role, workingDir: event.workingDir }, { timeoutMs: HARNESS_SESSION_READY_TIMEOUT_MS }, (ops, context5) => ops.stopAgent({
118082
118471
  chatroomId,
118083
118472
  role,
118084
- reason: "user.restart",
118473
+ reason: AgentStartReasonCode.USER_RESTART,
118085
118474
  pid: spawnResult.pid,
118086
118475
  workingDir: event.workingDir
118087
118476
  }, context5.signal));
@@ -118112,12 +118501,12 @@ async function runRestartOrchestrator(deps, event) {
118112
118501
  }
118113
118502
  var init_restart_orchestrator = __esm(() => {
118114
118503
  init_reliability();
118504
+ init_agent();
118115
118505
  init_build_agent_restart_event();
118116
118506
  init_restart_orchestrator_in_flight();
118117
118507
  init_api3();
118118
118508
  init_convex_error();
118119
118509
  init_execution_kind();
118120
- init_service_interfaces();
118121
118510
  });
118122
118511
 
118123
118512
  // src/daemon/entry/bridge/agent-control-bridge.ts
@@ -118272,19 +118661,6 @@ async function startAgent2(deps, input) {
118272
118661
  });
118273
118662
  }
118274
118663
 
118275
- // src/daemon/entry/enhancer/enhancer-drain-registry.ts
118276
- function setEnhancerDrainHandler(fn2) {
118277
- drainFn = fn2;
118278
- }
118279
- async function drainPendingEnhancerJobsIfRegistered() {
118280
- if (drainFn)
118281
- await drainFn();
118282
- }
118283
- function clearEnhancerDrainHandlerForTests() {
118284
- drainFn = null;
118285
- }
118286
- var drainFn = null;
118287
-
118288
118664
  // src/daemon/entry/events/agent/on-request-start-agent.ts
118289
118665
  var onRequestStartAgentEffect = (event) => exports_Effect.gen(function* () {
118290
118666
  const processManagerService = yield* DaemonAgentProcessManagerCommandService;
@@ -118301,7 +118677,6 @@ var onRequestStartAgentEffect = (event) => exports_Effect.gen(function* () {
118301
118677
  deadline: event.deadline,
118302
118678
  wantResume: event.wantResume
118303
118679
  });
118304
- await drainPendingEnhancerJobsIfRegistered();
118305
118680
  });
118306
118681
  });
118307
118682
  var init_on_request_start_agent = __esm(() => {
@@ -118637,435 +119012,19 @@ async function dispatchCommandInboundEvent(event) {
118637
119012
  }
118638
119013
  var handler2;
118639
119014
 
118640
- // src/daemon/entry/enhancer/constants.ts
118641
- var ENHANCER_AGENT_ROLE = "enhancer", ENHANCER_AGENT_END_GRACE_MS = 3000;
118642
-
118643
- // src/daemon/entry/enhancer/enhancer-log.ts
118644
- function formatEnhancerLogLine(message) {
118645
- const trimmed = message.trimEnd();
118646
- if (trimmed.startsWith(ENHANCER_LOG_PREFIX))
118647
- return trimmed;
118648
- return `${ENHANCER_LOG_PREFIX} ${trimmed}`;
118649
- }
118650
- function createEnhancerLogWriter(logSink, context5, clock4 = () => Date.now()) {
118651
- return {
118652
- write(message) {
118653
- const formatted = formatEnhancerLogLine(message);
118654
- logSink?.write({
118655
- timestamp: clock4(),
118656
- level: "info",
118657
- source: `harness:${context5.harness}`,
118658
- stream: "stdout",
118659
- message: formatted,
118660
- metadata: {
118661
- chatroomId: context5.chatroomId,
118662
- role: ENHANCER_AGENT_ROLE,
118663
- harness: context5.harness,
118664
- ...context5.pid !== undefined ? { pid: context5.pid } : {}
118665
- }
118666
- });
118667
- }
118668
- };
118669
- }
118670
- var ENHANCER_LOG_PREFIX = "[enhancer]";
118671
- var init_enhancer_log = () => {};
118672
-
118673
- // src/daemon/entry/enhancer/enhancer-spawn-registry.ts
118674
- function registerEnhancerSpawn(handle) {
118675
- const handles = byChatroom.get(handle.chatroomId) ?? new Set;
118676
- handles.add(handle);
118677
- byChatroom.set(handle.chatroomId, handles);
118678
- }
118679
- function unregisterEnhancerSpawn(handle) {
118680
- const handles = byChatroom.get(handle.chatroomId);
118681
- if (!handles)
118682
- return;
118683
- handles.delete(handle);
118684
- if (handles.size === 0)
118685
- byChatroom.delete(handle.chatroomId);
118686
- }
118687
- async function abortEnhancerSpawnsForChatroom(chatroomId) {
118688
- const handles = byChatroom.get(chatroomId);
118689
- if (!handles)
118690
- return;
118691
- await Promise.all([...handles].map(async (handle) => {
118692
- handle.abort();
118693
- await handle.stopProcess().catch(() => {
118694
- return;
118695
- });
118696
- }));
118697
- }
118698
- var byChatroom;
118699
- var init_enhancer_spawn_registry = __esm(() => {
118700
- byChatroom = new Map;
118701
- });
118702
-
118703
- // src/daemon/entry/enhancer/job-outcome-subscription.ts
118704
- function subscribeToEnhancerJobOutcome(args2) {
118705
- let currentState = null;
118706
- let resolveOutcome = null;
118707
- let rejectOutcome = null;
118708
- let stopped = false;
118709
- const outcome = new Promise((resolve5, reject) => {
118710
- resolveOutcome = resolve5;
118711
- rejectOutcome = reject;
118712
- });
118713
- const unsub = args2.wsClient.onUpdate(api.web.enhancer.index.getJobOutcome, {
118714
- sessionId: args2.sessionId,
118715
- chatroomId: args2.chatroomId,
118716
- jobId: args2.jobId
118717
- }, (state) => {
118718
- if (stopped)
118719
- return;
118720
- if (!state) {
118721
- rejectOutcome?.(new Error("Enhancer job not found"));
118722
- resolveOutcome = null;
118723
- rejectOutcome = null;
118724
- return;
118725
- }
118726
- currentState = state;
118727
- if (state.status === "complete" || state.status === "failed" || state.status === "cancelled") {
118728
- resolveOutcome?.(state);
118729
- resolveOutcome = null;
118730
- rejectOutcome = null;
118731
- }
118732
- }, (error51) => {
118733
- if (stopped)
118734
- return;
118735
- rejectOutcome?.(error51);
118736
- resolveOutcome = null;
118737
- rejectOutcome = null;
118738
- });
118739
- return {
118740
- outcome,
118741
- getCurrentState: () => currentState,
118742
- stop: () => {
118743
- if (stopped)
118744
- return;
118745
- stopped = true;
118746
- unsub();
118747
- resolveOutcome = null;
118748
- rejectOutcome = null;
118749
- }
118750
- };
118751
- }
118752
- var init_job_outcome_subscription = __esm(() => {
118753
- init_api3();
118754
- });
118755
-
118756
- // src/daemon/entry/enhancer/wait-for-enhancer-job.ts
118757
- async function waitForEnhancerJobResolution(params) {
118758
- const { sessionId, chatroomId, jobId, wsClient: wsClient2, onFailure, onSalvageComplete, log: log4, signal } = params;
118759
- const writeLog = (message) => log4?.write(message);
118760
- let outcome = null;
118761
- let salvagedText = "";
118762
- let agentEndTimer = null;
118763
- let resolveWait = null;
118764
- const waitPromise = new Promise((resolve5) => {
118765
- resolveWait = resolve5;
118766
- });
118767
- const subscription = subscribeToEnhancerJobOutcome({
118768
- wsClient: wsClient2,
118769
- sessionId,
118770
- chatroomId,
118771
- jobId
118772
- });
118773
- const finish = (resolution) => {
118774
- if (outcome)
118775
- return;
118776
- outcome = resolution;
118777
- resolveWait?.();
118778
- resolveWait = null;
118779
- };
118780
- const onAbort = () => finish("aborted");
118781
- if (signal?.aborted)
118782
- onAbort();
118783
- else
118784
- signal?.addEventListener("abort", onAbort, { once: true });
118785
- const failAfterAgentEnd = () => {
118786
- writeLog("agent_end: turn ended without complete — failing terminal");
118787
- onFailure("Agent exited without completing enhancer job", true);
118788
- finish("failed");
118789
- };
118790
- subscription.outcome.then((state) => {
118791
- if (outcome)
118792
- return;
118793
- const resolution = state.status === "complete" ? "complete" : "failed";
118794
- if (resolution === "complete")
118795
- writeLog(`completed job=${jobId}`);
118796
- finish(resolution);
118797
- }).catch((error51) => {
118798
- if (outcome)
118799
- return;
118800
- writeLog(`enhancer outcome subscription failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
118801
- onFailure("Enhancer job outcome subscription failed", false);
118802
- finish("failed");
118803
- });
118804
- params.onAssistantText?.((text) => {
118805
- salvagedText += text;
118806
- });
118807
- params.onAgentEnd?.(() => {
118808
- if (outcome)
118809
- return;
118810
- agentEndTimer = setTimeout(() => {
118811
- if (outcome)
118812
- return;
118813
- const state = subscription.getCurrentState();
118814
- if (state?.status === "complete") {
118815
- finish("complete");
118816
- return;
118817
- }
118818
- if (state?.status !== "running") {
118819
- failAfterAgentEnd();
118820
- return;
118821
- }
118822
- const trimmed = salvagedText.trim();
118823
- if (!trimmed || !onSalvageComplete) {
118824
- failAfterAgentEnd();
118825
- return;
118826
- }
118827
- onSalvageComplete(trimmed).catch(() => {
118828
- failAfterAgentEnd();
118829
- });
118830
- }, ENHANCER_AGENT_END_GRACE_MS);
118831
- });
118832
- params.onExit(() => {
118833
- if (outcome)
118834
- return;
118835
- onFailure("Agent process exited without completing enhancer job", false);
118836
- finish("failed");
118837
- });
118838
- await waitPromise;
118839
- if (agentEndTimer)
118840
- clearTimeout(agentEndTimer);
118841
- subscription.stop();
118842
- signal?.removeEventListener("abort", onAbort);
118843
- return outcome ?? "failed";
118844
- }
118845
- var init_wait_for_enhancer_job = __esm(() => {
118846
- init_job_outcome_subscription();
118847
- });
118848
-
118849
- // src/daemon/entry/enhancer-inbound-registry.ts
118850
- function registerEnhancerInboundHandler(h) {
119015
+ // src/daemon/entry/file-inbound-registry.ts
119016
+ function registerFileInboundHandler(h) {
118851
119017
  handler3 = h;
118852
119018
  }
118853
- function unregisterEnhancerInboundHandler() {
119019
+ function unregisterFileInboundHandler() {
118854
119020
  handler3 = undefined;
118855
119021
  }
118856
- async function dispatchEnhancerInboundEvent(event) {
119022
+ async function dispatchFileInboundEvent(event) {
118857
119023
  if (handler3)
118858
119024
  await handler3(event);
118859
119025
  }
118860
119026
  var handler3;
118861
119027
 
118862
- // src/daemon/entry/enhancer/job-subscriber.ts
118863
- async function processEnhancerJobForSpawn(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, job, inFlight2, logSink) {
118864
- if (inFlight2.has(job.jobId))
118865
- return;
118866
- if (isChatroomStopScopeActive(job.chatroomId))
118867
- return;
118868
- inFlight2.add(job.jobId);
118869
- let claimed = false;
118870
- let chatroomId = job.chatroomId;
118871
- let jobId = job.jobId;
118872
- let spawnResult = null;
118873
- let service4 = null;
118874
- let abortController = null;
118875
- let spawnHandle = null;
118876
- let agentEndSeen = false;
118877
- let resolveAgentEnd;
118878
- let agentEndPromise = Promise.resolve();
118879
- let log4 = createEnhancerLogWriter(logSink, {
118880
- chatroomId: job.chatroomId,
118881
- harness: "unknown"
118882
- });
118883
- try {
118884
- const claim = await backend2.mutation(api.daemon.enhancer.index.claimForSpawn, {
118885
- sessionId,
118886
- jobId: job.jobId,
118887
- machineId
118888
- });
118889
- if (!claim.claimed)
118890
- return;
118891
- claimed = true;
118892
- log4.write(`claimed job=${job.jobId} chatroom=${job.chatroomId}`);
118893
- const payload = await backend2.query(api.daemon.enhancer.index.getTaskDeliveryForJob, {
118894
- sessionId,
118895
- jobId: job.jobId,
118896
- convexUrl
118897
- });
118898
- chatroomId = payload.chatroomId;
118899
- jobId = payload.jobId;
118900
- log4 = createEnhancerLogWriter(logSink, {
118901
- chatroomId: payload.chatroomId,
118902
- harness: payload.agentHarness,
118903
- pid: undefined
118904
- });
118905
- log4.write(`spawning harness=${payload.agentHarness} model=${payload.model} job=${payload.jobId}`);
118906
- service4 = agentServices.get(payload.agentHarness) ?? null;
118907
- if (!service4) {
118908
- await backend2.mutation(api.web.enhancer.index.recordAttemptFailure, {
118909
- sessionId,
118910
- chatroomId: payload.chatroomId,
118911
- jobId: payload.jobId,
118912
- error: `Harness ${payload.agentHarness} not available on machine`
118913
- });
118914
- return;
118915
- }
118916
- const spawned = await service4.spawn({
118917
- workingDir: payload.workingDir,
118918
- prompt: createSpawnPrompt(payload.taskDeliveryOutput),
118919
- systemPrompt: payload.systemPrompt,
118920
- model: payload.model,
118921
- context: {
118922
- machineId,
118923
- chatroomId: payload.chatroomId,
118924
- role: ENHANCER_AGENT_ROLE
118925
- },
118926
- resolvedConvexUrl: convexUrl
118927
- });
118928
- spawnResult = spawned;
118929
- abortController = new AbortController;
118930
- spawnHandle = {
118931
- jobId: payload.jobId,
118932
- chatroomId: payload.chatroomId,
118933
- abort: () => abortController?.abort(),
118934
- stopProcess: async () => {
118935
- await service4?.stop(spawned.pid);
118936
- }
118937
- };
118938
- registerEnhancerSpawn(spawnHandle);
118939
- log4 = createEnhancerLogWriter(logSink, {
118940
- chatroomId: payload.chatroomId,
118941
- harness: payload.agentHarness,
118942
- pid: spawned.pid
118943
- });
118944
- spawned.onLogLine?.((line) => {
118945
- log4.write(line);
118946
- });
118947
- agentEndPromise = new Promise((resolve5) => {
118948
- resolveAgentEnd = resolve5;
118949
- });
118950
- spawned.onAgentEnd?.(() => {
118951
- if (agentEndSeen)
118952
- return;
118953
- agentEndSeen = true;
118954
- resolveAgentEnd?.();
118955
- });
118956
- await waitForEnhancerJobResolution({
118957
- sessionId,
118958
- chatroomId: payload.chatroomId,
118959
- jobId: payload.jobId,
118960
- wsClient: wsClient2,
118961
- log: log4,
118962
- onAssistantText: spawned.onAssistantText ? (cb) => spawned.onAssistantText?.(cb) : undefined,
118963
- onAgentEnd: spawned.onAgentEnd ? (cb) => spawned.onAgentEnd?.(cb) : undefined,
118964
- onExit: (cb) => spawned.onExit(() => cb()),
118965
- onSalvageComplete: async (content) => {
118966
- await backend2.mutation(api.web.enhancer.index.complete, {
118967
- sessionId,
118968
- chatroomId: payload.chatroomId,
118969
- jobId: payload.jobId,
118970
- enhancedContent: content
118971
- });
118972
- },
118973
- onFailure: async (error51, forceTerminal) => {
118974
- await backend2.mutation(api.web.enhancer.index.recordAttemptFailure, {
118975
- sessionId,
118976
- chatroomId: payload.chatroomId,
118977
- jobId: payload.jobId,
118978
- error: error51,
118979
- ...forceTerminal ? { forceTerminal: true } : {}
118980
- });
118981
- },
118982
- signal: abortController.signal
118983
- });
118984
- if (!abortController.signal.aborted)
118985
- log4.write(`completed job=${jobId}`);
118986
- } catch (err) {
118987
- log4.write(`error: ${err instanceof Error ? err.message : String(err)}`);
118988
- if (claimed) {
118989
- await backend2.mutation(api.web.enhancer.index.recordAttemptFailure, {
118990
- sessionId,
118991
- chatroomId,
118992
- jobId,
118993
- error: err instanceof Error ? err.message : String(err)
118994
- });
118995
- }
118996
- } finally {
118997
- inFlight2.delete(job.jobId);
118998
- if (spawnHandle)
118999
- unregisterEnhancerSpawn(spawnHandle);
119000
- if (spawnResult && service4) {
119001
- if (!agentEndSeen)
119002
- await Promise.race([
119003
- agentEndPromise,
119004
- new Promise((resolve5) => setTimeout(resolve5, ENHANCER_AGENT_END_GRACE_MS))
119005
- ]);
119006
- try {
119007
- await service4.stop(spawnResult.pid);
119008
- } catch {}
119009
- }
119010
- }
119011
- }
119012
- function processEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, jobs, inFlight2, logSink) {
119013
- for (const job of jobs ?? []) {
119014
- processEnhancerJobForSpawn(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, job, inFlight2, logSink);
119015
- }
119016
- }
119017
- async function drainPendingEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, inFlight2, logSink) {
119018
- const jobs = await backend2.query(api.daemon.enhancer.index.pendingForMachine, {
119019
- sessionId,
119020
- machineId
119021
- });
119022
- if (!jobs?.length)
119023
- return;
119024
- processEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, jobs, inFlight2, logSink);
119025
- }
119026
- function startEnhancerJobSubscriber(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, logSink) {
119027
- const inFlight2 = new Set;
119028
- registerEnhancerInboundHandler(async () => {
119029
- await drainPendingEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, inFlight2, logSink);
119030
- });
119031
- setEnhancerDrainHandler(() => drainPendingEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, inFlight2, logSink));
119032
- return {
119033
- stop: () => {
119034
- unregisterEnhancerInboundHandler();
119035
- clearEnhancerDrainHandlerForTests();
119036
- },
119037
- drainPendingEnhancerJobs: () => drainPendingEnhancerJobs(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, inFlight2, logSink)
119038
- };
119039
- }
119040
- var init_job_subscriber = __esm(() => {
119041
- init_enhancer_log();
119042
- init_enhancer_spawn_registry();
119043
- init_wait_for_enhancer_job();
119044
- init_api3();
119045
- init_service_interfaces();
119046
- });
119047
-
119048
- // src/daemon/entry/enhancer/start-subscriptions.ts
119049
- function startEnhancerSubscriptions(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, logSink) {
119050
- return startEnhancerJobSubscriber(sessionId, machineId, convexUrl, wsClient2, backend2, agentServices, logSink);
119051
- }
119052
- var init_start_subscriptions2 = __esm(() => {
119053
- init_job_subscriber();
119054
- });
119055
-
119056
- // src/daemon/entry/file-inbound-registry.ts
119057
- function registerFileInboundHandler(h) {
119058
- handler4 = h;
119059
- }
119060
- function unregisterFileInboundHandler() {
119061
- handler4 = undefined;
119062
- }
119063
- async function dispatchFileInboundEvent(event) {
119064
- if (handler4)
119065
- await handler4(event);
119066
- }
119067
- var handler4;
119068
-
119069
119028
  // src/daemon/entry/files/file-content-classifier.ts
119070
119029
  function extensionOf(path3) {
119071
119030
  const lastDot = path3.lastIndexOf(".");
@@ -119184,7 +119143,7 @@ var init_assert_registered_working_dir = __esm(() => {
119184
119143
 
119185
119144
  // src/infrastructure/services/workspace/workspace-path-security.ts
119186
119145
  import { realpath } from "node:fs/promises";
119187
- import { dirname as dirname11, isAbsolute, relative as relative2, resolve as resolve5, sep } from "node:path";
119146
+ import { dirname as dirname12, isAbsolute, relative as relative2, resolve as resolve5, sep } from "node:path";
119188
119147
  import { gunzipSync } from "node:zlib";
119189
119148
  function validateRelativePathSegments(filePath) {
119190
119149
  if (filePath.includes("\x00"))
@@ -119212,7 +119171,7 @@ function assertContained(workspaceRoot, targetPath) {
119212
119171
  return { ok: true };
119213
119172
  }
119214
119173
  async function resolveNonExistentTarget(workspaceRoot, candidate) {
119215
- let current = dirname11(candidate);
119174
+ let current = dirname12(candidate);
119216
119175
  for (;; ) {
119217
119176
  try {
119218
119177
  const ancestorReal = await realpath(current);
@@ -119230,7 +119189,7 @@ async function resolveNonExistentTarget(workspaceRoot, candidate) {
119230
119189
  if (code2 !== "ENOENT") {
119231
119190
  return { ok: false, error: "Invalid file path" };
119232
119191
  }
119233
- const parent = dirname11(current);
119192
+ const parent = dirname12(current);
119234
119193
  if (parent === current) {
119235
119194
  return { ok: false, error: "Path escapes workspace" };
119236
119195
  }
@@ -120034,16 +119993,16 @@ var require_ignore = __commonJS((exports, module) => {
120034
119993
  // src/infrastructure/services/workspace/workspace-ignore.ts
120035
119994
  import { promises as fsPromises } from "node:fs";
120036
119995
  import { readFile as readFile10 } from "node:fs/promises";
120037
- import { homedir as homedir9 } from "node:os";
119996
+ import { homedir as homedir10 } from "node:os";
120038
119997
  import path3 from "node:path";
120039
119998
  function normalizeRelativePath(relativePath) {
120040
119999
  return relativePath.replace(/\\/g, "/").replace(/^\.?\//, "").replace(/\/+$/, "");
120041
120000
  }
120042
120001
  function expandUserPath(filePath) {
120043
120002
  if (filePath === "~")
120044
- return homedir9();
120003
+ return homedir10();
120045
120004
  if (filePath.startsWith("~/"))
120046
- return path3.join(homedir9(), filePath.slice(2));
120005
+ return path3.join(homedir10(), filePath.slice(2));
120047
120006
  return filePath;
120048
120007
  }
120049
120008
  function readExcludesFileFromGitconfig(content) {
@@ -120074,11 +120033,11 @@ async function readGitconfigExcludesFilePath(configPath) {
120074
120033
  return path3.isAbsolute(raw) ? expandUserPath(raw) : path3.resolve(path3.dirname(configPath), raw);
120075
120034
  }
120076
120035
  async function resolveGitExtraExcludeFilePaths(rootDir) {
120077
- const globalConfig2 = process.env.GIT_CONFIG_GLOBAL?.length ? process.env.GIT_CONFIG_GLOBAL : path3.join(homedir9(), ".gitconfig");
120036
+ const globalConfig2 = process.env.GIT_CONFIG_GLOBAL?.length ? process.env.GIT_CONFIG_GLOBAL : path3.join(homedir10(), ".gitconfig");
120078
120037
  const localConfig = path3.join(rootDir, ".git", "config");
120079
120038
  let excludesFile = await readGitconfigExcludesFilePath(localConfig) ?? await readGitconfigExcludesFilePath(globalConfig2);
120080
120039
  if (!excludesFile) {
120081
- excludesFile = path3.join(process.env.XDG_CONFIG_HOME?.length ? process.env.XDG_CONFIG_HOME : path3.join(homedir9(), ".config"), "git", "ignore");
120040
+ excludesFile = path3.join(process.env.XDG_CONFIG_HOME?.length ? process.env.XDG_CONFIG_HOME : path3.join(homedir10(), ".config"), "git", "ignore");
120082
120041
  }
120083
120042
  const candidates = [excludesFile, path3.join(rootDir, ".git", "info", "exclude")];
120084
120043
  const existing = [];
@@ -121214,9 +121173,9 @@ class NodeFsHandler {
121214
121173
  if (this.fsw.closed) {
121215
121174
  return;
121216
121175
  }
121217
- const dirname13 = sp.dirname(file2);
121176
+ const dirname14 = sp.dirname(file2);
121218
121177
  const basename4 = sp.basename(file2);
121219
- const parent = this.fsw._getWatchedDir(dirname13);
121178
+ const parent = this.fsw._getWatchedDir(dirname14);
121220
121179
  let prevStats = stats;
121221
121180
  if (parent.has(basename4))
121222
121181
  return;
@@ -121243,7 +121202,7 @@ class NodeFsHandler {
121243
121202
  prevStats = newStats2;
121244
121203
  }
121245
121204
  } catch (error51) {
121246
- this.fsw._remove(dirname13, basename4);
121205
+ this.fsw._remove(dirname14, basename4);
121247
121206
  }
121248
121207
  } else if (parent.has(basename4)) {
121249
121208
  const at = newStats.atimeMs;
@@ -122632,8 +122591,8 @@ function diffPathIndexes(previous, next4) {
122632
122591
  // src/infrastructure/services/workspace/workspace-sync-state.ts
122633
122592
  import { createHash as createHash7, randomUUID as randomUUID12 } from "node:crypto";
122634
122593
  import * as fs11 from "node:fs/promises";
122635
- import { homedir as homedir10 } from "node:os";
122636
- import { join as join26 } from "node:path";
122594
+ import { homedir as homedir11 } from "node:os";
122595
+ import { join as join27 } from "node:path";
122637
122596
  function workspaceKeyFor(workingDir) {
122638
122597
  const normalized = normalizeWorkingDirForLookup(workingDir);
122639
122598
  return createHash7("sha256").update(normalized).digest("hex").slice(0, 16);
@@ -122647,10 +122606,10 @@ function buildPathIndex(entries2) {
122647
122606
  }
122648
122607
  function manifestPath(machineId, workingDir) {
122649
122608
  const key = workspaceKeyFor(workingDir);
122650
- return join26(SYNC_STATE_DIR, machineId, key, "manifest.json");
122609
+ return join27(SYNC_STATE_DIR, machineId, key, "manifest.json");
122651
122610
  }
122652
122611
  async function ensureDir(filePath) {
122653
- await fs11.mkdir(join26(filePath, ".."), { recursive: true, mode: 448 });
122612
+ await fs11.mkdir(join27(filePath, ".."), { recursive: true, mode: 448 });
122654
122613
  }
122655
122614
  async function loadWorkspaceSyncManifest(machineId, workingDir) {
122656
122615
  try {
@@ -122710,7 +122669,7 @@ function entriesFromPathIndex(paths) {
122710
122669
  }
122711
122670
  var SYNC_STATE_VERSION = "2", SYNC_STATE_DIR;
122712
122671
  var init_workspace_sync_state = __esm(() => {
122713
- SYNC_STATE_DIR = join26(homedir10(), ".chatroom", "sync-state");
122672
+ SYNC_STATE_DIR = join27(homedir11(), ".chatroom", "sync-state");
122714
122673
  });
122715
122674
 
122716
122675
  // src/infrastructure/services/workspace/workspace-file-tree-coordinator.ts
@@ -123088,12 +123047,12 @@ var init_durable_coalescing_state_schema = __esm(() => {
123088
123047
  });
123089
123048
 
123090
123049
  // src/daemon/infrastructure/outbox/lib/durable-coalescing-state-store.ts
123091
- import { mkdirSync as mkdirSync8 } from "node:fs";
123092
- import { dirname as dirname14 } from "node:path";
123093
- import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
123050
+ import { mkdirSync as mkdirSync9 } from "node:fs";
123051
+ import { dirname as dirname15 } from "node:path";
123052
+ import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
123094
123053
  function openDurableCoalescingStateStore(path8) {
123095
- mkdirSync8(dirname14(path8), { recursive: true });
123096
- const db = new DatabaseSync3(path8);
123054
+ mkdirSync9(dirname15(path8), { recursive: true });
123055
+ const db = new DatabaseSync4(path8);
123097
123056
  for (const sql of DURABLE_COALESCING_STATE_MIGRATIONS)
123098
123057
  db.exec(sql);
123099
123058
  db.prepare("UPDATE coalescing_outbox_entries SET status='pending' WHERE status='in_flight'").run();
@@ -124081,7 +124040,7 @@ var init_file_write_request_priority = __esm(() => {
124081
124040
 
124082
124041
  // src/daemon/entry/files/file-write-fulfillment.ts
124083
124042
  import { access as access4, mkdir as mkdir10, rename as rename4, rm as rm4, writeFile as writeFile10 } from "node:fs/promises";
124084
- import { dirname as dirname15 } from "node:path";
124043
+ import { dirname as dirname16 } from "node:path";
124085
124044
  function isTerminalFileWriteError(errorMessage) {
124086
124045
  const terminalMessages = new Set([
124087
124046
  "Invalid file path",
@@ -124180,7 +124139,7 @@ async function validateWriteOperation(operation, absolutePath) {
124180
124139
  }
124181
124140
  async function writePayloadToDisk(absolutePath, operation, content) {
124182
124141
  if (operation === "create") {
124183
- await mkdir10(dirname15(absolutePath), { recursive: true });
124142
+ await mkdir10(dirname16(absolutePath), { recursive: true });
124184
124143
  await writeFile10(absolutePath, content, { flag: "wx" });
124185
124144
  return;
124186
124145
  }
@@ -124258,7 +124217,7 @@ async function fulfillOneFileWriteRequest(session2, request2) {
124258
124217
  });
124259
124218
  return;
124260
124219
  }
124261
- await mkdir10(dirname15(targetResolved.absolutePath), { recursive: true });
124220
+ await mkdir10(dirname16(targetResolved.absolutePath), { recursive: true });
124262
124221
  await rename4(resolved.absolutePath, targetResolved.absolutePath);
124263
124222
  await completeWriteRequest(session2, claimed._id, claimed.revision, { status: "done" });
124264
124223
  const elapsed4 = Date.now() - startTime;
@@ -125333,16 +125292,16 @@ var init_workspace_list_subscription = __esm(() => {
125333
125292
 
125334
125293
  // src/daemon/entry/workspace-git-inbound-registry.ts
125335
125294
  function registerWorkspaceGitInboundHandler(h) {
125336
- handler5 = h;
125295
+ handler4 = h;
125337
125296
  }
125338
125297
  function unregisterWorkspaceGitInboundHandler() {
125339
- handler5 = undefined;
125298
+ handler4 = undefined;
125340
125299
  }
125341
125300
  async function dispatchWorkspaceGitInboundEvent(event) {
125342
- if (handler5)
125343
- await handler5(event);
125301
+ if (handler4)
125302
+ await handler4(event);
125344
125303
  }
125345
- var handler5;
125304
+ var handler4;
125346
125305
 
125347
125306
  // src/events/lifecycle/on-daemon-shutdown.ts
125348
125307
  var onDaemonShutdownEffect;
@@ -125360,6 +125319,19 @@ var init_on_daemon_shutdown = __esm(() => {
125360
125319
  const activeAgents = agentPm.listActive();
125361
125320
  if (activeAgents.length > 0) {
125362
125321
  console.log(`[${formatTimestamp()}] Stopping ${activeAgents.length} agent(s) locally...`);
125322
+ yield* exports_Effect.promise(async () => {
125323
+ try {
125324
+ const tasksReleased = await session2.backend.mutation(api.daemon.taskStatus.releaseMachineTasks, {
125325
+ sessionId: session2.sessionId,
125326
+ machineId: session2.machineId
125327
+ });
125328
+ if (tasksReleased > 0) {
125329
+ console.log(`[${formatTimestamp()}] Released ${tasksReleased} in-flight task(s) to pending`);
125330
+ }
125331
+ } catch (error51) {
125332
+ console.log(`[${formatTimestamp()}] ⚠️ Failed to release in-flight tasks on shutdown: ${error51 instanceof Error ? error51.message : String(error51)}`);
125333
+ }
125334
+ });
125363
125335
  let totalStopped = 0;
125364
125336
  let totalFailed = 0;
125365
125337
  yield* exports_Effect.all(activeAgents.map(({ chatroomId, role }) => exports_Effect.promise(() => agentPm.stopAgent({ chatroomId, role, reason: "daemon.shutdown" })).pipe(exports_Effect.tap(() => exports_Effect.sync(() => {
@@ -125389,7 +125361,6 @@ function createDaemonRuntime(deps) {
125389
125361
  let workspaceListSubscriptionHandle = null;
125390
125362
  let logObserverSubscriptionHandle = null;
125391
125363
  let agenticQueryWorkerHandle = null;
125392
- let enhancerWorkerHandle = null;
125393
125364
  let taskInboxHandle = null;
125394
125365
  let agentConfigRegistryHandle = null;
125395
125366
  const activeSessions = new Map;
@@ -125430,7 +125401,6 @@ function createDaemonRuntime(deps) {
125430
125401
  taskInboxHandle?.stop();
125431
125402
  logObserverSubscriptionHandle?.stop();
125432
125403
  agenticQueryWorkerHandle?.stop();
125433
- enhancerWorkerHandle?.stop();
125434
125404
  };
125435
125405
  const shutdown = async () => {
125436
125406
  if (isShuttingDown)
@@ -125548,7 +125518,6 @@ function createDaemonRuntime(deps) {
125548
125518
  backend: session2.backend,
125549
125519
  convexUrl: session2.convexUrl
125550
125520
  }, activeSessions, harnesses);
125551
- enhancerWorkerHandle = startEnhancerSubscriptions(session2.sessionId, session2.machineId, session2.convexUrl, deps.wsClient, session2.backend, session2.agentServices, getActiveLogSink());
125552
125521
  console.log(`
125553
125522
  Listening for commands...`);
125554
125523
  console.log(`Press Ctrl+C to stop
@@ -125585,7 +125554,6 @@ var init_daemon_runtime = __esm(() => {
125585
125554
  init_start_subscriptions();
125586
125555
  init_command_dispatch();
125587
125556
  init_daemon_services();
125588
- init_start_subscriptions2();
125589
125557
  init_file_content_subscription();
125590
125558
  init_file_tree_subscription();
125591
125559
  init_file_write_subscription();
@@ -125593,7 +125561,6 @@ var init_daemon_runtime = __esm(() => {
125593
125561
  init_orphan_tracker();
125594
125562
  init_command_run_subscription();
125595
125563
  init_log_observer_sync();
125596
- init_init_daemon();
125597
125564
  init_task_inbox_runtime();
125598
125565
  init_git_subscription();
125599
125566
  init_api3();
@@ -125636,21 +125603,6 @@ function createCommandRouterDeps() {
125636
125603
  }
125637
125604
  var init_command_bridge = () => {};
125638
125605
 
125639
- // src/daemon/domain/usecase/process-enhancer-job.ts
125640
- async function processEnhancerJobInbound(deps, event) {
125641
- await deps.dispatchInbound(event);
125642
- }
125643
-
125644
- // src/daemon/entry/bridge/enhancer-bridge.ts
125645
- function createEnhancerRouterDeps() {
125646
- return {
125647
- deliverInbound: async (event) => {
125648
- await processEnhancerJobInbound({ dispatchInbound: dispatchEnhancerInboundEvent }, event);
125649
- }
125650
- };
125651
- }
125652
- var init_enhancer_bridge = () => {};
125653
-
125654
125606
  // src/daemon/domain/usecase/fulfill-file-content-request.ts
125655
125607
  async function fulfillFileContentRequest(deps, event) {
125656
125608
  await deps.dispatchInbound(event);
@@ -125720,14 +125672,12 @@ function createDefaultEventRouterDeps() {
125720
125672
  command: createCommandRouterDeps(),
125721
125673
  workspaceGit: createWorkspaceGitRouterDeps(),
125722
125674
  file: createFileRouterDeps(),
125723
- agenticQuery: createAgenticQueryRouterDeps(),
125724
- enhancer: createEnhancerRouterDeps()
125675
+ agenticQuery: createAgenticQueryRouterDeps()
125725
125676
  };
125726
125677
  }
125727
125678
  var init_default_router_deps = __esm(() => {
125728
125679
  init_agentic_query_bridge();
125729
125680
  init_command_bridge();
125730
- init_enhancer_bridge();
125731
125681
  init_file_bridge();
125732
125682
  init_workspace_git_bridge();
125733
125683
  });
@@ -126008,7 +125958,6 @@ async function executeChatroomStopCommand(args2) {
126008
125958
  args2.apm.markStopIntent(args2.chatroomId, args2.role, "user.stop", undefined, args2.workingDir);
126009
125959
  else if (!args2.workingDir) {
126010
125960
  args2.apm.markChatroomStopIntent(args2.chatroomId, "user.stop");
126011
- await abortEnhancerSpawnsForChatroom(args2.chatroomId);
126012
125961
  }
126013
125962
  const confirmedDeps = args2.apm.getConfirmedStopAdapterDeps();
126014
125963
  const targets = await args2.apm.discoverStopTargets(args2.chatroomId);
@@ -126041,15 +125990,14 @@ async function executeChatroomStopCommand(args2) {
126041
125990
  }
126042
125991
  var init_execute_chatroom_stop_command = __esm(() => {
126043
125992
  init_reliability();
126044
- init_enhancer_spawn_registry();
126045
125993
  init_agent_process_service2();
126046
125994
  });
126047
125995
 
126048
125996
  // src/daemon/entry/persistence-path.ts
126049
- import { homedir as homedir11 } from "node:os";
126050
- import { join as join27 } from "node:path";
125997
+ import { homedir as homedir12 } from "node:os";
125998
+ import { join as join28 } from "node:path";
126051
125999
  function resolvePersistenceDbPath(machineId) {
126052
- return join27(homedir11(), ".chatroom", "daemon", machineId, "events.sqlite");
126000
+ return join28(homedir12(), ".chatroom", "daemon", machineId, "events.sqlite");
126053
126001
  }
126054
126002
  var init_persistence_path = () => {};
126055
126003
 
@@ -126067,13 +126015,6 @@ async function handleCommandInbound(deps, event) {
126067
126015
  }
126068
126016
  }
126069
126017
 
126070
- // src/daemon/domain/usecase/handle-enhancer-inbound.ts
126071
- async function handleEnhancerInbound(deps, event) {
126072
- if (deps.deliverInbound) {
126073
- await deps.deliverInbound(event);
126074
- }
126075
- }
126076
-
126077
126018
  // src/daemon/domain/usecase/handle-file-inbound.ts
126078
126019
  async function handleFileInbound(deps, event) {
126079
126020
  if (deps.deliverInbound) {
@@ -126109,9 +126050,6 @@ async function routeInboundEvent(deps, event) {
126109
126050
  case "agentic-query.prompt":
126110
126051
  await handleAgenticQueryInbound(deps.agenticQuery, event);
126111
126052
  break;
126112
- case "enhancer.job-assigned":
126113
- await handleEnhancerInbound(deps.enhancer, event);
126114
- break;
126115
126053
  default:
126116
126054
  }
126117
126055
  }
@@ -126219,80 +126157,6 @@ var init_command_run = __esm(() => {
126219
126157
  init_api3();
126220
126158
  });
126221
126159
 
126222
- // src/daemon/infrastructure/convex/subscribers/enhancer-job.ts
126223
- function startEnhancerJobSubscriber2(deps, onEvent) {
126224
- const seen = new Set;
126225
- const watchers = new Map;
126226
- let stopped = false;
126227
- let refreshInFlight = false;
126228
- const onPendingJobs = (jobs) => {
126229
- if (!jobs?.length)
126230
- return;
126231
- for (const job of jobs) {
126232
- if (job == null || typeof job !== "object" || !("jobId" in job))
126233
- continue;
126234
- if (seen.has(job.jobId))
126235
- continue;
126236
- seen.add(job.jobId);
126237
- onEvent({ type: "enhancer.job-assigned", jobId: job.jobId });
126238
- }
126239
- };
126240
- const onError3 = (err) => {
126241
- console.warn(`[daemon] enhancer-job subscriber error: ${err instanceof Error ? err.message : String(err)}`);
126242
- };
126243
- const watchChatroom = (chatroomId) => {
126244
- if (watchers.has(chatroomId) || stopped)
126245
- return;
126246
- const unsub = deps.wsClient.onUpdate(api.daemon.enhancer.index.pendingForChatroom, {
126247
- sessionId: deps.sessionId,
126248
- machineId: deps.machineId,
126249
- chatroomId
126250
- }, onPendingJobs, onError3);
126251
- watchers.set(chatroomId, unsub);
126252
- };
126253
- const refreshChatrooms = async () => {
126254
- if (refreshInFlight)
126255
- return;
126256
- refreshInFlight = true;
126257
- try {
126258
- const workspaces = await deps.wsClient.query(api.workspaces.listWorkspacesForMachine, {
126259
- sessionId: deps.sessionId,
126260
- machineId: deps.machineId
126261
- });
126262
- if (stopped)
126263
- return;
126264
- const roomIds = [
126265
- ...new Set((workspaces ?? []).map((ws) => ws.chatroomId))
126266
- ];
126267
- for (const [roomId, unsub] of watchers) {
126268
- if (!roomIds.includes(roomId)) {
126269
- unsub();
126270
- watchers.delete(roomId);
126271
- }
126272
- }
126273
- for (const roomId of roomIds)
126274
- watchChatroom(roomId);
126275
- } catch (error51) {
126276
- console.warn(`[daemon] enhancer-job room refresh failed: ${error51 instanceof Error ? error51.message : String(error51)}`);
126277
- } finally {
126278
- refreshInFlight = false;
126279
- }
126280
- };
126281
- refreshChatrooms();
126282
- return {
126283
- async stop() {
126284
- stopped = true;
126285
- for (const unsub of watchers.values())
126286
- unsub();
126287
- watchers.clear();
126288
- },
126289
- refreshChatrooms
126290
- };
126291
- }
126292
- var init_enhancer_job = __esm(() => {
126293
- init_api3();
126294
- });
126295
-
126296
126160
  // src/daemon/infrastructure/convex/subscribers/pending-file-request-dedup.ts
126297
126161
  function pendingConvexId(req) {
126298
126162
  if (req._id == null)
@@ -126609,7 +126473,6 @@ function startAllSubscribers(deps) {
126609
126473
  const onEvent = (event) => {
126610
126474
  routeInboundEvent(deps.router, event);
126611
126475
  };
126612
- const enhancerJob = startEnhancerJobSubscriber2(deps, onEvent);
126613
126476
  const gitRequest = startGitRequestSubscriber(deps, onEvent);
126614
126477
  const machineCommands = startMachineCommandInboxSubscriber(deps, async (claimed) => {
126615
126478
  if (claimed.type === "agent.stop") {
@@ -126640,8 +126503,7 @@ function startAllSubscribers(deps) {
126640
126503
  fileContent.stop(),
126641
126504
  fileWrite.stop(),
126642
126505
  agenticQuerySession.stop(),
126643
- agenticQueryPrompt.stop(),
126644
- enhancerJob.stop()
126506
+ agenticQueryPrompt.stop()
126645
126507
  ]);
126646
126508
  }
126647
126509
  };
@@ -126651,7 +126513,6 @@ var init_subscriber_registry = __esm(() => {
126651
126513
  init_agentic_query_prompt();
126652
126514
  init_agentic_query_session();
126653
126515
  init_command_run();
126654
- init_enhancer_job();
126655
126516
  init_file_content_request();
126656
126517
  init_file_tree_release_request();
126657
126518
  init_file_tree_request();
@@ -126700,12 +126561,12 @@ var init_schema = __esm(() => {
126700
126561
  });
126701
126562
 
126702
126563
  // src/daemon/infrastructure/persistence/open-database.ts
126703
- import { mkdirSync as mkdirSync9 } from "node:fs";
126704
- import { dirname as dirname16 } from "node:path";
126705
- import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
126564
+ import { mkdirSync as mkdirSync10 } from "node:fs";
126565
+ import { dirname as dirname17 } from "node:path";
126566
+ import { DatabaseSync as DatabaseSync5 } from "node:sqlite";
126706
126567
  function openDatabase(dbPath) {
126707
- mkdirSync9(dirname16(dbPath), { recursive: true });
126708
- const db = new DatabaseSync4(dbPath);
126568
+ mkdirSync10(dirname17(dbPath), { recursive: true });
126569
+ const db = new DatabaseSync5(dbPath);
126709
126570
  for (const sql of MIGRATIONS2) {
126710
126571
  db.exec(sql);
126711
126572
  }
@@ -134200,19 +134061,19 @@ function routeRequest(req, res, deps) {
134200
134061
 
134201
134062
  // src/daemon/local-web/server/serve-static.ts
134202
134063
  import { existsSync as existsSync10, readFileSync as readFileSync11 } from "node:fs";
134203
- import { dirname as dirname17, join as join28 } from "node:path";
134064
+ import { dirname as dirname18, join as join29 } from "node:path";
134204
134065
  import { fileURLToPath as fileURLToPath7 } from "node:url";
134205
134066
  function clientDistCandidates(here) {
134206
134067
  return [
134207
- join28(here, "client/build"),
134208
- join28(here, "../client/build"),
134209
- join28(here, "../src/daemon/local-web/client/build")
134068
+ join29(here, "client/build"),
134069
+ join29(here, "../client/build"),
134070
+ join29(here, "../src/daemon/local-web/client/build")
134210
134071
  ];
134211
134072
  }
134212
- function resolveClientDistDir(here = dirname17(fileURLToPath7(import.meta.url))) {
134073
+ function resolveClientDistDir(here = dirname18(fileURLToPath7(import.meta.url))) {
134213
134074
  const candidates = clientDistCandidates(here);
134214
134075
  for (const dir of candidates) {
134215
- if (existsSync10(join28(dir, "index.html")))
134076
+ if (existsSync10(join29(dir, "index.html")))
134216
134077
  return dir;
134217
134078
  }
134218
134079
  return candidates[0];
@@ -134225,7 +134086,7 @@ function tryServeStatic(req, res, distDir) {
134225
134086
  return false;
134226
134087
  }
134227
134088
  const safePath = pathname === "/" ? "/index.html" : pathname;
134228
- const filePath = join28(distDir, safePath);
134089
+ const filePath = join29(distDir, safePath);
134229
134090
  if (!filePath.startsWith(distDir)) {
134230
134091
  res.writeHead(403);
134231
134092
  res.end();
@@ -134242,7 +134103,7 @@ function tryServeStatic(req, res, distDir) {
134242
134103
  }
134243
134104
  return true;
134244
134105
  }
134245
- const indexPath = join28(distDir, "index.html");
134106
+ const indexPath = join29(distDir, "index.html");
134246
134107
  if (existsSync10(indexPath)) {
134247
134108
  res.writeHead(200, { "Content-Type": "text/html" });
134248
134109
  if (req.method === "HEAD") {
@@ -134291,6 +134152,14 @@ function normalizeError(err) {
134291
134152
  details: err.flatten()
134292
134153
  };
134293
134154
  }
134155
+ if (err instanceof ConvexError) {
134156
+ const data = err.data;
134157
+ return {
134158
+ code: data.code ?? "internal_error",
134159
+ message: data.message ?? err.message,
134160
+ details: err.data
134161
+ };
134162
+ }
134294
134163
  if (err instanceof Error) {
134295
134164
  const code2 = "code" in err && typeof err.code === "string" ? err.code : "internal_error";
134296
134165
  if (code2 === "not_found" || code2 === "validation_error" || code2 === "unauthorized") {
@@ -134301,6 +134170,7 @@ function normalizeError(err) {
134301
134170
  return { code: "internal_error", message: "Unknown error" };
134302
134171
  }
134303
134172
  var init_normalize_error = __esm(() => {
134173
+ init_values();
134304
134174
  init_zod();
134305
134175
  });
134306
134176
 
@@ -134431,6 +134301,16 @@ function extractAck(args2) {
134431
134301
  }
134432
134302
  return { payload: args2[0], ack: undefined };
134433
134303
  }
134304
+ async function resolveDebugStatePayload(deps, payload) {
134305
+ if (!deps.debugState)
134306
+ throw new Error("daemon debug state is not configured");
134307
+ const { chatroomId } = debugStateInputSchema.parse(payload ?? {});
134308
+ const data = await deps.debugState(chatroomId);
134309
+ if (data && typeof data === "object" && deps.cliGateway) {
134310
+ data.cliGateway = deps.cliGateway.debugState();
134311
+ }
134312
+ return data;
134313
+ }
134434
134314
  function registerSocketHandlers(io2, deps) {
134435
134315
  const logHistory = deps.logRepo ? createLogHistoryUseCase({ reader: deps.logRepo }) : undefined;
134436
134316
  const eventStreamHistory = deps.logRepo ? createEventStreamHistoryUseCase({ reader: deps.logRepo }) : undefined;
@@ -134450,13 +134330,22 @@ function registerSocketHandlers(io2, deps) {
134450
134330
  callAck(ack, { ok: false, error: normalizeError(err) });
134451
134331
  }
134452
134332
  });
134333
+ socket.on("cli.handoff", async (...args2) => {
134334
+ const { payload, ack } = extractAck(args2);
134335
+ try {
134336
+ if (!deps.cliGateway)
134337
+ throw new Error("CLI gateway is not configured");
134338
+ const data = await deps.cliGateway.handoff(payload);
134339
+ callAck(ack, { ok: true, data });
134340
+ } catch (err) {
134341
+ callAck(ack, { ok: false, error: normalizeError(err) });
134342
+ }
134343
+ });
134453
134344
  socket.on("daemon.debug.state", async (...args2) => {
134454
134345
  const { payload, ack } = extractAck(args2);
134455
134346
  try {
134456
- if (!deps.debugState)
134457
- throw new Error("daemon debug state is not configured");
134458
- const { chatroomId } = debugStateInputSchema.parse(payload ?? {});
134459
- callAck(ack, { ok: true, data: await deps.debugState(chatroomId) });
134347
+ const data = await resolveDebugStatePayload(deps, payload);
134348
+ callAck(ack, { ok: true, data });
134460
134349
  } catch (err) {
134461
134350
  callAck(ack, { ok: false, error: normalizeError(err) });
134462
134351
  }
@@ -134640,7 +134529,8 @@ async function startLocalWebServer(config4, deps = {}) {
134640
134529
  eventStreamHub: deps.eventStreamHub,
134641
134530
  backend: deps.backend,
134642
134531
  sessionId: deps.sessionId,
134643
- debugState: deps.debugState
134532
+ debugState: deps.debugState,
134533
+ cliGateway: deps.cliGateway
134644
134534
  });
134645
134535
  return {
134646
134536
  port: boundPort,
@@ -134696,6 +134586,80 @@ function createLogStreamHub() {
134696
134586
  };
134697
134587
  }
134698
134588
 
134589
+ // src/daemon/services/cli-gateway-service/index.ts
134590
+ function createCliGatewayService(deps) {
134591
+ const logs2 = [];
134592
+ const writeLog = (message) => {
134593
+ logs2.push({ timestamp: Date.now(), message });
134594
+ if (logs2.length > 100)
134595
+ logs2.shift();
134596
+ (deps.log ?? console.log)(`[CliGateway:${message}]`);
134597
+ };
134598
+ return {
134599
+ handoff: async (args2) => {
134600
+ writeLog(`request handoff chatroom=${args2.chatroomId} role=${args2.senderRole}`);
134601
+ const taskIdsBeforeCommit = getActiveTaskIdsForRole(deps, args2);
134602
+ const result = await commitHandoffMutation(deps, args2, writeLog);
134603
+ writeLog(`backend result success=${result.success}`);
134604
+ if (result.success) {
134605
+ await syncTaskServiceAfterHandoff(deps, args2, result, taskIdsBeforeCommit, writeLog);
134606
+ }
134607
+ return result;
134608
+ },
134609
+ debugState: () => ({ port: deps.port, logs: [...logs2] })
134610
+ };
134611
+ }
134612
+ function getActiveTaskIdsForRole(deps, args2) {
134613
+ return deps.taskService.listTasksForRole?.(args2.chatroomId, args2.senderRole).filter((task) => task.status === "acknowledged" || task.status === "in_progress").map((task) => task.taskId) ?? [];
134614
+ }
134615
+ async function commitHandoffMutation(deps, args2, writeLog) {
134616
+ try {
134617
+ return await deps.backend.mutation(api.messages.handoff, args2);
134618
+ } catch (error51) {
134619
+ writeLog(`backend failure=${error51 instanceof Error ? error51.message : String(error51)}`);
134620
+ throw error51;
134621
+ }
134622
+ }
134623
+ async function syncTaskServiceAfterHandoff(deps, args2, result, taskIdsBeforeCommit, writeLog) {
134624
+ try {
134625
+ const nextTask = await adoptNextHandoffTask(deps, args2, result);
134626
+ await deps.taskService.recordHandoffOutcome({
134627
+ chatroomId: args2.chatroomId,
134628
+ role: args2.senderRole,
134629
+ targetRole: args2.targetRole,
134630
+ nextTask,
134631
+ taskIds: taskIdsBeforeCommit
134632
+ });
134633
+ writeLog(`task-service updated chatroom=${args2.chatroomId} role=${args2.senderRole}` + (nextTask ? ` adopted=${nextTask.taskId}` : ""));
134634
+ } catch (error51) {
134635
+ writeLog(`post-commit sync failure chatroom=${args2.chatroomId} error=${error51 instanceof Error ? error51.message : String(error51)}`);
134636
+ await recordFallbackHandoffOutcome(deps, args2, writeLog);
134637
+ }
134638
+ }
134639
+ async function adoptNextHandoffTask(deps, args2, result) {
134640
+ if (!result.newTaskId || args2.targetRole.toLowerCase() === "user")
134641
+ return;
134642
+ return await deps.taskService.loadAssignedTaskForAction({
134643
+ chatroomId: args2.chatroomId,
134644
+ role: args2.targetRole,
134645
+ taskId: result.newTaskId
134646
+ }) ?? undefined;
134647
+ }
134648
+ async function recordFallbackHandoffOutcome(deps, args2, writeLog) {
134649
+ try {
134650
+ await deps.taskService.recordHandoffOutcome({
134651
+ chatroomId: args2.chatroomId,
134652
+ role: args2.senderRole,
134653
+ targetRole: args2.targetRole
134654
+ });
134655
+ } catch (cleanupError) {
134656
+ writeLog(`post-commit cleanup failure chatroom=${args2.chatroomId} error=${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`);
134657
+ }
134658
+ }
134659
+ var init_cli_gateway_service = __esm(() => {
134660
+ init_api3();
134661
+ });
134662
+
134699
134663
  // src/daemon/entry/start-daemon.ts
134700
134664
  async function startDaemon() {
134701
134665
  let resolveBoundPort;
@@ -134730,6 +134694,11 @@ async function startDaemon() {
134730
134694
  logEvent: init2.logEvent
134731
134695
  });
134732
134696
  const localWebPort = resolveLocalWebPort();
134697
+ const cliGateway = createCliGatewayService({
134698
+ backend: init2.backend,
134699
+ port: localWebPort,
134700
+ taskService: init2.taskService
134701
+ });
134733
134702
  const debugState = async (chatroomId) => {
134734
134703
  const persisted = await loadDaemonState(init2.machineId);
134735
134704
  const persistedAgents = Object.fromEntries(Object.entries(persisted?.agents ?? {}).filter(([key]) => key.startsWith(`${chatroomId}/`)));
@@ -134787,7 +134756,8 @@ async function startDaemon() {
134787
134756
  eventStreamHub,
134788
134757
  backend: init2.backend,
134789
134758
  sessionId: init2.sessionId,
134790
- debugState
134759
+ debugState,
134760
+ cliGateway
134791
134761
  });
134792
134762
  resolveBoundPort(localWeb.port);
134793
134763
  const subscribers = startAllSubscribers({
@@ -134848,6 +134818,7 @@ var init_start_daemon = __esm(() => {
134848
134818
  init_log_repository();
134849
134819
  init_socket3();
134850
134820
  init_create_local_web_server();
134821
+ init_cli_gateway_service();
134851
134822
  });
134852
134823
 
134853
134824
  // src/commands/machine/daemon-start/index.ts
@@ -135657,10 +135628,10 @@ teamCommand.command("set").description("Set the team preset for a chatroom").req
135657
135628
  const { setTeam: setTeam2 } = await Promise.resolve().then(() => (init_team(), exports_team));
135658
135629
  await setTeam2(options.chatroomId, options.team);
135659
135630
  });
135660
- handoffCommandGroup.command("view-template").description("Print the handoff message template for a role pair").option("--team-id <teamId>", "Team id (solo, duo); defaults to duo").action(async function(options) {
135631
+ handoffCommandGroup.command("view-template").description("Print a handoff message template; omit --next-role to inspect a generic architect or UI/UX engineer template").option("--team-id <teamId>", "Team id (solo, duo); defaults to duo").action(async function(options) {
135661
135632
  const parentOpts = this.parent?.opts() ?? {};
135662
- if (!parentOpts.role || !parentOpts.nextRole) {
135663
- console.error(parentOpts.role ? `error: required option '--next-role <nextRole>' not specified` : `error: required option '--role <role>' not specified`);
135633
+ if (!parentOpts.role) {
135634
+ console.error(`error: required option '--role <role>' not specified`);
135664
135635
  process.exit(1);
135665
135636
  }
135666
135637
  const { printHandoffViewTemplate: printHandoffViewTemplate2 } = await Promise.resolve().then(() => (init_view_template3(), exports_view_template));
@@ -136104,4 +136075,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
136104
136075
  });
136105
136076
  program2.parse();
136106
136077
 
136107
- //# debugId=812FE18B15DD283564756E2164756E21
136078
+ //# debugId=318DFD58C705B11264756E2164756E21