agentskeptic 1.1.0 → 1.1.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 (239) hide show
  1. package/README.md +22 -36
  2. package/config/marketing.json +6 -5
  3. package/dist/actionableFailure.d.ts.map +1 -1
  4. package/dist/actionableFailure.js +15 -0
  5. package/dist/actionableFailure.js.map +1 -1
  6. package/dist/assurance/assurancePathArgs.d.ts.map +1 -1
  7. package/dist/assurance/assurancePathArgs.js +1 -0
  8. package/dist/assurance/assurancePathArgs.js.map +1 -1
  9. package/dist/certificateDigest.d.ts +4 -0
  10. package/dist/certificateDigest.d.ts.map +1 -0
  11. package/dist/certificateDigest.js +7 -0
  12. package/dist/certificateDigest.js.map +1 -0
  13. package/dist/certificateDigest.test.d.ts +2 -0
  14. package/dist/certificateDigest.test.d.ts.map +1 -0
  15. package/dist/certificateDigest.test.js +14 -0
  16. package/dist/certificateDigest.test.js.map +1 -0
  17. package/dist/cli/initCommand.d.ts +2 -0
  18. package/dist/cli/initCommand.d.ts.map +1 -0
  19. package/dist/cli/initCommand.js +135 -0
  20. package/dist/cli/initCommand.js.map +1 -0
  21. package/dist/cli/lockOrchestration.d.ts.map +1 -1
  22. package/dist/cli/lockOrchestration.js +29 -4
  23. package/dist/cli/lockOrchestration.js.map +1 -1
  24. package/dist/cli/migrateCommand.d.ts +6 -0
  25. package/dist/cli/migrateCommand.d.ts.map +1 -0
  26. package/dist/cli/migrateCommand.js +97 -0
  27. package/dist/cli/migrateCommand.js.map +1 -0
  28. package/dist/cli.js +94 -109
  29. package/dist/cli.js.map +1 -1
  30. package/dist/cliArgv.test.d.ts +2 -0
  31. package/dist/cliArgv.test.d.ts.map +1 -0
  32. package/dist/cliArgv.test.js +34 -0
  33. package/dist/cliArgv.test.js.map +1 -0
  34. package/dist/cliOperationalCodes.d.ts +6 -0
  35. package/dist/cliOperationalCodes.d.ts.map +1 -1
  36. package/dist/cliOperationalCodes.js +6 -0
  37. package/dist/cliOperationalCodes.js.map +1 -1
  38. package/dist/commercial/activationCorrelation.d.ts +3 -0
  39. package/dist/commercial/activationCorrelation.d.ts.map +1 -0
  40. package/dist/commercial/activationCorrelation.js +6 -0
  41. package/dist/commercial/activationCorrelation.js.map +1 -0
  42. package/dist/commercial/getCurrentUsage.d.ts +16 -0
  43. package/dist/commercial/getCurrentUsage.d.ts.map +1 -0
  44. package/dist/commercial/getCurrentUsage.js +12 -0
  45. package/dist/commercial/getCurrentUsage.js.map +1 -0
  46. package/dist/commercial/licensePreflight.d.ts +1 -0
  47. package/dist/commercial/licensePreflight.d.ts.map +1 -1
  48. package/dist/commercial/licensePreflight.js +12 -77
  49. package/dist/commercial/licensePreflight.js.map +1 -1
  50. package/dist/commercial/postVerifyOutcomeBeacon.d.ts +2 -0
  51. package/dist/commercial/postVerifyOutcomeBeacon.d.ts.map +1 -1
  52. package/dist/commercial/postVerifyOutcomeBeacon.js +8 -13
  53. package/dist/commercial/postVerifyOutcomeBeacon.js.map +1 -1
  54. package/dist/compare.acceptance.test.js +25 -0
  55. package/dist/compare.acceptance.test.js.map +1 -1
  56. package/dist/compareRunManifest.d.ts +32 -0
  57. package/dist/compareRunManifest.d.ts.map +1 -0
  58. package/dist/compareRunManifest.js +60 -0
  59. package/dist/compareRunManifest.js.map +1 -0
  60. package/dist/debug-ui/app.css +42 -0
  61. package/dist/debug-ui/app.js +206 -15
  62. package/dist/debug-ui/index.html +11 -4
  63. package/dist/debug-ui/urlState.d.ts +15 -0
  64. package/dist/debug-ui/urlState.js +92 -0
  65. package/dist/debugCorpus.test.js +33 -7
  66. package/dist/debugCorpus.test.js.map +1 -1
  67. package/dist/debugPanels.d.ts +0 -3
  68. package/dist/debugPanels.d.ts.map +1 -1
  69. package/dist/debugPanels.js +0 -23
  70. package/dist/debugPanels.js.map +1 -1
  71. package/dist/debugPanels.test.js +16 -15
  72. package/dist/debugPanels.test.js.map +1 -1
  73. package/dist/debugServer.d.ts.map +1 -1
  74. package/dist/debugServer.js +19 -16
  75. package/dist/debugServer.js.map +1 -1
  76. package/dist/debugServer.test.js +18 -9
  77. package/dist/debugServer.test.js.map +1 -1
  78. package/dist/debugUiUrlState.test.d.ts +2 -0
  79. package/dist/debugUiUrlState.test.d.ts.map +1 -0
  80. package/dist/debugUiUrlState.test.js +49 -0
  81. package/dist/debugUiUrlState.test.js.map +1 -0
  82. package/dist/decisionGate.assertSafe.test.js +2 -2
  83. package/dist/decisionGate.assertSafe.test.js.map +1 -1
  84. package/dist/decisionGate.d.ts +2 -1
  85. package/dist/decisionGate.d.ts.map +1 -1
  86. package/dist/decisionGate.js +2 -1
  87. package/dist/decisionGate.js.map +1 -1
  88. package/dist/decisionGate.persistBundle.test.js +3 -3
  89. package/dist/decisionGate.persistBundle.test.js.map +1 -1
  90. package/dist/discovery-payload-v1.json +1 -1
  91. package/dist/enforceCli.d.ts.map +1 -1
  92. package/dist/enforceCli.js +15 -40
  93. package/dist/enforceCli.js.map +1 -1
  94. package/dist/enforceStateful.d.ts +2 -0
  95. package/dist/enforceStateful.d.ts.map +1 -0
  96. package/dist/enforceStateful.js +88 -0
  97. package/dist/enforceStateful.js.map +1 -0
  98. package/dist/enforcementProjection.d.ts +17 -0
  99. package/dist/enforcementProjection.d.ts.map +1 -0
  100. package/dist/enforcementProjection.js +20 -0
  101. package/dist/enforcementProjection.js.map +1 -0
  102. package/dist/executionTrace.test.js +35 -0
  103. package/dist/executionTrace.test.js.map +1 -1
  104. package/dist/executionTraceDiff.d.ts +36 -0
  105. package/dist/executionTraceDiff.d.ts.map +1 -0
  106. package/dist/executionTraceDiff.js +86 -0
  107. package/dist/executionTraceDiff.js.map +1 -0
  108. package/dist/executionTraceDiff.test.d.ts +2 -0
  109. package/dist/executionTraceDiff.test.d.ts.map +1 -0
  110. package/dist/executionTraceDiff.test.js +44 -0
  111. package/dist/executionTraceDiff.test.js.map +1 -0
  112. package/dist/failureOriginCatalog.d.ts.map +1 -1
  113. package/dist/failureOriginCatalog.js +3 -0
  114. package/dist/failureOriginCatalog.js.map +1 -1
  115. package/dist/firstFiveMinutesChecklist.d.ts +1 -1
  116. package/dist/firstFiveMinutesChecklist.d.ts.map +1 -1
  117. package/dist/firstFiveMinutesChecklist.js +2 -2
  118. package/dist/firstFiveMinutesChecklist.js.map +1 -1
  119. package/dist/index.d.ts +13 -7
  120. package/dist/index.d.ts.map +1 -1
  121. package/dist/index.js +10 -7
  122. package/dist/index.js.map +1 -1
  123. package/dist/operationalDisposition.d.ts +48 -0
  124. package/dist/operationalDisposition.d.ts.map +1 -1
  125. package/dist/operationalDisposition.js +48 -0
  126. package/dist/operationalDisposition.js.map +1 -1
  127. package/dist/publicDistribution.generated.d.ts +1 -1
  128. package/dist/publicDistribution.generated.js +1 -1
  129. package/dist/quickVerify/quickVerifyScope.d.ts +1 -1
  130. package/dist/quickVerify/quickVerifyScope.js +1 -1
  131. package/dist/regressionArtifact.d.ts +63 -0
  132. package/dist/regressionArtifact.d.ts.map +1 -0
  133. package/dist/regressionArtifact.js +290 -0
  134. package/dist/regressionArtifact.js.map +1 -0
  135. package/dist/runComparison.d.ts +0 -1
  136. package/dist/runComparison.d.ts.map +1 -1
  137. package/dist/runComparison.js +0 -83
  138. package/dist/runComparison.js.map +1 -1
  139. package/dist/runComparison.test.js +101 -57
  140. package/dist/runComparison.test.js.map +1 -1
  141. package/dist/schemaLoad.d.ts +1 -1
  142. package/dist/schemaLoad.d.ts.map +1 -1
  143. package/dist/schemaLoad.js +8 -0
  144. package/dist/schemaLoad.js.map +1 -1
  145. package/dist/sdk/AgentSkeptic.d.ts +37 -0
  146. package/dist/sdk/AgentSkeptic.d.ts.map +1 -0
  147. package/dist/sdk/AgentSkeptic.js +45 -0
  148. package/dist/sdk/AgentSkeptic.js.map +1 -0
  149. package/dist/sdk/Gate.d.ts +3 -0
  150. package/dist/sdk/Gate.d.ts.map +1 -0
  151. package/dist/sdk/Gate.js +2 -0
  152. package/dist/sdk/Gate.js.map +1 -0
  153. package/dist/sdk/_generated/openapi-types.d.ts +1034 -0
  154. package/dist/sdk/_generated/openapi-types.d.ts.map +1 -0
  155. package/dist/sdk/_generated/openapi-types.js +6 -0
  156. package/dist/sdk/_generated/openapi-types.js.map +1 -0
  157. package/dist/sdk/deprecate.d.ts +6 -0
  158. package/dist/sdk/deprecate.d.ts.map +1 -0
  159. package/dist/sdk/deprecate.js +14 -0
  160. package/dist/sdk/deprecate.js.map +1 -0
  161. package/dist/sdk/deprecatedPublicApi.d.ts +18 -0
  162. package/dist/sdk/deprecatedPublicApi.d.ts.map +1 -0
  163. package/dist/sdk/deprecatedPublicApi.js +36 -0
  164. package/dist/sdk/deprecatedPublicApi.js.map +1 -0
  165. package/dist/sdk/errors.d.ts +23 -0
  166. package/dist/sdk/errors.d.ts.map +1 -0
  167. package/dist/sdk/errors.js +28 -0
  168. package/dist/sdk/errors.js.map +1 -0
  169. package/dist/sdk/frameworks/next.d.ts +12 -0
  170. package/dist/sdk/frameworks/next.d.ts.map +1 -0
  171. package/dist/sdk/frameworks/next.js +29 -0
  172. package/dist/sdk/frameworks/next.js.map +1 -0
  173. package/dist/sdk/transport.d.ts +35 -0
  174. package/dist/sdk/transport.d.ts.map +1 -0
  175. package/dist/sdk/transport.js +180 -0
  176. package/dist/sdk/transport.js.map +1 -0
  177. package/dist/sortedJsonStringify.d.ts +2 -0
  178. package/dist/sortedJsonStringify.d.ts.map +1 -0
  179. package/dist/sortedJsonStringify.js +23 -0
  180. package/dist/sortedJsonStringify.js.map +1 -0
  181. package/dist/standardVerifyWorkflowCli.d.ts.map +1 -1
  182. package/dist/standardVerifyWorkflowCli.js +3 -2
  183. package/dist/standardVerifyWorkflowCli.js.map +1 -1
  184. package/dist/telemetry/maybeEmitOssClaimTicketUrl.d.ts +2 -0
  185. package/dist/telemetry/maybeEmitOssClaimTicketUrl.d.ts.map +1 -1
  186. package/dist/telemetry/maybeEmitOssClaimTicketUrl.js +13 -2
  187. package/dist/telemetry/maybeEmitOssClaimTicketUrl.js.map +1 -1
  188. package/dist/telemetry/postOssClaimContinuation.d.ts +1 -1
  189. package/dist/telemetry/postOssClaimContinuation.d.ts.map +1 -1
  190. package/dist/telemetry/postOssClaimContinuation.js +10 -6
  191. package/dist/telemetry/postOssClaimContinuation.js.map +1 -1
  192. package/dist/telemetry/postOssClaimTicket.d.ts +8 -0
  193. package/dist/telemetry/postOssClaimTicket.d.ts.map +1 -1
  194. package/dist/telemetry/postOssClaimTicket.js +31 -5
  195. package/dist/telemetry/postOssClaimTicket.js.map +1 -1
  196. package/dist/truthLayerError.d.ts +5 -2
  197. package/dist/truthLayerError.d.ts.map +1 -1
  198. package/dist/truthLayerError.js +6 -4
  199. package/dist/truthLayerError.js.map +1 -1
  200. package/dist/types.d.ts +10 -1
  201. package/dist/types.d.ts.map +1 -1
  202. package/dist/types.js.map +1 -1
  203. package/dist/verificationDiagnostics.d.ts.map +1 -1
  204. package/dist/verificationDiagnostics.js +4 -1
  205. package/dist/verificationDiagnostics.js.map +1 -1
  206. package/dist/verificationPolicy.d.ts.map +1 -1
  207. package/dist/verificationPolicy.js +13 -0
  208. package/dist/verificationPolicy.js.map +1 -1
  209. package/dist/verificationUserPhrases.d.ts.map +1 -1
  210. package/dist/verificationUserPhrases.js +3 -0
  211. package/dist/verificationUserPhrases.js.map +1 -1
  212. package/dist/verify/batchVerifyTelemetrySubcommand.d.ts.map +1 -1
  213. package/dist/verify/batchVerifyTelemetrySubcommand.js +8 -1
  214. package/dist/verify/batchVerifyTelemetrySubcommand.js.map +1 -1
  215. package/dist/verifyAgentskeptic.d.ts +1 -1
  216. package/dist/verifyAgentskeptic.d.ts.map +1 -1
  217. package/dist/verifyAgentskeptic.js +3 -3
  218. package/dist/verifyAgentskeptic.js.map +1 -1
  219. package/dist/verifyAgentskeptic.test.js +5 -5
  220. package/dist/verifyAgentskeptic.test.js.map +1 -1
  221. package/dist/vitestMonorepoRoot.d.ts +10 -0
  222. package/dist/vitestMonorepoRoot.d.ts.map +1 -0
  223. package/dist/vitestMonorepoRoot.js +97 -0
  224. package/dist/vitestMonorepoRoot.js.map +1 -0
  225. package/dist/wireReasonCodes.d.ts +3 -0
  226. package/dist/wireReasonCodes.d.ts.map +1 -1
  227. package/dist/wireReasonCodes.js +3 -0
  228. package/dist/wireReasonCodes.js.map +1 -1
  229. package/package.json +46 -12
  230. package/schemas/agentskeptic-error-codes.json +608 -0
  231. package/schemas/compare-run-manifest-v1.schema.json +65 -0
  232. package/schemas/conformance-normalized-result.schema.json +73 -0
  233. package/schemas/connector-capabilities.schema.json +38 -0
  234. package/schemas/execution-trace-view.schema.json +1 -1
  235. package/schemas/openapi-commercial-v1.in.yaml +580 -14
  236. package/schemas/openapi-commercial-v1.yaml +580 -14
  237. package/schemas/quick-verify-report.schema.json +1 -1
  238. package/schemas/regression-artifact-v1.schema.json +212 -0
  239. package/scripts/discovery-payload.lib.cjs +3 -3
