@gajae-code/coding-agent 0.18.8 → 0.18.9
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/CHANGELOG.md +98 -0
- package/dist/types/async/endpoint-id.d.ts +16 -0
- package/dist/types/async/support.d.ts +0 -14
- package/dist/types/commands/mcp-serve.d.ts +4 -0
- package/dist/types/discovery/mcp-json.d.ts +1 -0
- package/dist/types/eval/py/kernel.d.ts +18 -0
- package/dist/types/export/html/html-export-destination.d.ts +2 -0
- package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +5 -0
- package/dist/types/extensibility/skill-management.d.ts +1 -0
- package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -3
- package/dist/types/gjc-runtime/sidecar-bootstrap-env.d.ts +6 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +3 -0
- package/dist/types/gjc-runtime/tmux-common.d.ts +8 -5
- package/dist/types/lsp/config.d.ts +8 -5
- package/dist/types/runtime-mcp/manager.d.ts +2 -1
- package/dist/types/runtime-mcp/mcp-redirect.d.ts +9 -0
- package/dist/types/runtime-mcp/oauth-public-url.d.ts +3 -1
- package/dist/types/runtime-mcp/project-host-values.d.ts +27 -0
- package/dist/types/sdk/broker/broker.d.ts +6 -1
- package/dist/types/sdk/broker/doctor-restart.d.ts +5 -0
- package/dist/types/sdk/broker/ensure.d.ts +14 -1
- package/dist/types/session/session-storage.d.ts +7 -0
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +3 -0
- package/dist/types/task/isolation-dir.d.ts +26 -0
- package/dist/types/tools/ast-edit-write-path.d.ts +13 -0
- package/dist/types/tools/browser/cdp-url.d.ts +2 -0
- package/dist/types/tools/github-side-effect.d.ts +22 -0
- package/dist/types/tools/monitor-mutation.d.ts +9 -0
- package/package.json +7 -7
- package/src/async/endpoint-id.ts +26 -0
- package/src/async/job-manager.ts +19 -10
- package/src/async/support.ts +0 -25
- package/src/capability/index.ts +14 -16
- package/src/cli/doctor/repairs.ts +4 -1
- package/src/cli/doctor-supervisor.ts +3 -0
- package/src/cli/public-command-registry.ts +28 -6
- package/src/commands/mcp-serve.ts +7 -0
- package/src/commands/sdk.ts +453 -415
- package/src/config/file-lock.ts +9 -11
- package/src/config/model-profile-activation.ts +5 -5
- package/src/coordinator-mcp/server.ts +2 -0
- package/src/customization/inventory.ts +1 -13
- package/src/dap/config.ts +2 -2
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +10 -6
- package/src/discovery/builtin.ts +3 -1
- package/src/discovery/helpers.ts +12 -9
- package/src/discovery/mcp-json.ts +20 -12
- package/src/eval/py/executor.ts +459 -77
- package/src/eval/py/kernel.ts +79 -13
- package/src/eval/py/tool-bridge.ts +4 -0
- package/src/export/html/html-export-destination.ts +16 -0
- package/src/export/html/index.ts +2 -6
- package/src/extensibility/extensions/function-hooks-internal.ts +27 -0
- package/src/extensibility/extensions/runner.ts +11 -1
- package/src/extensibility/gjc-plugins/mcp-policy.ts +2 -1
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +27 -15
- package/src/extensibility/skill-management.ts +3 -0
- package/src/gjc-runtime/launch-worktree.ts +56 -5
- package/src/gjc-runtime/psmux-detect.ts +28 -4
- package/src/gjc-runtime/session-state-sidecar.ts +3 -2
- package/src/gjc-runtime/sidecar-bootstrap-env.ts +19 -0
- package/src/gjc-runtime/state-writer.ts +22 -8
- package/src/gjc-runtime/tmux-common.ts +8 -5
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/lsp/config.ts +12 -63
- package/src/memories/index.ts +88 -7
- package/src/modes/components/hook-editor.ts +1 -1
- package/src/modes/components/hook-selector.ts +1 -1
- package/src/modes/controllers/input-controller.ts +2 -2
- package/src/modes/controllers/runtime-mcp-command-controller.ts +21 -13
- package/src/modes/controllers/selector-controller.ts +3 -7
- package/src/modes/controllers/todo-command-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +1 -1
- package/src/runtime-mcp/client.ts +4 -2
- package/src/runtime-mcp/json-rpc.ts +2 -1
- package/src/runtime-mcp/manager.ts +29 -11
- package/src/runtime-mcp/mcp-redirect.ts +73 -0
- package/src/runtime-mcp/oauth-flow.ts +11 -4
- package/src/runtime-mcp/oauth-public-url.ts +8 -2
- package/src/runtime-mcp/pool.ts +6 -0
- package/src/runtime-mcp/project-host-values.ts +46 -0
- package/src/runtime-mcp/transports/http.ts +2 -1
- package/src/sdk/acp/adapter.ts +2 -0
- package/src/sdk/broker/broker.ts +6 -0
- package/src/sdk/broker/doctor-restart.ts +4 -0
- package/src/sdk/broker/ensure.ts +29 -2
- package/src/sdk/host/session-runtime.test.ts +58 -9
- package/src/sdk/host/session-runtime.ts +65 -35
- package/src/sdk/router/session-router.ts +7 -6
- package/src/sdk/session.ts +2 -7
- package/src/session/agent-session.ts +59 -17
- package/src/session/artifacts.ts +3 -2
- package/src/session/internal/managed-session-scope.ts +403 -81
- package/src/session/internal/managed-session-storage.ts +125 -17
- package/src/session/session-manager.ts +12 -0
- package/src/session/session-storage.ts +30 -1
- package/src/skill-state/active-state.ts +13 -0
- package/src/skill-state/workflow-mutation-guard.ts +102 -10
- package/src/slash-commands/builtin-registry.ts +1 -1
- package/src/slash-commands/helpers/mcp.ts +8 -4
- package/src/task/isolation-dir.ts +143 -0
- package/src/task/worktree.ts +16 -7
- package/src/tools/ast-edit-write-path.ts +68 -0
- package/src/tools/ast-edit.ts +7 -2
- package/src/tools/browser/cdp-url.ts +18 -0
- package/src/tools/browser.ts +8 -1
- package/src/tools/gh.ts +10 -0
- package/src/tools/github-side-effect.ts +112 -0
- package/src/tools/monitor-mutation.ts +27 -0
- package/src/tools/monitor.ts +7 -0
- package/src/tools/python.ts +7 -6
- package/src/utils/external-editor.ts +3 -2
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2639
|
-
|
|
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)
|
|
2642
|
-
if (collect)
|
|
2722
|
+
if (++retained > MANAGED_ARTIFACT_MAX_FILES) managedGcJournalCapacity();
|
|
2723
|
+
if (collect) {
|
|
2724
|
+
managedGcReserveInventoryWork(operation);
|
|
2725
|
+
entries.push(entry);
|
|
2726
|
+
}
|
|
2643
2727
|
}
|
|
2644
|
-
}
|
|
2645
|
-
|
|
2728
|
+
} catch (error) {
|
|
2729
|
+
primaryError = error;
|
|
2730
|
+
hasPrimaryError = true;
|
|
2646
2731
|
}
|
|
2647
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3233
|
-
|
|
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(
|
|
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
|
-
}
|
|
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
|
-
|
|
3399
|
-
|
|
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
|
-
"
|
|
3557
|
+
[operationError, ...(hasReleaseError ? [releaseError] : []), ...(hasCloseError ? [closeError] : [])],
|
|
3558
|
+
"managed_gc_retirement_operation_and_cleanup_failed",
|
|
3403
3559
|
);
|
|
3404
|
-
if (
|
|
3405
|
-
if (
|
|
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,
|
|
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"))
|
|
3436
|
-
|
|
3606
|
+
if (!hasFsCode(error, "ENOENT")) throw error;
|
|
3607
|
+
receiptDirectoryPresent = false;
|
|
3437
3608
|
}
|
|
3438
|
-
|
|
3439
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3548
|
-
}
|
|
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
|
-
|
|
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
|
-
() =>
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
3711
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
}
|
|
4313
|
-
store.close();
|
|
4314
|
-
}
|
|
4635
|
+
});
|
|
4315
4636
|
}
|
|
4316
|
-
const latestJournals = await
|
|
4317
|
-
agentDir: scope.agentDir,
|
|
4318
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4811
|
+
operation,
|
|
4490
4812
|
);
|
|
4491
4813
|
const persisted = persistedRead.receipt;
|
|
4492
4814
|
if (!persisted) throw new Error("durability_failed");
|