kolega-code 0.7.0__tar.gz → 0.7.2__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 (296) hide show
  1. {kolega_code-0.7.0 → kolega_code-0.7.2}/.env.example +3 -0
  2. {kolega_code-0.7.0 → kolega_code-0.7.2}/PKG-INFO +9 -1
  3. {kolega_code-0.7.0 → kolega_code-0.7.2}/README.md +8 -0
  4. {kolega_code-0.7.0 → kolega_code-0.7.2}/RELEASING.md +6 -0
  5. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/astro.config.mjs +4 -0
  6. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/configuration/providers-and-models.mdx +1 -0
  7. kolega_code-0.7.2/docs/src/content/docs/configuration/sign-in-with-chatgpt.mdx +93 -0
  8. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/hooks.md +1 -0
  9. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/tui/slash-commands.md +6 -0
  10. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/__init__.py +1 -1
  11. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/__init__.py +1 -1
  12. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/baseagent.py +63 -34
  13. kolega_code-0.7.2/kolega_code/agent/compression.py +146 -0
  14. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/context.py +2 -0
  15. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/conversation.py +131 -20
  16. kolega_code-0.7.2/kolega_code/agent/prompt_templates/auxiliary/compression/summary.system.md +28 -0
  17. kolega_code-0.7.2/kolega_code/agent/prompt_templates/auxiliary/compression/summary.user.md.j2 +17 -0
  18. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompts.py +7 -3
  19. kolega_code-0.7.2/kolega_code/agent/tests/compaction_helpers.py +189 -0
  20. kolega_code-0.7.2/kolega_code/agent/tests/llm/test_chatgpt_live.py +101 -0
  21. kolega_code-0.7.2/kolega_code/agent/tests/llm/test_chatgpt_oauth_provider.py +365 -0
  22. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_live_providers.py +5 -1
  23. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_base_agent.py +75 -68
  24. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_commands.py +51 -5
  25. kolega_code-0.7.2/kolega_code/agent/tests/test_compaction_integration.py +66 -0
  26. kolega_code-0.7.2/kolega_code/agent/tests/test_compaction_persistence.py +43 -0
  27. kolega_code-0.7.2/kolega_code/agent/tests/test_compaction_subagent.py +26 -0
  28. kolega_code-0.7.2/kolega_code/agent/tests/test_compaction_tui_events.py +98 -0
  29. kolega_code-0.7.2/kolega_code/agent/tests/test_compress_command.py +41 -0
  30. kolega_code-0.7.2/kolega_code/agent/tests/test_compression.py +143 -0
  31. kolega_code-0.7.2/kolega_code/agent/tests/test_conversation_compaction.py +192 -0
  32. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_prompts.py +1 -1
  33. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_think_hard_streaming.py +1 -0
  34. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/search_backends/firecrawl.py +10 -1
  35. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/terminal_tool.py +1 -0
  36. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/think_hard_tool.py +2 -0
  37. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/web_fetch_tool.py +1 -0
  38. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/utils/commands.py +26 -6
  39. kolega_code-0.7.2/kolega_code/auth/__init__.py +17 -0
  40. kolega_code-0.7.2/kolega_code/auth/_jwt.py +53 -0
  41. kolega_code-0.7.2/kolega_code/auth/chatgpt_oauth.py +222 -0
  42. kolega_code-0.7.2/kolega_code/auth/constants.py +67 -0
  43. kolega_code-0.7.2/kolega_code/auth/tests/test_chatgpt_oauth.py +172 -0
  44. kolega_code-0.7.2/kolega_code/auth/tests/test_tokens.py +134 -0
  45. kolega_code-0.7.2/kolega_code/auth/tokens.py +170 -0
  46. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/app.py +317 -23
  47. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/config.py +88 -8
  48. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/main.py +3 -0
  49. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/messages.py +15 -0
  50. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/provider_registry.py +12 -2
  51. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/session_store.py +3 -0
  52. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/settings.py +41 -0
  53. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/slash_commands.py +2 -0
  54. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/tests/test_app.py +761 -0
  55. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/tests/test_cli_config.py +25 -0
  56. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/tests/test_session_store.py +27 -0
  57. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/tests/test_settings.py +70 -1
  58. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/config.py +45 -4
  59. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/events.py +20 -0
  60. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/hooks/events.py +1 -0
  61. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/llm/client.py +19 -0
  62. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/llm/instrumented_client.py +2 -1
  63. kolega_code-0.7.2/kolega_code/llm/providers/chatgpt_oauth.py +501 -0
  64. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/llm/specs.py +70 -4
  65. kolega_code-0.7.2/kolega_code/tools/tests/__init__.py +0 -0
  66. {kolega_code-0.7.0 → kolega_code-0.7.2}/pyproject.toml +1 -1
  67. {kolega_code-0.7.0 → kolega_code-0.7.2}/uv.lock +2 -2
  68. kolega_code-0.7.0/kolega_code/agent/compression.py +0 -81
  69. kolega_code-0.7.0/kolega_code/agent/prompt_templates/auxiliary/compression/summary.system.md +0 -59
  70. kolega_code-0.7.0/kolega_code/agent/prompt_templates/auxiliary/compression/summary.user.md.j2 +0 -18
  71. {kolega_code-0.7.0 → kolega_code-0.7.2}/.github/workflows/ci.yml +0 -0
  72. {kolega_code-0.7.0 → kolega_code-0.7.2}/.github/workflows/docs.yml +0 -0
  73. {kolega_code-0.7.0 → kolega_code-0.7.2}/.github/workflows/release.yml +0 -0
  74. {kolega_code-0.7.0 → kolega_code-0.7.2}/.gitignore +0 -0
  75. {kolega_code-0.7.0 → kolega_code-0.7.2}/CONTRIBUTING.md +0 -0
  76. {kolega_code-0.7.0 → kolega_code-0.7.2}/LICENSE +0 -0
  77. {kolega_code-0.7.0 → kolega_code-0.7.2}/SECURITY.md +0 -0
  78. {kolega_code-0.7.0 → kolega_code-0.7.2}/conftest.py +0 -0
  79. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/.gitignore +0 -0
  80. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/README.md +0 -0
  81. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/package-lock.json +0 -0
  82. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/package.json +0 -0
  83. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/public/favicon.svg +0 -0
  84. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/assets/demo.gif +0 -0
  85. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/assets/kolega-dark.svg +0 -0
  86. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/assets/kolega-light.svg +0 -0
  87. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/cli/ask.md +0 -0
  88. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/cli/doctor.md +0 -0
  89. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/cli/overview.md +0 -0
  90. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/cli/sessions.md +0 -0
  91. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/concepts/agents.md +0 -0
  92. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/concepts/how-it-works.md +0 -0
  93. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/concepts/tools.md +0 -0
  94. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/configuration/environment-variables.md +0 -0
  95. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/configuration/settings-and-api-keys.mdx +0 -0
  96. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/getting-started/installation.mdx +0 -0
  97. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/getting-started/quick-start.mdx +0 -0
  98. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/gigacode.mdx +0 -0
  99. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/index.mdx +0 -0
  100. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/skills.mdx +0 -0
  101. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/tui/composer.md +0 -0
  102. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/tui/interface.md +0 -0
  103. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/tui/modes.md +0 -0
  104. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content/docs/tui/sessions-and-resume.md +0 -0
  105. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/content.config.ts +0 -0
  106. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/src/styles/brand.css +0 -0
  107. {kolega_code-0.7.0 → kolega_code-0.7.2}/docs/tsconfig.json +0 -0
  108. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/browseragent.py +0 -0
  109. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/coder.py +0 -0
  110. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/common.py +0 -0
  111. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/generalagent.py +0 -0
  112. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/investigationagent.py +0 -0
  113. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/orchestration/__init__.py +0 -0
  114. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/orchestration/budget.py +0 -0
  115. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/orchestration/errors.py +0 -0
  116. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/orchestration/executor.py +0 -0
  117. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/orchestration/guide.py +0 -0
  118. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/orchestration/journal.py +0 -0
  119. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/orchestration/runtime.py +0 -0
  120. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/orchestration/tests/__init__.py +0 -0
  121. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/orchestration/tests/test_orchestration.py +0 -0
  122. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/orchestration/types.py +0 -0
  123. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/planningagent.py +0 -0
  124. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_provider.py +0 -0
  125. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/auxiliary/terminal/output_summary.system.md +0 -0
  126. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/auxiliary/terminal/safety.system.md +0 -0
  127. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/auxiliary/tools/think_hard.system.md +0 -0
  128. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/extensions/cli/planning_questions.md +0 -0
  129. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/extensions/cli/shared_task_list.md +0 -0
  130. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/extensions/skills/catalog.md.j2 +0 -0
  131. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/system/agents/browser.md.j2 +0 -0
  132. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/system/agents/coder_cli.md.j2 +0 -0
  133. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/system/agents/general.md.j2 +0 -0
  134. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/system/agents/investigation.md.j2 +0 -0
  135. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/system/agents/planning.md.j2 +0 -0
  136. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/system/includes/agents_md_instructions.md +0 -0
  137. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/system/includes/environment_variables/workspace_env_vars.md +0 -0
  138. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/system/includes/frontend_guidance.md +0 -0
  139. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/system/includes/starter_templates/expo.md +0 -0
  140. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/system/includes/starter_templates/html_website.md +0 -0
  141. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/system/includes/starter_templates/mern_stack.md +0 -0
  142. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/system/includes/starter_templates/react_vite_shadcdn.md +0 -0
  143. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/user_tasks/cli/implement_plan.md.j2 +0 -0
  144. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/prompt_templates/user_tasks/cli/init_agents.md.j2 +0 -0
  145. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/__init__.py +0 -0
  146. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/__init__.py +0 -0
  147. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_anthropic_token_counting.py +0 -0
  148. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_billing_openai_cache.py +0 -0
  149. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_client.py +0 -0
  150. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_dashscope_mapping.py +0 -0
  151. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_error_boundary.py +0 -0
  152. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_exceptions.py +0 -0
  153. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_google_thought_signature.py +0 -0
  154. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_google_tool_signature_live.py +0 -0
  155. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_instrumented_client.py +0 -0
  156. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_instrumented_client_integration.py +0 -0
  157. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_langfuse_normalization.py +0 -0
  158. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_model_specs.py +0 -0
  159. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_openai_cached_tokens.py +0 -0
  160. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_openai_cached_tokens_stream.py +0 -0
  161. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_openai_message_conversion.py +0 -0
  162. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_openai_token_counting.py +0 -0
  163. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_thinking_effort.py +0 -0
  164. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/llm/test_tool_execution_ids.py +0 -0
  165. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/services/__init__.py +0 -0
  166. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/services/test_browser.py +0 -0
  167. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/services/test_browser_parity.py +0 -0
  168. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/services/test_file_system.py +0 -0
  169. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/services/test_sandbox_terminal_input.py +0 -0
  170. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/services/test_terminal.py +0 -0
  171. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/services/test_terminal_buffer.py +0 -0
  172. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/services/test_terminal_state_serializer.py +0 -0
  173. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_agent_tools_inventory.py +0 -0
  174. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_coder_attachments.py +0 -0
  175. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_coder_prompt_extensions.py +0 -0
  176. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_duplicate_tool_results.py +0 -0
  177. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_empty_message_handling.py +0 -0
  178. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_event_emitter.py +0 -0
  179. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_general_agent.py +0 -0
  180. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_gigacode_gating.py +0 -0
  181. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_hooks_integration.py +0 -0
  182. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_html.py +0 -0
  183. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_parallel_tool_calls.py +0 -0
  184. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_permissions.py +0 -0
  185. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_planning_agent.py +0 -0
  186. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_prompt_provider.py +0 -0
  187. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_tool_registry.py +0 -0
  188. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/test_tools.py +0 -0
  189. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/__init__.py +0 -0
  190. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_agent_tool.py +0 -0
  191. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_base_tool.py +0 -0
  192. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_browser_tool.py +0 -0
  193. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_build_tool.py +0 -0
  194. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_create_file_tool.py +0 -0
  195. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_edit_preview.py +0 -0
  196. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_glob_tool.py +0 -0
  197. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_glob_tool_sandbox_parity.py +0 -0
  198. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_list_directory_tool.py +0 -0
  199. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_read_file_tool.py +0 -0
  200. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_replace_entire_file_tool.py +0 -0
  201. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_replace_lines_tool.py +0 -0
  202. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_search_and_replace_tool.py +0 -0
  203. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_search_codebase_tool.py +0 -0
  204. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_terminal_tool.py +0 -0
  205. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_think_hard_integration.py +0 -0
  206. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_web_fetch_tool.py +0 -0
  207. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_web_search_tool.py +0 -0
  208. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tests/tool_backend/test_workflow_tool.py +0 -0
  209. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/agent_tool.py +0 -0
  210. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/base_tool.py +0 -0
  211. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/browser_tool.py +0 -0
  212. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/build_tool.py +0 -0
  213. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/create_file_tool.py +0 -0
  214. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/edit_preview.py +0 -0
  215. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/glob_tool.py +0 -0
  216. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/list_directory_tool.py +0 -0
  217. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/memory_tool.py +0 -0
  218. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/read_file_tool.py +0 -0
  219. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/replace_entire_file_tool.py +0 -0
  220. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/replace_lines_tool.py +0 -0
  221. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/search_and_replace_tool.py +0 -0
  222. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/search_backends/__init__.py +0 -0
  223. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/search_backends/base.py +0 -0
  224. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/search_backends/duckduckgo.py +0 -0
  225. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/search_backends/errors.py +0 -0
  226. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/search_backends/models.py +0 -0
  227. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/search_backends/searxng.py +0 -0
  228. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/search_backends/tavily.py +0 -0
  229. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/search_codebase_tool.py +0 -0
  230. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/streaming_tool.py +0 -0
  231. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/web_search_tool.py +0 -0
  232. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tool_backend/workflow_tool.py +0 -0
  233. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/agent/tools.py +0 -0
  234. {kolega_code-0.7.0/kolega_code/hooks → kolega_code-0.7.2/kolega_code/auth}/tests/__init__.py +0 -0
  235. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/__init__.py +0 -0
  236. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/connection.py +0 -0
  237. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/file_index.py +0 -0
  238. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/mentions.py +0 -0
  239. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/skills.py +0 -0
  240. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/tests/__init__.py +0 -0
  241. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/tests/test_connection.py +0 -0
  242. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/tests/test_file_index.py +0 -0
  243. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/tests/test_main.py +0 -0
  244. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/tests/test_mentions.py +0 -0
  245. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/tests/test_skills.py +0 -0
  246. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/tests/test_slash_commands.py +0 -0
  247. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/tests/test_themes.py +0 -0
  248. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/tests/test_updater.py +0 -0
  249. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/theme.py +0 -0
  250. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/cli/updater.py +0 -0
  251. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/hooks/__init__.py +0 -0
  252. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/hooks/backends.py +0 -0
  253. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/hooks/config.py +0 -0
  254. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/hooks/dispatcher.py +0 -0
  255. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/hooks/matcher.py +0 -0
  256. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/hooks/outcome.py +0 -0
  257. {kolega_code-0.7.0/kolega_code/llm/providers → kolega_code-0.7.2/kolega_code/hooks/tests}/__init__.py +0 -0
  258. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/hooks/tests/test_hooks.py +0 -0
  259. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/llm/exceptions.py +0 -0
  260. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/llm/models.py +0 -0
  261. {kolega_code-0.7.0/kolega_code/tools/tests → kolega_code-0.7.2/kolega_code/llm/providers}/__init__.py +0 -0
  262. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/llm/providers/anthropic.py +0 -0
  263. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/llm/providers/base.py +0 -0
  264. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/llm/providers/google.py +0 -0
  265. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/llm/providers/models.py +0 -0
  266. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/llm/providers/openai.py +0 -0
  267. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/llm/ratelimit.py +0 -0
  268. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/llm/tool_execution_ids.py +0 -0
  269. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/models/__init__.py +0 -0
  270. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/models/sandbox_terminal_state.py +0 -0
  271. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/permissions.py +0 -0
  272. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/runtime.py +0 -0
  273. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/sandbox/README.md +0 -0
  274. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/sandbox/__init__.py +0 -0
  275. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/sandbox/async_filesystem.py +0 -0
  276. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/sandbox/base.py +0 -0
  277. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/sandbox/browser.py +0 -0
  278. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/sandbox/event_loop.py +0 -0
  279. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/sandbox/filesystem.py +0 -0
  280. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/sandbox/local.py +0 -0
  281. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/sandbox/serializer.py +0 -0
  282. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/sandbox/terminal.py +0 -0
  283. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/sandbox/utils.py +0 -0
  284. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/services/base.py +0 -0
  285. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/services/browser.py +0 -0
  286. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/services/file_system.py +0 -0
  287. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/services/html.py +0 -0
  288. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/services/terminal.py +0 -0
  289. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/services/terminal_buffer.py +0 -0
  290. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/tools/__init__.py +0 -0
  291. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/tools/core.py +0 -0
  292. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/tools/definitions.py +0 -0
  293. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/tools/registry.py +0 -0
  294. {kolega_code-0.7.0 → kolega_code-0.7.2}/kolega_code/tools/tests/test_definitions.py +0 -0
  295. {kolega_code-0.7.0 → kolega_code-0.7.2}/run_tests.sh +0 -0
  296. {kolega_code-0.7.0 → kolega_code-0.7.2}/scripts/install-kolega-code.sh +0 -0
