gemcode 0.3.106__tar.gz → 0.3.108__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 (164) hide show
  1. {gemcode-0.3.106/src/gemcode.egg-info → gemcode-0.3.108}/PKG-INFO +13 -1
  2. {gemcode-0.3.106 → gemcode-0.3.108}/README.md +12 -0
  3. {gemcode-0.3.106 → gemcode-0.3.108}/pyproject.toml +1 -1
  4. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/agent.py +87 -2
  5. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/cli.py +29 -4
  6. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/config.py +25 -0
  7. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/invoke.py +14 -10
  8. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/kaira_daemon.py +6 -2
  9. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/model_errors.py +18 -0
  10. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/repl_commands.py +2 -0
  11. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/repl_slash.py +17 -0
  12. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tool_prompt_manifest.py +35 -1
  13. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tool_registry.py +3 -1
  14. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/subtask.py +3 -2
  15. gemcode-0.3.108/src/gemcode/tools/user_choice.py +58 -0
  16. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tui/scrollback.py +100 -48
  17. {gemcode-0.3.106 → gemcode-0.3.108/src/gemcode.egg-info}/PKG-INFO +13 -1
  18. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode.egg-info/SOURCES.txt +1 -0
  19. gemcode-0.3.108/tests/test_agent_instruction.py +76 -0
  20. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_model_errors.py +17 -1
  21. gemcode-0.3.106/tests/test_agent_instruction.py +0 -22
  22. {gemcode-0.3.106 → gemcode-0.3.108}/LICENSE +0 -0
  23. {gemcode-0.3.106 → gemcode-0.3.108}/MANIFEST.in +0 -0
  24. {gemcode-0.3.106 → gemcode-0.3.108}/setup.cfg +0 -0
  25. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/__init__.py +0 -0
  26. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/__main__.py +0 -0
  27. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/audit.py +0 -0
  28. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/autocompact.py +0 -0
  29. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/autotune.py +0 -0
  30. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/callbacks.py +0 -0
  31. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/capability_routing.py +0 -0
  32. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/checkpoints.py +0 -0
  33. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/compaction.py +0 -0
  34. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/computer_use/__init__.py +0 -0
  35. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/computer_use/browser_computer.py +0 -0
  36. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/context_budget.py +0 -0
  37. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/context_warning.py +0 -0
  38. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/credentials.py +0 -0
  39. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/curated_memory.py +0 -0
  40. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/dynamic_policy.py +0 -0
  41. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/evals/harness.py +0 -0
  42. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/hitl_session.py +0 -0
  43. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/hooks.py +0 -0
  44. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/ide_protocol.py +0 -0
  45. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/ide_stdio.py +0 -0
  46. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/intent_classifier.py +0 -0
  47. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/interactions.py +0 -0
  48. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/kaira_client.py +0 -0
  49. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/kaira_ipc.py +0 -0
  50. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/kaira_job_store.py +0 -0
  51. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/learning.py +0 -0
  52. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/limits.py +0 -0
  53. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/live_audio_engine.py +0 -0
  54. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/logging_config.py +0 -0
  55. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/mcp_loader.py +0 -0
  56. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/memory/__init__.py +0 -0
  57. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/memory/embedding_memory_service.py +0 -0
  58. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/memory/file_memory_service.py +0 -0
  59. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/modality_tools.py +0 -0
  60. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/model_routing.py +0 -0
  61. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/multimodal_input.py +0 -0
  62. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/openapi_loader.py +0 -0
  63. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/org.py +0 -0
  64. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/output_styles.py +0 -0
  65. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/paths.py +0 -0
  66. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/permissions.py +0 -0
  67. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/plugins/__init__.py +0 -0
  68. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
  69. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
  70. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/policy_profile.py +0 -0
  71. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/pricing.py +0 -0
  72. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/prompt_suggestions.py +0 -0
  73. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/query/__init__.py +0 -0
  74. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/query/config.py +0 -0
  75. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/query/deps.py +0 -0
  76. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/query/engine.py +0 -0
  77. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/query/stop_hooks.py +0 -0
  78. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/query/token_budget.py +0 -0
  79. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/query/transitions.py +0 -0
  80. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/query_sanitizer.py +0 -0
  81. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/refine.py +0 -0
  82. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/review_agent.py +0 -0
  83. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/rules.py +0 -0
  84. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/session_runtime.py +0 -0
  85. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/session_store.py +0 -0
  86. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/session_summariser.py +0 -0
  87. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/skills.py +0 -0
  88. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/slash_commands.py +0 -0
  89. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/thinking.py +0 -0
  90. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tool_result_store.py +0 -0
  91. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/__init__.py +0 -0
  92. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/bash.py +0 -0
  93. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/browser.py +0 -0
  94. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/compress_memory.py +0 -0
  95. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/curated_memory.py +0 -0
  96. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/edit.py +0 -0
  97. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/filesystem.py +0 -0
  98. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/notebook.py +0 -0
  99. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/notes.py +0 -0
  100. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/org_tools.py +0 -0
  101. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/repo_map.py +0 -0
  102. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/search.py +0 -0
  103. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/shell.py +0 -0
  104. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/shell_gate.py +0 -0
  105. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/skills.py +0 -0
  106. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/tasks.py +0 -0
  107. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/think.py +0 -0
  108. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/todo.py +0 -0
  109. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/veomem_tools.py +0 -0
  110. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/web.py +0 -0
  111. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools/web_search.py +0 -0
  112. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tools_inspector.py +0 -0
  113. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/trust.py +0 -0
  114. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tui/input_handler.py +0 -0
  115. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tui/spinner.py +0 -0
  116. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tui/welcome_banner.py +0 -0
  117. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/tui/welcome_rich.py +0 -0
  118. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/veomem_bridge.py +0 -0
  119. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/version.py +0 -0
  120. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/vertex.py +0 -0
  121. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/wal.py +0 -0
  122. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/web/__init__.py +0 -0
  123. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/web/sse_adapter.py +0 -0
  124. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/web/terminal_repl.py +0 -0
  125. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/web/web_sse_compat.py +0 -0
  126. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode/workspace_hints.py +0 -0
  127. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode.egg-info/dependency_links.txt +0 -0
  128. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode.egg-info/entry_points.txt +0 -0
  129. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode.egg-info/requires.txt +0 -0
  130. {gemcode-0.3.106 → gemcode-0.3.108}/src/gemcode.egg-info/top_level.txt +0 -0
  131. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_add_dir.py +0 -0
  132. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_autocompact.py +0 -0
  133. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_capability_routing.py +0 -0
  134. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_checkpoint_diff_command.py +0 -0
  135. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_cli_init.py +0 -0
  136. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_compress_memory_tool.py +0 -0
  137. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_computer_use_permissions.py +0 -0
  138. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_context_budget.py +0 -0
  139. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_context_warning.py +0 -0
  140. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_credentials.py +0 -0
  141. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_eval_harness_layout.py +0 -0
  142. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_ide_stdio_attachments.py +0 -0
  143. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_interactive_permission_ask.py +0 -0
  144. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_kaira_scheduler.py +0 -0
  145. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_modality_tools.py +0 -0
  146. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_model_error_retry.py +0 -0
  147. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_model_routing.py +0 -0
  148. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_multimodal_input.py +0 -0
  149. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_output_styles_and_rules.py +0 -0
  150. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_paths.py +0 -0
  151. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_permissions.py +0 -0
  152. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_prompt_suggestions.py +0 -0
  153. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_repl_commands.py +0 -0
  154. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_repl_slash.py +0 -0
  155. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_skills.py +0 -0
  156. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_slash_commands.py +0 -0
  157. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_slash_completion_registry.py +0 -0
  158. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_thinking_config.py +0 -0
  159. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_token_budget.py +0 -0
  160. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_tool_context_circulation.py +0 -0
  161. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_tools.py +0 -0
  162. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_tools_inspector.py +0 -0
  163. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_web_sse_adapter.py +0 -0
  164. {gemcode-0.3.106 → gemcode-0.3.108}/tests/test_workspace_hints.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemcode
