@zerwiz/ymir 0.1.33 → 0.1.35

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 (930) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/apps/README.md +4 -0
  3. package/apps/hlidskjalf/README.md +68 -0
  4. package/apps/hlidskjalf/android/app/build.gradle +73 -0
  5. package/apps/hlidskjalf/android/app/capacitor.build.gradle +19 -0
  6. package/apps/hlidskjalf/android/app/proguard-rules.pro +21 -0
  7. package/apps/hlidskjalf/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java +26 -0
  8. package/apps/hlidskjalf/android/app/src/main/AndroidManifest.xml +41 -0
  9. package/apps/hlidskjalf/android/app/src/main/java/org/zerwiz/ymir/MainActivity.java +5 -0
  10. package/apps/hlidskjalf/android/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
  11. package/apps/hlidskjalf/android/app/src/main/res/drawable/splash.png +0 -0
  12. package/apps/hlidskjalf/android/app/src/main/res/drawable-land-hdpi/splash.png +0 -0
  13. package/apps/hlidskjalf/android/app/src/main/res/drawable-land-mdpi/splash.png +0 -0
  14. package/apps/hlidskjalf/android/app/src/main/res/drawable-land-xhdpi/splash.png +0 -0
  15. package/apps/hlidskjalf/android/app/src/main/res/drawable-land-xxhdpi/splash.png +0 -0
  16. package/apps/hlidskjalf/android/app/src/main/res/drawable-land-xxxhdpi/splash.png +0 -0
  17. package/apps/hlidskjalf/android/app/src/main/res/drawable-port-hdpi/splash.png +0 -0
  18. package/apps/hlidskjalf/android/app/src/main/res/drawable-port-mdpi/splash.png +0 -0
  19. package/apps/hlidskjalf/android/app/src/main/res/drawable-port-xhdpi/splash.png +0 -0
  20. package/apps/hlidskjalf/android/app/src/main/res/drawable-port-xxhdpi/splash.png +0 -0
  21. package/apps/hlidskjalf/android/app/src/main/res/drawable-port-xxxhdpi/splash.png +0 -0
  22. package/apps/hlidskjalf/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +34 -0
  23. package/apps/hlidskjalf/android/app/src/main/res/layout/activity_main.xml +12 -0
  24. package/apps/hlidskjalf/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  25. package/apps/hlidskjalf/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
  26. package/apps/hlidskjalf/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  27. package/apps/hlidskjalf/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png +0 -0
  28. package/apps/hlidskjalf/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  29. package/apps/hlidskjalf/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  30. package/apps/hlidskjalf/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png +0 -0
  31. package/apps/hlidskjalf/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  32. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  33. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
  34. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  35. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  36. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
  37. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  38. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  39. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
  40. package/apps/hlidskjalf/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  41. package/apps/hlidskjalf/android/app/src/main/res/values/ic_launcher_background.xml +4 -0
  42. package/apps/hlidskjalf/android/app/src/main/res/values/strings.xml +7 -0
  43. package/apps/hlidskjalf/android/app/src/main/res/values/styles.xml +22 -0
  44. package/apps/hlidskjalf/android/app/src/main/res/xml/file_paths.xml +5 -0
  45. package/apps/hlidskjalf/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java +18 -0
  46. package/apps/hlidskjalf/android/build.gradle +29 -0
  47. package/apps/hlidskjalf/android/capacitor.settings.gradle +3 -0
  48. package/apps/hlidskjalf/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  49. package/apps/hlidskjalf/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  50. package/apps/hlidskjalf/android/gradle.properties +22 -0
  51. package/apps/hlidskjalf/android/gradlew +248 -0
  52. package/apps/hlidskjalf/android/gradlew.bat +92 -0
  53. package/apps/hlidskjalf/android/settings.gradle +5 -0
  54. package/apps/hlidskjalf/android/variables.gradle +16 -0
  55. package/apps/hlidskjalf/capacitor.config.ts +26 -0
  56. package/apps/hlidskjalf/dist/apple-touch-icon.png +0 -0
  57. package/apps/hlidskjalf/dist/assets/index-BWmNIyaG.css +1 -0
  58. package/apps/hlidskjalf/dist/assets/index-k7QEfNto.js +88 -0
  59. package/apps/hlidskjalf/dist/assets/index-k7QEfNto.js.map +1 -0
  60. package/apps/hlidskjalf/dist/icon.svg +7 -0
  61. package/apps/hlidskjalf/dist/index.html +64 -0
  62. package/apps/hlidskjalf/dist/manifest.webmanifest +31 -0
  63. package/apps/hlidskjalf/dist/og.jpg +0 -0
  64. package/apps/hlidskjalf/dist/og.png +0 -0
  65. package/apps/hlidskjalf/dist/og.svg +64 -0
  66. package/apps/hlidskjalf/dist/sw.js +39 -0
  67. package/apps/hlidskjalf/dist/ymir-icon.png +0 -0
  68. package/apps/hlidskjalf/dist/ymir-mark.svg +62 -0
  69. package/apps/hlidskjalf/electron/icon.png +0 -0
  70. package/apps/hlidskjalf/electron/main.cjs +236 -0
  71. package/apps/hlidskjalf/electron/preload.cjs +8 -0
  72. package/apps/hlidskjalf/electron/smidja-icon.png +0 -0
  73. package/apps/hlidskjalf/electron/ymir-hlidskjalf.desktop.in +12 -0
  74. package/apps/hlidskjalf/electron/ymir-smidja.desktop.in +12 -0
  75. package/apps/hlidskjalf/index.html +63 -0
  76. package/apps/hlidskjalf/package-lock.json +5398 -0
  77. package/apps/hlidskjalf/package.json +55 -0
  78. package/apps/hlidskjalf/public/apple-touch-icon.png +0 -0
  79. package/apps/hlidskjalf/public/icon.svg +7 -0
  80. package/apps/hlidskjalf/public/manifest.webmanifest +31 -0
  81. package/apps/hlidskjalf/public/og.jpg +0 -0
  82. package/apps/hlidskjalf/public/og.png +0 -0
  83. package/apps/hlidskjalf/public/og.svg +64 -0
  84. package/apps/hlidskjalf/public/sw.js +39 -0
  85. package/apps/hlidskjalf/public/ymir-icon.png +0 -0
  86. package/apps/hlidskjalf/public/ymir-mark.svg +62 -0
  87. package/apps/hlidskjalf/server/accounts.ts +159 -0
  88. package/apps/hlidskjalf/server/index.ts +2045 -0
  89. package/apps/hlidskjalf/src/app/App.tsx +116 -0
  90. package/apps/hlidskjalf/src/app/BottomStream.tsx +128 -0
  91. package/apps/hlidskjalf/src/app/MobileNav.tsx +87 -0
  92. package/apps/hlidskjalf/src/app/Rail.tsx +106 -0
  93. package/apps/hlidskjalf/src/app/Shell.tsx +135 -0
  94. package/apps/hlidskjalf/src/app/Topbar.tsx +130 -0
  95. package/apps/hlidskjalf/src/components/AccentPicker.tsx +207 -0
  96. package/apps/hlidskjalf/src/components/AccountMenu.tsx +115 -0
  97. package/apps/hlidskjalf/src/components/AgentCard.tsx +75 -0
  98. package/apps/hlidskjalf/src/components/EmberBackground.tsx +36 -0
  99. package/apps/hlidskjalf/src/components/Emblem.tsx +48 -0
  100. package/apps/hlidskjalf/src/components/Halls.tsx +155 -0
  101. package/apps/hlidskjalf/src/components/LoginModal.tsx +329 -0
  102. package/apps/hlidskjalf/src/components/Markdown.tsx +10 -0
  103. package/apps/hlidskjalf/src/components/MetricTile.tsx +40 -0
  104. package/apps/hlidskjalf/src/components/ModelPicker.tsx +100 -0
  105. package/apps/hlidskjalf/src/components/Overlay.tsx +134 -0
  106. package/apps/hlidskjalf/src/components/PRCard.tsx +154 -0
  107. package/apps/hlidskjalf/src/components/RecallPanel.tsx +116 -0
  108. package/apps/hlidskjalf/src/components/RuneTag.tsx +23 -0
  109. package/apps/hlidskjalf/src/components/Status.tsx +35 -0
  110. package/apps/hlidskjalf/src/components/TaskChip.tsx +23 -0
  111. package/apps/hlidskjalf/src/components/TraceRow.tsx +28 -0
  112. package/apps/hlidskjalf/src/data/lore.ts +127 -0
  113. package/apps/hlidskjalf/src/data/metadata.ts +147 -0
  114. package/apps/hlidskjalf/src/data/mythology.ts +95 -0
  115. package/apps/hlidskjalf/src/data/realms.ts +167 -0
  116. package/apps/hlidskjalf/src/gates/Cron.tsx +77 -0
  117. package/apps/hlidskjalf/src/gates/Decisions.tsx +67 -0
  118. package/apps/hlidskjalf/src/gates/Files.tsx +138 -0
  119. package/apps/hlidskjalf/src/gates/Fleet.tsx +184 -0
  120. package/apps/hlidskjalf/src/gates/Forge.tsx +486 -0
  121. package/apps/hlidskjalf/src/gates/OmniChat.tsx +217 -0
  122. package/apps/hlidskjalf/src/gates/Processes.tsx +162 -0
  123. package/apps/hlidskjalf/src/gates/Profile.tsx +225 -0
  124. package/apps/hlidskjalf/src/gates/Reviews.tsx +33 -0
  125. package/apps/hlidskjalf/src/gates/Runes.tsx +116 -0
  126. package/apps/hlidskjalf/src/gates/Runtime.tsx +106 -0
  127. package/apps/hlidskjalf/src/gates/Sessions.tsx +109 -0
  128. package/apps/hlidskjalf/src/gates/Stats.tsx +268 -0
  129. package/apps/hlidskjalf/src/gates/Tasks.tsx +173 -0
  130. package/apps/hlidskjalf/src/gates/Trace.tsx +155 -0
  131. package/apps/hlidskjalf/src/gates/Well.tsx +154 -0
  132. package/apps/hlidskjalf/src/gates/Worktrees.tsx +48 -0
  133. package/apps/hlidskjalf/src/hooks/useTenantDef.ts +13 -0
  134. package/apps/hlidskjalf/src/main.tsx +31 -0
  135. package/apps/hlidskjalf/src/services/api.ts +404 -0
  136. package/apps/hlidskjalf/src/services/auth.ts +107 -0
  137. package/apps/hlidskjalf/src/services/stream.ts +35 -0
  138. package/apps/hlidskjalf/src/state/store.ts +648 -0
  139. package/apps/hlidskjalf/src/state/ui.ts +62 -0
  140. package/apps/hlidskjalf/src/styles/auth.css +485 -0
  141. package/apps/hlidskjalf/src/styles/components.css +1259 -0
  142. package/apps/hlidskjalf/src/styles/forge.css +115 -0
  143. package/apps/hlidskjalf/src/styles/global.css +167 -0
  144. package/apps/hlidskjalf/src/styles/menus.css +175 -0
  145. package/apps/hlidskjalf/src/styles/mobile.css +298 -0
  146. package/apps/hlidskjalf/src/styles/overlays.css +205 -0
  147. package/apps/hlidskjalf/src/styles/shell.css +641 -0
  148. package/apps/hlidskjalf/src/types.ts +251 -0
  149. package/apps/hlidskjalf/src/vite-env.d.ts +9 -0
  150. package/apps/hlidskjalf/static/landing.md +6 -0
  151. package/apps/hlidskjalf/tsconfig.json +25 -0
  152. package/apps/hlidskjalf/vite.config.ts +49 -0
  153. package/apps/hlidskjalf-mobile/App.tsx +93 -0
  154. package/apps/hlidskjalf-mobile/app.json +13 -0
  155. package/apps/hlidskjalf-mobile/index.ts +4 -0
  156. package/apps/hlidskjalf-mobile/package.json +35 -0
  157. package/apps/hlidskjalf-mobile/public/icon.svg +7 -0
  158. package/apps/hlidskjalf-mobile/tsconfig.json +4 -0
  159. package/apps/odrerir/.astro/content.d.ts +159 -0
  160. package/apps/odrerir/.astro/dev.json +24 -0
  161. package/apps/odrerir/.astro/settings.json +5 -0
  162. package/apps/odrerir/.astro/types.d.ts +2 -0
  163. package/apps/odrerir/astro.config.mjs +79 -0
  164. package/apps/odrerir/dist/apple-touch-icon.png +0 -0
  165. package/apps/odrerir/dist/favicon-192.png +0 -0
  166. package/apps/odrerir/dist/favicon-32.png +0 -0
  167. package/apps/odrerir/dist/favicon-512.png +0 -0
  168. package/apps/odrerir/dist/favicon.svg +53 -0
  169. package/apps/odrerir/dist/icon.svg +7 -0
  170. package/apps/odrerir/dist/index.html +837 -0
  171. package/apps/odrerir/dist/og-square.png +0 -0
  172. package/apps/odrerir/dist/og-story.png +0 -0
  173. package/apps/odrerir/dist/og.png +0 -0
  174. package/apps/odrerir/dist/safari-pinned-tab.svg +7 -0
  175. package/apps/odrerir/dist/site.webmanifest +16 -0
  176. package/apps/odrerir/dist/styles/cloth.css +112 -0
  177. package/apps/odrerir/dist/styles/livehall.css +278 -0
  178. package/apps/odrerir/electron/icon.png +0 -0
  179. package/apps/odrerir/electron/main.cjs +183 -0
  180. package/apps/odrerir/electron/ymir-odrerir.desktop.in +12 -0
  181. package/apps/odrerir/package-lock.json +4195 -0
  182. package/apps/odrerir/package.json +30 -0
  183. package/apps/odrerir/public/apple-touch-icon.png +0 -0
  184. package/apps/odrerir/public/favicon-192.png +0 -0
  185. package/apps/odrerir/public/favicon-32.png +0 -0
  186. package/apps/odrerir/public/favicon-512.png +0 -0
  187. package/apps/odrerir/public/favicon.svg +53 -0
  188. package/apps/odrerir/public/icon.svg +7 -0
  189. package/apps/odrerir/public/og-square.png +0 -0
  190. package/apps/odrerir/public/og-story.png +0 -0
  191. package/apps/odrerir/public/og.png +0 -0
  192. package/apps/odrerir/public/safari-pinned-tab.svg +7 -0
  193. package/apps/odrerir/public/site.webmanifest +16 -0
  194. package/apps/odrerir/scripts/extract-cloth.mjs +101 -0
  195. package/apps/odrerir/src/index.html +837 -0
  196. package/apps/odrerir/src/styles/cloth.css +112 -0
  197. package/apps/odrerir/src/styles/livehall.css +278 -0
  198. package/apps/sessrumnir/.github/ISSUE_TEMPLATE/bug_report.yml +99 -0
  199. package/apps/sessrumnir/.github/ISSUE_TEMPLATE/config.yml +8 -0
  200. package/apps/sessrumnir/.github/ISSUE_TEMPLATE/feature_request.yml +46 -0
  201. package/apps/sessrumnir/.github/PULL_REQUEST_TEMPLATE.md +56 -0
  202. package/apps/sessrumnir/.github/SUPPORT.md +18 -0
  203. package/apps/sessrumnir/.github/workflows/build.yml +133 -0
  204. package/apps/sessrumnir/AGENTS.md +540 -0
  205. package/apps/sessrumnir/CLA.md +63 -0
  206. package/apps/sessrumnir/CONTRIBUTING.md +233 -0
  207. package/apps/sessrumnir/LICENSE +200 -0
  208. package/apps/sessrumnir/NOTICE +33 -0
  209. package/apps/sessrumnir/README.md +329 -0
  210. package/apps/sessrumnir/bin/sessrumnir-web.sh +117 -0
  211. package/apps/sessrumnir/bin/sessrumnir.js +169 -0
  212. package/apps/sessrumnir/docs/CNAME +1 -0
  213. package/apps/sessrumnir/docs/favicon.ico +0 -0
  214. package/apps/sessrumnir/docs/icon-512.png +0 -0
  215. package/apps/sessrumnir/docs/index.html +441 -0
  216. package/apps/sessrumnir/docs/robots.txt +4 -0
  217. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_181929.png +0 -0
  218. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182005.png +0 -0
  219. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182020.png +0 -0
  220. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182039.png +0 -0
  221. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182117.png +0 -0
  222. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182225.png +0 -0
  223. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182248.png +0 -0
  224. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182322.png +0 -0
  225. package/apps/sessrumnir/docs/screenshots/Screenshot_20260824_182340.png +0 -0
  226. package/apps/sessrumnir/docs/sitemap.xml +9 -0
  227. package/apps/sessrumnir/electron.vite.config.ts +49 -0
  228. package/apps/sessrumnir/eslint.config.mjs +75 -0
  229. package/apps/sessrumnir/i18next.config.ts +33 -0
  230. package/apps/sessrumnir/install.sh +144 -0
  231. package/apps/sessrumnir/out/main/index.js +10881 -0
  232. package/apps/sessrumnir/out/preload/index.js +722 -0
  233. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-400-normal-C8QS47vb.woff2 +0 -0
  234. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-400-normal-D3EsxgFc.woff +0 -0
  235. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-500-normal-B7dJQtg-.woff +0 -0
  236. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-500-normal-BLlg2W5x.woff2 +0 -0
  237. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-600-normal-BcIsDXzC.woff2 +0 -0
  238. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-600-normal-CBymlcNF.woff +0 -0
  239. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-400-normal-Bgrpe4p1.woff +0 -0
  240. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-400-normal-BlcaxZtM.woff2 +0 -0
  241. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-500-normal-CdQuyvtc.woff +0 -0
  242. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-500-normal-pZw22qtS.woff2 +0 -0
  243. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-600-normal-Dr-wLyOm.woff +0 -0
  244. package/apps/sessrumnir/out/renderer/assets/cormorant-cyrillic-ext-600-normal-dLR2-a_v.woff2 +0 -0
  245. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-400-normal-BGH8Vunh.woff2 +0 -0
  246. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-400-normal-C3_-2Ua-.woff +0 -0
  247. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-500-normal-Dj3SQ6fR.woff +0 -0
  248. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-500-normal-EBdSCOD3.woff2 +0 -0
  249. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-600-normal-B254Xisw.woff2 +0 -0
  250. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-600-normal-DUW-TI76.woff +0 -0
  251. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-400-normal-DuQ88yz3.woff2 +0 -0
  252. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-400-normal-DuXFa1Dr.woff +0 -0
  253. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-500-normal-AH9qog1s.woff2 +0 -0
  254. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-500-normal-DAuUCO41.woff +0 -0
  255. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-600-normal-CD-dlDpD.woff +0 -0
  256. package/apps/sessrumnir/out/renderer/assets/cormorant-latin-ext-600-normal-D3T6BBXG.woff2 +0 -0
  257. package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-400-normal-C-RiYxEf.woff2 +0 -0
  258. package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-400-normal-DmUuA7Y2.woff +0 -0
  259. package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-500-normal-DsPuwQHi.woff2 +0 -0
  260. package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-500-normal-tGBW_mI7.woff +0 -0
  261. package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-600-normal-Dar_cCT6.woff2 +0 -0
  262. package/apps/sessrumnir/out/renderer/assets/cormorant-vietnamese-600-normal-GIs8CF2x.woff +0 -0
  263. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-300-normal-Ba-HN6uq.woff +0 -0
  264. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-300-normal-ByB2hQUF.woff2 +0 -0
  265. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
  266. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
  267. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
  268. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
  269. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-300-normal-B8O3uHbr.woff +0 -0
  270. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-300-normal-BbtBtHoB.woff2 +0 -0
  271. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
  272. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
  273. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
  274. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
  275. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-300-normal-BPU54Tw3.woff +0 -0
  276. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-300-normal-BRS-C4KA.woff2 +0 -0
  277. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  278. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  279. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  280. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  281. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-300-normal-BEtQ2wm2.woff2 +0 -0
  282. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-300-normal-CQbCaAQ4.woff +0 -0
  283. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
  284. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
  285. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
  286. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
  287. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-300-normal-B6fSpYYV.woff +0 -0
  288. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-300-normal-CpdbWO_2.woff2 +0 -0
  289. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
  290. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
  291. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
  292. package/apps/sessrumnir/out/renderer/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
  293. package/apps/sessrumnir/out/renderer/assets/index-BxqsRq2x.css +4858 -0
  294. package/apps/sessrumnir/out/renderer/assets/index-TaD69rFk.js +99435 -0
  295. package/apps/sessrumnir/out/renderer/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  296. package/apps/sessrumnir/out/renderer/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  297. package/apps/sessrumnir/out/renderer/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  298. package/apps/sessrumnir/out/renderer/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  299. package/apps/sessrumnir/out/renderer/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  300. package/apps/sessrumnir/out/renderer/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  301. package/apps/sessrumnir/out/renderer/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  302. package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  303. package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  304. package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  305. package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  306. package/apps/sessrumnir/out/renderer/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  307. package/apps/sessrumnir/out/renderer/assets/newsreader-latin-ext-wght-italic-DFS986qL.woff2 +0 -0
  308. package/apps/sessrumnir/out/renderer/assets/newsreader-latin-ext-wght-normal-C-3rgBeH.woff2 +0 -0
  309. package/apps/sessrumnir/out/renderer/assets/newsreader-latin-wght-italic-Bxi8ein9.woff2 +0 -0
  310. package/apps/sessrumnir/out/renderer/assets/newsreader-latin-wght-normal-CCVVNp6i.woff2 +0 -0
  311. package/apps/sessrumnir/out/renderer/assets/newsreader-vietnamese-wght-italic-xAqemagA.woff2 +0 -0
  312. package/apps/sessrumnir/out/renderer/assets/newsreader-vietnamese-wght-normal-Czsa-EzN.woff2 +0 -0
  313. package/apps/sessrumnir/out/renderer/assets/openmoji-color-9qo_2DLG.woff2 +0 -0
  314. package/apps/sessrumnir/out/renderer/assets/pi-logo-B6SVnhuS.svg +7 -0
  315. package/apps/sessrumnir/out/renderer/index.html +19 -0
  316. package/apps/sessrumnir/out/renderer/theme-boot.js +30 -0
  317. package/apps/sessrumnir/package-lock.json +10636 -0
  318. package/apps/sessrumnir/package.json +183 -0
  319. package/apps/sessrumnir/public/apple-touch-icon.png +0 -0
  320. package/apps/sessrumnir/public/icon.svg +7 -0
  321. package/apps/sessrumnir/resources/icons/generate_icons.py +38 -0
  322. package/apps/sessrumnir/resources/icons/icon-128.png +0 -0
  323. package/apps/sessrumnir/resources/icons/icon-16.png +0 -0
  324. package/apps/sessrumnir/resources/icons/icon-256.png +0 -0
  325. package/apps/sessrumnir/resources/icons/icon-32.png +0 -0
  326. package/apps/sessrumnir/resources/icons/icon-48.png +0 -0
  327. package/apps/sessrumnir/resources/icons/icon-512.png +0 -0
  328. package/apps/sessrumnir/resources/icons/icon-64.png +0 -0
  329. package/apps/sessrumnir/resources/icons/icon.icns +0 -0
  330. package/apps/sessrumnir/resources/icons/icon.ico +0 -0
  331. package/apps/sessrumnir/resources/icons/icon.png +0 -0
  332. package/apps/sessrumnir/resources/icons/icon.svg +7 -0
  333. package/apps/sessrumnir/resources/linux/after-install.sh +23 -0
  334. package/apps/sessrumnir/resources/linux/after-remove.sh +18 -0
  335. package/apps/sessrumnir/resources/locales/en/translation.json +1735 -0
  336. package/apps/sessrumnir/resources/permission-prompt-text.test.ts +111 -0
  337. package/apps/sessrumnir/resources/permission-prompt-text.ts +84 -0
  338. package/apps/sessrumnir/resources/permission-rules.test.ts +351 -0
  339. package/apps/sessrumnir/resources/permission-rules.ts +293 -0
  340. package/apps/sessrumnir/resources/pi-desktop-permissions.ts +62 -0
  341. package/apps/sessrumnir/resources/ymir-sessrumnir.desktop.in +12 -0
  342. package/apps/sessrumnir/scripts/check-semantic-colors.mjs +57 -0
  343. package/apps/sessrumnir/src/main/activity-stats.test.ts +240 -0
  344. package/apps/sessrumnir/src/main/activity-stats.ts +596 -0
  345. package/apps/sessrumnir/src/main/agent-detection.test.ts +25 -0
  346. package/apps/sessrumnir/src/main/agent-detection.ts +114 -0
  347. package/apps/sessrumnir/src/main/app-data-paths.test.ts +90 -0
  348. package/apps/sessrumnir/src/main/app-data-paths.ts +95 -0
  349. package/apps/sessrumnir/src/main/app-log.test.ts +114 -0
  350. package/apps/sessrumnir/src/main/app-log.ts +210 -0
  351. package/apps/sessrumnir/src/main/archived-sessions.test.ts +50 -0
  352. package/apps/sessrumnir/src/main/archived-sessions.ts +95 -0
  353. package/apps/sessrumnir/src/main/attachment-reader.test.ts +56 -0
  354. package/apps/sessrumnir/src/main/attachment-reader.ts +52 -0
  355. package/apps/sessrumnir/src/main/auto-tag.test.ts +38 -0
  356. package/apps/sessrumnir/src/main/auto-tag.ts +75 -0
  357. package/apps/sessrumnir/src/main/autostart-linux.test.ts +56 -0
  358. package/apps/sessrumnir/src/main/autostart-linux.ts +58 -0
  359. package/apps/sessrumnir/src/main/cmd-escape.test.ts +424 -0
  360. package/apps/sessrumnir/src/main/cmd-escape.ts +186 -0
  361. package/apps/sessrumnir/src/main/council-manager.test.ts +167 -0
  362. package/apps/sessrumnir/src/main/council-manager.ts +265 -0
  363. package/apps/sessrumnir/src/main/diagnostics-report.test.ts +140 -0
  364. package/apps/sessrumnir/src/main/diagnostics-report.ts +91 -0
  365. package/apps/sessrumnir/src/main/diagnostics.ts +111 -0
  366. package/apps/sessrumnir/src/main/editor-guard.test.ts +59 -0
  367. package/apps/sessrumnir/src/main/editor-guard.ts +50 -0
  368. package/apps/sessrumnir/src/main/extension-ui-ipc.test.ts +299 -0
  369. package/apps/sessrumnir/src/main/extension-ui-ipc.ts +117 -0
  370. package/apps/sessrumnir/src/main/file-service.test.ts +279 -0
  371. package/apps/sessrumnir/src/main/file-service.ts +652 -0
  372. package/apps/sessrumnir/src/main/fs-errors.ts +42 -0
  373. package/apps/sessrumnir/src/main/get-messages-trim.test.ts +77 -0
  374. package/apps/sessrumnir/src/main/get-messages-trim.ts +97 -0
  375. package/apps/sessrumnir/src/main/git-conveyor.test.ts +305 -0
  376. package/apps/sessrumnir/src/main/git-conveyor.ts +338 -0
  377. package/apps/sessrumnir/src/main/git-worktree.test.ts +101 -0
  378. package/apps/sessrumnir/src/main/git-worktree.ts +201 -0
  379. package/apps/sessrumnir/src/main/i18n.ts +31 -0
  380. package/apps/sessrumnir/src/main/index.ts +619 -0
  381. package/apps/sessrumnir/src/main/ipc/active-engine.ts +15 -0
  382. package/apps/sessrumnir/src/main/ipc/context.ts +58 -0
  383. package/apps/sessrumnir/src/main/ipc/council-handlers.ts +126 -0
  384. package/apps/sessrumnir/src/main/ipc/diagnostics-handlers.ts +8 -0
  385. package/apps/sessrumnir/src/main/ipc/file-handlers.ts +96 -0
  386. package/apps/sessrumnir/src/main/ipc/git-conveyor-handlers.ts +55 -0
  387. package/apps/sessrumnir/src/main/ipc/model-handlers.ts +33 -0
  388. package/apps/sessrumnir/src/main/ipc/models-config-handlers.ts +80 -0
  389. package/apps/sessrumnir/src/main/ipc/notes-handlers.ts +65 -0
  390. package/apps/sessrumnir/src/main/ipc/package-handlers.ts +330 -0
  391. package/apps/sessrumnir/src/main/ipc/permission-rules-handlers.ts +223 -0
  392. package/apps/sessrumnir/src/main/ipc/pi-handlers.ts +136 -0
  393. package/apps/sessrumnir/src/main/ipc/pi-start-options.ts +150 -0
  394. package/apps/sessrumnir/src/main/ipc/run-pi-cli.ts +53 -0
  395. package/apps/sessrumnir/src/main/ipc/session-handlers.ts +491 -0
  396. package/apps/sessrumnir/src/main/ipc/settings.ts +120 -0
  397. package/apps/sessrumnir/src/main/ipc/skills-mcp-handlers.ts +192 -0
  398. package/apps/sessrumnir/src/main/ipc/system-handlers.ts +131 -0
  399. package/apps/sessrumnir/src/main/ipc/tag-handlers.ts +69 -0
  400. package/apps/sessrumnir/src/main/ipc/terminal-handlers.ts +41 -0
  401. package/apps/sessrumnir/src/main/ipc/theme-handlers.ts +131 -0
  402. package/apps/sessrumnir/src/main/ipc/update-handlers.ts +61 -0
  403. package/apps/sessrumnir/src/main/ipc/validation.ts +45 -0
  404. package/apps/sessrumnir/src/main/ipc/workflow-handlers.ts +108 -0
  405. package/apps/sessrumnir/src/main/ipc/workspace-activity-wiring.ts +179 -0
  406. package/apps/sessrumnir/src/main/ipc/workspace-handlers.ts +133 -0
  407. package/apps/sessrumnir/src/main/ipc-handlers.ts +98 -0
  408. package/apps/sessrumnir/src/main/map-concurrent.test.ts +80 -0
  409. package/apps/sessrumnir/src/main/map-concurrent.ts +30 -0
  410. package/apps/sessrumnir/src/main/models-file.test.ts +114 -0
  411. package/apps/sessrumnir/src/main/models-file.ts +89 -0
  412. package/apps/sessrumnir/src/main/notes-manager.test.ts +64 -0
  413. package/apps/sessrumnir/src/main/notes-manager.ts +183 -0
  414. package/apps/sessrumnir/src/main/notify-decision.test.ts +39 -0
  415. package/apps/sessrumnir/src/main/notify-decision.ts +25 -0
  416. package/apps/sessrumnir/src/main/omp-fork-points.test.ts +119 -0
  417. package/apps/sessrumnir/src/main/omp-fork-points.ts +105 -0
  418. package/apps/sessrumnir/src/main/omp-plugin-list.test.ts +94 -0
  419. package/apps/sessrumnir/src/main/omp-plugin-list.ts +103 -0
  420. package/apps/sessrumnir/src/main/package-catalog.test.ts +157 -0
  421. package/apps/sessrumnir/src/main/package-catalog.ts +166 -0
  422. package/apps/sessrumnir/src/main/package-updates.test.ts +149 -0
  423. package/apps/sessrumnir/src/main/package-updates.ts +185 -0
  424. package/apps/sessrumnir/src/main/path-authorization.test.ts +64 -0
  425. package/apps/sessrumnir/src/main/path-authorization.ts +31 -0
  426. package/apps/sessrumnir/src/main/pi-binary-resolution.test.ts +582 -0
  427. package/apps/sessrumnir/src/main/pi-binary-resolution.ts +649 -0
  428. package/apps/sessrumnir/src/main/pi-event-router.test.ts +388 -0
  429. package/apps/sessrumnir/src/main/pi-event-router.ts +290 -0
  430. package/apps/sessrumnir/src/main/pi-paths.test.ts +134 -0
  431. package/apps/sessrumnir/src/main/pi-paths.ts +74 -0
  432. package/apps/sessrumnir/src/main/pi-rpc-manager.test.ts +467 -0
  433. package/apps/sessrumnir/src/main/pi-rpc-manager.ts +1319 -0
  434. package/apps/sessrumnir/src/main/process-tree.test.ts +94 -0
  435. package/apps/sessrumnir/src/main/renderer-origin.test.ts +33 -0
  436. package/apps/sessrumnir/src/main/renderer-origin.ts +35 -0
  437. package/apps/sessrumnir/src/main/session-lineage-reader.test.ts +317 -0
  438. package/apps/sessrumnir/src/main/session-lineage-reader.ts +111 -0
  439. package/apps/sessrumnir/src/main/session-metadata.test.ts +508 -0
  440. package/apps/sessrumnir/src/main/session-metadata.ts +526 -0
  441. package/apps/sessrumnir/src/main/session-name.test.ts +71 -0
  442. package/apps/sessrumnir/src/main/session-name.ts +69 -0
  443. package/apps/sessrumnir/src/main/session-paths.test.ts +110 -0
  444. package/apps/sessrumnir/src/main/session-paths.ts +88 -0
  445. package/apps/sessrumnir/src/main/session-tags.test.ts +61 -0
  446. package/apps/sessrumnir/src/main/session-tags.ts +253 -0
  447. package/apps/sessrumnir/src/main/session-trash.test.ts +66 -0
  448. package/apps/sessrumnir/src/main/session-trash.ts +65 -0
  449. package/apps/sessrumnir/src/main/settings-logic.ts +66 -0
  450. package/apps/sessrumnir/src/main/skills-discovery.test.ts +149 -0
  451. package/apps/sessrumnir/src/main/skills-discovery.ts +179 -0
  452. package/apps/sessrumnir/src/main/startup-launch.ts +63 -0
  453. package/apps/sessrumnir/src/main/terminal-service.ts +96 -0
  454. package/apps/sessrumnir/src/main/theme-store.test.ts +440 -0
  455. package/apps/sessrumnir/src/main/theme-store.ts +495 -0
  456. package/apps/sessrumnir/src/main/tray-decision.test.ts +46 -0
  457. package/apps/sessrumnir/src/main/tray-decision.ts +49 -0
  458. package/apps/sessrumnir/src/main/tray-manager.ts +220 -0
  459. package/apps/sessrumnir/src/main/tsconfig.json +20 -0
  460. package/apps/sessrumnir/src/main/window-background.test.ts +29 -0
  461. package/apps/sessrumnir/src/main/window-background.ts +16 -0
  462. package/apps/sessrumnir/src/main/workflow-discovery.test.ts +252 -0
  463. package/apps/sessrumnir/src/main/workflow-monitor.test.ts +371 -0
  464. package/apps/sessrumnir/src/main/workflow-monitor.ts +1020 -0
  465. package/apps/sessrumnir/src/main/workspace-activity.test.ts +227 -0
  466. package/apps/sessrumnir/src/main/workspace-activity.ts +215 -0
  467. package/apps/sessrumnir/src/main/workspace-manager.test.ts +680 -0
  468. package/apps/sessrumnir/src/main/workspace-manager.ts +1199 -0
  469. package/apps/sessrumnir/src/main/workspace-trust.test.ts +78 -0
  470. package/apps/sessrumnir/src/main/workspace-trust.ts +84 -0
  471. package/apps/sessrumnir/src/preload/index.ts +358 -0
  472. package/apps/sessrumnir/src/preload/tsconfig.json +20 -0
  473. package/apps/sessrumnir/src/renderer/index.html +18 -0
  474. package/apps/sessrumnir/src/renderer/public/theme-boot.js +30 -0
  475. package/apps/sessrumnir/src/renderer/src/app.tsx +194 -0
  476. package/apps/sessrumnir/src/renderer/src/assets/openmoji-color.woff2 +0 -0
  477. package/apps/sessrumnir/src/renderer/src/assets/pi-logo.svg +7 -0
  478. package/apps/sessrumnir/src/renderer/src/assets/ymir-mark.svg +55 -0
  479. package/apps/sessrumnir/src/renderer/src/assets.d.ts +7 -0
  480. package/apps/sessrumnir/src/renderer/src/components/chat-code-highlight.ts +239 -0
  481. package/apps/sessrumnir/src/renderer/src/components/chat-file-link.ts +140 -0
  482. package/apps/sessrumnir/src/renderer/src/components/chat-input.tsx +729 -0
  483. package/apps/sessrumnir/src/renderer/src/components/chat-panel-widths.test.ts +175 -0
  484. package/apps/sessrumnir/src/renderer/src/components/chat-panel-widths.ts +100 -0
  485. package/apps/sessrumnir/src/renderer/src/components/chat-panel.tsx +523 -0
  486. package/apps/sessrumnir/src/renderer/src/components/chat-project-picker.tsx +193 -0
  487. package/apps/sessrumnir/src/renderer/src/components/chat-search.tsx +251 -0
  488. package/apps/sessrumnir/src/renderer/src/components/code-editor-highlight.ts +85 -0
  489. package/apps/sessrumnir/src/renderer/src/components/code-editor-language.test.ts +19 -0
  490. package/apps/sessrumnir/src/renderer/src/components/code-editor-language.ts +88 -0
  491. package/apps/sessrumnir/src/renderer/src/components/code-editor.tsx +128 -0
  492. package/apps/sessrumnir/src/renderer/src/components/command-palette.tsx +315 -0
  493. package/apps/sessrumnir/src/renderer/src/components/command-results.tsx +80 -0
  494. package/apps/sessrumnir/src/renderer/src/components/composer-permission-menu.tsx +91 -0
  495. package/apps/sessrumnir/src/renderer/src/components/context-menu.tsx +457 -0
  496. package/apps/sessrumnir/src/renderer/src/components/copy-button.tsx +36 -0
  497. package/apps/sessrumnir/src/renderer/src/components/council-panels.tsx +193 -0
  498. package/apps/sessrumnir/src/renderer/src/components/custom-models-editor.tsx +330 -0
  499. package/apps/sessrumnir/src/renderer/src/components/diagnostics-panel.tsx +380 -0
  500. package/apps/sessrumnir/src/renderer/src/components/diff-viewer.tsx +318 -0
  501. package/apps/sessrumnir/src/renderer/src/components/ember-background.tsx +113 -0
  502. package/apps/sessrumnir/src/renderer/src/components/error-boundary.tsx +38 -0
  503. package/apps/sessrumnir/src/renderer/src/components/extension-ui-dialog-helpers.test.ts +43 -0
  504. package/apps/sessrumnir/src/renderer/src/components/extension-ui-dialog-helpers.ts +51 -0
  505. package/apps/sessrumnir/src/renderer/src/components/extension-ui-dialog.tsx +486 -0
  506. package/apps/sessrumnir/src/renderer/src/components/file-tree.tsx +784 -0
  507. package/apps/sessrumnir/src/renderer/src/components/git-conveyor-actions.tsx +246 -0
  508. package/apps/sessrumnir/src/renderer/src/components/home-screen.tsx +396 -0
  509. package/apps/sessrumnir/src/renderer/src/components/image-viewer.tsx +100 -0
  510. package/apps/sessrumnir/src/renderer/src/components/line-numbered-code.tsx +58 -0
  511. package/apps/sessrumnir/src/renderer/src/components/markdown-renderer.tsx +237 -0
  512. package/apps/sessrumnir/src/renderer/src/components/message-bubble.tsx +1006 -0
  513. package/apps/sessrumnir/src/renderer/src/components/mission-control.tsx +341 -0
  514. package/apps/sessrumnir/src/renderer/src/components/model-selector.tsx +219 -0
  515. package/apps/sessrumnir/src/renderer/src/components/note-picker.tsx +155 -0
  516. package/apps/sessrumnir/src/renderer/src/components/notes-panel.tsx +437 -0
  517. package/apps/sessrumnir/src/renderer/src/components/package-browser.tsx +604 -0
  518. package/apps/sessrumnir/src/renderer/src/components/permission-mode.test.ts +22 -0
  519. package/apps/sessrumnir/src/renderer/src/components/permission-mode.ts +49 -0
  520. package/apps/sessrumnir/src/renderer/src/components/permission-rules-editor-helpers.test.ts +44 -0
  521. package/apps/sessrumnir/src/renderer/src/components/permission-rules-editor-helpers.ts +35 -0
  522. package/apps/sessrumnir/src/renderer/src/components/permission-rules-editor.tsx +226 -0
  523. package/apps/sessrumnir/src/renderer/src/components/permission-selector.tsx +104 -0
  524. package/apps/sessrumnir/src/renderer/src/components/resize-handle.tsx +51 -0
  525. package/apps/sessrumnir/src/renderer/src/components/review-rail.tsx +194 -0
  526. package/apps/sessrumnir/src/renderer/src/components/session-menu-position.test.ts +22 -0
  527. package/apps/sessrumnir/src/renderer/src/components/session-menu-position.ts +34 -0
  528. package/apps/sessrumnir/src/renderer/src/components/session-panel.tsx +711 -0
  529. package/apps/sessrumnir/src/renderer/src/components/session-runtime-indicator.tsx +43 -0
  530. package/apps/sessrumnir/src/renderer/src/components/settings-panel.tsx +1360 -0
  531. package/apps/sessrumnir/src/renderer/src/components/sidebar-activity.test.ts +47 -0
  532. package/apps/sessrumnir/src/renderer/src/components/sidebar-activity.ts +76 -0
  533. package/apps/sessrumnir/src/renderer/src/components/sidebar-session-labels.test.ts +73 -0
  534. package/apps/sessrumnir/src/renderer/src/components/sidebar-session-labels.ts +47 -0
  535. package/apps/sessrumnir/src/renderer/src/components/sidebar.tsx +1045 -0
  536. package/apps/sessrumnir/src/renderer/src/components/skills-panel.tsx +159 -0
  537. package/apps/sessrumnir/src/renderer/src/components/stats-panel.tsx +368 -0
  538. package/apps/sessrumnir/src/renderer/src/components/status-bar.tsx +274 -0
  539. package/apps/sessrumnir/src/renderer/src/components/status-popover.tsx +481 -0
  540. package/apps/sessrumnir/src/renderer/src/components/streaming-bubble.tsx +128 -0
  541. package/apps/sessrumnir/src/renderer/src/components/subagent-progress.tsx +254 -0
  542. package/apps/sessrumnir/src/renderer/src/components/task-launcher.tsx +168 -0
  543. package/apps/sessrumnir/src/renderer/src/components/terminal.tsx +203 -0
  544. package/apps/sessrumnir/src/renderer/src/components/theme-editor-helpers.test.ts +35 -0
  545. package/apps/sessrumnir/src/renderer/src/components/theme-editor-helpers.ts +35 -0
  546. package/apps/sessrumnir/src/renderer/src/components/theme-editor.tsx +440 -0
  547. package/apps/sessrumnir/src/renderer/src/components/theme-gallery.tsx +373 -0
  548. package/apps/sessrumnir/src/renderer/src/components/thinking-level-selector.tsx +85 -0
  549. package/apps/sessrumnir/src/renderer/src/components/timeline.tsx +289 -0
  550. package/apps/sessrumnir/src/renderer/src/components/tool-call-icon.ts +32 -0
  551. package/apps/sessrumnir/src/renderer/src/components/workflow-navigator.tsx +901 -0
  552. package/apps/sessrumnir/src/renderer/src/components/workspace-tabs.tsx +241 -0
  553. package/apps/sessrumnir/src/renderer/src/global.d.ts +8 -0
  554. package/apps/sessrumnir/src/renderer/src/hooks/use-folder-drop.ts +101 -0
  555. package/apps/sessrumnir/src/renderer/src/hooks.test.ts +88 -0
  556. package/apps/sessrumnir/src/renderer/src/hooks.ts +574 -0
  557. package/apps/sessrumnir/src/renderer/src/i18n.test.ts +54 -0
  558. package/apps/sessrumnir/src/renderer/src/i18n.ts +89 -0
  559. package/apps/sessrumnir/src/renderer/src/index.css +425 -0
  560. package/apps/sessrumnir/src/renderer/src/main.tsx +42 -0
  561. package/apps/sessrumnir/src/renderer/src/message-grouping.test.ts +422 -0
  562. package/apps/sessrumnir/src/renderer/src/message-grouping.ts +410 -0
  563. package/apps/sessrumnir/src/renderer/src/message-parsing.test.ts +86 -0
  564. package/apps/sessrumnir/src/renderer/src/message-parsing.ts +166 -0
  565. package/apps/sessrumnir/src/renderer/src/store-external-prompts.test.ts +161 -0
  566. package/apps/sessrumnir/src/renderer/src/store-session-transitions.test.ts +2090 -0
  567. package/apps/sessrumnir/src/renderer/src/store-timeline.test.ts +52 -0
  568. package/apps/sessrumnir/src/renderer/src/store-turn-error.test.ts +136 -0
  569. package/apps/sessrumnir/src/renderer/src/store.ts +3726 -0
  570. package/apps/sessrumnir/src/renderer/src/subagent-detection.test.ts +55 -0
  571. package/apps/sessrumnir/src/renderer/src/theme/engine.test.ts +29 -0
  572. package/apps/sessrumnir/src/renderer/src/theme/engine.ts +20 -0
  573. package/apps/sessrumnir/src/renderer/src/themes/breeze-claudius.json +69 -0
  574. package/apps/sessrumnir/src/renderer/src/themes/breeze-dark.json +69 -0
  575. package/apps/sessrumnir/src/renderer/src/themes/breeze-light.json +69 -0
  576. package/apps/sessrumnir/src/renderer/src/themes/builtin-themes.test.ts +95 -0
  577. package/apps/sessrumnir/src/renderer/src/themes/dark.json +46 -0
  578. package/apps/sessrumnir/src/renderer/src/themes/fensalir.json +82 -0
  579. package/apps/sessrumnir/src/renderer/src/themes/gruvbox.json +69 -0
  580. package/apps/sessrumnir/src/renderer/src/themes/index.ts +60 -0
  581. package/apps/sessrumnir/src/renderer/src/themes/light.json +46 -0
  582. package/apps/sessrumnir/src/renderer/src/themes/nord.json +69 -0
  583. package/apps/sessrumnir/src/renderer/src/themes/sessrumnir.json +82 -0
  584. package/apps/sessrumnir/src/renderer/src/utils/debounced-buffer.test.ts +81 -0
  585. package/apps/sessrumnir/src/renderer/src/utils/debounced-buffer.ts +57 -0
  586. package/apps/sessrumnir/src/renderer/src/utils/format-relative-time.test.ts +62 -0
  587. package/apps/sessrumnir/src/renderer/src/utils/format-relative-time.ts +28 -0
  588. package/apps/sessrumnir/src/renderer/src/utils/heatmap-grid.test.ts +57 -0
  589. package/apps/sessrumnir/src/renderer/src/utils/heatmap-grid.ts +41 -0
  590. package/apps/sessrumnir/src/renderer/src/utils/ipc-error.test.ts +22 -0
  591. package/apps/sessrumnir/src/renderer/src/utils/ipc-error.ts +12 -0
  592. package/apps/sessrumnir/src/renderer/src/utils/model-search.test.ts +63 -0
  593. package/apps/sessrumnir/src/renderer/src/utils/model-search.ts +20 -0
  594. package/apps/sessrumnir/src/renderer/src/utils/planning-prompt.test.ts +31 -0
  595. package/apps/sessrumnir/src/renderer/src/utils/planning-prompt.ts +27 -0
  596. package/apps/sessrumnir/src/renderer/src/utils/preview-load-error.test.ts +15 -0
  597. package/apps/sessrumnir/src/renderer/src/utils/preview-load-error.ts +22 -0
  598. package/apps/sessrumnir/src/renderer/src/utils/process-status-label.test.ts +13 -0
  599. package/apps/sessrumnir/src/renderer/src/utils/process-status-label.ts +19 -0
  600. package/apps/sessrumnir/src/renderer/src/utils/quick-switcher.test.ts +68 -0
  601. package/apps/sessrumnir/src/renderer/src/utils/quick-switcher.ts +52 -0
  602. package/apps/sessrumnir/src/renderer/src/utils/rank-file-results.test.ts +46 -0
  603. package/apps/sessrumnir/src/renderer/src/utils/rank-file-results.ts +23 -0
  604. package/apps/sessrumnir/src/renderer/src/utils/relative-time.tsx +25 -0
  605. package/apps/sessrumnir/src/renderer/src/utils/session-title.test.ts +98 -0
  606. package/apps/sessrumnir/src/renderer/src/utils/session-title.ts +58 -0
  607. package/apps/sessrumnir/src/renderer/src/utils/stale-guard.test.ts +29 -0
  608. package/apps/sessrumnir/src/renderer/src/utils/stale-guard.ts +20 -0
  609. package/apps/sessrumnir/src/renderer/src/utils/theme.test.ts +174 -0
  610. package/apps/sessrumnir/src/renderer/src/utils/theme.ts +159 -0
  611. package/apps/sessrumnir/src/renderer/src/utils/workflow-runs.test.ts +130 -0
  612. package/apps/sessrumnir/src/renderer/src/utils/workflow-runs.ts +84 -0
  613. package/apps/sessrumnir/src/renderer/tsconfig.json +24 -0
  614. package/apps/sessrumnir/src/shared/agent-engine-label.test.ts +45 -0
  615. package/apps/sessrumnir/src/shared/agent-engine-label.ts +53 -0
  616. package/apps/sessrumnir/src/shared/app-settings.test.ts +25 -0
  617. package/apps/sessrumnir/src/shared/app-settings.ts +23 -0
  618. package/apps/sessrumnir/src/shared/bridge-http.ts +341 -0
  619. package/apps/sessrumnir/src/shared/bridge.ts +337 -0
  620. package/apps/sessrumnir/src/shared/council-config.test.ts +300 -0
  621. package/apps/sessrumnir/src/shared/council-config.ts +356 -0
  622. package/apps/sessrumnir/src/shared/default-settings.ts +39 -0
  623. package/apps/sessrumnir/src/shared/folder-drop.test.ts +118 -0
  624. package/apps/sessrumnir/src/shared/folder-drop.ts +74 -0
  625. package/apps/sessrumnir/src/shared/fork-point.test.ts +30 -0
  626. package/apps/sessrumnir/src/shared/fork-point.ts +24 -0
  627. package/apps/sessrumnir/src/shared/i18n/i18next.d.ts +10 -0
  628. package/apps/sessrumnir/src/shared/i18n/index.test.ts +51 -0
  629. package/apps/sessrumnir/src/shared/i18n/index.ts +50 -0
  630. package/apps/sessrumnir/src/shared/i18n/languages.ts +12 -0
  631. package/apps/sessrumnir/src/shared/i18n/locale-checks.ts +98 -0
  632. package/apps/sessrumnir/src/shared/i18n/locales.test.ts +92 -0
  633. package/apps/sessrumnir/src/shared/i18n/pseudo.test.ts +39 -0
  634. package/apps/sessrumnir/src/shared/i18n/pseudo.ts +44 -0
  635. package/apps/sessrumnir/src/shared/i18n/resolve.test.ts +43 -0
  636. package/apps/sessrumnir/src/shared/i18n/resolve.ts +41 -0
  637. package/apps/sessrumnir/src/shared/i18n/resources.ts +11 -0
  638. package/apps/sessrumnir/src/shared/ipc-contracts.ts +1539 -0
  639. package/apps/sessrumnir/src/shared/models-config.test.ts +124 -0
  640. package/apps/sessrumnir/src/shared/models-config.ts +167 -0
  641. package/apps/sessrumnir/src/shared/package-filter.ts +19 -0
  642. package/apps/sessrumnir/src/shared/package-spec.test.ts +51 -0
  643. package/apps/sessrumnir/src/shared/package-spec.ts +25 -0
  644. package/apps/sessrumnir/src/shared/path-compare.test.ts +38 -0
  645. package/apps/sessrumnir/src/shared/path-compare.ts +76 -0
  646. package/apps/sessrumnir/src/shared/permission-mode.ts +8 -0
  647. package/apps/sessrumnir/src/shared/pi-command.test.ts +165 -0
  648. package/apps/sessrumnir/src/shared/pi-command.ts +139 -0
  649. package/apps/sessrumnir/src/shared/product-name.ts +7 -0
  650. package/apps/sessrumnir/src/shared/session-lineage.test.ts +103 -0
  651. package/apps/sessrumnir/src/shared/session-lineage.ts +50 -0
  652. package/apps/sessrumnir/src/shared/session-preview.test.ts +93 -0
  653. package/apps/sessrumnir/src/shared/session-preview.ts +56 -0
  654. package/apps/sessrumnir/src/shared/sidebar-width.test.ts +58 -0
  655. package/apps/sessrumnir/src/shared/sidebar-width.ts +37 -0
  656. package/apps/sessrumnir/src/shared/theme/builtin-ids.ts +16 -0
  657. package/apps/sessrumnir/src/shared/theme/resolve.test.ts +66 -0
  658. package/apps/sessrumnir/src/shared/theme/resolve.ts +29 -0
  659. package/apps/sessrumnir/src/shared/theme/syntax-defaults.ts +24 -0
  660. package/apps/sessrumnir/src/shared/theme/theme-file.test.ts +88 -0
  661. package/apps/sessrumnir/src/shared/theme/theme-file.ts +132 -0
  662. package/apps/sessrumnir/src/shared/theme/tokens.test.ts +35 -0
  663. package/apps/sessrumnir/src/shared/theme/tokens.ts +86 -0
  664. package/apps/sessrumnir/src/shared/tsconfig.json +17 -0
  665. package/apps/sessrumnir/src/shared/untrusted-data.test.ts +32 -0
  666. package/apps/sessrumnir/src/shared/untrusted-data.ts +22 -0
  667. package/apps/sessrumnir/src/shared/version-compare.test.ts +33 -0
  668. package/apps/sessrumnir/src/shared/version-compare.ts +50 -0
  669. package/apps/sessrumnir/src/shared/workflow-control.ts +33 -0
  670. package/apps/sessrumnir/src/web-server.ts +315 -0
  671. package/apps/sessrumnir/tsconfig.json +26 -0
  672. package/apps/smidja/smidja_build.py +45 -0
  673. package/apps/smidja/smidja_build_review.py +76 -0
  674. package/apps/smidja/smidja_build_test.py +79 -0
  675. package/apps/smidja/smidja_document.py +76 -0
  676. package/apps/smidja/smidja_modules/agent_cc.py +15 -0
  677. package/apps/smidja/smidja_modules/agent_opencode.py +194 -0
  678. package/apps/smidja/smidja_modules/agent_pi.py +392 -0
  679. package/apps/smidja/smidja_modules/agents.py +993 -0
  680. package/apps/smidja/smidja_modules/changes.py +103 -0
  681. package/apps/smidja/smidja_modules/console.py +131 -0
  682. package/apps/smidja/smidja_modules/data_types.py +447 -0
  683. package/apps/smidja/smidja_modules/gates.py +108 -0
  684. package/apps/smidja/smidja_modules/git_helper.py +120 -0
  685. package/apps/smidja/smidja_modules/lm_local.py +144 -0
  686. package/apps/smidja/smidja_modules/permissions.py +194 -0
  687. package/apps/smidja/smidja_modules/prompts.py +21 -0
  688. package/apps/smidja/smidja_modules/quality.py +240 -0
  689. package/apps/smidja/smidja_modules/runner.py +155 -0
  690. package/apps/smidja/smidja_modules/session.py +50 -0
  691. package/apps/smidja/smidja_modules/stall_watch.py +149 -0
  692. package/apps/smidja/smidja_modules/tracer.py +271 -0
  693. package/apps/smidja/smidja_modules/utils.py +77 -0
  694. package/apps/smidja/smidja_orchestrate.py +162 -0
  695. package/apps/smidja/smidja_plan.py +45 -0
  696. package/apps/smidja/smidja_plan_build.py +55 -0
  697. package/apps/smidja/smidja_plan_build_test.py +86 -0
  698. package/apps/smidja/smidja_plan_build_test_quality.py +98 -0
  699. package/apps/smidja/smidja_prompt.py +44 -0
  700. package/apps/smidja/smidja_quality.py +49 -0
  701. package/apps/smidja/smidja_recon_iv.py +202 -0
  702. package/apps/smidja/smidja_scout.py +45 -0
  703. package/apps/smidja/smidja_simple_sdlc.py +183 -0
  704. package/apps/smidja/smidja_smidja_config/smidja.config.yaml +143 -0
  705. package/apps/smidja-factory/AGENTS.md +165 -0
  706. package/apps/smidja-factory/README.md +3 -0
  707. package/apps/smidja-factory/SKILL.md +18 -0
  708. package/apps/smidja-factory/apps/visualizer/.oxlintrc.json +18 -0
  709. package/apps/smidja-factory/apps/visualizer/bun.lock +269 -0
  710. package/apps/smidja-factory/apps/visualizer/desktop/icon.png +0 -0
  711. package/apps/smidja-factory/apps/visualizer/desktop/main.js +140 -0
  712. package/apps/smidja-factory/apps/visualizer/desktop/package-lock.json +871 -0
  713. package/apps/smidja-factory/apps/visualizer/desktop/package.json +18 -0
  714. package/apps/smidja-factory/apps/visualizer/desktop/preload.js +19 -0
  715. package/apps/smidja-factory/apps/visualizer/index.html +28 -0
  716. package/apps/smidja-factory/apps/visualizer/package.json +31 -0
  717. package/apps/smidja-factory/apps/visualizer/public/icon.svg +7 -0
  718. package/apps/smidja-factory/apps/visualizer/public/models/claude.png +0 -0
  719. package/apps/smidja-factory/apps/visualizer/public/models/gemini.png +0 -0
  720. package/apps/smidja-factory/apps/visualizer/public/models/kimi.png +0 -0
  721. package/apps/smidja-factory/apps/visualizer/public/models/openai.png +0 -0
  722. package/apps/smidja-factory/apps/visualizer/public/models/zai.png +0 -0
  723. package/apps/smidja-factory/apps/visualizer/server/chat.ts +425 -0
  724. package/apps/smidja-factory/apps/visualizer/server/db.ts +1050 -0
  725. package/apps/smidja-factory/apps/visualizer/server/index.ts +648 -0
  726. package/apps/smidja-factory/apps/visualizer/server/model-catalog.ts +90 -0
  727. package/apps/smidja-factory/apps/visualizer/server/roster-api.ts +75 -0
  728. package/apps/smidja-factory/apps/visualizer/server/roster_resolve.py +170 -0
  729. package/apps/smidja-factory/apps/visualizer/server/settings.ts +106 -0
  730. package/apps/smidja-factory/apps/visualizer/shared/types.ts +630 -0
  731. package/apps/smidja-factory/apps/visualizer/src/App.vue +379 -0
  732. package/apps/smidja-factory/apps/visualizer/src/components/ChatInput.vue +155 -0
  733. package/apps/smidja-factory/apps/visualizer/src/components/ChatThread.vue +219 -0
  734. package/apps/smidja-factory/apps/visualizer/src/components/ChatView.vue +335 -0
  735. package/apps/smidja-factory/apps/visualizer/src/components/DecisionsView.vue +177 -0
  736. package/apps/smidja-factory/apps/visualizer/src/components/DetailSection.vue +76 -0
  737. package/apps/smidja-factory/apps/visualizer/src/components/MemoryView.vue +397 -0
  738. package/apps/smidja-factory/apps/visualizer/src/components/PhaseDetail.vue +1354 -0
  739. package/apps/smidja-factory/apps/visualizer/src/components/PhaseDots.vue +55 -0
  740. package/apps/smidja-factory/apps/visualizer/src/components/SessionCard.vue +583 -0
  741. package/apps/smidja-factory/apps/visualizer/src/components/SessionMiniCard.vue +58 -0
  742. package/apps/smidja-factory/apps/visualizer/src/components/SessionPanel.vue +272 -0
  743. package/apps/smidja-factory/apps/visualizer/src/components/SessionStartForm.vue +152 -0
  744. package/apps/smidja-factory/apps/visualizer/src/components/SessionTrace.vue +1157 -0
  745. package/apps/smidja-factory/apps/visualizer/src/components/SessionsList.vue +96 -0
  746. package/apps/smidja-factory/apps/visualizer/src/components/SettingsView.vue +152 -0
  747. package/apps/smidja-factory/apps/visualizer/src/components/StatChip.vue +98 -0
  748. package/apps/smidja-factory/apps/visualizer/src/components/StatsView.vue +382 -0
  749. package/apps/smidja-factory/apps/visualizer/src/components/StatusChip.vue +73 -0
  750. package/apps/smidja-factory/apps/visualizer/src/components/ToolCallCard.vue +156 -0
  751. package/apps/smidja-factory/apps/visualizer/src/lib/api.ts +177 -0
  752. package/apps/smidja-factory/apps/visualizer/src/lib/chat-api.ts +95 -0
  753. package/apps/smidja-factory/apps/visualizer/src/lib/chat-store.ts +351 -0
  754. package/apps/smidja-factory/apps/visualizer/src/lib/events.ts +131 -0
  755. package/apps/smidja-factory/apps/visualizer/src/lib/format.ts +92 -0
  756. package/apps/smidja-factory/apps/visualizer/src/lib/highlight.ts +54 -0
  757. package/apps/smidja-factory/apps/visualizer/src/lib/markdown.ts +120 -0
  758. package/apps/smidja-factory/apps/visualizer/src/lib/models.ts +42 -0
  759. package/apps/smidja-factory/apps/visualizer/src/lib/router.ts +67 -0
  760. package/apps/smidja-factory/apps/visualizer/src/lib/types.ts +61 -0
  761. package/apps/smidja-factory/apps/visualizer/src/main.ts +13 -0
  762. package/apps/smidja-factory/apps/visualizer/src/style.css +592 -0
  763. package/apps/smidja-factory/apps/visualizer/tsconfig.json +30 -0
  764. package/apps/smidja-factory/apps/visualizer/vite.config.js +32 -0
  765. package/apps/smidja-factory/apps/visualizer/vite.config.ts +31 -0
  766. package/apps/smidja-factory/cookbooks/create_config.md +61 -0
  767. package/apps/smidja-factory/cookbooks/create_smidja.md +120 -0
  768. package/apps/smidja-factory/cookbooks/how_to_prompt_for_the_eng.md +110 -0
  769. package/apps/smidja-factory/cookbooks/install.md +56 -0
  770. package/apps/smidja-factory/cookbooks/run_smidja.md +122 -0
  771. package/apps/smidja-factory/cookbooks/smidja_overview.md +88 -0
  772. package/apps/smidja-factory/cookbooks/update_config.md +103 -0
  773. package/apps/smidja-factory/cookbooks/update_modules.md +100 -0
  774. package/apps/smidja-factory/cookbooks/update_smidja.md +90 -0
  775. package/apps/smidja-factory/docs/enhancement-plan.md +420 -0
  776. package/apps/smidja-factory/references/config.md +202 -0
  777. package/apps/smidja-factory/references/handoff.md +161 -0
  778. package/apps/smidja-factory/references/observability.md +166 -0
  779. package/apps/smidja-factory/scripts/install.py +113 -0
  780. package/apps/smidja-factory/scripts/make_config.py +35 -0
  781. package/apps/smidja-factory/scripts/make_smidja.py +119 -0
  782. package/apps/smidja-factory/skills/add-or-edit-ai-models/SKILL.md +151 -0
  783. package/apps/smidja-factory/skills/create-new-agent/SKILL.md +125 -0
  784. package/apps/smidja-factory/skills/create-new-teams/SKILL.md +151 -0
  785. package/apps/smidja-factory/skills/git-ops/README.md +25 -0
  786. package/apps/smidja-factory/skills/git-ops/SKILL.md +18 -0
  787. package/apps/smidja-factory/skills/git-ops/create_branch.sh +79 -0
  788. package/apps/smidja-factory/skills/git-ops/safe_commit.sh +82 -0
  789. package/apps/smidja-factory/skills/git-ops/sync_upstream.sh +94 -0
  790. package/apps/smidja-factory/skills/how-to-run-agent-teams/SKILL.md +117 -0
  791. package/apps/smidja-factory/skills/smidja-instructions/SKILL.md +168 -0
  792. package/apps/smidja-factory/skills/smidja-launcher/SKILL.md +155 -0
  793. package/apps/smidja-factory/skills/smidja-start/SKILL.md +266 -0
  794. package/apps/smidja-factory/skills/volundr/SKILL.md +232 -0
  795. package/apps/smidja-factory/skills/volundr/prompt/dispatch-prompt.md +91 -0
  796. package/apps/smidja-factory/skills/volundr/prompt/volundr-system.md +63 -0
  797. package/apps/smidja-factory/skills/volundr/scripts/volundr-observe.py +52 -0
  798. package/apps/smidja-factory/skills/volundr/scripts/volundr-recall.py +54 -0
  799. package/apps/smidja-factory/skills/volundr/scripts/volundr-status.py +51 -0
  800. package/apps/smidja-factory/skills/volundr/scripts/volundr-teach.py +47 -0
  801. package/apps/smidja-factory/templates/env.sample +28 -0
  802. package/apps/smidja-factory/templates/harness_engineering/subagents.ts +706 -0
  803. package/apps/smidja-factory/templates/harness_engineering/themeMap.ts +145 -0
  804. package/apps/smidja-factory/templates/justfile +89 -0
  805. package/apps/smidja-factory/templates/prompt_engineering/builder/system.md +13 -0
  806. package/apps/smidja-factory/templates/prompt_engineering/builder/user.md +34 -0
  807. package/apps/smidja-factory/templates/prompt_engineering/documenter/system.md +17 -0
  808. package/apps/smidja-factory/templates/prompt_engineering/documenter/user.md +48 -0
  809. package/apps/smidja-factory/templates/prompt_engineering/orchestrator/system.md +55 -0
  810. package/apps/smidja-factory/templates/prompt_engineering/orchestrator/user.md +39 -0
  811. package/apps/smidja-factory/templates/prompt_engineering/planner/system.md +21 -0
  812. package/apps/smidja-factory/templates/prompt_engineering/planner/user.md +45 -0
  813. package/apps/smidja-factory/templates/prompt_engineering/recon_orchestrator/system.md +51 -0
  814. package/apps/smidja-factory/templates/prompt_engineering/recon_orchestrator/user.md +26 -0
  815. package/apps/smidja-factory/templates/prompt_engineering/reviewer/system.md +16 -0
  816. package/apps/smidja-factory/templates/prompt_engineering/reviewer/user.md +44 -0
  817. package/apps/smidja-factory/templates/prompt_engineering/scout/system.md +32 -0
  818. package/apps/smidja-factory/templates/prompt_engineering/scout/user.md +34 -0
  819. package/apps/smidja-factory/templates/smidja/smidja_build.py +45 -0
  820. package/apps/smidja-factory/templates/smidja/smidja_build_review.py +76 -0
  821. package/apps/smidja-factory/templates/smidja/smidja_build_test.py +79 -0
  822. package/apps/smidja-factory/templates/smidja/smidja_document.py +76 -0
  823. package/apps/smidja-factory/templates/smidja/smidja_modules/agent_cc.py +15 -0
  824. package/apps/smidja-factory/templates/smidja/smidja_modules/agent_opencode.py +194 -0
  825. package/apps/smidja-factory/templates/smidja/smidja_modules/agent_pi.py +392 -0
  826. package/apps/smidja-factory/templates/smidja/smidja_modules/agents.py +993 -0
  827. package/apps/smidja-factory/templates/smidja/smidja_modules/changes.py +103 -0
  828. package/apps/smidja-factory/templates/smidja/smidja_modules/console.py +131 -0
  829. package/apps/smidja-factory/templates/smidja/smidja_modules/data_types.py +447 -0
  830. package/apps/smidja-factory/templates/smidja/smidja_modules/gates.py +108 -0
  831. package/apps/smidja-factory/templates/smidja/smidja_modules/git_helper.py +120 -0
  832. package/apps/smidja-factory/templates/smidja/smidja_modules/lm_local.py +144 -0
  833. package/apps/smidja-factory/templates/smidja/smidja_modules/permissions.py +194 -0
  834. package/apps/smidja-factory/templates/smidja/smidja_modules/prompts.py +21 -0
  835. package/apps/smidja-factory/templates/smidja/smidja_modules/quality.py +240 -0
  836. package/apps/smidja-factory/templates/smidja/smidja_modules/runner.py +155 -0
  837. package/apps/smidja-factory/templates/smidja/smidja_modules/session.py +50 -0
  838. package/apps/smidja-factory/templates/smidja/smidja_modules/stall_watch.py +149 -0
  839. package/apps/smidja-factory/templates/smidja/smidja_modules/tracer.py +271 -0
  840. package/apps/smidja-factory/templates/smidja/smidja_modules/utils.py +77 -0
  841. package/apps/smidja-factory/templates/smidja/smidja_orchestrate.py +162 -0
  842. package/apps/smidja-factory/templates/smidja/smidja_plan.py +45 -0
  843. package/apps/smidja-factory/templates/smidja/smidja_plan_build.py +55 -0
  844. package/apps/smidja-factory/templates/smidja/smidja_plan_build_test.py +86 -0
  845. package/apps/smidja-factory/templates/smidja/smidja_plan_build_test_quality.py +98 -0
  846. package/apps/smidja-factory/templates/smidja/smidja_prompt.py +44 -0
  847. package/apps/smidja-factory/templates/smidja/smidja_quality.py +49 -0
  848. package/apps/smidja-factory/templates/smidja/smidja_recon_iv.py +202 -0
  849. package/apps/smidja-factory/templates/smidja/smidja_scout.py +45 -0
  850. package/apps/smidja-factory/templates/smidja/smidja_simple_sdlc.py +183 -0
  851. package/apps/smidja-factory/templates/smidja.config.yaml +143 -0
  852. package/bin/app-build.sh +36 -0
  853. package/bin/electron-lib.sh +25 -0
  854. package/bin/sessrumnir.sh +1 -1
  855. package/bin/ymir-install.sh +7 -0
  856. package/install.sh +88 -0
  857. package/package.json +16 -7
  858. package/scripts/electron.sh +6 -2
  859. package/.agents/bus/messages.json +0 -1
  860. package/.agents/config/agents.machine.example.yaml +0 -16
  861. package/.pi/mcp.json +0 -16
  862. package/assets/reference/state/.branch-eligible-owner +0 -4
  863. package/assets/reference/state/.branch-mirror-cursor +0 -1
  864. package/assets/reference/state/.branch-outcomes-cursor +0 -1
  865. package/assets/reference/state/.branch-session +0 -1
  866. package/assets/reference/state/.count-default_wF_p5 +0 -1
  867. package/assets/reference/state/.count-default_wF_p6 +0 -1
  868. package/assets/reference/state/.count-default_wR_p2 +0 -1
  869. package/assets/reference/state/.count-default_wS_p2 +0 -1
  870. package/assets/reference/state/.count-firstmate_fm-server-knowledge +0 -1
  871. package/assets/reference/state/.hash-default_wF_p5 +0 -1
  872. package/assets/reference/state/.hash-default_wF_p6 +0 -1
  873. package/assets/reference/state/.hash-default_wR_p2 +0 -1
  874. package/assets/reference/state/.hash-default_wS_p2 +0 -1
  875. package/assets/reference/state/.hash-firstmate_fm-server-knowledge +0 -1
  876. package/assets/reference/state/.heartbeat-streak +0 -1
  877. package/assets/reference/state/.inactive-outcome-reconcile +0 -2
  878. package/assets/reference/state/.lock +0 -1
  879. package/assets/reference/state/.pi-branch-extension-loaded +0 -1
  880. package/assets/reference/state/.pi-turnend-extension-loaded +0 -2
  881. package/assets/reference/state/.pi-watch-extension-loaded +0 -2
  882. package/assets/reference/state/.seen-server-knowledge_turn-ended +0 -1
  883. package/assets/reference/state/.session-start-agents-baseline +0 -2
  884. package/assets/reference/state/.session-start-complete +0 -1
  885. package/assets/reference/state/.stale-default_wF_p5 +0 -1
  886. package/assets/reference/state/.stale-default_wR_p2 +0 -1
  887. package/assets/reference/state/.stale-firstmate_fm-server-knowledge +0 -1
  888. package/assets/reference/state/.stale-since-default_wF_p5 +0 -1
  889. package/assets/reference/state/.stale-since-default_wR_p2 +0 -1
  890. package/assets/reference/state/.stale-since-firstmate_fm-server-knowledge +0 -1
  891. package/assets/reference/state/.startup-network.delivered +0 -1
  892. package/assets/reference/state/.startup-network.report +0 -1
  893. package/assets/reference/state/.startup-network.status +0 -10
  894. package/assets/reference/state/.startup-network.timings +0 -6
  895. package/assets/reference/state/.trace-context-effective +0 -1
  896. package/assets/reference/state/.wake-queue +0 -0
  897. package/assets/reference/state/.wake-queue.seq +0 -1
  898. package/assets/reference/state/.wake-rows.consume.BcqXSq +0 -0
  899. package/assets/reference/state/.wake-rows.consume.NDZZei +0 -0
  900. package/assets/reference/state/.wake-rows.consume.VGkhYm +0 -0
  901. package/assets/reference/state/.wake-rows.consume.tR2rTy +0 -0
  902. package/assets/reference/state/.watch-arm-output.3581p6 +0 -0
  903. package/assets/reference/state/.watch-arm-output.3ciDNP +0 -0
  904. package/assets/reference/state/.watch-arm-output.SksR6k +0 -0
  905. package/assets/reference/state/.watch-cycle-exits.log +0 -22
  906. package/assets/reference/state/.watch-deliveries.log +0 -19
  907. package/assets/reference/state/.watch-triage.log +0 -38
  908. package/assets/reference/state/.watch.lock/fm-home +0 -1
  909. package/assets/reference/state/.watch.lock/pid +0 -1
  910. package/assets/reference/state/.watch.lock/pid-identity +0 -1
  911. package/assets/reference/state/.watch.lock/watcher-path +0 -1
  912. package/assets/reference/state/.watch.lock.owner.qqbxmS/fm-home +0 -1
  913. package/assets/reference/state/.watch.lock.owner.qqbxmS/pid +0 -1
  914. package/assets/reference/state/.watch.lock.owner.qqbxmS/pid-identity +0 -1
  915. package/assets/reference/state/.watch.lock.owner.qqbxmS/watcher-path +0 -1
  916. package/assets/reference/state/.watcher-down +0 -1
  917. package/assets/reference/state/branch-outcomes.jsonl +0 -1
  918. package/assets/reference/state/branch-session/2026-09-04T16-49-38-671Z_01a06d53-6b6f-7619-930d-e6dfd9a4a29e.jsonl +0 -211
  919. package/assets/reference/state/home-summary.json +0 -42
  920. package/assets/reference/state/server-knowledge.busy-gen +0 -1
  921. package/assets/reference/state/server-knowledge.busy-state +0 -1
  922. package/assets/reference/state/server-knowledge.herdr-presentation +0 -12
  923. package/assets/reference/state/server-knowledge.meta +0 -18
  924. package/assets/reference/state/server-knowledge.pi-ext.ts +0 -26
  925. package/assets/reference/state/server-knowledge.turn-ended +0 -0
  926. /package/{assets/reference/state/.last-check → apps/hlidskjalf/src/app/.gitkeep} +0 -0
  927. /package/{assets/reference/state/.last-heartbeat → apps/hlidskjalf/src/components/.gitkeep} +0 -0
  928. /package/{assets/reference/state/.last-watcher-beat → apps/hlidskjalf/src/services/.gitkeep} +0 -0
  929. /package/{assets/reference/state/.main-eligible-rows.tmp.p2HMKo → apps/smidja/smidja_modules/__init__.py} +0 -0
  930. /package/{assets/reference/state/.main-eligible-rows.tmp.Exxm82 → apps/smidja-factory/templates/smidja/smidja_modules/__init__.py} +0 -0