@@ -0,0 +1,32 @@
1
+ export type OutcomeCertificateRunKindForCompare = "contract_sql" | "contract_sql_langgraph_checkpoint_trust";
2
+ export type CertificateProfileV1 = {
3
+ mode: "uniform";
4
+ outcomeCertificateRunKind: OutcomeCertificateRunKindForCompare;
5
+ } | {
6
+ mode: "perRun";
7
+ entries: Array<{
8
+ runIndex: number;
9
+ outcomeCertificateRunKind: OutcomeCertificateRunKindForCompare;
10
+ }>;
11
+ };
12
+ export type CompareRunManifestEntryV1 = {
13
+ displayLabel: string;
14
+ workflowResult: string;
15
+ events: string;
16
+ };
17
+ export type CompareRunManifestV1 = {
18
+ schemaVersion: 1;
19
+ baseDirectory: string;
20
+ certificateProfile: CertificateProfileV1;
21
+ runs: CompareRunManifestEntryV1[];
22
+ };
23
+ /**
24
+ * Read and validate a compare-run manifest. Resolves `baseDirectory` and validates certificate profile
25
+ * invariants (per-run index coverage) beyond JSON Schema.
26
+ */
27
+ export declare function loadCompareRunManifest(manifestFilePath: string): {
28
+ manifest: CompareRunManifestV1;
29
+ baseDirAbs: string;
30
+ manifestFileAbs: string;
31
+ };
32
+ //# sourceMappingURL=compareRunManifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compareRunManifest.d.ts","sourceRoot":"","sources":["../src/compareRunManifest.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,mCAAmC,GAAG,cAAc,GAAG,yCAAyC,CAAC;AAE7G,MAAM,MAAM,oBAAoB,GAC5B;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,yBAAyB,EAAE,mCAAmC,CAAC;CAChE,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,yBAAyB,EAAE,mCAAmC,CAAA;KAAE,CAAC,CAAC;CACtG,CAAC;AAEN,MAAM,MAAM,yBAAyB,GAAG;IACtC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,aAAa,EAAE,CAAC,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,oBAAoB,CAAC;IACzC,IAAI,EAAE,yBAAyB,EAAE,CAAC;CACnC,CAAC;AAmCF;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,gBAAgB,EAAE,MAAM,GACvB;IAAE,QAAQ,EAAE,oBAAoB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CA0BjF"}
@@ -0,0 +1,60 @@
1
+ import { readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { CLI_OPERATIONAL_CODES } from "./cliOperationalCodes.js";
4
+ import { loadSchemaValidator } from "./schemaLoad.js";
5
+ import { TruthLayerError } from "./truthLayerError.js";
6
+ function validateProfileAgainstRuns(m, baseDir) {
7
+ const n = m.runs.length;
8
+ if (m.certificateProfile.mode === "perRun") {
9
+ const e = m.certificateProfile.entries;
10
+ if (e.length !== n) {
11
+ throw new TruthLayerError(CLI_OPERATIONAL_CODES.COMPARE_MANIFEST_SCHEMA_INVALID, `certificateProfile.entries.length (${e.length}) must equal runs.length (${n})`);
12
+ }
13
+ const want = new Set(Array.from({ length: n }, (_, i) => i));
14
+ const got = new Set();
15
+ for (const ent of e) {
16
+ if (got.has(ent.runIndex)) {
17
+ throw new TruthLayerError(CLI_OPERATIONAL_CODES.COMPARE_MANIFEST_SCHEMA_INVALID, `duplicate runIndex ${ent.runIndex} in certificateProfile.entries`);
18
+ }
19
+ got.add(ent.runIndex);
20
+ }
21
+ if (got.size !== n || ![...got].every((i) => want.has(i))) {
22
+ throw new TruthLayerError(CLI_OPERATIONAL_CODES.COMPARE_MANIFEST_SCHEMA_INVALID, `certificateProfile.entries must list runIndex 0..${n - 1} exactly once each`);
23
+ }
24
+ }
25
+ }
26
+ /**
27
+ * Read and validate a compare-run manifest. Resolves `baseDirectory` and validates certificate profile
28
+ * invariants (per-run index coverage) beyond JSON Schema.
29
+ */
30
+ export function loadCompareRunManifest(manifestFilePath) {
31
+ const manifestFileAbs = path.resolve(manifestFilePath);
32
+ let raw;
33
+ try {
34
+ raw = readFileSync(manifestFileAbs, "utf8");
35
+ }
36
+ catch (e) {
37
+ if (e instanceof TruthLayerError)
38
+ throw e;
39
+ const msg = e instanceof Error ? e.message : String(e);
40
+ throw new TruthLayerError(CLI_OPERATIONAL_CODES.COMPARE_MANIFEST_READ_FAILED, msg);
41
+ }
42
+ let parsed;
43
+ try {
44
+ parsed = JSON.parse(raw);
45
+ }
46
+ catch (e) {
47
+ const msg = e instanceof Error ? e.message : String(e);
48
+ throw new TruthLayerError(CLI_OPERATIONAL_CODES.COMPARE_INPUT_JSON_SYNTAX, msg);
49
+ }
50
+ const v = loadSchemaValidator("compare-run-manifest-v1");
51
+ if (!v(parsed)) {
52
+ const detail = JSON.stringify(v.errors ?? []);
53
+ throw new TruthLayerError(CLI_OPERATIONAL_CODES.COMPARE_MANIFEST_SCHEMA_INVALID, detail);
54
+ }
55
+ const m = parsed;
56
+ validateProfileAgainstRuns(m, manifestFileAbs);
57
+ const baseDirAbs = path.resolve(path.dirname(manifestFileAbs), m.baseDirectory);
58
+ return { manifest: m, baseDirAbs, manifestFileAbs };
59
+ }
60
+ //# sourceMappingURL=compareRunManifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compareRunManifest.js","sourceRoot":"","sources":["../src/compareRunManifest.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,22 @@ header h1 {
16
16
  margin: 0 0 1rem;
17
17
  max-width: 52rem;
18
18
  }
