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
pythinker_code/app.py
ADDED
|
@@ -0,0 +1,820 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import contextlib
|
|
5
|
+
import dataclasses
|
|
6
|
+
import sys
|
|
7
|
+
import time
|
|
8
|
+
import warnings
|
|
9
|
+
from collections.abc import AsyncGenerator, Callable
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import TYPE_CHECKING, Any
|
|
12
|
+
|
|
13
|
+
import pythinker_host
|
|
14
|
+
from pydantic import SecretStr
|
|
15
|
+
from pythinker_host.path import HostPath
|
|
16
|
+
|
|
17
|
+
from pythinker_code.agentspec import DEFAULT_AGENT_FILE
|
|
18
|
+
from pythinker_code.auth.oauth import OAuthManager, get_device_id
|
|
19
|
+
from pythinker_code.background.models import is_terminal_status
|
|
20
|
+
from pythinker_code.cli import InputFormat, OutputFormat
|
|
21
|
+
from pythinker_code.config import Config, LLMModel, LLMProvider, load_config
|
|
22
|
+
from pythinker_code.constant import VERSION
|
|
23
|
+
from pythinker_code.llm import augment_provider_with_env_vars, create_llm, model_display_name
|
|
24
|
+
from pythinker_code.session import Session
|
|
25
|
+
from pythinker_code.share import get_share_dir
|
|
26
|
+
from pythinker_code.soul import RunCancelled, run_soul
|
|
27
|
+
from pythinker_code.soul.agent import Runtime, load_agent
|
|
28
|
+
from pythinker_code.soul.context import Context
|
|
29
|
+
from pythinker_code.soul.pythinkersoul import PythinkerSoul
|
|
30
|
+
from pythinker_code.utils.aioqueue import QueueShutDown
|
|
31
|
+
from pythinker_code.utils.envvar import get_env_bool
|
|
32
|
+
from pythinker_code.utils.logging import logger, open_original_stderr, redirect_stderr_to_logger
|
|
33
|
+
from pythinker_code.utils.path import shorten_home
|
|
34
|
+
from pythinker_code.wire import Wire, WireUISide
|
|
35
|
+
from pythinker_code.wire.types import ApprovalRequest, ApprovalResponse, ContentPart, WireMessage
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
from fastmcp.mcp_config import MCPConfig
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _patch_session_id(record: dict[str, Any]) -> None:
|
|
42
|
+
"""Inject the current session ID (from ContextVar) into log records."""
|
|
43
|
+
try:
|
|
44
|
+
from pythinker_code.soul.toolset import get_session_id
|
|
45
|
+
|
|
46
|
+
sid = get_session_id()
|
|
47
|
+
record["extra"]["sid"] = sid if sid else ""
|
|
48
|
+
except Exception:
|
|
49
|
+
record["extra"].setdefault("sid", "")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def enable_logging(debug: bool = False, *, redirect_stderr: bool = True) -> None:
|
|
53
|
+
# NOTE: stderr redirection is implemented by swapping the process-level fd=2 (dup2).
|
|
54
|
+
# That can hide Click/Typer error output during CLI startup, so some entrypoints delay
|
|
55
|
+
# installing it until after critical initialization succeeds.
|
|
56
|
+
logger.remove() # Remove default stderr handler
|
|
57
|
+
logger.enable("pythinker_code")
|
|
58
|
+
if debug:
|
|
59
|
+
logger.enable("pythinker_core")
|
|
60
|
+
logger.add(
|
|
61
|
+
get_share_dir() / "logs" / "pythinker.log",
|
|
62
|
+
# FIXME: configure level for different modules
|
|
63
|
+
level="TRACE" if debug else "INFO",
|
|
64
|
+
format=(
|
|
65
|
+
"{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | "
|
|
66
|
+
"{name}:{function}:{line} | {extra[sid]} - {message}"
|
|
67
|
+
),
|
|
68
|
+
rotation="06:00",
|
|
69
|
+
retention="10 days",
|
|
70
|
+
)
|
|
71
|
+
logger.configure(extra={"sid": ""}, patcher=_patch_session_id)
|
|
72
|
+
if redirect_stderr:
|
|
73
|
+
redirect_stderr_to_logger()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _write_original_stderr(text: str) -> None:
|
|
77
|
+
"""Write a user-facing notice to the terminal even if ``fd=2`` has been
|
|
78
|
+
redirected into the logger by ``redirect_stderr_to_logger``.
|
|
79
|
+
|
|
80
|
+
Falls back to ``sys.stderr`` when no redirector is installed (tests,
|
|
81
|
+
early-startup code paths), matching the semantics of ``_emit_fatal_error``
|
|
82
|
+
in ``cli/__init__.py``.
|
|
83
|
+
"""
|
|
84
|
+
with open_original_stderr() as stream:
|
|
85
|
+
if stream is not None:
|
|
86
|
+
stream.write(text.encode("utf-8", errors="replace"))
|
|
87
|
+
stream.flush()
|
|
88
|
+
return
|
|
89
|
+
sys.stderr.write(text)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
async def _refresh_managed_models_silent(config: Config) -> None:
|
|
93
|
+
from pythinker_code.auth.platforms import refresh_managed_models
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
await refresh_managed_models(config)
|
|
97
|
+
except Exception as exc:
|
|
98
|
+
logger.warning("Background managed-model refresh failed: {error}", error=exc)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _cleanup_stale_foreground_subagents(runtime: Runtime) -> None:
|
|
102
|
+
subagent_store = getattr(runtime, "subagent_store", None)
|
|
103
|
+
if subagent_store is None:
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
stale_agent_ids = [
|
|
107
|
+
record.agent_id
|
|
108
|
+
for record in subagent_store.list_instances()
|
|
109
|
+
if record.status == "running_foreground"
|
|
110
|
+
]
|
|
111
|
+
for agent_id in stale_agent_ids:
|
|
112
|
+
logger.warning(
|
|
113
|
+
"Marking stale foreground subagent instance as failed during startup: {agent_id}",
|
|
114
|
+
agent_id=agent_id,
|
|
115
|
+
)
|
|
116
|
+
subagent_store.update_instance(agent_id, status="failed")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class PythinkerCLI:
|
|
120
|
+
@staticmethod
|
|
121
|
+
async def create(
|
|
122
|
+
session: Session,
|
|
123
|
+
*,
|
|
124
|
+
# Basic configuration
|
|
125
|
+
config: Config | Path | None = None,
|
|
126
|
+
model_name: str | None = None,
|
|
127
|
+
thinking: bool | None = None,
|
|
128
|
+
# Run mode
|
|
129
|
+
yolo: bool = False,
|
|
130
|
+
auto: bool = False,
|
|
131
|
+
runtime_auto: bool = False,
|
|
132
|
+
plan_mode: bool = False,
|
|
133
|
+
resumed: bool = False,
|
|
134
|
+
ui_mode: str = "shell",
|
|
135
|
+
# Extensions
|
|
136
|
+
agent_file: Path | None = None,
|
|
137
|
+
mcp_configs: list[MCPConfig] | list[dict[str, Any]] | None = None,
|
|
138
|
+
skills_dirs: list[HostPath] | None = None,
|
|
139
|
+
# Loop control
|
|
140
|
+
max_steps_per_turn: int | None = None,
|
|
141
|
+
max_retries_per_step: int | None = None,
|
|
142
|
+
max_ralph_iterations: int | None = None,
|
|
143
|
+
startup_progress: Callable[[str], None] | None = None,
|
|
144
|
+
defer_mcp_loading: bool = False,
|
|
145
|
+
) -> PythinkerCLI:
|
|
146
|
+
"""
|
|
147
|
+
Create a PythinkerCLI instance.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
session (Session): A session created by `Session.create` or `Session.continue_`.
|
|
151
|
+
config (Config | Path | None, optional): Configuration to use, or path to config file.
|
|
152
|
+
Defaults to None.
|
|
153
|
+
model_name (str | None, optional): Name of the model to use. Defaults to None.
|
|
154
|
+
thinking (bool | None, optional): Whether to enable thinking mode. Defaults to None.
|
|
155
|
+
yolo (bool, optional): Dangerously skip permission approvals. The user is still
|
|
156
|
+
reachable via ``AskUserQuestion``. Defaults to False.
|
|
157
|
+
auto (bool, optional): Invocation-level auto mode (no user is present to answer
|
|
158
|
+
questions or approve actions). Implies auto-approve. Defaults to False.
|
|
159
|
+
runtime_auto (bool, optional): Internal invocation-only auto-mode overlay, used by
|
|
160
|
+
print mode so it stays non-interactive without changing persisted session auto
|
|
161
|
+
state. Defaults to False.
|
|
162
|
+
agent_file (Path | None, optional): Path to the agent file. Defaults to None.
|
|
163
|
+
mcp_configs (list[MCPConfig | dict[str, Any]] | None, optional): MCP configs to load
|
|
164
|
+
MCP tools from. Defaults to None.
|
|
165
|
+
skills_dirs (list[HostPath] | None, optional): Custom skills directories that
|
|
166
|
+
override default user/project discovery. Defaults to None.
|
|
167
|
+
max_steps_per_turn (int | None, optional): Maximum number of steps in one turn.
|
|
168
|
+
Defaults to None.
|
|
169
|
+
max_retries_per_step (int | None, optional): Maximum number of retries in one step.
|
|
170
|
+
Defaults to None.
|
|
171
|
+
max_ralph_iterations (int | None, optional): Extra iterations after the first turn in
|
|
172
|
+
Ralph mode. Defaults to None.
|
|
173
|
+
startup_progress (Callable[[str], None] | None, optional): Progress callback used by
|
|
174
|
+
interactive startup UI. Defaults to None.
|
|
175
|
+
defer_mcp_loading (bool, optional): Defer MCP startup until the interactive shell is
|
|
176
|
+
ready. Defaults to False.
|
|
177
|
+
|
|
178
|
+
Raises:
|
|
179
|
+
FileNotFoundError: When the agent file is not found.
|
|
180
|
+
ConfigError(PythinkerCLIException, ValueError): When the configuration is invalid.
|
|
181
|
+
AgentSpecError(PythinkerCLIException, ValueError): When the agent specification is
|
|
182
|
+
invalid.
|
|
183
|
+
SystemPromptTemplateError(PythinkerCLIException, ValueError): When the system prompt
|
|
184
|
+
template is invalid.
|
|
185
|
+
InvalidToolError(PythinkerCLIException, ValueError): When any tool cannot be loaded.
|
|
186
|
+
MCPConfigError(PythinkerCLIException, ValueError): When any MCP configuration is
|
|
187
|
+
invalid.
|
|
188
|
+
MCPRuntimeError(PythinkerCLIException, RuntimeError): When any MCP server cannot be
|
|
189
|
+
connected.
|
|
190
|
+
"""
|
|
191
|
+
_create_t0 = time.monotonic()
|
|
192
|
+
_phase_timings_ms: dict[str, int] = {}
|
|
193
|
+
|
|
194
|
+
if startup_progress is not None:
|
|
195
|
+
startup_progress("Loading configuration...")
|
|
196
|
+
|
|
197
|
+
_phase_t = time.monotonic()
|
|
198
|
+
config = config if isinstance(config, Config) else load_config(config)
|
|
199
|
+
_phase_timings_ms["config_ms"] = int((time.monotonic() - _phase_t) * 1000)
|
|
200
|
+
if max_steps_per_turn is not None:
|
|
201
|
+
config.loop_control.max_steps_per_turn = max_steps_per_turn
|
|
202
|
+
if max_retries_per_step is not None:
|
|
203
|
+
config.loop_control.max_retries_per_step = max_retries_per_step
|
|
204
|
+
if max_ralph_iterations is not None:
|
|
205
|
+
config.loop_control.max_ralph_iterations = max_ralph_iterations
|
|
206
|
+
logger.info("Loaded config: {config}", config=config)
|
|
207
|
+
|
|
208
|
+
_phase_t = time.monotonic()
|
|
209
|
+
oauth = OAuthManager(config)
|
|
210
|
+
|
|
211
|
+
bg_refresh_task = asyncio.create_task(_refresh_managed_models_silent(config))
|
|
212
|
+
|
|
213
|
+
model: LLMModel | None = None
|
|
214
|
+
provider: LLMProvider | None = None
|
|
215
|
+
|
|
216
|
+
# try to use config file
|
|
217
|
+
if not model_name and config.default_model:
|
|
218
|
+
# no --model specified && default model is set in config
|
|
219
|
+
model = config.models[config.default_model]
|
|
220
|
+
provider = config.providers[model.provider]
|
|
221
|
+
if model_name and model_name in config.models:
|
|
222
|
+
# --model specified && model is set in config
|
|
223
|
+
model = config.models[model_name]
|
|
224
|
+
provider = config.providers[model.provider]
|
|
225
|
+
|
|
226
|
+
if not model:
|
|
227
|
+
model = LLMModel(provider="", model="", max_context_size=100_000)
|
|
228
|
+
provider = LLMProvider(type="pythinker", base_url="", api_key=SecretStr(""))
|
|
229
|
+
|
|
230
|
+
# try overwrite with environment variables
|
|
231
|
+
assert provider is not None
|
|
232
|
+
assert model is not None
|
|
233
|
+
env_overrides = augment_provider_with_env_vars(provider, model, provider_key=model.provider)
|
|
234
|
+
|
|
235
|
+
# determine thinking mode
|
|
236
|
+
thinking = config.default_thinking if thinking is None else thinking
|
|
237
|
+
|
|
238
|
+
# determine yolo mode
|
|
239
|
+
yolo = yolo if yolo else config.default_yolo
|
|
240
|
+
|
|
241
|
+
# determine plan mode (only for new sessions, not restored)
|
|
242
|
+
if not resumed:
|
|
243
|
+
plan_mode = plan_mode if plan_mode else config.default_plan_mode
|
|
244
|
+
|
|
245
|
+
llm = create_llm(
|
|
246
|
+
provider,
|
|
247
|
+
model,
|
|
248
|
+
thinking=thinking,
|
|
249
|
+
session_id=session.id,
|
|
250
|
+
oauth=oauth,
|
|
251
|
+
)
|
|
252
|
+
if llm is not None:
|
|
253
|
+
logger.info("Using LLM provider: {provider}", provider=provider)
|
|
254
|
+
logger.info("Using LLM model: {model}", model=model)
|
|
255
|
+
logger.info("Thinking mode: {thinking}", thinking=thinking)
|
|
256
|
+
|
|
257
|
+
if startup_progress is not None:
|
|
258
|
+
startup_progress("Scanning workspace...")
|
|
259
|
+
|
|
260
|
+
runtime = await Runtime.create(
|
|
261
|
+
config,
|
|
262
|
+
oauth,
|
|
263
|
+
llm,
|
|
264
|
+
session,
|
|
265
|
+
yolo,
|
|
266
|
+
auto=auto,
|
|
267
|
+
runtime_auto=runtime_auto,
|
|
268
|
+
skills_dirs=skills_dirs,
|
|
269
|
+
)
|
|
270
|
+
runtime.ui_mode = ui_mode
|
|
271
|
+
runtime.resumed = resumed
|
|
272
|
+
runtime.notifications.recover()
|
|
273
|
+
runtime.background_tasks.reconcile()
|
|
274
|
+
_cleanup_stale_foreground_subagents(runtime)
|
|
275
|
+
_phase_timings_ms["init_ms"] = int((time.monotonic() - _phase_t) * 1000)
|
|
276
|
+
|
|
277
|
+
# Refresh plugin configs with fresh credentials (e.g. OAuth tokens)
|
|
278
|
+
try:
|
|
279
|
+
from pythinker_code.plugin.manager import (
|
|
280
|
+
collect_host_values,
|
|
281
|
+
get_plugins_dir,
|
|
282
|
+
refresh_plugin_configs,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
host_values = collect_host_values(config, oauth)
|
|
286
|
+
if host_values.get("api_key"):
|
|
287
|
+
refresh_plugin_configs(get_plugins_dir(), host_values)
|
|
288
|
+
except Exception:
|
|
289
|
+
logger.debug("Failed to refresh plugin configs, skipping")
|
|
290
|
+
|
|
291
|
+
if agent_file is None:
|
|
292
|
+
agent_file = DEFAULT_AGENT_FILE
|
|
293
|
+
if startup_progress is not None:
|
|
294
|
+
startup_progress("Loading agent...")
|
|
295
|
+
|
|
296
|
+
_phase_t = time.monotonic()
|
|
297
|
+
agent = await load_agent(
|
|
298
|
+
agent_file,
|
|
299
|
+
runtime,
|
|
300
|
+
mcp_configs=mcp_configs or [],
|
|
301
|
+
start_mcp_loading=not defer_mcp_loading,
|
|
302
|
+
)
|
|
303
|
+
_phase_timings_ms["mcp_ms"] = int((time.monotonic() - _phase_t) * 1000)
|
|
304
|
+
|
|
305
|
+
if startup_progress is not None:
|
|
306
|
+
startup_progress("Restoring conversation...")
|
|
307
|
+
context = Context(session.context_file)
|
|
308
|
+
await context.restore()
|
|
309
|
+
|
|
310
|
+
if context.system_prompt is not None:
|
|
311
|
+
agent = dataclasses.replace(agent, system_prompt=context.system_prompt)
|
|
312
|
+
else:
|
|
313
|
+
await context.write_system_prompt(agent.system_prompt)
|
|
314
|
+
|
|
315
|
+
soul = PythinkerSoul(agent, context=context)
|
|
316
|
+
|
|
317
|
+
# Activate plan mode if requested (for new sessions or --plan flag)
|
|
318
|
+
if plan_mode and not soul.plan_mode:
|
|
319
|
+
await soul.set_plan_mode_from_manual(True)
|
|
320
|
+
elif plan_mode and soul.plan_mode:
|
|
321
|
+
# Already in plan mode from restored session, trigger activation reminder
|
|
322
|
+
soul.schedule_plan_activation_reminder()
|
|
323
|
+
|
|
324
|
+
# Create and inject hook engine
|
|
325
|
+
from pythinker_code.hooks.engine import HookEngine
|
|
326
|
+
|
|
327
|
+
hook_engine = HookEngine(config.hooks, cwd=str(session.work_dir))
|
|
328
|
+
soul.set_hook_engine(hook_engine)
|
|
329
|
+
runtime.hook_engine = hook_engine
|
|
330
|
+
|
|
331
|
+
# --- Initialize telemetry ---
|
|
332
|
+
from pythinker_code.telemetry import attach_sink, set_context
|
|
333
|
+
from pythinker_code.telemetry import disable as disable_telemetry
|
|
334
|
+
|
|
335
|
+
telemetry_disabled = not config.telemetry or get_env_bool("PYTHINKER_DISABLE_TELEMETRY")
|
|
336
|
+
if telemetry_disabled:
|
|
337
|
+
disable_telemetry()
|
|
338
|
+
else:
|
|
339
|
+
device_id = get_device_id()
|
|
340
|
+
set_context(device_id=device_id, session_id=session.id)
|
|
341
|
+
|
|
342
|
+
# Initialize Sentry/Bugsink (errors) and OTel SDK (traces + logs)
|
|
343
|
+
# before the in-process EventSink picks up its first event.
|
|
344
|
+
from pythinker_code.telemetry import otel as _otel
|
|
345
|
+
from pythinker_code.telemetry import sentry as _sentry
|
|
346
|
+
|
|
347
|
+
_sentry.init(
|
|
348
|
+
version=VERSION,
|
|
349
|
+
device_id=device_id,
|
|
350
|
+
extra_tags={
|
|
351
|
+
"ui_mode": ui_mode,
|
|
352
|
+
"model": model.model if model else "",
|
|
353
|
+
},
|
|
354
|
+
)
|
|
355
|
+
_otel.init(version=VERSION, ui_mode=ui_mode, device_id=device_id)
|
|
356
|
+
|
|
357
|
+
from pythinker_code.telemetry.sink import EventSink
|
|
358
|
+
|
|
359
|
+
sink = EventSink(
|
|
360
|
+
version=VERSION,
|
|
361
|
+
model=model.model if model else "",
|
|
362
|
+
ui_mode=ui_mode,
|
|
363
|
+
)
|
|
364
|
+
attach_sink(sink)
|
|
365
|
+
|
|
366
|
+
from pythinker_code.telemetry import track, track_session_started_once
|
|
367
|
+
from pythinker_code.telemetry.crash import install_asyncio_handler, set_phase
|
|
368
|
+
|
|
369
|
+
# App init finished — enter runtime phase and hook asyncio crashes.
|
|
370
|
+
install_asyncio_handler()
|
|
371
|
+
set_phase("runtime")
|
|
372
|
+
|
|
373
|
+
if ui_mode != "wire":
|
|
374
|
+
track_session_started_once(ui_mode=ui_mode, resumed=resumed)
|
|
375
|
+
track(
|
|
376
|
+
"started",
|
|
377
|
+
resumed=resumed,
|
|
378
|
+
yolo=runtime.approval.is_yolo(),
|
|
379
|
+
auto=runtime.approval.is_auto(),
|
|
380
|
+
)
|
|
381
|
+
track(
|
|
382
|
+
"startup_perf",
|
|
383
|
+
duration_ms=int((time.monotonic() - _create_t0) * 1000),
|
|
384
|
+
config_ms=_phase_timings_ms.get("config_ms", 0),
|
|
385
|
+
init_ms=_phase_timings_ms.get("init_ms", 0),
|
|
386
|
+
mcp_ms=_phase_timings_ms.get("mcp_ms", 0),
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
return PythinkerCLI(soul, runtime, env_overrides, bg_refresh_task)
|
|
390
|
+
|
|
391
|
+
def __init__(
|
|
392
|
+
self,
|
|
393
|
+
_soul: PythinkerSoul,
|
|
394
|
+
_runtime: Runtime,
|
|
395
|
+
_env_overrides: dict[str, str],
|
|
396
|
+
_bg_refresh_task: asyncio.Task[None] | None = None,
|
|
397
|
+
) -> None:
|
|
398
|
+
self._soul = _soul
|
|
399
|
+
self._runtime = _runtime
|
|
400
|
+
self._env_overrides = _env_overrides
|
|
401
|
+
self._bg_refresh_task = _bg_refresh_task
|
|
402
|
+
|
|
403
|
+
@property
|
|
404
|
+
def soul(self) -> PythinkerSoul:
|
|
405
|
+
"""Get the PythinkerSoul instance."""
|
|
406
|
+
return self._soul
|
|
407
|
+
|
|
408
|
+
@property
|
|
409
|
+
def session(self) -> Session:
|
|
410
|
+
"""Get the Session instance."""
|
|
411
|
+
return self._runtime.session
|
|
412
|
+
|
|
413
|
+
async def shutdown_background_tasks(self) -> None:
|
|
414
|
+
"""Kill active background tasks on exit, unless keep_alive_on_exit is configured.
|
|
415
|
+
|
|
416
|
+
Prints a stderr notice naming each task so the user knows what is being
|
|
417
|
+
terminated, waits out the configured kill grace period so SIGTERM can
|
|
418
|
+
take effect, then reconciles and reports any workers that ignored the
|
|
419
|
+
signal.
|
|
420
|
+
|
|
421
|
+
This runs on the CLI's hard-shutdown path, so every failure mode must
|
|
422
|
+
be contained: disk IO errors from ``list_tasks`` / ``reconcile`` or
|
|
423
|
+
store corruption must not propagate and replace the real exit code
|
|
424
|
+
with a traceback.
|
|
425
|
+
"""
|
|
426
|
+
# Cancel the startup managed-model refresh task if it is still running
|
|
427
|
+
# so it does not outlive the CLI process.
|
|
428
|
+
if self._bg_refresh_task is not None and not self._bg_refresh_task.done():
|
|
429
|
+
self._bg_refresh_task.cancel()
|
|
430
|
+
|
|
431
|
+
bg_config = self._runtime.config.background
|
|
432
|
+
if bg_config.keep_alive_on_exit:
|
|
433
|
+
return
|
|
434
|
+
|
|
435
|
+
try:
|
|
436
|
+
manager = self._runtime.background_tasks
|
|
437
|
+
active_views = [
|
|
438
|
+
v
|
|
439
|
+
for v in manager.list_tasks(status=None, limit=None)
|
|
440
|
+
if not is_terminal_status(v.runtime.status)
|
|
441
|
+
]
|
|
442
|
+
if not active_views:
|
|
443
|
+
return
|
|
444
|
+
|
|
445
|
+
# Split by whether the task has already been kill-requested (e.g.
|
|
446
|
+
# by the ``--print`` timeout path which ran immediately before
|
|
447
|
+
# this shutdown). For those:
|
|
448
|
+
# - don't re-announce on stderr (user saw the timeout notice)
|
|
449
|
+
# - don't re-kill with a generic reason, which would overwrite
|
|
450
|
+
# the more specific ``kill_reason`` on disk
|
|
451
|
+
# We still reconcile + grace-wait for them so they reach terminal
|
|
452
|
+
# status before the process exits.
|
|
453
|
+
fresh_targets = [v for v in active_views if v.control.kill_requested_at is None]
|
|
454
|
+
|
|
455
|
+
if fresh_targets:
|
|
456
|
+
# Build and emit the kill notice via ``open_original_stderr``
|
|
457
|
+
# — ``sys.stderr.write`` alone would silently land in
|
|
458
|
+
# ``pythinker.log`` because ``redirect_stderr_to_logger`` has
|
|
459
|
+
# replaced fd=2 with a pipe into the logger by this point.
|
|
460
|
+
lines = [f"\u26a0 Killing {len(fresh_targets)} background tasks:\n"]
|
|
461
|
+
for view in fresh_targets:
|
|
462
|
+
description = view.spec.description or ""
|
|
463
|
+
if len(description) > 60:
|
|
464
|
+
description = description[:57] + "..."
|
|
465
|
+
lines.append(f" {view.spec.id} {description}\n")
|
|
466
|
+
_write_original_stderr("".join(lines))
|
|
467
|
+
|
|
468
|
+
killed: list[str] = []
|
|
469
|
+
for view in fresh_targets:
|
|
470
|
+
try:
|
|
471
|
+
manager.kill(view.spec.id, reason="CLI session ended")
|
|
472
|
+
killed.append(view.spec.id)
|
|
473
|
+
except Exception:
|
|
474
|
+
logger.exception(
|
|
475
|
+
"Failed to kill task {task_id} during shutdown",
|
|
476
|
+
task_id=view.spec.id,
|
|
477
|
+
)
|
|
478
|
+
if killed:
|
|
479
|
+
logger.info(
|
|
480
|
+
"Stopped {n} background task(s) on exit: {ids}",
|
|
481
|
+
n=len(killed),
|
|
482
|
+
ids=killed,
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
await asyncio.sleep(bg_config.kill_grace_period_ms / 1000)
|
|
486
|
+
manager.reconcile()
|
|
487
|
+
survivors = [
|
|
488
|
+
v
|
|
489
|
+
for v in manager.list_tasks(status=None, limit=None)
|
|
490
|
+
if not is_terminal_status(v.runtime.status)
|
|
491
|
+
]
|
|
492
|
+
if survivors:
|
|
493
|
+
# Distinguish "worker is mid-shutdown" (kill request on record,
|
|
494
|
+
# SIGTERM delivered, worker just hasn't written terminal state
|
|
495
|
+
# yet) from a genuine leak (never got kill-requested, i.e.
|
|
496
|
+
# ``manager.kill`` raised). Without this split, users saw
|
|
497
|
+
# ``killed N`` from the --print timeout path immediately
|
|
498
|
+
# followed by ``(N tasks still alive)`` here — a direct
|
|
499
|
+
# semantic contradiction.
|
|
500
|
+
terminating = [s for s in survivors if s.control.kill_requested_at is not None]
|
|
501
|
+
leaking = [s for s in survivors if s.control.kill_requested_at is None]
|
|
502
|
+
# Report leaks first — ``stop request failed`` is strictly
|
|
503
|
+
# more severe than ``still terminating`` (the latter will
|
|
504
|
+
# resolve on its own once the worker writes terminal state).
|
|
505
|
+
if leaking:
|
|
506
|
+
_write_original_stderr(
|
|
507
|
+
f" ({len(leaking)} tasks still running; stop request failed)\n"
|
|
508
|
+
)
|
|
509
|
+
if terminating:
|
|
510
|
+
_write_original_stderr(f" ({len(terminating)} tasks still terminating)\n")
|
|
511
|
+
except Exception:
|
|
512
|
+
logger.warning("Error during background task shutdown; continuing exit", exc_info=True)
|
|
513
|
+
|
|
514
|
+
async def await_bg_tasks_shutdown(self, timeout: float = 2.0) -> None:
|
|
515
|
+
"""Await completion of the model-refresh background task after cancellation."""
|
|
516
|
+
task = self._bg_refresh_task
|
|
517
|
+
if task is None or task.done():
|
|
518
|
+
return
|
|
519
|
+
# Best-effort cleanup — errors inside the task are already logged.
|
|
520
|
+
with contextlib.suppress(TimeoutError, asyncio.CancelledError, Exception):
|
|
521
|
+
await asyncio.wait_for(asyncio.shield(task), timeout=timeout)
|
|
522
|
+
|
|
523
|
+
@contextlib.asynccontextmanager
|
|
524
|
+
async def _env(self) -> AsyncGenerator[None]:
|
|
525
|
+
original_cwd = HostPath.cwd()
|
|
526
|
+
await pythinker_host.chdir(self._runtime.session.work_dir)
|
|
527
|
+
try:
|
|
528
|
+
# to ignore possible warnings from dateparser
|
|
529
|
+
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
530
|
+
async with self._runtime.oauth.refreshing(self._runtime):
|
|
531
|
+
yield
|
|
532
|
+
finally:
|
|
533
|
+
await pythinker_host.chdir(original_cwd)
|
|
534
|
+
|
|
535
|
+
async def run(
|
|
536
|
+
self,
|
|
537
|
+
user_input: str | list[ContentPart],
|
|
538
|
+
cancel_event: asyncio.Event,
|
|
539
|
+
merge_wire_messages: bool = False,
|
|
540
|
+
) -> AsyncGenerator[WireMessage]:
|
|
541
|
+
"""
|
|
542
|
+
Run the Pythinker CLI instance without any UI and yield Wire messages directly.
|
|
543
|
+
|
|
544
|
+
Args:
|
|
545
|
+
user_input (str | list[ContentPart]): The user input to the agent.
|
|
546
|
+
cancel_event (asyncio.Event): An event to cancel the run.
|
|
547
|
+
merge_wire_messages (bool): Whether to merge Wire messages as much as possible.
|
|
548
|
+
|
|
549
|
+
Yields:
|
|
550
|
+
WireMessage: The Wire messages from the `PythinkerSoul`.
|
|
551
|
+
|
|
552
|
+
Raises:
|
|
553
|
+
LLMNotSet: When the LLM is not set.
|
|
554
|
+
LLMNotSupported: When the LLM does not have required capabilities.
|
|
555
|
+
ChatProviderError: When the LLM provider returns an error.
|
|
556
|
+
MaxStepsReached: When the maximum number of steps is reached.
|
|
557
|
+
RunCancelled: When the run is cancelled by the cancel event.
|
|
558
|
+
"""
|
|
559
|
+
async with self._env():
|
|
560
|
+
wire_future = asyncio.Future[WireUISide]()
|
|
561
|
+
stop_ui_loop = asyncio.Event()
|
|
562
|
+
approval_bridge_tasks: dict[str, asyncio.Task[None]] = {}
|
|
563
|
+
forwarded_approval_requests: dict[str, ApprovalRequest] = {}
|
|
564
|
+
|
|
565
|
+
async def _bridge_approval_request(request: ApprovalRequest) -> None:
|
|
566
|
+
try:
|
|
567
|
+
response = await request.wait()
|
|
568
|
+
assert self._runtime.approval_runtime is not None
|
|
569
|
+
self._runtime.approval_runtime.resolve(
|
|
570
|
+
request.id, response, feedback=request.feedback
|
|
571
|
+
)
|
|
572
|
+
finally:
|
|
573
|
+
approval_bridge_tasks.pop(request.id, None)
|
|
574
|
+
forwarded_approval_requests.pop(request.id, None)
|
|
575
|
+
|
|
576
|
+
def _forward_approval_request(wire: Wire, request: ApprovalRequest) -> None:
|
|
577
|
+
if request.id in forwarded_approval_requests:
|
|
578
|
+
return
|
|
579
|
+
forwarded_approval_requests[request.id] = request
|
|
580
|
+
if request.id not in approval_bridge_tasks:
|
|
581
|
+
approval_bridge_tasks[request.id] = asyncio.create_task(
|
|
582
|
+
_bridge_approval_request(request)
|
|
583
|
+
)
|
|
584
|
+
wire.soul_side.send(request)
|
|
585
|
+
|
|
586
|
+
async def _ui_loop_fn(wire: Wire) -> None:
|
|
587
|
+
wire_future.set_result(wire.ui_side(merge=merge_wire_messages))
|
|
588
|
+
assert self._runtime.root_wire_hub is not None
|
|
589
|
+
assert self._runtime.approval_runtime is not None
|
|
590
|
+
root_hub_queue = self._runtime.root_wire_hub.subscribe()
|
|
591
|
+
stop_task = asyncio.create_task(stop_ui_loop.wait())
|
|
592
|
+
queue_task = asyncio.create_task(root_hub_queue.get())
|
|
593
|
+
try:
|
|
594
|
+
for pending in self._runtime.approval_runtime.list_pending():
|
|
595
|
+
_forward_approval_request(
|
|
596
|
+
wire,
|
|
597
|
+
ApprovalRequest(
|
|
598
|
+
id=pending.id,
|
|
599
|
+
tool_call_id=pending.tool_call_id,
|
|
600
|
+
sender=pending.sender,
|
|
601
|
+
action=pending.action,
|
|
602
|
+
description=pending.description,
|
|
603
|
+
display=pending.display,
|
|
604
|
+
source_kind=pending.source.kind,
|
|
605
|
+
source_id=pending.source.id,
|
|
606
|
+
agent_id=pending.source.agent_id,
|
|
607
|
+
subagent_type=pending.source.subagent_type,
|
|
608
|
+
),
|
|
609
|
+
)
|
|
610
|
+
while True:
|
|
611
|
+
done, _ = await asyncio.wait(
|
|
612
|
+
[stop_task, queue_task],
|
|
613
|
+
return_when=asyncio.FIRST_COMPLETED,
|
|
614
|
+
)
|
|
615
|
+
if stop_task in done:
|
|
616
|
+
break
|
|
617
|
+
try:
|
|
618
|
+
msg = queue_task.result()
|
|
619
|
+
except QueueShutDown:
|
|
620
|
+
break
|
|
621
|
+
match msg:
|
|
622
|
+
case ApprovalRequest() as request:
|
|
623
|
+
_forward_approval_request(wire, request)
|
|
624
|
+
queue_task = asyncio.create_task(root_hub_queue.get())
|
|
625
|
+
continue
|
|
626
|
+
case ApprovalResponse() as response:
|
|
627
|
+
if (
|
|
628
|
+
request := forwarded_approval_requests.get(response.request_id)
|
|
629
|
+
) and not request.resolved:
|
|
630
|
+
request.resolve(response.response, response.feedback)
|
|
631
|
+
case _:
|
|
632
|
+
pass
|
|
633
|
+
wire.soul_side.send(msg)
|
|
634
|
+
queue_task = asyncio.create_task(root_hub_queue.get())
|
|
635
|
+
finally:
|
|
636
|
+
stop_task.cancel()
|
|
637
|
+
queue_task.cancel()
|
|
638
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
639
|
+
await stop_task
|
|
640
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
641
|
+
await queue_task
|
|
642
|
+
for task in list(approval_bridge_tasks.values()):
|
|
643
|
+
task.cancel()
|
|
644
|
+
for task in list(approval_bridge_tasks.values()):
|
|
645
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
646
|
+
await task
|
|
647
|
+
approval_bridge_tasks.clear()
|
|
648
|
+
forwarded_approval_requests.clear()
|
|
649
|
+
assert self._runtime.root_wire_hub is not None
|
|
650
|
+
self._runtime.root_wire_hub.unsubscribe(root_hub_queue)
|
|
651
|
+
|
|
652
|
+
run_cancel_event = asyncio.Event()
|
|
653
|
+
|
|
654
|
+
async def _mirror_external_cancel() -> None:
|
|
655
|
+
await cancel_event.wait()
|
|
656
|
+
run_cancel_event.set()
|
|
657
|
+
|
|
658
|
+
external_cancel_task = asyncio.create_task(
|
|
659
|
+
_mirror_external_cancel(),
|
|
660
|
+
name="cancel-event-mirror",
|
|
661
|
+
)
|
|
662
|
+
soul_task = asyncio.create_task(
|
|
663
|
+
run_soul(
|
|
664
|
+
self.soul,
|
|
665
|
+
user_input,
|
|
666
|
+
_ui_loop_fn,
|
|
667
|
+
run_cancel_event,
|
|
668
|
+
runtime=self._runtime,
|
|
669
|
+
)
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
wire_shut_down = False
|
|
673
|
+
try:
|
|
674
|
+
wire_ui = await wire_future
|
|
675
|
+
while True:
|
|
676
|
+
msg = await wire_ui.receive()
|
|
677
|
+
yield msg
|
|
678
|
+
except QueueShutDown:
|
|
679
|
+
wire_shut_down = True
|
|
680
|
+
pass
|
|
681
|
+
finally:
|
|
682
|
+
# stop consuming Wire messages
|
|
683
|
+
stop_ui_loop.set()
|
|
684
|
+
cleanup_cancelled_run = False
|
|
685
|
+
if not wire_shut_down and not soul_task.done() and not cancel_event.is_set():
|
|
686
|
+
cleanup_cancelled_run = True
|
|
687
|
+
run_cancel_event.set()
|
|
688
|
+
# wait for the soul task to finish, or raise
|
|
689
|
+
try:
|
|
690
|
+
await soul_task
|
|
691
|
+
except RunCancelled:
|
|
692
|
+
if not cleanup_cancelled_run:
|
|
693
|
+
raise
|
|
694
|
+
finally:
|
|
695
|
+
external_cancel_task.cancel()
|
|
696
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
697
|
+
await external_cancel_task
|
|
698
|
+
|
|
699
|
+
async def run_shell(
|
|
700
|
+
self, command: str | None = None, *, prefill_text: str | None = None
|
|
701
|
+
) -> bool:
|
|
702
|
+
"""Run the Pythinker CLI instance with shell UI."""
|
|
703
|
+
from pythinker_code.ui.shell import Shell, WelcomeInfoItem
|
|
704
|
+
|
|
705
|
+
if command is None:
|
|
706
|
+
from pythinker_code.ui.shell.update import check_update_gate
|
|
707
|
+
|
|
708
|
+
check_update_gate()
|
|
709
|
+
|
|
710
|
+
welcome_info = [
|
|
711
|
+
WelcomeInfoItem(
|
|
712
|
+
name="Directory", value=str(shorten_home(self._runtime.session.work_dir))
|
|
713
|
+
),
|
|
714
|
+
WelcomeInfoItem(name="Session", value=self._runtime.session.id),
|
|
715
|
+
]
|
|
716
|
+
if base_url := self._env_overrides.get("PYTHINKER_BASE_URL"):
|
|
717
|
+
welcome_info.append(
|
|
718
|
+
WelcomeInfoItem(
|
|
719
|
+
name="API URL",
|
|
720
|
+
value=f"{base_url} (from PYTHINKER_BASE_URL)",
|
|
721
|
+
level=WelcomeInfoItem.Level.WARN,
|
|
722
|
+
)
|
|
723
|
+
)
|
|
724
|
+
if self._env_overrides.get("PYTHINKER_API_KEY"):
|
|
725
|
+
welcome_info.append(
|
|
726
|
+
WelcomeInfoItem(
|
|
727
|
+
name="API Key",
|
|
728
|
+
value="****** (from PYTHINKER_API_KEY)",
|
|
729
|
+
level=WelcomeInfoItem.Level.WARN,
|
|
730
|
+
)
|
|
731
|
+
)
|
|
732
|
+
if not self._runtime.llm:
|
|
733
|
+
welcome_info.append(
|
|
734
|
+
WelcomeInfoItem(
|
|
735
|
+
name="Model",
|
|
736
|
+
value="not set, send /login to login",
|
|
737
|
+
level=WelcomeInfoItem.Level.WARN,
|
|
738
|
+
)
|
|
739
|
+
)
|
|
740
|
+
elif "PYTHINKER_MODEL_NAME" in self._env_overrides:
|
|
741
|
+
welcome_info.append(
|
|
742
|
+
WelcomeInfoItem(
|
|
743
|
+
name="Model",
|
|
744
|
+
value=f"{self._soul.model_name} (from PYTHINKER_MODEL_NAME)",
|
|
745
|
+
level=WelcomeInfoItem.Level.WARN,
|
|
746
|
+
)
|
|
747
|
+
)
|
|
748
|
+
else:
|
|
749
|
+
welcome_info.append(
|
|
750
|
+
WelcomeInfoItem(
|
|
751
|
+
name="Model",
|
|
752
|
+
value=model_display_name(
|
|
753
|
+
self._soul.model_name,
|
|
754
|
+
self._runtime.llm.model_config if self._runtime.llm else None,
|
|
755
|
+
),
|
|
756
|
+
level=WelcomeInfoItem.Level.INFO,
|
|
757
|
+
)
|
|
758
|
+
)
|
|
759
|
+
model_name = self._soul.model_name
|
|
760
|
+
if model_name not in (
|
|
761
|
+
"pythinker-for-coding",
|
|
762
|
+
"pythinker-code",
|
|
763
|
+
) and not model_name.startswith("pythinker-ai"):
|
|
764
|
+
welcome_info.append(
|
|
765
|
+
WelcomeInfoItem(
|
|
766
|
+
name="Tip",
|
|
767
|
+
value="send /login to use Pythinker for Coding",
|
|
768
|
+
level=WelcomeInfoItem.Level.WARN,
|
|
769
|
+
)
|
|
770
|
+
)
|
|
771
|
+
welcome_info.append(
|
|
772
|
+
WelcomeInfoItem(
|
|
773
|
+
name="Tip",
|
|
774
|
+
value=(
|
|
775
|
+
"Spot a bug or have feedback? Type /feedback right in this session"
|
|
776
|
+
" — every report makes Pythinker better."
|
|
777
|
+
),
|
|
778
|
+
level=WelcomeInfoItem.Level.INFO,
|
|
779
|
+
)
|
|
780
|
+
)
|
|
781
|
+
async with self._env():
|
|
782
|
+
shell = Shell(self._soul, welcome_info=welcome_info, prefill_text=prefill_text)
|
|
783
|
+
return await shell.run(command)
|
|
784
|
+
|
|
785
|
+
async def run_print(
|
|
786
|
+
self,
|
|
787
|
+
input_format: InputFormat,
|
|
788
|
+
output_format: OutputFormat,
|
|
789
|
+
command: str | None = None,
|
|
790
|
+
*,
|
|
791
|
+
final_only: bool = False,
|
|
792
|
+
) -> int:
|
|
793
|
+
"""Run the Pythinker CLI instance with print UI."""
|
|
794
|
+
from pythinker_code.ui.print import Print
|
|
795
|
+
|
|
796
|
+
async with self._env():
|
|
797
|
+
print_ = Print(
|
|
798
|
+
self._soul,
|
|
799
|
+
input_format,
|
|
800
|
+
output_format,
|
|
801
|
+
self._runtime.session.context_file,
|
|
802
|
+
final_only=final_only,
|
|
803
|
+
)
|
|
804
|
+
return await print_.run(command)
|
|
805
|
+
|
|
806
|
+
async def run_acp(self) -> None:
|
|
807
|
+
"""Run the Pythinker CLI instance as ACP server."""
|
|
808
|
+
from pythinker_code.ui.acp import ACP
|
|
809
|
+
|
|
810
|
+
async with self._env():
|
|
811
|
+
acp = ACP(self._soul)
|
|
812
|
+
await acp.run()
|
|
813
|
+
|
|
814
|
+
async def run_wire_stdio(self) -> None:
|
|
815
|
+
"""Run the Pythinker CLI instance as Wire server over stdio."""
|
|
816
|
+
from pythinker_code.wire.server import WireServer
|
|
817
|
+
|
|
818
|
+
async with self._env():
|
|
819
|
+
server = WireServer(self._soul)
|
|
820
|
+
await server.serve()
|