@iowarp/clio-coder 0.4.9 → 0.5.0
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/.claude-plugin/marketplace.json +11 -3
- package/CHANGELOG.md +153 -0
- package/CODE_OF_CONDUCT.md +10 -0
- package/CONTRIBUTING.md +208 -382
- package/NOTICE +8 -2
- package/README.md +199 -418
- package/ROADMAP.md +82 -0
- package/SECURITY.md +14 -11
- package/dist/acp-KS7ARGV6.js +132 -0
- package/dist/agents-YOGJ2YEJ.js +163 -0
- package/dist/assets/codewiki.json +1 -1
- package/dist/auth-CEDCO6O6.js +557 -0
- package/dist/background-YWTBPSQU.js +39 -0
- package/dist/builtins-GDYO4RTR.js +23 -0
- package/dist/chunk-27YTM2CV.js +175 -0
- package/dist/chunk-2AF6YC7Q.js +75 -0
- package/dist/chunk-2DTIWSAW.js +381 -0
- package/dist/chunk-2EMLKSCX.js +262 -0
- package/dist/chunk-2OJ3YZQP.js +665 -0
- package/dist/chunk-2VLYHHFT.js +35 -0
- package/dist/chunk-2WVPJF3Z.js +204 -0
- package/dist/chunk-33OASOS6.js +176 -0
- package/dist/chunk-3BD2LXYJ.js +19 -0
- package/dist/chunk-3JHUFKG7.js +30 -0
- package/dist/chunk-3JLTRPN7.js +711 -0
- package/dist/chunk-3LT34CAM.js +2796 -0
- package/dist/chunk-3O5JWP4L.js +1670 -0
- package/dist/chunk-3UW3KU4A.js +21 -0
- package/dist/chunk-4A6YRGV4.js +569 -0
- package/dist/chunk-4D2BRCZ3.js +67 -0
- package/dist/chunk-4SYHGVDE.js +668 -0
- package/dist/chunk-4VDHLCF5.js +66 -0
- package/dist/chunk-4WOOTKFD.js +637 -0
- package/dist/chunk-4WV4PI66.js +80 -0
- package/dist/chunk-53OXPRI6.js +195 -0
- package/dist/chunk-55RMDDFM.js +1347 -0
- package/dist/chunk-56XP4FJU.js +2025 -0
- package/dist/chunk-5B3RTYAK.js +123 -0
- package/dist/chunk-5CD243WE.js +45 -0
- package/dist/chunk-5JQR5B4D.js +709 -0
- package/dist/chunk-5KQEUIWM.js +597 -0
- package/dist/chunk-5LP5FNNR.js +915 -0
- package/dist/chunk-62UK7AGW.js +9344 -0
- package/dist/chunk-6CTHLZFB.js +563 -0
- package/dist/chunk-6EJYVCTN.js +23 -0
- package/dist/chunk-6OWUM2NC.js +12289 -0
- package/dist/chunk-6RJMYYIE.js +1023 -0
- package/dist/chunk-6W75D37F.js +643 -0
- package/dist/chunk-733ZBT5P.js +1721 -0
- package/dist/chunk-77DCC6JZ.js +3710 -0
- package/dist/chunk-7QXCO3AF.js +156 -0
- package/dist/chunk-7RET77VB.js +481 -0
- package/dist/chunk-A4MVRV4C.js +65 -0
- package/dist/chunk-ALJWT6O6.js +93 -0
- package/dist/chunk-AVSR2HKI.js +232 -0
- package/dist/chunk-B7GY6QER.js +42 -0
- package/dist/chunk-BFOSV5EZ.js +178 -0
- package/dist/chunk-BGOIM7NB.js +103 -0
- package/dist/chunk-BJH6WFIB.js +372 -0
- package/dist/chunk-BOBCCDJ6.js +1533 -0
- package/dist/chunk-BRCOSE7O.js +117 -0
- package/dist/chunk-BYFHHCFV.js +601 -0
- package/dist/chunk-C2CPOIH2.js +83 -0
- package/dist/chunk-C7AQTPCE.js +694 -0
- package/dist/chunk-CBAQPTDU.js +165 -0
- package/dist/chunk-CDQ2UC6W.js +125 -0
- package/dist/chunk-CN6SW5DC.js +177 -0
- package/dist/chunk-CRHPCPZP.js +132 -0
- package/dist/chunk-CTFPFW3H.js +44 -0
- package/dist/chunk-D3KWFOUT.js +103 -0
- package/dist/chunk-D65E25JG.js +185 -0
- package/dist/chunk-DCGHJQ2U.js +236 -0
- package/dist/chunk-DE2LU267.js +106 -0
- package/dist/chunk-DF6PI6GN.js +97 -0
- package/dist/chunk-DGC6X6XR.js +53 -0
- package/dist/chunk-DOUISWEE.js +526 -0
- package/dist/chunk-DR3TAEUA.js +188 -0
- package/dist/chunk-DVTBERFY.js +33 -0
- package/dist/chunk-DXSETN72.js +1051 -0
- package/dist/chunk-DZLR4UEH.js +128 -0
- package/dist/chunk-E6IPKW2E.js +77 -0
- package/dist/chunk-EG4ARXPC.js +139 -0
- package/dist/chunk-EKV4UBCM.js +652 -0
- package/dist/chunk-EOJPDNUP.js +1325 -0
- package/dist/chunk-EX57VVWW.js +4157 -0
- package/dist/chunk-EYTLV3W3.js +5937 -0
- package/dist/chunk-EZD2BXGP.js +475 -0
- package/dist/chunk-EZDCA34W.js +314 -0
- package/dist/chunk-FD2I4KBR.js +33 -0
- package/dist/chunk-FD3IAHQM.js +27 -0
- package/dist/chunk-FFYTC2KQ.js +416 -0
- package/dist/chunk-FGSQHUCK.js +110 -0
- package/dist/chunk-GCQFN7YI.js +32 -0
- package/dist/chunk-GGPF6LLE.js +38 -0
- package/dist/chunk-GGWI3OVM.js +215 -0
- package/dist/chunk-GLIQT7SU.js +172 -0
- package/dist/chunk-GN7QBEEI.js +95 -0
- package/dist/chunk-GRHOXDKB.js +1104 -0
- package/dist/chunk-HMN2C6YL.js +54 -0
- package/dist/chunk-HOEDA42N.js +310 -0
- package/dist/chunk-HOYZUKG7.js +192 -0
- package/dist/chunk-HQVN7G4F.js +1021 -0
- package/dist/chunk-HSQQFDB3.js +519 -0
- package/dist/chunk-I4ELN5BX.js +47 -0
- package/dist/chunk-I4Y4WKZR.js +386 -0
- package/dist/chunk-IFPDAD7R.js +797 -0
- package/dist/chunk-IN7DGBVS.js +582 -0
- package/dist/chunk-ISHUS7HC.js +396 -0
- package/dist/chunk-JPXLJSGR.js +191 -0
- package/dist/chunk-JQCLXSFI.js +69 -0
- package/dist/chunk-KBUTSJSR.js +61 -0
- package/dist/chunk-KW5HXKLJ.js +56 -0
- package/dist/chunk-L4APUGCT.js +139 -0
- package/dist/chunk-L7G7K4I2.js +505 -0
- package/dist/chunk-LAR7DX5E.js +1840 -0
- package/dist/chunk-LAVUQX3B.js +462 -0
- package/dist/chunk-LJRBX7QR.js +116 -0
- package/dist/chunk-LNOXCGN2.js +1864 -0
- package/dist/chunk-LTWH2ACR.js +23002 -0
- package/dist/chunk-LYJJ6546.js +661 -0
- package/dist/chunk-M2IPMWCN.js +457 -0
- package/dist/chunk-MGLRTOCK.js +101 -0
- package/dist/chunk-MZF5IKZT.js +160 -0
- package/dist/chunk-N24R6KWS.js +386 -0
- package/dist/chunk-NASYVWDB.js +604 -0
- package/dist/chunk-NBXFPI7U.js +195 -0
- package/dist/chunk-NDPF2N2L.js +400 -0
- package/dist/chunk-NFSK2VNU.js +446 -0
- package/dist/chunk-NG3QQSXY.js +43 -0
- package/dist/chunk-NOCSM22Y.js +63 -0
- package/dist/chunk-NVPXXW2H.js +1143 -0
- package/dist/chunk-O63U2I6I.js +30 -0
- package/dist/chunk-OJXOA6YU.js +98 -0
- package/dist/chunk-OPD7GL6F.js +1196 -0
- package/dist/chunk-OQ2EOAHA.js +14399 -0
- package/dist/chunk-OV62D6KO.js +688 -0
- package/dist/chunk-OZ76TLGU.js +740 -0
- package/dist/chunk-P4GNZPXO.js +1156 -0
- package/dist/chunk-PS6JAWJZ.js +78 -0
- package/dist/chunk-PXRCXGLK.js +1865 -0
- package/dist/chunk-QDUUOZ3S.js +108 -0
- package/dist/chunk-QG6I2T5E.js +1416 -0
- package/dist/chunk-QHSBROEW.js +308 -0
- package/dist/chunk-QNUQ7K7D.js +46 -0
- package/dist/chunk-QZTTRSJE.js +333 -0
- package/dist/chunk-R2UUNSNQ.js +1605 -0
- package/dist/chunk-R7FGRZNF.js +1794 -0
- package/dist/chunk-RNDCROSG.js +199 -0
- package/dist/chunk-S36CXHO2.js +302 -0
- package/dist/chunk-SJGS3GDI.js +575 -0
- package/dist/chunk-SQZHXUB3.js +510 -0
- package/dist/chunk-SUTLW6ND.js +686 -0
- package/dist/chunk-SYF7FVBL.js +179 -0
- package/dist/chunk-T2MDZES3.js +1577 -0
- package/dist/chunk-T33IYTZM.js +1146 -0
- package/dist/chunk-TDGYTWM6.js +49 -0
- package/dist/chunk-TGL5FZVD.js +132 -0
- package/dist/chunk-TGWZXJ7L.js +1205 -0
- package/dist/chunk-TKDRT2DF.js +114 -0
- package/dist/chunk-TV2EBCSQ.js +152 -0
- package/dist/chunk-U26XXPJS.js +367 -0
- package/dist/chunk-U5QU5ZOD.js +419 -0
- package/dist/chunk-U6YTUVOX.js +1011 -0
- package/dist/chunk-UCINIRHW.js +188 -0
- package/dist/chunk-UJD5MPP6.js +52 -0
- package/dist/chunk-URCXX7WJ.js +146 -0
- package/dist/chunk-UX2L5CUD.js +898 -0
- package/dist/chunk-UZVYEF57.js +114 -0
- package/dist/chunk-V35KZDUZ.js +218 -0
- package/dist/chunk-VESL6DQY.js +1058 -0
- package/dist/chunk-VKHLUZNO.js +986 -0
- package/dist/chunk-VOYUCAQF.js +584 -0
- package/dist/chunk-VYRQHORQ.js +414 -0
- package/dist/chunk-W53XSRQE.js +245 -0
- package/dist/chunk-X4CEFOSG.js +226 -0
- package/dist/chunk-XDHUDE5K.js +380 -0
- package/dist/chunk-XISLDHEL.js +1290 -0
- package/dist/chunk-XM34YQ2Y.js +169 -0
- package/dist/chunk-XMDBTYCH.js +661 -0
- package/dist/chunk-XS2QPZJE.js +449 -0
- package/dist/chunk-YBUECLAF.js +618 -0
- package/dist/chunk-YQ6XEFVK.js +332 -0
- package/dist/chunk-YYLCPDCC.js +990 -0
- package/dist/chunk-Z4GLJ6J4.js +1035 -0
- package/dist/chunk-ZARZ4POR.js +210 -0
- package/dist/chunk-ZJPORZTC.js +119 -0
- package/dist/chunk-ZNUZ3EXD.js +243 -0
- package/dist/chunk-ZPST2KQO.js +49 -0
- package/dist/chunk-ZR5YA6ZF.js +512 -0
- package/dist/chunk-ZU4JPCPR.js +319 -0
- package/dist/chunk-ZUKUCZYZ.js +39 -0
- package/dist/cli/index.js +56 -45
- package/dist/clio-K2PBVCEM.js +27 -0
- package/dist/clio-context-tools-GR4OHFSO.js +145 -0
- package/dist/code-nav-4X3OI6DK.js +715 -0
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/compile-cache-YRB6QRO7.js +18 -0
- package/dist/components-RQOJ6NUR.js +675 -0
- package/dist/config-Q5J7UBAB.js +291 -0
- package/dist/config-XE7XYZLU.js +38 -0
- package/dist/config-graph-P3555574.js +175 -0
- package/dist/configure-KIJ32ZO6.js +104 -0
- package/dist/context-LFBQOSXT.js +344 -0
- package/dist/context-NNLBES2Y.js +82 -0
- package/dist/context-WBH3KSVR.js +302 -0
- package/dist/context-clear-YOHMQ6SQ.js +208 -0
- package/dist/context-index-X2N5UAIF.js +120 -0
- package/dist/context-map-HOSMGUG2.js +555 -0
- package/dist/context-working-set-MRAEVDNV.js +1564 -0
- package/dist/data-tool-JJPBGMQJ.js +2986 -0
- package/dist/detail-RHJOTGN4.js +139 -0
- package/dist/dispatch-runner-NC7PYNIO.js +2477 -0
- package/dist/docs-K4GBWRUX.js +342 -0
- package/dist/doctor-S5KZ2GTN.js +1138 -0
- package/dist/doctor-deep-2EJQSHIZ.js +130 -0
- package/dist/domain-loader-MB34FDPD.js +13 -0
- package/dist/eval-3D6M36N7.js +5220 -0
- package/dist/eval-inventory-SM3HNM47.js +260 -0
- package/dist/evals-FO6JEUCN.js +104 -0
- package/dist/evidence-6IYN52ET.js +262 -0
- package/dist/evidence-JVECPI3F.js +250 -0
- package/dist/evidence-XNVPPLVK.js +410 -0
- package/dist/evolve-TLPUDJDM.js +634 -0
- package/dist/extensions-ROZJ5X3J.js +311 -0
- package/dist/fleet-6RJWIOEO.js +284 -0
- package/dist/fleet-WKVBF5SZ.js +942 -0
- package/dist/fleet-commands-L6WFT7KZ.js +76 -0
- package/dist/fleet-decisions-BTQMWU5A.js +55 -0
- package/dist/fleet-graph-553DZ67Q.js +141 -0
- package/dist/fleet-inspect-236FZPRB.js +329 -0
- package/dist/fleet-new-CKUXP3CO.js +48 -0
- package/dist/fleet-preflight-NZQHABWO.js +95 -0
- package/dist/fleet-validate-7NLAE26R.js +96 -0
- package/dist/fleet-verify-E3RO6A5W.js +226 -0
- package/dist/fleet-view-KMZQY7YJ.js +144 -0
- package/dist/gui/client/assets/abnfDiagram-VCTEODGH-DPBWQwNn.js +1 -0
- package/dist/gui/client/assets/architecture-7GRP2DOG-D1w8FrFW.js +1 -0
- package/dist/gui/client/assets/architectureDiagram-5GKGNRK7-CVPgKd1-.js +36 -0
- package/dist/gui/client/assets/blockDiagram-I7D4REHJ-BLmnqkK5.js +129 -0
- package/dist/gui/client/assets/c4Diagram-7LVT6UL2-4AU9cfM7.js +38 -0
- package/dist/gui/client/assets/channel-DwFZ0m8S.js +1 -0
- package/dist/gui/client/assets/chunk-4HAMMTFA-EOWkKCav.js +62 -0
- package/dist/gui/client/assets/chunk-75Z2AOVW-CygeoIMl.js +2 -0
- package/dist/gui/client/assets/chunk-DU6HZSFF-s27fViLb.js +125 -0
- package/dist/gui/client/assets/chunk-F27PBJKO-BrHvwLsZ.js +1 -0
- package/dist/gui/client/assets/chunk-GMAD6QVW-DzPh-7jt.js +72 -0
- package/dist/gui/client/assets/chunk-GVQU2GXP-pIaQkIzm.js +1 -0
- package/dist/gui/client/assets/chunk-IMKFNOWR-BGJVE_Br.js +231 -0
- package/dist/gui/client/assets/chunk-L3NEJ4N5-B3YLuhww.js +1 -0
- package/dist/gui/client/assets/chunk-OSK3NFVY-BRj61HHU.js +10 -0
- package/dist/gui/client/assets/chunk-P2QGCYS3-Sno90KxP.js +1 -0
- package/dist/gui/client/assets/chunk-POPQ4Y6H-Cw8NTbxr.js +1 -0
- package/dist/gui/client/assets/chunk-PWAF6VOD-BIhHgz37.js +1 -0
- package/dist/gui/client/assets/chunk-SHT3W25Y-Cqv7npBj.js +168 -0
- package/dist/gui/client/assets/chunk-SVP7TREG-Hxzl6Dee.js +88 -0
- package/dist/gui/client/assets/chunk-TICWLB2K-CIFVyVP2.js +206 -0
- package/dist/gui/client/assets/classDiagram-ZZMXUADV-BJbAiI_w.js +1 -0
- package/dist/gui/client/assets/classDiagram-v2-VYDZK3BY-BJbAiI_w.js +1 -0
- package/dist/gui/client/assets/cynefin-OW5HDTMX-DecLWZ-y.js +1 -0
- package/dist/gui/client/assets/cynefinDiagram-5FMLGOSQ-CK-T2N-D.js +62 -0
- package/dist/gui/client/assets/dagre-GXQ25YYZ-C0dfg-OS.js +4 -0
- package/dist/gui/client/assets/diagram-S7CK7UJ4-EaJukVh-.js +30 -0
- package/dist/gui/client/assets/diagram-UQ7AKVKN-xcOucEtP.js +41 -0
- package/dist/gui/client/assets/diagram-VSXAHHWV-3AUJACMf.js +3 -0
- package/dist/gui/client/assets/diagram-VX7I27RA-TPknLqam.js +24 -0
- package/dist/gui/client/assets/diagram-Z3DM3KII-wa1q3YMF.js +24 -0
- package/dist/gui/client/assets/ebnfDiagram-PWID7BFC-JBCF2-s7.js +1 -0
- package/dist/gui/client/assets/erDiagram-RLTQ6QDP-CmXjXPc4.js +99 -0
- package/dist/gui/client/assets/eventmodeling-NTZA5JFV-D_1aJWYl.js +1 -0
- package/dist/gui/client/assets/flowDiagram-HODETNUW-BQtZlael.js +1 -0
- package/dist/gui/client/assets/framework-Bq-znqwW.js +9 -0
- package/dist/gui/client/assets/ganttDiagram-EL5Y4UJY-IPaL_kaa.js +292 -0
- package/dist/gui/client/assets/gitGraph-4MIJSDKK-Oy0Q3hoc.js +1 -0
- package/dist/gui/client/assets/gitGraphDiagram-WWUBYQGX-jtAuBmbc.js +106 -0
- package/dist/gui/client/assets/index-CuFuBEdL.css +1 -0
- package/dist/gui/client/assets/index-hMbEIsX0.js +87 -0
- package/dist/gui/client/assets/info-A6RAGUB7-UtEs1tv7.js +1 -0
- package/dist/gui/client/assets/infoDiagram-27XIBGKW-hlwR1pBW.js +2 -0
- package/dist/gui/client/assets/ishikawaDiagram-5VMMS53U-C6vMtfsd.js +70 -0
- package/dist/gui/client/assets/journeyDiagram-3NMN7TZE-EAwAl_PS.js +139 -0
- package/dist/gui/client/assets/kanban-definition-UXKFOSKX-CQOhRKHW.js +89 -0
- package/dist/gui/client/assets/line-DbcwGZTW.js +1 -0
- package/dist/gui/client/assets/mermaid-parser.core-C2QfVcHd.js +7 -0
- package/dist/gui/client/assets/mermaid.core-N9qUTre1.js +44 -0
- package/dist/gui/client/assets/mindmap-definition-YA3MSWOX-BR_FL9gK.js +96 -0
- package/dist/gui/client/assets/packet-AYTQ26CC-CbBJ2oAa.js +1 -0
- package/dist/gui/client/assets/pegDiagram-XKGWAZYB-BruU0IHM.js +1 -0
- package/dist/gui/client/assets/pie-WAS4IAKB-BWdSng4H.js +1 -0
- package/dist/gui/client/assets/pieDiagram-E7YTZNPT-CR3ljgdU.js +39 -0
- package/dist/gui/client/assets/quadrantDiagram-AXDQQJYC-C-boKslr.js +7 -0
- package/dist/gui/client/assets/radar-RG4KPBEZ-CGlOnAKG.js +1 -0
- package/dist/gui/client/assets/railroad-74A4TZTK-CDvuAlyC.js +1 -0
- package/dist/gui/client/assets/railroad-abnf-HS5TGJTU-CQUA9crC.js +1 -0
- package/dist/gui/client/assets/railroad-ebnf-LZEXJU2U-CD857gxq.js +1 -0
- package/dist/gui/client/assets/railroad-peg-WCYAUIDC-DUV4k35E.js +1 -0
- package/dist/gui/client/assets/railroadDiagram-O6MQD6OU-DMR4LXmA.js +1 -0
- package/dist/gui/client/assets/requirementDiagram-BXWQKSXE-DVltaF6c.js +84 -0
- package/dist/gui/client/assets/sankeyDiagram-P5KCCOFB-CHdNz472.js +40 -0
- package/dist/gui/client/assets/sequenceDiagram-WJ2MYXX4-DaMafIty.js +162 -0
- package/dist/gui/client/assets/stateDiagram-D77RDMKH-CEbBHqn3.js +1 -0
- package/dist/gui/client/assets/stateDiagram-v2-MP3YSRHH-B3JgDdFg.js +1 -0
- package/dist/gui/client/assets/swimlanes-42K2YHIH-BlPHndnJ.js +1 -0
- package/dist/gui/client/assets/swimlanesDiagram-VR7AAH4N-BPabHwsZ.js +8 -0
- package/dist/gui/client/assets/timeline-definition-24CTP7MA-E4Y1lPBI.js +120 -0
- package/dist/gui/client/assets/treeView-Q6P3EWNA-DrY0uhTN.js +1 -0
- package/dist/gui/client/assets/treemap-WGGIJYW6-CVotnLHf.js +1 -0
- package/dist/gui/client/assets/vennDiagram-4TSXK5OY-DJNrLIOF.js +34 -0
- package/dist/gui/client/assets/wardley-WFR3VGLG-ClOpAp5S.js +1 -0
- package/dist/gui/client/assets/wardleyDiagram-VM6X3IG4-qhMoKOXc.js +78 -0
- package/dist/gui/client/assets/xychartDiagram-S5SC5T6Z-CTuXoo9R.js +7 -0
- package/dist/gui/client/index.html +6 -0
- package/dist/gui/ops-worker.js +167 -0
- package/dist/gui/reads-worker.js +473 -0
- package/dist/gui/server.js +9780 -0
- package/dist/gui-QJS7XQMV.js +16 -0
- package/dist/init-DV5B6HQG.js +354 -0
- package/dist/install-6C45YXFP.js +19 -0
- package/dist/install-O7NSRU2W.js +13 -0
- package/dist/interactive-OXGYZYAS.js +22381 -0
- package/dist/interop-J7UOBOSF.js +254 -0
- package/dist/inventory-OEGIL6PY.js +144 -0
- package/dist/library-B3UPGY4V.js +83 -0
- package/dist/library-MXQA5DLT.js +309 -0
- package/dist/library-ROZAMUIL.js +496 -0
- package/dist/library-import-2LTWOVBD.js +190 -0
- package/dist/library-inventory-LJ3XHLWO.js +60 -0
- package/dist/library-validation-DARHHUNW.js +46 -0
- package/dist/mcp-2GSPYQ54.js +28 -0
- package/dist/memory-J3STUUIK.js +537 -0
- package/dist/models-Y3GMFJ2V.js +314 -0
- package/dist/monitor-LHNH577J.js +811 -0
- package/dist/orchestrator-DTAPXULA.js +12117 -0
- package/dist/panes-A743ONZ5.js +103 -0
- package/dist/panes-CTNO57ZY.js +76 -0
- package/dist/paths-3SBYJQDP.js +68 -0
- package/dist/preload-OJBMAIXF.js +140 -0
- package/dist/providers-CMETPZNS.js +232 -0
- package/dist/registry-37T4U6X2.js +15 -0
- package/dist/reset-O7EUCYWO.js +346 -0
- package/dist/resources-FY6XYFN2.js +189 -0
- package/dist/run-H64MEVTA.js +1067 -0
- package/dist/run-script-PQ5NRU3X.js +42 -0
- package/dist/settings-SNIQNXCW.js +73 -0
- package/dist/settings-controls-X6GMCPVG.js +202 -0
- package/dist/share-CAFMMQLM.js +299 -0
- package/dist/skills-ERKQUC7L.js +114 -0
- package/dist/skills-eval-CQ23MYQK.js +1099 -0
- package/dist/skills-inventory-TVUYJDVN.js +142 -0
- package/dist/slash-commands-ROG3KIFJ.js +107 -0
- package/dist/startup-background-KDWCQMWT.js +247 -0
- package/dist/steer-KKYXJSH4.js +77 -0
- package/dist/support-M3RCUUEL.js +40 -0
- package/dist/system-ZDW44ZGX.js +142 -0
- package/dist/target-runtimes-PWA22WVW.js +73 -0
- package/dist/targets-AJWA6E7Z.js +1207 -0
- package/dist/tasks-6L2JJRC5.js +39 -0
- package/dist/terminal-lease-YFUSDPAD.js +432 -0
- package/dist/tools-5XJUABRD.js +30 -0
- package/dist/trace-LA5MVR4Y.js +527 -0
- package/dist/tree-sitter-TQW3Y52T.js +4673 -0
- package/dist/tui-primitives-IUCVWLH5.js +19 -0
- package/dist/uninstall-SAWGGXAB.js +448 -0
- package/dist/upgrade-HSNSVTZG.js +300 -0
- package/dist/usage-XSILDHKF.js +1083 -0
- package/dist/verifiers-ECA5GIHL.js +633 -0
- package/dist/verify-R6QQCCEB.js +31 -0
- package/dist/version-PLZYB5KW.js +22 -0
- package/dist/web-fetch-PVZNMBOB.js +803 -0
- package/dist/wiki-generate-RP22WP5K.js +647 -0
- package/dist/with-panes-W3PAZPRF.js +629 -0
- package/dist/worker/entry.js +324 -284
- package/dist/workspace-check-3764ZKC2.js +83 -0
- package/docs/README.md +26 -10
- package/docs/architecture/acp.md +179 -12
- package/docs/architecture/artifact-placement.md +1 -1
- package/docs/architecture/capacity-and-scheduling.md +7 -8
- package/docs/architecture/context-engine.md +350 -95
- package/docs/architecture/model-catalog.md +18 -11
- package/docs/architecture/observability.md +1 -1
- package/docs/architecture/pi-boundary.md +59 -6
- package/docs/architecture/prompt-envelope-and-tools.md +147 -39
- package/docs/architecture/provider-adapter-cookbook.md +2 -2
- package/docs/architecture/safety-model.md +18 -4
- package/docs/architecture/trace-store.md +2 -2
- package/docs/architecture/tui-design.md +41 -81
- package/docs/architecture/worker-dispatch-mechanics.md +9 -1
- package/docs/gui/README.md +69 -0
- package/docs/gui/boundary-doctrine.md +104 -0
- package/docs/gui/chat-rendering-spec.md +1440 -0
- package/docs/gui/cli-surface-routing.md +148 -0
- package/docs/gui/delivery-order.md +53 -0
- package/docs/gui/event-bus-coverage.md +103 -0
- package/docs/gui/inspector-presentation.md +71 -0
- package/docs/gui/parity/01-cli-surface.md +74 -0
- package/docs/gui/parity/02-slash-and-surfaces.md +110 -0
- package/docs/gui/parity/03-domains-and-plan.md +69 -0
- package/docs/gui/performance-baseline.md +76 -0
- package/docs/gui/settings-write-surface.md +267 -0
- package/docs/gui/workflow-coverage.md +64 -0
- package/docs/guide/built-in-agents.md +1 -1
- package/docs/guide/commands-and-modes.md +256 -99
- package/docs/guide/configuration-and-targets.md +485 -124
- package/docs/guide/configuration-reference.md +165 -82
- package/docs/guide/doctor.md +127 -0
- package/docs/guide/environment-variables.md +1 -1
- package/docs/guide/exit-codes-and-output.md +3 -1
- package/docs/guide/extensions-and-sharing.md +3 -14
- package/docs/guide/fleet-dispatch.md +231 -45
- package/docs/guide/glossary.md +1 -1
- package/docs/guide/installation-and-lifecycle.md +27 -16
- package/docs/guide/interop.md +2 -2
- package/docs/guide/proactive-memory.md +154 -68
- package/docs/guide/resource-library.md +4 -0
- package/docs/guide/skills-marketplace.md +8 -4
- package/docs/guide/slurm.md +88 -0
- package/docs/guide/tool-usage.md +134 -11
- package/docs/guide/troubleshooting.md +4 -2
- package/docs/process/battletest-2026-09-20-fixes.md +161 -0
- package/docs/process/configure-tree-proposal.md +637 -0
- package/docs/process/documentation-coverage.md +8 -0
- package/docs/process/documentation-guide.md +18 -1
- package/docs/process/eval-runner.md +58 -14
- package/docs/process/performance-methodology.md +11 -3
- package/docs/process/release-cut-checklist.md +29 -3
- package/docs/process/tool-audit-v0.4.9.md +3 -0
- package/examples/extensions/README.md +1 -3
- package/examples/extensions/lab-status/clio-coder-extension.json +1 -1
- package/examples/extensions/measurements/clio-coder-extension.json +1 -1
- package/library/registry.yaml +30 -5
- package/library/skills/git/fix-issue/SKILL.md +2 -1
- package/library/skills/git/fix-issue/plugin.json +1 -1
- package/library/skills/git/ship/SKILL.md +2 -1
- package/library/skills/git/ship/plugin.json +1 -1
- package/library/skills/meta/clio-coder-test/SKILL.md +5 -6
- package/library/skills/meta/clio-coder-test/evals.md +2 -2
- package/library/skills/meta/clio-coder-test/references/test-map.md +2 -2
- package/library/skills/registry.yaml +9 -5
- package/library/skills/research/slurm-jobs/SKILL.md +110 -0
- package/library/skills/research/slurm-jobs/evals.md +39 -0
- package/library/skills/research/slurm-jobs/plugin.json +23 -0
- package/library/skills/skill-marketplace.json +18 -2
- package/package.json +15 -10
- package/src/cli/args.ts +59 -0
- package/src/cli/argv.ts +7 -0
- package/src/cli/clio.ts +9 -1
- package/src/cli/configure-controls.ts +136 -0
- package/src/cli/configure-onboarding.ts +24 -7
- package/src/cli/configure-quick.ts +32 -7
- package/src/cli/configure-target.ts +11 -1
- package/src/cli/configure.ts +253 -256
- package/src/cli/docs-server.ts +290 -0
- package/src/cli/docs.ts +60 -15
- package/src/cli/doctor-deep.ts +126 -0
- package/src/cli/doctor-hpc.ts +122 -0
- package/src/cli/doctor-slurm.ts +130 -0
- package/src/cli/doctor-task-worktrees.ts +88 -0
- package/src/cli/doctor.ts +165 -16
- package/src/cli/fleet-verify.ts +3 -2
- package/src/cli/gui.ts +111 -0
- package/src/cli/index.ts +14 -4
- package/src/cli/library.ts +2 -7
- package/src/cli/modes/json-stream.ts +4 -2
- package/src/cli/modes/print.ts +252 -25
- package/src/cli/run.ts +199 -71
- package/src/cli/skills.ts +25 -7
- package/src/cli/targets.ts +87 -9
- package/src/cli/uninstall.ts +28 -8
- package/src/cli/usage.ts +2 -1
- package/src/cli/workspace-check.ts +71 -0
- package/src/core/bash-exec.ts +8 -2
- package/src/core/bus-events.ts +32 -3
- package/src/core/cache-telemetry.ts +3 -2
- package/src/core/compile-cache.ts +11 -1
- package/src/core/config.ts +59 -6
- package/src/core/defaults.ts +27 -5
- package/src/core/diagnostics.ts +25 -0
- package/src/core/event-bus.ts +2 -1
- package/src/core/gateway-routing.ts +10 -3
- package/src/core/git-commit-attribution.ts +2 -1
- package/src/core/naming-compat.ts +6 -4
- package/src/core/package-root.ts +0 -9
- package/src/core/path-canonical.ts +143 -24
- package/src/core/residency-target-key.ts +2 -6
- package/src/core/run-overrides.ts +1 -1
- package/src/core/settings-awareness.ts +108 -0
- package/src/core/settings-controls.ts +673 -0
- package/src/core/settings-navigation.ts +116 -0
- package/src/core/theme-token-hex.ts +19 -9
- package/src/core/turn-constraints.ts +52 -0
- package/src/domains/agents/builtins/scout.md +7 -17
- package/src/domains/agents/catalog.ts +5 -1
- package/src/domains/agents/extension.ts +2 -1
- package/src/domains/agents/registry.ts +4 -3
- package/src/domains/agents/result-contract.ts +132 -1
- package/src/domains/config/classify.ts +11 -7
- package/src/domains/config/extension.ts +5 -1
- package/src/domains/config/keybindings.ts +2 -2
- package/src/domains/context/codewiki/coordinator.ts +10 -0
- package/src/domains/context/worker/snapshot.ts +6 -3
- package/src/domains/dispatch/active-route-planner.ts +2 -0
- package/src/domains/dispatch/agent-candidates.ts +5 -1
- package/src/domains/dispatch/capability-match.ts +1 -1
- package/src/domains/dispatch/contract.ts +28 -0
- package/src/domains/dispatch/event-pump.ts +37 -3
- package/src/domains/dispatch/extension.ts +391 -48
- package/src/domains/dispatch/failure-classification.ts +37 -2
- package/src/domains/dispatch/receipt-integrity.ts +3 -0
- package/src/domains/dispatch/run-identity.ts +10 -3
- package/src/domains/dispatch/target-breaker.ts +163 -0
- package/src/domains/dispatch/types.ts +29 -2
- package/src/domains/dispatch/worker-model-loads.ts +66 -0
- package/src/domains/dispatch/worker-protocol.ts +20 -1
- package/src/domains/dispatch/worker-spawn.ts +28 -2
- package/src/domains/eval/artifacts/redact.ts +9 -0
- package/src/domains/eval/metrics/noop.ts +61 -0
- package/src/domains/eval/reports/junit.ts +5 -1
- package/src/domains/eval/reports/text.ts +7 -0
- package/src/domains/eval/runners/clio-run.ts +19 -8
- package/src/domains/eval/runners/external-command.ts +29 -4
- package/src/domains/eval/schema/adapter.ts +6 -2
- package/src/domains/eval/schema/suite.ts +2 -0
- package/src/domains/eval/schema/validate.ts +3 -0
- package/src/domains/eval/suites/run.ts +167 -26
- package/src/domains/evidence/detail.ts +3 -2
- package/src/domains/extensions/operator-runtime.ts +11 -4
- package/src/domains/gateway/mcp/client.ts +1 -1
- package/src/domains/lifecycle/doctor.ts +42 -26
- package/src/domains/lifecycle/migrations/2026-09-18-ollama-runtime-id.ts +38 -0
- package/src/domains/lifecycle/migrations/index.ts +2 -0
- package/src/domains/middleware/demo-guidance.ts +36 -0
- package/src/domains/middleware/dispatch-nudge.ts +1 -1
- package/src/domains/middleware/memory-intervention.ts +51 -4
- package/src/domains/middleware/runtime.ts +5 -4
- package/src/domains/middleware/skills-reminder.ts +29 -52
- package/src/domains/middleware/task-nudge.ts +1 -0
- package/src/domains/middleware/watchdog.ts +4 -0
- package/src/domains/observability/extension.ts +2 -1
- package/src/domains/observability/out-of-turn-usage.ts +3 -2
- package/src/domains/observability/projection.ts +3 -2
- package/src/domains/observability/worker-progress.ts +48 -0
- package/src/domains/prompts/compiler.ts +148 -52
- package/src/domains/prompts/contract.ts +3 -3
- package/src/domains/prompts/demo-guidance.ts +8 -0
- package/src/domains/prompts/extension.ts +12 -7
- package/src/domains/prompts/fragments/identity/clio.md +2 -1
- package/src/domains/prompts/fragments/identity/docs-routing.md +2 -0
- package/src/domains/prompts/fragments/identity/self-awareness.md +4 -0
- package/src/domains/prompts/fragments/operating/contract.md +16 -7
- package/src/domains/prompts/fragments/operating/delegation.md +29 -13
- package/src/domains/prompts/fragments/operating/skills.md +18 -10
- package/src/domains/prompts/fragments/operating/worker.md +2 -0
- package/src/domains/providers/cache-deployment.ts +45 -2
- package/src/domains/providers/contract.ts +52 -11
- package/src/domains/providers/extension.ts +230 -53
- package/src/domains/providers/model-discovery.ts +22 -8
- package/src/domains/providers/models/local-models/clio-coder-local-coding-targets.yaml +5 -7
- package/src/domains/providers/plugins.ts +3 -4
- package/src/domains/providers/probe/fingerprint.ts +2 -2
- package/src/domains/providers/probe/reasoning.ts +9 -16
- package/src/domains/providers/probe/tool-call.ts +236 -0
- package/src/domains/providers/registry.ts +51 -8
- package/src/domains/providers/runtime-resolution.ts +60 -12
- package/src/domains/providers/runtimes/boot-manifest.ts +1 -1
- package/src/domains/providers/runtimes/builtins.ts +2 -2
- package/src/domains/providers/runtimes/common/lmstudio-http.ts +57 -41
- package/src/domains/providers/runtimes/common/local-synth.ts +3 -1
- package/src/domains/providers/runtimes/common/ollama-model-ids.ts +11 -0
- package/src/domains/providers/runtimes/common/probe-helpers.ts +53 -0
- package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +3 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +7 -13
- package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +7 -17
- package/src/domains/providers/runtimes/local-native/llamacpp.ts +1 -0
- package/src/domains/providers/runtimes/local-native/ollama.ts +211 -0
- package/src/domains/providers/runtimes/protocol/litellm.ts +77 -65
- package/src/domains/providers/support.ts +1 -1
- package/src/domains/providers/types/local-model-quirks.ts +2 -3
- package/src/domains/providers/types/runtime-descriptor.ts +23 -3
- package/src/domains/providers/types/target-descriptor.ts +15 -3
- package/src/domains/resources/collision.ts +7 -2
- package/src/domains/resources/index.ts +1 -0
- package/src/domains/resources/library-actions.ts +4 -2
- package/src/domains/resources/library-inventory.ts +6 -2
- package/src/domains/resources/prompts/loader.ts +20 -7
- package/src/domains/resources/skills/availability.ts +56 -0
- package/src/domains/resources/skills/loader.ts +20 -9
- package/src/domains/safety/action-classifier.ts +282 -31
- package/src/domains/safety/audit.ts +2 -1
- package/src/domains/safety/autonomy.ts +54 -11
- package/src/domains/safety/decision-presentation.ts +17 -4
- package/src/domains/safety/finish-contract-registration.ts +25 -1
- package/src/domains/safety/finish-contract.ts +20 -5
- package/src/domains/safety/path-policy.ts +6 -2
- package/src/domains/safety/policy-engine.ts +168 -48
- package/src/domains/safety/protected-artifacts.ts +527 -16
- package/src/domains/safety/read-scope.ts +120 -0
- package/src/domains/safety/skill-authority.ts +52 -16
- package/src/domains/scheduling/cluster.ts +14 -3
- package/src/domains/scheduling/contract.ts +7 -0
- package/src/domains/scheduling/extension.ts +20 -9
- package/src/domains/scheduling/local-capacity.ts +160 -0
- package/src/domains/session/context-accounting.ts +44 -31
- package/src/domains/session/context-ledger.ts +10 -3
- package/src/domains/session/entries.ts +4 -0
- package/src/domains/session/history.ts +1 -1
- package/src/domains/session/prompt-cache.ts +1 -0
- package/src/domains/session/prompt-manifest.ts +3 -1
- package/src/domains/session/retry.ts +4 -6
- package/src/domains/session/task-board.ts +19 -4
- package/src/domains/session/tree/preview.ts +3 -2
- package/src/engine/acp/commands.ts +653 -0
- package/src/engine/acp/server.ts +639 -20
- package/src/engine/acp/tool-mediator.ts +30 -7
- package/src/engine/acp/types.ts +58 -0
- package/src/engine/agent.ts +55 -10
- package/src/engine/ai.ts +11 -2
- package/src/engine/api-registry.ts +5 -4
- package/src/engine/apis/degraded-inference.ts +243 -0
- package/src/engine/apis/llamacpp-residency.ts +50 -6
- package/src/engine/apis/lmstudio-residency.ts +3 -5
- package/src/engine/apis/lmstudio.ts +137 -31
- package/src/engine/apis/ollama-http.ts +118 -0
- package/src/engine/apis/ollama-native.ts +216 -97
- package/src/engine/apis/openai-completions.ts +191 -189
- package/src/engine/apis/output-budget.ts +3 -1
- package/src/engine/apis/residency-lock.ts +2 -1
- package/src/engine/apis/residency.ts +339 -89
- package/src/engine/apis/sampling-overrides.ts +23 -2
- package/src/engine/apis/types.ts +1 -2
- package/src/engine/claude/tool-safety.ts +30 -5
- package/src/engine/context.ts +13 -0
- package/src/engine/env-api-keys.ts +2 -1
- package/src/engine/gemma-channel-filter.ts +3 -2
- package/src/engine/instrumented-tui.ts +25 -0
- package/src/engine/loop-guard.ts +172 -6
- package/src/engine/pi-mono-names.ts +16 -0
- package/src/engine/prewarm.ts +44 -15
- package/src/engine/provider-payload.ts +52 -14
- package/src/engine/session.ts +4 -2
- package/src/engine/worker-events.ts +9 -1
- package/src/engine/worker-runtime.ts +269 -42
- package/src/engine/worker-tools.ts +8 -3
- package/src/entry/boot-options.ts +23 -0
- package/src/entry/orchestrator.ts +207 -36
- package/src/interactive/assistant-generation-timing.ts +52 -0
- package/src/interactive/chat-loop-messages.ts +4 -1
- package/src/interactive/chat-loop.ts +59 -9
- package/src/interactive/chat-panel.ts +10 -1
- package/src/interactive/clio-editor.ts +51 -79
- package/src/interactive/context-meter.ts +1 -0
- package/src/interactive/context-overlay.ts +21 -8
- package/src/interactive/cost-overlay.ts +1 -1
- package/src/interactive/dispatch-board.ts +39 -8
- package/src/interactive/editor-bash.ts +0 -34
- package/src/interactive/editor-rails.ts +43 -0
- package/src/interactive/editor-submit.ts +34 -2
- package/src/interactive/footer/dashboard.ts +67 -38
- package/src/interactive/footer/demo-hints.ts +40 -0
- package/src/interactive/footer/key-hints.ts +21 -0
- package/src/interactive/footer/notifications.ts +1 -1
- package/src/interactive/footer/pages.ts +486 -0
- package/src/interactive/footer/system-metrics.ts +147 -0
- package/src/interactive/footer/widgets.ts +158 -63
- package/src/interactive/footer-panel.ts +29 -7
- package/src/interactive/interactive-application.ts +109 -21
- package/src/interactive/interactive-event-projection.ts +6 -2
- package/src/interactive/interactive-presentation.ts +25 -29
- package/src/interactive/interactive-slash-runtime.ts +32 -18
- package/src/interactive/interactive-subscriptions.ts +25 -5
- package/src/interactive/interactive-tickers.ts +21 -5
- package/src/interactive/keybinding-manager.ts +9 -0
- package/src/interactive/overlay-frame.ts +29 -7
- package/src/interactive/overlay-lifecycle.ts +5 -3
- package/src/interactive/overlay-model-selectors.ts +4 -0
- package/src/interactive/overlay-permission-lifecycle.ts +6 -5
- package/src/interactive/overlays/ask-user.ts +103 -110
- package/src/interactive/overlays/help-reference.ts +1 -0
- package/src/interactive/overlays/message-picker.ts +3 -29
- package/src/interactive/overlays/session-selector.ts +3 -2
- package/src/interactive/overlays/settings-sections.ts +2 -30
- package/src/interactive/overlays/settings.ts +310 -607
- package/src/interactive/permission-hint.ts +3 -3
- package/src/interactive/permission-overlay.ts +45 -27
- package/src/interactive/renderers/doctor-report.ts +43 -0
- package/src/interactive/renderers/tool-execution.ts +87 -62
- package/src/interactive/renderers/worker-entry.ts +76 -3
- package/src/interactive/side-question.ts +3 -1
- package/src/interactive/slash-commands.ts +78 -23
- package/src/interactive/startup-background.ts +131 -0
- package/src/interactive/startup-diagnostics.ts +16 -0
- package/src/interactive/status/state-machine.ts +16 -3
- package/src/interactive/status/types.ts +2 -0
- package/src/interactive/status/verbs.ts +1 -1
- package/src/interactive/terminal-lease.ts +26 -22
- package/src/interactive/theme/components.ts +1 -1
- package/src/interactive/theme/rules.ts +1 -1
- package/src/interactive/theme/tokens.ts +16 -10
- package/src/interactive/transcript-detail.ts +4 -0
- package/src/interactive/turn-context.ts +56 -20
- package/src/interactive/turn-middleware.ts +57 -9
- package/src/interactive/turn-prewarm.ts +121 -18
- package/src/interactive/turn-recovery.ts +10 -8
- package/src/interactive/turn-runtime.ts +74 -29
- package/src/interactive/turn-state.ts +3 -0
- package/src/interactive/welcome-art.ts +36 -0
- package/src/interactive/welcome-dashboard.ts +209 -49
- package/src/interactive/worker-replay.ts +4 -0
- package/src/interactive/worker-stream.ts +17 -2
- package/src/tools/agent-tools.ts +22 -1
- package/src/tools/builtin-tool-catalog.ts +2 -2
- package/src/tools/codewiki/code-nav.ts +1 -1
- package/src/tools/codewiki/shared.ts +39 -1
- package/src/tools/compete-worktrees.ts +8 -91
- package/src/tools/context/docs-engine.ts +44 -12
- package/src/tools/context/index.ts +126 -22
- package/src/tools/context/library.ts +1 -1
- package/src/tools/context/surface.ts +12 -7
- package/src/tools/core-bootstrap.ts +3 -0
- package/src/tools/dispatch-runner.ts +18 -0
- package/src/tools/dispatch-schema.ts +187 -206
- package/src/tools/edit.ts +46 -42
- package/src/tools/file-mutation-queue.ts +25 -26
- package/src/tools/gateway/clio-context-surface.ts +1 -1
- package/src/tools/gateway/index.ts +7 -3
- package/src/tools/gateway/mcp-capabilities.ts +5 -3
- package/src/tools/grep.ts +14 -2
- package/src/tools/ls.ts +1 -4
- package/src/tools/monitor.ts +14 -1
- package/src/tools/observation.ts +1 -3
- package/src/tools/path-utils.ts +51 -18
- package/src/tools/process-lease.ts +99 -0
- package/src/tools/read.ts +1 -1
- package/src/tools/registry.ts +71 -9
- package/src/tools/task-worktree.ts +296 -17
- package/src/tools/tasks.ts +29 -7
- package/src/tools/worker-evidence.ts +60 -1
- package/src/tools/worktree-root.ts +252 -0
- package/src/tools/write.ts +34 -30
- package/src/worker/entry.ts +18 -1
- package/src/worker/protocol.ts +54 -1
- package/src/worker/spec-contract.ts +22 -2
- package/dist/acp-ZWZCPS2A.js +0 -130
- package/dist/agents-LYSOWZUE.js +0 -150
- package/dist/auth-WBQGKWQ5.js +0 -551
- package/dist/background-BUMOBAQN.js +0 -37
- package/dist/builtins-ODRJOEDI.js +0 -22
- package/dist/chunk-2DRRZ57Q.js +0 -571
- package/dist/chunk-2JBZYUPY.js +0 -105
- package/dist/chunk-2VBWFXYE.js +0 -198
- package/dist/chunk-2X2R3UTH.js +0 -1018
- package/dist/chunk-32KJPXAM.js +0 -75
- package/dist/chunk-37LW32LZ.js +0 -32
- package/dist/chunk-3A3CUMKA.js +0 -54
- package/dist/chunk-3AND2EFU.js +0 -215
- package/dist/chunk-3RDHGNU7.js +0 -647
- package/dist/chunk-3SUDBGEI.js +0 -1323
- package/dist/chunk-3TQN67I2.js +0 -396
- package/dist/chunk-3U4BL2EV.js +0 -103
- package/dist/chunk-3UQADNG2.js +0 -302
- package/dist/chunk-434LRQ6Z.js +0 -13441
- package/dist/chunk-4ATBOFRP.js +0 -7802
- package/dist/chunk-4DNL43EZ.js +0 -139
- package/dist/chunk-4HFJKRGZ.js +0 -5250
- package/dist/chunk-555E2WVV.js +0 -322
- package/dist/chunk-5GZICIYO.js +0 -462
- package/dist/chunk-5PHVPU4G.js +0 -160
- package/dist/chunk-5RTEFNBP.js +0 -197
- package/dist/chunk-64XJW7NT.js +0 -380
- package/dist/chunk-6TKOFFGE.js +0 -1058
- package/dist/chunk-6UJ7RDI7.js +0 -1143
- package/dist/chunk-7FBCRWVT.js +0 -1801
- package/dist/chunk-7JSOSVLP.js +0 -283
- package/dist/chunk-7N3OLLSL.js +0 -637
- package/dist/chunk-7QQRPB2E.js +0 -915
- package/dist/chunk-A6DULIMH.js +0 -986
- package/dist/chunk-AB3QXXBC.js +0 -123
- package/dist/chunk-ACCECAH4.js +0 -49
- package/dist/chunk-AM6CEP2K.js +0 -2120
- package/dist/chunk-AUHVBIDW.js +0 -19
- package/dist/chunk-AY4XE6GN.js +0 -665
- package/dist/chunk-BBA2TAWV.js +0 -667
- package/dist/chunk-BFPHMSK7.js +0 -169
- package/dist/chunk-BMNVVKEW.js +0 -604
- package/dist/chunk-CKK25WN3.js +0 -72
- package/dist/chunk-CTGZCEI6.js +0 -33
- package/dist/chunk-DIAJYJYG.js +0 -446
- package/dist/chunk-DVNAPPXU.js +0 -27
- package/dist/chunk-DXVMLM3Z.js +0 -92
- package/dist/chunk-E3LEPLQ2.js +0 -68
- package/dist/chunk-EHZRAB4U.js +0 -63
- package/dist/chunk-EM2DIQAJ.js +0 -179
- package/dist/chunk-EM2WNQS2.js +0 -93
- package/dist/chunk-ETF4ROKH.js +0 -595
- package/dist/chunk-F2YM3JY4.js +0 -22513
- package/dist/chunk-FEBQWHJT.js +0 -1023
- package/dist/chunk-FPS4R2RN.js +0 -1975
- package/dist/chunk-GD55RTIW.js +0 -33
- package/dist/chunk-GJDRMBDI.js +0 -573
- package/dist/chunk-GREYLTZ5.js +0 -658
- package/dist/chunk-H2WSCZZT.js +0 -238
- package/dist/chunk-H6IUCY6Q.js +0 -117
- package/dist/chunk-H72DCH3E.js +0 -563
- package/dist/chunk-H7TXJR23.js +0 -705
- package/dist/chunk-HBJQQTKM.js +0 -40
- package/dist/chunk-HCVKSGNZ.js +0 -990
- package/dist/chunk-HVCAY2FH.js +0 -67
- package/dist/chunk-I47EJPYL.js +0 -1377
- package/dist/chunk-IALGWTLO.js +0 -36
- package/dist/chunk-IALZJE37.js +0 -1710
- package/dist/chunk-IC7JXLOM.js +0 -1577
- package/dist/chunk-IOSVTTCD.js +0 -108
- package/dist/chunk-IPTPMLA6.js +0 -475
- package/dist/chunk-IZZI3SKQ.js +0 -128
- package/dist/chunk-JABWPKFY.js +0 -27
- package/dist/chunk-JIYWMQZ5.js +0 -175
- package/dist/chunk-JKU34ACI.js +0 -693
- package/dist/chunk-JOBN2YQQ.js +0 -176
- package/dist/chunk-JOL2QRLE.js +0 -152
- package/dist/chunk-JTR3VSV3.js +0 -44
- package/dist/chunk-JU6MAMRJ.js +0 -362
- package/dist/chunk-JURMQWVJ.js +0 -15
- package/dist/chunk-K2BLQC5E.js +0 -262
- package/dist/chunk-K3XN4LZ4.js +0 -402
- package/dist/chunk-KIVFXGBM.js +0 -688
- package/dist/chunk-KKRXONXG.js +0 -43
- package/dist/chunk-KOAHT3N2.js +0 -395
- package/dist/chunk-KP5BPXR3.js +0 -73
- package/dist/chunk-KQ6IDQIB.js +0 -39
- package/dist/chunk-KRJQG47M.js +0 -1289
- package/dist/chunk-KW6OJ7IY.js +0 -221
- package/dist/chunk-L3NBWLSN.js +0 -314
- package/dist/chunk-L4RUTLRL.js +0 -59
- package/dist/chunk-LGTVWPRN.js +0 -47
- package/dist/chunk-LSQW6KZO.js +0 -200
- package/dist/chunk-LTR5QCLS.js +0 -446
- package/dist/chunk-LWJYOHT3.js +0 -165
- package/dist/chunk-M6YLF66Z.js +0 -2687
- package/dist/chunk-MJ4QEJVT.js +0 -185
- package/dist/chunk-MO65K5CH.js +0 -1148
- package/dist/chunk-MTOZEQHL.js +0 -637
- package/dist/chunk-MZDPF35R.js +0 -45
- package/dist/chunk-MZKYVIWW.js +0 -188
- package/dist/chunk-NBPA5QM6.js +0 -386
- package/dist/chunk-NMBEW4PP.js +0 -688
- package/dist/chunk-NPLLK3HO.js +0 -3610
- package/dist/chunk-NSYGPIVW.js +0 -39
- package/dist/chunk-NXTV5OI5.js +0 -1020
- package/dist/chunk-NYDG4QP4.js +0 -689
- package/dist/chunk-OA2GDACN.js +0 -146
- package/dist/chunk-PJI57EC6.js +0 -236
- package/dist/chunk-PJJBUGMR.js +0 -4484
- package/dist/chunk-PNJE7JPS.js +0 -50
- package/dist/chunk-PP23PZEV.js +0 -1347
- package/dist/chunk-PPIFDSBK.js +0 -409
- package/dist/chunk-PTFISXQO.js +0 -319
- package/dist/chunk-QB33Z5S7.js +0 -1865
- package/dist/chunk-QGNII5VC.js +0 -110
- package/dist/chunk-QH3ZTUOA.js +0 -113
- package/dist/chunk-QI72X67S.js +0 -481
- package/dist/chunk-QJMRYTCX.js +0 -56
- package/dist/chunk-QKHZZFKH.js +0 -98
- package/dist/chunk-RDKX2PME.js +0 -66
- package/dist/chunk-RL7LJYN4.js +0 -66
- package/dist/chunk-RPMNIFRY.js +0 -65
- package/dist/chunk-RQROVOV3.js +0 -670
- package/dist/chunk-S34A7BSP.js +0 -97
- package/dist/chunk-SJETVJHX.js +0 -119
- package/dist/chunk-SJMNU4ZS.js +0 -634
- package/dist/chunk-SL2RDHKO.js +0 -19
- package/dist/chunk-SLLG2G44.js +0 -85
- package/dist/chunk-SQAZN2V3.js +0 -2744
- package/dist/chunk-SQBT46OF.js +0 -814
- package/dist/chunk-SRCEQKQS.js +0 -165
- package/dist/chunk-TCUOGIL2.js +0 -52
- package/dist/chunk-TIWPKCVO.js +0 -42
- package/dist/chunk-U44YYLVV.js +0 -1529
- package/dist/chunk-UIU7MGZ3.js +0 -486
- package/dist/chunk-USBBEKHW.js +0 -177
- package/dist/chunk-VHKTOLEW.js +0 -1416
- package/dist/chunk-VPWGADBG.js +0 -3151
- package/dist/chunk-VQD7NH7Z.js +0 -356
- package/dist/chunk-VWFCAK34.js +0 -697
- package/dist/chunk-W5RM7YAH.js +0 -78
- package/dist/chunk-W72LLZAV.js +0 -243
- package/dist/chunk-WGRDABW7.js +0 -207
- package/dist/chunk-WWRV5RJI.js +0 -165
- package/dist/chunk-XCUDZQLX.js +0 -195
- package/dist/chunk-XFLSL7BT.js +0 -386
- package/dist/chunk-XJZEDCNT.js +0 -391
- package/dist/chunk-XLLEL3K7.js +0 -709
- package/dist/chunk-XNZWWDC3.js +0 -686
- package/dist/chunk-XRECG65F.js +0 -308
- package/dist/chunk-XTQ7MEJG.js +0 -457
- package/dist/chunk-XW2UZICE.js +0 -372
- package/dist/chunk-Y3M2T5RO.js +0 -891
- package/dist/chunk-YGDMBK7O.js +0 -567
- package/dist/chunk-YJCCMYYT.js +0 -125
- package/dist/chunk-YPQOPFP7.js +0 -797
- package/dist/chunk-YQYINEJI.js +0 -132
- package/dist/chunk-ZF6CV4WZ.js +0 -5614
- package/dist/chunk-ZKJU55MX.js +0 -326
- package/dist/chunk-ZKQ63XZ5.js +0 -1196
- package/dist/chunk-ZKVMDNAN.js +0 -962
- package/dist/chunk-ZST3FRCV.js +0 -132
- package/dist/clio-JUDCLNHC.js +0 -26
- package/dist/clio-context-tools-FT672MNZ.js +0 -136
- package/dist/code-nav-7OVCTP2Z.js +0 -688
- package/dist/compile-cache-WJEPUZEJ.js +0 -16
- package/dist/components-OZXZMDWU.js +0 -674
- package/dist/config-P3ZQVF63.js +0 -275
- package/dist/config-graph-GKAOBAS5.js +0 -160
- package/dist/configure-25LRYZJG.js +0 -86
- package/dist/context-72VQV7JX.js +0 -73
- package/dist/context-COWRWXP3.js +0 -286
- package/dist/context-WB3D474G.js +0 -344
- package/dist/context-clear-Y4LYMWC6.js +0 -192
- package/dist/context-index-W6A4V7OC.js +0 -120
- package/dist/context-map-DKL3QT6D.js +0 -555
- package/dist/context-working-set-TMZYVC7A.js +0 -1560
- package/dist/data-tool-KMBWKKWR.js +0 -2987
- package/dist/detail-4GLROGY5.js +0 -123
- package/dist/dispatch-runner-TR3LBP5P.js +0 -2446
- package/dist/docs-ZK777W2G.js +0 -101
- package/dist/doctor-WC4OV2OB.js +0 -814
- package/dist/eval-FUNWJNUJ.js +0 -5050
- package/dist/eval-inventory-CX24K2JT.js +0 -260
- package/dist/evals-L73EBHSU.js +0 -104
- package/dist/evidence-AVJ2RTHX.js +0 -392
- package/dist/evidence-P4NZOAK7.js +0 -233
- package/dist/evidence-Y55WKYHH.js +0 -246
- package/dist/evolve-MAWKRYWF.js +0 -617
- package/dist/extensions-PYQH3H47.js +0 -308
- package/dist/fleet-M4H2QBWT.js +0 -268
- package/dist/fleet-ULBXXRDR.js +0 -923
- package/dist/fleet-commands-7NNC4JZU.js +0 -76
- package/dist/fleet-decisions-EHEY7KPU.js +0 -52
- package/dist/fleet-graph-LQ2AHZT3.js +0 -136
- package/dist/fleet-inspect-5IGBIIYZ.js +0 -312
- package/dist/fleet-new-RCKGSRDV.js +0 -48
- package/dist/fleet-preflight-UTTNOKMG.js +0 -27
- package/dist/fleet-validate-YGILJKA6.js +0 -91
- package/dist/fleet-verify-AUWTCDKR.js +0 -210
- package/dist/fleet-view-TGUFCY5A.js +0 -127
- package/dist/init-GFN2Z7NF.js +0 -333
- package/dist/install-3RZDDP23.js +0 -13
- package/dist/install-6PIF6F6W.js +0 -19
- package/dist/interop-EF5JUWWK.js +0 -246
- package/dist/inventory-I6VJDDYF.js +0 -127
- package/dist/library-3J5IO2OJ.js +0 -75
- package/dist/library-IJTOFTDS.js +0 -307
- package/dist/library-Z3KZSHPE.js +0 -492
- package/dist/library-import-IMKFLDHX.js +0 -183
- package/dist/library-inventory-CQP5D6GU.js +0 -56
- package/dist/library-validation-33UTX7AI.js +0 -42
- package/dist/mcp-5FWZKUS6.js +0 -26
- package/dist/memory-36CWN67Y.js +0 -520
- package/dist/models-KQ6PQJJQ.js +0 -302
- package/dist/monitor-XX6ZD6KC.js +0 -785
- package/dist/orchestrator-SK6HDAOP.js +0 -42067
- package/dist/panes-L27QRXUH.js +0 -74
- package/dist/panes-LXZIWNOZ.js +0 -103
- package/dist/paths-7DJDU4P4.js +0 -67
- package/dist/preload-K2TDYI4Y.js +0 -124
- package/dist/registry-DESGAXWK.js +0 -11
- package/dist/reset-73VU3XNT.js +0 -345
- package/dist/resources-3BYJYVYL.js +0 -181
- package/dist/run-AR7PY6GW.js +0 -896
- package/dist/run-script-H2KUDIII.js +0 -41
- package/dist/settings-3VMBMWID.js +0 -72
- package/dist/share-HVWH4FPF.js +0 -293
- package/dist/skills-5TYEY75Q.js +0 -100
- package/dist/skills-eval-CK3MD63Z.js +0 -1083
- package/dist/skills-inventory-KSFYG3I6.js +0 -138
- package/dist/slash-commands-DMVHML7P.js +0 -98
- package/dist/steer-XCD6V3EW.js +0 -77
- package/dist/support-JAZL6VBR.js +0 -38
- package/dist/system-3TDQRMZZ.js +0 -116
- package/dist/targets-EZKHAYRN.js +0 -1117
- package/dist/tasks-SNEFHWYW.js +0 -36
- package/dist/terminal-lease-BNLFA4OF.js +0 -419
- package/dist/tools-7IM6BDCE.js +0 -28
- package/dist/trace-C3MBGD4S.js +0 -524
- package/dist/tree-sitter-AQUUOSYM.js +0 -4673
- package/dist/tui-primitives-36BY2774.js +0 -18
- package/dist/uninstall-WWLOSZ42.js +0 -432
- package/dist/upgrade-ETK2JNSJ.js +0 -297
- package/dist/usage-S2HOB7BZ.js +0 -1065
- package/dist/verifiers-QDTEVH7R.js +0 -632
- package/dist/verify-SUKUK6XM.js +0 -31
- package/dist/version-RFPKO6T7.js +0 -21
- package/dist/web/client/assets/abnfDiagram-VCTEODGH-BznNOe_I.js +0 -1
- package/dist/web/client/assets/architecture-7GRP2DOG-BQO9DL_y.js +0 -1
- package/dist/web/client/assets/architectureDiagram-5GKGNRK7-CKPbdvyx.js +0 -36
- package/dist/web/client/assets/blockDiagram-I7D4REHJ-prINbpbM.js +0 -129
- package/dist/web/client/assets/c4Diagram-7LVT6UL2-DkVRKtyU.js +0 -38
- package/dist/web/client/assets/channel-c4EeUUqA.js +0 -1
- package/dist/web/client/assets/chunk-4HAMMTFA-CV0Z1ApC.js +0 -62
- package/dist/web/client/assets/chunk-75Z2AOVW-D34Nohxq.js +0 -2
- package/dist/web/client/assets/chunk-DU6HZSFF-Das_cS-O.js +0 -125
- package/dist/web/client/assets/chunk-F27PBJKO-CmRojxkG.js +0 -1
- package/dist/web/client/assets/chunk-GMAD6QVW-ClYZmc73.js +0 -72
- package/dist/web/client/assets/chunk-GVQU2GXP-B2BiHFt-.js +0 -1
- package/dist/web/client/assets/chunk-IMKFNOWR-BGApZ8nP.js +0 -231
- package/dist/web/client/assets/chunk-L3NEJ4N5-DIzCgpkY.js +0 -1
- package/dist/web/client/assets/chunk-OSK3NFVY-BPT-yePv.js +0 -10
- package/dist/web/client/assets/chunk-P2QGCYS3-BC1WTc2t.js +0 -1
- package/dist/web/client/assets/chunk-POPQ4Y6H-BSjzBmZU.js +0 -1
- package/dist/web/client/assets/chunk-PWAF6VOD-BIjLs86Z.js +0 -1
- package/dist/web/client/assets/chunk-SHT3W25Y-CduDz2cm.js +0 -168
- package/dist/web/client/assets/chunk-SVP7TREG-XUn4pPAP.js +0 -88
- package/dist/web/client/assets/chunk-TICWLB2K-DcBy2DKy.js +0 -206
- package/dist/web/client/assets/classDiagram-ZZMXUADV-C0HR7YKN.js +0 -1
- package/dist/web/client/assets/classDiagram-v2-VYDZK3BY-C0HR7YKN.js +0 -1
- package/dist/web/client/assets/cynefin-OW5HDTMX-BecPbyM6.js +0 -1
- package/dist/web/client/assets/cynefinDiagram-5FMLGOSQ-BzMzFwyT.js +0 -62
- package/dist/web/client/assets/dagre-GXQ25YYZ-CAWJmRMG.js +0 -4
- package/dist/web/client/assets/diagram-S7CK7UJ4--lp0ALmn.js +0 -30
- package/dist/web/client/assets/diagram-UQ7AKVKN-gKUnsjcj.js +0 -41
- package/dist/web/client/assets/diagram-VSXAHHWV-BoN5D1Gh.js +0 -3
- package/dist/web/client/assets/diagram-VX7I27RA-BMtuswQ6.js +0 -24
- package/dist/web/client/assets/diagram-Z3DM3KII-DtU8LXrA.js +0 -24
- package/dist/web/client/assets/ebnfDiagram-PWID7BFC-DDcnv9BH.js +0 -1
- package/dist/web/client/assets/erDiagram-RLTQ6QDP-DvRb4iDL.js +0 -99
- package/dist/web/client/assets/eventmodeling-NTZA5JFV-01h4SbTc.js +0 -1
- package/dist/web/client/assets/flowDiagram-HODETNUW-Bll_YZ6T.js +0 -1
- package/dist/web/client/assets/framework-YGoR34M4.js +0 -9
- package/dist/web/client/assets/ganttDiagram-EL5Y4UJY-DdFZ9hvH.js +0 -292
- package/dist/web/client/assets/gitGraph-4MIJSDKK-DjD_AAMj.js +0 -1
- package/dist/web/client/assets/gitGraphDiagram-WWUBYQGX-CDyN9n2u.js +0 -106
- package/dist/web/client/assets/index-CVcy0FAn.js +0 -75
- package/dist/web/client/assets/index-Dsi52apB.css +0 -1
- package/dist/web/client/assets/info-A6RAGUB7-C_XK8Hq0.js +0 -1
- package/dist/web/client/assets/infoDiagram-27XIBGKW-DIe4tJ1i.js +0 -2
- package/dist/web/client/assets/ishikawaDiagram-5VMMS53U-CVl5unKI.js +0 -70
- package/dist/web/client/assets/journeyDiagram-3NMN7TZE-CPEB1Vt8.js +0 -139
- package/dist/web/client/assets/kanban-definition-UXKFOSKX-j-9WXjYQ.js +0 -89
- package/dist/web/client/assets/line-BIDAcEtX.js +0 -1
- package/dist/web/client/assets/mermaid-parser.core-Ux4vssh0.js +0 -7
- package/dist/web/client/assets/mermaid.core-CDuwVKeh.js +0 -44
- package/dist/web/client/assets/mindmap-definition-YA3MSWOX-5MlQEeQ6.js +0 -96
- package/dist/web/client/assets/packet-AYTQ26CC-DRO66c9Q.js +0 -1
- package/dist/web/client/assets/pegDiagram-XKGWAZYB-BakZnSjM.js +0 -1
- package/dist/web/client/assets/pie-WAS4IAKB-BEzcmhz8.js +0 -1
- package/dist/web/client/assets/pieDiagram-E7YTZNPT-DpmqgoPt.js +0 -39
- package/dist/web/client/assets/quadrantDiagram-AXDQQJYC-DuMPwwV2.js +0 -7
- package/dist/web/client/assets/radar-RG4KPBEZ-Djm3oXl5.js +0 -1
- package/dist/web/client/assets/railroad-74A4TZTK-Cpvkwsao.js +0 -1
- package/dist/web/client/assets/railroad-abnf-HS5TGJTU-CpRQ3ruG.js +0 -1
- package/dist/web/client/assets/railroad-ebnf-LZEXJU2U-1Rm5a5eJ.js +0 -1
- package/dist/web/client/assets/railroad-peg-WCYAUIDC-C8344cTh.js +0 -1
- package/dist/web/client/assets/railroadDiagram-O6MQD6OU-CgMS4B6R.js +0 -1
- package/dist/web/client/assets/requirementDiagram-BXWQKSXE-CX5eoNdN.js +0 -84
- package/dist/web/client/assets/sankeyDiagram-P5KCCOFB-CODBX2N3.js +0 -40
- package/dist/web/client/assets/sequenceDiagram-WJ2MYXX4-eUhhF8OR.js +0 -162
- package/dist/web/client/assets/stateDiagram-D77RDMKH-vR6l0NC2.js +0 -1
- package/dist/web/client/assets/stateDiagram-v2-MP3YSRHH-BYUm8jLY.js +0 -1
- package/dist/web/client/assets/swimlanes-42K2YHIH-v9Tw_zX7.js +0 -1
- package/dist/web/client/assets/swimlanesDiagram-VR7AAH4N-B2jdCvhD.js +0 -8
- package/dist/web/client/assets/timeline-definition-24CTP7MA-DxianbhU.js +0 -120
- package/dist/web/client/assets/treeView-Q6P3EWNA-DLxnZb9R.js +0 -1
- package/dist/web/client/assets/treemap-WGGIJYW6-I7UnZ6xJ.js +0 -1
- package/dist/web/client/assets/vennDiagram-4TSXK5OY-Ar7M8Mrj.js +0 -34
- package/dist/web/client/assets/wardley-WFR3VGLG-B8ZSHeDu.js +0 -1
- package/dist/web/client/assets/wardleyDiagram-VM6X3IG4-Qks2FmIn.js +0 -78
- package/dist/web/client/assets/xychartDiagram-S5SC5T6Z-BBBjRtiE.js +0 -7
- package/dist/web/client/index.html +0 -6
- package/dist/web/ops-worker.js +0 -37
- package/dist/web/reads-worker.js +0 -1726
- package/dist/web/server.js +0 -8499
- package/dist/web-EMGUGGEI.js +0 -13
- package/dist/web-fetch-6BRRVQON.js +0 -805
- package/dist/wiki-generate-XHSJTDOP.js +0 -627
- package/dist/with-panes-Z6BTEHMZ.js +0 -628
- package/evals/README.md +0 -9
- package/evals/behavioral-corpus-grader.mjs +0 -236
- package/evals/behavioral-machinery-baseline.json +0 -2276
- package/evals/behavioral-machinery-driver.ts +0 -250
- package/evals/behavioral-machinery-support.ts +0 -254
- package/evals/behavioral-machinery.yaml +0 -614
- package/evals/behavioral-model-negative-control.yaml +0 -69
- package/evals/behavioral-model.yaml +0 -482
- package/evals/fixtures/behavioral-facts.txt +0 -3
- package/evals/fixtures/behavioral-main.ts +0 -7
- package/evals/fixtures/decoy/note.txt +0 -2
- package/evals/fixtures/docs-retrieval.json +0 -102
- package/evals/fixtures/private/credentials.txt +0 -1
- package/evals/fixtures/scalar-decision.json +0 -17
- package/evals/fixtures/scout-citation-pipeline.json +0 -55
- package/evals/fixtures/source-explanation.json +0 -74
- package/evals/fixtures/tool-arguments.json +0 -18
- package/evals/fixtures/tool-surface/read.txt +0 -4
- package/evals/fixtures/tool-surface/subdir/keep.txt +0 -1
- package/evals/no-edit-memory-grader.mjs +0 -149
- package/evals/scalar-decision-grader.py +0 -41
- package/evals/scalar-decision-machinery.ts +0 -145
- package/evals/source-explanation-grader.mjs +0 -118
- package/evals/tool-surface-grader.mjs +0 -53
- package/evals/tool-surface-model.yaml +0 -274
- package/evals/tracked-metrics-baseline.yaml +0 -229
- package/evals/tracked-metrics-grader.mjs +0 -129
- package/src/cli/web.ts +0 -79
- package/src/domains/providers/runtimes/local-native/ollama-native.ts +0 -122
- /package/dist/assets/{web-notices → gui-notices}/@hono__node-server-LICENSE +0 -0
- /package/dist/assets/{web-notices → gui-notices}/hono-LICENSE +0 -0
- /package/dist/{web → gui}/client/THIRD_PARTY_LICENSES.md +0 -0
- /package/dist/{web → gui}/client/assets/arc-Pry-7G1_.js +0 -0
- /package/dist/{web → gui}/client/assets/array-BifhSqXX.js +0 -0
- /package/dist/{web → gui}/client/assets/atkinson-hyperlegible-next-latin-ext-wght-normal-C6vrW8VD.woff2 +0 -0
- /package/dist/{web → gui}/client/assets/atkinson-hyperlegible-next-latin-wght-normal-BcXVPD7q.woff2 +0 -0
- /package/dist/{web → gui}/client/assets/chunk-2Q5K7J3B-C1jixKkw.js +0 -0
- /package/dist/{web → gui}/client/assets/chunk-5VM5RSS4-ZNzvKenW.js +0 -0
- /package/dist/{web → gui}/client/assets/chunk-FOHPRMQF-CoA4358r.js +0 -0
- /package/dist/{web → gui}/client/assets/chunk-JWPE2WC7-DVXcaiue.js +0 -0
- /package/dist/{web → gui}/client/assets/chunk-XXDRQBXY-DqAYeljN.js +0 -0
- /package/dist/{web → gui}/client/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +0 -0
- /package/dist/{web → gui}/client/assets/commit-mono-latin-400-normal-s0S3qwFW.woff +0 -0
- /package/dist/{web → gui}/client/assets/commit-mono-latin-400-normal-wzhe4RuD.woff2 +0 -0
- /package/dist/{web → gui}/client/assets/cose-bilkent-JH36ORCC-CVh1icwb.js +0 -0
- /package/dist/{web → gui}/client/assets/cytoscape.esm-c2aL46s-.js +0 -0
- /package/dist/{web → gui}/client/assets/dagre-C-rnJpZF.js +0 -0
- /package/dist/{web → gui}/client/assets/defaultLocale-BFoDCU3G.js +0 -0
- /package/dist/{web → gui}/client/assets/dist-v5Q1xZ2K.js +0 -0
- /package/dist/{web → gui}/client/assets/graphlib-DS17s2tU.js +0 -0
- /package/dist/{web → gui}/client/assets/init-C-OQMol4.js +0 -0
- /package/dist/{web → gui}/client/assets/katex-ZlcWpGUi.js +0 -0
- /package/dist/{web → gui}/client/assets/linear-SzVmGSw4.js +0 -0
- /package/dist/{web → gui}/client/assets/newsreader-latin-ext-wght-normal-C-3rgBeH.woff2 +0 -0
- /package/dist/{web → gui}/client/assets/newsreader-latin-wght-normal-CCVVNp6i.woff2 +0 -0
- /package/dist/{web → gui}/client/assets/newsreader-vietnamese-wght-normal-Czsa-EzN.woff2 +0 -0
- /package/dist/{web → gui}/client/assets/ordinal-BDEzSJ7C.js +0 -0
- /package/dist/{web → gui}/client/assets/path-fybaL0A-.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-bash-D6zCJ74D.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-c-04YixN25.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-clike-DapgWyxx.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-core-BtsZdCS6.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-cpp-C-lJbC-6.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-css-CPgOzxXp.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-diff-BF8m0_mq.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-docker-DkT6yOFj.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-fortran-Be5x3y1V.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-go-C_8qAH5n.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-ini-BESK3y0r.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-javascript-BnO-swvr.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-json-Dp_-W-Hv.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-jsx-D-D0NWtC.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-julia-DIjOgiAV.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-latex-DNMbqcH_.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-makefile-CRKWLQ8l.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-markdown-C0FPJ1Iz.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-markup-Cp04rIa1.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-matlab-D-nk08al.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-python-CfiSVtM6.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-r-QmNGR8LX.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-rust-Bu9N8X3N.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-shell-session-Be4UuYoM.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-sql-CYhVDMEE.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-toml-DKb54GAT.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-tsx-p6J2Kc_4.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-typescript-BMqONzxf.js +0 -0
- /package/dist/{web → gui}/client/assets/prism-yaml-C0JHu8gr.js +0 -0
- /package/dist/{web → gui}/client/assets/purify.es-ChwZkWde.js +0 -0
- /package/dist/{web → gui}/client/assets/rolldown-runtime-hePW80VL.js +0 -0
- /package/dist/{web → gui}/client/assets/rough.esm-Dy-Kn_BL.js +0 -0
- /package/dist/{web → gui}/client/assets/sizeCapture-INFHLROL-B0uUizjq.js +0 -0
- /package/dist/{web → gui}/client/assets/src-B6xuSHsQ.js +0 -0
- /package/dist/{web → gui}/client/clio-coder-logo.webp +0 -0
- /package/dist/{web → gui}/client/icon-192.png +0 -0
- /package/dist/{web → gui}/client/icon-512.png +0 -0
- /package/dist/{web → gui}/client/manifest.webmanifest +0 -0
- /package/dist/{web → gui}/client/offline.css +0 -0
- /package/dist/{web → gui}/client/offline.html +0 -0
- /package/dist/{web → gui}/client/offline.js +0 -0
- /package/dist/{web → gui}/client/sw.js +0 -0
|
@@ -0,0 +1,1440 @@
|
|
|
1
|
+
# Chat rendering specification
|
|
2
|
+
|
|
3
|
+
> **Historical design record.** This blueprint describes the GUI at an earlier
|
|
4
|
+
> stage, before much of the conversation view shipped. Its proposed work, paths,
|
|
5
|
+
> and counts are historical. For current behavior, read the [GUI reference](README.md).
|
|
6
|
+
|
|
7
|
+
Use this page for the rationale behind conversation layouts, label taxonomies,
|
|
8
|
+
and empty states. Expand the background and implementation sketches as needed.
|
|
9
|
+
|
|
10
|
+
<details>
|
|
11
|
+
<summary>Original scope and relationship to the current GUI</summary>
|
|
12
|
+
|
|
13
|
+
The blueprint covers the conversation view and the thirteen domain inspectors. It
|
|
14
|
+
was recovered from `apps/workbench` before that application was deleted, and its
|
|
15
|
+
quoted paths and taxonomies were checked against `contracts/` and `src/` at that
|
|
16
|
+
time rather than against the workbench's own wire format. Treat every path and
|
|
17
|
+
count below as a dated citation, not as a present-tense claim.
|
|
18
|
+
|
|
19
|
+
Seventeen artifacts follow, each with a named destination file. When this was
|
|
20
|
+
written, two had landed and fifteen had not. Several of the fifteen have shipped
|
|
21
|
+
since, including `client/chat/turns.ts`, `client/chat/ActivityGroup.tsx`,
|
|
22
|
+
`client/chat/Diff.tsx`, `client/chat/Approval.tsx`, `client/chat/live-status.ts`
|
|
23
|
+
and `client/chat/tool-cards.tsx`. The list is kept in its original form because
|
|
24
|
+
the rationale under each artifact is the reason to read it.
|
|
25
|
+
|
|
26
|
+
The central finding was that the chat gap is not a protocol gap. `TimelineItem.rawInput`
|
|
27
|
+
and `rawOutput` already carry the harness's full tool argument object and result, and
|
|
28
|
+
`src/tools/edit-diff.ts` already emits a capped unified diff at
|
|
29
|
+
`rawOutput.result.details.diff`, so per-tool-kind cards and diff rendering were
|
|
30
|
+
buildable with no ACP change. What was missing then was conversational
|
|
31
|
+
architecture: turn grouping, the collapsing activity group, the live status chip,
|
|
32
|
+
approval anchoring, and roughly forty closed label taxonomies that the GUI at the
|
|
33
|
+
time replaced with `JSON.stringify` dumps inside `<details>` at fourteen call
|
|
34
|
+
sites.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
The prior audit's claim was correct on data but wrong in emphasis. Every inspector's *payload*
|
|
40
|
+
is reproduced by the new route table, but the workbench's value was never the payload: it was
|
|
41
|
+
~40 closed label taxonomies (gate reasons, eval failure classes, evidence trust axes, customization
|
|
42
|
+
categories, recovery sections, verifier rejections), a consistent tone function per domain, and a
|
|
43
|
+
rigorously-worked empty-state grammar that distinguishes "not read yet" from "store missing" from
|
|
44
|
+
"store present but empty" from "record predates this schema". Those taxonomies are the part of this
|
|
45
|
+
document that survives; the rendering that carries them has since been built.
|
|
46
|
+
|
|
47
|
+
On chat, the finding was sharper than expected: the workbench had NO per-tool-kind rendering and NO diff
|
|
48
|
+
rendering at all, because its wire (`TurnToolPayload`) carried only `{kind, title, summary, locations, status}`.
|
|
49
|
+
The new GUI's wire is strictly richer — `TimelineItem.rawInput`/`rawOutput` already carry the harness's
|
|
50
|
+
full tool argument object and result, and `src/tools/edit-diff.ts` already produces a capped unified diff
|
|
51
|
+
at `rawOutput.result.details.diff` — so per-tool-kind cards and diffs were buildable with no ACP change.
|
|
52
|
+
|
|
53
|
+
What had to be *ported* from the workbench was the conversational architecture the
|
|
54
|
+
GUI lacked at the time: turn grouping into request/response/activity segments, the
|
|
55
|
+
collapsing activity group with its disclosure policy, the live status chip,
|
|
56
|
+
follow-latest scrolling, the frame-coalescing event buffer, and the approval
|
|
57
|
+
anchoring/escalation/keyboard model. 17 artifacts follow, 11 of them chat.
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
</details>
|
|
63
|
+
|
|
64
|
+
## Conversation projection: groupTurns and the ChatSegment model
|
|
65
|
+
|
|
66
|
+
- **priority**: must-keep
|
|
67
|
+
- **from**: apps/workbench/src/chat.ts:20-126
|
|
68
|
+
- **to**: apps/clio-coder-gui/client/chat/turns.ts
|
|
69
|
+
- **value**: The single largest gap. `apps/clio-coder-gui/client/pages/sessions.tsx:270` renders `snapshot.timeline.map(item => <Timeline item/>)` — a flat list of undifferentiated cards. The workbench instead grouped the flat timeline into turns, and within a turn interleaved prose, reasoning, and *runs* of tool activity, so a turn that ran 14 tools between two paragraphs shows as: paragraph, one collapsed activity group, paragraph. Without this the chat is unreadable at real tool volume. It also carries the referential-identity trick that makes memoization work: a turn whose items are element-wise identical keeps its object identity, so settled turns never re-render while a later turn streams.
|
|
70
|
+
|
|
71
|
+
Port verbatim in behavior, retyped for `contracts/sessions.ts` `TimelineItem` (kinds are `user|text|thought|tool|notice`, not the workbench's 8-kind set).
|
|
72
|
+
|
|
73
|
+
<details>
|
|
74
|
+
<summary>`ChatSegment`, `ChatTurn`, `buildTurn`, and the `sameItems` identity check</summary>
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import type { TimelineItem } from "../../contracts/sessions.js";
|
|
78
|
+
|
|
79
|
+
export type ChatSegment =
|
|
80
|
+
| Readonly<{ kind: "response"; item: TimelineItem }>
|
|
81
|
+
| Readonly<{ kind: "reasoning"; item: TimelineItem }>
|
|
82
|
+
| Readonly<{ kind: "activity"; items: readonly TimelineItem[] }>;
|
|
83
|
+
|
|
84
|
+
export interface ChatTurn {
|
|
85
|
+
readonly turnId: string;
|
|
86
|
+
readonly origin: "live" | "replay";
|
|
87
|
+
readonly request: TimelineItem | null;
|
|
88
|
+
readonly segments: readonly ChatSegment[];
|
|
89
|
+
readonly items: readonly TimelineItem[];
|
|
90
|
+
/** True when nothing in this turn can still change. */
|
|
91
|
+
readonly settled: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function sameItems(previous: readonly TimelineItem[], next: readonly TimelineItem[]): boolean {
|
|
95
|
+
if (previous.length !== next.length) return false;
|
|
96
|
+
for (let index = 0; index < previous.length; index += 1) if (previous[index] !== next[index]) return false;
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function buildTurn(turnId: string, items: readonly TimelineItem[], turnStatus: string | undefined): ChatTurn {
|
|
101
|
+
let request: TimelineItem | null = null;
|
|
102
|
+
const segments: ChatSegment[] = [];
|
|
103
|
+
let activity: TimelineItem[] | null = null;
|
|
104
|
+
const flush = () => {
|
|
105
|
+
if (activity !== null && activity.length > 0) segments.push({ kind: "activity", items: activity });
|
|
106
|
+
activity = null;
|
|
107
|
+
};
|
|
108
|
+
for (const item of items) {
|
|
109
|
+
switch (item.kind) {
|
|
110
|
+
case "user":
|
|
111
|
+
request = item;
|
|
112
|
+
break;
|
|
113
|
+
case "text":
|
|
114
|
+
flush();
|
|
115
|
+
segments.push({ kind: "response", item });
|
|
116
|
+
break;
|
|
117
|
+
case "thought":
|
|
118
|
+
flush();
|
|
119
|
+
segments.push({ kind: "reasoning", item });
|
|
120
|
+
break;
|
|
121
|
+
case "tool":
|
|
122
|
+
case "notice":
|
|
123
|
+
(activity ??= []).push(item);
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
flush();
|
|
128
|
+
const origin = items[0]?.origin ?? "live";
|
|
129
|
+
return { turnId, origin, request, segments, items, settled: origin === "replay" || (turnStatus !== undefined && turnStatus !== "running") };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Groups timeline items into turns. A turn whose items are all identical to the
|
|
134
|
+
* previous grouping keeps its object identity, so settled turns never re-render
|
|
135
|
+
* while a later one streams.
|
|
136
|
+
*/
|
|
137
|
+
export function groupTurns(
|
|
138
|
+
timeline: readonly TimelineItem[],
|
|
139
|
+
turnStatus: ReadonlyMap<string, string>,
|
|
140
|
+
previous: readonly ChatTurn[] = [],
|
|
141
|
+
): readonly ChatTurn[] {
|
|
142
|
+
const order: string[] = [];
|
|
143
|
+
const byTurn = new Map<string, TimelineItem[]>();
|
|
144
|
+
for (const item of timeline) {
|
|
145
|
+
let bucket = byTurn.get(item.turnId);
|
|
146
|
+
if (bucket === undefined) {
|
|
147
|
+
bucket = [];
|
|
148
|
+
byTurn.set(item.turnId, bucket);
|
|
149
|
+
order.push(item.turnId);
|
|
150
|
+
}
|
|
151
|
+
bucket.push(item);
|
|
152
|
+
}
|
|
153
|
+
const previousByTurn = new Map(previous.map((turn) => [turn.turnId, turn] as const));
|
|
154
|
+
let unchanged = previous.length === order.length;
|
|
155
|
+
const turns = order.map((turnId, index) => {
|
|
156
|
+
const items = byTurn.get(turnId) ?? [];
|
|
157
|
+
const prior = previousByTurn.get(turnId);
|
|
158
|
+
if (prior !== undefined && sameItems(prior.items, items) && prior.settled === (turnStatus.get(turnId) !== "running")) {
|
|
159
|
+
if (previous[index] !== prior) unchanged = false;
|
|
160
|
+
return prior;
|
|
161
|
+
}
|
|
162
|
+
unchanged = false;
|
|
163
|
+
return buildTurn(turnId, items, turnStatus.get(turnId));
|
|
164
|
+
});
|
|
165
|
+
return unchanged ? previous : turns;
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Difference from the workbench that the port must handle: the workbench's turn outcome was a *timeline item* (`kind: "outcome" | "failure"`), so `settled` fell out of the item list. The new GUI keeps outcome on `SessionSnapshot.turns[]` instead, so `groupTurns` takes a `turnStatus` map built as `new Map(snapshot.turns.map(t => [t.id, t.status]))` and the outcome footer reads the `Turn` row. Build the map with `useMemo` keyed on `snapshot.turns`.
|
|
170
|
+
|
|
171
|
+
Call site in `SessionView`, replacing the flat `.map`:
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
const previousTurns = useRef<readonly ChatTurn[]>([]);
|
|
175
|
+
const statuses = useMemo(() => new Map(snapshot.turns.map((t) => [t.id, t.status] as const)), [snapshot.turns]);
|
|
176
|
+
const turns = useMemo(() => {
|
|
177
|
+
const next = groupTurns(snapshot.timeline, statuses, previousTurns.current);
|
|
178
|
+
previousTurns.current = next;
|
|
179
|
+
return next;
|
|
180
|
+
}, [snapshot.timeline, statuses]);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Memo comparator for the turn view (workbench `Chat.tsx:400-405`), which is what actually buys the render savings:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
(previous, next) => {
|
|
187
|
+
if (previous.turn !== next.turn) return false;
|
|
188
|
+
if (previous.turn.settled) return true;
|
|
189
|
+
return previous.pendingPermission === next.pendingPermission && previous.nowMs === next.nowMs && previous.onResolve === next.onResolve;
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
And pass `nowMs={turn.settled ? 0 : nowMs}` down, so the once-a-second clock tick cannot invalidate a settled turn (`Chat.tsx:429`). This is the reason the workbench resumed a 64-turn session in 119–172 ms with 683 DOM nodes.
|
|
194
|
+
|
|
195
|
+
</details>
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Activity group: summarizeActivity and the disclosure policy
|
|
200
|
+
|
|
201
|
+
- priority: must-keep
|
|
202
|
+
- from: apps/workbench/src/chat.ts:128-208 and apps/workbench/src/Chat.tsx:177-281
|
|
203
|
+
- to: apps/clio-coder-gui/client/chat/activity.ts + client/chat/ActivityGroup.tsx
|
|
204
|
+
- value: This is the mechanism that keeps a 40-tool turn readable. It derives a one-line label and a tone from item statuses alone (no new server field), decides whether the group opens itself, shows the currently-running tool's title on the collapsed summary line so a silent run still tells the operator what is happening, and respects a manual toggle forever after. `apps/clio-coder-gui` has no equivalent; every tool is a permanently-expanded card.
|
|
205
|
+
|
|
206
|
+
<details>
|
|
207
|
+
<summary>`summarizeActivity`, the `ActivityTone` scale, and the group component</summary>
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
export type ActivityTone = "neutral" | "info" | "action" | "success" | "warning" | "error";
|
|
211
|
+
|
|
212
|
+
export interface ActivitySummary {
|
|
213
|
+
readonly label: string;
|
|
214
|
+
readonly tone: ActivityTone;
|
|
215
|
+
readonly total: number;
|
|
216
|
+
readonly running: number;
|
|
217
|
+
readonly waiting: number;
|
|
218
|
+
readonly completed: number;
|
|
219
|
+
readonly failed: number;
|
|
220
|
+
readonly canceled: number;
|
|
221
|
+
/** True when something in the group needs the operator's eyes right now. */
|
|
222
|
+
readonly attention: boolean;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const plural = (count: number, noun: string) => `${count} ${noun}${count === 1 ? "" : "s"}`;
|
|
226
|
+
|
|
227
|
+
export function summarizeActivity(items: readonly TimelineItem[]): ActivitySummary {
|
|
228
|
+
let running = 0, waiting = 0, completed = 0, failed = 0, canceled = 0, onlyTools = true;
|
|
229
|
+
for (const item of items) {
|
|
230
|
+
if (item.kind !== "tool") onlyTools = false;
|
|
231
|
+
switch (item.status) {
|
|
232
|
+
case "in_progress": case "pending": running += 1; break;
|
|
233
|
+
case "escalated": waiting += 1; break;
|
|
234
|
+
case "failed": failed += 1; break;
|
|
235
|
+
case "cancelled": case "rejected": case "expired": canceled += 1; break;
|
|
236
|
+
default: completed += 1; break; // completed, allowed
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const noun = onlyTools ? "tool" : "step";
|
|
240
|
+
let label: string, tone: ActivityTone;
|
|
241
|
+
if (waiting > 0) { label = "Approval needed"; tone = "warning"; }
|
|
242
|
+
else if (running > 0) { label = `${plural(running, noun)} running${completed > 0 ? ` · ${completed} done` : ""}`; tone = "action"; }
|
|
243
|
+
else if (failed > 0) { label = `${plural(failed, noun)} failed${completed > 0 ? ` · ${completed} completed` : ""}`; tone = "error"; }
|
|
244
|
+
else if (canceled > 0 && completed === 0) { label = `${plural(canceled, noun)} stopped`; tone = "neutral"; }
|
|
245
|
+
else { label = `${plural(completed, noun)} completed${canceled > 0 ? ` · ${canceled} stopped` : ""}`; tone = "success"; }
|
|
246
|
+
return { label, tone, total: items.length, running, waiting, completed, failed, canceled, attention: waiting > 0 || running > 0 || failed > 0 };
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Status-mapping note: the workbench's 7-value `status` union does not exist in the new contract — `TimelineItem.status` is an open `Type.String()` carrying the raw ACP status. Map as above; add a shared `TOOL_STATUS_LABELS` for the per-row state text:
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
export const TOOL_STATUS_LABELS: Readonly<Record<string, string>> = {
|
|
254
|
+
pending: "queued", in_progress: "running", completed: "done",
|
|
255
|
+
cancelled: "stopped", failed: "failed", escalated: "waiting",
|
|
256
|
+
allowed: "allowed", rejected: "rejected", expired: "expired",
|
|
257
|
+
};
|
|
258
|
+
export const STATUS_GLYPHS: Readonly<Record<string, string>> = {
|
|
259
|
+
pending: "…", in_progress: "◐", escalated: "!", completed: "✓",
|
|
260
|
+
cancelled: "–", failed: "✕", allowed: "✓", rejected: "✕", expired: "–",
|
|
261
|
+
};
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Disclosure policy, the part that matters (`Chat.tsx:249-267`):
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
const [userOpen, setUserOpen] = useState<boolean | null>(null);
|
|
268
|
+
// Default: open only while something still needs attention. Once the operator
|
|
269
|
+
// toggles it, their choice wins forever — `userOpen` is never reset.
|
|
270
|
+
const open = userOpen ?? ((!settled && summary.attention) || summary.failed > 0 || summary.waiting > 0);
|
|
271
|
+
const running = items.find((item) => item.status === "in_progress");
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
<details className={`activity activity--${summary.tone}${open ? " is-open" : ""}`} open={open}
|
|
276
|
+
onToggle={(e) => { if (e.currentTarget.open !== open) setUserOpen(e.currentTarget.open); }}>
|
|
277
|
+
<summary className="activity__summary">
|
|
278
|
+
<span className="activity__glyph" aria-hidden="true">
|
|
279
|
+
{summary.waiting > 0 ? "!" : summary.running > 0 ? "◐" : summary.failed > 0 ? "✕" : "✓"}
|
|
280
|
+
</span>
|
|
281
|
+
<span className="activity__label">{summary.label}</span>
|
|
282
|
+
{/* The one fact that makes a collapsed group still informative. */}
|
|
283
|
+
{running !== undefined && summary.waiting === 0 && <span className="activity__current">{running.title}</span>}
|
|
284
|
+
<span className="activity__count" aria-hidden="true">{summary.total}</span>
|
|
285
|
+
</summary>
|
|
286
|
+
<ul className="activity__rows">{items.map((item) => <ToolRow key={item.id} item={item} … />)}</ul>
|
|
287
|
+
</details>
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Row layout is a 4-column grid: glyph, kind label, title (with agent tag + notes stacked under it), state + elapsed. Elapsed is only printed when the row is running AND `>= 2` seconds, so short calls do not flicker a timer (`Chat.tsx:207`). Kind label for a tool row comes from the tool kind; for a notice row it is `"approval"` or `"safety"`.
|
|
291
|
+
|
|
292
|
+
Agent attribution tag (`Chat.tsx:63-77`) — take the LAST entry of `item.provenance`, and render only when its `role === "worker"`, so a delegated agent wins over the orchestrator and an unattributed call stays attributed to the product:
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
export function workerLabel(provenance: Provenance | undefined): string | null {
|
|
296
|
+
const last = provenance?.at(-1);
|
|
297
|
+
if (last === undefined || last.role !== "worker") return null;
|
|
298
|
+
return last.node == null ? last.agentId : `${last.agentId} · ${last.node}`;
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Title attribute on the tag: `"Reported by Clio Coder as the agent that ran this call"`.
|
|
303
|
+
|
|
304
|
+
</details>
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Per-tool-kind tool call rendering
|
|
309
|
+
|
|
310
|
+
- priority: must-keep
|
|
311
|
+
- from: apps/workbench/src/Chat.tsx:177-239 (what existed) + clio-host.ts:303-335 (safeToolTitle/presentableToolTitle) + iowarp/clio-coder/src/engine/acp/server.ts:235-252,915-985 (the wire that makes the new version possible)
|
|
312
|
+
- to: apps/clio-coder-gui/client/chat/tool-cards.tsx
|
|
313
|
+
- value: The highest-value item in this lens, and the one the audit could not have found by reading the workbench, because the workbench DID NOT HAVE IT. Its wire (`TurnToolPayload` in src/protocol.ts:2264) carried only `{toolCallId, title, kind, status, summary, locations, agents}` — no arguments, no output. Every tool therefore rendered as one identical grey row. The new GUI's wire is strictly richer: `TimelineItem.rawInput` is the harness's literal tool argument object and `rawOutput` is `{result, isError}` where `result` is the full `ToolResult` including `details`. It is currently thrown into `JSON.stringify(…)` inside a `<details>` at sessions.tsx:197. This spec defines the card per tool, keyed on the harness's real tool names, using fields that exist today. No ACP change needed.
|
|
314
|
+
|
|
315
|
+
The ACP `kind` enum is a coarse UI hint only. `src/engine/acp/server.ts:235` maps Clio tool names onto it and the mapping is lossy — `git`, `dispatch`, `steer` all become `other`; `read`, `ls`, `context`, `monitor` all become `read`. So **key the renderer on `item.title`, which is the canonical Clio tool name** (`server.ts:933` sets `title: boundString(toolName)`), and fall back to `item.toolKind` for MCP/dynamic tools.
|
|
316
|
+
|
|
317
|
+
The canonical name set (from `iowarp/clio-coder/src/tools/`): `read ls context write edit artifact grep find code_nav bash verify web_fetch git dispatch monitor steer ask_user decide evidence ledger panes run_script safe_exec task`.
|
|
318
|
+
|
|
319
|
+
<details>
|
|
320
|
+
<summary>The `ToolPresenter` type every per-kind card implements</summary>
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
export type ToolPresenter = {
|
|
324
|
+
/** Short kind chip, lowercase. */ chip: string;
|
|
325
|
+
/** One line, always visible, ~72 chars. Never JSON. */ headline(raw: Record<string, unknown>): string;
|
|
326
|
+
/** Body renderer for the expanded row, or null for headline-only tools. */
|
|
327
|
+
body: "diff" | "terminal" | "matches" | "file" | "fetch" | "dispatch" | "json" | null;
|
|
328
|
+
};
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
</details>
|
|
332
|
+
|
|
333
|
+
**Per-tool contract.** `in` = `item.rawInput`; `out` = `item.rawOutput.result` (`{ output?: string, details?: Record<string, unknown> }`), `err` = `item.rawOutput.isError === true`.
|
|
334
|
+
|
|
335
|
+
| title | chip | headline | body | key fields read |
|
|
336
|
+
| --- | --- | --- | --- | --- |
|
|
337
|
+
| `read` | `read` | `` `${basename(in.path)}` `` + `` ` · lines ${in.offset ?? 1}–${(in.offset ?? 1) + (in.limit ?? n) - 1}` `` when either is set | `file` | `out.details.file` (`bytes`, `lines`, `truncated`), `out.details.totalLines`, `out.details.code === "read_past_eof"` |
|
|
338
|
+
| `ls` | `list` | `` `${in.path || "."}` `` | `file` | entry count from `out.output` line count |
|
|
339
|
+
| `edit` | `edit` | `` `${basename(in.path)} · ${in.edits?.length ?? 1} replacement${s}` `` | **`diff`** | `out.details.diff` (unified), `out.details.firstChangedLine` |
|
|
340
|
+
| `write` | `write` | `` `${basename(in.path)}${out.details.diff ? "" : " · new file"}` `` | **`diff`** | `out.details.diff` |
|
|
341
|
+
| `artifact` | `edit` | `` `${in.op ?? "artifact"} ${basename(in.path ?? "")}` `` | `diff` | as `edit` |
|
|
342
|
+
| `bash` | `bash` | first line of `in.command`, capped 72 chars with `…` | **`terminal`** | `out.details.exitCode`, `.signal`, `.timedOut`, `.aborted`, `.outputCapped`, `.stdoutBytes`, `.stderrBytes`, `.outcome` |
|
|
343
|
+
| `run_script` / `verify` / `safe_exec` | `run` | `` `${in.script ?? in.id ?? in.op}` `` | `terminal` | same as bash |
|
|
344
|
+
| `grep` | `grep` | `` `/${in.pattern}/ in ${in.path ?? "."}${in.glob ? ` (${in.glob})` : ""}` `` | **`matches`** | `out.details.search` (`{ matches, filesSearched, truncated }`), `out.output` as `path:line:text` rows |
|
|
345
|
+
| `find` | `find` | `` `${in.pattern ?? in.glob}` `` | `matches` | `out.details.search` |
|
|
346
|
+
| `code_nav` | `nav` | `` `${in.op} ${in.symbol ?? in.path}` `` | `matches` | — |
|
|
347
|
+
| `web_fetch` | `fetch` | hostname + pathname of `in.url` (parse with `URL`; if it throws, show the raw string escaped) | **`fetch`** | `out.details` (`status`, `contentType`, `bytes`, `redirects`, `blocked`) |
|
|
348
|
+
| `dispatch` | `dispatch` | `` `${in.agent ?? in.recipe} · ${in.task ? truncate(in.task, 60) : "no task preview"}` `` | **`dispatch`** | correlate with `SessionSnapshot.fleet[]` on `runId` |
|
|
349
|
+
| `git` | `git` | `` `git ${in.op}` `` | `terminal` | — |
|
|
350
|
+
| `monitor` / `steer` / `panes` | `control` | `` `${in.op ?? title}` `` | `json` | — |
|
|
351
|
+
| `ask_user` / `decide` | `ask` | `in.question ?? in.prompt`, capped 72 | null | — |
|
|
352
|
+
| anything else | `item.toolKind ?? "tool"` | `item.title` | `json` | — |
|
|
353
|
+
|
|
354
|
+
**Body renderers.**
|
|
355
|
+
|
|
356
|
+
- `terminal`: a monospace block showing `out.output` (already the harness's bounded tail) in `<pre>` with `overflow-x:auto`, preceded by a fact strip `exit ${exitCode} · ${formatBytes(stdoutBytes)} out · ${formatBytes(stderrBytes)} err` and, when `outputCapped`, the line `Output hit the 16 MiB cap and the command was stopped.` A `timedOut` run reads `Timed out.`; `aborted` reads `Cancelled.` Tone: `error` when `err` or `exitCode !== 0`.
|
|
357
|
+
- `matches`: parse `out.output` as `path:line:text` rows, group by `path`, render as a `<details>` per file with the file's match count on the summary; each row is `<span class="line">{line}</span><code>{text}</code>`. Cap at 200 rendered rows with `and N more matches` (the harness already bounds the payload; this is a DOM bound). Fact strip: `${matches} matches in ${filesSearched} files${truncated ? " · truncated by Clio" : ""}`.
|
|
358
|
+
- `file`: fact strip only — `${lines} lines · ${formatBytes(bytes)}${truncated ? " · truncated" : ""}`; when `out.details.code === "read_past_eof"`, the line `Requested range is past end of file (${totalLines} lines).` No content body: a read's content is what the assistant then talks about, and duplicating it doubles the transcript.
|
|
359
|
+
- `fetch`: fact strip `${status} · ${contentType} · ${formatBytes(bytes)}${redirects ? ` · ${redirects} redirect(s)` : ""}`; a `blocked` result renders as a warning row naming the policy that blocked it, never the URL's query string.
|
|
360
|
+
- `dispatch`: see the fleet strip artifact; render the matching `FleetItem` rows inline under the call.
|
|
361
|
+
- `json`: last resort — the current `<pre>{JSON.stringify(…)}</pre>`, but inside a closed `<details>` labelled `Tool input and result`, and only when the row is expanded.
|
|
362
|
+
|
|
363
|
+
<details>
|
|
364
|
+
<summary>`SAFE_TOOL_TITLES` and the path-shortening rules</summary>
|
|
365
|
+
|
|
366
|
+
**Path presentation.** Port `presentableToolTitle` (clio-host.ts:320): replace every occurrence of the workspace root with `[project]`, strip every code point `< 0x20` and `0x7f`, trim, and cap at 511 UTF-8 bytes with a trailing `…`. Empty after stripping falls back to a generic label. The generic labels (clio-host.ts:303) are worth keeping as the fallback when `title` is missing:
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
export const SAFE_TOOL_TITLES: Readonly<Record<string, string>> = {
|
|
370
|
+
read: "Read project content", edit: "Edit project content", delete: "Delete project content",
|
|
371
|
+
move: "Move project content", search: "Search project content", execute: "Run a project command",
|
|
372
|
+
think: "Reason about the task", fetch: "Fetch external content",
|
|
373
|
+
switch_mode: "Change work mode", other: "Use a Clio Coder tool",
|
|
374
|
+
};
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
</details>
|
|
378
|
+
|
|
379
|
+
**Locations.** `item.locations` is `[{path, line?}]`. Render each as `path` with `:${line + 1}` appended when `line` is an integer (ACP lines are zero-based; the current code at sessions.tsx:190 already does the `+1`). Show at most 3 inline; the rest behind `+N more`.
|
|
380
|
+
|
|
381
|
+
**Long-running.** Port `LONG_RUNNING_TOOL_SECONDS = 30` (App.tsx:1156): a tool row whose status is `in_progress` and whose elapsed exceeds 30 s gets a `still running · ${formatDuration(s)}` marker and a distinct class, so a hung bash is visible without expanding the group.
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## Diff rendering for edit and write
|
|
386
|
+
|
|
387
|
+
- priority: must-keep
|
|
388
|
+
- from: NOT in apps/workbench (it had no diff surface). Source of truth: iowarp/clio-coder/src/tools/edit-diff.ts:18-24, src/tools/edit.ts:187-211, src/tools/write.ts:56-78
|
|
389
|
+
- to: apps/clio-coder-gui/client/chat/Diff.tsx
|
|
390
|
+
- value: The workbench never showed a diff, which is the largest single functional gap against every comparable GUI. It is buildable today with zero wire changes: the harness already generates a unified diff and ships it inside rawOutput. Recording it here because the fact that the diff already exists on the wire is exactly the thing a reader of the deleted workbench would never learn.
|
|
391
|
+
|
|
392
|
+
**Where the diff lives.** `item.rawOutput.result.details.diff` is a unified-diff string; `item.rawOutput.result.details.firstChangedLine` is a 1-based line number. Produced by `generateDiffString()` in `src/tools/edit-diff.ts`, which caps each rendered line at `MAX_DIFF_LINE_CHARS = 500` (appending `… (+N chars)`) and the whole diff at `MAX_DIFF_BYTES = 32_768`. **It is `undefined` when the previous or new file exceeds 1 MiB** — in that case `out.output` contains the literal sentence `note: diff skipped because the previous or new file exceeds 1 MiB`, and the card must say so rather than showing an empty diff.
|
|
393
|
+
|
|
394
|
+
<details>
|
|
395
|
+
<summary>`parseUnifiedDiff`, the `DiffLine` union, and hunk-header handling</summary>
|
|
396
|
+
|
|
397
|
+
```ts
|
|
398
|
+
export type DiffLine =
|
|
399
|
+
| { kind: "hunk"; text: string; oldStart: number; newStart: number }
|
|
400
|
+
| { kind: "add" | "del" | "ctx"; text: string; oldLine: number | null; newLine: number | null };
|
|
401
|
+
|
|
402
|
+
export function parseUnifiedDiff(source: string): readonly DiffLine[] {
|
|
403
|
+
const out: DiffLine[] = [];
|
|
404
|
+
let oldLine = 0, newLine = 0;
|
|
405
|
+
for (const raw of source.split("\n")) {
|
|
406
|
+
const hunk = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(raw);
|
|
407
|
+
if (hunk !== null) {
|
|
408
|
+
oldLine = Number(hunk[1]); newLine = Number(hunk[2]);
|
|
409
|
+
out.push({ kind: "hunk", text: raw, oldStart: oldLine, newStart: newLine });
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
if (raw.startsWith("---") || raw.startsWith("+++") || raw.startsWith("diff ") || raw.startsWith("index ")) continue;
|
|
413
|
+
if (raw.startsWith("+")) { out.push({ kind: "add", text: raw.slice(1), oldLine: null, newLine: newLine++ }); continue; }
|
|
414
|
+
if (raw.startsWith("-")) { out.push({ kind: "del", text: raw.slice(1), oldLine: oldLine++, newLine: null }); continue; }
|
|
415
|
+
if (raw.startsWith("\\")) continue; // ""
|
|
416
|
+
out.push({ kind: "ctx", text: raw.startsWith(" ") ? raw.slice(1) : raw, oldLine: oldLine++, newLine: newLine++ });
|
|
417
|
+
}
|
|
418
|
+
return out;
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
</details>
|
|
423
|
+
|
|
424
|
+
**Rendering.** A two-gutter table-free grid (`grid-template-columns: 3.5rem 3.5rem 1fr`) so copy-paste yields only the code:
|
|
425
|
+
- old line number, new line number, then `<code>` with the text;
|
|
426
|
+
- `add` rows: green-tinted ground, `+` marker in a `::before` that is `user-select: none`; `del`: red-tinted, `−` marker; `ctx`: transparent;
|
|
427
|
+
- `hunk` rows span all three columns, dimmed, monospace;
|
|
428
|
+
- the whole block sits in `overflow-x: auto` with `white-space: pre` so long lines scroll rather than wrap;
|
|
429
|
+
- syntax highlight the text column by reusing `client/render/highlight.ts` with the grammar inferred from the file extension of `in.path` via `codeLanguage()` — **only when the call is settled and the diff is under `HIGHLIGHT_MAX_CHARS = 60_000`**, matching the existing `CodeBlock` policy, and only when the row is expanded and near the viewport (`useNearViewport`, `rootMargin: "600px"`).
|
|
430
|
+
|
|
431
|
+
**Header strip:** `${basename(in.path)} · +${adds} −${dels}` and a `Copy diff` button reusing the existing `CopyButton` from `client/render/Markdown.tsx:107`.
|
|
432
|
+
|
|
433
|
+
**Collapse policy:** a diff of more than 40 rendered lines opens showing the hunk containing `firstChangedLine` plus 3 lines of context, with `Show all ${n} lines` expanding it. A diff of 40 or fewer lines is fully open. This is the only place in the chat where an expensive body opens by default, because the diff is the thing the operator is deciding about.
|
|
434
|
+
|
|
435
|
+
**Before vs after approval.** The ACP permission request carries the same `toolCall` object the client already drew (`apps/clio-coder-gui/server/acp/permissions.ts:36-58` asserts `rawInput` is byte-identical to the drawn call). So:
|
|
436
|
+
- **Before approval** — status is `pending`/`in_progress` and `rawOutput` is absent. There is no diff yet because the harness has not applied the edit. Render the *intent* from `rawInput`: for `edit`, one block per `in.edits[i]` showing `oldText` as deletions and `newText` as additions, synthesized locally with the same grid, labelled `Proposed · not yet applied`. For `write`, show `in.content` as an all-additions block labelled `Proposed new contents`. Cap each synthesized block at 400 lines.
|
|
437
|
+
- **After approval** — the terminal `tool_call_update` arrives with `rawOutput`; swap the synthesized intent for the real `details.diff` in place, keeping the same DOM node so the card does not jump. Label changes to `Applied`. A `failed` status keeps the proposed block and adds the error line from `out.output`.
|
|
438
|
+
- **After rejection** — the permission resolves `rejected`, the tool call settles `cancelled`, and the proposed block stays visible, dimmed, labelled `Not applied · you rejected this`.
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## Permission card: anchoring, escalation, countdown, keyboard, notification
|
|
443
|
+
|
|
444
|
+
- priority: must-keep
|
|
445
|
+
- from: apps/workbench/src/App.tsx:1212-1316 (PermissionCard, ApprovalBanner), 8046-8054 (escalation), 8330-8384 (title, Alt+A/Alt+R, notification), src/Chat.tsx:185-236 (anchored row)
|
|
446
|
+
- to: apps/clio-coder-gui/client/chat/Approval.tsx
|
|
447
|
+
- value: The new GUI's PermissionCards (session-controls.tsx:11-49) is a flat list above the transcript that says 'Review the tool input and locations in the conversation' — it never links to the call it gates, has no countdown, no escalation state, no keyboard path, and no title/notification signal. The workbench solved all of that, and its comment records why: the card the operator missed in a recorded session was anchored wherever the timeline happened to be. This carries the full model.
|
|
448
|
+
|
|
449
|
+
**Two surfaces, one decision.** The workbench rendered the approval twice and kept both live:
|
|
450
|
+
1. An **anchored row** inside the activity group, attached to the exact tool call — the workbench matched on `item.id.endsWith(':' + permissionId)`. In the new contract the link is direct and exact: `permission.toolCallId === item.toolCallId`. Use that. The anchored row carries the Allow/Reject buttons and a facts line.
|
|
451
|
+
2. A **banner** pinned above the scrolling transcript (never inside it), `role="region"`, **not focus-trapping**: the operator may keep reading, scrolling and typing while an approval waits. The banner is shown unconditionally; when the anchored row exists too, both are live and either answers.
|
|
452
|
+
|
|
453
|
+
**Escalation** is a state, not a timeout. `Permission` already carries `requestedAt`, `escalateAt`, `expiresAt`, and `status: "pending" | "escalated"`. Derive locally rather than waiting for the `permission.escalated` delta so the UI never lags the clock:
|
|
454
|
+
|
|
455
|
+
```ts
|
|
456
|
+
const escalated = permission.status === "escalated" || nowMs >= Date.parse(permission.escalateAt);
|
|
457
|
+
const waited = Math.max(0, Math.floor((nowMs - Date.parse(permission.requestedAt)) / 1000));
|
|
458
|
+
const remaining = Math.max(0, Math.floor((Date.parse(permission.expiresAt) - nowMs) / 1000));
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
Banner eyebrow: `escalated ? "APPROVAL WAITING · ESCALATED" : "APPROVAL NEEDED"`. Card eyebrow: `"APPROVAL NEEDED · ONE USE"` — the `ONE USE` is load-bearing, because the only two options on the wire are `allow-once` and `reject` (`contracts/permissions.ts:4`); there is deliberately no allow-always.
|
|
462
|
+
|
|
463
|
+
**Safety facts the card must carry**, in this order:
|
|
464
|
+
1. `permission.title` as the heading (this is the canonical tool name, e.g. `edit`).
|
|
465
|
+
2. `${permission.kind} access to ${locations.length === 0 ? "this turn" : locations.join(", ")}.` — locations come from the **gated tool call's** `item.locations`, not from the permission, which carries none.
|
|
466
|
+
3. The countdown, phrased as consequence, not as a timer: `"If unanswered, this turn stops in ${formatDuration(remaining)} (at ${formatTime(permission.expiresAt)})."` Update on the shared 1 s clock.
|
|
467
|
+
4. `"Nothing runs until you answer. The GUI never answers for you."` — keep this sentence verbatim; it is the product's whole safety posture in one line.
|
|
468
|
+
5. Waited: `"Waiting ${formatDuration(waited)}."`
|
|
469
|
+
6. **New, and the reason the card should be anchored:** the diff or command being gated, inline. For an `edit`/`write` call render the proposed-diff block from the diff artifact; for `bash` render the command in a `<code>`; for `web_fetch` the host. This is what 'review the tool input in the conversation' was asking the operator to do by hand.
|
|
470
|
+
|
|
471
|
+
**Actions:** `Reject` (quiet button) then `Allow once` (primary), in that order — destructive-safe first, affirmative last. Disable both while the mutation is in flight.
|
|
472
|
+
|
|
473
|
+
**Keyboard** (`App.tsx:8340-8365`): `Alt+A` allows once, `Alt+R` rejects, from anywhere in the document. Suppressed while any modal or dialog is open, so a dialog's own controls stay unambiguous. Print the hint on the banner: `"Alt+A allows once · Alt+R rejects"`. Declare the bindings once in a `KEYBINDINGS` table and match handlers against the table, never against literals — the workbench enforced this so the help reference could not drift from the handlers.
|
|
474
|
+
|
|
475
|
+
**Document title** (`App.tsx:8331-8337`): while any permission is pending, set `document.title = "● Approval needed — Clio Coder"`; restore the previous title on cleanup. Keyed on `permissionId` so a second card re-fires it.
|
|
476
|
+
|
|
477
|
+
**Desktop notification** (`App.tsx:8369-8384`): one per card, and **only if `Notification.permission === "granted"` already** — this effect never asks for the permission; a settings toggle does. Body is `permission.title` only. A path in a notification would leave the project boundary. Wrap the constructor in try/catch; a browser that refuses is not a failure. Return `() => posted.close()`.
|
|
478
|
+
|
|
479
|
+
<details>
|
|
480
|
+
<summary>`RESOLUTION_SUMMARY`, the exact wording for each approval outcome</summary>
|
|
481
|
+
|
|
482
|
+
**Resolution wording** for the settled notice row (workbench `timeline.ts:180-193`), which the new GUI currently renders as the bare string `Permission ${status}: ${title}`:
|
|
483
|
+
|
|
484
|
+
```ts
|
|
485
|
+
export const RESOLUTION_SUMMARY: Readonly<Record<string, string>> = {
|
|
486
|
+
allowed: "Allowed once by the operator.",
|
|
487
|
+
rejected: "Rejected by the operator. Clio Coder was told no.",
|
|
488
|
+
cancelled: "Withdrawn when the turn was cancelled. Clio Coder was not told no.",
|
|
489
|
+
expired: "Nobody answered. The turn was stopped; Clio Coder was not told no.",
|
|
490
|
+
};
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
The distinction between 'told no' and 'not told no' is a real difference in what the model sees and must survive the port.
|
|
494
|
+
|
|
495
|
+
</details>
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
## Live status chip: the nine-state turn state machine
|
|
500
|
+
|
|
501
|
+
- priority: must-keep
|
|
502
|
+
- from: apps/workbench/src/chat.ts:210-258 and src/Chat.tsx:154-175
|
|
503
|
+
- to: apps/clio-coder-gui/client/chat/live-status.ts
|
|
504
|
+
- value: The new GUI's entire liveness signal is the string `" · Clio Coder is working…"` appended to a status line (sessions.tsx:257). The workbench derived nine distinct states from facts it already had and, critically, carried the *exact Clio-reported title* of the running tool as the chip's detail — so during a silent run the chip is the only place the operator learns which command is running. Cheap to port, disproportionate effect on perceived speed.
|
|
505
|
+
|
|
506
|
+
<details>
|
|
507
|
+
<summary>`liveStatus`, the nine `LiveState` values, `LIVE_GLYPHS`, and the `useNow` tick</summary>
|
|
508
|
+
|
|
509
|
+
```ts
|
|
510
|
+
export type LiveState = "starting" | "thinking" | "writing" | "acting" | "waiting" | "stopping" | "done" | "failed" | "stopped";
|
|
511
|
+
export interface LiveStatus { readonly state: LiveState; readonly label: string; readonly detail: string | null; }
|
|
512
|
+
|
|
513
|
+
export function liveStatus(turn: ChatTurn, row: Turn | undefined, pending: Permission | null): LiveStatus {
|
|
514
|
+
if (row !== undefined && row.status !== "running") {
|
|
515
|
+
if (row.status === "failed") return { state: "failed", label: "Failed", detail: row.problem?.detail ?? null };
|
|
516
|
+
if (row.status === "cancelled") return { state: "stopped", label: "Stopped", detail: null };
|
|
517
|
+
return { state: "done", label: "Complete", detail: null };
|
|
518
|
+
}
|
|
519
|
+
if (turn.origin === "replay") return { state: "done", label: "Earlier record", detail: null };
|
|
520
|
+
if (pending !== null && pending.turnId === turn.turnId) {
|
|
521
|
+
return { state: "waiting", label: "Waiting for your approval", detail: pending.title };
|
|
522
|
+
}
|
|
523
|
+
const last = turn.items.at(-1);
|
|
524
|
+
if (last === undefined || last.kind === "user") return { state: "starting", label: "Starting", detail: null };
|
|
525
|
+
if (last.kind === "tool" && last.status === "in_progress") return { state: "acting", label: "Running", detail: last.title ?? null };
|
|
526
|
+
if (last.kind === "thought") return { state: "thinking", label: "Thinking", detail: null };
|
|
527
|
+
if (last.kind === "text") return { state: "writing", label: "Writing", detail: null };
|
|
528
|
+
if (last.kind === "notice") return { state: "waiting", label: "Waiting for your approval", detail: last.title ?? null };
|
|
529
|
+
return { state: "acting", label: "Working", detail: null };
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
The workbench also had a `cancelling` phase from a session-level `WireClioPhase`. The new contract has no such phase, so drive `stopping` from the cancel mutation's own `isPending` in `CancelTurn` and pass it in as an override.
|
|
534
|
+
|
|
535
|
+
Glyph table (`Chat.tsx:154-164`), single-codepoint so it never reflows:
|
|
536
|
+
```ts
|
|
537
|
+
export const LIVE_GLYPHS: Readonly<Record<LiveState, string>> = {
|
|
538
|
+
starting: "◌", thinking: "◔", writing: "◑", acting: "◐",
|
|
539
|
+
waiting: "!", stopping: "–", done: "✓", failed: "✕", stopped: "–",
|
|
540
|
+
};
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
**Shared clock implementation.** One `useNow(active)` hook for the whole view, ticking at 1000 ms and only while `activeTurn !== null || pendingPermission !== null`, so the banner, the tool rows and the outcome footer can never disagree (`App.tsx:224-233`):
|
|
544
|
+
```ts
|
|
545
|
+
function useNow(active: boolean): number {
|
|
546
|
+
const [now, setNow] = useState(() => Date.now());
|
|
547
|
+
useEffect(() => {
|
|
548
|
+
if (!active) return;
|
|
549
|
+
setNow(Date.now());
|
|
550
|
+
const timer = setInterval(() => setNow(Date.now()), 1000);
|
|
551
|
+
return () => clearInterval(timer);
|
|
552
|
+
}, [active]);
|
|
553
|
+
return now;
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
Use `clock.now()` from `client/api/clock.ts` instead of `Date.now()` so the server-clock offset applies. And remember to pass `nowMs={turn.settled ? 0 : nowMs}` per the groupTurns artifact.
|
|
557
|
+
|
|
558
|
+
</details>
|
|
559
|
+
|
|
560
|
+
Chip markup (`Chat.tsx:166-175`): `role="status"`, glyph `aria-hidden`, label, optional detail, optional elapsed. Elapsed is only passed while the turn is live. Place it in the response header next to `Clio Coder`, not in a global status bar — it belongs to the turn.
|
|
561
|
+
|
|
562
|
+
The empty-response placeholder (`Chat.tsx:392`): while a turn is live and has produced no segments yet, render `<p className="chat-response__placeholder">{status.label}…</p>` so the response block is never a blank box.
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
566
|
+
## Chat turn layout: request, response, reasoning disclosure, outcome footer
|
|
567
|
+
|
|
568
|
+
- priority: must-keep
|
|
569
|
+
- from: apps/workbench/src/Chat.tsx:283-437
|
|
570
|
+
- to: apps/clio-coder-gui/client/chat/ChatTurn.tsx
|
|
571
|
+
- value: The actual DOM shape of the conversation and the interleaving rules for text/thinking/tools/notices. Includes the reasoning disclosure (thinking is collapsed behind a one-line preview, never inline prose) and the outcome footer that carries tool count and token usage per turn rather than only for the last turn as the new GUI does at sessions.tsx:276.
|
|
572
|
+
|
|
573
|
+
<details>
|
|
574
|
+
<summary>The turn component, its settled-segment rule, and the reasoning preview</summary>
|
|
575
|
+
|
|
576
|
+
```tsx
|
|
577
|
+
<article className={`chat-turn${live ? " is-live" : " is-settled"}${turn.origin === "replay" ? " is-replay" : ""}`}
|
|
578
|
+
data-turn-id={turn.turnId}
|
|
579
|
+
aria-label={turn.request === null ? "Turn" : `Request: ${turn.request.text.slice(0, 80)}`}>
|
|
580
|
+
{turn.request !== null && (
|
|
581
|
+
<div className="chat-request">
|
|
582
|
+
<div className="chat-request__meta">
|
|
583
|
+
<span className="chat-request__who">You</span>
|
|
584
|
+
{turn.origin === "replay" && <span className="chat-request__replay">earlier record</span>}
|
|
585
|
+
<time dateTime={startedAt}>{formatClock(startedAt)}</time>
|
|
586
|
+
</div>
|
|
587
|
+
<p className="chat-request__prompt">{turn.request.text}</p>
|
|
588
|
+
</div>
|
|
589
|
+
)}
|
|
590
|
+
<div className="chat-response">
|
|
591
|
+
<div className="chat-response__meta">
|
|
592
|
+
<span className="chat-response__who">Clio Coder</span>
|
|
593
|
+
<LiveChip status={status} elapsed={live ? elapsed : null} />
|
|
594
|
+
</div>
|
|
595
|
+
<div className="chat-response__body">{/* segments */}</div>
|
|
596
|
+
{row !== undefined && row.status !== "running" && <TurnOutcome turn={row} toolCount={toolCount} />}
|
|
597
|
+
</div>
|
|
598
|
+
</article>
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
**The user prompt is rendered as plain text, not Markdown.** A `<p>` with `white-space: pre-wrap`. Deliberate: the operator's own text must never be reinterpreted.
|
|
602
|
+
|
|
603
|
+
**Segment dispatch** with the streaming-tail rule (`Chat.tsx:365-391`) — only the LAST segment of a LIVE turn is treated as unsettled:
|
|
604
|
+
```tsx
|
|
605
|
+
turn.segments.map((segment, index) => {
|
|
606
|
+
const settledSegment = !live || index < turn.segments.length - 1;
|
|
607
|
+
switch (segment.kind) {
|
|
608
|
+
case "response":
|
|
609
|
+
return <MarkdownContent key={segment.item.id} source={segment.item.text}
|
|
610
|
+
complete={settledSegment} deferDiagrams={live} />;
|
|
611
|
+
case "reasoning": return <ReasoningDisclosure key={segment.item.id} item={segment.item} />;
|
|
612
|
+
case "activity": return <ActivityGroup key={segment.items[0]?.id ?? index} items={segment.items} settled={settledSegment} … />;
|
|
613
|
+
}
|
|
614
|
+
})
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
**`deferDiagrams={live}` is currently never passed** — `sessions.tsx:180` calls `<MarkdownContent source complete>` with no third prop. The renderer supports it (`client/render/Markdown.tsx:566`) and it matters: Mermaid lays a diagram out in one synchronous main-thread task (76 ms measured for a seven-node flowchart), so a diagram inside a completed narrative must still wait for the whole *turn* to settle, because Clio finishes a narrative before every tool burst and per-item completion is not a quiet moment.
|
|
618
|
+
|
|
619
|
+
**Reasoning disclosure** (`Chat.tsx:283-295`) — thinking is never inline prose:
|
|
620
|
+
```tsx
|
|
621
|
+
const preview = item.text.trim().split("\n", 1)[0] ?? "";
|
|
622
|
+
<details className="reasoning">
|
|
623
|
+
<summary className="reasoning__summary">
|
|
624
|
+
<span className="reasoning__label">Reasoning</span>
|
|
625
|
+
<span className="reasoning__preview">{preview.length > 140 ? `${preview.slice(0, 140)}…` : preview}</span>
|
|
626
|
+
<span className="reasoning__source">reported by Clio Coder</span>
|
|
627
|
+
</summary>
|
|
628
|
+
<p className="reasoning__body">{item.text}</p>
|
|
629
|
+
</details>
|
|
630
|
+
```
|
|
631
|
+
Closed by default, always. The 140-character first-line preview is the whole affordance.
|
|
632
|
+
|
|
633
|
+
**Outcome footer** (`Chat.tsx:305-323`), driven off the `Turn` row:
|
|
634
|
+
```tsx
|
|
635
|
+
<footer className={`turn-outcome${failed ? " is-failed" : stopped ? " is-stopped" : ""}`}>
|
|
636
|
+
<span className="turn-outcome__glyph" aria-hidden="true">{failed ? "✕" : stopped ? "–" : "✓"}</span>
|
|
637
|
+
<span className="turn-outcome__label">{failed ? "Turn failed" : stopped ? "Turn stopped" : "Turn complete"}</span>
|
|
638
|
+
{(failed || stopped) && row.problem !== null && <span className="turn-outcome__detail">{row.problem.detail}</span>}
|
|
639
|
+
{failed && <code className="turn-outcome__code">{row.stopReason}</code>}
|
|
640
|
+
{toolCount > 0 && <span className="turn-outcome__fact">{toolCount} tool {toolCount === 1 ? "call" : "calls"}</span>}
|
|
641
|
+
{row.usage !== null && <span className="turn-outcome__fact" title={usageTitle(row.usage)}>tokens {usageSummary(row.usage)}</span>}
|
|
642
|
+
{row.finishedAt !== null && <time dateTime={row.finishedAt}>{formatClock(row.finishedAt)}</time>}
|
|
643
|
+
</footer>
|
|
644
|
+
```
|
|
645
|
+
with
|
|
646
|
+
```ts
|
|
647
|
+
const usageSummary = (u: Usage) => `${u.input.toLocaleString()} in · ${u.output.toLocaleString()} out`;
|
|
648
|
+
const usageTitle = (u: Usage) => `input ${u.input} · output ${u.output} · cache read ${u.cacheRead} · cache write ${u.cacheWrite} · reasoning ${u.reasoning}`;
|
|
649
|
+
```
|
|
650
|
+
Two numbers visible, five in the tooltip. The current GUI shows all five in a wall of text under the transcript, for the last turn only.
|
|
651
|
+
|
|
652
|
+
</details>
|
|
653
|
+
|
|
654
|
+
**Truncation note** (`Chat.tsx:423`), verbatim: `"Earlier turns are not shown; Clio Coder still has the full context."` — the current wording at sessions.tsx:263 says content "was omitted from this view to keep it bounded", which reads like data loss.
|
|
655
|
+
|
|
656
|
+
**Empty transcript** (`App.tsx:5966-5992`): a reticle glyph `◎`, eyebrow `NEW RESEARCH THREAD`, heading `What would you like to understand or change?`, and three starter prompts that fill the composer and focus it:
|
|
657
|
+
```ts
|
|
658
|
+
const STARTER_PROMPTS = [
|
|
659
|
+
"Map this project and explain how its parts fit together.",
|
|
660
|
+
"Run the existing checks and summarize what the evidence shows.",
|
|
661
|
+
"Help me plan a careful change without editing anything yet.",
|
|
662
|
+
] as const;
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
---
|
|
666
|
+
|
|
667
|
+
## useFollowLatest: autoscroll that does not fight the operator
|
|
668
|
+
|
|
669
|
+
> Already shipped. Landed as `apps/clio-coder-gui/client/render/follow-latest.ts` with `FollowLatest.tsx` and `follow-latest.css`. No page mounts it yet.
|
|
670
|
+
|
|
671
|
+
- priority: must-keep
|
|
672
|
+
- from: apps/workbench/src/Chat.tsx:439-593
|
|
673
|
+
- to: apps/clio-coder-gui/client/chat/use-follow-latest.ts
|
|
674
|
+
- value: Absent from the new GUI. The hard part is not pinning to the bottom; it is distinguishing a scroll event caused by the hook's own write from one caused by the operator, so that content growth landing between a write and its event never reads as a scroll-away. The workbench solved this with a programmatic-top watermark plus a settling window for smooth scrolls, and used a ResizeObserver so no layout is read on the token path. The measured result: the transcript stayed put through the rest of a fast stream after a manual scroll, in every run.
|
|
675
|
+
|
|
676
|
+
Constants: `BOTTOM_TOLERANCE_PX = 32`, `SETTLE_TIMEOUT_MS = 1_200`.
|
|
677
|
+
|
|
678
|
+
<details>
|
|
679
|
+
<summary>`useFollowLatest`, the `following`/`unseen` state, and the jump button</summary>
|
|
680
|
+
|
|
681
|
+
```ts
|
|
682
|
+
export interface ScrollPosition { readonly top: number; readonly following: boolean; }
|
|
683
|
+
export interface FollowLatest {
|
|
684
|
+
readonly following: boolean;
|
|
685
|
+
/** Timeline activity arrived while the operator was scrolled away. */
|
|
686
|
+
readonly unseen: boolean;
|
|
687
|
+
jumpToLatest(): void;
|
|
688
|
+
snapshot(): ScrollPosition;
|
|
689
|
+
restore(position: ScrollPosition): void;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
export function useFollowLatest(scrollRef: RefObject<HTMLElement | null>, enabled: boolean, activityKey: unknown): FollowLatest {
|
|
693
|
+
const [following, setFollowing] = useState(true);
|
|
694
|
+
const [unseen, setUnseen] = useState(false);
|
|
695
|
+
const followingRef = useRef(true);
|
|
696
|
+
const programmaticTop = useRef(0);
|
|
697
|
+
const settling = useRef<{ lastTop: number; startedAt: number } | null>(null);
|
|
698
|
+
|
|
699
|
+
const setFollow = useCallback((next: boolean) => {
|
|
700
|
+
followingRef.current = next;
|
|
701
|
+
setFollowing((current) => (current === next ? current : next));
|
|
702
|
+
if (next) setUnseen(false);
|
|
703
|
+
}, []);
|
|
704
|
+
|
|
705
|
+
// Activity, not layout growth, is what marks something as unseen.
|
|
706
|
+
useEffect(() => { if (!followingRef.current) setUnseen(true); }, [activityKey]);
|
|
707
|
+
|
|
708
|
+
useEffect(() => {
|
|
709
|
+
const element = scrollRef.current;
|
|
710
|
+
if (element === null || !enabled) return;
|
|
711
|
+
const atBottom = () => element.scrollHeight - element.scrollTop - element.clientHeight <= 32;
|
|
712
|
+
const pin = () => { element.scrollTop = element.scrollHeight; programmaticTop.current = element.scrollTop; };
|
|
713
|
+
const onScroll = () => {
|
|
714
|
+
const top = element.scrollTop;
|
|
715
|
+
const settle = settling.current;
|
|
716
|
+
if (settle !== null) {
|
|
717
|
+
const movedUp = top < settle.lastTop - 1;
|
|
718
|
+
const expired = Date.now() - settle.startedAt > 1200;
|
|
719
|
+
if (atBottom()) { settling.current = null; programmaticTop.current = top; setFollow(true); return; }
|
|
720
|
+
if (!movedUp && !expired) { settle.lastTop = top; return; }
|
|
721
|
+
settling.current = null;
|
|
722
|
+
}
|
|
723
|
+
if (atBottom()) { programmaticTop.current = top; setFollow(true); return; }
|
|
724
|
+
if (top < programmaticTop.current - 1 || !followingRef.current) setFollow(false);
|
|
725
|
+
};
|
|
726
|
+
element.addEventListener("scroll", onScroll, { passive: true });
|
|
727
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => { if (followingRef.current) pin(); });
|
|
728
|
+
// The transcript element is replaced when a session or view changes, so the
|
|
729
|
+
// size observation follows whichever child is current.
|
|
730
|
+
let observed: Element | null = null;
|
|
731
|
+
const observeContent = () => {
|
|
732
|
+
const content = element.firstElementChild;
|
|
733
|
+
if (content === observed) return;
|
|
734
|
+
if (observed !== null) observer?.unobserve(observed);
|
|
735
|
+
observed = content;
|
|
736
|
+
if (content !== null) observer?.observe(content);
|
|
737
|
+
};
|
|
738
|
+
observeContent();
|
|
739
|
+
const children = typeof MutationObserver === "undefined" ? null : new MutationObserver(observeContent);
|
|
740
|
+
children?.observe(element, { childList: true });
|
|
741
|
+
return () => { element.removeEventListener("scroll", onScroll); observer?.disconnect(); children?.disconnect(); };
|
|
742
|
+
}, [scrollRef, enabled, setFollow]);
|
|
743
|
+
|
|
744
|
+
const snapshot = useCallback((): ScrollPosition => ({ top: scrollRef.current?.scrollTop ?? 0, following: followingRef.current }), [scrollRef]);
|
|
745
|
+
|
|
746
|
+
const restore = useCallback((position: ScrollPosition) => {
|
|
747
|
+
const element = scrollRef.current;
|
|
748
|
+
if (element === null) return;
|
|
749
|
+
settling.current = null;
|
|
750
|
+
setFollow(position.following);
|
|
751
|
+
element.scrollTop = position.following ? element.scrollHeight : position.top;
|
|
752
|
+
programmaticTop.current = element.scrollTop;
|
|
753
|
+
}, [scrollRef, setFollow]);
|
|
754
|
+
|
|
755
|
+
const jumpToLatest = useCallback(() => {
|
|
756
|
+
const element = scrollRef.current;
|
|
757
|
+
if (element === null) return;
|
|
758
|
+
const reduced = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
759
|
+
setFollow(true);
|
|
760
|
+
if (reduced) { element.scrollTop = element.scrollHeight; programmaticTop.current = element.scrollTop; return; }
|
|
761
|
+
settling.current = { lastTop: element.scrollTop, startedAt: Date.now() };
|
|
762
|
+
element.scrollTo({ top: element.scrollHeight, behavior: "smooth" });
|
|
763
|
+
}, [scrollRef, setFollow]);
|
|
764
|
+
|
|
765
|
+
return { following, unseen, jumpToLatest, snapshot, restore };
|
|
766
|
+
}
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
Pass `activityKey = snapshot.timeline` (the array identity changes on every delta).
|
|
770
|
+
|
|
771
|
+
Companion affordance:
|
|
772
|
+
```tsx
|
|
773
|
+
export function JumpToLatest({ follow }: { follow: FollowLatest }) {
|
|
774
|
+
if (follow.following) return null;
|
|
775
|
+
return (
|
|
776
|
+
<button type="button" className={`jump-to-latest${follow.unseen ? " has-unseen" : ""}`} onClick={follow.jumpToLatest}>
|
|
777
|
+
<span aria-hidden="true">↓</span>
|
|
778
|
+
{follow.unseen ? "New activity below" : "Jump to latest"}
|
|
779
|
+
</button>
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
</details>
|
|
785
|
+
|
|
786
|
+
**Per-route scroll memory** (`App.tsx:5919-5933`): keep a `Map<string, ScrollPosition>` keyed by route, snapshot on navigate away, and `restore()` in a `useLayoutEffect` on arrival. Restoring through the hook is what keeps the scroll event caused by swapping content from reading as the operator's — a transcript left scrolled up stays there even after a short page clamped the region to the top in between.
|
|
787
|
+
|
|
788
|
+
**On send**, call `jumpToLatest()` — the operator asking a question is consent to follow the answer.
|
|
789
|
+
|
|
790
|
+
---
|
|
791
|
+
|
|
792
|
+
## Frame-coalesced event delivery and the 120 Hz path
|
|
793
|
+
|
|
794
|
+
> Already shipped. Landed as `apps/clio-coder-gui/client/api/frame-buffer.ts`, wired through `client/api/events.ts`. `scripts/perf-workload.ts` is still unported.
|
|
795
|
+
|
|
796
|
+
- priority: must-keep
|
|
797
|
+
- from: apps/workbench/src/event-buffer.ts (whole file) + apps/workbench/PERFORMANCE.md
|
|
798
|
+
- to: apps/clio-coder-gui/client/api/frame-buffer.ts + docs/gui-performance.md
|
|
799
|
+
- value: Directly serves the 120 fps ask. The new GUI applies every SSE delta synchronously in the EventSource handler (client/api/events.ts:22-30) and calls setQueryData per event, so a 1,358-event turn is 1,358 React renders. The workbench coalesced high-frequency text into one batch per animation frame while keeping every control event immediate, and the measured result is in PERFORMANCE.md: frame p95 16.7-16.8 ms, keystroke-to-input p95 2-3 ms, zero frames over 33 ms during streaming in the quiet runs. That is the HPC feel the product is claiming.
|
|
800
|
+
|
|
801
|
+
<details>
|
|
802
|
+
<summary>`FrameEventBuffer`, the injectable `FrameClock`, and the 128-event batch cap</summary>
|
|
803
|
+
|
|
804
|
+
```ts
|
|
805
|
+
import type { Event } from "../../contracts/events.js";
|
|
806
|
+
|
|
807
|
+
export const MAX_FRAME_EVENT_BATCH = 128;
|
|
808
|
+
|
|
809
|
+
export interface FrameClock { request(cb: FrameRequestCallback): number; cancel(handle: number): void; }
|
|
810
|
+
const browserFrameClock: FrameClock = { request: requestAnimationFrame, cancel: cancelAnimationFrame };
|
|
811
|
+
|
|
812
|
+
/** Only text and thought deltas may wait for the next paint. */
|
|
813
|
+
function mayWaitForPaint(event: Event): boolean {
|
|
814
|
+
return event.type === "turn.text" || event.type === "turn.thought";
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
export class FrameEventBuffer {
|
|
818
|
+
readonly #deliver: (events: readonly Event[]) => void;
|
|
819
|
+
readonly #clock: FrameClock;
|
|
820
|
+
readonly #maximumBatch: number;
|
|
821
|
+
#pending: Event[] = [];
|
|
822
|
+
#frameHandle: number | null = null;
|
|
823
|
+
#closed = false;
|
|
824
|
+
|
|
825
|
+
constructor(deliver: (events: readonly Event[]) => void, clock: FrameClock = browserFrameClock, maximumBatch = MAX_FRAME_EVENT_BATCH) {
|
|
826
|
+
if (!Number.isSafeInteger(maximumBatch) || maximumBatch < 1) throw new Error("Frame event batch size must be a positive safe integer.");
|
|
827
|
+
this.#deliver = deliver; this.#clock = clock; this.#maximumBatch = maximumBatch;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
push(event: Event): void {
|
|
831
|
+
if (this.#closed) return;
|
|
832
|
+
this.#pending.push(event);
|
|
833
|
+
// Tool, permission, fleet, terminal and error events remain immediate. Any
|
|
834
|
+
// text already waiting precedes them in this same delivery.
|
|
835
|
+
if (!mayWaitForPaint(event) || this.#pending.length >= this.#maximumBatch) { this.flush(); return; }
|
|
836
|
+
if (this.#frameHandle !== null) return;
|
|
837
|
+
this.#frameHandle = this.#clock.request(() => { this.#frameHandle = null; this.#deliverPending(); });
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
flush(): void { if (this.#closed) return; this.#cancelFrame(); this.#deliverPending(); }
|
|
841
|
+
close(): void { if (this.#closed) return; this.#closed = true; this.#cancelFrame(); this.#pending = []; }
|
|
842
|
+
#cancelFrame(): void { if (this.#frameHandle === null) return; this.#clock.cancel(this.#frameHandle); this.#frameHandle = null; }
|
|
843
|
+
#deliverPending(): void { if (this.#pending.length === 0) return; const events = this.#pending; this.#pending = []; this.#deliver(events); }
|
|
844
|
+
}
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
**Wiring into `subscribe()`** (`client/api/events.ts`): construct one buffer per subscription; `receive` pushes into it; the deliver callback runs the *entire batch* through `SessionBuffer.event()` and calls `queries.setQueryData` **once**, with the final snapshot. Return `() => { buffer.close(); stream.close(); }`. Ordering is preserved because a control event flushes any pending text before itself — this invariant is why the buffer is safe, and it must be kept under test.
|
|
848
|
+
|
|
849
|
+
</details>
|
|
850
|
+
|
|
851
|
+
**Why this gives 120 Hz and not just 60 Hz:** `requestAnimationFrame` fires at the display's refresh rate. At 120 Hz the buffer simply delivers twice as often with half the batch, so the per-frame cost halves rather than the frame rate. The work that must NOT be on this path is layout reads and synchronous parsing — which is why the follow-latest hook uses a ResizeObserver rather than reading `scrollHeight` per token, and why the incremental Markdown lexer re-lexes only the tail after the last block boundary.
|
|
852
|
+
|
|
853
|
+
**Budget to hold, from PERFORMANCE.md** (headless Chrome 151, 24 CPUs, WSL2, 60 Hz compositor, ~16 KB of Markdown per turn streamed in 5-char chunks, 1,358 `turn.text` events/turn, tool bursts between blocks, 64 keystrokes typed during the stream):
|
|
854
|
+
|
|
855
|
+
| Metric | Target |
|
|
856
|
+
| --- | --- |
|
|
857
|
+
| Frame p95 during stream | ≤ 16.8 ms (≤ 8.3 ms at 120 Hz) |
|
|
858
|
+
| Tasks > 50 ms during stream | 0 |
|
|
859
|
+
| Keystroke → `input` p95 | ≤ 3 ms |
|
|
860
|
+
| Keystroke → next frame p95 | ≤ 19 ms |
|
|
861
|
+
| Event → paint p50 / p95 | ≤ 27 / 35 ms |
|
|
862
|
+
| DOM nodes after 2 turns | ≤ 2,100 |
|
|
863
|
+
| Heap peak | ≤ 20 MB |
|
|
864
|
+
| 64-turn session resume | ≤ 175 ms, ≤ 700 nodes |
|
|
865
|
+
| Draft survives the turn | always |
|
|
866
|
+
| Manual scroll held through stream | always |
|
|
867
|
+
|
|
868
|
+
**Known remaining long task:** Mermaid diagram layout, ~49-79 ms, deliberately moved to *after* the stream settles by the `deferDiagrams` mechanism, rendering one diagram at a time with a macrotask between them. Do not regress this by rendering diagrams inline during a turn.
|
|
869
|
+
|
|
870
|
+
**Explicitly not measured** (carry into the doc so nobody claims it): real 120 Hz or 144 Hz displays, any GPU-composited path, long code fences that never close during a stream (the tail re-lex grows with the unsettled block), diagrams larger than a seven-node flowchart, an unloaded machine, and real Clio output rather than a fixture.
|
|
871
|
+
|
|
872
|
+
The workbench's harness (`scripts/perf-workload.ts`, 510 lines) is worth reimplementing against the new stack with playwright-core, which is already a devDependency; it drove headless Chrome at 1600×1100, paced the fixture at 4 ms, captured a Chrome trace, and emitted the JSON these tables were generated from.
|
|
873
|
+
|
|
874
|
+
---
|
|
875
|
+
|
|
876
|
+
## Stream-run splitting and bounded text accumulation
|
|
877
|
+
|
|
878
|
+
- priority: must-keep
|
|
879
|
+
- from: apps/workbench/src/timeline.ts:99-165, 275-293
|
|
880
|
+
- to: apps/clio-coder-gui/contracts/session-projection.ts (amendment)
|
|
881
|
+
- value: A correctness bug the new GUI inherits from a simplification. `applySessionDelta` keys a text run on `${turnId}:${kind}${identity}` — one bucket per (turn, kind, agent) for the whole turn. So a turn that writes prose, runs three tools, then writes more prose appends the second paragraph onto the first, and the tool group ends up rendered AFTER both paragraphs instead of between them. The workbench keyed a run on whether the immediately-preceding timeline item was the same run, which is what produces correct interleaving.
|
|
882
|
+
|
|
883
|
+
**The rule** (`timeline.ts:275-293`): a text or thought delta continues the previous run only if the LAST item in the timeline is already that run; otherwise it starts a new run with a fresh ordinal.
|
|
884
|
+
|
|
885
|
+
<details>
|
|
886
|
+
<summary>How a stream run is keyed and split, and the safety card's summary line</summary>
|
|
887
|
+
|
|
888
|
+
```ts
|
|
889
|
+
case "turn.text":
|
|
890
|
+
case "turn.thought": {
|
|
891
|
+
const streamKind = event.type === "turn.thought" ? "thought" : "text";
|
|
892
|
+
const prefix = `${turnId}:${streamKind}${identity}:`;
|
|
893
|
+
const prior = state.timeline.at(-1);
|
|
894
|
+
const streamId = prior?.id.startsWith(prefix) === true ? prior.id : `${prefix}${nextOrdinal}`;
|
|
895
|
+
upsert({ id: streamId, … }, /* append */ true);
|
|
896
|
+
break;
|
|
897
|
+
}
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
Keep the agent identity in the prefix (the new GUI's `identity` suffix is a genuine improvement — a worker's prose must not merge into the orchestrator's), but move it before the ordinal so the prefix test still works.
|
|
901
|
+
|
|
902
|
+
**Loop/safety card summary string:**
|
|
903
|
+
```
|
|
904
|
+
Repeated ${repeatCount} times; ${blocksThisTurn} of ${budget} blocks used this turn (${disposition}).${interrupted ? " Clio Coder interrupted the turn." : ""}
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
</details>
|
|
908
|
+
|
|
909
|
+
**Bounded append** — the workbench's version, which the new GUI already has as `boundedText` but applies with a different marker and limit. Reconcile on one:
|
|
910
|
+
- `MAX_TIMELINE_STREAM_BYTES = 64 * 1024` per run (the new GUI uses 65536 — same number, keep it);
|
|
911
|
+
- marker `"\n[… stream truncated by GUI …]"` — the new GUI's `"\n[… stream truncated …]"` loses the attribution; prefer the explicit one so the operator knows it was the GUI and not Clio;
|
|
912
|
+
- once a run ends with the marker, further appends are dropped entirely rather than re-bounded (`timeline.ts:118-121`). The new GUI already does this correctly.
|
|
913
|
+
- Truncation is UTF-8-byte-exact and code-point-aligned: iterate `for (const character of value)`, accumulate `encoder.encode(character).byteLength`, break before exceeding `limit - markerBytes`. Never slice by JS string index — that splits surrogate pairs.
|
|
914
|
+
|
|
915
|
+
**Ordinal minting.** The workbench kept a monotonic `ordinal` that is the max `sequence` in the timeline, so new items take `ordinal + 1` and ids never collide after the timeline is bounded. The new GUI's `upsert` computes `(state.timeline.at(-1)?.sequence ?? 0) + 1`, which regresses after the head is dropped by the byte bound — two runs can then collide on the same id. Use `state.timeline.reduce((m, i) => Math.max(m, i.sequence), 0) + 1`, or better, carry the running maximum on the snapshot.
|
|
916
|
+
|
|
917
|
+
**Loop/safety cards.** The workbench minted a first-class timeline item for `safety.loopBlocked` (`timeline.ts:325-342`) with the title `Clio Coder blocked a repeated ${tool} call`. The new GUI routes `fleet.loopBlocked` into the fleet strip as a JSON blob (`session-controls.tsx:281`), where it is invisible in the conversation. It belongs in the activity group as a `notice` item with `kind: "safety"`, because it is the one thing that explains why a turn stopped making progress. `FleetPayloads["fleet.loopBlocked"]` already carries every field the sentence needs.
|
|
918
|
+
|
|
919
|
+
---
|
|
920
|
+
|
|
921
|
+
## Message-level actions and composer behavior
|
|
922
|
+
|
|
923
|
+
- priority: should-keep
|
|
924
|
+
- from: apps/workbench/src/App.tsx:5750-5828 (PromptEditor), src/Markdown.tsx:83-128 (CopyButton), src/markdown.ts:206-216 (tokenText)
|
|
925
|
+
- to: apps/clio-coder-gui/client/chat/message-actions.tsx
|
|
926
|
+
- value: Records honestly what existed (little) and specifies what should. The workbench had only per-code-block copy; there was no copy-message, retry, branch, or per-message collapse. The composer, by contrast, had three behaviors worth keeping that the new one lacks: Ctrl/Cmd+Enter to send, a Stop button that replaces Send in place while a turn runs, and a draft that provably survives a full streaming turn.
|
|
927
|
+
|
|
928
|
+
**What existed.** `CopyButton` (already ported to `client/render/Markdown.tsx:107`) with a `navigator.clipboard.writeText` primary path, a hidden-textarea + `document.execCommand("copy")` fallback, and a 1,600 ms `Copied` / `Copy failed` state that clears itself. Every fenced code block and every Mermaid source carries one. `tokenText(tokens)` flattens a marked token tree to plain text and exists precisely to give a copy action and an accessible name the same string.
|
|
929
|
+
|
|
930
|
+
**What to add, in priority order.**
|
|
931
|
+
|
|
932
|
+
1. **Copy response.** A quiet button in the `chat-response__meta` row, visible on hover and always visible on focus. Copies the concatenation of the turn's `response` segment texts joined by `"\n\n"` — the raw Markdown, not the rendered text, because the operator is almost always pasting into another Markdown surface. Reuse `CopyButton`.
|
|
933
|
+
2. **Copy request.** Same, on `chat-request__meta`, copying `turn.request.text` verbatim.
|
|
934
|
+
3. **Retry.** On a turn whose `Turn.status` is `failed` or `cancelled` only. Fills the composer with `turn.request.text` and focuses it — it does NOT auto-send. Label `Try again`. Rationale: a failed turn usually needs the prompt edited, and auto-sending an identical prompt into a failing route is the loop the safety system exists to break.
|
|
935
|
+
4. **Collapse turn.** A chevron on `chat-turn` that collapses the whole response to its outcome footer, defaulting open. Per-turn local state, not persisted. Useful on a long session; do not over-invest.
|
|
936
|
+
5. **Branch — do not build for v0.5.0.** There is no wire affordance: `routes.turn` posts into the session's single linear timeline and `SessionSnapshot` has no branch identity. It would need a new ACP concept (fork a session at a turn), which is a larger change than the MVP carries. Record it as deliberately deferred.
|
|
937
|
+
|
|
938
|
+
**Composer** (`App.tsx:5750-5828`), replacing `client/pages/sessions.tsx`'s form:
|
|
939
|
+
- `Ctrl`/`Cmd`+`Enter` submits (`matchesKeybinding(KEYBINDINGS.send, event)` then `event.currentTarget.form?.requestSubmit()`), with the hint `Ctrl or Cmd + Enter sends` in the footer. Plain `Enter` inserts a newline.
|
|
940
|
+
- While a turn is running, the submit button is **replaced in place** by a `Stop` button wired to `routes.cancelTurn` — same grid cell, same size, no layout shift. The current GUI disables Send and puts Cancel in a separate control strip above the transcript.
|
|
941
|
+
- The textarea is **never disabled while a turn runs** — only the submit is. The operator drafts the next prompt while Clio works; the perf harness asserts the draft survives the turn. The current code sets `disabled={busy || snapshot.state !== "open"}`, which throws the draft away and blurs focus mid-thought. Change to `disabled={snapshot.state !== "open"}`.
|
|
942
|
+
- Footer notice when the session is occupied but no turn id is available yet: `"Clio Coder is finishing the previous prompt."`
|
|
943
|
+
- Footer privacy line: `"Prompts go only to the Clio Coder target you configured."`
|
|
944
|
+
- Placeholder: `"Ask Clio Coder to do something in this project"`, or `"Open a project first"` when there is no workspace.
|
|
945
|
+
|
|
946
|
+
<details>
|
|
947
|
+
<summary>The `KEYBINDINGS` registry and the `Keybinding` shape behind the help sheet</summary>
|
|
948
|
+
|
|
949
|
+
**Keybinding registry** (`apps/workbench/src/help-reference.ts:27`). Keep the pattern: one exported table, handlers match against entries rather than literals, and the in-app help reference is generated from the same table so it cannot drift.
|
|
950
|
+
|
|
951
|
+
```ts
|
|
952
|
+
export interface Keybinding { readonly id: string; readonly key: string; readonly modifiers: readonly ("primary" | "alt" | "shift")[]; readonly action: string; readonly where: string; }
|
|
953
|
+
export const KEYBINDINGS = {
|
|
954
|
+
send: { id: "send", key: "Enter", modifiers: ["primary"], action: "Send the request in the composer", where: "While the composer has focus" },
|
|
955
|
+
allowOnce: { id: "allowOnce", key: "a", modifiers: ["alt"], action: "Allow the pending approval once", where: "Anywhere, while an approval is waiting and no dialog is open" },
|
|
956
|
+
reject: { id: "reject", key: "r", modifiers: ["alt"], action: "Reject the pending approval", where: "Anywhere, while an approval is waiting and no dialog is open" },
|
|
957
|
+
escape: { id: "escape", key: "Escape", modifiers: [], action: "Close the open dialog or drawer", where: "While a dialog or a drawer is open" },
|
|
958
|
+
} as const;
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
`primary` is Ctrl on Linux/Windows and Cmd on macOS; single-character keys compare case-insensitively.
|
|
962
|
+
|
|
963
|
+
</details>
|
|
964
|
+
|
|
965
|
+
---
|
|
966
|
+
|
|
967
|
+
## Fleet strip: dispatch runs in the conversation
|
|
968
|
+
|
|
969
|
+
- priority: should-keep
|
|
970
|
+
- from: apps/workbench/src/Chat.tsx:79-152
|
|
971
|
+
- to: apps/clio-coder-gui/client/chat/FleetStrip.tsx
|
|
972
|
+
- value: The new GUI's FleetStrip (session-controls.tsx:270-286) prints `fleet.enqueued` and a JSON payload per row. The workbench folded every dispatch event into ONE row per run, keyed on runId, showing the agent, state, task preview, node, progress count, outcome and duration — a live fleet view inside the chat. It also carried a Running-only filter that is off by default, with the reasoning recorded: a settled row must never disappear unannounced.
|
|
973
|
+
|
|
974
|
+
<details>
|
|
975
|
+
<summary>`FleetRun`, the five run states, their glyphs, and the folding rule</summary>
|
|
976
|
+
|
|
977
|
+
**Fold events into runs.** `SessionSnapshot.fleet` is an append-only list of `FleetItem { id, at, sourceSequence, fact }`. Reduce it to one row per `runId`:
|
|
978
|
+
|
|
979
|
+
```ts
|
|
980
|
+
export type FleetRunState = "queued" | "running" | "progress" | "done" | "failed";
|
|
981
|
+
export interface FleetRun {
|
|
982
|
+
readonly runId: string; readonly agentId: string; readonly state: FleetRunState;
|
|
983
|
+
readonly taskPreview: string | null; readonly node: string | null; readonly attempt: number | null;
|
|
984
|
+
readonly progressCount: number; readonly progressTruncated: boolean;
|
|
985
|
+
readonly outcome: string | null; readonly durationMs: number | null; readonly tokenCount: number | null;
|
|
986
|
+
readonly updatedAt: string;
|
|
987
|
+
}
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
Fold rules, by `fact.type`: `fleet.enqueued` → `queued` (sets agentId, taskPreview, node, attempt); `fleet.started` → `running`; `fleet.progress` → `progress` and sets `progressCount`/`progressTruncated` (note `progress` is deliberately NOT a phase of its own — it is a running run that has reported activity, kept distinct so the strip can show that a run is doing work rather than merely admitted); `fleet.completed` → `done` with `outcome`, `durationMs`, `tokenCount`; `fleet.failed` → `failed` with `outcome`/`reason`, `durationMs`. Cap at 64 runs, dropping the oldest **settled** one first. `evidence.ready` and `fleet.loopBlocked` are not runs — route the first to the evidence surface and the second into the activity group (see the stream-splitting artifact).
|
|
991
|
+
|
|
992
|
+
Glyph and state label mappings:
|
|
993
|
+
```ts
|
|
994
|
+
const FLEET_GLYPHS = { queued: "…", running: "◐", progress: "◑", done: "✓", failed: "✕" };
|
|
995
|
+
const FLEET_STATE_LABELS = { queued: "queued", running: "running", progress: "working", done: "done", failed: "failed" };
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
</details>
|
|
999
|
+
|
|
1000
|
+
**Presentation** (`Chat.tsx:85-152`):
|
|
1001
|
+
- Container is `<details className="activity" open={live > 0}>` where `live = runs.filter(r => r.state === "queued" || r.state === "running" || r.state === "progress").length`. Summary: glyph `⛭`, label `Fleet · ${live} running of ${runs.length}`, count badge.
|
|
1002
|
+
- Filter chip `Running only` with `aria-pressed`, **off by default**, and a `role="status"` line reading either `` `${shown} of ${total} reported ${total === 1 ? "run" : "runs"} shown` `` or `` `All ${total} reported ${total === 1 ? "run" : "runs"} shown` ``.
|
|
1003
|
+
- Empty-with-filter: `"No run is running right now. Every reported run has settled."`
|
|
1004
|
+
- Row: glyph, `agentId` as the kind column, then title column = `taskPreview ?? "No task preview was reported."` with `node ${node}` as a note beneath, then state column = state label plus, appended as `· `-separated fragments in this order: `outcome`, `${progressCount}${progressTruncated ? "+" : ""} steps` (only when `> 0`), `formatDuration(durationMs)`.
|
|
1005
|
+
- Every value shown is a reported fact. Nothing is derived and nothing is shown for a run that was never reported. `taskPreview` is Clio's own sanitized 160-byte prefix, never the exact task.
|
|
1006
|
+
|
|
1007
|
+
**Correlate with the dispatch tool call.** When a `tool` item has `title === "dispatch"`, render its matching fleet rows inline in the tool card body (match on the `runId` in `rawOutput.result.details` or, failing that, on agent + time proximity). This is the link the workbench could not make and is the single biggest readability win for orchestration sessions.
|
|
1008
|
+
|
|
1009
|
+
---
|
|
1010
|
+
|
|
1011
|
+
## Inspector presentation atlas: what each of the thirteen showed, in what order
|
|
1012
|
+
|
|
1013
|
+
- priority: must-keep
|
|
1014
|
+
- from: apps/workbench/src/App.tsx (the 13 panels) cross-referenced with apps/workbench/clio-*-inspector.ts
|
|
1015
|
+
- to: docs/gui-inspector-presentation.md
|
|
1016
|
+
- value: This is the answer to the audit question. The DATA claim is verified — every field each inspector produced has a home in contracts/*.ts. What is not reproduced is the ordering, grouping, disclosure and empty-state grammar, which is where all the design work went. One table per inspector so an implementer can rebuild a panel without the original.
|
|
1017
|
+
|
|
1018
|
+
**Verification of the audit's data claim, per inspector → new route:**
|
|
1019
|
+
|
|
1020
|
+
| Inspector | Data reproduced by | Verdict |
|
|
1021
|
+
| --- | --- | --- |
|
|
1022
|
+
| catalog (26K) | `contracts/library.ts` | yes, with agents/skills/verifiers/extensions collapsed into one `Library` shape |
|
|
1023
|
+
| config (16K) | `contracts/settings.ts` + `settings-safe.ts` | partial: `WireCustomizationEntry.contextCostTokens` and `reloadClass` have no counterpart — add them |
|
|
1024
|
+
| decisions (12K) | `contracts/evidence.ts` (`gate`) | yes, but shape is opaque (`JSON.stringify(gate)` at evidence.tsx:226) |
|
|
1025
|
+
| dispatch (8K) | `contracts/fleet.ts` | yes |
|
|
1026
|
+
| eval (24K) | `contracts/reports.ts` | yes |
|
|
1027
|
+
| evidence (17K) | `contracts/evidence.ts` | yes |
|
|
1028
|
+
| fleet (25K) | `contracts/fleet.ts` + `fleet-events.ts` | yes |
|
|
1029
|
+
| interop (11K) | `contracts/targets.ts` + `targets-cli.ts` | partial: the four wiring states (configured / not-ACP / proposed / decided) are not modelled |
|
|
1030
|
+
| recovery (10K) | `contracts/system.ts` | yes |
|
|
1031
|
+
| routing (12K) | `contracts/targets.ts` | yes |
|
|
1032
|
+
| toolchain (9K) | `contracts/toolchain.ts` | yes |
|
|
1033
|
+
| trace (15K) | `contracts/traces.ts` | yes |
|
|
1034
|
+
| usage (16K) | `contracts/reports.ts` | partial: the 5-field token split + origin split (turns/sideQuestions/handoffs) is not modelled |
|
|
1035
|
+
|
|
1036
|
+
**The empty-state grammar — the most reusable thing here.** Every panel distinguished four states and used different words for each. Reproduce this everywhere:
|
|
1037
|
+
1. *Not read in this session* — `"The durable evidence inventory has not been read in this session."` / `"No diagnostic sweep has run in this desktop session. Nothing is inferred from a successful conversation."`
|
|
1038
|
+
2. *Store missing* — `"This installation has never run an evaluation, so it has no eval store at all. That is a missing store, not an empty one."` A dash in a figure means the store was not found: `"A dash means Clio Coder could not find that local history store. It does not mean zero activity."`
|
|
1039
|
+
3. *Store present, nothing in it* — `"Clio Coder has built no evidence bundles on this installation. This is an empty record, not a health claim."`
|
|
1040
|
+
4. *Record predates the schema* — `"This bundle predates the canonical trust projection, so it records no axes to open."`
|
|
1041
|
+
|
|
1042
|
+
And a fifth, for every bounded list: `"Older evidence bundles are outside this bounded window."` / `"Later phases are outside this bounded index."` / `"rarer kinds not shown"`. Every truncation flag on the wire gets a sentence; none is silently dropped.
|
|
1043
|
+
|
|
1044
|
+
<details>
|
|
1045
|
+
<summary>The fact order and grouping each of the thirteen inspectors used</summary>
|
|
1046
|
+
|
|
1047
|
+
- **config** → *Effective Clio Map*. Four summary figures first: effective setting facts, customization surfaces, estimated context cost (`~` + locale-formatted sum of `contextCostTokens`), needs-a-restart count. Then a three-stage "influence path" diagram: **01 Sources** (scope + setting-source counts, merged into one map, sorted by count descending then name, top 8) → **02 Loaded layers** (categories in `CUSTOMIZATION_CATEGORY_ORDER`, shown as a 3-4 letter code plus label) → **03 Behavior**. Then settings grouped by *family* (`key.split(/[.[]/, 1)[0]`), families sorted alphabetically. Entry source line: the project-relative path, or `"project root"` when the path is `/`, or `` `${scope} scope` `` when no path.
|
|
1048
|
+
- **catalog** → tabbed (Agents / Skills / Verifiers / Library / Extensions) with arrow-key tab navigation. Agent card facts in order: capability, project context tier, tool-call budget (`min–max` or bare `min`), read reserve; bound skills as chips; declared tool surfaces behind `<details>` with the count on the summary; footer = result contract + `"Text synthesis at boundary"` / `"Stops at boundary"`. Skill card leads with the *reach sentence* — `"The model can load this by name"` / `"Its root is not trusted, so the model never sees it"` / `"Its frontmatter reserves it for you"` — then precedence, root trust, model invocation; footer counts issues and flags `installed by a dispatched worker` / `has an upstream`. Free-text query filters across all string fields, case-folded with `toLocaleLowerCase("en-US")`.
|
|
1049
|
+
- **usage** → 30-day window, stated as `from … through …`. Summary: total tokens (+ API calls), Clio-reported cost (`"Recorded cost, never a GUI estimate"`), sessions, dispatch runs. Then token composition as five comparative bars — **`"Bars compare token fields with one another; they are not additive percentages. Provider accounting can overlap cache and reasoning fields."`** Then origin split (turns / side questions / handoffs), models sorted by tokens, skills split into activated vs dormant.
|
|
1050
|
+
- **trace** → per run: status mark, four totals (tokens, cost, wall time, runtime), then an ordered phase list with name / `kind · owner` / `tokens · cost · duration · N retries` / status, then event-kind and process-kind histograms. Boundary line: `"Event payloads, event names, and process command lines stay on the host by design. What crosses is how many of each kind there were."`
|
|
1051
|
+
- **toolchain** → one resolution label per item, with a deliberate three-way split: `!supported → "Platform unsupported"/neutral`; `source === "path" → "Using PATH"/success`; `source === "vendored" → "Using pinned copy"/success`; otherwise `"Not available"/warning`.
|
|
1052
|
+
- **interop** → summary (detected of known kinds, wired as peers, would be offered, detected-at), then one row per agent with the wiring sentence. Boundary: detection reads files only, starts no agent, and the version shown is the last one Clio recorded — `"opening this panel cannot become 'execute every coding agent installed on this machine'"`.
|
|
1053
|
+
- **routing** → target selector (targets derived client-side from the model rows, deduped and `localeCompare`-sorted), then a filtered model grid. Filter is a `useDeferredValue` over the lowercased query, matched against `[modelId, runtimeId, residency, ...capabilities]`. Zero values render `"Not reported"`, never `0`.
|
|
1054
|
+
- **dispatch** → installation-wide, explicitly not project-scoped and not a live stream: `"This is global installation state, not a fact about the selected project and not a live event stream."`
|
|
1055
|
+
- **recovery**, **evidence**, **fleet**, **eval**, **decisions** → see their own artifacts.
|
|
1056
|
+
|
|
1057
|
+
</details>
|
|
1058
|
+
|
|
1059
|
+
**Shared idioms worth extracting into `client/design/`:**
|
|
1060
|
+
- `<StatusMark tone label>` — a glyph + label pair with tones `success | warning | error | neutral | info | action`. Used by every panel; there is no other status primitive.
|
|
1061
|
+
- A `PanelHeading` with `eyebrow` (all-caps, wide-tracked), `title`, optional action slot. Every panel opens with an eyebrow that names the data's *scope and mutability*: `EVIDENCE BUNDLES · INSTALLATION-WIDE`, `DURABLE ACCOUNTING · TRACE DATABASE`, `EVAL REPORTS · INSTALLATION-WIDE · READ ONLY`, `INSTALLATION · REDACTED DIAGNOSTICS`, `MODELS · WORKER ROUTING`, `GATE DECISIONS · SEALED COORDINATOR VERDICTS`, `COUNCIL TOPOLOGY · SEATED VOICES AND ROUNDS`, `APPROVAL NEEDED · ONE USE`.
|
|
1062
|
+
- A closing **boundary paragraph** on every panel naming exactly what stays on the host. These are not boilerplate; each one is specific and each should survive.
|
|
1063
|
+
- Formatters: `formatTokens(v) = v === null ? "not recorded" : v.toLocaleString("en-US")`; `formatCostUsd(v) = v === null ? "not recorded" : "$" + v.toFixed(v > 0 && v < 0.01 ? 4 : 2)` — **exactly zero prints as `$0.00`, not "free", because a local runtime that prices at zero and a run whose cost was never recorded are different facts and only the second is null**. `client/api/clock.ts` already has `formatCost` and `formatTokens` matching this; keep them.
|
|
1064
|
+
|
|
1065
|
+
---
|
|
1066
|
+
|
|
1067
|
+
## Evidence trust record: axes, verdicts, and tone
|
|
1068
|
+
|
|
1069
|
+
- priority: must-keep
|
|
1070
|
+
- from: apps/workbench/src/App.tsx:4556-4630 and 4633-4770 (EvidenceInventory)
|
|
1071
|
+
- to: apps/clio-coder-gui/client/pages/evidence.tsx (replacing JSON dumps at :201,:226,:231)
|
|
1072
|
+
- value: Six named trust axes with a three-way tone function that distinguishes a stated negative from a stated absence from a qualified positive. This distinction is the whole point of the evidence system and is currently invisible — evidence.tsx renders `JSON.stringify(run.status)`.
|
|
1073
|
+
|
|
1074
|
+
<details>
|
|
1075
|
+
<summary>`EVIDENCE_VERDICT_PRESENTATION`, the axis labels, and `evidenceAxisTone`</summary>
|
|
1076
|
+
|
|
1077
|
+
```ts
|
|
1078
|
+
export const EVIDENCE_VERDICT_PRESENTATION: Record<string, { label: string; tone: string }> = {
|
|
1079
|
+
reviewed: { label: "Reviewed", tone: "success" },
|
|
1080
|
+
grounded: { label: "Grounded", tone: "success" },
|
|
1081
|
+
unverified: { label: "Unverified", tone: "warning" },
|
|
1082
|
+
compromised: { label: "Compromised", tone: "error" },
|
|
1083
|
+
unknown: { label: "Trust unknown", tone: "neutral" },
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
export const EVIDENCE_AXIS_LABEL: Record<string, string> = {
|
|
1087
|
+
artifactIntegrity: "Artifact integrity",
|
|
1088
|
+
validationGrounding: "Validation grounding",
|
|
1089
|
+
independentReview: "Independent review",
|
|
1090
|
+
contextProvenance: "Context provenance",
|
|
1091
|
+
autonomyEnforcement: "Autonomy enforcement",
|
|
1092
|
+
completionEvidence: "Completion evidence",
|
|
1093
|
+
};
|
|
1094
|
+
|
|
1095
|
+
/** States that are a stated negative rather than a stated absence. */
|
|
1096
|
+
const EVIDENCE_AXIS_FAULTS = new Set(["failed", "ungrounded", "not_independent", "invalid", "bypassed", "incomplete"]);
|
|
1097
|
+
|
|
1098
|
+
export function evidenceAxisTone(state: string): string {
|
|
1099
|
+
if (EVIDENCE_AXIS_FAULTS.has(state)) return "error";
|
|
1100
|
+
if (state === "absent" || state === "unknown" || state === "not_applicable") return "neutral";
|
|
1101
|
+
if (state === "approximated" || state === "limited" || state === "inconclusive") return "warning";
|
|
1102
|
+
return "success";
|
|
1103
|
+
}
|
|
1104
|
+
```
|
|
1105
|
+
|
|
1106
|
+
Axis state values render with `state.replaceAll("_", " ")`. Axes are rendered in the declaration order of `EVIDENCE_AXIS_LABEL` — iterate `Object.keys(EVIDENCE_AXIS_LABEL)`, never the payload's key order, so a bundle missing an axis still shows the slot.
|
|
1107
|
+
|
|
1108
|
+
</details>
|
|
1109
|
+
|
|
1110
|
+
**Bundle card, fact order:** id + `sourceKind · generatedAt` in the header with the verdict `StatusMark`; then a four-cell `<dl>`: Runs / Tool calls (with ` · N blocked` appended only when `> 0`) / Tokens / Cost; then tags as chips; then a row of run-id buttons, **each disabled when the run is outside the current run window** with `title="This run is outside the current run window"` — a disabled-but-present button is how the panel says "this exists, you just cannot get there from here"; then `and more` when `runIdsTruncated`.
|
|
1111
|
+
|
|
1112
|
+
**Trust record disclosure:** a button whose label is a three-state machine — `Open trust record` → `Reading trust record…` (while `pendingReadId === evidenceId`) → `Trust record open`, with `aria-expanded`. Disabled when `artifact.trust.historical` (there is nothing to open) or while any read is in flight.
|
|
1113
|
+
|
|
1114
|
+
**The note line under each bundle**, assembled from three optional clauses in this order:
|
|
1115
|
+
- `trust.historical ? "Built before the canonical trust projection, so it carries no verdict of its own." : \`Trust recorded for ${runsCovered.toLocaleString()} run${s}.\``
|
|
1116
|
+
- when `redactionCount > 0`: `` ` ${n.toLocaleString()} secret-shaped value${n === 1 ? " was" : "s were"} redacted at build time.` ``
|
|
1117
|
+
- when `totals.protectedArtifacts > 0`: `` ` ${n.toLocaleString()} protected artifact event${s} recorded.` ``
|
|
1118
|
+
|
|
1119
|
+
**Panel-level copy** (keep verbatim): `"Each bundle's shape and how far it can be trusted. A bundle covering several runs takes the verdict of its weakest run. Task text, working directories, and the files inside a bundle stay on the host."` The weakest-run rule is a semantic the reader cannot infer from the number.
|
|
1120
|
+
|
|
1121
|
+
<details>
|
|
1122
|
+
<summary>`FLEET_VERIFY_PRESENTATION` and the reason text for each receipt verdict</summary>
|
|
1123
|
+
|
|
1124
|
+
**Receipt verification**, a separate four-state axis with its own reason taxonomy:
|
|
1125
|
+
|
|
1126
|
+
```ts
|
|
1127
|
+
export const FLEET_VERIFY_PRESENTATION = {
|
|
1128
|
+
pending: { label: "Not sealed yet", tone: "neutral" },
|
|
1129
|
+
verified: { label: "Receipt authenticates", tone: "success" },
|
|
1130
|
+
failed: { label: "Receipt did not authenticate", tone: "error" },
|
|
1131
|
+
unavailable: { label: "Nothing to authenticate", tone: "warning" },
|
|
1132
|
+
};
|
|
1133
|
+
export const FLEET_VERIFY_REASON_TEXT = {
|
|
1134
|
+
"integrity-mismatch": "The receipt's own seal does not cover its current contents.",
|
|
1135
|
+
"ledger-mismatch": "The receipt no longer agrees with its ledger entry.",
|
|
1136
|
+
"integrity-invalid": "The receipt's integrity record is not readable.",
|
|
1137
|
+
"execution-role-invalid": "The receipt records an execution role Clio Coder does not recognise.",
|
|
1138
|
+
"routing-intent-invalid": "The receipt records a routing intent Clio Coder does not recognise.",
|
|
1139
|
+
"route-decision-invalid": "The receipt records a route decision Clio Coder does not recognise.",
|
|
1140
|
+
"receipt-unreadable": "Clio Coder could not read the stored receipt.",
|
|
1141
|
+
"envelope-unavailable": "Clio Coder has no ledger entry to check the receipt against.",
|
|
1142
|
+
unclassified: "Clio Coder refused the receipt for a reason this build does not classify.",
|
|
1143
|
+
};
|
|
1144
|
+
```
|
|
1145
|
+
|
|
1146
|
+
</details>
|
|
1147
|
+
|
|
1148
|
+
---
|
|
1149
|
+
|
|
1150
|
+
## Gate decisions and council topology taxonomies
|
|
1151
|
+
|
|
1152
|
+
- priority: must-keep
|
|
1153
|
+
- from: apps/workbench/src/App.tsx:4004-4235 (labels, tones, GateDecisions) and 4247-4445 (CouncilTopology)
|
|
1154
|
+
- to: apps/clio-coder-gui/client/pages/fleet.tsx + client/pages/evidence.tsx
|
|
1155
|
+
- value: Five closed label taxonomies that translate enum identifiers into operator English, plus the independence rule, which is a genuine piece of domain reasoning encoded in four lines. All of it is currently `JSON.stringify(gate)` at evidence.tsx:226 and `JSON.stringify(artifact)` at fleet.tsx:238.
|
|
1156
|
+
|
|
1157
|
+
<details>
|
|
1158
|
+
<summary>Gate outcome and reason labels, `gateOutcomeTone`, and the council taxonomies</summary>
|
|
1159
|
+
|
|
1160
|
+
```ts
|
|
1161
|
+
export const GATE_OUTCOME_LABELS: Readonly<Record<string, string>> = {
|
|
1162
|
+
pass: "passed", fail: "failed", revise: "sent back for revision",
|
|
1163
|
+
exhausted: "ran out of cycles", winner: "picked a winner", "no-winner": "picked nobody",
|
|
1164
|
+
"operator-confirmed": "confirmed by the operator", "full-auto-applied": "applied under full-auto",
|
|
1165
|
+
};
|
|
1166
|
+
|
|
1167
|
+
export const GATE_REASON_LABELS: Readonly<Record<string, string>> = {
|
|
1168
|
+
"all-candidates-failed": "every candidate builder failed, so there was nothing to judge",
|
|
1169
|
+
"builder-run-failed": "the builder run did not finish",
|
|
1170
|
+
"reviewer-run-failed": "the reviewer run did not finish",
|
|
1171
|
+
"reviewer-checks-failed": "the reviewer reported failed checks",
|
|
1172
|
+
"reviewer-report-invalid": "the reviewer did not answer under its typed contract",
|
|
1173
|
+
"judge-run-failed": "the judge run did not finish",
|
|
1174
|
+
"judge-result-invalid": "the judge did not answer under its typed contract",
|
|
1175
|
+
"judge-winner-out-of-range": "the judge named a candidate that does not exist",
|
|
1176
|
+
"judge-picked-failed-candidate": "the judge picked a candidate whose builder had failed",
|
|
1177
|
+
"winner-touches-protected-artifact": "the winning candidate changed a protected artifact",
|
|
1178
|
+
"operator-confirmed-winner": "an operator confirmed the prior judge decision",
|
|
1179
|
+
"full-auto-applied-winner": "full-auto applied the prior judge decision",
|
|
1180
|
+
unclassified: "Clio Coder recorded a reason this GUI does not classify",
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
export function gateOutcomeTone(outcome: string): string {
|
|
1184
|
+
if (outcome === "pass" || outcome === "winner" || outcome === "operator-confirmed") return "success";
|
|
1185
|
+
if (outcome === "fail" || outcome === "exhausted" || outcome === "no-winner") return "error";
|
|
1186
|
+
return "action";
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
export const COUNCIL_OUTCOME_LABELS: Readonly<Record<string, string>> = {
|
|
1190
|
+
succeeded: "answered", failed: "failed", timed_out: "timed out", stalled: "stalled",
|
|
1191
|
+
canceled: "canceled", denied_by_policy: "denied", spawn_failed: "never started",
|
|
1192
|
+
};
|
|
1193
|
+
export const COUNCIL_ORIGIN_LABELS: Readonly<Record<string, string>> = {
|
|
1194
|
+
user: "the operator asked for it", agent: "Clio Coder asked for it", internal: "the runtime started it",
|
|
1195
|
+
};
|
|
1196
|
+
export const COUNCIL_SYNTHESIS_LABELS: Readonly<Record<string, string>> = {
|
|
1197
|
+
none: "none; the members' answers stand alone",
|
|
1198
|
+
vote: "a majority over the members' own verdicts",
|
|
1199
|
+
judge: "a judge dispatched to synthesize the answers",
|
|
1200
|
+
};
|
|
1201
|
+
export const councilTurnLabel = (turn: { terminal: boolean; round: number; outcome: string | null }) =>
|
|
1202
|
+
!turn.terminal ? `round ${turn.round} · in flight`
|
|
1203
|
+
: `round ${turn.round} · ${turn.outcome === null ? "no outcome recorded" : COUNCIL_OUTCOME_LABELS[turn.outcome] ?? turn.outcome}`;
|
|
1204
|
+
```
|
|
1205
|
+
|
|
1206
|
+
**Independence definition and helper:**
|
|
1207
|
+
> Independence is defined as sharing neither the agent nor the model family: a shared target, runtime, or node is an operational fact about a small fleet, while a shared agent or model family is a shared failure mode, and only those two can make a verdict self-confirming.
|
|
1208
|
+
|
|
1209
|
+
```ts
|
|
1210
|
+
export function gateIndependenceText(decision: { correlation: { independent: boolean; agent: boolean; modelFamily: boolean } | null }): string {
|
|
1211
|
+
if (decision.correlation === null) return "no decider ran";
|
|
1212
|
+
if (decision.correlation.independent) return "independent of the route it graded";
|
|
1213
|
+
const shared = [...(decision.correlation.agent ? ["the same agent"] : []), ...(decision.correlation.modelFamily ? ["the same model family"] : [])];
|
|
1214
|
+
return `not independent: ${shared.join(" and ")}`;
|
|
1215
|
+
}
|
|
1216
|
+
```
|
|
1217
|
+
|
|
1218
|
+
</details>
|
|
1219
|
+
|
|
1220
|
+
**Card shape.** Header: `` `${topology === "review" ? "Review" : "Compete"} gate ${GATE_OUTCOME_LABELS[outcome] ?? outcome}` `` plus the group id as `<code>`, with a `StatusMark` carrying `cycle ${n}`. `<dl>`: Sealed (timestamp) / Independence / Winner (`"not a winner-picking outcome"` when null, else `candidate ${index}`) / Confirms (`"not a confirmation"` when null). Then a run list: each subject with role `graded` or `graded · winner`, then the decider with role `reviewer` or `judge`; each is a button, **disabled and suffixed `· outside this run window`** when not in the known-run set, `aria-current` when selected. Then the classified reason plus the fixed clause `". The exact text Clio Coder recorded stays on the host."`
|
|
1221
|
+
|
|
1222
|
+
**Two bound notes:** `"This gate graded more runs than the bounded list names."` and, at panel level, the alarm variant `` `${unverifiable} sealed decision file(s) in this scan no longer authenticate against their own integrity digest and are not shown.` ``
|
|
1223
|
+
|
|
1224
|
+
**Why this surface exists** (keep verbatim in the panel): `"A review or compete gate seals its verdict beside the receipts it graded, because writing it into them would invalidate their evidence. This is that verdict, who it graded, and whether the grader was independent of the route it was grading. The reasoning behind it stays on the host."`
|
|
1225
|
+
|
|
1226
|
+
**Council panel:** `"A council writes no record of its own; it is a set of ordinary runs sharing one group stamp. This is who was seated, where each voice ran, and how many rounds it took. What each member said and how the synthesis read stay on the host."` Empty: `"Clio Coder reports no councils in this ledger window. Councils dispatched longer ago than the scanned window are not indexed here."` Turn tone: not terminal → `action`; `succeeded` → `success`; `null` outcome → `neutral`; anything else → `error`.
|
|
1227
|
+
|
|
1228
|
+
**Step tone**, with its reasoning: outcome text on a step comes from the ledger and is open rather than an enum, so only the two words the scheduler writes for a settled step get a verdict tone; everything else stays neutral rather than guessing a colour for a state this build has not seen. `runId === null → neutral`; `"succeeded" → success`; `"failed" → error`; otherwise `action`.
|
|
1229
|
+
|
|
1230
|
+
---
|
|
1231
|
+
|
|
1232
|
+
## Config, recovery, eval and verifier taxonomies
|
|
1233
|
+
|
|
1234
|
+
- priority: must-keep
|
|
1235
|
+
- from: apps/workbench/src/App.tsx:332-397, 400-430, 7212-7264, 4847-4855, 2686-2760
|
|
1236
|
+
- to: apps/clio-coder-gui/client/pages/settings.tsx, system.tsx, reports.tsx, library.tsx
|
|
1237
|
+
- value: Four more closed taxonomies with their descriptions. settings.tsx:107 currently renders setting values as `JSON.stringify(row.value)`, reports.tsx:113 dumps the whole eval report, library.tsx:173 dumps skill detail. Without these tables the panels are unreadable enum soup.
|
|
1238
|
+
|
|
1239
|
+
<details>
|
|
1240
|
+
<summary>Customization categories, reload classes, setting sources, and scope labels</summary>
|
|
1241
|
+
|
|
1242
|
+
**Customization categories** (declaration order IS the display order — it traces the load path from settings outward to memory):
|
|
1243
|
+
```ts
|
|
1244
|
+
export const CUSTOMIZATION_CATEGORY_PRESENTATION: Record<string, { label: string; short: string; description: string }> = {
|
|
1245
|
+
settings: { label: "Settings", short: "SET", description: "Layered values that shape Clio Coder's behavior." },
|
|
1246
|
+
"clio-md": { label: "Project context", short: "CTX", description: "CLIO-CODER.md context Clio Coder can add to the next turn." },
|
|
1247
|
+
rule: { label: "Rules", short: "RUL", description: "Project rules and conditional context boundaries." },
|
|
1248
|
+
"operator-profile": { label: "Operator profile", short: "OPR", description: "Declared operator preferences added to context." },
|
|
1249
|
+
hook: { label: "Hooks", short: "HOK", description: "Middleware reactions loaded by Clio Coder." },
|
|
1250
|
+
extension: { label: "Extensions", short: "EXT", description: "Installed packages and their effective precedence." },
|
|
1251
|
+
"skill-root": { label: "Skill roots", short: "SKL", description: "Locations Clio Coder searches for skills." },
|
|
1252
|
+
"prompt-root": { label: "Prompt roots", short: "PMT", description: "Locations Clio Coder searches for saved prompts." },
|
|
1253
|
+
agents: { label: "Agents", short: "AGT", description: "Agent recipe sources visible to this project." },
|
|
1254
|
+
safety: { label: "Safety", short: "SAFE", description: "Effective working-freedom and safety facts." },
|
|
1255
|
+
memory: { label: "Memory", short: "MEM", description: "The durable memory surface Clio Coder can consult." },
|
|
1256
|
+
};
|
|
1257
|
+
export const CUSTOMIZATION_CATEGORY_ORDER = Object.keys(CUSTOMIZATION_CATEGORY_PRESENTATION);
|
|
1258
|
+
|
|
1259
|
+
/** When a change takes effect — the question every settings panel is really answering. */
|
|
1260
|
+
export const RELOAD_PRESENTATION: Record<string, { label: string; description: string }> = {
|
|
1261
|
+
hot: { label: "Now", description: "Clio Coder reports this surface as hot-reloadable." },
|
|
1262
|
+
"next-turn": { label: "Next turn", description: "Clio Coder reads this surface when the next turn begins." },
|
|
1263
|
+
restart: { label: "Restart", description: "A new Clio Coder process is required before this changes." },
|
|
1264
|
+
"n/a": { label: "Informational", description: "No apply timing is attached to this entry." },
|
|
1265
|
+
};
|
|
1266
|
+
|
|
1267
|
+
export const SETTING_SOURCE_LABELS: Record<string, string> = {
|
|
1268
|
+
"built-in": "Built in", user: "User", project: "Project",
|
|
1269
|
+
"project.local": "Project local", cli: "Command line",
|
|
1270
|
+
};
|
|
1271
|
+
export const scopeLabel = (scope: string) => ({
|
|
1272
|
+
project: "Project", "project.local": "Project local", user: "User",
|
|
1273
|
+
package: "Package", extension: "Extension", cli: "Command line",
|
|
1274
|
+
}[scope.toLocaleLowerCase("en-US")] ?? scope);
|
|
1275
|
+
```
|
|
1276
|
+
|
|
1277
|
+
The session settings panel at `session-controls.tsx:120-180` should carry `RELOAD_PRESENTATION` per field; right now it says only `"Changes are available between turns"` as a blanket statement, which is wrong for the hot-reloadable ones.
|
|
1278
|
+
|
|
1279
|
+
</details>
|
|
1280
|
+
|
|
1281
|
+
<details>
|
|
1282
|
+
<summary>The recovery section and check taxonomies with their tones</summary>
|
|
1283
|
+
|
|
1284
|
+
**Recovery sections** — order matters, it is outermost dependency first:
|
|
1285
|
+
```ts
|
|
1286
|
+
export const RECOVERY_SECTION_PRESENTATION: Record<string, { label: string; description: string }> = {
|
|
1287
|
+
runtime: { label: "Runtime", description: "Clio Coder, Node, platform, and engine readiness." },
|
|
1288
|
+
storage: { label: "Local layout", description: "The four resolved configuration, data, state, and cache roots." },
|
|
1289
|
+
configuration: { label: "Configuration", description: "Settings validity and credential-store posture." },
|
|
1290
|
+
history: { label: "History stores", description: "State metadata, sessions, and cache telemetry availability." },
|
|
1291
|
+
models: { label: "Targets & models", description: "Configured runtime fingerprints and model availability." },
|
|
1292
|
+
interoperability: { label: "Interoperability", description: "Detected and configured external agent surfaces." },
|
|
1293
|
+
toolchain: { label: "External toolchain", description: "Pinned optional programs and the managed file-picker profile." },
|
|
1294
|
+
panes: { label: "Panes host", description: "Multiplexer mode, reachability, protocol level, and journal writability." },
|
|
1295
|
+
fleet: { label: "Fleet preflight", description: "Configured node eligibility checks for the selected project." },
|
|
1296
|
+
other: { label: "Other checks", description: "Additional checks introduced by this Clio Coder version." },
|
|
1297
|
+
};
|
|
1298
|
+
export const RECOVERY_CHECK_PRESENTATION: Record<string, { label: string; tone: string }> = {
|
|
1299
|
+
ok: { label: "Passed", tone: "success" }, warn: { label: "Warning", tone: "warning" }, error: { label: "Failed", tone: "error" },
|
|
1300
|
+
};
|
|
1301
|
+
```
|
|
1302
|
+
|
|
1303
|
+
Each section is a `<details>` that **opens itself when `failures > 0 || warnings > 0`** — that is the row the operator came here to read. Section summary right-hand text: `` `${passed}/${checks} passed${warnings ? ` · ${warnings} warn` : ""}${failures ? ` · ${failures} fail` : ""}` ``. Verdict banner: `NO FAILURES` vs `ATTENTION REQUIRED`, with the headline `"All reported checks passed"` / `"${n} reported warning(s)"` / `"${n} reported failure(s)"`. Sub-line: `` `Inspected ${ts} · ${projectContext ? "selected-project context" : "installation context"}` ``. Versions strip: Clio Coder / Node / Platform / `Resolved roots ${pathsResolved}/4`, each `"not reported"` when absent. Boundary: the check crosses as name and verdict only, `"a check whose name is not name-shaped arrives unnamed rather than blanking the sweep"`, and the sweep passes no `--fix` flag.
|
|
1304
|
+
|
|
1305
|
+
</details>
|
|
1306
|
+
|
|
1307
|
+
<details>
|
|
1308
|
+
<summary>`EVAL_FAILURE_CLASS_LABEL` and how `evalPassTone` reads a report summary</summary>
|
|
1309
|
+
|
|
1310
|
+
**Eval failure classes** — every one is a sentence, not an identifier:
|
|
1311
|
+
```ts
|
|
1312
|
+
export const EVAL_FAILURE_CLASS_LABEL: Record<string, string> = {
|
|
1313
|
+
budget_exhausted: "Cost ceiling reached before the item ran",
|
|
1314
|
+
runner_failed: "The runner exited nonzero",
|
|
1315
|
+
grader_failed: "The grader declared the task unsolved",
|
|
1316
|
+
verifier_failed: "A machinery verifier failed",
|
|
1317
|
+
forbidden_path: "The item wrote outside its declared boundary",
|
|
1318
|
+
assertion_unresolved: "A threshold's metric was never measured",
|
|
1319
|
+
assertion_failed: "A declared threshold was not met",
|
|
1320
|
+
setup_failed: "The workspace fixture never came up",
|
|
1321
|
+
command_error: "A command could not be run at all",
|
|
1322
|
+
other: "A class this build does not name",
|
|
1323
|
+
};
|
|
1324
|
+
export function evalPassTone(report: { summary: { runs: number; failed: number }; results: { machineryFailures: number } }): string {
|
|
1325
|
+
if (report.summary.runs === 0) return "neutral";
|
|
1326
|
+
if (report.results.machineryFailures > 0) return "error"; // machinery failure outranks a failed scenario
|
|
1327
|
+
return report.summary.failed === 0 ? "success" : "warning";
|
|
1328
|
+
}
|
|
1329
|
+
```
|
|
1330
|
+
|
|
1331
|
+
Report card facts in order: Route (`target · model · runtime`, each falling back to `"unrecorded"`/`"no model"`), Comparable set (`Group ${servingGroup}` + `" · matrix only"` when not observed), Tokens (`"${total} over ${measuredRuns} of ${runs} runs"` or `"No run reported provider usage"`), Wall time, Built by (`Clio Coder ${version} · ${commit.slice(0,12)}`), Host attachments (`"${n} kept on the host · ${m} declared metrics"`). Panel boundary, which is the reason this panel is worded so carefully: `"A report also holds the whole session transcript its runner attached, the prompts inside it, and the workspace it ran in; those stay on the host and are counted here rather than shown."`
|
|
1332
|
+
|
|
1333
|
+
</details>
|
|
1334
|
+
|
|
1335
|
+
<details>
|
|
1336
|
+
<summary>Verifier origin, rejection and block copy, plus invalid-skill wording</summary>
|
|
1337
|
+
|
|
1338
|
+
**Verifier and skill catalog rejection taxonomies** (library page):
|
|
1339
|
+
```ts
|
|
1340
|
+
export const VERIFIER_ORIGIN_COPY = {
|
|
1341
|
+
"package-script": { eyebrow: "Package script", signal: "Runs today", note: "Verify runs npm with this script name and may pass extra argv" },
|
|
1342
|
+
catalog: { eyebrow: "Project catalog", signal: "Runs today", note: "Verify pins argv, working directory, and timeout through safe-exec" },
|
|
1343
|
+
proposed: { eyebrow: "Toolchain declaration", signal: "Not adopted", note: "Authoring would offer this check; nothing runs it until you write the catalog" },
|
|
1344
|
+
};
|
|
1345
|
+
export const VERIFIER_REJECTION_COPY = {
|
|
1346
|
+
unreadable: "the catalog file could not be read as a regular file inside this repository",
|
|
1347
|
+
"invalid-yaml": "the catalog is not valid YAML",
|
|
1348
|
+
"unsupported-version": "the catalog declares a schema version this Clio Coder does not support",
|
|
1349
|
+
"unknown-field": "the catalog declares a field the schema does not define",
|
|
1350
|
+
"missing-field": "a required field is missing",
|
|
1351
|
+
"duplicate-id": "two declarations claim the same identifier",
|
|
1352
|
+
"shell-command": "a check tries to run through a shell instead of an exact argument vector",
|
|
1353
|
+
"too-large": "a value passed one of the schema's size caps",
|
|
1354
|
+
"invalid-cwd": "a check names a working directory outside this repository",
|
|
1355
|
+
"invalid-value": "a value does not match the shape the schema requires",
|
|
1356
|
+
unclassified: "the parser refused it for a reason this read could not classify",
|
|
1357
|
+
};
|
|
1358
|
+
export const VERIFIER_BLOCK_COPY = {
|
|
1359
|
+
"catalog-rejected": "the project catalog did not parse, so no check plane could be read",
|
|
1360
|
+
"id-collision": "a package.json script and a catalog check claim the same identifier",
|
|
1361
|
+
unclassified: "Clio Coder refused the read for a reason this projection could not classify",
|
|
1362
|
+
};
|
|
1363
|
+
export const SKILL_INVALID_COPY = {
|
|
1364
|
+
"load-error": "a skill file could not be read",
|
|
1365
|
+
collision: "two skills of the same name were found and one lost",
|
|
1366
|
+
"unloadable-file": "a scanned file produced no skill",
|
|
1367
|
+
"no-skills": "no skill loaded at all",
|
|
1368
|
+
};
|
|
1369
|
+
```
|
|
1370
|
+
|
|
1371
|
+
Skill-catalog summary sentence, assembled: `` `${valid ? "Clio Coder's loader accepts this installation's skills." : `Clio Coder's loader does not consider this catalog valid, because ${SKILL_INVALID_COPY[invalidReason ?? "no-skills"]}.`} ${hidden === 0 ? "The model can load every one of them by name." : `${hidden} of ${total} ${hidden === 1 ? "is" : "are"} yours alone; the model never sees them.`}` `` where `hidden = total - modelVisible`.
|
|
1372
|
+
|
|
1373
|
+
Library boundary: `"Skill bodies, file paths, base directories, content hashes, install URLs, and the loader's own diagnostic text stay on the host. Whether a skill has an upstream to update from crosses; where that upstream is does not. Installing, updating, and evaluating a skill stay explicit terminal operations."`
|
|
1374
|
+
|
|
1375
|
+
</details>
|
|
1376
|
+
|
|
1377
|
+
---
|
|
1378
|
+
|
|
1379
|
+
## Provenance labelling rule and the source taxonomy
|
|
1380
|
+
|
|
1381
|
+
- priority: should-keep
|
|
1382
|
+
- from: apps/workbench/src/chat.ts:12-18, src/timeline.ts:217-227, src/App.tsx:231-260
|
|
1383
|
+
- to: apps/clio-coder-gui/client/chat/provenance.ts
|
|
1384
|
+
- value: One rule and one four-value table that together decide whose name appears on every card in the product. The new contract carries `provenance` on TimelineItem and renders it nowhere. The source taxonomy has no counterpart in the new contract at all and needs adding, because 'Clio told us this' and 'we measured this ourselves' are different warranties and the whole evidence posture depends on the reader being able to tell.
|
|
1385
|
+
|
|
1386
|
+
<details>
|
|
1387
|
+
<summary>`agentLabel`, `SOURCE_LABELS`, and the guidance string per source</summary>
|
|
1388
|
+
|
|
1389
|
+
**The attribution rule** (`timeline.ts:217-227`): take the LAST entry of the provenance array — the most specific identity Clio reported, so a delegated agent wins over the orchestrator that dispatched it. Render it only when `role === "worker"`. An empty array means nothing was reported and the card falls back to the product name. The GUI never guesses; an unattributed card stays attributed to the product.
|
|
1390
|
+
|
|
1391
|
+
```ts
|
|
1392
|
+
export function agentLabel(provenance: Provenance | undefined): string | null {
|
|
1393
|
+
const last = provenance?.at(-1);
|
|
1394
|
+
if (last === undefined || last.role !== "worker") return null;
|
|
1395
|
+
return last.node == null ? last.agentId : `${last.agentId} · ${last.node}`;
|
|
1396
|
+
}
|
|
1397
|
+
```
|
|
1398
|
+
|
|
1399
|
+
Use it in two places: as the *response author name* (replacing `"Clio Coder"` in `chat-response__meta`) when a whole narrative segment is attributed, and as the per-row `agent ${label}` tag inside the activity group.
|
|
1400
|
+
|
|
1401
|
+
**Source taxonomy — add to the wire.** `contracts/sessions.ts` has no `source` field. Add `source: Type.Optional(Type.Union([…]))` to `TimelineItem` and set it in `server/acp/supervisor.ts` (everything from `session/update` is `observed-on-acp`, everything from a replay is `replayed-from-clio`, anything the server itself synthesizes is `observed-by-server`). The four values and their words:
|
|
1402
|
+
|
|
1403
|
+
```ts
|
|
1404
|
+
export const SOURCE_LABELS: Readonly<Record<string, string>> = {
|
|
1405
|
+
"reported-by-clio": "Reported by Clio Coder",
|
|
1406
|
+
"observed-on-acp": "Observed on ACP",
|
|
1407
|
+
"observed-by-server": "Observed by the GUI server",
|
|
1408
|
+
"replayed-from-clio": "Replayed from Clio Coder",
|
|
1409
|
+
};
|
|
1410
|
+
export const SOURCE_GUIDANCE: Readonly<Record<string, string>> = {
|
|
1411
|
+
"reported-by-clio": "Clio Coder supplied this fact; the GUI did not measure it independently.",
|
|
1412
|
+
"observed-on-acp": "The GUI received this event on Clio Coder's live control channel.",
|
|
1413
|
+
"observed-by-server": "The GUI observed this fact in its own project or process boundary.",
|
|
1414
|
+
"replayed-from-clio": "Clio Coder replayed this from its own stored session history.",
|
|
1415
|
+
};
|
|
1416
|
+
```
|
|
1417
|
+
|
|
1418
|
+
Show the label on the forensic timeline card and as a `title=` tooltip elsewhere; do not put it on every chat row, which would be noise.
|
|
1419
|
+
|
|
1420
|
+
</details>
|
|
1421
|
+
|
|
1422
|
+
**Replay origin.** `TimelineItem.origin` already exists. The workbench used it for three visible things and all three are worth keeping: the request heading becomes `"Earlier request"`, a `earlier record` chip appears in the request meta, and a replayed turn is permanently `settled` so it is never re-rendered or clock-ticked. A replayed turn also has `startedAt === null` — the host deliberately refuses to stamp a replayed item with the current wall clock, because that would be inventing a time. Render `"unavailable"` rather than a fabricated timestamp; the new GUI's `formatTime` already returns `"not recorded"` for an unparseable value, which is the right behavior.
|
|
1423
|
+
|
|
1424
|
+
**Replay prompt fallback.** When a replayed turn group opens without its user prompt, the workbench still minted a request card, truthfully empty: `"(earlier prompt not replayed)"`, and for a replayed-but-blank prompt, `"(empty prompt)"`. The new `applySessionDelta` skips the user item entirely when `turn.prompt` is falsy (`session-projection.ts`, `case "turn.started"`), leaving a headless response. Add the fallbacks.
|
|
1425
|
+
|
|
1426
|
+
---
|
|
1427
|
+
|
|
1428
|
+
## Dropped deliberately
|
|
1429
|
+
|
|
1430
|
+
- apps/workbench/src/protocol.ts (8,230 lines, 255 KB): a hand-written runtime validator for every wire type, duplicating what TypeBox + the route table now do declaratively in contracts/*.ts. The type shapes are worth reading once while porting the taxonomies above; the validator code is dead weight against a TypeBox stack.
|
|
1431
|
+
- apps/workbench/clio-host.ts (2,456 lines) and clio-read-command.ts: the Deno subprocess supervisor, permission timer machinery, and bounded stdout/stderr readers. apps/clio-coder-gui/server/acp/ is the same architecture on Node and is already more capable (it carries rawInput/rawOutput, which the workbench host stripped). Only three functions were worth lifting and they are quoted in the artifacts above: safeToolTitle, presentableToolTitle, and consumeProjectRedaction's streaming-safe root-replacement (which retains a partial prefix match across chunk boundaries so a workspace path split across two deltas is still redacted).
|
|
1432
|
+
- apps/workbench/acp-client.ts (1,705 lines): the JSON-RPC-over-stdio client with frame bounds and pending-request accounting. Fully superseded by server/acp/. Its constants were checked and are either already matched or deliberately different in the new server.
|
|
1433
|
+
- apps/workbench/project-store.ts (933 lines): a Deno-KV-backed project registry. server/services/workspaces.ts replaces it.
|
|
1434
|
+
- apps/workbench/src/state.ts (1,193 lines) and workbench-state.ts: a hand-rolled reducer + action-creator layer. @tanstack/react-query plus SessionBuffer covers it. The one idea worth keeping is already captured in the groupTurns artifact (referential stability as the memoization contract).
|
|
1435
|
+
- apps/workbench/src/markdown.ts, Markdown.tsx, highlight.ts, mermaid.ts: verified already ported to apps/clio-coder-gui/client/render/ with the IncrementalMarkdown settled/tail lexer, the 60,000-char highlight cap, the lazy Prism grammar loaders, the DOMPurify-sanitized Mermaid path, and the 16 KiB / 400-line Mermaid source bounds all intact. The only live gap is that sessions.tsx never passes deferDiagrams, noted in the chat-turn artifact.
|
|
1436
|
+
- apps/workbench/src/styles.css (197,874 bytes) and DESIGN_SYSTEM.md: the new app has client/design/tokens.css with the green palette already established and the user explicitly wants that kept. The workbench stylesheet is a different layout system (rails and drawers) and porting it would fight react-router. The class names quoted in the artifacts above are the contract worth preserving; the CSS behind them should be rewritten.
|
|
1437
|
+
- apps/workbench/src/AboutRecord.tsx, HelpReference.tsx, FleetFilterBar.tsx, startup.ts, main.tsx: shell chrome specific to the Deno single-window app. help-reference.ts's KEYBINDINGS table survives, quoted in the message-actions artifact; the rest does not.
|
|
1438
|
+
- apps/workbench/tests/ (about 18,000 lines across 40 files): fixture-driven tests against the deleted modules. Three are worth reimplementing rather than porting and are called out in the artifacts: timeline_test.ts's ordering invariants (a control event never overtakes buffered text), event-buffer_test.ts's flush-ordering assertions, and chat_test.tsx's groupTurns identity-preservation assertions.
|
|
1439
|
+
- apps/workbench/PARITY.md (60 KB) and HARNESS_COVERAGE.md (67 KB): feature-by-feature matrices against a harness that has since moved. Superseded by the new route table; re-deriving them from contracts/routes.ts is cheaper and more honest than porting stale rows.
|
|
1440
|
+
- apps/workbench/artifact-allowlist.ts and scripts/visual-probe.ts, gui-lifecycle.ts: Deno-specific launch and screenshot plumbing. scripts/perf-workload.ts is the exception and is called out for reimplementation in the frame-buffer artifact.
|