turnloop 0.1.10__tar.gz → 0.1.12__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 (162) hide show
  1. {turnloop-0.1.10 → turnloop-0.1.12}/PKG-INFO +12 -6
  2. {turnloop-0.1.10 → turnloop-0.1.12}/README.md +1244 -1238
  3. {turnloop-0.1.10 → turnloop-0.1.12}/pyproject.toml +1 -1
  4. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_config.py +42 -0
  5. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_providers.py +169 -0
  6. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_skills_install.py +208 -0
  7. turnloop-0.1.12/tests/test_subagent_extras.py +186 -0
  8. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_system_prompt_layout.py +9 -0
  9. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/__init__.py +1 -1
  10. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/agent/factory.py +172 -175
  11. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/agent/loop.py +14 -0
  12. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/agent/system_prompt.py +2 -1
  13. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/cli.py +17 -6
  14. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/config.py +17 -2
  15. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/diagnostics.py +5 -3
  16. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/providers/base.py +53 -3
  17. turnloop-0.1.12/turnloop/providers/registry.py +111 -0
  18. turnloop-0.1.10/turnloop/providers/registry.py +0 -75
  19. {turnloop-0.1.10 → turnloop-0.1.12}/.gitignore +0 -0
  20. {turnloop-0.1.10 → turnloop-0.1.12}/tests/conftest.py +0 -0
  21. {turnloop-0.1.10 → turnloop-0.1.12}/tests/fixtures/anthropic_tool_call.sse +0 -0
  22. {turnloop-0.1.10 → turnloop-0.1.12}/tests/fixtures/github_chrome.html +0 -0
  23. {turnloop-0.1.10 → turnloop-0.1.12}/tests/fixtures/github_page.html +0 -0
  24. {turnloop-0.1.10 → turnloop-0.1.12}/tests/fixtures/glm_tool_call.sse +0 -0
  25. {turnloop-0.1.10 → turnloop-0.1.12}/tests/fixtures/mcp_echo_server.py +0 -0
  26. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_bash.py +0 -0
  27. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_compaction.py +0 -0
  28. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_config_screen.py +0 -0
  29. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_configio.py +0 -0
  30. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_experiments.py +0 -0
  31. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_hooks_mcp_commands.py +0 -0
  32. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_loop.py +0 -0
  33. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_permissions.py +0 -0
  34. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_tools_files.py +0 -0
  35. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_tui.py +0 -0
  36. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_webfetch.py +0 -0
  37. {turnloop-0.1.10 → turnloop-0.1.12}/tests/test_websearch.py +0 -0
  38. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/__main__.py +0 -0
  39. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/agent/__init__.py +0 -0
  40. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/agent/headless.py +0 -0
  41. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/agent/subagent.py +0 -0
  42. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/commands/__init__.py +0 -0
  43. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/commands/dispatch.py +0 -0
  44. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/commands/loader.py +0 -0
  45. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/configio.py +0 -0
  46. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/context/__init__.py +0 -0
  47. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/context/budget.py +0 -0
  48. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/context/compaction.py +0 -0
  49. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/context/memory.py +0 -0
  50. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/core/__init__.py +0 -0
  51. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/core/events.py +0 -0
  52. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/core/ids.py +0 -0
  53. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/core/messages.py +0 -0
  54. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/core/tokens.py +0 -0
  55. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/errors.py +0 -0
  56. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/__init__.py +0 -0
  57. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/configs/bakeoff.yaml +0 -0
  58. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/configs/ceiling-check.yaml +0 -0
  59. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/configs/context-glm.yaml +0 -0
  60. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/configs/context.yaml +0 -0
  61. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/configs/glm-selfhosted.yaml +0 -0
  62. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/configs/groq-free.yaml +0 -0
  63. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/configs/hard-calibration.yaml +0 -0
  64. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/configs/hard-glm.yaml +0 -0
  65. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/configs/loop.yaml +0 -0
  66. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/configs/nvidia-smoke.yaml +0 -0
  67. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/configs/reliability.yaml +0 -0
  68. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/configs/rewrite-calibration.yaml +0 -0
  69. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/configs/smoke.yaml +0 -0
  70. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/graders.py +0 -0
  71. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/report.py +0 -0
  72. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/runner.py +0 -0
  73. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/default.yaml +0 -0
  74. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/bulky/module_1.py +0 -0
  75. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/bulky/module_2.py +0 -0
  76. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/bulky/module_3.py +0 -0
  77. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/bulky/module_4.py +0 -0
  78. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/bulky/module_5.py +0 -0
  79. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/bulky/module_6.py +0 -0
  80. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/bulky/module_7.py +0 -0
  81. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/bulky/module_8.py +0 -0
  82. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/circular_import/models.py +0 -0
  83. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/circular_import/services.py +0 -0
  84. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/circular_import/test_cancel.py +0 -0
  85. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/cli_flag/app.py +0 -0
  86. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/cross_file_contract/decode.py +0 -0
  87. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/cross_file_contract/encode.py +0 -0
  88. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/cross_file_contract/test_roundtrip.py +0 -0
  89. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/cross_file_contract/test_wire_format.py +0 -0
  90. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/empty/.keep +0 -0
  91. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/failing_test/calc.py +0 -0
  92. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/failing_test/test_calc.py +0 -0
  93. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/generate_bulky.py +0 -0
  94. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/grep_edit/ingest.py +0 -0
  95. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/grep_edit/load.py +0 -0
  96. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/grep_edit/transform.py +0 -0
  97. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/misleading_traceback/checkout.py +0 -0
  98. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/misleading_traceback/invoice.py +0 -0
  99. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/misleading_traceback/pricing.py +0 -0
  100. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/misleading_traceback/test_checkout.py +0 -0
  101. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/misleading_traceback/test_invoice.py +0 -0
  102. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/needle/inventory.py +0 -0
  103. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/needle/pricing.py +0 -0
  104. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/needle/shipping.py +0 -0
  105. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/regression_trap/test_validators.py +0 -0
  106. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/regression_trap/validators.py +0 -0
  107. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/rename/billing.py +0 -0
  108. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/rename/report.py +0 -0
  109. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/rename/test_billing.py +0 -0
  110. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/spec/parser.py +0 -0
  111. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/spec/test_parser.py +0 -0
  112. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/subtle_spec_edge/leaderboard.py +0 -0
  113. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/subtle_spec_edge/test_leaderboard.py +0 -0
  114. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/experiments/suite/fixtures/util/util.py +0 -0
  115. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/hooks/__init__.py +0 -0
  116. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/hooks/runner.py +0 -0
  117. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/mcp/__init__.py +0 -0
  118. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/mcp/adapter.py +0 -0
  119. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/mcp/client.py +0 -0
  120. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/permissions/__init__.py +0 -0
  121. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/permissions/engine.py +0 -0
  122. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/permissions/rules.py +0 -0
  123. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/providers/__init__.py +0 -0
  124. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/providers/anthropic.py +0 -0
  125. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/providers/gemini.py +0 -0
  126. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/providers/mock.py +0 -0
  127. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/providers/openai_compat.py +0 -0
  128. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/providers/pricing.py +0 -0
  129. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/providers/sse.py +0 -0
  130. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/sessions/__init__.py +0 -0
  131. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/sessions/models.py +0 -0
  132. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/sessions/store.py +0 -0
  133. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/skills_install.py +0 -0
  134. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/__init__.py +0 -0
  135. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/ask.py +0 -0
  136. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/base.py +0 -0
  137. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/bash.py +0 -0
  138. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/builtin.py +0 -0
  139. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/edit.py +0 -0
  140. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/glob.py +0 -0
  141. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/grep.py +0 -0
  142. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/read.py +0 -0
  143. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/runner.py +0 -0
  144. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/shell.py +0 -0
  145. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/skill.py +0 -0
  146. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/task.py +0 -0
  147. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/textio.py +0 -0
  148. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/todo.py +0 -0
  149. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/webfetch.py +0 -0
  150. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/websearch.py +0 -0
  151. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tools/write.py +0 -0
  152. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tui/__init__.py +0 -0
  153. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tui/app.py +0 -0
  154. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tui/app.tcss +0 -0
  155. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tui/bridge.py +0 -0
  156. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tui/config_screen.py +0 -0
  157. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tui/mcp_screen.py +0 -0
  158. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tui/session_screen.py +0 -0
  159. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tui/widgets/__init__.py +0 -0
  160. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tui/widgets/permission.py +0 -0
  161. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tui/widgets/status.py +0 -0
  162. {turnloop-0.1.10 → turnloop-0.1.12}/turnloop/tui/widgets/transcript.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: turnloop