@@ -0,0 +1,1050 @@
1
+ /**
2
+ * SQLite reader over a target repo's smidja.db.
3
+ *
4
+ * The read connection is opened readonly and every query on it is a SELECT —
5
+ * the writers are the tracers of running smidja processes, and WAL lets us read
6
+ * straight through their inserts.
7
+ *
8
+ * ONE exception, opened lazily on its own connection: `setArchived`. Archiving
9
+ * is review triage — "I have looked at this run" — which has to outlive a
10
+ * browser, so it lives on the session row rather than in localStorage. It is
11
+ * the only write this process can make, it touches exactly one column, and it
12
+ * never runs unless a human clicks the button.
13
+ */
14
+ import { Database } from "bun:sqlite";
15
+ import { existsSync } from "node:fs";
16
+ import { dirname, isAbsolute, join, resolve } from "node:path";
17
+ import type {
18
+ AgentSession,
19
+ AgentStartPayload,
20
+ DecisionsBucket,
21
+ DecisionsResponse,
22
+ Envelope,
23
+ Event,
24
+ EventsPage,
25
+ GateResult,
26
+ ModelProviderStat,
27
+ Phase,
28
+ Session,
29
+ SessionDetail,
30
+ SessionSummary,
31
+ SessionUsage,
32
+ StatsResponse,
33
+ } from "../shared/types.ts";
34
+
35
+ export const DEFAULT_DB_RELATIVE = "apps/smidja/smidja_data/smidja.db";
36
+ const MAX_LIMIT = 1000;
37
+ const DEFAULT_LIMIT = 500;
38
+
39
+ /**
40
+ * Commercial model catalog for the stats "Commercial comparison" panel.
41
+ * Rates are standard pay-as-you-go API prices per 1M tokens (input/output),
42
+ * from the tokens-optimization spec. Where the spec quotes a range, the
43
+ * midpoint is used. `cache` is the share of the input price billed on a
44
+ * cache read (published caching rate per provider: OpenAI/DeepSeek/Meta 50%,
45
+ * Google 25%, Anthropic 10%).
46
+ */
47
+ interface VendorModelDef {
48
+ id: string;
49
+ name: string;
50
+ provider: string;
51
+ tier: 1 | 2 | 3;
52
+ rank: number;
53
+ pin: number;
54
+ po: number;
55
+ cache: number;
56
+ }
57
+
58
+ const VENDOR_MODELS: VendorModelDef[] = [
59
+ // Tier 1 — heavyweight flagships & enterprise intelligence
60
+ { id: "gpt-5.5-pro", name: "GPT-5.5 Pro", provider: "OpenAI", tier: 1, rank: 1, pin: 30.0, po: 180.0, cache: 0.5 },
61
+ { id: "o3-pro", name: "o3-pro", provider: "OpenAI", tier: 1, rank: 2, pin: 20.0, po: 80.0, cache: 0.5 },
62
+ { id: "claude-opus", name: "Claude Opus 4.6 / 5", provider: "Anthropic", tier: 1, rank: 3, pin: 5.0, po: 25.0, cache: 0.1 },
63
+ { id: "gpt-5.5", name: "GPT-5.5", provider: "OpenAI", tier: 1, rank: 4, pin: 5.0, po: 30.0, cache: 0.5 },
64
+ { id: "llama-3.1-405b", name: "Llama 3.1 405B", provider: "Meta (Hosted)", tier: 1, rank: 5, pin: 3.75, po: 3.75, cache: 0.5 },
65
+ { id: "gpt-5.4", name: "GPT-5.4", provider: "OpenAI", tier: 1, rank: 6, pin: 2.5, po: 15.0, cache: 0.5 },
66
+ { id: "gpt-4o", name: "GPT-4o", provider: "OpenAI", tier: 1, rank: 7, pin: 2.5, po: 10.0, cache: 0.5 },
67
+ { id: "gemini-3.1-pro", name: "Gemini 3.1 Pro", provider: "Google", tier: 1, rank: 8, pin: 2.0, po: 12.0, cache: 0.25 },
68
+ { id: "o3", name: "o3", provider: "OpenAI", tier: 1, rank: 9, pin: 2.0, po: 8.0, cache: 0.5 },
69
+ { id: "claude-sonnet", name: "Claude Sonnet 4.6 / 5", provider: "Anthropic", tier: 1, rank: 10, pin: 2.5, po: 12.5, cache: 0.1 },
70
+ // Tier 2 — mid-range & high-efficiency workhorses
71
+ { id: "mistral-large-3", name: "Mistral Large 3", provider: "Mistral AI", tier: 2, rank: 11, pin: 0.5, po: 1.5, cache: 0.5 },
72
+ { id: "claude-haiku", name: "Claude Haiku 4.5", provider: "Anthropic", tier: 2, rank: 12, pin: 1.0, po: 5.0, cache: 0.1 },
73
+ { id: "gemini-2.5-pro", name: "Gemini 2.5 Pro", provider: "Google", tier: 2, rank: 13, pin: 1.25, po: 10.0, cache: 0.25 },
74
+ { id: "o4-mini", name: "o4-mini", provider: "OpenAI", tier: 2, rank: 14, pin: 0.55, po: 2.2, cache: 0.5 },
75
+ { id: "deepseek-r1", name: "DeepSeek R1", provider: "DeepSeek", tier: 2, rank: 15, pin: 0.55, po: 2.19, cache: 0.5 },
76
+ { id: "gemini-3-flash-preview", name: "Gemini 3 Flash Preview", provider: "Google", tier: 2, rank: 16, pin: 0.5, po: 3.0, cache: 0.25 },
77
+ { id: "command-r-plus", name: "Command R+", provider: "Cohere", tier: 2, rank: 17, pin: 2.5, po: 10.0, cache: 0.5 },
78
+ { id: "gpt-5.4-mini", name: "GPT-5.4 Mini", provider: "OpenAI", tier: 2, rank: 18, pin: 0.75, po: 4.5, cache: 0.5 },
79
+ { id: "deepseek-v4", name: "DeepSeek V4", provider: "DeepSeek", tier: 2, rank: 19, pin: 0.3, po: 0.5, cache: 0.5 },
80
+ { id: "codestral", name: "Codestral", provider: "Mistral AI", tier: 2, rank: 20, pin: 0.3, po: 0.9, cache: 0.5 },
81
+ { id: "deepseek-chat-v3.2", name: "DeepSeek-Chat (V3.2)", provider: "DeepSeek", tier: 2, rank: 21, pin: 0.28, po: 0.42, cache: 0.5 },
82
+ { id: "llama-3.3-70b", name: "Llama 3.3 70B", provider: "Meta (Hosted)", tier: 2, rank: 22, pin: 0.41, po: 0.52, cache: 0.5 },
83
+ { id: "mistral-medium-3.5", name: "Mistral Medium 3.5", provider: "Mistral AI", tier: 2, rank: 23, pin: 1.5, po: 7.5, cache: 0.5 },
84
+ { id: "gpt-4.1", name: "GPT-4.1", provider: "OpenAI", tier: 2, rank: 24, pin: 2.0, po: 8.0, cache: 0.5 },
85
+ { id: "qwen-2.5-72b", name: "Qwen 2.5 72B", provider: "Alibaba (Hosted)", tier: 2, rank: 25, pin: 0.35, po: 0.4, cache: 0.5 },
86
+ // Tier 3 — flash, mini & edge engines
87
+ { id: "gemini-3.7-flash", name: "Gemini 3.7 Flash", provider: "Google", tier: 3, rank: 26, pin: 0.75, po: 3.75, cache: 0.25 },
88
+ { id: "gemini-3.6-flash", name: "Gemini 3.6 Flash", provider: "Google", tier: 3, rank: 27, pin: 0.75, po: 3.75, cache: 0.25 },
89
+ { id: "gpt-4.1-mini", name: "GPT-4.1 Mini", provider: "OpenAI", tier: 3, rank: 28, pin: 0.4, po: 1.6, cache: 0.5 },
90
+ { id: "llama-4-maverick", name: "Llama 4 Maverick", provider: "Meta (Hosted)", tier: 3, rank: 29, pin: 0.25, po: 0.875, cache: 0.5 },
91
+ { id: "gpt-5-mini", name: "GPT-5 Mini", provider: "OpenAI", tier: 3, rank: 30, pin: 0.25, po: 2.0, cache: 0.5 },
92
+ { id: "gemini-3.1-flash-lite", name: "Gemini 3.1 Flash-Lite", provider: "Google", tier: 3, rank: 31, pin: 0.25, po: 1.5, cache: 0.25 },
93
+ { id: "gpt-5.4-nano", name: "GPT-5.4 Nano", provider: "OpenAI", tier: 3, rank: 32, pin: 0.2, po: 1.25, cache: 0.5 },
94
+ { id: "mistral-small-4", name: "Mistral Small 4", provider: "Mistral AI", tier: 3, rank: 33, pin: 0.15, po: 0.6, cache: 0.5 },
95
+ { id: "gpt-4o-mini", name: "GPT-4o mini", provider: "OpenAI", tier: 3, rank: 34, pin: 0.15, po: 0.6, cache: 0.5 },
96
+ { id: "gpt-4.1-nano", name: "GPT-4.1 Nano", provider: "OpenAI", tier: 3, rank: 35, pin: 0.1, po: 0.4, cache: 0.5 },
97
+ { id: "gemini-2.5-flash-lite", name: "Gemini 2.5 Flash-Lite", provider: "Google", tier: 3, rank: 36, pin: 0.1, po: 0.4, cache: 0.25 },
98
+ { id: "llama-4-scout", name: "Llama 4 Scout", provider: "Meta (Hosted)", tier: 3, rank: 37, pin: 0.165, po: 0.5, cache: 0.5 },
99
+ { id: "ministral-8b", name: "Ministral 8B", provider: "Mistral AI", tier: 3, rank: 38, pin: 0.1, po: 0.1, cache: 0.5 },
100
+ { id: "llama-3.2-3b", name: "Llama 3.2 3B", provider: "Meta (Hosted)", tier: 3, rank: 39, pin: 0.02, po: 0.02, cache: 0.5 },
101
+ { id: "ministral-3b", name: "Ministral 3B", provider: "Mistral AI", tier: 3, rank: 40, pin: 0.04, po: 0.04, cache: 0.5 },
102
+ ];
103
+
104
+ const TIER_LABEL: Record<number, string> = {
105
+ 1: "Tier 1 — heavyweight flagships & enterprise intelligence",
106
+ 2: "Tier 2 — mid-range & high-efficiency workhorses",
107
+ 3: "Tier 3 — flash, mini & edge engines",
108
+ };
109
+
110
+ /**
111
+ * Model ids local runs resolve to. Anything not under one of these prefixes
112
+ * is treated as an online/cloud API — the stats panel's local-vs-online split
113
+ * hangs off this. pi runs land on `lmstudio/…`; opencode picks up cloud
114
+ * providers (`opencode/…`, `opencode-go/…`) which are online.
115
+ */
116
+ const LOCAL_MODEL_PREFIXES = ["lmstudio/", "ollama/", "localhost/", "vllm/", "unsloth/"];
117
+ const isLocalModel = (model: string) =>
118
+ LOCAL_MODEL_PREFIXES.some((p) => model.toLowerCase().startsWith(p));
119
+
120
+ /** The fix per failure class — what an operator should change to make it work. */
121
+ const FIXES: Record<string, string> = {
122
+ JSON_CONTRACT: "use a stronger model for the agent (deepseek-v4-flash via ocrd), or harden its user.md: 'Respond with ONLY valid JSON matching <Type>'",
123
+ EMPTY_COMMIT: "drop the redundant commit phase, or gate it on `git status --porcelain` being non-empty",
124
+ STOPPED: "run was killed (SIGTERM) — re-run with more time or resume with --smidja-id",
125
+ AGENT_EXIT: "agent process exited 1 — check the raw stream for the failing tool call",
126
+ HALLUCINATED_BUILD: "builder must actually run tools before reporting — enforce the tool-use mandate in builder/system.md",
127
+ HALLUCINATED_COORDINATION: "orchestrator must dispatch subagents before reporting — enforce in orchestrator/system.md",
128
+ UNKNOWN: "inspect the run with `smidja audit` / `smidja diagnose`",
129
+ };
130
+
131
+ /** Map a failed phase + error to a failure class (mirrors smidja diagnose). */
132
+ export function classifyFailure(phase: string, error: string | null): string {
133
+ const err = error ?? "";
134
+ if (err.includes("never produced valid") || err.includes("JSON")) return "JSON_CONTRACT";
135
+ if (err.includes("nothing to commit")) return "EMPTY_COMMIT";
136
+ if (err === "143" || err === "130") return "STOPPED";
137
+ if (err === "1") return "AGENT_EXIT";
138
+ if (phase === "build") return "HALLUCINATED_BUILD";
139
+ if (phase === "orchestrate") return "HALLUCINATED_COORDINATION";
140
+ return "UNKNOWN";
141
+ }
142
+
143
+ /**
144
+ * Resolve the db path: --db arg wins, then CMD_DB, then <cwd>/apps/smidja/smidja_data/smidja.db.
145
+ * The db lives in the TARGET repo, so cwd is the repo the visualizer is pointed at.
146
+ */
147
+ export function resolveDbPath(argv: string[] = Bun.argv): string {
148
+ const flagIndex = argv.indexOf("--db");
149
+ const inline = argv.find((a) => a.startsWith("--db="));
150
+ const raw =
151
+ (flagIndex !== -1 ? argv[flagIndex + 1] : undefined) ??
152
+ inline?.slice("--db=".length) ??
153
+ process.env.CMD_DB ??
154
+ DEFAULT_DB_RELATIVE;
155
+
156
+ return isAbsolute(raw) ? raw : resolve(process.cwd(), raw);
157
+ }
158
+
159
+ /**
160
+ * The target repo's root, found by walking up from the db to a marker the repo
161
+ * always carries (`.agents/`, else `.git/`). Counting dirnames was brittle: the
162
+ * smithy moved from `<repo>/smidja/` to `<repo>/apps/smidja/`, and a fixed count
163
+ * would have to change in every module that derived anything from it.
164
+ */
165
+ export function repoRootOf(dbPath: string): string {
166
+ let dir = dirname(dbPath);
167
+ for (let i = 0; i < 8; i++) {
168
+ if (existsSync(join(dir, ".agents")) || existsSync(join(dir, ".git"))) return dir;
169
+ const up = dirname(dir);
170
+ if (up === dir) break;
171
+ dir = up;
172
+ }
173
+ return resolve(dirname(dirname(dirname(dbPath)))); // legacy fallback
174
+ }
175
+
176
+ export class smidjaDb {
177
+ readonly path: string;
178
+ /**
179
+ * Where the smidja session dirs live: `{data_dir}/sessions/{smidja_id}/{agent}/`.
180
+ * The db sits in the same data_dir (config's `observability.db` defaults to
181
+ * `apps/smidja/smidja_data/smidja.db`), so deriving it as a sibling of the db file keeps
182
+ * working when the whole data_dir is relocated.
183
+ */
184
+ readonly sessionsDir: string;
185
+ readonly journalMode: string;
186
+ private readonly db: Database;
187
+ /** Opened on first archive and kept; null until then. */
188
+ /// ghost: Kaia auto-admission one-shots (smidja_prompt 'What Kaia already
189
+ /// remembers…' / 'You are admitting smidja session…') are plumbing, already
190
+ /// recorded inside the parent run as handoff/admitted events. Hide them from
191
+ /// the session list unless CMD_SHOW_ADMISSIONS=1.
192
+ private admissionFilter(): string {
193
+ if (process.env.CMD_SHOW_ADMISSIONS === "1") return "";
194
+ const isName = this.hasColumn("sessions", "smidja_name");
195
+ const nameCond = isName
196
+ ? "smidja_name = 'smidja_prompt' AND"
197
+ : "";
198
+ return (
199
+ " AND NOT COALESCE(" +
200
+ nameCond +
201
+ " (request LIKE 'What Kaia already remembers%' OR request LIKE 'You are admitting smidja session%'), 0)"
202
+ );
203
+ }
204
+ private writer: Database | null = null;
205
+ /** Cache for optionalColumn(), keyed "table.column". Only ever false → true. */
206
+ private readonly columnCache = new Map<string, boolean>();
207
+
208
+ constructor(path: string) {
209
+ if (!existsSync(path)) {
210
+ throw new Error(
211
+ `smidja.db not found at ${path}\n` +
212
+ `Point the visualizer at a target repo: --db <path> or CMD_DB=<path>, ` +
213
+ `or run it from a repo root containing ${DEFAULT_DB_RELATIVE}`,
214
+ );
215
+ }
216
+ this.path = path;
217
+ this.sessionsDir = resolve(dirname(path), "sessions");
218
+ this.db = new Database(path, { readonly: true });
219
+
220
+ // WAL is set by the tracer when it creates the db; a readonly connection
221
+ // cannot change it, so we assert rather than set, and always take the
222
+ // busy_timeout so a concurrent writer never turns into a failed request.
223
+ this.db.exec("PRAGMA busy_timeout = 5000");
224
+ this.db.exec("PRAGMA synchronous = NORMAL");
225
+ const mode = this.db
226
+ .query<{ journal_mode: string }, []>("PRAGMA journal_mode")
227
+ .get();
228
+ this.journalMode = mode?.journal_mode ?? "unknown";
229
+ if (this.journalMode.toLowerCase() !== "wal") {
230
+ console.warn(
231
+ `[db] journal_mode is "${this.journalMode}", expected "wal" — ` +
232
+ `live reads during agent writes may block`,
233
+ );
234
+ }
235
+
236
+ }
237
+
238
+ /**
239
+ * A SELECT fragment for a column the tracer adds by migration.
240
+ *
241
+ * We open readonly and cannot run those ALTERs ourselves, so selecting one
242
+ * blindly would throw "no such column" on every request against a db an older
243
+ * tracer wrote. Instead we probe and substitute NULL, which reads downstream
244
+ * as "this db predates the column" — the same thing the UI shows for a row
245
+ * the migration didn't backfill.
246
+ *
247
+ * The probe re-runs while the column is missing, because the tracer's ALTER
248
+ * can land while we're serving: a startup-only check would keep returning
249
+ * NULL for the rest of the process even after the data arrived. Once seen,
250
+ * a column never goes away, so it latches.
251
+ */
252
+ private hasColumn(table: string, column: string): boolean {
253
+ const key = `${table}.${column}`;
254
+ if (!this.columnCache.get(key)) {
255
+ const cols = this.db
256
+ .query<{ name: string }, []>(`PRAGMA table_info(${table})`)
257
+ .all();
258
+ this.columnCache.set(key, cols.some((c) => c.name === column));
259
+ }
260
+ return this.columnCache.get(key) ?? false;
261
+ }
262
+
263
+ private optionalColumn(table: string, column: string): string {
264
+ return this.hasColumn(table, column) ? column : `NULL AS ${column}`;
265
+ }
266
+
267
+ /** Does the table exist? A fresh/new db has no schema until the tracer runs. */
268
+ private hasTable(table: string): boolean {
269
+ const row = this.db
270
+ .query<{ n: number }, [string]>(
271
+ "SELECT COUNT(*) AS n FROM sqlite_master WHERE type='table' AND name=?",
272
+ )
273
+ .get(table);
274
+ return (row?.n ?? 0) > 0;
275
+ }
276
+
277
+ close(): void {
278
+ this.writer?.close();
279
+ this.db.close();
280
+ }
281
+
282
+ /**
283
+ * Archive or restore a session — the only write in this process.
284
+ *
285
+ * busy_timeout matters: a run may be mid-insert on the same WAL db, and a
286
+ * click should wait its turn rather than fail. Returns false when the id
287
+ * does not exist, so the route can 404 instead of silently succeeding.
288
+ */
289
+ setArchived(adwId: string, archived: boolean): boolean {
290
+ if (!this.hasColumn("sessions", "archived")) {
291
+ throw new Error("this db predates the archived column — run any smidja once to migrate it");
292
+ }
293
+ if (!this.writer) {
294
+ this.writer = new Database(this.path);
295
+ this.writer.exec("PRAGMA busy_timeout=5000;");
296
+ }
297
+ this.writer
298
+ .query("UPDATE sessions SET archived = ? WHERE smidja_id = ?")
299
+ .run(archived ? 1 : 0, adwId);
300
+ return this.session(adwId) !== null;
301
+ }
302
+
303
+ /** Live processes of a run — what a Stop button must SIGTERM, children first.
304
+ *
305
+ * Rows keep `ended_at NULL` until the smidja finalizes them; that does not
306
+ * happen if the smidja was killed from code predating the SIGTERM handler,
307
+ * crashed, or was SIGKILL'd. A still-open row can then point at a process
308
+ * that is already dead, and dead pids must not be signalled (Stop/Pause
309
+ * acting on ghosts). Each row is checked against the live process table. */
310
+ liveProcesses(adwId: string): { kind: string; name: string; pid: number; command: string }[] {
311
+ const rows = this.db
312
+ .query<
313
+ { kind: string; name: string; pid: number; command: string },
314
+ [string]
315
+ >(
316
+ "SELECT kind, name, pid, command FROM processes WHERE smidja_id = ? AND ended_at IS NULL ORDER BY id",
317
+ )
318
+ .all(adwId);
319
+ return rows.filter((r) => pidAlive(r.pid));
320
+ }
321
+
322
+ /**
323
+ * Server-side half of Stop: close a run's process rows and mark the session
324
+ * finished. The running smidja does this itself on SIGTERM (session.py's
325
+ * `_finalize_when_killed`), but only when it runs code with that handler and
326
+ * the signal lands cleanly. When it does not (old code, crash, SIGKILL) the
327
+ * rows would say "running" for ever. Idempotent: a run already settled by
328
+ * the smidja (or a session already terminal) is left untouched. Phases that are
329
+ * still `running` are closed as `fail` too, so a stopped run does not show a
330
+ * phase that claims to be in flight long after its processes are gone.
331
+ */
332
+ finalizeStopped(adwId: string): void {
333
+ const row = this.db
334
+ .query<{ status: string | null }, [string]>("SELECT status FROM sessions WHERE smidja_id = ?")
335
+ .get(adwId);
336
+ if (!row) return;
337
+ if (row.status === "success" || row.status === "fail") return;
338
+ this.ensureWriter();
339
+ const now = new Date().toISOString();
340
+ this.writer!.exec("BEGIN");
341
+ try {
342
+ this.writer!
343
+ .query("UPDATE processes SET ended_at = ? WHERE smidja_id = ? AND ended_at IS NULL")
344
+ .run(now, adwId);
345
+ this.writer!
346
+ .query(
347
+ "UPDATE phases SET status = 'fail', error = COALESCE(error, 'stopped'), ended_at = ? " +
348
+ "WHERE smidja_id = ? AND status = 'running'",
349
+ )
350
+ .run(now, adwId);
351
+ this.writer!
352
+ .query("UPDATE sessions SET status = 'fail', ended_at = ? WHERE smidja_id = ?")
353
+ .run(now, adwId);
354
+ this.writer!.exec("COMMIT");
355
+ } catch (error) {
356
+ this.writer!.exec("ROLLBACK");
357
+ throw error;
358
+ }
359
+ }
360
+
361
+ /** Lazily open the writable connection (same pattern as setArchived). */
362
+ private ensureWriter(): void {
363
+ if (!this.writer) {
364
+ this.writer = new Database(this.path);
365
+ this.writer.exec("PRAGMA busy_timeout=5000;");
366
+ this.writer.exec("PRAGMA journal_mode=wal;");
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Reconcile stale `running` rows against reality before they are served.
372
+ *
373
+ * A run is `running` until its smidja finalizes it. If that smidja was killed
374
+ * from code with no SIGTERM finalizer, crashed, or was SIGKILL'd, the row
375
+ * stays `running` forever with open process rows — the trace claims work is
376
+ * in flight that is already dead, and the L1 card keeps offering Stop/Pause
377
+ * that can only act on ghosts. A running session always has its own smidja
378
+ * process registered, so: for every session still marked running, if none of
379
+ * its registered pids are alive any more, finalize it as failed. Idempotent
380
+ * and safe for genuinely-live runs (their smidja pid is alive). Cheap: it only
381
+ * scans the small set of non-terminal sessions. Called at startup and on the
382
+ * L1 list route.
383
+ */
384
+ reconcileStaleRunning(): number {
385
+ // A fresh/new db (0 bytes) has no tables yet — the tracer creates them on
386
+ // its first run. Reading against an empty schema would crash the server
387
+ // ("no such table"), so a brand-new smidja.db is treated as having
388
+ // nothing to reconcile. Tables appear as soon as the first agent runs.
389
+ if (!this.hasTable("sessions")) return 0;
390
+ const running = this.db
391
+ .query<{ smidja_id: string }, []>(
392
+ "SELECT DISTINCT smidja_id FROM sessions WHERE status = 'running'",
393
+ )
394
+ .all();
395
+ let closed = 0;
396
+ for (const { smidja_id } of running) {
397
+ const procs = this.db
398
+ .query<{ pid: number }, [string]>(
399
+ "SELECT pid FROM processes WHERE smidja_id = ? AND ended_at IS NULL",
400
+ )
401
+ .all(smidja_id);
402
+ if (procs.length === 0) continue; // no process rows — leave it for the winner
403
+ if (procs.some((p) => pidAlive(p.pid))) continue; // still genuinely live
404
+ this.finalizeStopped(smidja_id);
405
+ closed += 1;
406
+ }
407
+ // A session that is already terminal but still has `running` phases means a
408
+ // run was finalized (usually by a stop) after its last phase had been closed
409
+ // out of order, or by an older finalizer that left the phase behind. Those
410
+ // phases can never complete — their session is done — so close them as fail.
411
+ closed += this.closeOrphanPhases();
412
+ return closed;
413
+ }
414
+
415
+ /** Close any `running` phases whose session is already terminal (success/fail). */
416
+ private closeOrphanPhases(): number {
417
+ const orphans = this.db
418
+ .query<{ smidja_id: string }, []>(
419
+ "SELECT ph.smidja_id FROM phases ph JOIN sessions s ON s.smidja_id = ph.smidja_id " +
420
+ "WHERE ph.status = 'running' AND s.status IN ('success','fail')",
421
+ )
422
+ .all();
423
+ if (orphans.length === 0) return 0;
424
+ this.ensureWriter();
425
+ const now = new Date().toISOString();
426
+ this.writer!.exec("BEGIN");
427
+ try {
428
+ for (const { smidja_id } of orphans) {
429
+ this.writer!
430
+ .query(
431
+ "UPDATE phases SET status = 'fail', error = COALESCE(error, 'stopped'), ended_at = ? " +
432
+ "WHERE smidja_id = ? AND status = 'running'",
433
+ )
434
+ .run(now, smidja_id);
435
+ }
436
+ this.writer!.exec("COMMIT");
437
+ } catch (error) {
438
+ this.writer!.exec("ROLLBACK");
439
+ throw error;
440
+ }
441
+ return orphans.length;
442
+ }
443
+
444
+ /**
445
+ * Which runs are live right now — the L1 view needs to know a card is
446
+ * running before it offers Pause/Stop. Dead pids (stale open rows) are
447
+ * excluded so a run whose processes have gone is not still offered Stop.
448
+ */
449
+ liveRunIds(): string[] {
450
+ const rows = this.db
451
+ .query<{ smidja_id: string; pid: number }, []>(
452
+ "SELECT DISTINCT smidja_id, pid FROM processes WHERE ended_at IS NULL",
453
+ )
454
+ .all();
455
+ const seen = new Set<string>();
456
+ for (const r of rows) {
457
+ if (pidAlive(r.pid)) seen.add(r.smidja_id);
458
+ }
459
+ return [...seen];
460
+ }
461
+
462
+ /**
463
+ * Decisions — the self-improving surface. Groups the trace by failure class
464
+ * and model so an operator can see what to change to make the system work
465
+ * better, not just that it failed.
466
+ */
467
+ decisions(): DecisionsResponse {
468
+ if (!this.hasTable("sessions")) {
469
+ return { total_failed: 0, decisions: [], generated_at: new Date().toISOString() };
470
+ }
471
+ const failed = this.db
472
+ .query<{ smidja_id: string; smidja_name: string; model: string | null; phase: string; error: string; ran_at: string }, []>(
473
+ `SELECT s.smidja_id, s.smidja_name, ag.model, ph.name AS phase, ph.error,
474
+ COALESCE(s.started_at, '') AS ran_at
475
+ FROM sessions s
476
+ JOIN phases ph ON ph.smidja_id = s.smidja_id AND ph.status = 'fail'
477
+ LEFT JOIN agent_sessions ag ON ag.smidja_id = s.smidja_id AND ag.agent = ph.owner
478
+ WHERE s.status IN ('fail','running')
479
+ ORDER BY s.started_at DESC`,
480
+ )
481
+ .all();
482
+
483
+ const buckets = new Map<string, DecisionsBucket>();
484
+ for (const row of failed) {
485
+ const cls = classifyFailure(row.phase, row.error);
486
+ const key = `${cls}|${row.model ?? 'unknown'}`;
487
+ if (!buckets.has(key)) {
488
+ buckets.set(key, {
489
+ diagnosis: cls,
490
+ model: row.model ?? 'unknown',
491
+ count: 0,
492
+ last_seen: '',
493
+ runs: [],
494
+ fix: FIXES[cls] ?? 'inspect the run',
495
+ });
496
+ }
497
+ const b = buckets.get(key)!;
498
+ b.count += 1;
499
+ b.runs.push(row.smidja_id);
500
+ if (row.ran_at > b.last_seen) b.last_seen = row.ran_at;
501
+ }
502
+ const decisions = [...buckets.values()].toSorted((a, b) => b.count - a.count);
503
+ const total_failed = failed.length;
504
+ return { total_failed, decisions, generated_at: new Date().toISOString() };
505
+ }
506
+
507
+ /**
508
+ * Statistics — runs, tokens, cost, cache-hit ratio, and commercial savings.
509
+ * The token metrics follow the LLM Token Optimization template
510
+ * (assets/llm tokens optimization.md): cache-hit ratio = cached reads /
511
+ * total ingested; commercial savings compare actual spend against OpenAI
512
+ * GPT-4o and Google Gemini rates.
513
+ */
514
+ stats(): StatsResponse {
515
+ if (!this.hasTable("sessions")) {
516
+ const empty = {
517
+ runs: 0, success: 0, fail: 0, running: 0, tokens: 0, cost: 0,
518
+ };
519
+ return {
520
+ totals: empty,
521
+ usage: { input: 0, output: 0, cache_read: 0, cache_write: 0, total: 0 },
522
+ cache_hit_ratio: 0,
523
+ avg_cache_hit_per_run: 0,
524
+ vendors: { gpt4o: { cached_cost: 0, total_cost: 0, savings: 0, savings_pct: 0 }, gemini: { cached_cost: 0, total_cost: 0, savings: 0, savings_pct: 0 } },
525
+ vendor_catalog: [],
526
+ providers: { local: { events: 0, sessions: 0, tokens: 0, cost: 0, input: 0, output: 0, cache_read: 0 }, online: { events: 0, sessions: 0, tokens: 0, cost: 0, input: 0, output: 0, cache_read: 0 }, per_model: [] },
527
+ by_chain: [],
528
+ by_model: [],
529
+ generated_at: new Date().toISOString(),
530
+ };
531
+ }
532
+ const sessions = this.db
533
+ .query<{ smidja_id: string; smidja_name: string; status: string | null; engineer: string | null; total_tokens: number | null; total_cost: number | null; started_at: string | null }, []>(
534
+ `SELECT smidja_id, smidja_name, status, engineer, total_tokens, total_cost, started_at
535
+ FROM sessions ORDER BY started_at DESC`,
536
+ )
537
+ .all();
538
+
539
+ const totals = { runs: 0, success: 0, fail: 0, running: 0, tokens: 0, cost: 0 };
540
+ for (const s of sessions) {
541
+ totals.runs += 1;
542
+ if (s.status === "success") totals.success += 1;
543
+ else if (s.status === "fail") totals.fail += 1;
544
+ else if (s.status === "running") totals.running += 1;
545
+ totals.tokens += s.total_tokens ?? 0;
546
+ totals.cost += s.total_cost ?? 0;
547
+ }
548
+
549
+ // token breakdown + cache-hit ratio from agent_end events (per agent),
550
+ // plus per-model attribution so stats can split local vs online.
551
+ const usage = {
552
+ input: 0, output: 0, cache_read: 0, cache_write: 0, total: 0,
553
+ };
554
+ const cacheHitRuns: number[] = [];
555
+ interface ModelAgg {
556
+ events: number;
557
+ sessions: Set<string>;
558
+ tokens: number;
559
+ cost: number;
560
+ input: number;
561
+ output: number;
562
+ cache_read: number;
563
+ coding_agent: string | null;
564
+ }
565
+ const modelAggs = new Map<string, ModelAgg>();
566
+ // agent_start carries the model/coding_agent for the agent_end that follows.
567
+ const agentModel = new Map<string, string | null>();
568
+ const agentCoding = new Map<string, string | null>();
569
+ // Every model that ever STARTED — so runs that never reported usage (no
570
+ // agent_end telemetry, e.g. some scouts) still land in the per-model list.
571
+ const started = new Map<string, { events: number; sessions: Set<string>; coding_agent: string | null }>();
572
+ const emptyAgg = (): ModelAgg => ({
573
+ events: 0, sessions: new Set(), tokens: 0, cost: 0,
574
+ input: 0, output: 0, cache_read: 0, coding_agent: null,
575
+ });
576
+ for (const s of sessions) {
577
+ const evs = this.eventsRaw(s.smidja_id);
578
+ let r = 0, t = 0;
579
+ for (const e of evs) {
580
+ if (e.type === "agent_start") {
581
+ const sp = e.payload as { model?: string; coding_agent?: string };
582
+ if (sp?.model) {
583
+ const key = `${s.smidja_id}|${e.name}`;
584
+ if (!agentModel.has(key)) {
585
+ const st = started.get(sp.model) ?? { events: 0, sessions: new Set<string>(), coding_agent: sp.coding_agent ?? null };
586
+ st.events += 1;
587
+ st.sessions.add(s.smidja_id);
588
+ st.coding_agent = sp.coding_agent ?? st.coding_agent;
589
+ started.set(sp.model, st);
590
+ }
591
+ agentModel.set(key, sp.model);
592
+ agentCoding.set(key, sp.coding_agent ?? null);
593
+ }
594
+ continue;
595
+ }
596
+ if (e.type !== "agent_end") continue;
597
+ const p = e.payload as { usage?: { input_tokens?: number; output_tokens?: number; cache_read_tokens?: number; cache_write_tokens?: number; total_tokens?: number; input_cost?: number; output_cost?: number; cache_read_cost?: number; cache_write_cost?: number }; cost?: number };
598
+ const u = p?.usage;
599
+ if (!u) continue;
600
+ usage.input += u.input_tokens ?? 0;
601
+ usage.output += u.output_tokens ?? 0;
602
+ usage.cache_read += u.cache_read_tokens ?? 0;
603
+ usage.cache_write += u.cache_write_tokens ?? 0;
604
+ usage.total += u.total_tokens ?? 0;
605
+ r += u.cache_read_tokens ?? 0;
606
+ t += (u.input_tokens ?? 0) + (u.cache_read_tokens ?? 0);
607
+ const model = e.name ? agentModel.get(`${s.smidja_id}|${e.name}`) : undefined;
608
+ if (!model) continue;
609
+ const usageCost = (u.input_cost ?? 0) + (u.output_cost ?? 0) + (u.cache_read_cost ?? 0) + (u.cache_write_cost ?? 0);
610
+ const agg = modelAggs.get(model) ?? emptyAgg();
611
+ agg.events += 1;
612
+ agg.sessions.add(s.smidja_id);
613
+ agg.tokens += u.total_tokens ?? 0;
614
+ agg.cost += usageCost || (p.cost ?? 0);
615
+ agg.input += u.input_tokens ?? 0;
616
+ agg.output += u.output_tokens ?? 0;
617
+ agg.cache_read += u.cache_read_tokens ?? 0;
618
+ const coding = e.name ? agentCoding.get(`${s.smidja_id}|${e.name}`) : undefined;
619
+ agg.coding_agent = coding ?? agg.coding_agent;
620
+ modelAggs.set(model, agg);
621
+ }
622
+ if (t > 0) cacheHitRuns.push(r / t);
623
+ }
624
+
625
+ // Runs that started but never reported usage get listed with calls only
626
+ // (tokens/cost 0) instead of vanishing from the panel.
627
+ for (const [model, st] of started) {
628
+ if (modelAggs.has(model)) continue;
629
+ const agg = emptyAgg();
630
+ agg.events = st.events;
631
+ agg.sessions = st.sessions;
632
+ agg.coding_agent = st.coding_agent;
633
+ modelAggs.set(model, agg);
634
+ }
635
+ const ingested = usage.input + usage.cache_read;
636
+ const cacheHitRatio = ingested > 0 ? usage.cache_read / ingested : 0;
637
+
638
+ // commercial savings (rates per 1M from the tokens-optimization spec)
639
+ const vendor = (pin: number, pcr: number, po: number) => {
640
+ const std = (usage.input / 1_000_000) * pin;
641
+ const cached = (usage.cache_read / 1_000_000) * pcr;
642
+ const out = (usage.output / 1_000_000) * po;
643
+ const total = std + cached + out;
644
+ const savings = total - totals.cost;
645
+ // "% saved" = what we did not pay, as a share of what the vendor would charge
646
+ const savings_pct = total > 0 ? savings / total : 0;
647
+ return { cached_cost: cached, total_cost: total, savings, savings_pct };
648
+ };
649
+ const gpt4o = vendor(2.5, 1.25, 10.0);
650
+ const gemini = vendor(1.25, 0.3125, 5.0);
651
+
652
+ // Every catalog model, computed against the same usage — the UI's
653
+ // dropdown lets an operator ask "what would THIS run have cost on X?".
654
+ const vendor_catalog = VENDOR_MODELS.map((m) => {
655
+ const v = vendor(m.pin, m.pin * m.cache, m.po);
656
+ return {
657
+ id: m.id,
658
+ name: m.name,
659
+ provider: m.provider,
660
+ tier: m.tier,
661
+ rank: m.rank,
662
+ tier_label: TIER_LABEL[m.tier],
663
+ input_price: m.pin,
664
+ output_price: m.po,
665
+ cache_price: m.pin * m.cache,
666
+ ...v,
667
+ };
668
+ });
669
+
670
+ // local vs online rollup — local = own hardware (LM Studio/Ollama/… via
671
+ // pi and other local runners), online = cloud APIs.
672
+ const providerAgg = () => ({ events: 0, sessions: 0, tokens: 0, cost: 0, input: 0, output: 0, cache_read: 0 });
673
+ const providers = { local: providerAgg(), online: providerAgg() };
674
+ for (const [model, agg] of modelAggs) {
675
+ const p = providers[isLocalModel(model) ? "local" : "online"];
676
+ p.events += agg.events;
677
+ p.sessions += agg.sessions.size;
678
+ p.tokens += agg.tokens;
679
+ p.cost += agg.cost;
680
+ p.input += agg.input;
681
+ p.output += agg.output;
682
+ p.cache_read += agg.cache_read;
683
+ }
684
+ const per_model: ModelProviderStat[] = [...modelAggs.entries()]
685
+ .map(([model, agg]) => ({
686
+ model,
687
+ kind: isLocalModel(model) ? ("local" as const) : ("online" as const),
688
+ coding_agent: agg.coding_agent,
689
+ events: agg.events,
690
+ tokens: agg.tokens,
691
+ cost: agg.cost,
692
+ }))
693
+ .toSorted((a, b) => b.tokens - a.tokens);
694
+
695
+ // per-chain and per-model rollups
696
+ const byChain = new Map<string, { runs: number; success: number; tokens: number; cost: number }>();
697
+ const byModel = new Map<string, { runs: number; success: number; tokens: number; cost: number }>();
698
+ for (const s of sessions) {
699
+ const chain = (s.smidja_name ?? "smidja").split(" + ")[0];
700
+ const chainAgg = byChain.get(chain) ?? { runs: 0, success: 0, tokens: 0, cost: 0 };
701
+ chainAgg.runs += 1;
702
+ if (s.status === "success") chainAgg.success += 1;
703
+ chainAgg.tokens += s.total_tokens ?? 0;
704
+ chainAgg.cost += s.total_cost ?? 0;
705
+ byChain.set(chain, chainAgg);
706
+
707
+ const modelAgg = byModel.get(s.smidja_name ?? "?") ?? { runs: 0, success: 0, tokens: 0, cost: 0 };
708
+ modelAgg.runs += 1;
709
+ if (s.status === "success") modelAgg.success += 1;
710
+ modelAgg.tokens += s.total_tokens ?? 0;
711
+ modelAgg.cost += s.total_cost ?? 0;
712
+ byModel.set(s.smidja_name ?? "?", modelAgg);
713
+ }
714
+
715
+ return {
716
+ totals,
717
+ usage,
718
+ cache_hit_ratio: cacheHitRatio,
719
+ avg_cache_hit_per_run: cacheHitRuns.length ? cacheHitRuns.reduce((a, b) => a + b, 0) / cacheHitRuns.length : 0,
720
+ vendors: { gpt4o, gemini },
721
+ vendor_catalog,
722
+ providers: { ...providers, per_model },
723
+ by_chain: [...byChain.entries()].map(([chain, agg]) => Object.assign({ chain }, agg)).toSorted((a, b) => b.runs - a.runs),
724
+ by_model: [...byModel.entries()].map(([model, agg]) => Object.assign({ model }, agg)).toSorted((a, b) => b.runs - a.runs),
725
+ generated_at: new Date().toISOString(),
726
+ };
727
+ }
728
+
729
+ /** Sessions, most recent first, each with its phase statuses for the progress dots. */
730
+ sessions(limit = 200, includeAll = false): SessionSummary[] {
731
+ // Fresh/new db: no schema yet — the tracer creates it on first agent run.
732
+ // Return an empty list instead of crashing on "no such table: sessions".
733
+ if (!this.hasTable("sessions")) return [];
734
+ // includeAll restores the old list behavior: every run (archived ones and
735
+ // Kaia's plumbing one-shots) is listed, so the chat sidebar's "Past
736
+ // sessions" shows everything. Additive — the default stays the current
737
+ // cleaned list.
738
+ const filters: string[] = [];
739
+ if (!includeAll) {
740
+ const admission = this.admissionFilter().replace(/^ AND /, ""); // keeps its own leading " AND " -> strip for the JOIN
741
+ filters.push(
742
+ `COALESCE(${this.hasColumn("sessions", "archived") ? "archived" : "0"}, 0) = 0`,
743
+ admission,
744
+ );
745
+ }
746
+ const clean = filters.filter(Boolean);
747
+ const where = clean.length ? `WHERE ${clean.join(" AND ")}` : "";
748
+ const rows = this.db
749
+ .query<Session, [number]>(
750
+ `SELECT smidja_id, ${this.optionalColumn("sessions", "smidja_name")}, request,
751
+ status, engineer, started_at, ended_at,
752
+ total_tokens, total_cost,
753
+ ${this.optionalColumn("sessions", "archived")}
754
+ FROM sessions
755
+ ${where}
756
+ ORDER BY started_at DESC, rowid DESC
757
+ LIMIT ?`,
758
+ )
759
+ .all(clamp(limit, 1, MAX_LIMIT));
760
+
761
+ if (rows.length === 0) return [];
762
+
763
+ // Embed each session's phases so the L1 progress dots cost no extra request.
764
+ const ids = rows.map((row) => row.smidja_id);
765
+ const placeholders = ids.map(() => "?").join(", ");
766
+ const phaseRows = this.db
767
+ .query<Phase, string[]>(
768
+ `SELECT phase_id, smidja_id, seq, name, kind, owner, description, status,
769
+ attempt, retries, error, started_at, ended_at
770
+ FROM phases WHERE smidja_id IN (${placeholders}) ORDER BY seq, rowid`,
771
+ )
772
+ .all(...ids);
773
+
774
+ const byAdw = new Map<string, Phase[]>();
775
+ for (const phase of phaseRows) {
776
+ const list = byAdw.get(phase.smidja_id);
777
+ if (list) list.push(phase);
778
+ else byAdw.set(phase.smidja_id, [phase]);
779
+ }
780
+
781
+ // Agents come along too: an L1 card draws a per-agent dot timeline, and its
782
+ // dots are colored per agent — without this it would be one request per card.
783
+ const agentsByAdw = this.agentsFor(ids);
784
+
785
+ const summaries: SessionSummary[] = [];
786
+ for (const session of rows) {
787
+ const phases = byAdw.get(session.smidja_id) ?? [];
788
+ summaries.push(
789
+ Object.assign(session, {
790
+ phases,
791
+ phase_count: phases.length,
792
+ agents: agentsByAdw.get(session.smidja_id) ?? [],
793
+ // Derived from the session's first agent for the chat sidebar's past
794
+ // list (SessionLaunch.model) — null when the db predates the column.
795
+ model: agentsByAdw.get(session.smidja_id)?.[0]?.model ?? null,
796
+ }),
797
+ );
798
+ }
799
+ return summaries;
800
+ }
801
+
802
+ session(adwId: string): Session | null {
803
+ return (
804
+ this.db
805
+ .query<Session, [string]>(
806
+ `SELECT smidja_id, ${this.optionalColumn("sessions", "smidja_name")}, request,
807
+ status, engineer, started_at, ended_at,
808
+ total_tokens, total_cost
809
+ FROM sessions WHERE smidja_id = ?`,
810
+ )
811
+ .get(adwId) ?? null
812
+ );
813
+ }
814
+
815
+ phases(adwId: string): Phase[] {
816
+ return this.db
817
+ .query<Phase, [string]>(
818
+ `SELECT phase_id, smidja_id, seq, name, kind, owner, description, status,
819
+ attempt, retries, error, started_at, ended_at
820
+ FROM phases WHERE smidja_id = ? ORDER BY seq, rowid`,
821
+ )
822
+ .all(adwId);
823
+ }
824
+
825
+ agentSessions(adwId: string): AgentSession[] {
826
+ return this.agentsFor([adwId]).get(adwId) ?? [];
827
+ }
828
+
829
+ /**
830
+ * Agents per session, for a set of ids at once: the agent_sessions rows plus
831
+ * anything that has started but not finished.
832
+ *
833
+ * agents.py writes the agent_sessions row only after the envelope persists, so
834
+ * a running agent has no row there — precisely the case the live view exists
835
+ * for. Its model, color and session_id are already on the agent_start event,
836
+ * so a lane is labelled and colored from the moment the agent spawns.
837
+ */
838
+ private agentsFor(adwIds: string[]): Map<string, AgentSession[]> {
839
+ const byAdw = new Map<string, AgentSession[]>();
840
+ if (adwIds.length === 0) return byAdw;
841
+ const placeholders = adwIds.map(() => "?").join(", ");
842
+
843
+ const append = (adwId: string, agent: AgentSession) => {
844
+ const list = byAdw.get(adwId);
845
+ if (list) list.push(agent);
846
+ else byAdw.set(adwId, [agent]);
847
+ };
848
+
849
+ const color = this.optionalColumn("agent_sessions", "color");
850
+ const ctxUsed = this.optionalColumn("agent_sessions", "context_tokens");
851
+ const ctxWindow = this.optionalColumn("agent_sessions", "context_window");
852
+
853
+ const completed = this.db
854
+ .query<AgentSession, string[]>(
855
+ `SELECT smidja_id, agent, coding_agent, model, session_id, ${color},
856
+ ${ctxUsed}, ${ctxWindow}, created_at, last_used_at
857
+ FROM agent_sessions WHERE smidja_id IN (${placeholders})
858
+ ORDER BY created_at, agent`,
859
+ )
860
+ .all(...adwIds);
861
+ for (const row of completed) append(row.smidja_id, row);
862
+
863
+ const started = this.db
864
+ .query<
865
+ {
866
+ smidja_id: string;
867
+ agent: string | null;
868
+ payload_json: string | null;
869
+ started_at: string | null;
870
+ },
871
+ string[]
872
+ >(
873
+ `SELECT e.smidja_id, p.owner AS agent, e.payload_json, e.started_at
874
+ FROM events e JOIN phases p ON p.phase_id = e.phase_id
875
+ WHERE e.smidja_id IN (${placeholders}) AND e.type = 'agent_start'
876
+ ORDER BY e.rowid`,
877
+ )
878
+ .all(...adwIds);
879
+
880
+ for (const row of started) {
881
+ if (!row.agent) continue;
882
+ // A finished row is authoritative; only fill genuine gaps.
883
+ if (byAdw.get(row.smidja_id)?.some((a) => a.agent === row.agent)) continue;
884
+ let payload: AgentStartPayload = {};
885
+ try {
886
+ payload = JSON.parse(row.payload_json ?? "{}") as AgentStartPayload;
887
+ } catch {
888
+ // A malformed payload just means no label — never a failed request.
889
+ }
890
+ append(row.smidja_id, {
891
+ smidja_id: row.smidja_id,
892
+ agent: row.agent,
893
+ coding_agent: null,
894
+ model: payload.model ?? null,
895
+ session_id: payload.session_id ?? null,
896
+ color: payload.color ?? null,
897
+ // Occupancy is only known once the agent's turn closes.
898
+ context_tokens: null,
899
+ context_window: null,
900
+ created_at: row.started_at,
901
+ last_used_at: row.started_at,
902
+ });
903
+ }
904
+ return byAdw;
905
+ }
906
+
907
+ /** Session + phases + agents in one shot — L2 needs all three to draw lanes. */
908
+ sessionDetail(adwId: string): SessionDetail | null {
909
+ const session = this.session(adwId);
910
+ if (!session) return null;
911
+
912
+ return {
913
+ session,
914
+ usage: this.usage(adwId),
915
+ phases: this.phases(adwId),
916
+ agents: this.agentSessions(adwId),
917
+ };
918
+ }
919
+
920
+ /**
921
+ * Raw tokens read and written, beside the billed headline.
922
+ *
923
+ * Derived from the `agent_end` payloads rather than stored, so every run
924
+ * already in the db gets the split without a migration or a re-run.
925
+ *
926
+ * `total_tokens` is a SPEND number: every turn re-sends the whole
927
+ * conversation, so an 86k conversation over 49 turns bills millions. These
928
+ * two say what actually moved — material read for the first time, and
929
+ * material generated. The gap between them and the headline is cached
930
+ * re-reads, which is usually most of it.
931
+ */
932
+ usage(adwId: string): SessionUsage {
933
+ const rows = this.db
934
+ .query<{ payload_json: string | null }, [string]>(
935
+ "SELECT payload_json FROM events WHERE smidja_id = ? AND type = 'agent_end'",
936
+ )
937
+ .all(adwId);
938
+
939
+ let read = 0;
940
+ let written = 0;
941
+ for (const row of rows) {
942
+ if (!row.payload_json) continue;
943
+ try {
944
+ const u = (JSON.parse(row.payload_json) as { usage?: Record<string, number> }).usage;
945
+ if (!u) continue;
946
+ // RAW reads only: material entering the context for the first time,
947
+ // billed either as uncached input or as a cache write. Cache reads are
948
+ // the same tokens served again on later turns — counting them here
949
+ // would rebuild the very inflation this split exists to expose.
950
+ read += (u.input_tokens ?? 0) + (u.cache_write_tokens ?? 0);
951
+ written += u.output_tokens ?? 0;
952
+ } catch {
953
+ /* a payload written by an older tracer simply contributes nothing */
954
+ }
955
+ }
956
+ return { read, written };
957
+ }
958
+
959
+ /**
960
+ * The polling query. Rowid cursor, insertion order, bounded page — the same
961
+ * mechanism serves the live tail and lazy-paged history.
962
+ */
963
+ /**
964
+ * All raw events for a session — the full record, not a page. Used by the
965
+ * stats rollups (token breakdown per agent_end).
966
+ */
967
+ eventsRaw(adwId: string): { type: string; name: string | null; payload: Record<string, unknown> }[] {
968
+ const rows = this.db
969
+ .query<{ type: string; name: string | null; payload_json: string | null }, [string]>(
970
+ "SELECT type, name, payload_json FROM events WHERE smidja_id = ? ORDER BY rowid",
971
+ )
972
+ .all(adwId);
973
+ return rows.map((r) => ({ type: r.type, name: r.name, payload: (r.payload_json ? JSON.parse(r.payload_json) : {}) as Record<string, unknown> }));
974
+ }
975
+
976
+ events(adwId: string, after = 0, limit = DEFAULT_LIMIT): EventsPage {
977
+ const cappedLimit = clamp(limit, 1, MAX_LIMIT);
978
+ const events = this.db
979
+ .query<Event, [string, number, number]>(
980
+ `SELECT rowid, event_id, smidja_id, phase_id, parent_id, type, name,
981
+ payload_json, tokens, started_at, ended_at
982
+ FROM events
983
+ WHERE smidja_id = ? AND rowid > ?
984
+ ORDER BY rowid
985
+ LIMIT ?`,
986
+ )
987
+ .all(adwId, Math.max(0, after), cappedLimit);
988
+
989
+ return {
990
+ events,
991
+ cursor: events.length > 0 ? events[events.length - 1]!.rowid : Math.max(0, after),
992
+ has_more: events.length === cappedLimit,
993
+ };
994
+ }
995
+
996
+ envelopes(adwId: string): Envelope[] {
997
+ return this.db
998
+ .query<Envelope, [string]>(
999
+ `SELECT envelope_id, smidja_id, phase_id, agent, output_type, payload_json,
1000
+ valid, attempt, created_at
1001
+ FROM envelopes WHERE smidja_id = ? ORDER BY created_at, rowid`,
1002
+ )
1003
+ .all(adwId);
1004
+ }
1005
+
1006
+ gates(adwId: string): GateResult[] {
1007
+ const checks = this.optionalColumn("gate_results", "checks_json");
1008
+ return this.db
1009
+ .query<GateResult, [string]>(
1010
+ `SELECT id, smidja_id, phase_id, attempt, gate, passed, violations_json,
1011
+ ${checks}, created_at
1012
+ FROM gate_results WHERE smidja_id = ? ORDER BY id`,
1013
+ )
1014
+ .all(adwId);
1015
+ }
1016
+
1017
+ sessionCount(includeAll = false): number {
1018
+ // Fresh/new db: no schema yet — the tracer creates it on first agent run.
1019
+ if (!this.hasTable("sessions")) return 0;
1020
+ // includeAll mirrors sessions(limit, includeAll): count every run so the
1021
+ // "Past sessions" total agrees with ?scope=all. Default stays the cleaned
1022
+ // count. Additive.
1023
+ const where = includeAll ? "" : `WHERE 1=1 ${this.admissionFilter()}`;
1024
+ const row = this.db
1025
+ .query<{ n: number }, []>(`SELECT COUNT(*) AS n FROM sessions ${where}`)
1026
+ .get();
1027
+ return row?.n ?? 0;
1028
+ }
1029
+ }
1030
+
1031
+ function clamp(value: number, min: number, max: number): number {
1032
+ if (!Number.isFinite(value)) return min;
1033
+ return Math.min(max, Math.max(min, Math.trunc(value)));
1034
+ }
1035
+
1036
+ /**
1037
+ * Is a pid still in the process table? `kill(pid, 0)` sends no signal and only
1038
+ * probes: it succeeds if the process exists (or is a zombie we can't reap), and
1039
+ * throws ESRCH once it is gone. It is the cheapest reliable liveness check and
1040
+ * is what the Stop/Pause endpoints trust before they signal anything.
1041
+ */
1042
+ function pidAlive(pid: number): boolean {
1043
+ if (!Number.isInteger(pid) || pid <= 1) return false;
1044
+ try {
1045
+ process.kill(pid, 0);
1046
+ return true;
1047
+ } catch (error) {
1048
+ return (error as NodeJS.ErrnoException).code === "EPERM"; // exists but owned by other
1049
+ }
1050
+ }