ccqa 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/ccqa.mjs +221 -59
- package/dist/hub-client/index.d.mts +8 -0
- package/dist/hub-client/index.mjs +2 -0
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/bin/ccqa.mjs
CHANGED
|
@@ -2425,22 +2425,22 @@ function toAgentBrowserArgs(action) {
|
|
|
2425
2425
|
if (!action.locator) return null;
|
|
2426
2426
|
return [
|
|
2427
2427
|
lit("select"),
|
|
2428
|
-
|
|
2428
|
+
val(locatorToSelector(action.locator)),
|
|
2429
2429
|
val(action.value ?? "")
|
|
2430
2430
|
];
|
|
2431
2431
|
case "drag":
|
|
2432
2432
|
if (!action.locator || !action.target) return null;
|
|
2433
2433
|
return [
|
|
2434
2434
|
lit("drag"),
|
|
2435
|
-
|
|
2436
|
-
|
|
2435
|
+
val(locatorToSelector(action.locator)),
|
|
2436
|
+
val(locatorToSelector(action.target))
|
|
2437
2437
|
];
|
|
2438
2438
|
case "upload": {
|
|
2439
2439
|
const files = action.files ?? [];
|
|
2440
2440
|
if (!action.locator || files.length === 0) return null;
|
|
2441
2441
|
return [
|
|
2442
2442
|
lit("upload"),
|
|
2443
|
-
|
|
2443
|
+
val(locatorToSelector(action.locator)),
|
|
2444
2444
|
...files.map(val)
|
|
2445
2445
|
];
|
|
2446
2446
|
}
|
|
@@ -2480,7 +2480,7 @@ function interactionToArgs(action) {
|
|
|
2480
2480
|
const abAction = action.action === "type" ? "fill" : action.action;
|
|
2481
2481
|
const takesInput = action.action === "fill" || action.action === "type";
|
|
2482
2482
|
if (!(loc.by !== "css" || action.index !== void 0 || action.action === "focus")) {
|
|
2483
|
-
const args = [lit(abAction),
|
|
2483
|
+
const args = [lit(abAction), val(loc.value)];
|
|
2484
2484
|
if (takesInput) args.push(val(action.value ?? ""));
|
|
2485
2485
|
return args;
|
|
2486
2486
|
}
|
|
@@ -2490,7 +2490,7 @@ function interactionToArgs(action) {
|
|
|
2490
2490
|
if (loc.by !== "css") return null;
|
|
2491
2491
|
if (action.index === "first" || action.index === "last") out.push(lit(action.index));
|
|
2492
2492
|
else out.push(lit("nth"), lit(String(action.index)));
|
|
2493
|
-
out.push(
|
|
2493
|
+
out.push(val(loc.value));
|
|
2494
2494
|
} else {
|
|
2495
2495
|
if (loc.by === "css") return null;
|
|
2496
2496
|
out.push(lit(loc.by), val(loc.value));
|
|
@@ -3016,6 +3016,7 @@ const ReportSpecResultSchema = z.object({
|
|
|
3016
3016
|
assertions: z.array(ReportAssertionSchema).nullable(),
|
|
3017
3017
|
analysis: FailureAnalysisSchema.nullable(),
|
|
3018
3018
|
analysisSkipped: z.string().nullable(),
|
|
3019
|
+
customPromptVersion: z.string().optional(),
|
|
3019
3020
|
analysisBase: z.object({
|
|
3020
3021
|
ref: z.string(),
|
|
3021
3022
|
sha: z.string()
|
|
@@ -3055,6 +3056,7 @@ const RunReportDataSchema = z.object({
|
|
|
3055
3056
|
kind: z.enum(["run", "drift"]).default("run"),
|
|
3056
3057
|
createdAt: z.string(),
|
|
3057
3058
|
runId: z.string().nullable(),
|
|
3059
|
+
runUrl: z.string().nullable().optional(),
|
|
3058
3060
|
git: GitEnvelopeSchema,
|
|
3059
3061
|
model: z.string().nullable(),
|
|
3060
3062
|
language: z.string().nullable().default(null),
|
|
@@ -3097,14 +3099,54 @@ const LabelsExportSchema = z.object({
|
|
|
3097
3099
|
* names and failure signals) lives here and on the hub — never hard-coded
|
|
3098
3100
|
* into ccqa itself.
|
|
3099
3101
|
*/
|
|
3102
|
+
/**
|
|
3103
|
+
* One per-target overlay: the same learned-note fields as the top-level, minus
|
|
3104
|
+
* `basePromptVersion` (shared across the whole document — the base analysis
|
|
3105
|
+
* prompt is target-agnostic).
|
|
3106
|
+
*/
|
|
3107
|
+
const AnalysisCustomPromptOverlaySchema = z.object({
|
|
3108
|
+
customPromptVersion: z.string(),
|
|
3109
|
+
generatedAt: z.string(),
|
|
3110
|
+
guidance: z.string()
|
|
3111
|
+
});
|
|
3100
3112
|
const AnalysisCustomPromptSchema = z.object({
|
|
3101
3113
|
schemaVersion: z.literal(1),
|
|
3102
3114
|
basePromptVersion: z.string(),
|
|
3103
3115
|
customPromptVersion: z.string(),
|
|
3104
3116
|
generatedAt: z.string(),
|
|
3105
|
-
guidance: z.string()
|
|
3117
|
+
guidance: z.string(),
|
|
3118
|
+
byTarget: z.record(z.string(), AnalysisCustomPromptOverlaySchema).optional()
|
|
3106
3119
|
});
|
|
3107
3120
|
/**
|
|
3121
|
+
* Lift one overlay into a standalone single-target `AnalysisCustomPrompt`: the
|
|
3122
|
+
* overlay's own note fields plus the document-wide `schemaVersion` /
|
|
3123
|
+
* `basePromptVersion`, and never a `byTarget` map. Passing the document itself
|
|
3124
|
+
* as the overlay yields the un-scoped top-level note as a clean single prompt.
|
|
3125
|
+
*/
|
|
3126
|
+
function overlayAsPrompt(base, overlay) {
|
|
3127
|
+
return {
|
|
3128
|
+
schemaVersion: base.schemaVersion,
|
|
3129
|
+
basePromptVersion: base.basePromptVersion,
|
|
3130
|
+
customPromptVersion: overlay.customPromptVersion,
|
|
3131
|
+
generatedAt: overlay.generatedAt,
|
|
3132
|
+
guidance: overlay.guidance
|
|
3133
|
+
};
|
|
3134
|
+
}
|
|
3135
|
+
/**
|
|
3136
|
+
* The effective single overlay for one target: its `byTarget` entry when it has
|
|
3137
|
+
* usable guidance, else the un-scoped top-level note when THAT has guidance,
|
|
3138
|
+
* else null. The returned value is a plain single-target `AnalysisCustomPrompt`
|
|
3139
|
+
* (no `byTarget`), so every downstream consumer — the prompt block and the
|
|
3140
|
+
* recorded `customPromptVersion` — sees exactly what was injected for the row.
|
|
3141
|
+
*/
|
|
3142
|
+
function resolveCustomPromptForTarget(cp, target) {
|
|
3143
|
+
if (!cp) return null;
|
|
3144
|
+
const scoped = cp.byTarget?.[target];
|
|
3145
|
+
if (scoped && scoped.guidance.trim()) return overlayAsPrompt(cp, scoped);
|
|
3146
|
+
if (cp.guidance.trim()) return overlayAsPrompt(cp, cp);
|
|
3147
|
+
return null;
|
|
3148
|
+
}
|
|
3149
|
+
/**
|
|
3108
3150
|
* Render the custom prompt as a prompt section, or "" when there's nothing to add.
|
|
3109
3151
|
* Returning "" for the empty/absent case is what keeps the base prompt
|
|
3110
3152
|
* byte-for-byte identical when no custom prompt is supplied (backward compatibility).
|
|
@@ -5694,6 +5736,7 @@ function createFailureAnalysisPass(deps) {
|
|
|
5694
5736
|
warnedDiffUnavailable = true;
|
|
5695
5737
|
info(`failure analysis: source diff unavailable (${specDiff.error}) — analyzing without diff context`);
|
|
5696
5738
|
}
|
|
5739
|
+
const customPrompt = resolveCustomPromptForTarget(deps.customPrompt, input.target);
|
|
5697
5740
|
const fields = {
|
|
5698
5741
|
analysis: null,
|
|
5699
5742
|
analysisSkipped: null,
|
|
@@ -5733,7 +5776,7 @@ function createFailureAnalysisPass(deps) {
|
|
|
5733
5776
|
...input.artifactsDir ? { artifactsDir: input.artifactsDir } : {},
|
|
5734
5777
|
...deps.language ? { outputLanguage: deps.language } : {},
|
|
5735
5778
|
...deps.triageUserPrompt ? { triageUserPrompt: deps.triageUserPrompt } : {},
|
|
5736
|
-
...
|
|
5779
|
+
...customPrompt ? { customPrompt } : {}
|
|
5737
5780
|
}, {
|
|
5738
5781
|
...deps.model ? { model: deps.model } : {},
|
|
5739
5782
|
cwd: deps.cwd,
|
|
@@ -5746,7 +5789,8 @@ function createFailureAnalysisPass(deps) {
|
|
|
5746
5789
|
printAnalysis(featureName, specName, outcome.analysis);
|
|
5747
5790
|
return {
|
|
5748
5791
|
...fields,
|
|
5749
|
-
analysis: outcome.analysis
|
|
5792
|
+
analysis: outcome.analysis,
|
|
5793
|
+
...customPrompt ? { customPromptVersion: customPrompt.customPromptVersion } : {}
|
|
5750
5794
|
};
|
|
5751
5795
|
} };
|
|
5752
5796
|
}
|
|
@@ -5849,6 +5893,7 @@ async function analyzeExternalRows(rows, run) {
|
|
|
5849
5893
|
readScript: () => readGeneratedTestSources(ref, deps.cwd),
|
|
5850
5894
|
failureLog: row.failureLogExcerpt ?? "",
|
|
5851
5895
|
specYaml: row.specYaml,
|
|
5896
|
+
target: row.target ?? "agent-browser",
|
|
5852
5897
|
driftIssues,
|
|
5853
5898
|
artifactsDir: readableArtifactsDir(ref, deps)
|
|
5854
5899
|
});
|
|
@@ -7967,7 +8012,8 @@ function analysisFieldsFor(a, status) {
|
|
|
7967
8012
|
analysisSkipped: a.analysisSkipped,
|
|
7968
8013
|
failureLogExcerpt: a.failureLogExcerpt,
|
|
7969
8014
|
diffExcerpt: a.diffExcerpt,
|
|
7970
|
-
...a.analysisBase ? { analysisBase: a.analysisBase } : {}
|
|
8015
|
+
...a.analysisBase ? { analysisBase: a.analysisBase } : {},
|
|
8016
|
+
...a.customPromptVersion ? { customPromptVersion: a.customPromptVersion } : {}
|
|
7971
8017
|
};
|
|
7972
8018
|
if (status === "failed") return { analysisSkipped: ANALYSIS_DISABLED };
|
|
7973
8019
|
return {};
|
|
@@ -8192,6 +8238,7 @@ async function analyzeOneLiveFailure(r, diffProvider, driftForSpec, auth, opts,
|
|
|
8192
8238
|
diffExcerpt: null
|
|
8193
8239
|
};
|
|
8194
8240
|
if (specDiff.error) info(`failure analysis: source diff unavailable (${specDiff.error}) — analyzing without diff context`);
|
|
8241
|
+
const customPrompt = resolveCustomPromptForTarget(opts.customPrompt, AGENT_BROWSER_TARGET);
|
|
8195
8242
|
const outcome = await analyzeFailure({
|
|
8196
8243
|
liveTranscriptExcerpt: excerpt,
|
|
8197
8244
|
specYaml: r.specYaml,
|
|
@@ -8203,7 +8250,7 @@ async function analyzeOneLiveFailure(r, diffProvider, driftForSpec, auth, opts,
|
|
|
8203
8250
|
driftIssues: driftForSpec,
|
|
8204
8251
|
...opts.language ? { outputLanguage: opts.language } : {},
|
|
8205
8252
|
...opts.triageUserPrompt ? { triageUserPrompt: opts.triageUserPrompt } : {},
|
|
8206
|
-
...
|
|
8253
|
+
...customPrompt ? { customPrompt } : {}
|
|
8207
8254
|
}, {
|
|
8208
8255
|
...opts.model ? { model: opts.model } : {},
|
|
8209
8256
|
cwd,
|
|
@@ -8220,7 +8267,8 @@ async function analyzeOneLiveFailure(r, diffProvider, driftForSpec, auth, opts,
|
|
|
8220
8267
|
analysisBase: {
|
|
8221
8268
|
ref: specDiff.base.ref,
|
|
8222
8269
|
sha: specDiff.base.sha
|
|
8223
|
-
}
|
|
8270
|
+
},
|
|
8271
|
+
...customPrompt ? { customPromptVersion: customPrompt.customPromptVersion } : {}
|
|
8224
8272
|
};
|
|
8225
8273
|
}
|
|
8226
8274
|
function count(steps, target) {
|
|
@@ -8536,27 +8584,27 @@ function actionToLine$1(action) {
|
|
|
8536
8584
|
if (val) assertLine = `abAssertNotVisible(${jExpr$1("text=" + val)}, 180_000);`;
|
|
8537
8585
|
break;
|
|
8538
8586
|
case "element_visible":
|
|
8539
|
-
if (sel) assertLine = `abAssertVisible(${
|
|
8587
|
+
if (sel) assertLine = `abAssertVisible(${jExpr$1(sel)});`;
|
|
8540
8588
|
break;
|
|
8541
8589
|
case "element_not_visible":
|
|
8542
|
-
if (sel) assertLine = `abAssertNotVisible(${
|
|
8590
|
+
if (sel) assertLine = `abAssertNotVisible(${jExpr$1(sel)});`;
|
|
8543
8591
|
break;
|
|
8544
8592
|
case "url_contains":
|
|
8545
8593
|
if (val) assertLine = `abAssertUrl(${jExpr$1(val)});`;
|
|
8546
8594
|
break;
|
|
8547
8595
|
case "element_enabled":
|
|
8548
8596
|
if (isStateSelector(sel)) return tautologicalStateAssertMarker(action, sel);
|
|
8549
|
-
if (sel && !sel.startsWith("text=") && !sel.startsWith("[aria-label=")) assertLine = `abAssertEnabled(${
|
|
8597
|
+
if (sel && !sel.startsWith("text=") && !sel.startsWith("[aria-label=")) assertLine = `abAssertEnabled(${jExpr$1(sel)});`;
|
|
8550
8598
|
break;
|
|
8551
8599
|
case "element_disabled":
|
|
8552
8600
|
if (isStateSelector(sel)) return tautologicalStateAssertMarker(action, sel);
|
|
8553
|
-
if (sel && !sel.startsWith("text=") && !sel.startsWith("[aria-label=")) assertLine = `abAssertDisabled(${
|
|
8601
|
+
if (sel && !sel.startsWith("text=") && !sel.startsWith("[aria-label=")) assertLine = `abAssertDisabled(${jExpr$1(sel)});`;
|
|
8554
8602
|
break;
|
|
8555
8603
|
case "element_checked":
|
|
8556
|
-
if (sel) assertLine = `abAssertChecked(${
|
|
8604
|
+
if (sel) assertLine = `abAssertChecked(${jExpr$1(sel)});`;
|
|
8557
8605
|
break;
|
|
8558
8606
|
case "element_unchecked":
|
|
8559
|
-
if (sel) assertLine = `abAssertUnchecked(${
|
|
8607
|
+
if (sel) assertLine = `abAssertUnchecked(${jExpr$1(sel)});`;
|
|
8560
8608
|
break;
|
|
8561
8609
|
}
|
|
8562
8610
|
if (comment && assertLine) return `${comment}\n ${assertLine}`;
|
|
@@ -8571,8 +8619,9 @@ function actionToLine$1(action) {
|
|
|
8571
8619
|
}
|
|
8572
8620
|
/**
|
|
8573
8621
|
* Render one argv token into TS source: env-expandable tokens (fill values,
|
|
8574
|
-
* URLs, find texts) become template literals via
|
|
8575
|
-
*
|
|
8622
|
+
* URLs, find texts, CSS/selector-engine strings) become template literals via
|
|
8623
|
+
* `jExpr` when they carry a `${VAR}` / `$VAR` ref; command words and flags are
|
|
8624
|
+
* plain string literals.
|
|
8576
8625
|
*/
|
|
8577
8626
|
function renderToken(token) {
|
|
8578
8627
|
return token.expandsEnv ? jExpr$1(token.text) : j$1(token.text);
|
|
@@ -8605,7 +8654,7 @@ const j$1 = (s) => JSON.stringify(s);
|
|
|
8605
8654
|
* emits them as `${process.env.VAR ?? ""}` template-literal substitutions
|
|
8606
8655
|
* instead of baking the literal `$VAR` string into the script. Used for
|
|
8607
8656
|
* values that came from a spec or block param: form fills, opened URLs,
|
|
8608
|
-
* assertion texts/URLs.
|
|
8657
|
+
* assertion texts/URLs, and selector strings carrying a `${RUN_ID}`-style ref.
|
|
8609
8658
|
*/
|
|
8610
8659
|
const jExpr$1 = (s) => envRefsToJsExpression(s);
|
|
8611
8660
|
//#endregion
|
|
@@ -9487,6 +9536,9 @@ function emitPlaywrightDraft(input) {
|
|
|
9487
9536
|
* Render a locator (plus positional pick) as a Playwright locator expression.
|
|
9488
9537
|
* Semantic strategies map 1:1 onto the getBy* family; `by: "css"` keeps its
|
|
9489
9538
|
* raw selector-engine string (locator() accepts `text=...` forms verbatim).
|
|
9539
|
+
* Every locator value — css included — goes through `jExpr`, so a `${VAR}` /
|
|
9540
|
+
* `$VAR` ref in a recorded selector expands to a `process.env` template
|
|
9541
|
+
* literal instead of baking the literal ref text into the selector.
|
|
9490
9542
|
*/
|
|
9491
9543
|
function locatorToPlaywright(locator, index) {
|
|
9492
9544
|
let expr;
|
|
@@ -9518,7 +9570,7 @@ function locatorToPlaywright(locator, index) {
|
|
|
9518
9570
|
expr = `page.getByTestId(${jExpr(locator.value)})`;
|
|
9519
9571
|
break;
|
|
9520
9572
|
case "css":
|
|
9521
|
-
expr = `page.locator(${
|
|
9573
|
+
expr = `page.locator(${jExpr(locator.value)})`;
|
|
9522
9574
|
break;
|
|
9523
9575
|
}
|
|
9524
9576
|
if (index === "first") return `${expr}.first()`;
|
|
@@ -10061,6 +10113,25 @@ function createIncrementalReport(reportDir, envelope, sink) {
|
|
|
10061
10113
|
};
|
|
10062
10114
|
}
|
|
10063
10115
|
//#endregion
|
|
10116
|
+
//#region src/run/github-run.ts
|
|
10117
|
+
/**
|
|
10118
|
+
* The GitHub Actions run URL for the current job, built from the standard
|
|
10119
|
+
* Actions environment variables. Returns null unless all three are present,
|
|
10120
|
+
* so nothing is ever invented for a local run — the same "only when in CI"
|
|
10121
|
+
* contract the report envelope's `runId` (GITHUB_RUN_ID) already follows.
|
|
10122
|
+
*/
|
|
10123
|
+
function githubRunUrl(env = process.env) {
|
|
10124
|
+
const server = env["GITHUB_SERVER_URL"];
|
|
10125
|
+
const repo = env["GITHUB_REPOSITORY"];
|
|
10126
|
+
const runId = githubRunId(env);
|
|
10127
|
+
if (!server || !repo || !runId) return null;
|
|
10128
|
+
return `${server}/${repo}/actions/runs/${runId}`;
|
|
10129
|
+
}
|
|
10130
|
+
/** The current GitHub Actions run id (GITHUB_RUN_ID); null outside Actions. */
|
|
10131
|
+
function githubRunId(env = process.env) {
|
|
10132
|
+
return env["GITHUB_RUN_ID"] ?? null;
|
|
10133
|
+
}
|
|
10134
|
+
//#endregion
|
|
10064
10135
|
//#region src/prompts/agent-update.ts
|
|
10065
10136
|
/**
|
|
10066
10137
|
* Build the prompts used by `--update-agent-prompt` to refresh
|
|
@@ -10582,11 +10653,15 @@ async function executeRun(targets, opts) {
|
|
|
10582
10653
|
let hubSink;
|
|
10583
10654
|
if (hubCtx != null && opts.pushReport) try {
|
|
10584
10655
|
const branch = await detectBranch(cwd);
|
|
10656
|
+
const ciRunId = githubRunId();
|
|
10657
|
+
const runUrl = githubRunUrl();
|
|
10585
10658
|
const opened = await hubCtx.hub.openRun({
|
|
10586
10659
|
project: hubCtx.project,
|
|
10587
10660
|
...branch ? { branch } : {},
|
|
10588
10661
|
...opts.profile ? { profile: opts.profile } : {},
|
|
10589
10662
|
...git.head ? { gitHead: git.head } : {},
|
|
10663
|
+
...ciRunId ? { ciRunId } : {},
|
|
10664
|
+
...runUrl ? { runUrl } : {},
|
|
10590
10665
|
kind: "run"
|
|
10591
10666
|
});
|
|
10592
10667
|
hubRunId = opened.id;
|
|
@@ -10932,6 +11007,7 @@ async function analyzeDeterministicSummaries(summaries, cwd, reportDir, { pass,
|
|
|
10932
11007
|
readScript: () => readScriptSafe(s.scriptFile),
|
|
10933
11008
|
failureLog,
|
|
10934
11009
|
specYaml,
|
|
11010
|
+
target: AGENT_BROWSER_TARGET,
|
|
10935
11011
|
driftIssues
|
|
10936
11012
|
});
|
|
10937
11013
|
results.push({
|
|
@@ -10940,6 +11016,7 @@ async function analyzeDeterministicSummaries(summaries, cwd, reportDir, { pass,
|
|
|
10940
11016
|
analysis: fields.analysis,
|
|
10941
11017
|
analysisSkipped: fields.analysisSkipped,
|
|
10942
11018
|
...fields.analysisBase ? { analysisBase: fields.analysisBase } : {},
|
|
11019
|
+
...fields.customPromptVersion ? { customPromptVersion: fields.customPromptVersion } : {},
|
|
10943
11020
|
driftIssues,
|
|
10944
11021
|
failureLogExcerpt: failureLog.length > 0 ? failureLog : null,
|
|
10945
11022
|
diffExcerpt: fields.diffExcerpt,
|
|
@@ -10958,11 +11035,13 @@ async function analyzeDeterministicSummaries(summaries, cwd, reportDir, { pass,
|
|
|
10958
11035
|
*/
|
|
10959
11036
|
function buildReportEnvelope(args) {
|
|
10960
11037
|
const { git, customPromptVersion, triageUserPromptHash, opts } = args;
|
|
11038
|
+
const runUrl = githubRunUrl();
|
|
10961
11039
|
return {
|
|
10962
11040
|
schemaVersion: 1,
|
|
10963
11041
|
kind: "run",
|
|
10964
11042
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10965
|
-
runId:
|
|
11043
|
+
runId: githubRunId(),
|
|
11044
|
+
...runUrl !== null ? { runUrl } : {},
|
|
10966
11045
|
git: {
|
|
10967
11046
|
head: git.head,
|
|
10968
11047
|
base: git.base?.ref ?? null,
|
|
@@ -14427,6 +14506,7 @@ z.object({
|
|
|
14427
14506
|
gitHead: z.string().nullable(),
|
|
14428
14507
|
promptVersion: z.string(),
|
|
14429
14508
|
ciRunId: z.string().nullable(),
|
|
14509
|
+
runUrl: z.string().nullable().optional(),
|
|
14430
14510
|
reportCreatedAt: z.string(),
|
|
14431
14511
|
createdAt: z.string()
|
|
14432
14512
|
});
|
|
@@ -14592,6 +14672,7 @@ function createPushRunHandler(config) {
|
|
|
14592
14672
|
gitHead: report.git.head,
|
|
14593
14673
|
promptVersion: report.promptVersion,
|
|
14594
14674
|
ciRunId: report.runId,
|
|
14675
|
+
runUrl: report.runUrl ?? null,
|
|
14595
14676
|
reportCreatedAt: report.createdAt,
|
|
14596
14677
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
14597
14678
|
};
|
|
@@ -14619,6 +14700,8 @@ function createOpenRunHandler(config) {
|
|
|
14619
14700
|
return async (ctx) => {
|
|
14620
14701
|
const { project, branch, profile, kind } = parseRunScope(ctx);
|
|
14621
14702
|
const gitHead = ctx.url.searchParams.get("gitHead");
|
|
14703
|
+
const ciRunId = ctx.url.searchParams.get("ciRunId");
|
|
14704
|
+
const runUrl = ctx.url.searchParams.get("runUrl");
|
|
14622
14705
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
14623
14706
|
const run = {
|
|
14624
14707
|
id: randomUUID(),
|
|
@@ -14635,7 +14718,8 @@ function createOpenRunHandler(config) {
|
|
|
14635
14718
|
},
|
|
14636
14719
|
gitHead: gitHead || null,
|
|
14637
14720
|
promptVersion: "",
|
|
14638
|
-
ciRunId: null,
|
|
14721
|
+
ciRunId: ciRunId || null,
|
|
14722
|
+
runUrl: runUrl || null,
|
|
14639
14723
|
reportCreatedAt: now,
|
|
14640
14724
|
createdAt: now
|
|
14641
14725
|
};
|
|
@@ -14654,6 +14738,7 @@ const PatchRunRequestSchema = z.object({
|
|
|
14654
14738
|
language: z.string().nullable().optional(),
|
|
14655
14739
|
promptVersion: z.string().optional(),
|
|
14656
14740
|
customPromptVersion: z.string().nullable().optional(),
|
|
14741
|
+
runUrl: z.string().nullable().optional(),
|
|
14657
14742
|
triageUserPromptHash: z.string().optional()
|
|
14658
14743
|
}).partial().optional()
|
|
14659
14744
|
});
|
|
@@ -14754,6 +14839,7 @@ function createPatchRunHandler(config) {
|
|
|
14754
14839
|
...reportMeta?.language !== void 0 ? { language: reportMeta.language } : {},
|
|
14755
14840
|
...reportMeta?.promptVersion !== void 0 ? { promptVersion: reportMeta.promptVersion } : {},
|
|
14756
14841
|
...reportMeta?.customPromptVersion !== void 0 ? { customPromptVersion: reportMeta.customPromptVersion } : {},
|
|
14842
|
+
...reportMeta?.runUrl !== void 0 ? { runUrl: reportMeta.runUrl } : {},
|
|
14757
14843
|
...reportMeta?.triageUserPromptHash !== void 0 ? { triageUserPromptHash: reportMeta.triageUserPromptHash } : {}
|
|
14758
14844
|
};
|
|
14759
14845
|
const merged = mergeResults(current?.results ?? [], rows);
|
|
@@ -15684,6 +15770,9 @@ ${HTML_BODY}
|
|
|
15684
15770
|
</body>
|
|
15685
15771
|
</html>`;
|
|
15686
15772
|
}
|
|
15773
|
+
function refreshButton(id) {
|
|
15774
|
+
return `<button class="btn ghost sm" id="${id}"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-3-6.7L21 8"/><path d="M21 3v5h-5"/></svg> <span data-i18n="common.refresh">Refresh</span></button>`;
|
|
15775
|
+
}
|
|
15687
15776
|
const HTML_BODY = `
|
|
15688
15777
|
<div id="login" class="login" hidden>
|
|
15689
15778
|
<div class="login-card">
|
|
@@ -15743,9 +15832,7 @@ const HTML_BODY = `
|
|
|
15743
15832
|
<div class="page-bar">
|
|
15744
15833
|
<h1 data-i18n="projects.title">Projects</h1>
|
|
15745
15834
|
<div class="spacer"></div>
|
|
15746
|
-
|
|
15747
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-3-6.7L21 8"/><path d="M21 3v5h-5"/></svg> Refresh
|
|
15748
|
-
</button>
|
|
15835
|
+
${refreshButton("projects-refresh")}
|
|
15749
15836
|
<button class="btn primary sm" id="projects-new">
|
|
15750
15837
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg> <span data-i18n="projects.new">New project</span>
|
|
15751
15838
|
</button>
|
|
@@ -15761,9 +15848,7 @@ const HTML_BODY = `
|
|
|
15761
15848
|
<div class="page-bar">
|
|
15762
15849
|
<h1 data-i18n="runs.title">Runs</h1>
|
|
15763
15850
|
<div class="spacer"></div>
|
|
15764
|
-
|
|
15765
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-3-6.7L21 8"/><path d="M21 3v5h-5"/></svg> Refresh
|
|
15766
|
-
</button>
|
|
15851
|
+
${refreshButton("runs-refresh")}
|
|
15767
15852
|
</div>
|
|
15768
15853
|
<div class="content">
|
|
15769
15854
|
<div class="card" id="runs-card">
|
|
@@ -15784,7 +15869,7 @@ const HTML_BODY = `
|
|
|
15784
15869
|
<h1 data-i18n="perspectives.title">Perspectives</h1>
|
|
15785
15870
|
<span class="updated" id="persp-updated"></span>
|
|
15786
15871
|
<div class="spacer"></div>
|
|
15787
|
-
|
|
15872
|
+
${refreshButton("persp-refresh")}
|
|
15788
15873
|
</div>
|
|
15789
15874
|
<div class="content">
|
|
15790
15875
|
<p id="persp-status" class="empty-note" hidden></p>
|
|
@@ -15854,7 +15939,7 @@ const HTML_BODY = `
|
|
|
15854
15939
|
<div class="page-bar">
|
|
15855
15940
|
<h1 data-i18n="learning.title">Learning</h1>
|
|
15856
15941
|
<div class="spacer"></div>
|
|
15857
|
-
|
|
15942
|
+
${refreshButton("jobs-refresh")}
|
|
15858
15943
|
</div>
|
|
15859
15944
|
<div class="content">
|
|
15860
15945
|
<p id="jobs-status" class="empty-note" hidden></p>
|
|
@@ -15877,7 +15962,7 @@ const HTML_BODY = `
|
|
|
15877
15962
|
<div class="proj-menu" id="sec-profile-menu" role="menu" hidden></div>
|
|
15878
15963
|
</div>
|
|
15879
15964
|
<div class="spacer"></div>
|
|
15880
|
-
|
|
15965
|
+
${refreshButton("sec-load")}
|
|
15881
15966
|
</div>
|
|
15882
15967
|
<div class="content">
|
|
15883
15968
|
<div class="scope-note">
|
|
@@ -15902,7 +15987,7 @@ const HTML_BODY = `
|
|
|
15902
15987
|
<div class="page-bar">
|
|
15903
15988
|
<h1 data-i18n="prompts.title">Prompts</h1>
|
|
15904
15989
|
<div class="spacer"></div>
|
|
15905
|
-
|
|
15990
|
+
${refreshButton("pr-load")}
|
|
15906
15991
|
</div>
|
|
15907
15992
|
<div class="content">
|
|
15908
15993
|
<p id="prompts-status" class="empty-note" hidden></p>
|
|
@@ -16143,6 +16228,8 @@ const CSS = `
|
|
|
16143
16228
|
.subline { margin-top: 3px; }
|
|
16144
16229
|
.ci-badge { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-family: var(--mono); color: var(--muted); background: var(--surface-3); border: 1px solid var(--border); border-radius: 5px; padding: 1px 6px; }
|
|
16145
16230
|
.ci-badge.local { color: var(--muted-2); }
|
|
16231
|
+
a.ci-badge { text-decoration: none; }
|
|
16232
|
+
a.ci-badge:hover { color: var(--fg); border-color: var(--fg-dim); }
|
|
16146
16233
|
|
|
16147
16234
|
.badge { display: inline-flex; align-items: center; gap: 5px; padding: 2px 8px 2px 7px; border-radius: var(--radius-sm); font-size: 12px; font-weight: 500; border: 1px solid transparent; }
|
|
16148
16235
|
.badge .d { width: 6px; height: 6px; border-radius: 50%; }
|
|
@@ -16606,6 +16693,7 @@ const CLIENT_JS = `
|
|
|
16606
16693
|
"prompt.runnAgent.hint": "Notes ccqa keeps for itself while generating runn runbooks, refined automatically. Read-only — ccqa regenerates it.",
|
|
16607
16694
|
"prompt.triageUser.hint": "Rules you write for how failure causes are classified — e.g. which kinds of changes count as a spec change on this project. Applied on every failure analysis.",
|
|
16608
16695
|
"prompt.customPrompt.hint": "Learned from your triage grades to make ccqa classify failure causes the way you do. Read-only — a learning job creates it.",
|
|
16696
|
+
"prompt.customPrompt.fallback": "Un-scoped (fallback)",
|
|
16609
16697
|
"prompt.readonly": "read-only",
|
|
16610
16698
|
"prompt.notSet": "Not set. Type guidance and Save to store it on the hub.",
|
|
16611
16699
|
"prompt.notSetRo": "Not set yet — ccqa fills this in as it runs.",
|
|
@@ -16623,7 +16711,7 @@ const CLIENT_JS = `
|
|
|
16623
16711
|
"jobs.failed": "The learning job failed.", "jobs.newCustomPrompt": "New custom prompt:", "jobs.empty": "No learning jobs yet. Grade failing specs on a run, then Learn."
|
|
16624
16712
|
},
|
|
16625
16713
|
ja: {
|
|
16626
|
-
"nav.projects": "プロジェクト", "nav.runs": "実行", "nav.perspectives": "
|
|
16714
|
+
"nav.projects": "プロジェクト", "nav.runs": "実行", "nav.perspectives": "テスト観点", "nav.secrets": "シークレット",
|
|
16627
16715
|
"nav.prompts": "プロンプト", "nav.learning": "学習",
|
|
16628
16716
|
"app.project": "プロジェクト", "app.profile": "プロファイル", "app.disconnect": "切断", "app.noProject": "プロジェクト未選択",
|
|
16629
16717
|
"app.newProfile": "新規プロファイル",
|
|
@@ -16666,15 +16754,15 @@ const CLIENT_JS = `
|
|
|
16666
16754
|
"learn.cta.desc": "採点した内容をもとに、ccqaが次回から同じように失敗の原因を分類できるよう学習します。",
|
|
16667
16755
|
"learn.cta.run": "学習",
|
|
16668
16756
|
"secrets.title": "シークレット", "prompts.title": "プロンプト", "learning.title": "学習",
|
|
16669
|
-
"perspectives.title": "
|
|
16757
|
+
"perspectives.title": "テスト観点",
|
|
16670
16758
|
"perspectives.search": "ケースを検索…",
|
|
16671
16759
|
"perspectives.filter.all": "すべて", "perspectives.filter.deterministic": "決定的",
|
|
16672
16760
|
"perspectives.filter.live": "ライブ", "perspectives.filter.norec": "未recordのみ",
|
|
16673
16761
|
"perspectives.col.case": "ケース", "perspectives.col.mode": "モード", "perspectives.col.status": "状態",
|
|
16674
16762
|
"perspectives.noHit": "該当するケースがありません。",
|
|
16675
16763
|
"perspectives.updated": "最終更新:",
|
|
16676
|
-
"perspectives.empty": "
|
|
16677
|
-
"perspectives.loadFailed": "
|
|
16764
|
+
"perspectives.empty": "まだテスト観点がありません。ccqa perspectives を実行するか、recordすると自動作成されます。",
|
|
16765
|
+
"perspectives.loadFailed": "テスト観点の読み込みに失敗しました",
|
|
16678
16766
|
"perspectives.mode.deterministic": "決定的", "perspectives.mode.live": "ライブ",
|
|
16679
16767
|
"perspectives.status.runnable": "実行可能", "perspectives.status.notRecorded": "未record",
|
|
16680
16768
|
"perspectives.metric.features": "機能", "perspectives.metric.cases": "テストケース",
|
|
@@ -16703,6 +16791,7 @@ const CLIENT_JS = `
|
|
|
16703
16791
|
"prompt.runnAgent.hint": "runnランブック生成中にccqaが自分用に書き留め、自動で洗練していくメモです。読み取り専用 — ccqaが再生成します。",
|
|
16704
16792
|
"prompt.triageUser.hint": "失敗原因を分類するときのルールを自分で書きます(例: どの変更をこのプロジェクトで仕様変更として扱うか)。失敗分析のたびに適用されます。",
|
|
16705
16793
|
"prompt.customPrompt.hint": "あなたの採点から学習し、ccqaがあなたと同じように失敗の原因を分類できるようにします。読み取り専用 — 学習ジョブが生成します。",
|
|
16794
|
+
"prompt.customPrompt.fallback": "共通(フォールバック)",
|
|
16706
16795
|
"prompt.readonly": "読み取り専用",
|
|
16707
16796
|
"prompt.notSet": "未設定。指示を入力して保存するとハブに保存されます。",
|
|
16708
16797
|
"prompt.notSetRo": "未設定 — ccqaが実行しながら自動で書き込みます。",
|
|
@@ -16910,9 +16999,21 @@ const CLIENT_JS = `
|
|
|
16910
16999
|
}
|
|
16911
17000
|
|
|
16912
17001
|
function ciBadge(run) {
|
|
16913
|
-
|
|
16914
|
-
|
|
16915
|
-
|
|
17002
|
+
if (!run.ciRunId) return el("span", "ci-badge local", "local run");
|
|
17003
|
+
var text = "Actions #" + run.ciRunId;
|
|
17004
|
+
// A link to the GitHub Actions run when the URL was recorded; same chip
|
|
17005
|
+
// style otherwise (plain text).
|
|
17006
|
+
if (run.runUrl) {
|
|
17007
|
+
var a = el("a", "ci-badge", text);
|
|
17008
|
+
a.href = run.runUrl;
|
|
17009
|
+
a.target = "_blank";
|
|
17010
|
+
a.rel = "noopener";
|
|
17011
|
+
// The runs-list row is itself clickable (opens the run); opening the CI
|
|
17012
|
+
// link must not also navigate the row.
|
|
17013
|
+
a.addEventListener("click", function (e) { e.stopPropagation(); });
|
|
17014
|
+
return a;
|
|
17015
|
+
}
|
|
17016
|
+
return el("span", "ci-badge", text);
|
|
16916
17017
|
}
|
|
16917
17018
|
|
|
16918
17019
|
function labelChip(label) {
|
|
@@ -18636,13 +18737,29 @@ const CLIENT_JS = `
|
|
|
18636
18737
|
}
|
|
18637
18738
|
|
|
18638
18739
|
// The custom prompt body is JSON (schemaVersion/basePromptVersion/customPromptVersion/
|
|
18639
|
-
// generatedAt/guidance
|
|
18640
|
-
//
|
|
18641
|
-
//
|
|
18740
|
+
// generatedAt/guidance, plus an optional per-target byTarget map); the textarea
|
|
18741
|
+
// only ever shows the learned guidance text, never the raw JSON. When byTarget
|
|
18742
|
+
// is present, the slot shows the un-scoped fallback (when it has guidance) plus
|
|
18743
|
+
// each per-target overlay under a short header, so it reflects the whole learned
|
|
18744
|
+
// set. A parse failure falls back to the raw text so a malformed custom prompt
|
|
18745
|
+
// still shows something instead of leaving the UI stuck.
|
|
18642
18746
|
function customPromptDisplayText(text) {
|
|
18643
18747
|
if (text == null) return "";
|
|
18748
|
+
var NL = "\\n";
|
|
18644
18749
|
try {
|
|
18645
18750
|
var parsed = JSON.parse(text);
|
|
18751
|
+
var byTarget = parsed && parsed.byTarget;
|
|
18752
|
+
if (byTarget && typeof byTarget === "object") {
|
|
18753
|
+
var parts = [];
|
|
18754
|
+
var top = typeof parsed.guidance === "string" ? parsed.guidance.trim() : "";
|
|
18755
|
+
if (top) parts.push("[" + t("prompt.customPrompt.fallback") + "]" + NL + top);
|
|
18756
|
+
Object.keys(byTarget).sort().forEach(function (tg) {
|
|
18757
|
+
var entry = byTarget[tg];
|
|
18758
|
+
var g = entry && typeof entry.guidance === "string" ? entry.guidance.trim() : "";
|
|
18759
|
+
if (g) parts.push("[" + tg + "]" + NL + g);
|
|
18760
|
+
});
|
|
18761
|
+
if (parts.length) return parts.join(NL + NL);
|
|
18762
|
+
}
|
|
18646
18763
|
return typeof parsed.guidance === "string" ? parsed.guidance : text;
|
|
18647
18764
|
} catch (e) {
|
|
18648
18765
|
// A malformed custom prompt shouldn't blank the panel; show the raw body but
|
|
@@ -19268,6 +19385,22 @@ function evidenceSignalFor(headline, note) {
|
|
|
19268
19385
|
}
|
|
19269
19386
|
function createLearningWorker(deps) {
|
|
19270
19387
|
const { storage, invoke = invokeClaudeStreaming, authCheck = driftAuthAvailable } = deps;
|
|
19388
|
+
/**
|
|
19389
|
+
* One Claude call turning a batch of graded cases into a calibration note.
|
|
19390
|
+
* Returns null when nothing usable came back so the caller can drop that
|
|
19391
|
+
* group's overlay without failing the whole job.
|
|
19392
|
+
*/
|
|
19393
|
+
const learnGuidance = async (cases) => {
|
|
19394
|
+
const { result, isError } = await invoke({
|
|
19395
|
+
prompt: buildLearningUserPrompt(cases.slice(0, LEARNING_MAX_CASES)),
|
|
19396
|
+
systemPrompt: LEARNING_SYSTEM_PROMPT,
|
|
19397
|
+
allowedTools: [],
|
|
19398
|
+
disableBuiltinTools: true,
|
|
19399
|
+
maxTurns: 1
|
|
19400
|
+
}, () => {});
|
|
19401
|
+
const guidance = result?.trim();
|
|
19402
|
+
return isError || !guidance ? null : guidance;
|
|
19403
|
+
};
|
|
19271
19404
|
return async function runLearningJob(job) {
|
|
19272
19405
|
const auth = authCheck();
|
|
19273
19406
|
if (!auth.ok) throw new Error(`triage learning needs Claude auth on the hub: ${auth.reason}`);
|
|
@@ -19285,7 +19418,8 @@ function createLearningWorker(deps) {
|
|
|
19285
19418
|
predicted: r.predicted.label,
|
|
19286
19419
|
actualCause: actual,
|
|
19287
19420
|
evidenceSignal: evidenceSignalFor(r.predicted.headline, r.note),
|
|
19288
|
-
matches: r.predicted.label === actual
|
|
19421
|
+
matches: r.predicted.label === actual,
|
|
19422
|
+
...r.target ? { target: r.target } : {}
|
|
19289
19423
|
});
|
|
19290
19424
|
}
|
|
19291
19425
|
}
|
|
@@ -19294,32 +19428,47 @@ function createLearningWorker(deps) {
|
|
|
19294
19428
|
runLimit,
|
|
19295
19429
|
casesConsidered: cases.length
|
|
19296
19430
|
} });
|
|
19297
|
-
const
|
|
19298
|
-
|
|
19299
|
-
|
|
19300
|
-
|
|
19301
|
-
|
|
19302
|
-
|
|
19303
|
-
|
|
19304
|
-
|
|
19305
|
-
|
|
19306
|
-
|
|
19431
|
+
const fallbackCases = [];
|
|
19432
|
+
const targetCases = /* @__PURE__ */ new Map();
|
|
19433
|
+
for (const c of cases) {
|
|
19434
|
+
if (!c.target) {
|
|
19435
|
+
fallbackCases.push(c);
|
|
19436
|
+
continue;
|
|
19437
|
+
}
|
|
19438
|
+
const list = targetCases.get(c.target) ?? [];
|
|
19439
|
+
list.push(c);
|
|
19440
|
+
targetCases.set(c.target, list);
|
|
19441
|
+
}
|
|
19307
19442
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
19443
|
+
const sortedTargets = [...targetCases.keys()].sort((a, b) => a.localeCompare(b));
|
|
19444
|
+
const [fallbackGuidance, ...targetGuidances] = await Promise.all([fallbackCases.length > 0 ? learnGuidance(fallbackCases) : Promise.resolve(null), ...sortedTargets.map((target) => learnGuidance(targetCases.get(target)))]);
|
|
19445
|
+
const byTarget = {};
|
|
19446
|
+
sortedTargets.forEach((target, i) => {
|
|
19447
|
+
const guidance = targetGuidances[i];
|
|
19448
|
+
if (guidance) byTarget[target] = {
|
|
19449
|
+
customPromptVersion: `${generatedAt}-${target}-c${targetCases.get(target).length}`,
|
|
19450
|
+
generatedAt,
|
|
19451
|
+
guidance
|
|
19452
|
+
};
|
|
19453
|
+
});
|
|
19454
|
+
if (!fallbackGuidance && Object.keys(byTarget).length === 0) throw new Error("triage learning: Claude returned no usable calibration note");
|
|
19455
|
+
const prevCustomPrompt = await loadStoredCustomPrompt(storage, job.project);
|
|
19308
19456
|
const customPrompt = {
|
|
19309
19457
|
schemaVersion: 1,
|
|
19310
19458
|
basePromptVersion: "7",
|
|
19311
|
-
customPromptVersion: `${generatedAt}-c${
|
|
19459
|
+
customPromptVersion: `${generatedAt}-c${fallbackCases.length}`,
|
|
19312
19460
|
generatedAt,
|
|
19313
|
-
guidance
|
|
19461
|
+
guidance: fallbackGuidance ?? "",
|
|
19462
|
+
...Object.keys(byTarget).length > 0 ? { byTarget } : {}
|
|
19314
19463
|
};
|
|
19315
19464
|
AnalysisCustomPromptSchema.parse(customPrompt);
|
|
19316
19465
|
const beforePrompt = buildFailureAnalysisPrompt({
|
|
19317
19466
|
...PROMPT_PREVIEW_FIXTURE,
|
|
19318
|
-
customPrompt: prevCustomPrompt
|
|
19467
|
+
customPrompt: representativeOverlay(prevCustomPrompt)
|
|
19319
19468
|
});
|
|
19320
19469
|
const afterPrompt = buildFailureAnalysisPrompt({
|
|
19321
19470
|
...PROMPT_PREVIEW_FIXTURE,
|
|
19322
|
-
customPrompt
|
|
19471
|
+
customPrompt: representativeOverlay(customPrompt)
|
|
19323
19472
|
});
|
|
19324
19473
|
await storage.prompts.put(job.project, "analysis-custom-prompt", new TextEncoder().encode(JSON.stringify(customPrompt)), {
|
|
19325
19474
|
customPromptVersion: customPrompt.customPromptVersion,
|
|
@@ -19336,6 +19485,19 @@ function createLearningWorker(deps) {
|
|
|
19336
19485
|
});
|
|
19337
19486
|
};
|
|
19338
19487
|
}
|
|
19488
|
+
/**
|
|
19489
|
+
* A single representative overlay for the before/after prompt preview: the
|
|
19490
|
+
* un-scoped fallback when it has guidance, else the first target overlay by
|
|
19491
|
+
* name, else null. Only the preview uses this — the stored blob keeps every
|
|
19492
|
+
* overlay; run-time injection picks per target (resolveCustomPromptForTarget).
|
|
19493
|
+
*/
|
|
19494
|
+
function representativeOverlay(cp) {
|
|
19495
|
+
if (!cp) return null;
|
|
19496
|
+
if (cp.guidance.trim()) return overlayAsPrompt(cp, cp);
|
|
19497
|
+
const firstTarget = cp.byTarget ? Object.keys(cp.byTarget).sort()[0] : void 0;
|
|
19498
|
+
const overlay = firstTarget ? cp.byTarget?.[firstTarget] : void 0;
|
|
19499
|
+
return overlay ? overlayAsPrompt(cp, overlay) : null;
|
|
19500
|
+
}
|
|
19339
19501
|
/** Read the currently-stored custom prompt, or null when there is none / it's unreadable. */
|
|
19340
19502
|
async function loadStoredCustomPrompt(storage, project) {
|
|
19341
19503
|
const entry = await storage.prompts.get(project, "analysis-custom-prompt");
|
|
@@ -52,6 +52,7 @@ declare const RunSchema: z.ZodObject<{
|
|
|
52
52
|
gitHead: z.ZodNullable<z.ZodString>;
|
|
53
53
|
promptVersion: z.ZodString;
|
|
54
54
|
ciRunId: z.ZodNullable<z.ZodString>;
|
|
55
|
+
runUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
55
56
|
reportCreatedAt: z.ZodString;
|
|
56
57
|
createdAt: z.ZodString;
|
|
57
58
|
}, z.core.$strip>;
|
|
@@ -217,6 +218,7 @@ declare const ReportSpecResultSchema: z.ZodObject<{
|
|
|
217
218
|
reasoning: z.ZodString;
|
|
218
219
|
}, z.core.$strip>>;
|
|
219
220
|
analysisSkipped: z.ZodNullable<z.ZodString>;
|
|
221
|
+
customPromptVersion: z.ZodOptional<z.ZodString>;
|
|
220
222
|
analysisBase: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
221
223
|
ref: z.ZodString;
|
|
222
224
|
sha: z.ZodString;
|
|
@@ -319,6 +321,7 @@ declare const RunReportDataSchema: z.ZodObject<{
|
|
|
319
321
|
}>>;
|
|
320
322
|
createdAt: z.ZodString;
|
|
321
323
|
runId: z.ZodNullable<z.ZodString>;
|
|
324
|
+
runUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
322
325
|
git: z.ZodObject<{
|
|
323
326
|
head: z.ZodNullable<z.ZodString>;
|
|
324
327
|
base: z.ZodNullable<z.ZodString>;
|
|
@@ -384,6 +387,7 @@ declare const RunReportDataSchema: z.ZodObject<{
|
|
|
384
387
|
reasoning: z.ZodString;
|
|
385
388
|
}, z.core.$strip>>;
|
|
386
389
|
analysisSkipped: z.ZodNullable<z.ZodString>;
|
|
390
|
+
customPromptVersion: z.ZodOptional<z.ZodString>;
|
|
387
391
|
analysisBase: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
388
392
|
ref: z.ZodString;
|
|
389
393
|
sha: z.ZodString;
|
|
@@ -584,6 +588,10 @@ interface HubClient {
|
|
|
584
588
|
profile?: string;
|
|
585
589
|
kind?: "run" | "drift";
|
|
586
590
|
gitHead?: string;
|
|
591
|
+
/** CI run id (GITHUB_RUN_ID) and its run URL, stamped at open time so an
|
|
592
|
+
* interrupted incremental run still links back to its CI run. */
|
|
593
|
+
ciRunId?: string;
|
|
594
|
+
runUrl?: string;
|
|
587
595
|
}): Promise<Run>;
|
|
588
596
|
/** Add finished spec rows (+ evidence) to a running run; `done` closes it. */
|
|
589
597
|
patchRun(id: string, body: PatchRunRequest): Promise<Run>;
|
|
@@ -110,6 +110,8 @@ function createHubClient(opts) {
|
|
|
110
110
|
if (meta.profile) params.set("profile", meta.profile);
|
|
111
111
|
if (meta.kind) params.set("kind", meta.kind);
|
|
112
112
|
if (meta.gitHead) params.set("gitHead", meta.gitHead);
|
|
113
|
+
if (meta.ciRunId) params.set("ciRunId", meta.ciRunId);
|
|
114
|
+
if (meta.runUrl) params.set("runUrl", meta.runUrl);
|
|
113
115
|
return json(`/api/v1/runs/open?${params}`, { method: "POST" });
|
|
114
116
|
},
|
|
115
117
|
patchRun(id, body) {
|
package/dist/package.json
CHANGED