19
+ .cli-hint {
20
+ color: #c9d1d9;
21
+ margin: 0 0 1rem;
22
+ max-width: 52rem;
23
+ font-size: 0.95rem;
24
+ }
25
+ .cli-hint code {
26
+ display: block;
27
+ margin-top: 0.35rem;
28
+ padding: 0.45rem 0.6rem;
29
+ background: #161b22;
30
+ border: 1px solid #30363d;
31
+ border-radius: 6px;
32
+ color: #79c0ff;
33
+ font-size: 0.85rem;
34
+ }
19
35
  .tabs {
20
36
  display: flex;
21
37
  gap: 0.5rem;
@@ -171,6 +187,32 @@ tr.selected {
171
187
  border-color: #f0883e;
172
188
  background: #2d1f12;
173
189
  }
190
+ @keyframes tracePulseFocus {
191
+ 0% {
192
+ box-shadow: 0 0 0 0 rgba(240, 136, 62, 0.55);
193
+ }
194
+ 70% {
195
+ box-shadow: 0 0 0 10px rgba(240, 136, 62, 0);
196
+ }
197
+ 100% {
198
+ box-shadow: 0 0 0 0 rgba(240, 136, 62, 0);
199
+ }
200
+ }
201
+ .trace-step.pulse-focus {
202
+ animation: tracePulseFocus 0.85s ease-out 1;
203
+ }
204
+ .focus-target-list {
205
+ display: flex;
206
+ flex-wrap: wrap;
207
+ gap: 0.5rem;
208
+ margin-top: 0.5rem;
209
+ }
210
+ .focus-target-btn {
211
+ text-align: left;
212
+ max-width: 100%;
213
+ font-size: 0.8rem;
214
+ line-height: 1.3;
215
+ }
174
216
  button {
175
217
  cursor: pointer;
176
218
  background: #21262d;
@@ -1,9 +1,16 @@
1
- /* global fetch, URLSearchParams, document, window */
1
+ import {
2
+ DEBUG_CONSOLE_DEFAULT_TAB,
3
+ parseDebugConsoleUrl,
4
+ resolvedFilters,
5
+ serializeDebugConsoleUrl,
6
+ } from "./urlState.js";
2
7
 
3
8
  const state = {
4
9
  nextCursor: null,
5
10
  filterParams: new URLSearchParams(),
6
11
  selected: new Set(),
12
+ currentTab: DEBUG_CONSOLE_DEFAULT_TAB,
13
+ openRunId: null,
7
14
  };
8
15
 
9
16
  function api(path, opts) {
@@ -35,7 +42,6 @@ function buildFilterParamsFromForm() {
35
42
  p.set(k, String(v));
36
43
  }
37
44
  }
38
- // Unchecked checkboxes are omitted from FormData.
39
45
  const includeLoadErrorsEl = form.querySelector('[name="includeLoadErrors"]');
40
46
  if (includeLoadErrorsEl && !includeLoadErrorsEl.checked) {
41
47
  p.set("includeLoadErrors", "false");
@@ -47,6 +53,84 @@ function buildFilterParamsFromForm() {
47
53
  return p;
48
54
  }
49
55
 
56
+ /** @param {Record<string, string>} filters */
57
+ function applyFiltersToForm(filters) {
58
+ const form = document.getElementById("filters");
59
+ const r = resolvedFilters(filters);
60
+ for (const el of form.querySelectorAll("input[name], select[name]")) {
61
+ const name = el.getAttribute("name");
62
+ if (!name || name === "includeLoadErrors" || name === "hasPathFindings") continue;
63
+ if (el instanceof HTMLSelectElement || el instanceof HTMLInputElement) {
64
+ el.value = r[name] ?? "";
65
+ }
66
+ }
67
+ const inc = form.querySelector('[name="includeLoadErrors"]');
68
+ if (inc instanceof HTMLInputElement) {
69
+ inc.checked = r.includeLoadErrors !== "false";
70
+ }
71
+ const hpf = form.querySelector('[name="hasPathFindings"]');
72
+ if (hpf instanceof HTMLInputElement) {
73
+ hpf.checked = r.hasPathFindings === "true";
74
+ }
75
+ }
76
+
77
+ /** @param {URLSearchParams} p */
78
+ function filterObjectFromSearchParams(p) {
79
+ const keys = [
80
+ "loadStatus",
81
+ "workflowId",
82
+ "status",
83
+ "failureCategory",
84
+ "reasonCode",
85
+ "toolId",
86
+ "customerId",
87
+ "timeFrom",
88
+ "timeTo",
89
+ ];
90
+ /** @type {Record<string, string>} */
91
+ const out = {};
92
+ for (const k of keys) {
93
+ const v = p.get(k);
94
+ if (v != null && v !== "") {
95
+ out[k] = v;
96
+ }
97
+ }
98
+ if (p.has("includeLoadErrors")) {
99
+ out.includeLoadErrors = p.get("includeLoadErrors") === "true" ? "true" : "false";
100
+ } else {
101
+ out.includeLoadErrors = "true";
102
+ }
103
+ if (p.has("hasPathFindings")) {
104
+ out.hasPathFindings = p.get("hasPathFindings") === "true" ? "true" : "false";
105
+ } else {
106
+ out.hasPathFindings = "false";
107
+ }
108
+ return out;
109
+ }
110
+
111
+ function pushUrl() {
112
+ const filters = filterObjectFromSearchParams(state.filterParams);
113
+ const q = serializeDebugConsoleUrl({
114
+ tab: state.currentTab,
115
+ run: state.openRunId,
116
+ filters,
117
+ });
118
+ const path = window.location.pathname || "/";
119
+ const url = q ? `${path}?${q}` : path;
120
+ history.replaceState(null, "", url);
121
+ }
122
+
123
+ function setActiveTab(tab) {
124
+ const t = tab === "patterns" || tab === "compare" ? tab : DEBUG_CONSOLE_DEFAULT_TAB;
125
+ state.currentTab = t;
126
+ document.querySelectorAll(".tab").forEach((b) => {
127
+ b.classList.toggle("active", b.dataset.tab === t);
128
+ });
129
+ document.querySelectorAll(".panel").forEach((p) => {
130
+ p.classList.toggle("active", p.id === `panel-${t}`);
131
+ });
132
+ }
133
+
50
134
  async function loadRuns(append) {
51
135
  const p = new URLSearchParams(state.filterParams);
52
136
  p.set("limit", "100");
@@ -117,16 +201,69 @@ function formatVerdictSurface(vs) {
117
201
  </div>`;
118
202
  }
119
203
 
204
+ function focusButtonLabel(t) {
205
+ const v = String(t.value);
206
+ const short = t.rationale && t.rationale.length > 72 ? `${t.rationale.slice(0, 69)}…` : t.rationale || `${t.kind}: ${v}`;
207
+ return `${t.kind}: ${v} — ${short}`;
208
+ }
209
+
210
+ /**
211
+ * @param {HTMLElement} body
212
+ * @param {string} kind
213
+ * @param {string} value
214
+ */
215
+ function scrollToFocusTarget(body, kind, value) {
216
+ for (const el of body.querySelectorAll(".trace-step")) {
217
+ if (kind === "seq" && el.getAttribute("data-seq") === value) {
218
+ pulseEl(el);
219
+ return;
220
+ }
221
+ if (kind === "ingestIndex" && el.getAttribute("data-ingest-index") === value) {
222
+ pulseEl(el);
223
+ return;
224
+ }
225
+ if (kind === "runEventId" && el.getAttribute("data-run-event-id") === value) {
226
+ pulseEl(el);
227
+ return;
228
+ }
229
+ }
230
+ }
231
+
232
+ /** @param {HTMLElement} el */
233
+ function pulseEl(el) {
234
+ el.scrollIntoView({ block: "nearest", behavior: "smooth" });
235
+ el.classList.add("pulse-focus");
236
+ window.setTimeout(() => {
237
+ el.classList.remove("pulse-focus");
238
+ }, 900);
239
+ }
240
+
241
+ /** @param {HTMLElement} body */
242
+ function wireFocusButtons(body) {
243
+ body.addEventListener("click", (ev) => {
244
+ const btn = ev.target && ev.target.closest && ev.target.closest(".focus-target-btn");
245
+ if (!btn || !(btn instanceof HTMLElement)) return;
246
+ const kind = btn.dataset.kind;
247
+ const value = btn.dataset.value;
248
+ if (!kind || value == null) return;
249
+ scrollToFocusTarget(body, kind, value);
250
+ });
251
+ }
252
+
120
253
  async function openDetail(runId) {
121
254
  const drawer = document.getElementById("detail-drawer");
122
255
  const body = document.getElementById("detail-body");
123
256
  const title = document.getElementById("detail-title");
124
257
  drawer.classList.remove("hidden");
258
+ state.openRunId = runId;
259
+ pushUrl();
125
260
  title.textContent = `Run: ${runId}`;
126
261
  body.innerHTML = "<p>Loading…</p>";
127
262
  try {
128
263
  const data = await api(`/api/runs/${encodeURIComponent(runId)}`);
129
264
  if (data.loadStatus === "error") {
265
+ state.openRunId = runId;
266
+ pushUrl();
130
267
  body.innerHTML = `
131
268
  <p class="focus-panel"><strong>Load error</strong> <code>${escapeHtml(data.error.code)}</code></p>
132
269
  <pre class="json-out">${escapeHtml(JSON.stringify(data, null, 2))}</pre>
@@ -135,10 +272,26 @@ async function openDetail(runId) {
135
272
  }
136
273
  let focusHtml = "";
137
274
  const focusSet = new Set();
275
+ /** @type {{ targets: { kind: string; value: string | number; rationale: string }[] }} */
276
+ let focusPayload = { targets: [] };
138
277
  try {
139
278
  const focus = await api(`/api/runs/${encodeURIComponent(runId)}/focus`);
140
- focusHtml = `<div class="focus-panel"><strong>Focus targets (from API)</strong><pre>${escapeHtml(JSON.stringify(focus.targets, null, 2))}</pre></div>`;
141
- for (const t of focus.targets) {
279
+ focusPayload = focus;
280
+ const buttons =
281
+ focus.targets && focus.targets.length
282
+ ? focus.targets
283
+ .map((t) => {
284
+ const v = String(t.value);
285
+ const label = escapeHtml(focusButtonLabel(t));
286
+ return `<button type="button" class="focus-target-btn" data-kind="${escapeHtml(t.kind)}" data-value="${escapeHtml(v)}" title="${escapeHtml(t.rationale)}">${label}</button>`;
287
+ })
288
+ .join("")
289
+ : "";
290
+ focusHtml =
291
+ focus.targets && focus.targets.length
292
+ ? `<div class="focus-panel" role="region" aria-label="Focus targets"><strong>Focus targets</strong><p class="meta">Click a target to scroll to the matching trace step.</p><div class="focus-target-list">${buttons}</div></div>`
293
+ : "";
294
+ for (const t of focus.targets || []) {
142
295
  if (t.kind === "seq") focusSet.add(`seq:${t.value}`);
143
296
  if (t.kind === "ingestIndex") focusSet.add(`ingest:${t.value}`);
144
297
  if (t.kind === "runEventId") focusSet.add(`runEventId:${t.value}`);
@@ -156,7 +309,13 @@ async function openDetail(runId) {
156
309
  (seqKey && focusSet.has(seqKey)) ||
157
310
  focusSet.has(ingestKey) ||
158
311
  (runEvKey && focusSet.has(runEvKey));
159
- return `<div class="trace-step ${hit ? "focus-hit" : ""}" data-idx="${i}">${escapeHtml(JSON.stringify(n))}</div>`;
312
+ const seqAttr = seq != null ? ` data-seq="${escapeHtml(String(seq))}"` : "";
313
+ const ingestAttr = ` data-ingest-index="${escapeHtml(String(n.ingestIndex ?? ""))}"`;
314
+ const rev =
315
+ n.runEventId != null && n.runEventId !== ""
316
+ ? ` data-run-event-id="${escapeHtml(String(n.runEventId))}"`
317
+ : "";
318
+ return `<div class="trace-step ${hit ? "focus-hit" : ""}" data-idx="${i}"${seqAttr}${ingestAttr}${rev}>${escapeHtml(JSON.stringify(n))}</div>`;
160
319
  });
161
320
  const trustHtml =
162
321
  typeof data.runTrustPanelHtml === "string"
@@ -171,14 +330,25 @@ async function openDetail(runId) {
171
330
  <h3>WorkflowResult</h3>
172
331
  <pre class="json-out">${escapeHtml(JSON.stringify(data.workflowResult, null, 2))}</pre>
173
332
  `;
333
+ if (focusPayload.targets && focusPayload.targets.length) {
334
+ wireFocusButtons(body);
335
+ }
336
+ pushUrl();
174
337
  } catch (e) {
175
338
  body.innerHTML = `<p class="focus-panel">Error: ${escapeHtml(e.message)}</p>`;
176
339
  }
177
340
  }
178
341
 
342
+ function closeDetail() {
343
+ document.getElementById("detail-drawer").classList.add("hidden");
344
+ state.openRunId = null;
345
+ pushUrl();
346
+ }
347
+
179
348
  document.getElementById("filters").addEventListener("submit", (ev) => {
180
349
  ev.preventDefault();
181
350
  state.filterParams = buildFilterParamsFromForm();
351
+ pushUrl();
182
352
  loadRuns(false).catch((e) => alert(e.message));
183
353
  });
184
354
 
@@ -186,6 +356,7 @@ document.getElementById("clear-filters").addEventListener("click", () => {
186
356
  document.getElementById("filters").reset();
187
357
  document.querySelector('[name="includeLoadErrors"]').checked = true;
188
358
  state.filterParams = buildFilterParamsFromForm();
359
+ pushUrl();
189
360
  loadRuns(false).catch((e) => alert(e.message));
190
361
  });
191
362
 
@@ -195,10 +366,8 @@ document.getElementById("load-more").addEventListener("click", () => {
195
366
 
196
367
  document.querySelectorAll(".tab").forEach((btn) => {
197
368
  btn.addEventListener("click", () => {
198
- document.querySelectorAll(".tab").forEach((b) => b.classList.remove("active"));
199
- document.querySelectorAll(".panel").forEach((p) => p.classList.remove("active"));
200
- btn.classList.add("active");
201
- document.getElementById(`panel-${btn.dataset.tab}`).classList.add("active");
369
+ setActiveTab(btn.dataset.tab || DEBUG_CONSOLE_DEFAULT_TAB);
370
+ pushUrl();
202
371
  });
203
372
  });
204
373
 
@@ -224,10 +393,11 @@ document.getElementById("run-compare").addEventListener("click", async () => {
224
393
  headers: { "Content-Type": "application/json" },
225
394
  body: JSON.stringify({ runIds: ids }),
226
395
  });
227
- if (typeof data.comparePanelHtml === "string") {
228
- out.innerHTML = data.comparePanelHtml;
396
+ const html = data?.regression?.narrativeHtml;
397
+ if (typeof html === "string" && html.length > 0) {
398
+ out.innerHTML = html;
229
399
  } else {
230
- out.textContent = "comparePanelHtml missing in response.";
400
+ out.textContent = "regression.narrativeHtml missing in response.";
231
401
  }
232
402
  } catch (e) {
233
403
  out.textContent = "";
@@ -236,10 +406,31 @@ document.getElementById("run-compare").addEventListener("click", async () => {
236
406
  });
237
407
 
238
408
  document.getElementById("close-detail").addEventListener("click", () => {
239
- document.getElementById("detail-drawer").classList.add("hidden");
409
+ closeDetail();
240
410
  });
241
411
 
242
- state.filterParams = buildFilterParamsFromForm();
243
- loadRuns(false).catch((e) => {
412
+ window.addEventListener("popstate", () => {
413
+ void applyUrlToUi();
414
+ });
415
+
416
+ async function applyUrlToUi() {
417
+ const parsed = parseDebugConsoleUrl(new URLSearchParams(window.location.search));
418
+ applyFiltersToForm(parsed.filters);
419
+ state.filterParams = buildFilterParamsFromForm();
420
+ setActiveTab(parsed.tab);
421
+ try {
422
+ await loadRuns(false);
423
+ } catch (e) {
424
+ document.getElementById("runs-meta").textContent = `Failed to load: ${e.message}`;
425
+ return;
426
+ }
427
+ if (parsed.run) {
428
+ await openDetail(parsed.run);
429
+ } else {
430
+ closeDetail();
431
+ }
432
+ }
433
+
434
+ void applyUrlToUi().catch((e) => {
244
435
  document.getElementById("runs-meta").textContent = `Failed to load: ${e.message}`;
245
436
  });
@@ -3,13 +3,20 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>workflow-verifier — Debug Console</title>
6
+ <title>agentskeptic — Debug Console</title>
7
7
  <link rel="stylesheet" href="app.css" />
8
8
  </head>
9
9
  <body>
10
10
  <header>
11
- <h1>Debug Console</h1>
12
- <p class="sub">Local corpus triage (127.0.0.1 only). API provides focus targets; no duplicated linking logic in the browser.</p>
11
+ <h1>agentskeptic Debug Console</h1>
12
+ <p class="sub">
13
+ Local corpus triage (127.0.0.1 only). Focus targets come from the API; this UI links them to trace steps—no duplicated
14
+ linking rules in the browser.
15
+ </p>
16
+ <p class="cli-hint">
17
+ After running verification from the CLI, explore saved runs here:
18
+ <code>agentskeptic debug --corpus examples/debug-corpus</code>
19
+ </p>
13
20
  </header>
14
21
 
15
22
  <nav class="tabs">
@@ -74,6 +81,6 @@
74
81
  <div id="detail-body"></div>
75
82
  </section>
76
83
 
77
- <script src="app.js"></script>
84
+ <script type="module" src="app.js"></script>
78
85
  </body>
79
86
  </html>
@@ -0,0 +1,15 @@
1
+ export type DebugConsoleUrlState = {
2
+ tab: string;
3
+ run: string | null;
4
+ filters: Record<string, string>;
5
+ };
6
+
7
+ export const DEBUG_CONSOLE_DEFAULT_TAB: string;
8
+
9
+ export function parseDebugConsoleUrl(searchParams: URLSearchParams): DebugConsoleUrlState;
10
+
11
+ export function defaultFilterRecord(): Record<string, string>;
12
+
13
+ export function resolvedFilters(filters: Record<string, string>): Record<string, string>;
14
+
15
+ export function serializeDebugConsoleUrl(state: DebugConsoleUrlState): string;
@@ -0,0 +1,92 @@
1
+ /** @typedef {{ tab: string, run: string | null, filters: Record<string, string> }} DebugConsoleUrlState */
2
+
3
+ export const DEBUG_CONSOLE_DEFAULT_TAB = "runs";
4
+
5
+ const VALID_TABS = new Set(["runs", "patterns", "compare"]);
6
+
7
+ const FILTER_TEXT_KEYS = [
8
+ "loadStatus",
9
+ "workflowId",
10
+ "status",
11
+ "failureCategory",
12
+ "reasonCode",
13
+ "toolId",
14
+ "customerId",
15
+ "timeFrom",
16
+ "timeTo",
17
+ ];
18
+
19
+ /**
20
+ * @param {URLSearchParams} searchParams
21
+ * @returns {DebugConsoleUrlState}
22
+ */
23
+ export function parseDebugConsoleUrl(searchParams) {
24
+ const rawTab = searchParams.get("tab") || DEBUG_CONSOLE_DEFAULT_TAB;
25
+ const tab = VALID_TABS.has(rawTab) ? rawTab : DEBUG_CONSOLE_DEFAULT_TAB;
26
+ const runRaw = searchParams.get("run");
27
+ const run = runRaw != null && runRaw !== "" ? runRaw : null;
28
+
29
+ /** @type {Record<string, string>} */
30
+ const filters = {};
31
+ for (const k of FILTER_TEXT_KEYS) {
32
+ const v = searchParams.get(k);
33
+ if (v != null && v !== "") {
34
+ filters[k] = v;
35
+ }
36
+ }
37
+
38
+ if (searchParams.has("includeLoadErrors")) {
39
+ filters.includeLoadErrors = searchParams.get("includeLoadErrors") === "true" ? "true" : "false";
40
+ }
41
+ if (searchParams.has("hasPathFindings")) {
42
+ filters.hasPathFindings = searchParams.get("hasPathFindings") === "true" ? "true" : "false";
43
+ }
44
+
45
+ return { tab, run, filters };
46
+ }
47
+
48
+ /**
49
+ * Default filter semantics aligned with debug-ui/app.js (includeLoadErrors checked by default).
50
+ * @returns {Record<string, string>}
51
+ */
52
+ export function defaultFilterRecord() {
53
+ return { includeLoadErrors: "true" };
54
+ }
55
+
56
+ /**
57
+ * Merge URL-derived filters with defaults for form population.
58
+ * @param {Record<string, string>} filters
59
+ */
60
+ export function resolvedFilters(filters) {
61
+ const out = { ...defaultFilterRecord(), ...filters };
62
+ if (!("hasPathFindings" in out)) {
63
+ out.hasPathFindings = "false";
64
+ }
65
+ return out;
66
+ }
67
+
68
+ /**
69
+ * @param {DebugConsoleUrlState} state
70
+ * @returns {string} query string without leading "?"
71
+ */
72
+ export function serializeDebugConsoleUrl(state) {
73
+ const p = new URLSearchParams();
74
+ p.set("tab", state.tab || DEBUG_CONSOLE_DEFAULT_TAB);
75
+ if (state.run) {
76
+ p.set("run", state.run);
77
+ }
78
+ const f = state.filters || {};
79
+ for (const k of FILTER_TEXT_KEYS) {
80
+ const v = f[k];
81
+ if (v != null && v !== "") {
82
+ p.set(k, String(v));
83
+ }
84
+ }
85
+ if (f.includeLoadErrors === "false") {
86
+ p.set("includeLoadErrors", "false");
87
+ }
88
+ if (f.hasPathFindings === "true") {
89
+ p.set("hasPathFindings", "true");
90
+ }
91
+ return p.toString();
92
+ }
@@ -1,4 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
+ import assert from "node:assert/strict";
2
3
  import { copyFileSync, mkdirSync, readFileSync, writeFileSync, rmSync, mkdtempSync } from "node:fs";
3
4
  import { join } from "node:path";
4
5
  import { tmpdir } from "node:os";
@@ -6,19 +7,44 @@ import { describe, expect, it } from "vitest";
6
7
  import { CLI_OPERATIONAL_CODES } from "./cliOperationalCodes.js";
7
8
  import { DEBUG_CORPUS_CODES, isPathUnderRoot, listCorpusRunIds, loadAllCorpusRuns, loadCorpusRun, resolveCorpusRootReal, } from "./debugCorpus.js";
8
9
  import { WORKFLOW_RESULT_RUN_LEVEL_CODES_MISMATCH_MESSAGE } from "./runLevelDriftMessages.js";
9
- import { fileURLToPath } from "node:url";
10
- const root = join(fileURLToPath(import.meta.url), "..", "..");
10
+ import { monorepoRootForVitest } from "./vitestMonorepoRoot.js";
11
+ const root = monorepoRootForVitest(import.meta.url);
11
12
  const negativeRoot = join(root, "test", "fixtures", "corpus-negative");
12
13
  const runOkDir = join(root, "examples", "debug-corpus", "run_ok");
13
14
  describe("debugCorpus", () => {
14
- it("examples/debug-corpus has one sealed run (run_ok)", () => {
15
+ it("examples/debug-corpus ships the curated demo library (5 sealed ok runs)", () => {
15
16
  const corpus = join(root, "examples", "debug-corpus");
16
17
  const outcomes = loadAllCorpusRuns(corpus);
17
- expect(outcomes).toHaveLength(1);
18
+ const runIds = listCorpusRunIds(corpus);
19
+ assert.equal(outcomes.length, 5, `expected 5 runs under ${corpus}; listCorpusRunIds=[${runIds.join(", ")}] cwd=${process.cwd()}`);
20
+ const errLines = [];
21
+ for (const o of outcomes) {
22
+ if (o.loadStatus === "error") {
23
+ errLines.push(`${o.runId}: ${o.error.code} – ${o.error.message}`);
24
+ }
25
+ }
26
+ assert.equal(errLines.length, 0, `load failures (integrity/schema/path) under ${corpus}: ${errLines.join(" | ")}`);
18
27
  const ok = outcomes.filter((o) => o.loadStatus === "ok");
19
- expect(ok).toHaveLength(1);
20
- expect(ok[0].runId).toBe("run_ok");
21
- expect(ok[0].agentRunRecord.workflowId).toBe("wf_complete");
28
+ expect(ok).toHaveLength(5);
29
+ const ids = new Set(ok.map((o) => o.runId));
30
+ for (const id of [
31
+ "run_ok",
32
+ "run_value_mismatch",
33
+ "run_row_absent",
34
+ "run_path_nonempty",
35
+ "run_complete_b",
36
+ ]) {
37
+ expect(ids.has(id)).toBe(true);
38
+ }
39
+ const withFailureEvidence = ok.filter((o) => o.workflowResult.workflowTruthReport.failureAnalysis !== null &&
40
+ o.workflowResult.workflowTruthReport.failureAnalysis.evidence.length > 0);
41
+ expect(withFailureEvidence).toHaveLength(2);
42
+ expect(withFailureEvidence.map((o) => o.runId).sort()).toEqual(["run_row_absent", "run_value_mismatch"]);
43
+ const pathFindings = ok.filter((o) => o.workflowResult.workflowTruthReport.executionPathFindings.length > 0);
44
+ expect(pathFindings).toHaveLength(1);
45
+ expect(pathFindings[0].runId).toBe("run_path_nonempty");
46
+ const wfComplete = ok.filter((o) => o.workflowResult.workflowId === "wf_complete");
47
+ expect(wfComplete.length).toBeGreaterThanOrEqual(3);
22
48
  });
23
49
  it("corpus-negative fixtures produce expected error codes", () => {
24
50
  const rootReal = resolveCorpusRootReal(negativeRoot);