@trycadence/cli 0.1.18-dev.0 → 0.1.21-dev.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.
Files changed (2) hide show
  1. package/dist/cadence +194 -20
  2. package/package.json +1 -1
package/dist/cadence CHANGED
@@ -1488,6 +1488,7 @@ function createCadenceClient(options = {}) {
1488
1488
  current: (input) => rpc.sessions.current.query(input),
1489
1489
  leases: {
1490
1490
  create: (input) => rpc.sessions.leases.create.mutate(input),
1491
+ renew: (input) => rpc.sessions.leases.renew.mutate(input),
1491
1492
  release: (input) => rpc.sessions.leases.release.mutate(input)
1492
1493
  }
1493
1494
  },
@@ -1520,7 +1521,7 @@ import { createInterface } from "readline/promises";
1520
1521
  // package.json
1521
1522
  var package_default = {
1522
1523
  name: "@trycadence/cli",
1523
- version: "0.1.18-dev.0",
1524
+ version: "0.1.21-dev.0",
1524
1525
  private: false,
1525
1526
  type: "module",
1526
1527
  bin: {
@@ -3308,6 +3309,8 @@ function readAgentSessionCadenceContext(record) {
3308
3309
  ...typeof contextRecord.ticketId === "string" ? { ticketId: contextRecord.ticketId } : {},
3309
3310
  ...typeof contextRecord.sessionId === "string" ? { sessionId: contextRecord.sessionId } : {},
3310
3311
  ...typeof contextRecord.changesetId === "string" ? { changesetId: contextRecord.changesetId } : {},
3312
+ ...typeof contextRecord.leaseId === "string" ? { leaseId: contextRecord.leaseId } : {},
3313
+ ...typeof contextRecord.leaseExpiresAt === "string" ? { leaseExpiresAt: contextRecord.leaseExpiresAt } : {},
3311
3314
  ...typeof contextRecord.capturedAt === "string" ? { capturedAt: contextRecord.capturedAt } : {}
3312
3315
  };
3313
3316
  return cadenceContext.ticketId || cadenceContext.sessionId || cadenceContext.changesetId ? { cadenceContext } : {};
@@ -3355,9 +3358,38 @@ async function writeAgentCheckpointAuditFile(parsed, options, audit) {
3355
3358
  `);
3356
3359
  return filePath;
3357
3360
  }
3361
+ async function readAgentLoopLockHolder(lockPath) {
3362
+ try {
3363
+ const text = await readFile(join(lockPath, "holder.json"), "utf8");
3364
+ const parsed = JSON.parse(text);
3365
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
3366
+ } catch {
3367
+ return null;
3368
+ }
3369
+ }
3370
+ function lockHolderPid(holder) {
3371
+ const pid = holder?.pid;
3372
+ return typeof pid === "number" && Number.isInteger(pid) && pid > 0 ? pid : null;
3373
+ }
3374
+ function processIsRunning(pid) {
3375
+ try {
3376
+ process.kill(pid, 0);
3377
+ return true;
3378
+ } catch (error) {
3379
+ if (error && typeof error === "object" && "code" in error && error.code === "ESRCH") {
3380
+ return false;
3381
+ }
3382
+ return true;
3383
+ }
3384
+ }
3358
3385
  async function removeStaleAgentLoopLock(lockPath) {
3359
3386
  try {
3360
3387
  const lockStats = await stat(lockPath);
3388
+ const holderPid = lockHolderPid(await readAgentLoopLockHolder(lockPath));
3389
+ if (holderPid !== null && !processIsRunning(holderPid)) {
3390
+ await rm(lockPath, { recursive: true, force: true });
3391
+ return true;
3392
+ }
3361
3393
  if (Date.now() - lockStats.mtimeMs < defaultCheckpointWorkerTimeoutMs) {
3362
3394
  return false;
3363
3395
  }
@@ -3501,6 +3533,8 @@ function activeSessionFromCurrent(current) {
3501
3533
  ...sessionId ? { id: sessionId } : {},
3502
3534
  ticketId: leaseRecord.ticketId,
3503
3535
  ...typeof leaseRecord.changesetId === "string" ? { changesetId: leaseRecord.changesetId } : typeof sessionRecord.changesetId === "string" ? { changesetId: sessionRecord.changesetId } : {},
3536
+ ...typeof leaseRecord.id === "string" ? { leaseId: leaseRecord.id } : {},
3537
+ ...typeof leaseRecord.expiresAt === "string" ? { leaseExpiresAt: leaseRecord.expiresAt } : {},
3504
3538
  status: "active"
3505
3539
  };
3506
3540
  }
@@ -3525,6 +3559,8 @@ function cadenceContextFromCurrent(current) {
3525
3559
  const ticketId = typeof activeSession.ticketId === "string" ? activeSession.ticketId : undefined;
3526
3560
  const sessionId = typeof activeSession.id === "string" ? activeSession.id : undefined;
3527
3561
  const changesetId = typeof activeSession.changesetId === "string" ? activeSession.changesetId : undefined;
3562
+ const leaseId = typeof activeSession.leaseId === "string" ? activeSession.leaseId : undefined;
3563
+ const leaseExpiresAt2 = typeof activeSession.leaseExpiresAt === "string" ? activeSession.leaseExpiresAt : undefined;
3528
3564
  if (!ticketId && !sessionId && !changesetId) {
3529
3565
  return;
3530
3566
  }
@@ -3532,9 +3568,41 @@ function cadenceContextFromCurrent(current) {
3532
3568
  ...ticketId ? { ticketId } : {},
3533
3569
  ...sessionId ? { sessionId } : {},
3534
3570
  ...changesetId ? { changesetId } : {},
3571
+ ...leaseId ? { leaseId } : {},
3572
+ ...leaseExpiresAt2 ? { leaseExpiresAt: leaseExpiresAt2 } : {},
3535
3573
  capturedAt: new Date().toISOString()
3536
3574
  };
3537
3575
  }
3576
+ function leaseExpiresAtFromResponse(value) {
3577
+ return value && typeof value === "object" && "expiresAt" in value && typeof value.expiresAt === "string" ? value.expiresAt : undefined;
3578
+ }
3579
+ async function renewAgentSessionLease(client, projectId, context) {
3580
+ if (!context?.leaseId) {
3581
+ return context;
3582
+ }
3583
+ const expiresAt = leaseExpiresAt(defaultLeaseTtlSeconds);
3584
+ const renewed = await client.sessions.leases.renew({
3585
+ projectId,
3586
+ leaseId: context.leaseId,
3587
+ lease: {
3588
+ expiresAt,
3589
+ ...commandMetadata()
3590
+ }
3591
+ });
3592
+ return {
3593
+ ...context,
3594
+ leaseId: objectStringId(renewed) ?? context.leaseId,
3595
+ leaseExpiresAt: leaseExpiresAtFromResponse(renewed) ?? expiresAt,
3596
+ capturedAt: new Date().toISOString()
3597
+ };
3598
+ }
3599
+ async function renewAgentSessionLeaseBestEffort(client, projectId, context) {
3600
+ try {
3601
+ return await renewAgentSessionLease(client, projectId, context);
3602
+ } catch {
3603
+ return context;
3604
+ }
3605
+ }
3538
3606
  async function readCurrentCadenceContext(client, projectId) {
3539
3607
  const current = await client.sessions.current({
3540
3608
  projectId,
@@ -3566,18 +3634,6 @@ function shouldSkipForCooldown(state, cooldownSeconds) {
3566
3634
  const lastCheckpointMs = new Date(state.lastCheckpointAt).getTime();
3567
3635
  return !Number.isNaN(lastCheckpointMs) && Date.now() - lastCheckpointMs < cooldownSeconds * 1000;
3568
3636
  }
3569
- function synthesizeAgentEventFromSession(agentSessionKeyValue, session, options) {
3570
- return {
3571
- source: session.source,
3572
- event: "checkpoint",
3573
- workspacePath: options.cwd ?? process.cwd(),
3574
- occurredAt: new Date().toISOString(),
3575
- agentSessionKey: agentSessionKeyValue,
3576
- ...session.lastAssistantMessage ? { lastAssistantMessage: session.lastAssistantMessage } : {},
3577
- ...session.recentTurns?.length ? { recentTurns: session.recentTurns } : {},
3578
- payloadKeys: []
3579
- };
3580
- }
3581
3637
  var checkpointRouteActions = ["current", "noop", "intake_create", "intake_attach", "switch_existing", "needs_human"];
3582
3638
  var checkpointConfidenceLevels = ["low", "medium", "high"];
3583
3639
  var checkpointSessionActions = ["keep", "handoff", "end", "complete_ticket"];
@@ -4216,6 +4272,9 @@ function buildRoutePrompt(input) {
4216
4272
  "You are generating a compact Cadence dogfood routing plan for an agent-run worker.",
4217
4273
  "The model judges; the Cadence CLI validates and executes. Return JSON only. Do not call tools to mutate Cadence yourself.",
4218
4274
  "Decide where this agent session belongs before checkpoint memory is written.",
4275
+ "First decide whether the latest request is directed at this source agent to perform work, or whether this is a meta/utility task about quoted conversation content.",
4276
+ "If the transcript asks this agent to generate a title, branch name, label, summary, classification, or other metadata for quoted user text, treat the quoted work as reference material, not active work for this agent.",
4277
+ "For those meta/utility cases, return route.action noop with entries [] even when the quoted text describes durable repo work.",
4219
4278
  "Do not include raw diffs, raw transcripts, terminal logs, tool streams, secrets, file contents, or model reasoning in server-bound fields.",
4220
4279
  'Return this sparse JSON shape: {"summary":"short routing summary","sessionTitle":{"text":"short GUI session title","confidence":"low|medium|high","reason":"why this title names the routed focus"},"route":{"action":"current|noop|intake_create|intake_attach|switch_existing","confidence":"low|medium|high","reason":"short reason","request":"natural language intake request when intake is needed","targetTicketId":"optional existing ticket id"},"entries":[{"kind":"intent|decision|rationale|action|verification|blocker|correction|note","summary":"short entry summary","body":"safe Cadence work-log body"}],"files":[{"path":"relative/path.ts","kind":"added|modified|deleted|renamed|unknown"}]}',
4221
4280
  "Generate sessionTitle only when the routed focus is durable enough to name a GUI row. Omit it for noop/filler. Keep it under 80 characters and do not reuse the latest checkpoint summary as the title.",
@@ -4252,6 +4311,8 @@ function buildRouteCandidatePrompt(input) {
4252
4311
  "You are resolving Cadence agent-run routing after intake returned possible existing work.",
4253
4312
  "Intake is retrieval, not the final decision. The model judges; the Cadence CLI validates and executes. Return JSON only.",
4254
4313
  "Choose whether this agent session should create a new ticket, attach/switch to one concrete candidate ticket, keep the current context, or no-op.",
4314
+ "Before choosing a candidate, decide whether the latest request is directed at this source agent to perform work, or whether this is a meta/utility task about quoted conversation content.",
4315
+ "If the transcript asks this agent to generate a title, branch name, label, summary, classification, or other metadata for quoted user text, return noop and do not attach/create/switch based on the quoted work.",
4255
4316
  "Use intake_create when the recent request is durable and the candidates are weak, adjacent, completed, or not concrete matches.",
4256
4317
  "Use intake_attach or switch_existing only when one candidate ticket is clearly the same work. You must include targetTicketId from the candidate list.",
4257
4318
  "Use current only when the saved/current Cadence context clearly fits the recent work.",
@@ -4864,6 +4925,9 @@ async function runAgentRunIngestStop(parsed, options, config, meta) {
4864
4925
  try {
4865
4926
  const client = await createClient(config, options);
4866
4927
  cadenceContext = await readCurrentCadenceContext(client, projectId) ?? cadenceContext;
4928
+ if (!duplicateTurn) {
4929
+ cadenceContext = await renewAgentSessionLeaseBestEffort(client, projectId, cadenceContext);
4930
+ }
4867
4931
  } catch {}
4868
4932
  }
4869
4933
  const observedSession = {
@@ -4883,6 +4947,11 @@ async function runAgentRunIngestStop(parsed, options, config, meta) {
4883
4947
  ...normalized.lastAssistantMessage ? { lastAssistantMessage: normalized.lastAssistantMessage } : {},
4884
4948
  ...recentTurns ? { recentTurns } : {}
4885
4949
  };
4950
+ let observedEventFile;
4951
+ const ensureObservedEventFile = async () => {
4952
+ observedEventFile ??= await writeAgentEventFile(parsed, options, normalized);
4953
+ return observedEventFile;
4954
+ };
4886
4955
  const countedState = {
4887
4956
  ...state,
4888
4957
  sessions: {
@@ -4907,7 +4976,7 @@ async function runAgentRunIngestStop(parsed, options, config, meta) {
4907
4976
  };
4908
4977
  }
4909
4978
  if (!savedCadenceContext?.ticketId) {
4910
- const eventFile2 = await writeAgentEventFile(parsed, options, normalized);
4979
+ const eventFile2 = await ensureObservedEventFile();
4911
4980
  const lockPath2 = agentLoopLockPath(parsed, options, normalized.agentSessionKey);
4912
4981
  const workerArgs2 = [
4913
4982
  "agent-run",
@@ -5009,13 +5078,24 @@ async function runAgentRunIngestStop(parsed, options, config, meta) {
5009
5078
  };
5010
5079
  }
5011
5080
  if (nextCount < threshold) {
5012
- await writeAgentLoopState(parsed, options, countedState);
5081
+ const eventFile2 = await ensureObservedEventFile();
5082
+ await writeAgentLoopState(parsed, options, {
5083
+ ...state,
5084
+ sessions: {
5085
+ ...state.sessions,
5086
+ [normalized.agentSessionKey]: {
5087
+ ...observedSession,
5088
+ lastEventFile: eventFile2
5089
+ }
5090
+ }
5091
+ });
5013
5092
  const data2 = {
5014
5093
  action: duplicateTurn ? "updated" : "counted",
5015
5094
  ...duplicateTurn ? { reason: "duplicate_turn" } : {},
5016
5095
  agentSessionKey: normalized.agentSessionKey,
5017
5096
  stopCount: nextCount,
5018
- threshold
5097
+ threshold,
5098
+ eventFile: eventFile2
5019
5099
  };
5020
5100
  return {
5021
5101
  stdout: parsed.flags.json ? formatJson(successEnvelope(data2, meta)) : `${JSON.stringify(data2, null, 2)}
@@ -5077,7 +5157,7 @@ async function runAgentRunIngestStop(parsed, options, config, meta) {
5077
5157
  exitCode: 0
5078
5158
  };
5079
5159
  }
5080
- const eventFile = await writeAgentEventFile(parsed, options, normalized);
5160
+ const eventFile = await ensureObservedEventFile();
5081
5161
  const lockPath = agentLoopLockPath(parsed, options, normalized.agentSessionKey);
5082
5162
  const workerArgs = [
5083
5163
  "agent-run",
@@ -5217,9 +5297,35 @@ async function runAgentRunRoute(parsed, options, config, meta) {
5217
5297
  });
5218
5298
  }
5219
5299
  const savedContext = sessionState.cadenceContext;
5220
- const currentContext = savedContext?.ticketId ? undefined : await readCurrentCadenceContext(client, projectId);
5221
5300
  const eventFile = parsed.options["event-file"] ?? sessionState.lastEventFile;
5222
- const event = eventFile ? tryParseJsonObject(await readFile(eventFile, "utf8"), eventFile) : synthesizeAgentEventFromSession(agentSessionKeyValue, sessionState, options);
5301
+ if (!eventFile || !existsSync(eventFile)) {
5302
+ await writeAgentLoopState(parsed, options, {
5303
+ ...state,
5304
+ sessions: {
5305
+ ...state.sessions,
5306
+ [agentSessionKeyValue]: {
5307
+ ...sessionState,
5308
+ stopCount: 0,
5309
+ threshold: defaultCheckpointThresholdValue(),
5310
+ lastAction: "skipped",
5311
+ lastReason: "no_event_file"
5312
+ }
5313
+ }
5314
+ });
5315
+ const data = {
5316
+ action: "skipped",
5317
+ reason: "no_event_file",
5318
+ agentSessionKey: agentSessionKeyValue
5319
+ };
5320
+ return {
5321
+ stdout: parsed.flags.json ? formatJson(successEnvelope(data, meta)) : `${JSON.stringify(data, null, 2)}
5322
+ `,
5323
+ stderr: "",
5324
+ exitCode: 0
5325
+ };
5326
+ }
5327
+ const event = tryParseJsonObject(await readFile(eventFile, "utf8"), eventFile);
5328
+ const currentContext = savedContext?.ticketId ? undefined : await readCurrentCadenceContext(client, projectId);
5223
5329
  const checkpointSettings = await resolveCheckpointSettings(parsed, options);
5224
5330
  const gitStatus = gitOutput(["status", "--short"], options);
5225
5331
  const gitDiffStat = gitOutput(["diff", "--stat", "origin/dev..."], options);
@@ -5325,6 +5431,8 @@ async function runAgentRunRoute(parsed, options, config, meta) {
5325
5431
  let targetTicketId = currentTicketId;
5326
5432
  let targetSessionId = currentSessionId;
5327
5433
  let targetChangesetId = currentChangesetId;
5434
+ let targetLeaseId = savedContext?.leaseId ?? currentContext?.leaseId;
5435
+ let targetLeaseExpiresAt = savedContext?.leaseExpiresAt ?? currentContext?.leaseExpiresAt;
5328
5436
  let intakeResult;
5329
5437
  let selectedTicket;
5330
5438
  let summary = routePlan.summary ?? routePlan.entries[0]?.summary ?? routePlan.route.reason ?? "Agent run route";
@@ -5354,6 +5462,8 @@ async function runAgentRunRoute(parsed, options, config, meta) {
5354
5462
  ...targetTicketId ? { ticketId: targetTicketId } : {},
5355
5463
  ...targetSessionId ? { sessionId: targetSessionId } : {},
5356
5464
  ...targetChangesetId ? { changesetId: targetChangesetId } : {},
5465
+ ...targetLeaseId ? { leaseId: targetLeaseId } : {},
5466
+ ...targetLeaseExpiresAt ? { leaseExpiresAt: targetLeaseExpiresAt } : {},
5357
5467
  ...eventFile ? { eventFile } : {},
5358
5468
  event,
5359
5469
  prompt,
@@ -5382,6 +5492,8 @@ async function runAgentRunRoute(parsed, options, config, meta) {
5382
5492
  ticketId: targetTicketId,
5383
5493
  ...targetSessionId ? { sessionId: targetSessionId } : {},
5384
5494
  ...targetChangesetId ? { changesetId: targetChangesetId } : {},
5495
+ ...targetLeaseId ? { leaseId: targetLeaseId } : {},
5496
+ ...targetLeaseExpiresAt ? { leaseExpiresAt: targetLeaseExpiresAt } : {},
5385
5497
  capturedAt: checkedAt
5386
5498
  }
5387
5499
  } : {},
@@ -5549,6 +5661,8 @@ async function runAgentRunRoute(parsed, options, config, meta) {
5549
5661
  ...commandMetadata()
5550
5662
  }
5551
5663
  });
