@webpresso/agent-kit 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @webpresso/agent-kit might be problematic. Click here for more details.
- package/bin/_run.js +4 -1
- package/dist/esm/audit/blueprint-trust.js +14 -10
- package/dist/esm/audit/changeset-evidence.d.ts +23 -0
- package/dist/esm/audit/changeset-evidence.js +30 -0
- package/dist/esm/audit/changeset-required.d.ts +10 -0
- package/dist/esm/audit/changeset-required.js +73 -0
- package/dist/esm/audit/ci-guardrails-detection.d.ts +27 -0
- package/dist/esm/audit/ci-guardrails-detection.js +68 -0
- package/dist/esm/audit/ci-guardrails-wiring.js +3 -13
- package/dist/esm/audit/registry.d.ts +4 -4
- package/dist/esm/audit/registry.js +6 -1
- package/dist/esm/blueprint/core/parser.js +61 -4
- package/dist/esm/blueprint/core/schema.d.ts +1 -0
- package/dist/esm/blueprint/core/schema.js +26 -0
- package/dist/esm/blueprint/lifecycle/audit.js +22 -10
- package/dist/esm/blueprint/lifecycle/review-provenance.d.ts +6 -0
- package/dist/esm/blueprint/lifecycle/review-provenance.js +39 -8
- package/dist/esm/blueprint/lifecycle/rollout-anchor.d.ts +97 -0
- package/dist/esm/blueprint/lifecycle/rollout-anchor.js +161 -0
- package/dist/esm/blueprint/sync/client.d.ts +2 -1
- package/dist/esm/blueprint/sync/client.js +1 -1
- package/dist/esm/blueprint/trust/dossier.d.ts +1 -0
- package/dist/esm/blueprint/trust/dossier.js +40 -6
- package/dist/esm/blueprint/trust/promotion.js +12 -0
- package/dist/esm/build/cli-mcp-parity.js +5 -0
- package/dist/esm/cli/cli.d.ts +1 -1
- package/dist/esm/cli/cli.js +9 -0
- package/dist/esm/cli/commands/agent-launch.d.ts +2 -0
- package/dist/esm/cli/commands/agent-launch.js +31 -20
- package/dist/esm/cli/commands/audit.js +4 -0
- package/dist/esm/cli/commands/ci-preflight.js +24 -3
- package/dist/esm/cli/commands/init/convergence-apply.d.ts +64 -0
- package/dist/esm/cli/commands/init/convergence-apply.js +127 -23
- package/dist/esm/cli/commands/init/index.d.ts +2 -0
- package/dist/esm/cli/commands/init/index.js +2 -2
- package/dist/esm/cli/commands/init/package-root.d.ts +7 -0
- package/dist/esm/cli/commands/init/package-root.js +10 -2
- package/dist/esm/cli/commands/init/scaffold-base-kit.d.ts +12 -3
- package/dist/esm/cli/commands/init/scaffold-base-kit.js +31 -30
- package/dist/esm/cli/commands/init/scaffolders/agent-hooks/index.js +3 -1
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.d.ts +17 -2
- package/dist/esm/cli/commands/init/scaffolders/file-transaction.js +189 -17
- package/dist/esm/cli/commands/opencode-probe.d.ts +107 -0
- package/dist/esm/cli/commands/opencode-probe.js +323 -0
- package/dist/esm/cli/commands/opencode-rotate.d.ts +23 -0
- package/dist/esm/cli/commands/opencode-rotate.js +23 -11
- package/dist/esm/cli/commands/quality-runner.js +3 -3
- package/dist/esm/cli/commands/review.js +27 -5
- package/dist/esm/cli/commands/rust-check.d.ts +24 -0
- package/dist/esm/cli/commands/rust-check.js +110 -0
- package/dist/esm/cli/commands/typecheck.d.ts +1 -0
- package/dist/esm/cli/commands/typecheck.js +30 -1
- package/dist/esm/cli/commands/worktree/router-dispatch.d.ts +6 -0
- package/dist/esm/cli/commands/worktree/router-dispatch.js +113 -1
- package/dist/esm/cli/commands/worktree/router.js +10 -0
- package/dist/esm/hooks/doctor.d.ts +2 -0
- package/dist/esm/hooks/doctor.js +37 -1
- package/dist/esm/hooks/pretool-guard/validators/worktree-discipline.js +6 -1
- package/dist/esm/hooks/shared/types.js +27 -1
- package/dist/esm/hooks/stop/qa-changed-files.js +18 -0
- package/dist/esm/mcp/blueprint/handlers/document-mutations.d.ts +5 -0
- package/dist/esm/mcp/blueprint/handlers/document-mutations.js +31 -7
- package/dist/esm/mcp/tools/_shared/audit-kinds.d.ts +1 -1
- package/dist/esm/mcp/tools/audit.d.ts +2 -1
- package/dist/esm/mcp/tools/audit.js +12 -0
- package/dist/esm/mcp/tools/audits.d.ts +1 -0
- package/dist/esm/mcp/tools/session-fetch-and-index.d.ts +1 -1
- package/dist/esm/package.json +2 -0
- package/dist/esm/platform/client.d.ts +7 -1
- package/dist/esm/platform/github-contract-source.d.ts +2 -1
- package/dist/esm/pr-description/contract.js +24 -4
- package/dist/esm/review/authority.js +14 -9
- package/dist/esm/review/availability.d.ts +11 -0
- package/dist/esm/review/availability.js +18 -3
- package/dist/esm/review/execution/adapters.d.ts +11 -1
- package/dist/esm/review/execution/adapters.js +10 -1
- package/dist/esm/review/execution/artifacts.d.ts +1 -1
- package/dist/esm/review/execution/review-checkout.js +35 -16
- package/dist/esm/review/execution/supervisor.js +31 -4
- package/dist/esm/review/execution/types.d.ts +1 -0
- package/dist/esm/review/subject.d.ts +13 -0
- package/dist/esm/review/subject.js +42 -4
- package/dist/esm/rust/affected-manifests.d.ts +38 -0
- package/dist/esm/rust/affected-manifests.js +69 -0
- package/dist/esm/session-memory/fetch-index.d.ts +1 -1
- package/dist/esm/status/snapshot.js +2 -6
- package/dist/esm/test-helpers/global-setup.d.ts +0 -1
- package/dist/esm/test-helpers/global-setup.js +18 -15
- package/dist/esm/typecheck/planner.d.ts +9 -1
- package/dist/esm/typecheck/planner.js +31 -0
- package/dist/esm/worktrees/orphan-scan.d.ts +50 -0
- package/dist/esm/worktrees/orphan-scan.js +211 -0
- package/package.json +14 -12
|
@@ -95,7 +95,12 @@ export function validateWorktreeDiscipline(input) {
|
|
|
95
95
|
return { validator: VALIDATOR_NAME, passed: true };
|
|
96
96
|
}
|
|
97
97
|
function normalizedToolName(input) {
|
|
98
|
-
|
|
98
|
+
// tool_name is typed string but malformed host envelopes can deliver non-strings;
|
|
99
|
+
// never call string methods without a typeof guard (same class as isBashInput).
|
|
100
|
+
const raw = input.tool_name;
|
|
101
|
+
if (typeof raw !== "string")
|
|
102
|
+
return "";
|
|
103
|
+
return raw.toLowerCase().replaceAll(/[^a-z]/gu, "");
|
|
99
104
|
}
|
|
100
105
|
function applyPatchTargets(input) {
|
|
101
106
|
if (!normalizedToolName(input).endsWith("applypatch"))
|
|
@@ -78,7 +78,33 @@ export function resolveHookSessionId(input, env = process.env) {
|
|
|
78
78
|
return undefined;
|
|
79
79
|
}
|
|
80
80
|
export function isBashInput(input) {
|
|
81
|
-
|
|
81
|
+
// A bare `command` field is NOT sufficient: Codex delivers `apply_patch` patch
|
|
82
|
+
// text inside `tool_input.command`, the same field a shell command uses.
|
|
83
|
+
// Discriminate on the tool name instead. Absent / non-string tool name ⇒
|
|
84
|
+
// unknown/legacy host ⇒ classify as shell so command guards still fire (a
|
|
85
|
+
// guard must fail toward inspecting, never toward silently skipping). Malformed
|
|
86
|
+
// non-string tool_name must not throw (optional chaining only guards nullish).
|
|
87
|
+
if (!("command" in (input.tool_input || {})))
|
|
88
|
+
return false;
|
|
89
|
+
const rawToolName = input.tool_name;
|
|
90
|
+
const toolName = typeof rawToolName === "string" ? rawToolName.trim().toLowerCase() : undefined;
|
|
91
|
+
if (!toolName)
|
|
92
|
+
return true;
|
|
93
|
+
return (toolName === "bash" ||
|
|
94
|
+
toolName === "shell" ||
|
|
95
|
+
toolName === "exec" ||
|
|
96
|
+
toolName === "exec_command" ||
|
|
97
|
+
// Codex names its sandboxed shell tool `local_shell` (see
|
|
98
|
+
// session-memory/provider-readers `local_shell_call`); without it a real
|
|
99
|
+
// shell command bypasses the danger/forbidden/worktree guards.
|
|
100
|
+
toolName === "local_shell" ||
|
|
101
|
+
// Grok's shell tool: `run_terminal_command` (envelope name) and the internal
|
|
102
|
+
// `run_terminal_cmd` its Bash-mapping surfaces (docs.x.ai/build/features/hooks).
|
|
103
|
+
toolName === "run_terminal_command" ||
|
|
104
|
+
toolName === "run_terminal_cmd" ||
|
|
105
|
+
toolName.endsWith(".exec_command") ||
|
|
106
|
+
toolName.endsWith("_exec_command") ||
|
|
107
|
+
toolName.endsWith("__exec_command"));
|
|
82
108
|
}
|
|
83
109
|
export function isFileEditInput(input) {
|
|
84
110
|
const toolInput = input.tool_input || {};
|
|
@@ -98,6 +98,24 @@ const CROSS_CUTTING_TEST_DEPENDENTS = [
|
|
|
98
98
|
when: /^(README\.md|docs\/guides\/session-memory\.md)$/u,
|
|
99
99
|
tests: ["src/mcp/tools/session-docs.test.ts", "src/build/package-manifest.test.ts"],
|
|
100
100
|
},
|
|
101
|
+
{
|
|
102
|
+
// blueprint-server.ts owns the MCP architecture contract (tool naming,
|
|
103
|
+
// handler wiring, transport shape). Its architecture test is not colocated
|
|
104
|
+
// under the same basename, so --affected would otherwise skip it.
|
|
105
|
+
when: /^src\/mcp\/blueprint-server\./u,
|
|
106
|
+
tests: ["src/mcp/blueprint-server.test-architecture.test.ts"],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
// Package surface / publishability contracts live in the audit package,
|
|
110
|
+
// not next to package.json or the manifest builder.
|
|
111
|
+
when: /^(package\.json|src\/build\/package-manifest\.ts)$/u,
|
|
112
|
+
tests: ["src/audit/package-surface.test.ts"],
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
// Marketplace and plugin.json validation is a build-side contract test.
|
|
116
|
+
when: /^\.claude-plugin\/(marketplace|plugin)\.json$/u,
|
|
117
|
+
tests: ["src/build/validate-marketplace.test.ts"],
|
|
118
|
+
},
|
|
101
119
|
];
|
|
102
120
|
/**
|
|
103
121
|
* Every test path the cross-cutting map can yield.
|
|
@@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import matter from "gray-matter";
|
|
5
5
|
import { parseBlueprint } from "#core/parser";
|
|
6
|
+
import { taskIdSchema } from "#core/schema";
|
|
6
7
|
import { evaluateApprovalGate, formatApprovalGateRequirement } from "#lifecycle/audit";
|
|
7
8
|
import { canonicalizeBlueprint } from "#lifecycle/canonicalize.js";
|
|
8
9
|
import { BlueprintCreationService } from "#service/BlueprintCreationService";
|
|
@@ -40,7 +41,7 @@ const createSchema = MutationTarget.extend({
|
|
|
40
41
|
head_at_ingest: z.string().nullable().optional(),
|
|
41
42
|
});
|
|
42
43
|
const putTaskSchema = z.object({
|
|
43
|
-
id:
|
|
44
|
+
id: taskIdSchema,
|
|
44
45
|
title: z.string().min(1),
|
|
45
46
|
status: z.enum(["todo", "in-progress", "blocked", "done", "dropped"]).default("todo"),
|
|
46
47
|
wave: z.string().optional(),
|
|
@@ -72,6 +73,12 @@ const putPromotionGateSchema = z.object({
|
|
|
72
73
|
command: z.string().min(1),
|
|
73
74
|
expected_outcome: z.string().min(1),
|
|
74
75
|
last_result: z.string().min(1),
|
|
76
|
+
// Explicit per-gate pre-implementation deferral. When set, `wp blueprint
|
|
77
|
+
// promote` skips executing/existence-checking this gate's command entirely
|
|
78
|
+
// and records a `deferred: ...` Last result instead. See
|
|
79
|
+
// src/blueprint/trust/promotion.ts and catalog/agent/rules
|
|
80
|
+
// /pre-implementation.md's Promotion gate section.
|
|
81
|
+
defer: z.literal("pre-implementation").optional(),
|
|
75
82
|
});
|
|
76
83
|
const putTrustDossierSchema = z.object({
|
|
77
84
|
readiness: putTrustReadinessSchema,
|
|
@@ -180,6 +187,11 @@ const putTrustDossierJsonSchema = {
|
|
|
180
187
|
command: { type: "string", description: "wp or ./bin/wp facade command" },
|
|
181
188
|
expected_outcome: { type: "string" },
|
|
182
189
|
last_result: { type: "string" },
|
|
190
|
+
defer: {
|
|
191
|
+
type: "string",
|
|
192
|
+
const: "pre-implementation",
|
|
193
|
+
description: "Explicit opt-in: skip executing/existence-checking this gate at promote time",
|
|
194
|
+
},
|
|
183
195
|
},
|
|
184
196
|
required: ["gate", "command", "expected_outcome", "last_result"],
|
|
185
197
|
additionalProperties: false,
|
|
@@ -323,12 +335,7 @@ function renderTrustDossierMarkdown(dossier) {
|
|
|
323
335
|
"",
|
|
324
336
|
"### Promotion Gates",
|
|
325
337
|
"",
|
|
326
|
-
...
|
|
327
|
-
gate.gate,
|
|
328
|
-
gate.command,
|
|
329
|
-
gate.expected_outcome,
|
|
330
|
-
gate.last_result,
|
|
331
|
-
])),
|
|
338
|
+
...renderPromotionGatesTable(dossier.promotion_gates),
|
|
332
339
|
"",
|
|
333
340
|
"### Residual Unknowns",
|
|
334
341
|
"",
|
|
@@ -336,6 +343,23 @@ function renderTrustDossierMarkdown(dossier) {
|
|
|
336
343
|
"",
|
|
337
344
|
];
|
|
338
345
|
}
|
|
346
|
+
// Only emits the (opt-in) Defer column when at least one gate uses it, so
|
|
347
|
+
// the common no-defer case keeps rendering the legacy 4-column table exactly
|
|
348
|
+
// as before -- matching src/blueprint/trust/dossier.ts's parser, which
|
|
349
|
+
// accepts either width.
|
|
350
|
+
function renderPromotionGatesTable(gates) {
|
|
351
|
+
const hasDefer = gates.some((gate) => gate.defer !== undefined);
|
|
352
|
+
if (!hasDefer) {
|
|
353
|
+
return renderMarkdownTable(["Gate", "Command", "Expected outcome", "Last result"], gates.map((gate) => [gate.gate, gate.command, gate.expected_outcome, gate.last_result]));
|
|
354
|
+
}
|
|
355
|
+
return renderMarkdownTable(["Gate", "Command", "Expected outcome", "Last result", "Defer"], gates.map((gate) => [
|
|
356
|
+
gate.gate,
|
|
357
|
+
gate.command,
|
|
358
|
+
gate.expected_outcome,
|
|
359
|
+
gate.last_result,
|
|
360
|
+
gate.defer ?? "",
|
|
361
|
+
]));
|
|
362
|
+
}
|
|
339
363
|
function normalizeResidualUnknowns(value) {
|
|
340
364
|
if (value === undefined)
|
|
341
365
|
return "None.";
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const MCP_AUDIT_KINDS: readonly ["test-smells", "tph-e2e", "agents", "catalog-drift", "package-surface", "reference-parity-matrix", "docs-frontmatter", "blueprint-readme-drift", "blueprint-pr-coverage", "blueprint-lifecycle", "blueprint-trust", "architecture-drift", "cloudflare-deploy-contract", "absolute-path-policy", "no-first-party-mjs", "no-math-random", "roadmap-links", "bundle-budget", "commit-message", "tech-debt", "supported-agent-clis", "hook-surface", "harness-surfaces", "weakness-mining", "harness-overlay-evidence", "host-substitution-risk", "ai-contracts", "atomic-state-writes", "no-relative-package-scripts", "toolchain-isolation", "open-source-licenses", "secrets-policy", "no-dev-vars", "github-actions-secrets", "secret-provider-quarantine", "security-quality-regressions", "secrets-config", "consumer-agent-kit-dependency", "ci-test-perf", "test-scan-perf", "session-memory-hardcut", "worktree-main-ownership", "typescript-version", "ci-guardrails-wiring", "sast"];
|
|
1
|
+
export declare const MCP_AUDIT_KINDS: readonly ["test-smells", "tph-e2e", "agents", "catalog-drift", "package-surface", "reference-parity-matrix", "docs-frontmatter", "blueprint-readme-drift", "blueprint-pr-coverage", "blueprint-lifecycle", "blueprint-trust", "architecture-drift", "cloudflare-deploy-contract", "absolute-path-policy", "no-first-party-mjs", "no-math-random", "roadmap-links", "bundle-budget", "commit-message", "tech-debt", "supported-agent-clis", "hook-surface", "harness-surfaces", "weakness-mining", "harness-overlay-evidence", "host-substitution-risk", "ai-contracts", "atomic-state-writes", "no-relative-package-scripts", "toolchain-isolation", "open-source-licenses", "secrets-policy", "no-dev-vars", "github-actions-secrets", "secret-provider-quarantine", "security-quality-regressions", "secrets-config", "consumer-agent-kit-dependency", "ci-test-perf", "test-scan-perf", "session-memory-hardcut", "worktree-main-ownership", "typescript-version", "ci-guardrails-wiring", "sast", "changeset-required"];
|
|
2
2
|
export type MCPAuditKind = (typeof MCP_AUDIT_KINDS)[number];
|
|
3
3
|
export declare function isMCPAuditKind(value: string): value is MCPAuditKind;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { z } from "zod";
|
|
17
17
|
import type { ToolDescriptor } from "#mcp/auto-discover";
|
|
18
|
-
export declare const KINDS: readonly ["test-smells", "tph-e2e", "agents", "catalog-drift", "package-surface", "reference-parity-matrix", "docs-frontmatter", "blueprint-readme-drift", "blueprint-pr-coverage", "blueprint-lifecycle", "blueprint-trust", "architecture-drift", "cloudflare-deploy-contract", "absolute-path-policy", "no-first-party-mjs", "no-math-random", "roadmap-links", "bundle-budget", "commit-message", "tech-debt", "supported-agent-clis", "hook-surface", "harness-surfaces", "weakness-mining", "harness-overlay-evidence", "host-substitution-risk", "ai-contracts", "atomic-state-writes", "no-relative-package-scripts", "toolchain-isolation", "open-source-licenses", "secrets-policy", "no-dev-vars", "github-actions-secrets", "secret-provider-quarantine", "security-quality-regressions", "secrets-config", "consumer-agent-kit-dependency", "ci-test-perf", "test-scan-perf", "session-memory-hardcut", "worktree-main-ownership", "typescript-version", "ci-guardrails-wiring", "sast"];
|
|
18
|
+
export declare const KINDS: readonly ["test-smells", "tph-e2e", "agents", "catalog-drift", "package-surface", "reference-parity-matrix", "docs-frontmatter", "blueprint-readme-drift", "blueprint-pr-coverage", "blueprint-lifecycle", "blueprint-trust", "architecture-drift", "cloudflare-deploy-contract", "absolute-path-policy", "no-first-party-mjs", "no-math-random", "roadmap-links", "bundle-budget", "commit-message", "tech-debt", "supported-agent-clis", "hook-surface", "harness-surfaces", "weakness-mining", "harness-overlay-evidence", "host-substitution-risk", "ai-contracts", "atomic-state-writes", "no-relative-package-scripts", "toolchain-isolation", "open-source-licenses", "secrets-policy", "no-dev-vars", "github-actions-secrets", "secret-provider-quarantine", "security-quality-regressions", "secrets-config", "consumer-agent-kit-dependency", "ci-test-perf", "test-scan-perf", "session-memory-hardcut", "worktree-main-ownership", "typescript-version", "ci-guardrails-wiring", "sast", "changeset-required"];
|
|
19
19
|
export declare const inputSchema: z.ZodObject<{
|
|
20
20
|
kind: z.ZodEnum<{
|
|
21
21
|
"absolute-path-policy": "absolute-path-policy";
|
|
@@ -29,6 +29,7 @@ export declare const inputSchema: z.ZodObject<{
|
|
|
29
29
|
"blueprint-trust": "blueprint-trust";
|
|
30
30
|
"bundle-budget": "bundle-budget";
|
|
31
31
|
"catalog-drift": "catalog-drift";
|
|
32
|
+
"changeset-required": "changeset-required";
|
|
32
33
|
"ci-guardrails-wiring": "ci-guardrails-wiring";
|
|
33
34
|
"ci-test-perf": "ci-test-perf";
|
|
34
35
|
"cloudflare-deploy-contract": "cloudflare-deploy-contract";
|
|
@@ -569,6 +569,18 @@ export async function dispatchAudit(input) {
|
|
|
569
569
|
details: auditResult,
|
|
570
570
|
};
|
|
571
571
|
}
|
|
572
|
+
case "changeset-required": {
|
|
573
|
+
const { auditChangesetRequired } = await import("#audit/changeset-required");
|
|
574
|
+
const auditResult = auditChangesetRequired(input.cwd ?? input.directory ?? process.cwd(), {
|
|
575
|
+
baseRef: input.baseRef,
|
|
576
|
+
});
|
|
577
|
+
return {
|
|
578
|
+
passed: auditResult.ok,
|
|
579
|
+
summary: summarizeRepoAudit(kind, auditResult),
|
|
580
|
+
kind,
|
|
581
|
+
details: auditResult,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
572
584
|
default: {
|
|
573
585
|
// Exhaustiveness check — z.enum should make this unreachable.
|
|
574
586
|
const _exhaustive = kind;
|
|
@@ -25,6 +25,7 @@ declare const inputSchema: z.ZodObject<{
|
|
|
25
25
|
"blueprint-trust": "blueprint-trust";
|
|
26
26
|
"bundle-budget": "bundle-budget";
|
|
27
27
|
"catalog-drift": "catalog-drift";
|
|
28
|
+
"changeset-required": "changeset-required";
|
|
28
29
|
"ci-guardrails-wiring": "ci-guardrails-wiring";
|
|
29
30
|
"ci-test-perf": "ci-test-perf";
|
|
30
31
|
"cloudflare-deploy-contract": "cloudflare-deploy-contract";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ToolDescriptor, ToolHandlerExtra, ToolHandlerResult } from "#mcp/auto-discover";
|
|
2
2
|
export interface SessionFetchAndIndexDeps {
|
|
3
|
-
readonly fetchImpl?:
|
|
3
|
+
readonly fetchImpl?: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
4
4
|
}
|
|
5
5
|
export declare function handleSessionFetchAndIndex(raw: unknown, extra?: ToolHandlerExtra, deps?: SessionFetchAndIndexDeps): Promise<ToolHandlerResult>;
|
|
6
6
|
declare const tool: ToolDescriptor;
|
package/dist/esm/package.json
CHANGED
|
@@ -89,6 +89,8 @@
|
|
|
89
89
|
"#compiler/*": "./compiler/*.js",
|
|
90
90
|
"#review/*.js": "./review/*.js",
|
|
91
91
|
"#review/*": "./review/*.js",
|
|
92
|
+
"#rust/*.js": "./rust/*.js",
|
|
93
|
+
"#rust/*": "./rust/*.js",
|
|
92
94
|
"#telemetry/*.js": "./telemetry/*.js",
|
|
93
95
|
"#telemetry/*": "./telemetry/*.js",
|
|
94
96
|
"#codex/app-server/client": "./codex/app-server/client.js",
|
|
@@ -44,8 +44,14 @@ export interface PlatformResponse<T = unknown> {
|
|
|
44
44
|
readonly status: number;
|
|
45
45
|
readonly data: T;
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Injectable fetch call-signature used by platform transport and tests.
|
|
49
|
+
* Narrower than `typeof fetch` (which requires Bun/DOM statics like
|
|
50
|
+
* `preconnect`) so vitest mocks remain assignable without casts.
|
|
51
|
+
*/
|
|
52
|
+
export type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
47
53
|
export interface PlatformClientOptions {
|
|
48
|
-
readonly fetchFn?:
|
|
54
|
+
readonly fetchFn?: FetchLike;
|
|
49
55
|
readonly maxAttempts?: number;
|
|
50
56
|
readonly baseRetryDelayMs?: number;
|
|
51
57
|
readonly maxRetryDelayMs?: number;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { type AuthenticatedPlatformContractSnapshot, type PlatformContractAuthority, type PlatformContractAuthorityProbeResult, type PlatformContractSourceAdapter } from "./contract-provenance.js";
|
|
2
|
+
import { type FetchLike } from "./client.js";
|
|
2
3
|
export interface GitHubContractSourceAdapterOptions {
|
|
3
4
|
readonly token: string;
|
|
4
5
|
readonly authority?: string;
|
|
5
6
|
readonly apiUrl?: string;
|
|
6
|
-
readonly fetchFn?:
|
|
7
|
+
readonly fetchFn?: FetchLike;
|
|
7
8
|
readonly sleep?: (delayMs: number) => Promise<void>;
|
|
8
9
|
}
|
|
9
10
|
export declare class GitHubContractSourceAdapter implements PlatformContractSourceAdapter {
|
|
@@ -31,13 +31,33 @@ const SESSION_SKIP_PREFIX = /^Session-skip:/im;
|
|
|
31
31
|
function trim(value) {
|
|
32
32
|
return value?.trim() ?? "";
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Finds the value of a "<label>: <value>" list item at the head of a line,
|
|
36
|
+
* tolerating a leading "- " bullet and markdown emphasis wrapping the label
|
|
37
|
+
* (`**bold**`, `__bold__`, `*italic*`, `_italic_`) on either side of the
|
|
38
|
+
* colon — e.g. both "- Execution model(s): x" and
|
|
39
|
+
* "- **Execution model(s):** x" match. Still anchored to the start of the
|
|
40
|
+
* line (after stripping the bullet/emphasis decoration): a mention of the
|
|
41
|
+
* label mid-sentence never matches. Mirrors the CI `require_field` awk
|
|
42
|
+
* matcher in `.github/workflows/ci.agent-kit.yml` — keep the two in lockstep.
|
|
43
|
+
*/
|
|
34
44
|
function fieldValue(body, label) {
|
|
35
45
|
const expected = label.toLowerCase();
|
|
36
46
|
for (const line of body.split(/\r?\n/u)) {
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
const colonIndex = line.indexOf(":");
|
|
48
|
+
if (colonIndex === -1)
|
|
49
|
+
continue;
|
|
50
|
+
const before = line
|
|
51
|
+
.slice(0, colonIndex)
|
|
52
|
+
.replace(/^\s+/u, "")
|
|
53
|
+
.replace(/^-\s+/u, "")
|
|
54
|
+
.replace(/[*_]+/gu, "");
|
|
55
|
+
if (before.toLowerCase() !== expected)
|
|
56
|
+
continue;
|
|
57
|
+
return line
|
|
58
|
+
.slice(colonIndex + 1)
|
|
59
|
+
.replace(/^[*_]+/u, "")
|
|
60
|
+
.trim();
|
|
41
61
|
}
|
|
42
62
|
return undefined;
|
|
43
63
|
}
|
|
@@ -9,7 +9,7 @@ import { createHash } from "node:crypto";
|
|
|
9
9
|
import { existsSync, lstatSync, readFileSync } from "node:fs";
|
|
10
10
|
import path from "node:path";
|
|
11
11
|
import { evaluateReviewEvents, parseReviewEventLog } from "./events.js";
|
|
12
|
-
import { createDeliverySubjectAtRef, createLegacyPlanSubjectAtRef, createPlanSubjectAtRef, resolveReviewCommit, } from "./subject.js";
|
|
12
|
+
import { createDeliverySubjectAtRef, createLegacyPlanSubjectAtRef, createPlanSubjectAtRef, createPreTagsStripPlanSubjectAtRef, resolveReviewCommit, } from "./subject.js";
|
|
13
13
|
const GIT_TIMEOUT_MS = 5_000;
|
|
14
14
|
const GIT_MAX_BUFFER_BYTES = 128 * 1024 * 1024;
|
|
15
15
|
const REGULAR_MODE_PATTERN = /^100(?:644|755)$/u;
|
|
@@ -138,18 +138,23 @@ export function readReviewAuthorityAtRef(cwd, ref, stableSlug, purpose) {
|
|
|
138
138
|
subjectDigest: subject.digest,
|
|
139
139
|
}, compromisedIds);
|
|
140
140
|
if (purpose === "plan" && evaluation.approvals.length === 0) {
|
|
141
|
-
// Digest-compatibility
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
141
|
+
// Digest-compatibility fallbacks (newest-first after current):
|
|
142
|
+
// 1) pre-tags-strip: lifecycle strip without advisory `tags` (approvals recorded
|
|
143
|
+
// after the stripped-key extension but before the tags strip)
|
|
144
|
+
// 2) legacy: status + completed_at only (pre-extension)
|
|
145
|
+
// New events always record the current digest (with tags strip).
|
|
146
|
+
for (const make of [createPreTagsStripPlanSubjectAtRef, createLegacyPlanSubjectAtRef]) {
|
|
147
|
+
const alt = make(cwd, commit, stableSlug, overview.path);
|
|
148
|
+
if (alt.digest === subject.digest)
|
|
149
|
+
continue;
|
|
147
150
|
evaluation = evaluateReviewEvents(events, {
|
|
148
151
|
purpose,
|
|
149
152
|
blueprintSlug: stableSlug,
|
|
150
|
-
subjectScheme:
|
|
151
|
-
subjectDigest:
|
|
153
|
+
subjectScheme: alt.scheme,
|
|
154
|
+
subjectDigest: alt.digest,
|
|
152
155
|
}, compromisedIds);
|
|
156
|
+
if (evaluation.approvals.length > 0)
|
|
157
|
+
break;
|
|
153
158
|
}
|
|
154
159
|
}
|
|
155
160
|
return {
|
|
@@ -45,6 +45,17 @@ export declare function recordReviewAvailability(projectRoot: string, event: Rev
|
|
|
45
45
|
export declare function recordReviewAvailabilityWithLock(projectRoot: string, event: ReviewAvailabilityEvent): Promise<ReviewAvailabilityState>;
|
|
46
46
|
export declare function isProviderSuppressed(state: ReviewAvailabilityState, provider: ReviewProvider): boolean;
|
|
47
47
|
export declare function suppressedReviewers(state: ReviewAvailabilityState): Set<string>;
|
|
48
|
+
/**
|
|
49
|
+
* Backstop age for suppressions recorded without a parseable retry window.
|
|
50
|
+
*
|
|
51
|
+
* Without this, a provider/account failure that never set `retryAfterMs`
|
|
52
|
+
* (unparsable limit text, incomplete event payload) stays suppressed forever
|
|
53
|
+
* and permanently removes that surface from rotation. 24h is long enough that
|
|
54
|
+
* a real multi-hour/day window still wins when present, and short enough that
|
|
55
|
+
* a sticky unknown-limit entry self-heals on the next calendar day instead of
|
|
56
|
+
* requiring a manual availability reset.
|
|
57
|
+
*/
|
|
58
|
+
export declare const MAX_SUPPRESSION_AGE_MS: number;
|
|
48
59
|
export declare function pruneExpiredAvailabilitySuppressions(state: ReviewAvailabilityState, nowMs?: number): {
|
|
49
60
|
state: ReviewAvailabilityState;
|
|
50
61
|
pruned: string[];
|
|
@@ -247,21 +247,36 @@ export function suppressedReviewers(state) {
|
|
|
247
247
|
.filter((record) => record.suppressed)
|
|
248
248
|
.map((record) => record.canonicalId));
|
|
249
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* Backstop age for suppressions recorded without a parseable retry window.
|
|
252
|
+
*
|
|
253
|
+
* Without this, a provider/account failure that never set `retryAfterMs`
|
|
254
|
+
* (unparsable limit text, incomplete event payload) stays suppressed forever
|
|
255
|
+
* and permanently removes that surface from rotation. 24h is long enough that
|
|
256
|
+
* a real multi-hour/day window still wins when present, and short enough that
|
|
257
|
+
* a sticky unknown-limit entry self-heals on the next calendar day instead of
|
|
258
|
+
* requiring a manual availability reset.
|
|
259
|
+
*/
|
|
260
|
+
export const MAX_SUPPRESSION_AGE_MS = 24 * 60 * 60 * 1000;
|
|
250
261
|
/**
|
|
251
262
|
* Shared expiry predicate for a suppressed availability record: applies to
|
|
252
263
|
* both the reviewer/provider store and the opencode-go account store below.
|
|
253
264
|
* A record clears when its terminal classification is no longer a failure
|
|
254
|
-
* classification, or when its retry window has
|
|
265
|
+
* classification, or when its retry window (or the max-age backstop) has
|
|
266
|
+
* elapsed.
|
|
255
267
|
*/
|
|
256
268
|
function suppressionExpired(record, nowMs) {
|
|
257
269
|
if (!record.suppressed)
|
|
258
270
|
return false;
|
|
259
271
|
if (!suppressedRecordStillApplies(record))
|
|
260
272
|
return true;
|
|
261
|
-
if (record.
|
|
273
|
+
if (record.lastFailureAt === undefined)
|
|
262
274
|
return false;
|
|
263
275
|
const failureAt = Date.parse(record.lastFailureAt);
|
|
264
|
-
|
|
276
|
+
if (!Number.isFinite(failureAt))
|
|
277
|
+
return false;
|
|
278
|
+
const windowMs = record.retryAfterMs === undefined ? MAX_SUPPRESSION_AGE_MS : record.retryAfterMs;
|
|
279
|
+
return failureAt + windowMs <= nowMs;
|
|
265
280
|
}
|
|
266
281
|
function pruneRecordMap(records, nowMs, pruned) {
|
|
267
282
|
return Object.fromEntries(Object.entries(records).map(([key, record]) => {
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import type { ReviewRunInput, ReviewStreamAdapter } from "./types.js";
|
|
1
|
+
import type { ReviewAdapterEvent, ReviewRunInput, ReviewStreamAdapter } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Classify an OpenCode Go usage-limit message and derive its real reset window.
|
|
4
|
+
*
|
|
5
|
+
* Exported because the `wp opencode` launch-time health probe
|
|
6
|
+
* (`#cli/commands/opencode-probe.js`) runs the same provider text through the
|
|
7
|
+
* same classifier. A second copy of this wording/duration math would drift the
|
|
8
|
+
* moment the provider changes a phrase, and the two consumers would then
|
|
9
|
+
* disagree about whether an account is capped.
|
|
10
|
+
*/
|
|
11
|
+
export declare function opencodeUsageLimit(message: string): ReviewAdapterEvent | null;
|
|
2
12
|
export declare const claudeReviewAdapter: ReviewStreamAdapter;
|
|
3
13
|
export declare const codexReviewAdapter: ReviewStreamAdapter;
|
|
4
14
|
export declare const opencodeReviewAdapter: ReviewStreamAdapter;
|
|
@@ -92,7 +92,16 @@ function parseResetDurationMs(message) {
|
|
|
92
92
|
}
|
|
93
93
|
return duration > 0 ? duration : undefined;
|
|
94
94
|
}
|
|
95
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Classify an OpenCode Go usage-limit message and derive its real reset window.
|
|
97
|
+
*
|
|
98
|
+
* Exported because the `wp opencode` launch-time health probe
|
|
99
|
+
* (`#cli/commands/opencode-probe.js`) runs the same provider text through the
|
|
100
|
+
* same classifier. A second copy of this wording/duration math would drift the
|
|
101
|
+
* moment the provider changes a phrase, and the two consumers would then
|
|
102
|
+
* disagree about whether an account is capped.
|
|
103
|
+
*/
|
|
104
|
+
export function opencodeUsageLimit(message) {
|
|
96
105
|
if (!/\busage limit reached\b/iu.test(message))
|
|
97
106
|
return null;
|
|
98
107
|
const limitWindow = /\bweekly usage limit reached\b/iu.test(message)
|
|
@@ -14,4 +14,4 @@ export interface ReviewArtifactWriter {
|
|
|
14
14
|
export declare function reviewRunStatePath(root: string, runId: string): string;
|
|
15
15
|
export type ReviewRunStateCleanupResult = "missing" | "not-running" | "removed" | "refused";
|
|
16
16
|
export declare function removeOwnedRunningReviewState(root: string, runId: string, reservationToken: string): ReviewRunStateCleanupResult;
|
|
17
|
-
export declare function createReviewArtifactWriter(root: string, runId: string, startedAt: string, reservationToken?:
|
|
17
|
+
export declare function createReviewArtifactWriter(root: string, runId: string, startedAt: string, reservationToken?: string): ReviewArtifactWriter;
|
|
@@ -23,24 +23,34 @@ function frame(hash, label, value) {
|
|
|
23
23
|
hash.update(`${label}\0${bytes.byteLength}\0`, "utf8");
|
|
24
24
|
hash.update(bytes);
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
// Fingerprint over the filtered line set `status()` produces below, minus
|
|
27
|
+
// pure gitignored (`!!`) entries, so the two contamination checks
|
|
28
|
+
// (`changes()`'s status() diff and the caller worktree fingerprint) cannot
|
|
29
|
+
// drift apart on what counts as "source" content while still tolerating
|
|
30
|
+
// expected runtime bookkeeping.
|
|
31
|
+
//
|
|
32
|
+
// Before the status()-alignment fix, the fingerprint hashed the raw
|
|
33
|
+
// unfiltered `git status` output (without `--ignored`), so anything status()
|
|
34
|
+
// already tolerates (the self-hosting bootstrap's idempotent `package.json`
|
|
35
|
+
// trailing-whitespace normalization, and non-source ignored paths under
|
|
36
|
+
// dist/.tshy/node_modules) could still flip the fingerprint and false-flag a
|
|
37
|
+
// clean caller worktree as contaminated.
|
|
38
|
+
//
|
|
39
|
+
// Reusing status() alone re-introduces a different false positive: the
|
|
40
|
+
// supervisor writes process records under gitignored `.webpresso/` (and similar
|
|
41
|
+
// runtime roots) during the run. Those appear as `!!` lines only when
|
|
42
|
+
// `--ignored` is on. Dropping `!!` here restores the prior fingerprint
|
|
43
|
+
// contract for ignored paths while keeping the package.json / non-`!!` filters
|
|
44
|
+
// shared with status(). Checkout-side status() still reports non-derived
|
|
45
|
+
// gitignored writes (e.g. a stray `.env`) on the disposable review tree.
|
|
46
|
+
function worktreeFingerprint(cwd, ignoredPaths = new Set()) {
|
|
27
47
|
const hash = createHash("sha256");
|
|
28
|
-
const
|
|
29
|
-
frame(hash, "
|
|
48
|
+
const lines = status(cwd, ignoredPaths).filter((line) => !line.startsWith("!!"));
|
|
49
|
+
frame(hash, "status-lines", lines.join("\n"));
|
|
30
50
|
frame(hash, "staged", gitBytes(cwd, ["diff", "--cached", "--binary", "--no-ext-diff", "HEAD", "--"]));
|
|
31
|
-
const records = porcelain.split("\0").filter(Boolean);
|
|
32
51
|
const dirtyPaths = new Set();
|
|
33
|
-
for (
|
|
34
|
-
|
|
35
|
-
dirtyPaths.add(record.slice(3));
|
|
36
|
-
if (record.slice(0, 2).match(/[RC]/u)) {
|
|
37
|
-
const pairedPath = records[index + 1];
|
|
38
|
-
if (pairedPath) {
|
|
39
|
-
dirtyPaths.add(pairedPath);
|
|
40
|
-
index += 1;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
52
|
+
for (const line of lines)
|
|
53
|
+
dirtyPaths.add(statusEntryPath(line));
|
|
44
54
|
for (const relativePath of [...dirtyPaths].filter(Boolean).toSorted()) {
|
|
45
55
|
const absolutePath = join(cwd, relativePath);
|
|
46
56
|
frame(hash, "dirty-path", relativePath);
|
|
@@ -108,13 +118,22 @@ function isNormalizedPackageJsonModification(cwd, line) {
|
|
|
108
118
|
return false;
|
|
109
119
|
}
|
|
110
120
|
}
|
|
121
|
+
// Single source of truth for turning a `git status --porcelain=v1` line into
|
|
122
|
+
// its relative path, shared by status()'s filter predicate and
|
|
123
|
+
// worktreeFingerprint()'s dirty-path hashing so both stay aligned by
|
|
124
|
+
// construction. Does not special-case the rename arrow (`old -> new`); a
|
|
125
|
+
// rename line's raw tail is used as-is, matching this function's prior
|
|
126
|
+
// (pre-refactor) inline behavior.
|
|
127
|
+
function statusEntryPath(line) {
|
|
128
|
+
return line.slice(3).replace(/\/$/u, "");
|
|
129
|
+
}
|
|
111
130
|
function status(cwd, ignoredPaths = new Set()) {
|
|
112
131
|
const output = git(cwd, ["status", "--porcelain=v1", "--untracked-files=all", "--ignored"]);
|
|
113
132
|
return output
|
|
114
133
|
.split(/\r?\n/u)
|
|
115
134
|
.filter((line) => line.length > 0)
|
|
116
135
|
.filter((line) => {
|
|
117
|
-
const relativePath = line
|
|
136
|
+
const relativePath = statusEntryPath(line);
|
|
118
137
|
if (ignoredPaths.has(relativePath))
|
|
119
138
|
return false;
|
|
120
139
|
if (line.startsWith("!!") && isNonSourceIgnoredPath(relativePath))
|
|
@@ -123,6 +123,10 @@ export async function superviseReviewExecution(input, adapter, deps = {}) {
|
|
|
123
123
|
let fallbackSelectionTimer;
|
|
124
124
|
let pendingFallback;
|
|
125
125
|
const seenFrontiers = new Set();
|
|
126
|
+
const droppedEventWarnings = [];
|
|
127
|
+
const recordDroppedProviderEvent = (reason) => {
|
|
128
|
+
droppedEventWarnings.push(reason);
|
|
129
|
+
};
|
|
126
130
|
const decoder = new StringDecoder("utf8");
|
|
127
131
|
const diagnosticDecoder = new StringDecoder("utf8");
|
|
128
132
|
let buffered = "";
|
|
@@ -230,19 +234,41 @@ export async function superviseReviewExecution(input, adapter, deps = {}) {
|
|
|
230
234
|
const processLine = (line) => {
|
|
231
235
|
if (line.trim().length === 0 || status !== null)
|
|
232
236
|
return;
|
|
233
|
-
|
|
234
|
-
fail("protocol-unsupported", "provider-event-too-large");
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
+
const oversized = Buffer.byteLength(line, "utf8") > MAX_PROVIDER_EVENT_BYTES;
|
|
237
238
|
let value;
|
|
238
239
|
try {
|
|
239
240
|
value = JSON.parse(line);
|
|
240
241
|
}
|
|
241
242
|
catch {
|
|
243
|
+
// An oversized frame that also fails to parse carries no recoverable
|
|
244
|
+
// signal (a genuinely truncated/malformed line looks the same either
|
|
245
|
+
// way) -- drop it with a warning instead of failing a run that may
|
|
246
|
+
// still receive a complete, well-formed terminal verdict later. A
|
|
247
|
+
// normal-sized malformed line is still a protocol violation.
|
|
248
|
+
if (oversized) {
|
|
249
|
+
recordDroppedProviderEvent("provider-event-too-large-unparseable");
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
242
252
|
fail("protocol-unsupported", "invalid-json-event");
|
|
243
253
|
return;
|
|
244
254
|
}
|
|
245
255
|
const event = adapter.parseEvent(value);
|
|
256
|
+
if (oversized && (event.kind === "terminal" || event.kind === "failure")) {
|
|
257
|
+
// Terminal/verdict and failure frames are load-bearing: an oversized
|
|
258
|
+
// one is treated as a protocol violation, same as before this fix.
|
|
259
|
+
fail("protocol-unsupported", "provider-event-too-large");
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (oversized) {
|
|
263
|
+
// Non-terminal (progress/neutral/unsupported) oversized frames are
|
|
264
|
+
// dropped rather than failing the whole run -- a provider that echoes
|
|
265
|
+
// e.g. a large lockfile read in a progress frame must not discard an
|
|
266
|
+
// otherwise-complete review verdict. See catalog/agent/rules
|
|
267
|
+
// /no-timeout-as-fix.md: the byte bound stays in place, we just stop
|
|
268
|
+
// treating "one big frame" as fatal to the whole run.
|
|
269
|
+
recordDroppedProviderEvent(`provider-event-too-large-${event.kind}`);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
246
272
|
if (event.kind === "unsupported") {
|
|
247
273
|
fail("protocol-unsupported", "unknown-provider-event");
|
|
248
274
|
return;
|
|
@@ -433,6 +459,7 @@ export async function superviseReviewExecution(input, adapter, deps = {}) {
|
|
|
433
459
|
...(limitWindow === undefined ? {} : { limitWindow }),
|
|
434
460
|
...(retryAfterMs === undefined ? {} : { retryAfterMs }),
|
|
435
461
|
summaryPath: artifacts.summaryPath,
|
|
462
|
+
...(droppedEventWarnings.length > 0 ? { warnings: [...droppedEventWarnings] } : {}),
|
|
436
463
|
};
|
|
437
464
|
}
|
|
438
465
|
function recordReviewProcessStart(reviewChild, processGroupId) {
|
|
@@ -43,9 +43,22 @@ export declare function createPlanSubjectDigestForContent(blueprintSlug: string,
|
|
|
43
43
|
* `normalizePlanSubjectContent`.
|
|
44
44
|
*/
|
|
45
45
|
export declare function legacyNormalizePlanSubjectContent(content: Buffer | string): Buffer;
|
|
46
|
+
/**
|
|
47
|
+
* Digest-compatibility variant for review events recorded after the lifecycle
|
|
48
|
+
* stripped-key extension but before the advisory `tags` strip. Verification
|
|
49
|
+
* accepts a match against this tier so existing plan approvals on tagged
|
|
50
|
+
* blueprints are not stranded by the tags-strip rollout.
|
|
51
|
+
*/
|
|
52
|
+
export declare function preTagsStripNormalizePlanSubjectContent(content: Buffer | string): Buffer;
|
|
46
53
|
export declare function createPlanSubjectAtRef(cwd: string, ref: string, blueprintSlug: string, blueprintPath?: string): ReviewSubject;
|
|
47
54
|
/** Plan subject digested with the pre-extension normalization (see above). */
|
|
48
55
|
export declare function createLegacyPlanSubjectAtRef(cwd: string, ref: string, blueprintSlug: string, blueprintPath?: string): ReviewSubject;
|
|
56
|
+
/** Plan subject digested without the advisory `tags` strip (pre-tags-strip tier). */
|
|
57
|
+
export declare function createPreTagsStripPlanSubjectAtRef(cwd: string, ref: string, blueprintSlug: string, blueprintPath?: string): ReviewSubject;
|
|
58
|
+
/** Working-ledger digest for the pre-tags-strip compatibility tier. */
|
|
59
|
+
export declare function createPreTagsStripPlanSubjectDigestForContent(blueprintSlug: string, content: Buffer | string): string;
|
|
60
|
+
/** Working-ledger digest for the pre-extension legacy compatibility tier. */
|
|
61
|
+
export declare function createLegacyPlanSubjectDigestForContent(blueprintSlug: string, content: Buffer | string): string;
|
|
49
62
|
export declare const reviewSubjectInternals: {
|
|
50
63
|
isExcludedReviewEnvelope: typeof isExcludedReviewEnvelope;
|
|
51
64
|
isReviewEvidencePath: typeof isReviewEvidencePath;
|