zjcode 0.0.3__tar.gz → 0.0.4__tar.gz

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 (380) hide show
  1. {zjcode-0.0.3 → zjcode-0.0.4}/PKG-INFO +1 -1
  2. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_version.py +1 -1
  3. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/input.py +75 -74
  4. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/media_utils.py +44 -6
  5. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/model_config.py +108 -0
  6. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/chat_input.py +12 -2
  7. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/model_selector.py +305 -6
  8. zjcode-0.0.4/doc/2026-07-19-/345/210/207/346/215/242/345/210/260/345/205/254/345/274/200PyPI.md +423 -0
  9. {zjcode-0.0.3 → zjcode-0.0.4}/pyproject.toml +1 -1
  10. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_app.py +3 -3
  11. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_media_utils.py +165 -130
  12. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_model_config.py +133 -0
  13. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_chat_input.py +81 -54
  14. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_model_selector.py +73 -3
  15. {zjcode-0.0.3 → zjcode-0.0.4}/uv.lock +1 -1
  16. zjcode-0.0.3/doc/2026-07-19-/345/210/207/346/215/242/345/210/260/345/205/254/345/274/200PyPI.md +0 -150
  17. {zjcode-0.0.3 → zjcode-0.0.4}/.gitignore +0 -0
  18. {zjcode-0.0.3 → zjcode-0.0.4}/AGENTS.md +0 -0
  19. {zjcode-0.0.3 → zjcode-0.0.4}/ARCHITECTURE.md +0 -0
  20. {zjcode-0.0.3 → zjcode-0.0.4}/CHANGELOG.md +0 -0
  21. {zjcode-0.0.3 → zjcode-0.0.4}/COMMANDS.md +0 -0
  22. {zjcode-0.0.3 → zjcode-0.0.4}/DEVELOPMENT.md +0 -0
  23. {zjcode-0.0.3 → zjcode-0.0.4}/Makefile +0 -0
  24. {zjcode-0.0.3 → zjcode-0.0.4}/README.md +0 -0
  25. {zjcode-0.0.3 → zjcode-0.0.4}/THREAT_MODEL.md +0 -0
  26. {zjcode-0.0.3 → zjcode-0.0.4}/bump-version.py +0 -0
  27. {zjcode-0.0.3 → zjcode-0.0.4}/dcode-dev.ps1 +0 -0
  28. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/__init__.py +0 -0
  29. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/__main__.py +0 -0
  30. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_ask_user_types.py +0 -0
  31. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_cli_context.py +0 -0
  32. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_constants.py +0 -0
  33. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_debug.py +0 -0
  34. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_debug_buffer.py +0 -0
  35. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_env_vars.py +0 -0
  36. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_fake_models.py +0 -0
  37. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_git.py +0 -0
  38. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_paths.py +0 -0
  39. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_server_config.py +0 -0
  40. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_session_stats.py +0 -0
  41. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_startup_error.py +0 -0
  42. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_testing_models.py +0 -0
  43. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_textual_patches.py +0 -0
  44. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/_tool_stream.py +0 -0
  45. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/agent.py +0 -0
  46. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/app.py +0 -0
  47. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/app.tcss +0 -0
  48. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/approval_mode.py +0 -0
  49. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/ask_user.py +0 -0
  50. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/auth_display.py +0 -0
  51. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/auth_store.py +0 -0
  52. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/built_in_skills/__init__.py +0 -0
  53. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/built_in_skills/remember/SKILL.md +0 -0
  54. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/built_in_skills/skill-creator/SKILL.md +0 -0
  55. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/built_in_skills/skill-creator/scripts/init_skill.py +0 -0
  56. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/built_in_skills/skill-creator/scripts/quick_validate.py +0 -0
  57. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/client/__init__.py +0 -0
  58. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/client/commands/__init__.py +0 -0
  59. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/client/commands/auth.py +0 -0
  60. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/client/commands/config.py +0 -0
  61. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/client/commands/mcp.py +0 -0
  62. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/client/commands/tools.py +0 -0
  63. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/client/launch/__init__.py +0 -0
  64. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/client/launch/server.py +0 -0
  65. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/client/launch/server_manager.py +0 -0
  66. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/client/non_interactive.py +0 -0
  67. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/client/remote_client.py +0 -0
  68. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/clipboard.py +0 -0
  69. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/command_registry.py +0 -0
  70. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/config.py +0 -0
  71. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/config_manifest.py +0 -0
  72. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/configurable_model.py +0 -0
  73. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/default_agent_prompt.md +0 -0
  74. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/doctor.py +0 -0
  75. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/editor.py +0 -0
  76. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/event_bus.py +0 -0
  77. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/extras_info.py +0 -0
  78. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/file_ops.py +0 -0
  79. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/formatting.py +0 -0
  80. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/goal_rubric.py +0 -0
  81. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/goal_tools.py +0 -0
  82. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/hooks.py +0 -0
  83. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/integrations/__init__.py +0 -0
  84. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/integrations/openai_codex.py +0 -0
  85. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/integrations/sandbox_config.py +0 -0
  86. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/integrations/sandbox_factory.py +0 -0
  87. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/integrations/sandbox_provider.py +0 -0
  88. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/integrations/sandbox_registry.py +0 -0
  89. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/iterm_cursor_guide.py +0 -0
  90. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/local_context.py +0 -0
  91. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/main.py +0 -0
  92. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/managed_tools.py +0 -0
  93. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/mcp_auth.py +0 -0
  94. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/mcp_config.py +0 -0
  95. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/mcp_disabled.py +0 -0
  96. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/mcp_login_service.py +0 -0
  97. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/mcp_oauth_ui.py +0 -0
  98. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/mcp_providers/__init__.py +0 -0
  99. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/mcp_providers/_registry.py +0 -0
  100. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/mcp_providers/base.py +0 -0
  101. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/mcp_providers/github.py +0 -0
  102. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/mcp_providers/slack.py +0 -0
  103. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/mcp_tools.py +0 -0
  104. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/mcp_trust.py +0 -0
  105. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/memory_guard.py +0 -0
  106. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/notifications.py +0 -0
  107. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/offload.py +0 -0
  108. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/onboarding.py +0 -0
  109. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/output.py +0 -0
  110. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/paste_collapse.py +0 -0
  111. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/project_utils.py +0 -0
  112. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/py.typed +0 -0
  113. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/reasoning_effort.py +0 -0
  114. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/reliable_rubric.py +0 -0
  115. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/resume_state.py +0 -0
  116. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/server_graph.py +0 -0
  117. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/session_end_summary.py +0 -0
  118. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/sessions.py +0 -0
  119. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/skills/__init__.py +0 -0
  120. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/skills/commands.py +0 -0
  121. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/skills/invocation.py +0 -0
  122. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/skills/load.py +0 -0
  123. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/skills/trust.py +0 -0
  124. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/state_migration.py +0 -0
  125. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/subagents.py +0 -0
  126. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/system_prompt.md +0 -0
  127. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/terminal_capabilities.py +0 -0
  128. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/terminal_escape.py +0 -0
  129. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/theme.py +0 -0
  130. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/todo_list_prompt.md +0 -0
  131. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tool_catalog.py +0 -0
  132. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tool_display.py +0 -0
  133. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tools.py +0 -0
  134. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/__init__.py +0 -0
  135. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/textual_adapter.py +0 -0
  136. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/__init__.py +0 -0
  137. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/_js_eval_display.py +0 -0
  138. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/_links.py +0 -0
  139. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/agent_selector.py +0 -0
  140. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/approval.py +0 -0
  141. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/ask_user.py +0 -0
  142. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/auth.py +0 -0
  143. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/autocomplete.py +0 -0
  144. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/codex_auth.py +0 -0
  145. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/cwd_switch.py +0 -0
  146. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/debug_console.py +0 -0
  147. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/diff.py +0 -0
  148. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/effort_selector.py +0 -0
  149. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/goal_review.py +0 -0
  150. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/goal_status.py +0 -0
  151. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/history.py +0 -0
  152. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/install_confirm.py +0 -0
  153. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/launch_init.py +0 -0
  154. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/loading.py +0 -0
  155. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/mcp_login.py +0 -0
  156. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/mcp_reconnect.py +0 -0
  157. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/mcp_viewer.py +0 -0
  158. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/message_store.py +0 -0
  159. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/messages.py +0 -0
  160. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/notification_center.py +0 -0
  161. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/notification_detail.py +0 -0
  162. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/notification_settings.py +0 -0
  163. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/restart_prompt.py +0 -0
  164. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/skill_trust.py +0 -0
  165. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/startup_tip.py +0 -0
  166. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/status.py +0 -0
  167. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/subagent_panel.py +0 -0
  168. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/theme_selector.py +0 -0
  169. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/thread_selector.py +0 -0
  170. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/tool_renderers.py +0 -0
  171. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/tool_widgets.py +0 -0
  172. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/update_available.py +0 -0
  173. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/update_confirm.py +0 -0
  174. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/update_progress.py +0 -0
  175. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/tui/widgets/welcome.py +0 -0
  176. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/turn_end_summary.py +0 -0
  177. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/ui.py +0 -0
  178. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/unicode_security.py +0 -0
  179. {zjcode-0.0.3 → zjcode-0.0.4}/deepagents_code/update_check.py +0 -0
  180. {zjcode-0.0.3 → zjcode-0.0.4}/doc/2025-01-24-/347/211/210/346/234/254/345/215/207/347/272/247/344/270/216Web/351/203/250/347/275/262.md" +0 -0
  181. {zjcode-0.0.3 → zjcode-0.0.4}/doc/2025-07-27-/345/244/215/345/210/266/346/214/211/351/222/256-CopyTurnButton/345/256/236/347/216/260.md" +0 -0
  182. {zjcode-0.0.3 → zjcode-0.0.4}/doc/2025-07-27-/351/241/271/347/233/256/345/210/206/346/236/220-deepagents-code/346/236/266/346/236/204/344/270/216/346/250/241/345/235/227/346/246/202/350/247/210.md" +0 -0
  183. {zjcode-0.0.3 → zjcode-0.0.4}/doc/2026-01-24-/351/243/236/344/271/246/346/216/245/345/205/245deepagents/350/260/203/347/240/224.md" +0 -0
  184. {zjcode-0.0.3 → zjcode-0.0.4}/doc/2026-07-08-windows-dev-deployment-script.md +0 -0
  185. {zjcode-0.0.3 → zjcode-0.0.4}/doc/2026-07-11-/345/233/276/347/211/207/347/262/230/350/264/264BadRequestError-Agent/345/244/261/346/225/210/344/277/256/345/244/215.md" +0 -0
  186. {zjcode-0.0.3 → zjcode-0.0.4}/doc/2026-07-12-/346/257/217/350/275/256/346/211/223/345/215/260/346/250/241/345/236/213/345/223/215/345/272/224/345/205/203/346/225/260/346/215/256.md" +0 -0
  187. {zjcode-0.0.3 → zjcode-0.0.4}/doc/2026-07-12-/347/201/253/345/261/261/345/274/225/346/223/216/345/220/204/346/250/241/345/236/213/347/274/226/347/240/201/350/203/275/345/212/233/350/257/246/347/273/206/350/257/204/346/265/213/346/212/245/345/221/212.md" +0 -0
  188. {zjcode-0.0.3 → zjcode-0.0.4}/doc/2026-07-12-/347/201/253/345/261/261/345/274/225/346/223/216/346/250/241/345/236/213/347/274/226/347/240/201/350/203/275/345/212/233/345/237/272/345/207/206/346/265/213/350/257/225.md" +0 -0
  189. {zjcode-0.0.3 → zjcode-0.0.4}/doc/2026-07-15-learn/345/210/206/346/224/257/344/277/256/345/244/215-/346/265/213/350/257/225/344/270/216lint/345/205/250/347/273/277.md" +0 -0
  190. {zjcode-0.0.3 → zjcode-0.0.4}/doc/2026-07-18-session-end-summary/345/206/205/347/275/256.md" +0 -0
  191. {zjcode-0.0.3 → zjcode-0.0.4}/doc/2026-07-18-/345/267/245/345/205/267/347/273/204/351/273/230/350/256/244/345/261/225/345/274/200.md" +0 -0
  192. {zjcode-0.0.3 → zjcode-0.0.4}/examples/skills/arxiv-search/SKILL.md +0 -0
  193. {zjcode-0.0.3 → zjcode-0.0.4}/examples/skills/arxiv-search/arxiv_search.py +0 -0
  194. {zjcode-0.0.3 → zjcode-0.0.4}/examples/skills/langgraph-docs/SKILL.md +0 -0
  195. {zjcode-0.0.3 → zjcode-0.0.4}/examples/skills/skill-creator/SKILL.md +0 -0
  196. {zjcode-0.0.3 → zjcode-0.0.4}/examples/skills/skill-creator/scripts/init_skill.py +0 -0
  197. {zjcode-0.0.3 → zjcode-0.0.4}/examples/skills/skill-creator/scripts/quick_validate.py +0 -0
  198. {zjcode-0.0.3 → zjcode-0.0.4}/examples/skills/web-research/SKILL.md +0 -0
  199. {zjcode-0.0.3 → zjcode-0.0.4}/hatch_build.py +0 -0
  200. {zjcode-0.0.3 → zjcode-0.0.4}/images/tui.png +0 -0
  201. {zjcode-0.0.3 → zjcode-0.0.4}/run-dcode-dev.ps1 +0 -0
  202. {zjcode-0.0.3 → zjcode-0.0.4}/run-dcode-dev.sh +0 -0
  203. {zjcode-0.0.3 → zjcode-0.0.4}/run-publish.sh +0 -0
  204. {zjcode-0.0.3 → zjcode-0.0.4}/run-setup-dev-env.sh +0 -0
  205. {zjcode-0.0.3 → zjcode-0.0.4}/scripts/benchmark_huoshan_models.py +0 -0
  206. {zjcode-0.0.3 → zjcode-0.0.4}/scripts/benchmark_report_20260712_151546.md +0 -0
  207. {zjcode-0.0.3 → zjcode-0.0.4}/scripts/benchmark_results_20260712_151546.json +0 -0
  208. {zjcode-0.0.3 → zjcode-0.0.4}/scripts/check_imports.py +0 -0
  209. {zjcode-0.0.3 → zjcode-0.0.4}/scripts/debug_server.sh +0 -0
  210. {zjcode-0.0.3 → zjcode-0.0.4}/scripts/generate_commands_catalog.py +0 -0
  211. {zjcode-0.0.3 → zjcode-0.0.4}/scripts/install.sh +0 -0
  212. {zjcode-0.0.3 → zjcode-0.0.4}/setup-dev-env.ps1 +0 -0
  213. {zjcode-0.0.3 → zjcode-0.0.4}/tests/README.md +0 -0
  214. {zjcode-0.0.3 → zjcode-0.0.4}/tests/integration_tests/__init__.py +0 -0
  215. {zjcode-0.0.3 → zjcode-0.0.4}/tests/integration_tests/benchmarks/__init__.py +0 -0
  216. {zjcode-0.0.3 → zjcode-0.0.4}/tests/integration_tests/benchmarks/test_codspeed_import_benchmarks.py +0 -0
  217. {zjcode-0.0.3 → zjcode-0.0.4}/tests/integration_tests/benchmarks/test_startup_benchmarks.py +0 -0
  218. {zjcode-0.0.3 → zjcode-0.0.4}/tests/integration_tests/conftest.py +0 -0
  219. {zjcode-0.0.3 → zjcode-0.0.4}/tests/integration_tests/test_acp_mode.py +0 -0
  220. {zjcode-0.0.3 → zjcode-0.0.4}/tests/integration_tests/test_auto_approve_remote.py +0 -0
  221. {zjcode-0.0.3 → zjcode-0.0.4}/tests/integration_tests/test_compact_resume.py +0 -0
  222. {zjcode-0.0.3 → zjcode-0.0.4}/tests/integration_tests/test_sandbox_factory.py +0 -0
  223. {zjcode-0.0.3 → zjcode-0.0.4}/tests/integration_tests/test_sandbox_operations.py +0 -0
  224. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/__init__.py +0 -0
  225. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/client/__init__.py +0 -0
  226. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/client/commands/__init__.py +0 -0
  227. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/client/commands/test_auth.py +0 -0
  228. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/client/commands/test_mcp.py +0 -0
  229. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/client/commands/test_tools.py +0 -0
  230. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/conftest.py +0 -0
  231. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/data/coding_agent_v1_validator.json +0 -0
  232. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/skills/__init__.py +0 -0
  233. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/skills/test_commands.py +0 -0
  234. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/skills/test_load.py +0 -0
  235. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/skills/test_skill_trust.py +0 -0
  236. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/skills/test_skills_json.py +0 -0
  237. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/smoke_tests/__init__.py +0 -0
  238. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/smoke_tests/conftest.py +0 -0
  239. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/smoke_tests/snapshots/system_prompt_interactive_local.md +0 -0
  240. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/smoke_tests/test_system_prompt.py +0 -0
  241. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test__tool_stream.py +0 -0
  242. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_agent.py +0 -0
  243. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_agent_friendly.py +0 -0
  244. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_approval_mode.py +0 -0
  245. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_args.py +0 -0
  246. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_ask_user_middleware.py +0 -0
  247. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_auth_display.py +0 -0
  248. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_auth_store.py +0 -0
  249. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_auth_web_search_restart.py +0 -0
  250. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_charset.py +0 -0
  251. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_clipboard.py +0 -0
  252. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_coding_agent_metadata.py +0 -0
  253. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_command_registry.py +0 -0
  254. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_compact_tool.py +0 -0
  255. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_config.py +0 -0
  256. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_config_manifest.py +0 -0
  257. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_configurable_model.py +0 -0
  258. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_cursor_blink.py +0 -0
  259. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_cursor_style.py +0 -0
  260. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_debug.py +0 -0
  261. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_debug_buffer.py +0 -0
  262. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_debug_console.py +0 -0
  263. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_doctor.py +0 -0
  264. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_editor.py +0 -0
  265. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_end_to_end.py +0 -0
  266. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_env_vars.py +0 -0
  267. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_error_handling.py +0 -0
  268. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_event_bus.py +0 -0
  269. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_exception_handling.py +0 -0
  270. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_extras_info.py +0 -0
  271. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_file_ops.py +0 -0
  272. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_formatting.py +0 -0
  273. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_git.py +0 -0
  274. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_goal_rubric.py +0 -0
  275. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_goal_tools.py +0 -0
  276. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_hatch_build.py +0 -0
  277. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_hooks.py +0 -0
  278. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_imports.py +0 -0
  279. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_input_parsing.py +0 -0
  280. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_install_command.py +0 -0
  281. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_install_script.py +0 -0
  282. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_iterm_cursor_guide.py +0 -0
  283. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_local_context.py +0 -0
  284. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_main.py +0 -0
  285. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_main_acp_mode.py +0 -0
  286. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_main_args.py +0 -0
  287. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_managed_tools.py +0 -0
  288. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_mcp_auth.py +0 -0
  289. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_mcp_config.py +0 -0
  290. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_mcp_disabled.py +0 -0
  291. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_mcp_login_service.py +0 -0
  292. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_mcp_oauth_ui.py +0 -0
  293. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_mcp_tools.py +0 -0
  294. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_mcp_trust.py +0 -0
  295. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_memory_guard.py +0 -0
  296. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_model_switch.py +0 -0
  297. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_non_interactive.py +0 -0
  298. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_notifications.py +0 -0
  299. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_offload.py +0 -0
  300. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_offload_dict_messages.py +0 -0
  301. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_onboarding.py +0 -0
  302. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_openai_codex_integration.py +0 -0
  303. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_output.py +0 -0
  304. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_paths.py +0 -0
  305. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_reasoning_effort.py +0 -0
  306. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_reliable_rubric.py +0 -0
  307. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_reload.py +0 -0
  308. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_remote_client.py +0 -0
  309. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_resume_state.py +0 -0
  310. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_rubric_cli.py +0 -0
  311. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_sandbox_config.py +0 -0
  312. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_sandbox_factory.py +0 -0
  313. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_sandbox_provider.py +0 -0
  314. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_sandbox_registry.py +0 -0
  315. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_server.py +0 -0
  316. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_server_config.py +0 -0
  317. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_server_graph.py +0 -0
  318. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_server_helpers.py +0 -0
  319. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_server_manager.py +0 -0
  320. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_session_end_summary.py +0 -0
  321. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_session_stats.py +0 -0
  322. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_sessions.py +0 -0
  323. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_shell_allow_list.py +0 -0
  324. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_skill_invocation.py +0 -0
  325. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_startup_fast_paths.py +0 -0
  326. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_state_migration.py +0 -0
  327. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_subagents.py +0 -0
  328. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_terminal_capabilities.py +0 -0
  329. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_terminal_escape.py +0 -0
  330. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_terminal_progress_preference.py +0 -0
  331. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_textual_patches.py +0 -0
  332. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_theme.py +0 -0
  333. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_threads_resume.py +0 -0
  334. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_tool_catalog.py +0 -0
  335. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_tool_display.py +0 -0
  336. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_tracing_replicas.py +0 -0
  337. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_transcript_virtualization.py +0 -0
  338. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_turn_end_summary.py +0 -0
  339. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_ui.py +0 -0
  340. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_unicode_security.py +0 -0
  341. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_update_check.py +0 -0
  342. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/test_version.py +0 -0
  343. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tools/__init__.py +0 -0
  344. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tools/test_current_thread.py +0 -0
  345. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tools/test_fetch_url.py +0 -0
  346. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/__init__.py +0 -0
  347. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/test_subagent_stream.py +0 -0
  348. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/test_textual_adapter.py +0 -0
  349. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/__init__.py +0 -0
  350. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_agent_selector.py +0 -0
  351. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_approval.py +0 -0
  352. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_ask_user.py +0 -0
  353. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_auth_widgets.py +0 -0
  354. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_autocomplete.py +0 -0
  355. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_cwd_switch.py +0 -0
  356. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_diff.py +0 -0
  357. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_goal_review.py +0 -0
  358. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_history.py +0 -0
  359. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_install_confirm.py +0 -0
  360. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_launch_init.py +0 -0
  361. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_links.py +0 -0
  362. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_loading.py +0 -0
  363. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_mcp_login_modal.py +0 -0
  364. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_mcp_reconnect.py +0 -0
  365. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_mcp_viewer.py +0 -0
  366. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_message_store.py +0 -0
  367. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_messages.py +0 -0
  368. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_notification_center.py +0 -0
  369. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_notification_detail.py +0 -0
  370. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_restart_prompt.py +0 -0
  371. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_skill_trust_screen.py +0 -0
  372. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_startup_tip.py +0 -0
  373. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_status.py +0 -0
  374. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_subagent_panel.py +0 -0
  375. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_thread_selector.py +0 -0
  376. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_tool_renderers.py +0 -0
  377. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_update_available.py +0 -0
  378. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_update_confirm.py +0 -0
  379. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_update_progress.py +0 -0
  380. {zjcode-0.0.3 → zjcode-0.0.4}/tests/unit_tests/tui/widgets/test_welcome.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zjcode
