@webpresso/agent-kit 3.1.30 → 3.3.0
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/README.md +53 -15
- package/bin/_run.js +183 -20
- package/catalog/AGENTS.md.tpl +3 -3
- package/catalog/agent/agents/implementer.md +45 -0
- package/catalog/agent/rules/parallel-orchestration-quota.md +100 -0
- package/catalog/agent/rules/pre-implementation.md +28 -1
- package/catalog/agent/rules/supported-agent-clis.md +9 -0
- package/catalog/agent/rules/typescript-version.md +50 -0
- package/catalog/agent/skills/codex/SKILL.md +25 -1
- package/catalog/agent/skills/verify/SKILL.md +4 -2
- package/catalog/docs/templates/blueprint.md +1 -1
- package/dist/esm/audit/agents.js +1 -0
- package/dist/esm/audit/blueprint-lifecycle-sql.d.ts +12 -0
- package/dist/esm/audit/blueprint-lifecycle-sql.js +23 -2
- package/dist/esm/audit/blueprint-pr-coverage.js +4 -0
- package/dist/esm/audit/blueprint-trust.js +14 -10
- package/dist/esm/audit/changeset-evidence.d.ts +23 -0
- package/dist/esm/audit/changeset-evidence.js +30 -0
- package/dist/esm/audit/changeset-required.d.ts +10 -0
- package/dist/esm/audit/changeset-required.js +73 -0
- package/dist/esm/audit/ci-guardrails-detection.d.ts +27 -0
- package/dist/esm/audit/ci-guardrails-detection.js +68 -0
- package/dist/esm/audit/ci-guardrails-wiring.d.ts +22 -0
- package/dist/esm/audit/ci-guardrails-wiring.js +80 -0
- package/dist/esm/audit/ci-test-perf.js +30 -0
- package/dist/esm/audit/command-surface.js +4 -2
- package/dist/esm/audit/host-substitution-risk.d.ts +2 -0
- package/dist/esm/audit/host-substitution-risk.js +256 -0
- package/dist/esm/audit/registry.d.ts +4 -4
- package/dist/esm/audit/registry.js +13 -1
- package/dist/esm/audit/toolchain-isolation.js +1 -2
- package/dist/esm/audit/typescript-version.d.ts +2 -0
- package/dist/esm/audit/typescript-version.js +123 -0
- package/dist/esm/blueprint/core/parser.js +101 -2
- package/dist/esm/blueprint/core/schema.d.ts +1 -0
- package/dist/esm/blueprint/core/schema.js +26 -0
- package/dist/esm/blueprint/core/validation/criteria.d.ts +14 -0
- package/dist/esm/blueprint/core/validation/criteria.js +41 -2
- package/dist/esm/blueprint/core/validation/state.js +25 -14
- package/dist/esm/blueprint/db/migrations/run.js +4 -2
- package/dist/esm/blueprint/lifecycle/audit.d.ts +20 -0
- package/dist/esm/blueprint/lifecycle/audit.js +87 -20
- package/dist/esm/blueprint/lifecycle/canonicalize.d.ts +6 -0
- package/dist/esm/blueprint/lifecycle/canonicalize.js +135 -0
- package/dist/esm/blueprint/lifecycle/local.d.ts +2 -1
- package/dist/esm/blueprint/lifecycle/local.js +19 -3
- package/dist/esm/blueprint/lifecycle/review-provenance.d.ts +6 -0
- package/dist/esm/blueprint/lifecycle/review-provenance.js +68 -12
- package/dist/esm/blueprint/lifecycle/rollout-anchor.d.ts +97 -0
- package/dist/esm/blueprint/lifecycle/rollout-anchor.js +161 -0
- package/dist/esm/blueprint/service/BlueprintCreationService.d.ts +1 -0
- package/dist/esm/blueprint/service/BlueprintCreationService.js +53 -4
- package/dist/esm/blueprint/service/blueprint-tech-debt-links.js +16 -2
- package/dist/esm/blueprint/sync/client.d.ts +2 -1
- package/dist/esm/blueprint/sync/client.js +1 -1
- package/dist/esm/blueprint/trust/command-runner.d.ts +26 -0
- package/dist/esm/blueprint/trust/command-runner.js +148 -0
- package/dist/esm/blueprint/trust/dossier.d.ts +1 -0
- package/dist/esm/blueprint/trust/dossier.js +40 -6
- package/dist/esm/blueprint/trust/gates.d.ts +7 -0
- package/dist/esm/blueprint/trust/gates.js +20 -0
- package/dist/esm/blueprint/trust/promotion.d.ts +8 -0
- package/dist/esm/blueprint/trust/promotion.js +74 -112
- package/dist/esm/blueprint/trust/scaffold.d.ts +19 -0
- package/dist/esm/blueprint/trust/scaffold.js +64 -0
- package/dist/esm/blueprint/trust/validator.d.ts +1 -0
- package/dist/esm/blueprint/trust/validator.js +7 -1
- package/dist/esm/blueprint/utils/archive.js +27 -5
- package/dist/esm/blueprint/utils/document-paths.d.ts +18 -0
- package/dist/esm/blueprint/utils/document-paths.js +45 -12
- package/dist/esm/build/cli-mcp-parity.js +15 -0
- package/dist/esm/cli/bundle/commands/blueprint.d.ts +1 -0
- package/dist/esm/cli/bundle/commands/blueprint.js +1 -0
- package/dist/esm/cli/cli.d.ts +1 -1
- package/dist/esm/cli/cli.js +43 -0
- package/dist/esm/cli/commands/agent-launch.d.ts +8 -1
- package/dist/esm/cli/commands/agent-launch.js +93 -4
- package/dist/esm/cli/commands/audit-core.d.ts +1 -1
- package/dist/esm/cli/commands/audit-core.js +8 -1
- package/dist/esm/cli/commands/audit.js +21 -5
- package/dist/esm/cli/commands/blueprint/execution.js +18 -6
- package/dist/esm/cli/commands/blueprint/mutations.js +59 -40
- package/dist/esm/cli/commands/blueprint/router-dispatch.d.ts +2 -1
- package/dist/esm/cli/commands/blueprint/router-dispatch.js +20 -1
- package/dist/esm/cli/commands/blueprint/router-output.d.ts +21 -0
- package/dist/esm/cli/commands/blueprint/router-output.js +48 -17
- package/dist/esm/cli/commands/blueprint/router.d.ts +7 -0
- package/dist/esm/cli/commands/blueprint/router.js +13 -1
- package/dist/esm/cli/commands/ci-preflight.d.ts +13 -2
- package/dist/esm/cli/commands/ci-preflight.js +30 -6
- package/dist/esm/cli/commands/compile.js +5 -31
- package/dist/esm/cli/commands/dash/delivery-status.d.ts +1 -0
- package/dist/esm/cli/commands/dash/delivery-status.js +11 -4
- package/dist/esm/cli/commands/doctor.js +5 -0
- package/dist/esm/cli/commands/hook.d.ts +5 -0
- package/dist/esm/cli/commands/hook.js +15 -9
- package/dist/esm/cli/commands/init/config.js +2 -2
- package/dist/esm/cli/commands/init/convergence-apply.d.ts +88 -0
- package/dist/esm/cli/commands/init/convergence-apply.js +325 -0
- package/dist/esm/cli/commands/init/convergence-state.d.ts +13 -0
- package/dist/esm/cli/commands/init/convergence-state.js +54 -0
- package/dist/esm/cli/commands/init/convergence.d.ts +23 -0
- package/dist/esm/cli/commands/init/convergence.js +57 -0
- package/dist/esm/cli/commands/init/git-convergence.d.ts +19 -0
- package/dist/esm/cli/commands/init/git-convergence.js +168 -0
- package/dist/esm/cli/commands/init/gitignore-patcher.js +2 -0
- package/dist/esm/cli/commands/init/grok-privacy-smoke.d.ts +52 -0
- package/dist/esm/cli/commands/init/grok-privacy-smoke.js +91 -0
- package/dist/esm/cli/commands/init/host-native-proof.d.ts +18 -0
- package/dist/esm/cli/commands/init/host-native-proof.js +88 -0
- package/dist/esm/cli/commands/init/host-visibility.d.ts +2 -2
- package/dist/esm/cli/commands/init/host-visibility.js +94 -5
- package/dist/esm/cli/commands/init/index.d.ts +3 -0
- package/dist/esm/cli/commands/init/index.js +326 -173
- package/dist/esm/cli/commands/init/mcp-spec.d.ts +57 -0
- package/dist/esm/cli/commands/init/mcp-spec.js +273 -0
- package/dist/esm/cli/commands/init/merge.d.ts +1 -0
- package/dist/esm/cli/commands/init/merge.js +30 -1
- package/dist/esm/cli/commands/init/package-root.d.ts +7 -0
- package/dist/esm/cli/commands/init/package-root.js +10 -2
- package/dist/esm/cli/commands/init/plugin-cache-prune.d.ts +0 -3
- package/dist/esm/cli/commands/init/plugin-cache-prune.js +0 -1
- package/dist/esm/cli/commands/init/scaffold-agents-md.d.ts +1 -1
- package/dist/esm/cli/commands/init/scaffold-agents-md.js +9 -3
- package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +20 -0
- package/dist/esm/cli/commands/init/scaffold-base-kit.js +61 -5
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.d.ts +14 -8
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/emitters/grok.js +41 -24
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.d.ts +2 -0
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +54 -24
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.d.ts +12 -31
- package/dist/esm/cli/commands/init/scaffolders/codex-mcp/index.js +288 -202
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.d.ts +22 -0
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.js +230 -0
- package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.d.ts +38 -0
- package/dist/esm/cli/commands/init/scaffolders/grok-mcp/index.js +73 -0
- package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.d.ts +21 -0
- package/dist/esm/cli/commands/init/scaffolders/opencode-plugin/index.js +559 -63
- package/dist/esm/cli/commands/init/untracked-collision.d.ts +14 -0
- package/dist/esm/cli/commands/init/untracked-collision.js +42 -0
- package/dist/esm/cli/commands/opencode-probe.d.ts +107 -0
- package/dist/esm/cli/commands/opencode-probe.js +323 -0
- package/dist/esm/cli/commands/opencode-rotate.d.ts +72 -0
- package/dist/esm/cli/commands/opencode-rotate.js +147 -0
- package/dist/esm/cli/commands/public-readiness.d.ts +11 -0
- package/dist/esm/cli/commands/public-readiness.js +42 -0
- package/dist/esm/cli/commands/quality-runner.js +3 -3
- package/dist/esm/cli/commands/review.d.ts +35 -3
- package/dist/esm/cli/commands/review.js +751 -209
- package/dist/esm/cli/commands/run-wait.d.ts +2 -0
- package/dist/esm/cli/commands/run-wait.js +122 -0
- package/dist/esm/cli/commands/rust-check.d.ts +24 -0
- package/dist/esm/cli/commands/rust-check.js +110 -0
- package/dist/esm/cli/commands/setup-help.js +1 -1
- package/dist/esm/cli/commands/setup-repair-help.js +1 -1
- package/dist/esm/cli/commands/typecheck.d.ts +1 -0
- package/dist/esm/cli/commands/typecheck.js +30 -1
- package/dist/esm/cli/commands/worktree/router-dispatch.d.ts +6 -0
- package/dist/esm/cli/commands/worktree/router-dispatch.js +126 -9
- package/dist/esm/cli/commands/worktree/router.js +10 -0
- package/dist/esm/cli/direct-provider-launch.js +9 -5
- package/dist/esm/cli/optional-tool-freshness.d.ts +27 -0
- package/dist/esm/cli/optional-tool-freshness.js +219 -9
- package/dist/esm/daemon/domains/git-actions.d.ts +29 -0
- package/dist/esm/daemon/domains/git-actions.js +136 -0
- package/dist/esm/daemon/domains/run-registry-interop.d.ts +16 -0
- package/dist/esm/daemon/domains/run-registry-interop.js +45 -0
- package/dist/esm/daemon/domains/sessions-search.d.ts +11 -0
- package/dist/esm/daemon/domains/sessions-search.js +29 -0
- package/dist/esm/daemon/handlers/blueprints-handlers.d.ts +5 -0
- package/dist/esm/daemon/handlers/blueprints-handlers.js +36 -0
- package/dist/esm/daemon/handlers/deploy-handlers.d.ts +6 -0
- package/dist/esm/daemon/handlers/deploy-handlers.js +5 -0
- package/dist/esm/daemon/handlers/fleet-handlers.d.ts +15 -0
- package/dist/esm/daemon/handlers/fleet-handlers.js +91 -0
- package/dist/esm/daemon/handlers/git-handlers.d.ts +6 -0
- package/dist/esm/daemon/handlers/git-handlers.js +27 -0
- package/dist/esm/daemon/handlers/product.d.ts +7 -0
- package/dist/esm/daemon/handlers/product.js +10 -119
- package/dist/esm/daemon/handlers/sessions-handlers.d.ts +12 -0
- package/dist/esm/daemon/handlers/sessions-handlers.js +31 -0
- package/dist/esm/daemon/handlers/shared.d.ts +5 -0
- package/dist/esm/daemon/handlers/shared.js +16 -0
- package/dist/esm/daemon/methods.d.ts +8 -1
- package/dist/esm/daemon/methods.js +7 -0
- package/dist/esm/daemon/protocol/contract.d.ts +43 -0
- package/dist/esm/daemon/protocol/contract.js +26 -0
- package/dist/esm/daemon/protocol/index.d.ts +1 -1
- package/dist/esm/daemon/protocol/index.js +1 -1
- package/dist/esm/hooks/__conformance__/matrix.d.ts +6 -0
- package/dist/esm/hooks/__conformance__/matrix.js +137 -0
- package/dist/esm/hooks/conformance/batch.js +5 -0
- package/dist/esm/hooks/doctor.d.ts +5 -3
- package/dist/esm/hooks/doctor.js +67 -16
- package/dist/esm/hooks/permission-request/index.js +9 -3
- package/dist/esm/hooks/permission-request/policy.d.ts +9 -0
- package/dist/esm/hooks/permission-request/policy.js +13 -0
- package/dist/esm/hooks/pretool-guard/dev-routing.d.ts +9 -0
- package/dist/esm/hooks/pretool-guard/dev-routing.js +41 -5
- package/dist/esm/hooks/pretool-guard/logger.d.ts +7 -0
- package/dist/esm/hooks/pretool-guard/logger.js +16 -0
- package/dist/esm/hooks/pretool-guard/runner.d.ts +6 -0
- package/dist/esm/hooks/pretool-guard/runner.js +68 -16
- package/dist/esm/hooks/pretool-guard/validators/blueprint.js +30 -10
- package/dist/esm/hooks/pretool-guard/validators/forbidden-commands.js +54 -0
- package/dist/esm/hooks/pretool-guard/validators/index.d.ts +7 -0
- package/dist/esm/hooks/pretool-guard/validators/index.js +21 -0
- package/dist/esm/hooks/pretool-guard/validators/path-contract.d.ts +19 -0
- package/dist/esm/hooks/pretool-guard/validators/path-contract.js +25 -8
- package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +6 -1
- package/dist/esm/hooks/shared/guard-outcome.d.ts +149 -0
- package/dist/esm/hooks/shared/guard-outcome.js +215 -0
- package/dist/esm/hooks/shared/hook-bootstrap.d.ts +26 -0
- package/dist/esm/hooks/shared/hook-bootstrap.js +64 -1
- package/dist/esm/hooks/shared/types.d.ts +8 -5
- package/dist/esm/hooks/shared/types.js +50 -9
- package/dist/esm/hooks/stop/qa-changed-files.d.ts +11 -0
- package/dist/esm/hooks/stop/qa-changed-files.js +92 -0
- package/dist/esm/mcp/blueprint/_shared/lifecycle.js +14 -1
- package/dist/esm/mcp/blueprint/handlers/document-mutations.d.ts +5 -0
- package/dist/esm/mcp/blueprint/handlers/document-mutations.js +109 -11
- package/dist/esm/mcp/blueprint/handlers/review-log.js +8 -1
- package/dist/esm/mcp/blueprint/handlers/task-advance.js +113 -54
- package/dist/esm/mcp/blueprint/handlers/task-verify.js +39 -19
- package/dist/esm/mcp/blueprint/registration.js +3 -2
- package/dist/esm/mcp/server.d.ts +1 -0
- package/dist/esm/mcp/server.js +27 -0
- package/dist/esm/mcp/tools/_names.d.ts +1 -1
- package/dist/esm/mcp/tools/_names.js +5 -0
- package/dist/esm/mcp/tools/_registry.js +10 -0
- package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
- package/dist/esm/mcp/tools/_shared/result.js +44 -1
- package/dist/esm/mcp/tools/audit.d.ts +5 -1
- package/dist/esm/mcp/tools/audit.js +42 -0
- package/dist/esm/mcp/tools/audits.d.ts +4 -0
- package/dist/esm/mcp/tools/ci-preflight.d.ts +50 -0
- package/dist/esm/mcp/tools/ci-preflight.js +183 -0
- package/dist/esm/mcp/tools/pr-upsert.js +39 -1
- package/dist/esm/mcp/tools/review-run.d.ts +28 -0
- package/dist/esm/mcp/tools/review-run.js +216 -0
- package/dist/esm/mcp/tools/run-wait.d.ts +86 -0
- package/dist/esm/mcp/tools/run-wait.js +593 -0
- package/dist/esm/mcp/tools/session-fetch-and-index.d.ts +1 -1
- package/dist/esm/mcp/tools/session-id.d.ts +3 -0
- package/dist/esm/mcp/tools/session-id.js +45 -0
- package/dist/esm/mcp/tools/session-info.d.ts +3 -0
- package/dist/esm/mcp/tools/session-info.js +49 -0
- package/dist/esm/output-transforms/audit.d.ts +23 -0
- package/dist/esm/output-transforms/audit.js +47 -0
- package/dist/esm/output-transforms/index.js +2 -0
- package/dist/esm/package.json +2 -0
- package/dist/esm/platform/client.d.ts +7 -1
- package/dist/esm/platform/github-contract-source.d.ts +2 -1
- package/dist/esm/pr-description/contract.js +24 -4
- package/dist/esm/review/authority.js +27 -14
- package/dist/esm/review/availability.d.ts +33 -0
- package/dist/esm/review/availability.js +122 -20
- package/dist/esm/review/delivery-verifier.d.ts +27 -0
- package/dist/esm/review/delivery-verifier.js +115 -0
- package/dist/esm/review/events.d.ts +3 -2
- package/dist/esm/review/events.js +23 -3
- package/dist/esm/review/execution/adapters.d.ts +11 -1
- package/dist/esm/review/execution/adapters.js +36 -10
- package/dist/esm/review/execution/artifacts.d.ts +1 -1
- package/dist/esm/review/execution/output-policy.d.ts +2 -0
- package/dist/esm/review/execution/output-policy.js +12 -0
- package/dist/esm/review/execution/review-checkout.js +121 -21
- package/dist/esm/review/execution/supervisor.js +36 -4
- package/dist/esm/review/execution/types.d.ts +9 -0
- package/dist/esm/review/opencode-account.d.ts +24 -0
- package/dist/esm/review/opencode-account.js +68 -0
- package/dist/esm/review/opencode-auth.d.ts +33 -0
- package/dist/esm/review/opencode-auth.js +79 -0
- package/dist/esm/review/subject.d.ts +25 -0
- package/dist/esm/review/subject.js +116 -26
- package/dist/esm/runtime/opencode-account-materializer.d.ts +34 -0
- package/dist/esm/runtime/opencode-account-materializer.js +33 -0
- package/dist/esm/rust/affected-manifests.d.ts +38 -0
- package/dist/esm/rust/affected-manifests.js +69 -0
- package/dist/esm/session-memory/fetch-index.d.ts +1 -1
- package/dist/esm/status/snapshot.js +32 -25
- package/dist/esm/test/duration-sequencer.d.ts +20 -0
- package/dist/esm/test/duration-sequencer.js +54 -0
- package/dist/esm/test/shard-durations.json +922 -0
- package/dist/esm/test/worker-budget.d.ts +6 -0
- package/dist/esm/test/worker-budget.js +59 -1
- package/dist/esm/test-helpers/global-setup.d.ts +1 -2
- package/dist/esm/test-helpers/global-setup.js +79 -32
- package/dist/esm/typecheck/planner.d.ts +9 -1
- package/dist/esm/typecheck/planner.js +31 -0
- package/dist/esm/ultragoal/service.d.ts +7 -0
- package/dist/esm/ultragoal/service.js +42 -2
- package/dist/esm/utils/write-json-file.js +3 -1
- package/dist/esm/worktrees/codex-project-trust.d.ts +15 -0
- package/dist/esm/worktrees/codex-project-trust.js +56 -0
- package/dist/esm/worktrees/orphan-scan.d.ts +50 -0
- package/dist/esm/worktrees/orphan-scan.js +211 -0
- package/dist/esm/worktrees/registry.d.ts +8 -0
- package/dist/esm/worktrees/registry.js +18 -3
- package/package.json +18 -13
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { loadOpencodeAccountAvailability, pruneExpiredOpencodeAccountSuppressions, recordOpencodeAccountAvailabilityWithLock, } from "#review/availability.js";
|
|
2
|
+
import { opencodeAccountCanonicalId, selectActiveOpencodeAccount, } from "#review/opencode-account.js";
|
|
3
|
+
import { materializeOpencodeGoAuth, readOpencodeGoKey } from "#review/opencode-auth.js";
|
|
4
|
+
import { resolveOpencodeAccountsGracefully } from "#runtime/opencode-account-materializer.js";
|
|
5
|
+
/** The `--wp-rotate` flag, namespaced like `--wp-permission-mode`. */
|
|
6
|
+
export const OPENCODE_ROTATE_FLAG = "--wp-rotate";
|
|
7
|
+
/**
|
|
8
|
+
* Cooldown to record when an account is known to be capped but the real reset
|
|
9
|
+
* window is unknown: it assumes the shortest real OpenCode Go window (5 hours).
|
|
10
|
+
*
|
|
11
|
+
* A manual `--wp-rotate` has no provider response at all, so it always lands
|
|
12
|
+
* here. The launch-time health probe (`#cli/commands/opencode-probe.js`) uses it
|
|
13
|
+
* only as a fallback, because that path *does* have a provider response and
|
|
14
|
+
* normally records the true parsed window instead.
|
|
15
|
+
*
|
|
16
|
+
* Recording no window at all would leave the record permanently suppressed —
|
|
17
|
+
* `suppressionExpired` only clears a record that carries `retryAfterMs` — so
|
|
18
|
+
* every rotated account would be cooled forever and selection would degrade to
|
|
19
|
+
* "first account" again. A too-short guess is self-correcting: the account is
|
|
20
|
+
* retried, and if it is still capped the gate (or the next probe/rotation)
|
|
21
|
+
* simply cools it again.
|
|
22
|
+
*/
|
|
23
|
+
export const OPENCODE_UNKNOWN_LIMIT_COOLDOWN_MS = 5 * 60 * 60 * 1_000;
|
|
24
|
+
function coolActiveAccount(canonicalId, deps) {
|
|
25
|
+
if (deps.recordCooldown)
|
|
26
|
+
return deps.recordCooldown(canonicalId);
|
|
27
|
+
// Same event shape the review gate records on an in-flight usage limit, so
|
|
28
|
+
// both writers produce identical, mutually-readable cooldown records.
|
|
29
|
+
return recordOpencodeAccountAvailabilityWithLock(canonicalId, {
|
|
30
|
+
failureCode: "opencode-go-usage-limit",
|
|
31
|
+
terminalClassification: "provider-limit-reached",
|
|
32
|
+
limitWindow: "unknown",
|
|
33
|
+
retryAfterMs: OPENCODE_UNKNOWN_LIMIT_COOLDOWN_MS,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Pick the account to make active next.
|
|
38
|
+
*
|
|
39
|
+
* `excludeId` is the account we just cooled. It is filtered out here rather
|
|
40
|
+
* than relied upon coming back suppressed from the store: that keeps rotation
|
|
41
|
+
* correct even if the cooldown write is slow, contended, or fails outright, and
|
|
42
|
+
* removes a read-after-write dependency from the hot path.
|
|
43
|
+
*/
|
|
44
|
+
function selectNextAccount(accounts, excludeId) {
|
|
45
|
+
const pruned = pruneExpiredOpencodeAccountSuppressions(loadOpencodeAccountAvailability());
|
|
46
|
+
const candidates = excludeId === undefined ? accounts : accounts.filter((account) => account.id !== excludeId);
|
|
47
|
+
// Callers guarantee at least one candidate: rotation needs >= 2 accounts, and
|
|
48
|
+
// the no-exclude path is only reached with a non-empty list.
|
|
49
|
+
const pool = candidates.length > 0 ? candidates : accounts;
|
|
50
|
+
const selection = selectActiveOpencodeAccount(pool, pruned.state);
|
|
51
|
+
return selection ?? { account: pool[0], cooled: false };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Cool the currently-materialized opencode-go account and materialize the next
|
|
55
|
+
* usable one into the global `auth.json`.
|
|
56
|
+
*
|
|
57
|
+
* The active account is identified by reading the global store back and matching
|
|
58
|
+
* its key against the resolved accounts — `wp` wrote that value itself, so this
|
|
59
|
+
* stays correct without persisting a separate "last launched" record. When the
|
|
60
|
+
* key matches nothing (a hand-edited store, or a fresh machine), the rotation
|
|
61
|
+
* degrades to a plain reselect and cools nothing, because cooling an account we
|
|
62
|
+
* cannot identify would suppress the wrong one.
|
|
63
|
+
*
|
|
64
|
+
* Never throws: the caller launches the TUI regardless of the outcome.
|
|
65
|
+
*/
|
|
66
|
+
export async function rotateActiveOpencodeAccount(cwd, secretProfile, deps = {}) {
|
|
67
|
+
try {
|
|
68
|
+
return await rotateOrThrow(cwd, secretProfile, deps);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
// The cooldown write takes the user-scope lock the review gate also holds
|
|
72
|
+
// (contention rejects after a few short retries) and the auth write can fail
|
|
73
|
+
// with EACCES/ENOSPC. Neither may abort the launch, so both degrade to the
|
|
74
|
+
// same "carry on and launch" outcome the sibling select path uses.
|
|
75
|
+
return { kind: "unavailable", reason: error instanceof Error ? error.message : String(error) };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function rotateOrThrow(cwd, secretProfile, deps) {
|
|
79
|
+
const resolution = (deps.resolveOpencodeAccounts ?? resolveOpencodeAccountsGracefully)({
|
|
80
|
+
cwd,
|
|
81
|
+
secretProfile,
|
|
82
|
+
});
|
|
83
|
+
if (!resolution.ok)
|
|
84
|
+
return { kind: "unavailable", reason: resolution.reason };
|
|
85
|
+
const accounts = resolution.accounts;
|
|
86
|
+
if (accounts.length < 2)
|
|
87
|
+
return { kind: "single-account" };
|
|
88
|
+
const activeKey = (deps.readActiveKey ?? (() => readOpencodeGoKey()))();
|
|
89
|
+
const current = accounts.find((account) => account.key === activeKey);
|
|
90
|
+
const materialize = deps.materializeGlobalOpencodeAuth ?? ((key) => void materializeOpencodeGoAuth(key));
|
|
91
|
+
if (!current) {
|
|
92
|
+
const next = selectNextAccount(accounts);
|
|
93
|
+
materialize(next.account.key);
|
|
94
|
+
return { kind: "reselected", activeAccountId: next.account.id, activeIsCooled: next.cooled };
|
|
95
|
+
}
|
|
96
|
+
await coolActiveAccount(opencodeAccountCanonicalId(current.id), deps);
|
|
97
|
+
const next = selectNextAccount(accounts, current.id);
|
|
98
|
+
materialize(next.account.key);
|
|
99
|
+
return {
|
|
100
|
+
kind: "rotated",
|
|
101
|
+
cooledAccountId: current.id,
|
|
102
|
+
activeAccountId: next.account.id,
|
|
103
|
+
activeIsCooled: next.cooled,
|
|
104
|
+
...(next.waitMs === undefined ? {} : { waitMs: next.waitMs }),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Render a cooldown remainder as a short ` (resets in ~Xd)` suffix, or the empty
|
|
109
|
+
* string when the window is unknown. Shared with the launch-time health probe so
|
|
110
|
+
* rotation and probe output cannot drift apart.
|
|
111
|
+
*/
|
|
112
|
+
export function formatOpencodeResetWait(waitMs) {
|
|
113
|
+
if (waitMs === undefined)
|
|
114
|
+
return "";
|
|
115
|
+
const hours = waitMs / 3_600_000;
|
|
116
|
+
if (hours >= 24)
|
|
117
|
+
return ` (resets in ~${Math.ceil(hours / 24)}d)`;
|
|
118
|
+
if (hours >= 1)
|
|
119
|
+
return ` (resets in ~${Math.ceil(hours)}h)`;
|
|
120
|
+
return ` (resets in ~${Math.max(1, Math.ceil(waitMs / 60_000))}m)`;
|
|
121
|
+
}
|
|
122
|
+
/** Human-readable lines describing a rotation outcome, in print order. */
|
|
123
|
+
export function describeOpencodeRotate(outcome) {
|
|
124
|
+
switch (outcome.kind) {
|
|
125
|
+
case "unavailable":
|
|
126
|
+
return [`wp opencode: could not resolve OpenCode Go accounts (${outcome.reason}).`];
|
|
127
|
+
case "single-account":
|
|
128
|
+
return [
|
|
129
|
+
"wp opencode: only one OpenCode Go account is configured; nothing to rotate to. Add OPENCODE_GO_KEY_<ID> secrets to enable rotation.",
|
|
130
|
+
];
|
|
131
|
+
case "reselected":
|
|
132
|
+
return [
|
|
133
|
+
"wp opencode: the active key matches no configured account, so nothing was cooled.",
|
|
134
|
+
`wp opencode: now active -> opencode-go:${outcome.activeAccountId}`,
|
|
135
|
+
];
|
|
136
|
+
case "rotated": {
|
|
137
|
+
const lines = [
|
|
138
|
+
`wp opencode: cooled opencode-go:${outcome.cooledAccountId} (usage limit)`,
|
|
139
|
+
`wp opencode: now active -> opencode-go:${outcome.activeAccountId}`,
|
|
140
|
+
];
|
|
141
|
+
if (outcome.activeIsCooled) {
|
|
142
|
+
lines.push(`wp opencode: every account is within its cooldown; using the soonest-expiring one${formatOpencodeResetWait(outcome.waitMs)}. Run again after it resets, or add another OPENCODE_GO_KEY_<ID>.`);
|
|
143
|
+
}
|
|
144
|
+
return lines;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CAC } from "cac";
|
|
2
|
+
export declare const PUBLIC_READINESS_COMMAND_HELP: string;
|
|
3
|
+
export interface PublicReadinessDeps {
|
|
4
|
+
readonly cwd?: string;
|
|
5
|
+
readonly resolveRepoRoot?: (cwd: string) => string | null;
|
|
6
|
+
readonly hasReadinessScript?: (repoRoot: string) => boolean;
|
|
7
|
+
readonly run?: (repoRoot: string) => number;
|
|
8
|
+
readonly stderr?: Pick<typeof process.stderr, "write">;
|
|
9
|
+
}
|
|
10
|
+
export declare function runPublicReadiness(deps?: PublicReadinessDeps): number;
|
|
11
|
+
export declare function registerPublicReadinessCommand(cli: CAC): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { getGitTopLevel } from "#git/changed-files";
|
|
5
|
+
export const PUBLIC_READINESS_COMMAND_HELP = [
|
|
6
|
+
"Run the public package readiness gate.",
|
|
7
|
+
"",
|
|
8
|
+
"Delegates to scripts/public-readiness.ts, preserving that script as the",
|
|
9
|
+
"single owner of package and repository visibility readiness checks.",
|
|
10
|
+
"",
|
|
11
|
+
"Examples:",
|
|
12
|
+
" wp public:readiness",
|
|
13
|
+
].join("\n");
|
|
14
|
+
function defaultRunPublicReadiness(repoRoot) {
|
|
15
|
+
const result = spawnSync("bun", ["scripts/public-readiness.ts"], {
|
|
16
|
+
cwd: repoRoot,
|
|
17
|
+
stdio: "inherit",
|
|
18
|
+
env: process.env,
|
|
19
|
+
});
|
|
20
|
+
if (result.error) {
|
|
21
|
+
process.stderr.write(`${result.error.message}\n`);
|
|
22
|
+
return 1;
|
|
23
|
+
}
|
|
24
|
+
return typeof result.status === "number" ? result.status : 1;
|
|
25
|
+
}
|
|
26
|
+
export function runPublicReadiness(deps = {}) {
|
|
27
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
28
|
+
const stderr = deps.stderr ?? process.stderr;
|
|
29
|
+
const resolveRepoRoot = deps.resolveRepoRoot ?? ((current) => getGitTopLevel(current));
|
|
30
|
+
const hasReadinessScript = deps.hasReadinessScript ??
|
|
31
|
+
((repoRoot) => existsSync(path.join(repoRoot, "scripts", "public-readiness.ts")));
|
|
32
|
+
const run = deps.run ?? defaultRunPublicReadiness;
|
|
33
|
+
const repoRoot = resolveRepoRoot(cwd) ?? cwd;
|
|
34
|
+
if (!hasReadinessScript(repoRoot)) {
|
|
35
|
+
stderr.write(`wp public:readiness: scripts/public-readiness.ts is missing at ${repoRoot}.\n`);
|
|
36
|
+
return 1;
|
|
37
|
+
}
|
|
38
|
+
return run(repoRoot);
|
|
39
|
+
}
|
|
40
|
+
export function registerPublicReadinessCommand(cli) {
|
|
41
|
+
cli.command("public:readiness", PUBLIC_READINESS_COMMAND_HELP).action(() => runPublicReadiness());
|
|
42
|
+
}
|
|
@@ -147,9 +147,9 @@ function renderSummaryLines(summary, transformed, entry, passed) {
|
|
|
147
147
|
}
|
|
148
148
|
if (!passed || transformed.truncated) {
|
|
149
149
|
lines.push(`Full log: wp logs ${entry.command}`);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
if (transformed.elisions && transformed.elisions.length > 0) {
|
|
151
|
+
lines.push(...transformed.elisions.map((elision) => `Retrieve elision: ${elision.retrieveTool} id=${elision.id}`));
|
|
152
|
+
}
|
|
153
153
|
}
|
|
154
154
|
if (transformed.warnings && transformed.warnings.length > 0) {
|
|
155
155
|
lines.push(...transformed.warnings.map((warning) => `Warning: ${warning}`));
|
|
@@ -2,8 +2,11 @@ import type { CAC } from "cac";
|
|
|
2
2
|
import { runReviewExecution, type ReviewEffort, type ReviewLimitWindow, type ReviewProvider, type ReviewRunSummary, type ReviewStage } from "#review/execution/index.js";
|
|
3
3
|
import { createReviewTargetCheckout, type ReviewTargetCheckout } from "#review/execution/review-checkout.js";
|
|
4
4
|
import { type ReviewProcessRecord } from "#review/execution/process-registry.js";
|
|
5
|
+
import { type OpencodeAccount } from "#review/opencode-account.js";
|
|
6
|
+
import { resolveOpencodeAccountsGracefully } from "#runtime/opencode-account-materializer.js";
|
|
5
7
|
import { evaluateApprovalGate } from "#lifecycle/audit";
|
|
6
8
|
import { type ReviewEvent } from "#review/events.js";
|
|
9
|
+
import { type ReviewGateVerification } from "#review/delivery-verifier.js";
|
|
7
10
|
import { runCommand } from "#mcp/tools/_shared/run-command.js";
|
|
8
11
|
export type ReviewVerdict = "approve" | "approve-with-nits" | "reject" | "no-verdict";
|
|
9
12
|
export type ReviewStatus = "complete" | "timed-out" | "incomplete" | "unavailable" | "failed";
|
|
@@ -77,6 +80,8 @@ export interface ReviewCommandOptions {
|
|
|
77
80
|
artifactRoot?: string;
|
|
78
81
|
idleSeconds?: string;
|
|
79
82
|
maxProgressEvents?: string;
|
|
83
|
+
reason?: string;
|
|
84
|
+
confirm?: boolean;
|
|
80
85
|
}
|
|
81
86
|
interface ReviewGateAttempt {
|
|
82
87
|
readonly provider: ReviewProvider;
|
|
@@ -90,6 +95,7 @@ interface ReviewGateAttempt {
|
|
|
90
95
|
readonly verdict: ReviewVerdict;
|
|
91
96
|
readonly terminalClassification: string;
|
|
92
97
|
readonly failureCode?: string;
|
|
98
|
+
readonly providerErrorDetail?: string;
|
|
93
99
|
readonly limitWindow?: ReviewLimitWindow;
|
|
94
100
|
readonly retryAfterMs?: number;
|
|
95
101
|
readonly artifact?: string;
|
|
@@ -105,7 +111,7 @@ interface ReviewGateResult {
|
|
|
105
111
|
readonly targetSha: string;
|
|
106
112
|
readonly baseSha: string;
|
|
107
113
|
readonly promptHash: string;
|
|
108
|
-
readonly status: "approved" | "needs-review" | "rejected" | "recorded-untracked";
|
|
114
|
+
readonly status: "approved" | "needs-review" | "rejected" | "scope-exhausted" | "recorded-untracked" | "verification-failed";
|
|
109
115
|
readonly approvalGate: ReturnType<typeof evaluateApprovalGate>;
|
|
110
116
|
readonly statePath: string;
|
|
111
117
|
readonly attempts: readonly ReviewGateAttempt[];
|
|
@@ -123,6 +129,7 @@ interface ReviewGateResult {
|
|
|
123
129
|
readonly rev?: string;
|
|
124
130
|
}[];
|
|
125
131
|
readonly issues?: readonly string[];
|
|
132
|
+
readonly verification?: ReviewGateVerification;
|
|
126
133
|
readonly message: string;
|
|
127
134
|
}
|
|
128
135
|
interface ReviewLedger {
|
|
@@ -164,6 +171,22 @@ export declare function runReviewRunCommand(projectRoot: string, options: Review
|
|
|
164
171
|
export declare function readReviewLedger(projectRoot: string, slug: string): Promise<ReviewLedger>;
|
|
165
172
|
export declare function logReviewEntry(projectRoot: string, slug: string, input: ReviewCommandOptions): Promise<ReviewEntry>;
|
|
166
173
|
export declare function logReviewEvent(projectRoot: string, slug: string, input: ReviewCommandOptions): Promise<ReviewEvent>;
|
|
174
|
+
/**
|
|
175
|
+
* `wp review override` — the gated, auditable human escape hatch for the
|
|
176
|
+
* promotion approval gate. It records a `maintainer-override` approval that
|
|
177
|
+
* satisfies the gate distinctly (never as a strong/OpenCode-Go approval).
|
|
178
|
+
*
|
|
179
|
+
* Trust properties:
|
|
180
|
+
* - REFUSES unless a formal strong-reviewer gate has already REJECTED the exact
|
|
181
|
+
* current subject (precondition + target binding are the same lookup).
|
|
182
|
+
* - Requires an explicit `--reason` and `--confirm` — no accidental invocation.
|
|
183
|
+
* - Reviewer id `maintainer-override` is not in the `wp review log` reviewer
|
|
184
|
+
* enum, so neither `wp review log` nor `wp_blueprint_review_log` can mint it;
|
|
185
|
+
* this command is the only writer. The read-time policy in
|
|
186
|
+
* `#review/events` treats it as human-like, so its manual-stage record is a
|
|
187
|
+
* legitimate governance approval rather than a disguised machine approval.
|
|
188
|
+
*/
|
|
189
|
+
export declare function runReviewOverride(projectRoot: string, slug: string, input: ReviewCommandOptions): Promise<ReviewEvent>;
|
|
167
190
|
export declare function classifyProviderOutput(output: string): Pick<ReviewGateAttempt, "status" | "verdict" | "terminalClassification">;
|
|
168
191
|
export declare function reviewerFamily(model: string): ReviewReviewerId;
|
|
169
192
|
type GateProviderResultStage = ReviewStage | "approval-gate";
|
|
@@ -176,18 +199,26 @@ export declare function invokeProvider(input: {
|
|
|
176
199
|
readonly provider: ReviewProvider;
|
|
177
200
|
readonly targetSha: string;
|
|
178
201
|
readonly signal?: AbortSignal;
|
|
179
|
-
|
|
202
|
+
readonly authDataDir?: string;
|
|
203
|
+
}, executeReview?: typeof runReviewExecution, runCatalogCommand?: typeof runCommand, createTargetCheckout?: typeof createReviewTargetCheckout, getSharedCheckout?: () => ReviewTargetCheckout | undefined): Promise<{
|
|
180
204
|
output: string;
|
|
181
205
|
model: string;
|
|
182
206
|
reviewer: string;
|
|
183
207
|
stage?: GateProviderResultStage;
|
|
184
|
-
} & Pick<ReviewGateAttempt, "status" | "verdict" | "terminalClassification" | "failureCode" | "limitWindow" | "retryAfterMs">>;
|
|
208
|
+
} & Pick<ReviewGateAttempt, "status" | "verdict" | "terminalClassification" | "failureCode" | "providerErrorDetail" | "limitWindow" | "retryAfterMs">>;
|
|
185
209
|
export interface ReviewGateDependencies {
|
|
186
210
|
readonly createTargetCheckout?: typeof createReviewTargetCheckout;
|
|
187
211
|
readonly invokeProvider?: typeof invokeProvider;
|
|
188
212
|
readonly runCatalogCommand?: typeof runCommand;
|
|
189
213
|
readonly runReviewExecution?: typeof runReviewExecution;
|
|
190
214
|
readonly signal?: AbortSignal;
|
|
215
|
+
/** Test seam: resolve opencode-go accounts from the secret provider. */
|
|
216
|
+
readonly resolveOpencodeAccounts?: typeof resolveOpencodeAccountsGracefully;
|
|
217
|
+
/**
|
|
218
|
+
* Test seam: materialize an account's isolated auth store and return the
|
|
219
|
+
* `XDG_DATA_HOME` directory to inject for that account's spawns.
|
|
220
|
+
*/
|
|
221
|
+
readonly prepareOpencodeCredential?: (account: OpencodeAccount) => string;
|
|
191
222
|
}
|
|
192
223
|
/**
|
|
193
224
|
* Sanctioned fallback when `wp review gate` does not reach "approved" — points
|
|
@@ -198,6 +229,7 @@ export interface ReviewGateDependencies {
|
|
|
198
229
|
export declare function reviewGateNonConvergenceHint(slug: string, context?: {
|
|
199
230
|
readonly gateId?: string;
|
|
200
231
|
readonly failureCode?: string;
|
|
232
|
+
readonly status?: ReviewGateResult["status"];
|
|
201
233
|
}): string;
|
|
202
234
|
export declare function runReviewGate(projectRoot: string, slug: string, input: ReviewCommandOptions, dependencies?: ReviewGateDependencies): Promise<ReviewGateResult>;
|
|
203
235
|
export declare function buildReviewScoreboard(entries: readonly ReviewEntry[]): readonly ReviewScoreboardRow[];
|