@gajae-code/coding-agent 0.18.8 → 0.18.10

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 (113) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/dist/types/async/endpoint-id.d.ts +16 -0
  3. package/dist/types/async/support.d.ts +0 -14
  4. package/dist/types/commands/mcp-serve.d.ts +4 -0
  5. package/dist/types/discovery/mcp-json.d.ts +1 -0
  6. package/dist/types/eval/py/kernel.d.ts +18 -0
  7. package/dist/types/export/html/html-export-destination.d.ts +2 -0
  8. package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +5 -0
  9. package/dist/types/extensibility/skill-management.d.ts +1 -0
  10. package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -3
  11. package/dist/types/gjc-runtime/sidecar-bootstrap-env.d.ts +6 -0
  12. package/dist/types/gjc-runtime/state-writer.d.ts +3 -0
  13. package/dist/types/gjc-runtime/tmux-common.d.ts +8 -5
  14. package/dist/types/lsp/config.d.ts +8 -5
  15. package/dist/types/runtime-mcp/manager.d.ts +2 -1
  16. package/dist/types/runtime-mcp/mcp-redirect.d.ts +9 -0
  17. package/dist/types/runtime-mcp/oauth-public-url.d.ts +3 -1
  18. package/dist/types/runtime-mcp/project-host-values.d.ts +27 -0
  19. package/dist/types/sdk/broker/broker.d.ts +6 -1
  20. package/dist/types/sdk/broker/doctor-restart.d.ts +5 -0
  21. package/dist/types/sdk/broker/ensure.d.ts +14 -1
  22. package/dist/types/session/session-storage.d.ts +7 -0
  23. package/dist/types/skill-state/workflow-mutation-guard.d.ts +3 -0
  24. package/dist/types/task/isolation-dir.d.ts +26 -0
  25. package/dist/types/tools/ast-edit-write-path.d.ts +13 -0
  26. package/dist/types/tools/browser/cdp-url.d.ts +2 -0
  27. package/dist/types/tools/github-side-effect.d.ts +22 -0
  28. package/dist/types/tools/monitor-mutation.d.ts +9 -0
  29. package/package.json +7 -7
  30. package/src/async/endpoint-id.ts +26 -0
  31. package/src/async/job-manager.ts +19 -10
  32. package/src/async/support.ts +0 -25
  33. package/src/capability/index.ts +14 -16
  34. package/src/cli/doctor/repairs.ts +4 -1
  35. package/src/cli/doctor-supervisor.ts +3 -0
  36. package/src/cli/public-command-registry.ts +28 -6
  37. package/src/commands/mcp-serve.ts +7 -0
  38. package/src/commands/sdk.ts +453 -415
  39. package/src/config/file-lock.ts +9 -11
  40. package/src/config/model-profile-activation.ts +5 -5
  41. package/src/coordinator-mcp/server.ts +2 -0
  42. package/src/customization/inventory.ts +1 -13
  43. package/src/dap/config.ts +2 -2
  44. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +10 -6
  45. package/src/discovery/builtin.ts +3 -1
  46. package/src/discovery/helpers.ts +12 -9
  47. package/src/discovery/mcp-json.ts +20 -12
  48. package/src/eval/py/executor.ts +459 -77
  49. package/src/eval/py/kernel.ts +79 -13
  50. package/src/eval/py/tool-bridge.ts +4 -0
  51. package/src/export/html/html-export-destination.ts +16 -0
  52. package/src/export/html/index.ts +2 -6
  53. package/src/extensibility/extensions/function-hooks-internal.ts +27 -0
  54. package/src/extensibility/extensions/runner.ts +11 -1
  55. package/src/extensibility/gjc-plugins/mcp-policy.ts +2 -1
  56. package/src/extensibility/gjc-plugins/runtime-adapters.ts +27 -15
  57. package/src/extensibility/skill-management.ts +3 -0
  58. package/src/gjc-runtime/launch-worktree.ts +56 -5
  59. package/src/gjc-runtime/psmux-detect.ts +28 -4
  60. package/src/gjc-runtime/session-state-sidecar.ts +3 -2
  61. package/src/gjc-runtime/sidecar-bootstrap-env.ts +19 -0
  62. package/src/gjc-runtime/state-writer.ts +22 -8
  63. package/src/gjc-runtime/tmux-common.ts +8 -5
  64. package/src/internal-urls/docs-index.generated.ts +7 -7
  65. package/src/lsp/config.ts +12 -63
  66. package/src/memories/index.ts +88 -7
  67. package/src/modes/components/hook-editor.ts +1 -1
  68. package/src/modes/components/hook-selector.ts +1 -1
  69. package/src/modes/controllers/input-controller.ts +2 -2
  70. package/src/modes/controllers/runtime-mcp-command-controller.ts +21 -13
  71. package/src/modes/controllers/selector-controller.ts +3 -7
  72. package/src/modes/controllers/todo-command-controller.ts +1 -1
  73. package/src/modes/interactive-mode.ts +1 -1
  74. package/src/runtime-mcp/client.ts +4 -2
  75. package/src/runtime-mcp/json-rpc.ts +2 -1
  76. package/src/runtime-mcp/manager.ts +29 -11
  77. package/src/runtime-mcp/mcp-redirect.ts +73 -0
  78. package/src/runtime-mcp/oauth-flow.ts +11 -4
  79. package/src/runtime-mcp/oauth-public-url.ts +8 -2
  80. package/src/runtime-mcp/pool.ts +6 -0
  81. package/src/runtime-mcp/project-host-values.ts +46 -0
  82. package/src/runtime-mcp/transports/http.ts +2 -1
  83. package/src/sdk/acp/adapter.ts +2 -0
  84. package/src/sdk/broker/broker.ts +6 -0
  85. package/src/sdk/broker/doctor-restart.ts +4 -0
  86. package/src/sdk/broker/ensure.ts +29 -2
  87. package/src/sdk/host/session-runtime.test.ts +58 -9
  88. package/src/sdk/host/session-runtime.ts +65 -35
  89. package/src/sdk/router/session-router.ts +7 -6
  90. package/src/sdk/session.ts +2 -7
  91. package/src/session/agent-session.ts +59 -17
  92. package/src/session/artifacts.ts +3 -2
  93. package/src/session/internal/managed-session-scope.ts +403 -81
  94. package/src/session/internal/managed-session-storage.ts +125 -17
  95. package/src/session/session-manager.ts +12 -0
  96. package/src/session/session-storage.ts +30 -1
  97. package/src/skill-state/active-state.ts +13 -0
  98. package/src/skill-state/workflow-mutation-guard.ts +102 -10
  99. package/src/slash-commands/builtin-registry.ts +1 -1
  100. package/src/slash-commands/helpers/mcp.ts +8 -4
  101. package/src/task/isolation-dir.ts +143 -0
  102. package/src/task/worktree.ts +16 -7
  103. package/src/tools/ast-edit-write-path.ts +68 -0
  104. package/src/tools/ast-edit.ts +7 -2
  105. package/src/tools/browser/cdp-url.ts +18 -0
  106. package/src/tools/browser.ts +8 -1
  107. package/src/tools/gh.ts +10 -0
  108. package/src/tools/github-side-effect.ts +112 -0
  109. package/src/tools/monitor-mutation.ts +27 -0
  110. package/src/tools/monitor.ts +7 -0
  111. package/src/tools/python.ts +7 -6
  112. package/src/utils/external-editor.ts +3 -2
  113. package/src/web/insane/url-guard.ts +2 -1
@@ -2543,6 +2543,30 @@ function managedGcScopeReader(scope: ManagedScope, trusted: ManagedGcTrustedScop
2543
2543
  "read-only",
2544
2544
  );
2545
2545
  }
2546
+
2547
+ function managedGcWithClosedReader<T>(store: ManagedSessionDescendantStore, operation: () => T): T {
2548
+ let result: T | undefined;
2549
+ let operationError: unknown;
2550
+ let hasOperationError = false;
2551
+ try {
2552
+ result = operation();
2553
+ } catch (error) {
2554
+ operationError = error;
2555
+ hasOperationError = true;
2556
+ }
2557
+ let closeError: unknown;
2558
+ let hasCloseError = false;
2559
+ try {
2560
+ store.close();
2561
+ } catch (error) {
2562
+ closeError = error;
2563
+ hasCloseError = true;
2564
+ }
2565
+ if (hasOperationError) throw operationError;
2566
+ if (hasCloseError) throw closeError;
2567
+ return result as T;
2568
+ }
2569
+
2546
2570
  function managedGcRetirementTranscriptKey(transcriptPath: string): string {
2547
2571
  return createHash("sha256").update(path.resolve(transcriptPath), "utf8").digest("hex");
2548
2572
  }
