devcontour-harness 0.0.0-stage → 0.1.0-alpha.6
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/LICENSE +21 -0
- package/README.md +183 -2
- package/docs/architecture.md +473 -0
- package/docs/compatibility-versions.md +56 -0
- package/docs/compatibility.md +192 -0
- package/docs/connectors.md +323 -0
- package/docs/contracts.md +603 -0
- package/docs/integrations/hindsight.md +148 -0
- package/docs/quickstart.md +182 -0
- package/docs/release-notes/0.1.0-alpha.6.md +238 -0
- package/docs/review-probes.md +120 -0
- package/docs/review-protocol.md +251 -0
- package/docs/toolchains.md +167 -0
- package/docs/turbo-starter.md +269 -0
- package/docs/upgrade.md +202 -0
- package/examples/README.md +27 -0
- package/examples/external-connector/README.md +19 -0
- package/examples/external-connector/connector.mjs +55 -0
- package/examples/files-only/README.md +28 -0
- package/examples/files-only/context/norms/rules.md +4 -0
- package/examples/files-only/graph.json +23 -0
- package/examples/files-only/policy.json +30 -0
- package/examples/one-vendor/README.md +19 -0
- package/examples/one-vendor/policy.json +27 -0
- package/examples/single-task/README.md +18 -0
- package/examples/single-task/graph.json +27 -0
- package/examples/single-task/policy-same-vendor.json +55 -0
- package/examples/single-task/policy.json +52 -0
- package/examples/single-task/src/.gitkeep +0 -0
- package/examples/single-task/test/run.js +19 -0
- package/examples/toolchains/README.md +32 -0
- package/examples/toolchains/go/bad/label/label.go +9 -0
- package/examples/toolchains/go/good/label/label.go +9 -0
- package/examples/toolchains/go/policy.json +79 -0
- package/examples/toolchains/go/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/go/repo/_gitignore +1 -0
- package/examples/toolchains/go/repo/go.mod +5 -0
- package/examples/toolchains/go/repo/go.sum +2 -0
- package/examples/toolchains/go/repo/label/label.go +9 -0
- package/examples/toolchains/go/repo/label/label_test.go +27 -0
- package/examples/toolchains/go/repo/label/version.go +9 -0
- package/examples/toolchains/go/task.json +25 -0
- package/examples/toolchains/mixed-go/bad/backend/label/label.go +9 -0
- package/examples/toolchains/mixed-go/bad/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-go/good/backend/label/label.go +9 -0
- package/examples/toolchains/mixed-go/good/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-go/policy.json +126 -0
- package/examples/toolchains/mixed-go/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/mixed-go/repo/_gitignore +6 -0
- package/examples/toolchains/mixed-go/repo/backend/go.mod +3 -0
- package/examples/toolchains/mixed-go/repo/backend/label/label.go +9 -0
- package/examples/toolchains/mixed-go/repo/backend/label/label_test.go +30 -0
- package/examples/toolchains/mixed-go/repo/contract/check.mjs +17 -0
- package/examples/toolchains/mixed-go/repo/contract/label.json +9 -0
- package/examples/toolchains/mixed-go/repo/frontend/package.json +15 -0
- package/examples/toolchains/mixed-go/repo/frontend/pnpm-lock.yaml +22 -0
- package/examples/toolchains/mixed-go/repo/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-go/repo/frontend/test/label.test.js +11 -0
- package/examples/toolchains/mixed-go/task.json +25 -0
- package/examples/toolchains/mixed-python/bad/backend/src/fx_backend/__init__.py +6 -0
- package/examples/toolchains/mixed-python/bad/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-python/good/backend/src/fx_backend/__init__.py +6 -0
- package/examples/toolchains/mixed-python/good/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-python/policy.json +121 -0
- package/examples/toolchains/mixed-python/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/mixed-python/repo/_gitignore +6 -0
- package/examples/toolchains/mixed-python/repo/backend/.python-version +1 -0
- package/examples/toolchains/mixed-python/repo/backend/pyproject.toml +19 -0
- package/examples/toolchains/mixed-python/repo/backend/src/fx_backend/__init__.py +6 -0
- package/examples/toolchains/mixed-python/repo/backend/tests/test_label.py +11 -0
- package/examples/toolchains/mixed-python/repo/backend/uv.lock +108 -0
- package/examples/toolchains/mixed-python/repo/contract/check.mjs +17 -0
- package/examples/toolchains/mixed-python/repo/contract/label.json +9 -0
- package/examples/toolchains/mixed-python/repo/frontend/package.json +15 -0
- package/examples/toolchains/mixed-python/repo/frontend/pnpm-lock.yaml +22 -0
- package/examples/toolchains/mixed-python/repo/frontend/src/label.js +4 -0
- package/examples/toolchains/mixed-python/repo/frontend/test/label.test.js +11 -0
- package/examples/toolchains/mixed-python/task.json +25 -0
- package/examples/toolchains/npm/bad/src/label.js +4 -0
- package/examples/toolchains/npm/good/src/label.js +4 -0
- package/examples/toolchains/npm/policy.json +82 -0
- package/examples/toolchains/npm/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/npm/repo/_gitignore +4 -0
- package/examples/toolchains/npm/repo/package-lock.json +22 -0
- package/examples/toolchains/npm/repo/package.json +13 -0
- package/examples/toolchains/npm/repo/src/label.js +4 -0
- package/examples/toolchains/npm/repo/test/label.test.js +13 -0
- package/examples/toolchains/npm/task.json +25 -0
- package/examples/toolchains/pnpm/bad/packages/label/src/label.js +4 -0
- package/examples/toolchains/pnpm/good/packages/label/src/label.js +4 -0
- package/examples/toolchains/pnpm/policy.json +85 -0
- package/examples/toolchains/pnpm/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/pnpm/repo/_gitignore +4 -0
- package/examples/toolchains/pnpm/repo/package.json +10 -0
- package/examples/toolchains/pnpm/repo/packages/checker/check.mjs +5 -0
- package/examples/toolchains/pnpm/repo/packages/checker/package.json +9 -0
- package/examples/toolchains/pnpm/repo/packages/label/package.json +14 -0
- package/examples/toolchains/pnpm/repo/packages/label/src/label.js +4 -0
- package/examples/toolchains/pnpm/repo/packages/label/test/label.test.js +13 -0
- package/examples/toolchains/pnpm/repo/pnpm-lock.yaml +29 -0
- package/examples/toolchains/pnpm/repo/pnpm-workspace.yaml +2 -0
- package/examples/toolchains/pnpm/task.json +25 -0
- package/examples/toolchains/python/bad/src/fx_label/__init__.py +6 -0
- package/examples/toolchains/python/good/src/fx_label/__init__.py +6 -0
- package/examples/toolchains/python/policy.json +80 -0
- package/examples/toolchains/python/repo/.python-version +1 -0
- package/examples/toolchains/python/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/python/repo/_gitignore +5 -0
- package/examples/toolchains/python/repo/pyproject.toml +19 -0
- package/examples/toolchains/python/repo/src/fx_label/__init__.py +6 -0
- package/examples/toolchains/python/repo/tests/test_label.py +13 -0
- package/examples/toolchains/python/repo/uv.lock +108 -0
- package/examples/toolchains/python/task.json +25 -0
- package/examples/toolchains/turbo/bad/packages/label/src/label.js +4 -0
- package/examples/toolchains/turbo/good/packages/label/src/label.js +4 -0
- package/examples/toolchains/turbo/policy.json +110 -0
- package/examples/toolchains/turbo/repo/REQUIREMENTS.md +9 -0
- package/examples/toolchains/turbo/repo/_gitignore +4 -0
- package/examples/toolchains/turbo/repo/package.json +13 -0
- package/examples/toolchains/turbo/repo/packages/checker/check.mjs +5 -0
- package/examples/toolchains/turbo/repo/packages/checker/package.json +9 -0
- package/examples/toolchains/turbo/repo/packages/label/package.json +14 -0
- package/examples/toolchains/turbo/repo/packages/label/src/label.js +4 -0
- package/examples/toolchains/turbo/repo/packages/label/test/label.test.js +13 -0
- package/examples/toolchains/turbo/repo/pnpm-lock.yaml +94 -0
- package/examples/toolchains/turbo/repo/pnpm-workspace.yaml +2 -0
- package/examples/toolchains/turbo/repo/turbo.json +7 -0
- package/examples/toolchains/turbo/task.json +25 -0
- package/examples/turbo-starter/README.md +16 -0
- package/examples/turbo-starter/changes/issued-at-consumer-only.patch +12 -0
- package/examples/turbo-starter/changes/issued-at.patch +130 -0
- package/examples/turbo-starter/cleanup.mjs +58 -0
- package/examples/turbo-starter/graph.json +32 -0
- package/examples/turbo-starter/harness/acceptance/db-isolation.test.mjs +97 -0
- package/examples/turbo-starter/harness/acceptance/env-probe.test.mjs +75 -0
- package/examples/turbo-starter/harness/acceptance/generation.test.mjs +54 -0
- package/examples/turbo-starter/harness/acceptance/issued-at.test.mjs +43 -0
- package/examples/turbo-starter/harness/acceptance/lifecycle-probe.test.mjs +47 -0
- package/examples/turbo-starter/harness/compose-bridge.mjs +593 -0
- package/examples/turbo-starter/harness/compose.acceptance.yml +23 -0
- package/examples/turbo-starter/harness/env/backend.env.template +7 -0
- package/examples/turbo-starter/harness/env/compose.env.template +6 -0
- package/examples/turbo-starter/policy.json +109 -0
- package/examples/turbo-starter/policy.lifecycle.json +114 -0
- package/examples/turbo-starter/setup.mjs +352 -0
- package/examples/two-vendors/policy.json +30 -0
- package/lib/agents/cli-adapter.d.ts +48 -0
- package/lib/agents/cli-adapter.js +386 -0
- package/lib/agents/inspect.d.ts +6 -0
- package/lib/agents/inspect.js +46 -0
- package/lib/agents/probe-server.d.ts +2 -0
- package/lib/agents/probe-server.js +13 -0
- package/lib/agents/review-probe.d.ts +295 -0
- package/lib/agents/review-probe.js +872 -0
- package/lib/agents/runtime-events.d.ts +11 -0
- package/lib/agents/runtime-events.js +21 -0
- package/lib/agents/schemas.d.ts +217 -0
- package/lib/agents/schemas.js +124 -0
- package/lib/agents/usage.d.ts +8 -0
- package/lib/agents/usage.js +83 -0
- package/lib/cli.d.ts +2 -0
- package/lib/cli.js +491 -0
- package/lib/connectors/files.d.ts +108 -0
- package/lib/connectors/files.js +133 -0
- package/lib/contracts/checks.d.ts +307 -0
- package/lib/contracts/checks.js +181 -0
- package/lib/contracts/cli.d.ts +3895 -0
- package/lib/contracts/cli.js +664 -0
- package/lib/contracts/context.d.ts +135 -0
- package/lib/contracts/context.js +129 -0
- package/lib/contracts/digest.d.ts +9 -0
- package/lib/contracts/digest.js +22 -0
- package/lib/contracts/errors.d.ts +81 -0
- package/lib/contracts/errors.js +70 -0
- package/lib/contracts/events.d.ts +827 -0
- package/lib/contracts/events.js +258 -0
- package/lib/contracts/execution.d.ts +3216 -0
- package/lib/contracts/execution.js +872 -0
- package/lib/contracts/failure.d.ts +67 -0
- package/lib/contracts/failure.js +127 -0
- package/lib/contracts/index.d.ts +18 -0
- package/lib/contracts/index.js +18 -0
- package/lib/contracts/policy.d.ts +1080 -0
- package/lib/contracts/policy.js +544 -0
- package/lib/contracts/ports.d.ts +417 -0
- package/lib/contracts/ports.js +1 -0
- package/lib/contracts/primitives.d.ts +63 -0
- package/lib/contracts/primitives.js +73 -0
- package/lib/contracts/profiles.d.ts +336 -0
- package/lib/contracts/profiles.js +109 -0
- package/lib/contracts/published.d.ts +3237 -0
- package/lib/contracts/published.js +34 -0
- package/lib/contracts/result.d.ts +1280 -0
- package/lib/contracts/result.js +311 -0
- package/lib/contracts/review.d.ts +441 -0
- package/lib/contracts/review.js +477 -0
- package/lib/contracts/toolchain.d.ts +96 -0
- package/lib/contracts/toolchain.js +89 -0
- package/lib/contracts/version.d.ts +17 -0
- package/lib/contracts/version.js +17 -0
- package/lib/contracts/work.d.ts +173 -0
- package/lib/contracts/work.js +178 -0
- package/lib/control/base-move.d.ts +44 -0
- package/lib/control/base-move.js +90 -0
- package/lib/control/doctor.d.ts +50 -0
- package/lib/control/doctor.js +113 -0
- package/lib/control/harness.d.ts +1125 -0
- package/lib/control/harness.js +741 -0
- package/lib/control/provenance.d.ts +17 -0
- package/lib/control/provenance.js +25 -0
- package/lib/control/recovery.d.ts +237 -0
- package/lib/control/recovery.js +236 -0
- package/lib/control/review.d.ts +62 -0
- package/lib/control/review.js +178 -0
- package/lib/control/workflow.d.ts +247 -0
- package/lib/control/workflow.js +259 -0
- package/lib/engine/budget.d.ts +345 -0
- package/lib/engine/budget.js +278 -0
- package/lib/engine/classify.d.ts +8 -0
- package/lib/engine/classify.js +32 -0
- package/lib/engine/engine.d.ts +1527 -0
- package/lib/engine/engine.js +2259 -0
- package/lib/engine/failure.d.ts +1 -0
- package/lib/engine/failure.js +3 -0
- package/lib/engine/joint-runner.d.ts +257 -0
- package/lib/engine/joint-runner.js +254 -0
- package/lib/engine/measured.d.ts +15 -0
- package/lib/engine/measured.js +82 -0
- package/lib/engine/prompt.d.ts +66 -0
- package/lib/engine/prompt.js +131 -0
- package/lib/engine/property-recovery.d.ts +50 -0
- package/lib/engine/property-recovery.js +171 -0
- package/lib/engine/repair.d.ts +33 -0
- package/lib/engine/repair.js +82 -0
- package/lib/engine/result.d.ts +9 -0
- package/lib/engine/result.js +218 -0
- package/lib/engine/reuse.d.ts +80 -0
- package/lib/engine/reuse.js +102 -0
- package/lib/engine/runner.d.ts +360 -0
- package/lib/engine/runner.js +1836 -0
- package/lib/engine/standalone.d.ts +6 -0
- package/lib/engine/standalone.js +48 -0
- package/lib/engine/usage.d.ts +87 -0
- package/lib/engine/usage.js +165 -0
- package/lib/environment/isolation.d.ts +110 -0
- package/lib/environment/isolation.js +238 -0
- package/lib/environment/lifecycle.d.ts +40 -0
- package/lib/environment/lifecycle.js +197 -0
- package/lib/environment/live-calls.d.ts +14 -0
- package/lib/environment/live-calls.js +73 -0
- package/lib/environment/os-tools.d.ts +16 -0
- package/lib/environment/os-tools.js +35 -0
- package/lib/environment/process.d.ts +32 -0
- package/lib/environment/process.js +369 -0
- package/lib/environment/redaction.d.ts +22 -0
- package/lib/environment/redaction.js +102 -0
- package/lib/environment/resources.d.ts +31 -0
- package/lib/environment/resources.js +120 -0
- package/lib/index.d.ts +65 -0
- package/lib/index.js +67 -0
- package/lib/integrations/connector/commands.d.ts +358 -0
- package/lib/integrations/connector/commands.js +428 -0
- package/lib/integrations/connector/config.d.ts +215 -0
- package/lib/integrations/connector/config.js +214 -0
- package/lib/integrations/connector/delivery.d.ts +107 -0
- package/lib/integrations/connector/delivery.js +591 -0
- package/lib/integrations/connector/fake.d.ts +291 -0
- package/lib/integrations/connector/fake.js +786 -0
- package/lib/integrations/connector/history.d.ts +124 -0
- package/lib/integrations/connector/history.js +497 -0
- package/lib/integrations/connector/index.d.ts +16 -0
- package/lib/integrations/connector/index.js +16 -0
- package/lib/integrations/connector/redact.d.ts +19 -0
- package/lib/integrations/connector/redact.js +91 -0
- package/lib/integrations/connector/registry.d.ts +15 -0
- package/lib/integrations/connector/registry.js +43 -0
- package/lib/integrations/connector/snapshot.d.ts +131 -0
- package/lib/integrations/connector/snapshot.js +422 -0
- package/lib/integrations/connector/source.d.ts +37 -0
- package/lib/integrations/connector/source.js +73 -0
- package/lib/integrations/connector/steps.d.ts +71 -0
- package/lib/integrations/connector/steps.js +188 -0
- package/lib/integrations/connector/store.d.ts +140 -0
- package/lib/integrations/connector/store.js +277 -0
- package/lib/integrations/connector/summary.d.ts +38 -0
- package/lib/integrations/connector/summary.js +59 -0
- package/lib/integrations/connector/testing-worker.d.ts +1 -0
- package/lib/integrations/connector/testing-worker.js +81 -0
- package/lib/integrations/connector/testing.d.ts +205 -0
- package/lib/integrations/connector/testing.js +1615 -0
- package/lib/integrations/connector/transport.d.ts +240 -0
- package/lib/integrations/connector/transport.js +28 -0
- package/lib/integrations/memory/context.d.ts +120 -0
- package/lib/integrations/memory/context.js +239 -0
- package/lib/integrations/memory/index.d.ts +8 -0
- package/lib/integrations/memory/index.js +8 -0
- package/lib/integrations/memory/provider.d.ts +79 -0
- package/lib/integrations/memory/provider.js +22 -0
- package/lib/integrations/memory/sink.d.ts +56 -0
- package/lib/integrations/memory/sink.js +116 -0
- package/lib/integrations/memory/testing.d.ts +44 -0
- package/lib/integrations/memory/testing.js +61 -0
- package/lib/integrations/relay-v1/index.d.ts +122 -0
- package/lib/integrations/relay-v1/index.js +860 -0
- package/lib/integrations/relay-v2/index.d.ts +145 -0
- package/lib/integrations/relay-v2/index.js +752 -0
- package/lib/integrations/relay-v2/process.d.ts +32 -0
- package/lib/integrations/relay-v2/process.js +112 -0
- package/lib/integrations/relay-v2/protocol.d.ts +172 -0
- package/lib/integrations/relay-v2/protocol.js +142 -0
- package/lib/package.d.ts +2 -0
- package/lib/package.js +3 -0
- package/lib/profiles/resolve.d.ts +190 -0
- package/lib/profiles/resolve.js +273 -0
- package/lib/storage/document.d.ts +1234 -0
- package/lib/storage/document.js +80 -0
- package/lib/storage/sqlite.d.ts +35 -0
- package/lib/storage/sqlite.js +260 -0
- package/lib/testing/index.d.ts +5 -0
- package/lib/testing/index.js +5 -0
- package/lib/testing/store-contract.d.ts +8 -0
- package/lib/testing/store-contract.js +167 -0
- package/lib/toolchains/index.d.ts +9 -0
- package/lib/toolchains/index.js +9 -0
- package/lib/toolchains/inspect.d.ts +105 -0
- package/lib/toolchains/inspect.js +831 -0
- package/lib/toolchains/manifests.d.ts +67 -0
- package/lib/toolchains/manifests.js +238 -0
- package/lib/toolchains/probe.d.ts +28 -0
- package/lib/toolchains/probe.js +54 -0
- package/lib/toolchains/recipes.d.ts +33 -0
- package/lib/toolchains/recipes.js +51 -0
- package/lib/validate.d.ts +36 -0
- package/lib/validate.js +175 -0
- package/lib/verification/acceptance.d.ts +284 -0
- package/lib/verification/acceptance.js +670 -0
- package/lib/verification/checks.d.ts +72 -0
- package/lib/verification/checks.js +190 -0
- package/lib/verification/confirm.d.ts +125 -0
- package/lib/verification/confirm.js +411 -0
- package/lib/verification/corpus.d.ts +1303 -0
- package/lib/verification/corpus.js +744 -0
- package/lib/verification/dependencies.d.ts +66 -0
- package/lib/verification/dependencies.js +144 -0
- package/lib/verification/gates.d.ts +160 -0
- package/lib/verification/gates.js +600 -0
- package/lib/verification/joint.d.ts +63 -0
- package/lib/verification/joint.js +121 -0
- package/lib/verification/proof.d.ts +54 -0
- package/lib/verification/proof.js +96 -0
- package/lib/verification/property.d.ts +86 -0
- package/lib/verification/property.js +214 -0
- package/lib/verification/requirements.d.ts +25 -0
- package/lib/verification/requirements.js +69 -0
- package/lib/verification/self-check.d.ts +29 -0
- package/lib/verification/self-check.js +99 -0
- package/lib/verification/spike.d.ts +79 -0
- package/lib/verification/spike.js +124 -0
- package/package.json +100 -4
- package/profiles/expo-mobile.json +15 -0
- package/profiles/go-api.json +15 -0
- package/profiles/go-gotestsum.json +21 -0
- package/profiles/mobile-maestro.json +29 -0
- package/profiles/nest-api.json +26 -0
- package/profiles/next-product.json +72 -0
- package/profiles/node-checks.json +77 -0
- package/profiles/npm-ci.json +22 -0
- package/profiles/pnpm-frozen.json +24 -0
- package/profiles/python-api.json +32 -0
- package/profiles/python-uv.json +26 -0
- package/profiles/react-native.json +50 -0
- package/profiles/react-vite-admin.json +71 -0
- package/schemas/context-snapshot.schema.json +231 -0
- package/schemas/contract-document.schema.json +385 -0
- package/schemas/execution-document.schema.json +3695 -0
- package/schemas/execution-policy.schema.json +2196 -0
- package/schemas/joint-verification.schema.json +617 -0
- package/schemas/result-envelope.schema.json +1652 -0
- package/schemas/review-record.schema.json +177 -0
- package/schemas/run-event.schema.json +2458 -0
- package/schemas/run-result.schema.json +1570 -0
- package/schemas/stack-profile.schema.json +535 -0
- package/schemas/work-graph.schema.json +381 -0
- package/schemas/work-order.schema.json +256 -0
- package/skills/harness/SKILL.md +64 -0
- package/templates/AGENTS.md +13 -0
- package/templates/controller-mode/AGENTS.controller.md +58 -0
- package/templates/controller-mode/README.md +44 -0
- package/templates/graph.json +22 -0
- package/templates/policy.json +28 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { TaskFailure } from "./failure.js";
|
|
2
|
+
import { callRecords, liveCall, usageTotals } from "./usage.js";
|
|
3
|
+
const closed = (w) => !!w && ['done', 'cancelled'].includes(w.status);
|
|
4
|
+
/** Группа, о которой вызов: идентификатор, у которого хост знает поручения. */
|
|
5
|
+
const isGroup = (c, view, id) => c.scopes.members(id, view).length > 0;
|
|
6
|
+
/**
|
|
7
|
+
* Предмет вызова — поручение: по попытке либо по ID предмета, если вид
|
|
8
|
+
* предмета не назван иначе. Вызов о группе или контракте с ID, совпавшим с
|
|
9
|
+
* поручением, этим поручением не становится.
|
|
10
|
+
*/
|
|
11
|
+
function subjectWork(view, meta) {
|
|
12
|
+
const byRun = meta.runId ? view.attempt(meta.runId)?.workId : undefined;
|
|
13
|
+
if (byRun)
|
|
14
|
+
return byRun;
|
|
15
|
+
if (meta.subjectKind && meta.subjectKind !== 'work')
|
|
16
|
+
return undefined;
|
|
17
|
+
if (legacyGroupCall(meta))
|
|
18
|
+
return undefined;
|
|
19
|
+
return meta.subjectId && view.work(meta.subjectId) ? meta.subjectId : undefined;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Прежняя запись вызова о группе: вида предмета нет, но при вызове закреплена
|
|
23
|
+
* группа без поручения и без попытки. Вызов о поручении закреплял его
|
|
24
|
+
* `workId`, поэтому совпадение ID предмета с поручением такую запись не
|
|
25
|
+
* делает расходом одного поручения — она остаётся расходом группы.
|
|
26
|
+
*/
|
|
27
|
+
export function legacyGroupCall(r) {
|
|
28
|
+
return !r.subjectKind && !r.runId && !!r.groupId && !r.workId;
|
|
29
|
+
}
|
|
30
|
+
/** Поручение записи: закреплённое при вызове, у прежних записей — по попытке. */
|
|
31
|
+
const recordWork = (view, r) => r.workId ?? subjectWork(view, r);
|
|
32
|
+
/** Группа записи: закреплённая при вызове, у прежних записей — по текущему составу. */
|
|
33
|
+
function recordGroup(c, view, r) {
|
|
34
|
+
if (r.groupId)
|
|
35
|
+
return r.groupId;
|
|
36
|
+
if (r.subjectId && (!r.subjectKind || r.subjectKind === 'group') && isGroup(c, view, r.subjectId))
|
|
37
|
+
return r.subjectId;
|
|
38
|
+
const work = recordWork(view, r);
|
|
39
|
+
return work ? c.scopes.groupOf(work, view) : undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Группы поручения: текущая и те, в которых оно тратило. Поручение,
|
|
43
|
+
* перенесённое из исчерпанной группы, остаётся под её ограничением.
|
|
44
|
+
*/
|
|
45
|
+
function workGroups(c, view, records, workId) {
|
|
46
|
+
const groups = new Set();
|
|
47
|
+
const current = c.scopes.groupOf(workId, view);
|
|
48
|
+
if (current)
|
|
49
|
+
groups.add(current);
|
|
50
|
+
for (const r of records)
|
|
51
|
+
if ((r.workId === workId || r.workIds?.includes(workId)) && r.groupId)
|
|
52
|
+
groups.add(r.groupId);
|
|
53
|
+
return groups;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Записи вызовов области. Поручению принадлежат его попытки, вызовы о нём и
|
|
57
|
+
* вызовы о его группе целиком (ревью плана): правила долевой атрибуции нет,
|
|
58
|
+
* и вызов о группе не должен считаться нулевым для поручения. Группе — вызовы,
|
|
59
|
+
* закреплённые за ней при вызове: перенос поручения в другую группу расход не
|
|
60
|
+
* переносит.
|
|
61
|
+
*/
|
|
62
|
+
export function scopeRecords(c, view, records, scope) {
|
|
63
|
+
if (scope.kind === 'total')
|
|
64
|
+
return records;
|
|
65
|
+
if (scope.kind === 'group')
|
|
66
|
+
return records.filter((r) => recordGroup(c, view, r) === scope.id);
|
|
67
|
+
const groups = workGroups(c, view, records, scope.id);
|
|
68
|
+
return records.filter((r) => {
|
|
69
|
+
if (r.workIds)
|
|
70
|
+
return r.workIds.includes(scope.id);
|
|
71
|
+
const work = recordWork(view, r);
|
|
72
|
+
return work === scope.id || (!work && groups.has(recordGroup(c, view, r) ?? ''));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Попытки области, вызовы которых не записаны: выданы до учёта вызовов (без
|
|
77
|
+
* отметки `callsRecorded`), модель в них вызывалась (есть кандидат или замер
|
|
78
|
+
* реализации), а записи реализации нет. Их стоимость и время неизвестны и не
|
|
79
|
+
* выдумываются; посторонние и более поздние вызовы пробел не закрывают, а у
|
|
80
|
+
* отмеченной попытки отсутствие записи значит, что вызова не было.
|
|
81
|
+
*/
|
|
82
|
+
function uncoveredRuns(c, view, records, scope) {
|
|
83
|
+
const implemented = new Set(records.filter((r) => r.stage === 'implementation' && r.runId).map((r) => r.runId));
|
|
84
|
+
const works = scope.kind === 'total'
|
|
85
|
+
? undefined
|
|
86
|
+
: scope.kind === 'work'
|
|
87
|
+
? new Set([scope.id])
|
|
88
|
+
: new Set(c.scopes.members(scope.id, view));
|
|
89
|
+
return view
|
|
90
|
+
.attempts()
|
|
91
|
+
.filter((a) => !a.callsRecorded &&
|
|
92
|
+
(!works || works.has(a.workId)) &&
|
|
93
|
+
!implemented.has(a.id) &&
|
|
94
|
+
(a.candidateSha || a.timings?.some((t) => t.stage.startsWith('implementation')))).length;
|
|
95
|
+
}
|
|
96
|
+
export function budgetStatus(c, view, scope, records, at = Date.now()) {
|
|
97
|
+
const limit = c.policy.budgets?.[scope.kind];
|
|
98
|
+
if (!limit)
|
|
99
|
+
return undefined;
|
|
100
|
+
const all = records ?? callRecords(view);
|
|
101
|
+
const own = scopeRecords(c, view, all, scope);
|
|
102
|
+
const spent = {
|
|
103
|
+
...usageTotals(own, false, at),
|
|
104
|
+
uncoveredRuns: uncoveredRuns(c, view, all, scope),
|
|
105
|
+
// Исключение из неизвестных — только для вызовов с подтверждённо живым
|
|
106
|
+
// владельцем: вызов умершего процесса неизвестен сразу, не дожидаясь
|
|
107
|
+
// восстановления.
|
|
108
|
+
liveCalls: own.filter((r) => liveCall(r, at, 2 * c.policy.runTimeoutMs)).length,
|
|
109
|
+
};
|
|
110
|
+
const name = scope.kind === 'total' ? 'total' : `${scope.kind} ${scope.id}`;
|
|
111
|
+
const usd = spent.reportedUsd + spent.estimatedUsd;
|
|
112
|
+
const detail = `израсходовано: сообщено $${spent.reportedUsd.toFixed(4)}, оценено $${spent.estimatedUsd.toFixed(4)}, ` +
|
|
113
|
+
`неизвестных вызовов ${spent.unknownCalls}, время ${Math.round(spent.durationMs / 1000)} с` +
|
|
114
|
+
(spent.uncoveredRuns ? `, попыток без учёта вызовов ${spent.uncoveredRuns}` : '');
|
|
115
|
+
if (limit.usd !== undefined && usd >= limit.usd)
|
|
116
|
+
return {
|
|
117
|
+
scope,
|
|
118
|
+
limit,
|
|
119
|
+
spent,
|
|
120
|
+
state: 'exceeded',
|
|
121
|
+
reason: `Бюджет ${name} исчерпан: предел $${limit.usd}; ${detail}`,
|
|
122
|
+
};
|
|
123
|
+
if (limit.timeMs !== undefined && spent.durationMs >= limit.timeMs)
|
|
124
|
+
return {
|
|
125
|
+
scope,
|
|
126
|
+
limit,
|
|
127
|
+
spent,
|
|
128
|
+
state: 'exceeded',
|
|
129
|
+
reason: `Бюджет времени ${name} исчерпан: предел ${Math.round(limit.timeMs / 1000)} с; ${detail}`,
|
|
130
|
+
};
|
|
131
|
+
// Идущий вызов живого процесса начат в пределах бюджета и не прерывается:
|
|
132
|
+
// его стоимость станет известна по окончании, а неопределённым бюджет
|
|
133
|
+
// делают остальные вызовы без стоимости. Иначе любой идущий вызов
|
|
134
|
+
// останавливал бы параллельную работу той же области. Перерасход поэтому
|
|
135
|
+
// ограничен вызовами, идущими одновременно.
|
|
136
|
+
const settledUnknown = spent.unknownCalls - spent.liveCalls;
|
|
137
|
+
const unknown = [
|
|
138
|
+
...(limit.usd !== undefined && settledUnknown
|
|
139
|
+
? [`у ${settledUnknown} вызовов неизвестна стоимость`]
|
|
140
|
+
: []),
|
|
141
|
+
...(limit.timeMs !== undefined && spent.unknownDurationCalls
|
|
142
|
+
? [`у ${spent.unknownDurationCalls} вызовов неизвестно время`]
|
|
143
|
+
: []),
|
|
144
|
+
...(spent.uncoveredRuns ? [`у ${spent.uncoveredRuns} попыток вызовы не записаны`] : []),
|
|
145
|
+
];
|
|
146
|
+
if (unknown.length)
|
|
147
|
+
return {
|
|
148
|
+
scope,
|
|
149
|
+
limit,
|
|
150
|
+
spent,
|
|
151
|
+
state: 'undetermined',
|
|
152
|
+
reason: `Бюджет ${name} не определён: ${unknown.join(', ')}; ${detail}`,
|
|
153
|
+
};
|
|
154
|
+
return { scope, limit, spent, state: 'within' };
|
|
155
|
+
}
|
|
156
|
+
/** Бюджеты, относящиеся к поручению: его собственный, его групп и общий. */
|
|
157
|
+
export function workBudgets(c, view, workId, records = callRecords(view), at = Date.now()) {
|
|
158
|
+
return [
|
|
159
|
+
budgetStatus(c, view, { kind: 'work', id: workId }, records, at),
|
|
160
|
+
...[...workGroups(c, view, records, workId)].map((id) => budgetStatus(c, view, { kind: 'group', id }, records, at)),
|
|
161
|
+
budgetStatus(c, view, { kind: 'total' }, records, at),
|
|
162
|
+
].filter((b) => !!b);
|
|
163
|
+
}
|
|
164
|
+
/** Группа вызова и её поручения: из входа вызова и по составу, известному хосту. */
|
|
165
|
+
function callGroup(c, view, meta) {
|
|
166
|
+
const id = meta.group?.id ??
|
|
167
|
+
(meta.subjectId &&
|
|
168
|
+
(!meta.subjectKind || meta.subjectKind === 'group') &&
|
|
169
|
+
isGroup(c, view, meta.subjectId)
|
|
170
|
+
? meta.subjectId
|
|
171
|
+
: undefined);
|
|
172
|
+
if (!id)
|
|
173
|
+
return undefined;
|
|
174
|
+
const members = [...new Set([...(meta.group?.workIds ?? []), ...c.scopes.members(id, view)])];
|
|
175
|
+
return { id, members };
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Бюджеты вызова модели: по его попытке или поручению — бюджеты поручения; о
|
|
179
|
+
* группе — бюджеты группы и каждого её открытого поручения, за которыми вызов
|
|
180
|
+
* будет учтён; о прочем (контракт) — общий.
|
|
181
|
+
*/
|
|
182
|
+
export function callBudgets(c, view, meta, records = callRecords(view), at = Date.now()) {
|
|
183
|
+
const workId = meta.group ? undefined : subjectWork(view, meta);
|
|
184
|
+
if (workId)
|
|
185
|
+
return workBudgets(c, view, workId, records, at);
|
|
186
|
+
const group = callGroup(c, view, meta);
|
|
187
|
+
const open = group ? group.members.filter((id) => !closed(view.work(id))) : [];
|
|
188
|
+
const seen = new Set();
|
|
189
|
+
return [
|
|
190
|
+
group ? budgetStatus(c, view, { kind: 'group', id: group.id }, records, at) : undefined,
|
|
191
|
+
...open.flatMap((id) => workBudgets(c, view, id, records, at)),
|
|
192
|
+
budgetStatus(c, view, { kind: 'total' }, records, at),
|
|
193
|
+
].filter((b) => {
|
|
194
|
+
if (!b)
|
|
195
|
+
return false;
|
|
196
|
+
const key = `${b.scope.kind}:${b.scope.id ?? ''}`;
|
|
197
|
+
if (seen.has(key))
|
|
198
|
+
return false;
|
|
199
|
+
seen.add(key);
|
|
200
|
+
return true;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Поручение и группа вызова на момент вызова: за ними остаётся его расход.
|
|
205
|
+
* У вызова о группе — её открытые поручения.
|
|
206
|
+
*/
|
|
207
|
+
export function callAttribution(c, view, meta, all = false) {
|
|
208
|
+
const workId = meta.group ? undefined : subjectWork(view, meta);
|
|
209
|
+
if (workId) {
|
|
210
|
+
const groupId = c.scopes.groupOf(workId, view);
|
|
211
|
+
return { workId, ...(groupId ? { groupId } : {}) };
|
|
212
|
+
}
|
|
213
|
+
const group = callGroup(c, view, meta);
|
|
214
|
+
if (!group)
|
|
215
|
+
return {};
|
|
216
|
+
return {
|
|
217
|
+
groupId: group.id,
|
|
218
|
+
workIds: all ? group.members : group.members.filter((id) => !closed(view.work(id))),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/** Бюджет, который запрещает тратить: превышен или не определён при `onUnknown: stop`. */
|
|
222
|
+
export function blockingBudget(c, statuses) {
|
|
223
|
+
const stopOnUnknown = (c.policy.budgets?.onUnknown ?? 'stop') === 'stop';
|
|
224
|
+
return statuses.find((b) => b.state === 'exceeded' || (b.state === 'undetermined' && stopOnUnknown));
|
|
225
|
+
}
|
|
226
|
+
/** Отказ до траты: превышенный или неопределённый бюджет не даёт начать вызов. */
|
|
227
|
+
export function assertBudget(c, statuses) {
|
|
228
|
+
const blocking = blockingBudget(c, statuses);
|
|
229
|
+
if (blocking)
|
|
230
|
+
throw new TaskFailure('budget', blocking.reason);
|
|
231
|
+
}
|
|
232
|
+
/** Компактный вид бюджета для статуса и потребителей. */
|
|
233
|
+
export function budgetView(b) {
|
|
234
|
+
const usd = b.spent.reportedUsd + b.spent.estimatedUsd;
|
|
235
|
+
return {
|
|
236
|
+
scope: b.scope,
|
|
237
|
+
state: b.state,
|
|
238
|
+
limit: b.limit,
|
|
239
|
+
reportedUsd: b.spent.reportedUsd,
|
|
240
|
+
estimatedUsd: b.spent.estimatedUsd,
|
|
241
|
+
unknownCalls: b.spent.unknownCalls,
|
|
242
|
+
unfinishedCalls: b.spent.unfinishedCalls,
|
|
243
|
+
abandonedCalls: b.spent.abandonedCalls,
|
|
244
|
+
errorCalls: b.spent.errorCalls,
|
|
245
|
+
uncoveredRuns: b.spent.uncoveredRuns,
|
|
246
|
+
durationMs: b.spent.durationMs,
|
|
247
|
+
// Остаток денег известен, только когда неизвестных вызовов и попыток нет.
|
|
248
|
+
remainingUsd: b.limit.usd === undefined ||
|
|
249
|
+
b.spent.unknownCalls > b.spent.liveCalls ||
|
|
250
|
+
b.spent.uncoveredRuns > 0
|
|
251
|
+
? null
|
|
252
|
+
: Math.max(0, b.limit.usd - usd),
|
|
253
|
+
remainingMs: b.limit.timeMs === undefined || b.spent.unknownDurationCalls > 0 || b.spent.uncoveredRuns > 0
|
|
254
|
+
? null
|
|
255
|
+
: Math.max(0, b.limit.timeMs - b.spent.durationMs),
|
|
256
|
+
...(b.reason ? { reason: b.reason } : {}),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/** Бюджеты установки: общий, группы с незавершённой работой, незавершённые поручения. */
|
|
260
|
+
export function budgetOverview(c, view, records) {
|
|
261
|
+
if (!c.policy.budgets)
|
|
262
|
+
return undefined;
|
|
263
|
+
const all = records ?? callRecords(view);
|
|
264
|
+
const open = view.works().filter((w) => !closed(w));
|
|
265
|
+
const groups = new Set(open.map((w) => c.scopes.groupOf(w.id, view)).filter((g) => !!g));
|
|
266
|
+
const scopes = [
|
|
267
|
+
{ kind: 'total' },
|
|
268
|
+
...[...groups].map((id) => ({ kind: 'group', id })),
|
|
269
|
+
...open.map((w) => ({ kind: 'work', id: w.id })),
|
|
270
|
+
];
|
|
271
|
+
return {
|
|
272
|
+
onUnknown: c.policy.budgets.onUnknown,
|
|
273
|
+
budgets: scopes
|
|
274
|
+
.map((scope) => budgetStatus(c, view, scope, all))
|
|
275
|
+
.filter((b) => !!b)
|
|
276
|
+
.map(budgetView),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BlockedError, type FailureKind } from '../contracts/index.ts';
|
|
2
|
+
export { BlockedError };
|
|
3
|
+
/**
|
|
4
|
+
* Отказ провайдера, а не работы: он повторится на любой следующей выдаче.
|
|
5
|
+
* Список узкий намеренно — временные ошибки сети сюда не входят.
|
|
6
|
+
*/
|
|
7
|
+
export declare const providerRefusal: (message: string) => boolean;
|
|
8
|
+
export declare function classifyFailure(error: unknown, message: string): FailureKind;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BlockedError, HarnessError, TaskFailure, } from "../contracts/index.js";
|
|
2
|
+
export { BlockedError };
|
|
3
|
+
/**
|
|
4
|
+
* Отказ провайдера, а не работы: он повторится на любой следующей выдаче.
|
|
5
|
+
* Список узкий намеренно — временные ошибки сети сюда не входят.
|
|
6
|
+
*/
|
|
7
|
+
export const providerRefusal = (message) => /credit balance|insufficient credit|not logged in|please run \/login|invalid api key|authentication_error|insufficient_quota|unauthorized|oauth token|token expired|401|session limit|usage limit|rate limit|rate_limit|limit reached|too many requests|429|overloaded/i.test(message);
|
|
8
|
+
/** Класс отказа для случаев, где он не поставлен в точке обнаружения. */
|
|
9
|
+
/** Отказ движка с кодом, который сам называет класс отказа попытки. */
|
|
10
|
+
const byCode = {
|
|
11
|
+
GATE_FAILED: 'gate',
|
|
12
|
+
REVIEW_REJECTED: 'review',
|
|
13
|
+
REVIEW_PROTOCOL_INVALID: 'review-protocol',
|
|
14
|
+
SCOPE_VIOLATION: 'scope-violation',
|
|
15
|
+
BUDGET_EXHAUSTED: 'budget',
|
|
16
|
+
};
|
|
17
|
+
export function classifyFailure(error, message) {
|
|
18
|
+
if (error instanceof TaskFailure)
|
|
19
|
+
return error.kind;
|
|
20
|
+
// Приёмка отказала по правилу (нет доказательства, ревью, область): класс —
|
|
21
|
+
// это правило, а не «неизвестно».
|
|
22
|
+
if (error instanceof HarnessError && byCode[error.code])
|
|
23
|
+
return byCode[error.code];
|
|
24
|
+
if (providerRefusal(message))
|
|
25
|
+
return 'provider-auth';
|
|
26
|
+
if (/исчерпан лимит времени|истёк|истек|тайм-?аут|timeout|превысил/i.test(message))
|
|
27
|
+
return 'timeout';
|
|
28
|
+
if (error instanceof BlockedError ||
|
|
29
|
+
/отменен|прерван|abort|econnreset|epipe|socket hang up|connection reset|stream closed|channel closed|fetch failed|network error/i.test(message))
|
|
30
|
+
return 'environment';
|
|
31
|
+
return 'unknown';
|
|
32
|
+
}
|