@oscharko-dev/keiko 0.2.0-beta.4 → 0.2.0-beta.5

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 (228) hide show
  1. package/dist/ui/csp-hashes.json +18 -18
  2. package/dist/ui/static/404.html +1 -1
  3. package/dist/ui/static/__next.__PAGE__.txt +2 -2
  4. package/dist/ui/static/__next._full.txt +3 -3
  5. package/dist/ui/static/__next._head.txt +1 -1
  6. package/dist/ui/static/__next._index.txt +2 -2
  7. package/dist/ui/static/__next._tree.txt +2 -2
  8. package/dist/ui/static/_next/static/chunks/1m-kvwm6_90_3.css +1 -0
  9. package/dist/ui/static/_next/static/chunks/2ngm8iwdb1cbv.js +106 -0
  10. package/dist/ui/static/_not-found/__next._full.txt +2 -2
  11. package/dist/ui/static/_not-found/__next._head.txt +1 -1
  12. package/dist/ui/static/_not-found/__next._index.txt +2 -2
  13. package/dist/ui/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
  14. package/dist/ui/static/_not-found/__next._not-found.txt +1 -1
  15. package/dist/ui/static/_not-found/__next._tree.txt +2 -2
  16. package/dist/ui/static/_not-found.html +1 -1
  17. package/dist/ui/static/_not-found.txt +2 -2
  18. package/dist/ui/static/index.html +1 -1
  19. package/dist/ui/static/index.txt +3 -3
  20. package/dist/ui/static/launch/__next._full.txt +3 -3
  21. package/dist/ui/static/launch/__next._head.txt +1 -1
  22. package/dist/ui/static/launch/__next._index.txt +2 -2
  23. package/dist/ui/static/launch/__next._tree.txt +2 -2
  24. package/dist/ui/static/launch/__next.launch.__PAGE__.txt +2 -2
  25. package/dist/ui/static/launch/__next.launch.txt +1 -1
  26. package/dist/ui/static/launch.html +1 -1
  27. package/dist/ui/static/launch.txt +3 -3
  28. package/dist/ui/static/local-knowledge/__next._full.txt +3 -3
  29. package/dist/ui/static/local-knowledge/__next._head.txt +1 -1
  30. package/dist/ui/static/local-knowledge/__next._index.txt +2 -2
  31. package/dist/ui/static/local-knowledge/__next._tree.txt +2 -2
  32. package/dist/ui/static/local-knowledge/__next.local-knowledge.__PAGE__.txt +2 -2
  33. package/dist/ui/static/local-knowledge/__next.local-knowledge.txt +1 -1
  34. package/dist/ui/static/local-knowledge/capsule/__next._full.txt +2 -2
  35. package/dist/ui/static/local-knowledge/capsule/__next._head.txt +1 -1
  36. package/dist/ui/static/local-knowledge/capsule/__next._index.txt +2 -2
  37. package/dist/ui/static/local-knowledge/capsule/__next._tree.txt +2 -2
  38. package/dist/ui/static/local-knowledge/capsule/__next.local-knowledge.capsule.__PAGE__.txt +1 -1
  39. package/dist/ui/static/local-knowledge/capsule/__next.local-knowledge.capsule.txt +1 -1
  40. package/dist/ui/static/local-knowledge/capsule/__next.local-knowledge.txt +1 -1
  41. package/dist/ui/static/local-knowledge/capsule.html +1 -1
  42. package/dist/ui/static/local-knowledge/capsule.txt +2 -2
  43. package/dist/ui/static/local-knowledge.html +1 -1
  44. package/dist/ui/static/local-knowledge.txt +3 -3
  45. package/dist/ui/static/memoriaviva/__next._full.txt +2 -2
  46. package/dist/ui/static/memoriaviva/__next._head.txt +1 -1
  47. package/dist/ui/static/memoriaviva/__next._index.txt +2 -2
  48. package/dist/ui/static/memoriaviva/__next._tree.txt +2 -2
  49. package/dist/ui/static/memoriaviva/__next.memoriaviva.__PAGE__.txt +1 -1
  50. package/dist/ui/static/memoriaviva/__next.memoriaviva.txt +1 -1
  51. package/dist/ui/static/memoriaviva/consolidation/__next._full.txt +2 -2
  52. package/dist/ui/static/memoriaviva/consolidation/__next._head.txt +1 -1
  53. package/dist/ui/static/memoriaviva/consolidation/__next._index.txt +2 -2
  54. package/dist/ui/static/memoriaviva/consolidation/__next._tree.txt +2 -2
  55. package/dist/ui/static/memoriaviva/consolidation/__next.memoriaviva.consolidation.__PAGE__.txt +1 -1
  56. package/dist/ui/static/memoriaviva/consolidation/__next.memoriaviva.consolidation.txt +1 -1
  57. package/dist/ui/static/memoriaviva/consolidation/__next.memoriaviva.txt +1 -1
  58. package/dist/ui/static/memoriaviva/consolidation.html +1 -1
  59. package/dist/ui/static/memoriaviva/consolidation.txt +2 -2
  60. package/dist/ui/static/memoriaviva/detail/__next._full.txt +2 -2
  61. package/dist/ui/static/memoriaviva/detail/__next._head.txt +1 -1
  62. package/dist/ui/static/memoriaviva/detail/__next._index.txt +2 -2
  63. package/dist/ui/static/memoriaviva/detail/__next._tree.txt +2 -2
  64. package/dist/ui/static/memoriaviva/detail/__next.memoriaviva.detail.__PAGE__.txt +1 -1
  65. package/dist/ui/static/memoriaviva/detail/__next.memoriaviva.detail.txt +1 -1
  66. package/dist/ui/static/memoriaviva/detail/__next.memoriaviva.txt +1 -1
  67. package/dist/ui/static/memoriaviva/detail.html +1 -1
  68. package/dist/ui/static/memoriaviva/detail.txt +2 -2
  69. package/dist/ui/static/memoriaviva/review-queue/__next._full.txt +2 -2
  70. package/dist/ui/static/memoriaviva/review-queue/__next._head.txt +1 -1
  71. package/dist/ui/static/memoriaviva/review-queue/__next._index.txt +2 -2
  72. package/dist/ui/static/memoriaviva/review-queue/__next._tree.txt +2 -2
  73. package/dist/ui/static/memoriaviva/review-queue/__next.memoriaviva.review-queue.__PAGE__.txt +1 -1
  74. package/dist/ui/static/memoriaviva/review-queue/__next.memoriaviva.review-queue.txt +1 -1
  75. package/dist/ui/static/memoriaviva/review-queue/__next.memoriaviva.txt +1 -1
  76. package/dist/ui/static/memoriaviva/review-queue.html +1 -1
  77. package/dist/ui/static/memoriaviva/review-queue.txt +2 -2
  78. package/dist/ui/static/memoriaviva.html +1 -1
  79. package/dist/ui/static/memoriaviva.txt +2 -2
  80. package/node_modules/@oscharko-dev/keiko-cli/dist/.tsbuildinfo +1 -1
  81. package/node_modules/@oscharko-dev/keiko-cli/dist/run.d.ts +3 -1
  82. package/node_modules/@oscharko-dev/keiko-cli/dist/run.d.ts.map +1 -1
  83. package/node_modules/@oscharko-dev/keiko-cli/dist/run.js +73 -41
  84. package/node_modules/@oscharko-dev/keiko-cli/package.json +1 -1
  85. package/node_modules/@oscharko-dev/keiko-contracts/dist/.tsbuildinfo +1 -1
  86. package/node_modules/@oscharko-dev/keiko-contracts/dist/index.d.ts +1 -1
  87. package/node_modules/@oscharko-dev/keiko-contracts/dist/index.js +1 -1
  88. package/node_modules/@oscharko-dev/keiko-contracts/dist/qualityIntelligence/bffWire.d.ts +2 -0
  89. package/node_modules/@oscharko-dev/keiko-contracts/dist/qualityIntelligence/bffWire.d.ts.map +1 -1
  90. package/node_modules/@oscharko-dev/keiko-contracts/package.json +1 -1
  91. package/node_modules/@oscharko-dev/keiko-evaluations/dist/.tsbuildinfo +1 -1
  92. package/node_modules/@oscharko-dev/keiko-evaluations/package.json +1 -1
  93. package/node_modules/@oscharko-dev/keiko-evidence/dist/.tsbuildinfo +1 -1
  94. package/node_modules/@oscharko-dev/keiko-evidence/dist/index.d.ts +1 -1
  95. package/node_modules/@oscharko-dev/keiko-evidence/dist/index.d.ts.map +1 -1
  96. package/node_modules/@oscharko-dev/keiko-evidence/dist/index.js +1 -1
  97. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/index.d.ts +3 -3
  98. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/index.d.ts.map +1 -1
  99. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/index.js +1 -1
  100. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/manifestSchema.d.ts +18 -1
  101. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/manifestSchema.d.ts.map +1 -1
  102. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/store.d.ts +33 -0
  103. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/store.d.ts.map +1 -1
  104. package/node_modules/@oscharko-dev/keiko-evidence/dist/qualityIntelligence/store.js +69 -0
  105. package/node_modules/@oscharko-dev/keiko-evidence/package.json +1 -1
  106. package/node_modules/@oscharko-dev/keiko-harness/dist/.tsbuildinfo +1 -1
  107. package/node_modules/@oscharko-dev/keiko-harness/package.json +1 -1
  108. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/.tsbuildinfo +1 -1
  109. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/conversation/index.d.ts +0 -1
  110. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/conversation/index.d.ts.map +1 -1
  111. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/conversation/index.js +0 -1
  112. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/conversation/model-gateway-answer-generator.d.ts.map +1 -1
  113. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/conversation/model-gateway-answer-generator.js +7 -1
  114. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/conversation/types.d.ts.map +1 -1
  115. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/conversation/types.js +2 -3
  116. package/node_modules/@oscharko-dev/keiko-local-knowledge/package.json +1 -1
  117. package/node_modules/@oscharko-dev/keiko-memory-capture/package.json +1 -1
  118. package/node_modules/@oscharko-dev/keiko-memory-consolidation/package.json +1 -1
  119. package/node_modules/@oscharko-dev/keiko-memory-governance/package.json +1 -1
  120. package/node_modules/@oscharko-dev/keiko-memory-retrieval/package.json +1 -1
  121. package/node_modules/@oscharko-dev/keiko-memory-vault/dist/.tsbuildinfo +1 -1
  122. package/node_modules/@oscharko-dev/keiko-memory-vault/package.json +1 -1
  123. package/node_modules/@oscharko-dev/keiko-model-gateway/dist/.tsbuildinfo +1 -1
  124. package/node_modules/@oscharko-dev/keiko-model-gateway/dist/normalize.d.ts +1 -0
  125. package/node_modules/@oscharko-dev/keiko-model-gateway/dist/normalize.d.ts.map +1 -1
  126. package/node_modules/@oscharko-dev/keiko-model-gateway/dist/normalize.js +22 -1
  127. package/node_modules/@oscharko-dev/keiko-model-gateway/dist/openai-adapter.d.ts.map +1 -1
  128. package/node_modules/@oscharko-dev/keiko-model-gateway/dist/openai-adapter.js +18 -4
  129. package/node_modules/@oscharko-dev/keiko-model-gateway/package.json +1 -1
  130. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/.tsbuildinfo +1 -1
  131. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/export/adapters/alm.d.ts.map +1 -1
  132. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/export/adapters/alm.js +29 -30
  133. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/export/adapters/markdown.d.ts.map +1 -1
  134. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/export/adapters/markdown.js +29 -5
  135. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/export/adapters/qtest.d.ts.map +1 -1
  136. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/export/adapters/qtest.js +29 -29
  137. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/export/adapters/spreadsheetSafeCsv.d.ts +2 -1
  138. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/export/adapters/spreadsheetSafeCsv.d.ts.map +1 -1
  139. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/export/adapters/spreadsheetSafeCsv.js +40 -2
  140. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/export/adapters/xray.d.ts.map +1 -1
  141. package/node_modules/@oscharko-dev/keiko-quality-intelligence/dist/export/adapters/xray.js +6 -2
  142. package/node_modules/@oscharko-dev/keiko-quality-intelligence/package.json +1 -1
  143. package/node_modules/@oscharko-dev/keiko-sdk/dist/.tsbuildinfo +1 -1
  144. package/node_modules/@oscharko-dev/keiko-sdk/package.json +1 -1
  145. package/node_modules/@oscharko-dev/keiko-security/dist/.tsbuildinfo +1 -1
  146. package/node_modules/@oscharko-dev/keiko-security/package.json +1 -1
  147. package/node_modules/@oscharko-dev/keiko-server/dist/.tsbuildinfo +1 -1
  148. package/node_modules/@oscharko-dev/keiko-server/dist/assistant-response.d.ts +6 -0
  149. package/node_modules/@oscharko-dev/keiko-server/dist/assistant-response.d.ts.map +1 -0
  150. package/node_modules/@oscharko-dev/keiko-server/dist/assistant-response.js +12 -0
  151. package/node_modules/@oscharko-dev/keiko-server/dist/chat-handlers.d.ts +1 -1
  152. package/node_modules/@oscharko-dev/keiko-server/dist/chat-handlers.d.ts.map +1 -1
  153. package/node_modules/@oscharko-dev/keiko-server/dist/chat-handlers.js +11 -4
  154. package/node_modules/@oscharko-dev/keiko-server/dist/chat-stream-handlers.js +1 -1
  155. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-orchestrator.d.ts +0 -1
  156. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-orchestrator.d.ts.map +1 -1
  157. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-orchestrator.js +222 -18
  158. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-qa-hybrid.d.ts.map +1 -1
  159. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-qa-hybrid.js +3 -1
  160. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-qa-multi-source.d.ts.map +1 -1
  161. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-qa-multi-source.js +3 -1
  162. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-qa.d.ts.map +1 -1
  163. package/node_modules/@oscharko-dev/keiko-server/dist/grounded-qa.js +3 -1
  164. package/node_modules/@oscharko-dev/keiko-server/dist/local-knowledge-grounded-qa.d.ts.map +1 -1
  165. package/node_modules/@oscharko-dev/keiko-server/dist/local-knowledge-grounded-qa.js +4 -1
  166. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/connectorErrors.d.ts.map +1 -1
  167. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/connectorErrors.js +11 -8
  168. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/editRoutes.d.ts.map +1 -1
  169. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/editRoutes.js +1 -0
  170. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/exportRoutes.d.ts.map +1 -1
  171. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/exportRoutes.js +89 -30
  172. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/generationPort.d.ts.map +1 -1
  173. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/generationPort.js +12 -1
  174. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/handoffErrors.d.ts +1 -1
  175. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/handoffErrors.d.ts.map +1 -1
  176. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/handoffErrors.js +1 -0
  177. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/handoffRoutes.d.ts.map +1 -1
  178. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/handoffRoutes.js +41 -9
  179. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/index.d.ts +1 -0
  180. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/index.d.ts.map +1 -1
  181. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/index.js +2 -0
  182. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/judgePort.d.ts.map +1 -1
  183. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/judgePort.js +17 -3
  184. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/retentionRoutes.d.ts +5 -0
  185. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/retentionRoutes.d.ts.map +1 -0
  186. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/retentionRoutes.js +70 -0
  187. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/reviewRoutes.d.ts.map +1 -1
  188. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/reviewRoutes.js +6 -2
  189. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/reviewStore.d.ts +25 -4
  190. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/reviewStore.d.ts.map +1 -1
  191. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/reviewStore.js +72 -9
  192. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/runIngestion.d.ts.map +1 -1
  193. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/runIngestion.js +46 -3
  194. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/runRegistry.d.ts.map +1 -1
  195. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/runRegistry.js +2 -0
  196. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/uiRoutes.d.ts.map +1 -1
  197. package/node_modules/@oscharko-dev/keiko-server/dist/qualityIntelligence/uiRoutes.js +12 -2
  198. package/node_modules/@oscharko-dev/keiko-server/dist/routes.d.ts.map +1 -1
  199. package/node_modules/@oscharko-dev/keiko-server/dist/routes.js +4 -1
  200. package/node_modules/@oscharko-dev/keiko-server/dist/store-handlers.d.ts.map +1 -1
  201. package/node_modules/@oscharko-dev/keiko-server/dist/store-handlers.js +4 -1
  202. package/node_modules/@oscharko-dev/keiko-server/package.json +1 -1
  203. package/node_modules/@oscharko-dev/keiko-tools/dist/.tsbuildinfo +1 -1
  204. package/node_modules/@oscharko-dev/keiko-tools/package.json +1 -1
  205. package/node_modules/@oscharko-dev/keiko-verification/dist/.tsbuildinfo +1 -1
  206. package/node_modules/@oscharko-dev/keiko-verification/package.json +1 -1
  207. package/node_modules/@oscharko-dev/keiko-workflows/dist/.tsbuildinfo +1 -1
  208. package/node_modules/@oscharko-dev/keiko-workflows/dist/planner/anchors.d.ts.map +1 -1
  209. package/node_modules/@oscharko-dev/keiko-workflows/dist/planner/anchors.js +68 -0
  210. package/node_modules/@oscharko-dev/keiko-workflows/package.json +1 -1
  211. package/node_modules/@oscharko-dev/keiko-workspace/dist/.tsbuildinfo +1 -1
  212. package/node_modules/@oscharko-dev/keiko-workspace/dist/discovery.d.ts.map +1 -1
  213. package/node_modules/@oscharko-dev/keiko-workspace/dist/discovery.js +34 -2
  214. package/node_modules/@oscharko-dev/keiko-workspace/dist/realpath.d.ts +1 -0
  215. package/node_modules/@oscharko-dev/keiko-workspace/dist/realpath.d.ts.map +1 -1
  216. package/node_modules/@oscharko-dev/keiko-workspace/dist/realpath.js +2 -2
  217. package/node_modules/@oscharko-dev/keiko-workspace/dist/repoSearchMatchers.d.ts.map +1 -1
  218. package/node_modules/@oscharko-dev/keiko-workspace/dist/repoSearchMatchers.js +68 -0
  219. package/node_modules/@oscharko-dev/keiko-workspace/package.json +1 -1
  220. package/package.json +1 -1
  221. package/dist/ui/static/_next/static/chunks/1t04tfgin0v_g.js +0 -106
  222. package/dist/ui/static/_next/static/chunks/3wr_35f2vg6sd.css +0 -1
  223. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/conversation/scripted-answer-generator.d.ts +0 -6
  224. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/conversation/scripted-answer-generator.d.ts.map +0 -1
  225. package/node_modules/@oscharko-dev/keiko-local-knowledge/dist/conversation/scripted-answer-generator.js +0 -49
  226. /package/dist/ui/static/_next/static/{Hb6brrvZY2nZtizQlYhVt → hR2gBQqCDKdPdSxBqZhqv}/_buildManifest.js +0 -0
  227. /package/dist/ui/static/_next/static/{Hb6brrvZY2nZtizQlYhVt → hR2gBQqCDKdPdSxBqZhqv}/_clientMiddlewareManifest.js +0 -0
  228. /package/dist/ui/static/_next/static/{Hb6brrvZY2nZtizQlYhVt → hR2gBQqCDKdPdSxBqZhqv}/_ssgManifest.js +0 -0