@@ -2570,9 +2594,52 @@ interface ManagedGcDirectoryEvidence {
2570
2594
  interface ManagedGcDirectoryInventory {
2571
2595
  readonly evidence: ManagedGcDirectoryEvidence;
2572
2596
  readonly entryCount: number;
2597
+ readonly physicalEntryCount: number;
2573
2598
  readonly entries: readonly fs.Dirent[];
2574
2599
  }
2575
2600
 
2601
+ const MANAGED_GC_INVENTORY_MAX_WORK_UNITS = MANAGED_ARTIFACT_MAX_FILES * 2;
2602
+
2603
+ type ManagedGcInventoryOperation =
2604
+ | { readonly kind: "inspection"; workUnits: number; observedEntries: number }
2605
+ | { readonly kind: "leased-journal"; readonly lock: ManagedStorageLock };
2606
+
2607
+ function newManagedGcInspectionOperation(): ManagedGcInventoryOperation {
2608
+ return { kind: "inspection", workUnits: 0, observedEntries: 0 };
2609
+ }
2610
+
2611
+ function managedGcReserveInventoryWork(operation: ManagedGcInventoryOperation, amount = 1): void {
2612
+ if (!Number.isSafeInteger(amount) || amount < 0) managedGcJournalCapacity();
2613
+ if (operation.kind === "leased-journal") {
2614
+ operation.lock.assertOwned();
2615
+ return;
2616
+ }
2617
+ if (!Number.isSafeInteger(operation.workUnits) || operation.workUnits > MANAGED_GC_INVENTORY_MAX_WORK_UNITS - amount)
2618
+ managedGcJournalCapacity();
2619
+ operation.workUnits += amount;
2620
+ }
2621
+
2622
+ function managedGcComponentInputAllowance(pathname: string): number {
2623
+ if (typeof pathname !== "string") managedGcJournalCapacity();
2624
+ const allowance = pathname.length + 1;
2625
+ if (!Number.isSafeInteger(allowance)) managedGcJournalCapacity();
2626
+ return allowance;
2627
+ }
2628
+
2629
+ function managedGcReserveScopeComponentInputs(operation: ManagedGcInventoryOperation, scope: ManagedScope): void {
2630
+ managedGcReserveInventoryWork(operation, managedGcComponentInputAllowance(scope.canonicalCwd));
2631
+ managedGcReserveInventoryWork(operation, managedGcComponentInputAllowance(scope.agentDir));
2632
+ managedGcReserveInventoryWork(operation, managedGcComponentInputAllowance(scope.sessionsRoot));
2633
+ managedGcReserveInventoryWork(operation, managedGcComponentInputAllowance(scope.directoryPath));
2634
+ }
2635
+
2636
+ function managedGcAdmitInventoryEntry(operation: ManagedGcInventoryOperation): void {
2637
+ if (operation.kind === "inspection" && operation.observedEntries >= MANAGED_ARTIFACT_MAX_FILES)
2638
+ managedGcJournalCapacity();
2639
+ managedGcReserveInventoryWork(operation);
2640
+ if (operation.kind === "inspection") operation.observedEntries++;
2641
+ }
2642
+
2576
2643
  interface ManagedGcReceiptHistory {
2577
2644
  readonly entryCount: number;
2578
2645
  readonly encodedBytes: number;
@@ -2624,29 +2691,59 @@ function managedGcStreamDirectoryInventory(
2624
2691
  pathname: string,
2625
2692
  countEntry: (entry: fs.Dirent) => boolean,
2626
2693
  retain: (entry: fs.Dirent) => boolean,
2627
- capacity: () => never = managedGcJournalCapacity,
2694
+ operation: ManagedGcInventoryOperation,
2628
2695
  ): ManagedGcDirectoryInventory {
2696
+ managedGcReserveInventoryWork(operation);
2629
2697
  const beforeStat = fs.lstatSync(pathname, { bigint: true });
2630
2698
  if (!beforeStat.isDirectory() || beforeStat.isSymbolicLink()) throw new Error("managed_gc_scope_authority_mismatch");
2631
2699
  const before = managedGcDirectoryEvidence(beforeStat);
2632
- const scan = (collect: boolean): { count: number; entries: fs.Dirent[] } => {
2700
+ const scan = (collect: boolean): { count: number; physicalEntryCount: number; entries: fs.Dirent[] } => {
2701
+ managedGcReserveInventoryWork(operation);
2633
2702
  const entries: fs.Dirent[] = [];
2634
2703
  let count = 0;
2704
+ let physicalEntryCount = 0;
2635
2705
  let retained = 0;
2636
- const directory = fs.opendirSync(pathname);
2706
+ // Reserve open/close plus conservative processing for the dot entries Node suppresses.
2707
+ managedGcReserveInventoryWork(operation, 4);
2708
+ let directory: fs.Dir | undefined;
2709
+ let primaryError: unknown;
2710
+ let hasPrimaryError = false;
2637
2711
  try {
2638
- for (let entry = directory.readSync(); entry; entry = directory.readSync()) {
2639
- if (countEntry(entry) && ++count > MANAGED_ARTIFACT_MAX_FILES) capacity();
2712
+ directory = fs.opendirSync(pathname, { bufferSize: 1 });
2713
+ for (;;) {
2714
+ managedGcReserveInventoryWork(operation);
2715
+ const entry = directory.readSync();
2716
+ if (!entry) break;
2717
+ managedGcAdmitInventoryEntry(operation);
2718
+ if (!Number.isSafeInteger(physicalEntryCount + 1)) managedGcJournalCapacity();
2719
+ physicalEntryCount++;
2720
+ if (countEntry(entry) && ++count > MANAGED_ARTIFACT_MAX_FILES) managedGcJournalCapacity();
2640
2721
  if (!retain(entry)) continue;
2641
- if (++retained > MANAGED_ARTIFACT_MAX_FILES) capacity();
2642
- if (collect) entries.push(entry);
2722
+ if (++retained > MANAGED_ARTIFACT_MAX_FILES) managedGcJournalCapacity();
2723
+ if (collect) {
2724
+ managedGcReserveInventoryWork(operation);
2725
+ entries.push(entry);
2726
+ }
2643
2727
  }
2644
- } finally {
2645
- directory.closeSync();
2728
+ } catch (error) {
2729
+ primaryError = error;
2730
+ hasPrimaryError = true;
2646
2731
  }
2647
- return { count, entries };
2732
+ let closeError: unknown;
2733
+ if (directory) {
2734
+ try {
2735
+ directory.closeSync();
2736
+ } catch (error) {
2737
+ closeError = error;
2738
+ }
2739
+ }
2740
+ if (hasPrimaryError) throw primaryError;
2741
+ if (closeError !== undefined) throw closeError;
2742
+ managedGcReserveInventoryWork(operation);
2743
+ return { count, physicalEntryCount, entries };
2648
2744
  };
2649
2745
  const counted = scan(false);
2746
+ managedGcReserveInventoryWork(operation);
2650
2747
  const afterCount = fs.lstatSync(pathname, { bigint: true });
2651
2748
  if (
2652
2749
  !afterCount.isDirectory() ||
@@ -2655,30 +2752,45 @@ function managedGcStreamDirectoryInventory(
2655
2752
  )
2656
2753
  throw new Error("managed_gc_scope_authority_mismatch");
2657
2754
  const collected = scan(true);
2755
+ managedGcReserveInventoryWork(operation);
2658
2756
  const afterCollect = fs.lstatSync(pathname, { bigint: true });
2659
2757
  if (
2660
2758
  collected.count !== counted.count ||
2759
+ collected.physicalEntryCount !== counted.physicalEntryCount ||
2661
2760
  !afterCollect.isDirectory() ||
2662
2761
  afterCollect.isSymbolicLink() ||
2663
2762
  !managedGcSameDirectoryEvidence(before, managedGcDirectoryEvidence(afterCollect))
2664
2763
  )
2665
2764
  throw new Error("managed_gc_scope_authority_mismatch");
2765
+ managedGcReserveInventoryWork(operation, collected.entries.length);
2666
2766
  collected.entries.sort((left, right) => left.name.localeCompare(right.name));
2667
- return { evidence: before, entryCount: counted.count, entries: collected.entries };
2767
+ return {
2768
+ evidence: before,
2769
+ entryCount: counted.count,
2770
+ physicalEntryCount: counted.physicalEntryCount,
2771
+ entries: collected.entries,
2772
+ };
2668
2773
  }
2669
2774
 
2670
2775
  function managedGcReceiptDirectoryInventory(
2671
2776
  scope: ManagedScope,
2672
2777
  store: ManagedSessionDescendantStore,
2778
+ operation: ManagedGcInventoryOperation,
2673
2779
  ): ManagedGcDirectoryInventory {
2674
2780
  const relativePath = MANAGED_GC_RETIREMENT_RECEIPTS;
2781
+ managedGcReserveInventoryWork(
2782
+ operation,
2783
+ managedGcComponentInputAllowance(scope.directoryPath) + relativePath.length,
2784
+ );
2675
2785
  const pathname = path.join(scope.directoryPath, relativePath);
2676
2786
  const before = store.captureDirectoryIdentity(relativePath);
2677
2787
  const inventory = managedGcStreamDirectoryInventory(
2678
2788
  pathname,
2679
2789
  entry => entry.name.startsWith(MANAGED_GC_RETIREMENT_PREFIX),
2680
2790
  entry => entry.name.startsWith(MANAGED_GC_RETIREMENT_PREFIX),
2791
+ operation,
2681
2792
  );
2793
+ managedGcReserveInventoryWork(operation);
2682
2794
  const after = store.captureDirectoryIdentity(relativePath);
2683
2795
  if (
2684
2796
  before.dev !== after.dev ||
@@ -2687,6 +2799,7 @@ function managedGcReceiptDirectoryInventory(
2687
2799
  before.ino !== inventory.evidence.ino
2688
2800
  )
2689
2801
  throw new Error("task_artifact_owner_continuation_authority_mismatch");
2802
+ managedGcReserveInventoryWork(operation);
2690
2803
  store.assertBound();
2691
2804
  return inventory;
2692
2805
  }
@@ -3088,7 +3201,7 @@ function readManagedGcSessionRetirementReceiptUnlocked(
3088
3201
  transcriptPathValue: string,
3089
3202
  trusted: ManagedGcTrustedScope,
3090
3203
  store: ManagedSessionDescendantStore,
3091
- lock?: ManagedStorageLock,
3204
+ operation: ManagedGcInventoryOperation,
3092
3205
  inventory?: ManagedGcDirectoryInventory,
3093
3206
  targetNames?: readonly string[],
3094
3207
  preparedBootstrap?: {
@@ -3101,11 +3214,12 @@ function readManagedGcSessionRetirementReceiptUnlocked(
3101
3214
  readonly prepared: ManagedGcSessionRetirementReceipt | undefined;
3102
3215
  readonly history: ManagedGcReceiptHistory;
3103
3216
  } {
3217
+ managedGcReserveInventoryWork(operation, managedGcComponentInputAllowance(transcriptPathValue));
3104
3218
  const transcriptPath = validateManagedGcTranscriptPath(scope, transcriptPathValue);
3105
- lock?.assertOwned();
3219
+ managedGcReserveInventoryWork(operation);
3106
3220
  store.assertBound();
3107
- const receiptInventory = inventory ?? managedGcReceiptDirectoryInventory(scope, store);
3108
- const names = targetNames ?? managedGcReceiptNames(scope, transcriptPath, store, receiptInventory);
3221
+ const receiptInventory = inventory ?? managedGcReceiptDirectoryInventory(scope, store, operation);
3222
+ const names = targetNames ?? managedGcReceiptNames(scope, transcriptPath, store, operation, receiptInventory);
3109
3223
  if (names.length > MANAGED_ARTIFACT_MAX_FILES) managedGcJournalCapacity();
3110
3224
  const prefix = `${MANAGED_GC_RETIREMENT_PREFIX}${managedGcRetirementTranscriptKey(transcriptPath)}-`;
3111
3225
  const pendingPrefix = `${prefix}owner_pending-`;
@@ -3121,7 +3235,7 @@ function readManagedGcSessionRetirementReceiptUnlocked(
3121
3235
  if (!names.includes(preparedName)) {
3122
3236
  if (names.length > 0) throw new Error("task_artifact_owner_continuation_state_missing");
3123
3237
  if (!inventory) {
3124
- const after = managedGcReceiptDirectoryInventory(scope, store);
3238
+ const after = managedGcReceiptDirectoryInventory(scope, store, operation);
3125
3239
  if (!managedGcSameReceiptDirectoryInventory(receiptInventory, after))
3126
3240
  throw new Error("task_artifact_owner_continuation_authority_mismatch");
3127
3241
  }
@@ -3140,6 +3254,7 @@ function readManagedGcSessionRetirementReceiptUnlocked(
3140
3254
  }
3141
3255
  const target = prepared;
3142
3256
  assertManagedGcTranscriptUnchangedOrAbsent(scope, target, true);
3257
+ managedGcReserveInventoryWork(operation, names.length);
3143
3258
  const nameSet = new Set(names);
3144
3259
  if (!nameSet.has(preparedName)) throw new Error("task_artifact_owner_continuation_state_missing");
3145
3260
  const readState = (state: ManagedGcSessionRetirementState, attempt?: number) =>
@@ -3162,6 +3277,7 @@ function readManagedGcSessionRetirementReceiptUnlocked(
3162
3277
  (nameSet.has(retiredName) && ownerRetired?.state !== "owner_retired")
3163
3278
  )
3164
3279
  throw new Error("task_artifact_owner_continuation_state_missing");
3280
+ managedGcReserveInventoryWork(operation, names.length);
3165
3281
  const attempts = names
3166
3282
  .filter(name => name.startsWith(pendingPrefix))
3167
3283
  .map(name => {
@@ -3215,7 +3331,7 @@ function readManagedGcSessionRetirementReceiptUnlocked(
3215
3331
  );
3216
3332
  }
3217
3333
  if (!inventory) {
3218
- const after = managedGcReceiptDirectoryInventory(scope, store);
3334
+ const after = managedGcReceiptDirectoryInventory(scope, store, operation);
3219
3335
  if (!managedGcSameReceiptDirectoryInventory(receiptInventory, after))
3220
3336
  throw new Error("task_artifact_owner_continuation_authority_mismatch");
3221
3337
  }
@@ -3226,14 +3342,16 @@ function managedGcReceiptNames(
3226
3342
  scope: ManagedScope,
3227
3343
  transcriptPath: string,
3228
3344
  store: ManagedSessionDescendantStore,
3345
+ operation: ManagedGcInventoryOperation,
3229
3346
  inventory?: ManagedGcDirectoryInventory,
3230
3347
  ): string[] {
3231
3348
  const prefix = `${MANAGED_GC_RETIREMENT_PREFIX}${managedGcRetirementTranscriptKey(transcriptPath)}-`;
3232
- const matching = (inventory ?? managedGcReceiptDirectoryInventory(scope, store)).entries.filter(entry =>
3233
- entry.name.startsWith(prefix),
3234
- );
3349
+ const directoryInventory = inventory ?? managedGcReceiptDirectoryInventory(scope, store, operation);
3350
+ managedGcReserveInventoryWork(operation, directoryInventory.entries.length);
3351
+ const matching = directoryInventory.entries.filter(entry => entry.name.startsWith(prefix));
3235
3352
  if (matching.some(entry => !entry.isFile() || entry.isSymbolicLink()))
3236
3353
  throw new Error("task_artifact_owner_continuation_corrupt");
3354
+ managedGcReserveInventoryWork(operation, matching.length);
3237
3355
  const names = matching.map(entry => entry.name);
3238
3356
  const pendingPrefix = `${prefix}owner_pending-`;
3239
3357
  for (const name of names) {
@@ -3254,6 +3372,7 @@ function managedGcSameReceiptDirectoryInventory(
3254
3372
  ): boolean {
3255
3373
  return (
3256
3374
  left.entryCount === right.entryCount &&
3375
+ left.physicalEntryCount === right.physicalEntryCount &&
3257
3376
  managedGcSameDirectoryEvidence(left.evidence, right.evidence) &&
3258
3377
  left.entries.length === right.entries.length &&
3259
3378
  left.entries.every(
@@ -3273,14 +3392,16 @@ function managedGcSameReceiptDirectoryInventory(
3273
3392
  function managedGcOptionalReceiptDirectoryInventory(
3274
3393
  scope: ManagedScope,
3275
3394
  store: ManagedSessionDescendantStore,
3395
+ operation: ManagedGcInventoryOperation,
3276
3396
  ): ManagedGcDirectoryInventory | undefined {
3277
3397
  try {
3398
+ managedGcReserveInventoryWork(operation);
3278
3399
  store.captureDirectoryIdentity(MANAGED_GC_RETIREMENT_RECEIPTS);
3279
3400
  } catch (error) {
3280
3401
  if (hasFsCode(error, "ENOENT")) return undefined;
3281
3402
  throw error;
3282
3403
  }
3283
- return managedGcReceiptDirectoryInventory(scope, store);
3404
+ return managedGcReceiptDirectoryInventory(scope, store, operation);
3284
3405
  }
3285
3406
 
3286
3407
  function managedGcDiscoveryScopeFence(
@@ -3289,22 +3410,33 @@ function managedGcDiscoveryScopeFence(
3289
3410
  scopeIdentity: { readonly dev: string; readonly ino: string },
3290
3411
  binding: ManagedFileSnapshot,
3291
3412
  openReader: () => ManagedSessionDescendantStore,
3413
+ operation: ManagedGcInventoryOperation,
3292
3414
  ): ManagedGcDiscoveryScopeFence {
3415
+ managedGcReserveInventoryWork(operation);
3293
3416
  const internalDirectoryIdentity = store.captureDirectoryIdentity(MANAGED_INTERNAL_DIRECTORY);
3417
+ managedGcReserveInventoryWork(operation);
3294
3418
  return {
3295
3419
  scope,
3296
3420
  scopeIdentity,
3297
3421
  binding,
3298
3422
  internalDirectoryIdentity,
3299
- receiptInventory: managedGcOptionalReceiptDirectoryInventory(scope, store),
3423
+ receiptInventory: managedGcOptionalReceiptDirectoryInventory(scope, store, operation),
3300
3424
  openReader,
3301
3425
  };
3302
3426
  }
3303
3427
 
3304
- function managedGcAssertDiscoveryScopeFence(fence: ManagedGcDiscoveryScopeFence): void {
3428
+ function managedGcAssertDiscoveryScopeFence(
3429
+ fence: ManagedGcDiscoveryScopeFence,
3430
+ operation: ManagedGcInventoryOperation,
3431
+ ): void {
3432
+ managedGcReserveInventoryWork(operation);
3305
3433
  const store = fence.openReader();
3434
+ let primaryError: unknown;
3435
+ let hasPrimaryError = false;
3306
3436
  try {
3437
+ managedGcReserveInventoryWork(operation);
3307
3438
  store.verifyRootSecurity();
3439
+ managedGcReserveInventoryWork(operation);
3308
3440
  const currentScope = fs.lstatSync(fence.scope.directoryPath, { bigint: true });
3309
3441
  if (
3310
3442
  !currentScope.isDirectory() ||
@@ -3313,6 +3445,7 @@ function managedGcAssertDiscoveryScopeFence(fence: ManagedGcDiscoveryScopeFence)
3313
3445
  currentScope.ino.toString() !== fence.scopeIdentity.ino
3314
3446
  )
3315
3447
  throw new Error("managed_gc_scope_authority_mismatch");
3448
+ managedGcReserveInventoryWork(operation);
3316
3449
  const currentBinding = store.readExpected(MANAGED_SESSION_BINDING_FILE);
3317
3450
  if (
3318
3451
  !currentBinding ||
@@ -3320,13 +3453,14 @@ function managedGcAssertDiscoveryScopeFence(fence: ManagedGcDiscoveryScopeFence)
3320
3453
  !currentBinding.bytes.equals(fence.binding.bytes)
3321
3454
  )
3322
3455
  throw new Error("managed_gc_scope_authority_mismatch");
3456
+ managedGcReserveInventoryWork(operation);
3323
3457
  const internalDirectoryIdentity = store.captureDirectoryIdentity(MANAGED_INTERNAL_DIRECTORY);
3324
3458
  if (
3325
3459
  internalDirectoryIdentity.dev !== fence.internalDirectoryIdentity.dev ||
3326
3460
  internalDirectoryIdentity.ino !== fence.internalDirectoryIdentity.ino
3327
3461
  )
3328
3462
  throw new Error("managed_gc_scope_authority_mismatch");
3329
- const currentReceipts = managedGcOptionalReceiptDirectoryInventory(fence.scope, store);
3463
+ const currentReceipts = managedGcOptionalReceiptDirectoryInventory(fence.scope, store, operation);
3330
3464
  if (
3331
3465
  (fence.receiptInventory === undefined) !== (currentReceipts === undefined) ||
3332
3466
  (fence.receiptInventory !== undefined &&
@@ -3334,18 +3468,29 @@ function managedGcAssertDiscoveryScopeFence(fence: ManagedGcDiscoveryScopeFence)
3334
3468
  !managedGcSameReceiptDirectoryInventory(fence.receiptInventory, currentReceipts))
3335
3469
  )
3336
3470
  throw new Error("task_artifact_owner_continuation_authority_mismatch");
3471
+ managedGcReserveInventoryWork(operation);
3337
3472
  store.assertBound();
3338
- } finally {
3473
+ } catch (error) {
3474
+ primaryError = error;
3475
+ hasPrimaryError = true;
3476
+ }
3477
+ let closeError: unknown;
3478
+ try {
3339
3479
  store.close();
3480
+ } catch (error) {
3481
+ closeError = error;
3340
3482
  }
3483
+ if (hasPrimaryError) throw primaryError;
3484
+ if (closeError !== undefined) throw closeError;
3341
3485
  }
3342
3486
 
3343
3487
  function assertManagedGcReceiptHistoryDirectoryUnchanged(
3344
3488
  scope: ManagedScope,
3345
3489
  store: ManagedSessionDescendantStore,
3346
3490
  history: ManagedGcReceiptHistory,
3491
+ operation: ManagedGcInventoryOperation,
3347
3492
  ): void {
3348
- const current = managedGcReceiptDirectoryInventory(scope, store);
3493
+ const current = managedGcReceiptDirectoryInventory(scope, store, operation);
3349
3494
  if (
3350
3495
  !managedGcSameReceiptDirectoryInventory(history.directoryInventory, current) ||
3351
3496
  current.entryCount !== history.directoryEntryCount ||
@@ -3370,6 +3515,7 @@ async function withManagedGcRetirementJournal<T>(
3370
3515
  let operationError: unknown;
3371
3516
  let result: T | undefined;
3372
3517
  let operationCompleted = false;
3518
+ let hasOperationError = false;
3373
3519
  try {
3374
3520
  store.verifyRootSecurity();
3375
3521
  store.ensureDirectory(MANAGED_GC_RETIREMENT_RECEIPTS);
@@ -3386,23 +3532,34 @@ async function withManagedGcRetirementJournal<T>(
3386
3532
  operationCompleted = true;
3387
3533
  } catch (error) {
3388
3534
  operationError = error;
3535
+ hasOperationError = true;
3389
3536
  }
3390
3537
  let releaseError: unknown;
3538
+ let hasReleaseError = false;
3391
3539
  if (lock) {
3392
3540
  try {
3393
3541
  await lock.release();
3394
3542
  } catch (error) {
3395
3543
  releaseError = error;
3544
+ hasReleaseError = true;
3396
3545
  }
3397
3546
  }
3398
- store.close();
3399
- if (operationError !== undefined && releaseError !== undefined)
3547
+ let closeError: unknown;
3548
+ let hasCloseError = false;
3549
+ try {
3550
+ store.close();
3551
+ } catch (error) {
3552
+ closeError = error;
3553
+ hasCloseError = true;
3554
+ }
3555
+ if (hasOperationError && (hasReleaseError || hasCloseError))
3400
3556
  throw new AggregateError(
3401
- [operationError, releaseError],
3402
- "managed_gc_retirement_operation_and_lock_release_failed",
3557
+ [operationError, ...(hasReleaseError ? [releaseError] : []), ...(hasCloseError ? [closeError] : [])],
3558
+ "managed_gc_retirement_operation_and_cleanup_failed",
3403
3559
  );
3404
- if (operationError !== undefined) throw operationError;
3405
- if (releaseError !== undefined) throw releaseError;
3560
+ if (hasOperationError) throw operationError;
3561
+ if (hasReleaseError) throw releaseError;
3562
+ if (hasCloseError) throw closeError;
3406
3563
  if (!operationCompleted) throw new Error("managed_gc_retirement_result_missing");
3407
3564
  return result as T;
3408
3565
  }
@@ -3413,7 +3570,10 @@ export async function readManagedGcSessionRetirementReceipt(
3413
3570
  transcriptPath: string,
3414
3571
  ): Promise<ManagedGcSessionRetirementReceipt | undefined> {
3415
3572
  const result = await withManagedGcRetirementJournal(scope, transcriptPath, (trusted, store, lock) =>
3416
- readManagedGcSessionRetirementReceiptUnlocked(scope, transcriptPath, trusted, store, lock),
3573
+ readManagedGcSessionRetirementReceiptUnlocked(scope, transcriptPath, trusted, store, {
3574
+ kind: "leased-journal",
3575
+ lock,
3576
+ }),
3417
3577
  );
3418
3578
  return result.receipt;
3419
3579
  }
@@ -3423,44 +3583,78 @@ export async function readManagedGcSessionRetirementReceiptReadOnly(
3423
3583
  scope: ManagedScope,
3424
3584
  transcriptPath: string,
3425
3585
  ): Promise<ManagedGcSessionRetirementReceipt | undefined> {
3586
+ const operation = newManagedGcInspectionOperation();
3587
+ managedGcReserveScopeComponentInputs(operation, scope);
3588
+ managedGcReserveInventoryWork(operation, managedGcComponentInputAllowance(transcriptPath));
3426
3589
  const trusted = managedGcTrustedScope(scope);
3427
3590
  validateManagedGcTranscriptPath(scope, transcriptPath);
3591
+ managedGcReserveInventoryWork(operation, 2);
3428
3592
  const store = managedGcScopeReader(scope, trusted);
3593
+ let receipt: ManagedGcSessionRetirementReceipt | undefined;
3594
+ let primaryError: unknown;
3595
+ let hasPrimaryError = false;
3429
3596
  try {
3597
+ managedGcReserveInventoryWork(operation);
3430
3598
  store.verifyRootSecurity();
3599
+ managedGcReserveInventoryWork(operation);
3431
3600
  store.assertBound();
3601
+ let receiptDirectoryPresent = true;
3432
3602
  try {
3603
+ managedGcReserveInventoryWork(operation);
3433
3604
  store.captureDirectoryIdentity(MANAGED_GC_RETIREMENT_RECEIPTS);
3434
3605
  } catch (error) {
3435
- if (hasFsCode(error, "ENOENT")) return undefined;
3436
- throw error;
3606
+ if (!hasFsCode(error, "ENOENT")) throw error;
3607
+ receiptDirectoryPresent = false;
3437
3608
  }
3438
- return readManagedGcSessionRetirementReceiptUnlocked(scope, transcriptPath, trusted, store).receipt;
3439
- } finally {
3609
+ if (receiptDirectoryPresent)
3610
+ receipt = readManagedGcSessionRetirementReceiptUnlocked(
3611
+ scope,
3612
+ transcriptPath,
3613
+ trusted,
3614
+ store,
3615
+ operation,
3616
+ ).receipt;
3617
+ } catch (error) {
3618
+ primaryError = error;
3619
+ hasPrimaryError = true;
3620
+ }
3621
+ let closeError: unknown;
3622
+ try {
3440
3623
  store.close();
3624
+ } catch (error) {
3625
+ closeError = error;
3441
3626
  }
3627
+ if (hasPrimaryError) throw primaryError;
3628
+ if (closeError !== undefined) throw closeError;
3629
+ return receipt;
3442
3630
  }
3443
3631
 
3444
3632
  function hasManagedGcRetirementJournalWithoutWorkspace(
3445
3633
  scope: ManagedScope,
3634
+ operation: ManagedGcInventoryOperation,
3446
3635
  onEmptyInventory?: (fence: ManagedGcDiscoveryScopeFence) => void,
3447
3636
  ): boolean {
3637
+ managedGcReserveScopeComponentInputs(operation, scope);
3448
3638
  if (
3449
3639
  path.resolve(scope.canonicalCwd) !== scope.canonicalCwd ||
3450
3640
  scope.platform !== (process.platform === "win32" ? "win32" : "posix") ||
3451
3641
  scope.directoryName !== `v2-${scopeDigest(scope.platform, scope.canonicalCwd)}`
3452
3642
  )
3453
3643
  throw new Error("managed_gc_scope_authority_mismatch");
3644
+ managedGcReserveInventoryWork(operation);
3454
3645
  const identity = fs.lstatSync(scope.directoryPath, { bigint: true });
3455
3646
  if (!identity.isDirectory() || identity.isSymbolicLink()) throw new Error("managed_gc_scope_authority_mismatch");
3456
3647
  const rootPath = configuredRootPath(scope);
3648
+ managedGcReserveInventoryWork(operation);
3457
3649
  const rootIdentity = fs.lstatSync(rootPath, { bigint: true });
3458
3650
  const assertRoot = (): void => {
3651
+ managedGcReserveInventoryWork(operation);
3459
3652
  const security = validateNativeSecurityResult(
3460
3653
  verifyExistingManagedScopeDirectory(rootPath),
3461
3654
  "verify",
3462
3655
  "directory",
3463
3656
  );
3657
+ managedGcReserveInventoryWork(operation);
3464
3658
  const current = fs.lstatSync(rootPath, { bigint: true });
3465
3659
  if (
3466
3660
  !security.ok ||
@@ -3472,12 +3666,14 @@ function hasManagedGcRetirementJournalWithoutWorkspace(
3472
3666
  throw new Error("managed_gc_scope_authority_mismatch");
3473
3667
  };
3474
3668
  assertRoot();
3669
+ managedGcReserveInventoryWork(operation);
3475
3670
  const rootAuthority = managedDirectoryRoot(rootPath);
3476
3671
  if (
3477
3672
  rootAuthority.dev !== BigInt.asUintN(64, rootIdentity.dev) ||
3478
3673
  rootAuthority.ino !== BigInt.asUintN(64, rootIdentity.ino)
3479
3674
  )
3480
3675
  throw new Error("managed_gc_scope_authority_mismatch");
3676
+ managedGcReserveInventoryWork(operation, 2);
3481
3677
  const store = new ManagedSessionDescendantStore(
3482
3678
  rootAuthority,
3483
3679
  scope.directoryPath,
@@ -3491,20 +3687,26 @@ function hasManagedGcRetirementJournalWithoutWorkspace(
3491
3687
  },
3492
3688
  "read-only",
3493
3689
  );
3690
+ let result: boolean | undefined;
3691
+ let primaryError: unknown;
3692
+ let hasPrimaryError = false;
3494
3693
  try {
3694
+ managedGcReserveInventoryWork(operation);
3495
3695
  store.verifyRootSecurity();
3696
+ managedGcReserveInventoryWork(operation);
3496
3697
  const binding = store.readExpected(MANAGED_SESSION_BINDING_FILE);
3497
3698
  if (!binding || validateBindingRaw(scope, binding.bytes.toString("utf8")))
3498
3699
  throw new Error("managed_gc_scope_authority_mismatch");
3499
3700
  const readNames = (): ManagedGcDirectoryInventory | undefined => {
3500
3701
  try {
3501
- return managedGcReceiptDirectoryInventory(scope, store);
3702
+ return managedGcReceiptDirectoryInventory(scope, store, operation);
3502
3703
  } catch (error) {
3503
3704
  if (hasFsCode(error, "ENOENT")) return undefined;
3504
3705
  throw error;
3505
3706
  }
3506
3707
  };
3507
3708
  const before = readNames();
3709
+ managedGcReserveInventoryWork(operation);
3508
3710
  const currentBinding = store.readExpected(MANAGED_SESSION_BINDING_FILE);
3509
3711
  const after = readNames();
3510
3712
  if (
@@ -3515,21 +3717,25 @@ function hasManagedGcRetirementJournalWithoutWorkspace(
3515
3717
  (before !== undefined && after !== undefined && !managedGcSameReceiptDirectoryInventory(before, after))
3516
3718
  )
3517
3719
  throw new Error("managed_gc_scope_authority_mismatch");
3720
+ managedGcReserveInventoryWork(operation);
3518
3721
  store.verifyRootSecurity();
3519
3722
  assertRoot();
3520
3723
  const hasJournal = before?.entries.some(entry => entry.name.startsWith(MANAGED_GC_RETIREMENT_PREFIX)) ?? false;
3521
3724
  if (!hasJournal && onEmptyInventory) {
3725
+ managedGcReserveInventoryWork(operation);
3522
3726
  const internalDirectoryIdentity = store.captureDirectoryIdentity(MANAGED_INTERNAL_DIRECTORY);
3523
3727
  const policy: ManagedSessionSecurityPolicy =
3524
3728
  scope.platform === "win32" ? "windows-existing-verify-first" : "default";
3729
+ managedGcReserveInventoryWork(operation, 2);
3525
3730
  onEmptyInventory({
3526
3731
  scope,
3527
3732
  scopeIdentity: { dev: identity.dev.toString(), ino: identity.ino.toString() },
3528
3733
  binding,
3529
3734
  internalDirectoryIdentity,
3530
3735
  receiptInventory: before,
3531
- openReader: () =>
3532
- new ManagedSessionDescendantStore(
3736
+ openReader: () => {
3737
+ managedGcReserveInventoryWork(operation, 2);
3738
+ return new ManagedSessionDescendantStore(
3533
3739
  rootAuthority,
3534
3740
  scope.directoryPath,
3535
3741
  undefined,
@@ -3541,13 +3747,24 @@ function hasManagedGcRetirementJournalWithoutWorkspace(
3541
3747
  ino: BigInt.asUintN(64, identity.ino),
3542
3748
  },
3543
3749
  "read-only",
3544
- ),
3750
+ );
3751
+ },
3545
3752
  });
3546
3753
  }
3547
- return hasJournal;
3548
- } finally {
3754
+ result = hasJournal;
3755
+ } catch (error) {
3756
+ primaryError = error;
3757
+ hasPrimaryError = true;
3758
+ }
3759
+ let closeError: unknown;
3760
+ try {
3549
3761
  store.close();
3762
+ } catch (error) {
3763
+ closeError = error;
3550
3764
  }
3765
+ if (hasPrimaryError) throw primaryError;
3766
+ if (closeError !== undefined) throw closeError;
3767
+ return result as boolean;
3551
3768
  }
3552
3769
 
3553
3770
  /** Discover complete owner journals under authenticated existing v2 scopes without initializing storage. */
@@ -3555,15 +3772,28 @@ export async function discoverManagedGcSessionRetirementReceipts(input: {
3555
3772
  agentDir: string;
3556
3773
  sessionsRoot: string;
3557
3774
  }): Promise<readonly { readonly scope: ManagedScope; readonly receipt: ManagedGcSessionRetirementReceipt }[]> {
3775
+ const operation = newManagedGcInspectionOperation();
3776
+ return discoverManagedGcSessionRetirementReceiptsWithOperation(input, operation);
3777
+ }
3778
+
3779
+ async function discoverManagedGcSessionRetirementReceiptsWithOperation(
3780
+ input: { agentDir: string; sessionsRoot: string },
3781
+ operation: ManagedGcInventoryOperation,
3782
+ ): Promise<readonly { readonly scope: ManagedScope; readonly receipt: ManagedGcSessionRetirementReceipt }[]> {
3783
+ managedGcReserveInventoryWork(operation, managedGcComponentInputAllowance(input.agentDir));
3558
3784
  const agentDir = canonicalizeTrustedPath(input.agentDir);
3785
+ managedGcReserveInventoryWork(operation, managedGcComponentInputAllowance(input.sessionsRoot));
3559
3786
  const sessionsRoot = canonicalizeTrustedPath(input.sessionsRoot);
3787
+ managedGcReserveInventoryWork(operation);
3560
3788
  const profileStat = fs.lstatSync(agentDir, { bigint: true });
3789
+ managedGcReserveInventoryWork(operation);
3561
3790
  const rootStat = fs.lstatSync(sessionsRoot, { bigint: true });
3562
3791
  for (const [pathname, stat] of [
3563
3792
  [agentDir, profileStat],
3564
3793
  [sessionsRoot, rootStat],
3565
3794
  ] as const) {
3566
3795
  if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error("managed_gc_scope_authority_mismatch");
3796
+ managedGcReserveInventoryWork(operation);
3567
3797
  const verified = validateNativeSecurityResult(
3568
3798
  verifyExistingManagedScopeDirectory(pathname),
3569
3799
  "verify",
@@ -3577,22 +3807,28 @@ export async function discoverManagedGcSessionRetirementReceipts(input: {
3577
3807
  profileDev: profileStat.dev,
3578
3808
  profileIno: profileStat.ino,
3579
3809
  };
3810
+ managedGcReserveInventoryWork(operation);
3580
3811
  const rootInventory = managedGcStreamDirectoryInventory(
3581
3812
  sessionsRoot,
3582
3813
  entry => /^v2-[a-z2-7]{52}$/u.test(entry.name),
3583
3814
  entry => /^v2-[a-z2-7]{52}$/u.test(entry.name),
3815
+ operation,
3584
3816
  );
3585
3817
  if (rootInventory.evidence.dev !== rootStat.dev.toString() || rootInventory.evidence.ino !== rootStat.ino.toString())
3586
3818
  throw new Error("managed_gc_scope_authority_mismatch");
3587
3819
  const entries = rootInventory.entries;
3820
+ managedGcReserveInventoryWork(operation, entries.length);
3588
3821
  const result: Array<{ readonly scope: ManagedScope; readonly receipt: ManagedGcSessionRetirementReceipt }> = [];
3822
+ managedGcReserveInventoryWork(operation, entries.length);
3589
3823
  const scopeFences: ManagedGcDiscoveryScopeFence[] = [];
3590
3824
  // Fail closed for the whole root: a partial inventory cannot prove live sibling absence.
3591
3825
  for (const entry of entries) {
3592
3826
  if (!/^v2-[a-z2-7]{52}$/u.test(entry.name)) continue;
3593
3827
  if (!entry.isDirectory() || entry.isSymbolicLink()) throw new Error("managed_gc_scope_authority_mismatch");
3828
+ managedGcReserveInventoryWork(operation, 2);
3594
3829
  const scopePath = path.join(sessionsRoot, entry.name);
3595
3830
  const bindingPath = path.join(scopePath, MANAGED_SESSION_BINDING_FILE);
3831
+ managedGcReserveInventoryWork(operation);
3596
3832
  const bindingBytes = captureManagedFileNoFollow(bindingPath).bytes;
3597
3833
  let bindingValue: unknown;
3598
3834
  try {
@@ -3602,6 +3838,7 @@ export async function discoverManagedGcSessionRetirementReceipts(input: {
3602
3838
  }
3603
3839
  if (!isBinding(bindingValue) || bindingValue.identityDigest !== entry.name.slice(3))
3604
3840
  throw new Error("managed_gc_scope_authority_mismatch");
3841
+ managedGcReserveInventoryWork(operation, managedGcComponentInputAllowance(bindingValue.canonicalPath));
3605
3842
  const resolved = resolveManagedGcScopeForRead({ cwd: bindingValue.canonicalPath, agentDir, sessionsRoot });
3606
3843
  if (resolved.kind === "error" && resolved.code === "cwd_missing") {
3607
3844
  const storedScope: ManagedScope = {
@@ -3617,7 +3854,8 @@ export async function discoverManagedGcSessionRetirementReceipts(input: {
3617
3854
  platform: bindingValue.platform,
3618
3855
  };
3619
3856
  if (
3620
- !hasManagedGcRetirementJournalWithoutWorkspace(storedScope, fence => {
3857
+ !hasManagedGcRetirementJournalWithoutWorkspace(storedScope, operation, fence => {
3858
+ managedGcReserveInventoryWork(operation);
3621
3859
  scopeFences.push(fence);
3622
3860
  })
3623
3861
  )
@@ -3626,9 +3864,12 @@ export async function discoverManagedGcSessionRetirementReceipts(input: {
3626
3864
  if (resolved.kind !== "resolved" || resolved.scope.directoryPath !== scopePath)
3627
3865
  throw new Error("managed_gc_scope_authority_mismatch");
3628
3866
  const scope = resolved.scope;
3867
+ managedGcReserveScopeComponentInputs(operation, scope);
3629
3868
  const trusted = managedGcTrustedScope(scope);
3869
+ managedGcReserveInventoryWork(operation, 2);
3630
3870
  const store = managedGcScopeReader(scope, trusted);
3631
- try {
3871
+ const hasReceiptDirectory = managedGcWithClosedReader(store, () => {
3872
+ managedGcReserveInventoryWork(operation);
3632
3873
  const binding = store.readExpected(MANAGED_SESSION_BINDING_FILE);
3633
3874
  if (
3634
3875
  !binding ||
@@ -3641,11 +3882,17 @@ export async function discoverManagedGcSessionRetirementReceipts(input: {
3641
3882
  store,
3642
3883
  { dev: trusted.identity.dev.toString(), ino: trusted.identity.ino.toString() },
3643
3884
  binding,
3644
- () => managedGcScopeReader(scope, trusted),
3885
+ () => {
3886
+ managedGcReserveInventoryWork(operation, 2);
3887
+ return managedGcScopeReader(scope, trusted);
3888
+ },
3889
+ operation,
3645
3890
  );
3891
+ managedGcReserveInventoryWork(operation);
3646
3892
  scopeFences.push(scopeFence);
3647
3893
  const receiptInventory = scopeFence.receiptInventory;
3648
- if (!receiptInventory) continue;
3894
+ if (!receiptInventory) return false;
3895
+ managedGcReserveInventoryWork(operation, receiptInventory.physicalEntryCount);
3649
3896
  const groups = new Map<string, string[]>();
3650
3897
  for (const item of receiptInventory.entries) {
3651
3898
  if (!item.name.startsWith(MANAGED_GC_RETIREMENT_PREFIX)) continue;
@@ -3657,17 +3904,21 @@ export async function discoverManagedGcSessionRetirementReceipts(input: {
3657
3904
  )
3658
3905
  )
3659
3906
  throw new Error("task_artifact_owner_continuation_corrupt");
3907
+ managedGcReserveInventoryWork(operation);
3660
3908
  const key = item.name.slice(MANAGED_GC_RETIREMENT_PREFIX.length, MANAGED_GC_RETIREMENT_PREFIX.length + 64);
3661
3909
  let group = groups.get(key);
3662
3910
  if (!group) {
3911
+ managedGcReserveInventoryWork(operation);
3663
3912
  group = [];
3664
3913
  groups.set(key, group);
3665
3914
  }
3915
+ managedGcReserveInventoryWork(operation);
3666
3916
  group.push(item.name);
3667
3917
  }
3668
3918
  for (const [key, group] of groups) {
3669
3919
  const preparedName = `${MANAGED_GC_RETIREMENT_PREFIX}${key}-prepared.json`;
3670
3920
  if (!group.includes(preparedName)) throw new Error("task_artifact_owner_continuation_state_missing");
3921
+ managedGcReserveInventoryWork(operation);
3671
3922
  const bootstrapHistory: MutableManagedGcReceiptHistory = {
3672
3923
  encodedBytes: 0,
3673
3924
  admittedFiles: new Set<string>(),
@@ -3688,6 +3939,9 @@ export async function discoverManagedGcSessionRetirementReceipts(input: {
3688
3939
  preparedValue && typeof preparedValue === "object"
3689
3940
  ? (preparedValue as Record<string, unknown>).transcriptPath
3690
3941
  : undefined;
3942
+ if (typeof transcriptPath === "string") {
3943
+ managedGcReserveInventoryWork(operation, managedGcComponentInputAllowance(transcriptPath));
3944
+ }
3691
3945
  if (typeof transcriptPath !== "string" || managedGcRetirementTranscriptKey(transcriptPath) !== key)
3692
3946
  throw new Error("task_artifact_owner_continuation_authority_mismatch");
3693
3947
  const read = readManagedGcSessionRetirementReceiptUnlocked(
@@ -3695,31 +3949,35 @@ export async function discoverManagedGcSessionRetirementReceipts(input: {
3695
3949
  transcriptPath,
3696
3950
  trusted,
3697
3951
  store,
3698
- undefined,
3952
+ operation,
3699
3953
  receiptInventory,
3700
3954
  group,
3701
3955
  { snapshot: preparedFile, value: preparedValue, history: bootstrapHistory },
3702
3956
  );
3703
3957
  const receipt = read.receipt;
3704
3958
  if (!receipt) throw new Error("task_artifact_owner_continuation_state_missing");
3959
+ managedGcReserveInventoryWork(operation);
3705
3960
  result.push({ scope, receipt });
3706
3961
  }
3707
- const latestInventory = managedGcReceiptDirectoryInventory(scope, store);
3962
+ const latestInventory = managedGcReceiptDirectoryInventory(scope, store, operation);
3708
3963
  if (!managedGcSameReceiptDirectoryInventory(receiptInventory, latestInventory))
3709
3964
  throw new Error("task_artifact_owner_continuation_authority_mismatch");
3710
- } finally {
3711
- store.close();
3712
- }
3965
+ return true;
3966
+ });
3967
+ if (!hasReceiptDirectory) continue;
3713
3968
  }
3714
- for (const fence of scopeFences) managedGcAssertDiscoveryScopeFence(fence);
3969
+ for (const fence of scopeFences) managedGcAssertDiscoveryScopeFence(fence, operation);
3715
3970
  const rootInventoryAfter = managedGcStreamDirectoryInventory(
3716
3971
  sessionsRoot,
3717
3972
  entry => /^v2-[a-z2-7]{52}$/u.test(entry.name),
3718
3973
  entry => /^v2-[a-z2-7]{52}$/u.test(entry.name),
3974
+ operation,
3719
3975
  );
3720
3976
  if (!managedGcSameReceiptDirectoryInventory(rootInventory, rootInventoryAfter))
3721
3977
  throw new Error("managed_gc_scope_authority_mismatch");
3978
+ managedGcReserveInventoryWork(operation);
3722
3979
  const rootAfter = fs.lstatSync(sessionsRoot, { bigint: true });
3980
+ managedGcReserveInventoryWork(operation);
3723
3981
  const profileAfter = fs.lstatSync(agentDir, { bigint: true });
3724
3982
  if (
3725
3983
  !rootAfter.isDirectory() ||
@@ -3732,6 +3990,7 @@ export async function discoverManagedGcSessionRetirementReceipts(input: {
3732
3990
  profileAfter.ino !== inventoryBefore.profileIno
3733
3991
  )
3734
3992
  throw new Error("managed_gc_scope_authority_mismatch");
3993
+ managedGcReserveInventoryWork(operation, result.length);
3735
3994
  return result.sort((left, right) => left.receipt.transcriptPath.localeCompare(right.receipt.transcriptPath));
3736
3995
  }
3737
3996
 
@@ -3754,10 +4013,17 @@ function managedGcProtocolDirectorySnapshot(
3754
4013
  scope: ManagedScope,
3755
4014
  store: ManagedSessionDescendantStore,
3756
4015
  role: ManagedGcProtocolRole | typeof MANAGED_INTERNAL_DIRECTORY,
4016
+ operation: ManagedGcInventoryOperation,
3757
4017
  ): { readonly path: string; readonly stat: fs.BigIntStats; readonly names: readonly string[] } {
3758
4018
  const relativePath = role === MANAGED_INTERNAL_DIRECTORY ? role : `${MANAGED_INTERNAL_DIRECTORY}/${role}`;
4019
+ managedGcReserveInventoryWork(
4020
+ operation,
4021
+ managedGcComponentInputAllowance(scope.directoryPath) + relativePath.length,
4022
+ );
3759
4023
  const pathname = path.join(scope.directoryPath, relativePath);
4024
+ managedGcReserveInventoryWork(operation);
3760
4025
  const before = managedGcProtocolDirectoryStat(pathname);
4026
+ managedGcReserveInventoryWork(operation);
3761
4027
  const retained = store.captureDirectoryIdentity(relativePath);
3762
4028
  if (retained.dev !== before.dev.toString() || retained.ino !== before.ino.toString())
3763
4029
  throw new Error("managed_gc_protocol_directory_identity_mismatch");
@@ -3765,8 +4031,11 @@ function managedGcProtocolDirectorySnapshot(
3765
4031
  pathname,
3766
4032
  entry => role === MANAGED_RECEIPTS_DIRECTORY && entry.name.startsWith(MANAGED_GC_RETIREMENT_PREFIX),
3767
4033
  () => true,
4034
+ operation,
3768
4035
  );
4036
+ managedGcReserveInventoryWork(operation, inventory.entries.length);
3769
4037
  const names = inventory.entries.map(entry => entry.name);
4038
+ managedGcReserveInventoryWork(operation);
3770
4039
  const after = managedGcProtocolDirectoryStat(pathname);
3771
4040
  if (
3772
4041
  after.dev !== before.dev ||
@@ -3777,6 +4046,7 @@ function managedGcProtocolDirectorySnapshot(
3777
4046
  !managedGcSameDirectoryEvidence(inventory.evidence, managedGcDirectoryEvidence(before))
3778
4047
  )
3779
4048
  throw new Error("managed_gc_protocol_directory_changed");
4049
+ managedGcReserveInventoryWork(operation);
3780
4050
  store.assertBound();
3781
4051
  return { path: pathname, stat: before, names };
3782
4052
  }
@@ -3998,12 +4268,16 @@ export function managedGcProtocolInspectorForLock(
3998
4268
  scope: ManagedScope,
3999
4269
  lock: ManagedStorageLock,
4000
4270
  ): ManagedGcProtocolScopeInspector {
4001
- const inspect = managedGcProtocolScopeInspectorForScope(scope);
4271
+ const inspect = managedGcProtocolScopeInspectorWithOperation(scope);
4002
4272
  return async inputs => {
4273
+ const operation = newManagedGcInspectionOperation();
4274
+ managedGcReserveScopeComponentInputs(operation, scope);
4275
+ managedGcReserveInventoryWork(operation);
4003
4276
  lock.assertOwned();
4004
4277
  managedGcTrustedScope(scope);
4005
- const snapshots = await inspect(inputs);
4278
+ const snapshots = await inspect(inputs, operation);
4006
4279
  lock.assertOwned();
4280
+ managedGcReserveScopeComponentInputs(operation, scope);
4007
4281
  managedGcTrustedScope(scope);
4008
4282
  return snapshots;
4009
4283
  };
@@ -4011,30 +4285,54 @@ export function managedGcProtocolInspectorForLock(
4011
4285
 
4012
4286
  /** Bind a read-only protocol inventory to an already prepared scope's root/profile/session identities. */
4013
4287
  export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): ManagedGcProtocolScopeInspector {
4288
+ const inspect = managedGcProtocolScopeInspectorWithOperation(scope);
4289
+ return async inputs => {
4290
+ const operation = newManagedGcInspectionOperation();
4291
+ return inspect(inputs, operation);
4292
+ };
4293
+ }
4294
+
4295
+ function managedGcProtocolScopeInspectorWithOperation(
4296
+ scope: ManagedScope,
4297
+ ): (
4298
+ inputs: readonly ManagedGcProtocolScopeInput[],
4299
+ operation: ManagedGcInventoryOperation,
4300
+ ) => Promise<readonly ManagedGcProtocolScopeSnapshot[]> {
4014
4301
  const trusted = managedGcTrustedScope(scope);
4015
4302
  const configuredRoot = managedGcProtocolPathIdentity(trusted.root.canonicalPath);
4016
4303
  const profile = managedGcProtocolPathIdentity(scope.agentDir);
4017
4304
  const sessionsRoot = managedGcProtocolPathIdentity(scope.sessionsRoot);
4018
4305
  const protocol = managedGcProtocolPathIdentity(path.join(scope.directoryPath, MANAGED_INTERNAL_DIRECTORY));
4019
- const assertAuthority = (): void => {
4306
+ const authorityIdentities = [configuredRoot, profile, sessionsRoot, protocol] as const;
4307
+ const assertAuthority = (operation: ManagedGcInventoryOperation): void => {
4308
+ managedGcReserveScopeComponentInputs(operation, scope);
4309
+ managedGcReserveInventoryWork(operation);
4020
4310
  managedGcTrustedScope(scope);
4021
- for (const expected of [configuredRoot, profile, sessionsRoot, protocol]) {
4311
+ for (const expected of authorityIdentities) {
4312
+ managedGcReserveInventoryWork(operation);
4022
4313
  const current = managedGcProtocolPathIdentity(expected.path);
4023
4314
  if (current.dev !== expected.dev || current.ino !== expected.ino)
4024
4315
  throw new Error("managed_gc_protocol_root_identity_mismatch");
4025
4316
  }
4026
4317
  };
4027
- return async (inputs: readonly ManagedGcProtocolScopeInput[]) => {
4028
- assertAuthority();
4029
- const journals = await discoverManagedGcSessionRetirementReceipts({
4030
- agentDir: scope.agentDir,
4031
- sessionsRoot: scope.sessionsRoot,
4032
- });
4033
- assertAuthority();
4318
+ return async (inputs: readonly ManagedGcProtocolScopeInput[], operation: ManagedGcInventoryOperation) => {
4034
4319
  if (inputs.length > MANAGED_GC_PROTOCOL_MAX_ENTRIES) throw new Error("managed_gc_protocol_scope_limit_exceeded");
4320
+ managedGcReserveInventoryWork(operation, inputs.length);
4321
+ assertAuthority(operation);
4322
+ const journals = await discoverManagedGcSessionRetirementReceiptsWithOperation(
4323
+ { agentDir: scope.agentDir, sessionsRoot: scope.sessionsRoot },
4324
+ operation,
4325
+ );
4326
+ assertAuthority(operation);
4327
+ managedGcReserveInventoryWork(operation);
4328
+ managedGcReserveInventoryWork(operation);
4035
4329
  const snapshots: ManagedGcProtocolScopeSnapshot[] = [];
4036
4330
  let totalFiles = 0;
4037
4331
  for (const input of inputs) {
4332
+ if (typeof input.scopePath !== "string" || typeof input.protocolIdentity?.path !== "string")
4333
+ throw new Error("managed_gc_protocol_scope_path_mismatch");
4334
+ managedGcReserveInventoryWork(operation, managedGcComponentInputAllowance(input.scopePath));
4335
+ managedGcReserveInventoryWork(operation, managedGcComponentInputAllowance(input.protocolIdentity.path));
4038
4336
  if (
4039
4337
  path.resolve(input.scopePath) !== input.scopePath ||
4040
4338
  path.dirname(input.scopePath) !== scope.sessionsRoot ||
@@ -4043,8 +4341,10 @@ export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): Ma
4043
4341
  input.protocolIdentity.path !== path.join(input.scopePath, MANAGED_INTERNAL_DIRECTORY)
4044
4342
  )
4045
4343
  throw new Error("managed_gc_protocol_scope_path_mismatch");
4344
+ managedGcReserveInventoryWork(operation, 3);
4046
4345
  const bindingPath = path.join(input.scopePath, MANAGED_SESSION_BINDING_FILE);
4047
4346
  const binding = captureManagedFileNoFollow(bindingPath);
4347
+ managedGcReserveInventoryWork(operation);
4048
4348
  const bindingStat = fs.lstatSync(bindingPath, { bigint: true });
4049
4349
  const bindingIdentity: ManagedGcProtocolFileSnapshot = {
4050
4350
  name: MANAGED_SESSION_BINDING_FILE,
@@ -4061,6 +4361,7 @@ export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): Ma
4061
4361
  throw new Error("managed_gc_protocol_binding_identity_mismatch");
4062
4362
  const bindingValue: unknown = JSON.parse(binding.bytes.toString("utf8"));
4063
4363
  if (!isBinding(bindingValue)) throw new Error("managed_gc_scope_authority_mismatch");
4364
+ managedGcReserveInventoryWork(operation, managedGcComponentInputAllowance(bindingValue.canonicalPath));
4064
4365
  const resolved = resolveManagedGcScopeForRead({
4065
4366
  cwd: bindingValue.canonicalPath,
4066
4367
  agentDir: scope.agentDir,
@@ -4081,10 +4382,11 @@ export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): Ma
4081
4382
  directoryPath: input.scopePath,
4082
4383
  platform: bindingValue.platform,
4083
4384
  };
4084
- if (hasManagedGcRetirementJournalWithoutWorkspace(candidateScope))
4385
+ if (hasManagedGcRetirementJournalWithoutWorkspace(candidateScope, operation))
4085
4386
  throw new Error("managed_gc_protocol_scope_unrecognized");
4086
4387
  } else if (resolved.kind === "resolved" && resolved.scope.directoryPath === input.scopePath) {
4087
4388
  candidateScope = resolved.scope;
4389
+ managedGcReserveScopeComponentInputs(operation, candidateScope);
4088
4390
  candidateTrusted = managedGcTrustedScope(candidateScope);
4089
4391
  } else {
4090
4392
  throw new Error("managed_gc_protocol_scope_unrecognized");
@@ -4099,9 +4401,11 @@ export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): Ma
4099
4401
  candidateScope.sessionsRoot !== scope.sessionsRoot
4100
4402
  )
4101
4403
  throw new Error("managed_gc_protocol_root_mismatch");
4404
+ managedGcReserveInventoryWork(operation);
4102
4405
  const scopeIdentity = managedGcProtocolPathIdentity(candidateScope.directoryPath);
4103
4406
  if (scopeIdentity.dev !== input.scopeIdentity.dev || scopeIdentity.ino !== input.scopeIdentity.ino)
4104
4407
  throw new Error("managed_gc_protocol_scope_identity_mismatch");
4408
+ managedGcReserveInventoryWork(operation, 2);
4105
4409
  const store = candidateTrusted
4106
4410
  ? managedGcScopeReader(candidateScope, candidateTrusted)
4107
4411
  : new ManagedSessionDescendantStore(
@@ -4117,7 +4421,8 @@ export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): Ma
4117
4421
  },
4118
4422
  "read-only",
4119
4423
  );
4120
- try {
4424
+ managedGcWithClosedReader(store, () => {
4425
+ managedGcReserveInventoryWork(operation);
4121
4426
  store.verifyRootSecurity();
4122
4427
  const checkedBinding = managedGcProtocolFileSnapshot(candidateScope, store, MANAGED_SESSION_BINDING_FILE);
4123
4428
  if (!managedGcProtocolFileIdentityMatches(checkedBinding.identity, input.bindingIdentity))
@@ -4126,6 +4431,7 @@ export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): Ma
4126
4431
  candidateScope,
4127
4432
  store,
4128
4433
  MANAGED_INTERNAL_DIRECTORY,
4434
+ operation,
4129
4435
  );
4130
4436
  if (
4131
4437
  protocolDirectory.path !== input.protocolIdentity.path ||
@@ -4138,10 +4444,12 @@ export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): Ma
4138
4444
  MANAGED_GC_PROTOCOL_ROLES.some(role => !protocolDirectory.names.includes(role))
4139
4445
  )
4140
4446
  throw new Error("managed_gc_protocol_roles_invalid");
4447
+ managedGcReserveInventoryWork(operation, 2);
4141
4448
  const directories: ManagedGcProtocolDirectorySnapshot[] = [];
4142
4449
  const receiptHistories = new Map<string, MutableManagedGcReceiptHistory>();
4143
4450
  for (const role of MANAGED_GC_PROTOCOL_ROLES) {
4144
- const directory = managedGcProtocolDirectorySnapshot(candidateScope, store, role);
4451
+ const directory = managedGcProtocolDirectorySnapshot(candidateScope, store, role, operation);
4452
+ managedGcReserveInventoryWork(operation, directory.names.length);
4145
4453
  const files: ManagedGcProtocolFileSnapshot[] = [];
4146
4454
  for (const name of directory.names) {
4147
4455
  if (!name || path.basename(name) !== name || name.includes(".jsonl"))
@@ -4218,12 +4526,13 @@ export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): Ma
4218
4526
  const detail = error instanceof Error ? error.message : "unknown";
4219
4527
  throw new Error(`${detail} [${role}/${name}]`);
4220
4528
  }
4529
+ managedGcReserveInventoryWork(operation);
4221
4530
  files.push(captured.identity);
4222
4531
  totalFiles++;
4223
4532
  if (totalFiles > MANAGED_GC_PROTOCOL_MAX_ENTRIES)
4224
4533
  throw new Error("managed_gc_protocol_entry_limit_exceeded");
4225
4534
  }
4226
- const finalDirectory = managedGcProtocolDirectorySnapshot(candidateScope, store, role);
4535
+ const finalDirectory = managedGcProtocolDirectorySnapshot(candidateScope, store, role, operation);
4227
4536
  if (
4228
4537
  finalDirectory.stat.dev !== directory.stat.dev ||
4229
4538
  finalDirectory.stat.ino !== directory.stat.ino ||
@@ -4234,6 +4543,7 @@ export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): Ma
4234
4543
  finalDirectory.names.some((name, index) => name !== directory.names[index])
4235
4544
  )
4236
4545
  throw new Error("managed_gc_protocol_directory_changed");
4546
+ managedGcReserveInventoryWork(operation);
4237
4547
  directories.push({
4238
4548
  role,
4239
4549
  path: directory.path,
@@ -4245,7 +4555,12 @@ export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): Ma
4245
4555
  files,
4246
4556
  });
4247
4557
  }
4248
- const finalProtocol = managedGcProtocolDirectorySnapshot(candidateScope, store, MANAGED_INTERNAL_DIRECTORY);
4558
+ const finalProtocol = managedGcProtocolDirectorySnapshot(
4559
+ candidateScope,
4560
+ store,
4561
+ MANAGED_INTERNAL_DIRECTORY,
4562
+ operation,
4563
+ );
4249
4564
  if (
4250
4565
  finalProtocol.stat.dev !== protocolDirectory.stat.dev ||
4251
4566
  finalProtocol.stat.ino !== protocolDirectory.stat.ino ||
@@ -4256,10 +4571,16 @@ export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): Ma
4256
4571
  finalProtocol.names.some((name, index) => name !== protocolDirectory.names[index])
4257
4572
  )
4258
4573
  throw new Error("managed_gc_protocol_tree_changed");
4574
+ managedGcReserveInventoryWork(operation);
4259
4575
  const comparisonReceiptHistories = new Map<string, MutableManagedGcReceiptHistory>();
4260
4576
  for (const directory of directories) {
4261
4577
  const relative = `${MANAGED_INTERNAL_DIRECTORY}/${directory.role}`;
4262
- const currentDirectory = managedGcProtocolDirectorySnapshot(candidateScope, store, directory.role);
4578
+ const currentDirectory = managedGcProtocolDirectorySnapshot(
4579
+ candidateScope,
4580
+ store,
4581
+ directory.role,
4582
+ operation,
4583
+ );
4263
4584
  const names = currentDirectory.names;
4264
4585
  if (
4265
4586
  currentDirectory.stat.dev !== BigInt(directory.dev) ||
@@ -4280,6 +4601,7 @@ export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): Ma
4280
4601
  if (!managedGcProtocolFileIdentityMatches(actual.identity, expected))
4281
4602
  throw new Error("managed_gc_protocol_file_changed");
4282
4603
  }
4604
+ managedGcReserveInventoryWork(operation);
4283
4605
  const current = managedGcProtocolDirectoryStat(directory.path);
4284
4606
  if (
4285
4607
  current.dev.toString() !== directory.dev ||
@@ -4293,6 +4615,7 @@ export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): Ma
4293
4615
  const finalBinding = managedGcProtocolFileSnapshot(candidateScope, store, MANAGED_SESSION_BINDING_FILE);
4294
4616
  if (!managedGcProtocolFileIdentityMatches(finalBinding.identity, input.bindingIdentity))
4295
4617
  throw new Error("managed_gc_protocol_binding_changed");
4618
+ managedGcReserveInventoryWork(operation);
4296
4619
  snapshots.push({
4297
4620
  configuredRoot,
4298
4621
  profile,
@@ -4309,16 +4632,14 @@ export function managedGcProtocolScopeInspectorForScope(scope: ManagedScope): Ma
4309
4632
  protocolMode: Number(protocolDirectory.stat.mode & 0o777n),
4310
4633
  directories,
4311
4634
  });
4312
- } finally {
4313
- store.close();
4314
- }
4635
+ });
4315
4636
  }
4316
- const latestJournals = await discoverManagedGcSessionRetirementReceipts({
4317
- agentDir: scope.agentDir,
4318
- sessionsRoot: scope.sessionsRoot,
4319
- });
4637
+ const latestJournals = await discoverManagedGcSessionRetirementReceiptsWithOperation(
4638
+ { agentDir: scope.agentDir, sessionsRoot: scope.sessionsRoot },
4639
+ operation,
4640
+ );
4320
4641
  if (!deepSame(latestJournals, journals)) throw new Error("managed_gc_protocol_journal_changed");
4321
- assertAuthority();
4642
+ assertAuthority(operation);
4322
4643
  return snapshots;
4323
4644
  };
4324
4645
  }
@@ -4393,12 +4714,13 @@ export async function publishManagedGcSessionRetirementReceipt(
4393
4714
  ): Promise<ManagedGcSessionRetirementReceipt> {
4394
4715
  validateManagedGcTranscriptPath(scope, receipt.transcriptPath);
4395
4716
  return withManagedGcRetirementJournal(scope, receipt.transcriptPath, (trusted, store, lock) => {
4717
+ const operation: ManagedGcInventoryOperation = { kind: "leased-journal", lock };
4396
4718
  const currentRead = readManagedGcSessionRetirementReceiptUnlocked(
4397
4719
  scope,
4398
4720
  receipt.transcriptPath,
4399
4721
  trusted,
4400
4722
  store,
4401
- lock,
4723
+ operation,
4402
4724
  );
4403
4725
  const current = currentRead.receipt;
4404
4726
  const target = receipt;
@@ -4462,7 +4784,7 @@ export async function publishManagedGcSessionRetirementReceipt(
4462
4784
  throw new Error("task_artifact_owner_continuation_state_order_invalid");
4463
4785
  }
4464
4786
  }
4465
- assertManagedGcReceiptHistoryDirectoryUnchanged(scope, store, currentRead.history);
4787
+ assertManagedGcReceiptHistoryDirectoryUnchanged(scope, store, currentRead.history, operation);
4466
4788
  const { record, parsed } = managedGcReceiptForPublication(
4467
4789
  scope,
4468
4790
  receipt,
@@ -4486,7 +4808,7 @@ export async function publishManagedGcSessionRetirementReceipt(
4486
4808
  receipt.transcriptPath,
4487
4809
  trusted,
4488
4810
  store,
4489
- lock,
4811
+ operation,
4490
4812
  );
4491
4813
  const persisted = persistedRead.receipt;
4492
4814
  if (!persisted) throw new Error("durability_failed");