3
- Version: 0.3.106
3
+ Version: 0.3.108
4
4
  Summary: Local-first coding agent on Google Gemini + ADK
5
5
  Author: GemCode Contributors
6
6
  License: Apache License
@@ -327,6 +327,17 @@ GemCode combines:
327
327
  Reference:
328
328
  - [`../docs/tools-and-permissions.md`](../docs/tools-and-permissions.md)
329
329
 
330
+ ### Super mode (fully autonomous)
331
+
332
+ Use when you want GemCode to run without GemCode’s own confirmation prompts (mutations, shell, ADK confirmation handoffs, AFC stdin prompt, attachment gate, auto-trust on CLI, and non-interactive `get_user_choice` = first option).
333
+
334
+ - **CLI:** `gemcode -C . --super "your task"`
335
+ - **Env:** `GEMCODE_SUPER_MODE=1`
336
+ - **REPL/TUI:** `/super` (use `/super off` to clear the flag only)
337
+ - **Kaira:** `gemcode kaira -C . --super`
338
+
339
+ Details and safety notes: [`../docs/tools-and-permissions.md`](../docs/tools-and-permissions.md#super-mode-fully-autonomous).
340
+
330
341
  ## Common commands
331
342
 
332
343
  ### Inspect models
@@ -410,6 +421,7 @@ Status note:
410
421
  | `/review` | Run a review workflow |
411
422
  | `/eval` | Run evaluation gates |
412
423
  | `/kaira` | Show scheduler usage help |
424
+ | `/super` | Super mode: auto-approve tools, no GemCode HITL · `/super off` |
413
425
 
414
426
  Detailed behavior:
415
427
  - [`../docs/cli-and-repl.md`](../docs/cli-and-repl.md)
@@ -135,6 +135,17 @@ GemCode combines:
135
135
  Reference:
136
136
  - [`../docs/tools-and-permissions.md`](../docs/tools-and-permissions.md)
137
137
 
138
+ ### Super mode (fully autonomous)
139
+
140
+ Use when you want GemCode to run without GemCode’s own confirmation prompts (mutations, shell, ADK confirmation handoffs, AFC stdin prompt, attachment gate, auto-trust on CLI, and non-interactive `get_user_choice` = first option).
141
+
142
+ - **CLI:** `gemcode -C . --super "your task"`
143
+ - **Env:** `GEMCODE_SUPER_MODE=1`
144
+ - **REPL/TUI:** `/super` (use `/super off` to clear the flag only)
145
+ - **Kaira:** `gemcode kaira -C . --super`
146
+
147
+ Details and safety notes: [`../docs/tools-and-permissions.md`](../docs/tools-and-permissions.md#super-mode-fully-autonomous).
148
+
138
149
  ## Common commands
139
150
 
140
151
  ### Inspect models
@@ -218,6 +229,7 @@ Status note:
218
229
  | `/review` | Run a review workflow |
219
230
  | `/eval` | Run evaluation gates |
220
231
  | `/kaira` | Show scheduler usage help |
232
+ | `/super` | Super mode: auto-approve tools, no GemCode HITL · `/super off` |
221
233
 
222
234
  Detailed behavior:
223
235
  - [`../docs/cli-and-repl.md`](../docs/cli-and-repl.md)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gemcode"
7
- version = "0.3.106"
7
+ version = "0.3.108"
8
8
  description = "Local-first coding agent on Google Gemini + ADK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -76,6 +76,8 @@ def build_global_instruction() -> str:
76
76
  "You are GemCode, an expert software engineering agent powered by Google Gemini. "
77
77
  "Think deeply about what the person actually wants before you do anything. "
78
78
  "Use exactly as many tools as the task genuinely requires — no more. "
79
+ "When routing or capabilities change between turns, still prefer minimal tools, "
80
+ "repo-grounded evidence, and verification before claiming done. "
79
81
  "Act fully and autonomously when action is needed. "
80
82
  "Always use read-only tools before shell or write tools. "
81
83
  "Never create CLAUDE.md or AGENTS.md; use GEMINI.md for project instructions."
@@ -353,6 +355,79 @@ def _build_runtime_facts(cfg: GemCodeConfig) -> str:
353
355
  - **Working in subfolders** — call `list_directory(\"Desktop\")`, `glob_files(\"**/query.ts\")`, `read_file(\"testing/ai-edtech-app/src/app/page.tsx\")` directly. Never claim access is blocked unless a tool returned an explicit error.{git_section}{curated_section}{veomem_section}"""
354
356
 
355
357
 
358
+ def _build_calibration_section(cfg: GemCodeConfig) -> str:
359
+ """
360
+ Meta-instruction for "smart" behavior under dynamic routing (auto model/capability mode,
361
+ orchestration tools, etc.). Kept compact to limit prompt bloat.
362
+ """
363
+ mm = (getattr(cfg, "model_mode", "") or "").strip().lower()
364
+ cm = (getattr(cfg, "capability_mode", "") or "").strip().lower()
365
+ bits: list[str] = []
366
+ if mm == "auto":
367
+ bits.append("`model_mode=auto` (model may shift per turn for speed vs depth)")
368
+ if cm == "auto":
369
+ bits.append("`capability_mode=auto` (deep research / extras may attach per turn)")
370
+ session_note = ""
371
+ if bits:
372
+ session_note = (
373
+ "\n**This session uses dynamic routing:** "
374
+ + " · ".join(bits)
375
+ + ". Defaults are **hints** — override with judgment when the task clearly needs more or less depth.\n"
376
+ )
377
+
378
+ return f"""## Calibration and dynamic routing (all modes)
379
+
380
+ Infer **intent → depth → tools** without fixed buckets (requests vary):
381
+
382
+ | Stance | Move |
383
+ | --- | --- |
384
+ | **Explain / review** | Read-only recon first; cite **paths**; avoid unrelated edits. |
385
+ | **Implement / fix** | Recon → plan (`todo_write` when 3+ steps) → smallest change → **verify** (tests, lint, build slice, or read-back). |
386
+ | **Debug** | One hypothesis per iteration; change one variable between tries; never repeat the same failing command verbatim. |
387
+ | **External facts** | Use web/research tools when the answer is outside the repo (docs, APIs, CVEs). Prefer **repo files** for how *this* codebase behaves. |
388
+
389
+ **Evidence:** tie non-obvious claims about this workspace to **files or command output** you actually saw.
390
+
391
+ **Orchestration:** use `spawn_subtasks`, org delegation, or background jobs only when work is **parallel** or **role-split**. Merge into **one** answer with a single recommendation; skip fan-out for small linear tasks.
392
+
393
+ **Anti-patterns:** tool spam; “done” without verification on risky edits; searching the web when the source file is already in-tree; repeating identical failures.{session_note}"""
394
+
395
+
396
+ def _engineering_discipline_instruction_enabled() -> bool:
397
+ """Extra prompt section: cautious change quality. Opt out with GEMCODE_ENGINEERING_DISCIPLINE=0."""
398
+ import os
399
+
400
+ v = os.environ.get("GEMCODE_ENGINEERING_DISCIPLINE", "1").strip().lower()
401
+ return v not in ("0", "false", "no", "off")
402
+
403
+
404
+ def _build_engineering_discipline_section(cfg: GemCodeConfig) -> str:
405
+ """Prompt block for minimal, evidence-grounded edits; trivial fixes need not dwell on it."""
406
+ _ = cfg # reserved for future project-scoped tuning
407
+ return """## Engineering discipline (change quality)
408
+
409
+ **Tradeoff:** biases toward careful, minimal diffs over speed. For trivial edits, use judgment.
410
+
411
+ ### Ambiguity
412
+ - Briefly state **what you understood** before substantial implementation. If several readings fit, outline them or ask **one** precise question — do not silently pick and run.
413
+ - Prefer **evidence from this repo** (reads, grep, tests) over guessed APIs, paths, or behaviour.
414
+
415
+ ### Scope and simplicity
416
+ - Deliver **what was asked**, not a roadmap of extras. Avoid speculative features, abstraction layers, or configurability “for later” unless the user requested flexibility.
417
+ - Prefer the **smallest** correct change. Expand structure only when complexity is already present or clearly required.
418
+
419
+ ### Surgical edits
420
+ - Change **only** what is necessary for the outcome; match surrounding **style and patterns** unless project docs say otherwise.
421
+ - Do not refactor, rename, or reformat unrelated code in the same pass. Note worthwhile cleanups separately if helpful.
422
+ - Remove **orphans your edit introduced** (e.g. unused imports from your change). Leave pre-existing dead code unless the user asks to remove it.
423
+
424
+ ### When to call it done
425
+ - Turn fuzzy requests into **checkable** outcomes where it matters (e.g. bug → reproduce → fix → same checks green).
426
+ - After material edits, run the **cheapest** falsifying step you can: targeted test, lint, build, or re-read the critical path — not guess-and-hope.
427
+
428
+ """
429
+
430
+
356
431
  def _build_memory_section(cfg: GemCodeConfig) -> str:
357
432
  """Injected when enable_memory=True so the agent understands and uses memory."""
358
433
  mem_path = cfg.project_root / ".gemcode" / "memories.jsonl"
@@ -559,6 +634,12 @@ def build_instruction(cfg: GemCodeConfig) -> str:
559
634
  "on",
560
635
  )
561
636
 
637
+ discipline_block = (
638
+ _build_engineering_discipline_section(cfg)
639
+ if _engineering_discipline_instruction_enabled()
640
+ else ""
641
+ )
642
+
562
643
  base = f"""You are GemCode, an expert software engineering agent powered by Google Gemini.
563
644
  You run locally via the GemCode CLI. You are the same agent the user launched — not a hosted portal.
564
645
 
@@ -610,6 +691,8 @@ You have native deep thinking capability — use it actively:
610
691
  - Prefer **small, targeted tool outputs** by default (saves context, improves accuracy).
611
692
  - If a tool output was **offloaded** (you see a `tool_result:<sha>` reference), and you need details, call `load_tool_result(ref)` and extract only the relevant slice.
612
693
 
694
+ {_build_calibration_section(cfg)}
695
+ {discipline_block}
613
696
  ## Tool selection guide (only when needed)
614
697
 
615
698
  Keep tool usage minimal. Prefer short, targeted calls and keep tool outputs small.
@@ -1013,9 +1096,11 @@ def build_root_agent(
1013
1096
  tools = [preload_memory, *tools]
1014
1097
 
1015
1098
  # ADK built-in interactive + artifact tools — always available when ADK supports them.
1099
+ # In super mode, ``get_user_choice`` auto-picks the first option (no UI).
1016
1100
  try:
1017
- from google.adk.tools import get_user_choice, load_artifacts, exit_loop
1018
- tools = [*tools, get_user_choice, load_artifacts, exit_loop]
1101
+ from gemcode.tools.user_choice import append_user_choice_load_artifacts_exit_loop
1102
+
1103
+ append_user_choice_load_artifacts_exit_loop(cfg, tools)
1019
1104
  except Exception:
1020
1105
  pass
1021
1106
 
@@ -13,7 +13,7 @@ import uuid
13
13
  import warnings
14
14
  from pathlib import Path
15
15
 
16
- from gemcode.config import GemCodeConfig, load_cli_environment
16
+ from gemcode.config import GemCodeConfig, apply_super_mode, load_cli_environment
17
17
  from gemcode.tools_inspector import inspect_tools, smoke_tools
18
18
  from gemcode.invoke import run_turn
19
19
  from gemcode.model_routing import pick_effective_model
@@ -43,6 +43,11 @@ def _maybe_prompt_trust(cfg: GemCodeConfig) -> None:
43
43
  On first use in a project root, ask the user to trust the folder so file,
44
44
  shell, and git tools can run. If not trusted, we exit before any tool runs.
45
45
  """
46
+ if getattr(cfg, "super_mode", False):
47
+ root = cfg.project_root.resolve()
48
+ if not is_trusted_root(root):
49
+ trust_root(root, trusted=True)
50
+ return
46
51
  # Non-interactive sessions can't answer prompts.
47
52
  if not (hasattr(sys.stdin, "isatty") and sys.stdin.isatty()):
48
53
  return
@@ -220,7 +225,9 @@ async def _run_repl(cfg: GemCodeConfig, session_id: str, *, use_mcp: bool) -> No
220
225
  "on",
221
226
  ):
222
227
  try:
223
- if (
228
+ if getattr(cfg, "super_mode", False):
229
+ pass
230
+ elif (
224
231
  hasattr(sys.stdin, "isatty")
225
232
  and sys.stdin.isatty()
226
233
  and not cfg.yes_to_all
@@ -922,6 +929,11 @@ def main() -> None:
922
929
  action="store_true",
923
930
  help="Allow write_file / search_replace (disables interactive HITL prompts).",
924
931
  )
932
+ kaira_parser.add_argument(
933
+ "--super",
934
+ action="store_true",
935
+ help="Fully autonomous jobs: auto-approve tools/shell, no HITL (implies --yes).",
936
+ )
925
937
  kaira_parser.add_argument(
926
938
  "--interactive-ask",
927
939
  action="store_true",
@@ -978,7 +990,11 @@ def main() -> None:
978
990
  cfg.model_mode = "fast"
979
991
 
980
992
  cfg.yes_to_all = bool(args.yes)
981
- if args.interactive_ask:
993
+ if getattr(args, "super", False):
994
+ cfg.super_mode = True
995
+ if cfg.super_mode:
996
+ apply_super_mode(cfg)
997
+ elif args.interactive_ask:
982
998
  cfg.interactive_permission_ask = True
983
999
  else:
984
1000
  if "GEMCODE_INTERACTIVE_PERMISSION_ASK" not in os.environ:
@@ -1023,6 +1039,11 @@ def main() -> None:
1023
1039
  parser.add_argument("-C", "--directory", type=Path, default=Path.cwd(), help="Project root")
1024
1040
  parser.add_argument("--session", default=None, help="Session id for SQLite-backed history")
1025
1041
  parser.add_argument("--yes", action="store_true", help="Allow write_file / search_replace")
1042
+ parser.add_argument(
1043
+ "--super",
1044
+ action="store_true",
1045
+ help="Super mode: auto-approve all tool/shell use, skip HITL and AFC prompts (implies --yes).",
1046
+ )
1026
1047
  parser.add_argument(
1027
1048
  "--interactive-ask",
1028
1049
  action="store_true",
@@ -1096,7 +1117,11 @@ def main() -> None:
1096
1117
  if args.model_mode is None:
1097
1118
  cfg.model_mode = "fast"
1098
1119
  cfg.yes_to_all = args.yes
1099
- if args.interactive_ask:
1120
+ if args.super:
1121
+ cfg.super_mode = True
1122
+ if cfg.super_mode:
1123
+ apply_super_mode(cfg)
1124
+ elif args.interactive_ask:
1100
1125
  cfg.interactive_permission_ask = True
1101
1126
  else:
1102
1127
  # If user didn't explicitly set env, default to HITL when we're in a TTY.
@@ -374,6 +374,13 @@ class GemCodeConfig:
374
374
  default_factory=lambda: _truthy_env("GEMCODE_BACKGROUND_LEARNER", default=False)
375
375
  )
376
376
 
377
+ # "Super" mode: fully autonomous session — auto-approve mutating/shell tools,
378
+ # skip interactive HITL and AFC tool-mode prompts, and prefer non-blocking UX.
379
+ # Enable with ``GEMCODE_SUPER_MODE=1`` or ``gemcode --super``.
380
+ super_mode: bool = field(
381
+ default_factory=lambda: _truthy_env("GEMCODE_SUPER_MODE", default=False)
382
+ )
383
+
377
384
  def __post_init__(self) -> None:
378
385
  self.project_root = self.project_root.resolve()
379
386
  # Default agentic depth when env omits GEMCODE_MAX_LLM_CALLS (was: None → SDK default).
@@ -479,6 +486,24 @@ class GemCodeConfig:
479
486
  "tar",
480
487
  )
481
488
  )
489
+ if self.super_mode:
490
+ apply_super_mode(self)
491
+
492
+
493
+ def apply_super_mode(cfg: GemCodeConfig) -> None:
494
+ """
495
+ Apply super-mode policy: no interactive tool HITL, auto-approve GemCode gates,
496
+ keep all toolsets (skip AFC stdin prompt via ``_afc_choice``), relax strict
497
+ permission mode so shell tools can run.
498
+ """
499
+ if not getattr(cfg, "super_mode", False):
500
+ return
501
+ cfg.yes_to_all = True
502
+ cfg.interactive_permission_ask = False
503
+ if getattr(cfg, "permission_mode", "") == "strict":
504
+ cfg.permission_mode = "default"
505
+ object.__setattr__(cfg, "_afc_choice", "all")
506
+ object.__setattr__(cfg, "_attachments_allowed", True)
482
507
 
483
508
 
484
509
  def load_dotenv_optional() -> None:
@@ -18,11 +18,6 @@ from google.adk.runners import Runner
18
18
  from google.genai import types
19
19
 
20
20
 
21
- # Delays (seconds) between successive transient-error retries: 2s, 5s, 12s.
22
- # Three retries = up to ~19 seconds of total wait before giving up.
23
- _TRANSIENT_RETRY_DELAYS = [2.0, 5.0, 12.0]
24
-
25
-
26
21
  _HITL_PROMPT_LOCK = Lock()
27
22
 
28
23
  async def _maybe_enqueue_kaira_autopilot(*, cfg: "GemCodeConfig", session_id: str) -> None:
@@ -423,14 +418,14 @@ async def run_turn(
423
418
  next_message=current_message, do_reset=do_reset
424
419
  )
425
420
  except Exception as _exc:
426
- from gemcode.model_errors import is_transient_error
427
- if is_transient_error(_exc) and transient_attempts < len(_TRANSIENT_RETRY_DELAYS):
428
- delay = _TRANSIENT_RETRY_DELAYS[transient_attempts]
421
+ from gemcode.model_errors import API_TRANSIENT_RETRY_DELAYS_SEC, is_transient_error
422
+ if is_transient_error(_exc) and transient_attempts < len(API_TRANSIENT_RETRY_DELAYS_SEC):
423
+ delay = API_TRANSIENT_RETRY_DELAYS_SEC[transient_attempts]
429
424
  transient_attempts += 1
430
425
  _tui_active = os.environ.get("GEMCODE_TUI_ACTIVE", "0").lower() in ("1", "true", "yes", "on")
431
426
  _msg = (
432
427
  f"\n[gemcode] Transient API error ({type(_exc).__name__}). "
433
- f"Retrying in {delay:.0f}s (attempt {transient_attempts}/{len(_TRANSIENT_RETRY_DELAYS)})...\n"
428
+ f"Retrying in {delay:.0f}s (attempt {transient_attempts}/{len(API_TRANSIENT_RETRY_DELAYS_SEC)})...\n"
434
429
  )
435
430
  print(_msg, file=sys.stderr)
436
431
  # Surface retry notice in TUI if available.
@@ -460,11 +455,20 @@ async def run_turn(
460
455
  and hasattr(sys.stdin, "isatty")
461
456
  and sys.stdin.isatty()
462
457
  )
458
+ auto_ok = bool(
459
+ cfg is not None
460
+ and (
461
+ bool(getattr(cfg, "yes_to_all", False))
462
+ or bool(getattr(cfg, "super_mode", False))
463
+ )
464
+ )
463
465
 
464
466
  parts: list[types.Part] = []
465
467
  for fc in confirmation_fcs:
466
468
  tool_name, hint = _extract_hint_and_tool(fc)
467
- if interactive_enabled:
469
+ if auto_ok:
470
+ ok = True
471
+ elif interactive_enabled:
468
472
  suffix = f"\n Hint: {hint}" if hint else ""
469
473
  ok = _prompt_yes_no(
470
474
  f"\n[gemcode HITL] Approve tool call '{tool_name}'? [y/N]{suffix}\n> "
@@ -491,8 +491,12 @@ class KairaDaemon:
491
491
  parts: list[types.Part] = []
492
492
  for fc in confirmation_fcs:
493
493
  tool_name, hint = _extract_hint_and_tool(fc)
494
- ok = False
495
- if self._ipc is not None:
494
+ auto_ok = bool(
495
+ getattr(self.cfg, "yes_to_all", False)
496
+ or getattr(self.cfg, "super_mode", False)
497
+ )
498
+ ok = bool(auto_ok)
499
+ if not ok and self._ipc is not None:
496
500
  try:
497
501
  ok = await self._ipc.request_confirmation(
498
502
  job_id=job.job_id,
@@ -4,6 +4,9 @@ from __future__ import annotations
4
4
 
5
5
  import re
6
6
 
7
+ # Seconds between retries for transient API failures (shared by invoke.py and TUI).
8
+ API_TRANSIENT_RETRY_DELAYS_SEC: tuple[float, ...] = (2.0, 5.0, 12.0)
9
+
7
10
 
8
11
  def is_transient_error(error: Exception) -> bool:
9
12
  """Return True for HTTP 503 / 429 and similar transient API errors that are safe to retry.
@@ -29,6 +32,21 @@ def is_transient_error(error: Exception) -> bool:
29
32
  return True
30
33
 
31
34
  msg = str(error)
35
+ ml = msg.lower()
36
+
37
+ # httpx / google-genai often raise ``ServerError`` for HTTP 500 without ``APIError.code``.
38
+ if et == "ServerError" or "ServerError" in et:
39
+ if any(code in msg for code in ("500", "502", "503", "504")):
40
+ return True
41
+
42
+ # Gemini REST body shape: ``'code': 500`` / ``"INTERNAL"`` / "Internal error encountered."
43
+ if ("'code': 500" in msg or '"code": 500' in msg or re.search(r"\b500\b", msg)) and any(
44
+ p in ml for p in ("internal", "unavailable", "try again", "deadline", "timeout", "backend")
45
+ ):
46
+ return True
47
+ if "internal error encountered" in ml:
48
+ return True
49
+
32
50
  # Match the specific phrases Gemini uses in 503 responses
33
51
  if "503" in msg and any(p in msg for p in ("high demand", "service unavailable", "overloaded")):
34
52
  return True
@@ -257,6 +257,7 @@ SLASH_COMMANDS: list[tuple[str, str]] = [
257
257
  ("skills", "List GemSkills"),
258
258
  ("status", "Model, capabilities, thinking, limits"),
259
259
  ("style", "Output styles · /style <name>|off"),
260
+ ("super", "Super mode · auto-approve tools/shell, no HITL · /super off"),
260
261
  ("summarise", "Summarise current session, persist key points, then reset · /summarize same"),
261
262
  ("thinking", "Thinking verbose/brief/off, budget, level"),
262
263
  ("tools", "Tool inventory · /tools smoke"),
@@ -334,6 +335,7 @@ def slash_help_lines() -> list[str]:
334
335
  " Aliases: /image /img /file",
335
336
  " /trust Show workspace trust status (file/shell tools)",
336
337
  " /trust on|off Trust or revoke trust for this project root (~/.gemcode/trust.json)",
338
+ " /super Fully autonomous session (auto-approve tools; no HITL). /super off to disable flag only",
337
339
  " /init Analyze project structure and generate GEMINI.md",
338
340
  " /init force Regenerate GEMINI.md even if it already exists",
339
341
  " /cost Show token usage and estimated cost for this session",
@@ -616,6 +616,23 @@ async def process_repl_slash(
616
616
  out()
617
617
  return ReplSlashResult(skip_model_turn=True)
618
618
 
619
+ # ── /super (fully autonomous: no HITL, auto-approve tools) ─────────────────
620
+ if name == "super":
621
+ from gemcode.config import apply_super_mode
622
+
623
+ args_s = (sc.args or "").strip().lower()
624
+ if args_s in ("off", "0", "false", "no"):
625
+ cfg.super_mode = False
626
+ out("Super mode: off (yes_to_all unchanged; restart or adjust flags to change approvals).")
627
+ out()
628
+ return ReplSlashResult(skip_model_turn=True)
629
+ cfg.super_mode = True
630
+ apply_super_mode(cfg)
631
+ out("Super mode: on — mutating/shell tools and ADK confirmations auto-approved; no AFC stdin prompt.")
632
+ out("Equivalent: gemcode --super or GEMCODE_SUPER_MODE=1")
633
+ out()
634
+ return ReplSlashResult(skip_model_turn=True)
635
+
619
636
  # ── /add-dir (safe multi-root access) ──────────────────────────────────────
620
637
  if name in ("add-dir", "add_dir", "adddir"):
621
638
  args = (sc.args or "").strip()
@@ -55,6 +55,14 @@ def build_tool_manifest(cfg: GemCodeConfig) -> str | None:
55
55
  yes_to_all = bool(getattr(cfg, "yes_to_all", False))
56
56
  interactive_ask_on = bool(getattr(cfg, "interactive_permission_ask", False))
57
57
  sticky_hitl = bool(getattr(cfg, "interactive_hitl_sticky_session", True))
58
+ super_on = bool(getattr(cfg, "super_mode", False))
59
+ guc_manifest = (
60
+ "- **`get_user_choice(options)`** — (super mode) returns the **first** option immediately with no user prompt. "
61
+ "Put the preferred default first."
62
+ if super_on
63
+ else "- **`get_user_choice(options)`** — present the user with a structured list of options (2–6 items). "
64
+ "Returns the user's selected option. Use instead of open-ended questions when responses are bounded."
65
+ )
58
66
 
59
67
  # Core custom tools.
60
68
  read_only = sorted(READ_ONLY_TOOLS)
@@ -97,6 +105,30 @@ def build_tool_manifest(cfg: GemCodeConfig) -> str | None:
97
105
 
98
106
  memory_on = bool(getattr(cfg, "enable_memory", False))
99
107
 
108
+ mm = (getattr(cfg, "model_mode", "") or "").strip().lower()
109
+ cm = (getattr(cfg, "capability_mode", "") or "").strip().lower()
110
+ auto_routing_note = ""
111
+ if mm == "auto" or cm == "auto":
112
+ auto_routing_note = (
113
+ f"\n- **Dynamic routing:** `model_mode={mm}`, `capability_mode={cm}` — per-turn defaults are **hints**; "
114
+ "still pick the smallest depth/toolset that fits the ask.\n"
115
+ )
116
+
117
+ calibration_manifest = f"""### Calibration (aligned with main instruction)
118
+ - **Repo vs external:** ground claims about *this repo* with reads/grep/tests; use research/web for **external** docs, APIs, or facts outside the tree.
119
+ - **Orchestration:** use `run_subtask` / `spawn_subtasks` only for **parallel independent** work or explicit verification; merge into **one** answer; skip fan-out for trivial linear tasks.
120
+ - **Finish line:** verify risky edits (test/lint/read-back) before declaring done.{auto_routing_note}"""
121
+
122
+ _disc = os.environ.get("GEMCODE_ENGINEERING_DISCIPLINE", "1").strip().lower()
123
+ discipline_manifest = ""
124
+ if _disc not in ("0", "false", "no", "off"):
125
+ discipline_manifest = """
126
+
127
+ ### Engineering discipline (aligned with main instruction)
128
+ - **Ambiguity:** state what you understood or ask **one** precise question; prefer repo evidence over guesses.
129
+ - **Scope:** deliver the ask with the **smallest** adequate change; skip speculative extras.
130
+ - **Surgical:** touch only what you must; match local style; clean up orphans **your** edit introduced."""
131
+
100
132
  manifest = f"""## Tool system (GemCode)
101
133
 
102
134
  ### Execution model
@@ -104,6 +136,8 @@ def build_tool_manifest(cfg: GemCodeConfig) -> str | None:
104
136
  - **Reason end-to-end autonomously.** The user expects complete tasks, not a questionnaire. Use `think` before complex actions, `todo_write` to track multi-step work.
105
137
  - **Never stop after the first tool call succeeds.** Keep going until the full task is done or you hit a genuine blocker.
106
138
 
139
+ {calibration_manifest}{discipline_manifest}
140
+
107
141
  ### Permission policy
108
142
  | Setting | Value |
109
143
  |---------|-------|
@@ -126,7 +160,7 @@ def build_tool_manifest(cfg: GemCodeConfig) -> str | None:
126
160
  - Returns the sub-agent's final text as `result`.
127
161
  - Use for: context-heavy exploration (reading 50+ files), parallel investigation of independent subsystems, verification passes after you finish work.
128
162
  - Always give the sub-agent enough context to work independently; end the task with "Summarise your findings clearly."
129
- - **`get_user_choice(question, choices)`** — present the user with a structured list of options (2–6 items). Returns the user's selected option. Use instead of open-ended questions when the set of valid responses is known and bounded (e.g. framework choice, migration strategy, naming convention).
163
+ {guc_manifest}
130
164
  - **`load_artifacts(filenames)`** — load one or more named artifacts (binary/large files) saved in this or a previous session. Returns the content of each. Use `user:filename` prefix for cross-session artifacts.
131
165
  - **`exit_loop()`** — signal the surrounding LoopAgent to stop iterating and emit the final result. Only relevant when running inside a LoopAgent pipeline (e.g. the `/refine` write→test→fix loop).
132
166
 
@@ -38,7 +38,9 @@ SHELL_TOOLS: frozenset[str] = frozenset({"run_command", "bash"})
38
38
  # Session planning only (no disk / shell; no extra permission)
39
39
  # think — in-context reasoning scratchpad (no-op, no side effects)
40
40
  # run_subtask — spawns a sub-agent; inherits parent permission settings
41
- PLANNING_TOOLS: frozenset[str] = frozenset({"todo_write", "think", "run_subtask"})
41
+ PLANNING_TOOLS: frozenset[str] = frozenset(
42
+ {"todo_write", "think", "run_subtask", "spawn_subtasks"}
43
+ )
42
44
 
43
45
  ToolConcurrency = Literal["parallel_safe", "serial_mutating", "shell"]
44
46
 
@@ -43,8 +43,9 @@ def _build_sub_tools(cfg: GemCodeConfig) -> list:
43
43
 
44
44
  # ADK special interactive tools — always try to include.
45
45
  try:
46
- from google.adk.tools import get_user_choice, load_artifacts, exit_loop
47
- tools = [*tools, get_user_choice, load_artifacts, exit_loop]
46
+ from gemcode.tools.user_choice import append_user_choice_load_artifacts_exit_loop
47
+
48
+ append_user_choice_load_artifacts_exit_loop(cfg, tools)
48
49
  except Exception:
49
50
  pass
50
51
 
@@ -0,0 +1,58 @@
1
+ """User-choice tool: interactive (ADK) vs automatic (super mode)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ if TYPE_CHECKING:
8
+ from gemcode.config import GemCodeConfig
9
+
10
+
11
+ def make_super_get_user_choice_tool(cfg: GemCodeConfig):
12
+ """
13
+ Non-interactive stand-in for ADK ``get_user_choice`` when ``cfg.super_mode``.
14
+
15
+ ADK's built-in tool is a LongRunningFunctionTool that returns ``None`` and
16
+ waits for UI; in super mode we return the first non-empty option immediately.
17
+ """
18
+
19
+ def get_user_choice(options: list[str], tool_context: Any) -> str | None:
20
+ """Pick the first option without blocking on human input (super mode)."""
21
+ try:
22
+ tool_context.actions.skip_summarization = True
23
+ except Exception:
24
+ pass
25
+ for opt in options or []:
26
+ s = str(opt).strip()
27
+ if s:
28
+ return s
29
+ return None
30
+
31
+ get_user_choice.__name__ = "get_user_choice"
32
+ return get_user_choice
33
+
34
+
35
+ def append_user_choice_load_artifacts_exit_loop(
36
+ cfg: GemCodeConfig, tools: list,
37
+ ) -> None:
38
+ """
39
+ Append ``get_user_choice``, ``load_artifacts``, ``exit_loop`` like ADK defaults.
40
+
41
+ When ``cfg.super_mode``, ``get_user_choice`` is a plain function that auto-picks
42
+ the first option; otherwise the ADK LongRunningFunctionTool is used.
43
+ """
44
+ if getattr(cfg, "super_mode", False):
45
+ tools.append(make_super_get_user_choice_tool(cfg))
46
+ else:
47
+ try:
48
+ from google.adk.tools import get_user_choice as adk_get_user_choice
49
+
50
+ tools.append(adk_get_user_choice)
51
+ except Exception:
52
+ pass
53
+ try:
54
+ from google.adk.tools import exit_loop, load_artifacts
55
+
56
+ tools.extend([load_artifacts, exit_loop])
57
+ except Exception:
58
+ pass