pythinker-code 2.0.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.
- pythinker_code/CHANGELOG.md +16 -0
- pythinker_code/__init__.py +0 -0
- pythinker_code/__main__.py +92 -0
- pythinker_code/acp/AGENTS.md +93 -0
- pythinker_code/acp/__init__.py +13 -0
- pythinker_code/acp/convert.py +128 -0
- pythinker_code/acp/host.py +298 -0
- pythinker_code/acp/mcp.py +46 -0
- pythinker_code/acp/server.py +497 -0
- pythinker_code/acp/session.py +496 -0
- pythinker_code/acp/tools.py +167 -0
- pythinker_code/acp/types.py +13 -0
- pythinker_code/acp/version.py +45 -0
- pythinker_code/agents/default/agent.yaml +36 -0
- pythinker_code/agents/default/coder.yaml +25 -0
- pythinker_code/agents/default/explore.yaml +46 -0
- pythinker_code/agents/default/plan.yaml +30 -0
- pythinker_code/agents/default/system.md +164 -0
- pythinker_code/agents/okabe/agent.yaml +22 -0
- pythinker_code/agentspec.py +163 -0
- pythinker_code/app.py +820 -0
- pythinker_code/approval_runtime/__init__.py +29 -0
- pythinker_code/approval_runtime/models.py +42 -0
- pythinker_code/approval_runtime/runtime.py +235 -0
- pythinker_code/auth/__init__.py +25 -0
- pythinker_code/auth/anthropic_direct.py +207 -0
- pythinker_code/auth/deepseek.py +192 -0
- pythinker_code/auth/lm_studio.py +418 -0
- pythinker_code/auth/minimax.py +203 -0
- pythinker_code/auth/oauth.py +1122 -0
- pythinker_code/auth/ollama.py +293 -0
- pythinker_code/auth/openai.py +771 -0
- pythinker_code/auth/opencode_go.py +203 -0
- pythinker_code/auth/openrouter.py +225 -0
- pythinker_code/auth/platforms.py +466 -0
- pythinker_code/background/__init__.py +36 -0
- pythinker_code/background/agent_runner.py +231 -0
- pythinker_code/background/ids.py +19 -0
- pythinker_code/background/manager.py +650 -0
- pythinker_code/background/models.py +105 -0
- pythinker_code/background/store.py +237 -0
- pythinker_code/background/summary.py +66 -0
- pythinker_code/background/worker.py +209 -0
- pythinker_code/cli/__init__.py +1326 -0
- pythinker_code/cli/__main__.py +19 -0
- pythinker_code/cli/_lazy_group.py +238 -0
- pythinker_code/cli/export.py +322 -0
- pythinker_code/cli/info.py +62 -0
- pythinker_code/cli/mcp.py +349 -0
- pythinker_code/cli/plugin.py +351 -0
- pythinker_code/cli/toad.py +74 -0
- pythinker_code/cli/vis.py +38 -0
- pythinker_code/cli/web.py +80 -0
- pythinker_code/config.py +453 -0
- pythinker_code/constant.py +33 -0
- pythinker_code/exception.py +43 -0
- pythinker_code/hooks/__init__.py +4 -0
- pythinker_code/hooks/config.py +34 -0
- pythinker_code/hooks/engine.py +371 -0
- pythinker_code/hooks/events.py +190 -0
- pythinker_code/hooks/runner.py +89 -0
- pythinker_code/llm.py +412 -0
- pythinker_code/metadata.py +79 -0
- pythinker_code/notifications/__init__.py +33 -0
- pythinker_code/notifications/llm.py +77 -0
- pythinker_code/notifications/manager.py +145 -0
- pythinker_code/notifications/models.py +50 -0
- pythinker_code/notifications/notifier.py +41 -0
- pythinker_code/notifications/store.py +118 -0
- pythinker_code/notifications/wire.py +21 -0
- pythinker_code/plugin/__init__.py +124 -0
- pythinker_code/plugin/manager.py +153 -0
- pythinker_code/plugin/tool.py +173 -0
- pythinker_code/prompts/__init__.py +6 -0
- pythinker_code/prompts/compact.md +73 -0
- pythinker_code/prompts/init.md +21 -0
- pythinker_code/py.typed +0 -0
- pythinker_code/session.py +319 -0
- pythinker_code/session_fork.py +325 -0
- pythinker_code/session_state.py +132 -0
- pythinker_code/share.py +14 -0
- pythinker_code/skill/__init__.py +727 -0
- pythinker_code/skill/flow/__init__.py +99 -0
- pythinker_code/skill/flow/d2.py +482 -0
- pythinker_code/skill/flow/mermaid.py +266 -0
- pythinker_code/skills/pythinker-code-help/SKILL.md +54 -0
- pythinker_code/skills/skill-creator/SKILL.md +367 -0
- pythinker_code/soul/__init__.py +304 -0
- pythinker_code/soul/agent.py +520 -0
- pythinker_code/soul/approval.py +267 -0
- pythinker_code/soul/btw.py +214 -0
- pythinker_code/soul/compaction.py +189 -0
- pythinker_code/soul/context.py +339 -0
- pythinker_code/soul/denwarenji.py +39 -0
- pythinker_code/soul/dynamic_injection.py +84 -0
- pythinker_code/soul/dynamic_injections/__init__.py +0 -0
- pythinker_code/soul/dynamic_injections/auto_mode.py +72 -0
- pythinker_code/soul/dynamic_injections/plan_mode.py +239 -0
- pythinker_code/soul/message.py +92 -0
- pythinker_code/soul/pythinkersoul.py +1613 -0
- pythinker_code/soul/slash.py +340 -0
- pythinker_code/soul/toolset.py +788 -0
- pythinker_code/subagents/__init__.py +21 -0
- pythinker_code/subagents/builder.py +42 -0
- pythinker_code/subagents/core.py +86 -0
- pythinker_code/subagents/git_context.py +172 -0
- pythinker_code/subagents/models.py +54 -0
- pythinker_code/subagents/output.py +71 -0
- pythinker_code/subagents/registry.py +28 -0
- pythinker_code/subagents/runner.py +428 -0
- pythinker_code/subagents/store.py +196 -0
- pythinker_code/telemetry/__init__.py +211 -0
- pythinker_code/telemetry/config.py +54 -0
- pythinker_code/telemetry/crash.py +157 -0
- pythinker_code/telemetry/metrics.py +208 -0
- pythinker_code/telemetry/otel.py +240 -0
- pythinker_code/telemetry/sentry.py +167 -0
- pythinker_code/telemetry/sink.py +189 -0
- pythinker_code/tools/AGENTS.md +6 -0
- pythinker_code/tools/__init__.py +105 -0
- pythinker_code/tools/agent/__init__.py +277 -0
- pythinker_code/tools/agent/description.md +41 -0
- pythinker_code/tools/ask_user/__init__.py +159 -0
- pythinker_code/tools/ask_user/description.md +19 -0
- pythinker_code/tools/background/__init__.py +318 -0
- pythinker_code/tools/background/list.md +10 -0
- pythinker_code/tools/background/output.md +11 -0
- pythinker_code/tools/background/stop.md +8 -0
- pythinker_code/tools/display.py +46 -0
- pythinker_code/tools/dmail/__init__.py +38 -0
- pythinker_code/tools/dmail/dmail.md +17 -0
- pythinker_code/tools/file/__init__.py +30 -0
- pythinker_code/tools/file/glob.md +17 -0
- pythinker_code/tools/file/glob.py +160 -0
- pythinker_code/tools/file/grep.md +6 -0
- pythinker_code/tools/file/grep_local.py +589 -0
- pythinker_code/tools/file/plan_mode.py +45 -0
- pythinker_code/tools/file/read.md +16 -0
- pythinker_code/tools/file/read.py +300 -0
- pythinker_code/tools/file/read_media.md +24 -0
- pythinker_code/tools/file/read_media.py +217 -0
- pythinker_code/tools/file/replace.md +7 -0
- pythinker_code/tools/file/replace.py +195 -0
- pythinker_code/tools/file/utils.py +257 -0
- pythinker_code/tools/file/write.md +5 -0
- pythinker_code/tools/file/write.py +177 -0
- pythinker_code/tools/plan/__init__.py +327 -0
- pythinker_code/tools/plan/description.md +29 -0
- pythinker_code/tools/plan/enter.py +190 -0
- pythinker_code/tools/plan/enter_description.md +35 -0
- pythinker_code/tools/plan/heroes.py +277 -0
- pythinker_code/tools/shell/__init__.py +253 -0
- pythinker_code/tools/shell/bash.md +35 -0
- pythinker_code/tools/shell/powershell.md +30 -0
- pythinker_code/tools/test.py +55 -0
- pythinker_code/tools/think/__init__.py +21 -0
- pythinker_code/tools/think/think.md +1 -0
- pythinker_code/tools/todo/__init__.py +168 -0
- pythinker_code/tools/todo/set_todo_list.md +23 -0
- pythinker_code/tools/utils.py +199 -0
- pythinker_code/tools/web/__init__.py +4 -0
- pythinker_code/tools/web/fetch.md +1 -0
- pythinker_code/tools/web/fetch.py +189 -0
- pythinker_code/tools/web/search.md +1 -0
- pythinker_code/tools/web/search.py +163 -0
- pythinker_code/ui/__init__.py +0 -0
- pythinker_code/ui/acp/__init__.py +99 -0
- pythinker_code/ui/print/__init__.py +474 -0
- pythinker_code/ui/print/visualize.py +185 -0
- pythinker_code/ui/shell/__init__.py +1696 -0
- pythinker_code/ui/shell/console.py +109 -0
- pythinker_code/ui/shell/debug.py +190 -0
- pythinker_code/ui/shell/echo.py +17 -0
- pythinker_code/ui/shell/export_import.py +117 -0
- pythinker_code/ui/shell/keyboard.py +300 -0
- pythinker_code/ui/shell/mcp_status.py +113 -0
- pythinker_code/ui/shell/model_picker.py +318 -0
- pythinker_code/ui/shell/oauth.py +272 -0
- pythinker_code/ui/shell/placeholders.py +531 -0
- pythinker_code/ui/shell/prompt.py +2278 -0
- pythinker_code/ui/shell/replay.py +215 -0
- pythinker_code/ui/shell/session_picker.py +227 -0
- pythinker_code/ui/shell/setup.py +212 -0
- pythinker_code/ui/shell/slash.py +898 -0
- pythinker_code/ui/shell/startup.py +32 -0
- pythinker_code/ui/shell/task_browser.py +486 -0
- pythinker_code/ui/shell/update.py +350 -0
- pythinker_code/ui/shell/usage.py +291 -0
- pythinker_code/ui/shell/usage_adapters/__init__.py +50 -0
- pythinker_code/ui/shell/usage_adapters/anthropic_admin.py +233 -0
- pythinker_code/ui/shell/usage_adapters/base.py +72 -0
- pythinker_code/ui/shell/usage_adapters/deepseek.py +137 -0
- pythinker_code/ui/shell/usage_adapters/minimax.py +236 -0
- pythinker_code/ui/shell/usage_adapters/openai_admin.py +225 -0
- pythinker_code/ui/shell/usage_adapters/openai_chatgpt.py +241 -0
- pythinker_code/ui/shell/usage_adapters/opencode_go.py +232 -0
- pythinker_code/ui/shell/usage_adapters/openrouter.py +105 -0
- pythinker_code/ui/shell/usage_adapters/pythinker.py +189 -0
- pythinker_code/ui/shell/usage_adapters/pythinker_ai.py +50 -0
- pythinker_code/ui/shell/usage_render.py +150 -0
- pythinker_code/ui/shell/visualize/__init__.py +165 -0
- pythinker_code/ui/shell/visualize/_approval_panel.py +505 -0
- pythinker_code/ui/shell/visualize/_blocks.py +629 -0
- pythinker_code/ui/shell/visualize/_btw_panel.py +224 -0
- pythinker_code/ui/shell/visualize/_input_router.py +48 -0
- pythinker_code/ui/shell/visualize/_interactive.py +523 -0
- pythinker_code/ui/shell/visualize/_live_view.py +826 -0
- pythinker_code/ui/shell/visualize/_question_panel.py +586 -0
- pythinker_code/ui/theme.py +241 -0
- pythinker_code/usage_ratelimit_cache.py +175 -0
- pythinker_code/utils/__init__.py +0 -0
- pythinker_code/utils/aiohttp.py +24 -0
- pythinker_code/utils/aioqueue.py +72 -0
- pythinker_code/utils/broadcast.py +37 -0
- pythinker_code/utils/changelog.py +108 -0
- pythinker_code/utils/clipboard.py +246 -0
- pythinker_code/utils/datetime.py +64 -0
- pythinker_code/utils/diff.py +135 -0
- pythinker_code/utils/editor.py +91 -0
- pythinker_code/utils/environment.py +73 -0
- pythinker_code/utils/envvar.py +22 -0
- pythinker_code/utils/export.py +696 -0
- pythinker_code/utils/file_filter.py +375 -0
- pythinker_code/utils/frontmatter.py +70 -0
- pythinker_code/utils/io.py +27 -0
- pythinker_code/utils/logging.py +146 -0
- pythinker_code/utils/media_tags.py +29 -0
- pythinker_code/utils/message.py +24 -0
- pythinker_code/utils/path.py +199 -0
- pythinker_code/utils/proctitle.py +33 -0
- pythinker_code/utils/proxy.py +31 -0
- pythinker_code/utils/pyinstaller.py +45 -0
- pythinker_code/utils/rich/__init__.py +33 -0
- pythinker_code/utils/rich/columns.py +99 -0
- pythinker_code/utils/rich/diff_render.py +481 -0
- pythinker_code/utils/rich/markdown.py +900 -0
- pythinker_code/utils/rich/markdown_sample.md +108 -0
- pythinker_code/utils/rich/markdown_sample_short.md +2 -0
- pythinker_code/utils/rich/syntax.py +114 -0
- pythinker_code/utils/sensitive.py +54 -0
- pythinker_code/utils/server.py +121 -0
- pythinker_code/utils/signals.py +43 -0
- pythinker_code/utils/slashcmd.py +124 -0
- pythinker_code/utils/string.py +41 -0
- pythinker_code/utils/subprocess_env.py +73 -0
- pythinker_code/utils/term.py +168 -0
- pythinker_code/utils/typing.py +20 -0
- pythinker_code/vis/__init__.py +0 -0
- pythinker_code/vis/api/__init__.py +5 -0
- pythinker_code/vis/api/sessions.py +687 -0
- pythinker_code/vis/api/statistics.py +209 -0
- pythinker_code/vis/api/system.py +19 -0
- pythinker_code/vis/app.py +175 -0
- pythinker_code/vis/static/assets/highlighted-body-B3W2YXNL-D2MTYyJz.js +1 -0
- pythinker_code/vis/static/assets/index-CezafTt_.js +185 -0
- pythinker_code/vis/static/assets/index-DSRInNnm.css +1 -0
- pythinker_code/vis/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- pythinker_code/vis/static/index.html +17 -0
- pythinker_code/web/__init__.py +5 -0
- pythinker_code/web/api/__init__.py +15 -0
- pythinker_code/web/api/config.py +208 -0
- pythinker_code/web/api/open_in.py +199 -0
- pythinker_code/web/api/sessions.py +1225 -0
- pythinker_code/web/app.py +451 -0
- pythinker_code/web/auth.py +191 -0
- pythinker_code/web/models.py +98 -0
- pythinker_code/web/runner/__init__.py +5 -0
- pythinker_code/web/runner/messages.py +57 -0
- pythinker_code/web/runner/process.py +754 -0
- pythinker_code/web/runner/worker.py +97 -0
- pythinker_code/web/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- pythinker_code/web/static/assets/_baseUniq--dyU3g5v.js +1 -0
- pythinker_code/web/static/assets/abap-BdImnpbu.js +1 -0
- pythinker_code/web/static/assets/actionscript-3-CfeIJUat.js +1 -0
- pythinker_code/web/static/assets/ada-bCR0ucgS.js +1 -0
- pythinker_code/web/static/assets/andromeeda-C-Jbm3Hp.js +1 -0
- pythinker_code/web/static/assets/angular-html-CU67Zn6k.js +1 -0
- pythinker_code/web/static/assets/angular-ts-BwZT4LLn.js +1 -0
- pythinker_code/web/static/assets/apache-Pmp26Uib.js +1 -0
- pythinker_code/web/static/assets/apex-D8_7TLub.js +1 -0
- pythinker_code/web/static/assets/apl-dKokRX4l.js +1 -0
- pythinker_code/web/static/assets/applescript-Co6uUVPk.js +1 -0
- pythinker_code/web/static/assets/ara-BRHolxvo.js +1 -0
- pythinker_code/web/static/assets/arc-DkMjLpYa.js +1 -0
- pythinker_code/web/static/assets/architectureDiagram-VXUJARFQ-CHWVaGo9.js +36 -0
- pythinker_code/web/static/assets/asciidoc-Dv7Oe6Be.js +1 -0
- pythinker_code/web/static/assets/asm-D_Q5rh1f.js +1 -0
- pythinker_code/web/static/assets/astro-CbQHKStN.js +1 -0
- pythinker_code/web/static/assets/aurora-x-D-2ljcwZ.js +1 -0
- pythinker_code/web/static/assets/awk-DMzUqQB5.js +1 -0
- pythinker_code/web/static/assets/ayu-dark-CmMr59Fi.js +1 -0
- pythinker_code/web/static/assets/ballerina-BFfxhgS-.js +1 -0
- pythinker_code/web/static/assets/bat-BkioyH1T.js +1 -0
- pythinker_code/web/static/assets/beancount-k_qm7-4y.js +1 -0
- pythinker_code/web/static/assets/berry-uYugtg8r.js +1 -0
- pythinker_code/web/static/assets/bibtex-CHM0blh-.js +1 -0
- pythinker_code/web/static/assets/bicep-Bmn6On1c.js +1 -0
- pythinker_code/web/static/assets/blade-D4QpJJKB.js +1 -0
- pythinker_code/web/static/assets/blockDiagram-VD42YOAC-DzdKe497.js +122 -0
- pythinker_code/web/static/assets/bsl-BO_Y6i37.js +1 -0
- pythinker_code/web/static/assets/c-BIGW1oBm.js +1 -0
- pythinker_code/web/static/assets/c3-VCDPK7BO.js +1 -0
- pythinker_code/web/static/assets/c4Diagram-YG6GDRKO-D84Blotg.js +10 -0
- pythinker_code/web/static/assets/cadence-Bv_4Rxtq.js +1 -0
- pythinker_code/web/static/assets/cairo-KRGpt6FW.js +1 -0
- pythinker_code/web/static/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- pythinker_code/web/static/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- pythinker_code/web/static/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- pythinker_code/web/static/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- pythinker_code/web/static/assets/channel-CllSjjdl.js +1 -0
- pythinker_code/web/static/assets/chunk-4BX2VUAB-C9w8wleE.js +1 -0
- pythinker_code/web/static/assets/chunk-55IACEB6-YlYJ8HnF.js +1 -0
- pythinker_code/web/static/assets/chunk-B4BG7PRW-Bwtz_AHU.js +165 -0
- pythinker_code/web/static/assets/chunk-DI55MBZ5-BbEHkl8h.js +220 -0
- pythinker_code/web/static/assets/chunk-FMBD7UC4-BKPbvjLC.js +15 -0
- pythinker_code/web/static/assets/chunk-QN33PNHL-D73dQvKf.js +1 -0
- pythinker_code/web/static/assets/chunk-QZHKN3VN-zGiLKes_.js +1 -0
- pythinker_code/web/static/assets/chunk-TZMSLE5B-LHJCi2fy.js +1 -0
- pythinker_code/web/static/assets/clarity-D53aC0YG.js +1 -0
- pythinker_code/web/static/assets/classDiagram-2ON5EDUG-vX27iZwa.js +1 -0
- pythinker_code/web/static/assets/classDiagram-v2-WZHVMYZB-vX27iZwa.js +1 -0
- pythinker_code/web/static/assets/clojure-P80f7IUj.js +1 -0
- pythinker_code/web/static/assets/clone-DYBkaPm2.js +1 -0
- pythinker_code/web/static/assets/cmake-D1j8_8rp.js +1 -0
- pythinker_code/web/static/assets/cobol-nwyudZeR.js +1 -0
- pythinker_code/web/static/assets/code-block-IT6T5CEO-NtKViZGl.js +2 -0
- pythinker_code/web/static/assets/codeowners-Bp6g37R7.js +1 -0
- pythinker_code/web/static/assets/codeql-DsOJ9woJ.js +1 -0
- pythinker_code/web/static/assets/coffee-Ch7k5sss.js +1 -0
- pythinker_code/web/static/assets/common-lisp-Cg-RD9OK.js +1 -0
- pythinker_code/web/static/assets/coq-DkFqJrB1.js +1 -0
- pythinker_code/web/static/assets/cose-bilkent-S5V4N54A-DialjZpd.js +1 -0
- pythinker_code/web/static/assets/cpp-CofmeUqb.js +1 -0
- pythinker_code/web/static/assets/crystal-tKQVLTB8.js +1 -0
- pythinker_code/web/static/assets/csharp-K5feNrxe.js +1 -0
- pythinker_code/web/static/assets/css-DPfMkruS.js +1 -0
- pythinker_code/web/static/assets/csv-fuZLfV_i.js +1 -0
- pythinker_code/web/static/assets/cue-D82EKSYY.js +1 -0
- pythinker_code/web/static/assets/cypher-COkxafJQ.js +1 -0
- pythinker_code/web/static/assets/cytoscape.esm-C_Fzpdck.js +321 -0
- pythinker_code/web/static/assets/d-85-TOEBH.js +1 -0
- pythinker_code/web/static/assets/dagre-6UL2VRFP-DfuvkZZ7.js +4 -0
- pythinker_code/web/static/assets/dark-plus-C3mMm8J8.js +1 -0
- pythinker_code/web/static/assets/dart-CF10PKvl.js +1 -0
- pythinker_code/web/static/assets/dax-CEL-wOlO.js +1 -0
- pythinker_code/web/static/assets/defaultLocale-DX6XiGOO.js +1 -0
- pythinker_code/web/static/assets/desktop-BmXAJ9_W.js +1 -0
- pythinker_code/web/static/assets/diagram-PSM6KHXK-DLGctX3v.js +24 -0
- pythinker_code/web/static/assets/diagram-QEK2KX5R-DnxN6S0F.js +43 -0
- pythinker_code/web/static/assets/diagram-S2PKOQOG-Caq_Set9.js +24 -0
- pythinker_code/web/static/assets/diff-D97Zzqfu.js +1 -0
- pythinker_code/web/static/assets/docker-BcOcwvcX.js +1 -0
- pythinker_code/web/static/assets/dotenv-Da5cRb03.js +1 -0
- pythinker_code/web/static/assets/dracula-BzJJZx-M.js +1 -0
- pythinker_code/web/static/assets/dracula-soft-BXkSAIEj.js +1 -0
- pythinker_code/web/static/assets/dream-maker-BtqSS_iP.js +1 -0
- pythinker_code/web/static/assets/edge-BkV0erSs.js +1 -0
- pythinker_code/web/static/assets/elixir-CDX3lj18.js +1 -0
- pythinker_code/web/static/assets/elm-DbKCFpqz.js +1 -0
- pythinker_code/web/static/assets/emacs-lisp-C9XAeP06.js +1 -0
- pythinker_code/web/static/assets/erDiagram-Q2GNP2WA-BgTfALoK.js +60 -0
- pythinker_code/web/static/assets/erb-BOJIQeun.js +1 -0
- pythinker_code/web/static/assets/erlang-DsQrWhSR.js +1 -0
- pythinker_code/web/static/assets/everforest-dark-BgDCqdQA.js +1 -0
- pythinker_code/web/static/assets/everforest-light-C8M2exoo.js +1 -0
- pythinker_code/web/static/assets/fennel-BYunw83y.js +1 -0
- pythinker_code/web/static/assets/fish-BvzEVeQv.js +1 -0
- pythinker_code/web/static/assets/flowDiagram-NV44I4VS-QjW_fnGi.js +162 -0
- pythinker_code/web/static/assets/fluent-C4IJs8-o.js +1 -0
- pythinker_code/web/static/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- pythinker_code/web/static/assets/fortran-free-form-BxgE0vQu.js +1 -0
- pythinker_code/web/static/assets/fsharp-CXgrBDvD.js +1 -0
- pythinker_code/web/static/assets/ganttDiagram-JELNMOA3-fqi8JFof.js +267 -0
- pythinker_code/web/static/assets/gdresource-B7Tvp0Sc.js +1 -0
- pythinker_code/web/static/assets/gdscript-DTMYz4Jt.js +1 -0
- pythinker_code/web/static/assets/gdshader-DkwncUOv.js +1 -0
- pythinker_code/web/static/assets/genie-D0YGMca9.js +1 -0
- pythinker_code/web/static/assets/gherkin-DyxjwDmM.js +1 -0
- pythinker_code/web/static/assets/git-commit-F4YmCXRG.js +1 -0
- pythinker_code/web/static/assets/git-rebase-r7XF79zn.js +1 -0
- pythinker_code/web/static/assets/gitGraphDiagram-NY62KEGX-i7o6VQ8x.js +65 -0
- pythinker_code/web/static/assets/github-dark-DHJKELXO.js +1 -0
- pythinker_code/web/static/assets/github-dark-default-Cuk6v7N8.js +1 -0
- pythinker_code/web/static/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- pythinker_code/web/static/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- pythinker_code/web/static/assets/github-light-DAi9KRSo.js +1 -0
- pythinker_code/web/static/assets/github-light-default-D7oLnXFd.js +1 -0
- pythinker_code/web/static/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- pythinker_code/web/static/assets/gleam-BspZqrRM.js +1 -0
- pythinker_code/web/static/assets/glimmer-js-Rg0-pVw9.js +1 -0
- pythinker_code/web/static/assets/glimmer-ts-U6CK756n.js +1 -0
- pythinker_code/web/static/assets/glsl-DplSGwfg.js +1 -0
- pythinker_code/web/static/assets/gn-n2N0HUVH.js +1 -0
- pythinker_code/web/static/assets/gnuplot-DdkO51Og.js +1 -0
- pythinker_code/web/static/assets/go-Dn2_MT6a.js +1 -0
- pythinker_code/web/static/assets/graph-C0vZK2pT.js +1 -0
- pythinker_code/web/static/assets/graphql-ChdNCCLP.js +1 -0
- pythinker_code/web/static/assets/groovy-gcz8RCvz.js +1 -0
- pythinker_code/web/static/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- pythinker_code/web/static/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- pythinker_code/web/static/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- pythinker_code/web/static/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- pythinker_code/web/static/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- pythinker_code/web/static/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- pythinker_code/web/static/assets/hack-CaT9iCJl.js +1 -0
- pythinker_code/web/static/assets/haml-B8DHNrY2.js +1 -0
- pythinker_code/web/static/assets/handlebars-BL8al0AC.js +1 -0
- pythinker_code/web/static/assets/haskell-Df6bDoY_.js +1 -0
- pythinker_code/web/static/assets/haxe-CzTSHFRz.js +1 -0
- pythinker_code/web/static/assets/hcl-BWvSN4gD.js +1 -0
- pythinker_code/web/static/assets/hjson-D5-asLiD.js +1 -0
- pythinker_code/web/static/assets/hlsl-D3lLCCz7.js +1 -0
- pythinker_code/web/static/assets/houston-DnULxvSX.js +1 -0
- pythinker_code/web/static/assets/html-GMplVEZG.js +1 -0
- pythinker_code/web/static/assets/html-derivative-BFtXZ54Q.js +1 -0
- pythinker_code/web/static/assets/http-jrhK8wxY.js +1 -0
- pythinker_code/web/static/assets/hurl-irOxFIW8.js +1 -0
- pythinker_code/web/static/assets/hxml-Bvhsp5Yf.js +1 -0
- pythinker_code/web/static/assets/hy-DFXneXwc.js +1 -0
- pythinker_code/web/static/assets/imba-DGztddWO.js +1 -0
- pythinker_code/web/static/assets/index-BYCCk6-K.js +153 -0
- pythinker_code/web/static/assets/index-BpoLgcEt.js +1 -0
- pythinker_code/web/static/assets/index-Cpy4G3uJ.js +2 -0
- pythinker_code/web/static/assets/index-CzV_vCfu.css +1 -0
- pythinker_code/web/static/assets/index-DI2oedCt.js +19 -0
- pythinker_code/web/static/assets/index-DdIkp80K.js +5 -0
- pythinker_code/web/static/assets/infoDiagram-WHAUD3N6-BMPpeZSM.js +2 -0
- pythinker_code/web/static/assets/ini-BEwlwnbL.js +1 -0
- pythinker_code/web/static/assets/init-Gi6I4Gst.js +1 -0
- pythinker_code/web/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- pythinker_code/web/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- pythinker_code/web/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- pythinker_code/web/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- pythinker_code/web/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- pythinker_code/web/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- pythinker_code/web/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- pythinker_code/web/static/assets/java-CylS5w8V.js +1 -0
- pythinker_code/web/static/assets/javascript-wDzz0qaB.js +1 -0
- pythinker_code/web/static/assets/jinja-4LBKfQ-Z.js +1 -0
- pythinker_code/web/static/assets/jison-wvAkD_A8.js +1 -0
- pythinker_code/web/static/assets/journeyDiagram-XKPGCS4Q-DAM7gngo.js +139 -0
- pythinker_code/web/static/assets/json-Cp-IABpG.js +1 -0
- pythinker_code/web/static/assets/json5-C9tS-k6U.js +1 -0
- pythinker_code/web/static/assets/jsonc-Des-eS-w.js +1 -0
- pythinker_code/web/static/assets/jsonl-DcaNXYhu.js +1 -0
- pythinker_code/web/static/assets/jsonnet-DFQXde-d.js +1 -0
- pythinker_code/web/static/assets/jssm-C2t-YnRu.js +1 -0
- pythinker_code/web/static/assets/jsx-g9-lgVsj.js +1 -0
- pythinker_code/web/static/assets/julia-CxzCAyBv.js +1 -0
- pythinker_code/web/static/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- pythinker_code/web/static/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- pythinker_code/web/static/assets/kanagawa-wave-DWedfzmr.js +1 -0
- pythinker_code/web/static/assets/kanban-definition-3W4ZIXB7-ChpBpV0k.js +89 -0
- pythinker_code/web/static/assets/katex-D2lIc1rk.css +1 -0
- pythinker_code/web/static/assets/kdl-DV7GczEv.js +1 -0
- pythinker_code/web/static/assets/kotlin-BdnUsdx6.js +1 -0
- pythinker_code/web/static/assets/kusto-DZf3V79B.js +1 -0
- pythinker_code/web/static/assets/laserwave-DUszq2jm.js +1 -0
- pythinker_code/web/static/assets/latex-B4uzh10-.js +1 -0
- pythinker_code/web/static/assets/layout-C3Jp1gKO.js +1 -0
- pythinker_code/web/static/assets/lean-BZvkOJ9d.js +1 -0
- pythinker_code/web/static/assets/less-B1dDrJ26.js +1 -0
- pythinker_code/web/static/assets/light-plus-B7mTdjB0.js +1 -0
- pythinker_code/web/static/assets/linear-BGHtL1N4.js +1 -0
- pythinker_code/web/static/assets/liquid-DYVedYrR.js +1 -0
- pythinker_code/web/static/assets/llvm-BtvRca6l.js +1 -0
- pythinker_code/web/static/assets/log-2UxHyX5q.js +1 -0
- pythinker_code/web/static/assets/logo-BtOb2qkB.js +1 -0
- pythinker_code/web/static/assets/lua-BbnMAYS6.js +1 -0
- pythinker_code/web/static/assets/luau-C-HG3fhB.js +1 -0
- pythinker_code/web/static/assets/make-CHLpvVh8.js +1 -0
- pythinker_code/web/static/assets/markdown-Cvjx9yec.js +1 -0
- pythinker_code/web/static/assets/marko-DZsq8hO1.js +1 -0
- pythinker_code/web/static/assets/material-theme-D5KoaKCx.js +1 -0
- pythinker_code/web/static/assets/material-theme-darker-BfHTSMKl.js +1 -0
- pythinker_code/web/static/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- pythinker_code/web/static/assets/material-theme-ocean-CyktbL80.js +1 -0
- pythinker_code/web/static/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- pythinker_code/web/static/assets/matlab-D7o27uSR.js +1 -0
- pythinker_code/web/static/assets/mdc-DUICxH0z.js +1 -0
- pythinker_code/web/static/assets/mdx-Cmh6b_Ma.js +1 -0
- pythinker_code/web/static/assets/mermaid-VLURNSYL-B2P5VJ9v.css +1 -0
- pythinker_code/web/static/assets/mermaid-VLURNSYL-C_HW6koB.js +465 -0
- pythinker_code/web/static/assets/mermaid-mWjccvbQ.js +1 -0
- pythinker_code/web/static/assets/mermaid.core-CnT4VrPC.js +191 -0
- pythinker_code/web/static/assets/min-Dn5VRVmX.js +1 -0
- pythinker_code/web/static/assets/min-dark-CafNBF8u.js +1 -0
- pythinker_code/web/static/assets/min-light-CTRr51gU.js +1 -0
- pythinker_code/web/static/assets/mindmap-definition-VGOIOE7T-x8EwhfIt.js +68 -0
- pythinker_code/web/static/assets/mipsasm-CKIfxQSi.js +1 -0
- pythinker_code/web/static/assets/mojo-B93PlW-d.js +1 -0
- pythinker_code/web/static/assets/monokai-D4h5O-jR.js +1 -0
- pythinker_code/web/static/assets/moonbit-Ba13S78F.js +1 -0
- pythinker_code/web/static/assets/move-Bu9oaDYs.js +1 -0
- pythinker_code/web/static/assets/narrat-DRg8JJMk.js +1 -0
- pythinker_code/web/static/assets/nextflow-BrzmwbiE.js +1 -0
- pythinker_code/web/static/assets/nginx-DknmC5AR.js +1 -0
- pythinker_code/web/static/assets/night-owl-C39BiMTA.js +1 -0
- pythinker_code/web/static/assets/nim-CVrawwO9.js +1 -0
- pythinker_code/web/static/assets/nix-CwoSXNpI.js +1 -0
- pythinker_code/web/static/assets/nord-Ddv68eIx.js +1 -0
- pythinker_code/web/static/assets/nushell-C-sUppwS.js +1 -0
- pythinker_code/web/static/assets/objective-c-DXmwc3jG.js +1 -0
- pythinker_code/web/static/assets/objective-cpp-CLxacb5B.js +1 -0
- pythinker_code/web/static/assets/ocaml-C0hk2d4L.js +1 -0
- pythinker_code/web/static/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- pythinker_code/web/static/assets/one-light-PoHY5YXO.js +1 -0
- pythinker_code/web/static/assets/openscad-C4EeE6gA.js +1 -0
- pythinker_code/web/static/assets/ordinal-Cboi1Yqb.js +1 -0
- pythinker_code/web/static/assets/pascal-D93ZcfNL.js +1 -0
- pythinker_code/web/static/assets/perl-C0TMdlhV.js +1 -0
- pythinker_code/web/static/assets/php-CDn_0X-4.js +1 -0
- pythinker_code/web/static/assets/pieDiagram-ADFJNKIX-DgxBKGz2.js +30 -0
- pythinker_code/web/static/assets/pkl-u5AG7uiY.js +1 -0
- pythinker_code/web/static/assets/plastic-3e1v2bzS.js +1 -0
- pythinker_code/web/static/assets/plsql-ChMvpjG-.js +1 -0
- pythinker_code/web/static/assets/po-BTJTHyun.js +1 -0
- pythinker_code/web/static/assets/poimandres-CS3Unz2-.js +1 -0
- pythinker_code/web/static/assets/polar-C0HS_06l.js +1 -0
- pythinker_code/web/static/assets/postcss-CXtECtnM.js +1 -0
- pythinker_code/web/static/assets/powerquery-CEu0bR-o.js +1 -0
- pythinker_code/web/static/assets/powershell-Dpen1YoG.js +1 -0
- pythinker_code/web/static/assets/prisma-Dd19v3D-.js +1 -0
- pythinker_code/web/static/assets/prolog-CbFg5uaA.js +1 -0
- pythinker_code/web/static/assets/proto-C7zT0LnQ.js +1 -0
- pythinker_code/web/static/assets/pug-CGlum2m_.js +1 -0
- pythinker_code/web/static/assets/puppet-BMWR74SV.js +1 -0
- pythinker_code/web/static/assets/purescript-CklMAg4u.js +1 -0
- pythinker_code/web/static/assets/python-B6aJPvgy.js +1 -0
- pythinker_code/web/static/assets/qml-3beO22l8.js +1 -0
- pythinker_code/web/static/assets/qmldir-C8lEn-DE.js +1 -0
- pythinker_code/web/static/assets/qss-IeuSbFQv.js +1 -0
- pythinker_code/web/static/assets/quadrantDiagram-AYHSOK5B-DSpe_dqk.js +7 -0
- pythinker_code/web/static/assets/r-Dspwwk_N.js +1 -0
- pythinker_code/web/static/assets/racket-BqYA7rlc.js +1 -0
- pythinker_code/web/static/assets/raku-DXvB9xmW.js +1 -0
- pythinker_code/web/static/assets/razor-C1TweQQi.js +1 -0
- pythinker_code/web/static/assets/red-bN70gL4F.js +1 -0
- pythinker_code/web/static/assets/reg-C-SQnVFl.js +1 -0
- pythinker_code/web/static/assets/regexp-CDVJQ6XC.js +1 -0
- pythinker_code/web/static/assets/rel-C3B-1QV4.js +1 -0
- pythinker_code/web/static/assets/requirementDiagram-UZGBJVZJ-8o9hozL-.js +64 -0
- pythinker_code/web/static/assets/riscv-BM1_JUlF.js +1 -0
- pythinker_code/web/static/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
- pythinker_code/web/static/assets/rose-pine-moon-D4_iv3hh.js +1 -0
- pythinker_code/web/static/assets/rose-pine-qdsjHGoJ.js +1 -0
- pythinker_code/web/static/assets/rosmsg-BJDFO7_C.js +1 -0
- pythinker_code/web/static/assets/rst-B0xPkSld.js +1 -0
- pythinker_code/web/static/assets/ruby-BvKwtOVI.js +1 -0
- pythinker_code/web/static/assets/rust-B1yitclQ.js +1 -0
- pythinker_code/web/static/assets/sankeyDiagram-TZEHDZUN-BLOSUixH.js +10 -0
- pythinker_code/web/static/assets/sas-cz2c8ADy.js +1 -0
- pythinker_code/web/static/assets/sass-Cj5Yp3dK.js +1 -0
- pythinker_code/web/static/assets/scala-C151Ov-r.js +1 -0
- pythinker_code/web/static/assets/scheme-C98Dy4si.js +1 -0
- pythinker_code/web/static/assets/scss-OYdSNvt2.js +1 -0
- pythinker_code/web/static/assets/sdbl-DVxCFoDh.js +1 -0
- pythinker_code/web/static/assets/sequenceDiagram-WL72ISMW-6F2G8JTU.js +145 -0
- pythinker_code/web/static/assets/shaderlab-Dg9Lc6iA.js +1 -0
- pythinker_code/web/static/assets/shellscript-Yzrsuije.js +1 -0
- pythinker_code/web/static/assets/shellsession-BADoaaVG.js +1 -0
- pythinker_code/web/static/assets/slack-dark-BthQWCQV.js +1 -0
- pythinker_code/web/static/assets/slack-ochin-DqwNpetd.js +1 -0
- pythinker_code/web/static/assets/smalltalk-BERRCDM3.js +1 -0
- pythinker_code/web/static/assets/snazzy-light-Bw305WKR.js +1 -0
- pythinker_code/web/static/assets/solarized-dark-DXbdFlpD.js +1 -0
- pythinker_code/web/static/assets/solarized-light-L9t79GZl.js +1 -0
- pythinker_code/web/static/assets/solidity-rGO070M0.js +1 -0
- pythinker_code/web/static/assets/soy-Brmx7dQM.js +1 -0
- pythinker_code/web/static/assets/sparql-rVzFXLq3.js +1 -0
- pythinker_code/web/static/assets/splunk-BtCnVYZw.js +1 -0
- pythinker_code/web/static/assets/sql-BLtJtn59.js +1 -0
- pythinker_code/web/static/assets/ssh-config-_ykCGR6B.js +1 -0
- pythinker_code/web/static/assets/stata-BH5u7GGu.js +1 -0
- pythinker_code/web/static/assets/stateDiagram-FKZM4ZOC-DP8xP0iJ.js +1 -0
- pythinker_code/web/static/assets/stateDiagram-v2-4FDKWEC3-1l6-EZNX.js +1 -0
- pythinker_code/web/static/assets/stylus-BEDo0Tqx.js +1 -0
- pythinker_code/web/static/assets/svelte-zxCyuUbr.js +1 -0
- pythinker_code/web/static/assets/swift-Dg5xB15N.js +1 -0
- pythinker_code/web/static/assets/synthwave-84-CbfX1IO0.js +1 -0
- pythinker_code/web/static/assets/system-verilog-CnnmHF94.js +1 -0
- pythinker_code/web/static/assets/systemd-4A_iFExJ.js +1 -0
- pythinker_code/web/static/assets/talonscript-CkByrt1z.js +1 -0
- pythinker_code/web/static/assets/tasl-QIJgUcNo.js +1 -0
- pythinker_code/web/static/assets/tcl-dwOrl1Do.js +1 -0
- pythinker_code/web/static/assets/templ-W15q3VgB.js +1 -0
- pythinker_code/web/static/assets/terraform-BETggiCN.js +1 -0
- pythinker_code/web/static/assets/tex-CvyZ59Mk.js +1 -0
- pythinker_code/web/static/assets/timeline-definition-IT6M3QCI-DMgruDfK.js +61 -0
- pythinker_code/web/static/assets/tokyo-night-hegEt444.js +1 -0
- pythinker_code/web/static/assets/toml-vGWfd6FD.js +1 -0
- pythinker_code/web/static/assets/treemap-KMMF4GRG-Bo9ZkrAK.js +128 -0
- pythinker_code/web/static/assets/ts-tags-zn1MmPIZ.js +1 -0
- pythinker_code/web/static/assets/tsv-B_m7g4N7.js +1 -0
- pythinker_code/web/static/assets/tsx-COt5Ahok.js +1 -0
- pythinker_code/web/static/assets/turtle-BsS91CYL.js +1 -0
- pythinker_code/web/static/assets/twig-CO9l9SDP.js +1 -0
- pythinker_code/web/static/assets/typescript-BPQ3VLAy.js +1 -0
- pythinker_code/web/static/assets/typespec-BGHnOYBU.js +1 -0
- pythinker_code/web/static/assets/typst-DHCkPAjA.js +1 -0
- pythinker_code/web/static/assets/v-BcVCzyr7.js +1 -0
- pythinker_code/web/static/assets/vala-CsfeWuGM.js +1 -0
- pythinker_code/web/static/assets/vb-D17OF-Vu.js +1 -0
- pythinker_code/web/static/assets/verilog-BQ8w6xss.js +1 -0
- pythinker_code/web/static/assets/vesper-DU1UobuO.js +1 -0
- pythinker_code/web/static/assets/vhdl-CeAyd5Ju.js +1 -0
- pythinker_code/web/static/assets/viml-CJc9bBzg.js +1 -0
- pythinker_code/web/static/assets/vitesse-black-Bkuqu6BP.js +1 -0
- pythinker_code/web/static/assets/vitesse-dark-D0r3Knsf.js +1 -0
- pythinker_code/web/static/assets/vitesse-light-CVO1_9PV.js +1 -0
- pythinker_code/web/static/assets/vue-DN_0RTcg.js +1 -0
- pythinker_code/web/static/assets/vue-html-AaS7Mt5G.js +1 -0
- pythinker_code/web/static/assets/vue-vine-CQOfvN7w.js +1 -0
- pythinker_code/web/static/assets/vyper-CDx5xZoG.js +1 -0
- pythinker_code/web/static/assets/wasm-CG6Dc4jp.js +1 -0
- pythinker_code/web/static/assets/wasm-MzD3tlZU.js +1 -0
- pythinker_code/web/static/assets/wenyan-BV7otONQ.js +1 -0
- pythinker_code/web/static/assets/wgsl-Dx-B1_4e.js +1 -0
- pythinker_code/web/static/assets/wikitext-BhOHFoWU.js +1 -0
- pythinker_code/web/static/assets/wit-5i3qLPDT.js +1 -0
- pythinker_code/web/static/assets/wolfram-lXgVvXCa.js +1 -0
- pythinker_code/web/static/assets/xml-sdJ4AIDG.js +1 -0
- pythinker_code/web/static/assets/xsl-CtQFsRM5.js +1 -0
- pythinker_code/web/static/assets/xychartDiagram-PRI3JC2R-GeF2johi.js +7 -0
- pythinker_code/web/static/assets/yaml-Buea-lGh.js +1 -0
- pythinker_code/web/static/assets/zenscript-DVFEvuxE.js +1 -0
- pythinker_code/web/static/assets/zig-VOosw3JB.js +1 -0
- pythinker_code/web/static/brand/apple-touch-icon.png +0 -0
- pythinker_code/web/static/brand/arctecture.webp +0 -0
- pythinker_code/web/static/brand/bimi-logo.svg +46 -0
- pythinker_code/web/static/brand/favicon.ico +0 -0
- pythinker_code/web/static/brand/fonts/dm-sans-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/dm-sans-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-sans-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-sans-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-serif-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-serif-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-italic-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-italic-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/roboto-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/roboto-latin.woff2 +0 -0
- pythinker_code/web/static/brand/icon-192.png +0 -0
- pythinker_code/web/static/brand/icon-512.png +0 -0
- pythinker_code/web/static/brand/icon.svg +1 -0
- pythinker_code/web/static/brand/logo.png +0 -0
- pythinker_code/web/static/brand/pythinker_animated.svg +79 -0
- pythinker_code/web/static/brand/robots.txt +4 -0
- pythinker_code/web/static/index.html +15 -0
- pythinker_code/web/static/logo.png +0 -0
- pythinker_code/web/store/__init__.py +1 -0
- pythinker_code/web/store/sessions.py +432 -0
- pythinker_code/wire/__init__.py +148 -0
- pythinker_code/wire/file.py +151 -0
- pythinker_code/wire/jsonrpc.py +263 -0
- pythinker_code/wire/protocol.py +2 -0
- pythinker_code/wire/root_hub.py +27 -0
- pythinker_code/wire/serde.py +26 -0
- pythinker_code/wire/server.py +1069 -0
- pythinker_code/wire/types.py +698 -0
- pythinker_code-2.0.0.dist-info/METADATA +660 -0
- pythinker_code-2.0.0.dist-info/RECORD +731 -0
- pythinker_code-2.0.0.dist-info/WHEEL +4 -0
- pythinker_code-2.0.0.dist-info/entry_points.txt +4 -0
- pythinker_code-2.0.0.dist-info/licenses/LICENSE +202 -0
- pythinker_code-2.0.0.dist-info/licenses/NOTICE +14 -0
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
# pyright: reportUnusedClass=false
|
|
2
|
+
"""Renderable block components for the streaming agent view.
|
|
3
|
+
|
|
4
|
+
Each block receives data via method calls and produces Rich renderables.
|
|
5
|
+
They have no knowledge of the event loop or prompt_toolkit.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import time
|
|
12
|
+
from collections import deque
|
|
13
|
+
from typing import TYPE_CHECKING, Any, NamedTuple, cast
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from markdown_it import MarkdownIt
|
|
17
|
+
|
|
18
|
+
import streamingjson # type: ignore[reportMissingTypeStubs]
|
|
19
|
+
from rich.console import Group, RenderableType
|
|
20
|
+
from rich.spinner import Spinner
|
|
21
|
+
from rich.style import Style
|
|
22
|
+
from rich.text import Text
|
|
23
|
+
|
|
24
|
+
from pythinker_code.soul import format_context_status, format_token_count
|
|
25
|
+
from pythinker_code.tools import extract_key_argument
|
|
26
|
+
from pythinker_code.ui.shell.console import console
|
|
27
|
+
from pythinker_code.utils.datetime import format_elapsed
|
|
28
|
+
from pythinker_code.utils.rich.columns import BulletColumns
|
|
29
|
+
from pythinker_code.utils.rich.diff_render import (
|
|
30
|
+
collect_diff_hunks,
|
|
31
|
+
render_diff_panel,
|
|
32
|
+
render_diff_summary_panel,
|
|
33
|
+
)
|
|
34
|
+
from pythinker_code.utils.rich.markdown import Markdown
|
|
35
|
+
from pythinker_code.wire.types import (
|
|
36
|
+
BackgroundTaskDisplayBlock,
|
|
37
|
+
BriefDisplayBlock,
|
|
38
|
+
DiffDisplayBlock,
|
|
39
|
+
Notification,
|
|
40
|
+
StatusUpdate,
|
|
41
|
+
TodoDisplayBlock,
|
|
42
|
+
ToolCall,
|
|
43
|
+
ToolCallPart,
|
|
44
|
+
ToolResult,
|
|
45
|
+
ToolReturnValue,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
_ELLIPSIS = "..."
|
|
49
|
+
_THINKING_PREVIEW_LINES = 6
|
|
50
|
+
_SELF_CLOSING_BLOCKS = frozenset(("fence", "code_block", "hr", "html_block"))
|
|
51
|
+
MAX_SUBAGENT_TOOL_CALLS_TO_SHOW = 4
|
|
52
|
+
|
|
53
|
+
# Animated bullet frames shown after the "Thinking" label. Dots grow in
|
|
54
|
+
# from the left, reach three, then drain out from the left — a continuous
|
|
55
|
+
# rightward flow that loops every ``_BULLET_FRAME_INTERVAL * len(frames)``.
|
|
56
|
+
_BULLET_FRAMES = (". ", ".. ", "...", " ..", " .", " ")
|
|
57
|
+
_BULLET_FRAME_INTERVAL = 0.13 # seconds per frame
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _bullet_frame_for(elapsed: float) -> str:
|
|
61
|
+
"""Select the current bullet frame from wall-clock elapsed time."""
|
|
62
|
+
idx = int(elapsed / _BULLET_FRAME_INTERVAL) % len(_BULLET_FRAMES)
|
|
63
|
+
return _BULLET_FRAMES[idx]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _truncate_to_display_width(line: str, max_width: int) -> str:
|
|
67
|
+
"""Truncate *line* so its terminal display width fits within *max_width*.
|
|
68
|
+
|
|
69
|
+
Uses ``rich.cells.cell_len`` for CJK-aware column width measurement.
|
|
70
|
+
"""
|
|
71
|
+
from rich.cells import cell_len
|
|
72
|
+
|
|
73
|
+
if cell_len(line) <= max_width:
|
|
74
|
+
return line
|
|
75
|
+
ellipsis_width = cell_len(_ELLIPSIS)
|
|
76
|
+
budget = max_width - ellipsis_width
|
|
77
|
+
width = 0
|
|
78
|
+
for i, ch in enumerate(line):
|
|
79
|
+
width += cell_len(ch)
|
|
80
|
+
if width > budget:
|
|
81
|
+
return line[:i] + _ELLIPSIS
|
|
82
|
+
return line
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# Lazy-initialized markdown-it parser for incremental token commitment.
|
|
86
|
+
_md_parser: MarkdownIt | None = None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _get_md_parser() -> MarkdownIt:
|
|
90
|
+
global _md_parser
|
|
91
|
+
if _md_parser is None:
|
|
92
|
+
from markdown_it import MarkdownIt
|
|
93
|
+
|
|
94
|
+
# Match the extensions used by the rendering path (utils/rich/markdown.py)
|
|
95
|
+
# so that block boundaries are detected consistently.
|
|
96
|
+
_md_parser = MarkdownIt().enable("strikethrough").enable("table")
|
|
97
|
+
return _md_parser
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _estimate_tokens(text: str) -> float:
|
|
101
|
+
"""Estimate token count for mixed CJK/Latin text.
|
|
102
|
+
|
|
103
|
+
Returns a **float** so that callers can accumulate across small chunks
|
|
104
|
+
without per-chunk floor truncation (e.g. a 3-char ASCII chunk would
|
|
105
|
+
yield 0 if truncated to int immediately, but 0.75 as float).
|
|
106
|
+
|
|
107
|
+
Heuristics based on common BPE tokenizers (cl100k, o200k):
|
|
108
|
+
- CJK ideographs: ~1.5 tokens per character (often split into 2-byte pieces)
|
|
109
|
+
- Latin / ASCII: ~1 token per 4 characters (words average ~4 chars)
|
|
110
|
+
"""
|
|
111
|
+
cjk = 0
|
|
112
|
+
other = 0
|
|
113
|
+
for ch in text:
|
|
114
|
+
cp = ord(ch)
|
|
115
|
+
if (
|
|
116
|
+
0x4E00 <= cp <= 0x9FFF # CJK Unified Ideographs
|
|
117
|
+
or 0x3400 <= cp <= 0x4DBF # CJK Extension A
|
|
118
|
+
or 0xF900 <= cp <= 0xFAFF # CJK Compatibility Ideographs
|
|
119
|
+
or 0x3000 <= cp <= 0x303F # CJK Symbols and Punctuation
|
|
120
|
+
or 0xFF00 <= cp <= 0xFFEF # Fullwidth Forms
|
|
121
|
+
):
|
|
122
|
+
cjk += 1
|
|
123
|
+
else:
|
|
124
|
+
other += 1
|
|
125
|
+
return cjk * 1.5 + other / 4
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _find_committed_boundary(text: str) -> int | None:
|
|
129
|
+
"""Return the character offset up to which *text* can be safely committed.
|
|
130
|
+
|
|
131
|
+
Uses the incremental token commitment algorithm: parse text into block-level
|
|
132
|
+
tokens via ``markdown-it-py``, confirm all blocks except the last one (which
|
|
133
|
+
may be incomplete due to streaming truncation).
|
|
134
|
+
|
|
135
|
+
Returns ``None`` when there are fewer than 2 blocks (nothing to confirm yet).
|
|
136
|
+
"""
|
|
137
|
+
md = _get_md_parser()
|
|
138
|
+
tokens = md.parse(text)
|
|
139
|
+
|
|
140
|
+
# Collect only TOP-LEVEL block boundaries by tracking nesting depth.
|
|
141
|
+
# Nested tokens (e.g. list_item_open inside bullet_list_open) must not be
|
|
142
|
+
# treated as independent blocks — otherwise lists and blockquotes get split.
|
|
143
|
+
block_maps: list[list[int]] = []
|
|
144
|
+
depth = 0
|
|
145
|
+
for t in tokens:
|
|
146
|
+
if t.nesting == 1:
|
|
147
|
+
if depth == 0 and t.map is not None:
|
|
148
|
+
block_maps.append(t.map)
|
|
149
|
+
depth += 1
|
|
150
|
+
elif t.nesting == -1:
|
|
151
|
+
depth -= 1
|
|
152
|
+
elif depth == 0 and t.type in _SELF_CLOSING_BLOCKS and t.map is not None:
|
|
153
|
+
block_maps.append(t.map)
|
|
154
|
+
|
|
155
|
+
if len(block_maps) < 2:
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
# Convert end-line number to character offset by scanning newlines.
|
|
159
|
+
target_line = block_maps[-2][1]
|
|
160
|
+
offset = 0
|
|
161
|
+
for _ in range(target_line):
|
|
162
|
+
offset = text.index("\n", offset) + 1
|
|
163
|
+
return offset
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _tail_lines(text: str, n: int) -> str:
|
|
167
|
+
"""Extract the last *n* lines from *text* via reverse scanning (O(n))."""
|
|
168
|
+
pos = len(text)
|
|
169
|
+
for _ in range(n):
|
|
170
|
+
pos = text.rfind("\n", 0, pos)
|
|
171
|
+
if pos == -1:
|
|
172
|
+
return text
|
|
173
|
+
return text[pos + 1 :]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class _ContentBlock:
|
|
177
|
+
"""Streaming content block with incremental markdown commitment.
|
|
178
|
+
|
|
179
|
+
For **composing** (``is_think=False``), confirmed markdown blocks are flushed
|
|
180
|
+
to the terminal permanently via ``console.print()`` as they become complete,
|
|
181
|
+
giving users real-time streaming output. Only the unconfirmed tail remains
|
|
182
|
+
in the transient Rich Live area.
|
|
183
|
+
|
|
184
|
+
For **thinking** (``is_think=True``), the default behavior is to keep the
|
|
185
|
+
raw reasoning text only for token accounting and never render it. The
|
|
186
|
+
Live area shows a compact ``Thinking`` label with an animated bullet
|
|
187
|
+
sequence, elapsed time, token count, and a live tokens/second pulse;
|
|
188
|
+
when the block ends, a one-liner ``Thought for Xs · N tokens`` is
|
|
189
|
+
committed to history in grey italics.
|
|
190
|
+
|
|
191
|
+
When ``show_thinking_stream=True``, the legacy behavior is restored: the
|
|
192
|
+
Live area shows a ``Thinking...`` spinner above a 6-line scrolling preview
|
|
193
|
+
of the raw reasoning text, and the full reasoning markdown is committed
|
|
194
|
+
to history when the block ends.
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
def __init__(self, is_think: bool, *, show_thinking_stream: bool = False):
|
|
198
|
+
self.is_think = is_think
|
|
199
|
+
self._show_thinking_stream = show_thinking_stream
|
|
200
|
+
self._spinner = Spinner("dots", "")
|
|
201
|
+
self.raw_text = ""
|
|
202
|
+
# Accumulated float estimate — avoids per-chunk int truncation.
|
|
203
|
+
self._token_count: float = 0.0
|
|
204
|
+
self._start_time = time.monotonic()
|
|
205
|
+
# Incremental commitment state (composing only).
|
|
206
|
+
self._committed_len = 0
|
|
207
|
+
self._has_printed_bullet = False
|
|
208
|
+
|
|
209
|
+
# -- Public API ----------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
def append(self, content: str) -> None:
|
|
212
|
+
self.raw_text += content
|
|
213
|
+
self._token_count += _estimate_tokens(content)
|
|
214
|
+
# Block boundaries require newlines; skip parse for mid-line chunks.
|
|
215
|
+
if not self.is_think and "\n" in content:
|
|
216
|
+
self._flush_committed()
|
|
217
|
+
|
|
218
|
+
def compose(self) -> RenderableType:
|
|
219
|
+
"""Render the transient Live area content.
|
|
220
|
+
|
|
221
|
+
Thinking mode shows the italic ``Thinking`` label with animated
|
|
222
|
+
bullets; composing mode shows the dots spinner over the
|
|
223
|
+
uncommitted markdown tail. When ``show_thinking_stream`` is enabled,
|
|
224
|
+
thinking mode falls back to the legacy ``Thinking...`` spinner stacked
|
|
225
|
+
above a 6-line scrolling preview of the raw reasoning text.
|
|
226
|
+
"""
|
|
227
|
+
if self.is_think:
|
|
228
|
+
if self._show_thinking_stream:
|
|
229
|
+
return self._compose_thinking_stream()
|
|
230
|
+
return self._compose_thinking()
|
|
231
|
+
return self._compose_spinner()
|
|
232
|
+
|
|
233
|
+
def compose_final(self) -> RenderableType:
|
|
234
|
+
"""Render the remaining uncommitted content when the block ends."""
|
|
235
|
+
if self.is_think:
|
|
236
|
+
if self._show_thinking_stream:
|
|
237
|
+
remaining = self._pending_text()
|
|
238
|
+
if not remaining:
|
|
239
|
+
return Text("")
|
|
240
|
+
return BulletColumns(
|
|
241
|
+
Markdown(remaining, style="grey50 italic"),
|
|
242
|
+
bullet_style="grey50",
|
|
243
|
+
)
|
|
244
|
+
elapsed_str = format_elapsed(time.monotonic() - self._start_time)
|
|
245
|
+
count_str = format_token_count(int(self._token_count))
|
|
246
|
+
return Text(
|
|
247
|
+
f"Thought for {elapsed_str} · {count_str} tokens",
|
|
248
|
+
style="grey50 italic",
|
|
249
|
+
)
|
|
250
|
+
remaining = self._pending_text()
|
|
251
|
+
if not remaining:
|
|
252
|
+
return Text("")
|
|
253
|
+
return self._wrap_bullet(Markdown(remaining))
|
|
254
|
+
|
|
255
|
+
def has_pending(self) -> bool:
|
|
256
|
+
"""Whether there is uncommitted content to flush."""
|
|
257
|
+
# Thinking blocks always commit a final trace line if any content
|
|
258
|
+
# was received, so gate on raw_text rather than uncommitted length.
|
|
259
|
+
if self.is_think:
|
|
260
|
+
return bool(self.raw_text)
|
|
261
|
+
return bool(self._pending_text())
|
|
262
|
+
|
|
263
|
+
# -- Private -------------------------------------------------------------
|
|
264
|
+
|
|
265
|
+
def _pending_text(self) -> str:
|
|
266
|
+
return self.raw_text[self._committed_len :]
|
|
267
|
+
|
|
268
|
+
def _wrap_bullet(self, renderable: RenderableType) -> BulletColumns:
|
|
269
|
+
"""First call gets the ``•`` bullet; subsequent calls get a space."""
|
|
270
|
+
if self._has_printed_bullet:
|
|
271
|
+
return BulletColumns(renderable, bullet=Text(" "))
|
|
272
|
+
self._has_printed_bullet = True
|
|
273
|
+
return BulletColumns(renderable)
|
|
274
|
+
|
|
275
|
+
def _flush_committed(self) -> None:
|
|
276
|
+
"""Commit confirmed markdown blocks to permanent terminal output."""
|
|
277
|
+
pending = self._pending_text()
|
|
278
|
+
if not pending:
|
|
279
|
+
return
|
|
280
|
+
boundary = _find_committed_boundary(pending)
|
|
281
|
+
if boundary is None:
|
|
282
|
+
return
|
|
283
|
+
committed_text = pending[:boundary]
|
|
284
|
+
console.print(self._wrap_bullet(Markdown(committed_text)))
|
|
285
|
+
self._committed_len += boundary
|
|
286
|
+
|
|
287
|
+
def _compose_spinner(self) -> Spinner:
|
|
288
|
+
elapsed = time.monotonic() - self._start_time
|
|
289
|
+
elapsed_str = format_elapsed(elapsed)
|
|
290
|
+
count_str = f"{format_token_count(int(self._token_count))} tokens"
|
|
291
|
+
|
|
292
|
+
self._spinner.text = Text.assemble(
|
|
293
|
+
("Composing...", ""),
|
|
294
|
+
(f" {elapsed_str}", "grey50"),
|
|
295
|
+
(f" · {count_str}", "grey50"),
|
|
296
|
+
)
|
|
297
|
+
return self._spinner
|
|
298
|
+
|
|
299
|
+
def _compose_thinking_stream(self) -> RenderableType:
|
|
300
|
+
"""Legacy 'Thinking...' spinner stacked over a 6-line scrolling preview."""
|
|
301
|
+
spinner = self._compose_thinking_spinner()
|
|
302
|
+
pending = self._pending_text()
|
|
303
|
+
if not pending:
|
|
304
|
+
return spinner
|
|
305
|
+
preview = self._build_preview(pending)
|
|
306
|
+
return Group(spinner, Text(preview, style="grey50 italic"))
|
|
307
|
+
|
|
308
|
+
def _compose_thinking_spinner(self) -> Spinner:
|
|
309
|
+
"""Legacy 'Thinking...' spinner header used by the stream-mode preview."""
|
|
310
|
+
elapsed = time.monotonic() - self._start_time
|
|
311
|
+
elapsed_str = format_elapsed(elapsed)
|
|
312
|
+
count_str = f"{format_token_count(int(self._token_count))} tokens"
|
|
313
|
+
self._spinner.text = Text.assemble(
|
|
314
|
+
("Thinking...", ""),
|
|
315
|
+
(f" {elapsed_str}", "grey50"),
|
|
316
|
+
(f" · {count_str}", "grey50"),
|
|
317
|
+
)
|
|
318
|
+
return self._spinner
|
|
319
|
+
|
|
320
|
+
def _build_preview(self, text: str) -> str:
|
|
321
|
+
"""Tail-trim *text* to the last ``_THINKING_PREVIEW_LINES`` and clamp width."""
|
|
322
|
+
max_width = console.width - 2 if console.width else 78
|
|
323
|
+
tail_text = _tail_lines(text, _THINKING_PREVIEW_LINES)
|
|
324
|
+
lines = tail_text.split("\n")
|
|
325
|
+
return "\n".join(_truncate_to_display_width(line, max_width) for line in lines)
|
|
326
|
+
|
|
327
|
+
def _compose_thinking(self) -> Text:
|
|
328
|
+
"""Render the thinking line: italic Thinking + bullets + metadata."""
|
|
329
|
+
elapsed = time.monotonic() - self._start_time
|
|
330
|
+
elapsed_str = format_elapsed(elapsed)
|
|
331
|
+
tokens_int = int(self._token_count)
|
|
332
|
+
count_str = f"{format_token_count(tokens_int)} tokens"
|
|
333
|
+
frame = _bullet_frame_for(elapsed)
|
|
334
|
+
|
|
335
|
+
parts: list[tuple[str, str | Style]] = [
|
|
336
|
+
("Thinking", "italic"),
|
|
337
|
+
(f" {frame}", "cyan"),
|
|
338
|
+
(f" {elapsed_str}", "grey50"),
|
|
339
|
+
(f" · {count_str}", "grey50"),
|
|
340
|
+
]
|
|
341
|
+
|
|
342
|
+
# Live tok/s pulse — a real heartbeat signal that confirms the model
|
|
343
|
+
# is still streaming even when the raw content is hidden.
|
|
344
|
+
if elapsed > 0.5 and tokens_int > 0:
|
|
345
|
+
rate = int(tokens_int / elapsed)
|
|
346
|
+
if rate > 0:
|
|
347
|
+
parts.append((f" · {rate} tok/s", "grey50"))
|
|
348
|
+
|
|
349
|
+
return Text.assemble(*parts)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
class _ToolCallBlock:
|
|
353
|
+
class FinishedSubCall(NamedTuple):
|
|
354
|
+
call: ToolCall
|
|
355
|
+
result: ToolReturnValue
|
|
356
|
+
|
|
357
|
+
def __init__(self, tool_call: ToolCall):
|
|
358
|
+
self._tool_name = tool_call.function.name
|
|
359
|
+
self._lexer = streamingjson.Lexer()
|
|
360
|
+
if tool_call.function.arguments is not None:
|
|
361
|
+
self._lexer.append_string(tool_call.function.arguments)
|
|
362
|
+
|
|
363
|
+
self._argument = extract_key_argument(self._lexer, self._tool_name)
|
|
364
|
+
self._full_url = self._extract_full_url(tool_call.function.arguments, self._tool_name)
|
|
365
|
+
self._result: ToolReturnValue | None = None
|
|
366
|
+
self._subagent_id: str | None = None
|
|
367
|
+
self._subagent_type: str | None = None
|
|
368
|
+
|
|
369
|
+
self._ongoing_subagent_tool_calls: dict[str, ToolCall] = {}
|
|
370
|
+
self._last_subagent_tool_call: ToolCall | None = None
|
|
371
|
+
self._n_finished_subagent_tool_calls = 0
|
|
372
|
+
self._finished_subagent_tool_calls = deque[_ToolCallBlock.FinishedSubCall](
|
|
373
|
+
maxlen=MAX_SUBAGENT_TOOL_CALLS_TO_SHOW
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
self._spinning_dots = Spinner("dots", text="")
|
|
377
|
+
self._renderable: RenderableType = self._compose()
|
|
378
|
+
|
|
379
|
+
def compose(self) -> RenderableType:
|
|
380
|
+
return self._renderable
|
|
381
|
+
|
|
382
|
+
@property
|
|
383
|
+
def finished(self) -> bool:
|
|
384
|
+
return self._result is not None
|
|
385
|
+
|
|
386
|
+
def append_args_part(self, args_part: str):
|
|
387
|
+
if self.finished:
|
|
388
|
+
return
|
|
389
|
+
self._lexer.append_string(args_part)
|
|
390
|
+
# TODO: maybe don't extract detail if it's already stable
|
|
391
|
+
argument = extract_key_argument(self._lexer, self._tool_name)
|
|
392
|
+
if argument and argument != self._argument:
|
|
393
|
+
self._argument = argument
|
|
394
|
+
self._full_url = self._extract_full_url(self._lexer.complete_json(), self._tool_name)
|
|
395
|
+
self._renderable = BulletColumns(
|
|
396
|
+
self._build_headline_text(),
|
|
397
|
+
bullet=self._spinning_dots,
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
def finish(self, result: ToolReturnValue):
|
|
401
|
+
self._result = result
|
|
402
|
+
self._renderable = self._compose()
|
|
403
|
+
|
|
404
|
+
def append_sub_tool_call(self, tool_call: ToolCall):
|
|
405
|
+
self._ongoing_subagent_tool_calls[tool_call.id] = tool_call
|
|
406
|
+
self._last_subagent_tool_call = tool_call
|
|
407
|
+
|
|
408
|
+
def append_sub_tool_call_part(self, tool_call_part: ToolCallPart):
|
|
409
|
+
if self._last_subagent_tool_call is None:
|
|
410
|
+
return
|
|
411
|
+
if not tool_call_part.arguments_part:
|
|
412
|
+
return
|
|
413
|
+
if self._last_subagent_tool_call.function.arguments is None:
|
|
414
|
+
self._last_subagent_tool_call.function.arguments = tool_call_part.arguments_part
|
|
415
|
+
else:
|
|
416
|
+
self._last_subagent_tool_call.function.arguments += tool_call_part.arguments_part
|
|
417
|
+
|
|
418
|
+
def finish_sub_tool_call(self, tool_result: ToolResult):
|
|
419
|
+
self._last_subagent_tool_call = None
|
|
420
|
+
sub_tool_call = self._ongoing_subagent_tool_calls.pop(tool_result.tool_call_id, None)
|
|
421
|
+
if sub_tool_call is None:
|
|
422
|
+
return
|
|
423
|
+
|
|
424
|
+
self._finished_subagent_tool_calls.append(
|
|
425
|
+
_ToolCallBlock.FinishedSubCall(
|
|
426
|
+
call=sub_tool_call,
|
|
427
|
+
result=tool_result.return_value,
|
|
428
|
+
)
|
|
429
|
+
)
|
|
430
|
+
self._n_finished_subagent_tool_calls += 1
|
|
431
|
+
self._renderable = self._compose()
|
|
432
|
+
|
|
433
|
+
def set_subagent_metadata(self, agent_id: str, subagent_type: str) -> None:
|
|
434
|
+
changed = (self._subagent_id, self._subagent_type) != (agent_id, subagent_type)
|
|
435
|
+
self._subagent_id = agent_id
|
|
436
|
+
self._subagent_type = subagent_type
|
|
437
|
+
if changed:
|
|
438
|
+
self._renderable = self._compose()
|
|
439
|
+
|
|
440
|
+
def _compose(self) -> RenderableType:
|
|
441
|
+
lines: list[RenderableType] = [
|
|
442
|
+
self._build_headline_text(),
|
|
443
|
+
]
|
|
444
|
+
if self._subagent_id is not None and self._subagent_type is not None:
|
|
445
|
+
lines.append(
|
|
446
|
+
BulletColumns(
|
|
447
|
+
Text(
|
|
448
|
+
f"subagent {self._subagent_type} ({self._subagent_id})",
|
|
449
|
+
style="grey50",
|
|
450
|
+
),
|
|
451
|
+
bullet_style="grey50",
|
|
452
|
+
)
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
if self._n_finished_subagent_tool_calls > MAX_SUBAGENT_TOOL_CALLS_TO_SHOW:
|
|
456
|
+
n_hidden = self._n_finished_subagent_tool_calls - MAX_SUBAGENT_TOOL_CALLS_TO_SHOW
|
|
457
|
+
lines.append(
|
|
458
|
+
BulletColumns(
|
|
459
|
+
Text(
|
|
460
|
+
f"{n_hidden} more tool call{'s' if n_hidden > 1 else ''} ...",
|
|
461
|
+
style="grey50 italic",
|
|
462
|
+
),
|
|
463
|
+
bullet_style="grey50",
|
|
464
|
+
)
|
|
465
|
+
)
|
|
466
|
+
for sub_call, sub_result in self._finished_subagent_tool_calls:
|
|
467
|
+
argument = extract_key_argument(
|
|
468
|
+
sub_call.function.arguments or "", sub_call.function.name
|
|
469
|
+
)
|
|
470
|
+
sub_url = self._extract_full_url(sub_call.function.arguments, sub_call.function.name)
|
|
471
|
+
sub_text = Text()
|
|
472
|
+
sub_text.append("Used ")
|
|
473
|
+
sub_text.append(sub_call.function.name, style="blue")
|
|
474
|
+
if argument:
|
|
475
|
+
sub_text.append(" (", style="grey50")
|
|
476
|
+
arg_style = Style(color="grey50", link=sub_url) if sub_url else "grey50"
|
|
477
|
+
sub_text.append(argument, style=arg_style)
|
|
478
|
+
sub_text.append(")", style="grey50")
|
|
479
|
+
lines.append(
|
|
480
|
+
BulletColumns(
|
|
481
|
+
sub_text,
|
|
482
|
+
bullet_style="green" if not sub_result.is_error else "dark_red",
|
|
483
|
+
)
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
if self._result is not None:
|
|
487
|
+
display = self._result.display
|
|
488
|
+
idx = 0
|
|
489
|
+
while idx < len(display):
|
|
490
|
+
block = display[idx]
|
|
491
|
+
if isinstance(block, DiffDisplayBlock):
|
|
492
|
+
# Collect consecutive same-file diff blocks
|
|
493
|
+
path = block.path
|
|
494
|
+
diff_blocks: list[DiffDisplayBlock] = []
|
|
495
|
+
while idx < len(display):
|
|
496
|
+
b = display[idx]
|
|
497
|
+
if not isinstance(b, DiffDisplayBlock) or b.path != path:
|
|
498
|
+
break
|
|
499
|
+
diff_blocks.append(b)
|
|
500
|
+
idx += 1
|
|
501
|
+
if any(b.is_summary for b in diff_blocks):
|
|
502
|
+
lines.append(render_diff_summary_panel(path, diff_blocks))
|
|
503
|
+
else:
|
|
504
|
+
hunks, added_total, removed_total = collect_diff_hunks(diff_blocks)
|
|
505
|
+
if hunks:
|
|
506
|
+
lines.append(render_diff_panel(path, hunks, added_total, removed_total))
|
|
507
|
+
elif isinstance(block, BriefDisplayBlock):
|
|
508
|
+
style = "grey50" if not self._result.is_error else "dark_red"
|
|
509
|
+
if block.text:
|
|
510
|
+
lines.append(Markdown(block.text, style=style))
|
|
511
|
+
idx += 1
|
|
512
|
+
elif isinstance(block, TodoDisplayBlock):
|
|
513
|
+
markdown = self._render_todo_markdown(block)
|
|
514
|
+
if markdown:
|
|
515
|
+
lines.append(Markdown(markdown, style="grey50"))
|
|
516
|
+
idx += 1
|
|
517
|
+
elif isinstance(block, BackgroundTaskDisplayBlock):
|
|
518
|
+
lines.append(
|
|
519
|
+
Markdown(
|
|
520
|
+
(f"`{block.task_id}` [{block.status}] {block.description}"),
|
|
521
|
+
style="grey50",
|
|
522
|
+
)
|
|
523
|
+
)
|
|
524
|
+
idx += 1
|
|
525
|
+
else:
|
|
526
|
+
idx += 1
|
|
527
|
+
|
|
528
|
+
if self.finished:
|
|
529
|
+
assert self._result is not None
|
|
530
|
+
return BulletColumns(
|
|
531
|
+
Group(*lines),
|
|
532
|
+
bullet_style="green" if not self._result.is_error else "dark_red",
|
|
533
|
+
)
|
|
534
|
+
else:
|
|
535
|
+
return BulletColumns(
|
|
536
|
+
Group(*lines),
|
|
537
|
+
bullet=self._spinning_dots,
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
@staticmethod
|
|
541
|
+
def _extract_full_url(arguments: str | None, tool_name: str) -> str | None:
|
|
542
|
+
"""Extract the full URL from FetchURL tool arguments."""
|
|
543
|
+
if tool_name != "FetchURL" or not arguments:
|
|
544
|
+
return None
|
|
545
|
+
try:
|
|
546
|
+
args = json.loads(arguments, strict=False)
|
|
547
|
+
except (json.JSONDecodeError, TypeError):
|
|
548
|
+
return None
|
|
549
|
+
if isinstance(args, dict):
|
|
550
|
+
url = cast(dict[str, Any], args).get("url")
|
|
551
|
+
if url:
|
|
552
|
+
return str(url)
|
|
553
|
+
return None
|
|
554
|
+
|
|
555
|
+
def _build_headline_text(self) -> Text:
|
|
556
|
+
text = Text()
|
|
557
|
+
text.append("Used " if self.finished else "Using ")
|
|
558
|
+
text.append(self._tool_name, style="blue")
|
|
559
|
+
if self._argument:
|
|
560
|
+
text.append(" (", style="grey50")
|
|
561
|
+
arg_style = Style(color="grey50", link=self._full_url) if self._full_url else "grey50"
|
|
562
|
+
text.append(self._argument, style=arg_style)
|
|
563
|
+
text.append(")", style="grey50")
|
|
564
|
+
return text
|
|
565
|
+
|
|
566
|
+
def _render_todo_markdown(self, block: TodoDisplayBlock) -> str:
|
|
567
|
+
lines: list[str] = []
|
|
568
|
+
for todo in block.items:
|
|
569
|
+
normalized = todo.status.replace("_", " ").lower()
|
|
570
|
+
match normalized:
|
|
571
|
+
case "pending":
|
|
572
|
+
lines.append(f"- {todo.title}")
|
|
573
|
+
case "in progress":
|
|
574
|
+
lines.append(f"- {todo.title} ←")
|
|
575
|
+
case "done":
|
|
576
|
+
lines.append(f"- ~~{todo.title}~~")
|
|
577
|
+
case _:
|
|
578
|
+
lines.append(f"- {todo.title}")
|
|
579
|
+
return "\n".join(lines)
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
class _NotificationBlock:
|
|
583
|
+
_SEVERITY_STYLE = {
|
|
584
|
+
"info": "cyan",
|
|
585
|
+
"success": "green",
|
|
586
|
+
"warning": "yellow",
|
|
587
|
+
"error": "red",
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
def __init__(self, notification: Notification):
|
|
591
|
+
self.notification = notification
|
|
592
|
+
|
|
593
|
+
def compose(self) -> RenderableType:
|
|
594
|
+
style = self._SEVERITY_STYLE.get(self.notification.severity, "cyan")
|
|
595
|
+
lines: list[RenderableType] = [Text(self.notification.title, style=f"bold {style}")]
|
|
596
|
+
body = self.notification.body.strip()
|
|
597
|
+
if body:
|
|
598
|
+
body_lines = body.splitlines()
|
|
599
|
+
preview = "\n".join(body_lines[:2])
|
|
600
|
+
if len(body_lines) > 2:
|
|
601
|
+
preview += "\n..."
|
|
602
|
+
lines.append(Text(preview, style="grey50"))
|
|
603
|
+
return BulletColumns(Group(*lines), bullet_style=style)
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
class _StatusBlock:
|
|
607
|
+
def __init__(self, initial: StatusUpdate) -> None:
|
|
608
|
+
self.text = Text("", justify="right")
|
|
609
|
+
self._context_usage: float = 0.0
|
|
610
|
+
self._context_tokens: int = 0
|
|
611
|
+
self._max_context_tokens: int = 0
|
|
612
|
+
self.update(initial)
|
|
613
|
+
|
|
614
|
+
def render(self) -> RenderableType:
|
|
615
|
+
return self.text
|
|
616
|
+
|
|
617
|
+
def update(self, status: StatusUpdate) -> None:
|
|
618
|
+
if status.context_usage is not None:
|
|
619
|
+
self._context_usage = status.context_usage
|
|
620
|
+
if status.context_tokens is not None:
|
|
621
|
+
self._context_tokens = status.context_tokens
|
|
622
|
+
if status.max_context_tokens is not None:
|
|
623
|
+
self._max_context_tokens = status.max_context_tokens
|
|
624
|
+
if status.context_usage is not None:
|
|
625
|
+
self.text.plain = format_context_status(
|
|
626
|
+
self._context_usage,
|
|
627
|
+
self._context_tokens,
|
|
628
|
+
self._max_context_tokens,
|
|
629
|
+
)
|