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