3
- Version: 0.0.3
3
+ Version: 0.0.4
4
4
  Summary: Private-branded terminal coding agent forked from deepagents-code.
5
5
  Project-URL: Homepage, https://docs.langchain.com/oss/python/deepagents/overview
6
6
  Project-URL: Documentation, https://reference.langchain.com/python/deepagents/
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Keep the `x-release-please-version` annotation — release-please uses it to
4
4
  # bump `__version__` in sync with `pyproject.toml` on every release PR.
5
- __version__ = "0.0.3" # x-release-please-version
5
+ __version__ = "0.0.4" # x-release-please-version
6
6
 
7
7
  DISTRIBUTION_NAME = "zjcode"
8
8
  """Distribution (wheel/PyPI) name for this private-branded build.
@@ -4,6 +4,7 @@ import logging
4
4
  import re
5
5
  import shlex
6
6
  from dataclasses import dataclass, replace
7
+ from datetime import datetime
7
8
  from difflib import SequenceMatcher
8
9
  from pathlib import Path
9
10
  from typing import Literal
@@ -59,20 +60,45 @@ slash commands, so a `/` mid-string is not highlighted.
59
60
  MediaKind = Literal["image", "video"]
60
61
  """Accepted values for the `kind` parameter in `MediaTracker` methods."""
61
62
 
62
- IMAGE_PLACEHOLDER_PATTERN = re.compile(r"\[image (?P<id>\d+)\]")
63
- """Pattern for image placeholders with a named `id` capture group.
63
+ IMAGE_PLACEHOLDER_PATTERN = re.compile(
64
+ r"\[img_(?P<id>[0-9]+(?:_[0-9]+)?)\]"
65
+ )
66
+ """Pattern for image placeholders (e.g. ``[img_20250115143022]``).
64
67
 
65
- Used to extract numeric IDs from placeholder tokens so the tracker can prune
66
- stale entries and compute the next available ID.
68
+ The id is either a local timestamp ``YYYYMMDDHHMMSS`` or (in tests) a plain
69
+ counter, optionally followed by ``_N`` to disambiguate same-second collisions.
67
70
  """
