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,474 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
from functools import partial
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from pythinker_core.chat_provider import (
|
|
11
|
+
APIConnectionError,
|
|
12
|
+
APIEmptyResponseError,
|
|
13
|
+
APIStatusError,
|
|
14
|
+
APITimeoutError,
|
|
15
|
+
ChatProviderError,
|
|
16
|
+
)
|
|
17
|
+
from pythinker_core.message import Message
|
|
18
|
+
from rich import print
|
|
19
|
+
|
|
20
|
+
from pythinker_code.background.models import is_terminal_status
|
|
21
|
+
from pythinker_code.cli import ExitCode, InputFormat, OutputFormat
|
|
22
|
+
from pythinker_code.soul import (
|
|
23
|
+
LLMNotSet,
|
|
24
|
+
LLMNotSupported,
|
|
25
|
+
MaxStepsReached,
|
|
26
|
+
RunCancelled,
|
|
27
|
+
Soul,
|
|
28
|
+
run_soul,
|
|
29
|
+
)
|
|
30
|
+
from pythinker_code.soul.pythinkersoul import PythinkerSoul
|
|
31
|
+
from pythinker_code.ui.print.visualize import visualize
|
|
32
|
+
from pythinker_code.utils.logging import logger, open_original_stderr
|
|
33
|
+
from pythinker_code.utils.signals import install_sigint_handler
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Print:
|
|
37
|
+
"""
|
|
38
|
+
An app implementation that prints the agent behavior to the console.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
soul (Soul): The soul to run.
|
|
42
|
+
input_format (InputFormat): The input format to use.
|
|
43
|
+
output_format (OutputFormat): The output format to use.
|
|
44
|
+
context_file (Path): The file to store the context.
|
|
45
|
+
final_only (bool): Whether to only print the final assistant message.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(
|
|
49
|
+
self,
|
|
50
|
+
soul: Soul,
|
|
51
|
+
input_format: InputFormat,
|
|
52
|
+
output_format: OutputFormat,
|
|
53
|
+
context_file: Path,
|
|
54
|
+
*,
|
|
55
|
+
final_only: bool = False,
|
|
56
|
+
):
|
|
57
|
+
self.soul = soul
|
|
58
|
+
self.input_format: InputFormat = input_format
|
|
59
|
+
self.output_format: OutputFormat = output_format
|
|
60
|
+
self.context_file = context_file
|
|
61
|
+
self.final_only = final_only
|
|
62
|
+
|
|
63
|
+
async def run(self, command: str | None = None) -> int:
|
|
64
|
+
cancel_event = asyncio.Event()
|
|
65
|
+
|
|
66
|
+
def _handler():
|
|
67
|
+
logger.debug("SIGINT received.")
|
|
68
|
+
cancel_event.set()
|
|
69
|
+
|
|
70
|
+
loop = asyncio.get_running_loop()
|
|
71
|
+
remove_sigint = install_sigint_handler(loop, _handler)
|
|
72
|
+
|
|
73
|
+
if command is None and not sys.stdin.isatty() and self.input_format == "text":
|
|
74
|
+
command = sys.stdin.read().strip()
|
|
75
|
+
logger.info("Read command from stdin: {command}", command=command)
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
while True:
|
|
79
|
+
if command is None:
|
|
80
|
+
if self.input_format == "text":
|
|
81
|
+
return ExitCode.SUCCESS
|
|
82
|
+
else:
|
|
83
|
+
assert self.input_format == "stream-json"
|
|
84
|
+
command = self._read_next_command()
|
|
85
|
+
if command is None:
|
|
86
|
+
return ExitCode.SUCCESS
|
|
87
|
+
|
|
88
|
+
if command:
|
|
89
|
+
logger.info("Running agent with command: {command}", command=command)
|
|
90
|
+
if self.output_format == "text" and not self.final_only:
|
|
91
|
+
print(command)
|
|
92
|
+
runtime = self.soul.runtime if isinstance(self.soul, PythinkerSoul) else None
|
|
93
|
+
await run_soul(
|
|
94
|
+
self.soul,
|
|
95
|
+
command,
|
|
96
|
+
partial(visualize, self.output_format, self.final_only),
|
|
97
|
+
cancel_event,
|
|
98
|
+
runtime.session.wire_file if runtime else None,
|
|
99
|
+
runtime,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# In one-shot text mode the process exits after this
|
|
103
|
+
# function returns, which would kill still-running
|
|
104
|
+
# background agents. Poll until they finish, calling
|
|
105
|
+
# reconcile() each iteration (the notification pump
|
|
106
|
+
# inside run_soul is no longer running, so we must
|
|
107
|
+
# drive reconcile ourselves to recover lost workers
|
|
108
|
+
# and publish terminal notifications). Only re-enter
|
|
109
|
+
# the soul when there are pending LLM notifications.
|
|
110
|
+
#
|
|
111
|
+
# stream-json mode is multi-turn: background tasks
|
|
112
|
+
# from one command must not block the next command.
|
|
113
|
+
#
|
|
114
|
+
# keep_alive_on_exit opts into "fire and forget"
|
|
115
|
+
# semantics: background tasks are meant to outlive
|
|
116
|
+
# the CLI process, so Print must not block waiting
|
|
117
|
+
# for them.
|
|
118
|
+
if (
|
|
119
|
+
runtime
|
|
120
|
+
and runtime.role == "root"
|
|
121
|
+
and self.input_format == "text"
|
|
122
|
+
and not runtime.config.background.keep_alive_on_exit
|
|
123
|
+
):
|
|
124
|
+
manager = runtime.background_tasks
|
|
125
|
+
notifications = runtime.notifications
|
|
126
|
+
bg_config = runtime.config.background
|
|
127
|
+
|
|
128
|
+
# Snapshot the active tasks at the moment we start
|
|
129
|
+
# waiting. Used both to derive the wait cap and to
|
|
130
|
+
# name the specific tasks in the timeout follow-up
|
|
131
|
+
# prompt. The cap is the longest remaining task
|
|
132
|
+
# budget, clipped to the global ceiling — buggy
|
|
133
|
+
# tasks that never self-terminate can't hang the
|
|
134
|
+
# CLI forever.
|
|
135
|
+
initial_views = [
|
|
136
|
+
v
|
|
137
|
+
for v in manager.list_tasks(status=None, limit=None)
|
|
138
|
+
if not is_terminal_status(v.runtime.status)
|
|
139
|
+
]
|
|
140
|
+
if initial_views:
|
|
141
|
+
agent_default = bg_config.agent_task_timeout_s
|
|
142
|
+
# Use an explicit None check — the falsy idiom
|
|
143
|
+
# ``v.spec.timeout_s or agent_default`` would
|
|
144
|
+
# silently promote a legitimate ``timeout_s=0``
|
|
145
|
+
# to the agent default (900s), contradicting
|
|
146
|
+
# the caller's explicit intent.
|
|
147
|
+
task_timeouts = [
|
|
148
|
+
v.spec.timeout_s if v.spec.timeout_s is not None else agent_default
|
|
149
|
+
for v in initial_views
|
|
150
|
+
]
|
|
151
|
+
wait_cap = min(max(task_timeouts), bg_config.print_wait_ceiling_s)
|
|
152
|
+
else:
|
|
153
|
+
# No active tasks at snapshot time — fall back to
|
|
154
|
+
# the ceiling so a pending re-entry that spawns
|
|
155
|
+
# new long-running work still has a deadline.
|
|
156
|
+
# Otherwise ``--print`` can hang forever on
|
|
157
|
+
# mid-wait task creation.
|
|
158
|
+
wait_cap = bg_config.print_wait_ceiling_s
|
|
159
|
+
deadline = time.monotonic() + wait_cap
|
|
160
|
+
timed_out = False
|
|
161
|
+
|
|
162
|
+
while not cancel_event.is_set():
|
|
163
|
+
# Drive reconcile() ourselves: the notification
|
|
164
|
+
# pump inside run_soul is no longer running, so
|
|
165
|
+
# we must recover lost workers and publish
|
|
166
|
+
# terminal notifications here.
|
|
167
|
+
manager.reconcile()
|
|
168
|
+
if notifications.has_pending_for_sink("llm"):
|
|
169
|
+
# Re-enter soul so the LLM can process the
|
|
170
|
+
# completion notification. Do this even if
|
|
171
|
+
# other tasks are still active — progress on
|
|
172
|
+
# completed tasks should not wait on siblings.
|
|
173
|
+
# Pending notifications are checked BEFORE
|
|
174
|
+
# the deadline: a late-completing task
|
|
175
|
+
# should not lose its result just because
|
|
176
|
+
# the cap has been breached.
|
|
177
|
+
bg_prompt = (
|
|
178
|
+
"<system-reminder>"
|
|
179
|
+
"Background tasks have completed."
|
|
180
|
+
" Process their results."
|
|
181
|
+
"</system-reminder>"
|
|
182
|
+
)
|
|
183
|
+
# Bypass ``UserPromptSubmit`` — this is an
|
|
184
|
+
# internal synthetic prompt, not user input.
|
|
185
|
+
# A user-configured prompt-blocking hook
|
|
186
|
+
# would drop the notification and hang the
|
|
187
|
+
# wait loop.
|
|
188
|
+
#
|
|
189
|
+
# Transient LLM failures here must not flip
|
|
190
|
+
# the original command's exit code: the
|
|
191
|
+
# user's real command (first ``run_soul``
|
|
192
|
+
# call) already succeeded — a provider
|
|
193
|
+
# outage while acknowledging a background
|
|
194
|
+
# notification is not a command failure.
|
|
195
|
+
# Let ``RunCancelled`` bubble (Ctrl+C
|
|
196
|
+
# semantics); for anything else, drain the
|
|
197
|
+
# pending notifications for this sink so
|
|
198
|
+
# the loop doesn't tight-loop on the same
|
|
199
|
+
# failing notification, and ``continue`` so
|
|
200
|
+
# OTHER active tasks can still be waited on
|
|
201
|
+
# (breaking would abandon them and let
|
|
202
|
+
# shutdown force-kill them).
|
|
203
|
+
try:
|
|
204
|
+
await run_soul(
|
|
205
|
+
self.soul,
|
|
206
|
+
bg_prompt,
|
|
207
|
+
partial(visualize, self.output_format, self.final_only),
|
|
208
|
+
cancel_event,
|
|
209
|
+
runtime.session.wire_file,
|
|
210
|
+
runtime,
|
|
211
|
+
skip_user_prompt_hook=True,
|
|
212
|
+
)
|
|
213
|
+
except RunCancelled:
|
|
214
|
+
raise
|
|
215
|
+
except Exception:
|
|
216
|
+
logger.warning(
|
|
217
|
+
"Pending notification re-entry failed;"
|
|
218
|
+
" draining pending notifications and"
|
|
219
|
+
" continuing to wait for remaining tasks",
|
|
220
|
+
exc_info=True,
|
|
221
|
+
)
|
|
222
|
+
# Force-drain pending LLM notifications
|
|
223
|
+
# so the loop does not tight-loop on the
|
|
224
|
+
# same failing notification. They are
|
|
225
|
+
# effectively lost (the LLM never
|
|
226
|
+
# acknowledged them), but siblings can
|
|
227
|
+
# still be waited for and the original
|
|
228
|
+
# success exit code is preserved.
|
|
229
|
+
while True:
|
|
230
|
+
claimed = notifications.claim_for_sink("llm", limit=8)
|
|
231
|
+
if not claimed:
|
|
232
|
+
break
|
|
233
|
+
for view in claimed:
|
|
234
|
+
notifications.ack("llm", view.event.id)
|
|
235
|
+
continue
|
|
236
|
+
if not manager.has_active_tasks():
|
|
237
|
+
# Re-check once after noticing no active
|
|
238
|
+
# tasks: a worker may have finished between
|
|
239
|
+
# the reconcile above and this snapshot,
|
|
240
|
+
# leaving a terminal state on disk that we
|
|
241
|
+
# haven't published yet. Without this
|
|
242
|
+
# second reconcile+pending check, that
|
|
243
|
+
# final completion notification would be
|
|
244
|
+
# lost when the process exits.
|
|
245
|
+
manager.reconcile()
|
|
246
|
+
if notifications.has_pending_for_sink("llm"):
|
|
247
|
+
continue
|
|
248
|
+
break
|
|
249
|
+
# Timeout check runs only when tasks are still
|
|
250
|
+
# active: a loop iteration that would otherwise
|
|
251
|
+
# break out cleanly must not be redirected into
|
|
252
|
+
# the kill path just because the clock has
|
|
253
|
+
# moved past the deadline (e.g. after a long
|
|
254
|
+
# part-complete re-entry). Using
|
|
255
|
+
# ``has_active_tasks()`` rather than the
|
|
256
|
+
# ``initial_views`` snapshot ensures tasks
|
|
257
|
+
# spawned by a pending re-entry are also bound
|
|
258
|
+
# by the deadline.
|
|
259
|
+
if time.monotonic() >= deadline:
|
|
260
|
+
# Race: the last task may have transitioned
|
|
261
|
+
# to terminal state on disk in the tiny
|
|
262
|
+
# window between the ``has_active_tasks()``
|
|
263
|
+
# check above and this deadline test. Do
|
|
264
|
+
# one final reconcile + pending/active
|
|
265
|
+
# re-check so a natural near-deadline
|
|
266
|
+
# completion exits via the success path
|
|
267
|
+
# instead of the (spurious) kill-and-
|
|
268
|
+
# FAILURE path.
|
|
269
|
+
manager.reconcile()
|
|
270
|
+
if notifications.has_pending_for_sink("llm"):
|
|
271
|
+
continue
|
|
272
|
+
if not manager.has_active_tasks():
|
|
273
|
+
break
|
|
274
|
+
timed_out = True
|
|
275
|
+
break
|
|
276
|
+
# Still waiting for tasks to finish.
|
|
277
|
+
await asyncio.sleep(1.0)
|
|
278
|
+
|
|
279
|
+
if cancel_event.is_set():
|
|
280
|
+
raise RunCancelled
|
|
281
|
+
|
|
282
|
+
if timed_out:
|
|
283
|
+
# Re-read the active list at timeout time so
|
|
284
|
+
# tasks spawned mid-wait (e.g. by a part-
|
|
285
|
+
# complete re-entry into the soul) are named
|
|
286
|
+
# in the follow-up prompt too.
|
|
287
|
+
timed_out_views = [
|
|
288
|
+
v
|
|
289
|
+
for v in manager.list_tasks(status=None, limit=None)
|
|
290
|
+
if not is_terminal_status(v.runtime.status)
|
|
291
|
+
]
|
|
292
|
+
killed = manager.kill_all_active(reason="print_wait_timeout")
|
|
293
|
+
# ``sys.stderr`` has been redirected to the
|
|
294
|
+
# logger pipe at this point in the CLI
|
|
295
|
+
# lifecycle, so writing directly to it would
|
|
296
|
+
# silently land in ``pythinker.log``. Use the
|
|
297
|
+
# pre-redirect fd to surface the notice on the
|
|
298
|
+
# user's terminal.
|
|
299
|
+
timeout_notice = (
|
|
300
|
+
f"timed out waiting for background tasks "
|
|
301
|
+
f"({wait_cap}s), killed {len(killed)} tasks\n"
|
|
302
|
+
)
|
|
303
|
+
with open_original_stderr() as stream:
|
|
304
|
+
if stream is not None:
|
|
305
|
+
stream.write(timeout_notice.encode("utf-8", errors="replace"))
|
|
306
|
+
stream.flush()
|
|
307
|
+
else:
|
|
308
|
+
sys.stderr.write(timeout_notice)
|
|
309
|
+
# Label each task by its real post-kill state
|
|
310
|
+
# instead of a blanket ``(killed)``. Reasons
|
|
311
|
+
# the naive label is wrong:
|
|
312
|
+
# 1. ``kill_all_active`` appends the id even
|
|
313
|
+
# when ``kill()`` early-returns because
|
|
314
|
+
# the task was already terminal (natural
|
|
315
|
+
# completion race between the snapshot
|
|
316
|
+
# and the kill loop).
|
|
317
|
+
# 2. ``kill()`` can also raise after sending
|
|
318
|
+
# SIGTERM (e.g. merged_view IO failure),
|
|
319
|
+
# so absence from ``killed`` ≠ "no kill
|
|
320
|
+
# was attempted".
|
|
321
|
+
# Re-read the current state after kill_all_active
|
|
322
|
+
# and classify each task separately:
|
|
323
|
+
# - already-terminal → ``already finished``
|
|
324
|
+
# - kill_requested_at set → ``kill requested``
|
|
325
|
+
# - neither → ``kill failed``
|
|
326
|
+
post_kill_views = {
|
|
327
|
+
v.spec.id: v for v in manager.list_tasks(status=None, limit=None)
|
|
328
|
+
}
|
|
329
|
+
label_lines: list[str] = []
|
|
330
|
+
for v in timed_out_views:
|
|
331
|
+
latest = post_kill_views.get(v.spec.id, v)
|
|
332
|
+
if is_terminal_status(latest.runtime.status):
|
|
333
|
+
label = "already finished"
|
|
334
|
+
elif latest.control.kill_requested_at is not None:
|
|
335
|
+
label = "kill requested"
|
|
336
|
+
else:
|
|
337
|
+
label = "kill failed"
|
|
338
|
+
label_lines.append(
|
|
339
|
+
f" - {v.spec.id}: {v.spec.description} ({label})"
|
|
340
|
+
)
|
|
341
|
+
task_lines = "\n".join(label_lines)
|
|
342
|
+
timeout_prompt = (
|
|
343
|
+
"<system-reminder>\n"
|
|
344
|
+
f"Background tasks exceeded the {wait_cap}s wait"
|
|
345
|
+
" limit; stop was requested where possible:\n"
|
|
346
|
+
f"{task_lines}\n"
|
|
347
|
+
"Summarize progress and inform the user,"
|
|
348
|
+
" then conclude.\n"
|
|
349
|
+
"</system-reminder>"
|
|
350
|
+
)
|
|
351
|
+
# The follow-up turn can fail (provider outage,
|
|
352
|
+
# MaxStepsReached). Swallow those so the exit
|
|
353
|
+
# code stays FAILURE (the user already saw
|
|
354
|
+
# "timed out ... killed N tasks" on stderr —
|
|
355
|
+
# reclassifying to e.g. RETRYABLE would
|
|
356
|
+
# contradict that), and ensure reconcile()
|
|
357
|
+
# still runs in the ``finally`` block to flush
|
|
358
|
+
# terminal notifications. RunCancelled is
|
|
359
|
+
# explicitly re-raised so Ctrl+C keeps its
|
|
360
|
+
# cancel semantics (outer ``except RunCancelled``
|
|
361
|
+
# → "Interrupted by user") instead of being
|
|
362
|
+
# silently reclassified as a timeout.
|
|
363
|
+
try:
|
|
364
|
+
await run_soul(
|
|
365
|
+
self.soul,
|
|
366
|
+
timeout_prompt,
|
|
367
|
+
partial(visualize, self.output_format, self.final_only),
|
|
368
|
+
cancel_event,
|
|
369
|
+
runtime.session.wire_file,
|
|
370
|
+
runtime,
|
|
371
|
+
skip_user_prompt_hook=True,
|
|
372
|
+
)
|
|
373
|
+
except RunCancelled:
|
|
374
|
+
raise
|
|
375
|
+
except Exception:
|
|
376
|
+
logger.warning(
|
|
377
|
+
"Timeout follow-up soul turn failed; continuing shutdown",
|
|
378
|
+
exc_info=True,
|
|
379
|
+
)
|
|
380
|
+
finally:
|
|
381
|
+
# The follow-up soul turn already took a
|
|
382
|
+
# few seconds — enough natural window for
|
|
383
|
+
# worker supervisors to notice the SIGTERM
|
|
384
|
+
# and write their terminal status to disk.
|
|
385
|
+
# One last reconcile picks up that on-disk
|
|
386
|
+
# state so the persisted task view is
|
|
387
|
+
# consistent with what the user was just
|
|
388
|
+
# told ("killed"), instead of being stuck
|
|
389
|
+
# on "running" until the next CLI start.
|
|
390
|
+
#
|
|
391
|
+
# Guard against reconcile raising inside
|
|
392
|
+
# ``finally`` during a ``RunCancelled``
|
|
393
|
+
# propagation: an unhandled exception here
|
|
394
|
+
# would replace the active ``RunCancelled``
|
|
395
|
+
# and bypass the outer ``except RunCancelled``
|
|
396
|
+
# branch, surfacing as an ``Unknown error``
|
|
397
|
+
# instead of ``Interrupted by user``.
|
|
398
|
+
try:
|
|
399
|
+
manager.reconcile()
|
|
400
|
+
except Exception:
|
|
401
|
+
logger.warning(
|
|
402
|
+
"Post-timeout reconcile failed; continuing exit",
|
|
403
|
+
exc_info=True,
|
|
404
|
+
)
|
|
405
|
+
return ExitCode.FAILURE
|
|
406
|
+
else:
|
|
407
|
+
logger.info("Empty command, skipping")
|
|
408
|
+
|
|
409
|
+
command = None
|
|
410
|
+
except LLMNotSet as e:
|
|
411
|
+
logger.exception("LLM not set:")
|
|
412
|
+
print(str(e))
|
|
413
|
+
return ExitCode.FAILURE
|
|
414
|
+
except LLMNotSupported as e:
|
|
415
|
+
logger.exception("LLM not supported:")
|
|
416
|
+
print(str(e))
|
|
417
|
+
return ExitCode.FAILURE
|
|
418
|
+
except ChatProviderError as e:
|
|
419
|
+
logger.exception("LLM provider error:")
|
|
420
|
+
print(str(e))
|
|
421
|
+
return self._classify_provider_error(e)
|
|
422
|
+
except MaxStepsReached as e:
|
|
423
|
+
logger.warning("Max steps reached: {n_steps}", n_steps=e.n_steps)
|
|
424
|
+
print(str(e))
|
|
425
|
+
return ExitCode.FAILURE
|
|
426
|
+
except RunCancelled:
|
|
427
|
+
logger.error("Interrupted by user")
|
|
428
|
+
print("Interrupted by user")
|
|
429
|
+
return ExitCode.FAILURE
|
|
430
|
+
except BaseException as e:
|
|
431
|
+
logger.exception("Unknown error:")
|
|
432
|
+
print(f"Unknown error: {e}")
|
|
433
|
+
raise
|
|
434
|
+
finally:
|
|
435
|
+
remove_sigint()
|
|
436
|
+
return ExitCode.FAILURE
|
|
437
|
+
|
|
438
|
+
_RETRYABLE_STATUS_CODES = {429, 500, 502, 503, 504}
|
|
439
|
+
|
|
440
|
+
@staticmethod
|
|
441
|
+
def _classify_provider_error(e: ChatProviderError) -> int:
|
|
442
|
+
"""Classify a ChatProviderError into an exit code."""
|
|
443
|
+
if isinstance(e, (APIConnectionError, APITimeoutError, APIEmptyResponseError)):
|
|
444
|
+
return ExitCode.RETRYABLE
|
|
445
|
+
if isinstance(e, APIStatusError):
|
|
446
|
+
if e.status_code in Print._RETRYABLE_STATUS_CODES:
|
|
447
|
+
return ExitCode.RETRYABLE
|
|
448
|
+
return ExitCode.FAILURE
|
|
449
|
+
return ExitCode.FAILURE
|
|
450
|
+
|
|
451
|
+
def _read_next_command(self) -> str | None:
|
|
452
|
+
while True:
|
|
453
|
+
json_line = sys.stdin.readline()
|
|
454
|
+
if not json_line:
|
|
455
|
+
# EOF
|
|
456
|
+
return None
|
|
457
|
+
|
|
458
|
+
json_line = json_line.strip()
|
|
459
|
+
if not json_line:
|
|
460
|
+
# for empty line, read next line
|
|
461
|
+
continue
|
|
462
|
+
|
|
463
|
+
try:
|
|
464
|
+
data = json.loads(json_line)
|
|
465
|
+
message = Message.model_validate(data)
|
|
466
|
+
if message.role == "user":
|
|
467
|
+
return message.extract_text(sep="\n")
|
|
468
|
+
logger.warning(
|
|
469
|
+
"Ignoring message with role `{role}`: {json_line}",
|
|
470
|
+
role=message.role,
|
|
471
|
+
json_line=json_line,
|
|
472
|
+
)
|
|
473
|
+
except Exception:
|
|
474
|
+
logger.warning("Ignoring invalid user message: {json_line}", json_line=json_line)
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
from typing import Protocol
|
|
2
|
+
|
|
3
|
+
import rich
|
|
4
|
+
from pythinker_core.message import Message
|
|
5
|
+
|
|
6
|
+
from pythinker_code.cli import OutputFormat
|
|
7
|
+
from pythinker_code.soul.message import tool_result_to_message
|
|
8
|
+
from pythinker_code.utils.aioqueue import QueueShutDown
|
|
9
|
+
from pythinker_code.wire import Wire
|
|
10
|
+
from pythinker_code.wire.types import (
|
|
11
|
+
ContentPart,
|
|
12
|
+
Notification,
|
|
13
|
+
PlanDisplay,
|
|
14
|
+
StepBegin,
|
|
15
|
+
StepInterrupted,
|
|
16
|
+
ToolCall,
|
|
17
|
+
ToolCallPart,
|
|
18
|
+
ToolResult,
|
|
19
|
+
WireMessage,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Printer(Protocol):
|
|
24
|
+
def feed(self, msg: WireMessage) -> None: ...
|
|
25
|
+
def flush(self) -> None: ...
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _merge_content(buffer: list[ContentPart], part: ContentPart) -> None:
|
|
29
|
+
if not buffer or not buffer[-1].merge_in_place(part):
|
|
30
|
+
buffer.append(part)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TextPrinter(Printer):
|
|
34
|
+
def feed(self, msg: WireMessage) -> None:
|
|
35
|
+
rich.print(msg)
|
|
36
|
+
|
|
37
|
+
def flush(self) -> None:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class JsonPrinter(Printer):
|
|
42
|
+
def __init__(self) -> None:
|
|
43
|
+
self._content_buffer: list[ContentPart] = []
|
|
44
|
+
"""The buffer to merge content parts."""
|
|
45
|
+
self._tool_call_buffer: list[ToolCall] = []
|
|
46
|
+
"""The buffer to store the current assistant message's tool calls."""
|
|
47
|
+
self._pending_notifications: list[Notification] = []
|
|
48
|
+
"""Notifications buffered until the current assistant message reaches a safe boundary."""
|
|
49
|
+
self._last_tool_call: ToolCall | None = None
|
|
50
|
+
|
|
51
|
+
def feed(self, msg: WireMessage) -> None:
|
|
52
|
+
match msg:
|
|
53
|
+
case StepBegin() | StepInterrupted():
|
|
54
|
+
self.flush()
|
|
55
|
+
case Notification() as notification:
|
|
56
|
+
if self._content_buffer or self._tool_call_buffer:
|
|
57
|
+
self._pending_notifications.append(notification)
|
|
58
|
+
else:
|
|
59
|
+
self._flush_assistant_message()
|
|
60
|
+
self._flush_notifications()
|
|
61
|
+
self._emit_notification(notification)
|
|
62
|
+
case ContentPart() as part:
|
|
63
|
+
# merge with previous parts as much as possible
|
|
64
|
+
_merge_content(self._content_buffer, part)
|
|
65
|
+
case ToolCall() as call:
|
|
66
|
+
self._tool_call_buffer.append(call)
|
|
67
|
+
self._last_tool_call = call
|
|
68
|
+
case ToolCallPart() as part:
|
|
69
|
+
if self._last_tool_call is None:
|
|
70
|
+
return
|
|
71
|
+
assert self._last_tool_call.merge_in_place(part)
|
|
72
|
+
case ToolResult() as result:
|
|
73
|
+
self._flush_assistant_message()
|
|
74
|
+
self._flush_notifications()
|
|
75
|
+
message = tool_result_to_message(result)
|
|
76
|
+
print(message.model_dump_json(exclude_none=True), flush=True)
|
|
77
|
+
case PlanDisplay() as plan:
|
|
78
|
+
self._flush_assistant_message()
|
|
79
|
+
self._flush_notifications()
|
|
80
|
+
print(plan.model_dump_json(exclude_none=True), flush=True)
|
|
81
|
+
case _:
|
|
82
|
+
# ignore other messages
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
def _flush_assistant_message(self) -> None:
|
|
86
|
+
if not self._content_buffer and not self._tool_call_buffer:
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
message = Message(
|
|
90
|
+
role="assistant",
|
|
91
|
+
content=self._content_buffer,
|
|
92
|
+
tool_calls=self._tool_call_buffer or None,
|
|
93
|
+
)
|
|
94
|
+
print(message.model_dump_json(exclude_none=True), flush=True)
|
|
95
|
+
|
|
96
|
+
self._content_buffer.clear()
|
|
97
|
+
self._tool_call_buffer.clear()
|
|
98
|
+
self._last_tool_call = None
|
|
99
|
+
|
|
100
|
+
def _emit_notification(self, notification: Notification) -> None:
|
|
101
|
+
print(notification.model_dump_json(exclude_none=True), flush=True)
|
|
102
|
+
|
|
103
|
+
def _flush_notifications(self) -> None:
|
|
104
|
+
for notification in self._pending_notifications:
|
|
105
|
+
self._emit_notification(notification)
|
|
106
|
+
self._pending_notifications.clear()
|
|
107
|
+
|
|
108
|
+
def flush(self) -> None:
|
|
109
|
+
self._flush_assistant_message()
|
|
110
|
+
self._flush_notifications()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class FinalOnlyTextPrinter(Printer):
|
|
114
|
+
def __init__(self) -> None:
|
|
115
|
+
self._content_buffer: list[ContentPart] = []
|
|
116
|
+
|
|
117
|
+
def feed(self, msg: WireMessage) -> None:
|
|
118
|
+
match msg:
|
|
119
|
+
case StepBegin() | StepInterrupted():
|
|
120
|
+
self._content_buffer.clear()
|
|
121
|
+
case ContentPart() as part:
|
|
122
|
+
_merge_content(self._content_buffer, part)
|
|
123
|
+
case _:
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
def flush(self) -> None:
|
|
127
|
+
if not self._content_buffer:
|
|
128
|
+
return
|
|
129
|
+
message = Message(role="assistant", content=self._content_buffer)
|
|
130
|
+
text = message.extract_text()
|
|
131
|
+
if text:
|
|
132
|
+
print(text, flush=True)
|
|
133
|
+
self._content_buffer.clear()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class FinalOnlyJsonPrinter(Printer):
|
|
137
|
+
def __init__(self) -> None:
|
|
138
|
+
self._content_buffer: list[ContentPart] = []
|
|
139
|
+
|
|
140
|
+
def feed(self, msg: WireMessage) -> None:
|
|
141
|
+
match msg:
|
|
142
|
+
case StepBegin() | StepInterrupted():
|
|
143
|
+
self._content_buffer.clear()
|
|
144
|
+
case ContentPart() as part:
|
|
145
|
+
_merge_content(self._content_buffer, part)
|
|
146
|
+
case _:
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
def flush(self) -> None:
|
|
150
|
+
if not self._content_buffer:
|
|
151
|
+
return
|
|
152
|
+
message = Message(role="assistant", content=self._content_buffer)
|
|
153
|
+
text = message.extract_text()
|
|
154
|
+
if text:
|
|
155
|
+
final_message = Message(role="assistant", content=text)
|
|
156
|
+
print(final_message.model_dump_json(exclude_none=True), flush=True)
|
|
157
|
+
self._content_buffer.clear()
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
async def visualize(output_format: OutputFormat, final_only: bool, wire: Wire) -> None:
|
|
161
|
+
if final_only:
|
|
162
|
+
match output_format:
|
|
163
|
+
case "text":
|
|
164
|
+
handler = FinalOnlyTextPrinter()
|
|
165
|
+
case "stream-json":
|
|
166
|
+
handler = FinalOnlyJsonPrinter()
|
|
167
|
+
else:
|
|
168
|
+
match output_format:
|
|
169
|
+
case "text":
|
|
170
|
+
handler = TextPrinter()
|
|
171
|
+
case "stream-json":
|
|
172
|
+
handler = JsonPrinter()
|
|
173
|
+
|
|
174
|
+
wire_ui = wire.ui_side(merge=True)
|
|
175
|
+
while True:
|
|
176
|
+
try:
|
|
177
|
+
msg = await wire_ui.receive()
|
|
178
|
+
except QueueShutDown:
|
|
179
|
+
handler.flush()
|
|
180
|
+
break
|
|
181
|
+
|
|
182
|
+
handler.feed(msg)
|
|
183
|
+
|
|
184
|
+
if isinstance(msg, StepInterrupted):
|
|
185
|
+
break
|