@@ -7,10 +7,31 @@
7
7
  // nothing is approved by default (#282 AC). All reads tolerate a missing artifact.
8
8
  import { createNodeContainedJsonArtifactStore, } from "@oscharko-dev/keiko-evidence";
9
9
  import { QualityIntelligence } from "@oscharko-dev/keiko-contracts";
10
+ import { QualityIntelligenceReview } from "@oscharko-dev/keiko-quality-intelligence";
11
+ /**
12
+ * Redact a reviewer label before it lands in the persisted (append-only) audit log. The label is
13
+ * user-supplied, so a secret-shaped value must be scrubbed at persist time — the `.review.json`
14
+ * companion otherwise bypasses the QI persist redactor (Issue #282 FIX M1). The live redactor maps
15
+ * string→string; the non-string fallback keeps the type honest without `any`.
16
+ */
17
+ const redactLabel = (label, redact) => {
18
+ const redacted = redact(label);
19
+ return typeof redacted === "string" ? redacted : label;
20
+ };
10
21
  export const QI_REVIEW_SCHEMA_VERSION = 1;
11
22
  const REVIEW_SUFFIX = ".review.json";
12
23
  const REVIEW_STATES = new Set(QualityIntelligence.QUALITY_INTELLIGENCE_REVIEW_STATES);
