terramend 0.2.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 +145 -0
- package/dist/agents/claude.d.ts +73 -0
- package/dist/agents/claudePretoolGate.d.ts +99 -0
- package/dist/agents/gateServer.d.ts +7 -0
- package/dist/agents/index.d.ts +6 -0
- package/dist/agents/nativeFsDenies.d.ts +28 -0
- package/dist/agents/opencode.d.ts +231 -0
- package/dist/agents/opencodePlugin.d.ts +85 -0
- package/dist/agents/opencodeShared.d.ts +40 -0
- package/dist/agents/postRun.d.ts +132 -0
- package/dist/agents/reviewer.d.ts +38 -0
- package/dist/agents/sessionLabeler.d.ts +97 -0
- package/dist/agents/shared.d.ts +189 -0
- package/dist/agents/subagentModels.d.ts +19 -0
- package/dist/agents/subagentToolGates.d.ts +55 -0
- package/dist/cli.mjs +197426 -0
- package/dist/external.d.ts +227 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +196783 -0
- package/dist/internal/index.d.ts +18 -0
- package/dist/internal.js +1714 -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/checkSuite.d.ts +25 -0
- package/dist/mcp/checkout.d.ts +77 -0
- package/dist/mcp/comment.d.ts +119 -0
- package/dist/mcp/commitInfo.d.ts +9 -0
- package/dist/mcp/crosswalk.d.ts +105 -0
- package/dist/mcp/dependencies.d.ts +8 -0
- package/dist/mcp/geminiSanitizer.d.ts +28 -0
- package/dist/mcp/git.d.ts +46 -0
- package/dist/mcp/guardrails.d.ts +104 -0
- package/dist/mcp/issue.d.ts +18 -0
- package/dist/mcp/issueComments.d.ts +9 -0
- package/dist/mcp/issueEvents.d.ts +9 -0
- package/dist/mcp/issueInfo.d.ts +9 -0
- package/dist/mcp/labels.d.ts +12 -0
- package/dist/mcp/localContext.d.ts +19 -0
- package/dist/mcp/moduleExtraction.d.ts +71 -0
- package/dist/mcp/moduleTests.d.ts +104 -0
- package/dist/mcp/modules.d.ts +179 -0
- package/dist/mcp/output.d.ts +12 -0
- package/dist/mcp/pathSafety.d.ts +14 -0
- package/dist/mcp/policy.d.ts +48 -0
- package/dist/mcp/pr.d.ts +49 -0
- package/dist/mcp/prInfo.d.ts +9 -0
- package/dist/mcp/providerSchema.d.ts +50 -0
- package/dist/mcp/review.d.ts +199 -0
- package/dist/mcp/reviewComments.d.ts +178 -0
- package/dist/mcp/roots.d.ts +58 -0
- package/dist/mcp/scope.d.ts +15 -0
- package/dist/mcp/selectMode.d.ts +18 -0
- package/dist/mcp/server.d.ts +48 -0
- package/dist/mcp/shared.d.ts +47 -0
- package/dist/mcp/shell.d.ts +37 -0
- package/dist/mcp/staleFix.d.ts +51 -0
- package/dist/mcp/terraform/cost.d.ts +55 -0
- package/dist/mcp/terraform/currency.d.ts +94 -0
- package/dist/mcp/terraform/decisions.d.ts +178 -0
- package/dist/mcp/terraform/findings.d.ts +75 -0
- package/dist/mcp/terraform/plan.d.ts +157 -0
- package/dist/mcp/terraform/scanners.d.ts +131 -0
- package/dist/mcp/terraform/tools.d.ts +63 -0
- package/dist/mcp/terraform/types.d.ts +172 -0
- package/dist/mcp/terraform.d.ts +22 -0
- package/dist/mcp/terratest.d.ts +83 -0
- package/dist/mcp/upload.d.ts +6 -0
- package/dist/models.d.ts +171 -0
- package/dist/modes.d.ts +26 -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 +64 -0
- package/dist/skills/terraform-best-practices/SKILL.md +369 -0
- package/dist/toolState.d.ts +135 -0
- package/dist/utils/activity.d.ts +40 -0
- package/dist/utils/agent.d.ts +20 -0
- package/dist/utils/agentHangReport.d.ts +38 -0
- package/dist/utils/apiFetch.d.ts +19 -0
- package/dist/utils/apiKeys.d.ts +41 -0
- package/dist/utils/apiUrl.d.ts +20 -0
- package/dist/utils/assets.d.ts +8 -0
- package/dist/utils/billingErrors.d.ts +85 -0
- package/dist/utils/body.d.ts +34 -0
- package/dist/utils/buildTerramendFooter.d.ts +25 -0
- package/dist/utils/byokFallback.d.ts +85 -0
- package/dist/utils/claudeSubscription.d.ts +30 -0
- package/dist/utils/cli.d.ts +10 -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/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 +78 -0
- package/dist/utils/globals.d.ts +3 -0
- package/dist/utils/install.d.ts +60 -0
- package/dist/utils/instructions.d.ts +48 -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/normalizeEnv.d.ts +30 -0
- package/dist/utils/openCodeModels.d.ts +11 -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 +105 -0
- package/dist/utils/prSummary.d.ts +61 -0
- package/dist/utils/progressComment.d.ts +146 -0
- package/dist/utils/providerErrors.d.ts +31 -0
- package/dist/utils/rangeDiff.d.ts +51 -0
- package/dist/utils/remediationCommand.d.ts +55 -0
- package/dist/utils/retry.d.ts +13 -0
- package/dist/utils/reviewCleanup.d.ts +14 -0
- package/dist/utils/run.d.ts +9 -0
- package/dist/utils/runContext.d.ts +60 -0
- package/dist/utils/runContextData.d.ts +23 -0
- package/dist/utils/runErrorRenderer.d.ts +64 -0
- package/dist/utils/runLifecycle.d.ts +86 -0
- package/dist/utils/runStartupLog.d.ts +15 -0
- package/dist/utils/secrets.d.ts +22 -0
- package/dist/utils/setup.d.ts +90 -0
- package/dist/utils/shell.d.ts +32 -0
- package/dist/utils/skills.d.ts +10 -0
- package/dist/utils/subprocess.d.ts +80 -0
- package/dist/utils/terraformMcp.d.ts +42 -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 +39 -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 +119 -0
- package/src/agents/claude.test.ts +1016 -0
- package/src/agents/claude.ts +1246 -0
- package/src/agents/claudePretoolGate.test.ts +28 -0
- package/src/agents/claudePretoolGate.ts +173 -0
- package/src/agents/gateServer.test.ts +204 -0
- package/src/agents/gateServer.ts +124 -0
- package/src/agents/index.ts +10 -0
- package/src/agents/nativeFsDenies.ts +82 -0
- package/src/agents/opencode.test.ts +1440 -0
- package/src/agents/opencode.ts +1312 -0
- package/src/agents/opencodePlugin.ts +222 -0
- package/src/agents/opencodeShared.test.ts +34 -0
- package/src/agents/opencodeShared.ts +121 -0
- package/src/agents/postRun.test.ts +549 -0
- package/src/agents/postRun.ts +535 -0
- package/src/agents/reviewer.ts +104 -0
- package/src/agents/sessionLabeler.test.ts +247 -0
- package/src/agents/sessionLabeler.ts +178 -0
- package/src/agents/shared.test.ts +76 -0
- package/src/agents/shared.ts +292 -0
- package/src/agents/subagentModels.test.ts +113 -0
- package/src/agents/subagentModels.ts +40 -0
- package/src/agents/subagentRegistration.test.ts +41 -0
- package/src/agents/subagentToolGates.ts +114 -0
- package/src/cli.test.ts +129 -0
- package/src/cli.ts +105 -0
- package/src/commands/gha.test.ts +192 -0
- package/src/commands/gha.ts +188 -0
- package/src/commands/mcp.ts +122 -0
- package/src/config.ts +1 -0
- package/src/entry.ts +7 -0
- package/src/entryPost.stdlibOnly.test.ts +109 -0
- package/src/entryPost.ts +99 -0
- package/src/external.test.ts +16 -0
- package/src/external.ts +302 -0
- package/src/index.ts +11 -0
- package/src/internal/index.ts +71 -0
- package/src/lifecycle.ts +2 -0
- package/src/main.test.ts +873 -0
- package/src/main.ts +712 -0
- package/src/mcp/__fixtures__/terramend-scratch-pr-49-review-3485940013.json +110 -0
- package/src/mcp/__fixtures__/terramend-scratch-pr-64-review-3531000326.json +14 -0
- package/src/mcp/__fixtures__/terramend-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/checkSuite.test.ts +245 -0
- package/src/mcp/checkSuite.ts +255 -0
- package/src/mcp/checkout.test.ts +752 -0
- package/src/mcp/checkout.ts +886 -0
- package/src/mcp/comment.test.ts +772 -0
- package/src/mcp/comment.ts +582 -0
- package/src/mcp/commitInfo.test.ts +127 -0
- package/src/mcp/commitInfo.ts +61 -0
- package/src/mcp/crosswalk.test.ts +106 -0
- package/src/mcp/crosswalk.ts +339 -0
- package/src/mcp/dependencies.test.ts +309 -0
- package/src/mcp/dependencies.ts +189 -0
- package/src/mcp/geminiSanitizer.test.ts +287 -0
- package/src/mcp/geminiSanitizer.ts +207 -0
- package/src/mcp/git.test.ts +1083 -0
- package/src/mcp/git.ts +890 -0
- package/src/mcp/guardrails.test.ts +705 -0
- package/src/mcp/guardrails.ts +465 -0
- package/src/mcp/issue.test.ts +113 -0
- package/src/mcp/issue.ts +73 -0
- package/src/mcp/issueComments.test.ts +69 -0
- package/src/mcp/issueComments.ts +48 -0
- package/src/mcp/issueEvents.test.ts +134 -0
- package/src/mcp/issueEvents.ts +100 -0
- package/src/mcp/issueInfo.test.ts +104 -0
- package/src/mcp/issueInfo.ts +72 -0
- package/src/mcp/labels.test.ts +52 -0
- package/src/mcp/labels.ts +34 -0
- package/src/mcp/localContext.ts +28 -0
- package/src/mcp/localServer.test.ts +75 -0
- package/src/mcp/localServer.ts +131 -0
- package/src/mcp/moduleExtraction.test.ts +261 -0
- package/src/mcp/moduleExtraction.ts +313 -0
- package/src/mcp/moduleTests.test.ts +269 -0
- package/src/mcp/moduleTests.ts +421 -0
- package/src/mcp/modules.test.ts +640 -0
- package/src/mcp/modules.ts +696 -0
- package/src/mcp/output.test.ts +96 -0
- package/src/mcp/output.ts +70 -0
- package/src/mcp/pathSafety.test.ts +44 -0
- package/src/mcp/pathSafety.ts +28 -0
- package/src/mcp/policy.test.ts +282 -0
- package/src/mcp/policy.ts +199 -0
- package/src/mcp/pr.test.ts +387 -0
- package/src/mcp/pr.ts +194 -0
- package/src/mcp/prInfo.test.ts +96 -0
- package/src/mcp/prInfo.ts +91 -0
- package/src/mcp/providerSchema.test.ts +85 -0
- package/src/mcp/providerSchema.ts +175 -0
- package/src/mcp/review.test.ts +936 -0
- package/src/mcp/review.ts +923 -0
- package/src/mcp/reviewComments.test.ts +549 -0
- package/src/mcp/reviewComments.ts +896 -0
- package/src/mcp/roots.test.ts +175 -0
- package/src/mcp/roots.ts +217 -0
- package/src/mcp/scope.test.ts +59 -0
- package/src/mcp/scope.ts +65 -0
- package/src/mcp/security.test.ts +720 -0
- package/src/mcp/selectMode.test.ts +210 -0
- package/src/mcp/selectMode.ts +181 -0
- package/src/mcp/server.test.ts +292 -0
- package/src/mcp/server.ts +403 -0
- package/src/mcp/shared.ts +100 -0
- package/src/mcp/shell.test.ts +520 -0
- package/src/mcp/shell.ts +505 -0
- package/src/mcp/staleFix.test.ts +237 -0
- package/src/mcp/staleFix.ts +277 -0
- package/src/mcp/terraform/cost.ts +163 -0
- package/src/mcp/terraform/currency.test.ts +338 -0
- package/src/mcp/terraform/currency.ts +336 -0
- package/src/mcp/terraform/decisions.ts +527 -0
- package/src/mcp/terraform/findings.ts +333 -0
- package/src/mcp/terraform/plan.ts +348 -0
- package/src/mcp/terraform/scanners.ts +809 -0
- package/src/mcp/terraform/tools.test.ts +1071 -0
- package/src/mcp/terraform/tools.ts +908 -0
- package/src/mcp/terraform/types.ts +305 -0
- package/src/mcp/terraform.test.ts +1957 -0
- package/src/mcp/terraform.ts +23 -0
- package/src/mcp/terratest.test.ts +105 -0
- package/src/mcp/terratest.ts +196 -0
- package/src/mcp/toolFiltering.test.ts +85 -0
- package/src/mcp/upload.test.ts +180 -0
- package/src/mcp/upload.ts +112 -0
- package/src/models.test.ts +300 -0
- package/src/models.ts +708 -0
- package/src/modes.test.ts +107 -0
- package/src/modes.ts +880 -0
- package/src/prep/index.ts +43 -0
- package/src/prep/installNodeDependencies.test.ts +298 -0
- package/src/prep/installNodeDependencies.ts +196 -0
- package/src/prep/installPythonDependencies.test.ts +268 -0
- package/src/prep/installPythonDependencies.ts +199 -0
- package/src/prep/types.ts +38 -0
- package/src/reviewQuality.test.ts +63 -0
- package/src/reviewQuality.ts +134 -0
- package/src/runCli.test.ts +214 -0
- package/src/runCli.ts +282 -0
- package/src/skills/terraform-best-practices/SKILL.md +369 -0
- package/src/toolState.test.ts +45 -0
- package/src/toolState.ts +252 -0
- package/src/utils/activity.test.ts +188 -0
- package/src/utils/activity.ts +210 -0
- package/src/utils/agent.test.ts +251 -0
- package/src/utils/agent.ts +139 -0
- package/src/utils/agentHangReport.test.ts +203 -0
- package/src/utils/agentHangReport.ts +170 -0
- package/src/utils/apiFetch.test.ts +115 -0
- package/src/utils/apiFetch.ts +62 -0
- package/src/utils/apiKeys.test.ts +344 -0
- package/src/utils/apiKeys.ts +206 -0
- package/src/utils/apiUrl.test.ts +30 -0
- package/src/utils/apiUrl.ts +59 -0
- package/src/utils/assets.test.ts +153 -0
- package/src/utils/assets.ts +107 -0
- package/src/utils/billingErrors.test.ts +121 -0
- package/src/utils/billingErrors.ts +189 -0
- package/src/utils/body.test.ts +217 -0
- package/src/utils/body.ts +168 -0
- package/src/utils/buildTerramendFooter.test.ts +38 -0
- package/src/utils/buildTerramendFooter.ts +82 -0
- package/src/utils/byokFallback.test.ts +205 -0
- package/src/utils/byokFallback.ts +128 -0
- package/src/utils/claudeSubscription.test.ts +179 -0
- package/src/utils/claudeSubscription.ts +93 -0
- package/src/utils/cli.ts +31 -0
- package/src/utils/codexHome.test.ts +190 -0
- package/src/utils/codexHome.ts +191 -0
- package/src/utils/codexOAuth.ts +147 -0
- package/src/utils/codexRefreshDetect.test.ts +85 -0
- package/src/utils/codexRefreshDetect.ts +35 -0
- package/src/utils/diffCoverage.test.ts +468 -0
- package/src/utils/diffCoverage.ts +404 -0
- package/src/utils/errorReport.test.ts +135 -0
- package/src/utils/errorReport.ts +83 -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.test.ts +322 -0
- package/src/utils/gitAuth.ts +263 -0
- package/src/utils/gitAuthServer.test.ts +260 -0
- package/src/utils/gitAuthServer.ts +182 -0
- package/src/utils/github.test.ts +615 -0
- package/src/utils/github.ts +538 -0
- package/src/utils/globals.ts +9 -0
- package/src/utils/humanEditCapture.test.ts +100 -0
- package/src/utils/humanEditCapture.ts +193 -0
- package/src/utils/install.test.ts +768 -0
- package/src/utils/install.ts +492 -0
- package/src/utils/instructions.test.ts +240 -0
- package/src/utils/instructions.ts +543 -0
- package/src/utils/leapingComment.test.ts +51 -0
- package/src/utils/leapingComment.ts +18 -0
- package/src/utils/learnings.test.ts +87 -0
- package/src/utils/learnings.ts +138 -0
- package/src/utils/learningsTocRender.test.ts +116 -0
- package/src/utils/learningsTruncate.test.ts +39 -0
- package/src/utils/learningsTruncate.ts +42 -0
- package/src/utils/lifecycle.test.ts +195 -0
- package/src/utils/lifecycle.ts +198 -0
- package/src/utils/log.test.ts +402 -0
- package/src/utils/log.ts +432 -0
- package/src/utils/normalizeEnv.test.ts +91 -0
- package/src/utils/normalizeEnv.ts +106 -0
- package/src/utils/openCodeModels.ts +82 -0
- package/src/utils/overrides.test.ts +89 -0
- package/src/utils/overrides.ts +98 -0
- package/src/utils/packageManager.test.ts +321 -0
- package/src/utils/packageManager.ts +257 -0
- package/src/utils/patchWorkflowRunFields.test.ts +92 -0
- package/src/utils/patchWorkflowRunFields.ts +150 -0
- package/src/utils/payload.test.ts +497 -0
- package/src/utils/payload.ts +371 -0
- package/src/utils/postApiFetch.ts +51 -0
- package/src/utils/prSummary.test.ts +224 -0
- package/src/utils/prSummary.ts +147 -0
- package/src/utils/progressComment.ts +261 -0
- package/src/utils/providerErrors.test.ts +315 -0
- package/src/utils/providerErrors.ts +172 -0
- package/src/utils/rangeDiff.test.ts +236 -0
- package/src/utils/rangeDiff.ts +182 -0
- package/src/utils/remediationCommand.test.ts +163 -0
- package/src/utils/remediationCommand.ts +119 -0
- package/src/utils/retry.test.ts +153 -0
- package/src/utils/retry.ts +58 -0
- package/src/utils/reviewCleanup.ts +106 -0
- package/src/utils/run.ts +99 -0
- package/src/utils/runContext.ts +145 -0
- package/src/utils/runContextData.ts +58 -0
- package/src/utils/runErrorRenderer.test.ts +95 -0
- package/src/utils/runErrorRenderer.ts +259 -0
- package/src/utils/runFixture.ts +76 -0
- package/src/utils/runLifecycle.ts +237 -0
- package/src/utils/runStartupLog.ts +60 -0
- package/src/utils/secrets.test.ts +103 -0
- package/src/utils/secrets.ts +177 -0
- package/src/utils/setup.test.ts +509 -0
- package/src/utils/setup.ts +352 -0
- package/src/utils/shell.ts +103 -0
- package/src/utils/skills.test.ts +46 -0
- package/src/utils/skills.ts +67 -0
- package/src/utils/subprocess.test.ts +170 -0
- package/src/utils/subprocess.ts +438 -0
- package/src/utils/terraformMcp.test.ts +63 -0
- package/src/utils/terraformMcp.ts +83 -0
- package/src/utils/time.test.ts +105 -0
- package/src/utils/time.ts +59 -0
- package/src/utils/timer.test.ts +91 -0
- package/src/utils/timer.ts +72 -0
- package/src/utils/todoTracking.test.ts +223 -0
- package/src/utils/todoTracking.ts +167 -0
- package/src/utils/token.test.ts +239 -0
- package/src/utils/token.ts +186 -0
- package/src/utils/version.ts +10 -0
- package/src/utils/versioning.test.ts +34 -0
- package/src/utils/versioning.ts +44 -0
- package/src/utils/vertex.ts +85 -0
- package/src/utils/workflow.ts +25 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source for the opencode plugin we drop into the per-run tmpdir at
|
|
3
|
+
* `<XDG_CONFIG_HOME>/opencode/plugin/terramend-events.ts`. The harness already
|
|
4
|
+
* redirects `XDG_CONFIG_HOME` to `ctx.tmpdir/.config` (see `opencode.ts`
|
|
5
|
+
* `homeEnv`), so opencode's auto-discovery scans the tmpdir, never the user's
|
|
6
|
+
* working tree. opencode's `Global.Path.config` resolves to
|
|
7
|
+
* `path.join(xdgConfig, "opencode")` and the config layer auto-discovers
|
|
8
|
+
* plugins from every directory in its scan list — including
|
|
9
|
+
* `Global.Path.config` — by globbing `{plugin,plugins}/*.{ts,js}` via
|
|
10
|
+
* `ConfigPlugin.load(dir)`.
|
|
11
|
+
*
|
|
12
|
+
* We MUST NOT write into the user's repo working tree. The repo is a checkout
|
|
13
|
+
* the agent operates on; only the agent's own tools (gated by
|
|
14
|
+
* `OPENCODE_PERMISSION`) may modify it. The whole reason we redirect HOME and
|
|
15
|
+
* XDG_CONFIG_HOME is so harness-side files (config, plugins, scratch state)
|
|
16
|
+
* land in the tmpdir.
|
|
17
|
+
*
|
|
18
|
+
* Why the events plugin exists: opencode's `task` tool runs subagents
|
|
19
|
+
* in-process and the CLI's `cli/cmd/run.ts` event loop filters
|
|
20
|
+
* `part.sessionID !== sessionID`, so subagent-internal `message.part.updated`
|
|
21
|
+
* events are silently discarded before reaching our parent NDJSON stream.
|
|
22
|
+
* plugins, by contrast, receive EVERY bus event via `bus.subscribeAll()`
|
|
23
|
+
* regardless of session.
|
|
24
|
+
*
|
|
25
|
+
* The events plugin re-emits every relevant bus event onto opencode's stdout
|
|
26
|
+
* as a single JSON line wrapped in a sentinel envelope. our `runOpenCode`
|
|
27
|
+
* parser recognises the envelope, unpacks it, and routes the inner part
|
|
28
|
+
* through the existing handlers with a per-session label from `SessionLabeler`
|
|
29
|
+
* so each subagent's tool calls / text appear inline alongside the
|
|
30
|
+
* orchestrator's.
|
|
31
|
+
*
|
|
32
|
+
* The subagent gate (the `tool.execute.before` hook that hard-blocks
|
|
33
|
+
* state-mutating MCP tool calls from a subagent session) lives in a SEPARATE
|
|
34
|
+
* plugin — `TERRAMEND_OPENCODE_GATE_PLUGIN_SOURCE` below — because it's the
|
|
35
|
+
* load-bearing security fence and must ship into the opencode harness,
|
|
36
|
+
* whereas this events re-emitter was only needed by the legacy CLI-parsing
|
|
37
|
+
* path (the active `opencode.ts` reads subagent events directly off the SDK
|
|
38
|
+
* event stream, so it installs ONLY the gate plugin). Deny-list source of
|
|
39
|
+
* truth: `src/agents/subagentToolGates.ts`.
|
|
40
|
+
*
|
|
41
|
+
* Dumb plugin / smart parent split: the events plugin emits every part for
|
|
42
|
+
* every session. the parent dedupes against the orchestrator's own session id
|
|
43
|
+
* (which it already knows from the `init` event). this keeps the plugin trivial
|
|
44
|
+
* and keeps the per-session attribution logic on the parent side where the
|
|
45
|
+
* SessionLabeler already lives.
|
|
46
|
+
*
|
|
47
|
+
* Event-name prefixing: the wrapped event-type sentinel is
|
|
48
|
+
* `terramend_bus_event` — picked to be unmistakably ours so a future opencode
|
|
49
|
+
* release that introduces a coincidentally-named event type won't collide.
|
|
50
|
+
*/
|
|
51
|
+
export declare const TERRAMEND_BUS_EVENT_TYPE: "terramend_bus_event";
|
|
52
|
+
export declare const TERRAMEND_OPENCODE_PLUGIN_FILENAME: "terramend-events.ts";
|
|
53
|
+
export declare const TERRAMEND_OPENCODE_GATE_PLUGIN_FILENAME: "terramend-subagent-gate.ts";
|
|
54
|
+
/**
|
|
55
|
+
* Source written verbatim to `<XDG_CONFIG_HOME>/opencode/plugin/terramend-events.ts`.
|
|
56
|
+
*
|
|
57
|
+
* - Structural typing only (no runtime import of `@opencode-ai/plugin`):
|
|
58
|
+
* opencode installs that dep into the directory containing the plugin
|
|
59
|
+
* alongside discovery, but a) the dep isn't required for the structural
|
|
60
|
+
* shape we use, and b) keeping zero imports avoids any module-resolution
|
|
61
|
+
* coupling to opencode's plugin-loader internals across versions.
|
|
62
|
+
* - default export is the plugin factory (opencode's plugin loader accepts
|
|
63
|
+
* default exports as the server entrypoint).
|
|
64
|
+
* - we only forward `message.part.updated`. that's where the user-visible
|
|
65
|
+
* subagent activity (tool calls, text, step transitions) lives. add more
|
|
66
|
+
* event types here if the parent needs them.
|
|
67
|
+
* - JSON.stringify+single write keeps the line atomic up to PIPE_BUF (4KB on
|
|
68
|
+
* Linux). longer parts may interleave with concurrent stdout writers; the
|
|
69
|
+
* parser tolerates non-JSON lines (logs them at debug) so a torn line is a
|
|
70
|
+
* missed event, not a crash.
|
|
71
|
+
*/
|
|
72
|
+
export declare const TERRAMEND_OPENCODE_PLUGIN_SOURCE: string;
|
|
73
|
+
/**
|
|
74
|
+
* Standalone subagent gate plugin written to
|
|
75
|
+
* `<XDG_CONFIG_HOME>/opencode/plugin/terramend-subagent-gate.ts`. Installed by
|
|
76
|
+
* the in-process `opencode.ts` harness — the gate is the load-bearing security
|
|
77
|
+
* fence, so it ships independently of the events re-emitter above (which the
|
|
78
|
+
* in-process harness doesn't need).
|
|
79
|
+
*
|
|
80
|
+
* Hard-blocks state-mutating MCP tool calls originating from a subagent
|
|
81
|
+
* session via `tool.execute.before`, complementing the runtime backstops from
|
|
82
|
+
* PR #796 (action/mcp/checkout.ts, action/mcp/git.ts). Deny-list source of
|
|
83
|
+
* truth: `action/agents/subagentToolGates.ts`.
|
|
84
|
+
*/
|
|
85
|
+
export declare const TERRAMEND_OPENCODE_GATE_PLUGIN_SOURCE: string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type OpenCodeConfig = {
|
|
2
|
+
mcp?: Record<string, unknown>;
|
|
3
|
+
permission?: Record<string, unknown>;
|
|
4
|
+
provider?: Record<string, unknown>;
|
|
5
|
+
agent?: Record<string, unknown>;
|
|
6
|
+
experimental?: Record<string, unknown>;
|
|
7
|
+
model?: string;
|
|
8
|
+
enabled_providers?: string[];
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Build the `provider.google.models[id].options` map that pins every direct-Google
|
|
13
|
+
* Gemini alias to `thinkingLevel: "high"`. Sourced from the model registry so
|
|
14
|
+
* adding/renaming a Google alias in `action/models.ts` flows through automatically.
|
|
15
|
+
*/
|
|
16
|
+
export declare function geminiHighThinkingOverrides(): Record<string, {
|
|
17
|
+
options: object;
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Read-only `reviewfrog` subagent for lens-based review. Non-mutative +
|
|
21
|
+
* non-recursive — enforced by the system prompt in reviewer.ts.
|
|
22
|
+
*
|
|
23
|
+
* Per-subagent `model:` override is driven by the registry in
|
|
24
|
+
* `action/models.ts` via each alias's `subagentModel` field. Currently wired:
|
|
25
|
+
* Anthropic opus → sonnet, OpenAI gpt-pro → gpt and gpt → gpt-5.4, Google
|
|
26
|
+
* gemini-pro → gemini-flash. Other providers inherit (no override).
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildReviewerAgentConfig(orchestratorModel: string | undefined): Record<string, unknown>;
|
|
29
|
+
/**
|
|
30
|
+
* Install the opencode-ai npm tarball and return the path to the executable.
|
|
31
|
+
*
|
|
32
|
+
* The bin path differs by version: v1.4.x and earlier shipped `bin/opencode`;
|
|
33
|
+
* v1.14+ renames the platform-specific binary to `bin/opencode.exe` for every
|
|
34
|
+
* OS via the postinstall script. Callers pass the binPath that matches their
|
|
35
|
+
* pinned version so a v1↔v2 swap can't silently install the wrong file.
|
|
36
|
+
*/
|
|
37
|
+
export declare function installOpencodeCli(params: {
|
|
38
|
+
binPath: string;
|
|
39
|
+
}): Promise<string>;
|
|
40
|
+
export declare function autoSelectModel(): string | undefined;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { type AgentResult, type AgentRunContext, type AgentUsage, type PostRunIssues, type StopHookFailure } from "#app/agents/shared";
|
|
2
|
+
import type { ToolState } from "#app/toolState";
|
|
3
|
+
/**
|
|
4
|
+
* derive "agent picked a review mode but never produced visible output" from
|
|
5
|
+
* the literal facts on `toolState`. returns the selected mode when the gate
|
|
6
|
+
* should fire, `null` otherwise — pure read, no side effects, safe to invoke
|
|
7
|
+
* after every agent attempt.
|
|
8
|
+
*
|
|
9
|
+
* the gate is anchored to `hadProgressComment` so silent runs (non-issue
|
|
10
|
+
* events, dispatcher skipped seeding) don't fire a nudge there's no UI for.
|
|
11
|
+
*
|
|
12
|
+
* `Review` and `IncrementalReview` have different valid exits:
|
|
13
|
+
* - Review: only `create_pull_request_review` counts. `report_progress` is
|
|
14
|
+
* not a substitute — a Review run that exits with just a summary comment
|
|
15
|
+
* has produced nothing reviewable on the PR. matches the hard-fail
|
|
16
|
+
* message at `expected = "create_pull_request_review"` below.
|
|
17
|
+
* - IncrementalReview: `report_progress` is a legitimate "no review
|
|
18
|
+
* warranted" exit, so either toolState flag short-circuits.
|
|
19
|
+
* splitting per mode also closes the bypass where a subagent (e.g. a
|
|
20
|
+
* `task`-dispatched `reviewfrog` lens) calls `report_progress` and silences
|
|
21
|
+
* the gate even though the orchestrator never submitted a review.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getUnsubmittedReview(toolState: ToolState): "Review" | "IncrementalReview" | null;
|
|
24
|
+
/**
|
|
25
|
+
* run the user-configured stop hook.
|
|
26
|
+
*
|
|
27
|
+
* parallel to `executeLifecycleHook` (which soft-fails with a warning), but
|
|
28
|
+
* returns structured output so agent harnesses can feed the failure back into
|
|
29
|
+
* the session as a resume prompt.
|
|
30
|
+
*
|
|
31
|
+
* - non-zero exit → `StopHookFailure`, actionable: the output is fed to the
|
|
32
|
+
* agent so it can fix the underlying issue.
|
|
33
|
+
* - timeout / spawn error → null, treated as passed: we can't usefully ask the
|
|
34
|
+
* agent to fix an infrastructure problem, and retrying would risk infinite
|
|
35
|
+
* loops.
|
|
36
|
+
*/
|
|
37
|
+
export declare function executeStopHook(script: string): Promise<StopHookFailure | null>;
|
|
38
|
+
export declare function buildStopHookPrompt(failure: StopHookFailure): string;
|
|
39
|
+
export declare function buildSummaryStalePrompt(filePath: string): string;
|
|
40
|
+
export declare function buildUnsubmittedReviewPrompt(mode: "Review" | "IncrementalReview"): string;
|
|
41
|
+
/**
|
|
42
|
+
* check the post-run gates: did the stop hook pass, is the working tree
|
|
43
|
+
* clean, and (when applicable) did the agent touch the rolling PR summary
|
|
44
|
+
* snapshot or produce review output? returns everything that still needs
|
|
45
|
+
* nudging so the caller can render a single combined resume prompt.
|
|
46
|
+
*
|
|
47
|
+
* reads run state directly off `ctx.toolState` so each invocation sees the
|
|
48
|
+
* latest mutations from MCP tool calls. `skipSummaryStale` lets the loop
|
|
49
|
+
* suppress the summary-stale check after the one-shot nudge has been
|
|
50
|
+
* delivered (re-firing it would burn the retry budget on a soft gate the
|
|
51
|
+
* agent has already decided not to act on).
|
|
52
|
+
*/
|
|
53
|
+
export declare function collectPostRunIssues(ctx: AgentRunContext, options?: {
|
|
54
|
+
skipSummaryStale?: boolean;
|
|
55
|
+
}): Promise<PostRunIssues>;
|
|
56
|
+
export declare function buildPostRunPrompt(issues: PostRunIssues): string;
|
|
57
|
+
/**
|
|
58
|
+
* terminal-only post-run finalize: re-checks the hard-fail gates after the
|
|
59
|
+
* agent has exited and converts a successful result to a hard-fail when
|
|
60
|
+
* `stopHook` or `unsubmittedReview` is still failing. used by harnesses
|
|
61
|
+
* that inject follow-up turns via a mechanism other than the resume
|
|
62
|
+
* callback (e.g. the Claude managed Stop hook + gate server). soft gates
|
|
63
|
+
* (`dirtyTree`, `summaryStale`) are intentionally not re-checked here —
|
|
64
|
+
* they never flip a successful run to failed.
|
|
65
|
+
*/
|
|
66
|
+
export declare function finalizeAgentResult<R extends AgentResult>(params: {
|
|
67
|
+
ctx: AgentRunContext;
|
|
68
|
+
result: R;
|
|
69
|
+
}): Promise<R>;
|
|
70
|
+
export declare function shouldRunReflection(mode: string | undefined): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* prompt for a dedicated post-run reflection turn nudging the agent to edit
|
|
73
|
+
* the rolling learnings file if it discovered anything worth persisting.
|
|
74
|
+
*
|
|
75
|
+
* this exists because passive "if you learned something, write it down"
|
|
76
|
+
* instructions baked into mode checklists are frequently ignored — the agent
|
|
77
|
+
* stays focused on the task and the meta-ask falls through. delivering it
|
|
78
|
+
* as its own resume turn, with nothing competing for attention, raises the
|
|
79
|
+
* fire rate substantially.
|
|
80
|
+
*
|
|
81
|
+
* the file is the single source of truth — there is no separate MCP tool
|
|
82
|
+
* call. the server reads the file at end-of-run and persists any edits to
|
|
83
|
+
* `Repo.learnings`.
|
|
84
|
+
*
|
|
85
|
+
* the prompt copy is shaped by repo-wide audits of the actual content the
|
|
86
|
+
* agent has been writing (issue #619 in terramend/app). recurring failure
|
|
87
|
+
* modes the framing pushes back on:
|
|
88
|
+
* - massive multi-paragraph "bullets" that are really mini-articles
|
|
89
|
+
* - facts anchored to moving repo state (PR / review / commit / branch
|
|
90
|
+
* refs, dates, version pins, line numbers) that decay within weeks
|
|
91
|
+
* - sections growing into giant flat lists with no internal structure,
|
|
92
|
+
* forcing future runs to read kilobytes to find one fact
|
|
93
|
+
*
|
|
94
|
+
* single litmus delivered in the prompt: "would a future run on this repo
|
|
95
|
+
* do its work better because this bullet exists?". tool-quirk workarounds
|
|
96
|
+
* are explicitly allowed when the agent burned calls discovering the
|
|
97
|
+
* quirk this run — recording the workaround prevents next run from
|
|
98
|
+
* repeating the waste. tradeoff: the same quirk gets duplicated across
|
|
99
|
+
* repos, so when a quirk is fixed upstream in tool descriptions the
|
|
100
|
+
* per-repo bullets go stale and we have no batch-invalidation path.
|
|
101
|
+
*/
|
|
102
|
+
export declare function buildLearningsReflectionPrompt(filePath: string): string;
|
|
103
|
+
/**
|
|
104
|
+
* shared post-run retry loop used by every agent harness.
|
|
105
|
+
*
|
|
106
|
+
* checks the post-run gates (stop hook + dirty tree), and if either is
|
|
107
|
+
* failing, invokes `resume` to let the agent fix and push in the same turn.
|
|
108
|
+
* bails at `MAX_POST_RUN_RETRIES` attempts. the `canResume` predicate is
|
|
109
|
+
* consulted before each retry — harnesses that can't re-enter the session
|
|
110
|
+
* (e.g. claude without a sessionId) return false here.
|
|
111
|
+
*
|
|
112
|
+
* an optional `reflectionPrompt` fires exactly once, after the gates first
|
|
113
|
+
* observe a clean state. it's a one-shot nudge (e.g. "update learnings if
|
|
114
|
+
* relevant"), not a gate, so it does not consume the gate-retry budget. if
|
|
115
|
+
* the reflection turn dirties the tree, the loop picks that up on the next
|
|
116
|
+
* iteration via the normal dirty-tree gate.
|
|
117
|
+
*
|
|
118
|
+
* stop hook must pass for the run to succeed; persistent hook failures are
|
|
119
|
+
* surfaced as `AgentResult.error`. dirty-tree-only failures preserve prior
|
|
120
|
+
* behavior: they're logged but don't fail the run.
|
|
121
|
+
*/
|
|
122
|
+
export declare function runPostRunRetryLoop<R extends AgentResult>(params: {
|
|
123
|
+
ctx: AgentRunContext;
|
|
124
|
+
initialResult: R;
|
|
125
|
+
initialUsage: AgentUsage | undefined;
|
|
126
|
+
resume: (context: {
|
|
127
|
+
prompt: string;
|
|
128
|
+
previousResult: R;
|
|
129
|
+
}) => Promise<R>;
|
|
130
|
+
canResume?: ((result: R) => boolean) | undefined;
|
|
131
|
+
reflectionPrompt?: string | undefined;
|
|
132
|
+
}): Promise<AgentResult>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Definition of the `reviewfrog` named subagent — the constrained
|
|
3
|
+
* read-only worker dispatched by Build mode self-review and the in-Terramend
|
|
4
|
+
* /anneal multi-lens review.
|
|
5
|
+
*
|
|
6
|
+
* The contract: non-mutative + non-recursive.
|
|
7
|
+
* allow: file reads, grep/glob, web search/fetch, read-only MCP queries
|
|
8
|
+
* deny: state-changing MCP tools, file writes, shell, nested subagent dispatch
|
|
9
|
+
*
|
|
10
|
+
* Enforcement is now belt-and-suspenders:
|
|
11
|
+
* 1. Machine-enforced PreToolUse gates intercept every state-mutating MCP
|
|
12
|
+
* tool call originating from a subagent session and refuse it before
|
|
13
|
+
* MCP runs. See action/agents/subagentToolGates.ts (the deny list),
|
|
14
|
+
* action/agents/claudePretoolGate.ts (Claude Code's PreToolUse hook),
|
|
15
|
+
* and action/agents/opencodePlugin.ts (opencode's tool.execute.before
|
|
16
|
+
* hook). Followed PR #796 which added runtime backstops inside
|
|
17
|
+
* checkout_pr / push_branch after a subagent-originated tool call
|
|
18
|
+
* clobbered an unrelated PR branch in zed-industries/cloud.
|
|
19
|
+
* 2. The prose system prompt below as a backup against (a) tools added
|
|
20
|
+
* to the MCP server without a corresponding deny-list update, and
|
|
21
|
+
* (b) shell/git read-vs-write distinctions the static gate can't see.
|
|
22
|
+
* It states the rule as a no-op-if-reverted invariant the model can
|
|
23
|
+
* apply to any tool, including ones added after this comment was
|
|
24
|
+
* written.
|
|
25
|
+
*
|
|
26
|
+
* Historical note: per-agent `disallowedTools` in claude-code is upstream-
|
|
27
|
+
* broken for subagent-spawned tool calls (anthropics/claude-agent-sdk-
|
|
28
|
+
* typescript#172, open as of Mar 2026), which is why the gate runs at
|
|
29
|
+
* PreToolUse rather than tool-registration time.
|
|
30
|
+
*/
|
|
31
|
+
export declare const REVIEWER_AGENT_NAME = "reviewfrog";
|
|
32
|
+
/**
|
|
33
|
+
* System prompt baked into the named reviewer subagent. The orchestrator
|
|
34
|
+
* supplies the per-call task content (YOUR TASK, the diff, the lens) at
|
|
35
|
+
* dispatch time; this preamble enforces the role and constraints regardless
|
|
36
|
+
* of what the orchestrator sends.
|
|
37
|
+
*/
|
|
38
|
+
export declare const REVIEWER_SYSTEM_PROMPT: string;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Track per-session labels so log lines from parallel subagents can be
|
|
3
|
+
* differentiated. The orchestrator dispatches lens subagents
|
|
4
|
+
* via the Task tool; each subagent runs in its own opencode/claude Session
|
|
5
|
+
* with its own `sessionID` (or `session_id`) tag on the NDJSON event stream.
|
|
6
|
+
*
|
|
7
|
+
* Without per-session prefixing, parallel subagent tool_use / tool_result /
|
|
8
|
+
* text events appear as a single interleaved stream tagged with `[Terramend]`,
|
|
9
|
+
* making it impossible for a human reading the logs to attribute work to a
|
|
10
|
+
* specific lens.
|
|
11
|
+
*
|
|
12
|
+
* The labeler is deliberately runtime-agnostic — both opencode.ts and
|
|
13
|
+
* claude.ts feed it the same shape. The contract is FIFO: when the orchestrator
|
|
14
|
+
* dispatches N task tool_use blocks in a single assistant turn (the parallel
|
|
15
|
+
* fan-out the multi-lens prompt requires), the i-th new sessionID is assumed
|
|
16
|
+
* to belong to the i-th task dispatch. This is correct as long as parallel
|
|
17
|
+
* dispatches are emitted in source-order and the runtimes respect that order
|
|
18
|
+
* when assigning child sessions; we do not depend on it for correctness of
|
|
19
|
+
* the read-only contract — only for log readability.
|
|
20
|
+
*/
|
|
21
|
+
export interface TaskDispatchInput {
|
|
22
|
+
description?: string | undefined;
|
|
23
|
+
subagent_type?: string | undefined;
|
|
24
|
+
prompt?: string | undefined;
|
|
25
|
+
}
|
|
26
|
+
export declare const ORCHESTRATOR_LABEL = "orchestrator";
|
|
27
|
+
/**
|
|
28
|
+
* Extract a human-readable label from a Task tool's input. Tries (in order):
|
|
29
|
+
* 1. explicit `lens: <name>` marker on a line in the prompt — preferred,
|
|
30
|
+
* lets the orchestrator name the lens deterministically
|
|
31
|
+
* 2. the Task tool's `description` field — short, written by orchestrator
|
|
32
|
+
* per call, usually enough
|
|
33
|
+
* 3. the `subagent_type` — falls back to the named
|
|
34
|
+
* subagent identity when description is missing
|
|
35
|
+
* 4. generic "subagent" — last resort
|
|
36
|
+
*/
|
|
37
|
+
export declare function deriveLabelFromTaskInput(input: TaskDispatchInput): string;
|
|
38
|
+
/**
|
|
39
|
+
* Stateful tracker mapping subagent activity back to human-readable labels.
|
|
40
|
+
*
|
|
41
|
+
* Two attribution channels are supported because the runtimes differ:
|
|
42
|
+
*
|
|
43
|
+
* - **OpenCode** spawns each subagent as its own opencode `Session` with
|
|
44
|
+
* a distinct `sessionID`. The harness records each Task dispatch into a
|
|
45
|
+
* pending FIFO queue; the next previously-unseen sessionID consumes the
|
|
46
|
+
* head of the queue and binds it to that label.
|
|
47
|
+
*
|
|
48
|
+
* - **Claude Code** runs subagents inside the orchestrator's session — they
|
|
49
|
+
* all share `session_id` — and instead stamps every subagent message with
|
|
50
|
+
* `parent_tool_use_id` pointing at the Agent tool_use id that spawned them.
|
|
51
|
+
* The harness binds each Agent tool_use id to its dispatched label up
|
|
52
|
+
* front, then `labelFor` looks the label up directly when an event arrives
|
|
53
|
+
* carrying that `parent_tool_use_id`.
|
|
54
|
+
*
|
|
55
|
+
* `labelFor(sessionID, parentToolUseId?)` accepts both: when
|
|
56
|
+
* `parentToolUseId` is set and known it short-circuits to the direct mapping;
|
|
57
|
+
* otherwise it falls through to the FIFO/sessionID path.
|
|
58
|
+
*/
|
|
59
|
+
export declare class SessionLabeler {
|
|
60
|
+
private readonly labels;
|
|
61
|
+
private readonly labelsByToolUseId;
|
|
62
|
+
private readonly pendingLabels;
|
|
63
|
+
private fallbackCounter;
|
|
64
|
+
/**
|
|
65
|
+
* Record a Task/Agent tool dispatch.
|
|
66
|
+
*
|
|
67
|
+
* @param input Task tool input — used to derive the lens label.
|
|
68
|
+
* @param toolUseId Optional Agent tool_use id. When provided, future events
|
|
69
|
+
* carrying `parent_tool_use_id === toolUseId` resolve
|
|
70
|
+
* directly to this label without consuming the FIFO queue
|
|
71
|
+
* (Claude path). Always also pushed to the FIFO queue so
|
|
72
|
+
* the OpenCode path still works when toolUseId is absent.
|
|
73
|
+
*/
|
|
74
|
+
recordTaskDispatch(input: TaskDispatchInput, toolUseId?: string | null): string;
|
|
75
|
+
/**
|
|
76
|
+
* Return a label for the given event.
|
|
77
|
+
*
|
|
78
|
+
* @param sessionID Session id from the event (OpenCode: per-session;
|
|
79
|
+
* Claude: shared across orchestrator + subagents).
|
|
80
|
+
* @param parentToolUseId Claude's `parent_tool_use_id` — non-null on
|
|
81
|
+
* subagent messages. When set and known, takes
|
|
82
|
+
* priority over the FIFO/sessionID path.
|
|
83
|
+
*/
|
|
84
|
+
labelFor(sessionID: string | undefined | null, parentToolUseId?: string | null): string;
|
|
85
|
+
/** number of distinct sessions seen so far (for diagnostics) */
|
|
86
|
+
size(): number;
|
|
87
|
+
/** all (sessionID, label) pairs, oldest first */
|
|
88
|
+
entries(): Array<[string, string]>;
|
|
89
|
+
/** how many pending labels are queued waiting to bind to a new session */
|
|
90
|
+
pendingDispatchCount(): number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Format a log message with a session label prefix in magenta. Mirrors the
|
|
94
|
+
* style of utils/log.ts:prefixLines() so per-session prefixes look the same
|
|
95
|
+
* as the dormant withLogPrefix-based ones.
|
|
96
|
+
*/
|
|
97
|
+
export declare function formatWithLabel(label: string, message: string): string;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import type { AgentId } from "#app/external";
|
|
2
|
+
import type { ToolState } from "#app/toolState";
|
|
3
|
+
import type { ResolvedInstructions } from "#app/utils/instructions";
|
|
4
|
+
import type { ResolvedPayload } from "#app/utils/payload";
|
|
5
|
+
import type { TodoTracker } from "#app/utils/todoTracking";
|
|
6
|
+
export declare const MAX_STDERR_LINES = 20;
|
|
7
|
+
/**
|
|
8
|
+
* how many times the post-run loop may resume the agent to fix a dirty tree
|
|
9
|
+
* or a failing stop hook before giving up.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MAX_POST_RUN_RETRIES = 3;
|
|
12
|
+
export declare function getGitStatus(): string;
|
|
13
|
+
export declare function buildCommitPrompt(status: string): string;
|
|
14
|
+
export interface StopHookFailure {
|
|
15
|
+
exitCode: number;
|
|
16
|
+
output: string;
|
|
17
|
+
}
|
|
18
|
+
export interface SummaryStale {
|
|
19
|
+
/** absolute path to the seeded snapshot file the agent was meant to edit. */
|
|
20
|
+
filePath: string;
|
|
21
|
+
}
|
|
22
|
+
export interface PostRunIssues {
|
|
23
|
+
stopHook?: StopHookFailure;
|
|
24
|
+
dirtyTree?: string;
|
|
25
|
+
/** populated when the rolling PR summary file is byte-identical to its
|
|
26
|
+
* seed, i.e. the agent never touched it. soft gate — nudges once via a
|
|
27
|
+
* resume turn but never fails the run, parallel to dirtyTree semantics. */
|
|
28
|
+
summaryStale?: SummaryStale;
|
|
29
|
+
/**
|
|
30
|
+
* populated when the agent selected a review mode but the post-run check
|
|
31
|
+
* over toolState shows neither a `create_pull_request_review` submission
|
|
32
|
+
* nor a final `report_progress` write happened. derived inline from
|
|
33
|
+
* `toolState.selectedMode` + `toolState.review` + `toolState.finalSummaryWritten`
|
|
34
|
+
* via {@link getUnsubmittedReview} — no parallel toolState flag is stored.
|
|
35
|
+
* carries the mode name so the resume prompt can reference it. handled like
|
|
36
|
+
* `stopHook`: nudge via resume, hard-fail if still unsatisfied after
|
|
37
|
+
* `MAX_POST_RUN_RETRIES`.
|
|
38
|
+
*/
|
|
39
|
+
unsubmittedReview?: "Review" | "IncrementalReview";
|
|
40
|
+
}
|
|
41
|
+
export declare function hasPostRunIssues(issues: PostRunIssues): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* token/cost usage data from a single agent run.
|
|
44
|
+
*
|
|
45
|
+
* NOTE on semantics: `inputTokens` here is the *total* billable input for the
|
|
46
|
+
* run — non-cached input + cache read + cache write — matching the per-agent
|
|
47
|
+
* SDK conventions. This is what gets persisted to `WorkflowRun.inputTokens`.
|
|
48
|
+
*
|
|
49
|
+
* The stdout token table and markdown step summary display a different "Input"
|
|
50
|
+
* column that shows only the non-cached portion (derivable as
|
|
51
|
+
* `inputTokens - cacheReadTokens - cacheWriteTokens`) so humans can see the
|
|
52
|
+
* cache hit ratio at a glance. Dashboards that query `WorkflowRun.inputTokens`
|
|
53
|
+
* directly are seeing the full total, not the log column.
|
|
54
|
+
*/
|
|
55
|
+
export interface AgentUsage {
|
|
56
|
+
agent: string;
|
|
57
|
+
/** full billable input: non-cached + cache read + cache write */
|
|
58
|
+
inputTokens: number;
|
|
59
|
+
outputTokens: number;
|
|
60
|
+
cacheReadTokens?: number | undefined;
|
|
61
|
+
cacheWriteTokens?: number | undefined;
|
|
62
|
+
costUsd?: number | undefined;
|
|
63
|
+
}
|
|
64
|
+
export interface AgentToolUseEvent {
|
|
65
|
+
toolName: string;
|
|
66
|
+
input: unknown;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Result returned by agent execution
|
|
70
|
+
*/
|
|
71
|
+
export interface AgentResult {
|
|
72
|
+
success: boolean;
|
|
73
|
+
output?: string | undefined;
|
|
74
|
+
error?: string | undefined;
|
|
75
|
+
metadata?: Record<string, unknown>;
|
|
76
|
+
usage?: AgentUsage | undefined;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Env var that carries the per-run MCP bearer token to BOTH agent harnesses,
|
|
80
|
+
* which reference it from their MCP client config so the on-disk/in-config form
|
|
81
|
+
* holds only a placeholder, never the raw token:
|
|
82
|
+
* - Claude Code expands `${TERRAMEND_MCP_TOKEN}` in .mcp.json headers.
|
|
83
|
+
* - opencode expands `{env:TERRAMEND_MCP_TOKEN}` in remote-MCP headers.
|
|
84
|
+
* The `_TOKEN` suffix also makes filterEnv() strip it from the MCP shell
|
|
85
|
+
* sandbox. Set only on the agent/server spawn env (never process.env), so a
|
|
86
|
+
* co-located dependency-install subprocess never inherits it. See
|
|
87
|
+
* ToolContext.mcpServerToken and gateServer.ts for the same pattern.
|
|
88
|
+
*/
|
|
89
|
+
export declare const MCP_SERVER_TOKEN_ENV = "TERRAMEND_MCP_TOKEN";
|
|
90
|
+
/**
|
|
91
|
+
* Context passed to agent.run() and threaded through the post-run loop.
|
|
92
|
+
*
|
|
93
|
+
* design rule: this is the single object that flows through the harness and
|
|
94
|
+
* downstream utilities by reference. derived predicates (e.g.
|
|
95
|
+
* `getUnsubmittedReview`), tmpfile paths, and seed bytes live on
|
|
96
|
+
* `toolState` — read them at the call site, do not duplicate them onto this
|
|
97
|
+
* interface. utilities that need run state should accept `ctx` whole, not
|
|
98
|
+
* destructure a narrow subset.
|
|
99
|
+
*/
|
|
100
|
+
export interface AgentRunContext {
|
|
101
|
+
payload: ResolvedPayload;
|
|
102
|
+
resolvedModel?: string | undefined;
|
|
103
|
+
mcpServerUrl: string;
|
|
104
|
+
/** per-run bearer token the agent's MCP client presents to mcpServerUrl. See
|
|
105
|
+
* ToolContext.mcpServerToken — delivered to the client config out-of-band so
|
|
106
|
+
* it never lands in a readable file. */
|
|
107
|
+
mcpServerToken: string;
|
|
108
|
+
tmpdir: string;
|
|
109
|
+
/** harness-owned secret paths that agent filesystem tools must never read. */
|
|
110
|
+
secretDenyPaths?: string[] | undefined;
|
|
111
|
+
instructions: ResolvedInstructions;
|
|
112
|
+
todoTracker?: TodoTracker | undefined;
|
|
113
|
+
/**
|
|
114
|
+
* user-configured stop hook script. runs after the agent finishes each
|
|
115
|
+
* attempt; non-zero exit resumes the agent with the hook output as
|
|
116
|
+
* guidance. null when the repo has no stop hook configured.
|
|
117
|
+
*/
|
|
118
|
+
stopScript?: string | null | undefined;
|
|
119
|
+
/**
|
|
120
|
+
* mutable per-run state shared with the MCP server (by reference). post-run
|
|
121
|
+
* gates read fresh values from it after each agent attempt — `summaryFilePath`,
|
|
122
|
+
* `summarySeed`, `selectedMode`, `review`, `finalSummaryWritten`,
|
|
123
|
+
* `hadProgressComment` are all consulted by `collectPostRunIssues`. see
|
|
124
|
+
* `action/toolState.ts` for the literal-state design rule.
|
|
125
|
+
*/
|
|
126
|
+
toolState: ToolState;
|
|
127
|
+
/**
|
|
128
|
+
* called synchronously when the agent subprocess is killed for inner
|
|
129
|
+
* activity timeout. lets main.ts tear down shared resources (MCP HTTP
|
|
130
|
+
* server) so lingering SSE reconnects don't keep the outer timer alive.
|
|
131
|
+
*/
|
|
132
|
+
onActivityTimeout?: (() => void) | undefined;
|
|
133
|
+
onToolUse?: ((event: AgentToolUseEvent) => void) | undefined;
|
|
134
|
+
/**
|
|
135
|
+
* Terramend API JWT scoped to this run. agents only need this when they
|
|
136
|
+
* have to write state back to Terramend mid-run (today: opencode.ts uses
|
|
137
|
+
* it to seed the post-hook's writeback envelope for Codex auth refresh).
|
|
138
|
+
* empty string when the run wasn't context-resolved (e.g. local dry-runs).
|
|
139
|
+
*/
|
|
140
|
+
apiToken: string;
|
|
141
|
+
}
|
|
142
|
+
export interface Agent {
|
|
143
|
+
name: AgentId;
|
|
144
|
+
install: (token?: string) => Promise<string>;
|
|
145
|
+
run: (ctx: AgentRunContext) => Promise<AgentResult>;
|
|
146
|
+
}
|
|
147
|
+
export declare const agent: (input: Agent) => Agent;
|
|
148
|
+
/** format a USD cost to 4 decimal places, always showing the leading zero */
|
|
149
|
+
export declare function formatCostUsd(costUsd: number): string;
|
|
150
|
+
/**
|
|
151
|
+
* merge two AgentUsage snapshots into one running total.
|
|
152
|
+
*
|
|
153
|
+
* both agent harnesses invoke their runner multiple times per `run()` when the
|
|
154
|
+
* post-run retry loop kicks in (MAX_POST_RUN_RETRIES). each invocation
|
|
155
|
+
* produces its own AgentUsage; we sum them so downstream callers (usage
|
|
156
|
+
* summary, WorkflowRun persistence) see the whole session — not just the
|
|
157
|
+
* final retry's slice.
|
|
158
|
+
*
|
|
159
|
+
* returns `undefined` when both sides are empty so callers can short-circuit
|
|
160
|
+
* without a special case. zero-valued cache / cost fields are dropped to
|
|
161
|
+
* `undefined` for symmetry with each harness's `buildUsage`.
|
|
162
|
+
*/
|
|
163
|
+
export declare function mergeAgentUsage(a: AgentUsage | undefined, b: AgentUsage | undefined): AgentUsage | undefined;
|
|
164
|
+
/**
|
|
165
|
+
* unified per-run token table used by every agent harness.
|
|
166
|
+
*
|
|
167
|
+
* columns are kept stable across agents and models so downstream log parsers
|
|
168
|
+
* (scripts/token-usage.ts, cost dashboards) only have to understand one format:
|
|
169
|
+
*
|
|
170
|
+
* Input non-cached input tokens sent this run
|
|
171
|
+
* Cache Read input tokens served from prompt cache (Anthropic, etc.)
|
|
172
|
+
* Cache Write input tokens written to prompt cache this run
|
|
173
|
+
* Output assistant output tokens
|
|
174
|
+
* Total sum of the four columns — the real billable quantity
|
|
175
|
+
* Cost ($) USD cost reported by the provider (only rendered when known)
|
|
176
|
+
*
|
|
177
|
+
* models that don't report prompt caching leave Cache Read / Write at 0.
|
|
178
|
+
* OpenCode emits per-step `part.cost` sourced from models.dev (works across
|
|
179
|
+
* Anthropic, OpenAI, Google, xAI, DeepSeek, Moonshot, OpenRouter, etc.);
|
|
180
|
+
* Claude CLI emits `total_cost_usd` on its final `result` event. pass the
|
|
181
|
+
* accumulated value via `costUsd` to render the Cost column.
|
|
182
|
+
*/
|
|
183
|
+
export declare function logTokenTable(t: {
|
|
184
|
+
input: number;
|
|
185
|
+
cacheRead: number;
|
|
186
|
+
cacheWrite: number;
|
|
187
|
+
output: number;
|
|
188
|
+
costUsd?: number | undefined;
|
|
189
|
+
}): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive a cheaper subagent model override from the orchestrator's resolved
|
|
3
|
+
* model spec.
|
|
4
|
+
*
|
|
5
|
+
* This is a pure registry lookup: every alias in `action/models.ts` declares
|
|
6
|
+
* its own `subagentModel` (alias key in the same provider). At runtime we
|
|
7
|
+
* reverse-lookup the orchestrator's resolved slug to find the alias that
|
|
8
|
+
* produced it, follow the `subagentModel` pointer, and return the target
|
|
9
|
+
* alias's resolve / openRouterResolve depending on which route the
|
|
10
|
+
* orchestrator was using.
|
|
11
|
+
*
|
|
12
|
+
* Returns `{ reviewer: undefined }` when the orchestrator's alias has no
|
|
13
|
+
* `subagentModel` (e.g. it's already at a sufficiently cheap tier, or its
|
|
14
|
+
* provider doesn't have a clean cheaper-but-capable sibling). See models.ts
|
|
15
|
+
* for the wiring + per-provider rationale.
|
|
16
|
+
*/
|
|
17
|
+
export declare function deriveSubagentModels(orchestratorSpec: string | undefined): {
|
|
18
|
+
reviewer: string | undefined;
|
|
19
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for MCP tools subagents are forbidden from calling.
|
|
3
|
+
*
|
|
4
|
+
* Subagents share the orchestrator's in-process git working tree, `toolState`,
|
|
5
|
+
* progress comment, and run-scoped pr/branch context. A subagent that calls
|
|
6
|
+
* `checkout_pr` switches the orchestrator's HEAD; one that calls `push_branch`
|
|
7
|
+
* pushes whatever the orchestrator happens to have committed. The 2026-05-18
|
|
8
|
+
* `zed-industries/cloud` incident hit exactly this: a `reviewfrog` lens
|
|
9
|
+
* dispatched `checkout_pr({2582})` mid-review, the orchestrator's next push
|
|
10
|
+
* clobbered an unrelated engineer's branch. PR #796 added runtime backstops
|
|
11
|
+
* inside `checkout_pr`/`push_branch`; this list is the upstream gate that
|
|
12
|
+
* stops the call from ever reaching MCP when it originates from a subagent.
|
|
13
|
+
*
|
|
14
|
+
* The gate is enforced at two pre-tool hooks:
|
|
15
|
+
* - opencode: `tool.execute.before` (action/agents/opencodePlugin.ts)
|
|
16
|
+
* - claude: `PreToolUse` settings hook (action/agents/claudePretoolGate.ts)
|
|
17
|
+
*
|
|
18
|
+
* Names are stored in their canonical bare form (the FastMCP tool `name`
|
|
19
|
+
* field). Each runtime presents them with a different prefix:
|
|
20
|
+
* - claude: `mcp__terramend__<name>`
|
|
21
|
+
* - opencode: `terramend_<name>`
|
|
22
|
+
* The hooks strip those prefixes before comparing.
|
|
23
|
+
*
|
|
24
|
+
* Read-only MCP tools (`get_*`, `list_*`, `git_fetch`, `get_check_suite_logs`,
|
|
25
|
+
* `await_dependency_installation`, etc.) and the `git`/`shell` tools stay off
|
|
26
|
+
* this list — denying them would make review work impossible. The reviewer system prompt
|
|
27
|
+
* (`action/agents/reviewer.ts`) already forbids state-changing shell/git
|
|
28
|
+
* subcommands as a prose constraint; this list is the belt-and-suspenders
|
|
29
|
+
* machine fence for the high-stakes mutations we can identify by name alone.
|
|
30
|
+
*
|
|
31
|
+
* When adding a state-changing MCP tool to `action/mcp/server.ts`, add its
|
|
32
|
+
* canonical name here too. Inclusions justified inline.
|
|
33
|
+
*/
|
|
34
|
+
export declare const SUBAGENT_DENIED_TOOLS: readonly ["checkout_pr", "push_branch", "push_tags", "delete_branch", "create_pull_request", "update_pull_request_body", "close_pull_request", "create_issue", "create_issue_comment", "edit_issue_comment", "reply_to_review_comment", "create_pull_request_review", "resolve_review_thread", "add_labels", "set_output", "report_progress", "select_mode", "start_dependency_installation", "kill_background", "upload_file"];
|
|
35
|
+
export type SubagentDeniedTool = (typeof SUBAGENT_DENIED_TOOLS)[number];
|
|
36
|
+
/**
|
|
37
|
+
* Strip the runtime-specific MCP prefix from a tool name and return the
|
|
38
|
+
* canonical bare name (matching FastMCP's `name:` field). Returns the input
|
|
39
|
+
* unchanged if it doesn't carry a known prefix — keeping comparison simple
|
|
40
|
+
* for native (non-MCP) tools, which never appear on the deny list anyway.
|
|
41
|
+
*/
|
|
42
|
+
export declare function stripMcpPrefix(toolName: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Whether `toolName` (in any runtime's prefix style) names a tool that
|
|
45
|
+
* subagents must not call.
|
|
46
|
+
*/
|
|
47
|
+
export declare function isSubagentDeniedTool(toolName: string): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Human-readable refusal surfaced to the model when a denied tool is gated.
|
|
50
|
+
* Phrased so a halfway-attentive subagent realises (a) the tool is denied to
|
|
51
|
+
* it specifically, (b) why (shared in-process state with the orchestrator),
|
|
52
|
+
* and (c) what to do instead (report findings; the orchestrator can call the
|
|
53
|
+
* tool directly).
|
|
54
|
+
*/
|
|
55
|
+
export declare function buildSubagentDenyMessage(toolName: string): string;
|