68
71
 
69
- VIDEO_PLACEHOLDER_PATTERN = re.compile(r"\[video (?P<id>\d+)\]")
70
- """Pattern for video placeholders with a named `id` capture group.
72
+ VIDEO_PLACEHOLDER_PATTERN = re.compile(
73
+ r"\[vid_(?P<id>[0-9]+(?:_[0-9]+)?)\]"
74
+ )
75
+ """Pattern for video placeholders (e.g. ``[vid_20250115143022]``).
71
76
 
72
- Used to extract numeric IDs from placeholder tokens so the tracker can prune
73
- stale entries and compute the next available ID.
77
+ Same id scheme as `IMAGE_PLACEHOLDER_PATTERN` but with a ``vid_`` prefix so
78
+ images and videos never share an id namespace.
74
79
  """
75
80
 
81
+ _MEDIA_PLACEHOLDER_PREFIX: dict[MediaKind, str] = {
82
+ "image": "img_",
83
+ "video": "vid_",
84
+ }
85
+
86
+
87
+ def _now_timestamp() -> str:
88
+ """Return the current local time formatted as ``YYYYMMDDHHMMSS``."""
89
+ return datetime.now().strftime("%Y%m%d%H%M%S")
90
+
91
+
92
+ def _generate_media_id(kind: MediaKind) -> str: # noqa: ARG001
93
+ """Generate a placeholder id for a new media attachment.
94
+
95
+ Exposed as a hook so tests can substitute a deterministic id generator
96
+ without patching `datetime`. ``kind`` is accepted for future
97
+ per-namespace override needs but the production path returns the same
98
+ timestamp for both images and videos.
99
+ """
100
+ return _now_timestamp()
101
+
76
102
  _UNICODE_SPACE_EQUIVALENTS = str.maketrans(
77
103
  {
78
104
  "\u00a0": " ", # NO-BREAK SPACE
@@ -111,13 +137,11 @@ class MediaTracker:
111
137
  def __init__(self) -> None:
112
138
  """Initialize an empty media tracker.
