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