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.
Files changed (47) hide show
  1. package/ARCHITECTURE.md +28 -14
  2. package/CHANGELOG.md +46 -0
  3. package/README.md +30 -10
  4. package/dist/app/mode-policy.d.ts +3 -1
  5. package/dist/app/mode-policy.d.ts.map +1 -1
  6. package/dist/app/preflight.d.ts.map +1 -1
  7. package/dist/app/register-smart-compact-tool.d.ts.map +1 -1
  8. package/dist/app/run-context.d.ts +3 -3
  9. package/dist/app/run-context.d.ts.map +1 -1
  10. package/dist/app/run-smart-compact.d.ts.map +1 -1
  11. package/dist/app/steps/extract.d.ts.map +1 -1
  12. package/dist/app/steps/prepare.d.ts.map +1 -1
  13. package/dist/app/steps/state.d.ts.map +1 -1
  14. package/dist/app/steps/synthesize.d.ts.map +1 -1
  15. package/dist/app/steps/tier.d.ts +5 -4
  16. package/dist/app/steps/tier.d.ts.map +1 -1
  17. package/dist/app/steps/window.d.ts.map +1 -1
  18. package/dist/constants.d.ts +1 -1
  19. package/dist/domain/provider-evaluation.d.ts.map +1 -1
  20. package/dist/domain/telemetry.d.ts.map +1 -1
  21. package/dist/domain/tool-semantics.d.ts.map +1 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +1489 -1248
  24. package/dist/infra/ai-messages.d.ts +6 -2
  25. package/dist/infra/ai-messages.d.ts.map +1 -1
  26. package/dist/phases/explore.d.ts.map +1 -1
  27. package/dist/phases/synthesize.d.ts +5 -0
  28. package/dist/phases/synthesize.d.ts.map +1 -1
  29. package/dist/phases/verify.d.ts.map +1 -1
  30. package/dist/provider-eval.js +348 -22
  31. package/dist/provider-scenario-eval.js +363 -33
  32. package/dist/telemetry-report.js +125 -22
  33. package/dist/types.d.ts +3 -0
  34. package/dist/types.d.ts.map +1 -1
  35. package/dist/ui/error-format.d.ts +3 -1
  36. package/dist/ui/error-format.d.ts.map +1 -1
  37. package/dist/ui/metrics-report.d.ts.map +1 -1
  38. package/dist/ui/overlays.d.ts.map +1 -1
  39. package/dist/utils/cache.d.ts.map +1 -1
  40. package/dist/utils/file-needles.d.ts +24 -0
  41. package/dist/utils/file-needles.d.ts.map +1 -1
  42. package/dist/utils/file-ref-detect.d.ts +6 -1
  43. package/dist/utils/file-ref-detect.d.ts.map +1 -1
  44. package/dist/utils/helpers.d.ts +1 -0
  45. package/dist/utils/helpers.d.ts.map +1 -1
  46. package/dist/utils/session-log.d.ts.map +1 -1
  47. 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 { Message } from "@earendil-works/pi-ai";
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,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEzD;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,CAEpE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,cAAc,GAAG,UAAU,EAAE,CAE3F"}
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;AAoGD,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"}
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;AAsNjE,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,CAgKzB;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
+ {"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;AAkCrB,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;AA8SD,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;AAwUD,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;AA8XD,wBAAgB,aAAa,CAC5B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,oBAAoB,EAChC,UAAU,GAAE,eAAe,GAAG,IAAW,EACzC,QAAQ,GAAE,oBAAyB,GACjC,kBAAkB,CA2CpB;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"}
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"}
@@ -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.0";
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
- function buildUniquePathNeedles(filePath, allPaths) {
671
- const normalized = allPaths.map(normalizePath);
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
- let owners = 0;
674
- for (const candidate of normalized) {
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
- owners++;
677
- if (owners > 1)
699
+ count++;
700
+ if (count > 1)
678
701
  return false;
679
702
  }
680
- return owners === 1;
703
+ return count === 1;
681
704
  });
682
705
  }
683
- function isKnownPathReference(ref, knownPaths) {
684
- const normalizedRef = normalizePath(ref).replace(/^\/+/, "");
685
- if (!normalizedRef)
686
- return false;
687
- const pathShaped = normalizedRef.includes("/");
688
- return knownPaths.some((path) => {
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 && !/[\w./-]/.test(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.split("/").some((_, index, parts) => parts.slice(index).join("/").startsWith(normalizedRef + "/"));
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
- var FILE_REF_CANDIDATE_RE = /[\w.\/-]+\.[\w]+/g;
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
- for (const match of summary.matchAll(matcher)) {
849
- if (/[\\/]/.test(summary[(match.index ?? 0) + match[0].length] ?? ""))
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
- if (isLikelyFileRef(match[0]))
852
- refs.push(match[0]);
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",