@cleocode/core 2026.10.3 → 2026.10.5-rc.ci.672
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/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +7 -37
- package/dist/bootstrap.js.map +1 -1
- package/dist/build-provenance.json +2 -2
- package/dist/caamp/adapter.d.ts.map +1 -1
- package/dist/caamp/adapter.js +5 -2
- package/dist/caamp/adapter.js.map +1 -1
- package/dist/changesets/parse-frontmatter.d.ts +1 -1
- package/dist/changesets/parse-frontmatter.d.ts.map +1 -1
- package/dist/changesets/parse-frontmatter.js +2 -1
- package/dist/changesets/parse-frontmatter.js.map +1 -1
- package/dist/changesets/parser.d.ts +1 -1
- package/dist/changesets/parser.d.ts.map +1 -1
- package/dist/changesets/parser.js +2 -1
- package/dist/changesets/parser.js.map +1 -1
- package/dist/changesets/writer.d.ts +8 -1
- package/dist/changesets/writer.d.ts.map +1 -1
- package/dist/changesets/writer.js +56 -5
- package/dist/changesets/writer.js.map +1 -1
- package/dist/cloud/keys.d.ts +9 -0
- package/dist/cloud/keys.d.ts.map +1 -1
- package/dist/cloud/keys.js +11 -0
- package/dist/cloud/keys.js.map +1 -1
- package/dist/cloud/nexus-attach.d.ts +17 -0
- package/dist/cloud/nexus-attach.d.ts.map +1 -1
- package/dist/cloud/nexus-attach.js +30 -9
- package/dist/cloud/nexus-attach.js.map +1 -1
- package/dist/cloud/nexus-auth.d.ts +7 -1
- package/dist/cloud/nexus-auth.d.ts.map +1 -1
- package/dist/cloud/nexus-auth.js +7 -1
- package/dist/cloud/nexus-auth.js.map +1 -1
- package/dist/cloud/nexus-cloud-activity.d.ts.map +1 -1
- package/dist/cloud/nexus-cloud-activity.js +2 -1
- package/dist/cloud/nexus-cloud-activity.js.map +1 -1
- package/dist/cloud/nexus-cloud-conflicts.d.ts +48 -0
- package/dist/cloud/nexus-cloud-conflicts.d.ts.map +1 -0
- package/dist/cloud/nexus-cloud-conflicts.js +68 -0
- package/dist/cloud/nexus-cloud-conflicts.js.map +1 -0
- package/dist/cloud/nexus-cloud-status.d.ts +42 -6
- package/dist/cloud/nexus-cloud-status.d.ts.map +1 -1
- package/dist/cloud/nexus-cloud-status.js +233 -19
- package/dist/cloud/nexus-cloud-status.js.map +1 -1
- package/dist/cloud/nexus-cloud.d.ts +18 -1
- package/dist/cloud/nexus-cloud.d.ts.map +1 -1
- package/dist/cloud/nexus-cloud.js +56 -2
- package/dist/cloud/nexus-cloud.js.map +1 -1
- package/dist/cloud/nexus-credentials.d.ts +1 -1
- package/dist/cloud/nexus-credentials.d.ts.map +1 -1
- package/dist/cloud/nexus-credentials.js +1 -1
- package/dist/cloud/nexus-credentials.js.map +1 -1
- package/dist/cloud/nexus-enrol.d.ts +10 -1
- package/dist/cloud/nexus-enrol.d.ts.map +1 -1
- package/dist/cloud/nexus-enrol.js +47 -3
- package/dist/cloud/nexus-enrol.js.map +1 -1
- package/dist/cloud/nexus-first-run.d.ts +146 -0
- package/dist/cloud/nexus-first-run.d.ts.map +1 -0
- package/dist/cloud/nexus-first-run.js +358 -0
- package/dist/cloud/nexus-first-run.js.map +1 -0
- package/dist/cloud/nexus-link.d.ts +35 -4
- package/dist/cloud/nexus-link.d.ts.map +1 -1
- package/dist/cloud/nexus-link.js +172 -7
- package/dist/cloud/nexus-link.js.map +1 -1
- package/dist/cloud/nexus-presence-refresh.d.ts +65 -0
- package/dist/cloud/nexus-presence-refresh.d.ts.map +1 -0
- package/dist/cloud/nexus-presence-refresh.js +122 -0
- package/dist/cloud/nexus-presence-refresh.js.map +1 -0
- package/dist/cloud/nexus-project-history.d.ts +54 -0
- package/dist/cloud/nexus-project-history.d.ts.map +1 -0
- package/dist/cloud/nexus-project-history.js +108 -0
- package/dist/cloud/nexus-project-history.js.map +1 -0
- package/dist/cloud/nexus-project-names.d.ts +173 -0
- package/dist/cloud/nexus-project-names.d.ts.map +1 -0
- package/dist/cloud/nexus-project-names.js +314 -0
- package/dist/cloud/nexus-project-names.js.map +1 -0
- package/dist/cloud/nexus-vault-keys.d.ts +33 -2
- package/dist/cloud/nexus-vault-keys.d.ts.map +1 -1
- package/dist/cloud/nexus-vault-keys.js +144 -18
- package/dist/cloud/nexus-vault-keys.js.map +1 -1
- package/dist/cloud/nexus-vault.d.ts +7 -3
- package/dist/cloud/nexus-vault.d.ts.map +1 -1
- package/dist/cloud/nexus-vault.js +176 -29
- package/dist/cloud/nexus-vault.js.map +1 -1
- package/dist/cloud/presence.d.ts +1 -1
- package/dist/cloud/presence.d.ts.map +1 -1
- package/dist/cloud/presence.js +1 -1
- package/dist/cloud/presence.js.map +1 -1
- package/dist/cloud/projects.d.ts +7 -4
- package/dist/cloud/projects.d.ts.map +1 -1
- package/dist/cloud/projects.js +10 -1
- package/dist/cloud/projects.js.map +1 -1
- package/dist/cloud/signing.d.ts +14 -0
- package/dist/cloud/signing.d.ts.map +1 -1
- package/dist/cloud/signing.js +9 -0
- package/dist/cloud/signing.js.map +1 -1
- package/dist/conduit/cloud-endpoint.d.ts +96 -0
- package/dist/conduit/cloud-endpoint.d.ts.map +1 -0
- package/dist/conduit/cloud-endpoint.js +191 -0
- package/dist/conduit/cloud-endpoint.js.map +1 -0
- package/dist/conduit/conduit-client.d.ts.map +1 -1
- package/dist/conduit/conduit-client.js +2 -1
- package/dist/conduit/conduit-client.js.map +1 -1
- package/dist/conduit/factory.d.ts +4 -0
- package/dist/conduit/factory.d.ts.map +1 -1
- package/dist/conduit/factory.js +7 -1
- package/dist/conduit/factory.js.map +1 -1
- package/dist/conduit/http-transport.d.ts +12 -7
- package/dist/conduit/http-transport.d.ts.map +1 -1
- package/dist/conduit/http-transport.js +18 -11
- package/dist/conduit/http-transport.js.map +1 -1
- package/dist/conduit/index.d.ts +3 -1
- package/dist/conduit/index.d.ts.map +1 -1
- package/dist/conduit/index.js +3 -1
- package/dist/conduit/index.js.map +1 -1
- package/dist/conduit/sse-transport.d.ts +5 -1
- package/dist/conduit/sse-transport.d.ts.map +1 -1
- package/dist/conduit/sse-transport.js +10 -2
- package/dist/conduit/sse-transport.js.map +1 -1
- package/dist/config/registry.js +1 -1
- package/dist/config/registry.js.map +1 -1
- package/dist/core-paths.d.ts +13 -0
- package/dist/core-paths.d.ts.map +1 -0
- package/dist/core-paths.js +13 -0
- package/dist/core-paths.js.map +1 -0
- package/dist/decide/bench/source.js +1 -1
- package/dist/decide/bench/source.js.map +1 -1
- package/dist/decide/client.d.ts +1 -1
- package/dist/decide/client.d.ts.map +1 -1
- package/dist/decide/client.js +1 -1
- package/dist/decide/client.js.map +1 -1
- package/dist/decide/credentials.d.ts +1 -1
- package/dist/decide/credentials.d.ts.map +1 -1
- package/dist/decide/credentials.js +1 -1
- package/dist/decide/credentials.js.map +1 -1
- package/dist/decide/jev-wire.d.ts +1 -1
- package/dist/decide/jev-wire.d.ts.map +1 -1
- package/dist/decide/jev-wire.js +1 -1
- package/dist/decide/jev-wire.js.map +1 -1
- package/dist/decide/operations.d.ts +1 -1
- package/dist/decide/operations.d.ts.map +1 -1
- package/dist/decide/operations.js +1 -1
- package/dist/decide/operations.js.map +1 -1
- package/dist/decide/providers.d.ts +1 -1
- package/dist/decide/providers.d.ts.map +1 -1
- package/dist/decide/providers.js +1 -1
- package/dist/decide/providers.js.map +1 -1
- package/dist/decide/wizard.d.ts.map +1 -1
- package/dist/decide/wizard.js +1 -1
- package/dist/decide/wizard.js.map +1 -1
- package/dist/dispatch/config-domain-handler.d.ts +1 -1
- package/dist/dispatch/config-domain-handler.d.ts.map +1 -1
- package/dist/dispatch/config-domain-handler.js +1 -1
- package/dist/dispatch/config-domain-handler.js.map +1 -1
- package/dist/dispatch/contracts/derive-output-contract.d.ts +1 -1
- package/dist/dispatch/contracts/derive-output-contract.d.ts.map +1 -1
- package/dist/dispatch/contracts/derive-output-contract.js +2 -1
- package/dist/dispatch/contracts/derive-output-contract.js.map +1 -1
- package/dist/dispatch/contracts/ensures-schema.d.ts +1 -1
- package/dist/dispatch/contracts/ensures-schema.d.ts.map +1 -1
- package/dist/dispatch/contracts/ensures-schema.js +1 -1
- package/dist/dispatch/contracts/ensures-schema.js.map +1 -1
- package/dist/dispatch/contracts/input-contracts.d.ts +1 -1
- package/dist/dispatch/contracts/input-contracts.d.ts.map +1 -1
- package/dist/dispatch/contracts/input-contracts.js +3 -1
- package/dist/dispatch/contracts/input-contracts.js.map +1 -1
- package/dist/dispatch/contracts/output-contracts.d.ts +2 -1
- package/dist/dispatch/contracts/output-contracts.d.ts.map +1 -1
- package/dist/dispatch/contracts/output-contracts.js +1 -1
- package/dist/dispatch/contracts/output-contracts.js.map +1 -1
- package/dist/dispatch/describe-operation.d.ts +1 -1
- package/dist/dispatch/describe-operation.d.ts.map +1 -1
- package/dist/dispatch/describe-operation.js +1 -1
- package/dist/dispatch/describe-operation.js.map +1 -1
- package/dist/dispatch/mvi-projection.d.ts +1 -1
- package/dist/dispatch/mvi-projection.d.ts.map +1 -1
- package/dist/dispatch/mvi-projection.js +1 -1
- package/dist/dispatch/mvi-projection.js.map +1 -1
- package/dist/docs/display-alias.js +1 -1
- package/dist/docs/display-alias.js.map +1 -1
- package/dist/docs/docs-update.d.ts +1 -1
- package/dist/docs/docs-update.d.ts.map +1 -1
- package/dist/docs/docs-update.js +2 -1
- package/dist/docs/docs-update.js.map +1 -1
- package/dist/docs/export-document.js +14 -58410
- package/dist/docs/export-document.js.map +3 -3
- package/dist/docs/publish-pr.js +1 -1
- package/dist/docs/publish-pr.js.map +1 -1
- package/dist/docs/supersede.js +1 -1
- package/dist/docs/supersede.js.map +1 -1
- package/dist/docs/validate-body.d.ts +1 -1
- package/dist/docs/validate-body.d.ts.map +1 -1
- package/dist/docs/validate-body.js +1 -1
- package/dist/docs/validate-body.js.map +1 -1
- package/dist/docs/writer-registry.js +1 -1
- package/dist/docs/writer-registry.js.map +1 -1
- package/dist/doctor/acceptance-drift.js +1 -1
- package/dist/doctor/acceptance-drift.js.map +1 -1
- package/dist/doctor/db-substrate.d.ts +7 -10
- package/dist/doctor/db-substrate.d.ts.map +1 -1
- package/dist/doctor/db-substrate.js +11 -15
- package/dist/doctor/db-substrate.js.map +1 -1
- package/dist/doctor/invariant-audit.js +1 -1
- package/dist/doctor/invariant-audit.js.map +1 -1
- package/dist/doctor/malformed-task-ids.js +1 -1
- package/dist/doctor/malformed-task-ids.js.map +1 -1
- package/dist/doctor/migrations.d.ts +18 -0
- package/dist/doctor/migrations.d.ts.map +1 -1
- package/dist/doctor/migrations.js +54 -2
- package/dist/doctor/migrations.js.map +1 -1
- package/dist/doctor/row-identity-refill.d.ts +43 -0
- package/dist/doctor/row-identity-refill.d.ts.map +1 -0
- package/dist/doctor/row-identity-refill.js +186 -0
- package/dist/doctor/row-identity-refill.js.map +1 -0
- package/dist/doctor/superseded-store.d.ts.map +1 -1
- package/dist/doctor/superseded-store.js +51 -2
- package/dist/doctor/superseded-store.js.map +1 -1
- package/dist/doctor/sync-repair.d.ts +39 -0
- package/dist/doctor/sync-repair.d.ts.map +1 -0
- package/dist/doctor/sync-repair.js +71 -0
- package/dist/doctor/sync-repair.js.map +1 -0
- package/dist/doctor/sync-triggers.d.ts +63 -4
- package/dist/doctor/sync-triggers.d.ts.map +1 -1
- package/dist/doctor/sync-triggers.js +352 -9
- package/dist/doctor/sync-triggers.js.map +1 -1
- package/dist/doctor/twin-collapse.js +1 -1
- package/dist/doctor/twin-collapse.js.map +1 -1
- package/dist/engine/engine-ops.d.ts.map +1 -1
- package/dist/engine/engine-ops.js +7 -1
- package/dist/engine/engine-ops.js.map +1 -1
- package/dist/engine-result.d.ts +1 -1
- package/dist/engine-result.d.ts.map +1 -1
- package/dist/engine-result.js +1 -1
- package/dist/engine-result.js.map +1 -1
- package/dist/error-catalog.js +1 -1
- package/dist/error-catalog.js.map +1 -1
- package/dist/error-registry.d.ts +1 -1
- package/dist/error-registry.d.ts.map +1 -1
- package/dist/error-registry.js +1 -1
- package/dist/error-registry.js.map +1 -1
- package/dist/errors-to-engine.d.ts +20 -0
- package/dist/errors-to-engine.d.ts.map +1 -1
- package/dist/errors-to-engine.js +37 -0
- package/dist/errors-to-engine.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/gc/janitor.d.ts +23 -4
- package/dist/gc/janitor.d.ts.map +1 -1
- package/dist/gc/janitor.js +71 -11
- package/dist/gc/janitor.js.map +1 -1
- package/dist/goal/continuation.d.ts +1 -1
- package/dist/goal/continuation.d.ts.map +1 -1
- package/dist/goal/continuation.js +1 -1
- package/dist/goal/continuation.js.map +1 -1
- package/dist/goal/judge.d.ts.map +1 -1
- package/dist/goal/judge.js +2 -1
- package/dist/goal/judge.js.map +1 -1
- package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
- package/dist/hooks/handlers/session-hooks.js +1 -1
- package/dist/hooks/handlers/session-hooks.js.map +1 -1
- package/dist/init/engine-ops.js +1 -1
- package/dist/init/engine-ops.js.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +22 -37
- package/dist/init.js.map +1 -1
- package/dist/inject/index.js +1 -1
- package/dist/inject/index.js.map +1 -1
- package/dist/injection.d.ts.map +1 -1
- package/dist/injection.js +13 -0
- package/dist/injection.js.map +1 -1
- package/dist/internal.d.ts +4 -3
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +7 -4
- package/dist/internal.js.map +1 -1
- package/dist/issue/create.js +1 -1
- package/dist/issue/create.js.map +1 -1
- package/dist/json-schema-validator.js +1 -1
- package/dist/json-schema-validator.js.map +1 -1
- package/dist/lifecycle/index.d.ts +2 -23
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +5 -112
- package/dist/lifecycle/index.js.map +1 -1
- package/dist/lifecycle/kind-requirements.d.ts +1 -1
- package/dist/lifecycle/kind-requirements.d.ts.map +1 -1
- package/dist/lifecycle/kind-requirements.js +1 -1
- package/dist/lifecycle/kind-requirements.js.map +1 -1
- package/dist/lifecycle/pipeline.js +1 -1
- package/dist/lifecycle/pipeline.js.map +1 -1
- package/dist/lifecycle/resume.js +2 -2
- package/dist/lifecycle/resume.js.map +1 -1
- package/dist/lifecycle/state-machine.js +1 -1
- package/dist/lifecycle/state-machine.js.map +1 -1
- package/dist/lifecycle/status.d.ts +46 -0
- package/dist/lifecycle/status.d.ts.map +1 -0
- package/dist/lifecycle/status.js +147 -0
- package/dist/lifecycle/status.js.map +1 -0
- package/dist/lifecycle/verification/satisfies-validator.js +1 -1
- package/dist/lifecycle/verification/satisfies-validator.js.map +1 -1
- package/dist/llm/catalog-seeder.d.ts +1 -1
- package/dist/llm/catalog-seeder.d.ts.map +1 -1
- package/dist/llm/catalog-seeder.js +1 -1
- package/dist/llm/catalog-seeder.js.map +1 -1
- package/dist/llm/cli-ops.d.ts +1 -2
- package/dist/llm/cli-ops.d.ts.map +1 -1
- package/dist/llm/cli-ops.js +2 -1
- package/dist/llm/cli-ops.js.map +1 -1
- package/dist/llm/conversation.d.ts.map +1 -1
- package/dist/llm/conversation.js +15 -4
- package/dist/llm/conversation.js.map +1 -1
- package/dist/llm/credential-seeders/claude-code-seeder.js +1 -1
- package/dist/llm/credential-seeders/claude-code-seeder.js.map +1 -1
- package/dist/llm/credential-seeders/cleo-pkce-seeder.js +1 -1
- package/dist/llm/credential-seeders/cleo-pkce-seeder.js.map +1 -1
- package/dist/llm/credential-writeback.js +1 -1
- package/dist/llm/credential-writeback.js.map +1 -1
- package/dist/llm/entity-ops.d.ts +1 -1
- package/dist/llm/entity-ops.d.ts.map +1 -1
- package/dist/llm/entity-ops.js +2 -1
- package/dist/llm/entity-ops.js.map +1 -1
- package/dist/llm/llm-queue-admit.d.ts +1 -1
- package/dist/llm/llm-queue-admit.d.ts.map +1 -1
- package/dist/llm/llm-queue-admit.js +2 -1
- package/dist/llm/llm-queue-admit.js.map +1 -1
- package/dist/llm/onboarding/login-engine.js +1 -1
- package/dist/llm/onboarding/login-engine.js.map +1 -1
- package/dist/llm/pi/pi-errors.d.ts +1 -1
- package/dist/llm/pi/pi-errors.d.ts.map +1 -1
- package/dist/llm/pi/pi-errors.js +1 -1
- package/dist/llm/pi/pi-errors.js.map +1 -1
- package/dist/llm/plugin-facade.d.ts.map +1 -1
- package/dist/llm/plugin-facade.js +14 -93651
- package/dist/llm/plugin-facade.js.map +3 -3
- package/dist/llm/system-key.d.ts +1 -1
- package/dist/llm/system-key.d.ts.map +1 -1
- package/dist/llm/system-key.js +1 -1
- package/dist/llm/system-key.js.map +1 -1
- package/dist/llm/system-of-use-registry.d.ts +1 -1
- package/dist/llm/system-of-use-registry.d.ts.map +1 -1
- package/dist/llm/system-of-use-registry.js +1 -1
- package/dist/llm/system-of-use-registry.js.map +1 -1
- package/dist/llm/system-resolver.js +1 -1
- package/dist/llm/system-resolver.js.map +1 -1
- package/dist/llm/transports/bedrock.d.ts +1 -0
- package/dist/llm/transports/bedrock.d.ts.map +1 -1
- package/dist/llm/transports/bedrock.js +30 -12
- package/dist/llm/transports/bedrock.js.map +1 -1
- package/dist/memory/brain-host.d.ts +71 -0
- package/dist/memory/brain-host.d.ts.map +1 -0
- package/dist/memory/brain-host.js +84 -0
- package/dist/memory/brain-host.js.map +1 -0
- package/dist/memory/brain-writer-handlers.d.ts.map +1 -1
- package/dist/memory/brain-writer-handlers.js +24 -7
- package/dist/memory/brain-writer-handlers.js.map +1 -1
- package/dist/memory/brain-writer-thread.d.ts +66 -6
- package/dist/memory/brain-writer-thread.d.ts.map +1 -1
- package/dist/memory/brain-writer-thread.js +67 -10
- package/dist/memory/brain-writer-thread.js.map +1 -1
- package/dist/memory/brain-writer-worker.js +7 -0
- package/dist/memory/brain-writer-worker.js.map +1 -1
- package/dist/memory/decision-contradiction.d.ts.map +1 -1
- package/dist/memory/decision-contradiction.js +10 -5
- package/dist/memory/decision-contradiction.js.map +1 -1
- package/dist/memory/decisions.js +2 -1
- package/dist/memory/decisions.js.map +1 -1
- package/dist/memory/dialectic-evaluator.d.ts.map +1 -1
- package/dist/memory/dialectic-evaluator.js +4 -1
- package/dist/memory/dialectic-evaluator.js.map +1 -1
- package/dist/memory/embedding-backfill.d.ts +70 -0
- package/dist/memory/embedding-backfill.d.ts.map +1 -0
- package/dist/memory/embedding-backfill.js +110 -0
- package/dist/memory/embedding-backfill.js.map +1 -0
- package/dist/memory/index.js +1 -1
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/manifest-identity.js +1 -1
- package/dist/memory/manifest-identity.js.map +1 -1
- package/dist/memory/retrieval/observe.d.ts +23 -0
- package/dist/memory/retrieval/observe.d.ts.map +1 -1
- package/dist/memory/retrieval/observe.js +82 -36
- package/dist/memory/retrieval/observe.js.map +1 -1
- package/dist/memory/transcript-extractor.d.ts.map +1 -1
- package/dist/memory/transcript-extractor.js +3 -2
- package/dist/memory/transcript-extractor.js.map +1 -1
- package/dist/metrics/model-provider-registry.d.ts.map +1 -1
- package/dist/metrics/model-provider-registry.js +6 -0
- package/dist/metrics/model-provider-registry.js.map +1 -1
- package/dist/metrics/token-service.d.ts +33 -0
- package/dist/metrics/token-service.d.ts.map +1 -1
- package/dist/metrics/token-service.js +35 -0
- package/dist/metrics/token-service.js.map +1 -1
- package/dist/migration/index.js +1 -1
- package/dist/migration/index.js.map +1 -1
- package/dist/migration/manual/T10165-backfill-adr-index.js +1 -1
- package/dist/migration/manual/T10165-backfill-adr-index.js.map +1 -1
- package/dist/nexus/api-contracts.d.ts.map +1 -1
- package/dist/nexus/api-contracts.js +27 -11
- package/dist/nexus/api-contracts.js.map +1 -1
- package/dist/nexus/api-extractors/http-extractor.d.ts +2 -2
- package/dist/nexus/api-extractors/http-extractor.d.ts.map +1 -1
- package/dist/nexus/api-extractors/http-extractor.js +3 -3
- package/dist/nexus/api-extractors/http-extractor.js.map +1 -1
- package/dist/nexus/assessment-projection.d.ts +1 -1
- package/dist/nexus/assessment-projection.d.ts.map +1 -1
- package/dist/nexus/assessment-projection.js +1 -1
- package/dist/nexus/assessment-projection.js.map +1 -1
- package/dist/nexus/clusters.js +1 -1
- package/dist/nexus/clusters.js.map +1 -1
- package/dist/nexus/deps.d.ts +1 -1
- package/dist/nexus/deps.d.ts.map +1 -1
- package/dist/nexus/deps.js +1 -1
- package/dist/nexus/deps.js.map +1 -1
- package/dist/nexus/diff.d.ts +1 -1
- package/dist/nexus/diff.d.ts.map +1 -1
- package/dist/nexus/diff.js +3 -2
- package/dist/nexus/diff.js.map +1 -1
- package/dist/nexus/fleet-status.d.ts +1 -1
- package/dist/nexus/fleet-status.d.ts.map +1 -1
- package/dist/nexus/fleet-status.js +1 -1
- package/dist/nexus/fleet-status.js.map +1 -1
- package/dist/nexus/flows.js +1 -1
- package/dist/nexus/flows.js.map +1 -1
- package/dist/nexus/identity.d.ts +8 -0
- package/dist/nexus/identity.d.ts.map +1 -1
- package/dist/nexus/identity.js +17 -1
- package/dist/nexus/identity.js.map +1 -1
- package/dist/nexus/index.d.ts +1 -1
- package/dist/nexus/index.d.ts.map +1 -1
- package/dist/nexus/index.js +2 -2
- package/dist/nexus/index.js.map +1 -1
- package/dist/nexus/nexus-bridge.d.ts.map +1 -1
- package/dist/nexus/nexus-bridge.js +5 -5
- package/dist/nexus/nexus-bridge.js.map +1 -1
- package/dist/nexus/permissions.d.ts +1 -1
- package/dist/nexus/permissions.d.ts.map +1 -1
- package/dist/nexus/permissions.js +1 -1
- package/dist/nexus/permissions.js.map +1 -1
- package/dist/nexus/query-dsl.js +1 -1
- package/dist/nexus/query-dsl.js.map +1 -1
- package/dist/nexus/query.d.ts +1 -2
- package/dist/nexus/query.d.ts.map +1 -1
- package/dist/nexus/query.js +1 -1
- package/dist/nexus/query.js.map +1 -1
- package/dist/nexus/registry-errors.js +1 -1
- package/dist/nexus/registry-errors.js.map +1 -1
- package/dist/nexus/registry.d.ts +23 -1
- package/dist/nexus/registry.d.ts.map +1 -1
- package/dist/nexus/registry.js +77 -2
- package/dist/nexus/registry.js.map +1 -1
- package/dist/nexus/workspace.js +1 -1
- package/dist/nexus/workspace.js.map +1 -1
- package/dist/orchestrate/pivot.d.ts.map +1 -1
- package/dist/orchestrate/pivot.js +2 -1
- package/dist/orchestrate/pivot.js.map +1 -1
- package/dist/orchestrate/query-ops.js +1 -1
- package/dist/orchestrate/query-ops.js.map +1 -1
- package/dist/orchestrate/spawn-ops.js +1 -1
- package/dist/orchestrate/spawn-ops.js.map +1 -1
- package/dist/orchestration/atomicity.d.ts +1 -1
- package/dist/orchestration/atomicity.d.ts.map +1 -1
- package/dist/orchestration/atomicity.js +1 -1
- package/dist/orchestration/atomicity.js.map +1 -1
- package/dist/orchestration/classify.js +1 -1
- package/dist/orchestration/classify.js.map +1 -1
- package/dist/orchestration/index.js +1 -1
- package/dist/orchestration/index.js.map +1 -1
- package/dist/orchestration/parallel.js +1 -1
- package/dist/orchestration/parallel.js.map +1 -1
- package/dist/orchestration/protocol-validators.d.ts +1 -1
- package/dist/orchestration/protocol-validators.d.ts.map +1 -1
- package/dist/orchestration/protocol-validators.js +1 -1
- package/dist/orchestration/protocol-validators.js.map +1 -1
- package/dist/orchestration/skill-ops.js +1 -1
- package/dist/orchestration/skill-ops.js.map +1 -1
- package/dist/orchestration/spawn-prompt.d.ts +9 -0
- package/dist/orchestration/spawn-prompt.d.ts.map +1 -1
- package/dist/orchestration/spawn-prompt.js +13 -2
- package/dist/orchestration/spawn-prompt.js.map +1 -1
- package/dist/orchestration/spawn.js +1 -1
- package/dist/orchestration/spawn.js.map +1 -1
- package/dist/paths.d.ts +3 -1
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +4 -2
- package/dist/paths.js.map +1 -1
- package/dist/phases/deps.js +1 -1
- package/dist/phases/deps.js.map +1 -1
- package/dist/phases/index.js +1 -1
- package/dist/phases/index.js.map +1 -1
- package/dist/project-lifecycle.js +3 -3
- package/dist/project-lifecycle.js.map +1 -1
- package/dist/project-scope.js +1 -1
- package/dist/project-scope.js.map +1 -1
- package/dist/project-tombstone.js +1 -1
- package/dist/project-tombstone.js.map +1 -1
- package/dist/registrations.d.ts +30 -0
- package/dist/registrations.d.ts.map +1 -0
- package/dist/registrations.js +30 -0
- package/dist/registrations.js.map +1 -0
- package/dist/release/changesets-aggregator.js +1 -1
- package/dist/release/changesets-aggregator.js.map +1 -1
- package/dist/release/ci-evidence.d.ts +68 -2
- package/dist/release/ci-evidence.d.ts.map +1 -1
- package/dist/release/ci-evidence.js +203 -39
- package/dist/release/ci-evidence.js.map +1 -1
- package/dist/release/invariants/archive-reason-invariant.d.ts +1 -1
- package/dist/release/invariants/archive-reason-invariant.d.ts.map +1 -1
- package/dist/release/invariants/archive-reason-invariant.js +1 -1
- package/dist/release/invariants/archive-reason-invariant.js.map +1 -1
- package/dist/release/invariants/registry.d.ts +1 -1
- package/dist/release/invariants/registry.d.ts.map +1 -1
- package/dist/release/invariants/registry.js +1 -1
- package/dist/release/invariants/registry.js.map +1 -1
- package/dist/release/open.d.ts +4 -7
- package/dist/release/open.d.ts.map +1 -1
- package/dist/release/open.js +62 -24
- package/dist/release/open.js.map +1 -1
- package/dist/release/plan.d.ts +1 -2
- package/dist/release/plan.d.ts.map +1 -1
- package/dist/release/plan.js +4 -1
- package/dist/release/plan.js.map +1 -1
- package/dist/release/pr-evidence.d.ts +1 -1
- package/dist/release/pr-evidence.d.ts.map +1 -1
- package/dist/release/pr-evidence.js +2 -2
- package/dist/release/pr-evidence.js.map +1 -1
- package/dist/release/preflight-skip.d.ts +74 -10
- package/dist/release/preflight-skip.d.ts.map +1 -1
- package/dist/release/preflight-skip.js +254 -47
- package/dist/release/preflight-skip.js.map +1 -1
- package/dist/release/reconcile.d.ts +1 -1
- package/dist/release/reconcile.d.ts.map +1 -1
- package/dist/release/reconcile.js +19 -3
- package/dist/release/reconcile.js.map +1 -1
- package/dist/release/version-bump.d.ts +1 -1
- package/dist/release/version-bump.d.ts.map +1 -1
- package/dist/release/version-bump.js +1 -1
- package/dist/release/version-bump.js.map +1 -1
- package/dist/render/colors.js +1 -19
- package/dist/render/colors.js.map +1 -1
- package/dist/render/tasks/colors.js +1 -1
- package/dist/render/tasks/colors.js.map +1 -1
- package/dist/resources/admission-ledger.d.ts +587 -0
- package/dist/resources/admission-ledger.d.ts.map +1 -0
- package/dist/resources/admission-ledger.js +1401 -0
- package/dist/resources/admission-ledger.js.map +1 -0
- package/dist/resources/backend.d.ts +37 -1
- package/dist/resources/backend.d.ts.map +1 -1
- package/dist/resources/backend.js +1 -1
- package/dist/resources/darwin-backend.d.ts +152 -26
- package/dist/resources/darwin-backend.d.ts.map +1 -1
- package/dist/resources/darwin-backend.js +217 -34
- package/dist/resources/darwin-backend.js.map +1 -1
- package/dist/resources/governor.d.ts +36 -27
- package/dist/resources/governor.d.ts.map +1 -1
- package/dist/resources/governor.js +144 -49
- package/dist/resources/governor.js.map +1 -1
- package/dist/resources/heavy-command-hook-delivery.d.ts +144 -0
- package/dist/resources/heavy-command-hook-delivery.d.ts.map +1 -0
- package/dist/resources/heavy-command-hook-delivery.js +204 -0
- package/dist/resources/heavy-command-hook-delivery.js.map +1 -0
- package/dist/resources/heavy-command.d.ts +69 -0
- package/dist/resources/heavy-command.d.ts.map +1 -1
- package/dist/resources/heavy-command.js +479 -7
- package/dist/resources/heavy-command.js.map +1 -1
- package/dist/resources/pressure-gate.d.ts +148 -0
- package/dist/resources/pressure-gate.d.ts.map +1 -0
- package/dist/resources/pressure-gate.js +273 -0
- package/dist/resources/pressure-gate.js.map +1 -0
- package/dist/resources/run-admission.d.ts +33 -45
- package/dist/resources/run-admission.d.ts.map +1 -1
- package/dist/resources/run-admission.js +50 -118
- package/dist/resources/run-admission.js.map +1 -1
- package/dist/resources/run-class.d.ts +60 -4
- package/dist/resources/run-class.d.ts.map +1 -1
- package/dist/resources/run-class.js +474 -6
- package/dist/resources/run-class.js.map +1 -1
- package/dist/resources/run-governed.d.ts +43 -11
- package/dist/resources/run-governed.d.ts.map +1 -1
- package/dist/resources/run-governed.js +139 -99
- package/dist/resources/run-governed.js.map +1 -1
- package/dist/resources/slot-holder.d.ts +12 -4
- package/dist/resources/slot-holder.d.ts.map +1 -1
- package/dist/resources/slot-holder.js +19 -14
- package/dist/resources/slot-holder.js.map +1 -1
- package/dist/resources/spawn-wrapper.js +1 -1
- package/dist/resources/spawn-wrapper.js.map +1 -1
- package/dist/resources/supervisor-admit.d.ts +1 -1
- package/dist/resources/supervisor-admit.d.ts.map +1 -1
- package/dist/resources/supervisor-admit.js +2 -1
- package/dist/resources/supervisor-admit.js.map +1 -1
- package/dist/routing/build-command-groups.d.ts +1 -1
- package/dist/routing/build-command-groups.d.ts.map +1 -1
- package/dist/routing/build-command-groups.js +4 -1
- package/dist/routing/build-command-groups.js.map +1 -1
- package/dist/runtime/openapi/generate-openapi.d.ts +1 -1
- package/dist/runtime/openapi/generate-openapi.d.ts.map +1 -1
- package/dist/runtime/openapi/generate-openapi.js +1 -1
- package/dist/runtime/openapi/generate-openapi.js.map +1 -1
- package/dist/sagas/index.d.ts +3 -1
- package/dist/sagas/index.d.ts.map +1 -1
- package/dist/sagas/index.js +2 -5
- package/dist/sagas/index.js.map +1 -1
- package/dist/scaffold/ensure-skills.d.ts.map +1 -1
- package/dist/scaffold/ensure-skills.js +58 -2
- package/dist/scaffold/ensure-skills.js.map +1 -1
- package/dist/scaffold/project-identity.js +1 -1
- package/dist/scaffold/project-identity.js.map +1 -1
- package/dist/sdk/validator-attest.d.ts +1 -1
- package/dist/sdk/validator-attest.d.ts.map +1 -1
- package/dist/sdk/validator-attest.js +1 -1
- package/dist/sdk/validator-attest.js.map +1 -1
- package/dist/sdk/validator-reject.d.ts +1 -1
- package/dist/sdk/validator-reject.d.ts.map +1 -1
- package/dist/sdk/validator-reject.js +1 -1
- package/dist/sdk/validator-reject.js.map +1 -1
- package/dist/security/input-sanitization.js +2 -1
- package/dist/security/input-sanitization.js.map +1 -1
- package/dist/security/override-cap.js +1 -1
- package/dist/security/override-cap.js.map +1 -1
- package/dist/security/owner-override-auth.js +1 -1
- package/dist/security/owner-override-auth.js.map +1 -1
- package/dist/security/shared-evidence-tracker.js +1 -1
- package/dist/security/shared-evidence-tracker.js.map +1 -1
- package/dist/selfimprove/fix-gen.js +35 -93136
- package/dist/selfimprove/fix-gen.js.map +3 -3
- package/dist/sentient/daemon.d.ts.map +1 -1
- package/dist/sentient/daemon.js +3 -0
- package/dist/sentient/daemon.js.map +1 -1
- package/dist/sentient/tick.d.ts +7 -0
- package/dist/sentient/tick.d.ts.map +1 -1
- package/dist/sentient/tick.js +11 -0
- package/dist/sentient/tick.js.map +1 -1
- package/dist/sequence/index.js +1 -1
- package/dist/sequence/index.js.map +1 -1
- package/dist/session/engine-ops.d.ts +3 -45
- package/dist/session/engine-ops.d.ts.map +1 -1
- package/dist/session/engine-ops.js +63 -112
- package/dist/session/engine-ops.js.map +1 -1
- package/dist/session/status-op.d.ts +40 -0
- package/dist/session/status-op.d.ts.map +1 -0
- package/dist/session/status-op.js +71 -0
- package/dist/session/status-op.js.map +1 -0
- package/dist/session/task-current.d.ts +31 -0
- package/dist/session/task-current.d.ts.map +1 -0
- package/dist/session/task-current.js +59 -0
- package/dist/session/task-current.js.map +1 -0
- package/dist/sessions/assumptions.d.ts +1 -1
- package/dist/sessions/assumptions.d.ts.map +1 -1
- package/dist/sessions/assumptions.js +1 -1
- package/dist/sessions/assumptions.js.map +1 -1
- package/dist/sessions/briefing.d.ts.map +1 -1
- package/dist/sessions/briefing.js +11 -0
- package/dist/sessions/briefing.js.map +1 -1
- package/dist/sessions/context-inject.js +1 -1
- package/dist/sessions/context-inject.js.map +1 -1
- package/dist/sessions/decisions.d.ts +1 -1
- package/dist/sessions/decisions.d.ts.map +1 -1
- package/dist/sessions/decisions.js +1 -1
- package/dist/sessions/decisions.js.map +1 -1
- package/dist/sessions/focus-state-store.d.ts.map +1 -1
- package/dist/sessions/focus-state-store.js +2 -1
- package/dist/sessions/focus-state-store.js.map +1 -1
- package/dist/sessions/handoff.js +1 -1
- package/dist/sessions/handoff.js.map +1 -1
- package/dist/sessions/index.d.ts +1 -2
- package/dist/sessions/index.d.ts.map +1 -1
- package/dist/sessions/index.js +3 -2
- package/dist/sessions/index.js.map +1 -1
- package/dist/sessions/session-drift.js +1 -1
- package/dist/sessions/session-drift.js.map +1 -1
- package/dist/sessions/session-end-snapshot-entry.d.ts +4 -0
- package/dist/sessions/session-end-snapshot-entry.d.ts.map +1 -1
- package/dist/sessions/session-end-snapshot-entry.js +11 -0
- package/dist/sessions/session-end-snapshot-entry.js.map +1 -1
- package/dist/sessions/session-end-snapshot.d.ts.map +1 -1
- package/dist/sessions/session-end-snapshot.js +4 -0
- package/dist/sessions/session-end-snapshot.js.map +1 -1
- package/dist/sessions/session-enforcement.js +1 -1
- package/dist/sessions/session-enforcement.js.map +1 -1
- package/dist/sessions/session-show.d.ts +1 -2
- package/dist/sessions/session-show.d.ts.map +1 -1
- package/dist/sessions/session-show.js +1 -1
- package/dist/sessions/session-show.js.map +1 -1
- package/dist/sessions/session-stats.js +1 -1
- package/dist/sessions/session-stats.js.map +1 -1
- package/dist/sessions/session-suspend.d.ts +1 -2
- package/dist/sessions/session-suspend.d.ts.map +1 -1
- package/dist/sessions/session-suspend.js +1 -1
- package/dist/sessions/session-suspend.js.map +1 -1
- package/dist/sessions/session-switch.js +1 -1
- package/dist/sessions/session-switch.js.map +1 -1
- package/dist/sessions/snapshot.js +1 -1
- package/dist/sessions/snapshot.js.map +1 -1
- package/dist/sessions/terminal-identity.d.ts +1 -1
- package/dist/sessions/terminal-identity.d.ts.map +1 -1
- package/dist/sessions/terminal-identity.js +1 -1
- package/dist/sessions/terminal-identity.js.map +1 -1
- package/dist/setup/sections/integrations.d.ts.map +1 -1
- package/dist/setup/sections/integrations.js +9 -0
- package/dist/setup/sections/integrations.js.map +1 -1
- package/dist/setup/sections/models-roles.js +1 -1
- package/dist/setup/sections/models-roles.js.map +1 -1
- package/dist/setup/sections/verification.d.ts.map +1 -1
- package/dist/setup/sections/verification.js +8 -2
- package/dist/setup/sections/verification.js.map +1 -1
- package/dist/shutdown.d.ts +24 -0
- package/dist/shutdown.d.ts.map +1 -1
- package/dist/shutdown.js +30 -1
- package/dist/shutdown.js.map +1 -1
- package/dist/skills/agents/install.d.ts +46 -6
- package/dist/skills/agents/install.d.ts.map +1 -1
- package/dist/skills/agents/install.js +86 -17
- package/dist/skills/agents/install.js.map +1 -1
- package/dist/skills/injection/subagent.js +1 -1
- package/dist/skills/injection/subagent.js.map +1 -1
- package/dist/skills/manifests/research.d.ts.map +1 -1
- package/dist/skills/manifests/research.js +2 -1
- package/dist/skills/manifests/research.js.map +1 -1
- package/dist/skills/orchestrator/spawn.js +1 -1
- package/dist/skills/orchestrator/spawn.js.map +1 -1
- package/dist/skills/orchestrator/validator.js +1 -1
- package/dist/skills/orchestrator/validator.js.map +1 -1
- package/dist/snapshot/index.d.ts.map +1 -1
- package/dist/snapshot/index.js +13 -0
- package/dist/snapshot/index.js.map +1 -1
- package/dist/spawn/agent-identity.js +1 -1
- package/dist/spawn/agent-identity.js.map +1 -1
- package/dist/stats/index.d.ts.map +1 -1
- package/dist/stats/index.js +2 -1
- package/dist/stats/index.js.map +1 -1
- package/dist/store/agent-install.d.ts.map +1 -1
- package/dist/store/agent-install.js +2 -1
- package/dist/store/agent-install.js.map +1 -1
- package/dist/store/agent-registry-accessor.d.ts +1 -0
- package/dist/store/agent-registry-accessor.d.ts.map +1 -1
- package/dist/store/agent-registry-accessor.js +3 -1
- package/dist/store/agent-registry-accessor.js.map +1 -1
- package/dist/store/atomic.js +1 -1
- package/dist/store/atomic.js.map +1 -1
- package/dist/store/attachment-store.js +1 -1
- package/dist/store/attachment-store.js.map +1 -1
- package/dist/store/audit-log-schema.d.ts +645 -0
- package/dist/store/audit-log-schema.d.ts.map +1 -0
- package/dist/store/audit-log-schema.js +30 -0
- package/dist/store/audit-log-schema.js.map +1 -0
- package/dist/store/background-ops.d.ts +26 -0
- package/dist/store/background-ops.d.ts.map +1 -1
- package/dist/store/background-ops.js +46 -0
- package/dist/store/background-ops.js.map +1 -1
- package/dist/store/backup-recover.d.ts +36 -1
- package/dist/store/backup-recover.d.ts.map +1 -1
- package/dist/store/backup-recover.js +99 -1
- package/dist/store/backup-recover.js.map +1 -1
- package/dist/store/backup-verify.d.ts +1 -1
- package/dist/store/backup-verify.d.ts.map +1 -1
- package/dist/store/backup-verify.js +6 -1
- package/dist/store/backup-verify.js.map +1 -1
- package/dist/store/backup.js +1 -1
- package/dist/store/backup.js.map +1 -1
- package/dist/store/conduit-sqlite.d.ts.map +1 -1
- package/dist/store/conduit-sqlite.js +2 -26
- package/dist/store/conduit-sqlite.js.map +1 -1
- package/dist/store/converters.js +1 -1
- package/dist/store/converters.js.map +1 -1
- package/dist/store/db-helpers.js +1 -1
- package/dist/store/db-helpers.js.map +1 -1
- package/dist/store/dependency-cycles.js +1 -1
- package/dist/store/dependency-cycles.js.map +1 -1
- package/dist/store/display-id-alias.d.ts +2 -2
- package/dist/store/display-id-alias.d.ts.map +1 -1
- package/dist/store/display-id-alias.js +5 -28
- package/dist/store/display-id-alias.js.map +1 -1
- package/dist/store/drizzle-node-sqlite.d.ts +56 -0
- package/dist/store/drizzle-node-sqlite.d.ts.map +1 -0
- package/dist/store/drizzle-node-sqlite.js +87 -0
- package/dist/store/drizzle-node-sqlite.js.map +1 -0
- package/dist/store/dual-scope-db.d.ts +26 -33
- package/dist/store/dual-scope-db.d.ts.map +1 -1
- package/dist/store/dual-scope-db.js +416 -122
- package/dist/store/dual-scope-db.js.map +1 -1
- package/dist/store/exodus/abort-events.d.ts +134 -0
- package/dist/store/exodus/abort-events.d.ts.map +1 -1
- package/dist/store/exodus/abort-events.js +201 -0
- package/dist/store/exodus/abort-events.js.map +1 -1
- package/dist/store/exodus/migrate.d.ts +7 -0
- package/dist/store/exodus/migrate.d.ts.map +1 -1
- package/dist/store/exodus/migrate.js +156 -118
- package/dist/store/exodus/migrate.js.map +1 -1
- package/dist/store/exodus/on-open.d.ts +36 -0
- package/dist/store/exodus/on-open.d.ts.map +1 -1
- package/dist/store/exodus/on-open.js +102 -27
- package/dist/store/exodus/on-open.js.map +1 -1
- package/dist/store/exodus/prior-recoveries.d.ts +75 -0
- package/dist/store/exodus/prior-recoveries.d.ts.map +1 -0
- package/dist/store/exodus/prior-recoveries.js +122 -0
- package/dist/store/exodus/prior-recoveries.js.map +1 -0
- package/dist/store/exodus/reconcile.d.ts.map +1 -1
- package/dist/store/exodus/reconcile.js +188 -24
- package/dist/store/exodus/reconcile.js.map +1 -1
- package/dist/store/exodus/runtime-targets.d.ts +3 -3
- package/dist/store/exodus/runtime-targets.js +3 -3
- package/dist/store/exodus/task-id-collision-sql.d.ts +52 -0
- package/dist/store/exodus/task-id-collision-sql.d.ts.map +1 -0
- package/dist/store/exodus/task-id-collision-sql.js +97 -0
- package/dist/store/exodus/task-id-collision-sql.js.map +1 -0
- package/dist/store/exodus/task-id-remap.d.ts +113 -0
- package/dist/store/exodus/task-id-remap.d.ts.map +1 -0
- package/dist/store/exodus/task-id-remap.js +301 -0
- package/dist/store/exodus/task-id-remap.js.map +1 -0
- package/dist/store/exodus/verify-migration.js +1 -1
- package/dist/store/exodus/verify-migration.js.map +1 -1
- package/dist/store/exodus/write-guard.d.ts +136 -0
- package/dist/store/exodus/write-guard.d.ts.map +1 -0
- package/dist/store/exodus/write-guard.js +289 -0
- package/dist/store/exodus/write-guard.js.map +1 -0
- package/dist/store/identity-share.d.ts +31 -0
- package/dist/store/identity-share.d.ts.map +1 -0
- package/dist/store/identity-share.js +54 -0
- package/dist/store/identity-share.js.map +1 -0
- package/dist/store/import-remap.js +1 -1
- package/dist/store/import-remap.js.map +1 -1
- package/dist/store/import-sort.js +1 -1
- package/dist/store/import-sort.js.map +1 -1
- package/dist/store/json.js +1 -1
- package/dist/store/json.js.map +1 -1
- package/dist/store/legacy-tasks-lineage.js +3 -3
- package/dist/store/legacy-tasks-lineage.js.map +1 -1
- package/dist/store/lock.d.ts +17 -0
- package/dist/store/lock.d.ts.map +1 -1
- package/dist/store/lock.js +18 -1
- package/dist/store/lock.js.map +1 -1
- package/dist/store/memory-sqlite.d.ts.map +1 -1
- package/dist/store/memory-sqlite.js +2 -25
- package/dist/store/memory-sqlite.js.map +1 -1
- package/dist/store/migration-files.d.ts +30 -0
- package/dist/store/migration-files.d.ts.map +1 -0
- package/dist/store/migration-files.js +59 -0
- package/dist/store/migration-files.js.map +1 -0
- package/dist/store/migration-manager.d.ts.map +1 -1
- package/dist/store/migration-manager.js +14 -14
- package/dist/store/migration-manager.js.map +1 -1
- package/dist/store/migration-runner.d.ts.map +1 -1
- package/dist/store/migration-runner.js +2 -2
- package/dist/store/migration-runner.js.map +1 -1
- package/dist/store/migration-sqlite.d.ts +1 -1
- package/dist/store/migration-sqlite.d.ts.map +1 -1
- package/dist/store/migration-sqlite.js +290 -355
- package/dist/store/migration-sqlite.js.map +1 -1
- package/dist/store/open-cleo-db.d.ts.map +1 -1
- package/dist/store/open-cleo-db.js +4 -2
- package/dist/store/open-cleo-db.js.map +1 -1
- package/dist/store/pid-alive.d.ts +15 -0
- package/dist/store/pid-alive.d.ts.map +1 -0
- package/dist/store/pid-alive.js +24 -0
- package/dist/store/pid-alive.js.map +1 -0
- package/dist/store/portable-bundle-scan.d.ts +1 -1
- package/dist/store/portable-bundle-scan.d.ts.map +1 -1
- package/dist/store/portable-bundle-scan.js +1 -1
- package/dist/store/portable-bundle-scan.js.map +1 -1
- package/dist/store/portable-bundle.d.ts +10 -1
- package/dist/store/portable-bundle.d.ts.map +1 -1
- package/dist/store/portable-bundle.js +10 -1
- package/dist/store/portable-bundle.js.map +1 -1
- package/dist/store/recover-malformed-db.d.ts +1 -1
- package/dist/store/recover-malformed-db.d.ts.map +1 -1
- package/dist/store/recover-malformed-db.js +1 -1
- package/dist/store/recover-malformed-db.js.map +1 -1
- package/dist/store/repair-malformed-dbs.d.ts +1 -1
- package/dist/store/repair-malformed-dbs.d.ts.map +1 -1
- package/dist/store/repair-malformed-dbs.js +1 -1
- package/dist/store/repair-malformed-dbs.js.map +1 -1
- package/dist/store/restore-marker.d.ts +89 -0
- package/dist/store/restore-marker.d.ts.map +1 -0
- package/dist/store/restore-marker.js +197 -0
- package/dist/store/restore-marker.js.map +1 -0
- package/dist/store/restore-store.d.ts +85 -0
- package/dist/store/restore-store.d.ts.map +1 -0
- package/dist/store/restore-store.js +391 -0
- package/dist/store/restore-store.js.map +1 -0
- package/dist/store/row-identity.d.ts +123 -1
- package/dist/store/row-identity.d.ts.map +1 -1
- package/dist/store/row-identity.js +454 -80
- package/dist/store/row-identity.js.map +1 -1
- package/dist/store/schema/agent-registry-schema.d.ts +1 -1
- package/dist/store/schema/agent-registry-schema.d.ts.map +1 -1
- package/dist/store/schema/agent-registry-schema.js +5 -1
- package/dist/store/schema/agent-registry-schema.js.map +1 -1
- package/dist/store/schema/cleo-global/agent-registry.d.ts +1 -1
- package/dist/store/schema/cleo-global/agent-registry.d.ts.map +1 -1
- package/dist/store/schema/cleo-global/agent-registry.js +9 -5
- package/dist/store/schema/cleo-global/agent-registry.js.map +1 -1
- package/dist/store/schema/cleo-global/models-catalog.js +1 -1
- package/dist/store/schema/cleo-global/models-catalog.js.map +1 -1
- package/dist/store/schema/cleo-global/nexus.js +1 -1
- package/dist/store/schema/cleo-global/nexus.js.map +1 -1
- package/dist/store/schema/cleo-project/provenance-commits.d.ts +1 -1
- package/dist/store/schema/cleo-project/provenance-orphans.d.ts +1 -1
- package/dist/store/schema/cleo-project/provenance-orphans.d.ts.map +1 -1
- package/dist/store/schema/cleo-project/provenance-orphans.js +9 -2
- package/dist/store/schema/cleo-project/provenance-orphans.js.map +1 -1
- package/dist/store/schema/cleo-project/provenance-rest.d.ts +1 -1
- package/dist/store/schema/cleo-project/provenance-rest.js +1 -1
- package/dist/store/schema/cleo-project/provenance-rest.js.map +1 -1
- package/dist/store/schema/cleo-shared/brain.js +1 -1
- package/dist/store/schema/cleo-shared/brain.js.map +1 -1
- package/dist/store/schema/memory-schema.d.ts +1 -1
- package/dist/store/schema/memory-schema.d.ts.map +1 -1
- package/dist/store/schema/memory-schema.js +1 -1
- package/dist/store/schema/memory-schema.js.map +1 -1
- package/dist/store/schema/nexus-schema.js +1 -1
- package/dist/store/schema/nexus-schema.js.map +1 -1
- package/dist/store/service-host-matcher.d.ts +1 -1
- package/dist/store/service-host-matcher.d.ts.map +1 -1
- package/dist/store/service-host-matcher.js +1 -1
- package/dist/store/service-host-matcher.js.map +1 -1
- package/dist/store/service-oauth.js +1 -1
- package/dist/store/service-oauth.js.map +1 -1
- package/dist/store/session-store.d.ts.map +1 -1
- package/dist/store/session-store.js +7 -1
- package/dist/store/session-store.js.map +1 -1
- package/dist/store/sqlite-backup.d.ts +1 -1
- package/dist/store/sqlite-backup.d.ts.map +1 -1
- package/dist/store/sqlite-backup.js +1 -1
- package/dist/store/sqlite-backup.js.map +1 -1
- package/dist/store/sqlite-data-accessor.d.ts +52 -1
- package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
- package/dist/store/sqlite-data-accessor.js +95 -22
- package/dist/store/sqlite-data-accessor.js.map +1 -1
- package/dist/store/sqlite-native.d.ts.map +1 -1
- package/dist/store/sqlite-native.js +9 -9
- package/dist/store/sqlite-native.js.map +1 -1
- package/dist/store/sqlite-pragmas.d.ts +12 -5
- package/dist/store/sqlite-pragmas.d.ts.map +1 -1
- package/dist/store/sqlite-pragmas.js +13 -2
- package/dist/store/sqlite-pragmas.js.map +1 -1
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +7 -32
- package/dist/store/sqlite.js.map +1 -1
- package/dist/store/status-registry.d.ts +1 -1
- package/dist/store/status-registry.d.ts.map +1 -1
- package/dist/store/status-registry.js +1 -1
- package/dist/store/status-registry.js.map +1 -1
- package/dist/store/sync/apply/applier.d.ts +119 -0
- package/dist/store/sync/apply/applier.d.ts.map +1 -0
- package/dist/store/sync/apply/applier.js +1239 -0
- package/dist/store/sync/apply/applier.js.map +1 -0
- package/dist/store/sync/apply/fk.d.ts +47 -0
- package/dist/store/sync/apply/fk.d.ts.map +1 -0
- package/dist/store/sync/apply/fk.js +89 -0
- package/dist/store/sync/apply/fk.js.map +1 -0
- package/dist/store/sync/apply/footprints.d.ts +60 -0
- package/dist/store/sync/apply/footprints.d.ts.map +1 -0
- package/dist/store/sync/apply/footprints.js +235 -0
- package/dist/store/sync/apply/footprints.js.map +1 -0
- package/dist/store/sync/apply/frame.d.ts +80 -0
- package/dist/store/sync/apply/frame.d.ts.map +1 -0
- package/dist/store/sync/apply/frame.js +127 -0
- package/dist/store/sync/apply/frame.js.map +1 -0
- package/dist/store/sync/apply/index.d.ts +11 -0
- package/dist/store/sync/apply/index.d.ts.map +1 -0
- package/dist/store/sync/apply/index.js +11 -0
- package/dist/store/sync/apply/index.js.map +1 -0
- package/dist/store/sync/apply/parent-delete.d.ts +38 -0
- package/dist/store/sync/apply/parent-delete.d.ts.map +1 -0
- package/dist/store/sync/apply/parent-delete.js +41 -0
- package/dist/store/sync/apply/parent-delete.js.map +1 -0
- package/dist/store/sync/apply/post-apply.d.ts +67 -0
- package/dist/store/sync/apply/post-apply.d.ts.map +1 -0
- package/dist/store/sync/apply/post-apply.js +118 -0
- package/dist/store/sync/apply/post-apply.js.map +1 -0
- package/dist/store/sync/apply/refs.d.ts +60 -0
- package/dist/store/sync/apply/refs.d.ts.map +1 -0
- package/dist/store/sync/apply/refs.js +90 -0
- package/dist/store/sync/apply/refs.js.map +1 -0
- package/dist/store/sync/apply/write-api.d.ts +195 -0
- package/dist/store/sync/apply/write-api.d.ts.map +1 -0
- package/dist/store/sync/apply/write-api.js +416 -0
- package/dist/store/sync/apply/write-api.js.map +1 -0
- package/dist/store/sync/apply-intent.d.ts +117 -0
- package/dist/store/sync/apply-intent.d.ts.map +1 -0
- package/dist/store/sync/apply-intent.js +225 -0
- package/dist/store/sync/apply-intent.js.map +1 -0
- package/dist/store/sync/capture.d.ts +32 -2
- package/dist/store/sync/capture.d.ts.map +1 -1
- package/dist/store/sync/capture.js +47 -13
- package/dist/store/sync/capture.js.map +1 -1
- package/dist/store/sync/conflicts.d.ts +81 -0
- package/dist/store/sync/conflicts.d.ts.map +1 -0
- package/dist/store/sync/conflicts.js +98 -0
- package/dist/store/sync/conflicts.js.map +1 -0
- package/dist/store/sync/field-leave.d.ts +146 -0
- package/dist/store/sync/field-leave.d.ts.map +1 -0
- package/dist/store/sync/field-leave.js +241 -0
- package/dist/store/sync/field-leave.js.map +1 -0
- package/dist/store/sync/flags.d.ts +119 -0
- package/dist/store/sync/flags.d.ts.map +1 -1
- package/dist/store/sync/flags.js +360 -0
- package/dist/store/sync/flags.js.map +1 -1
- package/dist/store/sync/held.d.ts +197 -0
- package/dist/store/sync/held.d.ts.map +1 -0
- package/dist/store/sync/held.js +314 -0
- package/dist/store/sync/held.js.map +1 -0
- package/dist/store/sync/identity-fill.d.ts.map +1 -1
- package/dist/store/sync/identity-fill.js +10 -2
- package/dist/store/sync/identity-fill.js.map +1 -1
- package/dist/store/sync/inbox.d.ts +114 -0
- package/dist/store/sync/inbox.d.ts.map +1 -0
- package/dist/store/sync/inbox.js +222 -0
- package/dist/store/sync/inbox.js.map +1 -0
- package/dist/store/sync/inherit.d.ts +36 -0
- package/dist/store/sync/inherit.d.ts.map +1 -0
- package/dist/store/sync/inherit.js +38 -0
- package/dist/store/sync/inherit.js.map +1 -0
- package/dist/store/sync/merge/engine.d.ts +114 -0
- package/dist/store/sync/merge/engine.d.ts.map +1 -0
- package/dist/store/sync/merge/engine.js +698 -0
- package/dist/store/sync/merge/engine.js.map +1 -0
- package/dist/store/sync/merge/index.d.ts +11 -0
- package/dist/store/sync/merge/index.d.ts.map +1 -0
- package/dist/store/sync/merge/index.js +11 -0
- package/dist/store/sync/merge/index.js.map +1 -0
- package/dist/store/sync/merge/rules.d.ts +91 -0
- package/dist/store/sync/merge/rules.d.ts.map +1 -0
- package/dist/store/sync/merge/rules.js +151 -0
- package/dist/store/sync/merge/rules.js.map +1 -0
- package/dist/store/sync/merge/types.d.ts +206 -0
- package/dist/store/sync/merge/types.d.ts.map +1 -0
- package/dist/store/sync/merge/types.js +15 -0
- package/dist/store/sync/merge/types.js.map +1 -0
- package/dist/store/sync/migration-hooks.d.ts +47 -0
- package/dist/store/sync/migration-hooks.d.ts.map +1 -0
- package/dist/store/sync/migration-hooks.js +74 -0
- package/dist/store/sync/migration-hooks.js.map +1 -0
- package/dist/store/sync/netting.d.ts +5 -0
- package/dist/store/sync/netting.d.ts.map +1 -1
- package/dist/store/sync/netting.js +29 -2
- package/dist/store/sync/netting.js.map +1 -1
- package/dist/store/sync/remap.d.ts +72 -0
- package/dist/store/sync/remap.d.ts.map +1 -0
- package/dist/store/sync/remap.js +209 -0
- package/dist/store/sync/remap.js.map +1 -0
- package/dist/store/sync/repair.d.ts +180 -0
- package/dist/store/sync/repair.d.ts.map +1 -0
- package/dist/store/sync/repair.js +499 -0
- package/dist/store/sync/repair.js.map +1 -0
- package/dist/store/sync/replica-registry.d.ts +54 -0
- package/dist/store/sync/replica-registry.d.ts.map +1 -1
- package/dist/store/sync/replica-registry.js +72 -2
- package/dist/store/sync/replica-registry.js.map +1 -1
- package/dist/store/sync/replica.d.ts +71 -9
- package/dist/store/sync/replica.d.ts.map +1 -1
- package/dist/store/sync/replica.js +95 -11
- package/dist/store/sync/replica.js.map +1 -1
- package/dist/store/sync/row-meta.d.ts +148 -0
- package/dist/store/sync/row-meta.d.ts.map +1 -0
- package/dist/store/sync/row-meta.js +200 -0
- package/dist/store/sync/row-meta.js.map +1 -0
- package/dist/store/sync/schema.d.ts +13 -5
- package/dist/store/sync/schema.d.ts.map +1 -1
- package/dist/store/sync/schema.js +34 -7
- package/dist/store/sync/schema.js.map +1 -1
- package/dist/store/sync/sealer-values.d.ts.map +1 -1
- package/dist/store/sync/sealer-values.js +1 -0
- package/dist/store/sync/sealer-values.js.map +1 -1
- package/dist/store/sync/sealer.d.ts +96 -0
- package/dist/store/sync/sealer.d.ts.map +1 -1
- package/dist/store/sync/sealer.js +532 -58
- package/dist/store/sync/sealer.js.map +1 -1
- package/dist/store/sync/sequencing.d.ts +175 -0
- package/dist/store/sync/sequencing.d.ts.map +1 -0
- package/dist/store/sync/sequencing.js +295 -0
- package/dist/store/sync/sequencing.js.map +1 -0
- package/dist/store/sync/structural.d.ts +30 -2
- package/dist/store/sync/structural.d.ts.map +1 -1
- package/dist/store/sync/structural.js +64 -2
- package/dist/store/sync/structural.js.map +1 -1
- package/dist/store/sync/timestamp-canon.d.ts +44 -0
- package/dist/store/sync/timestamp-canon.d.ts.map +1 -0
- package/dist/store/sync/timestamp-canon.js +102 -0
- package/dist/store/sync/timestamp-canon.js.map +1 -0
- package/dist/store/sync/timestamps.d.ts +1 -30
- package/dist/store/sync/timestamps.d.ts.map +1 -1
- package/dist/store/sync/timestamps.js +2 -89
- package/dist/store/sync/timestamps.js.map +1 -1
- package/dist/store/sync/trigger-classes.d.ts +8 -0
- package/dist/store/sync/trigger-classes.d.ts.map +1 -1
- package/dist/store/sync/trigger-classes.js +13 -0
- package/dist/store/sync/trigger-classes.js.map +1 -1
- package/dist/store/sync/write-actor.d.ts +58 -0
- package/dist/store/sync/write-actor.d.ts.map +1 -0
- package/dist/store/sync/write-actor.js +67 -0
- package/dist/store/sync/write-actor.js.map +1 -0
- package/dist/store/table-classification.d.ts +27 -0
- package/dist/store/table-classification.d.ts.map +1 -1
- package/dist/store/table-classification.js +116 -0
- package/dist/store/table-classification.js.map +1 -1
- package/dist/store/task-claim.d.ts +1 -1
- package/dist/store/task-claim.d.ts.map +1 -1
- package/dist/store/task-claim.js +1 -1
- package/dist/store/task-claim.js.map +1 -1
- package/dist/store/task-reference-columns.d.ts +27 -0
- package/dist/store/task-reference-columns.d.ts.map +1 -0
- package/dist/store/task-reference-columns.js +65 -0
- package/dist/store/task-reference-columns.js.map +1 -0
- package/dist/store/task-version.d.ts +1 -1
- package/dist/store/task-version.d.ts.map +1 -1
- package/dist/store/task-version.js +1 -1
- package/dist/store/task-version.js.map +1 -1
- package/dist/store/tasks-schema.d.ts +2 -1
- package/dist/store/tasks-schema.d.ts.map +1 -1
- package/dist/store/tasks-schema.js +5 -4
- package/dist/store/tasks-schema.js.map +1 -1
- package/dist/store/tasks-sqlite.d.ts +1 -1
- package/dist/store/tasks-sqlite.d.ts.map +1 -1
- package/dist/store/tasks-sqlite.js +3 -1
- package/dist/store/tasks-sqlite.js.map +1 -1
- package/dist/store/twin-collapse.d.ts +43 -14
- package/dist/store/twin-collapse.d.ts.map +1 -1
- package/dist/store/twin-collapse.js +373 -32
- package/dist/store/twin-collapse.js.map +1 -1
- package/dist/store/validation-schemas.d.ts +44 -674
- package/dist/store/validation-schemas.d.ts.map +1 -1
- package/dist/store/validation-schemas.js +2 -22
- package/dist/store/validation-schemas.js.map +1 -1
- package/dist/store/vault-manifest.d.ts +6 -2
- package/dist/store/vault-manifest.d.ts.map +1 -1
- package/dist/store/vault-manifest.js +10 -3
- package/dist/store/vault-manifest.js.map +1 -1
- package/dist/store/worktree-build-guard.js +1 -1
- package/dist/store/worktree-build-guard.js.map +1 -1
- package/dist/store/worktree-isolation-guard.js +1 -1
- package/dist/store/worktree-isolation-guard.js.map +1 -1
- package/dist/store/writer-lease.d.ts.map +1 -1
- package/dist/store/writer-lease.js +1 -11
- package/dist/store/writer-lease.js.map +1 -1
- package/dist/store/zod-schema-factory.d.ts +16 -0
- package/dist/store/zod-schema-factory.d.ts.map +1 -0
- package/dist/store/zod-schema-factory.js +23 -0
- package/dist/store/zod-schema-factory.js.map +1 -0
- package/dist/system/backup.d.ts +106 -0
- package/dist/system/backup.d.ts.map +1 -1
- package/dist/system/backup.js +295 -19
- package/dist/system/backup.js.map +1 -1
- package/dist/system/cleanup.js +1 -1
- package/dist/system/cleanup.js.map +1 -1
- package/dist/system/health.d.ts +13 -0
- package/dist/system/health.d.ts.map +1 -1
- package/dist/system/health.js +96 -1
- package/dist/system/health.js.map +1 -1
- package/dist/system/labels.js +1 -1
- package/dist/system/labels.js.map +1 -1
- package/dist/system/migrate.js +1 -1
- package/dist/system/migrate.js.map +1 -1
- package/dist/system/safestop.d.ts.map +1 -1
- package/dist/system/safestop.js +1 -1
- package/dist/system/safestop.js.map +1 -1
- package/dist/task-work/current.d.ts +34 -0
- package/dist/task-work/current.d.ts.map +1 -0
- package/dist/task-work/current.js +34 -0
- package/dist/task-work/current.js.map +1 -0
- package/dist/task-work/index.d.ts +2 -19
- package/dist/task-work/index.d.ts.map +1 -1
- package/dist/task-work/index.js +6 -20
- package/dist/task-work/index.js.map +1 -1
- package/dist/tasks/ac-identity.d.ts +28 -0
- package/dist/tasks/ac-identity.d.ts.map +1 -0
- package/dist/tasks/ac-identity.js +43 -0
- package/dist/tasks/ac-identity.js.map +1 -0
- package/dist/tasks/ac-immutability.d.ts +1 -1
- package/dist/tasks/ac-immutability.d.ts.map +1 -1
- package/dist/tasks/ac-immutability.js +1 -1
- package/dist/tasks/ac-immutability.js.map +1 -1
- package/dist/tasks/ac-table.d.ts +2 -14
- package/dist/tasks/ac-table.d.ts.map +1 -1
- package/dist/tasks/ac-table.js +6 -29
- package/dist/tasks/ac-table.js.map +1 -1
- package/dist/tasks/acceptance-input.js +1 -1
- package/dist/tasks/acceptance-input.js.map +1 -1
- package/dist/tasks/add.d.ts.map +1 -1
- package/dist/tasks/add.js +3 -1
- package/dist/tasks/add.js.map +1 -1
- package/dist/tasks/affected-packages.d.ts +100 -19
- package/dist/tasks/affected-packages.d.ts.map +1 -1
- package/dist/tasks/affected-packages.js +169 -32
- package/dist/tasks/affected-packages.js.map +1 -1
- package/dist/tasks/affected-template.d.ts +93 -0
- package/dist/tasks/affected-template.d.ts.map +1 -0
- package/dist/tasks/affected-template.js +198 -0
- package/dist/tasks/affected-template.js.map +1 -0
- package/dist/tasks/archive.d.ts.map +1 -1
- package/dist/tasks/archive.js +1 -1
- package/dist/tasks/archive.js.map +1 -1
- package/dist/tasks/axis-filters.d.ts.map +1 -1
- package/dist/tasks/axis-filters.js +2 -1
- package/dist/tasks/axis-filters.js.map +1 -1
- package/dist/tasks/child-disposition.js +1 -1
- package/dist/tasks/child-disposition.js.map +1 -1
- package/dist/tasks/collection.js +1 -1
- package/dist/tasks/collection.js.map +1 -1
- package/dist/tasks/command-line.d.ts +4 -1
- package/dist/tasks/command-line.d.ts.map +1 -1
- package/dist/tasks/command-line.js +7 -2
- package/dist/tasks/command-line.js.map +1 -1
- package/dist/tasks/complete.d.ts.map +1 -1
- package/dist/tasks/complete.js +2 -1
- package/dist/tasks/complete.js.map +1 -1
- package/dist/tasks/decompose.js +1 -1
- package/dist/tasks/decompose.js.map +1 -1
- package/dist/tasks/delete.js +1 -1
- package/dist/tasks/delete.js.map +1 -1
- package/dist/tasks/done-plan.d.ts +2 -1
- package/dist/tasks/done-plan.d.ts.map +1 -1
- package/dist/tasks/done-plan.js +21 -21
- package/dist/tasks/done-plan.js.map +1 -1
- package/dist/tasks/done-record.d.ts +1 -1
- package/dist/tasks/done-record.d.ts.map +1 -1
- package/dist/tasks/done-record.js +1 -1
- package/dist/tasks/done-record.js.map +1 -1
- package/dist/tasks/duplicate-detector.d.ts.map +1 -1
- package/dist/tasks/duplicate-detector.js +11 -5
- package/dist/tasks/duplicate-detector.js.map +1 -1
- package/dist/tasks/enforcement.d.ts +2 -1
- package/dist/tasks/enforcement.d.ts.map +1 -1
- package/dist/tasks/enforcement.js +1 -1
- package/dist/tasks/enforcement.js.map +1 -1
- package/dist/tasks/engine-wrap-ops.js +1 -1
- package/dist/tasks/engine-wrap-ops.js.map +1 -1
- package/dist/tasks/engine-wrap.d.ts.map +1 -1
- package/dist/tasks/engine-wrap.js +7 -3
- package/dist/tasks/engine-wrap.js.map +1 -1
- package/dist/tasks/epic-enforcement.js +1 -1
- package/dist/tasks/epic-enforcement.js.map +1 -1
- package/dist/tasks/evidence.d.ts.map +1 -1
- package/dist/tasks/evidence.js +216 -20
- package/dist/tasks/evidence.js.map +1 -1
- package/dist/tasks/find.js +1 -1
- package/dist/tasks/find.js.map +1 -1
- package/dist/tasks/gate-result-cache.js +1 -1
- package/dist/tasks/gate-result-cache.js.map +1 -1
- package/dist/tasks/gate-runner.d.ts.map +1 -1
- package/dist/tasks/gate-runner.js +1 -1
- package/dist/tasks/gate-runner.js.map +1 -1
- package/dist/tasks/heavy-tool-env.d.ts +454 -19
- package/dist/tasks/heavy-tool-env.d.ts.map +1 -1
- package/dist/tasks/heavy-tool-env.js +818 -42
- package/dist/tasks/heavy-tool-env.js.map +1 -1
- package/dist/tasks/index.d.ts +3 -3
- package/dist/tasks/index.d.ts.map +1 -1
- package/dist/tasks/index.js +3 -3
- package/dist/tasks/index.js.map +1 -1
- package/dist/tasks/labels.js +1 -1
- package/dist/tasks/labels.js.map +1 -1
- package/dist/tasks/nexus-impact-gate.js +1 -1
- package/dist/tasks/nexus-impact-gate.js.map +1 -1
- package/dist/tasks/pipeline-stage.js +1 -1
- package/dist/tasks/pipeline-stage.js.map +1 -1
- package/dist/tasks/population.js +1 -1
- package/dist/tasks/population.js.map +1 -1
- package/dist/tasks/ranking-audit.js +1 -1
- package/dist/tasks/ranking-audit.js.map +1 -1
- package/dist/tasks/reconcile-scope.js +1 -1
- package/dist/tasks/reconcile-scope.js.map +1 -1
- package/dist/tasks/relates.js +1 -1
- package/dist/tasks/relates.js.map +1 -1
- package/dist/tasks/req.d.ts.map +1 -1
- package/dist/tasks/req.js +2 -1
- package/dist/tasks/req.js.map +1 -1
- package/dist/tasks/severity-attestation.d.ts +1 -1
- package/dist/tasks/severity-attestation.d.ts.map +1 -1
- package/dist/tasks/severity-attestation.js +1 -1
- package/dist/tasks/severity-attestation.js.map +1 -1
- package/dist/tasks/show.d.ts.map +1 -1
- package/dist/tasks/show.js +47 -3
- package/dist/tasks/show.js.map +1 -1
- package/dist/tasks/task-import.js +1 -1
- package/dist/tasks/task-import.js.map +1 -1
- package/dist/tasks/task-reparent.d.ts.map +1 -1
- package/dist/tasks/task-reparent.js +2 -1
- package/dist/tasks/task-reparent.js.map +1 -1
- package/dist/tasks/test-run-binding.d.ts.map +1 -1
- package/dist/tasks/test-run-binding.js +9 -2
- package/dist/tasks/test-run-binding.js.map +1 -1
- package/dist/tasks/tool-cache-env.d.ts +20 -13
- package/dist/tasks/tool-cache-env.d.ts.map +1 -1
- package/dist/tasks/tool-cache-env.js +27 -48
- package/dist/tasks/tool-cache-env.js.map +1 -1
- package/dist/tasks/tool-cache.d.ts +30 -7
- package/dist/tasks/tool-cache.d.ts.map +1 -1
- package/dist/tasks/tool-cache.js +74 -20
- package/dist/tasks/tool-cache.js.map +1 -1
- package/dist/tasks/tool-resolver.d.ts +8 -0
- package/dist/tasks/tool-resolver.d.ts.map +1 -1
- package/dist/tasks/tool-resolver.js +15 -1
- package/dist/tasks/tool-resolver.js.map +1 -1
- package/dist/tasks/tool-runner-guard.d.ts +131 -0
- package/dist/tasks/tool-runner-guard.d.ts.map +1 -0
- package/dist/tasks/tool-runner-guard.js +141 -0
- package/dist/tasks/tool-runner-guard.js.map +1 -0
- package/dist/tasks/tool-semaphore.d.ts +106 -302
- package/dist/tasks/tool-semaphore.d.ts.map +1 -1
- package/dist/tasks/tool-semaphore.js +132 -558
- package/dist/tasks/tool-semaphore.js.map +1 -1
- package/dist/tasks/update.d.ts.map +1 -1
- package/dist/tasks/update.js +2 -1
- package/dist/tasks/update.js.map +1 -1
- package/dist/upgrade.d.ts.map +1 -1
- package/dist/upgrade.js +35 -31
- package/dist/upgrade.js.map +1 -1
- package/dist/validation/doctor/checks.d.ts +41 -0
- package/dist/validation/doctor/checks.d.ts.map +1 -1
- package/dist/validation/doctor/checks.js +251 -0
- package/dist/validation/doctor/checks.js.map +1 -1
- package/dist/validation/doctor/index.d.ts +1 -1
- package/dist/validation/doctor/index.d.ts.map +1 -1
- package/dist/validation/doctor/index.js +1 -1
- package/dist/validation/doctor/index.js.map +1 -1
- package/dist/validation/engine-ops.js +1 -1
- package/dist/validation/engine-ops.js.map +1 -1
- package/dist/validation/engine.d.ts +1 -1
- package/dist/validation/engine.d.ts.map +1 -1
- package/dist/validation/engine.js +2 -1
- package/dist/validation/engine.js.map +1 -1
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/index.js.map +1 -1
- package/dist/validation/operation-gate-validators.js +1 -1
- package/dist/validation/operation-gate-validators.js.map +1 -1
- package/dist/validation/protocol-common.js +1 -1
- package/dist/validation/protocol-common.js.map +1 -1
- package/dist/validation/protocols/_shared.js +1 -1
- package/dist/validation/protocols/_shared.js.map +1 -1
- package/dist/validation/validate-ops.js +1 -1
- package/dist/validation/validate-ops.js.map +1 -1
- package/dist/validation/verification.js +1 -1
- package/dist/validation/verification.js.map +1 -1
- package/dist/workgraph/index.d.ts +1 -1
- package/dist/workgraph/index.d.ts.map +1 -1
- package/dist/workgraph/index.js +1 -1
- package/dist/workgraph/index.js.map +1 -1
- package/dist/workgraph/reparent-preflight.d.ts +1 -2
- package/dist/workgraph/reparent-preflight.d.ts.map +1 -1
- package/dist/workgraph/reparent-preflight.js +1 -1
- package/dist/workgraph/reparent-preflight.js.map +1 -1
- package/dist/workgraph/scaffold-validate.js +1 -1
- package/dist/workgraph/scaffold-validate.js.map +1 -1
- package/dist/worktree/force-unlock.d.ts +1 -1
- package/dist/worktree/force-unlock.d.ts.map +1 -1
- package/dist/worktree/force-unlock.js +1 -1
- package/dist/worktree/force-unlock.js.map +1 -1
- package/dist/worktree/isolation.d.ts +1 -1
- package/dist/worktree/isolation.d.ts.map +1 -1
- package/dist/worktree/isolation.js +1 -1
- package/dist/worktree/isolation.js.map +1 -1
- package/dist/worktree/list.d.ts +1 -1
- package/dist/worktree/list.d.ts.map +1 -1
- package/dist/worktree/list.js +1 -1
- package/dist/worktree/list.js.map +1 -1
- package/dist/worktree/prune.d.ts +1 -1
- package/dist/worktree/prune.d.ts.map +1 -1
- package/dist/worktree/prune.js +1 -1
- package/dist/worktree/prune.js.map +1 -1
- package/dist/worktree/worktree-adopt.d.ts +1 -2
- package/dist/worktree/worktree-adopt.d.ts.map +1 -1
- package/dist/worktree/worktree-adopt.js +1 -1
- package/dist/worktree/worktree-adopt.js.map +1 -1
- package/migrations/sync-journal/20261004120000_t12757-apply-intent/migration.sql +21 -0
- package/migrations/sync-journal/20261005120000_t12344-inbox/migration.sql +67 -0
- package/migrations/sync-journal/20261006120000_t13193-rebase-undo/migration.sql +51 -0
- package/migrations/sync-journal/20261006130000_t13193-rebase-state/migration.sql +17 -0
- package/migrations/sync-journal/20261006140000_t13193-held-rows/migration.sql +24 -0
- package/package.json +13 -13
- package/schemas/project-context.schema.json +19 -1
- package/templates/CLEO-INJECTION.md +11 -11
- package/templates/CLEO-REFERENCE.md +2 -2
- package/templates/workflows/release-prepare.yml.tmpl +10 -10
|
@@ -0,0 +1,1401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The admission ledger: ONE machine-wide budget and ONE FIFO queue for every
|
|
3
|
+
* heavy run (T13133, epic T13121).
|
|
4
|
+
*
|
|
5
|
+
* It replaces the layers that admitted heavy work before it: the tool
|
|
6
|
+
* semaphore's per-tool slot directories, the governor's `test-run`,
|
|
7
|
+
* `scoped-build` and `full-build` slots, `cleo run`'s per-class queues and the
|
|
8
|
+
* darwin one-slot rule. Each layer bounded only what it could see, and two of
|
|
9
|
+
* them taken in opposite orders deadlocked the machine queue (T13133). Here
|
|
10
|
+
* every heavy run — a `cleo verify` evidence run, a `cleo run` job, the vitest
|
|
11
|
+
* project probe — asks one ledger for a share of one budget.
|
|
12
|
+
*
|
|
13
|
+
* ## Budget
|
|
14
|
+
*
|
|
15
|
+
* Bytes, not runs: `capacity = totalRAM − max(4 GiB, 25%)`. A run asks for its
|
|
16
|
+
* footprint ({@link footprintForTool}, {@link footprintForClass}); a run larger
|
|
17
|
+
* than the capacity is clamped to it and runs alone, so nothing waits forever
|
|
18
|
+
* on its size. Pressure scales the budget: the memory gate (`pressure-gate.ts`,
|
|
19
|
+
* T13127) refusing means nothing starts; a `hold` verdict halves the budget and
|
|
20
|
+
* a CPU-saturated `backoff` admits one run at a time (each keeps one run
|
|
21
|
+
* going when nothing else is running).
|
|
22
|
+
*
|
|
23
|
+
* ## Exclusive runs
|
|
24
|
+
*
|
|
25
|
+
* A `full-build` (`cleo run --class full-build`: a workspace-wide build,
|
|
26
|
+
* turbo/nx across every package) is EXCLUSIVE: at most one exclusive run is
|
|
27
|
+
* admitted machine-wide at a time, whatever its footprint (T13237). Bytes
|
|
28
|
+
* alone stopped guaranteeing that once T13132 charged a heavy run half the
|
|
29
|
+
* budget: two whole-workspace builds fit and ran at once, the saturation
|
|
30
|
+
* shape behind the P0 crash. Other runs still share the budget beside it.
|
|
31
|
+
*
|
|
32
|
+
* ## Queue
|
|
33
|
+
*
|
|
34
|
+
* FIFO with backfill: the oldest waiting run starts when it fits; a later run
|
|
35
|
+
* may start ahead of it while it is young, if it fits now. Once the oldest
|
|
36
|
+
* waiting run has waited {@link LEDGER_RESERVATION_MS} it holds a reservation
|
|
37
|
+
* and nothing passes it, so neither a big run nor a stream of small ones can
|
|
38
|
+
* starve the other. Any process that enters the ledger (an arrival, a waiter's
|
|
39
|
+
* periodic pass, a release) runs the scheduling pass for everyone; waiters see
|
|
40
|
+
* their admission by reading the file.
|
|
41
|
+
*
|
|
42
|
+
* ## Re-entrancy
|
|
43
|
+
*
|
|
44
|
+
* A grant exports `CLEO_ADMISSION=<id>.<nonce>` to its children. A request
|
|
45
|
+
* made inside an admitted run's process tree rides that grant (no new budget,
|
|
46
|
+
* no queue), so a `cleo verify` under `cleo run`, or a `cleo run` under an
|
|
47
|
+
* evidence run, never waits for the budget its own ancestor holds. The token
|
|
48
|
+
* only says which grant to check: the caller must descend from the holder
|
|
49
|
+
* (ancestry, or membership of a tool process group it started), with the
|
|
50
|
+
* holder's start time matching, so the env var alone grants nothing. A wrapper
|
|
51
|
+
* that scrubs the environment is caught by the same ancestry scan.
|
|
52
|
+
*
|
|
53
|
+
* ## The file
|
|
54
|
+
*
|
|
55
|
+
* `<cleoHome>/admission/ledger.json`, rewritten by tmp + rename inside a
|
|
56
|
+
* `proper-lockfile` critical section that only reads, decides and writes:
|
|
57
|
+
* pressure samples and process probes happen before it is entered, and it is
|
|
58
|
+
* never held across a spawn or a wait. An entry is dropped once its holder is
|
|
59
|
+
* provably gone: pid gone (or recycled, by start time) and every tool group it
|
|
60
|
+
* started gone; a fresh heartbeat or an unreadable probe keeps it.
|
|
61
|
+
*
|
|
62
|
+
* @module resources/admission-ledger
|
|
63
|
+
* @task T13133
|
|
64
|
+
* @epic T13121
|
|
65
|
+
*/
|
|
66
|
+
import { execFileSync } from 'node:child_process';
|
|
67
|
+
import { randomBytes } from 'node:crypto';
|
|
68
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
69
|
+
import { hostname, totalmem } from 'node:os';
|
|
70
|
+
import { join } from 'node:path';
|
|
71
|
+
import lockfile from 'proper-lockfile';
|
|
72
|
+
import { getLogger } from '../logger.js';
|
|
73
|
+
import { getCleoHome } from '../paths.js';
|
|
74
|
+
import { admissionCapacityBytes, admissionReserveBytes, defaultSingleProcessHeapMb, GIB_PER_WORKER, HEAVY_TOOL_HEAP_MB, heavyToolWorkers, isHeavyTool, isMemoryBoundTool, } from '../tasks/heavy-tool-env.js';
|
|
75
|
+
import { pressureScore, ResourceMonitor } from './monitor.js';
|
|
76
|
+
import { checkMemoryGate, MEMORY_GATE_RETRY_AFTER_MS, } from './pressure-gate.js';
|
|
77
|
+
import { processAncestors, processGroupOf } from './run-admission.js';
|
|
78
|
+
import { ownProcessStartedAt, systemPidProbe } from './slot-holder.js';
|
|
79
|
+
import { followToolGroups, isProbeableId } from './tool-groups.js';
|
|
80
|
+
let _log = null;
|
|
81
|
+
function log() {
|
|
82
|
+
if (_log === null)
|
|
83
|
+
_log = getLogger('admission-ledger');
|
|
84
|
+
return _log;
|
|
85
|
+
}
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// Constants
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
/** The env var a grant exports to its children: `<id>.<nonce>`. */
|
|
90
|
+
export const ADMISSION_ENV = 'CLEO_ADMISSION';
|
|
91
|
+
/** The oldest waiting run holds a reservation (no backfill passes it) after this long. */
|
|
92
|
+
export const LEDGER_RESERVATION_MS = 120_000;
|
|
93
|
+
/** An admitted run refreshes its heartbeat this often. */
|
|
94
|
+
export const LEDGER_HEARTBEAT_MS = 15_000;
|
|
95
|
+
/** A heartbeat older than this no longer proves its holder alive on its own. */
|
|
96
|
+
export const LEDGER_HEARTBEAT_STALE_MS = 60_000;
|
|
97
|
+
/** A waiter names the holders (and any suspected cycle) after this long, then this often. */
|
|
98
|
+
export const LEDGER_HOLDER_REPORT_MS = 60_000;
|
|
99
|
+
/** A crashed process's critical-section lock is taken over after this long. */
|
|
100
|
+
export const LEDGER_CRITICAL_STALE_MS = 10_000;
|
|
101
|
+
/** How often a waiter re-reads the ledger to see whether it was admitted. */
|
|
102
|
+
const READ_POLL_MS = 250;
|
|
103
|
+
/** How often a waiter runs a scheduling pass of its own when nobody has sampled pressure lately. */
|
|
104
|
+
const PASS_EVERY_MS = 2_000;
|
|
105
|
+
/** A pressure verdict written to the ledger this recently is reused instead of sampling again. */
|
|
106
|
+
const PRESSURE_REUSE_MS = PASS_EVERY_MS;
|
|
107
|
+
/**
|
|
108
|
+
* The ledger file format this CLEO reads and writes. A file with a newer
|
|
109
|
+
* version is never rewritten by this one: admission runs ungoverned instead.
|
|
110
|
+
*/
|
|
111
|
+
export const LEDGER_VERSION = 1;
|
|
112
|
+
/**
|
|
113
|
+
* An entry whose holder cannot be positively identified (pid gone, recycled,
|
|
114
|
+
* unprobeable, or no recorded start time) is dropped once its heartbeat is
|
|
115
|
+
* this old. A live holder heartbeats every {@link LEDGER_HEARTBEAT_MS}. The
|
|
116
|
+
* old slot locks had the same 10-minute takeover as a backstop.
|
|
117
|
+
*/
|
|
118
|
+
export const LEDGER_ORPHAN_MS = 600_000;
|
|
119
|
+
/**
|
|
120
|
+
* `CLEO_ADMISSION_PRESSURE=off`: admission ignores memory and CPU pressure
|
|
121
|
+
* and admits on the byte budget alone (a CI runner whose PSI is noisy, and
|
|
122
|
+
* the test suite, which must never depend on the host's load). An explicit
|
|
123
|
+
* sampler passed to {@link admit} is still used.
|
|
124
|
+
*/
|
|
125
|
+
export const ADMISSION_PRESSURE_ENV = 'CLEO_ADMISSION_PRESSURE';
|
|
126
|
+
/** One GiB in bytes. */
|
|
127
|
+
export const GIB = 1024 ** 3;
|
|
128
|
+
/** What a light run (audit, security scan) or a config probe is charged. */
|
|
129
|
+
export const LIGHT_FOOTPRINT_BYTES = GIB;
|
|
130
|
+
/**
|
|
131
|
+
* Resident memory one Node process holds beyond its V8 heap ceiling, in MiB:
|
|
132
|
+
* code, native allocations, buffers (the same allowance the heavy worker count
|
|
133
|
+
* makes: {@link GIB_PER_WORKER} minus the default heap).
|
|
134
|
+
*/
|
|
135
|
+
export const PROCESS_OVERHEAD_MB = GIB_PER_WORKER * 1024 - HEAVY_TOOL_HEAP_MB;
|
|
136
|
+
/** Lock retries for the critical section: up to ~15 s, longer than the stale takeover. */
|
|
137
|
+
/**
|
|
138
|
+
* Lock attempts while another process is inside the critical section, and the
|
|
139
|
+
* backoff between them (ms, growing by 1.2, randomized up to 2x): about 15 s
|
|
140
|
+
* in all. Only contention (`ELOCKED`) is retried; any other error (a
|
|
141
|
+
* read-only CLEO home) fails at once, so a sandbox runs ungoverned without
|
|
142
|
+
* first waiting out the retries.
|
|
143
|
+
*/
|
|
144
|
+
const LOCK_ATTEMPTS = 400;
|
|
145
|
+
const LOCK_BACKOFF_MS = [2, 40];
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
// Budget and footprints
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
/** The machine budget and the reserve it leaves (owned by the heavy-tool plan, which sizes runs to it). */
|
|
150
|
+
export { admissionCapacityBytes, admissionReserveBytes };
|
|
151
|
+
/**
|
|
152
|
+
* What one heavy test or build run is charged: the worker count the heavy-tool
|
|
153
|
+
* overlay gives it (`heavyToolWorkers`) × {@link GIB_PER_WORKER}.
|
|
154
|
+
*
|
|
155
|
+
* @param totalBytes - physical RAM. @defaultValue os.totalmem()
|
|
156
|
+
* @param env - the environment (the per-run share). @defaultValue process.env
|
|
157
|
+
*/
|
|
158
|
+
export function heavyRunFootprintBytes(totalBytes = totalmem(), env = process.env) {
|
|
159
|
+
return heavyToolWorkers(totalBytes / GIB, env) * GIB_PER_WORKER * GIB;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* What an evidence run of a canonical tool is charged: a heavy test or build
|
|
163
|
+
* run its workers × {@link GIB_PER_WORKER}; a single-process memory-bound run
|
|
164
|
+
* (`typecheck`, `lint`) its heap ceiling plus {@link PROCESS_OVERHEAD_MB} (the
|
|
165
|
+
* heap the run is planned with, T13122, or the machine's default for a single
|
|
166
|
+
* process); anything else {@link LIGHT_FOOTPRINT_BYTES}.
|
|
167
|
+
*
|
|
168
|
+
* @param canonical - the canonical tool.
|
|
169
|
+
* @param totalBytes - physical RAM. @defaultValue os.totalmem()
|
|
170
|
+
* @param heapMb - the heap ceiling the run is spawned with, in MiB, when known.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* footprintForTool('test', 48 * GIB); // 36 GiB (6 workers × 6 GiB)
|
|
175
|
+
* footprintForTool('typecheck', 48 * GIB); // 6 GiB (4096 MiB heap + 2048 MiB)
|
|
176
|
+
* footprintForTool('typecheck', 48 * GIB, 8192); // 10 GiB
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export function footprintForTool(canonical, totalBytes = totalmem(), heapMb) {
|
|
180
|
+
if (isHeavyTool(canonical))
|
|
181
|
+
return heavyRunFootprintBytes(totalBytes);
|
|
182
|
+
if (isMemoryBoundTool(canonical)) {
|
|
183
|
+
const heap = heapMb ?? defaultSingleProcessHeapMb(totalBytes / GIB);
|
|
184
|
+
return (Math.max(0, heap) + PROCESS_OVERHEAD_MB) * 1024 * 1024;
|
|
185
|
+
}
|
|
186
|
+
return LIGHT_FOOTPRINT_BYTES;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* What a planned memory-bound run is charged: every process it may start ×
|
|
190
|
+
* (its heap ceiling + {@link PROCESS_OVERHEAD_MB}), i.e. workspace packages in
|
|
191
|
+
* flight × workers × per-process cost. The plan's limits are what the child is
|
|
192
|
+
* spawned with, so the charge is enforced, not estimated (T13132).
|
|
193
|
+
*
|
|
194
|
+
* @param plan - the `resources` of `planHeavyToolEnv`.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* // 1 package × 3 workers × (4096 + 2048) MiB
|
|
199
|
+
* planFootprintBytes({ workspaceConcurrency: 1, workers: 3, heapMb: 4096 }); // 18 GiB
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
export function planFootprintBytes(plan) {
|
|
203
|
+
const processes = Math.max(1, plan.workspaceConcurrency) * Math.max(1, plan.workers);
|
|
204
|
+
return processes * (Math.max(0, plan.heapMb) + PROCESS_OVERHEAD_MB) * 1024 * 1024;
|
|
205
|
+
}
|
|
206
|
+
/** Governor classes whose admission is the ledger's. */
|
|
207
|
+
export const LEDGER_CLASSES = new Set([
|
|
208
|
+
'test-run',
|
|
209
|
+
'scoped-build',
|
|
210
|
+
'full-build',
|
|
211
|
+
]);
|
|
212
|
+
/**
|
|
213
|
+
* Whether the ledger admits a governor class.
|
|
214
|
+
*
|
|
215
|
+
* @param cls - the governor class.
|
|
216
|
+
*/
|
|
217
|
+
export function isLedgerClass(cls) {
|
|
218
|
+
return LEDGER_CLASSES.has(cls);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* What a `cleo run` job of a ledger class is charged.
|
|
222
|
+
*
|
|
223
|
+
* @param cls - the governor class.
|
|
224
|
+
* @param totalBytes - physical RAM. @defaultValue os.totalmem()
|
|
225
|
+
*/
|
|
226
|
+
export function footprintForClass(cls, totalBytes = totalmem()) {
|
|
227
|
+
return isLedgerClass(cls) ? heavyRunFootprintBytes(totalBytes) : LIGHT_FOOTPRINT_BYTES;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* The ledger directory under the CLEO home.
|
|
231
|
+
*
|
|
232
|
+
* @param cleoHome - the CLEO home. @defaultValue {@link getCleoHome}
|
|
233
|
+
*/
|
|
234
|
+
export function admissionDir(cleoHome = getCleoHome()) {
|
|
235
|
+
return join(cleoHome, 'admission');
|
|
236
|
+
}
|
|
237
|
+
function ledgerFile(dir) {
|
|
238
|
+
return join(dir, 'ledger.json');
|
|
239
|
+
}
|
|
240
|
+
function isEntry(v) {
|
|
241
|
+
if (typeof v !== 'object' || v === null)
|
|
242
|
+
return false;
|
|
243
|
+
const e = v;
|
|
244
|
+
return (typeof e.id === 'string' &&
|
|
245
|
+
typeof e.nonce === 'string' &&
|
|
246
|
+
isProbeableId(e.pid) &&
|
|
247
|
+
typeof e.host === 'string' &&
|
|
248
|
+
(e.startedAt === null || typeof e.startedAt === 'string') &&
|
|
249
|
+
typeof e.label === 'string' &&
|
|
250
|
+
typeof e.command === 'string' &&
|
|
251
|
+
typeof e.footprintBytes === 'number' &&
|
|
252
|
+
Number.isFinite(e.footprintBytes) &&
|
|
253
|
+
(e.state === 'waiting' || e.state === 'admitted') &&
|
|
254
|
+
typeof e.enqueuedAtMs === 'number' &&
|
|
255
|
+
typeof e.heartbeatAtMs === 'number' &&
|
|
256
|
+
Array.isArray(e.toolGroups));
|
|
257
|
+
}
|
|
258
|
+
const SHARES = new Set(['full', 'half', 'one', 'none']);
|
|
259
|
+
function isCachedPressure(v) {
|
|
260
|
+
if (typeof v !== 'object' || v === null)
|
|
261
|
+
return false;
|
|
262
|
+
const c = v;
|
|
263
|
+
return (typeof c.share === 'string' &&
|
|
264
|
+
SHARES.has(c.share) &&
|
|
265
|
+
(c.lightShare === undefined ||
|
|
266
|
+
(typeof c.lightShare === 'string' && SHARES.has(c.lightShare))) &&
|
|
267
|
+
typeof c.sampledAtMs === 'number' &&
|
|
268
|
+
(c.reading === null || (typeof c.reading === 'object' && c.reading !== null)));
|
|
269
|
+
}
|
|
270
|
+
function readLedgerDoc(dir) {
|
|
271
|
+
const empty = {
|
|
272
|
+
version: LEDGER_VERSION,
|
|
273
|
+
entries: [],
|
|
274
|
+
foreign: [],
|
|
275
|
+
pressure: null,
|
|
276
|
+
corrupt: false,
|
|
277
|
+
};
|
|
278
|
+
let raw;
|
|
279
|
+
try {
|
|
280
|
+
raw = readFileSync(ledgerFile(dir), 'utf-8');
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
return empty;
|
|
284
|
+
}
|
|
285
|
+
let parsed;
|
|
286
|
+
try {
|
|
287
|
+
parsed = JSON.parse(raw);
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
return { ...empty, corrupt: true };
|
|
291
|
+
}
|
|
292
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
293
|
+
return { ...empty, corrupt: true };
|
|
294
|
+
}
|
|
295
|
+
const o = parsed;
|
|
296
|
+
const version = typeof o.version === 'number' && Number.isFinite(o.version) ? o.version : LEDGER_VERSION;
|
|
297
|
+
const entries = [];
|
|
298
|
+
const foreign = [];
|
|
299
|
+
for (const v of Array.isArray(o.entries) ? o.entries : []) {
|
|
300
|
+
if (isEntry(v))
|
|
301
|
+
entries.push(v);
|
|
302
|
+
else if (typeof v === 'object' && v !== null && !Array.isArray(v))
|
|
303
|
+
foreign.push(v);
|
|
304
|
+
}
|
|
305
|
+
return {
|
|
306
|
+
version,
|
|
307
|
+
entries,
|
|
308
|
+
foreign,
|
|
309
|
+
pressure: isCachedPressure(o.pressure) ? o.pressure : null,
|
|
310
|
+
corrupt: false,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Read the ledger's entries without the lock (writes are atomic renames, so a
|
|
315
|
+
* reader sees a whole file). A missing or corrupt file has no entries;
|
|
316
|
+
* entries of a format this CLEO does not know are left out (they are kept in
|
|
317
|
+
* the file and still charged).
|
|
318
|
+
*
|
|
319
|
+
* @param dir - the ledger directory. @defaultValue {@link admissionDir}
|
|
320
|
+
*/
|
|
321
|
+
export function readLedger(dir = admissionDir()) {
|
|
322
|
+
return readLedgerDoc(dir).entries;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Entries of a format this CLEO does not know (written by another CLEO
|
|
326
|
+
* version sharing the CLEO home), read without the lock.
|
|
327
|
+
*
|
|
328
|
+
* @param dir - the ledger directory. @defaultValue {@link admissionDir}
|
|
329
|
+
*/
|
|
330
|
+
export function readForeignEntries(dir = admissionDir()) {
|
|
331
|
+
return readLedgerDoc(dir).foreign;
|
|
332
|
+
}
|
|
333
|
+
function writeLedgerDoc(dir, doc) {
|
|
334
|
+
const file = ledgerFile(dir);
|
|
335
|
+
const tmp = `${file}.${process.pid}.${randomBytes(4).toString('hex')}.tmp`;
|
|
336
|
+
try {
|
|
337
|
+
writeFileSync(tmp, JSON.stringify({
|
|
338
|
+
version: LEDGER_VERSION,
|
|
339
|
+
// Foreign entries go back where they were found, verbatim.
|
|
340
|
+
entries: [...doc.entries, ...doc.foreign],
|
|
341
|
+
pressure: doc.pressure,
|
|
342
|
+
}), 'utf-8');
|
|
343
|
+
renameSync(tmp, file);
|
|
344
|
+
}
|
|
345
|
+
catch (err) {
|
|
346
|
+
try {
|
|
347
|
+
rmSync(tmp, { force: true });
|
|
348
|
+
}
|
|
349
|
+
catch {
|
|
350
|
+
// Nothing more to do.
|
|
351
|
+
}
|
|
352
|
+
throw err;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* The ledger file was written by a newer CLEO: this one never rewrites it.
|
|
357
|
+
* Admission treats it like unwritable state and runs ungoverned.
|
|
358
|
+
*/
|
|
359
|
+
export class LedgerVersionError extends Error {
|
|
360
|
+
/** Error code, matched by {@link admissionIoError}. */
|
|
361
|
+
code = 'E_LEDGER_VERSION';
|
|
362
|
+
/** The ledger file. */
|
|
363
|
+
path;
|
|
364
|
+
/**
|
|
365
|
+
* @param version - the version found in the file.
|
|
366
|
+
* @param path - the ledger file.
|
|
367
|
+
*/
|
|
368
|
+
constructor(version, path) {
|
|
369
|
+
super(`admission ledger ${path} has format version ${version}; this CLEO understands ${LEDGER_VERSION} and will not rewrite it`);
|
|
370
|
+
this.name = 'LedgerVersionError';
|
|
371
|
+
this.path = path;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/** Take the ledger lock, retrying only while another process holds it. */
|
|
375
|
+
async function lockLedger(file) {
|
|
376
|
+
for (let attempt = 1;; attempt++) {
|
|
377
|
+
try {
|
|
378
|
+
return await lockfile.lock(file, {
|
|
379
|
+
lockfilePath: `${file}.lock`,
|
|
380
|
+
realpath: false,
|
|
381
|
+
stale: LEDGER_CRITICAL_STALE_MS,
|
|
382
|
+
retries: 0,
|
|
383
|
+
onCompromised: (err) => {
|
|
384
|
+
log().warn({ err: err.message }, 'admission ledger lock compromised; continuing');
|
|
385
|
+
},
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
catch (err) {
|
|
389
|
+
const code = err?.code;
|
|
390
|
+
if (code !== 'ELOCKED' || attempt >= LOCK_ATTEMPTS)
|
|
391
|
+
throw err;
|
|
392
|
+
const backoff = Math.min(LOCK_BACKOFF_MS[1], LOCK_BACKOFF_MS[0] * 1.2 ** attempt);
|
|
393
|
+
await new Promise((resolve) => {
|
|
394
|
+
setTimeout(resolve, backoff * (1 + Math.random()));
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Run `fn` inside the ledger's critical section: read, decide, write. `fn`
|
|
401
|
+
* must be synchronous and do no I/O of its own; its `doc` (or `null` for no
|
|
402
|
+
* change) is written back before the lock is released. A file of a newer
|
|
403
|
+
* format throws {@link LedgerVersionError} and is left untouched; a corrupt
|
|
404
|
+
* file is moved aside before it is replaced.
|
|
405
|
+
*/
|
|
406
|
+
async function withLedger(dir, fn) {
|
|
407
|
+
mkdirSync(dir, { recursive: true });
|
|
408
|
+
const file = ledgerFile(dir);
|
|
409
|
+
const unlock = await lockLedger(file);
|
|
410
|
+
try {
|
|
411
|
+
const current = readLedgerDoc(dir);
|
|
412
|
+
if (current.version > LEDGER_VERSION) {
|
|
413
|
+
// @sync-invariant none:local-only the admission ledger is a machine-local file, not a store write
|
|
414
|
+
throw new LedgerVersionError(current.version, file);
|
|
415
|
+
}
|
|
416
|
+
const { doc, result } = fn(current);
|
|
417
|
+
if (doc !== null) {
|
|
418
|
+
if (current.corrupt) {
|
|
419
|
+
log().warn({ file }, 'admission ledger was not valid JSON; moved aside and replaced');
|
|
420
|
+
renameSync(file, `${file}.corrupt-${Date.now()}`);
|
|
421
|
+
}
|
|
422
|
+
writeLedgerDoc(dir, doc);
|
|
423
|
+
}
|
|
424
|
+
return result;
|
|
425
|
+
}
|
|
426
|
+
finally {
|
|
427
|
+
try {
|
|
428
|
+
await unlock();
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
// Already released (stale takeover): the post-condition holds.
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
// ---------------------------------------------------------------------------
|
|
436
|
+
// Liveness
|
|
437
|
+
// ---------------------------------------------------------------------------
|
|
438
|
+
/**
|
|
439
|
+
* Whether an entry's holder is still there.
|
|
440
|
+
*
|
|
441
|
+
* - A pid that is provably gone (or recycled: alive with another start time,
|
|
442
|
+
* once the heartbeat is stale) is dead as soon as every tool group it
|
|
443
|
+
* started is gone too; a detached tool outlives a SIGKILLed cleo.
|
|
444
|
+
* - A fresh heartbeat, or a live pid whose start time matches the recorded
|
|
445
|
+
* one, is alive.
|
|
446
|
+
* - A holder that cannot be identified (the probe fails, or no start time was
|
|
447
|
+
* recorded or can be read) is alive until its heartbeat is
|
|
448
|
+
* {@link LEDGER_ORPHAN_MS} old, and so is a gone holder whose tool group id
|
|
449
|
+
* still runs (a group id carries no start time, so it may be recycled).
|
|
450
|
+
* Past that bound both are dead: nothing holds the budget forever.
|
|
451
|
+
* - An entry of another host is judged by its heartbeat alone.
|
|
452
|
+
*
|
|
453
|
+
* @param entry - the ledger entry.
|
|
454
|
+
* @param nowMs - the clock.
|
|
455
|
+
* @param probe - pid probe (tests inject one).
|
|
456
|
+
*/
|
|
457
|
+
export function entryLiveness(entry, nowMs, probe = systemPidProbe) {
|
|
458
|
+
const age = nowMs - entry.heartbeatAtMs;
|
|
459
|
+
if (entry.host !== hostname())
|
|
460
|
+
return age < LEDGER_HEARTBEAT_STALE_MS ? 'alive' : 'dead';
|
|
461
|
+
const orphaned = age >= LEDGER_ORPHAN_MS;
|
|
462
|
+
const goneUnlessGroups = () => orphaned || entry.toolGroups.every((g) => isProbeableId(g) && probe.groupLiveness(g) === 'gone')
|
|
463
|
+
? 'dead'
|
|
464
|
+
: 'alive';
|
|
465
|
+
const pid = probe.liveness(entry.pid);
|
|
466
|
+
if (pid === 'gone')
|
|
467
|
+
return goneUnlessGroups();
|
|
468
|
+
if (age < LEDGER_HEARTBEAT_STALE_MS)
|
|
469
|
+
return 'alive';
|
|
470
|
+
if (pid === 'alive' && entry.startedAt !== null) {
|
|
471
|
+
const startedAt = probe.startedAt(entry.pid);
|
|
472
|
+
if (startedAt === entry.startedAt)
|
|
473
|
+
return 'alive';
|
|
474
|
+
if (startedAt !== null)
|
|
475
|
+
return goneUnlessGroups(); // a recycled pid
|
|
476
|
+
}
|
|
477
|
+
return orphaned ? 'dead' : 'alive';
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* The budget an entry of an unknown format is charged: its numeric
|
|
481
|
+
* `footprintBytes` when it has one, else the whole capacity.
|
|
482
|
+
*/
|
|
483
|
+
function foreignCharge(f, capacityBytes) {
|
|
484
|
+
const fp = f.footprintBytes;
|
|
485
|
+
return typeof fp === 'number' && Number.isFinite(fp) && fp >= 0
|
|
486
|
+
? Math.min(fp, capacityBytes)
|
|
487
|
+
: capacityBytes;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Whether an entry of an unknown format is provably finished: its heartbeat
|
|
491
|
+
* (when it has one) is {@link LEDGER_ORPHAN_MS} old, or its pid on this host
|
|
492
|
+
* is gone.
|
|
493
|
+
*/
|
|
494
|
+
function foreignLiveness(f, nowMs, probe) {
|
|
495
|
+
const hb = f.heartbeatAtMs;
|
|
496
|
+
if (typeof hb === 'number' && Number.isFinite(hb) && nowMs - hb >= LEDGER_ORPHAN_MS)
|
|
497
|
+
return 'dead';
|
|
498
|
+
if (isProbeableId(f.pid) && f.host === hostname() && probe.liveness(f.pid) === 'gone') {
|
|
499
|
+
return 'dead';
|
|
500
|
+
}
|
|
501
|
+
return 'alive';
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* A run larger than this is heavy: more than one worker's footprint
|
|
505
|
+
* ({@link GIB_PER_WORKER}), i.e. a multi-process test or build run. CPU
|
|
506
|
+
* saturation narrows only heavy runs (T13132): a single-process typecheck or
|
|
507
|
+
* lint, a single-file test run and the config probe are one process each, and
|
|
508
|
+
* serialising them behind a whole suite only starved them.
|
|
509
|
+
*/
|
|
510
|
+
export const HEAVY_FOOTPRINT_BYTES = GIB_PER_WORKER * GIB;
|
|
511
|
+
/**
|
|
512
|
+
* The budget share for light runs ({@link HEAVY_FOOTPRINT_BYTES} or less): the
|
|
513
|
+
* memory signal alone — `none` while the memory gate refuses, `half` at memory
|
|
514
|
+
* hold, else `full`. CPU pressure never narrows it (T13132, T13170).
|
|
515
|
+
*
|
|
516
|
+
* @param sample - a backend sample.
|
|
517
|
+
* @param gateRefuses - the memory gate's verdict for the sample.
|
|
518
|
+
*/
|
|
519
|
+
export function lightBudgetShare(sample, gateRefuses) {
|
|
520
|
+
if (gateRefuses)
|
|
521
|
+
return 'none';
|
|
522
|
+
const memory = (sample.globalPressure?.some ?? sample.slicePressure?.some)?.avg10 ?? 0;
|
|
523
|
+
return memory > 10 ? 'half' : 'full';
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* The budget share for HEAVY runs (above {@link HEAVY_FOOTPRINT_BYTES}), from
|
|
527
|
+
* the memory gate's verdict and the pressure score (memory, or CPU rescaled;
|
|
528
|
+
* see `pressureScore`). Light runs use {@link lightBudgetShare}.
|
|
529
|
+
*
|
|
530
|
+
* @param sample - a backend sample.
|
|
531
|
+
* @param gateRefuses - the memory gate's verdict for the sample.
|
|
532
|
+
*/
|
|
533
|
+
export function budgetShare(sample, gateRefuses) {
|
|
534
|
+
if (gateRefuses)
|
|
535
|
+
return 'none';
|
|
536
|
+
const score = pressureScore(sample);
|
|
537
|
+
if (score > 25)
|
|
538
|
+
return 'one';
|
|
539
|
+
if (score > 10)
|
|
540
|
+
return 'half';
|
|
541
|
+
return 'full';
|
|
542
|
+
}
|
|
543
|
+
function charged(entry, capacityBytes) {
|
|
544
|
+
return Math.min(Math.max(0, entry.footprintBytes), capacityBytes);
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* One scheduling pass: admit every waiting entry the budget and the queue
|
|
548
|
+
* order allow. Pure.
|
|
549
|
+
*
|
|
550
|
+
* - FIFO by `enqueuedAtMs`. A later entry may be admitted ahead of an older
|
|
551
|
+
* one that does not fit (backfill) until that older one has waited
|
|
552
|
+
* `reservationMs`; from then on nothing passes it.
|
|
553
|
+
* - A footprint above the capacity is charged the capacity (it runs alone).
|
|
554
|
+
* - Heavy runs (above {@link HEAVY_FOOTPRINT_BYTES}) take `share`: `half`
|
|
555
|
+
* halves the budget, `one` admits a single heavy run at a time (CPU
|
|
556
|
+
* saturated). Light runs take `lightShare` (memory alone), so CPU saturation
|
|
557
|
+
* never serialises them behind a heavy run (T13132). Either still admits the
|
|
558
|
+
* oldest waiting entry when nothing is admitted; `none` admits nothing.
|
|
559
|
+
*
|
|
560
|
+
* @param entries - the ledger.
|
|
561
|
+
* @param ctx - capacity, pressure share and clock.
|
|
562
|
+
* @returns the ledger with admissions applied (same order).
|
|
563
|
+
*
|
|
564
|
+
* @example
|
|
565
|
+
* ```ts
|
|
566
|
+
* schedulePass(entries, { capacityBytes: 36 * GIB, share: 'full', nowMs: Date.now() });
|
|
567
|
+
* ```
|
|
568
|
+
*/
|
|
569
|
+
export function schedulePass(entries, ctx) {
|
|
570
|
+
const lightShare = ctx.lightShare ?? ctx.share;
|
|
571
|
+
if (ctx.share === 'none' && lightShare === 'none')
|
|
572
|
+
return [...entries];
|
|
573
|
+
const reservationMs = ctx.reservationMs ?? LEDGER_RESERVATION_MS;
|
|
574
|
+
const bytesFor = (share) => share === 'full' || share === 'one'
|
|
575
|
+
? ctx.capacityBytes
|
|
576
|
+
: share === 'half'
|
|
577
|
+
? ctx.capacityBytes / 2
|
|
578
|
+
: 0;
|
|
579
|
+
// Light runs see the memory budget. Heavy runs see the narrower of it and
|
|
580
|
+
// their own share; `one` admits a single heavy run at a time.
|
|
581
|
+
const lightBudget = bytesFor(lightShare);
|
|
582
|
+
const heavyBudget = Math.min(lightBudget, bytesFor(ctx.share));
|
|
583
|
+
let used = ctx.foreign?.bytes ?? 0;
|
|
584
|
+
let running = ctx.foreign?.count ?? 0;
|
|
585
|
+
// Conservative: an entry of an unknown format counts as heavy under `one`.
|
|
586
|
+
let heavyRunning = ctx.foreign?.count ?? 0;
|
|
587
|
+
// T13237: admitted exclusive runs (a full-build holds the machine-wide slot).
|
|
588
|
+
let exclusiveRunning = 0;
|
|
589
|
+
for (const e of entries) {
|
|
590
|
+
if (e.state === 'admitted') {
|
|
591
|
+
used += charged(e, ctx.capacityBytes);
|
|
592
|
+
running++;
|
|
593
|
+
if (isHeavy(e))
|
|
594
|
+
heavyRunning++;
|
|
595
|
+
if (e.exclusive === true)
|
|
596
|
+
exclusiveRunning++;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
const fits = (w, cost) => {
|
|
600
|
+
if (w.exclusive === true && exclusiveRunning > 0)
|
|
601
|
+
return false;
|
|
602
|
+
if (!isHeavy(w))
|
|
603
|
+
return lightShare !== 'none' && used + cost <= lightBudget;
|
|
604
|
+
if (ctx.share === 'none')
|
|
605
|
+
return false;
|
|
606
|
+
if (ctx.share === 'one' && heavyRunning > 0)
|
|
607
|
+
return false;
|
|
608
|
+
return used + cost <= heavyBudget;
|
|
609
|
+
};
|
|
610
|
+
const waiting = entries
|
|
611
|
+
.filter((e) => e.state === 'waiting')
|
|
612
|
+
.sort((a, b) => a.enqueuedAtMs - b.enqueuedAtMs || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
613
|
+
const admit = new Set();
|
|
614
|
+
let blocked = false;
|
|
615
|
+
// Bytes held for a reserved heavy head that only the `one` rule blocks: light
|
|
616
|
+
// runs may still pass it, but only within what is left after its share.
|
|
617
|
+
let reservedForHead = null;
|
|
618
|
+
/** Admit `w` (every admission goes through here, so the counters stay true). */
|
|
619
|
+
const take = (w, cost) => {
|
|
620
|
+
admit.add(w.id);
|
|
621
|
+
used += cost;
|
|
622
|
+
running++;
|
|
623
|
+
if (isHeavy(w))
|
|
624
|
+
heavyRunning++;
|
|
625
|
+
if (w.exclusive === true)
|
|
626
|
+
exclusiveRunning++;
|
|
627
|
+
};
|
|
628
|
+
for (const w of waiting) {
|
|
629
|
+
const cost = charged(w, ctx.capacityBytes);
|
|
630
|
+
if (reservedForHead !== null) {
|
|
631
|
+
// Only light runs pass a reserved head, within what its share leaves —
|
|
632
|
+
// and never a second exclusive run (T13237, #1899 review HIGH: a full
|
|
633
|
+
// build is "light" at exactly HEAVY_FOOTPRINT_BYTES on a 16 GiB host).
|
|
634
|
+
if (!isHeavy(w) &&
|
|
635
|
+
!(w.exclusive === true && exclusiveRunning > 0) &&
|
|
636
|
+
lightShare !== 'none' &&
|
|
637
|
+
used + cost + reservedForHead <= lightBudget) {
|
|
638
|
+
take(w, cost);
|
|
639
|
+
}
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
const share = isHeavy(w) ? ctx.share : lightShare;
|
|
643
|
+
// With nothing running, the oldest waiting run always starts (it is
|
|
644
|
+
// charged at most the capacity), so pressure narrows but never stops work;
|
|
645
|
+
// only the memory gate (`none`) stops it.
|
|
646
|
+
if (fits(w, cost) || (running === 0 && share !== 'none')) {
|
|
647
|
+
take(w, cost);
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
if (!blocked) {
|
|
651
|
+
blocked = true;
|
|
652
|
+
if (ctx.nowMs - w.enqueuedAtMs >= reservationMs) {
|
|
653
|
+
// A heavy head blocked by bytes stops everything behind it. One blocked
|
|
654
|
+
// only by CPU saturation (`one`) would starve light runs that cannot
|
|
655
|
+
// delay it, so its bytes are reserved and light runs keep backfilling
|
|
656
|
+
// around them (T13132, #1865 review MED-1).
|
|
657
|
+
const cpuOnly = isHeavy(w) && ctx.share === 'one' && heavyRunning > 0 && used + cost <= heavyBudget;
|
|
658
|
+
// Likewise a head blocked only because another exclusive run holds
|
|
659
|
+
// the slot (T13237): reserve its bytes, let light runs pass around it.
|
|
660
|
+
const exclusiveOnly = w.exclusive === true &&
|
|
661
|
+
exclusiveRunning > 0 &&
|
|
662
|
+
used + cost <= (isHeavy(w) ? heavyBudget : lightBudget);
|
|
663
|
+
if (!cpuOnly && !exclusiveOnly)
|
|
664
|
+
break;
|
|
665
|
+
reservedForHead = cost;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return entries.map((e) => admit.has(e.id)
|
|
670
|
+
? { ...e, state: 'admitted', admittedAtMs: ctx.nowMs, heartbeatAtMs: ctx.nowMs }
|
|
671
|
+
: e);
|
|
672
|
+
}
|
|
673
|
+
/** Whether an entry is a heavy (multi-process) run: above {@link HEAVY_FOOTPRINT_BYTES}. */
|
|
674
|
+
function isHeavy(e) {
|
|
675
|
+
return e.footprintBytes > HEAVY_FOOTPRINT_BYTES;
|
|
676
|
+
}
|
|
677
|
+
// ---------------------------------------------------------------------------
|
|
678
|
+
// Re-entrancy
|
|
679
|
+
// ---------------------------------------------------------------------------
|
|
680
|
+
/**
|
|
681
|
+
* Split a {@link ADMISSION_ENV} value into its id and nonce, or `null`.
|
|
682
|
+
*
|
|
683
|
+
* @param value - the env value.
|
|
684
|
+
*/
|
|
685
|
+
export function parseAdmissionToken(value) {
|
|
686
|
+
if (!value)
|
|
687
|
+
return null;
|
|
688
|
+
const at = value.lastIndexOf('.');
|
|
689
|
+
if (at <= 0 || at === value.length - 1)
|
|
690
|
+
return null;
|
|
691
|
+
return { id: value.slice(0, at), nonce: value.slice(at + 1) };
|
|
692
|
+
}
|
|
693
|
+
const systemProcessFacts = {
|
|
694
|
+
ancestorsOf: processAncestors,
|
|
695
|
+
groupOf: processGroupOf,
|
|
696
|
+
startedAt: (pid) => systemPidProbe.startedAt(pid),
|
|
697
|
+
};
|
|
698
|
+
/**
|
|
699
|
+
* The admitted entry whose process tree `pid` belongs to, or `null`.
|
|
700
|
+
*
|
|
701
|
+
* Proof is ancestry: the holder is an ancestor of `pid`, with its recorded
|
|
702
|
+
* start time matching. A holder recorded without a start time (`ps` failed)
|
|
703
|
+
* proves nothing by ancestry alone, since its pid may have been recycled into
|
|
704
|
+
* an ancestor: it also needs the token to name it (T13188). The token in `env`
|
|
705
|
+
* only narrows which entry is checked
|
|
706
|
+
* first; a missing or foreign token falls back to checking every admitted
|
|
707
|
+
* entry by ancestry, so a wrapper that scrubs the environment cannot deadlock
|
|
708
|
+
* a nested run — except under a holder recorded without a start time, which
|
|
709
|
+
* needs the token: such a nested run waits for its own parent until it times
|
|
710
|
+
* out or the entry is reaped as unidentifiable ({@link LEDGER_ORPHAN_MS}). A
|
|
711
|
+
* rare case (`ps` failed at admission), and safer than letting a recycled pid
|
|
712
|
+
* ride a dead grant.
|
|
713
|
+
*
|
|
714
|
+
* Group membership (`pid` runs in a tool process group the holder started,
|
|
715
|
+
* which covers a tool that outlived a killed holder) is accepted only for the
|
|
716
|
+
* entry the token names on this host: a process group id carries no start
|
|
717
|
+
* time, so a recycled group id alone proves nothing.
|
|
718
|
+
*
|
|
719
|
+
* @param entries - the ledger.
|
|
720
|
+
* @param pid - the requesting process.
|
|
721
|
+
* @param env - its environment.
|
|
722
|
+
* @param facts - process probes (tests inject them).
|
|
723
|
+
*/
|
|
724
|
+
export function enclosingGrant(entries, pid, env, facts = systemProcessFacts) {
|
|
725
|
+
const host = hostname();
|
|
726
|
+
// No host filter for ancestry: an ancestor is local by definition, and a
|
|
727
|
+
// laptop's hostname follows the network (LOW-4 of the #1829 review).
|
|
728
|
+
const admitted = entries.filter((e) => e.state === 'admitted' && e.pid !== pid);
|
|
729
|
+
if (admitted.length === 0)
|
|
730
|
+
return null;
|
|
731
|
+
const token = parseAdmissionToken(env[ADMISSION_ENV]);
|
|
732
|
+
const hinted = token ? admitted.filter((e) => e.id === token.id && e.nonce === token.nonce) : [];
|
|
733
|
+
const ordered = [...hinted, ...admitted.filter((e) => !hinted.includes(e))];
|
|
734
|
+
let ancestors;
|
|
735
|
+
let group;
|
|
736
|
+
for (const e of ordered) {
|
|
737
|
+
ancestors ??= facts.ancestorsOf(pid);
|
|
738
|
+
group ??= facts.groupOf(pid);
|
|
739
|
+
const byAncestry = ancestors?.includes(e.pid) === true &&
|
|
740
|
+
(e.startedAt === null ? hinted.includes(e) : facts.startedAt(e.pid) === e.startedAt);
|
|
741
|
+
const byGroup = e.host === host &&
|
|
742
|
+
hinted.includes(e) &&
|
|
743
|
+
group !== null &&
|
|
744
|
+
isProbeableId(group) &&
|
|
745
|
+
e.toolGroups.includes(group);
|
|
746
|
+
if (!byAncestry && !byGroup)
|
|
747
|
+
continue;
|
|
748
|
+
// A known start time that differs means a recycled pid: never this holder.
|
|
749
|
+
if (ancestors?.includes(e.pid) === true && e.startedAt !== null && !byAncestry)
|
|
750
|
+
continue;
|
|
751
|
+
return e;
|
|
752
|
+
}
|
|
753
|
+
return null;
|
|
754
|
+
}
|
|
755
|
+
/** The {@link ADMISSION_ENV} value of an entry. */
|
|
756
|
+
export function admissionToken(entry) {
|
|
757
|
+
return `${entry.id}.${entry.nonce}`;
|
|
758
|
+
}
|
|
759
|
+
// ---------------------------------------------------------------------------
|
|
760
|
+
// Reports
|
|
761
|
+
// ---------------------------------------------------------------------------
|
|
762
|
+
function gib(bytes) {
|
|
763
|
+
return `${(bytes / GIB).toFixed(bytes < 10 * GIB ? 1 : 0)} GiB`;
|
|
764
|
+
}
|
|
765
|
+
function age(ms) {
|
|
766
|
+
const s = Math.max(0, Math.round(ms / 1000));
|
|
767
|
+
return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m ${String(s % 60).padStart(2, '0')}s`;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* ` [scope=full, task T1043]` for an entry that says what it covers, else ''.
|
|
771
|
+
*
|
|
772
|
+
* @param e - a ledger entry.
|
|
773
|
+
*/
|
|
774
|
+
export function describeScope(e) {
|
|
775
|
+
const parts = [
|
|
776
|
+
...(e.scope !== undefined ? [`scope=${e.scope}`] : []),
|
|
777
|
+
...(e.task !== undefined ? [`task ${e.task}`] : []),
|
|
778
|
+
];
|
|
779
|
+
return parts.length > 0 ? ` [${parts.join(', ')}]` : '';
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* One line per admitted run: label, scope and task, pid, command, directory and age.
|
|
783
|
+
*
|
|
784
|
+
* @param entries - the ledger.
|
|
785
|
+
* @param nowMs - the clock.
|
|
786
|
+
*/
|
|
787
|
+
export function describeHolders(entries, nowMs) {
|
|
788
|
+
return entries
|
|
789
|
+
.filter((e) => e.state === 'admitted')
|
|
790
|
+
.map((e) => `${e.label}${describeScope(e)} pid ${e.pid} (${e.command})${e.cwd ? ` in ${e.cwd}` : ''}, ${gib(e.footprintBytes)}, for ${age(nowMs - (e.admittedAtMs ?? e.enqueuedAtMs))}`);
|
|
791
|
+
}
|
|
792
|
+
/** Commands too generic to name a lock-taking wrapper. */
|
|
793
|
+
const GENERIC_PROGRAMS = new Set([
|
|
794
|
+
'node',
|
|
795
|
+
'cleo',
|
|
796
|
+
'ct',
|
|
797
|
+
'sh',
|
|
798
|
+
'bash',
|
|
799
|
+
'zsh',
|
|
800
|
+
'env',
|
|
801
|
+
'nice',
|
|
802
|
+
'nohup',
|
|
803
|
+
'time',
|
|
804
|
+
'npm',
|
|
805
|
+
'pnpm',
|
|
806
|
+
'npx',
|
|
807
|
+
'yarn',
|
|
808
|
+
'bun',
|
|
809
|
+
'vitest',
|
|
810
|
+
'tsc',
|
|
811
|
+
'login',
|
|
812
|
+
'tmux',
|
|
813
|
+
'claude',
|
|
814
|
+
]);
|
|
815
|
+
/** The program a command line runs: the first argument that is not an interpreter or a flag. */
|
|
816
|
+
function programOf(command) {
|
|
817
|
+
for (const token of command.trim().split(/\s+/)) {
|
|
818
|
+
if (token.startsWith('-') || token.includes('='))
|
|
819
|
+
continue;
|
|
820
|
+
const base = token.split('/').pop() ?? token;
|
|
821
|
+
if (/^(node|bash|sh|zsh|env|nice|nohup|time|python3?)$/.test(base))
|
|
822
|
+
continue;
|
|
823
|
+
return base;
|
|
824
|
+
}
|
|
825
|
+
return null;
|
|
826
|
+
}
|
|
827
|
+
/** `ps -A -o pid=,ppid=,command=`, or `null` when `ps` fails. */
|
|
828
|
+
function processTable() {
|
|
829
|
+
let out;
|
|
830
|
+
try {
|
|
831
|
+
out = execFileSync('/bin/ps', ['-A', '-o', 'pid=,ppid=,command='], {
|
|
832
|
+
encoding: 'utf-8',
|
|
833
|
+
timeout: 2000,
|
|
834
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
835
|
+
env: { ...process.env, LC_ALL: 'C' },
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
catch {
|
|
839
|
+
return null;
|
|
840
|
+
}
|
|
841
|
+
const table = new Map();
|
|
842
|
+
for (const line of out.split('\n')) {
|
|
843
|
+
const m = /^\s*(\d+)\s+(\d+)\s+(.*)$/.exec(line);
|
|
844
|
+
if (m?.[1] && m[2])
|
|
845
|
+
table.set(Number(m[1]), { ppid: Number(m[2]), command: m[3] ?? '' });
|
|
846
|
+
}
|
|
847
|
+
return table;
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* A suspected wait cycle through a lock outside CLEO: a holder's process tree
|
|
851
|
+
* and this waiter's ancestry run the same (non-generic) program, such as a
|
|
852
|
+
* wrapper script that takes its own lock. `null` when nothing points to one.
|
|
853
|
+
*
|
|
854
|
+
* @param pid - the waiting process.
|
|
855
|
+
* @param holders - admitted entries.
|
|
856
|
+
* @param table - the process table (`pid → {ppid, command}`).
|
|
857
|
+
*/
|
|
858
|
+
export function suspectCycle(pid, holders, table) {
|
|
859
|
+
const ours = new Map();
|
|
860
|
+
for (let at = table.get(pid)?.ppid, n = 0; at !== undefined && at > 1 && n < 64; n++) {
|
|
861
|
+
const row = table.get(at);
|
|
862
|
+
if (!row)
|
|
863
|
+
break;
|
|
864
|
+
const prog = programOf(row.command);
|
|
865
|
+
if (prog && !GENERIC_PROGRAMS.has(prog) && !ours.has(prog))
|
|
866
|
+
ours.set(prog, at);
|
|
867
|
+
at = row.ppid;
|
|
868
|
+
}
|
|
869
|
+
if (ours.size === 0)
|
|
870
|
+
return null;
|
|
871
|
+
const children = new Map();
|
|
872
|
+
for (const [child, row] of table) {
|
|
873
|
+
const list = children.get(row.ppid) ?? [];
|
|
874
|
+
list.push(child);
|
|
875
|
+
children.set(row.ppid, list);
|
|
876
|
+
}
|
|
877
|
+
for (const h of holders) {
|
|
878
|
+
const stack = [...(children.get(h.pid) ?? [])];
|
|
879
|
+
const seen = new Set();
|
|
880
|
+
while (stack.length > 0) {
|
|
881
|
+
const at = stack.pop();
|
|
882
|
+
if (seen.has(at) || seen.size > 4096)
|
|
883
|
+
continue;
|
|
884
|
+
seen.add(at);
|
|
885
|
+
const prog = programOf(table.get(at)?.command ?? '');
|
|
886
|
+
const mine = prog ? ours.get(prog) : undefined;
|
|
887
|
+
if (prog && mine !== undefined) {
|
|
888
|
+
return (`suspected wait cycle: holder pid ${h.pid} (${h.label}) runs ${prog} (pid ${at}), and this ` +
|
|
889
|
+
`waiter runs under ${prog} (pid ${mine}). If ${prog} takes a lock of its own, neither can ` +
|
|
890
|
+
`proceed: run heavy work under one of them, not both.`);
|
|
891
|
+
}
|
|
892
|
+
stack.push(...(children.get(at) ?? []));
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
return null;
|
|
896
|
+
}
|
|
897
|
+
/** Filesystem errors meaning the ledger cannot be kept here. */
|
|
898
|
+
const STATE_IO_CODES = new Set([
|
|
899
|
+
'EACCES',
|
|
900
|
+
'EPERM',
|
|
901
|
+
'EROFS',
|
|
902
|
+
'ENOSPC',
|
|
903
|
+
'EDQUOT',
|
|
904
|
+
'ENOTDIR',
|
|
905
|
+
'ELOOP',
|
|
906
|
+
// A ledger written by a newer CLEO: never rewritten, so admission runs ungoverned.
|
|
907
|
+
'E_LEDGER_VERSION',
|
|
908
|
+
]);
|
|
909
|
+
/**
|
|
910
|
+
* The code and path of an error that means "admission state cannot be kept
|
|
911
|
+
* here" (a sandbox that blocks writes to the CLEO home, a read-only or full
|
|
912
|
+
* disk, a broken CLEO home), or `null` for any other error, which is a bug and
|
|
913
|
+
* must not be hidden.
|
|
914
|
+
*
|
|
915
|
+
* @param err - a caught error.
|
|
916
|
+
*/
|
|
917
|
+
export function admissionIoError(err) {
|
|
918
|
+
const e = err;
|
|
919
|
+
if (!e || typeof e !== 'object' || typeof e.code !== 'string')
|
|
920
|
+
return null;
|
|
921
|
+
if (!STATE_IO_CODES.has(e.code))
|
|
922
|
+
return null;
|
|
923
|
+
return { code: e.code, path: typeof e.path === 'string' ? e.path : null };
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Why a run is ungoverned, in words: `admission state is not writable (EACCES
|
|
927
|
+
* /path)`, or, for a ledger a newer CLEO wrote, that this CLEO will not
|
|
928
|
+
* rewrite it. Callers append what they do about it.
|
|
929
|
+
*
|
|
930
|
+
* @param io - from {@link admissionIoError} or a grant's `ungoverned`.
|
|
931
|
+
*/
|
|
932
|
+
export function describeAdmissionIoError(io) {
|
|
933
|
+
const at = io.path ? ` ${io.path}` : '';
|
|
934
|
+
return io.code === 'E_LEDGER_VERSION'
|
|
935
|
+
? `the admission ledger${at} was written by a newer CLEO, which this one never rewrites (upgrade CLEO to share the machine budget)`
|
|
936
|
+
: `admission state is not writable (${io.code}${at})`;
|
|
937
|
+
}
|
|
938
|
+
function passThrough(token, nested, ungoverned) {
|
|
939
|
+
return {
|
|
940
|
+
id: null,
|
|
941
|
+
token,
|
|
942
|
+
nested,
|
|
943
|
+
ungoverned,
|
|
944
|
+
waitedMs: 0,
|
|
945
|
+
footprintBytes: 0,
|
|
946
|
+
release: async () => { },
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
function newId(pid, nowMs) {
|
|
950
|
+
return `${pid}-${nowMs}-${randomBytes(3).toString('hex')}`;
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Sample pressure and ask the memory gate, outside the critical section. A
|
|
954
|
+
* verdict another process wrote to the ledger less than
|
|
955
|
+
* {@link PRESSURE_REUSE_MS} ago is reused instead, unless `opts.sample` is
|
|
956
|
+
* given, so a crowd of waiters costs one sample per interval, not one each.
|
|
957
|
+
*/
|
|
958
|
+
async function sampleShare(opts, cached, nowMs) {
|
|
959
|
+
if (!opts.sample) {
|
|
960
|
+
if (process.env[ADMISSION_PRESSURE_ENV] === 'off') {
|
|
961
|
+
return { share: 'full', lightShare: 'full', reading: null, sampled: null };
|
|
962
|
+
}
|
|
963
|
+
if (cached !== null && isFresh(cached, nowMs)) {
|
|
964
|
+
return {
|
|
965
|
+
share: cached.share,
|
|
966
|
+
lightShare: cached.lightShare ?? cached.share,
|
|
967
|
+
reading: cached.reading,
|
|
968
|
+
sampled: null,
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
let sample;
|
|
973
|
+
try {
|
|
974
|
+
sample = await (opts.sample ?? (() => new ResourceMonitor().sample()))();
|
|
975
|
+
}
|
|
976
|
+
catch {
|
|
977
|
+
sample = null;
|
|
978
|
+
}
|
|
979
|
+
if (sample === null)
|
|
980
|
+
return { share: 'full', lightShare: 'full', reading: null, sampled: null };
|
|
981
|
+
const gate = checkMemoryGate(sample, opts.now ? { now: opts.now } : {});
|
|
982
|
+
const share = budgetShare(sample, gate.refuse);
|
|
983
|
+
const lightShare = lightBudgetShare(sample, gate.refuse);
|
|
984
|
+
const reading = gate.refuse ? gate.reading : null;
|
|
985
|
+
return {
|
|
986
|
+
share,
|
|
987
|
+
lightShare,
|
|
988
|
+
reading,
|
|
989
|
+
sampled: { share, lightShare, reading, sampledAtMs: nowMs },
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
function isFresh(cached, nowMs) {
|
|
993
|
+
const age = nowMs - cached.sampledAtMs;
|
|
994
|
+
return age >= 0 && age < PRESSURE_REUSE_MS;
|
|
995
|
+
}
|
|
996
|
+
/** The provably finished entries of a ledger, probed outside the critical section. */
|
|
997
|
+
function findDead(doc, nowMs, probe) {
|
|
998
|
+
const entries = new Map();
|
|
999
|
+
for (const e of doc.entries) {
|
|
1000
|
+
if (entryLiveness(e, nowMs, probe) === 'dead')
|
|
1001
|
+
entries.set(e.id, e.heartbeatAtMs);
|
|
1002
|
+
}
|
|
1003
|
+
const foreign = new Set();
|
|
1004
|
+
for (const f of doc.foreign) {
|
|
1005
|
+
if (foreignLiveness(f, nowMs, probe) === 'dead')
|
|
1006
|
+
foreign.add(JSON.stringify(f));
|
|
1007
|
+
}
|
|
1008
|
+
return { entries, foreign };
|
|
1009
|
+
}
|
|
1010
|
+
const NO_DEAD = { entries: new Map(), foreign: new Set() };
|
|
1011
|
+
/** The budget foreign entries take, counted as running. */
|
|
1012
|
+
function foreignLoad(foreign, capacityBytes) {
|
|
1013
|
+
let bytes = 0;
|
|
1014
|
+
for (const f of foreign)
|
|
1015
|
+
bytes += foreignCharge(f, capacityBytes);
|
|
1016
|
+
return { bytes, count: foreign.length };
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* Drop the dead (unless they heartbeated since the probe), charge foreign
|
|
1020
|
+
* entries, schedule, and keep the freshest pressure verdict.
|
|
1021
|
+
*/
|
|
1022
|
+
function reapAndSchedule(doc, dead, ctx, sampled) {
|
|
1023
|
+
const live = doc.entries.filter((e) => dead.entries.get(e.id) !== e.heartbeatAtMs);
|
|
1024
|
+
const foreign = doc.foreign.filter((f) => !dead.foreign.has(JSON.stringify(f)));
|
|
1025
|
+
return {
|
|
1026
|
+
...doc,
|
|
1027
|
+
entries: schedulePass(live, { ...ctx, foreign: foreignLoad(foreign, ctx.capacityBytes) }),
|
|
1028
|
+
foreign,
|
|
1029
|
+
pressure: sampled ?? doc.pressure,
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Ask the ledger for a share of the machine budget.
|
|
1034
|
+
*
|
|
1035
|
+
* `CLEO_RESOURCES_MODE=off` admits everything. Re-entrant: inside an admitted
|
|
1036
|
+
* run's process tree it returns a pass-through grant at once. Otherwise it joins the FIFO queue; with `wait: false` it
|
|
1037
|
+
* leaves again unless admitted on the spot, and with `wait: true` it waits
|
|
1038
|
+
* (reporting memory-pressure waits and, after a minute, the holders and any
|
|
1039
|
+
* suspected cycle) until admitted or `timeoutMs`. Never throws for admission
|
|
1040
|
+
* control. An unwritable ledger, or one written by a newer CLEO, is a
|
|
1041
|
+
* pass-through grant with `ungoverned` set (callers say so with
|
|
1042
|
+
* {@link describeAdmissionIoError}); any other error propagates.
|
|
1043
|
+
*
|
|
1044
|
+
* @param req - what is asked for.
|
|
1045
|
+
* @param opts - waiting and reporting.
|
|
1046
|
+
*
|
|
1047
|
+
* @example
|
|
1048
|
+
* ```ts
|
|
1049
|
+
* const out = await admit({ label: 'tool:test', footprintBytes: footprintForTool('test') }, { wait: true });
|
|
1050
|
+
* if (out.admitted) {
|
|
1051
|
+
* try { await run({ env: { [ADMISSION_ENV]: out.grant.token } }); } finally { await out.grant.release(); }
|
|
1052
|
+
* }
|
|
1053
|
+
* ```
|
|
1054
|
+
*/
|
|
1055
|
+
export async function admit(req, opts) {
|
|
1056
|
+
// The governor's off switch turns admission off here too.
|
|
1057
|
+
if (process.env.CLEO_RESOURCES_MODE === 'off') {
|
|
1058
|
+
return { admitted: true, grant: passThrough('', false, null) };
|
|
1059
|
+
}
|
|
1060
|
+
try {
|
|
1061
|
+
return await admitInner(req, opts);
|
|
1062
|
+
}
|
|
1063
|
+
catch (err) {
|
|
1064
|
+
const io = admissionIoError(err);
|
|
1065
|
+
if (io === null)
|
|
1066
|
+
throw err;
|
|
1067
|
+
return { admitted: true, grant: passThrough('', false, io) };
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
async function admitInner(req, opts) {
|
|
1071
|
+
const now = opts.now ?? Date.now;
|
|
1072
|
+
const sleep = opts.sleep ??
|
|
1073
|
+
((ms) => new Promise((resolve) => {
|
|
1074
|
+
setTimeout(resolve, ms);
|
|
1075
|
+
}));
|
|
1076
|
+
const dir = opts.dir ?? admissionDir();
|
|
1077
|
+
const pid = opts.pid ?? process.pid;
|
|
1078
|
+
const env = opts.env ?? process.env;
|
|
1079
|
+
const probe = opts.probe ?? systemPidProbe;
|
|
1080
|
+
const capacityBytes = opts.capacityBytes ?? admissionCapacityBytes();
|
|
1081
|
+
const timeoutMs = opts.timeoutMs ?? 3_600_000;
|
|
1082
|
+
const t0 = now();
|
|
1083
|
+
// Re-entrant: a request inside an admitted run's tree rides its grant.
|
|
1084
|
+
const parent = enclosingGrant(readLedger(dir), pid, env, opts.facts);
|
|
1085
|
+
if (parent !== null) {
|
|
1086
|
+
return { admitted: true, grant: passThrough(admissionToken(parent), true, null) };
|
|
1087
|
+
}
|
|
1088
|
+
const entry = {
|
|
1089
|
+
id: newId(pid, t0),
|
|
1090
|
+
nonce: randomBytes(12).toString('hex'),
|
|
1091
|
+
pid,
|
|
1092
|
+
host: hostname(),
|
|
1093
|
+
startedAt: pid === process.pid ? ownProcessStartedAt() : probe.startedAt(pid),
|
|
1094
|
+
label: req.label,
|
|
1095
|
+
command: req.command ?? process.argv.slice(1).join(' '),
|
|
1096
|
+
cwd: req.cwd === undefined ? process.cwd() : req.cwd, // CWD-OK: display only — where the run was started, shown in status and reports
|
|
1097
|
+
footprintBytes: Math.max(0, req.footprintBytes),
|
|
1098
|
+
state: 'waiting',
|
|
1099
|
+
enqueuedAtMs: t0,
|
|
1100
|
+
admittedAtMs: null,
|
|
1101
|
+
heartbeatAtMs: t0,
|
|
1102
|
+
toolGroups: [],
|
|
1103
|
+
...(req.scope !== undefined ? { scope: req.scope } : {}),
|
|
1104
|
+
...(req.task !== undefined ? { task: req.task } : {}),
|
|
1105
|
+
...(req.exclusive === true ? { exclusive: true } : {}),
|
|
1106
|
+
};
|
|
1107
|
+
/** One pass: reap, (re-)enqueue ourselves, schedule; returns our entry's state. */
|
|
1108
|
+
const pass = async () => {
|
|
1109
|
+
const before = readLedgerDoc(dir);
|
|
1110
|
+
const nowMs = now();
|
|
1111
|
+
const { share, lightShare, reading, sampled } = await sampleShare(opts, before.pressure, nowMs);
|
|
1112
|
+
const dead = findDead(before, nowMs, probe);
|
|
1113
|
+
return withLedger(dir, (doc) => {
|
|
1114
|
+
let entries = doc.entries;
|
|
1115
|
+
const at = entries.findIndex((e) => e.id === entry.id);
|
|
1116
|
+
if (at < 0)
|
|
1117
|
+
entries = [...entries, { ...entry, heartbeatAtMs: nowMs }];
|
|
1118
|
+
else if (entries[at]?.state === 'waiting') {
|
|
1119
|
+
entries = entries.map((e) => (e.id === entry.id ? { ...e, heartbeatAtMs: nowMs } : e));
|
|
1120
|
+
}
|
|
1121
|
+
const next = reapAndSchedule({ ...doc, entries }, dead, { capacityBytes, share, lightShare, nowMs }, sampled);
|
|
1122
|
+
return {
|
|
1123
|
+
doc: next,
|
|
1124
|
+
result: { mine: next.entries.find((e) => e.id === entry.id) ?? null, doc: next, reading },
|
|
1125
|
+
};
|
|
1126
|
+
});
|
|
1127
|
+
};
|
|
1128
|
+
const leave = async () => withLedger(dir, (doc) => {
|
|
1129
|
+
const mine = doc.entries.find((e) => e.id === entry.id) ?? null;
|
|
1130
|
+
if (mine?.state === 'admitted')
|
|
1131
|
+
return { doc: null, result: mine };
|
|
1132
|
+
return {
|
|
1133
|
+
doc: { ...doc, entries: doc.entries.filter((e) => e.id !== entry.id) },
|
|
1134
|
+
result: null,
|
|
1135
|
+
};
|
|
1136
|
+
});
|
|
1137
|
+
const refusal = (doc, reading) => {
|
|
1138
|
+
const nowMs = now();
|
|
1139
|
+
const ahead = doc.entries.filter((e) => e.state === 'waiting' && e.id !== entry.id && e.enqueuedAtMs <= entry.enqueuedAtMs).length;
|
|
1140
|
+
const load = budgetLoad(doc, capacityBytes);
|
|
1141
|
+
const exclusiveHolder = entry.exclusive === true
|
|
1142
|
+
? doc.entries.find((e) => e.state === 'admitted' && e.exclusive === true && e.id !== entry.id)
|
|
1143
|
+
: undefined;
|
|
1144
|
+
const reason = reading !== null
|
|
1145
|
+
? `memory pressure ${reading.score} (refused above ${reading.refuseAbove}, resumes at ` +
|
|
1146
|
+
`${reading.resumeAtOrBelow} or below): ${reading.summary}`
|
|
1147
|
+
: exclusiveHolder !== undefined
|
|
1148
|
+
? `another full build holds the machine-wide full-build slot (${exclusiveHolder.label} pid ` +
|
|
1149
|
+
`${exclusiveHolder.pid}); one runs at a time, whatever its size${ahead > 0 ? `; ${ahead} waiting ahead` : ''}`
|
|
1150
|
+
: `machine budget in use: ${gib(load.bytes)} of ${gib(capacityBytes)} by ${load.runs} run(s); ` +
|
|
1151
|
+
`this run needs ${gib(charged(entry, capacityBytes))}${ahead > 0 ? `; ${ahead} waiting ahead` : ''}`;
|
|
1152
|
+
return {
|
|
1153
|
+
reason,
|
|
1154
|
+
retryAfterMs: reading !== null ? MEMORY_GATE_RETRY_AFTER_MS : 2_000,
|
|
1155
|
+
memoryPressure: reading,
|
|
1156
|
+
ahead,
|
|
1157
|
+
holders: describeLoad(doc, nowMs),
|
|
1158
|
+
};
|
|
1159
|
+
};
|
|
1160
|
+
let first = await pass();
|
|
1161
|
+
if (first.mine?.state === 'admitted') {
|
|
1162
|
+
return { admitted: true, grant: holdGrant(first.mine, dir, now() - t0, opts) };
|
|
1163
|
+
}
|
|
1164
|
+
if (!opts.wait) {
|
|
1165
|
+
const raced = await leave();
|
|
1166
|
+
if (raced !== null)
|
|
1167
|
+
return { admitted: true, grant: holdGrant(raced, dir, now() - t0, opts) };
|
|
1168
|
+
return { admitted: false, refusal: refusal(first.doc, first.reading) };
|
|
1169
|
+
}
|
|
1170
|
+
let lastPassAt = now();
|
|
1171
|
+
let lastReportAt = t0;
|
|
1172
|
+
let reading = first.reading;
|
|
1173
|
+
for (;;) {
|
|
1174
|
+
if (reading !== null)
|
|
1175
|
+
opts.memoryPressure?.waiting(reading, now() - t0);
|
|
1176
|
+
if (now() - lastReportAt >= LEDGER_HOLDER_REPORT_MS && opts.notice) {
|
|
1177
|
+
lastReportAt = now();
|
|
1178
|
+
opts.notice(holderReport(first.doc, entry, pid, now() - t0, capacityBytes, now()));
|
|
1179
|
+
}
|
|
1180
|
+
const remaining = timeoutMs - (now() - t0);
|
|
1181
|
+
if (remaining <= 0) {
|
|
1182
|
+
const raced = await leave();
|
|
1183
|
+
if (raced !== null)
|
|
1184
|
+
return { admitted: true, grant: holdGrant(raced, dir, now() - t0, opts) };
|
|
1185
|
+
return { admitted: false, refusal: refusal(first.doc, reading) };
|
|
1186
|
+
}
|
|
1187
|
+
await sleep(Math.min(opts.pollMs ?? READ_POLL_MS, remaining));
|
|
1188
|
+
const seen = readLedgerDoc(dir);
|
|
1189
|
+
const mine = seen.entries.find((e) => e.id === entry.id);
|
|
1190
|
+
if (mine?.state === 'admitted') {
|
|
1191
|
+
opts.memoryPressure?.admitted(now() - t0, null);
|
|
1192
|
+
return { admitted: true, grant: holdGrant(mine, dir, now() - t0, opts) };
|
|
1193
|
+
}
|
|
1194
|
+
if (seen.pressure !== null && isFresh(seen.pressure, now()))
|
|
1195
|
+
reading = seen.pressure.reading;
|
|
1196
|
+
if (passDue(mine === undefined, now() - lastPassAt, seen.pressure, now(), opts)) {
|
|
1197
|
+
lastPassAt = now();
|
|
1198
|
+
first = await pass();
|
|
1199
|
+
reading = first.reading;
|
|
1200
|
+
if (first.mine?.state === 'admitted') {
|
|
1201
|
+
opts.memoryPressure?.admitted(now() - t0, null);
|
|
1202
|
+
return { admitted: true, grant: holdGrant(first.mine, dir, now() - t0, opts) };
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Whether a waiter runs a pass of its own now: its entry is gone (reaped or
|
|
1209
|
+
* never written), its heartbeat is due, or a pass is due and nobody has
|
|
1210
|
+
* sampled pressure lately (a fresh verdict in the ledger means another
|
|
1211
|
+
* process just ran a pass, which scheduled everyone).
|
|
1212
|
+
*/
|
|
1213
|
+
function passDue(missing, sinceLastPassMs, pressure, nowMs, opts) {
|
|
1214
|
+
if (missing || sinceLastPassMs >= LEDGER_HEARTBEAT_MS)
|
|
1215
|
+
return true;
|
|
1216
|
+
if (sinceLastPassMs < PASS_EVERY_MS)
|
|
1217
|
+
return false;
|
|
1218
|
+
// An injected sampler never reads the cache, so it never waits on it either.
|
|
1219
|
+
return opts.sample !== undefined || pressure === null || !isFresh(pressure, nowMs);
|
|
1220
|
+
}
|
|
1221
|
+
/** What the admitted runs and foreign entries take from the budget. */
|
|
1222
|
+
function budgetLoad(doc, capacityBytes) {
|
|
1223
|
+
const admitted = doc.entries.filter((e) => e.state === 'admitted');
|
|
1224
|
+
const foreign = foreignLoad(doc.foreign, capacityBytes);
|
|
1225
|
+
return {
|
|
1226
|
+
bytes: admitted.reduce((n, e) => n + charged(e, capacityBytes), 0) + foreign.bytes,
|
|
1227
|
+
runs: admitted.length + foreign.count,
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
/** {@link describeHolders}, plus a line for entries of another CLEO version. */
|
|
1231
|
+
function describeLoad(doc, nowMs) {
|
|
1232
|
+
const lines = describeHolders(doc.entries, nowMs);
|
|
1233
|
+
if (doc.foreign.length > 0) {
|
|
1234
|
+
lines.push(`${doc.foreign.length} entr${doc.foreign.length === 1 ? 'y' : 'ies'} of another CLEO version`);
|
|
1235
|
+
}
|
|
1236
|
+
return lines;
|
|
1237
|
+
}
|
|
1238
|
+
/** The holder report a long waiter prints. */
|
|
1239
|
+
function holderReport(doc, mine, pid, waitedMs, capacityBytes, nowMs) {
|
|
1240
|
+
const holders = doc.entries.filter((e) => e.state === 'admitted');
|
|
1241
|
+
const used = budgetLoad(doc, capacityBytes).bytes;
|
|
1242
|
+
const ahead = doc.entries.filter((e) => e.state === 'waiting' && e.id !== mine.id && e.enqueuedAtMs <= mine.enqueuedAtMs).length;
|
|
1243
|
+
const lines = [
|
|
1244
|
+
`still waiting after ${age(waitedMs)} for the machine budget (${gib(used)} of ${gib(capacityBytes)} in use, ` +
|
|
1245
|
+
`${ahead} waiting ahead). Holders: ${describeLoad(doc, nowMs).join('; ') || 'none'}.`,
|
|
1246
|
+
];
|
|
1247
|
+
const table = holders.length > 0 ? processTable() : null;
|
|
1248
|
+
const cycle = table ? suspectCycle(pid, holders, table) : null;
|
|
1249
|
+
if (cycle)
|
|
1250
|
+
lines.push(cycle);
|
|
1251
|
+
return lines.join(' ');
|
|
1252
|
+
}
|
|
1253
|
+
/** First and longest delay between attempts to remove a released entry that failed. */
|
|
1254
|
+
const RELEASE_RETRY_MS = [1_000, LEDGER_HEARTBEAT_MS];
|
|
1255
|
+
/** Hold an admitted entry: heartbeat, follow tool groups, release. */
|
|
1256
|
+
function holdGrant(entry, dir, waitedMs, opts) {
|
|
1257
|
+
const update = (patch) => withLedger(dir, (doc) => {
|
|
1258
|
+
if (!doc.entries.some((e) => e.id === entry.id))
|
|
1259
|
+
return { doc: null, result: undefined };
|
|
1260
|
+
return {
|
|
1261
|
+
doc: { ...doc, entries: doc.entries.map((e) => (e.id === entry.id ? patch(e) : e)) },
|
|
1262
|
+
result: undefined,
|
|
1263
|
+
};
|
|
1264
|
+
}).catch(() => {
|
|
1265
|
+
// Best effort: a missed heartbeat only means the next probe asks ps.
|
|
1266
|
+
});
|
|
1267
|
+
const now = opts.now ?? Date.now;
|
|
1268
|
+
const timer = setInterval(() => {
|
|
1269
|
+
void update((e) => ({ ...e, heartbeatAtMs: now() }));
|
|
1270
|
+
}, LEDGER_HEARTBEAT_MS);
|
|
1271
|
+
timer.unref();
|
|
1272
|
+
const following = followToolGroups((groups) => {
|
|
1273
|
+
void update((e) => ({ ...e, toolGroups: [...groups] }));
|
|
1274
|
+
});
|
|
1275
|
+
if (following.groups.length > 0)
|
|
1276
|
+
void update((e) => ({ ...e, toolGroups: [...following.groups] }));
|
|
1277
|
+
/** Remove the entry and schedule the queue. True when done (or never possible here). */
|
|
1278
|
+
const removeOnce = async () => {
|
|
1279
|
+
try {
|
|
1280
|
+
const before = readLedgerDoc(dir);
|
|
1281
|
+
const nowMs = now();
|
|
1282
|
+
const { share, lightShare, sampled } = await sampleShare(opts, before.pressure, nowMs);
|
|
1283
|
+
const capacityBytes = opts.capacityBytes ?? admissionCapacityBytes();
|
|
1284
|
+
await withLedger(dir, (doc) => ({
|
|
1285
|
+
doc: reapAndSchedule({ ...doc, entries: doc.entries.filter((e) => e.id !== entry.id) }, NO_DEAD, { capacityBytes, share, lightShare, nowMs }, sampled),
|
|
1286
|
+
result: undefined,
|
|
1287
|
+
}));
|
|
1288
|
+
return true;
|
|
1289
|
+
}
|
|
1290
|
+
catch (err) {
|
|
1291
|
+
// State that cannot be written cannot be cleaned either: the entry is
|
|
1292
|
+
// reaped once this process is gone.
|
|
1293
|
+
if (admissionIoError(err) !== null)
|
|
1294
|
+
return true;
|
|
1295
|
+
log().warn({ err: err instanceof Error ? err.message : String(err), id: entry.id }, 'admission release failed; retrying in the background');
|
|
1296
|
+
return false;
|
|
1297
|
+
}
|
|
1298
|
+
};
|
|
1299
|
+
/** Keep trying in the background (never keeping the process alive) until removed. */
|
|
1300
|
+
const retry = (delayMs) => {
|
|
1301
|
+
const t = setTimeout(() => {
|
|
1302
|
+
void removeOnce().then((done) => {
|
|
1303
|
+
if (!done)
|
|
1304
|
+
retry(Math.min(delayMs * 2, RELEASE_RETRY_MS[1]));
|
|
1305
|
+
});
|
|
1306
|
+
}, delayMs);
|
|
1307
|
+
t.unref();
|
|
1308
|
+
};
|
|
1309
|
+
let released = false;
|
|
1310
|
+
return {
|
|
1311
|
+
id: entry.id,
|
|
1312
|
+
token: admissionToken(entry),
|
|
1313
|
+
nested: false,
|
|
1314
|
+
ungoverned: null,
|
|
1315
|
+
waitedMs,
|
|
1316
|
+
footprintBytes: entry.footprintBytes,
|
|
1317
|
+
release: async () => {
|
|
1318
|
+
if (released)
|
|
1319
|
+
return;
|
|
1320
|
+
released = true;
|
|
1321
|
+
clearInterval(timer);
|
|
1322
|
+
following.stop();
|
|
1323
|
+
if (!(await removeOnce()))
|
|
1324
|
+
retry(RELEASE_RETRY_MS[0]);
|
|
1325
|
+
},
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
/** Remove the ledger file (tests). @internal */
|
|
1329
|
+
export function _resetAdmissionLedgerForTest(dir) {
|
|
1330
|
+
try {
|
|
1331
|
+
rmSync(ledgerFile(dir ?? admissionDir()), { force: true });
|
|
1332
|
+
}
|
|
1333
|
+
catch {
|
|
1334
|
+
// Absent already, or no CLEO home to clean.
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
/** The id shown for an entry of an unknown format. */
|
|
1338
|
+
function foreignId(f) {
|
|
1339
|
+
return typeof f.id === 'string' ? f.id : `foreign:${JSON.stringify(f).slice(0, 60)}`;
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Drop every entry whose holder is provably gone, and schedule. For
|
|
1343
|
+
* `cleo doctor tool-locks --reap`; admission does this on every pass.
|
|
1344
|
+
*
|
|
1345
|
+
* @param opts - ledger directory, capacity and probes (tests).
|
|
1346
|
+
* @returns the ids removed (entries of another CLEO version included).
|
|
1347
|
+
*/
|
|
1348
|
+
export async function reapLedger(opts = {}) {
|
|
1349
|
+
const dir = opts.dir ?? admissionDir();
|
|
1350
|
+
const now = opts.now ?? Date.now;
|
|
1351
|
+
const before = readLedgerDoc(dir);
|
|
1352
|
+
const nowMs = now();
|
|
1353
|
+
const dead = findDead(before, nowMs, opts.probe ?? systemPidProbe);
|
|
1354
|
+
if (dead.entries.size === 0 && dead.foreign.size === 0)
|
|
1355
|
+
return [];
|
|
1356
|
+
const { share, lightShare, sampled } = await sampleShare({ wait: false, ...opts }, before.pressure, nowMs);
|
|
1357
|
+
const capacityBytes = opts.capacityBytes ?? admissionCapacityBytes();
|
|
1358
|
+
return withLedger(dir, (doc) => {
|
|
1359
|
+
const removed = [
|
|
1360
|
+
...doc.entries.filter((e) => dead.entries.get(e.id) === e.heartbeatAtMs).map((e) => e.id),
|
|
1361
|
+
...doc.foreign.filter((f) => dead.foreign.has(JSON.stringify(f))).map(foreignId),
|
|
1362
|
+
];
|
|
1363
|
+
return {
|
|
1364
|
+
doc: reapAndSchedule(doc, dead, { capacityBytes, share, lightShare, nowMs }, sampled),
|
|
1365
|
+
result: removed,
|
|
1366
|
+
};
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Remove one entry by id, whatever its liveness, and schedule. For
|
|
1371
|
+
* `cleo doctor tool-locks --remove <id>`, when an entry cannot be proven dead
|
|
1372
|
+
* (its holder is unidentifiable) but the operator knows it is.
|
|
1373
|
+
*
|
|
1374
|
+
* @param id - the entry id (as `cleo doctor tool-locks` lists it).
|
|
1375
|
+
* @param opts - ledger directory, capacity and probes (tests).
|
|
1376
|
+
* @returns whether an entry was removed.
|
|
1377
|
+
*/
|
|
1378
|
+
export async function removeLedgerEntry(id, opts = {}) {
|
|
1379
|
+
const dir = opts.dir ?? admissionDir();
|
|
1380
|
+
const now = opts.now ?? Date.now;
|
|
1381
|
+
const before = readLedgerDoc(dir);
|
|
1382
|
+
if (!before.entries.some((e) => e.id === id) &&
|
|
1383
|
+
!before.foreign.some((f) => foreignId(f) === id)) {
|
|
1384
|
+
return false;
|
|
1385
|
+
}
|
|
1386
|
+
const nowMs = now();
|
|
1387
|
+
const { share, lightShare, sampled } = await sampleShare({ wait: false, ...opts }, before.pressure, nowMs);
|
|
1388
|
+
const capacityBytes = opts.capacityBytes ?? admissionCapacityBytes();
|
|
1389
|
+
return withLedger(dir, (doc) => {
|
|
1390
|
+
const entries = doc.entries.filter((e) => e.id !== id);
|
|
1391
|
+
const foreign = doc.foreign.filter((f) => foreignId(f) !== id);
|
|
1392
|
+
if (entries.length === doc.entries.length && foreign.length === doc.foreign.length) {
|
|
1393
|
+
return { doc: null, result: false };
|
|
1394
|
+
}
|
|
1395
|
+
return {
|
|
1396
|
+
doc: reapAndSchedule({ ...doc, entries, foreign }, NO_DEAD, { capacityBytes, share, lightShare, nowMs }, sampled),
|
|
1397
|
+
result: true,
|
|
1398
|
+
};
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
//# sourceMappingURL=admission-ledger.js.map
|