3
- Version: 0.1.10
3
+ Version: 0.1.12
4
4
  Summary: A local-first agentic coding harness: streaming tool loop, permission gating, context compaction, subagents, and any LLM provider.
5
5
  Project-URL: Homepage, https://github.com/Pranesh-2005/turnloop
6
6
  Author: Pranesh
@@ -49,7 +49,7 @@ tl experiment run smoke # a measurement run, offline and free
49
49
 
50
50
  Four runtime dependencies: `textual`, `pydantic`, `httpx`, `pyyaml`. No vendor
51
51
  SDKs, no agent framework — the loop is the point, so the loop is written here.
52
- ~21,500 lines of Python, 364 tests, no network in the default test run.
52
+ ~21,900 lines of Python, 383 tests, no network in the default test run.
53
53
 
54
54
  ---
55
55
 
@@ -117,7 +117,7 @@ point.
117
117
  Verify:
118
118
 
119
119
  ```bash
120
- tl --version # turnloop 0.1.10
120
+ tl --version # turnloop 0.1.12
121
121
  tl doctor # every provider, key presence, shell, context budget
122
122
  ```
123
123
 
@@ -439,6 +439,12 @@ All nine ship preconfigured. `doctor` shows which have keys present.
439
439
  | `blaxel` | openai_compat | `gpt-4o-mini` | `BL_API_KEY` | sandbox; ignores the model field |