113
139
 
114
- Sets up empty lists to store images and videos, and initializes the
115
- ID counters to 1 for generating unique placeholder identifiers.
140
+ Sets up empty lists to store images and videos. Placeholder IDs are now
141
+ derived from timestamps, so there is no per-tracker counter to keep.
116
142
  """
117
143
  self.images: list[ImageData] = []
118
144
  self.videos: list[VideoData] = []
119
- self.next_image_id: int = 1
120
- self.next_video_id: int = 1
121
145
 
122
146
  def add_media(
123
147
  self,
@@ -131,38 +155,55 @@ class MediaTracker:
131
155
  Args:
132
156
  data: The image or video data to track.
133
157
  kind: Media type key.
134
- existing_text: Current draft text. Placeholder IDs already present
135
- here are skipped so literal user text is not bound to new media.
158
+ existing_text: Current draft text. Placeholder tokens already
159
+ present there are skipped so literal user text is not rebound
160
+ to new media (a same-second collision falls through to a
161
+ ``_2`` / ``_3`` suffix).
136
162
 
137
163
  Returns:
138
- Placeholder string like "[image 1]" or "[video 1]".
164
+ Placeholder string like ``[img_20250115143022]`` or
165
+ ``[vid_20250115143022]``. When the base timestamp is already used
166
+ in the current draft or by another tracked item, a ``_N`` suffix
167
+ is appended so every attachment gets a unique token.
139
168
  """
