@tokenfactory/acc-runner 0.43.0 → 0.44.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -3
- package/dist/companion-run.d.ts.map +1 -1
- package/dist/companion-run.js +20 -0
- package/dist/companion-run.js.map +1 -1
- package/dist/config.d.ts +72 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +117 -3
- package/dist/config.js.map +1 -1
- package/dist/cost-pricing.d.ts +45 -0
- package/dist/cost-pricing.d.ts.map +1 -1
- package/dist/cost-pricing.js +57 -0
- package/dist/cost-pricing.js.map +1 -1
- package/dist/doctor.d.ts +4 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +32 -3
- package/dist/doctor.js.map +1 -1
- package/dist/engines/registry.d.ts +17 -6
- package/dist/engines/registry.d.ts.map +1 -1
- package/dist/engines/registry.js +38 -13
- package/dist/engines/registry.js.map +1 -1
- package/dist/git-auth/github-token-source.d.ts +54 -0
- package/dist/git-auth/github-token-source.d.ts.map +1 -0
- package/dist/git-auth/github-token-source.js +44 -0
- package/dist/git-auth/github-token-source.js.map +1 -0
- package/dist/git-auth/index.d.ts +4 -0
- package/dist/git-auth/index.d.ts.map +1 -1
- package/dist/git-auth/index.js +4 -0
- package/dist/git-auth/index.js.map +1 -1
- package/dist/git-auth/installation-token.d.ts +95 -0
- package/dist/git-auth/installation-token.d.ts.map +1 -0
- package/dist/git-auth/installation-token.js +192 -0
- package/dist/git-auth/installation-token.js.map +1 -0
- package/dist/instance.d.ts +30 -0
- package/dist/instance.d.ts.map +1 -0
- package/dist/instance.js +51 -0
- package/dist/instance.js.map +1 -0
- package/dist/keychain.d.ts +4 -8
- package/dist/keychain.d.ts.map +1 -1
- package/dist/keychain.js +10 -4
- package/dist/keychain.js.map +1 -1
- package/dist/local-repo/command.d.ts +28 -0
- package/dist/local-repo/command.d.ts.map +1 -0
- package/dist/local-repo/command.js +178 -0
- package/dist/local-repo/command.js.map +1 -0
- package/dist/local-repo/index.d.ts +119 -0
- package/dist/local-repo/index.d.ts.map +1 -0
- package/dist/local-repo/index.js +141 -0
- package/dist/local-repo/index.js.map +1 -0
- package/dist/login.d.ts +4 -0
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +6 -1
- package/dist/login.js.map +1 -1
- package/dist/mcp-spawn.d.ts +19 -0
- package/dist/mcp-spawn.d.ts.map +1 -1
- package/dist/mcp-spawn.js +52 -26
- package/dist/mcp-spawn.js.map +1 -1
- package/dist/memory-context.d.ts +106 -0
- package/dist/memory-context.d.ts.map +1 -0
- package/dist/memory-context.js +220 -0
- package/dist/memory-context.js.map +1 -0
- package/dist/memory-curation.d.ts +172 -0
- package/dist/memory-curation.d.ts.map +1 -0
- package/dist/memory-curation.js +277 -0
- package/dist/memory-curation.js.map +1 -0
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +34 -0
- package/dist/program.js.map +1 -1
- package/dist/provider-auth.d.ts +70 -1
- package/dist/provider-auth.d.ts.map +1 -1
- package/dist/provider-auth.js +106 -2
- package/dist/provider-auth.js.map +1 -1
- package/dist/runtime/cache-dir.d.ts.map +1 -1
- package/dist/runtime/cache-dir.js +8 -3
- package/dist/runtime/cache-dir.js.map +1 -1
- package/dist/runtime/decision-provenance.d.ts +63 -0
- package/dist/runtime/decision-provenance.d.ts.map +1 -0
- package/dist/runtime/decision-provenance.js +107 -0
- package/dist/runtime/decision-provenance.js.map +1 -0
- package/dist/runtime/prompt-substitute.d.ts +100 -0
- package/dist/runtime/prompt-substitute.d.ts.map +1 -0
- package/dist/runtime/prompt-substitute.js +124 -0
- package/dist/runtime/prompt-substitute.js.map +1 -0
- package/dist/runtime/review-cost-cap.d.ts +167 -0
- package/dist/runtime/review-cost-cap.d.ts.map +1 -0
- package/dist/runtime/review-cost-cap.js +182 -0
- package/dist/runtime/review-cost-cap.js.map +1 -0
- package/dist/runtime/reviewer-abstain.d.ts +101 -0
- package/dist/runtime/reviewer-abstain.d.ts.map +1 -0
- package/dist/runtime/reviewer-abstain.js +138 -0
- package/dist/runtime/reviewer-abstain.js.map +1 -0
- package/dist/runtime/reviewer.d.ts +75 -0
- package/dist/runtime/reviewer.d.ts.map +1 -1
- package/dist/runtime/reviewer.js +235 -81
- package/dist/runtime/reviewer.js.map +1 -1
- package/dist/runtime/singleton.d.ts.map +1 -1
- package/dist/runtime/singleton.js +9 -1
- package/dist/runtime/singleton.js.map +1 -1
- package/dist/serving-auth/index.d.ts +79 -0
- package/dist/serving-auth/index.d.ts.map +1 -0
- package/dist/serving-auth/index.js +169 -0
- package/dist/serving-auth/index.js.map +1 -0
- package/dist/task-runner.d.ts +19 -2
- package/dist/task-runner.d.ts.map +1 -1
- package/dist/task-runner.js +110 -28
- package/dist/task-runner.js.map +1 -1
- package/dist/watch.d.ts +13 -0
- package/dist/watch.d.ts.map +1 -1
- package/dist/watch.js +128 -20
- package/dist/watch.js.map +1 -1
- package/package.json +3 -2
package/dist/provider-auth.js
CHANGED
|
@@ -29,9 +29,53 @@
|
|
|
29
29
|
import os from "node:os";
|
|
30
30
|
import path from "node:path";
|
|
31
31
|
import { RUNNER_KEYCHAIN_SERVICE } from "./types.js";
|
|
32
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* NS RUN-3: the vault-sourced key cached by hydrateAnthropicApiKey (below),
|
|
34
|
+
* when ACC_RUNNER_ANTHROPIC_KEY_SOURCE=secret-store resolved one at startup.
|
|
35
|
+
* Deliberately NOT process.env — the ambient-key guard
|
|
36
|
+
* (scripts/ci/no-ambient-anthropic-key.mjs) is STRICT for this tree: zero
|
|
37
|
+
* literal `process.env.ANTHROPIC_API_KEY` reads OR writes, no allowlist. A
|
|
38
|
+
* module-level cache, consulted only through the parameterised
|
|
39
|
+
* anthropicApiKey() below, keeps every consumer (resolveAuthMode,
|
|
40
|
+
* withAnthropicAuth, authPrecedenceHint) on the same sanctioned resolution
|
|
41
|
+
* path without ever touching the ambient global.
|
|
42
|
+
*/
|
|
43
|
+
let vaultAnthropicApiKey = null;
|
|
44
|
+
/** Test-only: clear the vault-sourced key cache between tests. */
|
|
45
|
+
export function __resetVaultAnthropicApiKeyForTests() {
|
|
46
|
+
vaultAnthropicApiKey = null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* RUNNER-SERVING-AUTH: seed the SAME module cache from a source other than
|
|
50
|
+
* the vault fetcher — specifically the org's serving-auth
|
|
51
|
+
* broker (`serving-auth/index.ts`), which hands back the sealed BYOK key when
|
|
52
|
+
* an admin has chosen api_key serving in Settings.
|
|
53
|
+
*
|
|
54
|
+
* This is deliberately a second door onto ONE cache rather than a second
|
|
55
|
+
* mechanism: every consumer (anthropicApiKey → resolveAuthMode →
|
|
56
|
+
* withAnthropicAuth) keeps resolving through the same sanctioned path, and the
|
|
57
|
+
* runner still never reads or writes `process.env.ANTHROPIC_API_KEY`, which the
|
|
58
|
+
* ambient-key guard (scripts/ci/no-ambient-anthropic-key.mjs) forbids outright
|
|
59
|
+
* in this tree.
|
|
60
|
+
*
|
|
61
|
+
* A blank/whitespace key is IGNORED rather than cached: caching "" would make
|
|
62
|
+
* `anthropicApiKey()` fall through to a stale earlier value, and clearing a key
|
|
63
|
+
* is not something this function is for (choose subscription for that — the
|
|
64
|
+
* kind, not the key, is what stops a spawn from being billed).
|
|
65
|
+
*/
|
|
66
|
+
export function cacheServingApiKey(key) {
|
|
67
|
+
const trimmed = (key ?? "").trim();
|
|
68
|
+
if (trimmed)
|
|
69
|
+
vaultAnthropicApiKey = trimmed;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Trimmed ANTHROPIC_API_KEY from `env`, or the NS RUN-3 vault-sourced key
|
|
73
|
+
* (see hydrateAnthropicApiKey) when `env` doesn't carry one, or "" when
|
|
74
|
+
* neither is set. An explicit value in `env` always wins.
|
|
75
|
+
*/
|
|
33
76
|
export function anthropicApiKey(env = process.env) {
|
|
34
|
-
|
|
77
|
+
const fromEnv = (env.ANTHROPIC_API_KEY ?? "").trim();
|
|
78
|
+
return fromEnv || vaultAnthropicApiKey || "";
|
|
35
79
|
}
|
|
36
80
|
/**
|
|
37
81
|
* Opt-in claude-config isolation. Even with ANTHROPIC_API_KEY
|
|
@@ -172,6 +216,66 @@ export function authPrecedenceHint(env = process.env) {
|
|
|
172
216
|
return ("if you still hit 429 session caps, a claude.ai login is shadowing the API key — " +
|
|
173
217
|
"run `claude /logout` or set ACC_RUNNER_ISOLATE_CLAUDE_CONFIG=1");
|
|
174
218
|
}
|
|
219
|
+
export const ANTHROPIC_KEY_SOURCE_ENV = "ACC_RUNNER_ANTHROPIC_KEY_SOURCE";
|
|
220
|
+
/** Vault secret name the operator stores the Anthropic key under. */
|
|
221
|
+
export const ANTHROPIC_API_KEY_SECRET_NAME = "ANTHROPIC_API_KEY";
|
|
222
|
+
/**
|
|
223
|
+
* Resolve which source ANTHROPIC_API_KEY should come from. Defaults to
|
|
224
|
+
* "env" (today's behaviour, unchanged) so existing desktop runners and
|
|
225
|
+
* containers with the key baked into their launch env see no change — the
|
|
226
|
+
* secret-store path is opt-in via ACC_RUNNER_ANTHROPIC_KEY_SOURCE=secret-store.
|
|
227
|
+
*/
|
|
228
|
+
export function resolveAnthropicKeySource(env = process.env) {
|
|
229
|
+
const v = (env[ANTHROPIC_KEY_SOURCE_ENV] ?? "").trim().toLowerCase();
|
|
230
|
+
return v === "secret-store" || v === "secret_store" || v === "vault" ? "secret-store" : "env";
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Fetch ANTHROPIC_API_KEY from the vault via the supplied fetcher and trim
|
|
234
|
+
* it. Propagates the fetcher's `secret_unavailable: …` error verbatim on
|
|
235
|
+
* failure — never adds the plaintext to a message.
|
|
236
|
+
*/
|
|
237
|
+
export async function anthropicApiKeyFromSecretStore(opts) {
|
|
238
|
+
const plaintext = await opts.fetcher(ANTHROPIC_API_KEY_SECRET_NAME, opts.agentId, opts.toolName);
|
|
239
|
+
return plaintext.trim();
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Startup-time env hydration: when ACC_RUNNER_ANTHROPIC_KEY_SOURCE is
|
|
243
|
+
* "secret-store", resolves ANTHROPIC_API_KEY from the vault, caches it (see
|
|
244
|
+
* vaultAnthropicApiKey above) so anthropicApiKey()'s fallback picks it up for
|
|
245
|
+
* every later call, and returns `base` with it set too; otherwise returns
|
|
246
|
+
* `base` unchanged. An ANTHROPIC_API_KEY already present in `base` always
|
|
247
|
+
* wins and is never clobbered, so a local override for debugging still works
|
|
248
|
+
* even with the source flag flipped. Never mutates `base`.
|
|
249
|
+
*
|
|
250
|
+
* Wired into watch.ts's and companion-run.ts's startup paths (both call this
|
|
251
|
+
* ONCE at process startup, before the first withAnthropicAuth call / claude
|
|
252
|
+
* spawn, and rely on the module cache rather than assigning the result to
|
|
253
|
+
* process.env — see the ambient-key guard note above anthropicApiKey()).
|
|
254
|
+
*/
|
|
255
|
+
export async function hydrateAnthropicApiKey(base = process.env, storeOpts) {
|
|
256
|
+
if (resolveAnthropicKeySource(base) !== "secret-store")
|
|
257
|
+
return base;
|
|
258
|
+
// Local-override check: base's OWN property, not the vault-cache fallback
|
|
259
|
+
// anthropicApiKey() also consults — an explicit env value always wins here,
|
|
260
|
+
// regardless of anything already cached from a prior hydration.
|
|
261
|
+
if ((base.ANTHROPIC_API_KEY ?? "").trim())
|
|
262
|
+
return base;
|
|
263
|
+
if (!storeOpts) {
|
|
264
|
+
throw new Error(`${ANTHROPIC_KEY_SOURCE_ENV}=secret-store requires a vault fetcher at startup (agentId + fetcher)`);
|
|
265
|
+
}
|
|
266
|
+
const key = await anthropicApiKeyFromSecretStore(storeOpts);
|
|
267
|
+
if (!key) {
|
|
268
|
+
throw new Error(`anthropic_secret_unavailable: ${ANTHROPIC_API_KEY_SECRET_NAME} resolved to an empty value`);
|
|
269
|
+
}
|
|
270
|
+
// NS RUN-3: cache it for anthropicApiKey()'s fallback so downstream
|
|
271
|
+
// consumers (withAnthropicAuth, resolveAuthMode, …) resolve the vault key
|
|
272
|
+
// WITHOUT the runner ever writing process.env.ANTHROPIC_API_KEY directly
|
|
273
|
+
// (see the module-cache doc comment above anthropicApiKey()). The returned
|
|
274
|
+
// env object still carries it explicitly too, for a caller that reads the
|
|
275
|
+
// return value directly.
|
|
276
|
+
vaultAnthropicApiKey = key;
|
|
277
|
+
return { ...base, ANTHROPIC_API_KEY: key };
|
|
278
|
+
}
|
|
175
279
|
/**
|
|
176
280
|
* The per-provider keychain account under the frozen `acc-runner` service.
|
|
177
281
|
* FROZEN-CONTRACT EXTENSION (Track-A note): this ADDS a namespaced account
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-auth.js","sourceRoot":"","sources":["../src/provider-auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"provider-auth.js","sourceRoot":"","sources":["../src/provider-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-dir.d.ts","sourceRoot":"","sources":["../../src/runtime/cache-dir.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cache-dir.d.ts","sourceRoot":"","sources":["../../src/runtime/cache-dir.ts"],"names":[],"mappings":"AAoBA;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAU3E;AAED,wBAAgB,QAAQ,IAAI,MAAM,CAMjC"}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import os from "node:os";
|
|
18
18
|
import path from "node:path";
|
|
19
|
+
import { resolveInstance } from "../instance.js";
|
|
19
20
|
/**
|
|
20
21
|
* The filesystem-safe slug that namespaces the default cache root. Returns
|
|
21
22
|
* the sanitised ACC_RUNNER_ID when set, else "" (no namespacing). A minted
|
|
@@ -25,11 +26,15 @@ import path from "node:path";
|
|
|
25
26
|
*/
|
|
26
27
|
export function cacheNamespace(env = process.env) {
|
|
27
28
|
const id = env.ACC_RUNNER_ID?.trim();
|
|
28
|
-
if (!id)
|
|
29
|
-
return "";
|
|
30
29
|
// Keep the id readable on disk but strip anything that could escape the
|
|
31
30
|
// directory or trip a shell (/, .., spaces, etc.).
|
|
32
|
-
|
|
31
|
+
if (id)
|
|
32
|
+
return id.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
33
|
+
// NAMED-INSTANCES-1: a named instance namespaces the root too (isolated
|
|
34
|
+
// locks/worktrees/quarantine, no hand-assigned id/dir). ACC_RUNNER_ID still
|
|
35
|
+
// wins; unnamed stays "". Name is path-safe; `instance-` avoids id slugs.
|
|
36
|
+
const instance = resolveInstance(env);
|
|
37
|
+
return instance ? `instance-${instance}` : "";
|
|
33
38
|
}
|
|
34
39
|
export function cacheDir() {
|
|
35
40
|
const override = process.env.ACC_RUNNER_CACHE_DIR?.trim();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-dir.js","sourceRoot":"","sources":["../../src/runtime/cache-dir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"cache-dir.js","sourceRoot":"","sources":["../../src/runtime/cache-dir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,MAAyB,OAAO,CAAC,GAAG;IACjE,MAAM,EAAE,GAAG,GAAG,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC;IACrC,wEAAwE;IACxE,mDAAmD;IACnD,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;IACnD,wEAAwE;IACxE,4EAA4E;IAC5E,0EAA0E;IAC1E,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACtC,OAAO,QAAQ,CAAC,CAAC,CAAC,YAAY,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,IAAI,EAAE,CAAC;IAC1D,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC7D,MAAM,EAAE,GAAG,cAAc,EAAE,CAAC;IAC5B,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REVIEW-DECISION-PROVENANCE (runner half; mirrors
|
|
3
|
+
* api/_lib/reviewer/decision-provenance.ts, which carries the full rationale).
|
|
4
|
+
*
|
|
5
|
+
* MEASURED FAULT (PR #1913): `decision = "reject"` was persisted while every
|
|
6
|
+
* entry in `reasons[]` concluded "Met." — a full-pass analysis — with nothing on
|
|
7
|
+
* the row explaining the disagreement. That verdict was not asserted by the
|
|
8
|
+
* reviewer; the prose fallback in `extractReviewerDecision` SYNTHESIZED it from
|
|
9
|
+
* the first approve/reject keyword when the model answered in markdown, and left
|
|
10
|
+
* no trace. The contrast case (PR #1911) was a legitimate forced reject that
|
|
11
|
+
* named its rationale in `reasons[]`.
|
|
12
|
+
*
|
|
13
|
+
* INVARIANT: a forced or synthesized decision carries its justification in
|
|
14
|
+
* `reasons[]` plus a machine-readable marker naming the override source. The
|
|
15
|
+
* runner is the hot-path writer (acc.submit_review), so the guard runs here,
|
|
16
|
+
* immediately before the row is persisted.
|
|
17
|
+
*
|
|
18
|
+
* JUDGEMENT IS NEVER ALTERED — nothing here flips approve↔reject. `reasons[]` is
|
|
19
|
+
* the only channel (acc.review_queue stores `reasons text[]` and submit_review
|
|
20
|
+
* takes no extra argument), so the marker rides there in a parseable form, the
|
|
21
|
+
* same way the read-coverage telemetry line does.
|
|
22
|
+
*
|
|
23
|
+
* PURE. No I/O, no clock, no client.
|
|
24
|
+
*/
|
|
25
|
+
/** Where a decision came from when it was not asserted by the analysis itself. */
|
|
26
|
+
export type DecisionSource = "prose_keyword" | "reconciled_contradiction";
|
|
27
|
+
export interface DecisionProvenance {
|
|
28
|
+
decision_source: DecisionSource;
|
|
29
|
+
override_reason: string;
|
|
30
|
+
}
|
|
31
|
+
/** Stable prefix for cheap row detection. */
|
|
32
|
+
export declare const DECISION_PROVENANCE_PREFIX = "decision_provenance:";
|
|
33
|
+
/** Rationale stamped when the prose fallback synthesizes the verdict. */
|
|
34
|
+
export declare const PROSE_KEYWORD_OVERRIDE_REASON: string;
|
|
35
|
+
/** Rationale stamped when a reject is contradicted by its own reasons. */
|
|
36
|
+
export declare const CONTRADICTION_OVERRIDE_REASON: string;
|
|
37
|
+
/** Render a provenance record as the reasons[] line that persists it. */
|
|
38
|
+
export declare function decisionProvenanceReason(p: DecisionProvenance): string;
|
|
39
|
+
/** True when a single reason IS the provenance marker (never a finding). */
|
|
40
|
+
export declare function isDecisionProvenanceReason(reason: string | null | undefined): boolean;
|
|
41
|
+
/** The first provenance marker carried by a verdict's reasons, or null. */
|
|
42
|
+
export declare function parseDecisionProvenance(reasons: readonly (string | null | undefined)[] | null | undefined): DecisionProvenance | null;
|
|
43
|
+
/**
|
|
44
|
+
* True when a verdict's reasons read as a FULL-PASS analysis: at least one
|
|
45
|
+
* criterion recorded as met and no defect language anywhere. Lines that are
|
|
46
|
+
* neither (telemetry such as the coverage line) are tolerated.
|
|
47
|
+
*/
|
|
48
|
+
export declare function allCriteriaMet(reasons: readonly (string | null | undefined)[] | null | undefined): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* The submit-seam guard. A reject whose reasons record a clean full pass and
|
|
51
|
+
* which carries no provenance marker gets one appended, so the #1913 shape can
|
|
52
|
+
* never be persisted unexplained. Every other verdict is returned untouched and
|
|
53
|
+
* the DECISION IS NEVER CHANGED. Idempotent.
|
|
54
|
+
*/
|
|
55
|
+
export declare function reconcileReviewDecision<D extends string>(verdict: {
|
|
56
|
+
decision: D;
|
|
57
|
+
reasons?: readonly (string | null | undefined)[] | null;
|
|
58
|
+
}): {
|
|
59
|
+
decision: D;
|
|
60
|
+
reasons: string[];
|
|
61
|
+
provenance: DecisionProvenance | null;
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=decision-provenance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-provenance.d.ts","sourceRoot":"","sources":["../../src/runtime/decision-provenance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,kFAAkF;AAClF,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,0BAA0B,CAAC;AAE1E,MAAM,WAAW,kBAAkB;IACjC,eAAe,EAAE,cAAc,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,6CAA6C;AAC7C,eAAO,MAAM,0BAA0B,yBAAyB,CAAC;AAKjE,yEAAyE;AACzE,eAAO,MAAM,6BAA6B,QAGe,CAAC;AAE1D,0EAA0E;AAC1E,eAAO,MAAM,6BAA6B,QAGK,CAAC;AAEhD,yEAAyE;AACzE,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAGtE;AAED,4EAA4E;AAC5E,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAErF;AAED,2EAA2E;AAC3E,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,GACjE,kBAAkB,GAAG,IAAI,CAS3B;AAeD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,GACjE,OAAO,CAST;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,MAAM,EAAE,OAAO,EAAE;IACjE,QAAQ,EAAE,CAAC,CAAC;IACZ,OAAO,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAAG,IAAI,CAAC;CACzD,GAAG;IAAE,QAAQ,EAAE,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,CAAA;CAAE,CAiB5E"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REVIEW-DECISION-PROVENANCE (runner half; mirrors
|
|
3
|
+
* api/_lib/reviewer/decision-provenance.ts, which carries the full rationale).
|
|
4
|
+
*
|
|
5
|
+
* MEASURED FAULT (PR #1913): `decision = "reject"` was persisted while every
|
|
6
|
+
* entry in `reasons[]` concluded "Met." — a full-pass analysis — with nothing on
|
|
7
|
+
* the row explaining the disagreement. That verdict was not asserted by the
|
|
8
|
+
* reviewer; the prose fallback in `extractReviewerDecision` SYNTHESIZED it from
|
|
9
|
+
* the first approve/reject keyword when the model answered in markdown, and left
|
|
10
|
+
* no trace. The contrast case (PR #1911) was a legitimate forced reject that
|
|
11
|
+
* named its rationale in `reasons[]`.
|
|
12
|
+
*
|
|
13
|
+
* INVARIANT: a forced or synthesized decision carries its justification in
|
|
14
|
+
* `reasons[]` plus a machine-readable marker naming the override source. The
|
|
15
|
+
* runner is the hot-path writer (acc.submit_review), so the guard runs here,
|
|
16
|
+
* immediately before the row is persisted.
|
|
17
|
+
*
|
|
18
|
+
* JUDGEMENT IS NEVER ALTERED — nothing here flips approve↔reject. `reasons[]` is
|
|
19
|
+
* the only channel (acc.review_queue stores `reasons text[]` and submit_review
|
|
20
|
+
* takes no extra argument), so the marker rides there in a parseable form, the
|
|
21
|
+
* same way the read-coverage telemetry line does.
|
|
22
|
+
*
|
|
23
|
+
* PURE. No I/O, no clock, no client.
|
|
24
|
+
*/
|
|
25
|
+
/** Stable prefix for cheap row detection. */
|
|
26
|
+
export const DECISION_PROVENANCE_PREFIX = "decision_provenance:";
|
|
27
|
+
const PROVENANCE_RE = /^decision_provenance:\s*decision_source=(\S+)\s+override_reason=([\s\S]*)$/i;
|
|
28
|
+
/** Rationale stamped when the prose fallback synthesizes the verdict. */
|
|
29
|
+
export const PROSE_KEYWORD_OVERRIDE_REASON = "the reviewer answered in prose instead of the structured verdict object, so " +
|
|
30
|
+
"this decision was synthesized by the parser from the first approve/reject " +
|
|
31
|
+
"keyword in the text rather than asserted as a verdict";
|
|
32
|
+
/** Rationale stamped when a reject is contradicted by its own reasons. */
|
|
33
|
+
export const CONTRADICTION_OVERRIDE_REASON = "the analysis recorded every acceptance criterion as met, yet the verdict is " +
|
|
34
|
+
"reject and the row states no override rationale — the verdict is kept as " +
|
|
35
|
+
"submitted and flagged here for the operator";
|
|
36
|
+
/** Render a provenance record as the reasons[] line that persists it. */
|
|
37
|
+
export function decisionProvenanceReason(p) {
|
|
38
|
+
const rationale = p.override_reason.replace(/\s+/g, " ").trim() || "(unstated)";
|
|
39
|
+
return `${DECISION_PROVENANCE_PREFIX} decision_source=${p.decision_source} override_reason=${rationale}`;
|
|
40
|
+
}
|
|
41
|
+
/** True when a single reason IS the provenance marker (never a finding). */
|
|
42
|
+
export function isDecisionProvenanceReason(reason) {
|
|
43
|
+
return typeof reason === "string" && PROVENANCE_RE.test(reason.trim());
|
|
44
|
+
}
|
|
45
|
+
/** The first provenance marker carried by a verdict's reasons, or null. */
|
|
46
|
+
export function parseDecisionProvenance(reasons) {
|
|
47
|
+
for (const raw of reasons ?? []) {
|
|
48
|
+
if (typeof raw !== "string")
|
|
49
|
+
continue;
|
|
50
|
+
const m = PROVENANCE_RE.exec(raw.trim());
|
|
51
|
+
if (m) {
|
|
52
|
+
return { decision_source: m[1], override_reason: m[2].trim() };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
/** Severity tag the server-side calibrator stamps on stored findings. */
|
|
58
|
+
const SEVERITY_TAG_RE = /^\s*\[(?:blocking|advisory)\]\s*/i;
|
|
59
|
+
/** A finding that concludes the criterion it names was satisfied. */
|
|
60
|
+
const MET_RE = /\bmet\b\s*[.)\]]?\s*$/i;
|
|
61
|
+
/**
|
|
62
|
+
* Defect / non-satisfaction language. Present anywhere ⇒ the analysis is not a
|
|
63
|
+
* clean full pass and the guard stands down — deliberately biased toward NOT
|
|
64
|
+
* stamping, since a missed stamp is an audit gap while a wrong one would
|
|
65
|
+
* editorialize a real reject.
|
|
66
|
+
*/
|
|
67
|
+
const DEFECT_RE = /\b(?:not\s+met|unmet|partially\s+met|fail(?:s|ed|ing|ure)?|missing|absent|violat\w*|blocking|defect|regression|broken|incorrect|unsafe|does\s+not|is\s+not|are\s+not|was\s+not|cannot|can['’]?t)\b/i;
|
|
68
|
+
/**
|
|
69
|
+
* True when a verdict's reasons read as a FULL-PASS analysis: at least one
|
|
70
|
+
* criterion recorded as met and no defect language anywhere. Lines that are
|
|
71
|
+
* neither (telemetry such as the coverage line) are tolerated.
|
|
72
|
+
*/
|
|
73
|
+
export function allCriteriaMet(reasons) {
|
|
74
|
+
const findings = (reasons ?? [])
|
|
75
|
+
.filter((r) => typeof r === "string")
|
|
76
|
+
.filter((r) => !isDecisionProvenanceReason(r))
|
|
77
|
+
.map((r) => r.replace(SEVERITY_TAG_RE, "").trim())
|
|
78
|
+
.filter((r) => r.length > 0);
|
|
79
|
+
if (findings.length === 0)
|
|
80
|
+
return false;
|
|
81
|
+
if (findings.some((r) => DEFECT_RE.test(r)))
|
|
82
|
+
return false;
|
|
83
|
+
return findings.some((r) => MET_RE.test(r));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The submit-seam guard. A reject whose reasons record a clean full pass and
|
|
87
|
+
* which carries no provenance marker gets one appended, so the #1913 shape can
|
|
88
|
+
* never be persisted unexplained. Every other verdict is returned untouched and
|
|
89
|
+
* the DECISION IS NEVER CHANGED. Idempotent.
|
|
90
|
+
*/
|
|
91
|
+
export function reconcileReviewDecision(verdict) {
|
|
92
|
+
const reasons = (verdict.reasons ?? []).filter((r) => typeof r === "string");
|
|
93
|
+
const existing = parseDecisionProvenance(reasons);
|
|
94
|
+
if (verdict.decision !== "reject" || existing !== null || !allCriteriaMet(reasons)) {
|
|
95
|
+
return { decision: verdict.decision, reasons, provenance: existing };
|
|
96
|
+
}
|
|
97
|
+
const provenance = {
|
|
98
|
+
decision_source: "reconciled_contradiction",
|
|
99
|
+
override_reason: CONTRADICTION_OVERRIDE_REASON,
|
|
100
|
+
};
|
|
101
|
+
return {
|
|
102
|
+
decision: verdict.decision,
|
|
103
|
+
reasons: [...reasons, decisionProvenanceReason(provenance)],
|
|
104
|
+
provenance,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=decision-provenance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-provenance.js","sourceRoot":"","sources":["../../src/runtime/decision-provenance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S15-FU/REVIEW-1 (AC1) — literal-safe reviewer-prompt placeholder substitution.
|
|
3
|
+
*
|
|
4
|
+
* THE FAULT THIS CLOSES (the LEDGER-1 #1933 episode)
|
|
5
|
+
* ----------------------------------------------------------------------------
|
|
6
|
+
* `renderReviewerPrompt` interpolated the PR diff into the reviewer prompt with
|
|
7
|
+
*
|
|
8
|
+
* template.replace("{{pr_diff}}", diff)
|
|
9
|
+
*
|
|
10
|
+
* `String.prototype.replace` treats a STRING replacement as a pattern: `$$`,
|
|
11
|
+
* `$&`, `` $` ``, `$'` and `$1`…`$9` are expanded, not copied. So a diff whose
|
|
12
|
+
* text contains `$'` — which every one of these clean SQL literals does —
|
|
13
|
+
*
|
|
14
|
+
* select seq, slug, sha from acc.applied_migrations where seq ~ '^[0-9]+$' order by 1
|
|
15
|
+
* ^^ $'
|
|
16
|
+
*
|
|
17
|
+
* made `replace` splice in "the portion of the template AFTER the match": the
|
|
18
|
+
* reviewer's OWN instruction text, starting at
|
|
19
|
+
* "Now answer in this exact JSON shape, with no surrounding prose:". The
|
|
20
|
+
* reviewer then read that prose INSIDE the SQL literal and — correctly, given
|
|
21
|
+
* what it was shown — reported "prose spliced into the literal" as a corruption
|
|
22
|
+
* defect, at 0.75–0.85 confidence, across several independent sessions. The
|
|
23
|
+
* diff on disk was clean the whole time; the harness corrupted it on the way in.
|
|
24
|
+
*
|
|
25
|
+
* (Same root cause as the 3× `gatherAppliedLedger` corruption in
|
|
26
|
+
* `scripts/ci/migration-lint.mjs`: `$'` expansion in a string replacement, not
|
|
27
|
+
* context bleed.)
|
|
28
|
+
*
|
|
29
|
+
* THE RULE: NEVER pass untrusted text as `replace`'s string replacement. A
|
|
30
|
+
* FUNCTION replacement is copied verbatim — `$` has no meaning in its return
|
|
31
|
+
* value — so `substitutePlaceholder` below is the only sanctioned way to put a
|
|
32
|
+
* diff, PR body, title, description, or acceptance criterion into a prompt.
|
|
33
|
+
*
|
|
34
|
+
* Mirrored, byte-for-byte in behaviour, from
|
|
35
|
+
* `api/_lib/reviewer/prompt-substitute.ts` (the server path). The runner ships as
|
|
36
|
+
* a published npm package and cannot import from `api/_lib`, so the module is
|
|
37
|
+
* duplicated and pinned by a cross-implementation parity test.
|
|
38
|
+
* // keep in sync with api/_lib/reviewer/prompt-substitute.ts
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* The `$`-sequences `String.prototype.replace` expands inside a STRING
|
|
42
|
+
* replacement. Documented here because the whole module exists to defeat them.
|
|
43
|
+
*
|
|
44
|
+
* `$$` → a literal `$`
|
|
45
|
+
* `$&` → the matched substring
|
|
46
|
+
* ``$` `` → everything BEFORE the match
|
|
47
|
+
* `$'` → everything AFTER the match ← the #1933 corruption
|
|
48
|
+
* `$n` → capture group n
|
|
49
|
+
*/
|
|
50
|
+
export declare const REPLACEMENT_EXPANSION_SEQUENCES: readonly ["$$", "$&", "$`", "$'"];
|
|
51
|
+
/**
|
|
52
|
+
* True when `value` contains a sequence that `String.prototype.replace` would
|
|
53
|
+
* expand if it were used as a string replacement. Diagnostic only — the
|
|
54
|
+
* substitution below is safe either way — but it lets the render seam log that a
|
|
55
|
+
* value was in the dangerous class without ever emitting the value itself.
|
|
56
|
+
*/
|
|
57
|
+
export declare function hasReplacementExpansion(value: string | null | undefined): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Substitute the FIRST occurrence of `placeholder` with `value`, VERBATIM.
|
|
60
|
+
*
|
|
61
|
+
* The replacement is a function, so no `$`-sequence in `value` is expanded and
|
|
62
|
+
* no template text can bleed into it. An absent placeholder is a no-op (the
|
|
63
|
+
* template is returned unchanged) — same as the string form it replaces.
|
|
64
|
+
*/
|
|
65
|
+
export declare function substitutePlaceholder(template: string, placeholder: string, value: string | null | undefined): string;
|
|
66
|
+
/**
|
|
67
|
+
* Apply an ORDERED list of `[placeholder, value]` pairs. Order matters only in
|
|
68
|
+
* that each pass sees the output of the previous one; because values are
|
|
69
|
+
* inserted verbatim, a value that happens to contain a later placeholder's
|
|
70
|
+
* literal text (`"{{pr_diff}}"` inside a PR body, say) is NOT re-substituted
|
|
71
|
+
* away — the later pass still matches the TEMPLATE's own placeholder first,
|
|
72
|
+
* since it appears earlier in the string only if the template put it there.
|
|
73
|
+
*/
|
|
74
|
+
export declare function substitutePlaceholders(template: string, pairs: ReadonlyArray<readonly [string, string | null | undefined]>): string;
|
|
75
|
+
/** One value that did not survive substitution verbatim. */
|
|
76
|
+
export interface PromptRenderBleed {
|
|
77
|
+
/** The placeholder whose value was corrupted (e.g. `{{pr_diff}}`). */
|
|
78
|
+
placeholder: string;
|
|
79
|
+
/** Length of the value we expected to find verbatim (never the value itself). */
|
|
80
|
+
expected_chars: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* INTEGRITY CHECK: every value substituted for a placeholder THE TEMPLATE
|
|
84
|
+
* ACTUALLY CONTAINS must appear VERBATIM in the rendered output. Anything else
|
|
85
|
+
* means the render mangled file content — the #1933 class — and the review must
|
|
86
|
+
* NOT proceed on that prompt (the reviewer would be judging text the repo does
|
|
87
|
+
* not contain).
|
|
88
|
+
*
|
|
89
|
+
* `template` is required precisely so a placeholder the template OMITS is not
|
|
90
|
+
* reported: a prompt that never asks for the PR body was not corrupted by the
|
|
91
|
+
* body's absence, it simply does not carry it. Only a value the template asked
|
|
92
|
+
* for and did not receive verbatim is a bleed.
|
|
93
|
+
*
|
|
94
|
+
* Pure and allocation-cheap (`includes`, no regex). Values are never echoed into
|
|
95
|
+
* the result, so a bleed report is safe to log.
|
|
96
|
+
*/
|
|
97
|
+
export declare function findPromptRenderBleed(template: string, rendered: string, pairs: ReadonlyArray<readonly [string, string | null | undefined]>): PromptRenderBleed[];
|
|
98
|
+
/** Operator-facing one-liner naming the corrupted placeholders (never the values). */
|
|
99
|
+
export declare function describePromptRenderBleed(bleeds: PromptRenderBleed[]): string;
|
|
100
|
+
//# sourceMappingURL=prompt-substitute.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-substitute.d.ts","sourceRoot":"","sources":["../../src/runtime/prompt-substitute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,+BAA+B,mCAAoC,CAAC;AAEjF;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAGjF;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,MAAM,CAGR;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,GACjE,MAAM,CAMR;AAED,4DAA4D;AAC5D,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,iFAAiF;IACjF,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,GACjE,iBAAiB,EAAE,CAarB;AAED,sFAAsF;AACtF,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAI7E"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S15-FU/REVIEW-1 (AC1) — literal-safe reviewer-prompt placeholder substitution.
|
|
3
|
+
*
|
|
4
|
+
* THE FAULT THIS CLOSES (the LEDGER-1 #1933 episode)
|
|
5
|
+
* ----------------------------------------------------------------------------
|
|
6
|
+
* `renderReviewerPrompt` interpolated the PR diff into the reviewer prompt with
|
|
7
|
+
*
|
|
8
|
+
* template.replace("{{pr_diff}}", diff)
|
|
9
|
+
*
|
|
10
|
+
* `String.prototype.replace` treats a STRING replacement as a pattern: `$$`,
|
|
11
|
+
* `$&`, `` $` ``, `$'` and `$1`…`$9` are expanded, not copied. So a diff whose
|
|
12
|
+
* text contains `$'` — which every one of these clean SQL literals does —
|
|
13
|
+
*
|
|
14
|
+
* select seq, slug, sha from acc.applied_migrations where seq ~ '^[0-9]+$' order by 1
|
|
15
|
+
* ^^ $'
|
|
16
|
+
*
|
|
17
|
+
* made `replace` splice in "the portion of the template AFTER the match": the
|
|
18
|
+
* reviewer's OWN instruction text, starting at
|
|
19
|
+
* "Now answer in this exact JSON shape, with no surrounding prose:". The
|
|
20
|
+
* reviewer then read that prose INSIDE the SQL literal and — correctly, given
|
|
21
|
+
* what it was shown — reported "prose spliced into the literal" as a corruption
|
|
22
|
+
* defect, at 0.75–0.85 confidence, across several independent sessions. The
|
|
23
|
+
* diff on disk was clean the whole time; the harness corrupted it on the way in.
|
|
24
|
+
*
|
|
25
|
+
* (Same root cause as the 3× `gatherAppliedLedger` corruption in
|
|
26
|
+
* `scripts/ci/migration-lint.mjs`: `$'` expansion in a string replacement, not
|
|
27
|
+
* context bleed.)
|
|
28
|
+
*
|
|
29
|
+
* THE RULE: NEVER pass untrusted text as `replace`'s string replacement. A
|
|
30
|
+
* FUNCTION replacement is copied verbatim — `$` has no meaning in its return
|
|
31
|
+
* value — so `substitutePlaceholder` below is the only sanctioned way to put a
|
|
32
|
+
* diff, PR body, title, description, or acceptance criterion into a prompt.
|
|
33
|
+
*
|
|
34
|
+
* Mirrored, byte-for-byte in behaviour, from
|
|
35
|
+
* `api/_lib/reviewer/prompt-substitute.ts` (the server path). The runner ships as
|
|
36
|
+
* a published npm package and cannot import from `api/_lib`, so the module is
|
|
37
|
+
* duplicated and pinned by a cross-implementation parity test.
|
|
38
|
+
* // keep in sync with api/_lib/reviewer/prompt-substitute.ts
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* The `$`-sequences `String.prototype.replace` expands inside a STRING
|
|
42
|
+
* replacement. Documented here because the whole module exists to defeat them.
|
|
43
|
+
*
|
|
44
|
+
* `$$` → a literal `$`
|
|
45
|
+
* `$&` → the matched substring
|
|
46
|
+
* ``$` `` → everything BEFORE the match
|
|
47
|
+
* `$'` → everything AFTER the match ← the #1933 corruption
|
|
48
|
+
* `$n` → capture group n
|
|
49
|
+
*/
|
|
50
|
+
export const REPLACEMENT_EXPANSION_SEQUENCES = ["$$", "$&", "$`", "$'"];
|
|
51
|
+
/**
|
|
52
|
+
* True when `value` contains a sequence that `String.prototype.replace` would
|
|
53
|
+
* expand if it were used as a string replacement. Diagnostic only — the
|
|
54
|
+
* substitution below is safe either way — but it lets the render seam log that a
|
|
55
|
+
* value was in the dangerous class without ever emitting the value itself.
|
|
56
|
+
*/
|
|
57
|
+
export function hasReplacementExpansion(value) {
|
|
58
|
+
const text = value ?? "";
|
|
59
|
+
return /\$[$&`'\d]/.test(text);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Substitute the FIRST occurrence of `placeholder` with `value`, VERBATIM.
|
|
63
|
+
*
|
|
64
|
+
* The replacement is a function, so no `$`-sequence in `value` is expanded and
|
|
65
|
+
* no template text can bleed into it. An absent placeholder is a no-op (the
|
|
66
|
+
* template is returned unchanged) — same as the string form it replaces.
|
|
67
|
+
*/
|
|
68
|
+
export function substitutePlaceholder(template, placeholder, value) {
|
|
69
|
+
const text = value ?? "";
|
|
70
|
+
return (template ?? "").replace(placeholder, () => text);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Apply an ORDERED list of `[placeholder, value]` pairs. Order matters only in
|
|
74
|
+
* that each pass sees the output of the previous one; because values are
|
|
75
|
+
* inserted verbatim, a value that happens to contain a later placeholder's
|
|
76
|
+
* literal text (`"{{pr_diff}}"` inside a PR body, say) is NOT re-substituted
|
|
77
|
+
* away — the later pass still matches the TEMPLATE's own placeholder first,
|
|
78
|
+
* since it appears earlier in the string only if the template put it there.
|
|
79
|
+
*/
|
|
80
|
+
export function substitutePlaceholders(template, pairs) {
|
|
81
|
+
let out = template ?? "";
|
|
82
|
+
for (const [placeholder, value] of pairs) {
|
|
83
|
+
out = substitutePlaceholder(out, placeholder, value);
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* INTEGRITY CHECK: every value substituted for a placeholder THE TEMPLATE
|
|
89
|
+
* ACTUALLY CONTAINS must appear VERBATIM in the rendered output. Anything else
|
|
90
|
+
* means the render mangled file content — the #1933 class — and the review must
|
|
91
|
+
* NOT proceed on that prompt (the reviewer would be judging text the repo does
|
|
92
|
+
* not contain).
|
|
93
|
+
*
|
|
94
|
+
* `template` is required precisely so a placeholder the template OMITS is not
|
|
95
|
+
* reported: a prompt that never asks for the PR body was not corrupted by the
|
|
96
|
+
* body's absence, it simply does not carry it. Only a value the template asked
|
|
97
|
+
* for and did not receive verbatim is a bleed.
|
|
98
|
+
*
|
|
99
|
+
* Pure and allocation-cheap (`includes`, no regex). Values are never echoed into
|
|
100
|
+
* the result, so a bleed report is safe to log.
|
|
101
|
+
*/
|
|
102
|
+
export function findPromptRenderBleed(template, rendered, pairs) {
|
|
103
|
+
const out = [];
|
|
104
|
+
const tpl = template ?? "";
|
|
105
|
+
const text = rendered ?? "";
|
|
106
|
+
for (const [placeholder, value] of pairs) {
|
|
107
|
+
const expected = value ?? "";
|
|
108
|
+
if (expected.length === 0)
|
|
109
|
+
continue;
|
|
110
|
+
if (!tpl.includes(placeholder))
|
|
111
|
+
continue;
|
|
112
|
+
if (!text.includes(expected)) {
|
|
113
|
+
out.push({ placeholder, expected_chars: expected.length });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
/** Operator-facing one-liner naming the corrupted placeholders (never the values). */
|
|
119
|
+
export function describePromptRenderBleed(bleeds) {
|
|
120
|
+
return bleeds
|
|
121
|
+
.map((b) => `${b.placeholder} (${b.expected_chars} chars did not survive verbatim)`)
|
|
122
|
+
.join("; ");
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=prompt-substitute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-substitute.js","sourceRoot":"","sources":["../../src/runtime/prompt-substitute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC;AAEjF;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAgC;IACtE,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;IACzB,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,QAAgB,EAChB,WAAmB,EACnB,KAAgC;IAEhC,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;IACzB,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;AAC3D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CACpC,QAAgB,EAChB,KAAkE;IAElE,IAAI,GAAG,GAAG,QAAQ,IAAI,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;QACzC,GAAG,GAAG,qBAAqB,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAUD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CACnC,QAAgB,EAChB,QAAgB,EAChB,KAAkE;IAElE,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,MAAM,GAAG,GAAG,QAAQ,IAAI,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,QAAQ,IAAI,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE,CAAC;QAC7B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACpC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC;YAAE,SAAS;QACzC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,yBAAyB,CAAC,MAA2B;IACnE,OAAO,MAAM;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,cAAc,kCAAkC,CAAC;SACnF,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
|