pi-smart-compact 9.6.0 → 9.6.2
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/ARCHITECTURE.md +28 -14
- package/CHANGELOG.md +46 -0
- package/README.md +30 -10
- package/dist/app/mode-policy.d.ts +3 -1
- package/dist/app/mode-policy.d.ts.map +1 -1
- package/dist/app/preflight.d.ts.map +1 -1
- package/dist/app/register-smart-compact-tool.d.ts.map +1 -1
- package/dist/app/run-context.d.ts +3 -3
- package/dist/app/run-context.d.ts.map +1 -1
- package/dist/app/run-smart-compact.d.ts.map +1 -1
- package/dist/app/steps/extract.d.ts.map +1 -1
- package/dist/app/steps/prepare.d.ts.map +1 -1
- package/dist/app/steps/state.d.ts.map +1 -1
- package/dist/app/steps/synthesize.d.ts.map +1 -1
- package/dist/app/steps/tier.d.ts +5 -4
- package/dist/app/steps/tier.d.ts.map +1 -1
- package/dist/app/steps/window.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/domain/provider-evaluation.d.ts.map +1 -1
- package/dist/domain/telemetry.d.ts.map +1 -1
- package/dist/domain/tool-semantics.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1489 -1248
- package/dist/infra/ai-messages.d.ts +6 -2
- package/dist/infra/ai-messages.d.ts.map +1 -1
- package/dist/phases/explore.d.ts.map +1 -1
- package/dist/phases/synthesize.d.ts +5 -0
- package/dist/phases/synthesize.d.ts.map +1 -1
- package/dist/phases/verify.d.ts.map +1 -1
- package/dist/provider-eval.js +348 -22
- package/dist/provider-scenario-eval.js +363 -33
- package/dist/telemetry-report.js +125 -22
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/error-format.d.ts +3 -1
- package/dist/ui/error-format.d.ts.map +1 -1
- package/dist/ui/metrics-report.d.ts.map +1 -1
- package/dist/ui/overlays.d.ts.map +1 -1
- package/dist/utils/cache.d.ts.map +1 -1
- package/dist/utils/file-needles.d.ts +24 -0
- package/dist/utils/file-needles.d.ts.map +1 -1
- package/dist/utils/file-ref-detect.d.ts +6 -1
- package/dist/utils/file-ref-detect.d.ts.map +1 -1
- package/dist/utils/helpers.d.ts +1 -0
- package/dist/utils/helpers.d.ts.map +1 -1
- package/dist/utils/session-log.d.ts.map +1 -1
- package/package.json +4 -2
|
@@ -15,9 +15,13 @@
|
|
|
15
15
|
* conversation buffer in native `Message[]` directly (see `phases/explore.ts`),
|
|
16
16
|
* which is the preferred pattern for any new code that talks to a provider.
|
|
17
17
|
*/
|
|
18
|
-
import type
|
|
19
|
-
import type { LlmMessage } from "../types.ts";
|
|
18
|
+
import { type Message } from "@earendil-works/pi-ai";
|
|
19
|
+
import type { LlmMessage, SessionMessageEntry } from "../types.ts";
|
|
20
20
|
import type { SecretScrubber } from "../domain/scrub.ts";
|
|
21
|
+
/** Preserve the host's context projection and original entry IDs, including custom and branch summaries. */
|
|
22
|
+
export declare function contextMessageEntries(entries: readonly unknown[]): SessionMessageEntry[];
|
|
23
|
+
/** Text transcript without the host summarizer's implicit 2,000-character tool-result cap. */
|
|
24
|
+
export declare function serializeConversationText(messages: LlmMessage[]): string;
|
|
21
25
|
/**
|
|
22
26
|
* Upcast a raw branch entry's `message` to a `Message` for `convertToLlm`.
|
|
23
27
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-messages.d.ts","sourceRoot":"","sources":["../../src/infra/ai-messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"ai-messages.d.ts","sourceRoot":"","sources":["../../src/infra/ai-messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAe,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,4GAA4G;AAC5G,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,GAAG,mBAAmB,EAAE,CAKxF;AAED,8FAA8F;AAC9F,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,MAAM,CAKxE;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEzD;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,CAIpE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,cAAc,GAAG,UAAU,EAAE,CAE3F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explore.d.ts","sourceRoot":"","sources":["../../src/phases/explore.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,KAAK,EACL,GAAG,EAKH,eAAe,EAChB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EACV,UAAU,EACV,oBAAoB,EACpB,iBAAiB,EAElB,MAAM,aAAa,CAAC;AAcrB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAIjE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAuBpD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,oBAAoB,GAAG,OAAO,CAqBvE;
|
|
1
|
+
{"version":3,"file":"explore.d.ts","sourceRoot":"","sources":["../../src/phases/explore.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,KAAK,EACL,GAAG,EAKH,eAAe,EAChB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EACV,UAAU,EACV,oBAAoB,EACpB,iBAAiB,EAElB,MAAM,aAAa,CAAC;AAcrB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAIjE,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAuBpD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,oBAAoB,GAAG,OAAO,CAqBvE;AAsHD,wBAAgB,sBAAsB,CACpC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAC1D,WAAW,EAAE,UAAU,EAAE,EACzB,QAAQ,iBAAuB,GAC9B,MAAM,CA6MR;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,UAAU,EAAE,GACxB,iBAAiB,CAkDnB;AA6DD,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,OAAO,EACf,WAAW,EAAE,UAAU,EAAE,GACxB,iBAAiB,CAgCnB;AAED,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,UAAU,EAAE,GACxB,iBAAiB,CAWnB;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,UAAU,EAAE,EACzB,UAAU,EAAE,oBAAoB,EAChC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,EACnD,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,MAAM,CAAC,EAAE,WAAW,EACpB,SAAS,SAAyB,EAClC,MAAM,CAAC,EAAE,CACP,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,KAC5C,IAAI,EACT,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC;IACT,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC,CAkTD"}
|
|
@@ -5,6 +5,11 @@ import type { Model, Api, ProviderHeaders } from "@earendil-works/pi-ai";
|
|
|
5
5
|
import type { LlmMessage, LlmChunk, ChunkSummary, StructuredExtraction, ExplorationReport, ProfileConfig, CompactionState } from "../types.ts";
|
|
6
6
|
import { type TokenEstimator } from "../utils/tokens.ts";
|
|
7
7
|
import type { SmartCompactServices } from "../infra/services.ts";
|
|
8
|
+
/** A provider response completed, but did not satisfy the batch-output contract. */
|
|
9
|
+
export declare class BatchSummaryFormatError extends Error {
|
|
10
|
+
readonly name = "BatchSummaryFormatError";
|
|
11
|
+
constructor(reason: string);
|
|
12
|
+
}
|
|
8
13
|
export declare function chunkLlmMessages(msgs: LlmMessage[], boundaries: import("../types.ts").TopicBoundary[], pc: ProfileConfig, estimator?: TokenEstimator, focus?: string): LlmChunk[];
|
|
9
14
|
export declare function singlePassCompact(convText: string, extraction: StructuredExtraction, report: ExplorationReport | null, prevContext: string, model: Model<Api>, auth: {
|
|
10
15
|
apiKey: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"synthesize.d.ts","sourceRoot":"","sources":["../../src/phases/synthesize.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EACX,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,MAAM,aAAa,CAAC;AAYrB,OAAO,EAGN,KAAK,cAAc,EACnB,MAAM,oBAAoB,CAAC;AAU5B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"synthesize.d.ts","sourceRoot":"","sources":["../../src/phases/synthesize.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EACX,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,MAAM,aAAa,CAAC;AAYrB,OAAO,EAGN,KAAK,cAAc,EACnB,MAAM,oBAAoB,CAAC;AAU5B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AA0BjE,oFAAoF;AACpF,qBAAa,uBAAwB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,6BAA6B;IAE1C,YAAY,MAAM,EAAE,MAAM,EAEzB;CACD;AAoRD,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE,UAAU,EAAE,EAClB,UAAU,EAAE,OAAO,aAAa,EAAE,aAAa,EAAE,EACjD,EAAE,EAAE,aAAa,EACjB,SAAS,GAAE,cAAqC,EAChD,KAAK,CAAC,EAAE,MAAM,GACZ,QAAQ,EAAE,CAwGZ;AAED,wBAAsB,iBAAiB,CACtC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,oBAAoB,EAChC,MAAM,EAAE,iBAAiB,GAAG,IAAI,EAChC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,EACnD,YAAY,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,oBAAoB,EAC/B,KAAK,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,CAAC,CAAA;CAAE,CAAC,CA2D3C;AAED,wBAAsB,cAAc,CACnC,KAAK,EAAE,QAAQ,EAAE,EACjB,UAAU,EAAE,oBAAoB,EAChC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,EACnD,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,oBAAoB,EAC/B,eAAe,CAAC,EAAE,MAAM,EACxB,UAAU,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAyKzB;AA6BD,wBAAsB,WAAW,CAChC,SAAS,EAAE,YAAY,EAAE,EACzB,UAAU,EAAE,oBAAoB,EAChC,MAAM,EAAE,iBAAiB,GAAG,IAAI,EAChC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,EACnD,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,oBAAoB,EAC/B,KAAK,CAAC,EAAE,MAAM,EACd,UAAU,GAAE,eAAe,GAAG,IAAW,GACvC,OAAO,CAAC,MAAM,CAAC,CAmDjB;AAED,wBAAgB,gBAAgB,CAC/B,SAAS,EAAE,YAAY,EAAE,EACzB,UAAU,EAAE,oBAAoB,EAChC,QAAQ,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,EAChD,YAAY,GAAE,MAAc,EAC5B,UAAU,GAAE,eAAe,GAAG,IAAW,GACvC,MAAM,CAwIR;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,GAAG,YAAY,CAiB7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/phases/verify.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACX,KAAK,EACL,GAAG,EACH,eAAe,EAEf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EACX,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,qBAAqB,EACrB,kBAAkB,EAClB,UAAU,EACV,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/phases/verify.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACX,KAAK,EACL,GAAG,EACH,eAAe,EAEf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EACX,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,qBAAqB,EACrB,kBAAkB,EAClB,UAAU,EACV,MAAM,aAAa,CAAC;AAoCrB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,MAAM,WAAW,oBAAoB;IACpC,cAAc,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IACvC,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAiTD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAkClE;AAED,wBAAgB,0BAA0B,CACzC,MAAM,EAAE,kBAAkB,GACxB,MAAM,GAAG,IAAI,CAcf;AAED,wFAAwF;AACxF,qBAAa,qBAAsB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,YACC,MAAM,EAAE,kBAAkB,EAC1B,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,qBAAqB,EAW5B;CACD;AA4UD,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAK1E;AAED,qGAAqG;AACrG,wBAAgB,8BAA8B,CAC7C,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,kBAAkB,EAC1B,UAAU,EAAE,oBAAoB,EAChC,UAAU,GAAE,eAAe,GAAG,IAAW,EACzC,QAAQ,GAAE,oBAAyB,EACnC,SAAS,SAAI,GACX;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,eAAe,EAAE,CAAA;CAAE,CAyB7E;AAkYD,wBAAgB,aAAa,CAC5B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,oBAAoB,EAChC,UAAU,GAAE,eAAe,GAAG,IAAW,EACzC,QAAQ,GAAE,oBAAyB,GACjC,kBAAkB,CA4CpB;AAED,iHAAiH;AACjH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,eAAe,EAAE,EACvB,UAAU,EAAE,oBAAoB,EAChC,UAAU,GAAE,eAAe,GAAG,IAAW,EACzC,QAAQ,GAAE,oBAAyB,GACjC,MAAM,CA4MR;AA2CD,wBAAsB,YAAY,CACjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,eAAe,EAAE,EACvB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,eAAe,CAAA;CAAE,EACnD,MAAM,CAAC,EAAE,WAAW,EACpB,QAAQ,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,MAAM,CAAC,CA2DjB"}
|
package/dist/provider-eval.js
CHANGED
|
@@ -156,7 +156,7 @@ function formatProviderEvaluation(report) {
|
|
|
156
156
|
import fs2 from "fs";
|
|
157
157
|
|
|
158
158
|
// src/constants.ts
|
|
159
|
-
var VERSION = "9.6.
|
|
159
|
+
var VERSION = "9.6.2";
|
|
160
160
|
var SETTLED_TRIGGER_COOLDOWN_MS = 10 * 60000;
|
|
161
161
|
var COMPACT_SYSTEM_PREFIX = "You are an expert conversation summarizer for a coding agent. " + "Produce structured markdown summaries. " + "Follow output format exactly. " + "Use EXACT names \u2014 never paraphrase code identifiers. " + "Trust deterministic extraction data over intuition.";
|
|
162
162
|
var PROFILES = {
|
|
@@ -667,38 +667,117 @@ function buildPathNeedles(filePath) {
|
|
|
667
667
|
}
|
|
668
668
|
return needles;
|
|
669
669
|
}
|
|
670
|
-
|
|
671
|
-
|
|
670
|
+
var MAX_INDEXED_SUFFIX_CHARS = 1024;
|
|
671
|
+
function buildPathNeedleOwnershipIndex(allPaths) {
|
|
672
|
+
const owners = new Map;
|
|
673
|
+
const normalizedPaths = allPaths.map(normalizePath);
|
|
674
|
+
let hasUnindexedSuffixes = false;
|
|
675
|
+
for (const normalized of normalizedPaths) {
|
|
676
|
+
const suffixes = new Set([normalized]);
|
|
677
|
+
for (let index = 0;index < normalized.length; index++) {
|
|
678
|
+
if (normalized[index] !== "/")
|
|
679
|
+
continue;
|
|
680
|
+
if (normalized.length - index - 1 <= MAX_INDEXED_SUFFIX_CHARS) {
|
|
681
|
+
suffixes.add(normalized.slice(index + 1));
|
|
682
|
+
} else {
|
|
683
|
+
hasUnindexedSuffixes = true;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
for (const suffix of suffixes) {
|
|
687
|
+
owners.set(suffix, (owners.get(suffix) ?? 0) + 1);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return { counts: owners, normalizedPaths, hasUnindexedSuffixes };
|
|
691
|
+
}
|
|
692
|
+
function buildUniquePathNeedlesFromIndex(filePath, owners) {
|
|
672
693
|
return buildPathNeedles(filePath).filter((needle) => {
|
|
673
|
-
|
|
674
|
-
|
|
694
|
+
if (!owners.hasUnindexedSuffixes)
|
|
695
|
+
return owners.counts.get(needle) === 1;
|
|
696
|
+
let count = 0;
|
|
697
|
+
for (const candidate of owners.normalizedPaths) {
|
|
675
698
|
if (candidate === needle || candidate.endsWith("/" + needle))
|
|
676
|
-
|
|
677
|
-
if (
|
|
699
|
+
count++;
|
|
700
|
+
if (count > 1)
|
|
678
701
|
return false;
|
|
679
702
|
}
|
|
680
|
-
return
|
|
703
|
+
return count === 1;
|
|
681
704
|
});
|
|
682
705
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
const
|
|
688
|
-
|
|
706
|
+
var PATH_CANDIDATE_CHAR_RE = /[\w./-]/;
|
|
707
|
+
function buildKnownPathReferenceIndex(knownPaths) {
|
|
708
|
+
const segmentSuffixes = new Set;
|
|
709
|
+
const boundarySuffixes = new Set;
|
|
710
|
+
const normalizedPaths = [];
|
|
711
|
+
let hasUnindexedSuffixes = false;
|
|
712
|
+
for (const path of knownPaths) {
|
|
689
713
|
const normalizedPath = normalizePath(path).replace(/^\/+/, "");
|
|
714
|
+
if (!normalizedPath)
|
|
715
|
+
continue;
|
|
716
|
+
normalizedPaths.push(normalizedPath);
|
|
717
|
+
segmentSuffixes.add(normalizedPath);
|
|
718
|
+
for (let index = 0;index < normalizedPath.length; index++) {
|
|
719
|
+
if (normalizedPath[index] === "/") {
|
|
720
|
+
if (normalizedPath.length - index - 1 <= MAX_INDEXED_SUFFIX_CHARS) {
|
|
721
|
+
segmentSuffixes.add(normalizedPath.slice(index + 1));
|
|
722
|
+
} else {
|
|
723
|
+
hasUnindexedSuffixes = true;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
if (index > 0 && !PATH_CANDIDATE_CHAR_RE.test(normalizedPath[index - 1])) {
|
|
727
|
+
if (normalizedPath.length - index <= MAX_INDEXED_SUFFIX_CHARS) {
|
|
728
|
+
boundarySuffixes.add(normalizedPath.slice(index));
|
|
729
|
+
} else {
|
|
730
|
+
hasUnindexedSuffixes = true;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return {
|
|
736
|
+
segmentSuffixes,
|
|
737
|
+
sortedSegmentSuffixes: [...segmentSuffixes].sort(),
|
|
738
|
+
boundarySuffixes,
|
|
739
|
+
normalizedPaths,
|
|
740
|
+
hasUnindexedSuffixes
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
function matchesKnownPathReference(normalizedRef, normalizedPaths) {
|
|
744
|
+
const pathShaped = normalizedRef.includes("/");
|
|
745
|
+
return normalizedPaths.some((normalizedPath) => {
|
|
690
746
|
if (normalizedPath === normalizedRef || normalizedPath.endsWith("/" + normalizedRef))
|
|
691
747
|
return true;
|
|
692
748
|
if (normalizedPath.endsWith(normalizedRef)) {
|
|
693
749
|
const boundary = normalizedPath[normalizedPath.length - normalizedRef.length - 1];
|
|
694
|
-
if (boundary &&
|
|
750
|
+
if (boundary && !PATH_CANDIDATE_CHAR_RE.test(boundary))
|
|
695
751
|
return true;
|
|
696
752
|
}
|
|
697
753
|
if (!pathShaped)
|
|
698
754
|
return false;
|
|
699
|
-
return normalizedPath.
|
|
755
|
+
return normalizedPath.startsWith(normalizedRef + "/") || normalizedPath.includes("/" + normalizedRef + "/");
|
|
700
756
|
});
|
|
701
757
|
}
|
|
758
|
+
function sortedHasPrefix(values, prefix) {
|
|
759
|
+
let low = 0;
|
|
760
|
+
let high = values.length;
|
|
761
|
+
while (low < high) {
|
|
762
|
+
const middle = low + high >>> 1;
|
|
763
|
+
if (values[middle] < prefix)
|
|
764
|
+
low = middle + 1;
|
|
765
|
+
else
|
|
766
|
+
high = middle;
|
|
767
|
+
}
|
|
768
|
+
return values[low]?.startsWith(prefix) ?? false;
|
|
769
|
+
}
|
|
770
|
+
function isKnownPathReferenceInIndex(ref, index) {
|
|
771
|
+
const normalizedRef = normalizePath(ref).replace(/^\/+/, "");
|
|
772
|
+
if (!normalizedRef)
|
|
773
|
+
return false;
|
|
774
|
+
if (index.segmentSuffixes.has(normalizedRef) || index.boundarySuffixes.has(normalizedRef)) {
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
if (normalizedRef.includes("/") && sortedHasPrefix(index.sortedSegmentSuffixes, normalizedRef + "/"))
|
|
778
|
+
return true;
|
|
779
|
+
return index.hasUnindexedSuffixes ? matchesKnownPathReference(normalizedRef, index.normalizedPaths) : false;
|
|
780
|
+
}
|
|
702
781
|
|
|
703
782
|
// src/domain/tool-semantics.ts
|
|
704
783
|
var PATH_KEYS = [
|
|
@@ -832,7 +911,12 @@ function damageReportsFile() {
|
|
|
832
911
|
// src/utils/file-ref-detect.ts
|
|
833
912
|
var CODE_EXT_RE = /\.(ts|tsx|js|jsx|mjs|cjs|rs|py|go|java|rb|cs|cpp|c|h|hpp|swift|kt|scala|php|css|scss|html|json|yaml|yml|toml|md|mdx|sh|sql|tf|ini|env|lock|gradle|xml)$/i;
|
|
834
913
|
var VERSION_RE = /^v?\d+(?:\.\d+)+(?:[-+][\w.-]+)?$/i;
|
|
835
|
-
|
|
914
|
+
function isAsciiWordCode(code) {
|
|
915
|
+
return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code === 95 || code >= 97 && code <= 122;
|
|
916
|
+
}
|
|
917
|
+
function isCandidateCode(code) {
|
|
918
|
+
return isAsciiWordCode(code) || code === 45 || code === 46 || code === 47;
|
|
919
|
+
}
|
|
836
920
|
function isLikelyFileRef(candidate) {
|
|
837
921
|
if (candidate.startsWith("//") || VERSION_RE.test(candidate))
|
|
838
922
|
return false;
|
|
@@ -843,13 +927,32 @@ function isLikelyFileRef(candidate) {
|
|
|
843
927
|
return CODE_EXT_RE.test(candidate);
|
|
844
928
|
}
|
|
845
929
|
function extractFileRefs(summary) {
|
|
846
|
-
const matcher = new RegExp(FILE_REF_CANDIDATE_RE.source, FILE_REF_CANDIDATE_RE.flags);
|
|
847
930
|
const refs = [];
|
|
848
|
-
|
|
849
|
-
|
|
931
|
+
let cursor = 0;
|
|
932
|
+
while (cursor < summary.length) {
|
|
933
|
+
while (cursor < summary.length && !isCandidateCode(summary.charCodeAt(cursor)))
|
|
934
|
+
cursor++;
|
|
935
|
+
const runStart = cursor;
|
|
936
|
+
while (cursor < summary.length && isCandidateCode(summary.charCodeAt(cursor)))
|
|
937
|
+
cursor++;
|
|
938
|
+
const runEnd = cursor;
|
|
939
|
+
let extensionDot = -1;
|
|
940
|
+
for (let index = runStart + 1;index + 1 < runEnd; index++) {
|
|
941
|
+
if (summary.charCodeAt(index) === 46 && isAsciiWordCode(summary.charCodeAt(index + 1))) {
|
|
942
|
+
extensionDot = index;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
if (extensionDot < 0)
|
|
850
946
|
continue;
|
|
851
|
-
|
|
852
|
-
|
|
947
|
+
let matchEnd = extensionDot + 2;
|
|
948
|
+
while (matchEnd < runEnd && isAsciiWordCode(summary.charCodeAt(matchEnd))) {
|
|
949
|
+
matchEnd++;
|
|
950
|
+
}
|
|
951
|
+
const candidate = summary.slice(runStart, matchEnd);
|
|
952
|
+
if (/[\\/]/.test(summary[matchEnd] ?? ""))
|
|
953
|
+
continue;
|
|
954
|
+
if (isLikelyFileRef(candidate))
|
|
955
|
+
refs.push(candidate);
|
|
853
956
|
}
|
|
854
957
|
return refs;
|
|
855
958
|
}
|
|
@@ -1712,6 +1815,229 @@ var processToolSupport = new ToolSupportCache;
|
|
|
1712
1815
|
var processTokenCalibration = new TokenCalibrationStore;
|
|
1713
1816
|
var _default = createServices();
|
|
1714
1817
|
|
|
1818
|
+
// src/domain/telemetry.ts
|
|
1819
|
+
function p95(values) {
|
|
1820
|
+
if (!values.length)
|
|
1821
|
+
return 0;
|
|
1822
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
1823
|
+
return sorted[Math.max(0, Math.ceil(sorted.length * 0.95) - 1)] ?? 0;
|
|
1824
|
+
}
|
|
1825
|
+
function stats(entries, damage) {
|
|
1826
|
+
const evidence = entries.filter((entry) => entry.status !== "dry-run");
|
|
1827
|
+
const successfulRuns = evidence.filter((entry) => entry.status === "success");
|
|
1828
|
+
const quality = successfulRuns.filter((entry) => typeof entry.verificationScore === "number");
|
|
1829
|
+
const appliedRunIds = new Set(successfulRuns.filter((entry) => typeof entry.runId === "string" && entry.runId.length >= 8).map((entry) => entry.runId));
|
|
1830
|
+
const observedScores = new Map;
|
|
1831
|
+
for (const observation of damage) {
|
|
1832
|
+
if (!observation.runId || !appliedRunIds.has(observation.runId) || typeof observation.damageScore !== "number" || !Number.isFinite(observation.damageScore))
|
|
1833
|
+
continue;
|
|
1834
|
+
observedScores.set(observation.runId, Math.max(observedScores.get(observation.runId) ?? 0, Math.max(0, Math.min(100, observation.damageScore))));
|
|
1835
|
+
}
|
|
1836
|
+
const damaging = [...observedScores.values()].filter((score) => score > 0).length;
|
|
1837
|
+
return {
|
|
1838
|
+
runs: entries.length,
|
|
1839
|
+
appliedRuns: evidence.length,
|
|
1840
|
+
successRate: evidence.length ? successfulRuns.length / evidence.length : 1,
|
|
1841
|
+
avgQuality: quality.length ? quality.reduce((sum, entry) => sum + (entry.verificationScore ?? 0), 0) / quality.length : null,
|
|
1842
|
+
qualityCoverage: evidence.length ? quality.length / evidence.length : 0,
|
|
1843
|
+
p95LatencyMs: p95(evidence.map((entry) => entry.durationMs ?? entry.avgLatency).filter(Number.isFinite)),
|
|
1844
|
+
avgTokens: evidence.length ? evidence.reduce((sum, entry) => sum + entry.totalInput + entry.totalCacheHit + (entry.totalCacheWrite ?? 0) + entry.totalOutput, 0) / evidence.length : 0,
|
|
1845
|
+
fallbackRate: evidence.length ? evidence.filter((entry) => entry.method === "heuristic" || Array.isArray(entry.providerRoutes) && entry.providerRoutes.some((route) => route.successes < route.calls)).length / evidence.length : 0,
|
|
1846
|
+
damageRate: observedScores.size ? damaging / observedScores.size : 0,
|
|
1847
|
+
damageCoverage: successfulRuns.length ? observedScores.size / successfulRuns.length : 0
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1850
|
+
function roundStats(value) {
|
|
1851
|
+
return {
|
|
1852
|
+
...value,
|
|
1853
|
+
successRate: Math.round(value.successRate * 1000) / 1000,
|
|
1854
|
+
avgQuality: value.avgQuality == null ? null : Math.round(value.avgQuality * 10) / 10,
|
|
1855
|
+
qualityCoverage: Math.round(value.qualityCoverage * 1000) / 1000,
|
|
1856
|
+
p95LatencyMs: Math.round(value.p95LatencyMs),
|
|
1857
|
+
avgTokens: Math.round(value.avgTokens),
|
|
1858
|
+
fallbackRate: Math.round(value.fallbackRate * 1000) / 1000,
|
|
1859
|
+
damageRate: Math.round(value.damageRate * 1000) / 1000,
|
|
1860
|
+
damageCoverage: Math.round(value.damageCoverage * 1000) / 1000
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
function assessCanary(entries, damageEntries, options) {
|
|
1864
|
+
const minCanaryRuns = Math.max(5, options.minCanaryRuns ?? 20);
|
|
1865
|
+
const canaryEntries = entries.filter((entry) => entry.metricsSchemaVersion === 2 && entry.version === options.version && entry.releaseChannel === "canary").slice(-Math.max(100, minCanaryRuns));
|
|
1866
|
+
const baselineEntries = entries.filter((entry) => entry.metricsSchemaVersion === 2 && (entry.releaseChannel ?? "stable") === "stable").slice(-(options.baselineRuns ?? Math.max(50, minCanaryRuns * 2)));
|
|
1867
|
+
const baseline = stats(baselineEntries, damageEntries);
|
|
1868
|
+
const canary = stats(canaryEntries, damageEntries);
|
|
1869
|
+
const triggers = [];
|
|
1870
|
+
const failureBaseline = 1 - baseline.successRate;
|
|
1871
|
+
const failureCanary = 1 - canary.successRate;
|
|
1872
|
+
if (canary.appliedRuns >= 3 && (failureCanary > 0.050001 || failureCanary - failureBaseline >= 0.050001)) {
|
|
1873
|
+
triggers.push({
|
|
1874
|
+
metric: "failure-rate",
|
|
1875
|
+
baseline: failureBaseline,
|
|
1876
|
+
canary: failureCanary,
|
|
1877
|
+
threshold: failureCanary > 0.050001 ? ">5% absolute" : "+5pp regression"
|
|
1878
|
+
});
|
|
1879
|
+
}
|
|
1880
|
+
if (canary.avgQuality != null && (canary.avgQuality < 85 || baseline.avgQuality != null && baseline.avgQuality - canary.avgQuality >= 5)) {
|
|
1881
|
+
triggers.push({
|
|
1882
|
+
metric: "quality",
|
|
1883
|
+
baseline: baseline.avgQuality ?? 0,
|
|
1884
|
+
canary: canary.avgQuality,
|
|
1885
|
+
threshold: canary.avgQuality < 85 ? "<85 absolute" : "-5 points"
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
if (baseline.p95LatencyMs >= 1000 && canary.p95LatencyMs >= baseline.p95LatencyMs * 1.5) {
|
|
1889
|
+
triggers.push({ metric: "latency", baseline: baseline.p95LatencyMs, canary: canary.p95LatencyMs, threshold: "+50% p95" });
|
|
1890
|
+
}
|
|
1891
|
+
if (baseline.avgTokens >= 1000 && canary.avgTokens >= baseline.avgTokens * 1.5) {
|
|
1892
|
+
triggers.push({ metric: "tokens", baseline: baseline.avgTokens, canary: canary.avgTokens, threshold: "+50%" });
|
|
1893
|
+
}
|
|
1894
|
+
if (canary.fallbackRate - baseline.fallbackRate >= 0.1) {
|
|
1895
|
+
triggers.push({ metric: "fallback", baseline: baseline.fallbackRate, canary: canary.fallbackRate, threshold: "+10pp" });
|
|
1896
|
+
}
|
|
1897
|
+
if (canary.damageRate - baseline.damageRate >= 0.1) {
|
|
1898
|
+
triggers.push({ metric: "damage", baseline: baseline.damageRate, canary: canary.damageRate, threshold: "+10pp" });
|
|
1899
|
+
}
|
|
1900
|
+
const canarySampleAdequacy = Math.min(1, canary.appliedRuns / minCanaryRuns);
|
|
1901
|
+
const baselineSampleAdequacy = Math.min(1, baseline.appliedRuns / Math.max(20, minCanaryRuns));
|
|
1902
|
+
const dataConfidence = Math.round(100 * (canarySampleAdequacy * 0.25 + baselineSampleAdequacy * 0.15 + canary.qualityCoverage * canarySampleAdequacy * 0.2 + canary.damageCoverage * canarySampleAdequacy * 0.2 + baseline.damageCoverage * baselineSampleAdequacy * 0.2));
|
|
1903
|
+
const reasons = [];
|
|
1904
|
+
let decision = "hold";
|
|
1905
|
+
if (triggers.length && canary.appliedRuns >= 3) {
|
|
1906
|
+
decision = "rollback";
|
|
1907
|
+
reasons.push(...triggers.map((trigger) => trigger.metric + " crossed " + trigger.threshold));
|
|
1908
|
+
} else if (canary.appliedRuns < minCanaryRuns) {
|
|
1909
|
+
reasons.push("need " + (minCanaryRuns - canary.appliedRuns) + " more canary runs with applied outcomes");
|
|
1910
|
+
} else if (baseline.appliedRuns < Math.max(20, minCanaryRuns)) {
|
|
1911
|
+
reasons.push("stable baseline is too small");
|
|
1912
|
+
} else if (canary.qualityCoverage < 0.7) {
|
|
1913
|
+
reasons.push("schema-v2 quality coverage is below 70%");
|
|
1914
|
+
} else if (canary.damageCoverage < 0.7) {
|
|
1915
|
+
reasons.push("correlated canary damage-observation coverage is below 70%");
|
|
1916
|
+
} else if (baseline.damageCoverage < 0.7) {
|
|
1917
|
+
reasons.push("correlated stable damage-observation coverage is below 70%");
|
|
1918
|
+
} else if ((canary.avgQuality ?? 0) < 85) {
|
|
1919
|
+
reasons.push("absolute verifier quality is below 85");
|
|
1920
|
+
} else if (canary.successRate < 0.949999) {
|
|
1921
|
+
reasons.push("absolute success rate is below 95%");
|
|
1922
|
+
} else {
|
|
1923
|
+
decision = "promote";
|
|
1924
|
+
reasons.push("sample, absolute quality, reliability, latency, token, fallback, and damage gates passed");
|
|
1925
|
+
}
|
|
1926
|
+
return {
|
|
1927
|
+
version: options.version,
|
|
1928
|
+
decision,
|
|
1929
|
+
dataConfidence,
|
|
1930
|
+
baseline: roundStats(baseline),
|
|
1931
|
+
canary: roundStats(canary),
|
|
1932
|
+
triggers,
|
|
1933
|
+
reasons
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
function safeMetricLabel(value, fallback) {
|
|
1937
|
+
if (typeof value !== "string" || !/^[\w./:@+-]{1,160}$/.test(value))
|
|
1938
|
+
return fallback;
|
|
1939
|
+
return value;
|
|
1940
|
+
}
|
|
1941
|
+
var TELEMETRY_FAILURE_KINDS = new Set([
|
|
1942
|
+
"cancelled",
|
|
1943
|
+
"timeout",
|
|
1944
|
+
"rate-limit",
|
|
1945
|
+
"authentication",
|
|
1946
|
+
"budget",
|
|
1947
|
+
"output-limit",
|
|
1948
|
+
"provider",
|
|
1949
|
+
"persistence",
|
|
1950
|
+
"validation",
|
|
1951
|
+
"verification",
|
|
1952
|
+
"yield",
|
|
1953
|
+
"internal"
|
|
1954
|
+
]);
|
|
1955
|
+
function isTelemetryFailureKind(value) {
|
|
1956
|
+
return typeof value === "string" && TELEMETRY_FAILURE_KINDS.has(value);
|
|
1957
|
+
}
|
|
1958
|
+
function buildPrivacySafeTelemetry(entries, damageEntries, options) {
|
|
1959
|
+
const groups = new Map;
|
|
1960
|
+
const failures = {};
|
|
1961
|
+
for (const entry of entries) {
|
|
1962
|
+
const version = safeMetricLabel(entry.version, "legacy");
|
|
1963
|
+
const channel = entry.releaseChannel === "canary" ? "canary" : "stable";
|
|
1964
|
+
const provider = safeMetricLabel(entry.provider, "unknown");
|
|
1965
|
+
const rawModel = safeMetricLabel(entry.model, "unknown");
|
|
1966
|
+
const model = rawModel.startsWith(provider + "/") ? rawModel.slice(provider.length + 1) : rawModel;
|
|
1967
|
+
const key = [version, channel, provider, model].join("\x00");
|
|
1968
|
+
const group = groups.get(key) ?? {
|
|
1969
|
+
version,
|
|
1970
|
+
channel,
|
|
1971
|
+
provider,
|
|
1972
|
+
model,
|
|
1973
|
+
runs: 0,
|
|
1974
|
+
successes: 0,
|
|
1975
|
+
quality: 0,
|
|
1976
|
+
qualityRuns: 0,
|
|
1977
|
+
latency: 0,
|
|
1978
|
+
input: 0,
|
|
1979
|
+
output: 0
|
|
1980
|
+
};
|
|
1981
|
+
group.runs++;
|
|
1982
|
+
if (entry.status === "success" || entry.status === "dry-run")
|
|
1983
|
+
group.successes++;
|
|
1984
|
+
if (entry.metricsSchemaVersion === 2 && typeof entry.verificationScore === "number") {
|
|
1985
|
+
group.quality += entry.verificationScore;
|
|
1986
|
+
group.qualityRuns++;
|
|
1987
|
+
}
|
|
1988
|
+
group.latency += entry.avgLatency;
|
|
1989
|
+
group.input += entry.totalInput + entry.totalCacheHit + (entry.totalCacheWrite ?? 0);
|
|
1990
|
+
group.output += entry.totalOutput;
|
|
1991
|
+
groups.set(key, group);
|
|
1992
|
+
if (isTelemetryFailureKind(entry.failureKind)) {
|
|
1993
|
+
failures[entry.failureKind] = (failures[entry.failureKind] ?? 0) + 1;
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
const aggregates = [...groups.values()].map((group) => ({
|
|
1997
|
+
version: group.version,
|
|
1998
|
+
channel: group.channel,
|
|
1999
|
+
provider: group.provider,
|
|
2000
|
+
model: group.model,
|
|
2001
|
+
runs: group.runs,
|
|
2002
|
+
successes: group.successes,
|
|
2003
|
+
avgQuality: group.qualityRuns ? Math.round(group.quality / group.qualityRuns * 10) / 10 : null,
|
|
2004
|
+
avgLatencyMs: group.runs ? Math.round(group.latency / group.runs) : 0,
|
|
2005
|
+
inputTokens: group.input,
|
|
2006
|
+
outputTokens: group.output
|
|
2007
|
+
})).sort((a, b) => b.runs - a.runs || a.version.localeCompare(b.version));
|
|
2008
|
+
return {
|
|
2009
|
+
generatedAt: new Date().toISOString(),
|
|
2010
|
+
totalRuns: entries.length,
|
|
2011
|
+
aggregates,
|
|
2012
|
+
failures,
|
|
2013
|
+
canary: assessCanary(entries, damageEntries, options),
|
|
2014
|
+
privacy: "aggregate-only; no session ids, project ids, prompts, summaries, paths, or error text"
|
|
2015
|
+
};
|
|
2016
|
+
}
|
|
2017
|
+
function formatPrivacySafeTelemetry(report) {
|
|
2018
|
+
const lines = [
|
|
2019
|
+
"# Smart Compact Telemetry",
|
|
2020
|
+
"",
|
|
2021
|
+
"Privacy: " + report.privacy + ".",
|
|
2022
|
+
"",
|
|
2023
|
+
"| Version | Channel | Provider/model | Runs | Success | Quality | Latency | Input | Output |",
|
|
2024
|
+
"|---|---|---|---:|---:|---:|---:|---:|---:|"
|
|
2025
|
+
];
|
|
2026
|
+
for (const item of report.aggregates) {
|
|
2027
|
+
lines.push("| " + item.version + " | " + item.channel + " | " + item.provider + "/" + item.model + " | " + item.runs + " | " + item.successes + "/" + item.runs + " | " + (item.avgQuality == null ? "n/a" : item.avgQuality.toFixed(1)) + " | " + item.avgLatencyMs + "ms | " + item.inputTokens + " | " + item.outputTokens + " |");
|
|
2028
|
+
}
|
|
2029
|
+
lines.push("", "## Canary: " + report.canary.decision.toUpperCase() + " (data confidence " + report.canary.dataConfidence + "%)", "");
|
|
2030
|
+
const baseline = report.canary.baseline;
|
|
2031
|
+
const canary = report.canary.canary;
|
|
2032
|
+
lines.push("| Gate | Stable baseline | Canary |", "|---|---:|---:|", "| Runs (total/applied) | " + baseline.runs + "/" + baseline.appliedRuns + " | " + canary.runs + "/" + canary.appliedRuns + " |", "| Success | " + Math.round(baseline.successRate * 100) + "% | " + Math.round(canary.successRate * 100) + "% |", "| Verify quality | " + (baseline.avgQuality ?? "n/a") + " | " + (canary.avgQuality ?? "n/a") + " |", "| p95 duration | " + baseline.p95LatencyMs + "ms | " + canary.p95LatencyMs + "ms |", "| Avg tokens | " + baseline.avgTokens + " | " + canary.avgTokens + " |", "| Fallback | " + Math.round(baseline.fallbackRate * 100) + "% | " + Math.round(canary.fallbackRate * 100) + "% |", "| Damage | " + Math.round(baseline.damageRate * 100) + "% | " + Math.round(canary.damageRate * 100) + "% |", "| Damage observed | " + Math.round(baseline.damageCoverage * 100) + "% | " + Math.round(canary.damageCoverage * 100) + "% |", "");
|
|
2033
|
+
for (const reason of report.canary.reasons)
|
|
2034
|
+
lines.push("- " + reason);
|
|
2035
|
+
const failureText = Object.entries(report.failures).map(([kind, count]) => kind + "=" + count).join(", ");
|
|
2036
|
+
lines.push("", "Failures: " + (failureText || "none classified"));
|
|
2037
|
+
return lines.join(`
|
|
2038
|
+
`);
|
|
2039
|
+
}
|
|
2040
|
+
|
|
1715
2041
|
// src/utils/cache.ts
|
|
1716
2042
|
var INTERNAL_PHASES = new Set([
|
|
1717
2043
|
"explore-retry",
|