13
24
  const isReviewState = (value) => typeof value === "string" && REVIEW_STATES.has(value);
25
+ // FIX L1 (Issue #282) — candidate ids are arbitrary strings. Building the candidate-state map over a
26
+ // null-prototype object means a candidate literally named `__proto__` / `constructor` cannot collide
27
+ // with an Object.prototype member (no prototype-pollution, no spurious own-key reads). Behaviour is
28
+ // identical for normal ids.
29
+ const toNullProtoStates = (source) => {
30
+ const target = Object.create(null);
31
+ for (const [id, state] of Object.entries(source))
32
+ target[id] = state;
33
+ return target;
34
+ };
14
35
  const parseArtifact = (value) => {
15
36
  if (typeof value !== "object" || value === null)
16
37
  return undefined;
@@ -21,7 +42,10 @@ const parseArtifact = (value) => {
21
42
  return undefined;
22
43
  if (typeof record.candidateStates !== "object" || record.candidateStates === null)
23
44
  return undefined;
24
- return value;
45
+ // Rehydrate candidateStates onto a null-proto object so a persisted `__proto__`/`constructor`
46
+ // candidate id round-trips as an own key rather than the prototype member it was parsed into.
47
+ const candidateStates = toNullProtoStates(record.candidateStates);
48
+ return { ...value, candidateStates };
25
49
  };
26
50
  const storeFor = (evidenceDir) => createNodeContainedJsonArtifactStore(evidenceDir, REVIEW_SUFFIX, { parse: parseArtifact });
27
51
  export const loadRunReviewState = (runId, evidenceDir) => storeFor(evidenceDir).load(runId);
@@ -38,17 +62,52 @@ const ACTION_TARGET = {
38
62
  reopen: "open",
39
63
  withdraw: "withdrawn",
40
64
  };
65
+ // FIX A (Issue #282) — legal-transition predicate, resurrecting the audited pure terminal-state
66
+ // check from keiko-quality-intelligence. A transition from `from` via `action` (target `to`) is
67
+ // legal iff:
68
+ // * to !== from (reject every no-op, including reopen-from-open), AND
69
+ // * action === "reopen" OR the source state is not terminal.
70
+ // reopen is the deliberate, audited undo from any non-open state (changes-requested / approved /
71
+ // rejected / withdrawn → open). Every other action (approve / reject / request-changes / withdraw)
72
+ // is legal only from a non-terminal state — this blocks silent illegal flips (approve a rejected,
73
+ // reject an approved) while keeping re-decision possible via an explicit reopen.
74
+ const isLegalTransition = (from, action) => {
75
+ const to = ACTION_TARGET[action];
76
+ if (to === from)
77
+ return false;
78
+ return action === "reopen" || !QualityIntelligenceReview.isTerminalReviewState(from);
79
+ };
80
+ /**
81
+ * Thrown by `applyReviewDecision` when the requested action is not a legal transition from the
82
+ * current review state. Nothing is persisted and no audit entry is appended — the append-only log
83
+ * never attests a transition the audited domain declares illegal. The route maps this to a 409.
84
+ */
85
+ export class QualityIntelligenceReviewTransitionRejected extends Error {
86
+ from;
87
+ action;
88
+ toState;
89
+ constructor(from, action, toState) {
90
+ super(`Review transition ${from} → ${action} (${toState}) is not permitted.`);
91
+ this.name = "QualityIntelligenceReviewTransitionRejected";
92
+ this.from = from;
93
+ this.action = action;
94
+ this.toState = toState;
95
+ }
96
+ }
41
97
  const emptyArtifact = (runId, now) => ({
42
98
  qiReviewSchemaVersion: QI_REVIEW_SCHEMA_VERSION,
43
99
  runId,
44
100
  runState: "open",
45
- candidateStates: {},
101
+ candidateStates: toNullProtoStates({}),
46
102
  auditLog: [],
47
103
  lastUpdatedAt: now,
48
104
  });
49
105
  /**
50
- * Apply a review decision and persist the updated artifact. Pure transition + append-only audit
51
- * entry; returns the new artifact. The caller is responsible for authorising the action.
106
+ * Apply a review decision and persist the updated artifact. Validates transition legality first
107
+ * (FIX A): an illegal transition throws `QualityIntelligenceReviewTransitionRejected` and persists
108
+ * nothing — no audit entry is ever appended for a rejected transition. On success, appends an
109
+ * append-only audit entry (with a redacted reviewer label, FIX M1) and returns the new artifact.
110
+ * The caller is responsible for authorising the action.
52
111
  */
53
112
  export const applyReviewDecision = (input) => {
54
113
  const current = loadRunReviewState(input.runId, input.evidenceDir) ?? emptyArtifact(input.runId, input.now);
@@ -57,15 +116,18 @@ export const applyReviewDecision = (input) => {
57
116
  const fromState = isCandidate
58
117
  ? candidateReviewStateOf(current, input.candidateId)
59
118
  : current.runState;
119
+ if (!isLegalTransition(fromState, input.action)) {
120
+ throw new QualityIntelligenceReviewTransitionRejected(fromState, input.action, toState);
121
+ }
60
122
  const candidateStates = isCandidate
61
- ? { ...current.candidateStates, [input.candidateId]: toState }
123
+ ? Object.assign(toNullProtoStates(current.candidateStates), { [input.candidateId]: toState })
62
124
  : current.candidateStates;
63
125
  const audit = {
64
126
  at: input.now,
65
127
  action: input.action,
66
128
  scope: input.scope,
67
129
  ...(isCandidate ? { candidateId: input.candidateId } : {}),
68
- reviewerLabel: input.reviewerLabel,
130
+ reviewerLabel: redactLabel(input.reviewerLabel, input.redact),
69
131
  fromState,
70
132
  toState,
71
133
  };
@@ -82,8 +144,9 @@ export const applyReviewDecision = (input) => {
82
144
  };
83
145
  /**
84
146
  * Append an append-only `edit` audit entry for an inline candidate edit. Review state is NOT
85
- * transitioned — `fromState`/`toState` are the candidate's existing review state. Persists and
86
- * returns the updated review artifact (created empty on first use).
147
+ * transitioned — `fromState`/`toState` are the candidate's existing review state. The reviewer label
148
+ * is redacted before persist (FIX M1). Persists and returns the updated review artifact (created
149
+ * empty on first use).
87
150
  */
88
151
  export const appendEditAudit = (input) => {
89
152
  const current = loadRunReviewState(input.runId, input.evidenceDir) ?? emptyArtifact(input.runId, input.now);
@@ -93,7 +156,7 @@ export const appendEditAudit = (input) => {
93
156
  action: "edit",
94
157
  scope: "candidate",
95
158
  candidateId: input.candidateId,
96
- reviewerLabel: input.reviewerLabel,
159
+ reviewerLabel: redactLabel(input.reviewerLabel, input.redact),
97
160
  fromState: state,
98
161
  toState: state,
99
162
  };
@@ -1 +1 @@
1
- {"version":3,"file":"runIngestion.d.ts","sourceRoot":"","sources":["../../src/qualityIntelligence/runIngestion.ts"],"names":[],"mappings":"AASA,OAAO,EAAuB,KAAK,mBAAmB,IAAI,EAAE,EAAE,MAAM,+BAA+B,CAAC;AAqBpG,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AACrF,OAAO,KAAK,EACV,+BAA+B,EAC/B,kCAAkC,EACnC,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAyC9F,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,SAAgB,IAAI,EAAE,MAAM,CAAC;gBACjB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,+BAA+B,CAAC,MAAM,CAAC,CAAC;IACvD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,+BAA+B,CAAC,MAAM,CAAC,CAAC;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,iCAAiC,EAAE,CAAC;IACpE,QAAQ,CAAC,aAAa,EAAE,SAAS,+BAA+B,EAAE,CAAC;IACnE,QAAQ,CAAC,cAAc,EAAE;QACvB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;QACxC,QAAQ,CAAC,cAAc,EAAE,EAAE,CAAC,iCAAiC,CAAC;KAC/D,CAAC;IACF,QAAQ,CAAC,eAAe,EAAE,SAAS,eAAe,EAAE,CAAC;IACrD,yFAAyF;IACzF,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,SAAS,eAAe,EAAE,CAAC;CACrD;AAoyBD,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,OAAO,EAAE,kCAAkC,CAAC;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,gHAAgH;IAChH,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACvD;;;OAGG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAC/D;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;CAC5D;AAsED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,iBAAiB,CAiDtF"}
1
+ {"version":3,"file":"runIngestion.d.ts","sourceRoot":"","sources":["../../src/qualityIntelligence/runIngestion.ts"],"names":[],"mappings":"AAUA,OAAO,EAAuB,KAAK,mBAAmB,IAAI,EAAE,EAAE,MAAM,+BAA+B,CAAC;AAqBpG,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AACrF,OAAO,KAAK,EACV,+BAA+B,EAC/B,kCAAkC,EACnC,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAyC9F,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,SAAgB,IAAI,EAAE,MAAM,CAAC;gBACjB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,+BAA+B,CAAC,MAAM,CAAC,CAAC;IACvD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,+BAA+B,CAAC,MAAM,CAAC,CAAC;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,iCAAiC,EAAE,CAAC;IACpE,QAAQ,CAAC,aAAa,EAAE,SAAS,+BAA+B,EAAE,CAAC;IACnE,QAAQ,CAAC,cAAc,EAAE;QACvB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;QACxC,QAAQ,CAAC,cAAc,EAAE,EAAE,CAAC,iCAAiC,CAAC;KAC/D,CAAC;IACF,QAAQ,CAAC,eAAe,EAAE,SAAS,eAAe,EAAE,CAAC;IACrD,yFAAyF;IACzF,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,SAAS,eAAe,EAAE,CAAC;CACrD;AAk1BD,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,OAAO,EAAE,kCAAkC,CAAC;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,gHAAgH;IAChH,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACvD;;;OAGG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAC/D;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;CAC5D;AAsED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,iBAAiB,CAiDtF"}
@@ -5,6 +5,7 @@
5
5
  // `@oscharko-dev/keiko-quality-intelligence`. The server tier owns IO (it is the only layer that
6
6
  // may touch the filesystem); the pure domain owns splitting + hashing. Oversize and unsupported
7
7
  // inputs fail with user-actionable errors (#278 AC) before any model prompt is built.
8
+ import { realpathSync } from "node:fs";
8
9
  import { dirname, isAbsolute, relative, resolve } from "node:path";
9
10
  import { QualityIntelligence } from "@oscharko-dev/keiko-contracts";
10
11
  import { redact, sha256Hex } from "@oscharko-dev/keiko-security";
@@ -66,6 +67,18 @@ const CREDENTIAL_LABEL_SHAPES = [
66
67
  /\bBearer\s+\S+/giu,
67
68
  /-----BEGIN [A-Z ]*PRIVATE KEY-----/gu,
68
69
  ];
70
+ // Replace every control character (C0 range incl. tab/newline/CR, plus DEL) with a space using a
71
+ // code-point scan — the `no-control-regex` lint rule forbids a control-range regex literal, and a
72
+ // scan is the established in-package idiom (mirrors generationPort.scrubEvidenceText). Keeps a
73
+ // label single-line.
74
+ function collapseControlCharsToSpace(value) {
75
+ let out = "";
76
+ for (const ch of value) {
77
+ const cp = ch.codePointAt(0) ?? 0;
78
+ out += cp <= 0x1f || cp === 0x7f ? " " : ch;
79
+ }
80
+ return out;
81
+ }
69
82
  const sanitiseLabel = (label) => {
70
83
  // Strip any URL authority — ANY scheme (http, file, s3, ftp, …), not just http(s) — plus the
71
84
  // well-known credential token shapes, so a browser-supplied label never carries a URL or secret
@@ -73,6 +86,12 @@ const sanitiseLabel = (label) => {
73
86
  let cleaned = label.replace(/[a-z][a-z0-9+.-]*:\/\/\S+/giu, " ");
74
87
  for (const shape of CREDENTIAL_LABEL_SHAPES)
75
88
  cleaned = cleaned.replace(shape, " ");
89
+ // Replace every control character (newline, CR, tab, NUL, DEL, …) with a space so a multi-line or
90
+ // control-laden label can never carry a second line of content into the browser-streamed envelope
91
+ // displayLabel. Without this, the absolute-path basename-collapse below (which splits on "/" only)
92
+ // would keep a trailing "\n<more content>" glued inside the final path segment — defeating the
93
+ // basename defence and emitting a multi-line label (#277/#278 envelope display-surface invariant).
94
+ cleaned = collapseControlCharsToSpace(cleaned);
76
95
  cleaned = cleaned.trim();
77
96
  // Collapse an absolute POSIX / Windows-drive / UNC path label to its final segment so the
78
97
  // display label never leaks the filesystem layout (the basename is the useful display token).
@@ -86,10 +105,34 @@ const sanitiseLabel = (label) => {
86
105
  // Reject a source whose absolute path (any segment) names a denied credential location. isDenied
87
106
  // inspects EVERY path segment, so a denied ancestor cannot be hidden by rooting a read deeper. Shared
88
107
  // by the folder and single-file paths so both honour the same containment guard (Epic #729 security).
108
+ // Also rejects the symlink variant (assertRealPathNotDenied) so a benign-named link cannot resolve
109
+ // into a protected location.
89
110
  function assertNotDenied(absPath, label, noun) {
90
111
  if (isDenied(absPath)) {
91
112
  throw new QiIngestionError("QI_SOURCE_DENIED", `${noun} "${label}" is in a protected location.`);
92
113
  }
114
+ assertRealPathNotDenied(absPath, label, noun);
115
+ }
116
+ // Defense-in-depth against a symlinked workspace root. The keiko-workspace deny gate (readWorkspaceFile)
117
+ // inspects only the path RELATIVE to the realpath'd root, so a denied segment AT or ABOVE the connected
118
+ // root is invisible to it: a benign-named "~/docs" symlink whose real target is "~/.aws" lets a
119
+ // supported file inside it read through to the model, even though the lexical assertNotDenied above sees
120
+ // only "docs". Re-running the deny gate over the REAL (symlink-resolved) absolute path rejects it. The
121
+ // lexical check above already covers the no-symlink case, so this only ADDS denials when realpath
122
+ // diverges into a protected location; a non-existent target surfaces later as NOT_FOUND, so a failed
123
+ // realpath is a deliberate no-op here. (#713 single-file security review: "deny-list still applies";
124
+ // #729 folder-root parity — both ingest paths share this boundary blind spot.)
125
+ function assertRealPathNotDenied(absPath, label, noun) {
126
+ let realPath;
127
+ try {
128
+ realPath = realpathSync(absPath);
129
+ }
130
+ catch {
131
+ return;
132
+ }
133
+ if (realPath !== absPath && isDenied(realPath)) {
134
+ throw new QiIngestionError("QI_SOURCE_DENIED", `${noun} "${label}" is in a protected location.`);
135
+ }
93
136
  }
94
137
  const envelopeIdFor = (index, label, content) => {
95
138
  const digest = sha256Hex(`qi-src-v1|${String(index)}|${label}|${content}`).slice(0, 24);
@@ -194,8 +237,8 @@ function atomsForWorkspaceEntry(entry, envelopeId) {
194
237
  // entry becomes one content-bearing atom under a single repository-context envelope.
195
238
  function ingestWorkspace(source, index, registeredAt, byteBudget) {
196
239
  const label = sanitiseLabel(source.label);
197
- // Reject a folder whose ROOT names a denied credential location: connecting e.g. ~/.aws or
198
- // ~/.docker AS A FOLDER would otherwise ingest credential files whose RELATIVE paths
240
+ // Reject a folder whose ROOT names a denied credential location (lexically or via a symlinked root):
241
+ // connecting e.g. ~/.aws AS A FOLDER would otherwise ingest credential files whose RELATIVE paths
199
242
  // ("credentials", "config.json") never trip the per-file deny check (#729 security).
200
243
  assertNotDenied(resolve(source.path), label, "Folder");
201
244
  let workspace;
@@ -332,7 +375,7 @@ function ingestFile(source, index, registeredAt, byteBudget) {
332
375
  throw new QiIngestionError("QI_SOURCE_UNSUPPORTED", `File "${label}" is not a supported single-file document.`);
333
376
  }
334
377
  // Reject any path whose segments name a denied credential directory or file (.ssh, .aws, .env,
335
- // *.pem, id_rsa, …) regardless of how the workspace root resolves below.
378
+ // *.pem, id_rsa, …) — lexically or after symlink resolution — regardless of the workspace root below.
336
379
  assertNotDenied(absFile, label, "File");
337
380
  const content = readSingleFileContent(absFile, label);
338
381
  // keiko-workspace decodes as UTF-8; a NUL byte is the canonical binary marker. A binary file that
@@ -1 +1 @@
1
- {"version":3,"file":"runRegistry.d.ts","sourceRoot":"","sources":["../../src/qualityIntelligence/runRegistry.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAErF,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEjF,UAAU,WAAW;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACnE;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkC;IAEvD,oGAAoG;IACpG,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,eAAe;IAY7D,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI;IAMzE,0FAA0F;IAC1F,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,iBAAiB,EAAE,SAAS,CAAC,GAAG,IAAI;IAM5E,oFAAoF;IACpF,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAO9B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAIhC,oFAAoF;IACpF,mBAAmB,IAAI,SAAS,+BAA+B,EAAE;IAUjE,wCAAwC;IACxC,KAAK,IAAI,IAAI;CAId;AAGD,eAAO,MAAM,aAAa,eAAsB,CAAC"}
1
+ {"version":3,"file":"runRegistry.d.ts","sourceRoot":"","sources":["../../src/qualityIntelligence/runRegistry.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAErF,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEjF,UAAU,WAAW;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACnE;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkC;IAEvD,oGAAoG;IACpG,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,eAAe;IAY7D,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI;IAMzE,0FAA0F;IAC1F,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,iBAAiB,EAAE,SAAS,CAAC,GAAG,IAAI;IAM5E,oFAAoF;IACpF,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAO9B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAIhC,oFAAoF;IACpF,mBAAmB,IAAI,SAAS,+BAA+B,EAAE;IAYjE,wCAAwC;IACxC,KAAK,IAAI,IAAI;CAId;AAGD,eAAO,MAAM,aAAa,eAAsB,CAAC"}
@@ -51,6 +51,8 @@ export class QiRunRegistry {
51
51
  requestedAt: run.requestedAt,
52
52
  completedAt: null,
53
53
  totals: { ...run.totals },
54
+ // An in-flight run is not yet persisted and cannot have been reviewed (Issue #282 FIX A11y-2).
55
+ reviewState: "open",
54
56
  }));
55
57
  }
56
58
  /** Test seam: clear all active runs. */
@@ -1 +1 @@
1
- {"version":3,"file":"uiRoutes.d.ts","sourceRoot":"","sources":["../../src/qualityIntelligence/uiRoutes.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAOhD,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAqMzC,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAkDpF;AAMD,wBAAgB,cAAc,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAmClF"}
1
+ {"version":3,"file":"uiRoutes.d.ts","sourceRoot":"","sources":["../../src/qualityIntelligence/uiRoutes.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAOhD,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAmNzC,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAkDpF;AAMD,wBAAgB,cAAc,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAmClF"}
@@ -29,7 +29,7 @@ function resolveEvidenceDir(deps) {
29
29
  // Projection helpers — build browser-safe wire shapes from manifest data.
30
30
  // NEVER include raw prompt, raw source content, credentials, or unsafe markdown.
31
31
  // ---------------------------------------------------------------------------
32
- function projectRunSummary(manifest) {
32
+ function projectRunSummary(manifest, reviewState) {
33
33
  if (manifest === undefined)
34
34
  return null;
35
35
  return {
@@ -42,8 +42,18 @@ function projectRunSummary(manifest) {
42
42
  findings: manifest.totals.findings,
43
43
  exports: manifest.totals.exports,
44
44
  },
45
+ reviewState,
45
46
  };
46
47
  }
48
+ // FIX A11y-2 (Issue #282) — resolve the run's overall review state for the list item so the hub list
49
+ // can show a per-run lifecycle badge (AC1). Loads the small `.review.json` companion per listed run;
50
+ // this is acceptable because the list path already loads a per-run manifest and the list is bounded
51
+ // by the route limit. Defaults to "open" when no companion exists or no evidence dir is configured.
52
+ function listReviewStateFor(runId, evidenceDir) {
53
+ if (evidenceDir === undefined)
54
+ return "open";
55
+ return runReviewStateOf(loadRunReviewState(runId, evidenceDir));
56
+ }
47
57
  /**
48
58
  * Build a candidateId → weak-test flag map from the persisted test-quality findings (Epic #736).
49
59
  * Only findings of kind "test-quality" that carry a candidateId contribute; the first finding wins
@@ -202,7 +212,7 @@ export function handleListQiRuns(ctx, deps) {
202
212
  break;
203
213
  try {
204
214
  const manifest = loadQualityIntelligenceRun(id, { evidenceDir });
205
- const summary = projectRunSummary(manifest);
215
+ const summary = projectRunSummary(manifest, listReviewStateFor(id, evidenceDir));
206
216
  if (summary !== null)
207
217
  runs.push(summary);
208
218
  }
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAoI/C,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACrE;AAID,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED,eAAO,MAAM,SAAS,eAAsB,CAAC;AAC7C,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,OAAO,SAAS,CAAC;AAE5D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAElD,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;CACnB;AAED,MAAM,MAAM,YAAY,GAAG,CACzB,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,KAChB,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;CAChC;AAUD,eAAO,MAAM,UAAU,EAAE,SAAS,eAAe,EAwRhD,CAAC;AA6BF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD;AAKD,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,UAAU,GAAG,oBAAoB,GAAG,SAAS,CA2B/C;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,CAEjE;AAED,wBAAgB,YAAY,IAAI,QAAQ,CAEvC;AAED,wBAAgB,oBAAoB,IAAI,QAAQ,CAE/C"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAqI/C,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACrE;AAID,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED,eAAO,MAAM,SAAS,eAAsB,CAAC;AAC7C,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,OAAO,SAAS,CAAC;AAE5D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAElD,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;CACnB;AAED,MAAM,MAAM,YAAY,GAAG,CACzB,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,KAChB,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;CAChC;AAUD,eAAO,MAAM,UAAU,EAAE,SAAS,eAAe,EA2RhD,CAAC;AA6BF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD;AAKD,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,UAAU,GAAG,oBAAoB,GAAG,SAAS,CA2B/C;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,QAAQ,CAEjE;AAED,wBAAgB,YAAY,IAAI,QAAQ,CAEvC;AAED,wBAAgB,oBAAoB,IAAI,QAAQ,CAE/C"}
@@ -22,7 +22,7 @@ import { handleFilesContent, handleFilesDirectories, handleFilesPreview, handleF
22
22
  import { handleBrowserApplyScreenshot, handleBrowserContent, handleBrowserEvents, handleBrowserNavigate, handleBrowserScreenshot, handleBrowserStatus, handleCreateBrowserSession, handleDeleteBrowserSession, } from "./browser.js";
23
23
  import { handleCancelLocalKnowledgeCapsuleIndexing, handleConnectLocalKnowledgeCapsule, handleCreateLocalKnowledgeCapsule, handleCreateLocalKnowledgeCapsuleSet, handleDeleteLocalKnowledgeCapsule, handleDisconnectLocalKnowledgeCapsule, handleGetLocalKnowledgeCapsule, handleListLocalKnowledgeCapsules, handleListLocalKnowledgeCapsuleSets, handleReindexLocalKnowledgeCapsule, handleStartLocalKnowledgeCapsuleIndexing, handleUpdateLocalKnowledgeCapsule, } from "./local-knowledge-handlers.js";
24
24
  import { handleRelationshipCreate, handleRelationshipDelete, handleRelationshipDependencies, handleRelationshipEvents, handleRelationshipExplain, handleRelationshipGet, handleRelationshipHealth, handleRelationshipImpact, handleRelationshipList, handleRelationshipPatch, handleRelationshipValidate, } from "./relationship-handlers.js";
25
- import { handleQiCapabilities, handleQiDryRunFigma, handleQiDryRunJira, handleQiSourceSelect, handleListQiRuns, handleGetQiRun, QI_HANDOFF_ROUTE_GROUP, QI_RUN_EXECUTION_ROUTE_GROUP, QI_REVIEW_ROUTE_GROUP, QI_EXPORT_ROUTE_GROUP, QI_EDIT_ROUTE_GROUP, QI_TRACEABILITY_ROUTE_GROUP, QI_RECHECK_ROUTE_GROUP, } from "./qualityIntelligence/index.js";
25
+ import { handleQiCapabilities, handleQiDryRunFigma, handleQiDryRunJira, handleQiSourceSelect, handleListQiRuns, handleGetQiRun, QI_HANDOFF_ROUTE_GROUP, QI_RUN_EXECUTION_ROUTE_GROUP, QI_REVIEW_ROUTE_GROUP, QI_EXPORT_ROUTE_GROUP, QI_EDIT_ROUTE_GROUP, QI_RETENTION_ROUTE_GROUP, QI_TRACEABILITY_ROUTE_GROUP, QI_RECHECK_ROUTE_GROUP, } from "./qualityIntelligence/index.js";
26
26
  import { handleFigmaTriggerSnapshot, handleFigmaLoadSnapshot, handleFigmaRevokeToken, } from "./qualityIntelligence/figmaSnapshotRoutes.js";
27
27
  import { handleFigmaGenerateCode } from "./qualityIntelligence/figmaCodegenRoutes.js";
28
28
  export const STREAMING = Symbol("streaming");
@@ -274,6 +274,9 @@ export const API_ROUTES = [
274
274
  // Issue #726 (Epic #712) — inline candidate editing. Literal-suffix POST /runs/:id/edit
275
275
  // disambiguates against /runs/:id/cancel just like /review and /export above.
276
276
  ...QI_EDIT_ROUTE_GROUP,
277
+ // Issue #282 follow-up (Epic #270) — run-deletion control. DELETE /runs/:id is method-distinct
278
+ // from GET /runs/:id and sweeps every server-owned companion (ADR-0023 D8).
279
+ ...QI_RETENTION_ROUTE_GROUP,
277
280
  // Issue #740 (Epic #734) — requirement↔test traceability matrix export.
278
281
  ...QI_TRACEABILITY_ROUTE_GROUP,
279
282
  // Issue #743 (Epic #735) — drift re-check + targeted regeneration. Literal-suffix POST routes
@@ -1 +1 @@
1
- {"version":3,"file":"store-handlers.d.ts","sourceRoot":"","sources":["../src/store-handlers.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAuH/C,iBAAS,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAEpD;AAmND,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAMvF;AAMD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAatB;AAeD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAQtB;AAMD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAcvF;AAgBD,wBAAgB,eAAe,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAYnF;AAMD,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAetB;AA0bD,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAuBtB;AAMD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAQpF;AAMD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAgBtF;AAMD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAwBtB;AA6CD,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAWtB;AAuBD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAgBtB;AAGD,OAAO,EAAE,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"store-handlers.d.ts","sourceRoot":"","sources":["../src/store-handlers.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAwH/C,iBAAS,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAEpD;AAmND,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAMvF;AAMD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAatB;AAeD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAQtB;AAMD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAcvF;AAgBD,wBAAgB,eAAe,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAYnF;AAMD,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAetB;AA0bD,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAuBtB;AAMD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAQpF;AAMD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAkBtF;AAMD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAwBtB;AA6CD,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAWtB;AAuBD,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAgBtB;AAGD,OAAO,EAAE,cAAc,EAAE,CAAC"}
@@ -10,6 +10,7 @@ import { UiStoreError, assertUiDbOutsideProject, isProjectAvailable, validatePro
10
10
  import { pathIsDenied } from "./files-deny.js";
11
11
  import { clearGroundedContextIndexesForConversation, clearGroundedContextIndexesForWorkspace, } from "./grounded-context-index.js";
12
12
  import { clearGroundedTurnsForConversation, clearGroundedTurnsForWorkspace, } from "./grounded-turn-registry.js";
13
+ import { isLegacyEmptyAssistantPlaceholder } from "./assistant-response.js";
13
14
  // Issue #184 — workspace-relative path gate. isValidScopePath is the canonical validator from
14
15
  // @oscharko-dev/keiko-contracts/connected-context (issue #178). Reusing it here keeps the BFF
15
16
  // boundary aligned with the rest of the connected-repo surface and avoids regex drift.
@@ -748,7 +749,9 @@ export function handleListMessages(ctx, deps) {
748
749
  if (!chatBelongsToProject(deps, projectPath, chatId)) {
749
750
  return notFoundResult("Chat not found.");
750
751
  }
751
- const messages = deps.store.listMessages(chatId, limit);
752
+ const messages = deps.store
753
+ .listMessages(chatId, limit)
754
+ .filter((message) => !isLegacyEmptyAssistantPlaceholder(message));
752
755
  return { status: 200, body: { messages } };
753
756
  });
754
757
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscharko-dev/keiko-server",
3
- "version": "0.2.0-beta.4",
3
+ "version": "0.2.0-beta.5",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",