440
440
  | `ollama` | openai_compat | `qwen2.5-coder:14b` | none | local |
441
441
 
442
+ **Multiple keys per provider.** Free-tier accounts run out fast, so any `*_API_KEY`
443
+ also accepts numbered siblings: `GROQ_API_KEY`, `GROQ_API_KEY_1`, `GROQ_API_KEY_2`, ...
444
+ up to `_20`. On a 429, 401, or 403, turnloop rotates to the next configured key and
445
+ retries the same turn instead of failing it — each key is tried at most once per turn.
446
+ `doctor` reports the count, e.g. `GROQ_API_KEY set (3 keys)`, never the values.
447
+
442
448
  Three provider facts that are configuration rather than capability, each learned from
443
449
  a live failure:
444
450
 
@@ -1023,7 +1029,7 @@ one-line edit in a CRLF checkout does not produce a whole-file diff.
1023
1029
  ## Testing
1024
1030
 
1025
1031
  ```bash
1026
- pytest # 364 tests, no network
1032
+ pytest # 383 tests, no network
1027
1033
  ruff check turnloop
1028
1034
  mypy turnloop
1029
1035
  ```
@@ -1032,12 +1038,12 @@ mypy turnloop
1032
1038
  |---|---|---|
1033
1039
  | `test_permissions.py` | 50 | rule grammar, compound-command splitting, mode enforcement |
1034
1040
  | `test_experiments.py` | 36 | runner, graders, report, suite invariants, config resolution |
1035
- | `test_providers.py` | 35 | adapters vs recorded `.sse`, image blocks, thought signatures |
1041
+ | `test_providers.py` | 44 | adapters vs recorded `.sse`, image blocks, thought signatures, multi-key rotation on 429/401 |
1036
1042
  | `test_tools_files.py` | 28 | Read/Write/Edit/Glob/Grep, encodings, newlines, image detection |
1037
1043
  | `test_hooks_mcp_commands.py` | 28 | lifecycle hooks, MCP client, slash commands |
1038
1044
  | `test_tui.py` | 25 | Textual snapshots, `/config` and `/mcp add` driven through a real app |
1039
1045
  | `test_config.py` | 23 | layering, env overrides, presets, narrow-console guard |
1040
- | `test_skills_install.py` | 39 | frontmatter validation, GitHub resolution, agent-mirror tiebreak, concurrent fetch ordering, console caps, EOF on every prompt, drive-root refusal |
1046
+ | `test_skills_install.py` | 43 | frontmatter validation, GitHub resolution, agent-mirror tiebreak, concurrent fetch ordering, console caps, non-TTY refusal on an open stdin pipe, drive-root refusal |
1041
1047
  | `test_loop.py` | 23 | streaming, truncation, iteration cap |
1042
1048
  | `test_compaction.py` | 16 | three tiers, tool_use/tool_result invariant |
1043
1049
  | `test_configio.py` | 15 | minimal-diff writes, atomic save, secret-bearing MCP targets, the settings deny rules |