@cleocode/core 2026.10.1 → 2026.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-provenance.json +2 -2
- package/dist/cloud/journal.d.ts +9 -5
- package/dist/cloud/journal.d.ts.map +1 -1
- package/dist/cloud/journal.js +29 -8
- package/dist/cloud/journal.js.map +1 -1
- package/dist/cloud/manifest-check.d.ts +140 -4
- package/dist/cloud/manifest-check.d.ts.map +1 -1
- package/dist/cloud/manifest-check.js +309 -10
- package/dist/cloud/manifest-check.js.map +1 -1
- package/dist/cloud/nexus-attach.d.ts +25 -0
- package/dist/cloud/nexus-attach.d.ts.map +1 -1
- package/dist/cloud/nexus-attach.js +81 -6
- package/dist/cloud/nexus-attach.js.map +1 -1
- package/dist/cloud/nexus-cloud-activity.d.ts +37 -0
- package/dist/cloud/nexus-cloud-activity.d.ts.map +1 -0
- package/dist/cloud/nexus-cloud-activity.js +79 -0
- package/dist/cloud/nexus-cloud-activity.js.map +1 -0
- package/dist/cloud/nexus-cloud-status.d.ts.map +1 -1
- package/dist/cloud/nexus-cloud-status.js +36 -0
- package/dist/cloud/nexus-cloud-status.js.map +1 -1
- package/dist/cloud/nexus-device.d.ts +1 -1
- package/dist/cloud/nexus-enrol.d.ts.map +1 -1
- package/dist/cloud/nexus-enrol.js +5 -0
- package/dist/cloud/nexus-enrol.js.map +1 -1
- package/dist/cloud/nexus-home.d.ts +74 -0
- package/dist/cloud/nexus-home.d.ts.map +1 -0
- package/dist/cloud/nexus-home.js +97 -0
- package/dist/cloud/nexus-home.js.map +1 -0
- package/dist/cloud/nexus-vault-keys.d.ts +129 -0
- package/dist/cloud/nexus-vault-keys.d.ts.map +1 -0
- package/dist/cloud/nexus-vault-keys.js +277 -0
- package/dist/cloud/nexus-vault-keys.js.map +1 -0
- package/dist/cloud/nexus-vault-state.d.ts +121 -0
- package/dist/cloud/nexus-vault-state.d.ts.map +1 -0
- package/dist/cloud/nexus-vault-state.js +274 -0
- package/dist/cloud/nexus-vault-state.js.map +1 -0
- package/dist/cloud/nexus-vault.d.ts +160 -0
- package/dist/cloud/nexus-vault.d.ts.map +1 -0
- package/dist/cloud/nexus-vault.js +1733 -0
- package/dist/cloud/nexus-vault.js.map +1 -0
- package/dist/cloud/signing.d.ts +24 -8
- package/dist/cloud/signing.d.ts.map +1 -1
- package/dist/cloud/signing.js +61 -15
- package/dist/cloud/signing.js.map +1 -1
- package/dist/docs/export-document.js +8137 -3318
- package/dist/docs/export-document.js.map +3 -3
- package/dist/doctor/migrations.d.ts +75 -0
- package/dist/doctor/migrations.d.ts.map +1 -0
- package/dist/doctor/migrations.js +136 -0
- package/dist/doctor/migrations.js.map +1 -0
- package/dist/doctor/sync-sealer.d.ts +48 -0
- package/dist/doctor/sync-sealer.d.ts.map +1 -0
- package/dist/doctor/sync-sealer.js +109 -0
- package/dist/doctor/sync-sealer.js.map +1 -0
- package/dist/doctor/sync-triggers.d.ts +53 -0
- package/dist/doctor/sync-triggers.d.ts.map +1 -0
- package/dist/doctor/sync-triggers.js +157 -0
- package/dist/doctor/sync-triggers.js.map +1 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +5 -1
- package/dist/init.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/llm/plugin-facade.js +8442 -3623
- package/dist/llm/plugin-facade.js.map +3 -3
- package/dist/nexus/projects-clean.d.ts +3 -0
- package/dist/nexus/projects-clean.d.ts.map +1 -1
- package/dist/nexus/projects-clean.js +9 -3
- package/dist/nexus/projects-clean.js.map +1 -1
- package/dist/nexus/registry-roots.d.ts +3 -1
- package/dist/nexus/registry-roots.d.ts.map +1 -1
- package/dist/nexus/registry-roots.js +5 -2
- package/dist/nexus/registry-roots.js.map +1 -1
- package/dist/nexus/registry.d.ts +8 -1
- package/dist/nexus/registry.d.ts.map +1 -1
- package/dist/nexus/registry.js +24 -10
- package/dist/nexus/registry.js.map +1 -1
- package/dist/nexus/workspace.d.ts +4 -1
- package/dist/nexus/workspace.d.ts.map +1 -1
- package/dist/nexus/workspace.js +10 -4
- package/dist/nexus/workspace.js.map +1 -1
- package/dist/orchestrate/spawn-ops.d.ts.map +1 -1
- package/dist/orchestrate/spawn-ops.js +7 -2
- package/dist/orchestrate/spawn-ops.js.map +1 -1
- package/dist/orchestrate/worker-verify.d.ts +121 -12
- package/dist/orchestrate/worker-verify.d.ts.map +1 -1
- package/dist/orchestrate/worker-verify.js +172 -21
- package/dist/orchestrate/worker-verify.js.map +1 -1
- package/dist/orchestration/spawn-prompt.d.ts.map +1 -1
- package/dist/orchestration/spawn-prompt.js +15 -9
- package/dist/orchestration/spawn-prompt.js.map +1 -1
- package/dist/release/ci-evidence.d.ts +9 -0
- package/dist/release/ci-evidence.d.ts.map +1 -1
- package/dist/release/ci-evidence.js +9 -2
- package/dist/release/ci-evidence.js.map +1 -1
- package/dist/resources/backend.d.ts +15 -3
- package/dist/resources/backend.d.ts.map +1 -1
- package/dist/resources/backend.js +7 -3
- package/dist/resources/backend.js.map +1 -1
- package/dist/resources/darwin-backend.d.ts +142 -0
- package/dist/resources/darwin-backend.d.ts.map +1 -0
- package/dist/resources/darwin-backend.js +288 -0
- package/dist/resources/darwin-backend.js.map +1 -0
- package/dist/resources/governor.d.ts +44 -1
- package/dist/resources/governor.d.ts.map +1 -1
- package/dist/resources/governor.js +100 -36
- package/dist/resources/governor.js.map +1 -1
- package/dist/resources/heavy-command.d.ts +132 -0
- package/dist/resources/heavy-command.d.ts.map +1 -0
- package/dist/resources/heavy-command.js +768 -0
- package/dist/resources/heavy-command.js.map +1 -0
- package/dist/resources/monitor.d.ts +50 -1
- package/dist/resources/monitor.d.ts.map +1 -1
- package/dist/resources/monitor.js +83 -1
- package/dist/resources/monitor.js.map +1 -1
- package/dist/resources/run-admission.d.ts +303 -0
- package/dist/resources/run-admission.d.ts.map +1 -0
- package/dist/resources/run-admission.js +648 -0
- package/dist/resources/run-admission.js.map +1 -0
- package/dist/resources/run-class.d.ts +111 -0
- package/dist/resources/run-class.d.ts.map +1 -0
- package/dist/resources/run-class.js +658 -0
- package/dist/resources/run-class.js.map +1 -0
- package/dist/resources/run-governed.d.ts +158 -0
- package/dist/resources/run-governed.d.ts.map +1 -0
- package/dist/resources/run-governed.js +406 -0
- package/dist/resources/run-governed.js.map +1 -0
- package/dist/resources/slot-holder.d.ts +267 -0
- package/dist/resources/slot-holder.d.ts.map +1 -0
- package/dist/resources/slot-holder.js +505 -0
- package/dist/resources/slot-holder.js.map +1 -0
- package/dist/resources/tool-groups.d.ts +74 -0
- package/dist/resources/tool-groups.d.ts.map +1 -0
- package/dist/resources/tool-groups.js +118 -0
- package/dist/resources/tool-groups.js.map +1 -0
- package/dist/routing/build-command-groups.d.ts.map +1 -1
- package/dist/routing/build-command-groups.js +1 -0
- package/dist/routing/build-command-groups.js.map +1 -1
- package/dist/selfimprove/fix-gen.js +8446 -3627
- package/dist/selfimprove/fix-gen.js.map +3 -3
- package/dist/sentient/cross-project-hygiene.d.ts +3 -2
- package/dist/sentient/cross-project-hygiene.d.ts.map +1 -1
- package/dist/sentient/cross-project-hygiene.js +5 -2
- package/dist/sentient/cross-project-hygiene.js.map +1 -1
- package/dist/sentient/state.d.ts +7 -0
- package/dist/sentient/state.d.ts.map +1 -1
- package/dist/sentient/state.js.map +1 -1
- package/dist/sentient/tick.d.ts +14 -2
- package/dist/sentient/tick.d.ts.map +1 -1
- package/dist/sentient/tick.js +63 -4
- package/dist/sentient/tick.js.map +1 -1
- package/dist/store/backup-recover.d.ts.map +1 -1
- package/dist/store/backup-recover.js +5 -0
- package/dist/store/backup-recover.js.map +1 -1
- package/dist/store/data-accessor.d.ts +1 -0
- package/dist/store/data-accessor.d.ts.map +1 -1
- package/dist/store/data-accessor.js +7 -0
- package/dist/store/data-accessor.js.map +1 -1
- package/dist/store/dual-scope-db.d.ts +23 -1
- package/dist/store/dual-scope-db.d.ts.map +1 -1
- package/dist/store/dual-scope-db.js +126 -36
- package/dist/store/dual-scope-db.js.map +1 -1
- package/dist/store/exodus/recovery.d.ts.map +1 -1
- package/dist/store/exodus/recovery.js +15 -0
- package/dist/store/exodus/recovery.js.map +1 -1
- package/dist/store/legacy-tasks-lineage.d.ts.map +1 -1
- package/dist/store/legacy-tasks-lineage.js +5 -0
- package/dist/store/legacy-tasks-lineage.js.map +1 -1
- package/dist/store/lock.d.ts +8 -1
- package/dist/store/lock.d.ts.map +1 -1
- package/dist/store/lock.js +10 -2
- package/dist/store/lock.js.map +1 -1
- package/dist/store/migration-manager.d.ts +5 -0
- package/dist/store/migration-manager.d.ts.map +1 -1
- package/dist/store/migration-manager.js +11 -0
- package/dist/store/migration-manager.js.map +1 -1
- package/dist/store/migration-runner.d.ts +158 -0
- package/dist/store/migration-runner.d.ts.map +1 -0
- package/dist/store/migration-runner.js +493 -0
- package/dist/store/migration-runner.js.map +1 -0
- package/dist/store/portable-bundle-import.d.ts +15 -1
- package/dist/store/portable-bundle-import.d.ts.map +1 -1
- package/dist/store/portable-bundle-import.js +24 -4
- package/dist/store/portable-bundle-import.js.map +1 -1
- package/dist/store/portable-bundle-relocate.d.ts +43 -0
- package/dist/store/portable-bundle-relocate.d.ts.map +1 -1
- package/dist/store/portable-bundle-relocate.js +79 -5
- package/dist/store/portable-bundle-relocate.js.map +1 -1
- package/dist/store/portable-bundle-scan.d.ts +20 -0
- package/dist/store/portable-bundle-scan.d.ts.map +1 -1
- package/dist/store/portable-bundle-scan.js +38 -3
- package/dist/store/portable-bundle-scan.js.map +1 -1
- package/dist/store/portable-bundle.d.ts +33 -0
- package/dist/store/portable-bundle.d.ts.map +1 -1
- package/dist/store/portable-bundle.js +34 -7
- package/dist/store/portable-bundle.js.map +1 -1
- package/dist/store/row-identity-registry.d.ts +137 -7
- package/dist/store/row-identity-registry.d.ts.map +1 -1
- package/dist/store/row-identity-registry.js +364 -9
- package/dist/store/row-identity-registry.js.map +1 -1
- package/dist/store/row-identity.d.ts.map +1 -1
- package/dist/store/row-identity.js +9 -10
- package/dist/store/row-identity.js.map +1 -1
- package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
- package/dist/store/sqlite-data-accessor.js +10 -0
- package/dist/store/sqlite-data-accessor.js.map +1 -1
- package/dist/store/sqlite.d.ts +31 -0
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +5 -1
- package/dist/store/sqlite.js.map +1 -1
- package/dist/store/sync/capture.d.ts +203 -0
- package/dist/store/sync/capture.d.ts.map +1 -0
- package/dist/store/sync/capture.js +562 -0
- package/dist/store/sync/capture.js.map +1 -0
- package/dist/store/sync/clock-store.d.ts.map +1 -1
- package/dist/store/sync/clock-store.js +2 -0
- package/dist/store/sync/clock-store.js.map +1 -1
- package/dist/store/sync/fk-orphans.d.ts +133 -0
- package/dist/store/sync/fk-orphans.d.ts.map +1 -0
- package/dist/store/sync/fk-orphans.js +265 -0
- package/dist/store/sync/fk-orphans.js.map +1 -0
- package/dist/store/sync/flags.d.ts +7 -0
- package/dist/store/sync/flags.d.ts.map +1 -1
- package/dist/store/sync/flags.js +16 -0
- package/dist/store/sync/flags.js.map +1 -1
- package/dist/store/sync/hlc.d.ts.map +1 -1
- package/dist/store/sync/hlc.js +4 -0
- package/dist/store/sync/hlc.js.map +1 -1
- package/dist/store/sync/identity-fill.d.ts +45 -0
- package/dist/store/sync/identity-fill.d.ts.map +1 -0
- package/dist/store/sync/identity-fill.js +85 -0
- package/dist/store/sync/identity-fill.js.map +1 -0
- package/dist/store/sync/machinery.d.ts +27 -0
- package/dist/store/sync/machinery.d.ts.map +1 -0
- package/dist/store/sync/machinery.js +44 -0
- package/dist/store/sync/machinery.js.map +1 -0
- package/dist/store/sync/netting.d.ts +120 -0
- package/dist/store/sync/netting.d.ts.map +1 -0
- package/dist/store/sync/netting.js +336 -0
- package/dist/store/sync/netting.js.map +1 -0
- package/dist/store/sync/replica.d.ts +27 -2
- package/dist/store/sync/replica.d.ts.map +1 -1
- package/dist/store/sync/replica.js +47 -3
- package/dist/store/sync/replica.js.map +1 -1
- package/dist/store/sync/schema.d.ts +12 -0
- package/dist/store/sync/schema.d.ts.map +1 -1
- package/dist/store/sync/schema.js +21 -0
- package/dist/store/sync/schema.js.map +1 -1
- package/dist/store/sync/seal-backlog.d.ts +21 -0
- package/dist/store/sync/seal-backlog.d.ts.map +1 -0
- package/dist/store/sync/seal-backlog.js +24 -0
- package/dist/store/sync/seal-backlog.js.map +1 -0
- package/dist/store/sync/sealer-values.d.ts +34 -0
- package/dist/store/sync/sealer-values.d.ts.map +1 -0
- package/dist/store/sync/sealer-values.js +53 -0
- package/dist/store/sync/sealer-values.js.map +1 -0
- package/dist/store/sync/sealer.d.ts +145 -0
- package/dist/store/sync/sealer.d.ts.map +1 -0
- package/dist/store/sync/sealer.js +906 -0
- package/dist/store/sync/sealer.js.map +1 -0
- package/dist/store/sync/structural.d.ts +63 -0
- package/dist/store/sync/structural.d.ts.map +1 -0
- package/dist/store/sync/structural.js +180 -0
- package/dist/store/sync/structural.js.map +1 -0
- package/dist/store/sync/timestamps.d.ts +92 -0
- package/dist/store/sync/timestamps.d.ts.map +1 -0
- package/dist/store/sync/timestamps.js +228 -0
- package/dist/store/sync/timestamps.js.map +1 -0
- package/dist/store/sync/tombstones.d.ts +75 -0
- package/dist/store/sync/tombstones.d.ts.map +1 -0
- package/dist/store/sync/tombstones.js +103 -0
- package/dist/store/sync/tombstones.js.map +1 -0
- package/dist/store/sync/trigger-classes.d.ts +221 -0
- package/dist/store/sync/trigger-classes.d.ts.map +1 -0
- package/dist/store/sync/trigger-classes.js +441 -0
- package/dist/store/sync/trigger-classes.js.map +1 -0
- package/dist/store/sync/trigger-suspend-at-path.d.ts +23 -0
- package/dist/store/sync/trigger-suspend-at-path.d.ts.map +1 -0
- package/dist/store/sync/trigger-suspend-at-path.js +44 -0
- package/dist/store/sync/trigger-suspend-at-path.js.map +1 -0
- package/dist/store/sync/writer-version.d.ts +43 -0
- package/dist/store/sync/writer-version.d.ts.map +1 -0
- package/dist/store/sync/writer-version.js +90 -0
- package/dist/store/sync/writer-version.js.map +1 -0
- package/dist/store/table-classification.d.ts.map +1 -1
- package/dist/store/table-classification.js +102 -0
- package/dist/store/table-classification.js.map +1 -1
- package/dist/store/trigger-suspend-schema.d.ts +44 -0
- package/dist/store/trigger-suspend-schema.d.ts.map +1 -0
- package/dist/store/trigger-suspend-schema.js +25 -0
- package/dist/store/trigger-suspend-schema.js.map +1 -0
- package/dist/store/vault-manifest.d.ts +365 -0
- package/dist/store/vault-manifest.d.ts.map +1 -0
- package/dist/store/vault-manifest.js +812 -0
- package/dist/store/vault-manifest.js.map +1 -0
- package/dist/store/writer-lease.d.ts +31 -0
- package/dist/store/writer-lease.d.ts.map +1 -1
- package/dist/store/writer-lease.js +65 -2
- package/dist/store/writer-lease.js.map +1 -1
- package/dist/system/health.d.ts.map +1 -1
- package/dist/system/health.js +21 -0
- package/dist/system/health.js.map +1 -1
- package/dist/system/project-health.d.ts.map +1 -1
- package/dist/system/project-health.js +3 -1
- package/dist/system/project-health.js.map +1 -1
- package/dist/tasks/affected-packages.d.ts +58 -0
- package/dist/tasks/affected-packages.d.ts.map +1 -1
- package/dist/tasks/affected-packages.js +75 -6
- package/dist/tasks/affected-packages.js.map +1 -1
- package/dist/tasks/affected-scope.d.ts +150 -23
- package/dist/tasks/affected-scope.d.ts.map +1 -1
- package/dist/tasks/affected-scope.js +320 -32
- package/dist/tasks/affected-scope.js.map +1 -1
- package/dist/tasks/change-set.d.ts +96 -0
- package/dist/tasks/change-set.d.ts.map +1 -1
- package/dist/tasks/change-set.js +169 -4
- package/dist/tasks/change-set.js.map +1 -1
- package/dist/tasks/complete-ci.d.ts +115 -0
- package/dist/tasks/complete-ci.d.ts.map +1 -0
- package/dist/tasks/complete-ci.js +171 -0
- package/dist/tasks/complete-ci.js.map +1 -0
- package/dist/tasks/complete.d.ts.map +1 -1
- package/dist/tasks/complete.js +50 -17
- package/dist/tasks/complete.js.map +1 -1
- package/dist/tasks/compute-task-view.d.ts.map +1 -1
- package/dist/tasks/compute-task-view.js +7 -0
- package/dist/tasks/compute-task-view.js.map +1 -1
- package/dist/tasks/done-plan.d.ts.map +1 -1
- package/dist/tasks/done-plan.js +51 -30
- package/dist/tasks/done-plan.js.map +1 -1
- package/dist/tasks/done-record.d.ts +6 -0
- package/dist/tasks/done-record.d.ts.map +1 -1
- package/dist/tasks/done-record.js +9 -3
- package/dist/tasks/done-record.js.map +1 -1
- package/dist/tasks/evidence.d.ts +3 -2
- package/dist/tasks/evidence.d.ts.map +1 -1
- package/dist/tasks/evidence.js +143 -12
- package/dist/tasks/evidence.js.map +1 -1
- package/dist/tasks/gate-notes.d.ts +22 -0
- package/dist/tasks/gate-notes.d.ts.map +1 -0
- package/dist/tasks/gate-notes.js +33 -0
- package/dist/tasks/gate-notes.js.map +1 -0
- package/dist/tasks/index.d.ts +2 -1
- package/dist/tasks/index.d.ts.map +1 -1
- package/dist/tasks/index.js +2 -1
- package/dist/tasks/index.js.map +1 -1
- package/dist/tasks/test-run-binding.d.ts +117 -0
- package/dist/tasks/test-run-binding.d.ts.map +1 -0
- package/dist/tasks/test-run-binding.js +365 -0
- package/dist/tasks/test-run-binding.js.map +1 -0
- package/dist/tasks/tool-cache-env.d.ts +123 -0
- package/dist/tasks/tool-cache-env.d.ts.map +1 -0
- package/dist/tasks/tool-cache-env.js +287 -0
- package/dist/tasks/tool-cache-env.js.map +1 -0
- package/dist/tasks/tool-cache-failed-first.d.ts +155 -0
- package/dist/tasks/tool-cache-failed-first.d.ts.map +1 -0
- package/dist/tasks/tool-cache-failed-first.js +285 -0
- package/dist/tasks/tool-cache-failed-first.js.map +1 -0
- package/dist/tasks/tool-cache.d.ts +375 -55
- package/dist/tasks/tool-cache.d.ts.map +1 -1
- package/dist/tasks/tool-cache.js +912 -303
- package/dist/tasks/tool-cache.js.map +1 -1
- package/dist/tasks/tool-resolver.d.ts +4 -3
- package/dist/tasks/tool-resolver.d.ts.map +1 -1
- package/dist/tasks/tool-resolver.js +15 -4
- package/dist/tasks/tool-resolver.js.map +1 -1
- package/dist/tasks/tool-semaphore.d.ts +67 -7
- package/dist/tasks/tool-semaphore.d.ts.map +1 -1
- package/dist/tasks/tool-semaphore.js +134 -56
- package/dist/tasks/tool-semaphore.js.map +1 -1
- package/dist/upgrade.d.ts.map +1 -1
- package/dist/upgrade.js +4 -0
- package/dist/upgrade.js.map +1 -1
- package/dist/validation/engine-ops.d.ts +6 -0
- package/dist/validation/engine-ops.d.ts.map +1 -1
- package/dist/validation/engine-ops.js +8 -0
- package/dist/validation/engine-ops.js.map +1 -1
- package/migrations/drizzle-cleo-project/20260930170000_t12819-trigger-suspend-clause/migration.sql +367 -0
- package/migrations/sync-journal/20260930120000_t12343-capture/migration.sql +47 -0
- package/migrations/sync-journal/20261001170000_t12984-sealer/migration.sql +101 -0
- package/package.json +13 -13
- package/templates/CLEO-INJECTION.md +5 -5
- package/templates/CLEO-REFERENCE.md +8 -4
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* macOS ResourceMonitor backend.
|
|
3
|
+
*
|
|
4
|
+
* macOS has no PSI. This backend maps the kernel's own signals onto the
|
|
5
|
+
* PSI-shaped {@link ResourceSample}, so `evaluateState` and the slot scaling
|
|
6
|
+
* work unchanged:
|
|
7
|
+
*
|
|
8
|
+
* - `kern.memorystatus_vm_pressure_level` — the kernel's memory pressure
|
|
9
|
+
* verdict (1 normal, 2 warning, 4 critical), the signal jetsam acts on
|
|
10
|
+
* - `kern.memorystatus_level` — free memory as a percentage of RAM, counting
|
|
11
|
+
* reclaimable pages (unlike `os.freemem()`, which reports only the free
|
|
12
|
+
* page list and reads near zero on a healthy Mac)
|
|
13
|
+
* - `vm.swapusage` — swap in use (reported, not scored: macOS allocates
|
|
14
|
+
* swapfiles on demand, so used/total sits at 80–95% on a healthy Mac)
|
|
15
|
+
* - `vm.loadavg` + `hw.perflevel{0,1}.logicalcpu` (or `hw.ncpu`) —
|
|
16
|
+
* run-queue length per effective core (CPU saturation)
|
|
17
|
+
*
|
|
18
|
+
* ## Sampling discipline (darwin amendment)
|
|
19
|
+
*
|
|
20
|
+
* Node has no sysctl binding, so `sample()` runs ONE `/usr/sbin/sysctl` exec
|
|
21
|
+
* (≈4 ms) for all names, and caches its output for `cacheTtlMs` (default 2 s)
|
|
22
|
+
* across every backend instance in the process. A poll loop or a burst of
|
|
23
|
+
* admissions therefore spawns at most one child per TTL. No other process or
|
|
24
|
+
* file read happens on the sample path; `sweepChildRss` (low frequency) uses
|
|
25
|
+
* `ps`.
|
|
26
|
+
*
|
|
27
|
+
* ## Mapping
|
|
28
|
+
*
|
|
29
|
+
* Memory (`globalPressure`): `some avg10` is the higher of a kernel-level score
|
|
30
|
+
* (warning 15, critical 40) and a free-memory score (`2 × (20 − free%)`,
|
|
31
|
+
* clamped at 0). `full avg10` is
|
|
32
|
+
* 15 when the kernel reports critical. With the monitor's default thresholds
|
|
33
|
+
* (hold 10 / backoff 20), a kernel warning holds and critical backs off.
|
|
34
|
+
*
|
|
35
|
+
* CPU (`cpuPressure`): effective cores are performance cores plus half the
|
|
36
|
+
* efficiency cores (a 6P+12E machine counts as 12; `hw.ncpu` when the split is
|
|
37
|
+
* unknown). With `r = load / cores`, `some = 100 × (r − 1) / r`
|
|
38
|
+
* once `r > 1`: the share of runnable work that is waiting for a core. A box
|
|
39
|
+
* running twice its cores scores 50. avg10/avg60/avg300 come from the 1/5/15
|
|
40
|
+
* minute load averages. Memory is instantaneous in all three windows.
|
|
41
|
+
*
|
|
42
|
+
* @module resources/darwin-backend
|
|
43
|
+
* @task T12981
|
|
44
|
+
* @epic T12978
|
|
45
|
+
*/
|
|
46
|
+
import { execFile } from 'node:child_process';
|
|
47
|
+
import { totalmem } from 'node:os';
|
|
48
|
+
/** The sysctl names one sample reads, in one exec. */
|
|
49
|
+
export const DARWIN_SYSCTL_NAMES = [
|
|
50
|
+
'kern.memorystatus_vm_pressure_level',
|
|
51
|
+
'kern.memorystatus_level',
|
|
52
|
+
'vm.swapusage',
|
|
53
|
+
'vm.loadavg',
|
|
54
|
+
'hw.ncpu',
|
|
55
|
+
'hw.perflevel0.logicalcpu',
|
|
56
|
+
'hw.perflevel1.logicalcpu',
|
|
57
|
+
];
|
|
58
|
+
const MIB = 1024 * 1024;
|
|
59
|
+
function parseSize(value) {
|
|
60
|
+
const m = /^([\d.]+)([KMGT])?$/.exec(value.trim());
|
|
61
|
+
if (!m?.[1])
|
|
62
|
+
return null;
|
|
63
|
+
const n = Number.parseFloat(m[1]);
|
|
64
|
+
const unit = { K: 1024, M: MIB, G: 1024 * MIB, T: 1024 * 1024 * MIB }[m[2] ?? ''] ?? 1;
|
|
65
|
+
return Number.isFinite(n) ? n * unit : null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Parse `sysctl <names>` output (`name: value` per line).
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* parseDarwinSysctl('kern.memorystatus_vm_pressure_level: 2\nhw.ncpu: 18\n');
|
|
73
|
+
* // → { pressureLevel: 2, ncpu: 18, freePercent: null, ... }
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export function parseDarwinSysctl(output) {
|
|
77
|
+
const values = new Map();
|
|
78
|
+
for (const line of output.split('\n')) {
|
|
79
|
+
const idx = line.indexOf(':');
|
|
80
|
+
if (idx <= 0)
|
|
81
|
+
continue;
|
|
82
|
+
values.set(line.slice(0, idx).trim(), line.slice(idx + 1).trim());
|
|
83
|
+
}
|
|
84
|
+
const int = (name) => {
|
|
85
|
+
const raw = values.get(name);
|
|
86
|
+
if (raw === undefined || !/^-?\d+$/.test(raw))
|
|
87
|
+
return null;
|
|
88
|
+
return Number.parseInt(raw, 10);
|
|
89
|
+
};
|
|
90
|
+
let swapTotalBytes = null;
|
|
91
|
+
let swapUsedBytes = null;
|
|
92
|
+
const swap = values.get('vm.swapusage');
|
|
93
|
+
if (swap) {
|
|
94
|
+
swapTotalBytes = parseSize(/total = (\S+)/.exec(swap)?.[1] ?? '');
|
|
95
|
+
swapUsedBytes = parseSize(/used = (\S+)/.exec(swap)?.[1] ?? '');
|
|
96
|
+
}
|
|
97
|
+
let loadAvg = null;
|
|
98
|
+
const load = values.get('vm.loadavg');
|
|
99
|
+
if (load) {
|
|
100
|
+
const nums = load
|
|
101
|
+
.replace(/[{}]/g, ' ')
|
|
102
|
+
.trim()
|
|
103
|
+
.split(/\s+/)
|
|
104
|
+
.map((s) => Number.parseFloat(s));
|
|
105
|
+
if (nums.length >= 3 && nums.slice(0, 3).every(Number.isFinite)) {
|
|
106
|
+
loadAvg = [nums[0], nums[1], nums[2]];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
pressureLevel: int('kern.memorystatus_vm_pressure_level'),
|
|
111
|
+
freePercent: int('kern.memorystatus_level'),
|
|
112
|
+
swapTotalBytes,
|
|
113
|
+
swapUsedBytes,
|
|
114
|
+
loadAvg,
|
|
115
|
+
ncpu: int('hw.ncpu'),
|
|
116
|
+
perfCores: int('hw.perflevel0.logicalcpu'),
|
|
117
|
+
efficiencyCores: int('hw.perflevel1.logicalcpu'),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function clamp(n) {
|
|
121
|
+
return Math.max(0, Math.min(100, n));
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Memory pressure score (PSI `some`-equivalent, 0–100) from darwin signals.
|
|
125
|
+
* Returns `null` when neither the kernel level nor free% is readable.
|
|
126
|
+
*/
|
|
127
|
+
export function darwinMemorySome(s) {
|
|
128
|
+
if (s.pressureLevel === null && s.freePercent === null)
|
|
129
|
+
return null;
|
|
130
|
+
const levelScore = s.pressureLevel === 4 ? 40 : s.pressureLevel === 2 ? 15 : 0;
|
|
131
|
+
const freeScore = s.freePercent === null ? 0 : Math.max(0, 2 * (20 - s.freePercent));
|
|
132
|
+
return clamp(Math.max(levelScore, freeScore));
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Cores that absorb load: performance cores plus half the efficiency cores,
|
|
136
|
+
* else `hw.ncpu`. `null` when neither is known.
|
|
137
|
+
*/
|
|
138
|
+
export function effectiveCores(s) {
|
|
139
|
+
if (s.perfCores !== null && s.perfCores > 0) {
|
|
140
|
+
return s.perfCores + (s.efficiencyCores ?? 0) / 2;
|
|
141
|
+
}
|
|
142
|
+
return s.ncpu;
|
|
143
|
+
}
|
|
144
|
+
/** CPU waiting share (0–100) for a load average on `ncpu` cores. */
|
|
145
|
+
export function cpuSomeFromLoad(load, ncpu) {
|
|
146
|
+
const r = load / Math.max(1, ncpu);
|
|
147
|
+
return r <= 1 ? 0 : clamp((100 * (r - 1)) / r);
|
|
148
|
+
}
|
|
149
|
+
function line(avg10, avg60, avg300) {
|
|
150
|
+
return { avg10, avg60, avg300, totalUs: 0 };
|
|
151
|
+
}
|
|
152
|
+
/** Build the memory and CPU PSI-equivalents from parsed signals. */
|
|
153
|
+
export function darwinPressure(s) {
|
|
154
|
+
const some = darwinMemorySome(s);
|
|
155
|
+
const memory = some === null
|
|
156
|
+
? null
|
|
157
|
+
: {
|
|
158
|
+
some: line(some, some, some),
|
|
159
|
+
full: s.pressureLevel === 4 ? line(15, 15, 15) : line(0, 0, 0),
|
|
160
|
+
};
|
|
161
|
+
let cpu = null;
|
|
162
|
+
const cores = effectiveCores(s);
|
|
163
|
+
if (s.loadAvg && cores) {
|
|
164
|
+
const [l1, l5, l15] = s.loadAvg;
|
|
165
|
+
cpu = {
|
|
166
|
+
some: line(cpuSomeFromLoad(l1, cores), cpuSomeFromLoad(l5, cores), cpuSomeFromLoad(l15, cores)),
|
|
167
|
+
full: null,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
return { memory, cpu };
|
|
171
|
+
}
|
|
172
|
+
const defaultSysctl = (names) => new Promise((resolve, reject) => {
|
|
173
|
+
execFile('/usr/sbin/sysctl', [...names], { timeout: 2000 }, (err, stdout) => {
|
|
174
|
+
// sysctl exits non-zero when ONE name is unknown but still prints the
|
|
175
|
+
// others; keep whatever it printed.
|
|
176
|
+
if (stdout)
|
|
177
|
+
resolve(stdout);
|
|
178
|
+
else
|
|
179
|
+
reject(err ?? new Error('sysctl produced no output'));
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
const defaultPsRss = (pids) => new Promise((resolve) => {
|
|
183
|
+
execFile('/bin/ps', ['-o', 'pid=,rss=', '-p', pids.join(',')], { timeout: 2000 }, (_err, stdout) => resolve(stdout ?? ''));
|
|
184
|
+
});
|
|
185
|
+
const defaultStat = async (path) => {
|
|
186
|
+
const { stat } = await import('node:fs/promises');
|
|
187
|
+
try {
|
|
188
|
+
return { size: (await stat(path)).size };
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
/** Process-wide cache of the default sysctl read. */
|
|
195
|
+
let sharedRead = null;
|
|
196
|
+
/** Reset the process-wide sysctl cache (tests). @internal */
|
|
197
|
+
export function resetDarwinSysctlCache() {
|
|
198
|
+
sharedRead = null;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* macOS implementation of {@link ResourceBackend}.
|
|
202
|
+
*
|
|
203
|
+
* `pressureAvailable` is `true` when the kernel memory signals were readable,
|
|
204
|
+
* so `evaluateState` uses the PSI path rather than degraded mode.
|
|
205
|
+
*/
|
|
206
|
+
export class DarwinResourceBackend {
|
|
207
|
+
walPaths;
|
|
208
|
+
cacheTtlMs;
|
|
209
|
+
sysctlFn;
|
|
210
|
+
psRssFn;
|
|
211
|
+
statFileFn;
|
|
212
|
+
totalMemBytes;
|
|
213
|
+
now;
|
|
214
|
+
local = null;
|
|
215
|
+
constructor(opts = {}) {
|
|
216
|
+
this.walPaths = opts.walPaths ?? [];
|
|
217
|
+
this.cacheTtlMs = opts.cacheTtlMs ?? 2000;
|
|
218
|
+
this.sysctlFn = opts.sysctlFn ?? null;
|
|
219
|
+
this.psRssFn = opts.psRssFn ?? defaultPsRss;
|
|
220
|
+
this.statFileFn = opts.statFileFn ?? defaultStat;
|
|
221
|
+
this.totalMemBytes = opts.totalMemBytes ?? totalmem();
|
|
222
|
+
this.now = opts.now ?? Date.now;
|
|
223
|
+
}
|
|
224
|
+
/** At most one sysctl exec per TTL; on failure the sample is degraded. */
|
|
225
|
+
async sample() {
|
|
226
|
+
const sampledAtMs = this.now();
|
|
227
|
+
let signals = null;
|
|
228
|
+
try {
|
|
229
|
+
signals = parseDarwinSysctl(await this.readSysctl(sampledAtMs));
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
signals = null;
|
|
233
|
+
}
|
|
234
|
+
const { memory, cpu } = signals ? darwinPressure(signals) : { memory: null, cpu: null };
|
|
235
|
+
const memAvailableBytes = signals?.freePercent != null
|
|
236
|
+
? Math.round((signals.freePercent / 100) * this.totalMemBytes)
|
|
237
|
+
: null;
|
|
238
|
+
const walObservations = [];
|
|
239
|
+
for (const walPath of this.walPaths) {
|
|
240
|
+
const info = await this.statFileFn(walPath);
|
|
241
|
+
walObservations.push({ walPath, sizeBytes: info?.size ?? null });
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
sampledAtMs,
|
|
245
|
+
pressureAvailable: memory !== null,
|
|
246
|
+
memAvailableBytes,
|
|
247
|
+
globalPressure: memory,
|
|
248
|
+
slicePressure: null,
|
|
249
|
+
cpuPressure: cpu,
|
|
250
|
+
walObservations,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/** RSS via one `ps` exec (no PSS on macOS: `pssBytes` mirrors RSS). */
|
|
254
|
+
async sweepChildRss(pids) {
|
|
255
|
+
const sampledAtMs = this.now();
|
|
256
|
+
const wanted = pids.filter((p) => Number.isInteger(p) && p > 0);
|
|
257
|
+
if (wanted.length === 0)
|
|
258
|
+
return { sampledAtMs, entries: [] };
|
|
259
|
+
const entries = [];
|
|
260
|
+
for (const row of (await this.psRssFn(wanted)).split('\n')) {
|
|
261
|
+
const m = /^\s*(\d+)\s+(\d+)\s*$/.exec(row);
|
|
262
|
+
if (!m?.[1] || !m[2])
|
|
263
|
+
continue;
|
|
264
|
+
const rssBytes = Number.parseInt(m[2], 10) * 1024;
|
|
265
|
+
entries.push({ pid: Number.parseInt(m[1], 10), rssBytes, pssBytes: rssBytes });
|
|
266
|
+
}
|
|
267
|
+
return { sampledAtMs, entries };
|
|
268
|
+
}
|
|
269
|
+
readSysctl(at) {
|
|
270
|
+
const fresh = (c) => c !== null && at - c.at < this.cacheTtlMs && at >= c.at;
|
|
271
|
+
if (this.sysctlFn) {
|
|
272
|
+
if (!fresh(this.local))
|
|
273
|
+
this.local = { at, output: this.sysctlFn(DARWIN_SYSCTL_NAMES) };
|
|
274
|
+
return this.local.output;
|
|
275
|
+
}
|
|
276
|
+
if (!fresh(sharedRead)) {
|
|
277
|
+
const output = defaultSysctl(DARWIN_SYSCTL_NAMES);
|
|
278
|
+
// A failed read must not be cached for the whole TTL.
|
|
279
|
+
output.catch(() => {
|
|
280
|
+
if (sharedRead?.output === output)
|
|
281
|
+
sharedRead = null;
|
|
282
|
+
});
|
|
283
|
+
sharedRead = { at, output };
|
|
284
|
+
}
|
|
285
|
+
return sharedRead.output;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=darwin-backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"darwin-backend.js","sourceRoot":"","sources":["../../src/resources/darwin-backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,11 +20,16 @@
|
|
|
20
20
|
* `interactive-cli` is NEVER gated; `full-build` is pinned to one machine-wide
|
|
21
21
|
* slot regardless of pressure.
|
|
22
22
|
*
|
|
23
|
+
* A local slot whose holder process is provably gone is reaped at once instead
|
|
24
|
+
* of waiting out the 10 min stale timeout (T12963), as the tool semaphore does
|
|
25
|
+
* for its slots (gh#1222).
|
|
26
|
+
*
|
|
23
27
|
* @task T11999
|
|
28
|
+
* @task T12963
|
|
24
29
|
* @epic T11992
|
|
25
30
|
* @adr resource-governor-never-oom-architecture §3.4
|
|
26
31
|
*/
|
|
27
|
-
import { type AdmissionResult, type GovernorMode, type ResourceClass } from '@cleocode/contracts';
|
|
32
|
+
import { type AdmissionResult, type GovernorMode, type ResourceClass, type ResourceGrant } from '@cleocode/contracts';
|
|
28
33
|
import type { ResourceSample } from './backend.js';
|
|
29
34
|
import { ResourceMonitor } from './monitor.js';
|
|
30
35
|
/**
|
|
@@ -86,6 +91,38 @@ export declare function computeClassBudget(cls: ResourceClass, sample: ResourceS
|
|
|
86
91
|
* and PIDs — exactly like the tool semaphore.
|
|
87
92
|
*/
|
|
88
93
|
export declare function governorSlotDir(cls: ResourceClass): string;
|
|
94
|
+
export { SLOT_LOCK_STALE_MS, SLOT_LOCK_UPDATE_MS } from './slot-holder.js';
|
|
95
|
+
/** An ungated grant (no slot held, `slot: -1`): `off` mode, `interactive-cli`, a nested run, fail-open. */
|
|
96
|
+
export declare function passThroughGrant(cls: ResourceClass): ResourceGrant;
|
|
97
|
+
/** Why the governor could not keep its state: the errno code and the path, when known. */
|
|
98
|
+
export interface GovernorIoError {
|
|
99
|
+
readonly code: string;
|
|
100
|
+
readonly path: string | null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The code and path of an error that means "the governor cannot keep state here" (a sandbox that
|
|
104
|
+
* blocks writes to the CLEO home, a read-only or full disk, a broken CLEO home), or null for any
|
|
105
|
+
* other error, which is a bug and must not be hidden.
|
|
106
|
+
*/
|
|
107
|
+
export declare function governorIoError(err: unknown): GovernorIoError | null;
|
|
108
|
+
/**
|
|
109
|
+
* Run an admission, failing OPEN when the governor cannot write its state
|
|
110
|
+
* ({@link governorIoError}): the work proceeds ungated with a
|
|
111
|
+
* {@link passThroughGrant}, and `ungoverned` says why. A sandboxed or
|
|
112
|
+
* read-only CLEO home must never turn into a deferral that never clears.
|
|
113
|
+
* Any other error is a bug and propagates.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```ts
|
|
117
|
+
* const { admission, ungoverned } = await admitFailOpen('agent-session', () =>
|
|
118
|
+
* governor.tryAcquire('agent-session'),
|
|
119
|
+
* );
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export declare function admitFailOpen(cls: ResourceClass, acquire: () => Promise<AdmissionResult>): Promise<{
|
|
123
|
+
readonly admission: AdmissionResult;
|
|
124
|
+
readonly ungoverned: GovernorIoError | null;
|
|
125
|
+
}>;
|
|
89
126
|
/** Options for {@link ResourceGovernor.acquire}. */
|
|
90
127
|
export interface AcquireOptions extends BudgetOptions {
|
|
91
128
|
/**
|
|
@@ -117,6 +154,12 @@ export declare class ResourceGovernor {
|
|
|
117
154
|
* Acquire one slot of `cls`. Returns a {@link ResourceGrant} on success or a
|
|
118
155
|
* {@link ResourceDeferral} on denial. Never throws for admission control;
|
|
119
156
|
* only genuinely unexpected I/O errors propagate.
|
|
157
|
+
*
|
|
158
|
+
* Only a held lock (`ELOCKED`) counts as a busy slot. Any other lock error
|
|
159
|
+
* (EACCES from a sandbox that can't write the slot dir, EROFS, ENOSPC…) is
|
|
160
|
+
* thrown once a pass found no free slot, never read as "busy": a slot that
|
|
161
|
+
* can never be taken would otherwise defer every caller until it times out
|
|
162
|
+
* (#1777 round 8, R8-1). Callers fail open with {@link admitFailOpen}.
|
|
120
163
|
*/
|
|
121
164
|
acquire(cls: ResourceClass, opts?: AcquireOptions): Promise<AdmissionResult>;
|
|
122
165
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"governor.d.ts","sourceRoot":"","sources":["../../src/resources/governor.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"governor.d.ts","sourceRoot":"","sources":["../../src/resources/governor.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,7 +20,12 @@
|
|
|
20
20
|
* `interactive-cli` is NEVER gated; `full-build` is pinned to one machine-wide
|
|
21
21
|
* slot regardless of pressure.
|
|
22
22
|
*
|
|
23
|
+
* A local slot whose holder process is provably gone is reaped at once instead
|
|
24
|
+
* of waiting out the 10 min stale timeout (T12963), as the tool semaphore does
|
|
25
|
+
* for its slots (gh#1222).
|
|
26
|
+
*
|
|
23
27
|
* @task T11999
|
|
28
|
+
* @task T12963
|
|
24
29
|
* @epic T11992
|
|
25
30
|
* @adr resource-governor-never-oom-architecture §3.4
|
|
26
31
|
*/
|
|
@@ -28,10 +33,11 @@ import { existsSync, mkdirSync, readdirSync, writeFileSync } from 'node:fs';
|
|
|
28
33
|
import { availableParallelism, totalmem } from 'node:os';
|
|
29
34
|
import { join } from 'node:path';
|
|
30
35
|
import { DEFAULT_RESOURCE_RETRY_AFTER_MS, } from '@cleocode/contracts';
|
|
31
|
-
import lockfile from 'proper-lockfile';
|
|
32
36
|
import { getLogger } from '../logger.js';
|
|
33
37
|
import { getCleoHome } from '../paths.js';
|
|
34
|
-
import { ResourceMonitor } from './monitor.js';
|
|
38
|
+
import { pressureScore, ResourceMonitor } from './monitor.js';
|
|
39
|
+
import { parentRunJob } from './run-admission.js';
|
|
40
|
+
import { _resetSlotHolderStateForTest, describeSlotHolders, lockSlot } from './slot-holder.js';
|
|
35
41
|
import { resolveSupervisorSocketPath, sendResourceAdmit, sendResourceRelease, } from './supervisor-admit.js';
|
|
36
42
|
let _log = null;
|
|
37
43
|
function log() {
|
|
@@ -77,12 +83,13 @@ function logSupervisorDegradeOnce(reason) {
|
|
|
77
83
|
export function _resetGovernorStateForTest() {
|
|
78
84
|
_cachedMode = null;
|
|
79
85
|
_supervisorDegradeLogged = false;
|
|
86
|
+
_resetSlotHolderStateForTest();
|
|
80
87
|
}
|
|
81
88
|
const MB = 1024 * 1024;
|
|
82
89
|
/** Extract `some avg10` (0–100) from a sample; 0 when unavailable. */
|
|
83
90
|
function someAvg10(sample) {
|
|
84
|
-
|
|
85
|
-
return
|
|
91
|
+
// T12981: memory or CPU, whichever is worse (CPU rescaled to this scale).
|
|
92
|
+
return pressureScore(sample);
|
|
86
93
|
}
|
|
87
94
|
/**
|
|
88
95
|
* Compute the slot budget for a class given a point-sample.
|
|
@@ -173,11 +180,14 @@ function ensureSlotFiles(dir, count) {
|
|
|
173
180
|
}
|
|
174
181
|
return paths;
|
|
175
182
|
}
|
|
176
|
-
|
|
183
|
+
// The slot timing constants live with the slot locks (T12963); re-exported
|
|
184
|
+
// for existing importers.
|
|
185
|
+
export { SLOT_LOCK_STALE_MS, SLOT_LOCK_UPDATE_MS } from './slot-holder.js';
|
|
177
186
|
function sleep(ms) {
|
|
178
187
|
return new Promise((r) => setTimeout(r, ms));
|
|
179
188
|
}
|
|
180
|
-
|
|
189
|
+
/** An ungated grant (no slot held, `slot: -1`): `off` mode, `interactive-cli`, a nested run, fail-open. */
|
|
190
|
+
export function passThroughGrant(cls) {
|
|
181
191
|
return {
|
|
182
192
|
deferred: false,
|
|
183
193
|
class: cls,
|
|
@@ -189,6 +199,54 @@ function passThroughGrant(cls) {
|
|
|
189
199
|
function deferral(cls, reason, retryAfterMs) {
|
|
190
200
|
return { deferred: true, class: cls, retryAfterMs, reason };
|
|
191
201
|
}
|
|
202
|
+
/** Filesystem errors meaning the governor's state can't be written here. */
|
|
203
|
+
const GOVERNOR_IO_CODES = new Set([
|
|
204
|
+
'EACCES',
|
|
205
|
+
'EPERM',
|
|
206
|
+
'EROFS',
|
|
207
|
+
'ENOSPC',
|
|
208
|
+
'EDQUOT',
|
|
209
|
+
'ENOTDIR',
|
|
210
|
+
'ELOOP',
|
|
211
|
+
]);
|
|
212
|
+
/**
|
|
213
|
+
* The code and path of an error that means "the governor cannot keep state here" (a sandbox that
|
|
214
|
+
* blocks writes to the CLEO home, a read-only or full disk, a broken CLEO home), or null for any
|
|
215
|
+
* other error, which is a bug and must not be hidden.
|
|
216
|
+
*/
|
|
217
|
+
export function governorIoError(err) {
|
|
218
|
+
const e = err;
|
|
219
|
+
if (!e || typeof e !== 'object' || typeof e.code !== 'string')
|
|
220
|
+
return null;
|
|
221
|
+
if (!GOVERNOR_IO_CODES.has(e.code))
|
|
222
|
+
return null;
|
|
223
|
+
return { code: e.code, path: typeof e.path === 'string' ? e.path : null };
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Run an admission, failing OPEN when the governor cannot write its state
|
|
227
|
+
* ({@link governorIoError}): the work proceeds ungated with a
|
|
228
|
+
* {@link passThroughGrant}, and `ungoverned` says why. A sandboxed or
|
|
229
|
+
* read-only CLEO home must never turn into a deferral that never clears.
|
|
230
|
+
* Any other error is a bug and propagates.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```ts
|
|
234
|
+
* const { admission, ungoverned } = await admitFailOpen('agent-session', () =>
|
|
235
|
+
* governor.tryAcquire('agent-session'),
|
|
236
|
+
* );
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
export async function admitFailOpen(cls, acquire) {
|
|
240
|
+
try {
|
|
241
|
+
return { admission: await acquire(), ungoverned: null };
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
const io = governorIoError(err);
|
|
245
|
+
if (io === null)
|
|
246
|
+
throw err;
|
|
247
|
+
return { admission: passThroughGrant(cls), ungoverned: io };
|
|
248
|
+
}
|
|
249
|
+
}
|
|
192
250
|
let _supervisorHolderSeq = 0;
|
|
193
251
|
/**
|
|
194
252
|
* A process-unique holder id for a supervisor `resource_admit`, so each acquire
|
|
@@ -209,6 +267,12 @@ export class ResourceGovernor {
|
|
|
209
267
|
* Acquire one slot of `cls`. Returns a {@link ResourceGrant} on success or a
|
|
210
268
|
* {@link ResourceDeferral} on denial. Never throws for admission control;
|
|
211
269
|
* only genuinely unexpected I/O errors propagate.
|
|
270
|
+
*
|
|
271
|
+
* Only a held lock (`ELOCKED`) counts as a busy slot. Any other lock error
|
|
272
|
+
* (EACCES from a sandbox that can't write the slot dir, EROFS, ENOSPC…) is
|
|
273
|
+
* thrown once a pass found no free slot, never read as "busy": a slot that
|
|
274
|
+
* can never be taken would otherwise defer every caller until it times out
|
|
275
|
+
* (#1777 round 8, R8-1). Callers fail open with {@link admitFailOpen}.
|
|
212
276
|
*/
|
|
213
277
|
async acquire(cls, opts = {}) {
|
|
214
278
|
// Ungated fast paths: off mode + interactive-cli are pure pass-through.
|
|
@@ -216,6 +280,15 @@ export class ResourceGovernor {
|
|
|
216
280
|
if (mode === 'off' || cls === 'interactive-cli') {
|
|
217
281
|
return passThroughGrant(cls);
|
|
218
282
|
}
|
|
283
|
+
// Inside a running `cleo run` job (e.g. `cleo run -- cleo verify`), the
|
|
284
|
+
// job's slot already covers this process tree: waiting for another slot
|
|
285
|
+
// of a budget-1 class would wait on ourselves (#1777 round 3, M-2). The
|
|
286
|
+
// env var only says "look"; parentRunJob's group + start-time check is
|
|
287
|
+
// what decides, so setting it by hand grants nothing.
|
|
288
|
+
// Only a parent holding THIS class covers it (#1777 round 4, MED-1).
|
|
289
|
+
if (process.env.CLEO_RUN_CLASS !== undefined && parentRunJob({ pid: process.pid, cls })) {
|
|
290
|
+
return passThroughGrant(cls);
|
|
291
|
+
}
|
|
219
292
|
const sample = opts.sample ?? (await (opts.monitor ?? new ResourceMonitor()).sample());
|
|
220
293
|
const budget = computeClassBudget(cls, sample, opts);
|
|
221
294
|
if (!Number.isFinite(budget))
|
|
@@ -242,45 +315,32 @@ export class ResourceGovernor {
|
|
|
242
315
|
do {
|
|
243
316
|
// Re-shuffle each pass so concurrent acquirers don't collide on slot 0.
|
|
244
317
|
const order = shuffledIndices(slots.length);
|
|
318
|
+
let lockError = null;
|
|
245
319
|
for (const idx of order) {
|
|
246
320
|
const path = slots[idx];
|
|
247
321
|
if (!path)
|
|
248
322
|
continue;
|
|
249
323
|
try {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
let released = false;
|
|
256
|
-
return {
|
|
257
|
-
deferred: false,
|
|
258
|
-
class: cls,
|
|
259
|
-
slot: idx,
|
|
260
|
-
acquiredAtMs: Date.now(),
|
|
261
|
-
release: async () => {
|
|
262
|
-
if (released)
|
|
263
|
-
return;
|
|
264
|
-
released = true;
|
|
265
|
-
try {
|
|
266
|
-
await release();
|
|
267
|
-
}
|
|
268
|
-
catch {
|
|
269
|
-
// Already released (e.g. stale recovery) — post-condition holds.
|
|
270
|
-
}
|
|
271
|
-
},
|
|
272
|
-
};
|
|
324
|
+
// T12963: a busy slot whose holder is provably dead is reaped here.
|
|
325
|
+
const release = await lockSlot(path, cls);
|
|
326
|
+
if (release) {
|
|
327
|
+
return { deferred: false, class: cls, slot: idx, acquiredAtMs: Date.now(), release };
|
|
328
|
+
}
|
|
273
329
|
}
|
|
274
|
-
catch {
|
|
275
|
-
//
|
|
330
|
+
catch (err) {
|
|
331
|
+
// Anything but a held lock is not "busy" (#1777 round 8, R8-1).
|
|
332
|
+
lockError ??= { err };
|
|
276
333
|
}
|
|
277
334
|
}
|
|
335
|
+
if (lockError !== null)
|
|
336
|
+
throw lockError.err;
|
|
278
337
|
if (!blocking)
|
|
279
338
|
break;
|
|
280
339
|
await sleep(pollMs);
|
|
281
340
|
} while (Date.now() - startedAt < timeoutMs);
|
|
282
341
|
return deferral(cls, `class '${cls}' is at capacity (${budget} slot(s)); ` +
|
|
283
|
-
(blocking ? `timed out after ${timeoutMs}ms` : 'no slot free')
|
|
342
|
+
(blocking ? `timed out after ${timeoutMs}ms` : 'no slot free') +
|
|
343
|
+
describeSlotHolders(slots), Math.min(pollMs * 4, DEFAULT_RESOURCE_RETRY_AFTER_MS));
|
|
284
344
|
}
|
|
285
345
|
/**
|
|
286
346
|
* Route an admission through the supervisor's central `resource_admit` verb.
|
|
@@ -355,7 +415,8 @@ function shuffledIndices(n) {
|
|
|
355
415
|
/**
|
|
356
416
|
* Count how many of `budget` slots are currently held, by probing each with a
|
|
357
417
|
* non-blocking lock. A successful probe-lock is released immediately — it never
|
|
358
|
-
* holds the slot, so it cannot starve a real acquirer.
|
|
418
|
+
* holds the slot, so it cannot starve a real acquirer. A slot held by a dead
|
|
419
|
+
* process is reaped, not counted (T12963).
|
|
359
420
|
*/
|
|
360
421
|
async function countHeldSlots(cls, budget) {
|
|
361
422
|
const dir = governorSlotDir(cls);
|
|
@@ -365,11 +426,14 @@ async function countHeldSlots(cls, budget) {
|
|
|
365
426
|
let held = 0;
|
|
366
427
|
for (const path of slots) {
|
|
367
428
|
try {
|
|
368
|
-
const release = await
|
|
369
|
-
|
|
429
|
+
const release = await lockSlot(path, cls);
|
|
430
|
+
if (release)
|
|
431
|
+
await release();
|
|
432
|
+
else
|
|
433
|
+
held++;
|
|
370
434
|
}
|
|
371
435
|
catch {
|
|
372
|
-
held
|
|
436
|
+
// Only a held lock is a holder; an unwritable slot dir is not (R8-1).
|
|
373
437
|
}
|
|
374
438
|
}
|
|
375
439
|
return held;
|