169
+ prefix = _MEDIA_PLACEHOLDER_PREFIX[kind]
170
+ base_id = _generate_media_id(kind)
171
+ existing = self._all_placeholders() | self._draft_placeholders(existing_text)
172
+ candidate = f"[{prefix}{base_id}]"
173
+ suffix = 2
174
+ while candidate in existing:
175
+ candidate = f"[{prefix}{base_id}_{suffix}]"
176
+ suffix += 1
177
+ data.placeholder = candidate
140
178
  if kind == "image":
141
- while f"[image {self.next_image_id}]" in existing_text:
142
- self.next_image_id += 1
143
- placeholder = f"[image {self.next_image_id}]"
144
- data.placeholder = placeholder
145
179
  self.images.append(data) # ty: ignore[invalid-argument-type]
146
- self.next_image_id += 1
147
180
  else:
148
- while f"[video {self.next_video_id}]" in existing_text:
149
- self.next_video_id += 1
150
- placeholder = f"[video {self.next_video_id}]"
151
- data.placeholder = placeholder
152
181
  self.videos.append(data) # ty: ignore[invalid-argument-type]
153
- self.next_video_id += 1
154
- return placeholder
182
+ return candidate
183
+
184
+ def _all_placeholders(self) -> set[str]:
185
+ """Return placeholders currently held by this tracker."""
186
+ return {item.placeholder for item in (*self.images, *self.videos)}
187
+
188
+ @staticmethod
189
+ def _draft_placeholders(text: str) -> set[str]:
190
+ """Return every media placeholder token literally present in `text`."""
191
+ tokens: set[str] = set()
192
+ for pattern in (IMAGE_PLACEHOLDER_PATTERN, VIDEO_PLACEHOLDER_PATTERN):
193
+ tokens.update(match.group(0) for match in pattern.finditer(text))
194
+ return tokens
155
195
 
