mycode-cli 0.1.0__py3-none-any.whl
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.
- mycode/__init__.py +1 -0
- mycode/cli/__init__.py +1 -0
- mycode/cli/chat.py +650 -0
- mycode/cli/main.py +245 -0
- mycode/cli/render.py +693 -0
- mycode/cli/runtime.py +271 -0
- mycode/cli/theme.py +109 -0
- mycode/core/__init__.py +30 -0
- mycode/core/agent.py +515 -0
- mycode/core/config.py +551 -0
- mycode/core/messages.py +166 -0
- mycode/core/models.py +144 -0
- mycode/core/models_catalog.json +2090 -0
- mycode/core/providers/__init__.py +86 -0
- mycode/core/providers/anthropic_like.py +366 -0
- mycode/core/providers/base.py +351 -0
- mycode/core/providers/gemini.py +321 -0
- mycode/core/providers/openai_chat.py +356 -0
- mycode/core/providers/openai_responses.py +326 -0
- mycode/core/session.py +537 -0
- mycode/core/system_prompt.md +10 -0
- mycode/core/system_prompt.py +319 -0
- mycode/core/tools.py +898 -0
- mycode/server/__init__.py +1 -0
- mycode/server/app.py +52 -0
- mycode/server/deps.py +29 -0
- mycode/server/routers/__init__.py +7 -0
- mycode/server/routers/chat.py +320 -0
- mycode/server/routers/sessions.py +77 -0
- mycode/server/routers/workspaces.py +92 -0
- mycode/server/run_manager.py +195 -0
- mycode/server/schemas.py +66 -0
- mycode/server/static/assets/EditDiff-C1ql7kft.js +12 -0
- mycode/server/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- mycode/server/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- mycode/server/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- mycode/server/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- mycode/server/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- mycode/server/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- mycode/server/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- mycode/server/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- mycode/server/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- mycode/server/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- mycode/server/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- mycode/server/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- mycode/server/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- mycode/server/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- mycode/server/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- mycode/server/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- mycode/server/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- mycode/server/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- mycode/server/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- mycode/server/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- mycode/server/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- mycode/server/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- mycode/server/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- mycode/server/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- mycode/server/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- mycode/server/static/assets/abap-BdImnpbu.js +1 -0
- mycode/server/static/assets/actionscript-3-CoDkCxhg.js +1 -0
- mycode/server/static/assets/ada-bCR0ucgS.js +1 -0
- mycode/server/static/assets/andromeeda-C4gqWexZ.js +1 -0
- mycode/server/static/assets/angular-html-DA-rfuFy.js +1 -0
- mycode/server/static/assets/angular-ts-BrjP3tb8.js +1 -0
- mycode/server/static/assets/apache-Pmp26Uib.js +1 -0
- mycode/server/static/assets/apex-D8_7TLub.js +1 -0
- mycode/server/static/assets/apl-CORt7UWP.js +1 -0
- mycode/server/static/assets/applescript-Co6uUVPk.js +1 -0
- mycode/server/static/assets/ara-BRHolxvo.js +1 -0
- mycode/server/static/assets/asciidoc-Ve4PFQV2.js +1 -0
- mycode/server/static/assets/asm-D_Q5rh1f.js +1 -0
- mycode/server/static/assets/astro-HNnZUWAn.js +1 -0
- mycode/server/static/assets/aurora-x-D-2ljcwZ.js +1 -0
- mycode/server/static/assets/auto-render-xntwXHOX.js +261 -0
- mycode/server/static/assets/awk-DMzUqQB5.js +1 -0
- mycode/server/static/assets/ayu-dark-DYE7WIF3.js +1 -0
- mycode/server/static/assets/ayu-light-BA47KaF1.js +1 -0
- mycode/server/static/assets/ayu-mirage-32ctXXKs.js +1 -0
- mycode/server/static/assets/ballerina-BFfxhgS-.js +1 -0
- mycode/server/static/assets/bat-BkioyH1T.js +1 -0
- mycode/server/static/assets/beancount-k_qm7-4y.js +1 -0
- mycode/server/static/assets/berry-uYugtg8r.js +1 -0
- mycode/server/static/assets/bibtex-CHM0blh-.js +1 -0
- mycode/server/static/assets/bicep-Bmn6On1c.js +1 -0
- mycode/server/static/assets/bird2-BIv1doCn.js +1 -0
- mycode/server/static/assets/blade-BjGOyj-B.js +1 -0
- mycode/server/static/assets/bsl-BO_Y6i37.js +1 -0
- mycode/server/static/assets/c-BIGW1oBm.js +1 -0
- mycode/server/static/assets/c3-eo99z4R2.js +1 -0
- mycode/server/static/assets/cadence-Bv_4Rxtq.js +1 -0
- mycode/server/static/assets/cairo-KRGpt6FW.js +1 -0
- mycode/server/static/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- mycode/server/static/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- mycode/server/static/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- mycode/server/static/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- mycode/server/static/assets/clarity-D53aC0YG.js +1 -0
- mycode/server/static/assets/clojure-P80f7IUj.js +1 -0
- mycode/server/static/assets/cmake-D1j8_8rp.js +1 -0
- mycode/server/static/assets/cobol-nBiQ_Alo.js +1 -0
- mycode/server/static/assets/codeowners-Bp6g37R7.js +1 -0
- mycode/server/static/assets/codeql-DsOJ9woJ.js +1 -0
- mycode/server/static/assets/coffee-Ch7k5sss.js +1 -0
- mycode/server/static/assets/common-lisp-Cg-RD9OK.js +1 -0
- mycode/server/static/assets/coq-DkFqJrB1.js +1 -0
- mycode/server/static/assets/cpp-CofmeUqb.js +1 -0
- mycode/server/static/assets/crystal-DNxU26gB.js +1 -0
- mycode/server/static/assets/csharp-COcwbKMJ.js +1 -0
- mycode/server/static/assets/css-CLj8gQPS.js +1 -0
- mycode/server/static/assets/csv-fuZLfV_i.js +1 -0
- mycode/server/static/assets/cue-D82EKSYY.js +1 -0
- mycode/server/static/assets/cypher-COkxafJQ.js +1 -0
- mycode/server/static/assets/d-85-TOEBH.js +1 -0
- mycode/server/static/assets/dark-plus-C3mMm8J8.js +1 -0
- mycode/server/static/assets/dart-bE4Kk8sk.js +1 -0
- mycode/server/static/assets/dax-CEL-wOlO.js +1 -0
- mycode/server/static/assets/desktop-BmXAJ9_W.js +1 -0
- mycode/server/static/assets/diff-D97Zzqfu.js +1 -0
- mycode/server/static/assets/docker-BcOcwvcX.js +1 -0
- mycode/server/static/assets/dotenv-Da5cRb03.js +1 -0
- mycode/server/static/assets/dracula-BzJJZx-M.js +1 -0
- mycode/server/static/assets/dracula-soft-BXkSAIEj.js +1 -0
- mycode/server/static/assets/dream-maker-BtqSS_iP.js +1 -0
- mycode/server/static/assets/edge-FbVlp4U3.js +1 -0
- mycode/server/static/assets/elixir-CkH2-t6x.js +1 -0
- mycode/server/static/assets/elm-DbKCFpqz.js +1 -0
- mycode/server/static/assets/emacs-lisp-CXvaQtF9.js +1 -0
- mycode/server/static/assets/erb-BYCe7drp.js +1 -0
- mycode/server/static/assets/erlang-DsQrWhSR.js +1 -0
- mycode/server/static/assets/everforest-dark-BgDCqdQA.js +1 -0
- mycode/server/static/assets/everforest-light-C8M2exoo.js +1 -0
- mycode/server/static/assets/fennel-BYunw83y.js +1 -0
- mycode/server/static/assets/fish-BvzEVeQv.js +1 -0
- mycode/server/static/assets/fluent-C4IJs8-o.js +1 -0
- mycode/server/static/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- mycode/server/static/assets/fortran-free-form-BxgE0vQu.js +1 -0
- mycode/server/static/assets/fsharp-CXgrBDvD.js +1 -0
- mycode/server/static/assets/gdresource-BOOCDP_w.js +1 -0
- mycode/server/static/assets/gdscript-C5YyOfLZ.js +1 -0
- mycode/server/static/assets/gdshader-DkwncUOv.js +1 -0
- mycode/server/static/assets/genie-D0YGMca9.js +1 -0
- mycode/server/static/assets/gherkin-DyxjwDmM.js +1 -0
- mycode/server/static/assets/git-commit-F4YmCXRG.js +1 -0
- mycode/server/static/assets/git-rebase-r7XF79zn.js +1 -0
- mycode/server/static/assets/github-dark-DHJKELXO.js +1 -0
- mycode/server/static/assets/github-dark-default-Cuk6v7N8.js +1 -0
- mycode/server/static/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- mycode/server/static/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- mycode/server/static/assets/github-light-DAi9KRSo.js +1 -0
- mycode/server/static/assets/github-light-default-D7oLnXFd.js +1 -0
- mycode/server/static/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- mycode/server/static/assets/gleam-BspZqrRM.js +1 -0
- mycode/server/static/assets/glimmer-js-ByusRIyA.js +1 -0
- mycode/server/static/assets/glimmer-ts-BfAWNZQY.js +1 -0
- mycode/server/static/assets/glsl-DplSGwfg.js +1 -0
- mycode/server/static/assets/gn-n2N0HUVH.js +1 -0
- mycode/server/static/assets/gnuplot-DdkO51Og.js +1 -0
- mycode/server/static/assets/go-C27-OAKa.js +1 -0
- mycode/server/static/assets/graphql-ChdNCCLP.js +1 -0
- mycode/server/static/assets/groovy-gcz8RCvz.js +1 -0
- mycode/server/static/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- mycode/server/static/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- mycode/server/static/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- mycode/server/static/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- mycode/server/static/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- mycode/server/static/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- mycode/server/static/assets/hack-i7_Ulhet.js +1 -0
- mycode/server/static/assets/haml-D5jkg6IW.js +1 -0
- mycode/server/static/assets/handlebars-BpdQsYii.js +1 -0
- mycode/server/static/assets/haskell-Df6bDoY_.js +1 -0
- mycode/server/static/assets/haxe-CzTSHFRz.js +1 -0
- mycode/server/static/assets/hcl-BWvSN4gD.js +1 -0
- mycode/server/static/assets/hjson-D5-asLiD.js +1 -0
- mycode/server/static/assets/hlsl-D3lLCCz7.js +1 -0
- mycode/server/static/assets/horizon-BUw7H-hv.js +1 -0
- mycode/server/static/assets/horizon-bright-CUuTKBJd.js +1 -0
- mycode/server/static/assets/houston-DnULxvSX.js +1 -0
- mycode/server/static/assets/html-derivative-DlHx6ybY.js +1 -0
- mycode/server/static/assets/html-pp8916En.js +1 -0
- mycode/server/static/assets/http-jrhK8wxY.js +1 -0
- mycode/server/static/assets/hurl-irOxFIW8.js +1 -0
- mycode/server/static/assets/hxml-Bvhsp5Yf.js +1 -0
- mycode/server/static/assets/hy-DFXneXwc.js +1 -0
- mycode/server/static/assets/imba-DGztddWO.js +1 -0
- mycode/server/static/assets/index-B4e4WQPq.css +1 -0
- mycode/server/static/assets/index-C2xTNJGd.js +203 -0
- mycode/server/static/assets/ini-BEwlwnbL.js +1 -0
- mycode/server/static/assets/java-CylS5w8V.js +1 -0
- mycode/server/static/assets/javascript-wDzz0qaB.js +1 -0
- mycode/server/static/assets/jinja-f2NsQr07.js +1 -0
- mycode/server/static/assets/jison-wvAkD_A8.js +1 -0
- mycode/server/static/assets/json-Cp-IABpG.js +1 -0
- mycode/server/static/assets/json5-C9tS-k6U.js +1 -0
- mycode/server/static/assets/jsonc-Des-eS-w.js +1 -0
- mycode/server/static/assets/jsonl-DcaNXYhu.js +1 -0
- mycode/server/static/assets/jsonnet-DFQXde-d.js +1 -0
- mycode/server/static/assets/jssm-C2t-YnRu.js +1 -0
- mycode/server/static/assets/jsx-g9-lgVsj.js +1 -0
- mycode/server/static/assets/julia-CxzCAyBv.js +1 -0
- mycode/server/static/assets/just-VxiPbLrw.js +1 -0
- mycode/server/static/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- mycode/server/static/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- mycode/server/static/assets/kanagawa-wave-DWedfzmr.js +1 -0
- mycode/server/static/assets/katex-DnJR2-55.css +1 -0
- mycode/server/static/assets/kdl-DV7GczEv.js +1 -0
- mycode/server/static/assets/kotlin-BdnUsdx6.js +1 -0
- mycode/server/static/assets/kusto-wEQ09or8.js +1 -0
- mycode/server/static/assets/laserwave-DUszq2jm.js +1 -0
- mycode/server/static/assets/latex-CWtU0Tv5.js +1 -0
- mycode/server/static/assets/lean-BZvkOJ9d.js +1 -0
- mycode/server/static/assets/less-B1dDrJ26.js +1 -0
- mycode/server/static/assets/light-plus-B7mTdjB0.js +1 -0
- mycode/server/static/assets/liquid-C0sCDyMI.js +1 -0
- mycode/server/static/assets/llvm-DjAJT7YJ.js +1 -0
- mycode/server/static/assets/log-2UxHyX5q.js +1 -0
- mycode/server/static/assets/logo-BtOb2qkB.js +1 -0
- mycode/server/static/assets/lua-BaeVxFsk.js +1 -0
- mycode/server/static/assets/luau-C-HG3fhB.js +1 -0
- mycode/server/static/assets/make-CHLpvVh8.js +1 -0
- mycode/server/static/assets/markdown-Cvjx9yec.js +1 -0
- mycode/server/static/assets/marko-DjSrsDqO.js +1 -0
- mycode/server/static/assets/material-theme-D5KoaKCx.js +1 -0
- mycode/server/static/assets/material-theme-darker-BfHTSMKl.js +1 -0
- mycode/server/static/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- mycode/server/static/assets/material-theme-ocean-CyktbL80.js +1 -0
- mycode/server/static/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- mycode/server/static/assets/matlab-D7o27uSR.js +1 -0
- mycode/server/static/assets/mdc-DTYItulj.js +1 -0
- mycode/server/static/assets/mdx-Cmh6b_Ma.js +1 -0
- mycode/server/static/assets/mermaid-mWjccvbQ.js +1 -0
- mycode/server/static/assets/min-dark-CafNBF8u.js +1 -0
- mycode/server/static/assets/min-light-CTRr51gU.js +1 -0
- mycode/server/static/assets/mipsasm-CKIfxQSi.js +1 -0
- mycode/server/static/assets/mojo-rZm6bMo-.js +1 -0
- mycode/server/static/assets/monokai-D4h5O-jR.js +1 -0
- mycode/server/static/assets/moonbit-_H4v1dQx.js +1 -0
- mycode/server/static/assets/move-IF9eRakj.js +1 -0
- mycode/server/static/assets/narrat-DRg8JJMk.js +1 -0
- mycode/server/static/assets/nextflow-C-mBbutL.js +1 -0
- mycode/server/static/assets/nextflow-groovy-vE_lwT2v.js +1 -0
- mycode/server/static/assets/nginx-BpAMiNFr.js +1 -0
- mycode/server/static/assets/night-owl-C39BiMTA.js +1 -0
- mycode/server/static/assets/night-owl-light-CMTm3GFP.js +1 -0
- mycode/server/static/assets/nim-BIad80T-.js +1 -0
- mycode/server/static/assets/nix-CwoSXNpI.js +1 -0
- mycode/server/static/assets/nord-Ddv68eIx.js +1 -0
- mycode/server/static/assets/nushell-Cz2AlsmD.js +1 -0
- mycode/server/static/assets/objective-c-DXmwc3jG.js +1 -0
- mycode/server/static/assets/objective-cpp-CLxacb5B.js +1 -0
- mycode/server/static/assets/ocaml-C0hk2d4L.js +1 -0
- mycode/server/static/assets/odin-BBf5iR-q.js +1 -0
- mycode/server/static/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- mycode/server/static/assets/one-light-C3Wv6jpd.js +1 -0
- mycode/server/static/assets/openscad-C4EeE6gA.js +1 -0
- mycode/server/static/assets/pascal-D93ZcfNL.js +1 -0
- mycode/server/static/assets/perl-NvoQZIq0.js +1 -0
- mycode/server/static/assets/php-R6g_5hLQ.js +1 -0
- mycode/server/static/assets/pkl-u5AG7uiY.js +1 -0
- mycode/server/static/assets/plastic-3e1v2bzS.js +1 -0
- mycode/server/static/assets/plsql-ChMvpjG-.js +1 -0
- mycode/server/static/assets/po-BTJTHyun.js +1 -0
- mycode/server/static/assets/poimandres-CS3Unz2-.js +1 -0
- mycode/server/static/assets/polar-C0HS_06l.js +1 -0
- mycode/server/static/assets/postcss-CXtECtnM.js +1 -0
- mycode/server/static/assets/powerquery-CEu0bR-o.js +1 -0
- mycode/server/static/assets/powershell-Dpen1YoG.js +1 -0
- mycode/server/static/assets/prisma-Dd19v3D-.js +1 -0
- mycode/server/static/assets/prolog-CbFg5uaA.js +1 -0
- mycode/server/static/assets/proto-C7zT0LnQ.js +1 -0
- mycode/server/static/assets/pug-DKIMFp6K.js +1 -0
- mycode/server/static/assets/puppet-BMWR74SV.js +1 -0
- mycode/server/static/assets/purescript-CklMAg4u.js +1 -0
- mycode/server/static/assets/python-B6aJPvgy.js +1 -0
- mycode/server/static/assets/qml-3beO22l8.js +1 -0
- mycode/server/static/assets/qmldir-C8lEn-DE.js +1 -0
- mycode/server/static/assets/qss-IeuSbFQv.js +1 -0
- mycode/server/static/assets/r-Dspwwk_N.js +1 -0
- mycode/server/static/assets/racket-BqYA7rlc.js +1 -0
- mycode/server/static/assets/raku-DXvB9xmW.js +1 -0
- mycode/server/static/assets/razor-BDqjjVU7.js +1 -0
- mycode/server/static/assets/red-bN70gL4F.js +1 -0
- mycode/server/static/assets/reg-C-SQnVFl.js +1 -0
- mycode/server/static/assets/regexp-CDVJQ6XC.js +1 -0
- mycode/server/static/assets/rel-C3B-1QV4.js +1 -0
- mycode/server/static/assets/riscv-BM1_JUlF.js +1 -0
- mycode/server/static/assets/ron-D8l8udqQ.js +1 -0
- mycode/server/static/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
- mycode/server/static/assets/rose-pine-moon-D4_iv3hh.js +1 -0
- mycode/server/static/assets/rose-pine-qdsjHGoJ.js +1 -0
- mycode/server/static/assets/rosmsg-BJDFO7_C.js +1 -0
- mycode/server/static/assets/rst-CRjBmOyv.js +1 -0
- mycode/server/static/assets/ruby-Wjq7vjNf.js +1 -0
- mycode/server/static/assets/rust-B1yitclQ.js +1 -0
- mycode/server/static/assets/sas-cz2c8ADy.js +1 -0
- mycode/server/static/assets/sass-Cj5Yp3dK.js +1 -0
- mycode/server/static/assets/scala-C151Ov-r.js +1 -0
- mycode/server/static/assets/scheme-C98Dy4si.js +1 -0
- mycode/server/static/assets/scss-D5BDwBP9.js +1 -0
- mycode/server/static/assets/sdbl-DVxCFoDh.js +1 -0
- mycode/server/static/assets/shaderlab-Dg9Lc6iA.js +1 -0
- mycode/server/static/assets/shellscript-Yzrsuije.js +1 -0
- mycode/server/static/assets/shellsession-BADoaaVG.js +1 -0
- mycode/server/static/assets/slack-dark-BthQWCQV.js +1 -0
- mycode/server/static/assets/slack-ochin-DqwNpetd.js +1 -0
- mycode/server/static/assets/smalltalk-BERRCDM3.js +1 -0
- mycode/server/static/assets/snazzy-light-Bw305WKR.js +1 -0
- mycode/server/static/assets/solarized-dark-DXbdFlpD.js +1 -0
- mycode/server/static/assets/solarized-light-L9t79GZl.js +1 -0
- mycode/server/static/assets/solidity-rGO070M0.js +1 -0
- mycode/server/static/assets/soy-8wufbnw4.js +1 -0
- mycode/server/static/assets/sparql-rVzFXLq3.js +1 -0
- mycode/server/static/assets/splunk-BtCnVYZw.js +1 -0
- mycode/server/static/assets/sql-BLtJtn59.js +1 -0
- mycode/server/static/assets/ssh-config-_ykCGR6B.js +1 -0
- mycode/server/static/assets/stata-BH5u7GGu.js +1 -0
- mycode/server/static/assets/stylus-BEDo0Tqx.js +1 -0
- mycode/server/static/assets/surrealql-Bq5Q-fJD.js +1 -0
- mycode/server/static/assets/svelte-Cy7k_4gC.js +1 -0
- mycode/server/static/assets/swift-D82vCrfD.js +1 -0
- mycode/server/static/assets/synthwave-84-CbfX1IO0.js +1 -0
- mycode/server/static/assets/system-verilog-CnnmHF94.js +1 -0
- mycode/server/static/assets/systemd-4A_iFExJ.js +1 -0
- mycode/server/static/assets/talonscript-CkByrt1z.js +1 -0
- mycode/server/static/assets/tasl-QIJgUcNo.js +1 -0
- mycode/server/static/assets/tcl-dwOrl1Do.js +1 -0
- mycode/server/static/assets/templ-DhtptRzy.js +1 -0
- mycode/server/static/assets/terraform-BETggiCN.js +1 -0
- mycode/server/static/assets/tex-idrVyKtj.js +1 -0
- mycode/server/static/assets/tokyo-night-hegEt444.js +1 -0
- mycode/server/static/assets/toml-vGWfd6FD.js +1 -0
- mycode/server/static/assets/ts-tags-DQrlYJgV.js +1 -0
- mycode/server/static/assets/tsv-B_m7g4N7.js +1 -0
- mycode/server/static/assets/tsx-COt5Ahok.js +1 -0
- mycode/server/static/assets/turtle-BsS91CYL.js +1 -0
- mycode/server/static/assets/twig-xg9kU7Mw.js +1 -0
- mycode/server/static/assets/typescript-BPQ3VLAy.js +1 -0
- mycode/server/static/assets/typespec-CAFt9gP4.js +1 -0
- mycode/server/static/assets/typst-DHCkPAjA.js +1 -0
- mycode/server/static/assets/v-BcVCzyr7.js +1 -0
- mycode/server/static/assets/vala-CsfeWuGM.js +1 -0
- mycode/server/static/assets/vb-D17OF-Vu.js +1 -0
- mycode/server/static/assets/verilog-BQ8w6xss.js +1 -0
- mycode/server/static/assets/vesper-DU1UobuO.js +1 -0
- mycode/server/static/assets/vhdl-CeAyd5Ju.js +1 -0
- mycode/server/static/assets/viml-CJc9bBzg.js +1 -0
- mycode/server/static/assets/vitesse-black-Bkuqu6BP.js +1 -0
- mycode/server/static/assets/vitesse-dark-D0r3Knsf.js +1 -0
- mycode/server/static/assets/vitesse-light-CVO1_9PV.js +1 -0
- mycode/server/static/assets/vue-D2xRrEX4.js +1 -0
- mycode/server/static/assets/vue-html-AaS7Mt5G.js +1 -0
- mycode/server/static/assets/vue-vine-BoDAl6tE.js +1 -0
- mycode/server/static/assets/vyper-CDx5xZoG.js +1 -0
- mycode/server/static/assets/wasm-CG6Dc4jp.js +1 -0
- mycode/server/static/assets/wasm-MzD3tlZU.js +1 -0
- mycode/server/static/assets/wenyan-BV7otONQ.js +1 -0
- mycode/server/static/assets/wgsl-Dx-B1_4e.js +1 -0
- mycode/server/static/assets/wikitext-BhOHFoWU.js +1 -0
- mycode/server/static/assets/wit-5i3qLPDT.js +1 -0
- mycode/server/static/assets/wolfram-lXgVvXCa.js +1 -0
- mycode/server/static/assets/xml-sdJ4AIDG.js +1 -0
- mycode/server/static/assets/xsl-CtQFsRM5.js +1 -0
- mycode/server/static/assets/yaml-Buea-lGh.js +1 -0
- mycode/server/static/assets/zenscript-DVFEvuxE.js +1 -0
- mycode/server/static/assets/zig-VOosw3JB.js +1 -0
- mycode/server/static/favicon_slashes.svg +12 -0
- mycode/server/static/index.html +35 -0
- mycode_cli-0.1.0.dist-info/METADATA +186 -0
- mycode_cli-0.1.0.dist-info/RECORD +404 -0
- mycode_cli-0.1.0.dist-info/WHEEL +4 -0
- mycode_cli-0.1.0.dist-info/entry_points.txt +2 -0
- mycode_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
mycode/core/session.py
ADDED
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
"""Session storage and timeline events (append-only JSONL).
|
|
2
|
+
|
|
3
|
+
Inspired by pi/mom design principles:
|
|
4
|
+
- append-only message log (JSONL)
|
|
5
|
+
- small metadata file per session
|
|
6
|
+
- no rewriting of full conversation on each turn
|
|
7
|
+
|
|
8
|
+
On disk:
|
|
9
|
+
|
|
10
|
+
~/.mycode/sessions/<session_id>/
|
|
11
|
+
meta.json
|
|
12
|
+
messages.jsonl # Internal message/block dicts (excluding system prompt)
|
|
13
|
+
tool-output/ # large bash outputs (referenced by tool results)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
import json
|
|
20
|
+
import os
|
|
21
|
+
from dataclasses import asdict, dataclass, field
|
|
22
|
+
from datetime import UTC, datetime
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any
|
|
25
|
+
from uuid import uuid4
|
|
26
|
+
|
|
27
|
+
from mycode.core.config import resolve_sessions_dir
|
|
28
|
+
from mycode.core.messages import ConversationMessage, build_message, flatten_message_text, text_block, tool_result_block
|
|
29
|
+
|
|
30
|
+
# ---------------------------------------------------------------------
|
|
31
|
+
# Session format and compacting defaults
|
|
32
|
+
# ---------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
MESSAGE_FORMAT_VERSION = 5
|
|
35
|
+
DEFAULT_SESSION_PROVIDER = "anthropic"
|
|
36
|
+
DEFAULT_SESSION_TITLE = "New chat"
|
|
37
|
+
DEFAULT_COMPACT_THRESHOLD = 0.8
|
|
38
|
+
|
|
39
|
+
COMPACT_SUMMARY_PROMPT = """\
|
|
40
|
+
Summarize this conversation to create a continuation document. \
|
|
41
|
+
This summary will replace the full conversation history, so it must \
|
|
42
|
+
capture everything needed to continue the work seamlessly.
|
|
43
|
+
|
|
44
|
+
Include:
|
|
45
|
+
|
|
46
|
+
1. **User Requests**: Every distinct request or instruction the user gave, \
|
|
47
|
+
in chronological order. Preserve the user's original wording for ambiguous \
|
|
48
|
+
or nuanced requests.
|
|
49
|
+
2. **Completed Work**: What was accomplished — files created, modified, or \
|
|
50
|
+
deleted; bugs fixed; features added. Include file paths and function names.
|
|
51
|
+
3. **Current State**: The exact state of the work right now — what is working, \
|
|
52
|
+
what is broken, what is partially done.
|
|
53
|
+
4. **Key Decisions**: Important decisions made, constraints discovered, \
|
|
54
|
+
approaches chosen or rejected, and why.
|
|
55
|
+
5. **Next Steps**: What remains to be done, any work that was in progress \
|
|
56
|
+
when this summary was generated.
|
|
57
|
+
|
|
58
|
+
Rules:
|
|
59
|
+
- Be specific: include file paths, function names, error messages, and \
|
|
60
|
+
concrete details.
|
|
61
|
+
- Do not add suggestions or opinions — only summarize what happened.
|
|
62
|
+
- Keep it concise but complete.\
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
_COMPACT_ACK = "Understood. I have the context from the conversation summary and will continue the work."
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------------------
|
|
69
|
+
# Compact and rewind session events
|
|
70
|
+
# ---------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _now() -> str:
|
|
74
|
+
return datetime.now(UTC).isoformat()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def should_compact(
|
|
78
|
+
last_usage: dict[str, Any] | None,
|
|
79
|
+
context_window: int | None,
|
|
80
|
+
threshold: float,
|
|
81
|
+
) -> bool:
|
|
82
|
+
"""Return True when the last response input tokens exceed the threshold."""
|
|
83
|
+
|
|
84
|
+
if not last_usage or not context_window or threshold <= 0:
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
# Providers report prompt/input usage under slightly different field names.
|
|
88
|
+
input_tokens = int(
|
|
89
|
+
last_usage.get("input_tokens") or last_usage.get("prompt_tokens") or last_usage.get("prompt_token_count") or 0
|
|
90
|
+
)
|
|
91
|
+
return input_tokens >= context_window * threshold
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def build_compact_event(
|
|
95
|
+
summary_text: str,
|
|
96
|
+
*,
|
|
97
|
+
provider: str,
|
|
98
|
+
model: str,
|
|
99
|
+
compacted_count: int,
|
|
100
|
+
usage: dict[str, Any] | None = None,
|
|
101
|
+
) -> ConversationMessage:
|
|
102
|
+
"""Build the compact event stored in session JSONL."""
|
|
103
|
+
|
|
104
|
+
meta: dict[str, Any] = {
|
|
105
|
+
"provider": provider,
|
|
106
|
+
"model": model,
|
|
107
|
+
"compacted_count": compacted_count,
|
|
108
|
+
}
|
|
109
|
+
if usage is not None:
|
|
110
|
+
meta["usage"] = usage
|
|
111
|
+
return build_message("compact", [text_block(summary_text)], meta=meta)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def apply_compact(messages: list[ConversationMessage]) -> list[ConversationMessage]:
|
|
115
|
+
"""Replace the latest compact event with a summary + synthetic ack."""
|
|
116
|
+
|
|
117
|
+
# Only the newest compact event matters. Older history before it is no
|
|
118
|
+
# longer visible once the summary replaces that earlier conversation.
|
|
119
|
+
last_compact_index: int | None = None
|
|
120
|
+
for index, message in enumerate(messages):
|
|
121
|
+
if message.get("role") == "compact":
|
|
122
|
+
last_compact_index = index
|
|
123
|
+
|
|
124
|
+
if last_compact_index is None:
|
|
125
|
+
return messages
|
|
126
|
+
|
|
127
|
+
summary_text = ""
|
|
128
|
+
for block in messages[last_compact_index].get("content") or []:
|
|
129
|
+
if isinstance(block, dict) and block.get("type") == "text":
|
|
130
|
+
summary_text = str(block.get("text") or "")
|
|
131
|
+
break
|
|
132
|
+
|
|
133
|
+
return [
|
|
134
|
+
build_message(
|
|
135
|
+
"user",
|
|
136
|
+
[text_block(f"[Conversation Summary]\n\n{summary_text}")],
|
|
137
|
+
meta={"synthetic": True},
|
|
138
|
+
),
|
|
139
|
+
build_message("assistant", [text_block(_COMPACT_ACK)], meta={"synthetic": True}),
|
|
140
|
+
*messages[last_compact_index + 1 :],
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def build_rewind_event(rewind_to: int) -> ConversationMessage:
|
|
145
|
+
"""Build a rewind marker to append to session JSONL."""
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
"role": "rewind",
|
|
149
|
+
"meta": {
|
|
150
|
+
"rewind_to": rewind_to,
|
|
151
|
+
"created_at": _now(),
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def apply_rewind(messages: list[ConversationMessage]) -> list[ConversationMessage]:
|
|
157
|
+
"""Apply rewind markers inline while replaying the raw message log."""
|
|
158
|
+
|
|
159
|
+
result: list[ConversationMessage] = []
|
|
160
|
+
for message in messages:
|
|
161
|
+
if message.get("role") == "rewind":
|
|
162
|
+
# Rewind indices refer to the visible message list at that moment,
|
|
163
|
+
# so replay truncates the accumulated result in place.
|
|
164
|
+
rewind_to = (message.get("meta") or {}).get("rewind_to", 0)
|
|
165
|
+
result = result[:rewind_to]
|
|
166
|
+
else:
|
|
167
|
+
result.append(message)
|
|
168
|
+
return result
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
# ---------------------------------------------------------------------
|
|
172
|
+
# Session metadata
|
|
173
|
+
# ---------------------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@dataclass
|
|
177
|
+
class SessionMeta:
|
|
178
|
+
id: str
|
|
179
|
+
title: str
|
|
180
|
+
provider: str
|
|
181
|
+
model: str
|
|
182
|
+
cwd: str
|
|
183
|
+
api_base: str | None
|
|
184
|
+
message_format_version: int
|
|
185
|
+
created_at: str
|
|
186
|
+
updated_at: str
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# ---------------------------------------------------------------------
|
|
190
|
+
# Session store
|
|
191
|
+
# ---------------------------------------------------------------------
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@dataclass
|
|
195
|
+
class SessionStore:
|
|
196
|
+
"""File-based session store backed by append-only JSONL files."""
|
|
197
|
+
|
|
198
|
+
data_dir: Path = field(default_factory=resolve_sessions_dir)
|
|
199
|
+
|
|
200
|
+
def __post_init__(self) -> None:
|
|
201
|
+
self.data_dir.mkdir(parents=True, exist_ok=True)
|
|
202
|
+
|
|
203
|
+
# ---------------------------------------------------------------------
|
|
204
|
+
# Session paths and small JSON helpers
|
|
205
|
+
# ---------------------------------------------------------------------
|
|
206
|
+
|
|
207
|
+
def session_dir(self, session_id: str) -> Path:
|
|
208
|
+
return self.data_dir / session_id
|
|
209
|
+
|
|
210
|
+
def meta_path(self, session_id: str) -> Path:
|
|
211
|
+
return self.session_dir(session_id) / "meta.json"
|
|
212
|
+
|
|
213
|
+
def messages_path(self, session_id: str) -> Path:
|
|
214
|
+
return self.session_dir(session_id) / "messages.jsonl"
|
|
215
|
+
|
|
216
|
+
def _ensure_session_dir(self, session_id: str) -> None:
|
|
217
|
+
session_dir = self.session_dir(session_id)
|
|
218
|
+
session_dir.mkdir(parents=True, exist_ok=True)
|
|
219
|
+
(session_dir / "tool-output").mkdir(parents=True, exist_ok=True)
|
|
220
|
+
|
|
221
|
+
def _read_meta(self, session_id: str) -> dict | None:
|
|
222
|
+
path = self.meta_path(session_id)
|
|
223
|
+
if not path.exists():
|
|
224
|
+
return None
|
|
225
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
226
|
+
|
|
227
|
+
def _write_meta(self, session_id: str, meta: dict) -> None:
|
|
228
|
+
self.meta_path(session_id).write_text(json.dumps(meta, indent=2), encoding="utf-8")
|
|
229
|
+
|
|
230
|
+
# ---------------------------------------------------------------------
|
|
231
|
+
# Session CRUD and loading
|
|
232
|
+
# ---------------------------------------------------------------------
|
|
233
|
+
|
|
234
|
+
def draft_session(
|
|
235
|
+
self,
|
|
236
|
+
title: str | None,
|
|
237
|
+
*,
|
|
238
|
+
provider: str = DEFAULT_SESSION_PROVIDER,
|
|
239
|
+
model: str,
|
|
240
|
+
cwd: str,
|
|
241
|
+
api_base: str | None,
|
|
242
|
+
) -> dict:
|
|
243
|
+
session_id = uuid4().hex
|
|
244
|
+
now = _now()
|
|
245
|
+
meta = asdict(
|
|
246
|
+
SessionMeta(
|
|
247
|
+
id=session_id,
|
|
248
|
+
title=title or DEFAULT_SESSION_TITLE,
|
|
249
|
+
provider=provider,
|
|
250
|
+
model=model,
|
|
251
|
+
cwd=os.path.abspath(cwd),
|
|
252
|
+
api_base=api_base,
|
|
253
|
+
message_format_version=MESSAGE_FORMAT_VERSION,
|
|
254
|
+
created_at=now,
|
|
255
|
+
updated_at=now,
|
|
256
|
+
)
|
|
257
|
+
)
|
|
258
|
+
return {"session": meta, "messages": []}
|
|
259
|
+
|
|
260
|
+
async def create_session(
|
|
261
|
+
self,
|
|
262
|
+
title: str | None,
|
|
263
|
+
*,
|
|
264
|
+
session_id: str | None = None,
|
|
265
|
+
provider: str = DEFAULT_SESSION_PROVIDER,
|
|
266
|
+
model: str,
|
|
267
|
+
cwd: str,
|
|
268
|
+
api_base: str | None,
|
|
269
|
+
) -> dict:
|
|
270
|
+
data = self.draft_session(
|
|
271
|
+
title,
|
|
272
|
+
provider=provider,
|
|
273
|
+
model=model,
|
|
274
|
+
cwd=cwd,
|
|
275
|
+
api_base=api_base,
|
|
276
|
+
)
|
|
277
|
+
session = data["session"]
|
|
278
|
+
if session_id:
|
|
279
|
+
session["id"] = session_id
|
|
280
|
+
session_id = str(session["id"])
|
|
281
|
+
|
|
282
|
+
def write_files() -> None:
|
|
283
|
+
self._ensure_session_dir(session_id)
|
|
284
|
+
self._write_meta(session_id, session)
|
|
285
|
+
self.messages_path(session_id).touch(exist_ok=True)
|
|
286
|
+
|
|
287
|
+
await asyncio.to_thread(write_files)
|
|
288
|
+
return data
|
|
289
|
+
|
|
290
|
+
async def list_sessions(self, *, cwd: str | None = None) -> list[dict]:
|
|
291
|
+
normalized = os.path.abspath(cwd) if cwd else None
|
|
292
|
+
|
|
293
|
+
def load_all() -> list[dict]:
|
|
294
|
+
out: list[dict] = []
|
|
295
|
+
for entry in self.data_dir.iterdir():
|
|
296
|
+
if not entry.is_dir():
|
|
297
|
+
continue
|
|
298
|
+
mp = entry / "meta.json"
|
|
299
|
+
if not mp.exists():
|
|
300
|
+
continue
|
|
301
|
+
try:
|
|
302
|
+
meta = json.loads(mp.read_text(encoding="utf-8"))
|
|
303
|
+
if normalized and os.path.abspath(meta.get("cwd") or "") != normalized:
|
|
304
|
+
continue
|
|
305
|
+
out.append(meta)
|
|
306
|
+
except Exception:
|
|
307
|
+
continue
|
|
308
|
+
|
|
309
|
+
out.sort(key=lambda m: m.get("updated_at") or "", reverse=True)
|
|
310
|
+
return out
|
|
311
|
+
|
|
312
|
+
return await asyncio.to_thread(load_all)
|
|
313
|
+
|
|
314
|
+
async def latest_session(self, *, cwd: str | None = None) -> dict | None:
|
|
315
|
+
sessions = await self.list_sessions(cwd=cwd)
|
|
316
|
+
return sessions[0] if sessions else None
|
|
317
|
+
|
|
318
|
+
async def load_session(self, session_id: str) -> dict | None:
|
|
319
|
+
def load() -> dict | None:
|
|
320
|
+
meta = self._read_meta(session_id)
|
|
321
|
+
if meta is None:
|
|
322
|
+
return None
|
|
323
|
+
|
|
324
|
+
# Read the raw append-only log first. Replay happens after that.
|
|
325
|
+
raw_messages: list[dict] = []
|
|
326
|
+
messages_path = self.messages_path(session_id)
|
|
327
|
+
try:
|
|
328
|
+
with messages_path.open("r", encoding="utf-8") as handle:
|
|
329
|
+
for line in handle:
|
|
330
|
+
line = line.strip()
|
|
331
|
+
if not line:
|
|
332
|
+
continue
|
|
333
|
+
try:
|
|
334
|
+
msg = json.loads(line)
|
|
335
|
+
if isinstance(msg, dict):
|
|
336
|
+
raw_messages.append(msg)
|
|
337
|
+
except Exception:
|
|
338
|
+
continue
|
|
339
|
+
except FileNotFoundError:
|
|
340
|
+
pass
|
|
341
|
+
|
|
342
|
+
# Replay order defines the visible conversation state.
|
|
343
|
+
# 1) compact rewrites older history into one summary view
|
|
344
|
+
# 2) rewind truncates that visible list by message index
|
|
345
|
+
# 3) interrupted tool repair patches the final visible state
|
|
346
|
+
visible_messages = apply_compact(raw_messages)
|
|
347
|
+
visible_messages = apply_rewind(visible_messages)
|
|
348
|
+
self._repair_interrupted_tool_loop(session_id, meta, visible_messages)
|
|
349
|
+
|
|
350
|
+
return {"session": meta, "messages": visible_messages}
|
|
351
|
+
|
|
352
|
+
return await asyncio.to_thread(load)
|
|
353
|
+
|
|
354
|
+
def _repair_interrupted_tool_loop(self, session_id: str, meta: dict, messages: list[dict]) -> None:
|
|
355
|
+
"""Append a synthetic tool result when the latest tool loop was interrupted.
|
|
356
|
+
|
|
357
|
+
The runtime persists sessions as append-only JSONL. If a previous run was
|
|
358
|
+
interrupted after an assistant emitted `tool_use` blocks but before a
|
|
359
|
+
matching `tool_result` user message was written, repair the session by
|
|
360
|
+
appending one synthetic error result message.
|
|
361
|
+
"""
|
|
362
|
+
|
|
363
|
+
pending_tool_use_ids: list[str] = []
|
|
364
|
+
pending_tool_call_index: int | None = None
|
|
365
|
+
|
|
366
|
+
# Find the latest assistant message that started a tool loop.
|
|
367
|
+
for index in range(len(messages) - 1, -1, -1):
|
|
368
|
+
message = messages[index]
|
|
369
|
+
if message.get("role") != "assistant":
|
|
370
|
+
continue
|
|
371
|
+
|
|
372
|
+
blocks = message.get("content")
|
|
373
|
+
if not isinstance(blocks, list):
|
|
374
|
+
continue
|
|
375
|
+
|
|
376
|
+
tool_use_ids = [
|
|
377
|
+
str(block.get("id") or "")
|
|
378
|
+
for block in blocks
|
|
379
|
+
if isinstance(block, dict) and block.get("type") == "tool_use" and block.get("id")
|
|
380
|
+
]
|
|
381
|
+
if not tool_use_ids:
|
|
382
|
+
continue
|
|
383
|
+
|
|
384
|
+
pending_tool_use_ids = tool_use_ids
|
|
385
|
+
pending_tool_call_index = index
|
|
386
|
+
break
|
|
387
|
+
|
|
388
|
+
if pending_tool_call_index is None:
|
|
389
|
+
return
|
|
390
|
+
|
|
391
|
+
# Then collect tool results that were actually recorded after it.
|
|
392
|
+
completed_tool_use_ids: set[str] = set()
|
|
393
|
+
for message in messages[pending_tool_call_index + 1 :]:
|
|
394
|
+
if message.get("role") != "user":
|
|
395
|
+
continue
|
|
396
|
+
|
|
397
|
+
blocks = message.get("content")
|
|
398
|
+
if not isinstance(blocks, list):
|
|
399
|
+
continue
|
|
400
|
+
|
|
401
|
+
for block in blocks:
|
|
402
|
+
if not isinstance(block, dict) or block.get("type") != "tool_result":
|
|
403
|
+
continue
|
|
404
|
+
tool_use_id = str(block.get("tool_use_id") or "")
|
|
405
|
+
if tool_use_id:
|
|
406
|
+
completed_tool_use_ids.add(tool_use_id)
|
|
407
|
+
|
|
408
|
+
missing_tool_use_ids = [
|
|
409
|
+
tool_use_id for tool_use_id in pending_tool_use_ids if tool_use_id not in completed_tool_use_ids
|
|
410
|
+
]
|
|
411
|
+
if not missing_tool_use_ids:
|
|
412
|
+
return
|
|
413
|
+
|
|
414
|
+
repair_message = build_message(
|
|
415
|
+
"user",
|
|
416
|
+
[
|
|
417
|
+
tool_result_block(
|
|
418
|
+
tool_use_id=tool_use_id,
|
|
419
|
+
model_text="error: tool call was interrupted",
|
|
420
|
+
display_text="Tool call was interrupted",
|
|
421
|
+
is_error=True,
|
|
422
|
+
)
|
|
423
|
+
for tool_use_id in missing_tool_use_ids
|
|
424
|
+
],
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
with self.messages_path(session_id).open("a", encoding="utf-8") as handle:
|
|
428
|
+
handle.write(json.dumps(repair_message, ensure_ascii=False))
|
|
429
|
+
handle.write("\n")
|
|
430
|
+
|
|
431
|
+
meta["updated_at"] = _now()
|
|
432
|
+
self._write_meta(session_id, meta)
|
|
433
|
+
messages.append(repair_message)
|
|
434
|
+
|
|
435
|
+
async def delete_session(self, session_id: str) -> None:
|
|
436
|
+
def delete() -> None:
|
|
437
|
+
sdir = self.session_dir(session_id)
|
|
438
|
+
if not sdir.exists():
|
|
439
|
+
return
|
|
440
|
+
# small recursive delete (no shutil.rmtree to keep deps minimal)
|
|
441
|
+
for p in sorted(sdir.rglob("*"), reverse=True):
|
|
442
|
+
try:
|
|
443
|
+
if p.is_file() or p.is_symlink():
|
|
444
|
+
p.unlink(missing_ok=True)
|
|
445
|
+
elif p.is_dir():
|
|
446
|
+
p.rmdir()
|
|
447
|
+
except Exception:
|
|
448
|
+
pass
|
|
449
|
+
try:
|
|
450
|
+
sdir.rmdir()
|
|
451
|
+
except Exception:
|
|
452
|
+
pass
|
|
453
|
+
|
|
454
|
+
await asyncio.to_thread(delete)
|
|
455
|
+
|
|
456
|
+
async def clear_session(self, session_id: str) -> None:
|
|
457
|
+
def clear() -> None:
|
|
458
|
+
meta = self._read_meta(session_id)
|
|
459
|
+
if meta is None:
|
|
460
|
+
return
|
|
461
|
+
meta["updated_at"] = _now()
|
|
462
|
+
self._write_meta(session_id, meta)
|
|
463
|
+
self.messages_path(session_id).write_text("", encoding="utf-8")
|
|
464
|
+
|
|
465
|
+
await asyncio.to_thread(clear)
|
|
466
|
+
|
|
467
|
+
# ---------------------------------------------------------------------
|
|
468
|
+
# Append-only updates
|
|
469
|
+
# ---------------------------------------------------------------------
|
|
470
|
+
|
|
471
|
+
async def append_rewind(self, session_id: str, rewind_to: int) -> None:
|
|
472
|
+
"""Append a rewind marker to the session JSONL."""
|
|
473
|
+
|
|
474
|
+
def append() -> None:
|
|
475
|
+
meta = self._read_meta(session_id)
|
|
476
|
+
if meta is None:
|
|
477
|
+
return
|
|
478
|
+
|
|
479
|
+
event = build_rewind_event(rewind_to)
|
|
480
|
+
messages_path = self.messages_path(session_id)
|
|
481
|
+
with messages_path.open("a", encoding="utf-8") as handle:
|
|
482
|
+
handle.write(json.dumps(event, ensure_ascii=False))
|
|
483
|
+
handle.write("\n")
|
|
484
|
+
|
|
485
|
+
meta["updated_at"] = _now()
|
|
486
|
+
self._write_meta(session_id, meta)
|
|
487
|
+
|
|
488
|
+
await asyncio.to_thread(append)
|
|
489
|
+
|
|
490
|
+
async def append_message(
|
|
491
|
+
self,
|
|
492
|
+
session_id: str,
|
|
493
|
+
message: dict,
|
|
494
|
+
*,
|
|
495
|
+
provider: str,
|
|
496
|
+
model: str,
|
|
497
|
+
cwd: str,
|
|
498
|
+
api_base: str | None,
|
|
499
|
+
) -> None:
|
|
500
|
+
def append() -> None:
|
|
501
|
+
meta = self._read_meta(session_id)
|
|
502
|
+
if meta is None:
|
|
503
|
+
# Create the on-disk session only when the first message is persisted.
|
|
504
|
+
self._ensure_session_dir(session_id)
|
|
505
|
+
now = _now()
|
|
506
|
+
meta = asdict(
|
|
507
|
+
SessionMeta(
|
|
508
|
+
id=session_id,
|
|
509
|
+
title=DEFAULT_SESSION_TITLE,
|
|
510
|
+
provider=provider,
|
|
511
|
+
model=model,
|
|
512
|
+
cwd=os.path.abspath(cwd),
|
|
513
|
+
api_base=api_base,
|
|
514
|
+
message_format_version=MESSAGE_FORMAT_VERSION,
|
|
515
|
+
created_at=now,
|
|
516
|
+
updated_at=now,
|
|
517
|
+
)
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
messages_path = self.messages_path(session_id)
|
|
521
|
+
with messages_path.open("a", encoding="utf-8") as handle:
|
|
522
|
+
handle.write(json.dumps(message, ensure_ascii=False))
|
|
523
|
+
handle.write("\n")
|
|
524
|
+
|
|
525
|
+
meta["updated_at"] = _now()
|
|
526
|
+
meta.setdefault("message_format_version", MESSAGE_FORMAT_VERSION)
|
|
527
|
+
|
|
528
|
+
if meta.get("title") == DEFAULT_SESSION_TITLE and message.get("role") == "user":
|
|
529
|
+
# Keep the default title until we see the first real user text,
|
|
530
|
+
# then promote a short preview into the session title.
|
|
531
|
+
title_text = flatten_message_text(message, include_thinking=False).replace("\n", " ").strip()
|
|
532
|
+
if title_text:
|
|
533
|
+
meta["title"] = title_text[:48]
|
|
534
|
+
|
|
535
|
+
self._write_meta(session_id, meta)
|
|
536
|
+
|
|
537
|
+
await asyncio.to_thread(append)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
You are mycode, an expert coding assistant.
|
|
2
|
+
|
|
3
|
+
You have four tools: read, write, edit, bash.
|
|
4
|
+
|
|
5
|
+
- Use bash for file operations and exploration like `ls`, `find`, `rg`, etc.
|
|
6
|
+
- Always set offset/limit when reading large files.
|
|
7
|
+
- Always read files before editing them.
|
|
8
|
+
- Use write only for new files or complete rewrites
|
|
9
|
+
- Your response should be concise and relevant.
|
|
10
|
+
- When available skills match the current task, prefer them over manual alternatives. To use a skill: read its `SKILL.md`, then follow the instructions inside.
|