@quikit/quikpilot-runner 8.2.7 → 8.2.8

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/dist/index.js CHANGED
@@ -73,10 +73,11 @@ async function describeQuikTrackFailure(res) {
73
73
 
74
74
  // src/callback.ts
75
75
  async function reportBack(params) {
76
- const { callbackUrl, runToken, status, branch, warnings, targeting, baselineFindings } = params;
76
+ const { callbackUrl, runToken, status, branch, warnings, targeting, baselineFindings, unresolved } = params;
77
77
  const body = { status };
78
78
  if (branch) body.branch = branch;
79
79
  if (warnings && warnings.length > 0) body.warnings = warnings;
80
+ if (unresolved && unresolved.length > 0) body.unresolved = unresolved;
80
81
  if (targeting) body.targeting = targeting;
81
82
  if (baselineFindings) body.baselineFindings = baselineFindings;
82
83
  const res = await fetch(callbackUrl, {
@@ -250,6 +251,16 @@ function findDeniedContextPath(contextPaths, patterns) {
250
251
  return null;
251
252
  }
252
253
 
254
+ // src/runtimeReplyError.ts
255
+ var RuntimeReplyError = class extends Error {
256
+ summary;
257
+ constructor(message, summary) {
258
+ super(message);
259
+ this.name = "RuntimeReplyError";
260
+ this.summary = summary;
261
+ }
262
+ };
263
+
253
264
  // src/envelope.ts
254
265
  var HARD_FAILURE_CODES = /* @__PURE__ */ new Set(["SEMANTIC_MISMATCH", "CONTRACT_REJECTED"]);
255
266
  function checkHardFailure(envelope, useCase, target) {
@@ -257,17 +268,25 @@ function checkHardFailure(envelope, useCase, target) {
257
268
  if (typeof code === "string" && HARD_FAILURE_CODES.has(code)) {
258
269
  const detail = envelope.error ?? envelope.normalizedText ?? "no detail";
259
270
  const where = target ? ` for ${target}` : "";
260
- throw new Error(`${useCase} failed${where} (${code}, not retried): ${detail}`);
271
+ throw new RuntimeReplyError(
272
+ `${useCase} failed${where} (${code}, not retried): ${detail}`,
273
+ `${useCase} failed${where} (${code}, not retried)`
274
+ );
261
275
  }
262
276
  }
263
277
  function unwrapExecute(envelope, useCase) {
264
278
  if (envelope?.success === false) {
265
- throw new Error(
266
- `${useCase} failed: ${envelope.errorCode ?? "UNKNOWN"} \u2014 ${envelope.error ?? envelope.normalizedText ?? "no message"}`
279
+ const code = envelope.errorCode ?? "UNKNOWN";
280
+ throw new RuntimeReplyError(
281
+ `${useCase} failed: ${code} \u2014 ${envelope.error ?? envelope.normalizedText ?? "no message"}`,
282
+ `${useCase} failed: ${code}`
267
283
  );
268
284
  }
269
285
  if (!envelope?.structuredJson || typeof envelope.structuredJson !== "object") {
270
- throw new Error(`${useCase} returned no structuredJson: ${JSON.stringify(envelope)}`);
286
+ throw new RuntimeReplyError(
287
+ `${useCase} returned no structuredJson: ${JSON.stringify(envelope)}`,
288
+ `${useCase} returned no structuredJson`
289
+ );
271
290
  }
272
291
  return envelope.structuredJson;
273
292
  }
@@ -419,7 +438,7 @@ function narrowJudgedByForTarget(pool, targets, codeTargetPath) {
419
438
  function toWireJudgedBy(pool) {
420
439
  return pool.map(({ automationId: _automationId, ...rest }) => rest);
421
440
  }
422
- async function runCodegen(gateway, targets, workspaceListing, requiredTests, deniedPaths, judgedByPool, warnings, contextLog) {
441
+ async function runCodegen(gateway, targets, workspaceListing, requiredTests, deniedPaths, judgedByPool, warnings, contextLog, outOfScope = []) {
423
442
  const results = [];
424
443
  const listing = new Set(workspaceListing);
425
444
  for (const target of targets) {
@@ -468,6 +487,9 @@ async function runCodegen(gateway, targets, workspaceListing, requiredTests, den
468
487
  if (deniedPaths.length > 0) {
469
488
  contextData.deniedPaths = deniedPaths;
470
489
  }
490
+ if (outOfScope.length > 0) {
491
+ contextData.outOfScope = outOfScope;
492
+ }
471
493
  if (target.automationIds && target.automationIds.length > 0) {
472
494
  const wanted = new Set(target.automationIds);
473
495
  contextData.requiredTests = requiredTests.filter((tc) => wanted.has(tc.automationId));
@@ -521,16 +543,23 @@ async function runCodegen(gateway, targets, workspaceListing, requiredTests, den
521
543
  }
522
544
  if (typeof envelope?.errorCode === "string" && NOT_RETRIED_CODES.has(envelope.errorCode)) {
523
545
  const detail = envelope.error ? `: ${envelope.error}` : "";
524
- throw new Error(`Codegen refused ${target.path}: ${envelope.errorCode} (not retried)${detail}`);
546
+ throw new RuntimeReplyError(
547
+ `Codegen refused ${target.path}: ${envelope.errorCode} (not retried)${detail}`,
548
+ `Codegen refused ${target.path}: ${envelope.errorCode} (not retried)`
549
+ );
525
550
  }
526
551
  if (envelope?.errorCode === JUDGED_BY_TOO_LARGE_CODE) {
527
552
  const detail = envelope.error ? `: ${envelope.error}` : "";
528
- throw new Error(`Codegen refused ${target.path}: ${envelope.errorCode} (after narrowing)${detail}`);
553
+ throw new RuntimeReplyError(
554
+ `Codegen refused ${target.path}: ${envelope.errorCode} (after narrowing)${detail}`,
555
+ `Codegen refused ${target.path}: ${envelope.errorCode} (after narrowing)`
556
+ );
529
557
  }
530
558
  const body = unwrapExecute(envelope, "pilot.codegen");
531
559
  if (body.outcome !== "GENERATED" || typeof body.newContent !== "string") {
532
- throw new Error(
533
- `Codegen refused ${target.path}${body.refusalReason ? `: ${body.refusalReason}` : ""}`
560
+ throw new RuntimeReplyError(
561
+ `Codegen refused ${target.path}${body.refusalReason ? `: ${body.refusalReason}` : ""}`,
562
+ `Codegen refused ${target.path}`
534
563
  );
535
564
  }
536
565
  if (!isNewFile) {
@@ -1360,6 +1389,36 @@ async function runBaselineCheck(params) {
1360
1389
  }
1361
1390
  }
1362
1391
 
1392
+ // src/failureReason.ts
1393
+ var MAX_FAILURE_REASON = 1e3;
1394
+ var MIN_SECRET_LENGTH = 8;
1395
+ var CREDENTIAL_PATTERNS = [
1396
+ /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}/g,
1397
+ /\bgithub_pat_[A-Za-z0-9_]{20,}/g,
1398
+ /x-access-token:[^@\s'"]+/gi,
1399
+ // Only the header form ("Authorization: basic <token>"), or a bearer value that contains a digit: a bare
1400
+ // "basic authentication" or "bearer of the news" is prose, not a credential.
1401
+ /\bauthorization:\s*(?:basic|bearer|token)\s+[^\s'"]{8,}/gi,
1402
+ /\bbearer\s+(?=[A-Za-z0-9._~+/=-]*\d)[A-Za-z0-9._~+/=-]{12,}/gi,
1403
+ // A signed JWT is three dot-separated segments; QuikPilot's own run token is an encrypted JWT (alg dir) of five, the
1404
+ // second one empty. The run token is also matched by exact value, but one that has expired or rotated is not the
1405
+ // current value, so its shape has to catch it.
1406
+ /\beyJ[A-Za-z0-9_-]{8,}(?:\.[A-Za-z0-9_-]*){2,4}/g
1407
+ ];
1408
+ function failureSummary(err) {
1409
+ if (err instanceof RuntimeReplyError) return err.summary;
1410
+ return err instanceof Error ? err.message : String(err);
1411
+ }
1412
+ function describeFailureReason(message, secrets = []) {
1413
+ let text = message;
1414
+ for (const secret of secrets) {
1415
+ if (secret.length >= MIN_SECRET_LENGTH) text = text.split(secret).join("***");
1416
+ }
1417
+ for (const pattern of CREDENTIAL_PATTERNS) text = text.replace(pattern, "***");
1418
+ text = text.replace(/\s*\n\s*/g, " | ").trim();
1419
+ return text.length > MAX_FAILURE_REASON ? `${text.slice(0, MAX_FAILURE_REASON)}\u2026` : text;
1420
+ }
1421
+
1363
1422
  // src/scopeCheck.ts
1364
1423
  var import_node_fs7 = require("node:fs");
1365
1424
  var ROUTE_METHOD_RE = /\b(GET|POST|PUT|PATCH|DELETE)\b/i;
@@ -1474,7 +1533,7 @@ function validateTarget(target, workspaceRoot, workspaceListing, deniedPaths) {
1474
1533
  );
1475
1534
  }
1476
1535
  }
1477
- async function runTargeting(gateway, design, workspaceRoot, workspaceListing, requiredTests, deniedPaths, testDiscovery) {
1536
+ async function runTargeting(gateway, design, workspaceRoot, workspaceListing, requiredTests, deniedPaths, testDiscovery, outOfScope = []) {
1478
1537
  const contextData = {
1479
1538
  // NESTED, NOT FLATTENED — a second vocabulary for the design shape is exactly what
1480
1539
  // QUIKTR-1034 cost.
@@ -1489,6 +1548,9 @@ async function runTargeting(gateway, design, workspaceRoot, workspaceListing, re
1489
1548
  if (deniedPaths.length > 0) {
1490
1549
  contextData.deniedPaths = deniedPaths;
1491
1550
  }
1551
+ if (outOfScope.length > 0) {
1552
+ contextData.outOfScope = outOfScope;
1553
+ }
1492
1554
  contextData.testDiscovery = testDiscovery;
1493
1555
  const res = await fetchWithRetry(`${gateway.baseUrl}/ai/execute`, {
1494
1556
  method: "POST",
@@ -1511,7 +1573,10 @@ async function runTargeting(gateway, design, workspaceRoot, workspaceListing, re
1511
1573
  return { outcome: "NO_TARGET", reason: body.noTargetReason ?? null };
1512
1574
  }
1513
1575
  if (body.outcome !== "TARGETED" || !Array.isArray(body.targets)) {
1514
- throw new Error(`pilot.targeting returned an unexpected shape: ${JSON.stringify(body)}`);
1576
+ throw new RuntimeReplyError(
1577
+ `pilot.targeting returned an unexpected shape: ${JSON.stringify(body)}`,
1578
+ "pilot.targeting returned an unexpected shape"
1579
+ );
1515
1580
  }
1516
1581
  const listing = new Set(workspaceListing);
1517
1582
  for (const target of body.targets) {
@@ -1690,6 +1755,13 @@ async function run(env2) {
1690
1755
  let branch;
1691
1756
  let runToken;
1692
1757
  const warnings = [];
1758
+ const failureReasons = [];
1759
+ const recordFailure = (err) => {
1760
+ const message = err instanceof Error ? err.message : String(err);
1761
+ console.error(message);
1762
+ const reason = describeFailureReason(failureSummary(err), runToken ? [runToken] : []);
1763
+ if (reason) failureReasons.push(reason);
1764
+ };
1693
1765
  let targeted = false;
1694
1766
  let generatedFilePaths = [];
1695
1767
  let targetingResult;
@@ -1700,6 +1772,7 @@ async function run(env2) {
1700
1772
  packet = await fetchPacket(env2.packetUrl, runToken, env2.githubRunId);
1701
1773
  checkoutTargetBranch(packet.targetBranch);
1702
1774
  const workspaceListing = listWorkspaceFiles(packet.workspaceRoot);
1775
+ const outOfScope = (packet.outOfScope ?? []).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
1703
1776
  const targeting = await runTargeting(
1704
1777
  packet.gateway,
1705
1778
  packet.design,
@@ -1707,7 +1780,8 @@ async function run(env2) {
1707
1780
  workspaceListing,
1708
1781
  packet.requiredTests,
1709
1782
  packet.guardrails.deniedPaths,
1710
- packet.testDiscovery
1783
+ packet.testDiscovery,
1784
+ outOfScope
1711
1785
  );
1712
1786
  targetingResult = targeting;
1713
1787
  if (targeting.outcome === "NO_TARGET") {
@@ -1755,7 +1829,8 @@ async function run(env2) {
1755
1829
  packet.guardrails.deniedPaths,
1756
1830
  judgedByPool,
1757
1831
  warnings,
1758
- contextLog
1832
+ contextLog,
1833
+ outOfScope
1759
1834
  )
1760
1835
  ]);
1761
1836
  const depCheck = checkImportDependencies(
@@ -1818,7 +1893,7 @@ async function run(env2) {
1818
1893
  }
1819
1894
  } catch (err) {
1820
1895
  hardFailure = true;
1821
- console.error(err instanceof Error ? err.message : String(err));
1896
+ recordFailure(err);
1822
1897
  }
1823
1898
  if (!runToken) {
1824
1899
  console.error("No run token \u2014 the OIDC exchange failed before QuikTrack could be reached.");
@@ -1855,7 +1930,7 @@ async function run(env2) {
1855
1930
  branch = result.branch;
1856
1931
  } catch (err) {
1857
1932
  hardFailure = true;
1858
- console.error(err instanceof Error ? err.message : String(err));
1933
+ recordFailure(err);
1859
1934
  }
1860
1935
  }
1861
1936
  await reportBack({
@@ -1866,7 +1941,8 @@ async function run(env2) {
1866
1941
  warnings,
1867
1942
  // Omitted when targeting itself never produced a result (nothing exists to record).
1868
1943
  targeting: targetingResult ? buildTargetingRecord(targetingResult, contextLog) : void 0,
1869
- baselineFindings
1944
+ baselineFindings,
1945
+ unresolved: failureReasons.length > 0 ? failureReasons : void 0
1870
1946
  });
1871
1947
  return { hardFailure };
1872
1948
  }