@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,993 @@
1
+ """Config loading/validation and agent execution.
2
+
3
+ Every smidja validates its agents before running (fail fast, nothing spawns
4
+ against a half-valid config). Every agent call parses against a concrete
5
+ output type; parse failures and gate violations re-prompt the SAME session
6
+ with a correction — context intact, bounded retries. Agent proposes, code
7
+ disposes.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import os
14
+ import re
15
+ import shutil
16
+ import time
17
+ from pathlib import Path
18
+ from typing import Literal, Optional
19
+
20
+ import yaml
21
+
22
+ from . import agent_opencode, agent_pi, lm_local, permissions, prompts, stall_watch
23
+ from .data_types import (AgentCall, AgentConfig, EnvelopeBase, EventRecord,
24
+ GateCheck, GateReport, Phase, PhaseParams, PiRequest,
25
+ smidjaConfig, UsageBreakdown)
26
+ from .utils import new_id, now_iso
27
+
28
+ # ── the always-available primary fallback model: Nemotron 3 Ultra Free ─────
29
+ # Slow but good, and always reachable. When an agent's configured model fails
30
+ # to resolve or its dispatch produces no usable output, the send() path retries
31
+ # ONCE on the fallback model, then records a `model_fallback` trace event.
32
+ # Surface-aware: Nemotron works in BOTH coding agents.
33
+ # * opencode (ocrd profile) → opencode/nemotron-3-ultra-free (native alias)
34
+ # * pi harness → openrouter/nvidia/nemotron-3-ultra-550b-a55b:free
35
+ # Override with SMIDJA_FALLBACK_MODEL_PI / SMIDJA_FALLBACK_MODEL_OPENCODE (or set
36
+ # the var to "" to disable the retry entirely).
37
+ FALLBACK_MODEL_PI = os.environ.get(
38
+ "SMIDJA_FALLBACK_MODEL_PI", "openrouter/nvidia/nemotron-3-ultra-550b-a55b:free")
39
+ FALLBACK_MODEL_OPENCODE = os.environ.get(
40
+ "SMIDJA_FALLBACK_MODEL_OPENCODE", "opencode/nemotron-3-ultra-free")
41
+
42
+
43
+ def _fallback_model(agent) -> str | None:
44
+ """The fallback model for an agent's coding surface, if any.
45
+
46
+ Returns None when a fallback is disabled (env empty), when the agent is
47
+ ALREADY on the fallback (no point retrying the same model), or when the
48
+ surface is unknown.
49
+ """
50
+ fb = (FALLBACK_MODEL_OPENCODE if agent.coding_agent == "opencode"
51
+ else FALLBACK_MODEL_PI if agent.coding_agent == "pi" else "")
52
+ if not fb or fb == agent.model:
53
+ return None
54
+ return fb
55
+
56
+
57
+ # Scout roles eligible for the interval context-reset (Pillar C): rotating to a
58
+ # fresh window seeded with the latest checkpoint instead of forgetting early
59
+ # findings when the window approaches 0.85 × its ceiling.
60
+ _SCOUT_AGENTS = frozenset({"scout", "wot-scout"})
61
+ _CONTEXT_RESET_HEADER = (
62
+ "## CONTEXT RESET — new session seeded with the recon's latest checkpoint\n\n"
63
+ "Your window was nearly full, so you were given a fresh one. Everything you "
64
+ "found so far is in the checkpoint(s) below — RE-READ them before continuing, "
65
+ "then keep mapping from where they left off. Cite paths you already found "
66
+ "directly; do not re-walk them from scratch.\n\n"
67
+ )
68
+
69
+
70
+ def _latest_checkpoint(handoff_dir: Path) -> str:
71
+ """Return the newest interval checkpoint's content (capped), or empty."""
72
+ candidates = [
73
+ (handoff_dir / "running_summary.md"),
74
+ (handoff_dir / "scout_findings.md"),
75
+ ]
76
+ import glob as _glob
77
+ numbering = sorted(_glob.glob(str(handoff_dir / "scout_findings_*.md")))
78
+ for path in numbering:
79
+ candidates.append(Path(path))
80
+ best: Optional[Path] = None
81
+ best_mtime = -1
82
+ for path in candidates:
83
+ try:
84
+ m = path.stat().st_mtime
85
+ except OSError:
86
+ continue
87
+ if m > best_mtime:
88
+ best, best_mtime = path, m
89
+ if best is None:
90
+ return ""
91
+ try:
92
+ text = best.read_text(errors="replace")
93
+ except OSError:
94
+ return ""
95
+ return text[:6000] if len(text) > 6000 else text
96
+
97
+
98
+ JSON_FIX_ATTEMPTS = 3 # continue-with-correction attempts for malformed JSON
99
+
100
+
101
+ class GateFailure(RuntimeError):
102
+ pass
103
+
104
+
105
+ # ── config ───────────────────────────────────────────────────────────────────
106
+
107
+ _ENV_VAR = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}")
108
+
109
+
110
+ def _expand_one(text: str) -> str:
111
+ """Expand the first `${VAR:-default}` in `text`, matching nested braces.
112
+
113
+ `default` may itself contain `${...}` (e.g. `${SMIDJA_PLANNER_MODEL:-${SMIDJA_LOCAL_MODEL:-lmstudio/...}}`);
114
+ the closing brace is found by depth counting, so nesting survives.
115
+ """
116
+ i = text.find("${")
117
+ if i == -1:
118
+ return text
119
+ depth, j = 0, i + 2
120
+ while j < len(text):
121
+ if text.startswith("${", j):
122
+ depth += 1
123
+ j += 2
124
+ continue
125
+ if text[j] == "}":
126
+ if depth == 0:
127
+ break
128
+ depth -= 1
129
+ j += 1
130
+ if j >= len(text):
131
+ return text # unclosed — leave as-is
132
+ inner = text[i + 2:j]
133
+ if ":-" in inner:
134
+ name, default = inner.split(":-", 1)
135
+ else:
136
+ name, default = inner, None
137
+ value = os.environ.get(name)
138
+ if value is None:
139
+ value = default if default is not None else ""
140
+ return text[:i] + value + text[j + 1:]
141
+
142
+
143
+ def _expand_env(value):
144
+ """Expand `${VAR}` / `${VAR:-default}` in config strings from the environment.
145
+
146
+ Rosters stay environment-agnostic: write `model: ${SMIDJA_LOCAL_MODEL:-lmstudio/qwen3.5-9b}`
147
+ and each machine's `.env` decides the backend without editing the roster.
148
+
149
+ Defaults may nest (`${SMIDJA_PLANNER_MODEL:-${SMIDJA_LOCAL_MODEL:-lmstudio/...}}`);
150
+ expansion loops until no placeholder remains.
151
+ """
152
+ if isinstance(value, str):
153
+ out = value
154
+ for _ in range(8): # bounded: nested defaults, no cycles
155
+ nxt = _expand_one(out)
156
+ if nxt == out:
157
+ break
158
+ out = nxt
159
+ return out
160
+ if isinstance(value, list):
161
+ return [_expand_env(item) for item in value]
162
+ if isinstance(value, dict):
163
+ return {key: _expand_env(item) for key, item in value.items()}
164
+ return value
165
+
166
+
167
+ def load_config(path: str = "smidja/smidja_smidja_config/smidja.config.yaml") -> smidjaConfig:
168
+ raw = _expand_env(yaml.safe_load(Path(path).read_text()) or {})
169
+ defaults = raw.get("defaults", {}) or {}
170
+ for agent in raw.get("agents", []) or []:
171
+ for key in ("coding_agent", "model", "thinking", "color", "tools", "writes"):
172
+ if key in defaults:
173
+ agent.setdefault(key, defaults[key])
174
+ agent.setdefault("harness_engineering", defaults.get("harness_engineering", []))
175
+ return smidjaConfig(**raw)
176
+
177
+
178
+ def resolve(cfg: smidjaConfig, name: str) -> AgentConfig:
179
+ for agent in cfg.agents:
180
+ if agent.name == name:
181
+ return agent
182
+ raise SystemExit(f"agent {name!r} is not defined in the config — "
183
+ f"available: {[a.name for a in cfg.agents]}")
184
+
185
+
186
+ def validate(cfg: smidjaConfig, required: list[str]) -> None:
187
+ """Fail fast: every required name must resolve to a usable agent."""
188
+ problems = []
189
+ for name in required:
190
+ try:
191
+ agent = resolve(cfg, name)
192
+ except SystemExit as e:
193
+ problems.append(str(e))
194
+ continue
195
+ if agent.coding_agent not in ("pi", "opencode"):
196
+ problems.append(f"agent {name!r}: coding_agent {agent.coding_agent!r} "
197
+ f"is not implemented (pi | opencode)")
198
+ elif agent.coding_agent == "pi":
199
+ try:
200
+ agent_pi.resolve_model(agent.model)
201
+ except ValueError as e:
202
+ problems.append(f"agent {name!r}: {e}")
203
+ else:
204
+ if "/" not in agent.model:
205
+ problems.append(f"agent {name!r}: opencode model must be "
206
+ f"provider/model (no pi registry lookup): {agent.model}")
207
+ for label, ref in (("system", agent.prompt_engineering.system),
208
+ ("user", agent.prompt_engineering.user)):
209
+ if not Path(ref).is_file():
210
+ problems.append(f"agent {name!r}: {label} prompt not found: {ref}")
211
+ if problems:
212
+ raise SystemExit("config validation failed:\n- " + "\n- ".join(problems))
213
+
214
+
215
+ # ── execution ────────────────────────────────────────────────────────────────
216
+
217
+ def execute(run, phase: Phase, call: AgentCall) -> EnvelopeBase:
218
+ """One agent call: render prompts -> pi run -> typed parse -> gates -> envelope."""
219
+ agent = resolve(run.cfg, phase.params.owner)
220
+ agent_dir = run.session_dir / agent.name
221
+ agent_dir.mkdir(parents=True, exist_ok=True)
222
+
223
+ # gates receive (envelope, run); the current phase is what gates must read
224
+ # the agent's raw stream from (loop_guard / tools_were_used).
225
+ run._current_phase = phase
226
+ try:
227
+ return _execute(run, phase, call, agent, agent_dir)
228
+ finally:
229
+ run._current_phase = None
230
+
231
+
232
+ def _execute(run, phase: Phase, call: AgentCall, agent, agent_dir) -> EnvelopeBase:
233
+
234
+ variables = {
235
+ "prompt": call.prompt,
236
+ "previous_envelope": call.previous.model_dump_json(indent=2) if call.previous else "(none)",
237
+ "context_handoff_dir": str(run.context_handoff_dir),
238
+ }
239
+ system_text = prompts.render(agent.prompt_engineering.system, variables)
240
+ user_text = prompts.render(agent.prompt_engineering.user, variables)
241
+
242
+ # Live engineer steering: a `steer.md` written to the session dir (from the
243
+ # visualizer's Pause → steer box, or `smidja steer`) is injected as standing
244
+ # guidance into EVERY subsequent agent call. This is how the engineer tells
245
+ # the run "you are not building what I asked — do this instead" mid-flight.
246
+ steer_file = run.session_dir / "steer.md"
247
+ if steer_file.exists():
248
+ steer_text = steer_file.read_text().strip()
249
+ if steer_text:
250
+ system_text += (
251
+ "\n\n## LIVE ENGINEER GUIDANCE (must follow — highest priority)\n\n"
252
+ f"{steer_text}"
253
+ )
254
+ # Kaia's startup notes (Pillar D admission) ride a separate file so they are
255
+ # clearly inherited context, not a live steer — injected the same standing way.
256
+ kaia_file = run.session_dir / "kaia_notes.md"
257
+ if kaia_file.exists():
258
+ kaia_text = kaia_file.read_text().strip()
259
+ if kaia_text:
260
+ system_text += (
261
+ "\n\n## KAIA ADMISSION NOTES (orchestrator context for this task)\n\n"
262
+ f"{kaia_text}\n\n"
263
+ )
264
+ prompts.save(agent_dir / "prompts", "system.md", system_text)
265
+ prompts.save(agent_dir / "prompts", "user.md", user_text)
266
+
267
+ session_id = _agent_session_id(run, agent)
268
+ run.tracer.event(EventRecord(smidja_id=run.smidja_id, phase_id=phase.phase_id,
269
+ type="agent_start", name=agent.name,
270
+ payload={"model": agent.model, "thinking": agent.thinking,
271
+ "color": agent.color,
272
+ "session_id": session_id,
273
+ "coding_agent": agent.coding_agent,
274
+ "purpose": agent.purpose,
275
+ "tools": agent.tools, # None = all tools
276
+ "harness_engineering": agent.harness_engineering}))
277
+ run.console.agent_started(agent.name, agent.model, session_id)
278
+
279
+ # Parse retries and gate corrections re-enter the SAME pi session, so the
280
+ # last send is the one whose context occupancy is current — while spend is
281
+ # the opposite: every send costs, so usage accumulates across all of them.
282
+ latest: agent_pi.PiResult | None = None
283
+ spent = UsageBreakdown()
284
+ dispatcher = agent_pi.run if agent.coding_agent == "pi" else agent_opencode.run
285
+
286
+ # The model this agent is ACTUALLY on. It starts at the configured model and
287
+ # only ever moves to the fallback (never back), so every send in the phase
288
+ # stays on one model — a session continuation must not silently switch models.
289
+ current_model = agent.model
290
+
291
+ def _dispatch(req: PiRequest) -> agent_pi.PiResult:
292
+ """One dispatcher run, bracketed by process tracking (killable)."""
293
+ if agent.coding_agent == "pi" and req.model.startswith("lmstudio/"):
294
+ # Keep VRAM from overflowing between phases: if this model is not
295
+ # already resident, free the others so it can load cleanly (the
296
+ # llama.cpp engine 400s / SIGABRTs when a load won't fit).
297
+ lm_local.make_room_for(req.model.split("/", 1)[1])
298
+ # G3 stall watchdog: a live lmstudio pi child that stops writing while
299
+ # the GPU is pegged gets unload→reload + killed so the phase recovers
300
+ # (additive; disabled for opencode/cloud, and off via SMIDJA_STALL_WATCH=0).
301
+ watch = stall_watch.watch_for(req, run, phase, agent)
302
+
303
+ def _on_spawn(pid: int) -> None:
304
+ run.tracer.process_start(
305
+ run.smidja_id, "agent", agent.name, pid,
306
+ f"{agent.coding_agent} {agent.name} {req.model}")
307
+ if watch:
308
+ watch.arm(pid)
309
+
310
+ def _on_exit(pid: int) -> None:
311
+ if watch:
312
+ watch.disarm()
313
+ run.tracer.process_end(run.smidja_id, pid)
314
+
315
+ return dispatcher(
316
+ req,
317
+ on_event=_event_forwarder(run, phase, agent.name),
318
+ on_spawn=_on_spawn,
319
+ on_exit=_on_exit)
320
+
321
+ def _usable(result: agent_pi.PiResult) -> bool:
322
+ """Output we can build on: a real response, or a clean (rc 0) exit."""
323
+ return bool(result.text) or result.returncode == 0
324
+
325
+ def send(prompt_text: str) -> agent_pi.PiResult:
326
+ nonlocal latest, session_id, current_model
327
+ # Scout-only context reset: when the previous window is nearly full, a
328
+ # long recon scout would otherwise forget its early findings (pi's
329
+ # auto-compaction only frees space, it never rescues recall). Rotate to
330
+ # a FRESH session seeded with the latest checkpoint file, so the scout
331
+ # keeps going with the context it actually needs. Non-scout agents keep
332
+ # the same window (and pi's own compaction) untouched.
333
+ if (latest is not None and agent.name in _SCOUT_AGENTS
334
+ and latest.context_window and latest.context_tokens
335
+ and latest.context_tokens >= 0.85 * latest.context_window):
336
+ seed = _latest_checkpoint(run.context_handoff_dir)
337
+ if seed:
338
+ fresh = f"smidja-{run.smidja_id}-{agent.name}-{new_id(4)}"
339
+ session_id = fresh
340
+ run.save_agent_map(agent.name, {"session_id": fresh,
341
+ "model": current_model,
342
+ "coding_agent": agent.coding_agent})
343
+ prompt_text = _CONTEXT_RESET_HEADER + seed + "\n\n---\n\n" + prompt_text
344
+ run.tracer.event(EventRecord(
345
+ smidja_id=run.smidja_id, phase_id=phase.phase_id,
346
+ type="log", name="context_reset",
347
+ payload={"agent": agent.name, "session_id": fresh,
348
+ "seed": seed[:80] + "…" if len(seed) > 80 else seed}))
349
+ request = PiRequest(
350
+ prompt=prompt_text,
351
+ system_prompt=system_text,
352
+ model=current_model,
353
+ thinking=agent.thinking,
354
+ session_id=session_id,
355
+ # absolute: these are read by the pi subprocess, which runs in repo_root
356
+ session_dir=str((agent_dir / "pi_sessions").resolve()),
357
+ raw_output_path=str((agent_dir / "raw_output.jsonl").resolve()),
358
+ tools=agent.tools,
359
+ extensions=agent.harness_engineering,
360
+ cwd=str(run.repo_root),
361
+ )
362
+ # Primary attempt on the configured model; a hard failure retries ONCE
363
+ # on the always-available fallback (Nemotron 3 Ultra Free). A dispatch
364
+ # that raises (model unresolvable / provider down) or returns no output
365
+ # is a dead model, not an agent failure — so the phase survives on the
366
+ # fallback with a fresh session rather than aborting the whole run.
367
+ result: agent_pi.PiResult | None = None
368
+ error: str | None = None
369
+ try:
370
+ result = _dispatch(request)
371
+ except Exception as exc: # noqa: BLE001 — model dead, not agent error
372
+ error = str(exc)
373
+ if result is not None and not _usable(result):
374
+ error = (f"returncode={result.returncode}, "
375
+ f"text={'yes' if result.text else 'no'}")
376
+ if error:
377
+ run.tracer.event(EventRecord(
378
+ smidja_id=run.smidja_id, phase_id=phase.phase_id,
379
+ type="model_error", name="dispatch",
380
+ payload={"agent": agent.name, "model": current_model,
381
+ "error": error[:400]}))
382
+ fb = _fallback_model(agent)
383
+ if error and fb:
384
+ fresh = f"smidja-{run.smidja_id}-{agent.name}-{new_id(4)}"
385
+ session_id = fresh
386
+ current_model = fb
387
+ run.save_agent_map(agent.name, {"session_id": fresh,
388
+ "model": fb,
389
+ "coding_agent": agent.coding_agent})
390
+ run.tracer.event(EventRecord(
391
+ smidja_id=run.smidja_id, phase_id=phase.phase_id,
392
+ type="model_fallback", name=agent.coding_agent,
393
+ payload={"agent": agent.name, "from": agent.model, "to": fb,
394
+ "reason": "primary dispatch returned no usable output"}))
395
+ fb_request = PiRequest(
396
+ prompt=request.prompt,
397
+ system_prompt=request.system_prompt,
398
+ model=fb,
399
+ thinking=request.thinking,
400
+ session_id=fresh,
401
+ session_dir=request.session_dir,
402
+ raw_output_path=request.raw_output_path,
403
+ tools=request.tools,
404
+ extensions=request.extensions,
405
+ cwd=request.cwd,
406
+ )
407
+ result = _dispatch(fb_request)
408
+ if result is not None and not _usable(result):
409
+ raise RuntimeError(
410
+ f"{agent.name} produced no usable output on {current_model} "
411
+ f"(fallback {fb or 'disabled'})")
412
+ if result is None:
413
+ raise RuntimeError(
414
+ f"{agent.name} could not dispatch on {current_model} "
415
+ f"({error or 'no result'}) — fallback {fb or 'disabled'}")
416
+ if result.session_id:
417
+ session_id = result.session_id # opencode: real ses_... id, for continuation
418
+ run.add_usage(result.tokens, result.cost)
419
+ spent.merge(result.usage)
420
+ latest = result
421
+ return result
422
+
423
+ # What the tree looked like before this agent got its hands on it. Every
424
+ # send in this phase — first prompt, JSON retries, gate corrections — is
425
+ # measured against this one baseline.
426
+ tree_before = permissions.snapshot(run)
427
+
428
+ result = send(user_text)
429
+ envelope, attempt = _parse_with_retries(run, phase, call, result, send)
430
+
431
+ # claim gates — violations flow back into the SAME session as corrections
432
+ for gate_attempt in range(1, max(1, phase.params.retries + 1) + 1):
433
+ violations = []
434
+ for gate in call.gates:
435
+ report = _as_report(gate(envelope, run))
436
+ found = report.violations
437
+ run.tracer.gate_row(phase, gate.__name__, report, gate_attempt)
438
+ run.tracer.event(EventRecord(
439
+ smidja_id=run.smidja_id, phase_id=phase.phase_id,
440
+ type="gate_fail" if found else "gate_pass", name=gate.__name__,
441
+ payload={"attempt": gate_attempt, "violations": found,
442
+ "checks": [c.model_dump() for c in report.checks]}))
443
+ run.console.gate_result(gate.__name__, report)
444
+ violations.extend(found)
445
+ if not violations:
446
+ break
447
+ if gate_attempt > phase.params.retries:
448
+ raise GateFailure(f"{agent.name} failed gates after {gate_attempt} attempt(s):\n- "
449
+ + "\n- ".join(violations))
450
+ phase.attempt = gate_attempt
451
+ run.console.retry(agent.name, gate_attempt, phase.params.retries,
452
+ f"{len(violations)} gate violation(s)")
453
+ correction = ("Your previous response failed validation:\n- "
454
+ + "\n- ".join(violations)
455
+ + "\n\nFix these problems, then re-emit ONLY your Report JSON.")
456
+ result = send(correction)
457
+ envelope, attempt = _parse_with_retries(run, phase, call, result, send)
458
+
459
+ # Permission is checked after every send is done, and before the envelope is
460
+ # accepted: an agent does not get to report success on a phase in which it
461
+ # wrote somewhere it was not allowed to.
462
+ try:
463
+ touched = permissions.enforce(run, phase, agent, tree_before)
464
+ except permissions.PermissionBreach as breach:
465
+ run.tracer.event(EventRecord(smidja_id=run.smidja_id, phase_id=phase.phase_id,
466
+ type="error", name="permission_breach",
467
+ payload={"agent": agent.name, "error": str(breach),
468
+ "writes": agent.writes,
469
+ "protected_files": run.cfg.defaults.protected_files}))
470
+ raise
471
+ if touched:
472
+ run.tracer.event(EventRecord(smidja_id=run.smidja_id, phase_id=phase.phase_id,
473
+ type="log", name="paths_touched",
474
+ payload={"agent": agent.name, "paths": touched}))
475
+
476
+ _persist_envelope(run, phase, agent.name, call, envelope, attempt, valid=True)
477
+ run.console.envelope_summary(envelope)
478
+ context = latest or result
479
+ run.tracer.agent_session_row(run.smidja_id, agent, session_id,
480
+ context_tokens=context.context_tokens,
481
+ context_window=context.context_window)
482
+ run.save_agent_map(agent.name, {"session_id": session_id, "model": agent.model,
483
+ "coding_agent": agent.coding_agent})
484
+ run.tracer.event(EventRecord(smidja_id=run.smidja_id, phase_id=phase.phase_id,
485
+ type="handoff", name=agent.name,
486
+ payload={"artifacts": envelope.artifacts,
487
+ "summary": envelope.summary}))
488
+ run.tracer.event(EventRecord(smidja_id=run.smidja_id, phase_id=phase.phase_id,
489
+ type="agent_end", name=agent.name,
490
+ # Phase totals, not the last send's: a retried
491
+ # phase paid for every attempt.
492
+ tokens=spent.total_tokens,
493
+ payload={"cost": spent.total_cost,
494
+ "usage": spent.model_dump(),
495
+ "context_tokens": context.context_tokens,
496
+ "context_window": context.context_window}))
497
+ run.console.agent_finished(agent.name, spent.total_tokens, spent.total_cost)
498
+ if envelope.status != "success":
499
+ raise RuntimeError(f"{agent.name} reported status={envelope.status!r}: {envelope.summary}")
500
+ return envelope
501
+
502
+
503
+ # ── internals ────────────────────────────────────────────────────────────────
504
+
505
+ def _as_report(result) -> GateReport:
506
+ """Accept a GateReport, or a legacy gate that returned a violations list."""
507
+ if isinstance(result, GateReport):
508
+ return result
509
+ return GateReport(checks=[GateCheck(item=str(v), ok=False) for v in (result or [])])
510
+
511
+
512
+ def _agent_session_id(run, agent: AgentConfig) -> str:
513
+ entry = run.agent_map.get(agent.name)
514
+ if entry and entry.get("model") == agent.model:
515
+ return entry["session_id"] # rejoin the existing context window
516
+ return f"smidja-{run.smidja_id}-{agent.name}-{new_id(4)}"
517
+
518
+
519
+ # Subagent lanes: the visualizer draws one lane per distinct owner across
520
+ # agent-kind phases, so an orchestrator task-tool subagent that only got a
521
+ # logged trace event was INVISIBLE as a team member. We materialize each
522
+ # dispatch as a real phase row (kind='agent', owner=<subagent>) with its own
523
+ # spans — exactly how a manifest agent earns its lane. Seqs count from a large
524
+ # base so subagent lanes sort after every manifest phase (session-card dots)
525
+ # and can never collide with them.
526
+ _SUBAGENT_SEQ_BASE = 1000
527
+
528
+ # Smidja team roles for task-dispatch lane labelling (matches the harness
529
+ # SUBAGENT_TYPES list). Longest-first so "recon_orchestrator" wins over "recon".
530
+ _SUBAGENT_ROLES = ("recon_orchestrator", "planner", "builder", "reviewer",
531
+ "documenter", "scout", "recon", "ui_builder", "general")
532
+
533
+
534
+ def _infer_task_role(description: str) -> str:
535
+ """Map a task description / subagent_type to a smidja role label.
536
+
537
+ The orchestrator often omits subagent_type (or the model writes it wrong),
538
+ so the lane name is inferred from the description keywords — "Scout
539
+ (read-only recon)…" becomes `scout`, never `general`. Unknown descriptions
540
+ fall back to `general`. Additive helper (G2); the terminal materializer
541
+ keeps using the explicit subagent_type when present.
542
+ """
543
+ hay = (description or "").strip().lower()
544
+ for role in _SUBAGENT_ROLES:
545
+ if role in hay:
546
+ return role
547
+ return "general"
548
+
549
+
550
+ def _materialize_subagent_start(run, phase: Phase, parent_agent: str, event: dict) -> None:
551
+ """Open a subagent lane the moment an orchestrator `task`/`subagent_create`
552
+ tool call STARTS, so the child is visible below Kaia while it runs.
553
+
554
+ Old behavior materialized only at `tool_execution_end` (terminal-only lane,
555
+ and nothing at all if the child stalls). This additive step writes the
556
+ phase row + phase_start + agent_start with status=running immediately; the
557
+ terminal path (kept below) then completes the SAME phase idempotently.
558
+ Deliberately NO agent_sessions row: a task-dispatched `scout` must never
559
+ overwrite a same-named manifest scout's real row (same rule as the closer).
560
+ """
561
+ args = event.get("args") or {}
562
+ sub = _infer_task_role(str(args.get("description") or args.get("subagent_type") or ""))
563
+ if sub == "general" and args.get("subagent_type"):
564
+ sub = str(args.get("subagent_type")).strip().lower()
565
+ description = ((str(args.get("description") or "")).strip()[:140]
566
+ or f"dispatched by {parent_agent} via the task tool")
567
+ prompt = (str(args.get("prompt") or args.get("task") or ""))[-1200:]
568
+ try:
569
+ parent_cfg = resolve(run.cfg, parent_agent)
570
+ model, surface = parent_cfg.model, parent_cfg.coding_agent
571
+ except Exception:
572
+ model, surface = None, None
573
+ run._subagent_counter += 1
574
+ ts = now_iso()
575
+ sub_phase = Phase(
576
+ phase_id=f"{phase.phase_id}::{sub}::{run._subagent_counter}",
577
+ smidja_id=run.smidja_id,
578
+ seq=phase.seq + _SUBAGENT_SEQ_BASE + run._subagent_counter,
579
+ params=PhaseParams(name=sub, kind="agent", owner=sub,
580
+ description=description),
581
+ status="running", started_at=ts, ended_at=None,
582
+ )
583
+ run.tracer.phase_upsert(sub_phase)
584
+ for rec in (
585
+ EventRecord(smidja_id=run.smidja_id, phase_id=sub_phase.phase_id,
586
+ type="phase_start", name=sub,
587
+ payload={"kind": "agent", "owner": sub,
588
+ "description": description,
589
+ "dispatched_by": parent_agent}),
590
+ EventRecord(smidja_id=run.smidja_id, phase_id=sub_phase.phase_id,
591
+ type="agent_start", name=sub,
592
+ payload={"model": model, "thinking": None, "color": None,
593
+ "session_id": None, "coding_agent": surface,
594
+ "purpose": description, "tools": None,
595
+ "harness_engineering": [],
596
+ "dispatched_by": parent_agent,
597
+ "surface": "task-tool"}),
598
+ ):
599
+ run.tracer.event(rec)
600
+ # Track the lane so the terminal closer completes the SAME phase_id.
601
+ call_id = str(event.get("toolCallId") or event.get("id") or "")
602
+ run._open_subagent_lanes.setdefault(phase.phase_id, {})[call_id or sub] = sub_phase.phase_id
603
+
604
+
605
+ def _materialize_subagent(run, phase: Phase, parent_agent: str, event: dict) -> None:
606
+ """Close (or, for a terminal-only dispatch, fully materialize) a subagent lane.
607
+
608
+ If the lane was already opened by `_materialize_subagent_start` at
609
+ tool_execution_start (G2), this idempotently completes that SAME phase:
610
+ agent_end + phase_end + status success/fail, and copies the child's /tmp
611
+ stream into the lane dir. If no open lane exists (opencode reports the task
612
+ RESULT, never a separate start), the phase is written here at completion
613
+ with a real timestamp — the original terminal path, preserved. The subagent
614
+ runs inside the dispatcher's session, so its lane inherits the dispatcher's
615
+ configured model + coding surface; its live session_id and context occupancy
616
+ are unknown here, so they are omitted rather than guessed (no context bar,
617
+ honest label). Deliberately NO agent_sessions row is written: that table's
618
+ key is (smidja_id, agent), so a task-dispatched `scout` must never overwrite a
619
+ same-named manifest scout's real row.
620
+ """
621
+ sub = event.get("subagent_type") or "general"
622
+ status = event.get("status") or "completed"
623
+ ok = status in ("completed", "success")
624
+ description = ((event.get("description") or "").strip()[:140]
625
+ or f"dispatched by {parent_agent} via the task tool")
626
+ prompt = (event.get("prompt") or "")[-1200:]
627
+ output = (event.get("output") or "").strip() or None
628
+
629
+ # ── G2: complete an already-open lane (started at tool_execution_start) ──
630
+ call_id = str(event.get("toolCallId") or event.get("id") or "")
631
+ opened = run._open_subagent_lanes.get(phase.phase_id, {})
632
+ sub_phase_id = opened.pop(call_id, None) or opened.pop(sub, None)
633
+ if sub_phase_id:
634
+ ts = now_iso()
635
+ closer = Phase(
636
+ phase_id=sub_phase_id,
637
+ smidja_id=run.smidja_id,
638
+ seq=phase.seq + _SUBAGENT_SEQ_BASE + run._subagent_counter,
639
+ params=PhaseParams(name=sub, kind="agent", owner=sub,
640
+ description=description),
641
+ status="success" if ok else "fail", started_at=ts, ended_at=ts,
642
+ )
643
+ run.tracer.phase_upsert(closer)
644
+ for rec in (
645
+ EventRecord(smidja_id=run.smidja_id, phase_id=sub_phase_id,
646
+ type="subagent_dispatch", name=sub,
647
+ payload={"description": description, "prompt": prompt,
648
+ "status": status, "agent": parent_agent,
649
+ "surface": "task-tool",
650
+ "dispatcher_phase": phase.phase_id}),
651
+ EventRecord(smidja_id=run.smidja_id, phase_id=sub_phase_id,
652
+ type="agent_end", name=sub, tokens=0,
653
+ payload={"cost": 0.0, "usage": {},
654
+ "context_tokens": None, "context_window": None,
655
+ "dispatched_by": parent_agent, "surface": "task-tool"}),
656
+ EventRecord(smidja_id=run.smidja_id, phase_id=sub_phase_id,
657
+ type="phase_end", name=sub,
658
+ payload={"status": "success" if ok else "fail"}),
659
+ ):
660
+ run.tracer.event(rec)
661
+ _capture_subagent_output(run, sub, sub_phase_id, output)
662
+ _copy_subagent_stream(run, sub, sub_phase_id)
663
+ return
664
+
665
+ # ── Original terminal-only path (no start event seen) ────────────────────
666
+ try:
667
+ parent_cfg = resolve(run.cfg, parent_agent)
668
+ model, surface = parent_cfg.model, parent_cfg.coding_agent
669
+ except Exception:
670
+ model, surface = None, None
671
+ run._subagent_counter += 1
672
+ ts = now_iso()
673
+ sub_phase = Phase(
674
+ phase_id=f"{phase.phase_id}::{sub}::{run._subagent_counter}",
675
+ smidja_id=run.smidja_id,
676
+ seq=phase.seq + _SUBAGENT_SEQ_BASE + run._subagent_counter,
677
+ params=PhaseParams(name=sub, kind="agent", owner=sub,
678
+ description=description),
679
+ status="success" if ok else "fail",
680
+ started_at=ts, ended_at=ts,
681
+ )
682
+ run.tracer.phase_upsert(sub_phase)
683
+ for rec in (
684
+ EventRecord(smidja_id=run.smidja_id, phase_id=sub_phase.phase_id,
685
+ type="phase_start", name=sub,
686
+ payload={"kind": "agent", "owner": sub,
687
+ "description": description,
688
+ "dispatched_by": parent_agent}),
689
+ EventRecord(smidja_id=run.smidja_id, phase_id=sub_phase.phase_id,
690
+ type="agent_start", name=sub,
691
+ payload={"model": model, "thinking": None, "color": None,
692
+ "session_id": None, "coding_agent": surface,
693
+ "purpose": description, "tools": None,
694
+ "harness_engineering": [],
695
+ "dispatched_by": parent_agent,
696
+ "surface": "task-tool"}),
697
+ EventRecord(smidja_id=run.smidja_id, phase_id=sub_phase.phase_id,
698
+ type="subagent_dispatch", name=sub,
699
+ payload={"description": description, "prompt": prompt,
700
+ "status": status, "agent": parent_agent,
701
+ "surface": "task-tool",
702
+ "dispatcher_phase": phase.phase_id}),
703
+ EventRecord(smidja_id=run.smidja_id, phase_id=sub_phase.phase_id,
704
+ type="agent_end", name=sub, tokens=0,
705
+ payload={"cost": 0.0, "usage": {},
706
+ "context_tokens": None, "context_window": None,
707
+ "dispatched_by": parent_agent, "surface": "task-tool"}),
708
+ EventRecord(smidja_id=run.smidja_id, phase_id=sub_phase.phase_id,
709
+ type="phase_end", name=sub,
710
+ payload={"status": "success" if ok else "fail"}),
711
+ ):
712
+ run.tracer.event(rec)
713
+ _capture_subagent_output(run, sub, sub_phase.phase_id, output)
714
+ _copy_subagent_stream(run, sub, sub_phase.phase_id)
715
+
716
+
717
+ def _capture_subagent_output(run, sub: str, sub_phase_id: str, output: str | None) -> None:
718
+ """Capture the child sub-agent's REAL final report into its lane (P1).
719
+
720
+ opencode's native `task` tool returns the child's task_result in the tool
721
+ state.output — the parent never sees the child's internal stream, only this
722
+ final text. Writing it as an `agent_output` event + `output.txt` makes the
723
+ lane's "outputs" section show what the child ACTUALLY produced instead of
724
+ an empty shell. Additive; absent output is a no-op.
725
+ """
726
+ if not output:
727
+ return
728
+ clipped = output[:30000]
729
+ try:
730
+ lane_dir = Path(run.data_base) / "sessions" / run.smidja_id / sub
731
+ lane_dir.mkdir(parents=True, exist_ok=True)
732
+ (lane_dir / "output.txt").write_text(clipped, encoding="utf-8")
733
+ except Exception:
734
+ pass
735
+ run.tracer.event(EventRecord(
736
+ smidja_id=run.smidja_id, phase_id=sub_phase_id,
737
+ type="agent_output", name=sub,
738
+ payload={"text": clipped, "surface": "task-tool", "agent": sub}))
739
+
740
+
741
+ def _copy_subagent_stream(run, sub: str, sub_phase_id: str) -> None:
742
+ """Best-effort: carry the child pi's stream into the lane dir so the
743
+ visualizer can render the sub-agent's thinking too (the thinking endpoint
744
+ parses raw_output.jsonl pi delta streams under the agent dir). The child's
745
+ session file lives in /tmp (task tool --session). Copy, never move: the
746
+ /tmp file is the active child's live record while it runs. The merged
747
+ subagents.ts task tool ALSO mirrors to the persistent subagents path, so a
748
+ subagent-* copy is attempted first, then the smidja's smidja-task-* name.
749
+ """
750
+ try:
751
+ lane_dir = Path(run.data_base) / "sessions" / run.smidja_id / sub
752
+ lane_dir.mkdir(parents=True, exist_ok=True)
753
+ dest = lane_dir / "raw_output.jsonl"
754
+ candidates = sorted(
755
+ [p for p in Path("/tmp").glob("smidja-task-*.jsonl")
756
+ if p.stat().st_mtime < time.time()] +
757
+ [p for p in Path(os.path.expanduser("~/.pi/agent/sessions/subagents")).glob("subagent-task-*.jsonl")],
758
+ key=lambda p: p.stat().st_mtime, reverse=True,
759
+ )
760
+ if candidates:
761
+ shutil.copyfile(candidates[0], dest)
762
+ except Exception:
763
+ pass
764
+
765
+
766
+ def _event_forwarder(run, phase: Phase, agent_name: str):
767
+ """One tool_call event per real tool call, plus compact events per compaction."""
768
+ tracker = agent_pi.ToolCallTracker()
769
+ activity = agent_pi.ActivityObserver()
770
+
771
+ def forward(event: dict) -> None:
772
+ if event.get("type") == "tool_execution_start" and (
773
+ event.get("toolName") in ("task", "subagent_create")):
774
+ # G2 live lanes: open the subagent lane the moment the dispatch
775
+ # starts (status=running), so the child is visible below Kaia while
776
+ # it runs instead of only appearing at tool_execution_end. The
777
+ # terminal path below completes the SAME lane idempotently.
778
+ _materialize_subagent_start(run, phase, agent_name, event)
779
+ return
780
+ if event.get("type") == "subagent_dispatch":
781
+ # opencode `task`-tool subagent dispatch (agent_opencode). One
782
+ # dispatch = one real agent the orchestrator spawned, and it must
783
+ # surface as its OWN lane exactly like a manifest agent, not just
784
+ # as a logged event — so we materialize it as a phase row + spans.
785
+ # Gates still verify dispatch from raw_output.jsonl (their source).
786
+ _materialize_subagent(run, phase, agent_name, event)
787
+ return
788
+ if (event.get("type") == "tool_execution_end"
789
+ and (event.get("toolName") in ("task", "subagent_dispatch"))):
790
+ # pi `task`-tool subagent dispatch (task-dispatch.ts extension).
791
+ # The child ran synchronously, so the _end event carries the result
792
+ # and we materialize the subagent lane at completion — exactly the
793
+ # same contract as opencode's terminal `task` result. Still traced
794
+ # as a plain tool call below? No: a dispatch is a lane, not a tool.
795
+ eargs = event.get("args") or {}
796
+ result_block = event.get("result") or {}
797
+ _materialize_subagent(run, phase, agent_name, {
798
+ "type": "subagent_dispatch",
799
+ "subagent_type": eargs.get("subagent_type")
800
+ or eargs.get("agent") or "general",
801
+ "description": eargs.get("description") or "",
802
+ "prompt": eargs.get("prompt") or "",
803
+ "status": "completed" if not event.get("isError") else "failed",
804
+ "output": (result_block.get("content") or [{}])[0].get("text")
805
+ if isinstance(result_block.get("content"), list)
806
+ else "",
807
+ })
808
+ return
809
+ if event.get("type") == "thinking":
810
+ # Model chain-of-thought (opencode reasoning parts; pi thinking blocks
811
+ # live only in pi_sessions). Kept out of the tool/compact branch so it
812
+ # can coexist; the visualizer also reads the raw streams directly.
813
+ run.tracer.event(EventRecord(smidja_id=run.smidja_id, phase_id=phase.phase_id,
814
+ type="thinking", name=agent_name,
815
+ payload={"text": (event.get("text") or "")[-2000:],
816
+ "surface": event.get("surface", ""),
817
+ "agent": agent_name}))
818
+ return
819
+ if event.get("type") == "usage":
820
+ # P2 additive live-spend: the dispatcher (opencode step-finish)
821
+ # reports tokens/cost as they happen. Trace it AND bump the session
822
+ # row NOW so the UI's cost/token numbers move while the run is live
823
+ # instead of staying 0 until the phase ends. Additive — the
824
+ # existing end-of-phase add_usage still finalizes.
825
+ tokens = int(event.get("tokens") or 0)
826
+ cost = float(event.get("cost") or 0.0)
827
+ run.add_usage(tokens, cost)
828
+ run.tracer.event(EventRecord(
829
+ smidja_id=run.smidja_id, phase_id=phase.phase_id,
830
+ type="usage", name=agent_name, tokens=tokens,
831
+ payload={"cost": cost, "surface": event.get("surface", ""),
832
+ "agent": agent_name}))
833
+ return
834
+ record = tracker.observe(event)
835
+ if record is not None:
836
+ # The call's span rides the columns; duration_ms stays in the payload as
837
+ # pi's own authoritative number.
838
+ run.tracer.event(EventRecord(smidja_id=run.smidja_id, phase_id=phase.phase_id,
839
+ type="tool_call", name=record.pop("label"),
840
+ started_at=record.pop("started_at", None),
841
+ ended_at=record.pop("ended_at", None),
842
+ payload={**record, "agent": agent_name}))
843
+ return
844
+ compact = activity.observe(event)
845
+ if compact is not None:
846
+ run.tracer.event(EventRecord(smidja_id=run.smidja_id, phase_id=phase.phase_id,
847
+ type="compact", name=compact.pop("subtype", ""),
848
+ payload={**compact, "agent": agent_name}))
849
+ return forward
850
+
851
+
852
+ def _extract_json(text: str) -> dict:
853
+ candidate = text
854
+ if "```" in text:
855
+ for block in text.split("```")[1::2]:
856
+ block = block.removeprefix("json").strip()
857
+ if block.startswith("{"):
858
+ candidate = block
859
+ break
860
+ start, end = candidate.find("{"), candidate.rfind("}")
861
+ if start == -1 or end <= start:
862
+ raise ValueError("no JSON object found in the response")
863
+ return json.loads(candidate[start:end + 1])
864
+
865
+
866
+ def _parse_with_retries(run, phase: Phase, call: AgentCall, result, send):
867
+ """Parse the final response against the declared output type; on failure,
868
+ continue the SAME session with a correction (bounded)."""
869
+ for attempt in range(1, JSON_FIX_ATTEMPTS + 2):
870
+ try:
871
+ payload = _extract_json(result.text)
872
+ return call.output_type.model_validate(payload), attempt
873
+ except Exception as error:
874
+ # G4 additive fallback — a truncated/mistyped Literal value (e.g.
875
+ # `"Succes"` from a model that clipped the enum mid-token) is
876
+ # repaired to the unique full literal BEFORE we re-prompt, so the
877
+ # run survives on the SAME session instead of spending a correction
878
+ # round on a typo. Strict path first; this only runs when that fails.
879
+ repaired = _lenient_literal_repair(_extract_json(result.text),
880
+ call.output_type)
881
+ if repaired is not None:
882
+ try:
883
+ return call.output_type.model_validate(repaired), attempt
884
+ except Exception:
885
+ pass # repair didn't help — fall through to the correction
886
+ _persist_envelope(run, phase, phase.params.owner, call, None, attempt,
887
+ valid=False, raw=result.text)
888
+ if attempt > JSON_FIX_ATTEMPTS:
889
+ raise RuntimeError(
890
+ f"{phase.params.owner} never produced valid "
891
+ f"{call.output_type.__name__} JSON: {error}") from error
892
+ run.console.retry(phase.params.owner, attempt, JSON_FIX_ATTEMPTS,
893
+ f"invalid {call.output_type.__name__} JSON: {error}")
894
+ # Send back the EXACT error AND a concrete, filled-in example of the
895
+ # envelope, so the model fixes rather than guesses. A model that
896
+ # emitted prose needs to SEE the target shape, not just be told it
897
+ # was wrong — small/local models can't infer a schema from a name.
898
+ example = _envelope_example(call.output_type, result.text)
899
+ result = send(
900
+ f"Your previous response was not valid JSON for the required "
901
+ f"structure.\n\nError: {error}\n\n"
902
+ f"The response must be EXACTLY one JSON object — no prose, no "
903
+ f"markdown code fences, no trailing text — with these fields: "
904
+ f"{', '.join(call.output_type.model_fields.keys())}.\n\n"
905
+ f"Here is the exact shape with YOUR content filled in:\n"
906
+ f"{example}\n\n"
907
+ f"Reply with ONLY that JSON object.")
908
+
909
+
910
+ def _lenient_literal_repair(payload: dict, output_type) -> dict | None:
911
+ """Additive G4 repair: for each Literal-typed envelope field whose value is
912
+ a PREFIX of exactly one allowed literal, substitute the full literal.
913
+
914
+ Handles `"Succes"`→`"success"`, `"fail"` already valid, `"f"`→`"fail"`, and
915
+ leaves everything else untouched. Returns None when nothing changed (so the
916
+ caller keeps the strict error path). Never loosens the strict path — a value
917
+ that is NOT a unique prefix is left alone and fails as before.
918
+ """
919
+ from typing import get_args, get_origin
920
+
921
+ changed = False
922
+ for name, field in output_type.model_fields.items():
923
+ if name not in payload or not isinstance(payload[name], str):
924
+ continue
925
+ ann = field.annotation
926
+ if get_origin(ann) is not Literal:
927
+ continue
928
+ allowed = {a for a in get_args(ann) if isinstance(a, str)}
929
+ if not allowed:
930
+ continue
931
+ raw = payload[name]
932
+ if raw in allowed:
933
+ continue
934
+ # Case-insensitive unique prefix match — `"Succes"` → `"success"`,
935
+ # `"FAIL"` → `"fail"` — so a model that clipped or mistyped the enum
936
+ # casing survives without a correction round.
937
+ low = raw.lower()
938
+ matches = [a for a in allowed if a.lower().startswith(low)]
939
+ if len(matches) == 1:
940
+ payload[name] = matches[0]
941
+ changed = True
942
+ return payload if changed else None
943
+
944
+
945
+ def _envelope_example(output_type, raw_text: str) -> str:
946
+ """Build a concrete, prefilled JSON example of the output envelope so the
947
+ model can correct its format instead of guessing the schema.
948
+
949
+ Uses the model's own words from `raw_text` where they fit, so the retry
950
+ keeps the work the model already did and only fixes the envelope.
951
+ """
952
+ import json as _json
953
+
954
+ fields = {name: None for name in output_type.model_fields.keys()}
955
+ for name in fields:
956
+ ann = output_type.model_fields[name].annotation
957
+ tname = getattr(ann, "__name__", str(ann))
958
+ if tname in ("str",):
959
+ fields[name] = "…"
960
+ elif tname in ("bool",):
961
+ fields[name] = False
962
+ elif tname in ("int", "float"):
963
+ fields[name] = 0
964
+ elif "list" in tname.lower():
965
+ fields[name] = []
966
+ elif "dict" in tname.lower():
967
+ fields[name] = {}
968
+ # Prefer the model's own summary/changed_files if it mentioned them.
969
+ for name, hint in (("summary", "Summary:"), ("changed_files", "changed"),
970
+ ("status", "success"), ("commit_message", "commit")):
971
+ if name not in fields:
972
+ continue
973
+ for line in raw_text.splitlines():
974
+ low = line.lower()
975
+ if hint.lower() in low and ":" in line:
976
+ val = line.split(":", 1)[1].strip().strip('"').strip("'")
977
+ if val:
978
+ fields[name] = val if not isinstance(fields[name], list) else [val]
979
+ break
980
+ return _json.dumps(fields, indent=2)
981
+
982
+
983
+ def _persist_envelope(run, phase: Phase, agent_name: str, call: AgentCall,
984
+ envelope: Optional[EnvelopeBase], attempt: int,
985
+ valid: bool, raw: str = "") -> None:
986
+ payload_json = envelope.model_dump_json(indent=2) if envelope else json.dumps({"raw": raw[-2000:]})
987
+ run.tracer.envelope_row(phase, agent_name, call.output_type.__name__,
988
+ payload_json, valid, attempt)
989
+ if envelope:
990
+ record = {"agent_name": agent_name, "purpose": resolve(run.cfg, agent_name).purpose,
991
+ "output_type": call.output_type.__name__, "attempt": attempt,
992
+ **envelope.model_dump()}
993
+ (run.session_dir / agent_name / "envelope.json").write_text(json.dumps(record, indent=2))