@@ -3,6 +3,9 @@
3
3
 
4
4
  ANTHROPIC_API_KEY=
5
5
  OPENAI_API_KEY=
6
+ # Prefer a ChatGPT subscription over an OpenAI API key? Run `/login` in the TUI
7
+ # (no key needed). Optional override of the OAuth client id, for testing only:
8
+ # KOLEGA_CODE_CHATGPT_CLIENT_ID=
6
9
  GOOGLE_API_KEY=
7
10
  MOONSHOT_API_KEY=
8
11
  DEEPSEEK_API_KEY=
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kolega-code
3
- Version: 0.7.0
3
+ Version: 0.7.2
4
4
  Summary: Local-first AI coding agent for the terminal
5
5
  Project-URL: Homepage, https://kolega-ai.github.io/kolega-code/
6
6
  Project-URL: Documentation, https://kolega-ai.github.io/kolega-code/
@@ -176,6 +176,14 @@ and DeepSeek — and lets you assign **different models to different roles**: a
176
176
  long-context model for coding, a fast cheap model for small utility calls, and one for extended
177
177
  "thinking". See [Providers & Models](https://kolega-ai.github.io/kolega-code/configuration/providers-and-models/).
178
178
 
179
+ ## Sign in with ChatGPT
180
+
181
+ If you have a paid **ChatGPT** plan (Plus, Pro, or Business), you can use it to run OpenAI models
182
+ instead of a separate API key. Run `/login chatgpt` in the TUI, complete the browser sign-in, and
183
+ Kolega Code switches to the **OpenAI (ChatGPT subscription)** provider (default `gpt-5.5`). Tokens are
184
+ stored locally (chmod `600`) and refreshed automatically; `/logout chatgpt` removes them. See
185
+ [Sign in with ChatGPT](https://kolega-ai.github.io/kolega-code/configuration/sign-in-with-chatgpt/).
186
+
179
187
  ## Configuration
180
188
 
181
189
  Set your provider, model, and API keys from the **Settings** tab in the UI, or via environment
@@ -103,6 +103,14 @@ and DeepSeek — and lets you assign **different models to different roles**: a
103
103
  long-context model for coding, a fast cheap model for small utility calls, and one for extended
104
104
  "thinking". See [Providers & Models](https://kolega-ai.github.io/kolega-code/configuration/providers-and-models/).
105
105
 
106
+ ## Sign in with ChatGPT
107
+
108
+ If you have a paid **ChatGPT** plan (Plus, Pro, or Business), you can use it to run OpenAI models
109
+ instead of a separate API key. Run `/login chatgpt` in the TUI, complete the browser sign-in, and
110
+ Kolega Code switches to the **OpenAI (ChatGPT subscription)** provider (default `gpt-5.5`). Tokens are
111
+ stored locally (chmod `600`) and refreshed automatically; `/logout chatgpt` removes them. See
112
+ [Sign in with ChatGPT](https://kolega-ai.github.io/kolega-code/configuration/sign-in-with-chatgpt/).
113
+
106
114
  ## Configuration
107
115
 
108
116
  Set your provider, model, and API keys from the **Settings** tab in the UI, or via environment
@@ -20,6 +20,12 @@ release branch.
20
20
  2. Update `pyproject.toml`, `uv.lock`, and package `__version__` values to the
21
21
  release version.
22
22
 
23
+ Running `uv lock` advances the `exclude-newer` cutoff recorded in `uv.lock`,
24
+ because `pyproject.toml` sets `exclude-newer = "1 week"` as a relative
25
+ duration. The cutoff timestamp moving forward, and any dependency versions
26
+ released within the new one-week window being pulled in, is expected and
27
+ acceptable. Do not revert these changes.
28
+
23
29
  3. Run the fast test suite:
24
30
 
25
31
  ```bash
@@ -56,6 +56,10 @@ export default defineConfig({
56
56
  label: "Providers & Models",
57
57
  slug: "configuration/providers-and-models",
58
58
  },
59
+ {
60
+ label: "Sign in with ChatGPT",
61
+ slug: "configuration/sign-in-with-chatgpt",
62
+ },
59
63
  {
60
64
  label: "Settings & API Keys",
61
65
  slug: "configuration/settings-and-api-keys",
@@ -16,6 +16,7 @@ The agent runtime recognizes these providers:
16
16
  | --- | --- | --- |
17
17
  | Anthropic | `anthropic` | `ANTHROPIC_API_KEY` |
18
18
  | OpenAI | `openai` | `OPENAI_API_KEY` |
19
+ | OpenAI (ChatGPT subscription) | `openai_chatgpt` | _none — [sign in with ChatGPT](../sign-in-with-chatgpt/)_ |
19
20
  | Google | `google` | `GOOGLE_API_KEY` |
20
21
  | Groq | `groq` | `GROQ_API_KEY` |
21
22
  | Together.ai | `together` | `TOGETHER_API_KEY` |
@@ -0,0 +1,93 @@
1
+ ---
2
+ title: Sign in with ChatGPT
3
+ description: Use your ChatGPT subscription to run OpenAI models in Kolega Code, without an API key.
4
+ ---
5
+
6
+ import { Aside, Steps } from '@astrojs/starlight/components';
7
+
8
+ If you have a paid **ChatGPT** plan, you can use it to run OpenAI models in Kolega
9
+ Code — no separate `OPENAI_API_KEY` required. Sign in once with `/login chatgpt`
10
+ and Kolega Code uses your subscription for the **OpenAI (ChatGPT subscription)**
11
+ provider.
12
+
13
+ ## Eligibility
14
+
15
+ | Plan | Runs models |
16
+ | --- | --- |
17
+ | Plus | Yes |
18
+ | Pro | Yes |
19
+ | Business | Yes |
20
+ | Free | No |
21
+
22
+ A Free plan can sign in, but can't run models this way — use an
23
+ [`OPENAI_API_KEY`](../providers-and-models/) instead.
24
+
25
+ ## Signing in
26
+
27
+ <Steps>
28
+
29
+ 1. In the TUI composer, run:
30
+
31
+ ```text
32
+ /login chatgpt
33
+ ```
34
+
35
+ 2. Your browser opens to the ChatGPT sign-in page. Complete the sign-in there.
36
+ (If the browser doesn't open, the composer prints a URL you can paste in
37
+ yourself.)
38
+
39
+ 3. When sign-in finishes, the browser shows a confirmation page and Kolega Code
40
+ switches to the **OpenAI (ChatGPT subscription)** provider, ready to use.
41
+
42
+ </Steps>
43
+
44
+ ## Models
45
+
46
+ The ChatGPT-subscription provider (identifier `openai_chatgpt`) exposes the Codex
47
+ model line-up:
48
+
49
+ | Model | Label |
50
+ | --- | --- |
51
+ | `gpt-5.5` | GPT-5.5 — default, frontier coding and reasoning |
52
+ | `gpt-5.4` | GPT-5.4 — strong everyday coding |
53
+ | `gpt-5.4-mini` | GPT-5.4 Mini — fast and economical |
54
+ | `gpt-5.3-codex-spark` | GPT-5.3 Codex Spark — ultra-fast |
55
+
56
+ Switch between them with [`/model`](../../tui/slash-commands/) or from the
57
+ **Settings** tab. Each model supports a reasoning **thinking effort** you can set
58
+ with [`/effort`](../../tui/slash-commands/).
59
+
60
+ ## Where credentials live
61
+
62
+ Sign-in tokens are stored in your `settings.json` under `oauth_tokens`, in the
63
+ same [state directory](../settings-and-api-keys/#where-settings-live) as the rest
64
+ of your settings, with `0600` permissions (readable and writable only by your
65
+ user). They refresh automatically in the background, so a single sign-in keeps
66
+ working across sessions.
67
+
68
+ ## Checking status
69
+
70
+ `doctor` reports whether you're signed in, without exposing any token:
71
+
72
+ ```bash
73
+ kolega-code doctor --project .
74
+ ```
75
+
76
+ For the ChatGPT-subscription provider it shows `signed in as <email> (<plan>)` or
77
+ `not signed in`. See [`doctor`](../../cli/doctor/).
78
+
79
+ ## Signing out
80
+
81
+ To remove your stored ChatGPT credentials:
82
+
83
+ ```text
84
+ /logout chatgpt
85
+ ```
86
+
87
+ After signing out, switch to another provider — or sign in again with
88
+ `/login chatgpt` — to keep working.
89
+
90
+ <Aside type="tip">
91
+ `/login` and `/logout` take a provider name so more sign-in options can be added
92
+ over time. Today, `chatgpt` is the available target.
93
+ </Aside>
@@ -63,6 +63,7 @@ Both files use the same shape:
63
63
  | `PreToolUse` | Before a tool runs (after the permission gate) | Denies the tool; the reason is returned to the agent as a tool error, so it can adjust |
64
64
  | `PostToolUse` | After a tool succeeds (and on failure) | Ends the turn; the reason is shown as a warning |
65
65
  | `PreCompact` | Before the conversation is compacted | Advisory |
66
+ | `PostCompact` | After the conversation is compacted | Advisory; payload reports before/after token counts |
66
67
  | `Stop` | The agent is about to finish its turn | Keeps the agent working; the reason becomes its next instruction |
67
68
  | `SubagentStop` | A dispatched sub-agent finished | Advisory; can annotate the result the parent sees |
68
69
  | `Notification` | A permission prompt is about to be shown | Advisory (desktop notifications, sounds) |
@@ -40,6 +40,8 @@ These control the app and your session.
40
40
  | `/permissions` | Show or switch the shell/edit permission mode |
41
41
  | `/model` | Choose the active model |
42
42
  | `/effort` | Choose the active model's thinking effort |
43
+ | `/login` | Sign in to a provider, e.g. `/login chatgpt` |
44
+ | `/logout` | Sign out of a provider, e.g. `/logout chatgpt` |
43
45
  | `/gigacode` | Toggle [gigacode](../../gigacode/) workflow orchestration on or off |
44
46
  | `/copy` | Copy the last response to the clipboard |
45
47
  | `/version` | Show the Kolega Code version |
@@ -53,6 +55,10 @@ provider. You can also switch directly with `/model <name>`.
53
55
  Run `/effort` to open a selectable list of supported effort values for the
54
56
  active model. You can also switch directly with `/effort <level>`.
55
57
 
58
+ Run `/login chatgpt` to sign in with a ChatGPT subscription and use OpenAI models
59
+ without an API key; `/logout chatgpt` removes the stored credentials. See
60
+ [Sign in with ChatGPT](../../configuration/sign-in-with-chatgpt/).
61
+
56
62
  Run `/init` to have the agent inspect the repository and create or update a
57
63
  concise root `AGENTS.md`. Extra text after the command is passed as focus or
58
64
  constraints:
@@ -148,4 +148,4 @@ __all__ = [
148
148
  "parse_git_status_output",
149
149
  ]
150
150
 
151
- __version__ = "0.7.0"
151
+ __version__ = "0.7.2"
@@ -39,4 +39,4 @@ __all__ = [
39
39
  "ToolExtension",
40
40
  ]
41
41
 
42
- __version__ = "0.7.0"
42
+ __version__ = "0.7.2"
@@ -11,7 +11,7 @@ from pathlib import Path
11
11
  from typing import Any, AsyncGenerator, Dict, List, Optional
12
12
 
13
13
  from .common import LogMixin
14
- from .compression import HistoryCompressor
14
+ from .compression import CompactionResult, HistoryCompressor
15
15
  from kolega_code.config import AgentConfig, ModelProvider
16
16
  from kolega_code.events import AgentConnectionManager
17
17
  from .context import AgentContext, AgentServices, Telemetry, WorkspaceInfo
@@ -398,6 +398,14 @@ class BaseAgent(LogMixin):
398
398
  """Restores the message history from a list of dictionaries using custom methods."""
399
399
  self.conversation.restore(serialized_history)
400
400
 
401
+ def dump_compaction_state(self) -> Dict[str, Any]:
402
+ """Serialize the compaction boundary (summary + how many leading messages it folds)."""
403
+ return self.conversation.dump_compaction()
404
+
405
+ def restore_compaction_state(self, data: Optional[Dict[str, Any]]) -> None:
406
+ """Restore the compaction boundary; must be called after restore_message_history."""
407
+ self.conversation.restore_compaction(data)
408
+
401
409
  def _sanitize_oversized_tool_results(self) -> int:
402
410
  return self.conversation.sanitize_oversized_tool_results()
403
411
 
@@ -549,9 +557,12 @@ class BaseAgent(LogMixin):
549
557
  message=message,
550
558
  )
551
559
 
552
- async def compress_history(self) -> None:
560
+ async def compress_history(self) -> CompactionResult:
553
561
  """
554
- Non-destructively summarize the current history and mark a compression boundary.
562
+ Non-destructively summarize the current history, keeping recent turns
563
+ verbatim. Emits a compaction_status event around the work so the UI can
564
+ show progress, then recounts + emits a context_update so the gauge
565
+ refreshes. Returns the structured outcome.
555
566
  """
556
567
 
557
568
  async def on_info(message: str) -> None:
@@ -560,16 +571,34 @@ class BaseAgent(LogMixin):
560
571
  async def on_error(message: str) -> None:
561
572
  await self.log_error(message, sender=self.agent_name)
562
573
 
563
- await self.compressor.summarize(
564
- self.conversation,
565
- llm=self.llm,
566
- model=self.primary_model_config.model,
567
- max_completion_tokens=self.model_completion_tokens,
568
- temperature=self.model_default_temperature,
569
- thinking=self.primary_model_config.thinking_effort,
570
- on_info=on_info,
571
- on_error=on_error,
574
+ await self.emitter.compaction_status("started", "Compacting conversation…")
575
+ try:
576
+ result = await self.compressor.summarize(
577
+ self.conversation,
578
+ llm=self.llm,
579
+ model=self.primary_model_config.model,
580
+ temperature=self.model_default_temperature,
581
+ # No extended thinking: a bounded summary doesn't need it, and the
582
+ # thinking budget could otherwise exceed the small summary max_tokens.
583
+ thinking=None,
584
+ on_info=on_info,
585
+ on_error=on_error,
586
+ )
587
+ finally:
588
+ # Recount + emit so the context gauge reflects post-compaction reality
589
+ # (even on a no-op the UI may have been stale).
590
+ await self.count_current_context()
591
+
592
+ phase = "finished" if result.ok else "error"
593
+ summary_text = (
594
+ self.conversation.summary.get_text_content() if result.ok and self.conversation.summary else ""
572
595
  )
596
+ await self.emitter.compaction_status(phase, result.message, summary=summary_text)
597
+ return result
598
+
599
+ def clear_history(self) -> None:
600
+ """Drop all history and reset compaction state."""
601
+ self.conversation.clear()
573
602
 
574
603
  # ------------------------------------------------------------------
575
604
  # Tool execution
@@ -936,6 +965,7 @@ class BaseAgent(LogMixin):
936
965
  max_retries=model_config.rate_limits.max_retries,
937
966
  requests_per_minute=model_config.rate_limits.requests_per_minute,
938
967
  tokens_per_minute=model_config.rate_limits.tokens_per_minute,
968
+ token_manager=self.config.get_chatgpt_token_manager(),
939
969
  )
940
970
  response = await client.generate(
941
971
  model=model_config.model,
@@ -1135,9 +1165,8 @@ class BaseAgent(LogMixin):
1135
1165
  #
1136
1166
  # process_message_stream is the single canonical loop shared by every
1137
1167
  # agent. Subclasses customize behavior through the hook methods below
1138
- # (build_user_content, apply_compression_fallback, on_tool_use_start,
1139
- # should_stop_after_tools, recap_agent_outcome) rather than overriding
1140
- # the loop itself.
1168
+ # (build_user_content, on_tool_use_start, should_stop_after_tools,
1169
+ # recap_agent_outcome) rather than overriding the loop itself.
1141
1170
  # ------------------------------------------------------------------
1142
1171
 
1143
1172
  completion_log_message = "Processing complete"
@@ -1165,21 +1194,6 @@ class BaseAgent(LogMixin):
1165
1194
 
1166
1195
  return content_blocks
1167
1196
 
1168
- def apply_compression_fallback(self) -> None:
1169
- """
1170
- Hard-truncate history when compression alone could not get under budget.
1171
-
1172
- Default: keep the first message (the original task) plus any protected
1173
- skill-content messages.
1174
- """
1175
- first_message = self.history[0]
1176
- protected = [
1177
- message
1178
- for message in self.history
1179
- if message is not first_message and self._is_protected_skill_content(message)
1180
- ]
1181
- self.history = MessageHistory(protected + [first_message])
1182
-
1183
1197
  async def on_tool_use_start(self, tool_call_delta: Dict[str, Any]) -> None:
1184
1198
  """
1185
1199
  Called when the provider streams a tool_use_start event (Anthropic only).
@@ -1251,20 +1265,35 @@ class BaseAgent(LogMixin):
1251
1265
  logger.debug("Input token count: %s", token_count)
1252
1266
 
1253
1267
  if self.compressor.over_budget(token_count.input_tokens, self.model_context_length):
1268
+ before_tokens = token_count.input_tokens
1254
1269
  # PreCompact hooks (advisory): observe before history is compacted.
1255
1270
  await self.fire_hook(
1256
1271
  HookEvent.PRE_COMPACT,
1257
1272
  {
1258
1273
  "trigger": "auto",
1259
- "input_tokens": token_count.input_tokens,
1274
+ "input_tokens": before_tokens,
1260
1275
  "model_context_length": self.model_context_length,
1261
1276
  },
1262
1277
  )
1263
- await self.compress_history()
1278
+ result = await self.compress_history()
1264
1279
  token_count = await self.count_current_context()
1265
1280
 
1266
- if self.compressor.over_budget(token_count.input_tokens, self.model_context_length):
1267
- self.apply_compression_fallback()
1281
+ # PostCompact hooks (advisory): observe the outcome. There is no
1282
+ # destructive fallback — a bounded summary plus capped tool results
1283
+ # and a small verbatim tail keep us under budget; if somehow not, we
1284
+ # send as-is rather than wipe history.
1285
+ await self.fire_hook(
1286
+ HookEvent.POST_COMPACT,
1287
+ {
1288
+ "trigger": "auto",
1289
+ "ok": result.ok,
1290
+ "reason": result.reason,
1291
+ "summarized_messages": result.summarized_messages,
1292
+ "input_tokens_before": before_tokens,
1293
+ "input_tokens_after": token_count.input_tokens,
1294
+ "model_context_length": self.model_context_length,
1295
+ },
1296
+ )
1268
1297
 
1269
1298
  self.mark_cache_checkpoint()
1270
1299
 
@@ -0,0 +1,146 @@
1
+ """History compression: summarize a conversation when it outgrows the context window."""
2
+
3
+ import logging
4
+ from dataclasses import dataclass
5
+ from typing import Awaitable, Callable, Optional
6
+
7
+ from .conversation import Conversation
8
+ from kolega_code.llm.models import Message, MessageHistory, TextBlock
9
+ from .prompts import (
10
+ build_compression_summary_user_prompt,
11
+ COMPRESSION_SUMMARY_SYSTEM_PROMPT,
12
+ )
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ LogCallback = Callable[[str], Awaitable[None]]
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class CompactionResult:
21
+ """Outcome of a compaction attempt, surfaced to callers and the UI.
22
+
23
+ ``reason`` is a machine tag: "ok" | "too_few" | "nothing_to_summarize" | "llm_error".
24
+ ``message`` is a human-readable line for the command output / logs.
25
+ """
26
+
27
+ ok: bool
28
+ reason: str
29
+ summarized_messages: int = 0
30
+ message: str = ""
31
+
32
+
33
+ class HistoryCompressor:
34
+ """Summarizes a conversation non-destructively when it crosses the budget threshold."""
35
+
36
+ MIN_MESSAGES_TO_COMPRESS = 5
37
+ # How many of the most recent messages to keep verbatim after the summary.
38
+ KEEP_RECENT_MESSAGES = 6
39
+ # Don't bother summarizing a trivially short prefix.
40
+ MIN_PREFIX_TO_SUMMARIZE = 3
41
+ # Cap the summary length: the prompt targets ~600 words (~900 tokens), so a
42
+ # small ceiling keeps it tight and avoids the model's full completion budget.
43
+ SUMMARY_MAX_TOKENS = 2048
44
+
45
+ def __init__(self, threshold: float = 0.8) -> None:
46
+ # Fraction of the model context window above which compression kicks in
47
+ self.threshold = threshold
48
+
49
+ def over_budget(self, input_tokens: int, model_context_length: int) -> bool:
50
+ return input_tokens > model_context_length * self.threshold
51
+
52
+ async def summarize(
53
+ self,
54
+ conversation: Conversation,
55
+ *,
56
+ llm,
57
+ model: str,
58
+ temperature: float,
59
+ thinking,
60
+ on_info: Optional[LogCallback] = None,
61
+ on_error: Optional[LogCallback] = None,
62
+ ) -> CompactionResult:
63
+ """
64
+ Non-destructively summarize the aged-out prefix while keeping the most
65
+ recent turns verbatim, and move the compaction boundary.
66
+
67
+ Incremental: only the messages that have aged out since the previous
68
+ boundary are summarized, with the prior summary folded in for continuity.
69
+ Returns a CompactionResult describing what happened — never a silent no-op.
70
+ """
71
+ history = conversation.history
72
+ if not history or len(history) < self.MIN_MESSAGES_TO_COMPRESS:
73
+ return CompactionResult(
74
+ ok=False,
75
+ reason="too_few",
76
+ message=(
77
+ f"Nothing to compress yet ({len(history)} message(s); "
78
+ f"need at least {self.MIN_MESSAGES_TO_COMPRESS})."
79
+ ),
80
+ )
81
+
82
+ split = conversation.compaction_split_point(
83
+ keep_recent=self.KEEP_RECENT_MESSAGES, min_prefix=self.MIN_PREFIX_TO_SUMMARIZE
84
+ )
85
+ prior_through = conversation.compacted_through if conversation.summary is not None else 0
86
+ if split is None or split <= prior_through:
87
+ return CompactionResult(
88
+ ok=False,
89
+ reason="nothing_to_summarize",
90
+ message="Already compact — no older messages to summarize.",
91
+ )
92
+
93
+ if on_info:
94
+ await on_info("Compressing message history...")
95
+
96
+ try:
97
+ prefix = MessageHistory(list(history[prior_through:split]))
98
+ prefix_markdown = prefix.get_markdown_conversation()
99
+ previous_summary = conversation.summary.get_text_content() if conversation.summary is not None else None
100
+ user_prompt_filled = build_compression_summary_user_prompt(
101
+ prefix_markdown, previous_summary=previous_summary
102
+ )
103
+
104
+ messages = MessageHistory([Message(role="user", content=[TextBlock(text=user_prompt_filled)])])
105
+ system_message = Message(role="system", content=[TextBlock(text=COMPRESSION_SUMMARY_SYSTEM_PROMPT)])
106
+
107
+ # Stream and drain rather than calling generate(): the Anthropic SDK
108
+ # rejects non-streaming requests whose max_tokens is large enough to risk
109
+ # a >10-minute response, which the model's full completion budget triggers.
110
+ async with await llm.stream(
111
+ messages=messages,
112
+ system=system_message,
113
+ temperature=temperature,
114
+ model=model,
115
+ max_completion_tokens=self.SUMMARY_MAX_TOKENS,
116
+ thinking=thinking,
117
+ ) as stream:
118
+ async for _event in stream:
119
+ pass
120
+ response = await stream.get_final_message()
121
+
122
+ summary_text = response.get_text_content()
123
+ if not summary_text or not summary_text.strip():
124
+ msg = "Compression produced an empty summary; history left unchanged."
125
+ if on_error:
126
+ await on_error(msg)
127
+ else:
128
+ logger.error(msg)
129
+ return CompactionResult(ok=False, reason="llm_error", message=msg)
130
+
131
+ folded = split - prior_through
132
+ conversation.apply_compaction(summary_text, split)
133
+
134
+ done = f"Compressed {folded} older message(s) into a summary; kept the latest turns verbatim."
135
+ if on_info:
136
+ await on_info(done)
137
+ return CompactionResult(ok=True, reason="ok", summarized_messages=folded, message=done)
138
+
139
+ except Exception as e:
140
+ # Never swallow into a fake success: log the full traceback and surface
141
+ # the real error to the caller.
142
+ logger.exception("Failed to compress message history")
143
+ msg = f"Failed to compress message history: {type(e).__name__}: {e}"
144
+ if on_error:
145
+ await on_error(msg)
146
+ return CompactionResult(ok=False, reason="llm_error", message=msg)
@@ -113,6 +113,7 @@ class AgentContext:
113
113
  user_id=self.telemetry.user_id,
114
114
  user_email=self.telemetry.user_email,
115
115
  usage_recorder=self.telemetry.usage_recorder,
116
+ token_manager=self.config.get_chatgpt_token_manager(),
116
117
  )
117
118
 
118
119
  return LLMClient(
@@ -121,4 +122,5 @@ class AgentContext:
121
122
  max_retries=model_config.rate_limits.max_retries,
122
123
  requests_per_minute=model_config.rate_limits.requests_per_minute,
123
124
  tokens_per_minute=model_config.rate_limits.tokens_per_minute,
125
+ token_manager=self.config.get_chatgpt_token_manager(),
124
126
  )