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.
Files changed (731) hide show
  1. pythinker_code/CHANGELOG.md +16 -0
  2. pythinker_code/__init__.py +0 -0
  3. pythinker_code/__main__.py +92 -0
  4. pythinker_code/acp/AGENTS.md +93 -0
  5. pythinker_code/acp/__init__.py +13 -0
  6. pythinker_code/acp/convert.py +128 -0
  7. pythinker_code/acp/host.py +298 -0
  8. pythinker_code/acp/mcp.py +46 -0
  9. pythinker_code/acp/server.py +497 -0
  10. pythinker_code/acp/session.py +496 -0
  11. pythinker_code/acp/tools.py +167 -0
  12. pythinker_code/acp/types.py +13 -0
  13. pythinker_code/acp/version.py +45 -0
  14. pythinker_code/agents/default/agent.yaml +36 -0
  15. pythinker_code/agents/default/coder.yaml +25 -0
  16. pythinker_code/agents/default/explore.yaml +46 -0
  17. pythinker_code/agents/default/plan.yaml +30 -0
  18. pythinker_code/agents/default/system.md +164 -0
  19. pythinker_code/agents/okabe/agent.yaml +22 -0
  20. pythinker_code/agentspec.py +163 -0
  21. pythinker_code/app.py +820 -0
  22. pythinker_code/approval_runtime/__init__.py +29 -0
  23. pythinker_code/approval_runtime/models.py +42 -0
  24. pythinker_code/approval_runtime/runtime.py +235 -0
  25. pythinker_code/auth/__init__.py +25 -0
  26. pythinker_code/auth/anthropic_direct.py +207 -0
  27. pythinker_code/auth/deepseek.py +192 -0
  28. pythinker_code/auth/lm_studio.py +418 -0
  29. pythinker_code/auth/minimax.py +203 -0
  30. pythinker_code/auth/oauth.py +1122 -0
  31. pythinker_code/auth/ollama.py +293 -0
  32. pythinker_code/auth/openai.py +771 -0
  33. pythinker_code/auth/opencode_go.py +203 -0
  34. pythinker_code/auth/openrouter.py +225 -0
  35. pythinker_code/auth/platforms.py +466 -0
  36. pythinker_code/background/__init__.py +36 -0
  37. pythinker_code/background/agent_runner.py +231 -0
  38. pythinker_code/background/ids.py +19 -0
  39. pythinker_code/background/manager.py +650 -0
  40. pythinker_code/background/models.py +105 -0
  41. pythinker_code/background/store.py +237 -0
  42. pythinker_code/background/summary.py +66 -0
  43. pythinker_code/background/worker.py +209 -0
  44. pythinker_code/cli/__init__.py +1326 -0
  45. pythinker_code/cli/__main__.py +19 -0
  46. pythinker_code/cli/_lazy_group.py +238 -0
  47. pythinker_code/cli/export.py +322 -0
  48. pythinker_code/cli/info.py +62 -0
  49. pythinker_code/cli/mcp.py +349 -0
  50. pythinker_code/cli/plugin.py +351 -0
  51. pythinker_code/cli/toad.py +74 -0
  52. pythinker_code/cli/vis.py +38 -0
  53. pythinker_code/cli/web.py +80 -0
  54. pythinker_code/config.py +453 -0
  55. pythinker_code/constant.py +33 -0
  56. pythinker_code/exception.py +43 -0
  57. pythinker_code/hooks/__init__.py +4 -0
  58. pythinker_code/hooks/config.py +34 -0
  59. pythinker_code/hooks/engine.py +371 -0
  60. pythinker_code/hooks/events.py +190 -0
  61. pythinker_code/hooks/runner.py +89 -0
  62. pythinker_code/llm.py +412 -0
  63. pythinker_code/metadata.py +79 -0
  64. pythinker_code/notifications/__init__.py +33 -0
  65. pythinker_code/notifications/llm.py +77 -0
  66. pythinker_code/notifications/manager.py +145 -0
  67. pythinker_code/notifications/models.py +50 -0
  68. pythinker_code/notifications/notifier.py +41 -0
  69. pythinker_code/notifications/store.py +118 -0
  70. pythinker_code/notifications/wire.py +21 -0
  71. pythinker_code/plugin/__init__.py +124 -0
  72. pythinker_code/plugin/manager.py +153 -0
  73. pythinker_code/plugin/tool.py +173 -0
  74. pythinker_code/prompts/__init__.py +6 -0
  75. pythinker_code/prompts/compact.md +73 -0
  76. pythinker_code/prompts/init.md +21 -0
  77. pythinker_code/py.typed +0 -0
  78. pythinker_code/session.py +319 -0
  79. pythinker_code/session_fork.py +325 -0
  80. pythinker_code/session_state.py +132 -0
  81. pythinker_code/share.py +14 -0
  82. pythinker_code/skill/__init__.py +727 -0
  83. pythinker_code/skill/flow/__init__.py +99 -0
  84. pythinker_code/skill/flow/d2.py +482 -0
  85. pythinker_code/skill/flow/mermaid.py +266 -0
  86. pythinker_code/skills/pythinker-code-help/SKILL.md +54 -0
  87. pythinker_code/skills/skill-creator/SKILL.md +367 -0
  88. pythinker_code/soul/__init__.py +304 -0
  89. pythinker_code/soul/agent.py +520 -0
  90. pythinker_code/soul/approval.py +267 -0
  91. pythinker_code/soul/btw.py +214 -0
  92. pythinker_code/soul/compaction.py +189 -0
  93. pythinker_code/soul/context.py +339 -0
  94. pythinker_code/soul/denwarenji.py +39 -0
  95. pythinker_code/soul/dynamic_injection.py +84 -0
  96. pythinker_code/soul/dynamic_injections/__init__.py +0 -0
  97. pythinker_code/soul/dynamic_injections/auto_mode.py +72 -0
  98. pythinker_code/soul/dynamic_injections/plan_mode.py +239 -0
  99. pythinker_code/soul/message.py +92 -0
  100. pythinker_code/soul/pythinkersoul.py +1613 -0
  101. pythinker_code/soul/slash.py +340 -0
  102. pythinker_code/soul/toolset.py +788 -0
  103. pythinker_code/subagents/__init__.py +21 -0
  104. pythinker_code/subagents/builder.py +42 -0
  105. pythinker_code/subagents/core.py +86 -0
  106. pythinker_code/subagents/git_context.py +172 -0
  107. pythinker_code/subagents/models.py +54 -0
  108. pythinker_code/subagents/output.py +71 -0
  109. pythinker_code/subagents/registry.py +28 -0
  110. pythinker_code/subagents/runner.py +428 -0
  111. pythinker_code/subagents/store.py +196 -0
  112. pythinker_code/telemetry/__init__.py +211 -0
  113. pythinker_code/telemetry/config.py +54 -0
  114. pythinker_code/telemetry/crash.py +157 -0
  115. pythinker_code/telemetry/metrics.py +208 -0
  116. pythinker_code/telemetry/otel.py +240 -0
  117. pythinker_code/telemetry/sentry.py +167 -0
  118. pythinker_code/telemetry/sink.py +189 -0
  119. pythinker_code/tools/AGENTS.md +6 -0
  120. pythinker_code/tools/__init__.py +105 -0
  121. pythinker_code/tools/agent/__init__.py +277 -0
  122. pythinker_code/tools/agent/description.md +41 -0
  123. pythinker_code/tools/ask_user/__init__.py +159 -0
  124. pythinker_code/tools/ask_user/description.md +19 -0
  125. pythinker_code/tools/background/__init__.py +318 -0
  126. pythinker_code/tools/background/list.md +10 -0
  127. pythinker_code/tools/background/output.md +11 -0
  128. pythinker_code/tools/background/stop.md +8 -0
  129. pythinker_code/tools/display.py +46 -0
  130. pythinker_code/tools/dmail/__init__.py +38 -0
  131. pythinker_code/tools/dmail/dmail.md +17 -0
  132. pythinker_code/tools/file/__init__.py +30 -0
  133. pythinker_code/tools/file/glob.md +17 -0
  134. pythinker_code/tools/file/glob.py +160 -0
  135. pythinker_code/tools/file/grep.md +6 -0
  136. pythinker_code/tools/file/grep_local.py +589 -0
  137. pythinker_code/tools/file/plan_mode.py +45 -0
  138. pythinker_code/tools/file/read.md +16 -0
  139. pythinker_code/tools/file/read.py +300 -0
  140. pythinker_code/tools/file/read_media.md +24 -0
  141. pythinker_code/tools/file/read_media.py +217 -0
  142. pythinker_code/tools/file/replace.md +7 -0
  143. pythinker_code/tools/file/replace.py +195 -0
  144. pythinker_code/tools/file/utils.py +257 -0
  145. pythinker_code/tools/file/write.md +5 -0
  146. pythinker_code/tools/file/write.py +177 -0
  147. pythinker_code/tools/plan/__init__.py +327 -0
  148. pythinker_code/tools/plan/description.md +29 -0
  149. pythinker_code/tools/plan/enter.py +190 -0
  150. pythinker_code/tools/plan/enter_description.md +35 -0
  151. pythinker_code/tools/plan/heroes.py +277 -0
  152. pythinker_code/tools/shell/__init__.py +253 -0
  153. pythinker_code/tools/shell/bash.md +35 -0
  154. pythinker_code/tools/shell/powershell.md +30 -0
  155. pythinker_code/tools/test.py +55 -0
  156. pythinker_code/tools/think/__init__.py +21 -0
  157. pythinker_code/tools/think/think.md +1 -0
  158. pythinker_code/tools/todo/__init__.py +168 -0
  159. pythinker_code/tools/todo/set_todo_list.md +23 -0
  160. pythinker_code/tools/utils.py +199 -0
  161. pythinker_code/tools/web/__init__.py +4 -0
  162. pythinker_code/tools/web/fetch.md +1 -0
  163. pythinker_code/tools/web/fetch.py +189 -0
  164. pythinker_code/tools/web/search.md +1 -0
  165. pythinker_code/tools/web/search.py +163 -0
  166. pythinker_code/ui/__init__.py +0 -0
  167. pythinker_code/ui/acp/__init__.py +99 -0
  168. pythinker_code/ui/print/__init__.py +474 -0
  169. pythinker_code/ui/print/visualize.py +185 -0
  170. pythinker_code/ui/shell/__init__.py +1696 -0
  171. pythinker_code/ui/shell/console.py +109 -0
  172. pythinker_code/ui/shell/debug.py +190 -0
  173. pythinker_code/ui/shell/echo.py +17 -0
  174. pythinker_code/ui/shell/export_import.py +117 -0
  175. pythinker_code/ui/shell/keyboard.py +300 -0
  176. pythinker_code/ui/shell/mcp_status.py +113 -0
  177. pythinker_code/ui/shell/model_picker.py +318 -0
  178. pythinker_code/ui/shell/oauth.py +272 -0
  179. pythinker_code/ui/shell/placeholders.py +531 -0
  180. pythinker_code/ui/shell/prompt.py +2278 -0
  181. pythinker_code/ui/shell/replay.py +215 -0
  182. pythinker_code/ui/shell/session_picker.py +227 -0
  183. pythinker_code/ui/shell/setup.py +212 -0
  184. pythinker_code/ui/shell/slash.py +898 -0
  185. pythinker_code/ui/shell/startup.py +32 -0
  186. pythinker_code/ui/shell/task_browser.py +486 -0
  187. pythinker_code/ui/shell/update.py +350 -0
  188. pythinker_code/ui/shell/usage.py +291 -0
  189. pythinker_code/ui/shell/usage_adapters/__init__.py +50 -0
  190. pythinker_code/ui/shell/usage_adapters/anthropic_admin.py +233 -0
  191. pythinker_code/ui/shell/usage_adapters/base.py +72 -0
  192. pythinker_code/ui/shell/usage_adapters/deepseek.py +137 -0
  193. pythinker_code/ui/shell/usage_adapters/minimax.py +236 -0
  194. pythinker_code/ui/shell/usage_adapters/openai_admin.py +225 -0
  195. pythinker_code/ui/shell/usage_adapters/openai_chatgpt.py +241 -0
  196. pythinker_code/ui/shell/usage_adapters/opencode_go.py +232 -0
  197. pythinker_code/ui/shell/usage_adapters/openrouter.py +105 -0
  198. pythinker_code/ui/shell/usage_adapters/pythinker.py +189 -0
  199. pythinker_code/ui/shell/usage_adapters/pythinker_ai.py +50 -0
  200. pythinker_code/ui/shell/usage_render.py +150 -0
  201. pythinker_code/ui/shell/visualize/__init__.py +165 -0
  202. pythinker_code/ui/shell/visualize/_approval_panel.py +505 -0
  203. pythinker_code/ui/shell/visualize/_blocks.py +629 -0
  204. pythinker_code/ui/shell/visualize/_btw_panel.py +224 -0
  205. pythinker_code/ui/shell/visualize/_input_router.py +48 -0
  206. pythinker_code/ui/shell/visualize/_interactive.py +523 -0
  207. pythinker_code/ui/shell/visualize/_live_view.py +826 -0
  208. pythinker_code/ui/shell/visualize/_question_panel.py +586 -0
  209. pythinker_code/ui/theme.py +241 -0
  210. pythinker_code/usage_ratelimit_cache.py +175 -0
  211. pythinker_code/utils/__init__.py +0 -0
  212. pythinker_code/utils/aiohttp.py +24 -0
  213. pythinker_code/utils/aioqueue.py +72 -0
  214. pythinker_code/utils/broadcast.py +37 -0
  215. pythinker_code/utils/changelog.py +108 -0
  216. pythinker_code/utils/clipboard.py +246 -0
  217. pythinker_code/utils/datetime.py +64 -0
  218. pythinker_code/utils/diff.py +135 -0
  219. pythinker_code/utils/editor.py +91 -0
  220. pythinker_code/utils/environment.py +73 -0
  221. pythinker_code/utils/envvar.py +22 -0
  222. pythinker_code/utils/export.py +696 -0
  223. pythinker_code/utils/file_filter.py +375 -0
  224. pythinker_code/utils/frontmatter.py +70 -0
  225. pythinker_code/utils/io.py +27 -0
  226. pythinker_code/utils/logging.py +146 -0
  227. pythinker_code/utils/media_tags.py +29 -0
  228. pythinker_code/utils/message.py +24 -0
  229. pythinker_code/utils/path.py +199 -0
  230. pythinker_code/utils/proctitle.py +33 -0
  231. pythinker_code/utils/proxy.py +31 -0
  232. pythinker_code/utils/pyinstaller.py +45 -0
  233. pythinker_code/utils/rich/__init__.py +33 -0
  234. pythinker_code/utils/rich/columns.py +99 -0
  235. pythinker_code/utils/rich/diff_render.py +481 -0
  236. pythinker_code/utils/rich/markdown.py +900 -0
  237. pythinker_code/utils/rich/markdown_sample.md +108 -0
  238. pythinker_code/utils/rich/markdown_sample_short.md +2 -0
  239. pythinker_code/utils/rich/syntax.py +114 -0
  240. pythinker_code/utils/sensitive.py +54 -0
  241. pythinker_code/utils/server.py +121 -0
  242. pythinker_code/utils/signals.py +43 -0
  243. pythinker_code/utils/slashcmd.py +124 -0
  244. pythinker_code/utils/string.py +41 -0
  245. pythinker_code/utils/subprocess_env.py +73 -0
  246. pythinker_code/utils/term.py +168 -0
  247. pythinker_code/utils/typing.py +20 -0
  248. pythinker_code/vis/__init__.py +0 -0
  249. pythinker_code/vis/api/__init__.py +5 -0
  250. pythinker_code/vis/api/sessions.py +687 -0
  251. pythinker_code/vis/api/statistics.py +209 -0
  252. pythinker_code/vis/api/system.py +19 -0
  253. pythinker_code/vis/app.py +175 -0
  254. pythinker_code/vis/static/assets/highlighted-body-B3W2YXNL-D2MTYyJz.js +1 -0
  255. pythinker_code/vis/static/assets/index-CezafTt_.js +185 -0
  256. pythinker_code/vis/static/assets/index-DSRInNnm.css +1 -0
  257. pythinker_code/vis/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  258. pythinker_code/vis/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  259. pythinker_code/vis/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  260. pythinker_code/vis/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  261. pythinker_code/vis/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  262. pythinker_code/vis/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  263. pythinker_code/vis/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  264. pythinker_code/vis/static/index.html +17 -0
  265. pythinker_code/web/__init__.py +5 -0
  266. pythinker_code/web/api/__init__.py +15 -0
  267. pythinker_code/web/api/config.py +208 -0
  268. pythinker_code/web/api/open_in.py +199 -0
  269. pythinker_code/web/api/sessions.py +1225 -0
  270. pythinker_code/web/app.py +451 -0
  271. pythinker_code/web/auth.py +191 -0
  272. pythinker_code/web/models.py +98 -0
  273. pythinker_code/web/runner/__init__.py +5 -0
  274. pythinker_code/web/runner/messages.py +57 -0
  275. pythinker_code/web/runner/process.py +754 -0
  276. pythinker_code/web/runner/worker.py +97 -0
  277. pythinker_code/web/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  278. pythinker_code/web/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  279. pythinker_code/web/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  280. pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  281. pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  282. pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  283. pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  284. pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  285. pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  286. pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  287. pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  288. pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  289. pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  290. pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  291. pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  292. pythinker_code/web/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  293. pythinker_code/web/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  294. pythinker_code/web/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  295. pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  296. pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  297. pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  298. pythinker_code/web/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  299. pythinker_code/web/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  300. pythinker_code/web/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  301. pythinker_code/web/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  302. pythinker_code/web/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  303. pythinker_code/web/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  304. pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  305. pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  306. pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  307. pythinker_code/web/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  308. pythinker_code/web/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  309. pythinker_code/web/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  310. pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  311. pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  312. pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  313. pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  314. pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  315. pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  316. pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  317. pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  318. pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  319. pythinker_code/web/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  320. pythinker_code/web/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  321. pythinker_code/web/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  322. pythinker_code/web/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  323. pythinker_code/web/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  324. pythinker_code/web/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  325. pythinker_code/web/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  326. pythinker_code/web/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  327. pythinker_code/web/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  328. pythinker_code/web/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  329. pythinker_code/web/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  330. pythinker_code/web/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  331. pythinker_code/web/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  332. pythinker_code/web/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  333. pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  334. pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  335. pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  336. pythinker_code/web/static/assets/_baseUniq--dyU3g5v.js +1 -0
  337. pythinker_code/web/static/assets/abap-BdImnpbu.js +1 -0
  338. pythinker_code/web/static/assets/actionscript-3-CfeIJUat.js +1 -0
  339. pythinker_code/web/static/assets/ada-bCR0ucgS.js +1 -0
  340. pythinker_code/web/static/assets/andromeeda-C-Jbm3Hp.js +1 -0
  341. pythinker_code/web/static/assets/angular-html-CU67Zn6k.js +1 -0
  342. pythinker_code/web/static/assets/angular-ts-BwZT4LLn.js +1 -0
  343. pythinker_code/web/static/assets/apache-Pmp26Uib.js +1 -0
  344. pythinker_code/web/static/assets/apex-D8_7TLub.js +1 -0
  345. pythinker_code/web/static/assets/apl-dKokRX4l.js +1 -0
  346. pythinker_code/web/static/assets/applescript-Co6uUVPk.js +1 -0
  347. pythinker_code/web/static/assets/ara-BRHolxvo.js +1 -0
  348. pythinker_code/web/static/assets/arc-DkMjLpYa.js +1 -0
  349. pythinker_code/web/static/assets/architectureDiagram-VXUJARFQ-CHWVaGo9.js +36 -0
  350. pythinker_code/web/static/assets/asciidoc-Dv7Oe6Be.js +1 -0
  351. pythinker_code/web/static/assets/asm-D_Q5rh1f.js +1 -0
  352. pythinker_code/web/static/assets/astro-CbQHKStN.js +1 -0
  353. pythinker_code/web/static/assets/aurora-x-D-2ljcwZ.js +1 -0
  354. pythinker_code/web/static/assets/awk-DMzUqQB5.js +1 -0
  355. pythinker_code/web/static/assets/ayu-dark-CmMr59Fi.js +1 -0
  356. pythinker_code/web/static/assets/ballerina-BFfxhgS-.js +1 -0
  357. pythinker_code/web/static/assets/bat-BkioyH1T.js +1 -0
  358. pythinker_code/web/static/assets/beancount-k_qm7-4y.js +1 -0
  359. pythinker_code/web/static/assets/berry-uYugtg8r.js +1 -0
  360. pythinker_code/web/static/assets/bibtex-CHM0blh-.js +1 -0
  361. pythinker_code/web/static/assets/bicep-Bmn6On1c.js +1 -0
  362. pythinker_code/web/static/assets/blade-D4QpJJKB.js +1 -0
  363. pythinker_code/web/static/assets/blockDiagram-VD42YOAC-DzdKe497.js +122 -0
  364. pythinker_code/web/static/assets/bsl-BO_Y6i37.js +1 -0
  365. pythinker_code/web/static/assets/c-BIGW1oBm.js +1 -0
  366. pythinker_code/web/static/assets/c3-VCDPK7BO.js +1 -0
  367. pythinker_code/web/static/assets/c4Diagram-YG6GDRKO-D84Blotg.js +10 -0
  368. pythinker_code/web/static/assets/cadence-Bv_4Rxtq.js +1 -0
  369. pythinker_code/web/static/assets/cairo-KRGpt6FW.js +1 -0
  370. pythinker_code/web/static/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  371. pythinker_code/web/static/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  372. pythinker_code/web/static/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  373. pythinker_code/web/static/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  374. pythinker_code/web/static/assets/channel-CllSjjdl.js +1 -0
  375. pythinker_code/web/static/assets/chunk-4BX2VUAB-C9w8wleE.js +1 -0
  376. pythinker_code/web/static/assets/chunk-55IACEB6-YlYJ8HnF.js +1 -0
  377. pythinker_code/web/static/assets/chunk-B4BG7PRW-Bwtz_AHU.js +165 -0
  378. pythinker_code/web/static/assets/chunk-DI55MBZ5-BbEHkl8h.js +220 -0
  379. pythinker_code/web/static/assets/chunk-FMBD7UC4-BKPbvjLC.js +15 -0
  380. pythinker_code/web/static/assets/chunk-QN33PNHL-D73dQvKf.js +1 -0
  381. pythinker_code/web/static/assets/chunk-QZHKN3VN-zGiLKes_.js +1 -0
  382. pythinker_code/web/static/assets/chunk-TZMSLE5B-LHJCi2fy.js +1 -0
  383. pythinker_code/web/static/assets/clarity-D53aC0YG.js +1 -0
  384. pythinker_code/web/static/assets/classDiagram-2ON5EDUG-vX27iZwa.js +1 -0
  385. pythinker_code/web/static/assets/classDiagram-v2-WZHVMYZB-vX27iZwa.js +1 -0
  386. pythinker_code/web/static/assets/clojure-P80f7IUj.js +1 -0
  387. pythinker_code/web/static/assets/clone-DYBkaPm2.js +1 -0
  388. pythinker_code/web/static/assets/cmake-D1j8_8rp.js +1 -0
  389. pythinker_code/web/static/assets/cobol-nwyudZeR.js +1 -0
  390. pythinker_code/web/static/assets/code-block-IT6T5CEO-NtKViZGl.js +2 -0
  391. pythinker_code/web/static/assets/codeowners-Bp6g37R7.js +1 -0
  392. pythinker_code/web/static/assets/codeql-DsOJ9woJ.js +1 -0
  393. pythinker_code/web/static/assets/coffee-Ch7k5sss.js +1 -0
  394. pythinker_code/web/static/assets/common-lisp-Cg-RD9OK.js +1 -0
  395. pythinker_code/web/static/assets/coq-DkFqJrB1.js +1 -0
  396. pythinker_code/web/static/assets/cose-bilkent-S5V4N54A-DialjZpd.js +1 -0
  397. pythinker_code/web/static/assets/cpp-CofmeUqb.js +1 -0
  398. pythinker_code/web/static/assets/crystal-tKQVLTB8.js +1 -0
  399. pythinker_code/web/static/assets/csharp-K5feNrxe.js +1 -0
  400. pythinker_code/web/static/assets/css-DPfMkruS.js +1 -0
  401. pythinker_code/web/static/assets/csv-fuZLfV_i.js +1 -0
  402. pythinker_code/web/static/assets/cue-D82EKSYY.js +1 -0
  403. pythinker_code/web/static/assets/cypher-COkxafJQ.js +1 -0
  404. pythinker_code/web/static/assets/cytoscape.esm-C_Fzpdck.js +321 -0
  405. pythinker_code/web/static/assets/d-85-TOEBH.js +1 -0
  406. pythinker_code/web/static/assets/dagre-6UL2VRFP-DfuvkZZ7.js +4 -0
  407. pythinker_code/web/static/assets/dark-plus-C3mMm8J8.js +1 -0
  408. pythinker_code/web/static/assets/dart-CF10PKvl.js +1 -0
  409. pythinker_code/web/static/assets/dax-CEL-wOlO.js +1 -0
  410. pythinker_code/web/static/assets/defaultLocale-DX6XiGOO.js +1 -0
  411. pythinker_code/web/static/assets/desktop-BmXAJ9_W.js +1 -0
  412. pythinker_code/web/static/assets/diagram-PSM6KHXK-DLGctX3v.js +24 -0
  413. pythinker_code/web/static/assets/diagram-QEK2KX5R-DnxN6S0F.js +43 -0
  414. pythinker_code/web/static/assets/diagram-S2PKOQOG-Caq_Set9.js +24 -0
  415. pythinker_code/web/static/assets/diff-D97Zzqfu.js +1 -0
  416. pythinker_code/web/static/assets/docker-BcOcwvcX.js +1 -0
  417. pythinker_code/web/static/assets/dotenv-Da5cRb03.js +1 -0
  418. pythinker_code/web/static/assets/dracula-BzJJZx-M.js +1 -0
  419. pythinker_code/web/static/assets/dracula-soft-BXkSAIEj.js +1 -0
  420. pythinker_code/web/static/assets/dream-maker-BtqSS_iP.js +1 -0
  421. pythinker_code/web/static/assets/edge-BkV0erSs.js +1 -0
  422. pythinker_code/web/static/assets/elixir-CDX3lj18.js +1 -0
  423. pythinker_code/web/static/assets/elm-DbKCFpqz.js +1 -0
  424. pythinker_code/web/static/assets/emacs-lisp-C9XAeP06.js +1 -0
  425. pythinker_code/web/static/assets/erDiagram-Q2GNP2WA-BgTfALoK.js +60 -0
  426. pythinker_code/web/static/assets/erb-BOJIQeun.js +1 -0
  427. pythinker_code/web/static/assets/erlang-DsQrWhSR.js +1 -0
  428. pythinker_code/web/static/assets/everforest-dark-BgDCqdQA.js +1 -0
  429. pythinker_code/web/static/assets/everforest-light-C8M2exoo.js +1 -0
  430. pythinker_code/web/static/assets/fennel-BYunw83y.js +1 -0
  431. pythinker_code/web/static/assets/fish-BvzEVeQv.js +1 -0
  432. pythinker_code/web/static/assets/flowDiagram-NV44I4VS-QjW_fnGi.js +162 -0
  433. pythinker_code/web/static/assets/fluent-C4IJs8-o.js +1 -0
  434. pythinker_code/web/static/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  435. pythinker_code/web/static/assets/fortran-free-form-BxgE0vQu.js +1 -0
  436. pythinker_code/web/static/assets/fsharp-CXgrBDvD.js +1 -0
  437. pythinker_code/web/static/assets/ganttDiagram-JELNMOA3-fqi8JFof.js +267 -0
  438. pythinker_code/web/static/assets/gdresource-B7Tvp0Sc.js +1 -0
  439. pythinker_code/web/static/assets/gdscript-DTMYz4Jt.js +1 -0
  440. pythinker_code/web/static/assets/gdshader-DkwncUOv.js +1 -0
  441. pythinker_code/web/static/assets/genie-D0YGMca9.js +1 -0
  442. pythinker_code/web/static/assets/gherkin-DyxjwDmM.js +1 -0
  443. pythinker_code/web/static/assets/git-commit-F4YmCXRG.js +1 -0
  444. pythinker_code/web/static/assets/git-rebase-r7XF79zn.js +1 -0
  445. pythinker_code/web/static/assets/gitGraphDiagram-NY62KEGX-i7o6VQ8x.js +65 -0
  446. pythinker_code/web/static/assets/github-dark-DHJKELXO.js +1 -0
  447. pythinker_code/web/static/assets/github-dark-default-Cuk6v7N8.js +1 -0
  448. pythinker_code/web/static/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  449. pythinker_code/web/static/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  450. pythinker_code/web/static/assets/github-light-DAi9KRSo.js +1 -0
  451. pythinker_code/web/static/assets/github-light-default-D7oLnXFd.js +1 -0
  452. pythinker_code/web/static/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  453. pythinker_code/web/static/assets/gleam-BspZqrRM.js +1 -0
  454. pythinker_code/web/static/assets/glimmer-js-Rg0-pVw9.js +1 -0
  455. pythinker_code/web/static/assets/glimmer-ts-U6CK756n.js +1 -0
  456. pythinker_code/web/static/assets/glsl-DplSGwfg.js +1 -0
  457. pythinker_code/web/static/assets/gn-n2N0HUVH.js +1 -0
  458. pythinker_code/web/static/assets/gnuplot-DdkO51Og.js +1 -0
  459. pythinker_code/web/static/assets/go-Dn2_MT6a.js +1 -0
  460. pythinker_code/web/static/assets/graph-C0vZK2pT.js +1 -0
  461. pythinker_code/web/static/assets/graphql-ChdNCCLP.js +1 -0
  462. pythinker_code/web/static/assets/groovy-gcz8RCvz.js +1 -0
  463. pythinker_code/web/static/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  464. pythinker_code/web/static/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  465. pythinker_code/web/static/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  466. pythinker_code/web/static/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  467. pythinker_code/web/static/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  468. pythinker_code/web/static/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  469. pythinker_code/web/static/assets/hack-CaT9iCJl.js +1 -0
  470. pythinker_code/web/static/assets/haml-B8DHNrY2.js +1 -0
  471. pythinker_code/web/static/assets/handlebars-BL8al0AC.js +1 -0
  472. pythinker_code/web/static/assets/haskell-Df6bDoY_.js +1 -0
  473. pythinker_code/web/static/assets/haxe-CzTSHFRz.js +1 -0
  474. pythinker_code/web/static/assets/hcl-BWvSN4gD.js +1 -0
  475. pythinker_code/web/static/assets/hjson-D5-asLiD.js +1 -0
  476. pythinker_code/web/static/assets/hlsl-D3lLCCz7.js +1 -0
  477. pythinker_code/web/static/assets/houston-DnULxvSX.js +1 -0
  478. pythinker_code/web/static/assets/html-GMplVEZG.js +1 -0
  479. pythinker_code/web/static/assets/html-derivative-BFtXZ54Q.js +1 -0
  480. pythinker_code/web/static/assets/http-jrhK8wxY.js +1 -0
  481. pythinker_code/web/static/assets/hurl-irOxFIW8.js +1 -0
  482. pythinker_code/web/static/assets/hxml-Bvhsp5Yf.js +1 -0
  483. pythinker_code/web/static/assets/hy-DFXneXwc.js +1 -0
  484. pythinker_code/web/static/assets/imba-DGztddWO.js +1 -0
  485. pythinker_code/web/static/assets/index-BYCCk6-K.js +153 -0
  486. pythinker_code/web/static/assets/index-BpoLgcEt.js +1 -0
  487. pythinker_code/web/static/assets/index-Cpy4G3uJ.js +2 -0
  488. pythinker_code/web/static/assets/index-CzV_vCfu.css +1 -0
  489. pythinker_code/web/static/assets/index-DI2oedCt.js +19 -0
  490. pythinker_code/web/static/assets/index-DdIkp80K.js +5 -0
  491. pythinker_code/web/static/assets/infoDiagram-WHAUD3N6-BMPpeZSM.js +2 -0
  492. pythinker_code/web/static/assets/ini-BEwlwnbL.js +1 -0
  493. pythinker_code/web/static/assets/init-Gi6I4Gst.js +1 -0
  494. pythinker_code/web/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  495. pythinker_code/web/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  496. pythinker_code/web/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  497. pythinker_code/web/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  498. pythinker_code/web/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  499. pythinker_code/web/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  500. pythinker_code/web/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  501. pythinker_code/web/static/assets/java-CylS5w8V.js +1 -0
  502. pythinker_code/web/static/assets/javascript-wDzz0qaB.js +1 -0
  503. pythinker_code/web/static/assets/jinja-4LBKfQ-Z.js +1 -0
  504. pythinker_code/web/static/assets/jison-wvAkD_A8.js +1 -0
  505. pythinker_code/web/static/assets/journeyDiagram-XKPGCS4Q-DAM7gngo.js +139 -0
  506. pythinker_code/web/static/assets/json-Cp-IABpG.js +1 -0
  507. pythinker_code/web/static/assets/json5-C9tS-k6U.js +1 -0
  508. pythinker_code/web/static/assets/jsonc-Des-eS-w.js +1 -0
  509. pythinker_code/web/static/assets/jsonl-DcaNXYhu.js +1 -0
  510. pythinker_code/web/static/assets/jsonnet-DFQXde-d.js +1 -0
  511. pythinker_code/web/static/assets/jssm-C2t-YnRu.js +1 -0
  512. pythinker_code/web/static/assets/jsx-g9-lgVsj.js +1 -0
  513. pythinker_code/web/static/assets/julia-CxzCAyBv.js +1 -0
  514. pythinker_code/web/static/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  515. pythinker_code/web/static/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  516. pythinker_code/web/static/assets/kanagawa-wave-DWedfzmr.js +1 -0
  517. pythinker_code/web/static/assets/kanban-definition-3W4ZIXB7-ChpBpV0k.js +89 -0
  518. pythinker_code/web/static/assets/katex-D2lIc1rk.css +1 -0
  519. pythinker_code/web/static/assets/kdl-DV7GczEv.js +1 -0
  520. pythinker_code/web/static/assets/kotlin-BdnUsdx6.js +1 -0
  521. pythinker_code/web/static/assets/kusto-DZf3V79B.js +1 -0
  522. pythinker_code/web/static/assets/laserwave-DUszq2jm.js +1 -0
  523. pythinker_code/web/static/assets/latex-B4uzh10-.js +1 -0
  524. pythinker_code/web/static/assets/layout-C3Jp1gKO.js +1 -0
  525. pythinker_code/web/static/assets/lean-BZvkOJ9d.js +1 -0
  526. pythinker_code/web/static/assets/less-B1dDrJ26.js +1 -0
  527. pythinker_code/web/static/assets/light-plus-B7mTdjB0.js +1 -0
  528. pythinker_code/web/static/assets/linear-BGHtL1N4.js +1 -0
  529. pythinker_code/web/static/assets/liquid-DYVedYrR.js +1 -0
  530. pythinker_code/web/static/assets/llvm-BtvRca6l.js +1 -0
  531. pythinker_code/web/static/assets/log-2UxHyX5q.js +1 -0
  532. pythinker_code/web/static/assets/logo-BtOb2qkB.js +1 -0
  533. pythinker_code/web/static/assets/lua-BbnMAYS6.js +1 -0
  534. pythinker_code/web/static/assets/luau-C-HG3fhB.js +1 -0
  535. pythinker_code/web/static/assets/make-CHLpvVh8.js +1 -0
  536. pythinker_code/web/static/assets/markdown-Cvjx9yec.js +1 -0
  537. pythinker_code/web/static/assets/marko-DZsq8hO1.js +1 -0
  538. pythinker_code/web/static/assets/material-theme-D5KoaKCx.js +1 -0
  539. pythinker_code/web/static/assets/material-theme-darker-BfHTSMKl.js +1 -0
  540. pythinker_code/web/static/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  541. pythinker_code/web/static/assets/material-theme-ocean-CyktbL80.js +1 -0
  542. pythinker_code/web/static/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  543. pythinker_code/web/static/assets/matlab-D7o27uSR.js +1 -0
  544. pythinker_code/web/static/assets/mdc-DUICxH0z.js +1 -0
  545. pythinker_code/web/static/assets/mdx-Cmh6b_Ma.js +1 -0
  546. pythinker_code/web/static/assets/mermaid-VLURNSYL-B2P5VJ9v.css +1 -0
  547. pythinker_code/web/static/assets/mermaid-VLURNSYL-C_HW6koB.js +465 -0
  548. pythinker_code/web/static/assets/mermaid-mWjccvbQ.js +1 -0
  549. pythinker_code/web/static/assets/mermaid.core-CnT4VrPC.js +191 -0
  550. pythinker_code/web/static/assets/min-Dn5VRVmX.js +1 -0
  551. pythinker_code/web/static/assets/min-dark-CafNBF8u.js +1 -0
  552. pythinker_code/web/static/assets/min-light-CTRr51gU.js +1 -0
  553. pythinker_code/web/static/assets/mindmap-definition-VGOIOE7T-x8EwhfIt.js +68 -0
  554. pythinker_code/web/static/assets/mipsasm-CKIfxQSi.js +1 -0
  555. pythinker_code/web/static/assets/mojo-B93PlW-d.js +1 -0
  556. pythinker_code/web/static/assets/monokai-D4h5O-jR.js +1 -0
  557. pythinker_code/web/static/assets/moonbit-Ba13S78F.js +1 -0
  558. pythinker_code/web/static/assets/move-Bu9oaDYs.js +1 -0
  559. pythinker_code/web/static/assets/narrat-DRg8JJMk.js +1 -0
  560. pythinker_code/web/static/assets/nextflow-BrzmwbiE.js +1 -0
  561. pythinker_code/web/static/assets/nginx-DknmC5AR.js +1 -0
  562. pythinker_code/web/static/assets/night-owl-C39BiMTA.js +1 -0
  563. pythinker_code/web/static/assets/nim-CVrawwO9.js +1 -0
  564. pythinker_code/web/static/assets/nix-CwoSXNpI.js +1 -0
  565. pythinker_code/web/static/assets/nord-Ddv68eIx.js +1 -0
  566. pythinker_code/web/static/assets/nushell-C-sUppwS.js +1 -0
  567. pythinker_code/web/static/assets/objective-c-DXmwc3jG.js +1 -0
  568. pythinker_code/web/static/assets/objective-cpp-CLxacb5B.js +1 -0
  569. pythinker_code/web/static/assets/ocaml-C0hk2d4L.js +1 -0
  570. pythinker_code/web/static/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  571. pythinker_code/web/static/assets/one-light-PoHY5YXO.js +1 -0
  572. pythinker_code/web/static/assets/openscad-C4EeE6gA.js +1 -0
  573. pythinker_code/web/static/assets/ordinal-Cboi1Yqb.js +1 -0
  574. pythinker_code/web/static/assets/pascal-D93ZcfNL.js +1 -0
  575. pythinker_code/web/static/assets/perl-C0TMdlhV.js +1 -0
  576. pythinker_code/web/static/assets/php-CDn_0X-4.js +1 -0
  577. pythinker_code/web/static/assets/pieDiagram-ADFJNKIX-DgxBKGz2.js +30 -0
  578. pythinker_code/web/static/assets/pkl-u5AG7uiY.js +1 -0
  579. pythinker_code/web/static/assets/plastic-3e1v2bzS.js +1 -0
  580. pythinker_code/web/static/assets/plsql-ChMvpjG-.js +1 -0
  581. pythinker_code/web/static/assets/po-BTJTHyun.js +1 -0
  582. pythinker_code/web/static/assets/poimandres-CS3Unz2-.js +1 -0
  583. pythinker_code/web/static/assets/polar-C0HS_06l.js +1 -0
  584. pythinker_code/web/static/assets/postcss-CXtECtnM.js +1 -0
  585. pythinker_code/web/static/assets/powerquery-CEu0bR-o.js +1 -0
  586. pythinker_code/web/static/assets/powershell-Dpen1YoG.js +1 -0
  587. pythinker_code/web/static/assets/prisma-Dd19v3D-.js +1 -0
  588. pythinker_code/web/static/assets/prolog-CbFg5uaA.js +1 -0
  589. pythinker_code/web/static/assets/proto-C7zT0LnQ.js +1 -0
  590. pythinker_code/web/static/assets/pug-CGlum2m_.js +1 -0
  591. pythinker_code/web/static/assets/puppet-BMWR74SV.js +1 -0
  592. pythinker_code/web/static/assets/purescript-CklMAg4u.js +1 -0
  593. pythinker_code/web/static/assets/python-B6aJPvgy.js +1 -0
  594. pythinker_code/web/static/assets/qml-3beO22l8.js +1 -0
  595. pythinker_code/web/static/assets/qmldir-C8lEn-DE.js +1 -0
  596. pythinker_code/web/static/assets/qss-IeuSbFQv.js +1 -0
  597. pythinker_code/web/static/assets/quadrantDiagram-AYHSOK5B-DSpe_dqk.js +7 -0
  598. pythinker_code/web/static/assets/r-Dspwwk_N.js +1 -0
  599. pythinker_code/web/static/assets/racket-BqYA7rlc.js +1 -0
  600. pythinker_code/web/static/assets/raku-DXvB9xmW.js +1 -0
  601. pythinker_code/web/static/assets/razor-C1TweQQi.js +1 -0
  602. pythinker_code/web/static/assets/red-bN70gL4F.js +1 -0
  603. pythinker_code/web/static/assets/reg-C-SQnVFl.js +1 -0
  604. pythinker_code/web/static/assets/regexp-CDVJQ6XC.js +1 -0
  605. pythinker_code/web/static/assets/rel-C3B-1QV4.js +1 -0
  606. pythinker_code/web/static/assets/requirementDiagram-UZGBJVZJ-8o9hozL-.js +64 -0
  607. pythinker_code/web/static/assets/riscv-BM1_JUlF.js +1 -0
  608. pythinker_code/web/static/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
  609. pythinker_code/web/static/assets/rose-pine-moon-D4_iv3hh.js +1 -0
  610. pythinker_code/web/static/assets/rose-pine-qdsjHGoJ.js +1 -0
  611. pythinker_code/web/static/assets/rosmsg-BJDFO7_C.js +1 -0
  612. pythinker_code/web/static/assets/rst-B0xPkSld.js +1 -0
  613. pythinker_code/web/static/assets/ruby-BvKwtOVI.js +1 -0
  614. pythinker_code/web/static/assets/rust-B1yitclQ.js +1 -0
  615. pythinker_code/web/static/assets/sankeyDiagram-TZEHDZUN-BLOSUixH.js +10 -0
  616. pythinker_code/web/static/assets/sas-cz2c8ADy.js +1 -0
  617. pythinker_code/web/static/assets/sass-Cj5Yp3dK.js +1 -0
  618. pythinker_code/web/static/assets/scala-C151Ov-r.js +1 -0
  619. pythinker_code/web/static/assets/scheme-C98Dy4si.js +1 -0
  620. pythinker_code/web/static/assets/scss-OYdSNvt2.js +1 -0
  621. pythinker_code/web/static/assets/sdbl-DVxCFoDh.js +1 -0
  622. pythinker_code/web/static/assets/sequenceDiagram-WL72ISMW-6F2G8JTU.js +145 -0
  623. pythinker_code/web/static/assets/shaderlab-Dg9Lc6iA.js +1 -0
  624. pythinker_code/web/static/assets/shellscript-Yzrsuije.js +1 -0
  625. pythinker_code/web/static/assets/shellsession-BADoaaVG.js +1 -0
  626. pythinker_code/web/static/assets/slack-dark-BthQWCQV.js +1 -0
  627. pythinker_code/web/static/assets/slack-ochin-DqwNpetd.js +1 -0
  628. pythinker_code/web/static/assets/smalltalk-BERRCDM3.js +1 -0
  629. pythinker_code/web/static/assets/snazzy-light-Bw305WKR.js +1 -0
  630. pythinker_code/web/static/assets/solarized-dark-DXbdFlpD.js +1 -0
  631. pythinker_code/web/static/assets/solarized-light-L9t79GZl.js +1 -0
  632. pythinker_code/web/static/assets/solidity-rGO070M0.js +1 -0
  633. pythinker_code/web/static/assets/soy-Brmx7dQM.js +1 -0
  634. pythinker_code/web/static/assets/sparql-rVzFXLq3.js +1 -0
  635. pythinker_code/web/static/assets/splunk-BtCnVYZw.js +1 -0
  636. pythinker_code/web/static/assets/sql-BLtJtn59.js +1 -0
  637. pythinker_code/web/static/assets/ssh-config-_ykCGR6B.js +1 -0
  638. pythinker_code/web/static/assets/stata-BH5u7GGu.js +1 -0
  639. pythinker_code/web/static/assets/stateDiagram-FKZM4ZOC-DP8xP0iJ.js +1 -0
  640. pythinker_code/web/static/assets/stateDiagram-v2-4FDKWEC3-1l6-EZNX.js +1 -0
  641. pythinker_code/web/static/assets/stylus-BEDo0Tqx.js +1 -0
  642. pythinker_code/web/static/assets/svelte-zxCyuUbr.js +1 -0
  643. pythinker_code/web/static/assets/swift-Dg5xB15N.js +1 -0
  644. pythinker_code/web/static/assets/synthwave-84-CbfX1IO0.js +1 -0
  645. pythinker_code/web/static/assets/system-verilog-CnnmHF94.js +1 -0
  646. pythinker_code/web/static/assets/systemd-4A_iFExJ.js +1 -0
  647. pythinker_code/web/static/assets/talonscript-CkByrt1z.js +1 -0
  648. pythinker_code/web/static/assets/tasl-QIJgUcNo.js +1 -0
  649. pythinker_code/web/static/assets/tcl-dwOrl1Do.js +1 -0
  650. pythinker_code/web/static/assets/templ-W15q3VgB.js +1 -0
  651. pythinker_code/web/static/assets/terraform-BETggiCN.js +1 -0
  652. pythinker_code/web/static/assets/tex-CvyZ59Mk.js +1 -0
  653. pythinker_code/web/static/assets/timeline-definition-IT6M3QCI-DMgruDfK.js +61 -0
  654. pythinker_code/web/static/assets/tokyo-night-hegEt444.js +1 -0
  655. pythinker_code/web/static/assets/toml-vGWfd6FD.js +1 -0
  656. pythinker_code/web/static/assets/treemap-KMMF4GRG-Bo9ZkrAK.js +128 -0
  657. pythinker_code/web/static/assets/ts-tags-zn1MmPIZ.js +1 -0
  658. pythinker_code/web/static/assets/tsv-B_m7g4N7.js +1 -0
  659. pythinker_code/web/static/assets/tsx-COt5Ahok.js +1 -0
  660. pythinker_code/web/static/assets/turtle-BsS91CYL.js +1 -0
  661. pythinker_code/web/static/assets/twig-CO9l9SDP.js +1 -0
  662. pythinker_code/web/static/assets/typescript-BPQ3VLAy.js +1 -0
  663. pythinker_code/web/static/assets/typespec-BGHnOYBU.js +1 -0
  664. pythinker_code/web/static/assets/typst-DHCkPAjA.js +1 -0
  665. pythinker_code/web/static/assets/v-BcVCzyr7.js +1 -0
  666. pythinker_code/web/static/assets/vala-CsfeWuGM.js +1 -0
  667. pythinker_code/web/static/assets/vb-D17OF-Vu.js +1 -0
  668. pythinker_code/web/static/assets/verilog-BQ8w6xss.js +1 -0
  669. pythinker_code/web/static/assets/vesper-DU1UobuO.js +1 -0
  670. pythinker_code/web/static/assets/vhdl-CeAyd5Ju.js +1 -0
  671. pythinker_code/web/static/assets/viml-CJc9bBzg.js +1 -0
  672. pythinker_code/web/static/assets/vitesse-black-Bkuqu6BP.js +1 -0
  673. pythinker_code/web/static/assets/vitesse-dark-D0r3Knsf.js +1 -0
  674. pythinker_code/web/static/assets/vitesse-light-CVO1_9PV.js +1 -0
  675. pythinker_code/web/static/assets/vue-DN_0RTcg.js +1 -0
  676. pythinker_code/web/static/assets/vue-html-AaS7Mt5G.js +1 -0
  677. pythinker_code/web/static/assets/vue-vine-CQOfvN7w.js +1 -0
  678. pythinker_code/web/static/assets/vyper-CDx5xZoG.js +1 -0
  679. pythinker_code/web/static/assets/wasm-CG6Dc4jp.js +1 -0
  680. pythinker_code/web/static/assets/wasm-MzD3tlZU.js +1 -0
  681. pythinker_code/web/static/assets/wenyan-BV7otONQ.js +1 -0
  682. pythinker_code/web/static/assets/wgsl-Dx-B1_4e.js +1 -0
  683. pythinker_code/web/static/assets/wikitext-BhOHFoWU.js +1 -0
  684. pythinker_code/web/static/assets/wit-5i3qLPDT.js +1 -0
  685. pythinker_code/web/static/assets/wolfram-lXgVvXCa.js +1 -0
  686. pythinker_code/web/static/assets/xml-sdJ4AIDG.js +1 -0
  687. pythinker_code/web/static/assets/xsl-CtQFsRM5.js +1 -0
  688. pythinker_code/web/static/assets/xychartDiagram-PRI3JC2R-GeF2johi.js +7 -0
  689. pythinker_code/web/static/assets/yaml-Buea-lGh.js +1 -0
  690. pythinker_code/web/static/assets/zenscript-DVFEvuxE.js +1 -0
  691. pythinker_code/web/static/assets/zig-VOosw3JB.js +1 -0
  692. pythinker_code/web/static/brand/apple-touch-icon.png +0 -0
  693. pythinker_code/web/static/brand/arctecture.webp +0 -0
  694. pythinker_code/web/static/brand/bimi-logo.svg +46 -0
  695. pythinker_code/web/static/brand/favicon.ico +0 -0
  696. pythinker_code/web/static/brand/fonts/dm-sans-latin-ext.woff2 +0 -0
  697. pythinker_code/web/static/brand/fonts/dm-sans-latin.woff2 +0 -0
  698. pythinker_code/web/static/brand/fonts/instrument-sans-latin-ext.woff2 +0 -0
  699. pythinker_code/web/static/brand/fonts/instrument-sans-latin.woff2 +0 -0
  700. pythinker_code/web/static/brand/fonts/instrument-serif-latin-ext.woff2 +0 -0
  701. pythinker_code/web/static/brand/fonts/instrument-serif-latin.woff2 +0 -0
  702. pythinker_code/web/static/brand/fonts/libre-baskerville-italic-latin-ext.woff2 +0 -0
  703. pythinker_code/web/static/brand/fonts/libre-baskerville-italic-latin.woff2 +0 -0
  704. pythinker_code/web/static/brand/fonts/libre-baskerville-latin-ext.woff2 +0 -0
  705. pythinker_code/web/static/brand/fonts/libre-baskerville-latin.woff2 +0 -0
  706. pythinker_code/web/static/brand/fonts/roboto-latin-ext.woff2 +0 -0
  707. pythinker_code/web/static/brand/fonts/roboto-latin.woff2 +0 -0
  708. pythinker_code/web/static/brand/icon-192.png +0 -0
  709. pythinker_code/web/static/brand/icon-512.png +0 -0
  710. pythinker_code/web/static/brand/icon.svg +1 -0
  711. pythinker_code/web/static/brand/logo.png +0 -0
  712. pythinker_code/web/static/brand/pythinker_animated.svg +79 -0
  713. pythinker_code/web/static/brand/robots.txt +4 -0
  714. pythinker_code/web/static/index.html +15 -0
  715. pythinker_code/web/static/logo.png +0 -0
  716. pythinker_code/web/store/__init__.py +1 -0
  717. pythinker_code/web/store/sessions.py +432 -0
  718. pythinker_code/wire/__init__.py +148 -0
  719. pythinker_code/wire/file.py +151 -0
  720. pythinker_code/wire/jsonrpc.py +263 -0
  721. pythinker_code/wire/protocol.py +2 -0
  722. pythinker_code/wire/root_hub.py +27 -0
  723. pythinker_code/wire/serde.py +26 -0
  724. pythinker_code/wire/server.py +1069 -0
  725. pythinker_code/wire/types.py +698 -0
  726. pythinker_code-2.0.0.dist-info/METADATA +660 -0
  727. pythinker_code-2.0.0.dist-info/RECORD +731 -0
  728. pythinker_code-2.0.0.dist-info/WHEEL +4 -0
  729. pythinker_code-2.0.0.dist-info/entry_points.txt +4 -0
  730. pythinker_code-2.0.0.dist-info/licenses/LICENSE +202 -0
  731. 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()