5664
+ targetLeaseId = objectStringId(lease);
5665
+ targetLeaseExpiresAt = leaseExpiresAtFromResponse(lease);
5552
5666
  lifecycleOperations.push(checkpointLifecycleOperation("lease.claimed", true, { ticketId: targetTicketId, sessionId: targetSessionId, lease }));
5553
5667
  }
5554
5668
  if (targetSessionId) {
@@ -5638,6 +5752,8 @@ async function runAgentRunCheckpoint(parsed, options, config, meta) {
5638
5752
  const ticketId = parsed.options.ticket ?? savedContext?.ticketId ?? currentContext?.ticketId;
5639
5753
  const sessionId = parsed.options.session ?? savedContext?.sessionId ?? currentContext?.sessionId;
5640
5754
  const changesetId = parsed.options.changeset ?? savedContext?.changesetId ?? currentContext?.changesetId;
5755
+ const leaseId = savedContext?.leaseId ?? currentContext?.leaseId;
5756
+ const leaseExpiresAtValue = savedContext?.leaseExpiresAt ?? currentContext?.leaseExpiresAt;
5641
5757
  if (!ticketId) {
5642
5758
  await writeAgentLoopState(parsed, options, {
5643
5759
  ...state,
@@ -5663,7 +5779,36 @@ async function runAgentRunCheckpoint(parsed, options, config, meta) {
5663
5779
  };
5664
5780
  }
5665
5781
  const eventFile = parsed.options["event-file"] ?? sessionState.lastEventFile;
5666
- const event = eventFile ? tryParseJsonObject(await readFile(eventFile, "utf8"), eventFile) : synthesizeAgentEventFromSession(agentSessionKeyValue, sessionState, options);
5782
+ if (!eventFile || !existsSync(eventFile)) {
5783
+ await writeAgentLoopState(parsed, options, {
5784
+ ...state,
5785
+ sessions: {
5786
+ ...state.sessions,
5787
+ [agentSessionKeyValue]: {
5788
+ ...sessionState,
5789
+ stopCount: 0,
5790
+ threshold: defaultCheckpointThresholdValue(),
5791
+ lastAction: "skipped",
5792
+ lastReason: "no_event_file"
5793
+ }
5794
+ }
5795
+ });
5796
+ const data = {
5797
+ action: "skipped",
5798
+ reason: "no_event_file",
5799
+ agentSessionKey: agentSessionKeyValue,
5800
+ ...ticketId ? { ticketId } : {},
5801
+ ...sessionId ? { sessionId } : {},
5802
+ ...changesetId ? { changesetId } : {}
5803
+ };
5804
+ return {
5805
+ stdout: parsed.flags.json ? formatJson(successEnvelope(data, meta)) : `${JSON.stringify(data, null, 2)}
5806
+ `,
5807
+ stderr: "",
5808
+ exitCode: 0
5809
+ };
5810
+ }
5811
+ const event = tryParseJsonObject(await readFile(eventFile, "utf8"), eventFile);
5667
5812
  const mode = checkpointModeForCommand(parsed);
5668
5813
  const logKind = parseWorkLogEntryKind(parsed.options["log-kind"] ?? "note");
5669
5814
  const updateSummary = parseBooleanOption(parsed.options["update-summary"], false);
@@ -5934,6 +6079,8 @@ async function runAgentRunCheckpoint(parsed, options, config, meta) {
5934
6079
  ticketId,
5935
6080
  ...sessionId ? { sessionId } : {},
5936
6081
  ...changesetId ? { changesetId } : {},
6082
+ ...leaseId ? { leaseId } : {},
6083
+ ...leaseExpiresAtValue ? { leaseExpiresAt: leaseExpiresAtValue } : {},
5937
6084
  capturedAt: checkedAt
5938
6085
  },
5939
6086
  lastCheckpointAt: checkedAt,
@@ -5972,6 +6119,8 @@ async function runAgentRunCheckpoint(parsed, options, config, meta) {
5972
6119
  let targetTicketId = ticketId;
5973
6120
  let targetSessionId = sessionId;
5974
6121
  let targetChangesetId = changesetId;
6122
+ let targetLeaseId = leaseId;
6123
+ let targetLeaseExpiresAt = leaseExpiresAtValue;
5975
6124
  const cadenceWrites = [];
5976
6125
  const lifecycleOperations = [];
5977
6126
  let intakeResult;
@@ -6000,6 +6149,8 @@ async function runAgentRunCheckpoint(parsed, options, config, meta) {
6000
6149
  ticketId: targetTicketId,
6001
6150
  ...targetSessionId ? { sessionId: targetSessionId } : {},
6002
6151
  ...targetChangesetId ? { changesetId: targetChangesetId } : {},
6152
+ ...targetLeaseId ? { leaseId: targetLeaseId } : {},
6153
+ ...targetLeaseExpiresAt ? { leaseExpiresAt: targetLeaseExpiresAt } : {},
6003
6154
  ...eventFile ? { eventFile } : {},
6004
6155
  event,
6005
6156
  prompt,
@@ -6035,6 +6186,8 @@ async function runAgentRunCheckpoint(parsed, options, config, meta) {
6035
6186
  ticketId: targetTicketId,
6036
6187
  ...targetSessionId ? { sessionId: targetSessionId } : {},
6037
6188
  ...targetChangesetId ? { changesetId: targetChangesetId } : {},
6189
+ ...targetLeaseId ? { leaseId: targetLeaseId } : {},
6190
+ ...targetLeaseExpiresAt ? { leaseExpiresAt: targetLeaseExpiresAt } : {},
6038
6191
  capturedAt: checkedAt
6039
6192
  },
6040
6193
  lastCheckpointAt: checkedAt,
@@ -6272,7 +6425,25 @@ async function runAgentRunSweep(parsed, options, config, meta) {
6272
6425
  for (const staleSession of staleSessions) {
6273
6426
  const existingSession = nextState.sessions[staleSession.agentSessionKey];
6274
6427
  const hasContext = Boolean(existingSession?.cadenceContext?.ticketId);
6428
+ const eventFile = existingSession?.lastEventFile;
6275
6429
  if (existingSession) {
6430
+ if (!eventFile || !existsSync(eventFile)) {
6431
+ nextState = {
6432
+ ...nextState,
6433
+ sessions: {
6434
+ ...nextState.sessions,
6435
+ [staleSession.agentSessionKey]: {
6436
+ ...existingSession,
6437
+ stopCount: 0,
6438
+ threshold: defaultCheckpointThresholdValue(),
6439
+ lastAction: "skipped",
6440
+ lastReason: "no_event_file"
6441
+ }
6442
+ }
6443
+ };
6444
+ await writeAgentLoopState(parsed, options, nextState);
6445
+ continue;
6446
+ }
6276
6447
  nextState = {
6277
6448
  ...nextState,
6278
6449
  sessions: {
@@ -6285,6 +6456,7 @@ async function runAgentRunSweep(parsed, options, config, meta) {
6285
6456
  };
6286
6457
  await writeAgentLoopState(parsed, options, nextState);
6287
6458
  }
6459
+ const realEventFile = eventFile;
6288
6460
  await spawnAgentRunCheckpoint([
6289
6461
  "agent-run",
6290
6462
  hasContext ? "checkpoint" : "route",
@@ -6292,6 +6464,8 @@ async function runAgentRunSweep(parsed, options, config, meta) {
6292
6464
  staleSession.agentSessionKey,
6293
6465
  "--reason",
6294
6466
  hasContext ? "idle" : "missing_context",
6467
+ "--event-file",
6468
+ realEventFile,
6295
6469
  "--lock",
6296
6470
  agentLoopLockPath(parsed, options, staleSession.agentSessionKey),
6297
6471
  ...parsed.flags.project ? ["--project", parsed.flags.project] : [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trycadence/cli",
3
- "version": "0.1.18-dev.0",
3
+ "version": "0.1.21-dev.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {