@basou/cli 0.9.0 → 0.10.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
@@ -115,7 +115,8 @@ import { join } from "path";
115
115
  import {
116
116
  ApprovalSchema,
117
117
  ApprovalStatusSchema,
118
- appendEvent,
118
+ acquireLock,
119
+ appendChainedEventLocked,
119
120
  assertBasouRootSafe,
120
121
  basouPaths,
121
122
  enumerateApprovals,
@@ -320,55 +321,63 @@ async function doRunApprovalResolve(idInput, options, ctx, decision) {
320
321
  if (approval.status !== "pending") {
321
322
  throw new Error(`Approval status mismatch: pending YAML has status=${approval.status}`);
322
323
  }
323
- const sessionDir = join(paths.sessions, approval.session_id);
324
- for await (const ev of replayEvents(sessionDir, {
325
- onWarning: (w) => printReplayWarning(w, approval.session_id)
326
- })) {
327
- if (isApprovalEvent(ev) && ev.approval_id === approval.id && (ev.type === "approval_approved" || ev.type === "approval_rejected" || ev.type === "approval_expired")) {
328
- throw new Error(`Approval already resolved (per events.jsonl): ${idInput}`);
329
- }
330
- }
331
324
  const now = /* @__PURE__ */ new Date();
332
- if (isLazyExpired(approval, now)) {
333
- throw new Error(`Approval already expired: ${idInput}`);
334
- }
335
- let sessionStatus = null;
336
- try {
337
- sessionStatus = (await readSessionYaml(paths, approval.session_id)).session.status;
338
- } catch {
339
- sessionStatus = null;
340
- }
341
- if (sessionStatus === "imported") {
342
- throw new Error(`Cannot resolve an approval for an imported session: ${idInput}`);
343
- }
344
325
  const occurredAt = now.toISOString();
345
326
  const eventId = prefixedUlid("evt");
346
- if (decision === "approve") {
347
- const note = options.note ?? null;
348
- await appendEvent(sessionDir, {
349
- schema_version: "0.1.0",
350
- id: eventId,
351
- session_id: approval.session_id,
352
- occurred_at: occurredAt,
353
- source: "local-cli",
354
- type: "approval_approved",
355
- approval_id: approval.id,
356
- resolver: "local-cli",
357
- note
358
- });
359
- } else {
360
- const reason = options.reason;
361
- await appendEvent(sessionDir, {
362
- schema_version: "0.1.0",
363
- id: eventId,
364
- session_id: approval.session_id,
365
- occurred_at: occurredAt,
366
- source: "local-cli",
367
- type: "approval_rejected",
368
- approval_id: approval.id,
369
- resolver: "local-cli",
370
- reason
371
- });
327
+ const sessionLock = await acquireLock(paths, "session", approval.session_id);
328
+ try {
329
+ const sessionDir = join(paths.sessions, approval.session_id);
330
+ for await (const ev of replayEvents(sessionDir, {
331
+ onWarning: (w) => printReplayWarning(w, approval.session_id)
332
+ })) {
333
+ if (isApprovalEvent(ev) && ev.approval_id === approval.id && (ev.type === "approval_approved" || ev.type === "approval_rejected" || ev.type === "approval_expired")) {
334
+ throw new Error(`Approval already resolved (per events.jsonl): ${idInput}`);
335
+ }
336
+ }
337
+ if (isLazyExpired(approval, now)) {
338
+ throw new Error(`Approval already expired: ${idInput}`);
339
+ }
340
+ let sessionStatus = null;
341
+ try {
342
+ sessionStatus = (await readSessionYaml(paths, approval.session_id)).session.status;
343
+ } catch {
344
+ sessionStatus = null;
345
+ }
346
+ const attachable = sessionStatus === "initialized" || sessionStatus === "running" || sessionStatus === "waiting_approval";
347
+ if (sessionStatus !== null && !attachable) {
348
+ throw new Error(
349
+ `Cannot resolve an approval for a session that is not active (status=${sessionStatus}): ${idInput}`
350
+ );
351
+ }
352
+ if (decision === "approve") {
353
+ const note = options.note ?? null;
354
+ await appendChainedEventLocked(paths, approval.session_id, {
355
+ schema_version: "0.1.0",
356
+ id: eventId,
357
+ session_id: approval.session_id,
358
+ occurred_at: occurredAt,
359
+ source: "local-cli",
360
+ type: "approval_approved",
361
+ approval_id: approval.id,
362
+ resolver: "local-cli",
363
+ note
364
+ });
365
+ } else {
366
+ const reason = options.reason;
367
+ await appendChainedEventLocked(paths, approval.session_id, {
368
+ schema_version: "0.1.0",
369
+ id: eventId,
370
+ session_id: approval.session_id,
371
+ occurred_at: occurredAt,
372
+ source: "local-cli",
373
+ type: "approval_rejected",
374
+ approval_id: approval.id,
375
+ resolver: "local-cli",
376
+ reason
377
+ });
378
+ }
379
+ } finally {
380
+ await sessionLock.release();
372
381
  }
373
382
  const resolvedApproval = decision === "approve" ? {
374
383
  ...approval,
@@ -625,7 +634,7 @@ function printNoApprovals(options) {
625
634
 
626
635
  // src/commands/decision.ts
627
636
  import {
628
- acquireLock,
637
+ acquireLock as acquireLock2,
629
638
  appendEventToExistingSession,
630
639
  assertBasouRootSafe as assertBasouRootSafe2,
631
640
  basouPaths as basouPaths2,
@@ -690,7 +699,7 @@ async function doRunDecisionRecord(options, ctx) {
690
699
  if (options.session !== void 0) {
691
700
  const sessionId = await resolveSessionId(paths, options.session);
692
701
  const sesId = sessionId;
693
- const sessionLock = await acquireLock(paths, "session", sesId);
702
+ const sessionLock = await acquireLock2(paths, "session", sesId);
694
703
  let result;
695
704
  try {
696
705
  result = await appendEventToExistingSession({
@@ -954,10 +963,12 @@ import { mkdir } from "fs/promises";
954
963
  import { homedir } from "os";
955
964
  import { join as join2 } from "path";
956
965
  import {
966
+ acquireLock as acquireLock3,
957
967
  assertBasouRootSafe as assertBasouRootSafe4,
958
968
  basouPaths as basouPaths4,
959
969
  ChildProcessRunner,
960
- appendEvent as coreAppendEvent,
970
+ appendChainedEvent as coreAppendChainedEvent,
971
+ finalizeSessionYaml,
961
972
  getSnapshot,
962
973
  overwriteYamlFile,
963
974
  parseDuration,
@@ -983,7 +994,6 @@ function registerExecCommand(program2) {
983
994
  async function runExec(command, args, options, ctx = {}) {
984
995
  const runner = ctx.runner ?? new ChildProcessRunner();
985
996
  const now = ctx.now ?? (() => /* @__PURE__ */ new Date());
986
- const appendEvent2 = ctx.appendEvent ?? coreAppendEvent;
987
997
  const cwd = options.cwd ?? process.cwd();
988
998
  const timeout_ms = options.timeout !== void 0 ? parseDuration(options.timeout) : void 0;
989
999
  const repoRoot = await resolveRepositoryRootForExec(cwd);
@@ -993,6 +1003,9 @@ async function runExec(command, args, options, ctx = {}) {
993
1003
  const sessionId = prefixedUlid3("ses");
994
1004
  const sessionDir = join2(paths.sessions, sessionId);
995
1005
  await mkdir(sessionDir, { recursive: true });
1006
+ const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
1007
+ await coreAppendChainedEvent(paths, sessionId, event);
1008
+ });
996
1009
  const startedAt = now().toISOString();
997
1010
  const sessionYamlPath = join2(sessionDir, "session.yaml");
998
1011
  const session = buildInitialSession({
@@ -1004,7 +1017,7 @@ async function runExec(command, args, options, ctx = {}) {
1004
1017
  startedAt
1005
1018
  });
1006
1019
  await writeYamlFile(sessionYamlPath, session);
1007
- await appendEvent2(sessionDir, {
1020
+ await appendEvent(sessionDir, {
1008
1021
  schema_version: "0.1.0",
1009
1022
  type: "session_started",
1010
1023
  id: prefixedUlid3("evt"),
@@ -1013,10 +1026,10 @@ async function runExec(command, args, options, ctx = {}) {
1013
1026
  source: "terminal-recording"
1014
1027
  });
1015
1028
  if (options.snapshot !== false) {
1016
- await tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now, appendEvent2);
1029
+ await tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now, appendEvent);
1017
1030
  }
1018
1031
  const runningAt = now().toISOString();
1019
- await appendEvent2(sessionDir, {
1032
+ await appendEvent(sessionDir, {
1020
1033
  schema_version: "0.1.0",
1021
1034
  type: "session_status_changed",
1022
1035
  id: prefixedUlid3("evt"),
@@ -1026,9 +1039,14 @@ async function runExec(command, args, options, ctx = {}) {
1026
1039
  from: "initialized",
1027
1040
  to: "running"
1028
1041
  });
1029
- await mutateSessionYaml(sessionYamlPath, (s) => {
1030
- s.session.status = "running";
1031
- });
1042
+ const runningLock = await acquireLock3(paths, "session", sessionId);
1043
+ try {
1044
+ await mutateSessionYaml(sessionYamlPath, (s) => {
1045
+ s.session.status = "running";
1046
+ });
1047
+ } finally {
1048
+ await runningLock.release();
1049
+ }
1032
1050
  const controller = new AbortController();
1033
1051
  let signalReceived = null;
1034
1052
  let activeChild = null;
@@ -1064,7 +1082,7 @@ async function runExec(command, args, options, ctx = {}) {
1064
1082
  }
1065
1083
  });
1066
1084
  } catch (spawnError) {
1067
- await finalizeSessionAsFailed(sessionDir, sessionYamlPath, sessionId, appendEvent2, {
1085
+ await finalizeSessionAsFailed(paths, sessionDir, sessionId, appendEvent, {
1068
1086
  command,
1069
1087
  args,
1070
1088
  cwd,
@@ -1080,7 +1098,7 @@ async function runExec(command, args, options, ctx = {}) {
1080
1098
  activeChild = null;
1081
1099
  }
1082
1100
  const endedAt = now().toISOString();
1083
- await appendEvent2(sessionDir, {
1101
+ await appendEvent(sessionDir, {
1084
1102
  schema_version: "0.1.0",
1085
1103
  type: "command_executed",
1086
1104
  id: prefixedUlid3("evt"),
@@ -1096,10 +1114,10 @@ async function runExec(command, args, options, ctx = {}) {
1096
1114
  duration_ms: result.duration_ms
1097
1115
  });
1098
1116
  if (options.snapshot !== false) {
1099
- await tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now, appendEvent2);
1117
+ await tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now, appendEvent);
1100
1118
  }
1101
1119
  const finalStatus = decideFinalStatus(result, signalReceived);
1102
- await appendEvent2(sessionDir, {
1120
+ await appendEvent(sessionDir, {
1103
1121
  schema_version: "0.1.0",
1104
1122
  type: "session_status_changed",
1105
1123
  id: prefixedUlid3("evt"),
@@ -1109,7 +1127,7 @@ async function runExec(command, args, options, ctx = {}) {
1109
1127
  from: "running",
1110
1128
  to: finalStatus
1111
1129
  });
1112
- await appendEvent2(sessionDir, {
1130
+ await appendEvent(sessionDir, {
1113
1131
  schema_version: "0.1.0",
1114
1132
  type: "session_ended",
1115
1133
  id: prefixedUlid3("evt"),
@@ -1118,7 +1136,7 @@ async function runExec(command, args, options, ctx = {}) {
1118
1136
  source: "terminal-recording",
1119
1137
  ...result.exit_code !== null ? { exit_code: result.exit_code } : {}
1120
1138
  });
1121
- await mutateSessionYaml(sessionYamlPath, (s) => {
1139
+ await finalizeSessionYaml(paths, sessionId, (s) => {
1122
1140
  s.session.status = finalStatus;
1123
1141
  s.session.ended_at = endedAt;
1124
1142
  s.session.invocation.exit_code = result.exit_code;
@@ -1148,7 +1166,7 @@ function signalToExitCode(sig) {
1148
1166
  const num = SIGNUM_MAP[sig] ?? 1;
1149
1167
  return 128 + num;
1150
1168
  }
1151
- async function tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now, appendEvent2) {
1169
+ async function tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now, appendEvent) {
1152
1170
  let snapshot;
1153
1171
  try {
1154
1172
  snapshot = await getSnapshot(repoRoot);
@@ -1156,7 +1174,7 @@ async function tryAppendGitSnapshot(sessionDir, sessionId, repoRoot, now, append
1156
1174
  console.warn(normalizeGitSnapshotSkipMessage(error));
1157
1175
  return;
1158
1176
  }
1159
- await appendEvent2(sessionDir, {
1177
+ await appendEvent(sessionDir, {
1160
1178
  schema_version: "0.1.0",
1161
1179
  type: "git_snapshot",
1162
1180
  id: prefixedUlid3("evt"),
@@ -1212,8 +1230,8 @@ async function mutateSessionYaml(filePath, mutator) {
1212
1230
  const validated = SessionSchema.parse(parsed);
1213
1231
  await overwriteYamlFile(filePath, validated);
1214
1232
  }
1215
- async function finalizeSessionAsFailed(sessionDir, sessionYamlPath, sessionId, appendEvent2, ctx) {
1216
- await appendEvent2(sessionDir, {
1233
+ async function finalizeSessionAsFailed(paths, sessionDir, sessionId, appendEvent, ctx) {
1234
+ await appendEvent(sessionDir, {
1217
1235
  schema_version: "0.1.0",
1218
1236
  type: "command_executed",
1219
1237
  id: prefixedUlid3("evt"),
@@ -1228,7 +1246,7 @@ async function finalizeSessionAsFailed(sessionDir, sessionYamlPath, sessionId, a
1228
1246
  ...ctx.signalReceived !== null ? { received_signal: ctx.signalReceived } : {},
1229
1247
  duration_ms: 0
1230
1248
  });
1231
- await appendEvent2(sessionDir, {
1249
+ await appendEvent(sessionDir, {
1232
1250
  schema_version: "0.1.0",
1233
1251
  type: "session_status_changed",
1234
1252
  id: prefixedUlid3("evt"),
@@ -1238,7 +1256,7 @@ async function finalizeSessionAsFailed(sessionDir, sessionYamlPath, sessionId, a
1238
1256
  from: "running",
1239
1257
  to: "failed"
1240
1258
  });
1241
- await appendEvent2(sessionDir, {
1259
+ await appendEvent(sessionDir, {
1242
1260
  schema_version: "0.1.0",
1243
1261
  type: "session_ended",
1244
1262
  id: prefixedUlid3("evt"),
@@ -1246,7 +1264,7 @@ async function finalizeSessionAsFailed(sessionDir, sessionYamlPath, sessionId, a
1246
1264
  occurred_at: ctx.occurredAt,
1247
1265
  source: "terminal-recording"
1248
1266
  });
1249
- await mutateSessionYaml(sessionYamlPath, (s) => {
1267
+ await finalizeSessionYaml(paths, sessionId, (s) => {
1250
1268
  s.session.status = "failed";
1251
1269
  s.session.ended_at = ctx.occurredAt;
1252
1270
  s.session.invocation.exit_code = null;
@@ -2398,11 +2416,13 @@ import { mkdir as mkdir2 } from "fs/promises";
2398
2416
  import { homedir as homedir4 } from "os";
2399
2417
  import { join as join5 } from "path";
2400
2418
  import {
2419
+ acquireLock as acquireLock4,
2401
2420
  assertBasouRootSafe as assertBasouRootSafe8,
2402
2421
  basouPaths as basouPaths8,
2403
2422
  ChildProcessRunner as ChildProcessRunner2,
2404
2423
  claudeCodeAdapterMetadata,
2405
- appendEvent as coreAppendEvent2,
2424
+ appendChainedEvent as coreAppendChainedEvent2,
2425
+ finalizeSessionYaml as finalizeSessionYaml2,
2406
2426
  getDiff,
2407
2427
  getSnapshot as getSnapshot2,
2408
2428
  overwriteYamlFile as overwriteYamlFile2,
@@ -2438,7 +2458,6 @@ function registerRunCommand(program2, ctx = {}) {
2438
2458
  async function runClaudeCode(args, options, ctx = {}) {
2439
2459
  const runner = ctx.runner ?? new ChildProcessRunner2();
2440
2460
  const now = ctx.now ?? (() => /* @__PURE__ */ new Date());
2441
- const appendEvent2 = ctx.appendEvent ?? coreAppendEvent2;
2442
2461
  const resolveCommand = ctx.resolveCommand ?? resolveClaudeCodeCommand;
2443
2462
  const getDiffFn = ctx.getDiff ?? getDiff;
2444
2463
  const { command } = await resolveCommand();
@@ -2450,6 +2469,9 @@ async function runClaudeCode(args, options, ctx = {}) {
2450
2469
  const sessionId = prefixedUlid4("ses");
2451
2470
  const sessionDir = join5(paths.sessions, sessionId);
2452
2471
  await mkdir2(sessionDir, { recursive: true });
2472
+ const appendEvent = ctx.appendEvent ?? (async (_sessionDir, event) => {
2473
+ await coreAppendChainedEvent2(paths, sessionId, event);
2474
+ });
2453
2475
  const startedAt = now().toISOString();
2454
2476
  const sessionYamlPath = join5(sessionDir, "session.yaml");
2455
2477
  const session = buildInitialSession2({
@@ -2461,7 +2483,7 @@ async function runClaudeCode(args, options, ctx = {}) {
2461
2483
  startedAt
2462
2484
  });
2463
2485
  await writeYamlFile2(sessionYamlPath, session);
2464
- await appendEvent2(sessionDir, {
2486
+ await appendEvent(sessionDir, {
2465
2487
  schema_version: "0.1.0",
2466
2488
  type: "session_started",
2467
2489
  id: prefixedUlid4("evt"),
@@ -2471,10 +2493,10 @@ async function runClaudeCode(args, options, ctx = {}) {
2471
2493
  });
2472
2494
  let preSnapshot = null;
2473
2495
  if (options.snapshot !== false) {
2474
- preSnapshot = await tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appendEvent2);
2496
+ preSnapshot = await tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appendEvent);
2475
2497
  }
2476
2498
  const runningAt = now().toISOString();
2477
- await appendEvent2(sessionDir, {
2499
+ await appendEvent(sessionDir, {
2478
2500
  schema_version: "0.1.0",
2479
2501
  type: "session_status_changed",
2480
2502
  id: prefixedUlid4("evt"),
@@ -2484,9 +2506,14 @@ async function runClaudeCode(args, options, ctx = {}) {
2484
2506
  from: "initialized",
2485
2507
  to: "running"
2486
2508
  });
2487
- await mutateSessionYaml2(sessionYamlPath, (s) => {
2488
- s.session.status = "running";
2489
- });
2509
+ const runningLock = await acquireLock4(paths, "session", sessionId);
2510
+ try {
2511
+ await mutateSessionYaml2(sessionYamlPath, (s) => {
2512
+ s.session.status = "running";
2513
+ });
2514
+ } finally {
2515
+ await runningLock.release();
2516
+ }
2490
2517
  const controller = new AbortController();
2491
2518
  let signalReceived = null;
2492
2519
  let activeChild = null;
@@ -2521,7 +2548,7 @@ async function runClaudeCode(args, options, ctx = {}) {
2521
2548
  }
2522
2549
  });
2523
2550
  } catch (spawnError) {
2524
- await finalizeSessionAsFailed2(sessionDir, sessionYamlPath, sessionId, appendEvent2, {
2551
+ await finalizeSessionAsFailed2(paths, sessionDir, sessionId, appendEvent, {
2525
2552
  command,
2526
2553
  args,
2527
2554
  cwd: repoRoot,
@@ -2537,7 +2564,7 @@ async function runClaudeCode(args, options, ctx = {}) {
2537
2564
  activeChild = null;
2538
2565
  }
2539
2566
  const endedAt = now().toISOString();
2540
- await appendEvent2(sessionDir, {
2567
+ await appendEvent(sessionDir, {
2541
2568
  schema_version: "0.1.0",
2542
2569
  type: "command_executed",
2543
2570
  id: prefixedUlid4("evt"),
@@ -2554,7 +2581,7 @@ async function runClaudeCode(args, options, ctx = {}) {
2554
2581
  });
2555
2582
  let postSnapshot = null;
2556
2583
  if (options.snapshot !== false) {
2557
- postSnapshot = await tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appendEvent2);
2584
+ postSnapshot = await tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appendEvent);
2558
2585
  }
2559
2586
  let diff = null;
2560
2587
  if (preSnapshot !== null && postSnapshot !== null) {
@@ -2565,7 +2592,7 @@ async function runClaudeCode(args, options, ctx = {}) {
2565
2592
  preSnapshot.head,
2566
2593
  postSnapshot.head,
2567
2594
  now().toISOString(),
2568
- appendEvent2,
2595
+ appendEvent,
2569
2596
  getDiffFn
2570
2597
  );
2571
2598
  }
@@ -2575,7 +2602,7 @@ async function runClaudeCode(args, options, ctx = {}) {
2575
2602
  homedir: homedir4()
2576
2603
  }).sanitized;
2577
2604
  const finalStatus = decideFinalStatus2(result, signalReceived);
2578
- await appendEvent2(sessionDir, {
2605
+ await appendEvent(sessionDir, {
2579
2606
  schema_version: "0.1.0",
2580
2607
  type: "session_status_changed",
2581
2608
  id: prefixedUlid4("evt"),
@@ -2585,7 +2612,7 @@ async function runClaudeCode(args, options, ctx = {}) {
2585
2612
  from: "running",
2586
2613
  to: finalStatus
2587
2614
  });
2588
- await appendEvent2(sessionDir, {
2615
+ await appendEvent(sessionDir, {
2589
2616
  schema_version: "0.1.0",
2590
2617
  type: "session_ended",
2591
2618
  id: prefixedUlid4("evt"),
@@ -2594,7 +2621,7 @@ async function runClaudeCode(args, options, ctx = {}) {
2594
2621
  source: claudeCodeAdapterMetadata.kind,
2595
2622
  ...result.exit_code !== null ? { exit_code: result.exit_code } : {}
2596
2623
  });
2597
- await mutateSessionYaml2(sessionYamlPath, (s) => {
2624
+ await finalizeSessionYaml2(paths, sessionId, (s) => {
2598
2625
  s.session.status = finalStatus;
2599
2626
  s.session.ended_at = endedAt;
2600
2627
  s.session.invocation.exit_code = result.exit_code;
@@ -2623,7 +2650,7 @@ function signalToExitCode2(sig) {
2623
2650
  const num = SIGNUM_MAP2[sig] ?? 1;
2624
2651
  return 128 + num;
2625
2652
  }
2626
- async function tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appendEvent2) {
2653
+ async function tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appendEvent) {
2627
2654
  let snapshot;
2628
2655
  try {
2629
2656
  snapshot = await getSnapshot2(repoRoot);
@@ -2631,7 +2658,7 @@ async function tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appen
2631
2658
  console.warn(normalizeGitSnapshotSkipMessage2(error));
2632
2659
  return null;
2633
2660
  }
2634
- await appendEvent2(sessionDir, {
2661
+ await appendEvent(sessionDir, {
2635
2662
  schema_version: "0.1.0",
2636
2663
  type: "git_snapshot",
2637
2664
  id: prefixedUlid4("evt"),
@@ -2642,7 +2669,7 @@ async function tryAppendGitSnapshot2(sessionDir, sessionId, repoRoot, now, appen
2642
2669
  });
2643
2670
  return snapshot;
2644
2671
  }
2645
- async function tryAppendFileChangedEvents(sessionDir, sessionId, repoRoot, baseRef, headRef, occurredAt, appendEvent2, getDiffFn) {
2672
+ async function tryAppendFileChangedEvents(sessionDir, sessionId, repoRoot, baseRef, headRef, occurredAt, appendEvent, getDiffFn) {
2646
2673
  let diff;
2647
2674
  try {
2648
2675
  diff = await getDiffFn(repoRoot, baseRef, headRef);
@@ -2651,7 +2678,7 @@ async function tryAppendFileChangedEvents(sessionDir, sessionId, repoRoot, baseR
2651
2678
  return null;
2652
2679
  }
2653
2680
  for (const change of diff.changed_files) {
2654
- await appendEvent2(sessionDir, {
2681
+ await appendEvent(sessionDir, {
2655
2682
  schema_version: "0.1.0",
2656
2683
  type: "file_changed",
2657
2684
  id: prefixedUlid4("evt"),
@@ -2734,8 +2761,8 @@ async function mutateSessionYaml2(filePath, mutator) {
2734
2761
  const validated = SessionSchema2.parse(parsed);
2735
2762
  await overwriteYamlFile2(filePath, validated);
2736
2763
  }
2737
- async function finalizeSessionAsFailed2(sessionDir, sessionYamlPath, sessionId, appendEvent2, ctx) {
2738
- await appendEvent2(sessionDir, {
2764
+ async function finalizeSessionAsFailed2(paths, sessionDir, sessionId, appendEvent, ctx) {
2765
+ await appendEvent(sessionDir, {
2739
2766
  schema_version: "0.1.0",
2740
2767
  type: "command_executed",
2741
2768
  id: prefixedUlid4("evt"),
@@ -2750,7 +2777,7 @@ async function finalizeSessionAsFailed2(sessionDir, sessionYamlPath, sessionId,
2750
2777
  ...ctx.signalReceived !== null ? { received_signal: ctx.signalReceived } : {},
2751
2778
  duration_ms: 0
2752
2779
  });
2753
- await appendEvent2(sessionDir, {
2780
+ await appendEvent(sessionDir, {
2754
2781
  schema_version: "0.1.0",
2755
2782
  type: "session_status_changed",
2756
2783
  id: prefixedUlid4("evt"),
@@ -2760,7 +2787,7 @@ async function finalizeSessionAsFailed2(sessionDir, sessionYamlPath, sessionId,
2760
2787
  from: "running",
2761
2788
  to: "failed"
2762
2789
  });
2763
- await appendEvent2(sessionDir, {
2790
+ await appendEvent(sessionDir, {
2764
2791
  schema_version: "0.1.0",
2765
2792
  type: "session_ended",
2766
2793
  id: prefixedUlid4("evt"),
@@ -2768,7 +2795,7 @@ async function finalizeSessionAsFailed2(sessionDir, sessionYamlPath, sessionId,
2768
2795
  occurred_at: ctx.occurredAt,
2769
2796
  source: claudeCodeAdapterMetadata.kind
2770
2797
  });
2771
- await mutateSessionYaml2(sessionYamlPath, (s) => {
2798
+ await finalizeSessionYaml2(paths, sessionId, (s) => {
2772
2799
  s.session.status = "failed";
2773
2800
  s.session.ended_at = ctx.occurredAt;
2774
2801
  s.session.invocation.exit_code = null;
@@ -2791,7 +2818,7 @@ async function resolveRepositoryRootForRun(cwd) {
2791
2818
  import { readFile as readFile2 } from "fs/promises";
2792
2819
  import { basename as basename3, isAbsolute, join as join6, relative as relative2 } from "path";
2793
2820
  import {
2794
- acquireLock as acquireLock2,
2821
+ acquireLock as acquireLock5,
2795
2822
  appendEventToExistingSession as appendEventToExistingSession2,
2796
2823
  assertBasouRootSafe as assertBasouRootSafe9,
2797
2824
  basouPaths as basouPaths9,
@@ -3341,7 +3368,7 @@ async function doRunSessionNote(sessionIdInput, options, ctx) {
3341
3368
  }
3342
3369
  const occurredAt = (/* @__PURE__ */ new Date()).toISOString();
3343
3370
  const sesId = sessionId;
3344
- const sessionLock = await acquireLock2(paths, "session", sesId);
3371
+ const sessionLock = await acquireLock5(paths, "session", sesId);
3345
3372
  let result;
3346
3373
  try {
3347
3374
  result = await appendEventToExistingSession2({
@@ -4812,9 +4839,7 @@ import {
4812
4839
  verifyEventsChain
4813
4840
  } from "@basou/core";
4814
4841
  function registerVerifyCommand(program2) {
4815
- program2.command("verify").description(
4816
- "Verify the tamper-evidence hash chain of imported sessions' event logs (read-only)"
4817
- ).option("--session <id>", "Verify a single session (unique id prefix accepted)").option("--all", "Verify every session (the default when --session is omitted)").option("--json", "Output the verdicts as JSON").option("-v, --verbose", "Show error causes").action(async (opts) => {
4842
+ program2.command("verify").description("Verify the tamper-evidence hash chain of sessions' event logs (read-only)").option("--session <id>", "Verify a single session (unique id prefix accepted)").option("--all", "Verify every session (the default when --session is omitted)").option("--json", "Output the verdicts as JSON").option("-v, --verbose", "Show error causes").action(async (opts) => {
4818
4843
  await runVerify(opts);
4819
4844
  });
4820
4845
  }
@@ -4855,7 +4880,7 @@ async function doRunVerify(options, ctx) {
4855
4880
  }
4856
4881
  const tally = (status) => rows.filter((r) => r.status === status).length;
4857
4882
  console.log(
4858
- `Sessions: ${rows.length} total \u2014 ${tally("verified")} verified, ${tally("unchained")} unchained, ${tally("empty")} empty, ${tally("incomplete")} incomplete, ${tamperedCount} tampered`
4883
+ `Sessions: ${rows.length} total \u2014 ${tally("verified")} verified, ${tally("unchained")} unchained, ${tally("empty")} empty, ${tally("incomplete")} incomplete, ${tally("in_progress")} in_progress, ${tamperedCount} tampered`
4859
4884
  );
4860
4885
  }
4861
4886
  if (tamperedCount > 0) {
@@ -4870,8 +4895,10 @@ function renderVerdict(row) {
4870
4895
  return row.line !== void 0 ? `TAMPERED (${row.reason} at line ${row.line})` : `TAMPERED (${row.reason})`;
4871
4896
  case "incomplete":
4872
4897
  return "incomplete (session.yaml missing; re-import to repair)";
4898
+ case "in_progress":
4899
+ return `in_progress (${row.event_count} events; live session, anchor written at finalize)`;
4873
4900
  case "unchained":
4874
- return "unchained (not an imported session, or imported before chaining)";
4901
+ return "unchained (session created before event-log chaining)";
4875
4902
  case "empty":
4876
4903
  return "empty";
4877
4904
  }