infraweaver 0.1.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/LICENSE +661 -0
- package/README.md +244 -0
- package/dist/agents/claude.d.ts +113 -0
- package/dist/agents/claudePretoolGate.d.ts +137 -0
- package/dist/agents/dispatchMetrics.d.ts +77 -0
- package/dist/agents/gateServer.d.ts +7 -0
- package/dist/agents/index.d.ts +6 -0
- package/dist/agents/nativeFsDenies.d.ts +46 -0
- package/dist/agents/opencode.d.ts +284 -0
- package/dist/agents/opencodePlugin.d.ts +96 -0
- package/dist/agents/opencodeShared.d.ts +40 -0
- package/dist/agents/postRun.d.ts +126 -0
- package/dist/agents/reviewer.d.ts +41 -0
- package/dist/agents/sessionLabeler.d.ts +97 -0
- package/dist/agents/shared.d.ts +246 -0
- package/dist/agents/subagentModels.d.ts +19 -0
- package/dist/agents/tokenQuota.d.ts +118 -0
- package/dist/agents/writeGateSource.d.ts +22 -0
- package/dist/agents/writePolicy.d.ts +79 -0
- package/dist/brand.d.ts +67 -0
- package/dist/cli.mjs +247846 -0
- package/dist/external.d.ts +227 -0
- package/dist/i18n/scaffolding.d.ts +59 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +247061 -0
- package/dist/internal/index.d.ts +18 -0
- package/dist/internal.js +2398 -0
- package/dist/lifecycle.d.ts +2 -0
- package/dist/main.d.ts +8 -0
- package/dist/mcp/arkConfig.d.ts +1 -0
- package/dist/mcp/assess.d.ts +157 -0
- package/dist/mcp/capabilityContext.d.ts +71 -0
- package/dist/mcp/changeSummary.d.ts +52 -0
- package/dist/mcp/checkSuite.d.ts +27 -0
- package/dist/mcp/checkout.d.ts +92 -0
- package/dist/mcp/comment.d.ts +127 -0
- package/dist/mcp/commitInfo.d.ts +11 -0
- package/dist/mcp/crosswalk.d.ts +178 -0
- package/dist/mcp/crosswalkDigest.d.ts +1 -0
- package/dist/mcp/cyberEssentials.d.ts +24 -0
- package/dist/mcp/dashboard.d.ts +125 -0
- package/dist/mcp/dependencies.d.ts +12 -0
- package/dist/mcp/frameworks.d.ts +74 -0
- package/dist/mcp/geminiSanitizer.d.ts +28 -0
- package/dist/mcp/git.d.ts +60 -0
- package/dist/mcp/guardrails.d.ts +222 -0
- package/dist/mcp/issue.d.ts +20 -0
- package/dist/mcp/issueComments.d.ts +11 -0
- package/dist/mcp/issueEvents.d.ts +11 -0
- package/dist/mcp/issueInfo.d.ts +11 -0
- package/dist/mcp/labels.d.ts +14 -0
- package/dist/mcp/localContext.d.ts +26 -0
- package/dist/mcp/moduleExtraction.d.ts +79 -0
- package/dist/mcp/moduleTests.d.ts +106 -0
- package/dist/mcp/modules.d.ts +198 -0
- package/dist/mcp/output.d.ts +16 -0
- package/dist/mcp/pathSafety.d.ts +14 -0
- package/dist/mcp/policy.d.ts +50 -0
- package/dist/mcp/pr.d.ts +64 -0
- package/dist/mcp/prInfo.d.ts +11 -0
- package/dist/mcp/providerSchema.d.ts +52 -0
- package/dist/mcp/review.d.ts +212 -0
- package/dist/mcp/reviewComments.d.ts +245 -0
- package/dist/mcp/roots.d.ts +60 -0
- package/dist/mcp/scope.d.ts +26 -0
- package/dist/mcp/selectMode.d.ts +20 -0
- package/dist/mcp/server.d.ts +61 -0
- package/dist/mcp/shared.d.ts +62 -0
- package/dist/mcp/shell.d.ts +58 -0
- package/dist/mcp/staleFix.d.ts +138 -0
- package/dist/mcp/terraform/azurePrices.d.ts +8 -0
- package/dist/mcp/terraform/baseline.d.ts +57 -0
- package/dist/mcp/terraform/concernResult.d.ts +38 -0
- package/dist/mcp/terraform/cost.d.ts +55 -0
- package/dist/mcp/terraform/cspm.d.ts +80 -0
- package/dist/mcp/terraform/currency.d.ts +110 -0
- package/dist/mcp/terraform/decisions.d.ts +178 -0
- package/dist/mcp/terraform/eidas.d.ts +100 -0
- package/dist/mcp/terraform/evidence.d.ts +343 -0
- package/dist/mcp/terraform/findings.d.ts +76 -0
- package/dist/mcp/terraform/fixMemory.d.ts +81 -0
- package/dist/mcp/terraform/governance.d.ts +56 -0
- package/dist/mcp/terraform/hcl.d.ts +115 -0
- package/dist/mcp/terraform/iacLanguages.d.ts +120 -0
- package/dist/mcp/terraform/idleFloor.d.ts +78 -0
- package/dist/mcp/terraform/keyless.d.ts +68 -0
- package/dist/mcp/terraform/moduleDocs.d.ts +56 -0
- package/dist/mcp/terraform/nativeRules.d.ts +38 -0
- package/dist/mcp/terraform/nativeScan.d.ts +11 -0
- package/dist/mcp/terraform/noiseProfile.d.ts +100 -0
- package/dist/mcp/terraform/normalization.d.ts +52 -0
- package/dist/mcp/terraform/oscal.d.ts +103 -0
- package/dist/mcp/terraform/packs/azurerm-3-4.d.ts +2 -0
- package/dist/mcp/terraform/packs/cloudflare-4-5.d.ts +2 -0
- package/dist/mcp/terraform/paths.d.ts +28 -0
- package/dist/mcp/terraform/plan.d.ts +157 -0
- package/dist/mcp/terraform/planDrift.d.ts +199 -0
- package/dist/mcp/terraform/policyAuthor.d.ts +191 -0
- package/dist/mcp/terraform/prowlerOcsf.d.ts +136 -0
- package/dist/mcp/terraform/refactor.d.ts +183 -0
- package/dist/mcp/terraform/registry.d.ts +108 -0
- package/dist/mcp/terraform/risk.d.ts +41 -0
- package/dist/mcp/terraform/scanSession.d.ts +116 -0
- package/dist/mcp/terraform/scannerCache.d.ts +15 -0
- package/dist/mcp/terraform/scannerEgress.d.ts +72 -0
- package/dist/mcp/terraform/scannerJson.d.ts +13 -0
- package/dist/mcp/terraform/scanners.d.ts +205 -0
- package/dist/mcp/terraform/signing.d.ts +61 -0
- package/dist/mcp/terraform/stateLocking.d.ts +18 -0
- package/dist/mcp/terraform/subprocess.d.ts +43 -0
- package/dist/mcp/terraform/suppressions.d.ts +90 -0
- package/dist/mcp/terraform/taxonomy.d.ts +39 -0
- package/dist/mcp/terraform/tfquery.d.ts +47 -0
- package/dist/mcp/terraform/toolchainIntegrity.d.ts +82 -0
- package/dist/mcp/terraform/tools/authorPolicy.d.ts +59 -0
- package/dist/mcp/terraform/tools/detectPlanDrift.d.ts +20 -0
- package/dist/mcp/terraform/tools/emitCost.d.ts +11 -0
- package/dist/mcp/terraform/tools/emitSarif.d.ts +14 -0
- package/dist/mcp/terraform/tools/fixMemory.d.ts +19 -0
- package/dist/mcp/terraform/tools/infracostDiff.d.ts +22 -0
- package/dist/mcp/terraform/tools/ingestExternalFindings.d.ts +21 -0
- package/dist/mcp/terraform/tools/moduleLookup.d.ts +14 -0
- package/dist/mcp/terraform/tools/moduleSearch.d.ts +23 -0
- package/dist/mcp/terraform/tools/plan.d.ts +59 -0
- package/dist/mcp/terraform/tools/providerUpgrade.d.ts +55 -0
- package/dist/mcp/terraform/tools/readFindings.d.ts +17 -0
- package/dist/mcp/terraform/tools/scan.d.ts +114 -0
- package/dist/mcp/terraform/tools/validate.d.ts +31 -0
- package/dist/mcp/terraform/tools/verifyRemediation.d.ts +31 -0
- package/dist/mcp/terraform/tools/versionCurrency.d.ts +5 -0
- package/dist/mcp/terraform/tools.d.ts +16 -0
- package/dist/mcp/terraform/transformPacks.d.ts +267 -0
- package/dist/mcp/terraform/types.d.ts +226 -0
- package/dist/mcp/terraform/verification.d.ts +81 -0
- package/dist/mcp/terraform/vex.d.ts +115 -0
- package/dist/mcp/terraform/writeOnly.d.ts +67 -0
- package/dist/mcp/terraform.d.ts +51 -0
- package/dist/mcp/terratest.d.ts +85 -0
- package/dist/mcp/untrustedContent.d.ts +76 -0
- package/dist/mcp/upload.d.ts +8 -0
- package/dist/models.d.ts +181 -0
- package/dist/modes/address-reviews.d.ts +2 -0
- package/dist/modes/assess.d.ts +2 -0
- package/dist/modes/build.d.ts +2 -0
- package/dist/modes/compliance-audit.d.ts +2 -0
- package/dist/modes/cost-optimization.d.ts +2 -0
- package/dist/modes/drift-detect.d.ts +2 -0
- package/dist/modes/fix.d.ts +2 -0
- package/dist/modes/incremental-review.d.ts +2 -0
- package/dist/modes/index.d.ts +24 -0
- package/dist/modes/modernize-deprecated.d.ts +2 -0
- package/dist/modes/plan.d.ts +2 -0
- package/dist/modes/policy-gate.d.ts +2 -0
- package/dist/modes/prFormats.d.ts +3 -0
- package/dist/modes/refactor.d.ts +2 -0
- package/dist/modes/refresh-remediation.d.ts +2 -0
- package/dist/modes/remediate-and-refactor.d.ts +2 -0
- package/dist/modes/remediate.d.ts +2 -0
- package/dist/modes/resolve-conflicts.d.ts +2 -0
- package/dist/modes/review.d.ts +2 -0
- package/dist/modes/summarize-pr.d.ts +2 -0
- package/dist/modes/task.d.ts +2 -0
- package/dist/modes/terraform-code-review.d.ts +2 -0
- package/dist/modes/types.d.ts +9 -0
- package/dist/modes/update-dependencies.d.ts +2 -0
- package/dist/prep/index.d.ts +7 -0
- package/dist/prep/installNodeDependencies.d.ts +2 -0
- package/dist/prep/installPythonDependencies.d.ts +2 -0
- package/dist/prep/types.d.ts +31 -0
- package/dist/reviewQuality.d.ts +107 -0
- package/dist/skills/terraform-best-practices/SKILL.md +379 -0
- package/dist/toolState.d.ts +123 -0
- package/dist/utils/activity.d.ts +40 -0
- package/dist/utils/agent.d.ts +47 -0
- package/dist/utils/agentHangReport.d.ts +38 -0
- package/dist/utils/annotations.d.ts +17 -0
- package/dist/utils/apiFetch.d.ts +11 -0
- package/dist/utils/apiKeys.d.ts +48 -0
- package/dist/utils/apiUrl.d.ts +28 -0
- package/dist/utils/assets.d.ts +8 -0
- package/dist/utils/baseRefConfig.d.ts +121 -0
- package/dist/utils/billingErrors.d.ts +85 -0
- package/dist/utils/body.d.ts +34 -0
- package/dist/utils/buildInfraweaverFooter.d.ts +29 -0
- package/dist/utils/byokFallback.d.ts +85 -0
- package/dist/utils/changeImpact.d.ts +101 -0
- package/dist/utils/claudeSubscription.d.ts +30 -0
- package/dist/utils/cli.d.ts +10 -0
- package/dist/utils/cloudClient.d.ts +33 -0
- package/dist/utils/cloudFetchCore.d.ts +42 -0
- package/dist/utils/cloudReport.d.ts +114 -0
- package/dist/utils/codexHome.d.ts +29 -0
- package/dist/utils/codexOAuth.d.ts +60 -0
- package/dist/utils/diffCoverage.d.ts +63 -0
- package/dist/utils/env.d.ts +46 -0
- package/dist/utils/errorReport.d.ts +17 -0
- package/dist/utils/exitHandler.d.ts +8 -0
- package/dist/utils/fixDoubleEscapedString.d.ts +1 -0
- package/dist/utils/gitAuth.d.ts +84 -0
- package/dist/utils/gitAuthServer.d.ts +24 -0
- package/dist/utils/github.d.ts +104 -0
- package/dist/utils/globals.d.ts +3 -0
- package/dist/utils/infraweaverConfig.d.ts +56 -0
- package/dist/utils/install.d.ts +37 -0
- package/dist/utils/instructions.d.ts +48 -0
- package/dist/utils/keylessOidc.d.ts +28 -0
- package/dist/utils/leapingComment.d.ts +11 -0
- package/dist/utils/learnings.d.ts +62 -0
- package/dist/utils/learningsTruncate.d.ts +25 -0
- package/dist/utils/lifecycle.d.ts +57 -0
- package/dist/utils/log.d.ts +111 -0
- package/dist/utils/modelResolver.d.ts +63 -0
- package/dist/utils/moduleFetch.d.ts +39 -0
- package/dist/utils/normalizeEnv.d.ts +30 -0
- package/dist/utils/openCodeModels.d.ts +11 -0
- package/dist/utils/openaiCompatible.d.ts +26 -0
- package/dist/utils/opentofuMcp.d.ts +50 -0
- package/dist/utils/overrides.d.ts +40 -0
- package/dist/utils/packageManager.d.ts +49 -0
- package/dist/utils/patchWorkflowRunFields.d.ts +29 -0
- package/dist/utils/payload.d.ts +249 -0
- package/dist/utils/prSummary.d.ts +61 -0
- package/dist/utils/presets.d.ts +36 -0
- package/dist/utils/progressComment.d.ts +146 -0
- package/dist/utils/providerErrors.d.ts +31 -0
- package/dist/utils/proxyModel.d.ts +76 -0
- package/dist/utils/rangeDiff.d.ts +51 -0
- package/dist/utils/redact.d.ts +33 -0
- package/dist/utils/registryAuth.d.ts +52 -0
- package/dist/utils/remediationCommand.d.ts +58 -0
- package/dist/utils/retry.d.ts +38 -0
- package/dist/utils/reviewCleanup.d.ts +14 -0
- package/dist/utils/run.d.ts +9 -0
- package/dist/utils/runContext.d.ts +112 -0
- package/dist/utils/runContextData.d.ts +42 -0
- package/dist/utils/runErrorRenderer.d.ts +64 -0
- package/dist/utils/runLifecycle.d.ts +89 -0
- package/dist/utils/runStartupLog.d.ts +15 -0
- package/dist/utils/secrets.d.ts +34 -0
- package/dist/utils/setup.d.ts +90 -0
- package/dist/utils/shell.d.ts +44 -0
- package/dist/utils/skills.d.ts +10 -0
- package/dist/utils/subprocess.d.ts +81 -0
- package/dist/utils/terraformMcp.d.ts +46 -0
- package/dist/utils/time.d.ts +15 -0
- package/dist/utils/timer.d.ts +23 -0
- package/dist/utils/todoTracking.d.ts +16 -0
- package/dist/utils/token.d.ts +52 -0
- package/dist/utils/toolLicensing.d.ts +56 -0
- package/dist/utils/toolSelection.d.ts +73 -0
- package/dist/utils/toon.d.ts +16 -0
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/versioning.d.ts +7 -0
- package/dist/utils/vertex.d.ts +16 -0
- package/dist/utils/workflow.d.ts +13 -0
- package/package.json +132 -0
- package/src/agents/claude.ts +1588 -0
- package/src/agents/claudePretoolGate.ts +286 -0
- package/src/agents/dispatchMetrics.ts +162 -0
- package/src/agents/gateServer.ts +178 -0
- package/src/agents/index.ts +10 -0
- package/src/agents/nativeFsDenies.ts +164 -0
- package/src/agents/opencode.ts +1600 -0
- package/src/agents/opencodePlugin.ts +304 -0
- package/src/agents/opencodeShared.ts +134 -0
- package/src/agents/postRun.ts +615 -0
- package/src/agents/reviewer.ts +134 -0
- package/src/agents/sessionLabeler.ts +185 -0
- package/src/agents/shared.ts +377 -0
- package/src/agents/subagentModels.ts +40 -0
- package/src/agents/tokenQuota.ts +203 -0
- package/src/agents/writeGateSource.ts +148 -0
- package/src/agents/writePolicy.ts +132 -0
- package/src/brand.ts +84 -0
- package/src/cli.ts +117 -0
- package/src/commands/gha.ts +188 -0
- package/src/commands/mcp.ts +126 -0
- package/src/commands/verifyEvidence.ts +100 -0
- package/src/entry.ts +7 -0
- package/src/entryPost.ts +109 -0
- package/src/external.ts +308 -0
- package/src/i18n/scaffolding.ts +191 -0
- package/src/index.ts +7 -0
- package/src/internal/index.ts +74 -0
- package/src/lifecycle.ts +2 -0
- package/src/main.ts +815 -0
- package/src/mcp/__fixtures__/infraweaver-scratch-pr-49-review-3485940013.json +110 -0
- package/src/mcp/__fixtures__/infraweaver-scratch-pr-64-review-3531000326.json +14 -0
- package/src/mcp/__fixtures__/infraweaver-test-repo-pr-1.diff.json +67 -0
- package/src/mcp/__snapshots__/checkout.test.ts.snap +109 -0
- package/src/mcp/__snapshots__/reviewComments.test.ts.snap +71 -0
- package/src/mcp/arkConfig.ts +7 -0
- package/src/mcp/assess.ts +723 -0
- package/src/mcp/capabilityContext.ts +80 -0
- package/src/mcp/changeSummary.ts +145 -0
- package/src/mcp/checkSuite.ts +261 -0
- package/src/mcp/checkout.ts +1015 -0
- package/src/mcp/comment.ts +683 -0
- package/src/mcp/commitInfo.ts +57 -0
- package/src/mcp/crosswalk.ts +1253 -0
- package/src/mcp/crosswalkDigest.ts +5 -0
- package/src/mcp/cyberEssentials.ts +60 -0
- package/src/mcp/dashboard.ts +232 -0
- package/src/mcp/dependencies.ts +190 -0
- package/src/mcp/frameworks.ts +236 -0
- package/src/mcp/geminiSanitizer.ts +212 -0
- package/src/mcp/git.ts +1116 -0
- package/src/mcp/guardrails.ts +771 -0
- package/src/mcp/issue.ts +74 -0
- package/src/mcp/issueComments.ts +48 -0
- package/src/mcp/issueEvents.ts +100 -0
- package/src/mcp/issueInfo.ts +72 -0
- package/src/mcp/labels.ts +35 -0
- package/src/mcp/localContext.ts +65 -0
- package/src/mcp/localServer.ts +217 -0
- package/src/mcp/moduleExtraction.ts +368 -0
- package/src/mcp/moduleTests.ts +421 -0
- package/src/mcp/modules.ts +752 -0
- package/src/mcp/output.ts +71 -0
- package/src/mcp/pathSafety.ts +28 -0
- package/src/mcp/policy.ts +226 -0
- package/src/mcp/pr.ts +238 -0
- package/src/mcp/prInfo.ts +91 -0
- package/src/mcp/providerSchema.ts +175 -0
- package/src/mcp/review.ts +1081 -0
- package/src/mcp/reviewComments.ts +1137 -0
- package/src/mcp/roots.ts +217 -0
- package/src/mcp/scope.ts +82 -0
- package/src/mcp/selectMode.ts +207 -0
- package/src/mcp/server.ts +498 -0
- package/src/mcp/shared.ts +120 -0
- package/src/mcp/shell.ts +631 -0
- package/src/mcp/staleFix.ts +557 -0
- package/src/mcp/terraform/__snapshots__/moduleDocs.test.ts.snap +40 -0
- package/src/mcp/terraform/azurePrices.ts +38 -0
- package/src/mcp/terraform/baseline.ts +172 -0
- package/src/mcp/terraform/concernResult.ts +111 -0
- package/src/mcp/terraform/cost.ts +175 -0
- package/src/mcp/terraform/cspm.ts +326 -0
- package/src/mcp/terraform/currency.ts +354 -0
- package/src/mcp/terraform/decisions.ts +590 -0
- package/src/mcp/terraform/eidas.ts +134 -0
- package/src/mcp/terraform/evidence.ts +771 -0
- package/src/mcp/terraform/findings.ts +383 -0
- package/src/mcp/terraform/fixMemory.ts +253 -0
- package/src/mcp/terraform/governance.ts +231 -0
- package/src/mcp/terraform/hcl.ts +273 -0
- package/src/mcp/terraform/iacLanguages.ts +431 -0
- package/src/mcp/terraform/idleFloor.ts +304 -0
- package/src/mcp/terraform/keyless.ts +123 -0
- package/src/mcp/terraform/moduleDocs.ts +303 -0
- package/src/mcp/terraform/nativeRules.ts +152 -0
- package/src/mcp/terraform/nativeScan.ts +67 -0
- package/src/mcp/terraform/noiseProfile.ts +183 -0
- package/src/mcp/terraform/normalization.ts +265 -0
- package/src/mcp/terraform/oscal.ts +283 -0
- package/src/mcp/terraform/packs/azurerm-3-4.ts +382 -0
- package/src/mcp/terraform/packs/cloudflare-4-5.ts +114 -0
- package/src/mcp/terraform/paths.ts +59 -0
- package/src/mcp/terraform/plan.ts +377 -0
- package/src/mcp/terraform/planDrift.ts +520 -0
- package/src/mcp/terraform/policyAuthor.ts +886 -0
- package/src/mcp/terraform/prowlerOcsf.ts +380 -0
- package/src/mcp/terraform/refactor.ts +879 -0
- package/src/mcp/terraform/registry.ts +317 -0
- package/src/mcp/terraform/risk.ts +99 -0
- package/src/mcp/terraform/scanSession.ts +242 -0
- package/src/mcp/terraform/scannerCache.ts +86 -0
- package/src/mcp/terraform/scannerEgress.ts +141 -0
- package/src/mcp/terraform/scannerJson.ts +22 -0
- package/src/mcp/terraform/scanners.ts +1134 -0
- package/src/mcp/terraform/signing.ts +151 -0
- package/src/mcp/terraform/stateLocking.ts +106 -0
- package/src/mcp/terraform/subprocess.ts +102 -0
- package/src/mcp/terraform/suppressions.ts +551 -0
- package/src/mcp/terraform/taxonomy.ts +0 -0
- package/src/mcp/terraform/tfquery.ts +159 -0
- package/src/mcp/terraform/toolchainIntegrity.ts +151 -0
- package/src/mcp/terraform/tools/authorPolicy.ts +185 -0
- package/src/mcp/terraform/tools/detectPlanDrift.ts +279 -0
- package/src/mcp/terraform/tools/emitCost.ts +130 -0
- package/src/mcp/terraform/tools/emitSarif.ts +108 -0
- package/src/mcp/terraform/tools/fixMemory.ts +82 -0
- package/src/mcp/terraform/tools/infracostDiff.ts +110 -0
- package/src/mcp/terraform/tools/ingestExternalFindings.ts +259 -0
- package/src/mcp/terraform/tools/moduleLookup.ts +86 -0
- package/src/mcp/terraform/tools/moduleSearch.ts +46 -0
- package/src/mcp/terraform/tools/plan.ts +384 -0
- package/src/mcp/terraform/tools/providerUpgrade.ts +217 -0
- package/src/mcp/terraform/tools/readFindings.ts +138 -0
- package/src/mcp/terraform/tools/scan.ts +303 -0
- package/src/mcp/terraform/tools/validate.ts +160 -0
- package/src/mcp/terraform/tools/verifyRemediation.ts +190 -0
- package/src/mcp/terraform/tools/versionCurrency.ts +64 -0
- package/src/mcp/terraform/tools.ts +62 -0
- package/src/mcp/terraform/transformPacks.ts +594 -0
- package/src/mcp/terraform/types.ts +382 -0
- package/src/mcp/terraform/verification.ts +150 -0
- package/src/mcp/terraform/vex.ts +367 -0
- package/src/mcp/terraform/writeOnly.ts +252 -0
- package/src/mcp/terraform.ts +52 -0
- package/src/mcp/terratest.ts +215 -0
- package/src/mcp/untrustedContent.ts +126 -0
- package/src/mcp/upload.ts +123 -0
- package/src/models.ts +753 -0
- package/src/modes/address-reviews.ts +35 -0
- package/src/modes/assess.ts +27 -0
- package/src/modes/build.ts +86 -0
- package/src/modes/compliance-audit.ts +25 -0
- package/src/modes/cost-optimization.ts +36 -0
- package/src/modes/drift-detect.ts +31 -0
- package/src/modes/fix.ts +29 -0
- package/src/modes/incremental-review.ts +103 -0
- package/src/modes/index.ts +94 -0
- package/src/modes/modernize-deprecated.ts +36 -0
- package/src/modes/plan.ts +21 -0
- package/src/modes/policy-gate.ts +27 -0
- package/src/modes/prFormats.ts +325 -0
- package/src/modes/refactor.ts +74 -0
- package/src/modes/refresh-remediation.ts +40 -0
- package/src/modes/remediate-and-refactor.ts +42 -0
- package/src/modes/remediate.ts +78 -0
- package/src/modes/resolve-conflicts.ts +32 -0
- package/src/modes/review.ts +135 -0
- package/src/modes/summarize-pr.ts +42 -0
- package/src/modes/task.ts +26 -0
- package/src/modes/terraform-code-review.ts +68 -0
- package/src/modes/types.ts +17 -0
- package/src/modes/update-dependencies.ts +44 -0
- package/src/prep/index.ts +96 -0
- package/src/prep/installNodeDependencies.ts +251 -0
- package/src/prep/installPythonDependencies.ts +235 -0
- package/src/prep/types.ts +38 -0
- package/src/reviewQuality.ts +237 -0
- package/src/runCli.ts +335 -0
- package/src/skills/terraform-best-practices/SKILL.md +379 -0
- package/src/toolState.ts +241 -0
- package/src/utils/activity.ts +210 -0
- package/src/utils/agent.ts +245 -0
- package/src/utils/agentHangReport.ts +180 -0
- package/src/utils/annotations.ts +56 -0
- package/src/utils/apiFetch.ts +19 -0
- package/src/utils/apiKeys.ts +305 -0
- package/src/utils/apiUrl.ts +42 -0
- package/src/utils/assets.ts +123 -0
- package/src/utils/baseRefConfig.ts +254 -0
- package/src/utils/billingErrors.ts +210 -0
- package/src/utils/body.ts +168 -0
- package/src/utils/buildInfraweaverFooter.ts +104 -0
- package/src/utils/byokFallback.ts +137 -0
- package/src/utils/changeImpact.ts +330 -0
- package/src/utils/claudeSubscription.ts +93 -0
- package/src/utils/cli.ts +36 -0
- package/src/utils/cloudClient.ts +57 -0
- package/src/utils/cloudFetchCore.ts +139 -0
- package/src/utils/cloudReport.ts +316 -0
- package/src/utils/codexHome.ts +204 -0
- package/src/utils/codexOAuth.ts +154 -0
- package/src/utils/codexRefreshDetect.ts +36 -0
- package/src/utils/diffCoverage.ts +404 -0
- package/src/utils/env.ts +84 -0
- package/src/utils/errorReport.ts +94 -0
- package/src/utils/exitHandler.ts +35 -0
- package/src/utils/fixDoubleEscapedString.ts +9 -0
- package/src/utils/ghaCore.ts +13 -0
- package/src/utils/gitAuth.ts +268 -0
- package/src/utils/gitAuthServer.ts +186 -0
- package/src/utils/github.ts +676 -0
- package/src/utils/globals.ts +9 -0
- package/src/utils/humanEditCapture.ts +198 -0
- package/src/utils/infraweaverConfig.ts +202 -0
- package/src/utils/install.ts +279 -0
- package/src/utils/instructions.ts +640 -0
- package/src/utils/keylessOidc.ts +47 -0
- package/src/utils/leapingComment.ts +20 -0
- package/src/utils/learnings.ts +145 -0
- package/src/utils/learningsTruncate.ts +42 -0
- package/src/utils/lifecycle.ts +198 -0
- package/src/utils/log.ts +452 -0
- package/src/utils/modelResolver.ts +105 -0
- package/src/utils/moduleFetch.ts +138 -0
- package/src/utils/normalizeEnv.ts +106 -0
- package/src/utils/openCodeModels.ts +86 -0
- package/src/utils/openaiCompatible.ts +52 -0
- package/src/utils/opentofuMcp.ts +61 -0
- package/src/utils/overrides.ts +100 -0
- package/src/utils/packageManager.ts +257 -0
- package/src/utils/patchWorkflowRunFields.ts +173 -0
- package/src/utils/payload.ts +1206 -0
- package/src/utils/prSummary.ts +148 -0
- package/src/utils/presets.ts +105 -0
- package/src/utils/progressComment.ts +266 -0
- package/src/utils/providerErrors.ts +189 -0
- package/src/utils/proxyModel.ts +239 -0
- package/src/utils/rangeDiff.ts +182 -0
- package/src/utils/redact.ts +86 -0
- package/src/utils/registryAuth.ts +115 -0
- package/src/utils/remediationCommand.ts +144 -0
- package/src/utils/retry.ts +180 -0
- package/src/utils/reviewCleanup.ts +116 -0
- package/src/utils/run.ts +100 -0
- package/src/utils/runContext.ts +276 -0
- package/src/utils/runContextData.ts +121 -0
- package/src/utils/runErrorRenderer.ts +282 -0
- package/src/utils/runFixture.ts +91 -0
- package/src/utils/runLifecycle.ts +360 -0
- package/src/utils/runStartupLog.ts +67 -0
- package/src/utils/secrets.ts +208 -0
- package/src/utils/setup.ts +368 -0
- package/src/utils/shell.ts +132 -0
- package/src/utils/skills.ts +67 -0
- package/src/utils/subprocess.ts +474 -0
- package/src/utils/terraformMcp.ts +99 -0
- package/src/utils/time.ts +59 -0
- package/src/utils/timer.ts +72 -0
- package/src/utils/todoTracking.ts +168 -0
- package/src/utils/token.ts +294 -0
- package/src/utils/toolLicensing.ts +152 -0
- package/src/utils/toolSelection.ts +239 -0
- package/src/utils/toon.ts +76 -0
- package/src/utils/version.ts +10 -0
- package/src/utils/versioning.ts +44 -0
- package/src/utils/vertex.ts +94 -0
- package/src/utils/workflow.ts +25 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code `PreToolUse` hook source — written into `ctx.tmpdir` at runtime
|
|
3
|
+
* and registered via a tmpdir-scoped `settings.json` referenced by
|
|
4
|
+
* `--settings <path>` (see action/agents/claude.ts).
|
|
5
|
+
*
|
|
6
|
+
* Closes the subagent → state-mutating MCP tool path that motivated the
|
|
7
|
+
* 2026-05-18 zed-industries/cloud incident (`terralens` lens called
|
|
8
|
+
* `checkout_pr` mid-review and the orchestrator's next push clobbered an
|
|
9
|
+
* unrelated branch). Pairs with the `tool.execute.before` hook in
|
|
10
|
+
* action/agents/opencodePlugin.ts; both runtimes embed the same per-run deny
|
|
11
|
+
* set, derived from each MCP tool's `mutates` flag (`subagentDeniedToolNames`
|
|
12
|
+
* in action/mcp/server.ts).
|
|
13
|
+
*
|
|
14
|
+
* PreToolUse hook contract (verified against yasasbanukaofficial/claude-code
|
|
15
|
+
* `src/utils/hooks/hooksConfigManager.ts` and `src/utils/hooks.ts`):
|
|
16
|
+
* - stdin: JSON with `hook_event_name: "PreToolUse"`, `tool_name`,
|
|
17
|
+
* `tool_input`, `tool_use_id`, `session_id`, `cwd`, `transcript_path`,
|
|
18
|
+
* and crucially `agent_id` / `agent_type` populated when the call
|
|
19
|
+
* originates from a subagent (set by the SDK when a Task/Agent
|
|
20
|
+
* dispatches a tool — see `createBaseHookInput` in claude-code source).
|
|
21
|
+
* - exit 0 → allow, no output shown
|
|
22
|
+
* - exit 2 → block tool call AND show stderr to model (this is the path
|
|
23
|
+
* we want for the deny case — the subagent gets a clear refusal it can
|
|
24
|
+
* reason about and pick a different action)
|
|
25
|
+
* - other → show stderr to user only, continue with tool call
|
|
26
|
+
*
|
|
27
|
+
* The hook itself is intentionally tiny: stdin → JSON → check `agent_id`
|
|
28
|
+
* presence + `tool_name` against the deny list → exit 0 or 2. No deps.
|
|
29
|
+
*
|
|
30
|
+
* Why the script source is a string template, not a separate `.ts` file
|
|
31
|
+
* shipped with the action: the action runs as a published npm package; at
|
|
32
|
+
* install time we don't have the source on disk in a stable place. Embedding
|
|
33
|
+
* the source into `dist/main.mjs` and writing it out per-run keeps the path
|
|
34
|
+
* inside `ctx.tmpdir` (where `--settings` can find it) and survives bundle
|
|
35
|
+
* minification.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import { WRITE_GATE_HELPERS_SOURCE } from "#app/agents/writeGateSource";
|
|
39
|
+
import { PRODUCT_NAME } from "#app/brand";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The pinned `@anthropic-ai/claude-code` version against which the subagent
|
|
43
|
+
* gate's `agent_id` discriminator was last verified (see the contract notes in
|
|
44
|
+
* the gate source below). The gate fails OPEN for subagents if claude-code ever
|
|
45
|
+
* stops populating `agent_id` in the PreToolUse hook payload, so a version bump
|
|
46
|
+
* must be paired with a re-verification of `createBaseHookInput`.
|
|
47
|
+
*
|
|
48
|
+
* `claudePretoolGate.test.ts` asserts this equals the version pinned in
|
|
49
|
+
* `package.json` — that test fails on any bump, forcing the re-verification
|
|
50
|
+
* before the pin and this constant are updated together.
|
|
51
|
+
*
|
|
52
|
+
* 2.1.220 verified 2026-07-28 against the schema embedded in the shipped
|
|
53
|
+
* binary: the base hook input still declares `agent_id` as
|
|
54
|
+
* `string().optional().describe("Subagent identifier. Present only when the
|
|
55
|
+
* hook fires from within a subagent (e.g., a tool called by an AgentTool
|
|
56
|
+
* worker). Absent for the main thread, even in --agent sessions. Use this
|
|
57
|
+
* field (not agent_type) to distinguish subagent calls from main-thread
|
|
58
|
+
* calls.")` — exactly the discriminator the gate relies on, byte-for-byte
|
|
59
|
+
* unchanged from the 2.1.198 verification (2026-07-10), itself unchanged from
|
|
60
|
+
* 2.1.195 (2026-07-01) and 2.1.190 (2026-06-25), which was more explicit than
|
|
61
|
+
* the 2.1.177 schema it replaced. The 2.1.220 pass additionally confirmed the
|
|
62
|
+
* field is still POPULATED and not merely declared — the minified bundle
|
|
63
|
+
* assigns `agent_id` from real identifier sources (`e.agentId`,
|
|
64
|
+
* `e.parentAgentId`, `e.data.agentId`), not a hardcoded empty value.
|
|
65
|
+
*
|
|
66
|
+
* Scope of this verification, stated honestly: it is static inspection of the
|
|
67
|
+
* shipped binary. No CI job exercises the Claude subagent gate against a live
|
|
68
|
+
* agent — `security-tests.yml` is deliberately opencode-only (free keyless
|
|
69
|
+
* model; Claude Code cannot use an `opencode/*` gateway model). So this pin,
|
|
70
|
+
* plus the tripwire test, IS the contract check. Keep both.
|
|
71
|
+
*/
|
|
72
|
+
export const CLAUDE_CODE_AGENT_ID_VERIFIED_VERSION = "2.1.220" as const;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Source written to `<ctx.tmpdir>/infraweaver-pretool-gate.mjs`. Plain ESM,
|
|
76
|
+
* no TypeScript, no dependencies — node executes it directly via the
|
|
77
|
+
* `#!/usr/bin/env node` shebang and the executable bit set by the harness.
|
|
78
|
+
*
|
|
79
|
+
* `deniedTools` is the per-run deny set derived from each MCP tool's `mutates`
|
|
80
|
+
* flag (see `subagentDeniedToolNames`), embedded into the hook's `Set` literal so
|
|
81
|
+
* the gate fences exactly the mutating tools registered for this run — no
|
|
82
|
+
* hand-maintained list to drift.
|
|
83
|
+
*/
|
|
84
|
+
export const CLAUDE_PRETOOL_GATE_FILENAME =
|
|
85
|
+
"infraweaver-pretool-gate.mjs" as const;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Claude Code's native file-writing tools. The gate intercepts these to enforce
|
|
89
|
+
* `allowed_paths` / `protected_paths` at WRITE time; without them the only
|
|
90
|
+
* enforcement is the push-time diff scan, which lets a disallowed file exist in
|
|
91
|
+
* the tree for the rest of the run (see action/agents/writePolicy.ts).
|
|
92
|
+
*
|
|
93
|
+
* `MultiEdit` is included even though recent claude-code folds it into `Edit` —
|
|
94
|
+
* an extra name in the matcher is inert, a missing one is a hole.
|
|
95
|
+
*/
|
|
96
|
+
export const CLAUDE_WRITE_TOOL_NAMES = [
|
|
97
|
+
"Write",
|
|
98
|
+
"Edit",
|
|
99
|
+
"MultiEdit",
|
|
100
|
+
"NotebookEdit",
|
|
101
|
+
] as const;
|
|
102
|
+
|
|
103
|
+
export function buildClaudePretoolGateSource(
|
|
104
|
+
deniedTools: readonly string[],
|
|
105
|
+
writePolicyAbsolutePath: string,
|
|
106
|
+
): string {
|
|
107
|
+
return `#!/usr/bin/env node
|
|
108
|
+
// AUTOGENERATED by ${PRODUCT_NAME}. PreToolUse hook that (a) hard-blocks
|
|
109
|
+
// state-mutating MCP tool calls from subagents and (b) enforces the
|
|
110
|
+
// allowed_paths / protected_paths policy at write time.
|
|
111
|
+
// See action/agents/claudePretoolGate.ts.
|
|
112
|
+
|
|
113
|
+
const SUBAGENT_DENIED_TOOLS = new Set(${JSON.stringify(deniedTools)});
|
|
114
|
+
const WRITE_TOOLS = new Set(${JSON.stringify(CLAUDE_WRITE_TOOL_NAMES)});
|
|
115
|
+
const WRITE_POLICY_PATH = ${JSON.stringify(writePolicyAbsolutePath)};
|
|
116
|
+
${WRITE_GATE_HELPERS_SOURCE}
|
|
117
|
+
function stripMcpPrefix(toolName) {
|
|
118
|
+
if (toolName.startsWith("mcp__infraweaver__")) return toolName.slice("mcp__infraweaver__".length);
|
|
119
|
+
return toolName;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** the write path a given tool call targets, across the write-tool shapes. */
|
|
123
|
+
function targetPathOf(toolInput) {
|
|
124
|
+
if (!toolInput || typeof toolInput !== "object") return "";
|
|
125
|
+
if (typeof toolInput.file_path === "string") return toolInput.file_path;
|
|
126
|
+
if (typeof toolInput.notebook_path === "string") return toolInput.notebook_path;
|
|
127
|
+
return "";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let stdin = "";
|
|
131
|
+
process.stdin.setEncoding("utf8");
|
|
132
|
+
process.stdin.on("data", (chunk) => {
|
|
133
|
+
stdin += chunk;
|
|
134
|
+
});
|
|
135
|
+
process.stdin.on("end", () => {
|
|
136
|
+
let payload;
|
|
137
|
+
try {
|
|
138
|
+
payload = JSON.parse(stdin);
|
|
139
|
+
} catch {
|
|
140
|
+
// malformed input — exit non-blocking so the run continues. user-only
|
|
141
|
+
// stderr per the hook contract.
|
|
142
|
+
process.stderr.write("infraweaver-pretool-gate: malformed stdin JSON\\n");
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
const toolName = typeof payload?.tool_name === "string" ? payload.tool_name : "";
|
|
146
|
+
|
|
147
|
+
// claude-code populates agent_id whenever a tool call originates inside
|
|
148
|
+
// a Task/Agent subagent dispatch (createBaseHookInput in claude-code
|
|
149
|
+
// source) — see the full contract note above the MCP deny branch below.
|
|
150
|
+
const agentId = typeof payload?.agent_id === "string" ? payload.agent_id : "";
|
|
151
|
+
|
|
152
|
+
// write-path enforcement. deliberately BEFORE the subagent MCP deny
|
|
153
|
+
// below: a disallowed write is disallowed whoever makes it, and the
|
|
154
|
+
// orchestrator does most of the writing. no guarded mode selected yet (or a
|
|
155
|
+
// non-guarded mode) leaves ORCHESTRATOR writes untouched.
|
|
156
|
+
if (WRITE_TOOLS.has(toolName)) {
|
|
157
|
+
// subagents are read-only, unconditionally — before the path policy, which
|
|
158
|
+
// only constrains paths for guarded modes and would otherwise let a
|
|
159
|
+
// Task-dispatched lens Write/Edit the orchestrator's tree mid-review (the
|
|
160
|
+
// read-only contract was previously prose-only for native file writes).
|
|
161
|
+
if (agentId) {
|
|
162
|
+
process.stderr.write(
|
|
163
|
+
"subagent attempted a native file write via '" + toolName + "'. " +
|
|
164
|
+
"subagents are read-only: they share the orchestrator's in-process working tree, " +
|
|
165
|
+
"and file mutations are reserved for the orchestrator. " +
|
|
166
|
+
"report findings back to the orchestrator and let it perform the write.\\n"
|
|
167
|
+
);
|
|
168
|
+
process.exit(2);
|
|
169
|
+
}
|
|
170
|
+
let refusal = null;
|
|
171
|
+
try {
|
|
172
|
+
const policy = readWritePolicy(WRITE_POLICY_PATH);
|
|
173
|
+
if (!policy) process.exit(0);
|
|
174
|
+
const rawPath = targetPathOf(payload?.tool_input);
|
|
175
|
+
// a write tool with no resolvable path (an unexpected shape from a future
|
|
176
|
+
// claude-code version) is passed through — the push-time guardrails still
|
|
177
|
+
// catch it, and failing closed on an unknown shape would break the run.
|
|
178
|
+
if (!rawPath) process.exit(0);
|
|
179
|
+
refusal = refuseWrite(policy, rawPath, policy.root);
|
|
180
|
+
} catch (err) {
|
|
181
|
+
// an unexpected fault while DECIDING must not become an allow. exit 1
|
|
182
|
+
// would mean "show the user and continue with the tool call" per the
|
|
183
|
+
// hook contract, so refuse explicitly instead.
|
|
184
|
+
process.stderr.write("write blocked (guardrail fault): " + String(err && err.message) + "\\n");
|
|
185
|
+
process.exit(2);
|
|
186
|
+
}
|
|
187
|
+
if (!refusal) process.exit(0);
|
|
188
|
+
process.stderr.write(refusal + "\\n");
|
|
189
|
+
process.exit(2);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// subagent discriminator: on the orchestrator's main thread agent_id is
|
|
193
|
+
// undefined. agent_type can be set on the orchestrator itself via --agent,
|
|
194
|
+
// so it's not a reliable subagent discriminator on its own; agent_id is.
|
|
195
|
+
// contract verified against @anthropic-ai/claude-code 2.1.220 (pinned in
|
|
196
|
+
// package.json; see CLAUDE_CODE_AGENT_ID_VERIFIED_VERSION + the version
|
|
197
|
+
// tripwire in claudePretoolGate.test.ts); re-verify createBaseHookInput if
|
|
198
|
+
// that bumps — if agent_id ever stops being populated the gate fails OPEN.
|
|
199
|
+
if (!agentId) process.exit(0);
|
|
200
|
+
const bare = stripMcpPrefix(toolName);
|
|
201
|
+
if (!SUBAGENT_DENIED_TOOLS.has(bare)) process.exit(0);
|
|
202
|
+
process.stderr.write(
|
|
203
|
+
"subagent attempted to call denied tool '" + bare + "'. " +
|
|
204
|
+
"subagents share the orchestrator's in-process working tree and toolState; " +
|
|
205
|
+
"state-changing MCP tools (checkout_pr, push_branch, create_pull_request_review, " +
|
|
206
|
+
"report_progress, etc.) are reserved for the orchestrator. " +
|
|
207
|
+
"report findings back to the orchestrator and let it perform the mutation.\\n"
|
|
208
|
+
);
|
|
209
|
+
process.exit(2);
|
|
210
|
+
});
|
|
211
|
+
`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Settings JSON shape registered via `claude --settings <path>`. The
|
|
216
|
+
* matcher `^mcp__infraweaver__` is treated as a regex by claude-code's
|
|
217
|
+
* `matchesPattern` helper (anything outside `[a-zA-Z0-9_|]` triggers the
|
|
218
|
+
* regex branch — verified in src/utils/hooks.ts), so this anchors at the
|
|
219
|
+
* start of the tool name and fires for every Infraweaver MCP tool. We narrow
|
|
220
|
+
* inside the script itself rather than declaring per-tool matchers because
|
|
221
|
+
* the deny list is the source of truth.
|
|
222
|
+
*
|
|
223
|
+
* The hook process inherits the parent's PATH, so `node` resolves to the
|
|
224
|
+
* runner's node binary; the `--settings` flag accepts either a path or a
|
|
225
|
+
* literal JSON string per claude-code source `src/main.tsx` (`Path to a
|
|
226
|
+
* settings JSON file or a JSON string`), but we use a path so the script
|
|
227
|
+
* and its config sit side-by-side under `ctx.tmpdir`.
|
|
228
|
+
*
|
|
229
|
+
* `execToolDenyRules` are the native exec tools (Bash/Monitor/REPL/Workflow +
|
|
230
|
+
* their `Agent(...)` forms) to deny at a settings-source rule — the
|
|
231
|
+
* authoritative, bypass-immune layer. `--disallowedTools` alone (a `cliArg`
|
|
232
|
+
* deny) was observed to leak under `--dangerously-skip-permissions`, so the
|
|
233
|
+
* deny is carried here too. Both consumers use both returned fields: the flag
|
|
234
|
+
* `--settings` JSON (covers non-CI runs) writes the whole object, and
|
|
235
|
+
* `buildManagedSettings` (CI, /etc managed settings) spreads `hooks` and folds
|
|
236
|
+
* `permissions.deny` into its richer deny list.
|
|
237
|
+
*/
|
|
238
|
+
export function buildClaudePretoolGateSettings(
|
|
239
|
+
scriptAbsolutePath: string,
|
|
240
|
+
execToolDenyRules: string[],
|
|
241
|
+
): {
|
|
242
|
+
hooks: {
|
|
243
|
+
PreToolUse: Array<{
|
|
244
|
+
matcher: string;
|
|
245
|
+
hooks: Array<{ type: "command"; command: string; timeout?: number }>;
|
|
246
|
+
}>;
|
|
247
|
+
/** layered in by writePretoolGateAssets so non-CI runs (no managed
|
|
248
|
+
* settings) still fire the gate-server Stop hook. */
|
|
249
|
+
Stop?: Array<{ hooks: Array<{ type: "command"; command: string }> }>;
|
|
250
|
+
};
|
|
251
|
+
permissions: { deny: string[] };
|
|
252
|
+
} {
|
|
253
|
+
// shell-quote-safe because tmpdir paths created via node:fs.mkdtempSync
|
|
254
|
+
// don't contain spaces, but pass via a literal that node parses as a single
|
|
255
|
+
// argv entry to be defensive against future tmpdir layout changes.
|
|
256
|
+
const invoke = [
|
|
257
|
+
{
|
|
258
|
+
type: "command" as const,
|
|
259
|
+
command: `node ${JSON.stringify(scriptAbsolutePath)}`,
|
|
260
|
+
timeout: 5,
|
|
261
|
+
},
|
|
262
|
+
];
|
|
263
|
+
return {
|
|
264
|
+
hooks: {
|
|
265
|
+
PreToolUse: [
|
|
266
|
+
{
|
|
267
|
+
matcher: "^mcp__infraweaver__",
|
|
268
|
+
hooks: invoke,
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
// the native write tools, gated for allowed_paths / protected_paths.
|
|
272
|
+
// Built from CLAUDE_WRITE_TOOL_NAMES so the matcher and the set the
|
|
273
|
+
// script checks can't drift apart. Anchored `^(...)$` keeps the
|
|
274
|
+
// regex branch of claude-code's matchesPattern and avoids matching
|
|
275
|
+
// an unrelated tool that merely starts with "Edit".
|
|
276
|
+
matcher: `^(${CLAUDE_WRITE_TOOL_NAMES.join("|")})$`,
|
|
277
|
+
hooks: invoke,
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
},
|
|
281
|
+
// copy so callers that extend the deny list (writePretoolGateAssets adds
|
|
282
|
+
// the harness-tmpdir fence) can't mutate the shared CLAUDE_EXEC_TOOL_DENY_RULES
|
|
283
|
+
// constant, which would grow it every run and leak into --disallowedTools.
|
|
284
|
+
permissions: { deny: [...execToolDenyRules] },
|
|
285
|
+
};
|
|
286
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Specialist-dispatch instrumentation.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS. Review routing moved from a **count-parity** gate ("0 or 2+
|
|
5
|
+
* lenses, never exactly 1", gated on diff size) to **hypothesis coverage**
|
|
6
|
+
* (one specialist per falsifiable, load-bearing question, no size threshold).
|
|
7
|
+
* The evaluation that adopted it named its own risk plainly: the bar to
|
|
8
|
+
* dispatch *something* drops, each specialist re-reads the diff, so cost is
|
|
9
|
+
* "plausibly neutral-to-slightly-up" — and the recommendation was to ship it
|
|
10
|
+
* "behind a close watch on dispatch-rate/cost". See `docs/AGENT-HARDENING.md`.
|
|
11
|
+
*
|
|
12
|
+
* That watch never existed. Tokens were summed for the whole run with no split
|
|
13
|
+
* between the orchestrator and the subagents it dispatched, so the one number
|
|
14
|
+
* that would settle the question — what share of a run the specialists cost —
|
|
15
|
+
* was not observable at any point. "Plausibly neutral" was an assumption with
|
|
16
|
+
* no instrument behind it.
|
|
17
|
+
*
|
|
18
|
+
* WHAT IS MEASURED, AND HOW MUCH IT CAN BE TRUSTED. Two different qualities of
|
|
19
|
+
* signal, deliberately kept apart rather than blended into one number:
|
|
20
|
+
*
|
|
21
|
+
* - **Dispatch counts are exact.** They are recorded from the actual Task
|
|
22
|
+
* tool call, one per dispatch, not inferred from the event stream.
|
|
23
|
+
* - **The token split is robust but coarse.** Tokens are attributed to
|
|
24
|
+
* "orchestrator" vs "subagents", never per-lens. Per-session labels come
|
|
25
|
+
* from `SessionLabeler`, whose own contract says its FIFO dispatch↔session
|
|
26
|
+
* pairing is for log readability and is not depended on for correctness.
|
|
27
|
+
* A mis-paired label would corrupt a *per-lens* number — but both sides of
|
|
28
|
+
* a mis-pairing are still subagents, so the orchestrator/subagent split is
|
|
29
|
+
* unaffected by exactly the weakness the labeler warns about. That is why
|
|
30
|
+
* this file reports the split and not the breakdown.
|
|
31
|
+
*
|
|
32
|
+
* Deliberately NOT a database column. A new `WorkflowRun` field needs manual
|
|
33
|
+
* production DDL, and a metric nobody looks at is not a watch. This surfaces
|
|
34
|
+
* where the comparison actually gets made — the run's own log line and job
|
|
35
|
+
* summary — so "before/after the routing change" is two runs side by side.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import { ORCHESTRATOR_LABEL } from "#app/agents/sessionLabeler";
|
|
39
|
+
|
|
40
|
+
export interface DispatchTally {
|
|
41
|
+
/** subagent dispatches this run — exact, counted at the Task call. */
|
|
42
|
+
dispatches: number;
|
|
43
|
+
/** dispatch count per resolved label, e.g. `lens:iam-blast-radius`. */
|
|
44
|
+
byLabel: ReadonlyMap<string, number>;
|
|
45
|
+
/** billable tokens (input incl. cache + output) attributed to the orchestrator. */
|
|
46
|
+
orchestratorTokens: number;
|
|
47
|
+
/** …and to every subagent session combined. */
|
|
48
|
+
subagentTokens: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Per-run accumulator. Lives on `ToolState` and is written by whichever harness
|
|
53
|
+
* is driving, so the counts survive across agent invocations (post-run resume,
|
|
54
|
+
* transient-provider retry) the same way `usageEntries` does.
|
|
55
|
+
*/
|
|
56
|
+
export class DispatchRecorder {
|
|
57
|
+
#dispatches = 0;
|
|
58
|
+
readonly #byLabel = new Map<string, number>();
|
|
59
|
+
#orchestratorTokens = 0;
|
|
60
|
+
#subagentTokens = 0;
|
|
61
|
+
|
|
62
|
+
/** Record one subagent dispatch. Call at the Task tool_use, not at completion:
|
|
63
|
+
* a dispatch that errors out still cost the decision and usually the tokens. */
|
|
64
|
+
recordDispatch(label: string): void {
|
|
65
|
+
this.#dispatches++;
|
|
66
|
+
this.#byLabel.set(label, (this.#byLabel.get(label) ?? 0) + 1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Attribute billable tokens to a session label. Anything that is not the
|
|
71
|
+
* orchestrator counts as subagent — including a session the labeler could not
|
|
72
|
+
* name, because an unnamed session is by definition not the orchestrator's.
|
|
73
|
+
*/
|
|
74
|
+
recordTokens(label: string, billableTokens: number): void {
|
|
75
|
+
if (!Number.isFinite(billableTokens) || billableTokens <= 0) return;
|
|
76
|
+
if (label === ORCHESTRATOR_LABEL) this.#orchestratorTokens += billableTokens;
|
|
77
|
+
else this.#subagentTokens += billableTokens;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
tally(): DispatchTally {
|
|
81
|
+
return {
|
|
82
|
+
dispatches: this.#dispatches,
|
|
83
|
+
byLabel: new Map(this.#byLabel),
|
|
84
|
+
orchestratorTokens: this.#orchestratorTokens,
|
|
85
|
+
subagentTokens: this.#subagentTokens,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** subagent share of attributed tokens, 0–1. Null when nothing was attributed —
|
|
91
|
+
* distinct from 0, which means specialists genuinely cost nothing. */
|
|
92
|
+
export function subagentTokenShare(tally: DispatchTally): number | null {
|
|
93
|
+
const total = tally.orchestratorTokens + tally.subagentTokens;
|
|
94
|
+
if (total <= 0) return null;
|
|
95
|
+
return tally.subagentTokens / total;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const pct = (share: number): string => `${(share * 100).toFixed(1)}%`;
|
|
99
|
+
const fmt = (n: number): string => n.toLocaleString("en-US");
|
|
100
|
+
|
|
101
|
+
/** One stdout line. Empty when the run dispatched nothing AND attributed nothing —
|
|
102
|
+
* a solo run has no routing to report and the line would be noise. */
|
|
103
|
+
export function describeDispatchTally(tally: DispatchTally): string {
|
|
104
|
+
const share = subagentTokenShare(tally);
|
|
105
|
+
if (tally.dispatches === 0 && share === null) return "";
|
|
106
|
+
if (tally.dispatches === 0) return "» specialist dispatch: none this run";
|
|
107
|
+
const labels = [...tally.byLabel.entries()]
|
|
108
|
+
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
|
|
109
|
+
.map(([label, n]) => (n > 1 ? `${label}×${n}` : label))
|
|
110
|
+
.join(", ");
|
|
111
|
+
const cost =
|
|
112
|
+
share === null
|
|
113
|
+
? "token split unavailable"
|
|
114
|
+
: `${pct(share)} of billable tokens (${fmt(tally.subagentTokens)} of ${fmt(
|
|
115
|
+
tally.orchestratorTokens + tally.subagentTokens,
|
|
116
|
+
)})`;
|
|
117
|
+
return `» specialist dispatch: ${tally.dispatches} (${labels}) — ${cost}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Markdown block for the GitHub job summary, rendered beside the usage table.
|
|
122
|
+
* Collapsed like the usage table so it costs nothing to ignore, and carries its
|
|
123
|
+
* own accuracy caveat: a reader comparing two runs needs to know the split is
|
|
124
|
+
* coarse before they draw a conclusion from it.
|
|
125
|
+
*/
|
|
126
|
+
export function formatDispatchSummary(tally: DispatchTally): string {
|
|
127
|
+
const share = subagentTokenShare(tally);
|
|
128
|
+
if (tally.dispatches === 0 && share === null) return "";
|
|
129
|
+
|
|
130
|
+
const lines: string[] = [
|
|
131
|
+
"<details>",
|
|
132
|
+
"<summary>Specialist dispatch</summary>",
|
|
133
|
+
"",
|
|
134
|
+
`- **Dispatches:** ${tally.dispatches}`,
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
if (tally.dispatches > 0) {
|
|
138
|
+
const rows = [...tally.byLabel.entries()].sort(
|
|
139
|
+
(a, b) => b[1] - a[1] || a[0].localeCompare(b[0]),
|
|
140
|
+
);
|
|
141
|
+
for (const [label, n] of rows) lines.push(` - \`${label}\` — ${n}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (share === null) {
|
|
145
|
+
lines.push("- **Token split:** unavailable (no usage was attributed this run)");
|
|
146
|
+
} else {
|
|
147
|
+
lines.push(
|
|
148
|
+
`- **Orchestrator:** ${fmt(tally.orchestratorTokens)} billable tokens`,
|
|
149
|
+
`- **Subagents:** ${fmt(tally.subagentTokens)} billable tokens (${pct(share)})`,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
lines.push(
|
|
154
|
+
"",
|
|
155
|
+
"> Dispatch counts are exact. The token split is orchestrator-vs-subagent only —",
|
|
156
|
+
"> per-lens attribution would rest on the session labeler's FIFO pairing, which is",
|
|
157
|
+
"> maintained for log readability and not relied on for correctness.",
|
|
158
|
+
"",
|
|
159
|
+
"</details>",
|
|
160
|
+
);
|
|
161
|
+
return lines.join("\n");
|
|
162
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gate server — localhost HTTP sidecar that returns Stop-hook decisions for
|
|
3
|
+
* the Claude harness. the managed Stop hook (see `action/agents/claude.ts`)
|
|
4
|
+
* curls this server on every stop and writes back the resulting
|
|
5
|
+
* `{decision: "block", reason}` to inject a follow-up turn inside the live
|
|
6
|
+
* `queryLoop`, replacing the pre-PR-#795 `--resume <sessionId>` subprocess.
|
|
7
|
+
*
|
|
8
|
+
* the server captures `ctx` by closure so every request reads the latest
|
|
9
|
+
* mutations from MCP tool callbacks (which run in the same process). the
|
|
10
|
+
* counters live here, not on `toolState`, because gate-retry budget and
|
|
11
|
+
* reflection one-shot are harness concerns the literal `toolState` design
|
|
12
|
+
* rule (see `action/toolState.ts`) explicitly excludes.
|
|
13
|
+
*
|
|
14
|
+
* decision policy mirrors the old `runPostRunRetryLoop`:
|
|
15
|
+
* - gates dirty + budget remaining → block with combined gate prompt
|
|
16
|
+
* - gates clean + reflection pending → block with reflection prompt
|
|
17
|
+
* - otherwise → allow stop
|
|
18
|
+
* `summaryStale` is a one-shot nudge; once delivered the gate is suppressed
|
|
19
|
+
* so a deliberately-unchanged file doesn't burn the retry budget. when the
|
|
20
|
+
* budget is exhausted with hard-fail gates still failing, we allow the stop
|
|
21
|
+
* so `finalizeAgentResult` can render the terminal `AgentResult.success =
|
|
22
|
+
* false` — that state cannot be set from a stdout-only hook.
|
|
23
|
+
*/
|
|
24
|
+
import { randomUUID } from "node:crypto";
|
|
25
|
+
import { createServer } from "node:http";
|
|
26
|
+
import {
|
|
27
|
+
buildLearningsReflectionPrompt,
|
|
28
|
+
buildPostRunPrompt,
|
|
29
|
+
collectPostRunIssues,
|
|
30
|
+
shouldRunReflection,
|
|
31
|
+
} from "#app/agents/postRun";
|
|
32
|
+
import { type AgentRunContext, hasPostRunIssues, MAX_POST_RUN_RETRIES } from "#app/agents/shared";
|
|
33
|
+
import { log } from "#app/utils/cli";
|
|
34
|
+
|
|
35
|
+
export interface GateServerHandle {
|
|
36
|
+
url: string;
|
|
37
|
+
// bearer token the Stop hook must present. Passed to the agent process via a
|
|
38
|
+
// `_TOKEN`-suffixed env var so filterEnv() strips it from the agent's shell
|
|
39
|
+
// sandbox — only the real Stop hook (a child of the agent process, inheriting
|
|
40
|
+
// its full env) can read it. Without this, any loopback caller (incl. the
|
|
41
|
+
// agent) could poison the retry budget by hitting GET /gates.
|
|
42
|
+
token: string;
|
|
43
|
+
[Symbol.asyncDispose]: () => Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function startGateServer(ctx: AgentRunContext): Promise<GateServerHandle> {
|
|
47
|
+
let blockCount = 0;
|
|
48
|
+
let reflectionDelivered = false;
|
|
49
|
+
let summaryStaleNudged = false;
|
|
50
|
+
// task output captured when the reflection nudge is delivered, restored on the
|
|
51
|
+
// next /gates call so a set_output during reflection can't clobber the result.
|
|
52
|
+
// a flag (not an undefined sentinel) because the snapshot value may itself be
|
|
53
|
+
// undefined and must still be restored over a reflection-set value.
|
|
54
|
+
let reflectionOutputPending = false;
|
|
55
|
+
let reflectionOutputSnapshot: string | undefined;
|
|
56
|
+
// set when a block decision was confirmed delivered; cleared when the Stop
|
|
57
|
+
// hook comes back for another decision. a run that ends with this still set
|
|
58
|
+
// means the hook received a block but claude never re-stopped — worth a log
|
|
59
|
+
// line, since it points at a lost injection rather than a served one.
|
|
60
|
+
let blockAwaitingRefetch = false;
|
|
61
|
+
const token = randomUUID();
|
|
62
|
+
|
|
63
|
+
const server = createServer((req, res) => {
|
|
64
|
+
void (async () => {
|
|
65
|
+
if (req.method !== "GET" || req.url !== "/gates") {
|
|
66
|
+
res.writeHead(404).end();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Authenticate before reading state or mutating counters — an unauthorized
|
|
70
|
+
// request must not consume the retry budget.
|
|
71
|
+
if (req.headers.authorization !== `Bearer ${token}`) {
|
|
72
|
+
res.writeHead(403).end();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
blockAwaitingRefetch = false;
|
|
76
|
+
// send a decision and run `commit` (the state mutation that decision
|
|
77
|
+
// implies — budget consumed, one-shots latched) only once the response
|
|
78
|
+
// has actually flushed to the hook. the Stop hook's curl has a 30s
|
|
79
|
+
// timeout and an `|| {"block":false}` fallback, so mutating BEFORE
|
|
80
|
+
// delivery meant one hiccup silently burned a retry or the reflection
|
|
81
|
+
// one-shot with no log of the mismatch. an undelivered decision now
|
|
82
|
+
// leaves the state untouched and is retried on the next stop.
|
|
83
|
+
const send = (body: string, commit?: () => void): void => {
|
|
84
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
85
|
+
res.on("close", () => {
|
|
86
|
+
if (res.writableFinished) {
|
|
87
|
+
commit?.();
|
|
88
|
+
} else {
|
|
89
|
+
log.warning(
|
|
90
|
+
"» gate-server: decision response was not delivered (connection closed early) — state not mutated, will re-decide on the next stop",
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
res.end(body);
|
|
95
|
+
};
|
|
96
|
+
try {
|
|
97
|
+
const issues = await collectPostRunIssues(ctx, { skipSummaryStale: summaryStaleNudged });
|
|
98
|
+
if (hasPostRunIssues(issues)) {
|
|
99
|
+
if (blockCount >= MAX_POST_RUN_RETRIES) {
|
|
100
|
+
log.info("» gate-server: retry budget exhausted, allowing stop for terminal hard-fail");
|
|
101
|
+
send('{"block":false}');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const reason = buildPostRunPrompt(issues);
|
|
105
|
+
const attempt = blockCount + 1;
|
|
106
|
+
log.info(`» gate-server: blocking (attempt ${attempt}/${MAX_POST_RUN_RETRIES})`);
|
|
107
|
+
send(JSON.stringify({ block: true, reason }), () => {
|
|
108
|
+
if (issues.summaryStale) summaryStaleNudged = true;
|
|
109
|
+
blockCount = attempt;
|
|
110
|
+
blockAwaitingRefetch = true;
|
|
111
|
+
});
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// the reflection turn just delivered (previous /gates call) may have
|
|
115
|
+
// clobbered the task output: some models re-trigger the "call set_output
|
|
116
|
+
// when done" instruction during reflection and overwrite the real result
|
|
117
|
+
// with "done". restore the snapshot taken when we delivered it — the
|
|
118
|
+
// same defense the opencode runPostRunRetryLoop applies. The prompt-level
|
|
119
|
+
// "do NOT call set_output" instruction is defense one; this is defense two.
|
|
120
|
+
if (reflectionOutputPending) {
|
|
121
|
+
ctx.toolState.output = reflectionOutputSnapshot;
|
|
122
|
+
reflectionOutputPending = false;
|
|
123
|
+
}
|
|
124
|
+
const learningsPath = ctx.toolState.learningsFilePath;
|
|
125
|
+
if (
|
|
126
|
+
!reflectionDelivered &&
|
|
127
|
+
learningsPath &&
|
|
128
|
+
shouldRunReflection(ctx.toolState.selectedMode)
|
|
129
|
+
) {
|
|
130
|
+
const reason = buildLearningsReflectionPrompt(learningsPath);
|
|
131
|
+
log.info("» gate-server: delivering reflection nudge");
|
|
132
|
+
send(JSON.stringify({ block: true, reason }), () => {
|
|
133
|
+
reflectionDelivered = true;
|
|
134
|
+
reflectionOutputSnapshot = ctx.toolState.output;
|
|
135
|
+
reflectionOutputPending = true;
|
|
136
|
+
blockAwaitingRefetch = true;
|
|
137
|
+
});
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
send('{"block":false}');
|
|
141
|
+
} catch (err) {
|
|
142
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
143
|
+
log.warning(`» gate-server handler error: ${msg}`);
|
|
144
|
+
// never block claude on a server-side fault — allow the stop and
|
|
145
|
+
// let the harness's terminal hard-fail path own the decision.
|
|
146
|
+
send('{"block":false}');
|
|
147
|
+
}
|
|
148
|
+
})();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
await new Promise<void>((resolve, reject) => {
|
|
152
|
+
server.once("error", reject);
|
|
153
|
+
server.listen(0, "127.0.0.1", () => resolve());
|
|
154
|
+
});
|
|
155
|
+
const addr = server.address();
|
|
156
|
+
if (!addr || typeof addr === "string") {
|
|
157
|
+
throw new Error("gate-server: failed to bind localhost port");
|
|
158
|
+
}
|
|
159
|
+
const url = `http://127.0.0.1:${addr.port}/gates`;
|
|
160
|
+
log.debug(`» gate-server listening at ${url}`);
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
url,
|
|
164
|
+
token,
|
|
165
|
+
[Symbol.asyncDispose]: async () => {
|
|
166
|
+
if (blockAwaitingRefetch) {
|
|
167
|
+
// the hook confirmed receipt of a block decision but claude never
|
|
168
|
+
// stopped again — the injected turn was lost downstream (e.g. the
|
|
169
|
+
// hook script failed after the curl). budget was already consumed;
|
|
170
|
+
// surface the mismatch instead of losing it silently.
|
|
171
|
+
log.warning(
|
|
172
|
+
"» gate-server: a delivered block decision was never followed by another stop — the injected follow-up turn may have been lost",
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { claude } from "#app/agents/claude";
|
|
2
|
+
// In-process OpenCode harness, adapted to opencode-ai >=1.14.x SDK-v2 /
|
|
3
|
+
// Effect-ts CLI rewrite. (The legacy CLI-subprocess harness was removed; see
|
|
4
|
+
// git history if a revert is ever needed.)
|
|
5
|
+
import { opencode } from "#app/agents/opencode";
|
|
6
|
+
import type { Agent } from "#app/agents/shared";
|
|
7
|
+
|
|
8
|
+
export type { Agent, AgentUsage } from "#app/agents/shared";
|
|
9
|
+
|
|
10
|
+
export const agents = { claude, opencode } satisfies Record<string, Agent>;
|