@sema-agent/cli 1.0.2 → 1.0.3

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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/sema.js +572 -101
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/cli",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Sema — your own Claude Code-grade coding agent, in the terminal.",
5
5
  "license": "BUSL-1.1",
6
6
  "bin": {
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "dependencies": {
23
23
  "@sema-agent/sdk": "^0.0.46",
24
- "@sema-agent/server": "^1.185.0"
24
+ "@sema-agent/server": "^1.186.0"
25
25
  },
26
26
  "engines": {
27
27
  "node": ">=18"
package/sema.js CHANGED
@@ -103078,7 +103078,7 @@ var init_ErrorOverview = __esm({
103078
103078
  var import_react9, TerminalSizeContext;
103079
103079
  var init_TerminalSizeContext = __esm({
103080
103080
  "build-src/src/ink/components/TerminalSizeContext.tsx"() {
103081
- import_react9 = __toESM(require_react(), 1);
103081
+ import_react9 = __toESM(require_react());
103082
103082
  TerminalSizeContext = (0, import_react9.createContext)(null);
103083
103083
  }
103084
103084
  });
@@ -162088,6 +162088,13 @@ var init_engineTaskListSync = __esm({
162088
162088
  });
162089
162089
 
162090
162090
  // build-src/src/sema/upstreamBridge.ts
162091
+ function engineTaskNotificationDedupKey(n2) {
162092
+ const taskId = typeof n2.task_id === "string" ? n2.task_id : "";
162093
+ const rawStatus = typeof n2.status === "string" ? n2.status : "completed";
162094
+ const seq = typeof n2.seq === "number" || typeof n2.seq === "string" ? String(n2.seq) : "";
162095
+ const lane = n2.task_type === "external" ? "external:" : "";
162096
+ return `${lane}${taskId}:${rawStatus}${seq ? `:${seq}` : ""}`;
162097
+ }
162091
162098
  function deriveNotificationResidualLines(n2) {
162092
162099
  const out6 = [];
162093
162100
  if (Array.isArray(n2.recentSteps)) {
@@ -163161,8 +163168,7 @@ async function* sdkMessagesToCcEvents(sdk, meta4) {
163161
163168
  const taskId = typeof n2.task_id === "string" ? n2.task_id : void 0;
163162
163169
  if (taskId === void 0) break;
163163
163170
  const rawStatus = typeof n2.status === "string" ? n2.status : "completed";
163164
- const seq = typeof n2.seq === "number" || typeof n2.seq === "string" ? String(n2.seq) : "";
163165
- const dedupKey = `${taskId}:${rawStatus}${seq ? `:${seq}` : ""}`;
163171
+ const dedupKey = engineTaskNotificationDedupKey(n2);
163166
163172
  if (renderedEngineTaskNotifications.has(dedupKey)) break;
163167
163173
  renderedEngineTaskNotifications.add(dedupKey);
163168
163174
  markEngineWorkflowNotified(taskId);
@@ -362347,7 +362353,13 @@ function mapResponse(r) {
362347
362353
  }
362348
362354
  async function dispatchJob(job) {
362349
362355
  let info = await ensureDaemonRunning();
362350
- if (!info) return { ok: false, code: "daemon-unreachable" };
362356
+ if (!info) {
362357
+ return {
362358
+ ok: false,
362359
+ code: "daemon-unreachable",
362360
+ message: "the daemon was spawned but never came up (it likely crashed on boot)"
362361
+ };
362362
+ }
362351
362363
  const nonce = randomUUID17();
362352
362364
  for (let attempt = 0; attempt < 3; attempt++) {
362353
362365
  try {
@@ -363585,7 +363597,15 @@ async function handleBgFlagDaemon(rest, head) {
363585
363597
  } else if (result.code === "stale-short") {
363586
363598
  process.stderr.write("Previous session is still shutting down \u2014 try again in a moment\n");
363587
363599
  } else {
363600
+ const { getDaemonLogPath: getDaemonLogPath2 } = await Promise.resolve().then(() => (init_state2(), state_exports2));
363588
363601
  process.stderr.write("Couldn't reach the daemon (not running)\n");
363602
+ if (result.message) process.stderr.write(` cause: ${result.message}
363603
+ `);
363604
+ process.stderr.write(
363605
+ ` The task was NOT started. Check ${getDaemonLogPath2()} for the daemon's crash log,
363606
+ or run \`sema daemon run\` in a terminal to see the failure live, then retry \`sema --bg\`.
363607
+ `
363608
+ );
363589
363609
  }
363590
363610
  process.exitCode = 1;
363591
363611
  return;
@@ -409348,33 +409368,44 @@ function drainPendingMessages(taskId, getAppState, setAppState) {
409348
409368
  }));
409349
409369
  return drained;
409350
409370
  }
409351
- function enqueueAgentNotification({
409371
+ function agentHasLiveBackgroundChildren(agentId, tasks3) {
409372
+ for (const t2 of Object.values(tasks3)) {
409373
+ if (isLocalAgentTask(t2) && t2.ownerAgentId === agentId && (t2.status === "running" || t2.status === "pending")) {
409374
+ return true;
409375
+ }
409376
+ }
409377
+ return false;
409378
+ }
409379
+ function flushDeferredOwnerNotification(ownerAgentId, setAppState) {
409380
+ if (!ownerAgentId || !deferredOwnerNotifications.has(ownerAgentId)) {
409381
+ return;
409382
+ }
409383
+ let tasksSnapshot;
409384
+ setAppState((prev) => {
409385
+ tasksSnapshot = prev.tasks;
409386
+ return prev;
409387
+ });
409388
+ if (!tasksSnapshot) return;
409389
+ if (agentHasLiveBackgroundChildren(ownerAgentId, tasksSnapshot)) return;
409390
+ const message = deferredOwnerNotifications.get(ownerAgentId);
409391
+ if (!message) return;
409392
+ deferredOwnerNotifications.delete(ownerAgentId);
409393
+ enqueuePendingNotification({
409394
+ value: message,
409395
+ mode: "task-notification"
409396
+ });
409397
+ }
409398
+ function buildAgentNotificationMessage({
409352
409399
  taskId,
409353
409400
  description,
409354
409401
  status: status3,
409355
409402
  error: error52,
409356
- setAppState,
409357
409403
  finalMessage,
409358
409404
  usage,
409359
409405
  toolUseId,
409360
409406
  worktreePath,
409361
409407
  worktreeBranch
409362
409408
  }) {
409363
- let shouldEnqueue = false;
409364
- updateTaskState(taskId, setAppState, (task) => {
409365
- if (task.notified) {
409366
- return task;
409367
- }
409368
- shouldEnqueue = true;
409369
- return {
409370
- ...task,
409371
- notified: true
409372
- };
409373
- });
409374
- if (!shouldEnqueue) {
409375
- return;
409376
- }
409377
- abortSpeculation(setAppState);
409378
409409
  const summary = status3 === "completed" ? `Agent "${description}" completed` : status3 === "failed" ? `Agent "${description}" failed: ${error52 || "Unknown error"}` : `Agent "${description}" was stopped`;
409379
409410
  const outputPath = getTaskOutputPath(taskId);
409380
409411
  const toolUseIdLine = toolUseId ? `
@@ -409385,12 +409416,65 @@ function enqueueAgentNotification({
409385
409416
  <usage><total_tokens>${usage.totalTokens}</total_tokens><tool_uses>${usage.toolUses}</tool_uses><duration_ms>${usage.durationMs}</duration_ms></usage>` : "";
409386
409417
  const worktreeSection = worktreePath ? `
409387
409418
  <${WORKTREE_TAG}><${WORKTREE_PATH_TAG}>${worktreePath}</${WORKTREE_PATH_TAG}>${worktreeBranch ? `<${WORKTREE_BRANCH_TAG}>${worktreeBranch}</${WORKTREE_BRANCH_TAG}>` : ""}</${WORKTREE_TAG}>` : "";
409388
- const message = `<${TASK_NOTIFICATION_TAG}>
409419
+ return `<${TASK_NOTIFICATION_TAG}>
409389
409420
  <${TASK_ID_TAG}>${taskId}</${TASK_ID_TAG}>${toolUseIdLine}
409390
409421
  <${OUTPUT_FILE_TAG}>${outputPath}</${OUTPUT_FILE_TAG}>
409391
409422
  <${STATUS_TAG}>${status3}</${STATUS_TAG}>
409392
- <${SUMMARY_TAG}>${summary}</${SUMMARY_TAG}>${resultSection}${usageSection}${worktreeSection}
409423
+ <${SUMMARY_TAG}>${summary}</${SUMMARY_TAG}>
409424
+ <note>${TASK_NOTIFICATION_NOTE}</note>${resultSection}${usageSection}${worktreeSection}
409393
409425
  </${TASK_NOTIFICATION_TAG}>`;
409426
+ }
409427
+ function enqueueAgentNotification({
409428
+ taskId,
409429
+ description,
409430
+ status: status3,
409431
+ error: error52,
409432
+ setAppState,
409433
+ finalMessage,
409434
+ usage,
409435
+ toolUseId,
409436
+ worktreePath,
409437
+ worktreeBranch
409438
+ }) {
409439
+ let decision = "skip";
409440
+ setAppState((prev) => {
409441
+ const task = prev.tasks?.[taskId];
409442
+ if (!isLocalAgentTask(task)) {
409443
+ return prev;
409444
+ }
409445
+ if (task.notified) {
409446
+ decision = "skip";
409447
+ return prev;
409448
+ }
409449
+ const parkOnChildren = status3 === "completed" && agentHasLiveBackgroundChildren(taskId, prev.tasks);
409450
+ decision = parkOnChildren ? "defer" : "enqueue";
409451
+ return {
409452
+ ...prev,
409453
+ tasks: {
409454
+ ...prev.tasks,
409455
+ [taskId]: { ...task, notified: true }
409456
+ }
409457
+ };
409458
+ });
409459
+ if (decision === "skip") {
409460
+ return;
409461
+ }
409462
+ abortSpeculation(setAppState);
409463
+ const message = buildAgentNotificationMessage({
409464
+ taskId,
409465
+ description,
409466
+ status: status3,
409467
+ error: error52,
409468
+ finalMessage,
409469
+ usage,
409470
+ toolUseId,
409471
+ worktreePath,
409472
+ worktreeBranch
409473
+ });
409474
+ if (decision === "defer") {
409475
+ deferredOwnerNotifications.set(taskId, message);
409476
+ return;
409477
+ }
409394
409478
  enqueuePendingNotification({
409395
409479
  value: message,
409396
409480
  mode: "task-notification"
@@ -409398,11 +409482,13 @@ function enqueueAgentNotification({
409398
409482
  }
409399
409483
  function killAsyncAgent(taskId, setAppState) {
409400
409484
  let killed = false;
409485
+ let ownerAgentId;
409401
409486
  updateTaskState(taskId, setAppState, (task) => {
409402
409487
  if (task.status !== "running") {
409403
409488
  return task;
409404
409489
  }
409405
409490
  killed = true;
409491
+ ownerAgentId = task.ownerAgentId;
409406
409492
  task.abortController?.abort();
409407
409493
  task.unregisterCleanup?.();
409408
409494
  return {
@@ -409417,6 +409503,7 @@ function killAsyncAgent(taskId, setAppState) {
409417
409503
  });
409418
409504
  if (killed) {
409419
409505
  void evictTaskOutput(taskId);
409506
+ flushDeferredOwnerNotification(ownerAgentId, setAppState);
409420
409507
  }
409421
409508
  }
409422
409509
  function killAllRunningAgentTasks(tasks3, setAppState) {
@@ -409494,10 +409581,12 @@ function updateAgentSummary(taskId, summary, setAppState) {
409494
409581
  }
409495
409582
  function completeAgentTask(result, setAppState) {
409496
409583
  const taskId = result.agentId;
409584
+ let ownerAgentId;
409497
409585
  updateTaskState(taskId, setAppState, (task) => {
409498
409586
  if (task.status !== "running") {
409499
409587
  return task;
409500
409588
  }
409589
+ ownerAgentId = task.ownerAgentId;
409501
409590
  task.unregisterCleanup?.();
409502
409591
  return {
409503
409592
  ...task,
@@ -409511,12 +409600,15 @@ function completeAgentTask(result, setAppState) {
409511
409600
  };
409512
409601
  });
409513
409602
  void evictTaskOutput(taskId);
409603
+ flushDeferredOwnerNotification(ownerAgentId, setAppState);
409514
409604
  }
409515
409605
  function failAgentTask(taskId, error52, setAppState) {
409606
+ let ownerAgentId;
409516
409607
  updateTaskState(taskId, setAppState, (task) => {
409517
409608
  if (task.status !== "running") {
409518
409609
  return task;
409519
409610
  }
409611
+ ownerAgentId = task.ownerAgentId;
409520
409612
  task.unregisterCleanup?.();
409521
409613
  return {
409522
409614
  ...task,
@@ -409530,6 +409622,7 @@ function failAgentTask(taskId, error52, setAppState) {
409530
409622
  };
409531
409623
  });
409532
409624
  void evictTaskOutput(taskId);
409625
+ flushDeferredOwnerNotification(ownerAgentId, setAppState);
409533
409626
  }
409534
409627
  function registerAsyncAgent({
409535
409628
  agentId,
@@ -409542,11 +409635,13 @@ function registerAsyncAgent({
409542
409635
  }) {
409543
409636
  void initTaskOutputAsSymlink(agentId, getAgentTranscriptPath(asAgentId(agentId)));
409544
409637
  const abortController = parentAbortController ? createChildAbortController(parentAbortController) : createAbortController();
409638
+ const ownerAgentId = getAgentContext()?.agentId;
409545
409639
  const taskState = {
409546
409640
  ...createTaskStateBase(agentId, "local_agent", description, toolUseId),
409547
409641
  type: "local_agent",
409548
409642
  status: "running",
409549
409643
  agentId,
409644
+ ownerAgentId,
409550
409645
  prompt,
409551
409646
  selectedAgent,
409552
409647
  agentType: selectedAgent.agentType ?? "general-purpose",
@@ -409581,11 +409676,13 @@ function registerAgentForeground({
409581
409676
  const unregisterCleanup2 = registerCleanup(async () => {
409582
409677
  killAsyncAgent(agentId, setAppState);
409583
409678
  });
409679
+ const ownerAgentId = getAgentContext()?.agentId;
409584
409680
  const taskState = {
409585
409681
  ...createTaskStateBase(agentId, "local_agent", description, toolUseId),
409586
409682
  type: "local_agent",
409587
409683
  status: "running",
409588
409684
  agentId,
409685
+ ownerAgentId,
409589
409686
  prompt,
409590
409687
  selectedAgent,
409591
409688
  agentType: selectedAgent.agentType ?? "general-purpose",
@@ -409688,7 +409785,7 @@ function unregisterAgentForeground(taskId, setAppState) {
409688
409785
  });
409689
409786
  cleanupFn?.();
409690
409787
  }
409691
- var MAX_RECENT_ACTIVITIES2, LocalAgentTask, backgroundSignalResolvers;
409788
+ var MAX_RECENT_ACTIVITIES2, TASK_NOTIFICATION_NOTE, deferredOwnerNotifications, LocalAgentTask, backgroundSignalResolvers;
409692
409789
  var init_LocalAgentTask = __esm({
409693
409790
  "build-src/src/tasks/LocalAgentTask/LocalAgentTask.tsx"() {
409694
409791
  init_state();
@@ -409699,6 +409796,7 @@ var init_LocalAgentTask = __esm({
409699
409796
  init_SyntheticOutputTool();
409700
409797
  init_ids();
409701
409798
  init_abortController();
409799
+ init_agentContext();
409702
409800
  init_cleanupRegistry();
409703
409801
  init_collapseReadSearch();
409704
409802
  init_messageQueueManager();
@@ -409707,6 +409805,8 @@ var init_LocalAgentTask = __esm({
409707
409805
  init_framework();
409708
409806
  init_sdkProgress();
409709
409807
  MAX_RECENT_ACTIVITIES2 = 5;
409808
+ TASK_NOTIFICATION_NOTE = "A task-notification fires each time this agent stops with no live background children of its own. The user can send it another message and resume it, so the same task-id may notify more than once.";
409809
+ deferredOwnerNotifications = /* @__PURE__ */ new Map();
409710
409810
  LocalAgentTask = {
409711
409811
  name: "LocalAgentTask",
409712
409812
  type: "local_agent",
@@ -428073,22 +428173,28 @@ __export(engineLifecycleManager_exports, {
428073
428173
  getServiceAuthToken: () => getServiceAuthToken,
428074
428174
  hasManageableLocalEngine: () => hasManageableLocalEngine,
428075
428175
  initEngineLifecycle: () => initEngineLifecycle,
428176
+ installEngineExitTeardown: () => installEngineExitTeardown,
428076
428177
  isLocalEngineActive: () => isLocalEngineActive,
428077
428178
  isProcessAlive: () => isProcessAlive,
428179
+ isShellHostDead: () => isShellHostDead,
428078
428180
  pickFreeEnginePort: () => pickFreeEnginePort,
428079
428181
  pinnedEnginePort: () => pinnedEnginePort,
428080
428182
  probeHealth: () => probeHealth,
428081
428183
  probeLocalEngineHealth: () => probeLocalEngineHealth,
428082
428184
  readMemorySyncSettings: () => readMemorySyncSettings,
428083
428185
  readWorkflowSizeGuidelineSetting: () => readWorkflowSizeGuidelineSetting,
428186
+ requestEngineRetryNow: () => requestEngineRetryNow,
428084
428187
  resetDeadRespawnStateForTests: () => resetDeadRespawnStateForTests,
428188
+ resetEngineExitTeardownForTests: () => resetEngineExitTeardownForTests,
428085
428189
  resolveEngineEntry: () => resolveEngineEntry,
428086
428190
  respawnOnConfigDrift: () => respawnOnConfigDrift,
428087
428191
  restartEngine: () => restartEngine,
428088
428192
  runEngineDeadRespawn: () => runEngineDeadRespawn,
428193
+ setLocalEngineActiveForTests: () => setLocalEngineActiveForTests,
428089
428194
  setOnOwnedEngineExit: () => setOnOwnedEngineExit,
428090
428195
  shutdownEngine: () => shutdownEngine,
428091
- startHealthCheckLoop: () => startHealthCheckLoop
428196
+ startHealthCheckLoop: () => startHealthCheckLoop,
428197
+ teardownOwnedEngineSync: () => teardownOwnedEngineSync
428092
428198
  });
428093
428199
  import { spawn as spawn10 } from "child_process";
428094
428200
  import { createServer as createServer7 } from "net";
@@ -428918,12 +429024,28 @@ function resetDeadRespawnStateForTests() {
428918
429024
  recentDeathTimestamps = [];
428919
429025
  cooldownRetryPending = false;
428920
429026
  cooldownRetryLevel = 0;
429027
+ cooldownRetryFire = void 0;
429028
+ cooldownRetryAt = 0;
429029
+ if (cooldownRetryTimer) clearTimeout(cooldownRetryTimer);
429030
+ cooldownRetryTimer = void 0;
428921
429031
  }
428922
429032
  function nextCooldownDelayMs() {
428923
429033
  return Math.min(CRASH_LOOP_COOLDOWN_BASE_MS * 2 ** cooldownRetryLevel, CRASH_LOOP_COOLDOWN_CAP_MS);
428924
429034
  }
429035
+ function stampFailedNextRetryAt() {
429036
+ try {
429037
+ const cur = getEngineRuntimeStatus();
429038
+ if (cur.phase === "failed" && cooldownRetryAt > Date.now()) {
429039
+ setEngineRuntimeStatus({ phase: "failed", detail: cur.detail, nextRetryAt: cooldownRetryAt });
429040
+ }
429041
+ } catch {
429042
+ }
429043
+ }
428925
429044
  function scheduleCooldownRetry(deps2) {
428926
- if (cooldownRetryPending) return;
429045
+ if (cooldownRetryPending) {
429046
+ stampFailedNextRetryAt();
429047
+ return;
429048
+ }
428927
429049
  cooldownRetryPending = true;
428928
429050
  const delay = nextCooldownDelayMs();
428929
429051
  cooldownRetryLevel++;
@@ -428932,18 +429054,41 @@ function scheduleCooldownRetry(deps2) {
428932
429054
  );
428933
429055
  const fire = () => {
428934
429056
  cooldownRetryPending = false;
429057
+ cooldownRetryFire = void 0;
429058
+ cooldownRetryAt = 0;
429059
+ if (cooldownRetryTimer) {
429060
+ clearTimeout(cooldownRetryTimer);
429061
+ cooldownRetryTimer = void 0;
429062
+ }
428935
429063
  recentDeathTimestamps = [];
428936
429064
  void runEngineDeadRespawn(deps2).catch(() => {
428937
429065
  });
428938
429066
  };
429067
+ cooldownRetryFire = fire;
429068
+ cooldownRetryAt = Date.now() + delay;
429069
+ stampFailedNextRetryAt();
428939
429070
  if (deps2.scheduleRetry) {
428940
429071
  deps2.scheduleRetry(fire, delay);
428941
429072
  return;
428942
429073
  }
428943
- const t2 = setTimeout(fire, delay);
428944
- if (typeof t2.unref === "function") t2.unref();
429074
+ cooldownRetryTimer = setTimeout(fire, delay);
429075
+ if (typeof cooldownRetryTimer.unref === "function") cooldownRetryTimer.unref();
429076
+ }
429077
+ function requestEngineRetryNow() {
429078
+ if (!cooldownRetryPending || !cooldownRetryFire) return false;
429079
+ if (deadRespawnInFlight) return false;
429080
+ logForDebugging("[engine-lifecycle] manual retry-now requested \u2014 firing the parked cooldown retry immediately");
429081
+ cooldownRetryFire();
429082
+ return true;
428945
429083
  }
428946
429084
  async function runEngineDeadRespawn(deps2) {
429085
+ if (isShellHostDead()) {
429086
+ logForDebugging(
429087
+ "[engine-lifecycle] host terminal is gone \u2014 refusing engine respawn, shutting down the orphaned shell"
429088
+ );
429089
+ teardownOwnedEngineSync("orphan-respawn-guard");
429090
+ process.exit(129);
429091
+ }
428947
429092
  if (deadRespawnInFlight) return false;
428948
429093
  deadRespawnInFlight = true;
428949
429094
  const now2 = deps2.now ?? Date.now;
@@ -428960,7 +429105,9 @@ async function runEngineDeadRespawn(deps2) {
428960
429105
  );
428961
429106
  setEngineRuntimeStatus({
428962
429107
  phase: "failed",
428963
- detail: `engine crashed ${recentDeathTimestamps.length} times in ${CRASH_LOOP_WINDOW_MS / 6e4} minutes \u2014 auto-restart paused; retrying automatically in ${retryInS}s (or run sema doctor)`
429108
+ // detail 保留「retrying automatically in ~Ns」静态估计(headless bg 的 state.json 只见 detail);
429109
+ // 交互 footer 另有 nextRetryAt 活倒计时(渲染时剥掉这里的静态估计,不双报)。
429110
+ detail: `engine crashed ${recentDeathTimestamps.length} times in ${CRASH_LOOP_WINDOW_MS / 6e4} minutes \u2014 auto-restart paused; retrying automatically in ~${retryInS}s (or run sema doctor)`
428964
429111
  });
428965
429112
  scheduleCooldownRetry(deps2);
428966
429113
  return false;
@@ -428994,6 +429141,138 @@ async function runEngineDeadRespawn(deps2) {
428994
429141
  deadRespawnInFlight = false;
428995
429142
  }
428996
429143
  }
429144
+ function orphanWatchEligible() {
429145
+ if (process.env.SEMA_CODE_SESSION_KIND) return false;
429146
+ if (process.env.SEMA_JOB_DIR) return false;
429147
+ return process.stdin.isTTY === true && process.stdout.isTTY === true;
429148
+ }
429149
+ function isShellHostDead() {
429150
+ if (!orphanWatchEligible()) return false;
429151
+ return !process.stdout.writable || !process.stdin.readable;
429152
+ }
429153
+ function otherLiveSemaSessions() {
429154
+ const dir = join118(getClaudeConfigHomeDir(), "sessions");
429155
+ let files2;
429156
+ try {
429157
+ files2 = readdirSync7(dir);
429158
+ } catch {
429159
+ return 0;
429160
+ }
429161
+ let live = 0;
429162
+ for (const f of files2) {
429163
+ if (!/^\d+\.json$/.test(f)) continue;
429164
+ const pid = Number.parseInt(f.slice(0, -5), 10);
429165
+ if (pid === process.pid || !isProcessAlive(pid)) continue;
429166
+ try {
429167
+ const rec = JSON.parse(readFileSync25(join118(dir, f), "utf8"));
429168
+ if (rec.kind === "daemon") continue;
429169
+ if (rec.startedAt !== void 0 && processStartedAfter(pid, rec.startedAt + 15e3) === true) {
429170
+ continue;
429171
+ }
429172
+ live++;
429173
+ } catch {
429174
+ }
429175
+ }
429176
+ return live;
429177
+ }
429178
+ function pickTeardownTarget() {
429179
+ const inst = teardownGetInstance ? teardownGetInstance() : null;
429180
+ if (inst && inst.ownedByThisShell && isProcessAlive(inst.pid)) {
429181
+ return { pid: inst.pid, why: "owned" };
429182
+ }
429183
+ if (!localEngineActive) return null;
429184
+ const anchor = readPortFile(getEnginePortFilePath());
429185
+ if (!anchor || !isProcessAlive(anchor.pid)) return null;
429186
+ if (anchor.startedAt && processStartedAfter(anchor.pid, anchor.startedAt + 15e3) === true) {
429187
+ return null;
429188
+ }
429189
+ return { pid: anchor.pid, why: "anchor(last-session)" };
429190
+ }
429191
+ function cleanupAnchorForPid(pid) {
429192
+ try {
429193
+ const portPath = getEnginePortFilePath();
429194
+ const current3 = readPortFile(portPath);
429195
+ if (current3 && current3.pid === pid) {
429196
+ unlinkSync6(portPath);
429197
+ removeEngineTokenFile(getClaudeConfigHomeDir());
429198
+ }
429199
+ } catch {
429200
+ }
429201
+ }
429202
+ function teardownOwnedEngineSync(reason) {
429203
+ if (teardownDone) return;
429204
+ teardownDone = true;
429205
+ try {
429206
+ const target = pickTeardownTarget();
429207
+ if (!target) return;
429208
+ const peers = otherLiveSemaSessions();
429209
+ if (peers > 0) {
429210
+ logForDebugging(
429211
+ `[engine-lifecycle] exit teardown (${reason}): ${peers} other live sema session(s) still use the engine \u2014 leaving pid=${target.pid} running`
429212
+ );
429213
+ return;
429214
+ }
429215
+ logForDebugging(
429216
+ `[engine-lifecycle] exit teardown (${reason}): reaping ${target.why} engine pid=${target.pid} (no other live sessions)`
429217
+ );
429218
+ markEngineExitExpected(target.pid);
429219
+ try {
429220
+ process.kill(target.pid, "SIGTERM");
429221
+ } catch {
429222
+ }
429223
+ cleanupAnchorForPid(target.pid);
429224
+ } catch (e) {
429225
+ logForDebugging(`[engine-lifecycle] exit teardown (${reason}) error: ${String(e)}`);
429226
+ }
429227
+ }
429228
+ function resetEngineExitTeardownForTests() {
429229
+ teardownDone = false;
429230
+ }
429231
+ function setLocalEngineActiveForTests(v2) {
429232
+ localEngineActive = v2;
429233
+ }
429234
+ function installEngineExitTeardown(getInstance2) {
429235
+ teardownGetInstance = getInstance2;
429236
+ if (teardownInstalled) return;
429237
+ teardownInstalled = true;
429238
+ process.once("exit", () => teardownOwnedEngineSync("process-exit"));
429239
+ const onSignal = (sig, code2) => {
429240
+ let fired2 = false;
429241
+ process.on(sig, () => {
429242
+ if (fired2) return;
429243
+ fired2 = true;
429244
+ teardownOwnedEngineSync(`signal:${sig}`);
429245
+ setImmediate(() => {
429246
+ try {
429247
+ process.removeAllListeners(sig);
429248
+ process.kill(process.pid, sig);
429249
+ } catch {
429250
+ }
429251
+ setTimeout(() => {
429252
+ process.exit(code2);
429253
+ }, 200);
429254
+ });
429255
+ });
429256
+ };
429257
+ onSignal("SIGTERM", 143);
429258
+ onSignal("SIGHUP", 129);
429259
+ onSignal("SIGINT", 130);
429260
+ if (orphanWatchEligible()) {
429261
+ const pollMs = (() => {
429262
+ const v2 = Number.parseInt(process.env.SEMA_ORPHAN_POLL_MS ?? "", 10);
429263
+ return Number.isInteger(v2) && v2 >= 200 ? v2 : 15e3;
429264
+ })();
429265
+ const orphanTimer = setInterval(() => {
429266
+ if (isShellHostDead()) {
429267
+ clearInterval(orphanTimer);
429268
+ logForDebugging("[engine-lifecycle] TTY revoked (terminal closed) \u2014 teardown + exit");
429269
+ teardownOwnedEngineSync("tty-orphan");
429270
+ process.exit(129);
429271
+ }
429272
+ }, pollMs);
429273
+ if (typeof orphanTimer.unref === "function") orphanTimer.unref();
429274
+ }
429275
+ }
428997
429276
  async function shutdownEngine(instance) {
428998
429277
  if (!instance) return;
428999
429278
  if (!instance.ownedByThisShell) {
@@ -429089,7 +429368,7 @@ async function waitForExit(pid, timeoutMs) {
429089
429368
  }
429090
429369
  return !isProcessAlive(pid);
429091
429370
  }
429092
- var PORT_FILE_VERSION, HEALTH_POLL_INTERVAL_MS, HEALTH_POLL_MAX_ATTEMPTS, HEALTH_FETCH_TIMEOUT_MS, HEARTBEAT_INTERVAL_MS2, HEARTBEAT_MAX_MISSES, SHUTDOWN_GRACE_MS, SHUTDOWN_SECOND_TERM_GRACE_MS, LOCK_OPTS3, WORKFLOW_SIZE_GUIDELINE_VALUES, lastServiceAuthToken, lastInitFailureReason, lastInitFailureDetail, localEngineActive, DERIVED_PORT_PROBE_MAX, expectedExitPids, onOwnedEngineExitCb, deadRespawnInFlight, CRASH_LOOP_WINDOW_MS, CRASH_LOOP_MAX_DEATHS, recentDeathTimestamps, CRASH_LOOP_COOLDOWN_BASE_MS, CRASH_LOOP_COOLDOWN_CAP_MS, cooldownRetryPending, cooldownRetryLevel;
429371
+ var PORT_FILE_VERSION, HEALTH_POLL_INTERVAL_MS, HEALTH_POLL_MAX_ATTEMPTS, HEALTH_FETCH_TIMEOUT_MS, HEARTBEAT_INTERVAL_MS2, HEARTBEAT_MAX_MISSES, SHUTDOWN_GRACE_MS, SHUTDOWN_SECOND_TERM_GRACE_MS, LOCK_OPTS3, WORKFLOW_SIZE_GUIDELINE_VALUES, lastServiceAuthToken, lastInitFailureReason, lastInitFailureDetail, localEngineActive, DERIVED_PORT_PROBE_MAX, expectedExitPids, onOwnedEngineExitCb, deadRespawnInFlight, CRASH_LOOP_WINDOW_MS, CRASH_LOOP_MAX_DEATHS, recentDeathTimestamps, CRASH_LOOP_COOLDOWN_BASE_MS, CRASH_LOOP_COOLDOWN_CAP_MS, cooldownRetryPending, cooldownRetryLevel, cooldownRetryFire, cooldownRetryTimer, cooldownRetryAt, teardownGetInstance, teardownInstalled, teardownDone;
429093
429372
  var init_engineLifecycleManager = __esm({
429094
429373
  "build-src/src/sema/engineLifecycleManager.ts"() {
429095
429374
  init_state2();
@@ -429121,6 +429400,10 @@ var init_engineLifecycleManager = __esm({
429121
429400
  CRASH_LOOP_COOLDOWN_CAP_MS = 10 * 6e4;
429122
429401
  cooldownRetryPending = false;
429123
429402
  cooldownRetryLevel = 0;
429403
+ cooldownRetryAt = 0;
429404
+ teardownGetInstance = null;
429405
+ teardownInstalled = false;
429406
+ teardownDone = false;
429124
429407
  }
429125
429408
  });
429126
429409
 
@@ -430588,6 +430871,13 @@ ${sandboxHint}`;
430588
430871
  return `${API_ERROR_PREFIX}: ${detail} \xB7 the engine is unreachable \u2014 your message did not run; check the engine is up, then send it again.`;
430589
430872
  }
430590
430873
  async function* query(params) {
430874
+ try {
430875
+ if (getEngineRuntimeStatus().phase === "failed") {
430876
+ const { requestEngineRetryNow: requestEngineRetryNow2 } = await Promise.resolve().then(() => (init_engineLifecycleManager(), engineLifecycleManager_exports));
430877
+ requestEngineRetryNow2();
430878
+ }
430879
+ } catch {
430880
+ }
430591
430881
  if (!clientExplicitlySet && !process.env.SEMA_LIVE_BASEURL && !process.env.SEMA_SCENARIO) {
430592
430882
  let why = "engine autostart did not run";
430593
430883
  try {
@@ -447701,7 +447991,7 @@ function Settings({
447701
447991
  var import_react112, import_jsx_runtime190;
447702
447992
  var init_cmd_config = __esm({
447703
447993
  "build-src/src/sema/overrides/cmd-config.tsx"() {
447704
- import_react112 = __toESM(require_react());
447994
+ import_react112 = __toESM(require_react(), 1);
447705
447995
  init_useKeybinding();
447706
447996
  init_useExitOnCtrlCDWithKeybindings();
447707
447997
  init_useTerminalSize();
@@ -447712,7 +448002,7 @@ var init_cmd_config = __esm({
447712
448002
  init_Config();
447713
448003
  init_settings_usage();
447714
448004
  init_Stats();
447715
- import_jsx_runtime190 = __toESM(require_jsx_runtime());
448005
+ import_jsx_runtime190 = __toESM(require_jsx_runtime(), 1);
447716
448006
  }
447717
448007
  });
447718
448008
 
@@ -476546,8 +476836,8 @@ var init_sema_brand = __esm({
476546
476836
  "Slash-command gating wired to the fleet capability handshake"
476547
476837
  ]
476548
476838
  },
476549
- productVersion: "1.0.2",
476550
- announcement: "sema 1.0.2 \u2014 background tasks survive your ssh session, engines stop fighting over ports, and long sessions stay lean"
476839
+ productVersion: "1.0.3",
476840
+ announcement: "sema 1.0.3 \u2014 honest --bg failures with a cause, daemon crash logs you can find, clean engine teardown on every exit path, and background agents you can wake again"
476551
476841
  };
476552
476842
  }
476553
476843
  });
@@ -604439,6 +604729,14 @@ function PromptInputFooterWithFleet(props) {
604439
604729
  useEngineAgentPanelBridge(store.setState);
604440
604730
  const upgrade2 = React151.useSyncExternalStore(subscribeUpgradeState, getUpgradeState);
604441
604731
  const engineRt = React151.useSyncExternalStore(subscribeEngineRuntimeStatus, getEngineRuntimeStatus);
604732
+ const [nowTick, setNowTick] = React151.useState(() => Date.now());
604733
+ const wantCountdown = engineRt.phase === "failed" && typeof engineRt.nextRetryAt === "number";
604734
+ React151.useEffect(() => {
604735
+ if (!wantCountdown) return;
604736
+ const t2 = setInterval(() => setNowTick(Date.now()), 1e3);
604737
+ return () => clearInterval(t2);
604738
+ }, [wantCountdown]);
604739
+ const retryInS = wantCountdown ? Math.max(0, Math.ceil(((engineRt.nextRetryAt ?? 0) - nowTick) / 1e3)) : null;
604442
604740
  return /* @__PURE__ */ (0, import_jsx_runtime450.jsxs)(import_jsx_runtime450.Fragment, { children: [
604443
604741
  /* @__PURE__ */ (0, import_jsx_runtime450.jsx)(PromptInputFooter_default, { ...props }),
604444
604742
  engineRt.phase === "down" ? /* @__PURE__ */ (0, import_jsx_runtime450.jsx)(ThemedText, { color: "warning", italic: true, children: " engine not responding \u2014 restarting\u2026" }) : engineRt.phase === "respawning" ? /* @__PURE__ */ (0, import_jsx_runtime450.jsxs)(ThemedText, { color: "warning", italic: true, children: [
@@ -604449,7 +604747,11 @@ function PromptInputFooterWithFleet(props) {
604449
604747
  ")\u2026"
604450
604748
  ] }) : engineRt.phase === "recovered" ? /* @__PURE__ */ (0, import_jsx_runtime450.jsx)(ThemedText, { dimColor: true, italic: true, children: " engine restarted \u2713 \u2014 press \u2191 then enter to resend the interrupted prompt" }) : engineRt.phase === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime450.jsxs)(ThemedText, { color: "error", italic: true, children: [
604451
604749
  " engine down \u2014 ",
604452
- engineRt.detail ?? "automatic restart failed; run sema doctor or restart sema"
604750
+ (() => {
604751
+ const detail = engineRt.detail ?? "automatic restart failed; run sema doctor or restart sema";
604752
+ return retryInS !== null ? detail.replace(/\s*[—;]?\s*retrying automatically in ~?\d+s\s*/, " ") : detail;
604753
+ })(),
604754
+ retryInS !== null ? ` \xB7 auto-retry in ${retryInS}s \xB7 send a message to retry now` : ""
604453
604755
  ] }) : null,
604454
604756
  upgrade2.phase === "ready" ? /* @__PURE__ */ (0, import_jsx_runtime450.jsx)(ThemedText, { dimColor: true, italic: true, children: " engine update ready \xB7 applies between turns" }) : upgrade2.phase === "applying" ? /* @__PURE__ */ (0, import_jsx_runtime450.jsx)(ThemedText, { dimColor: true, italic: true, children: " updating engine\u2026" }) : upgrade2.phase === "done" ? /* @__PURE__ */ (0, import_jsx_runtime450.jsxs)(ThemedText, { dimColor: true, italic: true, children: [
604455
604757
  " engine updated \u2713",
@@ -609591,10 +609893,12 @@ var init_autoDream = __esm({
609591
609893
  import * as fs14 from "fs/promises";
609592
609894
  import { homedir as homedir46 } from "os";
609593
609895
  import { join as join169 } from "path";
609594
- function getCutoffDate() {
609896
+ function getCleanupPeriodDays() {
609595
609897
  const settings2 = getSettings_DEPRECATED() || {};
609596
- const cleanupPeriodDays = settings2.cleanupPeriodDays ?? DEFAULT_CLEANUP_PERIOD_DAYS;
609597
- const cleanupPeriodMs = cleanupPeriodDays * 24 * 60 * 60 * 1e3;
609898
+ return settings2.cleanupPeriodDays ?? DEFAULT_CLEANUP_PERIOD_DAYS;
609899
+ }
609900
+ function getCutoffDate() {
609901
+ const cleanupPeriodMs = getCleanupPeriodDays() * 24 * 60 * 60 * 1e3;
609598
609902
  return new Date(Date.now() - cleanupPeriodMs);
609599
609903
  }
609600
609904
  function addCleanupResults(a, b3) {
@@ -609679,7 +609983,7 @@ async function tryRmdir(dirPath, fsImpl) {
609679
609983
  }
609680
609984
  async function cleanupOldSessionFiles() {
609681
609985
  const cutoffDate = getCutoffDate();
609682
- const result = { messages: 0, errors: 0 };
609986
+ let result = { messages: 0, errors: 0 };
609683
609987
  const projectsDir = getProjectsDir2();
609684
609988
  const fsImpl = getFsImplementation();
609685
609989
  let projectDirents;
@@ -609712,58 +610016,131 @@ async function cleanupOldSessionFiles() {
609712
610016
  }
609713
610017
  } else if (entry.isDirectory()) {
609714
610018
  const sessionDir = join169(projectDir2, entry.name);
609715
- const toolResultsDir = join169(sessionDir, TOOL_RESULTS_SUBDIR);
609716
- let toolDirs;
610019
+ result = addCleanupResults(
610020
+ result,
610021
+ await cleanupToolResultsUnderSession(sessionDir, cutoffDate, fsImpl)
610022
+ );
610023
+ result = addCleanupResults(
610024
+ result,
610025
+ await cleanupSubagentsUnderSession(sessionDir, cutoffDate, fsImpl)
610026
+ );
610027
+ await tryRmdir(sessionDir, fsImpl);
610028
+ }
610029
+ }
610030
+ await tryRmdir(projectDir2, fsImpl);
610031
+ }
610032
+ return result;
610033
+ }
610034
+ async function cleanupToolResultsUnderSession(sessionDir, cutoffDate, fsImpl) {
610035
+ const result = { messages: 0, errors: 0 };
610036
+ const toolResultsDir = join169(sessionDir, TOOL_RESULTS_SUBDIR);
610037
+ let toolDirs;
610038
+ try {
610039
+ toolDirs = await fsImpl.readdir(toolResultsDir);
610040
+ } catch {
610041
+ return result;
610042
+ }
610043
+ for (const toolEntry of toolDirs) {
610044
+ if (toolEntry.isFile()) {
610045
+ try {
610046
+ if (await unlinkIfOld(
610047
+ join169(toolResultsDir, toolEntry.name),
610048
+ cutoffDate,
610049
+ fsImpl
610050
+ )) {
610051
+ result.messages++;
610052
+ }
610053
+ } catch {
610054
+ result.errors++;
610055
+ }
610056
+ } else if (toolEntry.isDirectory()) {
610057
+ const toolDirPath = join169(toolResultsDir, toolEntry.name);
610058
+ let toolFiles;
610059
+ try {
610060
+ toolFiles = await fsImpl.readdir(toolDirPath);
610061
+ } catch {
610062
+ continue;
610063
+ }
610064
+ for (const tf of toolFiles) {
610065
+ if (!tf.isFile()) continue;
609717
610066
  try {
609718
- toolDirs = await fsImpl.readdir(toolResultsDir);
610067
+ if (await unlinkIfOld(join169(toolDirPath, tf.name), cutoffDate, fsImpl)) {
610068
+ result.messages++;
610069
+ }
609719
610070
  } catch {
609720
- await tryRmdir(sessionDir, fsImpl);
609721
- continue;
610071
+ result.errors++;
610072
+ }
610073
+ }
610074
+ await tryRmdir(toolDirPath, fsImpl);
610075
+ }
610076
+ }
610077
+ await tryRmdir(toolResultsDir, fsImpl);
610078
+ return result;
610079
+ }
610080
+ async function cleanupSubagentsUnderSession(sessionDir, cutoffDate, fsImpl) {
610081
+ const result = { messages: 0, errors: 0 };
610082
+ const subagentsDir = join169(sessionDir, "subagents");
610083
+ let entries;
610084
+ try {
610085
+ entries = await fsImpl.readdir(subagentsDir);
610086
+ } catch {
610087
+ return result;
610088
+ }
610089
+ for (const entry of entries) {
610090
+ if (entry.isFile()) {
610091
+ try {
610092
+ if (await unlinkIfOld(join169(subagentsDir, entry.name), cutoffDate, fsImpl)) {
610093
+ result.messages++;
609722
610094
  }
609723
- for (const toolEntry of toolDirs) {
609724
- if (toolEntry.isFile()) {
610095
+ } catch {
610096
+ result.errors++;
610097
+ }
610098
+ } else if (entry.isDirectory()) {
610099
+ const subDir = join169(subagentsDir, entry.name);
610100
+ let subEntries;
610101
+ try {
610102
+ subEntries = await fsImpl.readdir(subDir);
610103
+ } catch {
610104
+ continue;
610105
+ }
610106
+ for (const subEntry of subEntries) {
610107
+ if (subEntry.isFile()) {
610108
+ try {
610109
+ if (await unlinkIfOld(
610110
+ join169(subDir, subEntry.name),
610111
+ cutoffDate,
610112
+ fsImpl
610113
+ )) {
610114
+ result.messages++;
610115
+ }
610116
+ } catch {
610117
+ result.errors++;
610118
+ }
610119
+ } else if (subEntry.isDirectory()) {
610120
+ const runDir = join169(subDir, subEntry.name);
610121
+ let runFiles;
610122
+ try {
610123
+ runFiles = await fsImpl.readdir(runDir);
610124
+ } catch {
610125
+ continue;
610126
+ }
610127
+ for (const rf of runFiles) {
610128
+ if (!rf.isFile()) continue;
609725
610129
  try {
609726
- if (await unlinkIfOld(
609727
- join169(toolResultsDir, toolEntry.name),
609728
- cutoffDate,
609729
- fsImpl
609730
- )) {
610130
+ if (await unlinkIfOld(join169(runDir, rf.name), cutoffDate, fsImpl)) {
609731
610131
  result.messages++;
609732
610132
  }
609733
610133
  } catch {
609734
610134
  result.errors++;
609735
610135
  }
609736
- } else if (toolEntry.isDirectory()) {
609737
- const toolDirPath = join169(toolResultsDir, toolEntry.name);
609738
- let toolFiles;
609739
- try {
609740
- toolFiles = await fsImpl.readdir(toolDirPath);
609741
- } catch {
609742
- continue;
609743
- }
609744
- for (const tf of toolFiles) {
609745
- if (!tf.isFile()) continue;
609746
- try {
609747
- if (await unlinkIfOld(
609748
- join169(toolDirPath, tf.name),
609749
- cutoffDate,
609750
- fsImpl
609751
- )) {
609752
- result.messages++;
609753
- }
609754
- } catch {
609755
- result.errors++;
609756
- }
609757
- }
609758
- await tryRmdir(toolDirPath, fsImpl);
609759
610136
  }
610137
+ await tryRmdir(runDir, fsImpl);
609760
610138
  }
609761
- await tryRmdir(toolResultsDir, fsImpl);
609762
- await tryRmdir(sessionDir, fsImpl);
609763
610139
  }
610140
+ await tryRmdir(subDir, fsImpl);
609764
610141
  }
609765
- await tryRmdir(projectDir2, fsImpl);
609766
610142
  }
610143
+ await tryRmdir(subagentsDir, fsImpl);
609767
610144
  return result;
609768
610145
  }
609769
610146
  async function cleanupSingleDirectory(dirPath, extension2, removeEmptyDir = true) {
@@ -609862,6 +610239,36 @@ async function cleanupOldSessionEnvDirs() {
609862
610239
  }
609863
610240
  return result;
609864
610241
  }
610242
+ async function cleanupOldJobDirs() {
610243
+ const result = { messages: 0, errors: 0 };
610244
+ const fsImpl = getFsImplementation();
610245
+ const periodDays = JOB_GC_PERIOD_DAYS ?? getCleanupPeriodDays();
610246
+ const cutoffDate = new Date(Date.now() - periodDays * 24 * 60 * 60 * 1e3);
610247
+ let shorts;
610248
+ try {
610249
+ shorts = await listJobShorts();
610250
+ } catch {
610251
+ return result;
610252
+ }
610253
+ for (const short of shorts) {
610254
+ try {
610255
+ const state3 = await readJobState(short);
610256
+ if (!state3) continue;
610257
+ if (!isTerminalJobState(state3.state)) continue;
610258
+ if (isJobWorkerAlive(state3)) continue;
610259
+ const stats3 = await fsImpl.stat(getJobStatePath(short));
610260
+ if (stats3.mtime >= cutoffDate) continue;
610261
+ await fsImpl.rm(getJobDir(short), { recursive: true, force: true });
610262
+ logForDebugging(
610263
+ `job GC: removed terminal job dir ${short} (state=${state3.state}, mtime=${stats3.mtime.toISOString()} < ${cutoffDate.toISOString()})`
610264
+ );
610265
+ result.messages++;
610266
+ } catch {
610267
+ result.errors++;
610268
+ }
610269
+ }
610270
+ return result;
610271
+ }
609865
610272
  async function cleanupOldDebugLogs() {
609866
610273
  const cutoffDate = getCutoffDate();
609867
610274
  const result = { messages: 0, errors: 0 };
@@ -610003,6 +610410,7 @@ async function cleanupOldMessageFilesInBackground() {
610003
610410
  await cleanupOldPlanFiles();
610004
610411
  await cleanupOldFileHistoryBackups();
610005
610412
  await cleanupOldSessionEnvDirs();
610413
+ await cleanupOldJobDirs();
610006
610414
  await cleanupOldDebugLogs();
610007
610415
  await cleanupOldImageCaches();
610008
610416
  await cleanupOldPastes(getCutoffDate());
@@ -610014,10 +610422,11 @@ async function cleanupOldMessageFilesInBackground() {
610014
610422
  await cleanupNpmCacheForAnthropicPackages();
610015
610423
  }
610016
610424
  }
610017
- var DEFAULT_CLEANUP_PERIOD_DAYS, ONE_DAY_MS2;
610425
+ var DEFAULT_CLEANUP_PERIOD_DAYS, JOB_GC_PERIOD_DAYS, ONE_DAY_MS2;
610018
610426
  var init_cleanup = __esm({
610019
610427
  "build-src/src/utils/cleanup.ts"() {
610020
610428
  init_analytics_index();
610429
+ init_state2();
610021
610430
  init_cachePaths();
610022
610431
  init_debug();
610023
610432
  init_envUtils();
@@ -610033,6 +610442,7 @@ var init_cleanup = __esm({
610033
610442
  init_toolResultStorage();
610034
610443
  init_worktree();
610035
610444
  DEFAULT_CLEANUP_PERIOD_DAYS = 30;
610445
+ JOB_GC_PERIOD_DAYS = null;
610036
610446
  ONE_DAY_MS2 = 24 * 60 * 60 * 1e3;
610037
610447
  }
610038
610448
  });
@@ -614645,8 +615055,8 @@ async function processResumedConversation(result, opts, context3) {
614645
615055
  const { bindSessionPersistencePaths: bindSessionPersistencePaths2, reconcileResumeJournal: reconcileResumeJournal2 } = await Promise.resolve().then(() => (init_sessionPersistenceWire(), sessionPersistenceWire_exports));
614646
615056
  bindSessionPersistencePaths2();
614647
615057
  if (process.env.SEMA_RECONCILE_DIAG) {
614648
- const { appendFileSync: appendFileSync7 } = await import("fs");
614649
- appendFileSync7(
615058
+ const { appendFileSync: appendFileSync8 } = await import("fs");
615059
+ appendFileSync8(
614650
615060
  process.env.SEMA_RECONCILE_DIAG,
614651
615061
  `[entry] sid=${sid} baseUrl=${process.env.SEMA_LIVE_BASEURL ?? "UNSET"}
614652
615062
  `
@@ -614656,8 +615066,8 @@ async function processResumedConversation(result, opts, context3) {
614656
615066
  const { cancelEngineRun: cancelEngineRun2 } = await Promise.resolve().then(() => (init_seamQuery(), seamQuery_exports));
614657
615067
  const r = await reconcileResumeJournal2(void 0, (taskId) => cancelEngineRun2(taskId));
614658
615068
  if (process.env.SEMA_RECONCILE_DIAG) {
614659
- const { appendFileSync: appendFileSync7 } = await import("fs");
614660
- appendFileSync7(
615069
+ const { appendFileSync: appendFileSync8 } = await import("fs");
615070
+ appendFileSync8(
614661
615071
  process.env.SEMA_RECONCILE_DIAG,
614662
615072
  `[done] ${r.decision.lastState} -> ${r.recovery}
614663
615073
  `
@@ -614669,8 +615079,8 @@ async function processResumedConversation(result, opts, context3) {
614669
615079
  );
614670
615080
  }
614671
615081
  } else if (process.env.SEMA_RECONCILE_DIAG) {
614672
- const { appendFileSync: appendFileSync7 } = await import("fs");
614673
- appendFileSync7(process.env.SEMA_RECONCILE_DIAG, `[skip] baseUrl UNSET
615082
+ const { appendFileSync: appendFileSync8 } = await import("fs");
615083
+ appendFileSync8(process.env.SEMA_RECONCILE_DIAG, `[skip] baseUrl UNSET
614674
615084
  `);
614675
615085
  }
614676
615086
  } catch (ePw) {
@@ -640016,6 +640426,10 @@ function toLiveRequest(req, capturedSessionId) {
640016
640426
  ;
640017
640427
  out6.forwardSubagentEvents = true;
640018
640428
  }
640429
+ if (out6.retainSubagentSessions === void 0) {
640430
+ ;
640431
+ out6.retainSubagentSessions = true;
640432
+ }
640019
640433
  if (typeof out6.scenario === "string" && SEMA_MOCK_SCENARIO_KEYS.has(out6.scenario)) {
640020
640434
  delete out6.scenario;
640021
640435
  }
@@ -640545,6 +640959,7 @@ __export(seamQueryEngine_exports, {
640545
640959
  makeAdapterQuery: () => makeAdapterQuery,
640546
640960
  makeCanUseTool: () => makeCanUseTool,
640547
640961
  makeHitlCanUseTool: () => makeHitlCanUseTool,
640962
+ printTaskNotificationDedupKey: () => printTaskNotificationDedupKey,
640548
640963
  runStream: () => runStream,
640549
640964
  setQueryEngineClient: () => setQueryEngineClient
640550
640965
  });
@@ -640597,6 +641012,10 @@ function notifiedOnce(key) {
640597
641012
  printNotifiedKeys.add(key);
640598
641013
  return false;
640599
641014
  }
641015
+ function printTaskNotificationDedupKey(n2) {
641016
+ const lane = n2.task_type === "external" ? "external:" : "";
641017
+ return `${lane}${String(n2.task_id)}:${String(n2.status)}${n2.seq !== void 0 ? `:${String(n2.seq)}` : ""}`;
641018
+ }
640600
641019
  async function* ask(params) {
640601
641020
  const activeClient = await resolveClient();
640602
641021
  if (!clientExplicitlySet2 && !process.env.SEMA_LIVE_BASEURL && !process.env.SEMA_SCENARIO) {
@@ -640724,7 +641143,7 @@ async function* ask(params) {
640724
641143
  }
640725
641144
  if (t2 === "task_notification") {
640726
641145
  const n2 = msg.notification;
640727
- if (n2 && typeof n2.task_id === "string" && !notifiedOnce(`${n2.task_id}:${String(n2.status)}${n2.seq !== void 0 ? `:${String(n2.seq)}` : ""}`)) {
641146
+ if (n2 && typeof n2.task_id === "string" && !notifiedOnce(printTaskNotificationDedupKey(n2))) {
640728
641147
  const s = n2.status;
640729
641148
  yield {
640730
641149
  type: "system",
@@ -664464,7 +664883,7 @@ Auth: unix socket -R \u2192 local proxy`, "info");
664464
664883
  pendingHookMessages
664465
664884
  }, renderAndRun);
664466
664885
  }
664467
- }).version("sema 1.0.2", "-v, --version", "Output the version number");
664886
+ }).version("sema 1.0.3", "-v, --version", "Output the version number");
664468
664887
  program2.option("-w, --worktree [name]", "Create a new git worktree for this session (optionally specify a name)");
664469
664888
  program2.option("--tmux", "Create a tmux session for the worktree (requires --worktree). Uses iTerm2 native panes when available; use --tmux=classic for traditional tmux.");
664470
664889
  if (canUserConfigureAdvisor()) {
@@ -670061,7 +670480,7 @@ __export(main_exports2, {
670061
670480
  daemonMain: () => daemonMain
670062
670481
  });
670063
670482
  import { spawn as spawn16 } from "child_process";
670064
- import { createWriteStream as createWriteStream5 } from "fs";
670483
+ import { appendFileSync as appendFileSync7, createWriteStream as createWriteStream5 } from "fs";
670065
670484
  import { chmod as chmod13, mkdir as mkdir50, readFile as readFile58, readdir as readdir33, rename as rename15, stat as stat54, unlink as unlink28 } from "fs/promises";
670066
670485
  import { createConnection as createConnection4, createServer as createServer9 } from "net";
670067
670486
  import { dirname as dirname88, join as join201 } from "path";
@@ -670103,6 +670522,31 @@ function parseOrigin(args) {
670103
670522
  const v2 = i >= 0 ? args[i + 1] : void 0;
670104
670523
  return v2 === "service" ? "service" : "transient";
670105
670524
  }
670525
+ function appendDaemonCrashLine(kind, err8) {
670526
+ const detail = err8 instanceof Error ? err8.stack ?? err8.message : String(err8);
670527
+ const line = `${(/* @__PURE__ */ new Date()).toISOString()} [daemon] FATAL ${kind} (pid ${process.pid}): ${detail}
670528
+ `;
670529
+ try {
670530
+ appendFileSync7(getDaemonLogPath(), line, { mode: 384 });
670531
+ } catch {
670532
+ try {
670533
+ process.stderr.write(line);
670534
+ } catch {
670535
+ }
670536
+ }
670537
+ }
670538
+ function installDaemonCrashHandlers() {
670539
+ if (crashHandlersInstalled) return;
670540
+ crashHandlersInstalled = true;
670541
+ process.on("uncaughtException", (err8) => {
670542
+ appendDaemonCrashLine("uncaughtException", err8);
670543
+ process.exit(1);
670544
+ });
670545
+ process.on("unhandledRejection", (reason) => {
670546
+ appendDaemonCrashLine("unhandledRejection", reason);
670547
+ process.exit(1);
670548
+ });
670549
+ }
670106
670550
  async function daemonMain(args) {
670107
670551
  const cdIdx = args.indexOf("--config-dir");
670108
670552
  if (cdIdx >= 0 && args[cdIdx + 1]) {
@@ -670111,8 +670555,20 @@ async function daemonMain(args) {
670111
670555
  const sub = args[0];
670112
670556
  switch (sub) {
670113
670557
  case "run": {
670558
+ installDaemonCrashHandlers();
670114
670559
  const supervisor = new Supervisor(parseOrigin(args.slice(1)));
670115
- const started = await supervisor.run();
670560
+ let started = false;
670561
+ try {
670562
+ started = await supervisor.run();
670563
+ } catch (e) {
670564
+ appendDaemonCrashLine("boot", e);
670565
+ process.stderr.write(
670566
+ `daemon failed to start \u2014 ${e instanceof Error ? e.message : String(e)} (details: ${getDaemonLogPath()})
670567
+ `
670568
+ );
670569
+ process.exitCode = 1;
670570
+ return;
670571
+ }
670116
670572
  if (!started) return;
670117
670573
  return;
670118
670574
  }
@@ -670182,7 +670638,7 @@ async function daemonMain(args) {
670182
670638
  }
670183
670639
  }
670184
670640
  }
670185
- var IDLE_EXIT_MS, DISPATCH_SCAN_MS, DAEMON_LOG_ROTATE_BYTES, ADOPTED_SWEEP_MS, CRASH_RESPAWN_MAX, CRASH_BACKOFF_BASE_MS, CRASH_BACKOFF_CAP_MS, CRASH_STABLE_UPTIME_MS, CRASH_WINDOW_MS, CRASH_WINDOW_MAX, Supervisor;
670641
+ var IDLE_EXIT_MS, DISPATCH_SCAN_MS, DAEMON_LOG_ROTATE_BYTES, ADOPTED_SWEEP_MS, CRASH_RESPAWN_MAX, CRASH_BACKOFF_BASE_MS, CRASH_BACKOFF_CAP_MS, CRASH_STABLE_UPTIME_MS, CRASH_WINDOW_MS, CRASH_WINDOW_MAX, Supervisor, crashHandlersInstalled;
670186
670642
  var init_main4 = __esm({
670187
670643
  "build-src/src/daemon/main.ts"() {
670188
670644
  init_envUtils();
@@ -670293,17 +670749,25 @@ var init_main4 = __esm({
670293
670749
  }
670294
670750
  await chmod13(sockPath, 384).catch(() => {
670295
670751
  });
670296
- await writeDaemonInfo({
670297
- pid: process.pid,
670298
- procStart: this.procStart,
670299
- writtenAt: Date.now(),
670300
- socketPath: sockPath,
670301
- authToken: this.authToken,
670302
- origin: this.origin
670303
- });
670304
- this.logLine(`[daemon] supervisor up pid=${process.pid} origin=${this.origin}`);
670305
- await this.recoverJobs();
670306
- await mkdir50(join201(getDispatchDir(), "rejected"), { recursive: true, mode: 448 });
670752
+ try {
670753
+ await writeDaemonInfo({
670754
+ pid: process.pid,
670755
+ procStart: this.procStart,
670756
+ writtenAt: Date.now(),
670757
+ socketPath: sockPath,
670758
+ authToken: this.authToken,
670759
+ origin: this.origin
670760
+ });
670761
+ this.logLine(`[daemon] supervisor up pid=${process.pid} origin=${this.origin}`);
670762
+ await this.recoverJobs();
670763
+ await mkdir50(join201(getDispatchDir(), "rejected"), { recursive: true, mode: 448 });
670764
+ } catch (e) {
670765
+ this.server?.close();
670766
+ this.server = null;
670767
+ await unlink28(sockPath).catch(() => {
670768
+ });
670769
+ throw e;
670770
+ }
670307
670771
  const scanTimer = setInterval(() => void this.scanDispatchDir(), DISPATCH_SCAN_MS);
670308
670772
  const idleTimer2 = setInterval(() => this.maybeIdleExit(), 3e4);
670309
670773
  const nonceTimer = setInterval(() => this.seenNonces.clear(), 6e4);
@@ -670958,6 +671422,7 @@ var init_main4 = __esm({
670958
671422
  }
670959
671423
  }
670960
671424
  };
671425
+ crashHandlersInstalled = false;
670961
671426
  }
670962
671427
  });
670963
671428
 
@@ -671851,6 +672316,11 @@ function toWireSettings(eff, cfg) {
671851
672316
  async function launchReplProduction() {
671852
672317
  const { installSemaProcessBackstop: installSemaProcessBackstop2 } = await Promise.resolve().then(() => (init_processBackstop(), processBackstop_exports));
671853
672318
  installSemaProcessBackstop2();
672319
+ try {
672320
+ const { installEngineExitTeardown: installEngineExitTeardown2 } = await Promise.resolve().then(() => (init_engineLifecycleManager(), engineLifecycleManager_exports));
672321
+ installEngineExitTeardown2(() => null);
672322
+ } catch {
672323
+ }
671854
672324
  const { applyFullscreenDefault: applyFullscreenDefault2 } = await Promise.resolve().then(() => (init_fullscreenDefault(), fullscreenDefault_exports));
671855
672325
  const fullscreenAction = applyFullscreenDefault2();
671856
672326
  if (process.env.SEMA_DEBUG) {
@@ -672022,9 +672492,10 @@ async function launchReplProduction() {
672022
672492
  }
672023
672493
  if (!process.env.SEMA_LIVE_BASEURL && process.env.SEMA_TOC_ENGINE !== "0") {
672024
672494
  try {
672025
- const { initEngineLifecycle: initEngineLifecycle2, startHealthCheckLoop: startHealthCheckLoop2, getServiceAuthToken: getServiceAuthToken2, respawnOnConfigDrift: respawnOnConfigDrift2, runEngineDeadRespawn: runEngineDeadRespawn2, setOnOwnedEngineExit: setOnOwnedEngineExit2 } = await Promise.resolve().then(() => (init_engineLifecycleManager(), engineLifecycleManager_exports));
672495
+ const { initEngineLifecycle: initEngineLifecycle2, startHealthCheckLoop: startHealthCheckLoop2, getServiceAuthToken: getServiceAuthToken2, respawnOnConfigDrift: respawnOnConfigDrift2, runEngineDeadRespawn: runEngineDeadRespawn2, setOnOwnedEngineExit: setOnOwnedEngineExit2, installEngineExitTeardown: installEngineExitTeardown2 } = await Promise.resolve().then(() => (init_engineLifecycleManager(), engineLifecycleManager_exports));
672026
672496
  let engineHealthLoop = null;
672027
672497
  let currentEngineInstance = null;
672498
+ installEngineExitTeardown2(() => currentEngineInstance);
672028
672499
  const doEngineRespawn = async (label) => {
672029
672500
  console.error(`[sema] local engine ${label} \u2014 restarting (stop-then-start serialize), session preserved`);
672030
672501
  try {