156
196
  def add_image(self, image_data: ImageData, *, existing_text: str = "") -> str:
157
197
  """Add an image and return its placeholder text.
158
198
 
159
199
  Args:
160
200
  image_data: The image data to track.
161
- existing_text: Current draft text. Placeholder IDs already present
162
- here are skipped so literal user text is not bound to new media.
201
+ existing_text: Current draft text. Placeholder tokens already
202
+ present there are skipped so literal user text is not bound to
203
+ new media.
163
204
 
164
205
  Returns:
165
- Placeholder string like "[image 1]".
206
+ Placeholder string like ``[img_20250115143022]``.
166
207
  """
167
208
  return self.add_media(image_data, "image", existing_text=existing_text)
168
209
 
@@ -171,11 +212,12 @@ class MediaTracker:
171
212
 
172
213
  Args:
173
214
  video_data: The video data to track.
174
- existing_text: Current draft text. Placeholder IDs already present
175
- here are skipped so literal user text is not bound to new media.
215
+ existing_text: Current draft text. Placeholder tokens already
216
+ present there are skipped so literal user text is not bound to
217
+ new media.
176
218
 
177
219
  Returns:
178
- Placeholder string like "[video 1]".
220
+ Placeholder string like ``[vid_20250115143022]``.
179
221
  """
180
222
  return self.add_media(video_data, "video", existing_text=existing_text)
181
223
 
@@ -196,19 +238,15 @@ class MediaTracker:
196
238
  return list(self.videos)
197
239
 
198
240
  def clear(self) -> None:
199
- """Clear all tracked media and reset counters."""
241
+ """Clear all tracked media."""
200
242
  self.images.clear()
201
243
  self.videos.clear()
202
- self.next_image_id = 1
203
- self.next_video_id = 1
204
244
 
205
245
  def snapshot(self) -> "MediaTracker":
206
246
  """Return an independent copy of the currently tracked media."""
207
247
  tracker = MediaTracker()
208
248
  tracker.images = [replace(img) for img in self.images]
209
249
  tracker.videos = [replace(vid) for vid in self.videos]
210
- tracker.next_image_id = self.next_image_id
211
- tracker.next_video_id = self.next_video_id
212
250
  return tracker
213
251
 
214
252
  def restore(self, snapshot: "MediaTracker") -> None:
@@ -219,8 +257,6 @@ class MediaTracker:
219
257
  """
220
258
  self.images = [replace(img) for img in snapshot.images]
221
259
  self.videos = [replace(vid) for vid in snapshot.videos]
222
- self.next_image_id = snapshot.next_image_id
223
- self.next_video_id = snapshot.next_video_id
224
260
 
225
261
  def sync_to_text(
226
262
  self,
@@ -298,12 +334,6 @@ class MediaTracker:
298
334
  self._update_placeholder_spans(
299
335
  self.images, matches, text, previous_text, cursor_offset
300
336
  )
301
- if not self.images:
302
- self.next_image_id = 1
303
- else:
304
- self.next_image_id = self._max_placeholder_id(
305
- self.images, IMAGE_PLACEHOLDER_PATTERN, len(self.images)
306
- )
307
337
  return bool(placeholders)
308
338
 
309
339
  def _sync_kind_videos(
@@ -329,12 +359,6 @@ class MediaTracker:
329
359
  self._update_placeholder_spans(
330
360
  self.videos, matches, text, previous_text, cursor_offset
331
361
  )
332
- if not self.videos:
333
- self.next_video_id = 1
334
- else:
335
- self.next_video_id = self._max_placeholder_id(
336
- self.videos, VIDEO_PLACEHOLDER_PATTERN, len(self.videos)
337
- )
338
362
  return bool(placeholders)
339
363
 
340
364
  def _update_placeholder_spans(
@@ -440,29 +464,6 @@ class MediaTracker:
440
464
  break
441
465
  return None
442
466
 
443
- @staticmethod
444
- def _max_placeholder_id(
445
- items: list[ImageData] | list[VideoData],
446
- pattern: re.Pattern[str],
447
- fallback_count: int,
448
- ) -> int:
449
- """Compute next ID from the highest surviving placeholder.
450
-
451
- Args:
452
- items: Surviving media items.
453
- pattern: Placeholder regex with an `id` group.
454
- fallback_count: Fallback when no IDs can be parsed.
455
-
456
- Returns:
457
- Next ID value (max_id + 1).
458
- """
459
- max_id = 0
460
- for item in items:
461
- match = pattern.fullmatch(item.placeholder)
462
- if match is not None:
463
- max_id = max(max_id, int(match.group("id")))
464
- return max_id + 1 if max_id else fallback_count + 1
465
-
466
467
 
467
468
  def parse_file_mentions(text: str) -> tuple[str, list[Path]]:
468
469
  r"""Extract `@file` mentions and return the text with resolved file paths.
@@ -52,6 +52,44 @@ VIDEO_EXTENSIONS: frozenset[str] = frozenset(
52
52
  MAX_MEDIA_BYTES: int = 20 * 1024 * 1024
53
53
  """Maximum media file size (20 MB). Keeps base64 payload under ~27 MB."""
54
54
 
55
+ PASTED_MEDIA_DIR: pathlib.Path = pathlib.Path.home() / ".zjcode" / "pasted"
56
+ """Directory where pasted media is archived for local reference.
57
+
58
+ Images pasted into the TUI are decoded and written here so the user can inspect
59
+ or forward the original file later. The base64 payload is still what gets sent
60
+ to the model; this local copy is purely an archive of what was pasted.
61
+ """
62
+
63
+
64
+ def save_pasted_media(base64_data: str, stem: str, image_format: str) -> pathlib.Path | None:
65
+ """Persist a pasted media payload to `PASTED_MEDIA_DIR` as `<stem>.<ext>`.
66
+
67
+ Failures are logged and swallowed: the local archive is a convenience, not
68
+ a hard requirement. The caller keeps the in-memory base64 blob regardless.
69
+
70
+ Args:
71
+ base64_data: Base64-encoded media bytes (no data-URL prefix).
72
+ stem: File stem (typically the placeholder id such as
73
+ ``img_20250115143022``). Callers guarantee uniqueness within the
74
+ current draft.
75
+ image_format: Image format label (e.g. ``"png"``, ``"jpeg"``); used to
76
+ derive the file extension.
77
+
78
+ Returns:
79
+ The absolute path written on success, ``None`` when the write failed.
80
+ """
81
+ try:
82
+ PASTED_MEDIA_DIR.mkdir(parents=True, exist_ok=True)
83
+ extension = image_format.lower() or "png"
84
+ if extension == "jpeg":
85
+ extension = "jpg"
86
+ target = PASTED_MEDIA_DIR / f"{stem}.{extension}"
87
+ target.write_bytes(base64.b64decode(base64_data))
88
+ except (OSError, ValueError) as exc:
89
+ logger.warning("Failed to archive pasted media to %s: %s", PASTED_MEDIA_DIR, exc)
90
+ return None
91
+ return target
92
+
55
93
 
56
94
  def strip_media_placeholders(
57
95
  text: str,
@@ -61,7 +99,7 @@ def strip_media_placeholders(
61
99
  ) -> str:
62
100
  """Remove display-only media placeholders from user text.
63
101
 
64
- Placeholders like `[image 1]` are inserted into the terminal input purely for
102
+ Placeholders like `[img_20250115143022]` are inserted into the terminal input purely for
65
103
  display; the actual media travels as structured content blocks. They must not
66
104
  leak into the canonical model-facing message or LangSmith trace as if the user
67
105
  typed them.
@@ -74,7 +112,7 @@ def strip_media_placeholders(
74
112
  Args:
75
113
  text: Raw user text that may contain media placeholders.
76
114
  placeholders: Exact placeholder tokens for the media actually attached to
77
- this message (e.g. ``["[image 1]", "[video 1]"]``).
115
+ this message (e.g. ``["[img_20250115143022]", "[vid_20250115143022]"]``).
78
116
  placeholder_spans: Exact `(start, end)` spans for tracked display tokens
79
117
  in `text`, when known.
80
118
 
@@ -118,7 +156,7 @@ def strip_media_placeholders(
118
156
 
119
157
  # Only strip spaces/tabs (not newlines) so code indentation on lines after a
120
158
  # removed placeholder is preserved. A full .strip() would collapse
121
- # "[image 1]\n def foo():" to "def foo():", losing the leading indent.
159
+ # "[img_20250115143022]\n def foo():" to "def foo():", losing the leading indent.
122
160
  cleaned = text
123
161
  for start, end in sorted(spans, reverse=True):
124
162
  cleaned = cleaned[:start] + cleaned[end:]
@@ -183,7 +221,7 @@ class ImageData:
183
221
 
184
222
  base64_data: str
185
223
  format: str # "png", "jpeg", etc.
186
- placeholder: str # Display text like "[image 1]"
224
+ placeholder: str # Display text like "[img_20250115143022]"
187
225
  placeholder_span: tuple[int, int] | None = None
188
226
 
189
227
  def to_message_content(self) -> dict:
@@ -204,7 +242,7 @@ class VideoData:
204
242
 
205
243
  base64_data: str
206
244
  format: str # "mp4", "quicktime", etc.
207
- placeholder: str # Display text like "[video 1]"
245
+ placeholder: str # Display text like "[vid_20250115143022]"
208
246
  placeholder_span: tuple[int, int] | None = None
209
247
 
210
248
  def to_message_content(self) -> "VideoContentBlock":
@@ -796,7 +834,7 @@ def create_multimodal_content(
796
834
  content_blocks = []
797
835
 
798
836
  # Add text block. Strip only the display-only placeholders bound to the media
799
- # actually attached here (e.g. "[image 1]") so the canonical/model-facing text
837
+ # actually attached here (e.g. "[img_20250115143022]") so the canonical/model-facing text
800
838
  # never contains fake user-authored placeholder text. When a span is known,
801
839
  # text that merely resembles the schema is preserved exactly; without a span
802
840
  # `strip_media_placeholders` falls back to removing one occurrence per item,
@@ -4154,3 +4154,111 @@ def save_custom_provider(
4154
4154
 
4155
4155
  clear_caches()
4156
4156
  return True
4157
+
4158
+
4159
+ class ModelDiscoveryError(Exception):
4160
+ """Raised when discovering models from a provider's `/models` endpoint fails.
4161
+
4162
+ The message is user-facing and safe to render verbatim in the TUI.
4163
+ """
4164
+
4165
+
4166
+ def fetch_provider_models(
4167
+ base_url: str,
4168
+ api_key: str | None = None,
4169
+ *,
4170
+ timeout: float = 10.0,
4171
+ include_retiring: bool = True,
4172
+ ) -> list[str]:
4173
+ """Fetch the list of available models from an OpenAI-compatible provider.
4174
+
4175
+ Calls `GET {base_url}/models` with a Bearer token when `api_key` is
4176
+ provided. Filters out models whose `status` is `"Shutdown"` (a Volcengine
4177
+ Ark extension); other providers usually omit the field, in which case all
4178
+ models are kept.
4179
+
4180
+ Args:
4181
+ base_url: Provider's OpenAI-compatible API base URL (with or without a
4182
+ trailing slash).
4183
+ api_key: Optional API key sent as `Authorization: Bearer <key>`.
4184
+ timeout: Total request timeout in seconds.
4185
+ include_retiring: When `False`, also drop models with `status="Retiring"`.
4186
+
4187
+ Returns:
4188
+ Sorted list of unique model IDs.
4189
+
4190
+ Raises:
4191
+ ModelDiscoveryError: If the request fails, the response is not
4192
+ successful, or the payload cannot be parsed as an OpenAI-style
4193
+ model list.
4194
+ """
4195
+ import httpx # local import to avoid pulling httpx at module import time
4196
+
4197
+ if not base_url:
4198
+ msg = "Base URL is required to discover models."
4199
+ raise ModelDiscoveryError(msg)
4200
+
4201
+ url = base_url.rstrip("/") + "/models"
4202
+ headers: dict[str, str] = {"Accept": "application/json"}
4203
+ if api_key:
4204
+ headers["Authorization"] = f"Bearer {api_key}"
4205
+
4206
+ try:
4207
+ response = httpx.get(url, headers=headers, timeout=timeout)
4208
+ except httpx.TimeoutException as exc:
4209
+ msg = f"Request to {url} timed out after {timeout:.0f}s."
4210
+ raise ModelDiscoveryError(msg) from exc
4211
+ except httpx.HTTPError as exc:
4212
+ msg = f"Could not reach {url}: {exc}"
4213
+ raise ModelDiscoveryError(msg) from exc
4214
+
4215
+ if response.status_code == 401: # noqa: PLR2004 -- HTTP status codes are self-documenting
4216
+ msg = "Authentication failed (HTTP 401). Check the API key."
4217
+ raise ModelDiscoveryError(msg)
4218
+ if response.status_code == 403: # noqa: PLR2004
4219
+ msg = "Forbidden (HTTP 403). The API key may lack permission."
4220
+ raise ModelDiscoveryError(msg)
4221
+ if response.status_code == 404: # noqa: PLR2004
4222
+ msg = f"{url} returned 404. This provider may not expose /models."
4223
+ raise ModelDiscoveryError(msg)
4224
+ if response.status_code >= 400: # noqa: PLR2004
4225
+ msg = f"HTTP {response.status_code} from {url}: {response.text[:200]}"
4226
+ raise ModelDiscoveryError(msg)
4227
+
4228
+ try:
4229
+ payload = response.json()
4230
+ except ValueError as exc:
4231
+ msg = f"{url} did not return JSON: {exc}"
4232
+ raise ModelDiscoveryError(msg) from exc
4233
+
4234
+ # OpenAI shape: {"object": "list", "data": [{"id": ..., ...}, ...]}
4235
+ # Some providers just return a bare list.
4236
+ if isinstance(payload, dict):
4237
+ data = payload.get("data", payload.get("models", []))
4238
+ else:
4239
+ data = payload
4240
+
4241
+ if not isinstance(data, list):
4242
+ msg = "Unexpected /models response shape (expected list under 'data')."
4243
+ raise ModelDiscoveryError(msg)
4244
+
4245
+ ids: set[str] = set()
4246
+ for entry in data:
4247
+ if isinstance(entry, str):
4248
+ ids.add(entry)
4249
+ continue
4250
+ if not isinstance(entry, dict):
4251
+ continue
4252
+ model_id = entry.get("id") or entry.get("name") or entry.get("model")
4253
+ if not isinstance(model_id, str) or not model_id:
4254
+ continue
4255
+ # Volcengine Ark surfaces lifecycle in `status`; hide retired entries.
4256
+ status = entry.get("status")
4257
+ if isinstance(status, str):
4258
+ if status == "Shutdown":
4259
+ continue
4260
+ if not include_retiring and status == "Retiring":
4261
+ continue
4262
+ ids.add(model_id)
4263
+
4264
+ return sorted(ids)
@@ -1449,8 +1449,18 @@ class ChatTextArea(TextArea):
1449
1449
  if image is not None:
1450
1450
  event.prevent_default()
1451
1451
  event.stop()
1452
- # Add image to media tracker to get numbered placeholder
1453
- placeholder = self._chat_input_owner._image_tracker.add_image(image)
1452
+ # Bind a timestamped placeholder before archiving so the local
1453
+ # filename matches the token the user sees (e.g. img_YYYYMMDDHHMMSS).
1454
+ existing_text = self.text
1455
+ placeholder = self._chat_input_owner._image_tracker.add_image(
1456
+ image, existing_text=existing_text
1457
+ )
1458
+ stem = placeholder.strip("[]")
1459
+ from deepagents_code.media_utils import save_pasted_media
1460
+
1461
+ await asyncio.to_thread(
1462
+ save_pasted_media, image.base64_data, stem, image.format
1463
+ )
1454
1464
  # Insert placeholder at cursor position
1455
1465
  self.insert(placeholder)
1456
1466
  return